@jaggerxtrm/specialists 3.9.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/config/mandatory-rules/README.md +4 -0
  2. package/config/mandatory-rules/gitnexus-required.md +12 -1
  3. package/config/mandatory-rules/serena-cheatsheet.md +41 -0
  4. package/config/specialists/debugger.specialist.json +2 -1
  5. package/config/specialists/executor.specialist.json +2 -1
  6. package/config/specialists/explorer.specialist.json +2 -1
  7. package/config/specialists/memory-processor.specialist.json +6 -1
  8. package/config/specialists/overthinker.specialist.json +2 -1
  9. package/config/specialists/planner.specialist.json +6 -1
  10. package/config/specialists/researcher.specialist.json +2 -1
  11. package/config/specialists/reviewer.specialist.json +2 -1
  12. package/config/specialists/specialists-creator.specialist.json +7 -1
  13. package/config/specialists/sync-docs.specialist.json +2 -1
  14. package/config/specialists/test-runner.specialist.json +2 -1
  15. package/dist/index.js +998 -533
  16. package/dist/lib.js +13219 -0
  17. package/dist/types/cli/attach.d.ts +2 -0
  18. package/dist/types/cli/attach.d.ts.map +1 -0
  19. package/dist/types/cli/clean.d.ts +2 -0
  20. package/dist/types/cli/clean.d.ts.map +1 -0
  21. package/dist/types/cli/config.d.ts +2 -0
  22. package/dist/types/cli/config.d.ts.map +1 -0
  23. package/dist/types/cli/db.d.ts +2 -0
  24. package/dist/types/cli/db.d.ts.map +1 -0
  25. package/dist/types/cli/doctor.d.ts +15 -0
  26. package/dist/types/cli/doctor.d.ts.map +1 -0
  27. package/dist/types/cli/edit.d.ts +2 -0
  28. package/dist/types/cli/edit.d.ts.map +1 -0
  29. package/dist/types/cli/end.d.ts +2 -0
  30. package/dist/types/cli/end.d.ts.map +1 -0
  31. package/dist/types/cli/epic.d.ts +8 -0
  32. package/dist/types/cli/epic.d.ts.map +1 -0
  33. package/dist/types/cli/feed.d.ts +19 -0
  34. package/dist/types/cli/feed.d.ts.map +1 -0
  35. package/dist/types/cli/follow-up.d.ts +2 -0
  36. package/dist/types/cli/follow-up.d.ts.map +1 -0
  37. package/dist/types/cli/format-helpers.d.ts +108 -0
  38. package/dist/types/cli/format-helpers.d.ts.map +1 -0
  39. package/dist/types/cli/help.d.ts +2 -0
  40. package/dist/types/cli/help.d.ts.map +1 -0
  41. package/dist/types/cli/init.d.ts +10 -0
  42. package/dist/types/cli/init.d.ts.map +1 -0
  43. package/dist/types/cli/install.d.ts +2 -0
  44. package/dist/types/cli/install.d.ts.map +1 -0
  45. package/dist/types/cli/list-rules.d.ts +2 -0
  46. package/dist/types/cli/list-rules.d.ts.map +1 -0
  47. package/dist/types/cli/list.d.ts +13 -0
  48. package/dist/types/cli/list.d.ts.map +1 -0
  49. package/dist/types/cli/memory.d.ts +2 -0
  50. package/dist/types/cli/memory.d.ts.map +1 -0
  51. package/dist/types/cli/merge.d.ts +79 -0
  52. package/dist/types/cli/merge.d.ts.map +1 -0
  53. package/dist/types/cli/models.d.ts +2 -0
  54. package/dist/types/cli/models.d.ts.map +1 -0
  55. package/dist/types/cli/node.d.ts +2 -0
  56. package/dist/types/cli/node.d.ts.map +1 -0
  57. package/dist/types/cli/poll.d.ts +25 -0
  58. package/dist/types/cli/poll.d.ts.map +1 -0
  59. package/dist/types/cli/ps.d.ts +2 -0
  60. package/dist/types/cli/ps.d.ts.map +1 -0
  61. package/dist/types/cli/quickstart.d.ts +2 -0
  62. package/dist/types/cli/quickstart.d.ts.map +1 -0
  63. package/dist/types/cli/result.d.ts +2 -0
  64. package/dist/types/cli/result.d.ts.map +1 -0
  65. package/dist/types/cli/resume.d.ts +2 -0
  66. package/dist/types/cli/resume.d.ts.map +1 -0
  67. package/dist/types/cli/run.d.ts +2 -0
  68. package/dist/types/cli/run.d.ts.map +1 -0
  69. package/dist/types/cli/script.d.ts +23 -0
  70. package/dist/types/cli/script.d.ts.map +1 -0
  71. package/dist/types/cli/serve.d.ts +17 -0
  72. package/dist/types/cli/serve.d.ts.map +1 -0
  73. package/dist/types/cli/setup.d.ts +2 -0
  74. package/dist/types/cli/setup.d.ts.map +1 -0
  75. package/dist/types/cli/status.d.ts +4 -0
  76. package/dist/types/cli/status.d.ts.map +1 -0
  77. package/dist/types/cli/steer.d.ts +2 -0
  78. package/dist/types/cli/steer.d.ts.map +1 -0
  79. package/dist/types/cli/stop.d.ts +2 -0
  80. package/dist/types/cli/stop.d.ts.map +1 -0
  81. package/dist/types/cli/tmux-utils.d.ts +23 -0
  82. package/dist/types/cli/tmux-utils.d.ts.map +1 -0
  83. package/dist/types/cli/validate.d.ts +11 -0
  84. package/dist/types/cli/validate.d.ts.map +1 -0
  85. package/dist/types/cli/version.d.ts +2 -0
  86. package/dist/types/cli/version.d.ts.map +1 -0
  87. package/dist/types/cli/view.d.ts +2 -0
  88. package/dist/types/cli/view.d.ts.map +1 -0
  89. package/dist/types/constants.d.ts +140 -0
  90. package/dist/types/constants.d.ts.map +1 -0
  91. package/dist/types/index.d.ts +8 -0
  92. package/dist/types/index.d.ts.map +1 -0
  93. package/dist/types/lib.d.ts +5 -0
  94. package/dist/types/lib.d.ts.map +1 -0
  95. package/dist/types/pi/backendMap.d.ts +3 -0
  96. package/dist/types/pi/backendMap.d.ts.map +1 -0
  97. package/dist/types/pi/session.d.ts +209 -0
  98. package/dist/types/pi/session.d.ts.map +1 -0
  99. package/dist/types/server.d.ts +10 -0
  100. package/dist/types/server.d.ts.map +1 -0
  101. package/dist/types/specialist/beads.d.ts +60 -0
  102. package/dist/types/specialist/beads.d.ts.map +1 -0
  103. package/dist/types/specialist/chain-identity.d.ts +18 -0
  104. package/dist/types/specialist/chain-identity.d.ts.map +1 -0
  105. package/dist/types/specialist/epic-lifecycle.d.ts +57 -0
  106. package/dist/types/specialist/epic-lifecycle.d.ts.map +1 -0
  107. package/dist/types/specialist/epic-readiness.d.ts +55 -0
  108. package/dist/types/specialist/epic-readiness.d.ts.map +1 -0
  109. package/dist/types/specialist/epic-reconciler.d.ts +35 -0
  110. package/dist/types/specialist/epic-reconciler.d.ts.map +1 -0
  111. package/dist/types/specialist/hooks.d.ts +44 -0
  112. package/dist/types/specialist/hooks.d.ts.map +1 -0
  113. package/dist/types/specialist/job-control.d.ts +29 -0
  114. package/dist/types/specialist/job-control.d.ts.map +1 -0
  115. package/dist/types/specialist/job-file-output.d.ts +4 -0
  116. package/dist/types/specialist/job-file-output.d.ts.map +1 -0
  117. package/dist/types/specialist/job-root.d.ts +21 -0
  118. package/dist/types/specialist/job-root.d.ts.map +1 -0
  119. package/dist/types/specialist/jobRegistry.d.ts +69 -0
  120. package/dist/types/specialist/jobRegistry.d.ts.map +1 -0
  121. package/dist/types/specialist/json-output.d.ts +2 -0
  122. package/dist/types/specialist/json-output.d.ts.map +1 -0
  123. package/dist/types/specialist/loader.d.ts +48 -0
  124. package/dist/types/specialist/loader.d.ts.map +1 -0
  125. package/dist/types/specialist/mandatory-rules.d.ts +41 -0
  126. package/dist/types/specialist/mandatory-rules.d.ts.map +1 -0
  127. package/dist/types/specialist/memory-retrieval.d.ts +34 -0
  128. package/dist/types/specialist/memory-retrieval.d.ts.map +1 -0
  129. package/dist/types/specialist/model-display.d.ts +4 -0
  130. package/dist/types/specialist/model-display.d.ts.map +1 -0
  131. package/dist/types/specialist/node-contract.d.ts +343 -0
  132. package/dist/types/specialist/node-contract.d.ts.map +1 -0
  133. package/dist/types/specialist/node-resolve.d.ts +5 -0
  134. package/dist/types/specialist/node-resolve.d.ts.map +1 -0
  135. package/dist/types/specialist/node-supervisor.d.ts +198 -0
  136. package/dist/types/specialist/node-supervisor.d.ts.map +1 -0
  137. package/dist/types/specialist/observability-db.d.ts +19 -0
  138. package/dist/types/specialist/observability-db.d.ts.map +1 -0
  139. package/dist/types/specialist/observability-sqlite.d.ts +218 -0
  140. package/dist/types/specialist/observability-sqlite.d.ts.map +1 -0
  141. package/dist/types/specialist/pipeline.d.ts +23 -0
  142. package/dist/types/specialist/pipeline.d.ts.map +1 -0
  143. package/dist/types/specialist/process-liveness.d.ts +2 -0
  144. package/dist/types/specialist/process-liveness.d.ts.map +1 -0
  145. package/dist/types/specialist/runner.d.ts +98 -0
  146. package/dist/types/specialist/runner.d.ts.map +1 -0
  147. package/dist/types/specialist/schema.d.ts +2875 -0
  148. package/dist/types/specialist/schema.d.ts.map +1 -0
  149. package/dist/types/specialist/script-runner.d.ts +47 -0
  150. package/dist/types/specialist/script-runner.d.ts.map +1 -0
  151. package/dist/types/specialist/supervisor.d.ts +142 -0
  152. package/dist/types/specialist/supervisor.d.ts.map +1 -0
  153. package/dist/types/specialist/templateEngine.d.ts +2 -0
  154. package/dist/types/specialist/templateEngine.d.ts.map +1 -0
  155. package/dist/types/specialist/timeline-events.d.ts +563 -0
  156. package/dist/types/specialist/timeline-events.d.ts.map +1 -0
  157. package/dist/types/specialist/timeline-query.d.ts +129 -0
  158. package/dist/types/specialist/timeline-query.d.ts.map +1 -0
  159. package/dist/types/specialist/worktree-gc.d.ts +24 -0
  160. package/dist/types/specialist/worktree-gc.d.ts.map +1 -0
  161. package/dist/types/specialist/worktree.d.ts +69 -0
  162. package/dist/types/specialist/worktree.d.ts.map +1 -0
  163. package/dist/types/tools/specialist/feed_specialist.tool.d.ts +51 -0
  164. package/dist/types/tools/specialist/feed_specialist.tool.d.ts.map +1 -0
  165. package/dist/types/tools/specialist/list_specialists.tool.d.ts +28 -0
  166. package/dist/types/tools/specialist/list_specialists.tool.d.ts.map +1 -0
  167. package/dist/types/tools/specialist/resume_specialist.tool.d.ts +46 -0
  168. package/dist/types/tools/specialist/resume_specialist.tool.d.ts.map +1 -0
  169. package/dist/types/tools/specialist/specialist_init.tool.d.ts +26 -0
  170. package/dist/types/tools/specialist/specialist_init.tool.d.ts.map +1 -0
  171. package/dist/types/tools/specialist/specialist_status.tool.d.ts +33 -0
  172. package/dist/types/tools/specialist/specialist_status.tool.d.ts.map +1 -0
  173. package/dist/types/tools/specialist/steer_specialist.tool.d.ts +38 -0
  174. package/dist/types/tools/specialist/steer_specialist.tool.d.ts.map +1 -0
  175. package/dist/types/tools/specialist/stop_specialist.tool.d.ts +31 -0
  176. package/dist/types/tools/specialist/stop_specialist.tool.d.ts.map +1 -0
  177. package/dist/types/tools/specialist/use_specialist.tool.d.ts +93 -0
  178. package/dist/types/tools/specialist/use_specialist.tool.d.ts.map +1 -0
  179. package/dist/types/utils/circuitBreaker.d.ts +19 -0
  180. package/dist/types/utils/circuitBreaker.d.ts.map +1 -0
  181. package/dist/types/utils/logger.d.ts +11 -0
  182. package/dist/types/utils/logger.d.ts.map +1 -0
  183. package/package.json +13 -2
package/dist/index.js CHANGED
@@ -21306,6 +21306,17 @@ class BeadsClient {
21306
21306
  const reason = `${status}, ${Math.round(durationMs)}ms, ${model}`;
21307
21307
  spawnSync3("bd", ["close", id, "-r", reason], { stdio: "ignore" });
21308
21308
  }
21309
+ closeBeadIfInProgress(id, reason) {
21310
+ if (!this.available || !id)
21311
+ return false;
21312
+ const bead = this.readBead(id);
21313
+ if (!bead)
21314
+ return false;
21315
+ if (bead.status !== "open" && bead.status !== "in_progress")
21316
+ return false;
21317
+ const result = spawnSync3("bd", ["close", id, "-r", reason], { stdio: "ignore" });
21318
+ return result.status === 0;
21319
+ }
21309
21320
  updateBeadNotes(id, notes) {
21310
21321
  if (!this.available || !id || !notes)
21311
21322
  return { ok: false, error: "beads unavailable or empty payload" };
@@ -21521,6 +21532,9 @@ function resolveOutputContractSchema(responseFormat, outputType, outputSchema) {
21521
21532
  }
21522
21533
  return mergedSchema;
21523
21534
  }
