@jaggerxtrm/specialists 3.3.1 → 3.3.3

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 (38) hide show
  1. package/config/hooks/specialists-complete.mjs +60 -0
  2. package/config/hooks/specialists-session-start.mjs +120 -0
  3. package/config/skills/specialists-creator/SKILL.md +506 -0
  4. package/config/skills/specialists-creator/scripts/validate-specialist.ts +41 -0
  5. package/config/skills/specialists-usage-workspace/iteration-1/eval-bead-background/old_skill/outputs/result.md +105 -0
  6. package/config/skills/specialists-usage-workspace/iteration-1/eval-bead-background/with_skill/outputs/result.md +93 -0
  7. package/config/skills/specialists-usage-workspace/iteration-1/eval-fresh-setup/old_skill/outputs/result.md +113 -0
  8. package/config/skills/specialists-usage-workspace/iteration-1/eval-fresh-setup/with_skill/outputs/result.md +131 -0
  9. package/config/skills/specialists-usage-workspace/iteration-1/eval-yaml-debug/old_skill/outputs/result.md +159 -0
  10. package/config/skills/specialists-usage-workspace/iteration-1/eval-yaml-debug/with_skill/outputs/result.md +150 -0
  11. package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/with_skill/outputs/result.md +180 -0
  12. package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/with_skill/timing.json +5 -0
  13. package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/without_skill/outputs/result.md +223 -0
  14. package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/without_skill/timing.json +5 -0
  15. package/config/skills/specialists-usage-workspace/iteration-2/eval-code-review/with_skill/timing.json +5 -0
  16. package/config/skills/specialists-usage-workspace/iteration-2/eval-code-review/without_skill/outputs/result.md +146 -0
  17. package/config/skills/specialists-usage-workspace/iteration-2/eval-code-review/without_skill/timing.json +5 -0
  18. package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/with_skill/outputs/result.md +89 -0
  19. package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/with_skill/timing.json +5 -0
  20. package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/without_skill/outputs/result.md +96 -0
  21. package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/without_skill/timing.json +5 -0
  22. package/config/skills/specialists-usage-workspace/skill-snapshot/SKILL.md.old +237 -0
  23. package/config/skills/using-specialists/SKILL.md +158 -0
  24. package/config/skills/using-specialists/evals/evals.json +68 -0
  25. package/config/specialists/.serena/project.yml +151 -0
  26. package/config/specialists/auto-remediation.specialist.yaml +70 -0
  27. package/config/specialists/bug-hunt.specialist.yaml +96 -0
  28. package/config/specialists/explorer.specialist.yaml +79 -0
  29. package/config/specialists/memory-processor.specialist.yaml +140 -0
  30. package/config/specialists/overthinker.specialist.yaml +63 -0
  31. package/config/specialists/parallel-runner.specialist.yaml +61 -0
  32. package/config/specialists/planner.specialist.yaml +87 -0
  33. package/config/specialists/specialists-creator.specialist.yaml +82 -0
  34. package/config/specialists/sync-docs.specialist.yaml +53 -0
  35. package/config/specialists/test-runner.specialist.yaml +58 -0
  36. package/config/specialists/xt-merge.specialist.yaml +78 -0
  37. package/dist/index.js +246 -214
  38. package/package.json +2 -3
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;
@@ -18571,6 +18562,7 @@ var init_timeline_events = __esm(() => {
18571
18562
  import {
18572
18563
  closeSync,
18573
18564
  existsSync as existsSync4,
18565
+ fsyncSync,
18574
18566
  mkdirSync,
18575
18567
  openSync,
18576
18568
  readdirSync,
@@ -18727,7 +18719,9 @@ class Supervisor {
18727
18719
  try {
18728
18720
  writeSync(eventsFd, JSON.stringify(event) + `
18729
18721
  `);
18730
- } catch {}
18722
+ } catch (err) {
18723
+ console.error(`[supervisor] Failed to write event: ${err?.message ?? err}`);
18724
+ }
18731
18725
  };
18732
18726
  appendTimelineEvent(createRunStartEvent(runOptions.name));
18733
18727
  const fifoPath = join3(dir, "steer.pipe");
@@ -18846,6 +18840,9 @@ class Supervisor {
18846
18840
  throw err;
18847
18841
  } finally {
18848
18842
  process.removeListener("SIGTERM", sigtermHandler);
18843
+ try {
18844
+ fsyncSync(eventsFd);
18845
+ } catch {}
18849
18846
  closeSync(eventsFd);
18850
18847
  try {
18851
18848
  if (existsSync4(fifoPath))
@@ -19511,20 +19508,17 @@ var exports_run = {};
19511
19508
  __export(exports_run, {
19512
19509
  run: () => run7
19513
19510
  });
19514
- import { spawn as spawn2 } from "node:child_process";
19515
19511
  import { join as join10 } from "node:path";
19516
19512
  async function parseArgs4(argv) {
19517
19513
  const name = argv[0];
19518
19514
  if (!name || name.startsWith("--")) {
19519
- console.error('Usage: specialists|sp run <name> [--prompt "..."] [--bead <id>] [--context-depth <n>] [--model <model>] [--no-beads] [--background] [--follow]');
19515
+ console.error('Usage: specialists|sp run <name> [--prompt "..."] [--bead <id>] [--context-depth <n>] [--model <model>] [--no-beads] [--keep-alive]');
19520
19516
  process.exit(1);
19521
19517
  }
19522
19518
  let prompt = "";
19523
19519
  let beadId;
19524
19520
  let model;
19525
19521
  let noBeads = false;
19526
- let background = false;
19527
- let follow = false;
19528
19522
  let keepAlive = false;
19529
19523
  let contextDepth = 1;
19530
19524
  for (let i = 1;i < argv.length; i++) {
@@ -19549,14 +19543,6 @@ async function parseArgs4(argv) {
19549
19543
  noBeads = true;
19550
19544
  continue;
19551
19545
  }
19552
- if (token === "--background") {
19553
- background = true;
19554
- continue;
19555
- }
19556
- if (token === "--follow") {
19557
- follow = true;
19558
- continue;
19559
- }
19560
19546
  if (token === "--keep-alive") {
19561
19547
  keepAlive = true;
19562
19548
  continue;
@@ -19580,7 +19566,7 @@ async function parseArgs4(argv) {
19580
19566
  console.error("Error: provide --prompt, pipe stdin, or use --bead <id>.");
19581
19567
  process.exit(1);
19582
19568
  }
19583
- return { name, prompt, beadId, model, noBeads, background, follow, keepAlive, contextDepth };
19569
+ return { name, prompt, beadId, model, noBeads, keepAlive, contextDepth };
19584
19570
  }
19585
19571
  async function run7() {
19586
19572
  const args = await parseArgs4(process.argv.slice(3));
@@ -19615,57 +19601,6 @@ async function run7() {
19615
19601
  circuitBreaker,
19616
19602
  beadsClient
19617
19603
  });
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
19604
  process.stderr.write(`
19670
19605
  ${bold5(`Running ${cyan3(args.name)}`)}
19671
19606
 
@@ -19717,7 +19652,6 @@ var init_run = __esm(() => {
19717
19652
  init_runner();
19718
19653
  init_hooks();
19719
19654
  init_beads();
19720
- init_supervisor();
19721
19655
  });
19722
19656
 
19723
19657
  // src/cli/format-helpers.ts
@@ -20058,6 +19992,9 @@ function readJobEvents(jobDir) {
20058
19992
  events.sort(compareTimelineEvents);
20059
19993
  return events;
20060
19994
  }
19995
+ function readJobEventsById(jobsDir, jobId) {
19996
+ return readJobEvents(join13(jobsDir, jobId));
19997
+ }
20061
19998
  function readAllJobEvents(jobsDir) {
20062
19999
  if (!existsSync9(jobsDir))
20063
20000
  return [];
@@ -20359,21 +20296,132 @@ var init_feed = __esm(() => {
20359
20296
  init_format_helpers();
20360
20297
  });
20361
20298
 
20299
+ // src/cli/poll.ts
20300
+ var exports_poll = {};
20301
+ __export(exports_poll, {
20302
+ run: () => run11
20303
+ });
20304
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
20305
+ import { join as join15 } from "node:path";
20306
+ function parseArgs6(argv) {
20307
+ let jobId;
20308
+ let cursor = 0;
20309
+ let json = false;
20310
+ for (let i = 0;i < argv.length; i++) {
20311
+ if (argv[i] === "--cursor" && argv[i + 1]) {
20312
+ cursor = parseInt(argv[++i], 10);
20313
+ if (isNaN(cursor) || cursor < 0)
20314
+ cursor = 0;
20315
+ continue;
20316
+ }
20317
+ if (argv[i] === "--json") {
20318
+ json = true;
20319
+ continue;
20320
+ }
20321
+ if (!argv[i].startsWith("--")) {
20322
+ jobId = argv[i];
20323
+ }
20324
+ }
20325
+ if (!jobId) {
20326
+ console.error("Usage: specialists poll <job-id> [--cursor N] [--json]");
20327
+ process.exit(1);
20328
+ }
20329
+ return { jobId, cursor, json };
20330
+ }
20331
+ async function run11() {
20332
+ const { jobId, cursor, json } = parseArgs6(process.argv.slice(3));
20333
+ const jobsDir = join15(process.cwd(), ".specialists", "jobs");
20334
+ const jobDir = join15(jobsDir, jobId);
20335
+ if (!existsSync11(jobDir)) {
20336
+ const result2 = {
20337
+ job_id: jobId,
20338
+ status: "error",
20339
+ elapsed_ms: 0,
20340
+ cursor: 0,
20341
+ output: "",
20342
+ output_delta: "",
20343
+ events: [],
20344
+ error: `Job not found: ${jobId}`
20345
+ };
20346
+ console.log(JSON.stringify(result2));
20347
+ process.exit(1);
20348
+ }
20349
+ const statusPath = join15(jobDir, "status.json");
20350
+ let status = null;
20351
+ if (existsSync11(statusPath)) {
20352
+ try {
20353
+ status = JSON.parse(readFileSync7(statusPath, "utf-8"));
20354
+ } catch {}
20355
+ }
20356
+ const resultPath = join15(jobDir, "result.txt");
20357
+ let output = "";
20358
+ if (existsSync11(resultPath)) {
20359
+ try {
20360
+ output = readFileSync7(resultPath, "utf-8");
20361
+ } catch {}
20362
+ }
20363
+ const events = readJobEventsById(jobsDir, jobId);
20364
+ const newEvents = events.slice(cursor);
20365
+ const nextCursor = events.length;
20366
+ const startedAt = status?.started_at_ms ?? Date.now();
20367
+ const lastEvent = status?.last_event_at_ms ?? Date.now();
20368
+ const elapsedMs = status?.status === "done" || status?.status === "error" ? lastEvent - startedAt : Date.now() - startedAt;
20369
+ const result = {
20370
+ job_id: jobId,
20371
+ status: status?.status ?? "starting",
20372
+ elapsed_ms: elapsedMs,
20373
+ cursor: nextCursor,
20374
+ output: status?.status === "done" ? output : "",
20375
+ output_delta: "",
20376
+ events: newEvents,
20377
+ current_event: status?.current_event,
20378
+ current_tool: status?.current_tool,
20379
+ model: status?.model,
20380
+ backend: status?.backend,
20381
+ bead_id: status?.bead_id,
20382
+ error: status?.error
20383
+ };
20384
+ if (json) {
20385
+ console.log(JSON.stringify(result, null, 2));
20386
+ } else {
20387
+ console.log(`Job: ${jobId}`);
20388
+ console.log(`Status: ${result.status}`);
20389
+ console.log(`Elapsed: ${Math.round(result.elapsed_ms / 1000)}s`);
20390
+ if (result.model)
20391
+ console.log(`Model: ${result.backend}/${result.model}`);
20392
+ if (result.bead_id)
20393
+ console.log(`Bead: ${result.bead_id}`);
20394
+ if (result.current_tool)
20395
+ console.log(`Tool: ${result.current_tool}`);
20396
+ if (result.error)
20397
+ console.log(`Error: ${result.error}`);
20398
+ console.log(`Events: ${newEvents.length} new (cursor: ${cursor} → ${nextCursor})`);
20399
+ if (result.status === "done" && result.output) {
20400
+ console.log(`
20401
+ --- Output ---`);
20402
+ console.log(result.output.slice(0, 500) + (result.output.length > 500 ? "..." : ""));
20403
+ }
20404
+ }
20405
+ }
20406
+ var init_poll = __esm(() => {
20407
+ init_timeline_query();
20408
+ });
20409
+
20362
20410
  // src/cli/steer.ts
20363
20411
  var exports_steer = {};
20364
20412
  __export(exports_steer, {
20365
- run: () => run11
20413
+ run: () => run12
20366
20414
  });
20367
- import { join as join15 } from "node:path";
20415
+ import { join as join16 } from "node:path";
20368
20416
  import { writeFileSync as writeFileSync6 } from "node:fs";
20369
- async function run11() {
20417
+ async function run12() {
20370
20418
  const jobId = process.argv[3];
20371
20419
  const message = process.argv[4];
20372
20420
  if (!jobId || !message) {
20373
20421
  console.error('Usage: specialists|sp steer <job-id> "<message>"');
20374
20422
  process.exit(1);
20375
20423
  }
20376
- const jobsDir = join15(process.cwd(), ".specialists", "jobs");
20424
+ const jobsDir = join16(process.cwd(), ".specialists", "jobs");
20377
20425
  const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
20378
20426
  const status = supervisor.readStatus(jobId);
20379
20427
  if (!status) {
@@ -20412,18 +20460,18 @@ var init_steer = __esm(() => {
20412
20460
  // src/cli/follow-up.ts
20413
20461
  var exports_follow_up = {};
20414
20462
  __export(exports_follow_up, {
20415
- run: () => run12
20463
+ run: () => run13
20416
20464
  });
20417
- import { join as join16 } from "node:path";
20465
+ import { join as join17 } from "node:path";
20418
20466
  import { writeFileSync as writeFileSync7 } from "node:fs";
20419
- async function run12() {
20467
+ async function run13() {
20420
20468
  const jobId = process.argv[3];
20421
20469
  const message = process.argv[4];
20422
20470
  if (!jobId || !message) {
20423
20471
  console.error('Usage: specialists|sp follow-up <job-id> "<message>"');
20424
20472
  process.exit(1);
20425
20473
  }
20426
- const jobsDir = join16(process.cwd(), ".specialists", "jobs");
20474
+ const jobsDir = join17(process.cwd(), ".specialists", "jobs");
20427
20475
  const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
20428
20476
  const status = supervisor.readStatus(jobId);
20429
20477
  if (!status) {
@@ -20464,16 +20512,16 @@ var init_follow_up = __esm(() => {
20464
20512
  // src/cli/stop.ts
20465
20513
  var exports_stop = {};
20466
20514
  __export(exports_stop, {
20467
- run: () => run13
20515
+ run: () => run14
20468
20516
  });
20469
- import { join as join17 } from "node:path";
20470
- async function run13() {
20517
+ import { join as join18 } from "node:path";
20518
+ async function run14() {
20471
20519
  const jobId = process.argv[3];
20472
20520
  if (!jobId) {
20473
20521
  console.error("Usage: specialists|sp stop <job-id>");
20474
20522
  process.exit(1);
20475
20523
  }
20476
- const jobsDir = join17(process.cwd(), ".specialists", "jobs");
20524
+ const jobsDir = join18(process.cwd(), ".specialists", "jobs");
20477
20525
  const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
20478
20526
  const status = supervisor.readStatus(jobId);
20479
20527
  if (!status) {
@@ -20513,7 +20561,7 @@ var init_stop = __esm(() => {
20513
20561
  // src/cli/quickstart.ts
20514
20562
  var exports_quickstart = {};
20515
20563
  __export(exports_quickstart, {
20516
- run: () => run14
20564
+ run: () => run15
20517
20565
  });
20518
20566
  function section2(title) {
20519
20567
  const bar = "─".repeat(60);
@@ -20527,7 +20575,7 @@ function cmd2(s) {
20527
20575
  function flag(s) {
20528
20576
  return green10(s);
20529
20577
  }
20530
- async function run14() {
20578
+ async function run15() {
20531
20579
  const lines = [
20532
20580
  "",
20533
20581
  bold7("specialists · Quick Start Guide"),
@@ -20744,11 +20792,11 @@ var bold7 = (s) => `\x1B[1m${s}\x1B[0m`, dim9 = (s) => `\x1B[2m${s}\x1B[0m`, yel
20744
20792
  // src/cli/doctor.ts
20745
20793
  var exports_doctor = {};
20746
20794
  __export(exports_doctor, {
20747
- run: () => run15
20795
+ run: () => run16
20748
20796
  });
20749
20797
  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";
20798
+ import { existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync as readdirSync4 } from "node:fs";
20799
+ import { join as join19 } from "node:path";
20752
20800
  function ok3(msg) {
20753
20801
  console.log(` ${green11("✓")} ${msg}`);
20754
20802
  }
@@ -20777,10 +20825,10 @@ function isInstalled2(bin) {
20777
20825
  return spawnSync7("which", [bin], { encoding: "utf8", timeout: 2000 }).status === 0;
20778
20826
  }
20779
20827
  function loadJson2(path) {
20780
- if (!existsSync11(path))
20828
+ if (!existsSync12(path))
20781
20829
  return null;
20782
20830
  try {
20783
- return JSON.parse(readFileSync7(path, "utf8"));
20831
+ return JSON.parse(readFileSync8(path, "utf8"));
20784
20832
  } catch {
20785
20833
  return null;
20786
20834
  }
@@ -20813,7 +20861,7 @@ function checkBd() {
20813
20861
  return false;
20814
20862
  }
20815
20863
  ok3(`bd installed ${dim10(sp("bd", ["--version"]).stdout || "")}`);
20816
- if (existsSync11(join18(CWD, ".beads")))
20864
+ if (existsSync12(join19(CWD, ".beads")))
20817
20865
  ok3(".beads/ present in project");
20818
20866
  else
20819
20867
  warn2(".beads/ not found in project");
@@ -20833,8 +20881,8 @@ function checkHooks() {
20833
20881
  section3("Claude Code hooks (2 expected)");
20834
20882
  let allPresent = true;
20835
20883
  for (const name of HOOK_NAMES) {
20836
- const dest = join18(HOOKS_DIR, name);
20837
- if (!existsSync11(dest)) {
20884
+ const dest = join19(HOOKS_DIR, name);
20885
+ if (!existsSync12(dest)) {
20838
20886
  fail2(`${name} ${red6("missing")}`);
20839
20887
  fix("specialists install");
20840
20888
  allPresent = false;
@@ -20878,18 +20926,18 @@ function checkMCP() {
20878
20926
  }
20879
20927
  function checkRuntimeDirs() {
20880
20928
  section3(".specialists/ runtime directories");
20881
- const rootDir = join18(CWD, ".specialists");
20882
- const jobsDir = join18(rootDir, "jobs");
20883
- const readyDir = join18(rootDir, "ready");
20929
+ const rootDir = join19(CWD, ".specialists");
20930
+ const jobsDir = join19(rootDir, "jobs");
20931
+ const readyDir = join19(rootDir, "ready");
20884
20932
  let allOk = true;
20885
- if (!existsSync11(rootDir)) {
20933
+ if (!existsSync12(rootDir)) {
20886
20934
  warn2(".specialists/ not found in current project");
20887
20935
  fix("specialists init");
20888
20936
  allOk = false;
20889
20937
  } else {
20890
20938
  ok3(".specialists/ present");
20891
20939
  for (const [subDir, label] of [[jobsDir, "jobs"], [readyDir, "ready"]]) {
20892
- if (!existsSync11(subDir)) {
20940
+ if (!existsSync12(subDir)) {
20893
20941
  warn2(`.specialists/${label}/ missing — auto-creating`);
20894
20942
  mkdirSync3(subDir, { recursive: true });
20895
20943
  ok3(`.specialists/${label}/ created`);
@@ -20902,8 +20950,8 @@ function checkRuntimeDirs() {
20902
20950
  }
20903
20951
  function checkZombieJobs() {
20904
20952
  section3("Background jobs");
20905
- const jobsDir = join18(CWD, ".specialists", "jobs");
20906
- if (!existsSync11(jobsDir)) {
20953
+ const jobsDir = join19(CWD, ".specialists", "jobs");
20954
+ if (!existsSync12(jobsDir)) {
20907
20955
  hint("No .specialists/jobs/ — skipping");
20908
20956
  return true;
20909
20957
  }
@@ -20921,11 +20969,11 @@ function checkZombieJobs() {
20921
20969
  let total = 0;
20922
20970
  let running = 0;
20923
20971
  for (const jobId of entries) {
20924
- const statusPath = join18(jobsDir, jobId, "status.json");
20925
- if (!existsSync11(statusPath))
20972
+ const statusPath = join19(jobsDir, jobId, "status.json");
20973
+ if (!existsSync12(statusPath))
20926
20974
  continue;
20927
20975
  try {
20928
- const status = JSON.parse(readFileSync7(statusPath, "utf8"));
20976
+ const status = JSON.parse(readFileSync8(statusPath, "utf8"));
20929
20977
  total++;
20930
20978
  if (status.status === "running" || status.status === "starting") {
20931
20979
  const pid = status.pid;
@@ -20952,7 +21000,7 @@ function checkZombieJobs() {
20952
21000
  }
20953
21001
  return zombies === 0;
20954
21002
  }
20955
- async function run15() {
21003
+ async function run16() {
20956
21004
  console.log(`
20957
21005
  ${bold8("specialists doctor")}
20958
21006
  `);
@@ -20976,11 +21024,11 @@ ${bold8("specialists doctor")}
20976
21024
  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
21025
  var init_doctor = __esm(() => {
20978
21026
  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");
21027
+ CLAUDE_DIR = join19(CWD, ".claude");
21028
+ SPECIALISTS_DIR = join19(CWD, ".specialists");
21029
+ HOOKS_DIR = join19(SPECIALISTS_DIR, "default", "hooks");
21030
+ SETTINGS_FILE = join19(CLAUDE_DIR, "settings.json");
21031
+ MCP_FILE2 = join19(CWD, ".mcp.json");
20984
21032
  HOOK_NAMES = [
20985
21033
  "specialists-complete.mjs",
20986
21034
  "specialists-session-start.mjs"
@@ -20990,11 +21038,11 @@ var init_doctor = __esm(() => {
20990
21038
  // src/cli/setup.ts
20991
21039
  var exports_setup = {};
20992
21040
  __export(exports_setup, {
20993
- run: () => run16
21041
+ run: () => run17
20994
21042
  });
20995
- import { existsSync as existsSync12, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "node:fs";
21043
+ import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "node:fs";
20996
21044
  import { homedir as homedir3 } from "node:os";
20997
- import { join as join19, resolve as resolve2 } from "node:path";
21045
+ import { join as join20, resolve as resolve2 } from "node:path";
20998
21046
  function ok4(msg) {
20999
21047
  console.log(` ${green12("✓")} ${msg}`);
21000
21048
  }
@@ -21004,15 +21052,15 @@ function skip2(msg) {
21004
21052
  function resolveTarget(target) {
21005
21053
  switch (target) {
21006
21054
  case "global":
21007
- return join19(homedir3(), ".claude", "CLAUDE.md");
21055
+ return join20(homedir3(), ".claude", "CLAUDE.md");
21008
21056
  case "agents":
21009
- return join19(process.cwd(), "AGENTS.md");
21057
+ return join20(process.cwd(), "AGENTS.md");
21010
21058
  case "project":
21011
21059
  default:
21012
- return join19(process.cwd(), "CLAUDE.md");
21060
+ return join20(process.cwd(), "CLAUDE.md");
21013
21061
  }
21014
21062
  }
21015
- function parseArgs6() {
21063
+ function parseArgs7() {
21016
21064
  const argv = process.argv.slice(3);
21017
21065
  let target = "project";
21018
21066
  let dryRun = false;
@@ -21037,8 +21085,8 @@ function parseArgs6() {
21037
21085
  }
21038
21086
  return { target, dryRun };
21039
21087
  }
21040
- async function run16() {
21041
- const { target, dryRun } = parseArgs6();
21088
+ async function run17() {
21089
+ const { target, dryRun } = parseArgs7();
21042
21090
  const filePath = resolve2(resolveTarget(target));
21043
21091
  const label = target === "global" ? "~/.claude/CLAUDE.md" : filePath.replace(process.cwd() + "/", "");
21044
21092
  console.log(`
@@ -21046,8 +21094,8 @@ ${bold9("specialists setup")}
21046
21094
  `);
21047
21095
  console.log(` Target: ${yellow8(label)}${dryRun ? dim11(" (dry-run)") : ""}
21048
21096
  `);
21049
- if (existsSync12(filePath)) {
21050
- const existing = readFileSync8(filePath, "utf8");
21097
+ if (existsSync13(filePath)) {
21098
+ const existing = readFileSync9(filePath, "utf8");
21051
21099
  if (existing.includes(MARKER)) {
21052
21100
  skip2(`${label} already contains Specialists Workflow section`);
21053
21101
  console.log(`
@@ -21157,13 +21205,13 @@ var init_setup = () => {};
21157
21205
  // src/cli/help.ts
21158
21206
  var exports_help = {};
21159
21207
  __export(exports_help, {
21160
- run: () => run17
21208
+ run: () => run18
21161
21209
  });
21162
21210
  function formatCommands(entries) {
21163
21211
  const width = Math.max(...entries.map(([cmd3]) => cmd3.length));
21164
21212
  return entries.map(([cmd3, desc]) => ` ${cmd3.padEnd(width)} ${desc}`);
21165
21213
  }
21166
- async function run17() {
21214
+ async function run18() {
21167
21215
  const lines = [
21168
21216
  "",
21169
21217
  "Specialists lets you run project-scoped specialist agents with a bead-first workflow.",
@@ -21178,8 +21226,8 @@ async function run17() {
21178
21226
  "",
21179
21227
  " Tracked work (primary)",
21180
21228
  ' bd create "Task title" -t task -p 1 --json',
21181
- " specialists run <name> --bead <id> [--context-depth N] [--background] [--follow]",
21182
- " specialists feed -f",
21229
+ " specialists run <name> --bead <id> [--context-depth N]",
21230
+ " specialists poll <job-id> --json # check status",
21183
21231
  ' bd close <id> --reason "Done"',
21184
21232
  "",
21185
21233
  " Ad-hoc work",
@@ -21190,7 +21238,11 @@ async function run17() {
21190
21238
  " --prompt is for quick untracked work",
21191
21239
  " --context-depth defaults to 1 with --bead",
21192
21240
  " --no-beads does not disable bead reading",
21193
- " --follow runs in background and streams output live",
21241
+ "",
21242
+ " Background execution",
21243
+ " Use Claude Code's native backgrounding (run_in_background: true)",
21244
+ " or run in a separate terminal and poll with:",
21245
+ " specialists poll <job-id> --json",
21194
21246
  "",
21195
21247
  bold10("Core commands:"),
21196
21248
  ...formatCommands(CORE_COMMANDS),
@@ -21204,10 +21256,10 @@ async function run17() {
21204
21256
  bold10("Examples:"),
21205
21257
  " specialists init",
21206
21258
  " specialists list",
21207
- " specialists run bug-hunt --bead unitAI-123 --background",
21208
- " specialists run sync-docs --follow # run + stream live output",
21259
+ " specialists run bug-hunt --bead unitAI-123",
21209
21260
  ' specialists run codebase-explorer --prompt "Map the CLI architecture"',
21210
- " specialists feed -f",
21261
+ " specialists poll abc123 --json # check job status",
21262
+ " specialists feed -f # stream all job events",
21211
21263
  ' specialists steer <job-id> "focus only on supervisor.ts"',
21212
21264
  ' specialists follow-up <job-id> "now write the fix"',
21213
21265
  " specialists result <job-id>",
@@ -21215,10 +21267,11 @@ async function run17() {
21215
21267
  bold10("More help:"),
21216
21268
  " specialists quickstart Full guide and workflow reference",
21217
21269
  " specialists run --help Run command details and flags",
21270
+ " specialists poll --help Job status polling details",
21218
21271
  " specialists steer --help Mid-run steering details",
21219
21272
  " specialists follow-up --help Multi-turn keep-alive details",
21220
21273
  " specialists init --help Bootstrap behavior and workflow injection",
21221
- " specialists feed --help Background job monitoring details",
21274
+ " specialists feed --help Job event streaming details",
21222
21275
  "",
21223
21276
  dim12("Project model: specialists are project-only; user-scope discovery is deprecated."),
21224
21277
  ""
@@ -21233,10 +21286,11 @@ var init_help = __esm(() => {
21233
21286
  ["list", "List specialists in this project"],
21234
21287
  ["run", "Run a specialist with --bead for tracked work or --prompt for ad-hoc work"],
21235
21288
  ["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"],
21289
+ ["poll", "Machine-readable job status polling (for scripts/Claude Code)"],
21290
+ ["result", "Print final output of a completed job"],
21291
+ ["steer", "Send a mid-run message to a running job"],
21238
21292
  ["follow-up", "Send a next-turn prompt to a keep-alive session (retains full context)"],
21239
- ["stop", "Stop a running background job"],
21293
+ ["stop", "Stop a running job"],
21240
21294
  ["status", "Show health, MCP state, and active jobs"],
21241
21295
  ["doctor", "Diagnose installation/runtime problems"],
21242
21296
  ["quickstart", "Full getting-started guide"],
@@ -27284,9 +27338,6 @@ class Protocol {
27284
27338
  }
27285
27339
  }
27286
27340
  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
27341
  this._transport = transport;
27291
27342
  const _onclose = this.transport?.onclose;
27292
27343
  this._transport.onclose = () => {
@@ -27319,10 +27370,6 @@ class Protocol {
27319
27370
  this._progressHandlers.clear();
27320
27371
  this._taskProgressTokens.clear();
27321
27372
  this._pendingDebouncedNotifications.clear();
27322
- for (const controller of this._requestHandlerAbortControllers.values()) {
27323
- controller.abort();
27324
- }
27325
- this._requestHandlerAbortControllers.clear();
27326
27373
  const error2 = McpError.fromError(ErrorCode.ConnectionClosed, "Connection closed");
27327
27374
  this._transport = undefined;
27328
27375
  this.onclose?.();
@@ -27373,8 +27420,6 @@ class Protocol {
27373
27420
  sessionId: capturedTransport?.sessionId,
27374
27421
  _meta: request.params?._meta,
27375
27422
  sendNotification: async (notification) => {
27376
- if (abortController.signal.aborted)
27377
- return;
27378
27423
  const notificationOptions = { relatedRequestId: request.id };
27379
27424
  if (relatedTaskId) {
27380
27425
  notificationOptions.relatedTask = { taskId: relatedTaskId };
@@ -27382,9 +27427,6 @@ class Protocol {
27382
27427
  await this.notification(notification, notificationOptions);
27383
27428
  },
27384
27429
  sendRequest: async (r, resultSchema, options) => {
27385
- if (abortController.signal.aborted) {
27386
- throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
27387
- }
27388
27430
  const requestOptions = { ...options, relatedRequestId: request.id };
27389
27431
  if (relatedTaskId && !requestOptions.relatedTask) {
27390
27432
  requestOptions.relatedTask = { taskId: relatedTaskId };
@@ -27998,62 +28040,6 @@ class ExperimentalServerTasks {
27998
28040
  requestStream(request, resultSchema, options) {
27999
28041
  return this._server.requestStream(request, resultSchema, options);
28000
28042
  }
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
28043
  async getTask(taskId, options) {
28058
28044
  return this._server.getTask({ taskId }, options);
28059
28045
  }
@@ -29293,7 +29279,7 @@ var next = process.argv[3];
29293
29279
  function wantsHelp() {
29294
29280
  return next === "--help" || next === "-h";
29295
29281
  }
29296
- async function run18() {
29282
+ async function run19() {
29297
29283
  if (sub === "install") {
29298
29284
  if (wantsHelp()) {
29299
29285
  console.log([
@@ -29436,7 +29422,7 @@ async function run18() {
29436
29422
  "",
29437
29423
  "Usage: specialists run <name> [options]",
29438
29424
  "",
29439
- "Run a specialist in foreground or background.",
29425
+ "Run a specialist. Streams output to stdout until completion.",
29440
29426
  "",
29441
29427
  "Primary modes:",
29442
29428
  " tracked: specialists run <name> --bead <id>",
@@ -29447,20 +29433,23 @@ async function run18() {
29447
29433
  " --prompt <text> Ad-hoc prompt for untracked work",
29448
29434
  " --context-depth <n> Dependency context depth when using --bead (default: 1)",
29449
29435
  " --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
29436
  " --model <model> Override the configured model for this run",
29437
+ " --keep-alive Keep session alive for follow-up prompts",
29453
29438
  "",
29454
29439
  "Examples:",
29455
29440
  " 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",
29441
+ " specialists run bug-hunt --bead unitAI-55d --context-depth 2",
29458
29442
  ' specialists run code-review --prompt "Audit src/api.ts"',
29459
29443
  " cat brief.md | specialists run report-generator",
29460
29444
  "",
29461
29445
  "Rules:",
29462
29446
  " Use --bead for tracked work.",
29463
29447
  " Use --prompt for quick ad-hoc work.",
29448
+ "",
29449
+ "Background execution:",
29450
+ " Use Claude Code's native backgrounding (run_in_background: true)",
29451
+ " or run in a separate terminal and poll with:",
29452
+ " specialists poll <job-id> --json",
29464
29453
  ""
29465
29454
  ].join(`
29466
29455
  `));
@@ -29553,6 +29542,49 @@ async function run18() {
29553
29542
  const { run: handler } = await Promise.resolve().then(() => (init_feed(), exports_feed));
29554
29543
  return handler();
29555
29544
  }
29545
+ if (sub === "poll") {
29546
+ if (wantsHelp()) {
29547
+ console.log([
29548
+ "",
29549
+ "Usage: specialists poll <job-id> [--cursor N] [--json]",
29550
+ "",
29551
+ "Machine-readable job status polling for scripts and Claude Code.",
29552
+ "Reads from .specialists/jobs/<id>/ files.",
29553
+ "",
29554
+ "Output (JSON mode):",
29555
+ " {",
29556
+ ' "job_id": "abc123",',
29557
+ ' "status": "running" | "done" | "error" | "waiting",',
29558
+ ' "elapsed_ms": 45000,',
29559
+ ' "cursor": 15,',
29560
+ ' "events": [...], // new events since cursor',
29561
+ ' "output": "...", // full output when done',
29562
+ ' "model": "claude-sonnet-4-6",',
29563
+ ' "bead_id": "unitAI-123"',
29564
+ " }",
29565
+ "",
29566
+ "Options:",
29567
+ " --cursor N Event index to start from (default: 0)",
29568
+ " --json Output as JSON (machine-readable)",
29569
+ "",
29570
+ "Examples:",
29571
+ " specialists poll abc123 --json",
29572
+ " specialists poll abc123 --cursor 5 --json",
29573
+ "",
29574
+ "Polling pattern in Claude Code:",
29575
+ " 1. Start job (blocks until done):",
29576
+ " specialists run planner --bead xtrm-p38n.1",
29577
+ " 2. Or use Claude Code native backgrounding",
29578
+ " 3. Poll for incremental status:",
29579
+ " specialists poll <job-id> --json",
29580
+ ""
29581
+ ].join(`
29582
+ `));
29583
+ return;
29584
+ }
29585
+ const { run: pollHandler } = await Promise.resolve().then(() => (init_poll(), exports_poll));
29586
+ return pollHandler();
29587
+ }
29556
29588
  if (sub === "steer") {
29557
29589
  if (wantsHelp()) {
29558
29590
  console.log([
@@ -29707,7 +29739,7 @@ Run 'specialists help' to see available commands.`);
29707
29739
  const server = new SpecialistsServer;
29708
29740
  await server.start();
29709
29741
  }
29710
- run18().catch((error2) => {
29742
+ run19().catch((error2) => {
29711
29743
  logger.error(`Fatal error: ${error2}`);
29712
29744
  process.exit(1);
29713
29745
  });