diff --git a/README.md b/README.md
index 1545cac..31b2689 100644
--- a/README.md
+++ b/README.md
@@ -321,6 +321,26 @@ Enable automatic worktree creation for a project. When enabled, new `/opencode`
 - 🚀 **Create PR button** — easily create pull requests from worktree
 - ⚡ **Per-project setting** — enable/disable independently for each project
 
+### `/autocode` — Toggle Automatic Passthrough Mode
+
+Enable automatic passthrough mode for a project. When enabled, every new thread the bot creates (via `/work` or `/opencode`) will already have passthrough mode on, so plain messages are sent to OpenCode without needing to run `/code` first.
+
+```
+/autocode
+```
+
+**How it works:**
+
+1. Run `/autocode` in a channel bound to a project
+2. The setting toggles on/off for that project
+3. New threads in that project skip the manual `/code` step
+
+**Features:**
+
+- 📱 **Mobile-friendly** — one less command to type per thread
+- 🧵 **Thread-scoped** — each new thread starts with passthrough enabled; `/code` still toggles it within a thread
+- ⚡ **Per-project setting** — enable/disable independently for each project
+
 ### `/queue` — Manage Message Queue
 
 Control the automated job queue for the current thread.
diff --git a/src/__tests__/autocode.test.ts b/src/__tests__/autocode.test.ts
new file mode 100644
index 0000000..05bb45d
--- /dev/null
+++ b/src/__tests__/autocode.test.ts
@@ -0,0 +1,176 @@
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { ChannelType } from 'discord.js';
+
+vi.mock('../services/dataStore.js');
+vi.mock('../services/worktreeManager.js');
+
+import { autocode } from '../commands/autocode.js';
+import { work } from '../commands/work.js';
+import { getOrCreateThread } from '../utils/threadHelper.js';
+import * as dataStore from '../services/dataStore.js';
+import * as worktreeManager from '../services/worktreeManager.js';
+
+function makeInteraction(channelId: string, isThread = false, parentId?: string) {
+  return {
+    channelId,
+    channel: isThread
+      ? { isThread: () => true, parentId }
+      : { isThread: () => false },
+    reply: vi.fn().mockResolvedValue(undefined),
+  } as any;
+}
+
+describe('/autocode', () => {
+  beforeEach(() => {
+    vi.clearAllMocks();
+  });
+
+  it('replies with an error when no project is bound to the channel', async () => {
+    const interaction = makeInteraction('channel-1');
+    vi.mocked(dataStore.getChannelBinding).mockReturnValue(undefined);
+
+    await autocode.execute(interaction);
+
+    expect(interaction.reply).toHaveBeenCalledOnce();
+    const arg = interaction.reply.mock.calls[0][0];
+    expect(arg.content).toContain('No project set');
+    expect(dataStore.setProjectAutoPassthrough).not.toHaveBeenCalled();
+  });
+
+  it('flips false → true and reports enabled', async () => {
+    const interaction = makeInteraction('channel-1');
+    vi.mocked(dataStore.getChannelBinding).mockReturnValue('myproj');
+    vi.mocked(dataStore.getProjectAutoPassthrough).mockReturnValue(false);
+    vi.mocked(dataStore.setProjectAutoPassthrough).mockReturnValue(true);
+
+    await autocode.execute(interaction);
+
+    expect(dataStore.setProjectAutoPassthrough).toHaveBeenCalledWith('myproj', true);
+    const arg = interaction.reply.mock.calls[0][0];
+    expect(arg.content).toMatch(/enabled/);
+    expect(arg.content).toContain('myproj');
+  });
+
+  it('flips true → false and reports disabled', async () => {
+    const interaction = makeInteraction('channel-1');
+    vi.mocked(dataStore.getChannelBinding).mockReturnValue('myproj');
+    vi.mocked(dataStore.getProjectAutoPassthrough).mockReturnValue(true);
+    vi.mocked(dataStore.setProjectAutoPassthrough).mockReturnValue(true);
+
+    await autocode.execute(interaction);
+
+    expect(dataStore.setProjectAutoPassthrough).toHaveBeenCalledWith('myproj', false);
+    const arg = interaction.reply.mock.calls[0][0];
+    expect(arg.content).toMatch(/disabled/);
+  });
+
+  it('resolves to the parent channel binding when invoked from a thread', async () => {
+    const interaction = makeInteraction('thread-id', true, 'parent-channel');
+    vi.mocked(dataStore.getChannelBinding).mockImplementation((id: string) =>
+      id === 'parent-channel' ? 'myproj' : undefined
+    );
+    vi.mocked(dataStore.getProjectAutoPassthrough).mockReturnValue(false);
+    vi.mocked(dataStore.setProjectAutoPassthrough).mockReturnValue(true);
+
+    await autocode.execute(interaction);
+
+    expect(dataStore.getChannelBinding).toHaveBeenCalledWith('parent-channel');
+    expect(dataStore.setProjectAutoPassthrough).toHaveBeenCalledWith('myproj', true);
+  });
+
+  it('reports failure when setProjectAutoPassthrough returns false', async () => {
+    const interaction = makeInteraction('channel-1');
+    vi.mocked(dataStore.getChannelBinding).mockReturnValue('ghostproj');
+    vi.mocked(dataStore.getProjectAutoPassthrough).mockReturnValue(false);
+    vi.mocked(dataStore.setProjectAutoPassthrough).mockReturnValue(false);
+
+    await autocode.execute(interaction);
+
+    const arg = interaction.reply.mock.calls[0][0];
+    expect(arg.content).toContain('not found');
+  });
+});
+
+describe('auto-passthrough seeding via /work', () => {
+  beforeEach(() => {
+    vi.clearAllMocks();
+    vi.mocked(worktreeManager.sanitizeBranchName).mockImplementation((b: string) => b);
+    vi.mocked(worktreeManager.createWorktree).mockResolvedValue('/tmp/worktree');
+
+    vi.mocked(dataStore.getChannelProjectPath).mockReturnValue('/tmp/project');
+    vi.mocked(dataStore.getWorktreeMappingByBranch).mockReturnValue(undefined);
+    vi.mocked(dataStore.setWorktreeMapping).mockReturnValue(undefined);
+    vi.mocked(dataStore.getChannelBinding).mockReturnValue('myproj');
+  });
+
+  async function runWork() {
+    const threadsCreate = vi.fn().mockResolvedValue({
+      id: 'new-thread-1',
+      send: vi.fn().mockResolvedValue(undefined),
+    });
+
+    const interaction = {
+      channelId: 'channel-1',
+      user: { id: 'user-1' },
+      channel: {
+        isThread: () => false,
+        type: ChannelType.GuildText,
+        threads: { create: threadsCreate },
+      },
+      options: { getString: (n: string) => (n === 'branch' ? 'feat-x' : null) },
+      reply: vi.fn().mockResolvedValue(undefined),
+      deferReply: vi.fn().mockResolvedValue(undefined),
+      editReply: vi.fn().mockResolvedValue(undefined),
+    } as any;
+
+    await work.execute(interaction);
+  }
+
+  it('seeds passthrough on the new thread when project has autoPassthrough enabled', async () => {
+    vi.mocked(dataStore.getProjectAutoPassthrough).mockReturnValue(true);
+    await runWork();
+    expect(dataStore.setPassthroughMode).toHaveBeenCalledWith('new-thread-1', true, 'user-1');
+  });
+
+  it('does not seed passthrough when autoPassthrough is disabled', async () => {
+    vi.mocked(dataStore.getProjectAutoPassthrough).mockReturnValue(false);
+    await runWork();
+    expect(dataStore.setPassthroughMode).not.toHaveBeenCalled();
+  });
+});
+
+describe('auto-passthrough seeding via /opencode (threadHelper)', () => {
+  beforeEach(() => {
+    vi.clearAllMocks();
+  });
+
+  async function runHelper() {
+    const threadsCreate = vi.fn().mockResolvedValue({ id: 'opencode-thread-1' });
+    const interaction = {
+      channelId: 'channel-1',
+      user: { id: 'user-1' },
+      channel: { isThread: () => false, threads: { create: threadsCreate } },
+    } as any;
+    return await getOrCreateThread(interaction, 'do a thing');
+  }
+
+  it('seeds passthrough when bound project has autoPassthrough enabled', async () => {
+    vi.mocked(dataStore.getChannelBinding).mockReturnValue('myproj');
+    vi.mocked(dataStore.getProjectAutoPassthrough).mockReturnValue(true);
+    await runHelper();
+    expect(dataStore.setPassthroughMode).toHaveBeenCalledWith('opencode-thread-1', true, 'user-1');
+  });
+
+  it('does not seed when no project is bound', async () => {
+    vi.mocked(dataStore.getChannelBinding).mockReturnValue(undefined);
+    await runHelper();
+    expect(dataStore.setPassthroughMode).not.toHaveBeenCalled();
+  });
+
+  it('does not seed when autoPassthrough is disabled', async () => {
+    vi.mocked(dataStore.getChannelBinding).mockReturnValue('myproj');
+    vi.mocked(dataStore.getProjectAutoPassthrough).mockReturnValue(false);
+    await runHelper();
+    expect(dataStore.setPassthroughMode).not.toHaveBeenCalled();
+  });
+});
diff --git a/src/commands/autocode.ts b/src/commands/autocode.ts
new file mode 100644
index 0000000..c6d807e
--- /dev/null
+++ b/src/commands/autocode.ts
@@ -0,0 +1,54 @@
+import {
+  SlashCommandBuilder,
+  ChatInputCommandInteraction,
+  MessageFlags,
+  ThreadChannel
+} from 'discord.js';
+import * as dataStore from '../services/dataStore.js';
+import type { Command } from './index.js';
+
+function getParentChannelId(interaction: ChatInputCommandInteraction): string {
+  const channel = interaction.channel;
+  if (channel?.isThread()) {
+    return (channel as ThreadChannel).parentId ?? interaction.channelId;
+  }
+  return interaction.channelId;
+}
+
+export const autocode: Command = {
+  data: new SlashCommandBuilder()
+    .setName('autocode')
+    .setDescription('Toggle automatic passthrough mode for new threads in this channel\'s project') as SlashCommandBuilder,
+
+  async execute(interaction: ChatInputCommandInteraction) {
+    const channelId = getParentChannelId(interaction);
+    const projectAlias = dataStore.getChannelBinding(channelId);
+
+    if (!projectAlias) {
+      await interaction.reply({
+        content: '❌ No project set for this channel. Use `/use <alias>` to bind a project first.',
+        flags: MessageFlags.Ephemeral
+      });
+      return;
+    }
+
+    const currentState = dataStore.getProjectAutoPassthrough(projectAlias);
+    const newState = !currentState;
+
+    const success = dataStore.setProjectAutoPassthrough(projectAlias, newState);
+    if (!success) {
+      await interaction.reply({
+        content: `❌ Project "${projectAlias}" not found.`,
+        flags: MessageFlags.Ephemeral
+      });
+      return;
+    }
+
+    const emoji = newState ? '✅' : '❌';
+    const status = newState ? 'enabled' : 'disabled';
+    await interaction.reply({
+      content: `${emoji} Auto-passthrough **${status}** for project **${projectAlias}**.\n\nNew threads will ${newState ? 'automatically enable' : 'NOT automatically enable'} passthrough mode (no slash command needed).`,
+      flags: MessageFlags.Ephemeral
+    });
+  }
+};
diff --git a/src/commands/index.ts b/src/commands/index.ts
index 5745ab7..41dd88e 100644
--- a/src/commands/index.ts
+++ b/src/commands/index.ts
@@ -6,6 +6,7 @@ import { opencode } from './opencode.js';
 import { work } from './work.js';
 import { code } from './code.js';
 import { autowork } from './autowork.js';
