@wrongstack/cli 0.7.5 → 0.7.7
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 +305 -157
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import * as
|
|
2
|
+
import * as path24 from 'path';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import * as fsp2 from 'fs/promises';
|
|
5
5
|
import { readdir, readFile } from 'fs/promises';
|
|
6
|
-
import { color, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, createDelegateTool, FLEET_ROSTER, createMcpControlTool, EternalAutonomyEngine, DefaultLogger, DefaultModelsRegistry, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokens, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, AutoApprovePermissionPolicy, makeLLMClassifier, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, atomicWrite, DefaultPluginAPI, makeAgentSubagentRunner, NULL_FLEET_BUS, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, AGENTS_BY_PHASE, dispatchAgent, formatTodosList, emptyPlan, clearPlan, savePlan, formatPlanTemplates, getPlanTemplate, addPlanItem, formatPlan, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, renderProgress, renderTaskGraph, loadGoal, goalFilePath, summarizeUsage, saveGoal, emptyGoal, buildGoalPreamble, formatGoal, InputBuilder, FsError, ERROR_CODES, projectHash, WrongStackError, defaultOrchestrator, decryptConfigSecrets, encryptConfigSecrets as encryptConfigSecrets$1, SpecVersioning, ParallelEternalEngine, allServers as allServers$1 } from '@wrongstack/core';
|
|
6
|
+
import { color, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, createDelegateTool, FLEET_ROSTER, createMcpControlTool, EternalAutonomyEngine, DefaultLogger, DefaultModelsRegistry, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokens, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, AutoApprovePermissionPolicy, makeLLMClassifier, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, atomicWrite, DefaultPluginAPI, makeAgentSubagentRunner, NULL_FLEET_BUS, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, AGENTS_BY_PHASE, dispatchAgent, formatTodosList, emptyPlan, clearPlan, savePlan, formatPlanTemplates, getPlanTemplate, addPlanItem, formatPlan, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, renderProgress, renderTaskGraph, loadGoal, goalFilePath, summarizeUsage, saveGoal, emptyGoal, buildGoalPreamble, formatGoal, pendingBtwCount, setBtwNote, InputBuilder, FsError, ERROR_CODES, projectHash, WrongStackError, defaultOrchestrator, decryptConfigSecrets, encryptConfigSecrets as encryptConfigSecrets$1, SpecVersioning, ParallelEternalEngine, allServers as allServers$1 } from '@wrongstack/core';
|
|
7
7
|
import { createRequire } from 'module';
|
|
8
8
|
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';
|
|
16
16
|
import { createDefaultContainer, routeImagesForModel, readClipboardImage } from '@wrongstack/runtime';
|
|
17
17
|
import { builtinToolsPack, rememberTool, forgetTool } from '@wrongstack/tools';
|
|
18
18
|
import * as readline from 'readline';
|
|
19
|
+
import * as fs4 from 'fs';
|
|
20
|
+
import { writeFileSync } from 'fs';
|
|
19
21
|
import { spawn } from 'child_process';
|
|
20
22
|
import { SkillInstaller } from '@wrongstack/core/skills';
|
|
21
23
|
import { WrongStackACPServer } from '@wrongstack/acp/agent';
|
|
@@ -24,7 +26,6 @@ import { ACP_AGENTS, SubagentBudget } from '@wrongstack/core/coordination';
|
|
|
24
26
|
import { allServers } from '@wrongstack/core/infrastructure';
|
|
25
27
|
import { createToolVisionAdapters } from '@wrongstack/runtime/vision';
|
|
26
28
|
import { ToolExecutor } from '@wrongstack/core/execution';
|
|
27
|
-
import { writeFileSync } from 'fs';
|
|
28
29
|
|
|
29
30
|
var __defProp = Object.defineProperty;
|
|
30
31
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -64,8 +65,10 @@ __export(sdd_exports, {
|
|
|
64
65
|
getActiveSDDPhase: () => getActiveSDDPhase,
|
|
65
66
|
getCurrentExecutingContext: () => getCurrentExecutingContext,
|
|
66
67
|
getCurrentTask: () => getCurrentTask,
|
|
68
|
+
getTaskGraphId: () => getTaskGraphId,
|
|
67
69
|
getTaskListText: () => getTaskListText,
|
|
68
70
|
getTaskProgress: () => getTaskProgress,
|
|
71
|
+
getTaskTracker: () => getTaskTracker,
|
|
69
72
|
markTaskCompleted: () => markTaskCompleted,
|
|
70
73
|
renderTaskListWithProgress: () => renderTaskListWithProgress,
|
|
71
74
|
trySaveImplementationPlan: () => trySaveImplementationPlan,
|
|
@@ -380,10 +383,9 @@ function buildSddCommand(opts) {
|
|
|
380
383
|
name: "sdd",
|
|
381
384
|
description: "AI-driven SDD: /sdd [new|approve|execute|cancel|status|list|show|templates]",
|
|
382
385
|
async run(args) {
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
const
|
|
386
|
-
const graphsDir = path23.join(projectRoot, ".wrongstack", "task-graphs");
|
|
386
|
+
opts.context;
|
|
387
|
+
const specsDir = opts.paths.projectSpecs;
|
|
388
|
+
const graphsDir = opts.paths.projectTaskGraphs;
|
|
387
389
|
const specStore = new SpecStore({ baseDir: specsDir });
|
|
388
390
|
new TaskGraphStore({ baseDir: graphsDir });
|
|
389
391
|
const versioning = sddState.getVersioning();
|
|
@@ -399,10 +401,10 @@ function buildSddCommand(opts) {
|
|
|
399
401
|
const forceFlag = rest.includes("--force") || rest.includes("-f");
|
|
400
402
|
const title = rest.filter((a) => !a.startsWith("-")).join(" ").trim() || "Untitled Feature";
|
|
401
403
|
if (!sessionState.getBuilder() && !forceFlag) {
|
|
402
|
-
const sessionPath =
|
|
404
|
+
const sessionPath = opts.paths.projectSddSession;
|
|
403
405
|
try {
|
|
404
406
|
await fsp2.access(sessionPath);
|
|
405
|
-
const projectContext2 = await gatherProjectContext(projectRoot);
|
|
407
|
+
const projectContext2 = await gatherProjectContext(opts.context?.projectRoot ?? process.cwd());
|
|
406
408
|
const tempBuilder = new AISpecBuilder({
|
|
407
409
|
store: specStore,
|
|
408
410
|
projectContext: projectContext2,
|
|
@@ -428,13 +430,13 @@ function buildSddCommand(opts) {
|
|
|
428
430
|
}
|
|
429
431
|
}
|
|
430
432
|
sddState.clearTaskState();
|
|
431
|
-
const projectContext = await gatherProjectContext(projectRoot);
|
|
433
|
+
const projectContext = await gatherProjectContext(opts.context?.projectRoot ?? process.cwd());
|
|
432
434
|
sddState.setBuilder(new AISpecBuilder({
|
|
433
435
|
store: specStore,
|
|
434
436
|
projectContext,
|
|
435
437
|
minQuestions: 2,
|
|
436
438
|
maxQuestions: 10,
|
|
437
|
-
sessionPath:
|
|
439
|
+
sessionPath: opts.paths.projectSddSession
|
|
438
440
|
}));
|
|
439
441
|
sddState.setSessionStartTime(Date.now());
|
|
440
442
|
sddState.setPhaseStartTime(Date.now());
|
|
@@ -547,8 +549,16 @@ Start executing the tasks one by one.`
|
|
|
547
549
|
};
|
|
548
550
|
}
|
|
549
551
|
// ── Task Execution ─────────────────────────────────────────────────
|
|
550
|
-
case "
|
|
551
|
-
case "
|
|
552
|
+
case "run":
|
|
553
|
+
case "execute": {
|
|
554
|
+
if (opts.onSddParallelRun) {
|
|
555
|
+
const slotsArg = restJoined.trim();
|
|
556
|
+
const slots = slotsArg ? Number.parseInt(slotsArg, 10) : void 0;
|
|
557
|
+
const message = await opts.onSddParallelRun(
|
|
558
|
+
slots && Number.isFinite(slots) ? { parallelSlots: Math.min(16, Math.max(1, slots)) } : {}
|
|
559
|
+
);
|
|
560
|
+
return { message };
|
|
561
|
+
}
|
|
552
562
|
const runBuilder = sddState.getBuilder();
|
|
553
563
|
if (!runBuilder) {
|
|
554
564
|
return {
|
|
@@ -572,6 +582,21 @@ User message:
|
|
|
572
582
|
Start executing the tasks one by one.`
|
|
573
583
|
};
|
|
574
584
|
}
|
|
585
|
+
case "parallel": {
|
|
586
|
+
if (!opts.onSddParallelRun) {
|
|
587
|
+
return { message: "SDD parallel run is not available in this session." };
|
|
588
|
+
}
|
|
589
|
+
const slotsArg = restJoined.trim();
|
|
590
|
+
const slots = slotsArg ? Number.parseInt(slotsArg, 10) : void 0;
|
|
591
|
+
const message = await opts.onSddParallelRun(
|
|
592
|
+
slots && Number.isFinite(slots) ? { parallelSlots: Math.min(16, Math.max(1, slots)) } : {}
|
|
593
|
+
);
|
|
594
|
+
return { message };
|
|
595
|
+
}
|
|
596
|
+
case "stop": {
|
|
597
|
+
opts.onSddParallelStop?.();
|
|
598
|
+
return { message: "SDD parallel run stopped." };
|
|
599
|
+
}
|
|
575
600
|
case "plan":
|
|
576
601
|
case "impl": {
|
|
577
602
|
const planBuilder = sddState.getBuilder();
|
|
@@ -1001,7 +1026,7 @@ Start executing the tasks one by one.`
|
|
|
1001
1026
|
return { message: lines2.join("\n") };
|
|
1002
1027
|
}
|
|
1003
1028
|
try {
|
|
1004
|
-
const graphStore2 = new TaskGraphStore({ baseDir:
|
|
1029
|
+
const graphStore2 = new TaskGraphStore({ baseDir: opts.paths.projectTaskGraphs });
|
|
1005
1030
|
const stored = await graphStore2.load(graphId);
|
|
1006
1031
|
if (stored) {
|
|
1007
1032
|
return { message: renderTaskGraph(stored, { compact: false }) };
|
|
@@ -1026,7 +1051,7 @@ Start executing the tasks one by one.`
|
|
|
1026
1051
|
return { message: lines.join("\n") };
|
|
1027
1052
|
}
|
|
1028
1053
|
case "cancel": {
|
|
1029
|
-
const sessionPath =
|
|
1054
|
+
const sessionPath = opts.paths.projectSddSession;
|
|
1030
1055
|
let deletedFromDisk = false;
|
|
1031
1056
|
try {
|
|
1032
1057
|
await fsp2.unlink(sessionPath);
|
|
@@ -1034,11 +1059,11 @@ Start executing the tasks one by one.`
|
|
|
1034
1059
|
} catch {
|
|
1035
1060
|
}
|
|
1036
1061
|
try {
|
|
1037
|
-
await fsp2.rm(
|
|
1062
|
+
await fsp2.rm(opts.paths.projectSpecs, { recursive: true, force: true });
|
|
1038
1063
|
} catch {
|
|
1039
1064
|
}
|
|
1040
1065
|
try {
|
|
1041
|
-
await fsp2.rm(
|
|
1066
|
+
await fsp2.rm(opts.paths.projectTaskGraphs, { recursive: true, force: true });
|
|
1042
1067
|
} catch {
|
|
1043
1068
|
}
|
|
1044
1069
|
const cancelBuilder = sddState.getBuilder();
|
|
@@ -1058,8 +1083,8 @@ Start executing the tasks one by one.`
|
|
|
1058
1083
|
if (sddState.getBuilder()) {
|
|
1059
1084
|
return { message: "An SDD session is already active. Use /sdd cancel first." };
|
|
1060
1085
|
}
|
|
1061
|
-
const sessionPath =
|
|
1062
|
-
const projectContext = await gatherProjectContext(projectRoot);
|
|
1086
|
+
const sessionPath = opts.paths.projectSddSession;
|
|
1087
|
+
const projectContext = await gatherProjectContext(opts.context?.projectRoot ?? process.cwd());
|
|
1063
1088
|
sddState.setBuilder(new AISpecBuilder({
|
|
1064
1089
|
store: specStore,
|
|
1065
1090
|
projectContext,
|
|
@@ -1222,7 +1247,7 @@ ${lines.join("\n")}`
|
|
|
1222
1247
|
return { message: "No task graph found. Generate tasks first." };
|
|
1223
1248
|
}
|
|
1224
1249
|
try {
|
|
1225
|
-
const graphStore2 = new TaskGraphStore({ baseDir:
|
|
1250
|
+
const graphStore2 = new TaskGraphStore({ baseDir: opts.paths.projectTaskGraphs });
|
|
1226
1251
|
const graph = await graphStore2.load(graphId);
|
|
1227
1252
|
if (!graph) {
|
|
1228
1253
|
return { message: "Could not load task graph." };
|
|
@@ -1373,7 +1398,7 @@ function sddHelp() {
|
|
|
1373
1398
|
async function gatherProjectContext(projectRoot) {
|
|
1374
1399
|
const parts = [];
|
|
1375
1400
|
try {
|
|
1376
|
-
const pkgPath =
|
|
1401
|
+
const pkgPath = path24.join(projectRoot, "package.json");
|
|
1377
1402
|
const pkgRaw = await fsp2.readFile(pkgPath, "utf8");
|
|
1378
1403
|
const pkg = JSON.parse(pkgRaw);
|
|
1379
1404
|
parts.push(`Project: ${String(pkg.name ?? "unknown")}`);
|
|
@@ -1389,13 +1414,13 @@ async function gatherProjectContext(projectRoot) {
|
|
|
1389
1414
|
} catch {
|
|
1390
1415
|
}
|
|
1391
1416
|
try {
|
|
1392
|
-
const tsconfigPath =
|
|
1417
|
+
const tsconfigPath = path24.join(projectRoot, "tsconfig.json");
|
|
1393
1418
|
await fsp2.access(tsconfigPath);
|
|
1394
1419
|
parts.push("Language: TypeScript");
|
|
1395
1420
|
} catch {
|
|
1396
1421
|
}
|
|
1397
1422
|
try {
|
|
1398
|
-
const srcDir =
|
|
1423
|
+
const srcDir = path24.join(projectRoot, "src");
|
|
1399
1424
|
const entries = await fsp2.readdir(srcDir, { withFileTypes: true });
|
|
1400
1425
|
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1401
1426
|
if (dirs.length > 0) {
|
|
@@ -1416,6 +1441,12 @@ async function findSpec(store, idOrTitle) {
|
|
|
1416
1441
|
if (match) return store.load(match.id);
|
|
1417
1442
|
return null;
|
|
1418
1443
|
}
|
|
1444
|
+
function getTaskGraphId() {
|
|
1445
|
+
return sddState.getTaskGraphId();
|
|
1446
|
+
}
|
|
1447
|
+
function getTaskTracker() {
|
|
1448
|
+
return sddState.getTaskTracker();
|
|
1449
|
+
}
|
|
1419
1450
|
var SDD_META_KEY, SDDState, sddState;
|
|
1420
1451
|
var init_sdd = __esm({
|
|
1421
1452
|
"src/slash-commands/sdd.ts"() {
|
|
@@ -1545,7 +1576,7 @@ __export(update_check_exports, {
|
|
|
1545
1576
|
getUpdateNotification: () => getUpdateNotification
|
|
1546
1577
|
});
|
|
1547
1578
|
function cachePath(homeFn = defaultHomeDir2) {
|
|
1548
|
-
return
|
|
1579
|
+
return path24.join(homeFn(), ".wrongstack", "update-cache.json");
|
|
1549
1580
|
}
|
|
1550
1581
|
function currentVersion() {
|
|
1551
1582
|
const req2 = createRequire(import.meta.url);
|
|
@@ -1582,7 +1613,7 @@ async function readCache(homeFn = defaultHomeDir2) {
|
|
|
1582
1613
|
}
|
|
1583
1614
|
async function writeCache(entry, homeFn = defaultHomeDir2) {
|
|
1584
1615
|
try {
|
|
1585
|
-
const dir =
|
|
1616
|
+
const dir = path24.dirname(cachePath(homeFn));
|
|
1586
1617
|
await fsp2.mkdir(dir, { recursive: true });
|
|
1587
1618
|
await fsp2.writeFile(cachePath(homeFn), JSON.stringify(entry, null, 2), "utf8");
|
|
1588
1619
|
} catch {
|
|
@@ -1821,7 +1852,21 @@ async function runWebUI(opts) {
|
|
|
1821
1852
|
const client = { ws, sessionId: opts.session.id };
|
|
1822
1853
|
clients.set(ws, client);
|
|
1823
1854
|
console.log("[WebUI] Client connected");
|
|
1855
|
+
let msgCount = 0;
|
|
1856
|
+
let windowResetAt = Date.now() + 6e4;
|
|
1824
1857
|
ws.on("message", async (data) => {
|
|
1858
|
+
const now = Date.now();
|
|
1859
|
+
if (now > windowResetAt) {
|
|
1860
|
+
msgCount = 0;
|
|
1861
|
+
windowResetAt = now + 6e4;
|
|
1862
|
+
}
|
|
1863
|
+
if (++msgCount > 60) {
|
|
1864
|
+
send(ws, {
|
|
1865
|
+
type: "error",
|
|
1866
|
+
payload: { phase: "rate_limit", message: "Too many messages. Please wait." }
|
|
1867
|
+
});
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1825
1870
|
try {
|
|
1826
1871
|
const msg = JSON.parse(data.toString());
|
|
1827
1872
|
await handleMessage(ws, client, msg);
|
|
@@ -2172,7 +2217,7 @@ async function runWebUI(opts) {
|
|
|
2172
2217
|
return {};
|
|
2173
2218
|
}
|
|
2174
2219
|
if (!parsed.providers) return {};
|
|
2175
|
-
const keyFile =
|
|
2220
|
+
const keyFile = path24.join(path24.dirname(opts.globalConfigPath), ".key");
|
|
2176
2221
|
const vault = new DefaultSecretVault$1({ keyFile });
|
|
2177
2222
|
return decryptConfigSecrets$1(parsed.providers, vault);
|
|
2178
2223
|
}
|
|
@@ -2205,7 +2250,7 @@ async function runWebUI(opts) {
|
|
|
2205
2250
|
parsed = {};
|
|
2206
2251
|
}
|
|
2207
2252
|
parsed.providers = providers;
|
|
2208
|
-
const keyFile =
|
|
2253
|
+
const keyFile = path24.join(path24.dirname(opts.globalConfigPath), ".key");
|
|
2209
2254
|
const vault = new DefaultSecretVault$1({ keyFile });
|
|
2210
2255
|
const encrypted = encryptConfigSecrets(parsed, vault);
|
|
2211
2256
|
await atomicWrite(opts.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
@@ -2409,7 +2454,7 @@ function parseSpawnFlags(input) {
|
|
|
2409
2454
|
return { description: rest.trim(), opts };
|
|
2410
2455
|
}
|
|
2411
2456
|
async function bootConfig(flags) {
|
|
2412
|
-
const cwd = typeof flags["cwd"] === "string" ?
|
|
2457
|
+
const cwd = typeof flags["cwd"] === "string" ? path24.resolve(flags["cwd"]) : process.cwd();
|
|
2413
2458
|
const pathResolver = new DefaultPathResolver(cwd);
|
|
2414
2459
|
const projectRoot = pathResolver.projectRoot;
|
|
2415
2460
|
const userHome = os6.homedir();
|
|
@@ -2476,7 +2521,7 @@ var ReadlineInputReader = class {
|
|
|
2476
2521
|
history = [];
|
|
2477
2522
|
pending = false;
|
|
2478
2523
|
constructor(opts = {}) {
|
|
2479
|
-
this.historyFile = opts.historyFile ??
|
|
2524
|
+
this.historyFile = opts.historyFile ?? path24.join(os6.homedir(), ".wrongstack", "history");
|
|
2480
2525
|
}
|
|
2481
2526
|
async loadHistory() {
|
|
2482
2527
|
try {
|
|
@@ -2488,7 +2533,7 @@ var ReadlineInputReader = class {
|
|
|
2488
2533
|
}
|
|
2489
2534
|
async saveHistory() {
|
|
2490
2535
|
try {
|
|
2491
|
-
await fsp2.mkdir(
|
|
2536
|
+
await fsp2.mkdir(path24.dirname(this.historyFile), { recursive: true });
|
|
2492
2537
|
await fsp2.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
|
|
2493
2538
|
} catch {
|
|
2494
2539
|
}
|
|
@@ -2715,20 +2760,20 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
2715
2760
|
if (PROTECTED_BASENAMES.has(filename)) {
|
|
2716
2761
|
throw new Error(`Refusing to delete protected file: ${filename}`);
|
|
2717
2762
|
}
|
|
2718
|
-
if (filename !==
|
|
2763
|
+
if (filename !== path24.basename(filename)) {
|
|
2719
2764
|
throw new Error(`Refusing to delete path with traversal: ${filename}`);
|
|
2720
2765
|
}
|
|
2721
2766
|
if (!filename.startsWith("config.json.") || !filename.endsWith(".bak")) {
|
|
2722
2767
|
throw new Error(`Refusing to delete unknown file: ${filename}`);
|
|
2723
2768
|
}
|
|
2724
|
-
const resolvedParent =
|
|
2769
|
+
const resolvedParent = path24.resolve(parentDir);
|
|
2725
2770
|
if (!resolvedParent.endsWith(".wrongstack")) {
|
|
2726
2771
|
throw new Error(`Unexpected parent directory for bak prune: ${resolvedParent}`);
|
|
2727
2772
|
}
|
|
2728
2773
|
}
|
|
2729
2774
|
async function safeDelete(filePath) {
|
|
2730
|
-
const dir =
|
|
2731
|
-
const filename =
|
|
2775
|
+
const dir = path24.dirname(filePath);
|
|
2776
|
+
const filename = path24.basename(filePath);
|
|
2732
2777
|
try {
|
|
2733
2778
|
assertSafeToDelete(filename, dir);
|
|
2734
2779
|
await fsp2.unlink(filePath);
|
|
@@ -2743,7 +2788,7 @@ function maskConfigSecrets(cfg) {
|
|
|
2743
2788
|
if (typeof cfg !== "object" || cfg === null) return {};
|
|
2744
2789
|
const out = {};
|
|
2745
2790
|
for (const [k, v] of Object.entries(cfg)) {
|
|
2746
|
-
if (k
|
|
2791
|
+
if (isSecretField(k)) {
|
|
2747
2792
|
out[k] = "[REDACTED]";
|
|
2748
2793
|
} else if (typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
2749
2794
|
out[k] = maskConfigSecrets(v);
|
|
@@ -2760,7 +2805,7 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
2760
2805
|
const o = JSON.stringify(oldCfg[k]);
|
|
2761
2806
|
const n = JSON.stringify(newCfg[k]);
|
|
2762
2807
|
if (o !== n) {
|
|
2763
|
-
if (k
|
|
2808
|
+
if (isSecretField(k)) {
|
|
2764
2809
|
changes.push(`${k}: [CHANGED]`);
|
|
2765
2810
|
} else if (typeof newCfg[k] !== "object") {
|
|
2766
2811
|
changes.push(`${k}: ${oldCfg[k] ?? "(unset)"} \u2192 ${newCfg[k]}`);
|
|
@@ -2773,16 +2818,16 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
2773
2818
|
}
|
|
2774
2819
|
var defaultHomeDir = () => os6__default.homedir();
|
|
2775
2820
|
function historyDir(homeFn = defaultHomeDir) {
|
|
2776
|
-
return
|
|
2821
|
+
return path24.join(homeFn(), ".wrongstack", "config.history", "entries");
|
|
2777
2822
|
}
|
|
2778
2823
|
function historyIndexPath(homeFn = defaultHomeDir) {
|
|
2779
|
-
return
|
|
2824
|
+
return path24.join(homeFn(), ".wrongstack", "config.history", "index.json");
|
|
2780
2825
|
}
|
|
2781
2826
|
function configPath(homeFn = defaultHomeDir) {
|
|
2782
|
-
return
|
|
2827
|
+
return path24.join(homeFn(), ".wrongstack", "config.json");
|
|
2783
2828
|
}
|
|
2784
2829
|
function backupLastPath(homeFn = defaultHomeDir) {
|
|
2785
|
-
return
|
|
2830
|
+
return path24.join(homeFn(), ".wrongstack", "config.json.last");
|
|
2786
2831
|
}
|
|
2787
2832
|
function entryId(ts) {
|
|
2788
2833
|
return ts.replace(/[:.]/g, "-").slice(0, 19);
|
|
@@ -2837,17 +2882,17 @@ async function backupCurrent(homeFn = defaultHomeDir) {
|
|
|
2837
2882
|
}
|
|
2838
2883
|
if (content !== void 0) {
|
|
2839
2884
|
try {
|
|
2840
|
-
const bakPath =
|
|
2885
|
+
const bakPath = path24.join(homeFn(), ".wrongstack", `config.json.${ts}.bak`);
|
|
2841
2886
|
await atomicWrite(bakPath, content);
|
|
2842
2887
|
} catch {
|
|
2843
2888
|
}
|
|
2844
2889
|
}
|
|
2845
2890
|
try {
|
|
2846
|
-
const dir =
|
|
2891
|
+
const dir = path24.join(homeFn(), ".wrongstack");
|
|
2847
2892
|
const files = await fsp2.readdir(dir);
|
|
2848
2893
|
const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
|
|
2849
2894
|
for (const f of baks.slice(10)) {
|
|
2850
|
-
await safeDelete(
|
|
2895
|
+
await safeDelete(path24.join(dir, f));
|
|
2851
2896
|
}
|
|
2852
2897
|
} catch {
|
|
2853
2898
|
}
|
|
@@ -2865,7 +2910,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
2865
2910
|
};
|
|
2866
2911
|
try {
|
|
2867
2912
|
await fsp2.writeFile(
|
|
2868
|
-
|
|
2913
|
+
path24.join(historyDir(homeFn), `${id}.json`),
|
|
2869
2914
|
JSON.stringify(entry, null, 2),
|
|
2870
2915
|
"utf8"
|
|
2871
2916
|
);
|
|
@@ -2873,7 +2918,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
2873
2918
|
throw new FsError({
|
|
2874
2919
|
message: err instanceof Error ? err.message : String(err),
|
|
2875
2920
|
code: ERROR_CODES.FS_WRITE_FAILED,
|
|
2876
|
-
path:
|
|
2921
|
+
path: path24.join(historyDir(homeFn), `${id}.json`),
|
|
2877
2922
|
cause: err
|
|
2878
2923
|
});
|
|
2879
2924
|
}
|
|
@@ -2888,7 +2933,7 @@ async function listHistory(homeFn = defaultHomeDir) {
|
|
|
2888
2933
|
}
|
|
2889
2934
|
async function getHistoryEntry(id, homeFn = defaultHomeDir) {
|
|
2890
2935
|
try {
|
|
2891
|
-
const raw = await fsp2.readFile(
|
|
2936
|
+
const raw = await fsp2.readFile(path24.join(historyDir(homeFn), `${id}.json`), "utf8");
|
|
2892
2937
|
return JSON.parse(raw);
|
|
2893
2938
|
} catch {
|
|
2894
2939
|
return null;
|
|
@@ -2948,10 +2993,10 @@ var theme = { primary: color.amber };
|
|
|
2948
2993
|
async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? __require("os").homedir()) {
|
|
2949
2994
|
try {
|
|
2950
2995
|
const { atomicWrite: atomicWrite8 } = await import('@wrongstack/core');
|
|
2951
|
-
const
|
|
2996
|
+
const fs21 = await import('fs/promises');
|
|
2952
2997
|
let existing = {};
|
|
2953
2998
|
try {
|
|
2954
|
-
const raw = await
|
|
2999
|
+
const raw = await fs21.readFile(configPath2, "utf8");
|
|
2955
3000
|
existing = JSON.parse(raw);
|
|
2956
3001
|
} catch {
|
|
2957
3002
|
}
|
|
@@ -3240,6 +3285,7 @@ var GROUPS = [
|
|
|
3240
3285
|
items: [
|
|
3241
3286
|
{ key: "Esc (while busy)", blurb: "soft interrupt \u2014 next message carries a STEERING preamble" },
|
|
3242
3287
|
{ key: "/steer <text>", blurb: "mid-flight redirect, works when Esc is eaten by tmux" },
|
|
3288
|
+
{ key: "/btw <note>", blurb: "non-aborting nudge \u2014 agent folds it in at its next step" },
|
|
3243
3289
|
{ key: "Ctrl+C \xD7 1 / \xD7 2 / \xD7 3", blurb: "cancel iteration \xB7 force-exit Ink \xB7 hard exit(130)" }
|
|
3244
3290
|
]
|
|
3245
3291
|
},
|
|
@@ -3265,7 +3311,8 @@ var GROUPS = [
|
|
|
3265
3311
|
]
|
|
3266
3312
|
}
|
|
3267
3313
|
];
|
|
3268
|
-
|
|
3314
|
+
GROUPS.reduce((n, g) => n + g.items.length, 0);
|
|
3315
|
+
GROUPS.map((g) => g.title);
|
|
3269
3316
|
function shouldSuppress(flags) {
|
|
3270
3317
|
if (flags["no-hints"] === true) return true;
|
|
3271
3318
|
if (flags["hints"] === false) return true;
|
|
@@ -3273,22 +3320,41 @@ function shouldSuppress(flags) {
|
|
|
3273
3320
|
if (env && env !== "0" && env.toLowerCase() !== "false") return true;
|
|
3274
3321
|
return false;
|
|
3275
3322
|
}
|
|
3276
|
-
|
|
3323
|
+
var wrap = (n) => (n % GROUPS.length + GROUPS.length) % GROUPS.length;
|
|
3324
|
+
function pickGroupIndex(opts) {
|
|
3325
|
+
if (typeof opts.groupIndex === "number") return wrap(opts.groupIndex);
|
|
3326
|
+
if (opts.cursorFile) {
|
|
3327
|
+
try {
|
|
3328
|
+
let current = 0;
|
|
3329
|
+
try {
|
|
3330
|
+
const parsed = Number.parseInt(fs4.readFileSync(opts.cursorFile, "utf8").trim(), 10);
|
|
3331
|
+
if (Number.isFinite(parsed)) current = wrap(parsed);
|
|
3332
|
+
} catch {
|
|
3333
|
+
}
|
|
3334
|
+
fs4.mkdirSync(path24.dirname(opts.cursorFile), { recursive: true });
|
|
3335
|
+
fs4.writeFileSync(opts.cursorFile, String(wrap(current + 1)));
|
|
3336
|
+
return current;
|
|
3337
|
+
} catch {
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
return Math.floor(Math.random() * GROUPS.length);
|
|
3341
|
+
}
|
|
3342
|
+
function printLaunchHints(renderer, flags, opts = {}) {
|
|
3277
3343
|
if (shouldSuppress(flags)) return;
|
|
3344
|
+
const idx = pickGroupIndex(opts);
|
|
3345
|
+
const group = GROUPS[idx];
|
|
3346
|
+
if (!group) return;
|
|
3278
3347
|
const lines = [];
|
|
3279
3348
|
lines.push("");
|
|
3280
3349
|
lines.push(
|
|
3281
|
-
` ${color.cyan("\u25C6")} ${color.bold(`
|
|
3350
|
+
` ${color.cyan("\u25C6")} ${color.bold(group.title)} ${color.dim(`(${idx + 1}/${GROUPS.length} \xB7 more each launch)`)}`
|
|
3282
3351
|
);
|
|
3283
|
-
for (const
|
|
3284
|
-
lines.push(` ${color.dim("\
|
|
3285
|
-
for (const item of group.items) {
|
|
3286
|
-
lines.push(` ${color.bold(item.key)} ${color.dim("\u2014")} ${color.dim(item.blurb)}`);
|
|
3287
|
-
}
|
|
3352
|
+
for (const item of group.items) {
|
|
3353
|
+
lines.push(` ${color.bold(item.key)} ${color.dim("\u2014")} ${color.dim(item.blurb)}`);
|
|
3288
3354
|
}
|
|
3289
3355
|
lines.push("");
|
|
3290
3356
|
lines.push(
|
|
3291
|
-
` ${color.dim(
|
|
3357
|
+
` ${color.dim(`${color.bold("/help")} lists everything \xB7 hide with ${color.bold("--no-hints")}`)}`
|
|
3292
3358
|
);
|
|
3293
3359
|
lines.push("");
|
|
3294
3360
|
renderer.write(`${lines.join("\n")}
|
|
@@ -3307,10 +3373,10 @@ async function detectPackageManager(root, declared) {
|
|
|
3307
3373
|
const name = declared.split("@")[0];
|
|
3308
3374
|
if (name) return name;
|
|
3309
3375
|
}
|
|
3310
|
-
if (await pathExists(
|
|
3311
|
-
if (await pathExists(
|
|
3312
|
-
if (await pathExists(
|
|
3313
|
-
if (await pathExists(
|
|
3376
|
+
if (await pathExists(path24.join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
3377
|
+
if (await pathExists(path24.join(root, "bun.lockb"))) return "bun";
|
|
3378
|
+
if (await pathExists(path24.join(root, "bun.lock"))) return "bun";
|
|
3379
|
+
if (await pathExists(path24.join(root, "yarn.lock"))) return "yarn";
|
|
3314
3380
|
return "npm";
|
|
3315
3381
|
}
|
|
3316
3382
|
function hasUsableScript(scripts, name) {
|
|
@@ -3331,7 +3397,7 @@ function parseMakeTargets(makefile) {
|
|
|
3331
3397
|
async function detectProjectFacts(root) {
|
|
3332
3398
|
const facts = { hints: [] };
|
|
3333
3399
|
try {
|
|
3334
|
-
const pkg = JSON.parse(await fsp2.readFile(
|
|
3400
|
+
const pkg = JSON.parse(await fsp2.readFile(path24.join(root, "package.json"), "utf8"));
|
|
3335
3401
|
const scripts = pkg.scripts ?? {};
|
|
3336
3402
|
const pm = await detectPackageManager(root, pkg.packageManager);
|
|
3337
3403
|
if (hasUsableScript(scripts, "build")) facts.build = `${pm} run build`;
|
|
@@ -3345,14 +3411,14 @@ async function detectProjectFacts(root) {
|
|
|
3345
3411
|
} catch {
|
|
3346
3412
|
}
|
|
3347
3413
|
try {
|
|
3348
|
-
if (!await pathExists(
|
|
3414
|
+
if (!await pathExists(path24.join(root, "pyproject.toml"))) throw new Error("not python");
|
|
3349
3415
|
facts.test ??= "pytest";
|
|
3350
3416
|
facts.lint ??= "ruff check .";
|
|
3351
3417
|
facts.hints.push("pyproject.toml");
|
|
3352
3418
|
} catch {
|
|
3353
3419
|
}
|
|
3354
3420
|
try {
|
|
3355
|
-
if (!await pathExists(
|
|
3421
|
+
if (!await pathExists(path24.join(root, "go.mod"))) throw new Error("not go");
|
|
3356
3422
|
facts.build ??= "go build ./...";
|
|
3357
3423
|
facts.test ??= "go test ./...";
|
|
3358
3424
|
facts.run ??= "go run .";
|
|
@@ -3360,7 +3426,7 @@ async function detectProjectFacts(root) {
|
|
|
3360
3426
|
} catch {
|
|
3361
3427
|
}
|
|
3362
3428
|
try {
|
|
3363
|
-
if (!await pathExists(
|
|
3429
|
+
if (!await pathExists(path24.join(root, "Cargo.toml"))) throw new Error("not rust");
|
|
3364
3430
|
facts.build ??= "cargo build";
|
|
3365
3431
|
facts.test ??= "cargo test";
|
|
3366
3432
|
facts.lint ??= "cargo clippy";
|
|
@@ -3369,7 +3435,7 @@ async function detectProjectFacts(root) {
|
|
|
3369
3435
|
} catch {
|
|
3370
3436
|
}
|
|
3371
3437
|
try {
|
|
3372
|
-
const makefile = await fsp2.readFile(
|
|
3438
|
+
const makefile = await fsp2.readFile(path24.join(root, "Makefile"), "utf8");
|
|
3373
3439
|
const targets = parseMakeTargets(makefile);
|
|
3374
3440
|
facts.build ??= targets.has("build") ? "make build" : "make";
|
|
3375
3441
|
if (targets.has("test")) facts.test ??= "make test";
|
|
@@ -4261,8 +4327,8 @@ function buildInitCommand(opts) {
|
|
|
4261
4327
|
name: "init",
|
|
4262
4328
|
description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
|
|
4263
4329
|
async run(_args, ctx) {
|
|
4264
|
-
const dir =
|
|
4265
|
-
const file =
|
|
4330
|
+
const dir = path24.join(ctx.projectRoot, ".wrongstack");
|
|
4331
|
+
const file = path24.join(dir, "AGENTS.md");
|
|
4266
4332
|
const detected = await detectProjectFacts(ctx.projectRoot);
|
|
4267
4333
|
const body = renderAgentsTemplate(detected);
|
|
4268
4334
|
await fsp2.mkdir(dir, { recursive: true });
|
|
@@ -4453,18 +4519,18 @@ function stateBadge(state) {
|
|
|
4453
4519
|
return color.dim(state);
|
|
4454
4520
|
}
|
|
4455
4521
|
}
|
|
4456
|
-
async function readConfig(
|
|
4522
|
+
async function readConfig(path25) {
|
|
4457
4523
|
try {
|
|
4458
|
-
return JSON.parse(await fsp2.readFile(
|
|
4524
|
+
return JSON.parse(await fsp2.readFile(path25, "utf8"));
|
|
4459
4525
|
} catch {
|
|
4460
4526
|
return {};
|
|
4461
4527
|
}
|
|
4462
4528
|
}
|
|
4463
|
-
async function writeConfig(
|
|
4529
|
+
async function writeConfig(path25, cfg) {
|
|
4464
4530
|
const raw = JSON.stringify(cfg, null, 2);
|
|
4465
|
-
const tmp =
|
|
4531
|
+
const tmp = path25 + ".tmp";
|
|
4466
4532
|
await fsp2.writeFile(tmp, raw, "utf8");
|
|
4467
|
-
await fsp2.rename(tmp,
|
|
4533
|
+
await fsp2.rename(tmp, path25);
|
|
4468
4534
|
}
|
|
4469
4535
|
|
|
4470
4536
|
// src/slash-commands/mcp.ts
|
|
@@ -4583,7 +4649,7 @@ function buildPlanCommand(opts) {
|
|
|
4583
4649
|
name: "plan",
|
|
4584
4650
|
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
4651
|
async run(args) {
|
|
4586
|
-
const planPath = opts.planPath;
|
|
4652
|
+
const planPath = opts.paths?.projectPlan ?? opts.planPath;
|
|
4587
4653
|
if (!planPath) return { message: "Plan storage is not configured for this session." };
|
|
4588
4654
|
const ctx = opts.context;
|
|
4589
4655
|
const sessionId = ctx?.session.id ?? "unknown";
|
|
@@ -5150,6 +5216,38 @@ ${color.dim("YOLO forced ON. Use /autonomy stop to end. Journal at /goal journal
|
|
|
5150
5216
|
}
|
|
5151
5217
|
};
|
|
5152
5218
|
}
|
|
5219
|
+
function buildBtwCommand(opts) {
|
|
5220
|
+
return {
|
|
5221
|
+
name: "btw",
|
|
5222
|
+
description: 'Drop a "by the way" note for the running agent without interrupting it \u2014 delivered at the next step',
|
|
5223
|
+
argsHint: "<note>",
|
|
5224
|
+
help: [
|
|
5225
|
+
"/btw <note> Stash a note; the agent reads it at the start of its next",
|
|
5226
|
+
" iteration (between tool calls) without restarting.",
|
|
5227
|
+
"/btw Show how many notes are pending.",
|
|
5228
|
+
"",
|
|
5229
|
+
"Use `/steer` instead when you need to abort the current work immediately."
|
|
5230
|
+
].join("\n"),
|
|
5231
|
+
async run(args) {
|
|
5232
|
+
const ctx = opts.context;
|
|
5233
|
+
if (!ctx) {
|
|
5234
|
+
return { message: "No active session \u2014 start a turn first, then use /btw to nudge it." };
|
|
5235
|
+
}
|
|
5236
|
+
const text = args.trim();
|
|
5237
|
+
if (!text) {
|
|
5238
|
+
const n = pendingBtwCount(ctx);
|
|
5239
|
+
return {
|
|
5240
|
+
message: n === 0 ? "No notes pending. Usage: /btw <note>" : `${n} note(s) pending \u2014 will reach the agent at its next step.`
|
|
5241
|
+
};
|
|
5242
|
+
}
|
|
5243
|
+
const pending = setBtwNote(ctx, text);
|
|
5244
|
+
return {
|
|
5245
|
+
message: `\u21AF Noted (${pending} pending) \u2014 the agent will fold this in at its next step:
|
|
5246
|
+
${text}`
|
|
5247
|
+
};
|
|
5248
|
+
}
|
|
5249
|
+
};
|
|
5250
|
+
}
|
|
5153
5251
|
var KNOWN_VERBS = /* @__PURE__ */ new Set([
|
|
5154
5252
|
"",
|
|
5155
5253
|
"show",
|
|
@@ -5180,7 +5278,7 @@ function buildGoalCommand(opts) {
|
|
|
5180
5278
|
"Stage flow: decide \u2192 execute \u2192 reflect \u2192 sleep | paused | stopped",
|
|
5181
5279
|
"Pausing stops after current iteration completes. Resume continues from next iteration.",
|
|
5182
5280
|
"",
|
|
5183
|
-
"Goals live in
|
|
5281
|
+
"Goals live in ~/.wrongstack/projects/<hash>/goal.json and persist across sessions.",
|
|
5184
5282
|
"A goal is the prerequisite for /autonomy eternal \u2014 the engine consults it on",
|
|
5185
5283
|
"every iteration to decide what to do next."
|
|
5186
5284
|
].join("\n"),
|
|
@@ -5189,7 +5287,7 @@ function buildGoalCommand(opts) {
|
|
|
5189
5287
|
const [verbRaw, ...rest] = trimmed.split(/\s+/);
|
|
5190
5288
|
const verb = (verbRaw ?? "").toLowerCase();
|
|
5191
5289
|
const restJoined = rest.join(" ").trim();
|
|
5192
|
-
const goalPath =
|
|
5290
|
+
const goalPath = opts.paths.projectGoal;
|
|
5193
5291
|
const verbForDispatch = verb && !KNOWN_VERBS.has(verb) ? "set" : verb;
|
|
5194
5292
|
const setText = verbForDispatch === "set" && !KNOWN_VERBS.has(verb) ? trimmed : restJoined;
|
|
5195
5293
|
switch (verbForDispatch) {
|
|
@@ -5661,7 +5759,7 @@ var DEFAULTS = {
|
|
|
5661
5759
|
cost: true
|
|
5662
5760
|
};
|
|
5663
5761
|
function resolveConfigPath() {
|
|
5664
|
-
return process.env[CONFIG_ENV] ??
|
|
5762
|
+
return process.env[CONFIG_ENV] ?? path24.join(process.env.HOME ?? "", ".wrongstack", "statusline.json");
|
|
5665
5763
|
}
|
|
5666
5764
|
async function loadStatuslineConfig() {
|
|
5667
5765
|
const p = resolveConfigPath();
|
|
@@ -5675,7 +5773,7 @@ async function loadStatuslineConfig() {
|
|
|
5675
5773
|
async function saveStatuslineConfig(cfg) {
|
|
5676
5774
|
const p = resolveConfigPath();
|
|
5677
5775
|
try {
|
|
5678
|
-
await fsp2.mkdir(
|
|
5776
|
+
await fsp2.mkdir(path24.dirname(p), { recursive: true });
|
|
5679
5777
|
await atomicWrite(p, JSON.stringify(cfg, null, 2));
|
|
5680
5778
|
} catch (err) {
|
|
5681
5779
|
throw new FsError({
|
|
@@ -6574,11 +6672,11 @@ When the error confidence is low (< 0.85) or the problem spans multiple files,
|
|
|
6574
6672
|
};
|
|
6575
6673
|
}
|
|
6576
6674
|
function makeInstaller(opts, projectRoot, global) {
|
|
6577
|
-
const globalRoot =
|
|
6675
|
+
const globalRoot = path24.join(os6.homedir(), ".wrongstack");
|
|
6578
6676
|
return new SkillInstaller({
|
|
6579
|
-
manifestPath:
|
|
6580
|
-
projectSkillsDir:
|
|
6581
|
-
globalSkillsDir:
|
|
6677
|
+
manifestPath: path24.join(globalRoot, "installed-skills.json"),
|
|
6678
|
+
projectSkillsDir: path24.join(projectRoot, ".wrongstack", "skills"),
|
|
6679
|
+
globalSkillsDir: path24.join(globalRoot, "skills"),
|
|
6582
6680
|
projectHash: projectHash(projectRoot),
|
|
6583
6681
|
skillLoader: opts.skillLoader
|
|
6584
6682
|
});
|
|
@@ -6768,6 +6866,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
6768
6866
|
buildYoloCommand(opts),
|
|
6769
6867
|
buildAutonomyCommand(opts),
|
|
6770
6868
|
buildGoalCommand(opts),
|
|
6869
|
+
buildBtwCommand(opts),
|
|
6771
6870
|
buildModeCommand(opts),
|
|
6772
6871
|
buildExitCommand(opts),
|
|
6773
6872
|
buildCommitCommand(),
|
|
@@ -6802,13 +6901,13 @@ var MANIFESTS = [
|
|
|
6802
6901
|
];
|
|
6803
6902
|
async function detectProjectKind(projectRoot) {
|
|
6804
6903
|
try {
|
|
6805
|
-
await fsp2.access(
|
|
6904
|
+
await fsp2.access(path24.join(projectRoot, ".wrongstack", "AGENTS.md"));
|
|
6806
6905
|
return "initialized";
|
|
6807
6906
|
} catch {
|
|
6808
6907
|
}
|
|
6809
6908
|
for (const m of MANIFESTS) {
|
|
6810
6909
|
try {
|
|
6811
|
-
await fsp2.access(
|
|
6910
|
+
await fsp2.access(path24.join(projectRoot, m));
|
|
6812
6911
|
return "project";
|
|
6813
6912
|
} catch {
|
|
6814
6913
|
}
|
|
@@ -6816,8 +6915,8 @@ async function detectProjectKind(projectRoot) {
|
|
|
6816
6915
|
return "empty";
|
|
6817
6916
|
}
|
|
6818
6917
|
async function scaffoldAgentsMd(projectRoot) {
|
|
6819
|
-
const dir =
|
|
6820
|
-
const file =
|
|
6918
|
+
const dir = path24.join(projectRoot, ".wrongstack");
|
|
6919
|
+
const file = path24.join(dir, "AGENTS.md");
|
|
6821
6920
|
const facts = await detectProjectFacts(projectRoot);
|
|
6822
6921
|
const body = renderAgentsTemplate(facts);
|
|
6823
6922
|
await fsp2.mkdir(dir, { recursive: true });
|
|
@@ -6830,7 +6929,7 @@ async function runProjectCheck(opts) {
|
|
|
6830
6929
|
if (kind === "initialized") {
|
|
6831
6930
|
renderer.write(
|
|
6832
6931
|
`
|
|
6833
|
-
${color.green("\u2713")} Project initialized ${color.dim(`(${
|
|
6932
|
+
${color.green("\u2713")} Project initialized ${color.dim(`(${path24.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
|
|
6834
6933
|
`
|
|
6835
6934
|
);
|
|
6836
6935
|
return true;
|
|
@@ -6861,7 +6960,7 @@ async function runProjectCheck(opts) {
|
|
|
6861
6960
|
}
|
|
6862
6961
|
return true;
|
|
6863
6962
|
}
|
|
6864
|
-
const gitDir =
|
|
6963
|
+
const gitDir = path24.join(projectRoot, ".git");
|
|
6865
6964
|
let hasGit = false;
|
|
6866
6965
|
try {
|
|
6867
6966
|
await fsp2.access(gitDir);
|
|
@@ -7254,14 +7353,14 @@ function summarize(value, name) {
|
|
|
7254
7353
|
if (typeof v === "object" && v !== null) {
|
|
7255
7354
|
const o = v;
|
|
7256
7355
|
if (name === "edit") {
|
|
7257
|
-
const
|
|
7356
|
+
const path25 = typeof o["path"] === "string" ? o["path"] : "";
|
|
7258
7357
|
const reps = typeof o["replacements"] === "number" ? o["replacements"] : 0;
|
|
7259
|
-
return `${
|
|
7358
|
+
return `${path25} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
|
|
7260
7359
|
}
|
|
7261
7360
|
if (name === "write") {
|
|
7262
|
-
const
|
|
7361
|
+
const path25 = typeof o["path"] === "string" ? o["path"] : "";
|
|
7263
7362
|
const bytes = typeof o["bytes"] === "number" ? o["bytes"] : void 0;
|
|
7264
|
-
return bytes !== void 0 ? `${
|
|
7363
|
+
return bytes !== void 0 ? `${path25} ${bytes}B` : path25;
|
|
7265
7364
|
}
|
|
7266
7365
|
if (typeof o["count"] === "number") {
|
|
7267
7366
|
return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
|
|
@@ -8271,7 +8370,7 @@ var doctorCmd = async (_args, deps) => {
|
|
|
8271
8370
|
}
|
|
8272
8371
|
try {
|
|
8273
8372
|
await fsp2.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
8274
|
-
const probe =
|
|
8373
|
+
const probe = path24.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
8275
8374
|
await fsp2.writeFile(probe, "");
|
|
8276
8375
|
await fsp2.unlink(probe);
|
|
8277
8376
|
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
@@ -8374,8 +8473,8 @@ var exportCmd = async (args, deps) => {
|
|
|
8374
8473
|
return 1;
|
|
8375
8474
|
}
|
|
8376
8475
|
if (output) {
|
|
8377
|
-
await fsp2.mkdir(
|
|
8378
|
-
await fsp2.writeFile(
|
|
8476
|
+
await fsp2.mkdir(path24.dirname(path24.resolve(deps.cwd, output)), { recursive: true });
|
|
8477
|
+
await fsp2.writeFile(path24.resolve(deps.cwd, output), rendered, "utf8");
|
|
8379
8478
|
deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
|
|
8380
8479
|
`);
|
|
8381
8480
|
} else {
|
|
@@ -8445,12 +8544,11 @@ var initCmd = async (_args, deps) => {
|
|
|
8445
8544
|
await fsp2.mkdir(deps.paths.globalRoot, { recursive: true });
|
|
8446
8545
|
const config = { version: 1, provider: providerId, model: modelId };
|
|
8447
8546
|
if (apiKey) config.apiKey = apiKey;
|
|
8448
|
-
const
|
|
8449
|
-
const vault = new DefaultSecretVault$1({ keyFile });
|
|
8547
|
+
const vault = new DefaultSecretVault$1({ keyFile: deps.paths.secretsKey });
|
|
8450
8548
|
const encrypted = encryptConfigSecrets(config, vault);
|
|
8451
8549
|
await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2));
|
|
8452
|
-
await fsp2.mkdir(
|
|
8453
|
-
const agentsFile =
|
|
8550
|
+
await fsp2.mkdir(path24.join(deps.projectRoot, ".wrongstack"), { recursive: true });
|
|
8551
|
+
const agentsFile = path24.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
|
|
8454
8552
|
const projectFacts = await detectProjectFacts(deps.projectRoot);
|
|
8455
8553
|
await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
|
|
8456
8554
|
deps.renderer.writeInfo(`Wrote ${deps.paths.globalConfig}`);
|
|
@@ -8756,7 +8854,7 @@ var usageCmd = async (_args, deps) => {
|
|
|
8756
8854
|
return 0;
|
|
8757
8855
|
};
|
|
8758
8856
|
var projectsCmd = async (_args, deps) => {
|
|
8759
|
-
const projectsRoot =
|
|
8857
|
+
const projectsRoot = path24.join(deps.paths.globalRoot, "projects");
|
|
8760
8858
|
try {
|
|
8761
8859
|
const entries = await fsp2.readdir(projectsRoot);
|
|
8762
8860
|
if (entries.length === 0) {
|
|
@@ -8766,7 +8864,7 @@ var projectsCmd = async (_args, deps) => {
|
|
|
8766
8864
|
for (const hash of entries) {
|
|
8767
8865
|
try {
|
|
8768
8866
|
const meta = JSON.parse(
|
|
8769
|
-
await fsp2.readFile(
|
|
8867
|
+
await fsp2.readFile(path24.join(projectsRoot, hash, "meta.json"), "utf8")
|
|
8770
8868
|
);
|
|
8771
8869
|
deps.renderer.write(
|
|
8772
8870
|
` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
|
|
@@ -8896,14 +8994,12 @@ Cache age: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never fetched"}. Run
|
|
|
8896
8994
|
);
|
|
8897
8995
|
return 0;
|
|
8898
8996
|
};
|
|
8899
|
-
|
|
8900
|
-
// src/subcommands/handlers/helpers.ts
|
|
8901
8997
|
function redactKeys(obj) {
|
|
8902
8998
|
if (!obj || typeof obj !== "object") return obj;
|
|
8903
8999
|
if (Array.isArray(obj)) return obj.map(redactKeys);
|
|
8904
9000
|
const out = {};
|
|
8905
9001
|
for (const [k, v] of Object.entries(obj)) {
|
|
8906
|
-
if (
|
|
9002
|
+
if (isSecretField(k) && typeof v === "string" && v.length > 0)
|
|
8907
9003
|
out[k] = "[REDACTED]";
|
|
8908
9004
|
else out[k] = redactKeys(v);
|
|
8909
9005
|
}
|
|
@@ -8927,7 +9023,7 @@ async function listFleetRuns(deps) {
|
|
|
8927
9023
|
}
|
|
8928
9024
|
const runs = [];
|
|
8929
9025
|
for (const id of entries) {
|
|
8930
|
-
const runDir =
|
|
9026
|
+
const runDir = path24.join(deps.paths.projectSessions, id);
|
|
8931
9027
|
let stat3;
|
|
8932
9028
|
try {
|
|
8933
9029
|
stat3 = await fsp2.stat(runDir);
|
|
@@ -8940,17 +9036,17 @@ async function listFleetRuns(deps) {
|
|
|
8940
9036
|
let subagentCount = 0;
|
|
8941
9037
|
let subagentsDir;
|
|
8942
9038
|
try {
|
|
8943
|
-
await fsp2.access(
|
|
9039
|
+
await fsp2.access(path24.join(runDir, "fleet.json"));
|
|
8944
9040
|
manifest = true;
|
|
8945
9041
|
} catch {
|
|
8946
9042
|
}
|
|
8947
9043
|
try {
|
|
8948
|
-
await fsp2.access(
|
|
9044
|
+
await fsp2.access(path24.join(runDir, "checkpoint.json"));
|
|
8949
9045
|
checkpoint = true;
|
|
8950
9046
|
} catch {
|
|
8951
9047
|
}
|
|
8952
9048
|
try {
|
|
8953
|
-
subagentsDir =
|
|
9049
|
+
subagentsDir = path24.join(runDir, "subagents");
|
|
8954
9050
|
const files = await fsp2.readdir(subagentsDir);
|
|
8955
9051
|
subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
|
|
8956
9052
|
} catch {
|
|
@@ -8979,7 +9075,7 @@ async function listFleetRuns(deps) {
|
|
|
8979
9075
|
return 0;
|
|
8980
9076
|
}
|
|
8981
9077
|
async function showFleetRun(runId, deps) {
|
|
8982
|
-
const runDir =
|
|
9078
|
+
const runDir = path24.join(deps.paths.projectSessions, runId);
|
|
8983
9079
|
let stat3;
|
|
8984
9080
|
try {
|
|
8985
9081
|
stat3 = await fsp2.stat(runDir);
|
|
@@ -8996,7 +9092,7 @@ async function showFleetRun(runId, deps) {
|
|
|
8996
9092
|
deps.renderer.write(color.bold(`
|
|
8997
9093
|
Fleet Run: ${runId}
|
|
8998
9094
|
`) + "\n");
|
|
8999
|
-
const manifestPath =
|
|
9095
|
+
const manifestPath = path24.join(runDir, "fleet.json");
|
|
9000
9096
|
let manifestData = null;
|
|
9001
9097
|
try {
|
|
9002
9098
|
manifestData = await fsp2.readFile(manifestPath, "utf8");
|
|
@@ -9012,7 +9108,7 @@ Fleet Run: ${runId}
|
|
|
9012
9108
|
deps.renderer.write(` ${color.dim("\u25CB")} fleet.json \u2014 not found
|
|
9013
9109
|
`);
|
|
9014
9110
|
}
|
|
9015
|
-
const checkpointPath =
|
|
9111
|
+
const checkpointPath = path24.join(runDir, "checkpoint.json");
|
|
9016
9112
|
let checkpointData = null;
|
|
9017
9113
|
try {
|
|
9018
9114
|
checkpointData = await fsp2.readFile(checkpointPath, "utf8");
|
|
@@ -9059,7 +9155,7 @@ Fleet Run: ${runId}
|
|
|
9059
9155
|
} catch {
|
|
9060
9156
|
}
|
|
9061
9157
|
}
|
|
9062
|
-
const subagentsDir =
|
|
9158
|
+
const subagentsDir = path24.join(runDir, "subagents");
|
|
9063
9159
|
let subagentFiles = [];
|
|
9064
9160
|
try {
|
|
9065
9161
|
subagentFiles = await fsp2.readdir(subagentsDir);
|
|
@@ -9071,7 +9167,7 @@ Fleet Run: ${runId}
|
|
|
9071
9167
|
Subagent transcripts (${subagentFiles.length}):
|
|
9072
9168
|
`);
|
|
9073
9169
|
for (const f of subagentFiles.sort()) {
|
|
9074
|
-
const filePath =
|
|
9170
|
+
const filePath = path24.join(subagentsDir, f);
|
|
9075
9171
|
let size;
|
|
9076
9172
|
try {
|
|
9077
9173
|
const s = await fsp2.stat(filePath);
|
|
@@ -9088,7 +9184,7 @@ Fleet Run: ${runId}
|
|
|
9088
9184
|
${color.dim("\u25CB")} No subagent transcripts
|
|
9089
9185
|
`);
|
|
9090
9186
|
}
|
|
9091
|
-
const sharedDir =
|
|
9187
|
+
const sharedDir = path24.join(runDir, "shared");
|
|
9092
9188
|
try {
|
|
9093
9189
|
const files = await fsp2.readdir(sharedDir);
|
|
9094
9190
|
deps.renderer.write(`
|
|
@@ -9255,7 +9351,7 @@ function findSessionId(args) {
|
|
|
9255
9351
|
var rewindCmd = async (args, deps) => {
|
|
9256
9352
|
const flags = parseRewindFlags(args);
|
|
9257
9353
|
const wpaths = resolveWstackPaths({ projectRoot: deps.projectRoot });
|
|
9258
|
-
const sessionsDir =
|
|
9354
|
+
const sessionsDir = path24.join(wpaths.globalRoot, "sessions");
|
|
9259
9355
|
const rewind = new DefaultSessionRewinder(sessionsDir);
|
|
9260
9356
|
let sessionId = findSessionId(args);
|
|
9261
9357
|
if (!sessionId) {
|
|
@@ -9492,7 +9588,7 @@ function resolveBundledSkillsDir() {
|
|
|
9492
9588
|
try {
|
|
9493
9589
|
const req2 = createRequire(import.meta.url);
|
|
9494
9590
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
9495
|
-
return
|
|
9591
|
+
return path24.join(path24.dirname(corePkg), "skills");
|
|
9496
9592
|
} catch {
|
|
9497
9593
|
return void 0;
|
|
9498
9594
|
}
|
|
@@ -9623,7 +9719,9 @@ async function boot(argv) {
|
|
|
9623
9719
|
flags["no-tui"] = true;
|
|
9624
9720
|
}
|
|
9625
9721
|
if (choices.yolo !== config.yolo) config = patchConfig(config, { yolo: choices.yolo });
|
|
9626
|
-
printLaunchHints(renderer, flags
|
|
9722
|
+
printLaunchHints(renderer, flags, {
|
|
9723
|
+
cursorFile: path24.join(wpaths.cacheDir, "hint-cursor")
|
|
9724
|
+
});
|
|
9627
9725
|
}
|
|
9628
9726
|
return {
|
|
9629
9727
|
config,
|
|
@@ -10653,7 +10751,7 @@ async function execute(deps) {
|
|
|
10653
10751
|
supportsVision,
|
|
10654
10752
|
attachments,
|
|
10655
10753
|
effectiveMaxContext,
|
|
10656
|
-
projectName:
|
|
10754
|
+
projectName: path24.basename(projectRoot) || void 0,
|
|
10657
10755
|
projectRoot,
|
|
10658
10756
|
getAutonomy,
|
|
10659
10757
|
onAutonomy,
|
|
@@ -10675,7 +10773,7 @@ async function execute(deps) {
|
|
|
10675
10773
|
supportsVision,
|
|
10676
10774
|
attachments,
|
|
10677
10775
|
effectiveMaxContext,
|
|
10678
|
-
projectName:
|
|
10776
|
+
projectName: path24.basename(projectRoot) || void 0,
|
|
10679
10777
|
getAutonomy,
|
|
10680
10778
|
onAutonomy,
|
|
10681
10779
|
getEternalEngine,
|
|
@@ -10777,7 +10875,7 @@ var MultiAgentHost = class {
|
|
|
10777
10875
|
doneCondition: { type: "all_tasks_done" },
|
|
10778
10876
|
maxConcurrent: this.opts.maxConcurrent ?? 4
|
|
10779
10877
|
};
|
|
10780
|
-
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ?
|
|
10878
|
+
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path24.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
|
|
10781
10879
|
this.director = new Director({
|
|
10782
10880
|
config: coordinatorConfig,
|
|
10783
10881
|
manifestPath: this.opts.manifestPath,
|
|
@@ -11022,7 +11120,7 @@ var MultiAgentHost = class {
|
|
|
11022
11120
|
model: opts?.model,
|
|
11023
11121
|
tools: opts?.tools
|
|
11024
11122
|
};
|
|
11025
|
-
const transcriptPath = this.sessionFactory ?
|
|
11123
|
+
const transcriptPath = this.sessionFactory ? path24.join(this.sessionFactory.dir, `${subagentConfig.name}.jsonl`) : void 0;
|
|
11026
11124
|
const { subagentId, taskId } = await this._spawnAndAssign(subagentConfig);
|
|
11027
11125
|
this.fleetManager?.addPendingTask(taskId, subagentId, description);
|
|
11028
11126
|
this.deps.events.emit("subagent.spawned", {
|
|
@@ -11165,16 +11263,16 @@ var MultiAgentHost = class {
|
|
|
11165
11263
|
if (this.director) return this.director;
|
|
11166
11264
|
this.opts.directorMode = true;
|
|
11167
11265
|
if (this.opts.fleetRoot && !this.opts.manifestPath) {
|
|
11168
|
-
this.opts.manifestPath =
|
|
11266
|
+
this.opts.manifestPath = path24.join(this.opts.fleetRoot, "fleet.json");
|
|
11169
11267
|
}
|
|
11170
11268
|
if (this.opts.fleetRoot && !this.opts.sharedScratchpadPath) {
|
|
11171
|
-
this.opts.sharedScratchpadPath =
|
|
11269
|
+
this.opts.sharedScratchpadPath = path24.join(this.opts.fleetRoot, "shared");
|
|
11172
11270
|
}
|
|
11173
11271
|
if (this.opts.fleetRoot && !this.opts.sessionsRoot) {
|
|
11174
|
-
this.opts.sessionsRoot =
|
|
11272
|
+
this.opts.sessionsRoot = path24.join(this.opts.fleetRoot, "subagents");
|
|
11175
11273
|
}
|
|
11176
11274
|
if (this.opts.fleetRoot && !this.opts.stateCheckpointPath) {
|
|
11177
|
-
this.opts.stateCheckpointPath =
|
|
11275
|
+
this.opts.stateCheckpointPath = path24.join(this.opts.fleetRoot, "director-state.json");
|
|
11178
11276
|
}
|
|
11179
11277
|
await this.ensureDirector();
|
|
11180
11278
|
return this.director ?? null;
|
|
@@ -11340,11 +11438,11 @@ var SessionStats = class {
|
|
|
11340
11438
|
if (e.name === "bash") this.bashCommands++;
|
|
11341
11439
|
else if (e.name === "fetch") this.fetches++;
|
|
11342
11440
|
if (!e.ok) return;
|
|
11343
|
-
const
|
|
11344
|
-
if (e.name === "read" &&
|
|
11345
|
-
else if (e.name === "edit" &&
|
|
11346
|
-
else if (e.name === "write" &&
|
|
11347
|
-
this.writtenPaths.add(
|
|
11441
|
+
const path25 = typeof input?.path === "string" ? input.path : void 0;
|
|
11442
|
+
if (e.name === "read" && path25) this.readPaths.add(path25);
|
|
11443
|
+
else if (e.name === "edit" && path25) this.editedPaths.add(path25);
|
|
11444
|
+
else if (e.name === "write" && path25) {
|
|
11445
|
+
this.writtenPaths.add(path25);
|
|
11348
11446
|
const content = typeof input?.content === "string" ? input.content : "";
|
|
11349
11447
|
this.bytesWritten += Buffer.byteLength(content, "utf8");
|
|
11350
11448
|
}
|
|
@@ -11632,7 +11730,7 @@ function setupMetrics(params) {
|
|
|
11632
11730
|
const dumpMetrics = () => {
|
|
11633
11731
|
if (!metricsSink) return;
|
|
11634
11732
|
try {
|
|
11635
|
-
const out =
|
|
11733
|
+
const out = path24.join(wpaths.projectSessions, "metrics.json");
|
|
11636
11734
|
const snap = metricsSink.snapshot();
|
|
11637
11735
|
writeFileSync(out, JSON.stringify(snap, null, 2));
|
|
11638
11736
|
} catch {
|
|
@@ -11823,12 +11921,12 @@ async function setupSession(params) {
|
|
|
11823
11921
|
}
|
|
11824
11922
|
const sessionRef = { current: session };
|
|
11825
11923
|
await recoveryLock.write(session.id).catch(() => void 0);
|
|
11826
|
-
const attachments = new DefaultAttachmentStore({ spoolDir:
|
|
11827
|
-
const queueStore = new QueueStore({ dir:
|
|
11924
|
+
const attachments = new DefaultAttachmentStore({ spoolDir: path24.join(wpaths.projectSessions, session.id, "attachments") });
|
|
11925
|
+
const queueStore = new QueueStore({ dir: path24.join(wpaths.projectSessions, session.id) });
|
|
11828
11926
|
const ctxSignal = new AbortController().signal;
|
|
11829
11927
|
const context = new Context({ systemPrompt, provider, session, signal: ctxSignal, tokenCounter, cwd, projectRoot, model: config.model });
|
|
11830
11928
|
if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
|
|
11831
|
-
const todosCheckpointPath =
|
|
11929
|
+
const todosCheckpointPath = path24.join(wpaths.projectSessions, `${session.id}.todos.json`);
|
|
11832
11930
|
if (resumeId) {
|
|
11833
11931
|
try {
|
|
11834
11932
|
const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
|
|
@@ -11840,13 +11938,13 @@ async function setupSession(params) {
|
|
|
11840
11938
|
}
|
|
11841
11939
|
}
|
|
11842
11940
|
const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session.id);
|
|
11843
|
-
const planPath =
|
|
11941
|
+
const planPath = path24.join(wpaths.projectSessions, `${session.id}.plan.json`);
|
|
11844
11942
|
context.state.setMeta("plan.path", planPath);
|
|
11845
11943
|
let dirState;
|
|
11846
11944
|
if (resumeId) {
|
|
11847
11945
|
try {
|
|
11848
|
-
const fleetRoot =
|
|
11849
|
-
dirState = await loadDirectorState(
|
|
11946
|
+
const fleetRoot = path24.join(wpaths.projectSessions, session.id);
|
|
11947
|
+
dirState = await loadDirectorState(path24.join(fleetRoot, "director-state.json"));
|
|
11850
11948
|
if (dirState) {
|
|
11851
11949
|
const tCounts = {};
|
|
11852
11950
|
for (const t of dirState.tasks) tCounts[t.status] = (tCounts[t.status] ?? 0) + 1;
|
|
@@ -11873,7 +11971,7 @@ function resolveBundledSkillsDir2() {
|
|
|
11873
11971
|
try {
|
|
11874
11972
|
const req2 = createRequire(import.meta.url);
|
|
11875
11973
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
11876
|
-
return
|
|
11974
|
+
return path24.join(path24.dirname(corePkg), "skills");
|
|
11877
11975
|
} catch {
|
|
11878
11976
|
return void 0;
|
|
11879
11977
|
}
|
|
@@ -11963,7 +12061,7 @@ async function main(argv) {
|
|
|
11963
12061
|
const skillLoader = container.resolve(TOKENS.SkillLoader);
|
|
11964
12062
|
const sessionRef = {};
|
|
11965
12063
|
const autonomyModeRef = { current: "off" };
|
|
11966
|
-
const goalPathForPrompt =
|
|
12064
|
+
const goalPathForPrompt = wpaths.projectGoal;
|
|
11967
12065
|
container.bind(
|
|
11968
12066
|
TOKENS.SystemPromptBuilder,
|
|
11969
12067
|
() => new DefaultSystemPromptBuilder({
|
|
@@ -11973,7 +12071,7 @@ async function main(argv) {
|
|
|
11973
12071
|
modeId,
|
|
11974
12072
|
modePrompt,
|
|
11975
12073
|
modelCapabilities,
|
|
11976
|
-
planPath: () => sessionRef.current ?
|
|
12074
|
+
planPath: () => sessionRef.current ? path24.join(wpaths.projectSessions, `${sessionRef.current.id}.plan.json`) : void 0,
|
|
11977
12075
|
contributors: [
|
|
11978
12076
|
// Injects the ETERNAL AUTONOMY block when the user has activated
|
|
11979
12077
|
// `/autonomy eternal`. Without this, the per-iteration directive
|
|
@@ -12172,12 +12270,12 @@ async function main(argv) {
|
|
|
12172
12270
|
}
|
|
12173
12271
|
}
|
|
12174
12272
|
};
|
|
12175
|
-
const fleetRoot = directorMode ?
|
|
12176
|
-
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] :
|
|
12177
|
-
const sharedScratchpadPath = directorMode ?
|
|
12178
|
-
const subagentSessionsRoot = directorMode ?
|
|
12179
|
-
const stateCheckpointPath = directorMode ?
|
|
12180
|
-
const fleetRootForPromotion =
|
|
12273
|
+
const fleetRoot = directorMode ? path24.join(wpaths.projectSessions, session.id) : void 0;
|
|
12274
|
+
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path24.join(fleetRoot, "fleet.json") : void 0;
|
|
12275
|
+
const sharedScratchpadPath = directorMode ? path24.join(fleetRoot, "shared") : void 0;
|
|
12276
|
+
const subagentSessionsRoot = directorMode ? path24.join(fleetRoot, "subagents") : void 0;
|
|
12277
|
+
const stateCheckpointPath = directorMode ? path24.join(fleetRoot, "director-state.json") : void 0;
|
|
12278
|
+
const fleetRootForPromotion = path24.join(wpaths.projectSessions, session.id);
|
|
12181
12279
|
const multiAgentHost = new MultiAgentHost(
|
|
12182
12280
|
{
|
|
12183
12281
|
container,
|
|
@@ -12269,6 +12367,7 @@ async function main(argv) {
|
|
|
12269
12367
|
const slashCmds = buildBuiltinSlashCommands({
|
|
12270
12368
|
registry: slashRegistry,
|
|
12271
12369
|
toolRegistry,
|
|
12370
|
+
paths: wpaths,
|
|
12272
12371
|
compactor: container.resolve(TOKENS.Compactor),
|
|
12273
12372
|
sessionStore,
|
|
12274
12373
|
skillLoader,
|
|
@@ -12486,7 +12585,7 @@ async function main(argv) {
|
|
|
12486
12585
|
return director.spawn(cfg);
|
|
12487
12586
|
},
|
|
12488
12587
|
onFleetLog: async (subagentId, mode) => {
|
|
12489
|
-
const subagentsRoot =
|
|
12588
|
+
const subagentsRoot = path24.join(fleetRootForPromotion, "subagents");
|
|
12490
12589
|
let runDirs;
|
|
12491
12590
|
try {
|
|
12492
12591
|
runDirs = await fsp2.readdir(subagentsRoot);
|
|
@@ -12495,7 +12594,7 @@ async function main(argv) {
|
|
|
12495
12594
|
}
|
|
12496
12595
|
const found = [];
|
|
12497
12596
|
for (const runId of runDirs) {
|
|
12498
|
-
const runDir =
|
|
12597
|
+
const runDir = path24.join(subagentsRoot, runId);
|
|
12499
12598
|
let files;
|
|
12500
12599
|
try {
|
|
12501
12600
|
files = await fsp2.readdir(runDir);
|
|
@@ -12504,7 +12603,7 @@ async function main(argv) {
|
|
|
12504
12603
|
}
|
|
12505
12604
|
for (const f of files) {
|
|
12506
12605
|
if (!f.endsWith(".jsonl")) continue;
|
|
12507
|
-
const full =
|
|
12606
|
+
const full = path24.join(runDir, f);
|
|
12508
12607
|
try {
|
|
12509
12608
|
const stat3 = await fsp2.stat(full);
|
|
12510
12609
|
found.push({
|
|
@@ -12601,7 +12700,7 @@ async function main(argv) {
|
|
|
12601
12700
|
}
|
|
12602
12701
|
const dir = await multiAgentHost.ensureDirector();
|
|
12603
12702
|
if (!dir) return "Director is not available.";
|
|
12604
|
-
const dirStatePath =
|
|
12703
|
+
const dirStatePath = path24.join(fleetRootForPromotion, "director-state.json");
|
|
12605
12704
|
const prior = await loadDirectorState(dirStatePath);
|
|
12606
12705
|
if (!prior) {
|
|
12607
12706
|
return "No prior director-state.json found \u2014 nothing to retry.";
|
|
@@ -12672,9 +12771,9 @@ async function main(argv) {
|
|
|
12672
12771
|
for (const tool of director2.tools(FLEET_ROSTER)) {
|
|
12673
12772
|
toolRegistry.register(tool);
|
|
12674
12773
|
}
|
|
12675
|
-
const mp =
|
|
12676
|
-
const sp =
|
|
12677
|
-
const ss =
|
|
12774
|
+
const mp = path24.join(fleetRootForPromotion, "fleet.json");
|
|
12775
|
+
const sp = path24.join(fleetRootForPromotion, "shared");
|
|
12776
|
+
const ss = path24.join(fleetRootForPromotion, "subagents");
|
|
12678
12777
|
const lines = [
|
|
12679
12778
|
`${color.green("\u2713")} Promoted to director mode.`,
|
|
12680
12779
|
` Roster: ${Object.keys(FLEET_ROSTER).join(", ")}`,
|
|
@@ -12828,13 +12927,62 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
12828
12927
|
onDispatchClassify: makeProviderClassifier(
|
|
12829
12928
|
context.provider,
|
|
12830
12929
|
context.model
|
|
12831
|
-
)
|
|
12930
|
+
),
|
|
12931
|
+
onSddParallelRun: async (opts) => {
|
|
12932
|
+
const { SddParallelRun } = await import('@wrongstack/core');
|
|
12933
|
+
const sdd = await Promise.resolve().then(() => (init_sdd(), sdd_exports));
|
|
12934
|
+
const tracker = sdd.getTaskTracker();
|
|
12935
|
+
const builder = sdd.getActiveBuilder();
|
|
12936
|
+
if (!tracker || !builder) {
|
|
12937
|
+
return "No active SDD session with tasks. Use /sdd new to start one.";
|
|
12938
|
+
}
|
|
12939
|
+
const session2 = builder.getSession();
|
|
12940
|
+
if (session2.phase !== "executing" && session2.phase !== "task_review") {
|
|
12941
|
+
return `Cannot run parallel in phase "${session2.phase}". Use /sdd approve first.`;
|
|
12942
|
+
}
|
|
12943
|
+
const graphId = sdd.getTaskGraphId();
|
|
12944
|
+
const graphStore = new (await import('@wrongstack/core')).TaskGraphStore({
|
|
12945
|
+
baseDir: wpaths.projectTaskGraphs
|
|
12946
|
+
});
|
|
12947
|
+
const graph = graphId ? await graphStore.load(graphId) : null;
|
|
12948
|
+
if (!graph) {
|
|
12949
|
+
return "No task graph found for the current SDD session.";
|
|
12950
|
+
}
|
|
12951
|
+
const subagentFactory = multiAgentHost.makeSubagentFactory(config);
|
|
12952
|
+
const run = new SddParallelRun({
|
|
12953
|
+
tracker,
|
|
12954
|
+
graph,
|
|
12955
|
+
agent,
|
|
12956
|
+
projectRoot,
|
|
12957
|
+
parallelSlots: opts?.parallelSlots,
|
|
12958
|
+
subagentFactory,
|
|
12959
|
+
onProgress: (p) => {
|
|
12960
|
+
renderer.write(` \u2591 wave ${p.wave + 1} \xB7 ${p.completed}/${p.total} tasks \xB7 ${p.percent}% done
|
|
12961
|
+
`);
|
|
12962
|
+
}
|
|
12963
|
+
});
|
|
12964
|
+
globalThis.__sddParallelRun = run;
|
|
12965
|
+
const result = await run.run();
|
|
12966
|
+
delete globalThis.__sddParallelRun;
|
|
12967
|
+
const lines = [
|
|
12968
|
+
`SDD parallel run complete:`,
|
|
12969
|
+
` ${result.totalWaves} waves \xB7 ${result.totalCompleted} done \xB7 ${result.totalFailed} failed`,
|
|
12970
|
+
` ${(result.totalDurationMs / 1e3).toFixed(1)}s total`
|
|
12971
|
+
];
|
|
12972
|
+
if (result.deadlocked) lines.push(color.red(" \u26A0 deadlock \u2014 tasks blocked by failed tasks."));
|
|
12973
|
+
if (result.stopRequested) lines.push(color.yellow(" \u26A1 stopped by user."));
|
|
12974
|
+
return lines.join("\n");
|
|
12975
|
+
},
|
|
12976
|
+
onSddParallelStop: () => {
|
|
12977
|
+
const run = globalThis.__sddParallelRun;
|
|
12978
|
+
run?.stop();
|
|
12979
|
+
}
|
|
12832
12980
|
});
|
|
12833
12981
|
for (const cmd of slashCmds) slashRegistry.register(cmd);
|
|
12834
12982
|
const eternalFlag = typeof flags["eternal"] === "string" ? flags["eternal"].trim() : "";
|
|
12835
12983
|
if (eternalFlag.length > 0) {
|
|
12836
|
-
const { saveGoal: saveGoal2, emptyGoal: emptyGoal2, goalFilePath:
|
|
12837
|
-
const goalPath =
|
|
12984
|
+
const { saveGoal: saveGoal2, emptyGoal: emptyGoal2, goalFilePath: goalFilePath3, loadGoal: loadGoal4 } = await import('@wrongstack/core');
|
|
12985
|
+
const goalPath = goalFilePath3(projectRoot);
|
|
12838
12986
|
const prior = await loadGoal4(goalPath);
|
|
12839
12987
|
const next = prior ? { ...prior, goal: eternalFlag, setAt: (/* @__PURE__ */ new Date()).toISOString(), lastActivityAt: (/* @__PURE__ */ new Date()).toISOString() } : emptyGoal2(eternalFlag);
|
|
12840
12988
|
await saveGoal2(goalPath, next);
|