@rallycry/conveyor-agent 6.1.1 → 6.3.0
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/dist/{chunk-4NL6FASS.js → chunk-WBBX5AIX.js} +677 -206
- package/dist/chunk-WBBX5AIX.js.map +1 -0
- package/dist/cli.js +12 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +45 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-4NL6FASS.js.map +0 -1
|
@@ -158,6 +158,24 @@ function searchIncidents(socket, status, source) {
|
|
|
158
158
|
function getTaskIncidents(socket, taskId) {
|
|
159
159
|
return emitRpc(requireSocket(socket), "agentRunner:getTaskIncidents", { taskId });
|
|
160
160
|
}
|
|
161
|
+
function requestScaleUp(socket, tier, reason) {
|
|
162
|
+
return emitRpc(requireSocket(socket), "agentRunner:scaleUp", { tier, reason });
|
|
163
|
+
}
|
|
164
|
+
function addDependency(socket, dependsOnSlugOrId) {
|
|
165
|
+
return emitRpcVoid(requireSocket(socket), "agentRunner:addDependency", { dependsOnSlugOrId });
|
|
166
|
+
}
|
|
167
|
+
function removeDependency(socket, dependsOnSlugOrId) {
|
|
168
|
+
return emitRpcVoid(requireSocket(socket), "agentRunner:removeDependency", { dependsOnSlugOrId });
|
|
169
|
+
}
|
|
170
|
+
function getDependencies(socket) {
|
|
171
|
+
return emitRpc(requireSocket(socket), "agentRunner:getDependencies", {});
|
|
172
|
+
}
|
|
173
|
+
function createFollowUpTask(socket, data) {
|
|
174
|
+
return emitRpc(requireSocket(socket), "agentRunner:createFollowUpTask", data);
|
|
175
|
+
}
|
|
176
|
+
function queryGcpLogs(socket, params) {
|
|
177
|
+
return emitRpc(requireSocket(socket), "agentRunner:queryGcpLogs", params);
|
|
178
|
+
}
|
|
161
179
|
|
|
162
180
|
// src/connection/task-connection.ts
|
|
163
181
|
var ConveyorConnection = class _ConveyorConnection {
|
|
@@ -165,6 +183,7 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
165
183
|
config;
|
|
166
184
|
eventBuffer = [];
|
|
167
185
|
flushTimer = null;
|
|
186
|
+
lastEmittedStatus = null;
|
|
168
187
|
static EVENT_BATCH_MS = 500;
|
|
169
188
|
earlyMessages = [];
|
|
170
189
|
earlyStop = false;
|
|
@@ -252,6 +271,9 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
252
271
|
this.socket.io.on("reconnect", (attempt) => {
|
|
253
272
|
process.stderr.write(`[conveyor] Reconnected after ${attempt} attempt(s)
|
|
254
273
|
`);
|
|
274
|
+
if (this.socket && this.lastEmittedStatus) {
|
|
275
|
+
this.socket.emit("agentRunner:statusUpdate", { status: this.lastEmittedStatus });
|
|
276
|
+
}
|
|
255
277
|
});
|
|
256
278
|
this.socket.io.on("reconnect_error", (err) => {
|
|
257
279
|
process.stderr.write(`[conveyor] Reconnection error: ${err.message}
|
|
@@ -296,6 +318,7 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
296
318
|
}
|
|
297
319
|
updateStatus(status) {
|
|
298
320
|
if (!this.socket) throw new Error("Not connected");
|
|
321
|
+
this.lastEmittedStatus = status;
|
|
299
322
|
this.socket.emit("agentRunner:statusUpdate", { status });
|
|
300
323
|
}
|
|
301
324
|
postChatMessage(content) {
|
|
@@ -387,6 +410,7 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
387
410
|
}
|
|
388
411
|
emitStatus(status) {
|
|
389
412
|
if (!this.socket) return;
|
|
413
|
+
this.lastEmittedStatus = status;
|
|
390
414
|
this.socket.emit("agentRunner:statusUpdate", { status });
|
|
391
415
|
}
|
|
392
416
|
emitRateLimitPause(resetsAt) {
|
|
@@ -455,7 +479,7 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
455
479
|
return triggerIdentification(this.socket);
|
|
456
480
|
}
|
|
457
481
|
async refreshAuthToken() {
|
|
458
|
-
const codespaceName = process.env.CODESPACE_NAME;
|
|
482
|
+
const codespaceName = process.env.CODESPACE_NAME || process.env.CLAUDESPACE_NAME;
|
|
459
483
|
const apiUrl = process.env.CONVEYOR_API_URL ?? this.config.conveyorApiUrl;
|
|
460
484
|
if (!codespaceName || !apiUrl) return false;
|
|
461
485
|
try {
|
|
@@ -497,10 +521,32 @@ var ConveyorConnection = class _ConveyorConnection {
|
|
|
497
521
|
getTaskIncidents(taskId) {
|
|
498
522
|
return getTaskIncidents(this.socket, taskId);
|
|
499
523
|
}
|
|
524
|
+
requestScaleUp(tier, reason) {
|
|
525
|
+
return requestScaleUp(this.socket, tier, reason);
|
|
526
|
+
}
|
|
527
|
+
addDependency(dependsOnSlugOrId) {
|
|
528
|
+
return addDependency(this.socket, dependsOnSlugOrId);
|
|
529
|
+
}
|
|
530
|
+
removeDependency(dependsOnSlugOrId) {
|
|
531
|
+
return removeDependency(this.socket, dependsOnSlugOrId);
|
|
532
|
+
}
|
|
533
|
+
getDependencies() {
|
|
534
|
+
return getDependencies(this.socket);
|
|
535
|
+
}
|
|
536
|
+
createFollowUpTask(data) {
|
|
537
|
+
return createFollowUpTask(this.socket, data);
|
|
538
|
+
}
|
|
539
|
+
queryGcpLogs(params) {
|
|
540
|
+
return queryGcpLogs(this.socket, params);
|
|
541
|
+
}
|
|
500
542
|
disconnect() {
|
|
501
543
|
this.flushEvents();
|
|
502
|
-
this.socket
|
|
503
|
-
|
|
544
|
+
if (this.socket) {
|
|
545
|
+
this.socket.io.reconnection(false);
|
|
546
|
+
this.socket.removeAllListeners();
|
|
547
|
+
this.socket.disconnect();
|
|
548
|
+
this.socket = null;
|
|
549
|
+
}
|
|
504
550
|
}
|
|
505
551
|
};
|
|
506
552
|
|
|
@@ -617,6 +663,7 @@ var ProjectConnection = class {
|
|
|
617
663
|
this.socket.io.on("reconnect", (attempt) => {
|
|
618
664
|
process.stderr.write(`[conveyor] Project socket reconnected after ${attempt} attempt(s)
|
|
619
665
|
`);
|
|
666
|
+
this.socket?.emit("projectRunner:heartbeat", {});
|
|
620
667
|
});
|
|
621
668
|
this.socket.io.on("reconnect_error", (err) => {
|
|
622
669
|
process.stderr.write(`[conveyor] Project socket reconnection error: ${err.message}
|
|
@@ -792,8 +839,12 @@ var ProjectConnection = class {
|
|
|
792
839
|
}
|
|
793
840
|
}
|
|
794
841
|
disconnect() {
|
|
795
|
-
this.socket
|
|
796
|
-
|
|
842
|
+
if (this.socket) {
|
|
843
|
+
this.socket.io.reconnection(false);
|
|
844
|
+
this.socket.removeAllListeners();
|
|
845
|
+
this.socket.disconnect();
|
|
846
|
+
this.socket = null;
|
|
847
|
+
}
|
|
797
848
|
}
|
|
798
849
|
};
|
|
799
850
|
|
|
@@ -849,6 +900,82 @@ function getCurrentBranch(cwd) {
|
|
|
849
900
|
return null;
|
|
850
901
|
}
|
|
851
902
|
}
|
|
903
|
+
function hasUnpushedCommits(cwd) {
|
|
904
|
+
try {
|
|
905
|
+
const currentBranch = getCurrentBranch(cwd);
|
|
906
|
+
if (!currentBranch) return false;
|
|
907
|
+
try {
|
|
908
|
+
execSync2(`git rev-parse origin/${currentBranch}`, {
|
|
909
|
+
cwd,
|
|
910
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
911
|
+
});
|
|
912
|
+
} catch {
|
|
913
|
+
try {
|
|
914
|
+
execSync2("git rev-parse HEAD", {
|
|
915
|
+
cwd,
|
|
916
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
917
|
+
});
|
|
918
|
+
return true;
|
|
919
|
+
} catch {
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
const ahead = execSync2(`git rev-list --count HEAD --not origin/${currentBranch}`, {
|
|
924
|
+
cwd,
|
|
925
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
926
|
+
}).toString().trim();
|
|
927
|
+
return parseInt(ahead, 10) > 0;
|
|
928
|
+
} catch {
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
function stageAndCommit(cwd, message) {
|
|
933
|
+
try {
|
|
934
|
+
execSync2("git add -A", {
|
|
935
|
+
cwd,
|
|
936
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
937
|
+
});
|
|
938
|
+
if (!hasUncommittedChanges(cwd)) {
|
|
939
|
+
return null;
|
|
940
|
+
}
|
|
941
|
+
execSync2(`git commit -m "${message}"`, {
|
|
942
|
+
cwd,
|
|
943
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
944
|
+
});
|
|
945
|
+
const hash = execSync2("git rev-parse HEAD", {
|
|
946
|
+
cwd,
|
|
947
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
948
|
+
}).toString().trim();
|
|
949
|
+
return hash;
|
|
950
|
+
} catch {
|
|
951
|
+
return null;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
function pushToOrigin(cwd) {
|
|
955
|
+
try {
|
|
956
|
+
const currentBranch = getCurrentBranch(cwd);
|
|
957
|
+
if (!currentBranch) return false;
|
|
958
|
+
try {
|
|
959
|
+
execSync2(`git push origin ${currentBranch}`, {
|
|
960
|
+
cwd,
|
|
961
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
962
|
+
});
|
|
963
|
+
return true;
|
|
964
|
+
} catch {
|
|
965
|
+
try {
|
|
966
|
+
execSync2(`git push --force-with-lease origin ${currentBranch}`, {
|
|
967
|
+
cwd,
|
|
968
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
969
|
+
});
|
|
970
|
+
return true;
|
|
971
|
+
} catch {
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
} catch {
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
852
979
|
|
|
853
980
|
// src/runner/worktree.ts
|
|
854
981
|
var WORKTREE_DIR = ".worktrees";
|
|
@@ -957,6 +1084,51 @@ function unshallowRepo(workspaceDir) {
|
|
|
957
1084
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
958
1085
|
import { execSync as execSync5 } from "child_process";
|
|
959
1086
|
|
|
1087
|
+
// src/harness/types.ts
|
|
1088
|
+
function defineTool(name, description, schema, handler, options) {
|
|
1089
|
+
return {
|
|
1090
|
+
name,
|
|
1091
|
+
description,
|
|
1092
|
+
schema,
|
|
1093
|
+
handler,
|
|
1094
|
+
annotations: options?.annotations
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// src/harness/claude-code/index.ts
|
|
1099
|
+
import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
|
|
1100
|
+
var ClaudeCodeHarness = class {
|
|
1101
|
+
async *executeQuery(opts) {
|
|
1102
|
+
const sdkEvents = query({
|
|
1103
|
+
prompt: opts.prompt,
|
|
1104
|
+
options: {
|
|
1105
|
+
...opts.options,
|
|
1106
|
+
...opts.resume ? { resume: opts.resume } : {}
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
for await (const event of sdkEvents) {
|
|
1110
|
+
yield event;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
createMcpServer(config) {
|
|
1114
|
+
const sdkTools = config.tools.map(
|
|
1115
|
+
(t) => tool(
|
|
1116
|
+
t.name,
|
|
1117
|
+
t.description,
|
|
1118
|
+
t.schema,
|
|
1119
|
+
t.handler,
|
|
1120
|
+
t.annotations ? { annotations: t.annotations } : void 0
|
|
1121
|
+
)
|
|
1122
|
+
);
|
|
1123
|
+
return createSdkMcpServer({ name: config.name, tools: sdkTools });
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
// src/harness/index.ts
|
|
1128
|
+
function createHarness() {
|
|
1129
|
+
return new ClaudeCodeHarness();
|
|
1130
|
+
}
|
|
1131
|
+
|
|
960
1132
|
// src/connection/tunnel-client.ts
|
|
961
1133
|
import { request as httpRequest } from "http";
|
|
962
1134
|
var logger2 = createServiceLogger("TunnelClient");
|
|
@@ -1229,11 +1401,11 @@ async function processAssistantEvent(event, host, turnToolCalls) {
|
|
|
1229
1401
|
const { content } = event.message;
|
|
1230
1402
|
const turnTextParts = [];
|
|
1231
1403
|
for (const block of content) {
|
|
1232
|
-
if (block.type === "text") {
|
|
1404
|
+
if (block.type === "text" && block.text) {
|
|
1233
1405
|
turnTextParts.push(block.text);
|
|
1234
1406
|
host.connection.sendEvent({ type: "message", content: block.text });
|
|
1235
1407
|
await host.callbacks.onEvent({ type: "message", content: block.text });
|
|
1236
|
-
} else if (block.type === "tool_use") {
|
|
1408
|
+
} else if (block.type === "tool_use" && block.name) {
|
|
1237
1409
|
const inputStr = typeof block.input === "string" ? block.input : JSON.stringify(block.input);
|
|
1238
1410
|
const isContentTool = ["edit", "write"].includes(block.name.toLowerCase());
|
|
1239
1411
|
const inputLimit = isContentTool ? 1e4 : 500;
|
|
@@ -1322,7 +1494,9 @@ function handleSuccessResult(event, host, context, startTime, lastAssistantUsage
|
|
|
1322
1494
|
const cumulativeTotal = host.costTracker.addQueryCost(event.total_cost_usd);
|
|
1323
1495
|
const { modelUsage } = event;
|
|
1324
1496
|
if (modelUsage && typeof modelUsage === "object") {
|
|
1325
|
-
host.costTracker.addModelUsage(
|
|
1497
|
+
host.costTracker.addModelUsage(
|
|
1498
|
+
modelUsage
|
|
1499
|
+
);
|
|
1326
1500
|
}
|
|
1327
1501
|
host.connection.sendEvent({ type: "completed", summary, costUsd: cumulativeTotal, durationMs });
|
|
1328
1502
|
if (modelUsage && typeof modelUsage === "object") {
|
|
@@ -1603,11 +1777,6 @@ async function processEvents(events, context, host) {
|
|
|
1603
1777
|
};
|
|
1604
1778
|
}
|
|
1605
1779
|
|
|
1606
|
-
// src/execution/query-executor.ts
|
|
1607
|
-
import {
|
|
1608
|
-
query
|
|
1609
|
-
} from "@anthropic-ai/claude-agent-sdk";
|
|
1610
|
-
|
|
1611
1780
|
// src/execution/pack-runner-prompt.ts
|
|
1612
1781
|
function findLastAgentMessageIndex(history) {
|
|
1613
1782
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
@@ -1636,11 +1805,11 @@ function buildPackRunnerSystemPrompt(context, config, setupLog) {
|
|
|
1636
1805
|
const parts = [
|
|
1637
1806
|
`You are an autonomous Pack Runner managing child tasks for the "${context.title}" project.`,
|
|
1638
1807
|
`You are running locally with full access to the repository and task management tools.`,
|
|
1639
|
-
`Your job is to
|
|
1808
|
+
`Your job is to execute child tasks by firing cloud builds, reviewing their PRs, and merging them \u2014 respecting dependency chains for parallel execution.`,
|
|
1640
1809
|
``,
|
|
1641
1810
|
`## Child Task Status Lifecycle`,
|
|
1642
1811
|
`- "Planning" \u2014 Not ready for execution. Skip it (or escalate if blocking).`,
|
|
1643
|
-
`- "Open" \u2014 Ready to execute. Use start_child_cloud_build to fire it.`,
|
|
1812
|
+
`- "Open" \u2014 Ready to execute (if dependencies are met). Use start_child_cloud_build to fire it.`,
|
|
1644
1813
|
`- "InProgress" \u2014 Currently being worked on by a Task Runner. Wait \u2014 it will move to ReviewPR when done.`,
|
|
1645
1814
|
`- "ReviewPR" \u2014 Task Runner finished and opened a PR. Review and merge it.`,
|
|
1646
1815
|
`- "Hold" \u2014 PR exists but is on hold for team review. Do not merge \u2014 skip and move on.`,
|
|
@@ -1651,32 +1820,35 @@ function buildPackRunnerSystemPrompt(context, config, setupLog) {
|
|
|
1651
1820
|
`Follow this loop each time you are launched or relaunched:`,
|
|
1652
1821
|
``,
|
|
1653
1822
|
`1. Call list_subtasks to see the current state of all child tasks.`,
|
|
1654
|
-
` The response includes PR info
|
|
1823
|
+
` The response includes PR info, agent assignment, and **dependency info** (dependencies array + allDependenciesMet flag).`,
|
|
1655
1824
|
``,
|
|
1656
|
-
`2. Evaluate
|
|
1657
|
-
` - "ReviewPR": Review and merge its PR with approve_and_merge_pr
|
|
1658
|
-
` - If merge fails due to pending CI: post a status update to chat, state you are going idle
|
|
1659
|
-
` - If merge fails due to failed CI: use get_task_cli(childTaskId) to check
|
|
1660
|
-
` - "InProgress": A Task Runner is actively working
|
|
1661
|
-
` - "Open"
|
|
1662
|
-
`
|
|
1825
|
+
`2. Evaluate children by status and dependency readiness:`,
|
|
1826
|
+
` - "ReviewPR": Review and merge its PR with approve_and_merge_pr. (Highest priority)`,
|
|
1827
|
+
` - If merge fails due to pending CI: post a status update to chat, state you are going idle.`,
|
|
1828
|
+
` - If merge fails due to failed CI: use get_task_cli(childTaskId) to check. Escalate to team.`,
|
|
1829
|
+
` - "InProgress": A Task Runner is actively working. Do nothing \u2014 wait.`,
|
|
1830
|
+
` - "Open" + allDependenciesMet=true: Ready to fire. Use start_child_cloud_build.`,
|
|
1831
|
+
` - "Open" + allDependenciesMet=false: Blocked \u2014 skip for now. Will be unblocked when deps complete.`,
|
|
1663
1832
|
` - "Hold": On hold \u2014 team must review before merge. Skip.`,
|
|
1664
1833
|
` - "ReviewDev" / "Complete": Already done. Skip.`,
|
|
1665
|
-
` - "Planning": Not ready. If
|
|
1834
|
+
` - "Planning": Not ready. If blocking progress, notify team.`,
|
|
1666
1835
|
``,
|
|
1667
|
-
`3.
|
|
1836
|
+
`3. Fire ALL ready "Open" tasks whose dependencies are met, not just one. Independent tasks can run in parallel.`,
|
|
1668
1837
|
``,
|
|
1669
|
-
`4. After
|
|
1838
|
+
`4. After merging a PR: run \`git pull origin ${context.baseBranch}\` then re-check list_subtasks \u2014 previously blocked tasks may now be ready.`,
|
|
1670
1839
|
``,
|
|
1671
|
-
`5.
|
|
1840
|
+
`5. After firing all ready tasks: report which tasks you fired to chat, then state you are going idle.`,
|
|
1841
|
+
``,
|
|
1842
|
+
`6. When ALL children are in "ReviewDev", "Complete", or "Hold" (no "Open", "InProgress", or "ReviewPR" remaining): do a final review, summarize results in chat, and mark this parent task complete with force_update_task_status("Complete").`,
|
|
1672
1843
|
``,
|
|
1673
1844
|
`## Important Rules`,
|
|
1674
|
-
`-
|
|
1675
|
-
`-
|
|
1845
|
+
`- When dependencies are set on children, use them to determine execution order. Fire all ready tasks in parallel.`,
|
|
1846
|
+
`- When NO dependencies are set on any children, fall back to ordinal order (one at a time). This preserves backward compatibility.`,
|
|
1847
|
+
`- After firing builds OR when waiting on CI, explicitly state you are going idle. The system will disconnect you and relaunch when there's a status change.`,
|
|
1676
1848
|
`- Do NOT attempt to write code yourself. Your role is coordination only.`,
|
|
1677
1849
|
`- If a child is stuck in "InProgress" for an unusually long time, use get_task_cli(childTaskId) to check its logs and escalate to the team if it appears stuck.`,
|
|
1678
1850
|
`- You can use get_task(childTaskId) to get a child's full details including PR URL and branch.`,
|
|
1679
|
-
`- list_subtasks returns PR info (githubPRNumber, githubPRUrl)
|
|
1851
|
+
`- list_subtasks returns PR info (githubPRNumber, githubPRUrl), agent assignment (agentId), and dependency info for each child \u2014 use this to verify readiness before firing builds.`,
|
|
1680
1852
|
`- You can use read_task_chat to check for team messages.`
|
|
1681
1853
|
];
|
|
1682
1854
|
if (context.storyPoints && context.storyPoints.length > 0) {
|
|
@@ -1715,11 +1887,12 @@ function buildPackRunnerInstructions(context, scenario) {
|
|
|
1715
1887
|
);
|
|
1716
1888
|
} else if (scenario === "idle_relaunch") {
|
|
1717
1889
|
parts.push(
|
|
1718
|
-
`You have been relaunched \u2014 a child task likely changed status
|
|
1890
|
+
`You have been relaunched \u2014 a child task likely changed status.`,
|
|
1719
1891
|
`Call list_subtasks to check the current state of all children.`,
|
|
1720
1892
|
`Look for children in "ReviewPR" status first \u2014 review and merge their PRs.`,
|
|
1721
|
-
`
|
|
1722
|
-
`If
|
|
1893
|
+
`Check if any previously blocked tasks now have allDependenciesMet=true \u2014 fire them.`,
|
|
1894
|
+
`If a child you previously fired is now in "ReviewDev", pull latest with \`git pull origin ${context.baseBranch}\`.`,
|
|
1895
|
+
`If no children need action, state you are going idle.`
|
|
1723
1896
|
);
|
|
1724
1897
|
} else {
|
|
1725
1898
|
const lastAgentIdx = findLastAgentMessageIndex(context.chatHistory);
|
|
@@ -1977,7 +2150,7 @@ function buildDiscoveryPrompt(context) {
|
|
|
1977
2150
|
`### Finishing Planning`,
|
|
1978
2151
|
`Once your plan is complete and all required properties are set, call the **ExitPlanMode** tool.`,
|
|
1979
2152
|
`- Required before ExitPlanMode will succeed: **plan** (via update_task), **story points** (via update_task_properties), **title** (via update_task_properties)`,
|
|
1980
|
-
`- ExitPlanMode validates these properties and
|
|
2153
|
+
`- ExitPlanMode validates these properties and marks planning as complete`,
|
|
1981
2154
|
`- It does NOT start building \u2014 the team controls when to switch to Build mode`
|
|
1982
2155
|
];
|
|
1983
2156
|
if (context) parts.push(...buildPropertyInstructions(context));
|
|
@@ -2018,8 +2191,8 @@ function buildModePrompt(agentMode, context) {
|
|
|
2018
2191
|
switch (agentMode) {
|
|
2019
2192
|
case "discovery":
|
|
2020
2193
|
return buildDiscoveryPrompt(context);
|
|
2021
|
-
case "building":
|
|
2022
|
-
|
|
2194
|
+
case "building": {
|
|
2195
|
+
const parts = [
|
|
2023
2196
|
`
|
|
2024
2197
|
## Mode: Building`,
|
|
2025
2198
|
`You are in Building mode \u2014 executing the plan.`,
|
|
@@ -2027,7 +2200,22 @@ function buildModePrompt(agentMode, context) {
|
|
|
2027
2200
|
`- Safety rules: no destructive operations, use --force-with-lease instead of --force`,
|
|
2028
2201
|
`- If this is a leaf task (no children): execute the plan directly`,
|
|
2029
2202
|
`- Goal: implement the plan, run tests, open a PR when done`
|
|
2030
|
-
]
|
|
2203
|
+
];
|
|
2204
|
+
if (process.env.CLAUDESPACE_NAME) {
|
|
2205
|
+
parts.push(
|
|
2206
|
+
``,
|
|
2207
|
+
`### Resource Management`,
|
|
2208
|
+
`Your pod starts with minimal resources (0.25 CPU / 1 Gi). You MUST call \`scale_up_resources\``,
|
|
2209
|
+
`BEFORE running any of these operations \u2014 they WILL fail or OOM at baseline resources:`,
|
|
2210
|
+
`- **light** (1 CPU / 4 Gi) \u2014 bun/npm/yarn install, pip install, basic dev servers, light builds`,
|
|
2211
|
+
`- **standard** (2 CPU / 8 Gi) \u2014 full dev servers, test suites, typecheck, lint`,
|
|
2212
|
+
`- **heavy** (4 CPU / 16 Gi) \u2014 E2E/browser automation, large parallel builds`,
|
|
2213
|
+
`Scaling is one-way (up only) and capped by project limits.`,
|
|
2214
|
+
`CRITICAL: Always scale to at least "light" before running any package install command.`
|
|
2215
|
+
);
|
|
2216
|
+
}
|
|
2217
|
+
return parts.join("\n");
|
|
2218
|
+
}
|
|
2031
2219
|
case "review":
|
|
2032
2220
|
return [
|
|
2033
2221
|
`
|
|
@@ -2262,7 +2450,7 @@ Your responses are sent directly to the task chat \u2014 the team sees everythin
|
|
|
2262
2450
|
);
|
|
2263
2451
|
if (!isPm || isPmActive) {
|
|
2264
2452
|
parts.push(
|
|
2265
|
-
`Use the mcp__conveyor__create_pull_request tool to open PRs \u2014
|
|
2453
|
+
`Use the mcp__conveyor__create_pull_request tool to open PRs \u2014 it automatically stages, commits, and pushes changes before creating the PR. Do NOT use gh CLI or shell commands for PR creation.`
|
|
2266
2454
|
);
|
|
2267
2455
|
}
|
|
2268
2456
|
if (options?.hasDebugTools) {
|
|
@@ -2475,12 +2663,12 @@ function buildFreshInstructions(isPm, isAutoMode, context, agentMode) {
|
|
|
2475
2663
|
`Your plan has been approved. Begin implementing it now.`,
|
|
2476
2664
|
`Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`,
|
|
2477
2665
|
`Start by reading the relevant source files mentioned in the plan, then write code.`,
|
|
2478
|
-
`When finished,
|
|
2666
|
+
`When finished, use the mcp__conveyor__create_pull_request tool to open a PR. Do NOT use gh CLI.`,
|
|
2479
2667
|
`
|
|
2480
2668
|
CRITICAL: You are in Auto mode. Do NOT report status, ask for confirmation, or go idle without making code changes.`,
|
|
2481
2669
|
`Your FIRST action must be reading source files from the plan, then immediately writing code.`,
|
|
2482
2670
|
`Do NOT summarize the plan or say "ready to implement" \u2014 start implementing.`,
|
|
2483
|
-
`When all changes are
|
|
2671
|
+
`When all changes are ready, use mcp__conveyor__create_pull_request to open a PR.`,
|
|
2484
2672
|
`If you are genuinely blocked, explain the specific blocker \u2014 do not go idle silently.`
|
|
2485
2673
|
];
|
|
2486
2674
|
}
|
|
@@ -2514,14 +2702,14 @@ CRITICAL: You are in Auto mode. Do NOT report status, ask for confirmation, or g
|
|
|
2514
2702
|
`Your FIRST action should be reading the relevant source files mentioned in the plan, then writing code. Do NOT run install, build, lint, test, or dev server commands first \u2014 the environment is already set up.`,
|
|
2515
2703
|
`Work on the git branch "${context.githubBranch}". Stay on this branch for the entire task. Do not checkout or create other branches.`,
|
|
2516
2704
|
`Your replies are visible to the team in chat \u2014 briefly describe what you're doing when you begin meaningful implementation, and again when the PR is ready.`,
|
|
2517
|
-
`When finished,
|
|
2705
|
+
`When finished, use the mcp__conveyor__create_pull_request tool to open a PR. Do NOT use gh CLI or any other method to create PRs.`
|
|
2518
2706
|
];
|
|
2519
2707
|
if (isAutoMode) {
|
|
2520
2708
|
parts.push(
|
|
2521
2709
|
`
|
|
2522
2710
|
CRITICAL: You are in Auto mode. Do NOT report status, ask for confirmation, or go idle without making code changes.`,
|
|
2523
2711
|
`Do NOT summarize the plan or say "ready to implement" \u2014 start implementing immediately.`,
|
|
2524
|
-
`When all changes are
|
|
2712
|
+
`When all changes are ready, you MUST use mcp__conveyor__create_pull_request to open a PR before finishing.`,
|
|
2525
2713
|
`If you are genuinely blocked, explain the specific blocker \u2014 do not go idle silently.`
|
|
2526
2714
|
);
|
|
2527
2715
|
}
|
|
@@ -2550,7 +2738,7 @@ New messages since your last run:`,
|
|
|
2550
2738
|
...newMessages.map((m) => `[${m.userName ?? "user"}]: ${m.content}`),
|
|
2551
2739
|
`
|
|
2552
2740
|
Address the requested changes directly. Do NOT re-investigate the codebase from scratch or write a new plan \u2014 go straight to implementing the feedback.`,
|
|
2553
|
-
`
|
|
2741
|
+
`Implement your updates and open a PR when finished.`
|
|
2554
2742
|
];
|
|
2555
2743
|
if (context.githubPRUrl) {
|
|
2556
2744
|
parts.push(
|
|
@@ -2601,7 +2789,7 @@ function buildInstructions(mode, context, scenario, agentMode, isAuto) {
|
|
|
2601
2789
|
);
|
|
2602
2790
|
if (agentMode === "auto" || agentMode === "building" && isAuto) {
|
|
2603
2791
|
parts.push(
|
|
2604
|
-
`If work is incomplete, continue implementing the plan. When finished,
|
|
2792
|
+
`If work is incomplete, continue implementing the plan. When finished, use mcp__conveyor__create_pull_request to open a PR.`,
|
|
2605
2793
|
`Do NOT go idle or wait for instructions \u2014 you are in auto mode.`
|
|
2606
2794
|
);
|
|
2607
2795
|
} else {
|
|
@@ -2632,11 +2820,7 @@ async function buildInitialPrompt(mode, context, isAuto, agentMode) {
|
|
|
2632
2820
|
return [...body, ...instructions].join("\n");
|
|
2633
2821
|
}
|
|
2634
2822
|
|
|
2635
|
-
// src/tools/index.ts
|
|
2636
|
-
import { createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
|
|
2637
|
-
|
|
2638
2823
|
// src/tools/common-tools.ts
|
|
2639
|
-
import { tool } from "@anthropic-ai/claude-agent-sdk";
|
|
2640
2824
|
import { z } from "zod";
|
|
2641
2825
|
function isImageMimeType(mimeType) {
|
|
2642
2826
|
return mimeType.startsWith("image/");
|
|
@@ -2657,7 +2841,7 @@ function formatCliEvent(e) {
|
|
|
2657
2841
|
return formatter ? formatter(e) : JSON.stringify(e);
|
|
2658
2842
|
}
|
|
2659
2843
|
function buildReadTaskChatTool(connection) {
|
|
2660
|
-
return
|
|
2844
|
+
return defineTool(
|
|
2661
2845
|
"read_task_chat",
|
|
2662
2846
|
"Read recent messages from a task chat. Omit task_id to read the current task's chat, or provide a child task ID to read a child's chat.",
|
|
2663
2847
|
{
|
|
@@ -2680,7 +2864,7 @@ function buildReadTaskChatTool(connection) {
|
|
|
2680
2864
|
);
|
|
2681
2865
|
}
|
|
2682
2866
|
function buildPostToChatTool(connection) {
|
|
2683
|
-
return
|
|
2867
|
+
return defineTool(
|
|
2684
2868
|
"post_to_chat",
|
|
2685
2869
|
"Post a message to a task chat. Your normal replies already appear in chat \u2014 only use this for explicit out-of-band updates or posting to a child task's chat.",
|
|
2686
2870
|
{
|
|
@@ -2704,7 +2888,7 @@ function buildPostToChatTool(connection) {
|
|
|
2704
2888
|
);
|
|
2705
2889
|
}
|
|
2706
2890
|
function buildForceUpdateTaskStatusTool(connection) {
|
|
2707
|
-
return
|
|
2891
|
+
return defineTool(
|
|
2708
2892
|
"force_update_task_status",
|
|
2709
2893
|
"EMERGENCY ONLY: Force-override a task's Kanban status. Status transitions happen automatically (building sets InProgress, PR creation sets ReviewPR, merge sets ReviewDev). Only use this if an automatic transition failed or a task is stuck in the wrong status. Omit task_id to update the current task, or provide a child task ID.",
|
|
2710
2894
|
{
|
|
@@ -2728,7 +2912,7 @@ function buildForceUpdateTaskStatusTool(connection) {
|
|
|
2728
2912
|
);
|
|
2729
2913
|
}
|
|
2730
2914
|
function buildGetTaskPlanTool(connection, config) {
|
|
2731
|
-
return
|
|
2915
|
+
return defineTool(
|
|
2732
2916
|
"get_task_plan",
|
|
2733
2917
|
"Re-read the latest task plan in case it was updated",
|
|
2734
2918
|
{},
|
|
@@ -2744,7 +2928,7 @@ function buildGetTaskPlanTool(connection, config) {
|
|
|
2744
2928
|
);
|
|
2745
2929
|
}
|
|
2746
2930
|
function buildGetTaskTool(connection) {
|
|
2747
|
-
return
|
|
2931
|
+
return defineTool(
|
|
2748
2932
|
"get_task",
|
|
2749
2933
|
"Look up a task by slug or ID to get its title, description, plan, and status",
|
|
2750
2934
|
{
|
|
@@ -2764,7 +2948,7 @@ function buildGetTaskTool(connection) {
|
|
|
2764
2948
|
);
|
|
2765
2949
|
}
|
|
2766
2950
|
function buildGetTaskCliTool(connection) {
|
|
2767
|
-
return
|
|
2951
|
+
return defineTool(
|
|
2768
2952
|
"get_task_cli",
|
|
2769
2953
|
"Read CLI execution logs from a task. Returns agent reasoning, tool calls, setup output, and other execution events. Use 'source' to filter: 'agent' for agent reasoning/tool calls only, 'application' for setup/dev-server output only.",
|
|
2770
2954
|
{
|
|
@@ -2792,7 +2976,7 @@ function buildGetTaskCliTool(connection) {
|
|
|
2792
2976
|
);
|
|
2793
2977
|
}
|
|
2794
2978
|
function buildListTaskFilesTool(connection) {
|
|
2795
|
-
return
|
|
2979
|
+
return defineTool(
|
|
2796
2980
|
"list_task_files",
|
|
2797
2981
|
"List all files attached to this task with metadata (name, type, size) and download URLs",
|
|
2798
2982
|
{},
|
|
@@ -2817,7 +3001,7 @@ function buildListTaskFilesTool(connection) {
|
|
|
2817
3001
|
);
|
|
2818
3002
|
}
|
|
2819
3003
|
function buildGetTaskFileTool(connection) {
|
|
2820
|
-
return
|
|
3004
|
+
return defineTool(
|
|
2821
3005
|
"get_task_file",
|
|
2822
3006
|
"Get a specific task file's content and download URL by file ID",
|
|
2823
3007
|
{ fileId: z.string().describe("The file ID to retrieve") },
|
|
@@ -2844,7 +3028,7 @@ function buildGetTaskFileTool(connection) {
|
|
|
2844
3028
|
);
|
|
2845
3029
|
}
|
|
2846
3030
|
function buildSearchIncidentsTool(connection) {
|
|
2847
|
-
return
|
|
3031
|
+
return defineTool(
|
|
2848
3032
|
"search_incidents",
|
|
2849
3033
|
"Search incidents in the current project. Optionally filter by status (Open, Acknowledged, Investigating, Resolved, Closed) or source.",
|
|
2850
3034
|
{
|
|
@@ -2865,7 +3049,7 @@ function buildSearchIncidentsTool(connection) {
|
|
|
2865
3049
|
);
|
|
2866
3050
|
}
|
|
2867
3051
|
function buildGetTaskIncidentsTool(connection) {
|
|
2868
|
-
return
|
|
3052
|
+
return defineTool(
|
|
2869
3053
|
"get_task_incidents",
|
|
2870
3054
|
"Get all incidents linked to the current task (or a specified task). Returns full incident details including title, description, severity, status, and source.",
|
|
2871
3055
|
{
|
|
@@ -2884,20 +3068,56 @@ function buildGetTaskIncidentsTool(connection) {
|
|
|
2884
3068
|
{ annotations: { readOnlyHint: true } }
|
|
2885
3069
|
);
|
|
2886
3070
|
}
|
|
2887
|
-
function buildCreatePullRequestTool(connection) {
|
|
2888
|
-
return
|
|
3071
|
+
function buildCreatePullRequestTool(connection, config) {
|
|
3072
|
+
return defineTool(
|
|
2889
3073
|
"create_pull_request",
|
|
2890
|
-
"Create a GitHub pull request for this task. Use this instead of gh CLI or git commands to create PRs.",
|
|
3074
|
+
"Create a GitHub pull request for this task. Automatically stages uncommitted changes, commits them, and pushes before creating the PR. Use this instead of gh CLI or git commands to create PRs.",
|
|
2891
3075
|
{
|
|
2892
3076
|
title: z.string().describe("The PR title"),
|
|
2893
3077
|
body: z.string().describe("The PR description/body in markdown"),
|
|
2894
3078
|
branch: z.string().optional().describe(
|
|
2895
3079
|
"The head branch name for the PR. If the task doesn't have a branch set, this will be used. Defaults to the task's existing branch."
|
|
3080
|
+
),
|
|
3081
|
+
baseBranch: z.string().optional().describe(
|
|
3082
|
+
"The base branch to target for the PR (e.g. 'main', 'develop'). Defaults to the project's configured dev branch."
|
|
3083
|
+
),
|
|
3084
|
+
commitMessage: z.string().optional().describe(
|
|
3085
|
+
"Commit message for staging uncommitted changes. If not provided, a default message based on the PR title will be used."
|
|
2896
3086
|
)
|
|
2897
3087
|
},
|
|
2898
|
-
async ({ title, body, branch }) => {
|
|
3088
|
+
async ({ title, body, branch, baseBranch, commitMessage }) => {
|
|
2899
3089
|
try {
|
|
2900
|
-
const
|
|
3090
|
+
const cwd = config.workspaceDir;
|
|
3091
|
+
if (hasUncommittedChanges(cwd)) {
|
|
3092
|
+
const message = commitMessage || `${title}
|
|
3093
|
+
|
|
3094
|
+
Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>`;
|
|
3095
|
+
const commitHash = stageAndCommit(cwd, message);
|
|
3096
|
+
if (commitHash) {
|
|
3097
|
+
connection.sendEvent({
|
|
3098
|
+
type: "message",
|
|
3099
|
+
content: `Auto-committed changes: ${commitHash.slice(0, 7)}`
|
|
3100
|
+
});
|
|
3101
|
+
} else {
|
|
3102
|
+
return textResult(
|
|
3103
|
+
"Failed to stage and commit changes. Please check git status and commit manually before creating PR."
|
|
3104
|
+
);
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
if (hasUnpushedCommits(cwd)) {
|
|
3108
|
+
const pushSuccess = pushToOrigin(cwd);
|
|
3109
|
+
if (pushSuccess) {
|
|
3110
|
+
connection.sendEvent({
|
|
3111
|
+
type: "message",
|
|
3112
|
+
content: "Auto-pushed committed changes to origin"
|
|
3113
|
+
});
|
|
3114
|
+
} else {
|
|
3115
|
+
return textResult(
|
|
3116
|
+
"Failed to push changes to origin. Please check git status and push manually before creating PR."
|
|
3117
|
+
);
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
const result = await connection.createPR({ title, body, branch, baseBranch });
|
|
2901
3121
|
connection.sendEvent({
|
|
2902
3122
|
type: "pr_created",
|
|
2903
3123
|
url: result.url,
|
|
@@ -2911,8 +3131,174 @@ function buildCreatePullRequestTool(connection) {
|
|
|
2911
3131
|
}
|
|
2912
3132
|
);
|
|
2913
3133
|
}
|
|
3134
|
+
function buildQueryGcpLogsTool(connection) {
|
|
3135
|
+
return defineTool(
|
|
3136
|
+
"query_gcp_logs",
|
|
3137
|
+
"Query GCP Cloud Logging for the current project. Returns log entries matching the given filters. Use severity to filter by minimum log level. The project's GCP credentials are used automatically.",
|
|
3138
|
+
{
|
|
3139
|
+
filter: z.string().optional().describe(
|
|
3140
|
+
`Cloud Logging filter expression (e.g., 'resource.type="gce_instance"'). Max 1000 chars.`
|
|
3141
|
+
),
|
|
3142
|
+
start_time: z.string().optional().describe("Start time in ISO 8601 format (e.g., '2024-01-01T00:00:00Z')"),
|
|
3143
|
+
end_time: z.string().optional().describe("End time in ISO 8601 format (e.g., '2024-01-02T00:00:00Z')"),
|
|
3144
|
+
severity: z.enum([
|
|
3145
|
+
"DEFAULT",
|
|
3146
|
+
"DEBUG",
|
|
3147
|
+
"INFO",
|
|
3148
|
+
"NOTICE",
|
|
3149
|
+
"WARNING",
|
|
3150
|
+
"ERROR",
|
|
3151
|
+
"CRITICAL",
|
|
3152
|
+
"ALERT",
|
|
3153
|
+
"EMERGENCY"
|
|
3154
|
+
]).optional().describe("Minimum severity level to filter by (default: all levels)"),
|
|
3155
|
+
page_size: z.number().optional().describe("Number of log entries to return (default 100, max 500)")
|
|
3156
|
+
},
|
|
3157
|
+
async ({ filter, start_time, end_time, severity, page_size }) => {
|
|
3158
|
+
try {
|
|
3159
|
+
const result = await connection.queryGcpLogs({
|
|
3160
|
+
filter,
|
|
3161
|
+
startTime: start_time,
|
|
3162
|
+
endTime: end_time,
|
|
3163
|
+
severity,
|
|
3164
|
+
pageSize: page_size
|
|
3165
|
+
});
|
|
3166
|
+
if (result.entries.length === 0) {
|
|
3167
|
+
return textResult("No log entries found matching the given filters.");
|
|
3168
|
+
}
|
|
3169
|
+
const summary = `Found ${result.entries.length} log entries${result.hasMore ? " (more available \u2014 refine filters or increase page_size)" : ""}.`;
|
|
3170
|
+
const formatted = result.entries.map((e) => `[${e.timestamp}] [${e.severity}] ${e.message}`).join("\n");
|
|
3171
|
+
return textResult(`${summary}
|
|
3172
|
+
|
|
3173
|
+
${formatted}`);
|
|
3174
|
+
} catch (error) {
|
|
3175
|
+
return textResult(
|
|
3176
|
+
`Failed to query GCP logs: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3177
|
+
);
|
|
3178
|
+
}
|
|
3179
|
+
},
|
|
3180
|
+
{ annotations: { readOnlyHint: true } }
|
|
3181
|
+
);
|
|
3182
|
+
}
|
|
3183
|
+
function buildScaleUpResourcesTool(connection) {
|
|
3184
|
+
return defineTool(
|
|
3185
|
+
"scale_up_resources",
|
|
3186
|
+
"Scale up the pod's CPU and memory resources. Use before running dev servers, tests, builds, or other resource-intensive operations. Tiers: 'light' (1 CPU / 4 Gi \u2014 installs, basic dev servers), 'standard' (2 CPU / 8 Gi \u2014 full dev servers, test suites, typecheck), 'heavy' (4 CPU / 16 Gi \u2014 E2E tests, large parallel builds). Scaling is one-way (up only) and capped by project limits.",
|
|
3187
|
+
{
|
|
3188
|
+
tier: z.enum(["light", "standard", "heavy"]).describe("The resource tier to scale up to"),
|
|
3189
|
+
reason: z.string().optional().describe("Brief reason for scaling (e.g., 'running test suite')")
|
|
3190
|
+
},
|
|
3191
|
+
async ({ tier, reason }) => {
|
|
3192
|
+
try {
|
|
3193
|
+
const result = await connection.requestScaleUp(tier, reason);
|
|
3194
|
+
if (result.success) {
|
|
3195
|
+
if (result.currentTier === result.previousTier) {
|
|
3196
|
+
return textResult(
|
|
3197
|
+
`Already at ${result.currentTier} tier (${result.cpu} CPU / ${result.memory} Gi). No scaling needed.`
|
|
3198
|
+
);
|
|
3199
|
+
}
|
|
3200
|
+
return textResult(
|
|
3201
|
+
`Scaled to ${result.cpu} CPU / ${result.memory} Gi (${result.currentTier} tier, was ${result.previousTier}).`
|
|
3202
|
+
);
|
|
3203
|
+
}
|
|
3204
|
+
return textResult(
|
|
3205
|
+
`Scale-up not available: ${result.error ?? "unknown error"}. Continuing at current resources.`
|
|
3206
|
+
);
|
|
3207
|
+
} catch (error) {
|
|
3208
|
+
return textResult(
|
|
3209
|
+
`Scale-up failed: ${error instanceof Error ? error.message : "unknown error"}. Continuing at current resources.`
|
|
3210
|
+
);
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
);
|
|
3214
|
+
}
|
|
3215
|
+
function buildAddDependencyTool(connection) {
|
|
3216
|
+
return defineTool(
|
|
3217
|
+
"add_dependency",
|
|
3218
|
+
"Add a dependency \u2014 this task cannot start until the specified task is merged to dev",
|
|
3219
|
+
{
|
|
3220
|
+
depends_on_slug_or_id: z.string().describe("Slug or ID of the task this task depends on")
|
|
3221
|
+
},
|
|
3222
|
+
async ({ depends_on_slug_or_id }) => {
|
|
3223
|
+
try {
|
|
3224
|
+
await connection.addDependency(depends_on_slug_or_id);
|
|
3225
|
+
return textResult(`Dependency added: this task now depends on "${depends_on_slug_or_id}"`);
|
|
3226
|
+
} catch (error) {
|
|
3227
|
+
return textResult(
|
|
3228
|
+
`Failed to add dependency: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3229
|
+
);
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
);
|
|
3233
|
+
}
|
|
3234
|
+
function buildRemoveDependencyTool(connection) {
|
|
3235
|
+
return defineTool(
|
|
3236
|
+
"remove_dependency",
|
|
3237
|
+
"Remove a dependency from this task",
|
|
3238
|
+
{
|
|
3239
|
+
depends_on_slug_or_id: z.string().describe("Slug or ID of the task to remove as dependency")
|
|
3240
|
+
},
|
|
3241
|
+
async ({ depends_on_slug_or_id }) => {
|
|
3242
|
+
try {
|
|
3243
|
+
await connection.removeDependency(depends_on_slug_or_id);
|
|
3244
|
+
return textResult("Dependency removed");
|
|
3245
|
+
} catch (error) {
|
|
3246
|
+
return textResult(
|
|
3247
|
+
`Failed to remove dependency: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3248
|
+
);
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
);
|
|
3252
|
+
}
|
|
3253
|
+
function buildGetDependenciesTool(connection) {
|
|
3254
|
+
return defineTool(
|
|
3255
|
+
"get_dependencies",
|
|
3256
|
+
"Get this task's dependencies and their current status (met = merged to dev)",
|
|
3257
|
+
{},
|
|
3258
|
+
async () => {
|
|
3259
|
+
try {
|
|
3260
|
+
const deps = await connection.getDependencies();
|
|
3261
|
+
return textResult(JSON.stringify(deps, null, 2));
|
|
3262
|
+
} catch (error) {
|
|
3263
|
+
return textResult(
|
|
3264
|
+
`Failed to get dependencies: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3265
|
+
);
|
|
3266
|
+
}
|
|
3267
|
+
},
|
|
3268
|
+
{ annotations: { readOnlyHint: true } }
|
|
3269
|
+
);
|
|
3270
|
+
}
|
|
3271
|
+
function buildCreateFollowUpTaskTool(connection) {
|
|
3272
|
+
return defineTool(
|
|
3273
|
+
"create_follow_up_task",
|
|
3274
|
+
"Create a follow-up task in this project that depends on the current task. Use for out-of-scope work, v1.1 features, or cleanup that should happen after this task merges.",
|
|
3275
|
+
{
|
|
3276
|
+
title: z.string().describe("Follow-up task title"),
|
|
3277
|
+
description: z.string().optional().describe("Brief description of the follow-up work"),
|
|
3278
|
+
plan: z.string().optional().describe("Implementation plan if known"),
|
|
3279
|
+
story_point_value: z.number().optional().describe("Story point estimate (1=Common, 2=Magic, 3=Rare, 5=Unique)")
|
|
3280
|
+
},
|
|
3281
|
+
async ({ title, description, plan, story_point_value }) => {
|
|
3282
|
+
try {
|
|
3283
|
+
const result = await connection.createFollowUpTask({
|
|
3284
|
+
title,
|
|
3285
|
+
description,
|
|
3286
|
+
plan,
|
|
3287
|
+
storyPointValue: story_point_value
|
|
3288
|
+
});
|
|
3289
|
+
return textResult(
|
|
3290
|
+
`Follow-up task created: "${title}" (slug: ${result.slug}). It depends on the current task and will be unblocked when this task is merged.`
|
|
3291
|
+
);
|
|
3292
|
+
} catch (error) {
|
|
3293
|
+
return textResult(
|
|
3294
|
+
`Failed to create follow-up task: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3295
|
+
);
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
);
|
|
3299
|
+
}
|
|
2914
3300
|
function buildCommonTools(connection, config) {
|
|
2915
|
-
|
|
3301
|
+
const tools = [
|
|
2916
3302
|
buildReadTaskChatTool(connection),
|
|
2917
3303
|
buildPostToChatTool(connection),
|
|
2918
3304
|
buildGetTaskPlanTool(connection, config),
|
|
@@ -2922,12 +3308,20 @@ function buildCommonTools(connection, config) {
|
|
|
2922
3308
|
buildGetTaskFileTool(connection),
|
|
2923
3309
|
buildSearchIncidentsTool(connection),
|
|
2924
3310
|
buildGetTaskIncidentsTool(connection),
|
|
2925
|
-
|
|
3311
|
+
buildQueryGcpLogsTool(connection),
|
|
3312
|
+
buildCreatePullRequestTool(connection, config),
|
|
3313
|
+
buildAddDependencyTool(connection),
|
|
3314
|
+
buildRemoveDependencyTool(connection),
|
|
3315
|
+
buildGetDependenciesTool(connection),
|
|
3316
|
+
buildCreateFollowUpTaskTool(connection)
|
|
2926
3317
|
];
|
|
3318
|
+
if (process.env.CLAUDESPACE_NAME) {
|
|
3319
|
+
tools.push(buildScaleUpResourcesTool(connection));
|
|
3320
|
+
}
|
|
3321
|
+
return tools;
|
|
2927
3322
|
}
|
|
2928
3323
|
|
|
2929
3324
|
// src/tools/pm-tools.ts
|
|
2930
|
-
import { tool as tool2 } from "@anthropic-ai/claude-agent-sdk";
|
|
2931
3325
|
import { z as z2 } from "zod";
|
|
2932
3326
|
function buildStoryPointDescription(storyPoints) {
|
|
2933
3327
|
if (storyPoints && storyPoints.length > 0) {
|
|
@@ -2938,7 +3332,7 @@ function buildStoryPointDescription(storyPoints) {
|
|
|
2938
3332
|
}
|
|
2939
3333
|
function buildSubtaskTools(connection, spDescription) {
|
|
2940
3334
|
return [
|
|
2941
|
-
|
|
3335
|
+
defineTool(
|
|
2942
3336
|
"create_subtask",
|
|
2943
3337
|
"Create a subtask under the current parent task. Use for breaking complex tasks into smaller pieces.",
|
|
2944
3338
|
{
|
|
@@ -2959,7 +3353,7 @@ function buildSubtaskTools(connection, spDescription) {
|
|
|
2959
3353
|
}
|
|
2960
3354
|
}
|
|
2961
3355
|
),
|
|
2962
|
-
|
|
3356
|
+
defineTool(
|
|
2963
3357
|
"update_subtask",
|
|
2964
3358
|
"Update an existing subtask's fields",
|
|
2965
3359
|
{
|
|
@@ -2979,7 +3373,7 @@ function buildSubtaskTools(connection, spDescription) {
|
|
|
2979
3373
|
}
|
|
2980
3374
|
}
|
|
2981
3375
|
),
|
|
2982
|
-
|
|
3376
|
+
defineTool(
|
|
2983
3377
|
"delete_subtask",
|
|
2984
3378
|
"Delete a subtask",
|
|
2985
3379
|
{ subtaskId: z2.string().describe("The subtask ID to delete") },
|
|
@@ -2992,7 +3386,7 @@ function buildSubtaskTools(connection, spDescription) {
|
|
|
2992
3386
|
}
|
|
2993
3387
|
}
|
|
2994
3388
|
),
|
|
2995
|
-
|
|
3389
|
+
defineTool(
|
|
2996
3390
|
"list_subtasks",
|
|
2997
3391
|
"List all subtasks under the current parent task. Returns status, PR info (githubPRNumber, githubPRUrl), agent assignment (agentId), and plan for each child.",
|
|
2998
3392
|
{},
|
|
@@ -3009,7 +3403,7 @@ function buildSubtaskTools(connection, spDescription) {
|
|
|
3009
3403
|
];
|
|
3010
3404
|
}
|
|
3011
3405
|
function buildUpdateTaskTool(connection) {
|
|
3012
|
-
return
|
|
3406
|
+
return defineTool(
|
|
3013
3407
|
"update_task",
|
|
3014
3408
|
"Save the finalized task plan and/or description",
|
|
3015
3409
|
{
|
|
@@ -3034,7 +3428,7 @@ function buildPmTools(connection, storyPoints, options) {
|
|
|
3034
3428
|
}
|
|
3035
3429
|
function buildPackTools(connection) {
|
|
3036
3430
|
return [
|
|
3037
|
-
|
|
3431
|
+
defineTool(
|
|
3038
3432
|
"start_child_cloud_build",
|
|
3039
3433
|
"Start a cloud build for a child task. The child must be in Open status with story points and an agent assigned.",
|
|
3040
3434
|
{
|
|
@@ -3051,7 +3445,7 @@ function buildPackTools(connection) {
|
|
|
3051
3445
|
}
|
|
3052
3446
|
}
|
|
3053
3447
|
),
|
|
3054
|
-
|
|
3448
|
+
defineTool(
|
|
3055
3449
|
"stop_child_build",
|
|
3056
3450
|
"Stop a running cloud build for a child task. Sends a stop signal to the child agent.",
|
|
3057
3451
|
{
|
|
@@ -3068,7 +3462,7 @@ function buildPackTools(connection) {
|
|
|
3068
3462
|
}
|
|
3069
3463
|
}
|
|
3070
3464
|
),
|
|
3071
|
-
|
|
3465
|
+
defineTool(
|
|
3072
3466
|
"approve_and_merge_pr",
|
|
3073
3467
|
"Approve and merge a child task's pull request. Only succeeds if all CI/CD checks are passing. Returns an error if checks are pending (retry after waiting) or failed (investigate). The child task must be in ReviewPR status.",
|
|
3074
3468
|
{
|
|
@@ -3096,11 +3490,10 @@ function buildPackTools(connection) {
|
|
|
3096
3490
|
}
|
|
3097
3491
|
|
|
3098
3492
|
// src/tools/discovery-tools.ts
|
|
3099
|
-
import { tool as tool3 } from "@anthropic-ai/claude-agent-sdk";
|
|
3100
3493
|
import { z as z3 } from "zod";
|
|
3101
3494
|
function buildIconTools(connection) {
|
|
3102
3495
|
return [
|
|
3103
|
-
|
|
3496
|
+
defineTool(
|
|
3104
3497
|
"list_icons",
|
|
3105
3498
|
"List available icons (default library + user-created). Returns icon IDs, names, and whether they're defaults. Call this FIRST before update_task_properties to check for existing matches.",
|
|
3106
3499
|
{},
|
|
@@ -3116,7 +3509,7 @@ function buildIconTools(connection) {
|
|
|
3116
3509
|
},
|
|
3117
3510
|
{ annotations: { readOnlyHint: true } }
|
|
3118
3511
|
),
|
|
3119
|
-
|
|
3512
|
+
defineTool(
|
|
3120
3513
|
"generate_task_icon",
|
|
3121
3514
|
"Generate a new SVG icon using AI and assign it to this task. Only use if no existing icon from list_icons is a good fit. Provide a concise visual description.",
|
|
3122
3515
|
{
|
|
@@ -3141,7 +3534,7 @@ function buildIconTools(connection) {
|
|
|
3141
3534
|
function buildDiscoveryTools(connection, context) {
|
|
3142
3535
|
const spDescription = buildStoryPointDescription(context?.storyPoints);
|
|
3143
3536
|
return [
|
|
3144
|
-
|
|
3537
|
+
defineTool(
|
|
3145
3538
|
"update_task_properties",
|
|
3146
3539
|
"Set one or more task properties in a single call. All fields are optional \u2014 only include the fields you want to update.",
|
|
3147
3540
|
{
|
|
@@ -3177,11 +3570,9 @@ function buildDiscoveryTools(connection, context) {
|
|
|
3177
3570
|
}
|
|
3178
3571
|
|
|
3179
3572
|
// src/tools/debug-tools.ts
|
|
3180
|
-
import { tool as tool6 } from "@anthropic-ai/claude-agent-sdk";
|
|
3181
3573
|
import { z as z6 } from "zod";
|
|
3182
3574
|
|
|
3183
3575
|
// src/tools/telemetry-tools.ts
|
|
3184
|
-
import { tool as tool4 } from "@anthropic-ai/claude-agent-sdk";
|
|
3185
3576
|
import { z as z4 } from "zod";
|
|
3186
3577
|
|
|
3187
3578
|
// src/debug/telemetry-injector.ts
|
|
@@ -3573,7 +3964,7 @@ function formatError(error) {
|
|
|
3573
3964
|
return error instanceof Error ? error.message : "Unknown error";
|
|
3574
3965
|
}
|
|
3575
3966
|
function buildGetTelemetryTool(manager) {
|
|
3576
|
-
return
|
|
3967
|
+
return defineTool(
|
|
3577
3968
|
"debug_get_telemetry",
|
|
3578
3969
|
"Query structured telemetry events (HTTP requests, database queries, Socket.IO events, errors) captured from the running dev server. Returns filtered, structured data instead of raw logs.",
|
|
3579
3970
|
{
|
|
@@ -3609,7 +4000,7 @@ function buildGetTelemetryTool(manager) {
|
|
|
3609
4000
|
);
|
|
3610
4001
|
}
|
|
3611
4002
|
function buildClearTelemetryTool(manager) {
|
|
3612
|
-
return
|
|
4003
|
+
return defineTool(
|
|
3613
4004
|
"debug_clear_telemetry",
|
|
3614
4005
|
"Clear all captured telemetry events from the buffer. Useful to reset before reproducing a specific issue.",
|
|
3615
4006
|
{},
|
|
@@ -3626,7 +4017,7 @@ function buildClearTelemetryTool(manager) {
|
|
|
3626
4017
|
);
|
|
3627
4018
|
}
|
|
3628
4019
|
function buildTelemetryStatusTool(manager) {
|
|
3629
|
-
return
|
|
4020
|
+
return defineTool(
|
|
3630
4021
|
"debug_telemetry_status",
|
|
3631
4022
|
"Check if telemetry is active, how many events have been captured, and which framework patches (Express, Prisma, Socket.IO) were successfully applied.",
|
|
3632
4023
|
{},
|
|
@@ -3652,7 +4043,6 @@ function buildTelemetryTools(manager) {
|
|
|
3652
4043
|
}
|
|
3653
4044
|
|
|
3654
4045
|
// src/tools/client-debug-tools.ts
|
|
3655
|
-
import { tool as tool5 } from "@anthropic-ai/claude-agent-sdk";
|
|
3656
4046
|
import { z as z5 } from "zod";
|
|
3657
4047
|
function requirePlaywrightClient(manager) {
|
|
3658
4048
|
if (!manager.isClientDebugMode()) {
|
|
@@ -3669,7 +4059,7 @@ function formatError2(error) {
|
|
|
3669
4059
|
}
|
|
3670
4060
|
function buildClientBreakpointTools(manager) {
|
|
3671
4061
|
return [
|
|
3672
|
-
|
|
4062
|
+
defineTool(
|
|
3673
4063
|
"debug_set_client_breakpoint",
|
|
3674
4064
|
"Set a breakpoint in client-side code running in the headless Chromium browser. V8 resolves source maps automatically, so original .tsx/.ts file paths work. Use this for React components, client utilities, and browser-side code.",
|
|
3675
4065
|
{
|
|
@@ -3695,7 +4085,7 @@ Breakpoint ID: ${breakpointId}${sourceMapNote}`
|
|
|
3695
4085
|
}
|
|
3696
4086
|
}
|
|
3697
4087
|
),
|
|
3698
|
-
|
|
4088
|
+
defineTool(
|
|
3699
4089
|
"debug_remove_client_breakpoint",
|
|
3700
4090
|
"Remove a previously set client-side breakpoint by its ID.",
|
|
3701
4091
|
{
|
|
@@ -3712,7 +4102,7 @@ Breakpoint ID: ${breakpointId}${sourceMapNote}`
|
|
|
3712
4102
|
}
|
|
3713
4103
|
}
|
|
3714
4104
|
),
|
|
3715
|
-
|
|
4105
|
+
defineTool(
|
|
3716
4106
|
"debug_list_client_breakpoints",
|
|
3717
4107
|
"List all active client-side breakpoints with their file, line, and condition.",
|
|
3718
4108
|
{},
|
|
@@ -3732,7 +4122,7 @@ Breakpoint ID: ${breakpointId}${sourceMapNote}`
|
|
|
3732
4122
|
}
|
|
3733
4123
|
function buildClientInspectionTools(manager) {
|
|
3734
4124
|
return [
|
|
3735
|
-
|
|
4125
|
+
defineTool(
|
|
3736
4126
|
"debug_inspect_client_paused",
|
|
3737
4127
|
"When the client-side debugger is paused at a breakpoint, returns the call stack and local variables. Includes React component state, props, and hooks when paused inside a component.",
|
|
3738
4128
|
{},
|
|
@@ -3775,7 +4165,7 @@ ${JSON.stringify(queuedHits, null, 2)}`
|
|
|
3775
4165
|
},
|
|
3776
4166
|
{ annotations: { readOnlyHint: true } }
|
|
3777
4167
|
),
|
|
3778
|
-
|
|
4168
|
+
defineTool(
|
|
3779
4169
|
"debug_evaluate_client",
|
|
3780
4170
|
"Evaluate a JavaScript expression in the client-side browser context. When paused at a client breakpoint, evaluates in the paused scope. Can access DOM, window, React internals, etc.",
|
|
3781
4171
|
{
|
|
@@ -3803,7 +4193,7 @@ ${JSON.stringify(queuedHits, null, 2)}`
|
|
|
3803
4193
|
}
|
|
3804
4194
|
function buildClientExecutionTools(manager) {
|
|
3805
4195
|
return [
|
|
3806
|
-
|
|
4196
|
+
defineTool(
|
|
3807
4197
|
"debug_continue_client",
|
|
3808
4198
|
"Resume client-side execution after the browser debugger has paused at a breakpoint.",
|
|
3809
4199
|
{},
|
|
@@ -3825,7 +4215,7 @@ function buildClientExecutionTools(manager) {
|
|
|
3825
4215
|
}
|
|
3826
4216
|
function buildClientInteractionTools(manager) {
|
|
3827
4217
|
return [
|
|
3828
|
-
|
|
4218
|
+
defineTool(
|
|
3829
4219
|
"debug_client_screenshot",
|
|
3830
4220
|
"Take a screenshot of the current page state in the headless browser. Returns the image as base64-encoded PNG.",
|
|
3831
4221
|
{},
|
|
@@ -3849,7 +4239,7 @@ function buildClientInteractionTools(manager) {
|
|
|
3849
4239
|
},
|
|
3850
4240
|
{ annotations: { readOnlyHint: true } }
|
|
3851
4241
|
),
|
|
3852
|
-
|
|
4242
|
+
defineTool(
|
|
3853
4243
|
"debug_navigate_client",
|
|
3854
4244
|
"Navigate the headless browser to a specific URL. Use this to reproduce specific flows or visit different pages.",
|
|
3855
4245
|
{
|
|
@@ -3866,7 +4256,7 @@ function buildClientInteractionTools(manager) {
|
|
|
3866
4256
|
}
|
|
3867
4257
|
}
|
|
3868
4258
|
),
|
|
3869
|
-
|
|
4259
|
+
defineTool(
|
|
3870
4260
|
"debug_click_client",
|
|
3871
4261
|
"Click an element on the page in the headless browser. Use CSS selectors to target elements. Useful for reproducing bugs by interacting with the UI programmatically.",
|
|
3872
4262
|
{
|
|
@@ -3888,7 +4278,7 @@ function buildClientInteractionTools(manager) {
|
|
|
3888
4278
|
];
|
|
3889
4279
|
}
|
|
3890
4280
|
function buildClientConsoleTool(manager) {
|
|
3891
|
-
return
|
|
4281
|
+
return defineTool(
|
|
3892
4282
|
"debug_get_client_console",
|
|
3893
4283
|
"Get console messages captured from the headless browser. Includes console.log, warn, error, etc.",
|
|
3894
4284
|
{
|
|
@@ -3916,7 +4306,7 @@ ${formatted}`);
|
|
|
3916
4306
|
);
|
|
3917
4307
|
}
|
|
3918
4308
|
function buildClientNetworkTool(manager) {
|
|
3919
|
-
return
|
|
4309
|
+
return defineTool(
|
|
3920
4310
|
"debug_get_client_network",
|
|
3921
4311
|
"Get network requests captured from the headless browser. Shows URLs, methods, status codes, and timing.",
|
|
3922
4312
|
{
|
|
@@ -3945,7 +4335,7 @@ ${formatted}`);
|
|
|
3945
4335
|
);
|
|
3946
4336
|
}
|
|
3947
4337
|
function buildClientErrorsTool(manager) {
|
|
3948
|
-
return
|
|
4338
|
+
return defineTool(
|
|
3949
4339
|
"debug_get_client_errors",
|
|
3950
4340
|
"Get uncaught errors captured from the headless browser. Includes error messages and source-mapped stack traces.",
|
|
3951
4341
|
{
|
|
@@ -4039,7 +4429,7 @@ Set breakpoints to test your hypothesis.${sourceMapWarning}` : `Debug mode activ
|
|
|
4039
4429
|
}
|
|
4040
4430
|
function buildDebugLifecycleTools(manager) {
|
|
4041
4431
|
return [
|
|
4042
|
-
|
|
4432
|
+
defineTool(
|
|
4043
4433
|
"debug_enter_mode",
|
|
4044
4434
|
"Activate debug mode: restarts the dev server with Node.js --inspect flag and connects the CDP debugger. Optionally launch a headless Chromium browser for client-side debugging. Use serverSide for backend breakpoints, clientSide for frontend breakpoints, or both for full-stack.",
|
|
4045
4435
|
{
|
|
@@ -4060,7 +4450,7 @@ function buildDebugLifecycleTools(manager) {
|
|
|
4060
4450
|
}
|
|
4061
4451
|
}
|
|
4062
4452
|
),
|
|
4063
|
-
|
|
4453
|
+
defineTool(
|
|
4064
4454
|
"debug_exit_mode",
|
|
4065
4455
|
"Exit debug mode: removes all breakpoints, disconnects the debugger, and restarts the dev server normally.",
|
|
4066
4456
|
{},
|
|
@@ -4080,7 +4470,7 @@ function buildDebugLifecycleTools(manager) {
|
|
|
4080
4470
|
}
|
|
4081
4471
|
function buildBreakpointTools(manager) {
|
|
4082
4472
|
return [
|
|
4083
|
-
|
|
4473
|
+
defineTool(
|
|
4084
4474
|
"debug_set_breakpoint",
|
|
4085
4475
|
"Set a breakpoint at the specified file and line number. Optionally provide a condition expression that must evaluate to true for the breakpoint to pause execution.",
|
|
4086
4476
|
{
|
|
@@ -4104,7 +4494,7 @@ Breakpoint ID: ${breakpointId}`
|
|
|
4104
4494
|
}
|
|
4105
4495
|
}
|
|
4106
4496
|
),
|
|
4107
|
-
|
|
4497
|
+
defineTool(
|
|
4108
4498
|
"debug_remove_breakpoint",
|
|
4109
4499
|
"Remove a previously set breakpoint by its ID.",
|
|
4110
4500
|
{
|
|
@@ -4122,7 +4512,7 @@ Breakpoint ID: ${breakpointId}`
|
|
|
4122
4512
|
}
|
|
4123
4513
|
}
|
|
4124
4514
|
),
|
|
4125
|
-
|
|
4515
|
+
defineTool(
|
|
4126
4516
|
"debug_list_breakpoints",
|
|
4127
4517
|
"List all currently active breakpoints with their file, line, and condition.",
|
|
4128
4518
|
{},
|
|
@@ -4142,7 +4532,7 @@ Breakpoint ID: ${breakpointId}`
|
|
|
4142
4532
|
}
|
|
4143
4533
|
function buildInspectionTools(manager) {
|
|
4144
4534
|
return [
|
|
4145
|
-
|
|
4535
|
+
defineTool(
|
|
4146
4536
|
"debug_inspect_paused",
|
|
4147
4537
|
"When the debugger is paused at a breakpoint, returns the call stack and local variables. Check this after a breakpoint is hit to understand the current execution state.",
|
|
4148
4538
|
{},
|
|
@@ -4185,7 +4575,7 @@ ${JSON.stringify(queuedHits, null, 2)}`
|
|
|
4185
4575
|
},
|
|
4186
4576
|
{ annotations: { readOnlyHint: true } }
|
|
4187
4577
|
),
|
|
4188
|
-
|
|
4578
|
+
defineTool(
|
|
4189
4579
|
"debug_evaluate",
|
|
4190
4580
|
"Evaluate a JavaScript expression in the current paused scope (or globally if not paused). When paused, use frameIndex to evaluate in a specific call frame.",
|
|
4191
4581
|
{
|
|
@@ -4214,7 +4604,7 @@ ${JSON.stringify(queuedHits, null, 2)}`
|
|
|
4214
4604
|
}
|
|
4215
4605
|
function buildProbeManagementTools(manager) {
|
|
4216
4606
|
return [
|
|
4217
|
-
|
|
4607
|
+
defineTool(
|
|
4218
4608
|
"debug_add_probe",
|
|
4219
4609
|
"Add a debug probe at a specific code location. Captures expression values each time the line executes \u2014 without pausing or modifying source files. Like console.log but better: structured, no diff pollution, auto-cleaned on debug exit.",
|
|
4220
4610
|
{
|
|
@@ -4244,7 +4634,7 @@ Trigger the code path, then use debug_get_probe_results to see captured values.`
|
|
|
4244
4634
|
}
|
|
4245
4635
|
}
|
|
4246
4636
|
),
|
|
4247
|
-
|
|
4637
|
+
defineTool(
|
|
4248
4638
|
"debug_remove_probe",
|
|
4249
4639
|
"Remove a previously set debug probe by its ID.",
|
|
4250
4640
|
{
|
|
@@ -4261,7 +4651,7 @@ Trigger the code path, then use debug_get_probe_results to see captured values.`
|
|
|
4261
4651
|
}
|
|
4262
4652
|
}
|
|
4263
4653
|
),
|
|
4264
|
-
|
|
4654
|
+
defineTool(
|
|
4265
4655
|
"debug_list_probes",
|
|
4266
4656
|
"List all active debug probes with their file, line, expressions, and labels.",
|
|
4267
4657
|
{},
|
|
@@ -4284,7 +4674,7 @@ Trigger the code path, then use debug_get_probe_results to see captured values.`
|
|
|
4284
4674
|
}
|
|
4285
4675
|
function buildProbeResultTools(manager) {
|
|
4286
4676
|
return [
|
|
4287
|
-
|
|
4677
|
+
defineTool(
|
|
4288
4678
|
"debug_get_probe_results",
|
|
4289
4679
|
"Fetch captured probe hit data. Returns expression values from each time a probed line executed.",
|
|
4290
4680
|
{
|
|
@@ -4359,7 +4749,7 @@ function formatProbeValue(value) {
|
|
|
4359
4749
|
}
|
|
4360
4750
|
function buildExecutionControlTools(manager) {
|
|
4361
4751
|
return [
|
|
4362
|
-
|
|
4752
|
+
defineTool(
|
|
4363
4753
|
"debug_continue",
|
|
4364
4754
|
"Resume execution after the debugger has paused at a breakpoint.",
|
|
4365
4755
|
{},
|
|
@@ -4377,7 +4767,7 @@ function buildExecutionControlTools(manager) {
|
|
|
4377
4767
|
}
|
|
4378
4768
|
}
|
|
4379
4769
|
),
|
|
4380
|
-
|
|
4770
|
+
defineTool(
|
|
4381
4771
|
"debug_step_over",
|
|
4382
4772
|
"Step over the current line while paused at a breakpoint. Executes the current line and pauses at the next line in the same function.",
|
|
4383
4773
|
{},
|
|
@@ -4395,7 +4785,7 @@ function buildExecutionControlTools(manager) {
|
|
|
4395
4785
|
}
|
|
4396
4786
|
}
|
|
4397
4787
|
),
|
|
4398
|
-
|
|
4788
|
+
defineTool(
|
|
4399
4789
|
"debug_step_into",
|
|
4400
4790
|
"Step into the function call on the current line while paused at a breakpoint. Pauses at the first line inside the called function.",
|
|
4401
4791
|
{},
|
|
@@ -4429,11 +4819,10 @@ function buildDebugTools(manager) {
|
|
|
4429
4819
|
}
|
|
4430
4820
|
|
|
4431
4821
|
// src/tools/code-review-tools.ts
|
|
4432
|
-
import { tool as tool7 } from "@anthropic-ai/claude-agent-sdk";
|
|
4433
4822
|
import { z as z7 } from "zod";
|
|
4434
4823
|
function buildCodeReviewTools(connection) {
|
|
4435
4824
|
return [
|
|
4436
|
-
|
|
4825
|
+
defineTool(
|
|
4437
4826
|
"approve_code_review",
|
|
4438
4827
|
"Approve the code review. Use this when the code passes all review criteria and is ready to merge.",
|
|
4439
4828
|
{
|
|
@@ -4455,7 +4844,7 @@ ${summary}`,
|
|
|
4455
4844
|
return textResult("Code review approved. Exiting.");
|
|
4456
4845
|
}
|
|
4457
4846
|
),
|
|
4458
|
-
|
|
4847
|
+
defineTool(
|
|
4459
4848
|
"request_code_changes",
|
|
4460
4849
|
"Request changes during code review. Use this when substantive issues are found that need to be fixed before merge.",
|
|
4461
4850
|
{
|
|
@@ -4524,30 +4913,30 @@ function getModeTools(agentMode, connection, config, context) {
|
|
|
4524
4913
|
return config.mode === "pm" ? buildPmTools(connection, context?.storyPoints, { includePackTools: false }) : [];
|
|
4525
4914
|
}
|
|
4526
4915
|
}
|
|
4527
|
-
function
|
|
4916
|
+
function buildConveyorTools(connection, config, context, agentMode, debugManager) {
|
|
4528
4917
|
const effectiveMode = agentMode ?? context?.agentMode ?? void 0;
|
|
4529
4918
|
if (effectiveMode === "code-review") {
|
|
4530
|
-
return
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
...buildCodeReviewTools(connection)
|
|
4540
|
-
]
|
|
4541
|
-
});
|
|
4919
|
+
return [
|
|
4920
|
+
buildReadTaskChatTool(connection),
|
|
4921
|
+
buildGetTaskPlanTool(connection, config),
|
|
4922
|
+
buildGetTaskTool(connection),
|
|
4923
|
+
buildGetTaskCliTool(connection),
|
|
4924
|
+
buildListTaskFilesTool(connection),
|
|
4925
|
+
buildGetTaskFileTool(connection),
|
|
4926
|
+
...buildCodeReviewTools(connection)
|
|
4927
|
+
];
|
|
4542
4928
|
}
|
|
4543
4929
|
const commonTools = buildCommonTools(connection, config);
|
|
4544
4930
|
const modeTools = getModeTools(effectiveMode, connection, config, context);
|
|
4545
4931
|
const discoveryTools = effectiveMode === "discovery" || effectiveMode === "auto" ? buildDiscoveryTools(connection, context) : [];
|
|
4546
4932
|
const debugTools = debugManager && effectiveMode === "building" ? buildDebugTools(debugManager) : [];
|
|
4547
4933
|
const emergencyTools = [buildForceUpdateTaskStatusTool(connection)];
|
|
4548
|
-
return
|
|
4934
|
+
return [...commonTools, ...modeTools, ...discoveryTools, ...debugTools, ...emergencyTools];
|
|
4935
|
+
}
|
|
4936
|
+
function createConveyorMcpServer(harness, connection, config, context, agentMode, debugManager) {
|
|
4937
|
+
return harness.createMcpServer({
|
|
4549
4938
|
name: "conveyor",
|
|
4550
|
-
tools:
|
|
4939
|
+
tools: buildConveyorTools(connection, config, context, agentMode, debugManager)
|
|
4551
4940
|
});
|
|
4552
4941
|
}
|
|
4553
4942
|
|
|
@@ -4556,6 +4945,14 @@ import { randomUUID } from "crypto";
|
|
|
4556
4945
|
var PM_PLAN_FILE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit"]);
|
|
4557
4946
|
var DESTRUCTIVE_CMD_PATTERN = /git\s+push\s+--force(?!\s*-with-lease)|git\s+reset\s+--hard|rm\s+-rf\s+\//;
|
|
4558
4947
|
var CODE_REVIEW_WRITE_CMD_PATTERN = /git\s+push|git\s+commit|git\s+add|rm\s+|mv\s+|cp\s+|mkdir\s+|touch\s+|chmod\s+|chown\s+/;
|
|
4948
|
+
var RESOURCE_HEAVY_PATTERNS = [
|
|
4949
|
+
/\bbun\s+(install|i|add)\b/,
|
|
4950
|
+
/\bnpm\s+(install|ci|i)\b/,
|
|
4951
|
+
/\byarn(\s+install)?\s/,
|
|
4952
|
+
/\bpnpm\s+(install|i|add)\b/,
|
|
4953
|
+
/\bpip\s+install\b/,
|
|
4954
|
+
/\bcargo\s+build\b/
|
|
4955
|
+
];
|
|
4559
4956
|
function isPlanFile(input) {
|
|
4560
4957
|
const filePath = String(input.file_path ?? input.path ?? "");
|
|
4561
4958
|
return filePath.includes(".claude/plans/");
|
|
@@ -4658,14 +5055,15 @@ async function handleExitPlanMode(host, input) {
|
|
|
4658
5055
|
].join("\n")
|
|
4659
5056
|
};
|
|
4660
5057
|
}
|
|
4661
|
-
await host.connection.triggerIdentification();
|
|
4662
|
-
host.hasExitedPlanMode = true;
|
|
4663
5058
|
if (host.agentMode === "discovery") {
|
|
5059
|
+
host.hasExitedPlanMode = true;
|
|
4664
5060
|
host.connection.postChatMessage(
|
|
4665
|
-
"
|
|
5061
|
+
"Plan complete. The task stays in Planning \u2014 identify it or switch to Build mode when ready."
|
|
4666
5062
|
);
|
|
4667
5063
|
return { behavior: "allow", updatedInput: input };
|
|
4668
5064
|
}
|
|
5065
|
+
await host.connection.triggerIdentification();
|
|
5066
|
+
host.hasExitedPlanMode = true;
|
|
4669
5067
|
const newMode = host.isParentTask ? "review" : "building";
|
|
4670
5068
|
host.pendingModeRestart = true;
|
|
4671
5069
|
if (host.onModeTransition) {
|
|
@@ -4704,35 +5102,47 @@ async function handleAskUserQuestion(host, input) {
|
|
|
4704
5102
|
return { behavior: "allow", updatedInput: { questions: input.questions, answers } };
|
|
4705
5103
|
}
|
|
4706
5104
|
var DENIAL_WARNING_THRESHOLD = 3;
|
|
5105
|
+
function isResourceHeavyCommand(command) {
|
|
5106
|
+
return RESOURCE_HEAVY_PATTERNS.some((p) => p.test(command));
|
|
5107
|
+
}
|
|
5108
|
+
var autoScaled = false;
|
|
5109
|
+
async function maybeAutoScale(host, toolName, input) {
|
|
5110
|
+
if (autoScaled || !process.env.CLAUDESPACE_NAME || toolName !== "Bash" || typeof input.command !== "string" || !isResourceHeavyCommand(input.command)) {
|
|
5111
|
+
return;
|
|
5112
|
+
}
|
|
5113
|
+
autoScaled = true;
|
|
5114
|
+
try {
|
|
5115
|
+
await host.connection.requestScaleUp("light", `auto-scale: ${input.command.slice(0, 60)}`);
|
|
5116
|
+
} catch {
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
function resolveToolAccess(host, toolName, input) {
|
|
5120
|
+
switch (host.agentMode) {
|
|
5121
|
+
case "discovery":
|
|
5122
|
+
return handleDiscoveryToolAccess(toolName, input);
|
|
5123
|
+
case "building":
|
|
5124
|
+
return handleBuildingToolAccess(toolName, input);
|
|
5125
|
+
case "review":
|
|
5126
|
+
return handleReviewToolAccess(toolName, input, host.isParentTask);
|
|
5127
|
+
case "auto":
|
|
5128
|
+
return handleAutoToolAccess(toolName, input, host.hasExitedPlanMode, host.isParentTask);
|
|
5129
|
+
case "code-review":
|
|
5130
|
+
return handleCodeReviewToolAccess(toolName, input);
|
|
5131
|
+
default:
|
|
5132
|
+
return { behavior: "allow", updatedInput: input };
|
|
5133
|
+
}
|
|
5134
|
+
}
|
|
4707
5135
|
function buildCanUseTool(host) {
|
|
4708
5136
|
let consecutiveDenials = 0;
|
|
4709
5137
|
return async (toolName, input) => {
|
|
5138
|
+
await maybeAutoScale(host, toolName, input);
|
|
4710
5139
|
if (toolName === "ExitPlanMode" && (host.agentMode === "auto" || host.agentMode === "discovery") && !host.hasExitedPlanMode) {
|
|
4711
5140
|
return await handleExitPlanMode(host, input);
|
|
4712
5141
|
}
|
|
4713
5142
|
if (toolName === "AskUserQuestion") {
|
|
4714
5143
|
return await handleAskUserQuestion(host, input);
|
|
4715
5144
|
}
|
|
4716
|
-
|
|
4717
|
-
switch (host.agentMode) {
|
|
4718
|
-
case "discovery":
|
|
4719
|
-
result = handleDiscoveryToolAccess(toolName, input);
|
|
4720
|
-
break;
|
|
4721
|
-
case "building":
|
|
4722
|
-
result = handleBuildingToolAccess(toolName, input);
|
|
4723
|
-
break;
|
|
4724
|
-
case "review":
|
|
4725
|
-
result = handleReviewToolAccess(toolName, input, host.isParentTask);
|
|
4726
|
-
break;
|
|
4727
|
-
case "auto":
|
|
4728
|
-
result = handleAutoToolAccess(toolName, input, host.hasExitedPlanMode, host.isParentTask);
|
|
4729
|
-
break;
|
|
4730
|
-
case "code-review":
|
|
4731
|
-
result = handleCodeReviewToolAccess(toolName, input);
|
|
4732
|
-
break;
|
|
4733
|
-
default:
|
|
4734
|
-
result = { behavior: "allow", updatedInput: input };
|
|
4735
|
-
}
|
|
5145
|
+
const result = resolveToolAccess(host, toolName, input);
|
|
4736
5146
|
if (result.behavior === "deny") {
|
|
4737
5147
|
consecutiveDenials++;
|
|
4738
5148
|
if (consecutiveDenials === DENIAL_WARNING_THRESHOLD) {
|
|
@@ -4758,6 +5168,7 @@ function buildHooks(host) {
|
|
|
4758
5168
|
{
|
|
4759
5169
|
hooks: [
|
|
4760
5170
|
async (input) => {
|
|
5171
|
+
if (host.isStopped()) return await Promise.resolve({ continue: false });
|
|
4761
5172
|
if (input.hook_event_name === "PostToolUse") {
|
|
4762
5173
|
const output = typeof input.tool_response === "string" ? input.tool_response.slice(0, 500) : JSON.stringify(input.tool_response).slice(0, 500);
|
|
4763
5174
|
host.connection.sendEvent({
|
|
@@ -4801,7 +5212,7 @@ function buildQueryOptions(host, context) {
|
|
|
4801
5212
|
mode
|
|
4802
5213
|
);
|
|
4803
5214
|
const settingSources = settings.settingSources ?? ["user", "project"];
|
|
4804
|
-
|
|
5215
|
+
return {
|
|
4805
5216
|
model: context.model || host.config.model,
|
|
4806
5217
|
systemPrompt: {
|
|
4807
5218
|
type: "preset",
|
|
@@ -4814,7 +5225,9 @@ function buildQueryOptions(host, context) {
|
|
|
4814
5225
|
allowDangerouslySkipPermissions: !needsCanUseTool,
|
|
4815
5226
|
canUseTool: buildCanUseTool(host),
|
|
4816
5227
|
tools: { type: "preset", preset: "claude_code" },
|
|
4817
|
-
mcpServers: {
|
|
5228
|
+
mcpServers: {
|
|
5229
|
+
conveyor: createConveyorMcpServer(host.harness, host.connection, host.config, context, mode)
|
|
5230
|
+
},
|
|
4818
5231
|
sandbox: context.useSandbox ? { enabled: true } : { enabled: false },
|
|
4819
5232
|
hooks: buildHooks(host),
|
|
4820
5233
|
maxTurns: mode === "code-review" ? Math.min(settings.maxTurns ?? 15, 15) : settings.maxTurns,
|
|
@@ -4828,7 +5241,6 @@ function buildQueryOptions(host, context) {
|
|
|
4828
5241
|
logger3.warn("Claude Code stderr", { data: data.trimEnd() });
|
|
4829
5242
|
}
|
|
4830
5243
|
};
|
|
4831
|
-
return baseOptions;
|
|
4832
5244
|
}
|
|
4833
5245
|
function buildMultimodalPrompt(textPrompt, context, skipImages = false) {
|
|
4834
5246
|
if (skipImages) return textPrompt;
|
|
@@ -4901,9 +5313,10 @@ async function runSdkQuery(host, context, followUpContent) {
|
|
|
4901
5313
|
const resume = context.claudeSessionId ?? void 0;
|
|
4902
5314
|
if (followUpContent) {
|
|
4903
5315
|
const prompt = await buildFollowUpPrompt(host, context, followUpContent);
|
|
4904
|
-
const agentQuery =
|
|
5316
|
+
const agentQuery = host.harness.executeQuery({
|
|
4905
5317
|
prompt: typeof prompt === "string" ? prompt : host.createInputStream(prompt),
|
|
4906
|
-
options: { ...options
|
|
5318
|
+
options: { ...options },
|
|
5319
|
+
resume
|
|
4907
5320
|
});
|
|
4908
5321
|
host.activeQuery = agentQuery;
|
|
4909
5322
|
try {
|
|
@@ -4921,9 +5334,10 @@ async function runSdkQuery(host, context, followUpContent) {
|
|
|
4921
5334
|
mode
|
|
4922
5335
|
);
|
|
4923
5336
|
const prompt = buildMultimodalPrompt(initialPrompt, context);
|
|
4924
|
-
const agentQuery =
|
|
5337
|
+
const agentQuery = host.harness.executeQuery({
|
|
4925
5338
|
prompt: host.createInputStream(prompt),
|
|
4926
|
-
options: { ...options
|
|
5339
|
+
options: { ...options },
|
|
5340
|
+
resume
|
|
4927
5341
|
});
|
|
4928
5342
|
host.activeQuery = agentQuery;
|
|
4929
5343
|
try {
|
|
@@ -4947,9 +5361,10 @@ async function buildRetryQuery(host, context, options, lastErrorWasImage) {
|
|
|
4947
5361
|
context,
|
|
4948
5362
|
lastErrorWasImage
|
|
4949
5363
|
);
|
|
4950
|
-
return
|
|
5364
|
+
return host.harness.executeQuery({
|
|
4951
5365
|
prompt: host.createInputStream(retryPrompt),
|
|
4952
|
-
options: { ...options
|
|
5366
|
+
options: { ...options },
|
|
5367
|
+
resume: void 0
|
|
4953
5368
|
});
|
|
4954
5369
|
}
|
|
4955
5370
|
async function handleAuthError(context, host, options) {
|
|
@@ -4969,9 +5384,10 @@ async function handleAuthError(context, host, options) {
|
|
|
4969
5384
|
await buildInitialPrompt(host.config.mode, context, host.config.isAuto, host.agentMode),
|
|
4970
5385
|
context
|
|
4971
5386
|
);
|
|
4972
|
-
const freshQuery =
|
|
5387
|
+
const freshQuery = host.harness.executeQuery({
|
|
4973
5388
|
prompt: host.createInputStream(freshPrompt),
|
|
4974
|
-
options: { ...options
|
|
5389
|
+
options: { ...options },
|
|
5390
|
+
resume: void 0
|
|
4975
5391
|
});
|
|
4976
5392
|
return runWithRetry(freshQuery, context, host, options);
|
|
4977
5393
|
}
|
|
@@ -4982,9 +5398,10 @@ async function handleStaleSession(context, host, options) {
|
|
|
4982
5398
|
await buildInitialPrompt(host.config.mode, context, host.config.isAuto, host.agentMode),
|
|
4983
5399
|
context
|
|
4984
5400
|
);
|
|
4985
|
-
const freshQuery =
|
|
5401
|
+
const freshQuery = host.harness.executeQuery({
|
|
4986
5402
|
prompt: host.createInputStream(freshPrompt),
|
|
4987
|
-
options: { ...options
|
|
5403
|
+
options: { ...options },
|
|
5404
|
+
resume: void 0
|
|
4988
5405
|
});
|
|
4989
5406
|
return runWithRetry(freshQuery, context, host, options);
|
|
4990
5407
|
}
|
|
@@ -5227,7 +5644,26 @@ function pushSetupLog(setupLog, line) {
|
|
|
5227
5644
|
setupLog.splice(0, setupLog.length - MAX_SETUP_LOG_LINES);
|
|
5228
5645
|
}
|
|
5229
5646
|
}
|
|
5647
|
+
function shouldAutoScale(setupCmd, startCmd) {
|
|
5648
|
+
const cmds = [setupCmd, startCmd].filter(Boolean).join(" ");
|
|
5649
|
+
const heavyPatterns = [
|
|
5650
|
+
/\bbun\s+(install|i)\b/,
|
|
5651
|
+
/\bnpm\s+(install|ci)\b/,
|
|
5652
|
+
/\byarn\s+(install)?\b/,
|
|
5653
|
+
/\bpnpm\s+(install|i)\b/,
|
|
5654
|
+
/\bbun\s+run\s+(dev|start|build|test)\b/,
|
|
5655
|
+
/\bnpm\s+run\s+(dev|start|build|test)\b/,
|
|
5656
|
+
/\bmake\b/,
|
|
5657
|
+
/\bcargo\s+build\b/,
|
|
5658
|
+
/\bpip\s+install\b/
|
|
5659
|
+
];
|
|
5660
|
+
return heavyPatterns.some((p) => p.test(cmds));
|
|
5661
|
+
}
|
|
5230
5662
|
async function executeSetupConfig(config, runnerConfig, connection, setupLog) {
|
|
5663
|
+
if (process.env.CLAUDESPACE_NAME && shouldAutoScale(config.setupCommand, config.startCommand)) {
|
|
5664
|
+
connection.requestScaleUp("light", "setup-phase auto-scale").catch(() => {
|
|
5665
|
+
});
|
|
5666
|
+
}
|
|
5231
5667
|
if (config.setupCommand) {
|
|
5232
5668
|
pushSetupLog(setupLog, `$ ${config.setupCommand}`);
|
|
5233
5669
|
await runSetupCommand(config.setupCommand, runnerConfig.workspaceDir, (stream, data) => {
|
|
@@ -5479,6 +5915,7 @@ function buildQueryHost(deps) {
|
|
|
5479
5915
|
config: deps.config,
|
|
5480
5916
|
connection: deps.connection,
|
|
5481
5917
|
callbacks: deps.callbacks,
|
|
5918
|
+
harness: deps.harness,
|
|
5482
5919
|
setupLog: deps.setupLog,
|
|
5483
5920
|
costTracker: deps.costTracker,
|
|
5484
5921
|
get agentMode() {
|
|
@@ -5542,11 +5979,13 @@ var AgentRunner = class {
|
|
|
5542
5979
|
conveyorConfig = null;
|
|
5543
5980
|
_queryHost = null;
|
|
5544
5981
|
tunnelClient = null;
|
|
5982
|
+
harness;
|
|
5545
5983
|
constructor(config, callbacks) {
|
|
5546
5984
|
this.config = config;
|
|
5547
5985
|
this.connection = new ConveyorConnection(config);
|
|
5548
5986
|
this.callbacks = callbacks;
|
|
5549
5987
|
this.planSync = new PlanSync(config.workspaceDir, this.connection);
|
|
5988
|
+
this.harness = createHarness();
|
|
5550
5989
|
}
|
|
5551
5990
|
get state() {
|
|
5552
5991
|
return this._state;
|
|
@@ -5649,6 +6088,9 @@ var AgentRunner = class {
|
|
|
5649
6088
|
const pastPlanning = this.taskContext.status !== "Planning" && this.taskContext.status !== "Unidentified";
|
|
5650
6089
|
if (this.agentMode === "auto" && pastPlanning) {
|
|
5651
6090
|
this.hasExitedPlanMode = true;
|
|
6091
|
+
this.agentMode = "building";
|
|
6092
|
+
const builderModel = this.taskContext.builderModel ?? this.taskContext.model;
|
|
6093
|
+
if (builderModel) this.taskContext.model = builderModel;
|
|
5652
6094
|
}
|
|
5653
6095
|
this.logEffectiveSettings();
|
|
5654
6096
|
if (process.env.CODESPACES === "true") unshallowRepo(this.config.workspaceDir);
|
|
@@ -5929,6 +6371,7 @@ var AgentRunner = class {
|
|
|
5929
6371
|
config: this.config,
|
|
5930
6372
|
connection: this.connection,
|
|
5931
6373
|
callbacks: this.callbacks,
|
|
6374
|
+
harness: this.harness,
|
|
5932
6375
|
setupLog: this.setupLog,
|
|
5933
6376
|
costTracker: this.costTracker,
|
|
5934
6377
|
planSync: this.planSync,
|
|
@@ -5952,16 +6395,21 @@ var AgentRunner = class {
|
|
|
5952
6395
|
this._queryHost = host;
|
|
5953
6396
|
return host;
|
|
5954
6397
|
}
|
|
6398
|
+
lastAnnouncedMode = null;
|
|
5955
6399
|
handleModeChange(newAgentMode) {
|
|
5956
6400
|
if (this.config.mode !== "pm") return;
|
|
5957
|
-
if (newAgentMode
|
|
6401
|
+
if (!newAgentMode || newAgentMode === this.agentMode) return;
|
|
6402
|
+
this.agentMode = newAgentMode;
|
|
5958
6403
|
this.updateExitedPlanModeFlag(newAgentMode);
|
|
5959
6404
|
const effectiveMode = this.effectiveAgentMode;
|
|
5960
6405
|
const isBuildCapable = effectiveMode === "building" || effectiveMode === "auto" && this.hasExitedPlanMode;
|
|
5961
6406
|
this.connection.emitModeChanged(effectiveMode);
|
|
5962
|
-
this.
|
|
5963
|
-
|
|
5964
|
-
|
|
6407
|
+
if (effectiveMode !== this.lastAnnouncedMode) {
|
|
6408
|
+
this.lastAnnouncedMode = effectiveMode;
|
|
6409
|
+
this.connection.postChatMessage(
|
|
6410
|
+
`Mode switched to **${effectiveMode}**${effectiveMode === "building" ? " \u2014 I now have direct coding access." : ""}`
|
|
6411
|
+
);
|
|
6412
|
+
}
|
|
5965
6413
|
if (isBuildCapable && this.taskContext?.status === "Open") {
|
|
5966
6414
|
this.connection.updateStatus("InProgress");
|
|
5967
6415
|
this.taskContext.status = "InProgress";
|
|
@@ -5999,8 +6447,19 @@ var AgentRunner = class {
|
|
|
5999
6447
|
}
|
|
6000
6448
|
stop() {
|
|
6001
6449
|
this.stopped = true;
|
|
6450
|
+
this.stopHeartbeat();
|
|
6002
6451
|
this.clearIdleTimers();
|
|
6452
|
+
const host = this._queryHost;
|
|
6453
|
+
if (host?.activeQuery) {
|
|
6454
|
+
const q = host.activeQuery;
|
|
6455
|
+
if (typeof q.abort === "function") {
|
|
6456
|
+
q.abort();
|
|
6457
|
+
}
|
|
6458
|
+
void host.activeQuery.return(void 0);
|
|
6459
|
+
host.activeQuery = null;
|
|
6460
|
+
}
|
|
6003
6461
|
this.tunnelClient?.disconnect();
|
|
6462
|
+
this.connection.disconnect();
|
|
6004
6463
|
if (this.inputResolver) {
|
|
6005
6464
|
this.inputResolver(null);
|
|
6006
6465
|
this.inputResolver = null;
|
|
@@ -6119,18 +6578,11 @@ var CommitWatcher = class {
|
|
|
6119
6578
|
}
|
|
6120
6579
|
};
|
|
6121
6580
|
|
|
6122
|
-
// src/runner/project-chat-handler.ts
|
|
6123
|
-
import {
|
|
6124
|
-
query as query2,
|
|
6125
|
-
createSdkMcpServer as createSdkMcpServer2
|
|
6126
|
-
} from "@anthropic-ai/claude-agent-sdk";
|
|
6127
|
-
|
|
6128
6581
|
// src/tools/project-tools.ts
|
|
6129
|
-
import { tool as tool8 } from "@anthropic-ai/claude-agent-sdk";
|
|
6130
6582
|
import { z as z8 } from "zod";
|
|
6131
6583
|
function buildReadTools(connection) {
|
|
6132
6584
|
return [
|
|
6133
|
-
|
|
6585
|
+
defineTool(
|
|
6134
6586
|
"list_tasks",
|
|
6135
6587
|
"List tasks in the project. Optionally filter by status or assignee.",
|
|
6136
6588
|
{
|
|
@@ -6150,7 +6602,7 @@ function buildReadTools(connection) {
|
|
|
6150
6602
|
},
|
|
6151
6603
|
{ annotations: { readOnlyHint: true } }
|
|
6152
6604
|
),
|
|
6153
|
-
|
|
6605
|
+
defineTool(
|
|
6154
6606
|
"get_task",
|
|
6155
6607
|
"Get detailed information about a task including its chat messages, child tasks, and codespace status.",
|
|
6156
6608
|
{ task_id: z8.string().describe("The task ID to look up") },
|
|
@@ -6166,7 +6618,7 @@ function buildReadTools(connection) {
|
|
|
6166
6618
|
},
|
|
6167
6619
|
{ annotations: { readOnlyHint: true } }
|
|
6168
6620
|
),
|
|
6169
|
-
|
|
6621
|
+
defineTool(
|
|
6170
6622
|
"search_tasks",
|
|
6171
6623
|
"Search tasks by tags, text query, or status filters.",
|
|
6172
6624
|
{
|
|
@@ -6187,7 +6639,7 @@ function buildReadTools(connection) {
|
|
|
6187
6639
|
},
|
|
6188
6640
|
{ annotations: { readOnlyHint: true } }
|
|
6189
6641
|
),
|
|
6190
|
-
|
|
6642
|
+
defineTool(
|
|
6191
6643
|
"list_tags",
|
|
6192
6644
|
"List all tags available in the project.",
|
|
6193
6645
|
{},
|
|
@@ -6203,7 +6655,7 @@ function buildReadTools(connection) {
|
|
|
6203
6655
|
},
|
|
6204
6656
|
{ annotations: { readOnlyHint: true } }
|
|
6205
6657
|
),
|
|
6206
|
-
|
|
6658
|
+
defineTool(
|
|
6207
6659
|
"get_project_summary",
|
|
6208
6660
|
"Get a summary of the project including task counts by status and active builds.",
|
|
6209
6661
|
{},
|
|
@@ -6223,7 +6675,7 @@ function buildReadTools(connection) {
|
|
|
6223
6675
|
}
|
|
6224
6676
|
function buildMutationTools(connection) {
|
|
6225
6677
|
return [
|
|
6226
|
-
|
|
6678
|
+
defineTool(
|
|
6227
6679
|
"create_task",
|
|
6228
6680
|
"Create a new task in the project.",
|
|
6229
6681
|
{
|
|
@@ -6244,7 +6696,7 @@ function buildMutationTools(connection) {
|
|
|
6244
6696
|
}
|
|
6245
6697
|
}
|
|
6246
6698
|
),
|
|
6247
|
-
|
|
6699
|
+
defineTool(
|
|
6248
6700
|
"update_task",
|
|
6249
6701
|
"Update an existing task's title, description, plan, status, or assignee.",
|
|
6250
6702
|
{
|
|
@@ -6347,7 +6799,8 @@ async function fetchContext(connection, chatId) {
|
|
|
6347
6799
|
function buildChatQueryOptions(agentCtx, projectDir, connection) {
|
|
6348
6800
|
const model = agentCtx?.model || FALLBACK_MODEL;
|
|
6349
6801
|
const settings = agentCtx?.agentSettings ?? {};
|
|
6350
|
-
const
|
|
6802
|
+
const harness = createHarness();
|
|
6803
|
+
const mcpServer = harness.createMcpServer({
|
|
6351
6804
|
name: "conveyor",
|
|
6352
6805
|
tools: buildProjectTools(connection)
|
|
6353
6806
|
});
|
|
@@ -6435,7 +6888,8 @@ async function runChatQuery(message, connection, projectDir, sessionId) {
|
|
|
6435
6888
|
const options = buildChatQueryOptions(agentCtx, projectDir, connection);
|
|
6436
6889
|
const prompt = buildPrompt(message, chatHistory);
|
|
6437
6890
|
connection.emitAgentStatus("running");
|
|
6438
|
-
const
|
|
6891
|
+
const harness = createHarness();
|
|
6892
|
+
const events = harness.executeQuery({
|
|
6439
6893
|
prompt,
|
|
6440
6894
|
options,
|
|
6441
6895
|
...sessionId ? { resume: sessionId } : {}
|
|
@@ -6480,12 +6934,8 @@ async function handleProjectChatMessage(message, connection, projectDir, session
|
|
|
6480
6934
|
}
|
|
6481
6935
|
}
|
|
6482
6936
|
|
|
6483
|
-
// src/runner/project-audit-handler.ts
|
|
6484
|
-
import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
|
|
6485
|
-
|
|
6486
6937
|
// src/tools/audit-tools.ts
|
|
6487
6938
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
6488
|
-
import { tool as tool9, createSdkMcpServer as createSdkMcpServer3 } from "@anthropic-ai/claude-agent-sdk";
|
|
6489
6939
|
import { z as z9 } from "zod";
|
|
6490
6940
|
function mapCreateTag(input) {
|
|
6491
6941
|
return {
|
|
@@ -6566,9 +7016,9 @@ function collectRecommendation(toolName, input, collector, onRecommendation) {
|
|
|
6566
7016
|
onRecommendation?.({ tagName: rec.tagName ?? rec.type, type: rec.type });
|
|
6567
7017
|
return JSON.stringify({ success: true, recommendationId: rec.id });
|
|
6568
7018
|
}
|
|
6569
|
-
function
|
|
6570
|
-
|
|
6571
|
-
|
|
7019
|
+
function buildAuditTools(collector, onRecommendation) {
|
|
7020
|
+
return [
|
|
7021
|
+
defineTool(
|
|
6572
7022
|
"recommend_create_tag",
|
|
6573
7023
|
"Recommend creating a new tag for an uncovered subsystem or area",
|
|
6574
7024
|
{
|
|
@@ -6587,7 +7037,7 @@ function createAuditMcpServer(collector, onRecommendation) {
|
|
|
6587
7037
|
return { content: [{ type: "text", text: result }] };
|
|
6588
7038
|
}
|
|
6589
7039
|
),
|
|
6590
|
-
|
|
7040
|
+
defineTool(
|
|
6591
7041
|
"recommend_update_description",
|
|
6592
7042
|
"Recommend updating a tag's description to better reflect its scope",
|
|
6593
7043
|
{
|
|
@@ -6606,7 +7056,7 @@ function createAuditMcpServer(collector, onRecommendation) {
|
|
|
6606
7056
|
return { content: [{ type: "text", text: result }] };
|
|
6607
7057
|
}
|
|
6608
7058
|
),
|
|
6609
|
-
|
|
7059
|
+
defineTool(
|
|
6610
7060
|
"recommend_context_link",
|
|
6611
7061
|
"Recommend linking a doc, rule, file, or folder to a tag's contextPaths",
|
|
6612
7062
|
{
|
|
@@ -6627,7 +7077,7 @@ function createAuditMcpServer(collector, onRecommendation) {
|
|
|
6627
7077
|
return { content: [{ type: "text", text: result }] };
|
|
6628
7078
|
}
|
|
6629
7079
|
),
|
|
6630
|
-
|
|
7080
|
+
defineTool(
|
|
6631
7081
|
"flag_documentation_gap",
|
|
6632
7082
|
"Flag a file that agents read heavily but has no tag documentation linked",
|
|
6633
7083
|
{
|
|
@@ -6648,7 +7098,7 @@ function createAuditMcpServer(collector, onRecommendation) {
|
|
|
6648
7098
|
return { content: [{ type: "text", text: result }] };
|
|
6649
7099
|
}
|
|
6650
7100
|
),
|
|
6651
|
-
|
|
7101
|
+
defineTool(
|
|
6652
7102
|
"recommend_merge_tags",
|
|
6653
7103
|
"Recommend merging one tag into another",
|
|
6654
7104
|
{
|
|
@@ -6668,7 +7118,7 @@ function createAuditMcpServer(collector, onRecommendation) {
|
|
|
6668
7118
|
return { content: [{ type: "text", text: result }] };
|
|
6669
7119
|
}
|
|
6670
7120
|
),
|
|
6671
|
-
|
|
7121
|
+
defineTool(
|
|
6672
7122
|
"recommend_rename_tag",
|
|
6673
7123
|
"Recommend renaming a tag",
|
|
6674
7124
|
{
|
|
@@ -6687,7 +7137,7 @@ function createAuditMcpServer(collector, onRecommendation) {
|
|
|
6687
7137
|
return { content: [{ type: "text", text: result }] };
|
|
6688
7138
|
}
|
|
6689
7139
|
),
|
|
6690
|
-
|
|
7140
|
+
defineTool(
|
|
6691
7141
|
"complete_audit",
|
|
6692
7142
|
"Signal that the audit is complete with a summary of all findings",
|
|
6693
7143
|
{ summary: z9.string().describe("Brief overview of all findings") },
|
|
@@ -6698,9 +7148,11 @@ function createAuditMcpServer(collector, onRecommendation) {
|
|
|
6698
7148
|
}
|
|
6699
7149
|
)
|
|
6700
7150
|
];
|
|
6701
|
-
|
|
7151
|
+
}
|
|
7152
|
+
function createAuditMcpServer(harness, collector, onRecommendation) {
|
|
7153
|
+
return harness.createMcpServer({
|
|
6702
7154
|
name: "tag-audit",
|
|
6703
|
-
tools:
|
|
7155
|
+
tools: buildAuditTools(collector, onRecommendation)
|
|
6704
7156
|
});
|
|
6705
7157
|
}
|
|
6706
7158
|
|
|
@@ -6810,7 +7262,8 @@ async function runAuditQuery(request, connection, projectDir) {
|
|
|
6810
7262
|
"Start by exploring the codebase structure, then analyze each tag for accuracy and completeness.",
|
|
6811
7263
|
"Call complete_audit when done."
|
|
6812
7264
|
].join("\n");
|
|
6813
|
-
const
|
|
7265
|
+
const harness = createHarness();
|
|
7266
|
+
const events = harness.executeQuery({
|
|
6814
7267
|
prompt: userPrompt,
|
|
6815
7268
|
options: {
|
|
6816
7269
|
model,
|
|
@@ -6819,7 +7272,7 @@ async function runAuditQuery(request, connection, projectDir) {
|
|
|
6819
7272
|
permissionMode: "bypassPermissions",
|
|
6820
7273
|
allowDangerouslySkipPermissions: true,
|
|
6821
7274
|
tools: { type: "preset", preset: "claude_code" },
|
|
6822
|
-
mcpServers: { "tag-audit": createAuditMcpServer(collector, onRecommendation) },
|
|
7275
|
+
mcpServers: { "tag-audit": createAuditMcpServer(harness, collector, onRecommendation) },
|
|
6823
7276
|
maxTurns: settings.maxTurns ?? 75,
|
|
6824
7277
|
maxBudgetUsd: settings.maxBudgetUsd ?? 5,
|
|
6825
7278
|
effort: settings.effort,
|
|
@@ -7349,8 +7802,23 @@ var ProjectRunner = class {
|
|
|
7349
7802
|
logger8.info("Connected, waiting for task assignments");
|
|
7350
7803
|
await new Promise((resolve2) => {
|
|
7351
7804
|
this.resolveLifecycle = resolve2;
|
|
7352
|
-
|
|
7353
|
-
|
|
7805
|
+
this.setupSignalHandlers();
|
|
7806
|
+
});
|
|
7807
|
+
}
|
|
7808
|
+
setupSignalHandlers() {
|
|
7809
|
+
process.on("SIGTERM", () => {
|
|
7810
|
+
setTimeout(() => {
|
|
7811
|
+
logger8.warn("Forcing exit after SIGTERM timeout");
|
|
7812
|
+
process.exit(1);
|
|
7813
|
+
}, STOP_TIMEOUT_MS + 1e4).unref();
|
|
7814
|
+
void this.stop();
|
|
7815
|
+
});
|
|
7816
|
+
process.on("SIGINT", () => {
|
|
7817
|
+
setTimeout(() => {
|
|
7818
|
+
logger8.warn("Forcing exit after SIGINT timeout");
|
|
7819
|
+
process.exit(1);
|
|
7820
|
+
}, STOP_TIMEOUT_MS + 1e4).unref();
|
|
7821
|
+
void this.stop();
|
|
7354
7822
|
});
|
|
7355
7823
|
}
|
|
7356
7824
|
handleAssignment(assignment) {
|
|
@@ -7409,7 +7877,10 @@ var ProjectRunner = class {
|
|
|
7409
7877
|
}
|
|
7410
7878
|
}
|
|
7411
7879
|
handleStopTask(taskId) {
|
|
7412
|
-
|
|
7880
|
+
let agent = this.activeAgents.get(taskId);
|
|
7881
|
+
if (!agent) {
|
|
7882
|
+
agent = this.activeAgents.get(`${taskId}:code-review`);
|
|
7883
|
+
}
|
|
7413
7884
|
if (!agent) return;
|
|
7414
7885
|
const shortId = taskId.slice(0, 8);
|
|
7415
7886
|
logger8.info("Stopping task", { taskId: shortId });
|
|
@@ -7455,7 +7926,7 @@ var ProjectRunner = class {
|
|
|
7455
7926
|
await Promise.race([
|
|
7456
7927
|
Promise.all(stopPromises),
|
|
7457
7928
|
new Promise((resolve2) => {
|
|
7458
|
-
setTimeout(resolve2,
|
|
7929
|
+
setTimeout(resolve2, STOP_TIMEOUT_MS + 5e3);
|
|
7459
7930
|
})
|
|
7460
7931
|
]);
|
|
7461
7932
|
this.connection.disconnect();
|
|
@@ -7546,4 +8017,4 @@ export {
|
|
|
7546
8017
|
ProjectRunner,
|
|
7547
8018
|
FileCache
|
|
7548
8019
|
};
|
|
7549
|
-
//# sourceMappingURL=chunk-
|
|
8020
|
+
//# sourceMappingURL=chunk-WBBX5AIX.js.map
|