+import { autocode } from './autocode.js';
 import { model } from './model.js';
 import { setports } from './setports.js';
 import { queue } from './queue.js';
@@ -28,6 +29,7 @@ commands.set(opencode.data.name, opencode);
 commands.set(work.data.name, work);
 commands.set(code.data.name, code);
 commands.set(autowork.data.name, autowork);
+commands.set(autocode.data.name, autocode);
 commands.set(model.data.name, model);
 commands.set(setports.data.name, setports as Command);
 commands.set(queue.data.name, queue);
diff --git a/src/commands/work.ts b/src/commands/work.ts
index a1fce3a..8ac1556 100644
--- a/src/commands/work.ts
+++ b/src/commands/work.ts
@@ -87,6 +87,11 @@ export const work: Command = {
         createdAt: Date.now()
       });
 
+      const alias = dataStore.getChannelBinding(i.channelId);
+      if (alias && dataStore.getProjectAutoPassthrough(alias)) {
+        dataStore.setPassthroughMode(thread.id, true, i.user.id);
+      }
+
       const embed = new EmbedBuilder()
         .setTitle(`🌳 Worktree: ${sanitizedBranch}`)
         .setDescription(description)
diff --git a/src/services/dataStore.ts b/src/services/dataStore.ts
index 6c0ecc8..f36bba2 100644
--- a/src/services/dataStore.ts
+++ b/src/services/dataStore.ts
@@ -244,6 +244,20 @@ export function getProjectAutoWorktree(alias: string): boolean {
   return project?.autoWorktree ?? false;
 }
 
