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.
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.
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
Cross-cutting findings surfaced by the branch-completion review.
Perf bug:
- Factory.Client closure was not memoized. Factory.ResolveKB internally
calls f.Client() to resolve --kb name → id, then the command's RunE
calls f.Client() again. Two SDK clients, two keyring reads, two
AuthRetryTransport allocations per name-resolved invocation, with
*independent* token state (a refresh in one was invisible to the
other). Switched to sync.Once like Secrets already does.
Silent bug bait:
- cmdutil.NormalizeHost docstring claimed CodeInputMissingFlag for the
empty case; code returned CodeInputInvalidArgument. Aligned doc to
code (present-but-empty is a bad value, not a missing flag).
Agent contract gaps:
- Five user-facing subcommands lacked SetAgentHelp: auth login /
logout / list / status and chat. Added concise strings with error-
code call-outs so agents can branch without parsing human strings.
Helper extraction (≥3 callers):
- text.KnowledgeDisplayName(fileName, title, id) — byte-identical
formatter that was in both cmd/doc/list.go and cmd/search/docs.go.
Takes fields directly so internal/text stays SDK-free.
- cmdutil.WrapHTTP(cause, fmt, args...) *Error — replaces the
`Wrapf(ClassifyHTTPError(err), err, ...)` pattern across 24 SDK
call sites. Sed-driven migration; off-pattern shapes in chat.go
(used streamErr) and cmdutil/kb.go (in-package) hand-edited.
Contract test gains a comment update: post-migration the dominant
pattern is WrapHTTP which the AST scanner skips entirely (only
NewError/Wrapf selectors inspected); ClassifyHTTPErrorOutputs()
bridge still covers the dynamic codes those paths can yield.
UX consistency:
- cmd/doc/list.go --page-size help now reads "Items per page
(1..1000)" matching cmd/session/list.go. The bounds validation
already enforced 1..1000; the help text was the last drift.
Comment-discipline sweep:
- Deleted the WHAT-only "*Options captures `weknora ...` flag state"
docstring across 23 files (context, kb, auth, doc, session, search,
chat, doctor, link). Where the line carried a real WHY clause
(kb/delete, doc/delete, session/delete, kb/edit), kept the WHY and
dropped only the leading WHAT phrase.
- Stripped third-party project-name attribution from inline comments
and one user-visible flag-help string across ~40 files in cli/cmd
and cli/internal (plus 4 test-file comments). Removed phrases like
"Mirrors `gh X`", "borrowed from lark-cli", "kubectl-style",
"gcloud `--project`", "Stripe pattern", and the embedded GitHub
URLs pointing at those projects. Behavioral descriptions and the
WHY behind each comment are preserved; only the upstream-name
attribution is gone. Inspiration / north-star references belong in
cli/AGENTS.md (the design doc) and commit messages, not scattered
through every file.
Triggered by an audit round that surfaced several false / fragile
parity claims (e.g. "Mirrors `gh repo edit`" — gh repo edit has no
--name flag; "matches gcloud `--project` id-or-name" — gcloud's
--project accepts ID only). Rather than fix them one by one, the
whole category of in-comment external-project references was
stripped uniformly.
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.