Commit Graph

5 Commits

Author SHA1 Message Date
nullkey 8bcbf5a154 refactor(cli): align command surface with mainstream conventions
Empirical mainstream-CLI surveys (gh / kubectl / aws / gcloud / stripe /
flyctl / terraform / vercel / netlify / lark) drove five alignment
fixes — each replaces a weknora-only design choice that mainstream CLIs
do not share. No backwards-compat shims; the CLI has no v0.1 users yet.

1. Single --kb flag (was --kb-id + --kb mutually exclusive)

   Survey: 0/7 mainstream CLIs use two parallel flags for "by id" vs
   "by name". Single flag (gh -R, gcloud --project) or positional
   (kubectl, stripe, terraform). Closest analog — gcloud --project —
   collapses identifier types onto one flag.

   Now: every command exposes one --kb flag; client-side prefix
   detection (cmdutil.IsKBID looks for "kb_") routes id-form values
   through directly and name-form values through ListKnowledgeBases.
   Mirrors gcloud --project's id-or-name auto-detection.

   Touched: search, chat, doc list / upload / delete, link.
   Factory.ResolveKB chain trimmed from 5 levels to 4.

2. link supersedes init

   Survey: only vercel and netlify ship both `init` AND `link` as
   siblings, and they keep them semantically distinct. weknora's pair
   wrote the same .weknora/project.yaml file with the same meaning,
   differentiated only by interactivity — that's a flag concern, not
   a command concern.

   Now: cmd/init/ deleted. cmd/link absorbs the interactive flow:
     - link --kb <id-or-name>  → non-interactive write
     - link on a TTY            → interactive prompt (lists KBs)
     - link non-TTY without --kb → CodeKBIDRequired
   Always overwrites silently (matches vercel link / netlify link /
   kubectl apply rather than git init's refuse-if-exists).

   Dead code purged: --force flag, CodeProjectAlreadyLinked error code.

3. whoami dropped

   Survey: 7/7 mainstream CLIs ship exactly one identity command —
   never both a status and a whoami. gh / gcloud / stripe pick status
   (config + live API); aws / kubectl / flyctl pick whoami (live API).

   weknora's auth status was already a superset of whoami (host +
   context + user + email + tenant_id + tenant_name vs user_id +
   tenant_id), so dropping whoami preserves all functionality and
   aligns with the gh / gcloud / stripe form.

4. kb get alias dropped

   `view` was already primary (gh repo view / gh pr view convention);
   `get` was kept as a cobra alias for v0.0/v0.1 callers. With no
   v0.0/v0.1 users to break, the alias is just noise on the command
   surface. Acceptance contract envelope cases renamed kb_get.* →
   kb_view.*; goldens renamed in lockstep.

