@jaggerxtrm/specialists 3.6.19 → 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,7 +25233,7 @@ 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;
@@ -25014,7 +25241,7 @@ function copyCanonicalSpecialists(cwd) {
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)) {
25244
+ if (existsSync10(dest)) {
25018
25245
  copyFileSync(src, dest);
25019
25246
  refreshed++;
25020
25247
  } else {
@@ -25041,7 +25268,7 @@ function copyCanonicalNodeConfigs(cwd) {
25041
25268
  skip("no node config files found in package");
25042
25269
  return;
25043
25270
  }
25044
- if (!existsSync9(targetDir)) {
25271
+ if (!existsSync10(targetDir)) {
25045
25272
  mkdirSync4(targetDir, { recursive: true });
25046
25273
  }
25047
25274
  let copied = 0;
@@ -25049,7 +25276,7 @@ function copyCanonicalNodeConfigs(cwd) {
25049
25276
  for (const file of files) {
25050
25277
  const src = join10(sourceDir, file);
25051
25278
  const dest = join10(targetDir, file);
25052
- if (existsSync9(dest)) {
25279
+ if (existsSync10(dest)) {
25053
25280
  copyFileSync(src, dest);
25054
25281
  refreshed++;
25055
25282
  } else {
@@ -25088,7 +25315,7 @@ function installProjectHooks(cwd) {
25088
25315
  for (const file of hooks) {
25089
25316
  const src = join10(sourceDir, file);
25090
25317
  const xtrmDest = join10(targetDir, file);
25091
- if (existsSync9(xtrmDest)) {
25318
+ if (existsSync10(xtrmDest)) {
25092
25319
  skippedCopies++;
25093
25320
  } else {
25094
25321
  copyFileSync(src, xtrmDest);
@@ -25096,7 +25323,7 @@ function installProjectHooks(cwd) {
25096
25323
  }
25097
25324
  const claudeHookPath = join10(claudeHooksDir, file);
25098
25325
  const relativeTarget = `../../.xtrm/hooks/specialists/${file}`;
25099
- if (existsSync9(claudeHookPath)) {
25326
+ if (existsSync10(claudeHookPath)) {
25100
25327
  const stats = lstatSync(claudeHookPath);
25101
25328
  if (!stats.isSymbolicLink()) {
25102
25329
  unlinkSync(claudeHookPath);
@@ -25104,7 +25331,7 @@ function installProjectHooks(cwd) {
25104
25331
  rewiredLinks++;
25105
25332
  continue;
25106
25333
  }
25107
- const currentTarget = resolve4(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
25334
+ const currentTarget = resolve5(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
25108
25335
  if (currentTarget !== xtrmDest) {
25109
25336
  unlinkSync(claudeHookPath);
25110
25337
  symlinkSync(relativeTarget, claudeHookPath);
@@ -25131,7 +25358,7 @@ function installProjectHooks(cwd) {
25131
25358
  function ensureProjectHookWiring(cwd) {
25132
25359
  const settingsPath = join10(cwd, ".claude", "settings.json");
25133
25360
  const settingsDir = join10(cwd, ".claude");
25134
- if (!existsSync9(settingsDir)) {
25361
+ if (!existsSync10(settingsDir)) {
25135
25362
  mkdirSync4(settingsDir, { recursive: true });
25136
25363
  }
25137
25364
  const settings = loadJson(settingsPath, {});
@@ -25167,7 +25394,7 @@ function ensureProjectHookWiring(cwd) {
25167
25394
  }
25168
25395
  }
25169
25396
  function ensureRootSymlink(rootPath, expectedTargetPath) {
25170
- if (!existsSync9(rootPath)) {
25397
+ if (!existsSync10(rootPath)) {
25171
25398
  mkdirSync4(dirname5(rootPath), { recursive: true });
25172
25399
  const relTarget = relative(dirname5(rootPath), expectedTargetPath);
25173
25400
  symlinkSync(relTarget, rootPath);
@@ -25179,14 +25406,14 @@ function ensureRootSymlink(rootPath, expectedTargetPath) {
25179
25406
  throw new Error(`${rootPath} must be a symlink to ${expectedTargetPath}. Aborting.`);
25180
25407
  }
25181
25408
  const linkTarget = readlinkSync(rootPath);
25182
- const resolvedTarget = resolve4(dirname5(rootPath), linkTarget);
25183
- const resolvedExpected = resolve4(expectedTargetPath);
25409
+ const resolvedTarget = resolve5(dirname5(rootPath), linkTarget);
25410
+ const resolvedExpected = resolve5(expectedTargetPath);
25184
25411
  if (resolvedTarget === resolvedExpected) {
25185
25412
  return;
25186
25413
  }
25187
25414
  const legacyTargets = [
25188
- resolve4(expectedTargetPath, "claude"),
25189
- resolve4(expectedTargetPath, "pi")
25415
+ resolve5(expectedTargetPath, "claude"),
25416
+ resolve5(expectedTargetPath, "pi")
25190
25417
  ];
25191
25418
  if (legacyTargets.includes(resolvedTarget)) {
25192
25419
  unlinkSync(rootPath);
@@ -25213,14 +25440,14 @@ function ensureActiveSkillSymlink(defaultSkillPath, activeLinkPath) {
25213
25440
  if (!stats.isSymbolicLink()) {
25214
25441
  throw new Error(`${activeLinkPath} already exists and is not a symlink.`);
25215
25442
  }
25216
- const currentTarget = resolve4(dirname5(activeLinkPath), readlinkSync(activeLinkPath));
25217
- if (currentTarget !== resolve4(defaultSkillPath)) {
25443
+ const currentTarget = resolve5(dirname5(activeLinkPath), readlinkSync(activeLinkPath));
25444
+ if (currentTarget !== resolve5(defaultSkillPath)) {
25218
25445
  throw new Error(`${activeLinkPath} points to an unexpected target.`);
25219
25446
  }
25220
25447
  }
25221
25448
  function installProjectSkills(cwd, syncSkills) {
25222
25449
  const xtrmRoot = join10(cwd, ".xtrm");
25223
- if (!existsSync9(xtrmRoot)) {
25450
+ if (!existsSync10(xtrmRoot)) {
25224
25451
  throw new Error(".xtrm/ is missing. Install xtrm first, then run specialists init.");
25225
25452
  }
25226
25453
  const sourceDir = resolvePackagePath("skills");
@@ -25244,7 +25471,7 @@ function installProjectSkills(cwd, syncSkills) {
25244
25471
  for (const skill of skills) {
25245
25472
  const src = join10(sourceDir, skill);
25246
25473
  const defaultSkillPath = join10(defaultRoot, skill);
25247
- if (existsSync9(defaultSkillPath)) {
25474
+ if (existsSync10(defaultSkillPath)) {
25248
25475
  if (syncSkills) {
25249
25476
  cpSync(src, defaultSkillPath, { recursive: true, force: true });
25250
25477
  refreshed++;
@@ -25266,7 +25493,7 @@ function createSpecialistsDirs(cwd) {
25266
25493
  const userDir = join10(cwd, ".specialists", "user");
25267
25494
  let created = 0;
25268
25495
  for (const dir of [defaultDir, userDir]) {
25269
- if (!existsSync9(dir)) {
25496
+ if (!existsSync10(dir)) {
25270
25497
  mkdirSync4(dir, { recursive: true });
25271
25498
  created++;
25272
25499
  }
@@ -25282,7 +25509,7 @@ function createRuntimeDirs(cwd) {
25282
25509
  ];
25283
25510
  let created = 0;
25284
25511
  for (const dir of runtimeDirs) {
25285
- if (!existsSync9(dir)) {
25512
+ if (!existsSync10(dir)) {
25286
25513
  mkdirSync4(dir, { recursive: true });
25287
25514
  created++;
25288
25515
  }
@@ -25306,7 +25533,7 @@ function ensureProjectMcp(cwd) {
25306
25533
  }
25307
25534
  function ensureGitignore(cwd) {
25308
25535
  const gitignorePath = join10(cwd, ".gitignore");
25309
- const existing = existsSync9(gitignorePath) ? readFileSync7(gitignorePath, "utf-8") : "";
25536
+ const existing = existsSync10(gitignorePath) ? readFileSync8(gitignorePath, "utf-8") : "";
25310
25537
  let added = 0;
25311
25538
  const lines = existing.split(`
25312
25539
  `);
@@ -25331,7 +25558,7 @@ function ensureObservabilityDb(cwd) {
25331
25558
  skip("observability DB path resolves inside jobs directory \u2014 skipped");
25332
25559
  return;
25333
25560
  }
25334
- const alreadyExists = existsSync9(location.dbPath);
25561
+ const alreadyExists = existsSync10(location.dbPath);
25335
25562
  if (alreadyExists) {
25336
25563
  skip("observability database already exists (not touched)");
25337
25564
  return;
@@ -25352,8 +25579,8 @@ function ensureObservabilityDb(cwd) {
25352
25579
  }
25353
25580
  function ensureAgentsMd(cwd) {
25354
25581
  const agentsPath = join10(cwd, "AGENTS.md");
25355
- if (existsSync9(agentsPath)) {
25356
- const existing = readFileSync7(agentsPath, "utf-8");
25582
+ if (existsSync10(agentsPath)) {
25583
+ const existing = readFileSync8(agentsPath, "utf-8");
25357
25584
  if (existing.includes(AGENTS_MARKER)) {
25358
25585
  skip("AGENTS.md already has Specialists section");
25359
25586
  } else {
@@ -25368,10 +25595,10 @@ function ensureAgentsMd(cwd) {
25368
25595
  }
25369
25596
  }
25370
25597
  function readJsonObject(path) {
25371
- if (!existsSync9(path))
25598
+ if (!existsSync10(path))
25372
25599
  return {};
25373
25600
  try {
25374
- return JSON.parse(readFileSync7(path, "utf-8"));
25601
+ return JSON.parse(readFileSync8(path, "utf-8"));
25375
25602
  } catch {
25376
25603
  return {};
25377
25604
  }
@@ -25399,14 +25626,14 @@ function hasHookCommand(settings, eventName, command) {
25399
25626
  function validateInitPostconditions(cwd) {
25400
25627
  const warnings = [];
25401
25628
  const xtrmHooksDir = join10(cwd, ".xtrm", "hooks", "specialists");
25402
- const xtrmHookFiles = existsSync9(xtrmHooksDir) ? readdirSync3(xtrmHooksDir).filter((file) => file.endsWith(".mjs")) : [];
25629
+ const xtrmHookFiles = existsSync10(xtrmHooksDir) ? readdirSync3(xtrmHooksDir).filter((file) => file.endsWith(".mjs")) : [];
25403
25630
  if (xtrmHookFiles.length === 0) {
25404
25631
  warnings.push(".xtrm/hooks/specialists/ is missing or has no .mjs hooks");
25405
25632
  }
25406
25633
  const claudeHooksDir = join10(cwd, ".claude", "hooks");
25407
25634
  for (const hookFile of xtrmHookFiles) {
25408
25635
  const claudeHookPath = join10(claudeHooksDir, hookFile);
25409
- if (!existsSync9(claudeHookPath)) {
25636
+ if (!existsSync10(claudeHookPath)) {
25410
25637
  warnings.push(`.claude/hooks/${hookFile} is missing`);
25411
25638
  continue;
25412
25639
  }
@@ -25415,8 +25642,8 @@ function validateInitPostconditions(cwd) {
25415
25642
  warnings.push(`.claude/hooks/${hookFile} is not a symlink`);
25416
25643
  continue;
25417
25644
  }
25418
- const expectedTarget = resolve4(xtrmHooksDir, hookFile);
25419
- const resolvedTarget = resolve4(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
25645
+ const expectedTarget = resolve5(xtrmHooksDir, hookFile);
25646
+ const resolvedTarget = resolve5(dirname5(claudeHookPath), readlinkSync(claudeHookPath));
25420
25647
  if (resolvedTarget !== expectedTarget) {
25421
25648
  warnings.push(`.claude/hooks/${hookFile} points to unexpected target`);
25422
25649
  }
@@ -25441,12 +25668,12 @@ function validateInitPostconditions(cwd) {
25441
25668
  }
25442
25669
  const runtimeDirs = [join10(cwd, ".specialists", "jobs"), join10(cwd, ".specialists", "ready")];
25443
25670
  for (const runtimeDir of runtimeDirs) {
25444
- if (!existsSync9(runtimeDir)) {
25671
+ if (!existsSync10(runtimeDir)) {
25445
25672
  warnings.push(`${relative(cwd, runtimeDir)} is missing`);
25446
25673
  }
25447
25674
  }
25448
25675
  const defaultSkillsRoot = join10(cwd, ".xtrm", "skills", "default");
25449
- 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()) : [];
25450
25677
  if (defaultSkills.length === 0) {
25451
25678
  warnings.push(".xtrm/skills/default/ is missing or has no skill directories");
25452
25679
  }
@@ -25461,7 +25688,7 @@ function validateInitPostconditions(cwd) {
25461
25688
  }
25462
25689
  ];
25463
25690
  for (const symlink of rootSymlinks) {
25464
- if (!existsSync9(symlink.linkPath)) {
25691
+ if (!existsSync10(symlink.linkPath)) {
25465
25692
  warnings.push(`${relative(cwd, symlink.linkPath)} is missing`);
25466
25693
  continue;
25467
25694
  }
@@ -25470,8 +25697,8 @@ function validateInitPostconditions(cwd) {
25470
25697
  warnings.push(`${relative(cwd, symlink.linkPath)} is not a symlink`);
25471
25698
  continue;
25472
25699
  }
25473
- const resolvedTarget = resolve4(dirname5(symlink.linkPath), readlinkSync(symlink.linkPath));
25474
- if (resolvedTarget !== resolve4(symlink.expectedTarget)) {
25700
+ const resolvedTarget = resolve5(dirname5(symlink.linkPath), readlinkSync(symlink.linkPath));
25701
+ if (resolvedTarget !== resolve5(symlink.expectedTarget)) {
25475
25702
  warnings.push(`${relative(cwd, symlink.linkPath)} points to an unexpected target`);
25476
25703
  }
25477
25704
  }
@@ -25645,8 +25872,8 @@ var exports_db = {};
25645
25872
  __export(exports_db, {
25646
25873
  run: () => run8
25647
25874
  });
25648
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
25649
- 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";
25650
25877
  function formatBytes(bytes) {
25651
25878
  if (bytes < 1024)
25652
25879
  return `${bytes} B`;
@@ -25795,7 +26022,7 @@ function parsePruneOptions(argv) {
25795
26022
  }
25796
26023
  function parseStatusFile(jobDirectoryPath, fallbackJobId) {
25797
26024
  const statusPath = join11(jobDirectoryPath, "status.json");
25798
- const statusRaw = readFileSync8(statusPath, "utf-8");
26025
+ const statusRaw = readFileSync9(statusPath, "utf-8");
25799
26026
  const parsed = JSON.parse(statusRaw);
25800
26027
  const jobId = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : fallbackJobId;
25801
26028
  const specialist = typeof parsed.specialist === "string" && parsed.specialist.length > 0 ? parsed.specialist : "unknown";
@@ -25810,9 +26037,9 @@ function parseStatusFile(jobDirectoryPath, fallbackJobId) {
25810
26037
  };
25811
26038
  }
25812
26039
  function replayEvents(eventsPath, sqliteClient, status) {
25813
- if (!existsSync10(eventsPath))
26040
+ if (!existsSync11(eventsPath))
25814
26041
  return 0;
25815
- const rawContent = readFileSync8(eventsPath, "utf-8");
26042
+ const rawContent = readFileSync9(eventsPath, "utf-8");
25816
26043
  const lines = rawContent.split(`
25817
26044
  `).map((line) => line.trim()).filter((line) => line.length > 0);
25818
26045
  let importedEvents = 0;
@@ -25838,7 +26065,7 @@ function runBackfill(options) {
25838
26065
  };
25839
26066
  try {
25840
26067
  const jobsDirectoryPath = resolveJobsDir(process.cwd());
25841
- if (!existsSync10(jobsDirectoryPath)) {
26068
+ if (!existsSync11(jobsDirectoryPath)) {
25842
26069
  console.log("No jobs directory found. Nothing to backfill.");
25843
26070
  return;
25844
26071
  }
@@ -25848,7 +26075,7 @@ function runBackfill(options) {
25848
26075
  continue;
25849
26076
  const jobDirectoryPath = join11(jobsDirectoryPath, jobEntry.name);
25850
26077
  const statusPath = join11(jobDirectoryPath, "status.json");
25851
- if (!existsSync10(statusPath))
26078
+ if (!existsSync11(statusPath))
25852
26079
  continue;
25853
26080
  try {
25854
26081
  const status = parseStatusFile(jobDirectoryPath, jobEntry.name);
@@ -25961,14 +26188,14 @@ ${bold7("specialists db prune")}
25961
26188
  }
25962
26189
  }
25963
26190
  function parseBenchmarkExportOptions(argv) {
25964
- const defaultOutput = resolve5(process.cwd(), ".specialists/benchmarks/executor-benchmark-rows.jsonl");
26191
+ const defaultOutput = resolve6(process.cwd(), ".specialists/benchmarks/executor-benchmark-rows.jsonl");
25965
26192
  let outputPath = defaultOutput;
25966
26193
  let epicId;
25967
26194
  let includePrepJobs = false;
25968
26195
  for (let i = 0;i < argv.length; i += 1) {
25969
26196
  const argument = argv[i];
25970
26197
  if (argument === "--output" && argv[i + 1]) {
25971
- outputPath = resolve5(process.cwd(), argv[i + 1]);
26198
+ outputPath = resolve6(process.cwd(), argv[i + 1]);
25972
26199
  i += 1;
25973
26200
  continue;
25974
26201
  }
@@ -26201,7 +26428,7 @@ __export(exports_validate, {
26201
26428
  ArgParseError: () => ArgParseError3
26202
26429
  });
26203
26430
  import { readFile as readFile3 } from "fs/promises";
26204
- import { existsSync as existsSync11 } from "fs";
26431
+ import { existsSync as existsSync12 } from "fs";
26205
26432
  import { join as join12 } from "path";
26206
26433
  function parseArgs4(argv) {
26207
26434
  const name = argv[0];
@@ -26221,11 +26448,11 @@ function findSpecialistFile(name) {
26221
26448
  ];
26222
26449
  for (const dir of scanDirs) {
26223
26450
  const jsonCandidate = join12(dir, `${name}.specialist.json`);
26224
- if (existsSync11(jsonCandidate)) {
26451
+ if (existsSync12(jsonCandidate)) {
26225
26452
  return jsonCandidate;
26226
26453
  }
26227
26454
  const yamlCandidate = join12(dir, `${name}.specialist.json`);
26228
- if (existsSync11(yamlCandidate)) {
26455
+ if (existsSync12(yamlCandidate)) {
26229
26456
  process.stderr.write(`[specialists] DEPRECATED: YAML specialist config detected at ${yamlCandidate}. Please migrate to .specialist.json
26230
26457
  `);
26231
26458
  return yamlCandidate;
@@ -26320,7 +26547,7 @@ __export(exports_edit, {
26320
26547
  run: () => run10
26321
26548
  });
26322
26549
  import { spawnSync as spawnSync10 } from "child_process";
26323
- 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";
26324
26551
  import { join as join13 } from "path";
26325
26552
  function loadPresets() {
26326
26553
  const paths = [
@@ -26328,9 +26555,9 @@ function loadPresets() {
26328
26555
  join13(process.cwd(), "config", "specialists", "presets.json")
26329
26556
  ];
26330
26557
  for (const p of paths) {
26331
- if (existsSync12(p)) {
26558
+ if (existsSync13(p)) {
26332
26559
  try {
26333
- const data = JSON.parse(readFileSync9(p, "utf-8"));
26560
+ const data = JSON.parse(readFileSync10(p, "utf-8"));
26334
26561
  return data;
26335
26562
  } catch {
26336
26563
  return {};
@@ -26528,7 +26755,7 @@ ${usage()}`);
26528
26755
  if (action === "get" && (pendingArrayOp || filePath)) {
26529
26756
  fail("Error: --get cannot be combined with --append/--remove/--file");
26530
26757
  }
26531
- if (filePath && !existsSync12(filePath)) {
26758
+ if (filePath && !existsSync13(filePath)) {
26532
26759
  fail(`Error: file not found: ${filePath}`);
26533
26760
  }
26534
26761
  return { name, all, scope, dryRun, action, path, value, filePath, preset };
@@ -26663,7 +26890,7 @@ function formatOutputValue(value) {
26663
26890
  }
26664
26891
  function openAllConfigSpecialistsInEditor() {
26665
26892
  const configDir = join13(process.cwd(), "config", "specialists");
26666
- if (!existsSync12(configDir)) {
26893
+ if (!existsSync13(configDir)) {
26667
26894
  fail(`Error: missing directory: ${configDir}`);
26668
26895
  }
26669
26896
  const files = readdirSync5(configDir).filter((file) => file.endsWith(".specialist.json")).sort().map((file) => join13(configDir, file));
@@ -26683,7 +26910,7 @@ function getRawValue(args, resolvedPath) {
26683
26910
  if (!MULTILINE_FILE_PATHS.has(resolvedPath.normalizedPath)) {
26684
26911
  fail(`Error: --file is only supported for: ${Array.from(MULTILINE_FILE_PATHS).join(", ")}`);
26685
26912
  }
26686
- return readFileSync9(args.filePath, "utf-8");
26913
+ return readFileSync10(args.filePath, "utf-8");
26687
26914
  }
26688
26915
  function getAtPath(root, segments) {
26689
26916
  let current = root;
@@ -26793,7 +27020,7 @@ async function run10() {
26793
27020
  }
26794
27021
  const targets2 = await resolveTargets(args);
26795
27022
  for (const target of targets2) {
26796
- const raw = readFileSync9(target.filePath, "utf-8");
27023
+ const raw = readFileSync10(target.filePath, "utf-8");
26797
27024
  const doc2 = JSON.parse(raw);
26798
27025
  for (const [fieldPath, fieldValue] of Object.entries(preset.fields)) {
26799
27026
  const resolved = resolvePath2(fieldPath);
@@ -26819,7 +27046,7 @@ async function run10() {
26819
27046
  fail("Error: no specialists found");
26820
27047
  }
26821
27048
  for (const target of targets) {
26822
- const raw = readFileSync9(target.filePath, "utf-8");
27049
+ const raw = readFileSync10(target.filePath, "utf-8");
26823
27050
  let doc2;
26824
27051
  try {
26825
27052
  const parsed = JSON.parse(raw);
@@ -26962,8 +27189,8 @@ var init_config = __esm(() => {
26962
27189
  });
26963
27190
 
26964
27191
  // src/specialist/worktree.ts
26965
- import { existsSync as existsSync13, symlinkSync as symlinkSync2, mkdirSync as mkdirSync6 } from "fs";
26966
- 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";
26967
27194
  import { spawnSync as spawnSync11, execFileSync as execFileSync2 } from "child_process";
26968
27195
  function deriveBranchName(beadId, specialistName) {
26969
27196
  return `feature/${beadId}-${slugify(specialistName)}`;
@@ -26996,11 +27223,11 @@ function provisionWorktree(options) {
26996
27223
  const branch = deriveBranchName(options.beadId, options.specialistName);
26997
27224
  const existingPath = findExistingWorktree(branch, cwd);
26998
27225
  if (existingPath) {
26999
- return { branch, worktreePath: resolve6(existingPath), reused: true };
27226
+ return { branch, worktreePath: resolve7(existingPath), reused: true };
27000
27227
  }
27001
27228
  const worktreeBase = options.worktreeBase ?? join14(commonRoot, ".worktrees", options.beadId);
27002
27229
  const worktreeName = deriveWorktreeName(options.beadId, options.specialistName);
27003
- const worktreePath = resolve6(join14(worktreeBase, worktreeName));
27230
+ const worktreePath = resolve7(join14(worktreeBase, worktreeName));
27004
27231
  createWorktreeViaBd(worktreePath, branch, commonRoot);
27005
27232
  symlinkPiNpmCache(commonRoot, worktreePath);
27006
27233
  return { branch, worktreePath, reused: false };
@@ -27008,7 +27235,7 @@ function provisionWorktree(options) {
27008
27235
  function symlinkPiNpmCache(commonRoot, worktreePath) {
27009
27236
  const source = join14(commonRoot, ".pi", "npm");
27010
27237
  const target = join14(worktreePath, ".pi", "npm");
27011
- if (!existsSync13(source) || existsSync13(target))
27238
+ if (!existsSync14(source) || existsSync14(target))
27012
27239
  return;
27013
27240
  try {
27014
27241
  mkdirSync6(join14(worktreePath, ".pi"), { recursive: true });
@@ -27074,7 +27301,7 @@ __export(exports_merge, {
27074
27301
  checkEpicUnresolvedGuard: () => checkEpicUnresolvedGuard,
27075
27302
  assertMainRepoCleanForMerge: () => assertMainRepoCleanForMerge
27076
27303
  });
27077
- 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";
27078
27305
  import { spawnSync as spawnSync12 } from "child_process";
27079
27306
  import { join as join15 } from "path";
27080
27307
  function parseOptions(argv) {
@@ -27254,7 +27481,7 @@ Use 'sp epic merge ${membership.epicId}' to publish all chains together, or 'sp
27254
27481
  }
27255
27482
  function readAllJobStatuses() {
27256
27483
  const jobsDir = resolveJobsDir();
27257
- if (!existsSync14(jobsDir))
27484
+ if (!existsSync15(jobsDir))
27258
27485
  return [];
27259
27486
  const entries = readdirSync6(jobsDir, { withFileTypes: true });
27260
27487
  const statuses = [];
@@ -27262,9 +27489,9 @@ function readAllJobStatuses() {
27262
27489
  if (!entry.isDirectory())
27263
27490
  continue;
27264
27491
  const statusPath = join15(jobsDir, entry.name, "status.json");
27265
- if (!existsSync14(statusPath))
27492
+ if (!existsSync15(statusPath))
27266
27493
  continue;
27267
- const parsed = readJson(readFileSync10(statusPath, "utf-8"));
27494
+ const parsed = readJson(readFileSync11(statusPath, "utf-8"));
27268
27495
  if (!parsed || typeof parsed !== "object")
27269
27496
  continue;
27270
27497
  statuses.push(parsed);
@@ -27763,7 +27990,9 @@ function formatToolDetail(event) {
27763
27990
  return `${toolName}: ${dim8("start")}`;
27764
27991
  }
27765
27992
  if (event.phase === "end" && event.is_error) {
27766
- 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")}`;
27767
27996
  }
27768
27997
  return `${toolName}: ${dim8(event.phase)}`;
27769
27998
  }
@@ -27781,6 +28010,8 @@ function formatEventLine(event, options) {
27781
28010
  if (event.type === "meta") {
27782
28011
  detailParts.push(`model=${event.model}`);
27783
28012
  detailParts.push(`backend=${event.backend}`);
28013
+ if (event.source)
28014
+ detailParts.push(`source=${event.source}`);
27784
28015
  } else if (event.type === "tool") {
27785
28016
  detail = formatToolDetail(event);
27786
28017
  } else if (event.type === "error") {
@@ -27920,7 +28151,7 @@ __export(exports_run, {
27920
28151
  run: () => run13
27921
28152
  });
27922
28153
  import { join as join16 } from "path";
27923
- import { readFileSync as readFileSync11 } from "fs";
28154
+ import { readFileSync as readFileSync12 } from "fs";
27924
28155
  import { randomBytes } from "crypto";
27925
28156
  import { spawn as cpSpawn, execSync as execSync3 } from "child_process";
27926
28157
  async function parseArgs6(argv) {
@@ -28035,13 +28266,13 @@ async function parseArgs6(argv) {
28035
28266
  process.exit(1);
28036
28267
  }
28037
28268
  if (!prompt && !beadId && !process.stdin.isTTY) {
28038
- prompt = await new Promise((resolve7) => {
28269
+ prompt = await new Promise((resolve8) => {
28039
28270
  let buf = "";
28040
28271
  process.stdin.setEncoding("utf-8");
28041
28272
  process.stdin.on("data", (chunk) => {
28042
28273
  buf += chunk;
28043
28274
  });
28044
- process.stdin.on("end", () => resolve7(buf.trim()));
28275
+ process.stdin.on("end", () => resolve8(buf.trim()));
28045
28276
  });
28046
28277
  }
28047
28278
  if (!prompt && !beadId && !reuseJobId) {
@@ -28199,7 +28430,7 @@ function startEventTailer(jobId, jobsDir, mode, specialist, beadId) {
28199
28430
  const drain = () => {
28200
28431
  let content;
28201
28432
  try {
28202
- content = readFileSync11(eventsPath, "utf-8");
28433
+ content = readFileSync12(eventsPath, "utf-8");
28203
28434
  } catch {
28204
28435
  return;
28205
28436
  }
@@ -28300,7 +28531,7 @@ async function run13() {
28300
28531
  const latestPath = join16(jobsDir2, "latest");
28301
28532
  const oldLatest = (() => {
28302
28533
  try {
28303
- return readFileSync11(latestPath, "utf-8").trim();
28534
+ return readFileSync12(latestPath, "utf-8").trim();
28304
28535
  } catch {
28305
28536
  return "";
28306
28537
  }
@@ -28328,7 +28559,7 @@ async function run13() {
28328
28559
  while (Date.now() < deadline) {
28329
28560
  await new Promise((r) => setTimeout(r, 100));
28330
28561
  try {
28331
- const current = readFileSync11(latestPath, "utf-8").trim();
28562
+ const current = readFileSync12(latestPath, "utf-8").trim();
28332
28563
  if (current && current !== oldLatest) {
28333
28564
  jobId2 = current;
28334
28565
  break;
@@ -28583,7 +28814,7 @@ var init_node_resolve = __esm(() => {
28583
28814
  });
28584
28815
 
28585
28816
  // src/specialist/job-control.ts
28586
- 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";
28587
28818
  import { join as join17 } from "path";
28588
28819
 
28589
28820
  class JobControl {
@@ -28615,8 +28846,8 @@ class JobControl {
28615
28846
  }
28616
28847
  };
28617
28848
  let resolveJobId;
28618
- const jobIdPromise = new Promise((resolve7) => {
28619
- resolveJobId = resolve7;
28849
+ const jobIdPromise = new Promise((resolve8) => {
28850
+ resolveJobId = resolve8;
28620
28851
  });
28621
28852
  this.supervisor = new Supervisor({
28622
28853
  runner: this.runner,
@@ -28659,10 +28890,10 @@ class JobControl {
28659
28890
  return sqliteResult;
28660
28891
  } catch {}
28661
28892
  const resultPath = this.resultPath(jobId);
28662
- if (!existsSync15(resultPath))
28893
+ if (!existsSync16(resultPath))
28663
28894
  return null;
28664
28895
  try {
28665
- return readFileSync12(resultPath, "utf-8");
28896
+ return readFileSync13(resultPath, "utf-8");
28666
28897
  } catch {
28667
28898
  return null;
28668
28899
  }
@@ -28681,7 +28912,7 @@ class JobControl {
28681
28912
  if (deadline !== undefined && Date.now() >= deadline) {
28682
28913
  throw new Error(`Timed out waiting for terminal status for job ${jobId}`);
28683
28914
  }
28684
- await new Promise((resolve7) => setTimeout(resolve7, backoffMs));
28915
+ await new Promise((resolve8) => setTimeout(resolve8, backoffMs));
28685
28916
  backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
28686
28917
  }
28687
28918
  }
@@ -28860,7 +29091,7 @@ function hashOutput(output2, salt) {
28860
29091
  return createHash3("sha256").update(value).digest("hex");
28861
29092
  }
28862
29093
  function sleep2(ms) {
28863
- return new Promise((resolve7) => setTimeout(resolve7, ms));
29094
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
28864
29095
  }
28865
29096
  function toContextHealth(contextPct) {
28866
29097
  if (contextPct === null)
@@ -30773,10 +31004,10 @@ var exports_node = {};
30773
31004
  __export(exports_node, {
30774
31005
  handleNodeCommand: () => handleNodeCommand
30775
31006
  });
30776
- 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";
30777
31008
  import { randomUUID } from "crypto";
30778
31009
  import { spawnSync as spawnSync14 } from "child_process";
30779
- 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";
30780
31011
  function parseNodeArgs(argv) {
30781
31012
  const command = argv[0];
30782
31013
  const supportedCommands = new Set(["run", "list", "promote", "members", "memory", "stop", "spawn-member", "create-bead", "complete", "wait-phase"]);
@@ -30960,8 +31191,8 @@ function toNodeName(filePath) {
30960
31191
  function discoverNodeConfigs(cwd) {
30961
31192
  const discoveredByName = new Map;
30962
31193
  for (const directory of NODE_DISCOVERY_DIRS) {
30963
- const absoluteDir = resolve7(cwd, directory.path);
30964
- if (!existsSync16(absoluteDir))
31194
+ const absoluteDir = resolve8(cwd, directory.path);
31195
+ if (!existsSync17(absoluteDir))
30965
31196
  continue;
30966
31197
  const files = readdirSync7(absoluteDir).filter((fileName) => fileName.endsWith(NODE_CONFIG_SUFFIX));
30967
31198
  for (const fileName of files) {
@@ -30975,8 +31206,8 @@ function discoverNodeConfigs(cwd) {
30975
31206
  return [...discoveredByName.values()].sort((left, right) => left.name.localeCompare(right.name));
30976
31207
  }
30977
31208
  function resolveNodeConfigPath(cwd, input2) {
30978
- const explicitPath = resolve7(cwd, input2);
30979
- if (existsSync16(explicitPath)) {
31209
+ const explicitPath = resolve8(cwd, input2);
31210
+ if (existsSync17(explicitPath)) {
30980
31211
  return explicitPath;
30981
31212
  }
30982
31213
  const normalizedName = input2.endsWith(NODE_CONFIG_SUFFIX) ? input2.slice(0, -NODE_CONFIG_SUFFIX.length) : input2;
@@ -31071,7 +31302,7 @@ async function handleNodeRun(args) {
31071
31302
  throw new Error("Observability SQLite DB is unavailable. Run: specialists db setup");
31072
31303
  }
31073
31304
  try {
31074
- 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");
31075
31306
  const config2 = parseNodeConfig(rawConfig);
31076
31307
  const loader = new SpecialistLoader;
31077
31308
  const runner = new SpecialistRunner({
@@ -31520,7 +31751,7 @@ async function handleNodeAction(args) {
31520
31751
  if (deadline !== null && Date.now() >= deadline) {
31521
31752
  throw new Error(`Timed out waiting for phase '${args.phaseId}' members: ${memberKeys.join(", ")}`);
31522
31753
  }
31523
- await new Promise((resolve8) => setTimeout(resolve8, 500));
31754
+ await new Promise((resolve9) => setTimeout(resolve9, 500));
31524
31755
  }
31525
31756
  } finally {
31526
31757
  sqliteClient.close();
@@ -31607,7 +31838,7 @@ var init_node = __esm(() => {
31607
31838
  });
31608
31839
 
31609
31840
  // src/specialist/epic-reconciler.ts
31610
- 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";
31611
31842
  import { join as join19 } from "path";
31612
31843
  function buildEpicLockPath(epicId) {
31613
31844
  const location = resolveObservabilityDbLocation();
@@ -31624,7 +31855,7 @@ function withEpicAdvisoryLock(epicId, action) {
31624
31855
  } catch {
31625
31856
  let holder = "unknown";
31626
31857
  try {
31627
- holder = readFileSync14(lockPath, "utf-8");
31858
+ holder = readFileSync15(lockPath, "utf-8");
31628
31859
  } catch {
31629
31860
  holder = "unknown";
31630
31861
  }
@@ -32600,7 +32831,7 @@ __export(exports_status, {
32600
32831
  run: () => run14
32601
32832
  });
32602
32833
  import { spawnSync as spawnSync16 } from "child_process";
32603
- import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
32834
+ import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
32604
32835
  import { join as join20 } from "path";
32605
32836
  function ok2(msg) {
32606
32837
  console.log(` ${green8("\u2713")} ${msg}`);
@@ -32693,9 +32924,9 @@ function countJobEvents(sqliteClient, jobsDir, jobId) {
32693
32924
  console.warn(`SQLite events read failed for job ${jobId}; falling back to events.jsonl`, error2);
32694
32925
  }
32695
32926
  const eventsFile = join20(jobsDir, jobId, "events.jsonl");
32696
- if (!existsSync17(eventsFile))
32927
+ if (!existsSync18(eventsFile))
32697
32928
  return 0;
32698
- const raw = readFileSync15(eventsFile, "utf-8").trim();
32929
+ const raw = readFileSync16(eventsFile, "utf-8").trim();
32699
32930
  if (!raw)
32700
32931
  return 0;
32701
32932
  return raw.split(`
@@ -32727,9 +32958,9 @@ function getLatestContextSnapshot(sqliteClient, jobsDir, jobId) {
32727
32958
  console.warn(`SQLite events read failed for job ${jobId}; falling back to events.jsonl`, error2);
32728
32959
  }
32729
32960
  const eventsFile = join20(jobsDir, jobId, "events.jsonl");
32730
- if (!existsSync17(eventsFile))
32961
+ if (!existsSync18(eventsFile))
32731
32962
  return null;
32732
- const lines = readFileSync15(eventsFile, "utf-8").split(`
32963
+ const lines = readFileSync16(eventsFile, "utf-8").split(`
32733
32964
  `);
32734
32965
  for (let index = lines.length - 1;index >= 0; index -= 1) {
32735
32966
  const line = lines[index].trim();
@@ -32834,11 +33065,11 @@ async function run14() {
32834
33065
  `).slice(1).map((line) => line.split(/\s+/)[0]).filter(Boolean)) : new Set;
32835
33066
  const bdInstalled = isInstalled2("bd");
32836
33067
  const bdVersion = bdInstalled ? cmd("bd", ["--version"]) : null;
32837
- const beadsPresent = existsSync17(join20(process.cwd(), ".beads"));
33068
+ const beadsPresent = existsSync18(join20(process.cwd(), ".beads"));
32838
33069
  const specialistsBin = cmd("which", ["specialists"]);
32839
33070
  const jobsDir = resolveJobsDir();
32840
33071
  let jobs = [];
32841
- if (existsSync17(jobsDir)) {
33072
+ if (existsSync18(jobsDir)) {
32842
33073
  supervisor = new Supervisor({
32843
33074
  runner: null,
32844
33075
  runOptions: null,
@@ -32995,7 +33226,7 @@ __export(exports_ps, {
32995
33226
  run: () => run15
32996
33227
  });
32997
33228
  import { spawnSync as spawnSync17 } from "child_process";
32998
- 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";
32999
33230
  import { join as join21 } from "path";
33000
33231
  function parseArgs7(argv) {
33001
33232
  let nodeId;
@@ -33026,25 +33257,25 @@ function isVisibleStatus(status, all) {
33026
33257
  return ACTIVE_STATES.includes(status);
33027
33258
  }
33028
33259
  function readStatusesFromFiles(jobsDir) {
33029
- if (!existsSync18(jobsDir))
33260
+ if (!existsSync19(jobsDir))
33030
33261
  return [];
33031
33262
  const statuses = [];
33032
33263
  for (const entry of readdirSync8(jobsDir)) {
33033
33264
  const statusPath = join21(jobsDir, entry, "status.json");
33034
- if (!existsSync18(statusPath))
33265
+ if (!existsSync19(statusPath))
33035
33266
  continue;
33036
33267
  try {
33037
- statuses.push(JSON.parse(readFileSync16(statusPath, "utf-8")));
33268
+ statuses.push(JSON.parse(readFileSync17(statusPath, "utf-8")));
33038
33269
  } catch {}
33039
33270
  }
33040
33271
  return statuses.sort((a, b) => b.started_at_ms - a.started_at_ms);
33041
33272
  }
33042
33273
  function readLastToolEventFromFile(jobsDir, jobId) {
33043
33274
  const eventsPath = join21(jobsDir, jobId, "events.jsonl");
33044
- if (!existsSync18(eventsPath))
33275
+ if (!existsSync19(eventsPath))
33045
33276
  return;
33046
33277
  try {
33047
- const lines = readFileSync16(eventsPath, "utf-8").split(`
33278
+ const lines = readFileSync17(eventsPath, "utf-8").split(`
33048
33279
  `);
33049
33280
  for (let index = lines.length - 1;index >= 0; index -= 1) {
33050
33281
  const line = lines[index]?.trim();
@@ -33802,7 +34033,7 @@ var exports_result = {};
33802
34033
  __export(exports_result, {
33803
34034
  run: () => run16
33804
34035
  });
33805
- import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
34036
+ import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
33806
34037
  import { join as join22 } from "path";
33807
34038
  function parseArgs8(argv) {
33808
34039
  let jobId;
@@ -33894,9 +34125,9 @@ function readTimelineEventsForResult(sqliteClient, jobsDir, jobId) {
33894
34125
  } catch {}
33895
34126
  }
33896
34127
  const eventsPath = join22(jobsDir, jobId, "events.jsonl");
33897
- if (!existsSync19(eventsPath))
34128
+ if (!existsSync20(eventsPath))
33898
34129
  return [];
33899
- return readFileSync17(eventsPath, "utf-8").split(`
34130
+ return readFileSync18(eventsPath, "utf-8").split(`
33900
34131
  `).map((line) => line.trim()).filter(Boolean).map((line) => parseTimelineEvent(line)).filter((event) => event !== null);
33901
34132
  }
33902
34133
  function deriveStartupSnapshot(status, events) {
@@ -34037,10 +34268,10 @@ async function run16() {
34037
34268
  } catch (error2) {
34038
34269
  console.warn(`SQLite result read failed for job ${jobId}; falling back to result.txt`, error2);
34039
34270
  }
34040
- if (!existsSync19(resultPath)) {
34271
+ if (!existsSync20(resultPath)) {
34041
34272
  return null;
34042
34273
  }
34043
- return readFileSync17(resultPath, "utf-8");
34274
+ return readFileSync18(resultPath, "utf-8");
34044
34275
  };
34045
34276
  if (args.wait) {
34046
34277
  const startMs = Date.now();
@@ -34214,7 +34445,7 @@ var init_result = __esm(() => {
34214
34445
  });
34215
34446
 
34216
34447
  // src/specialist/timeline-query.ts
34217
- 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";
34218
34449
  import { basename as basename5, join as join23 } from "path";
34219
34450
  function readJobEvents(jobDir) {
34220
34451
  const jobId = basename5(jobDir);
@@ -34226,9 +34457,9 @@ function readJobEvents(jobDir) {
34226
34457
  }
34227
34458
  } catch {}
34228
34459
  const eventsPath = join23(jobDir, "events.jsonl");
34229
- if (!existsSync20(eventsPath))
34460
+ if (!existsSync21(eventsPath))
34230
34461
  return [];
34231
- const content = readFileSync18(eventsPath, "utf-8");
34462
+ const content = readFileSync19(eventsPath, "utf-8");
34232
34463
  const lines = content.split(`
34233
34464
  `).filter(Boolean);
34234
34465
  const events = [];
@@ -34244,7 +34475,7 @@ function readJobEventsById(jobsDir, jobId) {
34244
34475
  return readJobEvents(join23(jobsDir, jobId));
34245
34476
  }
34246
34477
  function readAllJobEvents(jobsDir) {
34247
- if (!existsSync20(jobsDir))
34478
+ if (!existsSync21(jobsDir))
34248
34479
  return [];
34249
34480
  const batches = [];
34250
34481
  const entries = readdirSync9(jobsDir);
@@ -34261,9 +34492,9 @@ function readAllJobEvents(jobsDir) {
34261
34492
  const statusPath = join23(jobDir, "status.json");
34262
34493
  let specialist = "unknown";
34263
34494
  let beadId;
34264
- if (existsSync20(statusPath)) {
34495
+ if (existsSync21(statusPath)) {
34265
34496
  try {
34266
- const status = JSON.parse(readFileSync18(statusPath, "utf-8"));
34497
+ const status = JSON.parse(readFileSync19(statusPath, "utf-8"));
34267
34498
  specialist = status.specialist ?? "unknown";
34268
34499
  beadId = status.bead_id;
34269
34500
  } catch {}
@@ -34360,9 +34591,9 @@ __export(exports_feed, {
34360
34591
  });
34361
34592
  import {
34362
34593
  closeSync as closeSync2,
34363
- existsSync as existsSync21,
34594
+ existsSync as existsSync22,
34364
34595
  openSync as openSync3,
34365
- readFileSync as readFileSync19,
34596
+ readFileSync as readFileSync20,
34366
34597
  readdirSync as readdirSync10,
34367
34598
  statSync as statSync3
34368
34599
  } from "fs";
@@ -34474,6 +34705,10 @@ function formatStartupContextLine(event) {
34474
34705
  parts.push(`skills=${snapshot.skills.count}`);
34475
34706
  return parts.length > 0 ? dim8(` \u21B3 startup ${parts.join(" ")}`) : null;
34476
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
+ }
34477
34712
  if (event.type === "meta" && event.memory_injection) {
34478
34713
  const mem = event.memory_injection;
34479
34714
  return dim8(` \u21B3 memory static=${mem.static_tokens} dynamic=${mem.memory_tokens} gitnexus=${mem.gitnexus_tokens} total=${mem.total_tokens}`);
@@ -34507,7 +34742,7 @@ function readFileFresh(filePath) {
34507
34742
  let fd = null;
34508
34743
  try {
34509
34744
  fd = openSync3(filePath, "r");
34510
- return readFileSync19(fd, "utf-8");
34745
+ return readFileSync20(fd, "utf-8");
34511
34746
  } catch {
34512
34747
  return null;
34513
34748
  } finally {
@@ -34733,7 +34968,7 @@ function filterMergedEventsByNode(sqliteClient, jobsDir, merged, nodeId) {
34733
34968
  });
34734
34969
  }
34735
34970
  function listMatchingJobIds(sqliteClient, jobsDir, options) {
34736
- if (!existsSync21(jobsDir))
34971
+ if (!existsSync22(jobsDir))
34737
34972
  return [];
34738
34973
  const jobIds = [];
34739
34974
  for (const entry of readdirSync10(jobsDir)) {
@@ -34847,7 +35082,7 @@ async function followMerged(sqliteClient, jobsDir, options) {
34847
35082
  }
34848
35083
  const lastPrintedEventKey = new Map;
34849
35084
  const seenMetaKey = new Map;
34850
- await new Promise((resolve8) => {
35085
+ await new Promise((resolve9) => {
34851
35086
  const interval = setInterval(() => {
34852
35087
  const batches = filteredBatches();
34853
35088
  for (const jobId of listMatchingJobIds(sqliteClient, jobsDir, options)) {
@@ -34924,7 +35159,7 @@ async function followMerged(sqliteClient, jobsDir, options) {
34924
35159
  }
34925
35160
  if (!options.forever && trackedJobs.size > 0 && completedJobs.size === trackedJobs.size) {
34926
35161
  clearInterval(interval);
34927
- resolve8();
35162
+ resolve9();
34928
35163
  }
34929
35164
  }, 500);
34930
35165
  });
@@ -34934,7 +35169,7 @@ async function run17() {
34934
35169
  const sqliteClient = createObservabilitySqliteClient();
34935
35170
  try {
34936
35171
  const jobsDir = join24(process.cwd(), ".specialists", "jobs");
34937
- if (!existsSync21(jobsDir)) {
35172
+ if (!existsSync22(jobsDir)) {
34938
35173
  console.log(dim8("No jobs directory found."));
34939
35174
  return;
34940
35175
  }
@@ -34973,7 +35208,7 @@ var exports_poll = {};
34973
35208
  __export(exports_poll, {
34974
35209
  run: () => run18
34975
35210
  });
34976
- import { existsSync as existsSync22, readFileSync as readFileSync20 } from "fs";
35211
+ import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
34977
35212
  import { join as join25 } from "path";
34978
35213
  function parseArgs10(argv) {
34979
35214
  let jobId;
@@ -35014,16 +35249,16 @@ function readJobState(jobsDir, jobId, cursor, outputCursor) {
35014
35249
  const jobDir = join25(jobsDir, jobId);
35015
35250
  const statusPath = join25(jobDir, "status.json");
35016
35251
  let status = null;
35017
- if (existsSync22(statusPath)) {
35252
+ if (existsSync23(statusPath)) {
35018
35253
  try {
35019
- status = JSON.parse(readFileSync20(statusPath, "utf-8"));
35254
+ status = JSON.parse(readFileSync21(statusPath, "utf-8"));
35020
35255
  } catch {}
35021
35256
  }
35022
35257
  const resultPath = join25(jobDir, "result.txt");
35023
35258
  let fullOutput = "";
35024
- if (existsSync22(resultPath)) {
35259
+ if (existsSync23(resultPath)) {
35025
35260
  try {
35026
- fullOutput = readFileSync20(resultPath, "utf-8");
35261
+ fullOutput = readFileSync21(resultPath, "utf-8");
35027
35262
  } catch {}
35028
35263
  }
35029
35264
  const events = readJobEventsById(jobsDir, jobId);
@@ -35057,7 +35292,7 @@ async function run18() {
35057
35292
  const { jobId, cursor, outputCursor } = parseArgs10(process.argv.slice(3));
35058
35293
  const jobsDir = join25(process.cwd(), ".specialists", "jobs");
35059
35294
  const jobDir = join25(jobsDir, jobId);
35060
- if (!existsSync22(jobDir)) {
35295
+ if (!existsSync23(jobDir)) {
35061
35296
  const result2 = {
35062
35297
  job_id: jobId,
35063
35298
  status: "error",
@@ -35205,15 +35440,15 @@ async function run21() {
35205
35440
  }
35206
35441
 
35207
35442
  // src/specialist/worktree-gc.ts
35208
- 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";
35209
35444
  import { join as join26 } from "path";
35210
35445
  import { spawnSync as spawnSync18 } from "child_process";
35211
35446
  function readJobStatus2(jobDir) {
35212
35447
  const statusPath = join26(jobDir, "status.json");
35213
- if (!existsSync23(statusPath))
35448
+ if (!existsSync24(statusPath))
35214
35449
  return null;
35215
35450
  try {
35216
- return JSON.parse(readFileSync21(statusPath, "utf-8"));
35451
+ return JSON.parse(readFileSync22(statusPath, "utf-8"));
35217
35452
  } catch {
35218
35453
  return null;
35219
35454
  }
@@ -35225,7 +35460,7 @@ function isActive(status) {
35225
35460
  return ACTIVE_STATUSES.has(status);
35226
35461
  }
35227
35462
  function collectWorktreeGcCandidates(jobsDir) {
35228
- if (!existsSync23(jobsDir))
35463
+ if (!existsSync24(jobsDir))
35229
35464
  return [];
35230
35465
  const candidates = [];
35231
35466
  for (const entry of readdirSync11(jobsDir, { withFileTypes: true })) {
@@ -35241,7 +35476,7 @@ function collectWorktreeGcCandidates(jobsDir) {
35241
35476
  const { worktree_path: worktreePath, branch } = status;
35242
35477
  if (!worktreePath)
35243
35478
  continue;
35244
- if (!existsSync23(worktreePath))
35479
+ if (!existsSync24(worktreePath))
35245
35480
  continue;
35246
35481
  candidates.push({
35247
35482
  jobId: status.id,
@@ -35287,9 +35522,9 @@ __export(exports_clean, {
35287
35522
  run: () => run22
35288
35523
  });
35289
35524
  import {
35290
- existsSync as existsSync24,
35525
+ existsSync as existsSync25,
35291
35526
  readdirSync as readdirSync12,
35292
- readFileSync as readFileSync22,
35527
+ readFileSync as readFileSync23,
35293
35528
  rmSync as rmSync3,
35294
35529
  statSync as statSync4
35295
35530
  } from "fs";
@@ -35381,11 +35616,11 @@ function readCompletedJobDirectory(baseDirectory, entry) {
35381
35616
  if (containsProtectedSqliteArtifact(directoryPath))
35382
35617
  return null;
35383
35618
  const statusFilePath = join27(directoryPath, "status.json");
35384
- if (!existsSync24(statusFilePath))
35619
+ if (!existsSync25(statusFilePath))
35385
35620
  return null;
35386
35621
  let statusData;
35387
35622
  try {
35388
- statusData = JSON.parse(readFileSync22(statusFilePath, "utf-8"));
35623
+ statusData = JSON.parse(readFileSync23(statusFilePath, "utf-8"));
35389
35624
  } catch {
35390
35625
  return null;
35391
35626
  }
@@ -35482,7 +35717,7 @@ async function run22() {
35482
35717
  printUsageAndExit2(message);
35483
35718
  }
35484
35719
  const jobsDirectoryPath = resolveJobsDir();
35485
- if (!existsSync24(jobsDirectoryPath)) {
35720
+ if (!existsSync25(jobsDirectoryPath)) {
35486
35721
  console.log("No jobs directory found.");
35487
35722
  return;
35488
35723
  }
@@ -35667,7 +35902,7 @@ async function waitForProcessExit(pid, timeoutMs) {
35667
35902
  while (Date.now() < deadline) {
35668
35903
  if (!isProcessAlive(pid))
35669
35904
  return true;
35670
- await new Promise((resolve8) => setTimeout(resolve8, 100));
35905
+ await new Promise((resolve9) => setTimeout(resolve9, 100));
35671
35906
  }
35672
35907
  return !isProcessAlive(pid);
35673
35908
  }
@@ -35777,7 +36012,7 @@ __export(exports_attach, {
35777
36012
  run: () => run25
35778
36013
  });
35779
36014
  import { execFileSync as execFileSync3, spawnSync as spawnSync20 } from "child_process";
35780
- import { readFileSync as readFileSync23 } from "fs";
36015
+ import { readFileSync as readFileSync24 } from "fs";
35781
36016
  import { join as join28 } from "path";
35782
36017
  function exitWithError(message) {
35783
36018
  console.error(message);
@@ -35785,7 +36020,7 @@ function exitWithError(message) {
35785
36020
  }
35786
36021
  function readStatus(statusPath, jobId) {
35787
36022
  try {
35788
- return JSON.parse(readFileSync23(statusPath, "utf-8"));
36023
+ return JSON.parse(readFileSync24(statusPath, "utf-8"));
35789
36024
  } catch (error2) {
35790
36025
  if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
35791
36026
  exitWithError(`Job \`${jobId}\` not found. Run \`specialists status\` to see active jobs.`);
@@ -36061,8 +36296,8 @@ __export(exports_doctor, {
36061
36296
  });
36062
36297
  import { createHash as createHash4 } from "crypto";
36063
36298
  import { spawnSync as spawnSync21 } from "child_process";
36064
- import { existsSync as existsSync25, lstatSync as lstatSync2, mkdirSync as mkdirSync8, readdirSync as readdirSync13, readFileSync as readFileSync24, readlinkSync as readlinkSync2, writeFileSync as writeFileSync11 } from "fs";
36065
- 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";
36066
36301
  function ok3(msg) {
36067
36302
  console.log(` ${green14("\u2713")} ${msg}`);
36068
36303
  }
@@ -36091,10 +36326,10 @@ function isInstalled3(bin) {
36091
36326
  return spawnSync21("which", [bin], { encoding: "utf8", timeout: 2000 }).status === 0;
36092
36327
  }
36093
36328
  function loadJson2(path) {
36094
- if (!existsSync25(path))
36329
+ if (!existsSync26(path))
36095
36330
  return null;
36096
36331
  try {
36097
- return JSON.parse(readFileSync24(path, "utf8"));
36332
+ return JSON.parse(readFileSync25(path, "utf8"));
36098
36333
  } catch {
36099
36334
  return null;
36100
36335
  }
@@ -36137,7 +36372,7 @@ function checkBd() {
36137
36372
  return false;
36138
36373
  }
36139
36374
  ok3(`bd installed ${dim13(sp("bd", ["--version"]).stdout || "")}`);
36140
- if (existsSync25(join29(CWD, ".beads")))
36375
+ if (existsSync26(join29(CWD, ".beads")))
36141
36376
  ok3(".beads/ present in project");
36142
36377
  else
36143
36378
  warn3(".beads/ not found in project");
@@ -36158,7 +36393,7 @@ function checkHooks() {
36158
36393
  let allPresent = true;
36159
36394
  for (const name of HOOK_NAMES) {
36160
36395
  const canonicalPath = join29(HOOKS_DIR, name);
36161
- if (!existsSync25(canonicalPath)) {
36396
+ if (!existsSync26(canonicalPath)) {
36162
36397
  fail4(`${relative2(CWD, canonicalPath)} ${red7("missing")}`);
36163
36398
  fix("specialists init");
36164
36399
  allPresent = false;
@@ -36220,7 +36455,7 @@ function checkMCP() {
36220
36455
  }
36221
36456
  function hashFile(path) {
36222
36457
  const hash = createHash4("sha256");
36223
- hash.update(readFileSync24(path));
36458
+ hash.update(readFileSync25(path));
36224
36459
  return hash.digest("hex");
36225
36460
  }
36226
36461
  function collectFileHashes(rootDir) {
@@ -36238,12 +36473,12 @@ function collectFileHashes(rootDir) {
36238
36473
  hashes.set(relPath, hashFile(fullPath));
36239
36474
  }
36240
36475
  };
36241
- if (existsSync25(rootDir))
36476
+ if (existsSync26(rootDir))
36242
36477
  visit2(rootDir);
36243
36478
  return hashes;
36244
36479
  }
36245
36480
  function isSymlinkTo(linkPath, expectedTargetPath) {
36246
- if (!existsSync25(linkPath))
36481
+ if (!existsSync26(linkPath))
36247
36482
  return { ok: false, reason: "missing" };
36248
36483
  let stats;
36249
36484
  try {
@@ -36255,8 +36490,8 @@ function isSymlinkTo(linkPath, expectedTargetPath) {
36255
36490
  return { ok: false, reason: "not-symlink" };
36256
36491
  try {
36257
36492
  const rawTarget = readlinkSync2(linkPath);
36258
- const resolvedTarget = resolve8(dirname7(linkPath), rawTarget);
36259
- const resolvedExpected = resolve8(expectedTargetPath);
36493
+ const resolvedTarget = resolve9(dirname7(linkPath), rawTarget);
36494
+ const resolvedExpected = resolve9(expectedTargetPath);
36260
36495
  if (resolvedTarget !== resolvedExpected) {
36261
36496
  return { ok: false, reason: "wrong-target", target: rawTarget };
36262
36497
  }
@@ -36267,12 +36502,12 @@ function isSymlinkTo(linkPath, expectedTargetPath) {
36267
36502
  }
36268
36503
  function checkSkillDrift() {
36269
36504
  section3("Skill drift (.xtrm skill sync)");
36270
- if (!existsSync25(CONFIG_SKILLS_DIR)) {
36505
+ if (!existsSync26(CONFIG_SKILLS_DIR)) {
36271
36506
  fail4("config/skills/ missing");
36272
36507
  fix("restore config/skills/ from git");
36273
36508
  return false;
36274
36509
  }
36275
- if (!existsSync25(XTRM_DEFAULT_SKILLS_DIR)) {
36510
+ if (!existsSync26(XTRM_DEFAULT_SKILLS_DIR)) {
36276
36511
  fail4(".xtrm/skills/default/ missing");
36277
36512
  fix("specialists init --sync-skills");
36278
36513
  return false;
@@ -36315,7 +36550,7 @@ function checkSkillDrift() {
36315
36550
  let linksOk = true;
36316
36551
  for (const scope of ["claude", "pi"]) {
36317
36552
  const activeRoot = join29(XTRM_ACTIVE_SKILLS_DIR, scope);
36318
- if (!existsSync25(activeRoot)) {
36553
+ if (!existsSync26(activeRoot)) {
36319
36554
  fail4(`${relative2(CWD, activeRoot)}/ missing`);
36320
36555
  fix("specialists init --sync-skills");
36321
36556
  linksOk = false;
@@ -36374,14 +36609,14 @@ function checkRuntimeDirs() {
36374
36609
  const jobsDir = join29(rootDir, "jobs");
36375
36610
  const readyDir = join29(rootDir, "ready");
36376
36611
  let allOk = true;
36377
- if (!existsSync25(rootDir)) {
36612
+ if (!existsSync26(rootDir)) {
36378
36613
  warn3(".specialists/ not found in current project");
36379
36614
  fix("specialists init");
36380
36615
  allOk = false;
36381
36616
  } else {
36382
36617
  ok3(".specialists/ present");
36383
36618
  for (const [subDir, label] of [[jobsDir, "jobs"], [readyDir, "ready"]]) {
36384
- if (!existsSync25(subDir)) {
36619
+ if (!existsSync26(subDir)) {
36385
36620
  warn3(`.specialists/${label}/ missing \u2014 auto-creating`);
36386
36621
  mkdirSync8(subDir, { recursive: true });
36387
36622
  ok3(`.specialists/${label}/ created`);
@@ -36414,7 +36649,7 @@ function compareVersions(left, right) {
36414
36649
  }
36415
36650
  function setStatusError(statusPath) {
36416
36651
  try {
36417
- const raw = readFileSync24(statusPath, "utf8");
36652
+ const raw = readFileSync25(statusPath, "utf8");
36418
36653
  const status = JSON.parse(raw);
36419
36654
  status.status = "error";
36420
36655
  writeFileSync11(statusPath, `${JSON.stringify(status, null, 2)}
@@ -36437,10 +36672,10 @@ function cleanupProcesses(jobsDir, dryRun) {
36437
36672
  };
36438
36673
  for (const jobId of entries) {
36439
36674
  const statusPath = join29(jobsDir, jobId, "status.json");
36440
- if (!existsSync25(statusPath))
36675
+ if (!existsSync26(statusPath))
36441
36676
  continue;
36442
36677
  try {
36443
- const status = JSON.parse(readFileSync24(statusPath, "utf8"));
36678
+ const status = JSON.parse(readFileSync25(statusPath, "utf8"));
36444
36679
  result.total += 1;
36445
36680
  if (status.status !== "running" && status.status !== "starting")
36446
36681
  continue;
@@ -36517,7 +36752,7 @@ ${bold13("specialists doctor orphans")}
36517
36752
  function checkZombieJobs() {
36518
36753
  section3("Background jobs");
36519
36754
  const jobsDir = join29(CWD, ".specialists", "jobs");
36520
- if (!existsSync25(jobsDir)) {
36755
+ if (!existsSync26(jobsDir)) {
36521
36756
  hint("No .specialists/jobs/ \u2014 skipping");
36522
36757
  return true;
36523
36758
  }