Commit Graph

6 Commits

Author SHA1 Message Date
nullkey ad150c7126 refactor(cli): envelope sweep — Emit shape, batch ops, MCP StructuredContent, api flag cleanup
Sweep every CLI output path through the v0.7 envelope contract.

FormatOptions.Emit signature changes to (w, data, meta *output.Meta)
so list commands surface meta.count / has_more in the wire envelope.
TTY indent decision plumbed via FormatOptions.TTY (set by
ResolveDefault). --format and --jq promoted to persistent root flags
so unknown-subcommand paths still reach the typed-envelope guard
(per-command registration would have rejected --format on unknown
commands as cobra-prose exit 2). WEKNORA_FORMAT env-var fallback
honored before TTY-resolved default. AddFormatFlag retained for the
per-command --jq field-hint Long-text addendum.

~55 production Emit call sites swept across cli/cmd/**: list paths
populate meta.count; mutation paths pass nil meta; envelope.profile
threaded via globalProfile. Existing JSON-shape tests migrated to
decode the envelope wrapper.

Multi-target batch operations get a unified shape via new helpers:
- output.BatchItem + output.WriteBatchEnvelope (§4.5 per-item ok
  pattern; data:[{id, ok, result?|error?}], meta:{count, successes*,
  failures*}; Successes/Failures *int so explicit zero survives
  omitempty in all-fail case).
- cmdutil.RunBatch + cmdutil.EmitBatch + cmdutil.DeletedAtNow +
  cmdutil.ClassifyContextErr collapse the three previously-duplicated
  multi-delete pipelines (doc / chunk / session) into one call site
  pattern. doc upload --recursive uses the same helpers with a per-
  file resultFn. Summary error sets Silent:true so the stderr
  envelope path doesn't duplicate the stdout batch detail (exit 1
  preserved via Code → ExitCode).

ConfirmDestructive / ConfirmDestructiveBatch extended with action +
retryCmd parameters. Every destructive command (kb delete, kb edit,
doc delete, doc delete --all, agent delete, agent edit, session
delete, chunk delete, auth logout, api -X DELETE) now attaches
error.risk.{level:"destructive", action:"<noun.verb>"} +
error.retry_command on the exit-10 envelope. kb edit / agent edit
also gain the destructive guard wiring (CHANGELOG listed them as
gated but they were silently executing without -y).

MCP tool handlers' 31 error paths return CallToolResult with
IsError + Content text fallback + StructuredContent (envelope.error
shape via cmdutil.ErrorToDetail). Handler Out type changed from
typed *sdk.X to any so the go-sdk auto-marshal doesn't overwrite
StructuredContent with a zero-struct on error returns. Success path
manually populates CallToolResult.StructuredContent via successResult
helper.

weknora api cleanup:
- -d/--data flag removed; body via --input <file> or --input -
  (stdin) only. Aligns with gh / curl convention.
- HTTP method whitelist removed; any non-empty method accepted.
- HTTP Retry-After header → cmdutil.Error.RetryAfterSeconds →
  envelope.error.retry_after_seconds.
- runAPIPaginated raw-passthrough fallback now wraps response in
  envelope so --paginate never emits bare JSON to stdout.

Test sweep: ~60 cmd/** tests migrated to decode envelope shape.
9 wire-contract goldens updated under cli/acceptance/testdata/wire/.

Spec: docs/superpowers/specs/2026-05-20-weknora-cli-v0.7-design.md §4 / §5
2026-05-27 10:56:34 +08:00
nullkey 34bb0b5096 feat(cli): doc delete multi-id + api --paginate + paginate fixes + batch deletes
Batch-write surface and pagination consistency:

* weknora doc delete <doc-id> [<doc-id>...] — positional multi-id, default
  keep-going on failure. Single -y confirms entire batch.
* weknora session delete <session-id> [<session-id>...] — same shape.
* weknora chunk delete <chunk-id> [<chunk-id>...] --doc <doc-id> — multi-id
  with shared --doc parent.
* Multi-id partial-failure rolls up as operation.failed (exit 1), not
  server.error (exit 7) — failures are operation outcomes, not transient
  transport issues, and the retry-with-backoff hint for server.* would
  mislead callers.
* weknora api <path> --paginate — auto-walks offset pagination and merges
  pages into a single {data, total} JSON response.
* Paginate truncation fix across 6 list/follower call sites.
* All doc / search / chunk / session / kb list commands migrated to
  FormatOptions API.

Multi-id RunE only emits the {ok, failed} envelope when the operation
actually ran — pre-flight failures (e.g. confirmation_required) leave
stdout empty per the wire contract.

doc upload's missing-positional-or-flag check is wrapped as FlagError so
the exit code (2) matches the convention used by other commands that
require a positional argument directly.
2026-05-18 11:10:19 +08:00
nullkey 69fbbfc252 chore(cli): gofmt sweep
`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).
2026-05-15 12:03:56 +08:00
nullkey cc8254f862 refactor(cli): drop --dry-run + introduce bare-JSON output path
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.
2026-05-15 12:03:56 +08:00
nullkey 1b20b06f5e feat(cli): --json field-select, --jq, auth token, doc --from-url
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).
2026-05-15 12:03:56 +08:00
nullkey 2f8681b48e feat(cli): session subtree + kb edit / pin / empty
Roadmap items 3-5 (session) and 3-6/7/8 (kb manage).

cli/cmd/session/ (new package; sessioncmd to avoid shadowing stdlib):
- session list: paginated table (ID/TITLE/UPDATED). --page / --page-size
  with 1..1000 validation. _meta.has_more from page*size < total.
- session view <id>: prints metadata; non-empty fields only. Server
  timestamps arrive as strings; parsed best-effort as RFC3339.
- session delete <id>: high-risk-write; exit-10 confirmation in non-
  TTY/--json paths; --dry-run emits envelope.risk + dry_run:true.

cli/cmd/kb (extended):
- kb edit <id> [--name N] [--description D]: at least one flag required;
  *string options so unset fields stay unset in the PUT body. SDK
  UpdateKnowledgeBaseRequest has no embedding_model field, so the
  roadmap's --embedding-model dropped.
- kb pin <id> / kb unpin <id>: direct parity with gh issue pin /
  gh issue unpin (verified against gh manual). Idempotent: GetKnowledgeBase
  reads IsPinned, TogglePinKnowledgeBase fires only on state change.
  SDK KnowledgeBase struct gained the IsPinned field (server already
  returned it; SDK just hadn't modeled it — non-breaking additive).
- kb empty <id>: high-risk-write; exit-10 confirmation;
  --dry-run. Returns deleted_count from the async clear response.
  weknora-specific operation; no mainstream parallel.

Golden envelopes for kb_list and kb_view updated to include the new
is_pinned field — strict-additive change.

Cleanups surfaced by the post-commit reviewer round:
- ConfirmPrompter promoted to cli/internal/testutil/ (4-copy threshold
  reached: context/remove, kb/delete, kb/empty, session/delete).
  kb/delete_test.go's pre-existing local copy left untouched per the
  upstream-respect convention.
- kb pin/unpin idempotent no-op path no longer emits a write-class
  envelope. Added _meta.warnings "already {un}pinned — no server
  call made" and dropped the risk classification on the no-op branch.
- doc list --page-size was unbounded while session list enforces
  1..1000. Same validation added to doc list.

18 + 18 unit tests; e2e exit codes verified.

Roadmap: 3-5, 3-6, 3-7, 3-8.
2026-05-14 10:57:17 +08:00