@wrongstack/cli 0.257.0 → 0.260.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +785 -424
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as fsp5 from 'fs/promises';
|
|
3
|
-
import * as
|
|
3
|
+
import * as path39 from 'path';
|
|
4
4
|
import { join } from 'path';
|
|
5
|
-
import { color, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, DefaultSecretScrubber, resolveProjectDir, GlobalMailbox, TOKENS, ToolRegistry, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, attachDepWatcherBridge, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, wstackGlobalRoot, DEFAULT_CONTEXT_WINDOW_MODE_ID, recentTextTurns, enhanceUserPrompt, projectSlug, DefaultSystemPromptBuilder, mutateTasks, loadTasks, resolveContextWindowPolicy, repairToolUseAdjacency, mutatePlan, setPlanItemStatus, getPlanTemplate, loadPlan, emptyPlan, addPlanItem, savePlan, DefaultLogger, DefaultModelsRegistry, isStdinTTY, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, makeAutonomyPromptContributor, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore,
|
|
5
|
+
import { color, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, DefaultSecretScrubber, resolveProjectDir, GlobalMailbox, TOKENS, ToolRegistry, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, attachDepWatcherBridge, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, wstackGlobalRoot, DEFAULT_CONTEXT_WINDOW_MODE_ID, recentTextTurns, enhanceUserPrompt, projectSlug, DefaultSystemPromptBuilder, mutateTasks, loadTasks, resolveContextWindowPolicy, repairToolUseAdjacency, mutatePlan, setPlanItemStatus, getPlanTemplate, loadPlan, emptyPlan, addPlanItem, savePlan, DefaultLogger, DefaultModelsRegistry, isStdinTTY, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, makeAutonomyPromptContributor, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, Context, QueueStore, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, createDefaultPipelines, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, noOpVault, decryptConfigSecrets, encryptConfigSecrets, atomicWrite, setQueuedMessagesSnapshot, DefaultSessionRewinder, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionStore as DefaultSessionStore$1, ProviderRegistry, StreamHangError, ProviderError, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, getContextWindowMode, AGENT_CATALOG, dispatchAgent, formatTodosList, formatTaskList, formatTaskProgress, formatPlan, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, matrixKeyKind, phaseForRole, onResize, ERROR_CODES, FsError, ConfigError, InputBuilder, truncate, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, resolveMailboxIdentity, isSecretField as isSecretField$1 } from '@wrongstack/core';
|
|
6
6
|
import { DefaultSecretVault, encryptConfigSecrets as encryptConfigSecrets$1, decryptConfigSecrets as decryptConfigSecrets$1, isSecretField } from '@wrongstack/core/security';
|
|
7
7
|
import * as crypto3 from 'crypto';
|
|
8
|
-
import { createHash, randomUUID } from 'crypto';
|
|
8
|
+
import { createHash, randomBytes, randomUUID } from 'crypto';
|
|
9
9
|
import { createRequire } from 'module';
|
|
10
10
|
import * as os from 'os';
|
|
11
11
|
import os__default from 'os';
|
|
@@ -25,6 +25,7 @@ import { parseNextSteps } from '@wrongstack/tui';
|
|
|
25
25
|
import { ACP_AGENT_COMMANDS, makeACPSubagentRunner, makeACPSubagentRunnerWithStop } from '@wrongstack/acp';
|
|
26
26
|
import { WrongStackACPServer } from '@wrongstack/acp/agent';
|
|
27
27
|
import { ACP_AGENTS, SubagentBudget } from '@wrongstack/core/coordination';
|
|
28
|
+
import { loadBenchConfig, reportHeaderLine, readSummary, renderMarkdownReport, createPolyglotSuite, createSwebenchSuite, runBenchmark, writeJsonArtifacts, collectCellPredictions, writePredictionsJsonl, gradePolyglot, gradeSwebench } from '@wrongstack/bench';
|
|
28
29
|
import { allServers } from '@wrongstack/core/infrastructure';
|
|
29
30
|
import { ToolExecutor } from '@wrongstack/core/execution';
|
|
30
31
|
import { createToolVisionAdapters } from '@wrongstack/runtime/vision';
|
|
@@ -70,10 +71,10 @@ async function detectPackageManager2(root, declared) {
|
|
|
70
71
|
const name = declared.split("@")[0];
|
|
71
72
|
if (name) return name;
|
|
72
73
|
}
|
|
73
|
-
if (await pathExists(
|
|
74
|
-
if (await pathExists(
|
|
75
|
-
if (await pathExists(
|
|
76
|
-
if (await pathExists(
|
|
74
|
+
if (await pathExists(path39.join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
75
|
+
if (await pathExists(path39.join(root, "bun.lockb"))) return "bun";
|
|
76
|
+
if (await pathExists(path39.join(root, "bun.lock"))) return "bun";
|
|
77
|
+
if (await pathExists(path39.join(root, "yarn.lock"))) return "yarn";
|
|
77
78
|
return "npm";
|
|
78
79
|
}
|
|
79
80
|
function hasUsableScript(scripts, name) {
|
|
@@ -94,7 +95,7 @@ function parseMakeTargets(makefile) {
|
|
|
94
95
|
async function detectProjectFacts(root) {
|
|
95
96
|
const facts = { hints: [] };
|
|
96
97
|
try {
|
|
97
|
-
const pkg = JSON.parse(await fsp5.readFile(
|
|
98
|
+
const pkg = JSON.parse(await fsp5.readFile(path39.join(root, "package.json"), "utf8"));
|
|
98
99
|
const scripts = pkg.scripts ?? {};
|
|
99
100
|
const pm = await detectPackageManager2(root, pkg.packageManager);
|
|
100
101
|
if (hasUsableScript(scripts, "build")) facts.build = `${pm} run build`;
|
|
@@ -108,14 +109,14 @@ async function detectProjectFacts(root) {
|
|
|
108
109
|
} catch {
|
|
109
110
|
}
|
|
110
111
|
try {
|
|
111
|
-
if (!await pathExists(
|
|
112
|
+
if (!await pathExists(path39.join(root, "pyproject.toml"))) throw new Error("not python");
|
|
112
113
|
facts.test ??= "pytest";
|
|
113
114
|
facts.lint ??= "ruff check .";
|
|
114
115
|
facts.hints.push("pyproject.toml");
|
|
115
116
|
} catch {
|
|
116
117
|
}
|
|
117
118
|
try {
|
|
118
|
-
if (!await pathExists(
|
|
119
|
+
if (!await pathExists(path39.join(root, "go.mod"))) throw new Error("not go");
|
|
119
120
|
facts.build ??= "go build ./...";
|
|
120
121
|
facts.test ??= "go test ./...";
|
|
121
122
|
facts.run ??= "go run .";
|
|
@@ -123,7 +124,7 @@ async function detectProjectFacts(root) {
|
|
|
123
124
|
} catch {
|
|
124
125
|
}
|
|
125
126
|
try {
|
|
126
|
-
if (!await pathExists(
|
|
127
|
+
if (!await pathExists(path39.join(root, "Cargo.toml"))) throw new Error("not rust");
|
|
127
128
|
facts.build ??= "cargo build";
|
|
128
129
|
facts.test ??= "cargo test";
|
|
129
130
|
facts.lint ??= "cargo clippy";
|
|
@@ -132,7 +133,7 @@ async function detectProjectFacts(root) {
|
|
|
132
133
|
} catch {
|
|
133
134
|
}
|
|
134
135
|
try {
|
|
135
|
-
const makefile = await fsp5.readFile(
|
|
136
|
+
const makefile = await fsp5.readFile(path39.join(root, "Makefile"), "utf8");
|
|
136
137
|
const targets = parseMakeTargets(makefile);
|
|
137
138
|
facts.build ??= targets.has("build") ? "make build" : "make";
|
|
138
139
|
if (targets.has("test")) facts.test ??= "make test";
|
|
@@ -371,26 +372,26 @@ function fmtDuration(ms) {
|
|
|
371
372
|
const remMin = m - h * 60;
|
|
372
373
|
return `${h}h${remMin}m`;
|
|
373
374
|
}
|
|
374
|
-
function fmtTaskResultLine(r,
|
|
375
|
+
function fmtTaskResultLine(r, color72) {
|
|
375
376
|
const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
|
|
376
377
|
const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
|
|
377
378
|
const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
|
|
378
379
|
const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
|
|
379
|
-
const errKindChip = errKind ?
|
|
380
|
-
const errSnip = errMsg || errKind ? `${errKindChip}${
|
|
380
|
+
const errKindChip = errKind ? color72.dim(` [${errKind}]`) : "";
|
|
381
|
+
const errSnip = errMsg || errKind ? `${errKindChip}${color72.dim(errTail)}` : "";
|
|
381
382
|
switch (r.status) {
|
|
382
383
|
case "success":
|
|
383
|
-
return { mark:
|
|
384
|
+
return { mark: color72.green("\u2713"), stats, tail: "" };
|
|
384
385
|
case "timeout":
|
|
385
386
|
return {
|
|
386
|
-
mark:
|
|
387
|
-
stats: `${
|
|
387
|
+
mark: color72.yellow("\u23F1"),
|
|
388
|
+
stats: `${color72.yellow("timeout")} ${stats}`,
|
|
388
389
|
tail: errSnip
|
|
389
390
|
};
|
|
390
391
|
case "stopped":
|
|
391
|
-
return { mark:
|
|
392
|
+
return { mark: color72.dim("\u2298"), stats: `${color72.dim("stopped")} ${stats}`, tail: errSnip };
|
|
392
393
|
case "failed":
|
|
393
|
-
return { mark:
|
|
394
|
+
return { mark: color72.red("\u2717"), stats: `${color72.red("failed")} ${stats}`, tail: errSnip };
|
|
394
395
|
}
|
|
395
396
|
}
|
|
396
397
|
var init_utils = __esm({
|
|
@@ -675,7 +676,7 @@ async function findSpec(store, idOrTitle) {
|
|
|
675
676
|
async function gatherProjectContext2(projectRoot) {
|
|
676
677
|
const parts = [];
|
|
677
678
|
try {
|
|
678
|
-
const pkgPath =
|
|
679
|
+
const pkgPath = path39.join(projectRoot, "package.json");
|
|
679
680
|
const pkgRaw = await fsp5.readFile(pkgPath, "utf8");
|
|
680
681
|
const pkg = JSON.parse(pkgRaw);
|
|
681
682
|
parts.push(`Project: ${String(pkg.name ?? "unknown")}`);
|
|
@@ -693,13 +694,13 @@ async function gatherProjectContext2(projectRoot) {
|
|
|
693
694
|
} catch {
|
|
694
695
|
}
|
|
695
696
|
try {
|
|
696
|
-
const tsconfigPath =
|
|
697
|
+
const tsconfigPath = path39.join(projectRoot, "tsconfig.json");
|
|
697
698
|
await fsp5.access(tsconfigPath);
|
|
698
699
|
parts.push("Language: TypeScript");
|
|
699
700
|
} catch {
|
|
700
701
|
}
|
|
701
702
|
try {
|
|
702
|
-
const srcDir =
|
|
703
|
+
const srcDir = path39.join(projectRoot, "src");
|
|
703
704
|
const entries = await fsp5.readdir(srcDir, { withFileTypes: true });
|
|
704
705
|
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
705
706
|
if (dirs.length > 0) parts.push(`Source structure: src/${dirs.join(", src/")}`);
|
|
@@ -2124,12 +2125,12 @@ __export(project_utils_exports, {
|
|
|
2124
2125
|
touchProjectInManifest: () => touchProjectInManifest
|
|
2125
2126
|
});
|
|
2126
2127
|
function projectsJsonPath(globalConfigPath) {
|
|
2127
|
-
const base = globalConfigPath ?
|
|
2128
|
-
return
|
|
2128
|
+
const base = globalConfigPath ? path39.dirname(globalConfigPath) : wstackGlobalRoot();
|
|
2129
|
+
return path39.join(base, "projects.json");
|
|
2129
2130
|
}
|
|
2130
2131
|
function projectsDataDir(globalConfigPath) {
|
|
2131
|
-
const base = globalConfigPath ?
|
|
2132
|
-
return
|
|
2132
|
+
const base = globalConfigPath ? path39.dirname(globalConfigPath) : wstackGlobalRoot();
|
|
2133
|
+
return path39.join(base, "projects");
|
|
2133
2134
|
}
|
|
2134
2135
|
async function loadManifest(globalConfigPath) {
|
|
2135
2136
|
const file = projectsJsonPath(globalConfigPath);
|
|
@@ -2143,12 +2144,12 @@ async function loadManifest(globalConfigPath) {
|
|
|
2143
2144
|
}
|
|
2144
2145
|
async function saveManifest(manifest, globalConfigPath) {
|
|
2145
2146
|
const file = projectsJsonPath(globalConfigPath);
|
|
2146
|
-
await fsp5.mkdir(
|
|
2147
|
+
await fsp5.mkdir(path39.dirname(file), { recursive: true });
|
|
2147
2148
|
await fsp5.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
|
|
2148
2149
|
}
|
|
2149
2150
|
function generateSlug(root) {
|
|
2150
|
-
const base =
|
|
2151
|
-
const hash = createHash("sha256").update(
|
|
2151
|
+
const base = path39.basename(root).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
2152
|
+
const hash = createHash("sha256").update(path39.resolve(root)).digest("hex").slice(0, 6);
|
|
2152
2153
|
return `${base}-${hash}`;
|
|
2153
2154
|
}
|
|
2154
2155
|
function findProject(manifest, query) {
|
|
@@ -2163,29 +2164,29 @@ function findProject(manifest, query) {
|
|
|
2163
2164
|
return found;
|
|
2164
2165
|
}
|
|
2165
2166
|
async function ensureProjectDataDir(slug, globalConfigPath) {
|
|
2166
|
-
const dir =
|
|
2167
|
+
const dir = path39.join(projectsDataDir(globalConfigPath), slug);
|
|
2167
2168
|
await fsp5.mkdir(dir, { recursive: true });
|
|
2168
2169
|
return dir;
|
|
2169
2170
|
}
|
|
2170
2171
|
async function touchProjectInManifest(opts) {
|
|
2171
|
-
const root =
|
|
2172
|
+
const root = path39.resolve(opts.projectRoot);
|
|
2172
2173
|
const file = projectsJsonPath(opts.globalConfigPath);
|
|
2173
2174
|
let entry;
|
|
2174
2175
|
await withFileLock(file, async () => {
|
|
2175
2176
|
const manifest = await loadManifest(opts.globalConfigPath);
|
|
2176
2177
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2177
|
-
entry = manifest.projects.find((p) =>
|
|
2178
|
+
entry = manifest.projects.find((p) => path39.resolve(p.root) === root);
|
|
2178
2179
|
if (entry) {
|
|
2179
2180
|
entry.lastSeen = now;
|
|
2180
|
-
if (opts.workingDir) entry.lastWorkingDir =
|
|
2181
|
+
if (opts.workingDir) entry.lastWorkingDir = path39.resolve(opts.workingDir);
|
|
2181
2182
|
} else {
|
|
2182
2183
|
entry = {
|
|
2183
|
-
name: opts.name ??
|
|
2184
|
+
name: opts.name ?? path39.basename(root),
|
|
2184
2185
|
root,
|
|
2185
2186
|
slug: generateSlug(root),
|
|
2186
2187
|
createdAt: now,
|
|
2187
2188
|
lastSeen: now,
|
|
2188
|
-
lastWorkingDir: opts.workingDir ?
|
|
2189
|
+
lastWorkingDir: opts.workingDir ? path39.resolve(opts.workingDir) : void 0
|
|
2189
2190
|
};
|
|
2190
2191
|
manifest.projects.push(entry);
|
|
2191
2192
|
}
|
|
@@ -2379,7 +2380,7 @@ async function runProjectPicker(opts) {
|
|
|
2379
2380
|
const reservedBottom = 3;
|
|
2380
2381
|
const headerHeight = reservedTop + reservedBottom;
|
|
2381
2382
|
const baseVisibleHeight = Math.max(5, terminalHeight() - headerHeight);
|
|
2382
|
-
return new Promise((
|
|
2383
|
+
return new Promise((resolve11) => {
|
|
2383
2384
|
const wasRaw = stdin.isRaw;
|
|
2384
2385
|
const wasPaused = stdin.isPaused();
|
|
2385
2386
|
let filter = "";
|
|
@@ -2503,7 +2504,7 @@ async function runProjectPicker(opts) {
|
|
|
2503
2504
|
cleanup();
|
|
2504
2505
|
out.write(CURSOR_SHOW);
|
|
2505
2506
|
out.write("\n");
|
|
2506
|
-
|
|
2507
|
+
resolve11(void 0);
|
|
2507
2508
|
return;
|
|
2508
2509
|
}
|
|
2509
2510
|
if (ch === ESC) {
|
|
@@ -2516,7 +2517,7 @@ async function runProjectPicker(opts) {
|
|
|
2516
2517
|
cleanup();
|
|
2517
2518
|
out.write(CURSOR_SHOW);
|
|
2518
2519
|
out.write("\n");
|
|
2519
|
-
|
|
2520
|
+
resolve11(void 0);
|
|
2520
2521
|
return;
|
|
2521
2522
|
}
|
|
2522
2523
|
if (ch === BS || ch === "\b") {
|
|
@@ -2533,22 +2534,22 @@ async function runProjectPicker(opts) {
|
|
|
2533
2534
|
out.write(CURSOR_SHOW);
|
|
2534
2535
|
out.write("\n");
|
|
2535
2536
|
if (!item || item.key === "__divider__") {
|
|
2536
|
-
|
|
2537
|
+
resolve11(void 0);
|
|
2537
2538
|
return;
|
|
2538
2539
|
}
|
|
2539
2540
|
if (item.key === "quit") {
|
|
2540
|
-
|
|
2541
|
+
resolve11(void 0);
|
|
2541
2542
|
return;
|
|
2542
2543
|
}
|
|
2543
2544
|
if (item.key === "new-session") {
|
|
2544
|
-
|
|
2545
|
+
resolve11({ kind: "action", key: "new-session", action: "new-session" });
|
|
2545
2546
|
return;
|
|
2546
2547
|
}
|
|
2547
2548
|
if (item.key === "prev-sessions") {
|
|
2548
|
-
|
|
2549
|
+
resolve11({ kind: "action", key: "prev-sessions", action: "prev-sessions" });
|
|
2549
2550
|
return;
|
|
2550
2551
|
}
|
|
2551
|
-
|
|
2552
|
+
resolve11({ kind: "project", key: item.key });
|
|
2552
2553
|
return;
|
|
2553
2554
|
}
|
|
2554
2555
|
if (filter.length === 0) {
|
|
@@ -2556,7 +2557,7 @@ async function runProjectPicker(opts) {
|
|
|
2556
2557
|
cleanup();
|
|
2557
2558
|
out.write(CURSOR_SHOW);
|
|
2558
2559
|
out.write("\n");
|
|
2559
|
-
|
|
2560
|
+
resolve11(void 0);
|
|
2560
2561
|
return;
|
|
2561
2562
|
}
|
|
2562
2563
|
if (ch === "j") {
|
|
@@ -2585,7 +2586,7 @@ async function runProjectPicker(opts) {
|
|
|
2585
2586
|
try {
|
|
2586
2587
|
stdin.setRawMode(true);
|
|
2587
2588
|
} catch {
|
|
2588
|
-
|
|
2589
|
+
resolve11(void 0);
|
|
2589
2590
|
return;
|
|
2590
2591
|
}
|
|
2591
2592
|
stdin.resume();
|
|
@@ -2596,7 +2597,7 @@ async function runProjectPicker(opts) {
|
|
|
2596
2597
|
stdin.once("close", () => {
|
|
2597
2598
|
cleanup();
|
|
2598
2599
|
out.write(CURSOR_SHOW);
|
|
2599
|
-
|
|
2600
|
+
resolve11(void 0);
|
|
2600
2601
|
});
|
|
2601
2602
|
});
|
|
2602
2603
|
}
|
|
@@ -2631,7 +2632,7 @@ __export(update_check_exports, {
|
|
|
2631
2632
|
getUpdateNotification: () => getUpdateNotification
|
|
2632
2633
|
});
|
|
2633
2634
|
function cachePath(homeFn = defaultHomeDir2) {
|
|
2634
|
-
return
|
|
2635
|
+
return path39.join(homeFn(), ".wrongstack", "update-cache.json");
|
|
2635
2636
|
}
|
|
2636
2637
|
function currentVersion() {
|
|
2637
2638
|
const req2 = createRequire(import.meta.url);
|
|
@@ -2668,7 +2669,7 @@ async function readCache(homeFn = defaultHomeDir2) {
|
|
|
2668
2669
|
}
|
|
2669
2670
|
async function writeCache(entry, homeFn = defaultHomeDir2) {
|
|
2670
2671
|
try {
|
|
2671
|
-
const dir =
|
|
2672
|
+
const dir = path39.dirname(cachePath(homeFn));
|
|
2672
2673
|
await fsp5.mkdir(dir, { recursive: true });
|
|
2673
2674
|
await fsp5.writeFile(cachePath(homeFn), JSON.stringify(entry, null, 2), "utf8");
|
|
2674
2675
|
} catch {
|
|
@@ -2754,7 +2755,7 @@ function registerWebuiInstance(p, deps = {}) {
|
|
|
2754
2755
|
wsPort: p.wsPort,
|
|
2755
2756
|
host: p.host,
|
|
2756
2757
|
projectRoot: p.projectRoot,
|
|
2757
|
-
projectName:
|
|
2758
|
+
projectName: path39.basename(p.projectRoot) || p.projectRoot,
|
|
2758
2759
|
startedAt: p.startedAt,
|
|
2759
2760
|
url: `http://${p.host}:${p.httpPort}`
|
|
2760
2761
|
},
|
|
@@ -2818,7 +2819,7 @@ var init_lifecycle = __esm({
|
|
|
2818
2819
|
}
|
|
2819
2820
|
});
|
|
2820
2821
|
function getVault(globalConfigPath) {
|
|
2821
|
-
const keyFile =
|
|
2822
|
+
const keyFile = path39.join(path39.dirname(globalConfigPath ?? ""), ".key");
|
|
2822
2823
|
return new DefaultSecretVault({ keyFile });
|
|
2823
2824
|
}
|
|
2824
2825
|
async function loadSavedProviders(globalConfigPath) {
|
|
@@ -2852,7 +2853,7 @@ function resolveDistDir() {
|
|
|
2852
2853
|
try {
|
|
2853
2854
|
const requireFromHere = createRequire(import.meta.url);
|
|
2854
2855
|
const serverEntry = requireFromHere.resolve("@wrongstack/webui/server");
|
|
2855
|
-
return
|
|
2856
|
+
return path39.resolve(path39.dirname(serverEntry), "..");
|
|
2856
2857
|
} catch {
|
|
2857
2858
|
return null;
|
|
2858
2859
|
}
|
|
@@ -3124,10 +3125,10 @@ function handlePing(ctx, ws) {
|
|
|
3124
3125
|
ctx.send(ws, { type: "pong", payload: {} });
|
|
3125
3126
|
}
|
|
3126
3127
|
function handleToolConfirmResult(ctx, id, decision) {
|
|
3127
|
-
const
|
|
3128
|
-
if (
|
|
3128
|
+
const resolve11 = ctx.pendingConfirms.get(id);
|
|
3129
|
+
if (resolve11) {
|
|
3129
3130
|
ctx.pendingConfirms.delete(id);
|
|
3130
|
-
|
|
3131
|
+
resolve11(decision);
|
|
3131
3132
|
}
|
|
3132
3133
|
}
|
|
3133
3134
|
var init_connection = __esm({
|
|
@@ -3543,8 +3544,8 @@ function sendResult6(ctx, ws, success, message) {
|
|
|
3543
3544
|
ctx.send(ws, { type: "key.operation_result", payload: { success, message } });
|
|
3544
3545
|
}
|
|
3545
3546
|
async function handleProjectsList(ctx, ws) {
|
|
3546
|
-
const projectsBase = ctx.opts.globalConfigPath ?
|
|
3547
|
-
const manifestPath =
|
|
3547
|
+
const projectsBase = ctx.opts.globalConfigPath ? path39.resolve(path39.dirname(ctx.opts.globalConfigPath)) : wstackGlobalRoot();
|
|
3548
|
+
const manifestPath = path39.join(projectsBase, "projects.json");
|
|
3548
3549
|
try {
|
|
3549
3550
|
const raw = await fsp5.readFile(manifestPath, "utf8");
|
|
3550
3551
|
const manifest = JSON.parse(raw);
|
|
@@ -3557,22 +3558,22 @@ async function handleProjectsSelect(ctx, ws, payload) {
|
|
|
3557
3558
|
const { opts } = ctx;
|
|
3558
3559
|
const { root, name: projectName } = payload;
|
|
3559
3560
|
try {
|
|
3560
|
-
const resolved =
|
|
3561
|
+
const resolved = path39.resolve(root);
|
|
3561
3562
|
const stat7 = await fsp5.stat(resolved).catch(() => null);
|
|
3562
3563
|
if (!stat7?.isDirectory()) {
|
|
3563
3564
|
ctx.send(ws, {
|
|
3564
3565
|
type: "projects.selected",
|
|
3565
3566
|
payload: {
|
|
3566
3567
|
root,
|
|
3567
|
-
name: projectName ??
|
|
3568
|
+
name: projectName ?? path39.basename(root),
|
|
3568
3569
|
message: `Cannot switch: not a directory: ${resolved}`
|
|
3569
3570
|
}
|
|
3570
3571
|
});
|
|
3571
3572
|
return;
|
|
3572
3573
|
}
|
|
3573
3574
|
const manifest = await loadManifest(opts.globalConfigPath);
|
|
3574
|
-
const entry = manifest.projects.find((p) =>
|
|
3575
|
-
const displayName = projectName?.trim() || entry?.name ||
|
|
3575
|
+
const entry = manifest.projects.find((p) => path39.resolve(p.root) === resolved);
|
|
3576
|
+
const displayName = projectName?.trim() || entry?.name || path39.basename(resolved);
|
|
3576
3577
|
if (entry) {
|
|
3577
3578
|
entry.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
3578
3579
|
} else {
|
|
@@ -3618,8 +3619,8 @@ async function handleProjectsSelect(ctx, ws, payload) {
|
|
|
3618
3619
|
});
|
|
3619
3620
|
} catch {
|
|
3620
3621
|
}
|
|
3621
|
-
const globalRoot = opts.globalConfigPath ?
|
|
3622
|
-
const newSessionsDir =
|
|
3622
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : wstackGlobalRoot();
|
|
3623
|
+
const newSessionsDir = path39.join(resolveProjectDir(resolved, globalRoot), "sessions");
|
|
3623
3624
|
await fsp5.mkdir(newSessionsDir, { recursive: true });
|
|
3624
3625
|
const newStore = new DefaultSessionStore({ dir: newSessionsDir });
|
|
3625
3626
|
opts.sessionStore = newStore;
|
|
@@ -3649,11 +3650,11 @@ async function handleProjectsSelect(ctx, ws, payload) {
|
|
|
3649
3650
|
async function handleProjectsAdd(ctx, ws, payload) {
|
|
3650
3651
|
const { root: addRoot, name: addName } = payload;
|
|
3651
3652
|
try {
|
|
3652
|
-
const resolved =
|
|
3653
|
+
const resolved = path39.resolve(addRoot);
|
|
3653
3654
|
const stat7 = await fsp5.stat(resolved).catch(() => null);
|
|
3654
3655
|
if (!stat7?.isDirectory()) throw new Error(`Not a directory: ${resolved}`);
|
|
3655
3656
|
const manifest = await loadManifest(ctx.opts.globalConfigPath);
|
|
3656
|
-
const existing = manifest.projects.find((p) =>
|
|
3657
|
+
const existing = manifest.projects.find((p) => path39.resolve(p.root) === resolved);
|
|
3657
3658
|
if (existing) {
|
|
3658
3659
|
ctx.send(ws, {
|
|
3659
3660
|
type: "projects.added",
|
|
@@ -3666,7 +3667,7 @@ async function handleProjectsAdd(ctx, ws, payload) {
|
|
|
3666
3667
|
});
|
|
3667
3668
|
return;
|
|
3668
3669
|
}
|
|
3669
|
-
const name = addName?.trim() ||
|
|
3670
|
+
const name = addName?.trim() || path39.basename(resolved);
|
|
3670
3671
|
const slug = projectSlug(resolved);
|
|
3671
3672
|
await ensureProjectDataDir(slug, ctx.opts.globalConfigPath);
|
|
3672
3673
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3680,7 +3681,7 @@ async function handleProjectsAdd(ctx, ws, payload) {
|
|
|
3680
3681
|
ctx.send(ws, {
|
|
3681
3682
|
type: "projects.added",
|
|
3682
3683
|
payload: {
|
|
3683
|
-
name:
|
|
3684
|
+
name: path39.basename(addRoot),
|
|
3684
3685
|
root: addRoot,
|
|
3685
3686
|
slug: "",
|
|
3686
3687
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -3691,8 +3692,8 @@ async function handleProjectsAdd(ctx, ws, payload) {
|
|
|
3691
3692
|
async function handleWorkingDirSet(ctx, ws, newPath) {
|
|
3692
3693
|
try {
|
|
3693
3694
|
const wdRoot = ctx.opts.projectRoot ?? ctx.opts.agent.ctx.projectRoot;
|
|
3694
|
-
const resolved =
|
|
3695
|
-
if (!resolved.startsWith(wdRoot +
|
|
3695
|
+
const resolved = path39.resolve(wdRoot, newPath);
|
|
3696
|
+
if (!resolved.startsWith(wdRoot + path39.sep) && resolved !== wdRoot) {
|
|
3696
3697
|
sendResult6(ctx, ws, false, `Path must stay inside the project root: ${wdRoot}`);
|
|
3697
3698
|
return;
|
|
3698
3699
|
}
|
|
@@ -3933,13 +3934,13 @@ function sendResult8(ctx, ws, success, message) {
|
|
|
3933
3934
|
}
|
|
3934
3935
|
function storeFor(opts) {
|
|
3935
3936
|
return opts.sessionStore ?? new DefaultSessionStore({
|
|
3936
|
-
dir:
|
|
3937
|
+
dir: path39.join(opts.projectRoot ?? opts.agent.ctx.projectRoot, ".wrongstack", "sessions")
|
|
3937
3938
|
});
|
|
3938
3939
|
}
|
|
3939
3940
|
async function handleGoalGet(ctx, _ws) {
|
|
3940
3941
|
const projectRoot = ctx.opts.projectRoot ?? ctx.opts.agent.ctx.projectRoot;
|
|
3941
3942
|
try {
|
|
3942
|
-
const goalPath =
|
|
3943
|
+
const goalPath = path39.join(projectRoot, ".wrongstack", "goal.json");
|
|
3943
3944
|
const raw = await fsp5.readFile(goalPath, "utf8");
|
|
3944
3945
|
ctx.broadcast({ type: "goal.updated", payload: JSON.parse(raw) });
|
|
3945
3946
|
} catch {
|
|
@@ -4015,7 +4016,7 @@ async function handleSessionNew(ctx, _ws) {
|
|
|
4015
4016
|
function rewinderFor(opts) {
|
|
4016
4017
|
const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot;
|
|
4017
4018
|
return new DefaultSessionRewinder(
|
|
4018
|
-
opts.sessionsDir ??
|
|
4019
|
+
opts.sessionsDir ?? path39.join(projectRoot, ".wrongstack", "sessions"),
|
|
4019
4020
|
projectRoot
|
|
4020
4021
|
);
|
|
4021
4022
|
}
|
|
@@ -4349,14 +4350,14 @@ async function runWebUI(opts) {
|
|
|
4349
4350
|
let customModeStoreP = null;
|
|
4350
4351
|
const getCustomModeStore = () => {
|
|
4351
4352
|
customModeStoreP ??= (async () => {
|
|
4352
|
-
const dir = opts.globalConfigPath ?
|
|
4353
|
+
const dir = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : wstackGlobalRoot();
|
|
4353
4354
|
const store = createCustomModeStore(dir);
|
|
4354
4355
|
await store.load();
|
|
4355
4356
|
return store;
|
|
4356
4357
|
})();
|
|
4357
4358
|
return customModeStoreP;
|
|
4358
4359
|
};
|
|
4359
|
-
const autoPhaseStoreDir = opts.projectRoot ?
|
|
4360
|
+
const autoPhaseStoreDir = opts.projectRoot ? path39.join(opts.projectRoot, ".wrongstack", "autophase") : path39.join(os.tmpdir(), ".wrongstack", "autophase");
|
|
4360
4361
|
const autoPhaseHandler = new AutoPhaseWebSocketHandler(
|
|
4361
4362
|
opts.agent,
|
|
4362
4363
|
opts.agent.ctx,
|
|
@@ -4447,7 +4448,7 @@ async function runWebUI(opts) {
|
|
|
4447
4448
|
return;
|
|
4448
4449
|
}
|
|
4449
4450
|
const vault = new DefaultSecretVault({
|
|
4450
|
-
keyFile:
|
|
4451
|
+
keyFile: path39.join(path39.dirname(configPath2), ".key")
|
|
4451
4452
|
});
|
|
4452
4453
|
const decrypted = decryptConfigSecrets$1(parsed, vault);
|
|
4453
4454
|
const autonomyCfg = decrypted.autonomy ?? {};
|
|
@@ -4570,7 +4571,7 @@ async function runWebUI(opts) {
|
|
|
4570
4571
|
model: opts.agent.ctx.model,
|
|
4571
4572
|
provider: opts.agent.ctx.provider.id,
|
|
4572
4573
|
mode: opts.modeId ?? "default",
|
|
4573
|
-
projectName: opts.projectRoot ?
|
|
4574
|
+
projectName: opts.projectRoot ? path39.basename(opts.projectRoot) : void 0,
|
|
4574
4575
|
// Frontend reads `projectRoot` from session.start (ws-handlers setEnv) —
|
|
4575
4576
|
// omitting it left the store's projectRoot empty after a project switch.
|
|
4576
4577
|
projectRoot: opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
|
|
@@ -4596,7 +4597,7 @@ async function runWebUI(opts) {
|
|
|
4596
4597
|
const projectDir = resolveProjectDir(opts.projectRoot, wstackGlobalRoot());
|
|
4597
4598
|
const mailbox = new GlobalMailbox(projectDir, opts.events);
|
|
4598
4599
|
webuiClientId = `webui@${crypto3.randomUUID().slice(0, 8)}`;
|
|
4599
|
-
const projectName = opts.projectRoot ?
|
|
4600
|
+
const projectName = opts.projectRoot ? path39.basename(opts.projectRoot) : "unknown";
|
|
4600
4601
|
await mailbox.registerClient({
|
|
4601
4602
|
clientId: webuiClientId,
|
|
4602
4603
|
sessionId: opts.projectRoot,
|
|
@@ -4627,7 +4628,7 @@ async function runWebUI(opts) {
|
|
|
4627
4628
|
host,
|
|
4628
4629
|
httpPort,
|
|
4629
4630
|
wsPort,
|
|
4630
|
-
globalRoot:
|
|
4631
|
+
globalRoot: path39.dirname(opts.globalConfigPath ?? "")
|
|
4631
4632
|
});
|
|
4632
4633
|
if (httpServer) {
|
|
4633
4634
|
announceWebuiReady({
|
|
@@ -4642,7 +4643,7 @@ async function runWebUI(opts) {
|
|
|
4642
4643
|
`[WebUI] Frontend not served (run \`pnpm --filter @wrongstack/webui build\`). WS bridge still active on ws://${host}:${wsPort}.`
|
|
4643
4644
|
);
|
|
4644
4645
|
}
|
|
4645
|
-
const registryBaseDir = opts.globalConfigPath ?
|
|
4646
|
+
const registryBaseDir = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : void 0;
|
|
4646
4647
|
if (opts.projectRoot) {
|
|
4647
4648
|
registerWebuiInstance({
|
|
4648
4649
|
pid: process.pid,
|
|
@@ -4999,12 +5000,12 @@ async function runWebUI(opts) {
|
|
|
4999
5000
|
broadcast,
|
|
5000
5001
|
log: (m) => console.log(m)
|
|
5001
5002
|
};
|
|
5002
|
-
return new Promise((
|
|
5003
|
+
return new Promise((resolve11) => {
|
|
5003
5004
|
wss.on("listening", () => {
|
|
5004
5005
|
console.log(`[WebUI] WebSocket server running on ws://${host}:${port}`);
|
|
5005
5006
|
setupEvents();
|
|
5006
5007
|
opts.onListening?.({ httpPort, wsPort, host });
|
|
5007
|
-
const globalRoot = opts.globalConfigPath ?
|
|
5008
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : void 0;
|
|
5008
5009
|
if (globalRoot) {
|
|
5009
5010
|
const statusInterval = setInterval(async () => {
|
|
5010
5011
|
try {
|
|
@@ -5123,8 +5124,8 @@ async function runWebUI(opts) {
|
|
|
5123
5124
|
clients.delete(ws);
|
|
5124
5125
|
abortControllers.delete(ws);
|
|
5125
5126
|
if (clients.size === 0 && pendingConfirms.size > 0) {
|
|
5126
|
-
for (const [id,
|
|
5127
|
-
|
|
5127
|
+
for (const [id, resolve12] of pendingConfirms) {
|
|
5128
|
+
resolve12("no");
|
|
5128
5129
|
pendingConfirms.delete(id);
|
|
5129
5130
|
}
|
|
5130
5131
|
}
|
|
@@ -5163,7 +5164,7 @@ async function runWebUI(opts) {
|
|
|
5163
5164
|
wss,
|
|
5164
5165
|
pid: process.pid,
|
|
5165
5166
|
registryBaseDir,
|
|
5166
|
-
onStopped:
|
|
5167
|
+
onStopped: resolve11
|
|
5167
5168
|
});
|
|
5168
5169
|
registerWebuiSignalHandlers(signalShutdown);
|
|
5169
5170
|
});
|
|
@@ -5553,7 +5554,7 @@ async function runWebUI(opts) {
|
|
|
5553
5554
|
// ── Mailbox operations — project-level inter-agent messaging ────
|
|
5554
5555
|
case "mailbox.messages": {
|
|
5555
5556
|
const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
|
|
5556
|
-
const globalRoot = opts.globalConfigPath ?
|
|
5557
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
|
|
5557
5558
|
if (!projectRoot || !globalRoot) {
|
|
5558
5559
|
send(ws, {
|
|
5559
5560
|
type: "mailbox.messages",
|
|
@@ -5602,7 +5603,7 @@ async function runWebUI(opts) {
|
|
|
5602
5603
|
}
|
|
5603
5604
|
case "mailbox.agents": {
|
|
5604
5605
|
const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
|
|
5605
|
-
const globalRoot = opts.globalConfigPath ?
|
|
5606
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
|
|
5606
5607
|
if (!projectRoot || !globalRoot) {
|
|
5607
5608
|
send(ws, {
|
|
5608
5609
|
type: "mailbox.agents",
|
|
@@ -5645,7 +5646,7 @@ async function runWebUI(opts) {
|
|
|
5645
5646
|
}
|
|
5646
5647
|
case "mailbox.clear": {
|
|
5647
5648
|
const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
|
|
5648
|
-
const globalRoot = opts.globalConfigPath ?
|
|
5649
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
|
|
5649
5650
|
if (!projectRoot || !globalRoot) {
|
|
5650
5651
|
send(ws, { type: "mailbox.cleared", payload: { error: "No project root available" } });
|
|
5651
5652
|
break;
|
|
@@ -5715,7 +5716,7 @@ var init_webui_server = __esm({
|
|
|
5715
5716
|
var WORKTREE_PHASE_CONCURRENCY = 4;
|
|
5716
5717
|
var MAX_CMD_OUTPUT = 2e5;
|
|
5717
5718
|
function gitText(args, cwd) {
|
|
5718
|
-
return new Promise((
|
|
5719
|
+
return new Promise((resolve11, reject) => {
|
|
5719
5720
|
let child;
|
|
5720
5721
|
try {
|
|
5721
5722
|
child = spawn("git", args, {
|
|
@@ -5729,15 +5730,14 @@ function gitText(args, cwd) {
|
|
|
5729
5730
|
reject(err);
|
|
5730
5731
|
return;
|
|
5731
5732
|
}
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
if (
|
|
5735
|
-
}
|
|
5736
|
-
child.
|
|
5737
|
-
|
|
5738
|
-
});
|
|
5739
|
-
child.on("
|
|
5740
|
-
child.on("close", (code) => resolve10({ code: code ?? 1, out: out.trim() }));
|
|
5733
|
+
const chunks = [];
|
|
5734
|
+
const emit = (c) => {
|
|
5735
|
+
if (chunks.join("").length < MAX_CMD_OUTPUT) chunks.push(c.toString());
|
|
5736
|
+
};
|
|
5737
|
+
child.stdout?.on("data", emit);
|
|
5738
|
+
child.stderr?.on("data", emit);
|
|
5739
|
+
child.on("error", () => resolve11({ code: 1, out: chunks.join("") }));
|
|
5740
|
+
child.on("close", (code) => resolve11({ code: code ?? 1, out: chunks.join("").trim() }));
|
|
5741
5741
|
});
|
|
5742
5742
|
}
|
|
5743
5743
|
async function isGitRepo(cwd) {
|
|
@@ -5773,8 +5773,8 @@ function runCmd(cmd, args, cwd, shell = false) {
|
|
|
5773
5773
|
});
|
|
5774
5774
|
}
|
|
5775
5775
|
}
|
|
5776
|
-
return new Promise((
|
|
5777
|
-
|
|
5776
|
+
return new Promise((resolve11, reject) => {
|
|
5777
|
+
const chunks = [];
|
|
5778
5778
|
let child;
|
|
5779
5779
|
try {
|
|
5780
5780
|
child = spawn(cmd, args, {
|
|
@@ -5792,13 +5792,16 @@ function runCmd(cmd, args, cwd, shell = false) {
|
|
|
5792
5792
|
return;
|
|
5793
5793
|
}
|
|
5794
5794
|
const append = (c) => {
|
|
5795
|
-
|
|
5796
|
-
if (out.length > MAX_CMD_OUTPUT) out = out.slice(-MAX_CMD_OUTPUT);
|
|
5795
|
+
chunks.push(c.toString());
|
|
5797
5796
|
};
|
|
5798
5797
|
child.stdout?.on("data", append);
|
|
5799
5798
|
child.stderr?.on("data", append);
|
|
5800
|
-
child.on("error", (e) =>
|
|
5801
|
-
child.on("close", (code) =>
|
|
5799
|
+
child.on("error", (e) => resolve11({ code: 1, out: `${chunks.join("")}${String(e)}` }));
|
|
5800
|
+
child.on("close", (code) => {
|
|
5801
|
+
let out = chunks.join("");
|
|
5802
|
+
if (out.length > MAX_CMD_OUTPUT) out = out.slice(-MAX_CMD_OUTPUT);
|
|
5803
|
+
resolve11({ code: code ?? 1, out: out.trim() });
|
|
5804
|
+
});
|
|
5802
5805
|
});
|
|
5803
5806
|
}
|
|
5804
5807
|
function detectPackageManager(root) {
|
|
@@ -6273,7 +6276,7 @@ var ReadlineInputReader = class {
|
|
|
6273
6276
|
history = [];
|
|
6274
6277
|
pending = false;
|
|
6275
6278
|
constructor(opts = {}) {
|
|
6276
|
-
this.historyFile = opts.historyFile ??
|
|
6279
|
+
this.historyFile = opts.historyFile ?? path39.join(wstackGlobalRoot(), "history");
|
|
6277
6280
|
}
|
|
6278
6281
|
async loadHistory() {
|
|
6279
6282
|
try {
|
|
@@ -6285,7 +6288,7 @@ var ReadlineInputReader = class {
|
|
|
6285
6288
|
}
|
|
6286
6289
|
async saveHistory() {
|
|
6287
6290
|
try {
|
|
6288
|
-
await fsp5.mkdir(
|
|
6291
|
+
await fsp5.mkdir(path39.dirname(this.historyFile), { recursive: true });
|
|
6289
6292
|
await fsp5.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
|
|
6290
6293
|
} catch {
|
|
6291
6294
|
}
|
|
@@ -6304,31 +6307,31 @@ var ReadlineInputReader = class {
|
|
|
6304
6307
|
async readLine(prompt) {
|
|
6305
6308
|
if (this.history.length === 0) await this.loadHistory();
|
|
6306
6309
|
while (this.pending) {
|
|
6307
|
-
await new Promise((
|
|
6310
|
+
await new Promise((resolve11) => setTimeout(resolve11, 50));
|
|
6308
6311
|
}
|
|
6309
6312
|
this.pending = true;
|
|
6310
6313
|
try {
|
|
6311
6314
|
if (this.rl) {
|
|
6312
6315
|
const old = this.rl;
|
|
6313
6316
|
this.rl = void 0;
|
|
6314
|
-
await new Promise((
|
|
6317
|
+
await new Promise((resolve11) => {
|
|
6315
6318
|
if (old.closed) {
|
|
6316
|
-
|
|
6319
|
+
resolve11();
|
|
6317
6320
|
} else {
|
|
6318
|
-
old.once("close",
|
|
6321
|
+
old.once("close", resolve11);
|
|
6319
6322
|
old.close();
|
|
6320
6323
|
}
|
|
6321
6324
|
});
|
|
6322
6325
|
}
|
|
6323
6326
|
const fresh = this.ensure();
|
|
6324
6327
|
this.installPromptGuard(fresh);
|
|
6325
|
-
return new Promise((
|
|
6328
|
+
return new Promise((resolve11) => {
|
|
6326
6329
|
let settled = false;
|
|
6327
6330
|
const settle = (line) => {
|
|
6328
6331
|
if (settled) return;
|
|
6329
6332
|
settled = true;
|
|
6330
6333
|
setOutputLineGuard(null);
|
|
6331
|
-
|
|
6334
|
+
resolve11(line);
|
|
6332
6335
|
};
|
|
6333
6336
|
fresh.question(prompt ?? "> ", (line) => {
|
|
6334
6337
|
if (line.trim()) {
|
|
@@ -6381,7 +6384,7 @@ var ReadlineInputReader = class {
|
|
|
6381
6384
|
async readKey(prompt, options) {
|
|
6382
6385
|
setOutputLineGuard(null);
|
|
6383
6386
|
writeOut(prompt);
|
|
6384
|
-
return new Promise((
|
|
6387
|
+
return new Promise((resolve11) => {
|
|
6385
6388
|
const stdin = process.stdin;
|
|
6386
6389
|
const wasRaw = stdin.isRaw;
|
|
6387
6390
|
const wasPaused = stdin.isPaused();
|
|
@@ -6392,7 +6395,7 @@ var ReadlineInputReader = class {
|
|
|
6392
6395
|
if (key === "") {
|
|
6393
6396
|
cleanup();
|
|
6394
6397
|
writeOut("\n");
|
|
6395
|
-
|
|
6398
|
+
resolve11("");
|
|
6396
6399
|
return;
|
|
6397
6400
|
}
|
|
6398
6401
|
const opt = options.find(
|
|
@@ -6402,12 +6405,12 @@ var ReadlineInputReader = class {
|
|
|
6402
6405
|
cleanup();
|
|
6403
6406
|
writeOut(`${opt.key}
|
|
6404
6407
|
`);
|
|
6405
|
-
|
|
6408
|
+
resolve11(opt.value);
|
|
6406
6409
|
}
|
|
6407
6410
|
};
|
|
6408
6411
|
const onClose = () => {
|
|
6409
6412
|
cleanup();
|
|
6410
|
-
|
|
6413
|
+
resolve11("");
|
|
6411
6414
|
};
|
|
6412
6415
|
const cleanup = () => {
|
|
6413
6416
|
stdin.off("data", onData);
|
|
@@ -6436,7 +6439,7 @@ var ReadlineInputReader = class {
|
|
|
6436
6439
|
this.rl?.close();
|
|
6437
6440
|
this.rl = void 0;
|
|
6438
6441
|
writeOut(prompt);
|
|
6439
|
-
return new Promise((
|
|
6442
|
+
return new Promise((resolve11) => {
|
|
6440
6443
|
let buf = "";
|
|
6441
6444
|
const wasRaw = stdin.isRaw;
|
|
6442
6445
|
setRawMode(stdin, true);
|
|
@@ -6454,7 +6457,7 @@ var ReadlineInputReader = class {
|
|
|
6454
6457
|
cleanup();
|
|
6455
6458
|
writeOut(` ${dim(`[${buf.length} chars]`)}
|
|
6456
6459
|
`);
|
|
6457
|
-
|
|
6460
|
+
resolve11(buf);
|
|
6458
6461
|
return;
|
|
6459
6462
|
}
|
|
6460
6463
|
if (ch === "") {
|
|
@@ -6617,7 +6620,7 @@ function pickGroupIndex(opts) {
|
|
|
6617
6620
|
if (Number.isFinite(parsed)) current = wrap(parsed);
|
|
6618
6621
|
} catch {
|
|
6619
6622
|
}
|
|
6620
|
-
fs2.mkdirSync(
|
|
6623
|
+
fs2.mkdirSync(path39.dirname(opts.cursorFile), { recursive: true });
|
|
6621
6624
|
fs2.writeFileSync(opts.cursorFile, String(wrap(current + 1)));
|
|
6622
6625
|
return current;
|
|
6623
6626
|
} catch {
|
|
@@ -6670,11 +6673,11 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
6670
6673
|
throw new FsError({
|
|
6671
6674
|
message: `Refusing to delete protected file: ${filename}`,
|
|
6672
6675
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
6673
|
-
path:
|
|
6676
|
+
path: path39.join(parentDir, filename),
|
|
6674
6677
|
context: { reason: "protected_basename" }
|
|
6675
6678
|
});
|
|
6676
6679
|
}
|
|
6677
|
-
if (filename !==
|
|
6680
|
+
if (filename !== path39.basename(filename)) {
|
|
6678
6681
|
throw new FsError({
|
|
6679
6682
|
message: `Refusing to delete path with traversal: ${filename}`,
|
|
6680
6683
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
@@ -6686,11 +6689,11 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
6686
6689
|
throw new FsError({
|
|
6687
6690
|
message: `Refusing to delete unknown file: ${filename}`,
|
|
6688
6691
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
6689
|
-
path:
|
|
6692
|
+
path: path39.join(parentDir, filename),
|
|
6690
6693
|
context: { reason: "unknown_file_pattern" }
|
|
6691
6694
|
});
|
|
6692
6695
|
}
|
|
6693
|
-
const resolvedParent =
|
|
6696
|
+
const resolvedParent = path39.resolve(parentDir);
|
|
6694
6697
|
if (!resolvedParent.endsWith(".wrongstack")) {
|
|
6695
6698
|
throw new FsError({
|
|
6696
6699
|
message: `Unexpected parent directory for bak prune: ${resolvedParent}`,
|
|
@@ -6701,8 +6704,8 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
6701
6704
|
}
|
|
6702
6705
|
}
|
|
6703
6706
|
async function safeDelete(filePath) {
|
|
6704
|
-
const dir =
|
|
6705
|
-
const filename =
|
|
6707
|
+
const dir = path39.dirname(filePath);
|
|
6708
|
+
const filename = path39.basename(filePath);
|
|
6706
6709
|
try {
|
|
6707
6710
|
assertSafeToDelete(filename, dir);
|
|
6708
6711
|
await fsp5.unlink(filePath);
|
|
@@ -6747,16 +6750,16 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
6747
6750
|
}
|
|
6748
6751
|
var defaultHomeDir = () => os__default.homedir();
|
|
6749
6752
|
function historyDir(homeFn = defaultHomeDir) {
|
|
6750
|
-
return
|
|
6753
|
+
return path39.join(homeFn(), ".wrongstack", "config.history", "entries");
|
|
6751
6754
|
}
|
|
6752
6755
|
function historyIndexPath(homeFn = defaultHomeDir) {
|
|
6753
|
-
return
|
|
6756
|
+
return path39.join(homeFn(), ".wrongstack", "config.history", "index.json");
|
|
6754
6757
|
}
|
|
6755
6758
|
function configPath(homeFn = defaultHomeDir) {
|
|
6756
|
-
return
|
|
6759
|
+
return path39.join(homeFn(), ".wrongstack", "config.json");
|
|
6757
6760
|
}
|
|
6758
6761
|
function backupLastPath(homeFn = defaultHomeDir) {
|
|
6759
|
-
return
|
|
6762
|
+
return path39.join(homeFn(), ".wrongstack", "config.json.last");
|
|
6760
6763
|
}
|
|
6761
6764
|
function entryId(ts) {
|
|
6762
6765
|
return ts.replace(/[:.]/g, "-").slice(0, 19);
|
|
@@ -6814,7 +6817,7 @@ async function backupCurrent(homeFn = defaultHomeDir) {
|
|
|
6814
6817
|
}
|
|
6815
6818
|
if (content !== void 0) {
|
|
6816
6819
|
try {
|
|
6817
|
-
const bakPath =
|
|
6820
|
+
const bakPath = path39.join(homeFn(), ".wrongstack", `config.json.${ts}.bak`);
|
|
6818
6821
|
await atomicWrite(bakPath, content);
|
|
6819
6822
|
} catch (err) {
|
|
6820
6823
|
writeErr(
|
|
@@ -6823,11 +6826,11 @@ async function backupCurrent(homeFn = defaultHomeDir) {
|
|
|
6823
6826
|
}
|
|
6824
6827
|
}
|
|
6825
6828
|
try {
|
|
6826
|
-
const dir =
|
|
6829
|
+
const dir = path39.join(homeFn(), ".wrongstack");
|
|
6827
6830
|
const files = await fsp5.readdir(dir);
|
|
6828
6831
|
const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
|
|
6829
6832
|
for (const f of baks.slice(10)) {
|
|
6830
|
-
await safeDelete(
|
|
6833
|
+
await safeDelete(path39.join(dir, f));
|
|
6831
6834
|
}
|
|
6832
6835
|
} catch (err) {
|
|
6833
6836
|
writeErr(
|
|
@@ -6848,7 +6851,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
6848
6851
|
};
|
|
6849
6852
|
try {
|
|
6850
6853
|
await fsp5.writeFile(
|
|
6851
|
-
|
|
6854
|
+
path39.join(historyDir(homeFn), `${id}.json`),
|
|
6852
6855
|
JSON.stringify(entry, null, 2),
|
|
6853
6856
|
"utf8"
|
|
6854
6857
|
);
|
|
@@ -6856,7 +6859,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
6856
6859
|
throw new FsError({
|
|
6857
6860
|
message: err instanceof Error ? err.message : String(err),
|
|
6858
6861
|
code: ERROR_CODES.FS_WRITE_FAILED,
|
|
6859
|
-
path:
|
|
6862
|
+
path: path39.join(historyDir(homeFn), `${id}.json`),
|
|
6860
6863
|
cause: err
|
|
6861
6864
|
});
|
|
6862
6865
|
}
|
|
@@ -6871,7 +6874,7 @@ async function listHistory(homeFn = defaultHomeDir) {
|
|
|
6871
6874
|
}
|
|
6872
6875
|
async function getHistoryEntry(id, homeFn = defaultHomeDir) {
|
|
6873
6876
|
try {
|
|
6874
|
-
const raw = await fsp5.readFile(
|
|
6877
|
+
const raw = await fsp5.readFile(path39.join(historyDir(homeFn), `${id}.json`), "utf8");
|
|
6875
6878
|
return JSON.parse(raw);
|
|
6876
6879
|
} catch {
|
|
6877
6880
|
return null;
|
|
@@ -6988,10 +6991,10 @@ var theme = { primary: color.amber };
|
|
|
6988
6991
|
async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os__default.homedir()) {
|
|
6989
6992
|
try {
|
|
6990
6993
|
const { atomicWrite: atomicWrite17 } = await import('@wrongstack/core');
|
|
6991
|
-
const
|
|
6994
|
+
const fs39 = await import('fs/promises');
|
|
6992
6995
|
let existing = {};
|
|
6993
6996
|
try {
|
|
6994
|
-
const raw = await
|
|
6997
|
+
const raw = await fs39.readFile(configPath2, "utf8");
|
|
6995
6998
|
existing = JSON.parse(raw);
|
|
6996
6999
|
} catch {
|
|
6997
7000
|
}
|
|
@@ -7484,7 +7487,7 @@ function buildAutonomyCommand(opts) {
|
|
|
7484
7487
|
const current = opts.onAutonomy();
|
|
7485
7488
|
const lines = [`Autonomy mode: ${MODE_LABELS[current] ?? current}`];
|
|
7486
7489
|
try {
|
|
7487
|
-
const goal = await loadGoal(goalFilePath(opts.projectRoot));
|
|
7490
|
+
const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
|
|
7488
7491
|
if (goal) {
|
|
7489
7492
|
const u = summarizeUsage(goal);
|
|
7490
7493
|
lines.push(
|
|
@@ -7530,7 +7533,7 @@ function buildAutonomyCommand(opts) {
|
|
|
7530
7533
|
opts.onAutonomy("off");
|
|
7531
7534
|
let summaryLine = "";
|
|
7532
7535
|
try {
|
|
7533
|
-
const goal = await loadGoal(goalFilePath(opts.projectRoot));
|
|
7536
|
+
const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
|
|
7534
7537
|
if (goal) {
|
|
7535
7538
|
const u = summarizeUsage(goal);
|
|
7536
7539
|
if (u.iterationsWithUsage > 0) {
|
|
@@ -7570,7 +7573,7 @@ function buildAutonomyCommand(opts) {
|
|
|
7570
7573
|
if (newMode === "eternal" || newMode === "eternal-parallel") {
|
|
7571
7574
|
const wantKeep = modifiers.includes("--keep") || modifiers.includes("keep");
|
|
7572
7575
|
const wantNew = modifiers.includes("--new") || modifiers.includes("new");
|
|
7573
|
-
const goal = await loadGoal(goalFilePath(opts.projectRoot));
|
|
7576
|
+
const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
|
|
7574
7577
|
if (!goal) {
|
|
7575
7578
|
const msg3 = `${color.red("Eternal/parallel mode requires a goal.")} Run \`/goal set <mission>\` first.`;
|
|
7576
7579
|
opts.renderer.writeWarning(msg3);
|
|
@@ -7673,7 +7676,7 @@ function formatPhaseList(graph) {
|
|
|
7673
7676
|
}
|
|
7674
7677
|
async function gatherProjectContext(projectRoot) {
|
|
7675
7678
|
try {
|
|
7676
|
-
const raw = await fsp5.readFile(
|
|
7679
|
+
const raw = await fsp5.readFile(path39.join(projectRoot, "package.json"), "utf8");
|
|
7677
7680
|
const pkg = JSON.parse(raw);
|
|
7678
7681
|
const parts = [
|
|
7679
7682
|
`Project: ${String(pkg.name ?? "unknown")}`,
|
|
@@ -8144,7 +8147,9 @@ async function annotationsCommand(opts, sessionId) {
|
|
|
8144
8147
|
)
|
|
8145
8148
|
};
|
|
8146
8149
|
}
|
|
8147
|
-
const
|
|
8150
|
+
const annotationsOpts = { dir: storeDir, events: opts.events };
|
|
8151
|
+
if (opts.context?.traceId !== void 0) annotationsOpts.traceId = opts.context.traceId;
|
|
8152
|
+
const annotations = new AnnotationsStore(annotationsOpts);
|
|
8148
8153
|
const open = await annotations.listOpen(sessionId);
|
|
8149
8154
|
if (open.length === 0) {
|
|
8150
8155
|
return {
|
|
@@ -8651,8 +8656,19 @@ function listRoles() {
|
|
|
8651
8656
|
}
|
|
8652
8657
|
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
8653
8658
|
var MAX_OUTPUT_LINES = 500;
|
|
8659
|
+
var WINDOWS_CMD_METACHARACTERS = /[;&|<>^$,(){}[\]!#%'"\\/`]/;
|
|
8660
|
+
function validateCommand(cmd) {
|
|
8661
|
+
if (process.platform !== "win32") return;
|
|
8662
|
+
if (WINDOWS_CMD_METACHARACTERS.test(cmd)) {
|
|
8663
|
+
throw new Error(
|
|
8664
|
+
`Command contains disallowed metacharacters for Windows: ${cmd.match(WINDOWS_CMD_METACHARACTERS)?.[0] ?? "?"}
|
|
8665
|
+
The following characters are not allowed: ; & | < > ^ $ , ( ) { } [ ] ! # % ' " \\ / \` * ?`
|
|
8666
|
+
);
|
|
8667
|
+
}
|
|
8668
|
+
}
|
|
8654
8669
|
function runCommand(cmd, cwd, timeout) {
|
|
8655
|
-
return new Promise((
|
|
8670
|
+
return new Promise((resolve11) => {
|
|
8671
|
+
validateCommand(cmd);
|
|
8656
8672
|
const opts = {
|
|
8657
8673
|
cwd,
|
|
8658
8674
|
timeout,
|
|
@@ -8664,7 +8680,7 @@ function runCommand(cmd, cwd, timeout) {
|
|
|
8664
8680
|
shell: process.platform === "win32" ? true : false
|
|
8665
8681
|
};
|
|
8666
8682
|
execFile(cmd, [], opts, (error, stdout, stderr) => {
|
|
8667
|
-
|
|
8683
|
+
resolve11({
|
|
8668
8684
|
stdout,
|
|
8669
8685
|
stderr,
|
|
8670
8686
|
exitCode: typeof error?.code === "number" ? error.code : 0,
|
|
@@ -8733,6 +8749,7 @@ Examples:
|
|
|
8733
8749
|
/dev git diff --stat`
|
|
8734
8750
|
};
|
|
8735
8751
|
}
|
|
8752
|
+
validateCommand(cmd);
|
|
8736
8753
|
const cwd = opts.cwd;
|
|
8737
8754
|
const startedAt = Date.now();
|
|
8738
8755
|
opts.renderer.write(color.dim(`$ ${cmd}`));
|
|
@@ -9085,17 +9102,17 @@ function diagnoseConfig(cfg, plugins = []) {
|
|
|
9085
9102
|
function scanPlaintextSecrets(node, prefix, findings) {
|
|
9086
9103
|
if (!isPlainObject(node)) return;
|
|
9087
9104
|
for (const [key, value] of Object.entries(node)) {
|
|
9088
|
-
const
|
|
9105
|
+
const path40 = prefix ? `${prefix}.${key}` : key;
|
|
9089
9106
|
if (typeof value === "string") {
|
|
9090
9107
|
if (value.length > 0 && isSecretField$1(key) && !value.startsWith(ENC_PREFIX)) {
|
|
9091
9108
|
findings.push({
|
|
9092
|
-
path:
|
|
9109
|
+
path: path40,
|
|
9093
9110
|
problem: "looks like a plaintext secret (not vault-encrypted) \u2014 it will be encrypted on next boot",
|
|
9094
9111
|
severity: "warning"
|
|
9095
9112
|
});
|
|
9096
9113
|
}
|
|
9097
9114
|
} else if (isPlainObject(value)) {
|
|
9098
|
-
scanPlaintextSecrets(value,
|
|
9115
|
+
scanPlaintextSecrets(value, path40, findings);
|
|
9099
9116
|
}
|
|
9100
9117
|
}
|
|
9101
9118
|
}
|
|
@@ -9116,7 +9133,7 @@ function resolvePersistPath(deps) {
|
|
|
9116
9133
|
return deps.globalConfigPath;
|
|
9117
9134
|
}
|
|
9118
9135
|
async function ensureProjectDir(filePath) {
|
|
9119
|
-
const dir =
|
|
9136
|
+
const dir = path39.dirname(filePath);
|
|
9120
9137
|
try {
|
|
9121
9138
|
await fsp5.mkdir(dir, { recursive: true });
|
|
9122
9139
|
} catch {
|
|
@@ -9320,8 +9337,8 @@ function buildDoctorCommand(opts) {
|
|
|
9320
9337
|
return ` ${icon} ${color.cyan(f.path)} \u2014 ${f.problem}${fix}`;
|
|
9321
9338
|
}
|
|
9322
9339
|
async function findParsableBackup(file) {
|
|
9323
|
-
const dir =
|
|
9324
|
-
const base =
|
|
9340
|
+
const dir = path39.dirname(file);
|
|
9341
|
+
const base = path39.basename(file);
|
|
9325
9342
|
const candidates = [`${base}.last`];
|
|
9326
9343
|
try {
|
|
9327
9344
|
const siblings = await fsp5.readdir(dir);
|
|
@@ -9332,7 +9349,7 @@ function buildDoctorCommand(opts) {
|
|
|
9332
9349
|
}
|
|
9333
9350
|
for (const name of candidates) {
|
|
9334
9351
|
try {
|
|
9335
|
-
const raw = await fsp5.readFile(
|
|
9352
|
+
const raw = await fsp5.readFile(path39.join(dir, name), "utf8");
|
|
9336
9353
|
JSON.parse(raw);
|
|
9337
9354
|
return { name, raw };
|
|
9338
9355
|
} catch {
|
|
@@ -9449,7 +9466,7 @@ function buildDoctorCommand(opts) {
|
|
|
9449
9466
|
await atomicWrite(target.file, JSON.stringify(report.fixed, null, 2));
|
|
9450
9467
|
if (!target.isProject) {
|
|
9451
9468
|
try {
|
|
9452
|
-
const homeFn = () =>
|
|
9469
|
+
const homeFn = () => path39.dirname(path39.dirname(target.file));
|
|
9453
9470
|
await appendHistory(parsed, report.fixed, "config doctor auto-fix", homeFn);
|
|
9454
9471
|
} catch {
|
|
9455
9472
|
}
|
|
@@ -10430,11 +10447,11 @@ function classifyError(input) {
|
|
|
10430
10447
|
}
|
|
10431
10448
|
function extractCode(s) {
|
|
10432
10449
|
const ts = /\bTS\d+\b|\bCS\d+\b/.exec(s);
|
|
10433
|
-
if (ts) return ts[0];
|
|
10450
|
+
if (ts !== null) return ts[0];
|
|
10434
10451
|
const rust = /\bE\d{4,}\b/.exec(s);
|
|
10435
|
-
if (rust) return rust[0];
|
|
10452
|
+
if (rust !== null) return rust[0];
|
|
10436
10453
|
const c = /\bc\d+\b/i.exec(s);
|
|
10437
|
-
if (c) return c[0];
|
|
10454
|
+
if (c !== null) return c[0];
|
|
10438
10455
|
return void 0;
|
|
10439
10456
|
}
|
|
10440
10457
|
function needsSubagent(c) {
|
|
@@ -11310,7 +11327,7 @@ function buildGoalCommand(opts) {
|
|
|
11310
11327
|
case "":
|
|
11311
11328
|
case "show":
|
|
11312
11329
|
case "status": {
|
|
11313
|
-
const current = await loadGoal(goalPath);
|
|
11330
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11314
11331
|
if (!current) {
|
|
11315
11332
|
const msg2 = "No goal set. Use `/goal set <mission text>` to create one.";
|
|
11316
11333
|
opts.renderer.write(msg2);
|
|
@@ -11335,7 +11352,7 @@ function buildGoalCommand(opts) {
|
|
|
11335
11352
|
if (!refined) {
|
|
11336
11353
|
refined = refineGoalHeuristic(setText);
|
|
11337
11354
|
}
|
|
11338
|
-
const existing = await loadGoal(goalPath);
|
|
11355
|
+
const existing = await loadGoal(goalPath, opts.events);
|
|
11339
11356
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11340
11357
|
const next = existing ? {
|
|
11341
11358
|
...existing,
|
|
@@ -11352,7 +11369,7 @@ function buildGoalCommand(opts) {
|
|
|
11352
11369
|
refinedGoal: refined.refinedGoal,
|
|
11353
11370
|
deliverables: refined.deliverables
|
|
11354
11371
|
};
|
|
11355
|
-
await saveGoal(goalPath, next);
|
|
11372
|
+
await saveGoal(goalPath, next, opts.events);
|
|
11356
11373
|
const lines = [];
|
|
11357
11374
|
lines.push(`\u{1F3AF} ${color.green("Goal locked:")} ${color.bold(refined.refinedGoal)}`);
|
|
11358
11375
|
if (refined.refinedGoal !== setText) {
|
|
@@ -11379,7 +11396,7 @@ function buildGoalCommand(opts) {
|
|
|
11379
11396
|
};
|
|
11380
11397
|
}
|
|
11381
11398
|
case "refine": {
|
|
11382
|
-
const current = await loadGoal(goalPath);
|
|
11399
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11383
11400
|
if (!current) {
|
|
11384
11401
|
const msg2 = "No goal set to refine. Use /goal set <text> first.";
|
|
11385
11402
|
opts.renderer.writeWarning(msg2);
|
|
@@ -11398,7 +11415,7 @@ function buildGoalCommand(opts) {
|
|
|
11398
11415
|
refinedGoal: refined.refinedGoal,
|
|
11399
11416
|
deliverables: refined.deliverables
|
|
11400
11417
|
};
|
|
11401
|
-
await saveGoal(goalPath, updated);
|
|
11418
|
+
await saveGoal(goalPath, updated, opts.events);
|
|
11402
11419
|
const msg = `${color.green("\u2713")} Goal re-refined with ${refined.deliverables.length} deliverables.`;
|
|
11403
11420
|
opts.renderer.write(msg);
|
|
11404
11421
|
return { message: `${msg}
|
|
@@ -11407,14 +11424,14 @@ ${formatGoal(updated)}` };
|
|
|
11407
11424
|
}
|
|
11408
11425
|
case "clear":
|
|
11409
11426
|
case "reset": {
|
|
11410
|
-
const current = await loadGoal(goalPath);
|
|
11427
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11411
11428
|
if (!current) {
|
|
11412
11429
|
const msg2 = "No goal to clear.";
|
|
11413
11430
|
opts.renderer.write(msg2);
|
|
11414
11431
|
return { message: msg2 };
|
|
11415
11432
|
}
|
|
11416
11433
|
const abandoned = { ...current, goalState: "abandoned" };
|
|
11417
|
-
await saveGoal(goalPath, abandoned);
|
|
11434
|
+
await saveGoal(goalPath, abandoned, opts.events);
|
|
11418
11435
|
const { unlink: unlink4 } = await import('fs/promises');
|
|
11419
11436
|
try {
|
|
11420
11437
|
await unlink4(goalPath);
|
|
@@ -11428,7 +11445,7 @@ ${formatGoal(updated)}` };
|
|
|
11428
11445
|
}
|
|
11429
11446
|
case "journal":
|
|
11430
11447
|
case "log": {
|
|
11431
|
-
const current = await loadGoal(goalPath);
|
|
11448
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11432
11449
|
if (!current) {
|
|
11433
11450
|
const msg2 = "No goal set.";
|
|
11434
11451
|
opts.renderer.write(msg2);
|
|
@@ -11453,7 +11470,7 @@ ${lines.join("\n")}`;
|
|
|
11453
11470
|
return { message: msg };
|
|
11454
11471
|
}
|
|
11455
11472
|
case "pause": {
|
|
11456
|
-
const current = await loadGoal(goalPath);
|
|
11473
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11457
11474
|
if (!current) {
|
|
11458
11475
|
const msg2 = "No goal set \u2014 nothing to pause.";
|
|
11459
11476
|
opts.renderer.writeWarning(msg2);
|
|
@@ -11465,13 +11482,13 @@ ${lines.join("\n")}`;
|
|
|
11465
11482
|
return { message: msg2 };
|
|
11466
11483
|
}
|
|
11467
11484
|
const paused = { ...current, goalState: "paused" };
|
|
11468
|
-
await saveGoal(goalPath, paused);
|
|
11485
|
+
await saveGoal(goalPath, paused, opts.events);
|
|
11469
11486
|
const msg = `${color.cyan("Goal paused.")} Current iteration will finish, then the loop stops. Use /goal resume to continue.`;
|
|
11470
11487
|
opts.renderer.write(msg);
|
|
11471
11488
|
return { message: msg };
|
|
11472
11489
|
}
|
|
11473
11490
|
case "resume": {
|
|
11474
|
-
const current = await loadGoal(goalPath);
|
|
11491
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11475
11492
|
if (!current) {
|
|
11476
11493
|
const msg2 = "No goal set \u2014 cannot resume.";
|
|
11477
11494
|
opts.renderer.writeWarning(msg2);
|
|
@@ -11483,7 +11500,7 @@ ${lines.join("\n")}`;
|
|
|
11483
11500
|
return { message: msg2 };
|
|
11484
11501
|
}
|
|
11485
11502
|
const resumed = { ...current, goalState: "active" };
|
|
11486
|
-
await saveGoal(goalPath, resumed);
|
|
11503
|
+
await saveGoal(goalPath, resumed, opts.events);
|
|
11487
11504
|
const msg = `${color.green("Goal resumed.")} Loop will continue from the next iteration.`;
|
|
11488
11505
|
opts.renderer.write(msg);
|
|
11489
11506
|
return { message: msg };
|
|
@@ -11570,8 +11587,8 @@ function buildInitCommand(opts) {
|
|
|
11570
11587
|
description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
|
|
11571
11588
|
async run(_args, ctx) {
|
|
11572
11589
|
const root = ctx?.projectRoot ?? opts.projectRoot ?? process.cwd();
|
|
11573
|
-
const dir =
|
|
11574
|
-
const file =
|
|
11590
|
+
const dir = path39.join(root, ".wrongstack");
|
|
11591
|
+
const file = path39.join(dir, "AGENTS.md");
|
|
11575
11592
|
const isFirstInit = !await fileExists(file);
|
|
11576
11593
|
const detected = await detectProjectFacts(root);
|
|
11577
11594
|
const body = renderAgentsTemplate(detected);
|
|
@@ -11579,7 +11596,7 @@ function buildInitCommand(opts) {
|
|
|
11579
11596
|
await fsp5.writeFile(file, body, "utf8");
|
|
11580
11597
|
let nodePkg = false;
|
|
11581
11598
|
try {
|
|
11582
|
-
await fsp5.access(
|
|
11599
|
+
await fsp5.access(path39.join(root, "package.json"));
|
|
11583
11600
|
nodePkg = true;
|
|
11584
11601
|
} catch {
|
|
11585
11602
|
}
|
|
@@ -12160,8 +12177,20 @@ async function runAdd(name, enable, configured, configPath2, mcpRegistry, all) {
|
|
|
12160
12177
|
}
|
|
12161
12178
|
async function runRemove(name, configured, configPath2, mcpRegistry) {
|
|
12162
12179
|
if (!configured[name]) return `Server "${name}" is not in config.`;
|
|
12163
|
-
|
|
12164
|
-
|
|
12180
|
+
try {
|
|
12181
|
+
await mcpRegistry.stop(name);
|
|
12182
|
+
} catch (err) {
|
|
12183
|
+
console.error(
|
|
12184
|
+
JSON.stringify({
|
|
12185
|
+
level: "warn",
|
|
12186
|
+
event: "mcp.stop_failed_on_remove",
|
|
12187
|
+
server: name,
|
|
12188
|
+
message: err instanceof Error ? err.message : String(err),
|
|
12189
|
+
note: "config entry removed but server may still be running",
|
|
12190
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
12191
|
+
})
|
|
12192
|
+
);
|
|
12193
|
+
}
|
|
12165
12194
|
const full = await readConfig(configPath2);
|
|
12166
12195
|
const mcpServers = {
|
|
12167
12196
|
...full.mcpServers ?? {}
|
|
@@ -12200,8 +12229,20 @@ async function runEnable(name, configured, configPath2, mcpRegistry) {
|
|
|
12200
12229
|
async function runDisable(name, configured, configPath2, mcpRegistry) {
|
|
12201
12230
|
const cfg = configured[name];
|
|
12202
12231
|
if (!cfg) return `Server "${name}" is not in config.`;
|
|
12203
|
-
|
|
12204
|
-
|
|
12232
|
+
try {
|
|
12233
|
+
await mcpRegistry.stop(name);
|
|
12234
|
+
} catch (err) {
|
|
12235
|
+
console.error(
|
|
12236
|
+
JSON.stringify({
|
|
12237
|
+
level: "warn",
|
|
12238
|
+
event: "mcp.stop_failed_on_disable",
|
|
12239
|
+
server: name,
|
|
12240
|
+
message: err instanceof Error ? err.message : String(err),
|
|
12241
|
+
note: "config marked disabled but server may still be running",
|
|
12242
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
12243
|
+
})
|
|
12244
|
+
);
|
|
12245
|
+
}
|
|
12205
12246
|
const full = await readConfig(configPath2);
|
|
12206
12247
|
const mcpServers = {
|
|
12207
12248
|
...full.mcpServers ?? {}
|
|
@@ -12239,9 +12280,9 @@ function stateBadge(state) {
|
|
|
12239
12280
|
return color.dim(state);
|
|
12240
12281
|
}
|
|
12241
12282
|
}
|
|
12242
|
-
async function readConfig(
|
|
12283
|
+
async function readConfig(path40) {
|
|
12243
12284
|
try {
|
|
12244
|
-
return JSON.parse(await fsp5.readFile(
|
|
12285
|
+
return JSON.parse(await fsp5.readFile(path40, "utf8"));
|
|
12245
12286
|
} catch {
|
|
12246
12287
|
return {};
|
|
12247
12288
|
}
|
|
@@ -12249,11 +12290,11 @@ async function readConfig(path39) {
|
|
|
12249
12290
|
function isMcpServerRecord(value) {
|
|
12250
12291
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
12251
12292
|
}
|
|
12252
|
-
async function writeConfig(
|
|
12293
|
+
async function writeConfig(path40, cfg) {
|
|
12253
12294
|
const raw = JSON.stringify(cfg, null, 2);
|
|
12254
|
-
const tmp =
|
|
12295
|
+
const tmp = path40 + ".tmp";
|
|
12255
12296
|
await fsp5.writeFile(tmp, raw, "utf8");
|
|
12256
|
-
await fsp5.rename(tmp,
|
|
12297
|
+
await fsp5.rename(tmp, path40);
|
|
12257
12298
|
}
|
|
12258
12299
|
|
|
12259
12300
|
// src/slash-commands/mcp.ts
|
|
@@ -14552,7 +14593,7 @@ async function listProjectsCommand(opts, ctx) {
|
|
|
14552
14593
|
return { message: lines.join("\n") };
|
|
14553
14594
|
}
|
|
14554
14595
|
async function addProjectCommand(opts, ctx, targetPath, displayName) {
|
|
14555
|
-
const resolved =
|
|
14596
|
+
const resolved = path39.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), targetPath);
|
|
14556
14597
|
try {
|
|
14557
14598
|
await fsp5.access(resolved);
|
|
14558
14599
|
} catch {
|
|
@@ -14569,7 +14610,7 @@ async function addProjectCommand(opts, ctx, targetPath, displayName) {
|
|
|
14569
14610
|
message: color.yellow(`Project already registered: "${existing.name}" (${existing.slug})`)
|
|
14570
14611
|
};
|
|
14571
14612
|
}
|
|
14572
|
-
const name = displayName?.trim() ||
|
|
14613
|
+
const name = displayName?.trim() || path39.basename(resolved);
|
|
14573
14614
|
const slug = generateSlug(resolved);
|
|
14574
14615
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14575
14616
|
await ensureProjectDataDir(slug, opts.paths?.globalConfig);
|
|
@@ -14622,7 +14663,7 @@ async function removeProjectCommand(opts, _ctx, slugOrName) {
|
|
|
14622
14663
|
};
|
|
14623
14664
|
}
|
|
14624
14665
|
async function switchProjectCommand(opts, ctx, target, displayName) {
|
|
14625
|
-
const resolved =
|
|
14666
|
+
const resolved = path39.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), target);
|
|
14626
14667
|
try {
|
|
14627
14668
|
await fsp5.access(resolved);
|
|
14628
14669
|
} catch {
|
|
@@ -14636,8 +14677,8 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
|
|
|
14636
14677
|
try {
|
|
14637
14678
|
const req2 = createRequire(import.meta.url);
|
|
14638
14679
|
const pkgPath = req2.resolve("@wrongstack/cli/package.json");
|
|
14639
|
-
const pkgDir =
|
|
14640
|
-
cliPath =
|
|
14680
|
+
const pkgDir = path39.dirname(pkgPath);
|
|
14681
|
+
cliPath = path39.join(pkgDir, "dist", "index.js");
|
|
14641
14682
|
await fsp5.access(cliPath);
|
|
14642
14683
|
} catch {
|
|
14643
14684
|
cliPath = process.argv[1] ?? "";
|
|
@@ -14654,13 +14695,13 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
|
|
|
14654
14695
|
if (existing) {
|
|
14655
14696
|
existing.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
14656
14697
|
} else {
|
|
14657
|
-
const name = displayName?.trim() ||
|
|
14698
|
+
const name = displayName?.trim() || path39.basename(resolved);
|
|
14658
14699
|
const slug = generateSlug(resolved);
|
|
14659
14700
|
manifest.projects.push({ name, root: resolved, slug, lastSeen: (/* @__PURE__ */ new Date()).toISOString() });
|
|
14660
14701
|
await ensureProjectDataDir(slug, opts.paths?.globalConfig);
|
|
14661
14702
|
}
|
|
14662
14703
|
await saveManifest(manifest, opts.paths?.globalConfig);
|
|
14663
|
-
const targetName = displayName?.trim() ||
|
|
14704
|
+
const targetName = displayName?.trim() || path39.basename(resolved);
|
|
14664
14705
|
const canSwitch = await confirmProjectSwitch(opts, targetName);
|
|
14665
14706
|
if (!canSwitch) return { message: "" };
|
|
14666
14707
|
const nodeExe = process.execPath;
|
|
@@ -14776,8 +14817,8 @@ async function spawnInProject(opts, _ctx, root, projectName) {
|
|
|
14776
14817
|
try {
|
|
14777
14818
|
const req2 = createRequire(import.meta.url);
|
|
14778
14819
|
const pkgPath = req2.resolve("@wrongstack/cli/package.json");
|
|
14779
|
-
const pkgDir =
|
|
14780
|
-
cliPath =
|
|
14820
|
+
const pkgDir = path39.dirname(pkgPath);
|
|
14821
|
+
cliPath = path39.join(pkgDir, "dist", "index.js");
|
|
14781
14822
|
await fsp5.access(cliPath);
|
|
14782
14823
|
} catch {
|
|
14783
14824
|
cliPath = process.argv[1] ?? "";
|
|
@@ -14794,7 +14835,7 @@ async function spawnInProject(opts, _ctx, root, projectName) {
|
|
|
14794
14835
|
if (existing) {
|
|
14795
14836
|
existing.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
14796
14837
|
} else {
|
|
14797
|
-
const name = projectName ||
|
|
14838
|
+
const name = projectName || path39.basename(root);
|
|
14798
14839
|
const slug = generateSlug(root);
|
|
14799
14840
|
manifest.projects.push({ name, root, slug, lastSeen: (/* @__PURE__ */ new Date()).toISOString() });
|
|
14800
14841
|
await ensureProjectDataDir(slug, opts.paths?.globalConfig);
|
|
@@ -14826,8 +14867,8 @@ async function handleNewSession(_opts, _ctx) {
|
|
|
14826
14867
|
try {
|
|
14827
14868
|
const req2 = createRequire(import.meta.url);
|
|
14828
14869
|
const pkgPath = req2.resolve("@wrongstack/cli/package.json");
|
|
14829
|
-
const pkgDir =
|
|
14830
|
-
cliPath =
|
|
14870
|
+
const pkgDir = path39.dirname(pkgPath);
|
|
14871
|
+
cliPath = path39.join(pkgDir, "dist", "index.js");
|
|
14831
14872
|
await fsp5.access(cliPath);
|
|
14832
14873
|
} catch {
|
|
14833
14874
|
cliPath = process.argv[1] ?? "";
|
|
@@ -14885,7 +14926,7 @@ async function handlePrevSessions(opts, _ctx) {
|
|
|
14885
14926
|
return { message: lines.join("\n") };
|
|
14886
14927
|
}
|
|
14887
14928
|
async function runGit(args, cwd) {
|
|
14888
|
-
return new Promise((
|
|
14929
|
+
return new Promise((resolve11) => {
|
|
14889
14930
|
const child = spawn("git", args, {
|
|
14890
14931
|
cwd,
|
|
14891
14932
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -14896,8 +14937,8 @@ async function runGit(args, cwd) {
|
|
|
14896
14937
|
child.stdout?.on("data", (d) => {
|
|
14897
14938
|
stdout += d;
|
|
14898
14939
|
});
|
|
14899
|
-
child.on("error", () =>
|
|
14900
|
-
child.on("close", (code) =>
|
|
14940
|
+
child.on("error", () => resolve11({ stdout, code: 1 }));
|
|
14941
|
+
child.on("close", (code) => resolve11({ stdout, code: code ?? 0 }));
|
|
14901
14942
|
});
|
|
14902
14943
|
}
|
|
14903
14944
|
async function getChangedFiles(cwd) {
|
|
@@ -14938,7 +14979,7 @@ function buildReviewCommand(opts) {
|
|
|
14938
14979
|
for (const f of allChanged) {
|
|
14939
14980
|
if (f.path.startsWith(".wrongstack/")) continue;
|
|
14940
14981
|
try {
|
|
14941
|
-
await fsp5.access(
|
|
14982
|
+
await fsp5.access(path39.join(cwd, f.path));
|
|
14942
14983
|
existing.push(f);
|
|
14943
14984
|
} catch {
|
|
14944
14985
|
}
|
|
@@ -14949,7 +14990,7 @@ function buildReviewCommand(opts) {
|
|
|
14949
14990
|
const filesWithContent = [];
|
|
14950
14991
|
for (const f of existing.slice(0, 30)) {
|
|
14951
14992
|
try {
|
|
14952
|
-
const content = await fsp5.readFile(
|
|
14993
|
+
const content = await fsp5.readFile(path39.join(cwd, f.path), "utf8");
|
|
14953
14994
|
filesWithContent.push({ ...f, content });
|
|
14954
14995
|
} catch {
|
|
14955
14996
|
}
|
|
@@ -15322,7 +15363,7 @@ var DEFAULTS = {
|
|
|
15322
15363
|
working_dir: true
|
|
15323
15364
|
};
|
|
15324
15365
|
function resolveConfigPath() {
|
|
15325
|
-
return process.env[CONFIG_ENV] ??
|
|
15366
|
+
return process.env[CONFIG_ENV] ?? path39.join(process.env.HOME ?? "", ".wrongstack", "statusline.json");
|
|
15326
15367
|
}
|
|
15327
15368
|
async function loadStatuslineConfig() {
|
|
15328
15369
|
const p = resolveConfigPath();
|
|
@@ -15336,7 +15377,7 @@ async function loadStatuslineConfig() {
|
|
|
15336
15377
|
async function saveStatuslineConfig(cfg) {
|
|
15337
15378
|
const p = resolveConfigPath();
|
|
15338
15379
|
try {
|
|
15339
|
-
await fsp5.mkdir(
|
|
15380
|
+
await fsp5.mkdir(path39.dirname(p), { recursive: true });
|
|
15340
15381
|
await atomicWrite(p, JSON.stringify(cfg, null, 2));
|
|
15341
15382
|
} catch (err) {
|
|
15342
15383
|
throw new FsError({
|
|
@@ -15748,13 +15789,13 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
15748
15789
|
}
|
|
15749
15790
|
async function discoverPackageFiles(projectRoot) {
|
|
15750
15791
|
const files = [];
|
|
15751
|
-
const rootPkg =
|
|
15792
|
+
const rootPkg = path39.join(projectRoot, "package.json");
|
|
15752
15793
|
try {
|
|
15753
15794
|
await fsp5.access(rootPkg);
|
|
15754
15795
|
files.push(rootPkg);
|
|
15755
15796
|
} catch {
|
|
15756
15797
|
}
|
|
15757
|
-
const workspaceFile =
|
|
15798
|
+
const workspaceFile = path39.join(projectRoot, "pnpm-workspace.yaml");
|
|
15758
15799
|
try {
|
|
15759
15800
|
await fsp5.access(workspaceFile);
|
|
15760
15801
|
const content = await fsp5.readFile(workspaceFile, "utf8");
|
|
@@ -15764,12 +15805,12 @@ async function discoverPackageFiles(projectRoot) {
|
|
|
15764
15805
|
const globs = rawGlobs.split(/[\s,]+/).filter(Boolean).map((g) => g.replace(/['"]/g, ""));
|
|
15765
15806
|
for (const g of globs) {
|
|
15766
15807
|
const dirPrefix = g.replace(/\/?\*$/, "").replace(/\/\*$/, "");
|
|
15767
|
-
const dir =
|
|
15808
|
+
const dir = path39.join(projectRoot, dirPrefix);
|
|
15768
15809
|
try {
|
|
15769
15810
|
const entries = await fsp5.readdir(dir, { withFileTypes: true });
|
|
15770
15811
|
for (const e of entries) {
|
|
15771
15812
|
if (!e.isDirectory()) continue;
|
|
15772
|
-
const subPkg =
|
|
15813
|
+
const subPkg = path39.join(dir, e.name, "package.json");
|
|
15773
15814
|
try {
|
|
15774
15815
|
await fsp5.access(subPkg);
|
|
15775
15816
|
files.push(subPkg);
|
|
@@ -15784,7 +15825,7 @@ async function discoverPackageFiles(projectRoot) {
|
|
|
15784
15825
|
return files;
|
|
15785
15826
|
}
|
|
15786
15827
|
function buildTechStackTask(opts) {
|
|
15787
|
-
const pkgList = opts.packageFiles.map((f) => ` - ${
|
|
15828
|
+
const pkgList = opts.packageFiles.map((f) => ` - ${path39.relative(opts.projectRoot, f)}`).join("\n");
|
|
15788
15829
|
const header = opts.isInit ? [
|
|
15789
15830
|
"## Tech Stack Audit \u2014 First-Time Project Init",
|
|
15790
15831
|
"",
|
|
@@ -16212,7 +16253,7 @@ function buildWorkingDirCommand(_opts) {
|
|
|
16212
16253
|
}
|
|
16213
16254
|
const trimmed = args.trim();
|
|
16214
16255
|
if (!trimmed) {
|
|
16215
|
-
const rel2 =
|
|
16256
|
+
const rel2 = path39.relative(ctx.projectRoot, ctx.workingDir) || ".";
|
|
16216
16257
|
return {
|
|
16217
16258
|
message: [
|
|
16218
16259
|
`Working directory: ${color.bold(ctx.workingDir)}`,
|
|
@@ -16221,10 +16262,10 @@ function buildWorkingDirCommand(_opts) {
|
|
|
16221
16262
|
].join("\n")
|
|
16222
16263
|
};
|
|
16223
16264
|
}
|
|
16224
|
-
const resolved =
|
|
16225
|
-
const root =
|
|
16226
|
-
const rel =
|
|
16227
|
-
if (rel.startsWith("..") ||
|
|
16265
|
+
const resolved = path39.isAbsolute(trimmed) ? path39.resolve(trimmed) : path39.resolve(ctx.projectRoot, trimmed);
|
|
16266
|
+
const root = path39.resolve(ctx.projectRoot);
|
|
16267
|
+
const rel = path39.relative(root, resolved);
|
|
16268
|
+
if (rel.startsWith("..") || path39.isAbsolute(rel)) {
|
|
16228
16269
|
return {
|
|
16229
16270
|
message: color.red(
|
|
16230
16271
|
`Directory "${trimmed}" is outside the project root.
|
|
@@ -16249,8 +16290,8 @@ function buildWorkingDirCommand(_opts) {
|
|
|
16249
16290
|
message: color.red(err instanceof Error ? err.message : String(err))
|
|
16250
16291
|
};
|
|
16251
16292
|
}
|
|
16252
|
-
const prevRel =
|
|
16253
|
-
const newRel =
|
|
16293
|
+
const prevRel = path39.relative(ctx.projectRoot, previous) || ".";
|
|
16294
|
+
const newRel = path39.relative(ctx.projectRoot, resolved) || ".";
|
|
16254
16295
|
return {
|
|
16255
16296
|
message: [
|
|
16256
16297
|
color.green(` \u2713 ${prevRel} \u2192 ${color.bold(newRel)}`),
|
|
@@ -16450,13 +16491,13 @@ var MANIFESTS = [
|
|
|
16450
16491
|
];
|
|
16451
16492
|
async function detectProjectKind(projectRoot) {
|
|
16452
16493
|
try {
|
|
16453
|
-
await fsp5.access(
|
|
16494
|
+
await fsp5.access(path39.join(projectRoot, ".wrongstack", "AGENTS.md"));
|
|
16454
16495
|
return "initialized";
|
|
16455
16496
|
} catch {
|
|
16456
16497
|
}
|
|
16457
16498
|
for (const m of MANIFESTS) {
|
|
16458
16499
|
try {
|
|
16459
|
-
await fsp5.access(
|
|
16500
|
+
await fsp5.access(path39.join(projectRoot, m));
|
|
16460
16501
|
return "project";
|
|
16461
16502
|
} catch {
|
|
16462
16503
|
}
|
|
@@ -16464,8 +16505,8 @@ async function detectProjectKind(projectRoot) {
|
|
|
16464
16505
|
return "empty";
|
|
16465
16506
|
}
|
|
16466
16507
|
async function scaffoldAgentsMd(projectRoot) {
|
|
16467
|
-
const dir =
|
|
16468
|
-
const file =
|
|
16508
|
+
const dir = path39.join(projectRoot, ".wrongstack");
|
|
16509
|
+
const file = path39.join(dir, "AGENTS.md");
|
|
16469
16510
|
const facts = await detectProjectFacts(projectRoot);
|
|
16470
16511
|
const body = renderAgentsTemplate(facts);
|
|
16471
16512
|
await fsp5.mkdir(dir, { recursive: true });
|
|
@@ -16478,7 +16519,7 @@ async function runProjectCheck(opts) {
|
|
|
16478
16519
|
if (kind === "initialized") {
|
|
16479
16520
|
renderer.write(
|
|
16480
16521
|
`
|
|
16481
|
-
${color.green("\u2713")} Project initialized ${color.dim(`(${
|
|
16522
|
+
${color.green("\u2713")} Project initialized ${color.dim(`(${path39.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
|
|
16482
16523
|
`
|
|
16483
16524
|
);
|
|
16484
16525
|
return true;
|
|
@@ -16509,7 +16550,7 @@ async function runProjectCheck(opts) {
|
|
|
16509
16550
|
}
|
|
16510
16551
|
return true;
|
|
16511
16552
|
}
|
|
16512
|
-
const gitDir =
|
|
16553
|
+
const gitDir = path39.join(projectRoot, ".git");
|
|
16513
16554
|
let hasGit = false;
|
|
16514
16555
|
try {
|
|
16515
16556
|
await fsp5.access(gitDir);
|
|
@@ -16532,7 +16573,7 @@ async function runProjectCheck(opts) {
|
|
|
16532
16573
|
if (answer2 === "y" || answer2 === "yes") {
|
|
16533
16574
|
try {
|
|
16534
16575
|
const { spawn: spawn6 } = await import('child_process');
|
|
16535
|
-
await new Promise((
|
|
16576
|
+
await new Promise((resolve11, reject) => {
|
|
16536
16577
|
const child = spawn6("git", ["init"], {
|
|
16537
16578
|
cwd,
|
|
16538
16579
|
signal: AbortSignal.timeout(1e4),
|
|
@@ -16541,7 +16582,7 @@ async function runProjectCheck(opts) {
|
|
|
16541
16582
|
child.on("error", reject);
|
|
16542
16583
|
child.on(
|
|
16543
16584
|
"close",
|
|
16544
|
-
(code) => code === 0 ?
|
|
16585
|
+
(code) => code === 0 ? resolve11() : reject(new Error(`git init failed with ${code}`))
|
|
16545
16586
|
);
|
|
16546
16587
|
});
|
|
16547
16588
|
renderer.write(` ${color.green("\u2713")} Git repository initialized
|
|
@@ -16744,11 +16785,11 @@ async function countProjectFiles(projectRoot, threshold) {
|
|
|
16744
16785
|
for (const e of entries) {
|
|
16745
16786
|
if (SKIP_DIRS.has(e.name)) continue;
|
|
16746
16787
|
if (count >= threshold) return;
|
|
16747
|
-
const full =
|
|
16788
|
+
const full = path39.join(dir, e.name);
|
|
16748
16789
|
if (e.isDirectory()) {
|
|
16749
16790
|
await walk(full);
|
|
16750
16791
|
} else if (e.isFile()) {
|
|
16751
|
-
if (INDEXABLE_EXTS.has(
|
|
16792
|
+
if (INDEXABLE_EXTS.has(path39.extname(e.name))) {
|
|
16752
16793
|
count++;
|
|
16753
16794
|
}
|
|
16754
16795
|
}
|
|
@@ -17089,14 +17130,14 @@ function summarize(value, name) {
|
|
|
17089
17130
|
if (typeof v === "object" && v !== null) {
|
|
17090
17131
|
const o = v;
|
|
17091
17132
|
if (name === "edit") {
|
|
17092
|
-
const
|
|
17133
|
+
const path40 = typeof o["path"] === "string" ? o["path"] : "";
|
|
17093
17134
|
const reps = typeof o["replacements"] === "number" ? o["replacements"] : 0;
|
|
17094
|
-
return `${
|
|
17135
|
+
return `${path40} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
|
|
17095
17136
|
}
|
|
17096
17137
|
if (name === "write") {
|
|
17097
|
-
const
|
|
17138
|
+
const path40 = typeof o["path"] === "string" ? o["path"] : "";
|
|
17098
17139
|
const bytes = typeof o["bytes"] === "number" ? o["bytes"] : void 0;
|
|
17099
|
-
return bytes !== void 0 ? `${
|
|
17140
|
+
return bytes !== void 0 ? `${path40} ${bytes}B` : path40;
|
|
17100
17141
|
}
|
|
17101
17142
|
if (typeof o["count"] === "number") {
|
|
17102
17143
|
return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
|
|
@@ -17313,14 +17354,14 @@ var auditCmd = async (args, deps) => {
|
|
|
17313
17354
|
return verify.ok ? 0 : 1;
|
|
17314
17355
|
};
|
|
17315
17356
|
async function listAudits(log, dir, deps) {
|
|
17316
|
-
const
|
|
17317
|
-
const
|
|
17357
|
+
const fs39 = await import('fs/promises');
|
|
17358
|
+
const path40 = await import('path');
|
|
17318
17359
|
const out = [];
|
|
17319
17360
|
let foundRoot = true;
|
|
17320
17361
|
const scan = async (scanDir, prefix, depth) => {
|
|
17321
17362
|
let entries;
|
|
17322
17363
|
try {
|
|
17323
|
-
entries = await
|
|
17364
|
+
entries = await fs39.readdir(scanDir, { withFileTypes: true });
|
|
17324
17365
|
} catch {
|
|
17325
17366
|
if (depth === 0) foundRoot = false;
|
|
17326
17367
|
return;
|
|
@@ -17328,7 +17369,7 @@ async function listAudits(log, dir, deps) {
|
|
|
17328
17369
|
for (const entry of entries) {
|
|
17329
17370
|
if (entry.name.startsWith(".")) continue;
|
|
17330
17371
|
if (entry.isDirectory()) {
|
|
17331
|
-
if (depth === 0) await scan(
|
|
17372
|
+
if (depth === 0) await scan(path40.join(scanDir, entry.name), entry.name, depth + 1);
|
|
17332
17373
|
continue;
|
|
17333
17374
|
}
|
|
17334
17375
|
if (!entry.isFile() || !entry.name.endsWith(".audit.jsonl")) continue;
|
|
@@ -18247,7 +18288,200 @@ try {
|
|
|
18247
18288
|
} catch {
|
|
18248
18289
|
}
|
|
18249
18290
|
|
|
18250
|
-
// src/subcommands/handlers/
|
|
18291
|
+
// src/subcommands/handlers/bench.ts
|
|
18292
|
+
var benchCmd = async (args, deps) => {
|
|
18293
|
+
const sub = args[0];
|
|
18294
|
+
const rest = args.slice(1);
|
|
18295
|
+
switch (sub) {
|
|
18296
|
+
case "run":
|
|
18297
|
+
return benchRun(rest, deps);
|
|
18298
|
+
case "report":
|
|
18299
|
+
return benchReport(rest, deps);
|
|
18300
|
+
case "list":
|
|
18301
|
+
return benchList(rest, deps);
|
|
18302
|
+
default:
|
|
18303
|
+
printUsage(deps);
|
|
18304
|
+
return sub === void 0 ? 0 : 1;
|
|
18305
|
+
}
|
|
18306
|
+
};
|
|
18307
|
+
function printUsage(deps) {
|
|
18308
|
+
deps.renderer.write(
|
|
18309
|
+
[
|
|
18310
|
+
color.bold("wstack bench") + " \u2014 model-independent agentic benchmarks",
|
|
18311
|
+
"",
|
|
18312
|
+
" run Run a suite across a model matrix and write a report",
|
|
18313
|
+
" report Re-render report.md from a finished run directory",
|
|
18314
|
+
" list Show available suites and configured model cells",
|
|
18315
|
+
"",
|
|
18316
|
+
color.dim("Examples:"),
|
|
18317
|
+
color.dim(
|
|
18318
|
+
" wstack bench run --suite polyglot --polyglot-dir ./polyglot --models bench.config.json --limit 5"
|
|
18319
|
+
),
|
|
18320
|
+
color.dim(" wstack bench report ./bench-results/2026-06-14T10-00-00"),
|
|
18321
|
+
""
|
|
18322
|
+
].join("\n") + "\n"
|
|
18323
|
+
);
|
|
18324
|
+
}
|
|
18325
|
+
function flagStr(deps, name) {
|
|
18326
|
+
const v = deps.flags?.[name];
|
|
18327
|
+
return typeof v === "string" ? v : void 0;
|
|
18328
|
+
}
|
|
18329
|
+
function flagBool(deps, name) {
|
|
18330
|
+
const v = deps.flags?.[name];
|
|
18331
|
+
return v === true || v === "true";
|
|
18332
|
+
}
|
|
18333
|
+
async function resolveWstackEntry() {
|
|
18334
|
+
try {
|
|
18335
|
+
const req2 = createRequire(import.meta.url);
|
|
18336
|
+
const pkgPath = req2.resolve("@wrongstack/cli/package.json");
|
|
18337
|
+
const entry = path39.join(path39.dirname(pkgPath), "dist", "index.js");
|
|
18338
|
+
await fsp5.access(entry);
|
|
18339
|
+
return entry;
|
|
18340
|
+
} catch {
|
|
18341
|
+
return process.argv[1] ?? "";
|
|
18342
|
+
}
|
|
18343
|
+
}
|
|
18344
|
+
async function benchRun(_args, deps) {
|
|
18345
|
+
const suiteId = flagStr(deps, "suite") ?? "polyglot";
|
|
18346
|
+
const modelsPath = flagStr(deps, "models") ?? "bench.config.json";
|
|
18347
|
+
const limitRaw = flagStr(deps, "limit");
|
|
18348
|
+
const limit = limitRaw ? Math.max(1, Number.parseInt(limitRaw, 10)) : void 0;
|
|
18349
|
+
const outBase = flagStr(deps, "out") ?? "bench-results";
|
|
18350
|
+
let config;
|
|
18351
|
+
try {
|
|
18352
|
+
config = await loadBenchConfig(path39.resolve(deps.cwd, modelsPath));
|
|
18353
|
+
} catch (err) {
|
|
18354
|
+
deps.renderer.writeError(err instanceof Error ? err.message : String(err));
|
|
18355
|
+
return 1;
|
|
18356
|
+
}
|
|
18357
|
+
const concurrencyRaw = flagStr(deps, "concurrency");
|
|
18358
|
+
if (concurrencyRaw) {
|
|
18359
|
+
const c = Number.parseInt(concurrencyRaw, 10);
|
|
18360
|
+
if (c > 0) config.concurrency = c;
|
|
18361
|
+
}
|
|
18362
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
18363
|
+
const outDir = path39.resolve(deps.cwd, outBase, stamp);
|
|
18364
|
+
const predictionsDir = path39.join(outDir, "predictions");
|
|
18365
|
+
let suite;
|
|
18366
|
+
let grade;
|
|
18367
|
+
let isSwebench = false;
|
|
18368
|
+
if (suiteId === "polyglot") {
|
|
18369
|
+
const polyglotDir = flagStr(deps, "polyglot-dir");
|
|
18370
|
+
if (!polyglotDir) {
|
|
18371
|
+
deps.renderer.writeError("--polyglot-dir <path> is required for the polyglot suite.");
|
|
18372
|
+
return 1;
|
|
18373
|
+
}
|
|
18374
|
+
const languagesRaw = flagStr(deps, "languages");
|
|
18375
|
+
const languages = languagesRaw ? languagesRaw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
18376
|
+
suite = createPolyglotSuite({ polyglotDir: path39.resolve(deps.cwd, polyglotDir), languages });
|
|
18377
|
+
grade = (a) => gradePolyglot(a);
|
|
18378
|
+
} else if (suiteId === "swebench") {
|
|
18379
|
+
isSwebench = true;
|
|
18380
|
+
const datasetDir = flagStr(deps, "dataset-dir");
|
|
18381
|
+
const docker = flagBool(deps, "docker");
|
|
18382
|
+
suite = createSwebenchSuite({
|
|
18383
|
+
datasetDir: datasetDir ? path39.resolve(deps.cwd, datasetDir) : void 0,
|
|
18384
|
+
docker
|
|
18385
|
+
});
|
|
18386
|
+
grade = (a) => gradeSwebench({ ...a, predictionsDir });
|
|
18387
|
+
} else {
|
|
18388
|
+
deps.renderer.writeError(`unknown suite "${suiteId}" (expected: polyglot | swebench)`);
|
|
18389
|
+
return 1;
|
|
18390
|
+
}
|
|
18391
|
+
const toolNames = deps.toolRegistry?.list().map((t) => t.name) ?? [];
|
|
18392
|
+
const wstackEntry = await resolveWstackEntry();
|
|
18393
|
+
deps.renderer.writeInfo(`Running ${suiteId} across ${config.cells.length} model(s)\u2026`);
|
|
18394
|
+
let report;
|
|
18395
|
+
try {
|
|
18396
|
+
report = await runBenchmark({
|
|
18397
|
+
suite,
|
|
18398
|
+
grade,
|
|
18399
|
+
config,
|
|
18400
|
+
cliVersion: CLI_VERSION,
|
|
18401
|
+
toolNames,
|
|
18402
|
+
nodeBin: process.execPath,
|
|
18403
|
+
wstackEntry,
|
|
18404
|
+
limit,
|
|
18405
|
+
onProgress: (msg) => deps.renderer.write(color.dim(msg) + "\n")
|
|
18406
|
+
});
|
|
18407
|
+
} catch (err) {
|
|
18408
|
+
deps.renderer.writeError(err instanceof Error ? err.message : String(err));
|
|
18409
|
+
return 1;
|
|
18410
|
+
}
|
|
18411
|
+
await writeJsonArtifacts(outDir, report);
|
|
18412
|
+
const md = renderMarkdownReport(report);
|
|
18413
|
+
await fsp5.writeFile(path39.join(outDir, "report.md"), md, "utf8");
|
|
18414
|
+
deps.renderer.write("\n" + md + "\n");
|
|
18415
|
+
if (isSwebench) {
|
|
18416
|
+
for (const cell of config.cells) {
|
|
18417
|
+
const preds = await collectCellPredictions(predictionsDir, cell.label);
|
|
18418
|
+
if (preds.length === 0) continue;
|
|
18419
|
+
const file = await writePredictionsJsonl(outDir, cell.label, preds);
|
|
18420
|
+
deps.renderer.writeInfo(`Predictions for "${cell.label}" \u2192 ${file}`);
|
|
18421
|
+
}
|
|
18422
|
+
deps.renderer.writeInfo(
|
|
18423
|
+
"Grade with the official SWE-bench harness: python -m swebench.harness.run_evaluation --predictions_path <file> --run_id <id>"
|
|
18424
|
+
);
|
|
18425
|
+
}
|
|
18426
|
+
deps.renderer.writeInfo(`Report written to ${path39.join(outDir, "report.md")}`);
|
|
18427
|
+
return 0;
|
|
18428
|
+
}
|
|
18429
|
+
async function benchReport(args, deps) {
|
|
18430
|
+
const dir = args.find((a) => !a.startsWith("-"));
|
|
18431
|
+
if (!dir) {
|
|
18432
|
+
deps.renderer.writeError("Usage: wstack bench report <run-directory>");
|
|
18433
|
+
return 1;
|
|
18434
|
+
}
|
|
18435
|
+
const outDir = path39.resolve(deps.cwd, dir);
|
|
18436
|
+
let summary;
|
|
18437
|
+
try {
|
|
18438
|
+
summary = await readSummary(outDir);
|
|
18439
|
+
} catch (err) {
|
|
18440
|
+
deps.renderer.writeError(
|
|
18441
|
+
`cannot read summary.json in ${outDir}: ${err instanceof Error ? err.message : String(err)}`
|
|
18442
|
+
);
|
|
18443
|
+
return 1;
|
|
18444
|
+
}
|
|
18445
|
+
const md = renderMarkdownReport(summary);
|
|
18446
|
+
await fsp5.writeFile(path39.join(outDir, "report.md"), md, "utf8");
|
|
18447
|
+
deps.renderer.write("\n" + md + "\n");
|
|
18448
|
+
return 0;
|
|
18449
|
+
}
|
|
18450
|
+
async function benchList(_args, deps) {
|
|
18451
|
+
deps.renderer.write(color.bold("Suites\n"));
|
|
18452
|
+
deps.renderer.write(
|
|
18453
|
+
" polyglot " + color.dim("Aider polyglot (edit accuracy) \u2014 Phase 1, Docker-free\n")
|
|
18454
|
+
);
|
|
18455
|
+
deps.renderer.write(
|
|
18456
|
+
" swebench " + color.dim("SWE-bench Verified (end-to-end) \u2014 Phase 2, Docker-gated\n")
|
|
18457
|
+
);
|
|
18458
|
+
const modelsPath = flagStr(deps, "models");
|
|
18459
|
+
if (modelsPath) {
|
|
18460
|
+
try {
|
|
18461
|
+
const config = await loadBenchConfig(path39.resolve(deps.cwd, modelsPath));
|
|
18462
|
+
deps.renderer.write("\n" + color.bold("Model cells\n"));
|
|
18463
|
+
for (const cell of config.cells) {
|
|
18464
|
+
deps.renderer.write(
|
|
18465
|
+
` ${cell.label.padEnd(16)} ${color.dim(`${cell.provider}/${cell.model}`)}
|
|
18466
|
+
`
|
|
18467
|
+
);
|
|
18468
|
+
}
|
|
18469
|
+
const fp = reportHeaderLine({
|
|
18470
|
+
cliVersion: CLI_VERSION,
|
|
18471
|
+
toolNames: deps.toolRegistry?.list().map((t) => t.name) ?? [],
|
|
18472
|
+
maxIterations: config.maxIterations,
|
|
18473
|
+
yolo: true,
|
|
18474
|
+
subsetId: "(computed at run time)",
|
|
18475
|
+
hash: "(computed at run time)"
|
|
18476
|
+
});
|
|
18477
|
+
deps.renderer.write("\n" + color.dim(`Harness: ${fp}`) + "\n");
|
|
18478
|
+
} catch (err) {
|
|
18479
|
+
deps.renderer.writeError(err instanceof Error ? err.message : String(err));
|
|
18480
|
+
return 1;
|
|
18481
|
+
}
|
|
18482
|
+
}
|
|
18483
|
+
return 0;
|
|
18484
|
+
}
|
|
18251
18485
|
var diagCmd = async (_args, deps) => {
|
|
18252
18486
|
const cfg = deps.config;
|
|
18253
18487
|
const age = await deps.modelsRegistry.ageSeconds();
|
|
@@ -18341,7 +18575,7 @@ var doctorCmd = async (_args, deps) => {
|
|
|
18341
18575
|
}
|
|
18342
18576
|
try {
|
|
18343
18577
|
await fsp5.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
18344
|
-
const probe =
|
|
18578
|
+
const probe = path39.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
18345
18579
|
await fsp5.writeFile(probe, "");
|
|
18346
18580
|
await fsp5.unlink(probe);
|
|
18347
18581
|
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
@@ -18444,8 +18678,8 @@ var exportCmd = async (args, deps) => {
|
|
|
18444
18678
|
return 1;
|
|
18445
18679
|
}
|
|
18446
18680
|
if (output) {
|
|
18447
|
-
await fsp5.mkdir(
|
|
18448
|
-
await fsp5.writeFile(
|
|
18681
|
+
await fsp5.mkdir(path39.dirname(path39.resolve(deps.cwd, output)), { recursive: true });
|
|
18682
|
+
await fsp5.writeFile(path39.resolve(deps.cwd, output), rendered, "utf8");
|
|
18449
18683
|
deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
|
|
18450
18684
|
`);
|
|
18451
18685
|
} else {
|
|
@@ -18521,8 +18755,8 @@ var initCmd = async (_args, deps) => {
|
|
|
18521
18755
|
const vault = new DefaultSecretVault({ keyFile: deps.paths.secretsKey });
|
|
18522
18756
|
const encrypted = encryptConfigSecrets$1(config, vault);
|
|
18523
18757
|
await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
18524
|
-
await fsp5.mkdir(
|
|
18525
|
-
const agentsFile =
|
|
18758
|
+
await fsp5.mkdir(path39.join(deps.projectRoot, ".wrongstack"), { recursive: true });
|
|
18759
|
+
const agentsFile = path39.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
|
|
18526
18760
|
const projectFacts = await detectProjectFacts(deps.projectRoot);
|
|
18527
18761
|
await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
|
|
18528
18762
|
deps.renderer.writeInfo(`Wrote ${deps.paths.globalConfig}`);
|
|
@@ -18661,8 +18895,8 @@ async function serveMcpStdio(deps) {
|
|
|
18661
18895
|
log(
|
|
18662
18896
|
`wrongstack MCP server ready at ${handle2.url} \u2014 exposing ${allowed.length} tool(s) (${mode})${token ? " [token auth]" : ""}.`
|
|
18663
18897
|
);
|
|
18664
|
-
await new Promise((
|
|
18665
|
-
const stop = () =>
|
|
18898
|
+
await new Promise((resolve11) => {
|
|
18899
|
+
const stop = () => resolve11();
|
|
18666
18900
|
process.once("SIGINT", stop);
|
|
18667
18901
|
process.once("SIGTERM", stop);
|
|
18668
18902
|
});
|
|
@@ -19885,7 +20119,7 @@ var usageCmd = async (_args, deps) => {
|
|
|
19885
20119
|
return 0;
|
|
19886
20120
|
};
|
|
19887
20121
|
var projectsCmd = async (_args, deps) => {
|
|
19888
|
-
const projectsRoot =
|
|
20122
|
+
const projectsRoot = path39.join(deps.paths.globalRoot, "projects");
|
|
19889
20123
|
try {
|
|
19890
20124
|
const entries = await fsp5.readdir(projectsRoot);
|
|
19891
20125
|
if (entries.length === 0) {
|
|
@@ -19895,7 +20129,7 @@ var projectsCmd = async (_args, deps) => {
|
|
|
19895
20129
|
for (const hash of entries) {
|
|
19896
20130
|
try {
|
|
19897
20131
|
const meta = JSON.parse(
|
|
19898
|
-
await fsp5.readFile(
|
|
20132
|
+
await fsp5.readFile(path39.join(projectsRoot, hash, "meta.json"), "utf8")
|
|
19899
20133
|
);
|
|
19900
20134
|
deps.renderer.write(
|
|
19901
20135
|
` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
|
|
@@ -20344,7 +20578,7 @@ function findSessionId(args) {
|
|
|
20344
20578
|
var rewindCmd = async (args, deps) => {
|
|
20345
20579
|
const flags = parseRewindFlags(args);
|
|
20346
20580
|
const wpaths = resolveWstackPaths({ projectRoot: deps.projectRoot });
|
|
20347
|
-
const sessionsDir =
|
|
20581
|
+
const sessionsDir = path39.join(wpaths.globalRoot, "sessions");
|
|
20348
20582
|
const rewind = new DefaultSessionRewinder(sessionsDir, deps.projectRoot);
|
|
20349
20583
|
let sessionId = findSessionId(args);
|
|
20350
20584
|
if (!sessionId) {
|
|
@@ -20489,7 +20723,7 @@ async function listFleetRuns(deps) {
|
|
|
20489
20723
|
}
|
|
20490
20724
|
const runs = [];
|
|
20491
20725
|
for (const id of entries) {
|
|
20492
|
-
const runDir =
|
|
20726
|
+
const runDir = path39.join(deps.paths.projectSessions, id);
|
|
20493
20727
|
let stat7;
|
|
20494
20728
|
try {
|
|
20495
20729
|
stat7 = await fsp5.stat(runDir);
|
|
@@ -20502,17 +20736,17 @@ async function listFleetRuns(deps) {
|
|
|
20502
20736
|
let subagentCount = 0;
|
|
20503
20737
|
let subagentsDir;
|
|
20504
20738
|
try {
|
|
20505
|
-
await fsp5.access(
|
|
20739
|
+
await fsp5.access(path39.join(runDir, "fleet.json"));
|
|
20506
20740
|
manifest = true;
|
|
20507
20741
|
} catch {
|
|
20508
20742
|
}
|
|
20509
20743
|
try {
|
|
20510
|
-
await fsp5.access(
|
|
20744
|
+
await fsp5.access(path39.join(runDir, "checkpoint.json"));
|
|
20511
20745
|
checkpoint = true;
|
|
20512
20746
|
} catch {
|
|
20513
20747
|
}
|
|
20514
20748
|
try {
|
|
20515
|
-
subagentsDir =
|
|
20749
|
+
subagentsDir = path39.join(runDir, "subagents");
|
|
20516
20750
|
const files = await fsp5.readdir(subagentsDir);
|
|
20517
20751
|
subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
|
|
20518
20752
|
} catch {
|
|
@@ -20539,7 +20773,7 @@ async function listFleetRuns(deps) {
|
|
|
20539
20773
|
return 0;
|
|
20540
20774
|
}
|
|
20541
20775
|
async function showFleetRun(runId, deps) {
|
|
20542
|
-
const runDir =
|
|
20776
|
+
const runDir = path39.join(deps.paths.projectSessions, runId);
|
|
20543
20777
|
let stat7;
|
|
20544
20778
|
try {
|
|
20545
20779
|
stat7 = await fsp5.stat(runDir);
|
|
@@ -20556,7 +20790,7 @@ async function showFleetRun(runId, deps) {
|
|
|
20556
20790
|
deps.renderer.write(color.bold(`
|
|
20557
20791
|
Fleet Run: ${runId}
|
|
20558
20792
|
`) + "\n");
|
|
20559
|
-
const manifestPath =
|
|
20793
|
+
const manifestPath = path39.join(runDir, "fleet.json");
|
|
20560
20794
|
let manifestData = null;
|
|
20561
20795
|
try {
|
|
20562
20796
|
manifestData = await fsp5.readFile(manifestPath, "utf8");
|
|
@@ -20574,7 +20808,7 @@ Fleet Run: ${runId}
|
|
|
20574
20808
|
deps.renderer.write(` ${color.dim("\u25CB")} fleet.json \u2014 not found
|
|
20575
20809
|
`);
|
|
20576
20810
|
}
|
|
20577
|
-
const checkpointPath =
|
|
20811
|
+
const checkpointPath = path39.join(runDir, "checkpoint.json");
|
|
20578
20812
|
let checkpointData = null;
|
|
20579
20813
|
try {
|
|
20580
20814
|
checkpointData = await fsp5.readFile(checkpointPath, "utf8");
|
|
@@ -20621,7 +20855,7 @@ Fleet Run: ${runId}
|
|
|
20621
20855
|
} catch {
|
|
20622
20856
|
}
|
|
20623
20857
|
}
|
|
20624
|
-
const subagentsDir =
|
|
20858
|
+
const subagentsDir = path39.join(runDir, "subagents");
|
|
20625
20859
|
let subagentFiles = [];
|
|
20626
20860
|
try {
|
|
20627
20861
|
subagentFiles = await fsp5.readdir(subagentsDir);
|
|
@@ -20633,7 +20867,7 @@ Fleet Run: ${runId}
|
|
|
20633
20867
|
Subagent transcripts (${subagentFiles.length}):
|
|
20634
20868
|
`);
|
|
20635
20869
|
for (const f of subagentFiles.sort()) {
|
|
20636
|
-
const filePath =
|
|
20870
|
+
const filePath = path39.join(subagentsDir, f);
|
|
20637
20871
|
let size;
|
|
20638
20872
|
try {
|
|
20639
20873
|
const s = await fsp5.stat(filePath);
|
|
@@ -20650,7 +20884,7 @@ Fleet Run: ${runId}
|
|
|
20650
20884
|
${color.dim("\u25CB")} No subagent transcripts
|
|
20651
20885
|
`);
|
|
20652
20886
|
}
|
|
20653
|
-
const sharedDir =
|
|
20887
|
+
const sharedDir = path39.join(runDir, "shared");
|
|
20654
20888
|
try {
|
|
20655
20889
|
const files = await fsp5.readdir(sharedDir);
|
|
20656
20890
|
deps.renderer.write(`
|
|
@@ -20834,7 +21068,7 @@ var updateCmd = async (args, deps) => {
|
|
|
20834
21068
|
deps.renderer.write(`Updating wrongstack from v${info.current} to v${info.latest}...
|
|
20835
21069
|
`);
|
|
20836
21070
|
try {
|
|
20837
|
-
const result = await new Promise((
|
|
21071
|
+
const result = await new Promise((resolve11, reject) => {
|
|
20838
21072
|
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
20839
21073
|
const child = spawn(npmCommand, ["install", "-g", "wrongstack@latest"], {
|
|
20840
21074
|
cwd,
|
|
@@ -20847,7 +21081,7 @@ var updateCmd = async (args, deps) => {
|
|
|
20847
21081
|
_stderr += d;
|
|
20848
21082
|
});
|
|
20849
21083
|
child.on("error", reject);
|
|
20850
|
-
child.on("close", (code) =>
|
|
21084
|
+
child.on("close", (code) => resolve11({ code: code ?? 0 }));
|
|
20851
21085
|
});
|
|
20852
21086
|
if (result.code === 0) {
|
|
20853
21087
|
deps.renderer.write(
|
|
@@ -20958,7 +21192,8 @@ var subcommands = {
|
|
|
20958
21192
|
help: helpCmd,
|
|
20959
21193
|
projects: projectsCmd,
|
|
20960
21194
|
modeldiag: modeldiagCmd,
|
|
20961
|
-
quick: quickCmd
|
|
21195
|
+
quick: quickCmd,
|
|
21196
|
+
bench: benchCmd
|
|
20962
21197
|
};
|
|
20963
21198
|
|
|
20964
21199
|
// src/boot.ts
|
|
@@ -20976,7 +21211,7 @@ function resolveBundledSkillsDir() {
|
|
|
20976
21211
|
try {
|
|
20977
21212
|
const req2 = createRequire(import.meta.url);
|
|
20978
21213
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
20979
|
-
return
|
|
21214
|
+
return path39.join(path39.dirname(corePkg), "skills");
|
|
20980
21215
|
} catch {
|
|
20981
21216
|
return void 0;
|
|
20982
21217
|
}
|
|
@@ -21197,7 +21432,7 @@ async function boot(argv) {
|
|
|
21197
21432
|
const created = await registerProjectAtBoot({ projectRoot, cwd, wpaths });
|
|
21198
21433
|
if (created && isInteractiveTTY) {
|
|
21199
21434
|
renderer.write(
|
|
21200
|
-
color.dim(` \u2713 Registered "${
|
|
21435
|
+
color.dim(` \u2713 Registered "${path39.basename(projectRoot)}" in projects.json.
|
|
21201
21436
|
`)
|
|
21202
21437
|
);
|
|
21203
21438
|
}
|
|
@@ -21277,7 +21512,7 @@ async function boot(argv) {
|
|
|
21277
21512
|
} catch {
|
|
21278
21513
|
}
|
|
21279
21514
|
printLaunchHints(renderer, flags, {
|
|
21280
|
-
cursorFile:
|
|
21515
|
+
cursorFile: path39.join(wpaths.cacheDir, "hint-cursor")
|
|
21281
21516
|
});
|
|
21282
21517
|
} else {
|
|
21283
21518
|
const effectiveChoices = config.launch ? {
|
|
@@ -21315,7 +21550,7 @@ async function boot(argv) {
|
|
|
21315
21550
|
}
|
|
21316
21551
|
async function checkGitInCwd(opts) {
|
|
21317
21552
|
const { cwd, renderer, reader } = opts;
|
|
21318
|
-
const cwdGit =
|
|
21553
|
+
const cwdGit = path39.join(cwd, ".git");
|
|
21319
21554
|
let hasCwdGit = false;
|
|
21320
21555
|
try {
|
|
21321
21556
|
await fsp5.access(cwdGit);
|
|
@@ -21332,7 +21567,7 @@ async function checkGitInCwd(opts) {
|
|
|
21332
21567
|
if (answer === "y" || answer === "yes") {
|
|
21333
21568
|
try {
|
|
21334
21569
|
const { spawn: spawn6 } = await import('child_process');
|
|
21335
|
-
await new Promise((
|
|
21570
|
+
await new Promise((resolve11, reject) => {
|
|
21336
21571
|
const child = spawn6("git", ["init"], {
|
|
21337
21572
|
cwd,
|
|
21338
21573
|
signal: AbortSignal.timeout(1e4),
|
|
@@ -21341,7 +21576,7 @@ async function checkGitInCwd(opts) {
|
|
|
21341
21576
|
child.on("error", reject);
|
|
21342
21577
|
child.on(
|
|
21343
21578
|
"close",
|
|
21344
|
-
(code) => code === 0 ?
|
|
21579
|
+
(code) => code === 0 ? resolve11() : reject(new Error(`git init failed with ${code}`))
|
|
21345
21580
|
);
|
|
21346
21581
|
});
|
|
21347
21582
|
renderer.write(` ${color.green("\u2713")} Git repository initialized
|
|
@@ -21355,10 +21590,10 @@ async function checkGitInCwd(opts) {
|
|
|
21355
21590
|
}
|
|
21356
21591
|
}
|
|
21357
21592
|
}
|
|
21358
|
-
const parentDir =
|
|
21593
|
+
const parentDir = path39.dirname(cwd);
|
|
21359
21594
|
if (parentDir !== cwd) {
|
|
21360
21595
|
try {
|
|
21361
|
-
await fsp5.access(
|
|
21596
|
+
await fsp5.access(path39.join(parentDir, ".git"));
|
|
21362
21597
|
renderer.write(
|
|
21363
21598
|
` ${color.dim("\u2139")} A ${color.bold(".git")} repo exists in the parent directory: ${color.dim(parentDir)}
|
|
21364
21599
|
`
|
|
@@ -21370,7 +21605,7 @@ async function checkGitInCwd(opts) {
|
|
|
21370
21605
|
async function registerProjectAtBoot(opts) {
|
|
21371
21606
|
const { projectRoot, cwd, wpaths } = opts;
|
|
21372
21607
|
const manifest = await loadManifest(wpaths.globalConfig);
|
|
21373
|
-
const existed = manifest.projects.some((p) =>
|
|
21608
|
+
const existed = manifest.projects.some((p) => path39.resolve(p.root) === path39.resolve(projectRoot));
|
|
21374
21609
|
await touchProjectInManifest({
|
|
21375
21610
|
projectRoot,
|
|
21376
21611
|
globalConfigPath: wpaths.globalConfig,
|
|
@@ -21571,7 +21806,7 @@ function resolveBundledSkillsDir2() {
|
|
|
21571
21806
|
try {
|
|
21572
21807
|
const req2 = createRequire(import.meta.url);
|
|
21573
21808
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
21574
|
-
return
|
|
21809
|
+
return path39.join(path39.dirname(corePkg), "skills");
|
|
21575
21810
|
} catch {
|
|
21576
21811
|
return void 0;
|
|
21577
21812
|
}
|
|
@@ -21735,22 +21970,22 @@ function renderFleetLine(states, now, columns, version) {
|
|
|
21735
21970
|
const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
21736
21971
|
if (visible.length > max) {
|
|
21737
21972
|
let count = 0;
|
|
21738
|
-
|
|
21973
|
+
const out = [];
|
|
21739
21974
|
let i = 0;
|
|
21740
21975
|
while (i < line.length && count < max - 1) {
|
|
21741
21976
|
if (line[i] === "\x1B") {
|
|
21742
21977
|
const end = line.indexOf("m", i);
|
|
21743
21978
|
if (end !== -1) {
|
|
21744
|
-
out
|
|
21979
|
+
out.push(line.slice(i, end + 1));
|
|
21745
21980
|
i = end + 1;
|
|
21746
21981
|
continue;
|
|
21747
21982
|
}
|
|
21748
21983
|
}
|
|
21749
|
-
|
|
21984
|
+
if (line[i] !== void 0) out.push(line[i]);
|
|
21750
21985
|
count++;
|
|
21751
21986
|
i++;
|
|
21752
21987
|
}
|
|
21753
|
-
line = out + "\u2026";
|
|
21988
|
+
line = out.join("") + "\u2026";
|
|
21754
21989
|
}
|
|
21755
21990
|
return line;
|
|
21756
21991
|
}
|
|
@@ -22059,7 +22294,7 @@ async function runRepl(opts) {
|
|
|
22059
22294
|
clientMailbox.registerClient({
|
|
22060
22295
|
clientId,
|
|
22061
22296
|
sessionId: replProjectRoot,
|
|
22062
|
-
name: `REPL [${
|
|
22297
|
+
name: `REPL [${path39.basename(replProjectRoot)}]`,
|
|
22063
22298
|
source: "repl",
|
|
22064
22299
|
pid: process.pid
|
|
22065
22300
|
}).then(() => {
|
|
@@ -22127,7 +22362,7 @@ async function runRepl(opts) {
|
|
|
22127
22362
|
`[eternal] ${err instanceof Error ? err.message : String(err)}`
|
|
22128
22363
|
);
|
|
22129
22364
|
}
|
|
22130
|
-
await new Promise((
|
|
22365
|
+
await new Promise((resolve11) => setTimeout(resolve11, 250));
|
|
22131
22366
|
continue;
|
|
22132
22367
|
}
|
|
22133
22368
|
} else if (opts.getAutonomy?.() === "eternal-parallel") {
|
|
@@ -22203,7 +22438,7 @@ async function runRepl(opts) {
|
|
|
22203
22438
|
`[parallel] ${err instanceof Error ? err.message : String(err)}`
|
|
22204
22439
|
);
|
|
22205
22440
|
}
|
|
22206
|
-
await new Promise((
|
|
22441
|
+
await new Promise((resolve11) => setTimeout(resolve11, 250));
|
|
22207
22442
|
continue;
|
|
22208
22443
|
}
|
|
22209
22444
|
}
|
|
@@ -22770,12 +23005,12 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
|
|
|
22770
23005
|
let interval;
|
|
22771
23006
|
let lastTickedSecond = sec + 1;
|
|
22772
23007
|
let onAbort;
|
|
22773
|
-
return new Promise((
|
|
22774
|
-
onAbort = () =>
|
|
23008
|
+
return new Promise((resolve11) => {
|
|
23009
|
+
onAbort = () => resolve11(false);
|
|
22775
23010
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
22776
23011
|
interval = setInterval(() => {
|
|
22777
23012
|
if (signal.aborted) {
|
|
22778
|
-
|
|
23013
|
+
resolve11(false);
|
|
22779
23014
|
return;
|
|
22780
23015
|
}
|
|
22781
23016
|
const elapsed = Date.now() - start;
|
|
@@ -22783,7 +23018,7 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
|
|
|
22783
23018
|
if (remaining <= 0) {
|
|
22784
23019
|
opts.renderer.write(color.dim(` \u21B3 ${truncated}
|
|
22785
23020
|
`));
|
|
22786
|
-
|
|
23021
|
+
resolve11(true);
|
|
22787
23022
|
return;
|
|
22788
23023
|
}
|
|
22789
23024
|
if (opts.onCountdownTick && remaining !== lastTickedSecond) {
|
|
@@ -22794,7 +23029,7 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
|
|
|
22794
23029
|
opts.renderer.write(
|
|
22795
23030
|
color.yellow(" \u21B3 Countdown cancelled \u2014 switching to manual mode\n")
|
|
22796
23031
|
);
|
|
22797
|
-
|
|
23032
|
+
resolve11(false);
|
|
22798
23033
|
return;
|
|
22799
23034
|
}
|
|
22800
23035
|
} catch {
|
|
@@ -22940,6 +23175,23 @@ async function execute(deps) {
|
|
|
22940
23175
|
restoredToolCalls,
|
|
22941
23176
|
needsSetup
|
|
22942
23177
|
} = deps;
|
|
23178
|
+
const rootTraceId = context.traceId;
|
|
23179
|
+
const storageLog = (event, payload) => {
|
|
23180
|
+
const traceId = payload.traceId ?? rootTraceId;
|
|
23181
|
+
console.warn(JSON.stringify({
|
|
23182
|
+
level: "info",
|
|
23183
|
+
event,
|
|
23184
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23185
|
+
traceId,
|
|
23186
|
+
...payload
|
|
23187
|
+
}));
|
|
23188
|
+
};
|
|
23189
|
+
const onStorageRead = (...args) => storageLog("storage.read", args[0]);
|
|
23190
|
+
const onStorageWrite = (...args) => storageLog("storage.write", args[0]);
|
|
23191
|
+
const onStorageError = (...args) => storageLog("storage.error", args[0]);
|
|
23192
|
+
events.on("storage.read", onStorageRead);
|
|
23193
|
+
events.on("storage.write", onStorageWrite);
|
|
23194
|
+
events.on("storage.error", onStorageError);
|
|
22943
23195
|
let pendingChimeraWork;
|
|
22944
23196
|
events.onPattern("chimera.review_needed", (_event, payload) => {
|
|
22945
23197
|
const p = payload;
|
|
@@ -23385,7 +23637,7 @@ async function execute(deps) {
|
|
|
23385
23637
|
const toWrite = targetPath === wpaths.globalConfig ? decrypted : filterSafeForProject(decrypted);
|
|
23386
23638
|
const encrypted = encryptConfigSecrets(toWrite, noOpVault);
|
|
23387
23639
|
if (targetPath !== wpaths.globalConfig) {
|
|
23388
|
-
await fsp5.mkdir(
|
|
23640
|
+
await fsp5.mkdir(path39.dirname(targetPath), { recursive: true });
|
|
23389
23641
|
}
|
|
23390
23642
|
await atomicWrite(targetPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
23391
23643
|
configStore.update({
|
|
@@ -23462,7 +23714,7 @@ async function execute(deps) {
|
|
|
23462
23714
|
agentsMonitorController,
|
|
23463
23715
|
getLiveSessions: async () => {
|
|
23464
23716
|
const { SessionRegistry } = await import('@wrongstack/core');
|
|
23465
|
-
const globalRoot =
|
|
23717
|
+
const globalRoot = path39.dirname(wpaths.globalConfig);
|
|
23466
23718
|
const registry = new SessionRegistry(globalRoot);
|
|
23467
23719
|
const sessions = await registry.list();
|
|
23468
23720
|
return sessions.filter((s) => s.status !== "stale").map((s) => ({
|
|
@@ -23537,31 +23789,35 @@ async function execute(deps) {
|
|
|
23537
23789
|
const metaMode = context.meta?.["mode"];
|
|
23538
23790
|
return typeof metaMode === "string" ? metaMode : modeId ?? "default";
|
|
23539
23791
|
},
|
|
23540
|
-
registerDebugStreamCallback: (cb) => {
|
|
23541
|
-
|
|
23542
|
-
|
|
23792
|
+
registerDebugStreamCallback: async (cb) => {
|
|
23793
|
+
try {
|
|
23794
|
+
const { setDebugStreamCallback } = await import('@wrongstack/providers');
|
|
23795
|
+
setDebugStreamCallback(cb);
|
|
23796
|
+
} catch (err) {
|
|
23797
|
+
console.error(
|
|
23543
23798
|
JSON.stringify({
|
|
23544
23799
|
level: "error",
|
|
23545
23800
|
event: "execution.debug_stream_register_failed",
|
|
23546
23801
|
message: err instanceof Error ? err.message : String(err),
|
|
23547
23802
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
23548
23803
|
})
|
|
23549
|
-
)
|
|
23550
|
-
|
|
23804
|
+
);
|
|
23805
|
+
}
|
|
23551
23806
|
},
|
|
23552
|
-
restoreDebugStreamCallback: () => {
|
|
23553
|
-
|
|
23554
|
-
|
|
23555
|
-
|
|
23556
|
-
|
|
23807
|
+
restoreDebugStreamCallback: async () => {
|
|
23808
|
+
try {
|
|
23809
|
+
const { setDebugStreamCallback, defaultDebugStreamCallback } = await import('@wrongstack/providers');
|
|
23810
|
+
setDebugStreamCallback(defaultDebugStreamCallback);
|
|
23811
|
+
} catch (err) {
|
|
23812
|
+
console.error(
|
|
23557
23813
|
JSON.stringify({
|
|
23558
23814
|
level: "error",
|
|
23559
23815
|
event: "execution.debug_stream_restore_failed",
|
|
23560
23816
|
message: err instanceof Error ? err.message : String(err),
|
|
23561
23817
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
23562
23818
|
})
|
|
23563
|
-
)
|
|
23564
|
-
|
|
23819
|
+
);
|
|
23820
|
+
}
|
|
23565
23821
|
},
|
|
23566
23822
|
restoredMessages,
|
|
23567
23823
|
restoredToolCalls,
|
|
@@ -23587,7 +23843,7 @@ async function execute(deps) {
|
|
|
23587
23843
|
if (!sessionStore) return null;
|
|
23588
23844
|
try {
|
|
23589
23845
|
const { SessionRegistry } = await import('@wrongstack/core');
|
|
23590
|
-
const registry = new SessionRegistry(
|
|
23846
|
+
const registry = new SessionRegistry(path39.dirname(wpaths.globalConfig));
|
|
23591
23847
|
const live = (await registry.list()).find(
|
|
23592
23848
|
(s) => s.sessionId === sessionId && s.status !== "stale" && s.pid !== process.pid
|
|
23593
23849
|
);
|
|
@@ -23616,12 +23872,14 @@ async function execute(deps) {
|
|
|
23616
23872
|
if (oldWriter && oldWriter !== resumed.writer) {
|
|
23617
23873
|
const endedUsage = tokenCounter.total();
|
|
23618
23874
|
void (async () => {
|
|
23875
|
+
let appendOk = false;
|
|
23619
23876
|
try {
|
|
23620
23877
|
await oldWriter.append({
|
|
23621
23878
|
type: "session_end",
|
|
23622
23879
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23623
23880
|
usage: endedUsage
|
|
23624
23881
|
});
|
|
23882
|
+
appendOk = true;
|
|
23625
23883
|
} catch (err) {
|
|
23626
23884
|
console.error(
|
|
23627
23885
|
JSON.stringify({
|
|
@@ -23632,32 +23890,50 @@ async function execute(deps) {
|
|
|
23632
23890
|
})
|
|
23633
23891
|
);
|
|
23634
23892
|
}
|
|
23635
|
-
|
|
23636
|
-
|
|
23637
|
-
|
|
23638
|
-
|
|
23639
|
-
|
|
23640
|
-
|
|
23641
|
-
|
|
23642
|
-
|
|
23643
|
-
|
|
23644
|
-
|
|
23645
|
-
|
|
23893
|
+
if (appendOk) {
|
|
23894
|
+
try {
|
|
23895
|
+
await oldWriter.close();
|
|
23896
|
+
} catch (err) {
|
|
23897
|
+
console.error(
|
|
23898
|
+
JSON.stringify({
|
|
23899
|
+
level: "error",
|
|
23900
|
+
event: "execution.session_close_failed",
|
|
23901
|
+
message: err instanceof Error ? err.message : String(err),
|
|
23902
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
23903
|
+
})
|
|
23904
|
+
);
|
|
23905
|
+
}
|
|
23646
23906
|
}
|
|
23647
23907
|
})();
|
|
23648
23908
|
}
|
|
23649
23909
|
agent.ctx.session = resumed.writer;
|
|
23650
23910
|
tokenCounter.reset();
|
|
23651
|
-
void
|
|
23652
|
-
|
|
23653
|
-
|
|
23654
|
-
|
|
23655
|
-
|
|
23656
|
-
|
|
23657
|
-
|
|
23658
|
-
|
|
23659
|
-
|
|
23660
|
-
|
|
23911
|
+
void (async () => {
|
|
23912
|
+
try {
|
|
23913
|
+
await recoveryLock.clear();
|
|
23914
|
+
} catch (err) {
|
|
23915
|
+
console.error(
|
|
23916
|
+
JSON.stringify({
|
|
23917
|
+
level: "warn",
|
|
23918
|
+
event: "execution.recovery_lock_clear_failed",
|
|
23919
|
+
message: err instanceof Error ? err.message : String(err),
|
|
23920
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
23921
|
+
})
|
|
23922
|
+
);
|
|
23923
|
+
}
|
|
23924
|
+
try {
|
|
23925
|
+
await recoveryLock.write(resumed.writer.id);
|
|
23926
|
+
} catch (err) {
|
|
23927
|
+
console.error(
|
|
23928
|
+
JSON.stringify({
|
|
23929
|
+
level: "error",
|
|
23930
|
+
event: "execution.recovery_lock_update_failed",
|
|
23931
|
+
message: err instanceof Error ? err.message : String(err),
|
|
23932
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
23933
|
+
})
|
|
23934
|
+
);
|
|
23935
|
+
}
|
|
23936
|
+
})();
|
|
23661
23937
|
const { replaySessionEvents } = await import('@wrongstack/tui');
|
|
23662
23938
|
const entries = replaySessionEvents(
|
|
23663
23939
|
resumed.data.events,
|
|
@@ -23705,7 +23981,7 @@ async function execute(deps) {
|
|
|
23705
23981
|
if (slug === "new-session") {
|
|
23706
23982
|
pendingProjectSwitch = {
|
|
23707
23983
|
root: projectRoot,
|
|
23708
|
-
name:
|
|
23984
|
+
name: path39.basename(projectRoot) || projectRoot
|
|
23709
23985
|
};
|
|
23710
23986
|
}
|
|
23711
23987
|
return;
|
|
@@ -23766,13 +24042,13 @@ ${parts.join("\n")}
|
|
|
23766
24042
|
const { root, name, resumeSessionId } = pendingProjectSwitch;
|
|
23767
24043
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
23768
24044
|
const { spawn: spawn6 } = await import('child_process');
|
|
23769
|
-
const { createRequire:
|
|
24045
|
+
const { createRequire: createRequire8 } = await import('module');
|
|
23770
24046
|
let cliPath;
|
|
23771
24047
|
try {
|
|
23772
|
-
const req2 =
|
|
24048
|
+
const req2 = createRequire8(import.meta.url);
|
|
23773
24049
|
const pkgPath = req2.resolve("@wrongstack/cli/package.json");
|
|
23774
|
-
const pkgDir =
|
|
23775
|
-
cliPath =
|
|
24050
|
+
const pkgDir = path39.dirname(pkgPath);
|
|
24051
|
+
cliPath = path39.join(pkgDir, "dist", "index.js");
|
|
23776
24052
|
await fsp5.access(cliPath);
|
|
23777
24053
|
} catch {
|
|
23778
24054
|
cliPath = process.argv[1] ?? "";
|
|
@@ -23846,12 +24122,12 @@ ${parts.join("\n")}
|
|
|
23846
24122
|
)
|
|
23847
24123
|
);
|
|
23848
24124
|
renderer.writeInfo(color.dim(" Press Ctrl+C in this terminal to stop the WebUI server.\n"));
|
|
23849
|
-
const webuiExit = new Promise((
|
|
24125
|
+
const webuiExit = new Promise((resolve11) => {
|
|
23850
24126
|
const onSigint = () => {
|
|
23851
24127
|
renderer.setSilent(false);
|
|
23852
24128
|
renderer.write("\n");
|
|
23853
24129
|
renderer.writeInfo(color.yellow(" Shutting down WebUI server\u2026"));
|
|
23854
|
-
|
|
24130
|
+
resolve11(0);
|
|
23855
24131
|
};
|
|
23856
24132
|
process.on("SIGINT", onSigint);
|
|
23857
24133
|
process.on("SIGTERM", onSigint);
|
|
@@ -23859,13 +24135,13 @@ ${parts.join("\n")}
|
|
|
23859
24135
|
renderer.setSilent(false);
|
|
23860
24136
|
process.off("SIGINT", onSigint);
|
|
23861
24137
|
process.off("SIGTERM", onSigint);
|
|
23862
|
-
|
|
24138
|
+
resolve11(0);
|
|
23863
24139
|
}).catch((err) => {
|
|
23864
24140
|
renderer.setSilent(false);
|
|
23865
24141
|
process.off("SIGINT", onSigint);
|
|
23866
24142
|
process.off("SIGTERM", onSigint);
|
|
23867
24143
|
console.debug(`[execution] webui error: ${err}`);
|
|
23868
|
-
|
|
24144
|
+
resolve11(1);
|
|
23869
24145
|
});
|
|
23870
24146
|
});
|
|
23871
24147
|
code = await webuiExit;
|
|
@@ -23880,7 +24156,7 @@ ${parts.join("\n")}
|
|
|
23880
24156
|
supportsVision,
|
|
23881
24157
|
attachments,
|
|
23882
24158
|
effectiveMaxContext,
|
|
23883
|
-
projectName:
|
|
24159
|
+
projectName: path39.basename(projectRoot) || void 0,
|
|
23884
24160
|
getAutonomy,
|
|
23885
24161
|
onAutonomy,
|
|
23886
24162
|
getNextPredict,
|
|
@@ -24003,7 +24279,7 @@ function buildRoutingRunner(config, host) {
|
|
|
24003
24279
|
}
|
|
24004
24280
|
|
|
24005
24281
|
// src/fleet/host.ts
|
|
24006
|
-
var MultiAgentHost = class {
|
|
24282
|
+
var MultiAgentHost = class _MultiAgentHost {
|
|
24007
24283
|
constructor(deps, opts = {}) {
|
|
24008
24284
|
this.deps = deps;
|
|
24009
24285
|
this.opts = opts;
|
|
@@ -24032,6 +24308,19 @@ var MultiAgentHost = class {
|
|
|
24032
24308
|
* actionable error instead of a generic "Director could not be activated".
|
|
24033
24309
|
*/
|
|
24034
24310
|
promotionBlockReason = null;
|
|
24311
|
+
/** Guards `buildDirector` from overwriting a runner set by `spawnACP`. */
|
|
24312
|
+
directorRunnerSet = false;
|
|
24313
|
+
/** Event-bus off-handles registered in `buildDirector` — cleaned up in `dispose()`. */
|
|
24314
|
+
directorOffHandles = [];
|
|
24315
|
+
/** Coordinator task.assigned listener — cleaned up in `dispose()`. */
|
|
24316
|
+
coordinatorOffHandle = null;
|
|
24317
|
+
/** ACP runner cache — keyed by role/subagentId, reused across tasks to avoid
|
|
24318
|
+
* creating a new transport process on every ACP task dispatch. Stores the
|
|
24319
|
+
* pending promise so concurrent calls for the same subagentId share one spawn.
|
|
24320
|
+
* Bounded to 20 entries with LRU eviction to prevent unbounded memory growth. */
|
|
24321
|
+
acpRunnerCache = /* @__PURE__ */ new Map();
|
|
24322
|
+
acpRunnerAccessOrder = [];
|
|
24323
|
+
static ACP_CACHE_MAX = 20;
|
|
24035
24324
|
/**
|
|
24036
24325
|
* Force the lazy build path to run *now* and return the live Director,
|
|
24037
24326
|
* or null when director mode is off. Used by the CLI to register the
|
|
@@ -24080,7 +24369,8 @@ var MultiAgentHost = class {
|
|
|
24080
24369
|
if (this.opts.sessionsRoot && !this.sessionFactory) {
|
|
24081
24370
|
this.sessionFactory = makeDirectorSessionFactory({
|
|
24082
24371
|
sessionsRoot: this.opts.sessionsRoot,
|
|
24083
|
-
directorRunId: this.opts.directorRunId
|
|
24372
|
+
directorRunId: this.opts.directorRunId,
|
|
24373
|
+
traceId: this.opts.traceId
|
|
24084
24374
|
});
|
|
24085
24375
|
}
|
|
24086
24376
|
const coordinatorConfig = {
|
|
@@ -24088,7 +24378,7 @@ var MultiAgentHost = class {
|
|
|
24088
24378
|
doneCondition: { type: "all_tasks_done" },
|
|
24089
24379
|
maxConcurrent: this.opts.maxConcurrent ?? 4
|
|
24090
24380
|
};
|
|
24091
|
-
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ?
|
|
24381
|
+
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path39.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
|
|
24092
24382
|
this.director = new Director({
|
|
24093
24383
|
config: coordinatorConfig,
|
|
24094
24384
|
manifestPath: this.opts.manifestPath,
|
|
@@ -24113,60 +24403,71 @@ var MultiAgentHost = class {
|
|
|
24113
24403
|
this.fleetManager?.removePendingTask(task.id);
|
|
24114
24404
|
this.emitLifecycleCompleted(task.id, result);
|
|
24115
24405
|
});
|
|
24116
|
-
this.
|
|
24117
|
-
|
|
24118
|
-
|
|
24119
|
-
|
|
24120
|
-
|
|
24121
|
-
|
|
24122
|
-
|
|
24123
|
-
|
|
24124
|
-
});
|
|
24125
|
-
this.director.fleet.filter("budget.extended", (e) => {
|
|
24126
|
-
const payload = e.payload;
|
|
24127
|
-
this.deps.events.emit("subagent.budget_extended", {
|
|
24128
|
-
subagentId: e.subagentId,
|
|
24129
|
-
kind: payload.kind,
|
|
24130
|
-
newLimit: payload.newLimit,
|
|
24131
|
-
totalExtensions: payload.totalExtensions
|
|
24132
|
-
});
|
|
24133
|
-
});
|
|
24134
|
-
this.director.fleet.filter("ctx.pct", (e) => {
|
|
24135
|
-
const payload = e.payload;
|
|
24136
|
-
this.deps.events.emit("subagent.ctx_pct", {
|
|
24137
|
-
subagentId: e.subagentId,
|
|
24138
|
-
load: payload.load,
|
|
24139
|
-
tokens: payload.tokens,
|
|
24140
|
-
maxContext: payload.maxContext
|
|
24141
|
-
});
|
|
24142
|
-
});
|
|
24143
|
-
this.director.fleet.filter("subagent.spawned", (e) => {
|
|
24144
|
-
const payload = e.payload;
|
|
24145
|
-
this.deps.events.emit("subagent.spawned", {
|
|
24146
|
-
subagentId: payload.subagentId,
|
|
24147
|
-
taskId: payload.taskId,
|
|
24148
|
-
name: payload.name,
|
|
24149
|
-
provider: payload.provider,
|
|
24150
|
-
model: payload.model
|
|
24151
|
-
});
|
|
24152
|
-
});
|
|
24153
|
-
this.getCoordinator().on(
|
|
24154
|
-
"task.assigned",
|
|
24155
|
-
({
|
|
24156
|
-
task,
|
|
24157
|
-
subagentId
|
|
24158
|
-
}) => {
|
|
24159
|
-
this.deps.events.emit("subagent.task_started", {
|
|
24160
|
-
subagentId,
|
|
24161
|
-
taskId: task.id,
|
|
24162
|
-
description: task.description
|
|
24406
|
+
this.directorOffHandles.push(
|
|
24407
|
+
this.director.fleet.filter("budget.threshold_reached", (e) => {
|
|
24408
|
+
const payload = e.payload;
|
|
24409
|
+
this.deps.events.emit("subagent.budget_warning", {
|
|
24410
|
+
subagentId: e.subagentId,
|
|
24411
|
+
kind: payload.kind,
|
|
24412
|
+
used: payload.used,
|
|
24413
|
+
limit: payload.limit
|
|
24163
24414
|
});
|
|
24164
|
-
}
|
|
24415
|
+
})
|
|
24416
|
+
);
|
|
24417
|
+
this.directorOffHandles.push(
|
|
24418
|
+
this.director.fleet.filter("budget.extended", (e) => {
|
|
24419
|
+
const payload = e.payload;
|
|
24420
|
+
this.deps.events.emit("subagent.budget_extended", {
|
|
24421
|
+
subagentId: e.subagentId,
|
|
24422
|
+
kind: payload.kind,
|
|
24423
|
+
newLimit: payload.newLimit,
|
|
24424
|
+
totalExtensions: payload.totalExtensions
|
|
24425
|
+
});
|
|
24426
|
+
})
|
|
24427
|
+
);
|
|
24428
|
+
this.directorOffHandles.push(
|
|
24429
|
+
this.director.fleet.filter("ctx.pct", (e) => {
|
|
24430
|
+
const payload = e.payload;
|
|
24431
|
+
this.deps.events.emit("subagent.ctx_pct", {
|
|
24432
|
+
subagentId: e.subagentId,
|
|
24433
|
+
load: payload.load,
|
|
24434
|
+
tokens: payload.tokens,
|
|
24435
|
+
maxContext: payload.maxContext
|
|
24436
|
+
});
|
|
24437
|
+
})
|
|
24438
|
+
);
|
|
24439
|
+
this.directorOffHandles.push(
|
|
24440
|
+
this.director.fleet.filter("subagent.spawned", (e) => {
|
|
24441
|
+
const payload = e.payload;
|
|
24442
|
+
this.deps.events.emit("subagent.spawned", {
|
|
24443
|
+
subagentId: payload.subagentId,
|
|
24444
|
+
taskId: payload.taskId,
|
|
24445
|
+
name: payload.name,
|
|
24446
|
+
provider: payload.provider,
|
|
24447
|
+
model: payload.model
|
|
24448
|
+
});
|
|
24449
|
+
})
|
|
24165
24450
|
);
|
|
24451
|
+
const coordinatorTaskAssignedHandler = ({
|
|
24452
|
+
task,
|
|
24453
|
+
subagentId
|
|
24454
|
+
}) => {
|
|
24455
|
+
this.deps.events.emit("subagent.task_started", {
|
|
24456
|
+
subagentId,
|
|
24457
|
+
taskId: task.id,
|
|
24458
|
+
description: task.description
|
|
24459
|
+
});
|
|
24460
|
+
};
|
|
24461
|
+
const coordinator = this.getCoordinator();
|
|
24462
|
+
coordinator.on("task.assigned", coordinatorTaskAssignedHandler);
|
|
24463
|
+
this.coordinatorOffHandle = () => coordinator.off("task.assigned", coordinatorTaskAssignedHandler);
|
|
24166
24464
|
this.fleetEmitTool = makeFleetEmitTool(this.director);
|
|
24167
24465
|
this.fleetStatusTool = makeFleetStatusTool(this.director);
|
|
24168
24466
|
const runner = await this.buildSubagentRunner(config);
|
|
24169
|
-
this.
|
|
24467
|
+
if (!this.directorRunnerSet) {
|
|
24468
|
+
this.getCoordinator().setRunner(runner);
|
|
24469
|
+
this.directorRunnerSet = true;
|
|
24470
|
+
}
|
|
24170
24471
|
}
|
|
24171
24472
|
/**
|
|
24172
24473
|
* Returns the FleetEmitTool for director-mode subagents, if the director
|
|
@@ -24368,9 +24669,23 @@ var MultiAgentHost = class {
|
|
|
24368
24669
|
return buildRoutingRunner(config, this);
|
|
24369
24670
|
}
|
|
24370
24671
|
async buildACPRunner(subagentId) {
|
|
24672
|
+
const cached = this.acpRunnerCache.get(subagentId);
|
|
24673
|
+
if (cached) {
|
|
24674
|
+
const idx = this.acpRunnerAccessOrder.indexOf(subagentId);
|
|
24675
|
+
if (idx !== -1) this.acpRunnerAccessOrder.splice(idx, 1);
|
|
24676
|
+
this.acpRunnerAccessOrder.push(subagentId);
|
|
24677
|
+
return cached;
|
|
24678
|
+
}
|
|
24371
24679
|
const cmd = ACP_AGENT_COMMANDS[subagentId];
|
|
24372
24680
|
if (!cmd) throw new Error(`Unknown ACP agent: ${subagentId}`);
|
|
24373
|
-
|
|
24681
|
+
while (this.acpRunnerAccessOrder.length >= _MultiAgentHost.ACP_CACHE_MAX) {
|
|
24682
|
+
const oldest = this.acpRunnerAccessOrder.shift();
|
|
24683
|
+
if (oldest) this.acpRunnerCache.delete(oldest);
|
|
24684
|
+
}
|
|
24685
|
+
const p = makeACPSubagentRunner(cmd);
|
|
24686
|
+
this.acpRunnerCache.set(subagentId, p);
|
|
24687
|
+
this.acpRunnerAccessOrder.push(subagentId);
|
|
24688
|
+
return p;
|
|
24374
24689
|
}
|
|
24375
24690
|
/**
|
|
24376
24691
|
* Build a Provider for a subagent. When `overrideId` is supplied (from
|
|
@@ -24413,6 +24728,7 @@ var MultiAgentHost = class {
|
|
|
24413
24728
|
const coordinator = this.getCoordinator();
|
|
24414
24729
|
const acpRunner = await this.buildACPRunner(subagentId);
|
|
24415
24730
|
coordinator.setRunner(acpRunner);
|
|
24731
|
+
this.directorRunnerSet = true;
|
|
24416
24732
|
await coordinator.spawn({
|
|
24417
24733
|
id: subagentId,
|
|
24418
24734
|
name: subagentId,
|
|
@@ -24482,8 +24798,9 @@ var MultiAgentHost = class {
|
|
|
24482
24798
|
*/
|
|
24483
24799
|
async spawnAndWait(description, opts) {
|
|
24484
24800
|
const { taskId } = await this.spawn(description, opts);
|
|
24485
|
-
|
|
24486
|
-
|
|
24801
|
+
const director = this.director;
|
|
24802
|
+
if (!director) throw new Error("Director is not initialized");
|
|
24803
|
+
const results = await director.awaitTasks([taskId]);
|
|
24487
24804
|
const result = results[0];
|
|
24488
24805
|
if (!result) throw new Error(`Task ${taskId} completed but no result returned`);
|
|
24489
24806
|
return result;
|
|
@@ -24618,16 +24935,16 @@ var MultiAgentHost = class {
|
|
|
24618
24935
|
if (this.director) return this.director;
|
|
24619
24936
|
this.opts.directorMode = true;
|
|
24620
24937
|
if (this.opts.fleetRoot && !this.opts.manifestPath) {
|
|
24621
|
-
this.opts.manifestPath =
|
|
24938
|
+
this.opts.manifestPath = path39.join(this.opts.fleetRoot, "fleet.json");
|
|
24622
24939
|
}
|
|
24623
24940
|
if (this.opts.fleetRoot && !this.opts.sharedScratchpadPath) {
|
|
24624
|
-
this.opts.sharedScratchpadPath =
|
|
24941
|
+
this.opts.sharedScratchpadPath = path39.join(this.opts.fleetRoot, "shared");
|
|
24625
24942
|
}
|
|
24626
24943
|
if (this.opts.fleetRoot && !this.opts.sessionsRoot) {
|
|
24627
|
-
this.opts.sessionsRoot =
|
|
24944
|
+
this.opts.sessionsRoot = path39.join(this.opts.fleetRoot, "subagents");
|
|
24628
24945
|
}
|
|
24629
24946
|
if (this.opts.fleetRoot && !this.opts.stateCheckpointPath) {
|
|
24630
|
-
this.opts.stateCheckpointPath =
|
|
24947
|
+
this.opts.stateCheckpointPath = path39.join(this.opts.fleetRoot, "director-state.json");
|
|
24631
24948
|
}
|
|
24632
24949
|
await this.ensureDirector();
|
|
24633
24950
|
return this.director ?? null;
|
|
@@ -24697,6 +25014,24 @@ var MultiAgentHost = class {
|
|
|
24697
25014
|
this.getCoordinator().setMaxConcurrent(v);
|
|
24698
25015
|
}
|
|
24699
25016
|
}
|
|
25017
|
+
/**
|
|
25018
|
+
* Clean up all listeners and resources held by the host.
|
|
25019
|
+
* Unregisters all EventBus/FleetBus listeners registered in `buildDirector`
|
|
25020
|
+
* and stops the Director and its coordinator.
|
|
25021
|
+
*
|
|
25022
|
+
* Safe to call multiple times — subsequent calls are no-ops.
|
|
25023
|
+
*/
|
|
25024
|
+
async dispose() {
|
|
25025
|
+
for (const off of this.directorOffHandles) {
|
|
25026
|
+
off();
|
|
25027
|
+
}
|
|
25028
|
+
this.directorOffHandles.length = 0;
|
|
25029
|
+
this.coordinatorOffHandle?.();
|
|
25030
|
+
this.coordinatorOffHandle = null;
|
|
25031
|
+
if (this.director) {
|
|
25032
|
+
await this.director.shutdown();
|
|
25033
|
+
}
|
|
25034
|
+
}
|
|
24700
25035
|
};
|
|
24701
25036
|
|
|
24702
25037
|
// src/session-stats.ts
|
|
@@ -24738,11 +25073,11 @@ var SessionStats = class {
|
|
|
24738
25073
|
if (tool.name === "bash") this.bashCommands++;
|
|
24739
25074
|
else if (tool.name === "fetch") this.fetches++;
|
|
24740
25075
|
if (!tool.ok) return;
|
|
24741
|
-
const
|
|
24742
|
-
if (tool.name === "read" &&
|
|
24743
|
-
else if (tool.name === "edit" &&
|
|
24744
|
-
else if (tool.name === "write" &&
|
|
24745
|
-
this.writtenPaths.add(
|
|
25076
|
+
const path40 = typeof input?.path === "string" ? input.path : void 0;
|
|
25077
|
+
if (tool.name === "read" && path40) this.readPaths.add(path40);
|
|
25078
|
+
else if (tool.name === "edit" && path40) this.editedPaths.add(path40);
|
|
25079
|
+
else if (tool.name === "write" && path40) {
|
|
25080
|
+
this.writtenPaths.add(path40);
|
|
24746
25081
|
const content = typeof input?.content === "string" ? input.content : "";
|
|
24747
25082
|
this.bytesWritten += Buffer.byteLength(content, "utf8");
|
|
24748
25083
|
}
|
|
@@ -25054,7 +25389,7 @@ async function setupCodebaseIndexing(deps) {
|
|
|
25054
25389
|
if (tool?.mutating && FILE_EDIT_TOOLS.has(tool.name)) {
|
|
25055
25390
|
const fp = payload.toolUse.input?.file_path;
|
|
25056
25391
|
if (typeof fp === "string" && fp.length > 0) {
|
|
25057
|
-
const abs =
|
|
25392
|
+
const abs = path39.resolve(payload.ctx.cwd, fp);
|
|
25058
25393
|
if (isIndexableFile(abs)) {
|
|
25059
25394
|
enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
|
|
25060
25395
|
}
|
|
@@ -25073,7 +25408,7 @@ async function setupCodebaseIndexing(deps) {
|
|
|
25073
25408
|
if (!filename) return;
|
|
25074
25409
|
const rel = filename.toString();
|
|
25075
25410
|
if (isIgnored(rel)) return;
|
|
25076
|
-
const abs =
|
|
25411
|
+
const abs = path39.resolve(projectRoot, rel);
|
|
25077
25412
|
if (!isIndexableFile(abs)) return;
|
|
25078
25413
|
enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
|
|
25079
25414
|
});
|
|
@@ -25127,7 +25462,7 @@ function setupMetrics(params) {
|
|
|
25127
25462
|
const dumpMetrics = () => {
|
|
25128
25463
|
if (!metricsSink) return;
|
|
25129
25464
|
try {
|
|
25130
|
-
const out =
|
|
25465
|
+
const out = path39.join(wpaths.projectSessions, "metrics.json");
|
|
25131
25466
|
const snap = metricsSink.snapshot();
|
|
25132
25467
|
writeFileSync(out, JSON.stringify(snap, null, 2));
|
|
25133
25468
|
} catch {
|
|
@@ -25305,7 +25640,9 @@ async function setupSession(params) {
|
|
|
25305
25640
|
tokenCounter,
|
|
25306
25641
|
renderer,
|
|
25307
25642
|
flags,
|
|
25308
|
-
onRecovery
|
|
25643
|
+
onRecovery,
|
|
25644
|
+
// Optional EventBus for storage observability
|
|
25645
|
+
events: eventsBus
|
|
25309
25646
|
} = params;
|
|
25310
25647
|
sessionStore.prune(DEFAULT_SESSION_PRUNE_DAYS).then((count) => {
|
|
25311
25648
|
if (count > 0) renderer.writeInfo(`Pruned ${count} old session${count === 1 ? "" : "s"}.`);
|
|
@@ -25362,10 +25699,10 @@ async function setupSession(params) {
|
|
|
25362
25699
|
);
|
|
25363
25700
|
});
|
|
25364
25701
|
const attachments = new DefaultAttachmentStore({
|
|
25365
|
-
spoolDir:
|
|
25702
|
+
spoolDir: path39.join(wpaths.projectSessions, session?.id, "attachments")
|
|
25366
25703
|
});
|
|
25367
|
-
const queueStore = new QueueStore({ dir: path38.join(wpaths.projectSessions, session?.id) });
|
|
25368
25704
|
const ctxSignal = new AbortController().signal;
|
|
25705
|
+
const traceId = randomBytes(16).toString("hex");
|
|
25369
25706
|
const context = new Context({
|
|
25370
25707
|
systemPrompt,
|
|
25371
25708
|
provider,
|
|
@@ -25376,17 +25713,27 @@ async function setupSession(params) {
|
|
|
25376
25713
|
projectRoot,
|
|
25377
25714
|
model: config.model,
|
|
25378
25715
|
agentId: "leader",
|
|
25379
|
-
agentName: "Leader Agent"
|
|
25716
|
+
agentName: "Leader Agent",
|
|
25717
|
+
traceId
|
|
25380
25718
|
});
|
|
25381
25719
|
context.meta["packageTrackerOpts"] = {
|
|
25382
25720
|
storageDir: wpaths.projectDir,
|
|
25383
25721
|
projectRoot
|
|
25384
25722
|
};
|
|
25385
25723
|
if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
|
|
25386
|
-
const
|
|
25724
|
+
const queueStore = new QueueStore({
|
|
25725
|
+
dir: path39.join(wpaths.projectSessions, session?.id),
|
|
25726
|
+
...eventsBus ? { events: eventsBus } : {},
|
|
25727
|
+
...traceId ? { traceId } : {}
|
|
25728
|
+
});
|
|
25729
|
+
const todosCheckpointPath = path39.join(wpaths.projectSessions, `${session?.id}.todos.json`);
|
|
25387
25730
|
if (resumeId) {
|
|
25388
25731
|
try {
|
|
25389
|
-
const restoredTodos = await loadTodosCheckpoint(
|
|
25732
|
+
const restoredTodos = await loadTodosCheckpoint(
|
|
25733
|
+
todosCheckpointPath,
|
|
25734
|
+
eventsBus,
|
|
25735
|
+
traceId
|
|
25736
|
+
);
|
|
25390
25737
|
if (restoredTodos && restoredTodos.length > 0) {
|
|
25391
25738
|
context.state.replaceTodos(restoredTodos);
|
|
25392
25739
|
renderer.writeInfo(
|
|
@@ -25399,17 +25746,19 @@ async function setupSession(params) {
|
|
|
25399
25746
|
const detachTodosCheckpoint = attachTodosCheckpoint(
|
|
25400
25747
|
context.state,
|
|
25401
25748
|
todosCheckpointPath,
|
|
25402
|
-
session?.id
|
|
25749
|
+
session?.id,
|
|
25750
|
+
eventsBus,
|
|
25751
|
+
traceId
|
|
25403
25752
|
);
|
|
25404
|
-
const planPath =
|
|
25753
|
+
const planPath = path39.join(wpaths.projectSessions, `${session?.id}.plan.json`);
|
|
25405
25754
|
context.state.setMeta("plan.path", planPath);
|
|
25406
|
-
const taskPath =
|
|
25755
|
+
const taskPath = path39.join(wpaths.projectSessions, `${session?.id}.tasks.json`);
|
|
25407
25756
|
context.state.setMeta("task.path", taskPath);
|
|
25408
25757
|
let dirState;
|
|
25409
25758
|
if (resumeId) {
|
|
25410
25759
|
try {
|
|
25411
|
-
const fleetRoot =
|
|
25412
|
-
dirState = await loadDirectorState(
|
|
25760
|
+
const fleetRoot = path39.join(wpaths.projectSessions, session?.id);
|
|
25761
|
+
dirState = await loadDirectorState(path39.join(fleetRoot, "director-state.json"));
|
|
25413
25762
|
if (dirState) {
|
|
25414
25763
|
const tCounts = {};
|
|
25415
25764
|
for (const t of dirState.tasks) tCounts[t.status] = (tCounts[t.status] ?? 0) + 1;
|
|
@@ -25435,6 +25784,7 @@ async function setupSession(params) {
|
|
|
25435
25784
|
return {
|
|
25436
25785
|
session: expectDefined(session),
|
|
25437
25786
|
sessionRef,
|
|
25787
|
+
traceId,
|
|
25438
25788
|
context,
|
|
25439
25789
|
restoredMessages,
|
|
25440
25790
|
attachments,
|
|
@@ -25655,7 +26005,7 @@ async function main(argv) {
|
|
|
25655
26005
|
projectGoal: wpaths.projectGoal,
|
|
25656
26006
|
projectSessions: wpaths.projectSessions
|
|
25657
26007
|
},
|
|
25658
|
-
pathJoiner: { join: (a, b) =>
|
|
26008
|
+
pathJoiner: { join: (a, b) => path39.join(a, b) },
|
|
25659
26009
|
systemPromptBuilderToken: TOKENS.SystemPromptBuilder
|
|
25660
26010
|
});
|
|
25661
26011
|
const toolRegistry = new ToolRegistry();
|
|
@@ -25775,6 +26125,7 @@ async function main(argv) {
|
|
|
25775
26125
|
tokenCounter,
|
|
25776
26126
|
renderer,
|
|
25777
26127
|
flags,
|
|
26128
|
+
events,
|
|
25778
26129
|
onRecovery: (abandoned, autoRecover) => promptRecovery(reader, renderer, abandoned, autoRecover)
|
|
25779
26130
|
});
|
|
25780
26131
|
const session = sessResult.session;
|
|
@@ -25786,12 +26137,13 @@ async function main(argv) {
|
|
|
25786
26137
|
const planPath = sessResult.planPath;
|
|
25787
26138
|
const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
|
|
25788
26139
|
const priorFleetState = sessResult.priorFleetState;
|
|
26140
|
+
memoryStore.withTraceId(sessResult.traceId);
|
|
25789
26141
|
let tracker;
|
|
25790
26142
|
try {
|
|
25791
26143
|
const { getSessionRegistry, AgentStatusTracker } = await import('@wrongstack/core');
|
|
25792
26144
|
const registry = getSessionRegistry(wpaths.globalRoot);
|
|
25793
|
-
const projectSlug2 =
|
|
25794
|
-
const projectName =
|
|
26145
|
+
const projectSlug2 = path39.basename(wpaths.projectDir);
|
|
26146
|
+
const projectName = path39.basename(projectRoot);
|
|
25795
26147
|
let gitBranch;
|
|
25796
26148
|
try {
|
|
25797
26149
|
const { execSync } = await import('child_process');
|
|
@@ -25890,7 +26242,7 @@ async function main(argv) {
|
|
|
25890
26242
|
if (e.ok && (e.name === "write" || e.name === "edit" || e.name === "replace" || e.name === "patch")) {
|
|
25891
26243
|
const filePath = e.input?.path;
|
|
25892
26244
|
if (filePath) {
|
|
25893
|
-
const projectDir =
|
|
26245
|
+
const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
|
|
25894
26246
|
void recordFileAction(
|
|
25895
26247
|
{ storageDir: projectDir, projectRoot },
|
|
25896
26248
|
{
|
|
@@ -26056,7 +26408,7 @@ async function main(argv) {
|
|
|
26056
26408
|
let depWatcherDispose;
|
|
26057
26409
|
if (dwCfg?.["enabled"] === true) {
|
|
26058
26410
|
try {
|
|
26059
|
-
const projectDir =
|
|
26411
|
+
const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
|
|
26060
26412
|
const dwMailbox = new GlobalMailbox(projectDir, events);
|
|
26061
26413
|
depWatcherDispose = attachDepWatcherBridge({
|
|
26062
26414
|
events,
|
|
@@ -26155,12 +26507,12 @@ async function main(argv) {
|
|
|
26155
26507
|
}
|
|
26156
26508
|
}
|
|
26157
26509
|
};
|
|
26158
|
-
const fleetRoot = directorMode ?
|
|
26159
|
-
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] :
|
|
26160
|
-
const sharedScratchpadPath = directorMode ?
|
|
26161
|
-
const subagentSessionsRoot = directorMode ?
|
|
26162
|
-
const stateCheckpointPath = directorMode ?
|
|
26163
|
-
const fleetRootForPromotion =
|
|
26510
|
+
const fleetRoot = directorMode ? path39.join(wpaths.projectSessions, session.id) : void 0;
|
|
26511
|
+
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path39.join(expectDefined(fleetRoot), "fleet.json") : void 0;
|
|
26512
|
+
const sharedScratchpadPath = directorMode ? path39.join(expectDefined(fleetRoot), "shared") : void 0;
|
|
26513
|
+
const subagentSessionsRoot = directorMode ? path39.join(expectDefined(fleetRoot), "subagents") : void 0;
|
|
26514
|
+
const stateCheckpointPath = directorMode ? path39.join(expectDefined(fleetRoot), "director-state.json") : void 0;
|
|
26515
|
+
const fleetRootForPromotion = path39.join(wpaths.projectSessions, session.id);
|
|
26164
26516
|
const brainSettings = {
|
|
26165
26517
|
maxAutoRisk: "medium"
|
|
26166
26518
|
};
|
|
@@ -26265,7 +26617,8 @@ async function main(argv) {
|
|
|
26265
26617
|
sessionWriter: session,
|
|
26266
26618
|
maxConcurrent,
|
|
26267
26619
|
getLeaderMaxContext: () => effectiveMaxContext,
|
|
26268
|
-
brain
|
|
26620
|
+
brain,
|
|
26621
|
+
traceId: sessResult.traceId
|
|
26269
26622
|
}
|
|
26270
26623
|
);
|
|
26271
26624
|
toolRegistry.register(
|
|
@@ -26302,7 +26655,7 @@ async function main(argv) {
|
|
|
26302
26655
|
let techStackConsumerDispose;
|
|
26303
26656
|
if (dwCfg?.["enabled"] === true) {
|
|
26304
26657
|
try {
|
|
26305
|
-
const projectDir =
|
|
26658
|
+
const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
|
|
26306
26659
|
const tsMailbox = new GlobalMailbox(projectDir, events);
|
|
26307
26660
|
const fileAuthorOpts = {
|
|
26308
26661
|
storageDir: projectDir,
|
|
@@ -26335,7 +26688,7 @@ async function main(argv) {
|
|
|
26335
26688
|
let pkgOutdatedDispose;
|
|
26336
26689
|
if (dwCfg?.["enabled"] === true) {
|
|
26337
26690
|
try {
|
|
26338
|
-
const projectDir =
|
|
26691
|
+
const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
|
|
26339
26692
|
const pkgMailbox = new GlobalMailbox(projectDir, events);
|
|
26340
26693
|
const pkgTrackerOpts = {
|
|
26341
26694
|
storageDir: projectDir,
|
|
@@ -26709,7 +27062,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
26709
27062
|
return director.spawn(cfg);
|
|
26710
27063
|
},
|
|
26711
27064
|
onFleetLog: async (subagentId, mode) => {
|
|
26712
|
-
const subagentsRoot =
|
|
27065
|
+
const subagentsRoot = path39.join(fleetRootForPromotion, "subagents");
|
|
26713
27066
|
let runDirs;
|
|
26714
27067
|
try {
|
|
26715
27068
|
runDirs = await fsp5.readdir(subagentsRoot);
|
|
@@ -26718,7 +27071,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
26718
27071
|
}
|
|
26719
27072
|
const found = [];
|
|
26720
27073
|
for (const runId of runDirs) {
|
|
26721
|
-
const runDir =
|
|
27074
|
+
const runDir = path39.join(subagentsRoot, runId);
|
|
26722
27075
|
let files;
|
|
26723
27076
|
try {
|
|
26724
27077
|
files = await fsp5.readdir(runDir);
|
|
@@ -26727,7 +27080,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
26727
27080
|
}
|
|
26728
27081
|
for (const f of files) {
|
|
26729
27082
|
if (!f.endsWith(".jsonl")) continue;
|
|
26730
|
-
const full =
|
|
27083
|
+
const full = path39.join(runDir, f);
|
|
26731
27084
|
try {
|
|
26732
27085
|
const stat7 = await fsp5.stat(full);
|
|
26733
27086
|
found.push({
|
|
@@ -26828,7 +27181,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
26828
27181
|
}
|
|
26829
27182
|
const dir = await multiAgentHost.ensureDirector();
|
|
26830
27183
|
if (!dir) return "Director is not available.";
|
|
26831
|
-
const dirStatePath =
|
|
27184
|
+
const dirStatePath = path39.join(fleetRootForPromotion, "director-state.json");
|
|
26832
27185
|
const prior = await loadDirectorState(dirStatePath);
|
|
26833
27186
|
if (!prior) {
|
|
26834
27187
|
return "No prior director-state.json found \u2014 nothing to retry.";
|
|
@@ -26897,9 +27250,9 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
26897
27250
|
for (const tool of director2.tools(FLEET_ROSTER)) {
|
|
26898
27251
|
toolRegistry.register(tool);
|
|
26899
27252
|
}
|
|
26900
|
-
const mp =
|
|
26901
|
-
const sp =
|
|
26902
|
-
const ss =
|
|
27253
|
+
const mp = path39.join(fleetRootForPromotion, "fleet.json");
|
|
27254
|
+
const sp = path39.join(fleetRootForPromotion, "shared");
|
|
27255
|
+
const ss = path39.join(fleetRootForPromotion, "subagents");
|
|
26903
27256
|
const lines = [
|
|
26904
27257
|
`${color.green("\u2713")} Promoted to director mode.`,
|
|
26905
27258
|
` Roster: ${Object.keys(FLEET_ROSTER).join(", ")}`,
|
|
@@ -27003,7 +27356,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
27003
27356
|
// Real per-role factory: each dispatched slot runs as a fresh,
|
|
27004
27357
|
// isolated agent with the role's filtered tools + persona prompt
|
|
27005
27358
|
// (instead of sharing the leader agent's Context).
|
|
27006
|
-
subagentFactory: multiAgentHost.makeSubagentFactory(config)
|
|
27359
|
+
subagentFactory: multiAgentHost.makeSubagentFactory(config),
|
|
27360
|
+
events
|
|
27007
27361
|
};
|
|
27008
27362
|
parallelEngine = new ParallelEternalEngine(parallelOptions);
|
|
27009
27363
|
}
|
|
@@ -27017,7 +27371,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
27017
27371
|
maxContextTokens: effectiveMaxContext > 0 ? effectiveMaxContext : void 0,
|
|
27018
27372
|
onIteration: broadcastEternalIteration,
|
|
27019
27373
|
onStage: broadcastAutonomyStage,
|
|
27020
|
-
brain
|
|
27374
|
+
brain,
|
|
27375
|
+
events
|
|
27021
27376
|
});
|
|
27022
27377
|
}
|
|
27023
27378
|
void eternalEngine.prime();
|
|
@@ -27033,11 +27388,14 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
27033
27388
|
brainMonitor.stop();
|
|
27034
27389
|
brainQueue.dispose();
|
|
27035
27390
|
void mcpRegistry.stopAll();
|
|
27391
|
+
multiAgentHost.dispose().catch(
|
|
27392
|
+
(err) => logger.warn(`multiAgentHost.dispose() failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
27393
|
+
);
|
|
27036
27394
|
},
|
|
27037
27395
|
onBeforeExit: async () => {
|
|
27038
27396
|
const cwd2 = projectRoot;
|
|
27039
27397
|
const statusResult = await new Promise(
|
|
27040
|
-
(
|
|
27398
|
+
(resolve11, reject) => {
|
|
27041
27399
|
const child = spawn("git", ["status", "--porcelain"], {
|
|
27042
27400
|
cwd: cwd2,
|
|
27043
27401
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -27049,7 +27407,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
27049
27407
|
stdout += d;
|
|
27050
27408
|
});
|
|
27051
27409
|
child.on("error", reject);
|
|
27052
|
-
child.on("close", (code) =>
|
|
27410
|
+
child.on("close", (code) => resolve11({ stdout, code: code ?? 0 }));
|
|
27053
27411
|
}
|
|
27054
27412
|
);
|
|
27055
27413
|
if (statusResult.stdout.trim().length > 0) {
|
|
@@ -27314,8 +27672,11 @@ Reply YES to auto-proceed, NO to wait for human input.`
|
|
|
27314
27672
|
brain,
|
|
27315
27673
|
brainSettings,
|
|
27316
27674
|
getBrainLog: () => brainLog,
|
|
27317
|
-
// Clean up SessionStats event listeners when the REPL exits.
|
|
27318
|
-
onDestroy: () =>
|
|
27675
|
+
// Clean up SessionStats event listeners and all EventBus handlers when the REPL exits.
|
|
27676
|
+
onDestroy: () => {
|
|
27677
|
+
teardownHandlers.forEach((fn) => fn());
|
|
27678
|
+
stats.destroy(events);
|
|
27679
|
+
}
|
|
27319
27680
|
});
|
|
27320
27681
|
}
|
|
27321
27682
|
var isMain = import.meta.url === `file://${process.argv[1]?.replace(/\\/g, "/")}` || process.argv[1]?.endsWith("/cli/dist/index.js") || process.argv[1]?.endsWith("\\cli\\dist\\index.js");
|