+export function setProjectAutoPassthrough(alias: string, enabled: boolean): boolean {
+  const data = loadData();
+  const project = data.projects.find(p => p.alias === alias);
+  if (!project) return false;
+  project.autoPassthrough = enabled;
+  saveData(data);
+  return true;
+}
+
+export function getProjectAutoPassthrough(alias: string): boolean {
+  const project = getProject(alias);
+  return project?.autoPassthrough ?? false;
+}
+
 // Queue Management
 export function getQueue(threadId: string): QueuedMessage[] {
   const data = loadData();
diff --git a/src/types/index.ts b/src/types/index.ts
index 5160881..f6ae651 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -4,6 +4,7 @@ export interface ProjectConfig {
   alias: string;
   path: string;
   autoWorktree?: boolean;
+  autoPassthrough?: boolean;
 }
 
 export interface ChannelBinding {
diff --git a/src/utils/threadHelper.ts b/src/utils/threadHelper.ts
index eb17e18..e0704e6 100644
--- a/src/utils/threadHelper.ts
+++ b/src/utils/threadHelper.ts
@@ -1,15 +1,16 @@
 import { ChatInputCommandInteraction, ThreadAutoArchiveDuration, TextChannel, ThreadChannel } from 'discord.js';
+import * as dataStore from '../services/dataStore.js';
 
 export async function getOrCreateThread(
   interaction: ChatInputCommandInteraction,
   prompt: string
 ): Promise<ThreadChannel> {
   const channel = interaction.channel;
-  
+
   if (channel?.isThread()) {
     return channel;
   }
-  
+
   if (channel && 'threads' in channel) {
     const threadName = prompt.slice(0, 50) + (prompt.length > 50 ? '...' : '');
     const thread = await (channel as TextChannel).threads.create({
@@ -17,8 +18,14 @@ export async function getOrCreateThread(
       autoArchiveDuration: ThreadAutoArchiveDuration.OneDay,
       reason: 'OpenCode session'
     });
+
+    const alias = dataStore.getChannelBinding(interaction.channelId);
+    if (alias && dataStore.getProjectAutoPassthrough(alias)) {
+      dataStore.setPassthroughMode(thread.id, true, interaction.user.id);
+    }
+
     return thread;
   }
-  
+
   throw new Error('Cannot create thread in this channel.');
 }
