Files
WeKnora/cli/README.md
nullkey 2ee9741fa1 refactor(cli): finish context→profile cascade + post-review hardening (BREAKING)
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.
2026-05-27 10:56:34 +08:00

12 KiB
Raw Blame History

weknora — WeKnora CLI

A command-line interface for the WeKnora RAG knowledge-base server. Lets you authenticate, manage knowledge bases and documents, run hybrid search, and ask streaming RAG questions from your terminal or from an AI agent.

$ weknora --help
Command-line client for the WeKnora RAG server. Manage knowledge bases
and documents, run hybrid search, chat with grounded answers, or expose
a curated read-only MCP tool surface for AI agents.

Available Commands:
  agent       Manage custom agents (CRUD + status/check)
  api         Make a raw API request to the WeKnora server
  auth        Manage authentication credentials and profiles
  chat        Ask a streaming RAG question against a knowledge base
  chunk       Manage document chunks (RAG retrieval debug)
  completion  Generate the autocompletion script for the specified shell
  profile     Manage CLI profiles (named connection targets)
  doc         Manage documents in a knowledge base
  doctor      Run 4 self-checks: base URL, auth, server version, credential storage
  help        Help about any command
  kb          Manage knowledge bases
  link        Bind the current directory to a knowledge base
  mcp         Run weknora as a Model Context Protocol server
  search      Search across chunks, knowledge bases, documents, or sessions
  session     Manage chat sessions
  unlink      Remove the directory's knowledge-base binding
  version     Show CLI build metadata

The wire contract for AI agents is documented below. For contributing to the CLI source, see AGENTS.md.


Install

From source

Requires Go 1.26+.

git clone https://github.com/Tencent/WeKnora.git
cd WeKnora/cli
go build -o weknora .
sudo mv weknora /usr/local/bin/   # or anywhere on $PATH

Pre-built binaries

Pre-built binaries for Linux / macOS / Windows are produced by CI on each release. Grab the latest from the Releases page.


5-minute quickstart

# 1. Log in to your WeKnora server (interactive password prompt)
weknora auth login --host https://kb.example.com

# 2. Or pipe an API key from stdin (for CI / AI agents)
echo "sk-..." | weknora auth login --host https://kb.example.com --with-token

# 3. List knowledge bases
weknora kb list

# 4. Bind this directory to a knowledge base — subsequent commands auto-resolve --kb
weknora link --kb my-knowledge-base

# 5. Upload a document, then block until parsing finishes
weknora doc upload notes.md
weknora doc wait doc_abc                          # exit 0 completed, 1 failed, 124 --timeout, 130 ^C

# 6. Search
weknora search chunks "what is reciprocal rank fusion?"

# 7. Ask the LLM (streams to terminal)
weknora chat "summarise the design doc"

# 8. Manage custom agents and run them (see `weknora agent --help` / `weknora session --help`)
weknora agent list
weknora session ask --agent ag_abc "what's our q4 retention plan?"

# 9. Inspect a document's chunks for RAG retrieval debug
weknora chunk list --doc doc_xyz

# 10. Health & verification verbs
weknora kb status kb_abc       # fast snapshot: reachable / counts / processing flag (1 HTTP)
weknora kb check kb_abc        # deep verify: also aggregates failed_count via doc list (1+N HTTP)
weknora agent status ag_abc    # fast: reachable / model_id
weknora agent check ag_abc     # deep: probes every KB in the agent's scope

Agent quick start

For AI agents (Claude Code, Cursor, Gemini CLI, etc.) integrating WeKnora:

  1. Install: brew install weknora or go install github.com/Tencent/WeKnora/cli@latest
  2. Authenticate (background; extract login URL for the user):
    weknora auth login --host <server-url>
    
  3. Register MCP in the host's MCP config:
    {"mcpServers": {"weknora": {"command": "weknora", "args": ["mcp", "serve"]}}}
    
  4. Read the wire contract before parsing --format json output.
  5. Read the exit-10 anti-patterns before any destructive call.

Multi-profile

Switch between several WeKnora servers (or several tenants on the same server) without re-logging in:

weknora auth login --host https://prod.example.com    --name prod
weknora auth login --host https://staging.example.com --name staging --with-token < .staging-key
weknora auth list
weknora profile use prod

Credentials are persisted to your OS keyring (Keychain on macOS, libsecret on Linux, Wincred on Windows) when available, otherwise to a 0600-mode file under $XDG_CONFIG_HOME/weknora/secrets/. The active profile lives in ~/.config/weknora/config.yaml.

To remove a profile's stored credentials:

weknora auth logout                  # current profile
weknora auth logout --name staging   # specific
weknora auth logout --all

Wire contract

Designed to be AI-agent-first. Stable across minor releases; breaking changes announced in the changelog and the corresponding weknora --version bump.

Streams

  • stdout is the data channel: bare JSON with --format json, or human-formatted output. Never carries error text.
  • stderr is logs, progress, warnings, and errors. A non-empty stderr does not mean failure — read the exit code.

JSON output

Every command supports --format json, emitting bare JSON for the resource it produces — an array for list / search, a single object for view and write outcomes:

