mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-11 16:22:40 +02:00
commit
4709e6eb89
13 changed files with 578 additions and 14 deletions
|
|
@ -176,6 +176,7 @@ export class AgentRuntime implements IAgentRuntime {
|
|||
modelConfigRepo: this.modelConfigRepo,
|
||||
signal,
|
||||
abortRegistry: this.abortRegistry,
|
||||
bus: this.bus,
|
||||
})) {
|
||||
eventCount++;
|
||||
if (event.type !== "llm-stream-event") {
|
||||
|
|
@ -868,6 +869,7 @@ export async function* streamAgent({
|
|||
modelConfigRepo,
|
||||
signal,
|
||||
abortRegistry,
|
||||
bus,
|
||||
}: {
|
||||
state: AgentState,
|
||||
idGenerator: IMonotonicallyIncreasingIdGenerator;
|
||||
|
|
@ -876,6 +878,7 @@ export async function* streamAgent({
|
|||
modelConfigRepo: IModelConfigRepo;
|
||||
signal: AbortSignal;
|
||||
abortRegistry: IAbortRegistry;
|
||||
bus: IBus;
|
||||
}): AsyncGenerator<z.infer<typeof RunEvent>, void, unknown> {
|
||||
const logger = new PrefixLogger(`run-${runId}-${state.agentName}`);
|
||||
|
||||
|
|
@ -985,11 +988,12 @@ export async function* streamAgent({
|
|||
state: subflowState,
|
||||
idGenerator,
|
||||
runId,
|
||||
messageQueue,
|
||||
modelConfigRepo,
|
||||
signal,
|
||||
abortRegistry,
|
||||
})) {
|
||||
messageQueue,
|
||||
modelConfigRepo,
|
||||
signal,
|
||||
abortRegistry,
|
||||
bus,
|
||||
})) {
|
||||
yield* processEvent({
|
||||
...event,
|
||||
subflow: [toolCallId, ...event.subflow],
|
||||
|
|
@ -998,9 +1002,15 @@ export async function* streamAgent({
|
|||
if (!subflowState.getPendingAskHumans().length && !subflowState.getPendingPermissions().length) {
|
||||
result = subflowState.finalResponse();
|
||||
}
|
||||
} else {
|
||||
result = await execTool(agent.tools![toolCall.toolName], toolCall.arguments, { runId, signal, abortRegistry });
|
||||
}
|
||||
} else {
|
||||
result = await execTool(agent.tools![toolCall.toolName], toolCall.arguments, {
|
||||
runId,
|
||||
toolCallId,
|
||||
signal,
|
||||
abortRegistry,
|
||||
publish: (event) => bus.publish(event),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
if ((error instanceof Error && error.name === "AbortError") || signal.aborted) {
|
||||
throw error;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ ${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 is required for any document creation or editing task. The skill provides structured guidance for creating, editing, and refining documents in the knowledge base.
|
||||
|
||||
**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.
|
||||
|
||||
**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.
|
||||
|
||||
**Tracks (Auto-Updating Note Blocks):** When users ask you to **track**, **monitor**, **watch**, or **keep an eye on** something in a note — or say things like "every morning tell me X", "show the current Y in this note", "pin live updates of Z here" — load the \`tracks\` skill first. Also load it when a user presses Cmd+K with a note open and requests auto-refreshing content at the cursor. Track blocks are YAML-fenced scheduled blocks whose output is rewritten on each run — useful for weather, news, prices, status pages, and personal dashboards.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
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).
|
||||
|
||||
## Important: delegate ALL coding work
|
||||
|
||||
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.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The user must have one of the following installed on their machine:
|
||||
- **Claude Code** — https://claude.ai/code
|
||||
- **Codex** — https://codex.openai.com
|
||||
|
||||
These are external tools that you cannot install for the user.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Gather requirements
|
||||
|
||||
Before running anything, confirm the following with the user:
|
||||
|
||||
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.
|
||||
|
||||
### Step 2: Confirm execution plan
|
||||
|
||||
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 3: Execute with acpx
|
||||
|
||||
Use the \`executeCommand\` tool to run the coding agent via acpx. The command format is:
|
||||
|
||||
**For Claude Code:**
|
||||
` + "`" + `
|
||||
npx acpx@latest --approve-all --cwd <folder> claude exec "<prompt>"
|
||||
` + "`" + `
|
||||
|
||||
**For Codex:**
|
||||
` + "`" + `
|
||||
npx acpx@latest --approve-all --cwd <folder> codex exec "<prompt>"
|
||||
` + "`" + `
|
||||
|
||||
### 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:
|
||||
|
||||
` + "`" + `
|
||||
npx acpx@latest [global flags] <agent> exec "<prompt>"
|
||||
` + "`" + `
|
||||
|
||||
**Correct:**
|
||||
` + "`" + `
|
||||
npx acpx@latest --approve-all --cwd ~/projects/myapp claude exec "fix the bug"
|
||||
` + "`" + `
|
||||
|
||||
**Wrong (will fail):**
|
||||
` + "`" + `
|
||||
npx acpx@latest claude --approve-all exec "fix the bug"
|
||||
` + "`" + `
|
||||
|
||||
### 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 4: Report results
|
||||
|
||||
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.
|
||||
|
||||
- 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
|
||||
`;
|
||||
|
||||
export default skill;
|
||||
|
|
@ -11,6 +11,7 @@ import createPresentationsSkill from "./create-presentations/skill.js";
|
|||
|
||||
import appNavigationSkill from "./app-navigation/skill.js";
|
||||
import browserControlSkill from "./browser-control/skill.js";
|
||||
import codeWithAgentsSkill from "./code-with-agents/skill.js";
|
||||
import composioIntegrationSkill from "./composio-integration/skill.js";
|
||||
import tracksSkill from "./tracks/skill.js";
|
||||
import notifyUserSkill from "./notify-user/skill.js";
|
||||
|
|
@ -94,6 +95,12 @@ const definitions: SkillDefinition[] = [
|
|||
summary: "Navigate the app UI - open notes, switch views, filter/search the knowledge base, and manage saved views.",
|
||||
content: appNavigationSkill,
|
||||
},
|
||||
{
|
||||
id: "code-with-agents",
|
||||
title: "Code with Agents",
|
||||
summary: "Write code, build projects, create scripts, or fix bugs by delegating to Claude Code or Codex via acpx.",
|
||||
content: codeWithAgentsSkill,
|
||||
},
|
||||
{
|
||||
id: "tracks",
|
||||
title: "Tracks",
|
||||
|
|
|
|||
|
|
@ -969,6 +969,16 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
|
|||
const { promise, process: proc } = executeCommandAbortable(command, {
|
||||
cwd: workingDir,
|
||||
signal: ctx.signal,
|
||||
onData: (chunk: string) => {
|
||||
ctx.publish({
|
||||
runId: ctx.runId,
|
||||
type: "tool-output-stream",
|
||||
toolCallId: ctx.toolCallId,
|
||||
toolName: "executeCommand",
|
||||
output: chunk,
|
||||
subflow: [],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// Register process with abort registry for force-kill
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ export function executeCommandAbortable(
|
|||
timeout?: number;
|
||||
maxBuffer?: number;
|
||||
signal?: AbortSignal;
|
||||
onData?: (chunk: string) => void;
|
||||
}
|
||||
): { promise: Promise<AbortableCommandResult>; process: ChildProcess } {
|
||||
// Check if already aborted before spawning
|
||||
|
|
@ -177,16 +178,20 @@ export function executeCommandAbortable(
|
|||
|
||||
// Collect output
|
||||
proc.stdout?.on('data', (chunk: Buffer) => {
|
||||
const text = chunk.toString();
|
||||
const maxBuffer = options?.maxBuffer || 1024 * 1024;
|
||||
if (stdout.length < maxBuffer) {
|
||||
stdout += chunk.toString();
|
||||
stdout += text;
|
||||
}
|
||||
options?.onData?.(text);
|
||||
});
|
||||
proc.stderr?.on('data', (chunk: Buffer) => {
|
||||
const text = chunk.toString();
|
||||
const maxBuffer = options?.maxBuffer || 1024 * 1024;
|
||||
if (stderr.length < maxBuffer) {
|
||||
stderr += chunk.toString();
|
||||
stderr += text;
|
||||
}
|
||||
options?.onData?.(text);
|
||||
});
|
||||
|
||||
// Abort handler
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { ToolAttachment } from "@x/shared/dist/agent.js";
|
||||
import { RunEvent } from "@x/shared/dist/runs.js";
|
||||
import { z } from "zod";
|
||||
import { BuiltinTools } from "./builtin-tools.js";
|
||||
import { executeTool } from "../../mcp/mcp.js";
|
||||
|
|
@ -9,8 +10,10 @@ import { IAbortRegistry } from "../../runs/abort-registry.js";
|
|||
*/
|
||||
export interface ToolContext {
|
||||
runId: string;
|
||||
toolCallId: string;
|
||||
signal: AbortSignal;
|
||||
abortRegistry: IAbortRegistry;
|
||||
publish: (event: z.infer<typeof RunEvent>) => Promise<void>;
|
||||
}
|
||||
|
||||
async function execMcpTool(agentTool: z.infer<typeof ToolAttachment> & { type: "mcp" }, input: Record<string, unknown>): Promise<unknown> {
|
||||
|
|
@ -34,4 +37,4 @@ export async function execTool(agentTool: z.infer<typeof ToolAttachment>, input:
|
|||
return builtinTool.execute(input, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue