Commit Graph

13 Commits

Author SHA1 Message Date
nullkey 5f98df530c feat(cli): bundled Agent Skills + drift guards
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).
2026-06-08 20:18:43 +08:00
nullkey c90f4e895e refactor(cli): resource-command refactors — --kb resolver, MCP session_ask, session stop (BREAKING)
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.
2026-06-08 20:18:43 +08:00
nullkey c11df51c79 feat(cli): MCP Tool.Annotations on 10 tools (spec 2025-06-18)
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.
2026-05-28 19:29:50 +08:00
nullkey b395db55de chore(cli): adapt ListKnowledgeChunks interface to variadic chunk_type filter
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.
2026-05-27 10:56:34 +08:00
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 567d7ac74e feat(cli): --format / NDJSON / chat & agent invoke / MCP / SetAgentHelp / signal-aware ctx / kb create --storage-provider
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".
2026-05-18 11:10:19 +08:00
nullkey c87e35b34b chore(cli): polish + docs sync + pre-PR audit fixes
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.
2026-05-16 16:56:33 +08:00
nullkey 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).
2026-05-16 16:56:33 +08:00
nullkey 26fa43e2cc fix(cli): post-audit fixes (MCP MatchCount + sessions + auth + view)
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.
2026-05-16 16:56:33 +08:00
nullkey 5b07c9ab87 feat(cli): chunk subtree + MCP chunk_list tool + curation rationale
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.
2026-05-16 16:56:33 +08:00
nullkey 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
2026-05-15 12:03:56 +08:00
nullkey bdc589e1c0 refactor(cli): --limit/--all-pages, Go 1.26, internal/agent → aiclient
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.
2026-05-15 12:03:56 +08:00
nullkey 9bb83b47fd feat(cli): mcp serve curated stdio MCP server
`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).
2026-05-15 12:03:56 +08:00