@wrongstack/cli 0.7.4 → 0.7.6
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/dist/index.js +155 -36
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import * as os6 from 'os';
|
|
|
9
9
|
import os6__default from 'os';
|
|
10
10
|
import * as crypto2 from 'crypto';
|
|
11
11
|
import { randomUUID } from 'crypto';
|
|
12
|
-
import { DefaultSecretVault as DefaultSecretVault$1, encryptConfigSecrets, decryptConfigSecrets as decryptConfigSecrets$1 } from '@wrongstack/core/security';
|
|
12
|
+
import { DefaultSecretVault as DefaultSecretVault$1, encryptConfigSecrets, isSecretField, decryptConfigSecrets as decryptConfigSecrets$1 } from '@wrongstack/core/security';
|
|
13
13
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
14
14
|
import { MCPRegistry } from '@wrongstack/mcp';
|
|
15
15
|
import { buildProviderFactoriesFromRegistry, makeProviderFromConfig, capabilitiesFor } from '@wrongstack/providers';
|
|
@@ -64,8 +64,10 @@ __export(sdd_exports, {
|
|
|
64
64
|
getActiveSDDPhase: () => getActiveSDDPhase,
|
|
65
65
|
getCurrentExecutingContext: () => getCurrentExecutingContext,
|
|
66
66
|
getCurrentTask: () => getCurrentTask,
|
|
67
|
+
getTaskGraphId: () => getTaskGraphId,
|
|
67
68
|
getTaskListText: () => getTaskListText,
|
|
68
69
|
getTaskProgress: () => getTaskProgress,
|
|
70
|
+
getTaskTracker: () => getTaskTracker,
|
|
69
71
|
markTaskCompleted: () => markTaskCompleted,
|
|
70
72
|
renderTaskListWithProgress: () => renderTaskListWithProgress,
|
|
71
73
|
trySaveImplementationPlan: () => trySaveImplementationPlan,
|
|
@@ -380,10 +382,9 @@ function buildSddCommand(opts) {
|
|
|
380
382
|
name: "sdd",
|
|
381
383
|
description: "AI-driven SDD: /sdd [new|approve|execute|cancel|status|list|show|templates]",
|
|
382
384
|
async run(args) {
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
const
|
|
386
|
-
const graphsDir = path23.join(projectRoot, ".wrongstack", "task-graphs");
|
|
385
|
+
opts.context;
|
|
386
|
+
const specsDir = opts.paths.projectSpecs;
|
|
387
|
+
const graphsDir = opts.paths.projectTaskGraphs;
|
|
387
388
|
const specStore = new SpecStore({ baseDir: specsDir });
|
|
388
389
|
new TaskGraphStore({ baseDir: graphsDir });
|
|
389
390
|
const versioning = sddState.getVersioning();
|
|
@@ -399,10 +400,10 @@ function buildSddCommand(opts) {
|
|
|
399
400
|
const forceFlag = rest.includes("--force") || rest.includes("-f");
|
|
400
401
|
const title = rest.filter((a) => !a.startsWith("-")).join(" ").trim() || "Untitled Feature";
|
|
401
402
|
if (!sessionState.getBuilder() && !forceFlag) {
|
|
402
|
-
const sessionPath =
|
|
403
|
+
const sessionPath = opts.paths.projectSddSession;
|
|
403
404
|
try {
|
|
404
405
|
await fsp2.access(sessionPath);
|
|
405
|
-
const projectContext2 = await gatherProjectContext(projectRoot);
|
|
406
|
+
const projectContext2 = await gatherProjectContext(opts.context?.projectRoot ?? process.cwd());
|
|
406
407
|
const tempBuilder = new AISpecBuilder({
|
|
407
408
|
store: specStore,
|
|
408
409
|
projectContext: projectContext2,
|
|
@@ -428,13 +429,13 @@ function buildSddCommand(opts) {
|
|
|
428
429
|
}
|
|
429
430
|
}
|
|
430
431
|
sddState.clearTaskState();
|
|
431
|
-
const projectContext = await gatherProjectContext(projectRoot);
|
|
432
|
+
const projectContext = await gatherProjectContext(opts.context?.projectRoot ?? process.cwd());
|
|
432
433
|
sddState.setBuilder(new AISpecBuilder({
|
|
433
434
|
store: specStore,
|
|
434
435
|
projectContext,
|
|
435
436
|
minQuestions: 2,
|
|
436
437
|
maxQuestions: 10,
|
|
437
|
-
sessionPath:
|
|
438
|
+
sessionPath: opts.paths.projectSddSession
|
|
438
439
|
}));
|
|
439
440
|
sddState.setSessionStartTime(Date.now());
|
|
440
441
|
sddState.setPhaseStartTime(Date.now());
|
|
@@ -547,8 +548,16 @@ Start executing the tasks one by one.`
|
|
|
547
548
|
};
|
|
548
549
|
}
|
|
549
550
|
// ── Task Execution ─────────────────────────────────────────────────
|
|
550
|
-
case "
|
|
551
|
-
case "
|
|
551
|
+
case "run":
|
|
552
|
+
case "execute": {
|
|
553
|
+
if (opts.onSddParallelRun) {
|
|
554
|
+
const slotsArg = restJoined.trim();
|
|
555
|
+
const slots = slotsArg ? Number.parseInt(slotsArg, 10) : void 0;
|
|
556
|
+
const message = await opts.onSddParallelRun(
|
|
557
|
+
slots && Number.isFinite(slots) ? { parallelSlots: Math.min(16, Math.max(1, slots)) } : {}
|
|
558
|
+
);
|
|
559
|
+
return { message };
|
|
560
|
+
}
|
|
552
561
|
const runBuilder = sddState.getBuilder();
|
|
553
562
|
if (!runBuilder) {
|
|
554
563
|
return {
|
|
@@ -572,6 +581,21 @@ User message:
|
|
|
572
581
|
Start executing the tasks one by one.`
|
|
573
582
|
};
|
|
574
583
|
}
|
|
584
|
+
case "parallel": {
|
|
585
|
+
if (!opts.onSddParallelRun) {
|
|
586
|
+
return { message: "SDD parallel run is not available in this session." };
|
|
587
|
+
}
|
|
588
|
+
const slotsArg = restJoined.trim();
|
|
589
|
+
const slots = slotsArg ? Number.parseInt(slotsArg, 10) : void 0;
|
|
590
|
+
const message = await opts.onSddParallelRun(
|
|
591
|
+
slots && Number.isFinite(slots) ? { parallelSlots: Math.min(16, Math.max(1, slots)) } : {}
|
|
592
|
+
);
|
|
593
|
+
return { message };
|
|
594
|
+
}
|
|
595
|
+
case "stop": {
|
|
596
|
+
opts.onSddParallelStop?.();
|
|
597
|
+
return { message: "SDD parallel run stopped." };
|
|
598
|
+
}
|
|
575
599
|
case "plan":
|
|
576
600
|
case "impl": {
|
|
577
601
|
const planBuilder = sddState.getBuilder();
|
|
@@ -1001,7 +1025,7 @@ Start executing the tasks one by one.`
|
|
|
1001
1025
|
return { message: lines2.join("\n") };
|
|
1002
1026
|
}
|
|
1003
1027
|
try {
|
|
1004
|
-
const graphStore2 = new TaskGraphStore({ baseDir:
|
|
1028
|
+
const graphStore2 = new TaskGraphStore({ baseDir: opts.paths.projectTaskGraphs });
|
|
1005
1029
|
const stored = await graphStore2.load(graphId);
|
|
1006
1030
|
if (stored) {
|
|
1007
1031
|
return { message: renderTaskGraph(stored, { compact: false }) };
|
|
@@ -1026,7 +1050,7 @@ Start executing the tasks one by one.`
|
|
|
1026
1050
|
return { message: lines.join("\n") };
|
|
1027
1051
|
}
|
|
1028
1052
|
case "cancel": {
|
|
1029
|
-
const sessionPath =
|
|
1053
|
+
const sessionPath = opts.paths.projectSddSession;
|
|
1030
1054
|
let deletedFromDisk = false;
|
|
1031
1055
|
try {
|
|
1032
1056
|
await fsp2.unlink(sessionPath);
|
|
@@ -1034,11 +1058,11 @@ Start executing the tasks one by one.`
|
|
|
1034
1058
|
} catch {
|
|
1035
1059
|
}
|
|
1036
1060
|
try {
|
|
1037
|
-
await fsp2.rm(
|
|
1061
|
+
await fsp2.rm(opts.paths.projectSpecs, { recursive: true, force: true });
|
|
1038
1062
|
} catch {
|
|
1039
1063
|
}
|
|
1040
1064
|
try {
|
|
1041
|
-
await fsp2.rm(
|
|
1065
|
+
await fsp2.rm(opts.paths.projectTaskGraphs, { recursive: true, force: true });
|
|
1042
1066
|
} catch {
|
|
1043
1067
|
}
|
|
1044
1068
|
const cancelBuilder = sddState.getBuilder();
|
|
@@ -1058,8 +1082,8 @@ Start executing the tasks one by one.`
|
|
|
1058
1082
|
if (sddState.getBuilder()) {
|
|
1059
1083
|
return { message: "An SDD session is already active. Use /sdd cancel first." };
|
|
1060
1084
|
}
|
|
1061
|
-
const sessionPath =
|
|
1062
|
-
const projectContext = await gatherProjectContext(projectRoot);
|
|
1085
|
+
const sessionPath = opts.paths.projectSddSession;
|
|
1086
|
+
const projectContext = await gatherProjectContext(opts.context?.projectRoot ?? process.cwd());
|
|
1063
1087
|
sddState.setBuilder(new AISpecBuilder({
|
|
1064
1088
|
store: specStore,
|
|
1065
1089
|
projectContext,
|
|
@@ -1222,7 +1246,7 @@ ${lines.join("\n")}`
|
|
|
1222
1246
|
return { message: "No task graph found. Generate tasks first." };
|
|
1223
1247
|
}
|
|
1224
1248
|
try {
|
|
1225
|
-
const graphStore2 = new TaskGraphStore({ baseDir:
|
|
1249
|
+
const graphStore2 = new TaskGraphStore({ baseDir: opts.paths.projectTaskGraphs });
|
|
1226
1250
|
const graph = await graphStore2.load(graphId);
|
|
1227
1251
|
if (!graph) {
|
|
1228
1252
|
return { message: "Could not load task graph." };
|
|
@@ -1416,6 +1440,12 @@ async function findSpec(store, idOrTitle) {
|
|
|
1416
1440
|
if (match) return store.load(match.id);
|
|
1417
1441
|
return null;
|
|
1418
1442
|
}
|
|
1443
|
+
function getTaskGraphId() {
|
|
1444
|
+
return sddState.getTaskGraphId();
|
|
1445
|
+
}
|
|
1446
|
+
function getTaskTracker() {
|
|
1447
|
+
return sddState.getTaskTracker();
|
|
1448
|
+
}
|
|
1419
1449
|
var SDD_META_KEY, SDDState, sddState;
|
|
1420
1450
|
var init_sdd = __esm({
|
|
1421
1451
|
"src/slash-commands/sdd.ts"() {
|
|
@@ -1821,7 +1851,21 @@ async function runWebUI(opts) {
|
|
|
1821
1851
|
const client = { ws, sessionId: opts.session.id };
|
|
1822
1852
|
clients.set(ws, client);
|
|
1823
1853
|
console.log("[WebUI] Client connected");
|
|
1854
|
+
let msgCount = 0;
|
|
1855
|
+
let windowResetAt = Date.now() + 6e4;
|
|
1824
1856
|
ws.on("message", async (data) => {
|
|
1857
|
+
const now = Date.now();
|
|
1858
|
+
if (now > windowResetAt) {
|
|
1859
|
+
msgCount = 0;
|
|
1860
|
+
windowResetAt = now + 6e4;
|
|
1861
|
+
}
|
|
1862
|
+
if (++msgCount > 60) {
|
|
1863
|
+
send(ws, {
|
|
1864
|
+
type: "error",
|
|
1865
|
+
payload: { phase: "rate_limit", message: "Too many messages. Please wait." }
|
|
1866
|
+
});
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1825
1869
|
try {
|
|
1826
1870
|
const msg = JSON.parse(data.toString());
|
|
1827
1871
|
await handleMessage(ws, client, msg);
|
|
@@ -2743,7 +2787,7 @@ function maskConfigSecrets(cfg) {
|
|
|
2743
2787
|
if (typeof cfg !== "object" || cfg === null) return {};
|
|
2744
2788
|
const out = {};
|
|
2745
2789
|
for (const [k, v] of Object.entries(cfg)) {
|
|
2746
|
-
if (k
|
|
2790
|
+
if (isSecretField(k)) {
|
|
2747
2791
|
out[k] = "[REDACTED]";
|
|
2748
2792
|
} else if (typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
2749
2793
|
out[k] = maskConfigSecrets(v);
|
|
@@ -2760,7 +2804,7 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
2760
2804
|
const o = JSON.stringify(oldCfg[k]);
|
|
2761
2805
|
const n = JSON.stringify(newCfg[k]);
|
|
2762
2806
|
if (o !== n) {
|
|
2763
|
-
if (k
|
|
2807
|
+
if (isSecretField(k)) {
|
|
2764
2808
|
changes.push(`${k}: [CHANGED]`);
|
|
2765
2809
|
} else if (typeof newCfg[k] !== "object") {
|
|
2766
2810
|
changes.push(`${k}: ${oldCfg[k] ?? "(unset)"} \u2192 ${newCfg[k]}`);
|
|
@@ -4583,7 +4627,7 @@ function buildPlanCommand(opts) {
|
|
|
4583
4627
|
name: "plan",
|
|
4584
4628
|
description: "Strategic plan board: /plan [show|add <title>|start <id|#>|done <id|#>|remove <id|#>|promote <id|#> [subtask ...]|derive <id|#>|template [list|use <name>]|clear]",
|
|
4585
4629
|
async run(args) {
|
|
4586
|
-
const planPath = opts.planPath;
|
|
4630
|
+
const planPath = opts.paths?.projectPlan ?? opts.planPath;
|
|
4587
4631
|
if (!planPath) return { message: "Plan storage is not configured for this session." };
|
|
4588
4632
|
const ctx = opts.context;
|
|
4589
4633
|
const sessionId = ctx?.session.id ?? "unknown";
|
|
@@ -4825,11 +4869,27 @@ function buildSpawnCommand(opts) {
|
|
|
4825
4869
|
function buildAgentsCommand(opts) {
|
|
4826
4870
|
return {
|
|
4827
4871
|
name: "agents",
|
|
4828
|
-
description: "Show status of spawned subagents. /agents monitor opens the agents monitor overlay.",
|
|
4872
|
+
description: "Show status of spawned subagents. /agents monitor opens the agents monitor overlay. /agents on|off toggles the overlay.",
|
|
4873
|
+
help: [
|
|
4874
|
+
"Usage: /agents [monitor|on|off]",
|
|
4875
|
+
" /agents \u2014 show subagent status summary",
|
|
4876
|
+
" /agents monitor \u2014 open the agents monitor overlay",
|
|
4877
|
+
" /agents on \u2014 show the agents monitor overlay",
|
|
4878
|
+
" /agents off \u2014 hide the agents monitor overlay"
|
|
4879
|
+
].join("\n"),
|
|
4829
4880
|
async run(args) {
|
|
4881
|
+
const arg = args.trim().toLowerCase();
|
|
4882
|
+
if (arg === "monitor" || arg === "on") {
|
|
4883
|
+
opts.agentsMonitorController?.setVisible(true);
|
|
4884
|
+
return { message: "Agents monitor shown." };
|
|
4885
|
+
}
|
|
4886
|
+
if (arg === "off") {
|
|
4887
|
+
opts.agentsMonitorController?.setVisible(false);
|
|
4888
|
+
return { message: "Agents monitor hidden." };
|
|
4889
|
+
}
|
|
4830
4890
|
if (!opts.onAgents) return { message: "Multi-agent is not enabled in this session." };
|
|
4831
|
-
const subagentId =
|
|
4832
|
-
return { message: await opts.onAgents(subagentId
|
|
4891
|
+
const subagentId = arg || void 0;
|
|
4892
|
+
return { message: await opts.onAgents(subagentId) };
|
|
4833
4893
|
}
|
|
4834
4894
|
};
|
|
4835
4895
|
}
|
|
@@ -5164,7 +5224,7 @@ function buildGoalCommand(opts) {
|
|
|
5164
5224
|
"Stage flow: decide \u2192 execute \u2192 reflect \u2192 sleep | paused | stopped",
|
|
5165
5225
|
"Pausing stops after current iteration completes. Resume continues from next iteration.",
|
|
5166
5226
|
"",
|
|
5167
|
-
"Goals live in
|
|
5227
|
+
"Goals live in ~/.wrongstack/projects/<hash>/goal.json and persist across sessions.",
|
|
5168
5228
|
"A goal is the prerequisite for /autonomy eternal \u2014 the engine consults it on",
|
|
5169
5229
|
"every iteration to decide what to do next."
|
|
5170
5230
|
].join("\n"),
|
|
@@ -5173,7 +5233,7 @@ function buildGoalCommand(opts) {
|
|
|
5173
5233
|
const [verbRaw, ...rest] = trimmed.split(/\s+/);
|
|
5174
5234
|
const verb = (verbRaw ?? "").toLowerCase();
|
|
5175
5235
|
const restJoined = rest.join(" ").trim();
|
|
5176
|
-
const goalPath =
|
|
5236
|
+
const goalPath = opts.paths.projectGoal;
|
|
5177
5237
|
const verbForDispatch = verb && !KNOWN_VERBS.has(verb) ? "set" : verb;
|
|
5178
5238
|
const setText = verbForDispatch === "set" && !KNOWN_VERBS.has(verb) ? trimmed : restJoined;
|
|
5179
5239
|
switch (verbForDispatch) {
|
|
@@ -8429,8 +8489,7 @@ var initCmd = async (_args, deps) => {
|
|
|
8429
8489
|
await fsp2.mkdir(deps.paths.globalRoot, { recursive: true });
|
|
8430
8490
|
const config = { version: 1, provider: providerId, model: modelId };
|
|
8431
8491
|
if (apiKey) config.apiKey = apiKey;
|
|
8432
|
-
const
|
|
8433
|
-
const vault = new DefaultSecretVault$1({ keyFile });
|
|
8492
|
+
const vault = new DefaultSecretVault$1({ keyFile: deps.paths.secretsKey });
|
|
8434
8493
|
const encrypted = encryptConfigSecrets(config, vault);
|
|
8435
8494
|
await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2));
|
|
8436
8495
|
await fsp2.mkdir(path23.join(deps.projectRoot, ".wrongstack"), { recursive: true });
|
|
@@ -8880,14 +8939,12 @@ Cache age: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never fetched"}. Run
|
|
|
8880
8939
|
);
|
|
8881
8940
|
return 0;
|
|
8882
8941
|
};
|
|
8883
|
-
|
|
8884
|
-
// src/subcommands/handlers/helpers.ts
|
|
8885
8942
|
function redactKeys(obj) {
|
|
8886
8943
|
if (!obj || typeof obj !== "object") return obj;
|
|
8887
8944
|
if (Array.isArray(obj)) return obj.map(redactKeys);
|
|
8888
8945
|
const out = {};
|
|
8889
8946
|
for (const [k, v] of Object.entries(obj)) {
|
|
8890
|
-
if (
|
|
8947
|
+
if (isSecretField(k) && typeof v === "string" && v.length > 0)
|
|
8891
8948
|
out[k] = "[REDACTED]";
|
|
8892
8949
|
else out[k] = redactKeys(v);
|
|
8893
8950
|
}
|
|
@@ -10420,6 +10477,7 @@ async function execute(deps) {
|
|
|
10420
10477
|
fleetStreamController,
|
|
10421
10478
|
statuslineHiddenItems,
|
|
10422
10479
|
setStatuslineHiddenItems,
|
|
10480
|
+
agentsMonitorController,
|
|
10423
10481
|
getYolo,
|
|
10424
10482
|
getAutonomy,
|
|
10425
10483
|
onAutonomy,
|
|
@@ -10577,6 +10635,7 @@ async function execute(deps) {
|
|
|
10577
10635
|
fleetStreamController,
|
|
10578
10636
|
statuslineHiddenItems,
|
|
10579
10637
|
setStatuslineHiddenItems,
|
|
10638
|
+
agentsMonitorController,
|
|
10580
10639
|
initialGoal: goalFlag,
|
|
10581
10640
|
initialAsk: askFlag,
|
|
10582
10641
|
projectRoot,
|
|
@@ -11945,7 +12004,7 @@ async function main(argv) {
|
|
|
11945
12004
|
const skillLoader = container.resolve(TOKENS.SkillLoader);
|
|
11946
12005
|
const sessionRef = {};
|
|
11947
12006
|
const autonomyModeRef = { current: "off" };
|
|
11948
|
-
const goalPathForPrompt =
|
|
12007
|
+
const goalPathForPrompt = wpaths.projectGoal;
|
|
11949
12008
|
container.bind(
|
|
11950
12009
|
TOKENS.SystemPromptBuilder,
|
|
11951
12010
|
() => new DefaultSystemPromptBuilder({
|
|
@@ -12238,12 +12297,20 @@ async function main(argv) {
|
|
|
12238
12297
|
if (!hiddenItemsFromConfig[k]) hiddenItemsList.push(k);
|
|
12239
12298
|
}
|
|
12240
12299
|
const statuslineHiddenItems = hiddenItemsList;
|
|
12241
|
-
[...statuslineHiddenItems];
|
|
12300
|
+
let currentHiddenItems = [...statuslineHiddenItems];
|
|
12242
12301
|
const setStatuslineHiddenItems = (items) => {
|
|
12302
|
+
currentHiddenItems = items;
|
|
12303
|
+
};
|
|
12304
|
+
const agentsMonitorController = {
|
|
12305
|
+
visible: false,
|
|
12306
|
+
setVisible(visible) {
|
|
12307
|
+
this.visible = visible;
|
|
12308
|
+
}
|
|
12243
12309
|
};
|
|
12244
12310
|
const slashCmds = buildBuiltinSlashCommands({
|
|
12245
12311
|
registry: slashRegistry,
|
|
12246
12312
|
toolRegistry,
|
|
12313
|
+
paths: wpaths,
|
|
12247
12314
|
compactor: container.resolve(TOKENS.Compactor),
|
|
12248
12315
|
sessionStore,
|
|
12249
12316
|
skillLoader,
|
|
@@ -12261,6 +12328,9 @@ async function main(argv) {
|
|
|
12261
12328
|
llmProvider: provider,
|
|
12262
12329
|
llmModel: config.model,
|
|
12263
12330
|
statuslineConfig: statuslineConfigDeps,
|
|
12331
|
+
statuslineHiddenItems: [...currentHiddenItems],
|
|
12332
|
+
setStatuslineHiddenItems,
|
|
12333
|
+
agentsMonitorController,
|
|
12264
12334
|
onSpawn: async (description, spawnOpts) => {
|
|
12265
12335
|
const { subagentId, taskId } = await multiAgentHost.spawn(description, spawnOpts);
|
|
12266
12336
|
const tags = [];
|
|
@@ -12800,13 +12870,62 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
12800
12870
|
onDispatchClassify: makeProviderClassifier(
|
|
12801
12871
|
context.provider,
|
|
12802
12872
|
context.model
|
|
12803
|
-
)
|
|
12873
|
+
),
|
|
12874
|
+
onSddParallelRun: async (opts) => {
|
|
12875
|
+
const { SddParallelRun } = await import('@wrongstack/core');
|
|
12876
|
+
const sdd = await Promise.resolve().then(() => (init_sdd(), sdd_exports));
|
|
12877
|
+
const tracker = sdd.getTaskTracker();
|
|
12878
|
+
const builder = sdd.getActiveBuilder();
|
|
12879
|
+
if (!tracker || !builder) {
|
|
12880
|
+
return "No active SDD session with tasks. Use /sdd new to start one.";
|
|
12881
|
+
}
|
|
12882
|
+
const session2 = builder.getSession();
|
|
12883
|
+
if (session2.phase !== "executing" && session2.phase !== "task_review") {
|
|
12884
|
+
return `Cannot run parallel in phase "${session2.phase}". Use /sdd approve first.`;
|
|
12885
|
+
}
|
|
12886
|
+
const graphId = sdd.getTaskGraphId();
|
|
12887
|
+
const graphStore = new (await import('@wrongstack/core')).TaskGraphStore({
|
|
12888
|
+
baseDir: wpaths.projectTaskGraphs
|
|
12889
|
+
});
|
|
12890
|
+
const graph = graphId ? await graphStore.load(graphId) : null;
|
|
12891
|
+
if (!graph) {
|
|
12892
|
+
return "No task graph found for the current SDD session.";
|
|
12893
|
+
}
|
|
12894
|
+
const subagentFactory = multiAgentHost.makeSubagentFactory(config);
|
|
12895
|
+
const run = new SddParallelRun({
|
|
12896
|
+
tracker,
|
|
12897
|
+
graph,
|
|
12898
|
+
agent,
|
|
12899
|
+
projectRoot,
|
|
12900
|
+
parallelSlots: opts?.parallelSlots,
|
|
12901
|
+
subagentFactory,
|
|
12902
|
+
onProgress: (p) => {
|
|
12903
|
+
renderer.write(` \u2591 wave ${p.wave + 1} \xB7 ${p.completed}/${p.total} tasks \xB7 ${p.percent}% done
|
|
12904
|
+
`);
|
|
12905
|
+
}
|
|
12906
|
+
});
|
|
12907
|
+
globalThis.__sddParallelRun = run;
|
|
12908
|
+
const result = await run.run();
|
|
12909
|
+
delete globalThis.__sddParallelRun;
|
|
12910
|
+
const lines = [
|
|
12911
|
+
`SDD parallel run complete:`,
|
|
12912
|
+
` ${result.totalWaves} waves \xB7 ${result.totalCompleted} done \xB7 ${result.totalFailed} failed`,
|
|
12913
|
+
` ${(result.totalDurationMs / 1e3).toFixed(1)}s total`
|
|
12914
|
+
];
|
|
12915
|
+
if (result.deadlocked) lines.push(color.red(" \u26A0 deadlock \u2014 tasks blocked by failed tasks."));
|
|
12916
|
+
if (result.stopRequested) lines.push(color.yellow(" \u26A1 stopped by user."));
|
|
12917
|
+
return lines.join("\n");
|
|
12918
|
+
},
|
|
12919
|
+
onSddParallelStop: () => {
|
|
12920
|
+
const run = globalThis.__sddParallelRun;
|
|
12921
|
+
run?.stop();
|
|
12922
|
+
}
|
|
12804
12923
|
});
|
|
12805
12924
|
for (const cmd of slashCmds) slashRegistry.register(cmd);
|
|
12806
12925
|
const eternalFlag = typeof flags["eternal"] === "string" ? flags["eternal"].trim() : "";
|
|
12807
12926
|
if (eternalFlag.length > 0) {
|
|
12808
|
-
const { saveGoal: saveGoal2, emptyGoal: emptyGoal2, goalFilePath:
|
|
12809
|
-
const goalPath =
|
|
12927
|
+
const { saveGoal: saveGoal2, emptyGoal: emptyGoal2, goalFilePath: goalFilePath3, loadGoal: loadGoal4 } = await import('@wrongstack/core');
|
|
12928
|
+
const goalPath = goalFilePath3(projectRoot);
|
|
12810
12929
|
const prior = await loadGoal4(goalPath);
|
|
12811
12930
|
const next = prior ? { ...prior, goal: eternalFlag, setAt: (/* @__PURE__ */ new Date()).toISOString(), lastActivityAt: (/* @__PURE__ */ new Date()).toISOString() } : emptyGoal2(eternalFlag);
|
|
12812
12931
|
await saveGoal2(goalPath, next);
|