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