Add explain flag and detailed command explanations#27
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9da2d711c4
ℹ️ 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
Adds an --explain feature to request and display per-command “how it works” explanations, expanding the LLM command schema and wiring the flag into single-shot execution.
Changes:
- Added
--explainflag, updated system prompt generation, and extended the command JSON schema with an optionalexplanationfield. - Updated executor to optionally print command explanations and adjusted call sites for the new function signatures.
- Expanded tests for parsing the explanation field and improved shell detection test coverage.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/shell/detect_test.go | Adds additional windows-shell detection branch tests and new non-windows behavior tests. |
| internal/llm/types.go | Extends Command with optional Explanation JSON field. |
| internal/llm/prompt.go | Adds explain-controlled prompt injection for explanation instructions and updates prompt builder signature. |
| internal/llm/prompt_test.go | Updates prompt tests for new signature and adds explain=true/false coverage. |
| internal/llm/parse_test.go | Adds parsing tests for responses with/without explanation. |
| internal/interactive/repl.go | Updates prompt-building hook and executor invocation for new signatures (currently hardcoded explain=false). |
| internal/executor/executor.go | Adds an explain parameter to Run and prints explanations when enabled. |
| internal/executor/executor_test.go | Updates existing tests for new signature and adds explain on/off tests. |
| cmd/root.go | Introduces --explain flag and threads it through single-shot prompt building and execution. |
| cmd/root_single_shot_test.go | Ensures explain flag is reset between tests and adds a single-shot explain smoke test. |
| .claude/settings.local.json | Expands the allowed command list for local Claude tooling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Rules for commands: | ||
| - "risk" must be "safe" (read-only, informational) or "risky" (modifies files, processes, system state) | ||
| - "certainty" is 0-100, your confidence this is the correct command for what the user asked | ||
| - For multi-step tasks, return multiple commands in order. Use shell constructs like $(...) or pipes to chain when possible | ||
| - Generate commands appropriate for the detected OS and shell | ||
| - Never generate commands that could cause irreversible damage without clear user intent | ||
| {{EXPLAIN_INSTRUCTION}} | ||
| {{PLATFORM_HINTS}} |
There was a problem hiding this comment.
The prompt’s JSON example for command objects doesn’t mention the new optional "explanation" field, but the injected explain instruction requires it when --explain is enabled. This creates conflicting instructions for the model (schema says only command/description/risk/certainty, later says include explanation). Update the JSON example (or add an explicit note) so the schema is consistent with the explain mode output contract.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| func init() { | ||
| rootCmd.Flags().StringVar(&debugFlag, "debug", "", "Debug mode: screen (default) or file (overrides config)") | ||
| // When --debug is given without a value, default to config.DebugScreen | ||
| rootCmd.Flags().Lookup("debug").NoOptDefVal = config.DebugScreen | ||
| rootCmd.Flags().BoolVar(&explainFlag, "explain", false, "Show detailed explanation of each command") | ||
| // Allow flags to be interspersed with args | ||
| rootCmd.Flags().SetInterspersed(true) | ||
| } |
There was a problem hiding this comment.
The new --explain flag is defined at the root command level, but it has no effect in interactive mode (len(args)==0) because the flag value is never passed into interactive.Run, and the interactive REPL hardcodes explain=false for prompt building and command execution. Either propagate explainFlag into interactive mode or make the flag single-shot only (and document/validate that).
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback propogate the explain flag into the interactive mode.
internal/interactive/repl.go
Outdated
| replBuildSystemPrompt = func(osName, shellName, shellVersion, cwd string) string { | ||
| return llm.BuildSystemPrompt(osName, shellName, shellVersion, cwd, false) | ||
| } |
There was a problem hiding this comment.
Interactive mode currently hardcodes explain=false when building the system prompt. If --explain is meant to work consistently across modes, this prevents the model from returning the explanation field in interactive sessions. Consider threading an explain boolean into interactive.Run / replBuildSystemPrompt so the prompt can include the explanation instructions when requested.
| if runErr != nil { | ||
| t.Fatalf("Run() error = %v, want nil", runErr) | ||
| } | ||
| output := buf.String() | ||
| if !strings.Contains(output, "explain-test") { | ||
| t.Errorf("output missing explanation text:\n%s", output) | ||
| } |
There was a problem hiding this comment.
TestRunWithExplainTrue doesn’t actually verify that the explanation text is printed: it asserts the output contains "explain-test", but that substring also comes from executing echo explain-test. This test can pass even if the explanation printing logic is broken. Assert on a string that only appears in the explanation line (e.g., the full Explanation value or a dedicated prefix) to validate the feature.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
internal/shell/detect_test.go
Outdated
| func TestParentShellProcess_NonWindows(t *testing.T) { | ||
| // On non-Windows, parentShellProcess is a no-op stub that returns "". | ||
| got := parentShellProcess() | ||
| if got != "" { | ||
| t.Fatalf("parentShellProcess() = %q, want empty string on non-Windows", got) | ||
| } | ||
| } | ||
|
|
||
| func TestPreferredPowerShell_NonWindows(t *testing.T) { | ||
| // On non-Windows, preferredPowerShell returns "powershell". | ||
| got := preferredPowerShell() | ||
| if got != "powershell" { | ||
| t.Fatalf("preferredPowerShell() = %q, want powershell on non-Windows", got) | ||
| } |
There was a problem hiding this comment.
These tests assert non-Windows-specific behavior for parentShellProcess/preferredPowerShell but don’t skip/guard when running on Windows, where those functions have real implementations and the expectations differ. Add a runtime.GOOS check (t.Skip on windows) or move these into a //go:build !windows test file to keep the test suite portable.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
|
@kriserickson I've opened a new pull request, #29, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@kriserickson I've opened a new pull request, #30, to work on those changes. Once the pull request is ready, I'll request review from you. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@kriserickson I've opened a new pull request, #31, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@kriserickson I've opened a new pull request, #32, to work on those changes. Once the pull request is ready, I'll request review from you. |
…pt template Co-authored-by: kriserickson <325934+kriserickson@users.noreply.github.com>
…ild tag Co-authored-by: kriserickson <325934+kriserickson@users.noreply.github.com>
Co-authored-by: kriserickson <325934+kriserickson@users.noreply.github.com>
Co-authored-by: kriserickson <325934+kriserickson@users.noreply.github.com>
Fix ambiguous explain-flag test assertion and build failure in interactive mode
Move non-Windows shell stub tests behind `//go:build !windows` constraint
Fix inconsistent JSON schema for `explanation` field in prompt template
- Implement ValidatePath function to ensure paths are within the current working directory and do not match blocked patterns. - Create tests for ValidatePath covering valid paths, paths outside CWD, blocked patterns, and allowed files. - Introduce tools for listing directories, reading files, and checking commands with safety checks. - Add tests for tool execution, including handling of blocked files and large files. - Implement environment variable filtering to redact sensitive information. - Enhance command help functionality with fallbacks for Unix systems. - Add comprehensive tests for all new functionalities to ensure reliability and security.
|
@copilot fix the linting. |
|
@kriserickson I've opened a new pull request, #43, to work on those changes. Once the pull request is ready, I'll request review from you. |
Co-authored-by: kriserickson <325934+kriserickson@users.noreply.github.com>
Co-authored-by: kriserickson <325934+kriserickson@users.noreply.github.com>
Fix gofumpt formatting violation in detect_test.go
…uncation handling
Propagate --explain flag into interactive mode
--explainflag to provide detailed explanations for commands.BuildSystemPromptto include explanation instructions.