@wrongstack/cli 0.8.4 → 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 +435 -296
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
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,
|
|
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
|
-
import * as
|
|
9
|
-
import
|
|
8
|
+
import * as os6 from 'os';
|
|
9
|
+
import os6__default from 'os';
|
|
10
10
|
import * as crypto2 from 'crypto';
|
|
11
11
|
import { randomUUID } from 'crypto';
|
|
12
12
|
import { DefaultSecretVault as DefaultSecretVault$1, encryptConfigSecrets, isSecretField, decryptConfigSecrets as decryptConfigSecrets$1 } from '@wrongstack/core/security';
|
|
@@ -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();
|
|
@@ -1399,7 +1399,7 @@ async function gatherProjectContext(projectRoot) {
|
|
|
1399
1399
|
const parts = [];
|
|
1400
1400
|
try {
|
|
1401
1401
|
const pkgPath = path25.join(projectRoot, "package.json");
|
|
1402
|
-
const pkgRaw = await
|
|
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")}`);
|
|
@@ -1415,13 +1415,13 @@ async function gatherProjectContext(projectRoot) {
|
|
|
1415
1415
|
}
|
|
1416
1416
|
try {
|
|
1417
1417
|
const tsconfigPath = path25.join(projectRoot, "tsconfig.json");
|
|
1418
|
-
await
|
|
1418
|
+
await fsp3.access(tsconfigPath);
|
|
1419
1419
|
parts.push("Language: TypeScript");
|
|
1420
1420
|
} catch {
|
|
1421
1421
|
}
|
|
1422
1422
|
try {
|
|
1423
1423
|
const srcDir = path25.join(projectRoot, "src");
|
|
1424
|
-
const entries = await
|
|
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/")}`);
|
|
@@ -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;
|
|
@@ -1614,8 +1614,8 @@ async function readCache(homeFn = defaultHomeDir2) {
|
|
|
1614
1614
|
async function writeCache(entry, homeFn = defaultHomeDir2) {
|
|
1615
1615
|
try {
|
|
1616
1616
|
const dir = path25.dirname(cachePath(homeFn));
|
|
1617
|
-
await
|
|
1618
|
-
await
|
|
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
|
}
|
|
@@ -1686,7 +1686,7 @@ async function getUpdateNotification(signal, homeFn) {
|
|
|
1686
1686
|
var defaultHomeDir2, CACHE_TTL_MS;
|
|
1687
1687
|
var init_update_check = __esm({
|
|
1688
1688
|
"src/update-check.ts"() {
|
|
1689
|
-
defaultHomeDir2 = () =>
|
|
1689
|
+
defaultHomeDir2 = () => os6.homedir();
|
|
1690
1690
|
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
1691
1691
|
}
|
|
1692
1692
|
});
|
|
@@ -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
|
}
|
|
@@ -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(
|
|
@@ -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;
|
|
@@ -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`;
|
|
@@ -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";
|
|
@@ -3428,8 +3445,8 @@ function buildInitCommand(opts) {
|
|
|
3428
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.`;
|
|
@@ -3618,7 +3635,7 @@ function stateBadge(state) {
|
|
|
3618
3635
|
}
|
|
3619
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
|
}
|
|
@@ -3626,8 +3643,8 @@ async function readConfig(path26) {
|
|
|
3626
3643
|
async function writeConfig(path26, cfg) {
|
|
3627
3644
|
const raw = JSON.stringify(cfg, null, 2);
|
|
3628
3645
|
const tmp = path26 + ".tmp";
|
|
3629
|
-
await
|
|
3630
|
-
await
|
|
3646
|
+
await fsp3.writeFile(tmp, raw, "utf8");
|
|
3647
|
+
await fsp3.rename(tmp, path26);
|
|
3631
3648
|
}
|
|
3632
3649
|
|
|
3633
3650
|
// src/slash-commands/mcp.ts
|
|
@@ -4899,7 +4916,7 @@ function resolveConfigPath() {
|
|
|
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,7 +5824,7 @@ 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 = path25.join(
|
|
5827
|
+
const globalRoot = path25.join(os6.homedir(), ".wrongstack");
|
|
5811
5828
|
return new SkillInstaller({
|
|
5812
5829
|
manifestPath: path25.join(globalRoot, "installed-skills.json"),
|
|
5813
5830
|
projectSkillsDir: path25.join(projectRoot, ".wrongstack", "skills"),
|
|
@@ -5967,229 +5984,162 @@ function buildSkillUninstallCommand(opts) {
|
|
|
5967
5984
|
}
|
|
5968
5985
|
};
|
|
5969
5986
|
}
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
var DEFAULT_PHASES = [
|
|
5973
|
-
{
|
|
5974
|
-
name: "Discovery",
|
|
5975
|
-
description: "Requirements gathering and analysis",
|
|
5976
|
-
priority: "high",
|
|
5977
|
-
estimateHours: 2,
|
|
5978
|
-
parallelizable: false
|
|
5979
|
-
},
|
|
5980
|
-
{
|
|
5981
|
-
name: "Design",
|
|
5982
|
-
description: "Architecture and design decisions",
|
|
5983
|
-
priority: "critical",
|
|
5984
|
-
estimateHours: 4,
|
|
5985
|
-
parallelizable: false
|
|
5986
|
-
},
|
|
5987
|
-
{
|
|
5988
|
-
name: "Implementation",
|
|
5989
|
-
description: "Core feature development",
|
|
5990
|
-
priority: "critical",
|
|
5991
|
-
estimateHours: 12,
|
|
5992
|
-
parallelizable: false
|
|
5993
|
-
},
|
|
5994
|
-
{
|
|
5995
|
-
name: "Testing",
|
|
5996
|
-
description: "Unit, integration, and e2e tests",
|
|
5997
|
-
priority: "high",
|
|
5998
|
-
estimateHours: 6,
|
|
5999
|
-
parallelizable: true
|
|
6000
|
-
},
|
|
6001
|
-
{
|
|
6002
|
-
name: "Deployment",
|
|
6003
|
-
description: "Deploy to production",
|
|
6004
|
-
priority: "medium",
|
|
6005
|
-
estimateHours: 2,
|
|
6006
|
-
parallelizable: false
|
|
6007
|
-
}
|
|
6008
|
-
];
|
|
6009
|
-
function getStore() {
|
|
6010
|
-
const baseDir = path25.join(os7.homedir(), ".wrongstack", "autophase");
|
|
6011
|
-
return new PhaseStore({ baseDir });
|
|
5987
|
+
function getStore(opts) {
|
|
5988
|
+
return new PhaseStore({ baseDir: opts.paths.projectAutophase });
|
|
6012
5989
|
}
|
|
6013
5990
|
function formatProgress(p) {
|
|
6014
|
-
const
|
|
5991
|
+
const filled = Math.floor(p.percentComplete / 5);
|
|
5992
|
+
const bars = "\u2588".repeat(filled) + "\u2591".repeat(20 - filled);
|
|
6015
5993
|
return [
|
|
6016
5994
|
`
|
|
6017
5995
|
\u{1F4CA} Progress: ${bars} ${p.percentComplete}%`,
|
|
6018
5996
|
` \u{1F4CB} Phases: ${p.completed}/${p.totalPhases} done, ${p.running} running, ${p.pending} pending`,
|
|
6019
5997
|
` \u2705 Tasks: ${p.completedTasks}/${p.totalTasks} completed`,
|
|
6020
|
-
` \u23F1
|
|
5998
|
+
` \u23F1 Est: ${p.estimatedHours.toFixed(1)}h | Actual: ${p.actualHours.toFixed(1)}h`
|
|
6021
5999
|
].join("\n");
|
|
6022
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
|
+
};
|
|
6023
6010
|
function formatPhaseList(graph) {
|
|
6024
6011
|
const phases = Array.from(graph.phases.values());
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
const emoji = statusEmoji[p.status] ?? "\u26AA";
|
|
6036
|
-
const progress = p.taskGraph.nodes.size > 0 ? `${Array.from(p.taskGraph.nodes.values()).filter((t) => t.status === "completed").length}/${p.taskGraph.nodes.size}` : "0/0";
|
|
6037
|
-
return ` ${i + 1}. ${emoji} ${p.name} (${p.status}) \u2014 ${progress} tasks`;
|
|
6038
|
-
}).join("\n");
|
|
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");
|
|
6039
6022
|
}
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
}
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
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
|
+
};
|
|
6097
6080
|
}
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
if (!currentRunner) {
|
|
6103
|
-
return { message: "\u274C Aktif AutoPhase yok. \xD6nce `/autophase start` \xE7al\u0131\u015Ft\u0131r\u0131n." };
|
|
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." };
|
|
6104
6085
|
}
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
if (!currentRunner) {
|
|
6110
|
-
return { message: "\u274C Aktif AutoPhase yok." };
|
|
6086
|
+
case "resume": {
|
|
6087
|
+
if (!opts.onAutoPhaseResume) return { message: "\u274C AutoPhase host not available." };
|
|
6088
|
+
opts.onAutoPhaseResume();
|
|
6089
|
+
return { message: "\u25B6 AutoPhase resuming." };
|
|
6111
6090
|
}
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
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();
|
|
6118
6104
|
const graphs = await store.list();
|
|
6119
|
-
if (graphs.length === 0) {
|
|
6120
|
-
|
|
6121
|
-
}
|
|
6122
|
-
const
|
|
6123
|
-
return { message:
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
const phaseList = formatPhaseList(currentGraph);
|
|
6128
|
-
return {
|
|
6129
|
-
message: [
|
|
6130
|
-
`**${currentGraph.title}**`,
|
|
6131
|
-
progress ? formatProgress(progress) : "",
|
|
6132
|
-
"",
|
|
6133
|
-
"**Fazlar:**",
|
|
6134
|
-
phaseList,
|
|
6135
|
-
"",
|
|
6136
|
-
currentRunner.isPaused() ? "\u23F8\uFE0F Duraklat\u0131ld\u0131" : currentRunner.isRunning() ? "\u{1F504} \xC7al\u0131\u015F\u0131yor" : "\u23F9\uFE0F Durdu"
|
|
6137
|
-
].join("\n")
|
|
6138
|
-
};
|
|
6139
|
-
}
|
|
6140
|
-
case "list": {
|
|
6141
|
-
const graphs = await store.list();
|
|
6142
|
-
if (graphs.length === 0) {
|
|
6143
|
-
return { message: "Kay\u0131tl\u0131 AutoPhase projesi yok." };
|
|
6144
|
-
}
|
|
6145
|
-
const list = graphs.map((g) => {
|
|
6146
|
-
const statusEmoji = g.status === "completed" ? "\u2705" : g.status === "in_progress" ? "\u{1F504}" : "\u23F3";
|
|
6147
|
-
return ` ${statusEmoji} ${g.title} (g\xFCncelleme: ${new Date(g.updatedAt).toLocaleDateString("tr-TR")})`;
|
|
6148
|
-
}).join("\n");
|
|
6149
|
-
return { message: `**Kay\u0131tl\u0131 Projeler:**
|
|
6150
|
-
${list}` };
|
|
6151
|
-
}
|
|
6152
|
-
case "load": {
|
|
6153
|
-
const graphId = parts[1];
|
|
6154
|
-
if (!graphId) {
|
|
6155
|
-
return { message: "\u274C Graph ID gerekli. Kullan\u0131m: `/autophase load <id>`" };
|
|
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
|
+
};
|
|
6156
6113
|
}
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
return { message:
|
|
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
|
+
};
|
|
6160
6123
|
}
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
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
|
+
};
|
|
6171
6138
|
}
|
|
6172
|
-
await store.save(currentGraph);
|
|
6173
|
-
return { message: `\u2705 **${currentGraph.title}** kaydedildi.` };
|
|
6174
6139
|
}
|
|
6175
|
-
default:
|
|
6176
|
-
return {
|
|
6177
|
-
message: [
|
|
6178
|
-
"**AutoPhase Komutlar\u0131:**",
|
|
6179
|
-
"",
|
|
6180
|
-
"`/autophase start [title]` \u2014 Yeni proje ba\u015Flat",
|
|
6181
|
-
"`/autophase pause` \u2014 Duraklat",
|
|
6182
|
-
"`/autophase resume` \u2014 Devam et",
|
|
6183
|
-
"`/autophase stop` \u2014 Durdur",
|
|
6184
|
-
"`/autophase status` \u2014 Durum g\xF6ster",
|
|
6185
|
-
"`/autophase list` \u2014 Kay\u0131tl\u0131 projeleri listele",
|
|
6186
|
-
"`/autophase load <id>` \u2014 Projeyi y\xFCkle",
|
|
6187
|
-
"`/autophase save` \u2014 Aktif projeyi kaydet"
|
|
6188
|
-
].join("\n")
|
|
6189
|
-
};
|
|
6190
6140
|
}
|
|
6191
|
-
}
|
|
6192
|
-
}
|
|
6141
|
+
};
|
|
6142
|
+
}
|
|
6193
6143
|
|
|
6194
6144
|
// src/slash-commands/index.ts
|
|
6195
6145
|
function buildBuiltinSlashCommands(opts) {
|
|
@@ -6232,7 +6182,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
6232
6182
|
buildPushCommand(),
|
|
6233
6183
|
buildSecurityCommand(opts),
|
|
6234
6184
|
buildFixCommand(opts),
|
|
6235
|
-
|
|
6185
|
+
buildAutoPhaseCommand(opts),
|
|
6236
6186
|
buildStatuslineCommand({
|
|
6237
6187
|
cwd: opts.cwd,
|
|
6238
6188
|
hiddenItems: opts.statuslineHiddenItems ?? [],
|
|
@@ -6260,13 +6210,13 @@ var MANIFESTS = [
|
|
|
6260
6210
|
];
|
|
6261
6211
|
async function detectProjectKind(projectRoot) {
|
|
6262
6212
|
try {
|
|
6263
|
-
await
|
|
6213
|
+
await fsp3.access(path25.join(projectRoot, ".wrongstack", "AGENTS.md"));
|
|
6264
6214
|
return "initialized";
|
|
6265
6215
|
} catch {
|
|
6266
6216
|
}
|
|
6267
6217
|
for (const m of MANIFESTS) {
|
|
6268
6218
|
try {
|
|
6269
|
-
await
|
|
6219
|
+
await fsp3.access(path25.join(projectRoot, m));
|
|
6270
6220
|
return "project";
|
|
6271
6221
|
} catch {
|
|
6272
6222
|
}
|
|
@@ -6278,8 +6228,8 @@ async function scaffoldAgentsMd(projectRoot) {
|
|
|
6278
6228
|
const file = path25.join(dir, "AGENTS.md");
|
|
6279
6229
|
const facts = await detectProjectFacts(projectRoot);
|
|
6280
6230
|
const body = renderAgentsTemplate(facts);
|
|
6281
|
-
await
|
|
6282
|
-
await
|
|
6231
|
+
await fsp3.mkdir(dir, { recursive: true });
|
|
6232
|
+
await fsp3.writeFile(file, body, "utf8");
|
|
6283
6233
|
return file;
|
|
6284
6234
|
}
|
|
6285
6235
|
async function runProjectCheck(opts) {
|
|
@@ -6322,7 +6272,7 @@ async function runProjectCheck(opts) {
|
|
|
6322
6272
|
const gitDir = path25.join(projectRoot, ".git");
|
|
6323
6273
|
let hasGit = false;
|
|
6324
6274
|
try {
|
|
6325
|
-
await
|
|
6275
|
+
await fsp3.access(gitDir);
|
|
6326
6276
|
hasGit = true;
|
|
6327
6277
|
} catch {
|
|
6328
6278
|
}
|
|
@@ -6447,7 +6397,7 @@ async function bootConfig(flags) {
|
|
|
6447
6397
|
const cwd = typeof flags["cwd"] === "string" ? path25.resolve(flags["cwd"]) : process.cwd();
|
|
6448
6398
|
const pathResolver = new DefaultPathResolver(cwd);
|
|
6449
6399
|
const projectRoot = pathResolver.projectRoot;
|
|
6450
|
-
const userHome =
|
|
6400
|
+
const userHome = os6.homedir();
|
|
6451
6401
|
const wpaths = resolveWstackPaths({ projectRoot, userHome });
|
|
6452
6402
|
await ensureProjectMeta(wpaths, projectRoot);
|
|
6453
6403
|
const vault = new DefaultSecretVault({ keyFile: wpaths.secretsKey });
|
|
@@ -6495,13 +6445,13 @@ function flagsToConfigPatch(flags) {
|
|
|
6495
6445
|
}
|
|
6496
6446
|
async function ensureProjectMeta(paths, projectRoot) {
|
|
6497
6447
|
try {
|
|
6498
|
-
await
|
|
6448
|
+
await fsp3.mkdir(paths.projectDir, { recursive: true });
|
|
6499
6449
|
const meta = {
|
|
6500
6450
|
hash: paths.projectHash,
|
|
6501
6451
|
root: projectRoot,
|
|
6502
6452
|
lastSeen: (/* @__PURE__ */ new Date()).toISOString()
|
|
6503
6453
|
};
|
|
6504
|
-
await
|
|
6454
|
+
await fsp3.writeFile(paths.projectMeta, JSON.stringify(meta, null, 2));
|
|
6505
6455
|
} catch {
|
|
6506
6456
|
}
|
|
6507
6457
|
}
|
|
@@ -6511,11 +6461,11 @@ var ReadlineInputReader = class {
|
|
|
6511
6461
|
history = [];
|
|
6512
6462
|
pending = false;
|
|
6513
6463
|
constructor(opts = {}) {
|
|
6514
|
-
this.historyFile = opts.historyFile ?? path25.join(
|
|
6464
|
+
this.historyFile = opts.historyFile ?? path25.join(os6.homedir(), ".wrongstack", "history");
|
|
6515
6465
|
}
|
|
6516
6466
|
async loadHistory() {
|
|
6517
6467
|
try {
|
|
6518
|
-
const raw = await
|
|
6468
|
+
const raw = await fsp3.readFile(this.historyFile, "utf8");
|
|
6519
6469
|
this.history = raw.split("\n").filter(Boolean).slice(-1e3);
|
|
6520
6470
|
} catch {
|
|
6521
6471
|
this.history = [];
|
|
@@ -6523,8 +6473,8 @@ var ReadlineInputReader = class {
|
|
|
6523
6473
|
}
|
|
6524
6474
|
async saveHistory() {
|
|
6525
6475
|
try {
|
|
6526
|
-
await
|
|
6527
|
-
await
|
|
6476
|
+
await fsp3.mkdir(path25.dirname(this.historyFile), { recursive: true });
|
|
6477
|
+
await fsp3.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
|
|
6528
6478
|
} catch {
|
|
6529
6479
|
}
|
|
6530
6480
|
}
|
|
@@ -6766,7 +6716,7 @@ async function safeDelete(filePath) {
|
|
|
6766
6716
|
const filename = path25.basename(filePath);
|
|
6767
6717
|
try {
|
|
6768
6718
|
assertSafeToDelete(filename, dir);
|
|
6769
|
-
await
|
|
6719
|
+
await fsp3.unlink(filePath);
|
|
6770
6720
|
} catch (err) {
|
|
6771
6721
|
if (err instanceof Error && err.message.startsWith("Refusing")) {
|
|
6772
6722
|
process.stderr.write(`[config-history] SAFETY: ${err.message}
|
|
@@ -6806,7 +6756,7 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
6806
6756
|
}
|
|
6807
6757
|
return changes.length > 0 ? changes.slice(0, 5).join(", ") : "no changes";
|
|
6808
6758
|
}
|
|
6809
|
-
var defaultHomeDir = () =>
|
|
6759
|
+
var defaultHomeDir = () => os6__default.homedir();
|
|
6810
6760
|
function historyDir(homeFn = defaultHomeDir) {
|
|
6811
6761
|
return path25.join(homeFn(), ".wrongstack", "config.history", "entries");
|
|
6812
6762
|
}
|
|
@@ -6824,7 +6774,7 @@ function entryId(ts) {
|
|
|
6824
6774
|
}
|
|
6825
6775
|
async function ensureHistoryDir(homeFn = defaultHomeDir) {
|
|
6826
6776
|
try {
|
|
6827
|
-
await
|
|
6777
|
+
await fsp3.mkdir(historyDir(homeFn), { recursive: true });
|
|
6828
6778
|
} catch (err) {
|
|
6829
6779
|
throw new FsError({
|
|
6830
6780
|
message: err instanceof Error ? err.message : String(err),
|
|
@@ -6836,7 +6786,7 @@ async function ensureHistoryDir(homeFn = defaultHomeDir) {
|
|
|
6836
6786
|
}
|
|
6837
6787
|
async function readIndex(homeFn = defaultHomeDir) {
|
|
6838
6788
|
try {
|
|
6839
|
-
const raw = await
|
|
6789
|
+
const raw = await fsp3.readFile(historyIndexPath(homeFn), "utf8");
|
|
6840
6790
|
return JSON.parse(raw);
|
|
6841
6791
|
} catch {
|
|
6842
6792
|
return { version: 1, entries: [] };
|
|
@@ -6861,7 +6811,7 @@ async function backupCurrent(homeFn = defaultHomeDir) {
|
|
|
6861
6811
|
const ts = Date.now();
|
|
6862
6812
|
let content;
|
|
6863
6813
|
try {
|
|
6864
|
-
content = await
|
|
6814
|
+
content = await fsp3.readFile(cfg, "utf8");
|
|
6865
6815
|
} catch {
|
|
6866
6816
|
}
|
|
6867
6817
|
if (content !== void 0) {
|
|
@@ -6879,7 +6829,7 @@ async function backupCurrent(homeFn = defaultHomeDir) {
|
|
|
6879
6829
|
}
|
|
6880
6830
|
try {
|
|
6881
6831
|
const dir = path25.join(homeFn(), ".wrongstack");
|
|
6882
|
-
const files = await
|
|
6832
|
+
const files = await fsp3.readdir(dir);
|
|
6883
6833
|
const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
|
|
6884
6834
|
for (const f of baks.slice(10)) {
|
|
6885
6835
|
await safeDelete(path25.join(dir, f));
|
|
@@ -6899,7 +6849,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
6899
6849
|
diffSummary: diffSummary(oldCfg, newCfg)
|
|
6900
6850
|
};
|
|
6901
6851
|
try {
|
|
6902
|
-
await
|
|
6852
|
+
await fsp3.writeFile(
|
|
6903
6853
|
path25.join(historyDir(homeFn), `${id}.json`),
|
|
6904
6854
|
JSON.stringify(entry, null, 2),
|
|
6905
6855
|
"utf8"
|
|
@@ -6923,7 +6873,7 @@ async function listHistory(homeFn = defaultHomeDir) {
|
|
|
6923
6873
|
}
|
|
6924
6874
|
async function getHistoryEntry(id, homeFn = defaultHomeDir) {
|
|
6925
6875
|
try {
|
|
6926
|
-
const raw = await
|
|
6876
|
+
const raw = await fsp3.readFile(path25.join(historyDir(homeFn), `${id}.json`), "utf8");
|
|
6927
6877
|
return JSON.parse(raw);
|
|
6928
6878
|
} catch {
|
|
6929
6879
|
return null;
|
|
@@ -6935,7 +6885,7 @@ async function restoreFromHistory(id, homeFn = defaultHomeDir) {
|
|
|
6935
6885
|
await backupCurrent(homeFn);
|
|
6936
6886
|
let oldCfg = {};
|
|
6937
6887
|
try {
|
|
6938
|
-
const raw = await
|
|
6888
|
+
const raw = await fsp3.readFile(configPath(homeFn), "utf8");
|
|
6939
6889
|
oldCfg = JSON.parse(raw);
|
|
6940
6890
|
} catch {
|
|
6941
6891
|
}
|
|
@@ -6957,13 +6907,13 @@ async function restoreLast(homeFn = defaultHomeDir) {
|
|
|
6957
6907
|
const cfg = configPath(homeFn);
|
|
6958
6908
|
let oldCfg = {};
|
|
6959
6909
|
try {
|
|
6960
|
-
const raw = await
|
|
6910
|
+
const raw = await fsp3.readFile(cfg, "utf8");
|
|
6961
6911
|
oldCfg = JSON.parse(raw);
|
|
6962
6912
|
} catch {
|
|
6963
6913
|
}
|
|
6964
6914
|
let lastCfg = {};
|
|
6965
6915
|
try {
|
|
6966
|
-
const raw = await
|
|
6916
|
+
const raw = await fsp3.readFile(last, "utf8");
|
|
6967
6917
|
lastCfg = JSON.parse(raw);
|
|
6968
6918
|
} catch {
|
|
6969
6919
|
return { ok: false, error: "No prior backup found" };
|
|
@@ -6994,7 +6944,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
|
|
|
6994
6944
|
existing.provider = provider;
|
|
6995
6945
|
existing.model = model;
|
|
6996
6946
|
await backupCurrent(homeFn);
|
|
6997
|
-
await atomicWrite8(configPath2, JSON.stringify(existing, null, 2));
|
|
6947
|
+
await atomicWrite8(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
6998
6948
|
try {
|
|
6999
6949
|
await appendHistory(
|
|
7000
6950
|
oldCfg,
|
|
@@ -8424,7 +8374,7 @@ async function readKeyInput(deps, intent) {
|
|
|
8424
8374
|
async function loadProviders(deps) {
|
|
8425
8375
|
let raw;
|
|
8426
8376
|
try {
|
|
8427
|
-
raw = await
|
|
8377
|
+
raw = await fsp3.readFile(deps.globalConfigPath, "utf8");
|
|
8428
8378
|
} catch (err) {
|
|
8429
8379
|
if (err.code !== "ENOENT") {
|
|
8430
8380
|
deps.renderer.writeWarning(
|
|
@@ -8449,7 +8399,7 @@ async function mutateProviders(deps, mutator) {
|
|
|
8449
8399
|
let raw;
|
|
8450
8400
|
let fileExists = true;
|
|
8451
8401
|
try {
|
|
8452
|
-
raw = await
|
|
8402
|
+
raw = await fsp3.readFile(deps.globalConfigPath, "utf8");
|
|
8453
8403
|
} catch (err) {
|
|
8454
8404
|
if (err.code !== "ENOENT") {
|
|
8455
8405
|
throw new Error(
|
|
@@ -8595,7 +8545,7 @@ var diagCmd = async (_args, deps) => {
|
|
|
8595
8545
|
` modelsCache: ${deps.paths.modelsCache}`,
|
|
8596
8546
|
` cacheAge: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never"}`,
|
|
8597
8547
|
` node: ${process.version}`,
|
|
8598
|
-
` os: ${
|
|
8548
|
+
` os: ${os6.platform()} ${os6.release()}`,
|
|
8599
8549
|
` provider: ${cfg.provider ?? "<unset>"}`,
|
|
8600
8550
|
` model: ${cfg.model ?? "<unset>"}`,
|
|
8601
8551
|
` tools: ${deps.toolRegistry?.list().length ?? 0}`,
|
|
@@ -8663,7 +8613,7 @@ var doctorCmd = async (_args, deps) => {
|
|
|
8663
8613
|
});
|
|
8664
8614
|
}
|
|
8665
8615
|
try {
|
|
8666
|
-
await
|
|
8616
|
+
await fsp3.access(deps.paths.secretsKey);
|
|
8667
8617
|
checks.push({ name: "secret vault", status: "ok", detail: deps.paths.secretsKey });
|
|
8668
8618
|
} catch {
|
|
8669
8619
|
checks.push({
|
|
@@ -8673,10 +8623,10 @@ var doctorCmd = async (_args, deps) => {
|
|
|
8673
8623
|
});
|
|
8674
8624
|
}
|
|
8675
8625
|
try {
|
|
8676
|
-
await
|
|
8626
|
+
await fsp3.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
8677
8627
|
const probe = path25.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
8678
|
-
await
|
|
8679
|
-
await
|
|
8628
|
+
await fsp3.writeFile(probe, "");
|
|
8629
|
+
await fsp3.unlink(probe);
|
|
8680
8630
|
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
8681
8631
|
} catch (err) {
|
|
8682
8632
|
checks.push({
|
|
@@ -8777,8 +8727,8 @@ var exportCmd = async (args, deps) => {
|
|
|
8777
8727
|
return 1;
|
|
8778
8728
|
}
|
|
8779
8729
|
if (output) {
|
|
8780
|
-
await
|
|
8781
|
-
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");
|
|
8782
8732
|
deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
|
|
8783
8733
|
`);
|
|
8784
8734
|
} else {
|
|
@@ -8845,13 +8795,13 @@ var initCmd = async (_args, deps) => {
|
|
|
8845
8795
|
} else {
|
|
8846
8796
|
deps.renderer.writeInfo(`Found API key in env (${provider.envVars.join(" / ")}).`);
|
|
8847
8797
|
}
|
|
8848
|
-
await
|
|
8798
|
+
await fsp3.mkdir(deps.paths.globalRoot, { recursive: true });
|
|
8849
8799
|
const config = { version: 1, provider: providerId, model: modelId };
|
|
8850
8800
|
if (apiKey) config.apiKey = apiKey;
|
|
8851
8801
|
const vault = new DefaultSecretVault$1({ keyFile: deps.paths.secretsKey });
|
|
8852
8802
|
const encrypted = encryptConfigSecrets(config, vault);
|
|
8853
|
-
await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2));
|
|
8854
|
-
await
|
|
8803
|
+
await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
8804
|
+
await fsp3.mkdir(path25.join(deps.projectRoot, ".wrongstack"), { recursive: true });
|
|
8855
8805
|
const agentsFile = path25.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
|
|
8856
8806
|
const projectFacts = await detectProjectFacts(deps.projectRoot);
|
|
8857
8807
|
await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
|
|
@@ -8926,7 +8876,7 @@ async function addMcpServer(args, deps) {
|
|
|
8926
8876
|
serverCfg.enabled = enable;
|
|
8927
8877
|
let existing = {};
|
|
8928
8878
|
try {
|
|
8929
|
-
existing = JSON.parse(await
|
|
8879
|
+
existing = JSON.parse(await fsp3.readFile(deps.paths.globalConfig, "utf8"));
|
|
8930
8880
|
} catch {
|
|
8931
8881
|
}
|
|
8932
8882
|
const mcpServers = existing.mcpServers ?? {};
|
|
@@ -8935,7 +8885,7 @@ async function addMcpServer(args, deps) {
|
|
|
8935
8885
|
`);
|
|
8936
8886
|
mcpServers[name] = serverCfg;
|
|
8937
8887
|
existing.mcpServers = mcpServers;
|
|
8938
|
-
await atomicWrite(deps.paths.globalConfig, JSON.stringify(existing, null, 2));
|
|
8888
|
+
await atomicWrite(deps.paths.globalConfig, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
8939
8889
|
const verb = enable ? "Enabled" : "Added (disabled \u2014 set enabled:true to activate)";
|
|
8940
8890
|
deps.renderer.writeInfo(
|
|
8941
8891
|
`${verb} "${name}" (${serverCfg.transport}). Config written to ${deps.paths.globalConfig}.
|
|
@@ -8946,7 +8896,7 @@ async function addMcpServer(args, deps) {
|
|
|
8946
8896
|
async function removeMcpServer(name, deps) {
|
|
8947
8897
|
let existing = {};
|
|
8948
8898
|
try {
|
|
8949
|
-
existing = JSON.parse(await
|
|
8899
|
+
existing = JSON.parse(await fsp3.readFile(deps.paths.globalConfig, "utf8"));
|
|
8950
8900
|
} catch {
|
|
8951
8901
|
deps.renderer.writeError("No config file found.\n");
|
|
8952
8902
|
return 1;
|
|
@@ -8959,7 +8909,7 @@ async function removeMcpServer(name, deps) {
|
|
|
8959
8909
|
}
|
|
8960
8910
|
delete mcpServers[name];
|
|
8961
8911
|
existing.mcpServers = mcpServers;
|
|
8962
|
-
await atomicWrite(deps.paths.globalConfig, JSON.stringify(existing, null, 2));
|
|
8912
|
+
await atomicWrite(deps.paths.globalConfig, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
8963
8913
|
deps.renderer.writeInfo(`Removed "${name}" from config.
|
|
8964
8914
|
`);
|
|
8965
8915
|
return 0;
|
|
@@ -9067,7 +9017,7 @@ function renderConfiguredPlugins(config) {
|
|
|
9067
9017
|
}
|
|
9068
9018
|
async function readConfig2(file) {
|
|
9069
9019
|
try {
|
|
9070
|
-
return JSON.parse(await
|
|
9020
|
+
return JSON.parse(await fsp3.readFile(file, "utf8"));
|
|
9071
9021
|
} catch {
|
|
9072
9022
|
return {};
|
|
9073
9023
|
}
|
|
@@ -9097,7 +9047,7 @@ async function upsertPlugin(spec, opts, deps, verb) {
|
|
|
9097
9047
|
};
|
|
9098
9048
|
existing.plugins = plugins;
|
|
9099
9049
|
existing.features = features;
|
|
9100
|
-
await atomicWrite(deps.configPath, JSON.stringify(existing, null, 2));
|
|
9050
|
+
await atomicWrite(deps.configPath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
9101
9051
|
return {
|
|
9102
9052
|
code: 0,
|
|
9103
9053
|
level: "info",
|
|
@@ -9114,7 +9064,7 @@ async function removePlugin(spec, deps) {
|
|
|
9114
9064
|
return errorResult(`Plugin "${spec}" not in config.`);
|
|
9115
9065
|
}
|
|
9116
9066
|
existing.plugins = next;
|
|
9117
|
-
await atomicWrite(deps.configPath, JSON.stringify(existing, null, 2));
|
|
9067
|
+
await atomicWrite(deps.configPath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
9118
9068
|
return {
|
|
9119
9069
|
code: 0,
|
|
9120
9070
|
level: "info",
|
|
@@ -9160,7 +9110,7 @@ var usageCmd = async (_args, deps) => {
|
|
|
9160
9110
|
var projectsCmd = async (_args, deps) => {
|
|
9161
9111
|
const projectsRoot = path25.join(deps.paths.globalRoot, "projects");
|
|
9162
9112
|
try {
|
|
9163
|
-
const entries = await
|
|
9113
|
+
const entries = await fsp3.readdir(projectsRoot);
|
|
9164
9114
|
if (entries.length === 0) {
|
|
9165
9115
|
deps.renderer.write("No projects tracked.\n");
|
|
9166
9116
|
return 0;
|
|
@@ -9168,7 +9118,7 @@ var projectsCmd = async (_args, deps) => {
|
|
|
9168
9118
|
for (const hash of entries) {
|
|
9169
9119
|
try {
|
|
9170
9120
|
const meta = JSON.parse(
|
|
9171
|
-
await
|
|
9121
|
+
await fsp3.readFile(path25.join(projectsRoot, hash, "meta.json"), "utf8")
|
|
9172
9122
|
);
|
|
9173
9123
|
deps.renderer.write(
|
|
9174
9124
|
` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
|
|
@@ -9319,7 +9269,7 @@ var sessionsFleetCmd = async (args, deps) => {
|
|
|
9319
9269
|
async function listFleetRuns(deps) {
|
|
9320
9270
|
let entries = [];
|
|
9321
9271
|
try {
|
|
9322
|
-
entries = await
|
|
9272
|
+
entries = await fsp3.readdir(deps.paths.projectSessions);
|
|
9323
9273
|
} catch {
|
|
9324
9274
|
deps.renderer.writeError(`Cannot read projectSessions: ${deps.paths.projectSessions}
|
|
9325
9275
|
`);
|
|
@@ -9330,7 +9280,7 @@ async function listFleetRuns(deps) {
|
|
|
9330
9280
|
const runDir = path25.join(deps.paths.projectSessions, id);
|
|
9331
9281
|
let stat3;
|
|
9332
9282
|
try {
|
|
9333
|
-
stat3 = await
|
|
9283
|
+
stat3 = await fsp3.stat(runDir);
|
|
9334
9284
|
} catch {
|
|
9335
9285
|
continue;
|
|
9336
9286
|
}
|
|
@@ -9340,18 +9290,18 @@ async function listFleetRuns(deps) {
|
|
|
9340
9290
|
let subagentCount = 0;
|
|
9341
9291
|
let subagentsDir;
|
|
9342
9292
|
try {
|
|
9343
|
-
await
|
|
9293
|
+
await fsp3.access(path25.join(runDir, "fleet.json"));
|
|
9344
9294
|
manifest = true;
|
|
9345
9295
|
} catch {
|
|
9346
9296
|
}
|
|
9347
9297
|
try {
|
|
9348
|
-
await
|
|
9298
|
+
await fsp3.access(path25.join(runDir, "checkpoint.json"));
|
|
9349
9299
|
checkpoint = true;
|
|
9350
9300
|
} catch {
|
|
9351
9301
|
}
|
|
9352
9302
|
try {
|
|
9353
9303
|
subagentsDir = path25.join(runDir, "subagents");
|
|
9354
|
-
const files = await
|
|
9304
|
+
const files = await fsp3.readdir(subagentsDir);
|
|
9355
9305
|
subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
|
|
9356
9306
|
} catch {
|
|
9357
9307
|
}
|
|
@@ -9382,7 +9332,7 @@ async function showFleetRun(runId, deps) {
|
|
|
9382
9332
|
const runDir = path25.join(deps.paths.projectSessions, runId);
|
|
9383
9333
|
let stat3;
|
|
9384
9334
|
try {
|
|
9385
|
-
stat3 = await
|
|
9335
|
+
stat3 = await fsp3.stat(runDir);
|
|
9386
9336
|
} catch {
|
|
9387
9337
|
deps.renderer.writeError(`Fleet run not found: ${runId}
|
|
9388
9338
|
`);
|
|
@@ -9399,7 +9349,7 @@ Fleet Run: ${runId}
|
|
|
9399
9349
|
const manifestPath = path25.join(runDir, "fleet.json");
|
|
9400
9350
|
let manifestData = null;
|
|
9401
9351
|
try {
|
|
9402
|
-
manifestData = await
|
|
9352
|
+
manifestData = await fsp3.readFile(manifestPath, "utf8");
|
|
9403
9353
|
const manifest = JSON.parse(manifestData);
|
|
9404
9354
|
const subagents = manifest.subagents ?? [];
|
|
9405
9355
|
const tasks = manifest.tasks ?? [];
|
|
@@ -9415,12 +9365,12 @@ Fleet Run: ${runId}
|
|
|
9415
9365
|
const checkpointPath = path25.join(runDir, "checkpoint.json");
|
|
9416
9366
|
let checkpointData = null;
|
|
9417
9367
|
try {
|
|
9418
|
-
checkpointData = await
|
|
9368
|
+
checkpointData = await fsp3.readFile(checkpointPath, "utf8");
|
|
9419
9369
|
const snap = JSON.parse(checkpointData);
|
|
9420
9370
|
const lockPath = `${checkpointPath}.lock`;
|
|
9421
9371
|
let lockStatus = color.dim("\u25CB no lock");
|
|
9422
9372
|
try {
|
|
9423
|
-
const lockRaw = await
|
|
9373
|
+
const lockRaw = await fsp3.readFile(lockPath, "utf8");
|
|
9424
9374
|
const lock = JSON.parse(lockRaw);
|
|
9425
9375
|
lockStatus = `${color.yellow("\u25B8")} lock held by pid ${lock.pid} on ${lock.hostname} (started ${lock.startedAt})`;
|
|
9426
9376
|
} catch {
|
|
@@ -9462,7 +9412,7 @@ Fleet Run: ${runId}
|
|
|
9462
9412
|
const subagentsDir = path25.join(runDir, "subagents");
|
|
9463
9413
|
let subagentFiles = [];
|
|
9464
9414
|
try {
|
|
9465
|
-
subagentFiles = await
|
|
9415
|
+
subagentFiles = await fsp3.readdir(subagentsDir);
|
|
9466
9416
|
subagentFiles = subagentFiles.filter((f) => f.endsWith(".jsonl"));
|
|
9467
9417
|
} catch {
|
|
9468
9418
|
}
|
|
@@ -9474,7 +9424,7 @@ Fleet Run: ${runId}
|
|
|
9474
9424
|
const filePath = path25.join(subagentsDir, f);
|
|
9475
9425
|
let size;
|
|
9476
9426
|
try {
|
|
9477
|
-
const s = await
|
|
9427
|
+
const s = await fsp3.stat(filePath);
|
|
9478
9428
|
size = s.size;
|
|
9479
9429
|
} catch {
|
|
9480
9430
|
size = 0;
|
|
@@ -9490,7 +9440,7 @@ Fleet Run: ${runId}
|
|
|
9490
9440
|
}
|
|
9491
9441
|
const sharedDir = path25.join(runDir, "shared");
|
|
9492
9442
|
try {
|
|
9493
|
-
const files = await
|
|
9443
|
+
const files = await fsp3.readdir(sharedDir);
|
|
9494
9444
|
deps.renderer.write(`
|
|
9495
9445
|
Shared scratchpad: ${files.length} file(s)
|
|
9496
9446
|
`);
|
|
@@ -9787,7 +9737,7 @@ var skillsCmd = async (_args, deps) => {
|
|
|
9787
9737
|
};
|
|
9788
9738
|
var versionCmd = async (_args, deps) => {
|
|
9789
9739
|
deps.renderer.write(
|
|
9790
|
-
`WrongStack ${CLI_VERSION} (apiVersion ${API_VERSION}, node ${process.version}, ${
|
|
9740
|
+
`WrongStack ${CLI_VERSION} (apiVersion ${API_VERSION}, node ${process.version}, ${os6.platform()})
|
|
9791
9741
|
`
|
|
9792
9742
|
);
|
|
9793
9743
|
return 0;
|
|
@@ -10977,6 +10927,36 @@ async function execute(deps) {
|
|
|
10977
10927
|
const banneredFamily = savedProviderCfg?.family ?? resolvedProvider?.family;
|
|
10978
10928
|
const banneredKey = savedProviderCfg?.apiKey ?? config.apiKey ?? (resolvedProvider?.envVars ?? savedProviderCfg?.envVars ?? []).map((v) => process.env[v]).find((v) => !!v);
|
|
10979
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
|
+
};
|
|
10980
10960
|
try {
|
|
10981
10961
|
code = await runTui({
|
|
10982
10962
|
agent,
|
|
@@ -10995,6 +10975,7 @@ async function execute(deps) {
|
|
|
10995
10975
|
getEternalEngine,
|
|
10996
10976
|
subscribeEternalIteration,
|
|
10997
10977
|
subscribeEternalStage,
|
|
10978
|
+
subscribeAutoPhase,
|
|
10998
10979
|
appVersion: CLI_VERSION,
|
|
10999
10980
|
provider: config.provider,
|
|
11000
10981
|
family: banneredFamily,
|
|
@@ -11884,6 +11865,150 @@ function samplePaths(set) {
|
|
|
11884
11865
|
if (arr.length <= 2) return arr.join(", ");
|
|
11885
11866
|
return `${arr[0]}, \u2026 (+${arr.length - 1} more)`;
|
|
11886
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
|
+
}
|
|
11887
12012
|
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
11888
12013
|
var FILLED2 = "\u2588";
|
|
11889
12014
|
var EMPTY2 = "\u2591";
|
|
@@ -12051,7 +12176,7 @@ function setupMetrics(params) {
|
|
|
12051
12176
|
name: "session-store",
|
|
12052
12177
|
check: async () => {
|
|
12053
12178
|
try {
|
|
12054
|
-
await
|
|
12179
|
+
await fsp3.access(wpaths.projectSessions);
|
|
12055
12180
|
return { status: "healthy" };
|
|
12056
12181
|
} catch (e) {
|
|
12057
12182
|
return { status: "unhealthy", detail: e instanceof Error ? e.message : "access denied" };
|
|
@@ -12707,6 +12832,14 @@ async function main(argv) {
|
|
|
12707
12832
|
this.visible = visible;
|
|
12708
12833
|
}
|
|
12709
12834
|
};
|
|
12835
|
+
const autoPhaseHost = createAutoPhaseHost({
|
|
12836
|
+
multiAgentHost,
|
|
12837
|
+
getConfig: () => config,
|
|
12838
|
+
events,
|
|
12839
|
+
storeDir: wpaths.projectAutophase,
|
|
12840
|
+
log: (line) => renderer.write(`${line}
|
|
12841
|
+
`)
|
|
12842
|
+
});
|
|
12710
12843
|
const slashCmds = buildBuiltinSlashCommands({
|
|
12711
12844
|
registry: slashRegistry,
|
|
12712
12845
|
toolRegistry,
|
|
@@ -12716,6 +12849,7 @@ async function main(argv) {
|
|
|
12716
12849
|
skillLoader,
|
|
12717
12850
|
tokenCounter,
|
|
12718
12851
|
renderer,
|
|
12852
|
+
events,
|
|
12719
12853
|
memoryStore,
|
|
12720
12854
|
context,
|
|
12721
12855
|
cwd,
|
|
@@ -12944,7 +13078,7 @@ async function main(argv) {
|
|
|
12944
13078
|
const subagentsRoot = path25.join(fleetRootForPromotion, "subagents");
|
|
12945
13079
|
let runDirs;
|
|
12946
13080
|
try {
|
|
12947
|
-
runDirs = await
|
|
13081
|
+
runDirs = await fsp3.readdir(subagentsRoot);
|
|
12948
13082
|
} catch {
|
|
12949
13083
|
return "No fleet transcripts on disk \u2014 no subagents have been spawned for this session.";
|
|
12950
13084
|
}
|
|
@@ -12953,7 +13087,7 @@ async function main(argv) {
|
|
|
12953
13087
|
const runDir = path25.join(subagentsRoot, runId);
|
|
12954
13088
|
let files;
|
|
12955
13089
|
try {
|
|
12956
|
-
files = await
|
|
13090
|
+
files = await fsp3.readdir(runDir);
|
|
12957
13091
|
} catch {
|
|
12958
13092
|
continue;
|
|
12959
13093
|
}
|
|
@@ -12961,7 +13095,7 @@ async function main(argv) {
|
|
|
12961
13095
|
if (!f.endsWith(".jsonl")) continue;
|
|
12962
13096
|
const full = path25.join(runDir, f);
|
|
12963
13097
|
try {
|
|
12964
|
-
const stat3 = await
|
|
13098
|
+
const stat3 = await fsp3.stat(full);
|
|
12965
13099
|
found.push({
|
|
12966
13100
|
runId,
|
|
12967
13101
|
subagentId: f.replace(/\.jsonl$/, ""),
|
|
@@ -13000,7 +13134,7 @@ async function main(argv) {
|
|
|
13000
13134
|
].join("\n");
|
|
13001
13135
|
}
|
|
13002
13136
|
const t = matches[0];
|
|
13003
|
-
const raw = await
|
|
13137
|
+
const raw = await fsp3.readFile(t.file, "utf8");
|
|
13004
13138
|
if (mode === "raw") return raw;
|
|
13005
13139
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
13006
13140
|
const counts = {};
|
|
@@ -13332,7 +13466,12 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
13332
13466
|
onSddParallelStop: () => {
|
|
13333
13467
|
const run = globalThis.__sddParallelRun;
|
|
13334
13468
|
run?.stop();
|
|
13335
|
-
}
|
|
13469
|
+
},
|
|
13470
|
+
onAutoPhaseStart: autoPhaseHost.onAutoPhaseStart,
|
|
13471
|
+
onAutoPhasePause: autoPhaseHost.onAutoPhasePause,
|
|
13472
|
+
onAutoPhaseResume: autoPhaseHost.onAutoPhaseResume,
|
|
13473
|
+
onAutoPhaseStop: autoPhaseHost.onAutoPhaseStop,
|
|
13474
|
+
getAutoPhaseRunner: autoPhaseHost.getAutoPhaseRunner
|
|
13336
13475
|
});
|
|
13337
13476
|
for (const cmd of slashCmds) slashRegistry.register(cmd);
|
|
13338
13477
|
const eternalFlag = typeof flags["eternal"] === "string" ? flags["eternal"].trim() : "";
|