Client methods gained optional ...ResourceURLOptions, which broke
CLI service interfaces used for compile-time Client assignability and
caused `go build ./...` to fail on main. Update interfaces and fakes
so ChatService/ListService/AskService/ResumeService/ViewService/MCP
match the SDK again.
When agent update hit exit-10 confirmation, retry_argv dropped
--add-kb/--remove-kb/--system-prompt-file/--config-file, so re-running
after approval could not reproduce the original update.
Expand pflag SliceValue flags as repeated --flag value pairs in
BuildRetryArgv, and include the missing agent update flags in the
allow list. Fixes#2597.
Add functionality to normalize temporary attachment IDs, ensuring deduplication and enforcing a maximum limit of 5 attachments per message. Update related handlers and tests to support this new behavior, enhancing the robustness of attachment management in chat sessions.
Update the classification logic for follow-up questions to include reasoning about previously described images or documents. Ensure that follow-up queries referencing earlier attachments are correctly identified and processed without triggering unnecessary knowledge base searches. This change improves the model's ability to maintain context and respond accurately to user inquiries related to past interactions.
A hardening + finalization pass over the agent-first CLI: correctness fixes,
richer machine-readable signals, flag/naming consistency, and a symmetric
config surface. Pre-1.0, so it includes breaking renames.
Correctness:
- agent update: resolve/validate --model/--rerank-model (was storing a bogus
name verbatim, corrupting config.model_id).
- doctor: honor WEKNORA_HOST / WEKNORA_API_KEY (headless path no longer reports
"no host configured").
- session ask / MCP session_ask: text answer was empty on non-TTY — the agent
stream sets Done=true on an intermediate agent_query frame before the answer,
and AgentAccumulator treated the first Done as terminal. Terminate on the
`complete` event (new sdk.AgentResponseTypeComplete), not a per-frame Done.
- batch exit codes: any per-item failure collapses to operation.failed (exit 1),
including `doc upload --recursive` partial failures — a permanent per-file
failure (e.g. a duplicate) no longer surfaces as a retryable exit 7 an agent
would loop on; per-item typed errors stay in the envelope.
Agent-first signals & discovery:
- error.exit_code in the envelope (type + exit_code disambiguate the
input.invalid_argument exit-2-vs-5 split in one JSON read).
- meta.hint on empty content search and on draft doc create; doc wait fails fast
on a never-parsing draft instead of hanging to --timeout.
- retrieval-readiness is visible in the natural flow: kb status / kb check emit
retrieval_ready, and kb create hints the fix when no embedding model is bound
— an unconfigured KB no longer looks silently healthy.
- schema contract completeness: every leaf declares output + >=1 example
(drift-guarded); output strings match the meta actually emitted; chunk list
and search docs now emit meta.total_count (both previously dropped it).
- schema tolerates a quoted multi-word command label; zero-state auth — and
`link` with no profile — point at profile setup / the headless WEKNORA_KB_ID
path instead of looping on `auth login`.
- id-addressed reads tolerate a redundant --kb (doc view/wait, chunk list/view
accept and ignore it, declared in schema) so a carried-over --kb doesn't
exit 2; streaming commands warn that --jq does not apply to an NDJSON stream.
- keep JSON-always as the default; --jq hints spell out the .data path.
Consistency & gating:
- doc create: drop the deprecated --name alias (--title only; pre-1.0 break).
- chunk list --limit aligned to 1..10000; model list --limit/-L with
has_more/total_count; api write-gates -X PUT/PATCH (exit 10); skills install
expands a leading ~.
- docs corrected: search docs / doc list --keyword help is case-insensitive
(server does LOWER LIKE); AGENTS.md risk-action list (no phantom kb.init; add
model.update / kb.config.set) and batch example (failed item carries `error`);
session resume --message id comes from `message list`, not the stream.
- auth/profile ergonomics: env credentials are now first-class — `auth token`
prints the active WEKNORA_API_KEY / WEKNORA_TOKEN, and auth login/logout/refresh
give an env-aware message instead of looping on "run auth login". `auth logout`
clears credentials but keeps the profile registered (host preserved for
re-login); deleting a profile is `profile remove`'s job (clean logout/remove
separation, matching gh / lark).
Config surface (symmetric read/write, in-place model edits):
- kb config now returns a secret-free KBModelConfigView (was {}); `kb config`
reads, new `kb config set` writes; `kb init` removed (misnomer).
- kb create --chat-model: retrieval-ready in one step.
- model update: edit a model in place (id preserved, references survive) —
rotate --api-key-stdin, change base-url / display-name / etc.
- session continue-stream renamed to session resume.
Docs: AGENTS.md is the single wire-contract source; CHANGELOG slimmed; stale
kb-init / continue-stream references removed; skill wire-vocab guard extended.
AGENTS.md / weknora-shared SKILL document retrieval_ready (a KB needs an
embedding model to be searchable), that --jq does not apply to NDJSON
streams, and the env-credential-first auth path; the KB quickstart example
now creates a retrieval-ready KB.
Follow up on #1740/#1741 by typing terminal SSE errors in the SDK,
classifying them as server.error in the CLI/MCP, marking Projector done
on error frames, attaching session_id to MCP stream failures, and
migrating the e2e chat step to the bounded JSON envelope.
- Updated the CreateKnowledgeFromFile function to accept an optional processConfig parameter, enhancing flexibility in knowledge file uploads.
- Modified related service and test files to accommodate the new parameter, ensuring consistent handling across the upload process.
Make the agent-facing contract uniform across the whole surface:
- cobra parse errors (unknown flag, arg-count) emit the JSON envelope by
default, not bare prose — via a single DefaultFormatMode shared by the early
resolver and ResolveDefault.
- a malformed --jq is input.invalid_argument (exit 5), not internal.error.
- destructive-confirmation messages name the real operation (edit/remove,
not a hardcoded "delete").
- `doc wait` emits one envelope (Silent failure, no contradictory stderr);
completed marshals as [] not null.
- WEKNORA_AGENT_HELP covers every leaf command, enforced by a coverage drift
guard; a sibling guard enforces --dry-run on every mutation.
- `search docs`/`search chunks` resolve --kb through the same
flag→env→project-link chain as `doc list`/`chat` (no longer a hard --kb
requirement); a linked dir or WEKNORA_KB_ID now works without --kb, and an
unresolved KB is local.kb_id_required (exit 1). The destructive
`doc delete --all` keeps its explicit-only rule.
- exit-code and batch/wait ok-semantics documented in the shared skill, README,
and AGENTS.md.
Ship weknora-shared and weknora-rag-search Agent Skills (SKILL.md plus
references) so an agent driving the CLI learns the auth flow, output contract,
exit codes, and the chat vs session-ask vs search distinction without scraping
help text.
Add CI drift guards that keep the skills honest: every ```weknora``` block must
reference a live command/flag (skillparity), and every MCP tool named in a
skill must exist in the registry (skill_tool_parity).
Unify the --kb resolver (accepts name or UUID) across kb/doc/chunk/session/
search/chat/link; `agent create` attaches knowledge bases via --attach-kb
(no longer --kb). Rename the MCP tool agent_invoke → session_ask and drop dead
MCP error codes. Add `session stop` (StopSession), completing the
start / continue-stream / stop triad.
Across these commands, bring the agent contract up to par: --dry-run previews
on mutations (incl. kb pin/unpin, session stop), agent-help blobs on the
read/health surface, accurate destructive-confirmation verbs, and search
has_more truncation signalling so an agent can tell its results were capped.
Auth now acts on the active profile instead of per-command --host/--name:
register a target with `profile add <name> --host <url> --use`, then
`auth login`. `auth login --with-token` reads an API key from stdin
(non-interactive, agent default); password login is interactive-only.
`auth token` prints the raw credential by default (clean $(...) capture);
--format json emits the {token,mode,profile} envelope, --jq implies json.
Login mode label corrected (api-key vs bearer). profile use and the auth/
profile read commands gain --dry-run / agent-help where they were missing.
AGENTS.md gains three sections for the v0.8 surfaces:
- Stream recovery — session continue-stream replay-from-0 semantics
and the dedupe contract agents must implement
- Dry-run contract — when --dry-run applies, the meta.{dry_run,plan}
envelope shape, exit-code semantics (no exit 10
on destructive + --dry-run), the GET-reject rule
for `weknora api`, and the validation-parity
guarantee with the live path
- Risk metadata — what the Risk: prefix in --help means and how
cobra.Annotations["risk.{level,action}"] are
populated
README.md gains user-facing Dry-run preview and Resuming streams
sections.
CHANGELOG.md adds the v0.8 entry covering the new --dry-run flag,
MCP Tool.Annotations, session continue-stream, and the Risk: line.
Adds `weknora session continue-stream <session-id> --message <id>` for
re-attaching to an in-progress or already-completed SSE event buffer.
Server semantics (replay-from-0 + tail):
- Every connection replays the full stored event log from index 0,
then tails any new events. NOT cursor-from-disconnect. Agents that
already consumed events on the original stream MUST dedupe by
message_id + event hash to avoid double-processing.
- Buffer TTL: redis mode 1h hardcoded; memory mode = process lifetime.
After expiry the CLI surfaces local.sse_stream_aborted.
Output is NDJSON: one CLI-injected init line carrying
{session_id, message_id, profile} at stream head, then raw SDK
StreamResponse events verbatim. The init line lets agents thread the
resume to the original message in their dedupe table before the first
SDK frame arrives — output.InitEvent gains an omitempty MessageID field
for this purpose; non-resume init events stay unchanged.
The command always emits NDJSON regardless of --format — there is no
human-text use case for raw event-log replay (operator scenarios are
incident response / debugging). --dry-run is excluded for the same
reason streaming commands always are: a buffered plan makes no sense
for an event stream.
Two intertwined agent safety nets that share the same files:
1. --dry-run flag for offline preview of mutation commands
2. Risk: metadata + SetRisk helper for destructive command surfaces
Coverage (19 mutation commands with --dry-run):
kb.create/edit/delete agent.create/edit/delete
doc.create/upload/fetch/delete doc.delete_all (special variant)
session.delete chunk.delete profile.add/remove
auth.refresh/logout link unlink
api.{post,put,patch,delete} (api.get + --dry-run rejected, exit 2)
Envelope additions (omitempty in non-dry-run paths):
meta.dry_run: bool true when --dry-run was used
meta.plan: map {action, args} per the per-command taxonomy
Risk: metadata
--------------
cmdutil.SetRisk(cmd, action) stamps cobra.Annotations with
risk.level=destructive + risk.action=<action> on the 9 destructive
commands. The SetAgentHelp wrapper prepends a "Risk: <action>
(destructive)" line in the default text help path so agents see a clear
warning before parsing Usage. WEKNORA_AGENT_HELP=1 JSON path stays
unchanged — structured agent-help already carries warnings[].
Validation parity with the live path
------------------------------------
Every pure-local validation (flag presence, mutual exclusion, enum
bounds, URL/regex format, ResolveKBLocal for KB resolution that does not
require an SDK call) runs BEFORE the dry-run gate. This matches the
industry-standard "preview shows what live would do" contract:
--dry-run accepts exactly the same invocations the live path accepts and
rejects exactly the same invocations the live path rejects, modulo the
side-effecting work itself.
The side-effecting work (SDK calls, file writes, keyring writes, server-
side name → id resolution) is what --dry-run actually gates. Each
mutation file pairs its RunE validation block with a regression test
under *_dry_run_test.go / dryrun_validation_test.go so future refactors
don't reintroduce the gap.
Helper surface
--------------
- HandleDryRun(cmd, dryRun, plan) extracts the early-return so the
19 RunE call sites stay 3 lines each.
- EmitDryRun routes through FormatOptions.Emit, inheriting _notice /
TTY indent / --jq filtering for free.
- ResolveKBLocal mirrors ResolveKB but never calls the SDK; dry-run
paths use it so the plan reports the raw --kb value (UUID or name)
without a name → id lookup.
Streaming commands (chat, session ask, session continue-stream) are
deliberately excluded: a buffered plan makes no sense for an event
stream.
Lock semantics in the dry-run path:
- destructive + --dry-run: exit 0, no exit-10 confirmation prompt
- --dry-run + -y: byte-identical envelope to --dry-run alone
- --dry-run + --jq: filter applies to the preview envelope normally
Bumps modelcontextprotocol/go-sdk to v1.6.1 and populates Tool.Annotations
on every registered MCP tool per the per-tool hint table:
Read tools (8): destructiveHint=false, readOnlyHint=true,
idempotentHint=true, openWorldHint=false
Invoke tools (2): destructiveHint=false, readOnlyHint=false,
idempotentHint=false, openWorldHint=true
Invoke-class tools (chat, agent_invoke) carry openWorldHint=true because
the server may dispatch external skills (web_search etc.). Read tools are
sealed: idempotent + read-only + closed-world.
TestToolAnnotations_AllToolsHaveExpectedHints asserts the matrix so any
future drift surfaces in CI rather than at first client integration.
Upstream commit 5c0243cd ("feat: Support filtering chunks by multiple
chunk_type params") extended client.Client.ListKnowledgeChunks with a
trailing `chunkTypes ...string` variadic. The two narrow service
interfaces in cli/ — ListService in cli/cmd/chunk/list.go and
chunkListService in cli/internal/mcp/tools.go — plus the two fake
implementations in their *_test.go siblings, are updated to match so
*client.Client continues to satisfy them via duck typing.
No CLI flag surfaced yet; both call sites pass no filter and behavior
is unchanged. Exposing `--chunk-type` as a flag on `weknora chunk list`
and as an input field on the MCP `chunk_list` tool is a candidate for a
follow-up commit, per the broader SDK-required → CLI-required alignment
discussion.
Pre-flight: go build ./... clean; go test -count=1 ./... 28/28 packages
pass; go vet ./... clean; gofmt -l . empty.
Post-review polish on the v0.7 wire / surface contract. Bundles five
follow-ups that landed after the main BREAKING feat commit:
1. Complete context→profile cascade (internal API + YAML schema)
The prior commit renamed only the user-visible surface (commands /
flags / env / project link / envelope field). The internal Go API
and on-disk config schema were still half-renamed — an L-25
self-consistency violation flagged by post-merge review. Closed here:
Internal Go API:
- config.Context → config.Profile
- config.Config.CurrentContext → CurrentProfile
- config.Config.Contexts → Profiles
- LoginOptions.Context → LoginOptions.Profile
- clearContextSecrets() → clearProfileSecrets()
- saveContextRef() → saveProfileRef()
- secrets.Store: param name `context` → `profile` (interface +
FileStore + KeyringStore + MemStore)
- cmdutil.LoadSecret(store, context, key) → LoadSecret(store, profile, key)
- cmdutil.RefreshAndPersist's ctxName → profileName
- Local var `ctx := &config.Profile{...}` → `prof := &config.Profile{...}`
in auth/login.go to eliminate the visual collision with Go stdlib
context.Context that motivated the whole rename in the first place.
On-disk config.yaml schema:
- current_context: → current_profile:
- contexts: → profiles:
- Pre-1.0 break, no compat alias. Users on v0.6 dogfooded configs
must delete ~/.config/weknora/config.yaml or hand-rename the two
keys (CHANGELOG migration note added).
Tests / fixtures / golden files:
- factory_test.go YAML fixture + assertion updated.
- acceptance/e2e/e2e_test.go writeContextYAML → writeProfileYAML,
fixture YAML keys updated.
- acceptance/testdata/wire/doctor.error_network.json golden updated
("active context" → "active profile" in hint string).
User-visible prose sweep:
- cmd/mcp/serve.go --help Long: "active context (or --context)" →
"active profile (or --profile)" — most-visible miss.
- cmd/{kb/list, search/kb, session/list, api/api} Short/Long help.
- cmd/auth/login.go stdout: `(context=%s)` → `(profile=%s)`.
- cmd/auth/logout.go error: `"no current context"` → `"no current profile"`.
- cmd/doctor/doctor.go hint string (also the wire golden above).
- cmd/auth/refresh.go error: `"refresh token missing for context"` →
`"refresh token missing for profile"`.
- README.md: `## Multi-context` H2 → `## Multi-profile`; code-block
comment `# current context` → `# current profile`.
Code-comment / docstring sweep across cli/cmd/auth/ and
cli/internal/cmdutil/. Comments referencing Go stdlib context.Context,
the RAG / LLM "context window" concept, and historical CHANGELOG
entries for v0.4 / v0.5 were left alone.
CHANGELOG v0.7 BREAKING entry gains the on-disk-schema bullet under
the existing "context → profile" item.
2. Profile name validation (shell-injection guard)
`envelope.error.retry_command` is a single shell-string field. An
AI agent that exec()s it via `sh -c <retry_command>` was injectable
through a maliciously-named profile:
weknora auth logout --name 'x; rm -rf ~'
# would produce: retry_command = "weknora auth logout --name x; rm -rf ~ -y"
`cmd/profile/add.go` already enforced an alphanumeric + `-_.`
allowlist via `validateName`. The `auth login` and `auth logout`
paths bypassed it.
- Moved validation from `cmd/profile/add.go` to
`cli/internal/cmdutil/profilename.go` as exported
`ValidateProfileName` (cmdutil is the import-cycle-safe home;
internal/config can't depend on cmdutil).
- `auth login` runs the validator before any persist call.
- `auth logout` runs the validator on `opts.Name` before
constructing `retry_command`.
- Unit tests (`profilename_test.go`) cover the allowlist, empty
rejection, path-traversal, shell metacharacters (`;`, `&`, `|`,
`$()`, backticks, quotes, whitespace, glob, redirects), and the
user-facing hint text. The shell-metachar test exists as a
regression guard.
Wire shape (`retry_command` string → `retry_command_argv []string`)
remains a v0.8 additive change per ROADMAP — this fix removes the
practical exploit path without touching the wire contract.
3. AI-agent terminology disambiguation
"agent" has three referents in this codebase: (a) WeKnora's
server-side Custom Agent resource, (b) the removed `agent invoke`
verb, (c) external LLM/automation consumers. Per project memory
feedback_no_meta_disambiguation_in_docs, the fix is full-term
naming, not "X has N meanings" prose. Surgical changes at section
headers + ambiguous prose:
- AGENTS.md: "Agent decision shortcuts" → "AI agent decision
shortcuts"; "agent-callable surface" → "AI-agent-callable
surface".
- README.md: "Designed to be agent-first" → "AI-agent-first";
"Other agent ergonomics" → "Other AI-agent ergonomics"; "in
agent contexts" → "in AI-agent contexts"; "for CI / agents" →
"for CI / AI agents".
Anaphoric "agents" inside paragraphs that already established
"AI agents" was left alone — full substitution everywhere would
have been prose noise without clarity gain.
4. Wire-contract review follow-ups
Real findings from a second-pass review of the v0.7 envelope /
streaming / surface design. Per project memory
feedback_check_in_domain_anchor_first, candidate findings were
first verified against the in-domain peer CLI explicitly cited as
the envelope anchor; two earlier-flagged issues turned out to be
in-pattern and were withdrawn.
Surviving fixes:
- AGENTS.md success-envelope example rewritten. The prior example
showed `has_more: false` / `_notice: {}` as if they were always
present, but both fields are `omitempty` and never serialize
when zero / nil. Replaced with three realistic shapes (list /
single resource / mutation with no payload) and added a note
that optional fields are omitted when empty.
- cmd/chat/chat.go Args: MinimumNArgs(1) → ExactArgs(1).
v0.6 silently joined `weknora chat hello world` into
`"hello world"`. v0.7 now rejects multi-arg with exit 2,
matching `weknora session ask`. BREAKING; CHANGELOG entry
added under v0.7 BREAKING.
- internal/output/envelope.go extracts NewEnvelope(data, meta,
profile) constructor. The jq-filter path in
cmdutil.FormatOptions.Emit was manually rebuilding the
envelope literal alongside the canonical WriteEnvelope path —
drift risk when fields are added. Single construction point now.
- internal/cmdutil/factory.go adds AddKBFlag(cmd) helper.
Five files (chat, doc/list, doc/upload, doc/create, doc/fetch)
had verbatim-identical `cmd.Flags().String("kb", ...)`
declarations. Centralised so flag name + help text stay
in sync with Factory.ResolveKB. Docstring reordering + gofmt
fixup landed in the same edit to keep ResolveKB's own godoc
attached to its function.
5. OSS-readiness comment / doc sweep
Pre-publication scrub of code, comments, and shipped Markdown to
remove references that only make sense in the development repo:
- AGENTS.md "Deliberate deviations + mainstream alignments"
section: removed peer-project name-drops from the comparison
table; rewrote as five flagged design decisions with rationale
but no specific competitor named. The four rows that previously
contrasted against a named peer CLI now state WeKnora's choice
+ rationale directly. Section header renamed to "Design
decisions worth flagging" since it is no longer a
deviation/alignment matrix.
- CHANGELOG v0.7 BREAKING rationales: three references to a
named peer CLI removed; the context→profile rationale now
cites only mainstream multi-credential CLIs by category (AWS /
Stripe / OpenAI / Anthropic), and the `api -d/--data` removal
rationale cites only `gh api` / `curl`. `chat` BREAKING entry
rationale similarly simplified.
- 35 cross-references to design-spec section numbers (§4.1 /
§4.5 / §5.3 etc.) removed from Go doc comments and test
comments across 13 files. The referenced spec lives outside
the shipped tree; readers of the public repo cannot resolve
them. Each reference replaced with a self-contained semantic
description (e.g. "the batch envelope" / "AGENTS.md section
on the success path").
- Mixed-language strings translated to English:
- Four Go comments: internal/cmdutil/exit.go:213,215,
internal/cmdutil/errors.go:156,
internal/output/batch_test.go:90,
internal/output/envelope_test.go:27.
- One CHANGELOG section title:
`v0.7 — Agent-first wire contract + 命令面集中清理` →
`... + command-surface cleanup`.
- CJK test fixtures (internal/text/truncate_test.go CJK
truncation cases, cmd/session/list_test.go Chinese session
title, acceptance/e2e/e2e_test.go Chinese RAG corpus)
retained — they are intentional test inputs, not stray prose.
- Makefile help comment: `golangci-lint added in PR-9` →
`golangci-lint planned`. Internal PR numbering should not
surface in shipped Makefile prose.
Build green, 28/28 packages, +5 new ValidateProfileName tests.
go vet / gofmt / go mod verify / go mod tidy all clean.
Rationale for the cascade: pre-1.0 is the cheapest moment to close
L-25 self-consistency (L-26). The half-finished internal rename
would have perpetuated the very `context` vs `context.Context`
ambiguity that motivated v0.7's user-visible rename in the first
place.
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
Re-introduce the agent-first symmetric envelope deleted in commit
e623e820 (2026-05-15). Under the v0.7 constraint that AI agents are
primary consumers, the bare-JSON shape can't carry protocol channels
(_notice / risk / meta / request_id / profile) that agents need.
Errors-on-stderr and typed exit codes from e623e820 are preserved;
the envelope wraps the success/error payloads on top.
- cli/internal/output/envelope.go (new): Envelope / ErrorEnvelope /
Meta / ErrDetail / RiskDetail structs; WriteEnvelope +
WriteErrorEnvelope writers; PendingNotice plumbing for the open-map
_notice channel (reserved infra; producer wiring planned for v0.8).
- cli/internal/output/envelope_test.go (new): 7 tests covering
success / error / TTY indent / Notice / Risk shapes.
- cmdutil.Error extended with RetryCommand (directly-executable
argv distinct from prose Hint), RetryAfterSeconds (HTTP
Retry-After), *RiskInfo (nested level+action; ErrInternalServer-
vs-NotFound distinction), Detail (open structured payload),
Silent (suppress stderr emit while preserving Code for ExitCode).
- ErrorToDetail single source of envelope.error construction; reused
by stderr PrintError, MCP StructuredContent, and batch per-item
error path.
- WithHint / WithRetryCommand / WithRetryAfter / WithDetail /
WithRisk builders. IsCancelled helper. AsError unwrap helper.
- defaultHint covers ~21 codes; defaultRetryCommand symmetric
counterpart for 6 keyway codes. DefaultHint / DefaultRetryCommand
exported wrappers for cross-package callers.
- ClassifyHTTPError tightened: rescues HTTP 500 with structured
server-side code 1003 (ErrNotFound) into resource.not_found.
Server's generic 1007 (ErrInternalServer) bucket stays as
server.error — including it would mis-route validation failures
(e.g. SQLSTATE 22001) as not-found.
- PrintError dual-mode: text/human → prose with code:msg / hint /
retry lines; json/ndjson → envelope on stderr. Mode pinned by
root PersistentPreRunE via SetFormatMode. resolveFormatEarly()
scans argv before cobra dispatch so cobra-side validators
(unknown flag, arg-count) still surface as envelope when
--format json is in effect. Silent typed errors short-circuit
the stderr emit. FlagError mapped to input.invalid_argument for
the envelope (exit code stays 2 via FlagError class).
- Unknown-subcommand guard installed recursively at the root:
parents with subcommands but no Run/RunE get a typed RunE that
emits input.unknown_subcommand envelope with
detail.{unknown, command_path, available[]} and
retry_command "<parent> --help". cobra.ArbitraryArgs bypasses
legacyArgs validation so the guard receives unmatched argv.
- cmdutil.Error.Error() returns "<code>: <message>[: <cause>]" for
chain debugging; ErrorToDetail strips the code prefix from
envelope.message since the separate type field carries it
(prevents the doubled-prefix "code: code: ..." that agents would
see).
Spec: docs/superpowers/specs/2026-05-20-weknora-cli-v0.7-design.md §0 / §4
Wire-contract documentation and the CI check that keeps it honest.
* cli/README.md gains a verbatim --help block (top-level + subtrees),
an Exit codes table covering 0/1/2/3/4/5/6/7/10/124/130, a "Status
vs check" verb-pair subtable, and a "doc wait" paragraph spelling out
the four exit codes (0 / 1 / 124 / 130). The api passthrough note
trims storage provider out of the deep-config list now that
kb create --storage-provider is a polished flag.
* cli/AGENTS.md becomes the contributor guide: build/test, CRUD flag
conventions, the status/check verb pattern, long-poll wait commands,
the SetAgentHelp pattern, and a full Error code reference with 35
typed codes mapped to namespaces, exit codes, retryable / hint
guidance. Reference section is bracketed by HTML markers so a CI
parity test can keep it in sync with AllCodes().
* cli/internal/cmdutil/errors_doc_test.go enforces parity: every code
in AllCodes() must appear in AGENTS.md inside the markers, and
AGENTS.md must not reference codes that no longer exist. Fails CI
if a new typed code is added without documentation.
* CHANGELOG.md gets the v0.6 entry: BREAKING (--json / --no-stream /
WEKNORA_SDK_DEBUG / kb create --name), Added (--format / --jq /
doc wait / --log-level / kb-and-agent status & check / multi-id
delete / api --paginate / MCP schema extension / SetAgentHelp /
signal-aware ctx / kb create --storage-provider / new operation.*
namespace), Changed (multi-id partial-failure exit code, doc upload
FlagError, --log-level FlagError, multi-id stdout cleanup, README /
AGENTS.md changes), with a Migration from v0.5 section walking
every BREAKING through its v0.6 replacement.
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.
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.
Adds the structured-output and agent-help surface plus root-level signal
handling so AI agents (and humans working through pipes) get a stable
wire contract.
* --format text|json|ndjson flag, registered per-command on outputs that
need it; default text on TTY, json on pipe. --jq <expr> pairs with json
/ ndjson to filter or project. FormatOptions absorbs JQ; WantsJSON()
helper for the JSON dispatch.
* WriteNDJSON helper in internal/format/ (per ndjson.org: one JSON value
per line, arrays split element-per-line, empty slice → zero bytes).
* chat / agent invoke wire --format ndjson via SDK StreamResponse /
AgentStreamResponse 1:1 passthrough. Both commands detect ctx.Cancelled
in every stream + session-create path and emit a stable
"operation.cancelled" code on Ctrl-C / SIGTERM.
* main.go wires signal.NotifyContext(SIGINT, SIGTERM) into the root
context so long-running commands run their cancellation cleanup
(re-emit auto-created session id, etc); the process exits 130 when
the context was signal-cancelled, matching Unix convention.
* MCP chat / agent_invoke output schemas extended with thinking /
tool_calls / assistant_message_id (server-side accumulated; MCP
tools/call has no standard partial-response). doc_view and doc_download
now use doc_id (not knowledge_id) so agents see a single id naming
convention across all tools — matches the chunk_list / search_chunks
schemas and the CLI's <doc-id> positional.
* SetAgentHelp(cmd, AgentHelp{...}) — opt-in machine-friendly --help
payload activated by WEKNORA_AGENT_HELP=1. Applied to chat / kb list.
* kb create --storage-provider <local|minio|cos|tos|s3|oss|ks3> — sets
the new KB's storage_provider_config.provider at creation time (server
does not expose it on update). Required on self-hosted deployments
where the server-side default doesn't pre-populate a provider —
without it, subsequent doc upload returns a misleading "kb not found".
Code-reuse polish (post-implementation review pass):
- Extract text.OneLine(maxWidth, s) helper combining preview-row
normalization (newline/CR/tab → space) with text.Truncate's
UTF-8-safe truncation. Replaces agent/view.go truncate1Line (ASCII
'...' + byte-slice CJK-unsafe) and chunk/list.go singleLine.
- Lift cmdutil.OpenInput(path) for the '-' = stdin / else os.Open
pattern shared across agent create/edit and the api command.
Replaces agent/create.go's private openInput.
- Strip inline doc-spec parentheticals from source comments — those
belong in commit messages and project docs, not in source where
they rot.
Pre-PR audit fixes:
- doc upload: reject `--metadata` paired with `--from-url` as
input.invalid_argument up-front (the URL-ingest request type has
no metadata field server-side, so the pair would otherwise silently
drop). Long help and CHANGELOG updated to call out the asymmetry.
- doc upload (file path): map sdk.ErrDuplicateFile sentinel to
resource.already_exists. The sentinel arrives with no "HTTP error <n>:"
prefix because the SDK short-circuits on file-hash before reading the
HTTP status, so the previous WrapHTTP fall-through misclassified it
as network.error with a misleading "check base URL reachability" hint.
The --from-url branch already handled ErrDuplicateURL this way; this
closes the asymmetry. Caught by e2e re-upload of an already-ingested
file; regression test added.
- README exit-10 enumeration adds `agent delete` and `chunk delete`
(these were missing alongside the v0.5 destructive verbs they were
meant to gate).
Docs sync:
- cli/README.md: command tree now includes the chunk subtree; adds
agent / chunk lines to the 5-minute quickstart; adds a "Contributing
/ Reporting issues" section pointing at the repo's SECURITY.md and
AGENTS.md; drops third-party CLI parallels from the surface
description.
- cli/AGENTS.md: "Command surface design SOP" gains the
flag-vs-escape-hatch step. "CRUD command flag canon" renamed to the
hard-required-flags pattern with the contrast (TTY-prompts-fill)
defined inline rather than via opaque shorthand.
- cli/CHANGELOG.md: search docs case-sensitivity shift promoted to its
own #### Breaking changes subsection. MCP doc_list filter count
corrected from 5 to 6. Drops the bogus go.mod yaml.v3 entry (yaml.v3
was already a dependency on main; v0.5 added zero go.mod lines).
Replaces internal-Go identifiers (fuzzyTime, NoOptDefVal) with
user-language and drops the § section-symbol jargon.
Brings search docs and search sessions to v0.4 pagination canon
(--limit / --page-size / --all-pages, matching session list / doc list).
Both default --all-pages=true to preserve prior silent walk-all
behavior; explicit knobs added for users who want one-page fetch.
cli/AGENTS.md gains two new sections:
- Command surface design SOP — a 5-step SDK-schema-first pre-design
checklist for future contributors. Earlier spec drafts produced
schema-error classes (missing/mismatched fields, missing pagination
flags) when commands were designed from convention rather than from
the SDK; the SOP makes the SDK the ground truth.
- CRUD command flag canon — Mode A (hard-required + flag error, no
interactive prompts), the established pattern for non-auth CRUD.
Also fixes the agent invoke rationale source: the CLI-layer precedent
for invoke being a separate verb (not a chat mode) is documented
inline rather than referencing other vendor CLI behavior.
Four unrelated shipped-code drifts found during v0.5 audit cycles:
1. MCP search_chunks tool omitted MatchCount from SearchParams. Server
fell back to its default cap; agents asking for limit:50 silently
got fewer results. Adds MatchCount: limit to the struct literal.
2. search sessions printed UpdatedAt as raw RFC3339 while session list
used a fuzzy "X hours ago" render — same SDK field, two human
renderings. Switches to the shared text.FuzzyAgoStr helper for
parity.
3. auth status --json omitted three operationally-meaningful AuthUser
fields (username, is_active, can_access_all_tenants). Agents
branching on can_access_all_tenants previously needed a second
round-trip.
4. session view Long help claimed the SDK doesn't wrap session_messages;
it does (LoadMessages / GetMessagesBefore / GetRecentMessages all
exist in client/message.go). Rewrites the comment to be accurate.
New subtree (chunk list / view / delete) exposes RAG retrieval
debugging primitives with SDK-grounded field set (23 Chunk fields).
Pagination follows v0.4 canon: --limit / --page-size (1..1000) /
--all-pages.
- chunk list --doc <id>: enumerate by ChunkIndex (separate from
search chunks which is hybrid retrieval; Long help documents the
distinction)
- chunk view <id>: scope-less render via /chunks/by-id route; full
content verbatim
- chunk delete <id> --doc <id>: scope-flag + scope-id; L-13
destructive; 404 NOT idempotent; resource.not_found /
auth.forbidden / input.confirmation_required typed exit codes
documented in Long help
MCP server gains chunk_list as 10th curated tool. Schema deliberately
exposes only doc_id + limit (no pagination workflow on MCP); response
includes truncated_at_limit flag when total > limit.
cli/AGENTS.md MCP curation rationale rewritten: curated read-only is
a deliberate product call because the server side does not yet
enforce per-token scope. When server scope ships, mutation tools can
land in the MCP surface.
Shared helper cli/internal/text/timeago_string.go (FuzzyAgoStr)
extracted from session list during the C2 quality-review pass.
`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).
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
Compared the auth subtree (login/logout/list/status/refresh/token)
against gh CLI's auth implementation. Three gaps closed:
1. `auth login --with-token` validates the API key against `/auth/me`
before persisting (mirrors gh's pre-persist GetCurrentLogin probe).
A typo'd / expired / wrong-host key fails fast with
`auth.bad_credential` (exit 3) and nothing is written to the
keyring. Side benefit: api-key contexts now carry the resolved
`user` + `tenant_id` at rest, so `auth list` reflects who owns
the key — previously these columns were blank for `--with-token`
contexts because we never queried the server.
2. `auth login` prints a stderr advisory when the secrets store falls
back to the 0600 plaintext file (keychain unavailable — typical on
headless CI, WSL without DBus, agent containers). `weknora doctor`
carried the same info in its credential_storage check, but users
who go straight to `auth login` could miss it. gh has the same
silent-fallback gap; we're stricter here.
3. AGENTS.md adds an "Auth security contract" section documenting:
- Credential storage (keychain primary, 0600 file fallback)
- `--with-token` reads stdin (not flag value), pre-validated
- No env-var token bypass — by design, to avoid the
`/proc/<pid>/environ` / `ps -E` leak surface that
`GH_TOKEN`-style env vars expose
- `auth status` / `auth list` never emit token values
- `auth refresh --json` returns only `{context}` (never the
new tokens)
- `auth token` stdout has no trailing newline + TTY stderr hint
- `auth logout` is local-only (no server-side revocation)
Verified against gh CLI behavior (cli.github.com manual + cli/cli
trunk source):
| dimension | gh | weknora v0.4 |
|---------------------------------|----------------|--------------|
| pre-persist token validation | ✓ | ✓ (new) |
| OS keychain primary | go-keyring | go-keyring |
| stderr warning on file fallback | ✗ silent | ✓ (new) |
| `auth status` default token | masked prefix | not shown |
| `auth token` TTY warning | ✗ | ✓ |
| env-var token bypass | ✓ (GH_TOKEN) | ✗ by design |
| process-args / `ps` leak surface| ✗ stdin only | ✗ stdin only |
Removes the entire envelope machinery now that every success path
emits bare JSON:
- cli/internal/format/envelope.go (Envelope, Success, Failure,
SuccessWithRisk, WriteEnvelope, Meta, Notice, UpdateNotice,
VersionSkewNotice, Risk, RiskLevel, ErrorBody) + tests.
- cli/internal/format/filter.go envelope-specific helpers
(WriteEnvelopeFiltered, marshalEnvelope, applyFieldFilter,
filterDataPayload, filterObjectData); the reusable
filterArrayItems / filterObjectKeys / writeJQ stay for bare.go.
- cli/internal/cmdutil/exporter.go + tests (envelope-only).
- cli/internal/cmdutil/PrintErrorEnvelope + ToErrorBody +
operationRiskOf + Error.OperationRisk field + OperationRisk struct.
Error path: all errors now go to stderr via cmdutil.PrintError in
`code: message\nhint: ...` form, regardless of --json. Stdout stays
empty (or holds the partial-success the command already wrote) so
downstream `--json | jq` pipelines never have to filter error shapes
out of the success stream. Typed exit codes (3 auth.* / 4
resource.not_found / 5 input.* / 6 server.rate_limited / 7 server.*
+ network.* / 10 input.confirmation_required) carry the failure
class for agents that branch on it.
Acceptance contract:
- envelope_test.go → wire_test.go (TestEnvelopeGolden → TestWireGolden).
- testdata/envelopes/ → testdata/wire/.
- Error-path cases assert the typed code substring on stderr.
- Orphan whoami.*.json goldens deleted.
AGENTS.md + README.md rewritten for the bare-data contract:
- Drop envelope schema section + dry-run rule.
- Document bare JSON on stdout + `code: msg\nhint: …` on stderr.
- ADR-3 reframed around bare data and why error separation matters
for `--json | jq` pipelines.
WriteJSONFiltered short-circuits to WriteJSON when both filters are
empty (skip the marshal-buffer round-trip for the common case).
Final review pass:
- Fix wire-contract bug: `--json id,name` (space form) is broken by
pflag's NoOptDefVal; AGENTS.md / README.md / SetAgentHelp + the
field-discovery help text all switched to `--json=id,name`.
- Fix `weknora api --jq` silently ignored: api.go now routes through
WriteJSONFiltered with jopts.JQ.
- AGENTS.md: drop the false claim that `auth logout` honors `-y`
(logout is local-only with no ConfirmDestructive guard); list the
actual destructive commands instead.
- Rewrite cli/acceptance/e2e/e2e_test.go for the bare-data wire shape
(was still parsing `out["data"]` / `env["ok"]`).
- Add `JSONOptions.Emit(w, v)` helper; collapse ~33 repeated
`format.WriteJSONFiltered(iostreams.IO.Out, X, jopts.Fields,
jopts.JQ)` sites to `jopts.Emit(iostreams.IO.Out, X)` — drops the
format import from 22 cmd/* files.
- Delete single-caller `cmdutil.MustRequireFlag`; inline as
`_ = cmd.MarkFlagRequired(...)` everywhere.
- Add `_ = cmd.MarkFlagRequired("name")` to `kb create`; it was the
only write command relying on runtime --name validation while
`context add` already used the cobra-level mark.
- `context use`: register `--json` / `--jq` (was always emitting JSON
unconditionally with no human path and no flag — diverged from
every other write command); human mode now prints
`✓ Switched context to X (was Y)`.
- Replace per-package `confirmPrompter` / `scriptedConfirm` /
`errPrompter` test doubles with `testutil.ConfirmPrompter`.
- Rename `chatService` → `ChatService` (export to match siblings
`ListService` / `ViewService`); rename `printUploadSuccess` →
`renderUploadSuccess` (siblings use `render*`).
- `defaultHint(CodeResourceNotFound)`: drop the hardcoded
"list available with `weknora kb list`" — misleading on agent /
doc / session 404. Replaced with "verify the resource ID and try
again".
- Strip stale `v0.2/v0.3` / "envelope" / "v0.0/v0.1 supports only"
historical tags from production comments and a few test
descriptions.
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.
Cross-cutting cleanup that lands alongside the new feature surface:
- `--limit / -L` and `--all-pages` on every list command. Default
--limit 30 (gh-parity); --all-pages drains every server page
client-side, capped by --limit. Closes the audit finding that the
old "1000 max per call" implicit cap was undiscoverable.
- `auth token` emits a TTY-only stderr advisory when stdout is a
terminal (the credential just got displayed in scrollback) plus an
api-key-mode rotation hint.
- Comment + doc discipline pass: drop external project name
references from in-code comments (we reference them in design notes,
not inline).
- Bump `go` directive to 1.26.0 and CI matrix to 1.26.x to align with
the main module's go.mod.
- Rename `cli/internal/agent` → `cli/internal/aiclient` to
disambiguate from the new `cli/cmd/agent` resource subtree. The
package handles AI coding-agent env detection + per-command --help
annotations; the new name reflects that more precisely.
`weknora mcp serve` — long-lived stdio MCP (Model Context Protocol)
transport that exposes a fixed, curated tool surface to MCP-aware
agents (Claude Desktop, Claude Code, custom MCP clients).
Curated tool set (readonly by default):
- whoami — active context + tenant
- search (hybrid retrieval against a KB)
- kb list / view
- doc list / view
- agent list / view / invoke
- session list / view
The list is intentionally narrow to the read + agent-invoke surface;
destructive verbs (`delete` / `empty` / `upload`) are gated behind
`--write`. Schema is built from each leaf cobra command's flags so
adding a new tool is a single registry entry plus a Service interface.
Includes the simplify post-review polish + a second simplify pass to
fold the resulting feedback (typed schemas, agent_help wording, unify
chat / agent invoke option names).
Manages WeKnora's first-class Custom Agent resources — server-side
records (system prompt + model + allowed tools + KB scope) that the
user authored in the web UI.
Commands:
- `weknora agent list` — tenant-visible agents (built-in + custom),
sorted updated_at desc; `--limit`/`-L` caps the slice client-side.
- `weknora agent view <id>` — full sdk.Agent including nested
AgentConfig (mode / model / allowed_tools / KB scope). Human mode
prints a compact KV layout + Config: block.
- `weknora agent invoke <agent-id> "<text>"` — streams the agent's
configured workflow against a query over SSE. Auto-creates a fresh
session unless `--session` is passed. Streaming defaults to TTY +
no-stream/no-json; agent-friendly buffered single-object output
with `--json` (or `--no-stream`).
Decoupled from the existing `chat` subtree: agents bring their own
system prompt / tool surface / KB selection, so the chat / agent split
matches the server-side resource boundary.
Adds the filter flags users were reaching for via `--jq` post-filter:
- `kb list --pinned` — client-side filter to KBs with `IsPinned`.
- `doc list --status <pending|processing|completed|failed>` —
server-side query-param filter; `failed` surfaces ingestion errors
immediately for triage.
- `session list --since <duration>` — client-side filter to sessions
updated within the past duration. Accepts time.ParseDuration forms
(24h, 1h30m, 30m) plus a `<N>d` suffix for whole days (7d, 0.5d).
Server-side filters are forwarded as query params (where the API
supports them) to avoid pulling the full list into memory; client-side
filters apply after the fetch so they compose with `--limit`.