Skip to content

feat: add --print-only flag for shell integrations#4

Open
dimensi wants to merge 5 commits into
abakermi:masterfrom
dimensi:feat/print-only
Open

feat: add --print-only flag for shell integrations#4
dimensi wants to merge 5 commits into
abakermi:masterfrom
dimensi:feat/print-only

Conversation

@dimensi

@dimensi dimensi commented Mar 6, 2026

Copy link
Copy Markdown

Summary

  • Adds --print-only flag that outputs only the bare shell command to stdout, with no ANSI colors, no UI chrome, and no execution
  • Errors are routed to stderr in this mode so captures stay clean
  • Enables zsh widget and other shell integrations via cmd="$(nlsh --print-only "...")"

Usage

nlsh --print-only "commit all changes"
# → git commit -am "update"

Zsh widget example (bind to Ctrl+G):

nlsh-widget() {
  local cmd
  cmd="$(nlsh --print-only "$LBUFFER")" || return 0
  [[ -n "$cmd" ]] || return 0
  BUFFER="$cmd"
  CURSOR=${#BUFFER}
  zle redisplay
}
zle -N nlsh-widget
bindkey '^G' nlsh-widget

Test Plan

  • TestPrintOnlyCleanStdout — verifies stdout has no [System], no ANSI codes, no Command:/Execute?
  • All pkg/ tests pass
  • Manual smoke test: cmd="$(nlsh --print-only "list go files")" captures cleanly

Summary by CodeRabbit

  • New Features

    • Added print-only mode (--print-only flag) to output only the generated command without colors, UI elements, or automatic execution.
    • Included shell integration example for zsh with Ctrl+G keybinding.
  • Documentation

    • Updated README with print-only mode usage guide and examples.

Nafranets Nikita and others added 5 commits March 6, 2026 12:39
Add parseArgs helper to strip --print-only from args before dispatch.
Gate the [System] startup print on !printOnly so stdout stays clean
when --print-only is used. Route API key and config errors to stderr
with os.Exit(1) in print-only mode instead of log.Fatal.
- Add TestMain to integration_test.go that builds the nlsh binary before
  tests run, ensuring TestPrintOnlyNoStartupOutput never hits a missing binary
- Extract fatalError(printOnly, format, args) helper to deduplicate the
  repeated if/printOnly stderr+exit / log.Fatalf pattern across backend setup
- Refactor consecutive if/!printOnly + if/printOnly blocks in main() into a
  single clean if/else structure
Add printOnly bool parameter to handleSingleCommand. In print-only mode,
errors are routed to stderr and the bare command is printed to stdout with
no ANSI codes or UI chrome, then returns without executing. Add
TestPrintOnlyCleanStdout integration test to verify clean stdout output.
@coderabbitai

coderabbitai Bot commented Mar 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

nlsh now supports a print-only mode via a --print-only flag that outputs the generated command to stdout without colors, UI elements, or execution. The feature includes new command-line argument parsing, centralized error handling, and integration tests validating clean stdout output.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added "Print-only mode" section describing usage, shell integration example with zsh widget bound to Ctrl+G, and corresponding scenario in Examples section.
Core Feature Implementation
main.go
Introduced --print-only flag parsing via new parseArgs() function; added fatalError() helper for unified error handling; modified handleSingleCommand() to return command string in print-only mode instead of executing; updated main flow to route errors through fatalError() when in print-only or normal modes.
Integration Tests
integration_test.go
Added TestMain() to build nlsh binary before test execution and cleanup afterwards; added TestPrintOnlyCleanStdout() to validate that --print-only output contains no ANSI codes, system UI elements, or UI chrome; imported bytes and fmt packages for test support.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A print-only hop, so clean and bright,
No colors bleed, just pure delight!
Commands flow freely to stdout's care,
Piped through shells with rabbit flair! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 clearly and concisely describes the main change: adding a --print-only flag for shell integrations, which is the primary feature across all modified files (README, main.go, and integration_test.go).

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
README.md (1)

97-108: Consider adding a bash equivalent for broader compatibility.

The zsh widget is well-written, but many users use bash. A bash equivalent using bind -x would help those users.

📝 Optional: Add bash equivalent
**Bash equivalent** (add to `~/.bashrc`):

```bash
nlsh-readline() {
  local cmd
  cmd="$(nlsh --print-only "$READLINE_LINE")" || return
  [[ -n "$cmd" ]] || return
  READLINE_LINE="$cmd"
  READLINE_POINT=${`#READLINE_LINE`}
}
bind -x '"\C-g": nlsh-readline'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 97 - 108, Add a bash equivalent snippet to the README
so bash users can use nlsh via readline: create a function named nlsh-readline
that calls nlsh --print-only with "$READLINE_LINE", checks the result is
non-empty, then assigns it back to READLINE_LINE and sets READLINE_POINT to
${`#READLINE_LINE`}; register it with bind -x '"\C-g": nlsh-readline' and include
this example under the existing zsh widget section so readers of ~/.bashrc can
copy it.
integration_test.go (2)

