forge-delegate 1.0.4 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -116,6 +116,17 @@ inline; long ones are auto-backgrounded by Claude Code (past ~2 min) or run deta
116
116
 
117
117
  Full guide: [docs/DELEGATION.md](docs/DELEGATION.md)
118
118
 
119
+ ### The `delegate-work` skill
120
+
121
+ Setup also installs a Claude Code skill (`~/.claude/skills/delegate-work/`, or
122
+ `<project>/.claude/skills/` under `--scope project`). Skip it with `--no-skill`.
123
+
124
+ It exists because MCP server `instructions` are injected once at session start and
125
+ get buried as the context fills — by turn 30 they lose to whatever is in front of the
126
+ model. A skill description is re-matched against every user turn, so the delegation
127
+ triggers ("write tests for", "rename X everywhere", "fix the lint errors") stay live
128
+ for the whole session instead of decaying.
129
+
119
130
  ## Configuration
120
131
 
121
132
  Stored in `~/.forge-delegate/config.json`, read fresh on every call — change it anytime without
package/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createInterface } from "node:readline/promises";
3
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from "node:fs";
4
4
  import { join, dirname } from "node:path";
5
5
  import { homedir } from "node:os";
6
6
  import { fileURLToPath } from "node:url";
@@ -35,15 +35,33 @@ function launchArray() {
35
35
  return isPublished ? ["npx", "-y", "forge-delegate", "serve"] : [process.execPath, join(here, "cli.mjs"), "serve"];
36
36
  }
37
37
 
