@jaggerxtrm/specialists 3.6.18 → 3.7.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.
package/dist/index.js CHANGED
@@ -17511,7 +17511,7 @@ ${result.warnings.map((w) => ` \u26A0 ${w}`).join(`
17511
17511
  const raw = JSON.parse(jsonContent);
17512
17512
  return SpecialistSchema.parseAsync(raw);
17513
17513
  }
17514
- var KebabCase, Semver, MetadataSchema, ExecutionSchema, PromptSchema2, ScriptEntrySchema, SkillsSchema, CapabilitiesSchema, CommunicationSchema, ValidationSchema, StallDetectionSchema, SpecialistSchema;
17514
+ var KebabCase, Semver, MetadataSchema, ExecutionSchema, PromptSchema2, ScriptEntrySchema, SkillsSchema, CapabilitiesSchema, CommunicationSchema, ValidationSchema, MandatoryRuleSchema, MandatoryRulesSchema, StallDetectionSchema, SpecialistSchema;
17515
17515
  var init_schema = __esm(() => {
17516
17516
  init_zod();
17517
17517
  KebabCase = stringType().regex(/^[a-z][a-z0-9-]*$/, "Must be kebab-case");
@@ -17580,6 +17580,17 @@ var init_schema = __esm(() => {
17580
17580
  files_to_watch: arrayType(stringType()).optional(),
17581
17581
  stale_threshold_days: numberType().optional()
17582
17582
  }).optional();
17583
+ MandatoryRuleSchema = objectType({
17584
+ id: stringType(),
17585
+ level: enumType(["error", "warn", "info"]).default("error"),
17586
+ text: stringType(),
17587
+ when: stringType().optional()
17588
+ });
17589
+ MandatoryRulesSchema = objectType({
17590
+ template_sets: arrayType(KebabCase).default([]),
17591
+ disable_default_globals: booleanType().default(false),
17592
+ inline_rules: arrayType(MandatoryRuleSchema).default([])
17593
+ }).optional();
17583
17594
  StallDetectionSchema = objectType({
17584
17595
  running_silence_warn_ms: numberType().optional(),
17585
17596
  running_silence_error_ms: numberType().optional(),
@@ -17596,6 +17607,7 @@ var init_schema = __esm(() => {
17596
17607
  communication: CommunicationSchema,
17597
17608
  validation: ValidationSchema,
17598
17609
  stall_detection: StallDetectionSchema,
17610
+ mandatory_rules: MandatoryRulesSchema,
17599
17611
  output_file: stringType().optional(),
17600
17612
  beads_integration: enumType(["auto", "always", "never"]).default("auto"),
17601
17613
  beads_write_notes: booleanType().default(true),
@@ -18750,6 +18762,152 @@ function stripJsonFences(text) {
18750
18762
  return text.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim();
18751
18763
  }
18752
18764
 
18765
+ // src/specialist/mandatory-rules.ts
18766
+ import { existsSync as existsSync3, readFileSync } from "fs";
18767
+ import { resolve as resolve2 } from "path";
18768
+ function readJsonFile(filePath) {
18769
+ return JSON.parse(readFileSync(filePath, "utf8"));
18770
+ }
18771
+ function loadMandatoryRulesIndex(cwd) {
18772
+ const indexPath = resolve2(cwd, "config/mandatory-rules/index.json");
18773
+ if (!existsSync3(indexPath)) {
18774
+ console.warn("[specialist runner] Missing config/mandatory-rules/index.json; skipping MANDATORY_RULES injection");
18775
+ return null;
18776
+ }
18777
+ return readJsonFile(indexPath);
18778
+ }
18779
+ function parseQuotedScalar(value) {
18780
+ const trimmed = value.trim();
18781
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
18782
+ return trimmed.slice(1, -1);
18783
+ }
18784
+ return trimmed;
18785
+ }
18786
+ function parseRuleEntry(lines, startIndex) {
18787
+ const entryLine = lines[startIndex]?.trim();
18788
+ if (!entryLine?.startsWith("- "))
18789
+ return null;
18790
+ const firstLine = entryLine.slice(2).trim();
18791
+ const inlineFields = {};
18792
+ if (firstLine.length > 0 && !firstLine.includes(":")) {
18793
+ inlineFields.text = parseQuotedScalar(firstLine);
18794
+ } else if (firstLine.length > 0) {
18795
+ const [key, ...rest] = firstLine.split(":");
18796
+ inlineFields[key.trim()] = parseQuotedScalar(rest.join(":"));
18797
+ }
18798
+ let nextIndex = startIndex + 1;
18799
+ while (nextIndex < lines.length) {
18800
+ const line = lines[nextIndex];
18801
+ if (!line.trim()) {
18802
+ nextIndex += 1;
18803
+ continue;
18804
+ }
18805
+ if (/^\s*-\s+/.test(line))
18806
+ break;
18807
+ if (!/^\s+/.test(line))
18808
+ break;
18809
+ const trimmed = line.trim();
18810
+ const match = trimmed.match(/^([A-Za-z0-9_]+):\s*(.*)$/);
18811
+ if (!match) {
18812
+ nextIndex += 1;
18813
+ continue;
18814
+ }
18815
+ inlineFields[match[1]] = parseQuotedScalar(match[2]);
18816
+ nextIndex += 1;
18817
+ }
18818
+ if (!inlineFields.text)
18819
+ return null;
18820
+ return {
18821
+ rule: {
18822
+ id: inlineFields.id ?? "",
18823
+ level: inlineFields.level ?? "required",
18824
+ text: inlineFields.text,
18825
+ ...inlineFields.when ? { when: inlineFields.when } : {}
18826
+ },
18827
+ nextIndex
18828
+ };
18829
+ }
18830
+ function parseMandatoryRulesFrontmatter(content, setId) {
18831
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n?/);
18832
+ if (!frontmatterMatch)
18833
+ return [];
18834
+ const lines = frontmatterMatch[1].split(`
18835
+ `);
18836
+ const rulesHeaderIndex = lines.findIndex((line) => /^rules:\s*$/.test(line.trim()));
18837
+ if (rulesHeaderIndex === -1)
18838
+ return [];
18839
+ const rules = [];
18840
+ let index = rulesHeaderIndex + 1;
18841
+ while (index < lines.length) {
18842
+ const line = lines[index];
18843
+ if (!line.trim()) {
18844
+ index += 1;
18845
+ continue;
18846
+ }
18847
+ if (!/^\s*-\s+/.test(line))
18848
+ break;
18849
+ const parsed = parseRuleEntry(lines, index);
18850
+ if (!parsed)
18851
+ break;
18852
+ const ruleIndex = rules.length + 1;
18853
+ rules.push({
18854
+ id: parsed.rule.id || `${setId}-${ruleIndex}`,
18855
+ level: parsed.rule.level,
18856
+ text: parsed.rule.text,
18857
+ ...parsed.rule.when ? { when: parsed.rule.when } : {}
18858
+ });
18859
+ index = parsed.nextIndex;
18860
+ }
18861
+ return rules;
18862
+ }
18863
+ function readMandatoryRuleSet(cwd, id) {
18864
+ const candidates = [
18865
+ resolve2(cwd, `.specialists/mandatory-rules/${id}.md`),
18866
+ resolve2(cwd, `config/mandatory-rules/${id}.md`)
18867
+ ];
18868
+ const filePath = candidates.find((path) => existsSync3(path));
18869
+ if (!filePath)
18870
+ return null;
18871
+ const content = readFileSync(filePath, "utf8");
18872
+ const rules = parseMandatoryRulesFrontmatter(content, id);
18873
+ if (rules.length > 0)
18874
+ return { id, rules };
18875
+ const body = content.replace(/^---\n[\s\S]*?\n---\n?/, "").trim();
18876
+ if (!body)
18877
+ return null;
18878
+ return {
18879
+ id,
18880
+ rules: [{ id: `${id}-1`, level: "required", text: body.replace(/\s+/g, " ") }]
18881
+ };
18882
+ }
18883
+ function formatMandatoryRulesBlock(sets) {
18884
+ if (sets.length === 0)
18885
+ return "";
18886
+ const sections = sets.map((set2) => {
18887
+ const rules = set2.rules.map((rule) => `- [${rule.level}] ${rule.text}`).join(`
18888
+ `);
18889
+ return `### ${set2.id}
18890
+ ${rules}`;
18891
+ });
18892
+ return `## MANDATORY_RULES
18893
+ ${sections.join(`
18894
+
18895
+ `)}`;
18896
+ }
18897
+ function buildMandatoryRulesBlock(specialistConfig) {
18898
+ const cwd = specialistConfig.cwd ?? process.cwd();
18899
+ const index = loadMandatoryRulesIndex(cwd);
18900
+ if (!index)
18901
+ return "";
18902
+ const setIds = [
18903
+ ...index.required_template_sets ?? [],
18904
+ ...index.default_template_sets ?? []
18905
+ ];
18906
+ const sets = setIds.map((id) => readMandatoryRuleSet(cwd, id)).filter((set2) => set2 !== null);
18907
+ return formatMandatoryRulesBlock(sets);
18908
+ }
18909
+ var init_mandatory_rules = () => {};
18910
+
18753
18911
  // src/specialist/beads.ts
18754
18912
  import { spawnSync } from "child_process";
18755
18913
  function buildBeadContext(bead, completedBlockers = []) {
@@ -18903,7 +19061,7 @@ function shouldCreateBead(beadsIntegration, permissionRequired) {
18903
19061
  var init_beads = () => {};
18904
19062
 
18905
19063
  // src/specialist/observability-db.ts
18906
- import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
19064
+ import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
18907
19065
  import { spawnSync as spawnSync2 } from "child_process";
18908
19066
  import { join as join3, sep as sep2 } from "path";
18909
19067
  function resolveGitRootFrom(cwd) {
@@ -18953,7 +19111,7 @@ function resolveObservabilityDbLocation(cwd = process.cwd()) {
18953
19111
  }
18954
19112
  function ensureObservabilityDbFile(location) {
18955
19113
  mkdirSync2(location.dbDirectory, { recursive: true });
18956
- const alreadyExists = existsSync3(location.dbPath);
19114
+ const alreadyExists = existsSync4(location.dbPath);
18957
19115
  if (!alreadyExists) {
18958
19116
  writeFileSync2(location.dbPath, "", { encoding: "utf-8", flag: "wx" });
18959
19117
  }
@@ -18967,7 +19125,7 @@ function ensureGitignoreHasObservabilityDbEntries(gitRoot) {
18967
19125
  ".specialists/db/*.db-wal",
18968
19126
  ".specialists/db/*.db-shm"
18969
19127
  ];
18970
- const existing = existsSync3(gitignorePath) ? readFileSync(gitignorePath, "utf-8") : "";
19128
+ const existing = existsSync4(gitignorePath) ? readFileSync2(gitignorePath, "utf-8") : "";
18971
19129
  const existingLines = new Set(existing.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
18972
19130
  const missingEntries = requiredEntries.filter((entry) => !existingLines.has(entry));
18973
19131
  if (missingEntries.length === 0) {
@@ -18996,7 +19154,7 @@ var init_observability_db = __esm(() => {
18996
19154
 
18997
19155
  // src/specialist/job-root.ts
18998
19156
  import { spawnSync as spawnSync3 } from "child_process";
18999
- import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
19157
+ import { dirname as dirname2, join as join4, resolve as resolve3 } from "path";
19000
19158
  function resolveCommonGitRoot(cwd) {
19001
19159
  const result = spawnSync3("git", ["rev-parse", "--git-common-dir"], {
19002
19160
  cwd,
@@ -19008,7 +19166,7 @@ function resolveCommonGitRoot(cwd) {
19008
19166
  const gitCommonDir = result.stdout?.trim();
19009
19167
  if (!gitCommonDir)
19010
19168
  return;
19011
- return dirname2(resolve2(cwd, gitCommonDir));
19169
+ return dirname2(resolve3(cwd, gitCommonDir));
19012
19170
  }
19013
19171
  function resolveJobsDir(cwd = process.cwd()) {
19014
19172
  const commonRoot = resolveCommonGitRoot(cwd) ?? cwd;
@@ -19028,7 +19186,7 @@ function resolveCurrentBranch(cwd = process.cwd()) {
19028
19186
  var init_job_root = () => {};
19029
19187
 
19030
19188
  // src/specialist/observability-sqlite.ts
19031
- import { existsSync as existsSync4, readFileSync as readFileSync2, statSync } from "fs";
19189
+ import { existsSync as existsSync5, readFileSync as readFileSync3, statSync } from "fs";
19032
19190
  import { join as join5 } from "path";
19033
19191
  function loadBunDatabase() {
19034
19192
  if (_probed)
@@ -20632,7 +20790,7 @@ class SqliteClient {
20632
20790
  WHERE worktree_column IS NOT NULL AND worktree_column != ''
20633
20791
  `).all();
20634
20792
  for (const row of worktreeRows) {
20635
- if (existsSync4(row.worktree_column))
20793
+ if (existsSync5(row.worktree_column))
20636
20794
  continue;
20637
20795
  findings.push({
20638
20796
  kind: "stale-pointer",
@@ -20659,10 +20817,10 @@ function hasRunCompleteEvent(jobId, cwd = process.cwd()) {
20659
20817
  sqliteClient?.close();
20660
20818
  }
20661
20819
  const eventsPath = join5(resolveJobsDir(cwd), jobId, "events.jsonl");
20662
- if (!existsSync4(eventsPath))
20820
+ if (!existsSync5(eventsPath))
20663
20821
  return false;
20664
20822
  try {
20665
- const lines = readFileSync2(eventsPath, "utf-8").split(`
20823
+ const lines = readFileSync3(eventsPath, "utf-8").split(`
20666
20824
  `).map((line) => line.trim()).filter((line) => line.length > 0);
20667
20825
  for (const line of lines) {
20668
20826
  const event = JSON.parse(line);
@@ -20678,7 +20836,7 @@ function createObservabilitySqliteClient(cwd = process.cwd()) {
20678
20836
  if (!loadBunDatabase())
20679
20837
  return null;
20680
20838
  const location = resolveObservabilityDbLocation(cwd);
20681
- if (!existsSync4(location.dbPath))
20839
+ if (!existsSync5(location.dbPath))
20682
20840
  return null;
20683
20841
  try {
20684
20842
  const Ctor = loadBunDatabase();
@@ -20926,8 +21084,8 @@ var init_memory_retrieval = __esm(() => {
20926
21084
  import { createHash as createHash2 } from "crypto";
20927
21085
  import { writeFile } from "fs/promises";
20928
21086
  import { execSync as execSync2, spawnSync as spawnSync4 } from "child_process";
20929
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
20930
- import { basename as basename2, resolve as resolve3 } from "path";
21087
+ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
21088
+ import { basename as basename2, resolve as resolve4 } from "path";
20931
21089
  import { homedir as homedir2 } from "os";
20932
21090
  function runScript(command, cwd) {
20933
21091
  const run = (command ?? "").trim();
@@ -20958,7 +21116,7 @@ ${blocks}
20958
21116
  </pre_flight_context>`;
20959
21117
  }
20960
21118
  function resolvePath(p) {
20961
- return p.startsWith("~/") ? resolve3(homedir2(), p.slice(2)) : resolve3(p);
21119
+ return p.startsWith("~/") ? resolve4(homedir2(), p.slice(2)) : resolve4(p);
20962
21120
  }
20963
21121
  function commandExists(cmd) {
20964
21122
  const result = spawnSync4("which", [cmd], { stdio: "ignore" });
@@ -20966,7 +21124,7 @@ function commandExists(cmd) {
20966
21124
  }
20967
21125
  function validateShebang(filePath, errors5) {
20968
21126
  try {
20969
- const head = readFileSync3(filePath, "utf-8").slice(0, 120);
21127
+ const head = readFileSync4(filePath, "utf-8").slice(0, 120);
20970
21128
  if (!head.startsWith("#!"))
20971
21129
  return;
20972
21130
  const shebang = head.split(`
@@ -20996,7 +21154,7 @@ function validateBeforeRun(spec, permissionLevel) {
20996
21154
  const warnings = [];
20997
21155
  for (const p of spec.specialist.skills?.paths ?? []) {
20998
21156
  const abs = resolvePath(p);
20999
- if (!existsSync5(abs))
21157
+ if (!existsSync6(abs))
21000
21158
  warnings.push(` \u26A0 skills.paths: file not found: ${p}`);
21001
21159
  }
21002
21160
  for (const script of spec.specialist.skills?.scripts ?? []) {
@@ -21006,7 +21164,7 @@ function validateBeforeRun(spec, permissionLevel) {
21006
21164
  const isFilePath = run.startsWith("./") || run.startsWith("../") || run.startsWith("/") || run.startsWith("~/");
21007
21165
  if (isFilePath) {
21008
21166
  const abs = resolvePath(run);
21009
- if (!existsSync5(abs)) {
21167
+ if (!existsSync6(abs)) {
21010
21168
  errors5.push(` \u2717 skills.scripts: script not found: ${run}`);
21011
21169
  } else {
21012
21170
  validateShebang(abs, errors5);
@@ -21041,7 +21199,7 @@ ${errors5.join(`
21041
21199
  }
21042
21200
  }
21043
21201
  function sleep(ms) {
21044
- return new Promise((resolve4) => setTimeout(resolve4, ms));
21202
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
21045
21203
  }
21046
21204
  function getRetryDelayMs(attemptNumber) {
21047
21205
  const baseDelay = RETRY_BASE_DELAY_MS * 2 ** Math.max(0, attemptNumber - 1);
@@ -21332,7 +21490,7 @@ ${buildBeadBoundaryInstruction(runCwd, options.worktreeBoundary)}`.trim();
21332
21490
  execution.extensions?.gitnexus === false ? "pi-gitnexus" : undefined
21333
21491
  ].filter((value) => Boolean(value));
21334
21492
  validateBeforeRun(spec, permissionLevel);
21335
- const runCwd = resolve3(options.workingDirectory ?? process.cwd());
21493
+ const runCwd = resolve4(options.workingDirectory ?? process.cwd());
21336
21494
  const preScripts = spec.specialist.skills?.scripts?.filter((s) => s.phase === "pre") ?? [];
21337
21495
  const preResults = preScripts.map((s) => runScript(s.run ?? s.path, runCwd)).filter((_, i) => preScripts[i].inject_output);
21338
21496
  const preScriptOutput = formatScriptOutput(preResults);
@@ -21357,7 +21515,23 @@ ${buildBeadBoundaryInstruction(runCwd, options.worktreeBoundary)}`.trim();
21357
21515
  ...beadVariables
21358
21516
  };
21359
21517
  const taskTemplate = options.inputBeadId ? renderTemplate(prompt.task_template, beadTemplateVariables) : prompt.task_template;
21360
- const renderedTask = renderTemplate(taskTemplate, variables);
21518
+ let renderedTask = renderTemplate(taskTemplate, variables);
21519
+ let mandatoryRulesBlock = "";
21520
+ try {
21521
+ mandatoryRulesBlock = buildMandatoryRulesBlock({ cwd: runCwd });
21522
+ if (mandatoryRulesBlock.trim()) {
21523
+ const rulesTokens = Math.ceil(mandatoryRulesBlock.length / 4);
21524
+ if (rulesTokens <= 2000) {
21525
+ renderedTask = `${renderedTask}
21526
+
21527
+ ${mandatoryRulesBlock}`;
21528
+ } else {
21529
+ console.warn(`[specialist runner] Skipping MANDATORY_RULES injection: rules block too large (${rulesTokens} tokens, limit 2000)`);
21530
+ }
21531
+ }
21532
+ } catch (error2) {
21533
+ console.warn(`[specialist runner] Skipping MANDATORY_RULES injection: ${String(error2)}`);
21534
+ }
21361
21535
  const promptHash = createHash2("sha256").update(renderedTask).digest("hex").slice(0, 16);
21362
21536
  await hooks.emit("post_render", invocationId, metadata.name, metadata.version, {
21363
21537
  prompt_hash: promptHash,
@@ -21398,8 +21572,8 @@ Respond like smart caveman. Cut all filler, keep technical substance.
21398
21572
  ---
21399
21573
  `;
21400
21574
  try {
21401
- const gitnexusMetaPath = resolve3(runCwd, ".gitnexus/meta.json");
21402
- if (existsSync5(gitnexusMetaPath)) {
21575
+ const gitnexusMetaPath = resolve4(runCwd, ".gitnexus/meta.json");
21576
+ if (existsSync6(gitnexusMetaPath)) {
21403
21577
  agentsMd += `
21404
21578
 
21405
21579
  ---
@@ -21452,8 +21626,8 @@ ${memoryInjection.block}
21452
21626
  memoryTokens = memoryInjection.estimatedTokens;
21453
21627
  }
21454
21628
  try {
21455
- const gitnexusMetaPath = resolve3(runCwd, ".gitnexus/meta.json");
21456
- if (existsSync5(gitnexusMetaPath)) {
21629
+ const gitnexusMetaPath = resolve4(runCwd, ".gitnexus/meta.json");
21630
+ if (existsSync6(gitnexusMetaPath)) {
21457
21631
  const symbolCandidates = (beadForMemory.title.match(/\b(?:[A-Z][a-z0-9]+(?:[A-Z][a-z0-9]+)+|[a-z]+[A-Z][A-Za-z0-9]*)\b/g) ?? []).slice(0, 2);
21458
21632
  const summaries = [];
21459
21633
  for (const symbol of symbolCandidates) {
@@ -21503,6 +21677,35 @@ ${summaries.join(`
21503
21677
  }
21504
21678
  })
21505
21679
  });
21680
+ const mandatoryRulesInjection = (() => {
21681
+ if (!mandatoryRulesBlock.trim())
21682
+ return null;
21683
+ const setsLoaded = mandatoryRulesBlock.match(/^###\s+(.+)$/gm)?.map((line) => line.replace(/^###\s+/, "").trim()) ?? [];
21684
+ const ruleCount = (mandatoryRulesBlock.match(/^- \[[^\]]+\]/gm) ?? []).length;
21685
+ const payload = {
21686
+ source: "mandatory_rules_injection",
21687
+ data: {
21688
+ sets_loaded: setsLoaded,
21689
+ rules_count: ruleCount,
21690
+ inline_rules_count: ruleCount,
21691
+ globals_disabled: false,
21692
+ token_estimate: estimateInjectedTokens(mandatoryRulesBlock)
21693
+ }
21694
+ };
21695
+ return {
21696
+ payload,
21697
+ summary: JSON.stringify({
21698
+ kind: "meta",
21699
+ ...payload
21700
+ })
21701
+ };
21702
+ })();
21703
+ if (mandatoryRulesInjection) {
21704
+ onEvent?.("meta", {
21705
+ ...mandatoryRulesInjection.payload,
21706
+ summary: mandatoryRulesInjection.summary
21707
+ });
21708
+ }
21506
21709
  if (metadata.name === "reviewer" && options.reusedFromJobId) {
21507
21710
  const reviewerDiffContext = buildReviewerDiffContext(runCwd);
21508
21711
  agentsMd += buildReviewerDiffInstruction(reviewerDiffContext);
@@ -21723,6 +21926,7 @@ var PERMISSION_GATED_TOOLS, RETRY_BASE_DELAY_MS = 1000, RETRY_MAX_JITTER = 0.2,
21723
21926
  var init_runner = __esm(() => {
21724
21927
  init_session();
21725
21928
  init_circuitBreaker();
21929
+ init_mandatory_rules();
21726
21930
  init_beads();
21727
21931
  init_memory_retrieval();
21728
21932
  PERMISSION_GATED_TOOLS = {
@@ -21964,16 +22168,16 @@ __export(exports_version, {
21964
22168
  import { createRequire } from "module";
21965
22169
  import { fileURLToPath } from "url";
21966
22170
  import { dirname as dirname4, join as join7 } from "path";
21967
- import { existsSync as existsSync6 } from "fs";
22171
+ import { existsSync as existsSync7 } from "fs";
21968
22172
  async function run2() {
21969
22173
  const req = createRequire(import.meta.url);
21970
22174
  const here = dirname4(fileURLToPath(import.meta.url));
21971
22175
  const bundlePkgPath = join7(here, "..", "package.json");
21972
22176
  const sourcePkgPath = join7(here, "..", "..", "package.json");
21973
22177
  let pkg;
21974
- if (existsSync6(bundlePkgPath)) {
22178
+ if (existsSync7(bundlePkgPath)) {
21975
22179
  pkg = req("../package.json");
21976
- } else if (existsSync6(sourcePkgPath)) {
22180
+ } else if (existsSync7(sourcePkgPath)) {
21977
22181
  pkg = req("../../package.json");
21978
22182
  } else {
21979
22183
  console.error("Cannot find package.json");
@@ -22121,6 +22325,17 @@ function mapCallbackEventToTimelineEvent(callbackEvent, context) {
22121
22325
  backend: "injected",
22122
22326
  ...context.memoryInjection ? { memory_injection: context.memoryInjection } : {}
22123
22327
  };
22328
+ case "meta": {
22329
+ const payload = context.metaPayload;
22330
+ return {
22331
+ t,
22332
+ type: TIMELINE_EVENT_TYPES.META,
22333
+ model: payload?.model ?? "meta",
22334
+ backend: payload?.backend ?? "injected",
22335
+ ...payload?.source ? { source: payload.source } : {},
22336
+ ...payload?.data ? { data: payload.data } : {}
22337
+ };
22338
+ }
22124
22339
  case "text":
22125
22340
  return {
22126
22341
  t,
@@ -22400,7 +22615,7 @@ var init_epic_lifecycle = __esm(() => {
22400
22615
  });
22401
22616
 
22402
22617
  // src/specialist/process-liveness.ts
22403
- import { readFileSync as readFileSync4 } from "fs";
22618
+ import { readFileSync as readFileSync5 } from "fs";
22404
22619
  function isValidPid(pid) {
22405
22620
  return Number.isInteger(pid) && pid > 0;
22406
22621
  }
@@ -22414,7 +22629,7 @@ function isAliveBySignal(pid) {
22414
22629
  }
22415
22630
  function parseBootTimeMs() {
22416
22631
  try {
22417
- const procStat = readFileSync4("/proc/stat", "utf-8");
22632
+ const procStat = readFileSync5("/proc/stat", "utf-8");
22418
22633
  const bootLine = procStat.split(`
22419
22634
  `).find((line) => line.startsWith("btime "));
22420
22635
  if (!bootLine)
@@ -22429,7 +22644,7 @@ function parseBootTimeMs() {
22429
22644
  }
22430
22645
  function parseProcessStartTimeMs(pid) {
22431
22646
  try {
22432
- const statRaw = readFileSync4(`/proc/${pid}/stat`, "utf-8");
22647
+ const statRaw = readFileSync5(`/proc/${pid}/stat`, "utf-8");
22433
22648
  const closeParenIndex = statRaw.lastIndexOf(")");
22434
22649
  if (closeParenIndex < 0)
22435
22650
  return;
@@ -22791,12 +23006,12 @@ var init_tmux_utils = () => {};
22791
23006
  import {
22792
23007
  appendFileSync,
22793
23008
  closeSync,
22794
- existsSync as existsSync7,
23009
+ existsSync as existsSync8,
22795
23010
  fsyncSync,
22796
23011
  mkdirSync as mkdirSync3,
22797
23012
  openSync,
22798
23013
  readdirSync,
22799
- readFileSync as readFileSync5,
23014
+ readFileSync as readFileSync6,
22800
23015
  renameSync,
22801
23016
  rmSync,
22802
23017
  statSync as statSync2,
@@ -23136,8 +23351,8 @@ class Supervisor {
23136
23351
  } finally {
23137
23352
  this.pendingSqliteOperations -= 1;
23138
23353
  if (this.pendingSqliteOperations === 0) {
23139
- for (const resolve4 of this.pendingSqliteDrainResolvers) {
23140
- resolve4();
23354
+ for (const resolve5 of this.pendingSqliteDrainResolvers) {
23355
+ resolve5();
23141
23356
  }
23142
23357
  this.pendingSqliteDrainResolvers.clear();
23143
23358
  }
@@ -23146,8 +23361,8 @@ class Supervisor {
23146
23361
  async waitForPendingSqliteOperations() {
23147
23362
  if (this.pendingSqliteOperations === 0)
23148
23363
  return;
23149
- await new Promise((resolve4) => {
23150
- this.pendingSqliteDrainResolvers.add(resolve4);
23364
+ await new Promise((resolve5) => {
23365
+ this.pendingSqliteDrainResolvers.add(resolve5);
23151
23366
  });
23152
23367
  }
23153
23368
  async dispose() {
@@ -23207,10 +23422,10 @@ class Supervisor {
23207
23422
  }
23208
23423
  }
23209
23424
  const path = this.statusPath(id);
23210
- if (!existsSync7(path))
23425
+ if (!existsSync8(path))
23211
23426
  return null;
23212
23427
  try {
23213
- const status = JSON.parse(readFileSync5(path, "utf-8"));
23428
+ const status = JSON.parse(readFileSync6(path, "utf-8"));
23214
23429
  return this.withComputedLiveness(status);
23215
23430
  } catch {
23216
23431
  return null;
@@ -23244,15 +23459,15 @@ class Supervisor {
23244
23459
  console.warn(`[supervisor] SQLite listStatuses failed, falling back to file state: ${String(error2)}`);
23245
23460
  }
23246
23461
  }
23247
- if (!existsSync7(this.resolvedJobsDir))
23462
+ if (!existsSync8(this.resolvedJobsDir))
23248
23463
  return [];
23249
23464
  const jobs = [];
23250
23465
  for (const entry of readdirSync(this.resolvedJobsDir)) {
23251
23466
  const path = join8(this.resolvedJobsDir, entry, "status.json");
23252
- if (!existsSync7(path))
23467
+ if (!existsSync8(path))
23253
23468
  continue;
23254
23469
  try {
23255
- const status = JSON.parse(readFileSync5(path, "utf-8"));
23470
+ const status = JSON.parse(readFileSync6(path, "utf-8"));
23256
23471
  jobs.push(this.withComputedLiveness(status));
23257
23472
  } catch {}
23258
23473
  }
@@ -23326,7 +23541,7 @@ class Supervisor {
23326
23541
  }
23327
23542
  }
23328
23543
  gc() {
23329
- if (!existsSync7(this.resolvedJobsDir))
23544
+ if (!existsSync8(this.resolvedJobsDir))
23330
23545
  return;
23331
23546
  const cutoff = Date.now() - JOB_TTL_DAYS * 86400000;
23332
23547
  for (const entry of readdirSync(this.resolvedJobsDir)) {
@@ -23341,7 +23556,7 @@ class Supervisor {
23341
23556
  }
23342
23557
  }
23343
23558
  crashRecovery() {
23344
- if (!existsSync7(this.resolvedJobsDir))
23559
+ if (!existsSync8(this.resolvedJobsDir))
23345
23560
  return;
23346
23561
  const thresholds = {
23347
23562
  ...STALL_DETECTION_DEFAULTS,
@@ -23350,10 +23565,10 @@ class Supervisor {
23350
23565
  const now = Date.now();
23351
23566
  for (const entry of readdirSync(this.resolvedJobsDir)) {
23352
23567
  const statusPath = join8(this.resolvedJobsDir, entry, "status.json");
23353
- if (!existsSync7(statusPath))
23568
+ if (!existsSync8(statusPath))
23354
23569
  continue;
23355
23570
  try {
23356
- const s = JSON.parse(readFileSync5(statusPath, "utf-8"));
23571
+ const s = JSON.parse(readFileSync6(statusPath, "utf-8"));
23357
23572
  if (s.status === "running" || s.status === "starting") {
23358
23573
  if (!s.pid)
23359
23574
  continue;
@@ -23574,8 +23789,8 @@ class Supervisor {
23574
23789
  let keepAliveExitResolved = false;
23575
23790
  let isReadOnlySpecialist = false;
23576
23791
  let resolveKeepAliveExit;
23577
- const keepAliveExitPromise = new Promise((resolve4) => {
23578
- resolveKeepAliveExit = resolve4;
23792
+ const keepAliveExitPromise = new Promise((resolve5) => {
23793
+ resolveKeepAliveExit = resolve5;
23579
23794
  });
23580
23795
  const finishKeepAlive = (exit) => {
23581
23796
  if (keepAliveExitResolved)
@@ -23817,29 +24032,35 @@ ${appendError}
23817
24032
  }
23818
24033
  const toolCallId = details?.toolCallId;
23819
24034
  const toolState = toolCallId ? activeToolCalls.get(toolCallId) : latestUncorrelatedToolState;
23820
- const memoryInjection = (() => {
23821
- if (eventType !== "memory_injection" || !details?.summary)
24035
+ const parsedMeta = (() => {
24036
+ if (eventType !== "memory_injection" && eventType !== "meta" || !details?.summary)
23822
24037
  return;
23823
24038
  try {
23824
- const parsed = JSON.parse(details.summary);
23825
- const injection = parsed.memory_injection;
23826
- if (!injection)
23827
- return;
23828
- return {
23829
- static_tokens: injection.static_tokens ?? 0,
23830
- memory_tokens: injection.memory_tokens ?? 0,
23831
- gitnexus_tokens: injection.gitnexus_tokens ?? 0,
23832
- total_tokens: injection.total_tokens ?? 0
23833
- };
24039
+ return JSON.parse(details.summary);
23834
24040
  } catch {
23835
24041
  return;
23836
24042
  }
23837
24043
  })();
23838
- if (eventType === "memory_injection" && memoryInjection) {
24044
+ const metaDetails = details;
24045
+ const memoryInjection = parsedMeta?.memory_injection ? {
24046
+ static_tokens: parsedMeta.memory_injection.static_tokens ?? 0,
24047
+ memory_tokens: parsedMeta.memory_injection.memory_tokens ?? 0,
24048
+ gitnexus_tokens: parsedMeta.memory_injection.gitnexus_tokens ?? 0,
24049
+ total_tokens: parsedMeta.memory_injection.total_tokens ?? 0
24050
+ } : undefined;
24051
+ const mandatoryRulesInjection = parsedMeta?.source === "mandatory_rules_injection" && parsedMeta.data ? {
24052
+ sets_loaded: parsedMeta.data.sets_loaded ?? [],
24053
+ rules_count: parsedMeta.data.rules_count ?? 0,
24054
+ inline_rules_count: parsedMeta.data.inline_rules_count ?? 0,
24055
+ globals_disabled: parsedMeta.data.globals_disabled ?? false,
24056
+ token_estimate: parsedMeta.data.token_estimate ?? 0
24057
+ } : undefined;
24058
+ if (memoryInjection || mandatoryRulesInjection) {
23839
24059
  setStatus({
23840
24060
  startup_context: {
23841
24061
  ...statusSnapshot.startup_context ?? {},
23842
- memory_injection: memoryInjection
24062
+ ...memoryInjection ? { memory_injection: memoryInjection } : {},
24063
+ ...mandatoryRulesInjection ? { mandatory_rules_injection: mandatoryRulesInjection } : {}
23843
24064
  }
23844
24065
  });
23845
24066
  }
@@ -23871,7 +24092,13 @@ ${appendError}
23871
24092
  extension: details?.extension,
23872
24093
  errorMessage: details?.errorMessage
23873
24094
  },
23874
- memoryInjection
24095
+ memoryInjection,
24096
+ metaPayload: eventType === "meta" ? {
24097
+ model: details?.model,
24098
+ backend: metaDetails?.backend,
24099
+ source: metaDetails?.source,
24100
+ data: metaDetails?.data
24101
+ } : undefined
23875
24102
  });
23876
24103
  if (timelineEvent) {
23877
24104
  appendTimelineEvent(timelineEvent);
@@ -23956,7 +24183,7 @@ ${appendError}
23956
24183
  setStatus({ bead_id: beadId });
23957
24184
  }, (fn) => {
23958
24185
  steerFn = fn;
23959
- if (!existsSync7(fifoPath))
24186
+ if (!existsSync8(fifoPath))
23960
24187
  return;
23961
24188
  fifoFd = openSync(fifoPath, "r+");
23962
24189
  fifoReadStream = createReadStream("", { fd: fifoFd, autoClose: false });
@@ -24252,7 +24479,7 @@ ${appendError}
24252
24479
  } catch {}
24253
24480
  closeSync(eventsFd);
24254
24481
  try {
24255
- if (existsSync7(fifoPath))
24482
+ if (existsSync8(fifoPath))
24256
24483
  rmSync(fifoPath);
24257
24484
  } catch {}
24258
24485
  if (statusSnapshot.tmux_session) {
@@ -24301,7 +24528,7 @@ __export(exports_list, {
24301
24528
  ArgParseError: () => ArgParseError
24302
24529
  });
24303
24530
  import { spawnSync as spawnSync7 } from "child_process";
24304
- import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
24531
+ import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync7 } from "fs";
24305
24532
  import { join as join9 } from "path";
24306
24533
  import readline from "readline";
24307
24534
  function permissionBadge(permission) {
@@ -24334,14 +24561,14 @@ function toLiveJob(status) {
24334
24561
  }
24335
24562
  function readJobStatus(statusPath) {
24336
24563
  try {
24337
- return JSON.parse(readFileSync6(statusPath, "utf-8"));
24564
+ return JSON.parse(readFileSync7(statusPath, "utf-8"));
24338
24565
  } catch {
24339
24566
  return null;
24340
24567
  }
24341
24568
  }
24342
24569
  function listLiveJobs(showDead) {
24343
24570
  const jobsDir = join9(process.cwd(), ".specialists", "jobs");
24344
- if (!existsSync8(jobsDir))
24571
+ if (!existsSync9(jobsDir))
24345
24572
  return [];
24346
24573
  const jobs = 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);
24347
24574
  return jobs;
@@ -24360,7 +24587,7 @@ function renderLiveSelector(jobs, selectedIndex) {
24360
24587
  ];
24361
24588
  }
24362
24589
  function selectLiveJob(jobs) {
24363
- return new Promise((resolve4) => {
24590
+ return new Promise((resolve5) => {
24364
24591
  const input = process.stdin;
24365
24592
  const output = process.stdout;
24366
24593
  const wasRawMode = input.isTTY ? input.isRaw : false;
@@ -24376,7 +24603,7 @@ function selectLiveJob(jobs) {
24376
24603
  readline.moveCursor(output, 0, -renderedLineCount);
24377
24604
  readline.clearScreenDown(output);
24378
24605
  }
24379
- resolve4(selected);
24606
+ resolve5(selected);
24380
24607
  };
24381
24608
  const render = () => {
24382
24609
  if (renderedLineCount > 0) {
@@ -24903,9 +25130,9 @@ var exports_init = {};
24903
25130
  __export(exports_init, {
24904
25131
  run: () => run6
24905
25132
  });
24906
- import { copyFileSync, cpSync, existsSync as existsSync9, lstatSync, mkdirSync as mkdirSync4, readdirSync as readdirSync3, readFileSync as readFileSync7, readlinkSync, renameSync as renameSync2, symlinkSync, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
25133
+ 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";
24907
25134
  import { spawnSync as spawnSync9 } from "child_process";
24908
- import { basename as basename3, dirname as dirname5, join as join10, relative, resolve as resolve4 } from "path";
25135
+ import { basename as basename3, dirname as dirname5, join as join10, relative, resolve as resolve5 } from "path";
24909
25136
  import { fileURLToPath as fileURLToPath2 } from "url";
24910
25137
  function ok(msg) {
24911
25138
  console.log(` ${green4("\u2713")} ${msg}`);
@@ -24920,7 +25147,7 @@ function isInstalled(bin) {
24920
25147
  return spawnSync9("which", [bin], { encoding: "utf8", timeout: 2000 }).status === 0;
24921
25148
  }
24922
25149
  function assertXtrmPrerequisites(cwd) {
24923
- const hasXtrmDir = existsSync9(join10(cwd, ".xtrm"));
25150
+ const hasXtrmDir = existsSync10(join10(cwd, ".xtrm"));
24924
25151
  const hasXtCli = isInstalled("xt");
24925
25152
  if (hasXtrmDir && hasXtCli)
24926
25153
  return;
@@ -24942,10 +25169,10 @@ function warnMissingOptionalPrerequisites() {
24942
25169
  }
24943
25170
  }
24944
25171
  function loadJson(path, fallback) {
24945
- if (!existsSync9(path))
25172
+ if (!existsSync10(path))
24946
25173
  return structuredClone(fallback);
24947
25174
  try {
24948
- return JSON.parse(readFileSync7(path, "utf-8"));
25175
+ return JSON.parse(readFileSync8(path, "utf-8"));
24949
25176
  } catch {
24950
25177
  return structuredClone(fallback);
24951
25178
  }
@@ -24957,19 +25184,19 @@ function saveJson(path, value) {
24957
25184
  function resolvePackagePath(relativePath) {
24958
25185
  const configPath = `config/${relativePath}`;
24959
25186
  let resolved = fileURLToPath2(new URL(`../${configPath}`, import.meta.url));
24960
- if (existsSync9(resolved))
25187
+ if (existsSync10(resolved))
24961
25188
  return resolved;
24962
25189
  resolved = fileURLToPath2(new URL(`../../${configPath}`, import.meta.url));
24963
- if (existsSync9(resolved))
25190
+ if (existsSync10(resolved))
24964
25191
  return resolved;
24965
25192
  return null;
24966
25193
  }
24967
25194
  function migrateLegacySpecialists(cwd, scope) {
24968
25195
  const sourceDir = join10(cwd, ".specialists", scope, "specialists");
24969
- if (!existsSync9(sourceDir))
25196
+ if (!existsSync10(sourceDir))
24970
25197
  return;
24971
25198
  const targetDir = join10(cwd, ".specialists", scope);
24972
- if (!existsSync9(targetDir)) {
25199
+ if (!existsSync10(targetDir)) {
24973
25200
  mkdirSync4(targetDir, { recursive: true });
24974
25201
  }
24975
25202
  const files = readdirSync3(sourceDir).filter((f) => f.endsWith(".specialist.json") || f.endsWith(".specialist.json"));
@@ -24980,7 +25207,7 @@ function migrateLegacySpecialists(cwd, scope) {
24980
25207
  for (const file of files) {
24981
25208
  const src = join10(sourceDir, file);
24982
25209
  const dest = join10(targetDir, file);
24983
- if (existsSync9(dest)) {
25210
+ if (existsSync10(dest)) {
24984
25211
  skipped++;
24985
25212
  continue;
24986
25213
  }
@@ -25006,16 +25233,17 @@ function copyCanonicalSpecialists(cwd) {
25006
25233
  skip("no specialist files found in package");
25007
25234
  return;
25008
25235
  }
25009
- if (!existsSync9(targetDir)) {
25236
+ if (!existsSync10(targetDir)) {
25010
25237
  mkdirSync4(targetDir, { recursive: true });
25011
25238
  }
25012
25239
  let copied = 0;
25013
- let skipped = 0;
25240
+ let refreshed = 0;
25014
25241
  for (const file of files) {
25015
25242
  const src = join10(sourceDir, file);
25016
25243
  const dest = join10(targetDir, file);
25017
- if (existsSync9(dest)) {
25018
- skipped++;
25244
+ if (existsSync10(dest)) {
25245
+ copyFileSync(src, dest);
25246
+ refreshed++;
25019
25247
  } else {
25020
25248
  copyFileSync(src, dest);
25021
25249
  copied++;
@@ -25024,8 +25252,8 @@ function copyCanonicalSpecialists(cwd) {
25024
25252
  if (copied > 0) {
25025
25253
  ok(`copied ${copied} canonical specialist${copied === 1 ? "" : "s"} to .specialists/default/`);
25026
25254
  }
25027
- if (skipped > 0) {
25028
- skip(`${skipped} specialist${skipped === 1 ? "" : "s"} already exist (not overwritten)`);
25255
+ if (refreshed > 0) {
25256
+ ok(`re-synced ${refreshed} canonical specialist${refreshed === 1 ? "" : "s"} in .specialists/default/`);
25029
25257
  }
25030
25258
  }
25031
25259
  function copyCanonicalNodeConfigs(cwd) {
@@ -25040,16 +25268,17 @@ function copyCanonicalNodeConfigs(cwd) {
25040
25268
  skip("no node config files found in package");
25041
25269
  return;
25042
25270
  }
25043
- if (!existsSync9(targetDir)) {
25271
+ if (!existsSync10(targetDir)) {
25044
25272
  mkdirSync4(targetDir, { recursive: true });
25045
25273
  }
25046
25274
  let copied = 0;
25047
- let skipped = 0;
25275
+ let refreshed = 0;
25048
25276
  for (const file of files) {
25049
25277
  const src = join10(sourceDir, file);
25050
25278
  const dest = join10(targetDir, file);
25051
- if (existsSync9(dest)) {
25052
- skipped++;
25279
+ if (existsSync10(dest)) {
25280
+ copyFileSync(src, dest);
25281
+ refreshed++;
25053
25282
  } else {
25054
25283
  copyFileSync(src, dest);
25055
25284
  copied++;
@@ -25058,8 +25287,8 @@ function copyCanonicalNodeConfigs(cwd) {
25058
25287
  if (copied > 0) {
25059
25288
  ok(`copied ${copied} canonical node config${copied === 1 ? "" : "s"} to .specialists/default/nodes/`);
25060
25289
  }
25061
- if (skipped > 0) {
25062
- skip(`${skipped} node config${skipped === 1 ? "" : "s"} already exist (not overwritten)`);
25290
+ if (refreshed > 0) {
25291
+ ok(`re-synced ${refreshed} canonical node config${refreshed === 1 ? "" : "s"} in .specialists/default/nodes/`);
25063
25292
  }
25064
25293
  }
25065
25294
  function installProjectHooks(cwd) {
@@ -25086,7 +25315,7 @@ function installProjectHooks(cwd) {
25086
25315
  for (const file of hooks) {
25087
25316
  const src = join10(sourceDir, file);
25088
25317
  const xtrmDest = join10(targetDir, file);
25089
- if (existsSync9(xtrmDest)) {
25318
+ if (existsSync10(xtrmDest)) {
25090
25319
  skippedCopies++;
25091
25320
  } else {
25092
25321
  copyFileSync(src, xtrmDest);
@@ -25094,7 +25323,7 @@ function installProjectHooks(cwd) {
25094
25323
  }
25095
25324
  const claudeHookPath = join10(claudeHooksDir, file);
25096
25325
  const relativeTarget = `../../.xtrm/hooks/specialists/${file}`;
25097
- if (existsSync9(claudeHookPath)) {
25326
+ if (existsSync10(claudeHookPath)) {
25098
25327
  const stats = lstatSync(claudeHookPath);
25099
25328
  if (!stats.isSymbolicLink()) {
25100
25329
  unlinkSync(claudeHookPath);
@@ -25102,7 +25331,7 @@ function installProjectHooks(cwd) {
25102
25331
  rewiredLinks++;
25103
25332
  continue;
25104
25333
  }
25105
- const currentTarget = resolve4(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
25334
+ const currentTarget = resolve5(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
25106
25335
  if (currentTarget !== xtrmDest) {
25107
25336
  unlinkSync(claudeHookPath);
25108
25337
  symlinkSync(relativeTarget, claudeHookPath);
@@ -25129,7 +25358,7 @@ function installProjectHooks(cwd) {
25129
25358
  function ensureProjectHookWiring(cwd) {
25130
25359
  const settingsPath = join10(cwd, ".claude", "settings.json");
25131
25360
  const settingsDir = join10(cwd, ".claude");
25132
- if (!existsSync9(settingsDir)) {
25361
+ if (!existsSync10(settingsDir)) {
25133
25362
  mkdirSync4(settingsDir, { recursive: true });
25134
25363
  }
25135
25364
  const settings = loadJson(settingsPath, {});
@@ -25165,7 +25394,7 @@ function ensureProjectHookWiring(cwd) {
25165
25394
  }
25166
25395
  }
25167
25396
  function ensureRootSymlink(rootPath, expectedTargetPath) {
25168
- if (!existsSync9(rootPath)) {
25397
+ if (!existsSync10(rootPath)) {
25169
25398
  mkdirSync4(dirname5(rootPath), { recursive: true });
25170
25399
  const relTarget = relative(dirname5(rootPath), expectedTargetPath);
25171
25400
  symlinkSync(relTarget, rootPath);
@@ -25177,14 +25406,14 @@ function ensureRootSymlink(rootPath, expectedTargetPath) {
25177
25406
  throw new Error(`${rootPath} must be a symlink to ${expectedTargetPath}. Aborting.`);
25178
25407
  }
25179
25408
  const linkTarget = readlinkSync(rootPath);
25180
- const resolvedTarget = resolve4(dirname5(rootPath), linkTarget);
25181
- const resolvedExpected = resolve4(expectedTargetPath);
25409
+ const resolvedTarget = resolve5(dirname5(rootPath), linkTarget);
25410
+ const resolvedExpected = resolve5(expectedTargetPath);
25182
25411
  if (resolvedTarget === resolvedExpected) {
25183
25412
  return;
25184
25413
  }
25185
25414
  const legacyTargets = [
25186
- resolve4(expectedTargetPath, "claude"),
25187
- resolve4(expectedTargetPath, "pi")
25415
+ resolve5(expectedTargetPath, "claude"),
25416
+ resolve5(expectedTargetPath, "pi")
25188
25417
  ];
25189
25418
  if (legacyTargets.includes(resolvedTarget)) {
25190
25419
  unlinkSync(rootPath);
@@ -25211,14 +25440,14 @@ function ensureActiveSkillSymlink(defaultSkillPath, activeLinkPath) {
25211
25440
  if (!stats.isSymbolicLink()) {
25212
25441
  throw new Error(`${activeLinkPath} already exists and is not a symlink.`);
25213
25442
  }
25214
- const currentTarget = resolve4(dirname5(activeLinkPath), readlinkSync(activeLinkPath));
25215
- if (currentTarget !== resolve4(defaultSkillPath)) {
25443
+ const currentTarget = resolve5(dirname5(activeLinkPath), readlinkSync(activeLinkPath));
25444
+ if (currentTarget !== resolve5(defaultSkillPath)) {
25216
25445
  throw new Error(`${activeLinkPath} points to an unexpected target.`);
25217
25446
  }
25218
25447
  }
25219
25448
  function installProjectSkills(cwd, syncSkills) {
25220
25449
  const xtrmRoot = join10(cwd, ".xtrm");
25221
- if (!existsSync9(xtrmRoot)) {
25450
+ if (!existsSync10(xtrmRoot)) {
25222
25451
  throw new Error(".xtrm/ is missing. Install xtrm first, then run specialists init.");
25223
25452
  }
25224
25453
  const sourceDir = resolvePackagePath("skills");
@@ -25242,7 +25471,7 @@ function installProjectSkills(cwd, syncSkills) {
25242
25471
  for (const skill of skills) {
25243
25472
  const src = join10(sourceDir, skill);
25244
25473
  const defaultSkillPath = join10(defaultRoot, skill);
25245
- if (existsSync9(defaultSkillPath)) {
25474
+ if (existsSync10(defaultSkillPath)) {
25246
25475
  if (syncSkills) {
25247
25476
  cpSync(src, defaultSkillPath, { recursive: true, force: true });
25248
25477
  refreshed++;
@@ -25264,7 +25493,7 @@ function createSpecialistsDirs(cwd) {
25264
25493
  const userDir = join10(cwd, ".specialists", "user");
25265
25494
  let created = 0;
25266
25495
  for (const dir of [defaultDir, userDir]) {
25267
- if (!existsSync9(dir)) {
25496
+ if (!existsSync10(dir)) {
25268
25497
  mkdirSync4(dir, { recursive: true });
25269
25498
  created++;
25270
25499
  }
@@ -25280,7 +25509,7 @@ function createRuntimeDirs(cwd) {
25280
25509
  ];
25281
25510
  let created = 0;
25282
25511
  for (const dir of runtimeDirs) {
25283
- if (!existsSync9(dir)) {
25512
+ if (!existsSync10(dir)) {
25284
25513
  mkdirSync4(dir, { recursive: true });
25285
25514
  created++;
25286
25515
  }
@@ -25304,7 +25533,7 @@ function ensureProjectMcp(cwd) {
25304
25533
  }
25305
25534
  function ensureGitignore(cwd) {
25306
25535
  const gitignorePath = join10(cwd, ".gitignore");
25307
- const existing = existsSync9(gitignorePath) ? readFileSync7(gitignorePath, "utf-8") : "";
25536
+ const existing = existsSync10(gitignorePath) ? readFileSync8(gitignorePath, "utf-8") : "";
25308
25537
  let added = 0;
25309
25538
  const lines = existing.split(`
25310
25539
  `);
@@ -25329,7 +25558,7 @@ function ensureObservabilityDb(cwd) {
25329
25558
  skip("observability DB path resolves inside jobs directory \u2014 skipped");
25330
25559
  return;
25331
25560
  }
25332
- const alreadyExists = existsSync9(location.dbPath);
25561
+ const alreadyExists = existsSync10(location.dbPath);
25333
25562
  if (alreadyExists) {
25334
25563
  skip("observability database already exists (not touched)");
25335
25564
  return;
@@ -25350,8 +25579,8 @@ function ensureObservabilityDb(cwd) {
25350
25579
  }
25351
25580
  function ensureAgentsMd(cwd) {
25352
25581
  const agentsPath = join10(cwd, "AGENTS.md");
25353
- if (existsSync9(agentsPath)) {
25354
- const existing = readFileSync7(agentsPath, "utf-8");
25582
+ if (existsSync10(agentsPath)) {
25583
+ const existing = readFileSync8(agentsPath, "utf-8");
25355
25584
  if (existing.includes(AGENTS_MARKER)) {
25356
25585
  skip("AGENTS.md already has Specialists section");
25357
25586
  } else {
@@ -25366,10 +25595,10 @@ function ensureAgentsMd(cwd) {
25366
25595
  }
25367
25596
  }
25368
25597
  function readJsonObject(path) {
25369
- if (!existsSync9(path))
25598
+ if (!existsSync10(path))
25370
25599
  return {};
25371
25600
  try {
25372
- return JSON.parse(readFileSync7(path, "utf-8"));
25601
+ return JSON.parse(readFileSync8(path, "utf-8"));
25373
25602
  } catch {
25374
25603
  return {};
25375
25604
  }
@@ -25397,14 +25626,14 @@ function hasHookCommand(settings, eventName, command) {
25397
25626
  function validateInitPostconditions(cwd) {
25398
25627
  const warnings = [];
25399
25628
  const xtrmHooksDir = join10(cwd, ".xtrm", "hooks", "specialists");
25400
- const xtrmHookFiles = existsSync9(xtrmHooksDir) ? readdirSync3(xtrmHooksDir).filter((file) => file.endsWith(".mjs")) : [];
25629
+ const xtrmHookFiles = existsSync10(xtrmHooksDir) ? readdirSync3(xtrmHooksDir).filter((file) => file.endsWith(".mjs")) : [];
25401
25630
  if (xtrmHookFiles.length === 0) {
25402
25631
  warnings.push(".xtrm/hooks/specialists/ is missing or has no .mjs hooks");
25403
25632
  }
25404
25633
  const claudeHooksDir = join10(cwd, ".claude", "hooks");
25405
25634
  for (const hookFile of xtrmHookFiles) {
25406
25635
  const claudeHookPath = join10(claudeHooksDir, hookFile);
25407
- if (!existsSync9(claudeHookPath)) {
25636
+ if (!existsSync10(claudeHookPath)) {
25408
25637
  warnings.push(`.claude/hooks/${hookFile} is missing`);
25409
25638
  continue;
25410
25639
  }
@@ -25413,8 +25642,8 @@ function validateInitPostconditions(cwd) {
25413
25642
  warnings.push(`.claude/hooks/${hookFile} is not a symlink`);
25414
25643
  continue;
25415
25644
  }
25416
- const expectedTarget = resolve4(xtrmHooksDir, hookFile);
25417
- const resolvedTarget = resolve4(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
25645
+ const expectedTarget = resolve5(xtrmHooksDir, hookFile);
25646
+ const resolvedTarget = resolve5(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
25418
25647
  if (resolvedTarget !== expectedTarget) {
25419
25648
  warnings.push(`.claude/hooks/${hookFile} points to unexpected target`);
25420
25649
  }
@@ -25439,12 +25668,12 @@ function validateInitPostconditions(cwd) {
25439
25668
  }
25440
25669
  const runtimeDirs = [join10(cwd, ".specialists", "jobs"), join10(cwd, ".specialists", "ready")];
25441
25670
  for (const runtimeDir of runtimeDirs) {
25442
- if (!existsSync9(runtimeDir)) {
25671
+ if (!existsSync10(runtimeDir)) {
25443
25672
  warnings.push(`${relative(cwd, runtimeDir)} is missing`);
25444
25673
  }
25445
25674
  }
25446
25675
  const defaultSkillsRoot = join10(cwd, ".xtrm", "skills", "default");
25447
- const defaultSkills = existsSync9(defaultSkillsRoot) ? readdirSync3(defaultSkillsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()) : [];
25676
+ const defaultSkills = existsSync10(defaultSkillsRoot) ? readdirSync3(defaultSkillsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()) : [];
25448
25677
  if (defaultSkills.length === 0) {
25449
25678
  warnings.push(".xtrm/skills/default/ is missing or has no skill directories");
25450
25679
  }
@@ -25459,7 +25688,7 @@ function validateInitPostconditions(cwd) {
25459
25688
  }
25460
25689
  ];
25461
25690
  for (const symlink of rootSymlinks) {
25462
- if (!existsSync9(symlink.linkPath)) {
25691
+ if (!existsSync10(symlink.linkPath)) {
25463
25692
  warnings.push(`${relative(cwd, symlink.linkPath)} is missing`);
25464
25693
  continue;
25465
25694
  }
@@ -25468,8 +25697,8 @@ function validateInitPostconditions(cwd) {
25468
25697
  warnings.push(`${relative(cwd, symlink.linkPath)} is not a symlink`);
25469
25698
  continue;
25470
25699
  }
25471
- const resolvedTarget = resolve4(dirname5(symlink.linkPath), readlinkSync(symlink.linkPath));
25472
- if (resolvedTarget !== resolve4(symlink.expectedTarget)) {
25700
+ const resolvedTarget = resolve5(dirname5(symlink.linkPath), readlinkSync(symlink.linkPath));
25701
+ if (resolvedTarget !== resolve5(symlink.expectedTarget)) {
25473
25702
  warnings.push(`${relative(cwd, symlink.linkPath)} points to an unexpected target`);
25474
25703
  }
25475
25704
  }
@@ -25643,8 +25872,8 @@ var exports_db = {};
25643
25872
  __export(exports_db, {
25644
25873
  run: () => run8
25645
25874
  });
25646
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
25647
- import { dirname as dirname6, join as join11, resolve as resolve5 } from "path";
25875
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
25876
+ import { dirname as dirname6, join as join11, resolve as resolve6 } from "path";
25648
25877
  function formatBytes(bytes) {
25649
25878
  if (bytes < 1024)
25650
25879
  return `${bytes} B`;
@@ -25793,7 +26022,7 @@ function parsePruneOptions(argv) {
25793
26022
  }
25794
26023
  function parseStatusFile(jobDirectoryPath, fallbackJobId) {
25795
26024
  const statusPath = join11(jobDirectoryPath, "status.json");
25796
- const statusRaw = readFileSync8(statusPath, "utf-8");
26025
+ const statusRaw = readFileSync9(statusPath, "utf-8");
25797
26026
  const parsed = JSON.parse(statusRaw);
25798
26027
  const jobId = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : fallbackJobId;
25799
26028
  const specialist = typeof parsed.specialist === "string" && parsed.specialist.length > 0 ? parsed.specialist : "unknown";
@@ -25808,9 +26037,9 @@ function parseStatusFile(jobDirectoryPath, fallbackJobId) {
25808
26037
  };
25809
26038
  }
25810
26039
  function replayEvents(eventsPath, sqliteClient, status) {
25811
- if (!existsSync10(eventsPath))
26040
+ if (!existsSync11(eventsPath))
25812
26041
  return 0;
25813
- const rawContent = readFileSync8(eventsPath, "utf-8");
26042
+ const rawContent = readFileSync9(eventsPath, "utf-8");
25814
26043
  const lines = rawContent.split(`
25815
26044
  `).map((line) => line.trim()).filter((line) => line.length > 0);
25816
26045
  let importedEvents = 0;
@@ -25836,7 +26065,7 @@ function runBackfill(options) {
25836
26065
  };
25837
26066
  try {
25838
26067
  const jobsDirectoryPath = resolveJobsDir(process.cwd());
25839
- if (!existsSync10(jobsDirectoryPath)) {
26068
+ if (!existsSync11(jobsDirectoryPath)) {
25840
26069
  console.log("No jobs directory found. Nothing to backfill.");
25841
26070
  return;
25842
26071
  }
@@ -25846,7 +26075,7 @@ function runBackfill(options) {
25846
26075
  continue;
25847
26076
  const jobDirectoryPath = join11(jobsDirectoryPath, jobEntry.name);
25848
26077
  const statusPath = join11(jobDirectoryPath, "status.json");
25849
- if (!existsSync10(statusPath))
26078
+ if (!existsSync11(statusPath))
25850
26079
  continue;
25851
26080
  try {
25852
26081
  const status = parseStatusFile(jobDirectoryPath, jobEntry.name);
@@ -25959,14 +26188,14 @@ ${bold7("specialists db prune")}
25959
26188
  }
25960
26189
  }
25961
26190
  function parseBenchmarkExportOptions(argv) {
25962
- const defaultOutput = resolve5(process.cwd(), ".specialists/benchmarks/executor-benchmark-rows.jsonl");
26191
+ const defaultOutput = resolve6(process.cwd(), ".specialists/benchmarks/executor-benchmark-rows.jsonl");
25963
26192
  let outputPath = defaultOutput;
25964
26193
  let epicId;
25965
26194
  let includePrepJobs = false;
25966
26195
  for (let i = 0;i < argv.length; i += 1) {
25967
26196
  const argument = argv[i];
25968
26197
  if (argument === "--output" && argv[i + 1]) {
25969
- outputPath = resolve5(process.cwd(), argv[i + 1]);
26198
+ outputPath = resolve6(process.cwd(), argv[i + 1]);
25970
26199
  i += 1;
25971
26200
  continue;
25972
26201
  }
@@ -26199,7 +26428,7 @@ __export(exports_validate, {
26199
26428
  ArgParseError: () => ArgParseError3
26200
26429
  });
26201
26430
  import { readFile as readFile3 } from "fs/promises";
26202
- import { existsSync as existsSync11 } from "fs";
26431
+ import { existsSync as existsSync12 } from "fs";
26203
26432
  import { join as join12 } from "path";
26204
26433
  function parseArgs4(argv) {
26205
26434
  const name = argv[0];
@@ -26219,11 +26448,11 @@ function findSpecialistFile(name) {
26219
26448
  ];
26220
26449
  for (const dir of scanDirs) {
26221
26450
  const jsonCandidate = join12(dir, `${name}.specialist.json`);
26222
- if (existsSync11(jsonCandidate)) {
26451
+ if (existsSync12(jsonCandidate)) {
26223
26452
  return jsonCandidate;
26224
26453
  }
26225
26454
  const yamlCandidate = join12(dir, `${name}.specialist.json`);
26226
- if (existsSync11(yamlCandidate)) {
26455
+ if (existsSync12(yamlCandidate)) {
26227
26456
  process.stderr.write(`[specialists] DEPRECATED: YAML specialist config detected at ${yamlCandidate}. Please migrate to .specialist.json
26228
26457
  `);
26229
26458
  return yamlCandidate;
@@ -26318,7 +26547,7 @@ __export(exports_edit, {
26318
26547
  run: () => run10
26319
26548
  });
26320
26549
  import { spawnSync as spawnSync10 } from "child_process";
26321
- import { existsSync as existsSync12, readdirSync as readdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
26550
+ import { existsSync as existsSync13, readdirSync as readdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
26322
26551
  import { join as join13 } from "path";
26323
26552
  function loadPresets() {
26324
26553
  const paths = [
@@ -26326,9 +26555,9 @@ function loadPresets() {
26326
26555
  join13(process.cwd(), "config", "specialists", "presets.json")
26327
26556
  ];
26328
26557
  for (const p of paths) {
26329
- if (existsSync12(p)) {
26558
+ if (existsSync13(p)) {
26330
26559
  try {
26331
- const data = JSON.parse(readFileSync9(p, "utf-8"));
26560
+ const data = JSON.parse(readFileSync10(p, "utf-8"));
26332
26561
  return data;
26333
26562
  } catch {
26334
26563
  return {};
@@ -26526,7 +26755,7 @@ ${usage()}`);
26526
26755
  if (action === "get" && (pendingArrayOp || filePath)) {
26527
26756
  fail("Error: --get cannot be combined with --append/--remove/--file");
26528
26757
  }
26529
- if (filePath && !existsSync12(filePath)) {
26758
+ if (filePath && !existsSync13(filePath)) {
26530
26759
  fail(`Error: file not found: ${filePath}`);
26531
26760
  }
26532
26761
  return { name, all, scope, dryRun, action, path, value, filePath, preset };
@@ -26661,7 +26890,7 @@ function formatOutputValue(value) {
26661
26890
  }
26662
26891
  function openAllConfigSpecialistsInEditor() {
26663
26892
  const configDir = join13(process.cwd(), "config", "specialists");
26664
- if (!existsSync12(configDir)) {
26893
+ if (!existsSync13(configDir)) {
26665
26894
  fail(`Error: missing directory: ${configDir}`);
26666
26895
  }
26667
26896
  const files = readdirSync5(configDir).filter((file) => file.endsWith(".specialist.json")).sort().map((file) => join13(configDir, file));
@@ -26681,7 +26910,7 @@ function getRawValue(args, resolvedPath) {
26681
26910
  if (!MULTILINE_FILE_PATHS.has(resolvedPath.normalizedPath)) {
26682
26911
  fail(`Error: --file is only supported for: ${Array.from(MULTILINE_FILE_PATHS).join(", ")}`);
26683
26912
  }
26684
- return readFileSync9(args.filePath, "utf-8");
26913
+ return readFileSync10(args.filePath, "utf-8");
26685
26914
  }
26686
26915
  function getAtPath(root, segments) {
26687
26916
  let current = root;
@@ -26791,7 +27020,7 @@ async function run10() {
26791
27020
  }
26792
27021
  const targets2 = await resolveTargets(args);
26793
27022
  for (const target of targets2) {
26794
- const raw = readFileSync9(target.filePath, "utf-8");
27023
+ const raw = readFileSync10(target.filePath, "utf-8");
26795
27024
  const doc2 = JSON.parse(raw);
26796
27025
  for (const [fieldPath, fieldValue] of Object.entries(preset.fields)) {
26797
27026
  const resolved = resolvePath2(fieldPath);
@@ -26817,7 +27046,7 @@ async function run10() {
26817
27046
  fail("Error: no specialists found");
26818
27047
  }
26819
27048
  for (const target of targets) {
26820
- const raw = readFileSync9(target.filePath, "utf-8");
27049
+ const raw = readFileSync10(target.filePath, "utf-8");
26821
27050
  let doc2;
26822
27051
  try {
26823
27052
  const parsed = JSON.parse(raw);
@@ -26960,8 +27189,8 @@ var init_config = __esm(() => {
26960
27189
  });
26961
27190
 
26962
27191
  // src/specialist/worktree.ts
26963
- import { existsSync as existsSync13, symlinkSync as symlinkSync2, mkdirSync as mkdirSync6 } from "fs";
26964
- import { join as join14, resolve as resolve6 } from "path";
27192
+ import { existsSync as existsSync14, symlinkSync as symlinkSync2, mkdirSync as mkdirSync6 } from "fs";
27193
+ import { join as join14, resolve as resolve7 } from "path";
26965
27194
  import { spawnSync as spawnSync11, execFileSync as execFileSync2 } from "child_process";
26966
27195
  function deriveBranchName(beadId, specialistName) {
26967
27196
  return `feature/${beadId}-${slugify(specialistName)}`;
@@ -26994,11 +27223,11 @@ function provisionWorktree(options) {
26994
27223
  const branch = deriveBranchName(options.beadId, options.specialistName);
26995
27224
  const existingPath = findExistingWorktree(branch, cwd);
26996
27225
  if (existingPath) {
26997
- return { branch, worktreePath: resolve6(existingPath), reused: true };
27226
+ return { branch, worktreePath: resolve7(existingPath), reused: true };
26998
27227
  }
26999
27228
  const worktreeBase = options.worktreeBase ?? join14(commonRoot, ".worktrees", options.beadId);
27000
27229
  const worktreeName = deriveWorktreeName(options.beadId, options.specialistName);
27001
- const worktreePath = resolve6(join14(worktreeBase, worktreeName));
27230
+ const worktreePath = resolve7(join14(worktreeBase, worktreeName));
27002
27231
  createWorktreeViaBd(worktreePath, branch, commonRoot);
27003
27232
  symlinkPiNpmCache(commonRoot, worktreePath);
27004
27233
  return { branch, worktreePath, reused: false };
@@ -27006,7 +27235,7 @@ function provisionWorktree(options) {
27006
27235
  function symlinkPiNpmCache(commonRoot, worktreePath) {
27007
27236
  const source = join14(commonRoot, ".pi", "npm");
27008
27237
  const target = join14(worktreePath, ".pi", "npm");
27009
- if (!existsSync13(source) || existsSync13(target))
27238
+ if (!existsSync14(source) || existsSync14(target))
27010
27239
  return;
27011
27240
  try {
27012
27241
  mkdirSync6(join14(worktreePath, ".pi"), { recursive: true });
@@ -27072,7 +27301,7 @@ __export(exports_merge, {
27072
27301
  checkEpicUnresolvedGuard: () => checkEpicUnresolvedGuard,
27073
27302
  assertMainRepoCleanForMerge: () => assertMainRepoCleanForMerge
27074
27303
  });
27075
- import { existsSync as existsSync14, readdirSync as readdirSync6, readFileSync as readFileSync10 } from "fs";
27304
+ import { existsSync as existsSync15, readdirSync as readdirSync6, readFileSync as readFileSync11 } from "fs";
27076
27305
  import { spawnSync as spawnSync12 } from "child_process";
27077
27306
  import { join as join15 } from "path";
27078
27307
  function parseOptions(argv) {
@@ -27252,7 +27481,7 @@ Use 'sp epic merge ${membership.epicId}' to publish all chains together, or 'sp
27252
27481
  }
27253
27482
  function readAllJobStatuses() {
27254
27483
  const jobsDir = resolveJobsDir();
27255
- if (!existsSync14(jobsDir))
27484
+ if (!existsSync15(jobsDir))
27256
27485
  return [];
27257
27486
  const entries = readdirSync6(jobsDir, { withFileTypes: true });
27258
27487
  const statuses = [];
@@ -27260,9 +27489,9 @@ function readAllJobStatuses() {
27260
27489
  if (!entry.isDirectory())
27261
27490
  continue;
27262
27491
  const statusPath = join15(jobsDir, entry.name, "status.json");
27263
- if (!existsSync14(statusPath))
27492
+ if (!existsSync15(statusPath))
27264
27493
  continue;
27265
- const parsed = readJson(readFileSync10(statusPath, "utf-8"));
27494
+ const parsed = readJson(readFileSync11(statusPath, "utf-8"));
27266
27495
  if (!parsed || typeof parsed !== "object")
27267
27496
  continue;
27268
27497
  statuses.push(parsed);
@@ -27761,7 +27990,9 @@ function formatToolDetail(event) {
27761
27990
  return `${toolName}: ${dim8("start")}`;
27762
27991
  }
27763
27992
  if (event.phase === "end" && event.is_error) {
27764
- return `${toolName}: ${red2("error")}`;
27993
+ const summary = event.result_summary?.split(`
27994
+ `)[0]?.trim().slice(0, 120);
27995
+ return summary ? `${toolName}: ${red2(summary)}` : `${toolName}: ${red2("error")}`;
27765
27996
  }
27766
27997
  return `${toolName}: ${dim8(event.phase)}`;
27767
27998
  }
@@ -27779,6 +28010,8 @@ function formatEventLine(event, options) {
27779
28010
  if (event.type === "meta") {
27780
28011
  detailParts.push(`model=${event.model}`);
27781
28012
  detailParts.push(`backend=${event.backend}`);
28013
+ if (event.source)
28014
+ detailParts.push(`source=${event.source}`);
27782
28015
  } else if (event.type === "tool") {
27783
28016
  detail = formatToolDetail(event);
27784
28017
  } else if (event.type === "error") {
@@ -27918,7 +28151,7 @@ __export(exports_run, {
27918
28151
  run: () => run13
27919
28152
  });
27920
28153
  import { join as join16 } from "path";
27921
- import { readFileSync as readFileSync11 } from "fs";
28154
+ import { readFileSync as readFileSync12 } from "fs";
27922
28155
  import { randomBytes } from "crypto";
27923
28156
  import { spawn as cpSpawn, execSync as execSync3 } from "child_process";
27924
28157
  async function parseArgs6(argv) {
@@ -28033,13 +28266,13 @@ async function parseArgs6(argv) {
28033
28266
  process.exit(1);
28034
28267
  }
28035
28268
  if (!prompt && !beadId && !process.stdin.isTTY) {
28036
- prompt = await new Promise((resolve7) => {
28269
+ prompt = await new Promise((resolve8) => {
28037
28270
  let buf = "";
28038
28271
  process.stdin.setEncoding("utf-8");
28039
28272
  process.stdin.on("data", (chunk) => {
28040
28273
  buf += chunk;
28041
28274
  });
28042
- process.stdin.on("end", () => resolve7(buf.trim()));
28275
+ process.stdin.on("end", () => resolve8(buf.trim()));
28043
28276
  });
28044
28277
  }
28045
28278
  if (!prompt && !beadId && !reuseJobId) {
@@ -28197,7 +28430,7 @@ function startEventTailer(jobId, jobsDir, mode, specialist, beadId) {
28197
28430
  const drain = () => {
28198
28431
  let content;
28199
28432
  try {
28200
- content = readFileSync11(eventsPath, "utf-8");
28433
+ content = readFileSync12(eventsPath, "utf-8");
28201
28434
  } catch {
28202
28435
  return;
28203
28436
  }
@@ -28298,7 +28531,7 @@ async function run13() {
28298
28531
  const latestPath = join16(jobsDir2, "latest");
28299
28532
  const oldLatest = (() => {
28300
28533
  try {
28301
- return readFileSync11(latestPath, "utf-8").trim();
28534
+ return readFileSync12(latestPath, "utf-8").trim();
28302
28535
  } catch {
28303
28536
  return "";
28304
28537
  }
@@ -28326,7 +28559,7 @@ async function run13() {
28326
28559
  while (Date.now() < deadline) {
28327
28560
  await new Promise((r) => setTimeout(r, 100));
28328
28561
  try {
28329
- const current = readFileSync11(latestPath, "utf-8").trim();
28562
+ const current = readFileSync12(latestPath, "utf-8").trim();
28330
28563
  if (current && current !== oldLatest) {
28331
28564
  jobId2 = current;
28332
28565
  break;
@@ -28581,7 +28814,7 @@ var init_node_resolve = __esm(() => {
28581
28814
  });
28582
28815
 
28583
28816
  // src/specialist/job-control.ts
28584
- import { existsSync as existsSync15, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
28817
+ import { existsSync as existsSync16, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
28585
28818
  import { join as join17 } from "path";
28586
28819
 
28587
28820
  class JobControl {
@@ -28613,8 +28846,8 @@ class JobControl {
28613
28846
  }
28614
28847
  };
28615
28848
  let resolveJobId;
28616
- const jobIdPromise = new Promise((resolve7) => {
28617
- resolveJobId = resolve7;
28849
+ const jobIdPromise = new Promise((resolve8) => {
28850
+ resolveJobId = resolve8;
28618
28851
  });
28619
28852
  this.supervisor = new Supervisor({
28620
28853
  runner: this.runner,
@@ -28657,10 +28890,10 @@ class JobControl {
28657
28890
  return sqliteResult;
28658
28891
  } catch {}
28659
28892
  const resultPath = this.resultPath(jobId);
28660
- if (!existsSync15(resultPath))
28893
+ if (!existsSync16(resultPath))
28661
28894
  return null;
28662
28895
  try {
28663
- return readFileSync12(resultPath, "utf-8");
28896
+ return readFileSync13(resultPath, "utf-8");
28664
28897
  } catch {
28665
28898
  return null;
28666
28899
  }
@@ -28679,7 +28912,7 @@ class JobControl {
28679
28912
  if (deadline !== undefined && Date.now() >= deadline) {
28680
28913
  throw new Error(`Timed out waiting for terminal status for job ${jobId}`);
28681
28914
  }
28682
- await new Promise((resolve7) => setTimeout(resolve7, backoffMs));
28915
+ await new Promise((resolve8) => setTimeout(resolve8, backoffMs));
28683
28916
  backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
28684
28917
  }
28685
28918
  }
@@ -28858,7 +29091,7 @@ function hashOutput(output2, salt) {
28858
29091
  return createHash3("sha256").update(value).digest("hex");
28859
29092
  }
28860
29093
  function sleep2(ms) {
28861
- return new Promise((resolve7) => setTimeout(resolve7, ms));
29094
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
28862
29095
  }
28863
29096
  function toContextHealth(contextPct) {
28864
29097
  if (contextPct === null)
@@ -30771,10 +31004,10 @@ var exports_node = {};
30771
31004
  __export(exports_node, {
30772
31005
  handleNodeCommand: () => handleNodeCommand
30773
31006
  });
30774
- import { existsSync as existsSync16, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
31007
+ import { existsSync as existsSync17, readFileSync as readFileSync14, readdirSync as readdirSync7 } from "fs";
30775
31008
  import { randomUUID } from "crypto";
30776
31009
  import { spawnSync as spawnSync14 } from "child_process";
30777
- import { basename as basename4, join as join18, resolve as resolve7 } from "path";
31010
+ import { basename as basename4, join as join18, resolve as resolve8 } from "path";
30778
31011
  function parseNodeArgs(argv) {
30779
31012
  const command = argv[0];
30780
31013
  const supportedCommands = new Set(["run", "list", "promote", "members", "memory", "stop", "spawn-member", "create-bead", "complete", "wait-phase"]);
@@ -30958,8 +31191,8 @@ function toNodeName(filePath) {
30958
31191
  function discoverNodeConfigs(cwd) {
30959
31192
  const discoveredByName = new Map;
30960
31193
  for (const directory of NODE_DISCOVERY_DIRS) {
30961
- const absoluteDir = resolve7(cwd, directory.path);
30962
- if (!existsSync16(absoluteDir))
31194
+ const absoluteDir = resolve8(cwd, directory.path);
31195
+ if (!existsSync17(absoluteDir))
30963
31196
  continue;
30964
31197
  const files = readdirSync7(absoluteDir).filter((fileName) => fileName.endsWith(NODE_CONFIG_SUFFIX));
30965
31198
  for (const fileName of files) {
@@ -30973,8 +31206,8 @@ function discoverNodeConfigs(cwd) {
30973
31206
  return [...discoveredByName.values()].sort((left, right) => left.name.localeCompare(right.name));
30974
31207
  }
30975
31208
  function resolveNodeConfigPath(cwd, input2) {
30976
- const explicitPath = resolve7(cwd, input2);
30977
- if (existsSync16(explicitPath)) {
31209
+ const explicitPath = resolve8(cwd, input2);
31210
+ if (existsSync17(explicitPath)) {
30978
31211
  return explicitPath;
30979
31212
  }
30980
31213
  const normalizedName = input2.endsWith(NODE_CONFIG_SUFFIX) ? input2.slice(0, -NODE_CONFIG_SUFFIX.length) : input2;
@@ -31069,7 +31302,7 @@ async function handleNodeRun(args) {
31069
31302
  throw new Error("Observability SQLite DB is unavailable. Run: specialists db setup");
31070
31303
  }
31071
31304
  try {
31072
- const rawConfig = args.inlineJson ? args.inlineJson : readFileSync13(resolveNodeConfigPath(process.cwd(), args.nodeConfigInput), "utf-8");
31305
+ const rawConfig = args.inlineJson ? args.inlineJson : readFileSync14(resolveNodeConfigPath(process.cwd(), args.nodeConfigInput), "utf-8");
31073
31306
  const config2 = parseNodeConfig(rawConfig);
31074
31307
  const loader = new SpecialistLoader;
31075
31308
  const runner = new SpecialistRunner({
@@ -31518,7 +31751,7 @@ async function handleNodeAction(args) {
31518
31751
  if (deadline !== null && Date.now() >= deadline) {
31519
31752
  throw new Error(`Timed out waiting for phase '${args.phaseId}' members: ${memberKeys.join(", ")}`);
31520
31753
  }
31521
- await new Promise((resolve8) => setTimeout(resolve8, 500));
31754
+ await new Promise((resolve9) => setTimeout(resolve9, 500));
31522
31755
  }
31523
31756
  } finally {
31524
31757
  sqliteClient.close();
@@ -31605,7 +31838,7 @@ var init_node = __esm(() => {
31605
31838
  });
31606
31839
 
31607
31840
  // src/specialist/epic-reconciler.ts
31608
- import { mkdirSync as mkdirSync7, openSync as openSync2, readFileSync as readFileSync14, rmSync as rmSync2, writeFileSync as writeFileSync8 } from "fs";
31841
+ import { mkdirSync as mkdirSync7, openSync as openSync2, readFileSync as readFileSync15, rmSync as rmSync2, writeFileSync as writeFileSync8 } from "fs";
31609
31842
  import { join as join19 } from "path";
31610
31843
  function buildEpicLockPath(epicId) {
31611
31844
  const location = resolveObservabilityDbLocation();
@@ -31622,7 +31855,7 @@ function withEpicAdvisoryLock(epicId, action) {
31622
31855
  } catch {
31623
31856
  let holder = "unknown";
31624
31857
  try {
31625
- holder = readFileSync14(lockPath, "utf-8");
31858
+ holder = readFileSync15(lockPath, "utf-8");
31626
31859
  } catch {
31627
31860
  holder = "unknown";
31628
31861
  }
@@ -32598,7 +32831,7 @@ __export(exports_status, {
32598
32831
  run: () => run14
32599
32832
  });
32600
32833
  import { spawnSync as spawnSync16 } from "child_process";
32601
- import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
32834
+ import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
32602
32835
  import { join as join20 } from "path";
32603
32836
  function ok2(msg) {
32604
32837
  console.log(` ${green8("\u2713")} ${msg}`);
@@ -32691,9 +32924,9 @@ function countJobEvents(sqliteClient, jobsDir, jobId) {
32691
32924
  console.warn(`SQLite events read failed for job ${jobId}; falling back to events.jsonl`, error2);
32692
32925
  }
32693
32926
  const eventsFile = join20(jobsDir, jobId, "events.jsonl");
32694
- if (!existsSync17(eventsFile))
32927
+ if (!existsSync18(eventsFile))
32695
32928
  return 0;
32696
- const raw = readFileSync15(eventsFile, "utf-8").trim();
32929
+ const raw = readFileSync16(eventsFile, "utf-8").trim();
32697
32930
  if (!raw)
32698
32931
  return 0;
32699
32932
  return raw.split(`
@@ -32725,9 +32958,9 @@ function getLatestContextSnapshot(sqliteClient, jobsDir, jobId) {
32725
32958
  console.warn(`SQLite events read failed for job ${jobId}; falling back to events.jsonl`, error2);
32726
32959
  }
32727
32960
  const eventsFile = join20(jobsDir, jobId, "events.jsonl");
32728
- if (!existsSync17(eventsFile))
32961
+ if (!existsSync18(eventsFile))
32729
32962
  return null;
32730
- const lines = readFileSync15(eventsFile, "utf-8").split(`
32963
+ const lines = readFileSync16(eventsFile, "utf-8").split(`
32731
32964
  `);
32732
32965
  for (let index = lines.length - 1;index >= 0; index -= 1) {
32733
32966
  const line = lines[index].trim();
@@ -32832,11 +33065,11 @@ async function run14() {
32832
33065
  `).slice(1).map((line) => line.split(/\s+/)[0]).filter(Boolean)) : new Set;
32833
33066
  const bdInstalled = isInstalled2("bd");
32834
33067
  const bdVersion = bdInstalled ? cmd("bd", ["--version"]) : null;
32835
- const beadsPresent = existsSync17(join20(process.cwd(), ".beads"));
33068
+ const beadsPresent = existsSync18(join20(process.cwd(), ".beads"));
32836
33069
  const specialistsBin = cmd("which", ["specialists"]);
32837
33070
  const jobsDir = resolveJobsDir();
32838
33071
  let jobs = [];
32839
- if (existsSync17(jobsDir)) {
33072
+ if (existsSync18(jobsDir)) {
32840
33073
  supervisor = new Supervisor({
32841
33074
  runner: null,
32842
33075
  runOptions: null,
@@ -32993,7 +33226,7 @@ __export(exports_ps, {
32993
33226
  run: () => run15
32994
33227
  });
32995
33228
  import { spawnSync as spawnSync17 } from "child_process";
32996
- import { existsSync as existsSync18, readdirSync as readdirSync8, readFileSync as readFileSync16 } from "fs";
33229
+ import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync17 } from "fs";
32997
33230
  import { join as join21 } from "path";
32998
33231
  function parseArgs7(argv) {
32999
33232
  let nodeId;
@@ -33024,25 +33257,25 @@ function isVisibleStatus(status, all) {
33024
33257
  return ACTIVE_STATES.includes(status);
33025
33258
  }
33026
33259
  function readStatusesFromFiles(jobsDir) {
33027
- if (!existsSync18(jobsDir))
33260
+ if (!existsSync19(jobsDir))
33028
33261
  return [];
33029
33262
  const statuses = [];
33030
33263
  for (const entry of readdirSync8(jobsDir)) {
33031
33264
  const statusPath = join21(jobsDir, entry, "status.json");
33032
- if (!existsSync18(statusPath))
33265
+ if (!existsSync19(statusPath))
33033
33266
  continue;
33034
33267
  try {
33035
- statuses.push(JSON.parse(readFileSync16(statusPath, "utf-8")));
33268
+ statuses.push(JSON.parse(readFileSync17(statusPath, "utf-8")));
33036
33269
  } catch {}
33037
33270
  }
33038
33271
  return statuses.sort((a, b) => b.started_at_ms - a.started_at_ms);
33039
33272
  }
33040
33273
  function readLastToolEventFromFile(jobsDir, jobId) {
33041
33274
  const eventsPath = join21(jobsDir, jobId, "events.jsonl");
33042
- if (!existsSync18(eventsPath))
33275
+ if (!existsSync19(eventsPath))
33043
33276
  return;
33044
33277
  try {
33045
- const lines = readFileSync16(eventsPath, "utf-8").split(`
33278
+ const lines = readFileSync17(eventsPath, "utf-8").split(`
33046
33279
  `);
33047
33280
  for (let index = lines.length - 1;index >= 0; index -= 1) {
33048
33281
  const line = lines[index]?.trim();
@@ -33800,7 +34033,7 @@ var exports_result = {};
33800
34033
  __export(exports_result, {
33801
34034
  run: () => run16
33802
34035
  });
33803
- import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
34036
+ import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
33804
34037
  import { join as join22 } from "path";
33805
34038
  function parseArgs8(argv) {
33806
34039
  let jobId;
@@ -33892,9 +34125,9 @@ function readTimelineEventsForResult(sqliteClient, jobsDir, jobId) {
33892
34125
  } catch {}
33893
34126
  }
33894
34127
  const eventsPath = join22(jobsDir, jobId, "events.jsonl");
33895
- if (!existsSync19(eventsPath))
34128
+ if (!existsSync20(eventsPath))
33896
34129
  return [];
33897
- return readFileSync17(eventsPath, "utf-8").split(`
34130
+ return readFileSync18(eventsPath, "utf-8").split(`
33898
34131
  `).map((line) => line.trim()).filter(Boolean).map((line) => parseTimelineEvent(line)).filter((event) => event !== null);
33899
34132
  }
33900
34133
  function deriveStartupSnapshot(status, events) {
@@ -34035,10 +34268,10 @@ async function run16() {
34035
34268
  } catch (error2) {
34036
34269
  console.warn(`SQLite result read failed for job ${jobId}; falling back to result.txt`, error2);
34037
34270
  }
34038
- if (!existsSync19(resultPath)) {
34271
+ if (!existsSync20(resultPath)) {
34039
34272
  return null;
34040
34273
  }
34041
- return readFileSync17(resultPath, "utf-8");
34274
+ return readFileSync18(resultPath, "utf-8");
34042
34275
  };
34043
34276
  if (args.wait) {
34044
34277
  const startMs = Date.now();
@@ -34212,7 +34445,7 @@ var init_result = __esm(() => {
34212
34445
  });
34213
34446
 
34214
34447
  // src/specialist/timeline-query.ts
34215
- import { existsSync as existsSync20, readdirSync as readdirSync9, readFileSync as readFileSync18 } from "fs";
34448
+ import { existsSync as existsSync21, readdirSync as readdirSync9, readFileSync as readFileSync19 } from "fs";
34216
34449
  import { basename as basename5, join as join23 } from "path";
34217
34450
  function readJobEvents(jobDir) {
34218
34451
  const jobId = basename5(jobDir);
@@ -34224,9 +34457,9 @@ function readJobEvents(jobDir) {
34224
34457
  }
34225
34458
  } catch {}
34226
34459
  const eventsPath = join23(jobDir, "events.jsonl");
34227
- if (!existsSync20(eventsPath))
34460
+ if (!existsSync21(eventsPath))
34228
34461
  return [];
34229
- const content = readFileSync18(eventsPath, "utf-8");
34462
+ const content = readFileSync19(eventsPath, "utf-8");
34230
34463
  const lines = content.split(`
34231
34464
  `).filter(Boolean);
34232
34465
  const events = [];
@@ -34242,7 +34475,7 @@ function readJobEventsById(jobsDir, jobId) {
34242
34475
  return readJobEvents(join23(jobsDir, jobId));
34243
34476
  }
34244
34477
  function readAllJobEvents(jobsDir) {
34245
- if (!existsSync20(jobsDir))
34478
+ if (!existsSync21(jobsDir))
34246
34479
  return [];
34247
34480
  const batches = [];
34248
34481
  const entries = readdirSync9(jobsDir);
@@ -34259,9 +34492,9 @@ function readAllJobEvents(jobsDir) {
34259
34492
  const statusPath = join23(jobDir, "status.json");
34260
34493
  let specialist = "unknown";
34261
34494
  let beadId;
34262
- if (existsSync20(statusPath)) {
34495
+ if (existsSync21(statusPath)) {
34263
34496
  try {
34264
- const status = JSON.parse(readFileSync18(statusPath, "utf-8"));
34497
+ const status = JSON.parse(readFileSync19(statusPath, "utf-8"));
34265
34498
  specialist = status.specialist ?? "unknown";
34266
34499
  beadId = status.bead_id;
34267
34500
  } catch {}
@@ -34358,9 +34591,9 @@ __export(exports_feed, {
34358
34591
  });
34359
34592
  import {
34360
34593
  closeSync as closeSync2,
34361
- existsSync as existsSync21,
34594
+ existsSync as existsSync22,
34362
34595
  openSync as openSync3,
34363
- readFileSync as readFileSync19,
34596
+ readFileSync as readFileSync20,
34364
34597
  readdirSync as readdirSync10,
34365
34598
  statSync as statSync3
34366
34599
  } from "fs";
@@ -34472,6 +34705,10 @@ function formatStartupContextLine(event) {
34472
34705
  parts.push(`skills=${snapshot.skills.count}`);
34473
34706
  return parts.length > 0 ? dim8(` \u21B3 startup ${parts.join(" ")}`) : null;
34474
34707
  }
34708
+ if (event.type === "meta" && event.source === "mandatory_rules_injection" && event.data) {
34709
+ const data = event.data;
34710
+ return dim8(` \u21B3 mandatory_rules sets=${(data.sets_loaded ?? []).join(",") || "none"} rules=${data.rules_count ?? 0} tokens=~${data.token_estimate ?? 0}`);
34711
+ }
34475
34712
  if (event.type === "meta" && event.memory_injection) {
34476
34713
  const mem = event.memory_injection;
34477
34714
  return dim8(` \u21B3 memory static=${mem.static_tokens} dynamic=${mem.memory_tokens} gitnexus=${mem.gitnexus_tokens} total=${mem.total_tokens}`);
@@ -34505,7 +34742,7 @@ function readFileFresh(filePath) {
34505
34742
  let fd = null;
34506
34743
  try {
34507
34744
  fd = openSync3(filePath, "r");
34508
- return readFileSync19(fd, "utf-8");
34745
+ return readFileSync20(fd, "utf-8");
34509
34746
  } catch {
34510
34747
  return null;
34511
34748
  } finally {
@@ -34731,7 +34968,7 @@ function filterMergedEventsByNode(sqliteClient, jobsDir, merged, nodeId) {
34731
34968
  });
34732
34969
  }
34733
34970
  function listMatchingJobIds(sqliteClient, jobsDir, options) {
34734
- if (!existsSync21(jobsDir))
34971
+ if (!existsSync22(jobsDir))
34735
34972
  return [];
34736
34973
  const jobIds = [];
34737
34974
  for (const entry of readdirSync10(jobsDir)) {
@@ -34845,7 +35082,7 @@ async function followMerged(sqliteClient, jobsDir, options) {
34845
35082
  }
34846
35083
  const lastPrintedEventKey = new Map;
34847
35084
  const seenMetaKey = new Map;
34848
- await new Promise((resolve8) => {
35085
+ await new Promise((resolve9) => {
34849
35086
  const interval = setInterval(() => {
34850
35087
  const batches = filteredBatches();
34851
35088
  for (const jobId of listMatchingJobIds(sqliteClient, jobsDir, options)) {
@@ -34922,7 +35159,7 @@ async function followMerged(sqliteClient, jobsDir, options) {
34922
35159
  }
34923
35160
  if (!options.forever && trackedJobs.size > 0 && completedJobs.size === trackedJobs.size) {
34924
35161
  clearInterval(interval);
34925
- resolve8();
35162
+ resolve9();
34926
35163
  }
34927
35164
  }, 500);
34928
35165
  });
@@ -34932,7 +35169,7 @@ async function run17() {
34932
35169
  const sqliteClient = createObservabilitySqliteClient();
34933
35170
  try {
34934
35171
  const jobsDir = join24(process.cwd(), ".specialists", "jobs");
34935
- if (!existsSync21(jobsDir)) {
35172
+ if (!existsSync22(jobsDir)) {
34936
35173
  console.log(dim8("No jobs directory found."));
34937
35174
  return;
34938
35175
  }
@@ -34971,7 +35208,7 @@ var exports_poll = {};
34971
35208
  __export(exports_poll, {
34972
35209
  run: () => run18
34973
35210
  });
34974
- import { existsSync as existsSync22, readFileSync as readFileSync20 } from "fs";
35211
+ import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
34975
35212
  import { join as join25 } from "path";
34976
35213
  function parseArgs10(argv) {
34977
35214
  let jobId;
@@ -35012,16 +35249,16 @@ function readJobState(jobsDir, jobId, cursor, outputCursor) {
35012
35249
  const jobDir = join25(jobsDir, jobId);
35013
35250
  const statusPath = join25(jobDir, "status.json");
35014
35251
  let status = null;
35015
- if (existsSync22(statusPath)) {
35252
+ if (existsSync23(statusPath)) {
35016
35253
  try {
35017
- status = JSON.parse(readFileSync20(statusPath, "utf-8"));
35254
+ status = JSON.parse(readFileSync21(statusPath, "utf-8"));
35018
35255
  } catch {}
35019
35256
  }
35020
35257
  const resultPath = join25(jobDir, "result.txt");
35021
35258
  let fullOutput = "";
35022
- if (existsSync22(resultPath)) {
35259
+ if (existsSync23(resultPath)) {
35023
35260
  try {
35024
- fullOutput = readFileSync20(resultPath, "utf-8");
35261
+ fullOutput = readFileSync21(resultPath, "utf-8");
35025
35262
  } catch {}
35026
35263
  }
35027
35264
  const events = readJobEventsById(jobsDir, jobId);
@@ -35055,7 +35292,7 @@ async function run18() {
35055
35292
  const { jobId, cursor, outputCursor } = parseArgs10(process.argv.slice(3));
35056
35293
  const jobsDir = join25(process.cwd(), ".specialists", "jobs");
35057
35294
  const jobDir = join25(jobsDir, jobId);
35058
- if (!existsSync22(jobDir)) {
35295
+ if (!existsSync23(jobDir)) {
35059
35296
  const result2 = {
35060
35297
  job_id: jobId,
35061
35298
  status: "error",
@@ -35203,15 +35440,15 @@ async function run21() {
35203
35440
  }
35204
35441
 
35205
35442
  // src/specialist/worktree-gc.ts
35206
- import { existsSync as existsSync23, readdirSync as readdirSync11, readFileSync as readFileSync21 } from "fs";
35443
+ import { existsSync as existsSync24, readdirSync as readdirSync11, readFileSync as readFileSync22 } from "fs";
35207
35444
  import { join as join26 } from "path";
35208
35445
  import { spawnSync as spawnSync18 } from "child_process";
35209
35446
  function readJobStatus2(jobDir) {
35210
35447
  const statusPath = join26(jobDir, "status.json");
35211
- if (!existsSync23(statusPath))
35448
+ if (!existsSync24(statusPath))
35212
35449
  return null;
35213
35450
  try {
35214
- return JSON.parse(readFileSync21(statusPath, "utf-8"));
35451
+ return JSON.parse(readFileSync22(statusPath, "utf-8"));
35215
35452
  } catch {
35216
35453
  return null;
35217
35454
  }
@@ -35223,7 +35460,7 @@ function isActive(status) {
35223
35460
  return ACTIVE_STATUSES.has(status);
35224
35461
  }
35225
35462
  function collectWorktreeGcCandidates(jobsDir) {
35226
- if (!existsSync23(jobsDir))
35463
+ if (!existsSync24(jobsDir))
35227
35464
  return [];
35228
35465
  const candidates = [];
35229
35466
  for (const entry of readdirSync11(jobsDir, { withFileTypes: true })) {
@@ -35239,7 +35476,7 @@ function collectWorktreeGcCandidates(jobsDir) {
35239
35476
  const { worktree_path: worktreePath, branch } = status;
35240
35477
  if (!worktreePath)
35241
35478
  continue;
35242
- if (!existsSync23(worktreePath))
35479
+ if (!existsSync24(worktreePath))
35243
35480
  continue;
35244
35481
  candidates.push({
35245
35482
  jobId: status.id,
@@ -35285,9 +35522,9 @@ __export(exports_clean, {
35285
35522
  run: () => run22
35286
35523
  });
35287
35524
  import {
35288
- existsSync as existsSync24,
35525
+ existsSync as existsSync25,
35289
35526
  readdirSync as readdirSync12,
35290
- readFileSync as readFileSync22,
35527
+ readFileSync as readFileSync23,
35291
35528
  rmSync as rmSync3,
35292
35529
  statSync as statSync4
35293
35530
  } from "fs";
@@ -35379,11 +35616,11 @@ function readCompletedJobDirectory(baseDirectory, entry) {
35379
35616
  if (containsProtectedSqliteArtifact(directoryPath))
35380
35617
  return null;
35381
35618
  const statusFilePath = join27(directoryPath, "status.json");
35382
- if (!existsSync24(statusFilePath))
35619
+ if (!existsSync25(statusFilePath))
35383
35620
  return null;
35384
35621
  let statusData;
35385
35622
  try {
35386
- statusData = JSON.parse(readFileSync22(statusFilePath, "utf-8"));
35623
+ statusData = JSON.parse(readFileSync23(statusFilePath, "utf-8"));
35387
35624
  } catch {
35388
35625
  return null;
35389
35626
  }
@@ -35480,7 +35717,7 @@ async function run22() {
35480
35717
  printUsageAndExit2(message);
35481
35718
  }
35482
35719
  const jobsDirectoryPath = resolveJobsDir();
35483
- if (!existsSync24(jobsDirectoryPath)) {
35720
+ if (!existsSync25(jobsDirectoryPath)) {
35484
35721
  console.log("No jobs directory found.");
35485
35722
  return;
35486
35723
  }
@@ -35665,7 +35902,7 @@ async function waitForProcessExit(pid, timeoutMs) {
35665
35902
  while (Date.now() < deadline) {
35666
35903
  if (!isProcessAlive(pid))
35667
35904
  return true;
35668
- await new Promise((resolve8) => setTimeout(resolve8, 100));
35905
+ await new Promise((resolve9) => setTimeout(resolve9, 100));
35669
35906
  }
35670
35907
  return !isProcessAlive(pid);
35671
35908
  }
@@ -35775,7 +36012,7 @@ __export(exports_attach, {
35775
36012
  run: () => run25
35776
36013
  });
35777
36014
  import { execFileSync as execFileSync3, spawnSync as spawnSync20 } from "child_process";
35778
- import { readFileSync as readFileSync23 } from "fs";
36015
+ import { readFileSync as readFileSync24 } from "fs";
35779
36016
  import { join as join28 } from "path";
35780
36017
  function exitWithError(message) {
35781
36018
  console.error(message);
@@ -35783,7 +36020,7 @@ function exitWithError(message) {
35783
36020
  }
35784
36021
  function readStatus(statusPath, jobId) {
35785
36022
  try {
35786
- return JSON.parse(readFileSync23(statusPath, "utf-8"));
36023
+ return JSON.parse(readFileSync24(statusPath, "utf-8"));
35787
36024
  } catch (error2) {
35788
36025
  if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
35789
36026
  exitWithError(`Job \`${jobId}\` not found. Run \`specialists status\` to see active jobs.`);
@@ -36059,8 +36296,8 @@ __export(exports_doctor, {
36059
36296
  });
36060
36297
  import { createHash as createHash4 } from "crypto";
36061
36298
  import { spawnSync as spawnSync21 } from "child_process";
36062
- import { existsSync as existsSync25, lstatSync as lstatSync2, mkdirSync as mkdirSync8, readdirSync as readdirSync13, readFileSync as readFileSync24, readlinkSync as readlinkSync2, writeFileSync as writeFileSync11 } from "fs";
36063
- import { dirname as dirname7, join as join29, relative as relative2, resolve as resolve8 } from "path";
36299
+ import { existsSync as existsSync26, lstatSync as lstatSync2, mkdirSync as mkdirSync8, readdirSync as readdirSync13, readFileSync as readFileSync25, readlinkSync as readlinkSync2, writeFileSync as writeFileSync11 } from "fs";
36300
+ import { dirname as dirname7, join as join29, relative as relative2, resolve as resolve9 } from "path";
36064
36301
  function ok3(msg) {
36065
36302
  console.log(` ${green14("\u2713")} ${msg}`);
36066
36303
  }
@@ -36089,10 +36326,10 @@ function isInstalled3(bin) {
36089
36326
  return spawnSync21("which", [bin], { encoding: "utf8", timeout: 2000 }).status === 0;
36090
36327
  }
36091
36328
  function loadJson2(path) {
36092
- if (!existsSync25(path))
36329
+ if (!existsSync26(path))
36093
36330
  return null;
36094
36331
  try {
36095
- return JSON.parse(readFileSync24(path, "utf8"));
36332
+ return JSON.parse(readFileSync25(path, "utf8"));
36096
36333
  } catch {
36097
36334
  return null;
36098
36335
  }
@@ -36135,7 +36372,7 @@ function checkBd() {
36135
36372
  return false;
36136
36373
  }
36137
36374
  ok3(`bd installed ${dim13(sp("bd", ["--version"]).stdout || "")}`);
36138
- if (existsSync25(join29(CWD, ".beads")))
36375
+ if (existsSync26(join29(CWD, ".beads")))
36139
36376
  ok3(".beads/ present in project");
36140
36377
  else
36141
36378
  warn3(".beads/ not found in project");
@@ -36156,7 +36393,7 @@ function checkHooks() {
36156
36393
  let allPresent = true;
36157
36394
  for (const name of HOOK_NAMES) {
36158
36395
  const canonicalPath = join29(HOOKS_DIR, name);
36159
- if (!existsSync25(canonicalPath)) {
36396
+ if (!existsSync26(canonicalPath)) {
36160
36397
  fail4(`${relative2(CWD, canonicalPath)} ${red7("missing")}`);
36161
36398
  fix("specialists init");
36162
36399
  allPresent = false;
@@ -36218,7 +36455,7 @@ function checkMCP() {
36218
36455
  }
36219
36456
  function hashFile(path) {
36220
36457
  const hash = createHash4("sha256");
36221
- hash.update(readFileSync24(path));
36458
+ hash.update(readFileSync25(path));
36222
36459
  return hash.digest("hex");
36223
36460
  }
36224
36461
  function collectFileHashes(rootDir) {
@@ -36236,12 +36473,12 @@ function collectFileHashes(rootDir) {
36236
36473
  hashes.set(relPath, hashFile(fullPath));
36237
36474
  }
36238
36475
  };
36239
- if (existsSync25(rootDir))
36476
+ if (existsSync26(rootDir))
36240
36477
  visit2(rootDir);
36241
36478
  return hashes;
36242
36479
  }
36243
36480
  function isSymlinkTo(linkPath, expectedTargetPath) {
36244
- if (!existsSync25(linkPath))
36481
+ if (!existsSync26(linkPath))
36245
36482
  return { ok: false, reason: "missing" };
36246
36483
  let stats;
36247
36484
  try {
@@ -36253,8 +36490,8 @@ function isSymlinkTo(linkPath, expectedTargetPath) {
36253
36490
  return { ok: false, reason: "not-symlink" };
36254
36491
  try {
36255
36492
  const rawTarget = readlinkSync2(linkPath);
36256
- const resolvedTarget = resolve8(dirname7(linkPath), rawTarget);
36257
- const resolvedExpected = resolve8(expectedTargetPath);
36493
+ const resolvedTarget = resolve9(dirname7(linkPath), rawTarget);
36494
+ const resolvedExpected = resolve9(expectedTargetPath);
36258
36495
  if (resolvedTarget !== resolvedExpected) {
36259
36496
  return { ok: false, reason: "wrong-target", target: rawTarget };
36260
36497
  }
@@ -36265,12 +36502,12 @@ function isSymlinkTo(linkPath, expectedTargetPath) {
36265
36502
  }
36266
36503
  function checkSkillDrift() {
36267
36504
  section3("Skill drift (.xtrm skill sync)");
36268
- if (!existsSync25(CONFIG_SKILLS_DIR)) {
36505
+ if (!existsSync26(CONFIG_SKILLS_DIR)) {
36269
36506
  fail4("config/skills/ missing");
36270
36507
  fix("restore config/skills/ from git");
36271
36508
  return false;
36272
36509
  }
36273
- if (!existsSync25(XTRM_DEFAULT_SKILLS_DIR)) {
36510
+ if (!existsSync26(XTRM_DEFAULT_SKILLS_DIR)) {
36274
36511
  fail4(".xtrm/skills/default/ missing");
36275
36512
  fix("specialists init --sync-skills");
36276
36513
  return false;
@@ -36313,7 +36550,7 @@ function checkSkillDrift() {
36313
36550
  let linksOk = true;
36314
36551
  for (const scope of ["claude", "pi"]) {
36315
36552
  const activeRoot = join29(XTRM_ACTIVE_SKILLS_DIR, scope);
36316
- if (!existsSync25(activeRoot)) {
36553
+ if (!existsSync26(activeRoot)) {
36317
36554
  fail4(`${relative2(CWD, activeRoot)}/ missing`);
36318
36555
  fix("specialists init --sync-skills");
36319
36556
  linksOk = false;
@@ -36372,14 +36609,14 @@ function checkRuntimeDirs() {
36372
36609
  const jobsDir = join29(rootDir, "jobs");
36373
36610
  const readyDir = join29(rootDir, "ready");
36374
36611
  let allOk = true;
36375
- if (!existsSync25(rootDir)) {
36612
+ if (!existsSync26(rootDir)) {
36376
36613
  warn3(".specialists/ not found in current project");
36377
36614
  fix("specialists init");
36378
36615
  allOk = false;
36379
36616
  } else {
36380
36617
  ok3(".specialists/ present");
36381
36618
  for (const [subDir, label] of [[jobsDir, "jobs"], [readyDir, "ready"]]) {
36382
- if (!existsSync25(subDir)) {
36619
+ if (!existsSync26(subDir)) {
36383
36620
  warn3(`.specialists/${label}/ missing \u2014 auto-creating`);
36384
36621
  mkdirSync8(subDir, { recursive: true });
36385
36622
  ok3(`.specialists/${label}/ created`);
@@ -36412,7 +36649,7 @@ function compareVersions(left, right) {
36412
36649
  }
36413
36650
  function setStatusError(statusPath) {
36414
36651
  try {
36415
- const raw = readFileSync24(statusPath, "utf8");
36652
+ const raw = readFileSync25(statusPath, "utf8");
36416
36653
  const status = JSON.parse(raw);
36417
36654
  status.status = "error";
36418
36655
  writeFileSync11(statusPath, `${JSON.stringify(status, null, 2)}
@@ -36435,10 +36672,10 @@ function cleanupProcesses(jobsDir, dryRun) {
36435
36672
  };
36436
36673
  for (const jobId of entries) {
36437
36674
  const statusPath = join29(jobsDir, jobId, "status.json");
36438
- if (!existsSync25(statusPath))
36675
+ if (!existsSync26(statusPath))
36439
36676
  continue;
36440
36677
  try {
36441
- const status = JSON.parse(readFileSync24(statusPath, "utf8"));
36678
+ const status = JSON.parse(readFileSync25(statusPath, "utf8"));
36442
36679
  result.total += 1;
36443
36680
  if (status.status !== "running" && status.status !== "starting")
36444
36681
  continue;
@@ -36515,7 +36752,7 @@ ${bold13("specialists doctor orphans")}
36515
36752
  function checkZombieJobs() {
36516
36753
  section3("Background jobs");
36517
36754
  const jobsDir = join29(CWD, ".specialists", "jobs");
36518
- if (!existsSync25(jobsDir)) {
36755
+ if (!existsSync26(jobsDir)) {
36519
36756
  hint("No .specialists/jobs/ \u2014 skipping");
36520
36757
  return true;
36521
36758
  }