59-76: Test validates the contract but relies on implicit failure path.

The test correctly validates that stdout remains clean of [System], ANSI codes, and UI chrome. However, it relies on the binary failing early due to missing API credentials.

Consider adding a comment clarifying this dependency, or adding an explicit assertion on the exit code to make the test's assumptions clearer:

📝 Suggested: Make exit code expectation explicit
 func TestPrintOnlyCleanStdout(t *testing.T) {
 	cmd := exec.Command("./nlsh", "--print-only", "list files")
 	var stdout, stderr bytes.Buffer
 	cmd.Stdout = &stdout
 	cmd.Stderr = &stderr
-	// We expect non-zero exit (no API key in test env), but stdout must be clean
-	cmd.Run()
+	// Without API credentials, the binary exits early with non-zero status.
+	// This is expected; the key assertion is that stdout remains clean.
+	err := cmd.Run()
+	if err == nil {
+		t.Log("Note: command succeeded (API key may be set); stdout should still be clean")
+	}
 	out := stdout.String()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integration_test.go` around lines 59 - 76, The test TestPrintOnlyCleanStdout
depends on the binary failing (no API key) but doesn't assert that; update the
test to capture the result of cmd.Run() (replace the bare cmd.Run() call with
err := cmd.Run()), assert that err is non-nil and that the exit code is non-zero
(inspect the error as *exec.ExitError and check ExitCode()) to make the failure
path explicit, and add a short comment above TestPrintOnlyCleanStdout
documenting this dependency; refer to the TestPrintOnlyCleanStdout function and
the cmd.Run() invocation when making the change.

17-28: Consider adding error handling for cleanup and using t.TempDir() pattern.

Two minor observations:

  1. os.Remove("nlsh") error is ignored. While typically fine, logging the error in verbose mode could help debug CI issues.

  2. Building the binary in the working directory could conflict with parallel test runs or leave artifacts if m.Run() panics. Consider using a temp directory.

These are minor since the current approach works for this use case.

♻️ Optional: More robust cleanup
 func TestMain(m *testing.M) {
 	// Build binary for integration tests
-	build := exec.Command("go", "build", "-o", "nlsh", ".")
-	build.Dir = "."
+	binPath := "./nlsh_test_bin"
+	build := exec.Command("go", "build", "-o", binPath, ".")
 	if out, err := build.CombinedOutput(); err != nil {
 		fmt.Fprintf(os.Stderr, "Failed to build nlsh: %v\n%s\n", err, out)
 		os.Exit(1)
 	}
 	code := m.Run()
-	os.Remove("nlsh")
+	if err := os.Remove(binPath); err != nil {
+		fmt.Fprintf(os.Stderr, "Warning: failed to cleanup test binary: %v\n", err)
+	}
 	os.Exit(code)
 }

Note: If you use a different binary name, update TestPrintOnlyCleanStdout to use the same path.

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

In `@integration_test.go` around lines 17 - 28, TestMain currently builds "nlsh"
in the repo root, ignores os.Remove errors, and may leave artifacts if m.Run
panics; modify TestMain to create a temporary directory (os.MkdirTemp) for the
build output, set exec.Command output path and build.Dir to that temp dir, and
defer a cleanup function that removes the built binary and logs any removal
error (use t.Log or fmt.Fprintf to os.Stderr depending on context); ensure the
defer runs before calling m.Run so cleanup always executes, and update any tests
that reference the binary path (e.g., TestPrintOnlyCleanStdout) to use the new
temp binary path variable.
main.go (1)

55-64: Consider supporting --print-only=value and -p short flag.

The current implementation only matches the exact --print-only string. Users might expect --print-only=true or a short -p flag to work. Also, arguments like --print-only-something won't be filtered, which is fine, but --print-onlyextra without space won't be either (correctly so).

This is acceptable for a first iteration, but consider using a flag parsing library (e.g., flag package or pflag) for consistency if you plan to add more flags.

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

In `@main.go` around lines 55 - 64, The parseArgs function only matches the exact
"--print-only" token; update it to also accept the short flag "-p" and
"--print-only=value" forms by checking for the exact tokens "--print-only" and
"-p", and for a prefix "--print-only=" (use strings.HasPrefix to detect and
parse the value) — set printOnly=true when either form is present (treat "-p" or
a "--print-only=" with any non-empty value as true) and only append to remaining
when the arg is neither of those forms; keep the function name parseArgs and the
same return signature.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@integration_test.go`:
- Around line 59-76: The test TestPrintOnlyCleanStdout depends on the binary
failing (no API key) but doesn't assert that; update the test to capture the
result of cmd.Run() (replace the bare cmd.Run() call with err := cmd.Run()),
assert that err is non-nil and that the exit code is non-zero (inspect the error
as *exec.ExitError and check ExitCode()) to make the failure path explicit, and
add a short comment above TestPrintOnlyCleanStdout documenting this dependency;
refer to the TestPrintOnlyCleanStdout function and the cmd.Run() invocation when
making the change.
- Around line 17-28: TestMain currently builds "nlsh" in the repo root, ignores
os.Remove errors, and may leave artifacts if m.Run panics; modify TestMain to
create a temporary directory (os.MkdirTemp) for the build output, set
exec.Command output path and build.Dir to that temp dir, and defer a cleanup
function that removes the built binary and logs any removal error (use t.Log or
fmt.Fprintf to os.Stderr depending on context); ensure the defer runs before
calling m.Run so cleanup always executes, and update any tests that reference
the binary path (e.g., TestPrintOnlyCleanStdout) to use the new temp binary path
variable.

In `@main.go`:
- Around line 55-64: The parseArgs function only matches the exact
"--print-only" token; update it to also accept the short flag "-p" and
"--print-only=value" forms by checking for the exact tokens "--print-only" and
"-p", and for a prefix "--print-only=" (use strings.HasPrefix to detect and
parse the value) — set printOnly=true when either form is present (treat "-p" or
a "--print-only=" with any non-empty value as true) and only append to remaining
when the arg is neither of those forms; keep the function name parseArgs and the
same return signature.

In `@README.md`:
- Around line 97-108: Add a bash equivalent snippet to the README so bash users
can use nlsh via readline: create a function named nlsh-readline that calls nlsh
--print-only with "$READLINE_LINE", checks the result is non-empty, then assigns
it back to READLINE_LINE and sets READLINE_POINT to ${`#READLINE_LINE`}; register
it with bind -x '"\C-g": nlsh-readline' and include this example under the
existing zsh widget section so readers of ~/.bashrc can copy it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2b28d815-8498-4a94-994e-858a58bf7655

📥 Commits

Reviewing files that changed from the base of the PR and between 16416c5 and 26866d8.

📒 Files selected for processing (3)
  • README.md
  • integration_test.go
  • main.go

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