weknora kb list --format json                              # [{ "id": "kb_x", "name": "Eng" }, …]
weknora kb view kb_x --format json                         # { "id": "kb_x", "name": "Eng", … }
weknora kb list --format json --jq '.[] | {id, name}'      # project to listed fields
weknora kb list --format json --jq '.[].id'                # jq over the bare data

--format ndjson is also accepted for streaming list commands; each element is emitted as its own JSON line. --format json is the default regardless of TTY — running weknora kb list | jq works without an explicit flag. Use --format text for human-readable output.

Errors

On failure, stdout stays empty and the typed error goes to stderr in this format:

<code.namespace>: <message>[: <wrapped cause>]
hint: <actionable next-step>

Example:

auth.unauthenticated: fetch current user: HTTP error 401: ...
hint: run `weknora auth login`

The full code registry is in cli/internal/cmdutil/errors.go (AllCodes()). Code namespaces: auth.* / resource.* / input.* / server.* / network.* / local.* / mcp.* / operation.* (CLI-level wait/poll outcomes: operation.timeout, operation.failed, operation.cancelled).

Exit codes

Code Meaning Agent action
0 success continue
1 typed local.* / operation.failed / unclassified read stderr, decide retry/abort
2 flag / argument validation error re-check weknora <cmd> --help
3 auth.* (token missing / expired / forbidden) re-auth, then retry
4 resource.not_found verify the resource id
5 input.* (other than confirmation_required) adjust args, retry
6 server.rate_limited back off, retry
7 server.* / network.* transient — retry with backoff
10 input.confirmation_required (high-risk write) ask the human, retry with -y only after explicit approval
124 operation.timeout (e.g. doc wait --timeout reached) raise --timeout or check the underlying job
130 operation.cancelled (SIGINT / SIGTERM) stop, do not retry

Exit 10 is the wire-level signal for "destructive write needs explicit confirmation". Pass -y/--yes on kb delete / doc delete (including --all --kb=<id>) / session delete / profile remove (on the current profile) / agent delete / chunk delete when running headless. Never auto-add -y without the user's explicit go-ahead — exit 10 is the guard against unintended writes.

Other AI-agent ergonomics

  • For chat / session ask in AI-agent contexts, pass --format json — streaming tokens to stdout makes JSON parsing impossible.
  • --format json composes with the global --profile <name> for single-shot profile overrides without disk writes.
  • weknora mcp serve exposes a curated read-only tool surface over stdio MCP for any MCP-compatible client.

Advanced operations not exposed as flags

WeKnora CLI exposes top use cases as polished commands; deep configuration goes through the raw HTTP passthrough. CLI flag coverage targets common workflows, not 1:1 API parity. Examples of deep operations that intentionally go through weknora api:

  • Tuning a KB's nested config — chunking strategy, summary model, multimodal extraction defaults, FAQ thresholds, VLM model. Use weknora api PUT /api/v1/knowledge-bases/<id> --input - with a JSON body matching the server's UpdateKnowledgeBaseRequest. (Note: the storage provider is set once at create time via kb create --storage-provider <name> and is not updatable.)
  • Per-request chat parameters — multi-KB scope, summary model override, image attachments, web search toggle. Use weknora api POST /api/v1/knowledge-chat/<session-id> --input -.
  • Per-request session ask --agent overrides — same shape via weknora api POST /api/v1/agent-chat/<session-id> --input -.
  • Operations without a CLI verb — register / change-password / OIDC flows, organization / sharing endpoints, tenant management.

weknora api --help documents the raw passthrough. Run weknora doctor first to verify auth and base URL.


Health check

Run weknora doctor for a 4-status diagnostic (OK / warn / fail / skip) covering base URL reachability, authentication, server-CLI version skew, and credential storage backend. Add --format json for machine-readable output, --offline to skip network checks.

For per-resource verification, the status / check verb pair gives a fast vs deep choice:

Verb Cost Use
weknora kb status <kb-id> 1 HTTP live counts / processing flag
weknora kb check <kb-id> 1+N HTTP adds failed_count via doc-list page-walk
weknora agent status <agent-id> 1 HTTP reachable / model_id
weknora agent check <agent-id> 1+N HTTP also probes every KB in the agent's scope

weknora doc wait <doc-id> [<doc-id>...] blocks until each document reaches a terminal parse_status (completed or failed). Exit codes: 0 (all completed), 1 (any failed), 124 (--timeout reached), 130 (Ctrl-C / SIGTERM). Multi-target is polled concurrently (max 5 in flight; pipe through xargs -P for more).


Development

# Run unit + contract tests
go test ./...

# Run the real-server e2e suite (requires WEKNORA_E2E_HOST + token env vars)
go test -tags acceptance_e2e ./acceptance/e2e/...

# Static analysis
go vet ./...

CI (.github/workflows/cli.yml) runs build + unit + contract tests on Linux / macOS / Windows × Go 1.26, path-filtered to changes under cli/.


Contributing / Reporting issues

  • Bugs and feature requests: file an issue at github.com/Tencent/WeKnora/issues.
  • Security disclosures: see the repository-level SECURITY.md. Do not file public issues for security findings.
  • Pull requests: the developer guide for editing the CLI lives in AGENTS.md (build / test / command-surface design SOP / CRUD flag conventions). Run go test ./... -race -count=1 and go vet ./... before submitting.

License

MIT — see the repository LICENSE.