47a183aa65bdf17b7c8d601f7efe97cbde515e2a
19 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
34bb0b5096 |
feat(cli): doc delete multi-id + api --paginate + paginate fixes + batch deletes
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.
|
||
|
|
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. |
||
|
|
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). |
||
|
|
7bccd72ba3 |
feat(cli): search --all-pages canon catch-up + AGENTS.md SOP / CRUD canon
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. |
||
|
|
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. |
||
|
|
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).
|
||
|
|
4a5449233d |
fix(cli): plug v0.3 final review findings (json + auth + path + bounds + kb)
Seven bugs surfaced via two audit rounds — parallel reviewer agents
plus a real-server end-to-end demo. Each fix arrives with a
regression test.
1. doc upload --recursive --json corrupted the envelope stream.
Per-file FAIL/OK plain lines printed unconditionally to stdout,
then a Success envelope, then on partial failure a typed error
that the root handler turned into a SECOND Failure envelope —
three outputs where one was expected. Fix: gate the plain lines
behind !opts.JSONOut, and add cmdutil.Error.Silent so the JSON-
path partial-failure preserves its typed exit code without
triggering PrintErrorEnvelope's default Failure-envelope write.
2. auth refresh / AuthRetryTransport misclassified HTTP failures as
network.error. RefreshAndPersist wrapped every refresher error
with CodeNetworkError, but the SDK emits "HTTP error 401: ..."
for a rejected refresh token — which should surface as
auth.token_expired. Switched to WrapHTTP for proper status-
derived classification. Affects both `auth refresh` and the
transport's refresh closure.
3. doc download accepted ".." as a server-suggested filename. The
rejection list covered "" / "." / filepath.Separator but not
bare ".." — filepath.Base("..") is "..", which slipped through
to os.Create and produced a confusing local.file_io wrap. Added
to the rejection set.
4. search chunks / docs / kb / sessions had no lower bound on
--limit. `-L 0` / `-L -1` was forwarded to the server with
undefined behavior. Added a 1..1000 bound at the RunE boundary
across all four (matching doc list / session list page-size
bounds). Internal callers in tests can still pass Limit==0 for
the "no client-side cap" runChunks path — the bound only applies
at the user-input layer.
5. cli/AGENTS.md ADR-3 verb-canon summary listed only v0.2 verbs as
"gh-canonical" and missed v0.3 additions (edit, pin, unpin,
download — all gh-canonical) plus locally-introduced ones
(empty, refresh, add, remove, link). Rewritten as an explicit
gh-canonical / locally-introduced split.
6. kb pin returned 404. Server registers /knowledge-bases/{id}/pin
as PUT (router.go:292); SDK was using POST. gin's router silently
404s on method-mismatch (treats it as path-not-found, not 405),
so the CLI classified the response as resource.not_found and
masked the real failure mode. Switched the SDK to http.MethodPut.
The asymmetry that hid this past round 1: kb unpin on a freshly-
created KB hits the no-op branch in cmd/kb/pin.go that skips the
SDK call entirely, so unpin "worked" without ever exercising the
broken path. Only the real-server demo, where kb pin actually
fires, surfaced it.
7. kb edit clobbered current Name when only --description was
passed. EditOptions used *string to distinguish "unset" from
"set to empty", but sdk.UpdateKnowledgeBaseRequest declares both
fields as plain string (no omitempty), so the JSON body always
carried `"name": ""`. Server requires Name → 400. Fix: runEdit
does fetch-then-update — GetKnowledgeBase first, build the PUT
body with current values, then overlay user-set fields. Same
TOCTOU window as kb pin / unpin.
Audit-flagged items intentionally NOT changed:
- kb pin / unpin check-then-toggle TOCTOU: documented; the clean
fix would be a server-side setter and belongs in a separate API
change.
- AuthRetryTransport singleflight test gap for one concurrency
scenario; v0.4 polish.
- cli/README.md:50 "once v0.2 ships" and CHANGELOG.md:8
"10 top-level commands": v0.2-PR artifacts, not v0.3-introduced.
- kb edit / kb pin are v0.3-new commands, so neither bug needs a
cli/CHANGELOG.md Fixed entry — the v0.3 release ships them
working as the Added bullets advertise.
|
||
|
|
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. |
||
|
|
d54a7a5834 |
feat(cli): search verb-noun subtree (chunks/kb/docs/sessions)
Roadmap 3-1. Verb-noun shape borrowed from gh search (gh search repos / code / commits / issues / prs verified against the gh manual). Subcommands: - `search chunks "<q>" --kb X` — hybrid retrieval (RAG search). - `search kb "<q>"` — case-insensitive substring match across KB names and descriptions; sorted by name length (shortest hits first). - `search docs "<q>" --kb X` — pages through ListKnowledge filtering by title / file_name; stops once --limit matches are found. - `search sessions "<q>"` — pages through GetSessionsByTenant filtering by title / description. kb / docs / sessions are client-side filters because the server has no fuzzy search endpoint for any of them. ListKnowledgeBases returns the full tenant catalog in one call; the doc/session walkers chunk at 200 per request and stop early on limit. The parent `search` command is a pure dispatcher — there is no bare- positional form (no `weknora search "<q>"`). Cleanups surfaced by the post-commit reviewer round: - UX consistency: search docs's displayDocName ordered Title → FileName → "-", while doc list's displayName uses FileName → Title → ID. Same Knowledge rendered differently across commands. Aligned search docs on doc list's existing FileName-first convention. - cmdutil.ResolveKBFlag(ctx, lister, raw) — extracted the `IsKBID ? raw : ResolveKBNameToID` block duplicated across chunks and docs. - text.ContainsFold(needle, fields...) — replaces inline `strings.Contains(strings.ToLower(field), needle)` patterns. 37 unit tests across chunks/kb/docs/sessions plus the parent registration smoke-test. Roadmap: 3-1. |
||
|
|
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.
|
||
|
|
8bcbf5a154 |
refactor(cli): align command surface with mainstream conventions
Empirical mainstream-CLI surveys (gh / kubectl / aws / gcloud / stripe /
flyctl / terraform / vercel / netlify / lark) drove five alignment
fixes — each replaces a weknora-only design choice that mainstream CLIs
do not share. No backwards-compat shims; the CLI has no v0.1 users yet.
1. Single --kb flag (was --kb-id + --kb mutually exclusive)
Survey: 0/7 mainstream CLIs use two parallel flags for "by id" vs
"by name". Single flag (gh -R, gcloud --project) or positional
(kubectl, stripe, terraform). Closest analog — gcloud --project —
collapses identifier types onto one flag.
Now: every command exposes one --kb flag; client-side prefix
detection (cmdutil.IsKBID looks for "kb_") routes id-form values
through directly and name-form values through ListKnowledgeBases.
Mirrors gcloud --project's id-or-name auto-detection.
Touched: search, chat, doc list / upload / delete, link.
Factory.ResolveKB chain trimmed from 5 levels to 4.
2. link supersedes init
Survey: only vercel and netlify ship both `init` AND `link` as
siblings, and they keep them semantically distinct. weknora's pair
wrote the same .weknora/project.yaml file with the same meaning,
differentiated only by interactivity — that's a flag concern, not
a command concern.
Now: cmd/init/ deleted. cmd/link absorbs the interactive flow:
- link --kb <id-or-name> → non-interactive write
- link on a TTY → interactive prompt (lists KBs)
- link non-TTY without --kb → CodeKBIDRequired
Always overwrites silently (matches vercel link / netlify link /
kubectl apply rather than git init's refuse-if-exists).
Dead code purged: --force flag, CodeProjectAlreadyLinked error code.
3. whoami dropped
Survey: 7/7 mainstream CLIs ship exactly one identity command —
never both a status and a whoami. gh / gcloud / stripe pick status
(config + live API); aws / kubectl / flyctl pick whoami (live API).
weknora's auth status was already a superset of whoami (host +
context + user + email + tenant_id + tenant_name vs user_id +
tenant_id), so dropping whoami preserves all functionality and
aligns with the gh / gcloud / stripe form.
4. kb get alias dropped
`view` was already primary (gh repo view / gh pr view convention);
`get` was kept as a cobra alias for v0.0/v0.1 callers. With no
v0.0/v0.1 users to break, the alias is just noise on the command
surface. Acceptance contract envelope cases renamed kb_get.* →
kb_view.*; goldens renamed in lockstep.
5. api refactored to gh shape (-X/--method, default GET, auto-POST)
gh CLI's signature is `gh api <endpoint> [--method M]` — single
positional path, method as a flag, default GET, auto-promoted to
POST when a body is supplied. weknora's previous `api <method>
<path>` inverted this and forced the method to be passed even for
GET — a needless deviation from our declared north star.
Now: `api <path> [-X METHOD] [--data ...]`. Exit-10 protocol
on the DELETE escape-hatch is preserved; -X DELETE still hits
ConfirmDestructive when -y absent.
Plus: AGENTS.md gains an explicit note that `doctor` is a deliberate
divergence from gh / lark — borrowed from `flutter doctor` / `brew
doctor` because RAG deployments routinely break on misconfigured
embeddings / storage / credentials and a 4-status structured envelope
is the cleanest surface for it.
Tests: 24 cli packages green (was 26 in PR-14; init + whoami packages
removed). Acceptance contract envelope cases for whoami removed,
kb_get → kb_view renamed, search args / mock path updated for the
kb_<id> form. e2e harness flag args updated. Factory.ResolveKB tests
rewritten for the single-flag shape. api_test driver updated for the
positional-path / -X-method shape.
|
||
|
|
f7d7c8054d |
chore(cli): remove unused v0.0 scaffolding
Foundation PR-1 reserved several internal packages and helpers as
scaffolding for follow-up PRs that ended up taking different routes.
Audit confirms zero production references; this commit removes them so
the cli/ tree reflects what's actually shipped.
Removed (148 LOC):
cli/internal/safepaths/ — `Validate` / `WithinRoot` /
three sentinel errors. Reserved
for `weknora doc upload`'s path
scrubbing; that command landed
in PR-10 using its own
`validateUploadPath` (os.Stat +
regular-file check) — sufficient
for the actual threat model
(local CLI invocations).
cli/internal/cmdutil/json_flags.go — `AddJSONFlags` helper +
unused --jq / --template flag
registration. Reserved for PR-3
"lipgloss tables / jq evaluator"
which never materialized; every
command directly registers
BoolVar(&JSONOut, "json", ...)
since v0.0 ship time.
cmdutil.NewTableExporter — empty alias for jsonExporter,
reserved for the same PR-3
renderer. Removed; jsonExporter
stays under NewJSONExporter.
cmdutil.Options marker interface — empty interface{} reserved as a
convention; no command embeds
or asserts against it.
Stale comments fixed:
- cmd/root.go: package comment updated kb (list+get) → kb
(list+view+create+delete) and noted the `get` cobra alias.
- cmd/root.go: dropped --no-version-check forward-reference (no such flag).
- cmd/root.go: removed "(PR-7)" attribution from NewRootCmd doc comment.
- cmd/kb/kb.go: same package-comment update.
- cmd/chat/chat.go: replaced "PR-7" mention in --help example with a
generic placeholder so cobra-rendered help is review-clean.
- cmd/search/search.go: removed "Lipgloss tables arrive in PR-3"
forward-reference; the inline indent helper is the shipped form.
- internal/agent/annotations.go: ShouldUseAgentMode → DetectAIAgent
(removed in PR-12).
AGENTS.md "Known limitations" section added:
Documents that chat / search / doc upload currently surface server-side
precondition misses (LLM / vector store / storage engine not configured)
as `network.error` with `context deadline exceeded`. A planned future
release will introduce a `precondition.*` typed error namespace
(server returns HTTP 412 before opening the SSE / streaming response).
This documents the limitation honestly for reviewers and integrators
rather than claiming a behavior we don't yet have.
Tests: 27 cli packages pass (safepaths_test was the 28th — gone with the
package). go vet clean.
|
||
|
|
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.
|
||
|
|
fc5d16e331 |
feat(cli): top-level search command (PR-5)
The fourth and final v0.0 command — chunk hybrid retrieval, the demo
headline operation per ADR-3 (only one search command in the tree).
Maps to client.HybridSearch / GET /knowledge-bases/{id}/hybrid-search;
no SDK or server changes.
Flags:
- --kb (required): target knowledge base
- --top-k (default 8): max results
- --vector-threshold / --keyword-threshold: similarity floors
- --no-vector / --no-keyword: disable individual channels (mutually
exclusive at the validation gate; checked before the SDK client is
built so flag misuse fails fast)
- --json: emit envelope JSON (otherwise pretty list)
Service interface narrowed to just HybridSearch; tests inject fakes via
Factory.Client closure override.
Pretty rendering is a minimal text indent today; lipgloss tables arrive
in PR-3 of v0.2 (format/ split into presenters/style/tableprinter).
|