@jaggerxtrm/specialists 3.12.0 → 3.13.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/hooks/specialists-session-start.mjs +1 -1
- package/config/mandatory-rules/bead-id-verbatim.md +14 -0
- package/config/mandatory-rules/per-turn-handoff-schema.md +16 -0
- package/config/skills/specialists-creator/SKILL.md +16 -0
- package/config/skills/update-specialists/SKILL.md +183 -350
- package/config/skills/using-kpi/SKILL.md +86 -0
- package/config/skills/using-specialists-v2/SKILL.md +1 -1
- package/config/skills/using-specialists-v3/SKILL.md +390 -112
- package/config/specialists/changelog-keeper.specialist.json +2 -1
- package/config/specialists/code-sanity.specialist.json +3 -1
- package/config/specialists/debugger.specialist.json +3 -1
- package/config/specialists/executor.specialist.json +3 -1
- package/config/specialists/explorer.specialist.json +2 -1
- package/config/specialists/overthinker.specialist.json +2 -1
- package/config/specialists/planner.specialist.json +3 -1
- package/config/specialists/researcher.specialist.json +2 -1
- package/config/specialists/reviewer.specialist.json +3 -1
- package/config/specialists/security-auditor.specialist.json +53 -10
- package/config/specialists/specialists-creator.specialist.json +2 -2
- package/config/specialists/sync-docs.specialist.json +3 -1
- package/config/specialists/test-runner.specialist.json +2 -1
- package/dist/index.js +247 -355
- package/dist/lib.js +38 -19
- package/dist/types/cli/help.d.ts.map +1 -1
- package/dist/types/cli/run.d.ts.map +1 -1
- package/dist/types/cli/version-check.d.ts +3 -0
- package/dist/types/cli/version-check.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/specialist/mandatory-rules.d.ts +5 -0
- package/dist/types/specialist/mandatory-rules.d.ts.map +1 -1
- package/package.json +4 -4
- package/config/specialists/.serena/project.yml +0 -151
package/dist/index.js
CHANGED
|
@@ -5,25 +5,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __returnValue = (v) => v;
|
|
35
|
+
function __exportSetter(name, newValue) {
|
|
36
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
+
}
|
|
20
38
|
var __export = (target, all) => {
|
|
21
39
|
for (var name in all)
|
|
22
40
|
__defProp(target, name, {
|
|
23
41
|
get: all[name],
|
|
24
42
|
enumerable: true,
|
|
25
43
|
configurable: true,
|
|
26
|
-
set: (
|
|
44
|
+
set: __exportSetter.bind(all, name)
|
|
27
45
|
});
|
|
28
46
|
};
|
|
29
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -10248,7 +10266,7 @@ var require_formats = __commonJS((exports) => {
|
|
|
10248
10266
|
}
|
|
10249
10267
|
var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
10250
10268
|
function getTime(strictTimeZone) {
|
|
10251
|
-
return function
|
|
10269
|
+
return function time3(str) {
|
|
10252
10270
|
const matches = TIME.exec(str);
|
|
10253
10271
|
if (!matches)
|
|
10254
10272
|
return false;
|
|
@@ -11014,6 +11032,8 @@ var require_Alias = __commonJS((exports) => {
|
|
|
11014
11032
|
});
|
|
11015
11033
|
}
|
|
11016
11034
|
resolve(doc2, ctx) {
|
|
11035
|
+
if (ctx?.maxAliasCount === 0)
|
|
11036
|
+
throw new ReferenceError("Alias resolution is disabled");
|
|
11017
11037
|
let nodes;
|
|
11018
11038
|
if (ctx?.aliasResolveCache) {
|
|
11019
11039
|
nodes = ctx.aliasResolveCache;
|
|
@@ -11793,6 +11813,7 @@ var require_stringify = __commonJS((exports) => {
|
|
|
11793
11813
|
nullStr: "null",
|
|
11794
11814
|
simpleKeys: false,
|
|
11795
11815
|
singleQuote: null,
|
|
11816
|
+
trailingComma: false,
|
|
11796
11817
|
trueStr: "true",
|
|
11797
11818
|
verifyAliasOrder: true
|
|
11798
11819
|
}, doc2.schema.toStringOptions, options);
|
|
@@ -12062,18 +12083,18 @@ var require_merge = __commonJS((exports) => {
|
|
|
12062
12083
|
};
|
|
12063
12084
|
var isMergeKey = (ctx, key) => (merge2.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge2.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge2.tag && tag.default);
|
|
12064
12085
|
function addMergeToJSMap(ctx, map2, value) {
|
|
12065
|
-
|
|
12066
|
-
if (identity.isSeq(
|
|
12067
|
-
for (const it of
|
|
12086
|
+
const source = resolveAliasValue(ctx, value);
|
|
12087
|
+
if (identity.isSeq(source))
|
|
12088
|
+
for (const it of source.items)
|
|
12068
12089
|
mergeValue(ctx, map2, it);
|
|
12069
|
-
else if (Array.isArray(
|
|
12070
|
-
for (const it of
|
|
12090
|
+
else if (Array.isArray(source))
|
|
12091
|
+
for (const it of source)
|
|
12071
12092
|
mergeValue(ctx, map2, it);
|
|
12072
12093
|
else
|
|
12073
|
-
mergeValue(ctx, map2,
|
|
12094
|
+
mergeValue(ctx, map2, source);
|
|
12074
12095
|
}
|
|
12075
12096
|
function mergeValue(ctx, map2, value) {
|
|
12076
|
-
const source = ctx
|
|
12097
|
+
const source = resolveAliasValue(ctx, value);
|
|
12077
12098
|
if (!identity.isMap(source))
|
|
12078
12099
|
throw new Error("Merge sources must be maps or map aliases");
|
|
12079
12100
|
const srcMap = source.toJSON(null, ctx, Map);
|
|
@@ -12094,6 +12115,9 @@ var require_merge = __commonJS((exports) => {
|
|
|
12094
12115
|
}
|
|
12095
12116
|
return map2;
|
|
12096
12117
|
}
|
|
12118
|
+
function resolveAliasValue(ctx, value) {
|
|
12119
|
+
return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value;
|
|
12120
|
+
}
|
|
12097
12121
|
exports.addMergeToJSMap = addMergeToJSMap;
|
|
12098
12122
|
exports.isMergeKey = isMergeKey;
|
|
12099
12123
|
exports.merge = merge2;
|
|
@@ -12301,13 +12325,20 @@ ${indent}${line}` : `
|
|
|
12301
12325
|
if (comment)
|
|
12302
12326
|
reqNewline = true;
|
|
12303
12327
|
let str = stringify.stringify(item, itemCtx, () => comment = null);
|
|
12304
|
-
|
|
12328
|
+
reqNewline || (reqNewline = lines.length > linesAtValue || str.includes(`
|
|
12329
|
+
`));
|
|
12330
|
+
if (i < items.length - 1) {
|
|
12305
12331
|
str += ",";
|
|
12332
|
+
} else if (ctx.options.trailingComma) {
|
|
12333
|
+
if (ctx.options.lineWidth > 0) {
|
|
12334
|
+
reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
|
|
12335
|
+
}
|
|
12336
|
+
if (reqNewline) {
|
|
12337
|
+
str += ",";
|
|
12338
|
+
}
|
|
12339
|
+
}
|
|
12306
12340
|
if (comment)
|
|
12307
12341
|
str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
|
|
12308
|
-
if (!reqNewline && (lines.length > linesAtValue || str.includes(`
|
|
12309
|
-
`)))
|
|
12310
|
-
reqNewline = true;
|
|
12311
12342
|
lines.push(str);
|
|
12312
12343
|
linesAtValue = lines.length;
|
|
12313
12344
|
}
|
|
@@ -12662,7 +12693,7 @@ var require_stringifyNumber = __commonJS((exports) => {
|
|
|
12662
12693
|
if (!isFinite(num))
|
|
12663
12694
|
return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
|
|
12664
12695
|
let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
|
|
12665
|
-
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") &&
|
|
12696
|
+
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) {
|
|
12666
12697
|
let i = n.indexOf(".");
|
|
12667
12698
|
if (i < 0) {
|
|
12668
12699
|
i = n.length;
|
|
@@ -14887,7 +14918,7 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
|
|
|
14887
14918
|
while (next === " " || next === "\t")
|
|
14888
14919
|
next = source[++i + 1];
|
|
14889
14920
|
} else if (next === "x" || next === "u" || next === "U") {
|
|
14890
|
-
const length =
|
|
14921
|
+
const length = next === "x" ? 2 : next === "u" ? 4 : 8;
|
|
14891
14922
|
res += parseCharCode(source, i + 1, length, onError);
|
|
14892
14923
|
i += length;
|
|
14893
14924
|
} else {
|
|
@@ -14956,12 +14987,13 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
|
|
|
14956
14987
|
const cc = source.substr(offset, length);
|
|
14957
14988
|
const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
|
|
14958
14989
|
const code = ok ? parseInt(cc, 16) : NaN;
|
|
14959
|
-
|
|
14990
|
+
try {
|
|
14991
|
+
return String.fromCodePoint(code);
|
|
14992
|
+
} catch {
|
|
14960
14993
|
const raw = source.substr(offset - 2, length + 2);
|
|
14961
14994
|
onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`);
|
|
14962
14995
|
return raw;
|
|
14963
14996
|
}
|
|
14964
|
-
return String.fromCodePoint(code);
|
|
14965
14997
|
}
|
|
14966
14998
|
exports.resolveFlowScalar = resolveFlowScalar;
|
|
14967
14999
|
});
|
|
@@ -15102,17 +15134,22 @@ var require_compose_node = __commonJS((exports) => {
|
|
|
15102
15134
|
case "block-map":
|
|
15103
15135
|
case "block-seq":
|
|
15104
15136
|
case "flow-collection":
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
|
|
15137
|
+
try {
|
|
15138
|
+
node = composeCollection.composeCollection(CN, ctx, token, props, onError);
|
|
15139
|
+
if (anchor)
|
|
15140
|
+
node.anchor = anchor.source.substring(1);
|
|
15141
|
+
} catch (error2) {
|
|
15142
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
15143
|
+
onError(token, "RESOURCE_EXHAUSTION", message);
|
|
15144
|
+
}
|
|
15108
15145
|
break;
|
|
15109
15146
|
default: {
|
|
15110
15147
|
const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
|
|
15111
15148
|
onError(token, "UNEXPECTED_TOKEN", message);
|
|
15112
|
-
node = composeEmptyNode(ctx, token.offset, undefined, null, props, onError);
|
|
15113
15149
|
isSrcToken = false;
|
|
15114
15150
|
}
|
|
15115
15151
|
}
|
|
15152
|
+
node ?? (node = composeEmptyNode(ctx, token.offset, undefined, null, props, onError));
|
|
15116
15153
|
if (anchor && node.anchor === "")
|
|
15117
15154
|
onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
|
|
15118
15155
|
if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
|
|
@@ -21582,24 +21619,27 @@ function readMandatoryRuleSet(cwd, id) {
|
|
|
21582
21619
|
}
|
|
21583
21620
|
function formatMandatoryRulesBlock(sets, inlineRules = []) {
|
|
21584
21621
|
if (sets.length === 0 && inlineRules.length === 0)
|
|
21585
|
-
return "";
|
|
21622
|
+
return { block: "", sections: [] };
|
|
21586
21623
|
const sections = [
|
|
21587
21624
|
...sets.map((set2) => {
|
|
21588
21625
|
const rules = set2.rules.map((rule) => `- [${rule.level}] ${rule.text}`).join(`
|
|
21589
21626
|
`);
|
|
21590
|
-
return `### ${set2.id}
|
|
21591
|
-
${rules}
|
|
21627
|
+
return { setId: set2.id, block: `### ${set2.id}
|
|
21628
|
+
${rules}` };
|
|
21592
21629
|
}),
|
|
21593
21630
|
...inlineRules.length > 0 ? [
|
|
21594
|
-
|
|
21631
|
+
{
|
|
21632
|
+
setId: "specialist-inline-rules",
|
|
21633
|
+
block: `### specialist-inline-rules
|
|
21595
21634
|
${inlineRules.map((rule, index) => `- [${rule.level}] ${rule.text}${rule.id ? ` (id: ${rule.id})` : ` (id: inline-${index + 1})`}`).join(`
|
|
21596
21635
|
`)}`
|
|
21636
|
+
}
|
|
21597
21637
|
] : []
|
|
21598
21638
|
];
|
|
21599
|
-
return `## MANDATORY_RULES
|
|
21600
|
-
${sections.join(`
|
|
21639
|
+
return { block: `## MANDATORY_RULES
|
|
21640
|
+
${sections.map((section) => section.block).join(`
|
|
21601
21641
|
|
|
21602
|
-
`)}
|
|
21642
|
+
`)}`, sections };
|
|
21603
21643
|
}
|
|
21604
21644
|
function collectMandatoryRuleSets(cwd, setIds) {
|
|
21605
21645
|
const seen = new Set;
|
|
@@ -21633,9 +21673,10 @@ function buildMandatoryRulesInjection(specialistConfig) {
|
|
|
21633
21673
|
id: "workflow-quick-rules",
|
|
21634
21674
|
rules: [{ id: "workflow-quick-rules-1", level: "required", text: STATIC_WORKFLOW_RULES_BLOCK.trim().replace(/^##\s+Beads Workflow Quick Rules\n/, "") }]
|
|
21635
21675
|
}];
|
|
21636
|
-
const
|
|
21676
|
+
const formatted = formatMandatoryRulesBlock([...globals, ...sets], inlineRules);
|
|
21637
21677
|
return {
|
|
21638
|
-
block,
|
|
21678
|
+
block: formatted.block,
|
|
21679
|
+
sections: formatted.sections,
|
|
21639
21680
|
setsLoaded: [...globals.map((set2) => set2.id), ...sets.map((set2) => set2.id)],
|
|
21640
21681
|
ruleCount: [...globals, ...sets].reduce((count, set2) => count + set2.rules.length, 0) + inlineRules.length,
|
|
21641
21682
|
inlineRulesCount: inlineRules.length,
|
|
@@ -22565,9 +22606,8 @@ ${summaries.join(`
|
|
|
22565
22606
|
skillPaths.push(prompt.skill_inherit);
|
|
22566
22607
|
skillPaths.push(...spec.specialist.skills?.paths ?? []);
|
|
22567
22608
|
if (mandatoryRulesInjection) {
|
|
22568
|
-
for (const
|
|
22569
|
-
payloadComponents.push(measurePayloadComponent("mandatory_rule", setId,
|
|
22570
|
-
${mandatoryRulesBlock}`));
|
|
22609
|
+
for (const section of mandatoryRulesInjection.sections) {
|
|
22610
|
+
payloadComponents.push(measurePayloadComponent("mandatory_rule", section.setId, section.block));
|
|
22571
22611
|
}
|
|
22572
22612
|
}
|
|
22573
22613
|
for (const skillPath of skillPaths) {
|
|
@@ -31105,7 +31145,7 @@ function resolveWorkingDirectory(args, jobsDir, permissionRequired, readStatus)
|
|
|
31105
31145
|
if (args.reuseJobId !== undefined) {
|
|
31106
31146
|
const targetStatus = readStatus(args.reuseJobId);
|
|
31107
31147
|
if (!targetStatus) {
|
|
31108
|
-
console.error(`Error: cannot read status for job '${args.reuseJobId}'. ` + `Check the job id with: specialists
|
|
31148
|
+
console.error(`Error: cannot read status for job '${args.reuseJobId}'. ` + `Check the job id with: specialists ps ${args.reuseJobId} --json`);
|
|
31109
31149
|
process.exit(1);
|
|
31110
31150
|
}
|
|
31111
31151
|
const targetJobStatus = targetStatus.status;
|
|
@@ -31553,7 +31593,9 @@ ${bold11(`Running ${cyan6(args.name)}`)}
|
|
|
31553
31593
|
${green9("\u2713")} ${footer}
|
|
31554
31594
|
|
|
31555
31595
|
`);
|
|
31556
|
-
process.stderr.write(dim9(`
|
|
31596
|
+
process.stderr.write(dim9(`Status: specialists ps ${jobId} --json
|
|
31597
|
+
`));
|
|
31598
|
+
process.stderr.write(dim9(`Events: specialists feed ${jobId}
|
|
31557
31599
|
|
|
31558
31600
|
`));
|
|
31559
31601
|
process.exit(0);
|
|
@@ -35458,6 +35500,16 @@ import { spawnSync as spawnSync16 } from "child_process";
|
|
|
35458
35500
|
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync20, writeFileSync as writeFileSync9 } from "fs";
|
|
35459
35501
|
import { dirname as dirname8, join as join22 } from "path";
|
|
35460
35502
|
import { createRequire as createRequire3 } from "module";
|
|
35503
|
+
function readBundledPackageVersion(requireFn = require3) {
|
|
35504
|
+
for (const candidate of ["../package.json", "../../package.json"]) {
|
|
35505
|
+
try {
|
|
35506
|
+
const pkg = requireFn(candidate);
|
|
35507
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0)
|
|
35508
|
+
return pkg.version;
|
|
35509
|
+
} catch {}
|
|
35510
|
+
}
|
|
35511
|
+
return "0.0.0";
|
|
35512
|
+
}
|
|
35461
35513
|
function shouldRunVersionCheck() {
|
|
35462
35514
|
if (process.env.SPECIALISTS_OFFLINE === "1")
|
|
35463
35515
|
return false;
|
|
@@ -35565,7 +35617,7 @@ function markVersionCheckNotified(result) {
|
|
|
35565
35617
|
var require3, packageVersion, localVersion, CACHE_PATH, CACHE_MAX_AGE_MS2, NETWORK_TIMEOUT_MS = 2000;
|
|
35566
35618
|
var init_version_check = __esm(() => {
|
|
35567
35619
|
require3 = createRequire3(import.meta.url);
|
|
35568
|
-
|
|
35620
|
+
packageVersion = readBundledPackageVersion();
|
|
35569
35621
|
localVersion = packageVersion;
|
|
35570
35622
|
CACHE_PATH = join22(process.cwd(), ".specialists", "version-check.json");
|
|
35571
35623
|
CACHE_MAX_AGE_MS2 = 6 * 60 * 60 * 1000;
|
|
@@ -37382,9 +37434,6 @@ function readJobEvents(jobDir) {
|
|
|
37382
37434
|
events.sort(compareTimelineEvents);
|
|
37383
37435
|
return events;
|
|
37384
37436
|
}
|
|
37385
|
-
function readJobEventsById(jobsDir, jobId) {
|
|
37386
|
-
return readJobEvents(join26(jobsDir, jobId));
|
|
37387
|
-
}
|
|
37388
37437
|
function readAllJobEvents(jobsDir) {
|
|
37389
37438
|
const sqliteClient = createObservabilitySqliteClient();
|
|
37390
37439
|
try {
|
|
@@ -38189,130 +38238,13 @@ var init_feed = __esm(() => {
|
|
|
38189
38238
|
init_format_helpers();
|
|
38190
38239
|
});
|
|
38191
38240
|
|
|
38192
|
-
// src/cli/poll.ts
|
|
38193
|
-
var exports_poll = {};
|
|
38194
|
-
__export(exports_poll, {
|
|
38195
|
-
run: () => run19
|
|
38196
|
-
});
|
|
38197
|
-
import { existsSync as existsSync26, readFileSync as readFileSync26 } from "fs";
|
|
38198
|
-
import { join as join28 } from "path";
|
|
38199
|
-
function parseArgs11(argv) {
|
|
38200
|
-
let jobId;
|
|
38201
|
-
let cursor = 0;
|
|
38202
|
-
let outputCursor = 0;
|
|
38203
|
-
for (let i = 0;i < argv.length; i++) {
|
|
38204
|
-
if (argv[i] === "--cursor" && argv[i + 1]) {
|
|
38205
|
-
cursor = parseInt(argv[++i], 10);
|
|
38206
|
-
if (isNaN(cursor) || cursor < 0)
|
|
38207
|
-
cursor = 0;
|
|
38208
|
-
continue;
|
|
38209
|
-
}
|
|
38210
|
-
if (argv[i] === "--output-cursor" && argv[i + 1]) {
|
|
38211
|
-
outputCursor = parseInt(argv[++i], 10);
|
|
38212
|
-
if (isNaN(outputCursor) || outputCursor < 0)
|
|
38213
|
-
outputCursor = 0;
|
|
38214
|
-
continue;
|
|
38215
|
-
}
|
|
38216
|
-
if (argv[i] === "--json") {
|
|
38217
|
-
continue;
|
|
38218
|
-
}
|
|
38219
|
-
if (argv[i] === "--follow" || argv[i] === "-f") {
|
|
38220
|
-
process.stderr.write(`--follow removed from poll. Use 'specialists feed --follow' for live human-readable output.
|
|
38221
|
-
`);
|
|
38222
|
-
process.exit(1);
|
|
38223
|
-
}
|
|
38224
|
-
if (!argv[i].startsWith("-")) {
|
|
38225
|
-
jobId = argv[i];
|
|
38226
|
-
}
|
|
38227
|
-
}
|
|
38228
|
-
if (!jobId) {
|
|
38229
|
-
console.error("Usage: specialists poll <job-id> [--cursor N] [--output-cursor N]");
|
|
38230
|
-
process.exit(1);
|
|
38231
|
-
}
|
|
38232
|
-
return { jobId, cursor, outputCursor };
|
|
38233
|
-
}
|
|
38234
|
-
function readJobState(jobsDir, jobId, cursor, outputCursor) {
|
|
38235
|
-
const jobDir = join28(jobsDir, jobId);
|
|
38236
|
-
const statusPath = join28(jobDir, "status.json");
|
|
38237
|
-
let status = null;
|
|
38238
|
-
if (existsSync26(statusPath)) {
|
|
38239
|
-
try {
|
|
38240
|
-
status = JSON.parse(readFileSync26(statusPath, "utf-8"));
|
|
38241
|
-
} catch {}
|
|
38242
|
-
}
|
|
38243
|
-
const resultPath = join28(jobDir, "result.txt");
|
|
38244
|
-
let fullOutput = "";
|
|
38245
|
-
if (existsSync26(resultPath)) {
|
|
38246
|
-
try {
|
|
38247
|
-
fullOutput = readFileSync26(resultPath, "utf-8");
|
|
38248
|
-
} catch {}
|
|
38249
|
-
}
|
|
38250
|
-
const events = readJobEventsById(jobsDir, jobId);
|
|
38251
|
-
const newEvents = events.slice(cursor);
|
|
38252
|
-
const nextCursor = events.length;
|
|
38253
|
-
const startedAt = status?.started_at_ms ?? Date.now();
|
|
38254
|
-
const lastEvent = status?.last_event_at_ms ?? Date.now();
|
|
38255
|
-
const elapsedMs = status?.status === "done" || status?.status === "error" ? lastEvent - startedAt : Date.now() - startedAt;
|
|
38256
|
-
const isDone = status?.status === "done";
|
|
38257
|
-
const output2 = isDone ? fullOutput : "";
|
|
38258
|
-
const outputDelta = fullOutput.length > outputCursor ? fullOutput.slice(outputCursor) : "";
|
|
38259
|
-
const nextOutputCursor = fullOutput.length;
|
|
38260
|
-
return {
|
|
38261
|
-
job_id: jobId,
|
|
38262
|
-
status: status?.status ?? "starting",
|
|
38263
|
-
elapsed_ms: elapsedMs,
|
|
38264
|
-
cursor: nextCursor,
|
|
38265
|
-
output_cursor: nextOutputCursor,
|
|
38266
|
-
output: output2,
|
|
38267
|
-
output_delta: outputDelta,
|
|
38268
|
-
events: newEvents,
|
|
38269
|
-
current_event: status?.current_event,
|
|
38270
|
-
current_tool: status?.current_tool,
|
|
38271
|
-
model: status?.model,
|
|
38272
|
-
backend: status?.backend,
|
|
38273
|
-
bead_id: status?.bead_id,
|
|
38274
|
-
error: status?.error
|
|
38275
|
-
};
|
|
38276
|
-
}
|
|
38277
|
-
async function run19() {
|
|
38278
|
-
process.stderr.write(`[DEPRECATED] 'specialists poll' is scheduled for removal. Use 'sp ps <id> --json' for status and 'sp feed <id>' for events.
|
|
38279
|
-
`);
|
|
38280
|
-
const { jobId, cursor, outputCursor } = parseArgs11(process.argv.slice(3));
|
|
38281
|
-
const jobsDir = join28(process.cwd(), ".specialists", "jobs");
|
|
38282
|
-
const jobDir = join28(jobsDir, jobId);
|
|
38283
|
-
if (!existsSync26(jobDir)) {
|
|
38284
|
-
const result2 = {
|
|
38285
|
-
job_id: jobId,
|
|
38286
|
-
status: "error",
|
|
38287
|
-
elapsed_ms: 0,
|
|
38288
|
-
cursor: 0,
|
|
38289
|
-
output_cursor: 0,
|
|
38290
|
-
output: "",
|
|
38291
|
-
output_delta: "",
|
|
38292
|
-
events: [],
|
|
38293
|
-
error: `Job not found: ${jobId}`
|
|
38294
|
-
};
|
|
38295
|
-
console.log(JSON.stringify(result2));
|
|
38296
|
-
process.exit(1);
|
|
38297
|
-
}
|
|
38298
|
-
const result = readJobState(jobsDir, jobId, cursor, outputCursor);
|
|
38299
|
-
if (result.status !== "done" && result.status !== "error" && !result.output_delta) {
|
|
38300
|
-
process.stderr.write(`Tip: use 'specialists feed --follow' for live human-readable output.
|
|
38301
|
-
`);
|
|
38302
|
-
}
|
|
38303
|
-
console.log(JSON.stringify(result));
|
|
38304
|
-
}
|
|
38305
|
-
var init_poll = __esm(() => {
|
|
38306
|
-
init_timeline_query();
|
|
38307
|
-
});
|
|
38308
|
-
|
|
38309
38241
|
// src/cli/steer.ts
|
|
38310
38242
|
var exports_steer = {};
|
|
38311
38243
|
__export(exports_steer, {
|
|
38312
|
-
run: () =>
|
|
38244
|
+
run: () => run19
|
|
38313
38245
|
});
|
|
38314
38246
|
import { writeFileSync as writeFileSync10 } from "fs";
|
|
38315
|
-
async function
|
|
38247
|
+
async function run19() {
|
|
38316
38248
|
const jobId = process.argv[3];
|
|
38317
38249
|
const message = process.argv[4];
|
|
38318
38250
|
if (!jobId || !message) {
|
|
@@ -38363,10 +38295,10 @@ var init_steer = __esm(() => {
|
|
|
38363
38295
|
// src/cli/resume.ts
|
|
38364
38296
|
var exports_resume = {};
|
|
38365
38297
|
__export(exports_resume, {
|
|
38366
|
-
run: () =>
|
|
38298
|
+
run: () => run20
|
|
38367
38299
|
});
|
|
38368
38300
|
import { writeFileSync as writeFileSync11 } from "fs";
|
|
38369
|
-
async function
|
|
38301
|
+
async function run20() {
|
|
38370
38302
|
const jobId = process.argv[3];
|
|
38371
38303
|
const task = process.argv[4];
|
|
38372
38304
|
if (!jobId || !task) {
|
|
@@ -38419,24 +38351,24 @@ var init_resume = __esm(() => {
|
|
|
38419
38351
|
// src/cli/follow-up.ts
|
|
38420
38352
|
var exports_follow_up = {};
|
|
38421
38353
|
__export(exports_follow_up, {
|
|
38422
|
-
run: () =>
|
|
38354
|
+
run: () => run21
|
|
38423
38355
|
});
|
|
38424
|
-
async function
|
|
38356
|
+
async function run21() {
|
|
38425
38357
|
process.stderr.write("\x1B[33m\u26A0 DEPRECATED:\x1B[0m `specialists follow-up` is deprecated. Use `specialists resume` instead.\n\n");
|
|
38426
38358
|
const { run: resumeRun } = await Promise.resolve().then(() => (init_resume(), exports_resume));
|
|
38427
38359
|
return resumeRun();
|
|
38428
38360
|
}
|
|
38429
38361
|
|
|
38430
38362
|
// src/specialist/worktree-gc.ts
|
|
38431
|
-
import { existsSync as
|
|
38432
|
-
import { join as
|
|
38363
|
+
import { existsSync as existsSync26, readdirSync as readdirSync11, readFileSync as readFileSync26 } from "fs";
|
|
38364
|
+
import { join as join28 } from "path";
|
|
38433
38365
|
import { spawnSync as spawnSync19 } from "child_process";
|
|
38434
38366
|
function readJobStatus2(jobDir) {
|
|
38435
|
-
const statusPath =
|
|
38436
|
-
if (!
|
|
38367
|
+
const statusPath = join28(jobDir, "status.json");
|
|
38368
|
+
if (!existsSync26(statusPath))
|
|
38437
38369
|
return null;
|
|
38438
38370
|
try {
|
|
38439
|
-
return JSON.parse(
|
|
38371
|
+
return JSON.parse(readFileSync26(statusPath, "utf-8"));
|
|
38440
38372
|
} catch {
|
|
38441
38373
|
return null;
|
|
38442
38374
|
}
|
|
@@ -38458,7 +38390,7 @@ function collectWorktreeGcCandidates(jobsDir) {
|
|
|
38458
38390
|
const worktreePath = status.worktree_path;
|
|
38459
38391
|
if (!worktreePath)
|
|
38460
38392
|
return null;
|
|
38461
|
-
if (!
|
|
38393
|
+
if (!existsSync26(worktreePath))
|
|
38462
38394
|
return null;
|
|
38463
38395
|
return {
|
|
38464
38396
|
jobId: status.id,
|
|
@@ -38470,13 +38402,13 @@ function collectWorktreeGcCandidates(jobsDir) {
|
|
|
38470
38402
|
}
|
|
38471
38403
|
if (!getFileFallbackEnabled())
|
|
38472
38404
|
return [];
|
|
38473
|
-
if (!
|
|
38405
|
+
if (!existsSync26(jobsDir))
|
|
38474
38406
|
return [];
|
|
38475
38407
|
const candidates = [];
|
|
38476
38408
|
for (const entry of readdirSync11(jobsDir, { withFileTypes: true })) {
|
|
38477
38409
|
if (!entry.isDirectory())
|
|
38478
38410
|
continue;
|
|
38479
|
-
const status = readJobStatus2(
|
|
38411
|
+
const status = readJobStatus2(join28(jobsDir, entry.name));
|
|
38480
38412
|
if (!status)
|
|
38481
38413
|
continue;
|
|
38482
38414
|
if (isActive(status.status))
|
|
@@ -38486,7 +38418,7 @@ function collectWorktreeGcCandidates(jobsDir) {
|
|
|
38486
38418
|
const { worktree_path: worktreePath, branch } = status;
|
|
38487
38419
|
if (!worktreePath)
|
|
38488
38420
|
continue;
|
|
38489
|
-
if (!
|
|
38421
|
+
if (!existsSync26(worktreePath))
|
|
38490
38422
|
continue;
|
|
38491
38423
|
candidates.push({
|
|
38492
38424
|
jobId: status.id,
|
|
@@ -38530,10 +38462,10 @@ var init_worktree_gc = __esm(() => {
|
|
|
38530
38462
|
// src/cli/clean.ts
|
|
38531
38463
|
var exports_clean = {};
|
|
38532
38464
|
__export(exports_clean, {
|
|
38533
|
-
run: () =>
|
|
38465
|
+
run: () => run22
|
|
38534
38466
|
});
|
|
38535
|
-
import { existsSync as
|
|
38536
|
-
import { join as
|
|
38467
|
+
import { existsSync as existsSync27, readFileSync as readFileSync27, readdirSync as readdirSync12, rmSync as rmSync3, statSync as statSync4 } from "fs";
|
|
38468
|
+
import { join as join29 } from "path";
|
|
38537
38469
|
function parseTtlDaysFromEnvironment() {
|
|
38538
38470
|
const rawValue = process.env.SPECIALISTS_JOB_TTL_DAYS ?? process.env.JOB_TTL_DAYS;
|
|
38539
38471
|
if (!rawValue)
|
|
@@ -38616,7 +38548,7 @@ function parseOptions2(argv) {
|
|
|
38616
38548
|
function readDirectorySizeBytes(directoryPath) {
|
|
38617
38549
|
let totalBytes = 0;
|
|
38618
38550
|
for (const entry of readdirSync12(directoryPath, { withFileTypes: true })) {
|
|
38619
|
-
const entryPath =
|
|
38551
|
+
const entryPath = join29(directoryPath, entry.name);
|
|
38620
38552
|
const stats = statSync4(entryPath);
|
|
38621
38553
|
totalBytes += stats.isDirectory() ? readDirectorySizeBytes(entryPath) : stats.size;
|
|
38622
38554
|
}
|
|
@@ -38624,7 +38556,7 @@ function readDirectorySizeBytes(directoryPath) {
|
|
|
38624
38556
|
}
|
|
38625
38557
|
function containsProtectedSqliteArtifact(directoryPath) {
|
|
38626
38558
|
for (const entry of readdirSync12(directoryPath, { withFileTypes: true })) {
|
|
38627
|
-
const entryPath =
|
|
38559
|
+
const entryPath = join29(directoryPath, entry.name);
|
|
38628
38560
|
if (entry.isDirectory()) {
|
|
38629
38561
|
if (containsProtectedSqliteArtifact(entryPath))
|
|
38630
38562
|
return true;
|
|
@@ -38645,15 +38577,15 @@ function getJobTimestamps(status) {
|
|
|
38645
38577
|
function readCompletedJobDirectory(baseDirectory, entry) {
|
|
38646
38578
|
if (!entry.isDirectory())
|
|
38647
38579
|
return null;
|
|
38648
|
-
const directoryPath =
|
|
38580
|
+
const directoryPath = join29(baseDirectory, entry.name);
|
|
38649
38581
|
if (containsProtectedSqliteArtifact(directoryPath))
|
|
38650
38582
|
return null;
|
|
38651
|
-
const statusFilePath =
|
|
38652
|
-
if (!
|
|
38583
|
+
const statusFilePath = join29(directoryPath, "status.json");
|
|
38584
|
+
if (!existsSync27(statusFilePath))
|
|
38653
38585
|
return null;
|
|
38654
38586
|
let statusData;
|
|
38655
38587
|
try {
|
|
38656
|
-
statusData = JSON.parse(
|
|
38588
|
+
statusData = JSON.parse(readFileSync27(statusFilePath, "utf-8"));
|
|
38657
38589
|
} catch {
|
|
38658
38590
|
return null;
|
|
38659
38591
|
}
|
|
@@ -38667,8 +38599,8 @@ function collectCompletedJobs(jobsDirectoryPath) {
|
|
|
38667
38599
|
const statuses = sqliteClient?.listStatuses() ?? [];
|
|
38668
38600
|
if (statuses.length > 0) {
|
|
38669
38601
|
return statuses.filter((status) => COMPLETED_STATUSES.has(status.status)).map((status) => {
|
|
38670
|
-
const directoryPath =
|
|
38671
|
-
if (!
|
|
38602
|
+
const directoryPath = join29(jobsDirectoryPath, status.id);
|
|
38603
|
+
if (!existsSync27(directoryPath) || containsProtectedSqliteArtifact(directoryPath))
|
|
38672
38604
|
return null;
|
|
38673
38605
|
const { createdAtMs, completedAtMs } = getJobTimestamps(status);
|
|
38674
38606
|
return { id: status.id, directoryPath, completedAtMs, createdAtMs, sizeBytes: readDirectorySizeBytes(directoryPath) };
|
|
@@ -38808,7 +38740,7 @@ function removeStaleProcesses(statuses, dryRun) {
|
|
|
38808
38740
|
}
|
|
38809
38741
|
return updatedCount;
|
|
38810
38742
|
}
|
|
38811
|
-
async function
|
|
38743
|
+
async function run22() {
|
|
38812
38744
|
let options;
|
|
38813
38745
|
try {
|
|
38814
38746
|
options = parseOptions2(process.argv.slice(3));
|
|
@@ -38817,7 +38749,7 @@ async function run23() {
|
|
|
38817
38749
|
printUsageAndExit2(message);
|
|
38818
38750
|
}
|
|
38819
38751
|
const jobsDirectoryPath = resolveJobsDir();
|
|
38820
|
-
if (!
|
|
38752
|
+
if (!existsSync27(jobsDirectoryPath)) {
|
|
38821
38753
|
console.log("No jobs directory found.");
|
|
38822
38754
|
return;
|
|
38823
38755
|
}
|
|
@@ -38871,7 +38803,7 @@ var init_clean = __esm(() => {
|
|
|
38871
38803
|
// src/cli/end.ts
|
|
38872
38804
|
var exports_end = {};
|
|
38873
38805
|
__export(exports_end, {
|
|
38874
|
-
run: () =>
|
|
38806
|
+
run: () => run23
|
|
38875
38807
|
});
|
|
38876
38808
|
import { spawnSync as spawnSync20 } from "child_process";
|
|
38877
38809
|
function parseOptions3(argv) {
|
|
@@ -38955,7 +38887,7 @@ async function publishChain(beadId, options) {
|
|
|
38955
38887
|
console.log("Publication mode: direct merge");
|
|
38956
38888
|
}
|
|
38957
38889
|
}
|
|
38958
|
-
async function
|
|
38890
|
+
async function run23() {
|
|
38959
38891
|
let options;
|
|
38960
38892
|
try {
|
|
38961
38893
|
options = parseOptions3(process.argv.slice(3));
|
|
@@ -38996,7 +38928,7 @@ var init_end = __esm(() => {
|
|
|
38996
38928
|
// src/cli/stop.ts
|
|
38997
38929
|
var exports_stop = {};
|
|
38998
38930
|
__export(exports_stop, {
|
|
38999
|
-
run: () =>
|
|
38931
|
+
run: () => run24
|
|
39000
38932
|
});
|
|
39001
38933
|
function resolveTerminalStatus(jobId) {
|
|
39002
38934
|
return hasRunCompleteEvent(jobId) ? "done" : "cancelled";
|
|
@@ -39039,7 +38971,7 @@ function tryKillProcessGroup(pid) {
|
|
|
39039
38971
|
throw err;
|
|
39040
38972
|
}
|
|
39041
38973
|
}
|
|
39042
|
-
async function
|
|
38974
|
+
async function run24() {
|
|
39043
38975
|
let parsed;
|
|
39044
38976
|
try {
|
|
39045
38977
|
parsed = parseStopArgs(process.argv.slice(3));
|
|
@@ -39153,18 +39085,18 @@ var init_stop = __esm(() => {
|
|
|
39153
39085
|
// src/cli/attach.ts
|
|
39154
39086
|
var exports_attach = {};
|
|
39155
39087
|
__export(exports_attach, {
|
|
39156
|
-
run: () =>
|
|
39088
|
+
run: () => run25
|
|
39157
39089
|
});
|
|
39158
39090
|
import { execFileSync as execFileSync3, spawnSync as spawnSync21 } from "child_process";
|
|
39159
|
-
import { readFileSync as
|
|
39160
|
-
import { join as
|
|
39091
|
+
import { readFileSync as readFileSync28 } from "fs";
|
|
39092
|
+
import { join as join30 } from "path";
|
|
39161
39093
|
function exitWithError(message) {
|
|
39162
39094
|
console.error(message);
|
|
39163
39095
|
process.exit(1);
|
|
39164
39096
|
}
|
|
39165
39097
|
function readStatus(statusPath, jobId) {
|
|
39166
39098
|
try {
|
|
39167
|
-
return JSON.parse(
|
|
39099
|
+
return JSON.parse(readFileSync28(statusPath, "utf-8"));
|
|
39168
39100
|
} catch (error2) {
|
|
39169
39101
|
if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
|
|
39170
39102
|
exitWithError(`Job \`${jobId}\` not found. Run \`specialists status\` to see active jobs in current mode.`);
|
|
@@ -39173,13 +39105,13 @@ function readStatus(statusPath, jobId) {
|
|
|
39173
39105
|
exitWithError(`Failed to read status for job \`${jobId}\`: ${details}`);
|
|
39174
39106
|
}
|
|
39175
39107
|
}
|
|
39176
|
-
async function
|
|
39108
|
+
async function run25() {
|
|
39177
39109
|
const [jobId] = process.argv.slice(3);
|
|
39178
39110
|
if (!jobId) {
|
|
39179
39111
|
exitWithError("Usage: specialists attach <job-id> (normal runtime is DB-backed; job files are legacy/operator-only)");
|
|
39180
39112
|
}
|
|
39181
|
-
const jobsDir =
|
|
39182
|
-
const statusPath =
|
|
39113
|
+
const jobsDir = join30(process.cwd(), ".specialists", "jobs");
|
|
39114
|
+
const statusPath = join30(jobsDir, jobId, "status.json");
|
|
39183
39115
|
const status = readStatus(statusPath, jobId);
|
|
39184
39116
|
if (status.status === "done" || status.status === "error") {
|
|
39185
39117
|
exitWithError(`Job \`${jobId}\` has already completed (status: ${status.status}). Use \`specialists result ${jobId}\` to read output.`);
|
|
@@ -39201,15 +39133,15 @@ async function run26() {
|
|
|
39201
39133
|
var init_attach = () => {};
|
|
39202
39134
|
|
|
39203
39135
|
// src/specialist/drift-detector.ts
|
|
39204
|
-
import { existsSync as
|
|
39205
|
-
import { join as
|
|
39136
|
+
import { existsSync as existsSync28, readFileSync as readFileSync29, readdirSync as readdirSync13, rmSync as rmSync4 } from "fs";
|
|
39137
|
+
import { join as join31, resolve as resolve10, relative as relative2 } from "path";
|
|
39206
39138
|
function listFiles(root) {
|
|
39207
|
-
if (!
|
|
39139
|
+
if (!existsSync28(root))
|
|
39208
39140
|
return [];
|
|
39209
39141
|
const out = [];
|
|
39210
39142
|
const visit2 = (dir) => {
|
|
39211
39143
|
for (const entry of readdirSync13(dir, { withFileTypes: true })) {
|
|
39212
|
-
const full =
|
|
39144
|
+
const full = join31(dir, entry.name);
|
|
39213
39145
|
if (entry.isDirectory()) {
|
|
39214
39146
|
visit2(full);
|
|
39215
39147
|
continue;
|
|
@@ -39244,14 +39176,14 @@ function detectDriftForRepo(repoRoot) {
|
|
|
39244
39176
|
{ scope: "user", dir: resolve10(repoRoot, ".specialists/user") }
|
|
39245
39177
|
];
|
|
39246
39178
|
for (const { scope, dir } of scopes) {
|
|
39247
|
-
if (!
|
|
39179
|
+
if (!existsSync28(dir))
|
|
39248
39180
|
continue;
|
|
39249
39181
|
for (const file of listFiles(dir)) {
|
|
39250
39182
|
const rel = relPath(file, dir);
|
|
39251
|
-
const canonicalPath =
|
|
39252
|
-
if (!
|
|
39183
|
+
const canonicalPath = join31(asset.canonicalDir, rel);
|
|
39184
|
+
if (!existsSync28(canonicalPath))
|
|
39253
39185
|
continue;
|
|
39254
|
-
const bytesEqual =
|
|
39186
|
+
const bytesEqual = readFileSync29(file).equals(readFileSync29(canonicalPath));
|
|
39255
39187
|
findings.push(makeFinding(repoRoot, asset.kind, scope, file, canonicalPath, bytesEqual));
|
|
39256
39188
|
}
|
|
39257
39189
|
}
|
|
@@ -39275,7 +39207,7 @@ function detectDriftUnderRoot(root) {
|
|
|
39275
39207
|
continue;
|
|
39276
39208
|
if (entry.name === "node_modules" || entry.name === ".git")
|
|
39277
39209
|
continue;
|
|
39278
|
-
visit2(
|
|
39210
|
+
visit2(join31(dir, entry.name));
|
|
39279
39211
|
}
|
|
39280
39212
|
};
|
|
39281
39213
|
visit2(resolve10(root));
|
|
@@ -39315,10 +39247,10 @@ var init_drift_detector = __esm(() => {
|
|
|
39315
39247
|
// src/cli/prune-stale-defaults.ts
|
|
39316
39248
|
var exports_prune_stale_defaults = {};
|
|
39317
39249
|
__export(exports_prune_stale_defaults, {
|
|
39318
|
-
run: () =>
|
|
39250
|
+
run: () => run26
|
|
39319
39251
|
});
|
|
39320
39252
|
import { resolve as resolve11 } from "path";
|
|
39321
|
-
function
|
|
39253
|
+
function parseArgs11(argv) {
|
|
39322
39254
|
let dryRun = false;
|
|
39323
39255
|
let root = process.cwd();
|
|
39324
39256
|
let help = false;
|
|
@@ -39349,8 +39281,8 @@ function printHelp() {
|
|
|
39349
39281
|
console.log(" --dry-run List stale default snapshots without pruning");
|
|
39350
39282
|
console.log(" --root Repo root to scan");
|
|
39351
39283
|
}
|
|
39352
|
-
async function
|
|
39353
|
-
const { dryRun, root, help } =
|
|
39284
|
+
async function run26(argv = process.argv.slice(3)) {
|
|
39285
|
+
const { dryRun, root, help } = parseArgs11(argv);
|
|
39354
39286
|
if (help) {
|
|
39355
39287
|
printHelp();
|
|
39356
39288
|
return;
|
|
@@ -39376,7 +39308,7 @@ var init_prune_stale_defaults = __esm(() => {
|
|
|
39376
39308
|
// src/cli/quickstart.ts
|
|
39377
39309
|
var exports_quickstart = {};
|
|
39378
39310
|
__export(exports_quickstart, {
|
|
39379
|
-
run: () =>
|
|
39311
|
+
run: () => run27
|
|
39380
39312
|
});
|
|
39381
39313
|
function section2(title) {
|
|
39382
39314
|
const bar = "\u2500".repeat(60);
|
|
@@ -39390,7 +39322,7 @@ function cmd2(s) {
|
|
|
39390
39322
|
function flag(s) {
|
|
39391
39323
|
return green13(s);
|
|
39392
39324
|
}
|
|
39393
|
-
async function
|
|
39325
|
+
async function run27() {
|
|
39394
39326
|
const lines = [
|
|
39395
39327
|
"",
|
|
39396
39328
|
bold12("specialists \xB7 Quick Start Guide"),
|
|
@@ -39605,7 +39537,7 @@ var bold12 = (s) => `\x1B[1m${s}\x1B[0m`, dim12 = (s) => `\x1B[2m${s}\x1B[0m`, y
|
|
|
39605
39537
|
var exports_doctor = {};
|
|
39606
39538
|
__export(exports_doctor, {
|
|
39607
39539
|
setStatusError: () => setStatusError,
|
|
39608
|
-
run: () =>
|
|
39540
|
+
run: () => run28,
|
|
39609
39541
|
renderProcessSummary: () => renderProcessSummary,
|
|
39610
39542
|
parseVersionTuple: () => parseVersionTuple,
|
|
39611
39543
|
compareVersions: () => compareVersions2,
|
|
@@ -39613,8 +39545,8 @@ __export(exports_doctor, {
|
|
|
39613
39545
|
});
|
|
39614
39546
|
import { createHash as createHash5 } from "crypto";
|
|
39615
39547
|
import { spawnSync as spawnSync22 } from "child_process";
|
|
39616
|
-
import { existsSync as
|
|
39617
|
-
import { dirname as dirname9, join as
|
|
39548
|
+
import { existsSync as existsSync29, lstatSync as lstatSync2, mkdirSync as mkdirSync10, readdirSync as readdirSync14, readFileSync as readFileSync30, readlinkSync as readlinkSync2, writeFileSync as writeFileSync12 } from "fs";
|
|
39549
|
+
import { dirname as dirname9, join as join32, relative as relative3, resolve as resolve12 } from "path";
|
|
39618
39550
|
function ok3(msg) {
|
|
39619
39551
|
console.log(` ${green14("\u2713")} ${msg}`);
|
|
39620
39552
|
}
|
|
@@ -39643,10 +39575,10 @@ function isInstalled3(bin) {
|
|
|
39643
39575
|
return spawnSync22("which", [bin], { encoding: "utf8", timeout: 2000 }).status === 0;
|
|
39644
39576
|
}
|
|
39645
39577
|
function loadJson2(path) {
|
|
39646
|
-
if (!
|
|
39578
|
+
if (!existsSync29(path))
|
|
39647
39579
|
return null;
|
|
39648
39580
|
try {
|
|
39649
|
-
return JSON.parse(
|
|
39581
|
+
return JSON.parse(readFileSync30(path, "utf8"));
|
|
39650
39582
|
} catch {
|
|
39651
39583
|
return null;
|
|
39652
39584
|
}
|
|
@@ -39689,7 +39621,7 @@ function checkBd() {
|
|
|
39689
39621
|
return false;
|
|
39690
39622
|
}
|
|
39691
39623
|
ok3(`bd installed ${dim13(sp("bd", ["--version"]).stdout || "")}`);
|
|
39692
|
-
if (
|
|
39624
|
+
if (existsSync29(join32(CWD, ".beads")))
|
|
39693
39625
|
ok3(".beads/ present in project");
|
|
39694
39626
|
else
|
|
39695
39627
|
warn3(".beads/ not found in project");
|
|
@@ -39709,15 +39641,15 @@ function checkHooks() {
|
|
|
39709
39641
|
section3("Claude Code hooks (2 expected)");
|
|
39710
39642
|
let allPresent = true;
|
|
39711
39643
|
for (const name of HOOK_NAMES) {
|
|
39712
|
-
const canonicalPath =
|
|
39713
|
-
if (!
|
|
39644
|
+
const canonicalPath = join32(HOOKS_DIR, name);
|
|
39645
|
+
if (!existsSync29(canonicalPath)) {
|
|
39714
39646
|
fail4(`${relative3(CWD, canonicalPath)} ${red7("missing")}`);
|
|
39715
39647
|
fix("specialists init");
|
|
39716
39648
|
allPresent = false;
|
|
39717
39649
|
} else {
|
|
39718
39650
|
ok3(relative3(CWD, canonicalPath));
|
|
39719
39651
|
}
|
|
39720
|
-
const claudeHookPath =
|
|
39652
|
+
const claudeHookPath = join32(CLAUDE_HOOKS_DIR, name);
|
|
39721
39653
|
const symlinkState = isSymlinkTo(claudeHookPath, canonicalPath);
|
|
39722
39654
|
if (symlinkState.ok) {
|
|
39723
39655
|
ok3(`${relative3(CWD, claudeHookPath)} -> ${relative3(dirname9(claudeHookPath), canonicalPath)}`);
|
|
@@ -39792,14 +39724,14 @@ function checkVersion() {
|
|
|
39792
39724
|
}
|
|
39793
39725
|
function hashFile(path) {
|
|
39794
39726
|
const hash = createHash5("sha256");
|
|
39795
|
-
hash.update(
|
|
39727
|
+
hash.update(readFileSync30(path));
|
|
39796
39728
|
return hash.digest("hex");
|
|
39797
39729
|
}
|
|
39798
39730
|
function collectFileHashes(rootDir) {
|
|
39799
39731
|
const hashes = new Map;
|
|
39800
39732
|
const visit2 = (dir) => {
|
|
39801
39733
|
for (const entry of readdirSync14(dir, { withFileTypes: true })) {
|
|
39802
|
-
const fullPath =
|
|
39734
|
+
const fullPath = join32(dir, entry.name);
|
|
39803
39735
|
if (entry.isDirectory()) {
|
|
39804
39736
|
visit2(fullPath);
|
|
39805
39737
|
continue;
|
|
@@ -39810,12 +39742,12 @@ function collectFileHashes(rootDir) {
|
|
|
39810
39742
|
hashes.set(relPath2, hashFile(fullPath));
|
|
39811
39743
|
}
|
|
39812
39744
|
};
|
|
39813
|
-
if (
|
|
39745
|
+
if (existsSync29(rootDir))
|
|
39814
39746
|
visit2(rootDir);
|
|
39815
39747
|
return hashes;
|
|
39816
39748
|
}
|
|
39817
39749
|
function isSymlinkTo(linkPath, expectedTargetPath) {
|
|
39818
|
-
if (!
|
|
39750
|
+
if (!existsSync29(linkPath))
|
|
39819
39751
|
return { ok: false, reason: "missing" };
|
|
39820
39752
|
let stats;
|
|
39821
39753
|
try {
|
|
@@ -39839,12 +39771,12 @@ function isSymlinkTo(linkPath, expectedTargetPath) {
|
|
|
39839
39771
|
}
|
|
39840
39772
|
function checkSkillDrift() {
|
|
39841
39773
|
section3("Skill drift (.xtrm skill sync)");
|
|
39842
|
-
if (!
|
|
39774
|
+
if (!existsSync29(CONFIG_SKILLS_DIR)) {
|
|
39843
39775
|
fail4("config/skills/ missing");
|
|
39844
39776
|
fix("restore config/skills/ from git");
|
|
39845
39777
|
return false;
|
|
39846
39778
|
}
|
|
39847
|
-
if (!
|
|
39779
|
+
if (!existsSync29(XTRM_DEFAULT_SKILLS_DIR)) {
|
|
39848
39780
|
fail4(".xtrm/skills/default/ missing");
|
|
39849
39781
|
fix("specialists init --sync-skills");
|
|
39850
39782
|
return false;
|
|
@@ -39886,8 +39818,8 @@ function checkSkillDrift() {
|
|
|
39886
39818
|
}
|
|
39887
39819
|
let linksOk = true;
|
|
39888
39820
|
for (const scope of ["claude", "pi"]) {
|
|
39889
|
-
const activeRoot =
|
|
39890
|
-
if (!
|
|
39821
|
+
const activeRoot = join32(XTRM_ACTIVE_SKILLS_DIR, scope);
|
|
39822
|
+
if (!existsSync29(activeRoot)) {
|
|
39891
39823
|
fail4(`${relative3(CWD, activeRoot)}/ missing`);
|
|
39892
39824
|
fix("specialists init --sync-skills");
|
|
39893
39825
|
linksOk = false;
|
|
@@ -39895,8 +39827,8 @@ function checkSkillDrift() {
|
|
|
39895
39827
|
}
|
|
39896
39828
|
const defaultSkills = readdirSync14(XTRM_DEFAULT_SKILLS_DIR, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
39897
39829
|
for (const skillName of defaultSkills) {
|
|
39898
|
-
const activeLinkPath =
|
|
39899
|
-
const expectedTarget =
|
|
39830
|
+
const activeLinkPath = join32(activeRoot, skillName);
|
|
39831
|
+
const expectedTarget = join32(XTRM_DEFAULT_SKILLS_DIR, skillName);
|
|
39900
39832
|
const state = isSymlinkTo(activeLinkPath, expectedTarget);
|
|
39901
39833
|
if (state.ok)
|
|
39902
39834
|
continue;
|
|
@@ -39915,8 +39847,8 @@ function checkSkillDrift() {
|
|
|
39915
39847
|
}
|
|
39916
39848
|
}
|
|
39917
39849
|
const skillRootChecks = [
|
|
39918
|
-
{ root:
|
|
39919
|
-
{ root:
|
|
39850
|
+
{ root: join32(CLAUDE_DIR, "skills"), expected: ACTIVE_CLAUDE_SKILLS_DIR },
|
|
39851
|
+
{ root: join32(PI_DIR, "skills"), expected: ACTIVE_PI_SKILLS_DIR }
|
|
39920
39852
|
];
|
|
39921
39853
|
let rootLinksOk = true;
|
|
39922
39854
|
for (const check2 of skillRootChecks) {
|
|
@@ -39941,12 +39873,12 @@ function checkSkillDrift() {
|
|
|
39941
39873
|
return drifted.length === 0 && missingInDefault.length === 0 && linksOk && rootLinksOk;
|
|
39942
39874
|
}
|
|
39943
39875
|
function checkManagedMirror(label, sourceDir, mirrorDir, fixHint) {
|
|
39944
|
-
if (!
|
|
39876
|
+
if (!existsSync29(sourceDir)) {
|
|
39945
39877
|
warn3(`${label} source missing: ${relative3(CWD, sourceDir)}`);
|
|
39946
39878
|
fix(fixHint);
|
|
39947
39879
|
return false;
|
|
39948
39880
|
}
|
|
39949
|
-
if (!
|
|
39881
|
+
if (!existsSync29(mirrorDir)) {
|
|
39950
39882
|
fail4(`${label} mirror missing: ${relative3(CWD, mirrorDir)}`);
|
|
39951
39883
|
fix(fixHint);
|
|
39952
39884
|
return false;
|
|
@@ -39978,13 +39910,13 @@ function checkManagedMirror(label, sourceDir, mirrorDir, fixHint) {
|
|
|
39978
39910
|
function checkManagedAssetMirrors() {
|
|
39979
39911
|
section3("Managed mirrors (specialists / mandatory-rules / nodes)");
|
|
39980
39912
|
const specialistsOk = checkManagedMirror("specialists", CONFIG_SPECIALISTS_DIR, DEFAULT_SPECIALISTS_DIR, "specialists init --sync-defaults");
|
|
39981
|
-
const rulesOk = checkManagedMirror("mandatory-rules", CONFIG_MANDATORY_RULES_DIR,
|
|
39982
|
-
const nodesOk = checkManagedMirror("nodes", CONFIG_NODES_DIR,
|
|
39913
|
+
const rulesOk = checkManagedMirror("mandatory-rules", CONFIG_MANDATORY_RULES_DIR, join32(DEFAULT_SPECIALISTS_DIR, "mandatory-rules"), "specialists init --sync-defaults");
|
|
39914
|
+
const nodesOk = checkManagedMirror("nodes", CONFIG_NODES_DIR, join32(DEFAULT_SPECIALISTS_DIR, "nodes"), "specialists init --sync-defaults");
|
|
39983
39915
|
return specialistsOk && rulesOk && nodesOk;
|
|
39984
39916
|
}
|
|
39985
39917
|
function checkUserOverlayDrift() {
|
|
39986
39918
|
section3("User specialist overlays");
|
|
39987
|
-
if (!
|
|
39919
|
+
if (!existsSync29(USER_SPECIALISTS_DIR)) {
|
|
39988
39920
|
ok3("no user overlays present");
|
|
39989
39921
|
return true;
|
|
39990
39922
|
}
|
|
@@ -39995,14 +39927,14 @@ function checkUserOverlayDrift() {
|
|
|
39995
39927
|
}
|
|
39996
39928
|
let allOk = true;
|
|
39997
39929
|
for (const name of overlays) {
|
|
39998
|
-
const userPath =
|
|
39999
|
-
const defaultPath =
|
|
39930
|
+
const userPath = join32(USER_SPECIALISTS_DIR, name);
|
|
39931
|
+
const defaultPath = join32(DEFAULT_SPECIALISTS_DIR, name);
|
|
40000
39932
|
const userSpec = loadJson2(userPath);
|
|
40001
39933
|
if (!userSpec) {
|
|
40002
39934
|
warn3(`${name}: failed to parse \u2014 skipping drift check`);
|
|
40003
39935
|
continue;
|
|
40004
39936
|
}
|
|
40005
|
-
if (!
|
|
39937
|
+
if (!existsSync29(defaultPath)) {
|
|
40006
39938
|
ok3(`${name}: user-only overlay (no default to drift from)`);
|
|
40007
39939
|
continue;
|
|
40008
39940
|
}
|
|
@@ -40030,18 +39962,18 @@ function checkUserOverlayDrift() {
|
|
|
40030
39962
|
}
|
|
40031
39963
|
function checkRuntimeDirs() {
|
|
40032
39964
|
section3(".specialists/ runtime directories");
|
|
40033
|
-
const rootDir =
|
|
40034
|
-
const jobsDir =
|
|
40035
|
-
const readyDir =
|
|
39965
|
+
const rootDir = join32(CWD, ".specialists");
|
|
39966
|
+
const jobsDir = join32(rootDir, "jobs");
|
|
39967
|
+
const readyDir = join32(rootDir, "ready");
|
|
40036
39968
|
let allOk = true;
|
|
40037
|
-
if (!
|
|
39969
|
+
if (!existsSync29(rootDir)) {
|
|
40038
39970
|
warn3(".specialists/ not found in current project");
|
|
40039
39971
|
fix("specialists init");
|
|
40040
39972
|
allOk = false;
|
|
40041
39973
|
} else {
|
|
40042
39974
|
ok3(".specialists/ present");
|
|
40043
39975
|
for (const [subDir, label] of [[jobsDir, "jobs"], [readyDir, "ready"]]) {
|
|
40044
|
-
if (!
|
|
39976
|
+
if (!existsSync29(subDir)) {
|
|
40045
39977
|
warn3(`.specialists/${label}/ missing \u2014 auto-creating`);
|
|
40046
39978
|
mkdirSync10(subDir, { recursive: true });
|
|
40047
39979
|
ok3(`.specialists/${label}/ created`);
|
|
@@ -40055,8 +39987,8 @@ function checkRuntimeDirs() {
|
|
|
40055
39987
|
function checkClaudeMdFragments() {
|
|
40056
39988
|
section3("CLAUDE.md fragments");
|
|
40057
39989
|
const projectRoot = process.cwd();
|
|
40058
|
-
const claudeMd =
|
|
40059
|
-
if (!
|
|
39990
|
+
const claudeMd = join32(projectRoot, "CLAUDE.md");
|
|
39991
|
+
if (!existsSync29(claudeMd)) {
|
|
40060
39992
|
warn3("No CLAUDE.md in project root \u2014 skipping fragment check");
|
|
40061
39993
|
return true;
|
|
40062
39994
|
}
|
|
@@ -40182,7 +40114,7 @@ function compareVersions2(left, right) {
|
|
|
40182
40114
|
}
|
|
40183
40115
|
function setStatusError(statusPath) {
|
|
40184
40116
|
try {
|
|
40185
|
-
const raw =
|
|
40117
|
+
const raw = readFileSync30(statusPath, "utf8");
|
|
40186
40118
|
const status = JSON.parse(raw);
|
|
40187
40119
|
status.status = "error";
|
|
40188
40120
|
writeFileSync12(statusPath, `${JSON.stringify(status, null, 2)}
|
|
@@ -40236,11 +40168,11 @@ function cleanupProcesses(jobsDir, dryRun) {
|
|
|
40236
40168
|
zombieJobIds: []
|
|
40237
40169
|
};
|
|
40238
40170
|
for (const jobId of entries) {
|
|
40239
|
-
const statusPath =
|
|
40240
|
-
if (!
|
|
40171
|
+
const statusPath = join32(jobsDir, jobId, "status.json");
|
|
40172
|
+
if (!existsSync29(statusPath))
|
|
40241
40173
|
continue;
|
|
40242
40174
|
try {
|
|
40243
|
-
const status = JSON.parse(
|
|
40175
|
+
const status = JSON.parse(readFileSync30(statusPath, "utf8"));
|
|
40244
40176
|
result.total += 1;
|
|
40245
40177
|
if (status.status !== "running" && status.status !== "starting")
|
|
40246
40178
|
continue;
|
|
@@ -40325,8 +40257,8 @@ function resolveWatchdogMode() {
|
|
|
40325
40257
|
function checkZombieJobs() {
|
|
40326
40258
|
section3("Background jobs");
|
|
40327
40259
|
hint(`watchdog mode: ${resolveWatchdogMode()}`);
|
|
40328
|
-
const jobsDir =
|
|
40329
|
-
if (!
|
|
40260
|
+
const jobsDir = join32(CWD, ".specialists", "jobs");
|
|
40261
|
+
if (!existsSync29(jobsDir)) {
|
|
40330
40262
|
hint("No .specialists/jobs/ \u2014 skipping");
|
|
40331
40263
|
return true;
|
|
40332
40264
|
}
|
|
@@ -40344,7 +40276,7 @@ function checkZombieJobs() {
|
|
|
40344
40276
|
}
|
|
40345
40277
|
return result.zombies === 0;
|
|
40346
40278
|
}
|
|
40347
|
-
async function
|
|
40279
|
+
async function run28(argv = process.argv.slice(3)) {
|
|
40348
40280
|
const subcommand = argv[0];
|
|
40349
40281
|
if (subcommand === "orphans") {
|
|
40350
40282
|
runDoctorOrphans();
|
|
@@ -40391,24 +40323,24 @@ var init_doctor = __esm(() => {
|
|
|
40391
40323
|
init_drift_detector();
|
|
40392
40324
|
init_version_check();
|
|
40393
40325
|
CWD = process.cwd();
|
|
40394
|
-
CLAUDE_DIR =
|
|
40395
|
-
PI_DIR =
|
|
40396
|
-
XTRM_SKILLS_DIR =
|
|
40397
|
-
XTRM_DEFAULT_SKILLS_DIR =
|
|
40398
|
-
XTRM_ACTIVE_SKILLS_DIR =
|
|
40399
|
-
ACTIVE_CLAUDE_SKILLS_DIR =
|
|
40400
|
-
ACTIVE_PI_SKILLS_DIR =
|
|
40401
|
-
CONFIG_SKILLS_DIR =
|
|
40402
|
-
CONFIG_SPECIALISTS_DIR =
|
|
40403
|
-
CONFIG_MANDATORY_RULES_DIR =
|
|
40404
|
-
CONFIG_NODES_DIR =
|
|
40405
|
-
SPECIALISTS_DIR =
|
|
40406
|
-
DEFAULT_SPECIALISTS_DIR =
|
|
40407
|
-
USER_SPECIALISTS_DIR =
|
|
40408
|
-
HOOKS_DIR =
|
|
40409
|
-
CLAUDE_HOOKS_DIR =
|
|
40410
|
-
SETTINGS_FILE =
|
|
40411
|
-
MCP_FILE2 =
|
|
40326
|
+
CLAUDE_DIR = join32(CWD, ".claude");
|
|
40327
|
+
PI_DIR = join32(CWD, ".pi");
|
|
40328
|
+
XTRM_SKILLS_DIR = join32(CWD, ".xtrm", "skills");
|
|
40329
|
+
XTRM_DEFAULT_SKILLS_DIR = join32(XTRM_SKILLS_DIR, "default");
|
|
40330
|
+
XTRM_ACTIVE_SKILLS_DIR = join32(XTRM_SKILLS_DIR, "active");
|
|
40331
|
+
ACTIVE_CLAUDE_SKILLS_DIR = join32(XTRM_ACTIVE_SKILLS_DIR, "claude");
|
|
40332
|
+
ACTIVE_PI_SKILLS_DIR = join32(XTRM_ACTIVE_SKILLS_DIR, "pi");
|
|
40333
|
+
CONFIG_SKILLS_DIR = join32(CWD, "config", "skills");
|
|
40334
|
+
CONFIG_SPECIALISTS_DIR = join32(CWD, "config", "specialists");
|
|
40335
|
+
CONFIG_MANDATORY_RULES_DIR = join32(CWD, "config", "mandatory-rules");
|
|
40336
|
+
CONFIG_NODES_DIR = join32(CWD, "config", "nodes");
|
|
40337
|
+
SPECIALISTS_DIR = join32(CWD, ".specialists");
|
|
40338
|
+
DEFAULT_SPECIALISTS_DIR = join32(SPECIALISTS_DIR, "default");
|
|
40339
|
+
USER_SPECIALISTS_DIR = join32(SPECIALISTS_DIR, "user");
|
|
40340
|
+
HOOKS_DIR = join32(CWD, ".xtrm", "hooks", "specialists");
|
|
40341
|
+
CLAUDE_HOOKS_DIR = join32(CLAUDE_DIR, "hooks");
|
|
40342
|
+
SETTINGS_FILE = join32(CLAUDE_DIR, "settings.json");
|
|
40343
|
+
MCP_FILE2 = join32(CWD, ".mcp.json");
|
|
40412
40344
|
HOOK_NAMES = [
|
|
40413
40345
|
"specialists-complete.mjs",
|
|
40414
40346
|
"specialists-session-start.mjs"
|
|
@@ -40418,9 +40350,9 @@ var init_doctor = __esm(() => {
|
|
|
40418
40350
|
// src/cli/setup.ts
|
|
40419
40351
|
var exports_setup = {};
|
|
40420
40352
|
__export(exports_setup, {
|
|
40421
|
-
run: () =>
|
|
40353
|
+
run: () => run29
|
|
40422
40354
|
});
|
|
40423
|
-
async function
|
|
40355
|
+
async function run29() {
|
|
40424
40356
|
console.log("");
|
|
40425
40357
|
console.log(yellow13("\u26A0 DEPRECATED: `specialists setup` is deprecated"));
|
|
40426
40358
|
console.log("");
|
|
@@ -40441,20 +40373,20 @@ async function run30() {
|
|
|
40441
40373
|
var bold14 = (s) => `\x1B[1m${s}\x1B[0m`, yellow13 = (s) => `\x1B[33m${s}\x1B[0m`, dim14 = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
40442
40374
|
|
|
40443
40375
|
// src/cli/serve-hot-reload.ts
|
|
40444
|
-
import { existsSync as
|
|
40445
|
-
import { join as
|
|
40376
|
+
import { existsSync as existsSync30, readdirSync as readdirSync15, statSync as statSync5, watch as fsWatch } from "fs";
|
|
40377
|
+
import { join as join33 } from "path";
|
|
40446
40378
|
function specialistNameFromFile(file) {
|
|
40447
40379
|
const match = file.match(/^(.+)\.specialist\.(json|yaml)$/);
|
|
40448
40380
|
return match ? match[1] : null;
|
|
40449
40381
|
}
|
|
40450
40382
|
function snapshotMtimes(dir) {
|
|
40451
40383
|
const out = new Map;
|
|
40452
|
-
if (!
|
|
40384
|
+
if (!existsSync30(dir))
|
|
40453
40385
|
return out;
|
|
40454
40386
|
const entries = readdirSync15(dir).filter((name) => specialistNameFromFile(name) !== null);
|
|
40455
40387
|
for (const name of entries) {
|
|
40456
40388
|
try {
|
|
40457
|
-
out.set(name, statSync5(
|
|
40389
|
+
out.set(name, statSync5(join33(dir, name)).mtimeMs);
|
|
40458
40390
|
} catch {}
|
|
40459
40391
|
}
|
|
40460
40392
|
return out;
|
|
@@ -40511,7 +40443,7 @@ function createUserDirWatcher(opts) {
|
|
|
40511
40443
|
for (const file of changed)
|
|
40512
40444
|
queue(file);
|
|
40513
40445
|
}, opts.pollMs);
|
|
40514
|
-
} else if (
|
|
40446
|
+
} else if (existsSync30(opts.userDir)) {
|
|
40515
40447
|
try {
|
|
40516
40448
|
watcher = fsWatch(opts.userDir, { persistent: false }, (_eventType, filename) => {
|
|
40517
40449
|
queue(filename ? String(filename) : null);
|
|
@@ -40543,7 +40475,7 @@ var init_serve_hot_reload = () => {};
|
|
|
40543
40475
|
var exports_serve = {};
|
|
40544
40476
|
__export(exports_serve, {
|
|
40545
40477
|
startServe: () => startServe,
|
|
40546
|
-
run: () =>
|
|
40478
|
+
run: () => run30,
|
|
40547
40479
|
recordAuditFailure: () => recordAuditFailure,
|
|
40548
40480
|
evaluateReadiness: () => evaluateReadiness2,
|
|
40549
40481
|
createReadinessState: () => createReadinessState
|
|
@@ -40551,9 +40483,9 @@ __export(exports_serve, {
|
|
|
40551
40483
|
import { createServer } from "http";
|
|
40552
40484
|
import { once } from "events";
|
|
40553
40485
|
import { access, readdir as readdir2, readFile as readFile5, constants } from "fs/promises";
|
|
40554
|
-
import { existsSync as
|
|
40486
|
+
import { existsSync as existsSync31 } from "fs";
|
|
40555
40487
|
import { homedir as homedir3 } from "os";
|
|
40556
|
-
import { join as
|
|
40488
|
+
import { join as join34 } from "path";
|
|
40557
40489
|
function createReadinessState() {
|
|
40558
40490
|
return { shuttingDown: false, auditFailures: [], dbWriteFailuresTotal: 0 };
|
|
40559
40491
|
}
|
|
@@ -40569,7 +40501,7 @@ function pruneAuditFailures(state, now = Date.now()) {
|
|
|
40569
40501
|
}
|
|
40570
40502
|
}
|
|
40571
40503
|
async function checkUserDirSpecs(userDir) {
|
|
40572
|
-
if (!
|
|
40504
|
+
if (!existsSync31(userDir))
|
|
40573
40505
|
return "empty";
|
|
40574
40506
|
const entries = await readdir2(userDir).catch(() => []);
|
|
40575
40507
|
const specFiles = entries.filter((name) => name.endsWith(".specialist.json") || name.endsWith(".specialist.yaml"));
|
|
@@ -40578,7 +40510,7 @@ async function checkUserDirSpecs(userDir) {
|
|
|
40578
40510
|
let validCount = 0;
|
|
40579
40511
|
for (const file of specFiles) {
|
|
40580
40512
|
try {
|
|
40581
|
-
const content = await readFile5(
|
|
40513
|
+
const content = await readFile5(join34(userDir, file), "utf-8");
|
|
40582
40514
|
const json = file.endsWith(".json") ? content : null;
|
|
40583
40515
|
if (!json)
|
|
40584
40516
|
continue;
|
|
@@ -40596,7 +40528,7 @@ async function evaluateReadiness2(opts) {
|
|
|
40596
40528
|
if (opts.state.auditFailures.length > opts.auditFailureThreshold) {
|
|
40597
40529
|
return { ready: false, reason: "degraded:audit" };
|
|
40598
40530
|
}
|
|
40599
|
-
const piConfigPath = opts.piConfigPath ??
|
|
40531
|
+
const piConfigPath = opts.piConfigPath ?? join34(homedir3(), ".pi", "agent", "auth.json");
|
|
40600
40532
|
try {
|
|
40601
40533
|
await access(piConfigPath, constants.R_OK);
|
|
40602
40534
|
} catch {
|
|
@@ -40607,7 +40539,7 @@ async function evaluateReadiness2(opts) {
|
|
|
40607
40539
|
} catch {
|
|
40608
40540
|
return { ready: false, reason: "db_not_writable" };
|
|
40609
40541
|
}
|
|
40610
|
-
const userDir =
|
|
40542
|
+
const userDir = join34(opts.projectDir, ".specialists", "user");
|
|
40611
40543
|
const userDirResult = await checkUserDirSpecs(userDir);
|
|
40612
40544
|
if (userDirResult === "empty")
|
|
40613
40545
|
return { ready: false, reason: "empty_user_dir" };
|
|
@@ -40615,7 +40547,7 @@ async function evaluateReadiness2(opts) {
|
|
|
40615
40547
|
return { ready: false, reason: "invalid_spec_in_user_dir" };
|
|
40616
40548
|
return { ready: true };
|
|
40617
40549
|
}
|
|
40618
|
-
function
|
|
40550
|
+
function parseArgs12(argv) {
|
|
40619
40551
|
let port = 8000;
|
|
40620
40552
|
let concurrency = 4;
|
|
40621
40553
|
let queueTimeoutMs = 5000;
|
|
@@ -40677,13 +40609,13 @@ async function waitForSlot(limit, timeoutMs, getActive) {
|
|
|
40677
40609
|
return true;
|
|
40678
40610
|
}
|
|
40679
40611
|
async function startServe(argv = process.argv.slice(3)) {
|
|
40680
|
-
const args =
|
|
40612
|
+
const args = parseArgs12(argv);
|
|
40681
40613
|
const loader = new SpecialistLoader({ projectDir: args.projectDir });
|
|
40682
40614
|
const dbLocation = resolveObservabilityDbLocation(args.projectDir);
|
|
40683
40615
|
ensureObservabilityDbFile(dbLocation);
|
|
40684
40616
|
const db = createObservabilitySqliteClient(args.projectDir);
|
|
40685
40617
|
const readinessState = createReadinessState();
|
|
40686
|
-
const userDir =
|
|
40618
|
+
const userDir = join34(args.projectDir, ".specialists", "user");
|
|
40687
40619
|
const hotReload = createUserDirWatcher({ loader, userDir, pollMs: args.reloadPollMs });
|
|
40688
40620
|
let active = 0;
|
|
40689
40621
|
const children = new Set;
|
|
@@ -40768,7 +40700,7 @@ async function startServe(argv = process.argv.slice(3)) {
|
|
|
40768
40700
|
console.log(`sp serve listening on ${args.port}`);
|
|
40769
40701
|
return { server, args, db, readinessState };
|
|
40770
40702
|
}
|
|
40771
|
-
async function
|
|
40703
|
+
async function run30(argv = process.argv.slice(3)) {
|
|
40772
40704
|
await startServe(argv);
|
|
40773
40705
|
}
|
|
40774
40706
|
var AUDIT_WINDOW_MS = 60000;
|
|
@@ -40785,8 +40717,8 @@ var init_serve = __esm(() => {
|
|
|
40785
40717
|
var exports_script = {};
|
|
40786
40718
|
__export(exports_script, {
|
|
40787
40719
|
scriptCli: () => scriptCli,
|
|
40788
|
-
run: () =>
|
|
40789
|
-
parseArgs: () =>
|
|
40720
|
+
run: () => run31,
|
|
40721
|
+
parseArgs: () => parseArgs13,
|
|
40790
40722
|
mapExitCode: () => mapExitCode
|
|
40791
40723
|
});
|
|
40792
40724
|
import { spawnSync as spawnSync23 } from "child_process";
|
|
@@ -40796,7 +40728,7 @@ function parseVar(entry) {
|
|
|
40796
40728
|
throw new Error(`Invalid --vars entry: ${entry}`);
|
|
40797
40729
|
return [entry.slice(0, index), entry.slice(index + 1)];
|
|
40798
40730
|
}
|
|
40799
|
-
function
|
|
40731
|
+
function parseArgs13(argv) {
|
|
40800
40732
|
if (argv.length === 0)
|
|
40801
40733
|
throw new Error("Missing specialist name");
|
|
40802
40734
|
const specialist = argv[0];
|
|
@@ -40902,8 +40834,8 @@ function runUnderLock(lockPath, argv) {
|
|
|
40902
40834
|
return 75;
|
|
40903
40835
|
return flock.status ?? 1;
|
|
40904
40836
|
}
|
|
40905
|
-
async function
|
|
40906
|
-
const args =
|
|
40837
|
+
async function run31(argv = process.argv.slice(3)) {
|
|
40838
|
+
const args = parseArgs13(argv);
|
|
40907
40839
|
if (args.singleInstance && !process.env.SP_SCRIPT_NO_LOCK) {
|
|
40908
40840
|
process.exit(runUnderLock(args.singleInstance, argv));
|
|
40909
40841
|
}
|
|
@@ -40916,19 +40848,19 @@ var scriptCli;
|
|
|
40916
40848
|
var init_script = __esm(() => {
|
|
40917
40849
|
init_loader();
|
|
40918
40850
|
init_script_runner();
|
|
40919
|
-
scriptCli = { parseArgs:
|
|
40851
|
+
scriptCli = { parseArgs: parseArgs13, mapExitCode };
|
|
40920
40852
|
});
|
|
40921
40853
|
|
|
40922
40854
|
// src/cli/help.ts
|
|
40923
40855
|
var exports_help = {};
|
|
40924
40856
|
__export(exports_help, {
|
|
40925
|
-
run: () =>
|
|
40857
|
+
run: () => run32
|
|
40926
40858
|
});
|
|
40927
40859
|
function formatCommands(entries) {
|
|
40928
40860
|
const width = Math.max(...entries.map(([cmd3]) => cmd3.length));
|
|
40929
40861
|
return entries.map(([cmd3, desc]) => ` ${cmd3.padEnd(width)} ${desc}`);
|
|
40930
40862
|
}
|
|
40931
|
-
async function
|
|
40863
|
+
async function run32() {
|
|
40932
40864
|
const lines = [
|
|
40933
40865
|
"",
|
|
40934
40866
|
"Specialists lets you run project-scoped specialist agents with a bead-first workflow.",
|
|
@@ -40964,7 +40896,7 @@ async function run33() {
|
|
|
40964
40896
|
" Async patterns",
|
|
40965
40897
|
" MCP: use_specialist (foreground, returns result directly)",
|
|
40966
40898
|
' CLI: specialists run <name> --prompt "..." # job ID prints on stderr',
|
|
40967
|
-
" specialists feed|
|
|
40899
|
+
" specialists ps|feed|result <job-id> # observe/progress/final output",
|
|
40968
40900
|
' Shell: specialists run <name> --prompt "..." & # native shell backgrounding',
|
|
40969
40901
|
"",
|
|
40970
40902
|
" Background workflow",
|
|
@@ -41014,7 +40946,6 @@ async function run33() {
|
|
|
41014
40946
|
" specialists view --help View specialist configs",
|
|
41015
40947
|
" specialists edit --help Edit specialist fields (dot-path, presets)",
|
|
41016
40948
|
" specialists run --help Run command details and flags",
|
|
41017
|
-
" specialists poll --help [DEPRECATED] use sp ps + sp feed",
|
|
41018
40949
|
" specialists steer --help Mid-run steering details",
|
|
41019
40950
|
" specialists resume --help Multi-turn keep-alive details",
|
|
41020
40951
|
" specialists init --help Bootstrap behavior and workflow injection",
|
|
@@ -41042,7 +40973,6 @@ var init_help = __esm(() => {
|
|
|
41042
40973
|
["node", "Run and inspect NodeSupervisor nodes (run/status)"],
|
|
41043
40974
|
["epic", "Epic lifecycle management: list/status/resolve wave-bound chain groups"],
|
|
41044
40975
|
["feed", "Tail job events; use -f to follow all jobs"],
|
|
41045
|
-
["poll", "Machine-readable job status polling (for scripts/Claude Code)"],
|
|
41046
40976
|
["result", "Print final output of a completed job; --wait polls until done, --timeout <ms> sets a limit"],
|
|
41047
40977
|
["clean", "Purge completed job directories (TTL, --all, --keep, --dry-run)"],
|
|
41048
40978
|
["merge", "Publish one standalone chain (refuses unresolved epic chains)"],
|
|
@@ -44857,7 +44787,7 @@ var AssertObjectSchema = custom2((v) => v !== null && (typeof v === "object" ||
|
|
|
44857
44787
|
var ProgressTokenSchema = union([string2(), number2().int()]);
|
|
44858
44788
|
var CursorSchema = string2();
|
|
44859
44789
|
var TaskCreationParamsSchema = looseObject({
|
|
44860
|
-
ttl:
|
|
44790
|
+
ttl: number2().optional(),
|
|
44861
44791
|
pollInterval: number2().optional()
|
|
44862
44792
|
});
|
|
44863
44793
|
var TaskMetadataSchema = object2({
|
|
@@ -45011,7 +44941,8 @@ var ClientCapabilitiesSchema = object2({
|
|
|
45011
44941
|
roots: object2({
|
|
45012
44942
|
listChanged: boolean2().optional()
|
|
45013
44943
|
}).optional(),
|
|
45014
|
-
tasks: ClientTasksCapabilitySchema.optional()
|
|
44944
|
+
tasks: ClientTasksCapabilitySchema.optional(),
|
|
44945
|
+
extensions: record(string2(), AssertObjectSchema).optional()
|
|
45015
44946
|
});
|
|
45016
44947
|
var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
45017
44948
|
protocolVersion: string2(),
|
|
@@ -45036,7 +44967,8 @@ var ServerCapabilitiesSchema = object2({
|
|
|
45036
44967
|
tools: object2({
|
|
45037
44968
|
listChanged: boolean2().optional()
|
|
45038
44969
|
}).optional(),
|
|
45039
|
-
tasks: ServerTasksCapabilitySchema.optional()
|
|
44970
|
+
tasks: ServerTasksCapabilitySchema.optional(),
|
|
44971
|
+
extensions: record(string2(), AssertObjectSchema).optional()
|
|
45040
44972
|
});
|
|
45041
44973
|
var InitializeResultSchema = ResultSchema.extend({
|
|
45042
44974
|
protocolVersion: string2(),
|
|
@@ -45151,6 +45083,7 @@ var ResourceSchema = object2({
|
|
|
45151
45083
|
uri: string2(),
|
|
45152
45084
|
description: optional(string2()),
|
|
45153
45085
|
mimeType: optional(string2()),
|
|
45086
|
+
size: optional(number2()),
|
|
45154
45087
|
annotations: AnnotationsSchema.optional(),
|
|
45155
45088
|
_meta: optional(looseObject({}))
|
|
45156
45089
|
});
|
|
@@ -47146,6 +47079,10 @@ class Protocol {
|
|
|
47146
47079
|
this._progressHandlers.clear();
|
|
47147
47080
|
this._taskProgressTokens.clear();
|
|
47148
47081
|
this._pendingDebouncedNotifications.clear();
|
|
47082
|
+
for (const info of this._timeoutInfo.values()) {
|
|
47083
|
+
clearTimeout(info.timeoutId);
|
|
47084
|
+
}
|
|
47085
|
+
this._timeoutInfo.clear();
|
|
47149
47086
|
for (const controller of this._requestHandlerAbortControllers.values()) {
|
|
47150
47087
|
controller.abort();
|
|
47151
47088
|
}
|
|
@@ -47276,7 +47213,9 @@ class Protocol {
|
|
|
47276
47213
|
await capturedTransport?.send(errorResponse);
|
|
47277
47214
|
}
|
|
47278
47215
|
}).catch((error2) => this._onerror(new Error(`Failed to send response: ${error2}`))).finally(() => {
|
|
47279
|
-
this._requestHandlerAbortControllers.
|
|
47216
|
+
if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
|
|
47217
|
+
this._requestHandlerAbortControllers.delete(request.id);
|
|
47218
|
+
}
|
|
47280
47219
|
});
|
|
47281
47220
|
}
|
|
47282
47221
|
_onprogress(notification) {
|
|
@@ -48578,7 +48517,7 @@ var next = process.argv[3];
|
|
|
48578
48517
|
function wantsHelp() {
|
|
48579
48518
|
return next === "--help" || next === "-h";
|
|
48580
48519
|
}
|
|
48581
|
-
async function
|
|
48520
|
+
async function run33() {
|
|
48582
48521
|
if (sub === "install") {
|
|
48583
48522
|
if (wantsHelp()) {
|
|
48584
48523
|
console.log([
|
|
@@ -48963,7 +48902,7 @@ async function run34() {
|
|
|
48963
48902
|
"",
|
|
48964
48903
|
"Async execution patterns:",
|
|
48965
48904
|
" MCP: use_specialist (foreground, returns result directly)",
|
|
48966
|
-
" CLI: run prints [job started: <id>] on stderr, then use feed/
|
|
48905
|
+
" CLI: run prints [job started: <id>] on stderr, then use ps/feed/result",
|
|
48967
48906
|
' Shell: specialists run <name> --prompt "..." &',
|
|
48968
48907
|
""
|
|
48969
48908
|
].join(`
|
|
@@ -49201,53 +49140,6 @@ async function run34() {
|
|
|
49201
49140
|
const { run: handler } = await Promise.resolve().then(() => (init_feed(), exports_feed));
|
|
49202
49141
|
return handler();
|
|
49203
49142
|
}
|
|
49204
|
-
if (sub === "poll") {
|
|
49205
|
-
if (wantsHelp()) {
|
|
49206
|
-
console.log([
|
|
49207
|
-
"",
|
|
49208
|
-
"Usage: specialists poll <job-id> [--cursor N] [--json]",
|
|
49209
|
-
"",
|
|
49210
|
-
"[DEPRECATED] Scheduled for removal. Use:",
|
|
49211
|
-
" sp ps <id> --json for status",
|
|
49212
|
-
" sp feed <id> for events",
|
|
49213
|
-
"",
|
|
49214
|
-
"Machine-readable job status polling for scripts and Claude Code.",
|
|
49215
|
-
"Currently file-based; replacements above are DB-canonical.",
|
|
49216
|
-
"",
|
|
49217
|
-
"Output (JSON mode):",
|
|
49218
|
-
" {",
|
|
49219
|
-
' "job_id": "abc123",',
|
|
49220
|
-
' "status": "running" | "done" | "error" | "waiting",',
|
|
49221
|
-
' "elapsed_ms": 45000,',
|
|
49222
|
-
' "cursor": 15,',
|
|
49223
|
-
' "events": [...], // new events since cursor',
|
|
49224
|
-
' "output": "...", // full output when done',
|
|
49225
|
-
' "model": "claude-sonnet-4-6",',
|
|
49226
|
-
' "bead_id": "unitAI-123"',
|
|
49227
|
-
" }",
|
|
49228
|
-
"",
|
|
49229
|
-
"Options:",
|
|
49230
|
-
" --cursor N Event index to start from (default: 0)",
|
|
49231
|
-
" --json Output as JSON (machine-readable)",
|
|
49232
|
-
"",
|
|
49233
|
-
"Examples:",
|
|
49234
|
-
" specialists poll abc123 --json",
|
|
49235
|
-
" specialists poll abc123 --cursor 5 --json",
|
|
49236
|
-
"",
|
|
49237
|
-
"Polling pattern in Claude Code:",
|
|
49238
|
-
" 1. Start job (blocks until done):",
|
|
49239
|
-
" specialists run planner --bead xtrm-p38n.1",
|
|
49240
|
-
" 2. Or use Claude Code native backgrounding",
|
|
49241
|
-
" 3. Poll for incremental status:",
|
|
49242
|
-
" specialists poll <job-id> --json",
|
|
49243
|
-
""
|
|
49244
|
-
].join(`
|
|
49245
|
-
`));
|
|
49246
|
-
return;
|
|
49247
|
-
}
|
|
49248
|
-
const { run: pollHandler } = await Promise.resolve().then(() => (init_poll(), exports_poll));
|
|
49249
|
-
return pollHandler();
|
|
49250
|
-
}
|
|
49251
49143
|
if (sub === "steer") {
|
|
49252
49144
|
if (wantsHelp()) {
|
|
49253
49145
|
console.log([
|
|
@@ -49587,7 +49479,7 @@ Run 'specialists help' to see available commands.`);
|
|
|
49587
49479
|
const server = new SpecialistsServer;
|
|
49588
49480
|
await server.start();
|
|
49589
49481
|
}
|
|
49590
|
-
|
|
49482
|
+
run33().catch((error2) => {
|
|
49591
49483
|
logger.error(`Fatal error: ${error2}`);
|
|
49592
49484
|
process.exit(1);
|
|
49593
49485
|
});
|