@jaggerxtrm/specialists 3.12.0 → 3.14.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-script-specialists/SKILL.md +7 -5
- package/config/skills/using-specialists-v2/SKILL.md +1 -1
- package/config/skills/using-specialists-v3/SKILL.md +530 -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 +634 -498
- package/dist/lib.js +183 -62
- package/dist/types/cli/help.d.ts.map +1 -1
- package/dist/types/cli/run.d.ts.map +1 -1
- package/dist/types/cli/script.d.ts.map +1 -1
- package/dist/types/cli/serve.d.ts +11 -2
- package/dist/types/cli/serve.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/dist/types/specialist/observability-sqlite.d.ts +1 -0
- package/dist/types/specialist/observability-sqlite.d.ts.map +1 -1
- package/dist/types/specialist/schema.d.ts +27 -0
- package/dist/types/specialist/schema.d.ts.map +1 -1
- package/dist/types/specialist/script-runner.d.ts +5 -1
- package/dist/types/specialist/script-runner.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")) {
|
|
@@ -17524,6 +17561,7 @@ var init_schema = __esm(() => {
|
|
|
17524
17561
|
max_retries: numberType().int().min(0).default(0),
|
|
17525
17562
|
interactive: booleanType().default(false),
|
|
17526
17563
|
stdout_limit_bytes: numberType().int().positive().optional(),
|
|
17564
|
+
prompt_limit_bytes: numberType().int().positive().optional(),
|
|
17527
17565
|
response_format: enumType(["text", "json", "markdown"]).default("text"),
|
|
17528
17566
|
output_type: enumType(["codegen", "analysis", "review", "synthesis", "orchestration", "workflow", "research", "custom"]).default("custom"),
|
|
17529
17567
|
permission_required: enumType(["READ_ONLY", "LOW", "MEDIUM", "HIGH"]).default("READ_ONLY"),
|
|
@@ -19119,8 +19157,8 @@ function resolveCurrentBranch(cwd = process.cwd()) {
|
|
|
19119
19157
|
var init_job_root = () => {};
|
|
19120
19158
|
|
|
19121
19159
|
// src/specialist/observability-sqlite.ts
|
|
19122
|
-
import { existsSync as existsSync5, readFileSync as readFileSync3, statSync } from "fs";
|
|
19123
|
-
import { join as join5 } from "path";
|
|
19160
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, statSync } from "fs";
|
|
19161
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
19124
19162
|
function loadBunDatabase() {
|
|
19125
19163
|
if (_probed)
|
|
19126
19164
|
return _BunDatabase;
|
|
@@ -21183,23 +21221,30 @@ function hasRunCompleteEvent(jobId, cwd = process.cwd()) {
|
|
|
21183
21221
|
}
|
|
21184
21222
|
return false;
|
|
21185
21223
|
}
|
|
21186
|
-
function
|
|
21224
|
+
function openObservabilitySqliteClient(dbPath) {
|
|
21187
21225
|
if (!loadBunDatabase())
|
|
21188
21226
|
return null;
|
|
21189
|
-
const location = resolveObservabilityDbLocation(cwd);
|
|
21190
|
-
if (!existsSync5(location.dbPath))
|
|
21191
|
-
return null;
|
|
21192
21227
|
try {
|
|
21193
21228
|
const Ctor = loadBunDatabase();
|
|
21194
|
-
const initDb = new Ctor(
|
|
21229
|
+
const initDb = new Ctor(dbPath);
|
|
21195
21230
|
initDb.run(`PRAGMA busy_timeout=${BUSY_TIMEOUT_MS}`);
|
|
21196
21231
|
initSchema(initDb);
|
|
21197
21232
|
initDb.close();
|
|
21198
|
-
return new SqliteClient(
|
|
21233
|
+
return new SqliteClient(dbPath);
|
|
21199
21234
|
} catch {
|
|
21200
21235
|
return null;
|
|
21201
21236
|
}
|
|
21202
21237
|
}
|
|
21238
|
+
function createObservabilitySqliteClient(cwd = process.cwd()) {
|
|
21239
|
+
const location = resolveObservabilityDbLocation(cwd);
|
|
21240
|
+
if (!existsSync5(location.dbPath))
|
|
21241
|
+
return null;
|
|
21242
|
+
return openObservabilitySqliteClient(location.dbPath);
|
|
21243
|
+
}
|
|
21244
|
+
function createObservabilitySqliteClientAtPath(dbPath) {
|
|
21245
|
+
mkdirSync3(dirname3(dbPath), { recursive: true });
|
|
21246
|
+
return openObservabilitySqliteClient(dbPath);
|
|
21247
|
+
}
|
|
21203
21248
|
var _BunDatabase = null, _probed = false, BUSY_TIMEOUT_MS = 5000, MAX_RETRY_ATTEMPTS = 5, BASE_RETRY_DELAY_MS = 50, STALE_CLAIM_AGE_MS = 60000;
|
|
21204
21249
|
var init_observability_sqlite = __esm(() => {
|
|
21205
21250
|
init_observability_db();
|
|
@@ -21582,24 +21627,27 @@ function readMandatoryRuleSet(cwd, id) {
|
|
|
21582
21627
|
}
|
|
21583
21628
|
function formatMandatoryRulesBlock(sets, inlineRules = []) {
|
|
21584
21629
|
if (sets.length === 0 && inlineRules.length === 0)
|
|
21585
|
-
return "";
|
|
21630
|
+
return { block: "", sections: [] };
|
|
21586
21631
|
const sections = [
|
|
21587
21632
|
...sets.map((set2) => {
|
|
21588
21633
|
const rules = set2.rules.map((rule) => `- [${rule.level}] ${rule.text}`).join(`
|
|
21589
21634
|
`);
|
|
21590
|
-
return `### ${set2.id}
|
|
21591
|
-
${rules}
|
|
21635
|
+
return { setId: set2.id, block: `### ${set2.id}
|
|
21636
|
+
${rules}` };
|
|
21592
21637
|
}),
|
|
21593
21638
|
...inlineRules.length > 0 ? [
|
|
21594
|
-
|
|
21639
|
+
{
|
|
21640
|
+
setId: "specialist-inline-rules",
|
|
21641
|
+
block: `### specialist-inline-rules
|
|
21595
21642
|
${inlineRules.map((rule, index) => `- [${rule.level}] ${rule.text}${rule.id ? ` (id: ${rule.id})` : ` (id: inline-${index + 1})`}`).join(`
|
|
21596
21643
|
`)}`
|
|
21644
|
+
}
|
|
21597
21645
|
] : []
|
|
21598
21646
|
];
|
|
21599
|
-
return `## MANDATORY_RULES
|
|
21600
|
-
${sections.join(`
|
|
21647
|
+
return { block: `## MANDATORY_RULES
|
|
21648
|
+
${sections.map((section) => section.block).join(`
|
|
21601
21649
|
|
|
21602
|
-
`)}
|
|
21650
|
+
`)}`, sections };
|
|
21603
21651
|
}
|
|
21604
21652
|
function collectMandatoryRuleSets(cwd, setIds) {
|
|
21605
21653
|
const seen = new Set;
|
|
@@ -21633,9 +21681,10 @@ function buildMandatoryRulesInjection(specialistConfig) {
|
|
|
21633
21681
|
id: "workflow-quick-rules",
|
|
21634
21682
|
rules: [{ id: "workflow-quick-rules-1", level: "required", text: STATIC_WORKFLOW_RULES_BLOCK.trim().replace(/^##\s+Beads Workflow Quick Rules\n/, "") }]
|
|
21635
21683
|
}];
|
|
21636
|
-
const
|
|
21684
|
+
const formatted = formatMandatoryRulesBlock([...globals, ...sets], inlineRules);
|
|
21637
21685
|
return {
|
|
21638
|
-
block,
|
|
21686
|
+
block: formatted.block,
|
|
21687
|
+
sections: formatted.sections,
|
|
21639
21688
|
setsLoaded: [...globals.map((set2) => set2.id), ...sets.map((set2) => set2.id)],
|
|
21640
21689
|
ruleCount: [...globals, ...sets].reduce((count, set2) => count + set2.rules.length, 0) + inlineRules.length,
|
|
21641
21690
|
inlineRulesCount: inlineRules.length,
|
|
@@ -22565,9 +22614,8 @@ ${summaries.join(`
|
|
|
22565
22614
|
skillPaths.push(prompt.skill_inherit);
|
|
22566
22615
|
skillPaths.push(...spec.specialist.skills?.paths ?? []);
|
|
22567
22616
|
if (mandatoryRulesInjection) {
|
|
22568
|
-
for (const
|
|
22569
|
-
payloadComponents.push(measurePayloadComponent("mandatory_rule", setId,
|
|
22570
|
-
${mandatoryRulesBlock}`));
|
|
22617
|
+
for (const section of mandatoryRulesInjection.sections) {
|
|
22618
|
+
payloadComponents.push(measurePayloadComponent("mandatory_rule", section.setId, section.block));
|
|
22571
22619
|
}
|
|
22572
22620
|
}
|
|
22573
22621
|
for (const skillPath of skillPaths) {
|
|
@@ -22983,7 +23031,7 @@ var init_runner = __esm(() => {
|
|
|
22983
23031
|
|
|
22984
23032
|
// src/specialist/hooks.ts
|
|
22985
23033
|
import { appendFile, mkdir } from "fs/promises";
|
|
22986
|
-
import { dirname as
|
|
23034
|
+
import { dirname as dirname4 } from "path";
|
|
22987
23035
|
|
|
22988
23036
|
class HookEmitter {
|
|
22989
23037
|
tracePath;
|
|
@@ -22991,7 +23039,7 @@ class HookEmitter {
|
|
|
22991
23039
|
ready;
|
|
22992
23040
|
constructor(options) {
|
|
22993
23041
|
this.tracePath = options.tracePath;
|
|
22994
|
-
this.ready = mkdir(
|
|
23042
|
+
this.ready = mkdir(dirname4(options.tracePath), { recursive: true }).then(() => {});
|
|
22995
23043
|
}
|
|
22996
23044
|
async emit(hook, invocationId, specialistName, specialistVersion, payload) {
|
|
22997
23045
|
await this.ready;
|
|
@@ -23045,11 +23093,11 @@ __export(exports_version, {
|
|
|
23045
23093
|
});
|
|
23046
23094
|
import { createRequire } from "module";
|
|
23047
23095
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
23048
|
-
import { dirname as
|
|
23096
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
23049
23097
|
import { existsSync as existsSync8 } from "fs";
|
|
23050
23098
|
async function run2() {
|
|
23051
23099
|
const req = createRequire(import.meta.url);
|
|
23052
|
-
const here =
|
|
23100
|
+
const here = dirname5(fileURLToPath2(import.meta.url));
|
|
23053
23101
|
const bundlePkgPath = join7(here, "..", "package.json");
|
|
23054
23102
|
const sourcePkgPath = join7(here, "..", "..", "package.json");
|
|
23055
23103
|
let pkg;
|
|
@@ -24179,7 +24227,7 @@ import {
|
|
|
24179
24227
|
closeSync,
|
|
24180
24228
|
existsSync as existsSync10,
|
|
24181
24229
|
fsyncSync,
|
|
24182
|
-
mkdirSync as
|
|
24230
|
+
mkdirSync as mkdirSync4,
|
|
24183
24231
|
openSync,
|
|
24184
24232
|
readdirSync as readdirSync2,
|
|
24185
24233
|
readFileSync as readFileSync8,
|
|
@@ -24616,7 +24664,7 @@ class Supervisor {
|
|
|
24616
24664
|
return join9(this.resolvedJobsDir, "..", "ready");
|
|
24617
24665
|
}
|
|
24618
24666
|
writeReadyMarker(id) {
|
|
24619
|
-
|
|
24667
|
+
mkdirSync4(this.readyDir(), { recursive: true });
|
|
24620
24668
|
writeFileSync3(join9(this.readyDir(), id), "", "utf-8");
|
|
24621
24669
|
}
|
|
24622
24670
|
withComputedLiveness(status) {
|
|
@@ -24747,7 +24795,7 @@ class Supervisor {
|
|
|
24747
24795
|
if (!this.isJobFileOutputEnabled)
|
|
24748
24796
|
return;
|
|
24749
24797
|
const normalizedStatus = this.withStatusLineageDefaults(id, data);
|
|
24750
|
-
|
|
24798
|
+
mkdirSync4(this.jobDir(id), { recursive: true });
|
|
24751
24799
|
const path = this.statusPath(id);
|
|
24752
24800
|
const tmp = path + ".tmp";
|
|
24753
24801
|
writeFileSync3(tmp, JSON.stringify(normalizedStatus, null, 2), "utf-8");
|
|
@@ -24909,8 +24957,8 @@ class Supervisor {
|
|
|
24909
24957
|
const id = crypto.randomUUID().slice(0, 6);
|
|
24910
24958
|
const dir = this.jobDir(id);
|
|
24911
24959
|
const startedAtMs = Date.now();
|
|
24912
|
-
|
|
24913
|
-
|
|
24960
|
+
mkdirSync4(dir, { recursive: true });
|
|
24961
|
+
mkdirSync4(this.readyDir(), { recursive: true });
|
|
24914
24962
|
const nodeId = runOptions.variables?.node_id ?? runOptions.variables?.SPECIALISTS_NODE_ID;
|
|
24915
24963
|
const variablesKeys = Object.keys(runOptions.variables ?? {});
|
|
24916
24964
|
const activatedSkills = (runOptions.variables?.activated_skills ?? runOptions.variables?.skills_activated ?? "").split(",").map((skill) => skill.trim()).filter((skill) => skill.length > 0);
|
|
@@ -25188,7 +25236,7 @@ ${appendError}
|
|
|
25188
25236
|
try {
|
|
25189
25237
|
const output = await resumeFn(task);
|
|
25190
25238
|
latestOutput = output;
|
|
25191
|
-
|
|
25239
|
+
mkdirSync4(this.jobDir(id), { recursive: true });
|
|
25192
25240
|
writeFileSync3(this.resultPath(id), output, "utf-8");
|
|
25193
25241
|
try {
|
|
25194
25242
|
this.withSqliteOperation("upsertResult:resume_turn", (client) => client.upsertResult(id, output));
|
|
@@ -25596,7 +25644,7 @@ ${appendError}
|
|
|
25596
25644
|
});
|
|
25597
25645
|
latestOutput = result.output;
|
|
25598
25646
|
if (this.isJobFileOutputEnabled) {
|
|
25599
|
-
|
|
25647
|
+
mkdirSync4(this.jobDir(id), { recursive: true });
|
|
25600
25648
|
writeFileSync3(this.resultPath(id), latestOutput, "utf-8");
|
|
25601
25649
|
}
|
|
25602
25650
|
const elapsed = Math.round((Date.now() - startedAtMs) / 1000);
|
|
@@ -26584,9 +26632,9 @@ var exports_init = {};
|
|
|
26584
26632
|
__export(exports_init, {
|
|
26585
26633
|
run: () => run7
|
|
26586
26634
|
});
|
|
26587
|
-
import { copyFileSync, cpSync, existsSync as existsSync12, lstatSync, mkdirSync as
|
|
26635
|
+
import { copyFileSync, cpSync, existsSync as existsSync12, lstatSync, mkdirSync as mkdirSync5, readdirSync as readdirSync4, readFileSync as readFileSync10, readlinkSync, renameSync as renameSync2, symlinkSync, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
26588
26636
|
import { spawnSync as spawnSync9 } from "child_process";
|
|
26589
|
-
import { basename as basename4, dirname as
|
|
26637
|
+
import { basename as basename4, dirname as dirname6, join as join11, relative, resolve as resolve6 } from "path";
|
|
26590
26638
|
function ok(msg) {
|
|
26591
26639
|
console.log(` ${green4("\u2713")} ${msg}`);
|
|
26592
26640
|
}
|
|
@@ -26640,7 +26688,7 @@ function migrateLegacySpecialists(cwd, scope) {
|
|
|
26640
26688
|
return;
|
|
26641
26689
|
const targetDir = join11(cwd, ".specialists", scope);
|
|
26642
26690
|
if (!existsSync12(targetDir)) {
|
|
26643
|
-
|
|
26691
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
26644
26692
|
}
|
|
26645
26693
|
const files = readdirSync4(sourceDir).filter((f) => f.endsWith(".specialist.json") || f.endsWith(".specialist.json"));
|
|
26646
26694
|
if (files.length === 0)
|
|
@@ -26677,7 +26725,7 @@ function copyCanonicalSpecialists(cwd) {
|
|
|
26677
26725
|
return;
|
|
26678
26726
|
}
|
|
26679
26727
|
if (!existsSync12(targetDir)) {
|
|
26680
|
-
|
|
26728
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
26681
26729
|
}
|
|
26682
26730
|
let copied = 0;
|
|
26683
26731
|
let refreshed = 0;
|
|
@@ -26712,7 +26760,7 @@ function copyCanonicalMandatoryRules(cwd) {
|
|
|
26712
26760
|
return;
|
|
26713
26761
|
}
|
|
26714
26762
|
if (!existsSync12(targetDir)) {
|
|
26715
|
-
|
|
26763
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
26716
26764
|
}
|
|
26717
26765
|
let copied = 0;
|
|
26718
26766
|
let refreshed = 0;
|
|
@@ -26747,7 +26795,7 @@ function copyCanonicalNodeConfigs(cwd) {
|
|
|
26747
26795
|
return;
|
|
26748
26796
|
}
|
|
26749
26797
|
if (!existsSync12(targetDir)) {
|
|
26750
|
-
|
|
26798
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
26751
26799
|
}
|
|
26752
26800
|
let copied = 0;
|
|
26753
26801
|
let refreshed = 0;
|
|
@@ -26783,8 +26831,8 @@ function installProjectHooks(cwd) {
|
|
|
26783
26831
|
skip("no hook files found in package");
|
|
26784
26832
|
return;
|
|
26785
26833
|
}
|
|
26786
|
-
|
|
26787
|
-
|
|
26834
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
26835
|
+
mkdirSync5(claudeHooksDir, { recursive: true });
|
|
26788
26836
|
let copied = 0;
|
|
26789
26837
|
let skippedCopies = 0;
|
|
26790
26838
|
let linked = 0;
|
|
@@ -26809,7 +26857,7 @@ function installProjectHooks(cwd) {
|
|
|
26809
26857
|
rewiredLinks++;
|
|
26810
26858
|
continue;
|
|
26811
26859
|
}
|
|
26812
|
-
const currentTarget = resolve6(
|
|
26860
|
+
const currentTarget = resolve6(dirname6(claudeHookPath), readlinkSync(claudeHookPath));
|
|
26813
26861
|
if (currentTarget !== xtrmDest) {
|
|
26814
26862
|
unlinkSync(claudeHookPath);
|
|
26815
26863
|
symlinkSync(relativeTarget, claudeHookPath);
|
|
@@ -26837,7 +26885,7 @@ function ensureProjectHookWiring(cwd) {
|
|
|
26837
26885
|
const settingsPath = join11(cwd, ".claude", "settings.json");
|
|
26838
26886
|
const settingsDir = join11(cwd, ".claude");
|
|
26839
26887
|
if (!existsSync12(settingsDir)) {
|
|
26840
|
-
|
|
26888
|
+
mkdirSync5(settingsDir, { recursive: true });
|
|
26841
26889
|
}
|
|
26842
26890
|
const settings = loadJson(settingsPath, {});
|
|
26843
26891
|
if (!settings.hooks || typeof settings.hooks !== "object") {
|
|
@@ -26873,10 +26921,10 @@ function ensureProjectHookWiring(cwd) {
|
|
|
26873
26921
|
}
|
|
26874
26922
|
function ensureRootSymlink(rootPath, expectedTargetPath) {
|
|
26875
26923
|
if (!existsSync12(rootPath)) {
|
|
26876
|
-
|
|
26877
|
-
const relTarget = relative(
|
|
26924
|
+
mkdirSync5(dirname6(rootPath), { recursive: true });
|
|
26925
|
+
const relTarget = relative(dirname6(rootPath), expectedTargetPath);
|
|
26878
26926
|
symlinkSync(relTarget, rootPath);
|
|
26879
|
-
ok(`created ${basename4(
|
|
26927
|
+
ok(`created ${basename4(dirname6(rootPath))}/${basename4(rootPath)} \u2192 ${relTarget}`);
|
|
26880
26928
|
return;
|
|
26881
26929
|
}
|
|
26882
26930
|
const stats = lstatSync(rootPath);
|
|
@@ -26884,7 +26932,7 @@ function ensureRootSymlink(rootPath, expectedTargetPath) {
|
|
|
26884
26932
|
throw new Error(`${rootPath} must be a symlink to ${expectedTargetPath}. Aborting.`);
|
|
26885
26933
|
}
|
|
26886
26934
|
const linkTarget = readlinkSync(rootPath);
|
|
26887
|
-
const resolvedTarget = resolve6(
|
|
26935
|
+
const resolvedTarget = resolve6(dirname6(rootPath), linkTarget);
|
|
26888
26936
|
const resolvedExpected = resolve6(expectedTargetPath);
|
|
26889
26937
|
if (resolvedTarget === resolvedExpected) {
|
|
26890
26938
|
return;
|
|
@@ -26895,9 +26943,9 @@ function ensureRootSymlink(rootPath, expectedTargetPath) {
|
|
|
26895
26943
|
];
|
|
26896
26944
|
if (legacyTargets.includes(resolvedTarget)) {
|
|
26897
26945
|
unlinkSync(rootPath);
|
|
26898
|
-
const relTarget = relative(
|
|
26946
|
+
const relTarget = relative(dirname6(rootPath), expectedTargetPath);
|
|
26899
26947
|
symlinkSync(relTarget, rootPath);
|
|
26900
|
-
ok(`rewired ${basename4(
|
|
26948
|
+
ok(`rewired ${basename4(dirname6(rootPath))}/${basename4(rootPath)} \u2192 ${relTarget}`);
|
|
26901
26949
|
return;
|
|
26902
26950
|
}
|
|
26903
26951
|
throw new Error(`${rootPath} points to ${linkTarget}, expected ${expectedTargetPath}. Aborting.`);
|
|
@@ -26918,7 +26966,7 @@ function ensureActiveSkillSymlink(defaultSkillPath, activeLinkPath) {
|
|
|
26918
26966
|
if (!stats.isSymbolicLink()) {
|
|
26919
26967
|
throw new Error(`${activeLinkPath} already exists and is not a symlink.`);
|
|
26920
26968
|
}
|
|
26921
|
-
const currentTarget = resolve6(
|
|
26969
|
+
const currentTarget = resolve6(dirname6(activeLinkPath), readlinkSync(activeLinkPath));
|
|
26922
26970
|
if (currentTarget !== resolve6(defaultSkillPath)) {
|
|
26923
26971
|
throw new Error(`${activeLinkPath} points to an unexpected target.`);
|
|
26924
26972
|
}
|
|
@@ -26940,8 +26988,8 @@ function installProjectSkills(cwd, syncSkills) {
|
|
|
26940
26988
|
}
|
|
26941
26989
|
const defaultRoot = join11(cwd, ".xtrm", "skills", "default");
|
|
26942
26990
|
const activeRoot = join11(cwd, ".xtrm", "skills", "active");
|
|
26943
|
-
|
|
26944
|
-
|
|
26991
|
+
mkdirSync5(defaultRoot, { recursive: true });
|
|
26992
|
+
mkdirSync5(activeRoot, { recursive: true });
|
|
26945
26993
|
ensureRootSymlink(join11(cwd, ".claude", "skills"), activeRoot);
|
|
26946
26994
|
ensureRootSymlink(join11(cwd, ".pi", "skills"), activeRoot);
|
|
26947
26995
|
let copied = 0;
|
|
@@ -26972,7 +27020,7 @@ function createSpecialistsDirs(cwd) {
|
|
|
26972
27020
|
let created = 0;
|
|
26973
27021
|
for (const dir of [defaultDir, userDir]) {
|
|
26974
27022
|
if (!existsSync12(dir)) {
|
|
26975
|
-
|
|
27023
|
+
mkdirSync5(dir, { recursive: true });
|
|
26976
27024
|
created++;
|
|
26977
27025
|
}
|
|
26978
27026
|
}
|
|
@@ -26988,7 +27036,7 @@ function createRuntimeDirs(cwd) {
|
|
|
26988
27036
|
let created = 0;
|
|
26989
27037
|
for (const dir of runtimeDirs) {
|
|
26990
27038
|
if (!existsSync12(dir)) {
|
|
26991
|
-
|
|
27039
|
+
mkdirSync5(dir, { recursive: true });
|
|
26992
27040
|
created++;
|
|
26993
27041
|
}
|
|
26994
27042
|
}
|
|
@@ -27121,7 +27169,7 @@ function validateInitPostconditions(cwd) {
|
|
|
27121
27169
|
continue;
|
|
27122
27170
|
}
|
|
27123
27171
|
const expectedTarget = resolve6(xtrmHooksDir, hookFile);
|
|
27124
|
-
const resolvedTarget = resolve6(
|
|
27172
|
+
const resolvedTarget = resolve6(dirname6(claudeHookPath), readlinkSync(claudeHookPath));
|
|
27125
27173
|
if (resolvedTarget !== expectedTarget) {
|
|
27126
27174
|
warnings.push(`.claude/hooks/${hookFile} points to unexpected target`);
|
|
27127
27175
|
}
|
|
@@ -27175,7 +27223,7 @@ function validateInitPostconditions(cwd) {
|
|
|
27175
27223
|
warnings.push(`${relative(cwd, symlink.linkPath)} is not a symlink`);
|
|
27176
27224
|
continue;
|
|
27177
27225
|
}
|
|
27178
|
-
const resolvedTarget = resolve6(
|
|
27226
|
+
const resolvedTarget = resolve6(dirname6(symlink.linkPath), readlinkSync(symlink.linkPath));
|
|
27179
27227
|
if (resolvedTarget !== resolve6(symlink.expectedTarget)) {
|
|
27180
27228
|
warnings.push(`${relative(cwd, symlink.linkPath)} points to an unexpected target`);
|
|
27181
27229
|
}
|
|
@@ -27352,8 +27400,8 @@ var exports_db = {};
|
|
|
27352
27400
|
__export(exports_db, {
|
|
27353
27401
|
run: () => run9
|
|
27354
27402
|
});
|
|
27355
|
-
import { existsSync as existsSync13, mkdirSync as
|
|
27356
|
-
import { dirname as
|
|
27403
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readdirSync as readdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
27404
|
+
import { dirname as dirname7, join as join12, resolve as resolve7 } from "path";
|
|
27357
27405
|
function formatBytes(bytes) {
|
|
27358
27406
|
if (bytes < 1024)
|
|
27359
27407
|
return `${bytes} B`;
|
|
@@ -28013,8 +28061,8 @@ function runBenchmarkExport(options) {
|
|
|
28013
28061
|
});
|
|
28014
28062
|
}
|
|
28015
28063
|
rows.sort((a, b) => a.task_id.localeCompare(b.task_id) || a.executor_job_id.localeCompare(b.executor_job_id));
|
|
28016
|
-
const outputDirectory =
|
|
28017
|
-
|
|
28064
|
+
const outputDirectory = dirname7(options.outputPath);
|
|
28065
|
+
mkdirSync6(outputDirectory, { recursive: true });
|
|
28018
28066
|
const jsonl = rows.map((row) => JSON.stringify(row)).join(`
|
|
28019
28067
|
`);
|
|
28020
28068
|
writeFileSync5(options.outputPath, rows.length > 0 ? `${jsonl}
|
|
@@ -28105,6 +28153,19 @@ var init_db = __esm(() => {
|
|
|
28105
28153
|
import { spawn as spawn3 } from "child_process";
|
|
28106
28154
|
import { createHash as createHash3, randomUUID } from "crypto";
|
|
28107
28155
|
import { readFileSync as readFileSync12 } from "fs";
|
|
28156
|
+
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve8 } from "path";
|
|
28157
|
+
function isPathWithinRoot(candidatePath, rootPath) {
|
|
28158
|
+
const candidate = resolve8(candidatePath);
|
|
28159
|
+
const root = resolve8(rootPath);
|
|
28160
|
+
const rel = relative2(root, candidate);
|
|
28161
|
+
return rel === "" || rel.length > 0 && !rel.startsWith("..") && !isAbsolute2(rel);
|
|
28162
|
+
}
|
|
28163
|
+
function assertSkillPathWithinRoots(field, path, roots) {
|
|
28164
|
+
const allowed = roots.some((root) => isPathWithinRoot(path, root));
|
|
28165
|
+
if (!allowed) {
|
|
28166
|
+
throw new CompatGuardError(field, `skill path '${path}' not under any --allow-skills-roots entry`);
|
|
28167
|
+
}
|
|
28168
|
+
}
|
|
28108
28169
|
function hasUnsubstitutedVariables(template, variables) {
|
|
28109
28170
|
const matches = template.match(/\$([a-zA-Z_][a-zA-Z0-9_]*)/g) ?? [];
|
|
28110
28171
|
for (const match of matches) {
|
|
@@ -28123,8 +28184,8 @@ function compatGuard(spec, trust) {
|
|
|
28123
28184
|
if (execution.permission_required !== "READ_ONLY")
|
|
28124
28185
|
throw new CompatGuardError("execution.permission_required", "permission_required must be READ_ONLY");
|
|
28125
28186
|
const hasScripts = (spec.specialist.skills?.scripts?.length ?? 0) > 0;
|
|
28126
|
-
if (hasScripts
|
|
28127
|
-
throw new CompatGuardError("skills.scripts", "scripts not
|
|
28187
|
+
if (hasScripts) {
|
|
28188
|
+
throw new CompatGuardError("skills.scripts", "local scripts are not supported in script-class specialists");
|
|
28128
28189
|
}
|
|
28129
28190
|
const hasPaths = (spec.specialist.skills?.paths?.length ?? 0) > 0;
|
|
28130
28191
|
const hasSkillInherit = Boolean(spec.specialist.prompt.skill_inherit);
|
|
@@ -28134,26 +28195,34 @@ function compatGuard(spec, trust) {
|
|
|
28134
28195
|
if (hasSkillInherit && !trust?.allowSkills) {
|
|
28135
28196
|
throw new CompatGuardError("prompt.skill_inherit", "skills not allowed (enable with --allow-skills)");
|
|
28136
28197
|
}
|
|
28137
|
-
if (
|
|
28198
|
+
if (trust?.allowSkills && trust.allowSkillsRoots && trust.allowSkillsRoots.length > 0) {
|
|
28138
28199
|
const paths = spec.specialist.skills?.paths ?? [];
|
|
28139
|
-
for (const path of paths)
|
|
28140
|
-
|
|
28141
|
-
|
|
28142
|
-
|
|
28143
|
-
}
|
|
28200
|
+
for (const path of paths)
|
|
28201
|
+
assertSkillPathWithinRoots("skills.paths", path, trust.allowSkillsRoots);
|
|
28202
|
+
if (typeof spec.specialist.prompt.skill_inherit === "string") {
|
|
28203
|
+
assertSkillPathWithinRoots("prompt.skill_inherit", spec.specialist.prompt.skill_inherit, trust.allowSkillsRoots);
|
|
28144
28204
|
}
|
|
28145
28205
|
}
|
|
28146
28206
|
}
|
|
28207
|
+
function collectSkillPathEntries(spec) {
|
|
28208
|
+
return [
|
|
28209
|
+
...(spec.specialist.skills?.paths ?? []).map((path) => ({ path, source: "skills.paths" })),
|
|
28210
|
+
...typeof spec.specialist.prompt.skill_inherit === "string" ? [{ path: spec.specialist.prompt.skill_inherit, source: "prompt.skill_inherit" }] : []
|
|
28211
|
+
];
|
|
28212
|
+
}
|
|
28213
|
+
function collectSkillPaths(spec) {
|
|
28214
|
+
return collectSkillPathEntries(spec).map((entry) => entry.path);
|
|
28215
|
+
}
|
|
28147
28216
|
function computeSkillSources(spec) {
|
|
28148
|
-
const
|
|
28217
|
+
const entries = collectSkillPathEntries(spec);
|
|
28149
28218
|
const sources = [];
|
|
28150
|
-
for (const path of
|
|
28219
|
+
for (const { path, source } of entries) {
|
|
28151
28220
|
try {
|
|
28152
28221
|
const content = readFileSync12(path);
|
|
28153
28222
|
const sha256 = createHash3("sha256").update(content).digest("hex");
|
|
28154
|
-
sources.push({ path, sha256 });
|
|
28223
|
+
sources.push({ path, sha256, source });
|
|
28155
28224
|
} catch {
|
|
28156
|
-
sources.push({ path, sha256: "unreadable" });
|
|
28225
|
+
sources.push({ path, sha256: "unreadable", source });
|
|
28157
28226
|
}
|
|
28158
28227
|
}
|
|
28159
28228
|
return sources;
|
|
@@ -28164,6 +28233,34 @@ function renderTaskTemplate(template, variables) {
|
|
|
28164
28233
|
throw new Error(`Missing template variable: ${missing}`);
|
|
28165
28234
|
return renderTemplate(template, variables);
|
|
28166
28235
|
}
|
|
28236
|
+
function truncateForPrompt(value, limitBytes) {
|
|
28237
|
+
if (Buffer.byteLength(value, "utf8") <= limitBytes)
|
|
28238
|
+
return value;
|
|
28239
|
+
return `${value.slice(0, limitBytes)}
|
|
28240
|
+
... truncated ...`;
|
|
28241
|
+
}
|
|
28242
|
+
function buildJsonOutputContract(spec) {
|
|
28243
|
+
if (spec.specialist.execution.response_format !== "json")
|
|
28244
|
+
return;
|
|
28245
|
+
const schema = spec.specialist.prompt.output_schema;
|
|
28246
|
+
const required2 = Array.isArray(schema?.required) ? schema.required.filter((value) => typeof value === "string") : [];
|
|
28247
|
+
const lines = [
|
|
28248
|
+
"Output contract:",
|
|
28249
|
+
"- Return only valid JSON. Do not include Markdown fences, prose, or commentary."
|
|
28250
|
+
];
|
|
28251
|
+
if (required2.length > 0)
|
|
28252
|
+
lines.push(`- Include these required top-level keys: ${required2.join(", ")}.`);
|
|
28253
|
+
if (schema)
|
|
28254
|
+
lines.push(`- JSON schema: ${truncateForPrompt(JSON.stringify(schema), 4096)}`);
|
|
28255
|
+
return lines.join(`
|
|
28256
|
+
`);
|
|
28257
|
+
}
|
|
28258
|
+
function applyOutputContract(prompt, spec) {
|
|
28259
|
+
const contract = buildJsonOutputContract(spec);
|
|
28260
|
+
return contract ? `${prompt}
|
|
28261
|
+
|
|
28262
|
+
${contract}` : prompt;
|
|
28263
|
+
}
|
|
28167
28264
|
function mapErrorType(message) {
|
|
28168
28265
|
if (message.includes("Specialist not found"))
|
|
28169
28266
|
return "specialist_not_found";
|
|
@@ -28171,6 +28268,8 @@ function mapErrorType(message) {
|
|
|
28171
28268
|
return "specialist_load_error";
|
|
28172
28269
|
if (message.includes("Missing template variable"))
|
|
28173
28270
|
return "template_variable_missing";
|
|
28271
|
+
if (message.includes("prompt too large"))
|
|
28272
|
+
return "prompt_too_large";
|
|
28174
28273
|
if (message.includes("output too large"))
|
|
28175
28274
|
return "output_too_large";
|
|
28176
28275
|
if (message.includes("auth") || message.includes("403") || message.includes("401"))
|
|
@@ -28238,6 +28337,18 @@ function writeTraceRow(client, specialist, model, traceId, output2, durationMs,
|
|
|
28238
28337
|
onAuditFailure?.(error2);
|
|
28239
28338
|
}
|
|
28240
28339
|
}
|
|
28340
|
+
function resolvePromptLimitBytes(spec) {
|
|
28341
|
+
return spec.specialist.execution.prompt_limit_bytes ?? resolveEnvPromptLimitBytes() ?? DEFAULT_PROMPT_LIMIT_BYTES;
|
|
28342
|
+
}
|
|
28343
|
+
function resolveEnvPromptLimitBytes() {
|
|
28344
|
+
const raw = process.env.SPECIALISTS_SCRIPT_PROMPT_LIMIT_BYTES;
|
|
28345
|
+
if (raw === undefined)
|
|
28346
|
+
return;
|
|
28347
|
+
const envLimit = Number(raw);
|
|
28348
|
+
if (!Number.isFinite(envLimit) || envLimit <= 0)
|
|
28349
|
+
return;
|
|
28350
|
+
return Math.floor(envLimit);
|
|
28351
|
+
}
|
|
28241
28352
|
function resolveAssistantTextLimitBytes(spec) {
|
|
28242
28353
|
return spec.specialist.execution.stdout_limit_bytes ?? resolveEnvAssistantTextLimitBytes() ?? DEFAULT_ASSISTANT_TEXT_LIMIT_BYTES;
|
|
28243
28354
|
}
|
|
@@ -28253,8 +28364,9 @@ function resolveEnvAssistantTextLimitBytes() {
|
|
|
28253
28364
|
return Math.floor(envLimit);
|
|
28254
28365
|
}
|
|
28255
28366
|
function openObservabilityClient(options) {
|
|
28256
|
-
|
|
28257
|
-
|
|
28367
|
+
if (options.observabilityDbPath)
|
|
28368
|
+
return createObservabilitySqliteClientAtPath(options.observabilityDbPath);
|
|
28369
|
+
return createObservabilitySqliteClient(options.projectDir);
|
|
28258
28370
|
}
|
|
28259
28371
|
function resolveScriptSpecialistName(name) {
|
|
28260
28372
|
if (name === "changelog-keeper")
|
|
@@ -28268,9 +28380,28 @@ async function runScriptSpecialist(input2, options) {
|
|
|
28268
28380
|
const resolvedSpecialist = resolveScriptSpecialistName(input2.specialist);
|
|
28269
28381
|
const spec = await options.loader.get(resolvedSpecialist);
|
|
28270
28382
|
compatGuard(spec, options.trust);
|
|
28383
|
+
const skillPaths = options.trust?.allowSkills ? collectSkillPaths(spec) : [];
|
|
28271
28384
|
const skillSources = options.trust?.allowSkills ? computeSkillSources(spec) : undefined;
|
|
28272
28385
|
const template = input2.template ?? spec.specialist.prompt.task_template;
|
|
28273
|
-
const prompt = renderTaskTemplate(template, input2.variables ?? {});
|
|
28386
|
+
const prompt = applyOutputContract(renderTaskTemplate(template, input2.variables ?? {}), spec);
|
|
28387
|
+
const modelCandidates = collectModelCandidates(input2, spec, options);
|
|
28388
|
+
const promptLimitBytes = resolvePromptLimitBytes(spec);
|
|
28389
|
+
const promptBytes = Buffer.byteLength(prompt, "utf8");
|
|
28390
|
+
if (promptBytes > promptLimitBytes) {
|
|
28391
|
+
return {
|
|
28392
|
+
success: false,
|
|
28393
|
+
error: `prompt too large: ${promptBytes} bytes exceeds limit ${promptLimitBytes} bytes`,
|
|
28394
|
+
error_type: "prompt_too_large",
|
|
28395
|
+
meta: {
|
|
28396
|
+
specialist: resolvedSpecialist,
|
|
28397
|
+
requested_specialist: input2.requested_specialist ?? input2.specialist,
|
|
28398
|
+
resolved_specialist: resolvedSpecialist,
|
|
28399
|
+
model: modelCandidates[0],
|
|
28400
|
+
duration_ms: Date.now() - startedAt,
|
|
28401
|
+
trace_id: traceId
|
|
28402
|
+
}
|
|
28403
|
+
};
|
|
28404
|
+
}
|
|
28274
28405
|
if (process.env.SPECIALISTS_SCRIPT_STUB_OUTPUT) {
|
|
28275
28406
|
return {
|
|
28276
28407
|
success: true,
|
|
@@ -28286,11 +28417,11 @@ async function runScriptSpecialist(input2, options) {
|
|
|
28286
28417
|
};
|
|
28287
28418
|
}
|
|
28288
28419
|
const timeoutMs = input2.timeout_ms ?? spec.specialist.execution.timeout_ms ?? 120000;
|
|
28289
|
-
const modelCandidates = collectModelCandidates(input2, spec, options);
|
|
28290
28420
|
const assistantTextLimitBytes = resolveAssistantTextLimitBytes(spec);
|
|
28291
28421
|
const attempts = [];
|
|
28292
28422
|
for (const model of modelCandidates) {
|
|
28293
|
-
const
|
|
28423
|
+
const systemPrompt = spec.specialist.prompt.system || undefined;
|
|
28424
|
+
const attempt = await runSingleAttempt(prompt, model, input2.thinking_level ?? spec.specialist.execution.thinking_level, timeoutMs, assistantTextLimitBytes, options, systemPrompt, skillPaths);
|
|
28294
28425
|
attempts.push(attempt);
|
|
28295
28426
|
const parsed = classifyAttempt(attempt);
|
|
28296
28427
|
if (parsed.retryable)
|
|
@@ -28333,14 +28464,23 @@ function collectModelCandidates(input2, spec, options) {
|
|
|
28333
28464
|
const candidates = [input2.model_override, spec.specialist.execution.model, spec.specialist.execution.fallback_model, options.fallbackModel].filter((value) => typeof value === "string" && value.length > 0);
|
|
28334
28465
|
return [...new Set(candidates)];
|
|
28335
28466
|
}
|
|
28336
|
-
function runSingleAttempt(prompt, model, thinkingLevel, timeoutMs, assistantTextLimitBytes, options) {
|
|
28337
|
-
return new Promise((
|
|
28338
|
-
const args = ["--mode", "json", "--no-session", "--no-extensions", "--no-tools", "--
|
|
28467
|
+
function runSingleAttempt(prompt, model, thinkingLevel, timeoutMs, assistantTextLimitBytes, options, systemPrompt, skillPaths = []) {
|
|
28468
|
+
return new Promise((resolve9, reject) => {
|
|
28469
|
+
const args = ["--mode", "json", "--no-session", "--no-extensions", "--no-tools", "--offline", "--no-context-files", "--no-prompt-templates", "--no-themes"];
|
|
28470
|
+
if (skillPaths.length === 0)
|
|
28471
|
+
args.push("--no-skills");
|
|
28472
|
+
for (const skillPath of skillPaths)
|
|
28473
|
+
args.push("--skill", skillPath);
|
|
28474
|
+
args.push("--model", model);
|
|
28339
28475
|
if (thinkingLevel)
|
|
28340
28476
|
args.push("--thinking", thinkingLevel);
|
|
28341
|
-
|
|
28342
|
-
|
|
28477
|
+
if (systemPrompt)
|
|
28478
|
+
args.push("--system-prompt", systemPrompt);
|
|
28479
|
+
const pi = spawn3("pi", args, { stdio: ["pipe", "pipe", "pipe"], cwd: options.projectDir ?? process.cwd() });
|
|
28343
28480
|
options.onChild?.(pi);
|
|
28481
|
+
pi.stdin?.on("error", () => {});
|
|
28482
|
+
pi.stdin?.write(prompt);
|
|
28483
|
+
pi.stdin?.end();
|
|
28344
28484
|
let stderr = "";
|
|
28345
28485
|
let timedOut = false;
|
|
28346
28486
|
let outputTooLarge = false;
|
|
@@ -28409,7 +28549,7 @@ function runSingleAttempt(prompt, model, thinkingLevel, timeoutMs, assistantText
|
|
|
28409
28549
|
pi.on("error", reject);
|
|
28410
28550
|
pi.on("close", (code) => {
|
|
28411
28551
|
clearTimeout(timer);
|
|
28412
|
-
|
|
28552
|
+
resolve9({
|
|
28413
28553
|
model,
|
|
28414
28554
|
text: assistantText,
|
|
28415
28555
|
stderr,
|
|
@@ -28446,7 +28586,7 @@ function classifyAttempt(attempt) {
|
|
|
28446
28586
|
function isRetryableModelFailure(stderr, text) {
|
|
28447
28587
|
return stderr.includes("0 tokens") || stderr.includes("quota") || stderr.includes("rate limit") || stderr.includes("403") || stderr.includes("401") || stderr.includes("insufficient_quota") || !text && !stderr.trim();
|
|
28448
28588
|
}
|
|
28449
|
-
var CompatGuardError, DEFAULT_PENDING_LINE_LIMIT_BYTES, DEFAULT_ASSISTANT_TEXT_LIMIT_BYTES, DEFAULT_STDERR_LIMIT_BYTES;
|
|
28589
|
+
var CompatGuardError, DEFAULT_PENDING_LINE_LIMIT_BYTES, DEFAULT_ASSISTANT_TEXT_LIMIT_BYTES, DEFAULT_STDERR_LIMIT_BYTES, DEFAULT_PROMPT_LIMIT_BYTES;
|
|
28450
28590
|
var init_script_runner = __esm(() => {
|
|
28451
28591
|
init_observability_sqlite();
|
|
28452
28592
|
CompatGuardError = class CompatGuardError extends Error {
|
|
@@ -28460,6 +28600,7 @@ var init_script_runner = __esm(() => {
|
|
|
28460
28600
|
DEFAULT_PENDING_LINE_LIMIT_BYTES = 16 * 1024 * 1024;
|
|
28461
28601
|
DEFAULT_ASSISTANT_TEXT_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
28462
28602
|
DEFAULT_STDERR_LIMIT_BYTES = 1 * 1024 * 1024;
|
|
28603
|
+
DEFAULT_PROMPT_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
28463
28604
|
});
|
|
28464
28605
|
|
|
28465
28606
|
// src/cli/validate.ts
|
|
@@ -28612,7 +28753,7 @@ var exports_edit = {};
|
|
|
28612
28753
|
__export(exports_edit, {
|
|
28613
28754
|
run: () => run11
|
|
28614
28755
|
});
|
|
28615
|
-
import { existsSync as existsSync15, mkdirSync as
|
|
28756
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
|
|
28616
28757
|
import { join as join13 } from "path";
|
|
28617
28758
|
function loadPresets() {
|
|
28618
28759
|
const paths = [
|
|
@@ -29050,7 +29191,7 @@ function createUserFork(source, targetName) {
|
|
|
29050
29191
|
if (source.scope === "user")
|
|
29051
29192
|
return source;
|
|
29052
29193
|
const targetDir = join13(process.cwd(), ".specialists", "user");
|
|
29053
|
-
|
|
29194
|
+
mkdirSync7(targetDir, { recursive: true });
|
|
29054
29195
|
const targetFile = join13(targetDir, `${targetName}.specialist.json`);
|
|
29055
29196
|
const doc2 = readJsonFile2(source.filePath);
|
|
29056
29197
|
doc2.specialist = doc2.specialist ?? {};
|
|
@@ -29360,7 +29501,7 @@ __export(exports_config, {
|
|
|
29360
29501
|
});
|
|
29361
29502
|
import { readFileSync as readFileSync14 } from "fs";
|
|
29362
29503
|
import { spawnSync as spawnSync10 } from "child_process";
|
|
29363
|
-
import { dirname as
|
|
29504
|
+
import { dirname as dirname8, join as join15 } from "path";
|
|
29364
29505
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
29365
29506
|
function usage2() {
|
|
29366
29507
|
return [
|
|
@@ -29406,7 +29547,7 @@ function getGitTopLevel(projectDir) {
|
|
|
29406
29547
|
return result.stdout.trim() || undefined;
|
|
29407
29548
|
}
|
|
29408
29549
|
function getRuntimePackageVersion() {
|
|
29409
|
-
const runtimeDir =
|
|
29550
|
+
const runtimeDir = dirname8(fileURLToPath3(import.meta.url));
|
|
29410
29551
|
const packageJsonPath = runtimeDir.includes("/dist/") ? join15(runtimeDir, "..", "package.json") : join15(runtimeDir, "..", "..", "package.json");
|
|
29411
29552
|
return readPackageVersion(packageJsonPath);
|
|
29412
29553
|
}
|
|
@@ -29545,8 +29686,8 @@ var init_config = __esm(() => {
|
|
|
29545
29686
|
});
|
|
29546
29687
|
|
|
29547
29688
|
// src/specialist/worktree.ts
|
|
29548
|
-
import { existsSync as existsSync16, symlinkSync as symlinkSync2, mkdirSync as
|
|
29549
|
-
import { join as join16, resolve as
|
|
29689
|
+
import { existsSync as existsSync16, symlinkSync as symlinkSync2, mkdirSync as mkdirSync8 } from "fs";
|
|
29690
|
+
import { join as join16, resolve as resolve9 } from "path";
|
|
29550
29691
|
import { spawnSync as spawnSync11, execFileSync as execFileSync2 } from "child_process";
|
|
29551
29692
|
function deriveBranchName(beadId, specialistName) {
|
|
29552
29693
|
return `feature/${beadId}-${slugify(specialistName)}`;
|
|
@@ -29579,11 +29720,11 @@ function provisionWorktree(options) {
|
|
|
29579
29720
|
const branch = deriveBranchName(options.beadId, options.specialistName);
|
|
29580
29721
|
const existingPath = findExistingWorktree(branch, cwd);
|
|
29581
29722
|
if (existingPath) {
|
|
29582
|
-
return { branch, worktreePath:
|
|
29723
|
+
return { branch, worktreePath: resolve9(existingPath), reused: true };
|
|
29583
29724
|
}
|
|
29584
29725
|
const worktreeBase = options.worktreeBase ?? join16(commonRoot, ".worktrees", options.beadId);
|
|
29585
29726
|
const worktreeName = deriveWorktreeName(options.beadId, options.specialistName);
|
|
29586
|
-
const worktreePath =
|
|
29727
|
+
const worktreePath = resolve9(join16(worktreeBase, worktreeName));
|
|
29587
29728
|
createWorktreeViaBd(worktreePath, branch, commonRoot);
|
|
29588
29729
|
symlinkPiNpmCache(commonRoot, worktreePath);
|
|
29589
29730
|
return { branch, worktreePath, reused: false };
|
|
@@ -29594,7 +29735,7 @@ function symlinkPiNpmCache(commonRoot, worktreePath) {
|
|
|
29594
29735
|
if (!existsSync16(source) || existsSync16(target))
|
|
29595
29736
|
return;
|
|
29596
29737
|
try {
|
|
29597
|
-
|
|
29738
|
+
mkdirSync8(join16(worktreePath, ".pi"), { recursive: true });
|
|
29598
29739
|
symlinkSync2(source, target);
|
|
29599
29740
|
} catch {}
|
|
29600
29741
|
}
|
|
@@ -29634,12 +29775,12 @@ var init_worktree = __esm(() => {
|
|
|
29634
29775
|
});
|
|
29635
29776
|
|
|
29636
29777
|
// src/specialist/epic-reconciler.ts
|
|
29637
|
-
import { mkdirSync as
|
|
29778
|
+
import { mkdirSync as mkdirSync9, openSync as openSync2, readFileSync as readFileSync15, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
29638
29779
|
import { join as join17 } from "path";
|
|
29639
29780
|
function buildEpicLockPath(epicId) {
|
|
29640
29781
|
const location = resolveObservabilityDbLocation();
|
|
29641
29782
|
const lockDir = join17(location.dbDirectory, "locks");
|
|
29642
|
-
|
|
29783
|
+
mkdirSync9(lockDir, { recursive: true });
|
|
29643
29784
|
return join17(lockDir, `epic-${epicId}.lock`);
|
|
29644
29785
|
}
|
|
29645
29786
|
function withEpicAdvisoryLock(epicId, action) {
|
|
@@ -30981,13 +31122,13 @@ async function parseArgs7(argv) {
|
|
|
30981
31122
|
process.exit(1);
|
|
30982
31123
|
}
|
|
30983
31124
|
if (!prompt && !beadId && !process.stdin.isTTY) {
|
|
30984
|
-
prompt = await new Promise((
|
|
31125
|
+
prompt = await new Promise((resolve10) => {
|
|
30985
31126
|
let buf = "";
|
|
30986
31127
|
process.stdin.setEncoding("utf-8");
|
|
30987
31128
|
process.stdin.on("data", (chunk) => {
|
|
30988
31129
|
buf += chunk;
|
|
30989
31130
|
});
|
|
30990
|
-
process.stdin.on("end", () =>
|
|
31131
|
+
process.stdin.on("end", () => resolve10(buf.trim()));
|
|
30991
31132
|
});
|
|
30992
31133
|
}
|
|
30993
31134
|
if (!prompt && !beadId && !reuseJobId) {
|
|
@@ -31105,7 +31246,7 @@ function resolveWorkingDirectory(args, jobsDir, permissionRequired, readStatus)
|
|
|
31105
31246
|
if (args.reuseJobId !== undefined) {
|
|
31106
31247
|
const targetStatus = readStatus(args.reuseJobId);
|
|
31107
31248
|
if (!targetStatus) {
|
|
31108
|
-
console.error(`Error: cannot read status for job '${args.reuseJobId}'. ` + `Check the job id with: specialists
|
|
31249
|
+
console.error(`Error: cannot read status for job '${args.reuseJobId}'. ` + `Check the job id with: specialists ps ${args.reuseJobId} --json`);
|
|
31109
31250
|
process.exit(1);
|
|
31110
31251
|
}
|
|
31111
31252
|
const targetJobStatus = targetStatus.status;
|
|
@@ -31553,7 +31694,9 @@ ${bold11(`Running ${cyan6(args.name)}`)}
|
|
|
31553
31694
|
${green9("\u2713")} ${footer}
|
|
31554
31695
|
|
|
31555
31696
|
`);
|
|
31556
|
-
process.stderr.write(dim9(`
|
|
31697
|
+
process.stderr.write(dim9(`Status: specialists ps ${jobId} --json
|
|
31698
|
+
`));
|
|
31699
|
+
process.stderr.write(dim9(`Events: specialists feed ${jobId}
|
|
31557
31700
|
|
|
31558
31701
|
`));
|
|
31559
31702
|
process.exit(0);
|
|
@@ -31658,8 +31801,8 @@ class JobControl {
|
|
|
31658
31801
|
}
|
|
31659
31802
|
};
|
|
31660
31803
|
let resolveJobId;
|
|
31661
|
-
const jobIdPromise = new Promise((
|
|
31662
|
-
resolveJobId =
|
|
31804
|
+
const jobIdPromise = new Promise((resolve10) => {
|
|
31805
|
+
resolveJobId = resolve10;
|
|
31663
31806
|
});
|
|
31664
31807
|
this.supervisor = new Supervisor({
|
|
31665
31808
|
runner: this.runner,
|
|
@@ -31724,7 +31867,7 @@ class JobControl {
|
|
|
31724
31867
|
if (deadline !== undefined && Date.now() >= deadline) {
|
|
31725
31868
|
throw new Error(`Timed out waiting for terminal status for job ${jobId}`);
|
|
31726
31869
|
}
|
|
31727
|
-
await new Promise((
|
|
31870
|
+
await new Promise((resolve10) => setTimeout(resolve10, backoffMs));
|
|
31728
31871
|
backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
|
|
31729
31872
|
}
|
|
31730
31873
|
}
|
|
@@ -31903,7 +32046,7 @@ function hashOutput(output2, salt) {
|
|
|
31903
32046
|
return createHash4("sha256").update(value).digest("hex");
|
|
31904
32047
|
}
|
|
31905
32048
|
function sleep2(ms) {
|
|
31906
|
-
return new Promise((
|
|
32049
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
31907
32050
|
}
|
|
31908
32051
|
function toContextHealth(contextPct) {
|
|
31909
32052
|
if (contextPct === null)
|
|
@@ -33819,7 +33962,7 @@ __export(exports_node, {
|
|
|
33819
33962
|
import { existsSync as existsSync19, readFileSync as readFileSync19, readdirSync as readdirSync7 } from "fs";
|
|
33820
33963
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
33821
33964
|
import { spawnSync as spawnSync14 } from "child_process";
|
|
33822
|
-
import { basename as basename5, join as join21, resolve as
|
|
33965
|
+
import { basename as basename5, join as join21, resolve as resolve10 } from "path";
|
|
33823
33966
|
function parseNodeArgs(argv) {
|
|
33824
33967
|
const command = argv[0];
|
|
33825
33968
|
const supportedCommands = new Set(["run", "list", "promote", "members", "memory", "stop", "spawn-member", "create-bead", "complete", "wait-phase"]);
|
|
@@ -34008,7 +34151,7 @@ function toNodeName(filePath) {
|
|
|
34008
34151
|
function discoverNodeConfigs(cwd) {
|
|
34009
34152
|
const discoveredByName = new Map;
|
|
34010
34153
|
for (const directory of NODE_DISCOVERY_DIRS) {
|
|
34011
|
-
const absoluteDir =
|
|
34154
|
+
const absoluteDir = resolve10(cwd, directory.path);
|
|
34012
34155
|
if (!existsSync19(absoluteDir))
|
|
34013
34156
|
continue;
|
|
34014
34157
|
const files = readdirSync7(absoluteDir).filter((fileName) => fileName.endsWith(NODE_CONFIG_SUFFIX));
|
|
@@ -34030,7 +34173,7 @@ function getNodeDiscoverySummary() {
|
|
|
34030
34173
|
`);
|
|
34031
34174
|
}
|
|
34032
34175
|
function resolveNodeConfigPath(cwd, input2) {
|
|
34033
|
-
const explicitPath =
|
|
34176
|
+
const explicitPath = resolve10(cwd, input2);
|
|
34034
34177
|
if (existsSync19(explicitPath)) {
|
|
34035
34178
|
return { name: toNodeName(explicitPath), path: explicitPath, source: "repo" };
|
|
34036
34179
|
}
|
|
@@ -34583,7 +34726,7 @@ async function handleNodeAction(args) {
|
|
|
34583
34726
|
if (deadline !== null && Date.now() >= deadline) {
|
|
34584
34727
|
throw new Error(`Timed out waiting for phase '${args.phaseId}' members: ${memberKeys.join(", ")}`);
|
|
34585
34728
|
}
|
|
34586
|
-
await new Promise((
|
|
34729
|
+
await new Promise((resolve11) => setTimeout(resolve11, 500));
|
|
34587
34730
|
}
|
|
34588
34731
|
} finally {
|
|
34589
34732
|
sqliteClient.close();
|
|
@@ -35455,9 +35598,19 @@ var init_epic = __esm(() => {
|
|
|
35455
35598
|
|
|
35456
35599
|
// src/cli/version-check.ts
|
|
35457
35600
|
import { spawnSync as spawnSync16 } from "child_process";
|
|
35458
|
-
import { existsSync as existsSync20, mkdirSync as
|
|
35459
|
-
import { dirname as
|
|
35601
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync10, readFileSync as readFileSync20, writeFileSync as writeFileSync9 } from "fs";
|
|
35602
|
+
import { dirname as dirname9, join as join22 } from "path";
|
|
35460
35603
|
import { createRequire as createRequire3 } from "module";
|
|
35604
|
+
function readBundledPackageVersion(requireFn = require3) {
|
|
35605
|
+
for (const candidate of ["../package.json", "../../package.json"]) {
|
|
35606
|
+
try {
|
|
35607
|
+
const pkg = requireFn(candidate);
|
|
35608
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0)
|
|
35609
|
+
return pkg.version;
|
|
35610
|
+
} catch {}
|
|
35611
|
+
}
|
|
35612
|
+
return "0.0.0";
|
|
35613
|
+
}
|
|
35461
35614
|
function shouldRunVersionCheck() {
|
|
35462
35615
|
if (process.env.SPECIALISTS_OFFLINE === "1")
|
|
35463
35616
|
return false;
|
|
@@ -35480,7 +35633,7 @@ function readCachedVersionCheck() {
|
|
|
35480
35633
|
return readCache();
|
|
35481
35634
|
}
|
|
35482
35635
|
function writeCache(cache) {
|
|
35483
|
-
|
|
35636
|
+
mkdirSync10(dirname9(CACHE_PATH), { recursive: true });
|
|
35484
35637
|
writeFileSync9(CACHE_PATH, `${JSON.stringify(cache, null, 2)}
|
|
35485
35638
|
`, "utf8");
|
|
35486
35639
|
}
|
|
@@ -35565,7 +35718,7 @@ function markVersionCheckNotified(result) {
|
|
|
35565
35718
|
var require3, packageVersion, localVersion, CACHE_PATH, CACHE_MAX_AGE_MS2, NETWORK_TIMEOUT_MS = 2000;
|
|
35566
35719
|
var init_version_check = __esm(() => {
|
|
35567
35720
|
require3 = createRequire3(import.meta.url);
|
|
35568
|
-
|
|
35721
|
+
packageVersion = readBundledPackageVersion();
|
|
35569
35722
|
localVersion = packageVersion;
|
|
35570
35723
|
CACHE_PATH = join22(process.cwd(), ".specialists", "version-check.json");
|
|
35571
35724
|
CACHE_MAX_AGE_MS2 = 6 * 60 * 60 * 1000;
|
|
@@ -37382,9 +37535,6 @@ function readJobEvents(jobDir) {
|
|
|
37382
37535
|
events.sort(compareTimelineEvents);
|
|
37383
37536
|
return events;
|
|
37384
37537
|
}
|
|
37385
|
-
function readJobEventsById(jobsDir, jobId) {
|
|
37386
|
-
return readJobEvents(join26(jobsDir, jobId));
|
|
37387
|
-
}
|
|
37388
37538
|
function readAllJobEvents(jobsDir) {
|
|
37389
37539
|
const sqliteClient = createObservabilitySqliteClient();
|
|
37390
37540
|
try {
|
|
@@ -38056,7 +38206,7 @@ async function followMerged(sqliteClient, jobsDir, options) {
|
|
|
38056
38206
|
}
|
|
38057
38207
|
const lastPrintedEventKey = new Map;
|
|
38058
38208
|
const seenMetaKey = new Map;
|
|
38059
|
-
await new Promise((
|
|
38209
|
+
await new Promise((resolve11) => {
|
|
38060
38210
|
const interval = setInterval(() => {
|
|
38061
38211
|
const currentJobIds = listMatchingJobIds(sqliteClient, jobsDir, options);
|
|
38062
38212
|
const statusByJobId = new Map;
|
|
@@ -38144,7 +38294,7 @@ async function followMerged(sqliteClient, jobsDir, options) {
|
|
|
38144
38294
|
});
|
|
38145
38295
|
if (completedJobs.size === trackedJobs.size || allTrackedTerminal) {
|
|
38146
38296
|
clearInterval(interval);
|
|
38147
|
-
|
|
38297
|
+
resolve11();
|
|
38148
38298
|
}
|
|
38149
38299
|
}
|
|
38150
38300
|
}, 750);
|
|
@@ -38189,130 +38339,13 @@ var init_feed = __esm(() => {
|
|
|
38189
38339
|
init_format_helpers();
|
|
38190
38340
|
});
|
|
38191
38341
|
|
|
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
38342
|
// src/cli/steer.ts
|
|
38310
38343
|
var exports_steer = {};
|
|
38311
38344
|
__export(exports_steer, {
|
|
38312
|
-
run: () =>
|
|
38345
|
+
run: () => run19
|
|
38313
38346
|
});
|
|
38314
38347
|
import { writeFileSync as writeFileSync10 } from "fs";
|
|
38315
|
-
async function
|
|
38348
|
+
async function run19() {
|
|
38316
38349
|
const jobId = process.argv[3];
|
|
38317
38350
|
const message = process.argv[4];
|
|
38318
38351
|
if (!jobId || !message) {
|
|
@@ -38363,10 +38396,10 @@ var init_steer = __esm(() => {
|
|
|
38363
38396
|
// src/cli/resume.ts
|
|
38364
38397
|
var exports_resume = {};
|
|
38365
38398
|
__export(exports_resume, {
|
|
38366
|
-
run: () =>
|
|
38399
|
+
run: () => run20
|
|
38367
38400
|
});
|
|
38368
38401
|
import { writeFileSync as writeFileSync11 } from "fs";
|
|
38369
|
-
async function
|
|
38402
|
+
async function run20() {
|
|
38370
38403
|
const jobId = process.argv[3];
|
|
38371
38404
|
const task = process.argv[4];
|
|
38372
38405
|
if (!jobId || !task) {
|
|
@@ -38419,24 +38452,24 @@ var init_resume = __esm(() => {
|
|
|
38419
38452
|
// src/cli/follow-up.ts
|
|
38420
38453
|
var exports_follow_up = {};
|
|
38421
38454
|
__export(exports_follow_up, {
|
|
38422
|
-
run: () =>
|
|
38455
|
+
run: () => run21
|
|
38423
38456
|
});
|
|
38424
|
-
async function
|
|
38457
|
+
async function run21() {
|
|
38425
38458
|
process.stderr.write("\x1B[33m\u26A0 DEPRECATED:\x1B[0m `specialists follow-up` is deprecated. Use `specialists resume` instead.\n\n");
|
|
38426
38459
|
const { run: resumeRun } = await Promise.resolve().then(() => (init_resume(), exports_resume));
|
|
38427
38460
|
return resumeRun();
|
|
38428
38461
|
}
|
|
38429
38462
|
|
|
38430
38463
|
// src/specialist/worktree-gc.ts
|
|
38431
|
-
import { existsSync as
|
|
38432
|
-
import { join as
|
|
38464
|
+
import { existsSync as existsSync26, readdirSync as readdirSync11, readFileSync as readFileSync26 } from "fs";
|
|
38465
|
+
import { join as join28 } from "path";
|
|
38433
38466
|
import { spawnSync as spawnSync19 } from "child_process";
|
|
38434
38467
|
function readJobStatus2(jobDir) {
|
|
38435
|
-
const statusPath =
|
|
38436
|
-
if (!
|
|
38468
|
+
const statusPath = join28(jobDir, "status.json");
|
|
38469
|
+
if (!existsSync26(statusPath))
|
|
38437
38470
|
return null;
|
|
38438
38471
|
try {
|
|
38439
|
-
return JSON.parse(
|
|
38472
|
+
return JSON.parse(readFileSync26(statusPath, "utf-8"));
|
|
38440
38473
|
} catch {
|
|
38441
38474
|
return null;
|
|
38442
38475
|
}
|
|
@@ -38458,7 +38491,7 @@ function collectWorktreeGcCandidates(jobsDir) {
|
|
|
38458
38491
|
const worktreePath = status.worktree_path;
|
|
38459
38492
|
if (!worktreePath)
|
|
38460
38493
|
return null;
|
|
38461
|
-
if (!
|
|
38494
|
+
if (!existsSync26(worktreePath))
|
|
38462
38495
|
return null;
|
|
38463
38496
|
return {
|
|
38464
38497
|
jobId: status.id,
|
|
@@ -38470,13 +38503,13 @@ function collectWorktreeGcCandidates(jobsDir) {
|
|
|
38470
38503
|
}
|
|
38471
38504
|
if (!getFileFallbackEnabled())
|
|
38472
38505
|
return [];
|
|
38473
|
-
if (!
|
|
38506
|
+
if (!existsSync26(jobsDir))
|
|
38474
38507
|
return [];
|
|
38475
38508
|
const candidates = [];
|
|
38476
38509
|
for (const entry of readdirSync11(jobsDir, { withFileTypes: true })) {
|
|
38477
38510
|
if (!entry.isDirectory())
|
|
38478
38511
|
continue;
|
|
38479
|
-
const status = readJobStatus2(
|
|
38512
|
+
const status = readJobStatus2(join28(jobsDir, entry.name));
|
|
38480
38513
|
if (!status)
|
|
38481
38514
|
continue;
|
|
38482
38515
|
if (isActive(status.status))
|
|
@@ -38486,7 +38519,7 @@ function collectWorktreeGcCandidates(jobsDir) {
|
|
|
38486
38519
|
const { worktree_path: worktreePath, branch } = status;
|
|
38487
38520
|
if (!worktreePath)
|
|
38488
38521
|
continue;
|
|
38489
|
-
if (!
|
|
38522
|
+
if (!existsSync26(worktreePath))
|
|
38490
38523
|
continue;
|
|
38491
38524
|
candidates.push({
|
|
38492
38525
|
jobId: status.id,
|
|
@@ -38530,10 +38563,10 @@ var init_worktree_gc = __esm(() => {
|
|
|
38530
38563
|
// src/cli/clean.ts
|
|
38531
38564
|
var exports_clean = {};
|
|
38532
38565
|
__export(exports_clean, {
|
|
38533
|
-
run: () =>
|
|
38566
|
+
run: () => run22
|
|
38534
38567
|
});
|
|
38535
|
-
import { existsSync as
|
|
38536
|
-
import { join as
|
|
38568
|
+
import { existsSync as existsSync27, readFileSync as readFileSync27, readdirSync as readdirSync12, rmSync as rmSync3, statSync as statSync4 } from "fs";
|
|
38569
|
+
import { join as join29 } from "path";
|
|
38537
38570
|
function parseTtlDaysFromEnvironment() {
|
|
38538
38571
|
const rawValue = process.env.SPECIALISTS_JOB_TTL_DAYS ?? process.env.JOB_TTL_DAYS;
|
|
38539
38572
|
if (!rawValue)
|
|
@@ -38616,7 +38649,7 @@ function parseOptions2(argv) {
|
|
|
38616
38649
|
function readDirectorySizeBytes(directoryPath) {
|
|
38617
38650
|
let totalBytes = 0;
|
|
38618
38651
|
for (const entry of readdirSync12(directoryPath, { withFileTypes: true })) {
|
|
38619
|
-
const entryPath =
|
|
38652
|
+
const entryPath = join29(directoryPath, entry.name);
|
|
38620
38653
|
const stats = statSync4(entryPath);
|
|
38621
38654
|
totalBytes += stats.isDirectory() ? readDirectorySizeBytes(entryPath) : stats.size;
|
|
38622
38655
|
}
|
|
@@ -38624,7 +38657,7 @@ function readDirectorySizeBytes(directoryPath) {
|
|
|
38624
38657
|
}
|
|
38625
38658
|
function containsProtectedSqliteArtifact(directoryPath) {
|
|
38626
38659
|
for (const entry of readdirSync12(directoryPath, { withFileTypes: true })) {
|
|
38627
|
-
const entryPath =
|
|
38660
|
+
const entryPath = join29(directoryPath, entry.name);
|
|
38628
38661
|
if (entry.isDirectory()) {
|
|
38629
38662
|
if (containsProtectedSqliteArtifact(entryPath))
|
|
38630
38663
|
return true;
|
|
@@ -38645,15 +38678,15 @@ function getJobTimestamps(status) {
|
|
|
38645
38678
|
function readCompletedJobDirectory(baseDirectory, entry) {
|
|
38646
38679
|
if (!entry.isDirectory())
|
|
38647
38680
|
return null;
|
|
38648
|
-
const directoryPath =
|
|
38681
|
+
const directoryPath = join29(baseDirectory, entry.name);
|
|
38649
38682
|
if (containsProtectedSqliteArtifact(directoryPath))
|
|
38650
38683
|
return null;
|
|
38651
|
-
const statusFilePath =
|
|
38652
|
-
if (!
|
|
38684
|
+
const statusFilePath = join29(directoryPath, "status.json");
|
|
38685
|
+
if (!existsSync27(statusFilePath))
|
|
38653
38686
|
return null;
|
|
38654
38687
|
let statusData;
|
|
38655
38688
|
try {
|
|
38656
|
-
statusData = JSON.parse(
|
|
38689
|
+
statusData = JSON.parse(readFileSync27(statusFilePath, "utf-8"));
|
|
38657
38690
|
} catch {
|
|
38658
38691
|
return null;
|
|
38659
38692
|
}
|
|
@@ -38667,8 +38700,8 @@ function collectCompletedJobs(jobsDirectoryPath) {
|
|
|
38667
38700
|
const statuses = sqliteClient?.listStatuses() ?? [];
|
|
38668
38701
|
if (statuses.length > 0) {
|
|
38669
38702
|
return statuses.filter((status) => COMPLETED_STATUSES.has(status.status)).map((status) => {
|
|
38670
|
-
const directoryPath =
|
|
38671
|
-
if (!
|
|
38703
|
+
const directoryPath = join29(jobsDirectoryPath, status.id);
|
|
38704
|
+
if (!existsSync27(directoryPath) || containsProtectedSqliteArtifact(directoryPath))
|
|
38672
38705
|
return null;
|
|
38673
38706
|
const { createdAtMs, completedAtMs } = getJobTimestamps(status);
|
|
38674
38707
|
return { id: status.id, directoryPath, completedAtMs, createdAtMs, sizeBytes: readDirectorySizeBytes(directoryPath) };
|
|
@@ -38808,7 +38841,7 @@ function removeStaleProcesses(statuses, dryRun) {
|
|
|
38808
38841
|
}
|
|
38809
38842
|
return updatedCount;
|
|
38810
38843
|
}
|
|
38811
|
-
async function
|
|
38844
|
+
async function run22() {
|
|
38812
38845
|
let options;
|
|
38813
38846
|
try {
|
|
38814
38847
|
options = parseOptions2(process.argv.slice(3));
|
|
@@ -38817,7 +38850,7 @@ async function run23() {
|
|
|
38817
38850
|
printUsageAndExit2(message);
|
|
38818
38851
|
}
|
|
38819
38852
|
const jobsDirectoryPath = resolveJobsDir();
|
|
38820
|
-
if (!
|
|
38853
|
+
if (!existsSync27(jobsDirectoryPath)) {
|
|
38821
38854
|
console.log("No jobs directory found.");
|
|
38822
38855
|
return;
|
|
38823
38856
|
}
|
|
@@ -38871,7 +38904,7 @@ var init_clean = __esm(() => {
|
|
|
38871
38904
|
// src/cli/end.ts
|
|
38872
38905
|
var exports_end = {};
|
|
38873
38906
|
__export(exports_end, {
|
|
38874
|
-
run: () =>
|
|
38907
|
+
run: () => run23
|
|
38875
38908
|
});
|
|
38876
38909
|
import { spawnSync as spawnSync20 } from "child_process";
|
|
38877
38910
|
function parseOptions3(argv) {
|
|
@@ -38955,7 +38988,7 @@ async function publishChain(beadId, options) {
|
|
|
38955
38988
|
console.log("Publication mode: direct merge");
|
|
38956
38989
|
}
|
|
38957
38990
|
}
|
|
38958
|
-
async function
|
|
38991
|
+
async function run23() {
|
|
38959
38992
|
let options;
|
|
38960
38993
|
try {
|
|
38961
38994
|
options = parseOptions3(process.argv.slice(3));
|
|
@@ -38996,7 +39029,7 @@ var init_end = __esm(() => {
|
|
|
38996
39029
|
// src/cli/stop.ts
|
|
38997
39030
|
var exports_stop = {};
|
|
38998
39031
|
__export(exports_stop, {
|
|
38999
|
-
run: () =>
|
|
39032
|
+
run: () => run24
|
|
39000
39033
|
});
|
|
39001
39034
|
function resolveTerminalStatus(jobId) {
|
|
39002
39035
|
return hasRunCompleteEvent(jobId) ? "done" : "cancelled";
|
|
@@ -39027,7 +39060,7 @@ async function waitForProcessExit(pid, timeoutMs) {
|
|
|
39027
39060
|
while (Date.now() < deadline) {
|
|
39028
39061
|
if (!isProcessAlive(pid))
|
|
39029
39062
|
return true;
|
|
39030
|
-
await new Promise((
|
|
39063
|
+
await new Promise((resolve11) => setTimeout(resolve11, 100));
|
|
39031
39064
|
}
|
|
39032
39065
|
return !isProcessAlive(pid);
|
|
39033
39066
|
}
|
|
@@ -39039,7 +39072,7 @@ function tryKillProcessGroup(pid) {
|
|
|
39039
39072
|
throw err;
|
|
39040
39073
|
}
|
|
39041
39074
|
}
|
|
39042
|
-
async function
|
|
39075
|
+
async function run24() {
|
|
39043
39076
|
let parsed;
|
|
39044
39077
|
try {
|
|
39045
39078
|
parsed = parseStopArgs(process.argv.slice(3));
|
|
@@ -39153,18 +39186,18 @@ var init_stop = __esm(() => {
|
|
|
39153
39186
|
// src/cli/attach.ts
|
|
39154
39187
|
var exports_attach = {};
|
|
39155
39188
|
__export(exports_attach, {
|
|
39156
|
-
run: () =>
|
|
39189
|
+
run: () => run25
|
|
39157
39190
|
});
|
|
39158
39191
|
import { execFileSync as execFileSync3, spawnSync as spawnSync21 } from "child_process";
|
|
39159
|
-
import { readFileSync as
|
|
39160
|
-
import { join as
|
|
39192
|
+
import { readFileSync as readFileSync28 } from "fs";
|
|
39193
|
+
import { join as join30 } from "path";
|
|
39161
39194
|
function exitWithError(message) {
|
|
39162
39195
|
console.error(message);
|
|
39163
39196
|
process.exit(1);
|
|
39164
39197
|
}
|
|
39165
39198
|
function readStatus(statusPath, jobId) {
|
|
39166
39199
|
try {
|
|
39167
|
-
return JSON.parse(
|
|
39200
|
+
return JSON.parse(readFileSync28(statusPath, "utf-8"));
|
|
39168
39201
|
} catch (error2) {
|
|
39169
39202
|
if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
|
|
39170
39203
|
exitWithError(`Job \`${jobId}\` not found. Run \`specialists status\` to see active jobs in current mode.`);
|
|
@@ -39173,13 +39206,13 @@ function readStatus(statusPath, jobId) {
|
|
|
39173
39206
|
exitWithError(`Failed to read status for job \`${jobId}\`: ${details}`);
|
|
39174
39207
|
}
|
|
39175
39208
|
}
|
|
39176
|
-
async function
|
|
39209
|
+
async function run25() {
|
|
39177
39210
|
const [jobId] = process.argv.slice(3);
|
|
39178
39211
|
if (!jobId) {
|
|
39179
39212
|
exitWithError("Usage: specialists attach <job-id> (normal runtime is DB-backed; job files are legacy/operator-only)");
|
|
39180
39213
|
}
|
|
39181
|
-
const jobsDir =
|
|
39182
|
-
const statusPath =
|
|
39214
|
+
const jobsDir = join30(process.cwd(), ".specialists", "jobs");
|
|
39215
|
+
const statusPath = join30(jobsDir, jobId, "status.json");
|
|
39183
39216
|
const status = readStatus(statusPath, jobId);
|
|
39184
39217
|
if (status.status === "done" || status.status === "error") {
|
|
39185
39218
|
exitWithError(`Job \`${jobId}\` has already completed (status: ${status.status}). Use \`specialists result ${jobId}\` to read output.`);
|
|
@@ -39201,15 +39234,15 @@ async function run26() {
|
|
|
39201
39234
|
var init_attach = () => {};
|
|
39202
39235
|
|
|
39203
39236
|
// src/specialist/drift-detector.ts
|
|
39204
|
-
import { existsSync as
|
|
39205
|
-
import { join as
|
|
39237
|
+
import { existsSync as existsSync28, readFileSync as readFileSync29, readdirSync as readdirSync13, rmSync as rmSync4 } from "fs";
|
|
39238
|
+
import { join as join31, resolve as resolve11, relative as relative3 } from "path";
|
|
39206
39239
|
function listFiles(root) {
|
|
39207
|
-
if (!
|
|
39240
|
+
if (!existsSync28(root))
|
|
39208
39241
|
return [];
|
|
39209
39242
|
const out = [];
|
|
39210
39243
|
const visit2 = (dir) => {
|
|
39211
39244
|
for (const entry of readdirSync13(dir, { withFileTypes: true })) {
|
|
39212
|
-
const full =
|
|
39245
|
+
const full = join31(dir, entry.name);
|
|
39213
39246
|
if (entry.isDirectory()) {
|
|
39214
39247
|
visit2(full);
|
|
39215
39248
|
continue;
|
|
@@ -39222,7 +39255,7 @@ function listFiles(root) {
|
|
|
39222
39255
|
return out;
|
|
39223
39256
|
}
|
|
39224
39257
|
function relPath(path, base) {
|
|
39225
|
-
return
|
|
39258
|
+
return relative3(base, path) || ".";
|
|
39226
39259
|
}
|
|
39227
39260
|
function makeFinding(repoRoot, kind, scope, path, canonicalPath, bytesEqual) {
|
|
39228
39261
|
const rel = relPath(path, repoRoot);
|
|
@@ -39240,18 +39273,18 @@ function detectDriftForRepo(repoRoot) {
|
|
|
39240
39273
|
if (!asset.canonicalDir)
|
|
39241
39274
|
continue;
|
|
39242
39275
|
const scopes = [
|
|
39243
|
-
{ scope: "default", dir:
|
|
39244
|
-
{ scope: "user", dir:
|
|
39276
|
+
{ scope: "default", dir: resolve11(repoRoot, asset.managedDir) },
|
|
39277
|
+
{ scope: "user", dir: resolve11(repoRoot, ".specialists/user") }
|
|
39245
39278
|
];
|
|
39246
39279
|
for (const { scope, dir } of scopes) {
|
|
39247
|
-
if (!
|
|
39280
|
+
if (!existsSync28(dir))
|
|
39248
39281
|
continue;
|
|
39249
39282
|
for (const file of listFiles(dir)) {
|
|
39250
39283
|
const rel = relPath(file, dir);
|
|
39251
|
-
const canonicalPath =
|
|
39252
|
-
if (!
|
|
39284
|
+
const canonicalPath = join31(asset.canonicalDir, rel);
|
|
39285
|
+
if (!existsSync28(canonicalPath))
|
|
39253
39286
|
continue;
|
|
39254
|
-
const bytesEqual =
|
|
39287
|
+
const bytesEqual = readFileSync29(file).equals(readFileSync29(canonicalPath));
|
|
39255
39288
|
findings.push(makeFinding(repoRoot, asset.kind, scope, file, canonicalPath, bytesEqual));
|
|
39256
39289
|
}
|
|
39257
39290
|
}
|
|
@@ -39275,13 +39308,13 @@ function detectDriftUnderRoot(root) {
|
|
|
39275
39308
|
continue;
|
|
39276
39309
|
if (entry.name === "node_modules" || entry.name === ".git")
|
|
39277
39310
|
continue;
|
|
39278
|
-
visit2(
|
|
39311
|
+
visit2(join31(dir, entry.name));
|
|
39279
39312
|
}
|
|
39280
39313
|
};
|
|
39281
|
-
visit2(
|
|
39314
|
+
visit2(resolve11(root));
|
|
39282
39315
|
const summary = repos.flatMap((r) => r.findings);
|
|
39283
39316
|
return {
|
|
39284
|
-
root:
|
|
39317
|
+
root: resolve11(root),
|
|
39285
39318
|
repos,
|
|
39286
39319
|
summary: {
|
|
39287
39320
|
repos: repos.length,
|
|
@@ -39315,10 +39348,10 @@ var init_drift_detector = __esm(() => {
|
|
|
39315
39348
|
// src/cli/prune-stale-defaults.ts
|
|
39316
39349
|
var exports_prune_stale_defaults = {};
|
|
39317
39350
|
__export(exports_prune_stale_defaults, {
|
|
39318
|
-
run: () =>
|
|
39351
|
+
run: () => run26
|
|
39319
39352
|
});
|
|
39320
|
-
import { resolve as
|
|
39321
|
-
function
|
|
39353
|
+
import { resolve as resolve12 } from "path";
|
|
39354
|
+
function parseArgs11(argv) {
|
|
39322
39355
|
let dryRun = false;
|
|
39323
39356
|
let root = process.cwd();
|
|
39324
39357
|
let help = false;
|
|
@@ -39332,7 +39365,7 @@ function parseArgs12(argv) {
|
|
|
39332
39365
|
const value = argv[i + 1];
|
|
39333
39366
|
if (!value || value.startsWith("--"))
|
|
39334
39367
|
throw new Error("--root requires a value");
|
|
39335
|
-
root =
|
|
39368
|
+
root = resolve12(value);
|
|
39336
39369
|
i += 1;
|
|
39337
39370
|
continue;
|
|
39338
39371
|
}
|
|
@@ -39349,8 +39382,8 @@ function printHelp() {
|
|
|
39349
39382
|
console.log(" --dry-run List stale default snapshots without pruning");
|
|
39350
39383
|
console.log(" --root Repo root to scan");
|
|
39351
39384
|
}
|
|
39352
|
-
async function
|
|
39353
|
-
const { dryRun, root, help } =
|
|
39385
|
+
async function run26(argv = process.argv.slice(3)) {
|
|
39386
|
+
const { dryRun, root, help } = parseArgs11(argv);
|
|
39354
39387
|
if (help) {
|
|
39355
39388
|
printHelp();
|
|
39356
39389
|
return;
|
|
@@ -39376,7 +39409,7 @@ var init_prune_stale_defaults = __esm(() => {
|
|
|
39376
39409
|
// src/cli/quickstart.ts
|
|
39377
39410
|
var exports_quickstart = {};
|
|
39378
39411
|
__export(exports_quickstart, {
|
|
39379
|
-
run: () =>
|
|
39412
|
+
run: () => run27
|
|
39380
39413
|
});
|
|
39381
39414
|
function section2(title) {
|
|
39382
39415
|
const bar = "\u2500".repeat(60);
|
|
@@ -39390,7 +39423,7 @@ function cmd2(s) {
|
|
|
39390
39423
|
function flag(s) {
|
|
39391
39424
|
return green13(s);
|
|
39392
39425
|
}
|
|
39393
|
-
async function
|
|
39426
|
+
async function run27() {
|
|
39394
39427
|
const lines = [
|
|
39395
39428
|
"",
|
|
39396
39429
|
bold12("specialists \xB7 Quick Start Guide"),
|
|
@@ -39605,7 +39638,7 @@ var bold12 = (s) => `\x1B[1m${s}\x1B[0m`, dim12 = (s) => `\x1B[2m${s}\x1B[0m`, y
|
|
|
39605
39638
|
var exports_doctor = {};
|
|
39606
39639
|
__export(exports_doctor, {
|
|
39607
39640
|
setStatusError: () => setStatusError,
|
|
39608
|
-
run: () =>
|
|
39641
|
+
run: () => run28,
|
|
39609
39642
|
renderProcessSummary: () => renderProcessSummary,
|
|
39610
39643
|
parseVersionTuple: () => parseVersionTuple,
|
|
39611
39644
|
compareVersions: () => compareVersions2,
|
|
@@ -39613,8 +39646,8 @@ __export(exports_doctor, {
|
|
|
39613
39646
|
});
|
|
39614
39647
|
import { createHash as createHash5 } from "crypto";
|
|
39615
39648
|
import { spawnSync as spawnSync22 } from "child_process";
|
|
39616
|
-
import { existsSync as
|
|
39617
|
-
import { dirname as
|
|
39649
|
+
import { existsSync as existsSync29, lstatSync as lstatSync2, mkdirSync as mkdirSync11, readdirSync as readdirSync14, readFileSync as readFileSync30, readlinkSync as readlinkSync2, writeFileSync as writeFileSync12 } from "fs";
|
|
39650
|
+
import { dirname as dirname10, join as join32, relative as relative4, resolve as resolve13 } from "path";
|
|
39618
39651
|
function ok3(msg) {
|
|
39619
39652
|
console.log(` ${green14("\u2713")} ${msg}`);
|
|
39620
39653
|
}
|
|
@@ -39643,10 +39676,10 @@ function isInstalled3(bin) {
|
|
|
39643
39676
|
return spawnSync22("which", [bin], { encoding: "utf8", timeout: 2000 }).status === 0;
|
|
39644
39677
|
}
|
|
39645
39678
|
function loadJson2(path) {
|
|
39646
|
-
if (!
|
|
39679
|
+
if (!existsSync29(path))
|
|
39647
39680
|
return null;
|
|
39648
39681
|
try {
|
|
39649
|
-
return JSON.parse(
|
|
39682
|
+
return JSON.parse(readFileSync30(path, "utf8"));
|
|
39650
39683
|
} catch {
|
|
39651
39684
|
return null;
|
|
39652
39685
|
}
|
|
@@ -39689,7 +39722,7 @@ function checkBd() {
|
|
|
39689
39722
|
return false;
|
|
39690
39723
|
}
|
|
39691
39724
|
ok3(`bd installed ${dim13(sp("bd", ["--version"]).stdout || "")}`);
|
|
39692
|
-
if (
|
|
39725
|
+
if (existsSync29(join32(CWD, ".beads")))
|
|
39693
39726
|
ok3(".beads/ present in project");
|
|
39694
39727
|
else
|
|
39695
39728
|
warn3(".beads/ not found in project");
|
|
@@ -39709,22 +39742,22 @@ function checkHooks() {
|
|
|
39709
39742
|
section3("Claude Code hooks (2 expected)");
|
|
39710
39743
|
let allPresent = true;
|
|
39711
39744
|
for (const name of HOOK_NAMES) {
|
|
39712
|
-
const canonicalPath =
|
|
39713
|
-
if (!
|
|
39714
|
-
fail4(`${
|
|
39745
|
+
const canonicalPath = join32(HOOKS_DIR, name);
|
|
39746
|
+
if (!existsSync29(canonicalPath)) {
|
|
39747
|
+
fail4(`${relative4(CWD, canonicalPath)} ${red7("missing")}`);
|
|
39715
39748
|
fix("specialists init");
|
|
39716
39749
|
allPresent = false;
|
|
39717
39750
|
} else {
|
|
39718
|
-
ok3(
|
|
39751
|
+
ok3(relative4(CWD, canonicalPath));
|
|
39719
39752
|
}
|
|
39720
|
-
const claudeHookPath =
|
|
39753
|
+
const claudeHookPath = join32(CLAUDE_HOOKS_DIR, name);
|
|
39721
39754
|
const symlinkState = isSymlinkTo(claudeHookPath, canonicalPath);
|
|
39722
39755
|
if (symlinkState.ok) {
|
|
39723
|
-
ok3(`${
|
|
39756
|
+
ok3(`${relative4(CWD, claudeHookPath)} -> ${relative4(dirname10(claudeHookPath), canonicalPath)}`);
|
|
39724
39757
|
continue;
|
|
39725
39758
|
}
|
|
39726
39759
|
allPresent = false;
|
|
39727
|
-
const relHookPath =
|
|
39760
|
+
const relHookPath = relative4(CWD, claudeHookPath);
|
|
39728
39761
|
if (symlinkState.reason === "missing") {
|
|
39729
39762
|
fail4(`${relHookPath} missing`);
|
|
39730
39763
|
} else if (symlinkState.reason === "not-symlink") {
|
|
@@ -39792,30 +39825,30 @@ function checkVersion() {
|
|
|
39792
39825
|
}
|
|
39793
39826
|
function hashFile(path) {
|
|
39794
39827
|
const hash = createHash5("sha256");
|
|
39795
|
-
hash.update(
|
|
39828
|
+
hash.update(readFileSync30(path));
|
|
39796
39829
|
return hash.digest("hex");
|
|
39797
39830
|
}
|
|
39798
39831
|
function collectFileHashes(rootDir) {
|
|
39799
39832
|
const hashes = new Map;
|
|
39800
39833
|
const visit2 = (dir) => {
|
|
39801
39834
|
for (const entry of readdirSync14(dir, { withFileTypes: true })) {
|
|
39802
|
-
const fullPath =
|
|
39835
|
+
const fullPath = join32(dir, entry.name);
|
|
39803
39836
|
if (entry.isDirectory()) {
|
|
39804
39837
|
visit2(fullPath);
|
|
39805
39838
|
continue;
|
|
39806
39839
|
}
|
|
39807
39840
|
if (!entry.isFile())
|
|
39808
39841
|
continue;
|
|
39809
|
-
const relPath2 =
|
|
39842
|
+
const relPath2 = relative4(rootDir, fullPath);
|
|
39810
39843
|
hashes.set(relPath2, hashFile(fullPath));
|
|
39811
39844
|
}
|
|
39812
39845
|
};
|
|
39813
|
-
if (
|
|
39846
|
+
if (existsSync29(rootDir))
|
|
39814
39847
|
visit2(rootDir);
|
|
39815
39848
|
return hashes;
|
|
39816
39849
|
}
|
|
39817
39850
|
function isSymlinkTo(linkPath, expectedTargetPath) {
|
|
39818
|
-
if (!
|
|
39851
|
+
if (!existsSync29(linkPath))
|
|
39819
39852
|
return { ok: false, reason: "missing" };
|
|
39820
39853
|
let stats;
|
|
39821
39854
|
try {
|
|
@@ -39827,8 +39860,8 @@ function isSymlinkTo(linkPath, expectedTargetPath) {
|
|
|
39827
39860
|
return { ok: false, reason: "not-symlink" };
|
|
39828
39861
|
try {
|
|
39829
39862
|
const rawTarget = readlinkSync2(linkPath);
|
|
39830
|
-
const resolvedTarget =
|
|
39831
|
-
const resolvedExpected =
|
|
39863
|
+
const resolvedTarget = resolve13(dirname10(linkPath), rawTarget);
|
|
39864
|
+
const resolvedExpected = resolve13(expectedTargetPath);
|
|
39832
39865
|
if (resolvedTarget !== resolvedExpected) {
|
|
39833
39866
|
return { ok: false, reason: "wrong-target", target: rawTarget };
|
|
39834
39867
|
}
|
|
@@ -39839,12 +39872,12 @@ function isSymlinkTo(linkPath, expectedTargetPath) {
|
|
|
39839
39872
|
}
|
|
39840
39873
|
function checkSkillDrift() {
|
|
39841
39874
|
section3("Skill drift (.xtrm skill sync)");
|
|
39842
|
-
if (!
|
|
39875
|
+
if (!existsSync29(CONFIG_SKILLS_DIR)) {
|
|
39843
39876
|
fail4("config/skills/ missing");
|
|
39844
39877
|
fix("restore config/skills/ from git");
|
|
39845
39878
|
return false;
|
|
39846
39879
|
}
|
|
39847
|
-
if (!
|
|
39880
|
+
if (!existsSync29(XTRM_DEFAULT_SKILLS_DIR)) {
|
|
39848
39881
|
fail4(".xtrm/skills/default/ missing");
|
|
39849
39882
|
fix("specialists init --sync-skills");
|
|
39850
39883
|
return false;
|
|
@@ -39886,22 +39919,22 @@ function checkSkillDrift() {
|
|
|
39886
39919
|
}
|
|
39887
39920
|
let linksOk = true;
|
|
39888
39921
|
for (const scope of ["claude", "pi"]) {
|
|
39889
|
-
const activeRoot =
|
|
39890
|
-
if (!
|
|
39891
|
-
fail4(`${
|
|
39922
|
+
const activeRoot = join32(XTRM_ACTIVE_SKILLS_DIR, scope);
|
|
39923
|
+
if (!existsSync29(activeRoot)) {
|
|
39924
|
+
fail4(`${relative4(CWD, activeRoot)}/ missing`);
|
|
39892
39925
|
fix("specialists init --sync-skills");
|
|
39893
39926
|
linksOk = false;
|
|
39894
39927
|
continue;
|
|
39895
39928
|
}
|
|
39896
39929
|
const defaultSkills = readdirSync14(XTRM_DEFAULT_SKILLS_DIR, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
39897
39930
|
for (const skillName of defaultSkills) {
|
|
39898
|
-
const activeLinkPath =
|
|
39899
|
-
const expectedTarget =
|
|
39931
|
+
const activeLinkPath = join32(activeRoot, skillName);
|
|
39932
|
+
const expectedTarget = join32(XTRM_DEFAULT_SKILLS_DIR, skillName);
|
|
39900
39933
|
const state = isSymlinkTo(activeLinkPath, expectedTarget);
|
|
39901
39934
|
if (state.ok)
|
|
39902
39935
|
continue;
|
|
39903
39936
|
linksOk = false;
|
|
39904
|
-
const relLink =
|
|
39937
|
+
const relLink = relative4(CWD, activeLinkPath);
|
|
39905
39938
|
if (state.reason === "missing") {
|
|
39906
39939
|
fail4(`${relLink} missing`);
|
|
39907
39940
|
} else if (state.reason === "not-symlink") {
|
|
@@ -39915,18 +39948,18 @@ function checkSkillDrift() {
|
|
|
39915
39948
|
}
|
|
39916
39949
|
}
|
|
39917
39950
|
const skillRootChecks = [
|
|
39918
|
-
{ root:
|
|
39919
|
-
{ root:
|
|
39951
|
+
{ root: join32(CLAUDE_DIR, "skills"), expected: ACTIVE_CLAUDE_SKILLS_DIR },
|
|
39952
|
+
{ root: join32(PI_DIR, "skills"), expected: ACTIVE_PI_SKILLS_DIR }
|
|
39920
39953
|
];
|
|
39921
39954
|
let rootLinksOk = true;
|
|
39922
39955
|
for (const check2 of skillRootChecks) {
|
|
39923
39956
|
const state = isSymlinkTo(check2.root, check2.expected);
|
|
39924
39957
|
if (state.ok) {
|
|
39925
|
-
ok3(`${
|
|
39958
|
+
ok3(`${relative4(CWD, check2.root)} -> ${relative4(dirname10(check2.root), check2.expected)}`);
|
|
39926
39959
|
continue;
|
|
39927
39960
|
}
|
|
39928
39961
|
rootLinksOk = false;
|
|
39929
|
-
const relRoot =
|
|
39962
|
+
const relRoot = relative4(CWD, check2.root);
|
|
39930
39963
|
if (state.reason === "missing") {
|
|
39931
39964
|
fail4(`${relRoot} missing`);
|
|
39932
39965
|
} else if (state.reason === "not-symlink") {
|
|
@@ -39941,13 +39974,13 @@ function checkSkillDrift() {
|
|
|
39941
39974
|
return drifted.length === 0 && missingInDefault.length === 0 && linksOk && rootLinksOk;
|
|
39942
39975
|
}
|
|
39943
39976
|
function checkManagedMirror(label, sourceDir, mirrorDir, fixHint) {
|
|
39944
|
-
if (!
|
|
39945
|
-
warn3(`${label} source missing: ${
|
|
39977
|
+
if (!existsSync29(sourceDir)) {
|
|
39978
|
+
warn3(`${label} source missing: ${relative4(CWD, sourceDir)}`);
|
|
39946
39979
|
fix(fixHint);
|
|
39947
39980
|
return false;
|
|
39948
39981
|
}
|
|
39949
|
-
if (!
|
|
39950
|
-
fail4(`${label} mirror missing: ${
|
|
39982
|
+
if (!existsSync29(mirrorDir)) {
|
|
39983
|
+
fail4(`${label} mirror missing: ${relative4(CWD, mirrorDir)}`);
|
|
39951
39984
|
fix(fixHint);
|
|
39952
39985
|
return false;
|
|
39953
39986
|
}
|
|
@@ -39978,13 +40011,13 @@ function checkManagedMirror(label, sourceDir, mirrorDir, fixHint) {
|
|
|
39978
40011
|
function checkManagedAssetMirrors() {
|
|
39979
40012
|
section3("Managed mirrors (specialists / mandatory-rules / nodes)");
|
|
39980
40013
|
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,
|
|
40014
|
+
const rulesOk = checkManagedMirror("mandatory-rules", CONFIG_MANDATORY_RULES_DIR, join32(DEFAULT_SPECIALISTS_DIR, "mandatory-rules"), "specialists init --sync-defaults");
|
|
40015
|
+
const nodesOk = checkManagedMirror("nodes", CONFIG_NODES_DIR, join32(DEFAULT_SPECIALISTS_DIR, "nodes"), "specialists init --sync-defaults");
|
|
39983
40016
|
return specialistsOk && rulesOk && nodesOk;
|
|
39984
40017
|
}
|
|
39985
40018
|
function checkUserOverlayDrift() {
|
|
39986
40019
|
section3("User specialist overlays");
|
|
39987
|
-
if (!
|
|
40020
|
+
if (!existsSync29(USER_SPECIALISTS_DIR)) {
|
|
39988
40021
|
ok3("no user overlays present");
|
|
39989
40022
|
return true;
|
|
39990
40023
|
}
|
|
@@ -39995,14 +40028,14 @@ function checkUserOverlayDrift() {
|
|
|
39995
40028
|
}
|
|
39996
40029
|
let allOk = true;
|
|
39997
40030
|
for (const name of overlays) {
|
|
39998
|
-
const userPath =
|
|
39999
|
-
const defaultPath =
|
|
40031
|
+
const userPath = join32(USER_SPECIALISTS_DIR, name);
|
|
40032
|
+
const defaultPath = join32(DEFAULT_SPECIALISTS_DIR, name);
|
|
40000
40033
|
const userSpec = loadJson2(userPath);
|
|
40001
40034
|
if (!userSpec) {
|
|
40002
40035
|
warn3(`${name}: failed to parse \u2014 skipping drift check`);
|
|
40003
40036
|
continue;
|
|
40004
40037
|
}
|
|
40005
|
-
if (!
|
|
40038
|
+
if (!existsSync29(defaultPath)) {
|
|
40006
40039
|
ok3(`${name}: user-only overlay (no default to drift from)`);
|
|
40007
40040
|
continue;
|
|
40008
40041
|
}
|
|
@@ -40030,20 +40063,20 @@ function checkUserOverlayDrift() {
|
|
|
40030
40063
|
}
|
|
40031
40064
|
function checkRuntimeDirs() {
|
|
40032
40065
|
section3(".specialists/ runtime directories");
|
|
40033
|
-
const rootDir =
|
|
40034
|
-
const jobsDir =
|
|
40035
|
-
const readyDir =
|
|
40066
|
+
const rootDir = join32(CWD, ".specialists");
|
|
40067
|
+
const jobsDir = join32(rootDir, "jobs");
|
|
40068
|
+
const readyDir = join32(rootDir, "ready");
|
|
40036
40069
|
let allOk = true;
|
|
40037
|
-
if (!
|
|
40070
|
+
if (!existsSync29(rootDir)) {
|
|
40038
40071
|
warn3(".specialists/ not found in current project");
|
|
40039
40072
|
fix("specialists init");
|
|
40040
40073
|
allOk = false;
|
|
40041
40074
|
} else {
|
|
40042
40075
|
ok3(".specialists/ present");
|
|
40043
40076
|
for (const [subDir, label] of [[jobsDir, "jobs"], [readyDir, "ready"]]) {
|
|
40044
|
-
if (!
|
|
40077
|
+
if (!existsSync29(subDir)) {
|
|
40045
40078
|
warn3(`.specialists/${label}/ missing \u2014 auto-creating`);
|
|
40046
|
-
|
|
40079
|
+
mkdirSync11(subDir, { recursive: true });
|
|
40047
40080
|
ok3(`.specialists/${label}/ created`);
|
|
40048
40081
|
} else {
|
|
40049
40082
|
ok3(`.specialists/${label}/ present`);
|
|
@@ -40055,8 +40088,8 @@ function checkRuntimeDirs() {
|
|
|
40055
40088
|
function checkClaudeMdFragments() {
|
|
40056
40089
|
section3("CLAUDE.md fragments");
|
|
40057
40090
|
const projectRoot = process.cwd();
|
|
40058
|
-
const claudeMd =
|
|
40059
|
-
if (!
|
|
40091
|
+
const claudeMd = join32(projectRoot, "CLAUDE.md");
|
|
40092
|
+
if (!existsSync29(claudeMd)) {
|
|
40060
40093
|
warn3("No CLAUDE.md in project root \u2014 skipping fragment check");
|
|
40061
40094
|
return true;
|
|
40062
40095
|
}
|
|
@@ -40125,7 +40158,7 @@ function parseDoctorArgs(argv) {
|
|
|
40125
40158
|
const value = argv[i + 1];
|
|
40126
40159
|
if (!value || value.startsWith("--"))
|
|
40127
40160
|
throw new Error("--root requires a value");
|
|
40128
|
-
opts.root =
|
|
40161
|
+
opts.root = resolve13(value);
|
|
40129
40162
|
i += 1;
|
|
40130
40163
|
continue;
|
|
40131
40164
|
}
|
|
@@ -40182,7 +40215,7 @@ function compareVersions2(left, right) {
|
|
|
40182
40215
|
}
|
|
40183
40216
|
function setStatusError(statusPath) {
|
|
40184
40217
|
try {
|
|
40185
|
-
const raw =
|
|
40218
|
+
const raw = readFileSync30(statusPath, "utf8");
|
|
40186
40219
|
const status = JSON.parse(raw);
|
|
40187
40220
|
status.status = "error";
|
|
40188
40221
|
writeFileSync12(statusPath, `${JSON.stringify(status, null, 2)}
|
|
@@ -40236,11 +40269,11 @@ function cleanupProcesses(jobsDir, dryRun) {
|
|
|
40236
40269
|
zombieJobIds: []
|
|
40237
40270
|
};
|
|
40238
40271
|
for (const jobId of entries) {
|
|
40239
|
-
const statusPath =
|
|
40240
|
-
if (!
|
|
40272
|
+
const statusPath = join32(jobsDir, jobId, "status.json");
|
|
40273
|
+
if (!existsSync29(statusPath))
|
|
40241
40274
|
continue;
|
|
40242
40275
|
try {
|
|
40243
|
-
const status = JSON.parse(
|
|
40276
|
+
const status = JSON.parse(readFileSync30(statusPath, "utf8"));
|
|
40244
40277
|
result.total += 1;
|
|
40245
40278
|
if (status.status !== "running" && status.status !== "starting")
|
|
40246
40279
|
continue;
|
|
@@ -40325,8 +40358,8 @@ function resolveWatchdogMode() {
|
|
|
40325
40358
|
function checkZombieJobs() {
|
|
40326
40359
|
section3("Background jobs");
|
|
40327
40360
|
hint(`watchdog mode: ${resolveWatchdogMode()}`);
|
|
40328
|
-
const jobsDir =
|
|
40329
|
-
if (!
|
|
40361
|
+
const jobsDir = join32(CWD, ".specialists", "jobs");
|
|
40362
|
+
if (!existsSync29(jobsDir)) {
|
|
40330
40363
|
hint("No .specialists/jobs/ \u2014 skipping");
|
|
40331
40364
|
return true;
|
|
40332
40365
|
}
|
|
@@ -40344,7 +40377,7 @@ function checkZombieJobs() {
|
|
|
40344
40377
|
}
|
|
40345
40378
|
return result.zombies === 0;
|
|
40346
40379
|
}
|
|
40347
|
-
async function
|
|
40380
|
+
async function run28(argv = process.argv.slice(3)) {
|
|
40348
40381
|
const subcommand = argv[0];
|
|
40349
40382
|
if (subcommand === "orphans") {
|
|
40350
40383
|
runDoctorOrphans();
|
|
@@ -40391,24 +40424,24 @@ var init_doctor = __esm(() => {
|
|
|
40391
40424
|
init_drift_detector();
|
|
40392
40425
|
init_version_check();
|
|
40393
40426
|
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 =
|
|
40427
|
+
CLAUDE_DIR = join32(CWD, ".claude");
|
|
40428
|
+
PI_DIR = join32(CWD, ".pi");
|
|
40429
|
+
XTRM_SKILLS_DIR = join32(CWD, ".xtrm", "skills");
|
|
40430
|
+
XTRM_DEFAULT_SKILLS_DIR = join32(XTRM_SKILLS_DIR, "default");
|
|
40431
|
+
XTRM_ACTIVE_SKILLS_DIR = join32(XTRM_SKILLS_DIR, "active");
|
|
40432
|
+
ACTIVE_CLAUDE_SKILLS_DIR = join32(XTRM_ACTIVE_SKILLS_DIR, "claude");
|
|
40433
|
+
ACTIVE_PI_SKILLS_DIR = join32(XTRM_ACTIVE_SKILLS_DIR, "pi");
|
|
40434
|
+
CONFIG_SKILLS_DIR = join32(CWD, "config", "skills");
|
|
40435
|
+
CONFIG_SPECIALISTS_DIR = join32(CWD, "config", "specialists");
|
|
40436
|
+
CONFIG_MANDATORY_RULES_DIR = join32(CWD, "config", "mandatory-rules");
|
|
40437
|
+
CONFIG_NODES_DIR = join32(CWD, "config", "nodes");
|
|
40438
|
+
SPECIALISTS_DIR = join32(CWD, ".specialists");
|
|
40439
|
+
DEFAULT_SPECIALISTS_DIR = join32(SPECIALISTS_DIR, "default");
|
|
40440
|
+
USER_SPECIALISTS_DIR = join32(SPECIALISTS_DIR, "user");
|
|
40441
|
+
HOOKS_DIR = join32(CWD, ".xtrm", "hooks", "specialists");
|
|
40442
|
+
CLAUDE_HOOKS_DIR = join32(CLAUDE_DIR, "hooks");
|
|
40443
|
+
SETTINGS_FILE = join32(CLAUDE_DIR, "settings.json");
|
|
40444
|
+
MCP_FILE2 = join32(CWD, ".mcp.json");
|
|
40412
40445
|
HOOK_NAMES = [
|
|
40413
40446
|
"specialists-complete.mjs",
|
|
40414
40447
|
"specialists-session-start.mjs"
|
|
@@ -40418,9 +40451,9 @@ var init_doctor = __esm(() => {
|
|
|
40418
40451
|
// src/cli/setup.ts
|
|
40419
40452
|
var exports_setup = {};
|
|
40420
40453
|
__export(exports_setup, {
|
|
40421
|
-
run: () =>
|
|
40454
|
+
run: () => run29
|
|
40422
40455
|
});
|
|
40423
|
-
async function
|
|
40456
|
+
async function run29() {
|
|
40424
40457
|
console.log("");
|
|
40425
40458
|
console.log(yellow13("\u26A0 DEPRECATED: `specialists setup` is deprecated"));
|
|
40426
40459
|
console.log("");
|
|
@@ -40441,20 +40474,20 @@ async function run30() {
|
|
|
40441
40474
|
var bold14 = (s) => `\x1B[1m${s}\x1B[0m`, yellow13 = (s) => `\x1B[33m${s}\x1B[0m`, dim14 = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
40442
40475
|
|
|
40443
40476
|
// src/cli/serve-hot-reload.ts
|
|
40444
|
-
import { existsSync as
|
|
40445
|
-
import { join as
|
|
40477
|
+
import { existsSync as existsSync30, readdirSync as readdirSync15, statSync as statSync5, watch as fsWatch } from "fs";
|
|
40478
|
+
import { join as join33 } from "path";
|
|
40446
40479
|
function specialistNameFromFile(file) {
|
|
40447
40480
|
const match = file.match(/^(.+)\.specialist\.(json|yaml)$/);
|
|
40448
40481
|
return match ? match[1] : null;
|
|
40449
40482
|
}
|
|
40450
40483
|
function snapshotMtimes(dir) {
|
|
40451
40484
|
const out = new Map;
|
|
40452
|
-
if (!
|
|
40485
|
+
if (!existsSync30(dir))
|
|
40453
40486
|
return out;
|
|
40454
40487
|
const entries = readdirSync15(dir).filter((name) => specialistNameFromFile(name) !== null);
|
|
40455
40488
|
for (const name of entries) {
|
|
40456
40489
|
try {
|
|
40457
|
-
out.set(name, statSync5(
|
|
40490
|
+
out.set(name, statSync5(join33(dir, name)).mtimeMs);
|
|
40458
40491
|
} catch {}
|
|
40459
40492
|
}
|
|
40460
40493
|
return out;
|
|
@@ -40511,7 +40544,7 @@ function createUserDirWatcher(opts) {
|
|
|
40511
40544
|
for (const file of changed)
|
|
40512
40545
|
queue(file);
|
|
40513
40546
|
}, opts.pollMs);
|
|
40514
|
-
} else if (
|
|
40547
|
+
} else if (existsSync30(opts.userDir)) {
|
|
40515
40548
|
try {
|
|
40516
40549
|
watcher = fsWatch(opts.userDir, { persistent: false }, (_eventType, filename) => {
|
|
40517
40550
|
queue(filename ? String(filename) : null);
|
|
@@ -40543,17 +40576,20 @@ var init_serve_hot_reload = () => {};
|
|
|
40543
40576
|
var exports_serve = {};
|
|
40544
40577
|
__export(exports_serve, {
|
|
40545
40578
|
startServe: () => startServe,
|
|
40546
|
-
run: () =>
|
|
40579
|
+
run: () => run30,
|
|
40547
40580
|
recordAuditFailure: () => recordAuditFailure,
|
|
40548
40581
|
evaluateReadiness: () => evaluateReadiness2,
|
|
40549
|
-
createReadinessState: () => createReadinessState
|
|
40582
|
+
createReadinessState: () => createReadinessState,
|
|
40583
|
+
checkPiHelpForFlags: () => checkPiHelpForFlags
|
|
40550
40584
|
});
|
|
40551
40585
|
import { createServer } from "http";
|
|
40586
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
40552
40587
|
import { once } from "events";
|
|
40588
|
+
import { spawnSync as spawnSync23 } from "child_process";
|
|
40553
40589
|
import { access, readdir as readdir2, readFile as readFile5, constants } from "fs/promises";
|
|
40554
|
-
import { existsSync as
|
|
40590
|
+
import { existsSync as existsSync31 } from "fs";
|
|
40555
40591
|
import { homedir as homedir3 } from "os";
|
|
40556
|
-
import { join as
|
|
40592
|
+
import { join as join34 } from "path";
|
|
40557
40593
|
function createReadinessState() {
|
|
40558
40594
|
return { shuttingDown: false, auditFailures: [], dbWriteFailuresTotal: 0 };
|
|
40559
40595
|
}
|
|
@@ -40569,7 +40605,7 @@ function pruneAuditFailures(state, now = Date.now()) {
|
|
|
40569
40605
|
}
|
|
40570
40606
|
}
|
|
40571
40607
|
async function checkUserDirSpecs(userDir) {
|
|
40572
|
-
if (!
|
|
40608
|
+
if (!existsSync31(userDir))
|
|
40573
40609
|
return "empty";
|
|
40574
40610
|
const entries = await readdir2(userDir).catch(() => []);
|
|
40575
40611
|
const specFiles = entries.filter((name) => name.endsWith(".specialist.json") || name.endsWith(".specialist.yaml"));
|
|
@@ -40578,7 +40614,7 @@ async function checkUserDirSpecs(userDir) {
|
|
|
40578
40614
|
let validCount = 0;
|
|
40579
40615
|
for (const file of specFiles) {
|
|
40580
40616
|
try {
|
|
40581
|
-
const content = await readFile5(
|
|
40617
|
+
const content = await readFile5(join34(userDir, file), "utf-8");
|
|
40582
40618
|
const json = file.endsWith(".json") ? content : null;
|
|
40583
40619
|
if (!json)
|
|
40584
40620
|
continue;
|
|
@@ -40596,7 +40632,7 @@ async function evaluateReadiness2(opts) {
|
|
|
40596
40632
|
if (opts.state.auditFailures.length > opts.auditFailureThreshold) {
|
|
40597
40633
|
return { ready: false, reason: "degraded:audit" };
|
|
40598
40634
|
}
|
|
40599
|
-
const piConfigPath = opts.piConfigPath ??
|
|
40635
|
+
const piConfigPath = opts.piConfigPath ?? join34(homedir3(), ".pi", "agent", "auth.json");
|
|
40600
40636
|
try {
|
|
40601
40637
|
await access(piConfigPath, constants.R_OK);
|
|
40602
40638
|
} catch {
|
|
@@ -40607,26 +40643,41 @@ async function evaluateReadiness2(opts) {
|
|
|
40607
40643
|
} catch {
|
|
40608
40644
|
return { ready: false, reason: "db_not_writable" };
|
|
40609
40645
|
}
|
|
40610
|
-
|
|
40646
|
+
let warning;
|
|
40647
|
+
const canaryMode = opts.piCanaryMode ?? "off";
|
|
40648
|
+
if (canaryMode !== "off" && opts.piCanaryCheck) {
|
|
40649
|
+
const canaryFailure = await opts.piCanaryCheck();
|
|
40650
|
+
if (canaryFailure) {
|
|
40651
|
+
if (canaryMode === "require")
|
|
40652
|
+
return { ready: false, reason: canaryFailure };
|
|
40653
|
+
warning = canaryFailure;
|
|
40654
|
+
}
|
|
40655
|
+
}
|
|
40656
|
+
const userDir = join34(opts.projectDir, ".specialists", "user");
|
|
40611
40657
|
const userDirResult = await checkUserDirSpecs(userDir);
|
|
40612
40658
|
if (userDirResult === "empty")
|
|
40613
40659
|
return { ready: false, reason: "empty_user_dir" };
|
|
40614
40660
|
if (userDirResult === "invalid")
|
|
40615
40661
|
return { ready: false, reason: "invalid_spec_in_user_dir" };
|
|
40616
|
-
return { ready: true };
|
|
40662
|
+
return warning ? { ready: true, warning } : { ready: true };
|
|
40617
40663
|
}
|
|
40618
|
-
function
|
|
40664
|
+
function parseArgs12(argv) {
|
|
40619
40665
|
let port = 8000;
|
|
40620
40666
|
let concurrency = 4;
|
|
40621
40667
|
let queueTimeoutMs = 5000;
|
|
40622
40668
|
let shutdownGraceMs = 30000;
|
|
40623
40669
|
let projectDir = process.cwd();
|
|
40670
|
+
let dbPath;
|
|
40624
40671
|
let fallbackModel;
|
|
40625
40672
|
let auditFailureThreshold = 5;
|
|
40626
40673
|
let allowSkills = false;
|
|
40627
40674
|
let allowSkillsRoots = [];
|
|
40628
|
-
let allowLocalScripts = false;
|
|
40629
40675
|
let reloadPollMs = 0;
|
|
40676
|
+
let readinessCanaryMode = "off";
|
|
40677
|
+
const readinessRequiredPiFlags = [];
|
|
40678
|
+
let readinessCanarySpecialist;
|
|
40679
|
+
let readinessCanaryTimeoutMs = 5000;
|
|
40680
|
+
let logLevel = "info";
|
|
40630
40681
|
for (let i = 0;i < argv.length; i++) {
|
|
40631
40682
|
const token = argv[i];
|
|
40632
40683
|
if (token === "--port" && argv[i + 1])
|
|
@@ -40639,6 +40690,8 @@ function parseArgs13(argv) {
|
|
|
40639
40690
|
shutdownGraceMs = Number(argv[++i]);
|
|
40640
40691
|
else if ((token === "--project-dir" || token === "--user-dir") && argv[i + 1])
|
|
40641
40692
|
projectDir = argv[++i];
|
|
40693
|
+
else if (token === "--db-path" && argv[i + 1])
|
|
40694
|
+
dbPath = argv[++i];
|
|
40642
40695
|
else if (token === "--fallback-model" && argv[i + 1])
|
|
40643
40696
|
fallbackModel = argv[++i];
|
|
40644
40697
|
else if (token === "--audit-failure-threshold" && argv[i + 1])
|
|
@@ -40648,16 +40701,51 @@ function parseArgs13(argv) {
|
|
|
40648
40701
|
else if (token === "--allow-skills-roots" && argv[i + 1])
|
|
40649
40702
|
allowSkillsRoots = argv[++i].split(":").filter(Boolean);
|
|
40650
40703
|
else if (token === "--allow-local-scripts")
|
|
40651
|
-
|
|
40704
|
+
throw new Error("--allow-local-scripts is not supported for script-class specialists");
|
|
40652
40705
|
else if (token === "--reload-poll-ms" && argv[i + 1])
|
|
40653
40706
|
reloadPollMs = Number(argv[++i]);
|
|
40707
|
+
else if (token === "--readiness-canary" && argv[i + 1]) {
|
|
40708
|
+
const mode = argv[++i];
|
|
40709
|
+
if (mode === "off" || mode === "warn" || mode === "require")
|
|
40710
|
+
readinessCanaryMode = mode;
|
|
40711
|
+
} else if (token === "--readiness-required-pi-flag" && argv[i + 1])
|
|
40712
|
+
readinessRequiredPiFlags.push(argv[++i]);
|
|
40713
|
+
else if (token === "--readiness-canary-specialist" && argv[i + 1])
|
|
40714
|
+
readinessCanarySpecialist = argv[++i];
|
|
40715
|
+
else if (token === "--readiness-canary-timeout-ms" && argv[i + 1])
|
|
40716
|
+
readinessCanaryTimeoutMs = Number(argv[++i]);
|
|
40717
|
+
else if (token === "--log-level" && argv[i + 1]) {
|
|
40718
|
+
const value = argv[++i];
|
|
40719
|
+
if (value === "off" || value === "info" || value === "debug")
|
|
40720
|
+
logLevel = value;
|
|
40721
|
+
else
|
|
40722
|
+
throw new Error("--log-level must be one of: off, info, debug");
|
|
40723
|
+
}
|
|
40654
40724
|
}
|
|
40655
|
-
return { port, concurrency, queueTimeoutMs, shutdownGraceMs, projectDir, fallbackModel, auditFailureThreshold, allowSkills, allowSkillsRoots,
|
|
40725
|
+
return { port, concurrency, queueTimeoutMs, shutdownGraceMs, projectDir, dbPath, fallbackModel, auditFailureThreshold, allowSkills, allowSkillsRoots, reloadPollMs, readinessCanaryMode, readinessRequiredPiFlags, readinessCanarySpecialist, readinessCanaryTimeoutMs, logLevel };
|
|
40726
|
+
}
|
|
40727
|
+
function checkPiHelpForFlags(flags = DEFAULT_REQUIRED_PI_FLAGS) {
|
|
40728
|
+
const result = spawnSync23("pi", ["--help"], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
|
|
40729
|
+
if (result.error || result.status === 127)
|
|
40730
|
+
return "pi_binary_missing";
|
|
40731
|
+
const help = `${result.stdout ?? ""}
|
|
40732
|
+
${result.stderr ?? ""}`;
|
|
40733
|
+
const missing = flags.find((flag2) => !help.includes(flag2));
|
|
40734
|
+
return missing ? "pi_flag_missing" : undefined;
|
|
40656
40735
|
}
|
|
40657
40736
|
function sendJson(res, statusCode, body) {
|
|
40658
40737
|
res.writeHead(statusCode, { "content-type": "application/json" });
|
|
40659
40738
|
res.end(JSON.stringify(body));
|
|
40660
40739
|
}
|
|
40740
|
+
function emitGenerateLog(logLevel, entry) {
|
|
40741
|
+
if (logLevel === "off")
|
|
40742
|
+
return;
|
|
40743
|
+
console.log(JSON.stringify({ level: logLevel, ts: new Date().toISOString(), ...entry }));
|
|
40744
|
+
}
|
|
40745
|
+
function shortLogError(value, limit = 240) {
|
|
40746
|
+
const message = value instanceof Error ? value.message : String(value);
|
|
40747
|
+
return message.length <= limit ? message : `${message.slice(0, limit)}\u2026`;
|
|
40748
|
+
}
|
|
40661
40749
|
async function readBody(req) {
|
|
40662
40750
|
const chunks = [];
|
|
40663
40751
|
for await (const chunk of req)
|
|
@@ -40672,21 +40760,38 @@ async function waitForSlot(limit, timeoutMs, getActive) {
|
|
|
40672
40760
|
while (getActive() >= limit) {
|
|
40673
40761
|
if (Date.now() - startedAt >= timeoutMs)
|
|
40674
40762
|
return false;
|
|
40675
|
-
await new Promise((
|
|
40763
|
+
await new Promise((resolve14) => setTimeout(resolve14, 25));
|
|
40676
40764
|
}
|
|
40677
40765
|
return true;
|
|
40678
40766
|
}
|
|
40679
40767
|
async function startServe(argv = process.argv.slice(3)) {
|
|
40680
|
-
const args =
|
|
40768
|
+
const args = parseArgs12(argv);
|
|
40681
40769
|
const loader = new SpecialistLoader({ projectDir: args.projectDir });
|
|
40682
40770
|
const dbLocation = resolveObservabilityDbLocation(args.projectDir);
|
|
40683
|
-
|
|
40684
|
-
const db =
|
|
40771
|
+
const dbPath = args.dbPath ?? dbLocation.dbPath;
|
|
40772
|
+
const db = args.dbPath ? createObservabilitySqliteClientAtPath(args.dbPath) : (() => {
|
|
40773
|
+
ensureObservabilityDbFile(dbLocation);
|
|
40774
|
+
return createObservabilitySqliteClient(args.projectDir);
|
|
40775
|
+
})();
|
|
40685
40776
|
const readinessState = createReadinessState();
|
|
40686
|
-
const userDir =
|
|
40777
|
+
const userDir = join34(args.projectDir, ".specialists", "user");
|
|
40687
40778
|
const hotReload = createUserDirWatcher({ loader, userDir, pollMs: args.reloadPollMs });
|
|
40688
40779
|
let active = 0;
|
|
40689
40780
|
const children = new Set;
|
|
40781
|
+
const piCanaryCheck = async () => {
|
|
40782
|
+
const requiredFlags = args.readinessRequiredPiFlags.length > 0 ? args.readinessRequiredPiFlags : DEFAULT_REQUIRED_PI_FLAGS;
|
|
40783
|
+
const compatibilityFailure = checkPiHelpForFlags(requiredFlags);
|
|
40784
|
+
if (compatibilityFailure)
|
|
40785
|
+
return compatibilityFailure;
|
|
40786
|
+
if (!args.readinessCanarySpecialist)
|
|
40787
|
+
return;
|
|
40788
|
+
const result = await runScriptSpecialist({ specialist: args.readinessCanarySpecialist, trace: false, timeout_ms: args.readinessCanaryTimeoutMs }, {
|
|
40789
|
+
loader,
|
|
40790
|
+
fallbackModel: args.fallbackModel,
|
|
40791
|
+
observabilityDbPath: args.projectDir
|
|
40792
|
+
});
|
|
40793
|
+
return result.success ? undefined : "pi_smoke_failed";
|
|
40794
|
+
};
|
|
40690
40795
|
const server = createServer(async (req, res) => {
|
|
40691
40796
|
if (req.url === "/healthz")
|
|
40692
40797
|
return sendJson(res, 200, { ok: true });
|
|
@@ -40694,11 +40799,13 @@ async function startServe(argv = process.argv.slice(3)) {
|
|
|
40694
40799
|
const result = await evaluateReadiness2({
|
|
40695
40800
|
state: readinessState,
|
|
40696
40801
|
projectDir: args.projectDir,
|
|
40697
|
-
dbPath
|
|
40698
|
-
auditFailureThreshold: args.auditFailureThreshold
|
|
40802
|
+
dbPath,
|
|
40803
|
+
auditFailureThreshold: args.auditFailureThreshold,
|
|
40804
|
+
piCanaryMode: args.readinessCanaryMode,
|
|
40805
|
+
piCanaryCheck
|
|
40699
40806
|
});
|
|
40700
40807
|
if (result.ready) {
|
|
40701
|
-
return sendJson(res, 200, { ready: true, db_write_failures_total: readinessState.dbWriteFailuresTotal });
|
|
40808
|
+
return sendJson(res, 200, { ready: true, ...result.warning ? { warning: result.warning } : {}, db_write_failures_total: readinessState.dbWriteFailuresTotal });
|
|
40702
40809
|
}
|
|
40703
40810
|
return sendJson(res, 503, {
|
|
40704
40811
|
ready: false,
|
|
@@ -40708,27 +40815,65 @@ async function startServe(argv = process.argv.slice(3)) {
|
|
|
40708
40815
|
}
|
|
40709
40816
|
if (req.method !== "POST" || req.url !== "/v1/generate")
|
|
40710
40817
|
return sendJson(res, 404, { success: false, error: "not_found", error_type: "internal" });
|
|
40711
|
-
|
|
40818
|
+
const requestStartedAt = Date.now();
|
|
40819
|
+
const method = req.method ?? "POST";
|
|
40820
|
+
const path = req.url ?? "/v1/generate";
|
|
40821
|
+
const requestTraceId = randomUUID3();
|
|
40822
|
+
if (readinessState.shuttingDown) {
|
|
40823
|
+
emitGenerateLog(args.logLevel, {
|
|
40824
|
+
trace_id: requestTraceId,
|
|
40825
|
+
specialist: "unknown",
|
|
40826
|
+
status: "internal",
|
|
40827
|
+
duration_ms: Date.now() - requestStartedAt,
|
|
40828
|
+
prompt_bytes: 0,
|
|
40829
|
+
method,
|
|
40830
|
+
path,
|
|
40831
|
+
error: "shutting_down"
|
|
40832
|
+
});
|
|
40712
40833
|
return sendJson(res, 503, { success: false, error: "shutting_down", error_type: "internal" });
|
|
40834
|
+
}
|
|
40713
40835
|
const entered = await waitForSlot(args.concurrency, args.queueTimeoutMs, () => active);
|
|
40714
|
-
if (!entered)
|
|
40836
|
+
if (!entered) {
|
|
40837
|
+
emitGenerateLog(args.logLevel, {
|
|
40838
|
+
trace_id: requestTraceId,
|
|
40839
|
+
specialist: "unknown",
|
|
40840
|
+
status: "quota",
|
|
40841
|
+
duration_ms: Date.now() - requestStartedAt,
|
|
40842
|
+
prompt_bytes: 0,
|
|
40843
|
+
method,
|
|
40844
|
+
path,
|
|
40845
|
+
error: "too_many_requests"
|
|
40846
|
+
});
|
|
40715
40847
|
return sendJson(res, 429, { success: false, error: "too_many_requests", error_type: "quota" });
|
|
40848
|
+
}
|
|
40716
40849
|
active++;
|
|
40717
40850
|
const work = (async () => {
|
|
40851
|
+
let promptBytes = 0;
|
|
40852
|
+
let requestedSpecialist = "unknown";
|
|
40718
40853
|
try {
|
|
40719
40854
|
const raw = await readBody(req);
|
|
40855
|
+
promptBytes = Buffer.byteLength(raw, "utf8");
|
|
40720
40856
|
let parsed;
|
|
40721
40857
|
try {
|
|
40722
40858
|
parsed = JSON.parse(raw);
|
|
40723
40859
|
} catch {
|
|
40860
|
+
const duration_ms2 = Date.now() - requestStartedAt;
|
|
40861
|
+
const trace_id = requestTraceId;
|
|
40862
|
+
emitGenerateLog(args.logLevel, { trace_id, specialist: "unknown", status: "invalid_json", duration_ms: duration_ms2, prompt_bytes: promptBytes, method, path, error: "malformed_request" });
|
|
40724
40863
|
return sendJson(res, 400, { success: false, error: "malformed_request", error_type: "invalid_json" });
|
|
40725
40864
|
}
|
|
40726
|
-
if (!isValidRequest(parsed))
|
|
40865
|
+
if (!isValidRequest(parsed)) {
|
|
40866
|
+
const duration_ms2 = Date.now() - requestStartedAt;
|
|
40867
|
+
const trace_id = requestTraceId;
|
|
40868
|
+
emitGenerateLog(args.logLevel, { trace_id, specialist: "unknown", status: "invalid_json", duration_ms: duration_ms2, prompt_bytes: promptBytes, method, path, error: "malformed_request" });
|
|
40727
40869
|
return sendJson(res, 400, { success: false, error: "malformed_request", error_type: "invalid_json" });
|
|
40870
|
+
}
|
|
40871
|
+
requestedSpecialist = parsed.specialist;
|
|
40728
40872
|
const result = await runScriptSpecialist(parsed, {
|
|
40729
40873
|
loader,
|
|
40874
|
+
projectDir: args.projectDir,
|
|
40730
40875
|
fallbackModel: args.fallbackModel,
|
|
40731
|
-
observabilityDbPath: args.
|
|
40876
|
+
...args.dbPath ? { observabilityDbPath: args.dbPath } : {},
|
|
40732
40877
|
onChild: (child) => {
|
|
40733
40878
|
children.add(child);
|
|
40734
40879
|
child.once("exit", () => children.delete(child));
|
|
@@ -40736,11 +40881,37 @@ async function startServe(argv = process.argv.slice(3)) {
|
|
|
40736
40881
|
onAuditFailure: () => recordAuditFailure(readinessState),
|
|
40737
40882
|
trust: {
|
|
40738
40883
|
allowSkills: args.allowSkills,
|
|
40739
|
-
allowSkillsRoots: args.allowSkillsRoots
|
|
40740
|
-
allowLocalScripts: args.allowLocalScripts
|
|
40884
|
+
allowSkillsRoots: args.allowSkillsRoots
|
|
40741
40885
|
}
|
|
40742
40886
|
});
|
|
40887
|
+
const duration_ms = Date.now() - requestStartedAt;
|
|
40888
|
+
const meta = result.meta ?? {};
|
|
40889
|
+
emitGenerateLog(args.logLevel, {
|
|
40890
|
+
trace_id: meta.trace_id ?? requestTraceId,
|
|
40891
|
+
specialist: meta.specialist ?? (typeof parsed === "object" && parsed !== null ? String(parsed.specialist ?? "unknown") : "unknown"),
|
|
40892
|
+
resolved_specialist: meta.resolved_specialist,
|
|
40893
|
+
model: meta.model,
|
|
40894
|
+
status: result.success ? "success" : result.error_type,
|
|
40895
|
+
duration_ms: meta.duration_ms ?? duration_ms,
|
|
40896
|
+
prompt_bytes: promptBytes,
|
|
40897
|
+
method,
|
|
40898
|
+
path,
|
|
40899
|
+
...result.success ? {} : { error: shortLogError(result.error) }
|
|
40900
|
+
});
|
|
40743
40901
|
return sendJson(res, 200, result);
|
|
40902
|
+
} catch (error2) {
|
|
40903
|
+
emitGenerateLog(args.logLevel, {
|
|
40904
|
+
trace_id: requestTraceId,
|
|
40905
|
+
specialist: requestedSpecialist,
|
|
40906
|
+
status: "internal",
|
|
40907
|
+
duration_ms: Date.now() - requestStartedAt,
|
|
40908
|
+
prompt_bytes: promptBytes,
|
|
40909
|
+
method,
|
|
40910
|
+
path,
|
|
40911
|
+
error: shortLogError(error2)
|
|
40912
|
+
});
|
|
40913
|
+
if (!res.headersSent)
|
|
40914
|
+
return sendJson(res, 500, { success: false, error: "internal_error", error_type: "internal" });
|
|
40744
40915
|
} finally {
|
|
40745
40916
|
active--;
|
|
40746
40917
|
}
|
|
@@ -40768,10 +40939,10 @@ async function startServe(argv = process.argv.slice(3)) {
|
|
|
40768
40939
|
console.log(`sp serve listening on ${args.port}`);
|
|
40769
40940
|
return { server, args, db, readinessState };
|
|
40770
40941
|
}
|
|
40771
|
-
async function
|
|
40942
|
+
async function run30(argv = process.argv.slice(3)) {
|
|
40772
40943
|
await startServe(argv);
|
|
40773
40944
|
}
|
|
40774
|
-
var AUDIT_WINDOW_MS = 60000;
|
|
40945
|
+
var AUDIT_WINDOW_MS = 60000, DEFAULT_REQUIRED_PI_FLAGS;
|
|
40775
40946
|
var init_serve = __esm(() => {
|
|
40776
40947
|
init_loader();
|
|
40777
40948
|
init_script_runner();
|
|
@@ -40779,24 +40950,25 @@ var init_serve = __esm(() => {
|
|
|
40779
40950
|
init_observability_db();
|
|
40780
40951
|
init_schema();
|
|
40781
40952
|
init_serve_hot_reload();
|
|
40953
|
+
DEFAULT_REQUIRED_PI_FLAGS = ["--mode", "--no-session", "--no-extensions", "--no-tools", "--no-context-files", "--no-skills", "--no-prompt-templates", "--no-themes"];
|
|
40782
40954
|
});
|
|
40783
40955
|
|
|
40784
40956
|
// src/cli/script.ts
|
|
40785
40957
|
var exports_script = {};
|
|
40786
40958
|
__export(exports_script, {
|
|
40787
40959
|
scriptCli: () => scriptCli,
|
|
40788
|
-
run: () =>
|
|
40789
|
-
parseArgs: () =>
|
|
40960
|
+
run: () => run31,
|
|
40961
|
+
parseArgs: () => parseArgs13,
|
|
40790
40962
|
mapExitCode: () => mapExitCode
|
|
40791
40963
|
});
|
|
40792
|
-
import { spawnSync as
|
|
40964
|
+
import { spawnSync as spawnSync24 } from "child_process";
|
|
40793
40965
|
function parseVar(entry) {
|
|
40794
40966
|
const index = entry.indexOf("=");
|
|
40795
40967
|
if (index <= 0)
|
|
40796
40968
|
throw new Error(`Invalid --vars entry: ${entry}`);
|
|
40797
40969
|
return [entry.slice(0, index), entry.slice(index + 1)];
|
|
40798
40970
|
}
|
|
40799
|
-
function
|
|
40971
|
+
function parseArgs13(argv) {
|
|
40800
40972
|
if (argv.length === 0)
|
|
40801
40973
|
throw new Error("Missing specialist name");
|
|
40802
40974
|
const specialist = argv[0];
|
|
@@ -40892,7 +41064,7 @@ function printResult(result, json) {
|
|
|
40892
41064
|
console.error(result.error);
|
|
40893
41065
|
}
|
|
40894
41066
|
function runUnderLock(lockPath, argv) {
|
|
40895
|
-
const flock =
|
|
41067
|
+
const flock = spawnSync24("flock", ["-n", lockPath, "env", "SP_SCRIPT_NO_LOCK=1", process.execPath, process.argv[1], "script", ...argv], {
|
|
40896
41068
|
encoding: "utf-8",
|
|
40897
41069
|
stdio: "inherit"
|
|
40898
41070
|
});
|
|
@@ -40902,13 +41074,17 @@ function runUnderLock(lockPath, argv) {
|
|
|
40902
41074
|
return 75;
|
|
40903
41075
|
return flock.status ?? 1;
|
|
40904
41076
|
}
|
|
40905
|
-
async function
|
|
40906
|
-
const args =
|
|
41077
|
+
async function run31(argv = process.argv.slice(3)) {
|
|
41078
|
+
const args = parseArgs13(argv);
|
|
40907
41079
|
if (args.singleInstance && !process.env.SP_SCRIPT_NO_LOCK) {
|
|
40908
41080
|
process.exit(runUnderLock(args.singleInstance, argv));
|
|
40909
41081
|
}
|
|
40910
41082
|
const loader = new SpecialistLoader({ projectDir: args.projectDir });
|
|
40911
|
-
const result = await runScriptSpecialist(buildRequest(args), {
|
|
41083
|
+
const result = await runScriptSpecialist(buildRequest(args), {
|
|
41084
|
+
loader,
|
|
41085
|
+
projectDir: args.projectDir,
|
|
41086
|
+
...args.dbPath ? { observabilityDbPath: args.dbPath } : {}
|
|
41087
|
+
});
|
|
40912
41088
|
printResult(result, args.json);
|
|
40913
41089
|
process.exit(mapExitCode(result));
|
|
40914
41090
|
}
|
|
@@ -40916,19 +41092,19 @@ var scriptCli;
|
|
|
40916
41092
|
var init_script = __esm(() => {
|
|
40917
41093
|
init_loader();
|
|
40918
41094
|
init_script_runner();
|
|
40919
|
-
scriptCli = { parseArgs:
|
|
41095
|
+
scriptCli = { parseArgs: parseArgs13, mapExitCode };
|
|
40920
41096
|
});
|
|
40921
41097
|
|
|
40922
41098
|
// src/cli/help.ts
|
|
40923
41099
|
var exports_help = {};
|
|
40924
41100
|
__export(exports_help, {
|
|
40925
|
-
run: () =>
|
|
41101
|
+
run: () => run32
|
|
40926
41102
|
});
|
|
40927
41103
|
function formatCommands(entries) {
|
|
40928
41104
|
const width = Math.max(...entries.map(([cmd3]) => cmd3.length));
|
|
40929
41105
|
return entries.map(([cmd3, desc]) => ` ${cmd3.padEnd(width)} ${desc}`);
|
|
40930
41106
|
}
|
|
40931
|
-
async function
|
|
41107
|
+
async function run32() {
|
|
40932
41108
|
const lines = [
|
|
40933
41109
|
"",
|
|
40934
41110
|
"Specialists lets you run project-scoped specialist agents with a bead-first workflow.",
|
|
@@ -40964,7 +41140,7 @@ async function run33() {
|
|
|
40964
41140
|
" Async patterns",
|
|
40965
41141
|
" MCP: use_specialist (foreground, returns result directly)",
|
|
40966
41142
|
' CLI: specialists run <name> --prompt "..." # job ID prints on stderr',
|
|
40967
|
-
" specialists feed|
|
|
41143
|
+
" specialists ps|feed|result <job-id> # observe/progress/final output",
|
|
40968
41144
|
' Shell: specialists run <name> --prompt "..." & # native shell backgrounding',
|
|
40969
41145
|
"",
|
|
40970
41146
|
" Background workflow",
|
|
@@ -41014,7 +41190,6 @@ async function run33() {
|
|
|
41014
41190
|
" specialists view --help View specialist configs",
|
|
41015
41191
|
" specialists edit --help Edit specialist fields (dot-path, presets)",
|
|
41016
41192
|
" specialists run --help Run command details and flags",
|
|
41017
|
-
" specialists poll --help [DEPRECATED] use sp ps + sp feed",
|
|
41018
41193
|
" specialists steer --help Mid-run steering details",
|
|
41019
41194
|
" specialists resume --help Multi-turn keep-alive details",
|
|
41020
41195
|
" specialists init --help Bootstrap behavior and workflow injection",
|
|
@@ -41042,7 +41217,6 @@ var init_help = __esm(() => {
|
|
|
41042
41217
|
["node", "Run and inspect NodeSupervisor nodes (run/status)"],
|
|
41043
41218
|
["epic", "Epic lifecycle management: list/status/resolve wave-bound chain groups"],
|
|
41044
41219
|
["feed", "Tail job events; use -f to follow all jobs"],
|
|
41045
|
-
["poll", "Machine-readable job status polling (for scripts/Claude Code)"],
|
|
41046
41220
|
["result", "Print final output of a completed job; --wait polls until done, --timeout <ms> sets a limit"],
|
|
41047
41221
|
["clean", "Purge completed job directories (TTL, --all, --keep, --dry-run)"],
|
|
41048
41222
|
["merge", "Publish one standalone chain (refuses unresolved epic chains)"],
|
|
@@ -41083,7 +41257,7 @@ var init_help = __esm(() => {
|
|
|
41083
41257
|
});
|
|
41084
41258
|
|
|
41085
41259
|
// src/index.ts
|
|
41086
|
-
import { spawnSync as
|
|
41260
|
+
import { spawnSync as spawnSync25 } from "child_process";
|
|
41087
41261
|
|
|
41088
41262
|
// node_modules/zod/v4/core/core.js
|
|
41089
41263
|
var NEVER2 = Object.freeze({
|
|
@@ -44857,7 +45031,7 @@ var AssertObjectSchema = custom2((v) => v !== null && (typeof v === "object" ||
|
|
|
44857
45031
|
var ProgressTokenSchema = union([string2(), number2().int()]);
|
|
44858
45032
|
var CursorSchema = string2();
|
|
44859
45033
|
var TaskCreationParamsSchema = looseObject({
|
|
44860
|
-
ttl:
|
|
45034
|
+
ttl: number2().optional(),
|
|
44861
45035
|
pollInterval: number2().optional()
|
|
44862
45036
|
});
|
|
44863
45037
|
var TaskMetadataSchema = object2({
|
|
@@ -45011,7 +45185,8 @@ var ClientCapabilitiesSchema = object2({
|
|
|
45011
45185
|
roots: object2({
|
|
45012
45186
|
listChanged: boolean2().optional()
|
|
45013
45187
|
}).optional(),
|
|
45014
|
-
tasks: ClientTasksCapabilitySchema.optional()
|
|
45188
|
+
tasks: ClientTasksCapabilitySchema.optional(),
|
|
45189
|
+
extensions: record(string2(), AssertObjectSchema).optional()
|
|
45015
45190
|
});
|
|
45016
45191
|
var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
45017
45192
|
protocolVersion: string2(),
|
|
@@ -45036,7 +45211,8 @@ var ServerCapabilitiesSchema = object2({
|
|
|
45036
45211
|
tools: object2({
|
|
45037
45212
|
listChanged: boolean2().optional()
|
|
45038
45213
|
}).optional(),
|
|
45039
|
-
tasks: ServerTasksCapabilitySchema.optional()
|
|
45214
|
+
tasks: ServerTasksCapabilitySchema.optional(),
|
|
45215
|
+
extensions: record(string2(), AssertObjectSchema).optional()
|
|
45040
45216
|
});
|
|
45041
45217
|
var InitializeResultSchema = ResultSchema.extend({
|
|
45042
45218
|
protocolVersion: string2(),
|
|
@@ -45151,6 +45327,7 @@ var ResourceSchema = object2({
|
|
|
45151
45327
|
uri: string2(),
|
|
45152
45328
|
description: optional(string2()),
|
|
45153
45329
|
mimeType: optional(string2()),
|
|
45330
|
+
size: optional(number2()),
|
|
45154
45331
|
annotations: AnnotationsSchema.optional(),
|
|
45155
45332
|
_meta: optional(looseObject({}))
|
|
45156
45333
|
});
|
|
@@ -47146,6 +47323,10 @@ class Protocol {
|
|
|
47146
47323
|
this._progressHandlers.clear();
|
|
47147
47324
|
this._taskProgressTokens.clear();
|
|
47148
47325
|
this._pendingDebouncedNotifications.clear();
|
|
47326
|
+
for (const info of this._timeoutInfo.values()) {
|
|
47327
|
+
clearTimeout(info.timeoutId);
|
|
47328
|
+
}
|
|
47329
|
+
this._timeoutInfo.clear();
|
|
47149
47330
|
for (const controller of this._requestHandlerAbortControllers.values()) {
|
|
47150
47331
|
controller.abort();
|
|
47151
47332
|
}
|
|
@@ -47276,7 +47457,9 @@ class Protocol {
|
|
|
47276
47457
|
await capturedTransport?.send(errorResponse);
|
|
47277
47458
|
}
|
|
47278
47459
|
}).catch((error2) => this._onerror(new Error(`Failed to send response: ${error2}`))).finally(() => {
|
|
47279
|
-
this._requestHandlerAbortControllers.
|
|
47460
|
+
if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
|
|
47461
|
+
this._requestHandlerAbortControllers.delete(request.id);
|
|
47462
|
+
}
|
|
47280
47463
|
});
|
|
47281
47464
|
}
|
|
47282
47465
|
_onprogress(notification) {
|
|
@@ -48578,7 +48761,7 @@ var next = process.argv[3];
|
|
|
48578
48761
|
function wantsHelp() {
|
|
48579
48762
|
return next === "--help" || next === "-h";
|
|
48580
48763
|
}
|
|
48581
|
-
async function
|
|
48764
|
+
async function run33() {
|
|
48582
48765
|
if (sub === "install") {
|
|
48583
48766
|
if (wantsHelp()) {
|
|
48584
48767
|
console.log([
|
|
@@ -48963,7 +49146,7 @@ async function run34() {
|
|
|
48963
49146
|
"",
|
|
48964
49147
|
"Async execution patterns:",
|
|
48965
49148
|
" MCP: use_specialist (foreground, returns result directly)",
|
|
48966
|
-
" CLI: run prints [job started: <id>] on stderr, then use feed/
|
|
49149
|
+
" CLI: run prints [job started: <id>] on stderr, then use ps/feed/result",
|
|
48967
49150
|
' Shell: specialists run <name> --prompt "..." &',
|
|
48968
49151
|
""
|
|
48969
49152
|
].join(`
|
|
@@ -49201,53 +49384,6 @@ async function run34() {
|
|
|
49201
49384
|
const { run: handler } = await Promise.resolve().then(() => (init_feed(), exports_feed));
|
|
49202
49385
|
return handler();
|
|
49203
49386
|
}
|
|
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
49387
|
if (sub === "steer") {
|
|
49252
49388
|
if (wantsHelp()) {
|
|
49253
49389
|
console.log([
|
|
@@ -49531,7 +49667,7 @@ async function run34() {
|
|
|
49531
49667
|
if (wantsHelp()) {
|
|
49532
49668
|
console.log([
|
|
49533
49669
|
"",
|
|
49534
|
-
"Usage: specialists serve [--port <n>] [--concurrency <n>] [--shutdown-grace-ms <n>] [--project-dir <path>]",
|
|
49670
|
+
"Usage: specialists serve [--port <n>] [--concurrency <n>] [--shutdown-grace-ms <n>] [--project-dir <path>] [--db-path <observability.db>] [--readiness-canary off|warn|require] [--log-level off|info|debug]",
|
|
49535
49671
|
"",
|
|
49536
49672
|
"HTTP wrapper for script-class specialists.",
|
|
49537
49673
|
"",
|
|
@@ -49567,7 +49703,7 @@ async function run34() {
|
|
|
49567
49703
|
}
|
|
49568
49704
|
if (sub === "release") {
|
|
49569
49705
|
console.error("Deprecated. Use `xt release prepare/publish`. This alias will be removed in v4.0.");
|
|
49570
|
-
const result =
|
|
49706
|
+
const result = spawnSync25("xt", ["release", ...process.argv.slice(3)], { stdio: "inherit" });
|
|
49571
49707
|
if (result.error) {
|
|
49572
49708
|
console.error(`Failed to run xt release: ${result.error.message}`);
|
|
49573
49709
|
process.exit(1);
|
|
@@ -49587,7 +49723,7 @@ Run 'specialists help' to see available commands.`);
|
|
|
49587
49723
|
const server = new SpecialistsServer;
|
|
49588
49724
|
await server.start();
|
|
49589
49725
|
}
|
|
49590
|
-
|
|
49726
|
+
run33().catch((error2) => {
|
|
49591
49727
|
logger.error(`Fatal error: ${error2}`);
|
|
49592
49728
|
process.exit(1);
|
|
49593
49729
|
});
|