@jaggerxtrm/specialists 3.3.4 → 3.4.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/README.md +4 -2
- package/bin/install.js +15 -204
- package/config/skills/using-specialists/SKILL.md +1 -1
- package/config/specialists/debugger.specialist.yaml +111 -0
- package/config/specialists/explorer.specialist.yaml +1 -1
- package/dist/index.js +791 -566
- package/package.json +1 -1
- package/config/skills/specialists-usage-workspace/iteration-1/eval-bead-background/old_skill/outputs/result.md +0 -105
- package/config/skills/specialists-usage-workspace/iteration-1/eval-bead-background/with_skill/outputs/result.md +0 -93
- package/config/skills/specialists-usage-workspace/iteration-1/eval-fresh-setup/old_skill/outputs/result.md +0 -113
- package/config/skills/specialists-usage-workspace/iteration-1/eval-fresh-setup/with_skill/outputs/result.md +0 -131
- package/config/skills/specialists-usage-workspace/iteration-1/eval-yaml-debug/old_skill/outputs/result.md +0 -159
- package/config/skills/specialists-usage-workspace/iteration-1/eval-yaml-debug/with_skill/outputs/result.md +0 -150
- package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/with_skill/outputs/result.md +0 -180
- package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/with_skill/timing.json +0 -5
- package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/without_skill/outputs/result.md +0 -223
- package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/without_skill/timing.json +0 -5
- package/config/skills/specialists-usage-workspace/iteration-2/eval-code-review/with_skill/timing.json +0 -5
- package/config/skills/specialists-usage-workspace/iteration-2/eval-code-review/without_skill/outputs/result.md +0 -146
- package/config/skills/specialists-usage-workspace/iteration-2/eval-code-review/without_skill/timing.json +0 -5
- package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/with_skill/outputs/result.md +0 -89
- package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/with_skill/timing.json +0 -5
- package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/without_skill/outputs/result.md +0 -96
- package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/without_skill/timing.json +0 -5
- package/config/skills/specialists-usage-workspace/skill-snapshot/SKILL.md.old +0 -237
- package/config/specialists/bug-hunt.specialist.yaml +0 -96
package/dist/index.js
CHANGED
|
@@ -17410,7 +17410,98 @@ var init_dist = __esm(() => {
|
|
|
17410
17410
|
});
|
|
17411
17411
|
|
|
17412
17412
|
// src/specialist/schema.ts
|
|
17413
|
+
function formatPath(path) {
|
|
17414
|
+
return path.map((p) => typeof p === "number" ? `[${p}]` : p).join(".");
|
|
17415
|
+
}
|
|
17416
|
+
function getFriendlyMessage(issue2) {
|
|
17417
|
+
const path = formatPath(issue2.path);
|
|
17418
|
+
if (issue2.code === "invalid_string" && issue2.validation === "regex") {
|
|
17419
|
+
if (path.includes("name")) {
|
|
17420
|
+
return `Invalid specialist name: must be kebab-case (lowercase letters, numbers, hyphens). Got: "${issue2.path.at(-1) === "name" ? "invalid value" : "see schema"}"`;
|
|
17421
|
+
}
|
|
17422
|
+
if (path.includes("version")) {
|
|
17423
|
+
return `Invalid version: must be semver format (e.g., "1.0.0"). Got value that doesn't match pattern.`;
|
|
17424
|
+
}
|
|
17425
|
+
}
|
|
17426
|
+
if (issue2.code === "invalid_enum_value") {
|
|
17427
|
+
const allowed = issue2.options.map((o) => `"${o}"`).join(", ");
|
|
17428
|
+
if (path.includes("permission_required")) {
|
|
17429
|
+
return `Invalid permission_required: must be one of ${allowed}. This controls which pi tools are available.`;
|
|
17430
|
+
}
|
|
17431
|
+
if (path.includes("mode")) {
|
|
17432
|
+
return `Invalid execution.mode: must be one of ${allowed}.`;
|
|
17433
|
+
}
|
|
17434
|
+
if (path.includes("beads_integration")) {
|
|
17435
|
+
return `Invalid beads_integration: must be one of ${allowed}.`;
|
|
17436
|
+
}
|
|
17437
|
+
return `Invalid value at "${path}": expected one of ${allowed}, got "${issue2.received}"`;
|
|
17438
|
+
}
|
|
17439
|
+
if (issue2.code === "invalid_type") {
|
|
17440
|
+
return `Invalid type at "${path}": expected ${issue2.expected}, got ${issue2.received}`;
|
|
17441
|
+
}
|
|
17442
|
+
if (issue2.code === "invalid_literal") {
|
|
17443
|
+
return `Invalid value at "${path}": expected "${issue2.expected}"`;
|
|
17444
|
+
}
|
|
17445
|
+
if (issue2.code === "missing_key") {
|
|
17446
|
+
return `Missing required field: "${formatPath(issue2.path)}"`;
|
|
17447
|
+
}
|
|
17448
|
+
return issue2.message;
|
|
17449
|
+
}
|
|
17450
|
+
async function validateSpecialist(yamlContent) {
|
|
17451
|
+
const errors5 = [];
|
|
17452
|
+
const warnings = [];
|
|
17453
|
+
let raw;
|
|
17454
|
+
try {
|
|
17455
|
+
raw = $parse(yamlContent);
|
|
17456
|
+
} catch (e) {
|
|
17457
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
17458
|
+
errors5.push({
|
|
17459
|
+
path: "yaml",
|
|
17460
|
+
message: `YAML parse error: ${msg}`,
|
|
17461
|
+
code: "yaml_parse_error"
|
|
17462
|
+
});
|
|
17463
|
+
return { valid: false, errors: errors5, warnings };
|
|
17464
|
+
}
|
|
17465
|
+
const result = SpecialistSchema.safeParse(raw);
|
|
17466
|
+
if (!result.success) {
|
|
17467
|
+
for (const issue2 of result.error.issues) {
|
|
17468
|
+
errors5.push({
|
|
17469
|
+
path: formatPath(issue2.path),
|
|
17470
|
+
message: getFriendlyMessage(issue2),
|
|
17471
|
+
code: issue2.code
|
|
17472
|
+
});
|
|
17473
|
+
}
|
|
17474
|
+
} else {
|
|
17475
|
+
const spec = result.data;
|
|
17476
|
+
if (spec.specialist.prompt.normalize_template) {
|
|
17477
|
+
warnings.push("prompt.normalize_template is deprecated (Mercury compat) and will be ignored");
|
|
17478
|
+
}
|
|
17479
|
+
if (spec.specialist.execution.preferred_profile) {
|
|
17480
|
+
warnings.push("execution.preferred_profile is deprecated (Agent Forge compat) and will be ignored");
|
|
17481
|
+
}
|
|
17482
|
+
if (spec.specialist.execution.approval_mode) {
|
|
17483
|
+
warnings.push("execution.approval_mode is deprecated (Agent Forge compat) and will be ignored");
|
|
17484
|
+
}
|
|
17485
|
+
if (!spec.specialist.execution.model.includes("/")) {
|
|
17486
|
+
warnings.push(`Model "${spec.specialist.execution.model}" doesn't include a provider prefix. Expected format: "provider/model-id" (e.g., "anthropic/claude-sonnet-4-5")`);
|
|
17487
|
+
}
|
|
17488
|
+
}
|
|
17489
|
+
return { valid: errors5.length === 0, errors: errors5, warnings };
|
|
17490
|
+
}
|
|
17413
17491
|
async function parseSpecialist(yamlContent) {
|
|
17492
|
+
const result = await validateSpecialist(yamlContent);
|
|
17493
|
+
if (!result.valid) {
|
|
17494
|
+
const errorList = result.errors.map((e) => ` • ${e.message}`).join(`
|
|
17495
|
+
`);
|
|
17496
|
+
throw new Error(`Schema validation failed:
|
|
17497
|
+
${errorList}`);
|
|
17498
|
+
}
|
|
17499
|
+
if (result.warnings.length > 0) {
|
|
17500
|
+
process.stderr.write(`[specialists] warnings:
|
|
17501
|
+
${result.warnings.map((w) => ` ⚠ ${w}`).join(`
|
|
17502
|
+
`)}
|
|
17503
|
+
`);
|
|
17504
|
+
}
|
|
17414
17505
|
const raw = $parse(yamlContent);
|
|
17415
17506
|
return SpecialistSchema.parseAsync(raw);
|
|
17416
17507
|
}
|
|
@@ -17767,13 +17858,36 @@ class PiAgentSession {
|
|
|
17767
17858
|
handler?.(event);
|
|
17768
17859
|
return;
|
|
17769
17860
|
}
|
|
17770
|
-
if (type === "message_start"
|
|
17771
|
-
const
|
|
17772
|
-
if (
|
|
17773
|
-
this.options.
|
|
17861
|
+
if (type === "message_start") {
|
|
17862
|
+
const role = event.message?.role;
|
|
17863
|
+
if (role === "assistant") {
|
|
17864
|
+
this.options.onEvent?.("message_start_assistant");
|
|
17865
|
+
const { provider, model } = event.message ?? {};
|
|
17866
|
+
if (provider || model) {
|
|
17867
|
+
this.options.onMeta?.({ backend: provider ?? "", model: model ?? "" });
|
|
17868
|
+
}
|
|
17869
|
+
} else if (role === "toolResult") {
|
|
17870
|
+
this.options.onEvent?.("message_start_tool_result");
|
|
17871
|
+
}
|
|
17872
|
+
return;
|
|
17873
|
+
}
|
|
17874
|
+
if (type === "message_end") {
|
|
17875
|
+
const role = event.message?.role;
|
|
17876
|
+
if (role === "assistant") {
|
|
17877
|
+
this.options.onEvent?.("message_end_assistant");
|
|
17878
|
+
} else if (role === "toolResult") {
|
|
17879
|
+
this.options.onEvent?.("message_end_tool_result");
|
|
17774
17880
|
}
|
|
17775
17881
|
return;
|
|
17776
17882
|
}
|
|
17883
|
+
if (type === "turn_start") {
|
|
17884
|
+
this.options.onEvent?.("turn_start");
|
|
17885
|
+
return;
|
|
17886
|
+
}
|
|
17887
|
+
if (type === "turn_end") {
|
|
17888
|
+
this.options.onEvent?.("turn_end");
|
|
17889
|
+
return;
|
|
17890
|
+
}
|
|
17777
17891
|
if (type === "agent_end") {
|
|
17778
17892
|
const messages = event.messages ?? [];
|
|
17779
17893
|
const last = [...messages].reverse().find((m) => m.role === "assistant");
|
|
@@ -17786,11 +17900,11 @@ class PiAgentSession {
|
|
|
17786
17900
|
return;
|
|
17787
17901
|
}
|
|
17788
17902
|
if (type === "tool_execution_start") {
|
|
17789
|
-
this.options.onEvent?.("
|
|
17903
|
+
this.options.onEvent?.("tool_execution_start");
|
|
17790
17904
|
return;
|
|
17791
17905
|
}
|
|
17792
17906
|
if (type === "tool_execution_update") {
|
|
17793
|
-
this.options.onEvent?.("
|
|
17907
|
+
this.options.onEvent?.("tool_execution_update");
|
|
17794
17908
|
return;
|
|
17795
17909
|
}
|
|
17796
17910
|
if (type === "tool_execution_end") {
|
|
@@ -18475,6 +18589,23 @@ function mapCallbackEventToTimelineEvent(callbackEvent, context) {
|
|
|
18475
18589
|
phase: "start",
|
|
18476
18590
|
tool_call_id: context.toolCallId
|
|
18477
18591
|
};
|
|
18592
|
+
case "tool_execution_start":
|
|
18593
|
+
return {
|
|
18594
|
+
t,
|
|
18595
|
+
type: TIMELINE_EVENT_TYPES.TOOL,
|
|
18596
|
+
tool: context.tool ?? "unknown",
|
|
18597
|
+
phase: "start",
|
|
18598
|
+
tool_call_id: context.toolCallId
|
|
18599
|
+
};
|
|
18600
|
+
case "tool_execution_update":
|
|
18601
|
+
case "tool_execution":
|
|
18602
|
+
return {
|
|
18603
|
+
t,
|
|
18604
|
+
type: TIMELINE_EVENT_TYPES.TOOL,
|
|
18605
|
+
tool: context.tool ?? "unknown",
|
|
18606
|
+
phase: "update",
|
|
18607
|
+
tool_call_id: context.toolCallId
|
|
18608
|
+
};
|
|
18478
18609
|
case "tool_execution_end":
|
|
18479
18610
|
return {
|
|
18480
18611
|
t,
|
|
@@ -18484,6 +18615,18 @@ function mapCallbackEventToTimelineEvent(callbackEvent, context) {
|
|
|
18484
18615
|
tool_call_id: context.toolCallId,
|
|
18485
18616
|
is_error: context.isError
|
|
18486
18617
|
};
|
|
18618
|
+
case "message_start_assistant":
|
|
18619
|
+
return { t, type: TIMELINE_EVENT_TYPES.MESSAGE, phase: "start", role: "assistant" };
|
|
18620
|
+
case "message_end_assistant":
|
|
18621
|
+
return { t, type: TIMELINE_EVENT_TYPES.MESSAGE, phase: "end", role: "assistant" };
|
|
18622
|
+
case "message_start_tool_result":
|
|
18623
|
+
return { t, type: TIMELINE_EVENT_TYPES.MESSAGE, phase: "start", role: "toolResult" };
|
|
18624
|
+
case "message_end_tool_result":
|
|
18625
|
+
return { t, type: TIMELINE_EVENT_TYPES.MESSAGE, phase: "end", role: "toolResult" };
|
|
18626
|
+
case "turn_start":
|
|
18627
|
+
return { t, type: TIMELINE_EVENT_TYPES.TURN, phase: "start" };
|
|
18628
|
+
case "turn_end":
|
|
18629
|
+
return { t, type: TIMELINE_EVENT_TYPES.TURN, phase: "end" };
|
|
18487
18630
|
case "text":
|
|
18488
18631
|
return { t, type: TIMELINE_EVENT_TYPES.TEXT };
|
|
18489
18632
|
case "agent_end":
|
|
@@ -18564,6 +18707,8 @@ var init_timeline_events = __esm(() => {
|
|
|
18564
18707
|
THINKING: "thinking",
|
|
18565
18708
|
TOOL: "tool",
|
|
18566
18709
|
TEXT: "text",
|
|
18710
|
+
MESSAGE: "message",
|
|
18711
|
+
TURN: "turn",
|
|
18567
18712
|
RUN_COMPLETE: "run_complete",
|
|
18568
18713
|
DONE: "done",
|
|
18569
18714
|
AGENT_END: "agent_end"
|
|
@@ -18659,6 +18804,7 @@ class Supervisor {
|
|
|
18659
18804
|
return jobs.sort((a, b) => b.started_at_ms - a.started_at_ms);
|
|
18660
18805
|
}
|
|
18661
18806
|
writeStatusFile(id, data) {
|
|
18807
|
+
mkdirSync(this.jobDir(id), { recursive: true });
|
|
18662
18808
|
const path = this.statusPath(id);
|
|
18663
18809
|
const tmp = path + ".tmp";
|
|
18664
18810
|
writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
|
|
@@ -18726,6 +18872,14 @@ class Supervisor {
|
|
|
18726
18872
|
pid: process.pid
|
|
18727
18873
|
};
|
|
18728
18874
|
this.writeStatusFile(id, initialStatus);
|
|
18875
|
+
writeFileSync(join3(this.opts.jobsDir, "latest"), `${id}
|
|
18876
|
+
`, "utf-8");
|
|
18877
|
+
this.opts.onJobStarted?.({ id });
|
|
18878
|
+
let statusSnapshot = initialStatus;
|
|
18879
|
+
const setStatus = (updates) => {
|
|
18880
|
+
statusSnapshot = { ...statusSnapshot, ...updates };
|
|
18881
|
+
this.writeStatusFile(id, statusSnapshot);
|
|
18882
|
+
};
|
|
18729
18883
|
const eventsFd = openSync(this.eventsPath(id), "a");
|
|
18730
18884
|
const appendTimelineEvent = (event) => {
|
|
18731
18885
|
try {
|
|
@@ -18741,7 +18895,7 @@ class Supervisor {
|
|
|
18741
18895
|
if (needsFifo) {
|
|
18742
18896
|
try {
|
|
18743
18897
|
execFileSync("mkfifo", [fifoPath]);
|
|
18744
|
-
|
|
18898
|
+
setStatus({ fifo_path: fifoPath });
|
|
18745
18899
|
} catch {}
|
|
18746
18900
|
}
|
|
18747
18901
|
let textLogged = false;
|
|
@@ -18760,12 +18914,12 @@ class Supervisor {
|
|
|
18760
18914
|
const toolMatch = delta.match(/⚙ (.+?)…/);
|
|
18761
18915
|
if (toolMatch) {
|
|
18762
18916
|
currentTool = toolMatch[1];
|
|
18763
|
-
|
|
18917
|
+
setStatus({ current_tool: currentTool });
|
|
18764
18918
|
}
|
|
18765
18919
|
this.opts.onProgress?.(delta);
|
|
18766
18920
|
}, (eventType) => {
|
|
18767
18921
|
const now = Date.now();
|
|
18768
|
-
|
|
18922
|
+
setStatus({
|
|
18769
18923
|
status: "running",
|
|
18770
18924
|
current_event: eventType,
|
|
18771
18925
|
last_event_at_ms: now,
|
|
@@ -18782,13 +18936,13 @@ class Supervisor {
|
|
|
18782
18936
|
appendTimelineEvent({ t: Date.now(), type: TIMELINE_EVENT_TYPES.TEXT });
|
|
18783
18937
|
}
|
|
18784
18938
|
}, (meta) => {
|
|
18785
|
-
|
|
18939
|
+
setStatus({ model: meta.model, backend: meta.backend });
|
|
18786
18940
|
appendTimelineEvent(createMetaEvent(meta.model, meta.backend));
|
|
18787
18941
|
this.opts.onMeta?.(meta);
|
|
18788
18942
|
}, (fn) => {
|
|
18789
18943
|
killFn = fn;
|
|
18790
18944
|
}, (beadId) => {
|
|
18791
|
-
|
|
18945
|
+
setStatus({ bead_id: beadId });
|
|
18792
18946
|
}, (fn) => {
|
|
18793
18947
|
steerFn = fn;
|
|
18794
18948
|
if (!needsFifo || !existsSync4(fifoPath))
|
|
@@ -18802,17 +18956,18 @@ class Supervisor {
|
|
|
18802
18956
|
steerFn?.(parsed.message).catch(() => {});
|
|
18803
18957
|
} else if (parsed?.type === "prompt" && typeof parsed.message === "string") {
|
|
18804
18958
|
if (resumeFn) {
|
|
18805
|
-
|
|
18959
|
+
setStatus({ status: "running", current_event: "starting" });
|
|
18806
18960
|
resumeFn(parsed.message).then((output) => {
|
|
18961
|
+
mkdirSync(this.jobDir(id), { recursive: true });
|
|
18807
18962
|
writeFileSync(this.resultPath(id), output, "utf-8");
|
|
18808
|
-
|
|
18963
|
+
setStatus({
|
|
18809
18964
|
status: "waiting",
|
|
18810
18965
|
current_event: "waiting",
|
|
18811
18966
|
elapsed_s: Math.round((Date.now() - startedAtMs) / 1000),
|
|
18812
18967
|
last_event_at_ms: Date.now()
|
|
18813
18968
|
});
|
|
18814
18969
|
}).catch((err) => {
|
|
18815
|
-
|
|
18970
|
+
setStatus({ status: "error", error: err?.message ?? String(err) });
|
|
18816
18971
|
});
|
|
18817
18972
|
}
|
|
18818
18973
|
} else if (parsed?.type === "close") {
|
|
@@ -18824,14 +18979,15 @@ class Supervisor {
|
|
|
18824
18979
|
}, (rFn, cFn) => {
|
|
18825
18980
|
resumeFn = rFn;
|
|
18826
18981
|
closeFn = cFn;
|
|
18827
|
-
|
|
18982
|
+
setStatus({ status: "waiting", current_event: "waiting" });
|
|
18828
18983
|
});
|
|
18829
18984
|
const elapsed = Math.round((Date.now() - startedAtMs) / 1000);
|
|
18985
|
+
mkdirSync(this.jobDir(id), { recursive: true });
|
|
18830
18986
|
writeFileSync(this.resultPath(id), result.output, "utf-8");
|
|
18831
18987
|
if (result.beadId) {
|
|
18832
18988
|
this.opts.beadsClient?.updateBeadNotes(result.beadId, formatBeadNotes(result));
|
|
18833
18989
|
}
|
|
18834
|
-
|
|
18990
|
+
setStatus({
|
|
18835
18991
|
status: "done",
|
|
18836
18992
|
elapsed_s: elapsed,
|
|
18837
18993
|
last_event_at_ms: Date.now(),
|
|
@@ -18844,12 +19000,13 @@ class Supervisor {
|
|
|
18844
19000
|
backend: result.backend,
|
|
18845
19001
|
bead_id: result.beadId
|
|
18846
19002
|
}));
|
|
19003
|
+
mkdirSync(this.readyDir(), { recursive: true });
|
|
18847
19004
|
writeFileSync(join3(this.readyDir(), id), "", "utf-8");
|
|
18848
19005
|
return id;
|
|
18849
19006
|
} catch (err) {
|
|
18850
19007
|
const elapsed = Math.round((Date.now() - startedAtMs) / 1000);
|
|
18851
19008
|
const errorMsg = err?.message ?? String(err);
|
|
18852
|
-
|
|
19009
|
+
setStatus({
|
|
18853
19010
|
status: "error",
|
|
18854
19011
|
elapsed_s: elapsed,
|
|
18855
19012
|
error: errorMsg
|
|
@@ -18888,14 +19045,21 @@ var exports_install = {};
|
|
|
18888
19045
|
__export(exports_install, {
|
|
18889
19046
|
run: () => run
|
|
18890
19047
|
});
|
|
18891
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
18892
|
-
import { fileURLToPath } from "node:url";
|
|
18893
|
-
import { dirname as dirname2, join as join8 } from "node:path";
|
|
18894
19048
|
async function run() {
|
|
18895
|
-
|
|
18896
|
-
|
|
19049
|
+
console.log("");
|
|
19050
|
+
console.log(yellow("⚠ DEPRECATED: `specialists install` is deprecated"));
|
|
19051
|
+
console.log("");
|
|
19052
|
+
console.log(` Use ${bold("specialists init")} instead.`);
|
|
19053
|
+
console.log("");
|
|
19054
|
+
console.log(" The init command:");
|
|
19055
|
+
console.log(" • creates specialists/ and .specialists/ directories");
|
|
19056
|
+
console.log(" • registers the MCP server in .mcp.json");
|
|
19057
|
+
console.log(" • injects workflow context into AGENTS.md/CLAUDE.md");
|
|
19058
|
+
console.log("");
|
|
19059
|
+
console.log(` ${dim("Run: specialists init --help for full details")}`);
|
|
19060
|
+
console.log("");
|
|
18897
19061
|
}
|
|
18898
|
-
var
|
|
19062
|
+
var bold = (s) => `\x1B[1m${s}\x1B[0m`, yellow = (s) => `\x1B[33m${s}\x1B[0m`, dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
18899
19063
|
|
|
18900
19064
|
// src/cli/version.ts
|
|
18901
19065
|
var exports_version = {};
|
|
@@ -18903,9 +19067,23 @@ __export(exports_version, {
|
|
|
18903
19067
|
run: () => run2
|
|
18904
19068
|
});
|
|
18905
19069
|
import { createRequire as createRequire2 } from "node:module";
|
|
19070
|
+
import { fileURLToPath } from "node:url";
|
|
19071
|
+
import { dirname as dirname2, join as join8 } from "node:path";
|
|
19072
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
18906
19073
|
async function run2() {
|
|
18907
19074
|
const req = createRequire2(import.meta.url);
|
|
18908
|
-
const
|
|
19075
|
+
const here = dirname2(fileURLToPath(import.meta.url));
|
|
19076
|
+
const bundlePkgPath = join8(here, "..", "package.json");
|
|
19077
|
+
const sourcePkgPath = join8(here, "..", "..", "package.json");
|
|
19078
|
+
let pkg;
|
|
19079
|
+
if (existsSync6(bundlePkgPath)) {
|
|
19080
|
+
pkg = req("../package.json");
|
|
19081
|
+
} else if (existsSync6(sourcePkgPath)) {
|
|
19082
|
+
pkg = req("../../package.json");
|
|
19083
|
+
} else {
|
|
19084
|
+
console.error("Cannot find package.json");
|
|
19085
|
+
process.exit(1);
|
|
19086
|
+
}
|
|
18909
19087
|
console.log(`${pkg.name} v${pkg.version}`);
|
|
18910
19088
|
}
|
|
18911
19089
|
var init_version = () => {};
|
|
@@ -18970,19 +19148,19 @@ async function run3() {
|
|
|
18970
19148
|
}
|
|
18971
19149
|
const nameWidth = Math.max(...specialists.map((s) => s.name.length), 4);
|
|
18972
19150
|
console.log(`
|
|
18973
|
-
${
|
|
19151
|
+
${bold2(`Specialists (${specialists.length})`)}
|
|
18974
19152
|
`);
|
|
18975
19153
|
for (const s of specialists) {
|
|
18976
19154
|
const name = cyan(s.name.padEnd(nameWidth));
|
|
18977
|
-
const scopeTag = s.scope === "default" ? green("[default]") :
|
|
18978
|
-
const model =
|
|
19155
|
+
const scopeTag = s.scope === "default" ? green("[default]") : yellow2("[user]");
|
|
19156
|
+
const model = dim2(s.model);
|
|
18979
19157
|
const desc = s.description.length > 80 ? s.description.slice(0, 79) + "…" : s.description;
|
|
18980
19158
|
console.log(` ${name} ${scopeTag} ${model}`);
|
|
18981
|
-
console.log(` ${" ".repeat(nameWidth)} ${
|
|
19159
|
+
console.log(` ${" ".repeat(nameWidth)} ${dim2(desc)}`);
|
|
18982
19160
|
console.log();
|
|
18983
19161
|
}
|
|
18984
19162
|
}
|
|
18985
|
-
var
|
|
19163
|
+
var dim2 = (s) => `\x1B[2m${s}\x1B[0m`, bold2 = (s) => `\x1B[1m${s}\x1B[0m`, cyan = (s) => `\x1B[36m${s}\x1B[0m`, green = (s) => `\x1B[32m${s}\x1B[0m`, yellow2 = (s) => `\x1B[33m${s}\x1B[0m`, ArgParseError;
|
|
18986
19164
|
var init_list = __esm(() => {
|
|
18987
19165
|
init_loader();
|
|
18988
19166
|
ArgParseError = class ArgParseError extends Error {
|
|
@@ -19069,31 +19247,31 @@ async function run4() {
|
|
|
19069
19247
|
}
|
|
19070
19248
|
const total = models.length;
|
|
19071
19249
|
console.log(`
|
|
19072
|
-
${
|
|
19250
|
+
${bold3(`Models on pi`)} ${dim3(`(${total} total)`)}
|
|
19073
19251
|
`);
|
|
19074
19252
|
for (const [provider, pModels] of byProvider) {
|
|
19075
|
-
console.log(` ${cyan2(provider)} ${
|
|
19253
|
+
console.log(` ${cyan2(provider)} ${dim3(`${pModels.length} model${pModels.length !== 1 ? "s" : ""}`)}`);
|
|
19076
19254
|
const modelWidth = Math.max(...pModels.map((m) => m.model.length));
|
|
19077
19255
|
for (const m of pModels) {
|
|
19078
19256
|
const key = `${m.provider}/${m.model}`;
|
|
19079
19257
|
const inUse = usedBy.get(key);
|
|
19080
19258
|
const flags = [
|
|
19081
|
-
m.thinking ? green2("thinking") :
|
|
19082
|
-
m.images ?
|
|
19259
|
+
m.thinking ? green2("thinking") : dim3("·"),
|
|
19260
|
+
m.images ? dim3("images") : ""
|
|
19083
19261
|
].filter(Boolean).join(" ");
|
|
19084
|
-
const ctx =
|
|
19085
|
-
const usedLabel = inUse ? ` ${
|
|
19262
|
+
const ctx = dim3(`ctx ${m.context}`);
|
|
19263
|
+
const usedLabel = inUse ? ` ${yellow3("←")} ${dim3(inUse.join(", "))}` : "";
|
|
19086
19264
|
console.log(` ${m.model.padEnd(modelWidth)} ${ctx.padEnd(18)} ${flags}${usedLabel}`);
|
|
19087
19265
|
}
|
|
19088
19266
|
console.log();
|
|
19089
19267
|
}
|
|
19090
19268
|
if (!args.used) {
|
|
19091
|
-
console.log(
|
|
19092
|
-
console.log(
|
|
19269
|
+
console.log(dim3(` --provider <name> filter by provider`));
|
|
19270
|
+
console.log(dim3(` --used only show models used by your specialists`));
|
|
19093
19271
|
console.log();
|
|
19094
19272
|
}
|
|
19095
19273
|
}
|
|
19096
|
-
var
|
|
19274
|
+
var bold3 = (s) => `\x1B[1m${s}\x1B[0m`, dim3 = (s) => `\x1B[2m${s}\x1B[0m`, cyan2 = (s) => `\x1B[36m${s}\x1B[0m`, yellow3 = (s) => `\x1B[33m${s}\x1B[0m`, green2 = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
19097
19275
|
var init_models = __esm(() => {
|
|
19098
19276
|
init_loader();
|
|
19099
19277
|
});
|
|
@@ -19103,17 +19281,17 @@ var exports_init = {};
|
|
|
19103
19281
|
__export(exports_init, {
|
|
19104
19282
|
run: () => run5
|
|
19105
19283
|
});
|
|
19106
|
-
import { copyFileSync, cpSync, existsSync as
|
|
19284
|
+
import { copyFileSync, cpSync, existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
19107
19285
|
import { join as join9 } from "node:path";
|
|
19108
19286
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19109
19287
|
function ok(msg) {
|
|
19110
19288
|
console.log(` ${green3("✓")} ${msg}`);
|
|
19111
19289
|
}
|
|
19112
19290
|
function skip(msg) {
|
|
19113
|
-
console.log(` ${
|
|
19291
|
+
console.log(` ${yellow4("○")} ${msg}`);
|
|
19114
19292
|
}
|
|
19115
19293
|
function loadJson(path, fallback) {
|
|
19116
|
-
if (!
|
|
19294
|
+
if (!existsSync7(path))
|
|
19117
19295
|
return structuredClone(fallback);
|
|
19118
19296
|
try {
|
|
19119
19297
|
return JSON.parse(readFileSync3(path, "utf-8"));
|
|
@@ -19128,10 +19306,10 @@ function saveJson(path, value) {
|
|
|
19128
19306
|
function resolvePackagePath(relativePath) {
|
|
19129
19307
|
const configPath = `config/${relativePath}`;
|
|
19130
19308
|
let resolved = fileURLToPath2(new URL(`../${configPath}`, import.meta.url));
|
|
19131
|
-
if (
|
|
19309
|
+
if (existsSync7(resolved))
|
|
19132
19310
|
return resolved;
|
|
19133
19311
|
resolved = fileURLToPath2(new URL(`../../${configPath}`, import.meta.url));
|
|
19134
|
-
if (
|
|
19312
|
+
if (existsSync7(resolved))
|
|
19135
19313
|
return resolved;
|
|
19136
19314
|
return null;
|
|
19137
19315
|
}
|
|
@@ -19147,7 +19325,7 @@ function copyCanonicalSpecialists(cwd) {
|
|
|
19147
19325
|
skip("no specialist files found in package");
|
|
19148
19326
|
return;
|
|
19149
19327
|
}
|
|
19150
|
-
if (!
|
|
19328
|
+
if (!existsSync7(targetDir)) {
|
|
19151
19329
|
mkdirSync2(targetDir, { recursive: true });
|
|
19152
19330
|
}
|
|
19153
19331
|
let copied = 0;
|
|
@@ -19155,7 +19333,7 @@ function copyCanonicalSpecialists(cwd) {
|
|
|
19155
19333
|
for (const file of files) {
|
|
19156
19334
|
const src = join9(sourceDir, file);
|
|
19157
19335
|
const dest = join9(targetDir, file);
|
|
19158
|
-
if (
|
|
19336
|
+
if (existsSync7(dest)) {
|
|
19159
19337
|
skipped++;
|
|
19160
19338
|
} else {
|
|
19161
19339
|
copyFileSync(src, dest);
|
|
@@ -19169,19 +19347,19 @@ function copyCanonicalSpecialists(cwd) {
|
|
|
19169
19347
|
skip(`${skipped} specialist${skipped === 1 ? "" : "s"} already exist (not overwritten)`);
|
|
19170
19348
|
}
|
|
19171
19349
|
}
|
|
19172
|
-
function
|
|
19350
|
+
function installProjectHooks(cwd) {
|
|
19173
19351
|
const sourceDir = resolvePackagePath("hooks");
|
|
19174
19352
|
if (!sourceDir) {
|
|
19175
19353
|
skip("no canonical hooks found in package");
|
|
19176
19354
|
return;
|
|
19177
19355
|
}
|
|
19178
|
-
const targetDir = join9(cwd, ".
|
|
19356
|
+
const targetDir = join9(cwd, ".claude", "hooks");
|
|
19179
19357
|
const hooks = readdirSync2(sourceDir).filter((f) => f.endsWith(".mjs"));
|
|
19180
19358
|
if (hooks.length === 0) {
|
|
19181
19359
|
skip("no hook files found in package");
|
|
19182
19360
|
return;
|
|
19183
19361
|
}
|
|
19184
|
-
if (!
|
|
19362
|
+
if (!existsSync7(targetDir)) {
|
|
19185
19363
|
mkdirSync2(targetDir, { recursive: true });
|
|
19186
19364
|
}
|
|
19187
19365
|
let copied = 0;
|
|
@@ -19189,7 +19367,7 @@ function copyCanonicalHooks(cwd) {
|
|
|
19189
19367
|
for (const file of hooks) {
|
|
19190
19368
|
const src = join9(sourceDir, file);
|
|
19191
19369
|
const dest = join9(targetDir, file);
|
|
19192
|
-
if (
|
|
19370
|
+
if (existsSync7(dest)) {
|
|
19193
19371
|
skipped++;
|
|
19194
19372
|
} else {
|
|
19195
19373
|
copyFileSync(src, dest);
|
|
@@ -19197,16 +19375,16 @@ function copyCanonicalHooks(cwd) {
|
|
|
19197
19375
|
}
|
|
19198
19376
|
}
|
|
19199
19377
|
if (copied > 0) {
|
|
19200
|
-
ok(`
|
|
19378
|
+
ok(`installed ${copied} hook${copied === 1 ? "" : "s"} to .claude/hooks/`);
|
|
19201
19379
|
}
|
|
19202
19380
|
if (skipped > 0) {
|
|
19203
19381
|
skip(`${skipped} hook${skipped === 1 ? "" : "s"} already exist (not overwritten)`);
|
|
19204
19382
|
}
|
|
19205
19383
|
}
|
|
19206
|
-
function
|
|
19384
|
+
function ensureProjectHookWiring(cwd) {
|
|
19207
19385
|
const settingsPath = join9(cwd, ".claude", "settings.json");
|
|
19208
19386
|
const settingsDir = join9(cwd, ".claude");
|
|
19209
|
-
if (!
|
|
19387
|
+
if (!existsSync7(settingsDir)) {
|
|
19210
19388
|
mkdirSync2(settingsDir, { recursive: true });
|
|
19211
19389
|
}
|
|
19212
19390
|
const settings = loadJson(settingsPath, {});
|
|
@@ -19220,8 +19398,8 @@ function ensureProjectHooks(cwd) {
|
|
|
19220
19398
|
changed = true;
|
|
19221
19399
|
}
|
|
19222
19400
|
}
|
|
19223
|
-
addHook("UserPromptSubmit", "node .
|
|
19224
|
-
addHook("SessionStart", "node .
|
|
19401
|
+
addHook("UserPromptSubmit", "node .claude/hooks/specialists-complete.mjs");
|
|
19402
|
+
addHook("SessionStart", "node .claude/hooks/specialists-session-start.mjs");
|
|
19225
19403
|
if (changed) {
|
|
19226
19404
|
saveJson(settingsPath, settings);
|
|
19227
19405
|
ok("wired specialists hooks in .claude/settings.json");
|
|
@@ -19229,7 +19407,7 @@ function ensureProjectHooks(cwd) {
|
|
|
19229
19407
|
skip(".claude/settings.json already has specialists hooks");
|
|
19230
19408
|
}
|
|
19231
19409
|
}
|
|
19232
|
-
function
|
|
19410
|
+
function installProjectSkills(cwd) {
|
|
19233
19411
|
const sourceDir = resolvePackagePath("skills");
|
|
19234
19412
|
if (!sourceDir) {
|
|
19235
19413
|
skip("no canonical skills found in package");
|
|
@@ -19240,44 +19418,39 @@ function copyCanonicalSkills(cwd) {
|
|
|
19240
19418
|
skip("no skill directories found in package");
|
|
19241
19419
|
return;
|
|
19242
19420
|
}
|
|
19243
|
-
const
|
|
19244
|
-
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
let
|
|
19248
|
-
let
|
|
19249
|
-
for (const
|
|
19250
|
-
|
|
19251
|
-
|
|
19252
|
-
|
|
19253
|
-
|
|
19254
|
-
|
|
19255
|
-
|
|
19256
|
-
|
|
19421
|
+
const targetDirs = [
|
|
19422
|
+
join9(cwd, ".claude", "skills"),
|
|
19423
|
+
join9(cwd, ".pi", "skills")
|
|
19424
|
+
];
|
|
19425
|
+
let totalCopied = 0;
|
|
19426
|
+
let totalSkipped = 0;
|
|
19427
|
+
for (const targetDir of targetDirs) {
|
|
19428
|
+
if (!existsSync7(targetDir)) {
|
|
19429
|
+
mkdirSync2(targetDir, { recursive: true });
|
|
19430
|
+
}
|
|
19431
|
+
for (const skill of skills) {
|
|
19432
|
+
const src = join9(sourceDir, skill);
|
|
19433
|
+
const dest = join9(targetDir, skill);
|
|
19434
|
+
if (existsSync7(dest)) {
|
|
19435
|
+
totalSkipped++;
|
|
19436
|
+
} else {
|
|
19437
|
+
cpSync(src, dest, { recursive: true });
|
|
19438
|
+
totalCopied++;
|
|
19439
|
+
}
|
|
19257
19440
|
}
|
|
19258
19441
|
}
|
|
19259
|
-
if (
|
|
19260
|
-
ok(`
|
|
19442
|
+
if (totalCopied > 0) {
|
|
19443
|
+
ok(`installed ${skills.length} skill${skills.length === 1 ? "" : "s"} to .claude/skills/ and .pi/skills/`);
|
|
19261
19444
|
}
|
|
19262
|
-
if (
|
|
19263
|
-
skip(`${
|
|
19445
|
+
if (totalSkipped > 0) {
|
|
19446
|
+
skip(`${totalSkipped} skill location${totalSkipped === 1 ? "" : "s"} already exist (not overwritten)`);
|
|
19264
19447
|
}
|
|
19265
19448
|
}
|
|
19266
19449
|
function createUserDirs(cwd) {
|
|
19267
|
-
const
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
];
|
|
19272
|
-
let created = 0;
|
|
19273
|
-
for (const dir of userDirs) {
|
|
19274
|
-
if (!existsSync6(dir)) {
|
|
19275
|
-
mkdirSync2(dir, { recursive: true });
|
|
19276
|
-
created++;
|
|
19277
|
-
}
|
|
19278
|
-
}
|
|
19279
|
-
if (created > 0) {
|
|
19280
|
-
ok("created .specialists/user/ directories for custom assets");
|
|
19450
|
+
const userDir = join9(cwd, ".specialists", "user", "specialists");
|
|
19451
|
+
if (!existsSync7(userDir)) {
|
|
19452
|
+
mkdirSync2(userDir, { recursive: true });
|
|
19453
|
+
ok("created .specialists/user/specialists/ for custom specialists");
|
|
19281
19454
|
}
|
|
19282
19455
|
}
|
|
19283
19456
|
function createRuntimeDirs(cwd) {
|
|
@@ -19287,7 +19460,7 @@ function createRuntimeDirs(cwd) {
|
|
|
19287
19460
|
];
|
|
19288
19461
|
let created = 0;
|
|
19289
19462
|
for (const dir of runtimeDirs) {
|
|
19290
|
-
if (!
|
|
19463
|
+
if (!existsSync7(dir)) {
|
|
19291
19464
|
mkdirSync2(dir, { recursive: true });
|
|
19292
19465
|
created++;
|
|
19293
19466
|
}
|
|
@@ -19311,7 +19484,7 @@ function ensureProjectMcp(cwd) {
|
|
|
19311
19484
|
}
|
|
19312
19485
|
function ensureGitignore(cwd) {
|
|
19313
19486
|
const gitignorePath = join9(cwd, ".gitignore");
|
|
19314
|
-
const existing =
|
|
19487
|
+
const existing = existsSync7(gitignorePath) ? readFileSync3(gitignorePath, "utf-8") : "";
|
|
19315
19488
|
let added = 0;
|
|
19316
19489
|
const lines = existing.split(`
|
|
19317
19490
|
`);
|
|
@@ -19332,7 +19505,7 @@ function ensureGitignore(cwd) {
|
|
|
19332
19505
|
}
|
|
19333
19506
|
function ensureAgentsMd(cwd) {
|
|
19334
19507
|
const agentsPath = join9(cwd, "AGENTS.md");
|
|
19335
|
-
if (
|
|
19508
|
+
if (existsSync7(agentsPath)) {
|
|
19336
19509
|
const existing = readFileSync3(agentsPath, "utf-8");
|
|
19337
19510
|
if (existing.includes(AGENTS_MARKER)) {
|
|
19338
19511
|
skip("AGENTS.md already has Specialists section");
|
|
@@ -19350,40 +19523,42 @@ function ensureAgentsMd(cwd) {
|
|
|
19350
19523
|
async function run5() {
|
|
19351
19524
|
const cwd = process.cwd();
|
|
19352
19525
|
console.log(`
|
|
19353
|
-
${
|
|
19526
|
+
${bold4("specialists init")}
|
|
19354
19527
|
`);
|
|
19355
19528
|
copyCanonicalSpecialists(cwd);
|
|
19356
|
-
copyCanonicalHooks(cwd);
|
|
19357
|
-
copyCanonicalSkills(cwd);
|
|
19358
19529
|
createUserDirs(cwd);
|
|
19359
19530
|
createRuntimeDirs(cwd);
|
|
19360
19531
|
ensureGitignore(cwd);
|
|
19361
19532
|
ensureAgentsMd(cwd);
|
|
19362
19533
|
ensureProjectMcp(cwd);
|
|
19363
|
-
|
|
19534
|
+
installProjectHooks(cwd);
|
|
19535
|
+
ensureProjectHookWiring(cwd);
|
|
19536
|
+
installProjectSkills(cwd);
|
|
19364
19537
|
console.log(`
|
|
19365
|
-
${
|
|
19538
|
+
${bold4("Done!")}
|
|
19366
19539
|
`);
|
|
19367
|
-
console.log(` ${
|
|
19540
|
+
console.log(` ${dim4("Project-local installation:")}`);
|
|
19541
|
+
console.log(` .claude/hooks/ ${dim4("# hooks (Claude Code)")}`);
|
|
19542
|
+
console.log(` .claude/settings.json ${dim4("# hook wiring")}`);
|
|
19543
|
+
console.log(` .claude/skills/ ${dim4("# skills (Claude Code)")}`);
|
|
19544
|
+
console.log(` .pi/skills/ ${dim4("# skills (pi)")}`);
|
|
19545
|
+
console.log("");
|
|
19546
|
+
console.log(` ${dim4(".specialists/ structure:")}`);
|
|
19368
19547
|
console.log(` .specialists/`);
|
|
19369
|
-
console.log(` ├── default/
|
|
19370
|
-
console.log(` │
|
|
19371
|
-
console.log(`
|
|
19372
|
-
console.log(` │ └──
|
|
19373
|
-
console.log(` ├──
|
|
19374
|
-
console.log(`
|
|
19375
|
-
console.log(` │ ├── hooks/`);
|
|
19376
|
-
console.log(` │ └── skills/`);
|
|
19377
|
-
console.log(` ├── jobs/ ${dim3("# runtime (gitignored)")}`);
|
|
19378
|
-
console.log(` └── ready/ ${dim3("# runtime (gitignored)")}`);
|
|
19548
|
+
console.log(` ├── default/ ${dim4("# canonical specialists (from init)")}`);
|
|
19549
|
+
console.log(` │ └── specialists/`);
|
|
19550
|
+
console.log(` ├── user/ ${dim4("# your custom specialists")}`);
|
|
19551
|
+
console.log(` │ └── specialists/`);
|
|
19552
|
+
console.log(` ├── jobs/ ${dim4("# runtime (gitignored)")}`);
|
|
19553
|
+
console.log(` └── ready/ ${dim4("# runtime (gitignored)")}`);
|
|
19379
19554
|
console.log(`
|
|
19380
|
-
${
|
|
19381
|
-
console.log(` 1. Run ${
|
|
19382
|
-
console.log(` 2. Add custom specialists to ${
|
|
19383
|
-
console.log(` 3. Restart Claude Code to pick up changes
|
|
19555
|
+
${dim4("Next steps:")}`);
|
|
19556
|
+
console.log(` 1. Run ${yellow4("specialists list")} to see available specialists`);
|
|
19557
|
+
console.log(` 2. Add custom specialists to ${yellow4(".specialists/user/specialists/")}`);
|
|
19558
|
+
console.log(` 3. Restart Claude Code or pi to pick up changes
|
|
19384
19559
|
`);
|
|
19385
19560
|
}
|
|
19386
|
-
var
|
|
19561
|
+
var bold4 = (s) => `\x1B[1m${s}\x1B[0m`, green3 = (s) => `\x1B[32m${s}\x1B[0m`, yellow4 = (s) => `\x1B[33m${s}\x1B[0m`, dim4 = (s) => `\x1B[2m${s}\x1B[0m`, AGENTS_BLOCK, AGENTS_MARKER = "## Specialists", GITIGNORE_ENTRIES, MCP_FILE = ".mcp.json", MCP_SERVER_NAME = "specialists", MCP_SERVER_CONFIG;
|
|
19387
19562
|
var init_init = __esm(() => {
|
|
19388
19563
|
AGENTS_BLOCK = `
|
|
19389
19564
|
## Specialists
|
|
@@ -19399,13 +19574,126 @@ Add custom specialists to \`.specialists/user/specialists/\` to extend the defau
|
|
|
19399
19574
|
MCP_SERVER_CONFIG = { command: "specialists", args: [] };
|
|
19400
19575
|
});
|
|
19401
19576
|
|
|
19577
|
+
// src/cli/validate.ts
|
|
19578
|
+
var exports_validate = {};
|
|
19579
|
+
__export(exports_validate, {
|
|
19580
|
+
run: () => run6,
|
|
19581
|
+
parseArgs: () => parseArgs3,
|
|
19582
|
+
ArgParseError: () => ArgParseError2
|
|
19583
|
+
});
|
|
19584
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
19585
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
19586
|
+
import { join as join10 } from "node:path";
|
|
19587
|
+
function parseArgs3(argv) {
|
|
19588
|
+
const name = argv[0];
|
|
19589
|
+
if (!name || name.startsWith("--")) {
|
|
19590
|
+
throw new ArgParseError2("Usage: specialists validate <name> [--json]");
|
|
19591
|
+
}
|
|
19592
|
+
const json = argv.includes("--json");
|
|
19593
|
+
return { name, json };
|
|
19594
|
+
}
|
|
19595
|
+
function findSpecialistFile(name) {
|
|
19596
|
+
const scanDirs = [
|
|
19597
|
+
join10(process.cwd(), ".specialists", "user", "specialists"),
|
|
19598
|
+
join10(process.cwd(), ".specialists", "default", "specialists"),
|
|
19599
|
+
join10(process.cwd(), "specialists")
|
|
19600
|
+
];
|
|
19601
|
+
for (const dir of scanDirs) {
|
|
19602
|
+
const candidate = join10(dir, `${name}.specialist.yaml`);
|
|
19603
|
+
if (existsSync8(candidate)) {
|
|
19604
|
+
return candidate;
|
|
19605
|
+
}
|
|
19606
|
+
}
|
|
19607
|
+
return;
|
|
19608
|
+
}
|
|
19609
|
+
async function run6() {
|
|
19610
|
+
let args;
|
|
19611
|
+
try {
|
|
19612
|
+
args = parseArgs3(process.argv.slice(3));
|
|
19613
|
+
} catch (err) {
|
|
19614
|
+
if (err instanceof ArgParseError2) {
|
|
19615
|
+
console.error(`Error: ${err.message}`);
|
|
19616
|
+
process.exit(1);
|
|
19617
|
+
}
|
|
19618
|
+
throw err;
|
|
19619
|
+
}
|
|
19620
|
+
const { name, json } = args;
|
|
19621
|
+
const filePath = findSpecialistFile(name);
|
|
19622
|
+
if (!filePath) {
|
|
19623
|
+
if (json) {
|
|
19624
|
+
console.log(JSON.stringify({ valid: false, errors: [{ path: "name", message: `Specialist not found: ${name}`, code: "not_found" }] }));
|
|
19625
|
+
} else {
|
|
19626
|
+
console.error(`${red("✗")} Specialist not found: ${cyan3(name)}`);
|
|
19627
|
+
}
|
|
19628
|
+
process.exit(1);
|
|
19629
|
+
}
|
|
19630
|
+
let content;
|
|
19631
|
+
try {
|
|
19632
|
+
content = await readFile2(filePath, "utf-8");
|
|
19633
|
+
} catch (e) {
|
|
19634
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
19635
|
+
if (json) {
|
|
19636
|
+
console.log(JSON.stringify({ valid: false, errors: [{ path: "file", message: `Failed to read file: ${msg}`, code: "read_error" }] }));
|
|
19637
|
+
} else {
|
|
19638
|
+
console.error(`${red("✗")} Failed to read file: ${msg}`);
|
|
19639
|
+
}
|
|
19640
|
+
process.exit(1);
|
|
19641
|
+
}
|
|
19642
|
+
const result = await validateSpecialist(content);
|
|
19643
|
+
if (json) {
|
|
19644
|
+
console.log(JSON.stringify({
|
|
19645
|
+
valid: result.valid,
|
|
19646
|
+
errors: result.errors,
|
|
19647
|
+
warnings: result.warnings,
|
|
19648
|
+
file: filePath
|
|
19649
|
+
}, null, 2));
|
|
19650
|
+
process.exit(result.valid ? 0 : 1);
|
|
19651
|
+
}
|
|
19652
|
+
console.log(`
|
|
19653
|
+
${bold5("Validating")} ${cyan3(name)} ${dim5(`(${filePath})`)}
|
|
19654
|
+
`);
|
|
19655
|
+
if (result.valid) {
|
|
19656
|
+
console.log(`${green4("✓")} Schema validation passed
|
|
19657
|
+
`);
|
|
19658
|
+
} else {
|
|
19659
|
+
console.log(`${red("✗")} Schema validation failed:
|
|
19660
|
+
`);
|
|
19661
|
+
for (const error2 of result.errors) {
|
|
19662
|
+
console.log(` ${red("•")} ${error2.message}`);
|
|
19663
|
+
if (error2.path && error2.path !== "yaml") {
|
|
19664
|
+
console.log(` ${dim5(`path: ${error2.path}`)}`);
|
|
19665
|
+
}
|
|
19666
|
+
}
|
|
19667
|
+
console.log();
|
|
19668
|
+
}
|
|
19669
|
+
if (result.warnings.length > 0) {
|
|
19670
|
+
console.log(`${yellow5("Warnings")}:
|
|
19671
|
+
`);
|
|
19672
|
+
for (const warning of result.warnings) {
|
|
19673
|
+
console.log(` ${yellow5("⚠")} ${warning}`);
|
|
19674
|
+
}
|
|
19675
|
+
console.log();
|
|
19676
|
+
}
|
|
19677
|
+
process.exit(result.valid ? 0 : 1);
|
|
19678
|
+
}
|
|
19679
|
+
var bold5 = (s) => `\x1B[1m${s}\x1B[0m`, dim5 = (s) => `\x1B[2m${s}\x1B[0m`, green4 = (s) => `\x1B[32m${s}\x1B[0m`, red = (s) => `\x1B[31m${s}\x1B[0m`, yellow5 = (s) => `\x1B[33m${s}\x1B[0m`, cyan3 = (s) => `\x1B[36m${s}\x1B[0m`, ArgParseError2;
|
|
19680
|
+
var init_validate = __esm(() => {
|
|
19681
|
+
init_schema();
|
|
19682
|
+
ArgParseError2 = class ArgParseError2 extends Error {
|
|
19683
|
+
constructor(message) {
|
|
19684
|
+
super(message);
|
|
19685
|
+
this.name = "ArgParseError";
|
|
19686
|
+
}
|
|
19687
|
+
};
|
|
19688
|
+
});
|
|
19689
|
+
|
|
19402
19690
|
// src/cli/edit.ts
|
|
19403
19691
|
var exports_edit = {};
|
|
19404
19692
|
__export(exports_edit, {
|
|
19405
|
-
run: () =>
|
|
19693
|
+
run: () => run7
|
|
19406
19694
|
});
|
|
19407
19695
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
19408
|
-
function
|
|
19696
|
+
function parseArgs4(argv) {
|
|
19409
19697
|
const name = argv[0];
|
|
19410
19698
|
if (!name || name.startsWith("--")) {
|
|
19411
19699
|
console.error("Usage: specialists|sp edit <name> --<field> <value> [--dry-run]");
|
|
@@ -19424,8 +19712,8 @@ function parseArgs3(argv) {
|
|
|
19424
19712
|
}
|
|
19425
19713
|
if (token === "--scope") {
|
|
19426
19714
|
const v = argv[++i];
|
|
19427
|
-
if (v !== "
|
|
19428
|
-
console.error(`Error: --scope must be "
|
|
19715
|
+
if (v !== "default" && v !== "user") {
|
|
19716
|
+
console.error(`Error: --scope must be "default" or "user", got: "${v ?? ""}"`);
|
|
19429
19717
|
process.exit(1);
|
|
19430
19718
|
}
|
|
19431
19719
|
scope = v;
|
|
@@ -19467,8 +19755,8 @@ function setIn(doc2, path, value) {
|
|
|
19467
19755
|
node.set(leaf, value);
|
|
19468
19756
|
}
|
|
19469
19757
|
}
|
|
19470
|
-
async function
|
|
19471
|
-
const args =
|
|
19758
|
+
async function run7() {
|
|
19759
|
+
const args = parseArgs4(process.argv.slice(3));
|
|
19472
19760
|
const { name, field, value, dryRun, scope } = args;
|
|
19473
19761
|
const loader = new SpecialistLoader;
|
|
19474
19762
|
const all = await loader.list();
|
|
@@ -19476,7 +19764,7 @@ async function run6() {
|
|
|
19476
19764
|
if (!match) {
|
|
19477
19765
|
const hint = scope ? ` (scope: ${scope})` : "";
|
|
19478
19766
|
console.error(`Error: specialist "${name}" not found${hint}`);
|
|
19479
|
-
console.error(` Run ${
|
|
19767
|
+
console.error(` Run ${yellow6("specialists list")} to see available specialists`);
|
|
19480
19768
|
process.exit(1);
|
|
19481
19769
|
}
|
|
19482
19770
|
const raw = readFileSync4(match.filePath, "utf-8");
|
|
@@ -19492,10 +19780,10 @@ async function run6() {
|
|
|
19492
19780
|
const updated = doc2.toString();
|
|
19493
19781
|
if (dryRun) {
|
|
19494
19782
|
console.log(`
|
|
19495
|
-
${
|
|
19783
|
+
${bold6(`[dry-run] ${match.filePath}`)}
|
|
19496
19784
|
`);
|
|
19497
|
-
console.log(
|
|
19498
|
-
console.log(
|
|
19785
|
+
console.log(dim6("--- current"));
|
|
19786
|
+
console.log(dim6(`+++ updated`));
|
|
19499
19787
|
const oldLines = raw.split(`
|
|
19500
19788
|
`);
|
|
19501
19789
|
const newLines = updated.split(`
|
|
@@ -19503,8 +19791,8 @@ ${bold4(`[dry-run] ${match.filePath}`)}
|
|
|
19503
19791
|
newLines.forEach((line, i) => {
|
|
19504
19792
|
if (line !== oldLines[i]) {
|
|
19505
19793
|
if (oldLines[i] !== undefined)
|
|
19506
|
-
console.log(
|
|
19507
|
-
console.log(
|
|
19794
|
+
console.log(dim6(`- ${oldLines[i]}`));
|
|
19795
|
+
console.log(green5(`+ ${line}`));
|
|
19508
19796
|
}
|
|
19509
19797
|
});
|
|
19510
19798
|
console.log();
|
|
@@ -19512,9 +19800,9 @@ ${bold4(`[dry-run] ${match.filePath}`)}
|
|
|
19512
19800
|
}
|
|
19513
19801
|
writeFileSync5(match.filePath, updated, "utf-8");
|
|
19514
19802
|
const displayValue = field === "tags" ? `[${typedValue.join(", ")}]` : String(typedValue);
|
|
19515
|
-
console.log(`${
|
|
19803
|
+
console.log(`${green5("✓")} ${bold6(name)}: ${yellow6(field)} = ${displayValue}` + dim6(` (${match.filePath})`));
|
|
19516
19804
|
}
|
|
19517
|
-
var
|
|
19805
|
+
var bold6 = (s) => `\x1B[1m${s}\x1B[0m`, green5 = (s) => `\x1B[32m${s}\x1B[0m`, yellow6 = (s) => `\x1B[33m${s}\x1B[0m`, dim6 = (s) => `\x1B[2m${s}\x1B[0m`, FIELD_MAP, VALID_PERMISSIONS;
|
|
19518
19806
|
var init_edit = __esm(() => {
|
|
19519
19807
|
init_dist();
|
|
19520
19808
|
init_loader();
|
|
@@ -19532,10 +19820,10 @@ var init_edit = __esm(() => {
|
|
|
19532
19820
|
// src/cli/run.ts
|
|
19533
19821
|
var exports_run = {};
|
|
19534
19822
|
__export(exports_run, {
|
|
19535
|
-
run: () =>
|
|
19823
|
+
run: () => run8
|
|
19536
19824
|
});
|
|
19537
|
-
import { join as
|
|
19538
|
-
async function
|
|
19825
|
+
import { join as join11 } from "node:path";
|
|
19826
|
+
async function parseArgs5(argv) {
|
|
19539
19827
|
const name = argv[0];
|
|
19540
19828
|
if (!name || name.startsWith("--")) {
|
|
19541
19829
|
console.error('Usage: specialists|sp run <name> [--prompt "..."] [--bead <id>] [--context-depth <n>] [--model <model>] [--no-beads] [--keep-alive]');
|
|
@@ -19594,11 +19882,11 @@ async function parseArgs4(argv) {
|
|
|
19594
19882
|
}
|
|
19595
19883
|
return { name, prompt, beadId, model, noBeads, keepAlive, contextDepth };
|
|
19596
19884
|
}
|
|
19597
|
-
async function
|
|
19598
|
-
const args = await
|
|
19885
|
+
async function run8() {
|
|
19886
|
+
const args = await parseArgs5(process.argv.slice(3));
|
|
19599
19887
|
const loader = new SpecialistLoader;
|
|
19600
19888
|
const circuitBreaker = new CircuitBreaker;
|
|
19601
|
-
const hooks = new HookEmitter({ tracePath:
|
|
19889
|
+
const hooks = new HookEmitter({ tracePath: join11(process.cwd(), ".specialists", "trace.jsonl") });
|
|
19602
19890
|
const beadsClient = args.noBeads ? undefined : new BeadsClient;
|
|
19603
19891
|
const beadReader = beadsClient ?? new BeadsClient;
|
|
19604
19892
|
let prompt = args.prompt;
|
|
@@ -19610,7 +19898,7 @@ async function run7() {
|
|
|
19610
19898
|
}
|
|
19611
19899
|
const blockers = args.contextDepth > 0 ? beadReader.getCompletedBlockers(args.beadId, args.contextDepth) : [];
|
|
19612
19900
|
if (blockers.length > 0) {
|
|
19613
|
-
process.stderr.write(
|
|
19901
|
+
process.stderr.write(dim7(`
|
|
19614
19902
|
[context: ${blockers.length} completed dep${blockers.length > 1 ? "s" : ""} injected]
|
|
19615
19903
|
`));
|
|
19616
19904
|
}
|
|
@@ -19627,7 +19915,7 @@ async function run7() {
|
|
|
19627
19915
|
circuitBreaker,
|
|
19628
19916
|
beadsClient
|
|
19629
19917
|
});
|
|
19630
|
-
const jobsDir =
|
|
19918
|
+
const jobsDir = join11(process.cwd(), ".specialists", "jobs");
|
|
19631
19919
|
const supervisor = new Supervisor({
|
|
19632
19920
|
runner,
|
|
19633
19921
|
runOptions: {
|
|
@@ -19641,13 +19929,22 @@ async function run7() {
|
|
|
19641
19929
|
jobsDir,
|
|
19642
19930
|
beadsClient,
|
|
19643
19931
|
onProgress: (delta) => process.stdout.write(delta),
|
|
19644
|
-
onMeta: (meta) => process.stderr.write(
|
|
19932
|
+
onMeta: (meta) => process.stderr.write(dim7(`
|
|
19645
19933
|
[${meta.backend} / ${meta.model}]
|
|
19646
19934
|
|
|
19935
|
+
`)),
|
|
19936
|
+
onJobStarted: ({ id }) => process.stderr.write(dim7(`[job started: ${id}]
|
|
19647
19937
|
`))
|
|
19648
19938
|
});
|
|
19939
|
+
try {
|
|
19940
|
+
await loader.get(args.name);
|
|
19941
|
+
} catch (err) {
|
|
19942
|
+
process.stderr.write(`Error: ${err?.message ?? err}
|
|
19943
|
+
`);
|
|
19944
|
+
process.exit(1);
|
|
19945
|
+
}
|
|
19649
19946
|
process.stderr.write(`
|
|
19650
|
-
${
|
|
19947
|
+
${bold7(`Running ${cyan4(args.name)}`)}
|
|
19651
19948
|
|
|
19652
19949
|
`);
|
|
19653
19950
|
let jobId;
|
|
@@ -19664,17 +19961,18 @@ ${bold5(`Running ${cyan3(args.name)}`)}
|
|
|
19664
19961
|
`job ${jobId}`,
|
|
19665
19962
|
status?.bead_id ? `bead ${status.bead_id}` : "",
|
|
19666
19963
|
`${secs.toFixed(1)}s`,
|
|
19667
|
-
status?.model ?
|
|
19964
|
+
status?.model ? dim7(`${status.backend}/${status.model}`) : ""
|
|
19668
19965
|
].filter(Boolean).join(" ");
|
|
19669
19966
|
process.stderr.write(`
|
|
19670
|
-
${
|
|
19967
|
+
${green6("✓")} ${footer}
|
|
19671
19968
|
|
|
19672
19969
|
`);
|
|
19673
|
-
process.stderr.write(
|
|
19970
|
+
process.stderr.write(dim7(`Poll: specialists poll ${jobId} --json
|
|
19674
19971
|
|
|
19675
19972
|
`));
|
|
19973
|
+
process.exit(0);
|
|
19676
19974
|
}
|
|
19677
|
-
var
|
|
19975
|
+
var bold7 = (s) => `\x1B[1m${s}\x1B[0m`, dim7 = (s) => `\x1B[2m${s}\x1B[0m`, green6 = (s) => `\x1B[32m${s}\x1B[0m`, cyan4 = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
19678
19976
|
var init_run = __esm(() => {
|
|
19679
19977
|
init_loader();
|
|
19680
19978
|
init_runner();
|
|
@@ -19721,9 +20019,9 @@ class JobColorMap {
|
|
|
19721
20019
|
}
|
|
19722
20020
|
}
|
|
19723
20021
|
function formatEventLine(event, options) {
|
|
19724
|
-
const ts =
|
|
19725
|
-
const label = options.colorize(
|
|
19726
|
-
const prefix = `${options.colorize(`[${options.jobId}]`)} ${options.specialist}${options.beadId ? ` ${
|
|
20022
|
+
const ts = dim8(formatTime(event.t));
|
|
20023
|
+
const label = options.colorize(bold8(getEventLabel(event.type).padEnd(5)));
|
|
20024
|
+
const prefix = `${options.colorize(`[${options.jobId}]`)} ${options.specialist}${options.beadId ? ` ${dim8(`[${options.beadId}]`)}` : ""}`;
|
|
19727
20025
|
const detailParts = [];
|
|
19728
20026
|
if (event.type === "meta") {
|
|
19729
20027
|
detailParts.push(`model=${event.model}`);
|
|
@@ -19752,19 +20050,26 @@ function formatEventLine(event, options) {
|
|
|
19752
20050
|
detailParts.push("kind=assistant");
|
|
19753
20051
|
} else if (event.type === "thinking") {
|
|
19754
20052
|
detailParts.push("kind=model");
|
|
20053
|
+
} else if (event.type === "message") {
|
|
20054
|
+
detailParts.push(`phase=${event.phase}`);
|
|
20055
|
+
detailParts.push(`role=${event.role}`);
|
|
20056
|
+
} else if (event.type === "turn") {
|
|
20057
|
+
detailParts.push(`phase=${event.phase}`);
|
|
19755
20058
|
}
|
|
19756
|
-
const detail = detailParts.length > 0 ?
|
|
20059
|
+
const detail = detailParts.length > 0 ? dim8(detailParts.join(" ")) : "";
|
|
19757
20060
|
return `${ts} ${prefix} ${label}${detail ? ` ${detail}` : ""}`.trimEnd();
|
|
19758
20061
|
}
|
|
19759
|
-
var
|
|
20062
|
+
var dim8 = (s) => `\x1B[2m${s}\x1B[0m`, bold8 = (s) => `\x1B[1m${s}\x1B[0m`, cyan5 = (s) => `\x1B[36m${s}\x1B[0m`, yellow7 = (s) => `\x1B[33m${s}\x1B[0m`, red2 = (s) => `\x1B[31m${s}\x1B[0m`, green7 = (s) => `\x1B[32m${s}\x1B[0m`, blue = (s) => `\x1B[34m${s}\x1B[0m`, magenta = (s) => `\x1B[35m${s}\x1B[0m`, JOB_COLORS, EVENT_LABELS;
|
|
19760
20063
|
var init_format_helpers = __esm(() => {
|
|
19761
|
-
JOB_COLORS = [
|
|
20064
|
+
JOB_COLORS = [cyan5, yellow7, magenta, green7, blue, red2];
|
|
19762
20065
|
EVENT_LABELS = {
|
|
19763
20066
|
run_start: "START",
|
|
19764
20067
|
meta: "META",
|
|
19765
20068
|
thinking: "THINK",
|
|
19766
20069
|
tool: "TOOL",
|
|
19767
20070
|
text: "TEXT",
|
|
20071
|
+
message: "MSG",
|
|
20072
|
+
turn: "TURN",
|
|
19768
20073
|
run_complete: "DONE",
|
|
19769
20074
|
done: "DONE",
|
|
19770
20075
|
agent_end: "DONE",
|
|
@@ -19775,27 +20080,27 @@ var init_format_helpers = __esm(() => {
|
|
|
19775
20080
|
// src/cli/status.ts
|
|
19776
20081
|
var exports_status = {};
|
|
19777
20082
|
__export(exports_status, {
|
|
19778
|
-
run: () =>
|
|
20083
|
+
run: () => run9
|
|
19779
20084
|
});
|
|
19780
20085
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
19781
|
-
import { existsSync as
|
|
19782
|
-
import { join as
|
|
20086
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
20087
|
+
import { join as join12 } from "node:path";
|
|
19783
20088
|
function ok2(msg) {
|
|
19784
|
-
console.log(` ${
|
|
20089
|
+
console.log(` ${green7("✓")} ${msg}`);
|
|
19785
20090
|
}
|
|
19786
20091
|
function warn(msg) {
|
|
19787
|
-
console.log(` ${
|
|
20092
|
+
console.log(` ${yellow7("○")} ${msg}`);
|
|
19788
20093
|
}
|
|
19789
20094
|
function fail(msg) {
|
|
19790
|
-
console.log(` ${
|
|
20095
|
+
console.log(` ${red2("✗")} ${msg}`);
|
|
19791
20096
|
}
|
|
19792
20097
|
function info(msg) {
|
|
19793
|
-
console.log(` ${
|
|
20098
|
+
console.log(` ${dim8(msg)}`);
|
|
19794
20099
|
}
|
|
19795
20100
|
function section(label) {
|
|
19796
20101
|
const line = "─".repeat(Math.max(0, 38 - label.length));
|
|
19797
20102
|
console.log(`
|
|
19798
|
-
${
|
|
20103
|
+
${bold8(`── ${label} ${line}`)}`);
|
|
19799
20104
|
}
|
|
19800
20105
|
function cmd(bin, args) {
|
|
19801
20106
|
const r = spawnSync6(bin, args, {
|
|
@@ -19818,18 +20123,18 @@ function formatElapsed2(s) {
|
|
|
19818
20123
|
function statusColor(status) {
|
|
19819
20124
|
switch (status) {
|
|
19820
20125
|
case "running":
|
|
19821
|
-
return
|
|
20126
|
+
return cyan5(status);
|
|
19822
20127
|
case "done":
|
|
19823
|
-
return
|
|
20128
|
+
return green7(status);
|
|
19824
20129
|
case "error":
|
|
19825
|
-
return
|
|
20130
|
+
return red2(status);
|
|
19826
20131
|
case "starting":
|
|
19827
|
-
return
|
|
20132
|
+
return yellow7(status);
|
|
19828
20133
|
default:
|
|
19829
20134
|
return status;
|
|
19830
20135
|
}
|
|
19831
20136
|
}
|
|
19832
|
-
async function
|
|
20137
|
+
async function run9() {
|
|
19833
20138
|
const argv = process.argv.slice(3);
|
|
19834
20139
|
const jsonMode = argv.includes("--json");
|
|
19835
20140
|
const loader = new SpecialistLoader;
|
|
@@ -19841,11 +20146,11 @@ async function run8() {
|
|
|
19841
20146
|
`).slice(1).map((line) => line.split(/\s+/)[0]).filter(Boolean)) : new Set;
|
|
19842
20147
|
const bdInstalled = isInstalled("bd");
|
|
19843
20148
|
const bdVersion = bdInstalled ? cmd("bd", ["--version"]) : null;
|
|
19844
|
-
const beadsPresent =
|
|
20149
|
+
const beadsPresent = existsSync9(join12(process.cwd(), ".beads"));
|
|
19845
20150
|
const specialistsBin = cmd("which", ["specialists"]);
|
|
19846
|
-
const jobsDir =
|
|
20151
|
+
const jobsDir = join12(process.cwd(), ".specialists", "jobs");
|
|
19847
20152
|
let jobs = [];
|
|
19848
|
-
if (
|
|
20153
|
+
if (existsSync9(jobsDir)) {
|
|
19849
20154
|
const supervisor = new Supervisor({
|
|
19850
20155
|
runner: null,
|
|
19851
20156
|
runOptions: null,
|
|
@@ -19896,60 +20201,62 @@ async function run8() {
|
|
|
19896
20201
|
return;
|
|
19897
20202
|
}
|
|
19898
20203
|
console.log(`
|
|
19899
|
-
${
|
|
20204
|
+
${bold8("specialists status")}
|
|
19900
20205
|
`);
|
|
19901
20206
|
section("Specialists");
|
|
19902
20207
|
if (allSpecialists.length === 0) {
|
|
19903
|
-
warn(`no specialists found — run ${
|
|
20208
|
+
warn(`no specialists found — run ${yellow7("specialists init")} to scaffold`);
|
|
19904
20209
|
} else {
|
|
19905
20210
|
const byScope = allSpecialists.reduce((acc, s) => {
|
|
19906
20211
|
acc[s.scope] = (acc[s.scope] ?? 0) + 1;
|
|
19907
20212
|
return acc;
|
|
19908
20213
|
}, {});
|
|
19909
20214
|
const scopeSummary = Object.entries(byScope).map(([scope, n]) => `${n} ${scope}`).join(", ");
|
|
19910
|
-
ok2(`${allSpecialists.length} found ${
|
|
20215
|
+
ok2(`${allSpecialists.length} found ${dim8(`(${scopeSummary})`)}`);
|
|
19911
20216
|
for (const s of allSpecialists) {
|
|
19912
20217
|
const staleness = stalenessMap[s.name];
|
|
19913
20218
|
if (staleness === "AGED") {
|
|
19914
|
-
warn(`${s.name} ${
|
|
20219
|
+
warn(`${s.name} ${red2("AGED")} ${dim8(s.scope)}`);
|
|
19915
20220
|
} else if (staleness === "STALE") {
|
|
19916
|
-
warn(`${s.name} ${
|
|
20221
|
+
warn(`${s.name} ${yellow7("STALE")} ${dim8(s.scope)}`);
|
|
19917
20222
|
}
|
|
19918
20223
|
}
|
|
19919
20224
|
}
|
|
19920
20225
|
section("pi (coding agent runtime)");
|
|
19921
20226
|
if (!piInstalled) {
|
|
19922
|
-
fail(`pi not installed — install ${
|
|
20227
|
+
fail(`pi not installed — install ${yellow7("pi")} first`);
|
|
19923
20228
|
} else {
|
|
19924
20229
|
const vStr = piVersion?.ok ? `v${piVersion.stdout}` : "unknown version";
|
|
19925
|
-
const pStr = piProviders.size > 0 ? `${piProviders.size} provider${piProviders.size > 1 ? "s" : ""} active ${
|
|
20230
|
+
const pStr = piProviders.size > 0 ? `${piProviders.size} provider${piProviders.size > 1 ? "s" : ""} active ${dim8(`(${[...piProviders].join(", ")})`)} ` : yellow7("no providers configured — run pi config");
|
|
19926
20231
|
ok2(`${vStr} — ${pStr}`);
|
|
19927
20232
|
}
|
|
19928
20233
|
section("beads (issue tracker)");
|
|
19929
20234
|
if (!bdInstalled) {
|
|
19930
|
-
fail(`bd not installed — install ${
|
|
20235
|
+
fail(`bd not installed — install ${yellow7("bd")} first`);
|
|
19931
20236
|
} else {
|
|
19932
|
-
ok2(`bd installed${bdVersion?.ok ? ` ${
|
|
20237
|
+
ok2(`bd installed${bdVersion?.ok ? ` ${dim8(bdVersion.stdout)}` : ""}`);
|
|
19933
20238
|
if (beadsPresent) {
|
|
19934
20239
|
ok2(".beads/ present in project");
|
|
19935
20240
|
} else {
|
|
19936
|
-
warn(`.beads/ not found — run ${
|
|
20241
|
+
warn(`.beads/ not found — run ${yellow7("bd init")} to enable issue tracking`);
|
|
19937
20242
|
}
|
|
19938
20243
|
}
|
|
19939
20244
|
section("MCP");
|
|
19940
20245
|
if (!specialistsBin.ok) {
|
|
19941
|
-
fail(`specialists not installed globally — run ${
|
|
20246
|
+
fail(`specialists not installed globally — run ${yellow7("npm install -g @jaggerxtrm/specialists")}`);
|
|
19942
20247
|
} else {
|
|
19943
|
-
ok2(`specialists binary installed ${
|
|
20248
|
+
ok2(`specialists binary installed ${dim8(specialistsBin.stdout)}`);
|
|
19944
20249
|
info(`verify registration: claude mcp get specialists`);
|
|
19945
20250
|
info(`re-register: specialists install`);
|
|
19946
20251
|
}
|
|
19947
|
-
|
|
19948
|
-
|
|
20252
|
+
section("Active Jobs");
|
|
20253
|
+
if (jobs.length === 0) {
|
|
20254
|
+
info(" (none)");
|
|
20255
|
+
} else {
|
|
19949
20256
|
for (const job of jobs) {
|
|
19950
20257
|
const elapsed = formatElapsed2(job);
|
|
19951
|
-
const detail = job.status === "error" ?
|
|
19952
|
-
console.log(` ${
|
|
20258
|
+
const detail = job.status === "error" ? red2(job.error?.slice(0, 40) ?? "error") : job.current_tool ? dim8(`tool: ${job.current_tool}`) : dim8(job.current_event ?? "");
|
|
20259
|
+
console.log(` ${dim8(job.id)} ${job.specialist.padEnd(20)} ${statusColor(job.status).padEnd(7)} ${elapsed.padStart(6)} ${detail}`);
|
|
19953
20260
|
}
|
|
19954
20261
|
}
|
|
19955
20262
|
console.log();
|
|
@@ -19963,51 +20270,57 @@ var init_status = __esm(() => {
|
|
|
19963
20270
|
// src/cli/result.ts
|
|
19964
20271
|
var exports_result = {};
|
|
19965
20272
|
__export(exports_result, {
|
|
19966
|
-
run: () =>
|
|
20273
|
+
run: () => run10
|
|
19967
20274
|
});
|
|
19968
|
-
import { existsSync as
|
|
19969
|
-
import { join as
|
|
19970
|
-
async function
|
|
20275
|
+
import { existsSync as existsSync10, readFileSync as readFileSync5 } from "node:fs";
|
|
20276
|
+
import { join as join13 } from "node:path";
|
|
20277
|
+
async function run10() {
|
|
19971
20278
|
const jobId = process.argv[3];
|
|
19972
20279
|
if (!jobId) {
|
|
19973
20280
|
console.error("Usage: specialists|sp result <job-id>");
|
|
19974
20281
|
process.exit(1);
|
|
19975
20282
|
}
|
|
19976
|
-
const jobsDir =
|
|
20283
|
+
const jobsDir = join13(process.cwd(), ".specialists", "jobs");
|
|
19977
20284
|
const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
|
|
19978
20285
|
const status = supervisor.readStatus(jobId);
|
|
19979
20286
|
if (!status) {
|
|
19980
20287
|
console.error(`No job found: ${jobId}`);
|
|
19981
20288
|
process.exit(1);
|
|
19982
20289
|
}
|
|
20290
|
+
const resultPath = join13(jobsDir, jobId, "result.txt");
|
|
19983
20291
|
if (status.status === "running" || status.status === "starting") {
|
|
19984
|
-
|
|
20292
|
+
if (!existsSync10(resultPath)) {
|
|
20293
|
+
process.stderr.write(`${dim9(`Job ${jobId} is still ${status.status}. Use 'specialists feed --job ${jobId}' to follow.`)}
|
|
19985
20294
|
`);
|
|
19986
|
-
|
|
20295
|
+
process.exit(1);
|
|
20296
|
+
}
|
|
20297
|
+
process.stderr.write(`${dim9(`Job ${jobId} is currently ${status.status}. Showing last completed output while it continues.`)}
|
|
20298
|
+
`);
|
|
20299
|
+
process.stdout.write(readFileSync5(resultPath, "utf-8"));
|
|
20300
|
+
return;
|
|
19987
20301
|
}
|
|
19988
20302
|
if (status.status === "error") {
|
|
19989
|
-
process.stderr.write(`${
|
|
20303
|
+
process.stderr.write(`${red3(`Job ${jobId} failed:`)} ${status.error ?? "unknown error"}
|
|
19990
20304
|
`);
|
|
19991
20305
|
process.exit(1);
|
|
19992
20306
|
}
|
|
19993
|
-
|
|
19994
|
-
if (!existsSync8(resultPath)) {
|
|
20307
|
+
if (!existsSync10(resultPath)) {
|
|
19995
20308
|
console.error(`Result file not found for job ${jobId}`);
|
|
19996
20309
|
process.exit(1);
|
|
19997
20310
|
}
|
|
19998
20311
|
process.stdout.write(readFileSync5(resultPath, "utf-8"));
|
|
19999
20312
|
}
|
|
20000
|
-
var
|
|
20313
|
+
var dim9 = (s) => `\x1B[2m${s}\x1B[0m`, red3 = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
20001
20314
|
var init_result = __esm(() => {
|
|
20002
20315
|
init_supervisor();
|
|
20003
20316
|
});
|
|
20004
20317
|
|
|
20005
20318
|
// src/specialist/timeline-query.ts
|
|
20006
|
-
import { existsSync as
|
|
20007
|
-
import { join as
|
|
20319
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "node:fs";
|
|
20320
|
+
import { join as join14 } from "node:path";
|
|
20008
20321
|
function readJobEvents(jobDir) {
|
|
20009
|
-
const eventsPath =
|
|
20010
|
-
if (!
|
|
20322
|
+
const eventsPath = join14(jobDir, "events.jsonl");
|
|
20323
|
+
if (!existsSync11(eventsPath))
|
|
20011
20324
|
return [];
|
|
20012
20325
|
const content = readFileSync6(eventsPath, "utf-8");
|
|
20013
20326
|
const lines = content.split(`
|
|
@@ -20022,15 +20335,15 @@ function readJobEvents(jobDir) {
|
|
|
20022
20335
|
return events;
|
|
20023
20336
|
}
|
|
20024
20337
|
function readJobEventsById(jobsDir, jobId) {
|
|
20025
|
-
return readJobEvents(
|
|
20338
|
+
return readJobEvents(join14(jobsDir, jobId));
|
|
20026
20339
|
}
|
|
20027
20340
|
function readAllJobEvents(jobsDir) {
|
|
20028
|
-
if (!
|
|
20341
|
+
if (!existsSync11(jobsDir))
|
|
20029
20342
|
return [];
|
|
20030
20343
|
const batches = [];
|
|
20031
20344
|
const entries = readdirSync3(jobsDir);
|
|
20032
20345
|
for (const entry of entries) {
|
|
20033
|
-
const jobDir =
|
|
20346
|
+
const jobDir = join14(jobsDir, entry);
|
|
20034
20347
|
try {
|
|
20035
20348
|
const stat2 = __require("node:fs").statSync(jobDir);
|
|
20036
20349
|
if (!stat2.isDirectory())
|
|
@@ -20039,10 +20352,10 @@ function readAllJobEvents(jobsDir) {
|
|
|
20039
20352
|
continue;
|
|
20040
20353
|
}
|
|
20041
20354
|
const jobId = entry;
|
|
20042
|
-
const statusPath =
|
|
20355
|
+
const statusPath = join14(jobDir, "status.json");
|
|
20043
20356
|
let specialist = "unknown";
|
|
20044
20357
|
let beadId;
|
|
20045
|
-
if (
|
|
20358
|
+
if (existsSync11(statusPath)) {
|
|
20046
20359
|
try {
|
|
20047
20360
|
const status = JSON.parse(readFileSync6(statusPath, "utf-8"));
|
|
20048
20361
|
specialist = status.specialist ?? "unknown";
|
|
@@ -20105,10 +20418,10 @@ var init_timeline_query = __esm(() => {
|
|
|
20105
20418
|
// src/cli/feed.ts
|
|
20106
20419
|
var exports_feed = {};
|
|
20107
20420
|
__export(exports_feed, {
|
|
20108
|
-
run: () =>
|
|
20421
|
+
run: () => run11
|
|
20109
20422
|
});
|
|
20110
|
-
import { existsSync as
|
|
20111
|
-
import { join as
|
|
20423
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
20424
|
+
import { join as join15 } from "node:path";
|
|
20112
20425
|
function getHumanEventKey(event) {
|
|
20113
20426
|
switch (event.type) {
|
|
20114
20427
|
case "meta":
|
|
@@ -20119,6 +20432,10 @@ function getHumanEventKey(event) {
|
|
|
20119
20432
|
return "text";
|
|
20120
20433
|
case "thinking":
|
|
20121
20434
|
return "thinking";
|
|
20435
|
+
case "message":
|
|
20436
|
+
return `message:${event.role}:${event.phase}`;
|
|
20437
|
+
case "turn":
|
|
20438
|
+
return `turn:${event.phase}`;
|
|
20122
20439
|
case "run_start":
|
|
20123
20440
|
return `run_start:${event.specialist}:${event.bead_id ?? ""}`;
|
|
20124
20441
|
case "run_complete":
|
|
@@ -20156,7 +20473,7 @@ function parseSince(value) {
|
|
|
20156
20473
|
}
|
|
20157
20474
|
return;
|
|
20158
20475
|
}
|
|
20159
|
-
function
|
|
20476
|
+
function parseArgs6(argv) {
|
|
20160
20477
|
let jobId;
|
|
20161
20478
|
let specialist;
|
|
20162
20479
|
let since;
|
|
@@ -20201,7 +20518,7 @@ function parseArgs5(argv) {
|
|
|
20201
20518
|
function printSnapshot(merged, options) {
|
|
20202
20519
|
if (merged.length === 0) {
|
|
20203
20520
|
if (!options.json)
|
|
20204
|
-
console.log(
|
|
20521
|
+
console.log(dim8("No events found."));
|
|
20205
20522
|
return;
|
|
20206
20523
|
}
|
|
20207
20524
|
const colorMap = new JobColorMap;
|
|
@@ -20247,13 +20564,13 @@ async function followMerged(jobsDir, options) {
|
|
|
20247
20564
|
const initialBatchCount = filteredBatches().length;
|
|
20248
20565
|
if (!options.forever && initialBatchCount > 0 && completedJobs.size === initialBatchCount) {
|
|
20249
20566
|
if (!options.json) {
|
|
20250
|
-
process.stderr.write(
|
|
20567
|
+
process.stderr.write(dim8(`All jobs complete.
|
|
20251
20568
|
`));
|
|
20252
20569
|
}
|
|
20253
20570
|
return;
|
|
20254
20571
|
}
|
|
20255
20572
|
if (!options.json) {
|
|
20256
|
-
process.stderr.write(
|
|
20573
|
+
process.stderr.write(dim8(`Following... (Ctrl+C to stop)
|
|
20257
20574
|
`));
|
|
20258
20575
|
}
|
|
20259
20576
|
const lastPrintedEventKey = new Map;
|
|
@@ -20300,11 +20617,37 @@ async function followMerged(jobsDir, options) {
|
|
|
20300
20617
|
}, 500);
|
|
20301
20618
|
});
|
|
20302
20619
|
}
|
|
20303
|
-
|
|
20304
|
-
|
|
20305
|
-
|
|
20306
|
-
|
|
20307
|
-
|
|
20620
|
+
function showUsage() {
|
|
20621
|
+
console.log(`Usage: specialists feed <job-id> [options]
|
|
20622
|
+
specialists feed -f [--forever]
|
|
20623
|
+
|
|
20624
|
+
Read background job events.
|
|
20625
|
+
|
|
20626
|
+
Modes:
|
|
20627
|
+
specialists feed <job-id> Replay events for one job
|
|
20628
|
+
specialists feed <job-id> -f Follow one job until completion
|
|
20629
|
+
specialists feed -f Follow all jobs globally
|
|
20630
|
+
|
|
20631
|
+
Options:
|
|
20632
|
+
-f, --follow Follow live updates
|
|
20633
|
+
--forever Keep following in global mode even when all jobs complete
|
|
20634
|
+
|
|
20635
|
+
Examples:
|
|
20636
|
+
specialists feed 49adda
|
|
20637
|
+
specialists feed 49adda --follow
|
|
20638
|
+
specialists feed -f
|
|
20639
|
+
specialists feed -f --forever
|
|
20640
|
+
`);
|
|
20641
|
+
}
|
|
20642
|
+
async function run11() {
|
|
20643
|
+
const options = parseArgs6(process.argv.slice(3));
|
|
20644
|
+
if (!options.jobId && !options.follow) {
|
|
20645
|
+
showUsage();
|
|
20646
|
+
process.exit(1);
|
|
20647
|
+
}
|
|
20648
|
+
const jobsDir = join15(process.cwd(), ".specialists", "jobs");
|
|
20649
|
+
if (!existsSync12(jobsDir)) {
|
|
20650
|
+
console.log(dim8("No jobs directory found."));
|
|
20308
20651
|
return;
|
|
20309
20652
|
}
|
|
20310
20653
|
if (options.follow) {
|
|
@@ -20328,11 +20671,11 @@ var init_feed = __esm(() => {
|
|
|
20328
20671
|
// src/cli/poll.ts
|
|
20329
20672
|
var exports_poll = {};
|
|
20330
20673
|
__export(exports_poll, {
|
|
20331
|
-
run: () =>
|
|
20674
|
+
run: () => run12
|
|
20332
20675
|
});
|
|
20333
|
-
import { existsSync as
|
|
20334
|
-
import { join as
|
|
20335
|
-
function
|
|
20676
|
+
import { existsSync as existsSync13, readFileSync as readFileSync7 } from "node:fs";
|
|
20677
|
+
import { join as join16 } from "node:path";
|
|
20678
|
+
function parseArgs7(argv) {
|
|
20336
20679
|
let jobId;
|
|
20337
20680
|
let cursor = 0;
|
|
20338
20681
|
let json = false;
|
|
@@ -20357,11 +20700,11 @@ function parseArgs6(argv) {
|
|
|
20357
20700
|
}
|
|
20358
20701
|
return { jobId, cursor, json };
|
|
20359
20702
|
}
|
|
20360
|
-
async function
|
|
20361
|
-
const { jobId, cursor, json } =
|
|
20362
|
-
const jobsDir =
|
|
20363
|
-
const jobDir =
|
|
20364
|
-
if (!
|
|
20703
|
+
async function run12() {
|
|
20704
|
+
const { jobId, cursor, json } = parseArgs7(process.argv.slice(3));
|
|
20705
|
+
const jobsDir = join16(process.cwd(), ".specialists", "jobs");
|
|
20706
|
+
const jobDir = join16(jobsDir, jobId);
|
|
20707
|
+
if (!existsSync13(jobDir)) {
|
|
20365
20708
|
const result2 = {
|
|
20366
20709
|
job_id: jobId,
|
|
20367
20710
|
status: "error",
|
|
@@ -20375,16 +20718,16 @@ async function run11() {
|
|
|
20375
20718
|
console.log(JSON.stringify(result2));
|
|
20376
20719
|
process.exit(1);
|
|
20377
20720
|
}
|
|
20378
|
-
const statusPath =
|
|
20721
|
+
const statusPath = join16(jobDir, "status.json");
|
|
20379
20722
|
let status = null;
|
|
20380
|
-
if (
|
|
20723
|
+
if (existsSync13(statusPath)) {
|
|
20381
20724
|
try {
|
|
20382
20725
|
status = JSON.parse(readFileSync7(statusPath, "utf-8"));
|
|
20383
20726
|
} catch {}
|
|
20384
20727
|
}
|
|
20385
|
-
const resultPath =
|
|
20728
|
+
const resultPath = join16(jobDir, "result.txt");
|
|
20386
20729
|
let output = "";
|
|
20387
|
-
if (
|
|
20730
|
+
if (existsSync13(resultPath)) {
|
|
20388
20731
|
try {
|
|
20389
20732
|
output = readFileSync7(resultPath, "utf-8");
|
|
20390
20733
|
} catch {}
|
|
@@ -20439,18 +20782,18 @@ var init_poll = __esm(() => {
|
|
|
20439
20782
|
// src/cli/steer.ts
|
|
20440
20783
|
var exports_steer = {};
|
|
20441
20784
|
__export(exports_steer, {
|
|
20442
|
-
run: () =>
|
|
20785
|
+
run: () => run13
|
|
20443
20786
|
});
|
|
20444
|
-
import { join as
|
|
20787
|
+
import { join as join17 } from "node:path";
|
|
20445
20788
|
import { writeFileSync as writeFileSync6 } from "node:fs";
|
|
20446
|
-
async function
|
|
20789
|
+
async function run13() {
|
|
20447
20790
|
const jobId = process.argv[3];
|
|
20448
20791
|
const message = process.argv[4];
|
|
20449
20792
|
if (!jobId || !message) {
|
|
20450
20793
|
console.error('Usage: specialists|sp steer <job-id> "<message>"');
|
|
20451
20794
|
process.exit(1);
|
|
20452
20795
|
}
|
|
20453
|
-
const jobsDir =
|
|
20796
|
+
const jobsDir = join17(process.cwd(), ".specialists", "jobs");
|
|
20454
20797
|
const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
|
|
20455
20798
|
const status = supervisor.readStatus(jobId);
|
|
20456
20799
|
if (!status) {
|
|
@@ -20463,7 +20806,7 @@ async function run12() {
|
|
|
20463
20806
|
process.exit(1);
|
|
20464
20807
|
}
|
|
20465
20808
|
if (!status.fifo_path) {
|
|
20466
|
-
process.stderr.write(`${
|
|
20809
|
+
process.stderr.write(`${red4("Error:")} Job ${jobId} has no steer pipe.
|
|
20467
20810
|
`);
|
|
20468
20811
|
process.stderr.write(`Only jobs started with --background support mid-run steering.
|
|
20469
20812
|
`);
|
|
@@ -20473,15 +20816,15 @@ async function run12() {
|
|
|
20473
20816
|
const payload = JSON.stringify({ type: "steer", message }) + `
|
|
20474
20817
|
`;
|
|
20475
20818
|
writeFileSync6(status.fifo_path, payload, { flag: "a" });
|
|
20476
|
-
process.stdout.write(`${
|
|
20819
|
+
process.stdout.write(`${green8("✓")} Steer message sent to job ${jobId}
|
|
20477
20820
|
`);
|
|
20478
20821
|
} catch (err) {
|
|
20479
|
-
process.stderr.write(`${
|
|
20822
|
+
process.stderr.write(`${red4("Error:")} Failed to write to steer pipe: ${err?.message}
|
|
20480
20823
|
`);
|
|
20481
20824
|
process.exit(1);
|
|
20482
20825
|
}
|
|
20483
20826
|
}
|
|
20484
|
-
var
|
|
20827
|
+
var green8 = (s) => `\x1B[32m${s}\x1B[0m`, red4 = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
20485
20828
|
var init_steer = __esm(() => {
|
|
20486
20829
|
init_supervisor();
|
|
20487
20830
|
});
|
|
@@ -20489,18 +20832,18 @@ var init_steer = __esm(() => {
|
|
|
20489
20832
|
// src/cli/follow-up.ts
|
|
20490
20833
|
var exports_follow_up = {};
|
|
20491
20834
|
__export(exports_follow_up, {
|
|
20492
|
-
run: () =>
|
|
20835
|
+
run: () => run14
|
|
20493
20836
|
});
|
|
20494
|
-
import { join as
|
|
20837
|
+
import { join as join18 } from "node:path";
|
|
20495
20838
|
import { writeFileSync as writeFileSync7 } from "node:fs";
|
|
20496
|
-
async function
|
|
20839
|
+
async function run14() {
|
|
20497
20840
|
const jobId = process.argv[3];
|
|
20498
20841
|
const message = process.argv[4];
|
|
20499
20842
|
if (!jobId || !message) {
|
|
20500
20843
|
console.error('Usage: specialists|sp follow-up <job-id> "<message>"');
|
|
20501
20844
|
process.exit(1);
|
|
20502
20845
|
}
|
|
20503
|
-
const jobsDir =
|
|
20846
|
+
const jobsDir = join18(process.cwd(), ".specialists", "jobs");
|
|
20504
20847
|
const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
|
|
20505
20848
|
const status = supervisor.readStatus(jobId);
|
|
20506
20849
|
if (!status) {
|
|
@@ -20508,14 +20851,14 @@ async function run13() {
|
|
|
20508
20851
|
process.exit(1);
|
|
20509
20852
|
}
|
|
20510
20853
|
if (status.status !== "waiting") {
|
|
20511
|
-
process.stderr.write(`${
|
|
20854
|
+
process.stderr.write(`${red5("Error:")} Job ${jobId} is not in waiting state (status: ${status.status}).
|
|
20512
20855
|
`);
|
|
20513
20856
|
process.stderr.write(`Only jobs started with --keep-alive and --background support follow-up prompts.
|
|
20514
20857
|
`);
|
|
20515
20858
|
process.exit(1);
|
|
20516
20859
|
}
|
|
20517
20860
|
if (!status.fifo_path) {
|
|
20518
|
-
process.stderr.write(`${
|
|
20861
|
+
process.stderr.write(`${red5("Error:")} Job ${jobId} has no steer pipe.
|
|
20519
20862
|
`);
|
|
20520
20863
|
process.exit(1);
|
|
20521
20864
|
}
|
|
@@ -20523,17 +20866,17 @@ async function run13() {
|
|
|
20523
20866
|
const payload = JSON.stringify({ type: "prompt", message }) + `
|
|
20524
20867
|
`;
|
|
20525
20868
|
writeFileSync7(status.fifo_path, payload, { flag: "a" });
|
|
20526
|
-
process.stdout.write(`${
|
|
20869
|
+
process.stdout.write(`${green9("✓")} Follow-up sent to job ${jobId}
|
|
20527
20870
|
`);
|
|
20528
20871
|
process.stdout.write(` Use 'specialists feed ${jobId} --follow' to watch the response.
|
|
20529
20872
|
`);
|
|
20530
20873
|
} catch (err) {
|
|
20531
|
-
process.stderr.write(`${
|
|
20874
|
+
process.stderr.write(`${red5("Error:")} Failed to write to steer pipe: ${err?.message}
|
|
20532
20875
|
`);
|
|
20533
20876
|
process.exit(1);
|
|
20534
20877
|
}
|
|
20535
20878
|
}
|
|
20536
|
-
var
|
|
20879
|
+
var green9 = (s) => `\x1B[32m${s}\x1B[0m`, red5 = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
20537
20880
|
var init_follow_up = __esm(() => {
|
|
20538
20881
|
init_supervisor();
|
|
20539
20882
|
});
|
|
@@ -20541,16 +20884,16 @@ var init_follow_up = __esm(() => {
|
|
|
20541
20884
|
// src/cli/stop.ts
|
|
20542
20885
|
var exports_stop = {};
|
|
20543
20886
|
__export(exports_stop, {
|
|
20544
|
-
run: () =>
|
|
20887
|
+
run: () => run15
|
|
20545
20888
|
});
|
|
20546
|
-
import { join as
|
|
20547
|
-
async function
|
|
20889
|
+
import { join as join19 } from "node:path";
|
|
20890
|
+
async function run15() {
|
|
20548
20891
|
const jobId = process.argv[3];
|
|
20549
20892
|
if (!jobId) {
|
|
20550
20893
|
console.error("Usage: specialists|sp stop <job-id>");
|
|
20551
20894
|
process.exit(1);
|
|
20552
20895
|
}
|
|
20553
|
-
const jobsDir =
|
|
20896
|
+
const jobsDir = join19(process.cwd(), ".specialists", "jobs");
|
|
20554
20897
|
const supervisor = new Supervisor({ runner: null, runOptions: null, jobsDir });
|
|
20555
20898
|
const status = supervisor.readStatus(jobId);
|
|
20556
20899
|
if (!status) {
|
|
@@ -20558,31 +20901,31 @@ async function run14() {
|
|
|
20558
20901
|
process.exit(1);
|
|
20559
20902
|
}
|
|
20560
20903
|
if (status.status === "done" || status.status === "error") {
|
|
20561
|
-
process.stderr.write(`${
|
|
20904
|
+
process.stderr.write(`${dim10(`Job ${jobId} is already ${status.status}.`)}
|
|
20562
20905
|
`);
|
|
20563
20906
|
return;
|
|
20564
20907
|
}
|
|
20565
20908
|
if (!status.pid) {
|
|
20566
|
-
process.stderr.write(`${
|
|
20909
|
+
process.stderr.write(`${red6(`No PID recorded for job ${jobId}.`)}
|
|
20567
20910
|
`);
|
|
20568
20911
|
process.exit(1);
|
|
20569
20912
|
}
|
|
20570
20913
|
try {
|
|
20571
20914
|
process.kill(status.pid, "SIGTERM");
|
|
20572
|
-
process.stdout.write(`${
|
|
20915
|
+
process.stdout.write(`${green10("✓")} Sent SIGTERM to PID ${status.pid} (job ${jobId})
|
|
20573
20916
|
`);
|
|
20574
20917
|
} catch (err) {
|
|
20575
20918
|
if (err.code === "ESRCH") {
|
|
20576
|
-
process.stderr.write(`${
|
|
20919
|
+
process.stderr.write(`${red6(`Process ${status.pid} not found.`)} Job may have already completed.
|
|
20577
20920
|
`);
|
|
20578
20921
|
} else {
|
|
20579
|
-
process.stderr.write(`${
|
|
20922
|
+
process.stderr.write(`${red6("Error:")} ${err.message}
|
|
20580
20923
|
`);
|
|
20581
20924
|
process.exit(1);
|
|
20582
20925
|
}
|
|
20583
20926
|
}
|
|
20584
20927
|
}
|
|
20585
|
-
var
|
|
20928
|
+
var green10 = (s) => `\x1B[32m${s}\x1B[0m`, red6 = (s) => `\x1B[31m${s}\x1B[0m`, dim10 = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
20586
20929
|
var init_stop = __esm(() => {
|
|
20587
20930
|
init_supervisor();
|
|
20588
20931
|
});
|
|
@@ -20590,33 +20933,33 @@ var init_stop = __esm(() => {
|
|
|
20590
20933
|
// src/cli/quickstart.ts
|
|
20591
20934
|
var exports_quickstart = {};
|
|
20592
20935
|
__export(exports_quickstart, {
|
|
20593
|
-
run: () =>
|
|
20936
|
+
run: () => run16
|
|
20594
20937
|
});
|
|
20595
20938
|
function section2(title) {
|
|
20596
20939
|
const bar = "─".repeat(60);
|
|
20597
20940
|
return `
|
|
20598
|
-
${
|
|
20599
|
-
${
|
|
20941
|
+
${bold9(cyan6(title))}
|
|
20942
|
+
${dim11(bar)}`;
|
|
20600
20943
|
}
|
|
20601
20944
|
function cmd2(s) {
|
|
20602
|
-
return
|
|
20945
|
+
return yellow8(s);
|
|
20603
20946
|
}
|
|
20604
20947
|
function flag(s) {
|
|
20605
|
-
return
|
|
20948
|
+
return green11(s);
|
|
20606
20949
|
}
|
|
20607
|
-
async function
|
|
20950
|
+
async function run16() {
|
|
20608
20951
|
const lines = [
|
|
20609
20952
|
"",
|
|
20610
|
-
|
|
20611
|
-
|
|
20612
|
-
|
|
20953
|
+
bold9("specialists · Quick Start Guide"),
|
|
20954
|
+
dim11("One MCP server. Multiple AI backends. Intelligent orchestration."),
|
|
20955
|
+
dim11("Tip: sp is a shorter alias — sp run, sp list, sp feed etc. work identically."),
|
|
20613
20956
|
""
|
|
20614
20957
|
];
|
|
20615
20958
|
lines.push(section2("1. Installation"));
|
|
20616
20959
|
lines.push("");
|
|
20617
20960
|
lines.push(` ${cmd2("npm install -g @jaggerxtrm/specialists")} # install globally`);
|
|
20618
|
-
lines.push(` ${cmd2("specialists
|
|
20619
|
-
lines.push(` ${
|
|
20961
|
+
lines.push(` ${cmd2("specialists init")} # project setup:`);
|
|
20962
|
+
lines.push(` ${dim11(" # creates dirs, wires MCP + hooks, injects context")}`);
|
|
20620
20963
|
lines.push("");
|
|
20621
20964
|
lines.push(` Verify everything is healthy:`);
|
|
20622
20965
|
lines.push(` ${cmd2("specialists status")} # shows pi, beads, MCP, active jobs`);
|
|
@@ -20627,9 +20970,9 @@ async function run15() {
|
|
|
20627
20970
|
lines.push(` ${cmd2("specialists init")} # creates specialists/, .specialists/, AGENTS.md`);
|
|
20628
20971
|
lines.push("");
|
|
20629
20972
|
lines.push(` What this creates:`);
|
|
20630
|
-
lines.push(` ${
|
|
20631
|
-
lines.push(` ${
|
|
20632
|
-
lines.push(` ${
|
|
20973
|
+
lines.push(` ${dim11("specialists/")} — put your .specialist.yaml files here`);
|
|
20974
|
+
lines.push(` ${dim11(".specialists/")} — runtime data (jobs/, ready/) — gitignored`);
|
|
20975
|
+
lines.push(` ${dim11("AGENTS.md")} — context block injected into Claude sessions`);
|
|
20633
20976
|
lines.push("");
|
|
20634
20977
|
lines.push(section2("3. Discover Specialists"));
|
|
20635
20978
|
lines.push("");
|
|
@@ -20645,66 +20988,64 @@ async function run15() {
|
|
|
20645
20988
|
lines.push("");
|
|
20646
20989
|
lines.push(section2("4. Running a Specialist"));
|
|
20647
20990
|
lines.push("");
|
|
20648
|
-
lines.push(` ${
|
|
20649
|
-
lines.push(` ${cmd2("specialists run code-review")} ${flag("--prompt")} ${
|
|
20650
|
-
lines.push("");
|
|
20651
|
-
lines.push(` ${bold7("Background")} (returns a job ID immediately):`);
|
|
20652
|
-
lines.push(` ${cmd2("specialists run code-review")} ${flag("--prompt")} ${dim9('"..."')} ${flag("--background")}`);
|
|
20653
|
-
lines.push(` ${dim9(" # → Job started: job_a1b2c3d4")}`);
|
|
20991
|
+
lines.push(` ${bold9("Foreground")} (streams output to stdout):`);
|
|
20992
|
+
lines.push(` ${cmd2("specialists run code-review")} ${flag("--prompt")} ${dim11('"Review src/api.ts for security issues"')}`);
|
|
20654
20993
|
lines.push("");
|
|
20655
|
-
lines.push(` ${
|
|
20656
|
-
lines.push(` ${cmd2("specialists run code-review")} ${flag("--
|
|
20657
|
-
lines.push(` ${
|
|
20994
|
+
lines.push(` ${bold9("Tracked run")} (linked to a beads issue for workflow integration):`);
|
|
20995
|
+
lines.push(` ${cmd2("specialists run code-review")} ${flag("--bead")} ${dim11("unitAI-abc")}`);
|
|
20996
|
+
lines.push(` ${dim11(" # uses bead description as prompt, tracks result in issue")}`);
|
|
20658
20997
|
lines.push("");
|
|
20659
20998
|
lines.push(` Override model for one run:`);
|
|
20660
|
-
lines.push(` ${cmd2("specialists run code-review")} ${flag("--model")} ${
|
|
20999
|
+
lines.push(` ${cmd2("specialists run code-review")} ${flag("--model")} ${dim11("anthropic/claude-opus-4-6")} ${flag("--prompt")} ${dim11('"..."')}`);
|
|
20661
21000
|
lines.push("");
|
|
20662
21001
|
lines.push(` Run without beads issue tracking:`);
|
|
20663
|
-
lines.push(` ${cmd2("specialists run code-review")} ${flag("--no-beads")} ${flag("--prompt")} ${
|
|
21002
|
+
lines.push(` ${cmd2("specialists run code-review")} ${flag("--no-beads")} ${flag("--prompt")} ${dim11('"..."')}`);
|
|
20664
21003
|
lines.push("");
|
|
20665
21004
|
lines.push(` Pipe a prompt from stdin:`);
|
|
20666
21005
|
lines.push(` ${cmd2("cat my-brief.md | specialists run code-review")}`);
|
|
20667
21006
|
lines.push("");
|
|
20668
21007
|
lines.push(section2("5. Background Job Lifecycle"));
|
|
20669
21008
|
lines.push("");
|
|
20670
|
-
lines.push(`
|
|
21009
|
+
lines.push(` Use Claude Code's native backgrounding or run in a separate terminal.`);
|
|
21010
|
+
lines.push("");
|
|
21011
|
+
lines.push(` ${bold9("Watch progress")} — stream events as they arrive:`);
|
|
20671
21012
|
lines.push(` ${cmd2("specialists feed job_a1b2c3d4")} # print events so far`);
|
|
20672
21013
|
lines.push(` ${cmd2("specialists feed job_a1b2c3d4")} ${flag("--follow")} # tail and stream live updates`);
|
|
20673
21014
|
lines.push("");
|
|
20674
|
-
lines.push(` ${
|
|
21015
|
+
lines.push(` ${bold9("Read results")} — print the final output:`);
|
|
20675
21016
|
lines.push(` ${cmd2("specialists result job_a1b2c3d4")} # exits 1 if still running`);
|
|
20676
21017
|
lines.push("");
|
|
20677
|
-
lines.push(` ${
|
|
21018
|
+
lines.push(` ${bold9("Steer a running job")} — redirect the agent mid-run without cancelling:`);
|
|
20678
21019
|
lines.push(` ${cmd2("specialists steer job_a1b2c3d4")} ${flag('"focus only on supervisor.ts"')}`);
|
|
20679
|
-
lines.push(` ${
|
|
21020
|
+
lines.push(` ${dim11(" # delivered after current tool calls finish, before the next LLM call")}`);
|
|
20680
21021
|
lines.push("");
|
|
20681
|
-
lines.push(` ${
|
|
20682
|
-
lines.push(` ${cmd2("specialists run
|
|
20683
|
-
lines.push(` ${
|
|
21022
|
+
lines.push(` ${bold9("Keep-alive multi-turn")} — start with ${flag("--keep-alive")}, then follow up:`);
|
|
21023
|
+
lines.push(` ${cmd2("specialists run debugger")} ${flag("--bead unitAI-abc --keep-alive")}`);
|
|
21024
|
+
lines.push(` ${dim11(" # → status: waiting after first turn")}`);
|
|
20684
21025
|
lines.push(` ${cmd2("specialists result a1b2c3")} # read first turn`);
|
|
20685
21026
|
lines.push(` ${cmd2("specialists follow-up a1b2c3")} ${flag('"now write the fix"')} # next turn, same Pi context`);
|
|
20686
21027
|
lines.push(` ${cmd2("specialists feed a1b2c3")} ${flag("--follow")} # watch response`);
|
|
20687
21028
|
lines.push("");
|
|
20688
|
-
lines.push(` ${
|
|
21029
|
+
lines.push(` ${bold9("Cancel a job")}:`);
|
|
20689
21030
|
lines.push(` ${cmd2("specialists stop job_a1b2c3d4")} # sends SIGTERM to the agent process`);
|
|
20690
21031
|
lines.push("");
|
|
20691
|
-
lines.push(` ${
|
|
20692
|
-
lines.push(` ${
|
|
20693
|
-
lines.push(` ${
|
|
20694
|
-
lines.push(` ${
|
|
20695
|
-
lines.push(` ${
|
|
21032
|
+
lines.push(` ${bold9("Job files")} in ${dim11(".specialists/jobs/<job-id>/")}:`);
|
|
21033
|
+
lines.push(` ${dim11("status.json")} — id, specialist, status, pid, started_at, elapsed_s, current_tool`);
|
|
21034
|
+
lines.push(` ${dim11("events.jsonl")} — one JSON event per line (tool_use, text, agent_end, error …)`);
|
|
21035
|
+
lines.push(` ${dim11("result.txt")} — final output (written when status=done)`);
|
|
21036
|
+
lines.push(` ${dim11("steer.pipe")} — named FIFO for mid-run steering (removed on job completion)`);
|
|
20696
21037
|
lines.push("");
|
|
20697
21038
|
lines.push(section2("6. Editing Specialists"));
|
|
20698
21039
|
lines.push("");
|
|
20699
21040
|
lines.push(` Change a field without opening the YAML manually:`);
|
|
20700
|
-
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--model")} ${
|
|
20701
|
-
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--description")} ${
|
|
20702
|
-
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--timeout")} ${
|
|
20703
|
-
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--permission")} ${
|
|
20704
|
-
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--tags")} ${
|
|
21041
|
+
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--model")} ${dim11("anthropic/claude-sonnet-4-6")}`);
|
|
21042
|
+
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--description")} ${dim11('"Updated description"')}`);
|
|
21043
|
+
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--timeout")} ${dim11("120000")}`);
|
|
21044
|
+
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--permission")} ${dim11("HIGH")}`);
|
|
21045
|
+
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--tags")} ${dim11("analysis,security,review")}`);
|
|
20705
21046
|
lines.push("");
|
|
20706
21047
|
lines.push(` Preview without writing:`);
|
|
20707
|
-
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--model")} ${
|
|
21048
|
+
lines.push(` ${cmd2("specialists edit code-review")} ${flag("--model")} ${dim11("...")} ${flag("--dry-run")}`);
|
|
20708
21049
|
lines.push("");
|
|
20709
21050
|
lines.push(section2("7. .specialist.yaml Schema"));
|
|
20710
21051
|
lines.push("");
|
|
@@ -20751,100 +21092,98 @@ async function run15() {
|
|
|
20751
21092
|
" priority: 2 # 0=critical … 4=backlog"
|
|
20752
21093
|
];
|
|
20753
21094
|
for (const l of schemaLines) {
|
|
20754
|
-
lines.push(` ${
|
|
21095
|
+
lines.push(` ${dim11(l)}`);
|
|
20755
21096
|
}
|
|
20756
21097
|
lines.push("");
|
|
20757
21098
|
lines.push(section2("8. Hook System"));
|
|
20758
21099
|
lines.push("");
|
|
20759
|
-
lines.push(` Specialists emits lifecycle events to ${
|
|
21100
|
+
lines.push(` Specialists emits lifecycle events to ${dim11(".specialists/trace.jsonl")}:`);
|
|
20760
21101
|
lines.push("");
|
|
20761
|
-
lines.push(` ${
|
|
20762
|
-
lines.push(` ${
|
|
20763
|
-
lines.push(` ${
|
|
20764
|
-
lines.push(` ${
|
|
20765
|
-
lines.push(` ${
|
|
21102
|
+
lines.push(` ${bold9("Hook point")} ${bold9("When fired")}`);
|
|
21103
|
+
lines.push(` ${yellow8("specialist:start")} before the agent session begins`);
|
|
21104
|
+
lines.push(` ${yellow8("specialist:token")} on each streamed token (delta)`);
|
|
21105
|
+
lines.push(` ${yellow8("specialist:done")} after successful completion`);
|
|
21106
|
+
lines.push(` ${yellow8("specialist:error")} on failure or timeout`);
|
|
20766
21107
|
lines.push("");
|
|
20767
21108
|
lines.push(` Each event line in trace.jsonl:`);
|
|
20768
|
-
lines.push(` ${
|
|
21109
|
+
lines.push(` ${dim11('{"t":"<ISO>","hook":"specialist:done","specialist":"code-review","durationMs":4120}')}`);
|
|
20769
21110
|
lines.push("");
|
|
20770
21111
|
lines.push(` Tail the trace file to observe all activity:`);
|
|
20771
21112
|
lines.push(` ${cmd2("tail -f .specialists/trace.jsonl | jq .")}`);
|
|
20772
21113
|
lines.push("");
|
|
20773
21114
|
lines.push(section2("9. MCP Integration (Claude Code)"));
|
|
20774
21115
|
lines.push("");
|
|
20775
|
-
lines.push(` After ${cmd2("specialists
|
|
21116
|
+
lines.push(` After ${cmd2("specialists init")}, these MCP tools are available to Claude:`);
|
|
20776
21117
|
lines.push("");
|
|
20777
|
-
lines.push(` ${
|
|
20778
|
-
lines.push(` ${
|
|
20779
|
-
lines.push(` ${
|
|
20780
|
-
lines.push(` ${
|
|
20781
|
-
lines.push(` ${
|
|
20782
|
-
lines.push(` ${
|
|
20783
|
-
lines.push(` ${
|
|
20784
|
-
lines.push(` ${
|
|
20785
|
-
lines.push(` ${
|
|
20786
|
-
lines.push(` ${
|
|
21118
|
+
lines.push(` ${bold9("specialist_init")} — bootstrap: bd init + list specialists`);
|
|
21119
|
+
lines.push(` ${bold9("list_specialists")} — discover specialists (project/user/system)`);
|
|
21120
|
+
lines.push(` ${bold9("use_specialist")} — full lifecycle: load → agents.md → run → output`);
|
|
21121
|
+
lines.push(` ${bold9("run_parallel")} — concurrent or pipeline execution`);
|
|
21122
|
+
lines.push(` ${bold9("start_specialist")} — async job start, returns job ID`);
|
|
21123
|
+
lines.push(` ${bold9("poll_specialist")} — poll job status/output by ID`);
|
|
21124
|
+
lines.push(` ${bold9("steer_specialist")} — send a mid-run message to a running job`);
|
|
21125
|
+
lines.push(` ${bold9("follow_up_specialist")} — send a next-turn prompt to a keep-alive session`);
|
|
21126
|
+
lines.push(` ${bold9("stop_specialist")} — cancel a running job by ID`);
|
|
21127
|
+
lines.push(` ${bold9("specialist_status")} — circuit breaker health + staleness`);
|
|
20787
21128
|
lines.push("");
|
|
20788
21129
|
lines.push(section2("10. Common Workflows"));
|
|
20789
21130
|
lines.push("");
|
|
20790
|
-
lines.push(` ${
|
|
21131
|
+
lines.push(` ${bold9("Foreground review, save to file:")}`);
|
|
20791
21132
|
lines.push(` ${cmd2('specialists run code-review --prompt "Audit src/" > review.md')}`);
|
|
20792
21133
|
lines.push("");
|
|
20793
|
-
lines.push(` ${
|
|
20794
|
-
lines.push(` ${cmd2(
|
|
20795
|
-
lines.push(` ${
|
|
20796
|
-
lines.push(` ${cmd2("specialists result <job-id> > analysis.md")}`);
|
|
21134
|
+
lines.push(` ${bold9("Tracked run with beads integration:")}`);
|
|
21135
|
+
lines.push(` ${cmd2("specialists run deep-analysis --bead unitAI-abc")}`);
|
|
21136
|
+
lines.push(` ${dim11(" # prompt from bead, result tracked in bead")}`);
|
|
20797
21137
|
lines.push("");
|
|
20798
|
-
lines.push(` ${
|
|
20799
|
-
lines.push(` ${cmd2('specialists run deep-analysis --prompt "..." --background')}`);
|
|
21138
|
+
lines.push(` ${bold9("Steer a job mid-run:")}`);
|
|
20800
21139
|
lines.push(` ${cmd2('specialists steer <job-id> "focus only on the auth module"')}`);
|
|
20801
21140
|
lines.push(` ${cmd2("specialists result <job-id>")}`);
|
|
20802
21141
|
lines.push("");
|
|
20803
|
-
lines.push(` ${
|
|
20804
|
-
lines.push(` ${cmd2("specialists run
|
|
21142
|
+
lines.push(` ${bold9("Multi-turn keep-alive (iterative work):")}`);
|
|
21143
|
+
lines.push(` ${cmd2("specialists run debugger --bead unitAI-abc --keep-alive")}`);
|
|
20805
21144
|
lines.push(` ${cmd2("specialists result <job-id>")}`);
|
|
20806
21145
|
lines.push(` ${cmd2('specialists follow-up <job-id> "now write the fix for the root cause"')}`);
|
|
20807
21146
|
lines.push(` ${cmd2("specialists feed <job-id> --follow")}`);
|
|
20808
21147
|
lines.push("");
|
|
20809
|
-
lines.push(` ${
|
|
21148
|
+
lines.push(` ${bold9("Override model for a single run:")}`);
|
|
20810
21149
|
lines.push(` ${cmd2('specialists run code-review --model anthropic/claude-opus-4-6 --prompt "..."')}`);
|
|
20811
21150
|
lines.push("");
|
|
20812
|
-
lines.push(
|
|
20813
|
-
lines.push(` ${
|
|
20814
|
-
lines.push(` ${
|
|
21151
|
+
lines.push(dim11("─".repeat(62)));
|
|
21152
|
+
lines.push(` ${dim11("specialists help")} command list ${dim11("specialists <cmd> --help")} per-command flags`);
|
|
21153
|
+
lines.push(` ${dim11("specialists status")} health check ${dim11("specialists models")} available models`);
|
|
20815
21154
|
lines.push("");
|
|
20816
21155
|
console.log(lines.join(`
|
|
20817
21156
|
`));
|
|
20818
21157
|
}
|
|
20819
|
-
var
|
|
21158
|
+
var bold9 = (s) => `\x1B[1m${s}\x1B[0m`, dim11 = (s) => `\x1B[2m${s}\x1B[0m`, yellow8 = (s) => `\x1B[33m${s}\x1B[0m`, cyan6 = (s) => `\x1B[36m${s}\x1B[0m`, blue2 = (s) => `\x1B[34m${s}\x1B[0m`, green11 = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
20820
21159
|
|
|
20821
21160
|
// src/cli/doctor.ts
|
|
20822
21161
|
var exports_doctor = {};
|
|
20823
21162
|
__export(exports_doctor, {
|
|
20824
|
-
run: () =>
|
|
21163
|
+
run: () => run17
|
|
20825
21164
|
});
|
|
20826
21165
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
20827
|
-
import { existsSync as
|
|
20828
|
-
import { join as
|
|
21166
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync as readdirSync4 } from "node:fs";
|
|
21167
|
+
import { join as join20 } from "node:path";
|
|
20829
21168
|
function ok3(msg) {
|
|
20830
|
-
console.log(` ${
|
|
21169
|
+
console.log(` ${green12("✓")} ${msg}`);
|
|
20831
21170
|
}
|
|
20832
21171
|
function warn2(msg) {
|
|
20833
|
-
console.log(` ${
|
|
21172
|
+
console.log(` ${yellow9("○")} ${msg}`);
|
|
20834
21173
|
}
|
|
20835
21174
|
function fail2(msg) {
|
|
20836
|
-
console.log(` ${
|
|
21175
|
+
console.log(` ${red7("✗")} ${msg}`);
|
|
20837
21176
|
}
|
|
20838
21177
|
function fix(msg) {
|
|
20839
|
-
console.log(` ${
|
|
21178
|
+
console.log(` ${dim12("→ fix:")} ${yellow9(msg)}`);
|
|
20840
21179
|
}
|
|
20841
21180
|
function hint(msg) {
|
|
20842
|
-
console.log(` ${
|
|
21181
|
+
console.log(` ${dim12(msg)}`);
|
|
20843
21182
|
}
|
|
20844
21183
|
function section3(label) {
|
|
20845
21184
|
const line = "─".repeat(Math.max(0, 38 - label.length));
|
|
20846
21185
|
console.log(`
|
|
20847
|
-
${
|
|
21186
|
+
${bold10(`── ${label} ${line}`)}`);
|
|
20848
21187
|
}
|
|
20849
21188
|
function sp(bin, args) {
|
|
20850
21189
|
const r = spawnSync7(bin, args, { encoding: "utf8", stdio: "pipe", timeout: 5000 });
|
|
@@ -20854,7 +21193,7 @@ function isInstalled2(bin) {
|
|
|
20854
21193
|
return spawnSync7("which", [bin], { encoding: "utf8", timeout: 2000 }).status === 0;
|
|
20855
21194
|
}
|
|
20856
21195
|
function loadJson2(path) {
|
|
20857
|
-
if (!
|
|
21196
|
+
if (!existsSync14(path))
|
|
20858
21197
|
return null;
|
|
20859
21198
|
try {
|
|
20860
21199
|
return JSON.parse(readFileSync8(path, "utf8"));
|
|
@@ -20879,9 +21218,19 @@ function checkPi() {
|
|
|
20879
21218
|
fix("pi config (add at least one API key)");
|
|
20880
21219
|
return false;
|
|
20881
21220
|
}
|
|
20882
|
-
ok3(`pi ${vStr} — ${providers.size} provider${providers.size > 1 ? "s" : ""} active ${
|
|
21221
|
+
ok3(`pi ${vStr} — ${providers.size} provider${providers.size > 1 ? "s" : ""} active ${dim12(`(${[...providers].join(", ")})`)}`);
|
|
20883
21222
|
return true;
|
|
20884
21223
|
}
|
|
21224
|
+
function checkSpAlias() {
|
|
21225
|
+
section3("sp alias (specialists shortcut)");
|
|
21226
|
+
if (isInstalled2("sp")) {
|
|
21227
|
+
ok3("sp alias installed");
|
|
21228
|
+
return true;
|
|
21229
|
+
}
|
|
21230
|
+
fail2("sp alias not found in PATH");
|
|
21231
|
+
fix("npm install -g @jaggerxtrm/specialists@latest (reinstall to create symlink)");
|
|
21232
|
+
return false;
|
|
21233
|
+
}
|
|
20885
21234
|
function checkBd() {
|
|
20886
21235
|
section3("beads (issue tracker)");
|
|
20887
21236
|
if (!isInstalled2("bd")) {
|
|
@@ -20889,8 +21238,8 @@ function checkBd() {
|
|
|
20889
21238
|
fix("install beads (bd) first");
|
|
20890
21239
|
return false;
|
|
20891
21240
|
}
|
|
20892
|
-
ok3(`bd installed ${
|
|
20893
|
-
if (
|
|
21241
|
+
ok3(`bd installed ${dim12(sp("bd", ["--version"]).stdout || "")}`);
|
|
21242
|
+
if (existsSync14(join20(CWD, ".beads")))
|
|
20894
21243
|
ok3(".beads/ present in project");
|
|
20895
21244
|
else
|
|
20896
21245
|
warn2(".beads/ not found in project");
|
|
@@ -20903,16 +21252,16 @@ function checkXt() {
|
|
|
20903
21252
|
fix("install xtrm-tools first");
|
|
20904
21253
|
return false;
|
|
20905
21254
|
}
|
|
20906
|
-
ok3(`xt installed ${
|
|
21255
|
+
ok3(`xt installed ${dim12(sp("xt", ["--version"]).stdout || "")}`);
|
|
20907
21256
|
return true;
|
|
20908
21257
|
}
|
|
20909
21258
|
function checkHooks() {
|
|
20910
21259
|
section3("Claude Code hooks (2 expected)");
|
|
20911
21260
|
let allPresent = true;
|
|
20912
21261
|
for (const name of HOOK_NAMES) {
|
|
20913
|
-
const dest =
|
|
20914
|
-
if (!
|
|
20915
|
-
fail2(`${name} ${
|
|
21262
|
+
const dest = join20(HOOKS_DIR, name);
|
|
21263
|
+
if (!existsSync14(dest)) {
|
|
21264
|
+
fail2(`${name} ${red7("missing")}`);
|
|
20916
21265
|
fix("specialists install");
|
|
20917
21266
|
allPresent = false;
|
|
20918
21267
|
} else {
|
|
@@ -20955,18 +21304,18 @@ function checkMCP() {
|
|
|
20955
21304
|
}
|
|
20956
21305
|
function checkRuntimeDirs() {
|
|
20957
21306
|
section3(".specialists/ runtime directories");
|
|
20958
|
-
const rootDir =
|
|
20959
|
-
const jobsDir =
|
|
20960
|
-
const readyDir =
|
|
21307
|
+
const rootDir = join20(CWD, ".specialists");
|
|
21308
|
+
const jobsDir = join20(rootDir, "jobs");
|
|
21309
|
+
const readyDir = join20(rootDir, "ready");
|
|
20961
21310
|
let allOk = true;
|
|
20962
|
-
if (!
|
|
21311
|
+
if (!existsSync14(rootDir)) {
|
|
20963
21312
|
warn2(".specialists/ not found in current project");
|
|
20964
21313
|
fix("specialists init");
|
|
20965
21314
|
allOk = false;
|
|
20966
21315
|
} else {
|
|
20967
21316
|
ok3(".specialists/ present");
|
|
20968
21317
|
for (const [subDir, label] of [[jobsDir, "jobs"], [readyDir, "ready"]]) {
|
|
20969
|
-
if (!
|
|
21318
|
+
if (!existsSync14(subDir)) {
|
|
20970
21319
|
warn2(`.specialists/${label}/ missing — auto-creating`);
|
|
20971
21320
|
mkdirSync3(subDir, { recursive: true });
|
|
20972
21321
|
ok3(`.specialists/${label}/ created`);
|
|
@@ -20979,8 +21328,8 @@ function checkRuntimeDirs() {
|
|
|
20979
21328
|
}
|
|
20980
21329
|
function checkZombieJobs() {
|
|
20981
21330
|
section3("Background jobs");
|
|
20982
|
-
const jobsDir =
|
|
20983
|
-
if (!
|
|
21331
|
+
const jobsDir = join20(CWD, ".specialists", "jobs");
|
|
21332
|
+
if (!existsSync14(jobsDir)) {
|
|
20984
21333
|
hint("No .specialists/jobs/ — skipping");
|
|
20985
21334
|
return true;
|
|
20986
21335
|
}
|
|
@@ -20998,8 +21347,8 @@ function checkZombieJobs() {
|
|
|
20998
21347
|
let total = 0;
|
|
20999
21348
|
let running = 0;
|
|
21000
21349
|
for (const jobId of entries) {
|
|
21001
|
-
const statusPath =
|
|
21002
|
-
if (!
|
|
21350
|
+
const statusPath = join20(jobsDir, jobId, "status.json");
|
|
21351
|
+
if (!existsSync14(statusPath))
|
|
21003
21352
|
continue;
|
|
21004
21353
|
try {
|
|
21005
21354
|
const status = JSON.parse(readFileSync8(statusPath, "utf8"));
|
|
@@ -21016,7 +21365,7 @@ function checkZombieJobs() {
|
|
|
21016
21365
|
running++;
|
|
21017
21366
|
else {
|
|
21018
21367
|
zombies++;
|
|
21019
|
-
warn2(`${jobId} ${
|
|
21368
|
+
warn2(`${jobId} ${yellow9("ZOMBIE")} ${dim12(`pid ${pid} not found, status=${status.status}`)}`);
|
|
21020
21369
|
fix(`Edit .specialists/jobs/${jobId}/status.json → set "status": "error"`);
|
|
21021
21370
|
}
|
|
21022
21371
|
}
|
|
@@ -21029,11 +21378,12 @@ function checkZombieJobs() {
|
|
|
21029
21378
|
}
|
|
21030
21379
|
return zombies === 0;
|
|
21031
21380
|
}
|
|
21032
|
-
async function
|
|
21381
|
+
async function run17() {
|
|
21033
21382
|
console.log(`
|
|
21034
|
-
${
|
|
21383
|
+
${bold10("specialists doctor")}
|
|
21035
21384
|
`);
|
|
21036
21385
|
const piOk = checkPi();
|
|
21386
|
+
const spOk = checkSpAlias();
|
|
21037
21387
|
const bdOk = checkBd();
|
|
21038
21388
|
const xtOk = checkXt();
|
|
21039
21389
|
const hooksOk = checkHooks();
|
|
@@ -21043,21 +21393,21 @@ ${bold8("specialists doctor")}
|
|
|
21043
21393
|
const allOk = piOk && bdOk && xtOk && hooksOk && mcpOk && dirsOk && jobsOk;
|
|
21044
21394
|
console.log("");
|
|
21045
21395
|
if (allOk) {
|
|
21046
|
-
console.log(` ${
|
|
21396
|
+
console.log(` ${green12("✓")} ${bold10("All checks passed")} — specialists is healthy`);
|
|
21047
21397
|
} else {
|
|
21048
|
-
console.log(` ${
|
|
21049
|
-
console.log(` ${
|
|
21398
|
+
console.log(` ${yellow9("○")} ${bold10("Some checks failed")} — follow the fix hints above`);
|
|
21399
|
+
console.log(` ${dim12("specialists install fixes hook + MCP registration; pi, bd, and xt must be installed separately.")}`);
|
|
21050
21400
|
}
|
|
21051
21401
|
console.log("");
|
|
21052
21402
|
}
|
|
21053
|
-
var
|
|
21403
|
+
var bold10 = (s) => `\x1B[1m${s}\x1B[0m`, dim12 = (s) => `\x1B[2m${s}\x1B[0m`, green12 = (s) => `\x1B[32m${s}\x1B[0m`, yellow9 = (s) => `\x1B[33m${s}\x1B[0m`, red7 = (s) => `\x1B[31m${s}\x1B[0m`, CWD, CLAUDE_DIR, SPECIALISTS_DIR, HOOKS_DIR, SETTINGS_FILE, MCP_FILE2, HOOK_NAMES;
|
|
21054
21404
|
var init_doctor = __esm(() => {
|
|
21055
21405
|
CWD = process.cwd();
|
|
21056
|
-
CLAUDE_DIR =
|
|
21057
|
-
SPECIALISTS_DIR =
|
|
21058
|
-
HOOKS_DIR =
|
|
21059
|
-
SETTINGS_FILE =
|
|
21060
|
-
MCP_FILE2 =
|
|
21406
|
+
CLAUDE_DIR = join20(CWD, ".claude");
|
|
21407
|
+
SPECIALISTS_DIR = join20(CWD, ".specialists");
|
|
21408
|
+
HOOKS_DIR = join20(SPECIALISTS_DIR, "default", "hooks");
|
|
21409
|
+
SETTINGS_FILE = join20(CLAUDE_DIR, "settings.json");
|
|
21410
|
+
MCP_FILE2 = join20(CWD, ".mcp.json");
|
|
21061
21411
|
HOOK_NAMES = [
|
|
21062
21412
|
"specialists-complete.mjs",
|
|
21063
21413
|
"specialists-session-start.mjs"
|
|
@@ -21067,191 +21417,49 @@ var init_doctor = __esm(() => {
|
|
|
21067
21417
|
// src/cli/setup.ts
|
|
21068
21418
|
var exports_setup = {};
|
|
21069
21419
|
__export(exports_setup, {
|
|
21070
|
-
run: () =>
|
|
21420
|
+
run: () => run18
|
|
21071
21421
|
});
|
|
21072
|
-
|
|
21073
|
-
|
|
21074
|
-
|
|
21075
|
-
|
|
21076
|
-
console.log(` ${
|
|
21077
|
-
|
|
21078
|
-
|
|
21079
|
-
console.log(
|
|
21080
|
-
|
|
21081
|
-
|
|
21082
|
-
|
|
21083
|
-
case "global":
|
|
21084
|
-
return join20(homedir3(), ".claude", "CLAUDE.md");
|
|
21085
|
-
case "agents":
|
|
21086
|
-
return join20(process.cwd(), "AGENTS.md");
|
|
21087
|
-
case "project":
|
|
21088
|
-
default:
|
|
21089
|
-
return join20(process.cwd(), "CLAUDE.md");
|
|
21090
|
-
}
|
|
21091
|
-
}
|
|
21092
|
-
function parseArgs7() {
|
|
21093
|
-
const argv = process.argv.slice(3);
|
|
21094
|
-
let target = "project";
|
|
21095
|
-
let dryRun = false;
|
|
21096
|
-
for (let i = 0;i < argv.length; i++) {
|
|
21097
|
-
const token = argv[i];
|
|
21098
|
-
if (token === "--global" || token === "-g") {
|
|
21099
|
-
target = "global";
|
|
21100
|
-
continue;
|
|
21101
|
-
}
|
|
21102
|
-
if (token === "--agents" || token === "-a") {
|
|
21103
|
-
target = "agents";
|
|
21104
|
-
continue;
|
|
21105
|
-
}
|
|
21106
|
-
if (token === "--project" || token === "-p") {
|
|
21107
|
-
target = "project";
|
|
21108
|
-
continue;
|
|
21109
|
-
}
|
|
21110
|
-
if (token === "--dry-run") {
|
|
21111
|
-
dryRun = true;
|
|
21112
|
-
continue;
|
|
21113
|
-
}
|
|
21114
|
-
}
|
|
21115
|
-
return { target, dryRun };
|
|
21116
|
-
}
|
|
21117
|
-
async function run17() {
|
|
21118
|
-
const { target, dryRun } = parseArgs7();
|
|
21119
|
-
const filePath = resolve2(resolveTarget(target));
|
|
21120
|
-
const label = target === "global" ? "~/.claude/CLAUDE.md" : filePath.replace(process.cwd() + "/", "");
|
|
21121
|
-
console.log(`
|
|
21122
|
-
${bold9("specialists setup")}
|
|
21123
|
-
`);
|
|
21124
|
-
console.log(` Target: ${yellow8(label)}${dryRun ? dim11(" (dry-run)") : ""}
|
|
21125
|
-
`);
|
|
21126
|
-
if (existsSync13(filePath)) {
|
|
21127
|
-
const existing = readFileSync9(filePath, "utf8");
|
|
21128
|
-
if (existing.includes(MARKER)) {
|
|
21129
|
-
skip2(`${label} already contains Specialists Workflow section`);
|
|
21130
|
-
console.log(`
|
|
21131
|
-
${dim11("To force-update, remove the ## Specialists Workflow section and re-run.")}
|
|
21132
|
-
`);
|
|
21133
|
-
return;
|
|
21134
|
-
}
|
|
21135
|
-
if (dryRun) {
|
|
21136
|
-
console.log(dim11("─".repeat(60)));
|
|
21137
|
-
console.log(dim11("Would append to existing file:"));
|
|
21138
|
-
console.log("");
|
|
21139
|
-
console.log(WORKFLOW_BLOCK);
|
|
21140
|
-
console.log(dim11("─".repeat(60)));
|
|
21141
|
-
return;
|
|
21142
|
-
}
|
|
21143
|
-
const separator = existing.trimEnd().endsWith(`
|
|
21144
|
-
`) ? `
|
|
21145
|
-
` : `
|
|
21146
|
-
|
|
21147
|
-
`;
|
|
21148
|
-
writeFileSync8(filePath, existing.trimEnd() + separator + WORKFLOW_BLOCK, "utf8");
|
|
21149
|
-
ok4(`Appended Specialists Workflow section to ${label}`);
|
|
21150
|
-
} else {
|
|
21151
|
-
if (dryRun) {
|
|
21152
|
-
console.log(dim11("─".repeat(60)));
|
|
21153
|
-
console.log(dim11(`Would create ${label}:`));
|
|
21154
|
-
console.log("");
|
|
21155
|
-
console.log(WORKFLOW_BLOCK);
|
|
21156
|
-
console.log(dim11("─".repeat(60)));
|
|
21157
|
-
return;
|
|
21158
|
-
}
|
|
21159
|
-
writeFileSync8(filePath, WORKFLOW_BLOCK, "utf8");
|
|
21160
|
-
ok4(`Created ${label} with Specialists Workflow section`);
|
|
21161
|
-
}
|
|
21422
|
+
async function run18() {
|
|
21423
|
+
console.log("");
|
|
21424
|
+
console.log(yellow10("⚠ DEPRECATED: `specialists setup` is deprecated"));
|
|
21425
|
+
console.log("");
|
|
21426
|
+
console.log(` Use ${bold11("specialists init")} instead.`);
|
|
21427
|
+
console.log("");
|
|
21428
|
+
console.log(" The init command handles all setup tasks:");
|
|
21429
|
+
console.log(" • creates specialists/ and .specialists/ directories");
|
|
21430
|
+
console.log(" • adds .specialists/ to .gitignore");
|
|
21431
|
+
console.log(" • registers the MCP server in .mcp.json");
|
|
21432
|
+
console.log(" • injects workflow context into AGENTS.md/CLAUDE.md");
|
|
21162
21433
|
console.log("");
|
|
21163
|
-
console.log(
|
|
21164
|
-
console.log(
|
|
21165
|
-
console.log(
|
|
21166
|
-
console.log(`
|
|
21434
|
+
console.log(" Options:");
|
|
21435
|
+
console.log(" --force-workflow Overwrite existing workflow blocks");
|
|
21436
|
+
console.log("");
|
|
21437
|
+
console.log(` ${dim13("Run: specialists init --help for full details")}`);
|
|
21167
21438
|
console.log("");
|
|
21168
21439
|
}
|
|
21169
|
-
var
|
|
21170
|
-
|
|
21171
|
-
> Injected by \`specialists setup\`. Keep this section — agents use it for context.
|
|
21172
|
-
|
|
21173
|
-
### When to use specialists
|
|
21174
|
-
|
|
21175
|
-
Specialists are autonomous AI agents (running via the \`specialists\` MCP server)
|
|
21176
|
-
optimised for heavy tasks: code review, deep bug analysis, test generation,
|
|
21177
|
-
architecture design. Use them instead of doing the work yourself when the task
|
|
21178
|
-
would benefit from a fresh perspective, a second opinion, or a different model.
|
|
21179
|
-
|
|
21180
|
-
### Quick reference
|
|
21181
|
-
|
|
21182
|
-
\`\`\`
|
|
21183
|
-
# List available specialists
|
|
21184
|
-
specialists list # all scopes
|
|
21185
|
-
specialists list --scope project # this project only
|
|
21186
|
-
|
|
21187
|
-
# Run a specialist (foreground — streams output)
|
|
21188
|
-
specialists run <name> --prompt "..."
|
|
21189
|
-
|
|
21190
|
-
# Run async (background — immediate job ID)
|
|
21191
|
-
specialists run <name> --prompt "..." --background
|
|
21192
|
-
→ Job started: job_a1b2c3d4
|
|
21193
|
-
|
|
21194
|
-
# Watch / get results
|
|
21195
|
-
specialists feed job_a1b2c3d4 --follow # tail live events
|
|
21196
|
-
specialists result job_a1b2c3d4 # read final output
|
|
21197
|
-
specialists stop job_a1b2c3d4 # cancel if needed
|
|
21198
|
-
\`\`\`
|
|
21199
|
-
|
|
21200
|
-
### MCP tools (available in this session)
|
|
21201
|
-
|
|
21202
|
-
| Tool | Purpose |
|
|
21203
|
-
|------|---------|
|
|
21204
|
-
| \`specialist_init\` | Bootstrap: bd init + list specialists |
|
|
21205
|
-
| \`list_specialists\` | Discover specialists across scopes |
|
|
21206
|
-
| \`use_specialist\` | Run foreground: load → inject context → execute → output |
|
|
21207
|
-
| \`start_specialist\` | Start async: returns job ID immediately |
|
|
21208
|
-
| \`poll_specialist\` | Poll job status + delta output by ID |
|
|
21209
|
-
| \`stop_specialist\` | Cancel a running job |
|
|
21210
|
-
| \`run_parallel\` | Run multiple specialists concurrently or as a pipeline |
|
|
21211
|
-
| \`specialist_status\` | Circuit breaker health + staleness |
|
|
21212
|
-
|
|
21213
|
-
### Completion banner format
|
|
21214
|
-
|
|
21215
|
-
When a specialist finishes, you may see:
|
|
21216
|
-
|
|
21217
|
-
\`\`\`
|
|
21218
|
-
✓ bead unitAI-xxx 4.1s anthropic/claude-sonnet-4-6
|
|
21219
|
-
\`\`\`
|
|
21220
|
-
|
|
21221
|
-
This means:
|
|
21222
|
-
- The specialist completed successfully
|
|
21223
|
-
- A beads issue (\`unitAI-xxx\`) was created to track the run
|
|
21224
|
-
- The result can be fetched with \`specialists result <job-id>\`
|
|
21225
|
-
|
|
21226
|
-
### When NOT to use specialists
|
|
21227
|
-
|
|
21228
|
-
- Simple single-file edits — just do it directly
|
|
21229
|
-
- Tasks that need interactive back-and-forth — use foreground mode or work yourself
|
|
21230
|
-
- Short read-only queries — faster to answer directly
|
|
21231
|
-
`;
|
|
21232
|
-
var init_setup = () => {};
|
|
21440
|
+
var bold11 = (s) => `\x1B[1m${s}\x1B[0m`, yellow10 = (s) => `\x1B[33m${s}\x1B[0m`, dim13 = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
21233
21441
|
|
|
21234
21442
|
// src/cli/help.ts
|
|
21235
21443
|
var exports_help = {};
|
|
21236
21444
|
__export(exports_help, {
|
|
21237
|
-
run: () =>
|
|
21445
|
+
run: () => run19
|
|
21238
21446
|
});
|
|
21239
21447
|
function formatCommands(entries) {
|
|
21240
21448
|
const width = Math.max(...entries.map(([cmd3]) => cmd3.length));
|
|
21241
21449
|
return entries.map(([cmd3, desc]) => ` ${cmd3.padEnd(width)} ${desc}`);
|
|
21242
21450
|
}
|
|
21243
|
-
async function
|
|
21451
|
+
async function run19() {
|
|
21244
21452
|
const lines = [
|
|
21245
21453
|
"",
|
|
21246
21454
|
"Specialists lets you run project-scoped specialist agents with a bead-first workflow.",
|
|
21247
21455
|
"",
|
|
21248
|
-
|
|
21456
|
+
bold12("Usage:"),
|
|
21249
21457
|
" specialists|sp [command]",
|
|
21250
21458
|
" specialists|sp [command] --help",
|
|
21251
21459
|
"",
|
|
21252
|
-
|
|
21460
|
+
dim14(" sp is a shorter alias — sp run, sp list, sp feed etc. all work identically."),
|
|
21253
21461
|
"",
|
|
21254
|
-
|
|
21462
|
+
bold12("Common flows:"),
|
|
21255
21463
|
"",
|
|
21256
21464
|
" Tracked work (primary)",
|
|
21257
21465
|
' bd create "Task title" -t task -p 1 --json',
|
|
@@ -21273,19 +21481,19 @@ async function run18() {
|
|
|
21273
21481
|
" or run in a separate terminal and poll with:",
|
|
21274
21482
|
" specialists poll <job-id> --json",
|
|
21275
21483
|
"",
|
|
21276
|
-
|
|
21484
|
+
bold12("Core commands:"),
|
|
21277
21485
|
...formatCommands(CORE_COMMANDS),
|
|
21278
21486
|
"",
|
|
21279
|
-
|
|
21487
|
+
bold12("Extended commands:"),
|
|
21280
21488
|
...formatCommands(EXTENDED_COMMANDS),
|
|
21281
21489
|
"",
|
|
21282
|
-
|
|
21490
|
+
bold12("xtrm worktree commands:"),
|
|
21283
21491
|
...formatCommands(WORKTREE_COMMANDS),
|
|
21284
21492
|
"",
|
|
21285
|
-
|
|
21493
|
+
bold12("Examples:"),
|
|
21286
21494
|
" specialists init",
|
|
21287
21495
|
" specialists list",
|
|
21288
|
-
" specialists run
|
|
21496
|
+
" specialists run debugger --bead unitAI-123",
|
|
21289
21497
|
' specialists run codebase-explorer --prompt "Map the CLI architecture"',
|
|
21290
21498
|
" specialists poll abc123 --json # check job status",
|
|
21291
21499
|
" specialists feed -f # stream all job events",
|
|
@@ -21293,7 +21501,7 @@ async function run18() {
|
|
|
21293
21501
|
' specialists follow-up <job-id> "now write the fix"',
|
|
21294
21502
|
" specialists result <job-id>",
|
|
21295
21503
|
"",
|
|
21296
|
-
|
|
21504
|
+
bold12("More help:"),
|
|
21297
21505
|
" specialists quickstart Full guide and workflow reference",
|
|
21298
21506
|
" specialists run --help Run command details and flags",
|
|
21299
21507
|
" specialists poll --help Job status polling details",
|
|
@@ -21302,17 +21510,18 @@ async function run18() {
|
|
|
21302
21510
|
" specialists init --help Bootstrap behavior and workflow injection",
|
|
21303
21511
|
" specialists feed --help Job event streaming details",
|
|
21304
21512
|
"",
|
|
21305
|
-
|
|
21513
|
+
dim14("Project model: specialists are project-only; user-scope discovery is deprecated."),
|
|
21306
21514
|
""
|
|
21307
21515
|
];
|
|
21308
21516
|
console.log(lines.join(`
|
|
21309
21517
|
`));
|
|
21310
21518
|
}
|
|
21311
|
-
var
|
|
21519
|
+
var bold12 = (s) => `\x1B[1m${s}\x1B[0m`, dim14 = (s) => `\x1B[2m${s}\x1B[0m`, CORE_COMMANDS, EXTENDED_COMMANDS, WORKTREE_COMMANDS;
|
|
21312
21520
|
var init_help = __esm(() => {
|
|
21313
21521
|
CORE_COMMANDS = [
|
|
21314
21522
|
["init", "Bootstrap a project: dirs, workflow injection, project MCP registration"],
|
|
21315
21523
|
["list", "List specialists in this project"],
|
|
21524
|
+
["validate", "Validate a specialist YAML against the schema"],
|
|
21316
21525
|
["run", "Run a specialist with --bead for tracked work or --prompt for ad-hoc work"],
|
|
21317
21526
|
["feed", "Tail job events; use -f to follow all jobs"],
|
|
21318
21527
|
["poll", "Machine-readable job status polling (for scripts/Claude Code)"],
|
|
@@ -29308,23 +29517,20 @@ var next = process.argv[3];
|
|
|
29308
29517
|
function wantsHelp() {
|
|
29309
29518
|
return next === "--help" || next === "-h";
|
|
29310
29519
|
}
|
|
29311
|
-
async function
|
|
29520
|
+
async function run20() {
|
|
29312
29521
|
if (sub === "install") {
|
|
29313
29522
|
if (wantsHelp()) {
|
|
29314
29523
|
console.log([
|
|
29315
29524
|
"",
|
|
29316
|
-
"
|
|
29525
|
+
"⚠ DEPRECATED: Use `specialists init` instead.",
|
|
29317
29526
|
"",
|
|
29318
|
-
"
|
|
29319
|
-
"and installs specialists-specific project hooks.",
|
|
29320
|
-
"",
|
|
29321
|
-
"No flags — just run it.",
|
|
29527
|
+
"The install command is deprecated. Run `specialists init` for project setup.",
|
|
29322
29528
|
""
|
|
29323
29529
|
].join(`
|
|
29324
29530
|
`));
|
|
29325
29531
|
return;
|
|
29326
29532
|
}
|
|
29327
|
-
const { run: handler } = await Promise.resolve().then(() =>
|
|
29533
|
+
const { run: handler } = await Promise.resolve().then(() => exports_install);
|
|
29328
29534
|
return handler();
|
|
29329
29535
|
}
|
|
29330
29536
|
if (sub === "version" || sub === "--version" || sub === "-v") {
|
|
@@ -29413,6 +29619,38 @@ async function run19() {
|
|
|
29413
29619
|
const { run: handler } = await Promise.resolve().then(() => (init_init(), exports_init));
|
|
29414
29620
|
return handler();
|
|
29415
29621
|
}
|
|
29622
|
+
if (sub === "validate") {
|
|
29623
|
+
if (wantsHelp()) {
|
|
29624
|
+
console.log([
|
|
29625
|
+
"",
|
|
29626
|
+
"Usage: specialists validate <name> [--json]",
|
|
29627
|
+
"",
|
|
29628
|
+
"Validate a specialist YAML file against the schema.",
|
|
29629
|
+
"",
|
|
29630
|
+
"What it checks:",
|
|
29631
|
+
" - YAML syntax is valid",
|
|
29632
|
+
" - Required fields are present (name, version, description, category, model)",
|
|
29633
|
+
" - Field values match expected formats (kebab-case names, semver versions)",
|
|
29634
|
+
" - Enum values are valid (permission_required, mode, beads_integration)",
|
|
29635
|
+
"",
|
|
29636
|
+
"Options:",
|
|
29637
|
+
" --json Output validation result as JSON",
|
|
29638
|
+
"",
|
|
29639
|
+
"Examples:",
|
|
29640
|
+
" specialists validate my-specialist",
|
|
29641
|
+
" specialists validate my-specialist --json",
|
|
29642
|
+
"",
|
|
29643
|
+
"Exit codes:",
|
|
29644
|
+
" 0 — validation passed",
|
|
29645
|
+
" 1 — validation failed (errors) or specialist not found",
|
|
29646
|
+
""
|
|
29647
|
+
].join(`
|
|
29648
|
+
`));
|
|
29649
|
+
return;
|
|
29650
|
+
}
|
|
29651
|
+
const { run: handler } = await Promise.resolve().then(() => (init_validate(), exports_validate));
|
|
29652
|
+
return handler();
|
|
29653
|
+
}
|
|
29416
29654
|
if (sub === "edit") {
|
|
29417
29655
|
if (wantsHelp()) {
|
|
29418
29656
|
console.log([
|
|
@@ -29431,7 +29669,7 @@ async function run19() {
|
|
|
29431
29669
|
"",
|
|
29432
29670
|
"Options:",
|
|
29433
29671
|
" --dry-run Preview the change without writing",
|
|
29434
|
-
" --scope <
|
|
29672
|
+
" --scope <default|user> Disambiguate if same name exists in multiple scopes",
|
|
29435
29673
|
"",
|
|
29436
29674
|
"Examples:",
|
|
29437
29675
|
" specialists edit code-review --model anthropic/claude-opus-4-6",
|
|
@@ -29466,8 +29704,8 @@ async function run19() {
|
|
|
29466
29704
|
" --keep-alive Keep session alive for follow-up prompts",
|
|
29467
29705
|
"",
|
|
29468
29706
|
"Examples:",
|
|
29469
|
-
" specialists run
|
|
29470
|
-
" specialists run
|
|
29707
|
+
" specialists run debugger --bead unitAI-55d",
|
|
29708
|
+
" specialists run debugger --bead unitAI-55d --context-depth 2",
|
|
29471
29709
|
' specialists run code-review --prompt "Audit src/api.ts"',
|
|
29472
29710
|
" cat brief.md | specialists run report-generator",
|
|
29473
29711
|
"",
|
|
@@ -29632,7 +29870,7 @@ async function run19() {
|
|
|
29632
29870
|
' specialists steer a1b2c3 "skip tests, just fix the bug"',
|
|
29633
29871
|
"",
|
|
29634
29872
|
"Notes:",
|
|
29635
|
-
" - Only works for jobs
|
|
29873
|
+
" - Only works for running jobs.",
|
|
29636
29874
|
" - Delivery is best-effort: the agent processes it on its next turn.",
|
|
29637
29875
|
""
|
|
29638
29876
|
].join(`
|
|
@@ -29651,14 +29889,14 @@ async function run19() {
|
|
|
29651
29889
|
"Send a follow-up prompt to a waiting keep-alive specialist session.",
|
|
29652
29890
|
"The Pi session retains full conversation history between turns.",
|
|
29653
29891
|
"",
|
|
29654
|
-
"Requires: job started with --keep-alive
|
|
29892
|
+
"Requires: job started with --keep-alive.",
|
|
29655
29893
|
"",
|
|
29656
29894
|
"Examples:",
|
|
29657
29895
|
' specialists follow-up a1b2c3 "Now write the fix for the bug you found"',
|
|
29658
29896
|
' specialists follow-up a1b2c3 "Focus only on the auth module"',
|
|
29659
29897
|
"",
|
|
29660
29898
|
"Workflow:",
|
|
29661
|
-
" specialists run
|
|
29899
|
+
" specialists run debugger --bead <id> --keep-alive",
|
|
29662
29900
|
" # → Job started: a1b2c3 (status: waiting after first turn)",
|
|
29663
29901
|
" specialists result a1b2c3 # read first turn output",
|
|
29664
29902
|
' specialists follow-up a1b2c3 "..." # send next prompt',
|
|
@@ -29731,28 +29969,15 @@ async function run19() {
|
|
|
29731
29969
|
if (wantsHelp()) {
|
|
29732
29970
|
console.log([
|
|
29733
29971
|
"",
|
|
29734
|
-
"
|
|
29735
|
-
"",
|
|
29736
|
-
"Inject the Specialists Workflow context block into AGENTS.md or CLAUDE.md.",
|
|
29737
|
-
"This teaches agents in that project how to use specialists.",
|
|
29972
|
+
"⚠ DEPRECATED: Use `specialists init` instead.",
|
|
29738
29973
|
"",
|
|
29739
|
-
"
|
|
29740
|
-
" --project, -p Write to ./CLAUDE.md (default)",
|
|
29741
|
-
" --agents, -a Write to ./AGENTS.md",
|
|
29742
|
-
" --global, -g Write to ~/.claude/CLAUDE.md",
|
|
29743
|
-
" --dry-run Preview the block without writing",
|
|
29744
|
-
"",
|
|
29745
|
-
"Examples:",
|
|
29746
|
-
" specialists setup # → ./CLAUDE.md",
|
|
29747
|
-
" specialists setup --agents # → ./AGENTS.md",
|
|
29748
|
-
" specialists setup --global # → ~/.claude/CLAUDE.md",
|
|
29749
|
-
" specialists setup --dry-run # preview only",
|
|
29974
|
+
"The setup command is deprecated. Run `specialists init` for project setup.",
|
|
29750
29975
|
""
|
|
29751
29976
|
].join(`
|
|
29752
29977
|
`));
|
|
29753
29978
|
return;
|
|
29754
29979
|
}
|
|
29755
|
-
const { run: handler } = await Promise.resolve().then(() =>
|
|
29980
|
+
const { run: handler } = await Promise.resolve().then(() => exports_setup);
|
|
29756
29981
|
return handler();
|
|
29757
29982
|
}
|
|
29758
29983
|
if (sub === "help" || sub === "--help" || sub === "-h") {
|
|
@@ -29768,7 +29993,7 @@ Run 'specialists help' to see available commands.`);
|
|
|
29768
29993
|
const server = new SpecialistsServer;
|
|
29769
29994
|
await server.start();
|
|
29770
29995
|
}
|
|
29771
|
-
|
|
29996
|
+
run20().catch((error2) => {
|
|
29772
29997
|
logger.error(`Fatal error: ${error2}`);
|
|
29773
29998
|
process.exit(1);
|
|
29774
29999
|
});
|