@wrongstack/cli 0.41.0 → 0.54.1
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/data/providers.json +22 -1
- package/dist/index.js +903 -312
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import * as
|
|
2
|
+
import * as path8 from 'path';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import * as fsp3 from 'fs/promises';
|
|
5
|
-
import { color, writeErr, DefaultTaskStore, TaskTracker, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, renderTaskGraph, SpecVersioning, atomicWrite, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, createDelegateTool, FLEET_ROSTER, createMcpControlTool, DefaultLogger, DefaultModelsRegistry, isStdinTTY, writeOut, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1,
|
|
5
|
+
import { color, writeErr, DefaultTaskStore, TaskTracker, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, renderTaskGraph, SpecVersioning, DefaultSecretScrubber, atomicWrite, DefaultPathResolver, TOKENS, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, DefaultLogger, DefaultModelsRegistry, isStdinTTY, writeOut, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, bootConfig as bootConfig$1, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, DefaultPluginAPI, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, AGENTS_BY_PHASE, dispatchAgent, formatTodosList, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, emptyGoal, buildGoalPreamble, formatGoal, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, AGENT_CATALOG, matrixKeyKind, onResize, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, InputBuilder, FsError, ERROR_CODES } from '@wrongstack/core';
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
|
-
import * as
|
|
8
|
-
import
|
|
7
|
+
import * as os2 from 'os';
|
|
8
|
+
import os2__default from 'os';
|
|
9
9
|
import * as crypto2 from 'crypto';
|
|
10
10
|
import { randomUUID } from 'crypto';
|
|
11
11
|
import { DefaultSecretVault, decryptConfigSecrets, encryptConfigSecrets, isSecretField } from '@wrongstack/core/security';
|
|
12
12
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
13
13
|
import { MCPRegistry } from '@wrongstack/mcp';
|
|
14
|
-
import { buildProviderFactoriesFromRegistry, makeProviderFromConfig
|
|
14
|
+
import { capabilitiesFor, buildProviderFactoriesFromRegistry, makeProviderFromConfig } from '@wrongstack/providers';
|
|
15
15
|
import { createDefaultContainer, routeImagesForModel, readClipboardImage } from '@wrongstack/runtime';
|
|
16
16
|
import { builtinToolsPack, rememberTool, forgetTool } from '@wrongstack/tools';
|
|
17
17
|
import { fileURLToPath } from 'url';
|
|
@@ -1395,7 +1395,7 @@ function sddHelp() {
|
|
|
1395
1395
|
async function gatherProjectContext2(projectRoot) {
|
|
1396
1396
|
const parts = [];
|
|
1397
1397
|
try {
|
|
1398
|
-
const pkgPath =
|
|
1398
|
+
const pkgPath = path8.join(projectRoot, "package.json");
|
|
1399
1399
|
const pkgRaw = await fsp3.readFile(pkgPath, "utf8");
|
|
1400
1400
|
const pkg = JSON.parse(pkgRaw);
|
|
1401
1401
|
parts.push(`Project: ${String(pkg.name ?? "unknown")}`);
|
|
@@ -1411,13 +1411,13 @@ async function gatherProjectContext2(projectRoot) {
|
|
|
1411
1411
|
} catch {
|
|
1412
1412
|
}
|
|
1413
1413
|
try {
|
|
1414
|
-
const tsconfigPath =
|
|
1414
|
+
const tsconfigPath = path8.join(projectRoot, "tsconfig.json");
|
|
1415
1415
|
await fsp3.access(tsconfigPath);
|
|
1416
1416
|
parts.push("Language: TypeScript");
|
|
1417
1417
|
} catch {
|
|
1418
1418
|
}
|
|
1419
1419
|
try {
|
|
1420
|
-
const srcDir =
|
|
1420
|
+
const srcDir = path8.join(projectRoot, "src");
|
|
1421
1421
|
const entries = await fsp3.readdir(srcDir, { withFileTypes: true });
|
|
1422
1422
|
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1423
1423
|
if (dirs.length > 0) {
|
|
@@ -1573,7 +1573,7 @@ __export(update_check_exports, {
|
|
|
1573
1573
|
getUpdateNotification: () => getUpdateNotification
|
|
1574
1574
|
});
|
|
1575
1575
|
function cachePath(homeFn = defaultHomeDir2) {
|
|
1576
|
-
return
|
|
1576
|
+
return path8.join(homeFn(), ".wrongstack", "update-cache.json");
|
|
1577
1577
|
}
|
|
1578
1578
|
function currentVersion() {
|
|
1579
1579
|
const req2 = createRequire(import.meta.url);
|
|
@@ -1610,7 +1610,7 @@ async function readCache(homeFn = defaultHomeDir2) {
|
|
|
1610
1610
|
}
|
|
1611
1611
|
async function writeCache(entry, homeFn = defaultHomeDir2) {
|
|
1612
1612
|
try {
|
|
1613
|
-
const dir =
|
|
1613
|
+
const dir = path8.dirname(cachePath(homeFn));
|
|
1614
1614
|
await fsp3.mkdir(dir, { recursive: true });
|
|
1615
1615
|
await fsp3.writeFile(cachePath(homeFn), JSON.stringify(entry, null, 2), "utf8");
|
|
1616
1616
|
} catch {
|
|
@@ -1683,7 +1683,7 @@ async function getUpdateNotification(signal, homeFn) {
|
|
|
1683
1683
|
var defaultHomeDir2, CACHE_TTL_MS;
|
|
1684
1684
|
var init_update_check = __esm({
|
|
1685
1685
|
"src/update-check.ts"() {
|
|
1686
|
-
defaultHomeDir2 = () =>
|
|
1686
|
+
defaultHomeDir2 = () => os2.homedir();
|
|
1687
1687
|
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
1688
1688
|
}
|
|
1689
1689
|
});
|
|
@@ -1696,6 +1696,7 @@ __export(webui_server_exports, {
|
|
|
1696
1696
|
async function runWebUI(opts) {
|
|
1697
1697
|
const port = opts.port ?? 3457;
|
|
1698
1698
|
const clients = /* @__PURE__ */ new Map();
|
|
1699
|
+
const secretScrubber = new DefaultSecretScrubber();
|
|
1699
1700
|
let abortController = null;
|
|
1700
1701
|
const authToken = crypto2.randomBytes(16).toString("hex");
|
|
1701
1702
|
const wss = new WebSocketServer({ port, host: "127.0.0.1", maxPayload: 1 * 1024 * 1024 });
|
|
@@ -1735,7 +1736,7 @@ async function runWebUI(opts) {
|
|
|
1735
1736
|
payload: {
|
|
1736
1737
|
id: e.id,
|
|
1737
1738
|
name: e.name,
|
|
1738
|
-
input: e.input,
|
|
1739
|
+
input: secretScrubber.scrubObject(e.input),
|
|
1739
1740
|
messageId: `tool_${e.id}`
|
|
1740
1741
|
}
|
|
1741
1742
|
});
|
|
@@ -1764,8 +1765,8 @@ async function runWebUI(opts) {
|
|
|
1764
1765
|
name: e.name,
|
|
1765
1766
|
durationMs: e.durationMs,
|
|
1766
1767
|
ok: e.ok,
|
|
1767
|
-
input: e.input,
|
|
1768
|
-
output: e.output
|
|
1768
|
+
input: secretScrubber.scrubObject(e.input),
|
|
1769
|
+
output: secretScrubber.scrubObject(e.output)
|
|
1769
1770
|
}
|
|
1770
1771
|
});
|
|
1771
1772
|
})
|
|
@@ -1813,7 +1814,7 @@ async function runWebUI(opts) {
|
|
|
1813
1814
|
);
|
|
1814
1815
|
}
|
|
1815
1816
|
}
|
|
1816
|
-
return new Promise((
|
|
1817
|
+
return new Promise((resolve3) => {
|
|
1817
1818
|
wss.on("listening", () => {
|
|
1818
1819
|
console.log(`[WebUI] WebSocket server running on ws://127.0.0.1:${port}`);
|
|
1819
1820
|
setupEvents();
|
|
@@ -1914,7 +1915,7 @@ async function runWebUI(opts) {
|
|
|
1914
1915
|
clients.clear();
|
|
1915
1916
|
wss.close(() => {
|
|
1916
1917
|
console.log("[WebUI] Server stopped");
|
|
1917
|
-
|
|
1918
|
+
resolve3();
|
|
1918
1919
|
});
|
|
1919
1920
|
}
|
|
1920
1921
|
process.on("SIGINT", shutdown);
|
|
@@ -2231,7 +2232,7 @@ async function runWebUI(opts) {
|
|
|
2231
2232
|
return {};
|
|
2232
2233
|
}
|
|
2233
2234
|
if (!parsed.providers) return {};
|
|
2234
|
-
const keyFile =
|
|
2235
|
+
const keyFile = path8.join(path8.dirname(opts.globalConfigPath), ".key");
|
|
2235
2236
|
const vault = new DefaultSecretVault({ keyFile });
|
|
2236
2237
|
return decryptConfigSecrets(parsed.providers, vault);
|
|
2237
2238
|
}
|
|
@@ -2264,7 +2265,7 @@ async function runWebUI(opts) {
|
|
|
2264
2265
|
parsed = {};
|
|
2265
2266
|
}
|
|
2266
2267
|
parsed.providers = providers;
|
|
2267
|
-
const keyFile =
|
|
2268
|
+
const keyFile = path8.join(path8.dirname(opts.globalConfigPath), ".key");
|
|
2268
2269
|
const vault = new DefaultSecretVault({ keyFile });
|
|
2269
2270
|
const encrypted = encryptConfigSecrets(parsed, vault);
|
|
2270
2271
|
await atomicWrite(opts.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
@@ -2338,6 +2339,44 @@ function makeProviderClassifier(provider, model) {
|
|
|
2338
2339
|
}
|
|
2339
2340
|
});
|
|
2340
2341
|
}
|
|
2342
|
+
async function resolveRuntimeMaxContext(input) {
|
|
2343
|
+
const explicitContext = positiveNumber(input.config.context?.effectiveMaxContext);
|
|
2344
|
+
if (explicitContext) return explicitContext;
|
|
2345
|
+
const providerConfig = input.runtimeProviderConfig ?? input.config.providers?.[input.providerId];
|
|
2346
|
+
const providerOverride = positiveNumber(readConfiguredMaxContext(providerConfig));
|
|
2347
|
+
if (providerOverride) return providerOverride;
|
|
2348
|
+
const topLevelBaseUrlApplies = input.providerId === input.config.provider;
|
|
2349
|
+
const hasCustomBaseUrl = Boolean(
|
|
2350
|
+
providerConfig?.baseUrl || topLevelBaseUrlApplies && input.config.baseUrl
|
|
2351
|
+
);
|
|
2352
|
+
if (input.modelsRegistry && !hasCustomBaseUrl) {
|
|
2353
|
+
const mergedModels = mergeCustomModelDefs(
|
|
2354
|
+
providerConfig?.customModels,
|
|
2355
|
+
input.config.models
|
|
2356
|
+
);
|
|
2357
|
+
const caps = await capabilitiesFor(
|
|
2358
|
+
input.modelsRegistry,
|
|
2359
|
+
input.providerId,
|
|
2360
|
+
input.modelId,
|
|
2361
|
+
mergedModels
|
|
2362
|
+
).catch(() => void 0);
|
|
2363
|
+
const catalogMax = positiveNumber(caps?.maxContext);
|
|
2364
|
+
if (catalogMax) return catalogMax;
|
|
2365
|
+
const directModel = await input.modelsRegistry.getModel(input.providerId, input.modelId).catch(() => void 0);
|
|
2366
|
+
const directMax = positiveNumber(directModel?.capabilities.maxContext);
|
|
2367
|
+
if (directMax) return directMax;
|
|
2368
|
+
}
|
|
2369
|
+
return positiveNumber(input.provider.capabilities.maxContext) ?? 0;
|
|
2370
|
+
}
|
|
2371
|
+
function readConfiguredMaxContext(providerConfig) {
|
|
2372
|
+
if (!providerConfig || typeof providerConfig !== "object") return void 0;
|
|
2373
|
+
const capabilities = providerConfig.capabilities;
|
|
2374
|
+
if (!capabilities || typeof capabilities !== "object") return void 0;
|
|
2375
|
+
return capabilities.maxContext;
|
|
2376
|
+
}
|
|
2377
|
+
function positiveNumber(value) {
|
|
2378
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
2379
|
+
}
|
|
2341
2380
|
|
|
2342
2381
|
// src/arg-parser.ts
|
|
2343
2382
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
@@ -2361,6 +2400,7 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
2361
2400
|
"metrics",
|
|
2362
2401
|
"webui",
|
|
2363
2402
|
"no-check",
|
|
2403
|
+
"no-models-refresh",
|
|
2364
2404
|
"director",
|
|
2365
2405
|
"no-director",
|
|
2366
2406
|
"no-autonomy",
|
|
@@ -2485,10 +2525,10 @@ async function detectPackageManager(root, declared) {
|
|
|
2485
2525
|
const name = declared.split("@")[0];
|
|
2486
2526
|
if (name) return name;
|
|
2487
2527
|
}
|
|
2488
|
-
if (await pathExists(
|
|
2489
|
-
if (await pathExists(
|
|
2490
|
-
if (await pathExists(
|
|
2491
|
-
if (await pathExists(
|
|
2528
|
+
if (await pathExists(path8.join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
2529
|
+
if (await pathExists(path8.join(root, "bun.lockb"))) return "bun";
|
|
2530
|
+
if (await pathExists(path8.join(root, "bun.lock"))) return "bun";
|
|
2531
|
+
if (await pathExists(path8.join(root, "yarn.lock"))) return "yarn";
|
|
2492
2532
|
return "npm";
|
|
2493
2533
|
}
|
|
2494
2534
|
function hasUsableScript(scripts, name) {
|
|
@@ -2509,7 +2549,7 @@ function parseMakeTargets(makefile) {
|
|
|
2509
2549
|
async function detectProjectFacts(root) {
|
|
2510
2550
|
const facts = { hints: [] };
|
|
2511
2551
|
try {
|
|
2512
|
-
const pkg = JSON.parse(await fsp3.readFile(
|
|
2552
|
+
const pkg = JSON.parse(await fsp3.readFile(path8.join(root, "package.json"), "utf8"));
|
|
2513
2553
|
const scripts = pkg.scripts ?? {};
|
|
2514
2554
|
const pm = await detectPackageManager(root, pkg.packageManager);
|
|
2515
2555
|
if (hasUsableScript(scripts, "build")) facts.build = `${pm} run build`;
|
|
@@ -2523,14 +2563,14 @@ async function detectProjectFacts(root) {
|
|
|
2523
2563
|
} catch {
|
|
2524
2564
|
}
|
|
2525
2565
|
try {
|
|
2526
|
-
if (!await pathExists(
|
|
2566
|
+
if (!await pathExists(path8.join(root, "pyproject.toml"))) throw new Error("not python");
|
|
2527
2567
|
facts.test ??= "pytest";
|
|
2528
2568
|
facts.lint ??= "ruff check .";
|
|
2529
2569
|
facts.hints.push("pyproject.toml");
|
|
2530
2570
|
} catch {
|
|
2531
2571
|
}
|
|
2532
2572
|
try {
|
|
2533
|
-
if (!await pathExists(
|
|
2573
|
+
if (!await pathExists(path8.join(root, "go.mod"))) throw new Error("not go");
|
|
2534
2574
|
facts.build ??= "go build ./...";
|
|
2535
2575
|
facts.test ??= "go test ./...";
|
|
2536
2576
|
facts.run ??= "go run .";
|
|
@@ -2538,7 +2578,7 @@ async function detectProjectFacts(root) {
|
|
|
2538
2578
|
} catch {
|
|
2539
2579
|
}
|
|
2540
2580
|
try {
|
|
2541
|
-
if (!await pathExists(
|
|
2581
|
+
if (!await pathExists(path8.join(root, "Cargo.toml"))) throw new Error("not rust");
|
|
2542
2582
|
facts.build ??= "cargo build";
|
|
2543
2583
|
facts.test ??= "cargo test";
|
|
2544
2584
|
facts.lint ??= "cargo clippy";
|
|
@@ -2547,7 +2587,7 @@ async function detectProjectFacts(root) {
|
|
|
2547
2587
|
} catch {
|
|
2548
2588
|
}
|
|
2549
2589
|
try {
|
|
2550
|
-
const makefile = await fsp3.readFile(
|
|
2590
|
+
const makefile = await fsp3.readFile(path8.join(root, "Makefile"), "utf8");
|
|
2551
2591
|
const targets = parseMakeTargets(makefile);
|
|
2552
2592
|
facts.build ??= targets.has("build") ? "make build" : "make";
|
|
2553
2593
|
if (targets.has("test")) facts.test ??= "make test";
|
|
@@ -2729,8 +2769,16 @@ function buildAutonomyCommand(opts) {
|
|
|
2729
2769
|
const goal = await loadGoal(goalFilePath(opts.projectRoot));
|
|
2730
2770
|
if (goal) {
|
|
2731
2771
|
const u = summarizeUsage(goal);
|
|
2732
|
-
lines.push(
|
|
2733
|
-
|
|
2772
|
+
lines.push(
|
|
2773
|
+
color.dim(
|
|
2774
|
+
` Goal: ${goal.goal.length > 80 ? `${goal.goal.slice(0, 77)}\u2026` : goal.goal}`
|
|
2775
|
+
)
|
|
2776
|
+
);
|
|
2777
|
+
lines.push(
|
|
2778
|
+
color.dim(
|
|
2779
|
+
` Engine state: ${goal.engineState} \xB7 iterations: ${goal.iterations} \xB7 journal: ${goal.journal.length}`
|
|
2780
|
+
)
|
|
2781
|
+
);
|
|
2734
2782
|
if (u.iterationsWithUsage > 0) {
|
|
2735
2783
|
lines.push(
|
|
2736
2784
|
color.dim(
|
|
@@ -2741,7 +2789,9 @@ function buildAutonomyCommand(opts) {
|
|
|
2741
2789
|
const recent = goal.journal.slice(-10);
|
|
2742
2790
|
const failed = recent.filter((e) => e.status === "failure").length;
|
|
2743
2791
|
if (failed > 0) {
|
|
2744
|
-
lines.push(
|
|
2792
|
+
lines.push(
|
|
2793
|
+
color.amber(` Recent failures: ${failed} of last ${recent.length} iterations`)
|
|
2794
|
+
);
|
|
2745
2795
|
}
|
|
2746
2796
|
}
|
|
2747
2797
|
} catch {
|
|
@@ -2775,7 +2825,7 @@ function buildAutonomyCommand(opts) {
|
|
|
2775
2825
|
}
|
|
2776
2826
|
} catch {
|
|
2777
2827
|
}
|
|
2778
|
-
const msg2 = `${color.amber("Eternal/parallel mode stop requested.")}
|
|
2828
|
+
const msg2 = `${color.amber("Eternal/parallel mode stop requested.")} In-flight eternal work is cancelled; parallel fan-out stops after the current tick cleans up.${summaryLine}`;
|
|
2779
2829
|
opts.renderer.write(msg2);
|
|
2780
2830
|
return { message: msg2 };
|
|
2781
2831
|
}
|
|
@@ -2792,7 +2842,7 @@ function buildAutonomyCommand(opts) {
|
|
|
2792
2842
|
newMode = "eternal-parallel";
|
|
2793
2843
|
} else if (arg === "toggle" || arg === "cycle") {
|
|
2794
2844
|
const current = opts.onAutonomy() ?? "off";
|
|
2795
|
-
const cycle = ["off", "suggest", "auto", "eternal"];
|
|
2845
|
+
const cycle = ["off", "suggest", "auto", "eternal", "eternal-parallel"];
|
|
2796
2846
|
newMode = cycle[(cycle.indexOf(current) + 1) % cycle.length] ?? "off";
|
|
2797
2847
|
} else {
|
|
2798
2848
|
const msg2 = `Unknown argument: ${arg}. Use /autonomy on, off, suggest, eternal, parallel, stop, or toggle.`;
|
|
@@ -2907,7 +2957,7 @@ function formatPhaseList(graph) {
|
|
|
2907
2957
|
}
|
|
2908
2958
|
async function gatherProjectContext(projectRoot) {
|
|
2909
2959
|
try {
|
|
2910
|
-
const raw = await fsp3.readFile(
|
|
2960
|
+
const raw = await fsp3.readFile(path8.join(projectRoot, "package.json"), "utf8");
|
|
2911
2961
|
const pkg = JSON.parse(raw);
|
|
2912
2962
|
const parts = [
|
|
2913
2963
|
`Project: ${String(pkg.name ?? "unknown")}`,
|
|
@@ -4771,8 +4821,8 @@ function buildInitCommand(opts) {
|
|
|
4771
4821
|
name: "init",
|
|
4772
4822
|
description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
|
|
4773
4823
|
async run(_args, ctx) {
|
|
4774
|
-
const dir =
|
|
4775
|
-
const file =
|
|
4824
|
+
const dir = path8.join(ctx.projectRoot, ".wrongstack");
|
|
4825
|
+
const file = path8.join(dir, "AGENTS.md");
|
|
4776
4826
|
const detected = await detectProjectFacts(ctx.projectRoot);
|
|
4777
4827
|
const body = renderAgentsTemplate(detected);
|
|
4778
4828
|
await fsp3.mkdir(dir, { recursive: true });
|
|
@@ -4963,18 +5013,18 @@ function stateBadge(state) {
|
|
|
4963
5013
|
return color.dim(state);
|
|
4964
5014
|
}
|
|
4965
5015
|
}
|
|
4966
|
-
async function readConfig(
|
|
5016
|
+
async function readConfig(path25) {
|
|
4967
5017
|
try {
|
|
4968
|
-
return JSON.parse(await fsp3.readFile(
|
|
5018
|
+
return JSON.parse(await fsp3.readFile(path25, "utf8"));
|
|
4969
5019
|
} catch {
|
|
4970
5020
|
return {};
|
|
4971
5021
|
}
|
|
4972
5022
|
}
|
|
4973
|
-
async function writeConfig(
|
|
5023
|
+
async function writeConfig(path25, cfg) {
|
|
4974
5024
|
const raw = JSON.stringify(cfg, null, 2);
|
|
4975
|
-
const tmp =
|
|
5025
|
+
const tmp = path25 + ".tmp";
|
|
4976
5026
|
await fsp3.writeFile(tmp, raw, "utf8");
|
|
4977
|
-
await fsp3.rename(tmp,
|
|
5027
|
+
await fsp3.rename(tmp, path25);
|
|
4978
5028
|
}
|
|
4979
5029
|
|
|
4980
5030
|
// src/slash-commands/mcp.ts
|
|
@@ -5157,6 +5207,224 @@ ${targetMode.description}`
|
|
|
5157
5207
|
}
|
|
5158
5208
|
};
|
|
5159
5209
|
}
|
|
5210
|
+
var noOpVault = {
|
|
5211
|
+
encrypt: (v) => v,
|
|
5212
|
+
decrypt: (v) => v,
|
|
5213
|
+
isEncrypted: () => false
|
|
5214
|
+
};
|
|
5215
|
+
async function patchGlobalConfig(globalConfigPath, mutate) {
|
|
5216
|
+
let raw = "{}";
|
|
5217
|
+
let fileExists = true;
|
|
5218
|
+
try {
|
|
5219
|
+
raw = await fsp3.readFile(globalConfigPath, "utf8");
|
|
5220
|
+
} catch (err) {
|
|
5221
|
+
if (err.code !== "ENOENT") throw err;
|
|
5222
|
+
fileExists = false;
|
|
5223
|
+
}
|
|
5224
|
+
let parsed;
|
|
5225
|
+
try {
|
|
5226
|
+
parsed = JSON.parse(raw);
|
|
5227
|
+
} catch (err) {
|
|
5228
|
+
if (fileExists) {
|
|
5229
|
+
throw new Error(`Config at ${globalConfigPath} is not valid JSON: ${err.message}`);
|
|
5230
|
+
}
|
|
5231
|
+
parsed = {};
|
|
5232
|
+
}
|
|
5233
|
+
const decrypted = decryptConfigSecrets$1(parsed, noOpVault);
|
|
5234
|
+
mutate(decrypted);
|
|
5235
|
+
const encrypted = encryptConfigSecrets$1(decrypted, noOpVault);
|
|
5236
|
+
await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
5237
|
+
return decrypted;
|
|
5238
|
+
}
|
|
5239
|
+
function fmtModel(id, def) {
|
|
5240
|
+
const parts = [];
|
|
5241
|
+
if (def.provider) parts.push(`${color.dim("provider:")} ${color.cyan(def.provider)}`);
|
|
5242
|
+
if (def.name) parts.push(`${color.dim("name:")} ${def.name}`);
|
|
5243
|
+
const caps = def.capabilities;
|
|
5244
|
+
if (caps) {
|
|
5245
|
+
if (caps.maxContext) parts.push(`${color.dim("maxContext:")} ${color.yellow(String(caps.maxContext))}`);
|
|
5246
|
+
const flags = [];
|
|
5247
|
+
if (caps.tools) flags.push("tools");
|
|
5248
|
+
if (caps.vision) flags.push("vision");
|
|
5249
|
+
if (caps.reasoning) flags.push("reasoning");
|
|
5250
|
+
if (caps.streaming) flags.push("streaming");
|
|
5251
|
+
if (caps.jsonMode) flags.push("json");
|
|
5252
|
+
if (flags.length) parts.push(`${color.dim("caps:")} ${flags.join(", ")}`);
|
|
5253
|
+
}
|
|
5254
|
+
if (def.maxOutput) parts.push(`${color.dim("maxOutput:")} ${color.yellow(String(def.maxOutput))}`);
|
|
5255
|
+
return ` ${color.amber(id)} ${parts.join(" ")}`;
|
|
5256
|
+
}
|
|
5257
|
+
function safeAt(arr, idx) {
|
|
5258
|
+
const v = arr[idx];
|
|
5259
|
+
if (v === void 0) throw new Error(`Missing value at position ${idx}`);
|
|
5260
|
+
return v;
|
|
5261
|
+
}
|
|
5262
|
+
function parseFlags(tokens) {
|
|
5263
|
+
let modelId = "";
|
|
5264
|
+
const caps = {};
|
|
5265
|
+
let provider;
|
|
5266
|
+
let name;
|
|
5267
|
+
let maxOutput;
|
|
5268
|
+
let i = 0;
|
|
5269
|
+
while (i < tokens.length) {
|
|
5270
|
+
const t = tokens[i];
|
|
5271
|
+
if (t.startsWith("--")) {
|
|
5272
|
+
const key = t.slice(2);
|
|
5273
|
+
switch (key) {
|
|
5274
|
+
case "provider":
|
|
5275
|
+
provider = safeAt(tokens, ++i);
|
|
5276
|
+
break;
|
|
5277
|
+
case "name":
|
|
5278
|
+
name = safeAt(tokens, ++i);
|
|
5279
|
+
break;
|
|
5280
|
+
case "max-context":
|
|
5281
|
+
caps.maxContext = Number(safeAt(tokens, ++i));
|
|
5282
|
+
break;
|
|
5283
|
+
case "max-output":
|
|
5284
|
+
maxOutput = Number(safeAt(tokens, ++i));
|
|
5285
|
+
break;
|
|
5286
|
+
case "tools":
|
|
5287
|
+
caps.tools = true;
|
|
5288
|
+
break;
|
|
5289
|
+
case "vision":
|
|
5290
|
+
caps.vision = true;
|
|
5291
|
+
break;
|
|
5292
|
+
case "streaming":
|
|
5293
|
+
caps.streaming = true;
|
|
5294
|
+
break;
|
|
5295
|
+
case "reasoning":
|
|
5296
|
+
caps.reasoning = true;
|
|
5297
|
+
break;
|
|
5298
|
+
case "json-mode":
|
|
5299
|
+
caps.jsonMode = true;
|
|
5300
|
+
break;
|
|
5301
|
+
default:
|
|
5302
|
+
return { modelId: "", error: `Unknown flag: --${key}` };
|
|
5303
|
+
}
|
|
5304
|
+
} else if (!t.startsWith("-") && !modelId) {
|
|
5305
|
+
modelId = t;
|
|
5306
|
+
}
|
|
5307
|
+
i++;
|
|
5308
|
+
}
|
|
5309
|
+
if (!modelId) return { modelId: "", error: "missing model id" };
|
|
5310
|
+
const hasCaps = Object.keys(caps).length > 0;
|
|
5311
|
+
const def = {};
|
|
5312
|
+
if (provider !== void 0) def.provider = provider;
|
|
5313
|
+
if (name !== void 0) def.name = name;
|
|
5314
|
+
if (maxOutput !== void 0) def.maxOutput = maxOutput;
|
|
5315
|
+
if (hasCaps) def.capabilities = caps;
|
|
5316
|
+
return { modelId, def: Object.keys(def).length ? def : void 0 };
|
|
5317
|
+
}
|
|
5318
|
+
function buildModelsCommand(opts) {
|
|
5319
|
+
const help = [
|
|
5320
|
+
"Usage:",
|
|
5321
|
+
" /models List custom model definitions",
|
|
5322
|
+
" /models add <id> [flags] Add or update a custom model",
|
|
5323
|
+
" /models remove <id> Remove a custom model",
|
|
5324
|
+
"",
|
|
5325
|
+
"Flags for add:",
|
|
5326
|
+
" --provider <id> Owning provider",
|
|
5327
|
+
' --name "Display" Display name',
|
|
5328
|
+
" --max-context <N> Context window override",
|
|
5329
|
+
" --max-output <N> Max output tokens",
|
|
5330
|
+
" --tools Tool-capable",
|
|
5331
|
+
" --vision Vision-capable",
|
|
5332
|
+
" --streaming Streaming support",
|
|
5333
|
+
" --reasoning Reasoning support",
|
|
5334
|
+
" --json-mode JSON mode support",
|
|
5335
|
+
"",
|
|
5336
|
+
"Persisted to ~/.wrongstack/config.json."
|
|
5337
|
+
].join("\n");
|
|
5338
|
+
return {
|
|
5339
|
+
name: "models",
|
|
5340
|
+
description: "Manage custom model definitions.",
|
|
5341
|
+
help,
|
|
5342
|
+
async run(args) {
|
|
5343
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
5344
|
+
const sub = (parts[0] ?? "").toLowerCase();
|
|
5345
|
+
if (sub === "help" || sub === "--help") return { message: help };
|
|
5346
|
+
if (!opts.configStore || !opts.paths) {
|
|
5347
|
+
return { message: `${color.red("Error")} config store not available.` };
|
|
5348
|
+
}
|
|
5349
|
+
const config = opts.configStore.get();
|
|
5350
|
+
const globalConfigPath = opts.paths.globalConfig;
|
|
5351
|
+
if (!sub) {
|
|
5352
|
+
const models = config.models ?? {};
|
|
5353
|
+
const ids = Object.keys(models);
|
|
5354
|
+
if (ids.length === 0) {
|
|
5355
|
+
return {
|
|
5356
|
+
message: [
|
|
5357
|
+
`${color.bold("Custom Models")} ${color.dim("(none defined)")}`,
|
|
5358
|
+
"",
|
|
5359
|
+
color.dim(" Add one: /models add <id> --max-context 128000 --tools")
|
|
5360
|
+
].join("\n")
|
|
5361
|
+
};
|
|
5362
|
+
}
|
|
5363
|
+
return {
|
|
5364
|
+
message: [
|
|
5365
|
+
`${color.bold("Custom Models")} ${color.dim(`(${ids.length})`)}`,
|
|
5366
|
+
...ids.sort().map((id) => fmtModel(id, models[id]))
|
|
5367
|
+
].join("\n")
|
|
5368
|
+
};
|
|
5369
|
+
}
|
|
5370
|
+
try {
|
|
5371
|
+
if (sub === "add") {
|
|
5372
|
+
const { modelId, def, error } = parseFlags(parts.slice(1));
|
|
5373
|
+
if (error) {
|
|
5374
|
+
return { message: `${color.red("Error")}: ${error}. ${color.dim("/models help")}` };
|
|
5375
|
+
}
|
|
5376
|
+
if (!def && !error) {
|
|
5377
|
+
return { message: `${color.amber("Usage:")} /models add <id> [--max-context N] [--tools] ... ${color.dim("/models help")}` };
|
|
5378
|
+
}
|
|
5379
|
+
const existingModels = config.models ?? {};
|
|
5380
|
+
const existed = modelId in existingModels;
|
|
5381
|
+
const decrypted = await patchGlobalConfig(globalConfigPath, (cfg) => {
|
|
5382
|
+
const models = { ...cfg.models ?? {} };
|
|
5383
|
+
models[modelId] = {
|
|
5384
|
+
...models[modelId],
|
|
5385
|
+
...def,
|
|
5386
|
+
capabilities: {
|
|
5387
|
+
...models[modelId]?.capabilities,
|
|
5388
|
+
...def?.capabilities
|
|
5389
|
+
}
|
|
5390
|
+
};
|
|
5391
|
+
cfg.models = models;
|
|
5392
|
+
});
|
|
5393
|
+
opts.configStore.update({
|
|
5394
|
+
models: decrypted.models
|
|
5395
|
+
});
|
|
5396
|
+
return { message: `${color.green("\u2713")} ${color.amber(modelId)} ${existed ? "updated" : "added"}.` };
|
|
5397
|
+
}
|
|
5398
|
+
if (sub === "remove" || sub === "rm") {
|
|
5399
|
+
const modelId = parts[1];
|
|
5400
|
+
if (!modelId) {
|
|
5401
|
+
return { message: `${color.amber("Usage:")} /models remove <id>` };
|
|
5402
|
+
}
|
|
5403
|
+
const existing = config.models ?? {};
|
|
5404
|
+
if (!(modelId in existing)) {
|
|
5405
|
+
return { message: `${color.amber("Not found")}: custom model "${modelId}" is not defined.` };
|
|
5406
|
+
}
|
|
5407
|
+
const decrypted = await patchGlobalConfig(globalConfigPath, (cfg) => {
|
|
5408
|
+
const models = { ...cfg.models ?? {} };
|
|
5409
|
+
delete models[modelId];
|
|
5410
|
+
cfg.models = models;
|
|
5411
|
+
});
|
|
5412
|
+
opts.configStore.update({
|
|
5413
|
+
models: decrypted.models
|
|
5414
|
+
});
|
|
5415
|
+
return { message: `${color.green("\u2713")} removed ${color.amber(modelId)}` };
|
|
5416
|
+
}
|
|
5417
|
+
return {
|
|
5418
|
+
message: `${color.red("Unknown subcommand")} "${sub}". Try ${color.dim("/models")}, ${color.dim("/models add")}, or ${color.dim("/models help")}.`
|
|
5419
|
+
};
|
|
5420
|
+
} catch (err) {
|
|
5421
|
+
return {
|
|
5422
|
+
message: `${color.red("models error")}: ${err instanceof Error ? err.message : String(err)}`
|
|
5423
|
+
};
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5426
|
+
};
|
|
5427
|
+
}
|
|
5160
5428
|
function buildNextCommand(opts) {
|
|
5161
5429
|
return {
|
|
5162
5430
|
name: "next",
|
|
@@ -5414,7 +5682,7 @@ function summariseEvent(ev) {
|
|
|
5414
5682
|
return color.dim("\u2026");
|
|
5415
5683
|
}
|
|
5416
5684
|
}
|
|
5417
|
-
var
|
|
5685
|
+
var noOpVault2 = {
|
|
5418
5686
|
encrypt: (v) => v,
|
|
5419
5687
|
decrypt: (v) => v,
|
|
5420
5688
|
isEncrypted: () => false
|
|
@@ -5449,7 +5717,7 @@ function parseTarget(tokens) {
|
|
|
5449
5717
|
function fmtEntry(e) {
|
|
5450
5718
|
return e.provider ? `${e.provider}/${e.model}` : `${e.model} ${color.dim("(leader provider)")}`;
|
|
5451
5719
|
}
|
|
5452
|
-
async function
|
|
5720
|
+
async function patchGlobalConfig2(globalConfigPath, mutate) {
|
|
5453
5721
|
let raw = "{}";
|
|
5454
5722
|
let fileExists = true;
|
|
5455
5723
|
try {
|
|
@@ -5466,9 +5734,9 @@ async function patchGlobalConfig(globalConfigPath, mutate) {
|
|
|
5466
5734
|
throw new Error(`Config at ${globalConfigPath} is not valid JSON: ${err.message}`);
|
|
5467
5735
|
parsed = {};
|
|
5468
5736
|
}
|
|
5469
|
-
const decrypted = decryptConfigSecrets$1(parsed,
|
|
5737
|
+
const decrypted = decryptConfigSecrets$1(parsed, noOpVault2);
|
|
5470
5738
|
mutate(decrypted);
|
|
5471
|
-
const encrypted = encryptConfigSecrets$1(decrypted,
|
|
5739
|
+
const encrypted = encryptConfigSecrets$1(decrypted, noOpVault2);
|
|
5472
5740
|
await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
5473
5741
|
return decrypted;
|
|
5474
5742
|
}
|
|
@@ -5559,7 +5827,7 @@ function buildSetModelCommand(opts) {
|
|
|
5559
5827
|
message: `${color.red("Provider not available")}: "${provider}". Keyed: ${keyed.join(", ") || "(none)"}. ${color.dim("/setmodel list")}`
|
|
5560
5828
|
};
|
|
5561
5829
|
}
|
|
5562
|
-
const decrypted = await
|
|
5830
|
+
const decrypted = await patchGlobalConfig2(globalConfigPath, (cfg) => {
|
|
5563
5831
|
cfg.provider = provider;
|
|
5564
5832
|
cfg.model = model;
|
|
5565
5833
|
});
|
|
@@ -5590,7 +5858,7 @@ function buildSetModelCommand(opts) {
|
|
|
5590
5858
|
message: `${color.red("Provider not available")}: "${parsed.provider}". Keyed: ${keyed.join(", ") || "(none)"}.`
|
|
5591
5859
|
};
|
|
5592
5860
|
}
|
|
5593
|
-
const decrypted = await
|
|
5861
|
+
const decrypted = await patchGlobalConfig2(globalConfigPath, (cfg) => {
|
|
5594
5862
|
const matrix = { ...cfg.modelMatrix ?? {} };
|
|
5595
5863
|
matrix[key] = parsed.provider ? { provider: parsed.provider, model: parsed.model } : { model: parsed.model };
|
|
5596
5864
|
cfg.modelMatrix = matrix;
|
|
@@ -5607,7 +5875,7 @@ function buildSetModelCommand(opts) {
|
|
|
5607
5875
|
if (!(key in existing)) {
|
|
5608
5876
|
return { message: `${color.amber("No matrix entry")} for "${key}".` };
|
|
5609
5877
|
}
|
|
5610
|
-
const decrypted = await
|
|
5878
|
+
const decrypted = await patchGlobalConfig2(globalConfigPath, (cfg) => {
|
|
5611
5879
|
const matrix = { ...cfg.modelMatrix ?? {} };
|
|
5612
5880
|
delete matrix[key];
|
|
5613
5881
|
cfg.modelMatrix = matrix;
|
|
@@ -5659,7 +5927,7 @@ async function persistAutonomySetting(deps, mutator) {
|
|
|
5659
5927
|
}
|
|
5660
5928
|
|
|
5661
5929
|
// src/slash-commands/settings.ts
|
|
5662
|
-
var
|
|
5930
|
+
var noOpVault3 = {
|
|
5663
5931
|
encrypt: (v) => v,
|
|
5664
5932
|
decrypt: (v) => v,
|
|
5665
5933
|
isEncrypted: () => false
|
|
@@ -5724,7 +5992,7 @@ function buildSettingsCommand(opts) {
|
|
|
5724
5992
|
const persistDeps = {
|
|
5725
5993
|
configStore: opts.configStore,
|
|
5726
5994
|
globalConfigPath: opts.paths.globalConfig,
|
|
5727
|
-
vault:
|
|
5995
|
+
vault: noOpVault3
|
|
5728
5996
|
};
|
|
5729
5997
|
try {
|
|
5730
5998
|
if (sub === "delay") {
|
|
@@ -5854,7 +6122,7 @@ var DEFAULTS = {
|
|
|
5854
6122
|
cost: true
|
|
5855
6123
|
};
|
|
5856
6124
|
function resolveConfigPath() {
|
|
5857
|
-
return process.env[CONFIG_ENV] ??
|
|
6125
|
+
return process.env[CONFIG_ENV] ?? path8.join(process.env.HOME ?? "", ".wrongstack", "statusline.json");
|
|
5858
6126
|
}
|
|
5859
6127
|
async function loadStatuslineConfig() {
|
|
5860
6128
|
const p = resolveConfigPath();
|
|
@@ -5868,7 +6136,7 @@ async function loadStatuslineConfig() {
|
|
|
5868
6136
|
async function saveStatuslineConfig(cfg) {
|
|
5869
6137
|
const p = resolveConfigPath();
|
|
5870
6138
|
try {
|
|
5871
|
-
await fsp3.mkdir(
|
|
6139
|
+
await fsp3.mkdir(path8.dirname(p), { recursive: true });
|
|
5872
6140
|
await atomicWrite(p, JSON.stringify(cfg, null, 2));
|
|
5873
6141
|
} catch (err) {
|
|
5874
6142
|
throw new FsError({
|
|
@@ -6148,6 +6416,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
6148
6416
|
buildWorktreeCommand(opts),
|
|
6149
6417
|
buildSettingsCommand(opts),
|
|
6150
6418
|
buildSetModelCommand(opts),
|
|
6419
|
+
buildModelsCommand(opts),
|
|
6151
6420
|
buildCollabCommand(opts),
|
|
6152
6421
|
buildStatuslineCommand({
|
|
6153
6422
|
cwd: opts.cwd,
|
|
@@ -6176,13 +6445,13 @@ var MANIFESTS = [
|
|
|
6176
6445
|
];
|
|
6177
6446
|
async function detectProjectKind(projectRoot) {
|
|
6178
6447
|
try {
|
|
6179
|
-
await fsp3.access(
|
|
6448
|
+
await fsp3.access(path8.join(projectRoot, ".wrongstack", "AGENTS.md"));
|
|
6180
6449
|
return "initialized";
|
|
6181
6450
|
} catch {
|
|
6182
6451
|
}
|
|
6183
6452
|
for (const m of MANIFESTS) {
|
|
6184
6453
|
try {
|
|
6185
|
-
await fsp3.access(
|
|
6454
|
+
await fsp3.access(path8.join(projectRoot, m));
|
|
6186
6455
|
return "project";
|
|
6187
6456
|
} catch {
|
|
6188
6457
|
}
|
|
@@ -6190,8 +6459,8 @@ async function detectProjectKind(projectRoot) {
|
|
|
6190
6459
|
return "empty";
|
|
6191
6460
|
}
|
|
6192
6461
|
async function scaffoldAgentsMd(projectRoot) {
|
|
6193
|
-
const dir =
|
|
6194
|
-
const file =
|
|
6462
|
+
const dir = path8.join(projectRoot, ".wrongstack");
|
|
6463
|
+
const file = path8.join(dir, "AGENTS.md");
|
|
6195
6464
|
const facts = await detectProjectFacts(projectRoot);
|
|
6196
6465
|
const body = renderAgentsTemplate(facts);
|
|
6197
6466
|
await fsp3.mkdir(dir, { recursive: true });
|
|
@@ -6204,7 +6473,7 @@ async function runProjectCheck(opts) {
|
|
|
6204
6473
|
if (kind === "initialized") {
|
|
6205
6474
|
renderer.write(
|
|
6206
6475
|
`
|
|
6207
|
-
${color.green("\u2713")} Project initialized ${color.dim(`(${
|
|
6476
|
+
${color.green("\u2713")} Project initialized ${color.dim(`(${path8.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
|
|
6208
6477
|
`
|
|
6209
6478
|
);
|
|
6210
6479
|
return true;
|
|
@@ -6235,7 +6504,7 @@ async function runProjectCheck(opts) {
|
|
|
6235
6504
|
}
|
|
6236
6505
|
return true;
|
|
6237
6506
|
}
|
|
6238
|
-
const gitDir =
|
|
6507
|
+
const gitDir = path8.join(projectRoot, ".git");
|
|
6239
6508
|
let hasGit = false;
|
|
6240
6509
|
try {
|
|
6241
6510
|
await fsp3.access(gitDir);
|
|
@@ -6258,10 +6527,10 @@ async function runProjectCheck(opts) {
|
|
|
6258
6527
|
if (answer2 === "y" || answer2 === "yes") {
|
|
6259
6528
|
try {
|
|
6260
6529
|
const { spawn: spawn3 } = await import('child_process');
|
|
6261
|
-
await new Promise((
|
|
6530
|
+
await new Promise((resolve3, reject) => {
|
|
6262
6531
|
const child = spawn3("git", ["init"], { cwd: projectRoot });
|
|
6263
6532
|
child.on("error", reject);
|
|
6264
|
-
child.on("close", (code) => code === 0 ?
|
|
6533
|
+
child.on("close", (code) => code === 0 ? resolve3() : reject(new Error(`git init failed with ${code}`)));
|
|
6265
6534
|
});
|
|
6266
6535
|
renderer.write(` ${color.green("\u2713")} Git repository initialized
|
|
6267
6536
|
`);
|
|
@@ -6360,78 +6629,21 @@ async function runLaunchPrompts(opts) {
|
|
|
6360
6629
|
return { mode, yolo, director, autonomy };
|
|
6361
6630
|
}
|
|
6362
6631
|
async function bootConfig(flags) {
|
|
6363
|
-
const
|
|
6364
|
-
const pathResolver =
|
|
6365
|
-
const projectRoot = pathResolver.projectRoot;
|
|
6366
|
-
const userHome = os3.homedir();
|
|
6367
|
-
const wpaths = resolveWstackPaths({ projectRoot, userHome });
|
|
6368
|
-
await ensureProjectMeta(wpaths, projectRoot);
|
|
6369
|
-
const vault = new DefaultSecretVault$1({ keyFile: wpaths.secretsKey });
|
|
6370
|
-
for (const file of [wpaths.globalConfig, wpaths.projectLocalConfig]) {
|
|
6371
|
-
try {
|
|
6372
|
-
const { migrated } = await migratePlaintextSecrets(file, vault);
|
|
6373
|
-
if (migrated > 0) {
|
|
6374
|
-
writeErr(`[wstack] Encrypted ${migrated} plaintext secret(s) in ${file}
|
|
6375
|
-
`);
|
|
6376
|
-
}
|
|
6377
|
-
} catch {
|
|
6378
|
-
}
|
|
6379
|
-
}
|
|
6380
|
-
const configLoader = new DefaultConfigLoader({ paths: wpaths, vault });
|
|
6381
|
-
let config = await configLoader.load({ cliFlags: flagsToConfigPatch(flags) });
|
|
6382
|
-
const syncConfig = await configLoader.loadSyncConfig();
|
|
6383
|
-
if (syncConfig) {
|
|
6384
|
-
config = Object.freeze({ ...config, sync: syncConfig });
|
|
6385
|
-
}
|
|
6632
|
+
const opts = { flags, appLabel: "wstack" };
|
|
6633
|
+
const { cwd, projectRoot, userHome, wpaths, pathResolver, config, vault } = await bootConfig$1(opts);
|
|
6386
6634
|
return {
|
|
6387
6635
|
paths: { cwd, projectRoot, userHome, wpaths, pathResolver },
|
|
6388
6636
|
config,
|
|
6389
6637
|
vault
|
|
6390
6638
|
};
|
|
6391
6639
|
}
|
|
6392
|
-
function flagsToConfigPatch(flags) {
|
|
6393
|
-
const patch = {};
|
|
6394
|
-
if (typeof flags["provider"] === "string") patch.provider = flags["provider"];
|
|
6395
|
-
if (typeof flags["model"] === "string") patch.model = flags["model"];
|
|
6396
|
-
if (typeof flags["cwd"] === "string") patch.cwd = flags["cwd"];
|
|
6397
|
-
if (typeof flags["log-level"] === "string") {
|
|
6398
|
-
patch.log = { level: flags["log-level"] };
|
|
6399
|
-
} else if (flags["verbose"]) {
|
|
6400
|
-
patch.log = { level: "debug" };
|
|
6401
|
-
} else if (flags["trace"]) {
|
|
6402
|
-
patch.log = { level: "trace" };
|
|
6403
|
-
}
|
|
6404
|
-
if (flags["yolo"]) patch.yolo = true;
|
|
6405
|
-
if (flags["no-features"]) {
|
|
6406
|
-
patch.features = {
|
|
6407
|
-
mcp: false,
|
|
6408
|
-
plugins: false,
|
|
6409
|
-
memory: false,
|
|
6410
|
-
modelsRegistry: false,
|
|
6411
|
-
skills: false
|
|
6412
|
-
};
|
|
6413
|
-
}
|
|
6414
|
-
return patch;
|
|
6415
|
-
}
|
|
6416
|
-
async function ensureProjectMeta(paths, projectRoot) {
|
|
6417
|
-
try {
|
|
6418
|
-
await fsp3.mkdir(paths.projectDir, { recursive: true });
|
|
6419
|
-
const meta = {
|
|
6420
|
-
hash: paths.projectHash,
|
|
6421
|
-
root: projectRoot,
|
|
6422
|
-
lastSeen: (/* @__PURE__ */ new Date()).toISOString()
|
|
6423
|
-
};
|
|
6424
|
-
await fsp3.writeFile(paths.projectMeta, JSON.stringify(meta, null, 2));
|
|
6425
|
-
} catch {
|
|
6426
|
-
}
|
|
6427
|
-
}
|
|
6428
6640
|
var ReadlineInputReader = class {
|
|
6429
6641
|
rl;
|
|
6430
6642
|
historyFile;
|
|
6431
6643
|
history = [];
|
|
6432
6644
|
pending = false;
|
|
6433
6645
|
constructor(opts = {}) {
|
|
6434
|
-
this.historyFile = opts.historyFile ??
|
|
6646
|
+
this.historyFile = opts.historyFile ?? path8.join(os2.homedir(), ".wrongstack", "history");
|
|
6435
6647
|
}
|
|
6436
6648
|
async loadHistory() {
|
|
6437
6649
|
try {
|
|
@@ -6443,7 +6655,7 @@ var ReadlineInputReader = class {
|
|
|
6443
6655
|
}
|
|
6444
6656
|
async saveHistory() {
|
|
6445
6657
|
try {
|
|
6446
|
-
await fsp3.mkdir(
|
|
6658
|
+
await fsp3.mkdir(path8.dirname(this.historyFile), { recursive: true });
|
|
6447
6659
|
await fsp3.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
|
|
6448
6660
|
} catch {
|
|
6449
6661
|
}
|
|
@@ -6462,7 +6674,7 @@ var ReadlineInputReader = class {
|
|
|
6462
6674
|
async readLine(prompt) {
|
|
6463
6675
|
if (this.history.length === 0) await this.loadHistory();
|
|
6464
6676
|
while (this.pending) {
|
|
6465
|
-
await new Promise((
|
|
6677
|
+
await new Promise((resolve3) => setTimeout(resolve3, 50));
|
|
6466
6678
|
}
|
|
6467
6679
|
this.pending = true;
|
|
6468
6680
|
try {
|
|
@@ -6472,15 +6684,15 @@ var ReadlineInputReader = class {
|
|
|
6472
6684
|
this.rl = void 0;
|
|
6473
6685
|
}
|
|
6474
6686
|
const fresh = this.ensure();
|
|
6475
|
-
return new Promise((
|
|
6687
|
+
return new Promise((resolve3) => {
|
|
6476
6688
|
fresh.question(prompt ?? "> ", (line) => {
|
|
6477
6689
|
if (line.trim()) {
|
|
6478
6690
|
this.history.push(line);
|
|
6479
6691
|
void this.saveHistory();
|
|
6480
6692
|
}
|
|
6481
|
-
|
|
6693
|
+
resolve3(line);
|
|
6482
6694
|
});
|
|
6483
|
-
fresh.once("close", () =>
|
|
6695
|
+
fresh.once("close", () => resolve3(""));
|
|
6484
6696
|
}).then((result) => {
|
|
6485
6697
|
this.rl?.close();
|
|
6486
6698
|
this.rl = void 0;
|
|
@@ -6492,7 +6704,7 @@ var ReadlineInputReader = class {
|
|
|
6492
6704
|
}
|
|
6493
6705
|
async readKey(prompt, options) {
|
|
6494
6706
|
writeOut(prompt);
|
|
6495
|
-
return new Promise((
|
|
6707
|
+
return new Promise((resolve3) => {
|
|
6496
6708
|
const stdin = process.stdin;
|
|
6497
6709
|
const wasRaw = stdin.isRaw;
|
|
6498
6710
|
const wasPaused = stdin.isPaused();
|
|
@@ -6503,7 +6715,7 @@ var ReadlineInputReader = class {
|
|
|
6503
6715
|
if (key === "") {
|
|
6504
6716
|
cleanup();
|
|
6505
6717
|
writeOut("\n");
|
|
6506
|
-
|
|
6718
|
+
resolve3("");
|
|
6507
6719
|
return;
|
|
6508
6720
|
}
|
|
6509
6721
|
const opt = options.find(
|
|
@@ -6513,12 +6725,12 @@ var ReadlineInputReader = class {
|
|
|
6513
6725
|
cleanup();
|
|
6514
6726
|
writeOut(`${opt.key}
|
|
6515
6727
|
`);
|
|
6516
|
-
|
|
6728
|
+
resolve3(opt.value);
|
|
6517
6729
|
}
|
|
6518
6730
|
};
|
|
6519
6731
|
const onClose = () => {
|
|
6520
6732
|
cleanup();
|
|
6521
|
-
|
|
6733
|
+
resolve3("");
|
|
6522
6734
|
};
|
|
6523
6735
|
const cleanup = () => {
|
|
6524
6736
|
stdin.off("data", onData);
|
|
@@ -6546,7 +6758,7 @@ var ReadlineInputReader = class {
|
|
|
6546
6758
|
this.rl?.close();
|
|
6547
6759
|
this.rl = void 0;
|
|
6548
6760
|
writeOut(prompt);
|
|
6549
|
-
return new Promise((
|
|
6761
|
+
return new Promise((resolve3) => {
|
|
6550
6762
|
let buf = "";
|
|
6551
6763
|
const wasRaw = stdin.isRaw;
|
|
6552
6764
|
setRawMode(stdin, true);
|
|
@@ -6564,7 +6776,7 @@ var ReadlineInputReader = class {
|
|
|
6564
6776
|
cleanup();
|
|
6565
6777
|
writeOut(` ${dim(`[${buf.length} chars]`)}
|
|
6566
6778
|
`);
|
|
6567
|
-
|
|
6779
|
+
resolve3(buf);
|
|
6568
6780
|
return;
|
|
6569
6781
|
}
|
|
6570
6782
|
if (ch === "") {
|
|
@@ -6661,7 +6873,7 @@ async function buildPickableProviders(modelsRegistry, config) {
|
|
|
6661
6873
|
}
|
|
6662
6874
|
return out;
|
|
6663
6875
|
}
|
|
6664
|
-
var defaultUidFn = () =>
|
|
6876
|
+
var defaultUidFn = () => os2__default.userInfo().uid;
|
|
6665
6877
|
async function getFileUid(filePath) {
|
|
6666
6878
|
try {
|
|
6667
6879
|
const stat4 = await fsp3.stat(filePath);
|
|
@@ -6671,7 +6883,7 @@ async function getFileUid(filePath) {
|
|
|
6671
6883
|
}
|
|
6672
6884
|
}
|
|
6673
6885
|
async function checkConfigOwnership(homeFn, uidFn = defaultUidFn) {
|
|
6674
|
-
if (
|
|
6886
|
+
if (os2__default.platform() === "win32") return true;
|
|
6675
6887
|
const cfg = configPath(homeFn);
|
|
6676
6888
|
const fileUid = await getFileUid(cfg);
|
|
6677
6889
|
if (fileUid === void 0) return true;
|
|
@@ -6688,20 +6900,20 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
6688
6900
|
if (PROTECTED_BASENAMES.has(filename)) {
|
|
6689
6901
|
throw new Error(`Refusing to delete protected file: ${filename}`);
|
|
6690
6902
|
}
|
|
6691
|
-
if (filename !==
|
|
6903
|
+
if (filename !== path8.basename(filename)) {
|
|
6692
6904
|
throw new Error(`Refusing to delete path with traversal: ${filename}`);
|
|
6693
6905
|
}
|
|
6694
6906
|
if (!filename.startsWith("config.json.") || !filename.endsWith(".bak")) {
|
|
6695
6907
|
throw new Error(`Refusing to delete unknown file: ${filename}`);
|
|
6696
6908
|
}
|
|
6697
|
-
const resolvedParent =
|
|
6909
|
+
const resolvedParent = path8.resolve(parentDir);
|
|
6698
6910
|
if (!resolvedParent.endsWith(".wrongstack")) {
|
|
6699
6911
|
throw new Error(`Unexpected parent directory for bak prune: ${resolvedParent}`);
|
|
6700
6912
|
}
|
|
6701
6913
|
}
|
|
6702
6914
|
async function safeDelete(filePath) {
|
|
6703
|
-
const dir =
|
|
6704
|
-
const filename =
|
|
6915
|
+
const dir = path8.dirname(filePath);
|
|
6916
|
+
const filename = path8.basename(filePath);
|
|
6705
6917
|
try {
|
|
6706
6918
|
assertSafeToDelete(filename, dir);
|
|
6707
6919
|
await fsp3.unlink(filePath);
|
|
@@ -6744,18 +6956,18 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
6744
6956
|
}
|
|
6745
6957
|
return changes.length > 0 ? changes.slice(0, 5).join(", ") : "no changes";
|
|
6746
6958
|
}
|
|
6747
|
-
var defaultHomeDir = () =>
|
|
6959
|
+
var defaultHomeDir = () => os2__default.homedir();
|
|
6748
6960
|
function historyDir(homeFn = defaultHomeDir) {
|
|
6749
|
-
return
|
|
6961
|
+
return path8.join(homeFn(), ".wrongstack", "config.history", "entries");
|
|
6750
6962
|
}
|
|
6751
6963
|
function historyIndexPath(homeFn = defaultHomeDir) {
|
|
6752
|
-
return
|
|
6964
|
+
return path8.join(homeFn(), ".wrongstack", "config.history", "index.json");
|
|
6753
6965
|
}
|
|
6754
6966
|
function configPath(homeFn = defaultHomeDir) {
|
|
6755
|
-
return
|
|
6967
|
+
return path8.join(homeFn(), ".wrongstack", "config.json");
|
|
6756
6968
|
}
|
|
6757
6969
|
function backupLastPath(homeFn = defaultHomeDir) {
|
|
6758
|
-
return
|
|
6970
|
+
return path8.join(homeFn(), ".wrongstack", "config.json.last");
|
|
6759
6971
|
}
|
|
6760
6972
|
function entryId(ts) {
|
|
6761
6973
|
return ts.replace(/[:.]/g, "-").slice(0, 19);
|
|
@@ -6810,17 +7022,17 @@ async function backupCurrent(homeFn = defaultHomeDir) {
|
|
|
6810
7022
|
}
|
|
6811
7023
|
if (content !== void 0) {
|
|
6812
7024
|
try {
|
|
6813
|
-
const bakPath =
|
|
7025
|
+
const bakPath = path8.join(homeFn(), ".wrongstack", `config.json.${ts}.bak`);
|
|
6814
7026
|
await atomicWrite(bakPath, content);
|
|
6815
7027
|
} catch {
|
|
6816
7028
|
}
|
|
6817
7029
|
}
|
|
6818
7030
|
try {
|
|
6819
|
-
const dir =
|
|
7031
|
+
const dir = path8.join(homeFn(), ".wrongstack");
|
|
6820
7032
|
const files = await fsp3.readdir(dir);
|
|
6821
7033
|
const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
|
|
6822
7034
|
for (const f of baks.slice(10)) {
|
|
6823
|
-
await safeDelete(
|
|
7035
|
+
await safeDelete(path8.join(dir, f));
|
|
6824
7036
|
}
|
|
6825
7037
|
} catch {
|
|
6826
7038
|
}
|
|
@@ -6838,7 +7050,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
6838
7050
|
};
|
|
6839
7051
|
try {
|
|
6840
7052
|
await fsp3.writeFile(
|
|
6841
|
-
|
|
7053
|
+
path8.join(historyDir(homeFn), `${id}.json`),
|
|
6842
7054
|
JSON.stringify(entry, null, 2),
|
|
6843
7055
|
"utf8"
|
|
6844
7056
|
);
|
|
@@ -6846,7 +7058,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
6846
7058
|
throw new FsError({
|
|
6847
7059
|
message: err instanceof Error ? err.message : String(err),
|
|
6848
7060
|
code: ERROR_CODES.FS_WRITE_FAILED,
|
|
6849
|
-
path:
|
|
7061
|
+
path: path8.join(historyDir(homeFn), `${id}.json`),
|
|
6850
7062
|
cause: err
|
|
6851
7063
|
});
|
|
6852
7064
|
}
|
|
@@ -6861,7 +7073,7 @@ async function listHistory(homeFn = defaultHomeDir) {
|
|
|
6861
7073
|
}
|
|
6862
7074
|
async function getHistoryEntry(id, homeFn = defaultHomeDir) {
|
|
6863
7075
|
try {
|
|
6864
|
-
const raw = await fsp3.readFile(
|
|
7076
|
+
const raw = await fsp3.readFile(path8.join(historyDir(homeFn), `${id}.json`), "utf8");
|
|
6865
7077
|
return JSON.parse(raw);
|
|
6866
7078
|
} catch {
|
|
6867
7079
|
return null;
|
|
@@ -6926,11 +7138,11 @@ async function restoreLast(homeFn = defaultHomeDir) {
|
|
|
6926
7138
|
var theme = { primary: color.amber };
|
|
6927
7139
|
async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? __require("os").homedir()) {
|
|
6928
7140
|
try {
|
|
6929
|
-
const { atomicWrite:
|
|
6930
|
-
const
|
|
7141
|
+
const { atomicWrite: atomicWrite12 } = await import('@wrongstack/core');
|
|
7142
|
+
const fs24 = await import('fs/promises');
|
|
6931
7143
|
let existing = {};
|
|
6932
7144
|
try {
|
|
6933
|
-
const raw = await
|
|
7145
|
+
const raw = await fs24.readFile(configPath2, "utf8");
|
|
6934
7146
|
existing = JSON.parse(raw);
|
|
6935
7147
|
} catch {
|
|
6936
7148
|
}
|
|
@@ -6938,7 +7150,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
|
|
|
6938
7150
|
existing.provider = provider;
|
|
6939
7151
|
existing.model = model;
|
|
6940
7152
|
await backupCurrent(homeFn);
|
|
6941
|
-
await
|
|
7153
|
+
await atomicWrite12(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
6942
7154
|
try {
|
|
6943
7155
|
await appendHistory(
|
|
6944
7156
|
oldCfg,
|
|
@@ -7265,7 +7477,7 @@ function pickGroupIndex(opts) {
|
|
|
7265
7477
|
if (Number.isFinite(parsed)) current = wrap(parsed);
|
|
7266
7478
|
} catch {
|
|
7267
7479
|
}
|
|
7268
|
-
fs11.mkdirSync(
|
|
7480
|
+
fs11.mkdirSync(path8.dirname(opts.cursorFile), { recursive: true });
|
|
7269
7481
|
fs11.writeFileSync(opts.cursorFile, String(wrap(current + 1)));
|
|
7270
7482
|
return current;
|
|
7271
7483
|
} catch {
|
|
@@ -7601,14 +7813,14 @@ function summarize(value, name) {
|
|
|
7601
7813
|
if (typeof v === "object" && v !== null) {
|
|
7602
7814
|
const o = v;
|
|
7603
7815
|
if (name === "edit") {
|
|
7604
|
-
const
|
|
7816
|
+
const path25 = typeof o["path"] === "string" ? o["path"] : "";
|
|
7605
7817
|
const reps = typeof o["replacements"] === "number" ? o["replacements"] : 0;
|
|
7606
|
-
return `${
|
|
7818
|
+
return `${path25} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
|
|
7607
7819
|
}
|
|
7608
7820
|
if (name === "write") {
|
|
7609
|
-
const
|
|
7821
|
+
const path25 = typeof o["path"] === "string" ? o["path"] : "";
|
|
7610
7822
|
const bytes = typeof o["bytes"] === "number" ? o["bytes"] : void 0;
|
|
7611
|
-
return bytes !== void 0 ? `${
|
|
7823
|
+
return bytes !== void 0 ? `${path25} ${bytes}B` : path25;
|
|
7612
7824
|
}
|
|
7613
7825
|
if (typeof o["count"] === "number") {
|
|
7614
7826
|
return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
|
|
@@ -8468,7 +8680,7 @@ var updateCmd = async (args, deps) => {
|
|
|
8468
8680
|
deps.renderer.write(`Updating wrongstack from v${info.current} to v${info.latest}...
|
|
8469
8681
|
`);
|
|
8470
8682
|
try {
|
|
8471
|
-
const result = await new Promise((
|
|
8683
|
+
const result = await new Promise((resolve3, reject) => {
|
|
8472
8684
|
const child = spawn("npm", ["install", "-g", "wrongstack@latest"], {
|
|
8473
8685
|
cwd,
|
|
8474
8686
|
stdio: "pipe"
|
|
@@ -8478,7 +8690,7 @@ var updateCmd = async (args, deps) => {
|
|
|
8478
8690
|
_stderr += d;
|
|
8479
8691
|
});
|
|
8480
8692
|
child.on("error", reject);
|
|
8481
|
-
child.on("close", (code) =>
|
|
8693
|
+
child.on("close", (code) => resolve3({ code: code ?? 0 }));
|
|
8482
8694
|
});
|
|
8483
8695
|
if (result.code === 0) {
|
|
8484
8696
|
deps.renderer.write(`
|
|
@@ -8539,7 +8751,7 @@ var diagCmd = async (_args, deps) => {
|
|
|
8539
8751
|
` modelsCache: ${deps.paths.modelsCache}`,
|
|
8540
8752
|
` cacheAge: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never"}`,
|
|
8541
8753
|
` node: ${process.version}`,
|
|
8542
|
-
` os: ${
|
|
8754
|
+
` os: ${os2.platform()} ${os2.release()}`,
|
|
8543
8755
|
` provider: ${cfg.provider ?? "<unset>"}`,
|
|
8544
8756
|
` model: ${cfg.model ?? "<unset>"}`,
|
|
8545
8757
|
` tools: ${deps.toolRegistry?.list().length ?? 0}`,
|
|
@@ -8618,7 +8830,7 @@ var doctorCmd = async (_args, deps) => {
|
|
|
8618
8830
|
}
|
|
8619
8831
|
try {
|
|
8620
8832
|
await fsp3.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
8621
|
-
const probe =
|
|
8833
|
+
const probe = path8.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
8622
8834
|
await fsp3.writeFile(probe, "");
|
|
8623
8835
|
await fsp3.unlink(probe);
|
|
8624
8836
|
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
@@ -8721,8 +8933,8 @@ var exportCmd = async (args, deps) => {
|
|
|
8721
8933
|
return 1;
|
|
8722
8934
|
}
|
|
8723
8935
|
if (output) {
|
|
8724
|
-
await fsp3.mkdir(
|
|
8725
|
-
await fsp3.writeFile(
|
|
8936
|
+
await fsp3.mkdir(path8.dirname(path8.resolve(deps.cwd, output)), { recursive: true });
|
|
8937
|
+
await fsp3.writeFile(path8.resolve(deps.cwd, output), rendered, "utf8");
|
|
8726
8938
|
deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
|
|
8727
8939
|
`);
|
|
8728
8940
|
} else {
|
|
@@ -8795,8 +9007,8 @@ var initCmd = async (_args, deps) => {
|
|
|
8795
9007
|
const vault = new DefaultSecretVault({ keyFile: deps.paths.secretsKey });
|
|
8796
9008
|
const encrypted = encryptConfigSecrets(config, vault);
|
|
8797
9009
|
await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
8798
|
-
await fsp3.mkdir(
|
|
8799
|
-
const agentsFile =
|
|
9010
|
+
await fsp3.mkdir(path8.join(deps.projectRoot, ".wrongstack"), { recursive: true });
|
|
9011
|
+
const agentsFile = path8.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
|
|
8800
9012
|
const projectFacts = await detectProjectFacts(deps.projectRoot);
|
|
8801
9013
|
await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
|
|
8802
9014
|
deps.renderer.writeInfo(`Wrote ${deps.paths.globalConfig}`);
|
|
@@ -9102,7 +9314,7 @@ var usageCmd = async (_args, deps) => {
|
|
|
9102
9314
|
return 0;
|
|
9103
9315
|
};
|
|
9104
9316
|
var projectsCmd = async (_args, deps) => {
|
|
9105
|
-
const projectsRoot =
|
|
9317
|
+
const projectsRoot = path8.join(deps.paths.globalRoot, "projects");
|
|
9106
9318
|
try {
|
|
9107
9319
|
const entries = await fsp3.readdir(projectsRoot);
|
|
9108
9320
|
if (entries.length === 0) {
|
|
@@ -9112,7 +9324,7 @@ var projectsCmd = async (_args, deps) => {
|
|
|
9112
9324
|
for (const hash of entries) {
|
|
9113
9325
|
try {
|
|
9114
9326
|
const meta = JSON.parse(
|
|
9115
|
-
await fsp3.readFile(
|
|
9327
|
+
await fsp3.readFile(path8.join(projectsRoot, hash, "meta.json"), "utf8")
|
|
9116
9328
|
);
|
|
9117
9329
|
deps.renderer.write(
|
|
9118
9330
|
` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
|
|
@@ -9173,8 +9385,49 @@ ${color.dim(`Current: ${deps.config.provider ?? "<unset>"} / ${deps.config.model
|
|
|
9173
9385
|
return 1;
|
|
9174
9386
|
}
|
|
9175
9387
|
};
|
|
9388
|
+
function parseFlags2(args) {
|
|
9389
|
+
const flags = {};
|
|
9390
|
+
for (let i = 0; i < args.length; i++) {
|
|
9391
|
+
const a = args[i];
|
|
9392
|
+
if (a.startsWith("--")) {
|
|
9393
|
+
const eq = a.indexOf("=");
|
|
9394
|
+
if (eq !== -1) {
|
|
9395
|
+
flags[a.slice(2, eq)] = a.slice(eq + 1);
|
|
9396
|
+
} else {
|
|
9397
|
+
const name = a.slice(2);
|
|
9398
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
9399
|
+
flags[name] = args[++i] ?? "";
|
|
9400
|
+
} else {
|
|
9401
|
+
flags[name] = true;
|
|
9402
|
+
}
|
|
9403
|
+
}
|
|
9404
|
+
}
|
|
9405
|
+
}
|
|
9406
|
+
return flags;
|
|
9407
|
+
}
|
|
9408
|
+
function positionals(args) {
|
|
9409
|
+
const out = [];
|
|
9410
|
+
for (let i = 0; i < args.length; i++) {
|
|
9411
|
+
const a = args[i];
|
|
9412
|
+
if (a.startsWith("--")) {
|
|
9413
|
+
const eq = a.indexOf("=");
|
|
9414
|
+
if (eq === -1) {
|
|
9415
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
9416
|
+
i++;
|
|
9417
|
+
}
|
|
9418
|
+
}
|
|
9419
|
+
continue;
|
|
9420
|
+
}
|
|
9421
|
+
out.push(a);
|
|
9422
|
+
}
|
|
9423
|
+
return out;
|
|
9424
|
+
}
|
|
9425
|
+
var DEFAULT_PER_PAGE = 15;
|
|
9176
9426
|
var modelsCmd = async (args, deps) => {
|
|
9177
9427
|
const sub = args[0];
|
|
9428
|
+
if (sub === "add") return modelsAdd(args.slice(1), deps);
|
|
9429
|
+
if (sub === "remove") return modelsRemove(args.slice(1), deps);
|
|
9430
|
+
if (sub === "list") return modelsList(args.slice(1), deps);
|
|
9178
9431
|
if (sub === "refresh") {
|
|
9179
9432
|
deps.renderer.writeInfo("Refreshing models.dev cache\u2026");
|
|
9180
9433
|
try {
|
|
@@ -9188,9 +9441,13 @@ var modelsCmd = async (args, deps) => {
|
|
|
9188
9441
|
return 1;
|
|
9189
9442
|
}
|
|
9190
9443
|
}
|
|
9191
|
-
const
|
|
9444
|
+
const flags = parseFlags2(args);
|
|
9445
|
+
const search = typeof flags["search"] === "string" ? flags["search"].toLowerCase() : "";
|
|
9446
|
+
const perPage = Number(flags["per-page"]) > 0 ? Number(flags["per-page"]) : DEFAULT_PER_PAGE;
|
|
9447
|
+
const page = Math.max(1, Number(flags["page"]) || 1);
|
|
9448
|
+
const providerId = sub ?? deps.config.provider ?? "";
|
|
9192
9449
|
if (!providerId) {
|
|
9193
|
-
deps.renderer.writeError("Usage: wstack models <provider>
|
|
9450
|
+
deps.renderer.writeError("Usage: wstack models <provider> [--search <term>] [--page N] [--per-page N]");
|
|
9194
9451
|
return 1;
|
|
9195
9452
|
}
|
|
9196
9453
|
let lookupId = providerId;
|
|
@@ -9214,34 +9471,209 @@ var modelsCmd = async (args, deps) => {
|
|
|
9214
9471
|
`));
|
|
9215
9472
|
const userModels = deps.config.providers?.[providerId]?.models;
|
|
9216
9473
|
const catalogById = new Map(provider.models.map((m) => [m.id, m]));
|
|
9217
|
-
const
|
|
9474
|
+
const allSorted = userModels && userModels.length > 0 ? userModels.map((id) => catalogById.get(id) ?? { id, name: id }) : [...provider.models].sort(
|
|
9218
9475
|
(a, b) => (b.release_date ?? "").localeCompare(a.release_date ?? "")
|
|
9219
9476
|
);
|
|
9220
9477
|
if (userModels && userModels.length > 0)
|
|
9221
9478
|
deps.renderer.write(color.dim(`(${userModels.length} model(s) from your saved config)
|
|
9222
9479
|
`));
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9480
|
+
const filtered = search ? allSorted.filter((m) => m.id.toLowerCase().includes(search)) : allSorted;
|
|
9481
|
+
const total = filtered.length;
|
|
9482
|
+
const totalPages = Math.max(1, Math.ceil(total / perPage));
|
|
9483
|
+
const actualPage = Math.min(page, totalPages);
|
|
9484
|
+
const start = (actualPage - 1) * perPage;
|
|
9485
|
+
const pageItems = filtered.slice(start, start + perPage);
|
|
9486
|
+
const end = Math.min(start + pageItems.length, total);
|
|
9487
|
+
const pageHint = totalPages > 1 ? color.cyan(`[page ${actualPage}/${totalPages}]`) : "";
|
|
9488
|
+
const searchHint = search ? color.yellow(` (filtered: "${search}" \u2014 ${total} match${total === 1 ? "" : "es"})`) : color.dim(` (${total} model${total === 1 ? "" : "s"})`);
|
|
9489
|
+
deps.renderer.write(`${pageHint}${searchHint}
|
|
9490
|
+
`);
|
|
9491
|
+
if (pageItems.length === 0) {
|
|
9492
|
+
deps.renderer.write(color.dim("(no models match)\n"));
|
|
9493
|
+
} else {
|
|
9494
|
+
if (start > 0)
|
|
9495
|
+
deps.renderer.write(color.dim(` ${String.fromCharCode(8593)} ${start} above
|
|
9496
|
+
`));
|
|
9497
|
+
for (const m of pageItems) {
|
|
9498
|
+
const caps = [];
|
|
9499
|
+
if ("tool_call" in m && m.tool_call) caps.push("tools");
|
|
9500
|
+
if ("reasoning" in m && m.reasoning) caps.push("reasoning");
|
|
9501
|
+
if ("modalities" in m && m.modalities?.input?.includes("image")) caps.push("vision");
|
|
9502
|
+
const ctx = "limit" in m && m.limit?.context ? `${(m.limit.context / 1e3).toFixed(0)}k` : "?";
|
|
9503
|
+
const cost = "cost" in m && m.cost?.input !== void 0 ? `${m.cost.input}/${m.cost.output ?? "?"}` : "";
|
|
9504
|
+
deps.renderer.write(
|
|
9505
|
+
` ${m.id.padEnd(40)} ${color.dim(ctx.padStart(6))} ${color.dim(cost.padEnd(14))} ${color.dim(caps.join(","))}
|
|
9232
9506
|
`
|
|
9233
|
-
|
|
9507
|
+
);
|
|
9508
|
+
}
|
|
9509
|
+
if (end < total)
|
|
9510
|
+
deps.renderer.write(color.dim(` ${String.fromCharCode(8595)} ${total - end} below
|
|
9511
|
+
`));
|
|
9512
|
+
}
|
|
9513
|
+
const navLines = [];
|
|
9514
|
+
if (totalPages > 1) {
|
|
9515
|
+
if (actualPage > 1) navLines.push(`--page ${actualPage - 1} (prev)`);
|
|
9516
|
+
if (actualPage < totalPages) navLines.push(`--page ${actualPage + 1} (next)`);
|
|
9234
9517
|
}
|
|
9518
|
+
navLines.push("--search <term> (filter)");
|
|
9519
|
+
deps.renderer.write(color.dim(`
|
|
9520
|
+
${navLines.join(" \xB7 ")}
|
|
9521
|
+
`));
|
|
9235
9522
|
const age = await deps.modelsRegistry.ageSeconds();
|
|
9236
9523
|
deps.renderer.write(
|
|
9237
9524
|
color.dim(
|
|
9238
|
-
`
|
|
9239
|
-
Cache age: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never fetched"}. Run \`wstack models refresh\` to update.
|
|
9525
|
+
`Cache age: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never fetched"}. Run \`wstack models refresh\` to update.
|
|
9240
9526
|
`
|
|
9241
9527
|
)
|
|
9242
9528
|
);
|
|
9243
9529
|
return 0;
|
|
9244
9530
|
};
|
|
9531
|
+
async function mutateModelsConfig(deps, mutator) {
|
|
9532
|
+
const vault = deps.vault;
|
|
9533
|
+
const configPath2 = deps.paths.globalConfig;
|
|
9534
|
+
let fileExists = true;
|
|
9535
|
+
let raw;
|
|
9536
|
+
try {
|
|
9537
|
+
raw = await fsp3.readFile(configPath2, "utf8");
|
|
9538
|
+
} catch (err) {
|
|
9539
|
+
if (err.code !== "ENOENT") throw err;
|
|
9540
|
+
fileExists = false;
|
|
9541
|
+
raw = "{}";
|
|
9542
|
+
}
|
|
9543
|
+
let parsed;
|
|
9544
|
+
try {
|
|
9545
|
+
parsed = JSON.parse(raw);
|
|
9546
|
+
} catch (err) {
|
|
9547
|
+
if (fileExists) {
|
|
9548
|
+
throw new Error(
|
|
9549
|
+
`Refusing to overwrite corrupt config at ${configPath2} (${err.message}).`
|
|
9550
|
+
);
|
|
9551
|
+
}
|
|
9552
|
+
parsed = {};
|
|
9553
|
+
}
|
|
9554
|
+
const decrypted = decryptConfigSecrets$1(parsed, vault);
|
|
9555
|
+
const models = decrypted.models ?? {};
|
|
9556
|
+
mutator(models);
|
|
9557
|
+
decrypted.models = models;
|
|
9558
|
+
const encrypted = encryptConfigSecrets$1(decrypted, vault);
|
|
9559
|
+
await atomicWrite(configPath2, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
9560
|
+
}
|
|
9561
|
+
function parseSizeFlag(raw) {
|
|
9562
|
+
if (!raw) return void 0;
|
|
9563
|
+
const s = raw.trim().toLowerCase();
|
|
9564
|
+
const match = /^(\d+(?:\.\d+)?)\s*(k|m|b)?$/.exec(s);
|
|
9565
|
+
if (!match) return void 0;
|
|
9566
|
+
const num = Number.parseFloat(match[1]);
|
|
9567
|
+
const unit = match[2];
|
|
9568
|
+
if (unit === "b") return Math.round(num * 1e9);
|
|
9569
|
+
if (unit === "m") return Math.round(num * 1e6);
|
|
9570
|
+
if (unit === "k") return Math.round(num * 1e3);
|
|
9571
|
+
return Math.round(num);
|
|
9572
|
+
}
|
|
9573
|
+
function parseBoolFlag(flags, key) {
|
|
9574
|
+
if (flags[key] === true || flags[key] === "true") return true;
|
|
9575
|
+
if (flags[`no-${key}`] !== void 0) return false;
|
|
9576
|
+
return void 0;
|
|
9577
|
+
}
|
|
9578
|
+
async function modelsAdd(args, deps) {
|
|
9579
|
+
const flags = parseFlags2(args);
|
|
9580
|
+
const pos = positionals(args);
|
|
9581
|
+
const modelId = pos[0];
|
|
9582
|
+
if (!modelId) {
|
|
9583
|
+
deps.renderer.writeError(
|
|
9584
|
+
"Usage: wstack models add <modelId> [--provider <id>] [--name <name>] [--max-context <N>] [--max-output <N>] [--tools] [--no-tools] [--vision] [--no-vision] [--reasoning] [--streaming] [--no-streaming] [--json-mode]"
|
|
9585
|
+
);
|
|
9586
|
+
return 1;
|
|
9587
|
+
}
|
|
9588
|
+
const existing = deps.config.models?.[modelId];
|
|
9589
|
+
if (existing) {
|
|
9590
|
+
deps.renderer.writeWarning(
|
|
9591
|
+
`Model "${modelId}" already defined. Overwriting.`
|
|
9592
|
+
);
|
|
9593
|
+
}
|
|
9594
|
+
const capabilities = {};
|
|
9595
|
+
const toolsVal = parseBoolFlag(flags, "tools");
|
|
9596
|
+
if (toolsVal !== void 0) capabilities.tools = toolsVal;
|
|
9597
|
+
const visionVal = parseBoolFlag(flags, "vision");
|
|
9598
|
+
if (visionVal !== void 0) capabilities.vision = visionVal;
|
|
9599
|
+
const streamingVal = parseBoolFlag(flags, "streaming");
|
|
9600
|
+
if (streamingVal !== void 0) capabilities.streaming = streamingVal;
|
|
9601
|
+
const reasoningVal = parseBoolFlag(flags, "reasoning");
|
|
9602
|
+
if (reasoningVal !== void 0) capabilities.reasoning = reasoningVal;
|
|
9603
|
+
const jsonModeVal = parseBoolFlag(flags, "json-mode");
|
|
9604
|
+
if (jsonModeVal !== void 0) capabilities.jsonMode = jsonModeVal;
|
|
9605
|
+
const maxContextRaw = typeof flags["max-context"] === "string" ? flags["max-context"] : void 0;
|
|
9606
|
+
const maxContext = parseSizeFlag(maxContextRaw);
|
|
9607
|
+
if (maxContext !== void 0) capabilities.maxContext = maxContext;
|
|
9608
|
+
const def = {};
|
|
9609
|
+
const nameFlag = typeof flags["name"] === "string" ? flags["name"] : void 0;
|
|
9610
|
+
const providerFlag = typeof flags["provider"] === "string" ? flags["provider"] : void 0;
|
|
9611
|
+
if (nameFlag) def.name = nameFlag;
|
|
9612
|
+
if (providerFlag) def.provider = providerFlag;
|
|
9613
|
+
if (Object.keys(capabilities).length > 0) def.capabilities = capabilities;
|
|
9614
|
+
const maxOutputRaw = typeof flags["max-output"] === "string" ? flags["max-output"] : void 0;
|
|
9615
|
+
const maxOutput = parseSizeFlag(maxOutputRaw);
|
|
9616
|
+
if (maxOutput !== void 0) def.maxOutput = maxOutput;
|
|
9617
|
+
await mutateModelsConfig(deps, (models) => {
|
|
9618
|
+
models[modelId] = def;
|
|
9619
|
+
});
|
|
9620
|
+
deps.renderer.writeInfo(`Custom model "${modelId}" ${existing ? "updated" : "added"}.`);
|
|
9621
|
+
const capLines = [];
|
|
9622
|
+
if (def.capabilities) {
|
|
9623
|
+
for (const [k, v] of Object.entries(def.capabilities)) {
|
|
9624
|
+
capLines.push(` ${k}: ${v}`);
|
|
9625
|
+
}
|
|
9626
|
+
}
|
|
9627
|
+
if (def.maxOutput !== void 0) capLines.push(` maxOutput: ${def.maxOutput}`);
|
|
9628
|
+
if (capLines.length > 0) {
|
|
9629
|
+
deps.renderer.write(color.dim(capLines.join("\n") + "\n"));
|
|
9630
|
+
}
|
|
9631
|
+
return 0;
|
|
9632
|
+
}
|
|
9633
|
+
async function modelsRemove(args, deps) {
|
|
9634
|
+
const modelId = args[0];
|
|
9635
|
+
if (!modelId) {
|
|
9636
|
+
deps.renderer.writeError("Usage: wstack models remove <modelId>");
|
|
9637
|
+
return 1;
|
|
9638
|
+
}
|
|
9639
|
+
const existing = deps.config.models?.[modelId];
|
|
9640
|
+
if (!existing) {
|
|
9641
|
+
deps.renderer.writeError(`No custom model "${modelId}" found.`);
|
|
9642
|
+
return 1;
|
|
9643
|
+
}
|
|
9644
|
+
await mutateModelsConfig(deps, (models) => {
|
|
9645
|
+
delete models[modelId];
|
|
9646
|
+
});
|
|
9647
|
+
deps.renderer.writeInfo(`Removed custom model "${modelId}".`);
|
|
9648
|
+
return 0;
|
|
9649
|
+
}
|
|
9650
|
+
async function modelsList(_args, deps) {
|
|
9651
|
+
const models = deps.config.models ?? {};
|
|
9652
|
+
const entries = Object.entries(models);
|
|
9653
|
+
if (entries.length === 0) {
|
|
9654
|
+
deps.renderer.write(color.dim("No custom models defined.\n"));
|
|
9655
|
+
deps.renderer.write(color.dim("Use `wstack models add <modelId> --max-context 128k --tools`\n"));
|
|
9656
|
+
return 0;
|
|
9657
|
+
}
|
|
9658
|
+
deps.renderer.write(color.bold("Custom models\n"));
|
|
9659
|
+
for (const [id, def] of entries.sort(([a], [b]) => a.localeCompare(b))) {
|
|
9660
|
+
const label = def.name ?? id;
|
|
9661
|
+
const provider = def.provider ? ` ${color.dim(`(${def.provider})`)}` : "";
|
|
9662
|
+
deps.renderer.write(` ${color.bold(label)}${provider}
|
|
9663
|
+
`);
|
|
9664
|
+
if (def.capabilities) {
|
|
9665
|
+
for (const [k, v] of Object.entries(def.capabilities)) {
|
|
9666
|
+
deps.renderer.write(` ${color.dim(`${k}:`)} ${v}
|
|
9667
|
+
`);
|
|
9668
|
+
}
|
|
9669
|
+
}
|
|
9670
|
+
if (def.maxOutput !== void 0) {
|
|
9671
|
+
deps.renderer.write(` ${color.dim("maxOutput:")} ${def.maxOutput}
|
|
9672
|
+
`);
|
|
9673
|
+
}
|
|
9674
|
+
}
|
|
9675
|
+
return 0;
|
|
9676
|
+
}
|
|
9245
9677
|
function redactKeys(obj) {
|
|
9246
9678
|
if (!obj || typeof obj !== "object") return obj;
|
|
9247
9679
|
if (Array.isArray(obj)) return obj.map(redactKeys);
|
|
@@ -9271,7 +9703,7 @@ async function listFleetRuns(deps) {
|
|
|
9271
9703
|
}
|
|
9272
9704
|
const runs = [];
|
|
9273
9705
|
for (const id of entries) {
|
|
9274
|
-
const runDir =
|
|
9706
|
+
const runDir = path8.join(deps.paths.projectSessions, id);
|
|
9275
9707
|
let stat4;
|
|
9276
9708
|
try {
|
|
9277
9709
|
stat4 = await fsp3.stat(runDir);
|
|
@@ -9284,17 +9716,17 @@ async function listFleetRuns(deps) {
|
|
|
9284
9716
|
let subagentCount = 0;
|
|
9285
9717
|
let subagentsDir;
|
|
9286
9718
|
try {
|
|
9287
|
-
await fsp3.access(
|
|
9719
|
+
await fsp3.access(path8.join(runDir, "fleet.json"));
|
|
9288
9720
|
manifest = true;
|
|
9289
9721
|
} catch {
|
|
9290
9722
|
}
|
|
9291
9723
|
try {
|
|
9292
|
-
await fsp3.access(
|
|
9724
|
+
await fsp3.access(path8.join(runDir, "checkpoint.json"));
|
|
9293
9725
|
checkpoint = true;
|
|
9294
9726
|
} catch {
|
|
9295
9727
|
}
|
|
9296
9728
|
try {
|
|
9297
|
-
subagentsDir =
|
|
9729
|
+
subagentsDir = path8.join(runDir, "subagents");
|
|
9298
9730
|
const files = await fsp3.readdir(subagentsDir);
|
|
9299
9731
|
subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
|
|
9300
9732
|
} catch {
|
|
@@ -9323,7 +9755,7 @@ async function listFleetRuns(deps) {
|
|
|
9323
9755
|
return 0;
|
|
9324
9756
|
}
|
|
9325
9757
|
async function showFleetRun(runId, deps) {
|
|
9326
|
-
const runDir =
|
|
9758
|
+
const runDir = path8.join(deps.paths.projectSessions, runId);
|
|
9327
9759
|
let stat4;
|
|
9328
9760
|
try {
|
|
9329
9761
|
stat4 = await fsp3.stat(runDir);
|
|
@@ -9340,7 +9772,7 @@ async function showFleetRun(runId, deps) {
|
|
|
9340
9772
|
deps.renderer.write(color.bold(`
|
|
9341
9773
|
Fleet Run: ${runId}
|
|
9342
9774
|
`) + "\n");
|
|
9343
|
-
const manifestPath =
|
|
9775
|
+
const manifestPath = path8.join(runDir, "fleet.json");
|
|
9344
9776
|
let manifestData = null;
|
|
9345
9777
|
try {
|
|
9346
9778
|
manifestData = await fsp3.readFile(manifestPath, "utf8");
|
|
@@ -9356,7 +9788,7 @@ Fleet Run: ${runId}
|
|
|
9356
9788
|
deps.renderer.write(` ${color.dim("\u25CB")} fleet.json \u2014 not found
|
|
9357
9789
|
`);
|
|
9358
9790
|
}
|
|
9359
|
-
const checkpointPath =
|
|
9791
|
+
const checkpointPath = path8.join(runDir, "checkpoint.json");
|
|
9360
9792
|
let checkpointData = null;
|
|
9361
9793
|
try {
|
|
9362
9794
|
checkpointData = await fsp3.readFile(checkpointPath, "utf8");
|
|
@@ -9403,7 +9835,7 @@ Fleet Run: ${runId}
|
|
|
9403
9835
|
} catch {
|
|
9404
9836
|
}
|
|
9405
9837
|
}
|
|
9406
|
-
const subagentsDir =
|
|
9838
|
+
const subagentsDir = path8.join(runDir, "subagents");
|
|
9407
9839
|
let subagentFiles = [];
|
|
9408
9840
|
try {
|
|
9409
9841
|
subagentFiles = await fsp3.readdir(subagentsDir);
|
|
@@ -9415,7 +9847,7 @@ Fleet Run: ${runId}
|
|
|
9415
9847
|
Subagent transcripts (${subagentFiles.length}):
|
|
9416
9848
|
`);
|
|
9417
9849
|
for (const f of subagentFiles.sort()) {
|
|
9418
|
-
const filePath =
|
|
9850
|
+
const filePath = path8.join(subagentsDir, f);
|
|
9419
9851
|
let size;
|
|
9420
9852
|
try {
|
|
9421
9853
|
const s = await fsp3.stat(filePath);
|
|
@@ -9432,7 +9864,7 @@ Fleet Run: ${runId}
|
|
|
9432
9864
|
${color.dim("\u25CB")} No subagent transcripts
|
|
9433
9865
|
`);
|
|
9434
9866
|
}
|
|
9435
|
-
const sharedDir =
|
|
9867
|
+
const sharedDir = path8.join(runDir, "shared");
|
|
9436
9868
|
try {
|
|
9437
9869
|
const files = await fsp3.readdir(sharedDir);
|
|
9438
9870
|
deps.renderer.write(`
|
|
@@ -9599,7 +10031,7 @@ function findSessionId(args) {
|
|
|
9599
10031
|
var rewindCmd = async (args, deps) => {
|
|
9600
10032
|
const flags = parseRewindFlags(args);
|
|
9601
10033
|
const wpaths = resolveWstackPaths({ projectRoot: deps.projectRoot });
|
|
9602
|
-
const sessionsDir =
|
|
10034
|
+
const sessionsDir = path8.join(wpaths.globalRoot, "sessions");
|
|
9603
10035
|
const rewind = new DefaultSessionRewinder(sessionsDir, deps.projectRoot);
|
|
9604
10036
|
let sessionId = findSessionId(args);
|
|
9605
10037
|
if (!sessionId) {
|
|
@@ -9834,10 +10266,10 @@ var auditCmd = async (args, deps) => {
|
|
|
9834
10266
|
return verify.ok ? 0 : 1;
|
|
9835
10267
|
};
|
|
9836
10268
|
async function listAudits(log, dir, deps) {
|
|
9837
|
-
const
|
|
10269
|
+
const fs24 = await import('fs/promises');
|
|
9838
10270
|
let entries;
|
|
9839
10271
|
try {
|
|
9840
|
-
entries = await
|
|
10272
|
+
entries = await fs24.readdir(dir);
|
|
9841
10273
|
} catch {
|
|
9842
10274
|
deps.renderer.write(
|
|
9843
10275
|
color.dim(`No sessions dir found at ${dir}. Run a session first.`) + "\n"
|
|
@@ -9892,7 +10324,7 @@ var skillsCmd = async (_args, deps) => {
|
|
|
9892
10324
|
};
|
|
9893
10325
|
var versionCmd = async (_args, deps) => {
|
|
9894
10326
|
deps.renderer.write(
|
|
9895
|
-
`WrongStack ${CLI_VERSION} (apiVersion ${API_VERSION}, node ${process.version}, ${
|
|
10327
|
+
`WrongStack ${CLI_VERSION} (apiVersion ${API_VERSION}, node ${process.version}, ${os2.platform()})
|
|
9896
10328
|
`
|
|
9897
10329
|
);
|
|
9898
10330
|
return 0;
|
|
@@ -9914,6 +10346,9 @@ var helpCmd = async (_args, deps) => {
|
|
|
9914
10346
|
" wstack providers [--all] List providers from models.dev",
|
|
9915
10347
|
" wstack models [<provider>] List models",
|
|
9916
10348
|
" wstack models refresh Force-refresh cache",
|
|
10349
|
+
" wstack models add <mid> Add/override custom model (--max-context, --tools, --vision, \u2026)",
|
|
10350
|
+
" wstack models remove <mid> Remove a custom model",
|
|
10351
|
+
" wstack models list List all custom models",
|
|
9917
10352
|
" wstack mcp [list] List MCP servers",
|
|
9918
10353
|
" wstack plugin [list|status|official|install|add|remove|enable|disable] Manage plugins",
|
|
9919
10354
|
" wstack projects List tracked projects",
|
|
@@ -9974,22 +10409,22 @@ function fmtDuration(ms) {
|
|
|
9974
10409
|
const remMin = m - h * 60;
|
|
9975
10410
|
return `${h}h${remMin}m`;
|
|
9976
10411
|
}
|
|
9977
|
-
function fmtTaskResultLine(r,
|
|
10412
|
+
function fmtTaskResultLine(r, color46) {
|
|
9978
10413
|
const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
|
|
9979
10414
|
const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
|
|
9980
10415
|
const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
|
|
9981
10416
|
const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
|
|
9982
|
-
const errKindChip = errKind ?
|
|
9983
|
-
const errSnip = errMsg || errKind ? `${errKindChip}${
|
|
10417
|
+
const errKindChip = errKind ? color46.dim(` [${errKind}]`) : "";
|
|
10418
|
+
const errSnip = errMsg || errKind ? `${errKindChip}${color46.dim(errTail)}` : "";
|
|
9984
10419
|
switch (r.status) {
|
|
9985
10420
|
case "success":
|
|
9986
|
-
return { mark:
|
|
10421
|
+
return { mark: color46.green("\u2713"), stats, tail: "" };
|
|
9987
10422
|
case "timeout":
|
|
9988
|
-
return { mark:
|
|
10423
|
+
return { mark: color46.yellow("\u23F1"), stats: `${color46.yellow("timeout")} ${stats}`, tail: errSnip };
|
|
9989
10424
|
case "stopped":
|
|
9990
|
-
return { mark:
|
|
10425
|
+
return { mark: color46.dim("\u2298"), stats: `${color46.dim("stopped")} ${stats}`, tail: errSnip };
|
|
9991
10426
|
case "failed":
|
|
9992
|
-
return { mark:
|
|
10427
|
+
return { mark: color46.red("\u2717"), stats: `${color46.red("failed")} ${stats}`, tail: errSnip };
|
|
9993
10428
|
}
|
|
9994
10429
|
}
|
|
9995
10430
|
|
|
@@ -10007,7 +10442,7 @@ function resolveBundledSkillsDir() {
|
|
|
10007
10442
|
try {
|
|
10008
10443
|
const req2 = createRequire(import.meta.url);
|
|
10009
10444
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
10010
|
-
return
|
|
10445
|
+
return path8.join(path8.dirname(corePkg), "skills");
|
|
10011
10446
|
} catch {
|
|
10012
10447
|
return void 0;
|
|
10013
10448
|
}
|
|
@@ -10038,7 +10473,12 @@ async function boot(argv) {
|
|
|
10038
10473
|
const reader = new ReadlineInputReader({ historyFile: wpaths.historyFile });
|
|
10039
10474
|
const modelsRegistry = new DefaultModelsRegistry({
|
|
10040
10475
|
cacheFile: wpaths.modelsCache,
|
|
10041
|
-
|
|
10476
|
+
// Force a refresh attempt once per CLI process. Model metadata changes faster
|
|
10477
|
+
// than releases (new model ids, corrected context windows), and stale cache
|
|
10478
|
+
// here directly affects runtime behavior like context bars and compaction.
|
|
10479
|
+
// If the network is unavailable, DefaultModelsRegistry still falls back to
|
|
10480
|
+
// stale cache or the bundled overlay instead of failing startup.
|
|
10481
|
+
ttlSeconds: 0,
|
|
10042
10482
|
// Curated overlay merged on top of models.dev: fetched from GitHub raw for
|
|
10043
10483
|
// freshness, with the bundled file as the offline floor.
|
|
10044
10484
|
overlayUrl: GITHUB_PROVIDERS_OVERLAY_URL,
|
|
@@ -10052,6 +10492,15 @@ async function boot(argv) {
|
|
|
10052
10492
|
}).catch(() => {
|
|
10053
10493
|
});
|
|
10054
10494
|
}
|
|
10495
|
+
if (!flags["no-models-refresh"]) {
|
|
10496
|
+
try {
|
|
10497
|
+
await modelsRegistry.refresh();
|
|
10498
|
+
logger.info("models.dev catalog refreshed");
|
|
10499
|
+
} catch (err) {
|
|
10500
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10501
|
+
logger.warn(`models.dev refresh failed (${msg}); using cached catalog`);
|
|
10502
|
+
}
|
|
10503
|
+
}
|
|
10055
10504
|
const first = positional[0];
|
|
10056
10505
|
if (first && subcommands[first]) {
|
|
10057
10506
|
const container = createDefaultContainer({
|
|
@@ -10143,7 +10592,8 @@ async function boot(argv) {
|
|
|
10143
10592
|
else if (flags["no-director"] === true) directorPinned = false;
|
|
10144
10593
|
let autonomyPinned;
|
|
10145
10594
|
if (flags["no-autonomy"] === true) autonomyPinned = "off";
|
|
10146
|
-
else if (flags["eternal"] === true)
|
|
10595
|
+
else if (flags["eternal"] === true)
|
|
10596
|
+
autonomyPinned = "off";
|
|
10147
10597
|
else if (typeof flags["autonomy"] === "string") {
|
|
10148
10598
|
const v = flags["autonomy"].toLowerCase();
|
|
10149
10599
|
autonomyPinned = v === "off" || v === "no" || v === "false" ? "off" : "auto";
|
|
@@ -10178,7 +10628,7 @@ async function boot(argv) {
|
|
|
10178
10628
|
if (choices.director) flags["director"] = true;
|
|
10179
10629
|
flags["autonomy"] = choices.autonomy;
|
|
10180
10630
|
printLaunchHints(renderer, flags, {
|
|
10181
|
-
cursorFile:
|
|
10631
|
+
cursorFile: path8.join(wpaths.cacheDir, "hint-cursor")
|
|
10182
10632
|
});
|
|
10183
10633
|
}
|
|
10184
10634
|
return {
|
|
@@ -10556,7 +11006,7 @@ async function runRepl(opts) {
|
|
|
10556
11006
|
`[eternal] ${err instanceof Error ? err.message : String(err)}`
|
|
10557
11007
|
);
|
|
10558
11008
|
}
|
|
10559
|
-
await new Promise((
|
|
11009
|
+
await new Promise((resolve3) => setTimeout(resolve3, 250));
|
|
10560
11010
|
continue;
|
|
10561
11011
|
}
|
|
10562
11012
|
} else if (opts.getAutonomy?.() === "eternal-parallel") {
|
|
@@ -10602,7 +11052,7 @@ async function runRepl(opts) {
|
|
|
10602
11052
|
`[parallel] ${err instanceof Error ? err.message : String(err)}`
|
|
10603
11053
|
);
|
|
10604
11054
|
}
|
|
10605
|
-
await new Promise((
|
|
11055
|
+
await new Promise((resolve3) => setTimeout(resolve3, 250));
|
|
10606
11056
|
continue;
|
|
10607
11057
|
}
|
|
10608
11058
|
}
|
|
@@ -11152,7 +11602,17 @@ async function execute(deps) {
|
|
|
11152
11602
|
const visionAdapters = () => createToolVisionAdapters(agent.tools);
|
|
11153
11603
|
const supportsVision = async () => {
|
|
11154
11604
|
try {
|
|
11155
|
-
const
|
|
11605
|
+
const providerConfig = config.providers?.[context.provider.id];
|
|
11606
|
+
const mergedModels = mergeCustomModelDefs(
|
|
11607
|
+
providerConfig?.customModels,
|
|
11608
|
+
config.models
|
|
11609
|
+
);
|
|
11610
|
+
const caps = await capabilitiesFor(
|
|
11611
|
+
modelsRegistry,
|
|
11612
|
+
context.provider.id,
|
|
11613
|
+
context.model,
|
|
11614
|
+
mergedModels
|
|
11615
|
+
);
|
|
11156
11616
|
return caps.vision;
|
|
11157
11617
|
} catch {
|
|
11158
11618
|
return context.provider.capabilities.vision;
|
|
@@ -11256,6 +11716,11 @@ async function execute(deps) {
|
|
|
11256
11716
|
"phase.taskCompleted",
|
|
11257
11717
|
"phase.taskFailed",
|
|
11258
11718
|
"phase.taskRetrying",
|
|
11719
|
+
"phase.verifying",
|
|
11720
|
+
"phase.verifyFailed",
|
|
11721
|
+
"phase.repairing",
|
|
11722
|
+
"phase.conflictResolving",
|
|
11723
|
+
"phase.conflictResolved",
|
|
11259
11724
|
"autonomous.tick",
|
|
11260
11725
|
"graph.completed",
|
|
11261
11726
|
"graph.failed",
|
|
@@ -11383,10 +11848,18 @@ async function execute(deps) {
|
|
|
11383
11848
|
return getActiveSDDContext2();
|
|
11384
11849
|
},
|
|
11385
11850
|
onSDDOutput: async (output) => {
|
|
11386
|
-
const {
|
|
11851
|
+
const {
|
|
11852
|
+
trySaveSpecFromAIOutput: trySaveSpecFromAIOutput2,
|
|
11853
|
+
trySaveImplementationPlan: trySaveImplementationPlan2,
|
|
11854
|
+
trySaveTasksFromAIOutput: trySaveTasksFromAIOutput2,
|
|
11855
|
+
autoDetectTaskCompletion: autoDetectTaskCompletion2,
|
|
11856
|
+
getTaskProgress: getTaskProgress2,
|
|
11857
|
+
getActiveSDDPhase: getActiveSDDPhase2
|
|
11858
|
+
} = (init_sdd(), __toCommonJS(sdd_exports));
|
|
11387
11859
|
const messages = [];
|
|
11388
11860
|
const specSaved = await trySaveSpecFromAIOutput2(output);
|
|
11389
|
-
if (specSaved)
|
|
11861
|
+
if (specSaved)
|
|
11862
|
+
messages.push("\u2713 Spec detected and saved! Use /sdd approve to continue.");
|
|
11390
11863
|
const planSaved = trySaveImplementationPlan2(output);
|
|
11391
11864
|
if (planSaved) messages.push("\u2713 Implementation plan saved!");
|
|
11392
11865
|
const tasksSaved = await trySaveTasksFromAIOutput2(output);
|
|
@@ -11401,7 +11874,9 @@ async function execute(deps) {
|
|
|
11401
11874
|
if (autoCompleted > 0) {
|
|
11402
11875
|
const progress = getTaskProgress2();
|
|
11403
11876
|
if (progress) {
|
|
11404
|
-
messages.push(
|
|
11877
|
+
messages.push(
|
|
11878
|
+
`\u2713 ${autoCompleted} task(s) auto-completed! Progress: ${progress.completed}/${progress.total} (${progress.percentComplete}%)`
|
|
11879
|
+
);
|
|
11405
11880
|
}
|
|
11406
11881
|
}
|
|
11407
11882
|
}
|
|
@@ -11433,7 +11908,7 @@ async function execute(deps) {
|
|
|
11433
11908
|
supportsVision,
|
|
11434
11909
|
attachments,
|
|
11435
11910
|
effectiveMaxContext,
|
|
11436
|
-
projectName:
|
|
11911
|
+
projectName: path8.basename(projectRoot) || void 0,
|
|
11437
11912
|
projectRoot,
|
|
11438
11913
|
getAutonomy,
|
|
11439
11914
|
onAutonomy,
|
|
@@ -11461,7 +11936,7 @@ async function execute(deps) {
|
|
|
11461
11936
|
supportsVision,
|
|
11462
11937
|
attachments,
|
|
11463
11938
|
effectiveMaxContext,
|
|
11464
|
-
projectName:
|
|
11939
|
+
projectName: path8.basename(projectRoot) || void 0,
|
|
11465
11940
|
getAutonomy,
|
|
11466
11941
|
onAutonomy,
|
|
11467
11942
|
getNextPredict,
|
|
@@ -11562,7 +12037,8 @@ var MultiAgentHost = class {
|
|
|
11562
12037
|
directorBudget: this.opts.directorBudget,
|
|
11563
12038
|
manifestDebounceMs: 2e3,
|
|
11564
12039
|
checkpointDebounceMs: this.opts.checkpointDebounceMs ?? 250,
|
|
11565
|
-
maxSpawnDepth: 5
|
|
12040
|
+
maxSpawnDepth: 5,
|
|
12041
|
+
maxContext: this.opts.getLeaderMaxContext
|
|
11566
12042
|
});
|
|
11567
12043
|
this.fleetManager = fleetManager;
|
|
11568
12044
|
if (this.opts.sessionsRoot && !this.sessionFactory) {
|
|
@@ -11576,7 +12052,7 @@ var MultiAgentHost = class {
|
|
|
11576
12052
|
doneCondition: { type: "all_tasks_done" },
|
|
11577
12053
|
maxConcurrent: this.opts.maxConcurrent ?? 4
|
|
11578
12054
|
};
|
|
11579
|
-
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ?
|
|
12055
|
+
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path8.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
|
|
11580
12056
|
this.director = new Director({
|
|
11581
12057
|
config: coordinatorConfig,
|
|
11582
12058
|
manifestPath: this.opts.manifestPath,
|
|
@@ -11589,11 +12065,13 @@ var MultiAgentHost = class {
|
|
|
11589
12065
|
sessionsRoot: this.opts.sessionsRoot,
|
|
11590
12066
|
directorRunId: this.opts.directorRunId,
|
|
11591
12067
|
maxSpawnDepth: 5,
|
|
12068
|
+
maxContext: this.opts.getLeaderMaxContext,
|
|
11592
12069
|
// Live getter (not a snapshot) so a mid-session `/setmodel` takes
|
|
11593
12070
|
// effect on the next spawn — the director is built lazily + once.
|
|
11594
12071
|
modelMatrix: () => this.deps.configStore.get().modelMatrix,
|
|
11595
|
-
fleetManager
|
|
12072
|
+
fleetManager,
|
|
11596
12073
|
// pass so director.fleetManager is never undefined
|
|
12074
|
+
brain: this.opts.brain
|
|
11597
12075
|
});
|
|
11598
12076
|
this.director.on("task.completed", ({ task, result }) => {
|
|
11599
12077
|
this.fleetManager?.removePendingTask(task.id);
|
|
@@ -11779,9 +12257,18 @@ var MultiAgentHost = class {
|
|
|
11779
12257
|
subagentId: subCfg.id ?? subCfg.name ?? "subagent"
|
|
11780
12258
|
});
|
|
11781
12259
|
});
|
|
12260
|
+
const offCtxBridge = events.on("ctx.pct", (e) => {
|
|
12261
|
+
hostEvents.emit("subagent.ctx_pct", {
|
|
12262
|
+
subagentId: subCfg.id ?? subCfg.name ?? "subagent",
|
|
12263
|
+
load: e.load,
|
|
12264
|
+
tokens: e.tokens,
|
|
12265
|
+
maxContext: e.maxContext
|
|
12266
|
+
});
|
|
12267
|
+
});
|
|
11782
12268
|
const dispose = async () => {
|
|
11783
12269
|
offToolBridge();
|
|
11784
12270
|
offSummaryBridge();
|
|
12271
|
+
offCtxBridge();
|
|
11785
12272
|
try {
|
|
11786
12273
|
await subSession.close?.();
|
|
11787
12274
|
} catch {
|
|
@@ -11815,22 +12302,28 @@ var MultiAgentHost = class {
|
|
|
11815
12302
|
* use the leader and the calling code can decide to error later).
|
|
11816
12303
|
*/
|
|
11817
12304
|
async buildSubagentProvider(config, overrideId, model) {
|
|
11818
|
-
const
|
|
12305
|
+
const requestedProviderId = overrideId ?? config.provider;
|
|
12306
|
+
const providerId = requestedProviderId === config.provider || config.providers?.[requestedProviderId] !== void 0 || this.deps.providerRegistry.has(requestedProviderId) ? requestedProviderId : config.provider;
|
|
11819
12307
|
const newCfg = config.providers?.[providerId] ?? {
|
|
11820
12308
|
type: providerId,
|
|
11821
12309
|
apiKey: config.apiKey,
|
|
11822
12310
|
baseUrl: config.baseUrl
|
|
11823
12311
|
};
|
|
11824
|
-
const
|
|
12312
|
+
const cfgWithType = {
|
|
11825
12313
|
...newCfg,
|
|
11826
12314
|
type: providerId
|
|
11827
|
-
}
|
|
12315
|
+
};
|
|
12316
|
+
const provider = this.deps.providerRegistry.has(providerId) ? this.deps.providerRegistry.create(cfgWithType) : makeProviderFromConfig(providerId, cfgWithType);
|
|
11828
12317
|
if (this.deps.modelsRegistry) {
|
|
11829
12318
|
const resolvedModel = model ?? config.model;
|
|
11830
|
-
const
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
|
|
12319
|
+
const mc = await resolveRuntimeMaxContext({
|
|
12320
|
+
modelsRegistry: this.deps.modelsRegistry,
|
|
12321
|
+
config,
|
|
12322
|
+
provider,
|
|
12323
|
+
runtimeProviderConfig: cfgWithType,
|
|
12324
|
+
providerId,
|
|
12325
|
+
modelId: resolvedModel
|
|
12326
|
+
});
|
|
11834
12327
|
if (mc && mc > 0) provider.capabilities.maxContext = mc;
|
|
11835
12328
|
}
|
|
11836
12329
|
return provider;
|
|
@@ -12025,16 +12518,16 @@ var MultiAgentHost = class {
|
|
|
12025
12518
|
if (this.director) return this.director;
|
|
12026
12519
|
this.opts.directorMode = true;
|
|
12027
12520
|
if (this.opts.fleetRoot && !this.opts.manifestPath) {
|
|
12028
|
-
this.opts.manifestPath =
|
|
12521
|
+
this.opts.manifestPath = path8.join(this.opts.fleetRoot, "fleet.json");
|
|
12029
12522
|
}
|
|
12030
12523
|
if (this.opts.fleetRoot && !this.opts.sharedScratchpadPath) {
|
|
12031
|
-
this.opts.sharedScratchpadPath =
|
|
12524
|
+
this.opts.sharedScratchpadPath = path8.join(this.opts.fleetRoot, "shared");
|
|
12032
12525
|
}
|
|
12033
12526
|
if (this.opts.fleetRoot && !this.opts.sessionsRoot) {
|
|
12034
|
-
this.opts.sessionsRoot =
|
|
12527
|
+
this.opts.sessionsRoot = path8.join(this.opts.fleetRoot, "subagents");
|
|
12035
12528
|
}
|
|
12036
12529
|
if (this.opts.fleetRoot && !this.opts.stateCheckpointPath) {
|
|
12037
|
-
this.opts.stateCheckpointPath =
|
|
12530
|
+
this.opts.stateCheckpointPath = path8.join(this.opts.fleetRoot, "director-state.json");
|
|
12038
12531
|
}
|
|
12039
12532
|
await this.ensureDirector();
|
|
12040
12533
|
return this.director ?? null;
|
|
@@ -12220,11 +12713,11 @@ var SessionStats = class {
|
|
|
12220
12713
|
if (e.name === "bash") this.bashCommands++;
|
|
12221
12714
|
else if (e.name === "fetch") this.fetches++;
|
|
12222
12715
|
if (!e.ok) return;
|
|
12223
|
-
const
|
|
12224
|
-
if (e.name === "read" &&
|
|
12225
|
-
else if (e.name === "edit" &&
|
|
12226
|
-
else if (e.name === "write" &&
|
|
12227
|
-
this.writtenPaths.add(
|
|
12716
|
+
const path25 = typeof input?.path === "string" ? input.path : void 0;
|
|
12717
|
+
if (e.name === "read" && path25) this.readPaths.add(path25);
|
|
12718
|
+
else if (e.name === "edit" && path25) this.editedPaths.add(path25);
|
|
12719
|
+
else if (e.name === "write" && path25) {
|
|
12720
|
+
this.writtenPaths.add(path25);
|
|
12228
12721
|
const content = typeof input?.content === "string" ? input.content : "";
|
|
12229
12722
|
this.bytesWritten += Buffer.byteLength(content, "utf8");
|
|
12230
12723
|
}
|
|
@@ -12330,7 +12823,7 @@ function samplePaths(set) {
|
|
|
12330
12823
|
}
|
|
12331
12824
|
var WORKTREE_PHASE_CONCURRENCY = 4;
|
|
12332
12825
|
function gitText(args, cwd) {
|
|
12333
|
-
return new Promise((
|
|
12826
|
+
return new Promise((resolve3) => {
|
|
12334
12827
|
let out = "";
|
|
12335
12828
|
const child = spawn("git", args, {
|
|
12336
12829
|
cwd,
|
|
@@ -12343,8 +12836,8 @@ function gitText(args, cwd) {
|
|
|
12343
12836
|
child.stderr?.on("data", (c) => {
|
|
12344
12837
|
out += c.toString();
|
|
12345
12838
|
});
|
|
12346
|
-
child.on("error", () =>
|
|
12347
|
-
child.on("close", (code) =>
|
|
12839
|
+
child.on("error", () => resolve3({ code: 1, out }));
|
|
12840
|
+
child.on("close", (code) => resolve3({ code: code ?? 1, out: out.trim() }));
|
|
12348
12841
|
});
|
|
12349
12842
|
}
|
|
12350
12843
|
async function isGitRepo(cwd) {
|
|
@@ -12352,7 +12845,7 @@ async function isGitRepo(cwd) {
|
|
|
12352
12845
|
return code === 0 && out.trim() === "true";
|
|
12353
12846
|
}
|
|
12354
12847
|
function runCmd(cmd, args, cwd, shell = false) {
|
|
12355
|
-
return new Promise((
|
|
12848
|
+
return new Promise((resolve3) => {
|
|
12356
12849
|
let out = "";
|
|
12357
12850
|
const child = spawn(cmd, args, {
|
|
12358
12851
|
cwd,
|
|
@@ -12366,8 +12859,8 @@ function runCmd(cmd, args, cwd, shell = false) {
|
|
|
12366
12859
|
child.stderr?.on("data", (c) => {
|
|
12367
12860
|
out += c.toString();
|
|
12368
12861
|
});
|
|
12369
|
-
child.on("error", (e) =>
|
|
12370
|
-
child.on("close", (code) =>
|
|
12862
|
+
child.on("error", (e) => resolve3({ code: 1, out: `${out}${String(e)}` }));
|
|
12863
|
+
child.on("close", (code) => resolve3({ code: code ?? 1, out: out.trim() }));
|
|
12371
12864
|
});
|
|
12372
12865
|
}
|
|
12373
12866
|
function detectPackageManager2(root) {
|
|
@@ -12572,6 +13065,7 @@ ${res.out}` };
|
|
|
12572
13065
|
return false;
|
|
12573
13066
|
}
|
|
12574
13067
|
} : void 0,
|
|
13068
|
+
brain: deps.brain,
|
|
12575
13069
|
onPhaseComplete: (phase) => {
|
|
12576
13070
|
log(`\u2705 Phase completed: ${phase.name}`);
|
|
12577
13071
|
void persist(graph);
|
|
@@ -12593,11 +13087,20 @@ ${res.out}` };
|
|
|
12593
13087
|
});
|
|
12594
13088
|
const onUntyped = deps.events.on;
|
|
12595
13089
|
const offUntyped = deps.events.off;
|
|
13090
|
+
const finalizeActiveRun = () => {
|
|
13091
|
+
if (active?.graph.id !== graph.id) return;
|
|
13092
|
+
active.unsubscribe();
|
|
13093
|
+
active = null;
|
|
13094
|
+
};
|
|
12596
13095
|
const onDone = () => {
|
|
12597
13096
|
log(`\u{1F389} AutoPhase complete: ${graph.title}`);
|
|
12598
13097
|
void persist(graph);
|
|
13098
|
+
finalizeActiveRun();
|
|
13099
|
+
};
|
|
13100
|
+
const onFailed = () => {
|
|
13101
|
+
void persist(graph);
|
|
13102
|
+
finalizeActiveRun();
|
|
12599
13103
|
};
|
|
12600
|
-
const onFailed = () => void persist(graph);
|
|
12601
13104
|
onUntyped("graph.completed", onDone);
|
|
12602
13105
|
onUntyped("graph.failed", onFailed);
|
|
12603
13106
|
const unsubscribe = () => {
|
|
@@ -12607,6 +13110,8 @@ ${res.out}` };
|
|
|
12607
13110
|
active = { graph, orchestrator, abort, unsubscribe };
|
|
12608
13111
|
void orchestrator.start().catch((err) => {
|
|
12609
13112
|
log(`\u{1F4A5} AutoPhase aborted: ${err instanceof Error ? err.message : String(err)}`);
|
|
13113
|
+
void persist(graph);
|
|
13114
|
+
finalizeActiveRun();
|
|
12610
13115
|
});
|
|
12611
13116
|
return { ok: true, graph };
|
|
12612
13117
|
},
|
|
@@ -12781,11 +13286,27 @@ function setupPipelines(params) {
|
|
|
12781
13286
|
return pipelines;
|
|
12782
13287
|
}
|
|
12783
13288
|
async function setupCompaction(params) {
|
|
12784
|
-
const {
|
|
12785
|
-
|
|
12786
|
-
|
|
13289
|
+
const {
|
|
13290
|
+
compactor,
|
|
13291
|
+
events,
|
|
13292
|
+
modelsRegistry,
|
|
13293
|
+
context,
|
|
13294
|
+
config,
|
|
13295
|
+
provider,
|
|
13296
|
+
pipelines,
|
|
13297
|
+
fullConfig,
|
|
13298
|
+
sessionWriter,
|
|
13299
|
+
sessionBridge: providedBridge
|
|
13300
|
+
} = params;
|
|
13301
|
+
const effectiveMaxContext = await resolveRuntimeMaxContext({
|
|
13302
|
+
modelsRegistry,
|
|
13303
|
+
config,
|
|
13304
|
+
provider,
|
|
13305
|
+
providerId: config.provider ?? provider.id,
|
|
13306
|
+
modelId: config.model ?? context.model
|
|
13307
|
+
});
|
|
12787
13308
|
let autoCompactor;
|
|
12788
|
-
if (config.context.autoCompact !== false) {
|
|
13309
|
+
if (config.context.autoCompact !== false && effectiveMaxContext > 0) {
|
|
12789
13310
|
const auditLevel = resolveAuditLevel(fullConfig ?? config);
|
|
12790
13311
|
const sessionBridge = providedBridge ?? createSessionEventBridge(sessionWriter, auditLevel);
|
|
12791
13312
|
autoCompactor = new AutoCompactionMiddleware(
|
|
@@ -12872,7 +13393,7 @@ function setupMetrics(params) {
|
|
|
12872
13393
|
const dumpMetrics = () => {
|
|
12873
13394
|
if (!metricsSink) return;
|
|
12874
13395
|
try {
|
|
12875
|
-
const out =
|
|
13396
|
+
const out = path8.join(wpaths.projectSessions, "metrics.json");
|
|
12876
13397
|
const snap = metricsSink.snapshot();
|
|
12877
13398
|
writeFileSync(out, JSON.stringify(snap, null, 2));
|
|
12878
13399
|
} catch {
|
|
@@ -13131,12 +13652,12 @@ async function setupSession(params) {
|
|
|
13131
13652
|
}
|
|
13132
13653
|
const sessionRef = { current: session };
|
|
13133
13654
|
await recoveryLock.write(session.id).catch(() => void 0);
|
|
13134
|
-
const attachments = new DefaultAttachmentStore({ spoolDir:
|
|
13135
|
-
const queueStore = new QueueStore({ dir:
|
|
13655
|
+
const attachments = new DefaultAttachmentStore({ spoolDir: path8.join(wpaths.projectSessions, session.id, "attachments") });
|
|
13656
|
+
const queueStore = new QueueStore({ dir: path8.join(wpaths.projectSessions, session.id) });
|
|
13136
13657
|
const ctxSignal = new AbortController().signal;
|
|
13137
13658
|
const context = new Context({ systemPrompt, provider, session, signal: ctxSignal, tokenCounter, cwd, projectRoot, model: config.model });
|
|
13138
13659
|
if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
|
|
13139
|
-
const todosCheckpointPath =
|
|
13660
|
+
const todosCheckpointPath = path8.join(wpaths.projectSessions, `${session.id}.todos.json`);
|
|
13140
13661
|
if (resumeId) {
|
|
13141
13662
|
try {
|
|
13142
13663
|
const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
|
|
@@ -13148,13 +13669,13 @@ async function setupSession(params) {
|
|
|
13148
13669
|
}
|
|
13149
13670
|
}
|
|
13150
13671
|
const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session.id);
|
|
13151
|
-
const planPath =
|
|
13672
|
+
const planPath = path8.join(wpaths.projectSessions, `${session.id}.plan.json`);
|
|
13152
13673
|
context.state.setMeta("plan.path", planPath);
|
|
13153
13674
|
let dirState;
|
|
13154
13675
|
if (resumeId) {
|
|
13155
13676
|
try {
|
|
13156
|
-
const fleetRoot =
|
|
13157
|
-
dirState = await loadDirectorState(
|
|
13677
|
+
const fleetRoot = path8.join(wpaths.projectSessions, session.id);
|
|
13678
|
+
dirState = await loadDirectorState(path8.join(fleetRoot, "director-state.json"));
|
|
13158
13679
|
if (dirState) {
|
|
13159
13680
|
const tCounts = {};
|
|
13160
13681
|
for (const t of dirState.tasks) tCounts[t.status] = (tCounts[t.status] ?? 0) + 1;
|
|
@@ -13179,7 +13700,7 @@ function resolveBundledSkillsDir2() {
|
|
|
13179
13700
|
try {
|
|
13180
13701
|
const req2 = createRequire(import.meta.url);
|
|
13181
13702
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
13182
|
-
return
|
|
13703
|
+
return path8.join(path8.dirname(corePkg), "skills");
|
|
13183
13704
|
} catch {
|
|
13184
13705
|
return void 0;
|
|
13185
13706
|
}
|
|
@@ -13304,7 +13825,6 @@ var resolveSessionLoggingConfig = (cfg) => ({
|
|
|
13304
13825
|
}
|
|
13305
13826
|
}
|
|
13306
13827
|
});
|
|
13307
|
-
var stageListeners = /* @__PURE__ */ new Set();
|
|
13308
13828
|
async function main(argv) {
|
|
13309
13829
|
const ctx = await boot(argv);
|
|
13310
13830
|
if (typeof ctx === "number") return ctx;
|
|
@@ -13334,7 +13854,10 @@ async function main(argv) {
|
|
|
13334
13854
|
forceAllYolo: flags["force-all-yolo"] === true,
|
|
13335
13855
|
promptDelegate: makePromptDelegate(reader)
|
|
13336
13856
|
},
|
|
13337
|
-
compactor: {
|
|
13857
|
+
compactor: {
|
|
13858
|
+
preserveK: config.context.preserveK,
|
|
13859
|
+
eliseThreshold: config.context.eliseThreshold
|
|
13860
|
+
},
|
|
13338
13861
|
bundledSkillsDir: config.features.skills ? resolveBundledSkillsDir2() : void 0
|
|
13339
13862
|
});
|
|
13340
13863
|
const replayFlag = flags["replay"];
|
|
@@ -13349,9 +13872,7 @@ async function main(argv) {
|
|
|
13349
13872
|
mode,
|
|
13350
13873
|
logger
|
|
13351
13874
|
});
|
|
13352
|
-
logger.info(
|
|
13353
|
-
`replay: ProviderRunner bound in '${mode}' mode for session ${sessionId}`
|
|
13354
|
-
);
|
|
13875
|
+
logger.info(`replay: ProviderRunner bound in '${mode}' mode for session ${sessionId}`);
|
|
13355
13876
|
}
|
|
13356
13877
|
const configStore = container.resolve(TOKENS.ConfigStore);
|
|
13357
13878
|
container.bind(TOKENS.PathResolver, () => pathResolver);
|
|
@@ -13375,12 +13896,20 @@ async function main(argv) {
|
|
|
13375
13896
|
}
|
|
13376
13897
|
const modeId = activeMode?.id ?? "default";
|
|
13377
13898
|
const modePrompt = activeMode?.prompt ?? "";
|
|
13378
|
-
const resolvedModel = await
|
|
13379
|
-
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
|
|
13899
|
+
const [resolvedCaps, resolvedModel] = await Promise.all([
|
|
13900
|
+
capabilitiesFor(
|
|
13901
|
+
modelsRegistry,
|
|
13902
|
+
provider.id,
|
|
13903
|
+
config.model,
|
|
13904
|
+
mergeCustomModelDefs(config.providers?.[provider.id]?.customModels, config.models)
|
|
13905
|
+
).catch(() => void 0),
|
|
13906
|
+
modelsRegistry.getModel(config.provider, config.model).catch(() => void 0)
|
|
13907
|
+
]);
|
|
13908
|
+
const modelCapabilities = resolvedCaps ? {
|
|
13909
|
+
maxContextTokens: resolvedCaps.maxContext,
|
|
13910
|
+
supportsTools: resolvedCaps.tools,
|
|
13911
|
+
supportsVision: resolvedCaps.vision,
|
|
13912
|
+
supportsReasoning: resolvedModel?.capabilities.reasoning ?? false
|
|
13384
13913
|
} : void 0;
|
|
13385
13914
|
const memoryStore = container.resolve(TOKENS.MemoryStore);
|
|
13386
13915
|
const skillLoader = container.resolve(TOKENS.SkillLoader);
|
|
@@ -13396,15 +13925,15 @@ async function main(argv) {
|
|
|
13396
13925
|
modeId,
|
|
13397
13926
|
modePrompt,
|
|
13398
13927
|
modelCapabilities,
|
|
13399
|
-
planPath: () => sessionRef.current ?
|
|
13928
|
+
planPath: () => sessionRef.current ? path8.join(wpaths.projectSessions, `${sessionRef.current.id}.plan.json`) : void 0,
|
|
13400
13929
|
contributors: [
|
|
13401
13930
|
// Injects the ETERNAL AUTONOMY block when the user has activated
|
|
13402
|
-
//
|
|
13403
|
-
// is the only place the model sees the rules — compaction
|
|
13404
|
-
// drop it and the model forgets it's in autonomy mode.
|
|
13931
|
+
// a long-running autonomy engine. Without this, the per-iteration
|
|
13932
|
+
// directive is the only place the model sees the rules — compaction
|
|
13933
|
+
// can drop it and the model forgets it's in autonomy mode.
|
|
13405
13934
|
makeAutonomyPromptContributor({
|
|
13406
13935
|
goalPath: goalPathForPrompt,
|
|
13407
|
-
enabled: () => autonomyModeRef.current === "eternal"
|
|
13936
|
+
enabled: () => autonomyModeRef.current === "eternal" || autonomyModeRef.current === "eternal-parallel"
|
|
13408
13937
|
})
|
|
13409
13938
|
]
|
|
13410
13939
|
})
|
|
@@ -13421,7 +13950,13 @@ async function main(argv) {
|
|
|
13421
13950
|
const events = new EventBus();
|
|
13422
13951
|
events.setLogger(logger);
|
|
13423
13952
|
const { metricsSink, healthRegistry } = (() => {
|
|
13424
|
-
const ms = setupMetrics({
|
|
13953
|
+
const ms = setupMetrics({
|
|
13954
|
+
flags,
|
|
13955
|
+
wpaths,
|
|
13956
|
+
events,
|
|
13957
|
+
logger,
|
|
13958
|
+
config: { provider: config.provider, model: config.model }
|
|
13959
|
+
});
|
|
13425
13960
|
return ms;
|
|
13426
13961
|
})();
|
|
13427
13962
|
const spinner = new Spinner();
|
|
@@ -13587,7 +14122,7 @@ async function main(argv) {
|
|
|
13587
14122
|
});
|
|
13588
14123
|
const pipelines = setupPipelines({ events, logger });
|
|
13589
14124
|
const compactor = container.resolve(TOKENS.Compactor);
|
|
13590
|
-
const
|
|
14125
|
+
const compactionSetup = await setupCompaction({
|
|
13591
14126
|
compactor,
|
|
13592
14127
|
events,
|
|
13593
14128
|
modelsRegistry,
|
|
@@ -13599,18 +14134,41 @@ async function main(argv) {
|
|
|
13599
14134
|
sessionBridge
|
|
13600
14135
|
// share the same bridge for consistent audit logging (compaction + errors + future)
|
|
13601
14136
|
});
|
|
13602
|
-
|
|
13603
|
-
|
|
13604
|
-
|
|
13605
|
-
|
|
13606
|
-
|
|
14137
|
+
let effectiveMaxContext = compactionSetup.effectiveMaxContext;
|
|
14138
|
+
context.provider.capabilities.maxContext = effectiveMaxContext;
|
|
14139
|
+
const { autoCompactor } = compactionSetup;
|
|
14140
|
+
const refreshMaxContext = async (providerId, modelId, runtimeProviderConfig) => {
|
|
14141
|
+
const mc = await resolveRuntimeMaxContext({
|
|
14142
|
+
modelsRegistry,
|
|
14143
|
+
config,
|
|
14144
|
+
provider: context.provider,
|
|
14145
|
+
runtimeProviderConfig,
|
|
14146
|
+
providerId,
|
|
14147
|
+
modelId
|
|
14148
|
+
});
|
|
14149
|
+
effectiveMaxContext = mc;
|
|
14150
|
+
context.provider.capabilities.maxContext = effectiveMaxContext;
|
|
14151
|
+
if (effectiveMaxContext > 0) {
|
|
14152
|
+
autoCompactor?.setMaxContext(effectiveMaxContext);
|
|
14153
|
+
}
|
|
14154
|
+
events.emit("ctx.max_context", { providerId, modelId, maxContext: effectiveMaxContext });
|
|
14155
|
+
updateSpinnerContext();
|
|
13607
14156
|
};
|
|
13608
14157
|
const updateSpinnerContext = () => {
|
|
13609
14158
|
if (effectiveMaxContext > 0 && lastInputTokens > 0) {
|
|
13610
14159
|
spinner.setContext({ used: lastInputTokens, max: effectiveMaxContext });
|
|
13611
14160
|
} else spinner.setContext(void 0);
|
|
13612
14161
|
};
|
|
13613
|
-
const agent = createAgent({
|
|
14162
|
+
const agent = createAgent({
|
|
14163
|
+
container,
|
|
14164
|
+
tools: toolRegistry,
|
|
14165
|
+
providers: providerRegistry,
|
|
14166
|
+
events,
|
|
14167
|
+
pipelines,
|
|
14168
|
+
context,
|
|
14169
|
+
config,
|
|
14170
|
+
confirmAwaiter: makeConfirmAwaiter(reader)
|
|
14171
|
+
});
|
|
13614
14172
|
const mcpRegistry = new MCPRegistry({ toolRegistry, events, log: logger });
|
|
13615
14173
|
if (config.features.mcp) {
|
|
13616
14174
|
for (const cfg of Object.values(config.mcpServers ?? {})) {
|
|
@@ -13655,7 +14213,7 @@ async function main(argv) {
|
|
|
13655
14213
|
context.model = modelId;
|
|
13656
14214
|
config = patchConfig(config, { provider: providerId, model: modelId });
|
|
13657
14215
|
configStore.update({ provider: providerId, model: modelId });
|
|
13658
|
-
void refreshMaxContext(resolvedProviderId, modelId);
|
|
14216
|
+
void refreshMaxContext(resolvedProviderId, modelId, cfgWithType);
|
|
13659
14217
|
return null;
|
|
13660
14218
|
} catch (err) {
|
|
13661
14219
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -13684,12 +14242,26 @@ async function main(argv) {
|
|
|
13684
14242
|
}
|
|
13685
14243
|
}
|
|
13686
14244
|
};
|
|
13687
|
-
const
|
|
13688
|
-
const
|
|
13689
|
-
|
|
13690
|
-
|
|
13691
|
-
|
|
13692
|
-
|
|
14245
|
+
const stageListeners = /* @__PURE__ */ new Set();
|
|
14246
|
+
const broadcastAutonomyStage = (stage) => {
|
|
14247
|
+
for (const fn of stageListeners) {
|
|
14248
|
+
try {
|
|
14249
|
+
fn(stage);
|
|
14250
|
+
} catch {
|
|
14251
|
+
}
|
|
14252
|
+
}
|
|
14253
|
+
};
|
|
14254
|
+
const fleetRoot = directorMode ? path8.join(wpaths.projectSessions, session.id) : void 0;
|
|
14255
|
+
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path8.join(fleetRoot, "fleet.json") : void 0;
|
|
14256
|
+
const sharedScratchpadPath = directorMode ? path8.join(fleetRoot, "shared") : void 0;
|
|
14257
|
+
const subagentSessionsRoot = directorMode ? path8.join(fleetRoot, "subagents") : void 0;
|
|
14258
|
+
const stateCheckpointPath = directorMode ? path8.join(fleetRoot, "director-state.json") : void 0;
|
|
14259
|
+
const fleetRootForPromotion = path8.join(wpaths.projectSessions, session.id);
|
|
14260
|
+
const brainQueue = new BrainDecisionQueue(events);
|
|
14261
|
+
const brain = new ObservableBrainArbiter(
|
|
14262
|
+
new HumanEscalatingBrainArbiter(new DefaultBrainArbiter(), brainQueue),
|
|
14263
|
+
events
|
|
14264
|
+
);
|
|
13693
14265
|
const multiAgentHost = new MultiAgentHost(
|
|
13694
14266
|
{
|
|
13695
14267
|
container,
|
|
@@ -13714,7 +14286,9 @@ async function main(argv) {
|
|
|
13714
14286
|
fleetRoot: fleetRootForPromotion,
|
|
13715
14287
|
stateCheckpointPath,
|
|
13716
14288
|
sessionWriter: session,
|
|
13717
|
-
maxConcurrent
|
|
14289
|
+
maxConcurrent,
|
|
14290
|
+
getLeaderMaxContext: () => effectiveMaxContext,
|
|
14291
|
+
brain
|
|
13718
14292
|
}
|
|
13719
14293
|
);
|
|
13720
14294
|
toolRegistry.register(
|
|
@@ -13785,6 +14359,7 @@ async function main(argv) {
|
|
|
13785
14359
|
events,
|
|
13786
14360
|
storeDir: wpaths.projectAutophase,
|
|
13787
14361
|
projectRoot,
|
|
14362
|
+
brain,
|
|
13788
14363
|
log: (line) => renderer.write(`${line}
|
|
13789
14364
|
`)
|
|
13790
14365
|
});
|
|
@@ -13881,7 +14456,9 @@ async function main(argv) {
|
|
|
13881
14456
|
for (const a of s.live) {
|
|
13882
14457
|
if (a.status === "running" || a.status === "idle") {
|
|
13883
14458
|
const task = a.task ? ` \u2014 ${a.task.slice(0, 60)}` : "";
|
|
13884
|
-
lines.push(
|
|
14459
|
+
lines.push(
|
|
14460
|
+
` ${STATUS_ICON[a.status] ?? "?"} ${a.subagentId.slice(0, 8)} ${a.status}${task}`
|
|
14461
|
+
);
|
|
13885
14462
|
}
|
|
13886
14463
|
}
|
|
13887
14464
|
for (const p of s.pending) {
|
|
@@ -14015,7 +14592,8 @@ async function main(argv) {
|
|
|
14015
14592
|
}
|
|
14016
14593
|
},
|
|
14017
14594
|
onFleetSpawn: async (role) => {
|
|
14018
|
-
if (!director)
|
|
14595
|
+
if (!director)
|
|
14596
|
+
throw new Error("No director active \u2014 start with --director or use /autonomy parallel.");
|
|
14019
14597
|
const cfg = FLEET_ROSTER[role] ?? {
|
|
14020
14598
|
id: `manual-${Date.now()}`,
|
|
14021
14599
|
name: role,
|
|
@@ -14025,7 +14603,7 @@ async function main(argv) {
|
|
|
14025
14603
|
return director.spawn(cfg);
|
|
14026
14604
|
},
|
|
14027
14605
|
onFleetLog: async (subagentId, mode) => {
|
|
14028
|
-
const subagentsRoot =
|
|
14606
|
+
const subagentsRoot = path8.join(fleetRootForPromotion, "subagents");
|
|
14029
14607
|
let runDirs;
|
|
14030
14608
|
try {
|
|
14031
14609
|
runDirs = await fsp3.readdir(subagentsRoot);
|
|
@@ -14034,7 +14612,7 @@ async function main(argv) {
|
|
|
14034
14612
|
}
|
|
14035
14613
|
const found = [];
|
|
14036
14614
|
for (const runId of runDirs) {
|
|
14037
|
-
const runDir =
|
|
14615
|
+
const runDir = path8.join(subagentsRoot, runId);
|
|
14038
14616
|
let files;
|
|
14039
14617
|
try {
|
|
14040
14618
|
files = await fsp3.readdir(runDir);
|
|
@@ -14043,7 +14621,7 @@ async function main(argv) {
|
|
|
14043
14621
|
}
|
|
14044
14622
|
for (const f of files) {
|
|
14045
14623
|
if (!f.endsWith(".jsonl")) continue;
|
|
14046
|
-
const full =
|
|
14624
|
+
const full = path8.join(runDir, f);
|
|
14047
14625
|
try {
|
|
14048
14626
|
const stat4 = await fsp3.stat(full);
|
|
14049
14627
|
found.push({
|
|
@@ -14068,7 +14646,9 @@ async function main(argv) {
|
|
|
14068
14646
|
` ${color.cyan(t2.subagentId.padEnd(18))} ${color.dim(t2.runId.slice(0, 18))} ${color.dim(`${(t2.size / 1024).toFixed(1)} KB`)}`
|
|
14069
14647
|
);
|
|
14070
14648
|
}
|
|
14071
|
-
lines2.push(
|
|
14649
|
+
lines2.push(
|
|
14650
|
+
"Use `/fleet log <subagentId>` for a summary, or append `raw` for the full JSONL."
|
|
14651
|
+
);
|
|
14072
14652
|
return lines2.join("\n");
|
|
14073
14653
|
}
|
|
14074
14654
|
const matches = found.filter(
|
|
@@ -14097,7 +14677,9 @@ async function main(argv) {
|
|
|
14097
14677
|
const ev = JSON.parse(line);
|
|
14098
14678
|
counts[ev.type] = (counts[ev.type] ?? 0) + 1;
|
|
14099
14679
|
if (ev.type === "user_input" && !firstUser) {
|
|
14100
|
-
const txt = typeof ev.content === "string" ? ev.content : Array.isArray(ev.content) ? ev.content.filter(
|
|
14680
|
+
const txt = typeof ev.content === "string" ? ev.content : Array.isArray(ev.content) ? ev.content.filter(
|
|
14681
|
+
(b) => b.type === "text"
|
|
14682
|
+
).map((b) => b.text).join(" ") : "";
|
|
14101
14683
|
firstUser = txt.slice(0, 120);
|
|
14102
14684
|
}
|
|
14103
14685
|
if (ev.type === "llm_response") {
|
|
@@ -14140,7 +14722,7 @@ async function main(argv) {
|
|
|
14140
14722
|
}
|
|
14141
14723
|
const dir = await multiAgentHost.ensureDirector();
|
|
14142
14724
|
if (!dir) return "Director is not available.";
|
|
14143
|
-
const dirStatePath =
|
|
14725
|
+
const dirStatePath = path8.join(fleetRootForPromotion, "director-state.json");
|
|
14144
14726
|
const prior = await loadDirectorState(dirStatePath);
|
|
14145
14727
|
if (!prior) {
|
|
14146
14728
|
return "No prior director-state.json found \u2014 nothing to retry.";
|
|
@@ -14165,9 +14747,7 @@ async function main(argv) {
|
|
|
14165
14747
|
lines.push("Run `/fleet retry <taskId>` or `/fleet retry all` to re-assign.");
|
|
14166
14748
|
return lines.join("\n");
|
|
14167
14749
|
}
|
|
14168
|
-
const targets = taskId === "all" ? interrupted : interrupted.filter(
|
|
14169
|
-
(t) => t.taskId === taskId || t.taskId.startsWith(taskId)
|
|
14170
|
-
);
|
|
14750
|
+
const targets = taskId === "all" ? interrupted : interrupted.filter((t) => t.taskId === taskId || t.taskId.startsWith(taskId));
|
|
14171
14751
|
if (targets.length === 0) {
|
|
14172
14752
|
return `No interrupted task matched "${taskId}".`;
|
|
14173
14753
|
}
|
|
@@ -14211,9 +14791,9 @@ async function main(argv) {
|
|
|
14211
14791
|
for (const tool of director2.tools(FLEET_ROSTER)) {
|
|
14212
14792
|
toolRegistry.register(tool);
|
|
14213
14793
|
}
|
|
14214
|
-
const mp =
|
|
14215
|
-
const sp =
|
|
14216
|
-
const ss =
|
|
14794
|
+
const mp = path8.join(fleetRootForPromotion, "fleet.json");
|
|
14795
|
+
const sp = path8.join(fleetRootForPromotion, "shared");
|
|
14796
|
+
const ss = path8.join(fleetRootForPromotion, "subagents");
|
|
14217
14797
|
const lines = [
|
|
14218
14798
|
`${color.green("\u2713")} Promoted to director mode.`,
|
|
14219
14799
|
` Roster: ${Object.keys(FLEET_ROSTER).join(", ")}`,
|
|
@@ -14284,17 +14864,19 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
14284
14864
|
const effectiveMode = mode ?? "eternal";
|
|
14285
14865
|
if (effectiveMode === "eternal-parallel") {
|
|
14286
14866
|
if (!parallelEngine) {
|
|
14287
|
-
|
|
14867
|
+
const parallelOptions = {
|
|
14288
14868
|
agent,
|
|
14289
14869
|
projectRoot,
|
|
14290
14870
|
compactor: container.resolve(TOKENS.Compactor),
|
|
14291
14871
|
maxContextTokens: effectiveMaxContext > 0 ? effectiveMaxContext : void 0,
|
|
14292
14872
|
onIteration: broadcastEternalIteration,
|
|
14873
|
+
onStage: broadcastAutonomyStage,
|
|
14293
14874
|
// Real per-role factory: each dispatched slot runs as a fresh,
|
|
14294
14875
|
// isolated agent with the role's filtered tools + persona prompt
|
|
14295
14876
|
// (instead of sharing the leader agent's Context).
|
|
14296
14877
|
subagentFactory: multiAgentHost.makeSubagentFactory(config)
|
|
14297
|
-
}
|
|
14878
|
+
};
|
|
14879
|
+
parallelEngine = new ParallelEternalEngine(parallelOptions);
|
|
14298
14880
|
}
|
|
14299
14881
|
void parallelEngine.prime?.();
|
|
14300
14882
|
} else {
|
|
@@ -14304,7 +14886,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
14304
14886
|
projectRoot,
|
|
14305
14887
|
compactor: container.resolve(TOKENS.Compactor),
|
|
14306
14888
|
maxContextTokens: effectiveMaxContext > 0 ? effectiveMaxContext : void 0,
|
|
14307
|
-
onIteration: broadcastEternalIteration
|
|
14889
|
+
onIteration: broadcastEternalIteration,
|
|
14890
|
+
onStage: broadcastAutonomyStage
|
|
14308
14891
|
});
|
|
14309
14892
|
}
|
|
14310
14893
|
void eternalEngine.prime();
|
|
@@ -14315,20 +14898,26 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
14315
14898
|
parallelEngine?.stop();
|
|
14316
14899
|
},
|
|
14317
14900
|
onExit: () => {
|
|
14901
|
+
brainQueue.dispose();
|
|
14318
14902
|
void mcpRegistry.stopAll();
|
|
14319
14903
|
},
|
|
14320
14904
|
onBeforeExit: async () => {
|
|
14321
14905
|
const { spawn: spawn3 } = await import('child_process');
|
|
14322
14906
|
const cwd2 = projectRoot;
|
|
14323
|
-
const statusResult = await new Promise(
|
|
14324
|
-
|
|
14325
|
-
|
|
14326
|
-
|
|
14327
|
-
|
|
14328
|
-
|
|
14329
|
-
|
|
14330
|
-
|
|
14331
|
-
|
|
14907
|
+
const statusResult = await new Promise(
|
|
14908
|
+
(resolve3, reject) => {
|
|
14909
|
+
const child = spawn3("git", ["status", "--porcelain"], {
|
|
14910
|
+
cwd: cwd2,
|
|
14911
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
14912
|
+
});
|
|
14913
|
+
let stdout = "";
|
|
14914
|
+
child.stdout?.on("data", (d) => {
|
|
14915
|
+
stdout += d;
|
|
14916
|
+
});
|
|
14917
|
+
child.on("error", reject);
|
|
14918
|
+
child.on("close", (code) => resolve3({ stdout, code: code ?? 0 }));
|
|
14919
|
+
}
|
|
14920
|
+
);
|
|
14332
14921
|
if (statusResult.stdout.trim().length > 0) {
|
|
14333
14922
|
const lines = statusResult.stdout.split("\n").filter(Boolean);
|
|
14334
14923
|
return {
|
|
@@ -14405,8 +14994,10 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
14405
14994
|
parallelSlots: opts?.parallelSlots,
|
|
14406
14995
|
subagentFactory,
|
|
14407
14996
|
onProgress: (p) => {
|
|
14408
|
-
renderer.write(
|
|
14409
|
-
`
|
|
14997
|
+
renderer.write(
|
|
14998
|
+
` \u2591 wave ${p.wave + 1} \xB7 ${p.completed}/${p.total} tasks \xB7 ${p.percent}% done
|
|
14999
|
+
`
|
|
15000
|
+
);
|
|
14410
15001
|
}
|
|
14411
15002
|
});
|
|
14412
15003
|
globalThis.__sddParallelRun = run;
|