Skip to content

feat: Windows Console API approval prompts (Phase 3) - #11

Merged
php-workx merged 4 commits into
mainfrom
feat/windows-terminal-approval
Mar 30, 2026
Merged

feat: Windows Console API approval prompts (Phase 3)#11
php-workx merged 4 commits into
mainfrom
feat/windows-terminal-approval

Conversation

@php-workx

@php-workx php-workx commented Mar 30, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replace Phase 1 errNonInteractive stubs with full Windows Console API implementation for interactive approval prompts
  • Open CONIN$/CONOUT$ directly (not stdin/stdout) to preserve anti-spoofing properties
  • Raw mode via GetConsoleMode/SetConsoleMode, keystroke polling via WaitForSingleObject, buffer flush via FlushConsoleInputBuffer
  • Implement real fuse doctor console checks and terminal width detection on Windows
  • Delete Phase 1 scaffolding files (ioctl_windows.go, doctor_termios_windows.go)
  • Extract getContextVars() to shared code for cross-platform use

Files changed (8)

File Change
internal/approve/prompt_windows.go Full Console API prompt (~300 lines, replaces 12-line stub)
internal/approve/prompt_windows_test.go NEW — non-interactive mode tests
internal/approve/prompt_shared.go Add getContextVars() (extracted from unix-only file)
internal/approve/prompt_unix.go Remove getContextVars() (moved to shared)
internal/approve/prompt_test.go Add TestGetContextVars_* tests
internal/cli/doctor_live_windows.go Real console access + raw mode checks
internal/cli/help_width_windows.go Real GetConsoleScreenBufferInfo width detection
internal/approve/ioctl_windows.go DELETED — Phase 1 scaffolding
internal/cli/doctor_termios_windows.go DELETED — Phase 1 scaffolding

Test plan

  • GOOS=windows go build ./... — cross-compile passes
  • GOOS=windows go vet ./... — vet passes
  • go test ./... — all 15 packages pass (no Unix regression)
  • SonarQube quality gate: PASSED
  • Manual Windows testing: fuse run "echo hello" shows approval prompt
  • Manual anti-spoofing: echo "a" | fuse run "echo hello" does NOT auto-approve
  • Manual diagnostics: fuse doctor --security shows PASS for console checks

Specs: .agents/plans/2026-03-28-windows-terminal-approval.md
Pre-mortem: .agents/council/2026-03-28-pre-mortem-windows-terminal-approval.md

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Interactive approval prompts on Windows with timeouts, interrupt handling, and scope selection.
  • Improvements

    • Better Windows console detection, ANSI support probing, and terminal width measurement.
    • Error text now reports a generic "console unavailable" for non-interactive cases.
    • Prompt cancellation now returns explicit errors.
  • Bug Fixes

    • Non-interactive fast-path honored via flag/env; prompt cancellation/interrupts propagate errors.
  • Tests

    • Added Windows-focused tests for non-interactive flows and prompt rendering.
  • Chores

    • Removed obsolete Windows-only stub files and removed context-vars display from the TTY prompt.

Replace Phase 1 errNonInteractive stubs with full Windows Console API
implementation. Interactive approval prompts now work on Windows via
CONIN$/CONOUT$ handles with GetConsoleMode/SetConsoleMode raw mode,
WaitForSingleObject keystroke polling, and FlushConsoleInputBuffer
anti-spoofing. Also implements doctor diagnostics and terminal width
detection for Windows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kody-ai

This comment has been minimized.

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Implement Windows Console API approval prompts with anti-spoofing and diagnostics

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Replace Windows Console API stubs with full interactive approval prompts
  - Open CONIN$/CONOUT$ directly for anti-spoofing
  - Raw mode via GetConsoleMode/SetConsoleMode, keystroke polling via WaitForSingleObject
  - Support scope selection (once, command, session, forever)
• Extract getContextVars() to shared code for cross-platform reuse
• Implement real doctor diagnostics and terminal width detection on Windows
• Delete Phase 1 scaffolding files (ioctl_windows.go, doctor_termios_windows.go)
Diagram
flowchart LR
  A["Phase 1 Stubs<br/>errNonInteractive"] -->|Replace| B["Windows Console API<br/>CONIN$/CONOUT$"]
  B -->|Raw Mode| C["GetConsoleMode<br/>SetConsoleMode"]
  B -->|Input Polling| D["WaitForSingleObject<br/>Keystroke Read"]
  B -->|Anti-Spoofing| E["FlushConsoleInputBuffer"]
  F["getContextVars<br/>Unix-only"] -->|Extract| G["Shared Code<br/>prompt_shared.go"]
  G -->|Used by| B
  H["Doctor Checks<br/>SKIP"] -->|Implement| I["Live TTY Access<br/>Raw Mode PASS"]
  J["Terminal Width<br/>Default 80"] -->|Implement| K["GetConsoleScreenBufferInfo<br/>Real Width"]
