Skip to content

refactor(cli): stdlib-only CLI with LLM-friendly help and JSON config#188

Merged
tuannvm merged 8 commits into
mainfrom
worktree-feat+cli-improve
Apr 16, 2026
Merged

refactor(cli): stdlib-only CLI with LLM-friendly help and JSON config#188
tuannvm merged 8 commits into
mainfrom
worktree-feat+cli-improve

Conversation

@tuannvm
Copy link
Copy Markdown
Owner

@tuannvm tuannvm commented Apr 16, 2026

  • Replace gopkg.in/yaml.v3 with encoding/json for config (config.json)
  • Auto-migrate legacy YAML configs to JSON on first load
  • Use text/tabwriter for table output, encoding/csv for CSV output
  • Inject io.Writer/io.Reader for full testability (Commands, REPL)
  • Structured --help with NAME/SYNOPSIS/DESCRIPTION/COMMANDS/FLAGS/EXAMPLES/ENVIRONMENT
  • Per-subcommand --help (e.g. mcp-trino query --help)
  • Unix exit codes: 0 success, 1 error, 2 usage error
  • Signal handling via signal.NotifyContext (SIGINT/SIGTERM)
  • Errors to stderr, data to stdout
  • No third-party library imports in cli package

Summary

Changes

Testing

  • Tests pass locally
  • New tests added (if applicable)

Related Issues

Summary by CodeRabbit

  • New Features

    • Configuration supports both JSON and YAML with automatic format detection and JSON-first precedence
    • CLI and REPL output can be directed to custom streams for better testability and piping
  • Bug Fixes

    • Deterministic column ordering and corrected CSV/table output alignment and flush behavior
  • Improvements

    • Structured, per-command help (NAME/SYNOPSIS/etc.)
    • CLI errors printed to stderr with explicit exit codes; signal-aware shutdown handling
  • Documentation

    • Updated docs and README to reflect new help, config formats, and exit semantics

- Replace gopkg.in/yaml.v3 with encoding/json for config (config.json)
- Auto-migrate legacy YAML configs to JSON on first load
- Use text/tabwriter for table output, encoding/csv for CSV output
- Inject io.Writer/io.Reader for full testability (Commands, REPL)
- Structured --help with NAME/SYNOPSIS/DESCRIPTION/COMMANDS/FLAGS/EXAMPLES/ENVIRONMENT
- Per-subcommand --help (e.g. mcp-trino query --help)
- Unix exit codes: 0 success, 1 error, 2 usage error
- Signal handling via signal.NotifyContext (SIGINT/SIGTERM)
- Errors to stderr, data to stdout
- No third-party library imports in cli package

Co-authored-by: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

This PR refactors the CLI to use injected io.Writers for stdout/stderr, centralizes CLI parsing/execution in runCLI, implements structured help and usage errors with Unix-style exit codes, adds JSON-first + YAML config handling with migration, and updates tests to assert captured output across commands and REPL.

Changes

Cohort / File(s) Summary
CLI Entry Points
cmd/cli.go, cmd/main.go
Added runCLI(stdout, stderr, rawArgs) and usageError; switched flag parsing to ContinueOnError and routed help/usage output to stderr; added signal-aware context; introduced exitCLIError with exit-code semantics; made CLI wiring writer-aware.
Command Implementation
internal/cli/commands.go, internal/cli/commands_test.go
Added writer fields (out, errOut) and NewCommandsWithWriters; redirected all non-JSON output to writers; switched CSV/table output to encoding/csv/text/tabwriter; added extractSortedColumns and test helpers capturing stdout/stderr.
REPL
internal/cli/repl.go, internal/cli/repl_test.go
Added out io.Writer, introduced NewREPLWithReader to inject stdin, routed REPL output/errors to writers, and updated tests to use scripted input and captured buffers for assertions.
Configuration System
internal/cli/config.go, internal/cli/config_test.go
Added exported SSLConfig and OutputConfig; changed TrinoProfileConfig/CLIConfig shapes and tags; implemented JSON-first parsing (ParseCLIConfig) and ParseCLIConfigWithPath (YAML support & migration); updated load/save behavior and tests for JSON precedence and YAML fallback/migration.
Output Tests
internal/cli/output_test.go
Updated tests to use newTestCommands with buffers; added deterministic-order assertions for table/CSV output and JSON/CSV content checks.
Integration & CLI Help Tests
cmd/integration_test.go, cmd/main_test.go
Updated integration test expectations for help output to include SYNOPSIS; added extensive runCLI/help/usage tests using captured stdout/stderr and usageError cases.
Docs & Misc
README.md, CLAUDE.md, docs/installation.md, .gitignore
Documented structured help, exit codes, JSON/YAML config precedence, and writer-injection/testing changes; added .claude/harness/ to .gitignore.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI (runCLI)
    participant Config as Config Loader
    participant Commands as Commands
    participant Trino as TrinoClient
    participant Out as Writers (stdout/stderr)

    rect rgba(200,230,255,0.5)
    CLI->>Config: Load config (JSON-first / YAML fallback)
    Config-->>CLI: CLIConfig
    end

    rect rgba(200,255,200,0.5)
    CLI->>Commands: Build commands with writers (out, errOut) and context
    CLI->>Commands: Dispatch subcommand / REPL
    Commands->>Trino: Execute query / metadata calls
    Trino-->>Commands: QueryResult / Error
    Commands->>Out: Write formatted output (table/csv/json) or errors
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

