diff --git a/src/session-cli-builder.ts b/src/session-cli-builder.ts
index 95f9f0da..546ce557 100644
--- a/src/session-cli-builder.ts
+++ b/src/session-cli-builder.ts
@@ -21,6 +21,8 @@ function buildPermissionArgs(claudeMode: ClaudeMode, allowedTools?: string): str
switch (claudeMode) {
case 'dangerously-skip-permissions':
return ['--dangerously-skip-permissions'];
+ case 'auto':
+ return ['--permission-mode', 'auto'];
case 'allowedTools':
if (allowedTools) {
return ['--allowedTools', allowedTools];
diff --git a/src/session.ts b/src/session.ts
index 02ce3e1b..dd589f8d 100644
--- a/src/session.ts
+++ b/src/session.ts
@@ -1523,7 +1523,7 @@ export class Session extends EventEmitter {
// === Auto-accept workspace trust dialog ===
// Claude CLI 2.x shows "Yes, I trust this folder" prompt on first launch per directory.
- // Codeman sessions always use --dangerously-skip-permissions, so auto-accept.
+ // Codeman sessions run permission-skipping or classifier-guarded (auto) modes, so auto-accept.
if (!this._trustDialogAccepted && data.includes('trust this folder')) {
this._trustDialogAccepted = true;
console.log(`[Session] Auto-accepting workspace trust dialog for: ${this.id}`);
diff --git a/src/tmux-manager.ts b/src/tmux-manager.ts
index 13200fe8..8f8bd982 100644
--- a/src/tmux-manager.ts
+++ b/src/tmux-manager.ts
@@ -563,6 +563,8 @@ function buildClaudePermissionFlags(claudeMode?: ClaudeMode, allowedTools?: stri
switch (mode) {
case 'dangerously-skip-permissions':
return ' --dangerously-skip-permissions';
+ case 'auto':
+ return ' --permission-mode auto';
case 'allowedTools':
if (allowedTools) {
// Sanitize: allow tool names with patterns like Bash(git:*), space/comma-separated
@@ -674,7 +676,7 @@ function buildEffortSettingsFlag(effort?: EffortLevel): string {
return flag && value ? ` ${flag} '${value}'` : '';
}
-function buildSpawnCommand(options: {
+export function buildSpawnCommand(options: {
mode: SessionMode;
sessionId: string;
model?: string;
diff --git a/src/types/session.ts b/src/types/session.ts
index 0744beab..ad534587 100644
--- a/src/types/session.ts
+++ b/src/types/session.ts
@@ -35,10 +35,11 @@ export type SessionStatus = 'idle' | 'busy' | 'stopped' | 'error';
/**
* Claude CLI startup permission mode.
* - `'dangerously-skip-permissions'`: Bypass all permission prompts (default)
+ * - `'auto'`: Anthropic's classifier-guarded low-prompt mode (`--permission-mode auto`)
* - `'normal'`: Standard mode with permission prompts
* - `'allowedTools'`: Only allow specific tools (requires allowedTools list)
*/
-export type ClaudeMode = 'dangerously-skip-permissions' | 'normal' | 'allowedTools';
+export type ClaudeMode = 'dangerously-skip-permissions' | 'auto' | 'normal' | 'allowedTools';
/** Session mode: which CLI backend a session runs */
export type SessionMode = 'claude' | 'shell' | 'opencode' | 'codex' | 'gemini';
diff --git a/src/web/public/index.html b/src/web/public/index.html
index a70f360e..c3a6a7be 100644
--- a/src/web/public/index.html
+++ b/src/web/public/index.html
@@ -1449,6 +1449,7 @@
diff --git a/src/web/server.ts b/src/web/server.ts
index f3a2187b..649929eb 100644
--- a/src/web/server.ts
+++ b/src/web/server.ts
@@ -1514,7 +1514,12 @@ export class WebServer extends EventEmitter {
const claudeMode = settings.claudeMode as string | undefined;
const allowedTools = settings.allowedTools as string | undefined;
// Only return valid modes
- if (claudeMode === 'dangerously-skip-permissions' || claudeMode === 'normal' || claudeMode === 'allowedTools') {
+ if (
+ claudeMode === 'dangerously-skip-permissions' ||
+ claudeMode === 'auto' ||
+ claudeMode === 'normal' ||
+ claudeMode === 'allowedTools'
+ ) {
return { claudeMode, allowedTools };
}
return {};
diff --git a/test/claude-permission-mode.test.ts b/test/claude-permission-mode.test.ts
new file mode 100644
index 00000000..a10a1caa
--- /dev/null
+++ b/test/claude-permission-mode.test.ts
@@ -0,0 +1,83 @@
+/**
+ * @fileoverview Tests for Claude CLI startup permission modes, focused on the
+ * 'auto' mode (`--permission-mode auto`, Anthropic's recommended low-prompt mode)
+ * added alongside the default `--dangerously-skip-permissions`.
+ *
+ * Covers BOTH spawn paths, which build the permission flags independently:
+ * - session-cli-builder.buildInteractiveArgs (direct PTY, non-mux fallback)
+ * - tmux-manager.buildSpawnCommand (tmux pane command string)
+ * The default must stay 'dangerously-skip-permissions' when the setting is unset.
+ */
+
+import { describe, it, expect } from 'vitest';
+import { buildInteractiveArgs } from '../src/session-cli-builder.js';
+import { buildSpawnCommand } from '../src/tmux-manager.js';
+
+describe('buildInteractiveArgs permission modes (direct PTY path)', () => {
+ it('keeps --dangerously-skip-permissions as the skip-mode flag', () => {
+ const args = buildInteractiveArgs('sid-1', 'dangerously-skip-permissions');
+ expect(args).toContain('--dangerously-skip-permissions');
+ expect(args).not.toContain('--permission-mode');
+ });
+
+ it('auto mode emits --permission-mode auto and never the skip flag', () => {
+ const args = buildInteractiveArgs('sid-1', 'auto');
+ const idx = args.indexOf('--permission-mode');
+ expect(idx).toBeGreaterThanOrEqual(0);
+ expect(args[idx + 1]).toBe('auto');
+ expect(args).not.toContain('--dangerously-skip-permissions');
+ });
+
+ it('normal mode emits no permission flag at all', () => {
+ const args = buildInteractiveArgs('sid-1', 'normal');
+ expect(args).not.toContain('--dangerously-skip-permissions');
+ expect(args).not.toContain('--permission-mode');
+ });
+
+ it('allowedTools mode is unchanged by the auto addition', () => {
+ const args = buildInteractiveArgs('sid-1', 'allowedTools', undefined, 'Read,Grep');
+ expect(args).toEqual(expect.arrayContaining(['--allowedTools', 'Read,Grep']));
+ expect(args).not.toContain('--permission-mode');
+ });
+
+ it('auto mode composes with model and effort flags', () => {
+ const args = buildInteractiveArgs('sid-1', 'auto', 'opus', undefined, 'high');
+ expect(args).toEqual(expect.arrayContaining(['--permission-mode', 'auto', '--model', 'opus', '--effort', 'high']));
+ });
+});
+
+describe('buildSpawnCommand permission modes (tmux path)', () => {
+ it('unset claudeMode defaults to --dangerously-skip-permissions', () => {
+ const cmd = buildSpawnCommand({ mode: 'claude', sessionId: 'sid-1' });
+ expect(cmd).toContain('claude --dangerously-skip-permissions --session-id "sid-1"');
+ expect(cmd).not.toContain('--permission-mode');
+ });
+
+ it('auto mode emits --permission-mode auto and never the skip flag', () => {
+ const cmd = buildSpawnCommand({ mode: 'claude', sessionId: 'sid-1', claudeMode: 'auto' });
+ expect(cmd).toContain('claude --permission-mode auto --session-id "sid-1"');
+ expect(cmd).not.toContain('--dangerously-skip-permissions');
+ });
+
+ it('auto mode carries into BOTH legs of the resume fallback command', () => {
+ const cmd = buildSpawnCommand({
+ mode: 'claude',
+ sessionId: 'sid-1',
+ claudeMode: 'auto',
+ resumeSessionId: 'abc-123',
+ });
+ const [resumeLeg, fallbackLeg] = cmd.split('||');
+ expect(resumeLeg).toContain('--permission-mode auto');
+ expect(resumeLeg).toContain('--resume "abc-123"');
+ expect(fallbackLeg).toContain('--permission-mode auto');
+ expect(fallbackLeg).toContain('--session-id "sid-1"');
+ expect(cmd).not.toContain('--dangerously-skip-permissions');
+ });
+
+ it('normal mode emits no permission flag', () => {
+ const cmd = buildSpawnCommand({ mode: 'claude', sessionId: 'sid-1', claudeMode: 'normal' });
+ expect(cmd).toContain('claude --session-id "sid-1"');
+ expect(cmd).not.toContain('--permission-mode');
+ expect(cmd).not.toContain('--dangerously-skip-permissions');
+ });
+});