@wrongstack/cli 0.8.2 → 0.8.5
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 +545 -182
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import * as
|
|
2
|
+
import * as path25 from 'path';
|
|
3
3
|
import { join } from 'path';
|
|
4
|
-
import * as
|
|
4
|
+
import * as fsp3 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, pendingBtwCount, setBtwNote, 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, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, PhaseOrchestrator, 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';
|
|
@@ -403,7 +403,7 @@ function buildSddCommand(opts) {
|
|
|
403
403
|
if (!sessionState.getBuilder() && !forceFlag) {
|
|
404
404
|
const sessionPath = opts.paths.projectSddSession;
|
|
405
405
|
try {
|
|
406
|
-
await
|
|
406
|
+
await fsp3.access(sessionPath);
|
|
407
407
|
const projectContext2 = await gatherProjectContext(opts.context?.projectRoot ?? process.cwd());
|
|
408
408
|
const tempBuilder = new AISpecBuilder({
|
|
409
409
|
store: specStore,
|
|
@@ -1054,16 +1054,16 @@ Start executing the tasks one by one.`
|
|
|
1054
1054
|
const sessionPath = opts.paths.projectSddSession;
|
|
1055
1055
|
let deletedFromDisk = false;
|
|
1056
1056
|
try {
|
|
1057
|
-
await
|
|
1057
|
+
await fsp3.unlink(sessionPath);
|
|
1058
1058
|
deletedFromDisk = true;
|
|
1059
1059
|
} catch {
|
|
1060
1060
|
}
|
|
1061
1061
|
try {
|
|
1062
|
-
await
|
|
1062
|
+
await fsp3.rm(opts.paths.projectSpecs, { recursive: true, force: true });
|
|
1063
1063
|
} catch {
|
|
1064
1064
|
}
|
|
1065
1065
|
try {
|
|
1066
|
-
await
|
|
1066
|
+
await fsp3.rm(opts.paths.projectTaskGraphs, { recursive: true, force: true });
|
|
1067
1067
|
} catch {
|
|
1068
1068
|
}
|
|
1069
1069
|
const cancelBuilder = sddState.getBuilder();
|
|
@@ -1398,8 +1398,8 @@ function sddHelp() {
|
|
|
1398
1398
|
async function gatherProjectContext(projectRoot) {
|
|
1399
1399
|
const parts = [];
|
|
1400
1400
|
try {
|
|
1401
|
-
const pkgPath =
|
|
1402
|
-
const pkgRaw = await
|
|
1401
|
+
const pkgPath = path25.join(projectRoot, "package.json");
|
|
1402
|
+
const pkgRaw = await fsp3.readFile(pkgPath, "utf8");
|
|
1403
1403
|
const pkg = JSON.parse(pkgRaw);
|
|
1404
1404
|
parts.push(`Project: ${String(pkg.name ?? "unknown")}`);
|
|
1405
1405
|
parts.push(`Description: ${String(pkg.description ?? "none")}`);
|
|
@@ -1414,14 +1414,14 @@ async function gatherProjectContext(projectRoot) {
|
|
|
1414
1414
|
} catch {
|
|
1415
1415
|
}
|
|
1416
1416
|
try {
|
|
1417
|
-
const tsconfigPath =
|
|
1418
|
-
await
|
|
1417
|
+
const tsconfigPath = path25.join(projectRoot, "tsconfig.json");
|
|
1418
|
+
await fsp3.access(tsconfigPath);
|
|
1419
1419
|
parts.push("Language: TypeScript");
|
|
1420
1420
|
} catch {
|
|
1421
1421
|
}
|
|
1422
1422
|
try {
|
|
1423
|
-
const srcDir =
|
|
1424
|
-
const entries = await
|
|
1423
|
+
const srcDir = path25.join(projectRoot, "src");
|
|
1424
|
+
const entries = await fsp3.readdir(srcDir, { withFileTypes: true });
|
|
1425
1425
|
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1426
1426
|
if (dirs.length > 0) {
|
|
1427
1427
|
parts.push(`Source structure: src/${dirs.join(", src/")}`);
|
|
@@ -1576,7 +1576,7 @@ __export(update_check_exports, {
|
|
|
1576
1576
|
getUpdateNotification: () => getUpdateNotification
|
|
1577
1577
|
});
|
|
1578
1578
|
function cachePath(homeFn = defaultHomeDir2) {
|
|
1579
|
-
return
|
|
1579
|
+
return path25.join(homeFn(), ".wrongstack", "update-cache.json");
|
|
1580
1580
|
}
|
|
1581
1581
|
function currentVersion() {
|
|
1582
1582
|
const req2 = createRequire(import.meta.url);
|
|
@@ -1603,7 +1603,7 @@ function isNewer(a, b) {
|
|
|
1603
1603
|
}
|
|
1604
1604
|
async function readCache(homeFn = defaultHomeDir2) {
|
|
1605
1605
|
try {
|
|
1606
|
-
const raw = await
|
|
1606
|
+
const raw = await fsp3.readFile(cachePath(homeFn), "utf8");
|
|
1607
1607
|
const entry = JSON.parse(raw);
|
|
1608
1608
|
if (Date.now() - entry.timestamp > CACHE_TTL_MS) return null;
|
|
1609
1609
|
return entry;
|
|
@@ -1613,9 +1613,9 @@ async function readCache(homeFn = defaultHomeDir2) {
|
|
|
1613
1613
|
}
|
|
1614
1614
|
async function writeCache(entry, homeFn = defaultHomeDir2) {
|
|
1615
1615
|
try {
|
|
1616
|
-
const dir =
|
|
1617
|
-
await
|
|
1618
|
-
await
|
|
1616
|
+
const dir = path25.dirname(cachePath(homeFn));
|
|
1617
|
+
await fsp3.mkdir(dir, { recursive: true });
|
|
1618
|
+
await fsp3.writeFile(cachePath(homeFn), JSON.stringify(entry, null, 2), "utf8");
|
|
1619
1619
|
} catch {
|
|
1620
1620
|
}
|
|
1621
1621
|
}
|
|
@@ -1823,10 +1823,27 @@ async function runWebUI(opts) {
|
|
|
1823
1823
|
});
|
|
1824
1824
|
wss.on("connection", (ws, req2) => {
|
|
1825
1825
|
const isLoopback = (hostname) => hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
1826
|
+
const tokenMatches = (provided) => {
|
|
1827
|
+
if (!provided) return false;
|
|
1828
|
+
const a = Buffer.from(provided);
|
|
1829
|
+
const b = Buffer.from(authToken);
|
|
1830
|
+
return a.length === b.length && crypto2.timingSafeEqual(a, b);
|
|
1831
|
+
};
|
|
1826
1832
|
try {
|
|
1827
1833
|
const url = new URL(req2.url ?? "/", `http://localhost:${port}`);
|
|
1828
1834
|
const token = url.searchParams.get("token");
|
|
1829
|
-
const tokenOk = token
|
|
1835
|
+
const tokenOk = tokenMatches(token);
|
|
1836
|
+
const hostHeader = (req2.headers.host ?? "").trim();
|
|
1837
|
+
let hostOk = false;
|
|
1838
|
+
try {
|
|
1839
|
+
hostOk = !!hostHeader && isLoopback(new URL(`http://${hostHeader}`).hostname);
|
|
1840
|
+
} catch {
|
|
1841
|
+
hostOk = false;
|
|
1842
|
+
}
|
|
1843
|
+
if (!hostOk) {
|
|
1844
|
+
ws.close(4003, "Forbidden: non-loopback Host header");
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1830
1847
|
const origin = req2.headers.origin;
|
|
1831
1848
|
if (origin) {
|
|
1832
1849
|
try {
|
|
@@ -2206,7 +2223,7 @@ async function runWebUI(opts) {
|
|
|
2206
2223
|
if (!opts.globalConfigPath) return {};
|
|
2207
2224
|
let raw;
|
|
2208
2225
|
try {
|
|
2209
|
-
raw = await
|
|
2226
|
+
raw = await fsp3.readFile(opts.globalConfigPath, "utf8");
|
|
2210
2227
|
} catch {
|
|
2211
2228
|
return {};
|
|
2212
2229
|
}
|
|
@@ -2217,7 +2234,7 @@ async function runWebUI(opts) {
|
|
|
2217
2234
|
return {};
|
|
2218
2235
|
}
|
|
2219
2236
|
if (!parsed.providers) return {};
|
|
2220
|
-
const keyFile =
|
|
2237
|
+
const keyFile = path25.join(path25.dirname(opts.globalConfigPath), ".key");
|
|
2221
2238
|
const vault = new DefaultSecretVault$1({ keyFile });
|
|
2222
2239
|
return decryptConfigSecrets$1(parsed.providers, vault);
|
|
2223
2240
|
}
|
|
@@ -2226,7 +2243,7 @@ async function runWebUI(opts) {
|
|
|
2226
2243
|
let raw;
|
|
2227
2244
|
let fileExists = true;
|
|
2228
2245
|
try {
|
|
2229
|
-
raw = await
|
|
2246
|
+
raw = await fsp3.readFile(opts.globalConfigPath, "utf8");
|
|
2230
2247
|
} catch (err) {
|
|
2231
2248
|
if (err.code !== "ENOENT") {
|
|
2232
2249
|
throw new Error(
|
|
@@ -2250,7 +2267,7 @@ async function runWebUI(opts) {
|
|
|
2250
2267
|
parsed = {};
|
|
2251
2268
|
}
|
|
2252
2269
|
parsed.providers = providers;
|
|
2253
|
-
const keyFile =
|
|
2270
|
+
const keyFile = path25.join(path25.dirname(opts.globalConfigPath), ".key");
|
|
2254
2271
|
const vault = new DefaultSecretVault$1({ keyFile });
|
|
2255
2272
|
const encrypted = encryptConfigSecrets(parsed, vault);
|
|
2256
2273
|
await atomicWrite(opts.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
@@ -2459,7 +2476,7 @@ function parseSpawnFlags(input) {
|
|
|
2459
2476
|
}
|
|
2460
2477
|
async function pathExists(file) {
|
|
2461
2478
|
try {
|
|
2462
|
-
await
|
|
2479
|
+
await fsp3.access(file);
|
|
2463
2480
|
return true;
|
|
2464
2481
|
} catch {
|
|
2465
2482
|
return false;
|
|
@@ -2470,10 +2487,10 @@ async function detectPackageManager(root, declared) {
|
|
|
2470
2487
|
const name = declared.split("@")[0];
|
|
2471
2488
|
if (name) return name;
|
|
2472
2489
|
}
|
|
2473
|
-
if (await pathExists(
|
|
2474
|
-
if (await pathExists(
|
|
2475
|
-
if (await pathExists(
|
|
2476
|
-
if (await pathExists(
|
|
2490
|
+
if (await pathExists(path25.join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
2491
|
+
if (await pathExists(path25.join(root, "bun.lockb"))) return "bun";
|
|
2492
|
+
if (await pathExists(path25.join(root, "bun.lock"))) return "bun";
|
|
2493
|
+
if (await pathExists(path25.join(root, "yarn.lock"))) return "yarn";
|
|
2477
2494
|
return "npm";
|
|
2478
2495
|
}
|
|
2479
2496
|
function hasUsableScript(scripts, name) {
|
|
@@ -2494,7 +2511,7 @@ function parseMakeTargets(makefile) {
|
|
|
2494
2511
|
async function detectProjectFacts(root) {
|
|
2495
2512
|
const facts = { hints: [] };
|
|
2496
2513
|
try {
|
|
2497
|
-
const pkg = JSON.parse(await
|
|
2514
|
+
const pkg = JSON.parse(await fsp3.readFile(path25.join(root, "package.json"), "utf8"));
|
|
2498
2515
|
const scripts = pkg.scripts ?? {};
|
|
2499
2516
|
const pm = await detectPackageManager(root, pkg.packageManager);
|
|
2500
2517
|
if (hasUsableScript(scripts, "build")) facts.build = `${pm} run build`;
|
|
@@ -2508,14 +2525,14 @@ async function detectProjectFacts(root) {
|
|
|
2508
2525
|
} catch {
|
|
2509
2526
|
}
|
|
2510
2527
|
try {
|
|
2511
|
-
if (!await pathExists(
|
|
2528
|
+
if (!await pathExists(path25.join(root, "pyproject.toml"))) throw new Error("not python");
|
|
2512
2529
|
facts.test ??= "pytest";
|
|
2513
2530
|
facts.lint ??= "ruff check .";
|
|
2514
2531
|
facts.hints.push("pyproject.toml");
|
|
2515
2532
|
} catch {
|
|
2516
2533
|
}
|
|
2517
2534
|
try {
|
|
2518
|
-
if (!await pathExists(
|
|
2535
|
+
if (!await pathExists(path25.join(root, "go.mod"))) throw new Error("not go");
|
|
2519
2536
|
facts.build ??= "go build ./...";
|
|
2520
2537
|
facts.test ??= "go test ./...";
|
|
2521
2538
|
facts.run ??= "go run .";
|
|
@@ -2523,7 +2540,7 @@ async function detectProjectFacts(root) {
|
|
|
2523
2540
|
} catch {
|
|
2524
2541
|
}
|
|
2525
2542
|
try {
|
|
2526
|
-
if (!await pathExists(
|
|
2543
|
+
if (!await pathExists(path25.join(root, "Cargo.toml"))) throw new Error("not rust");
|
|
2527
2544
|
facts.build ??= "cargo build";
|
|
2528
2545
|
facts.test ??= "cargo test";
|
|
2529
2546
|
facts.lint ??= "cargo clippy";
|
|
@@ -2532,7 +2549,7 @@ async function detectProjectFacts(root) {
|
|
|
2532
2549
|
} catch {
|
|
2533
2550
|
}
|
|
2534
2551
|
try {
|
|
2535
|
-
const makefile = await
|
|
2552
|
+
const makefile = await fsp3.readFile(path25.join(root, "Makefile"), "utf8");
|
|
2536
2553
|
const targets = parseMakeTargets(makefile);
|
|
2537
2554
|
facts.build ??= targets.has("build") ? "make build" : "make";
|
|
2538
2555
|
if (targets.has("test")) facts.test ??= "make test";
|
|
@@ -3424,12 +3441,12 @@ function buildInitCommand(opts) {
|
|
|
3424
3441
|
name: "init",
|
|
3425
3442
|
description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
|
|
3426
3443
|
async run(_args, ctx) {
|
|
3427
|
-
const dir =
|
|
3428
|
-
const file =
|
|
3444
|
+
const dir = path25.join(ctx.projectRoot, ".wrongstack");
|
|
3445
|
+
const file = path25.join(dir, "AGENTS.md");
|
|
3429
3446
|
const detected = await detectProjectFacts(ctx.projectRoot);
|
|
3430
3447
|
const body = renderAgentsTemplate(detected);
|
|
3431
|
-
await
|
|
3432
|
-
await
|
|
3448
|
+
await fsp3.mkdir(dir, { recursive: true });
|
|
3449
|
+
await fsp3.writeFile(file, body, "utf8");
|
|
3433
3450
|
if (detected.hints.length > 0) {
|
|
3434
3451
|
const msg2 = `Wrote ${file}
|
|
3435
3452
|
Pre-filled: ${detected.hints.join(", ")}. Edit the file with project context and instructions the system prompt should carry.`;
|
|
@@ -3616,18 +3633,18 @@ function stateBadge(state) {
|
|
|
3616
3633
|
return color.dim(state);
|
|
3617
3634
|
}
|
|
3618
3635
|
}
|
|
3619
|
-
async function readConfig(
|
|
3636
|
+
async function readConfig(path26) {
|
|
3620
3637
|
try {
|
|
3621
|
-
return JSON.parse(await
|
|
3638
|
+
return JSON.parse(await fsp3.readFile(path26, "utf8"));
|
|
3622
3639
|
} catch {
|
|
3623
3640
|
return {};
|
|
3624
3641
|
}
|
|
3625
3642
|
}
|
|
3626
|
-
async function writeConfig(
|
|
3643
|
+
async function writeConfig(path26, cfg) {
|
|
3627
3644
|
const raw = JSON.stringify(cfg, null, 2);
|
|
3628
|
-
const tmp =
|
|
3629
|
-
await
|
|
3630
|
-
await
|
|
3645
|
+
const tmp = path26 + ".tmp";
|
|
3646
|
+
await fsp3.writeFile(tmp, raw, "utf8");
|
|
3647
|
+
await fsp3.rename(tmp, path26);
|
|
3631
3648
|
}
|
|
3632
3649
|
|
|
3633
3650
|
// src/slash-commands/mcp.ts
|
|
@@ -4894,12 +4911,12 @@ var DEFAULTS = {
|
|
|
4894
4911
|
cost: true
|
|
4895
4912
|
};
|
|
4896
4913
|
function resolveConfigPath() {
|
|
4897
|
-
return process.env[CONFIG_ENV] ??
|
|
4914
|
+
return process.env[CONFIG_ENV] ?? path25.join(process.env.HOME ?? "", ".wrongstack", "statusline.json");
|
|
4898
4915
|
}
|
|
4899
4916
|
async function loadStatuslineConfig() {
|
|
4900
4917
|
const p = resolveConfigPath();
|
|
4901
4918
|
try {
|
|
4902
|
-
const raw = await
|
|
4919
|
+
const raw = await fsp3.readFile(p, "utf8");
|
|
4903
4920
|
return { ...DEFAULTS, ...JSON.parse(raw) };
|
|
4904
4921
|
} catch {
|
|
4905
4922
|
return { ...DEFAULTS };
|
|
@@ -4908,7 +4925,7 @@ async function loadStatuslineConfig() {
|
|
|
4908
4925
|
async function saveStatuslineConfig(cfg) {
|
|
4909
4926
|
const p = resolveConfigPath();
|
|
4910
4927
|
try {
|
|
4911
|
-
await
|
|
4928
|
+
await fsp3.mkdir(path25.dirname(p), { recursive: true });
|
|
4912
4929
|
await atomicWrite(p, JSON.stringify(cfg, null, 2));
|
|
4913
4930
|
} catch (err) {
|
|
4914
4931
|
throw new FsError({
|
|
@@ -5807,11 +5824,11 @@ When the error confidence is low (< 0.85) or the problem spans multiple files,
|
|
|
5807
5824
|
};
|
|
5808
5825
|
}
|
|
5809
5826
|
function makeInstaller(opts, projectRoot, global) {
|
|
5810
|
-
const globalRoot =
|
|
5827
|
+
const globalRoot = path25.join(os6.homedir(), ".wrongstack");
|
|
5811
5828
|
return new SkillInstaller({
|
|
5812
|
-
manifestPath:
|
|
5813
|
-
projectSkillsDir:
|
|
5814
|
-
globalSkillsDir:
|
|
5829
|
+
manifestPath: path25.join(globalRoot, "installed-skills.json"),
|
|
5830
|
+
projectSkillsDir: path25.join(projectRoot, ".wrongstack", "skills"),
|
|
5831
|
+
globalSkillsDir: path25.join(globalRoot, "skills"),
|
|
5815
5832
|
projectHash: projectHash(projectRoot),
|
|
5816
5833
|
skillLoader: opts.skillLoader
|
|
5817
5834
|
});
|
|
@@ -5967,6 +5984,162 @@ function buildSkillUninstallCommand(opts) {
|
|
|
5967
5984
|
}
|
|
5968
5985
|
};
|
|
5969
5986
|
}
|
|
5987
|
+
function getStore(opts) {
|
|
5988
|
+
return new PhaseStore({ baseDir: opts.paths.projectAutophase });
|
|
5989
|
+
}
|
|
5990
|
+
function formatProgress(p) {
|
|
5991
|
+
const filled = Math.floor(p.percentComplete / 5);
|
|
5992
|
+
const bars = "\u2588".repeat(filled) + "\u2591".repeat(20 - filled);
|
|
5993
|
+
return [
|
|
5994
|
+
`
|
|
5995
|
+
\u{1F4CA} Progress: ${bars} ${p.percentComplete}%`,
|
|
5996
|
+
` \u{1F4CB} Phases: ${p.completed}/${p.totalPhases} done, ${p.running} running, ${p.pending} pending`,
|
|
5997
|
+
` \u2705 Tasks: ${p.completedTasks}/${p.totalTasks} completed`,
|
|
5998
|
+
` \u23F1 Est: ${p.estimatedHours.toFixed(1)}h | Actual: ${p.actualHours.toFixed(1)}h`
|
|
5999
|
+
].join("\n");
|
|
6000
|
+
}
|
|
6001
|
+
var STATUS_EMOJI = {
|
|
6002
|
+
pending: "\u23F3",
|
|
6003
|
+
ready: "\u{1F51C}",
|
|
6004
|
+
running: "\u{1F504}",
|
|
6005
|
+
paused: "\u23F8",
|
|
6006
|
+
completed: "\u2705",
|
|
6007
|
+
failed: "\u274C",
|
|
6008
|
+
skipped: "\u23ED"
|
|
6009
|
+
};
|
|
6010
|
+
function formatPhaseList(graph) {
|
|
6011
|
+
const phases = Array.from(graph.phases.values());
|
|
6012
|
+
return [
|
|
6013
|
+
"",
|
|
6014
|
+
"Phases:",
|
|
6015
|
+
...phases.map((p) => {
|
|
6016
|
+
const total = p.taskGraph.nodes.size;
|
|
6017
|
+
const done = Array.from(p.taskGraph.nodes.values()).filter((t) => t.status === "completed").length;
|
|
6018
|
+
const tasks = total > 0 ? ` (${done}/${total} todos)` : "";
|
|
6019
|
+
return ` ${STATUS_EMOJI[p.status] ?? "?"} ${p.name}: ${p.status}${tasks}`;
|
|
6020
|
+
})
|
|
6021
|
+
].join("\n");
|
|
6022
|
+
}
|
|
6023
|
+
async function gatherProjectContext2(projectRoot) {
|
|
6024
|
+
try {
|
|
6025
|
+
const raw = await fsp3.readFile(path25.join(projectRoot, "package.json"), "utf8");
|
|
6026
|
+
const pkg = JSON.parse(raw);
|
|
6027
|
+
const parts = [
|
|
6028
|
+
`Project: ${String(pkg.name ?? "unknown")}`,
|
|
6029
|
+
pkg.description ? `Description: ${String(pkg.description)}` : ""
|
|
6030
|
+
].filter(Boolean);
|
|
6031
|
+
return parts.join("\n") || void 0;
|
|
6032
|
+
} catch {
|
|
6033
|
+
return void 0;
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
function buildAutoPhaseCommand(opts) {
|
|
6037
|
+
return {
|
|
6038
|
+
name: "autophase",
|
|
6039
|
+
description: "Autonomous phase-based workflow \u2014 plans a project into phases of todos and builds it with the LLM.",
|
|
6040
|
+
help: [
|
|
6041
|
+
"Usage:",
|
|
6042
|
+
" /autophase Show current status",
|
|
6043
|
+
" /autophase start <goal> Plan + start an autonomous phase build",
|
|
6044
|
+
" /autophase pause Pause (in-flight tasks finish, no new ones start)",
|
|
6045
|
+
" /autophase resume Resume a paused run",
|
|
6046
|
+
" /autophase stop Stop and abort in-flight tasks",
|
|
6047
|
+
" /autophase save Persist current graph to disk",
|
|
6048
|
+
" /autophase load [title] Load a persisted graph (display only)",
|
|
6049
|
+
" /autophase list List saved projects",
|
|
6050
|
+
""
|
|
6051
|
+
].join("\n"),
|
|
6052
|
+
async run(args) {
|
|
6053
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
6054
|
+
const sub = parts[0] ?? "status";
|
|
6055
|
+
const store = getStore(opts);
|
|
6056
|
+
switch (sub) {
|
|
6057
|
+
case "start": {
|
|
6058
|
+
const goal = parts.slice(1).join(" ").trim();
|
|
6059
|
+
if (!goal) {
|
|
6060
|
+
return { message: "Usage: /autophase start <goal> \u2014 describe what to build." };
|
|
6061
|
+
}
|
|
6062
|
+
if (!opts.onAutoPhaseStart) {
|
|
6063
|
+
return { message: "\u274C AutoPhase is not available in this session (no LLM host wired)." };
|
|
6064
|
+
}
|
|
6065
|
+
const projectContext = await gatherProjectContext2(opts.projectRoot);
|
|
6066
|
+
const result = await opts.onAutoPhaseStart({ goal, projectContext });
|
|
6067
|
+
if (!result.ok) {
|
|
6068
|
+
return { message: `\u274C ${result.error}` };
|
|
6069
|
+
}
|
|
6070
|
+
return {
|
|
6071
|
+
message: [
|
|
6072
|
+
`\u{1F680} AutoPhase started: **${result.graph.title}**`,
|
|
6073
|
+
formatPhaseList(result.graph),
|
|
6074
|
+
"",
|
|
6075
|
+
"Building autonomously in the background \u2014 one subagent per todo.",
|
|
6076
|
+
"Use `/autophase` for status, `/autophase pause` to hold, `/autophase stop` to abort."
|
|
6077
|
+
].join("\n"),
|
|
6078
|
+
metadata: { autoPhaseInit: { title: result.graph.title } }
|
|
6079
|
+
};
|
|
6080
|
+
}
|
|
6081
|
+
case "pause": {
|
|
6082
|
+
if (!opts.onAutoPhasePause) return { message: "\u274C AutoPhase host not available." };
|
|
6083
|
+
opts.onAutoPhasePause();
|
|
6084
|
+
return { message: "\u23F8\uFE0F AutoPhase paused \u2014 running tasks will finish; no new ones will start." };
|
|
6085
|
+
}
|
|
6086
|
+
case "resume": {
|
|
6087
|
+
if (!opts.onAutoPhaseResume) return { message: "\u274C AutoPhase host not available." };
|
|
6088
|
+
opts.onAutoPhaseResume();
|
|
6089
|
+
return { message: "\u25B6 AutoPhase resuming." };
|
|
6090
|
+
}
|
|
6091
|
+
case "stop": {
|
|
6092
|
+
if (!opts.onAutoPhaseStop) return { message: "\u274C AutoPhase host not available." };
|
|
6093
|
+
opts.onAutoPhaseStop();
|
|
6094
|
+
return { message: "\u23F9 AutoPhase stopped \u2014 in-flight tasks aborted, progress saved." };
|
|
6095
|
+
}
|
|
6096
|
+
case "save": {
|
|
6097
|
+
const view = opts.getAutoPhaseRunner?.();
|
|
6098
|
+
if (!view) return { message: "\u274C No active AutoPhase to save." };
|
|
6099
|
+
await store.save(view.graph);
|
|
6100
|
+
return { message: `\u{1F4BE} AutoPhase saved: ${view.graph.title}` };
|
|
6101
|
+
}
|
|
6102
|
+
case "load": {
|
|
6103
|
+
const title = parts.slice(1).join(" ").trim();
|
|
6104
|
+
const graphs = await store.list();
|
|
6105
|
+
if (graphs.length === 0) return { message: "\u274C No saved projects." };
|
|
6106
|
+
const entry = title ? graphs.find((g) => g.title.toLowerCase().includes(title.toLowerCase())) : graphs[0];
|
|
6107
|
+
if (!entry) return { message: `\u274C No saved project matching "${title}".` };
|
|
6108
|
+
const graph = await store.load(entry.id);
|
|
6109
|
+
if (!graph) return { message: `\u274C Could not load project "${entry.title}".` };
|
|
6110
|
+
return {
|
|
6111
|
+
message: [`\u{1F4C2} Loaded (display only): **${graph.title}**`, formatPhaseList(graph)].join("\n")
|
|
6112
|
+
};
|
|
6113
|
+
}
|
|
6114
|
+
case "list": {
|
|
6115
|
+
const graphs = await store.list();
|
|
6116
|
+
if (graphs.length === 0) return { message: "No saved projects." };
|
|
6117
|
+
return {
|
|
6118
|
+
message: [
|
|
6119
|
+
"Saved AutoPhase projects:",
|
|
6120
|
+
...graphs.map((g) => ` \xB7 ${g.title} \u2014 ${g.status} (updated ${new Date(g.updatedAt).toLocaleString()})`)
|
|
6121
|
+
].join("\n")
|
|
6122
|
+
};
|
|
6123
|
+
}
|
|
6124
|
+
case "default":
|
|
6125
|
+
case "status": {
|
|
6126
|
+
const view = opts.getAutoPhaseRunner?.();
|
|
6127
|
+
if (!view) {
|
|
6128
|
+
return { message: "No active AutoPhase. Run `/autophase start <goal>` to begin." };
|
|
6129
|
+
}
|
|
6130
|
+
const progress = view.getProgress();
|
|
6131
|
+
return {
|
|
6132
|
+
message: [
|
|
6133
|
+
`**${view.graph.title}** ${view.isRunning() ? "\u{1F504} running" : "\u23F8 idle"}`,
|
|
6134
|
+
formatPhaseList(view.graph),
|
|
6135
|
+
...progress ? [formatProgress(progress)] : []
|
|
6136
|
+
].join("\n")
|
|
6137
|
+
};
|
|
6138
|
+
}
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
};
|
|
6142
|
+
}
|
|
5970
6143
|
|
|
5971
6144
|
// src/slash-commands/index.ts
|
|
5972
6145
|
function buildBuiltinSlashCommands(opts) {
|
|
@@ -6009,6 +6182,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
6009
6182
|
buildPushCommand(),
|
|
6010
6183
|
buildSecurityCommand(opts),
|
|
6011
6184
|
buildFixCommand(opts),
|
|
6185
|
+
buildAutoPhaseCommand(opts),
|
|
6012
6186
|
buildStatuslineCommand({
|
|
6013
6187
|
cwd: opts.cwd,
|
|
6014
6188
|
hiddenItems: opts.statuslineHiddenItems ?? [],
|
|
@@ -6036,13 +6210,13 @@ var MANIFESTS = [
|
|
|
6036
6210
|
];
|
|
6037
6211
|
async function detectProjectKind(projectRoot) {
|
|
6038
6212
|
try {
|
|
6039
|
-
await
|
|
6213
|
+
await fsp3.access(path25.join(projectRoot, ".wrongstack", "AGENTS.md"));
|
|
6040
6214
|
return "initialized";
|
|
6041
6215
|
} catch {
|
|
6042
6216
|
}
|
|
6043
6217
|
for (const m of MANIFESTS) {
|
|
6044
6218
|
try {
|
|
6045
|
-
await
|
|
6219
|
+
await fsp3.access(path25.join(projectRoot, m));
|
|
6046
6220
|
return "project";
|
|
6047
6221
|
} catch {
|
|
6048
6222
|
}
|
|
@@ -6050,12 +6224,12 @@ async function detectProjectKind(projectRoot) {
|
|
|
6050
6224
|
return "empty";
|
|
6051
6225
|
}
|
|
6052
6226
|
async function scaffoldAgentsMd(projectRoot) {
|
|
6053
|
-
const dir =
|
|
6054
|
-
const file =
|
|
6227
|
+
const dir = path25.join(projectRoot, ".wrongstack");
|
|
6228
|
+
const file = path25.join(dir, "AGENTS.md");
|
|
6055
6229
|
const facts = await detectProjectFacts(projectRoot);
|
|
6056
6230
|
const body = renderAgentsTemplate(facts);
|
|
6057
|
-
await
|
|
6058
|
-
await
|
|
6231
|
+
await fsp3.mkdir(dir, { recursive: true });
|
|
6232
|
+
await fsp3.writeFile(file, body, "utf8");
|
|
6059
6233
|
return file;
|
|
6060
6234
|
}
|
|
6061
6235
|
async function runProjectCheck(opts) {
|
|
@@ -6064,7 +6238,7 @@ async function runProjectCheck(opts) {
|
|
|
6064
6238
|
if (kind === "initialized") {
|
|
6065
6239
|
renderer.write(
|
|
6066
6240
|
`
|
|
6067
|
-
${color.green("\u2713")} Project initialized ${color.dim(`(${
|
|
6241
|
+
${color.green("\u2713")} Project initialized ${color.dim(`(${path25.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
|
|
6068
6242
|
`
|
|
6069
6243
|
);
|
|
6070
6244
|
return true;
|
|
@@ -6095,10 +6269,10 @@ async function runProjectCheck(opts) {
|
|
|
6095
6269
|
}
|
|
6096
6270
|
return true;
|
|
6097
6271
|
}
|
|
6098
|
-
const gitDir =
|
|
6272
|
+
const gitDir = path25.join(projectRoot, ".git");
|
|
6099
6273
|
let hasGit = false;
|
|
6100
6274
|
try {
|
|
6101
|
-
await
|
|
6275
|
+
await fsp3.access(gitDir);
|
|
6102
6276
|
hasGit = true;
|
|
6103
6277
|
} catch {
|
|
6104
6278
|
}
|
|
@@ -6220,7 +6394,7 @@ async function runLaunchPrompts(opts) {
|
|
|
6220
6394
|
return { mode, yolo, director, autonomy };
|
|
6221
6395
|
}
|
|
6222
6396
|
async function bootConfig(flags) {
|
|
6223
|
-
const cwd = typeof flags["cwd"] === "string" ?
|
|
6397
|
+
const cwd = typeof flags["cwd"] === "string" ? path25.resolve(flags["cwd"]) : process.cwd();
|
|
6224
6398
|
const pathResolver = new DefaultPathResolver(cwd);
|
|
6225
6399
|
const projectRoot = pathResolver.projectRoot;
|
|
6226
6400
|
const userHome = os6.homedir();
|
|
@@ -6271,13 +6445,13 @@ function flagsToConfigPatch(flags) {
|
|
|
6271
6445
|
}
|
|
6272
6446
|
async function ensureProjectMeta(paths, projectRoot) {
|
|
6273
6447
|
try {
|
|
6274
|
-
await
|
|
6448
|
+
await fsp3.mkdir(paths.projectDir, { recursive: true });
|
|
6275
6449
|
const meta = {
|
|
6276
6450
|
hash: paths.projectHash,
|
|
6277
6451
|
root: projectRoot,
|
|
6278
6452
|
lastSeen: (/* @__PURE__ */ new Date()).toISOString()
|
|
6279
6453
|
};
|
|
6280
|
-
await
|
|
6454
|
+
await fsp3.writeFile(paths.projectMeta, JSON.stringify(meta, null, 2));
|
|
6281
6455
|
} catch {
|
|
6282
6456
|
}
|
|
6283
6457
|
}
|
|
@@ -6287,11 +6461,11 @@ var ReadlineInputReader = class {
|
|
|
6287
6461
|
history = [];
|
|
6288
6462
|
pending = false;
|
|
6289
6463
|
constructor(opts = {}) {
|
|
6290
|
-
this.historyFile = opts.historyFile ??
|
|
6464
|
+
this.historyFile = opts.historyFile ?? path25.join(os6.homedir(), ".wrongstack", "history");
|
|
6291
6465
|
}
|
|
6292
6466
|
async loadHistory() {
|
|
6293
6467
|
try {
|
|
6294
|
-
const raw = await
|
|
6468
|
+
const raw = await fsp3.readFile(this.historyFile, "utf8");
|
|
6295
6469
|
this.history = raw.split("\n").filter(Boolean).slice(-1e3);
|
|
6296
6470
|
} catch {
|
|
6297
6471
|
this.history = [];
|
|
@@ -6299,8 +6473,8 @@ var ReadlineInputReader = class {
|
|
|
6299
6473
|
}
|
|
6300
6474
|
async saveHistory() {
|
|
6301
6475
|
try {
|
|
6302
|
-
await
|
|
6303
|
-
await
|
|
6476
|
+
await fsp3.mkdir(path25.dirname(this.historyFile), { recursive: true });
|
|
6477
|
+
await fsp3.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
|
|
6304
6478
|
} catch {
|
|
6305
6479
|
}
|
|
6306
6480
|
}
|
|
@@ -6526,23 +6700,23 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
6526
6700
|
if (PROTECTED_BASENAMES.has(filename)) {
|
|
6527
6701
|
throw new Error(`Refusing to delete protected file: ${filename}`);
|
|
6528
6702
|
}
|
|
6529
|
-
if (filename !==
|
|
6703
|
+
if (filename !== path25.basename(filename)) {
|
|
6530
6704
|
throw new Error(`Refusing to delete path with traversal: ${filename}`);
|
|
6531
6705
|
}
|
|
6532
6706
|
if (!filename.startsWith("config.json.") || !filename.endsWith(".bak")) {
|
|
6533
6707
|
throw new Error(`Refusing to delete unknown file: ${filename}`);
|
|
6534
6708
|
}
|
|
6535
|
-
const resolvedParent =
|
|
6709
|
+
const resolvedParent = path25.resolve(parentDir);
|
|
6536
6710
|
if (!resolvedParent.endsWith(".wrongstack")) {
|
|
6537
6711
|
throw new Error(`Unexpected parent directory for bak prune: ${resolvedParent}`);
|
|
6538
6712
|
}
|
|
6539
6713
|
}
|
|
6540
6714
|
async function safeDelete(filePath) {
|
|
6541
|
-
const dir =
|
|
6542
|
-
const filename =
|
|
6715
|
+
const dir = path25.dirname(filePath);
|
|
6716
|
+
const filename = path25.basename(filePath);
|
|
6543
6717
|
try {
|
|
6544
6718
|
assertSafeToDelete(filename, dir);
|
|
6545
|
-
await
|
|
6719
|
+
await fsp3.unlink(filePath);
|
|
6546
6720
|
} catch (err) {
|
|
6547
6721
|
if (err instanceof Error && err.message.startsWith("Refusing")) {
|
|
6548
6722
|
process.stderr.write(`[config-history] SAFETY: ${err.message}
|
|
@@ -6584,23 +6758,23 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
6584
6758
|
}
|
|
6585
6759
|
var defaultHomeDir = () => os6__default.homedir();
|
|
6586
6760
|
function historyDir(homeFn = defaultHomeDir) {
|
|
6587
|
-
return
|
|
6761
|
+
return path25.join(homeFn(), ".wrongstack", "config.history", "entries");
|
|
6588
6762
|
}
|
|
6589
6763
|
function historyIndexPath(homeFn = defaultHomeDir) {
|
|
6590
|
-
return
|
|
6764
|
+
return path25.join(homeFn(), ".wrongstack", "config.history", "index.json");
|
|
6591
6765
|
}
|
|
6592
6766
|
function configPath(homeFn = defaultHomeDir) {
|
|
6593
|
-
return
|
|
6767
|
+
return path25.join(homeFn(), ".wrongstack", "config.json");
|
|
6594
6768
|
}
|
|
6595
6769
|
function backupLastPath(homeFn = defaultHomeDir) {
|
|
6596
|
-
return
|
|
6770
|
+
return path25.join(homeFn(), ".wrongstack", "config.json.last");
|
|
6597
6771
|
}
|
|
6598
6772
|
function entryId(ts) {
|
|
6599
6773
|
return ts.replace(/[:.]/g, "-").slice(0, 19);
|
|
6600
6774
|
}
|
|
6601
6775
|
async function ensureHistoryDir(homeFn = defaultHomeDir) {
|
|
6602
6776
|
try {
|
|
6603
|
-
await
|
|
6777
|
+
await fsp3.mkdir(historyDir(homeFn), { recursive: true });
|
|
6604
6778
|
} catch (err) {
|
|
6605
6779
|
throw new FsError({
|
|
6606
6780
|
message: err instanceof Error ? err.message : String(err),
|
|
@@ -6612,7 +6786,7 @@ async function ensureHistoryDir(homeFn = defaultHomeDir) {
|
|
|
6612
6786
|
}
|
|
6613
6787
|
async function readIndex(homeFn = defaultHomeDir) {
|
|
6614
6788
|
try {
|
|
6615
|
-
const raw = await
|
|
6789
|
+
const raw = await fsp3.readFile(historyIndexPath(homeFn), "utf8");
|
|
6616
6790
|
return JSON.parse(raw);
|
|
6617
6791
|
} catch {
|
|
6618
6792
|
return { version: 1, entries: [] };
|
|
@@ -6637,7 +6811,7 @@ async function backupCurrent(homeFn = defaultHomeDir) {
|
|
|
6637
6811
|
const ts = Date.now();
|
|
6638
6812
|
let content;
|
|
6639
6813
|
try {
|
|
6640
|
-
content = await
|
|
6814
|
+
content = await fsp3.readFile(cfg, "utf8");
|
|
6641
6815
|
} catch {
|
|
6642
6816
|
}
|
|
6643
6817
|
if (content !== void 0) {
|
|
@@ -6648,17 +6822,17 @@ async function backupCurrent(homeFn = defaultHomeDir) {
|
|
|
6648
6822
|
}
|
|
6649
6823
|
if (content !== void 0) {
|
|
6650
6824
|
try {
|
|
6651
|
-
const bakPath =
|
|
6825
|
+
const bakPath = path25.join(homeFn(), ".wrongstack", `config.json.${ts}.bak`);
|
|
6652
6826
|
await atomicWrite(bakPath, content);
|
|
6653
6827
|
} catch {
|
|
6654
6828
|
}
|
|
6655
6829
|
}
|
|
6656
6830
|
try {
|
|
6657
|
-
const dir =
|
|
6658
|
-
const files = await
|
|
6831
|
+
const dir = path25.join(homeFn(), ".wrongstack");
|
|
6832
|
+
const files = await fsp3.readdir(dir);
|
|
6659
6833
|
const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
|
|
6660
6834
|
for (const f of baks.slice(10)) {
|
|
6661
|
-
await safeDelete(
|
|
6835
|
+
await safeDelete(path25.join(dir, f));
|
|
6662
6836
|
}
|
|
6663
6837
|
} catch {
|
|
6664
6838
|
}
|
|
@@ -6675,8 +6849,8 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
6675
6849
|
diffSummary: diffSummary(oldCfg, newCfg)
|
|
6676
6850
|
};
|
|
6677
6851
|
try {
|
|
6678
|
-
await
|
|
6679
|
-
|
|
6852
|
+
await fsp3.writeFile(
|
|
6853
|
+
path25.join(historyDir(homeFn), `${id}.json`),
|
|
6680
6854
|
JSON.stringify(entry, null, 2),
|
|
6681
6855
|
"utf8"
|
|
6682
6856
|
);
|
|
@@ -6684,7 +6858,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
6684
6858
|
throw new FsError({
|
|
6685
6859
|
message: err instanceof Error ? err.message : String(err),
|
|
6686
6860
|
code: ERROR_CODES.FS_WRITE_FAILED,
|
|
6687
|
-
path:
|
|
6861
|
+
path: path25.join(historyDir(homeFn), `${id}.json`),
|
|
6688
6862
|
cause: err
|
|
6689
6863
|
});
|
|
6690
6864
|
}
|
|
@@ -6699,7 +6873,7 @@ async function listHistory(homeFn = defaultHomeDir) {
|
|
|
6699
6873
|
}
|
|
6700
6874
|
async function getHistoryEntry(id, homeFn = defaultHomeDir) {
|
|
6701
6875
|
try {
|
|
6702
|
-
const raw = await
|
|
6876
|
+
const raw = await fsp3.readFile(path25.join(historyDir(homeFn), `${id}.json`), "utf8");
|
|
6703
6877
|
return JSON.parse(raw);
|
|
6704
6878
|
} catch {
|
|
6705
6879
|
return null;
|
|
@@ -6711,7 +6885,7 @@ async function restoreFromHistory(id, homeFn = defaultHomeDir) {
|
|
|
6711
6885
|
await backupCurrent(homeFn);
|
|
6712
6886
|
let oldCfg = {};
|
|
6713
6887
|
try {
|
|
6714
|
-
const raw = await
|
|
6888
|
+
const raw = await fsp3.readFile(configPath(homeFn), "utf8");
|
|
6715
6889
|
oldCfg = JSON.parse(raw);
|
|
6716
6890
|
} catch {
|
|
6717
6891
|
}
|
|
@@ -6733,13 +6907,13 @@ async function restoreLast(homeFn = defaultHomeDir) {
|
|
|
6733
6907
|
const cfg = configPath(homeFn);
|
|
6734
6908
|
let oldCfg = {};
|
|
6735
6909
|
try {
|
|
6736
|
-
const raw = await
|
|
6910
|
+
const raw = await fsp3.readFile(cfg, "utf8");
|
|
6737
6911
|
oldCfg = JSON.parse(raw);
|
|
6738
6912
|
} catch {
|
|
6739
6913
|
}
|
|
6740
6914
|
let lastCfg = {};
|
|
6741
6915
|
try {
|
|
6742
|
-
const raw = await
|
|
6916
|
+
const raw = await fsp3.readFile(last, "utf8");
|
|
6743
6917
|
lastCfg = JSON.parse(raw);
|
|
6744
6918
|
} catch {
|
|
6745
6919
|
return { ok: false, error: "No prior backup found" };
|
|
@@ -6770,7 +6944,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
|
|
|
6770
6944
|
existing.provider = provider;
|
|
6771
6945
|
existing.model = model;
|
|
6772
6946
|
await backupCurrent(homeFn);
|
|
6773
|
-
await atomicWrite8(configPath2, JSON.stringify(existing, null, 2));
|
|
6947
|
+
await atomicWrite8(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
6774
6948
|
try {
|
|
6775
6949
|
await appendHistory(
|
|
6776
6950
|
oldCfg,
|
|
@@ -7097,7 +7271,7 @@ function pickGroupIndex(opts) {
|
|
|
7097
7271
|
if (Number.isFinite(parsed)) current = wrap(parsed);
|
|
7098
7272
|
} catch {
|
|
7099
7273
|
}
|
|
7100
|
-
fs9.mkdirSync(
|
|
7274
|
+
fs9.mkdirSync(path25.dirname(opts.cursorFile), { recursive: true });
|
|
7101
7275
|
fs9.writeFileSync(opts.cursorFile, String(wrap(current + 1)));
|
|
7102
7276
|
return current;
|
|
7103
7277
|
} catch {
|
|
@@ -7433,14 +7607,14 @@ function summarize(value, name) {
|
|
|
7433
7607
|
if (typeof v === "object" && v !== null) {
|
|
7434
7608
|
const o = v;
|
|
7435
7609
|
if (name === "edit") {
|
|
7436
|
-
const
|
|
7610
|
+
const path26 = typeof o["path"] === "string" ? o["path"] : "";
|
|
7437
7611
|
const reps = typeof o["replacements"] === "number" ? o["replacements"] : 0;
|
|
7438
|
-
return `${
|
|
7612
|
+
return `${path26} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
|
|
7439
7613
|
}
|
|
7440
7614
|
if (name === "write") {
|
|
7441
|
-
const
|
|
7615
|
+
const path26 = typeof o["path"] === "string" ? o["path"] : "";
|
|
7442
7616
|
const bytes = typeof o["bytes"] === "number" ? o["bytes"] : void 0;
|
|
7443
|
-
return bytes !== void 0 ? `${
|
|
7617
|
+
return bytes !== void 0 ? `${path26} ${bytes}B` : path26;
|
|
7444
7618
|
}
|
|
7445
7619
|
if (typeof o["count"] === "number") {
|
|
7446
7620
|
return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
|
|
@@ -8200,7 +8374,7 @@ async function readKeyInput(deps, intent) {
|
|
|
8200
8374
|
async function loadProviders(deps) {
|
|
8201
8375
|
let raw;
|
|
8202
8376
|
try {
|
|
8203
|
-
raw = await
|
|
8377
|
+
raw = await fsp3.readFile(deps.globalConfigPath, "utf8");
|
|
8204
8378
|
} catch (err) {
|
|
8205
8379
|
if (err.code !== "ENOENT") {
|
|
8206
8380
|
deps.renderer.writeWarning(
|
|
@@ -8225,7 +8399,7 @@ async function mutateProviders(deps, mutator) {
|
|
|
8225
8399
|
let raw;
|
|
8226
8400
|
let fileExists = true;
|
|
8227
8401
|
try {
|
|
8228
|
-
raw = await
|
|
8402
|
+
raw = await fsp3.readFile(deps.globalConfigPath, "utf8");
|
|
8229
8403
|
} catch (err) {
|
|
8230
8404
|
if (err.code !== "ENOENT") {
|
|
8231
8405
|
throw new Error(
|
|
@@ -8439,7 +8613,7 @@ var doctorCmd = async (_args, deps) => {
|
|
|
8439
8613
|
});
|
|
8440
8614
|
}
|
|
8441
8615
|
try {
|
|
8442
|
-
await
|
|
8616
|
+
await fsp3.access(deps.paths.secretsKey);
|
|
8443
8617
|
checks.push({ name: "secret vault", status: "ok", detail: deps.paths.secretsKey });
|
|
8444
8618
|
} catch {
|
|
8445
8619
|
checks.push({
|
|
@@ -8449,10 +8623,10 @@ var doctorCmd = async (_args, deps) => {
|
|
|
8449
8623
|
});
|
|
8450
8624
|
}
|
|
8451
8625
|
try {
|
|
8452
|
-
await
|
|
8453
|
-
const probe =
|
|
8454
|
-
await
|
|
8455
|
-
await
|
|
8626
|
+
await fsp3.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
8627
|
+
const probe = path25.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
8628
|
+
await fsp3.writeFile(probe, "");
|
|
8629
|
+
await fsp3.unlink(probe);
|
|
8456
8630
|
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
8457
8631
|
} catch (err) {
|
|
8458
8632
|
checks.push({
|
|
@@ -8553,8 +8727,8 @@ var exportCmd = async (args, deps) => {
|
|
|
8553
8727
|
return 1;
|
|
8554
8728
|
}
|
|
8555
8729
|
if (output) {
|
|
8556
|
-
await
|
|
8557
|
-
await
|
|
8730
|
+
await fsp3.mkdir(path25.dirname(path25.resolve(deps.cwd, output)), { recursive: true });
|
|
8731
|
+
await fsp3.writeFile(path25.resolve(deps.cwd, output), rendered, "utf8");
|
|
8558
8732
|
deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
|
|
8559
8733
|
`);
|
|
8560
8734
|
} else {
|
|
@@ -8621,14 +8795,14 @@ var initCmd = async (_args, deps) => {
|
|
|
8621
8795
|
} else {
|
|
8622
8796
|
deps.renderer.writeInfo(`Found API key in env (${provider.envVars.join(" / ")}).`);
|
|
8623
8797
|
}
|
|
8624
|
-
await
|
|
8798
|
+
await fsp3.mkdir(deps.paths.globalRoot, { recursive: true });
|
|
8625
8799
|
const config = { version: 1, provider: providerId, model: modelId };
|
|
8626
8800
|
if (apiKey) config.apiKey = apiKey;
|
|
8627
8801
|
const vault = new DefaultSecretVault$1({ keyFile: deps.paths.secretsKey });
|
|
8628
8802
|
const encrypted = encryptConfigSecrets(config, vault);
|
|
8629
|
-
await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2));
|
|
8630
|
-
await
|
|
8631
|
-
const agentsFile =
|
|
8803
|
+
await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
8804
|
+
await fsp3.mkdir(path25.join(deps.projectRoot, ".wrongstack"), { recursive: true });
|
|
8805
|
+
const agentsFile = path25.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
|
|
8632
8806
|
const projectFacts = await detectProjectFacts(deps.projectRoot);
|
|
8633
8807
|
await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
|
|
8634
8808
|
deps.renderer.writeInfo(`Wrote ${deps.paths.globalConfig}`);
|
|
@@ -8702,7 +8876,7 @@ async function addMcpServer(args, deps) {
|
|
|
8702
8876
|
serverCfg.enabled = enable;
|
|
8703
8877
|
let existing = {};
|
|
8704
8878
|
try {
|
|
8705
|
-
existing = JSON.parse(await
|
|
8879
|
+
existing = JSON.parse(await fsp3.readFile(deps.paths.globalConfig, "utf8"));
|
|
8706
8880
|
} catch {
|
|
8707
8881
|
}
|
|
8708
8882
|
const mcpServers = existing.mcpServers ?? {};
|
|
@@ -8711,7 +8885,7 @@ async function addMcpServer(args, deps) {
|
|
|
8711
8885
|
`);
|
|
8712
8886
|
mcpServers[name] = serverCfg;
|
|
8713
8887
|
existing.mcpServers = mcpServers;
|
|
8714
|
-
await atomicWrite(deps.paths.globalConfig, JSON.stringify(existing, null, 2));
|
|
8888
|
+
await atomicWrite(deps.paths.globalConfig, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
8715
8889
|
const verb = enable ? "Enabled" : "Added (disabled \u2014 set enabled:true to activate)";
|
|
8716
8890
|
deps.renderer.writeInfo(
|
|
8717
8891
|
`${verb} "${name}" (${serverCfg.transport}). Config written to ${deps.paths.globalConfig}.
|
|
@@ -8722,7 +8896,7 @@ async function addMcpServer(args, deps) {
|
|
|
8722
8896
|
async function removeMcpServer(name, deps) {
|
|
8723
8897
|
let existing = {};
|
|
8724
8898
|
try {
|
|
8725
|
-
existing = JSON.parse(await
|
|
8899
|
+
existing = JSON.parse(await fsp3.readFile(deps.paths.globalConfig, "utf8"));
|
|
8726
8900
|
} catch {
|
|
8727
8901
|
deps.renderer.writeError("No config file found.\n");
|
|
8728
8902
|
return 1;
|
|
@@ -8735,7 +8909,7 @@ async function removeMcpServer(name, deps) {
|
|
|
8735
8909
|
}
|
|
8736
8910
|
delete mcpServers[name];
|
|
8737
8911
|
existing.mcpServers = mcpServers;
|
|
8738
|
-
await atomicWrite(deps.paths.globalConfig, JSON.stringify(existing, null, 2));
|
|
8912
|
+
await atomicWrite(deps.paths.globalConfig, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
8739
8913
|
deps.renderer.writeInfo(`Removed "${name}" from config.
|
|
8740
8914
|
`);
|
|
8741
8915
|
return 0;
|
|
@@ -8843,7 +9017,7 @@ function renderConfiguredPlugins(config) {
|
|
|
8843
9017
|
}
|
|
8844
9018
|
async function readConfig2(file) {
|
|
8845
9019
|
try {
|
|
8846
|
-
return JSON.parse(await
|
|
9020
|
+
return JSON.parse(await fsp3.readFile(file, "utf8"));
|
|
8847
9021
|
} catch {
|
|
8848
9022
|
return {};
|
|
8849
9023
|
}
|
|
@@ -8873,7 +9047,7 @@ async function upsertPlugin(spec, opts, deps, verb) {
|
|
|
8873
9047
|
};
|
|
8874
9048
|
existing.plugins = plugins;
|
|
8875
9049
|
existing.features = features;
|
|
8876
|
-
await atomicWrite(deps.configPath, JSON.stringify(existing, null, 2));
|
|
9050
|
+
await atomicWrite(deps.configPath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
8877
9051
|
return {
|
|
8878
9052
|
code: 0,
|
|
8879
9053
|
level: "info",
|
|
@@ -8890,7 +9064,7 @@ async function removePlugin(spec, deps) {
|
|
|
8890
9064
|
return errorResult(`Plugin "${spec}" not in config.`);
|
|
8891
9065
|
}
|
|
8892
9066
|
existing.plugins = next;
|
|
8893
|
-
await atomicWrite(deps.configPath, JSON.stringify(existing, null, 2));
|
|
9067
|
+
await atomicWrite(deps.configPath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
8894
9068
|
return {
|
|
8895
9069
|
code: 0,
|
|
8896
9070
|
level: "info",
|
|
@@ -8934,9 +9108,9 @@ var usageCmd = async (_args, deps) => {
|
|
|
8934
9108
|
return 0;
|
|
8935
9109
|
};
|
|
8936
9110
|
var projectsCmd = async (_args, deps) => {
|
|
8937
|
-
const projectsRoot =
|
|
9111
|
+
const projectsRoot = path25.join(deps.paths.globalRoot, "projects");
|
|
8938
9112
|
try {
|
|
8939
|
-
const entries = await
|
|
9113
|
+
const entries = await fsp3.readdir(projectsRoot);
|
|
8940
9114
|
if (entries.length === 0) {
|
|
8941
9115
|
deps.renderer.write("No projects tracked.\n");
|
|
8942
9116
|
return 0;
|
|
@@ -8944,7 +9118,7 @@ var projectsCmd = async (_args, deps) => {
|
|
|
8944
9118
|
for (const hash of entries) {
|
|
8945
9119
|
try {
|
|
8946
9120
|
const meta = JSON.parse(
|
|
8947
|
-
await
|
|
9121
|
+
await fsp3.readFile(path25.join(projectsRoot, hash, "meta.json"), "utf8")
|
|
8948
9122
|
);
|
|
8949
9123
|
deps.renderer.write(
|
|
8950
9124
|
` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
|
|
@@ -9095,7 +9269,7 @@ var sessionsFleetCmd = async (args, deps) => {
|
|
|
9095
9269
|
async function listFleetRuns(deps) {
|
|
9096
9270
|
let entries = [];
|
|
9097
9271
|
try {
|
|
9098
|
-
entries = await
|
|
9272
|
+
entries = await fsp3.readdir(deps.paths.projectSessions);
|
|
9099
9273
|
} catch {
|
|
9100
9274
|
deps.renderer.writeError(`Cannot read projectSessions: ${deps.paths.projectSessions}
|
|
9101
9275
|
`);
|
|
@@ -9103,10 +9277,10 @@ async function listFleetRuns(deps) {
|
|
|
9103
9277
|
}
|
|
9104
9278
|
const runs = [];
|
|
9105
9279
|
for (const id of entries) {
|
|
9106
|
-
const runDir =
|
|
9280
|
+
const runDir = path25.join(deps.paths.projectSessions, id);
|
|
9107
9281
|
let stat3;
|
|
9108
9282
|
try {
|
|
9109
|
-
stat3 = await
|
|
9283
|
+
stat3 = await fsp3.stat(runDir);
|
|
9110
9284
|
} catch {
|
|
9111
9285
|
continue;
|
|
9112
9286
|
}
|
|
@@ -9116,18 +9290,18 @@ async function listFleetRuns(deps) {
|
|
|
9116
9290
|
let subagentCount = 0;
|
|
9117
9291
|
let subagentsDir;
|
|
9118
9292
|
try {
|
|
9119
|
-
await
|
|
9293
|
+
await fsp3.access(path25.join(runDir, "fleet.json"));
|
|
9120
9294
|
manifest = true;
|
|
9121
9295
|
} catch {
|
|
9122
9296
|
}
|
|
9123
9297
|
try {
|
|
9124
|
-
await
|
|
9298
|
+
await fsp3.access(path25.join(runDir, "checkpoint.json"));
|
|
9125
9299
|
checkpoint = true;
|
|
9126
9300
|
} catch {
|
|
9127
9301
|
}
|
|
9128
9302
|
try {
|
|
9129
|
-
subagentsDir =
|
|
9130
|
-
const files = await
|
|
9303
|
+
subagentsDir = path25.join(runDir, "subagents");
|
|
9304
|
+
const files = await fsp3.readdir(subagentsDir);
|
|
9131
9305
|
subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
|
|
9132
9306
|
} catch {
|
|
9133
9307
|
}
|
|
@@ -9155,10 +9329,10 @@ async function listFleetRuns(deps) {
|
|
|
9155
9329
|
return 0;
|
|
9156
9330
|
}
|
|
9157
9331
|
async function showFleetRun(runId, deps) {
|
|
9158
|
-
const runDir =
|
|
9332
|
+
const runDir = path25.join(deps.paths.projectSessions, runId);
|
|
9159
9333
|
let stat3;
|
|
9160
9334
|
try {
|
|
9161
|
-
stat3 = await
|
|
9335
|
+
stat3 = await fsp3.stat(runDir);
|
|
9162
9336
|
} catch {
|
|
9163
9337
|
deps.renderer.writeError(`Fleet run not found: ${runId}
|
|
9164
9338
|
`);
|
|
@@ -9172,10 +9346,10 @@ async function showFleetRun(runId, deps) {
|
|
|
9172
9346
|
deps.renderer.write(color.bold(`
|
|
9173
9347
|
Fleet Run: ${runId}
|
|
9174
9348
|
`) + "\n");
|
|
9175
|
-
const manifestPath =
|
|
9349
|
+
const manifestPath = path25.join(runDir, "fleet.json");
|
|
9176
9350
|
let manifestData = null;
|
|
9177
9351
|
try {
|
|
9178
|
-
manifestData = await
|
|
9352
|
+
manifestData = await fsp3.readFile(manifestPath, "utf8");
|
|
9179
9353
|
const manifest = JSON.parse(manifestData);
|
|
9180
9354
|
const subagents = manifest.subagents ?? [];
|
|
9181
9355
|
const tasks = manifest.tasks ?? [];
|
|
@@ -9188,15 +9362,15 @@ Fleet Run: ${runId}
|
|
|
9188
9362
|
deps.renderer.write(` ${color.dim("\u25CB")} fleet.json \u2014 not found
|
|
9189
9363
|
`);
|
|
9190
9364
|
}
|
|
9191
|
-
const checkpointPath =
|
|
9365
|
+
const checkpointPath = path25.join(runDir, "checkpoint.json");
|
|
9192
9366
|
let checkpointData = null;
|
|
9193
9367
|
try {
|
|
9194
|
-
checkpointData = await
|
|
9368
|
+
checkpointData = await fsp3.readFile(checkpointPath, "utf8");
|
|
9195
9369
|
const snap = JSON.parse(checkpointData);
|
|
9196
9370
|
const lockPath = `${checkpointPath}.lock`;
|
|
9197
9371
|
let lockStatus = color.dim("\u25CB no lock");
|
|
9198
9372
|
try {
|
|
9199
|
-
const lockRaw = await
|
|
9373
|
+
const lockRaw = await fsp3.readFile(lockPath, "utf8");
|
|
9200
9374
|
const lock = JSON.parse(lockRaw);
|
|
9201
9375
|
lockStatus = `${color.yellow("\u25B8")} lock held by pid ${lock.pid} on ${lock.hostname} (started ${lock.startedAt})`;
|
|
9202
9376
|
} catch {
|
|
@@ -9235,10 +9409,10 @@ Fleet Run: ${runId}
|
|
|
9235
9409
|
} catch {
|
|
9236
9410
|
}
|
|
9237
9411
|
}
|
|
9238
|
-
const subagentsDir =
|
|
9412
|
+
const subagentsDir = path25.join(runDir, "subagents");
|
|
9239
9413
|
let subagentFiles = [];
|
|
9240
9414
|
try {
|
|
9241
|
-
subagentFiles = await
|
|
9415
|
+
subagentFiles = await fsp3.readdir(subagentsDir);
|
|
9242
9416
|
subagentFiles = subagentFiles.filter((f) => f.endsWith(".jsonl"));
|
|
9243
9417
|
} catch {
|
|
9244
9418
|
}
|
|
@@ -9247,10 +9421,10 @@ Fleet Run: ${runId}
|
|
|
9247
9421
|
Subagent transcripts (${subagentFiles.length}):
|
|
9248
9422
|
`);
|
|
9249
9423
|
for (const f of subagentFiles.sort()) {
|
|
9250
|
-
const filePath =
|
|
9424
|
+
const filePath = path25.join(subagentsDir, f);
|
|
9251
9425
|
let size;
|
|
9252
9426
|
try {
|
|
9253
|
-
const s = await
|
|
9427
|
+
const s = await fsp3.stat(filePath);
|
|
9254
9428
|
size = s.size;
|
|
9255
9429
|
} catch {
|
|
9256
9430
|
size = 0;
|
|
@@ -9264,9 +9438,9 @@ Fleet Run: ${runId}
|
|
|
9264
9438
|
${color.dim("\u25CB")} No subagent transcripts
|
|
9265
9439
|
`);
|
|
9266
9440
|
}
|
|
9267
|
-
const sharedDir =
|
|
9441
|
+
const sharedDir = path25.join(runDir, "shared");
|
|
9268
9442
|
try {
|
|
9269
|
-
const files = await
|
|
9443
|
+
const files = await fsp3.readdir(sharedDir);
|
|
9270
9444
|
deps.renderer.write(`
|
|
9271
9445
|
Shared scratchpad: ${files.length} file(s)
|
|
9272
9446
|
`);
|
|
@@ -9431,7 +9605,7 @@ function findSessionId(args) {
|
|
|
9431
9605
|
var rewindCmd = async (args, deps) => {
|
|
9432
9606
|
const flags = parseRewindFlags(args);
|
|
9433
9607
|
const wpaths = resolveWstackPaths({ projectRoot: deps.projectRoot });
|
|
9434
|
-
const sessionsDir =
|
|
9608
|
+
const sessionsDir = path25.join(wpaths.globalRoot, "sessions");
|
|
9435
9609
|
const rewind = new DefaultSessionRewinder(sessionsDir);
|
|
9436
9610
|
let sessionId = findSessionId(args);
|
|
9437
9611
|
if (!sessionId) {
|
|
@@ -9668,7 +9842,7 @@ function resolveBundledSkillsDir() {
|
|
|
9668
9842
|
try {
|
|
9669
9843
|
const req2 = createRequire(import.meta.url);
|
|
9670
9844
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
9671
|
-
return
|
|
9845
|
+
return path25.join(path25.dirname(corePkg), "skills");
|
|
9672
9846
|
} catch {
|
|
9673
9847
|
return void 0;
|
|
9674
9848
|
}
|
|
@@ -9830,7 +10004,7 @@ async function boot(argv) {
|
|
|
9830
10004
|
if (choices.director) flags["director"] = true;
|
|
9831
10005
|
flags["autonomy"] = choices.autonomy;
|
|
9832
10006
|
printLaunchHints(renderer, flags, {
|
|
9833
|
-
cursorFile:
|
|
10007
|
+
cursorFile: path25.join(wpaths.cacheDir, "hint-cursor")
|
|
9834
10008
|
});
|
|
9835
10009
|
}
|
|
9836
10010
|
return {
|
|
@@ -10753,6 +10927,36 @@ async function execute(deps) {
|
|
|
10753
10927
|
const banneredFamily = savedProviderCfg?.family ?? resolvedProvider?.family;
|
|
10754
10928
|
const banneredKey = savedProviderCfg?.apiKey ?? config.apiKey ?? (resolvedProvider?.envVars ?? savedProviderCfg?.envVars ?? []).map((v) => process.env[v]).find((v) => !!v);
|
|
10755
10929
|
const banneredKeyTail = banneredKey && banneredKey.length >= 3 ? banneredKey.slice(-3) : void 0;
|
|
10930
|
+
const autoPhaseHandlers = /* @__PURE__ */ new Map();
|
|
10931
|
+
const subscribeAutoPhase = (handler) => {
|
|
10932
|
+
const registrations = [];
|
|
10933
|
+
const autoPhaseEvents = [
|
|
10934
|
+
"phase.started",
|
|
10935
|
+
"phase.completed",
|
|
10936
|
+
"phase.failed",
|
|
10937
|
+
"phase.statusChange",
|
|
10938
|
+
"phase.taskCompleted",
|
|
10939
|
+
"phase.taskFailed",
|
|
10940
|
+
"phase.taskRetrying",
|
|
10941
|
+
"autonomous.tick",
|
|
10942
|
+
"graph.completed",
|
|
10943
|
+
"graph.failed",
|
|
10944
|
+
"agent.assigned",
|
|
10945
|
+
"agent.released"
|
|
10946
|
+
];
|
|
10947
|
+
const onUntyped = events.on.bind(events);
|
|
10948
|
+
const offUntyped = events.off.bind(events);
|
|
10949
|
+
for (const ev of autoPhaseEvents) {
|
|
10950
|
+
const h = (p) => handler(ev, p);
|
|
10951
|
+
autoPhaseHandlers.set(ev, h);
|
|
10952
|
+
onUntyped(ev, h);
|
|
10953
|
+
registrations.push(() => offUntyped(ev, h));
|
|
10954
|
+
}
|
|
10955
|
+
return () => {
|
|
10956
|
+
for (const unregister of registrations) unregister();
|
|
10957
|
+
autoPhaseHandlers.clear();
|
|
10958
|
+
};
|
|
10959
|
+
};
|
|
10756
10960
|
try {
|
|
10757
10961
|
code = await runTui({
|
|
10758
10962
|
agent,
|
|
@@ -10771,6 +10975,7 @@ async function execute(deps) {
|
|
|
10771
10975
|
getEternalEngine,
|
|
10772
10976
|
subscribeEternalIteration,
|
|
10773
10977
|
subscribeEternalStage,
|
|
10978
|
+
subscribeAutoPhase,
|
|
10774
10979
|
appVersion: CLI_VERSION,
|
|
10775
10980
|
provider: config.provider,
|
|
10776
10981
|
family: banneredFamily,
|
|
@@ -10861,7 +11066,7 @@ async function execute(deps) {
|
|
|
10861
11066
|
supportsVision,
|
|
10862
11067
|
attachments,
|
|
10863
11068
|
effectiveMaxContext,
|
|
10864
|
-
projectName:
|
|
11069
|
+
projectName: path25.basename(projectRoot) || void 0,
|
|
10865
11070
|
projectRoot,
|
|
10866
11071
|
getAutonomy,
|
|
10867
11072
|
onAutonomy,
|
|
@@ -10885,7 +11090,7 @@ async function execute(deps) {
|
|
|
10885
11090
|
supportsVision,
|
|
10886
11091
|
attachments,
|
|
10887
11092
|
effectiveMaxContext,
|
|
10888
|
-
projectName:
|
|
11093
|
+
projectName: path25.basename(projectRoot) || void 0,
|
|
10889
11094
|
getAutonomy,
|
|
10890
11095
|
onAutonomy,
|
|
10891
11096
|
getEternalEngine,
|
|
@@ -10989,7 +11194,7 @@ var MultiAgentHost = class {
|
|
|
10989
11194
|
doneCondition: { type: "all_tasks_done" },
|
|
10990
11195
|
maxConcurrent: this.opts.maxConcurrent ?? 4
|
|
10991
11196
|
};
|
|
10992
|
-
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ?
|
|
11197
|
+
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path25.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
|
|
10993
11198
|
this.director = new Director({
|
|
10994
11199
|
config: coordinatorConfig,
|
|
10995
11200
|
manifestPath: this.opts.manifestPath,
|
|
@@ -11234,7 +11439,7 @@ var MultiAgentHost = class {
|
|
|
11234
11439
|
model: opts?.model,
|
|
11235
11440
|
tools: opts?.tools
|
|
11236
11441
|
};
|
|
11237
|
-
const transcriptPath = this.sessionFactory ?
|
|
11442
|
+
const transcriptPath = this.sessionFactory ? path25.join(this.sessionFactory.dir, `${subagentConfig.name}.jsonl`) : void 0;
|
|
11238
11443
|
const { subagentId, taskId } = await this._spawnAndAssign(subagentConfig);
|
|
11239
11444
|
this.fleetManager?.addPendingTask(taskId, subagentId, description);
|
|
11240
11445
|
this.deps.events.emit("subagent.spawned", {
|
|
@@ -11377,16 +11582,16 @@ var MultiAgentHost = class {
|
|
|
11377
11582
|
if (this.director) return this.director;
|
|
11378
11583
|
this.opts.directorMode = true;
|
|
11379
11584
|
if (this.opts.fleetRoot && !this.opts.manifestPath) {
|
|
11380
|
-
this.opts.manifestPath =
|
|
11585
|
+
this.opts.manifestPath = path25.join(this.opts.fleetRoot, "fleet.json");
|
|
11381
11586
|
}
|
|
11382
11587
|
if (this.opts.fleetRoot && !this.opts.sharedScratchpadPath) {
|
|
11383
|
-
this.opts.sharedScratchpadPath =
|
|
11588
|
+
this.opts.sharedScratchpadPath = path25.join(this.opts.fleetRoot, "shared");
|
|
11384
11589
|
}
|
|
11385
11590
|
if (this.opts.fleetRoot && !this.opts.sessionsRoot) {
|
|
11386
|
-
this.opts.sessionsRoot =
|
|
11591
|
+
this.opts.sessionsRoot = path25.join(this.opts.fleetRoot, "subagents");
|
|
11387
11592
|
}
|
|
11388
11593
|
if (this.opts.fleetRoot && !this.opts.stateCheckpointPath) {
|
|
11389
|
-
this.opts.stateCheckpointPath =
|
|
11594
|
+
this.opts.stateCheckpointPath = path25.join(this.opts.fleetRoot, "director-state.json");
|
|
11390
11595
|
}
|
|
11391
11596
|
await this.ensureDirector();
|
|
11392
11597
|
return this.director ?? null;
|
|
@@ -11552,11 +11757,11 @@ var SessionStats = class {
|
|
|
11552
11757
|
if (e.name === "bash") this.bashCommands++;
|
|
11553
11758
|
else if (e.name === "fetch") this.fetches++;
|
|
11554
11759
|
if (!e.ok) return;
|
|
11555
|
-
const
|
|
11556
|
-
if (e.name === "read" &&
|
|
11557
|
-
else if (e.name === "edit" &&
|
|
11558
|
-
else if (e.name === "write" &&
|
|
11559
|
-
this.writtenPaths.add(
|
|
11760
|
+
const path26 = typeof input?.path === "string" ? input.path : void 0;
|
|
11761
|
+
if (e.name === "read" && path26) this.readPaths.add(path26);
|
|
11762
|
+
else if (e.name === "edit" && path26) this.editedPaths.add(path26);
|
|
11763
|
+
else if (e.name === "write" && path26) {
|
|
11764
|
+
this.writtenPaths.add(path26);
|
|
11560
11765
|
const content = typeof input?.content === "string" ? input.content : "";
|
|
11561
11766
|
this.bytesWritten += Buffer.byteLength(content, "utf8");
|
|
11562
11767
|
}
|
|
@@ -11660,6 +11865,150 @@ function samplePaths(set) {
|
|
|
11660
11865
|
if (arr.length <= 2) return arr.join(", ");
|
|
11661
11866
|
return `${arr[0]}, \u2026 (+${arr.length - 1} more)`;
|
|
11662
11867
|
}
|
|
11868
|
+
function createAutoPhaseHost(deps) {
|
|
11869
|
+
const store = new PhaseStore({ baseDir: deps.storeDir });
|
|
11870
|
+
let active = null;
|
|
11871
|
+
const log = deps.log ?? (() => {
|
|
11872
|
+
});
|
|
11873
|
+
async function runOnce(prompt, label, signal) {
|
|
11874
|
+
const factory = deps.multiAgentHost.makeSubagentFactory(deps.getConfig());
|
|
11875
|
+
const built = await factory({ name: label });
|
|
11876
|
+
try {
|
|
11877
|
+
const result = await built.agent.run(prompt, { signal });
|
|
11878
|
+
if (result.status !== "done") {
|
|
11879
|
+
throw new Error(result.error?.message ?? `subagent ended with status "${result.status}"`);
|
|
11880
|
+
}
|
|
11881
|
+
return result.finalText ?? "";
|
|
11882
|
+
} finally {
|
|
11883
|
+
await built.dispose?.();
|
|
11884
|
+
}
|
|
11885
|
+
}
|
|
11886
|
+
function buildTaskPrompt(task, phaseName, goal) {
|
|
11887
|
+
return [
|
|
11888
|
+
`You are executing one task inside an autonomous, phase-based build.`,
|
|
11889
|
+
`Overall goal: ${goal}`,
|
|
11890
|
+
`Current phase: ${phaseName}`,
|
|
11891
|
+
"",
|
|
11892
|
+
`TASK: ${task.title}`,
|
|
11893
|
+
task.description ? `Details: ${task.description}` : "",
|
|
11894
|
+
`Type: ${task.type} \xB7 Priority: ${task.priority}`,
|
|
11895
|
+
"",
|
|
11896
|
+
`Do the work now using your tools (read, edit, write, bash, \u2026). Make the`,
|
|
11897
|
+
`change real \u2014 do not just describe it. When finished, end with a one-line`,
|
|
11898
|
+
`summary of what you changed. If the task is impossible or already done,`,
|
|
11899
|
+
`say so explicitly.`
|
|
11900
|
+
].filter(Boolean).join("\n");
|
|
11901
|
+
}
|
|
11902
|
+
async function persist(graph) {
|
|
11903
|
+
try {
|
|
11904
|
+
await store.save(graph);
|
|
11905
|
+
} catch (err) {
|
|
11906
|
+
log(`\u26A0 AutoPhase save failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
11907
|
+
}
|
|
11908
|
+
}
|
|
11909
|
+
return {
|
|
11910
|
+
async onAutoPhaseStart({ goal, projectContext }) {
|
|
11911
|
+
if (active?.orchestrator.isRunning()) {
|
|
11912
|
+
return { ok: false, error: "An AutoPhase run is already in progress. Use /autophase stop first." };
|
|
11913
|
+
}
|
|
11914
|
+
const abort = new AbortController();
|
|
11915
|
+
log(`\u{1F9E0} Planning phases for: ${goal}`);
|
|
11916
|
+
let phases;
|
|
11917
|
+
try {
|
|
11918
|
+
const planner = new AutoPhasePlanner({
|
|
11919
|
+
goal,
|
|
11920
|
+
projectContext,
|
|
11921
|
+
runOnce: (p) => runOnce(p, "autophase-planner", abort.signal)
|
|
11922
|
+
});
|
|
11923
|
+
const result = await planner.plan();
|
|
11924
|
+
if (result.parseFailed || result.phases.length === 0) {
|
|
11925
|
+
return { ok: false, error: "The planner did not produce a usable phase plan. Try a more specific goal." };
|
|
11926
|
+
}
|
|
11927
|
+
phases = result.phases;
|
|
11928
|
+
} catch (err) {
|
|
11929
|
+
return { ok: false, error: `Planning failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
11930
|
+
}
|
|
11931
|
+
const todoCount = phases.reduce((n, p) => n + (p.taskTemplates?.length ?? 0), 0);
|
|
11932
|
+
log(`\u{1F4CB} Plan ready: ${phases.length} phases, ${todoCount} todos.`);
|
|
11933
|
+
const graph = await new PhaseGraphBuilder({
|
|
11934
|
+
title: goal,
|
|
11935
|
+
phases,
|
|
11936
|
+
autonomous: true
|
|
11937
|
+
}).build();
|
|
11938
|
+
await persist(graph);
|
|
11939
|
+
const orchestrator = new PhaseOrchestrator({
|
|
11940
|
+
graph,
|
|
11941
|
+
ctx: {
|
|
11942
|
+
executeTask: async (task, phaseId) => {
|
|
11943
|
+
const phase = graph.phases.get(phaseId);
|
|
11944
|
+
const phaseName = phase?.name ?? phaseId;
|
|
11945
|
+
return runOnce(
|
|
11946
|
+
buildTaskPrompt(task, phaseName, goal),
|
|
11947
|
+
`autophase-${phaseName}-${task.title}`.slice(0, 48),
|
|
11948
|
+
abort.signal
|
|
11949
|
+
);
|
|
11950
|
+
},
|
|
11951
|
+
onPhaseComplete: (phase) => {
|
|
11952
|
+
log(`\u2705 Phase completed: ${phase.name}`);
|
|
11953
|
+
void persist(graph);
|
|
11954
|
+
},
|
|
11955
|
+
onPhaseFail: (phase, error) => {
|
|
11956
|
+
log(`\u274C Phase failed: ${phase.name} \u2014 ${error.message}`);
|
|
11957
|
+
void persist(graph);
|
|
11958
|
+
}
|
|
11959
|
+
},
|
|
11960
|
+
events: deps.events,
|
|
11961
|
+
autonomous: true,
|
|
11962
|
+
maxConcurrentPhases: 1,
|
|
11963
|
+
// Sequential within a phase: each todo is a full-tool agent editing the
|
|
11964
|
+
// shared working tree, and todos in a phase typically build on one
|
|
11965
|
+
// another. Running two at once risks concurrent writes / lost edits.
|
|
11966
|
+
maxConcurrentTasks: 1
|
|
11967
|
+
});
|
|
11968
|
+
const onUntyped = deps.events.on;
|
|
11969
|
+
const offUntyped = deps.events.off;
|
|
11970
|
+
const onDone = () => {
|
|
11971
|
+
log(`\u{1F389} AutoPhase complete: ${graph.title}`);
|
|
11972
|
+
void persist(graph);
|
|
11973
|
+
};
|
|
11974
|
+
const onFailed = () => void persist(graph);
|
|
11975
|
+
onUntyped("graph.completed", onDone);
|
|
11976
|
+
onUntyped("graph.failed", onFailed);
|
|
11977
|
+
const unsubscribe = () => {
|
|
11978
|
+
offUntyped("graph.completed", onDone);
|
|
11979
|
+
offUntyped("graph.failed", onFailed);
|
|
11980
|
+
};
|
|
11981
|
+
active = { graph, orchestrator, abort, unsubscribe };
|
|
11982
|
+
void orchestrator.start().catch((err) => {
|
|
11983
|
+
log(`\u{1F4A5} AutoPhase aborted: ${err instanceof Error ? err.message : String(err)}`);
|
|
11984
|
+
});
|
|
11985
|
+
return { ok: true, graph };
|
|
11986
|
+
},
|
|
11987
|
+
onAutoPhasePause() {
|
|
11988
|
+
active?.orchestrator.pause();
|
|
11989
|
+
},
|
|
11990
|
+
onAutoPhaseResume() {
|
|
11991
|
+
active?.orchestrator.resume();
|
|
11992
|
+
},
|
|
11993
|
+
onAutoPhaseStop() {
|
|
11994
|
+
if (!active) return;
|
|
11995
|
+
active.abort.abort();
|
|
11996
|
+
active.orchestrator.stop();
|
|
11997
|
+
active.unsubscribe();
|
|
11998
|
+
void persist(active.graph);
|
|
11999
|
+
active = null;
|
|
12000
|
+
},
|
|
12001
|
+
getAutoPhaseRunner() {
|
|
12002
|
+
if (!active) return null;
|
|
12003
|
+
const a = active;
|
|
12004
|
+
return {
|
|
12005
|
+
graph: a.graph,
|
|
12006
|
+
getProgress: () => a.orchestrator.getProgress(),
|
|
12007
|
+
isRunning: () => a.orchestrator.isRunning()
|
|
12008
|
+
};
|
|
12009
|
+
}
|
|
12010
|
+
};
|
|
12011
|
+
}
|
|
11663
12012
|
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
11664
12013
|
var FILLED2 = "\u2588";
|
|
11665
12014
|
var EMPTY2 = "\u2591";
|
|
@@ -11827,7 +12176,7 @@ function setupMetrics(params) {
|
|
|
11827
12176
|
name: "session-store",
|
|
11828
12177
|
check: async () => {
|
|
11829
12178
|
try {
|
|
11830
|
-
await
|
|
12179
|
+
await fsp3.access(wpaths.projectSessions);
|
|
11831
12180
|
return { status: "healthy" };
|
|
11832
12181
|
} catch (e) {
|
|
11833
12182
|
return { status: "unhealthy", detail: e instanceof Error ? e.message : "access denied" };
|
|
@@ -11844,7 +12193,7 @@ function setupMetrics(params) {
|
|
|
11844
12193
|
const dumpMetrics = () => {
|
|
11845
12194
|
if (!metricsSink) return;
|
|
11846
12195
|
try {
|
|
11847
|
-
const out =
|
|
12196
|
+
const out = path25.join(wpaths.projectSessions, "metrics.json");
|
|
11848
12197
|
const snap = metricsSink.snapshot();
|
|
11849
12198
|
writeFileSync(out, JSON.stringify(snap, null, 2));
|
|
11850
12199
|
} catch {
|
|
@@ -12035,12 +12384,12 @@ async function setupSession(params) {
|
|
|
12035
12384
|
}
|
|
12036
12385
|
const sessionRef = { current: session };
|
|
12037
12386
|
await recoveryLock.write(session.id).catch(() => void 0);
|
|
12038
|
-
const attachments = new DefaultAttachmentStore({ spoolDir:
|
|
12039
|
-
const queueStore = new QueueStore({ dir:
|
|
12387
|
+
const attachments = new DefaultAttachmentStore({ spoolDir: path25.join(wpaths.projectSessions, session.id, "attachments") });
|
|
12388
|
+
const queueStore = new QueueStore({ dir: path25.join(wpaths.projectSessions, session.id) });
|
|
12040
12389
|
const ctxSignal = new AbortController().signal;
|
|
12041
12390
|
const context = new Context({ systemPrompt, provider, session, signal: ctxSignal, tokenCounter, cwd, projectRoot, model: config.model });
|
|
12042
12391
|
if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
|
|
12043
|
-
const todosCheckpointPath =
|
|
12392
|
+
const todosCheckpointPath = path25.join(wpaths.projectSessions, `${session.id}.todos.json`);
|
|
12044
12393
|
if (resumeId) {
|
|
12045
12394
|
try {
|
|
12046
12395
|
const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
|
|
@@ -12052,13 +12401,13 @@ async function setupSession(params) {
|
|
|
12052
12401
|
}
|
|
12053
12402
|
}
|
|
12054
12403
|
const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session.id);
|
|
12055
|
-
const planPath =
|
|
12404
|
+
const planPath = path25.join(wpaths.projectSessions, `${session.id}.plan.json`);
|
|
12056
12405
|
context.state.setMeta("plan.path", planPath);
|
|
12057
12406
|
let dirState;
|
|
12058
12407
|
if (resumeId) {
|
|
12059
12408
|
try {
|
|
12060
|
-
const fleetRoot =
|
|
12061
|
-
dirState = await loadDirectorState(
|
|
12409
|
+
const fleetRoot = path25.join(wpaths.projectSessions, session.id);
|
|
12410
|
+
dirState = await loadDirectorState(path25.join(fleetRoot, "director-state.json"));
|
|
12062
12411
|
if (dirState) {
|
|
12063
12412
|
const tCounts = {};
|
|
12064
12413
|
for (const t of dirState.tasks) tCounts[t.status] = (tCounts[t.status] ?? 0) + 1;
|
|
@@ -12085,7 +12434,7 @@ function resolveBundledSkillsDir2() {
|
|
|
12085
12434
|
try {
|
|
12086
12435
|
const req2 = createRequire(import.meta.url);
|
|
12087
12436
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
12088
|
-
return
|
|
12437
|
+
return path25.join(path25.dirname(corePkg), "skills");
|
|
12089
12438
|
} catch {
|
|
12090
12439
|
return void 0;
|
|
12091
12440
|
}
|
|
@@ -12185,7 +12534,7 @@ async function main(argv) {
|
|
|
12185
12534
|
modeId,
|
|
12186
12535
|
modePrompt,
|
|
12187
12536
|
modelCapabilities,
|
|
12188
|
-
planPath: () => sessionRef.current ?
|
|
12537
|
+
planPath: () => sessionRef.current ? path25.join(wpaths.projectSessions, `${sessionRef.current.id}.plan.json`) : void 0,
|
|
12189
12538
|
contributors: [
|
|
12190
12539
|
// Injects the ETERNAL AUTONOMY block when the user has activated
|
|
12191
12540
|
// `/autonomy eternal`. Without this, the per-iteration directive
|
|
@@ -12389,12 +12738,12 @@ async function main(argv) {
|
|
|
12389
12738
|
}
|
|
12390
12739
|
}
|
|
12391
12740
|
};
|
|
12392
|
-
const fleetRoot = directorMode ?
|
|
12393
|
-
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] :
|
|
12394
|
-
const sharedScratchpadPath = directorMode ?
|
|
12395
|
-
const subagentSessionsRoot = directorMode ?
|
|
12396
|
-
const stateCheckpointPath = directorMode ?
|
|
12397
|
-
const fleetRootForPromotion =
|
|
12741
|
+
const fleetRoot = directorMode ? path25.join(wpaths.projectSessions, session.id) : void 0;
|
|
12742
|
+
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path25.join(fleetRoot, "fleet.json") : void 0;
|
|
12743
|
+
const sharedScratchpadPath = directorMode ? path25.join(fleetRoot, "shared") : void 0;
|
|
12744
|
+
const subagentSessionsRoot = directorMode ? path25.join(fleetRoot, "subagents") : void 0;
|
|
12745
|
+
const stateCheckpointPath = directorMode ? path25.join(fleetRoot, "director-state.json") : void 0;
|
|
12746
|
+
const fleetRootForPromotion = path25.join(wpaths.projectSessions, session.id);
|
|
12398
12747
|
const multiAgentHost = new MultiAgentHost(
|
|
12399
12748
|
{
|
|
12400
12749
|
container,
|
|
@@ -12483,6 +12832,14 @@ async function main(argv) {
|
|
|
12483
12832
|
this.visible = visible;
|
|
12484
12833
|
}
|
|
12485
12834
|
};
|
|
12835
|
+
const autoPhaseHost = createAutoPhaseHost({
|
|
12836
|
+
multiAgentHost,
|
|
12837
|
+
getConfig: () => config,
|
|
12838
|
+
events,
|
|
12839
|
+
storeDir: wpaths.projectAutophase,
|
|
12840
|
+
log: (line) => renderer.write(`${line}
|
|
12841
|
+
`)
|
|
12842
|
+
});
|
|
12486
12843
|
const slashCmds = buildBuiltinSlashCommands({
|
|
12487
12844
|
registry: slashRegistry,
|
|
12488
12845
|
toolRegistry,
|
|
@@ -12492,6 +12849,7 @@ async function main(argv) {
|
|
|
12492
12849
|
skillLoader,
|
|
12493
12850
|
tokenCounter,
|
|
12494
12851
|
renderer,
|
|
12852
|
+
events,
|
|
12495
12853
|
memoryStore,
|
|
12496
12854
|
context,
|
|
12497
12855
|
cwd,
|
|
@@ -12717,27 +13075,27 @@ async function main(argv) {
|
|
|
12717
13075
|
return director.spawn(cfg);
|
|
12718
13076
|
},
|
|
12719
13077
|
onFleetLog: async (subagentId, mode) => {
|
|
12720
|
-
const subagentsRoot =
|
|
13078
|
+
const subagentsRoot = path25.join(fleetRootForPromotion, "subagents");
|
|
12721
13079
|
let runDirs;
|
|
12722
13080
|
try {
|
|
12723
|
-
runDirs = await
|
|
13081
|
+
runDirs = await fsp3.readdir(subagentsRoot);
|
|
12724
13082
|
} catch {
|
|
12725
13083
|
return "No fleet transcripts on disk \u2014 no subagents have been spawned for this session.";
|
|
12726
13084
|
}
|
|
12727
13085
|
const found = [];
|
|
12728
13086
|
for (const runId of runDirs) {
|
|
12729
|
-
const runDir =
|
|
13087
|
+
const runDir = path25.join(subagentsRoot, runId);
|
|
12730
13088
|
let files;
|
|
12731
13089
|
try {
|
|
12732
|
-
files = await
|
|
13090
|
+
files = await fsp3.readdir(runDir);
|
|
12733
13091
|
} catch {
|
|
12734
13092
|
continue;
|
|
12735
13093
|
}
|
|
12736
13094
|
for (const f of files) {
|
|
12737
13095
|
if (!f.endsWith(".jsonl")) continue;
|
|
12738
|
-
const full =
|
|
13096
|
+
const full = path25.join(runDir, f);
|
|
12739
13097
|
try {
|
|
12740
|
-
const stat3 = await
|
|
13098
|
+
const stat3 = await fsp3.stat(full);
|
|
12741
13099
|
found.push({
|
|
12742
13100
|
runId,
|
|
12743
13101
|
subagentId: f.replace(/\.jsonl$/, ""),
|
|
@@ -12776,7 +13134,7 @@ async function main(argv) {
|
|
|
12776
13134
|
].join("\n");
|
|
12777
13135
|
}
|
|
12778
13136
|
const t = matches[0];
|
|
12779
|
-
const raw = await
|
|
13137
|
+
const raw = await fsp3.readFile(t.file, "utf8");
|
|
12780
13138
|
if (mode === "raw") return raw;
|
|
12781
13139
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
12782
13140
|
const counts = {};
|
|
@@ -12832,7 +13190,7 @@ async function main(argv) {
|
|
|
12832
13190
|
}
|
|
12833
13191
|
const dir = await multiAgentHost.ensureDirector();
|
|
12834
13192
|
if (!dir) return "Director is not available.";
|
|
12835
|
-
const dirStatePath =
|
|
13193
|
+
const dirStatePath = path25.join(fleetRootForPromotion, "director-state.json");
|
|
12836
13194
|
const prior = await loadDirectorState(dirStatePath);
|
|
12837
13195
|
if (!prior) {
|
|
12838
13196
|
return "No prior director-state.json found \u2014 nothing to retry.";
|
|
@@ -12903,9 +13261,9 @@ async function main(argv) {
|
|
|
12903
13261
|
for (const tool of director2.tools(FLEET_ROSTER)) {
|
|
12904
13262
|
toolRegistry.register(tool);
|
|
12905
13263
|
}
|
|
12906
|
-
const mp =
|
|
12907
|
-
const sp =
|
|
12908
|
-
const ss =
|
|
13264
|
+
const mp = path25.join(fleetRootForPromotion, "fleet.json");
|
|
13265
|
+
const sp = path25.join(fleetRootForPromotion, "shared");
|
|
13266
|
+
const ss = path25.join(fleetRootForPromotion, "subagents");
|
|
12909
13267
|
const lines = [
|
|
12910
13268
|
`${color.green("\u2713")} Promoted to director mode.`,
|
|
12911
13269
|
` Roster: ${Object.keys(FLEET_ROSTER).join(", ")}`,
|
|
@@ -13108,7 +13466,12 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
13108
13466
|
onSddParallelStop: () => {
|
|
13109
13467
|
const run = globalThis.__sddParallelRun;
|
|
13110
13468
|
run?.stop();
|
|
13111
|
-
}
|
|
13469
|
+
},
|
|
13470
|
+
onAutoPhaseStart: autoPhaseHost.onAutoPhaseStart,
|
|
13471
|
+
onAutoPhasePause: autoPhaseHost.onAutoPhasePause,
|
|
13472
|
+
onAutoPhaseResume: autoPhaseHost.onAutoPhaseResume,
|
|
13473
|
+
onAutoPhaseStop: autoPhaseHost.onAutoPhaseStop,
|
|
13474
|
+
getAutoPhaseRunner: autoPhaseHost.getAutoPhaseRunner
|
|
13112
13475
|
});
|
|
13113
13476
|
for (const cmd of slashCmds) slashRegistry.register(cmd);
|
|
13114
13477
|
const eternalFlag = typeof flags["eternal"] === "string" ? flags["eternal"].trim() : "";
|