🐰
A rabbit taps the CLI keys with cheer,
Streams now flow to buffers far and near,
JSON first, YAML follows in line,
Tests listen close to every output sign,
Hops off content—help, usage—crystal clear.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: a CLI refactor using only stdlib with improved help and JSON config support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-feat+cli-improve

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (1)
internal/cli/output_test.go (1)

202-217: Make this test assert an observable behavior.

The context is cancelled, but err is discarded, so this passes whether Query honors cancellation or not. Either use a mock that returns ctx.Err() and assert the failure, or stop cancelling the context and rename the test to what it actually verifies.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cli/output_test.go` around lines 202 - 217, The test
TestQueryExecution_OutputFormattingWithMockClient is discarding err and
cancelling the context, so it doesn't assert behavior; either remove the early
cancel and rename the test if you just want to verify output formatting, or make
the mockTrinoClient simulate context cancellation (have its query implementation
return ctx.Err()) and then call cmd.Query(ctx, "SELECT 1") and assert the
returned error matches context.Canceled (or ctx.Err()); update expectations
accordingly and keep references to
TestQueryExecution_OutputFormattingWithMockClient, mockTrinoClient,
newTestCommands, and cmd.Query when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/cli.go`:
- Around line 241-249: The flag parse error branches for the "schemas"
(schemasFlagSet.Parse) and "tables" flag parsing currently return plain errors,
causing main to exit with code 1; change these to return usageError (the
sentinel/typed error used for usage exits) when flag parsing fails due to
invalid flags (e.g., unknown flag like --bogus). Concretely, in the block that
handles schemasFlagSet.Parse and the analogous tablesFlagSet.Parse branch,
detect parse failures from Parse and instead of returning fmt.Errorf("...: %w",
err) return usageError (or wrap the original err in usageError if your
convention requires it), preserving the hasFlags(commandArgs) fallback that
delegates to commands.Schemas when appropriate. Ensure both the schemas and
tables branches follow this same pattern.
- Around line 603-612: The code path that handles "config profile" currently
prints the usage when len(args) < 3 and returns nil, causing a successful (0)
exit for an incomplete command; change that to return a non-nil error after
printing usage so the CLI reports failure. Specifically, in the branch that
checks len(args) < 3 (uses args, w, and cliConfig.Current), keep the usage
output but replace the final "return nil" with a returned error (e.g.,
errors.New or fmt.Errorf) indicating a missing subcommand or invalid usage for
"config profile" so callers will exit non‑zero.
- Around line 123-126: The branch that handles explicit "--config ...yaml"
currently calls ParseYAMLConfigWithPath which only parses but leaves ConfigPath
pointing at the .yaml file; change this to run the YAML→JSON migration path
instead of the parse-only fallback: invoke the project's migration routine for
YAML configs (the function that converts and persists a canonical JSON config
and updates ConfigPath) rather than ParseYAMLConfigWithPath, and ensure after
migration ConfigPath points to the new config.json so subsequent commands (e.g.,
profile use) serialize to config.json not back to config.yaml.

In `@cmd/main.go`:
- Around line 67-72: The CLI exit handling is inconsistent: caller code
currently checks RunCLIMode() and maps *usageError to exitUsage but the
hasCLIOnlyFlags branch (and the path around lines 89-93) uses log.Fatalf,
producing a different exit code and message format; refactor so all CLI failures
funnel through a single helper (e.g., a new exitWithError(err error) used by
main and by branches) and change the hasCLIOnlyFlags branch and the other direct
log.Fatalf/Exit calls to either return a *usageError (so RunCLIMode returns it)
or call that helper; ensure the helper inspects errors with type assertion to
*usageError and exits with exitUsage for that type and exitError otherwise, and
use process-consistent message formatting (fmt.Fprintf to os.Stderr) instead of
log.Fatalf.