Loading

Grey Divider

File Changes

1. internal/approve/prompt_windows.go ✨ Enhancement +304/-9

Full Windows Console API interactive approval implementation

internal/approve/prompt_windows.go


2. internal/approve/prompt_windows_test.go 🧪 Tests +84/-0

New tests for Windows non-interactive mode and rendering

internal/approve/prompt_windows_test.go


3. internal/approve/prompt_shared.go ✨ Enhancement +25/-0

Extract getContextVars() to shared cross-platform code

internal/approve/prompt_shared.go


View more (6)
4. internal/approve/prompt_unix.go Refactoring +0/-23

Remove getContextVars() moved to shared code

internal/approve/prompt_unix.go


5. internal/approve/prompt_test.go 🧪 Tests +56/-1

Add comprehensive tests for getContextVars() function

internal/approve/prompt_test.go


6. internal/approve/ioctl_windows.go Miscellaneous +0/-8

Delete Phase 1 scaffolding stub file

internal/approve/ioctl_windows.go


7. internal/cli/doctor_live_windows.go ✨ Enhancement +69/-6

Implement real console access and raw mode diagnostics

internal/cli/doctor_live_windows.go


8. internal/cli/doctor_termios_windows.go Miscellaneous +0/-8

Delete Phase 1 scaffolding stub file

internal/cli/doctor_termios_windows.go


9. internal/cli/help_width_windows.go ✨ Enhancement +17/-6

Implement real terminal width detection via GetConsoleScreenBufferInfo

internal/cli/help_width_windows.go


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Mar 30, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Windows PromptUser shows approval prompt 📘 Rule violation ✓ Correctness
Description
The Windows implementation now renders and reads an interactive approval prompt instead of blocking
approval behavior on Windows. This violates the requirement that Windows builds must not show
approval prompts and should fail fast or return unsupported deterministically.
Code

internal/approve/prompt_windows.go[R21-98]

+// PromptUser shows a TUI approval prompt on the Windows console (CONIN$/CONOUT$).
+// Returns the user's decision (approved bool), chosen scope, and any error.
+// hookMode: true = short prompt timeout (25s), false = 5min timeout.
+func PromptUser(ctx context.Context, command, reason string, hookMode, nonInteractive bool) (approved bool, scope string, err error) {
+	// Fast path: non-interactive mode returns immediately without locking.
+	if nonInteractive || os.Getenv("FUSE_NON_INTERACTIVE") != "" {
+		return false, "", errNonInteractive
+	}
+
+	// Use TryLock to avoid blocking on the mutex for minutes when another
+	// approval prompt holds the lock. If the lock is unavailable, the DB poll
+	// goroutine can still resolve the request via the TUI.
+	if !ttyMu.TryLock() {
+		return false, "", errNonInteractive
+	}
+	defer ttyMu.Unlock()
+
+	conIn, conOut, err := openConsole(false) // already checked non-interactive above
+	if err != nil {
+		return false, "", err
+	}
+	defer func() { _ = conIn.Close() }()
+	defer func() { _ = conOut.Close() }()
+
+	inHandle := windows.Handle(conIn.Fd())
+
+	// Save original console mode.
+	var origMode uint32
+	if err := windows.GetConsoleMode(inHandle, &origMode); err != nil {
+		return false, "", fmt.Errorf("get console mode: %w", err)
+	}
+
+	// Restore console mode on panic.
+	defer func() {
+		if r := recover(); r != nil {
+			_ = windows.SetConsoleMode(inHandle, origMode)
+			fmt.Fprintf(os.Stderr, "fuse: prompt panic recovered: %v\n", r)
+			approved = false
+			scope = ""
+			err = fmt.Errorf("prompt panic: %v", r)
+		}
+	}()
+
+	// Set up signal handling.
+	sigCh := make(chan os.Signal, 1)
+	signal.Notify(sigCh, os.Interrupt) // only os.Interrupt on Windows (no SIGTERM/SIGHUP)
+	defer signal.Stop(sigCh)
+
+	// Enter raw mode: clear line input, echo, processed input, mouse, and window events.
+	rawMode := origMode &^ (windows.ENABLE_LINE_INPUT |
+		windows.ENABLE_ECHO_INPUT |
+		windows.ENABLE_PROCESSED_INPUT |
+		windows.ENABLE_MOUSE_INPUT |
+		windows.ENABLE_WINDOW_INPUT)
+	if err := windows.SetConsoleMode(inHandle, rawMode); err != nil {
+		return false, "", fmt.Errorf("set raw console mode: %w", err)
+	}
+
+	// Ensure console mode is always restored.
+	restoreConsole := func() {
+		_ = windows.SetConsoleMode(inHandle, origMode)
+	}
+	defer restoreConsole()
+
+	// Flush any stale input before rendering the prompt.
+	_ = windows.FlushConsoleInputBuffer(inHandle)
+
+	// Determine timeout.
+	timeout := 5 * time.Minute
+	if hookMode {
+		timeout = 25 * time.Second
+	}
+
+	// Render the prompt and read the user's decision.
+	renderPrompt(conOut, command, reason)
+	deadline := time.Now().Add(timeout)
+	return readApprovalDecision(ctx, conIn, conOut, deadline, sigCh)
+}
Evidence
PR Compliance ID 224206 requires approval commands/prompts to be blocked on Windows; however, the
new PromptUser implementation explicitly opens CONIN$/CONOUT$, renders an approval prompt, and
reads a user decision on Windows.

