@matthugh1/conductor-cli 0.2.0 → 0.2.2
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/agent-spawner-BNOGEYDK.js +232 -0
- package/dist/agent.js +78 -16
- package/dist/{branch-overview-XVHTGFCJ.js → branch-overview-DSSCUE5F.js} +1 -1
- package/dist/chunk-3MJBQK2F.js +75 -0
- package/dist/chunk-6AA726KG.js +238 -0
- package/dist/{chunk-IHARLSA6.js → chunk-7S5HKGS5.js} +2 -1
- package/dist/{chunk-MJKFQIYA.js → chunk-B2WDTKD7.js} +19 -20
- package/dist/{chunk-JZT526HU.js → chunk-KB2DTST2.js} +27 -81
- package/dist/{cli-config-TDSTAXIA.js → cli-config-2ZDXUUQN.js} +5 -1
- package/dist/{cli-tasks-NW3BONXC.js → cli-tasks-NM5D5PIZ.js} +3 -2
- package/dist/daemon-GGOJDZDB.js +598 -0
- package/dist/daemon-client-BE64H437.js +312 -0
- package/dist/{health-CTND2ANA.js → health-UFK7YCKQ.js} +1 -1
- package/dist/runner-prompt-MOOPKA5P.js +9 -0
- package/dist/{work-queue-YE5P4S7R.js → work-queue-U3JYHLX2.js} +11 -17
- package/dist/{worktree-manager-QKRBTPVC.js → worktree-manager-2ZUJEL3L.js} +2 -1
- package/package.json +2 -2
- package/dist/runner-prompt-2B6EXGN6.js +0 -139
- /package/dist/{chunk-VYINBHPQ.js → chunk-6VMREHG4.js} +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ../../src/core/daemon-client-types.ts
|
|
4
|
+
var DaemonClientError = class extends Error {
|
|
5
|
+
constructor(message, status) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.name = "DaemonClientError";
|
|
9
|
+
}
|
|
10
|
+
status;
|
|
11
|
+
};
|
|
12
|
+
var RETRY_STATUSES = /* @__PURE__ */ new Set([502, 503, 504]);
|
|
13
|
+
var MAX_RETRIES = 2;
|
|
14
|
+
async function fetchWithRetry(url, init) {
|
|
15
|
+
let lastError;
|
|
16
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
17
|
+
try {
|
|
18
|
+
const res = await fetch(url, init);
|
|
19
|
+
if (attempt < MAX_RETRIES && RETRY_STATUSES.has(res.status)) {
|
|
20
|
+
await sleep(backoffMs(attempt));
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
return res;
|
|
24
|
+
} catch (err) {
|
|
25
|
+
lastError = err;
|
|
26
|
+
if (attempt < MAX_RETRIES && isTransient(err)) {
|
|
27
|
+
await sleep(backoffMs(attempt));
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw lastError;
|
|
34
|
+
}
|
|
35
|
+
function isTransient(err) {
|
|
36
|
+
if (err instanceof TypeError) return true;
|
|
37
|
+
if (err instanceof Error) {
|
|
38
|
+
const msg = err.message.toLowerCase();
|
|
39
|
+
return msg.includes("econnrefused") || msg.includes("econnreset");
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
function backoffMs(attempt) {
|
|
44
|
+
return (attempt + 1) * 500;
|
|
45
|
+
}
|
|
46
|
+
function sleep(ms) {
|
|
47
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
48
|
+
}
|
|
49
|
+
async function assertOk(res, context) {
|
|
50
|
+
if (res.ok) return;
|
|
51
|
+
let message = `${context}: HTTP ${res.status}`;
|
|
52
|
+
try {
|
|
53
|
+
const body = await res.json();
|
|
54
|
+
if (body.error) message = `${context}: ${body.error}`;
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
throw new DaemonClientError(message, res.status);
|
|
58
|
+
}
|
|
59
|
+
async function jsonBody(res, context) {
|
|
60
|
+
await assertOk(res, context);
|
|
61
|
+
return await res.json();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ../../src/core/daemon-client.ts
|
|
65
|
+
function createDaemonClient(baseUrl = "https://conductor-297703646986.europe-west2.run.app", apiKey) {
|
|
66
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
67
|
+
let defaultProjectId;
|
|
68
|
+
let resolvedKey = apiKey ?? process.env.CONDUCTOR_API_KEY;
|
|
69
|
+
let cliConfigLoaded = false;
|
|
70
|
+
async function ensureConfig() {
|
|
71
|
+
if (cliConfigLoaded) return;
|
|
72
|
+
cliConfigLoaded = true;
|
|
73
|
+
try {
|
|
74
|
+
const { readApiKey, readProjectId } = await import("./cli-config-2ZDXUUQN.js");
|
|
75
|
+
if (!resolvedKey) resolvedKey = readApiKey();
|
|
76
|
+
if (!defaultProjectId) defaultProjectId = readProjectId();
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function authHeaders() {
|
|
81
|
+
const headers = {};
|
|
82
|
+
if (resolvedKey) {
|
|
83
|
+
headers.Authorization = `Bearer ${resolvedKey}`;
|
|
84
|
+
}
|
|
85
|
+
return headers;
|
|
86
|
+
}
|
|
87
|
+
function url(path, params) {
|
|
88
|
+
const u = new URL(path, base);
|
|
89
|
+
if (defaultProjectId) {
|
|
90
|
+
u.searchParams.set("projectId", defaultProjectId);
|
|
91
|
+
}
|
|
92
|
+
if (params) {
|
|
93
|
+
for (const [k, v] of Object.entries(params)) {
|
|
94
|
+
u.searchParams.set(k, v);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return u.toString();
|
|
98
|
+
}
|
|
99
|
+
async function post(path, body, params) {
|
|
100
|
+
await ensureConfig();
|
|
101
|
+
return fetchWithRetry(url(path, params), {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: { ...authHeaders(), "Content-Type": "application/json" },
|
|
104
|
+
body: JSON.stringify(body)
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async function patch(path, body, params) {
|
|
108
|
+
await ensureConfig();
|
|
109
|
+
return fetchWithRetry(url(path, params), {
|
|
110
|
+
method: "PATCH",
|
|
111
|
+
headers: { ...authHeaders(), "Content-Type": "application/json" },
|
|
112
|
+
body: JSON.stringify(body)
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async function get(path, params) {
|
|
116
|
+
await ensureConfig();
|
|
117
|
+
return fetchWithRetry(url(path, params), {
|
|
118
|
+
method: "GET",
|
|
119
|
+
headers: authHeaders()
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async function del(path, params) {
|
|
123
|
+
await ensureConfig();
|
|
124
|
+
return fetchWithRetry(url(path, params), {
|
|
125
|
+
method: "DELETE",
|
|
126
|
+
headers: authHeaders()
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
async resolveProject(opts) {
|
|
131
|
+
const res = await get("/api/projects");
|
|
132
|
+
const { projects } = await jsonBody(
|
|
133
|
+
res,
|
|
134
|
+
"resolveProject"
|
|
135
|
+
);
|
|
136
|
+
const match = projects.find((p) => {
|
|
137
|
+
if (opts.name && p.name === opts.name) return true;
|
|
138
|
+
if (opts.path && p.path === opts.path) return true;
|
|
139
|
+
return false;
|
|
140
|
+
});
|
|
141
|
+
if (!match) {
|
|
142
|
+
throw new DaemonClientError(
|
|
143
|
+
`Project not found (name=${opts.name ?? ""}, path=${opts.path ?? ""})`,
|
|
144
|
+
404
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
if (!match.path) {
|
|
148
|
+
throw new DaemonClientError(
|
|
149
|
+
`Project "${match.name}" has no local path registered.`,
|
|
150
|
+
400
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
defaultProjectId = match.id;
|
|
154
|
+
return { id: match.id, path: match.path };
|
|
155
|
+
},
|
|
156
|
+
async upsertHeartbeat(opts) {
|
|
157
|
+
const res = await post("/api/daemon/heartbeat", opts);
|
|
158
|
+
await assertOk(res, "upsertHeartbeat");
|
|
159
|
+
},
|
|
160
|
+
async clearHeartbeat(projectId) {
|
|
161
|
+
const res = await del("/api/daemon/heartbeat", { projectId });
|
|
162
|
+
await assertOk(res, "clearHeartbeat");
|
|
163
|
+
},
|
|
164
|
+
async createRun(opts) {
|
|
165
|
+
const res = await post("/api/daemon/runs", opts);
|
|
166
|
+
const { run } = await jsonBody(res, "createRun");
|
|
167
|
+
return run;
|
|
168
|
+
},
|
|
169
|
+
async completeRun(runId, opts) {
|
|
170
|
+
const res = await patch(`/api/daemon/runs/${runId}`, opts);
|
|
171
|
+
await assertOk(res, "completeRun");
|
|
172
|
+
},
|
|
173
|
+
async updateRunPid(runId, pid) {
|
|
174
|
+
const res = await patch(`/api/daemon/runs/${runId}`, { pid });
|
|
175
|
+
await assertOk(res, "updateRunPid");
|
|
176
|
+
},
|
|
177
|
+
async getQuota(projectId) {
|
|
178
|
+
const res = await get("/api/daemon/quota", { projectId });
|
|
179
|
+
const { count } = await jsonBody(res, "getQuota");
|
|
180
|
+
return count;
|
|
181
|
+
},
|
|
182
|
+
async purgeOldRuns(daysOld) {
|
|
183
|
+
const body = daysOld !== void 0 ? { daysOld } : {};
|
|
184
|
+
const res = await post("/api/daemon/purge", body);
|
|
185
|
+
const { purged } = await jsonBody(
|
|
186
|
+
res,
|
|
187
|
+
"purgeOldRuns"
|
|
188
|
+
);
|
|
189
|
+
return purged;
|
|
190
|
+
},
|
|
191
|
+
async pollWorkQueue(projectId, limit, opts) {
|
|
192
|
+
const params = { projectId };
|
|
193
|
+
if (limit !== void 0) params.limit = String(limit);
|
|
194
|
+
if (opts?.autonomous) params.autonomous = "true";
|
|
195
|
+
const res = await get("/api/work-queue", params);
|
|
196
|
+
return jsonBody(res, "pollWorkQueue");
|
|
197
|
+
},
|
|
198
|
+
async updateDeliverableStatus(deliverableId, status) {
|
|
199
|
+
const res = await patch(`/api/backlog/${deliverableId}`, { status });
|
|
200
|
+
await assertOk(res, "updateDeliverableStatus");
|
|
201
|
+
},
|
|
202
|
+
async getDeliverableDetail(deliverableId) {
|
|
203
|
+
const res = await get(`/api/backlog/${deliverableId}`);
|
|
204
|
+
if (res.status === 404) return null;
|
|
205
|
+
const data = await jsonBody(res, "getDeliverableDetail");
|
|
206
|
+
return {
|
|
207
|
+
prompt: data.deliverable?.prompt ?? null,
|
|
208
|
+
type: data.deliverable?.type ?? null
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
async getInitiativeForDeliverable(deliverableId) {
|
|
212
|
+
const res = await get(
|
|
213
|
+
`/api/pipeline/deliverables/${deliverableId}`
|
|
214
|
+
);
|
|
215
|
+
if (res.status === 404) return null;
|
|
216
|
+
const data = await jsonBody(res, "getInitiativeForDeliverable");
|
|
217
|
+
const d = data.parent;
|
|
218
|
+
if (!d?.initiativeId || !d.initiativeTitle) return null;
|
|
219
|
+
return {
|
|
220
|
+
initiativeId: d.initiativeId,
|
|
221
|
+
initiativeTitle: d.initiativeTitle
|
|
222
|
+
};
|
|
223
|
+
},
|
|
224
|
+
async appendOutputLine(runId, opts) {
|
|
225
|
+
const res = await post(`/api/daemon/runs/${runId}/output`, opts);
|
|
226
|
+
await assertOk(res, "appendOutputLine");
|
|
227
|
+
},
|
|
228
|
+
async hasPendingDecisions(projectId, deliverableId) {
|
|
229
|
+
const res = await get("/api/decisions", {
|
|
230
|
+
status: "pending",
|
|
231
|
+
scope: "deliverable",
|
|
232
|
+
deliverableId,
|
|
233
|
+
projectId
|
|
234
|
+
});
|
|
235
|
+
const { pending } = await jsonBody(res, "hasPendingDecisions");
|
|
236
|
+
return pending.length > 0;
|
|
237
|
+
},
|
|
238
|
+
async ingestAutonomousDeliverables(projectId, projectRoot) {
|
|
239
|
+
const res = await post("/api/daemon/ingest", {
|
|
240
|
+
projectId,
|
|
241
|
+
projectRoot
|
|
242
|
+
});
|
|
243
|
+
const { created } = await jsonBody(
|
|
244
|
+
res,
|
|
245
|
+
"ingestAutonomousDeliverables"
|
|
246
|
+
);
|
|
247
|
+
return created;
|
|
248
|
+
},
|
|
249
|
+
async routeUnassignedTasks(projectId) {
|
|
250
|
+
const res = await post(
|
|
251
|
+
"/api/daemon/tasks/route-unassigned",
|
|
252
|
+
{ projectId }
|
|
253
|
+
);
|
|
254
|
+
const { assignments } = await jsonBody(res, "routeUnassignedTasks");
|
|
255
|
+
return assignments;
|
|
256
|
+
},
|
|
257
|
+
async routePendingTasks(projectId, projectRoot) {
|
|
258
|
+
const res = await post("/api/daemon/route", {
|
|
259
|
+
projectId,
|
|
260
|
+
projectRoot
|
|
261
|
+
});
|
|
262
|
+
return jsonBody(
|
|
263
|
+
res,
|
|
264
|
+
"routePendingTasks"
|
|
265
|
+
);
|
|
266
|
+
},
|
|
267
|
+
// ── Agent backlog methods ───────────────────────────────────────
|
|
268
|
+
async listEnabledAgents(projectId) {
|
|
269
|
+
const res = await get("/api/daemon/agents", {
|
|
270
|
+
projectId,
|
|
271
|
+
enabledOnly: "true"
|
|
272
|
+
});
|
|
273
|
+
const { agents } = await jsonBody(
|
|
274
|
+
res,
|
|
275
|
+
"listEnabledAgents"
|
|
276
|
+
);
|
|
277
|
+
return agents;
|
|
278
|
+
},
|
|
279
|
+
async getNextTasksForAgent(agentId, limit) {
|
|
280
|
+
const params = { agentId };
|
|
281
|
+
if (limit !== void 0) params.limit = String(limit);
|
|
282
|
+
const res = await get("/api/daemon/tasks/next", params);
|
|
283
|
+
const { tasks } = await jsonBody(
|
|
284
|
+
res,
|
|
285
|
+
"getNextTasksForAgent"
|
|
286
|
+
);
|
|
287
|
+
return tasks;
|
|
288
|
+
},
|
|
289
|
+
async claimTask(taskId) {
|
|
290
|
+
const res = await post(`/api/daemon/tasks/${taskId}/claim`, {});
|
|
291
|
+
if (res.status === 409) return null;
|
|
292
|
+
const { task } = await jsonBody(
|
|
293
|
+
res,
|
|
294
|
+
"claimTask"
|
|
295
|
+
);
|
|
296
|
+
return task;
|
|
297
|
+
},
|
|
298
|
+
async updateTask(taskId, updates) {
|
|
299
|
+
const res = await patch(`/api/daemon/tasks/${taskId}`, updates);
|
|
300
|
+
if (res.status === 404) return null;
|
|
301
|
+
const { task } = await jsonBody(
|
|
302
|
+
res,
|
|
303
|
+
"updateTask"
|
|
304
|
+
);
|
|
305
|
+
return task;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
export {
|
|
310
|
+
DaemonClientError,
|
|
311
|
+
createDaemonClient
|
|
312
|
+
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getConflictBranchesForWorkQueue
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-7S5HKGS5.js";
|
|
5
|
+
import "./chunk-3MJBQK2F.js";
|
|
6
|
+
import "./chunk-6VMREHG4.js";
|
|
6
7
|
import {
|
|
7
8
|
getGitBranchInfo
|
|
8
9
|
} from "./chunk-FAZ7FCZQ.js";
|
|
@@ -444,7 +445,7 @@ async function computeWorkQueue(projectRoot, limit = 20, options) {
|
|
|
444
445
|
try {
|
|
445
446
|
const activeRows = await query(
|
|
446
447
|
`
|
|
447
|
-
SELECT d.id, d.title, d.assigned_role, i.title AS initiative_title,
|
|
448
|
+
SELECT d.id, d.title, d.assigned_role, d.autonomous, i.title AS initiative_title,
|
|
448
449
|
i.rice_score::text AS rice_score
|
|
449
450
|
FROM deliverables d
|
|
450
451
|
JOIN outcomes o ON o.id = d.outcome_id
|
|
@@ -469,7 +470,8 @@ async function computeWorkQueue(projectRoot, limit = 20, options) {
|
|
|
469
470
|
entityId: row.id,
|
|
470
471
|
entityType: "deliverable",
|
|
471
472
|
initiativeTitle: row.initiative_title,
|
|
472
|
-
riceScore: rice
|
|
473
|
+
riceScore: rice,
|
|
474
|
+
autonomous: row.autonomous
|
|
473
475
|
});
|
|
474
476
|
}
|
|
475
477
|
} catch {
|
|
@@ -477,7 +479,7 @@ async function computeWorkQueue(projectRoot, limit = 20, options) {
|
|
|
477
479
|
try {
|
|
478
480
|
const readyRows = await query(
|
|
479
481
|
`
|
|
480
|
-
SELECT d.id, d.title, d.assigned_role, i.title AS initiative_title,
|
|
482
|
+
SELECT d.id, d.title, d.assigned_role, d.autonomous, i.title AS initiative_title,
|
|
481
483
|
i.rice_score::text AS rice_score, i.current_stage
|
|
482
484
|
FROM deliverables d
|
|
483
485
|
JOIN outcomes o ON o.id = d.outcome_id
|
|
@@ -506,7 +508,8 @@ async function computeWorkQueue(projectRoot, limit = 20, options) {
|
|
|
506
508
|
entityId: row.id,
|
|
507
509
|
entityType: "deliverable",
|
|
508
510
|
initiativeTitle: row.initiative_title,
|
|
509
|
-
riceScore: rice
|
|
511
|
+
riceScore: rice,
|
|
512
|
+
autonomous: row.autonomous
|
|
510
513
|
});
|
|
511
514
|
}
|
|
512
515
|
} catch {
|
|
@@ -555,16 +558,7 @@ async function computeWorkQueue(projectRoot, limit = 20, options) {
|
|
|
555
558
|
ON prev.outcome_id = cur.outcome_id AND prev.rn = cur.rn - 1
|
|
556
559
|
WHERE cur.status = 'todo'
|
|
557
560
|
AND prev.status IN ('done', 'review')
|
|
558
|
-
AND
|
|
559
|
-
SELECT 1 FROM prompts p
|
|
560
|
-
WHERE p.deliverable_id = cur.id
|
|
561
|
-
)
|
|
562
|
-
AND NOT EXISTS (
|
|
563
|
-
SELECT 1 FROM prompts p
|
|
564
|
-
WHERE p.project_id = cur.project_id
|
|
565
|
-
AND p.initiative_id IS NULL
|
|
566
|
-
AND p.deliverable_id IS NULL
|
|
567
|
-
)
|
|
561
|
+
AND (cur.prompt IS NULL OR cur.prompt = '')
|
|
568
562
|
`,
|
|
569
563
|
[projectId]
|
|
570
564
|
);
|
|
@@ -612,7 +606,7 @@ async function computeWorkQueue(projectRoot, limit = 20, options) {
|
|
|
612
606
|
);
|
|
613
607
|
for (const row of blockedRows) {
|
|
614
608
|
const rice = parseRice(row.rice_score);
|
|
615
|
-
const isUnblocked = row.blocker_status === "done";
|
|
609
|
+
const isUnblocked = row.blocker_status === "done" || row.blocker_status === "review";
|
|
616
610
|
items.push({
|
|
617
611
|
priority: isUnblocked ? 0 : 50,
|
|
618
612
|
tier: isUnblocked ? "ready" : "pipeline",
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
removeWorktree,
|
|
12
12
|
scanWorktrees,
|
|
13
13
|
slugifyForBranch
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-KB2DTST2.js";
|
|
15
|
+
import "./chunk-3MJBQK2F.js";
|
|
15
16
|
import "./chunk-FAZ7FCZQ.js";
|
|
16
17
|
import "./chunk-PANC6BTV.js";
|
|
17
18
|
import "./chunk-4YEHSYVN.js";
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matthugh1/conductor-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Conductor CLI — session management, health checks, and autonomous runner for the Conductor pipeline",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"conductor": "
|
|
7
|
+
"conductor": "dist/agent.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist"
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// ../../src/core/runner-prompt.ts
|
|
4
|
-
var AUTONOMOUS_WORKFLOW = `
|
|
5
|
-
## Identity
|
|
6
|
-
|
|
7
|
-
You are an autonomous implementation engineer spawned by Conductor's runner.
|
|
8
|
-
You have no human in the loop \u2014 every decision you cannot make yourself must
|
|
9
|
-
be parked for morning review. Work carefully and methodically.
|
|
10
|
-
|
|
11
|
-
## Workflow (follow in order)
|
|
12
|
-
|
|
13
|
-
### 1. Start your session
|
|
14
|
-
|
|
15
|
-
Call conductor_session_brief with:
|
|
16
|
-
- projectRoot: the project name given in your assignment below
|
|
17
|
-
- deliverableId: the deliverable ID given in your assignment below
|
|
18
|
-
- agentRole: "implementation-engineer"
|
|
19
|
-
- agentType: "claude-code"
|
|
20
|
-
|
|
21
|
-
Save the returned sessionId \u2014 pass it to every subsequent Conductor call.
|
|
22
|
-
|
|
23
|
-
### 2. Read the brief and understand your assignment
|
|
24
|
-
|
|
25
|
-
The session brief contains your deliverable title, description, and
|
|
26
|
-
acceptance criteria. Read them carefully before writing any code.
|
|
27
|
-
|
|
28
|
-
### 3. Branch from main
|
|
29
|
-
|
|
30
|
-
Run: git checkout main && git pull && git checkout -b <branch-name>
|
|
31
|
-
|
|
32
|
-
Name the branch after the deliverable (e.g., feat/prompt-template).
|
|
33
|
-
Never branch from another feature branch.
|
|
34
|
-
|
|
35
|
-
### 4. Mark your deliverable in-progress
|
|
36
|
-
|
|
37
|
-
Call conductor_update_deliverable with status "in_progress".
|
|
38
|
-
|
|
39
|
-
### 5. Do the work
|
|
40
|
-
|
|
41
|
-
Implement the deliverable according to its acceptance criteria.
|
|
42
|
-
After each meaningful chunk of work, call conductor_log_built with
|
|
43
|
-
a plain English summary and the sessionId.
|
|
44
|
-
|
|
45
|
-
### 6. Self-check before submitting
|
|
46
|
-
|
|
47
|
-
Before creating a PR, run ALL of these checks:
|
|
48
|
-
|
|
49
|
-
1. npx tsc --noEmit (type check)
|
|
50
|
-
2. npm run build (build)
|
|
51
|
-
3. npm test (tests)
|
|
52
|
-
4. git status (working tree clean?)
|
|
53
|
-
5. Review your own code for:
|
|
54
|
-
- No \`any\` type assertions
|
|
55
|
-
- No console.log in production code
|
|
56
|
-
- No large files (>200 lines of code)
|
|
57
|
-
- No large functions (>50 lines)
|
|
58
|
-
- No unused imports
|
|
59
|
-
- No secrets or .env files staged
|
|
60
|
-
|
|
61
|
-
### 7. Retry on failure (up to 3 attempts)
|
|
62
|
-
|
|
63
|
-
If any check fails:
|
|
64
|
-
- Read the error output carefully
|
|
65
|
-
- Fix the issue
|
|
66
|
-
- Re-run the failing check
|
|
67
|
-
- Repeat up to 3 total attempts per check
|
|
68
|
-
|
|
69
|
-
If a check still fails after 3 attempts, go to the parking procedure (step 10).
|
|
70
|
-
|
|
71
|
-
### 8. Create a PR
|
|
72
|
-
|
|
73
|
-
When all checks pass:
|
|
74
|
-
- git add the relevant files (never use git add -A)
|
|
75
|
-
- git commit with a descriptive message
|
|
76
|
-
- git push -u origin <branch-name>
|
|
77
|
-
- gh pr create --title "<deliverable title>" --body "<summary>"
|
|
78
|
-
- Save the PR URL \u2014 you need it for the completion report
|
|
79
|
-
|
|
80
|
-
Never merge your own PR. Only create it.
|
|
81
|
-
|
|
82
|
-
### 9. Submit for review
|
|
83
|
-
|
|
84
|
-
Call conductor_update_deliverable with:
|
|
85
|
-
- status: "review"
|
|
86
|
-
- completionReport with all check results and the prUrl
|
|
87
|
-
|
|
88
|
-
Then call conductor_end_session with the sessionId.
|
|
89
|
-
|
|
90
|
-
### 10. Parking procedure (when stuck)
|
|
91
|
-
|
|
92
|
-
If you cannot complete the work:
|
|
93
|
-
- If it is a judgment call or ambiguous requirement:
|
|
94
|
-
Call conductor_log_decision with the options and reasoning.
|
|
95
|
-
- Call conductor_update_deliverable with status "parked".
|
|
96
|
-
- Call conductor_log_next describing what remains and why you stopped.
|
|
97
|
-
- Call conductor_end_session with the sessionId.
|
|
98
|
-
- Exit cleanly.
|
|
99
|
-
|
|
100
|
-
## Rules
|
|
101
|
-
|
|
102
|
-
- Follow all rules in CLAUDE.md (included above if present).
|
|
103
|
-
- Never merge your own PR.
|
|
104
|
-
- Never make silent decisions \u2014 log every judgment call via conductor_log_decision.
|
|
105
|
-
- If a subsequent deliverable exists in the same outcome, create a handoff
|
|
106
|
-
prompt via conductor_create_prompt before submitting for review.
|
|
107
|
-
- Keep commits atomic and messages descriptive.
|
|
108
|
-
- Do not install new dependencies without logging a decision.
|
|
109
|
-
- Do not modify files outside the scope of your deliverable.
|
|
110
|
-
- If you encounter merge conflicts, resolve them yourself.
|
|
111
|
-
- Work within the existing code style and patterns.
|
|
112
|
-
`.trim();
|
|
113
|
-
function assembleAutonomousPrompt(ctx) {
|
|
114
|
-
const parts = [];
|
|
115
|
-
if (ctx.claudeMd.length > 0) {
|
|
116
|
-
parts.push("# Project Rules (CLAUDE.md)\n\n" + ctx.claudeMd);
|
|
117
|
-
}
|
|
118
|
-
parts.push("# Autonomous Runner Instructions\n\n" + AUTONOMOUS_WORKFLOW);
|
|
119
|
-
parts.push(
|
|
120
|
-
`# Your Assignment
|
|
121
|
-
|
|
122
|
-
Project: ${ctx.projectName}
|
|
123
|
-
Deliverable: ${ctx.item.title}
|
|
124
|
-
Deliverable ID: ${ctx.item.entityId}
|
|
125
|
-
Initiative: ${ctx.item.initiativeTitle ?? "unknown"}
|
|
126
|
-
Priority: P${ctx.item.priority}
|
|
127
|
-
Action: ${ctx.item.action}
|
|
128
|
-
|
|
129
|
-
Start by calling conductor_session_brief with:
|
|
130
|
-
- projectRoot: "${ctx.projectName}"
|
|
131
|
-
- deliverableId: "${ctx.item.entityId}"
|
|
132
|
-
- agentRole: "implementation-engineer"
|
|
133
|
-
- agentType: "claude-code"`
|
|
134
|
-
);
|
|
135
|
-
return parts.join("\n\n---\n\n");
|
|
136
|
-
}
|
|
137
|
-
export {
|
|
138
|
-
assembleAutonomousPrompt
|
|
139
|
-
};
|
|
File without changes
|