@matthugh1/conductor-cli 0.2.4 → 0.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/{agent-spawner-BNOGEYDK.js → agent-spawner-YCGFXFXX.js} +32 -6
- package/dist/agent.js +10 -10
- package/dist/{branch-overview-RRHX3XGY.js → branch-overview-35OJQU7T.js} +3 -109
- package/dist/chunk-HXPYK5E3.js +116 -0
- package/dist/chunk-T27CR6PR.js +179 -0
- package/dist/daemon-427UNQNA.js +614 -0
- package/dist/{daemon-client-CTYOJMJP.js → daemon-client-RVY2YLMA.js} +13 -0
- package/dist/{git-hooks-RQ6WJQS4.js → git-hooks-CD25TLQV.js} +112 -14
- package/dist/{runner-prompt-MOOPKA5P.js → runner-prompt-7EUGIDWK.js} +1 -1
- package/package.json +1 -1
- package/dist/chunk-6AA726KG.js +0 -238
- package/dist/daemon-ZJDZIP3R.js +0 -607
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
assembleAutonomousPrompt
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-T27CR6PR.js";
|
|
5
5
|
|
|
6
6
|
// ../../src/core/agent-spawner.ts
|
|
7
7
|
import { spawn } from "child_process";
|
|
@@ -62,6 +62,24 @@ function parseStreamJsonLine(raw) {
|
|
|
62
62
|
}
|
|
63
63
|
return null;
|
|
64
64
|
}
|
|
65
|
+
function extractTokenUsage(raw) {
|
|
66
|
+
let event;
|
|
67
|
+
try {
|
|
68
|
+
event = JSON.parse(raw);
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
if (event.type !== "result") return null;
|
|
73
|
+
const usage = event.usage;
|
|
74
|
+
if (!usage) return null;
|
|
75
|
+
return {
|
|
76
|
+
inputTokens: typeof usage.input_tokens === "number" ? usage.input_tokens : 0,
|
|
77
|
+
outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0,
|
|
78
|
+
cacheReadTokens: typeof usage.cache_read_input_tokens === "number" ? usage.cache_read_input_tokens : 0,
|
|
79
|
+
cacheCreationTokens: typeof usage.cache_creation_input_tokens === "number" ? usage.cache_creation_input_tokens : 0,
|
|
80
|
+
totalCostUsd: typeof event.total_cost_usd === "number" ? event.total_cost_usd : 0
|
|
81
|
+
};
|
|
82
|
+
}
|
|
65
83
|
function spawnAgent(opts) {
|
|
66
84
|
let child = null;
|
|
67
85
|
let cancelled = false;
|
|
@@ -83,6 +101,7 @@ function spawnAgent(opts) {
|
|
|
83
101
|
async function runAgent(opts, onChild, isCancelled) {
|
|
84
102
|
const startTime = Date.now();
|
|
85
103
|
let lineCount = 0;
|
|
104
|
+
let tokenUsage;
|
|
86
105
|
let prompt;
|
|
87
106
|
if (opts.assembledPrompt) {
|
|
88
107
|
prompt = opts.assembledPrompt;
|
|
@@ -91,7 +110,8 @@ async function runAgent(opts, onChild, isCancelled) {
|
|
|
91
110
|
prompt = assembleAutonomousPrompt({
|
|
92
111
|
claudeMd,
|
|
93
112
|
projectName: opts.projectRoot,
|
|
94
|
-
item: opts.item
|
|
113
|
+
item: opts.item,
|
|
114
|
+
agentSlug: opts.agentSlug
|
|
95
115
|
});
|
|
96
116
|
}
|
|
97
117
|
const args = ["-p", prompt, "--output-format", "stream-json", "--verbose"];
|
|
@@ -119,6 +139,8 @@ async function runAgent(opts, onChild, isCancelled) {
|
|
|
119
139
|
if (child.stdout !== null) {
|
|
120
140
|
const rl = createInterface({ input: child.stdout });
|
|
121
141
|
rl.on("line", (raw) => {
|
|
142
|
+
const usage = extractTokenUsage(raw);
|
|
143
|
+
if (usage) tokenUsage = usage;
|
|
122
144
|
const text = parseStreamJsonLine(raw);
|
|
123
145
|
if (text === null) return;
|
|
124
146
|
lineCount++;
|
|
@@ -156,7 +178,7 @@ async function runAgent(opts, onChild, isCancelled) {
|
|
|
156
178
|
);
|
|
157
179
|
const durationMs = Date.now() - startTime;
|
|
158
180
|
if (isCancelled()) {
|
|
159
|
-
return { outcome: "cancelled", exitCode, lineCount, durationMs };
|
|
181
|
+
return { outcome: "cancelled", exitCode, lineCount, durationMs, tokenUsage };
|
|
160
182
|
}
|
|
161
183
|
if (exitCode === null) {
|
|
162
184
|
child.kill("SIGTERM");
|
|
@@ -169,18 +191,20 @@ async function runAgent(opts, onChild, isCancelled) {
|
|
|
169
191
|
exitCode: null,
|
|
170
192
|
lineCount,
|
|
171
193
|
durationMs,
|
|
172
|
-
errorMessage: `Task timed out after ${Math.round(opts.timeoutMs / 1e3)}s
|
|
194
|
+
errorMessage: `Task timed out after ${Math.round(opts.timeoutMs / 1e3)}s`,
|
|
195
|
+
tokenUsage
|
|
173
196
|
};
|
|
174
197
|
}
|
|
175
198
|
if (exitCode === 0) {
|
|
176
|
-
return { outcome: "completed", exitCode, lineCount, durationMs };
|
|
199
|
+
return { outcome: "completed", exitCode, lineCount, durationMs, tokenUsage };
|
|
177
200
|
}
|
|
178
201
|
return {
|
|
179
202
|
outcome: "failed",
|
|
180
203
|
exitCode,
|
|
181
204
|
lineCount,
|
|
182
205
|
durationMs,
|
|
183
|
-
errorMessage: `Agent exited with code ${exitCode}
|
|
206
|
+
errorMessage: `Agent exited with code ${exitCode}`,
|
|
207
|
+
tokenUsage
|
|
184
208
|
};
|
|
185
209
|
}
|
|
186
210
|
async function waitWithTimeout(child, timeoutMs, client, projectId, deliverableId, isCancelled) {
|
|
@@ -228,5 +252,7 @@ async function waitWithTimeout(child, timeoutMs, client, projectId, deliverableI
|
|
|
228
252
|
});
|
|
229
253
|
}
|
|
230
254
|
export {
|
|
255
|
+
extractTokenUsage,
|
|
256
|
+
parseStreamJsonLine,
|
|
231
257
|
spawnAgent
|
|
232
258
|
};
|
package/dist/agent.js
CHANGED
|
@@ -120,7 +120,7 @@ function writeErr(text) {
|
|
|
120
120
|
process.stderr.write(text + "\n");
|
|
121
121
|
}
|
|
122
122
|
async function cmdCheck(projectRoot, jsonOutput) {
|
|
123
|
-
const { createDaemonClient } = await import("./daemon-client-
|
|
123
|
+
const { createDaemonClient } = await import("./daemon-client-RVY2YLMA.js");
|
|
124
124
|
const client = createDaemonClient();
|
|
125
125
|
const project = await client.resolveProjectByPath(projectRoot);
|
|
126
126
|
if (!project) {
|
|
@@ -159,7 +159,7 @@ async function cmdCheck(projectRoot, jsonOutput) {
|
|
|
159
159
|
}
|
|
160
160
|
try {
|
|
161
161
|
const { getGitBranchInfo, formatGitBranchLine, getStatusSummary, getRecentCommits, runGit } = await import("./git-wrapper-QRZYTYCZ.js");
|
|
162
|
-
const { getHookStatus } = await import("./git-hooks-
|
|
162
|
+
const { getHookStatus } = await import("./git-hooks-CD25TLQV.js");
|
|
163
163
|
const branchInfo = await getGitBranchInfo(projectRoot);
|
|
164
164
|
const branchLine = formatGitBranchLine(branchInfo);
|
|
165
165
|
const status = await getStatusSummary(projectRoot);
|
|
@@ -209,7 +209,7 @@ async function cmdCheck(projectRoot, jsonOutput) {
|
|
|
209
209
|
}
|
|
210
210
|
try {
|
|
211
211
|
const { getGitBranchInfo } = await import("./git-wrapper-QRZYTYCZ.js");
|
|
212
|
-
const { getBranchOverview } = await import("./branch-overview-
|
|
212
|
+
const { getBranchOverview } = await import("./branch-overview-35OJQU7T.js");
|
|
213
213
|
const branchInfo = await getGitBranchInfo(projectRoot);
|
|
214
214
|
let currentBranch = branchInfo.branchName;
|
|
215
215
|
if (currentBranch?.startsWith("detached (")) currentBranch = null;
|
|
@@ -279,7 +279,7 @@ async function runLocalHealthChecks(projectRoot) {
|
|
|
279
279
|
}
|
|
280
280
|
const apiStart = performance.now();
|
|
281
281
|
try {
|
|
282
|
-
const { createDaemonClient } = await import("./daemon-client-
|
|
282
|
+
const { createDaemonClient } = await import("./daemon-client-RVY2YLMA.js");
|
|
283
283
|
await createDaemonClient().resolveProjectByPath(projectRoot);
|
|
284
284
|
checks.push({ name: "API", status: "pass", message: "Conductor server reachable", durationMs: Math.round(performance.now() - apiStart) });
|
|
285
285
|
} catch {
|
|
@@ -296,7 +296,7 @@ async function runLocalHealthChecks(projectRoot) {
|
|
|
296
296
|
};
|
|
297
297
|
}
|
|
298
298
|
async function cmdScan(projectRoot, jsonOutput) {
|
|
299
|
-
const { createDaemonClient } = await import("./daemon-client-
|
|
299
|
+
const { createDaemonClient } = await import("./daemon-client-RVY2YLMA.js");
|
|
300
300
|
const client = createDaemonClient();
|
|
301
301
|
const project = await client.resolveProjectByPath(projectRoot);
|
|
302
302
|
if (!project) {
|
|
@@ -415,7 +415,7 @@ async function cmdInit(projectRoot, jsonOutput) {
|
|
|
415
415
|
return 0;
|
|
416
416
|
}
|
|
417
417
|
async function cmdHooks(projectRoot, subcommand, jsonOutput) {
|
|
418
|
-
const { getHookStatus, installHooks } = await import("./git-hooks-
|
|
418
|
+
const { getHookStatus, installHooks } = await import("./git-hooks-CD25TLQV.js");
|
|
419
419
|
if (subcommand === "install") {
|
|
420
420
|
const result = await installHooks(projectRoot);
|
|
421
421
|
if (jsonOutput) {
|
|
@@ -457,7 +457,7 @@ async function cmdHooks(projectRoot, subcommand, jsonOutput) {
|
|
|
457
457
|
return 2;
|
|
458
458
|
}
|
|
459
459
|
async function cmdWatch(projectRoot, once, jsonOutput) {
|
|
460
|
-
const { createDaemonClient } = await import("./daemon-client-
|
|
460
|
+
const { createDaemonClient } = await import("./daemon-client-RVY2YLMA.js");
|
|
461
461
|
const { runGit } = await import("./git-wrapper-QRZYTYCZ.js");
|
|
462
462
|
const client = createDaemonClient();
|
|
463
463
|
const project = await client.resolveProjectByPath(projectRoot);
|
|
@@ -583,8 +583,8 @@ async function cmdWatch(projectRoot, once, jsonOutput) {
|
|
|
583
583
|
async function cmdRun(opts) {
|
|
584
584
|
const { spawn } = await import("child_process");
|
|
585
585
|
const { readFileSync, existsSync } = await import("fs");
|
|
586
|
-
const { assembleAutonomousPrompt } = await import("./runner-prompt-
|
|
587
|
-
const { createDaemonClient } = await import("./daemon-client-
|
|
586
|
+
const { assembleAutonomousPrompt } = await import("./runner-prompt-7EUGIDWK.js");
|
|
587
|
+
const { createDaemonClient } = await import("./daemon-client-RVY2YLMA.js");
|
|
588
588
|
const client = createDaemonClient();
|
|
589
589
|
let project;
|
|
590
590
|
let projectId;
|
|
@@ -803,7 +803,7 @@ async function main() {
|
|
|
803
803
|
});
|
|
804
804
|
break;
|
|
805
805
|
case "daemon": {
|
|
806
|
-
const { cmdDaemon, cmdDaemonCancel } = await import("./daemon-
|
|
806
|
+
const { cmdDaemon, cmdDaemonCancel } = await import("./daemon-427UNQNA.js");
|
|
807
807
|
if (parsed.subcommand === "cancel") {
|
|
808
808
|
exitCode = await cmdDaemonCancel(
|
|
809
809
|
parsed.projectRoot,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
query
|
|
4
|
+
} from "./chunk-HXPYK5E3.js";
|
|
5
|
+
import {
|
|
4
6
|
runGit
|
|
5
7
|
} from "./chunk-N2KKNG4C.js";
|
|
6
8
|
|
|
@@ -69,114 +71,6 @@ function resolveDeliverableLink(branchName, hints, sessionByBranch) {
|
|
|
69
71
|
return null;
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
// ../../src/core/db.ts
|
|
73
|
-
import { Pool } from "pg";
|
|
74
|
-
var pool = null;
|
|
75
|
-
function getDatabaseUrl() {
|
|
76
|
-
const url = process.env.DATABASE_URL;
|
|
77
|
-
if (!url) {
|
|
78
|
-
throw new ConductorError(
|
|
79
|
-
"DB_UNREACHABLE" /* DB_UNREACHABLE */,
|
|
80
|
-
"DATABASE_URL is not set. Set it to your Supabase (or other Postgres) connection string."
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
return url;
|
|
84
|
-
}
|
|
85
|
-
function getPool() {
|
|
86
|
-
if (pool) {
|
|
87
|
-
return pool;
|
|
88
|
-
}
|
|
89
|
-
const connectionString = getDatabaseUrl();
|
|
90
|
-
const isRemote = connectionString.includes("supabase.co");
|
|
91
|
-
pool = new Pool({
|
|
92
|
-
connectionString,
|
|
93
|
-
max: 10,
|
|
94
|
-
connectionTimeoutMillis: 5e3,
|
|
95
|
-
idleTimeoutMillis: 3e4,
|
|
96
|
-
...isRemote ? { ssl: { rejectUnauthorized: false } } : {}
|
|
97
|
-
});
|
|
98
|
-
pool.on("error", () => {
|
|
99
|
-
console.error(
|
|
100
|
-
"Unexpected database error on an idle connection. Check that Postgres is running."
|
|
101
|
-
);
|
|
102
|
-
});
|
|
103
|
-
return pool;
|
|
104
|
-
}
|
|
105
|
-
var TRANSIENT_CODES = /* @__PURE__ */ new Set(["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT"]);
|
|
106
|
-
function isTransientConnectionError(err) {
|
|
107
|
-
const code = getErrorCode(err);
|
|
108
|
-
if (code && TRANSIENT_CODES.has(code)) return true;
|
|
109
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
110
|
-
return msg.includes("ECONNRESET") || msg.includes("ECONNREFUSED") || msg.includes("ETIMEDOUT") || msg.includes("connection terminated unexpectedly");
|
|
111
|
-
}
|
|
112
|
-
async function query(sql, params) {
|
|
113
|
-
const p = getPool();
|
|
114
|
-
try {
|
|
115
|
-
const result = await p.query(sql, params);
|
|
116
|
-
return result.rows;
|
|
117
|
-
} catch (err) {
|
|
118
|
-
if (isTransientConnectionError(err)) {
|
|
119
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
120
|
-
try {
|
|
121
|
-
const result = await p.query(sql, params);
|
|
122
|
-
return result.rows;
|
|
123
|
-
} catch (retryErr) {
|
|
124
|
-
throw mapDbError(retryErr);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
throw mapDbError(err);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
function getErrorCode(err) {
|
|
131
|
-
if (err !== null && typeof err === "object" && "code" in err) {
|
|
132
|
-
const code = err.code;
|
|
133
|
-
return typeof code === "string" ? code : void 0;
|
|
134
|
-
}
|
|
135
|
-
return void 0;
|
|
136
|
-
}
|
|
137
|
-
function getBestErrorMessage(err) {
|
|
138
|
-
if (err instanceof AggregateError && err.errors.length > 0) {
|
|
139
|
-
const first = err.errors[0];
|
|
140
|
-
if (first instanceof Error && first.message.trim().length > 0) {
|
|
141
|
-
return first.message;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
if (err instanceof Error && err.message.trim().length > 0) {
|
|
145
|
-
return err.message;
|
|
146
|
-
}
|
|
147
|
-
return String(err);
|
|
148
|
-
}
|
|
149
|
-
function mapDbError(err) {
|
|
150
|
-
const code = getErrorCode(err);
|
|
151
|
-
const message = getBestErrorMessage(err);
|
|
152
|
-
if (code === "ECONNREFUSED" || code === "ECONNRESET" || code === "ETIMEDOUT" || message.includes("ECONNREFUSED") || message.includes("ECONNRESET") || message.includes("ETIMEDOUT") || message.includes("connect ECONNREFUSED") || message.includes("connection terminated unexpectedly")) {
|
|
153
|
-
return new ConductorError(
|
|
154
|
-
"DB_UNREACHABLE" /* DB_UNREACHABLE */,
|
|
155
|
-
"Could not reach the database. Postgres may not be running on this machine.",
|
|
156
|
-
true
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
if (message.includes("password") || message.includes("authentication") || message.includes("28P01")) {
|
|
160
|
-
return new ConductorError(
|
|
161
|
-
"DB_QUERY_FAILED" /* DB_QUERY_FAILED */,
|
|
162
|
-
"Could not sign in to the database. Check your username and password."
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
if (message.includes("does not exist") && message.includes("database")) {
|
|
166
|
-
return new ConductorError(
|
|
167
|
-
"DB_QUERY_FAILED" /* DB_QUERY_FAILED */,
|
|
168
|
-
"That database does not exist yet. Create it first, then try again."
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
if (message.trim().length > 0) {
|
|
172
|
-
return new ConductorError("DB_QUERY_FAILED" /* DB_QUERY_FAILED */, message);
|
|
173
|
-
}
|
|
174
|
-
return new ConductorError(
|
|
175
|
-
"DB_QUERY_FAILED" /* DB_QUERY_FAILED */,
|
|
176
|
-
"Something went wrong while talking to the database."
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
74
|
// ../../src/core/branch-overview.ts
|
|
181
75
|
var STALE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
182
76
|
var MAX_COMMITS = 20;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ConductorError
|
|
4
|
+
} from "./chunk-N2KKNG4C.js";
|
|
5
|
+
|
|
6
|
+
// ../../src/core/db.ts
|
|
7
|
+
import { Pool } from "pg";
|
|
8
|
+
var pool = null;
|
|
9
|
+
function getDatabaseUrl() {
|
|
10
|
+
const url = process.env.DATABASE_URL;
|
|
11
|
+
if (!url) {
|
|
12
|
+
throw new ConductorError(
|
|
13
|
+
"DB_UNREACHABLE" /* DB_UNREACHABLE */,
|
|
14
|
+
"DATABASE_URL is not set. Set it to your Supabase (or other Postgres) connection string."
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
return url;
|
|
18
|
+
}
|
|
19
|
+
function getPool() {
|
|
20
|
+
if (pool) {
|
|
21
|
+
return pool;
|
|
22
|
+
}
|
|
23
|
+
const connectionString = getDatabaseUrl();
|
|
24
|
+
const isRemote = connectionString.includes("supabase.co");
|
|
25
|
+
pool = new Pool({
|
|
26
|
+
connectionString,
|
|
27
|
+
max: 10,
|
|
28
|
+
connectionTimeoutMillis: 5e3,
|
|
29
|
+
idleTimeoutMillis: 3e4,
|
|
30
|
+
...isRemote ? { ssl: { rejectUnauthorized: true } } : {}
|
|
31
|
+
});
|
|
32
|
+
pool.on("error", () => {
|
|
33
|
+
console.error(
|
|
34
|
+
"Unexpected database error on an idle connection. Check that Postgres is running."
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
return pool;
|
|
38
|
+
}
|
|
39
|
+
var TRANSIENT_CODES = /* @__PURE__ */ new Set(["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT"]);
|
|
40
|
+
function isTransientConnectionError(err) {
|
|
41
|
+
const code = getErrorCode(err);
|
|
42
|
+
if (code && TRANSIENT_CODES.has(code)) return true;
|
|
43
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
44
|
+
return msg.includes("ECONNRESET") || msg.includes("ECONNREFUSED") || msg.includes("ETIMEDOUT") || msg.includes("connection terminated unexpectedly");
|
|
45
|
+
}
|
|
46
|
+
async function query(sql, params) {
|
|
47
|
+
const p = getPool();
|
|
48
|
+
try {
|
|
49
|
+
const result = await p.query(sql, params);
|
|
50
|
+
return result.rows;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (isTransientConnectionError(err)) {
|
|
53
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
54
|
+
try {
|
|
55
|
+
const result = await p.query(sql, params);
|
|
56
|
+
return result.rows;
|
|
57
|
+
} catch (retryErr) {
|
|
58
|
+
throw mapDbError(retryErr);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
throw mapDbError(err);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function getErrorCode(err) {
|
|
65
|
+
if (err !== null && typeof err === "object" && "code" in err) {
|
|
66
|
+
const code = err.code;
|
|
67
|
+
return typeof code === "string" ? code : void 0;
|
|
68
|
+
}
|
|
69
|
+
return void 0;
|
|
70
|
+
}
|
|
71
|
+
function getBestErrorMessage(err) {
|
|
72
|
+
if (err instanceof AggregateError && err.errors.length > 0) {
|
|
73
|
+
const first = err.errors[0];
|
|
74
|
+
if (first instanceof Error && first.message.trim().length > 0) {
|
|
75
|
+
return first.message;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (err instanceof Error && err.message.trim().length > 0) {
|
|
79
|
+
return err.message;
|
|
80
|
+
}
|
|
81
|
+
return String(err);
|
|
82
|
+
}
|
|
83
|
+
function mapDbError(err) {
|
|
84
|
+
const code = getErrorCode(err);
|
|
85
|
+
const message = getBestErrorMessage(err);
|
|
86
|
+
if (code === "ECONNREFUSED" || code === "ECONNRESET" || code === "ETIMEDOUT" || message.includes("ECONNREFUSED") || message.includes("ECONNRESET") || message.includes("ETIMEDOUT") || message.includes("connect ECONNREFUSED") || message.includes("connection terminated unexpectedly")) {
|
|
87
|
+
return new ConductorError(
|
|
88
|
+
"DB_UNREACHABLE" /* DB_UNREACHABLE */,
|
|
89
|
+
"Could not reach the database. Postgres may not be running on this machine.",
|
|
90
|
+
true
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (message.includes("password") || message.includes("authentication") || message.includes("28P01")) {
|
|
94
|
+
return new ConductorError(
|
|
95
|
+
"DB_QUERY_FAILED" /* DB_QUERY_FAILED */,
|
|
96
|
+
"Could not sign in to the database. Check your username and password."
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
if (message.includes("does not exist") && message.includes("database")) {
|
|
100
|
+
return new ConductorError(
|
|
101
|
+
"DB_QUERY_FAILED" /* DB_QUERY_FAILED */,
|
|
102
|
+
"That database does not exist yet. Create it first, then try again."
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
if (message.trim().length > 0) {
|
|
106
|
+
return new ConductorError("DB_QUERY_FAILED" /* DB_QUERY_FAILED */, message);
|
|
107
|
+
}
|
|
108
|
+
return new ConductorError(
|
|
109
|
+
"DB_QUERY_FAILED" /* DB_QUERY_FAILED */,
|
|
110
|
+
"Something went wrong while talking to the database."
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
query
|
|
116
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
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
|
+
When you need human input, use conductor_log_decision followed by
|
|
9
|
+
conductor_await_decision \u2014 this blocks until the human responds in the
|
|
10
|
+
Conductor panel. Work carefully and methodically.
|
|
11
|
+
|
|
12
|
+
## Workflow (follow ALL phases \u2014 do not exit early)
|
|
13
|
+
|
|
14
|
+
IMPORTANT: You must complete ALL 5 phases. Exiting after Phase 2 (Build) is the most common failure mode. Phases 3-5 are mandatory \u2014 your work is not done until conductor_end_session is called.
|
|
15
|
+
|
|
16
|
+
### Phase 1: Start
|
|
17
|
+
- Call conductor_session_brief (args in your assignment below). Save the sessionId.
|
|
18
|
+
- Read your assignment and acceptance criteria from the brief.
|
|
19
|
+
- Branch from origin/dev: git fetch origin && git checkout -b <branch> origin/dev
|
|
20
|
+
- Call conductor_update_deliverable with status: "in_progress"
|
|
21
|
+
|
|
22
|
+
### Phase 2: Build
|
|
23
|
+
- Implement the deliverable according to acceptance criteria.
|
|
24
|
+
- After each meaningful chunk, call conductor_log_built with a summary and sessionId.
|
|
25
|
+
- If you need a human decision, use conductor_log_decision \u2192 conductor_await_decision.
|
|
26
|
+
This is the ONLY way the human knows you need input \u2014 they cannot see your text output.
|
|
27
|
+
|
|
28
|
+
### Phase 3: Verify
|
|
29
|
+
- Run: npx tsc --noEmit, npm run build, npm test
|
|
30
|
+
- Review your own code: no \`any\` types, no console.log, no large files (>200 LOC), no secrets
|
|
31
|
+
- If a check fails, fix and retry (up to 3 attempts). If still failing after 3, go to Phase 5 parking.
|
|
32
|
+
|
|
33
|
+
### Phase 4: Ship
|
|
34
|
+
- git add relevant files (never git add -A), commit with descriptive message
|
|
35
|
+
- git push -u origin <branch>
|
|
36
|
+
- gh pr create --title "<deliverable title>" --body "<summary>" \u2014 save the PR URL
|
|
37
|
+
- Merge to dev (not main): git fetch origin && git checkout -b _dev-merge origin/dev && git merge --no-ff <branch> -m "chore: merge <branch> to dev" && git push origin HEAD:dev && git checkout <branch> && git branch -D _dev-merge
|
|
38
|
+
|
|
39
|
+
### Phase 5: Close
|
|
40
|
+
- Call conductor_update_deliverable with status: "review" and completionReport (include prUrl and all check results)
|
|
41
|
+
- Call conductor_end_session with sessionId
|
|
42
|
+
- If stuck and cannot complete: call conductor_update_deliverable with status: "parked", then conductor_log_next, then conductor_end_session
|
|
43
|
+
|
|
44
|
+
## Rules
|
|
45
|
+
|
|
46
|
+
- Follow all rules in CLAUDE.md (included above if present).
|
|
47
|
+
- Never merge your own PR to main \u2014 only create it. Always merge to dev after creating the PR.
|
|
48
|
+
- Never make silent decisions \u2014 log every judgment call via conductor_log_decision.
|
|
49
|
+
- If a subsequent deliverable exists in the same outcome, create a handoff
|
|
50
|
+
prompt via conductor_create_prompt before submitting for review.
|
|
51
|
+
- Keep commits atomic and messages descriptive.
|
|
52
|
+
- Do not install new dependencies without logging a decision.
|
|
53
|
+
- Do not modify files outside the scope of your deliverable.
|
|
54
|
+
- If you encounter merge conflicts, resolve them yourself.
|
|
55
|
+
- Work within the existing code style and patterns.
|
|
56
|
+
`.trim();
|
|
57
|
+
var EXIT_CHECKLIST = `
|
|
58
|
+
## Before you exit (REQUIRED)
|
|
59
|
+
|
|
60
|
+
Do NOT terminate until every item is done:
|
|
61
|
+
\u25A1 conductor_log_built called at least once with sessionId
|
|
62
|
+
\u25A1 All checks passed (tsc --noEmit, build, test, git status clean)
|
|
63
|
+
\u25A1 PR created via gh pr create \u2014 URL saved
|
|
64
|
+
\u25A1 Branch merged to dev
|
|
65
|
+
\u25A1 conductor_update_deliverable called with status: "review" and completionReport including prUrl
|
|
66
|
+
\u25A1 conductor_end_session called with sessionId
|
|
67
|
+
|
|
68
|
+
If you exit without completing these steps, your work will be flagged as incomplete and the deliverable will NOT move to review.
|
|
69
|
+
`.trim();
|
|
70
|
+
var FREEFORM_TASK_WORKFLOW = `
|
|
71
|
+
## Identity
|
|
72
|
+
|
|
73
|
+
You are an agent spawned by Conductor's daemon to execute a task.
|
|
74
|
+
When you need human input, use conductor_log_decision followed by
|
|
75
|
+
conductor_await_decision \u2014 this blocks until the human responds in the
|
|
76
|
+
Conductor panel. Work carefully and methodically.
|
|
77
|
+
|
|
78
|
+
## Workflow
|
|
79
|
+
|
|
80
|
+
1. Read the task description below carefully.
|
|
81
|
+
2. Execute the task as described.
|
|
82
|
+
3. If you hit a judgment call or blocker, log a decision via conductor_log_decision
|
|
83
|
+
and wait via conductor_await_decision. Do not proceed without resolution.
|
|
84
|
+
4. When finished, report what you accomplished clearly.
|
|
85
|
+
|
|
86
|
+
## Rules
|
|
87
|
+
|
|
88
|
+
- Follow all rules in CLAUDE.md (included above if present).
|
|
89
|
+
- Never make silent decisions \u2014 log every judgment call.
|
|
90
|
+
- Work within the existing code style and patterns.
|
|
91
|
+
- Do not install new dependencies without logging a decision.
|
|
92
|
+
`.trim();
|
|
93
|
+
function assembleAutonomousPrompt(ctx) {
|
|
94
|
+
const parts = [];
|
|
95
|
+
if (ctx.claudeMd.length > 0) {
|
|
96
|
+
parts.push("# Project Rules (CLAUDE.md)\n\n" + ctx.claudeMd);
|
|
97
|
+
}
|
|
98
|
+
parts.push("# Autonomous Runner Instructions\n\n" + AUTONOMOUS_WORKFLOW);
|
|
99
|
+
const briefArgs = [
|
|
100
|
+
`- projectRoot: "${ctx.projectName}"`,
|
|
101
|
+
`- deliverableId: "${ctx.item.entityId}"`
|
|
102
|
+
];
|
|
103
|
+
if (ctx.agentSlug) {
|
|
104
|
+
briefArgs.push(`- agentSlug: "${ctx.agentSlug}"`);
|
|
105
|
+
} else {
|
|
106
|
+
briefArgs.push(`- agentRole: "implementation-engineer"`);
|
|
107
|
+
}
|
|
108
|
+
briefArgs.push(`- agentType: "claude-code"`);
|
|
109
|
+
parts.push(
|
|
110
|
+
`# Your Assignment
|
|
111
|
+
|
|
112
|
+
Project: ${ctx.projectName}
|
|
113
|
+
Deliverable: ${ctx.item.title}
|
|
114
|
+
Deliverable ID: ${ctx.item.entityId}
|
|
115
|
+
Initiative: ${ctx.item.initiativeTitle ?? "unknown"}
|
|
116
|
+
Priority: P${ctx.item.priority}
|
|
117
|
+
Action: ${ctx.item.action}
|
|
118
|
+
|
|
119
|
+
Start by calling conductor_session_brief with:
|
|
120
|
+
` + briefArgs.join("\n")
|
|
121
|
+
);
|
|
122
|
+
parts.push(EXIT_CHECKLIST);
|
|
123
|
+
return parts.join("\n\n---\n\n");
|
|
124
|
+
}
|
|
125
|
+
function assembleTaskPrompt(agent, task, claudeMd) {
|
|
126
|
+
const parts = [];
|
|
127
|
+
if (claudeMd.length > 0) {
|
|
128
|
+
parts.push("# Project Rules (CLAUDE.md)\n\n" + claudeMd);
|
|
129
|
+
}
|
|
130
|
+
if (agent.systemPrompt.length > 0) {
|
|
131
|
+
parts.push(
|
|
132
|
+
`# Agent: ${agent.name}
|
|
133
|
+
|
|
134
|
+
## System Prompt
|
|
135
|
+
|
|
136
|
+
` + agent.systemPrompt
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (task.deliverableId) {
|
|
140
|
+
parts.push("# Autonomous Runner Instructions\n\n" + AUTONOMOUS_WORKFLOW);
|
|
141
|
+
} else {
|
|
142
|
+
parts.push("# Task Instructions\n\n" + FREEFORM_TASK_WORKFLOW);
|
|
143
|
+
}
|
|
144
|
+
const assignmentLines = [
|
|
145
|
+
`# Your Assignment`,
|
|
146
|
+
``,
|
|
147
|
+
`Task: ${task.title}`,
|
|
148
|
+
`Project: ${task.projectName}`
|
|
149
|
+
];
|
|
150
|
+
if (task.deliverableId) {
|
|
151
|
+
assignmentLines.push(`Deliverable ID: ${task.deliverableId}`);
|
|
152
|
+
assignmentLines.push(``);
|
|
153
|
+
assignmentLines.push(`Start by calling conductor_session_brief with:`);
|
|
154
|
+
assignmentLines.push(`- projectRoot: "${task.projectName}"`);
|
|
155
|
+
assignmentLines.push(`- deliverableId: "${task.deliverableId}"`);
|
|
156
|
+
if (agent.agentSlug) {
|
|
157
|
+
assignmentLines.push(`- agentSlug: "${agent.agentSlug}"`);
|
|
158
|
+
} else {
|
|
159
|
+
assignmentLines.push(`- agentRole: "implementation-engineer"`);
|
|
160
|
+
}
|
|
161
|
+
assignmentLines.push(`- agentType: "claude-code"`);
|
|
162
|
+
}
|
|
163
|
+
if (task.prompt.length > 0) {
|
|
164
|
+
assignmentLines.push(``);
|
|
165
|
+
assignmentLines.push(`## Task Details`);
|
|
166
|
+
assignmentLines.push(``);
|
|
167
|
+
assignmentLines.push(task.prompt);
|
|
168
|
+
}
|
|
169
|
+
parts.push(assignmentLines.join("\n"));
|
|
170
|
+
if (task.deliverableId) {
|
|
171
|
+
parts.push(EXIT_CHECKLIST);
|
|
172
|
+
}
|
|
173
|
+
return parts.join("\n\n---\n\n");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export {
|
|
177
|
+
assembleAutonomousPrompt,
|
|
178
|
+
assembleTaskPrompt
|
|
179
|
+
};
|