diff --git a/README.md b/README.md index 5fbb75a..42f5d39 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ Unknown, misspelled, or wrong-subcommand `--flags` exit 2 (`unknown flag --name | `--rationguard` | Start rationguard watcher alongside pluk (requires `rationguard` on `PATH` — `npm install -g @hivecommons/rationguard` — or `--rationguard-bin`; pluk never fetches it from the registry implicitly) | | `--rationguard-bin=` | Explicit rationguard command to run instead of the one on `PATH`, e.g. `--rationguard-bin='npx --yes @hivecommons/rationguard@0.13.0'` (also `PLUK_RATIONGUARD_BIN`); the flag is ignored with a warning without `--rationguard` | | `--rebuttal=log\|send` | Log rebuttals or auto-send them when rationguard detects excuses; other values exit with code 2 before attaching. Ignored with a warning without `--rationguard` | -| `--dangerous` | Skip CLI permission prompts (`--dangerously-skip-permissions` for claude, `--allow-all-tools` for copilot, `--full-auto` for codex, `--non-interactive` for goose, `--approval-mode yolo` for omp) | +| `--dangerous` | Skip CLI permission prompts (`--dangerously-skip-permissions` for claude, `--allow-all-tools` for copilot, `--full-auto` for codex, `--non-interactive` for goose, `--approval-mode yolo` for omp, `--yolo` for gemini, `--yes-always` for aider) | | `--dir=/path` | Working directory for the agent | | `--cli-args="..."` | Extra arguments to pass to the CLI | | `--no-open` | Don't attach: without `--rationguard`, leave the session detached and print the attach hint; with `--rationguard`, don't open a new window (macOS) or print the hint (elsewhere) | diff --git a/changelog.d/fixed-360.md b/changelog.d/fixed-360.md new file mode 100644 index 0000000..8972f14 --- /dev/null +++ b/changelog.d/fixed-360.md @@ -0,0 +1 @@ +- `pluk attach --dangerous` now passes `--yolo` to gemini and `--yes-always` to aider; a CLI with no known auto-approve flag now prints a warning even without `--verbose`. diff --git a/src/attach.ts b/src/attach.ts index 0ff2f40..453e97c 100644 --- a/src/attach.ts +++ b/src/attach.ts @@ -167,6 +167,8 @@ function resolveDangerousFlag(cli: string): string { codex: '--full-auto', goose: '--non-interactive', omp: '--approval-mode yolo', + gemini: '--yolo', + aider: '--yes-always', }; return DANGEROUS_FLAGS[cli] ?? ''; } @@ -341,7 +343,7 @@ export function attach(opts: AttachOptions): void { // and a single shellQuote() would hand the CLI one argv entry. fullCmd += ` ${splitShellWords(dangerFlag).map(shellQuote).join(' ')}`; } else { - log(`no dangerous/auto flag known for cli=${cli}, skipping`); + console.error(`Warning: --dangerous has no known auto-approve flag for cli=${cli}; starting without it`); } } console.error(`Starting ${cli}: ${fullCmd}`); diff --git a/test/attach-orchestration.test.js b/test/attach-orchestration.test.js index 6d14d38..04976a8 100644 --- a/test/attach-orchestration.test.js +++ b/test/attach-orchestration.test.js @@ -220,15 +220,28 @@ test('re-attaching to an existing session replaces the pipe instead of toggling assert.ok(!/ -o /.test(pipeCall), `pipe-pane must not use the -o toggle: ${pipeCall}`); }); -test('attach --dangerous with a CLI that has no auto-flag logs the skip and sends the bare command', () => { +test('attach --dangerous with a CLI that has no auto-flag warns without --verbose and sends the bare command', () => { const stubs = makeStubs({ which: ['tmux', 'pluk'] }); const { code, stderr } = runAttach( - ['agent-a', '--cli=aider', `--run-dir=${makeRunDir()}`, '--no-open', '--dangerous', '--verbose'], + ['agent-a', '--cli=nosuchcli', `--run-dir=${makeRunDir()}`, '--no-open', '--dangerous'], stubs, ); assert.equal(code, 0); - assert.match(stderr, /no dangerous\/auto flag known for cli=aider/); - assert.match(stubs.log('tmux'), /send-keys -t =agent-a: -- 'aider' Enter/); + assert.match(stderr, /Warning: --dangerous has no known auto-approve flag for cli=nosuchcli; starting without it/); + assert.match(stubs.log('tmux'), /send-keys -t =agent-a: -- 'nosuchcli' Enter/); +}); + +test('attach --dangerous passes --yolo to gemini and --yes-always to aider', () => { + for (const [cli, flag] of [['gemini', '--yolo'], ['aider', '--yes-always']]) { + const stubs = makeStubs({ which: ['tmux', 'pluk'] }); + const { code, stderr } = runAttach( + [`agent-${cli}`, `--cli=${cli}`, `--run-dir=${makeRunDir()}`, '--no-open', '--dangerous'], + stubs, + ); + assert.equal(code, 0); + assert.ok(!/no known auto-approve flag/.test(stderr), stderr); + assert.match(stubs.log('tmux'), new RegExp(`send-keys -t =agent-${cli}: -- '${cli}' '${flag}' Enter`)); + } }); // --- pluk binary resolution -------------------------------------------------------- diff --git a/test/cli-support-matrix.test.js b/test/cli-support-matrix.test.js index 3154192..6e681c2 100644 --- a/test/cli-support-matrix.test.js +++ b/test/cli-support-matrix.test.js @@ -130,3 +130,10 @@ test('README --dangerous row lists every DANGEROUS_FLAGS entry with its exact fl 'README --dangerous row should list exactly the CLIs DANGEROUS_FLAGS knows', ); }); + +test('every builtin and bundled CLI has a non-empty DANGEROUS_FLAGS entry', () => { + const flags = recordEntries(attachSource, 'DANGEROUS_FLAGS'); + for (const cli of new Set([...Object.keys(BUILTIN_PATTERNS), ...bundled])) { + assert.ok(flags[cli], `DANGEROUS_FLAGS has no flag for ${cli}; attach --dangerous would silently no-op (#360)`); + } +});