Rule 224206: Block APPROVAL commands on Windows builds
internal/approve/prompt_windows.go[21-98]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Windows builds must not show approval prompts or attempt approval behavior, but `internal/approve/prompt_windows.go` now implements an interactive approval prompt.
## Issue Context
Compliance requires that, under Windows conditions, approval-related logic is blocked/short-circuited with a clear deterministic unsupported error or failure status, instead of prompting.
## Fix Focus Areas
- internal/approve/prompt_windows.go[21-98]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. WaitForSingleObject error ignored🐞 Bug ⛯ Reliability
Description
readApprovalDecision/readScope discard the error returned by windows.WaitForSingleObject and treat
any non-WAIT_OBJECT_0 result as a retry, which masks WAIT_FAILED/invalid-handle conditions. This can
incorrectly surface as a prompt timeout instead of returning an actionable console error.
Code

internal/approve/prompt_windows.go[R142-146]

+		// Wait up to 100ms for input to become available.
+		event, _ := windows.WaitForSingleObject(inHandle, 100)
+		if event != windows.WAIT_OBJECT_0 {
+			continue // timeout or error — loop back to check ctx/deadline/signals
+		}
Evidence
Both polling loops ignore the error return from WaitForSingleObject (assigned to _), so
WAIT_FAILED/other failures cannot be distinguished from normal timeouts and will just loop until the
deadline is hit.

internal/approve/prompt_windows.go[121-146]
internal/approve/prompt_windows.go[187-213]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`windows.WaitForSingleObject` errors are ignored in the console polling loops, so real wait failures (e.g., invalid handle / WAIT_FAILED) get treated like a benign timeout and the loop continues until the overall prompt deadline.
### Issue Context
This happens in both `readApprovalDecision` and `readScope`.
### Fix Focus Areas
- internal/approve/prompt_windows.go[121-146]
- internal/approve/prompt_windows.go[187-213]
### Suggested fix
- Capture and check the error return:
- `event, err := windows.WaitForSingleObject(inHandle, 100)`
- If `err != nil`, return a wrapped error (e.g., `fmt.Errorf("wait for console input: %w", err)`) so callers see the real failure reason.
- Optionally handle non-timeout unexpected `event` values (e.g., `WAIT_FAILED`, `WAIT_ABANDONED`) as errors instead of looping silently.
- Apply the same fix in both loops to keep behavior consistent.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. VT output mode not restored🐞 Bug ⛯ Reliability
Description
renderPrompt enables ENABLE_VIRTUAL_TERMINAL_PROCESSING on the output console handle (CONOUT$) but
never restores the prior output mode. This permanently mutates console output settings for the
remainder of the process after a single prompt.
Code

internal/approve/prompt_windows.go[R253-260]