21535
+ function shellQuote(value) {
21536
+ return `'${value.replace(/'/g, `'''`)}'`;
21537
+ }
21524
21538
  function buildOutputContractInstruction(responseFormat, outputType, outputSchema) {
21525
21539
  if (responseFormat === "text")
21526
21540
  return "";
@@ -21545,6 +21559,141 @@ function buildOutputContractInstruction(responseFormat, outputType, outputSchema
21545
21559
  ${lines.join(`
21546
21560
  `)}`;
21547
21561
  }
21562
+ function readCommandOutput(cwd, command) {
21563
+ try {
21564
+ return execSync2(command, {
21565
+ cwd,
21566
+ encoding: "utf8",
21567
+ timeout: 1e4,
21568
+ stdio: ["ignore", "pipe", "pipe"]
21569
+ }).trim();
21570
+ } catch {
21571
+ return "";
21572
+ }
21573
+ }
21574
+ function resolveDefaultBranch(cwd) {
21575
+ const headRef = readCommandOutput(cwd, "git symbolic-ref refs/remotes/origin/HEAD");
21576
+ if (headRef) {
21577
+ return headRef.split("/").pop() ?? "main";
21578
+ }
21579
+ const remoteHead = readCommandOutput(cwd, "git remote show origin");
21580
+ const match = remoteHead.match(/HEAD branch:\s*(.+)/);
21581
+ return match?.[1]?.trim() || "main";
21582
+ }
21583
+ function readMergeBase(cwd) {
21584
+ const baseBranch = resolveDefaultBranch(cwd);
21585
+ return readCommandOutput(cwd, `git merge-base ${shellQuote(baseBranch)} HEAD`);
21586
+ }
21587
+ function extractInjectedFileDiff(hunks, file) {
21588
+ const marker = `### ${file}
21589
+ `;
21590
+ const start = hunks.indexOf(marker);
21591
+ if (start < 0)
21592
+ return "";
21593
+ const rest = hunks.slice(start + marker.length);
21594
+ const nextHeader = rest.indexOf(`
21595
+
21596
+ ### `);
21597
+ return (nextHeader >= 0 ? rest.slice(0, nextHeader) : rest).trim();
21598
+ }
21599
+ function parseInjectedReviewerDiffContext(variables) {
21600
+ const source = variables?.reviewer_diff_source?.trim();
21601
+ const stat2 = variables?.reviewer_diff_stat?.trim();
21602
+ const filesRaw = variables?.reviewer_diff_files?.trim();
21603
+ const hunks = variables?.reviewer_diff_hunks?.trim();
21604
+ if (!source || !filesRaw || !hunks)
21605
+ return null;
21606
+ const files = filesRaw.split(`
21607
+ `).map((line) => line.trim()).filter(Boolean);
21608
+ if (files.length === 0)
21609
+ return null;
21610
+ return {
21611
+ source,
21612
+ stat: stat2 || "(no stat)",
21613
+ files,
21614
+ hunks
21615
+ };
21616
+ }
21617
+ function getPatchSources(cwd, variables) {
21618
+ const mergeBase = readMergeBase(cwd);
21619
+ const injectedContext = parseInjectedReviewerDiffContext(variables);
21620
+ return [
21621
+ ...injectedContext ? [{
21622
+ source: injectedContext.source,
21623
+ stat: injectedContext.stat,
21624
+ files: injectedContext.files,
21625
+ diffForFile: (file) => extractInjectedFileDiff(injectedContext.hunks, file)
21626
+ }] : [],
21627
+ {
21628
+ source: "unstaged diff",
21629
+ stat: readCommandOutput(cwd, "git diff --stat"),
21630
+ files: readCommandOutput(cwd, "git diff --name-only").split(`
21631
+ `).map((line) => line.trim()).filter(Boolean),
21632
+ diffForFile: (file) => readCommandOutput(cwd, `git diff -- ${shellQuote(file)}`)
21633
+ },
21634
+ {
21635
+ source: "staged diff",
21636
+ stat: readCommandOutput(cwd, "git diff --cached --stat"),
21637
+ files: readCommandOutput(cwd, "git diff --cached --name-only").split(`
21638
+ `).map((line) => line.trim()).filter(Boolean),
21639
+ diffForFile: (file) => readCommandOutput(cwd, `git diff --cached -- ${shellQuote(file)}`)
21640
+ },
21641
+ {
21642
+ source: "branch-vs-base diff",
21643
+ stat: mergeBase ? readCommandOutput(cwd, `git diff --stat ${shellQuote(mergeBase)}..HEAD`) : "",
21644
+ files: mergeBase ? readCommandOutput(cwd, `git diff --name-only ${shellQuote(mergeBase)}..HEAD`).split(`
21645
+ `).map((line) => line.trim()).filter(Boolean) : [],
21646
+ diffForFile: (file) => mergeBase ? readCommandOutput(cwd, `git diff ${shellQuote(mergeBase)}..HEAD -- ${shellQuote(file)}`) : ""
21647
+ }
21648
+ ];
21649
+ }
21650
+ function buildReviewerDiffContext(cwd, variables, maxFiles = 20) {
21651
+ for (const source of getPatchSources(cwd, variables)) {
21652
+ const files = source.files.slice(0, maxFiles);
21653
+ if (files.length === 0)
21654
+ continue;
21655
+ const hunks = files.map((file) => {
21656
+ const diff = source.diffForFile(file);
21657
+ return diff ? `### ${file}
21658
+ ${diff}` : `### ${file}
21659
+ (no hunks)`;
21660
+ }).join(`
21661
+
21662
+ `);
21663
+ if (hunks.trim()) {
21664
+ return {
21665
+ source: source.source,
21666
+ stat: source.stat,
21667
+ files,
21668
+ hunks
21669
+ };
21670
+ }
21671
+ }
21672
+ throw new Error("Reviewer startup blocked: no patch context found in injected diff, unstaged diff, staged diff, or branch-vs-base diff.");
21673
+ }
21674
+ function buildReviewerDiffInstruction(context) {
21675
+ return `
21676
+
21677
+ ---
21678
+ ## Reviewer Diff Context
21679
+ Review only patch below. Ignore unrelated files, repo-wide exploration, and filesystem hunting.
21680
+ If patch context is empty, stop and fail fast.
21681
+
21682
+ Patch source:
21683
+ ${context.source}
21684
+
21685
+ Diff stat:
21686
+ ${context.stat || "(no stat)"}
21687
+
21688
+ Changed files:
21689
+ ${context.files.map((file) => `- ${file}`).join(`
21690
+ `)}
21691
+
21692
+ Diff hunks:
21693
+ ${context.hunks}
21694
+ ---
21695
+ `;
21696
+ }
21548
21697
  function tryParseJson(input) {
21549
21698
  try {
21550
21699
  return { value: JSON.parse(input) };
@@ -21675,7 +21824,7 @@ class SpecialistRunner {
21675
21824
  if (!bead) {
21676
21825
  return options.prompt;
21677
21826
  }
21678
- const contextDepth = Math.max(0, Math.trunc(options.contextDepth ?? 1));
21827
+ const contextDepth = Math.max(0, Math.trunc(options.contextDepth ?? 3));
21679
21828
  const blockers = contextDepth > 0 ? beadReader.getCompletedBlockers(options.inputBeadId, contextDepth) : [];
21680
21829
  const baseContext = buildBeadContext(bead, blockers);
21681
21830
  return `${baseContext}
@@ -21749,6 +21898,14 @@ ${mandatoryRulesBlock}`;
21749
21898
  } catch (error2) {
21750
21899
  console.warn(`[specialist runner] Skipping MANDATORY_RULES injection: ${String(error2)}`);
21751
21900
  }
21901
+ if (metadata.name === "reviewer") {
21902
+ try {
21903
+ const diffContext = buildReviewerDiffContext(runCwd, variables);
21904
+ renderedTask = `${renderedTask}${buildReviewerDiffInstruction(diffContext)}`;
21905
+ } catch (error2) {
21906
+ console.warn(`[specialist runner] Reviewer diff context unavailable: ${String(error2)}`);
21907
+ }
21908
+ }
21752
21909
  const promptHash = createHash2("sha256").update(renderedTask).digest("hex").slice(0, 16);
21753
21910
  await hooks.emit("post_render", invocationId, metadata.name, metadata.version, {
21754
21911
  prompt_hash: promptHash,
@@ -22109,7 +22266,8 @@ ${outputContractWarnings.map((msg) => ` \u26A0 ${msg}`).join(`
22109
22266
  beadId,
22110
22267
  metrics: runMetrics,
22111
22268
  permissionRequired: execution.permission_required,
22112
- autoCommit: execution.auto_commit
22269
+ autoCommit: execution.auto_commit,
22270
+ outputType
22113
22271
  };
22114
22272
  }
22115
22273
  async startAsync(options, registry2) {
@@ -22393,6 +22551,259 @@ async function run2() {
22393
22551
  }
22394
22552
  var init_version = () => {};
22395
22553
 
22554
+ // src/cli/list-rules.ts
22555
+ var exports_list_rules = {};
22556
+ __export(exports_list_rules, {
22557
+ run: () => run3
22558
+ });
22559
+ import { existsSync as existsSync8, readFileSync as readFileSync5, readdirSync } from "fs";
22560
+ import { resolve as resolve5, join as join8, basename as basename3 } from "path";
22561
+ function discoverRuleSets(cwd) {
22562
+ const seen = new Map;
22563
+ for (const { rel, tier } of RULE_TIERS) {
22564
+ const dir = resolve5(cwd, rel);
22565
+ if (!existsSync8(dir))
22566
+ continue;
22567
+ let files;
22568
+ try {
22569
+ files = readdirSync(dir);
22570
+ } catch {
22571
+ continue;
22572
+ }
22573
+ for (const file of files) {
22574
+ if (!file.endsWith(".md") || file === "README.md")
22575
+ continue;
22576
+ const id = file.replace(/\.md$/, "");
22577
+ if (!seen.has(id))
22578
+ seen.set(id, { id, source_path: join8(dir, file), source_tier: tier });
22579
+ }
22580
+ }
22581
+ return [...seen.values()].sort((a, b) => a.id.localeCompare(b.id));
22582
+ }
22583
+ function discoverSpecialists(cwd) {
22584
+ const seen = new Map;
22585
+ for (const { rel, tier } of SPEC_TIERS) {
22586
+ const dir = resolve5(cwd, rel);
22587
+ if (!existsSync8(dir))
22588
+ continue;
22589
+ let files;
22590
+ try {
22591
+ files = readdirSync(dir);
22592
+ } catch {
22593
+ continue;
22594
+ }
22595
+ for (const file of files) {
22596
+ if (!file.endsWith(".specialist.json"))
22597
+ continue;
22598
+ const name = file.replace(/\.specialist\.json$/, "");
22599
+ if (seen.has(name))
22600
+ continue;
22601
+ const path = join8(dir, file);
22602
+ try {
22603
+ const parsed = JSON.parse(readFileSync5(path, "utf-8"));
22604
+ const spec = parsed?.specialist;
22605
+ if (!spec)
22606
+ continue;
22607
+ const config2 = spec?.mandatory_rules;
22608
+ seen.set(name, {
22609
+ name,
22610
+ source_tier: tier,
22611
+ source_path: path,
22612
+ applied_rules: [],
22613
+ inline_rule_count: Array.isArray(config2?.inline_rules) ? config2.inline_rules.length : 0,
22614
+ globals_disabled: Boolean(config2?.disable_default_globals)
22615
+ });
22616
+ } catch {}
22617
+ }
22618
+ }
22619
+ return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
22620
+ }
22621
+ function appliedRulesForSpec(spec, spec_template_sets, required2, defaults) {
22622
+ const out = new Map;
22623
+ for (const id of required2)
22624
+ out.set(id, { id, scope: "required" });
22625
+ if (!spec.globals_disabled) {
22626
+ for (const id of defaults)
22627
+ if (!out.has(id))
22628
+ out.set(id, { id, scope: "default" });
22629
+ }
22630
+ for (const id of spec_template_sets)
22631
+ if (!out.has(id))
22632
+ out.set(id, { id, scope: "role-specific" });
22633
+ if (spec.inline_rule_count > 0)
22634
+ out.set(`__inline__${spec.name}`, { id: "(inline)", scope: "inline" });
22635
+ return [...out.values()];
22636
+ }
22637
+ function renderMatrix(rules, specs) {
22638
+ const ruleIds = rules.map((r) => r.id);
22639
+ const nameWidth = Math.max(15, ...specs.map((s) => s.name.length));
22640
+ const colWidth = Math.max(4, ...ruleIds.map((id) => Math.min(id.length, 18)));
22641
+ const lines = [];
22642
+ const header = ["specialist".padEnd(nameWidth), ...ruleIds.map((id) => id.slice(0, colWidth).padEnd(colWidth))];
22643
+ lines.push(header.join(" "));
22644
+ lines.push("-".repeat(header.join(" ").length));
22645
+ for (const spec of specs) {
22646
+ const cells = [spec.name.padEnd(nameWidth)];
22647
+ for (const id of ruleIds) {
22648
+ const applied = spec.applied_rules.find((r) => r.id === id);
22649
+ let mark = " . ";
22650
+ if (applied) {
22651
+ mark = applied.scope === "required" ? " R " : applied.scope === "default" ? " D " : " x ";
22652
+ }
22653
+ cells.push(mark.padEnd(colWidth));
22654
+ }
22655
+ lines.push(cells.join(" "));
22656
+ }
22657
+ lines.push("");
22658
+ lines.push(" R = required (always) D = default (unless disable_default_globals) x = role-specific . = not applied");
22659
+ return lines.join(`
22660
+ `);
22661
+ }
22662
+ function parseArgs(argv) {
22663
+ const opts = { json: false };
22664
+ for (let i = 0;i < argv.length; i++) {
22665
+ const t = argv[i];
22666
+ if (t === "--json")
22667
+ opts.json = true;
22668
+ else if (t === "--rule" && argv[i + 1])
22669
+ opts.filterRule = argv[++i];
22670
+ else if (t === "--specialist" && argv[i + 1])
22671
+ opts.filterSpecialist = argv[++i];
22672
+ else if (t === "--help" || t === "-h") {
22673
+ printUsage();
22674
+ process.exit(0);
22675
+ } else {
22676
+ process.stderr.write(`Unknown option: ${t}
22677
+ `);
22678
+ printUsage();
22679
+ process.exit(1);
22680
+ }
22681
+ }
22682
+ return opts;
22683
+ }
22684
+ function printUsage() {
22685
+ console.log([
22686
+ "",
22687
+ "Usage: specialists list-rules [--rule <id>] [--specialist <name>] [--json]",
22688
+ "",
22689
+ "Show which mandatory rules are loaded by which specialists.",
22690
+ "Walks tiers in runner-order: .specialists/ \u2192 .specialists/default/ \u2192 config/.",
22691
+ "",
22692
+ "Options:",
22693
+ " --rule <id> Filter to one rule, list every spec that loads it",
22694
+ " --specialist <name> Filter to one specialist, list every rule applied",
22695
+ " --json Structured output (rules[], specialists[])",
22696
+ "",
22697
+ "Examples:",
22698
+ " specialists list-rules",
22699
+ " specialists list-rules --rule gitnexus-required",
22700
+ " specialists list-rules --specialist reviewer",
22701
+ " specialists list-rules --json | jq .",
22702
+ ""
22703
+ ].join(`
22704
+ `));
22705
+ }
22706
+ async function run3() {
22707
+ const opts = parseArgs(process.argv.slice(3));
22708
+ const cwd = process.cwd();
22709
+ const index = loadMandatoryRulesIndex(cwd);
22710
+ const required2 = index?.required_template_sets ?? [];
22711
+ const defaults = index?.default_template_sets ?? [];
22712
+ const rules = discoverRuleSets(cwd);
22713
+ const specs = discoverSpecialists(cwd);
22714
+ for (const spec of specs) {
22715
+ const parsed = JSON.parse(readFileSync5(spec.source_path, "utf-8"));
22716
+ const sets = parsed?.specialist?.mandatory_rules?.template_sets ?? [];
22717
+ spec.applied_rules = appliedRulesForSpec(spec, sets, required2, defaults);
22718
+ }
22719
+ if (opts.filterRule) {
22720
+ const matchedSpecs = specs.map((s) => ({ name: s.name, source_tier: s.source_tier, scope: s.applied_rules.find((r) => r.id === opts.filterRule)?.scope })).filter((x) => !!x.scope);
22721
+ if (opts.json) {
22722
+ process.stdout.write(JSON.stringify({ rule: opts.filterRule, applied_to: matchedSpecs }, null, 2) + `
22723
+ `);
22724
+ return;
22725
+ }
22726
+ console.log(`
22727
+ Rule: ${opts.filterRule}
22728
+ `);
22729
+ if (matchedSpecs.length === 0) {
22730
+ console.log(" (no specialists pull this rule)");
22731
+ } else {
22732
+ for (const m of matchedSpecs)
22733
+ console.log(` ${m.name.padEnd(20)} (${m.scope}, tier=${m.source_tier})`);
22734
+ }
22735
+ return;
22736
+ }
22737
+ if (opts.filterSpecialist) {
22738
+ const spec = specs.find((s) => s.name === opts.filterSpecialist);
22739
+ if (!spec) {
22740
+ process.stderr.write(`No specialist found: ${opts.filterSpecialist}
22741
+ `);
22742
+ process.exit(1);
22743
+ }
22744
+ if (opts.json) {
22745
+ process.stdout.write(JSON.stringify(spec, null, 2) + `
22746
+ `);
22747
+ return;
22748
+ }
22749
+ console.log(`
22750
+ Specialist: ${spec.name} (tier=${spec.source_tier}, globals_disabled=${spec.globals_disabled})
22751
+ `);
22752
+ if (spec.applied_rules.length === 0) {
22753
+ console.log(" (no rules applied)");
22754
+ } else {
22755
+ for (const r of spec.applied_rules)
22756
+ console.log(` ${r.id.padEnd(28)} ${r.scope}`);
22757
+ }
22758
+ return;
22759
+ }
22760
+ if (opts.json) {
22761
+ process.stdout.write(JSON.stringify({
22762
+ rules: rules.map((r) => ({
22763
+ id: r.id,
22764
+ source_path: r.source_path,
22765
+ source_tier: r.source_tier,
22766
+ scope: required2.includes(r.id) ? "required" : defaults.includes(r.id) ? "default" : "role-specific"
22767
+ })),
22768
+ specialists: specs.map((s) => ({
22769
+ name: s.name,
22770
+ source_tier: s.source_tier,
22771
+ source_path: s.source_path,
22772
+ globals_disabled: s.globals_disabled,
22773
+ inline_rule_count: s.inline_rule_count,
22774
+ applied_rules: s.applied_rules
22775
+ }))
22776
+ }, null, 2) + `
22777
+ `);
22778
+ return;
22779
+ }
22780
+ console.log(`
22781
+ Mandatory rule library (${rules.length} sets, ${specs.length} specialists)
22782
+ `);
22783
+ console.log(renderMatrix(rules, specs));
22784
+ const orphans = rules.filter((r) => !specs.some((s) => s.applied_rules.some((a) => a.id === r.id)) && !required2.includes(r.id) && !defaults.includes(r.id));
22785
+ if (orphans.length > 0) {
22786
+ console.log(`
22787
+ Orphan rules (defined but not loaded by any specialist):`);
22788
+ for (const r of orphans)
22789
+ console.log(` ${r.id} (${basename3(r.source_path)}, tier=${r.source_tier})`);
22790
+ }
22791
+ }
22792
+ var RULE_TIERS, SPEC_TIERS;
22793
+ var init_list_rules = __esm(() => {
22794
+ init_mandatory_rules();
22795
+ RULE_TIERS = [
22796
+ { rel: ".specialists/mandatory-rules", tier: "overlay" },
22797
+ { rel: ".specialists/default/mandatory-rules", tier: "default" },
22798
+ { rel: "config/mandatory-rules", tier: "config" }
22799
+ ];
22800
+ SPEC_TIERS = [
22801
+ { rel: ".specialists/user", tier: "user" },
22802
+ { rel: ".specialists/default", tier: "default" },
22803
+ { rel: "config/specialists", tier: "config" }
22804
+ ];
22805
+ });
22806
+
22396
22807
  // src/specialist/job-file-output.ts
22397
22808
  function normalizeMode(value) {
22398
22809
  return (value ?? "").trim().toLowerCase();
@@ -22837,7 +23248,7 @@ var init_epic_lifecycle = __esm(() => {
22837
23248
  });
22838
23249
 
22839
23250
  // src/specialist/process-liveness.ts
22840
- import { readFileSync as readFileSync5 } from "fs";
23251
+ import { readFileSync as readFileSync6 } from "fs";
22841
23252
  function isValidPid(pid) {
22842
23253
  return Number.isInteger(pid) && pid > 0;
22843
23254
  }
@@ -22851,7 +23262,7 @@ function isAliveBySignal(pid) {
22851
23262
  }
22852
23263
  function parseBootTimeMs() {
22853
23264
  try {
22854
- const procStat = readFileSync5("/proc/stat", "utf-8");
23265
+ const procStat = readFileSync6("/proc/stat", "utf-8");
22855
23266
  const bootLine = procStat.split(`
22856
23267
  `).find((line) => line.startsWith("btime "));
22857
23268
  if (!bootLine)
@@ -22866,7 +23277,7 @@ function parseBootTimeMs() {
22866
23277
  }
22867
23278
  function parseProcessStartTimeMs(pid) {
22868
23279
  try {
22869
- const statRaw = readFileSync5(`/proc/${pid}/stat`, "utf-8");
23280
+ const statRaw = readFileSync6(`/proc/${pid}/stat`, "utf-8");
22870
23281
  const closeParenIndex = statRaw.lastIndexOf(")");
22871
23282
  if (closeParenIndex < 0)
22872
23283
  return;
@@ -23228,19 +23639,19 @@ var init_tmux_utils = () => {};
23228
23639
  import {
23229
23640
  appendFileSync,
23230
23641
  closeSync,
23231
- existsSync as existsSync8,
23642
+ existsSync as existsSync9,
23232
23643
  fsyncSync,
23233
23644
  mkdirSync as mkdirSync3,
23234
23645
  openSync,
23235
- readdirSync,
23236
- readFileSync as readFileSync6,
23646
+ readdirSync as readdirSync2,
23647
+ readFileSync as readFileSync7,
23237
23648
  renameSync,
23238
23649
  rmSync,
23239
23650
  statSync as statSync2,
23240
23651
  writeFileSync as writeFileSync3,
23241
23652
  writeSync
23242
23653
  } from "fs";
23243
- import { join as join8 } from "path";
23654
+ import { join as join9 } from "path";
23244
23655
  import { createInterface } from "readline";
23245
23656
  import { createReadStream } from "fs";
23246
23657
  import { spawn as spawn2, spawnSync as spawnSync6, execFileSync } from "child_process";
@@ -23574,8 +23985,8 @@ function isPidAlive(pid) {
23574
23985
  try {
23575
23986
  process.kill(pid, 0);
23576
23987
  return true;
23577
- } catch {
23578
- return false;
23988
+ } catch (err) {
23989
+ return err?.code === "EPERM";
23579
23990
  }
23580
23991
  }
23581
23992
  function isJobDead(status) {
@@ -23619,8 +24030,8 @@ class Supervisor {
23619
24030
  } finally {
23620
24031
  this.pendingSqliteOperations -= 1;
23621
24032
  if (this.pendingSqliteOperations === 0) {
23622
- for (const resolve5 of this.pendingSqliteDrainResolvers) {
23623
- resolve5();
24033
+ for (const resolve6 of this.pendingSqliteDrainResolvers) {
24034
+ resolve6();
23624
24035
  }
23625
24036
  this.pendingSqliteDrainResolvers.clear();
23626
24037
  }
@@ -23629,8 +24040,8 @@ class Supervisor {
23629
24040
  async waitForPendingSqliteOperations() {
23630
24041
  if (this.pendingSqliteOperations === 0)
23631
24042
  return;
23632
- await new Promise((resolve5) => {
23633
- this.pendingSqliteDrainResolvers.add(resolve5);
24043
+ await new Promise((resolve6) => {
24044
+ this.pendingSqliteDrainResolvers.add(resolve6);
23634
24045
  });
23635
24046
  }
23636
24047
  async dispose() {
@@ -23652,13 +24063,13 @@ class Supervisor {
23652
24063
  await this.disposePromise;
23653
24064
  }
23654
24065
  jobDir(id) {
23655
- return join8(this.resolvedJobsDir, id);
24066
+ return join9(this.resolvedJobsDir, id);
23656
24067
  }
23657
24068
  statusPath(id) {
23658
- return join8(this.jobDir(id), "status.json");
24069
+ return join9(this.jobDir(id), "status.json");
23659
24070
  }
23660
24071
  resultPath(id) {
23661
- return join8(this.jobDir(id), "result.txt");
24072
+ return join9(this.jobDir(id), "result.txt");
23662
24073
  }
23663
24074
  observabilityDbPath() {
23664
24075
  return resolveObservabilityDbLocation(this.opts.runOptions?.workingDirectory ?? process.cwd()).dbPath;
@@ -23668,14 +24079,14 @@ class Supervisor {
23668
24079
  return mode === "" || mode === "on" || mode === "true" || mode === "1";
23669
24080
  }
23670
24081
  eventsPath(id) {
23671
- return join8(this.jobDir(id), "events.jsonl");
24082
+ return join9(this.jobDir(id), "events.jsonl");
23672
24083
  }
23673
24084
  readyDir() {
23674
- return join8(this.resolvedJobsDir, "..", "ready");
24085
+ return join9(this.resolvedJobsDir, "..", "ready");
23675
24086
  }
23676
24087
  writeReadyMarker(id) {
23677
24088
  mkdirSync3(this.readyDir(), { recursive: true });
23678
- writeFileSync3(join8(this.readyDir(), id), "", "utf-8");
24089
+ writeFileSync3(join9(this.readyDir(), id), "", "utf-8");
23679
24090
  }
23680
24091
  withComputedLiveness(status) {
23681
24092
  return {
@@ -23697,10 +24108,10 @@ class Supervisor {
23697
24108
  }
23698
24109
  }
23699
24110
  const path = this.statusPath(id);
23700
- if (!existsSync8(path))
24111
+ if (!existsSync9(path))
23701
24112
  return null;
23702
24113
  try {
23703
- const status = JSON.parse(readFileSync6(path, "utf-8"));
24114
+ const status = JSON.parse(readFileSync7(path, "utf-8"));
23704
24115
  return this.withComputedLiveness(status);
23705
24116
  } catch {
23706
24117
  return null;
@@ -23734,15 +24145,15 @@ class Supervisor {
23734
24145
  console.warn(`[supervisor] SQLite listStatuses failed, falling back to file state: ${String(error2)}`);
23735
24146
  }
23736
24147
  }
23737
- if (!existsSync8(this.resolvedJobsDir))
24148
+ if (!existsSync9(this.resolvedJobsDir))
23738
24149
  return [];
23739
24150
  const jobs = [];
23740
- for (const entry of readdirSync(this.resolvedJobsDir)) {
23741
- const path = join8(this.resolvedJobsDir, entry, "status.json");
23742
- if (!existsSync8(path))
24151
+ for (const entry of readdirSync2(this.resolvedJobsDir)) {
24152
+ const path = join9(this.resolvedJobsDir, entry, "status.json");
24153
+ if (!existsSync9(path))
23743
24154
  continue;
23744
24155
  try {
23745
- const status = JSON.parse(readFileSync6(path, "utf-8"));
24156
+ const status = JSON.parse(readFileSync7(path, "utf-8"));
23746
24157
  jobs.push(this.withComputedLiveness(status));
23747
24158
  } catch {}
23748
24159
  }
@@ -23818,11 +24229,11 @@ class Supervisor {
23818
24229
  }
23819
24230
  }
23820
24231
  gc() {
23821
- if (!existsSync8(this.resolvedJobsDir))
24232
+ if (!existsSync9(this.resolvedJobsDir))
23822
24233
  return;
23823
24234
  const cutoff = Date.now() - JOB_TTL_DAYS * 86400000;
23824
- for (const entry of readdirSync(this.resolvedJobsDir)) {
23825
- const dir = join8(this.resolvedJobsDir, entry);
24235
+ for (const entry of readdirSync2(this.resolvedJobsDir)) {
24236
+ const dir = join9(this.resolvedJobsDir, entry);
23826
24237
  try {
23827
24238
  const stat2 = statSync2(dir);
23828
24239
  if (!stat2.isDirectory())
@@ -23833,7 +24244,7 @@ class Supervisor {
23833
24244
  }
23834
24245
  }
23835
24246
  crashRecovery() {
23836
- if (!existsSync8(this.resolvedJobsDir))
24247
+ if (!existsSync9(this.resolvedJobsDir))
23837
24248
  return;
23838
24249
  const thresholds = {
23839
24250
  ...STALL_DETECTION_DEFAULTS,
@@ -23864,7 +24275,7 @@ class Supervisor {
23864
24275
  const lastEventAt = status.last_event_at_ms ?? status.started_at_ms;
23865
24276
  const silenceMs = now - lastEventAt;
23866
24277
  if (silenceMs > thresholds.waiting_stale_ms) {
23867
- const eventsPath = join8(this.resolvedJobsDir, status.id, "events.jsonl");
24278
+ const eventsPath = join9(this.resolvedJobsDir, status.id, "events.jsonl");
23868
24279
  const event = createStaleWarningEvent("waiting_stale", {
23869
24280
  silence_ms: silenceMs,
23870
24281
  threshold_ms: thresholds.waiting_stale_ms
@@ -23878,12 +24289,12 @@ class Supervisor {
23878
24289
  }
23879
24290
  return;
23880
24291
  }
23881
- for (const entry of readdirSync(this.resolvedJobsDir)) {
23882
- const statusPath = join8(this.resolvedJobsDir, entry, "status.json");
23883
- if (!existsSync8(statusPath))
24292
+ for (const entry of readdirSync2(this.resolvedJobsDir)) {
24293
+ const statusPath = join9(this.resolvedJobsDir, entry, "status.json");
24294
+ if (!existsSync9(statusPath))
23884
24295
  continue;
23885
24296
  try {
23886
- const s = JSON.parse(readFileSync6(statusPath, "utf-8"));
24297
+ const s = JSON.parse(readFileSync7(statusPath, "utf-8"));
23887
24298
  if (s.status === "running" || s.status === "starting") {
23888
24299
  if (!s.pid)
23889
24300
  continue;
@@ -23915,7 +24326,7 @@ class Supervisor {
23915
24326
  const lastEventAt = s.last_event_at_ms ?? s.started_at_ms;
23916
24327
  const silenceMs = now - lastEventAt;
23917
24328
  if (silenceMs > thresholds.waiting_stale_ms) {
23918
- const eventsPath = join8(this.resolvedJobsDir, entry, "events.jsonl");
24329
+ const eventsPath = join9(this.resolvedJobsDir, entry, "events.jsonl");
23919
24330
  const event = createStaleWarningEvent("waiting_stale", {
23920
24331
  silence_ms: silenceMs,
23921
24332
  threshold_ms: thresholds.waiting_stale_ms
@@ -23989,7 +24400,7 @@ class Supervisor {
23989
24400
  this.writeStatusFile(id, initialStatus);
23990
24401
  const statusWatchdogPid = startDetachedStatusWatchdog(this.observabilityDbPath(), this.statusPath(id), id, process.pid);
23991
24402
  if (this.isJobFileOutputEnabled) {
23992
- writeFileSync3(join8(this.resolvedJobsDir, "latest"), `${id}
24403
+ writeFileSync3(join9(this.resolvedJobsDir, "latest"), `${id}
23993
24404
  `, "utf-8");
23994
24405
  }
23995
24406
  this.opts.onJobStarted?.({ id });
@@ -24062,7 +24473,7 @@ class Supervisor {
24062
24473
  if (runStartPersisted === undefined) {
24063
24474
  throw new Error("[supervisor] SQLite upsertStatusWithEvent failed during run start: database client unavailable");
24064
24475
  }
24065
- const fifoPath = join8(dir, "steer.pipe");
24476
+ const fifoPath = join9(dir, "steer.pipe");
24066
24477
  try {
24067
24478
  execFileSync("mkfifo", [fifoPath]);
24068
24479
  setStatus({ fifo_path: fifoPath });
@@ -24100,8 +24511,8 @@ class Supervisor {
24100
24511
  let keepAliveExitResolved = false;
24101
24512
  let isReadOnlySpecialist = false;
24102
24513
  let resolveKeepAliveExit;
24103
- const keepAliveExitPromise = new Promise((resolve5) => {
24104
- resolveKeepAliveExit = resolve5;
24514
+ const keepAliveExitPromise = new Promise((resolve6) => {
24515
+ resolveKeepAliveExit = resolve6;
24105
24516
  });
24106
24517
  const finishKeepAlive = (exit) => {
24107
24518
  if (keepAliveExitResolved)
@@ -24508,7 +24919,7 @@ ${appendError}
24508
24919
  setStatus({ bead_id: beadId });
24509
24920
  }, (fn) => {
24510
24921
  steerFn = fn;
24511
- if (!existsSync8(fifoPath))
24922
+ if (!existsSync9(fifoPath))
24512
24923
  return;
24513
24924
  fifoFd = openSync(fifoPath, "r+");
24514
24925
  fifoReadStream = createReadStream("", { fd: fifoFd, autoClose: false });
@@ -24682,9 +25093,12 @@ ${appendError}
24682
25093
  if (finalResult.beadId) {
24683
25094
  if (!inputBeadId) {
24684
25095
  this.opts.beadsClient?.closeBead(finalResult.beadId, "COMPLETE", finalResult.durationMs, finalResult.model);
25096
+ } else {
25097
+ this.opts.beadsClient?.closeBeadIfInProgress(finalResult.beadId, `Specialist ${runOptions.name} completed (job ${id})`);
24685
25098
  }
24686
25099
  }
24687
25100
  const completedAtMs = Date.now();
25101
+ const enrichedRunMetrics = finalResult.outputType ? { ...runMetrics, output_type: finalResult.outputType } : runMetrics;
24688
25102
  statusSnapshot = {
24689
25103
  ...statusSnapshot,
24690
25104
  status: "done",
@@ -24693,7 +25107,8 @@ ${appendError}
24693
25107
  model: finalResult.model,
24694
25108
  backend: finalResult.backend,
24695
25109
  bead_id: finalResult.beadId,
24696
- metrics: runMetrics
25110
+ metrics: enrichedRunMetrics,
25111
+ ...finalResult.outputType ? { output_type: finalResult.outputType } : {}
24697
25112
  };
24698
25113
  this.writeStatusFileOnly(id, statusSnapshot);
24699
25114
  const gitnexusSummary = gitnexusAccumulator.tool_invocations > 0 ? {
@@ -24712,7 +25127,7 @@ ${appendError}
24712
25127
  finish_reason: runMetrics.finish_reason,
24713
25128
  tool_calls: [...toolCallNames],
24714
25129
  exit_reason: runMetrics.exit_reason,
24715
- metrics: runMetrics,
25130
+ metrics: enrichedRunMetrics,
24716
25131
  ...gitnexusSummary ? { gitnexus_summary: gitnexusSummary } : {}
24717
25132
  }), latestOutput);
24718
25133
  return true;
@@ -24815,7 +25230,7 @@ ${appendError}
24815
25230
  closeSync(eventsFd);
24816
25231
  }
24817
25232
  try {
24818
- if (existsSync8(fifoPath))
25233
+ if (existsSync9(fifoPath))
24819
25234
  rmSync(fifoPath);
24820
25235
  } catch {}
24821
25236
  if (statusSnapshot.tmux_session) {
@@ -24860,13 +25275,13 @@ var init_supervisor = __esm(() => {
24860
25275
  // src/cli/list.ts
24861
25276
  var exports_list = {};
24862
25277
  __export(exports_list, {
24863
- run: () => run3,
24864
- parseArgs: () => parseArgs,
25278
+ run: () => run4,
25279
+ parseArgs: () => parseArgs2,
24865
25280
  ArgParseError: () => ArgParseError
24866
25281
  });
24867
25282
  import { spawnSync as spawnSync7 } from "child_process";
24868
- import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync7 } from "fs";
24869
- import { join as join9 } from "path";
25283
+ import { existsSync as existsSync10, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "fs";
25284
+ import { join as join10 } from "path";
24870
25285
  import readline from "readline";
24871
25286
  function permissionBadge(permission) {
24872
25287
  if (permission === "READ_ONLY")
@@ -24898,23 +25313,23 @@ function toLiveJob(status) {
24898
25313
  }
24899
25314
  function readJobStatus(statusPath) {
24900
25315
  try {
24901
- return JSON.parse(readFileSync7(statusPath, "utf-8"));
25316
+ return JSON.parse(readFileSync8(statusPath, "utf-8"));
24902
25317
  } catch {
24903
25318
  return null;
24904
25319
  }
24905
25320
  }
24906
25321
  function listLiveJobs(showDead) {
24907
25322
  const sqliteClient = createObservabilitySqliteClient();
24908
- const jobsDir = join9(process.cwd(), ".specialists", "jobs");
25323
+ const jobsDir = join10(process.cwd(), ".specialists", "jobs");
24909
25324
  const fileOutputEnabled = process.env.SPECIALISTS_JOB_FILE_OUTPUT === "on";
24910
25325
  const sqliteJobs = sqliteClient?.listStatuses().map((status) => toLiveJob(status)).filter((job) => job !== null).filter((job) => showDead || !job.isDead) ?? [];
24911
25326
  if (!fileOutputEnabled)
24912
25327
  return sqliteJobs.sort((a, b) => b.startedAtMs - a.startedAtMs);
24913
25328
  if (sqliteJobs.length > 0)
24914
25329
  return sqliteJobs.sort((a, b) => b.startedAtMs - a.startedAtMs);
24915
- if (!existsSync9(jobsDir))
25330
+ if (!existsSync10(jobsDir))
24916
25331
  return [];
24917
- return readdirSync2(jobsDir).map((entry) => toLiveJob(readJobStatus(join9(jobsDir, entry, "status.json")))).filter((job) => job !== null).filter((job) => showDead || !job.isDead).sort((a, b) => b.startedAtMs - a.startedAtMs);
25332
+ return readdirSync3(jobsDir).map((entry) => toLiveJob(readJobStatus(join10(jobsDir, entry, "status.json")))).filter((job) => job !== null).filter((job) => showDead || !job.isDead).sort((a, b) => b.startedAtMs - a.startedAtMs);
24918
25333
  }
24919
25334
  function formatLiveChoice(job) {
24920
25335
  const state = job.isDead ? "dead" : job.status;
@@ -24930,7 +25345,7 @@ function renderLiveSelector(jobs, selectedIndex) {
24930
25345
  ];
24931
25346
  }
24932
25347
  function selectLiveJob(jobs) {
24933
- return new Promise((resolve5) => {
25348
+ return new Promise((resolve6) => {
24934
25349
  const input = process.stdin;
24935
25350
  const output = process.stdout;
24936
25351
  const wasRawMode = input.isTTY ? input.isRaw : false;
@@ -24946,7 +25361,7 @@ function selectLiveJob(jobs) {
24946
25361
  readline.moveCursor(output, 0, -renderedLineCount);
24947
25362
  readline.clearScreenDown(output);
24948
25363
  }
24949
- resolve5(selected);
25364
+ resolve6(selected);
24950
25365
  };
24951
25366
  const render = () => {
24952
25367
  if (renderedLineCount > 0) {
@@ -25009,7 +25424,7 @@ async function runLiveMode(showDead) {
25009
25424
  process.exit(1);
25010
25425
  }
25011
25426
  }
25012
- function parseArgs(argv) {
25427
+ function parseArgs2(argv) {
25013
25428
  const result = {};
25014
25429
  for (let i = 0;i < argv.length; i++) {
25015
25430
  const token = argv[i];
@@ -25044,10 +25459,10 @@ function parseArgs(argv) {
25044
25459
  }
25045
25460
  return result;
25046
25461
  }
25047
- async function run3() {
25462
+ async function run4() {
25048
25463
  let args;
25049
25464
  try {
25050
- args = parseArgs(process.argv.slice(3));
25465
+ args = parseArgs2(process.argv.slice(3));
25051
25466
  } catch (err) {
25052
25467
  if (err instanceof ArgParseError) {
25053
25468
  console.error(`Error: ${err.message}`);
@@ -25115,7 +25530,7 @@ var init_list = __esm(() => {
25115
25530
  // src/cli/view.ts
25116
25531
  var exports_view = {};
25117
25532
  __export(exports_view, {
25118
- run: () => run4
25533
+ run: () => run5
25119
25534
  });
25120
25535
  import { readFile as readFile2 } from "fs/promises";
25121
25536
  import readline2 from "readline/promises";
@@ -25129,7 +25544,7 @@ function permissionBadge2(permission) {
25129
25544
  return yellow3("[MEDIUM]");
25130
25545
  return magenta2("[HIGH]");
25131
25546
  }
25132
- function parseArgs2(argv) {
25547
+ function parseArgs3(argv) {
25133
25548
  const parsed = { raw: false, all: false };
25134
25549
  for (let index = 0;index < argv.length; index++) {
25135
25550
  const token = argv[index];
@@ -25286,10 +25701,10 @@ async function printRaw(summary) {
25286
25701
  const content = await readFile2(summary.filePath, "utf-8");
25287
25702
  console.log(content);
25288
25703
  }
25289
- async function run4() {
25704
+ async function run5() {
25290
25705
  let args;
25291
25706
  try {
25292
- args = parseArgs2(process.argv.slice(3));
25707
+ args = parseArgs3(process.argv.slice(3));
25293
25708
  } catch (error2) {
25294
25709
  if (error2 instanceof ArgParseError2) {
25295
25710
  console.error(`Error: ${error2.message}`);
@@ -25360,7 +25775,7 @@ var init_view = __esm(() => {
25360
25775
  // src/cli/models.ts
25361
25776
  var exports_models = {};
25362
25777
  __export(exports_models, {
25363
- run: () => run5
25778
+ run: () => run6
25364
25779
  });
25365
25780
  import { spawnSync as spawnSync8 } from "child_process";
25366
25781
  function parsePiModels() {
@@ -25384,7 +25799,7 @@ function parsePiModels() {
25384
25799
  };
25385
25800
  }).filter((m) => m.provider && m.model);
25386
25801
  }
25387
- function parseArgs3(argv) {
25802
+ function parseArgs4(argv) {
25388
25803
  const out = {};
25389
25804
  for (let i = 0;i < argv.length; i++) {
25390
25805
  if (argv[i] === "--provider" && argv[i + 1]) {
@@ -25398,8 +25813,8 @@ function parseArgs3(argv) {
25398
25813
  }
25399
25814
  return out;
25400
25815
  }
25401
- async function run5() {
25402
- const args = parseArgs3(process.argv.slice(3));
25816
+ async function run6() {
25817
+ const args = parseArgs4(process.argv.slice(3));
25403
25818
  const loader = new SpecialistLoader;
25404
25819
  const specialists = await loader.list();
25405
25820
  const usedBy = new Map;
@@ -25465,11 +25880,11 @@ var init_models = __esm(() => {
25465
25880
  // src/cli/init.ts
25466
25881
  var exports_init = {};
25467
25882
  __export(exports_init, {
25468
- run: () => run6
25883
+ run: () => run7
25469
25884
  });
25470
- import { copyFileSync, cpSync, existsSync as existsSync10, lstatSync, mkdirSync as mkdirSync4, readdirSync as readdirSync3, readFileSync as readFileSync8, readlinkSync, renameSync as renameSync2, symlinkSync, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
25885
+ import { copyFileSync, cpSync, existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync9, readlinkSync, renameSync as renameSync2, symlinkSync, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
25471
25886
  import { spawnSync as spawnSync9 } from "child_process";
25472
- import { basename as basename3, dirname as dirname5, join as join10, relative, resolve as resolve5 } from "path";
25887
+ import { basename as basename4, dirname as dirname5, join as join11, relative, resolve as resolve6 } from "path";
25473
25888
  import { fileURLToPath as fileURLToPath2 } from "url";
25474
25889
  function ok(msg) {
25475
25890
  console.log(` ${green4("\u2713")} ${msg}`);
@@ -25484,7 +25899,7 @@ function isInstalled(bin) {
25484
25899
  return spawnSync9("which", [bin], { encoding: "utf8", timeout: 2000 }).status === 0;
25485
25900
  }
25486
25901
  function assertXtrmPrerequisites(cwd) {
25487
- const hasXtrmDir = existsSync10(join10(cwd, ".xtrm"));
25902
+ const hasXtrmDir = existsSync11(join11(cwd, ".xtrm"));
25488
25903
  const hasXtCli = isInstalled("xt");
25489
25904
  if (hasXtrmDir && hasXtCli)
25490
25905
  return;
@@ -25506,10 +25921,10 @@ function warnMissingOptionalPrerequisites() {
25506
25921
  }
25507
25922
  }
25508
25923
  function loadJson(path, fallback) {
25509
- if (!existsSync10(path))
25924
+ if (!existsSync11(path))
25510
25925
  return structuredClone(fallback);
25511
25926
  try {
25512
- return JSON.parse(readFileSync8(path, "utf-8"));
25927
+ return JSON.parse(readFileSync9(path, "utf-8"));
25513
25928
  } catch {
25514
25929
  return structuredClone(fallback);
25515
25930
  }
@@ -25521,30 +25936,30 @@ function saveJson(path, value) {
25521
25936
  function resolvePackagePath(relativePath) {
25522
25937
  const configPath = `config/${relativePath}`;
25523
25938
  let resolved = fileURLToPath2(new URL(`../${configPath}`, import.meta.url));
25524
- if (existsSync10(resolved))
25939
+ if (existsSync11(resolved))
25525
25940
  return resolved;
25526
25941
  resolved = fileURLToPath2(new URL(`../../${configPath}`, import.meta.url));
25527
- if (existsSync10(resolved))
25942
+ if (existsSync11(resolved))
25528
25943
  return resolved;
25529
25944
  return null;
25530
25945
  }
25531
25946
  function migrateLegacySpecialists(cwd, scope) {
25532
- const sourceDir = join10(cwd, ".specialists", scope, "specialists");
25533
- if (!existsSync10(sourceDir))
25947
+ const sourceDir = join11(cwd, ".specialists", scope, "specialists");
25948
+ if (!existsSync11(sourceDir))
25534
25949
  return;
25535
- const targetDir = join10(cwd, ".specialists", scope);
25536
- if (!existsSync10(targetDir)) {
25950
+ const targetDir = join11(cwd, ".specialists", scope);
25951
+ if (!existsSync11(targetDir)) {
25537
25952
  mkdirSync4(targetDir, { recursive: true });
25538
25953
  }
25539
- const files = readdirSync3(sourceDir).filter((f) => f.endsWith(".specialist.json") || f.endsWith(".specialist.json"));
25954
+ const files = readdirSync4(sourceDir).filter((f) => f.endsWith(".specialist.json") || f.endsWith(".specialist.json"));
25540
25955
  if (files.length === 0)
25541
25956
  return;
25542
25957
  let moved = 0;
25543
25958
  let skipped = 0;
25544
25959
  for (const file of files) {
25545
- const src = join10(sourceDir, file);
25546
- const dest = join10(targetDir, file);
25547
- if (existsSync10(dest)) {
25960
+ const src = join11(sourceDir, file);
25961
+ const dest = join11(targetDir, file);
25962
+ if (existsSync11(dest)) {
25548
25963
  skipped++;
25549
25964
  continue;
25550
25965
  }
@@ -25564,21 +25979,21 @@ function copyCanonicalSpecialists(cwd) {
25564
25979
  skip("no canonical specialists found in package");
25565
25980
  return;
25566
25981
  }
25567
- const targetDir = join10(cwd, ".specialists", "default");
25568
- const files = readdirSync3(sourceDir).filter((f) => f.endsWith(".specialist.json"));
25982
+ const targetDir = join11(cwd, ".specialists", "default");
25983
+ const files = readdirSync4(sourceDir).filter((f) => f.endsWith(".specialist.json"));
25569
25984
  if (files.length === 0) {
25570
25985
  skip("no specialist files found in package");
25571
25986
  return;
25572
25987
  }
25573
- if (!existsSync10(targetDir)) {
25988
+ if (!existsSync11(targetDir)) {
25574
25989
  mkdirSync4(targetDir, { recursive: true });
25575
25990
  }
25576
25991
  let copied = 0;
25577
25992
  let refreshed = 0;
25578
25993
  for (const file of files) {
25579
- const src = join10(sourceDir, file);
25580
- const dest = join10(targetDir, file);
25581
- if (existsSync10(dest)) {
25994
+ const src = join11(sourceDir, file);
25995
+ const dest = join11(targetDir, file);
25996
+ if (existsSync11(dest)) {
25582
25997
  copyFileSync(src, dest);
25583
25998
  refreshed++;
25584
25999
  } else {
@@ -25599,21 +26014,21 @@ function copyCanonicalMandatoryRules(cwd) {
25599
26014
  skip("no canonical mandatory-rules found in package");
25600
26015
  return;
25601
26016
  }
25602
- const targetDir = join10(cwd, ".specialists", "default", "mandatory-rules");
25603
- const files = readdirSync3(sourceDir).filter((f) => f.endsWith(".md") || f.endsWith(".json"));
26017
+ const targetDir = join11(cwd, ".specialists", "default", "mandatory-rules");
26018
+ const files = readdirSync4(sourceDir).filter((f) => f.endsWith(".md") || f.endsWith(".json"));
25604
26019
  if (files.length === 0) {
25605
26020
  skip("no mandatory-rules files found in package");
25606
26021
  return;
25607
26022
  }
25608
- if (!existsSync10(targetDir)) {
26023
+ if (!existsSync11(targetDir)) {
25609
26024
  mkdirSync4(targetDir, { recursive: true });
25610
26025
  }
25611
26026
  let copied = 0;
25612
26027
  let refreshed = 0;
25613
26028
  for (const file of files) {
25614
- const src = join10(sourceDir, file);
25615
- const dest = join10(targetDir, file);
25616
- if (existsSync10(dest)) {
26029
+ const src = join11(sourceDir, file);
26030
+ const dest = join11(targetDir, file);
26031
+ if (existsSync11(dest)) {
25617
26032
  copyFileSync(src, dest);
25618
26033
  refreshed++;
25619
26034
  } else {
@@ -25634,21 +26049,21 @@ function copyCanonicalNodeConfigs(cwd) {
25634
26049
  skip("no canonical node configs found in package");
25635
26050
  return;
25636
26051
  }
25637
- const targetDir = join10(cwd, ".specialists", "default", "nodes");
25638
- const files = readdirSync3(sourceDir).filter((f) => f.endsWith(".node.json"));
26052
+ const targetDir = join11(cwd, ".specialists", "default", "nodes");
26053
+ const files = readdirSync4(sourceDir).filter((f) => f.endsWith(".node.json"));
25639
26054
  if (files.length === 0) {
25640
26055
  skip("no node config files found in package");
25641
26056
  return;
25642
26057
  }
25643
- if (!existsSync10(targetDir)) {
26058
+ if (!existsSync11(targetDir)) {
25644
26059
  mkdirSync4(targetDir, { recursive: true });
25645
26060
  }
25646
26061
  let copied = 0;
25647
26062
  let refreshed = 0;
25648
26063
  for (const file of files) {
25649
- const src = join10(sourceDir, file);
25650
- const dest = join10(targetDir, file);
25651
- if (existsSync10(dest)) {
26064
+ const src = join11(sourceDir, file);
26065
+ const dest = join11(targetDir, file);
26066
+ if (existsSync11(dest)) {
25652
26067
  copyFileSync(src, dest);
25653
26068
  refreshed++;
25654
26069
  } else {
@@ -25669,10 +26084,10 @@ function installProjectHooks(cwd) {
25669
26084
  skip("no canonical hooks found in package");
25670
26085
  return;
25671
26086
  }
25672
- const xtrmHooksDir = join10(cwd, ".xtrm", "hooks");
25673
- const targetDir = join10(xtrmHooksDir, "specialists");
25674
- const claudeHooksDir = join10(cwd, ".claude", "hooks");
25675
- const hooks = readdirSync3(sourceDir).filter((f) => f.endsWith(".mjs"));
26087
+ const xtrmHooksDir = join11(cwd, ".xtrm", "hooks");
26088
+ const targetDir = join11(xtrmHooksDir, "specialists");
26089
+ const claudeHooksDir = join11(cwd, ".claude", "hooks");
26090
+ const hooks = readdirSync4(sourceDir).filter((f) => f.endsWith(".mjs"));
25676
26091
  if (hooks.length === 0) {
25677
26092
  skip("no hook files found in package");
25678
26093
  return;
@@ -25685,17 +26100,17 @@ function installProjectHooks(cwd) {
25685
26100
  let rewiredLinks = 0;
25686
26101
  let skippedLinks = 0;
25687
26102
  for (const file of hooks) {
25688
- const src = join10(sourceDir, file);
25689
- const xtrmDest = join10(targetDir, file);
25690
- if (existsSync10(xtrmDest)) {
26103
+ const src = join11(sourceDir, file);
26104
+ const xtrmDest = join11(targetDir, file);
26105
+ if (existsSync11(xtrmDest)) {
25691
26106
  skippedCopies++;
25692
26107
  } else {
25693
26108
  copyFileSync(src, xtrmDest);
25694
26109
  copied++;
25695
26110
  }
25696
- const claudeHookPath = join10(claudeHooksDir, file);
26111
+ const claudeHookPath = join11(claudeHooksDir, file);
25697
26112
  const relativeTarget = `../../.xtrm/hooks/specialists/${file}`;
25698
- if (existsSync10(claudeHookPath)) {
26113
+ if (existsSync11(claudeHookPath)) {
25699
26114
  const stats = lstatSync(claudeHookPath);
25700
26115
  if (!stats.isSymbolicLink()) {
25701
26116
  unlinkSync(claudeHookPath);
@@ -25703,7 +26118,7 @@ function installProjectHooks(cwd) {
25703
26118
  rewiredLinks++;
25704
26119
  continue;
25705
26120
  }
25706
- const currentTarget = resolve5(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
26121
+ const currentTarget = resolve6(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
25707
26122
  if (currentTarget !== xtrmDest) {
25708
26123
  unlinkSync(claudeHookPath);
25709
26124
  symlinkSync(relativeTarget, claudeHookPath);
@@ -25728,9 +26143,9 @@ function installProjectHooks(cwd) {
25728
26143
  skip(`${skippedLinks} hook${skippedLinks === 1 ? "" : "s"} already present in .claude/hooks/ (left unchanged)`);
25729
26144
  }
25730
26145
  function ensureProjectHookWiring(cwd) {
25731
- const settingsPath = join10(cwd, ".claude", "settings.json");
25732
- const settingsDir = join10(cwd, ".claude");
25733
- if (!existsSync10(settingsDir)) {
26146
+ const settingsPath = join11(cwd, ".claude", "settings.json");
26147
+ const settingsDir = join11(cwd, ".claude");
26148
+ if (!existsSync11(settingsDir)) {
25734
26149
  mkdirSync4(settingsDir, { recursive: true });
25735
26150
  }
25736
26151
  const settings = loadJson(settingsPath, {});
@@ -25766,11 +26181,11 @@ function ensureProjectHookWiring(cwd) {
25766
26181
  }
25767
26182
  }
25768
26183
  function ensureRootSymlink(rootPath, expectedTargetPath) {
25769
- if (!existsSync10(rootPath)) {
26184
+ if (!existsSync11(rootPath)) {
25770
26185
  mkdirSync4(dirname5(rootPath), { recursive: true });
25771
26186
  const relTarget = relative(dirname5(rootPath), expectedTargetPath);
25772
26187
  symlinkSync(relTarget, rootPath);
25773
- ok(`created ${basename3(dirname5(rootPath))}/${basename3(rootPath)} \u2192 ${relTarget}`);
26188
+ ok(`created ${basename4(dirname5(rootPath))}/${basename4(rootPath)} \u2192 ${relTarget}`);
25774
26189
  return;
25775
26190
  }
25776
26191
  const stats = lstatSync(rootPath);
@@ -25778,20 +26193,20 @@ function ensureRootSymlink(rootPath, expectedTargetPath) {
25778
26193
  throw new Error(`${rootPath} must be a symlink to ${expectedTargetPath}. Aborting.`);
25779
26194
  }
25780
26195
  const linkTarget = readlinkSync(rootPath);
25781
- const resolvedTarget = resolve5(dirname5(rootPath), linkTarget);
25782
- const resolvedExpected = resolve5(expectedTargetPath);
26196
+ const resolvedTarget = resolve6(dirname5(rootPath), linkTarget);
26197
+ const resolvedExpected = resolve6(expectedTargetPath);
25783
26198
  if (resolvedTarget === resolvedExpected) {
25784
26199
  return;
25785
26200
  }
25786
26201
  const legacyTargets = [
25787
- resolve5(expectedTargetPath, "claude"),
25788
- resolve5(expectedTargetPath, "pi")
26202
+ resolve6(expectedTargetPath, "claude"),
26203
+ resolve6(expectedTargetPath, "pi")
25789
26204
  ];
25790
26205
  if (legacyTargets.includes(resolvedTarget)) {
25791
26206
  unlinkSync(rootPath);
25792
26207
  const relTarget = relative(dirname5(rootPath), expectedTargetPath);
25793
26208
  symlinkSync(relTarget, rootPath);
25794
- ok(`rewired ${basename3(dirname5(rootPath))}/${basename3(rootPath)} \u2192 ${relTarget}`);
26209
+ ok(`rewired ${basename4(dirname5(rootPath))}/${basename4(rootPath)} \u2192 ${relTarget}`);
25795
26210
  return;
25796
26211
  }
25797
26212
  throw new Error(`${rootPath} points to ${linkTarget}, expected ${expectedTargetPath}. Aborting.`);
@@ -25803,7 +26218,7 @@ function ensureActiveSkillSymlink(defaultSkillPath, activeLinkPath) {
25803
26218
  } catch (error2) {
25804
26219
  const fileError = error2;
25805
26220
  if (fileError.code === "ENOENT") {
25806
- const relativeTarget = `../default/${basename3(defaultSkillPath)}`;
26221
+ const relativeTarget = `../default/${basename4(defaultSkillPath)}`;
25807
26222
  symlinkSync(relativeTarget, activeLinkPath, "dir");
25808
26223
  return;
25809
26224
  }
@@ -25812,14 +26227,14 @@ function ensureActiveSkillSymlink(defaultSkillPath, activeLinkPath) {
25812
26227
  if (!stats.isSymbolicLink()) {
25813
26228
  throw new Error(`${activeLinkPath} already exists and is not a symlink.`);
25814
26229
  }
25815
- const currentTarget = resolve5(dirname5(activeLinkPath), readlinkSync(activeLinkPath));
25816
- if (currentTarget !== resolve5(defaultSkillPath)) {
26230
+ const currentTarget = resolve6(dirname5(activeLinkPath), readlinkSync(activeLinkPath));
26231
+ if (currentTarget !== resolve6(defaultSkillPath)) {
25817
26232
  throw new Error(`${activeLinkPath} points to an unexpected target.`);
25818
26233
  }
25819
26234
  }
25820
26235
  function installProjectSkills(cwd, syncSkills) {
25821
- const xtrmRoot = join10(cwd, ".xtrm");
25822
- if (!existsSync10(xtrmRoot)) {
26236
+ const xtrmRoot = join11(cwd, ".xtrm");
26237
+ if (!existsSync11(xtrmRoot)) {
25823
26238
  throw new Error(".xtrm/ is missing. Install xtrm first, then run specialists init.");
25824
26239
  }
25825
26240
  const sourceDir = resolvePackagePath("skills");
@@ -25827,23 +26242,23 @@ function installProjectSkills(cwd, syncSkills) {
25827
26242
  skip("no canonical skills found in package");
25828
26243
  return;
25829
26244
  }
25830
- const skills = readdirSync3(sourceDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
26245
+ const skills = readdirSync4(sourceDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
25831
26246
  if (skills.length === 0) {
25832
26247
  skip("no skill directories found in package");
25833
26248
  return;
25834
26249
  }
25835
- const defaultRoot = join10(cwd, ".xtrm", "skills", "default");
25836
- const activeRoot = join10(cwd, ".xtrm", "skills", "active");
26250
+ const defaultRoot = join11(cwd, ".xtrm", "skills", "default");
26251
+ const activeRoot = join11(cwd, ".xtrm", "skills", "active");
25837
26252
  mkdirSync4(defaultRoot, { recursive: true });
25838
26253
  mkdirSync4(activeRoot, { recursive: true });
25839
- ensureRootSymlink(join10(cwd, ".claude", "skills"), activeRoot);
25840
- ensureRootSymlink(join10(cwd, ".pi", "skills"), activeRoot);
26254
+ ensureRootSymlink(join11(cwd, ".claude", "skills"), activeRoot);
26255
+ ensureRootSymlink(join11(cwd, ".pi", "skills"), activeRoot);
25841
26256
  let copied = 0;
25842
26257
  let refreshed = 0;
25843
26258
  for (const skill of skills) {
25844
- const src = join10(sourceDir, skill);
25845
- const defaultSkillPath = join10(defaultRoot, skill);
25846
- if (existsSync10(defaultSkillPath)) {
26259
+ const src = join11(sourceDir, skill);
26260
+ const defaultSkillPath = join11(defaultRoot, skill);
26261
+ if (existsSync11(defaultSkillPath)) {
25847
26262
  if (syncSkills) {
25848
26263
  cpSync(src, defaultSkillPath, { recursive: true, force: true });
25849
26264
  refreshed++;
@@ -25852,7 +26267,7 @@ function installProjectSkills(cwd, syncSkills) {
25852
26267
  cpSync(src, defaultSkillPath, { recursive: true });
25853
26268
  copied++;
25854
26269
  }
25855
- ensureActiveSkillSymlink(defaultSkillPath, join10(activeRoot, skill));
26270
+ ensureActiveSkillSymlink(defaultSkillPath, join11(activeRoot, skill));
25856
26271
  }
25857
26272
  if (copied > 0)
25858
26273
  ok(`copied ${copied} skill${copied === 1 ? "" : "s"} to .xtrm/skills/default/`);
@@ -25861,11 +26276,11 @@ function installProjectSkills(cwd, syncSkills) {
25861
26276
  ok("verified active skill symlinks in .xtrm/skills/active/");
25862
26277
  }
25863
26278
  function createSpecialistsDirs(cwd) {
25864
- const defaultDir = join10(cwd, ".specialists", "default");
25865
- const userDir = join10(cwd, ".specialists", "user");
26279
+ const defaultDir = join11(cwd, ".specialists", "default");
26280
+ const userDir = join11(cwd, ".specialists", "user");
25866
26281
  let created = 0;
25867
26282
  for (const dir of [defaultDir, userDir]) {
25868
- if (!existsSync10(dir)) {
26283
+ if (!existsSync11(dir)) {
25869
26284
  mkdirSync4(dir, { recursive: true });
25870
26285
  created++;
25871
26286
  }
@@ -25876,12 +26291,12 @@ function createSpecialistsDirs(cwd) {
25876
26291
  }
25877
26292
  function createRuntimeDirs(cwd) {
25878
26293
  const runtimeDirs = [
25879
- join10(cwd, ".specialists", "jobs"),
25880
- join10(cwd, ".specialists", "ready")
26294
+ join11(cwd, ".specialists", "jobs"),
26295
+ join11(cwd, ".specialists", "ready")
25881
26296
  ];
25882
26297
  let created = 0;
25883
26298
  for (const dir of runtimeDirs) {
25884
- if (!existsSync10(dir)) {
26299
+ if (!existsSync11(dir)) {
25885
26300
  mkdirSync4(dir, { recursive: true });
25886
26301
  created++;
25887
26302
  }
@@ -25891,7 +26306,7 @@ function createRuntimeDirs(cwd) {
25891
26306
  }
25892
26307
  }
25893
26308
  function ensureProjectMcp(cwd) {
25894
- const mcpPath = join10(cwd, MCP_FILE);
26309
+ const mcpPath = join11(cwd, MCP_FILE);
25895
26310
  const mcp = loadJson(mcpPath, { mcpServers: {} });
25896
26311
  mcp.mcpServers ??= {};
25897
26312
  const existing = mcp.mcpServers[MCP_SERVER_NAME];
@@ -25904,8 +26319,8 @@ function ensureProjectMcp(cwd) {
25904
26319
  ok("registered specialists in project .mcp.json");
25905
26320
  }
25906
26321
  function ensureGitignore(cwd) {
25907
- const gitignorePath = join10(cwd, ".gitignore");
25908
- const existing = existsSync10(gitignorePath) ? readFileSync8(gitignorePath, "utf-8") : "";
26322
+ const gitignorePath = join11(cwd, ".gitignore");
26323
+ const existing = existsSync11(gitignorePath) ? readFileSync9(gitignorePath, "utf-8") : "";
25909
26324
  let added = 0;
25910
26325
  const lines = existing.split(`
25911
26326
  `);
@@ -25930,7 +26345,7 @@ function ensureObservabilityDb(cwd) {
25930
26345
  skip("observability DB path resolves inside jobs directory \u2014 skipped");
25931
26346
  return;
25932
26347
  }
25933
- const alreadyExists = existsSync10(location.dbPath);
26348
+ const alreadyExists = existsSync11(location.dbPath);
25934
26349
  if (alreadyExists) {
25935
26350
  skip("observability database already exists (not touched)");
25936
26351
  return;
@@ -25950,9 +26365,9 @@ function ensureObservabilityDb(cwd) {
25950
26365
  ensureGitignoreHasObservabilityDbEntries(location.gitRoot);
25951
26366
  }
25952
26367
  function ensureAgentsMd(cwd) {
25953
- const agentsPath = join10(cwd, "AGENTS.md");
25954
- if (existsSync10(agentsPath)) {
25955
- const existing = readFileSync8(agentsPath, "utf-8");
26368
+ const agentsPath = join11(cwd, "AGENTS.md");
26369
+ if (existsSync11(agentsPath)) {
26370
+ const existing = readFileSync9(agentsPath, "utf-8");
25956
26371
  if (existing.includes(AGENTS_MARKER)) {
25957
26372
  skip("AGENTS.md already has Specialists section");
25958
26373
  } else {
@@ -25967,10 +26382,10 @@ function ensureAgentsMd(cwd) {
25967
26382
  }
25968
26383
  }
25969
26384
  function readJsonObject(path) {
25970
- if (!existsSync10(path))
26385
+ if (!existsSync11(path))
25971
26386
  return {};
25972
26387
  try {
25973
- return JSON.parse(readFileSync8(path, "utf-8"));
26388
+ return JSON.parse(readFileSync9(path, "utf-8"));
25974
26389
  } catch {
25975
26390
  return {};
25976
26391
  }
@@ -25997,15 +26412,15 @@ function hasHookCommand(settings, eventName, command) {
25997
26412
  }
25998
26413
  function validateInitPostconditions(cwd) {
25999
26414
  const warnings = [];
26000
- const xtrmHooksDir = join10(cwd, ".xtrm", "hooks", "specialists");
26001
- const xtrmHookFiles = existsSync10(xtrmHooksDir) ? readdirSync3(xtrmHooksDir).filter((file) => file.endsWith(".mjs")) : [];
26415
+ const xtrmHooksDir = join11(cwd, ".xtrm", "hooks", "specialists");
26416
+ const xtrmHookFiles = existsSync11(xtrmHooksDir) ? readdirSync4(xtrmHooksDir).filter((file) => file.endsWith(".mjs")) : [];
26002
26417
  if (xtrmHookFiles.length === 0) {
26003
26418
  warnings.push(".xtrm/hooks/specialists/ is missing or has no .mjs hooks");
26004
26419
  }
26005
- const claudeHooksDir = join10(cwd, ".claude", "hooks");
26420
+ const claudeHooksDir = join11(cwd, ".claude", "hooks");
26006
26421
  for (const hookFile of xtrmHookFiles) {
26007
- const claudeHookPath = join10(claudeHooksDir, hookFile);
26008
- if (!existsSync10(claudeHookPath)) {
26422
+ const claudeHookPath = join11(claudeHooksDir, hookFile);
26423
+ if (!existsSync11(claudeHookPath)) {
26009
26424
  warnings.push(`.claude/hooks/${hookFile} is missing`);
26010
26425
  continue;
26011
26426
  }
@@ -26014,13 +26429,13 @@ function validateInitPostconditions(cwd) {
26014
26429
  warnings.push(`.claude/hooks/${hookFile} is not a symlink`);
26015
26430
  continue;
26016
26431
  }
26017
- const expectedTarget = resolve5(xtrmHooksDir, hookFile);
26018
- const resolvedTarget = resolve5(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
26432
+ const expectedTarget = resolve6(xtrmHooksDir, hookFile);
26433
+ const resolvedTarget = resolve6(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
26019
26434
  if (resolvedTarget !== expectedTarget) {
26020
26435
  warnings.push(`.claude/hooks/${hookFile} points to unexpected target`);
26021
26436
  }
26022
26437
  }
26023
- const settings = readJsonObject(join10(cwd, ".claude", "settings.json"));
26438
+ const settings = readJsonObject(join11(cwd, ".claude", "settings.json"));
26024
26439
  const requiredHookWiring = [
26025
26440
  { event: "UserPromptSubmit", command: "node .claude/hooks/specialists-complete.mjs" },
26026
26441
  { event: "PostToolUse", command: "node .claude/hooks/specialists-complete.mjs" },
@@ -26032,35 +26447,35 @@ function validateInitPostconditions(cwd) {
26032
26447
  warnings.push(`.claude/settings.json missing hook wiring: ${hook.event} -> ${hook.command}`);
26033
26448
  }
26034
26449
  }
26035
- const mcp = readJsonObject(join10(cwd, ".mcp.json"));
26450
+ const mcp = readJsonObject(join11(cwd, ".mcp.json"));
26036
26451
  const mcpServers = mcp.mcpServers;
26037
26452
  const specialistsServer = mcpServers && typeof mcpServers === "object" ? mcpServers.specialists : undefined;
26038
26453
  if (!specialistsServer || typeof specialistsServer !== "object") {
26039
26454
  warnings.push(".mcp.json missing mcpServers.specialists registration");
26040
26455
  }
26041
- const runtimeDirs = [join10(cwd, ".specialists", "jobs"), join10(cwd, ".specialists", "ready")];
26456
+ const runtimeDirs = [join11(cwd, ".specialists", "jobs"), join11(cwd, ".specialists", "ready")];
26042
26457
  for (const runtimeDir of runtimeDirs) {
26043
- if (!existsSync10(runtimeDir)) {
26458
+ if (!existsSync11(runtimeDir)) {
26044
26459
  warnings.push(`${relative(cwd, runtimeDir)} is missing`);
26045
26460
  }
26046
26461
  }
26047
- const defaultSkillsRoot = join10(cwd, ".xtrm", "skills", "default");
26048
- const defaultSkills = existsSync10(defaultSkillsRoot) ? readdirSync3(defaultSkillsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()) : [];
26462
+ const defaultSkillsRoot = join11(cwd, ".xtrm", "skills", "default");
26463
+ const defaultSkills = existsSync11(defaultSkillsRoot) ? readdirSync4(defaultSkillsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()) : [];
26049
26464
  if (defaultSkills.length === 0) {
26050
26465
  warnings.push(".xtrm/skills/default/ is missing or has no skill directories");
26051
26466
  }
26052
26467
  const rootSymlinks = [
26053
26468
  {
26054
- linkPath: join10(cwd, ".claude", "skills"),
26055
- expectedTarget: join10(cwd, ".xtrm", "skills", "active")
26469
+ linkPath: join11(cwd, ".claude", "skills"),
26470
+ expectedTarget: join11(cwd, ".xtrm", "skills", "active")
26056
26471
  },
26057
26472
  {
26058
- linkPath: join10(cwd, ".pi", "skills"),
26059
- expectedTarget: join10(cwd, ".xtrm", "skills", "active")
26473
+ linkPath: join11(cwd, ".pi", "skills"),
26474
+ expectedTarget: join11(cwd, ".xtrm", "skills", "active")
26060
26475
  }
26061
26476
  ];
26062
26477
  for (const symlink of rootSymlinks) {
26063
- if (!existsSync10(symlink.linkPath)) {
26478
+ if (!existsSync11(symlink.linkPath)) {
26064
26479
  warnings.push(`${relative(cwd, symlink.linkPath)} is missing`);
26065
26480
  continue;
26066
26481
  }
@@ -26069,14 +26484,14 @@ function validateInitPostconditions(cwd) {
26069
26484
  warnings.push(`${relative(cwd, symlink.linkPath)} is not a symlink`);
26070
26485
  continue;
26071
26486
  }
26072
- const resolvedTarget = resolve5(dirname5(symlink.linkPath), readlinkSync(symlink.linkPath));
26073
- if (resolvedTarget !== resolve5(symlink.expectedTarget)) {
26487
+ const resolvedTarget = resolve6(dirname5(symlink.linkPath), readlinkSync(symlink.linkPath));
26488
+ if (resolvedTarget !== resolve6(symlink.expectedTarget)) {
26074
26489
  warnings.push(`${relative(cwd, symlink.linkPath)} points to an unexpected target`);
26075
26490
  }
26076
26491
  }
26077
26492
  return warnings;
26078
26493
  }
26079
- async function run6(opts = {}) {
26494
+ async function run7(opts = {}) {
26080
26495
  const cwd = process.cwd();
26081
26496
  const forceInit = process.env.SPECIALISTS_INIT_FORCE === "1";
26082
26497
  const inAgentSession = !forceInit && (!process.stdin.isTTY || !!process.env.SPECIALISTS_TMUX_SESSION || !!process.env.SPECIALISTS_JOB_ID || !!process.env.PI_SESSION_ID || !!process.env.PI_RPC_SOCKET);
@@ -26199,9 +26614,9 @@ Add custom specialists to \`.specialists/user/\` to extend defaults.
26199
26614
  // src/cli/memory.ts
26200
26615
  var exports_memory = {};
26201
26616
  __export(exports_memory, {
26202
- run: () => run7
26617
+ run: () => run8
26203
26618
  });
26204
- function printUsage() {
26619
+ function printUsage2() {
26205
26620
  console.log([
26206
26621
  "",
26207
26622
  "Usage: specialists memory <sync|refresh> [--force] [--json]",
@@ -26213,13 +26628,13 @@ function printUsage() {
26213
26628
  ].join(`
26214
26629
  `));
26215
26630
  }
26216
- async function run7(args = []) {
26631
+ async function run8(args = []) {
26217
26632
  const command = args[0] ?? "sync";
26218
26633
  const force = args.includes("--force");
26219
26634
  const asJson = args.includes("--json");
26220
26635
  const cwd = process.cwd();
26221
26636
  if (command !== "sync" && command !== "refresh") {
26222
- printUsage();
26637
+ printUsage2();
26223
26638
  process.exitCode = 1;
26224
26639
  return;
26225
26640
  }
@@ -26243,10 +26658,10 @@ var init_memory = __esm(() => {
26243
26658
  // src/cli/db.ts
26244
26659
  var exports_db = {};
26245
26660
  __export(exports_db, {
26246
- run: () => run8
26661
+ run: () => run9
26247
26662
  });
26248
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
26249
- import { dirname as dirname6, join as join11, resolve as resolve6 } from "path";
26663
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5, readdirSync as readdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
26664
+ import { dirname as dirname6, join as join12, resolve as resolve7 } from "path";
26250
26665
  function formatBytes(bytes) {
26251
26666
  if (bytes < 1024)
26252
26667
  return `${bytes} B`;
@@ -26467,8 +26882,8 @@ function parseStatsOptions(argv) {
26467
26882
  return { spec, model, sinceMs, format };
26468
26883
  }
26469
26884
  function parseStatusFile(jobDirectoryPath, fallbackJobId) {
26470
- const statusPath = join11(jobDirectoryPath, "status.json");
26471
- const statusRaw = readFileSync9(statusPath, "utf-8");
26885
+ const statusPath = join12(jobDirectoryPath, "status.json");
26886
+ const statusRaw = readFileSync10(statusPath, "utf-8");
26472
26887
  const parsed = JSON.parse(statusRaw);
26473
26888
  const jobId = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : fallbackJobId;
26474
26889
  const specialist = typeof parsed.specialist === "string" && parsed.specialist.length > 0 ? parsed.specialist : "unknown";
@@ -26483,9 +26898,9 @@ function parseStatusFile(jobDirectoryPath, fallbackJobId) {
26483
26898
  };
26484
26899
  }
26485
26900
  function replayEvents(eventsPath, sqliteClient, status) {
26486
- if (!existsSync11(eventsPath))
26901
+ if (!existsSync12(eventsPath))
26487
26902
  return 0;
26488
- const rawContent = readFileSync9(eventsPath, "utf-8");
26903
+ const rawContent = readFileSync10(eventsPath, "utf-8");
26489
26904
  const lines = rawContent.split(`
26490
26905
  `).map((line) => line.trim()).filter((line) => line.length > 0);
26491
26906
  let importedEvents = 0;
@@ -26511,17 +26926,17 @@ function runBackfill(options) {
26511
26926
  };
26512
26927
  try {
26513
26928
  const jobsDirectoryPath = resolveJobsDir(process.cwd());
26514
- if (!existsSync11(jobsDirectoryPath)) {
26929
+ if (!existsSync12(jobsDirectoryPath)) {
26515
26930
  console.log("No jobs directory found. Nothing to backfill.");
26516
26931
  return;
26517
26932
  }
26518
- const jobEntries = readdirSync4(jobsDirectoryPath, { withFileTypes: true });
26933
+ const jobEntries = readdirSync5(jobsDirectoryPath, { withFileTypes: true });
26519
26934
  for (const jobEntry of jobEntries) {
26520
26935
  if (!jobEntry.isDirectory())
26521
26936
  continue;
26522
- const jobDirectoryPath = join11(jobsDirectoryPath, jobEntry.name);
26523
- const statusPath = join11(jobDirectoryPath, "status.json");
26524
- if (!existsSync11(statusPath))
26937
+ const jobDirectoryPath = join12(jobsDirectoryPath, jobEntry.name);
26938
+ const statusPath = join12(jobDirectoryPath, "status.json");
26939
+ if (!existsSync12(statusPath))
26525
26940
  continue;
26526
26941
  try {
26527
26942
  const status = parseStatusFile(jobDirectoryPath, jobEntry.name);
@@ -26561,7 +26976,7 @@ function runBackfill(options) {
26561
26976
  }
26562
26977
  summary.jobsBackfilled += 1;
26563
26978
  if (options.importEvents) {
26564
- const eventsPath = join11(jobDirectoryPath, "events.jsonl");
26979
+ const eventsPath = join12(jobDirectoryPath, "events.jsonl");
26565
26980
  summary.eventsImported += replayEvents(eventsPath, sqliteClient, status);
26566
26981
  }
26567
26982
  } catch {
@@ -26688,14 +27103,14 @@ ${bold7("specialists db stats")}
26688
27103
  }
26689
27104
  }
26690
27105
  function parseBenchmarkExportOptions(argv) {
26691
- const defaultOutput = resolve6(process.cwd(), ".specialists/benchmarks/executor-benchmark-rows.jsonl");
27106
+ const defaultOutput = resolve7(process.cwd(), ".specialists/benchmarks/executor-benchmark-rows.jsonl");
26692
27107
  let outputPath = defaultOutput;
26693
27108
  let epicId;
26694
27109
  let includePrepJobs = false;
26695
27110
  for (let i = 0;i < argv.length; i += 1) {
26696
27111
  const argument = argv[i];
26697
27112
  if (argument === "--output" && argv[i + 1]) {
26698
- outputPath = resolve6(process.cwd(), argv[i + 1]);
27113
+ outputPath = resolve7(process.cwd(), argv[i + 1]);
26699
27114
  i += 1;
26700
27115
  continue;
26701
27116
  }
@@ -26875,7 +27290,7 @@ function runSetup() {
26875
27290
  const gitignoreResult = ensureGitignoreHasObservabilityDbEntries(location.gitRoot);
26876
27291
  printSetupResult(setupResult.created, gitignoreResult.changed, location);
26877
27292
  }
26878
- async function run8(argv = process.argv.slice(3)) {
27293
+ async function run9(argv = process.argv.slice(3)) {
26879
27294
  const subcommand = argv[0];
26880
27295
  if (!subcommand || subcommand === "--help" || subcommand === "-h") {
26881
27296
  printDbHelp();
@@ -27096,9 +27511,9 @@ async function runScriptSpecialist(input2, options) {
27096
27511
  pi.stderr.on("data", (chunk) => {
27097
27512
  stderr += String(chunk);
27098
27513
  });
27099
- const exitCode = await new Promise((resolve7, reject) => {
27514
+ const exitCode = await new Promise((resolve8, reject) => {
27100
27515
  pi.on("error", reject);
27101
- pi.on("close", (code) => resolve7(code ?? 0));
27516
+ pi.on("close", (code) => resolve8(code ?? 0));
27102
27517
  }).finally(() => clearTimeout(timer));
27103
27518
  const stdout = Buffer.concat(chunks).toString("utf-8");
27104
27519
  const text = extractAssistantText(stdout.split(/\r?\n/));
@@ -27149,13 +27564,13 @@ var init_script_runner = __esm(() => {
27149
27564
  // src/cli/validate.ts
27150
27565
  var exports_validate = {};
27151
27566
  __export(exports_validate, {
27152
- run: () => run9,
27153
- parseArgs: () => parseArgs4,
27567
+ run: () => run10,
27568
+ parseArgs: () => parseArgs5,
27154
27569
  ArgParseError: () => ArgParseError3
27155
27570
  });
27156
27571
  import { readFile as readFile3 } from "fs/promises";
27157
- import { existsSync as existsSync12 } from "fs";
27158
- function parseArgs4(argv) {
27572
+ import { existsSync as existsSync13 } from "fs";
27573
+ function parseArgs5(argv) {
27159
27574
  const value = argv[0];
27160
27575
  if (!value || value.startsWith("--")) {
27161
27576
  throw new ArgParseError3("Usage: specialists validate <name|path> [--target=<surface>] [--json]");
@@ -27191,10 +27606,10 @@ function formatCompatGuardError(message) {
27191
27606
  return "compatGuard: scripts";
27192
27607
  return `compatGuard: ${message}`;
27193
27608
  }
27194
- async function run9() {
27609
+ async function run10() {
27195
27610
  let args;
27196
27611
  try {
27197
- args = parseArgs4(process.argv.slice(3));
27612
+ args = parseArgs5(process.argv.slice(3));
27198
27613
  } catch (err) {
27199
27614
  if (err instanceof ArgParseError3) {
27200
27615
  console.error(`Error: ${err.message}`);
@@ -27229,7 +27644,7 @@ async function run9() {
27229
27644
  }
27230
27645
  process.exit(1);
27231
27646
  }
27232
- if (!existsSync12(summary.filePath)) {
27647
+ if (!existsSync13(summary.filePath)) {
27233
27648
  const message = `Failed to read file: ${summary.filePath}`;
27234
27649
  if (args.json) {
27235
27650
  console.log(JSON.stringify({ valid: false, errors: [{ path: "file", message, code: "read_error" }] }));
@@ -27294,19 +27709,19 @@ var init_validate = __esm(() => {
27294
27709
  // src/cli/edit.ts
27295
27710
  var exports_edit = {};
27296
27711
  __export(exports_edit, {
27297
- run: () => run10
27712
+ run: () => run11
27298
27713
  });
27299
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
27300
- import { join as join12 } from "path";
27714
+ import { existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
27715
+ import { join as join13 } from "path";
27301
27716
  function loadPresets() {
27302
27717
  const paths = [
27303
- join12(process.cwd(), "config", "presets.json"),
27304
- join12(process.cwd(), "config", "specialists", "presets.json")
27718
+ join13(process.cwd(), "config", "presets.json"),
27719
+ join13(process.cwd(), "config", "specialists", "presets.json")
27305
27720
  ];
27306
27721
  for (const p of paths) {
27307
- if (existsSync13(p)) {
27722
+ if (existsSync14(p)) {
27308
27723
  try {
27309
- const data = JSON.parse(readFileSync10(p, "utf-8"));
27724
+ const data = JSON.parse(readFileSync11(p, "utf-8"));
27310
27725
  return data;
27311
27726
  } catch {
27312
27727
  return {};
@@ -27346,7 +27761,7 @@ function fail(message) {
27346
27761
  console.error(message);
27347
27762
  process.exit(1);
27348
27763
  }
27349
- function parseArgs5(argv) {
27764
+ function parseArgs6(argv) {
27350
27765
  if (argv.includes("--list-presets")) {
27351
27766
  return { all: false, dryRun: false, action: "list-presets" };
27352
27767
  }
@@ -27512,7 +27927,7 @@ ${usage()}`);
27512
27927
  if (action === "get" && (pendingArrayOp || filePath)) {
27513
27928
  fail("Error: --get cannot be combined with --append/--remove/--file");
27514
27929
  }
27515
- if (filePath && !existsSync13(filePath)) {
27930
+ if (filePath && !existsSync14(filePath)) {
27516
27931
  fail(`Error: file not found: ${filePath}`);
27517
27932
  }
27518
27933
  return { name, all, scope, dryRun, action, path, value, filePath, preset, forkFrom };
@@ -27652,7 +28067,7 @@ function getRawValue(args, resolvedPath) {
27652
28067
  if (!MULTILINE_FILE_PATHS.has(resolvedPath.normalizedPath)) {
27653
28068
  fail(`Error: --file is only supported for: ${Array.from(MULTILINE_FILE_PATHS).join(", ")}`);
27654
28069
  }
27655
- return readFileSync10(args.filePath, "utf-8");
28070
+ return readFileSync11(args.filePath, "utf-8");
27656
28071
  }
27657
28072
  function getAtPath(root, segments) {
27658
28073
  let current = root;
@@ -27720,7 +28135,7 @@ ${bold9(`[dry-run] ${filePath}`)}
27720
28135
  }
27721
28136
  function readJsonFile2(filePath) {
27722
28137
  try {
27723
- const parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
28138
+ const parsed = JSON.parse(readFileSync11(filePath, "utf-8"));
27724
28139
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
27725
28140
  fail(`Error: specialist file must contain a JSON object (${filePath})`);
27726
28141
  }
@@ -27733,9 +28148,9 @@ function readJsonFile2(filePath) {
27733
28148
  function createUserFork(source, targetName) {
27734
28149
  if (source.scope === "user")
27735
28150
  return source;
27736
- const targetDir = join12(process.cwd(), ".specialists", "user");
28151
+ const targetDir = join13(process.cwd(), ".specialists", "user");
27737
28152
  mkdirSync6(targetDir, { recursive: true });
27738
- const targetFile = join12(targetDir, `${targetName}.specialist.json`);
28153
+ const targetFile = join13(targetDir, `${targetName}.specialist.json`);
27739
28154
  const doc2 = readJsonFile2(source.filePath);
27740
28155
  doc2.specialist = doc2.specialist ?? {};
27741
28156
  doc2.specialist.metadata = doc2.specialist.metadata ?? {};
@@ -27758,8 +28173,8 @@ async function resolveTargets(args) {
27758
28173
  }
27759
28174
  return [match];
27760
28175
  }
27761
- async function run10() {
27762
- const args = parseArgs5(process.argv.slice(3));
28176
+ async function run11() {
28177
+ const args = parseArgs6(process.argv.slice(3));
27763
28178
  if (args.action === "list-presets") {
27764
28179
  const presets = loadPresets();
27765
28180
  const entries = Object.entries(presets);
@@ -27784,7 +28199,7 @@ async function run10() {
27784
28199
  }
27785
28200
  const targets2 = await resolveTargets(args);
27786
28201
  for (const target of targets2) {
27787
- const raw = readFileSync10(target.filePath, "utf-8");
28202
+ const raw = readFileSync11(target.filePath, "utf-8");
27788
28203
  const doc2 = JSON.parse(raw);
27789
28204
  for (const [fieldPath, fieldValue] of Object.entries(preset.fields)) {
27790
28205
  const resolved = resolvePath2(fieldPath);
@@ -27819,7 +28234,7 @@ async function run10() {
27819
28234
  fail("Error: no specialists found");
27820
28235
  }
27821
28236
  for (const target of targets) {
27822
- const raw = readFileSync10(target.filePath, "utf-8");
28237
+ const raw = readFileSync11(target.filePath, "utf-8");
27823
28238
  let doc2;
27824
28239
  try {
27825
28240
  const parsed = JSON.parse(raw);
@@ -27877,7 +28292,7 @@ var init_edit = __esm(() => {
27877
28292
  // src/cli/config.ts
27878
28293
  var exports_config = {};
27879
28294
  __export(exports_config, {
27880
- run: () => run11
28295
+ run: () => run12
27881
28296
  });
27882
28297
  function usage2() {
27883
28298
  return [
@@ -27949,12 +28364,12 @@ ${usage2()}`);
27949
28364
  }
27950
28365
  return translated;
27951
28366
  }
27952
- async function run11() {
28367
+ async function run12() {
27953
28368
  const originalArgs = process.argv.slice(3);
27954
28369
  const editArgs = buildEditArgv(originalArgs);
27955
28370
  console.error(`${yellow9("\u26A0 DEPRECATED")} specialists config is deprecated. Use ${yellow9("specialists edit")} instead.`);
27956
28371
  process.argv = [process.argv[0] ?? "node", process.argv[1] ?? "specialists", "edit", ...editArgs];
27957
- await run10();
28372
+ await run11();
27958
28373
  }
27959
28374
  var yellow9 = (s) => `\x1B[33m${s}\x1B[0m`;
27960
28375
  var init_config = __esm(() => {
@@ -27962,8 +28377,8 @@ var init_config = __esm(() => {
27962
28377
  });
27963
28378
 
27964
28379
  // src/specialist/worktree.ts
27965
- import { existsSync as existsSync14, symlinkSync as symlinkSync2, mkdirSync as mkdirSync7 } from "fs";
27966
- import { join as join13, resolve as resolve7 } from "path";
28380
+ import { existsSync as existsSync15, symlinkSync as symlinkSync2, mkdirSync as mkdirSync7 } from "fs";
28381
+ import { join as join14, resolve as resolve8 } from "path";
27967
28382
  import { spawnSync as spawnSync10, execFileSync as execFileSync2 } from "child_process";
27968
28383
  function deriveBranchName(beadId, specialistName) {
27969
28384
  return `feature/${beadId}-${slugify(specialistName)}`;
@@ -27996,22 +28411,22 @@ function provisionWorktree(options) {
27996
28411
  const branch = deriveBranchName(options.beadId, options.specialistName);
27997
28412
  const existingPath = findExistingWorktree(branch, cwd);
27998
28413
  if (existingPath) {
27999
- return { branch, worktreePath: resolve7(existingPath), reused: true };
28414
+ return { branch, worktreePath: resolve8(existingPath), reused: true };
28000
28415
  }
28001
- const worktreeBase = options.worktreeBase ?? join13(commonRoot, ".worktrees", options.beadId);
28416
+ const worktreeBase = options.worktreeBase ?? join14(commonRoot, ".worktrees", options.beadId);
28002
28417
  const worktreeName = deriveWorktreeName(options.beadId, options.specialistName);
28003
- const worktreePath = resolve7(join13(worktreeBase, worktreeName));
28418
+ const worktreePath = resolve8(join14(worktreeBase, worktreeName));
28004
28419
  createWorktreeViaBd(worktreePath, branch, commonRoot);
28005
28420
  symlinkPiNpmCache(commonRoot, worktreePath);
28006
28421
  return { branch, worktreePath, reused: false };
28007
28422
  }
28008
28423
  function symlinkPiNpmCache(commonRoot, worktreePath) {
28009
- const source = join13(commonRoot, ".pi", "npm");
28010
- const target = join13(worktreePath, ".pi", "npm");
28011
- if (!existsSync14(source) || existsSync14(target))
28424
+ const source = join14(commonRoot, ".pi", "npm");
28425
+ const target = join14(worktreePath, ".pi", "npm");
28426
+ if (!existsSync15(source) || existsSync15(target))
28012
28427
  return;
28013
28428
  try {
28014
- mkdirSync7(join13(worktreePath, ".pi"), { recursive: true });
28429
+ mkdirSync7(join14(worktreePath, ".pi"), { recursive: true });
28015
28430
  symlinkSync2(source, target);
28016
28431
  } catch {}
28017
28432
  }
@@ -28058,7 +28473,7 @@ __export(exports_merge, {
28058
28473
  runTypecheckGate: () => runTypecheckGate,
28059
28474
  runRebuild: () => runRebuild,
28060
28475
  runMergePlan: () => runMergePlan,
28061
- run: () => run12,
28476
+ run: () => run13,
28062
28477
  resolveMergeTargetsForBeadIds: () => resolveMergeTargetsForBeadIds,
28063
28478
  resolveMergeTargets: () => resolveMergeTargets,
28064
28479
  resolveChainEpicMembership: () => resolveChainEpicMembership,
@@ -28074,9 +28489,9 @@ __export(exports_merge, {
28074
28489
  checkEpicUnresolvedGuard: () => checkEpicUnresolvedGuard,
28075
28490
  assertMainRepoCleanForMerge: () => assertMainRepoCleanForMerge
28076
28491
  });
28077
- import { existsSync as existsSync15, readdirSync as readdirSync5, readFileSync as readFileSync11 } from "fs";
28492
+ import { existsSync as existsSync16, readdirSync as readdirSync6, readFileSync as readFileSync12 } from "fs";
28078
28493
  import { spawnSync as spawnSync11 } from "child_process";
28079
- import { join as join14 } from "path";
28494
+ import { join as join15 } from "path";
28080
28495
  function parseOptions(argv) {
28081
28496
  let target = "";
28082
28497
  let rebuild = false;
@@ -28254,17 +28669,17 @@ Use 'sp epic merge ${membership.epicId}' to publish all chains together, or 'sp
28254
28669
  }
28255
28670
  function readAllJobStatuses() {
28256
28671
  const jobsDir = resolveJobsDir();
28257
- if (!existsSync15(jobsDir))
28672
+ if (!existsSync16(jobsDir))
28258
28673
  return [];
28259
- const entries = readdirSync5(jobsDir, { withFileTypes: true });
28674
+ const entries = readdirSync6(jobsDir, { withFileTypes: true });
28260
28675
  const statuses = [];
28261
28676
  for (const entry of entries) {
28262
28677
  if (!entry.isDirectory())
28263
28678
  continue;
28264
- const statusPath = join14(jobsDir, entry.name, "status.json");
28265
- if (!existsSync15(statusPath))
28679
+ const statusPath = join15(jobsDir, entry.name, "status.json");
28680
+ if (!existsSync16(statusPath))
28266
28681
  continue;
28267
- const parsed = readJson(readFileSync11(statusPath, "utf-8"));
28682
+ const parsed = readJson(readFileSync12(statusPath, "utf-8"));
28268
28683
  if (!parsed || typeof parsed !== "object")
28269
28684
  continue;
28270
28685
  statuses.push(parsed);
@@ -28663,7 +29078,7 @@ function executePublicationPlan(targets, options) {
28663
29078
  throw error2;
28664
29079
  }
28665
29080
  }
28666
- async function run12() {
29081
+ async function run13() {
28667
29082
  let options;
28668
29083
  try {
28669
29084
  options = parseOptions(process.argv.slice(3));
@@ -28921,13 +29336,13 @@ var init_format_helpers = __esm(() => {
28921
29336
  // src/cli/run.ts
28922
29337
  var exports_run = {};
28923
29338
  __export(exports_run, {
28924
- run: () => run13
29339
+ run: () => run14
28925
29340
  });
28926
- import { join as join15 } from "path";
28927
- import { readFileSync as readFileSync12 } from "fs";
29341
+ import { join as join16 } from "path";
29342
+ import { readFileSync as readFileSync13 } from "fs";
28928
29343
  import { randomBytes } from "crypto";
28929
29344
  import { spawn as cpSpawn, execSync as execSync3 } from "child_process";
28930
- async function parseArgs6(argv) {
29345
+ async function parseArgs7(argv) {
28931
29346
  const name = argv[0];
28932
29347
  if (!name || name.startsWith("--")) {
28933
29348
  console.error('Usage: specialists|sp run <name> [--prompt "..."] [--bead <id>] ' + "[--worktree] [--job <id>] [--force-job] [--epic <id>] [--force-stale-base] [--context-depth <n>] [--model <model>] " + "[--no-beads] [--no-bead-notes] [--keep-alive|--no-keep-alive] [--json|--raw]");
@@ -28942,7 +29357,7 @@ async function parseArgs6(argv) {
28942
29357
  let noKeepAlive = false;
28943
29358
  let background = false;
28944
29359
  let outputMode = "human";
28945
- let contextDepth = 1;
29360
+ let contextDepth = 3;
28946
29361
  let worktree = false;
28947
29362
  let reuseJobId;
28948
29363
  let forceJob = false;
@@ -29039,13 +29454,13 @@ async function parseArgs6(argv) {
29039
29454
  process.exit(1);
29040
29455
  }
29041
29456
  if (!prompt && !beadId && !process.stdin.isTTY) {
29042
- prompt = await new Promise((resolve8) => {
29457
+ prompt = await new Promise((resolve9) => {
29043
29458
  let buf = "";
29044
29459
  process.stdin.setEncoding("utf-8");
29045
29460
  process.stdin.on("data", (chunk) => {
29046
29461
  buf += chunk;
29047
29462
  });
29048
- process.stdin.on("end", () => resolve8(buf.trim()));
29463
+ process.stdin.on("end", () => resolve9(buf.trim()));
29049
29464
  });
29050
29465
  }
29051
29466
  if (!prompt && !beadId && !reuseJobId) {
@@ -29197,13 +29612,13 @@ function resolveWorkingDirectory(args, jobsDir, permissionRequired, readStatus)
29197
29612
  return {};
29198
29613
  }
29199
29614
  function startEventTailer(jobId, jobsDir, mode, specialist, beadId) {
29200
- const eventsPath = join15(jobsDir, jobId, "events.jsonl");
29615
+ const eventsPath = join16(jobsDir, jobId, "events.jsonl");
29201
29616
  let linesRead = 0;
29202
29617
  let activeInlinePhase = null;
29203
29618
  const drain = () => {
29204
29619
  let content;
29205
29620
  try {
29206
- content = readFileSync12(eventsPath, "utf-8");
29621
+ content = readFileSync13(eventsPath, "utf-8");
29207
29622
  } catch {
29208
29623
  return;
29209
29624
  }
@@ -29259,7 +29674,7 @@ function formatFooterModel(backend, model) {
29259
29674
  return model;
29260
29675
  return model.startsWith(`${backend}/`) ? model : `${backend}/${model}`;
29261
29676
  }
29262
- function shellQuote(value) {
29677
+ function shellQuote2(value) {
29263
29678
  return `'${value.replace(/'/g, `'\\''`)}'`;
29264
29679
  }
29265
29680
  function extractReviewedJobIdOverride(prompt) {
@@ -29294,46 +29709,75 @@ function buildInjectedReviewerDiffVariables(cwd, maxFiles = 20) {
29294
29709
  };
29295
29710
  const MAX_TOTAL_HUNKS_CHARS = 12000;
29296
29711
  const MAX_FILE_DIFF_CHARS = 2000;
29297
- const stat2 = read("git diff --stat");
29298
- const files = read("git diff --name-only").split(`
29712
+ const resolveMergeBase = () => {
29713
+ const headRef = read("git symbolic-ref refs/remotes/origin/HEAD");
29714
+ const baseBranch = headRef ? headRef.split("/").pop() ?? "main" : "main";
29715
+ return read(`git merge-base ${shellQuote2(baseBranch)} HEAD`);
29716
+ };
29717
+ const mergeBase = resolveMergeBase();
29718
+ const sources = [
29719
+ {
29720
+ label: "unstaged diff",
29721
+ statCmd: "git diff --stat",
29722
+ namesCmd: "git diff --name-only",
29723
+ diffCmd: (f) => `git diff -- ${shellQuote2(f)}`
29724
+ },
29725
+ {
29726
+ label: "staged diff",
29727
+ statCmd: "git diff --cached --stat",
29728
+ namesCmd: "git diff --cached --name-only",
29729
+ diffCmd: (f) => `git diff --cached -- ${shellQuote2(f)}`
29730
+ },
29731
+ ...mergeBase ? [{
29732
+ label: `branch-vs-base diff (${mergeBase.slice(0, 12)}..HEAD)`,
29733
+ statCmd: `git diff --stat ${shellQuote2(mergeBase)}..HEAD`,
29734
+ namesCmd: `git diff --name-only ${shellQuote2(mergeBase)}..HEAD`,
29735
+ diffCmd: (f) => `git diff ${shellQuote2(mergeBase)}..HEAD -- ${shellQuote2(f)}`
29736
+ }] : []
29737
+ ];
29738
+ for (const src of sources) {
29739
+ const stat2 = read(src.statCmd);
29740
+ const files = read(src.namesCmd).split(`
29299
29741
  `).map((line) => line.trim()).filter(Boolean).slice(0, maxFiles);
29300
- if (files.length === 0)
29301
- return {};
29302
- let remaining = MAX_TOTAL_HUNKS_CHARS;
29303
- const sections = [];
29304
- for (const file of files) {
29305
- if (remaining <= 0)
29306
- break;
29307
- const diff = read(`git diff -- ${shellQuote(file)}`);
29308
- const truncated = diff.length > MAX_FILE_DIFF_CHARS ? `${diff.slice(0, MAX_FILE_DIFF_CHARS)}
29742
+ if (files.length === 0)
29743
+ continue;
29744
+ let remaining = MAX_TOTAL_HUNKS_CHARS;
29745
+ const sections = [];
29746
+ for (const file of files) {
29747
+ if (remaining <= 0)
29748
+ break;
29749
+ const diff = read(src.diffCmd(file));
29750
+ const truncated = diff.length > MAX_FILE_DIFF_CHARS ? `${diff.slice(0, MAX_FILE_DIFF_CHARS)}
29309
29751
  ... [truncated]` : diff;
29310
- const section = truncated ? `### ${file}
29752
+ const section = truncated ? `### ${file}
29311
29753
  ${truncated}` : `### ${file}
29312
29754
  (no hunks)`;
29313
- if (section.length > remaining) {
29314
- sections.push(`${section.slice(0, remaining)}
29755
+ if (section.length > remaining) {
29756
+ sections.push(`${section.slice(0, remaining)}
29315
29757
  ... [truncated]`);
29316
- remaining = 0;
29317
- break;
29758
+ remaining = 0;
29759
+ break;
29760
+ }
29761
+ sections.push(section);
29762
+ remaining -= section.length + 2;
29318
29763
  }
29319
- sections.push(section);
29320
- remaining -= section.length + 2;
29321
- }
29322
- const hunks = sections.join(`
29764
+ const hunks = sections.join(`
29323
29765
 
29324
29766
  `);
29325
- if (!hunks.trim())
29326
- return {};
29327
- return {
29328
- reviewer_diff_source: "injected diff context",
29329
- reviewer_diff_stat: stat2 || "(no stat)",
29330
- reviewer_diff_files: files.join(`
29767
+ if (!hunks.trim())
29768
+ continue;
29769
+ return {
29770
+ reviewer_diff_source: `injected diff context (${src.label})`,
29771
+ reviewer_diff_stat: stat2 || "(no stat)",
29772
+ reviewer_diff_files: files.join(`
29331
29773
  `),
29332
- reviewer_diff_hunks: hunks
29333
- };
29774
+ reviewer_diff_hunks: hunks
29775
+ };
29776
+ }
29777
+ return {};
29334
29778
  }
29335
- async function run13() {
29336
- const args = await parseArgs6(process.argv.slice(3));
29779
+ async function run14() {
29780
+ const args = await parseArgs7(process.argv.slice(3));
29337
29781
  const loader = new SpecialistLoader;
29338
29782
  const specialist = await loader.get(args.name).catch((err) => {
29339
29783
  process.stderr.write(`Error: ${err?.message ?? err}
@@ -29354,17 +29798,17 @@ async function run13() {
29354
29798
  }
29355
29799
  if (args.background) {
29356
29800
  const jobsDir2 = resolveJobsDir();
29357
- const latestPath = join15(jobsDir2, "latest");
29801
+ const latestPath = join16(jobsDir2, "latest");
29358
29802
  const oldLatest = (() => {
29359
29803
  try {
29360
- return readFileSync12(latestPath, "utf-8").trim();
29804
+ return readFileSync13(latestPath, "utf-8").trim();
29361
29805
  } catch {
29362
29806
  return "";
29363
29807
  }
29364
29808
  })();
29365
29809
  const cwd = process.cwd();
29366
29810
  const innerArgs = process.argv.slice(2).filter((a) => a !== "--background");
29367
- const cmd = `${process.execPath} ${process.argv[1]} ${innerArgs.map(shellQuote).join(" ")}`;
29811
+ const cmd = `${process.execPath} ${process.argv[1]} ${innerArgs.map(shellQuote2).join(" ")}`;
29368
29812
  let childPid;
29369
29813
  if (isTmuxAvailable()) {
29370
29814
  const suffix = randomBytes(3).toString("hex");
@@ -29385,7 +29829,7 @@ async function run13() {
29385
29829
  while (Date.now() < deadline) {
29386
29830
  await new Promise((r) => setTimeout(r, 100));
29387
29831
  try {
29388
- const current = readFileSync12(latestPath, "utf-8").trim();
29832
+ const current = readFileSync13(latestPath, "utf-8").trim();
29389
29833
  if (current && current !== oldLatest) {
29390
29834
  jobId2 = current;
29391
29835
  break;
@@ -29404,7 +29848,7 @@ async function run13() {
29404
29848
  process.exit(0);
29405
29849
  }
29406
29850
  const circuitBreaker = new CircuitBreaker;
29407
- const hooks = new HookEmitter({ tracePath: join15(process.cwd(), ".specialists", "trace.jsonl") });
29851
+ const hooks = new HookEmitter({ tracePath: join16(process.cwd(), ".specialists", "trace.jsonl") });
29408
29852
  const beadsClient = args.noBeads ? undefined : new BeadsClient;
29409
29853
  const beadReader = beadsClient ?? new BeadsClient;
29410
29854
  let prompt = args.prompt;
@@ -29642,8 +30086,8 @@ var init_node_resolve = __esm(() => {
29642
30086
  });
29643
30087
 
29644
30088
  // src/specialist/job-control.ts
29645
- import { existsSync as existsSync16, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
29646
- import { join as join16 } from "path";
30089
+ import { existsSync as existsSync17, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
30090
+ import { join as join17 } from "path";
29647
30091
 
29648
30092
  class JobControl {
29649
30093
  supervisor;
@@ -29674,8 +30118,8 @@ class JobControl {
29674
30118
  }
29675
30119
  };
29676
30120
  let resolveJobId;
29677
- const jobIdPromise = new Promise((resolve8) => {
29678
- resolveJobId = resolve8;
30121
+ const jobIdPromise = new Promise((resolve9) => {
30122
+ resolveJobId = resolve9;
29679
30123
  });
29680
30124
  this.supervisor = new Supervisor({
29681
30125
  runner: this.runner,
@@ -29718,10 +30162,10 @@ class JobControl {
29718
30162
  return sqliteResult;
29719
30163
  } catch {}
29720
30164
  const resultPath = this.resultPath(jobId);
29721
- if (!existsSync16(resultPath))
30165
+ if (!existsSync17(resultPath))
29722
30166
  return null;
29723
30167
  try {
29724
- return readFileSync13(resultPath, "utf-8");
30168
+ return readFileSync14(resultPath, "utf-8");
29725
30169
  } catch {
29726
30170
  return null;
29727
30171
  }
@@ -29740,7 +30184,7 @@ class JobControl {
29740
30184
  if (deadline !== undefined && Date.now() >= deadline) {
29741
30185
  throw new Error(`Timed out waiting for terminal status for job ${jobId}`);
29742
30186
  }
29743
- await new Promise((resolve8) => setTimeout(resolve8, backoffMs));
30187
+ await new Promise((resolve9) => setTimeout(resolve9, backoffMs));
29744
30188
  backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
29745
30189
  }
29746
30190
  }
@@ -29757,7 +30201,7 @@ class JobControl {
29757
30201
  writeFileSync7(status.fifo_path, jsonLine, { flag: "a" });
29758
30202
  }
29759
30203
  resultPath(jobId) {
29760
- return join16(this.jobsDir, jobId, "result.txt");
30204
+ return join17(this.jobsDir, jobId, "result.txt");
29761
30205
  }
29762
30206
  }
29763
30207
  var TERMINAL_STATUSES2, INITIAL_BACKOFF_MS = 100, MAX_BACKOFF_MS = 2000;
@@ -29919,7 +30363,7 @@ function hashOutput(output2, salt) {
29919
30363
  return createHash3("sha256").update(value).digest("hex");
29920
30364
  }
29921
30365
  function sleep2(ms) {
29922
- return new Promise((resolve8) => setTimeout(resolve8, ms));
30366
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
29923
30367
  }
29924
30368
  function toContextHealth(contextPct) {
29925
30369
  if (contextPct === null)
@@ -31832,10 +32276,10 @@ var exports_node = {};
31832
32276
  __export(exports_node, {
31833
32277
  handleNodeCommand: () => handleNodeCommand
31834
32278
  });
31835
- import { existsSync as existsSync17, readFileSync as readFileSync14, readdirSync as readdirSync6 } from "fs";
32279
+ import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync7 } from "fs";
31836
32280
  import { randomUUID as randomUUID2 } from "crypto";
31837
32281
  import { spawnSync as spawnSync13 } from "child_process";
31838
- import { basename as basename4, join as join17, resolve as resolve8 } from "path";
32282
+ import { basename as basename5, join as join18, resolve as resolve9 } from "path";
31839
32283
  function parseNodeArgs(argv) {
31840
32284
  const command = argv[0];
31841
32285
  const supportedCommands = new Set(["run", "list", "promote", "members", "memory", "stop", "spawn-member", "create-bead", "complete", "wait-phase"]);
@@ -32018,18 +32462,18 @@ function parseNodeArgs(argv) {
32018
32462
  };
32019
32463
  }
32020
32464
  function toNodeName(filePath) {
32021
- const fileName = basename4(filePath);
32465
+ const fileName = basename5(filePath);
32022
32466
  return fileName.endsWith(NODE_CONFIG_SUFFIX) ? fileName.slice(0, -NODE_CONFIG_SUFFIX.length) : fileName;
32023
32467
  }
32024
32468
  function discoverNodeConfigs(cwd) {
32025
32469
  const discoveredByName = new Map;
32026
32470
  for (const directory of NODE_DISCOVERY_DIRS) {
32027
- const absoluteDir = resolve8(cwd, directory.path);
32028
- if (!existsSync17(absoluteDir))
32471
+ const absoluteDir = resolve9(cwd, directory.path);
32472
+ if (!existsSync18(absoluteDir))
32029
32473
  continue;
32030
- const files = readdirSync6(absoluteDir).filter((fileName) => fileName.endsWith(NODE_CONFIG_SUFFIX));
32474
+ const files = readdirSync7(absoluteDir).filter((fileName) => fileName.endsWith(NODE_CONFIG_SUFFIX));
32031
32475
  for (const fileName of files) {
32032
- const path = join17(absoluteDir, fileName);
32476
+ const path = join18(absoluteDir, fileName);
32033
32477
  const name = toNodeName(fileName);
32034
32478
  if (discoveredByName.has(name))
32035
32479
  continue;
@@ -32046,8 +32490,8 @@ function getNodeDiscoverySummary() {
32046
32490
  `);
32047
32491
  }
32048
32492
  function resolveNodeConfigPath(cwd, input2) {
32049
- const explicitPath = resolve8(cwd, input2);
32050
- if (existsSync17(explicitPath)) {
32493
+ const explicitPath = resolve9(cwd, input2);
32494
+ if (existsSync18(explicitPath)) {
32051
32495
  return { name: toNodeName(explicitPath), path: explicitPath, source: "repo" };
32052
32496
  }
32053
32497
  const normalizedName = input2.endsWith(NODE_CONFIG_SUFFIX) ? input2.slice(0, -NODE_CONFIG_SUFFIX.length) : input2;
@@ -32147,13 +32591,13 @@ async function handleNodeRun(args) {
32147
32591
  rawConfig = args.inlineJson;
32148
32592
  } else {
32149
32593
  const nodeConfigPath = resolveNodeConfigPath(process.cwd(), args.nodeConfigInput);
32150
- rawConfig = readFileSync14(nodeConfigPath.path, "utf-8");
32594
+ rawConfig = readFileSync15(nodeConfigPath.path, "utf-8");
32151
32595
  }
32152
32596
  const config2 = parseNodeConfig(rawConfig);
32153
32597
  const loader = new SpecialistLoader;
32154
32598
  const runner = new SpecialistRunner({
32155
32599
  loader,
32156
- hooks: new HookEmitter({ tracePath: join17(process.cwd(), ".specialists", "trace.jsonl") }),
32600
+ hooks: new HookEmitter({ tracePath: join18(process.cwd(), ".specialists", "trace.jsonl") }),
32157
32601
  circuitBreaker: new CircuitBreaker
32158
32602
  });
32159
32603
  const nodeId = `${config2.name}-${randomUUID2().slice(0, 8)}`;
@@ -32521,7 +32965,7 @@ async function createNodeActionRunnerDependencies() {
32521
32965
  const loader = new SpecialistLoader;
32522
32966
  const runner = new SpecialistRunner({
32523
32967
  loader,
32524
- hooks: new HookEmitter({ tracePath: join17(process.cwd(), ".specialists", "trace.jsonl") }),
32968
+ hooks: new HookEmitter({ tracePath: join18(process.cwd(), ".specialists", "trace.jsonl") }),
32525
32969
  circuitBreaker: new CircuitBreaker
32526
32970
  });
32527
32971
  return { loader, runner };
@@ -32599,7 +33043,7 @@ async function handleNodeAction(args) {
32599
33043
  if (deadline !== null && Date.now() >= deadline) {
32600
33044
  throw new Error(`Timed out waiting for phase '${args.phaseId}' members: ${memberKeys.join(", ")}`);
32601
33045
  }
32602
- await new Promise((resolve9) => setTimeout(resolve9, 500));
33046
+ await new Promise((resolve10) => setTimeout(resolve10, 500));
32603
33047
  }
32604
33048
  } finally {
32605
33049
  sqliteClient.close();
@@ -32690,13 +33134,13 @@ var init_node = __esm(() => {
32690
33134
  });
32691
33135
 
32692
33136
  // src/specialist/epic-reconciler.ts
32693
- import { mkdirSync as mkdirSync8, openSync as openSync2, readFileSync as readFileSync15, rmSync as rmSync2, writeFileSync as writeFileSync8 } from "fs";
32694
- import { join as join18 } from "path";
33137
+ import { mkdirSync as mkdirSync8, openSync as openSync2, readFileSync as readFileSync16, rmSync as rmSync2, writeFileSync as writeFileSync8 } from "fs";
33138
+ import { join as join19 } from "path";
32695
33139
  function buildEpicLockPath(epicId) {
32696
33140
  const location = resolveObservabilityDbLocation();
32697
- const lockDir = join18(location.dbDirectory, "locks");
33141
+ const lockDir = join19(location.dbDirectory, "locks");
32698
33142
  mkdirSync8(lockDir, { recursive: true });
32699
- return join18(lockDir, `epic-${epicId}.lock`);
33143
+ return join19(lockDir, `epic-${epicId}.lock`);
32700
33144
  }
32701
33145
  function withEpicAdvisoryLock(epicId, action) {
32702
33146
  const lockPath = buildEpicLockPath(epicId);
@@ -32707,7 +33151,7 @@ function withEpicAdvisoryLock(epicId, action) {
32707
33151
  } catch {
32708
33152
  let holder = "unknown";
32709
33153
  try {
32710
- holder = readFileSync15(lockPath, "utf-8");
33154
+ holder = readFileSync16(lockPath, "utf-8");
32711
33155
  } catch {
32712
33156
  holder = "unknown";
32713
33157
  }
@@ -33108,15 +33552,15 @@ function evaluateReadiness(epicId, state, chainRecords, sqlite) {
33108
33552
  }
33109
33553
  function gatherEpicList(sqlite, unresolvedOnly) {
33110
33554
  const epicRuns = sqlite.listEpicRuns();
33111
- return epicRuns.filter((run14) => !unresolvedOnly || isEpicUnresolvedState(run14.status)).map((run14) => {
33112
- const chainRecords = sqlite.listEpicChains(run14.epic_id);
33113
- const readiness = evaluateReadiness(run14.epic_id, run14.status, chainRecords, sqlite);
33555
+ return epicRuns.filter((run15) => !unresolvedOnly || isEpicUnresolvedState(run15.status)).map((run15) => {
33556
+ const chainRecords = sqlite.listEpicChains(run15.epic_id);
33557
+ const readiness = evaluateReadiness(run15.epic_id, run15.status, chainRecords, sqlite);
33114
33558
  return {
33115
- epic_id: run14.epic_id,
33116
- state: run14.status,
33559
+ epic_id: run15.epic_id,
33560
+ state: run15.status,
33117
33561
  chain_count: chainRecords.length,
33118
33562
  readiness,
33119
- updated_at_ms: run14.updated_at_ms
33563
+ updated_at_ms: run15.updated_at_ms
33120
33564
  };
33121
33565
  });
33122
33566
  }
@@ -33680,12 +34124,12 @@ var init_epic = __esm(() => {
33680
34124
  // src/cli/status.ts
33681
34125
  var exports_status = {};
33682
34126
  __export(exports_status, {
33683
- run: () => run14,
34127
+ run: () => run15,
33684
34128
  detectJobOutputMode: () => detectJobOutputMode
33685
34129
  });
33686
34130
  import { spawnSync as spawnSync15 } from "child_process";
33687
- import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
33688
- import { join as join19 } from "path";
34131
+ import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
34132
+ import { join as join20 } from "path";
33689
34133
  function ok2(msg) {
33690
34134
  console.log(` ${green8("\u2713")} ${msg}`);
33691
34135
  }
@@ -33782,10 +34226,10 @@ function countJobEvents(sqliteClient, jobsDir, jobId) {
33782
34226
  if (detectJobOutputMode() !== "on") {
33783
34227
  return 0;
33784
34228
  }
33785
- const eventsFile = join19(jobsDir, jobId, "events.jsonl");
33786
- if (!existsSync18(eventsFile))
34229
+ const eventsFile = join20(jobsDir, jobId, "events.jsonl");
34230
+ if (!existsSync19(eventsFile))
33787
34231
  return 0;
33788
- const raw = readFileSync16(eventsFile, "utf-8").trim();
34232
+ const raw = readFileSync17(eventsFile, "utf-8").trim();
33789
34233
  if (!raw)
33790
34234
  return 0;
33791
34235
  return raw.split(`
@@ -33819,10 +34263,10 @@ function getLatestContextSnapshot(sqliteClient, jobsDir, jobId) {
33819
34263
  if (detectJobOutputMode() !== "on") {
33820
34264
  return null;
33821
34265
  }
33822
- const eventsFile = join19(jobsDir, jobId, "events.jsonl");
33823
- if (!existsSync18(eventsFile))
34266
+ const eventsFile = join20(jobsDir, jobId, "events.jsonl");
34267
+ if (!existsSync19(eventsFile))
33824
34268
  return null;
33825
- const lines = readFileSync16(eventsFile, "utf-8").split(`
34269
+ const lines = readFileSync17(eventsFile, "utf-8").split(`
33826
34270
  `);
33827
34271
  for (let index = lines.length - 1;index >= 0; index -= 1) {
33828
34272
  const line = lines[index].trim();
@@ -33905,7 +34349,7 @@ ${bold10("specialists status")}
33905
34349
  console.log(` error ${red2(job.error)}`);
33906
34350
  console.log();
33907
34351
  }
33908
- async function run14() {
34352
+ async function run15() {
33909
34353
  const argv = process.argv.slice(3);
33910
34354
  let parsedArgs;
33911
34355
  try {
@@ -33927,12 +34371,12 @@ async function run14() {
33927
34371
  `).slice(1).map((line) => line.split(/\s+/)[0]).filter(Boolean)) : new Set;
33928
34372
  const bdInstalled = isInstalled2("bd");
33929
34373
  const bdVersion = bdInstalled ? cmd("bd", ["--version"]) : null;
33930
- const beadsPresent = existsSync18(join19(process.cwd(), ".beads"));
34374
+ const beadsPresent = existsSync19(join20(process.cwd(), ".beads"));
33931
34375
  const specialistsBin = cmd("which", ["specialists"]);
33932
34376
  const jobsDir = resolveJobsDir();
33933
34377
  const jobFileOutputMode = detectJobFileOutputMode();
33934
34378
  let jobs = [];
33935
- if (existsSync18(jobsDir)) {
34379
+ if (existsSync19(jobsDir)) {
33936
34380
  supervisor = new Supervisor({
33937
34381
  runner: null,
33938
34382
  runOptions: null,
@@ -34092,12 +34536,12 @@ var init_status = __esm(() => {
34092
34536
  // src/cli/ps.ts
34093
34537
  var exports_ps = {};
34094
34538
  __export(exports_ps, {
34095
- run: () => run15
34539
+ run: () => run16
34096
34540
  });
34097
34541
  import { spawnSync as spawnSync16 } from "child_process";
34098
- import { existsSync as existsSync19, readdirSync as readdirSync7, readFileSync as readFileSync17 } from "fs";
34099
- import { join as join20 } from "path";
34100
- function parseArgs7(argv) {
34542
+ import { existsSync as existsSync20, readdirSync as readdirSync8, readFileSync as readFileSync18 } from "fs";
34543
+ import { join as join21 } from "path";
34544
+ function parseArgs8(argv) {
34101
34545
  let nodeId;
34102
34546
  const positional = [];
34103
34547
  for (let i = 0;i < argv.length; i += 1) {
@@ -34126,25 +34570,25 @@ function isVisibleStatus(status, all) {
34126
34570
  return ACTIVE_STATES.includes(status);
34127
34571
  }
34128
34572
  function readStatusesFromFiles(jobsDir) {
34129
- if (!existsSync19(jobsDir))
34573
+ if (!existsSync20(jobsDir))
34130
34574
  return [];
34131
34575
  const statuses = [];
34132
- for (const entry of readdirSync7(jobsDir)) {
34133
- const statusPath = join20(jobsDir, entry, "status.json");
34134
- if (!existsSync19(statusPath))
34576
+ for (const entry of readdirSync8(jobsDir)) {
34577
+ const statusPath = join21(jobsDir, entry, "status.json");
34578
+ if (!existsSync20(statusPath))
34135
34579
  continue;
34136
34580
  try {
34137
- statuses.push(JSON.parse(readFileSync17(statusPath, "utf-8")));
34581
+ statuses.push(JSON.parse(readFileSync18(statusPath, "utf-8")));
34138
34582
  } catch {}
34139
34583
  }
34140
34584
  return statuses.sort((a, b) => b.started_at_ms - a.started_at_ms);
34141
34585
  }
34142
34586
  function readLastToolEventFromFile(jobsDir, jobId) {
34143
- const eventsPath = join20(jobsDir, jobId, "events.jsonl");
34144
- if (!existsSync19(eventsPath))
34587
+ const eventsPath = join21(jobsDir, jobId, "events.jsonl");
34588
+ if (!existsSync20(eventsPath))
34145
34589
  return;
34146
34590
  try {
34147
- const lines = readFileSync17(eventsPath, "utf-8").split(`
34591
+ const lines = readFileSync18(eventsPath, "utf-8").split(`
34148
34592
  `);
34149
34593
  for (let index = lines.length - 1;index >= 0; index -= 1) {
34150
34594
  const line = lines[index]?.trim();
@@ -34854,8 +35298,8 @@ async function follow(args) {
34854
35298
  }, 1000);
34855
35299
  });
34856
35300
  }
34857
- async function run15() {
34858
- const args = parseArgs7(process.argv.slice(3));
35301
+ async function run16() {
35302
+ const args = parseArgs8(process.argv.slice(3));
34859
35303
  const sqliteClient = createObservabilitySqliteClient();
34860
35304
  try {
34861
35305
  const resolvedArgs = {
@@ -34900,11 +35344,11 @@ var init_ps = __esm(() => {
34900
35344
  // src/cli/result.ts
34901
35345
  var exports_result = {};
34902
35346
  __export(exports_result, {
34903
- run: () => run16
35347
+ run: () => run17
34904
35348
  });
34905
- import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
34906
- import { join as join21 } from "path";
34907
- function parseArgs8(argv) {
35349
+ import { existsSync as existsSync21, readFileSync as readFileSync19 } from "fs";
35350
+ import { join as join22 } from "path";
35351
+ function parseArgs9(argv) {
34908
35352
  let jobId;
34909
35353
  let nodeId;
34910
35354
  let memberKey;
@@ -34993,10 +35437,10 @@ function readTimelineEventsForResult(sqliteClient, jobsDir, jobId) {
34993
35437
  return sqliteClient.readEvents(jobId);
34994
35438
  } catch {}
34995
35439
  }
34996
- const eventsPath = join21(jobsDir, jobId, "events.jsonl");
34997
- if (!existsSync20(eventsPath))
35440
+ const eventsPath = join22(jobsDir, jobId, "events.jsonl");
35441
+ if (!existsSync21(eventsPath))
34998
35442
  return [];
34999
- return readFileSync18(eventsPath, "utf-8").split(`
35443
+ return readFileSync19(eventsPath, "utf-8").split(`
35000
35444
  `).map((line) => line.trim()).filter(Boolean).map((line) => parseTimelineEvent(line)).filter((event) => event !== null);
35001
35445
  }
35002
35446
  function deriveStartupSnapshot(status, events) {
@@ -35074,8 +35518,8 @@ function formatStartupSnapshot(snapshot) {
35074
35518
  `)}
35075
35519
  `;
35076
35520
  }
35077
- async function run16() {
35078
- const args = parseArgs8(process.argv.slice(3));
35521
+ async function run17() {
35522
+ const args = parseArgs9(process.argv.slice(3));
35079
35523
  const emitJson = (status, output2, error2, startupContext = null) => {
35080
35524
  console.log(JSON.stringify({
35081
35525
  job: status ? {
@@ -35094,7 +35538,7 @@ async function run16() {
35094
35538
  error: error2
35095
35539
  }, null, 2));
35096
35540
  };
35097
- const jobsDir = join21(process.cwd(), ".specialists", "jobs");
35541
+ const jobsDir = join22(process.cwd(), ".specialists", "jobs");
35098
35542
  const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
35099
35543
  const sqliteClient = createObservabilitySqliteClient();
35100
35544
  const emitHumanResult = (output2, status, startupContext, trailingFooter) => {
@@ -35128,7 +35572,7 @@ async function run16() {
35128
35572
  const resolvedNodeId = args.nodeId ? resolveNodeRefWithClient(args.nodeId, sqliteClient) : resolveSingleActiveNodeRef(sqliteClient);
35129
35573
  return resolveJobIdFromNodeMember(sqliteClient, resolvedNodeId, args.memberKey);
35130
35574
  })();
35131
- const resultPath = join21(jobsDir, jobId, "result.txt");
35575
+ const resultPath = join22(jobsDir, jobId, "result.txt");
35132
35576
  const readResultOutput = () => {
35133
35577
  try {
35134
35578
  const sqliteResult = sqliteClient?.readResult(jobId) ?? null;
@@ -35137,10 +35581,10 @@ async function run16() {
35137
35581
  } catch (error2) {
35138
35582
  console.warn(`SQLite result read failed for job ${jobId}; falling back to result.txt`, error2);
35139
35583
  }
35140
- if (!existsSync20(resultPath)) {
35584
+ if (!existsSync21(resultPath)) {
35141
35585
  return null;
35142
35586
  }
35143
- return readFileSync18(resultPath, "utf-8");
35587
+ return readFileSync19(resultPath, "utf-8");
35144
35588
  };
35145
35589
  if (args.wait) {
35146
35590
  const startMs = Date.now();
@@ -35314,10 +35758,10 @@ var init_result = __esm(() => {
35314
35758
  });
35315
35759
 
35316
35760
  // src/specialist/timeline-query.ts
35317
- import { existsSync as existsSync21, readdirSync as readdirSync8, readFileSync as readFileSync19 } from "fs";
35318
- import { basename as basename5, join as join22 } from "path";
35761
+ import { existsSync as existsSync22, readdirSync as readdirSync9, readFileSync as readFileSync20 } from "fs";
35762
+ import { basename as basename6, join as join23 } from "path";
35319
35763
  function readJobEvents(jobDir) {
35320
- const jobId = basename5(jobDir);
35764
+ const jobId = basename6(jobDir);
35321
35765
  try {
35322
35766
  const sqliteEvents = createObservabilitySqliteClient()?.readEvents(jobId) ?? [];
35323
35767
  if (sqliteEvents.length > 0) {
@@ -35327,10 +35771,10 @@ function readJobEvents(jobDir) {
35327
35771
  } catch {}
35328
35772
  if (process.env.SPECIALISTS_JOB_FILE_OUTPUT !== "on")
35329
35773
  return [];
35330
- const eventsPath = join22(jobDir, "events.jsonl");
35331
- if (!existsSync21(eventsPath))
35774
+ const eventsPath = join23(jobDir, "events.jsonl");
35775
+ if (!existsSync22(eventsPath))
35332
35776
  return [];
35333
- const content = readFileSync19(eventsPath, "utf-8");
35777
+ const content = readFileSync20(eventsPath, "utf-8");
35334
35778
  const lines = content.split(`
35335
35779
  `).filter(Boolean);
35336
35780
  const events = [];
@@ -35343,7 +35787,7 @@ function readJobEvents(jobDir) {
35343
35787
  return events;
35344
35788
  }
35345
35789
  function readJobEventsById(jobsDir, jobId) {
35346
- return readJobEvents(join22(jobsDir, jobId));
35790
+ return readJobEvents(join23(jobsDir, jobId));
35347
35791
  }
35348
35792
  function readAllJobEvents(jobsDir) {
35349
35793
  const sqliteClient = createObservabilitySqliteClient();
@@ -35363,12 +35807,12 @@ function readAllJobEvents(jobsDir) {
35363
35807
  }
35364
35808
  if (process.env.SPECIALISTS_JOB_FILE_OUTPUT !== "on")
35365
35809
  return [];
35366
- if (!existsSync21(jobsDir))
35810
+ if (!existsSync22(jobsDir))
35367
35811
  return [];
35368
35812
  const batches = [];
35369
- const entries = readdirSync8(jobsDir);
35813
+ const entries = readdirSync9(jobsDir);
35370
35814
  for (const entry of entries) {
35371
- const jobDir = join22(jobsDir, entry);
35815
+ const jobDir = join23(jobsDir, entry);
35372
35816
  try {
35373
35817
  const stat2 = __require("fs").statSync(jobDir);
35374
35818
  if (!stat2.isDirectory())
@@ -35377,12 +35821,12 @@ function readAllJobEvents(jobsDir) {
35377
35821
  continue;
35378
35822
  }
35379
35823
  const jobId = entry;
35380
- const statusPath = join22(jobDir, "status.json");
35824
+ const statusPath = join23(jobDir, "status.json");
35381
35825
  let specialist = "unknown";
35382
35826
  let beadId;
35383
- if (existsSync21(statusPath)) {
35827
+ if (existsSync22(statusPath)) {
35384
35828
  try {
35385
- const status = JSON.parse(readFileSync19(statusPath, "utf-8"));
35829
+ const status = JSON.parse(readFileSync20(statusPath, "utf-8"));
35386
35830
  specialist = status.specialist ?? "unknown";
35387
35831
  beadId = status.bead_id;
35388
35832
  } catch {}
@@ -35475,17 +35919,17 @@ function formatSpecialistModel(specialist, model) {
35475
35919
  // src/cli/feed.ts
35476
35920
  var exports_feed = {};
35477
35921
  __export(exports_feed, {
35478
- run: () => run17
35922
+ run: () => run18
35479
35923
  });
35480
35924
  import {
35481
35925
  closeSync as closeSync2,
35482
- existsSync as existsSync22,
35926
+ existsSync as existsSync23,
35483
35927
  openSync as openSync3,
35484
- readFileSync as readFileSync20,
35485
- readdirSync as readdirSync9,
35928
+ readFileSync as readFileSync21,
35929
+ readdirSync as readdirSync10,
35486
35930
  statSync as statSync3
35487
35931
  } from "fs";
35488
- import { join as join23 } from "path";
35932
+ import { join as join24 } from "path";
35489
35933
  function getHumanEventKey(event) {
35490
35934
  switch (event.type) {
35491
35935
  case "meta":
@@ -35630,7 +36074,7 @@ function readFileFresh(filePath) {
35630
36074
  let fd = null;
35631
36075
  try {
35632
36076
  fd = openSync3(filePath, "r");
35633
- return readFileSync20(fd, "utf-8");
36077
+ return readFileSync21(fd, "utf-8");
35634
36078
  } catch {
35635
36079
  return null;
35636
36080
  } finally {
@@ -35647,7 +36091,7 @@ function readStatusJson(sqliteClient, jobsDir, jobId) {
35647
36091
  } catch (error2) {
35648
36092
  console.warn(`SQLite status read failed for job ${jobId}; falling back to status.json`, error2);
35649
36093
  }
35650
- const statusPath = join23(jobsDir, jobId, "status.json");
36094
+ const statusPath = join24(jobsDir, jobId, "status.json");
35651
36095
  const raw = readFileFresh(statusPath);
35652
36096
  if (!raw)
35653
36097
  return null;
@@ -35705,7 +36149,7 @@ function makeJobMetaReader(sqliteClient, jobsDir, options = {}) {
35705
36149
  return meta;
35706
36150
  };
35707
36151
  }
35708
- function parseArgs9(argv) {
36152
+ function parseArgs10(argv) {
35709
36153
  let jobId;
35710
36154
  let specialist;
35711
36155
  let nodeId;
@@ -35856,11 +36300,11 @@ function filterMergedEventsByNode(sqliteClient, jobsDir, merged, nodeId) {
35856
36300
  });
35857
36301
  }
35858
36302
  function listMatchingJobIds(sqliteClient, jobsDir, options) {
35859
- if (!existsSync22(jobsDir))
36303
+ if (!existsSync23(jobsDir))
35860
36304
  return [];
35861
36305
  const jobIds = [];
35862
- for (const entry of readdirSync9(jobsDir)) {
35863
- const jobDir = join23(jobsDir, entry);
36306
+ for (const entry of readdirSync10(jobsDir)) {
36307
+ const jobDir = join24(jobsDir, entry);
35864
36308
  try {
35865
36309
  if (!statSync3(jobDir).isDirectory())
35866
36310
  continue;
@@ -35897,7 +36341,7 @@ function readJobEventsFresh(sqliteClient, jobsDir, jobId) {
35897
36341
  } catch (error2) {
35898
36342
  console.warn(`SQLite events read failed for job ${jobId}; falling back to events.jsonl`, error2);
35899
36343
  }
35900
- const eventsPath = join23(jobsDir, jobId, "events.jsonl");
36344
+ const eventsPath = join24(jobsDir, jobId, "events.jsonl");
35901
36345
  const content = readFileFresh(eventsPath);
35902
36346
  if (!content)
35903
36347
  return [];
@@ -35921,7 +36365,7 @@ function readJobEventsIncremental(sqliteClient, jobsDir, jobId, afterSeq, fileCa
35921
36365
  } catch (error2) {
35922
36366
  console.warn(`SQLite incremental events read failed for job ${jobId}; falling back to events.jsonl`, error2);
35923
36367
  }
35924
- const eventsPath = join23(jobsDir, jobId, "events.jsonl");
36368
+ const eventsPath = join24(jobsDir, jobId, "events.jsonl");
35925
36369
  let stats;
35926
36370
  try {
35927
36371
  stats = statSync3(eventsPath);
@@ -35997,7 +36441,7 @@ async function followMerged(sqliteClient, jobsDir, options) {
35997
36441
  }
35998
36442
  const lastPrintedEventKey = new Map;
35999
36443
  const seenMetaKey = new Map;
36000
- await new Promise((resolve9) => {
36444
+ await new Promise((resolve10) => {
36001
36445
  const interval = setInterval(() => {
36002
36446
  const currentJobIds = listMatchingJobIds(sqliteClient, jobsDir, options);
36003
36447
  const statusByJobId = new Map;
@@ -36080,17 +36524,17 @@ async function followMerged(sqliteClient, jobsDir, options) {
36080
36524
  }
36081
36525
  if (!options.forever && trackedJobs.size > 0 && completedJobs.size === trackedJobs.size) {
36082
36526
  clearInterval(interval);
36083
- resolve9();
36527
+ resolve10();
36084
36528
  }
36085
36529
  }, 750);
36086
36530
  });
36087
36531
  }
36088
- async function run17() {
36089
- const options = parseArgs9(process.argv.slice(3));
36532
+ async function run18() {
36533
+ const options = parseArgs10(process.argv.slice(3));
36090
36534
  const sqliteClient = createObservabilitySqliteClient();
36091
36535
  try {
36092
- const jobsDir = join23(process.cwd(), ".specialists", "jobs");
36093
- if (!existsSync22(jobsDir)) {
36536
+ const jobsDir = join24(process.cwd(), ".specialists", "jobs");
36537
+ if (!existsSync23(jobsDir)) {
36094
36538
  console.log(dim8("No jobs directory found."));
36095
36539
  return;
36096
36540
  }
@@ -36127,11 +36571,11 @@ var init_feed = __esm(() => {
36127
36571
  // src/cli/poll.ts
36128
36572
  var exports_poll = {};
36129
36573
  __export(exports_poll, {
36130
- run: () => run18
36574
+ run: () => run19
36131
36575
  });
36132
- import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
36133
- import { join as join24 } from "path";
36134
- function parseArgs10(argv) {
36576
+ import { existsSync as existsSync24, readFileSync as readFileSync22 } from "fs";
36577
+ import { join as join25 } from "path";
36578
+ function parseArgs11(argv) {
36135
36579
  let jobId;
36136
36580
  let cursor = 0;
36137
36581
  let outputCursor = 0;
@@ -36167,19 +36611,19 @@ function parseArgs10(argv) {
36167
36611
  return { jobId, cursor, outputCursor };
36168
36612
  }
36169
36613
  function readJobState(jobsDir, jobId, cursor, outputCursor) {
36170
- const jobDir = join24(jobsDir, jobId);
36171
- const statusPath = join24(jobDir, "status.json");
36614
+ const jobDir = join25(jobsDir, jobId);
36615
+ const statusPath = join25(jobDir, "status.json");
36172
36616
  let status = null;
36173
- if (existsSync23(statusPath)) {
36617
+ if (existsSync24(statusPath)) {
36174
36618
  try {
36175
- status = JSON.parse(readFileSync21(statusPath, "utf-8"));
36619
+ status = JSON.parse(readFileSync22(statusPath, "utf-8"));
36176
36620
  } catch {}
36177
36621
  }
36178
- const resultPath = join24(jobDir, "result.txt");
36622
+ const resultPath = join25(jobDir, "result.txt");
36179
36623
  let fullOutput = "";
36180
- if (existsSync23(resultPath)) {
36624
+ if (existsSync24(resultPath)) {
36181
36625
  try {
36182
- fullOutput = readFileSync21(resultPath, "utf-8");
36626
+ fullOutput = readFileSync22(resultPath, "utf-8");
36183
36627
  } catch {}
36184
36628
  }
36185
36629
  const events = readJobEventsById(jobsDir, jobId);
@@ -36209,11 +36653,13 @@ function readJobState(jobsDir, jobId, cursor, outputCursor) {
36209
36653
  error: status?.error
36210
36654
  };
36211
36655
  }
36212
- async function run18() {
36213
- const { jobId, cursor, outputCursor } = parseArgs10(process.argv.slice(3));
36214
- const jobsDir = join24(process.cwd(), ".specialists", "jobs");
36215
- const jobDir = join24(jobsDir, jobId);
36216
- if (!existsSync23(jobDir)) {
36656
+ async function run19() {
36657
+ process.stderr.write(`[DEPRECATED] 'specialists poll' is scheduled for removal. Use 'sp ps <id> --json' for status and 'sp feed <id>' for events.
36658
+ `);
36659
+ const { jobId, cursor, outputCursor } = parseArgs11(process.argv.slice(3));
36660
+ const jobsDir = join25(process.cwd(), ".specialists", "jobs");
36661
+ const jobDir = join25(jobsDir, jobId);
36662
+ if (!existsSync24(jobDir)) {
36217
36663
  const result2 = {
36218
36664
  job_id: jobId,
36219
36665
  status: "error",
@@ -36242,10 +36688,10 @@ var init_poll = __esm(() => {
36242
36688
  // src/cli/steer.ts
36243
36689
  var exports_steer = {};
36244
36690
  __export(exports_steer, {
36245
- run: () => run19
36691
+ run: () => run20
36246
36692
  });
36247
36693
  import { writeFileSync as writeFileSync9 } from "fs";
36248
- async function run19() {
36694
+ async function run20() {
36249
36695
  const jobId = process.argv[3];
36250
36696
  const message = process.argv[4];
36251
36697
  if (!jobId || !message) {
@@ -36296,10 +36742,10 @@ var init_steer = __esm(() => {
36296
36742
  // src/cli/resume.ts
36297
36743
  var exports_resume = {};
36298
36744
  __export(exports_resume, {
36299
- run: () => run20
36745
+ run: () => run21
36300
36746
  });
36301
36747
  import { writeFileSync as writeFileSync10 } from "fs";
36302
- async function run20() {
36748
+ async function run21() {
36303
36749
  const jobId = process.argv[3];
36304
36750
  const task = process.argv[4];
36305
36751
  if (!jobId || !task) {
@@ -36352,24 +36798,24 @@ var init_resume = __esm(() => {
36352
36798
  // src/cli/follow-up.ts
36353
36799
  var exports_follow_up = {};
36354
36800
  __export(exports_follow_up, {
36355
- run: () => run21
36801
+ run: () => run22
36356
36802
  });
36357
- async function run21() {
36803
+ async function run22() {
36358
36804
  process.stderr.write("\x1B[33m\u26A0 DEPRECATED:\x1B[0m `specialists follow-up` is deprecated. Use `specialists resume` instead.\n\n");
36359
36805
  const { run: resumeRun } = await Promise.resolve().then(() => (init_resume(), exports_resume));
36360
36806
  return resumeRun();
36361
36807
  }
36362
36808
 
36363
36809
  // src/specialist/worktree-gc.ts
36364
- import { existsSync as existsSync24, readdirSync as readdirSync10, readFileSync as readFileSync22 } from "fs";
36365
- import { join as join25 } from "path";
36810
+ import { existsSync as existsSync25, readdirSync as readdirSync11, readFileSync as readFileSync23 } from "fs";
36811
+ import { join as join26 } from "path";
36366
36812
  import { spawnSync as spawnSync17 } from "child_process";
36367
36813
  function readJobStatus2(jobDir) {
36368
- const statusPath = join25(jobDir, "status.json");
36369
- if (!existsSync24(statusPath))
36814
+ const statusPath = join26(jobDir, "status.json");
36815
+ if (!existsSync25(statusPath))
36370
36816
  return null;
36371
36817
  try {
36372
- return JSON.parse(readFileSync22(statusPath, "utf-8"));
36818
+ return JSON.parse(readFileSync23(statusPath, "utf-8"));
36373
36819
  } catch {
36374
36820
  return null;
36375
36821
  }
@@ -36391,7 +36837,7 @@ function collectWorktreeGcCandidates(jobsDir) {
36391
36837
  const worktreePath = status.worktree_path;
36392
36838
  if (!worktreePath)
36393
36839
  return null;
36394
- if (!existsSync24(worktreePath))
36840
+ if (!existsSync25(worktreePath))
36395
36841
  return null;
36396
36842
  return {
36397
36843
  jobId: status.id,
@@ -36403,13 +36849,13 @@ function collectWorktreeGcCandidates(jobsDir) {
36403
36849
  }
36404
36850
  if (!getFileFallbackEnabled())
36405
36851
  return [];
36406
- if (!existsSync24(jobsDir))
36852
+ if (!existsSync25(jobsDir))
36407
36853
  return [];
36408
36854
  const candidates = [];
36409
- for (const entry of readdirSync10(jobsDir, { withFileTypes: true })) {
36855
+ for (const entry of readdirSync11(jobsDir, { withFileTypes: true })) {
36410
36856
  if (!entry.isDirectory())
36411
36857
  continue;
36412
- const status = readJobStatus2(join25(jobsDir, entry.name));
36858
+ const status = readJobStatus2(join26(jobsDir, entry.name));
36413
36859
  if (!status)
36414
36860
  continue;
36415
36861
  if (isActive(status.status))
@@ -36419,7 +36865,7 @@ function collectWorktreeGcCandidates(jobsDir) {
36419
36865
  const { worktree_path: worktreePath, branch } = status;
36420
36866
  if (!worktreePath)
36421
36867
  continue;
36422
- if (!existsSync24(worktreePath))
36868
+ if (!existsSync25(worktreePath))
36423
36869
  continue;
36424
36870
  candidates.push({
36425
36871
  jobId: status.id,
@@ -36463,16 +36909,16 @@ var init_worktree_gc = __esm(() => {
36463
36909
  // src/cli/clean.ts
36464
36910
  var exports_clean = {};
36465
36911
  __export(exports_clean, {
36466
- run: () => run22
36912
+ run: () => run23
36467
36913
  });
36468
36914
  import {
36469
- existsSync as existsSync25,
36470
- readdirSync as readdirSync11,
36471
- readFileSync as readFileSync23,
36915
+ existsSync as existsSync26,
36916
+ readdirSync as readdirSync12,
36917
+ readFileSync as readFileSync24,
36472
36918
  rmSync as rmSync3,
36473
36919
  statSync as statSync4
36474
36920
  } from "fs";
36475
- import { join as join26 } from "path";
36921
+ import { join as join27 } from "path";
36476
36922
  function parseTtlDaysFromEnvironment() {
36477
36923
  const rawValue = process.env.SPECIALISTS_JOB_TTL_DAYS ?? process.env.JOB_TTL_DAYS;
36478
36924
  if (!rawValue)
@@ -36526,9 +36972,9 @@ function parseOptions2(argv) {
36526
36972
  }
36527
36973
  function readDirectorySizeBytes(directoryPath) {
36528
36974
  let totalBytes = 0;
36529
- const entries = readdirSync11(directoryPath, { withFileTypes: true });
36975
+ const entries = readdirSync12(directoryPath, { withFileTypes: true });
36530
36976
  for (const entry of entries) {
36531
- const entryPath = join26(directoryPath, entry.name);
36977
+ const entryPath = join27(directoryPath, entry.name);
36532
36978
  const stats = statSync4(entryPath);
36533
36979
  if (stats.isDirectory()) {
36534
36980
  totalBytes += readDirectorySizeBytes(entryPath);
@@ -36539,9 +36985,9 @@ function readDirectorySizeBytes(directoryPath) {
36539
36985
  return totalBytes;
36540
36986
  }
36541
36987
  function containsProtectedSqliteArtifact(directoryPath) {
36542
- const entries = readdirSync11(directoryPath, { withFileTypes: true });
36988
+ const entries = readdirSync12(directoryPath, { withFileTypes: true });
36543
36989
  for (const entry of entries) {
36544
- const entryPath = join26(directoryPath, entry.name);
36990
+ const entryPath = join27(directoryPath, entry.name);
36545
36991
  if (entry.isDirectory()) {
36546
36992
  if (containsProtectedSqliteArtifact(entryPath))
36547
36993
  return true;
@@ -36562,15 +37008,15 @@ function getJobTimestamps(status) {
36562
37008
  function readCompletedJobDirectory(baseDirectory, entry) {
36563
37009
  if (!entry.isDirectory())
36564
37010
  return null;
36565
- const directoryPath = join26(baseDirectory, entry.name);
37011
+ const directoryPath = join27(baseDirectory, entry.name);
36566
37012
  if (containsProtectedSqliteArtifact(directoryPath))
36567
37013
  return null;
36568
- const statusFilePath = join26(directoryPath, "status.json");
36569
- if (!existsSync25(statusFilePath))
37014
+ const statusFilePath = join27(directoryPath, "status.json");
37015
+ if (!existsSync26(statusFilePath))
36570
37016
  return null;
36571
37017
  let statusData;
36572
37018
  try {
36573
- statusData = JSON.parse(readFileSync23(statusFilePath, "utf-8"));
37019
+ statusData = JSON.parse(readFileSync24(statusFilePath, "utf-8"));
36574
37020
  } catch {
36575
37021
  return null;
36576
37022
  }
@@ -36589,10 +37035,10 @@ function collectCompletedJobDirectoriesFromDb(jobsDirectoryPath) {
36589
37035
  const sqliteClient = createObservabilitySqliteClient();
36590
37036
  const statuses = sqliteClient?.listStatuses() ?? [];
36591
37037
  const completedJobs = statuses.filter((status) => COMPLETED_STATUSES.has(status.status)).map((status) => {
36592
- const directoryPath = join26(jobsDirectoryPath, status.id);
37038
+ const directoryPath = join27(jobsDirectoryPath, status.id);
36593
37039
  if (containsProtectedSqliteArtifact(directoryPath))
36594
37040
  return null;
36595
- if (!existsSync25(directoryPath))
37041
+ if (!existsSync26(directoryPath))
36596
37042
  return null;
36597
37043
  const { createdAtMs, completedAtMs } = getJobTimestamps(status);
36598
37044
  return {
@@ -36613,7 +37059,7 @@ function collectCompletedJobDirectories(jobsDirectoryPath) {
36613
37059
  }
36614
37060
  if (process.env.SPECIALISTS_JOB_FILE_OUTPUT !== "on")
36615
37061
  return [];
36616
- const entries = readdirSync11(jobsDirectoryPath, { withFileTypes: true });
37062
+ const entries = readdirSync12(jobsDirectoryPath, { withFileTypes: true });
36617
37063
  const completedJobs = [];
36618
37064
  for (const entry of entries) {
36619
37065
  const completedJob = readCompletedJobDirectory(jobsDirectoryPath, entry);
@@ -36684,7 +37130,7 @@ function printUsageAndExit2(message) {
36684
37130
  console.error("Usage: specialists|sp clean [--all] [--keep <n>] [--dry-run]");
36685
37131
  process.exit(1);
36686
37132
  }
36687
- async function run22() {
37133
+ async function run23() {
36688
37134
  let options;
36689
37135
  try {
36690
37136
  options = parseOptions2(process.argv.slice(3));
@@ -36693,7 +37139,7 @@ async function run22() {
36693
37139
  printUsageAndExit2(message);
36694
37140
  }
36695
37141
  const jobsDirectoryPath = resolveJobsDir();
36696
- if (!existsSync25(jobsDirectoryPath)) {
37142
+ if (!existsSync26(jobsDirectoryPath)) {
36697
37143
  console.log("No jobs directory found.");
36698
37144
  return;
36699
37145
  }
@@ -36728,7 +37174,7 @@ var init_clean = __esm(() => {
36728
37174
  // src/cli/end.ts
36729
37175
  var exports_end = {};
36730
37176
  __export(exports_end, {
36731
- run: () => run23
37177
+ run: () => run24
36732
37178
  });
36733
37179
  import { spawnSync as spawnSync18 } from "child_process";
36734
37180
  function parseOptions3(argv) {
@@ -36812,7 +37258,7 @@ async function publishChain(beadId, options) {
36812
37258
  console.log("Publication mode: direct merge");
36813
37259
  }
36814
37260
  }
36815
- async function run23() {
37261
+ async function run24() {
36816
37262
  let options;
36817
37263
  try {
36818
37264
  options = parseOptions3(process.argv.slice(3));
@@ -36853,7 +37299,7 @@ var init_end = __esm(() => {
36853
37299
  // src/cli/stop.ts
36854
37300
  var exports_stop = {};
36855
37301
  __export(exports_stop, {
36856
- run: () => run24
37302
+ run: () => run25
36857
37303
  });
36858
37304
  function resolveTerminalStatus(jobId) {
36859
37305
  return hasRunCompleteEvent(jobId) ? "done" : "cancelled";
@@ -36879,7 +37325,7 @@ async function waitForProcessExit(pid, timeoutMs) {
36879
37325
  while (Date.now() < deadline) {
36880
37326
  if (!isProcessAlive(pid))
36881
37327
  return true;
36882
- await new Promise((resolve9) => setTimeout(resolve9, 100));
37328
+ await new Promise((resolve10) => setTimeout(resolve10, 100));
36883
37329
  }
36884
37330
  return !isProcessAlive(pid);
36885
37331
  }
@@ -36891,7 +37337,7 @@ function tryKillProcessGroup(pid) {
36891
37337
  throw err;
36892
37338
  }
36893
37339
  }
36894
- async function run24() {
37340
+ async function run25() {
36895
37341
  let parsed;
36896
37342
  try {
36897
37343
  parsed = parseStopArgs(process.argv.slice(3));
@@ -36970,6 +37416,14 @@ async function run24() {
36970
37416
  process.stdout.write(`${dim11(` tmux session ${tmuxSession} killed`)}
36971
37417
  `);
36972
37418
  }
37419
+ if (status.bead_id) {
37420
+ const finalStatus = supervisor.readStatus(jobId)?.status ?? "cancelled";
37421
+ const beads = new BeadsClient;
37422
+ if (beads.closeBeadIfInProgress(status.bead_id, `Job ${jobId} stopped (${finalStatus})`)) {
37423
+ process.stdout.write(`${dim11(` bead ${status.bead_id} auto-closed`)}
37424
+ `);
37425
+ }
37426
+ }
36973
37427
  } finally {
36974
37428
  await supervisor.dispose();
36975
37429
  }
@@ -36980,24 +37434,25 @@ var init_stop = __esm(() => {
36980
37434
  init_job_root();
36981
37435
  init_observability_sqlite();
36982
37436
  init_process_liveness();
37437
+ init_beads();
36983
37438
  init_tmux_utils();
36984
37439
  });
36985
37440
 
36986
37441
  // src/cli/attach.ts
36987
37442
  var exports_attach = {};
36988
37443
  __export(exports_attach, {
36989
- run: () => run25
37444
+ run: () => run26
36990
37445
  });
36991
37446
  import { execFileSync as execFileSync3, spawnSync as spawnSync19 } from "child_process";
36992
- import { readFileSync as readFileSync24 } from "fs";
36993
- import { join as join27 } from "path";
37447
+ import { readFileSync as readFileSync25 } from "fs";
37448
+ import { join as join28 } from "path";
36994
37449
  function exitWithError(message) {
36995
37450
  console.error(message);
36996
37451
  process.exit(1);
36997
37452
  }
36998
37453
  function readStatus(statusPath, jobId) {
36999
37454
  try {
37000
- return JSON.parse(readFileSync24(statusPath, "utf-8"));
37455
+ return JSON.parse(readFileSync25(statusPath, "utf-8"));
37001
37456
  } catch (error2) {
37002
37457
  if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
37003
37458
  exitWithError(`Job \`${jobId}\` not found. Run \`specialists status\` to see active jobs in current mode.`);
@@ -37006,13 +37461,13 @@ function readStatus(statusPath, jobId) {
37006
37461
  exitWithError(`Failed to read status for job \`${jobId}\`: ${details}`);
37007
37462
  }
37008
37463
  }
37009
- async function run25() {
37464
+ async function run26() {
37010
37465
  const [jobId] = process.argv.slice(3);
37011
37466
  if (!jobId) {
37012
37467
  exitWithError("Usage: specialists attach <job-id> (normal runtime is DB-backed; job files are legacy/operator-only)");
37013
37468
  }
37014
- const jobsDir = join27(process.cwd(), ".specialists", "jobs");
37015
- const statusPath = join27(jobsDir, jobId, "status.json");
37469
+ const jobsDir = join28(process.cwd(), ".specialists", "jobs");
37470
+ const statusPath = join28(jobsDir, jobId, "status.json");
37016
37471
  const status = readStatus(statusPath, jobId);
37017
37472
  if (status.status === "done" || status.status === "error") {
37018
37473
  exitWithError(`Job \`${jobId}\` has already completed (status: ${status.status}). Use \`specialists result ${jobId}\` to read output.`);
@@ -37036,7 +37491,7 @@ var init_attach = () => {};
37036
37491
  // src/cli/quickstart.ts
37037
37492
  var exports_quickstart = {};
37038
37493
  __export(exports_quickstart, {
37039
- run: () => run26
37494
+ run: () => run27
37040
37495
  });
37041
37496
  function section2(title) {
37042
37497
  const bar = "\u2500".repeat(60);
@@ -37050,7 +37505,7 @@ function cmd2(s) {
37050
37505
  function flag(s) {
37051
37506
  return green13(s);
37052
37507
  }
37053
- async function run26() {
37508
+ async function run27() {
37054
37509
  const lines = [
37055
37510
  "",
37056
37511
  bold12("specialists \xB7 Quick Start Guide"),
@@ -37265,7 +37720,7 @@ var bold12 = (s) => `\x1B[1m${s}\x1B[0m`, dim12 = (s) => `\x1B[2m${s}\x1B[0m`, y
37265
37720
  var exports_doctor = {};
37266
37721
  __export(exports_doctor, {
37267
37722
  setStatusError: () => setStatusError,
37268
- run: () => run27,
37723
+ run: () => run28,
37269
37724
  renderProcessSummary: () => renderProcessSummary,
37270
37725
  parseVersionTuple: () => parseVersionTuple,
37271
37726
  compareVersions: () => compareVersions,
@@ -37273,8 +37728,8 @@ __export(exports_doctor, {
37273
37728
  });
37274
37729
  import { createHash as createHash4 } from "crypto";
37275
37730
  import { spawnSync as spawnSync20 } from "child_process";
37276
- import { existsSync as existsSync26, lstatSync as lstatSync2, mkdirSync as mkdirSync9, readdirSync as readdirSync12, readFileSync as readFileSync25, readlinkSync as readlinkSync2, writeFileSync as writeFileSync11 } from "fs";
37277
- import { dirname as dirname7, join as join28, relative as relative2, resolve as resolve9 } from "path";
37731
+ import { existsSync as existsSync27, lstatSync as lstatSync2, mkdirSync as mkdirSync9, readdirSync as readdirSync13, readFileSync as readFileSync26, readlinkSync as readlinkSync2, writeFileSync as writeFileSync11 } from "fs";
37732
+ import { dirname as dirname7, join as join29, relative as relative2, resolve as resolve10 } from "path";
37278
37733
  function ok3(msg) {
37279
37734
  console.log(` ${green14("\u2713")} ${msg}`);
37280
37735
  }
@@ -37303,10 +37758,10 @@ function isInstalled3(bin) {
37303
37758
  return spawnSync20("which", [bin], { encoding: "utf8", timeout: 2000 }).status === 0;
37304
37759
  }
37305
37760
  function loadJson2(path) {
37306
- if (!existsSync26(path))
37761
+ if (!existsSync27(path))
37307
37762
  return null;
37308
37763
  try {
37309
- return JSON.parse(readFileSync25(path, "utf8"));
37764
+ return JSON.parse(readFileSync26(path, "utf8"));
37310
37765
  } catch {
37311
37766
  return null;
37312
37767
  }
@@ -37349,7 +37804,7 @@ function checkBd() {
37349
37804
  return false;
37350
37805
  }
37351
37806
  ok3(`bd installed ${dim13(sp("bd", ["--version"]).stdout || "")}`);
37352
- if (existsSync26(join28(CWD, ".beads")))
37807
+ if (existsSync27(join29(CWD, ".beads")))
37353
37808
  ok3(".beads/ present in project");
37354
37809
  else
37355
37810
  warn3(".beads/ not found in project");
@@ -37369,15 +37824,15 @@ function checkHooks() {
37369
37824
  section3("Claude Code hooks (2 expected)");
37370
37825
  let allPresent = true;
37371
37826
  for (const name of HOOK_NAMES) {
37372
- const canonicalPath = join28(HOOKS_DIR, name);
37373
- if (!existsSync26(canonicalPath)) {
37827
+ const canonicalPath = join29(HOOKS_DIR, name);
37828
+ if (!existsSync27(canonicalPath)) {
37374
37829
  fail4(`${relative2(CWD, canonicalPath)} ${red7("missing")}`);
37375
37830
  fix("specialists init");
37376
37831
  allPresent = false;
37377
37832
  } else {
37378
37833
  ok3(relative2(CWD, canonicalPath));
37379
37834
  }
37380
- const claudeHookPath = join28(CLAUDE_HOOKS_DIR, name);
37835
+ const claudeHookPath = join29(CLAUDE_HOOKS_DIR, name);
37381
37836
  const symlinkState = isSymlinkTo(claudeHookPath, canonicalPath);
37382
37837
  if (symlinkState.ok) {
37383
37838
  ok3(`${relative2(CWD, claudeHookPath)} -> ${relative2(dirname7(claudeHookPath), canonicalPath)}`);
@@ -37432,14 +37887,14 @@ function checkMCP() {
37432
37887
  }
37433
37888
  function hashFile(path) {
37434
37889
  const hash = createHash4("sha256");
37435
- hash.update(readFileSync25(path));
37890
+ hash.update(readFileSync26(path));
37436
37891
  return hash.digest("hex");
37437
37892
  }
37438
37893
  function collectFileHashes(rootDir) {
37439
37894
  const hashes = new Map;
37440
37895
  const visit2 = (dir) => {
37441
- for (const entry of readdirSync12(dir, { withFileTypes: true })) {
37442
- const fullPath = join28(dir, entry.name);
37896
+ for (const entry of readdirSync13(dir, { withFileTypes: true })) {
37897
+ const fullPath = join29(dir, entry.name);
37443
37898
  if (entry.isDirectory()) {
37444
37899
  visit2(fullPath);
37445
37900
  continue;
@@ -37450,12 +37905,12 @@ function collectFileHashes(rootDir) {
37450
37905
  hashes.set(relPath, hashFile(fullPath));
37451
37906
  }
37452
37907
  };
37453
- if (existsSync26(rootDir))
37908
+ if (existsSync27(rootDir))
37454
37909
  visit2(rootDir);
37455
37910
  return hashes;
37456
37911
  }
37457
37912
  function isSymlinkTo(linkPath, expectedTargetPath) {
37458
- if (!existsSync26(linkPath))
37913
+ if (!existsSync27(linkPath))
37459
37914
  return { ok: false, reason: "missing" };
37460
37915
  let stats;
37461
37916
  try {
@@ -37467,8 +37922,8 @@ function isSymlinkTo(linkPath, expectedTargetPath) {
37467
37922
  return { ok: false, reason: "not-symlink" };
37468
37923
  try {
37469
37924
  const rawTarget = readlinkSync2(linkPath);
37470
- const resolvedTarget = resolve9(dirname7(linkPath), rawTarget);
37471
- const resolvedExpected = resolve9(expectedTargetPath);
37925
+ const resolvedTarget = resolve10(dirname7(linkPath), rawTarget);
37926
+ const resolvedExpected = resolve10(expectedTargetPath);
37472
37927
  if (resolvedTarget !== resolvedExpected) {
37473
37928
  return { ok: false, reason: "wrong-target", target: rawTarget };
37474
37929
  }
@@ -37479,12 +37934,12 @@ function isSymlinkTo(linkPath, expectedTargetPath) {
37479
37934
  }
37480
37935
  function checkSkillDrift() {
37481
37936
  section3("Skill drift (.xtrm skill sync)");
37482
- if (!existsSync26(CONFIG_SKILLS_DIR)) {
37937
+ if (!existsSync27(CONFIG_SKILLS_DIR)) {
37483
37938
  fail4("config/skills/ missing");
37484
37939
  fix("restore config/skills/ from git");
37485
37940
  return false;
37486
37941
  }
37487
- if (!existsSync26(XTRM_DEFAULT_SKILLS_DIR)) {
37942
+ if (!existsSync27(XTRM_DEFAULT_SKILLS_DIR)) {
37488
37943
  fail4(".xtrm/skills/default/ missing");
37489
37944
  fix("specialists init --sync-skills");
37490
37945
  return false;
@@ -37526,17 +37981,17 @@ function checkSkillDrift() {
37526
37981
  }
37527
37982
  let linksOk = true;
37528
37983
  for (const scope of ["claude", "pi"]) {
37529
- const activeRoot = join28(XTRM_ACTIVE_SKILLS_DIR, scope);
37530
- if (!existsSync26(activeRoot)) {
37984
+ const activeRoot = join29(XTRM_ACTIVE_SKILLS_DIR, scope);
37985
+ if (!existsSync27(activeRoot)) {
37531
37986
  fail4(`${relative2(CWD, activeRoot)}/ missing`);
37532
37987
  fix("specialists init --sync-skills");
37533
37988
  linksOk = false;
37534
37989
  continue;
37535
37990
  }
37536
- const defaultSkills = readdirSync12(XTRM_DEFAULT_SKILLS_DIR, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
37991
+ const defaultSkills = readdirSync13(XTRM_DEFAULT_SKILLS_DIR, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
37537
37992
  for (const skillName of defaultSkills) {
37538
- const activeLinkPath = join28(activeRoot, skillName);
37539
- const expectedTarget = join28(XTRM_DEFAULT_SKILLS_DIR, skillName);
37993
+ const activeLinkPath = join29(activeRoot, skillName);
37994
+ const expectedTarget = join29(XTRM_DEFAULT_SKILLS_DIR, skillName);
37540
37995
  const state = isSymlinkTo(activeLinkPath, expectedTarget);
37541
37996
  if (state.ok)
37542
37997
  continue;
@@ -37555,8 +38010,8 @@ function checkSkillDrift() {
37555
38010
  }
37556
38011
  }
37557
38012
  const skillRootChecks = [
37558
- { root: join28(CLAUDE_DIR, "skills"), expected: ACTIVE_CLAUDE_SKILLS_DIR },
37559
- { root: join28(PI_DIR, "skills"), expected: ACTIVE_PI_SKILLS_DIR }
38013
+ { root: join29(CLAUDE_DIR, "skills"), expected: ACTIVE_CLAUDE_SKILLS_DIR },
38014
+ { root: join29(PI_DIR, "skills"), expected: ACTIVE_PI_SKILLS_DIR }
37560
38015
  ];
37561
38016
  let rootLinksOk = true;
37562
38017
  for (const check2 of skillRootChecks) {
@@ -37581,12 +38036,12 @@ function checkSkillDrift() {
37581
38036
  return drifted.length === 0 && missingInDefault.length === 0 && linksOk && rootLinksOk;
37582
38037
  }
37583
38038
  function checkManagedMirror(label, sourceDir, mirrorDir, fixHint) {
37584
- if (!existsSync26(sourceDir)) {
38039
+ if (!existsSync27(sourceDir)) {
37585
38040
  warn3(`${label} source missing: ${relative2(CWD, sourceDir)}`);
37586
38041
  fix(fixHint);
37587
38042
  return false;
37588
38043
  }
37589
- if (!existsSync26(mirrorDir)) {
38044
+ if (!existsSync27(mirrorDir)) {
37590
38045
  fail4(`${label} mirror missing: ${relative2(CWD, mirrorDir)}`);
37591
38046
  fix(fixHint);
37592
38047
  return false;
@@ -37618,24 +38073,24 @@ function checkManagedMirror(label, sourceDir, mirrorDir, fixHint) {
37618
38073
  function checkManagedAssetMirrors() {
37619
38074
  section3("Managed mirrors (specialists / mandatory-rules / nodes)");
37620
38075
  const specialistsOk = checkManagedMirror("specialists", CONFIG_SPECIALISTS_DIR, DEFAULT_SPECIALISTS_DIR, "specialists init --sync-defaults");
37621
- const rulesOk = checkManagedMirror("mandatory-rules", CONFIG_MANDATORY_RULES_DIR, join28(DEFAULT_SPECIALISTS_DIR, "mandatory-rules"), "specialists init --sync-defaults");
37622
- const nodesOk = checkManagedMirror("nodes", CONFIG_NODES_DIR, join28(DEFAULT_SPECIALISTS_DIR, "nodes"), "specialists init --sync-defaults");
38076
+ const rulesOk = checkManagedMirror("mandatory-rules", CONFIG_MANDATORY_RULES_DIR, join29(DEFAULT_SPECIALISTS_DIR, "mandatory-rules"), "specialists init --sync-defaults");
38077
+ const nodesOk = checkManagedMirror("nodes", CONFIG_NODES_DIR, join29(DEFAULT_SPECIALISTS_DIR, "nodes"), "specialists init --sync-defaults");
37623
38078
  return specialistsOk && rulesOk && nodesOk;
37624
38079
  }
37625
38080
  function checkRuntimeDirs() {
37626
38081
  section3(".specialists/ runtime directories");
37627
- const rootDir = join28(CWD, ".specialists");
37628
- const jobsDir = join28(rootDir, "jobs");
37629
- const readyDir = join28(rootDir, "ready");
38082
+ const rootDir = join29(CWD, ".specialists");
38083
+ const jobsDir = join29(rootDir, "jobs");
38084
+ const readyDir = join29(rootDir, "ready");
37630
38085
  let allOk = true;
37631
- if (!existsSync26(rootDir)) {
38086
+ if (!existsSync27(rootDir)) {
37632
38087
  warn3(".specialists/ not found in current project");
37633
38088
  fix("specialists init");
37634
38089
  allOk = false;
37635
38090
  } else {
37636
38091
  ok3(".specialists/ present");
37637
38092
  for (const [subDir, label] of [[jobsDir, "jobs"], [readyDir, "ready"]]) {
37638
- if (!existsSync26(subDir)) {
38093
+ if (!existsSync27(subDir)) {
37639
38094
  warn3(`.specialists/${label}/ missing \u2014 auto-creating`);
37640
38095
  mkdirSync9(subDir, { recursive: true });
37641
38096
  ok3(`.specialists/${label}/ created`);
@@ -37668,7 +38123,7 @@ function compareVersions(left, right) {
37668
38123
  }
37669
38124
  function setStatusError(statusPath) {
37670
38125
  try {
37671
- const raw = readFileSync25(statusPath, "utf8");
38126
+ const raw = readFileSync26(statusPath, "utf8");
37672
38127
  const status = JSON.parse(raw);
37673
38128
  status.status = "error";
37674
38129
  writeFileSync11(statusPath, `${JSON.stringify(status, null, 2)}
@@ -37710,7 +38165,7 @@ function cleanupProcesses(jobsDir, dryRun) {
37710
38165
  }
37711
38166
  let entries;
37712
38167
  try {
37713
- entries = readdirSync12(jobsDir);
38168
+ entries = readdirSync13(jobsDir);
37714
38169
  } catch {
37715
38170
  entries = [];
37716
38171
  }
@@ -37722,11 +38177,11 @@ function cleanupProcesses(jobsDir, dryRun) {
37722
38177
  zombieJobIds: []
37723
38178
  };
37724
38179
  for (const jobId of entries) {
37725
- const statusPath = join28(jobsDir, jobId, "status.json");
37726
- if (!existsSync26(statusPath))
38180
+ const statusPath = join29(jobsDir, jobId, "status.json");
38181
+ if (!existsSync27(statusPath))
37727
38182
  continue;
37728
38183
  try {
37729
- const status = JSON.parse(readFileSync25(statusPath, "utf8"));
38184
+ const status = JSON.parse(readFileSync26(statusPath, "utf8"));
37730
38185
  result.total += 1;
37731
38186
  if (status.status !== "running" && status.status !== "starting")
37732
38187
  continue;
@@ -37811,8 +38266,8 @@ function resolveWatchdogMode() {
37811
38266
  function checkZombieJobs() {
37812
38267
  section3("Background jobs");
37813
38268
  hint(`watchdog mode: ${resolveWatchdogMode()}`);
37814
- const jobsDir = join28(CWD, ".specialists", "jobs");
37815
- if (!existsSync26(jobsDir)) {
38269
+ const jobsDir = join29(CWD, ".specialists", "jobs");
38270
+ if (!existsSync27(jobsDir)) {
37816
38271
  hint("No .specialists/jobs/ \u2014 skipping");
37817
38272
  return true;
37818
38273
  }
@@ -37830,7 +38285,7 @@ function checkZombieJobs() {
37830
38285
  }
37831
38286
  return result.zombies === 0;
37832
38287
  }
37833
- async function run27(argv = process.argv.slice(3)) {
38288
+ async function run28(argv = process.argv.slice(3)) {
37834
38289
  const subcommand = argv[0];
37835
38290
  if (subcommand === "orphans") {
37836
38291
  runDoctorOrphans();
@@ -37867,23 +38322,23 @@ var bold13 = (s) => `\x1B[1m${s}\x1B[0m`, dim13 = (s) => `\x1B[2m${s}\x1B[0m`, g
37867
38322
  var init_doctor = __esm(() => {
37868
38323
  init_observability_sqlite();
37869
38324
  CWD = process.cwd();
37870
- CLAUDE_DIR = join28(CWD, ".claude");
37871
- PI_DIR = join28(CWD, ".pi");
37872
- XTRM_SKILLS_DIR = join28(CWD, ".xtrm", "skills");
37873
- XTRM_DEFAULT_SKILLS_DIR = join28(XTRM_SKILLS_DIR, "default");
37874
- XTRM_ACTIVE_SKILLS_DIR = join28(XTRM_SKILLS_DIR, "active");
37875
- ACTIVE_CLAUDE_SKILLS_DIR = join28(XTRM_ACTIVE_SKILLS_DIR, "claude");
37876
- ACTIVE_PI_SKILLS_DIR = join28(XTRM_ACTIVE_SKILLS_DIR, "pi");
37877
- CONFIG_SKILLS_DIR = join28(CWD, "config", "skills");
37878
- CONFIG_SPECIALISTS_DIR = join28(CWD, "config", "specialists");
37879
- CONFIG_MANDATORY_RULES_DIR = join28(CWD, "config", "mandatory-rules");
37880
- CONFIG_NODES_DIR = join28(CWD, "config", "nodes");
37881
- SPECIALISTS_DIR = join28(CWD, ".specialists");
37882
- DEFAULT_SPECIALISTS_DIR = join28(SPECIALISTS_DIR, "default");
37883
- HOOKS_DIR = join28(CWD, ".xtrm", "hooks", "specialists");
37884
- CLAUDE_HOOKS_DIR = join28(CLAUDE_DIR, "hooks");
37885
- SETTINGS_FILE = join28(CLAUDE_DIR, "settings.json");
37886
- MCP_FILE2 = join28(CWD, ".mcp.json");
38325
+ CLAUDE_DIR = join29(CWD, ".claude");
38326
+ PI_DIR = join29(CWD, ".pi");
38327
+ XTRM_SKILLS_DIR = join29(CWD, ".xtrm", "skills");
38328
+ XTRM_DEFAULT_SKILLS_DIR = join29(XTRM_SKILLS_DIR, "default");
38329
+ XTRM_ACTIVE_SKILLS_DIR = join29(XTRM_SKILLS_DIR, "active");
38330
+ ACTIVE_CLAUDE_SKILLS_DIR = join29(XTRM_ACTIVE_SKILLS_DIR, "claude");
38331
+ ACTIVE_PI_SKILLS_DIR = join29(XTRM_ACTIVE_SKILLS_DIR, "pi");
38332
+ CONFIG_SKILLS_DIR = join29(CWD, "config", "skills");
38333
+ CONFIG_SPECIALISTS_DIR = join29(CWD, "config", "specialists");
38334
+ CONFIG_MANDATORY_RULES_DIR = join29(CWD, "config", "mandatory-rules");
38335
+ CONFIG_NODES_DIR = join29(CWD, "config", "nodes");
38336
+ SPECIALISTS_DIR = join29(CWD, ".specialists");
38337
+ DEFAULT_SPECIALISTS_DIR = join29(SPECIALISTS_DIR, "default");
38338
+ HOOKS_DIR = join29(CWD, ".xtrm", "hooks", "specialists");
38339
+ CLAUDE_HOOKS_DIR = join29(CLAUDE_DIR, "hooks");
38340
+ SETTINGS_FILE = join29(CLAUDE_DIR, "settings.json");
38341
+ MCP_FILE2 = join29(CWD, ".mcp.json");
37887
38342
  HOOK_NAMES = [
37888
38343
  "specialists-complete.mjs",
37889
38344
  "specialists-session-start.mjs"
@@ -37893,9 +38348,9 @@ var init_doctor = __esm(() => {
37893
38348
  // src/cli/setup.ts
37894
38349
  var exports_setup = {};
37895
38350
  __export(exports_setup, {
37896
- run: () => run28
38351
+ run: () => run29
37897
38352
  });
37898
- async function run28() {
38353
+ async function run29() {
37899
38354
  console.log("");
37900
38355
  console.log(yellow13("\u26A0 DEPRECATED: `specialists setup` is deprecated"));
37901
38356
  console.log("");
@@ -37919,11 +38374,11 @@ var bold14 = (s) => `\x1B[1m${s}\x1B[0m`, yellow13 = (s) => `\x1B[33m${s}\x1B[0m
37919
38374
  var exports_serve = {};
37920
38375
  __export(exports_serve, {
37921
38376
  startServe: () => startServe,
37922
- run: () => run29
38377
+ run: () => run30
37923
38378
  });
37924
38379
  import { createServer } from "http";
37925
38380
  import { once } from "events";
37926
- function parseArgs11(argv) {
38381
+ function parseArgs12(argv) {
37927
38382
  let port = 8000;
37928
38383
  let concurrency = 4;
37929
38384
  let queueTimeoutMs = 5000;
@@ -37965,12 +38420,12 @@ async function waitForSlot(limit, timeoutMs, getActive) {
37965
38420
  while (getActive() >= limit) {
37966
38421
  if (Date.now() - startedAt >= timeoutMs)
37967
38422
  return false;
37968
- await new Promise((resolve10) => setTimeout(resolve10, 25));
38423
+ await new Promise((resolve11) => setTimeout(resolve11, 25));
37969
38424
  }
37970
38425
  return true;
37971
38426
  }
37972
38427
  async function startServe(argv = process.argv.slice(3)) {
37973
- const args = parseArgs11(argv);
38428
+ const args = parseArgs12(argv);
37974
38429
  const loader = new SpecialistLoader({ projectDir: args.projectDir });
37975
38430
  const dbLocation = resolveObservabilityDbLocation(args.projectDir);
37976
38431
  ensureObservabilityDbFile(dbLocation);
@@ -38031,7 +38486,7 @@ async function startServe(argv = process.argv.slice(3)) {
38031
38486
  console.log(`sp serve listening on ${args.port}`);
38032
38487
  return { server, args, db };
38033
38488
  }
38034
- async function run29(argv = process.argv.slice(3)) {
38489
+ async function run30(argv = process.argv.slice(3)) {
38035
38490
  await startServe(argv);
38036
38491
  }
38037
38492
  var init_serve = __esm(() => {
@@ -38045,8 +38500,8 @@ var init_serve = __esm(() => {
38045
38500
  var exports_script = {};
38046
38501
  __export(exports_script, {
38047
38502
  scriptCli: () => scriptCli,
38048
- run: () => run30,
38049
- parseArgs: () => parseArgs12,
38503
+ run: () => run31,
38504
+ parseArgs: () => parseArgs13,
38050
38505
  mapExitCode: () => mapExitCode
38051
38506
  });
38052
38507
  import { spawnSync as spawnSync21 } from "child_process";
@@ -38056,7 +38511,7 @@ function parseVar(entry) {
38056
38511
  throw new Error(`Invalid --vars entry: ${entry}`);
38057
38512
  return [entry.slice(0, index), entry.slice(index + 1)];
38058
38513
  }
38059
- function parseArgs12(argv) {
38514
+ function parseArgs13(argv) {
38060
38515
  if (argv.length === 0)
38061
38516
  throw new Error("Missing specialist name");
38062
38517
  const specialist = argv[0];
@@ -38161,8 +38616,8 @@ function runUnderLock(lockPath, argv) {
38161
38616
  return 75;
38162
38617
  return flock.status ?? 1;
38163
38618
  }
38164
- async function run30(argv = process.argv.slice(3)) {
38165
- const args = parseArgs12(argv);
38619
+ async function run31(argv = process.argv.slice(3)) {
38620
+ const args = parseArgs13(argv);
38166
38621
  if (args.singleInstance && !process.env.SP_SCRIPT_NO_LOCK) {
38167
38622
  process.exit(runUnderLock(args.singleInstance, argv));
38168
38623
  }
@@ -38175,19 +38630,19 @@ var scriptCli;
38175
38630
  var init_script = __esm(() => {
38176
38631
  init_loader();
38177
38632
  init_script_runner();
38178
- scriptCli = { parseArgs: parseArgs12, mapExitCode };
38633
+ scriptCli = { parseArgs: parseArgs13, mapExitCode };
38179
38634
  });
38180
38635
 
38181
38636
  // src/cli/help.ts
38182
38637
  var exports_help = {};
38183
38638
  __export(exports_help, {
38184
- run: () => run31
38639
+ run: () => run32
38185
38640
  });
38186
38641
  function formatCommands(entries) {
38187
38642
  const width = Math.max(...entries.map(([cmd3]) => cmd3.length));
38188
38643
  return entries.map(([cmd3, desc]) => ` ${cmd3.padEnd(width)} ${desc}`);
38189
38644
  }
38190
- async function run31() {
38645
+ async function run32() {
38191
38646
  const lines = [
38192
38647
  "",
38193
38648
  "Specialists lets you run project-scoped specialist agents with a bead-first workflow.",
@@ -38203,7 +38658,7 @@ async function run31() {
38203
38658
  " Tracked work (primary)",
38204
38659
  ' bd create "Task title" -t task -p 1 --json',
38205
38660
  " specialists run <name> --bead <id> [--context-depth N]",
38206
- " specialists poll <job-id> --json # check status",
38661
+ " specialists ps <job-id> --json # check status",
38207
38662
  ' bd close <id> --reason "Done"',
38208
38663
  "",
38209
38664
  " Ad-hoc work",
@@ -38212,7 +38667,7 @@ async function run31() {
38212
38667
  " Rules",
38213
38668
  " --bead is for tracked work",
38214
38669
  " --prompt is for quick untracked work",
38215
- " --context-depth defaults to 1 with --bead",
38670
+ " --context-depth defaults to 3 with --bead",
38216
38671
  " --no-beads does not disable bead reading",
38217
38672
  "",
38218
38673
  " Output modes",
@@ -38247,12 +38702,13 @@ async function run31() {
38247
38702
  bold15("Examples:"),
38248
38703
  " specialists init",
38249
38704
  " specialists list",
38705
+ " specialists list-rules # rule \xD7 specialist matrix",
38250
38706
  " specialists view explorer --section prompt",
38251
38707
  " specialists edit executor --preset power",
38252
38708
  " specialists edit explorer --get specialist.execution.model",
38253
38709
  " specialists run debugger --bead unitAI-123",
38254
38710
  ' specialists run codebase-explorer --prompt "Map the CLI architecture"',
38255
- " specialists poll abc123 --json # check job status",
38711
+ " specialists ps abc123 --json # check job status",
38256
38712
  " specialists feed -f # stream all job events",
38257
38713
  ' specialists steer <job-id> "focus only on supervisor.ts"',
38258
38714
  ' specialists resume <job-id> "now write the fix"',
@@ -38272,7 +38728,7 @@ async function run31() {
38272
38728
  " specialists view --help View specialist configs",
38273
38729
  " specialists edit --help Edit specialist fields (dot-path, presets)",
38274
38730
  " specialists run --help Run command details and flags",
38275
- " specialists poll --help Job status polling details",
38731
+ " specialists poll --help [DEPRECATED] use sp ps + sp feed",
38276
38732
  " specialists steer --help Mid-run steering details",
38277
38733
  " specialists resume --help Multi-turn keep-alive details",
38278
38734
  " specialists init --help Bootstrap behavior and workflow injection",
@@ -38289,6 +38745,7 @@ var init_help = __esm(() => {
38289
38745
  CORE_COMMANDS = [
38290
38746
  ["init", "Bootstrap a project: dirs, workflow injection, project MCP registration"],
38291
38747
  ["list", "List specialists; --live for interactive tmux session picker"],
38748
+ ["list-rules", "Show mandatory-rule \xD7 specialist matrix; --rule/--specialist filters, --json"],
38292
38749
  ["view", "Pretty-print specialist config with readable prompts; --section, --raw"],
38293
38750
  ["edit", "Edit specialist fields via dot-path: set/get/append/remove, --preset, --list-presets"],
38294
38751
  ["validate", "Validate specialist schema; --target=script adds compatGuard checks"],
@@ -45828,7 +46285,7 @@ var next = process.argv[3];
45828
46285
  function wantsHelp() {
45829
46286
  return next === "--help" || next === "-h";
45830
46287
  }
45831
- async function run32() {
46288
+ async function run33() {
45832
46289
  if (sub === "install") {
45833
46290
  if (wantsHelp()) {
45834
46291
  console.log([
@@ -45848,6 +46305,10 @@ async function run32() {
45848
46305
  const { run: handler } = await Promise.resolve().then(() => (init_version(), exports_version));
45849
46306
  return handler();
45850
46307
  }
46308
+ if (sub === "list-rules") {
46309
+ const { run: handler } = await Promise.resolve().then(() => (init_list_rules(), exports_list_rules));
46310
+ return handler();
46311
+ }
45851
46312
  if (sub === "list") {
45852
46313
  if (wantsHelp()) {
45853
46314
  console.log([
@@ -46163,7 +46624,7 @@ async function run32() {
46163
46624
  "Options:",
46164
46625
  " --bead <id> Use an existing bead as the prompt source",
46165
46626
  " --prompt <text> Ad-hoc prompt for untracked work",
46166
- " --context-depth <n> Dependency context depth when using --bead (default: 1)",
46627
+ " --context-depth <n> Dependency context depth when using --bead (default: 3)",
46167
46628
  " --no-beads Do not create a new tracking bead (does not disable bead reading)",
46168
46629
  " --no-bead-notes Do not append completion notes to an external --bead",
46169
46630
  " --model <model> Override the configured model for this run",
@@ -46443,8 +46904,12 @@ async function run32() {
46443
46904
  "",
46444
46905
  "Usage: specialists poll <job-id> [--cursor N] [--json]",
46445
46906
  "",
46907
+ "[DEPRECATED] Scheduled for removal. Use:",
46908
+ " sp ps <id> --json for status",
46909
+ " sp feed <id> for events",
46910
+ "",
46446
46911
  "Machine-readable job status polling for scripts and Claude Code.",
46447
- "DB-backed in normal runtime; .specialists/jobs/<id>/ is legacy/operator-only.",
46912
+ "Currently file-based; replacements above are DB-canonical.",
46448
46913
  "",
46449
46914
  "Output (JSON mode):",
46450
46915
  " {",
@@ -46803,7 +47268,7 @@ Run 'specialists help' to see available commands.`);
46803
47268
  const server = new SpecialistsServer;
46804
47269
  await server.start();
46805
47270
  }
46806
- run32().catch((error2) => {
47271
+ run33().catch((error2) => {
46807
47272
  logger.error(`Fatal error: ${error2}`);
46808
47273
  process.exit(1);
46809
47274
  });