@matthugh1/conductor-cli 0.1.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 +79 -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,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
assembleAutonomousPrompt
|
|
4
|
+
} from "./chunk-6AA726KG.js";
|
|
5
|
+
|
|
6
|
+
// ../../src/core/agent-spawner.ts
|
|
7
|
+
import { spawn } from "child_process";
|
|
8
|
+
import { readFile } from "fs/promises";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { createInterface } from "readline";
|
|
11
|
+
async function readClaudeMd(projectRoot) {
|
|
12
|
+
try {
|
|
13
|
+
return await readFile(join(projectRoot, "CLAUDE.md"), "utf8");
|
|
14
|
+
} catch {
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function parseStreamJsonLine(raw) {
|
|
19
|
+
let event;
|
|
20
|
+
try {
|
|
21
|
+
event = JSON.parse(raw);
|
|
22
|
+
} catch {
|
|
23
|
+
return raw;
|
|
24
|
+
}
|
|
25
|
+
const type = event.type;
|
|
26
|
+
if (type === "assistant") {
|
|
27
|
+
const msg = event.message;
|
|
28
|
+
const content = msg?.content;
|
|
29
|
+
if (!content) return null;
|
|
30
|
+
const parts = [];
|
|
31
|
+
for (const block of content) {
|
|
32
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
33
|
+
parts.push(block.text);
|
|
34
|
+
} else if (block.type === "tool_use") {
|
|
35
|
+
const name = block.name;
|
|
36
|
+
parts.push(`[tool] ${name}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return parts.length > 0 ? parts.join("\n") : null;
|
|
40
|
+
}
|
|
41
|
+
if (type === "tool_result") {
|
|
42
|
+
const name = event.tool_name ?? "tool";
|
|
43
|
+
const content = event.content;
|
|
44
|
+
if (content && content.length > 200) {
|
|
45
|
+
return `[${name}] ${content.slice(0, 200)}...`;
|
|
46
|
+
}
|
|
47
|
+
return content ? `[${name}] ${content}` : `[${name}] done`;
|
|
48
|
+
}
|
|
49
|
+
if (type === "result") {
|
|
50
|
+
const subtype = event.subtype;
|
|
51
|
+
const result = event.result;
|
|
52
|
+
if (subtype === "success" && result) {
|
|
53
|
+
return `[result] ${result.slice(0, 500)}`;
|
|
54
|
+
}
|
|
55
|
+
if (subtype === "error") {
|
|
56
|
+
return `[error] ${event.error ?? "unknown error"}`;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (type === "system" || type === "rate_limit_event") {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
function spawnAgent(opts) {
|
|
66
|
+
let child = null;
|
|
67
|
+
let cancelled = false;
|
|
68
|
+
let pid = null;
|
|
69
|
+
const cancel = () => {
|
|
70
|
+
cancelled = true;
|
|
71
|
+
if (child !== null && child.exitCode === null) {
|
|
72
|
+
child.kill("SIGTERM");
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const done = runAgent(opts, (c) => {
|
|
76
|
+
child = c;
|
|
77
|
+
pid = c.pid ?? null;
|
|
78
|
+
}, () => cancelled);
|
|
79
|
+
return { done, cancel, get pid() {
|
|
80
|
+
return pid;
|
|
81
|
+
} };
|
|
82
|
+
}
|
|
83
|
+
async function runAgent(opts, onChild, isCancelled) {
|
|
84
|
+
const startTime = Date.now();
|
|
85
|
+
let lineCount = 0;
|
|
86
|
+
let prompt;
|
|
87
|
+
if (opts.assembledPrompt) {
|
|
88
|
+
prompt = opts.assembledPrompt;
|
|
89
|
+
} else {
|
|
90
|
+
const claudeMd = await readClaudeMd(opts.projectRoot);
|
|
91
|
+
prompt = assembleAutonomousPrompt({
|
|
92
|
+
claudeMd,
|
|
93
|
+
projectName: opts.projectRoot,
|
|
94
|
+
item: opts.item
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const args = ["-p", prompt, "--output-format", "stream-json", "--verbose"];
|
|
98
|
+
if (opts.modelOverride) {
|
|
99
|
+
args.push("--model", opts.modelOverride);
|
|
100
|
+
}
|
|
101
|
+
if (opts.skipPermissions !== false) {
|
|
102
|
+
args.push("--dangerously-skip-permissions");
|
|
103
|
+
}
|
|
104
|
+
const child = spawn("claude", args, {
|
|
105
|
+
cwd: opts.projectRoot,
|
|
106
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
107
|
+
env: { ...process.env }
|
|
108
|
+
});
|
|
109
|
+
onChild(child);
|
|
110
|
+
if (child.pid === void 0) {
|
|
111
|
+
return {
|
|
112
|
+
outcome: "failed",
|
|
113
|
+
exitCode: null,
|
|
114
|
+
lineCount: 0,
|
|
115
|
+
durationMs: Date.now() - startTime,
|
|
116
|
+
errorMessage: "Failed to spawn claude process"
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (child.stdout !== null) {
|
|
120
|
+
const rl = createInterface({ input: child.stdout });
|
|
121
|
+
rl.on("line", (raw) => {
|
|
122
|
+
const text = parseStreamJsonLine(raw);
|
|
123
|
+
if (text === null) return;
|
|
124
|
+
lineCount++;
|
|
125
|
+
const currentLine = lineCount;
|
|
126
|
+
opts.onLine?.("stdout", text);
|
|
127
|
+
opts.client.appendOutputLine(opts.runId, {
|
|
128
|
+
lineNumber: currentLine,
|
|
129
|
+
stream: "stdout",
|
|
130
|
+
content: text
|
|
131
|
+
}).catch(() => {
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (child.stderr !== null) {
|
|
136
|
+
const rl = createInterface({ input: child.stderr });
|
|
137
|
+
rl.on("line", (line) => {
|
|
138
|
+
lineCount++;
|
|
139
|
+
const currentLine = lineCount;
|
|
140
|
+
opts.onLine?.("stderr", line);
|
|
141
|
+
opts.client.appendOutputLine(opts.runId, {
|
|
142
|
+
lineNumber: currentLine,
|
|
143
|
+
stream: "stderr",
|
|
144
|
+
content: line
|
|
145
|
+
}).catch(() => {
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const exitCode = await waitWithTimeout(
|
|
150
|
+
child,
|
|
151
|
+
opts.timeoutMs,
|
|
152
|
+
opts.client,
|
|
153
|
+
opts.projectId,
|
|
154
|
+
opts.deliverableId,
|
|
155
|
+
isCancelled
|
|
156
|
+
);
|
|
157
|
+
const durationMs = Date.now() - startTime;
|
|
158
|
+
if (isCancelled()) {
|
|
159
|
+
return { outcome: "cancelled", exitCode, lineCount, durationMs };
|
|
160
|
+
}
|
|
161
|
+
if (exitCode === null) {
|
|
162
|
+
child.kill("SIGTERM");
|
|
163
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
164
|
+
if (child.exitCode === null) {
|
|
165
|
+
child.kill("SIGKILL");
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
outcome: "timeout",
|
|
169
|
+
exitCode: null,
|
|
170
|
+
lineCount,
|
|
171
|
+
durationMs,
|
|
172
|
+
errorMessage: `Task timed out after ${Math.round(opts.timeoutMs / 1e3)}s`
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (exitCode === 0) {
|
|
176
|
+
return { outcome: "completed", exitCode, lineCount, durationMs };
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
outcome: "failed",
|
|
180
|
+
exitCode,
|
|
181
|
+
lineCount,
|
|
182
|
+
durationMs,
|
|
183
|
+
errorMessage: `Agent exited with code ${exitCode}`
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
async function waitWithTimeout(child, timeoutMs, client, projectId, deliverableId, isCancelled) {
|
|
187
|
+
return new Promise((resolve) => {
|
|
188
|
+
let resolved = false;
|
|
189
|
+
let elapsedMs = 0;
|
|
190
|
+
const TICK_MS = 3e3;
|
|
191
|
+
const finish = (code) => {
|
|
192
|
+
if (!resolved) {
|
|
193
|
+
resolved = true;
|
|
194
|
+
clearInterval(timer);
|
|
195
|
+
resolve(code);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
child.on("close", (code) => {
|
|
199
|
+
finish(code ?? 1);
|
|
200
|
+
});
|
|
201
|
+
child.on("error", () => {
|
|
202
|
+
finish(1);
|
|
203
|
+
});
|
|
204
|
+
const timer = setInterval(async () => {
|
|
205
|
+
if (resolved || isCancelled()) {
|
|
206
|
+
clearInterval(timer);
|
|
207
|
+
if (isCancelled() && !resolved) {
|
|
208
|
+
finish(null);
|
|
209
|
+
}
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (deliverableId) {
|
|
213
|
+
try {
|
|
214
|
+
const paused = await client.hasPendingDecisions(projectId, deliverableId);
|
|
215
|
+
if (!paused) {
|
|
216
|
+
elapsedMs += TICK_MS;
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
elapsedMs += TICK_MS;
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
elapsedMs += TICK_MS;
|
|
223
|
+
}
|
|
224
|
+
if (elapsedMs >= timeoutMs) {
|
|
225
|
+
finish(null);
|
|
226
|
+
}
|
|
227
|
+
}, TICK_MS);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
export {
|
|
231
|
+
spawnAgent
|
|
232
|
+
};
|
package/dist/agent.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ensureDatabaseUrl,
|
|
4
4
|
postToServer
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-B2WDTKD7.js";
|
|
6
6
|
|
|
7
7
|
// ../../src/cli/agent.ts
|
|
8
8
|
import { resolve } from "path";
|
|
@@ -18,6 +18,12 @@ function parseArgs(argv) {
|
|
|
18
18
|
let consecutiveFailures = 3;
|
|
19
19
|
let skipPermissions = false;
|
|
20
20
|
let dryRun = false;
|
|
21
|
+
let checkInterval = 3e4;
|
|
22
|
+
let maxPerDay = 50;
|
|
23
|
+
let maxConcurrent = 1;
|
|
24
|
+
let noWorktree = false;
|
|
25
|
+
let apiUrl;
|
|
26
|
+
let apiKey;
|
|
21
27
|
let command;
|
|
22
28
|
let subcommand;
|
|
23
29
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -36,6 +42,18 @@ function parseArgs(argv) {
|
|
|
36
42
|
skipPermissions = true;
|
|
37
43
|
} else if (arg === "--dry-run") {
|
|
38
44
|
dryRun = true;
|
|
45
|
+
} else if (arg === "--check-interval" && i + 1 < args.length) {
|
|
46
|
+
checkInterval = Number.parseInt(args[++i], 10);
|
|
47
|
+
} else if (arg === "--max-per-day" && i + 1 < args.length) {
|
|
48
|
+
maxPerDay = Number.parseInt(args[++i], 10);
|
|
49
|
+
} else if (arg === "--max-concurrent" && i + 1 < args.length) {
|
|
50
|
+
maxConcurrent = Number.parseInt(args[++i], 10);
|
|
51
|
+
} else if (arg === "--no-worktree") {
|
|
52
|
+
noWorktree = true;
|
|
53
|
+
} else if (arg === "--api-url" && i + 1 < args.length) {
|
|
54
|
+
apiUrl = args[++i];
|
|
55
|
+
} else if (arg === "--api-key" && i + 1 < args.length) {
|
|
56
|
+
apiKey = args[++i];
|
|
39
57
|
} else if (arg === "--json") {
|
|
40
58
|
json = true;
|
|
41
59
|
} else if (arg === "--help" || arg === "-h") {
|
|
@@ -48,7 +66,7 @@ function parseArgs(argv) {
|
|
|
48
66
|
subcommand = arg;
|
|
49
67
|
}
|
|
50
68
|
}
|
|
51
|
-
return { command, subcommand, projectRoot, project, json, help, once, maxDeliverables, timeout, consecutiveFailures, skipPermissions, dryRun };
|
|
69
|
+
return { command, subcommand, projectRoot, project, json, help, once, maxDeliverables, timeout, consecutiveFailures, skipPermissions, dryRun, checkInterval, maxPerDay, maxConcurrent, noWorktree, apiUrl, apiKey };
|
|
52
70
|
}
|
|
53
71
|
var HELP_TEXT = `
|
|
54
72
|
Conductor \u2014 local agent CLI
|
|
@@ -62,10 +80,12 @@ Commands:
|
|
|
62
80
|
hooks Check hook status or install hooks
|
|
63
81
|
watch Poll for cleanup tasks and execute them locally
|
|
64
82
|
run Autonomous runner \u2014 work through deliverable queue
|
|
83
|
+
daemon Long-running daemon \u2014 polls for autonomous work
|
|
84
|
+
daemon cancel Stop a running daemon
|
|
65
85
|
|
|
66
86
|
Options:
|
|
67
87
|
--project-root <path> Project directory (default: cwd)
|
|
68
|
-
--project <name> Project name (for run
|
|
88
|
+
--project <name> Project name (for run/daemon commands)
|
|
69
89
|
--json Output as JSON instead of plain English
|
|
70
90
|
--once Process one task and exit (watch only)
|
|
71
91
|
--help Show this help text
|
|
@@ -77,6 +97,15 @@ Run options:
|
|
|
77
97
|
--skip-permissions Allow Claude to write files without prompting
|
|
78
98
|
--dry-run Show queue without spawning agents
|
|
79
99
|
|
|
100
|
+
Daemon options:
|
|
101
|
+
--check-interval <ms> Poll interval in milliseconds (default: 30000)
|
|
102
|
+
--max-per-day <n> Max tasks per day (default: 50)
|
|
103
|
+
--max-concurrent <n> Max concurrent agent tasks (default: 1)
|
|
104
|
+
--timeout <minutes> Max minutes per deliverable (default: 120)
|
|
105
|
+
--consecutive-failures <n> Stop after N failures in a row (default: 3)
|
|
106
|
+
--no-worktree Skip git worktree isolation
|
|
107
|
+
--skip-permissions Allow Claude to write files without prompting
|
|
108
|
+
|
|
80
109
|
Examples:
|
|
81
110
|
conductor check
|
|
82
111
|
conductor init --project-root /path/to/project
|
|
@@ -85,6 +114,8 @@ Examples:
|
|
|
85
114
|
conductor watch
|
|
86
115
|
conductor run --project Conductor --skip-permissions
|
|
87
116
|
conductor run --project Conductor --dry-run
|
|
117
|
+
conductor daemon --project Conductor
|
|
118
|
+
conductor daemon cancel --project Conductor
|
|
88
119
|
`.trim();
|
|
89
120
|
function writeOut(text) {
|
|
90
121
|
process.stdout.write(text + "\n");
|
|
@@ -113,7 +144,7 @@ async function cmdCheck(projectRoot, jsonOutput) {
|
|
|
113
144
|
}
|
|
114
145
|
return 1;
|
|
115
146
|
}
|
|
116
|
-
const { getEnvironmentHealthReport } = await import("./health-
|
|
147
|
+
const { getEnvironmentHealthReport } = await import("./health-UFK7YCKQ.js");
|
|
117
148
|
const report = await getEnvironmentHealthReport(projects[0].id);
|
|
118
149
|
if (jsonOutput) {
|
|
119
150
|
writeOut(JSON.stringify(report, null, 2));
|
|
@@ -188,7 +219,7 @@ async function cmdCheck(projectRoot, jsonOutput) {
|
|
|
188
219
|
}
|
|
189
220
|
try {
|
|
190
221
|
const { getGitBranchInfo } = await import("./git-wrapper-DVJ46TMA.js");
|
|
191
|
-
const { getBranchOverview } = await import("./branch-overview-
|
|
222
|
+
const { getBranchOverview } = await import("./branch-overview-DSSCUE5F.js");
|
|
192
223
|
const { saveBranchOverviewSnapshot } = await import("./git-snapshots-N3FBS7T3.js");
|
|
193
224
|
const branchInfo = await getGitBranchInfo(projectRoot);
|
|
194
225
|
let currentBranch = branchInfo.branchName;
|
|
@@ -214,7 +245,7 @@ async function cmdCheck(projectRoot, jsonOutput) {
|
|
|
214
245
|
return 0;
|
|
215
246
|
}
|
|
216
247
|
async function cmdScan(projectRoot, jsonOutput) {
|
|
217
|
-
const { scanWorktrees } = await import("./worktree-manager-
|
|
248
|
+
const { scanWorktrees } = await import("./worktree-manager-2ZUJEL3L.js");
|
|
218
249
|
const { query: dbQuery } = await import("./db-U6Y3QJDD.js");
|
|
219
250
|
const projects = await dbQuery(
|
|
220
251
|
"SELECT id FROM projects WHERE path = $1 LIMIT 1",
|
|
@@ -262,7 +293,7 @@ async function cmdScan(projectRoot, jsonOutput) {
|
|
|
262
293
|
async function cmdInit(projectRoot, jsonOutput) {
|
|
263
294
|
const { resolve: resolvePath, basename, join } = await import("path");
|
|
264
295
|
const { realpath, mkdir, readFile, writeFile, access } = await import("fs/promises");
|
|
265
|
-
const { getServerBaseUrl } = await import("./cli-config-
|
|
296
|
+
const { getServerBaseUrl } = await import("./cli-config-2ZDXUUQN.js");
|
|
266
297
|
let root = resolvePath(projectRoot.trim());
|
|
267
298
|
try {
|
|
268
299
|
root = await realpath(root);
|
|
@@ -379,7 +410,7 @@ async function cmdHooks(projectRoot, subcommand, jsonOutput) {
|
|
|
379
410
|
return 2;
|
|
380
411
|
}
|
|
381
412
|
async function cmdWatch(projectRoot, once, jsonOutput) {
|
|
382
|
-
const { claimNextTask, executeTask, failStaleTasks } = await import("./cli-tasks-
|
|
413
|
+
const { claimNextTask, executeTask, failStaleTasks } = await import("./cli-tasks-NM5D5PIZ.js");
|
|
383
414
|
const { query: dbQuery } = await import("./db-U6Y3QJDD.js");
|
|
384
415
|
const projects = await dbQuery(
|
|
385
416
|
"SELECT id FROM projects WHERE path = $1 LIMIT 1",
|
|
@@ -464,8 +495,8 @@ async function cmdWatch(projectRoot, once, jsonOutput) {
|
|
|
464
495
|
async function cmdRun(opts) {
|
|
465
496
|
const { spawn } = await import("child_process");
|
|
466
497
|
const { readFileSync, existsSync } = await import("fs");
|
|
467
|
-
const { assembleAutonomousPrompt } = await import("./runner-prompt-
|
|
468
|
-
const { computeWorkQueue } = await import("./work-queue-
|
|
498
|
+
const { assembleAutonomousPrompt } = await import("./runner-prompt-MOOPKA5P.js");
|
|
499
|
+
const { computeWorkQueue } = await import("./work-queue-U3JYHLX2.js");
|
|
469
500
|
const { query: dbQuery } = await import("./db-U6Y3QJDD.js");
|
|
470
501
|
let project = opts.projectName ?? opts.projectRoot;
|
|
471
502
|
if (opts.projectName) {
|
|
@@ -500,6 +531,7 @@ async function cmdRun(opts) {
|
|
|
500
531
|
let timedOut = false;
|
|
501
532
|
const args = [];
|
|
502
533
|
if (opts.skipPermissions) args.push("--dangerously-skip-permissions");
|
|
534
|
+
args.push("--verbose");
|
|
503
535
|
args.push("-p", prompt);
|
|
504
536
|
const child = spawn("claude", args, {
|
|
505
537
|
stdio: ["ignore", "inherit", "inherit"],
|
|
@@ -646,12 +678,15 @@ async function main() {
|
|
|
646
678
|
writeOut(HELP_TEXT);
|
|
647
679
|
process.exit(parsed.help ? 0 : 1);
|
|
648
680
|
}
|
|
649
|
-
const
|
|
650
|
-
if (
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
681
|
+
const needsDb = parsed.command !== "daemon";
|
|
682
|
+
if (needsDb) {
|
|
683
|
+
const hasDb = await ensureDatabaseUrl();
|
|
684
|
+
if (!hasDb) {
|
|
685
|
+
writeErr(
|
|
686
|
+
"Could not find a database connection. Make sure the MCP server is running (npm run mcp) and try again."
|
|
687
|
+
);
|
|
688
|
+
process.exit(1);
|
|
689
|
+
}
|
|
655
690
|
}
|
|
656
691
|
try {
|
|
657
692
|
let exitCode;
|
|
@@ -691,6 +726,34 @@ async function main() {
|
|
|
691
726
|
jsonOutput: parsed.json
|
|
692
727
|
});
|
|
693
728
|
break;
|
|
729
|
+
case "daemon": {
|
|
730
|
+
const { cmdDaemon, cmdDaemonCancel } = await import("./daemon-GGOJDZDB.js");
|
|
731
|
+
if (parsed.subcommand === "cancel") {
|
|
732
|
+
exitCode = await cmdDaemonCancel(
|
|
733
|
+
parsed.projectRoot,
|
|
734
|
+
parsed.project,
|
|
735
|
+
parsed.json,
|
|
736
|
+
parsed.apiUrl,
|
|
737
|
+
parsed.apiKey
|
|
738
|
+
);
|
|
739
|
+
} else {
|
|
740
|
+
exitCode = await cmdDaemon({
|
|
741
|
+
projectRoot: parsed.projectRoot,
|
|
742
|
+
projectName: parsed.project,
|
|
743
|
+
apiUrl: parsed.apiUrl,
|
|
744
|
+
apiKey: parsed.apiKey,
|
|
745
|
+
checkInterval: parsed.checkInterval,
|
|
746
|
+
maxPerDay: parsed.maxPerDay,
|
|
747
|
+
maxConsecutiveFailures: parsed.consecutiveFailures,
|
|
748
|
+
timeout: parsed.timeout,
|
|
749
|
+
maxConcurrent: parsed.maxConcurrent,
|
|
750
|
+
noWorktree: parsed.noWorktree,
|
|
751
|
+
skipPermissions: parsed.skipPermissions,
|
|
752
|
+
jsonOutput: parsed.json
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
break;
|
|
756
|
+
}
|
|
694
757
|
default:
|
|
695
758
|
writeErr(`Unknown command: ${parsed.command}`);
|
|
696
759
|
writeErr("Run conductor --help for usage.");
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
query
|
|
4
|
+
} from "./chunk-PANC6BTV.js";
|
|
5
|
+
|
|
6
|
+
// ../../src/core/notifications.ts
|
|
7
|
+
var VALID_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
8
|
+
"gate_rejection",
|
|
9
|
+
"gate_bypassed",
|
|
10
|
+
"review_needed",
|
|
11
|
+
"handoff_needed",
|
|
12
|
+
"decision_pending",
|
|
13
|
+
"stale_session",
|
|
14
|
+
"abandoned_session",
|
|
15
|
+
"missing_checkin",
|
|
16
|
+
"orphan_work",
|
|
17
|
+
"stage_transition",
|
|
18
|
+
"watchdog_flag",
|
|
19
|
+
"stale_worktree",
|
|
20
|
+
"autonomous_flag_changed"
|
|
21
|
+
]);
|
|
22
|
+
var VALID_PRIORITIES = /* @__PURE__ */ new Set([
|
|
23
|
+
"info",
|
|
24
|
+
"warning",
|
|
25
|
+
"action_needed"
|
|
26
|
+
]);
|
|
27
|
+
var VALID_LINK_TYPES = /* @__PURE__ */ new Set([
|
|
28
|
+
"deliverable",
|
|
29
|
+
"decision",
|
|
30
|
+
"initiative",
|
|
31
|
+
"session"
|
|
32
|
+
]);
|
|
33
|
+
async function createNotification(params) {
|
|
34
|
+
if (!params.projectId || params.projectId.trim().length === 0) {
|
|
35
|
+
throw new Error("A project id is required to create a notification.");
|
|
36
|
+
}
|
|
37
|
+
if (!VALID_EVENT_TYPES.has(params.eventType)) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Unknown event type "${params.eventType}". Expected one of: ${[...VALID_EVENT_TYPES].join(", ")}.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (!params.message || params.message.trim().length === 0) {
|
|
43
|
+
throw new Error("A notification needs a message.");
|
|
44
|
+
}
|
|
45
|
+
if (!VALID_PRIORITIES.has(params.priority)) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Unknown priority "${params.priority}". Expected info, warning, or action_needed.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (params.linkType !== void 0 && !VALID_LINK_TYPES.has(params.linkType)) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Unknown link type "${params.linkType}". Expected deliverable, decision, initiative, or session.`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const rows = await query(
|
|
56
|
+
`INSERT INTO notifications (project_id, event_type, message, priority, link_type, link_id, agent_type, agent_name)
|
|
57
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
58
|
+
RETURNING id`,
|
|
59
|
+
[
|
|
60
|
+
params.projectId,
|
|
61
|
+
params.eventType,
|
|
62
|
+
params.message.trim(),
|
|
63
|
+
params.priority,
|
|
64
|
+
params.linkType ?? null,
|
|
65
|
+
params.linkId ?? null,
|
|
66
|
+
params.agentType ?? null,
|
|
67
|
+
params.agentName ?? null
|
|
68
|
+
]
|
|
69
|
+
);
|
|
70
|
+
return rows[0].id;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export {
|
|
74
|
+
createNotification
|
|
75
|
+
};
|