In `@internal/cli/commands.go`:
- Around line 277-279: The truncation summary is being written to c.out which
corrupts CSV streams; instead emit that metadata to the error stream. Change the
fmt.Fprintf call that handles queryResults.Truncated (currently writing to
c.out) to write to c.errOut (or, if the command tracks output format,
conditionally write the message to c.errOut only when the active format is
"csv"). Update the code path around queryResults.Truncated so CSV output remains
machine-parseable by moving the truncation message off stdout.
- Around line 90-93: The current prints using fmt.Fprintln and fmt.Fprintf
discard write errors (to c.out) which can fail with injected io.Writer; update
the code that iterates over catalogs to capture and propagate errors: check the
returned error from fmt.Fprintln(c.out, "Catalogs:") and from each
fmt.Fprintf(c.out, "  - %s\n", catalog), and return or bubble that error from
the enclosing function (instead of ignoring it) so caller sees write failures;
use the existing c.out, catalogs and the surrounding function's error return to
locate where to add the error checks and early returns.

In `@internal/cli/config.go`:
- Around line 88-110: The YAML migration currently treats any unreadable or
malformed YAML as an empty config because parseSimpleYAML cannot fail; change
migrateYAMLConfig (and likewise ParseYAMLConfigWithPath) to detect and reject
malformed legacy YAML instead of silently defaulting: either update
parseSimpleYAML to return (CLIConfig, error) and propagate parse errors, or
after calling parseSimpleYAML check the raw file contents and parsed result
(e.g., if file is non-empty and parsed config equals zero-value or lacks
expected fields like Profiles/Current/Server) then return a descriptive error
rather than injecting defaults; ensure SaveCLIConfig is only called for
successfully parsed configs and update the callers of
parseSimpleYAML/ParseYAMLConfigWithPath accordingly.

In `@internal/cli/repl.go`:
- Around line 56-59: When Scan() returns false in the REPL loop (the block
containing if !r.scanner.Scan() { fmt.Fprintln(r.out); return nil }), check
r.scanner.Err() and return that error when non-nil instead of always returning
nil; if r.scanner.Err() is nil then treat it as EOF and return nil. Update the
code around r.scanner.Scan() to call err := r.scanner.Err() and handle err !=
nil by returning err, otherwise continue to print the newline and return nil.
Also ensure this logic is applied in the initial read path created by
NewREPLWithReader so real I/O failures are propagated.
- Around line 47-58: Writes to the injected io.Writer in repl.go (e.g., the
fmt.Fprintln/Fprint calls using r.out and r.prompt) currently ignore returned
errors; update the REPL to check each fmt.* return (and any fmt.Fprintf/Println
usages at the other occurrences) and propagate non-nil errors upstream (return
or wrap and return) instead of discarding them so write failures (broken pipe,
closed output) cause a proper error exit; specifically modify the write calls
around the greeting, prompt display, error/newline prints and any other
fmt.Fprint* uses in the REPL loop (referenced by r.out, r.prompt, r.scanner) to
handle and return the error.

---

Nitpick comments:
In `@internal/cli/output_test.go`:
- Around line 202-217: The test
TestQueryExecution_OutputFormattingWithMockClient is discarding err and
cancelling the context, so it doesn't assert behavior; either remove the early
cancel and rename the test if you just want to verify output formatting, or make
the mockTrinoClient simulate context cancellation (have its query implementation
return ctx.Err()) and then call cmd.Query(ctx, "SELECT 1") and assert the
returned error matches context.Canceled (or ctx.Err()); update expectations
accordingly and keep references to
TestQueryExecution_OutputFormattingWithMockClient, mockTrinoClient,
newTestCommands, and cmd.Query when making the change.
🪄 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: CHILL

Plan: Pro

Run ID: e8908f70-f1d9-4183-aeda-f9740d98f919

📥 Commits

Reviewing files that changed from the base of the PR and between 4a164d7 and 70b76f9.

📒 Files selected for processing (11)
  • .gitignore
  • cmd/cli.go
  • cmd/main.go
  • go.mod
  • internal/cli/commands.go
  • internal/cli/commands_test.go
  • internal/cli/config.go
  • internal/cli/config_test.go
  • internal/cli/output_test.go
  • internal/cli/repl.go
  • internal/cli/repl_test.go