38
- function registerClaude(launch, scope) {
38
+ // The MCP server's `instructions` are injected once at session start and decay as the
39
+ // context fills. A skill is re-matched against every user turn, so the delegation
40
+ // triggers stay live for the whole session.
41
+ function installClaudeSkill(scope, projectDir) {
42
+ const src = join(here, "skills", "delegate-work", "SKILL.md");
43
+ if (!existsSync(src)) return;
44
+ const root = scope === "user" ? join(homedir(), ".claude") : join(projectDir, ".claude");
45
+ const dest = join(root, "skills", "delegate-work", "SKILL.md");
46
+ mkdirSync(dirname(dest), { recursive: true });
47
+ copyFileSync(src, dest);
48
+ console.log(` \u2713 delegate-work skill (${dest})`);
49
+ }
50
+
51
+ // `claude mcp` writes .mcp.json relative to its own cwd, so a project-scope install
52
+ // must run from projectDir — otherwise it lands in whatever directory setup was
53
+ // invoked from, and installClaudeSkill (which does honour projectDir) disagrees
54
+ // with it.
55
+ function registerClaude(launch, scope, projectDir) {
56
+ const opts = scope === "project" ? { cwd: projectDir } : {};
39
57
  try {
40
- execSync(`claude mcp remove --scope ${scope} forge-delegate`, { stdio: "ignore" });
58
+ execSync(`claude mcp remove --scope ${scope} forge-delegate`, { ...opts, stdio: "ignore" });
41
59
  } catch {}
42
60
  try {
43
- execSync(`claude mcp remove forge-delegate`, { stdio: "ignore" });
61
+ execSync(`claude mcp remove forge-delegate`, { ...opts, stdio: "ignore" });
44
62
  } catch {}
45
- execSync(`claude mcp add --scope ${scope} forge-delegate -- ${launch}`, { stdio: "inherit" });
46
- console.log(` ✓ Claude Code (scope=${scope})`);
63
+ execSync(`claude mcp add --scope ${scope} forge-delegate -- ${launch}`, { ...opts, stdio: "inherit" });
64
+ console.log(` ✓ Claude Code (scope=${scope}${scope === "project" ? `, ${projectDir}` : ""})`);
47
65
  }
48
66
 
49
67
  function codexConfigPath(scope, projectDir) {
@@ -125,7 +143,10 @@ async function setup() {
125
143
  // 2. Register with chosen hosts (claude / codex / opencode)
126
144
  for (const target of targets) {
127
145
  try {
128
- if (target === "claude") registerClaude(launch, scope);
146
+ if (target === "claude") {
147
+ registerClaude(launch, scope, projectDir);
148
+ if (!flags["no-skill"]) installClaudeSkill(scope, projectDir);
149
+ }
129
150
  else if (target === "codex") registerCodex(launchArr, scope, projectDir);
130
151
  else if (target === "opencode") registerOpencode(launchArr, scope, projectDir);
131
152
  else console.log(` ✗ unknown target '${target}' (use claude,codex,opencode)`);
@@ -169,7 +190,6 @@ function configCmd(argv) {
169
190
  if (flags.variant) patch.defaultVariant = flags.variant;
170
191
  if (flags.timeout) patch.timeoutMs = Number(flags.timeout) * 1000;
171
192
  if (flags.dir) patch.defaultDirectory = flags.dir;
172
- if (flags["default-dir"]) patch.defaultDirectory = flags["default-dir"];
173
193
  if (flags.autoApprove !== undefined) patch.autoApprove = flags.autoApprove === "true" || flags.autoApprove === "1" || flags.autoApprove === true;
174
194
  saveConfig(patch);
175
195
  console.log(`updated ${configPath()}`);
@@ -236,8 +256,9 @@ forge-delegate — delegate coding work from Claude Code/Codex/opencode to other
236
256
 
237
257
  Usage:
238
258
  forge-delegate setup [--model provider/model] [--targets claude,codex,opencode]
239
- [--scope user|project] [--project-dir <path>]
259
+ [--scope user|project] [--project-dir <path>] [--no-skill]
240
260
  Interactive setup; flags optional (run bare to be prompted)
261
+ --no-skill skips installing the delegate-work skill
241
262
  forge-delegate serve Start the MCP server (used internally by agents)
242
263
  forge-delegate config get Show current configuration
243
264
  forge-delegate config set --model <m> [--agent <a>] [--variant <v>]
package/install.sh CHANGED
@@ -12,6 +12,7 @@
12
12
  # --targets a,b,c hosts to register with: claude,codex,opencode (default all)
13
13
  # --scope user|project registration scope (default user)
14
14
  # --project-dir <path> project for --scope project (default current dir)
15
+ # --no-skill skip installing the delegate-work skill for Claude Code
15
16
  # --no-opencode skip auto-installing opencode if missing
16
17
  # --no-npx skip the npx route, force clone/checkout
17
18
  # --help
@@ -23,6 +24,7 @@ SCOPE="user"
23
24
  PROJECT_DIR=""
24
25
  NO_OPENCODE=0
25
26
  NO_NPX=0
27
+ NO_SKILL=0
26
28
 
27
29
  while [[ $# -gt 0 ]]; do
28
30
  case "$1" in
@@ -34,10 +36,11 @@ while [[ $# -gt 0 ]]; do
34
36
  --scope=*) SCOPE="${1#*=}"; shift ;;
35
37
  --project-dir) PROJECT_DIR="${2:-}"; shift 2 ;;
36
38
  --project-dir=*) PROJECT_DIR="${1#*=}"; shift ;;
39
+ --no-skill) NO_SKILL=1; shift ;;
37
40
  --no-opencode) NO_OPENCODE=1; shift ;;
38
41
  --no-npx) NO_NPX=1; shift ;;
39
42
  --help|-h)
40
- sed -n '3,18p' "$0"
43
+ sed -n '3,19p' "$0"
41
44
  exit 0 ;;
42
45
  *) echo "unknown option: $1"; exit 1 ;;
43
46
  esac
@@ -68,6 +71,7 @@ fi
68
71
  ARGS=(--targets "$TARGETS" --scope "$SCOPE")
69
72
  if [[ -n "$MODEL" ]]; then ARGS+=(--model "$MODEL"); fi
70
73
  if [[ "$SCOPE" == "project" ]]; then ARGS+=(--project-dir "$PROJECT_DIR"); fi
74
+ if [[ "$NO_SKILL" -eq 1 ]]; then ARGS+=(--no-skill); fi
71
75
 
72
76
  # 3. Preferred route: published npm package (works on Windows, always latest)
73
77
  if [[ "$NO_NPX" -eq 0 ]] && command -v npx >/dev/null 2>&1; then
package/lib.mjs CHANGED
@@ -1,10 +1,8 @@
1
- import { execFile, spawn } from "node:child_process";
2
- import { promisify } from "node:util";
1
+ import { spawn } from "node:child_process";
3
2
  import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
3
  import { join } from "node:path";
5
4
  import { homedir } from "node:os";
6
5
 
7
- const execFileAsync = promisify(execFile);
8
6
  export const OPENCODE_TIMEOUT = Number(process.env.FORGE_OPENCODE_TIMEOUT ?? 900_000);
9
7
  const CONFIG_DIR = join(homedir(), ".forge-delegate");
10
8
  const AUTH_PATH = join(homedir(), ".local/share/opencode/auth.json");
@@ -344,14 +342,3 @@ export async function runOne(model, prompt) {
344
342
  throw new Error(`unknown model '${model}' — see list_models`);
345
343
  }
346
344
 
347
- export async function gitDiff() { try {
348
- const { stdout } = await execFileAsync("git", ["diff", "HEAD"], {
349
- cwd: process.cwd(),
350
- maxBuffer: 5 * 1024 * 1024,
351
- });
352
- return stdout.trim();
353
- } catch {
354
- return null;
355
- }
356
- }
357
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-delegate",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "MCP server that relays coding work to other AI models (DeepSeek, Gemini, OpenAI, Ollama) so Claude Code saves tokens",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,6 +16,7 @@
16
16
  "server.mjs",
17
17
  "lib.mjs",
18
18
  "install.sh",
19
+ "skills/",
19
20
  "docs/",
20
21
  "README.md"
21
22
  ],
package/server.mjs CHANGED
@@ -4,13 +4,13 @@ import { z } from "zod";
4
4
  import { execFile, spawn } from "node:child_process";
5
5
  import { promisify } from "node:util";
6
6
  import { mkdirSync, writeFileSync, readFileSync, existsSync, openSync, readdirSync } from "node:fs";
7
- import { join } from "node:path";
7
+ import { join, dirname } from "node:path";
8
+ import { fileURLToPath } from "node:url";
8
9
  import { homedir } from "node:os";
9
10
  import {
10
11
  logUsage,
11
12
  opencodeRun,
12
13
  runOne,
13
- gitDiff,
14
14
  opencodeArgs,
15
15
  parseEvents,
16
16
  pickVariant,
@@ -24,25 +24,63 @@ import {
24
24
 
25
25
  const execFileAsync = promisify(execFile);
26
26
  const RUNS_DIR = join(homedir(), ".forge-delegate", "runs");
27
+ const here = dirname(fileURLToPath(import.meta.url));
28
+ const VERSION = JSON.parse(readFileSync(join(here, "package.json"), "utf8")).version;
27
29
 
28
- const INSTRUCTIONS = `forge-delegate gives you a crew of external AI models to delegate coding work to. Hand off the high-volume, low-risk work (boilerplate, tests, lint/type fixes, mechanical edits) to a cheap or free model and keep the expensive agent focused on architecture and judgment calls.
30
+ // The server auto-updates (npx re-resolves latest on each spawn) but the installed
31
+ // skill is a file on disk that only `setup` rewrites — so a published skill change
32
+ // goes unnoticed. Compare the two and say so, rather than running stale in silence.
33
+ function skillStatus() {
34
+ const src = join(here, "skills", "delegate-work", "SKILL.md");
35
+ if (!existsSync(src)) return null;
36
+ const dests = [
37
+ join(homedir(), ".claude", "skills", "delegate-work", "SKILL.md"),
38
+ join(process.cwd(), ".claude", "skills", "delegate-work", "SKILL.md"),
39
+ ];
40
+ const installed = dests.filter(existsSync);
41
+ if (!installed.length) return { state: "missing", detail: "not installed" };
42
+ const want = readFileSync(src, "utf8");
43
+ const stale = installed.filter((d) => readFileSync(d, "utf8") !== want);
44
+ return stale.length
45
+ ? { state: "stale", detail: `out of date: ${stale.join(", ")}` }
46
+ : { state: "ok", detail: "up to date" };
47
+ }
48
+
49
+ const SKILL_STATUS = skillStatus();
50
+ if (SKILL_STATUS && SKILL_STATUS.state !== "ok") {
51
+ // stderr only — stdout is the JSON-RPC channel.
52
+ process.stderr.write(
53
+ `forge-delegate: delegate-work skill ${SKILL_STATUS.detail}. Run: npx -y forge-delegate setup\n`
54
+ );
55
+ }
29
56
 
30
- COST POLICY — MANDATORY, NON-NEGOTIABLE:
31
- The user pays per delegated call on a budget plan. You MUST pick only cheap or free models for EVERY delegation (ask_model, delegate). NEVER pick top-tier, premium, flagship, or frontier models — not even "just this once" or for hard tasks. If a task seems too hard for a cheap model, split it into smaller delegated steps instead of upgrading. Run list_models and prefer free/cheap models (deepseek flash tiers, free opencode models, local ollama).
57
+ const INSTRUCTIONS = `forge-delegate hands coding work to cheap external models running as full agents. They read the project and write the code in their own loop, so the work never passes through your context. Spend your context on judgment; delegate the typing.
58
+
59
+ DELEGATE BY DEFAULT — do not hand-write these yourself:
60
+ - Tests for code that already exists.
61
+ - Mechanical refactors: renames, signature changes, import rewrites, API migrations.
62
+ - Lint, type, and build errors — paste the error output and delegate the fix.
63
+ - Boilerplate: CRUD endpoints, config and schema files, adapters, fixtures, scaffolding.
64
+ - Ports and translations between languages, frameworks, or file formats.
65
+ - The same edit repeated across 3 or more files.
66
+ - Reading a large file or directory to answer one narrow question — delegate the reading, keep your context clean.
67
+ - The survey half of an audit ("does this follow X standards", "is this secure", "where is the tech debt", "how does X work"). Delegate the gathering, keep the verdict: ask for a structured digest with a file path behind every claim, then judge it yourself. A claim with no path is not a finding.
68
+
69
+ KEEP FOR YOURSELF: architecture, debugging an unknown root cause, security-sensitive code, anything where being wrong is expensive, and the verdict on whatever comes back. Delegation does not transfer responsibility for the result.
32
70
 
33
71
  Routing:
34
- - Quick one-shot questions → ask_model (direct API, no file access).
35
- - Multi-file edits/refactors/tests → delegate (inline by default; Claude Code auto-backgrounds calls past 2 min).
36
- - Parallel or fire-and-forget → delegate with background:true (returns a job id instantly); poll check_delegation for the result.
37
- - Follow-ups on a delegated area: pass the returned session id as continue_session.
38
- - Omit model/directory to use the configured defaults (get_delegate_config); change them anytime with set_delegate_config — never reinstall.
39
- - Never paste large files into your own context for delegation — delegate and let the agent read files itself.
40
-
41
- The per-tool descriptions are the authoritative reference.
72
+ - One-shot question, no file access needed -> ask_model.
73
+ - Anything touching files -> delegate (inline; Claude Code auto-backgrounds calls past 2 minutes).
74
+ - Parallel fan-out or long jobs -> delegate with background:true, then poll check_delegation.
75
+ - Follow-up in the same area -> pass the previous session id as continue_session.
76
+ - Omit model/directory/agent: the configured defaults are already correct (get_delegate_config to see them, set_delegate_config to change them — never reinstall).
77
+ - Never paste file contents into a task. Name the paths; the agent reads them itself.
78
+
79
+ Cost: the configured default model is already a cheap or free tier — just use it. Do not override \`model\` upward to a premium or flagship tier; if a task looks too big for the default, split it into smaller delegations instead.
42
80
  `;
43
81
 
44
82
  const server = new McpServer(
45
- { name: "forge-delegate", version: "1.0.0" },
83
+ { name: "forge-delegate", version: VERSION },
46
84
  { instructions: INSTRUCTIONS }
47
85
  );
48
86
 
@@ -79,7 +117,22 @@ async function startBackgroundJob(model, task, opts = {}) {
79
117
  stdio: ["ignore", out, out],
80
118
  });
81
119
  child.unref();
82
- writeFileSync(join(RUNS_DIR, `${id}.json`), JSON.stringify({ id, pid: child.pid, model, directory: cwd, started: Date.now(), logPath }));
120
+ const started = Date.now();
121
+ const deadline = opts.timeoutMs ? started + opts.timeoutMs : null;
122
+ if (deadline) {
123
+ setTimeout(() => {
124
+ try {
125
+ process.kill(-child.pid, "SIGKILL");
126
+ } catch {}
127
+ try {
128
+ process.kill(child.pid, "SIGKILL");
129
+ } catch {}
130
+ }, opts.timeoutMs).unref();
131
+ }
132
+ writeFileSync(
133
+ join(RUNS_DIR, `${id}.json`),
134
+ JSON.stringify({ id, pid: child.pid, model, directory: cwd, started, deadline, logPath })
135
+ );
83
136
  return id;
84
137
  }
85
138
 
@@ -120,7 +173,7 @@ server.tool(
120
173
 
121
174
  server.tool(
122
175
  "ask_model",
123
- "Send a one-shot prompt to an external AI model and get its raw response. Fast direct API call; the model has NO file access and CANNOT take actions. For work requiring tools/files use delegate instead. model defaults to the configured defaultModel.",
176
+ "USE THIS INSTEAD OF ANSWERING FROM YOUR OWN REASONING when the question is self-contained and cheap to verify: a syntax or API detail, a regex, a short snippet, a second opinion on a design, a summary of text you already have. Sends a one-shot prompt to an external model and returns its raw response. Fast direct API call; the model has NO file access and CANNOT take actions — for anything touching files use delegate instead. Omit model to use the configured default (already a cheap tier).",
124
177
  {
125
178
  model: z.string().optional().describe("provider/model, e.g. deepseek/deepseek-v4-flash, ollama:llama3 (defaults to config defaultModel)"),
126
179
  prompt: z.string(),
@@ -137,7 +190,7 @@ server.tool(
137
190
 
138
191
  server.tool(
139
192
  "delegate",
140
- "Delegate a coding task to another AI model running as a full agent (via local opencode): it reads your project, explores, and writes concrete code in its own tool loop — invisible to the caller's context. Runs INLINE by default (the result is returned in your context when done; Claude Code auto-backgrounds calls running past 2 minutes). Pass background:true to run detached and return a job id instantly, then poll check_delegation for the result — use that for parallel fan-out or hosts that block on long calls. Pass continue_session (returned by a previous delegation) to keep the same agent session instead of re-exploring. Optionally attach specific files in inline mode. model/directory/agent/autoApprove default from config.",
193
+ "USE THIS INSTEAD OF WRITING THE CODE YOURSELF when the task is: tests for code that already exists; a mechanical refactor (rename, signature change, import rewrite, API migration); fixing lint/type/build errors; boilerplate or scaffolding (CRUD endpoints, config, schemas, adapters, fixtures); a port between languages or frameworks; the same edit repeated across 3 or more files; reading a large file/directory to answer one narrow question; or the survey half of an audit (delegate the gathering, keep the verdict — demand a file path behind every claim). Hands the task to another AI model running as a full agent via local opencode: it reads your project, explores, and writes concrete code in its own tool loop — none of which enters your context. Keep architecture, unknown-root-cause debugging, and security-sensitive code for yourself, and always review what comes back. Runs INLINE by default (result returned when done; Claude Code auto-backgrounds calls past 2 minutes). Pass background:true to run detached and get a job id instantly, then poll check_delegation — use that for parallel fan-out or hosts that block on long calls. Pass continue_session (returned by a previous delegation) to keep the same agent session instead of re-exploring. Never paste file contents into task: name the paths and let the agent read them. model/directory/agent/autoApprove default from config — omit them.",
141
194
  {
142
195
  model: z.string().optional().describe("provider/model format, e.g. opencode/mimo-v2.5-free (defaults to config defaultModel)"),
143
196
  task: z.string(),
@@ -159,6 +212,7 @@ server.tool(
159
212
  directory: r.directory,
160
213
  agent: r.agent,
161
214
  autoApprove: r.autoApprove,
215
+ timeoutMs: r.timeoutMs,
162
216
  });
163
217
  return `Started background job ${id}. Poll with check_delegation(id: "${id}").`;
164
218
  });
@@ -202,7 +256,9 @@ server.tool(
202
256
  const parsed = parseEvents(log);
203
257
  const tail = parsed.text || "(agent still thinking, no text output yet)";
204
258
  const footer = parsed.sessionId ? ` · session: ${parsed.sessionId}` : "";
205
- return `[${running ? "RUNNING" : "DONE"}] ${id} (${meta.model}, ${Math.round((Date.now() - meta.started) / 1000)}s)${footer}\n\n${tail.slice(-8000)}`;
259
+ let status = running ? "RUNNING" : "DONE";
260
+ if (meta.deadline && Date.now() > meta.deadline) status = "TIMED OUT";
261
+ return `[${status}] ${id} (${meta.model}, ${Math.round((Date.now() - meta.started) / 1000)}s)${footer}\n\n${tail.slice(-8000)}`;
206
262
  })
207
263
  );
208
264
 
@@ -215,7 +271,8 @@ server.tool(
215
271
  const { profiles, ...rest } = cfg;
216
272
  const lines = Object.entries(rest).map(([k, v]) => ` ${k}: ${v ?? "(unset)"}`);
217
273
  const prof = Object.entries(profiles).map(([k, v]) => ` ${k}: ${JSON.stringify(v)}`).join("\n") || " (none)";
218
- return { content: [{ type: "text", text: `config: ${configPath()}\n${lines.join("\n")}\nprofiles:\n${prof}` }] };
274
+ const skill = SKILL_STATUS ? `\nskill (delegate-work): ${SKILL_STATUS.detail}${SKILL_STATUS.state === "ok" ? "" : " — run: npx -y forge-delegate setup"}` : "";
275
+ return { content: [{ type: "text", text: `config: ${configPath()}\n${lines.join("\n")}\nprofiles:\n${prof}${skill}` }] };
219
276
  }
220
277
  );
221
278
 
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: delegate-work
3
+ description: Hand a coding task to a cheap external model via forge-delegate instead of writing it yourself. Load BEFORE starting work that is high-volume and low-judgment - writing tests for code that already exists, mechanical refactors (renames, signature changes, import rewrites, API migrations), fixing lint/type/build errors, boilerplate and scaffolding (CRUD endpoints, config files, schemas, adapters, fixtures), porting code between languages or frameworks, the same edit repeated across 3 or more files, or reading a large file or directory to answer one narrow question. ALSO load for audit and survey questions - "does my codebase follow X", "audit", "review the architecture", "is this idiomatic", "where is the tech debt", "is this secure", "explore the codebase", "how does X work", "find where" - where the reading is wide but the judgment is narrow: delegate the survey, keep the verdict. Triggers on - "write tests for", "add tests", "rename X to Y everywhere", "refactor", "migrate", "port this to", "fix the lint errors", "fix the type errors", "scaffold", "boilerplate", "do the same for the other files".
4
+ ---
5
+
6
+ # Delegating work to a cheap model
7
+
8
+ `forge-delegate` runs another AI model as a full agent over this project. It reads
9
+ files, explores, and writes code in its own tool loop. None of that passes through
10
+ your context — you get back a summary and a diff on disk.
11
+
12
+ The point is not to save the user money on your tokens. It is that your context is
13
+ the scarce resource in a long session, and mechanical work burns it fast.
14
+
15
+ ## Decide
16
+
17
+ Delegate when the task is **high-volume and low-judgment**: the shape of the correct
18
+ answer is already known, and the work is typing it out across files.
19
+
20
+ | Delegate | Keep |
21
+ |---|---|
22
+ | Tests for code that already exists | Deciding what behaviour to test |
23
+ | Rename / signature change / import rewrite across files | Designing the new interface |
24
+ | Fixing lint, type, or build errors | Debugging an unknown root cause |
25
+ | CRUD endpoints, config, schemas, fixtures, adapters | Architecture and data modelling |
26
+ | Porting between languages or frameworks | Security-sensitive code |
27
+ | The same edit repeated across 3+ files | The first instance, so the pattern is right |
28
+ | Reading a big file to answer one question | Anything where being wrong is expensive |
29
+
30
+ Delegating does not transfer responsibility. Review what comes back before you
31
+ report it as done.
32
+
33
+ ## Delegate the survey, keep the verdict
34
+
35
+ An audit looks like judgment work, so it reads as "keep" — but only the *verdict* is
36
+ judgment. The input gathering is mechanical, wide, and the single most expensive
37
+ thing you can do to your own context. Split them:
38
+
39
+ 1. **Delegate the survey.** Ask for a structured digest, not an opinion:
40
+ "Report which framework this repo uses. List every routing and data-fetching
41
+ entry point: path, 3-line summary. Paths only, no assessment."
42
+ 2. **Keep the verdict.** Judge the digest yourself. You are now reading a page
43
+ instead of a repo, and the expensive-if-wrong part never left your hands.
44
+
45
+ This covers "does this follow X standards", "is this secure", "where is the tech
46
+ debt", "how does X work", "explore the codebase" — broad read, narrow judgment.
47
+
48
+ **Demand paths as evidence.** A survey's worth depends on the cheap model reporting
49
+ *absence* honestly, and "there is no Next.js here" is exactly the answer a weak model
50
+ is least likely to give — it will produce a plausible audit of a framework you do not
51
+ use rather than contradict the question's premise. Require a file path for every
52
+ claim. A claim with no path attached is not a finding; spot-check two of them before
53
+ you trust the rest.
54
+
55
+ ## Do it
56
+
57
+ 1. Do the judgment part yourself first — decide the interface, write the first
58
+ instance, name the files.
59
+ 2. Call `delegate` with a task that names paths. **Never paste file contents into
60
+ the task**; the agent reads them itself. That is the whole point.
61
+ 3. Omit `model`, `directory`, and `agent` — the configured defaults are already a
62
+ cheap or free tier. Do not raise `model` to a premium tier; if the task is too
63
+ big for the default, split it into smaller delegations.
64
+ 4. Review the result. Run the tests or the build yourself.
65
+
66
+ ```
67
+ delegate(task: "Write unit tests for the exported functions in src/parser.ts.
68
+ Follow the style of tests/lexer.test.ts. Cover the error paths.")
69
+ ```
70
+
71
+ Parallel or long-running work: `background: true` returns a job id immediately,
72
+ then poll `check_delegation(id)`. Follow-up in the same area: pass the previous
73
+ `session: <id>` back as `continue_session` so the agent skips re-exploring.
74
+
75
+ For a question that needs no file access at all — a syntax detail, a regex, a
76
+ second opinion — use `ask_model` instead; it is a direct API call with no agent loop.
77
+
78
+ ## Don't
79
+
80
+ - Don't delegate a task you cannot describe precisely. Vague task, vague diff.
81
+ - Don't delegate and walk away. Unreviewed delegated code is worse than no code.
82
+ - Don't paste large files into the task text.
83
+ - Don't accept a survey finding with no file path behind it.
84
+ - Don't override `model` upward because a task "seems hard" — split it instead.