+	// Try to enable ANSI/VT processing on the output handle.
+	var outMode uint32
+	if err := windows.GetConsoleMode(outHandle, &outMode); err == nil {
+		if err := windows.SetConsoleMode(outHandle, outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
+			// VT processing enabled — use ANSI colors.
+			renderPromptANSI(conOut, command, reason)
+			return
+		}
Evidence
The Windows prompt restores the *input* handle mode in PromptUser, but the *output* handle mode is
modified in renderPrompt via SetConsoleMode and no corresponding restore is performed on any exit
path.

internal/approve/prompt_windows.go[247-265]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The prompt enables VT processing on the CONOUT$ handle but doesn’t restore the original output console mode afterward, leaving global console state changed.
### Issue Context
`PromptUser` already saves/restores the input console mode. Output mode should follow the same pattern when it is modified.
### Fix Focus Areas
- internal/approve/prompt_windows.go[247-265]
- internal/approve/prompt_windows.go[24-98]
### Suggested fix
- Move VT enable/restore management to `PromptUser` (so it can `defer` restore around the entire prompt lifecycle):
- Capture `outOrigMode` via `GetConsoleMode(outHandle, &outOrigMode)`.
- If enabling VT succeeds, `defer windows.SetConsoleMode(outHandle, outOrigMode)`.
- Alternatively, have `renderPrompt` return whether it modified the output mode + the original mode, and let `PromptUser` restore it via `defer`.
- Keep the existing plain-text fallback behavior unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55294693-5a73-453d-bac7-667e80cfc473

📥 Commits

Reviewing files that changed from the base of the PR and between dbe4904 and 1c43ed3.

📒 Files selected for processing (3)
  • internal/cli/help.go
  • internal/cli/help_width_unix.go
  • internal/cli/help_width_windows.go

Walkthrough

Removed Windows ioctl stubs; added a shared env-var collector and tests; stopped displaying context vars in the Unix TTY prompt and made cancellation return an error; implemented a full interactive Windows console prompt with tests; improved Windows terminal detection and width logic.

Changes

Cohort / File(s) Summary
Removed Windows ioctl constants
internal/approve/ioctl_windows.go, internal/cli/doctor_termios_windows.go
Deleted Windows-only files that defined ioctl constants (ioctlGetTermios, ioctlSetTermios, doctorIoctlGetTermios, doctorIoctlSetTermios).
Approve: shared env helper & tests
internal/approve/prompt_shared.go, internal/approve/prompt_test.go
Added getContextVars() to collect selected environment variables into a comma-separated KEY=VALUE string; added tests for empty, single, and multiple-variable cases.
Approve: Unix prompt adjustments
internal/approve/prompt_unix.go
Removed the prompt’s use/collection of context vars and changed readApprovalDecision to return an error when ctx.Done() triggers (cancellation now surfaces an error).
Approve: Windows interactive prompt & tests
internal/approve/prompt_windows.go, internal/approve/prompt_windows_test.go
Replaced Windows stub with a full interactive PromptUser implementation (open CONIN$/CONOUT$, preserve/restore console modes, raw input, signal handling, ANSI/plain rendering, approval+scope selection, timeouts) and added Windows-only tests for non-interactive fast paths and prompt rendering.
CLI: Windows console/terminal utilities
internal/cli/doctor_live_windows.go, internal/cli/help_width_windows.go
Windows checks now open CONIN$ and verify/set console mode for live/raw-mode checks; terminalWidth() queries console screen buffer info; isTerminal(fd) now returns true when GetConsoleMode succeeds; added supportsANSI() for Windows.
CLI: ANSI support and help logic
internal/cli/help.go, internal/cli/help_width_unix.go
shouldColorize() now requires both terminal-ness and supportsANSI(); added supportsANSI() on Unix (returns true) to unify ANSI capability checks.
Tests: Windows skip message update
internal/cli/doctor_test.go
Updated Windows test skip message to explain console capability checks require an interactive console (CONIN$), commonly absent in CI.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% 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
Title check ✅ Passed The title clearly summarizes the main change: implementing Windows Console API approval prompts as Phase 3 of a feature, which matches the primary focus of the changeset.
Description check ✅ Passed The description provides a comprehensive summary, detailed file list, and test plan. It documents the major changes (Phase 1 stub replacement, Console API implementation, extracted shared code), includes new/deleted files, and covers test coverage for build, vet, and unit tests.

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


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

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements interactive approval prompts for Windows by utilizing the Windows Console API (CONIN$/CONOUT$). It includes logic for raw mode handling, signal processing, and ANSI color support with a plain text fallback. Additionally, it migrates shared environment variable logic to a common file and updates the 'doctor' command to verify console access on Windows. A redundant environment variable check was identified in the console opening logic.

Comment thread internal/approve/prompt_windows.go
Comment thread internal/approve/prompt_windows.go
Comment thread internal/approve/prompt_windows.go
Comment thread internal/approve/prompt_windows.go
@codecov

codecov Bot commented Mar 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.94737% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.27%. Comparing base (e297106) to head (1c43ed3).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
internal/cli/help_width_unix.go 0.00% 2 Missing ⚠️
internal/approve/prompt_unix.go 0.00% 1 Missing ⚠️
internal/cli/help.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #11      +/-   ##
==========================================
+ Coverage   71.17%   71.27%   +0.09%     
==========================================
  Files          73       73              
  Lines        8726     8728       +2     
==========================================
+ Hits         6211     6221      +10     
+ Misses       2022     2011      -11     
- Partials      493      496       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread internal/approve/prompt_windows.go
Comment thread internal/approve/prompt_windows.go
Comment thread internal/cli/doctor_live_windows.go

@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.

Actionable comments posted: 5

🧹 Nitpick comments (1)
internal/approve/prompt_test.go (1)

47-59: Optional: Simplify cleanup logic.

The current approach uses t.Setenv, os.Unsetenv, and a manual t.Cleanup together. Since getContextVars() checks val != "", calling t.Setenv(v, "") alone is sufficient — it sets the var to empty (which getContextVars treats as "not set") and automatically restores the original value after the test.

♻️ Simplified version
 func TestGetContextVars_Empty(t *testing.T) {
 	// With no relevant env vars set, should return empty string.
-	// Save and clear any that might be set.
 	vars := []string{
 		"AWS_PROFILE", "AWS_REGION", "AWS_DEFAULT_REGION",
 		"TF_WORKSPACE", "TF_VAR_environment",
 		"KUBECONFIG", "KUBECONTEXT",
 		"GCP_PROJECT", "GOOGLE_CLOUD_PROJECT",
 		"AZURE_SUBSCRIPTION",
 	}
-	saved := make(map[string]string)
 	for _, v := range vars {
-		if val, ok := os.LookupEnv(v); ok {
-			saved[v] = val
-			t.Setenv(v, "")
-			os.Unsetenv(v)
-		}
+		t.Setenv(v, "") // t.Setenv handles save/restore automatically
 	}
-	t.Cleanup(func() {
-		for k, v := range saved {
-			os.Setenv(k, v)
-		}
-	})

 	got := getContextVars()
 	if got != "" {
 		t.Errorf("expected empty string, got %q", got)
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/approve/prompt_test.go` around lines 47 - 59, Replace the manual
save/restore and os.Unsetenv calls with just t.Setenv(v, "") in the loop because
getContextVars treats empty string as unset; remove the saved map and t.Cleanup
block. Specifically, inside the loop over vars replace the
os.LookupEnv/save/unset logic with a single t.Setenv(v, "") call and delete the
saved map and t.Cleanup closure that restores env vars.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/approve/prompt_test.go`:
- Around line 67-73: The tests TestGetContextVars_SingleVar and
TestGetContextVars_MultipleVars are flaky because they don't clear other tracked
environment variables before asserting; modify these tests to unset or reset all
tracked vars used by getContextVars (e.g., AWS_PROFILE, AWS_REGION,
TF_WORKSPACE, KUBECONFIG, etc.) at the start of each test (use t.Setenv(var, "")
or os.Unsetenv) so the environment is deterministic, and to avoid duplication
extract the tracked vars slice into a package-level variable like
contextVarsForTest and have getContextVars (or the tests) reference that shared
slice so TestGetContextVars_Empty can also reuse it.

In `@internal/approve/prompt_windows.go`:
- Around line 69-77: The current rawMode calculation clears ENABLE_MOUSE_INPUT
but leaves Quick Edit enabled; update the mode setup in the block around
rawMode/origMode and the call to windows.SetConsoleMode(inHandle, rawMode) to
also set windows.ENABLE_EXTENDED_FLAGS and explicitly clear
windows.ENABLE_QUICK_EDIT_MODE (i.e., include windows.ENABLE_EXTENDED_FLAGS in
the bits you set and &^ clear windows.ENABLE_QUICK_EDIT_MODE from origMode when
building rawMode) so Quick Edit is disabled before calling SetConsoleMode.
- Around line 142-145: The loops calling windows.WaitForSingleObject currently
ignore its returned error and treat any non-WAIT_OBJECT_0 as a timeout; change
both call sites to check the error value and surface real API failures instead
of retrying: after calling windows.WaitForSingleObject(inHandle, 100) (and the
other analogous call), if err != nil return or propagate an error (with context
like "WaitForSingleObject failed for inHandle") rather than continue looping,
and only treat non-WAIT_OBJECT_0 as a benign timeout when err == nil; update the
surrounding functions that call WaitForSingleObject to return/propagate the
error accordingly so the approval prompt does not spin on real API failures.

In `@internal/cli/doctor_live_windows.go`:
- Around line 84-89: The checkLiveForegroundProcessGroup function currently
returns an out-of-band "SKIP" status; change it to use the documented doctor
contract by returning status "WARN" (or "PASS"/"FAIL" as appropriate) so it
aligns with other Windows live-console checks; update the returned checkResult
in checkLiveForegroundProcessGroup (and keep the name
checkNameLiveForegroundHandoff) to set status: "WARN" and retain or slightly
adjust the detail to "Windows job object support not yet implemented (planned:
Phase 4)".

In `@internal/cli/help_width_windows.go`:
- Around line 23-25: isTerminal() currently only checks GetConsoleMode and
therefore may return true even when Windows VT processing is not enabled; update
the logic so help colorization is safe by enabling VT mode (or detecting it
explicitly) before returning true. Specifically, in isTerminal(fd int) call
windows.GetConsoleMode to get outMode, then attempt to set
windows.SetConsoleMode(outHandle,
outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); if SetConsoleMode succeeds
return true, otherwise fall back to false (or keep original console mode and
return false); ensure this change integrates with shouldColorize() so ANSI
colors are only emitted when VT processing is enabled.

---

Nitpick comments:
In `@internal/approve/prompt_test.go`:
- Around line 47-59: Replace the manual save/restore and os.Unsetenv calls with
just t.Setenv(v, "") in the loop because getContextVars treats empty string as
unset; remove the saved map and t.Cleanup block. Specifically, inside the loop
over vars replace the os.LookupEnv/save/unset logic with a single t.Setenv(v,
"") call and delete the saved map and t.Cleanup closure that restores env vars.
🪄 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: 13e6d5f0-2d53-4e6a-bada-62542baaf802

📥 Commits

Reviewing files that changed from the base of the PR and between e297106 and 0da2a06.

📒 Files selected for processing (9)
  • internal/approve/ioctl_windows.go
  • internal/approve/prompt_shared.go
  • internal/approve/prompt_test.go
  • internal/approve/prompt_unix.go
  • internal/approve/prompt_windows.go
  • internal/approve/prompt_windows_test.go
  • internal/cli/doctor_live_windows.go
  • internal/cli/doctor_termios_windows.go
  • internal/cli/help_width_windows.go
💤 Files with no reviewable changes (3)
  • internal/approve/prompt_unix.go
  • internal/cli/doctor_termios_windows.go
  • internal/approve/ioctl_windows.go

Comment thread internal/approve/prompt_test.go
Comment thread internal/approve/prompt_windows.go
Comment thread internal/approve/prompt_windows.go Outdated
Comment thread internal/cli/doctor_live_windows.go
Comment thread internal/cli/help_width_windows.go
- Restore CONOUT$ console mode after approval prompt (VT processing leak)
- Fix ctx cancellation returning nil error causing RequestApproval hang
  (pre-existing bug, fixed on both Unix and Windows)
- Handle WaitForSingleObject WAIT_FAILED with early error return
- Fix flaky TestGetContextVars_SingleVar (clear all tracked env vars)
- Simplify TestGetContextVars_Empty cleanup (use only t.Setenv)
- Strengthen TestRenderPromptPlain to verify rendered content
- Make errNonInteractive message platform-neutral (console unavailable)
- Update stale Phase 3 skip comment in doctor_test.go

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kody-ai

This comment has been minimized.

@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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
internal/approve/prompt_windows.go (1)

75-83: ⚠️ Potential issue | 🟠 Major

Disable Quick Edit mode to prevent mouse-triggered selection freeze.

Clearing ENABLE_MOUSE_INPUT alone leaves Quick Edit mode enabled. A stray mouse click can put the console into selection mode and freeze the approval prompt. The Windows API requires setting ENABLE_EXTENDED_FLAGS when modifying Quick Edit mode.

Suggested fix
-	rawMode := origMode &^ (windows.ENABLE_LINE_INPUT |
+	rawMode := (origMode | windows.ENABLE_EXTENDED_FLAGS) &^ (windows.ENABLE_LINE_INPUT |
 		windows.ENABLE_ECHO_INPUT |
 		windows.ENABLE_PROCESSED_INPUT |
 		windows.ENABLE_MOUSE_INPUT |
+		windows.ENABLE_QUICK_EDIT_MODE |
 		windows.ENABLE_WINDOW_INPUT)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/approve/prompt_windows.go` around lines 75 - 83, The current
raw-mode calculation clears ENABLE_MOUSE_INPUT but leaves Quick Edit mode
enabled, which can cause the console to enter selection mode on mouse clicks;
update the code that computes rawMode (using origMode and the rawMode variable
passed to windows.SetConsoleMode) to first set ENABLE_EXTENDED_FLAGS on the mode
and then clear ENABLE_QUICK_EDIT_MODE along with ENABLE_MOUSE_INPUT (and the
other flags: ENABLE_LINE_INPUT, ENABLE_ECHO_INPUT, ENABLE_PROCESSED_INPUT,
ENABLE_WINDOW_INPUT) before calling windows.SetConsoleMode; ensure the mode you
pass includes WINDOWS.ENABLE_EXTENDED_FLAGS so Quick Edit is properly disabled.
🧹 Nitpick comments (1)
internal/approve/prompt_windows.go (1)

282-282: Consider handling os.Getwd() errors for security context.

Both renderPromptANSI (line 282) and renderPromptPlain (line 304) ignore the error from os.Getwd(). If the working directory is deleted or inaccessible, the prompt silently omits this context. For a security-sensitive approval prompt, displaying a placeholder is preferable to silent omission.

Suggested approach
-	cwd, _ := os.Getwd()
+	cwd, err := os.Getwd()
+	if err != nil {
+		cwd = "(unavailable)"
+	}

Also applies to: 304-304

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

In `@internal/approve/prompt_windows.go` at line 282, renderPromptANSI and
renderPromptPlain currently ignore errors from os.Getwd() (cwd, _ :=
os.Getwd()), which can silently omit working-directory context; update both
functions to check the error returned by os.Getwd(), and when it fails set cwd
to a clear placeholder (e.g., "<unknown cwd>" or similar) so the prompt shows an
explicit fallback instead of omitting the value; reference the cwd variable and
the functions renderPromptANSI and renderPromptPlain when making this 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 `@internal/approve/prompt_windows.go`:
- Around line 221-225: In readScope, handle windows.WaitForSingleObject failures
the same way as readApprovalDecision: check if event == windows.WAIT_FAILED and
return or propagate the underlying error instead of continuing the loop; update
the code around the call to windows.WaitForSingleObject(inHandle, 100) to detect
WAIT_FAILED, retrieve the last error (e.g., syscall.GetLastError or the
equivalent used elsewhere), and return that error so scope selection surfaces
API failures instead of silently spinning.

---

Duplicate comments:
In `@internal/approve/prompt_windows.go`:
- Around line 75-83: The current raw-mode calculation clears ENABLE_MOUSE_INPUT
but leaves Quick Edit mode enabled, which can cause the console to enter
selection mode on mouse clicks; update the code that computes rawMode (using
origMode and the rawMode variable passed to windows.SetConsoleMode) to first set
ENABLE_EXTENDED_FLAGS on the mode and then clear ENABLE_QUICK_EDIT_MODE along
with ENABLE_MOUSE_INPUT (and the other flags: ENABLE_LINE_INPUT,
ENABLE_ECHO_INPUT, ENABLE_PROCESSED_INPUT, ENABLE_WINDOW_INPUT) before calling
windows.SetConsoleMode; ensure the mode you pass includes
WINDOWS.ENABLE_EXTENDED_FLAGS so Quick Edit is properly disabled.

---

Nitpick comments:
In `@internal/approve/prompt_windows.go`:
- Line 282: renderPromptANSI and renderPromptPlain currently ignore errors from
os.Getwd() (cwd, _ := os.Getwd()), which can silently omit working-directory
context; update both functions to check the error returned by os.Getwd(), and
when it fails set cwd to a clear placeholder (e.g., "<unknown cwd>" or similar)
so the prompt shows an explicit fallback instead of omitting the value;
reference the cwd variable and the functions renderPromptANSI and
renderPromptPlain when making this 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: f72677bc-31eb-42be-bd55-84519ab1ccd3

📥 Commits

Reviewing files that changed from the base of the PR and between 0da2a06 and d455ae8.

📒 Files selected for processing (6)
  • internal/approve/prompt_shared.go
  • internal/approve/prompt_test.go
  • internal/approve/prompt_unix.go
  • internal/approve/prompt_windows.go
  • internal/approve/prompt_windows_test.go
  • internal/cli/doctor_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/approve/prompt_test.go

Comment thread internal/approve/prompt_windows.go
Comment thread internal/approve/prompt_unix.go
Comment thread internal/approve/prompt_windows_test.go
Comment thread internal/cli/doctor_test.go
…efer)

- Handle WAIT_FAILED in readScope (was missed by initial fix, caught by
  CodeRabbit second-round review)
- Add defer f.Close() in TestRenderPromptPlain to prevent fd leak on panic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kody-ai

This comment has been minimized.

@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 (1)
internal/approve/prompt_windows.go (1)

221-228: Capture WaitForSingleObject error details and use Windows constants for consistency.

Line 222 discards the error from WaitForSingleObject. While the code fails closed, capturing and logging the error improves diagnostics when console handles are invalid—consistent with the existing pattern at line 232 (slog.Debug("console read failed...")). Additionally, replace the magic number 0xFFFFFFFF with the windows.WAIT_FAILED constant for consistency with the nearby windows.WAIT_OBJECT_0 usage, and explicitly check windows.WAIT_TIMEOUT (258) for clarity.

Proposed refactor
-		event, _ := windows.WaitForSingleObject(inHandle, 100)
-		if event == 0xFFFFFFFF { // WAIT_FAILED — console handle invalid
-			return "", true // deny on failure
-		}
-		if event != windows.WAIT_OBJECT_0 {
-			continue
-		}
+		event, waitErr := windows.WaitForSingleObject(inHandle, 100)
+		if event == windows.WAIT_FAILED {
+			slog.Debug("wait for console input failed while selecting approval scope", "error", waitErr)
+			return "", true // deny on failure
+		}
+		if event == windows.WAIT_TIMEOUT {
+			continue
+		}
+		if event != windows.WAIT_OBJECT_0 {
+			slog.Debug("unexpected wait result while selecting approval scope", "event", event)
+			return "", true
+		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/approve/prompt_windows.go` around lines 221 - 228, Capture and
handle the error returned by windows.WaitForSingleObject (called with inHandle)
instead of discarding it: replace the magic 0xFFFFFFFF with windows.WAIT_FAILED,
explicitly check for windows.WAIT_TIMEOUT vs windows.WAIT_OBJECT_0, and when
WaitForSingleObject returns WAIT_FAILED or a non-WAIT_OBJECT_0 result log the
error details via slog.Debug (similar to the existing pattern around console
read) while preserving the current closed-fail behavior (return "", true on
failure).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/approve/prompt_windows.go`:
- Around line 221-228: Capture and handle the error returned by
windows.WaitForSingleObject (called with inHandle) instead of discarding it:
replace the magic 0xFFFFFFFF with windows.WAIT_FAILED, explicitly check for
windows.WAIT_TIMEOUT vs windows.WAIT_OBJECT_0, and when WaitForSingleObject
returns WAIT_FAILED or a non-WAIT_OBJECT_0 result log the error details via
slog.Debug (similar to the existing pattern around console read) while
preserving the current closed-fail behavior (return "", true on failure).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 92db0bcc-ac5f-4caf-8205-054cb0054642

📥 Commits

Reviewing files that changed from the base of the PR and between d455ae8 and dbe4904.

📒 Files selected for processing (2)
  • internal/approve/prompt_windows.go
  • internal/approve/prompt_windows_test.go
✅ Files skipped from review due to trivial changes (1)
  • internal/approve/prompt_windows_test.go

shouldColorize() now calls supportsANSI() which probes whether the
console supports ENABLE_VIRTUAL_TERMINAL_PROCESSING. On legacy conhost
(pre-Windows 10 1511), the probe fails and help output renders as
plain text instead of raw escape sequences. Unix returns true
unconditionally since all modern terminals support ANSI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kody-ai

kody-ai Bot commented Mar 30, 2026

Copy link
Copy Markdown

Kody Review Complete

Great news! 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Cross File
Business Logic

Access your configuration settings here.

@php-workx
php-workx merged commit ee2c689 into main Mar 30, 2026
12 checks passed
@php-workx
php-workx deleted the feat/windows-terminal-approval branch March 30, 2026 09:54
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