@jaggerxtrm/specialists 3.3.2 → 3.3.4
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/index.js +340 -279
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8531,7 +8531,6 @@ var require_limitLength = __commonJS((exports) => {
|
|
|
8531
8531
|
var require_pattern = __commonJS((exports) => {
|
|
8532
8532
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8533
8533
|
var code_1 = require_code2();
|
|
8534
|
-
var util_1 = require_util();
|
|
8535
8534
|
var codegen_1 = require_codegen();
|
|
8536
8535
|
var error2 = {
|
|
8537
8536
|
message: ({ schemaCode }) => (0, codegen_1.str)`must match pattern "${schemaCode}"`,
|
|
@@ -8544,18 +8543,10 @@ var require_pattern = __commonJS((exports) => {
|
|
|
8544
8543
|
$data: true,
|
|
8545
8544
|
error: error2,
|
|
8546
8545
|
code(cxt) {
|
|
8547
|
-
const {
|
|
8546
|
+
const { data, $data, schema, schemaCode, it } = cxt;
|
|
8548
8547
|
const u = it.opts.unicodeRegExp ? "u" : "";
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
const regExpCode = regExp.code === "new RegExp" ? (0, codegen_1._)`new RegExp` : (0, util_1.useFunc)(gen, regExp);
|
|
8552
|
-
const valid = gen.let("valid");
|
|
8553
|
-
gen.try(() => gen.assign(valid, (0, codegen_1._)`${regExpCode}(${schemaCode}, ${u}).test(${data})`), () => gen.assign(valid, false));
|
|
8554
|
-
cxt.fail$data((0, codegen_1._)`!${valid}`);
|
|
8555
|
-
} else {
|
|
8556
|
-
const regExp = (0, code_1.usePattern)(cxt, schema);
|
|
8557
|
-
cxt.fail$data((0, codegen_1._)`!${regExp}.test(${data})`);
|
|
8558
|
-
}
|
|
8548
|
+
const regExp = $data ? (0, codegen_1._)`(new RegExp(${schemaCode}, ${u}))` : (0, code_1.usePattern)(cxt, schema);
|
|
8549
|
+
cxt.fail$data((0, codegen_1._)`!${regExp}.test(${data})`);
|
|
8559
8550
|
}
|
|
8560
8551
|
};
|
|
8561
8552
|
exports.default = def;
|
|
@@ -17897,7 +17888,17 @@ class PiAgentSession {
|
|
|
17897
17888
|
if (this._killed)
|
|
17898
17889
|
return;
|
|
17899
17890
|
this.proc?.stdin?.end();
|
|
17900
|
-
|
|
17891
|
+
if (this.proc) {
|
|
17892
|
+
await new Promise((resolve) => {
|
|
17893
|
+
this.proc.on("close", () => resolve());
|
|
17894
|
+
setTimeout(() => {
|
|
17895
|
+
if (this.proc && !this._killed) {
|
|
17896
|
+
this.proc.kill();
|
|
17897
|
+
}
|
|
17898
|
+
resolve();
|
|
17899
|
+
}, 2000);
|
|
17900
|
+
});
|
|
17901
|
+
}
|
|
17901
17902
|
}
|
|
17902
17903
|
kill() {
|
|
17903
17904
|
if (this._killed)
|
|
@@ -18276,6 +18277,7 @@ ${preScripts.map((s) => ` • ${s.run}${s.inject_output ? " → $pre_script_o
|
|
|
18276
18277
|
let sessionBackend = model;
|
|
18277
18278
|
let session;
|
|
18278
18279
|
let keepAliveActive = false;
|
|
18280
|
+
let sessionClosed = false;
|
|
18279
18281
|
try {
|
|
18280
18282
|
session = await this.sessionFactory({
|
|
18281
18283
|
model,
|
|
@@ -18314,6 +18316,7 @@ ${preScripts.map((s) => ` • ${s.run}${s.inject_output ? " → $pre_script_o
|
|
|
18314
18316
|
onResumeReady(resumeFn, closeFn);
|
|
18315
18317
|
} else {
|
|
18316
18318
|
await session.close();
|
|
18319
|
+
sessionClosed = true;
|
|
18317
18320
|
}
|
|
18318
18321
|
const postScripts = spec.specialist.skills?.scripts?.filter((s) => s.phase === "post") ?? [];
|
|
18319
18322
|
for (const script of postScripts)
|
|
@@ -18338,7 +18341,7 @@ ${preScripts.map((s) => ` • ${s.run}${s.inject_output ? " → $pre_script_o
|
|
|
18338
18341
|
});
|
|
18339
18342
|
throw err;
|
|
18340
18343
|
} finally {
|
|
18341
|
-
if (!keepAliveActive) {
|
|
18344
|
+
if (!keepAliveActive && !sessionClosed) {
|
|
18342
18345
|
session?.kill();
|
|
18343
18346
|
}
|
|
18344
18347
|
}
|
|
@@ -18571,6 +18574,7 @@ var init_timeline_events = __esm(() => {
|
|
|
18571
18574
|
import {
|
|
18572
18575
|
closeSync,
|
|
18573
18576
|
existsSync as existsSync4,
|
|
18577
|
+
fsyncSync,
|
|
18574
18578
|
mkdirSync,
|
|
18575
18579
|
openSync,
|
|
18576
18580
|
readdirSync,
|
|
@@ -18727,14 +18731,19 @@ class Supervisor {
|
|
|
18727
18731
|
try {
|
|
18728
18732
|
writeSync(eventsFd, JSON.stringify(event) + `
|
|
18729
18733
|
`);
|
|
18730
|
-
} catch {
|
|
18734
|
+
} catch (err) {
|
|
18735
|
+
console.error(`[supervisor] Failed to write event: ${err?.message ?? err}`);
|
|
18736
|
+
}
|
|
18731
18737
|
};
|
|
18732
18738
|
appendTimelineEvent(createRunStartEvent(runOptions.name));
|
|
18733
18739
|
const fifoPath = join3(dir, "steer.pipe");
|
|
18734
|
-
|
|
18735
|
-
|
|
18736
|
-
|
|
18737
|
-
|
|
18740
|
+
const needsFifo = runOptions.keepAlive;
|
|
18741
|
+
if (needsFifo) {
|
|
18742
|
+
try {
|
|
18743
|
+
execFileSync("mkfifo", [fifoPath]);
|
|
18744
|
+
this.updateStatus(id, { fifo_path: fifoPath });
|
|
18745
|
+
} catch {}
|
|
18746
|
+
}
|
|
18738
18747
|
let textLogged = false;
|
|
18739
18748
|
let currentTool = "";
|
|
18740
18749
|
let currentToolCallId = "";
|
|
@@ -18742,6 +18751,8 @@ class Supervisor {
|
|
|
18742
18751
|
let steerFn;
|
|
18743
18752
|
let resumeFn;
|
|
18744
18753
|
let closeFn;
|
|
18754
|
+
let fifoReadStream;
|
|
18755
|
+
let fifoReadline;
|
|
18745
18756
|
const sigtermHandler = () => killFn?.();
|
|
18746
18757
|
process.once("SIGTERM", sigtermHandler);
|
|
18747
18758
|
try {
|
|
@@ -18751,6 +18762,7 @@ class Supervisor {
|
|
|
18751
18762
|
currentTool = toolMatch[1];
|
|
18752
18763
|
this.updateStatus(id, { current_tool: currentTool });
|
|
18753
18764
|
}
|
|
18765
|
+
this.opts.onProgress?.(delta);
|
|
18754
18766
|
}, (eventType) => {
|
|
18755
18767
|
const now = Date.now();
|
|
18756
18768
|
this.updateStatus(id, {
|
|
@@ -18772,41 +18784,43 @@ class Supervisor {
|
|
|
18772
18784
|
}, (meta) => {
|
|
18773
18785
|
this.updateStatus(id, { model: meta.model, backend: meta.backend });
|
|
18774
18786
|
appendTimelineEvent(createMetaEvent(meta.model, meta.backend));
|
|
18787
|
+
this.opts.onMeta?.(meta);
|
|
18775
18788
|
}, (fn) => {
|
|
18776
18789
|
killFn = fn;
|
|
18777
18790
|
}, (beadId) => {
|
|
18778
18791
|
this.updateStatus(id, { bead_id: beadId });
|
|
18779
18792
|
}, (fn) => {
|
|
18780
18793
|
steerFn = fn;
|
|
18781
|
-
if (existsSync4(fifoPath))
|
|
18782
|
-
|
|
18783
|
-
|
|
18784
|
-
|
|
18785
|
-
|
|
18786
|
-
|
|
18787
|
-
|
|
18788
|
-
|
|
18789
|
-
|
|
18790
|
-
|
|
18791
|
-
|
|
18792
|
-
|
|
18793
|
-
|
|
18794
|
-
|
|
18795
|
-
|
|
18796
|
-
|
|
18797
|
-
|
|
18798
|
-
|
|
18799
|
-
|
|
18800
|
-
this.updateStatus(id, { status: "error", error: err?.message ?? String(err) });
|
|
18794
|
+
if (!needsFifo || !existsSync4(fifoPath))
|
|
18795
|
+
return;
|
|
18796
|
+
fifoReadStream = createReadStream(fifoPath, { flags: "r+" });
|
|
18797
|
+
fifoReadline = createInterface({ input: fifoReadStream });
|
|
18798
|
+
fifoReadline.on("line", (line) => {
|
|
18799
|
+
try {
|
|
18800
|
+
const parsed = JSON.parse(line);
|
|
18801
|
+
if (parsed?.type === "steer" && typeof parsed.message === "string") {
|
|
18802
|
+
steerFn?.(parsed.message).catch(() => {});
|
|
18803
|
+
} else if (parsed?.type === "prompt" && typeof parsed.message === "string") {
|
|
18804
|
+
if (resumeFn) {
|
|
18805
|
+
this.updateStatus(id, { status: "running", current_event: "starting" });
|
|
18806
|
+
resumeFn(parsed.message).then((output) => {
|
|
18807
|
+
writeFileSync(this.resultPath(id), output, "utf-8");
|
|
18808
|
+
this.updateStatus(id, {
|
|
18809
|
+
status: "waiting",
|
|
18810
|
+
current_event: "waiting",
|
|
18811
|
+
elapsed_s: Math.round((Date.now() - startedAtMs) / 1000),
|
|
18812
|
+
last_event_at_ms: Date.now()
|
|
18801
18813
|
});
|
|
18802
|
-
}
|
|
18803
|
-
|
|
18804
|
-
|
|
18814
|
+
}).catch((err) => {
|
|
18815
|
+
this.updateStatus(id, { status: "error", error: err?.message ?? String(err) });
|
|
18816
|
+
});
|
|
18805
18817
|
}
|
|
18806
|
-
}
|
|
18807
|
-
|
|
18808
|
-
|
|
18809
|
-
|
|
18818
|
+
} else if (parsed?.type === "close") {
|
|
18819
|
+
closeFn?.().catch(() => {});
|
|
18820
|
+
}
|
|
18821
|
+
} catch {}
|
|
18822
|
+
});
|
|
18823
|
+
fifoReadline.on("error", () => {});
|
|
18810
18824
|
}, (rFn, cFn) => {
|
|
18811
18825
|
resumeFn = rFn;
|
|
18812
18826
|
closeFn = cFn;
|
|
@@ -18846,6 +18860,15 @@ class Supervisor {
|
|
|
18846
18860
|
throw err;
|
|
18847
18861
|
} finally {
|
|
18848
18862
|
process.removeListener("SIGTERM", sigtermHandler);
|
|
18863
|
+
try {
|
|
18864
|
+
fifoReadline?.close();
|
|
18865
|
+
} catch {}
|
|
18866
|
+
try {
|
|
18867
|
+
fifoReadStream?.destroy();
|
|
18868
|
+
} catch {}
|
|
18869
|
+
try {
|
|
18870
|
+
fsyncSync(eventsFd);
|
|
18871
|
+
} catch {}
|
|
18849
18872
|
closeSync(eventsFd);
|
|
18850
18873
|
try {
|
|
18851
18874
|
if (existsSync4(fifoPath))
|
|
@@ -19511,20 +19534,17 @@ var exports_run = {};
|
|
|
19511
19534
|
__export(exports_run, {
|
|
19512
19535
|
run: () => run7
|
|
19513
19536
|
});
|
|
19514
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
19515
19537
|
import { join as join10 } from "node:path";
|
|
19516
19538
|
async function parseArgs4(argv) {
|
|
19517
19539
|
const name = argv[0];
|
|
19518
19540
|
if (!name || name.startsWith("--")) {
|
|
19519
|
-
console.error('Usage: specialists|sp run <name> [--prompt "..."] [--bead <id>] [--context-depth <n>] [--model <model>] [--no-beads] [--
|
|
19541
|
+
console.error('Usage: specialists|sp run <name> [--prompt "..."] [--bead <id>] [--context-depth <n>] [--model <model>] [--no-beads] [--keep-alive]');
|
|
19520
19542
|
process.exit(1);
|
|
19521
19543
|
}
|
|
19522
19544
|
let prompt = "";
|
|
19523
19545
|
let beadId;
|
|
19524
19546
|
let model;
|
|
19525
19547
|
let noBeads = false;
|
|
19526
|
-
let background = false;
|
|
19527
|
-
let follow = false;
|
|
19528
19548
|
let keepAlive = false;
|
|
19529
19549
|
let contextDepth = 1;
|
|
19530
19550
|
for (let i = 1;i < argv.length; i++) {
|
|
@@ -19549,14 +19569,6 @@ async function parseArgs4(argv) {
|
|
|
19549
19569
|
noBeads = true;
|
|
19550
19570
|
continue;
|
|
19551
19571
|
}
|
|
19552
|
-
if (token === "--background") {
|
|
19553
|
-
background = true;
|
|
19554
|
-
continue;
|
|
19555
|
-
}
|
|
19556
|
-
if (token === "--follow") {
|
|
19557
|
-
follow = true;
|
|
19558
|
-
continue;
|
|
19559
|
-
}
|
|
19560
19572
|
if (token === "--keep-alive") {
|
|
19561
19573
|
keepAlive = true;
|
|
19562
19574
|
continue;
|
|
@@ -19580,7 +19592,7 @@ async function parseArgs4(argv) {
|
|
|
19580
19592
|
console.error("Error: provide --prompt, pipe stdin, or use --bead <id>.");
|
|
19581
19593
|
process.exit(1);
|
|
19582
19594
|
}
|
|
19583
|
-
return { name, prompt, beadId, model, noBeads,
|
|
19595
|
+
return { name, prompt, beadId, model, noBeads, keepAlive, contextDepth };
|
|
19584
19596
|
}
|
|
19585
19597
|
async function run7() {
|
|
19586
19598
|
const args = await parseArgs4(process.argv.slice(3));
|
|
@@ -19615,101 +19627,52 @@ async function run7() {
|
|
|
19615
19627
|
circuitBreaker,
|
|
19616
19628
|
beadsClient
|
|
19617
19629
|
});
|
|
19618
|
-
|
|
19619
|
-
|
|
19620
|
-
|
|
19621
|
-
|
|
19622
|
-
|
|
19623
|
-
|
|
19624
|
-
|
|
19625
|
-
|
|
19626
|
-
|
|
19627
|
-
|
|
19628
|
-
|
|
19629
|
-
|
|
19630
|
-
|
|
19631
|
-
|
|
19632
|
-
|
|
19633
|
-
let jobId;
|
|
19634
|
-
try {
|
|
19635
|
-
jobId = await supervisor.run();
|
|
19636
|
-
if (!args.follow) {
|
|
19637
|
-
process.stdout.write(`Job started: ${jobId}
|
|
19638
|
-
`);
|
|
19639
|
-
}
|
|
19640
|
-
} catch (err) {
|
|
19641
|
-
process.stderr.write(`Error: ${err?.message ?? err}
|
|
19642
|
-
`);
|
|
19643
|
-
process.exit(1);
|
|
19644
|
-
}
|
|
19645
|
-
if (args.follow) {
|
|
19646
|
-
await new Promise((resolve2, reject) => {
|
|
19647
|
-
const feed = spawn2("specialists", ["feed", "--job", jobId, "--follow"], {
|
|
19648
|
-
cwd: process.cwd(),
|
|
19649
|
-
stdio: "inherit"
|
|
19650
|
-
});
|
|
19651
|
-
feed.on("close", (code) => {
|
|
19652
|
-
if (code === 0) {
|
|
19653
|
-
resolve2();
|
|
19654
|
-
} else {
|
|
19655
|
-
reject(new Error(`Feed exited with code ${code}`));
|
|
19656
|
-
}
|
|
19657
|
-
});
|
|
19658
|
-
feed.on("error", (err) => {
|
|
19659
|
-
reject(err);
|
|
19660
|
-
});
|
|
19661
|
-
}).catch((err) => {
|
|
19662
|
-
process.stderr.write(`Error: ${err.message}
|
|
19663
|
-
`);
|
|
19664
|
-
process.exit(1);
|
|
19665
|
-
});
|
|
19666
|
-
}
|
|
19667
|
-
return;
|
|
19668
|
-
}
|
|
19669
|
-
process.stderr.write(`
|
|
19670
|
-
${bold5(`Running ${cyan3(args.name)}`)}
|
|
19671
|
-
|
|
19672
|
-
`);
|
|
19673
|
-
let trackingBeadId;
|
|
19674
|
-
const result = await runner.run({
|
|
19675
|
-
name: args.name,
|
|
19676
|
-
prompt,
|
|
19677
|
-
variables,
|
|
19678
|
-
backendOverride: args.model,
|
|
19679
|
-
inputBeadId: args.beadId
|
|
19680
|
-
}, (delta) => process.stdout.write(delta), undefined, (meta) => process.stderr.write(dim5(`
|
|
19630
|
+
const jobsDir = join10(process.cwd(), ".specialists", "jobs");
|
|
19631
|
+
const supervisor = new Supervisor({
|
|
19632
|
+
runner,
|
|
19633
|
+
runOptions: {
|
|
19634
|
+
name: args.name,
|
|
19635
|
+
prompt,
|
|
19636
|
+
variables,
|
|
19637
|
+
backendOverride: args.model,
|
|
19638
|
+
inputBeadId: args.beadId,
|
|
19639
|
+
keepAlive: args.keepAlive
|
|
19640
|
+
},
|
|
19641
|
+
jobsDir,
|
|
19642
|
+
beadsClient,
|
|
19643
|
+
onProgress: (delta) => process.stdout.write(delta),
|
|
19644
|
+
onMeta: (meta) => process.stderr.write(dim5(`
|
|
19681
19645
|
[${meta.backend} / ${meta.model}]
|
|
19682
19646
|
|
|
19683
|
-
`))
|
|
19684
|
-
|
|
19685
|
-
|
|
19647
|
+
`))
|
|
19648
|
+
});
|
|
19649
|
+
process.stderr.write(`
|
|
19650
|
+
${bold5(`Running ${cyan3(args.name)}`)}
|
|
19686
19651
|
|
|
19687
|
-
Interrupted.
|
|
19688
19652
|
`);
|
|
19689
|
-
|
|
19690
|
-
|
|
19691
|
-
|
|
19692
|
-
}
|
|
19693
|
-
|
|
19694
|
-
process.stderr.write(dim5(`
|
|
19695
|
-
[bead: ${beadId}]
|
|
19696
|
-
`));
|
|
19697
|
-
});
|
|
19698
|
-
if (result.output && !result.output.endsWith(`
|
|
19699
|
-
`))
|
|
19700
|
-
process.stdout.write(`
|
|
19653
|
+
let jobId;
|
|
19654
|
+
try {
|
|
19655
|
+
jobId = await supervisor.run();
|
|
19656
|
+
} catch (err) {
|
|
19657
|
+
process.stderr.write(`Error: ${err?.message ?? err}
|
|
19701
19658
|
`);
|
|
19702
|
-
|
|
19703
|
-
|
|
19659
|
+
process.exit(1);
|
|
19660
|
+
}
|
|
19661
|
+
const status = supervisor.readStatus(jobId);
|
|
19662
|
+
const secs = ((status?.last_event_at_ms ?? Date.now()) - (status?.started_at_ms ?? Date.now())) / 1000;
|
|
19704
19663
|
const footer = [
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
|
|
19664
|
+
`job ${jobId}`,
|
|
19665
|
+
status?.bead_id ? `bead ${status.bead_id}` : "",
|
|
19666
|
+
`${secs.toFixed(1)}s`,
|
|
19667
|
+
status?.model ? dim5(`${status.backend}/${status.model}`) : ""
|
|
19708
19668
|
].filter(Boolean).join(" ");
|
|
19709
19669
|
process.stderr.write(`
|
|
19710
19670
|
${green5("✓")} ${footer}
|
|
19711
19671
|
|
|
19712
19672
|
`);
|
|
19673
|
+
process.stderr.write(dim5(`Poll: specialists poll ${jobId} --json
|
|
19674
|
+
|
|
19675
|
+
`));
|
|
19713
19676
|
}
|
|
19714
19677
|
var bold5 = (s) => `\x1B[1m${s}\x1B[0m`, dim5 = (s) => `\x1B[2m${s}\x1B[0m`, green5 = (s) => `\x1B[32m${s}\x1B[0m`, cyan3 = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
19715
19678
|
var init_run = __esm(() => {
|
|
@@ -20058,6 +20021,9 @@ function readJobEvents(jobDir) {
|
|
|
20058
20021
|
events.sort(compareTimelineEvents);
|
|
20059
20022
|
return events;
|
|
20060
20023
|
}
|
|
20024
|
+
function readJobEventsById(jobsDir, jobId) {
|
|
20025
|
+
return readJobEvents(join13(jobsDir, jobId));
|
|
20026
|
+
}
|
|
20061
20027
|
function readAllJobEvents(jobsDir) {
|
|
20062
20028
|
if (!existsSync9(jobsDir))
|
|
20063
20029
|
return [];
|
|
@@ -20359,21 +20325,132 @@ var init_feed = __esm(() => {
|
|
|
20359
20325
|
init_format_helpers();
|
|
20360
20326
|
});
|
|
20361
20327
|
|
|
20328
|
+
// src/cli/poll.ts
|
|
20329
|
+
var exports_poll = {};
|
|
20330
|
+
__export(exports_poll, {
|
|
20331
|
+
run: () => run11
|
|
20332
|
+
});
|
|
20333
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
|
|
20334
|
+
import { join as join15 } from "node:path";
|
|
20335
|
+
function parseArgs6(argv) {
|
|
20336
|
+
let jobId;
|
|
20337
|
+
let cursor = 0;
|
|
20338
|
+
let json = false;
|
|
20339
|
+
for (let i = 0;i < argv.length; i++) {
|
|
20340
|
+
if (argv[i] === "--cursor" && argv[i + 1]) {
|
|
20341
|
+
cursor = parseInt(argv[++i], 10);
|
|
20342
|
+
if (isNaN(cursor) || cursor < 0)
|
|
20343
|
+
cursor = 0;
|
|
20344
|
+
continue;
|
|
20345
|
+
}
|
|
20346
|
+
if (argv[i] === "--json") {
|
|
20347
|
+
json = true;
|
|
20348
|
+
continue;
|
|
20349
|
+
}
|
|
20350
|
+
if (!argv[i].startsWith("--")) {
|
|
20351
|
+
jobId = argv[i];
|
|
20352
|
+
}
|
|
20353
|
+
}
|
|
20354
|
+
if (!jobId) {
|
|
20355
|
+
console.error("Usage: specialists poll <job-id> [--cursor N] [--json]");
|
|
20356
|
+
process.exit(1);
|
|
20357
|
+
}
|
|
20358
|
+
return { jobId, cursor, json };
|
|
20359
|
+
}
|
|
20360
|
+
async function run11() {
|
|
20361
|
+
const { jobId, cursor, json } = parseArgs6(process.argv.slice(3));
|
|
20362
|
+
const jobsDir = join15(process.cwd(), ".specialists", "jobs");
|
|
20363
|
+
const jobDir = join15(jobsDir, jobId);
|
|
20364
|
+
if (!existsSync11(jobDir)) {
|
|
20365
|
+
const result2 = {
|
|
20366
|
+
job_id: jobId,
|
|
20367
|
+
status: "error",
|
|
20368
|
+
elapsed_ms: 0,
|
|
20369
|
+
cursor: 0,
|
|
20370
|
+
output: "",
|
|
20371
|
+
output_delta: "",
|
|
20372
|
+
events: [],
|
|
20373
|
+
error: `Job not found: ${jobId}`
|
|
20374
|
+
};
|
|
20375
|
+
console.log(JSON.stringify(result2));
|
|
20376
|
+
process.exit(1);
|
|
20377
|
+
}
|
|
20378
|
+
const statusPath = join15(jobDir, "status.json");
|
|
20379
|
+
let status = null;
|
|
20380
|
+
if (existsSync11(statusPath)) {
|
|
20381
|
+
try {
|
|
20382
|
+
status = JSON.parse(readFileSync7(statusPath, "utf-8"));
|
|
20383
|
+
} catch {}
|
|
20384
|
+
}
|
|
20385
|
+
const resultPath = join15(jobDir, "result.txt");
|
|
20386
|
+
let output = "";
|
|
20387
|
+
if (existsSync11(resultPath)) {
|
|
20388
|
+
try {
|
|
20389
|
+
output = readFileSync7(resultPath, "utf-8");
|
|
20390
|
+
} catch {}
|
|
20391
|
+
}
|
|
20392
|
+
const events = readJobEventsById(jobsDir, jobId);
|
|
20393
|
+
const newEvents = events.slice(cursor);
|
|
20394
|
+
const nextCursor = events.length;
|
|
20395
|
+
const startedAt = status?.started_at_ms ?? Date.now();
|
|
20396
|
+
const lastEvent = status?.last_event_at_ms ?? Date.now();
|
|
20397
|
+
const elapsedMs = status?.status === "done" || status?.status === "error" ? lastEvent - startedAt : Date.now() - startedAt;
|
|
20398
|
+
const result = {
|
|
20399
|
+
job_id: jobId,
|
|
20400
|
+
status: status?.status ?? "starting",
|
|
20401
|
+
elapsed_ms: elapsedMs,
|
|
20402
|
+
cursor: nextCursor,
|
|
20403
|
+
output: status?.status === "done" ? output : "",
|
|
20404
|
+
output_delta: "",
|
|
20405
|
+
events: newEvents,
|
|
20406
|
+
current_event: status?.current_event,
|
|
20407
|
+
current_tool: status?.current_tool,
|
|
20408
|
+
model: status?.model,
|
|
20409
|
+
backend: status?.backend,
|
|
20410
|
+
bead_id: status?.bead_id,
|
|
20411
|
+
error: status?.error
|
|
20412
|
+
};
|
|
20413
|
+
if (json) {
|
|
20414
|
+
console.log(JSON.stringify(result, null, 2));
|
|
20415
|
+
} else {
|
|
20416
|
+
console.log(`Job: ${jobId}`);
|
|
20417
|
+
console.log(`Status: ${result.status}`);
|
|
20418
|
+
console.log(`Elapsed: ${Math.round(result.elapsed_ms / 1000)}s`);
|
|
20419
|
+
if (result.model)
|
|
20420
|
+
console.log(`Model: ${result.backend}/${result.model}`);
|
|
20421
|
+
if (result.bead_id)
|
|
20422
|
+
console.log(`Bead: ${result.bead_id}`);
|
|
20423
|
+
if (result.current_tool)
|
|
20424
|
+
console.log(`Tool: ${result.current_tool}`);
|
|
20425
|
+
if (result.error)
|
|
20426
|
+
console.log(`Error: ${result.error}`);
|
|
20427
|
+
console.log(`Events: ${newEvents.length} new (cursor: ${cursor} → ${nextCursor})`);
|
|
20428
|
+
if (result.status === "done" && result.output) {
|
|
20429
|
+
console.log(`
|
|
20430
|
+
--- Output ---`);
|
|
20431
|
+
console.log(result.output.slice(0, 500) + (result.output.length > 500 ? "..." : ""));
|
|
20432
|
+
}
|
|
20433
|
+
}
|
|
20434
|
+
}
|
|
20435
|
+
var init_poll = __esm(() => {
|
|
20436
|
+
init_timeline_query();
|
|
20437
|
+
});
|
|
20438
|
+
|
|
20362
20439
|
// src/cli/steer.ts
|
|
20363
20440
|
var exports_steer = {};
|
|
20364
20441
|
__export(exports_steer, {
|
|
20365
|
-
run: () =>
|
|
20442
|
+
run: () => run12
|
|
20366
20443
|
});
|
|
20367
|
-
import { join as
|
|
20444
|
+
import { join as join16 } from "node:path";
|
|
20368
20445
|
import { writeFileSync as writeFileSync6 } from "node:fs";
|
|
20369
|
-
async function
|
|
20446
|
+
async function run12() {
|
|
20370
20447
|
const jobId = process.argv[3];
|
|
20371
20448
|
const message = process.argv[4];
|
|
20372
20449
|
if (!jobId || !message) {
|
|
20373
20450
|
console.error('Usage: specialists|sp steer <job-id> "<message>"');
|
|
20374
20451
|
process.exit(1);
|
|
20375
20452
|
}
|
|
20376
|
-
const jobsDir =
|
|
20453
|
+
const jobsDir = join16(process.cwd(), ".specialists", "jobs");
|
|
20377
20454
|
const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
|
|
20378
20455
|
const status = supervisor.readStatus(jobId);
|
|
20379
20456
|
if (!status) {
|
|
@@ -20412,18 +20489,18 @@ var init_steer = __esm(() => {
|
|
|
20412
20489
|
// src/cli/follow-up.ts
|
|
20413
20490
|
var exports_follow_up = {};
|
|
20414
20491
|
__export(exports_follow_up, {
|
|
20415
|
-
run: () =>
|
|
20492
|
+
run: () => run13
|
|
20416
20493
|
});
|
|
20417
|
-
import { join as
|
|
20494
|
+
import { join as join17 } from "node:path";
|
|
20418
20495
|
import { writeFileSync as writeFileSync7 } from "node:fs";
|
|
20419
|
-
async function
|
|
20496
|
+
async function run13() {
|
|
20420
20497
|
const jobId = process.argv[3];
|
|
20421
20498
|
const message = process.argv[4];
|
|
20422
20499
|
if (!jobId || !message) {
|
|
20423
20500
|
console.error('Usage: specialists|sp follow-up <job-id> "<message>"');
|
|
20424
20501
|
process.exit(1);
|
|
20425
20502
|
}
|
|
20426
|
-
const jobsDir =
|
|
20503
|
+
const jobsDir = join17(process.cwd(), ".specialists", "jobs");
|
|
20427
20504
|
const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
|
|
20428
20505
|
const status = supervisor.readStatus(jobId);
|
|
20429
20506
|
if (!status) {
|
|
@@ -20464,16 +20541,16 @@ var init_follow_up = __esm(() => {
|
|
|
20464
20541
|
// src/cli/stop.ts
|
|
20465
20542
|
var exports_stop = {};
|
|
20466
20543
|
__export(exports_stop, {
|
|
20467
|
-
run: () =>
|
|
20544
|
+
run: () => run14
|
|
20468
20545
|
});
|
|
20469
|
-
import { join as
|
|
20470
|
-
async function
|
|
20546
|
+
import { join as join18 } from "node:path";
|
|
20547
|
+
async function run14() {
|
|
20471
20548
|
const jobId = process.argv[3];
|
|
20472
20549
|
if (!jobId) {
|
|
20473
20550
|
console.error("Usage: specialists|sp stop <job-id>");
|
|
20474
20551
|
process.exit(1);
|
|
20475
20552
|
}
|
|
20476
|
-
const jobsDir =
|
|
20553
|
+
const jobsDir = join18(process.cwd(), ".specialists", "jobs");
|
|
20477
20554
|
const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
|
|
20478
20555
|
const status = supervisor.readStatus(jobId);
|
|
20479
20556
|
if (!status) {
|
|
@@ -20513,7 +20590,7 @@ var init_stop = __esm(() => {
|
|
|
20513
20590
|
// src/cli/quickstart.ts
|
|
20514
20591
|
var exports_quickstart = {};
|
|
20515
20592
|
__export(exports_quickstart, {
|
|
20516
|
-
run: () =>
|
|
20593
|
+
run: () => run15
|
|
20517
20594
|
});
|
|
20518
20595
|
function section2(title) {
|
|
20519
20596
|
const bar = "─".repeat(60);
|
|
@@ -20527,7 +20604,7 @@ function cmd2(s) {
|
|
|
20527
20604
|
function flag(s) {
|
|
20528
20605
|
return green10(s);
|
|
20529
20606
|
}
|
|
20530
|
-
async function
|
|
20607
|
+
async function run15() {
|
|
20531
20608
|
const lines = [
|
|
20532
20609
|
"",
|
|
20533
20610
|
bold7("specialists · Quick Start Guide"),
|
|
@@ -20744,11 +20821,11 @@ var bold7 = (s) => `\x1B[1m${s}\x1B[0m`, dim9 = (s) => `\x1B[2m${s}\x1B[0m`, yel
|
|
|
20744
20821
|
// src/cli/doctor.ts
|
|
20745
20822
|
var exports_doctor = {};
|
|
20746
20823
|
__export(exports_doctor, {
|
|
20747
|
-
run: () =>
|
|
20824
|
+
run: () => run16
|
|
20748
20825
|
});
|
|
20749
20826
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
20750
|
-
import { existsSync as
|
|
20751
|
-
import { join as
|
|
20827
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync as readdirSync4 } from "node:fs";
|
|
20828
|
+
import { join as join19 } from "node:path";
|
|
20752
20829
|
function ok3(msg) {
|
|
20753
20830
|
console.log(` ${green11("✓")} ${msg}`);
|
|
20754
20831
|
}
|
|
@@ -20777,10 +20854,10 @@ function isInstalled2(bin) {
|
|
|
20777
20854
|
return spawnSync7("which", [bin], { encoding: "utf8", timeout: 2000 }).status === 0;
|
|
20778
20855
|
}
|
|
20779
20856
|
function loadJson2(path) {
|
|
20780
|
-
if (!
|
|
20857
|
+
if (!existsSync12(path))
|
|
20781
20858
|
return null;
|
|
20782
20859
|
try {
|
|
20783
|
-
return JSON.parse(
|
|
20860
|
+
return JSON.parse(readFileSync8(path, "utf8"));
|
|
20784
20861
|
} catch {
|
|
20785
20862
|
return null;
|
|
20786
20863
|
}
|
|
@@ -20813,7 +20890,7 @@ function checkBd() {
|
|
|
20813
20890
|
return false;
|
|
20814
20891
|
}
|
|
20815
20892
|
ok3(`bd installed ${dim10(sp("bd", ["--version"]).stdout || "")}`);
|
|
20816
|
-
if (
|
|
20893
|
+
if (existsSync12(join19(CWD, ".beads")))
|
|
20817
20894
|
ok3(".beads/ present in project");
|
|
20818
20895
|
else
|
|
20819
20896
|
warn2(".beads/ not found in project");
|
|
@@ -20833,8 +20910,8 @@ function checkHooks() {
|
|
|
20833
20910
|
section3("Claude Code hooks (2 expected)");
|
|
20834
20911
|
let allPresent = true;
|
|
20835
20912
|
for (const name of HOOK_NAMES) {
|
|
20836
|
-
const dest =
|
|
20837
|
-
if (!
|
|
20913
|
+
const dest = join19(HOOKS_DIR, name);
|
|
20914
|
+
if (!existsSync12(dest)) {
|
|
20838
20915
|
fail2(`${name} ${red6("missing")}`);
|
|
20839
20916
|
fix("specialists install");
|
|
20840
20917
|
allPresent = false;
|
|
@@ -20878,18 +20955,18 @@ function checkMCP() {
|
|
|
20878
20955
|
}
|
|
20879
20956
|
function checkRuntimeDirs() {
|
|
20880
20957
|
section3(".specialists/ runtime directories");
|
|
20881
|
-
const rootDir =
|
|
20882
|
-
const jobsDir =
|
|
20883
|
-
const readyDir =
|
|
20958
|
+
const rootDir = join19(CWD, ".specialists");
|
|
20959
|
+
const jobsDir = join19(rootDir, "jobs");
|
|
20960
|
+
const readyDir = join19(rootDir, "ready");
|
|
20884
20961
|
let allOk = true;
|
|
20885
|
-
if (!
|
|
20962
|
+
if (!existsSync12(rootDir)) {
|
|
20886
20963
|
warn2(".specialists/ not found in current project");
|
|
20887
20964
|
fix("specialists init");
|
|
20888
20965
|
allOk = false;
|
|
20889
20966
|
} else {
|
|
20890
20967
|
ok3(".specialists/ present");
|
|
20891
20968
|
for (const [subDir, label] of [[jobsDir, "jobs"], [readyDir, "ready"]]) {
|
|
20892
|
-
if (!
|
|
20969
|
+
if (!existsSync12(subDir)) {
|
|
20893
20970
|
warn2(`.specialists/${label}/ missing — auto-creating`);
|
|
20894
20971
|
mkdirSync3(subDir, { recursive: true });
|
|
20895
20972
|
ok3(`.specialists/${label}/ created`);
|
|
@@ -20902,8 +20979,8 @@ function checkRuntimeDirs() {
|
|
|
20902
20979
|
}
|
|
20903
20980
|
function checkZombieJobs() {
|
|
20904
20981
|
section3("Background jobs");
|
|
20905
|
-
const jobsDir =
|
|
20906
|
-
if (!
|
|
20982
|
+
const jobsDir = join19(CWD, ".specialists", "jobs");
|
|
20983
|
+
if (!existsSync12(jobsDir)) {
|
|
20907
20984
|
hint("No .specialists/jobs/ — skipping");
|
|
20908
20985
|
return true;
|
|
20909
20986
|
}
|
|
@@ -20921,11 +20998,11 @@ function checkZombieJobs() {
|
|
|
20921
20998
|
let total = 0;
|
|
20922
20999
|
let running = 0;
|
|
20923
21000
|
for (const jobId of entries) {
|
|
20924
|
-
const statusPath =
|
|
20925
|
-
if (!
|
|
21001
|
+
const statusPath = join19(jobsDir, jobId, "status.json");
|
|
21002
|
+
if (!existsSync12(statusPath))
|
|
20926
21003
|
continue;
|
|
20927
21004
|
try {
|
|
20928
|
-
const status = JSON.parse(
|
|
21005
|
+
const status = JSON.parse(readFileSync8(statusPath, "utf8"));
|
|
20929
21006
|
total++;
|
|
20930
21007
|
if (status.status === "running" || status.status === "starting") {
|
|
20931
21008
|
const pid = status.pid;
|
|
@@ -20952,7 +21029,7 @@ function checkZombieJobs() {
|
|
|
20952
21029
|
}
|
|
20953
21030
|
return zombies === 0;
|
|
20954
21031
|
}
|
|
20955
|
-
async function
|
|
21032
|
+
async function run16() {
|
|
20956
21033
|
console.log(`
|
|
20957
21034
|
${bold8("specialists doctor")}
|
|
20958
21035
|
`);
|
|
@@ -20976,11 +21053,11 @@ ${bold8("specialists doctor")}
|
|
|
20976
21053
|
var bold8 = (s) => `\x1B[1m${s}\x1B[0m`, dim10 = (s) => `\x1B[2m${s}\x1B[0m`, green11 = (s) => `\x1B[32m${s}\x1B[0m`, yellow7 = (s) => `\x1B[33m${s}\x1B[0m`, red6 = (s) => `\x1B[31m${s}\x1B[0m`, CWD, CLAUDE_DIR, SPECIALISTS_DIR, HOOKS_DIR, SETTINGS_FILE, MCP_FILE2, HOOK_NAMES;
|
|
20977
21054
|
var init_doctor = __esm(() => {
|
|
20978
21055
|
CWD = process.cwd();
|
|
20979
|
-
CLAUDE_DIR =
|
|
20980
|
-
SPECIALISTS_DIR =
|
|
20981
|
-
HOOKS_DIR =
|
|
20982
|
-
SETTINGS_FILE =
|
|
20983
|
-
MCP_FILE2 =
|
|
21056
|
+
CLAUDE_DIR = join19(CWD, ".claude");
|
|
21057
|
+
SPECIALISTS_DIR = join19(CWD, ".specialists");
|
|
21058
|
+
HOOKS_DIR = join19(SPECIALISTS_DIR, "default", "hooks");
|
|
21059
|
+
SETTINGS_FILE = join19(CLAUDE_DIR, "settings.json");
|
|
21060
|
+
MCP_FILE2 = join19(CWD, ".mcp.json");
|
|
20984
21061
|
HOOK_NAMES = [
|
|
20985
21062
|
"specialists-complete.mjs",
|
|
20986
21063
|
"specialists-session-start.mjs"
|
|
@@ -20990,11 +21067,11 @@ var init_doctor = __esm(() => {
|
|
|
20990
21067
|
// src/cli/setup.ts
|
|
20991
21068
|
var exports_setup = {};
|
|
20992
21069
|
__export(exports_setup, {
|
|
20993
|
-
run: () =>
|
|
21070
|
+
run: () => run17
|
|
20994
21071
|
});
|
|
20995
|
-
import { existsSync as
|
|
21072
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "node:fs";
|
|
20996
21073
|
import { homedir as homedir3 } from "node:os";
|
|
20997
|
-
import { join as
|
|
21074
|
+
import { join as join20, resolve as resolve2 } from "node:path";
|
|
20998
21075
|
function ok4(msg) {
|
|
20999
21076
|
console.log(` ${green12("✓")} ${msg}`);
|
|
21000
21077
|
}
|
|
@@ -21004,15 +21081,15 @@ function skip2(msg) {
|
|
|
21004
21081
|
function resolveTarget(target) {
|
|
21005
21082
|
switch (target) {
|
|
21006
21083
|
case "global":
|
|
21007
|
-
return
|
|
21084
|
+
return join20(homedir3(), ".claude", "CLAUDE.md");
|
|
21008
21085
|
case "agents":
|
|
21009
|
-
return
|
|
21086
|
+
return join20(process.cwd(), "AGENTS.md");
|
|
21010
21087
|
case "project":
|
|
21011
21088
|
default:
|
|
21012
|
-
return
|
|
21089
|
+
return join20(process.cwd(), "CLAUDE.md");
|
|
21013
21090
|
}
|
|
21014
21091
|
}
|
|
21015
|
-
function
|
|
21092
|
+
function parseArgs7() {
|
|
21016
21093
|
const argv = process.argv.slice(3);
|
|
21017
21094
|
let target = "project";
|
|
21018
21095
|
let dryRun = false;
|
|
@@ -21037,8 +21114,8 @@ function parseArgs6() {
|
|
|
21037
21114
|
}
|
|
21038
21115
|
return { target, dryRun };
|
|
21039
21116
|
}
|
|
21040
|
-
async function
|
|
21041
|
-
const { target, dryRun } =
|
|
21117
|
+
async function run17() {
|
|
21118
|
+
const { target, dryRun } = parseArgs7();
|
|
21042
21119
|
const filePath = resolve2(resolveTarget(target));
|
|
21043
21120
|
const label = target === "global" ? "~/.claude/CLAUDE.md" : filePath.replace(process.cwd() + "/", "");
|
|
21044
21121
|
console.log(`
|
|
@@ -21046,8 +21123,8 @@ ${bold9("specialists setup")}
|
|
|
21046
21123
|
`);
|
|
21047
21124
|
console.log(` Target: ${yellow8(label)}${dryRun ? dim11(" (dry-run)") : ""}
|
|
21048
21125
|
`);
|
|
21049
|
-
if (
|
|
21050
|
-
const existing =
|
|
21126
|
+
if (existsSync13(filePath)) {
|
|
21127
|
+
const existing = readFileSync9(filePath, "utf8");
|
|
21051
21128
|
if (existing.includes(MARKER)) {
|
|
21052
21129
|
skip2(`${label} already contains Specialists Workflow section`);
|
|
21053
21130
|
console.log(`
|
|
@@ -21157,13 +21234,13 @@ var init_setup = () => {};
|
|
|
21157
21234
|
// src/cli/help.ts
|
|
21158
21235
|
var exports_help = {};
|
|
21159
21236
|
__export(exports_help, {
|
|
21160
|
-
run: () =>
|
|
21237
|
+
run: () => run18
|
|
21161
21238
|
});
|
|
21162
21239
|
function formatCommands(entries) {
|
|
21163
21240
|
const width = Math.max(...entries.map(([cmd3]) => cmd3.length));
|
|
21164
21241
|
return entries.map(([cmd3, desc]) => ` ${cmd3.padEnd(width)} ${desc}`);
|
|
21165
21242
|
}
|
|
21166
|
-
async function
|
|
21243
|
+
async function run18() {
|
|
21167
21244
|
const lines = [
|
|
21168
21245
|
"",
|
|
21169
21246
|
"Specialists lets you run project-scoped specialist agents with a bead-first workflow.",
|
|
@@ -21178,8 +21255,8 @@ async function run17() {
|
|
|
21178
21255
|
"",
|
|
21179
21256
|
" Tracked work (primary)",
|
|
21180
21257
|
' bd create "Task title" -t task -p 1 --json',
|
|
21181
|
-
" specialists run <name> --bead <id> [--context-depth N]
|
|
21182
|
-
" specialists
|
|
21258
|
+
" specialists run <name> --bead <id> [--context-depth N]",
|
|
21259
|
+
" specialists poll <job-id> --json # check status",
|
|
21183
21260
|
' bd close <id> --reason "Done"',
|
|
21184
21261
|
"",
|
|
21185
21262
|
" Ad-hoc work",
|
|
@@ -21190,7 +21267,11 @@ async function run17() {
|
|
|
21190
21267
|
" --prompt is for quick untracked work",
|
|
21191
21268
|
" --context-depth defaults to 1 with --bead",
|
|
21192
21269
|
" --no-beads does not disable bead reading",
|
|
21193
|
-
"
|
|
21270
|
+
"",
|
|
21271
|
+
" Background execution",
|
|
21272
|
+
" Use Claude Code's native backgrounding (run_in_background: true)",
|
|
21273
|
+
" or run in a separate terminal and poll with:",
|
|
21274
|
+
" specialists poll <job-id> --json",
|
|
21194
21275
|
"",
|
|
21195
21276
|
bold10("Core commands:"),
|
|
21196
21277
|
...formatCommands(CORE_COMMANDS),
|
|
@@ -21204,10 +21285,10 @@ async function run17() {
|
|
|
21204
21285
|
bold10("Examples:"),
|
|
21205
21286
|
" specialists init",
|
|
21206
21287
|
" specialists list",
|
|
21207
|
-
" specialists run bug-hunt --bead unitAI-123
|
|
21208
|
-
" specialists run sync-docs --follow # run + stream live output",
|
|
21288
|
+
" specialists run bug-hunt --bead unitAI-123",
|
|
21209
21289
|
' specialists run codebase-explorer --prompt "Map the CLI architecture"',
|
|
21210
|
-
" specialists
|
|
21290
|
+
" specialists poll abc123 --json # check job status",
|
|
21291
|
+
" specialists feed -f # stream all job events",
|
|
21211
21292
|
' specialists steer <job-id> "focus only on supervisor.ts"',
|
|
21212
21293
|
' specialists follow-up <job-id> "now write the fix"',
|
|
21213
21294
|
" specialists result <job-id>",
|
|
@@ -21215,10 +21296,11 @@ async function run17() {
|
|
|
21215
21296
|
bold10("More help:"),
|
|
21216
21297
|
" specialists quickstart Full guide and workflow reference",
|
|
21217
21298
|
" specialists run --help Run command details and flags",
|
|
21299
|
+
" specialists poll --help Job status polling details",
|
|
21218
21300
|
" specialists steer --help Mid-run steering details",
|
|
21219
21301
|
" specialists follow-up --help Multi-turn keep-alive details",
|
|
21220
21302
|
" specialists init --help Bootstrap behavior and workflow injection",
|
|
21221
|
-
" specialists feed --help
|
|
21303
|
+
" specialists feed --help Job event streaming details",
|
|
21222
21304
|
"",
|
|
21223
21305
|
dim12("Project model: specialists are project-only; user-scope discovery is deprecated."),
|
|
21224
21306
|
""
|
|
@@ -21233,10 +21315,11 @@ var init_help = __esm(() => {
|
|
|
21233
21315
|
["list", "List specialists in this project"],
|
|
21234
21316
|
["run", "Run a specialist with --bead for tracked work or --prompt for ad-hoc work"],
|
|
21235
21317
|
["feed", "Tail job events; use -f to follow all jobs"],
|
|
21236
|
-
["
|
|
21237
|
-
["
|
|
21318
|
+
["poll", "Machine-readable job status polling (for scripts/Claude Code)"],
|
|
21319
|
+
["result", "Print final output of a completed job"],
|
|
21320
|
+
["steer", "Send a mid-run message to a running job"],
|
|
21238
21321
|
["follow-up", "Send a next-turn prompt to a keep-alive session (retains full context)"],
|
|
21239
|
-
["stop", "Stop a running
|
|
21322
|
+
["stop", "Stop a running job"],
|
|
21240
21323
|
["status", "Show health, MCP state, and active jobs"],
|
|
21241
21324
|
["doctor", "Diagnose installation/runtime problems"],
|
|
21242
21325
|
["quickstart", "Full getting-started guide"],
|
|
@@ -27284,9 +27367,6 @@ class Protocol {
|
|
|
27284
27367
|
}
|
|
27285
27368
|
}
|
|
27286
27369
|
async connect(transport) {
|
|
27287
|
-
if (this._transport) {
|
|
27288
|
-
throw new Error("Already connected to a transport. Call close() before connecting to a new transport, or use a separate Protocol instance per connection.");
|
|
27289
|
-
}
|
|
27290
27370
|
this._transport = transport;
|
|
27291
27371
|
const _onclose = this.transport?.onclose;
|
|
27292
27372
|
this._transport.onclose = () => {
|
|
@@ -27319,10 +27399,6 @@ class Protocol {
|
|
|
27319
27399
|
this._progressHandlers.clear();
|
|
27320
27400
|
this._taskProgressTokens.clear();
|
|
27321
27401
|
this._pendingDebouncedNotifications.clear();
|
|
27322
|
-
for (const controller of this._requestHandlerAbortControllers.values()) {
|
|
27323
|
-
controller.abort();
|
|
27324
|
-
}
|
|
27325
|
-
this._requestHandlerAbortControllers.clear();
|
|
27326
27402
|
const error2 = McpError.fromError(ErrorCode.ConnectionClosed, "Connection closed");
|
|
27327
27403
|
this._transport = undefined;
|
|
27328
27404
|
this.onclose?.();
|
|
@@ -27373,8 +27449,6 @@ class Protocol {
|
|
|
27373
27449
|
sessionId: capturedTransport?.sessionId,
|
|
27374
27450
|
_meta: request.params?._meta,
|
|
27375
27451
|
sendNotification: async (notification) => {
|
|
27376
|
-
if (abortController.signal.aborted)
|
|
27377
|
-
return;
|
|
27378
27452
|
const notificationOptions = { relatedRequestId: request.id };
|
|
27379
27453
|
if (relatedTaskId) {
|
|
27380
27454
|
notificationOptions.relatedTask = { taskId: relatedTaskId };
|
|
@@ -27382,9 +27456,6 @@ class Protocol {
|
|
|
27382
27456
|
await this.notification(notification, notificationOptions);
|
|
27383
27457
|
},
|
|
27384
27458
|
sendRequest: async (r, resultSchema, options) => {
|
|
27385
|
-
if (abortController.signal.aborted) {
|
|
27386
|
-
throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
|
|
27387
|
-
}
|
|
27388
27459
|
const requestOptions = { ...options, relatedRequestId: request.id };
|
|
27389
27460
|
if (relatedTaskId && !requestOptions.relatedTask) {
|
|
27390
27461
|
requestOptions.relatedTask = { taskId: relatedTaskId };
|
|
@@ -27998,62 +28069,6 @@ class ExperimentalServerTasks {
|
|
|
27998
28069
|
requestStream(request, resultSchema, options) {
|
|
27999
28070
|
return this._server.requestStream(request, resultSchema, options);
|
|
28000
28071
|
}
|
|
28001
|
-
createMessageStream(params, options) {
|
|
28002
|
-
const clientCapabilities = this._server.getClientCapabilities();
|
|
28003
|
-
if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) {
|
|
28004
|
-
throw new Error("Client does not support sampling tools capability.");
|
|
28005
|
-
}
|
|
28006
|
-
if (params.messages.length > 0) {
|
|
28007
|
-
const lastMessage = params.messages[params.messages.length - 1];
|
|
28008
|
-
const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
|
|
28009
|
-
const hasToolResults = lastContent.some((c) => c.type === "tool_result");
|
|
28010
|
-
const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : undefined;
|
|
28011
|
-
const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
|
|
28012
|
-
const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
|
|
28013
|
-
if (hasToolResults) {
|
|
28014
|
-
if (lastContent.some((c) => c.type !== "tool_result")) {
|
|
28015
|
-
throw new Error("The last message must contain only tool_result content if any is present");
|
|
28016
|
-
}
|
|
28017
|
-
if (!hasPreviousToolUse) {
|
|
28018
|
-
throw new Error("tool_result blocks are not matching any tool_use from the previous message");
|
|
28019
|
-
}
|
|
28020
|
-
}
|
|
28021
|
-
if (hasPreviousToolUse) {
|
|
28022
|
-
const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
|
|
28023
|
-
const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
|
|
28024
|
-
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
|
|
28025
|
-
throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
|
|
28026
|
-
}
|
|
28027
|
-
}
|
|
28028
|
-
}
|
|
28029
|
-
return this.requestStream({
|
|
28030
|
-
method: "sampling/createMessage",
|
|
28031
|
-
params
|
|
28032
|
-
}, CreateMessageResultSchema, options);
|
|
28033
|
-
}
|
|
28034
|
-
elicitInputStream(params, options) {
|
|
28035
|
-
const clientCapabilities = this._server.getClientCapabilities();
|
|
28036
|
-
const mode = params.mode ?? "form";
|
|
28037
|
-
switch (mode) {
|
|
28038
|
-
case "url": {
|
|
28039
|
-
if (!clientCapabilities?.elicitation?.url) {
|
|
28040
|
-
throw new Error("Client does not support url elicitation.");
|
|
28041
|
-
}
|
|
28042
|
-
break;
|
|
28043
|
-
}
|
|
28044
|
-
case "form": {
|
|
28045
|
-
if (!clientCapabilities?.elicitation?.form) {
|
|
28046
|
-
throw new Error("Client does not support form elicitation.");
|
|
28047
|
-
}
|
|
28048
|
-
break;
|
|
28049
|
-
}
|
|
28050
|
-
}
|
|
28051
|
-
const normalizedParams = mode === "form" && params.mode === undefined ? { ...params, mode: "form" } : params;
|
|
28052
|
-
return this.requestStream({
|
|
28053
|
-
method: "elicitation/create",
|
|
28054
|
-
params: normalizedParams
|
|
28055
|
-
}, ElicitResultSchema, options);
|
|
28056
|
-
}
|
|
28057
28072
|
async getTask(taskId, options) {
|
|
28058
28073
|
return this._server.getTask({ taskId }, options);
|
|
28059
28074
|
}
|
|
@@ -29293,7 +29308,7 @@ var next = process.argv[3];
|
|
|
29293
29308
|
function wantsHelp() {
|
|
29294
29309
|
return next === "--help" || next === "-h";
|
|
29295
29310
|
}
|
|
29296
|
-
async function
|
|
29311
|
+
async function run19() {
|
|
29297
29312
|
if (sub === "install") {
|
|
29298
29313
|
if (wantsHelp()) {
|
|
29299
29314
|
console.log([
|
|
@@ -29436,7 +29451,7 @@ async function run18() {
|
|
|
29436
29451
|
"",
|
|
29437
29452
|
"Usage: specialists run <name> [options]",
|
|
29438
29453
|
"",
|
|
29439
|
-
"Run a specialist
|
|
29454
|
+
"Run a specialist. Streams output to stdout until completion.",
|
|
29440
29455
|
"",
|
|
29441
29456
|
"Primary modes:",
|
|
29442
29457
|
" tracked: specialists run <name> --bead <id>",
|
|
@@ -29447,20 +29462,23 @@ async function run18() {
|
|
|
29447
29462
|
" --prompt <text> Ad-hoc prompt for untracked work",
|
|
29448
29463
|
" --context-depth <n> Dependency context depth when using --bead (default: 1)",
|
|
29449
29464
|
" --no-beads Do not create a new tracking bead (does not disable bead reading)",
|
|
29450
|
-
" --background Start async and return a job id",
|
|
29451
|
-
" --follow Run in background and stream output live",
|
|
29452
29465
|
" --model <model> Override the configured model for this run",
|
|
29466
|
+
" --keep-alive Keep session alive for follow-up prompts",
|
|
29453
29467
|
"",
|
|
29454
29468
|
"Examples:",
|
|
29455
29469
|
" specialists run bug-hunt --bead unitAI-55d",
|
|
29456
|
-
" specialists run bug-hunt --bead unitAI-55d --context-depth 2
|
|
29457
|
-
" specialists run sync-docs --follow",
|
|
29470
|
+
" specialists run bug-hunt --bead unitAI-55d --context-depth 2",
|
|
29458
29471
|
' specialists run code-review --prompt "Audit src/api.ts"',
|
|
29459
29472
|
" cat brief.md | specialists run report-generator",
|
|
29460
29473
|
"",
|
|
29461
29474
|
"Rules:",
|
|
29462
29475
|
" Use --bead for tracked work.",
|
|
29463
29476
|
" Use --prompt for quick ad-hoc work.",
|
|
29477
|
+
"",
|
|
29478
|
+
"Background execution:",
|
|
29479
|
+
" Use Claude Code's native backgrounding (run_in_background: true)",
|
|
29480
|
+
" or run in a separate terminal and poll with:",
|
|
29481
|
+
" specialists poll <job-id> --json",
|
|
29464
29482
|
""
|
|
29465
29483
|
].join(`
|
|
29466
29484
|
`));
|
|
@@ -29553,6 +29571,49 @@ async function run18() {
|
|
|
29553
29571
|
const { run: handler } = await Promise.resolve().then(() => (init_feed(), exports_feed));
|
|
29554
29572
|
return handler();
|
|
29555
29573
|
}
|
|
29574
|
+
if (sub === "poll") {
|
|
29575
|
+
if (wantsHelp()) {
|
|
29576
|
+
console.log([
|
|
29577
|
+
"",
|
|
29578
|
+
"Usage: specialists poll <job-id> [--cursor N] [--json]",
|
|
29579
|
+
"",
|
|
29580
|
+
"Machine-readable job status polling for scripts and Claude Code.",
|
|
29581
|
+
"Reads from .specialists/jobs/<id>/ files.",
|
|
29582
|
+
"",
|
|
29583
|
+
"Output (JSON mode):",
|
|
29584
|
+
" {",
|
|
29585
|
+
' "job_id": "abc123",',
|
|
29586
|
+
' "status": "running" | "done" | "error" | "waiting",',
|
|
29587
|
+
' "elapsed_ms": 45000,',
|
|
29588
|
+
' "cursor": 15,',
|
|
29589
|
+
' "events": [...], // new events since cursor',
|
|
29590
|
+
' "output": "...", // full output when done',
|
|
29591
|
+
' "model": "claude-sonnet-4-6",',
|
|
29592
|
+
' "bead_id": "unitAI-123"',
|
|
29593
|
+
" }",
|
|
29594
|
+
"",
|
|
29595
|
+
"Options:",
|
|
29596
|
+
" --cursor N Event index to start from (default: 0)",
|
|
29597
|
+
" --json Output as JSON (machine-readable)",
|
|
29598
|
+
"",
|
|
29599
|
+
"Examples:",
|
|
29600
|
+
" specialists poll abc123 --json",
|
|
29601
|
+
" specialists poll abc123 --cursor 5 --json",
|
|
29602
|
+
"",
|
|
29603
|
+
"Polling pattern in Claude Code:",
|
|
29604
|
+
" 1. Start job (blocks until done):",
|
|
29605
|
+
" specialists run planner --bead xtrm-p38n.1",
|
|
29606
|
+
" 2. Or use Claude Code native backgrounding",
|
|
29607
|
+
" 3. Poll for incremental status:",
|
|
29608
|
+
" specialists poll <job-id> --json",
|
|
29609
|
+
""
|
|
29610
|
+
].join(`
|
|
29611
|
+
`));
|
|
29612
|
+
return;
|
|
29613
|
+
}
|
|
29614
|
+
const { run: pollHandler } = await Promise.resolve().then(() => (init_poll(), exports_poll));
|
|
29615
|
+
return pollHandler();
|
|
29616
|
+
}
|
|
29556
29617
|
if (sub === "steer") {
|
|
29557
29618
|
if (wantsHelp()) {
|
|
29558
29619
|
console.log([
|
|
@@ -29707,7 +29768,7 @@ Run 'specialists help' to see available commands.`);
|
|
|
29707
29768
|
const server = new SpecialistsServer;
|
|
29708
29769
|
await server.start();
|
|
29709
29770
|
}
|
|
29710
|
-
|
|
29771
|
+
run19().catch((error2) => {
|
|
29711
29772
|
logger.error(`Fatal error: ${error2}`);
|
|
29712
29773
|
process.exit(1);
|
|
29713
29774
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaggerxtrm/specialists",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.4",
|
|
4
4
|
"description": "OmniSpecialist — 7-tool MCP orchestration layer powered by the Specialist System. Discover and execute .specialist.yaml files across project/user/system scopes via pi.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|