Files
Enterprise-Knowledge-Base/cli/cmd/link/unlink.go
T
nullkey 0e081aec5c feat(cli): --log-level + kb/agent status & check + cross-cutting refactor
Operability surface and the bulk of the jopts→fopts migration:

* --log-level error|warn|info|debug + WEKNORA_LOG_LEVEL env, wired to
  the SDK via client.SetDebugLevel. Invalid --log-level returns FlagError
  (exit 2).
* kb status <kb-id> / kb check <kb-id> verb split (1 HTTP vs 1+N for
  failed_count aggregation).
* agent status <agent-id> / agent check <agent-id> verb split
  (probes kb_scope_all_reachable via 1+N HTTP).
* kb create <name> positional (matches agent create).
* Positional id help strings namespaced (<kb-id> / <agent-id>).
* All auth / context / link / doctor / kb / agent CRUD commands migrated
  to the FormatOptions API.
* root.go Execute(ctx) takes a context so signal-cancellation propagates
  via cmd.Context() into long-running commands.
* Pagination termination uses len(accum) >= total (not page*pageSize)
  so server-capped page sizes do not truncate aggregations.
2026-05-18 11:10:19 +08:00

80 lines
2.6 KiB
Go

package linkcmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/Tencent/WeKnora/cli/internal/cmdutil"
"github.com/Tencent/WeKnora/cli/internal/iostreams"
"github.com/Tencent/WeKnora/cli/internal/projectlink"
)
// unlinkFields enumerates the fields surfaced for `--format json` discovery on
// `unlink`. Tracks the small unlinkResult struct.
var unlinkFields = []string{"project_link_path"}
type UnlinkOptions struct{}
// unlinkResult is the typed payload emitted under data.
type unlinkResult struct {
ProjectLinkPath string `json:"project_link_path"`
}
// NewCmdUnlink builds `weknora unlink`. Walks up from cwd so a user in
// a subdirectory of a linked project can unlink without cd-ing up first.
func NewCmdUnlink() *cobra.Command {
opts := &UnlinkOptions{}
cmd := &cobra.Command{
Use: "unlink",
Short: "Remove the directory's knowledge-base binding",
Long: `Removes the .weknora/project.yaml that ` + "`weknora link`" + ` previously
wrote, so subsequent commands no longer auto-resolve --kb from the link.
Walks up from the current directory until a link is found, mirroring the
discovery that ` + "`--kb`" + ` resolution uses; you do not need to cd to the
project root to unlink. Errors with input.invalid_argument when no link
is present anywhere in the parent chain.`,
Example: ` weknora unlink # remove the binding for this project
weknora unlink --format json # bare JSON (CI / agents)`,
Args: cobra.NoArgs,
RunE: func(c *cobra.Command, _ []string) error {
fopts, err := cmdutil.CheckFormatFlag(c)
if err != nil {
return err
}
fopts.ResolveDefault(iostreams.IO.IsStdoutTTY())
return runUnlink(opts, fopts)
},
}
cmdutil.AddFormatFlag(cmd, unlinkFields...)
return cmd
}
func runUnlink(opts *UnlinkOptions, fopts *cmdutil.FormatOptions) error {
cwd, err := os.Getwd()
if err != nil {
return cmdutil.Wrapf(cmdutil.CodeLocalFileIO, err, "get cwd")
}
linkPath, found, err := projectlink.Discover(cwd)
if err != nil {
return cmdutil.Wrapf(cmdutil.CodeLocalFileIO, err, "discover project link")
}
if !found {
return &cmdutil.Error{
Code: cmdutil.CodeInputInvalidArgument,
Message: fmt.Sprintf("no %s/%s found at or above %s", projectlink.DirName, projectlink.FileName, cwd),
Hint: "run `weknora link --kb <id>` first, or check you're in the right directory",
}
}
if err := projectlink.Remove(linkPath); err != nil {
return cmdutil.Wrapf(cmdutil.CodeLocalFileIO, err, "remove %s", linkPath)
}
if fopts.WantsJSON() {
return fopts.Emit(iostreams.IO.Out, unlinkResult{ProjectLinkPath: linkPath})
}
fmt.Fprintf(iostreams.IO.Out, "✓ Unlinked %s\n", linkPath)
return nil
}