ccf02e1a2a4c2ddfd3047ce103ffbd3f9039b601
8 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2ce348d020 |
feat(cli): --format json default + NDJSON event stream + context→profile cascade + help calibration + docs (BREAKING)
D1 — --format default flipped to json regardless of TTY: - v0.6: smart default (text on TTY, json on pipe). - v0.7: always json; TTY only affects indent (compact in pipe). Enum values unchanged (text | json | ndjson). - Typed FormatMode enum replaces untyped string consts. - --format / --jq promoted to persistent root flags so unknown- subcommand paths still reach the typed-envelope guard (per-command registration in v0.6 would have rejected --format on unknown commands as cobra-prose exit 2). - WEKNORA_FORMAT env var added; precedence --format > env > default. Invalid env values silently ignored. D2 — chat / session ask default to NDJSON event-stream: - New cli/internal/output/ndjson_stream.go: InitEvent struct + EmitInit / EmitSDKEvent / WriteNDJSONLine helpers. EmitInit doc encodes the must-be-first-line invariant agents key on. - chat / session ask: --format json AND --format ndjson both emit one JSON event per line (no envelope wrapping). CLI injects exactly one `init` event at stream head carrying session_id + optional kb_id / agent_id / profile. Subsequent events pass through verbatim from the SDK (passthrough discipline per spec §5.1). - --format text keeps the SSE-style live renderer. context → profile full cascade: - Command group: cli/cmd/context/ → cli/cmd/profile/ (git mv; package contextcmd → profilecmd). - Global flag --context → --profile. Factory.ContextOverride → ProfileOverride. WEKNORA_PROFILE env var honored (--profile flag > env > config.CurrentContext). When --profile or WEKNORA_PROFILE references a missing profile, the error is input.invalid_argument with hint "weknora profile list" — not the destructive local.config_corrupt path (which would have told users to delete their config file). - Binding file .weknora/project.yaml field context: → profile: (no backwards-compat alias; re-run weknora link). - profile use JSON fields current_context / previous_context → current_profile / previous_profile. - weknora link JSON field context → profile. - CodeLocalContextNotFound → CodeLocalProfileNotFound (typed code rename). - Envelope top-level profile field populated via globalProfile (set by root PersistentPreRunE from Factory.ActiveProfile). chat / session ask NDJSON init event carries the same profile. - Rationale: "context" collided with LLM context window / RAG context / Go context.Context; mainstream multi-credential CLIs (AWS / Stripe / OpenAI / Anthropic / lark) all use "profile". H2/C1' help calibration: - AgentHelp gains Warnings []string; single SetAgentHelp helper routes on WEKNORA_AGENT_HELP=1 (emits JSON blob including warnings) vs human help (appends "AI agents:" block from same source). Warnings surface as both a structured JSON field and visible help-text addendum without drift. - 9 destructive commands carry warnings: kb / doc / agent / session / chunk delete; profile remove; kb / agent edit; auth logout. - weknora doc wait dedups ids at entry; SIGINT mid-wait returns silently (root signal handler maps to exit 130) instead of being miscategorised as operation.timeout / operation.failed. A4 — docs: - cli/AGENTS.md gains four agent-facing sections: Wire contract for AI agents (stdout / stderr / NDJSON / _notice evolution / SDK contract boundary); Deliberate deviations + mainstream alignments; Pre-1.0 breaking policy; Exit-10 anti-patterns. ERROR_REFERENCE table extended. - cli/README.md adds Agent quick start under Wire contract. - cli/CHANGELOG.md v0.7 section: BREAKING entries with migration notes, Added (WEKNORA_FORMAT / WEKNORA_PROFILE / retry_command / retry_after_seconds / risk / _notice reserved infra / meta.count / meta.has_more / doc fetch / doc create / session ask / doc delete --all / NDJSON init), Changed (docs additions), Deprecated (none — pre-release one-shot breaking). Spec: docs/superpowers/specs/2026-05-20-weknora-cli-v0.7-design.md §3 / §4 / §5 / §6 / §11 |
||
|
|
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
|
||
|
|
7eeb3bec5d |
feat(cli): doc wait command (multi-target wait-all)
weknora doc wait <doc-id> [<doc-id>...] blocks until every given document reaches a terminal parse_status (completed / failed), --timeout expires, or the user interrupts (SIGINT). * --timeout DURATION (default 10m; exit 124 on timeout, matches GNU timeout(1) convention) * --interval DURATION (default 2s; exponential backoff to 15s + jitter) * Multi-id polled concurrently (max 5 parallel) * Exit code priority 1 > 124 > 0 (failed > timeout > completed) New typed errors: * operation.timeout → exit 124 * operation.failed → exit 1 * operation.cancelled → exit 1 (main raises to 130 on signal) server.session_create_failed gets a special case in ExitCode to map to exit 1 (workflow failure, not transient retry). doc view and doc download positional id namespaced to <doc-id>. |
||
|
|
f89d54362d |
feat(cli): doc/kb resource expansion — upload flags + list filters + view fields
Closes deep-tuning gaps in v0.4-shipped doc / kb / session commands. Each command had multiple SDK fields the CLI silently hardcoded or omitted; this commit threads them through. doc upload: - --enable-multimodel (tri-state via NoOptDefVal): toggle multimodal extraction (PDF/DOCX image-to-text) per upload. Empty explicit value (e.g. --enable-multimodel="" from uninterpolated shell var) rejected as input.invalid_argument rather than silently coerced. - --metadata key=value (repeatable): attach arbitrary metadata - --channel <name> (default api, override for browser / wechat ingests) - URL mode gains --title / --file-type / --tag-id; URL-only flags rejected with input.invalid_argument when used without --from-url doc list filter flags: - --keyword (server-side LIKE — case-sensitive per PG) - --file-type / --source / --tag-id - --start-time / --end-time (RFC3339) search docs switches from client-side substring to server-side Keyword via ListKnowledgeWithFilter — smaller wire payload, but case-sensitivity shifts (documented in CHANGELOG + help text + Long). MCP doc_list schema gains the same 6 filter fields (parity with CLI). session view --full + --limit: loads chat history via LoadMessages SDK method. kb view human KV expanded: type / pinned / temporary / processing state + count / summary model / created timestamp. All omit-empty. doc view human KV expanded: title (when distinct from filename) / description / source / channel / summary_status / enable_status / tag_id / storage (human bytes) / file_hash (12-char prefix). |
||
|
|
f2e8e3f56c |
refactor(cli): drop aiclient package; align AGENTS.md with mainstream
Survey of 10 mainstream CLIs (gh, lark, stripe, vercel, supabase, aws, azure, gcloud, openai/codex, github-copilot-cli) showed env-gated per-command --help blurbs are a Stripe-only pattern; gh uses env detect for telemetry only, and lark relies on installed agent Skills + MCP. Our cmd/mcp/serve already covers the dominant 2025/26 path, so internal/aiclient/ (136 LOC + 38 callsites) is net maintenance burden without precedent. - Drop internal/aiclient/ entirely (annotations + detect + tests) - Remove 38 SetAgentHelp callsites + agentAwareHelpFunc / SetHelpFunc wiring in cmd/root.go - Migrate 4 command-level rules to standard Long help (visible to all, not env-gated): doc upload mode mutex, kb edit at-least-one, kb pin idempotent, search chunks channel mutex - Rewrite AGENTS.md as a developer guide (gh-style 6 H2 / 167 lines): audience preamble + Build / Architecture / Command Structure / Testing / Code Style / Error Handling. Drops sections absent in surveyed projects (Commit & PR Conventions, Who Uses This CLI) - Clean 14 internal doc refs (ADR-N, spec §X, v0.X) in source comments and docs that pointed at docs/superpowers/ — that directory is local-only / uncommitted, so refs are dead for outside readers - Drop forward-looking "once v0.2 ships" from README |
||
|
|
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.
|
||
|
|
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).
|
||
|
|
35c79281c8 |
feat(cli): doc view + unlink (fill v0.3 design-gap audit)
Final design-pass audit on the v0.3 surface flagged two real gaps. (A) doc view <id> was missing. Every other resource subtree exposes a view verb (kb view, session view) for inspecting a single record, but doc — which has the richest metadata of the three (title, file name, type, size, parse_status, embedding_model, processed_at, error_message) — had no single-doc surface. Users wanting one doc's metadata had to `doc list | grep`. Implementation mirrors kb view: narrow ViewService(GetKnowledge) interface, --json envelope path, human KEY: VALUE renderer. Optional fields are omitted rather than rendered as "-" so the panel is dense. Tested: human renderer, title fallback when FileName empty, omit-empty contract, JSON envelope shape, 404 classification. (B) link had no counterpart. Once .weknora/project.yaml is written, the only way to clear it was `rm` by hand. Both vercel and netlify ship `unlink` as a top-level verb; not having one was a discoverability gap. Top-level rather than `link --clear` follows the verb-noun convention of the rest of the surface — the verb stands alone and the operation isn't parameterised. unlink walks up from cwd via projectlink.Discover (the same parent-chain logic Factory.ResolveKB uses on the read side), so a user in a subdirectory of a linked project can unlink without cd-ing up. Errors with input.invalid_argument when no link is found anywhere in the chain. Idempotent under racy concurrent removal: os.ErrNotExist on os.Remove falls through to a Success envelope since the post-condition holds either way. projectlink package gained Remove() alongside Save / Load / Discover so unlink doesn't reimplement the idempotent-remove pattern inline. Top-level registration in cmd/root.go, alongside link. cli/AGENTS.md verb canon line adds unlink to the locally-introduced list. cli/CHANGELOG.md gains an Added entry for each. 5 unit tests for view + 4 for unlink (cwd / walk-up / no-link error / JSON envelope). Full suite green. Intentionally deferred: - session edit (rename a session): sessions auto-name from the first prompt; polish rather than a gap. - link --clear as an alternative to unlink: top-level unlink is the documented form; aliases would just multiply the surface. |