Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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=<cmd>` | 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) |
Expand Down
1 change: 1 addition & 0 deletions changelog.d/fixed-360.md
Original file line number Diff line number Diff line change
@@ -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`.
4 changes: 3 additions & 1 deletion src/attach.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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] ?? '';
}
Expand Down Expand Up @@ -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}`);
Expand Down
21 changes: 17 additions & 4 deletions test/attach-orchestration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 --------------------------------------------------------
Expand Down
7 changes: 7 additions & 0 deletions test/cli-support-matrix.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)`);
}
});
Loading