@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.
Files changed (2) hide show
  1. package/dist/index.js +340 -279
  2. 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 { gen, data, $data, schema, schemaCode, it } = cxt;
8546
+ const { data, $data, schema, schemaCode, it } = cxt;
8548
8547
  const u = it.opts.unicodeRegExp ? "u" : "";
8549
- if ($data) {
8550
- const { regExp } = it.opts.code;
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
- await this._donePromise?.catch(() => {});
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
- try {
18735
- execFileSync("mkfifo", [fifoPath]);
18736
- this.updateStatus(id, { fifo_path: fifoPath });
18737
- } catch {}
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
- const rl = createInterface({ input: createReadStream(fifoPath, { flags: "r+" }) });
18783
- rl.on("line", (line) => {
18784
- try {
18785
- const parsed = JSON.parse(line);
18786
- if (parsed?.type === "steer" && typeof parsed.message === "string") {
18787
- steerFn?.(parsed.message).catch(() => {});
18788
- } else if (parsed?.type === "prompt" && typeof parsed.message === "string") {
18789
- if (resumeFn) {
18790
- this.updateStatus(id, { status: "running", current_event: "starting" });
18791
- resumeFn(parsed.message).then((output) => {
18792
- writeFileSync(this.resultPath(id), output, "utf-8");
18793
- this.updateStatus(id, {
18794
- status: "waiting",
18795
- current_event: "waiting",
18796
- elapsed_s: Math.round((Date.now() - startedAtMs) / 1000),
18797
- last_event_at_ms: Date.now()
18798
- });
18799
- }).catch((err) => {
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
- } else if (parsed?.type === "close") {
18804
- closeFn?.().catch(() => {});
18814
+ }).catch((err) => {
18815
+ this.updateStatus(id, { status: "error", error: err?.message ?? String(err) });
18816
+ });
18805
18817
  }
18806
- } catch {}
18807
- });
18808
- rl.on("error", () => {});
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] [--background] [--follow]');
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, background, follow, keepAlive, contextDepth };
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
- if (args.background || args.follow) {
19619
- const jobsDir = join10(process.cwd(), ".specialists", "jobs");
19620
- const supervisor = new Supervisor({
19621
- runner,
19622
- runOptions: {
19623
- name: args.name,
19624
- prompt,
19625
- variables,
19626
- backendOverride: args.model,
19627
- inputBeadId: args.beadId,
19628
- keepAlive: args.keepAlive
19629
- },
19630
- jobsDir,
19631
- beadsClient
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
- `)), (killFn) => {
19684
- process.on("SIGINT", () => {
19685
- process.stderr.write(`
19647
+ `))
19648
+ });
19649
+ process.stderr.write(`
19650
+ ${bold5(`Running ${cyan3(args.name)}`)}
19686
19651
 
19687
- Interrupted.
19688
19652
  `);
19689
- killFn();
19690
- process.exit(130);
19691
- });
19692
- }, (beadId) => {
19693
- trackingBeadId = beadId;
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
- const secs = (result.durationMs / 1000).toFixed(1);
19703
- const effectiveBeadId = args.beadId ?? trackingBeadId;
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
- effectiveBeadId ? `bead ${effectiveBeadId}` : "",
19706
- `${secs}s`,
19707
- dim5(result.model)
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: () => run11
20442
+ run: () => run12
20366
20443
  });
20367
- import { join as join15 } from "node:path";
20444
+ import { join as join16 } from "node:path";
20368
20445
  import { writeFileSync as writeFileSync6 } from "node:fs";
20369
- async function run11() {
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 = join15(process.cwd(), ".specialists", "jobs");
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: () => run12
20492
+ run: () => run13
20416
20493
  });
20417
- import { join as join16 } from "node:path";
20494
+ import { join as join17 } from "node:path";
20418
20495
  import { writeFileSync as writeFileSync7 } from "node:fs";
20419
- async function run12() {
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 = join16(process.cwd(), ".specialists", "jobs");
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: () => run13
20544
+ run: () => run14
20468
20545
  });
20469
- import { join as join17 } from "node:path";
20470
- async function run13() {
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 = join17(process.cwd(), ".specialists", "jobs");
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: () => run14
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 run14() {
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: () => run15
20824
+ run: () => run16
20748
20825
  });
20749
20826
  import { spawnSync as spawnSync7 } from "node:child_process";
20750
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
20751
- import { join as join18 } from "node:path";
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 (!existsSync11(path))
20857
+ if (!existsSync12(path))
20781
20858
  return null;
20782
20859
  try {
20783
- return JSON.parse(readFileSync7(path, "utf8"));
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 (existsSync11(join18(CWD, ".beads")))
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 = join18(HOOKS_DIR, name);
20837
- if (!existsSync11(dest)) {
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 = join18(CWD, ".specialists");
20882
- const jobsDir = join18(rootDir, "jobs");
20883
- const readyDir = join18(rootDir, "ready");
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 (!existsSync11(rootDir)) {
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 (!existsSync11(subDir)) {
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 = join18(CWD, ".specialists", "jobs");
20906
- if (!existsSync11(jobsDir)) {
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 = join18(jobsDir, jobId, "status.json");
20925
- if (!existsSync11(statusPath))
21001
+ const statusPath = join19(jobsDir, jobId, "status.json");
21002
+ if (!existsSync12(statusPath))
20926
21003
  continue;
20927
21004
  try {
20928
- const status = JSON.parse(readFileSync7(statusPath, "utf8"));
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 run15() {
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 = join18(CWD, ".claude");
20980
- SPECIALISTS_DIR = join18(CWD, ".specialists");
20981
- HOOKS_DIR = join18(SPECIALISTS_DIR, "default", "hooks");
20982
- SETTINGS_FILE = join18(CLAUDE_DIR, "settings.json");
20983
- MCP_FILE2 = join18(CWD, ".mcp.json");
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: () => run16
21070
+ run: () => run17
20994
21071
  });
20995
- import { existsSync as existsSync12, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "node:fs";
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 join19, resolve as resolve2 } from "node:path";
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 join19(homedir3(), ".claude", "CLAUDE.md");
21084
+ return join20(homedir3(), ".claude", "CLAUDE.md");
21008
21085
  case "agents":
21009
- return join19(process.cwd(), "AGENTS.md");
21086
+ return join20(process.cwd(), "AGENTS.md");
21010
21087
  case "project":
21011
21088
  default:
21012
- return join19(process.cwd(), "CLAUDE.md");
21089
+ return join20(process.cwd(), "CLAUDE.md");
21013
21090
  }
21014
21091
  }
21015
- function parseArgs6() {
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 run16() {
21041
- const { target, dryRun } = parseArgs6();
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 (existsSync12(filePath)) {
21050
- const existing = readFileSync8(filePath, "utf8");
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: () => run17
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 run17() {
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] [--background] [--follow]",
21182
- " specialists feed -f",
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
- " --follow runs in background and streams output live",
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 --background",
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 feed -f",
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 Background job monitoring details",
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
- ["result", "Print final output of a completed background job"],
21237
- ["steer", "Send a mid-run message to a running background job"],
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 background job"],
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 run18() {
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 in foreground or background.",
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 --background",
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
- run18().catch((error2) => {
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.2",
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",