Comment thread cmd/cli.go Outdated
Comment thread cmd/cli.go
Comment thread cmd/cli.go Outdated
Comment thread cmd/main.go Outdated
Comment thread internal/cli/commands.go Outdated
Comment on lines 90 to 93
fmt.Fprintln(c.out, "Catalogs:")
for _, catalog := range catalogs {
fmt.Printf(" - %s\n", catalog)
fmt.Fprintf(c.out, " - %s\n", catalog)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Return writer errors from the new output paths.

With io.Writer injection, these fmt.Fprint* calls can now fail, but this file still drops the errors. CI is already failing errcheck at this exact spot, and broken pipes will be reported as success unless the writes are propagated.

Suggested pattern
-	fmt.Fprintln(c.out, "Catalogs:")
+	if _, err := fmt.Fprintln(c.out, "Catalogs:"); err != nil {
+		return fmt.Errorf("write catalogs header: %w", err)
+	}
 	for _, catalog := range catalogs {
-		fmt.Fprintf(c.out, "  - %s\n", catalog)
+		if _, err := fmt.Fprintf(c.out, "  - %s\n", catalog); err != nil {
+			return fmt.Errorf("write catalog row: %w", err)
+		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fmt.Fprintln(c.out, "Catalogs:")
for _, catalog := range catalogs {
fmt.Printf(" - %s\n", catalog)
fmt.Fprintf(c.out, " - %s\n", catalog)
}
if _, err := fmt.Fprintln(c.out, "Catalogs:"); err != nil {
return fmt.Errorf("write catalogs header: %w", err)
}
for _, catalog := range catalogs {
if _, err := fmt.Fprintf(c.out, " - %s\n", catalog); err != nil {
return fmt.Errorf("write catalog row: %w", err)
}
}
🧰 Tools
🪛 GitHub Actions: Build & Verify Pipeline

[error] 90-90: golangci-lint (errcheck): Error return value of fmt.Fprintln is not checked.

🪛 GitHub Check: Code Quality

[failure] 92-92:
Error return value of fmt.Fprintf is not checked (errcheck)


[failure] 90-90:
Error return value of fmt.Fprintln is not checked (errcheck)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cli/commands.go` around lines 90 - 93, The current prints using
fmt.Fprintln and fmt.Fprintf discard write errors (to c.out) which can fail with
injected io.Writer; update the code that iterates over catalogs to capture and
propagate errors: check the returned error from fmt.Fprintln(c.out, "Catalogs:")
and from each fmt.Fprintf(c.out, "  - %s\n", catalog), and return or bubble that
error from the enclosing function (instead of ignoring it) so caller sees write
failures; use the existing c.out, catalogs and the surrounding function's error
return to locate where to add the error checks and early returns.

Comment thread internal/cli/commands.go
Comment thread internal/cli/config.go Outdated
Comment thread internal/cli/repl.go Outdated
Comment on lines 47 to 58
fmt.Fprintln(r.out, "mcp-trino CLI - Interactive Mode")
fmt.Fprintln(r.out, "Type '\\help' for help, '\\quit' or Ctrl-D to exit")
fmt.Fprintln(r.out)

history := []string{}

for {
// Display prompt
fmt.Print(r.prompt)
fmt.Fprint(r.out, r.prompt)

// Read input
if !r.scanner.Scan() {
// EOF (Ctrl-D)
fmt.Println()
fmt.Fprintln(r.out)
return nil
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Propagate REPL write failures instead of ignoring them.

Now that output goes through an injected io.Writer, every fmt.Fprint* here is fallible. These ignored returns are what errcheck is flagging, and a broken pipe currently looks like a clean REPL exit.

Suggested pattern
-	fmt.Fprint(r.out, r.prompt)
+	if _, err := fmt.Fprint(r.out, r.prompt); err != nil {
+		return fmt.Errorf("write prompt: %w", err)
+	}
@@
-			fmt.Fprintf(r.out, "Current format: %s\n", r.commands.format)
+			if _, err := fmt.Fprintf(r.out, "Current format: %s\n", r.commands.format); err != nil {
+				return fmt.Errorf("write format message: %w", err)
+			}
 			return nil

Also applies to: 81-81, 103-106, 153-163, 200-227

🧰 Tools
🪛 GitHub Check: Code Quality

[failure] 54-54:
Error return value of fmt.Fprint is not checked (errcheck)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cli/repl.go` around lines 47 - 58, Writes to the injected io.Writer
in repl.go (e.g., the fmt.Fprintln/Fprint calls using r.out and r.prompt)
currently ignore returned errors; update the REPL to check each fmt.* return
(and any fmt.Fprintf/Println usages at the other occurrences) and propagate
non-nil errors upstream (return or wrap and return) instead of discarding them
so write failures (broken pipe, closed output) cause a proper error exit;
specifically modify the write calls around the greeting, prompt display,
error/newline prints and any other fmt.Fprint* uses in the REPL loop (referenced
by r.out, r.prompt, r.scanner) to handle and return the error.

Comment thread internal/cli/repl.go
tuannvm and others added 2 commits April 16, 2026 07:59
- Extract exitCLIError helper in main.go for consistent exit handling
- YAML --config flag now migrates to JSON path, not parse-only
- Return usageError for invalid schemas/tables flags (exit code 2)
- config profile with no subcommand returns usageError
- CSV truncation message goes to stderr (keeps stdout machine-parseable)
- parseSimpleYAML returns error for malformed input
- REPL checks Scanner.Err() before treating Scan()==false as EOF

Co-authored-by: Claude <noreply@anthropic.com>
- Add gopkg.in/yaml.v3 as direct dep for config loading
- Support both config.json and config.yaml natively (no forced migration)
- Remove ~150 lines of fragile hand-rolled parsing code
- SaveCLIConfig serializes in format matching ConfigPath extension
- Legacy flat 'trino:' YAML auto-migrates to profiles via yaml.Unmarshal
- Add dual struct tags (json + yaml) on all config types
- Rewrite tests with dual-format coverage and roundtrip assertions

Co-authored-by: Claude <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
cmd/integration_test.go (1)

149-183: Integration tests still use YAML config files.

The integration tests create YAML config files (e.g., line 155-162), which is valid since YAML is still supported as a fallback. However, consider adding a test with JSON config to validate the JSON-first behavior described in the PR objectives.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/integration_test.go` around lines 149 - 183, Test
TestIntegration_ConfigFilePrecedence currently writes a YAML config
(configContent) but the PR wants JSON-first behavior validated; update or add a
test that also writes a JSON config file (e.g., configJSONContent) and invokes
the built binary with "--config" pointing to the JSON file (or both files to
assert JSON precedence) using the same tempDir/configFile pattern and the same
exec.Command setup, then assert the output contains the host value from the JSON
file (e.g., "from-json-host") while ensuring the YAML fallback case remains
covered in TestIntegration_ConfigFilePrecedence or a new test; reference
TestIntegration_ConfigFilePrecedence, configFile, configContent and the
exec.Command invocation to locate where to add the JSON creation and assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@cmd/integration_test.go`:
- Around line 149-183: Test TestIntegration_ConfigFilePrecedence currently
writes a YAML config (configContent) but the PR wants JSON-first behavior
validated; update or add a test that also writes a JSON config file (e.g.,
configJSONContent) and invokes the built binary with "--config" pointing to the
JSON file (or both files to assert JSON precedence) using the same
tempDir/configFile pattern and the same exec.Command setup, then assert the
output contains the host value from the JSON file (e.g., "from-json-host") while
ensuring the YAML fallback case remains covered in
TestIntegration_ConfigFilePrecedence or a new test; reference
TestIntegration_ConfigFilePrecedence, configFile, configContent and the
exec.Command invocation to locate where to add the JSON creation and assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 44b70eb8-fcb5-40c1-8386-90343ab85e49

📥 Commits

Reviewing files that changed from the base of the PR and between 70b76f9 and bcddcec.

📒 Files selected for processing (7)
  • cmd/cli.go
  • cmd/integration_test.go
  • cmd/main.go
  • internal/cli/commands.go
  • internal/cli/config.go
  • internal/cli/config_test.go
  • internal/cli/repl.go
✅ Files skipped from review due to trivial changes (1)
  • cmd/main.go

tuannvm and others added 2 commits April 16, 2026 09:31
- Test structured help sections (NAME/SYNOPSIS/DESCRIPTION/COMMANDS/FLAGS/EXAMPLES/ENVIRONMENT)
- Test all subcommand --help outputs contain required sections
- Test usageError returned for unknown commands, invalid format, missing args
- Test --help and --version return nil error
- Test REPL query errors go to stderr, not stdout
- Test REPL meta-command errors go to stderr
- Test REPL format command (set + display)
- Test REPL EOF graceful exit
- Coverage: internal/cli 81%->85%, cmd 9.5%->43.9%

Co-authored-by: Claude <noreply@anthropic.com>
… exit codes

- Document JSON + YAML config support with examples of both formats
- Add Built-in Help section showing --help and per-subcommand help
- Add Exit Codes table (0/1/2)
- Update CLAUDE.md CLI Layer description with new architecture details
- Fix docs/installation.md to use profiles format instead of legacy flat

Co-authored-by: Claude <noreply@anthropic.com>
@tuannvm tuannvm merged commit adf7984 into main Apr 16, 2026
7 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant