feat(cli): command help + output ergonomics (did-you-mean, per-subcommand help, ai UX) - #65
Conversation
📝 WalkthroughWalkthroughThis release delivers CLI 1.2.0 with improved error handling, AI command output controls, and multi-token help support. It introduces a Levenshtein distance-based suggestion system for "Did you mean?" error messages, adds compact output modes for investigation results with byte-truncation, and enables nested command help queries like ChangesCortex CLI 1.2.0 Release
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e81ca167b6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR improves the cortex CLI UX by adding fuzzy “did you mean?” suggestions for unknown commands/options, enabling per-subcommand nested --help output (e.g. ai search --help), and introducing new AI output/detail controls while wiring additional ai subcommands through parsing/help/dispatch. It also updates operator-facing docs to consistently reference the cortex binary and bumps the project version to 1.2.0.
Changes:
- Add Levenshtein-based suggestions for unknown commands/subcommands/options across the hand-rolled CLI parser.
- Extend help rendering to support nested subcommand help entries (e.g.
db status,compose logs,ai investigate) with dedicated usage lines. - Add AI output ergonomics (
--detail,--limit,--include-transcript,--max-bytes) and expose additionalaisubcommands through parse/help/output/dispatch; update docs + version metadata accordingly.
Reviewed changes
Copilot reviewed 36 out of 37 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main.rs | Routes unknown top-level commands through the CLI parser to surface improved errors/suggestions. |
| src/main_tests.rs | Updates top-level mode parsing tests to reflect new error text and host-state selector requirements. |
| src/cli/suggest.rs | Introduces suggestion utilities (did-you-mean + unknown command/option formatting). |
| src/cli/suggest_tests.rs | Adds unit tests for suggestion matching behavior. |
| src/cli/parse.rs | Adds top-level command candidate list + suggestion-backed unknown-command/option errors. |
| src/cli/parse_tests.rs | Expands parser tests for suggestion output and host-state selector validation. |
| src/cli/parse_command_log.rs | Adds suggestion-backed errors for shell and agent-command subcommands. |
| src/cli/parse_ai.rs | Adds AI subcommand candidate list + suggestion-backed errors; extends ai blocks flags (--limit, --detail). |
| src/cli/parse_ai_tests.rs | Adds tests for new AI flags and “did you mean?” behavior on unknown AI subcommands. |
| src/cli/parse_ai_more.rs | Extends ai investigate parsing with output control flags and suggestion-backed unknown-option errors. |
| src/cli/parse_ai_more_tests.rs | Adds tests for ai investigate output control flags parsing. |
| src/cli/parse_admin.rs | Adds suggestion-backed errors for service, db, compose, and setup subcommands. |
| src/cli/output_logs.rs | Adds print options + truncation behavior for ai blocks output (detail/limit aware). |
| src/cli/output_ai_more.rs | Adds compact-vs-full JSON behavior and truncation controls for ai investigate output. |
| src/cli/help.rs | Adds nested help catalog + nested lookup/classification to support per-subcommand --help. |
| src/cli/help_tests.rs | Adds tests ensuring nested help renders focused usage and classify_help returns nested paths. |
| src/cli/dispatch_tests.rs | Updates dispatch test snapshots for expanded/defaulted AI args. |
| src/cli/dispatch_db.rs | Updates operator guidance text to reference cortex instead of syslog. |
| src/cli/dispatch_ai.rs | Wires new AI print options into ai blocks and ai investigate dispatch paths. |
| src/cli/commands/silent_hosts.rs | Updates CLI docs/comments to reference cortex binary. |
| src/cli/commands/sig.rs | Adds suggestion-backed error for unknown sig subcommands. |
| src/cli/commands/notify.rs | Adds suggestion-backed error for unknown notify subcommands. |
| src/cli/commands/host_state.rs | Adds suggestion-backed unknown-option errors and enforces required host selector with usage. |
| src/cli/commands/compare.rs | Updates CLI docs/comments to reference cortex binary. |
| src/cli/commands/clock_skew.rs | Updates CLI docs/comments to reference cortex binary. |
| src/cli/commands/apps.rs | Updates CLI docs/comments to reference cortex binary. |
| src/cli/commands/anomalies.rs | Updates CLI docs/comments to reference cortex binary. |
| src/cli/args/ai.rs | Adds AiOutputDetail and new AI args fields for output/detail controls. |
| src/cli/args.rs | Re-exports AiOutputDetail from the args module. |
| src/cli.rs | Registers the new suggest module and re-exports updated CLI types. |
| server.json | Bumps server/package metadata to 1.2.0 and updates container tag. |
| mcpb/manifest.json | Bumps MCP bundle manifest version to 1.2.0. |
| docs/runbooks/deploy.md | Updates runbook commands to consistently use cortex binary. |
| docs/rollout.md | Updates rollout instructions to consistently use cortex binary. |
| CHANGELOG.md | Adds 1.2.0 release notes covering new help/suggestions/output ergonomics and doc fixes. |
| Cargo.toml | Bumps crate version to 1.2.0. |
| Cargo.lock | Updates lockfile to reflect the new crate version. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
4 issues found across 37 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/cli/parse_ai_more.rs">
<violation number="1" location="src/cli/parse_ai_more.rs:275">
P2: Did-you-mean matching uses the full `--flag=value` token, so typo suggestions frequently fail for equals-style options in `ai investigate`.</violation>
</file>
<file name="src/cli/help.rs">
<violation number="1" location="src/cli/help.rs:359">
P3: This usage line is misleading: `--detail full` is only honored in JSON mode right now, so terminal output does not become full-detail. Scope the note to JSON mode or update the terminal renderer to honor `detail`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…max-bytes, flag suggestions - Honor --detail full / --include-transcript in non-JSON `ai investigate` output: the terminal path now renders transcript_before/after + non-error nearby_logs (previously only JSON branches consulted detail). The help note is now accurate, not misleading. - --max-bytes truncates by UTF-8 byte length on a char boundary (new truncate_bytes) instead of character count, so the byte budget holds for non-ASCII input. char-limit call sites keep the char-based truncate(). - did_you_mean matches the flag NAME only (split on '='), so equals-style typos (--projct=foo) suggest --project. - UsageBlocksPrintOptions::default() detail Full -> Compact to match the CLI default for `ai blocks` (removes silent truncation-disable footgun). Tests: truncate_bytes byte-budget on char boundaries; did_you_mean =value. Resolves review thread PRRT_kwDORy0Fc86GiqML Resolves review thread PRRT_kwDORy0Fc86GijNS Resolves review thread PRRT_kwDORy0Fc86GiqMI Resolves review thread PRRT_kwDORy0Fc86Gih9x Resolves review thread PRRT_kwDORy0Fc86GiqMH Resolves review thread PRRT_kwDORy0Fc86GijN3 Resolves review thread PRRT_kwDORy0Fc86GijMn Resolves review thread PRRT_kwDORy0Fc86GiqL_
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/cli/help.rs (1)
197-217:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd the new AI subcommands to
cortex ai --help.
render_command("ai")reads thisusagelist, and it still omitsai similar,ai ask-history, andai incident-contexteven though those commands are documented inNESTED_CATALOGbelow. Right now the new commands are only discoverable if the user already knows their exact names.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/cli/help.rs` around lines 197 - 217, The AI subcommands list in the usage array used by render_command("ai") is missing the new commands (ai similar, ai ask-history, ai incident-context), so add those three strings into the usage array (the same format as the other entries) so render_command("ai") will include them; verify that the entries match their names in NESTED_CATALOG to keep help in sync with the implemented commands.src/cli/help_tests.rs (1)
112-137: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winAdd regressions for the uncovered nested-help cases.
These spot checks still pass while
cortex ai --helphides the new AI subcommands,setup doctor --helpresolves to the generic setup banner, and--server URL db status --helpfalls back to top-level help. A couple more assertions here would pin the intended behavior and prevent the catalog/classifier drift that slipped in.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/cli/help_tests.rs` around lines 112 - 137, Add regression assertions around classify_help to lock in nested-help behavior: using the same v helper, assert that classify_help(&v(&["ai", "--help"])) returns HelpRequest::TopLevel; assert that classify_help(&v(&["setup", "doctor", "--help"])) returns HelpRequest::Command("setup doctor".to_string()); and assert that classify_help(&v(&["--server", "URL", "db", "status", "--help"])) returns HelpRequest::TopLevel; place these next to the existing classify_help assertions in the classify_help_distinguishes_top_level_command_and_none test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Cargo.toml`:
- Line 3: Cargo.toml currently pins version = "1.2.0" but other version-bearing
manifests are missing or inconsistent; update all manifests to match this
release: add or update the "version": "1.2.0" field in package.json,
pyproject.toml (project.version or tool.poetry/version),
.codex-plugin/plugin.json, gemini-extension.json and add a "version": "1.2.0"
key to .claude-plugin/plugin.json; also update README.md to reference 1.2.0 and
add/update the version-.*-blue badge to reflect 1.2.0; keep existing entries in
CHANGELOG.md, mcpb/manifest.json and server.json as-is if they already show
1.2.0.
In `@src/cli/help.rs`:
- Around line 515-534: classify_help only recognizes two-token nested paths from
NESTED_CATALOG, but CATALOG includes "cortex setup doctor [--json]" and there is
no corresponding NestedCommandDoc for the "setup doctor" path, so "setup doctor
--help" falls back to generic help; add a NestedCommandDoc entry for the "setup
doctor" path (matching the usage string in CATALOG) to NESTED_CATALOG (or update
classify_help to accept single-token subcommands under "setup"), ensuring the
new NestedCommandDoc has path "setup doctor", summary and usage matching the
advertised CATALOG entry so classify_help can return the nested target for that
subcommand.
- Around line 826-838: The positionals collection logic incorrectly treats
option values as positional arguments; update the code that builds positionals
(the scan iterator used to produce positionals) to first consume recognized
pre-command global options and their values (e.g., "--server" and "--token"
which take a following value) as well as standalone flags, so that values like
"http://127.0.0.1:3100" are not included; then run the existing nested lookup
(nested_lookup) and single-command branch (is_known => HelpRequest::Command) on
the filtered positionals. Ensure you reference the same symbols: scan,
positionals, nested_lookup, is_known, and HelpRequest::Command when implementing
the pre-scan/filtering.
---
Outside diff comments:
In `@src/cli/help_tests.rs`:
- Around line 112-137: Add regression assertions around classify_help to lock in
nested-help behavior: using the same v helper, assert that
classify_help(&v(&["ai", "--help"])) returns HelpRequest::TopLevel; assert that
classify_help(&v(&["setup", "doctor", "--help"])) returns
HelpRequest::Command("setup doctor".to_string()); and assert that
classify_help(&v(&["--server", "URL", "db", "status", "--help"])) returns
HelpRequest::TopLevel; place these next to the existing classify_help assertions
in the classify_help_distinguishes_top_level_command_and_none test.
In `@src/cli/help.rs`:
- Around line 197-217: The AI subcommands list in the usage array used by
render_command("ai") is missing the new commands (ai similar, ai ask-history, ai
incident-context), so add those three strings into the usage array (the same
format as the other entries) so render_command("ai") will include them; verify
that the entries match their names in NESTED_CATALOG to keep help in sync with
the implemented commands.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f4177597-8ed4-4cb8-b7d2-d134dca38f38
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lockand included by**/*
📒 Files selected for processing (38)
CHANGELOG.mdCargo.tomldocs/rollout.mddocs/runbooks/deploy.mdmcpb/manifest.jsonserver.jsonsrc/cli.rssrc/cli/args.rssrc/cli/args/ai.rssrc/cli/commands/anomalies.rssrc/cli/commands/apps.rssrc/cli/commands/clock_skew.rssrc/cli/commands/compare.rssrc/cli/commands/host_state.rssrc/cli/commands/notify.rssrc/cli/commands/sig.rssrc/cli/commands/silent_hosts.rssrc/cli/dispatch_ai.rssrc/cli/dispatch_db.rssrc/cli/dispatch_tests.rssrc/cli/help.rssrc/cli/help_tests.rssrc/cli/output_ai_more.rssrc/cli/output_common.rssrc/cli/output_common_tests.rssrc/cli/output_logs.rssrc/cli/parse.rssrc/cli/parse_admin.rssrc/cli/parse_ai.rssrc/cli/parse_ai_more.rssrc/cli/parse_ai_more_tests.rssrc/cli/parse_ai_tests.rssrc/cli/parse_command_log.rssrc/cli/parse_tests.rssrc/cli/suggest.rssrc/cli/suggest_tests.rssrc/main.rssrc/main_tests.rs
There was a problem hiding this comment.
1 issue found across 6 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…, global-option-aware help, plugin version - `setup doctor --help` now resolves to nested help (added the NestedCommandDoc; it was advertised in CATALOG but missing from NESTED_CATALOG, so it fell back to the generic setup help). - classify_help skips values consumed by value-bearing global options (--server/--token) when building the command path, so `cortex --server URL db status --help` resolves to `db status` instead of the top-level banner. - Add "version": "1.2.0" to .claude-plugin/plugin.json (docs/CHECKLIST.md lists it as version-bearing; it was missing). check-version-sync now tracks 4 files. Note for the version thread: the other manifests cited (package.json, pyproject.toml, .codex-plugin/plugin.json, gemini-extension.json) do not exist in this Rust repo; the README already carries a dynamic crates.io version badge. Tests: setup_doctor_has_nested_help, classify_help_skips_global_option_values. Resolves review thread PRRT_kwDORy0Fc86GjTS9 Resolves review thread PRRT_kwDORy0Fc86GjTTD Resolves review thread PRRT_kwDORy0Fc86GjTSz
…max-bytes, flag suggestions - Honor --detail full / --include-transcript in non-JSON `ai investigate` output: the terminal path now renders transcript_before/after + non-error nearby_logs (previously only JSON branches consulted detail). The help note is now accurate, not misleading. - --max-bytes truncates by UTF-8 byte length on a char boundary (new truncate_bytes) instead of character count, so the byte budget holds for non-ASCII input. char-limit call sites keep the char-based truncate(). - did_you_mean matches the flag NAME only (split on '='), so equals-style typos (--projct=foo) suggest --project. - UsageBlocksPrintOptions::default() detail Full -> Compact to match the CLI default for `ai blocks` (removes silent truncation-disable footgun). Tests: truncate_bytes byte-budget on char boundaries; did_you_mean =value. Resolves review thread PRRT_kwDORy0Fc86GiqML Resolves review thread PRRT_kwDORy0Fc86GijNS Resolves review thread PRRT_kwDORy0Fc86GiqMI Resolves review thread PRRT_kwDORy0Fc86Gih9x Resolves review thread PRRT_kwDORy0Fc86GiqMH Resolves review thread PRRT_kwDORy0Fc86GijN3 Resolves review thread PRRT_kwDORy0Fc86GijMn Resolves review thread PRRT_kwDORy0Fc86GiqL_
…, global-option-aware help, plugin version - `setup doctor --help` now resolves to nested help (added the NestedCommandDoc; it was advertised in CATALOG but missing from NESTED_CATALOG, so it fell back to the generic setup help). - classify_help skips values consumed by value-bearing global options (--server/--token) when building the command path, so `cortex --server URL db status --help` resolves to `db status` instead of the top-level banner. - Add "version": "1.2.0" to .claude-plugin/plugin.json (docs/CHECKLIST.md lists it as version-bearing; it was missing). check-version-sync now tracks 4 files. Note for the version thread: the other manifests cited (package.json, pyproject.toml, .codex-plugin/plugin.json, gemini-extension.json) do not exist in this Rust repo; the README already carries a dynamic crates.io version badge. Tests: setup_doctor_has_nested_help, classify_help_skips_global_option_values. Resolves review thread PRRT_kwDORy0Fc86GjTS9 Resolves review thread PRRT_kwDORy0Fc86GjTTD Resolves review thread PRRT_kwDORy0Fc86GjTSz
For max_bytes below the 3-byte ellipsis, the byte budget was zeroed (saturating_sub) before the small-budget branch derived its cut, so the function returned an empty string instead of the leading 1–2 bytes. Reorder so the small-budget branch computes its cut from max_bytes directly. Test: truncate_bytes_tiny_budget_returns_prefix_not_empty. Resolves review thread PRRT_kwDORy0Fc86GjTqS
main advanced to 1.6.1 (graph-projection series) while this branch was open. Rebased onto it and bumped all version-bearing files (Cargo.toml, server.json, mcpb/manifest.json, .claude-plugin/plugin.json, Cargo.lock) from the inherited 1.6.1 to 1.7.0 — a minor bump for this feature branch per the version policy. CHANGELOG: the branch's CLI entry is now [1.7.0] above main's [1.6.1].
4b50c7b to
f4b70a0
Compare
wrapper_preserves_command_exit_when_spool_append_fails runs ['true'], a single token, which the wrapper executes via $SHELL -c (command_status). It was not #[serial], so it could run concurrently with wrapper_executes_multi_arg_commands_without_shell_reparse, which mutates the global SHELL and CORTEX_TEST_ARG_OUT env vars. Under the overlap, the spool test exec'd the other test's fake shell (wrong exit, assert fails) and that stray invocation corrupted CORTEX_TEST_ARG_OUT (other test's assert fails) — both failed together. Reproduced on CI where TMPDIR=/tmp forces the spool path through the shell branch; main's Tests job was already intermittently red from the same race. Marking the spool test #[serial] closes the window.
Rebased onto current
main(813ee80) — conflict-free except one import-list merge (kept bothAiOutputDetailandDbIntegrityStatusArgs). 1293 tests pass; clippy + fmt clean.What this adds
src/cli/suggest.rs): Levenshtein fuzzy-matching so a mistyped command, subcommand, or flag suggests the nearest valid one (e.g.cortex serach→ "unknown CLI command: serach — Did you meansearch?"). Threshold scales with token length.--help: extends the grouped help catalog with detailedusage:lines for the individual subcommands (ai *,db *,compose *,setup *,sig *), so e.g.cortex ai search --help/cortex db vacuum --helpshow their own flags.aisubcommands through parse + help + output:ai similar,ai ask-history,ai incident-context(arg types already existed; now exposed).output_ai_more.rs,output_logs.rs), incl. anAiOutputDetailmode.suggest_tests,parse_ai_tests,parse_ai_more_tests,parse_tests,main_tests).Notes
--helpalready onmain; complements (doesn't replace) it.Summary by cubic
Adds “Did you mean?” suggestions, nested per-subcommand help (now aware of
--server/--token), and compact/full output controls with UTF‑8 byte-accurate truncation to make the CLI easier to use. Also exposesai similar,ai ask-history, andai incident-context, updates docs, and bumps to 1.7.0.New Features
setup doctor; resolves correctly with global flags (e.g.,cortex --server URL db status --help).--projct=foo→--project); shown for top-level commands too.ai investigateandai blocks:--detail compact|full,--include-transcript,--max-bytes(UTF‑8 byte-accurate), and--limit; terminal and JSON honor--detail(compact JSON when compact);ai blockscaps to 25 in compact mode unless overridden.Bug Fixes
ai investigateterminal output now honors--detailand--include-transcript.--max-bytestruncates by UTF‑8 bytes on char boundaries; tiny budgets return a prefix instead of empty.host-staterequires--host-id/--hostnamewith usage; unknown top-level commands now include suggestions; docs referencecortexinstead ofsyslog.$SHELLenv races.Written for commit 97f4153. Summary will update on new commits.
Summary by CodeRabbit
New Features
help ai search).cortex ai investigateandcortex ai blockscommands.Bug Fixes
cortexinstead of legacysyslognaming.cortex host-stateerror messages to require explicit host selector with usage hints.Documentation