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.
`gofmt -w cli/` — trailing newlines, minor whitespace alignment. Caught
during PR-readiness review (`gofmt -l .` had been non-clean since the
em-dash → ASCII bulk replace earlier in the v0.4 series).
Two intertwined mainstream-alignment moves bundled because they share
the migration target (every command's --json path):
1. Drop --dry-run entirely. Survey of comparable API-wrapper CLIs
(gh, aws, stripe, lark): none expose --dry-run. The mainstream that
does (kubectl/git/helm/ansible) operates on declarative manifests
or local state where the preview is materially different from the
executed action. WeKnora's CLI just echoed the same parameters
that would have gone on the wire — the preview added no real
signal over `--help` + reading the call site. Removes:
- root --dry-run persistent flag + cmdutil/dryrun.go
- DryRun fields + EmitDryRun calls in 12 write commands
- format.Envelope.DryRun field
- 8 corresponding *_test.go cases
- --dry-run mention from README.md and CHANGELOG.md
- "dry_run":false from 16 golden envelopes
2. Migrate every --json output to bare data:
- New format.WriteJSON / WriteJSONFiltered helpers
(cli/internal/format/bare.go) share filterArrayItems /
filterObjectKeys / writeJQ with the (still-live for now) envelope
filter helpers.
- Read commands (kb/doc/session list+view, search chunks/docs/
sessions/kb, auth list/status, agent list/view, context list,
doctor) emit bare arrays / objects on stdout.
- Write commands (kb create/edit/delete/pin/empty, doc upload/
upload_recursive/delete, session delete, auth login/logout/
refresh/token, link/unlink, context add/use/remove, agent
invoke, chat, api, version) emit bare result objects. Risk
classification dropped — the resource + exit code already
convey the action.
Per-command shape changes:
list / search → []T (was {ok, data:{items:[…]}})
view → T (was {ok, data:T, _meta:…})
create / edit → T
delete / pin / etc. → {id, …action result…}
doctor → {summary, checks}
api → {status, headers, body}
_meta dropped on the read path:
pagination (page/page_size/total/has_more) — agents iterate with
--all-pages or accept --limit (gh CLI parity);
kb_id / context echo — caller already knows what it asked for.
Acceptance contract goldens regenerated for the new bare shape.
Error envelope on stdout (PrintErrorEnvelope) stays live for now —
the envelope-infra deletion lands in the next commit.
Output ergonomics:
- `--json` accepts a comma-separated field list (gh-parity); selects
named keys from the per-command payload. Bare `--json` keeps the
full shape.
- `--jq <expr>` evaluates a gojq expression over the JSON; pairs with
`--json field-list` so projection runs before jq.
- `--version` is a global cobra flag in addition to the `version`
subcommand; both render the same line.
- Per-command `--help` now renders the available JSON field list under
"JSON fields available via `--json id,name,...`" (field-discovery
parity with gh / kubectl `-o jsonpath`).
New commands:
- `auth token` — print the active context's credential to stdout for
shell command substitution (`WEKNORA_TOKEN=$(weknora auth token)`).
Default: raw secret, no trailing newline. `--json` emits
`{token, mode, context}`.
- `doc upload --from-url <URL>` — ingest a remote URL via the SDK
`CreateKnowledgeFromURL`. `--name` forwarded as `FileName` so the
server's known-extension heuristic upgrades crawl-mode to
file-download-mode where appropriate.
Includes the simplify post-review polish pass (field-filter unit
tests, --json/--jq compose check, agent_help copy fixes).
New v0.3 P0 entry 3-4: kubectl-style context-management subtree using
gh's `<noun> <verb>` surface convention consistent with the rest of
this CLI.
- context list: tabwriter rendering + --json envelope; reads config.yaml
only.
- context add <name> --host <url> [--user]: validates http(s) URL, first
context auto-becomes current, rejects duplicates with did-you-mean.
- context remove <name>: best-effort keyring cleanup like `auth logout`.
Removing the current context triggers exit-10 confirmation (lark-cli
skill protocol) — subsequent commands would lose their default
--context.
(`context use` predates v0.3; the subtree was previously use-only.)
Bugs caught and fixed inline by the post-commit reviewer round:
- auth login was accepting `http://` (empty host portion) because the
old validateHost only checked the scheme. New cmdutil.NormalizeHost
(shared by both login and context add) requires u.Host != "".
- context add's validateName claimed `..` was rejected but only denied
/ \\ space. Switched to positive allowlist [A-Za-z0-9._-] plus
explicit ./../path-separator rejection.
Helper consolidation:
- cli/internal/cmdutil/host.go: NormalizeHost (trim, scheme, host
non-empty) — both auth login and context add share it.
- cli/internal/format/dash.go: DashIfEmpty — promoted from copies in
cmd/auth/list.go and cmd/context/list.go.
- recordingStore test stub dropped in favor of secrets.NewMemStore;
contextKeyList test helper replaced by the existing contextKeys.
14 unit tests; 13 e2e branches verified.
Roadmap: 3-4.