+ ) : (
+
+
+
+
+ Use a coding agent (Claude Code or Codex)
+
+ )}
{lockedModel ? (
void
+ onSubmit: (message: PromptInputMessage, mentions?: FileMention[], attachments?: StagedAttachment[], searchEnabled?: boolean, codeMode?: 'claude' | 'codex') => void
onStop?: () => void
isProcessing: boolean
isStopping?: boolean
diff --git a/apps/x/packages/core/src/agents/runtime.ts b/apps/x/packages/core/src/agents/runtime.ts
index 883e8a14..ec2a516f 100644
--- a/apps/x/packages/core/src/agents/runtime.ts
+++ b/apps/x/packages/core/src/agents/runtime.ts
@@ -386,9 +386,10 @@ export async function mapAgentTool(t: z.infer): Promise exec ""
+\`\`\`
+
+Where \`\` is either \`claude\` or \`codex\` — pick based on (in priority order): an explicit in-chat override → the chip setting (\`${codeMode}\`).
+
+If the user's message is clearly NOT a coding request (small talk, an unrelated question), answer directly without invoking the coding agent. Code mode signals readiness, not that every message must route through the agent.`;
+ }
let streamError: string | null = null;
for await (const event of streamLlm(
model,
@@ -1365,11 +1392,16 @@ Do not announce the work directory unless it's relevant. Just use it.`;
const underlyingTool = agent.tools![part.toolName];
if (underlyingTool.type === "builtin" && underlyingTool.name === "ask-human") {
loopLogger.log('emitting ask-human-request, toolCallId:', part.toolCallId);
+ const rawOptions = (part.arguments as { options?: unknown }).options;
+ const options = Array.isArray(rawOptions)
+ ? rawOptions.filter((o): o is string => typeof o === 'string' && o.trim().length > 0)
+ : undefined;
yield* processEvent({
runId,
type: "ask-human-request",
toolCallId: part.toolCallId,
query: part.arguments.question,
+ ...(options && options.length > 0 ? { options } : {}),
subflow: [],
});
}
diff --git a/apps/x/packages/core/src/application/assistant/instructions.ts b/apps/x/packages/core/src/application/assistant/instructions.ts
index b0d1bcbb..cafa00f9 100644
--- a/apps/x/packages/core/src/application/assistant/instructions.ts
+++ b/apps/x/packages/core/src/application/assistant/instructions.ts
@@ -80,7 +80,7 @@ ${thirdPartyBlock}**Meeting Prep:** When users ask you to prepare for a meeting,
**Document Collaboration:** When users ask you to work on a document, collaborate on writing, create a new document, edit/refine existing notes, or say things like "let's work on [X]", "help me write [X]", "create a doc for [X]", or "let's draft [X]", you MUST load the \`doc-collab\` skill first. **This applies even for small one-off edits** — the skill carries the canonical *terse-and-scannable* writing style for the knowledge base, and that style applies whether you're authoring a fresh note or fixing a single section. Load it before writing anything into a note.
-**Code with Agents:** When users ask you to write code, build a project, create a script, fix a bug, or do any software development task, load the \`code-with-agents\` skill first. It provides guidance for delegating coding work to Claude Code or Codex via acpx.
+**Code with Agents:** When users ask you to write code, build a project, create a script, fix a bug, or do any software development task — **including simple things like "create a .c file" or "write a hello-world in Python"** — your FIRST action MUST be \`loadSkill('code-with-agents')\`. Do NOT reach for \`executeCommand\` (PowerShell / bash / shell) or any workspace file tool to do code work yourself before loading this skill. The skill decides whether to delegate to Claude Code / Codex (via acpx) or hand control back to you, and it presents the user a one-click choice when needed. Paths outside the Rowboat workspace root (e.g. \`G:/...\`, \`~/projects/...\`) are NORMAL for coding tasks — do NOT raise "outside workspace" concerns or fall back to your own tools.
**App Control:** When users ask you to open notes, show the bases or graph view, filter or search notes, or manage saved views, load the \`app-navigation\` skill first. It provides structured guidance for navigating the app UI and controlling the knowledge base view.
diff --git a/apps/x/packages/core/src/application/assistant/skills/code-with-agents/skill.ts b/apps/x/packages/core/src/application/assistant/skills/code-with-agents/skill.ts
index c2879228..efe30e13 100644
--- a/apps/x/packages/core/src/application/assistant/skills/code-with-agents/skill.ts
+++ b/apps/x/packages/core/src/application/assistant/skills/code-with-agents/skill.ts
@@ -1,90 +1,105 @@
export const skill = String.raw`
# Code with Agents Skill
-Use this skill when the user asks you to write code, build a project, create scripts, fix bugs, or do any software development task that should be delegated to a coding agent (Claude Code or Codex).
+Use this skill whenever the user asks you to write code, build a project, create scripts, fix bugs, read/explain code, or do any software development task — even simple file creations like "make a .c file".
-## Important: delegate ALL coding work
+Coding agents operate on **arbitrary file paths** (including paths outside the Rowboat workspace root, like \`G:/4th sem/CN\` or \`~/projects/foo\`). Do NOT raise "outside workspace" concerns, and do NOT fall back to your own \`executeCommand\` (PowerShell / bash) or workspace file tools to do code work yourself.
-Once the user has chosen to use Claude Code or Codex, you MUST delegate ALL code-related tasks to the coding agent. This includes:
-- Writing, editing, or refactoring code
-- Reading, summarizing, or explaining code
-- Debugging and fixing bugs
-- Running tests or build commands
-- Exploring project structure
-- Any other task that involves interacting with a codebase
+---
-Do NOT attempt to do any of these yourself — no reading files, no running commands, no writing code. You are the coordinator; the coding agent does the work. Your job is to translate the user's request into a clear prompt and pass it to the agent.
+## STEP 1 — MANDATORY FIRST ACTION
-## Prerequisites
+Look in your **system context** for a section titled **"# Code Mode (Active)"**.
-The user must have one of the following installed on their machine:
-- **Claude Code** — https://claude.ai/code
-- **Codex** — https://codex.openai.com
+### Case A — "# Code Mode (Active)" IS present
-These are external tools that you cannot install for the user.
+Code mode is on and the user has selected an agent. Skip directly to Step 2. Do NOT call ask-human.
-## Workflow
+### Case B — "# Code Mode (Active)" is NOT present
-### Step 1: Gather requirements
+Your **very next tool call MUST be \`ask-human\`** with options. Do not write any explanation text first. Do not describe a plan. Do not check the workspace boundary. Just call:
-Before running anything, confirm the following with the user:
+\`\`\`
+ask-human({
+ question: "How should I handle this coding request?",
+ options: [
+ "Use code mode (Claude Code)",
+ "Use code mode (Codex)",
+ "Continue with default Rowboat"
+ ]
+})
+\`\`\`
-1. **Working directory** — Ask which folder the code should be written in, unless the user has already specified it. Example: "Which folder should I work in?"
-2. **Agent choice** — Ask whether to use **Claude Code** or **Codex**. Mention that the chosen agent must already be installed on their machine.
+This is non-negotiable. The user gets clickable buttons. Free-text "which agent?" questions are forbidden here.
-### Step 2: Confirm execution plan
+**Branch on the response:**
+- "Use code mode (Claude Code)" → proceed to Step 2 with agent = \`claude\`.
+- "Use code mode (Codex)" → proceed to Step 2 with agent = \`codex\`.
+- "Continue with default Rowboat" → ABANDON this skill. Handle the request yourself using your own tools (workspace file tools, \`executeCommand\` shell, etc.). The rest of this skill does not apply for this turn.
-Once you know the folder and agent, tell the user:
+---
-> I'll use [Claude Code / Codex] to [description of the task] in \`[folder]\`. Permission requests from the coding agent itself (file writes, command execution, etc.) will be automatically approved once it starts. Wait for the user's confirmation before you execute anything.
+## STEP 2 — Resolve workdir, confirm, execute
-### Step 3: Execute with acpx
+**Resolve the workdir** (in this priority order):
+1. A path the user named in their original message (e.g. \`G:/4th sem/CN\`).
+2. The path from a "# User Work Directory" block in your context.
+3. Ask once in plain text: "Which folder should I work in?"
-Use the \`executeCommand\` tool to run the coding agent via acpx. The command format is:
+**Confirm briefly** with the user (one short line):
-**For Claude Code:**
-` + "`" + `
-npx acpx@latest --approve-all --cwd claude exec ""
-` + "`" + `
+> I'll use [Claude Code / Codex] to [task description] in \`[folder]\`. Permission requests from the coding agent will be auto-approved. Reply "yes" to proceed.
-**For Codex:**
-` + "`" + `
-npx acpx@latest --approve-all --cwd codex exec ""
-` + "`" + `
+**Execute** with the chosen agent. Call \`executeCommand\` with this exact shape:
+
+\`\`\`
+npx acpx@latest --approve-all --cwd exec ""
+\`\`\`
+
+Where \`\` is \`claude\` or \`codex\`, picked by (in priority order):
+- An explicit in-chat override from the user this turn ("use codex", "switch to claude") — honor it.
+- The agent chosen in Step 1 / the "# Code Mode (Active)" block.
+
+Concrete examples:
+
+\`\`\`
+npx acpx@latest --approve-all --cwd ~/projects/myapp claude exec "fix the off-by-one bug in foo.ts"
+npx acpx@latest --approve-all --cwd "G:/4th sem/CN" codex exec "create a C program that divides two numbers with divide-by-zero handling"
+\`\`\`
### Critical: flag order
-The \`--approve-all\` and \`--cwd\` flags are global flags and MUST come before the agent name (\`claude\` or \`codex\`). This is the correct order:
+\`--approve-all\` and \`--cwd\` are GLOBAL flags and MUST appear BEFORE the agent name:
-` + "`" + `
-npx acpx@latest [global flags] exec ""
-` + "`" + `
+- ✓ Correct: \`npx acpx@latest --approve-all --cwd exec ""\`
+- ✗ Wrong: \`npx acpx@latest --approve-all exec "..."\` (will fail)
-**Correct:**
-` + "`" + `
-npx acpx@latest --approve-all --cwd ~/projects/myapp claude exec "fix the bug"
-` + "`" + `
+### Writing good prompts for the agent
-**Wrong (will fail):**
-` + "`" + `
-npx acpx@latest claude --approve-all exec "fix the bug"
-` + "`" + `
+- Be specific: file names, function signatures, expected behavior.
+- Mention constraints (language, framework, style).
+- Expand short user requests into clear, actionable prompts.
-### Writing good prompts
+---
-When constructing the prompt for the coding agent:
-- Be specific and detailed about what to build or fix
-- Include file names, function signatures, and expected behavior
-- Mention any constraints (language, framework, style)
-- If the user gave you a short request, expand it into a clear, actionable prompt for the agent
+## STEP 3 — Report results
-### Step 4: Report results
+After the command finishes:
+- Pass through the coding agent's summary as-is. Do not rewrite.
+- Refer to file paths as plain text. Do NOT use \`\`\`file:path\`\`\` reference blocks.
+- Only add your own explanation if the command failed (non-zero exit). If exit code is 5, permissions were denied (shouldn't happen with \`--approve-all\` — flag this).
-After the command finishes, look for the summary that the coding agent produced at the end of its output and pass that along to the user as-is. Do not rewrite or add to it. Only add your own explanation if the command failed or the exit code is non-zero.
+---
-Do NOT use file reference blocks (e.g. \`\`\`file:path/to/file\`\`\`) when mentioning code files — they may not open correctly. Just refer to file paths as plain text.
+## Once delegating: delegate fully
-- If the exit code is 5, it means permissions were denied — this should not happen with \`--approve-all\`, but if it does, let the user know
+After Step 2 fires, delegate ALL related coding tasks for this turn to the coding agent — writing, editing, reading, debugging, exploring structure, running tests. You are the coordinator; the agent does the work.
+
+## Prerequisites (informational)
+
+The user must have one of these installed locally — these are external tools you cannot install:
+- Claude Code — https://claude.ai/code
+- Codex — https://codex.openai.com
`;
export default skill;
diff --git a/apps/x/packages/core/src/application/lib/message-queue.ts b/apps/x/packages/core/src/application/lib/message-queue.ts
index b3d2affa..a513b3ac 100644
--- a/apps/x/packages/core/src/application/lib/message-queue.ts
+++ b/apps/x/packages/core/src/application/lib/message-queue.ts
@@ -8,17 +8,20 @@ export type MiddlePaneContext =
| { kind: 'note'; path: string; content: string }
| { kind: 'browser'; url: string; title: string };
+export type CodeMode = 'claude' | 'codex';
+
type EnqueuedMessage = {
messageId: string;
message: UserMessageContentType;
voiceInput?: boolean;
voiceOutput?: VoiceOutputMode;
searchEnabled?: boolean;
+ codeMode?: CodeMode;
middlePaneContext?: MiddlePaneContext;
};
export interface IMessageQueue {
- enqueue(runId: string, message: UserMessageContentType, voiceInput?: boolean, voiceOutput?: VoiceOutputMode, searchEnabled?: boolean, middlePaneContext?: MiddlePaneContext): Promise;
+ enqueue(runId: string, message: UserMessageContentType, voiceInput?: boolean, voiceOutput?: VoiceOutputMode, searchEnabled?: boolean, middlePaneContext?: MiddlePaneContext, codeMode?: CodeMode): Promise;
dequeue(runId: string): Promise;
}
@@ -34,7 +37,7 @@ export class InMemoryMessageQueue implements IMessageQueue {
this.idGenerator = idGenerator;
}
- async enqueue(runId: string, message: UserMessageContentType, voiceInput?: boolean, voiceOutput?: VoiceOutputMode, searchEnabled?: boolean, middlePaneContext?: MiddlePaneContext): Promise {
+ async enqueue(runId: string, message: UserMessageContentType, voiceInput?: boolean, voiceOutput?: VoiceOutputMode, searchEnabled?: boolean, middlePaneContext?: MiddlePaneContext, codeMode?: CodeMode): Promise {
if (!this.store[runId]) {
this.store[runId] = [];
}
@@ -45,6 +48,7 @@ export class InMemoryMessageQueue implements IMessageQueue {
voiceInput,
voiceOutput,
searchEnabled,
+ codeMode,
middlePaneContext,
});
return id;
diff --git a/apps/x/packages/core/src/runs/runs.ts b/apps/x/packages/core/src/runs/runs.ts
index e10bf575..c316cd3b 100644
--- a/apps/x/packages/core/src/runs/runs.ts
+++ b/apps/x/packages/core/src/runs/runs.ts
@@ -39,9 +39,9 @@ export async function createRun(opts: z.infer): Promise
return run;
}
-export async function createMessage(runId: string, message: UserMessageContentType, voiceInput?: boolean, voiceOutput?: VoiceOutputMode, searchEnabled?: boolean, middlePaneContext?: MiddlePaneContext): Promise {
+export async function createMessage(runId: string, message: UserMessageContentType, voiceInput?: boolean, voiceOutput?: VoiceOutputMode, searchEnabled?: boolean, middlePaneContext?: MiddlePaneContext, codeMode?: 'claude' | 'codex'): Promise {
const queue = container.resolve('messageQueue');
- const id = await queue.enqueue(runId, message, voiceInput, voiceOutput, searchEnabled, middlePaneContext);
+ const id = await queue.enqueue(runId, message, voiceInput, voiceOutput, searchEnabled, middlePaneContext, codeMode);
const runtime = container.resolve('agentRuntime');
runtime.trigger(runId);
return id;
diff --git a/apps/x/packages/shared/src/ipc.ts b/apps/x/packages/shared/src/ipc.ts
index 230d384c..ec0d2e4c 100644
--- a/apps/x/packages/shared/src/ipc.ts
+++ b/apps/x/packages/shared/src/ipc.ts
@@ -228,6 +228,7 @@ const ipcSchemas = {
voiceInput: z.boolean().optional(),
voiceOutput: z.enum(['summary', 'full']).optional(),
searchEnabled: z.boolean().optional(),
+ codeMode: z.enum(['claude', 'codex']).optional(),
middlePaneContext: z.discriminatedUnion('kind', [
z.object({
kind: z.literal('note'),
diff --git a/apps/x/packages/shared/src/runs.ts b/apps/x/packages/shared/src/runs.ts
index 5ea0d667..a977db0b 100644
--- a/apps/x/packages/shared/src/runs.ts
+++ b/apps/x/packages/shared/src/runs.ts
@@ -75,6 +75,7 @@ export const AskHumanRequestEvent = BaseRunEvent.extend({
type: z.literal("ask-human-request"),
toolCallId: z.string(),
query: z.string(),
+ options: z.array(z.string()).optional(),
});
export const AskHumanResponseEvent = BaseRunEvent.extend({