26fa43e2ccbb65ba5512a3ed6b60df01bf25e430
11 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
69fbbfc252 |
chore(cli): gofmt sweep
`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). |
||
|
|
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 |
||
|
|
e623e8208f |
refactor(cli): delete envelope infrastructure, errors to stderr
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.
|
||
|
|
cc8254f862 |
refactor(cli): drop --dry-run + introduce bare-JSON output path
Two intertwined mainstream-alignment moves bundled because they share
the migration target (every command's --json path):
1. Drop --dry-run entirely. Survey of comparable API-wrapper CLIs
(gh, aws, stripe, lark): none expose --dry-run. The mainstream that
does (kubectl/git/helm/ansible) operates on declarative manifests
or local state where the preview is materially different from the
executed action. WeKnora's CLI just echoed the same parameters
that would have gone on the wire — the preview added no real
signal over `--help` + reading the call site. Removes:
- root --dry-run persistent flag + cmdutil/dryrun.go
- DryRun fields + EmitDryRun calls in 12 write commands
- format.Envelope.DryRun field
- 8 corresponding *_test.go cases
- --dry-run mention from README.md and CHANGELOG.md
- "dry_run":false from 16 golden envelopes
2. Migrate every --json output to bare data:
- New format.WriteJSON / WriteJSONFiltered helpers
(cli/internal/format/bare.go) share filterArrayItems /
filterObjectKeys / writeJQ with the (still-live for now) envelope
filter helpers.
- Read commands (kb/doc/session list+view, search chunks/docs/
sessions/kb, auth list/status, agent list/view, context list,
doctor) emit bare arrays / objects on stdout.
- Write commands (kb create/edit/delete/pin/empty, doc upload/
upload_recursive/delete, session delete, auth login/logout/
refresh/token, link/unlink, context add/use/remove, agent
invoke, chat, api, version) emit bare result objects. Risk
classification dropped — the resource + exit code already
convey the action.
Per-command shape changes:
list / search → []T (was {ok, data:{items:[…]}})
view → T (was {ok, data:T, _meta:…})
create / edit → T
delete / pin / etc. → {id, …action result…}
doctor → {summary, checks}
api → {status, headers, body}
_meta dropped on the read path:
pagination (page/page_size/total/has_more) — agents iterate with
--all-pages or accept --limit (gh CLI parity);
kb_id / context echo — caller already knows what it asked for.
Acceptance contract goldens regenerated for the new bare shape.
Error envelope on stdout (PrintErrorEnvelope) stays live for now —
the envelope-infra deletion lands in the next commit.
|
||
|
|
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. |
||
|
|
1b20b06f5e |
feat(cli): --json field-select, --jq, auth token, doc --from-url
Output ergonomics:
- `--json` accepts a comma-separated field list (gh-parity); selects
named keys from the per-command payload. Bare `--json` keeps the
full shape.
- `--jq <expr>` evaluates a gojq expression over the JSON; pairs with
`--json field-list` so projection runs before jq.
- `--version` is a global cobra flag in addition to the `version`
subcommand; both render the same line.
- Per-command `--help` now renders the available JSON field list under
"JSON fields available via `--json id,name,...`" (field-discovery
parity with gh / kubectl `-o jsonpath`).
New commands:
- `auth token` — print the active context's credential to stdout for
shell command substitution (`WEKNORA_TOKEN=$(weknora auth token)`).
Default: raw secret, no trailing newline. `--json` emits
`{token, mode, context}`.
- `doc upload --from-url <URL>` — ingest a remote URL via the SDK
`CreateKnowledgeFromURL`. `--name` forwarded as `FileName` so the
server's known-extension heuristic upgrades crawl-mode to
file-download-mode where appropriate.
Includes the simplify post-review polish pass (field-filter unit
tests, --json/--jq compose check, agent_help copy fixes).
|
||
|
|
5adcedf170 |
refactor(cli): v0.3 cross-cutting cleanup
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. |
||
|
|
e236be1ced |
fix(cli): correct KB id detection, SSE terminal-frame, and CI test isolation
Three defects surfaced during end-to-end RAG verification — the first two
block real chat usage, the third makes Linux CI flaky:
1. KB id detection — `IsKBID` was checking
`strings.HasPrefix(s, "kb_")`, but WeKnora generates KB ids as bare
UUIDs (internal/types/knowledge_base.go: `uuid.New().String()` stored
in a `varchar(36)` column). Real ids therefore fell through to the
name-resolution path:
$ weknora chat ... --kb a32a63ff-fb36-4874-bcaa-30f48570a694
Error: knowledge base not found: a32a63ff-...
Switched the discriminator to a UUID regex
(`^[0-9a-fA-F]{8}-…-[0-9a-fA-F]{12}$`). KB names are arbitrary
user-supplied strings, so the canonical 8-4-4-4-12 form is an
unambiguous signal. Mirrors gcloud `--project`'s id-vs-name detection.
2. SSE terminal-frame — the accumulator's `Append` was gating
finalization on `r.Done`, but the server's KnowledgeQAStream protocol
emits a leading `agent_query` frame with `done=true` to deliver
session + message metadata *before* the answer fragments arrive:
event: message
data: {"response_type":"agent_query","content":"","done":true,…}
event: message
data: {"response_type":"answer","content":"你好","done":false}
…
event: message
data: {"response_type":"complete","content":"","done":true}
The accumulator therefore flipped to `finished=true` on frame #1 and
discarded every subsequent answer fragment — `weknora chat … --json`
returned `answer: ""` even though the LLM reported completion_tokens
> 0. Fixed: terminate only on `response_type == complete`.
References still captured opportunistically (they may arrive on a
dedicated `references` event before the terminator).
3. doctor credential_storage CI isolation — the check probes the real
OS keyring via `secrets.NewBestEffortStore()`: present on macOS dev
machines → StatusOK; absent on Linux CI runners without libsecret /
Gnome-Keyring → StatusWarn ("falling back to file store"). That
host-dependence was leaking into two test classes that assumed
StatusOK:
* cmd/doctor/doctor_test.go: TestDoctor_AllOK and
TestDoctor_NoConfig_StillRunsCredentialStorage already had a
withCredStoreFactory seam but didn't use it. Added the pin.
* acceptance/contract/envelope_test.go: doctor.success_offline
and doctor.error_network golden cases. The contract test runs
through the cobra tree in-process and shares cmd/doctor's
package-level credStoreFactory var — but couldn't reach it
because the existing seam was unexported.
Fix: export `doctor.SetCredStoreFactoryForTest(fn) (restore func())`
for out-of-package tests; acceptance/contract/helpers_test.go adds
a TestMain that pins the factory to a MemStore-returning closure
for the whole suite (MemStore is neither *FileStore nor a real
keyring, so doctor's type-switch hits StatusOK). Production stays
at secrets.NewBestEffortStore — only the test hook is now reachable
from across packages.
Test fixtures and goldens that used the old `kb_xxx` literals or
`Done: true` terminators were rewritten to use real UUIDs and
`ResponseType: ResponseTypeComplete` respectively. Per-command --help
text and Long descriptions / Examples now show a UUID rather than
`kb_…` so users see the correct shape from the start. New
TestAccumulator_IgnoresAgentQueryDone pins the SSE terminator bug so
it can't regress.
Tests: 24 cli packages green on macOS dev + Linux/macOS/Windows CI
matrix. Verified end-to-end against a live WeKnora server: `weknora
chat "..." --kb <UUID> --no-stream --json` returns the full LLM answer
in the envelope, live token streaming in TTY mode works, and the
credential_storage check renders deterministic envelopes across hosts.
|
||
|
|
3fb3583a92 |
feat(cli): add api passthrough, chat streaming, doctor warn status
Close the v0.2 RAG demo loop and ship the validation infrastructure:
- weknora api <method> <path> [--data X | --data-file F]
Raw passthrough wrapping client.Raw, gh-style. JSON envelope mode
surfaces status / headers / parsed body. Non-2xx routes through
cmdutil.ClassifyHTTPStatus (factored out of ClassifyHTTPError so
both SDK-error and direct-status paths stay aligned — reuse review).
- weknora chat <text> [--session-id S] [--no-stream]
KnowledgeQAStream consumer with two output modes:
- TTY default: token streaming + references footer
- --json / --no-stream / non-TTY: buffered single envelope
Auto-creates a session when --session-id is omitted; the id prints
to stderr at start AND on stream failure (^C scrolls past the
first announcement, so the recovery hint is re-surfaced when the
user is most likely to need it).
- cli/internal/sse/Accumulator
buffers Content / References / SessionID across SDK callbacks.
Idempotent post-Done so misbehaving servers don't corrupt state.
- doctor: ok → ok / warn / fail / skip
warn marks soft issues that don't block: server within compat range
but >=1 minor behind CLI; credential storage falling back to file
because keyring is unavailable. Envelope.ok stays true on warn,
flips false on fail (exit 1). doctor.error_network golden updated.
- cli/acceptance/e2e/ real-server RAG full loop
Build-tagged //go:build acceptance_e2e — kept out of the default
`go test ./...`. Exercises kb create → doc upload → poll ready →
search → chat → cleanup against a server pointed at by
WEKNORA_E2E_HOST / _TOKEN.
- .github/workflows/cli-e2e.yml
manual workflow_dispatch + label-gated PR trigger
("acceptance-e2e"). No-ops gracefully when the secrets aren't set
so cross-fork PRs can't accidentally fail the suite.
|
||
|
|
bb592a59a6 |
feat(cli): contract test suite + dependabot (PR-8)
cli/acceptance/contract/:
envelope_test.go — 16 envelope golden cases (9 commands × {success/error
variants}; 3 cases dropped with rationale: doctor.success
non-offline has unstable timing detail; auth_login.* needs
stdin/keyring scaffold deferred to v0.2; context_use.error
needs leaf-local --json deferred to follow-up)
errorcodes_test.go — single-direction AST scan of cli/cmd/ extracting first
arg of cmdutil.NewError / cmdutil.Wrapf calls;
ClassifyHTTPError dynamic-classify bridged via
cmdutil.ClassifyHTTPErrorOutputs() per spec §4.3.
testdata/envelopes/ — 16 JSON golden files
helpers_test.go (PR-6 scaffold) extended:
runCmd now wires cobra Out/Err sinks (version uses c.OutOrStdout) AND
replicates cmd.Execute()'s error-envelope path so error-case goldens are
populated. Without this, every error scenario's golden was 0 bytes.
cli/cmd/root.go: mapCobraError → MapCobraError, wantsJSONOutput → WantsJSONOutput
(exported so the contract test helper can replicate Execute()'s
envelope-printing path without calling Execute() itself).
root_test.go updated to use new exported names.
.github/dependabot.yml (新增):gomod /cli + github-actions weekly,gh-style
ignore semver-major to avoid noise. Open-source
dependency safety,independent of release cadence.
v0.1 不发布到任何分发平台 (release infra 推迟到发布窗口 milestone)。
|
||
|
|
cf84bf2a38 |
feat(cli): add whoami / doctor / kb / context commands (PR-7)
5 new leaf commands wired into the root tree:
whoami — simplified `auth status` (user_id + tenant_id only)
doctor — 4-item self-check (base_url / auth / server_version / cred_storage)
with --offline / --no-cache flags + skip cascade + summary.all_passed
防 agent 看到 envelope.ok=true 误判命令整体 success
kb list — list KBs (default updated_at desc; 0 KB → "(no knowledge bases)")
tabwriter 4-col (ID/NAME/DOCS/UPDATED), display-width truncation
kb get — show single KB details (KEY: VALUE, suppress empty fields)
context use — switch default context (writes config.current_context),
带 levenshtein distance ≤ 2 的 did-you-mean hint
Each command uses the v0.0 narrow Service interface pattern (testable via
fakes), agent.SetAgentHelp for AI-friendly hints, and ClassifyHTTPError
for stable error code mapping.
cmdutil/errors.go: 新增 CodeLocalContextNotFound for `context use`,加入 AllCodes() 注册集.
cli/cmd/root.go: NewRootCmd 改为 exported (acceptance/contract 测试需要),
注册 4 个新命令 + 1 parent group; root_test.go 跟随更新.
|