[hooks] interactive arrow-key select for multi-CLI install prompt#222
Conversation
Replace the [B]oth/[C]laude/[D]codex text prompt that fires when both Claude Code and OpenAI Codex are detected during `policies --install` with an arrow-key single-select that mirrors the visual style of the existing policy selector (pointer, dim/bold rows, footer hint line). Non-TTY behaviour is unchanged: defaults to all detected CLIs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the "single-keypress prompt: install for [B]oth, [C]laude, or co[D]ex" line in the multi-CLI detection section with a description of the new arrow-key single-select. Translations will be regenerated by .github/workflows/translate-docs.yml on the next push to main. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR replaces the single-keypress B/C/D text prompt with an interactive arrow-key single-select menu for CLI selection when multiple agent CLIs are detected. Documentation and implementation are updated accordingly. Non-TTY behavior now defaults to installing all detected CLIs. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 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.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/hooks/install-prompt.ts (1)
104-123: Extract duplicated ANSI-safe truncation helper.
truncateLineis duplicated in two places (Line 104 and Line 250). A single shared helper will reduce drift risk when terminal rendering logic is adjusted later.Refactor sketch
+function truncateAnsiLine(line: string, width: number): string { + let visual = 0; + let result = ""; + let i = 0; + while (i < line.length) { + if (line[i] === "\x1B" && line[i + 1] === "[") { + let j = i + 2; + while (j < line.length && !/[A-Za-z]/.test(line[j])) j++; + j++; + result += line.slice(i, j); + i = j; + } else { + if (visual >= width) break; + result += line[i]; + visual++; + i++; + } + } + return result; +}Then replace both call sites with
truncateAnsiLine(...).Also applies to: 249-269
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/install-prompt.ts` around lines 104 - 123, The truncateLine ANSI-aware truncation function is duplicated; extract it into a single shared helper named truncateAnsiLine (preserving current logic: track visual width, copy ANSI escape sequences verbatim, and stop when visual >= width) and export it for reuse, then replace both existing implementations (the current truncateLine and the other duplicate used elsewhere) with calls to truncateAnsiLine so both places use the shared helper; update any imports/exports and ensure function name references (truncateLine -> truncateAnsiLine) at both call sites are changed accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/hooks/install-prompt.ts`:
- Around line 64-66: The current check only gates interactivity on
process.stdin.isTTY which can still allow prompts when stdout is redirected;
update the guard in the install prompt logic so both process.stdin.isTTY and
process.stdout.isTTY must be true before entering interactive prompt mode (i.e.,
return detected when either stream is not a TTY). Locate the condition in
src/hooks/install-prompt.ts around the block that reads "if
(!process.stdin.isTTY) return detected;" and change it to require both stdio
TTYs (use process.stdin.isTTY && process.stdout.isTTY) so prompting only happens
in fully interactive terminals.
---
Nitpick comments:
In `@src/hooks/install-prompt.ts`:
- Around line 104-123: The truncateLine ANSI-aware truncation function is
duplicated; extract it into a single shared helper named truncateAnsiLine
(preserving current logic: track visual width, copy ANSI escape sequences
verbatim, and stop when visual >= width) and export it for reuse, then replace
both existing implementations (the current truncateLine and the other duplicate
used elsewhere) with calls to truncateAnsiLine so both places use the shared
helper; update any imports/exports and ensure function name references
(truncateLine -> truncateAnsiLine) at both call sites are changed accordingly.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7ce8734a-1b53-4a7d-a9dd-9bc8a4409779
📒 Files selected for processing (3)
CHANGELOG.mddocs/configuration.mdxsrc/hooks/install-prompt.ts
| // Multiple detected. Prompt or default. | ||
| if (!process.stdin.isTTY) return detected; // non-interactive: install for all detected | ||
|
|
There was a problem hiding this comment.
Gate interactivity on both stdin and stdout TTYs.
At Line 65, checking only process.stdin.isTTY can still trigger an interactive prompt when output is redirected, causing hidden waits/escape-sequence output. Please require both streams to be TTY before entering prompt mode.
Proposed fix
- if (!process.stdin.isTTY) return detected; // non-interactive: install for all detected
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return detected; // non-interactive: install for all detected🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/hooks/install-prompt.ts` around lines 64 - 66, The current check only
gates interactivity on process.stdin.isTTY which can still allow prompts when
stdout is redirected; update the guard in the install prompt logic so both
process.stdin.isTTY and process.stdout.isTTY must be true before entering
interactive prompt mode (i.e., return detected when either stream is not a TTY).
Locate the condition in src/hooks/install-prompt.ts around the block that reads
"if (!process.stdin.isTTY) return detected;" and change it to require both stdio
TTYs (use process.stdin.isTTY && process.stdout.isTTY) so prompting only happens
in fully interactive terminals.
Bumps package.json from 0.0.9-beta.2 to 0.0.9 and rolls the ## Unreleased changelog section into ## 0.0.9 — 2026-04-28. 0.0.9 contents: Features: - OpenAI Codex hook integration via `failproofai policies --install --cli codex` (or --cli claude codex for both); supports all six Codex hook events, PermissionRequest wired through policy-evaluator, per-CLI telemetry tagging, interactive arrow-key CLI selector when both agents detected (#220, #222, #223) - Surface Slack community link in CLI banner and Reach Us menu (#225) Fixes: - Trailing blank line after enabled-policies summary in install output (#224) - Resolve hook bin via $CLAUDE_PROJECT_DIR (was relative path) (#219) - Mintlify validation of Arabic built-in-policies docs Docs: - Bump built-in policy count from 32 to 39 in README and translations (#207) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Promotes the version from 0.0.9-beta.3 to 0.0.9 stable. The ## 0.0.9 — 2026-04-28 changelog section is already up to date with the contents below; no CHANGELOG churn required. 0.0.9 contents: Features: - OpenAI Codex hook integration via `failproofai policies --install --cli codex` (or --cli claude codex for both); supports all six Codex hook events, PermissionRequest wired through policy-evaluator, per-CLI telemetry tagging, interactive arrow-key CLI selector when both agents detected (#220, #222, #223) - Activity dashboard CLI filter alongside event-type, policy, and session-id filters; Codex sessions in the activity feed are now clickable, with the existing log viewer rendering Codex transcripts via lib/codex-sessions.ts (#226) - Surface Slack community link in CLI banner and Reach Us menu (#225) - Show OpenAI Codex projects on the /projects page alongside Claude Code projects, with CLI badges per row and per session; /project/[name] is Codex-aware and the session viewer renders the CLI badge beside the Session Log header (#232) Fixes: - Trailing blank line after enabled-policies summary in install output (#224) - Resolve hook bin via $CLAUDE_PROJECT_DIR (was relative path) (#219) - Mintlify validation of Arabic built-in-policies docs - Mintlify parse error in docs/de/dashboard.mdx (#229) - block-read-outside-cwd false-deny on globs and -v host:/path (#230) - Decouple hook chain from clsx/tailwind via lib/format-date.ts (#231) Docs: - Bump built-in policy count from 32 to 39 in README and translations (#207) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
[B]oth / [C]laude / [D]codextext prompt that fires when both Claude Code and OpenAI Codex are detected duringfailproofai policies --installwith an arrow-key single-select.❯, dim/bold rows, footer hint line). Options:Both,Claude Code only,OpenAI Codex only. Keys: ↑↓ to move, Enter to select, ^C to quit.--cliflag still short-circuits the prompt.Test plan
bun run test:run __tests__/hooks/— 733/733 passbunx tsc --noEmitclean on changed filebun run lintclean (only pre-existing unrelated warning)bun build --target=node --format=cjs --outfile=dist/index.js src/index.tssucceedsbun bin/failproofai.mjs p -iin a TTY with bothclaudeandcodexon PATH🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
failproofai policies --installnow uses an interactive arrow-key menu for selecting agent CLIs when multiple are detected, replacing the previous single-keypress prompt.Documentation