5. api refactored to gh shape (-X/--method, default GET, auto-POST)

   gh CLI's signature is `gh api <endpoint> [--method M]` — single
   positional path, method as a flag, default GET, auto-promoted to
   POST when a body is supplied. weknora's previous `api <method>
   <path>` inverted this and forced the method to be passed even for
   GET — a needless deviation from our declared north star.

   Now: `api <path> [-X METHOD] [--data ...]`. Exit-10 protocol
   on the DELETE escape-hatch is preserved; -X DELETE still hits
   ConfirmDestructive when -y absent.

Plus: AGENTS.md gains an explicit note that `doctor` is a deliberate
divergence from gh / lark — borrowed from `flutter doctor` / `brew
doctor` because RAG deployments routinely break on misconfigured
embeddings / storage / credentials and a 4-status structured envelope
is the cleanest surface for it.

Tests: 24 cli packages green (was 26 in PR-14; init + whoami packages
removed). Acceptance contract envelope cases for whoami removed,
kb_get → kb_view renamed, search args / mock path updated for the
kb_<id> form. e2e harness flag args updated. Factory.ResolveKB tests
rewritten for the single-flag shape. api_test driver updated for the
positional-path / -X-method shape.
2026-05-12 13:20:42 +08:00
nullkey da9faa9e07 feat(cli): add agent-first affordance — envelope, exit-10, --dry-run
Borrows the lark-cli agent-affordance model
(https://github.com/larksuite/cli/blob/main/AGENTS.md +
skills/lark-shared/SKILL.md) so weknora is designed to be agent-friendly:
error messages, output format, and flag design follow conventions agents
can rely on.

cli/AGENTS.md (operational reference for LLM agents invoking weknora):
  Public document covering envelope schema, exit-code protocol
  (0/1/2/10/130), stdout/stderr separation, and behavioral rules.
  Sensitive commands (\`context use\`, \`kb delete\`, \`doc delete\`, \`init\`)
  gain "AI agents:" paragraphs in their cobra Long descriptions so
  guidance shows in --help.

format.Envelope schema additions:
  Risk    per-operation classification (read / write / high-risk-write +
          action description), populated by write commands on both success
          and failure paths.
  Notice  system advisories (CLI update available, server-CLI version
          skew); type defined, emit sites land in v0.3.
  DryRun  marker for envelopes returned from --dry-run preview paths.

  RiskLevel constants realigned to lark's taxonomy: read / write /
  high-risk-write (was: read / mutating / destructive — not yet wired by
  any command).

  cmdutil.Error gains OperationRisk; PrintErrorEnvelope auto-attaches it
  to envelope.Risk so destructive failure paths surface uniformly.

Exit-10 confirmation protocol:
  New ErrorCode \`input.confirmation_required\` mapped to exit code 10 in
  cmdutil.ExitCode. ConfirmDestructive now returns this code (with
  OperationRisk attached) when stdout is non-TTY or --json was set, with
  -y/--yes absent. Previous behavior — silent proceed in non-TTY — was
  unsafe: scripts and agents could delete resources with no explicit
  approval. Three test cases re-pinned around the new contract.

  This is a wire-contract change for any caller who relied on silent
  proceed; v0.0/v0.1 had no destructive commands, so the blast radius is
  contained to v0.2 itself.

--dry-run global flag:
  cmd write paths (kb create/delete, doc upload/delete, api POST/PUT/PATCH/
  DELETE) check cmdutil.IsDryRun(cmd) and skip the SDK call, emitting an
  envelope with dry_run=true plus a Risk classification. Read commands
  ignore --dry-run by design (no side effect to preview). Human-mode
  prints \`[dry-run] would <action>\` to stdout.

Command discovery: agents introspect via the existing \`--help\` surface
(consistent with gh / kubectl / aws / gcloud / terraform — none of them
ship a CLI-tree self-description command). An earlier draft added a
\`weknora schema\` reflection command; dropped after a mainstream survey
found it has no stable analog (lark-cli's schema describes Lark API
methods, not its own CLI tree).

Tests: 27 cli packages pass at this commit. Added two new tests covering
envelope.risk and envelope._notice serialization.
2026-05-12 13:20:42 +08:00
nullkey 19afd5eed9 feat(cli): add project-link foundation with init and link commands
v0.2 grounds the CLI's resource commands (kb / doc / chat / query) in a
per-project link file (.weknora/project.yaml) so users don't have to pass
--kb-id on every invocation. Mirrors npm/cargo/git: walk up the cwd tree
to find the project root, override via flag or env when needed.

This commit ships the foundation layer:

  - cli/internal/projectlink/  Discover (walk-up, depth=64) / Load / Save
  - cmdutil.Factory.ResolveKB  5-level fallback chain:
      --kb-id flag → --kb name (ListKnowledgeBases lookup) →
      WEKNORA_KB_ID env → walk-up project link → CodeKBIDRequired
  - cmdutil.ResolveKBNameToID   shared name→id helper used by init / link
                                / Factory.ResolveKB (was duplicated 3 ways
                                in early implementation; reuse review #2)
  - cli/cmd/init/               interactive (huh prompt) or flag-driven
                                first-time setup; refuses to overwrite
                                without --force
  - cli/cmd/link/               non-interactive update; --kb-id and --kb
                                are mutually exclusive and one is required

Also registers the v0.2 ErrorCode set (all codes for the eight new
commands) and AST-scan identToErrorCode mapping in one place — keeps the
acceptance/contract suite green across the v0.2 commit chain even before
later commits reference each code.
2026-05-12 13:20:42 +08:00
nullkey bb592a59a6 feat(cli): contract test suite + dependabot (PR-8)
cli/acceptance/contract/:
  envelope_test.go    — 16 envelope golden cases (9 commands × {success/error
                        variants}; 3 cases dropped with rationale: doctor.success
                        non-offline has unstable timing detail; auth_login.* needs
                        stdin/keyring scaffold deferred to v0.2; context_use.error
                        needs leaf-local --json deferred to follow-up)
  errorcodes_test.go  — single-direction AST scan of cli/cmd/ extracting first
                        arg of cmdutil.NewError / cmdutil.Wrapf calls;
                        ClassifyHTTPError dynamic-classify bridged via
                        cmdutil.ClassifyHTTPErrorOutputs() per spec §4.3.
  testdata/envelopes/ — 16 JSON golden files

helpers_test.go (PR-6 scaffold) extended:
  runCmd now wires cobra Out/Err sinks (version uses c.OutOrStdout) AND
  replicates cmd.Execute()'s error-envelope path so error-case goldens are
  populated. Without this, every error scenario's golden was 0 bytes.

cli/cmd/root.go: mapCobraError → MapCobraError, wantsJSONOutput → WantsJSONOutput
                 (exported so the contract test helper can replicate Execute()'s
                 envelope-printing path without calling Execute() itself).
                 root_test.go updated to use new exported names.

.github/dependabot.yml (新增):gomod /cli + github-actions weekly,gh-style
                              ignore semver-major to avoid noise. Open-source
                              dependency safety,independent of release cadence.

v0.1 不发布到任何分发平台 (release infra 推迟到发布窗口 milestone)。
2026-05-09 12:18:01 +08:00
nullkey a39805aa89 feat(cli): scaffold weknora CLI module foundation (PR-1)
Empty cli/ Go module with Factory + Options skeleton that all v0.x commands
plug into.

Foundation packages (10 internal/):
- cmdutil: Factory(3 closure: Config/Client/Prompter) + Options + Errors
  with namespaced ErrorCode + typed predicates (IsAuthError / IsNotFound /
  IsTransient / IsAuthExpired) + Exporter + AddJSONFlags + ExitCode/PrintError
- iostreams: package singleton with TTY detection; ColorEnabled honors
  NO_COLOR / TERM=dumb on demand
- agent: mode + detect + annotations (Stripe pkg/useragent pattern;
  CLAUDECODE / CURSOR_AGENT / CODEX_* / AIDER_* / CONTINUE_* /
  OPENCODE_* / GEMINICODER env detection)
- build: ldflags-injected version/commit/date
- safepaths: separator-aware path-traversal protection
- config: yaml.v3 schema + atomic save 0600 (XDG-style)
- format: success/failure envelope (code/hint/request_id/risk/retryable/
  console_url) with typed RiskLevel
- prompt: Prompter interface + AgentPrompter (rejects in agent mode)
- secrets: file 0600 store with weknora:<context>:<key> namespace
- testutil: XDGTempDir(t) test helper

Smoke command: weknora version (-/--json). Build/test/vet matrix in
.github/workflows/cli.yml across linux x macos x windows x Go 1.24.

Factory.Client returns CodeLocalUnimplemented until PR-3 wires the SDK
adapter; PR-1 commands (version) don't need it.
2026-05-07 23:19:46 +08:00