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