@wrongstack/cli 0.107.2 → 0.119.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/dist/index.js +1161 -156
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { color, writeErr, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, DefaultSecretScrubber, DefaultPathResolver, EventBus, TOKENS, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, SpecVersioning, atomicWrite, DefaultLogger, DefaultModelsRegistry, isStdinTTY, writeOut, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, resolveContextWindowPolicy, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, DefaultPluginAPI, ProviderError, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, AGENTS_BY_PHASE, dispatchAgent, formatTodosList, loadTasks, emptyTaskFile, saveTasks, formatTaskProgress, formatTaskList, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, AGENT_CATALOG, matrixKeyKind, phaseForRole, onResize, ERROR_CODES, InputBuilder, FsError } from '@wrongstack/core';
|
|
2
|
+
import { color, writeErr, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, DefaultSecretScrubber, DefaultPathResolver, EventBus, TOKENS, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, SpecVersioning, atomicWrite, DefaultLogger, DefaultModelsRegistry, isStdinTTY, writeOut, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, resolveContextWindowPolicy, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, DefaultPluginAPI, StreamHangError, ProviderError, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, AGENTS_BY_PHASE, dispatchAgent, formatTodosList, loadTasks, emptyTaskFile, saveTasks, formatTaskProgress, formatTaskList, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, AGENT_CATALOG, matrixKeyKind, phaseForRole, onResize, ERROR_CODES, InputBuilder, FsError } from '@wrongstack/core';
|
|
3
3
|
import * as fsp4 from 'fs/promises';
|
|
4
4
|
import { DefaultSecretVault, decryptConfigSecrets, encryptConfigSecrets, isSecretField } from '@wrongstack/core/security';
|
|
5
|
-
import * as
|
|
5
|
+
import * as path9 from 'path';
|
|
6
6
|
import { join } from 'path';
|
|
7
7
|
import { createRequire } from 'module';
|
|
8
8
|
import * as os2 from 'os';
|
|
@@ -18,7 +18,7 @@ import { createDefaultContainer, routeImagesForModel, readClipboardImage } from
|
|
|
18
18
|
import { builtinToolsPack, rememberTool, forgetTool, searchMemoryTool, relatedMemoryTool, runStartupIndex, isIndexableFile, enqueueReindex, cancelPendingReindexes } from '@wrongstack/tools';
|
|
19
19
|
import { fileURLToPath } from 'url';
|
|
20
20
|
import * as readline from 'readline';
|
|
21
|
-
import * as
|
|
21
|
+
import * as fs14 from 'fs';
|
|
22
22
|
import { writeFileSync, existsSync, readFileSync } from 'fs';
|
|
23
23
|
import { WrongStackACPServer } from '@wrongstack/acp/agent';
|
|
24
24
|
import { ACP_AGENT_COMMANDS, makeACPSubagentRunner, makeACPSubagentRunnerWithStop } from '@wrongstack/acp';
|
|
@@ -386,7 +386,7 @@ async function findSpec(store, idOrTitle) {
|
|
|
386
386
|
async function gatherProjectContext2(projectRoot) {
|
|
387
387
|
const parts = [];
|
|
388
388
|
try {
|
|
389
|
-
const pkgPath =
|
|
389
|
+
const pkgPath = path9.join(projectRoot, "package.json");
|
|
390
390
|
const pkgRaw = await fsp4.readFile(pkgPath, "utf8");
|
|
391
391
|
const pkg = JSON.parse(pkgRaw);
|
|
392
392
|
parts.push(`Project: ${String(pkg.name ?? "unknown")}`);
|
|
@@ -402,13 +402,13 @@ async function gatherProjectContext2(projectRoot) {
|
|
|
402
402
|
} catch {
|
|
403
403
|
}
|
|
404
404
|
try {
|
|
405
|
-
const tsconfigPath =
|
|
405
|
+
const tsconfigPath = path9.join(projectRoot, "tsconfig.json");
|
|
406
406
|
await fsp4.access(tsconfigPath);
|
|
407
407
|
parts.push("Language: TypeScript");
|
|
408
408
|
} catch {
|
|
409
409
|
}
|
|
410
410
|
try {
|
|
411
|
-
const srcDir =
|
|
411
|
+
const srcDir = path9.join(projectRoot, "src");
|
|
412
412
|
const entries = await fsp4.readdir(srcDir, { withFileTypes: true });
|
|
413
413
|
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
414
414
|
if (dirs.length > 0) parts.push(`Source structure: src/${dirs.join(", src/")}`);
|
|
@@ -1602,7 +1602,7 @@ __export(update_check_exports, {
|
|
|
1602
1602
|
getUpdateNotification: () => getUpdateNotification
|
|
1603
1603
|
});
|
|
1604
1604
|
function cachePath(homeFn = defaultHomeDir2) {
|
|
1605
|
-
return
|
|
1605
|
+
return path9.join(homeFn(), ".wrongstack", "update-cache.json");
|
|
1606
1606
|
}
|
|
1607
1607
|
function currentVersion() {
|
|
1608
1608
|
const req2 = createRequire(import.meta.url);
|
|
@@ -1639,7 +1639,7 @@ async function readCache(homeFn = defaultHomeDir2) {
|
|
|
1639
1639
|
}
|
|
1640
1640
|
async function writeCache(entry, homeFn = defaultHomeDir2) {
|
|
1641
1641
|
try {
|
|
1642
|
-
const dir =
|
|
1642
|
+
const dir = path9.dirname(cachePath(homeFn));
|
|
1643
1643
|
await fsp4.mkdir(dir, { recursive: true });
|
|
1644
1644
|
await fsp4.writeFile(cachePath(homeFn), JSON.stringify(entry, null, 2), "utf8");
|
|
1645
1645
|
} catch {
|
|
@@ -1746,7 +1746,7 @@ async function runWebUI(opts) {
|
|
|
1746
1746
|
try {
|
|
1747
1747
|
const requireFromHere = createRequire(import.meta.url);
|
|
1748
1748
|
const serverEntry = requireFromHere.resolve("@wrongstack/webui/server");
|
|
1749
|
-
const distDir =
|
|
1749
|
+
const distDir = path9.resolve(path9.dirname(serverEntry), "..");
|
|
1750
1750
|
httpServer = createHttpServer({ host, distDir, wsPort });
|
|
1751
1751
|
const openUrl = `http://${host}:${httpPort}`;
|
|
1752
1752
|
httpServer?.listen(httpPort, host, () => {
|
|
@@ -1763,7 +1763,7 @@ async function runWebUI(opts) {
|
|
|
1763
1763
|
`[WebUI] Frontend not served (run \`pnpm --filter @wrongstack/webui build\`): ${err instanceof Error ? err.message : String(err)}. WS bridge still active on ws://${host}:${wsPort}.`
|
|
1764
1764
|
);
|
|
1765
1765
|
}
|
|
1766
|
-
const registryBaseDir = opts.globalConfigPath ?
|
|
1766
|
+
const registryBaseDir = opts.globalConfigPath ? path9.dirname(opts.globalConfigPath) : void 0;
|
|
1767
1767
|
if (opts.projectRoot) {
|
|
1768
1768
|
void registerInstance(
|
|
1769
1769
|
{
|
|
@@ -1772,7 +1772,7 @@ async function runWebUI(opts) {
|
|
|
1772
1772
|
wsPort,
|
|
1773
1773
|
host,
|
|
1774
1774
|
projectRoot: opts.projectRoot,
|
|
1775
|
-
projectName:
|
|
1775
|
+
projectName: path9.basename(opts.projectRoot) || opts.projectRoot,
|
|
1776
1776
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1777
1777
|
url: `http://${host}:${httpPort}`
|
|
1778
1778
|
},
|
|
@@ -2400,7 +2400,7 @@ async function runWebUI(opts) {
|
|
|
2400
2400
|
}
|
|
2401
2401
|
}
|
|
2402
2402
|
function getVault() {
|
|
2403
|
-
const keyFile =
|
|
2403
|
+
const keyFile = path9.join(path9.dirname(opts.globalConfigPath ?? ""), ".key");
|
|
2404
2404
|
return new DefaultSecretVault({ keyFile });
|
|
2405
2405
|
}
|
|
2406
2406
|
async function loadSavedProviders() {
|
|
@@ -2701,10 +2701,10 @@ async function detectPackageManager(root, declared) {
|
|
|
2701
2701
|
const name = declared.split("@")[0];
|
|
2702
2702
|
if (name) return name;
|
|
2703
2703
|
}
|
|
2704
|
-
if (await pathExists(
|
|
2705
|
-
if (await pathExists(
|
|
2706
|
-
if (await pathExists(
|
|
2707
|
-
if (await pathExists(
|
|
2704
|
+
if (await pathExists(path9.join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
2705
|
+
if (await pathExists(path9.join(root, "bun.lockb"))) return "bun";
|
|
2706
|
+
if (await pathExists(path9.join(root, "bun.lock"))) return "bun";
|
|
2707
|
+
if (await pathExists(path9.join(root, "yarn.lock"))) return "yarn";
|
|
2708
2708
|
return "npm";
|
|
2709
2709
|
}
|
|
2710
2710
|
function hasUsableScript(scripts, name) {
|
|
@@ -2725,7 +2725,7 @@ function parseMakeTargets(makefile) {
|
|
|
2725
2725
|
async function detectProjectFacts(root) {
|
|
2726
2726
|
const facts = { hints: [] };
|
|
2727
2727
|
try {
|
|
2728
|
-
const pkg = JSON.parse(await fsp4.readFile(
|
|
2728
|
+
const pkg = JSON.parse(await fsp4.readFile(path9.join(root, "package.json"), "utf8"));
|
|
2729
2729
|
const scripts = pkg.scripts ?? {};
|
|
2730
2730
|
const pm = await detectPackageManager(root, pkg.packageManager);
|
|
2731
2731
|
if (hasUsableScript(scripts, "build")) facts.build = `${pm} run build`;
|
|
@@ -2739,14 +2739,14 @@ async function detectProjectFacts(root) {
|
|
|
2739
2739
|
} catch {
|
|
2740
2740
|
}
|
|
2741
2741
|
try {
|
|
2742
|
-
if (!await pathExists(
|
|
2742
|
+
if (!await pathExists(path9.join(root, "pyproject.toml"))) throw new Error("not python");
|
|
2743
2743
|
facts.test ??= "pytest";
|
|
2744
2744
|
facts.lint ??= "ruff check .";
|
|
2745
2745
|
facts.hints.push("pyproject.toml");
|
|
2746
2746
|
} catch {
|
|
2747
2747
|
}
|
|
2748
2748
|
try {
|
|
2749
|
-
if (!await pathExists(
|
|
2749
|
+
if (!await pathExists(path9.join(root, "go.mod"))) throw new Error("not go");
|
|
2750
2750
|
facts.build ??= "go build ./...";
|
|
2751
2751
|
facts.test ??= "go test ./...";
|
|
2752
2752
|
facts.run ??= "go run .";
|
|
@@ -2754,7 +2754,7 @@ async function detectProjectFacts(root) {
|
|
|
2754
2754
|
} catch {
|
|
2755
2755
|
}
|
|
2756
2756
|
try {
|
|
2757
|
-
if (!await pathExists(
|
|
2757
|
+
if (!await pathExists(path9.join(root, "Cargo.toml"))) throw new Error("not rust");
|
|
2758
2758
|
facts.build ??= "cargo build";
|
|
2759
2759
|
facts.test ??= "cargo test";
|
|
2760
2760
|
facts.lint ??= "cargo clippy";
|
|
@@ -2763,7 +2763,7 @@ async function detectProjectFacts(root) {
|
|
|
2763
2763
|
} catch {
|
|
2764
2764
|
}
|
|
2765
2765
|
try {
|
|
2766
|
-
const makefile = await fsp4.readFile(
|
|
2766
|
+
const makefile = await fsp4.readFile(path9.join(root, "Makefile"), "utf8");
|
|
2767
2767
|
const targets = parseMakeTargets(makefile);
|
|
2768
2768
|
facts.build ??= targets.has("build") ? "make build" : "make";
|
|
2769
2769
|
if (targets.has("test")) facts.test ??= "make test";
|
|
@@ -3071,7 +3071,7 @@ function formatPhaseList(graph) {
|
|
|
3071
3071
|
}
|
|
3072
3072
|
async function gatherProjectContext(projectRoot) {
|
|
3073
3073
|
try {
|
|
3074
|
-
const raw = await fsp4.readFile(
|
|
3074
|
+
const raw = await fsp4.readFile(path9.join(projectRoot, "package.json"), "utf8");
|
|
3075
3075
|
const pkg = JSON.parse(raw);
|
|
3076
3076
|
const parts = [
|
|
3077
3077
|
`Project: ${String(pkg.name ?? "unknown")}`,
|
|
@@ -3993,14 +3993,59 @@ function buildStatsCommand(opts) {
|
|
|
3993
3993
|
}
|
|
3994
3994
|
};
|
|
3995
3995
|
}
|
|
3996
|
+
function resolvePersistPath(deps) {
|
|
3997
|
+
const scope = deps.configStore.get().configScope;
|
|
3998
|
+
if (scope === "project" && deps.inProjectConfigPath) {
|
|
3999
|
+
return deps.inProjectConfigPath;
|
|
4000
|
+
}
|
|
4001
|
+
return deps.globalConfigPath;
|
|
4002
|
+
}
|
|
4003
|
+
async function ensureProjectDir(filePath) {
|
|
4004
|
+
const dir = path9.dirname(filePath);
|
|
4005
|
+
try {
|
|
4006
|
+
await fsp4.mkdir(dir, { recursive: true });
|
|
4007
|
+
} catch {
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
var PROJECT_SAFE_FIELDS = /* @__PURE__ */ new Set([
|
|
4011
|
+
"provider",
|
|
4012
|
+
"model",
|
|
4013
|
+
"fallbackModels",
|
|
4014
|
+
"modelMatrix",
|
|
4015
|
+
"maxConcurrent",
|
|
4016
|
+
"autonomy",
|
|
4017
|
+
"hints",
|
|
4018
|
+
"nextPrediction",
|
|
4019
|
+
"debugStream",
|
|
4020
|
+
"configScope",
|
|
4021
|
+
"yolo",
|
|
4022
|
+
"features",
|
|
4023
|
+
"context",
|
|
4024
|
+
"log",
|
|
4025
|
+
"session",
|
|
4026
|
+
"indexing",
|
|
4027
|
+
"tools",
|
|
4028
|
+
"launch"
|
|
4029
|
+
]);
|
|
4030
|
+
function filterSafeForProject(cfg) {
|
|
4031
|
+
const out = {};
|
|
4032
|
+
for (const [key, value] of Object.entries(cfg)) {
|
|
4033
|
+
if (PROJECT_SAFE_FIELDS.has(key)) {
|
|
4034
|
+
out[key] = value;
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
return out;
|
|
4038
|
+
}
|
|
3996
4039
|
async function persistAutonomySetting(deps, mutator) {
|
|
4040
|
+
const targetPath = resolvePersistPath(deps);
|
|
4041
|
+
await ensureProjectDir(targetPath);
|
|
3997
4042
|
let raw;
|
|
3998
4043
|
let fileExists = true;
|
|
3999
4044
|
try {
|
|
4000
|
-
raw = await fsp4.readFile(
|
|
4045
|
+
raw = await fsp4.readFile(targetPath, "utf8");
|
|
4001
4046
|
} catch (err) {
|
|
4002
4047
|
if (err.code !== "ENOENT") {
|
|
4003
|
-
throw new Error(`Could not read ${
|
|
4048
|
+
throw new Error(`Could not read ${targetPath}: ${err.message}`);
|
|
4004
4049
|
}
|
|
4005
4050
|
fileExists = false;
|
|
4006
4051
|
raw = "{}";
|
|
@@ -4010,7 +4055,7 @@ async function persistAutonomySetting(deps, mutator) {
|
|
|
4010
4055
|
parsed = JSON.parse(raw);
|
|
4011
4056
|
} catch (err) {
|
|
4012
4057
|
if (fileExists) {
|
|
4013
|
-
throw new Error(`Config at ${
|
|
4058
|
+
throw new Error(`Config at ${targetPath} is not valid JSON: ${err.message}`);
|
|
4014
4059
|
}
|
|
4015
4060
|
parsed = {};
|
|
4016
4061
|
}
|
|
@@ -4018,18 +4063,26 @@ async function persistAutonomySetting(deps, mutator) {
|
|
|
4018
4063
|
const autonomy = decrypted.autonomy ?? {};
|
|
4019
4064
|
mutator(autonomy);
|
|
4020
4065
|
decrypted.autonomy = autonomy;
|
|
4021
|
-
const
|
|
4022
|
-
|
|
4066
|
+
const newScope = decrypted.configScope;
|
|
4067
|
+
const actualTarget = newScope === "project" && deps.inProjectConfigPath ? deps.inProjectConfigPath : newScope === "global" ? deps.globalConfigPath : targetPath;
|
|
4068
|
+
if (actualTarget !== targetPath) {
|
|
4069
|
+
await ensureProjectDir(actualTarget);
|
|
4070
|
+
}
|
|
4071
|
+
const toWrite = actualTarget === deps.globalConfigPath ? decrypted : filterSafeForProject(decrypted);
|
|
4072
|
+
const encrypted = encryptConfigSecrets$1(toWrite, deps.vault);
|
|
4073
|
+
await atomicWrite(actualTarget, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
4023
4074
|
deps.configStore.update({ autonomy: decrypted.autonomy });
|
|
4024
4075
|
}
|
|
4025
4076
|
async function persistConfigSetting(deps, mutator) {
|
|
4077
|
+
const targetPath = resolvePersistPath(deps);
|
|
4078
|
+
await ensureProjectDir(targetPath);
|
|
4026
4079
|
let raw;
|
|
4027
4080
|
let fileExists = true;
|
|
4028
4081
|
try {
|
|
4029
|
-
raw = await fsp4.readFile(
|
|
4082
|
+
raw = await fsp4.readFile(targetPath, "utf8");
|
|
4030
4083
|
} catch (err) {
|
|
4031
4084
|
if (err.code !== "ENOENT") {
|
|
4032
|
-
throw new Error(`Could not read ${
|
|
4085
|
+
throw new Error(`Could not read ${targetPath}: ${err.message}`);
|
|
4033
4086
|
}
|
|
4034
4087
|
fileExists = false;
|
|
4035
4088
|
raw = "{}";
|
|
@@ -4039,14 +4092,20 @@ async function persistConfigSetting(deps, mutator) {
|
|
|
4039
4092
|
parsed = JSON.parse(raw);
|
|
4040
4093
|
} catch (err) {
|
|
4041
4094
|
if (fileExists) {
|
|
4042
|
-
throw new Error(`Config at ${
|
|
4095
|
+
throw new Error(`Config at ${targetPath} is not valid JSON: ${err.message}`);
|
|
4043
4096
|
}
|
|
4044
4097
|
parsed = {};
|
|
4045
4098
|
}
|
|
4046
4099
|
const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
|
|
4047
4100
|
mutator(decrypted);
|
|
4048
|
-
const
|
|
4049
|
-
|
|
4101
|
+
const newScope = decrypted.configScope;
|
|
4102
|
+
const actualTarget = newScope === "project" && deps.inProjectConfigPath ? deps.inProjectConfigPath : newScope === "global" ? deps.globalConfigPath : targetPath;
|
|
4103
|
+
if (actualTarget !== targetPath) {
|
|
4104
|
+
await ensureProjectDir(actualTarget);
|
|
4105
|
+
}
|
|
4106
|
+
const toWrite = actualTarget === deps.globalConfigPath ? decrypted : filterSafeForProject(decrypted);
|
|
4107
|
+
const encrypted = encryptConfigSecrets$1(toWrite, deps.vault);
|
|
4108
|
+
await atomicWrite(actualTarget, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
4050
4109
|
deps.configStore.update(decrypted);
|
|
4051
4110
|
}
|
|
4052
4111
|
async function persistTelegramConfig(deps, mutator) {
|
|
@@ -5690,8 +5749,8 @@ function buildInitCommand(opts) {
|
|
|
5690
5749
|
category: "Config",
|
|
5691
5750
|
description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
|
|
5692
5751
|
async run(_args, ctx) {
|
|
5693
|
-
const dir =
|
|
5694
|
-
const file =
|
|
5752
|
+
const dir = path9.join(ctx.projectRoot, ".wrongstack");
|
|
5753
|
+
const file = path9.join(dir, "AGENTS.md");
|
|
5695
5754
|
const detected = await detectProjectFacts(ctx.projectRoot);
|
|
5696
5755
|
const body = renderAgentsTemplate(detected);
|
|
5697
5756
|
await fsp4.mkdir(dir, { recursive: true });
|
|
@@ -5906,9 +5965,9 @@ function stateBadge(state) {
|
|
|
5906
5965
|
return color.dim(state);
|
|
5907
5966
|
}
|
|
5908
5967
|
}
|
|
5909
|
-
async function readConfig(
|
|
5968
|
+
async function readConfig(path27) {
|
|
5910
5969
|
try {
|
|
5911
|
-
return JSON.parse(await fsp4.readFile(
|
|
5970
|
+
return JSON.parse(await fsp4.readFile(path27, "utf8"));
|
|
5912
5971
|
} catch {
|
|
5913
5972
|
return {};
|
|
5914
5973
|
}
|
|
@@ -5916,11 +5975,11 @@ async function readConfig(path26) {
|
|
|
5916
5975
|
function isMcpServerRecord(value) {
|
|
5917
5976
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5918
5977
|
}
|
|
5919
|
-
async function writeConfig(
|
|
5978
|
+
async function writeConfig(path27, cfg) {
|
|
5920
5979
|
const raw = JSON.stringify(cfg, null, 2);
|
|
5921
|
-
const tmp =
|
|
5980
|
+
const tmp = path27 + ".tmp";
|
|
5922
5981
|
await fsp4.writeFile(tmp, raw, "utf8");
|
|
5923
|
-
await fsp4.rename(tmp,
|
|
5982
|
+
await fsp4.rename(tmp, path27);
|
|
5924
5983
|
}
|
|
5925
5984
|
|
|
5926
5985
|
// src/slash-commands/mcp.ts
|
|
@@ -7134,7 +7193,7 @@ function buildSetModelCommand(opts) {
|
|
|
7134
7193
|
for (const phase of MATRIX_PHASE_KEYS) {
|
|
7135
7194
|
const agents = AGENTS_BY_PHASE[phase];
|
|
7136
7195
|
if (agents && agents.length > 0) {
|
|
7137
|
-
picks.push(agents[0]
|
|
7196
|
+
picks.push(agents[0]?.config.role);
|
|
7138
7197
|
}
|
|
7139
7198
|
}
|
|
7140
7199
|
picks.push("security-scanner", "bug-hunter");
|
|
@@ -7379,6 +7438,130 @@ function buildSetModelCommand(opts) {
|
|
|
7379
7438
|
}
|
|
7380
7439
|
};
|
|
7381
7440
|
}
|
|
7441
|
+
function fmtTokens(n) {
|
|
7442
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
7443
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
7444
|
+
return String(n);
|
|
7445
|
+
}
|
|
7446
|
+
function fmtPrice(pricePer1k) {
|
|
7447
|
+
if (pricePer1k === void 0 || pricePer1k <= 0) return color.dim("\u2014");
|
|
7448
|
+
return `$${pricePer1k.toFixed(2)}/M tok`;
|
|
7449
|
+
}
|
|
7450
|
+
function contextBar(maxContext) {
|
|
7451
|
+
const emoji = maxContext > 2e5 ? "\u{1F7E2}" : maxContext > 128e3 ? "\u{1F7E1}" : "\u{1F534}";
|
|
7452
|
+
return `${emoji} ${fmtTokens(maxContext)}`;
|
|
7453
|
+
}
|
|
7454
|
+
function buildModelCapsCommand(opts) {
|
|
7455
|
+
return {
|
|
7456
|
+
name: "modelcaps",
|
|
7457
|
+
category: "Config",
|
|
7458
|
+
description: "List available models with capacities (context window, max output, pricing).",
|
|
7459
|
+
help: [
|
|
7460
|
+
"Usage:",
|
|
7461
|
+
" /modelcaps List all available models grouped by provider",
|
|
7462
|
+
" /modelcaps <provider> Show models for one provider only",
|
|
7463
|
+
" /modelcaps <fragment> Filter models by id fragment (case-insensitive)",
|
|
7464
|
+
" /modelcaps summary Show agent-type \u2192 model mapping matrix",
|
|
7465
|
+
"",
|
|
7466
|
+
"Capacities shown: context window, max output tokens, input/output pricing.",
|
|
7467
|
+
"\u25CF = API key present \xB7 \u25CB = no key (model listed but not usable)."
|
|
7468
|
+
].join("\n"),
|
|
7469
|
+
async run(args) {
|
|
7470
|
+
const trimmed = args.trim().toLowerCase();
|
|
7471
|
+
if (trimmed === "summary") {
|
|
7472
|
+
return {
|
|
7473
|
+
message: [
|
|
7474
|
+
`${color.bold("Agent-Type \u2192 Model Mapping")} ${color.dim("\u2014 use /setmodel")}`,
|
|
7475
|
+
"",
|
|
7476
|
+
`${color.dim("Run /setmodel to see the current model matrix and resolution chain.")}`,
|
|
7477
|
+
`${color.dim("Each agent role resolves its model via: role \u2192 phase \u2192 * \u2192 leader.")}`,
|
|
7478
|
+
"",
|
|
7479
|
+
`${color.dim("/setmodel \u2014 show leader + matrix + resolution summary")}`,
|
|
7480
|
+
`${color.dim("/setmodel resolve <role> \u2014 walk the resolution chain for one role")}`
|
|
7481
|
+
].join("\n")
|
|
7482
|
+
};
|
|
7483
|
+
}
|
|
7484
|
+
const cachePath2 = opts.paths?.modelsCache;
|
|
7485
|
+
if (!cachePath2) {
|
|
7486
|
+
return { message: `${color.red("Models cache path not available")}.` };
|
|
7487
|
+
}
|
|
7488
|
+
let providers;
|
|
7489
|
+
try {
|
|
7490
|
+
const raw = await fsp4.readFile(cachePath2, "utf8");
|
|
7491
|
+
const parsed = JSON.parse(raw);
|
|
7492
|
+
const payload = parsed.payload ?? parsed;
|
|
7493
|
+
providers = Object.entries(payload).map(([id, p]) => ({
|
|
7494
|
+
id: p.id ?? id,
|
|
7495
|
+
name: p.name ?? id,
|
|
7496
|
+
family: p.npm ?? id,
|
|
7497
|
+
models: Object.values(p.models ?? {}).map((m) => ({
|
|
7498
|
+
id: m.id,
|
|
7499
|
+
name: m.name,
|
|
7500
|
+
capabilities: {
|
|
7501
|
+
contextWindow: m.limit?.context,
|
|
7502
|
+
maxOutputTokens: m.limit?.output
|
|
7503
|
+
},
|
|
7504
|
+
pricing: m.cost
|
|
7505
|
+
}))
|
|
7506
|
+
}));
|
|
7507
|
+
} catch {
|
|
7508
|
+
return {
|
|
7509
|
+
message: [
|
|
7510
|
+
`${color.amber("Models cache not available")}.`,
|
|
7511
|
+
`${color.dim(`Expected at: ${cachePath2}`)}`,
|
|
7512
|
+
"",
|
|
7513
|
+
`${color.dim("Run wstack sync-models or wait for the next auto-sync.")}`
|
|
7514
|
+
].join("\n")
|
|
7515
|
+
};
|
|
7516
|
+
}
|
|
7517
|
+
const config = opts.configStore.get();
|
|
7518
|
+
const configProviders = config?.providers ?? {};
|
|
7519
|
+
function hasKey(providerId) {
|
|
7520
|
+
const pc = configProviders[providerId];
|
|
7521
|
+
if (!pc) return false;
|
|
7522
|
+
if (typeof pc.apiKey === "string" && pc.apiKey.length > 0) return true;
|
|
7523
|
+
if (Array.isArray(pc.apiKeys) && pc.apiKeys.some((k) => k?.apiKey)) return true;
|
|
7524
|
+
return false;
|
|
7525
|
+
}
|
|
7526
|
+
const lines = [
|
|
7527
|
+
`${color.bold("Available Models")} ${color.dim("\u2014 capacities + pricing")}`,
|
|
7528
|
+
""
|
|
7529
|
+
];
|
|
7530
|
+
let shown = 0;
|
|
7531
|
+
for (const prov of providers) {
|
|
7532
|
+
if (trimmed && !trimmed.includes("/") && !prov.id.toLowerCase().includes(trimmed) && !prov.name.toLowerCase().includes(trimmed)) {
|
|
7533
|
+
continue;
|
|
7534
|
+
}
|
|
7535
|
+
const keyed = hasKey(prov.id);
|
|
7536
|
+
const marker = keyed ? color.green("\u25CF") : color.dim("\u25CB");
|
|
7537
|
+
lines.push(` ${marker} ${color.bold(prov.id.padEnd(16))} ${color.dim(`(${prov.name})`)}`);
|
|
7538
|
+
const models = prov.models ?? [];
|
|
7539
|
+
if (models.length === 0) {
|
|
7540
|
+
lines.push(` ${color.dim("no models listed \u2014 any model id accepted")}`);
|
|
7541
|
+
}
|
|
7542
|
+
for (const m of models) {
|
|
7543
|
+
if (trimmed?.includes("/")) {
|
|
7544
|
+
const frag = trimmed.split("/").pop() ?? "";
|
|
7545
|
+
if (frag && !m.id.toLowerCase().includes(frag)) continue;
|
|
7546
|
+
}
|
|
7547
|
+
const cap = m.capabilities;
|
|
7548
|
+
const ctx = cap?.contextWindow ?? 0;
|
|
7549
|
+
const maxOut = cap?.maxOutputTokens ?? 0;
|
|
7550
|
+
lines.push(
|
|
7551
|
+
` ${color.cyan(m.id)} ${contextBar(ctx)}` + (maxOut > 0 ? ` ${color.dim("out")} ${fmtTokens(maxOut)}` : "") + ` ${color.dim("in")} ${fmtPrice(m.pricing?.input)} ${color.dim("out")} ${fmtPrice(m.pricing?.output)}`
|
|
7552
|
+
);
|
|
7553
|
+
shown++;
|
|
7554
|
+
}
|
|
7555
|
+
lines.push("");
|
|
7556
|
+
}
|
|
7557
|
+
if (shown === 0) {
|
|
7558
|
+
lines.push(` ${color.dim("No models matched. Try /modelcaps without a filter.")}`);
|
|
7559
|
+
}
|
|
7560
|
+
lines.push(color.dim(`${shown} model(s). \u25CF = key present \xB7 \u25CB = no key. Use /modelcaps summary for agent-type mapping.`));
|
|
7561
|
+
return { message: lines.join("\n") };
|
|
7562
|
+
}
|
|
7563
|
+
};
|
|
7564
|
+
}
|
|
7382
7565
|
var noOpVault4 = {
|
|
7383
7566
|
encrypt: (v) => v,
|
|
7384
7567
|
decrypt: (v) => v,
|
|
@@ -7396,6 +7579,8 @@ function buildSettingsCommand(opts) {
|
|
|
7396
7579
|
" /settings delay <seconds> Auto-proceed delay in auto mode (0 disables)",
|
|
7397
7580
|
" /settings mode <off|suggest|auto> Default autonomy mode at startup",
|
|
7398
7581
|
" /settings hints on|off Show or suppress rotating launch hints",
|
|
7582
|
+
" /settings debug-stream on|off Raw SSE hex-dump to stderr for debugging",
|
|
7583
|
+
" /settings config-scope global|project Save settings globally or per-project",
|
|
7399
7584
|
" /settings defaults Show built-in default values",
|
|
7400
7585
|
"",
|
|
7401
7586
|
"Settings are persisted to ~/.wrongstack/config.json."
|
|
@@ -7405,12 +7590,16 @@ function buildSettingsCommand(opts) {
|
|
|
7405
7590
|
const delay = autonomy?.autoProceedDelayMs ?? 45e3;
|
|
7406
7591
|
const mode = autonomy?.defaultMode ?? "off";
|
|
7407
7592
|
const hints = opts.configStore.get().hints !== false;
|
|
7593
|
+
const debugStream = opts.configStore.get().debugStream === true;
|
|
7594
|
+
const configScope = opts.configStore.get().configScope ?? "global";
|
|
7408
7595
|
return [
|
|
7409
7596
|
`${color.bold("WrongStack")} ${color.dim("\u2014 Settings")}`,
|
|
7410
7597
|
"",
|
|
7411
7598
|
` auto-proceed delay: ${color.cyan(formatDelay(delay))} ${color.dim("change: /settings delay <seconds>")}`,
|
|
7412
7599
|
` default autonomy mode: ${color.cyan(mode)} ${color.dim("change: /settings mode off|suggest|auto")}`,
|
|
7413
7600
|
` launch hints: ${hints ? color.cyan("on") : color.dim("off")} ${color.dim("change: /settings hints on|off")}`,
|
|
7601
|
+
` debug stream: ${debugStream ? color.cyan("on") : color.dim("off")} ${color.dim("change: /settings debug-stream on|off")}`,
|
|
7602
|
+
` config scope: ${color.cyan(configScope)} ${color.dim("change: /settings config-scope global|project")}`,
|
|
7414
7603
|
"",
|
|
7415
7604
|
color.dim(" Persisted to ~/.wrongstack/config.json \xB7 /settings help for more")
|
|
7416
7605
|
].join("\n");
|
|
@@ -7449,6 +7638,7 @@ function buildSettingsCommand(opts) {
|
|
|
7449
7638
|
const persistDeps = {
|
|
7450
7639
|
configStore: opts.configStore,
|
|
7451
7640
|
globalConfigPath: opts.paths.globalConfig,
|
|
7641
|
+
inProjectConfigPath: opts.paths.inProjectConfig,
|
|
7452
7642
|
vault: noOpVault4
|
|
7453
7643
|
};
|
|
7454
7644
|
try {
|
|
@@ -7493,8 +7683,32 @@ function buildSettingsCommand(opts) {
|
|
|
7493
7683
|
});
|
|
7494
7684
|
return { message: `${color.green("\u2713")} launch hints \u2192 ${on ? color.cyan("on") : color.dim("off")}` };
|
|
7495
7685
|
}
|
|
7686
|
+
if (sub === "debug-stream") {
|
|
7687
|
+
const raw = (parts[1] ?? "").toLowerCase();
|
|
7688
|
+
if (!["on", "off"].includes(raw)) {
|
|
7689
|
+
return { message: `${color.amber("Usage:")} /settings debug-stream on|off` };
|
|
7690
|
+
}
|
|
7691
|
+
const on = raw === "on";
|
|
7692
|
+
const { setDebugStreamEnabled } = await import('@wrongstack/providers');
|
|
7693
|
+
setDebugStreamEnabled(on);
|
|
7694
|
+
await persistConfigSetting(persistDeps, (cfg) => {
|
|
7695
|
+
cfg.debugStream = on;
|
|
7696
|
+
});
|
|
7697
|
+
return { message: `${color.green("\u2713")} debug stream \u2192 ${on ? color.cyan("on") : color.dim("off")} ${color.dim("raw SSE hex-dump to stderr")}` };
|
|
7698
|
+
}
|
|
7699
|
+
if (sub === "config-scope") {
|
|
7700
|
+
const raw = (parts[1] ?? "").toLowerCase();
|
|
7701
|
+
if (!["global", "project"].includes(raw)) {
|
|
7702
|
+
return { message: `${color.amber("Usage:")} /settings config-scope global|project` };
|
|
7703
|
+
}
|
|
7704
|
+
await persistConfigSetting(persistDeps, (cfg) => {
|
|
7705
|
+
cfg.configScope = raw;
|
|
7706
|
+
});
|
|
7707
|
+
const label = raw === "project" ? `${color.cyan("project")} \u2014 settings saved to <project>/.wrongstack/config.json` : `${color.cyan("global")} \u2014 settings saved to ~/.wrongstack/config.json`;
|
|
7708
|
+
return { message: `${color.green("\u2713")} config scope \u2192 ${label}` };
|
|
7709
|
+
}
|
|
7496
7710
|
return {
|
|
7497
|
-
message: `${color.red("Unknown setting")} "${sub}". Try ${color.dim("/settings")}, ${color.dim("/settings delay <s>")}, ${color.dim("/settings mode <m>")},
|
|
7711
|
+
message: `${color.red("Unknown setting")} "${sub}". Try ${color.dim("/settings")}, ${color.dim("/settings delay <s>")}, ${color.dim("/settings mode <m>")}, ${color.dim("/settings hints on|off")}, ${color.dim("/settings debug-stream on|off")}, or ${color.dim("/settings config-scope global|project")}.`
|
|
7498
7712
|
};
|
|
7499
7713
|
} catch (err) {
|
|
7500
7714
|
return {
|
|
@@ -7745,7 +7959,7 @@ var DEFAULTS = {
|
|
|
7745
7959
|
cost: true
|
|
7746
7960
|
};
|
|
7747
7961
|
function resolveConfigPath() {
|
|
7748
|
-
return process.env[CONFIG_ENV] ??
|
|
7962
|
+
return process.env[CONFIG_ENV] ?? path9.join(process.env.HOME ?? "", ".wrongstack", "statusline.json");
|
|
7749
7963
|
}
|
|
7750
7964
|
async function loadStatuslineConfig() {
|
|
7751
7965
|
const p = resolveConfigPath();
|
|
@@ -7759,7 +7973,7 @@ async function loadStatuslineConfig() {
|
|
|
7759
7973
|
async function saveStatuslineConfig(cfg) {
|
|
7760
7974
|
const p = resolveConfigPath();
|
|
7761
7975
|
try {
|
|
7762
|
-
await fsp4.mkdir(
|
|
7976
|
+
await fsp4.mkdir(path9.dirname(p), { recursive: true });
|
|
7763
7977
|
await atomicWrite(p, JSON.stringify(cfg, null, 2));
|
|
7764
7978
|
} catch (err) {
|
|
7765
7979
|
throw new FsError({
|
|
@@ -8276,6 +8490,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
8276
8490
|
buildSettingsCommand(opts),
|
|
8277
8491
|
buildTelegramSetupCommand(opts),
|
|
8278
8492
|
buildSetModelCommand(opts),
|
|
8493
|
+
buildModelCapsCommand(opts),
|
|
8279
8494
|
buildModelsCommand(opts),
|
|
8280
8495
|
buildCollabCommand(opts),
|
|
8281
8496
|
buildStatuslineCommand({
|
|
@@ -8305,13 +8520,13 @@ var MANIFESTS = [
|
|
|
8305
8520
|
];
|
|
8306
8521
|
async function detectProjectKind(projectRoot) {
|
|
8307
8522
|
try {
|
|
8308
|
-
await fsp4.access(
|
|
8523
|
+
await fsp4.access(path9.join(projectRoot, ".wrongstack", "AGENTS.md"));
|
|
8309
8524
|
return "initialized";
|
|
8310
8525
|
} catch {
|
|
8311
8526
|
}
|
|
8312
8527
|
for (const m of MANIFESTS) {
|
|
8313
8528
|
try {
|
|
8314
|
-
await fsp4.access(
|
|
8529
|
+
await fsp4.access(path9.join(projectRoot, m));
|
|
8315
8530
|
return "project";
|
|
8316
8531
|
} catch {
|
|
8317
8532
|
}
|
|
@@ -8319,8 +8534,8 @@ async function detectProjectKind(projectRoot) {
|
|
|
8319
8534
|
return "empty";
|
|
8320
8535
|
}
|
|
8321
8536
|
async function scaffoldAgentsMd(projectRoot) {
|
|
8322
|
-
const dir =
|
|
8323
|
-
const file =
|
|
8537
|
+
const dir = path9.join(projectRoot, ".wrongstack");
|
|
8538
|
+
const file = path9.join(dir, "AGENTS.md");
|
|
8324
8539
|
const facts = await detectProjectFacts(projectRoot);
|
|
8325
8540
|
const body = renderAgentsTemplate(facts);
|
|
8326
8541
|
await fsp4.mkdir(dir, { recursive: true });
|
|
@@ -8333,7 +8548,7 @@ async function runProjectCheck(opts) {
|
|
|
8333
8548
|
if (kind === "initialized") {
|
|
8334
8549
|
renderer.write(
|
|
8335
8550
|
`
|
|
8336
|
-
${color.green("\u2713")} Project initialized ${color.dim(`(${
|
|
8551
|
+
${color.green("\u2713")} Project initialized ${color.dim(`(${path9.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
|
|
8337
8552
|
`
|
|
8338
8553
|
);
|
|
8339
8554
|
return true;
|
|
@@ -8364,7 +8579,7 @@ async function runProjectCheck(opts) {
|
|
|
8364
8579
|
}
|
|
8365
8580
|
return true;
|
|
8366
8581
|
}
|
|
8367
|
-
const gitDir =
|
|
8582
|
+
const gitDir = path9.join(projectRoot, ".git");
|
|
8368
8583
|
let hasGit = false;
|
|
8369
8584
|
try {
|
|
8370
8585
|
await fsp4.access(gitDir);
|
|
@@ -8558,7 +8773,7 @@ var ReadlineInputReader = class {
|
|
|
8558
8773
|
history = [];
|
|
8559
8774
|
pending = false;
|
|
8560
8775
|
constructor(opts = {}) {
|
|
8561
|
-
this.historyFile = opts.historyFile ??
|
|
8776
|
+
this.historyFile = opts.historyFile ?? path9.join(os2.homedir(), ".wrongstack", "history");
|
|
8562
8777
|
}
|
|
8563
8778
|
async loadHistory() {
|
|
8564
8779
|
try {
|
|
@@ -8570,7 +8785,7 @@ var ReadlineInputReader = class {
|
|
|
8570
8785
|
}
|
|
8571
8786
|
async saveHistory() {
|
|
8572
8787
|
try {
|
|
8573
|
-
await fsp4.mkdir(
|
|
8788
|
+
await fsp4.mkdir(path9.dirname(this.historyFile), { recursive: true });
|
|
8574
8789
|
await fsp4.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
|
|
8575
8790
|
} catch {
|
|
8576
8791
|
}
|
|
@@ -8859,20 +9074,20 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
8859
9074
|
if (PROTECTED_BASENAMES.has(filename)) {
|
|
8860
9075
|
throw new Error(`Refusing to delete protected file: ${filename}`);
|
|
8861
9076
|
}
|
|
8862
|
-
if (filename !==
|
|
9077
|
+
if (filename !== path9.basename(filename)) {
|
|
8863
9078
|
throw new Error(`Refusing to delete path with traversal: ${filename}`);
|
|
8864
9079
|
}
|
|
8865
9080
|
if (!filename.startsWith("config.json.") || !filename.endsWith(".bak")) {
|
|
8866
9081
|
throw new Error(`Refusing to delete unknown file: ${filename}`);
|
|
8867
9082
|
}
|
|
8868
|
-
const resolvedParent =
|
|
9083
|
+
const resolvedParent = path9.resolve(parentDir);
|
|
8869
9084
|
if (!resolvedParent.endsWith(".wrongstack")) {
|
|
8870
9085
|
throw new Error(`Unexpected parent directory for bak prune: ${resolvedParent}`);
|
|
8871
9086
|
}
|
|
8872
9087
|
}
|
|
8873
9088
|
async function safeDelete(filePath) {
|
|
8874
|
-
const dir =
|
|
8875
|
-
const filename =
|
|
9089
|
+
const dir = path9.dirname(filePath);
|
|
9090
|
+
const filename = path9.basename(filePath);
|
|
8876
9091
|
try {
|
|
8877
9092
|
assertSafeToDelete(filename, dir);
|
|
8878
9093
|
await fsp4.unlink(filePath);
|
|
@@ -8917,16 +9132,16 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
8917
9132
|
}
|
|
8918
9133
|
var defaultHomeDir = () => os2__default.homedir();
|
|
8919
9134
|
function historyDir(homeFn = defaultHomeDir) {
|
|
8920
|
-
return
|
|
9135
|
+
return path9.join(homeFn(), ".wrongstack", "config.history", "entries");
|
|
8921
9136
|
}
|
|
8922
9137
|
function historyIndexPath(homeFn = defaultHomeDir) {
|
|
8923
|
-
return
|
|
9138
|
+
return path9.join(homeFn(), ".wrongstack", "config.history", "index.json");
|
|
8924
9139
|
}
|
|
8925
9140
|
function configPath(homeFn = defaultHomeDir) {
|
|
8926
|
-
return
|
|
9141
|
+
return path9.join(homeFn(), ".wrongstack", "config.json");
|
|
8927
9142
|
}
|
|
8928
9143
|
function backupLastPath(homeFn = defaultHomeDir) {
|
|
8929
|
-
return
|
|
9144
|
+
return path9.join(homeFn(), ".wrongstack", "config.json.last");
|
|
8930
9145
|
}
|
|
8931
9146
|
function entryId(ts) {
|
|
8932
9147
|
return ts.replace(/[:.]/g, "-").slice(0, 19);
|
|
@@ -8981,17 +9196,17 @@ async function backupCurrent(homeFn = defaultHomeDir) {
|
|
|
8981
9196
|
}
|
|
8982
9197
|
if (content !== void 0) {
|
|
8983
9198
|
try {
|
|
8984
|
-
const bakPath =
|
|
9199
|
+
const bakPath = path9.join(homeFn(), ".wrongstack", `config.json.${ts}.bak`);
|
|
8985
9200
|
await atomicWrite(bakPath, content);
|
|
8986
9201
|
} catch {
|
|
8987
9202
|
}
|
|
8988
9203
|
}
|
|
8989
9204
|
try {
|
|
8990
|
-
const dir =
|
|
9205
|
+
const dir = path9.join(homeFn(), ".wrongstack");
|
|
8991
9206
|
const files = await fsp4.readdir(dir);
|
|
8992
9207
|
const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
|
|
8993
9208
|
for (const f of baks.slice(10)) {
|
|
8994
|
-
await safeDelete(
|
|
9209
|
+
await safeDelete(path9.join(dir, f));
|
|
8995
9210
|
}
|
|
8996
9211
|
} catch {
|
|
8997
9212
|
}
|
|
@@ -9009,7 +9224,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
9009
9224
|
};
|
|
9010
9225
|
try {
|
|
9011
9226
|
await fsp4.writeFile(
|
|
9012
|
-
|
|
9227
|
+
path9.join(historyDir(homeFn), `${id}.json`),
|
|
9013
9228
|
JSON.stringify(entry, null, 2),
|
|
9014
9229
|
"utf8"
|
|
9015
9230
|
);
|
|
@@ -9017,7 +9232,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
9017
9232
|
throw new FsError({
|
|
9018
9233
|
message: err instanceof Error ? err.message : String(err),
|
|
9019
9234
|
code: ERROR_CODES.FS_WRITE_FAILED,
|
|
9020
|
-
path:
|
|
9235
|
+
path: path9.join(historyDir(homeFn), `${id}.json`),
|
|
9021
9236
|
cause: err
|
|
9022
9237
|
});
|
|
9023
9238
|
}
|
|
@@ -9032,7 +9247,7 @@ async function listHistory(homeFn = defaultHomeDir) {
|
|
|
9032
9247
|
}
|
|
9033
9248
|
async function getHistoryEntry(id, homeFn = defaultHomeDir) {
|
|
9034
9249
|
try {
|
|
9035
|
-
const raw = await fsp4.readFile(
|
|
9250
|
+
const raw = await fsp4.readFile(path9.join(historyDir(homeFn), `${id}.json`), "utf8");
|
|
9036
9251
|
return JSON.parse(raw);
|
|
9037
9252
|
} catch {
|
|
9038
9253
|
return null;
|
|
@@ -9098,10 +9313,10 @@ var theme = { primary: color.amber };
|
|
|
9098
9313
|
async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os2__default.homedir()) {
|
|
9099
9314
|
try {
|
|
9100
9315
|
const { atomicWrite: atomicWrite14 } = await import('@wrongstack/core');
|
|
9101
|
-
const
|
|
9316
|
+
const fs29 = await import('fs/promises');
|
|
9102
9317
|
let existing = {};
|
|
9103
9318
|
try {
|
|
9104
|
-
const raw = await
|
|
9319
|
+
const raw = await fs29.readFile(configPath2, "utf8");
|
|
9105
9320
|
existing = JSON.parse(raw);
|
|
9106
9321
|
} catch {
|
|
9107
9322
|
}
|
|
@@ -9437,12 +9652,12 @@ function pickGroupIndex(opts) {
|
|
|
9437
9652
|
try {
|
|
9438
9653
|
let current = 0;
|
|
9439
9654
|
try {
|
|
9440
|
-
const parsed = Number.parseInt(
|
|
9655
|
+
const parsed = Number.parseInt(fs14.readFileSync(opts.cursorFile, "utf8").trim(), 10);
|
|
9441
9656
|
if (Number.isFinite(parsed)) current = wrap(parsed);
|
|
9442
9657
|
} catch {
|
|
9443
9658
|
}
|
|
9444
|
-
|
|
9445
|
-
|
|
9659
|
+
fs14.mkdirSync(path9.dirname(opts.cursorFile), { recursive: true });
|
|
9660
|
+
fs14.writeFileSync(opts.cursorFile, String(wrap(current + 1)));
|
|
9446
9661
|
return current;
|
|
9447
9662
|
} catch {
|
|
9448
9663
|
}
|
|
@@ -9778,14 +9993,14 @@ function summarize(value, name) {
|
|
|
9778
9993
|
if (typeof v === "object" && v !== null) {
|
|
9779
9994
|
const o = v;
|
|
9780
9995
|
if (name === "edit") {
|
|
9781
|
-
const
|
|
9996
|
+
const path27 = typeof o["path"] === "string" ? o["path"] : "";
|
|
9782
9997
|
const reps = typeof o["replacements"] === "number" ? o["replacements"] : 0;
|
|
9783
|
-
return `${
|
|
9998
|
+
return `${path27} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
|
|
9784
9999
|
}
|
|
9785
10000
|
if (name === "write") {
|
|
9786
|
-
const
|
|
10001
|
+
const path27 = typeof o["path"] === "string" ? o["path"] : "";
|
|
9787
10002
|
const bytes = typeof o["bytes"] === "number" ? o["bytes"] : void 0;
|
|
9788
|
-
return bytes !== void 0 ? `${
|
|
10003
|
+
return bytes !== void 0 ? `${path27} ${bytes}B` : path27;
|
|
9789
10004
|
}
|
|
9790
10005
|
if (typeof o["count"] === "number") {
|
|
9791
10006
|
return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
|
|
@@ -10009,7 +10224,8 @@ ${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.am
|
|
|
10009
10224
|
renderer.write(color.dim(" (no keys saved)\n"));
|
|
10010
10225
|
} else {
|
|
10011
10226
|
for (let i = 0; i < keys.length; i++) {
|
|
10012
|
-
|
|
10227
|
+
const key = keys[i];
|
|
10228
|
+
if (key) renderKeyLine(renderer, key, i + 1, active);
|
|
10013
10229
|
}
|
|
10014
10230
|
}
|
|
10015
10231
|
}
|
|
@@ -10101,7 +10317,7 @@ async function confirm(deps, question) {
|
|
|
10101
10317
|
return answer === "y" || answer === "yes";
|
|
10102
10318
|
}
|
|
10103
10319
|
function suggestLabel(usedLabels) {
|
|
10104
|
-
|
|
10320
|
+
const candidate = "default";
|
|
10105
10321
|
if (!usedLabels.has(candidate)) return candidate;
|
|
10106
10322
|
let n = 2;
|
|
10107
10323
|
while (usedLabels.has(`key${n}`)) n++;
|
|
@@ -10420,7 +10636,7 @@ ${color.amber("?")} ${providerId} > `
|
|
|
10420
10636
|
if (!raw || raw === "b" || raw === "back" || raw === "q" || raw === "quit") {
|
|
10421
10637
|
return;
|
|
10422
10638
|
}
|
|
10423
|
-
const [verb, argRaw] = raw.split(/\s+/, 2);
|
|
10639
|
+
const [verb = "", argRaw = ""] = raw.split(/\s+/, 2);
|
|
10424
10640
|
const arg = argRaw ? Number.parseInt(argRaw, 10) : Number.NaN;
|
|
10425
10641
|
const handled = await dispatchAction(verb, arg, providerId, keys, cfg, deps);
|
|
10426
10642
|
if (handled === "exit") return;
|
|
@@ -11002,7 +11218,7 @@ var doctorCmd = async (_args, deps) => {
|
|
|
11002
11218
|
}
|
|
11003
11219
|
try {
|
|
11004
11220
|
await fsp4.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
11005
|
-
const probe =
|
|
11221
|
+
const probe = path9.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
11006
11222
|
await fsp4.writeFile(probe, "");
|
|
11007
11223
|
await fsp4.unlink(probe);
|
|
11008
11224
|
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
@@ -11105,8 +11321,8 @@ var exportCmd = async (args, deps) => {
|
|
|
11105
11321
|
return 1;
|
|
11106
11322
|
}
|
|
11107
11323
|
if (output) {
|
|
11108
|
-
await fsp4.mkdir(
|
|
11109
|
-
await fsp4.writeFile(
|
|
11324
|
+
await fsp4.mkdir(path9.dirname(path9.resolve(deps.cwd, output)), { recursive: true });
|
|
11325
|
+
await fsp4.writeFile(path9.resolve(deps.cwd, output), rendered, "utf8");
|
|
11110
11326
|
deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
|
|
11111
11327
|
`);
|
|
11112
11328
|
} else {
|
|
@@ -11179,8 +11395,8 @@ var initCmd = async (_args, deps) => {
|
|
|
11179
11395
|
const vault = new DefaultSecretVault({ keyFile: deps.paths.secretsKey });
|
|
11180
11396
|
const encrypted = encryptConfigSecrets(config, vault);
|
|
11181
11397
|
await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
11182
|
-
await fsp4.mkdir(
|
|
11183
|
-
const agentsFile =
|
|
11398
|
+
await fsp4.mkdir(path9.join(deps.projectRoot, ".wrongstack"), { recursive: true });
|
|
11399
|
+
const agentsFile = path9.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
|
|
11184
11400
|
const projectFacts = await detectProjectFacts(deps.projectRoot);
|
|
11185
11401
|
await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
|
|
11186
11402
|
deps.renderer.writeInfo(`Wrote ${deps.paths.globalConfig}`);
|
|
@@ -11637,7 +11853,7 @@ var usageCmd = async (_args, deps) => {
|
|
|
11637
11853
|
return 0;
|
|
11638
11854
|
};
|
|
11639
11855
|
var projectsCmd = async (_args, deps) => {
|
|
11640
|
-
const projectsRoot =
|
|
11856
|
+
const projectsRoot = path9.join(deps.paths.globalRoot, "projects");
|
|
11641
11857
|
try {
|
|
11642
11858
|
const entries = await fsp4.readdir(projectsRoot);
|
|
11643
11859
|
if (entries.length === 0) {
|
|
@@ -11647,7 +11863,7 @@ var projectsCmd = async (_args, deps) => {
|
|
|
11647
11863
|
for (const hash of entries) {
|
|
11648
11864
|
try {
|
|
11649
11865
|
const meta = JSON.parse(
|
|
11650
|
-
await fsp4.readFile(
|
|
11866
|
+
await fsp4.readFile(path9.join(projectsRoot, hash, "meta.json"), "utf8")
|
|
11651
11867
|
);
|
|
11652
11868
|
deps.renderer.write(
|
|
11653
11869
|
` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
|
|
@@ -12026,7 +12242,7 @@ async function listFleetRuns(deps) {
|
|
|
12026
12242
|
}
|
|
12027
12243
|
const runs = [];
|
|
12028
12244
|
for (const id of entries) {
|
|
12029
|
-
const runDir =
|
|
12245
|
+
const runDir = path9.join(deps.paths.projectSessions, id);
|
|
12030
12246
|
let stat4;
|
|
12031
12247
|
try {
|
|
12032
12248
|
stat4 = await fsp4.stat(runDir);
|
|
@@ -12039,17 +12255,17 @@ async function listFleetRuns(deps) {
|
|
|
12039
12255
|
let subagentCount = 0;
|
|
12040
12256
|
let subagentsDir;
|
|
12041
12257
|
try {
|
|
12042
|
-
await fsp4.access(
|
|
12258
|
+
await fsp4.access(path9.join(runDir, "fleet.json"));
|
|
12043
12259
|
manifest = true;
|
|
12044
12260
|
} catch {
|
|
12045
12261
|
}
|
|
12046
12262
|
try {
|
|
12047
|
-
await fsp4.access(
|
|
12263
|
+
await fsp4.access(path9.join(runDir, "checkpoint.json"));
|
|
12048
12264
|
checkpoint = true;
|
|
12049
12265
|
} catch {
|
|
12050
12266
|
}
|
|
12051
12267
|
try {
|
|
12052
|
-
subagentsDir =
|
|
12268
|
+
subagentsDir = path9.join(runDir, "subagents");
|
|
12053
12269
|
const files = await fsp4.readdir(subagentsDir);
|
|
12054
12270
|
subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
|
|
12055
12271
|
} catch {
|
|
@@ -12078,7 +12294,7 @@ async function listFleetRuns(deps) {
|
|
|
12078
12294
|
return 0;
|
|
12079
12295
|
}
|
|
12080
12296
|
async function showFleetRun(runId, deps) {
|
|
12081
|
-
const runDir =
|
|
12297
|
+
const runDir = path9.join(deps.paths.projectSessions, runId);
|
|
12082
12298
|
let stat4;
|
|
12083
12299
|
try {
|
|
12084
12300
|
stat4 = await fsp4.stat(runDir);
|
|
@@ -12095,7 +12311,7 @@ async function showFleetRun(runId, deps) {
|
|
|
12095
12311
|
deps.renderer.write(color.bold(`
|
|
12096
12312
|
Fleet Run: ${runId}
|
|
12097
12313
|
`) + "\n");
|
|
12098
|
-
const manifestPath =
|
|
12314
|
+
const manifestPath = path9.join(runDir, "fleet.json");
|
|
12099
12315
|
let manifestData = null;
|
|
12100
12316
|
try {
|
|
12101
12317
|
manifestData = await fsp4.readFile(manifestPath, "utf8");
|
|
@@ -12111,7 +12327,7 @@ Fleet Run: ${runId}
|
|
|
12111
12327
|
deps.renderer.write(` ${color.dim("\u25CB")} fleet.json \u2014 not found
|
|
12112
12328
|
`);
|
|
12113
12329
|
}
|
|
12114
|
-
const checkpointPath =
|
|
12330
|
+
const checkpointPath = path9.join(runDir, "checkpoint.json");
|
|
12115
12331
|
let checkpointData = null;
|
|
12116
12332
|
try {
|
|
12117
12333
|
checkpointData = await fsp4.readFile(checkpointPath, "utf8");
|
|
@@ -12158,7 +12374,7 @@ Fleet Run: ${runId}
|
|
|
12158
12374
|
} catch {
|
|
12159
12375
|
}
|
|
12160
12376
|
}
|
|
12161
|
-
const subagentsDir =
|
|
12377
|
+
const subagentsDir = path9.join(runDir, "subagents");
|
|
12162
12378
|
let subagentFiles = [];
|
|
12163
12379
|
try {
|
|
12164
12380
|
subagentFiles = await fsp4.readdir(subagentsDir);
|
|
@@ -12170,7 +12386,7 @@ Fleet Run: ${runId}
|
|
|
12170
12386
|
Subagent transcripts (${subagentFiles.length}):
|
|
12171
12387
|
`);
|
|
12172
12388
|
for (const f of subagentFiles.sort()) {
|
|
12173
|
-
const filePath =
|
|
12389
|
+
const filePath = path9.join(subagentsDir, f);
|
|
12174
12390
|
let size;
|
|
12175
12391
|
try {
|
|
12176
12392
|
const s = await fsp4.stat(filePath);
|
|
@@ -12187,7 +12403,7 @@ Fleet Run: ${runId}
|
|
|
12187
12403
|
${color.dim("\u25CB")} No subagent transcripts
|
|
12188
12404
|
`);
|
|
12189
12405
|
}
|
|
12190
|
-
const sharedDir =
|
|
12406
|
+
const sharedDir = path9.join(runDir, "shared");
|
|
12191
12407
|
try {
|
|
12192
12408
|
const files = await fsp4.readdir(sharedDir);
|
|
12193
12409
|
deps.renderer.write(`
|
|
@@ -12354,7 +12570,7 @@ function findSessionId(args) {
|
|
|
12354
12570
|
var rewindCmd = async (args, deps) => {
|
|
12355
12571
|
const flags = parseRewindFlags(args);
|
|
12356
12572
|
const wpaths = resolveWstackPaths({ projectRoot: deps.projectRoot });
|
|
12357
|
-
const sessionsDir =
|
|
12573
|
+
const sessionsDir = path9.join(wpaths.globalRoot, "sessions");
|
|
12358
12574
|
const rewind = new DefaultSessionRewinder(sessionsDir, deps.projectRoot);
|
|
12359
12575
|
let sessionId = findSessionId(args);
|
|
12360
12576
|
if (!sessionId) {
|
|
@@ -12594,10 +12810,10 @@ var auditCmd = async (args, deps) => {
|
|
|
12594
12810
|
return verify.ok ? 0 : 1;
|
|
12595
12811
|
};
|
|
12596
12812
|
async function listAudits(log, dir, deps) {
|
|
12597
|
-
const
|
|
12813
|
+
const fs29 = await import('fs/promises');
|
|
12598
12814
|
let entries;
|
|
12599
12815
|
try {
|
|
12600
|
-
entries = await
|
|
12816
|
+
entries = await fs29.readdir(dir);
|
|
12601
12817
|
} catch {
|
|
12602
12818
|
deps.renderer.write(
|
|
12603
12819
|
color.dim(`No sessions dir found at ${dir}. Run a session first.`) + "\n"
|
|
@@ -12650,6 +12866,743 @@ var skillsCmd = async (_args, deps) => {
|
|
|
12650
12866
|
);
|
|
12651
12867
|
return 0;
|
|
12652
12868
|
};
|
|
12869
|
+
var MODEL_PROFILES = [
|
|
12870
|
+
{ provider: "anthropic", pattern: /claude-opus/i, family: "Claude Opus", strengths: ["reasoning", "planning"], bestFor: ["planning", "security", "debugging"], costTier: "premium", speedTier: "slow" },
|
|
12871
|
+
{ provider: "anthropic", pattern: /claude-sonnet/i, family: "Claude Sonnet", strengths: ["coding", "balanced"], bestFor: ["coding", "general"], costTier: "standard", speedTier: "fast" },
|
|
12872
|
+
{ provider: "anthropic", pattern: /claude-haiku/i, family: "Claude Haiku", strengths: ["speed"], bestFor: ["lightweight", "docs"], avoidFor: ["planning"], costTier: "budget", speedTier: "fast" },
|
|
12873
|
+
{ provider: "openai", pattern: /gpt-5|o3|o4/i, family: "GPT-5/o3/o4", strengths: ["reasoning", "coding"], bestFor: ["planning", "coding", "debugging"], costTier: "premium", speedTier: "normal" },
|
|
12874
|
+
{ provider: "openai", pattern: /gpt-4/i, family: "GPT-4", strengths: ["coding"], bestFor: ["coding", "docs"], costTier: "standard", speedTier: "fast" },
|
|
12875
|
+
{ provider: "openai", pattern: /gpt-4o-mini/i, family: "GPT-4o Mini", strengths: ["speed"], bestFor: ["lightweight", "docs"], avoidFor: ["planning"], costTier: "budget", speedTier: "fast" },
|
|
12876
|
+
{ provider: "google", pattern: /gemini-(?:2\.5|3)/i, family: "Gemini 2.5/3", strengths: ["context", "coding"], bestFor: ["coding", "data"], costTier: "standard", speedTier: "normal" },
|
|
12877
|
+
{ provider: "google", pattern: /gemini.*flash/i, family: "Gemini Flash", strengths: ["speed"], bestFor: ["lightweight", "docs"], avoidFor: ["planning"], costTier: "budget", speedTier: "fast" },
|
|
12878
|
+
{ provider: "deepseek", pattern: /deepseek/i, family: "DeepSeek", strengths: ["coding", "cost-effective"], bestFor: ["coding", "general"], costTier: "standard", speedTier: "normal" }
|
|
12879
|
+
];
|
|
12880
|
+
function fmtTokens2(n) {
|
|
12881
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
12882
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
12883
|
+
return String(n);
|
|
12884
|
+
}
|
|
12885
|
+
function fmtMs(ms) {
|
|
12886
|
+
if (ms >= 1e3) return `${(ms / 1e3).toFixed(1)}s`;
|
|
12887
|
+
return `${ms}ms`;
|
|
12888
|
+
}
|
|
12889
|
+
function fmtPrice2(usdPer1M) {
|
|
12890
|
+
if (usdPer1M === void 0) return color.dim("?");
|
|
12891
|
+
if (usdPer1M >= 10) return `$${usdPer1M.toFixed(1)}`;
|
|
12892
|
+
return `$${usdPer1M.toFixed(2)}`;
|
|
12893
|
+
}
|
|
12894
|
+
function checkMark(ok) {
|
|
12895
|
+
return ok ? color.green("\u2713") : color.red("\u2717");
|
|
12896
|
+
}
|
|
12897
|
+
function costLabel(tier) {
|
|
12898
|
+
switch (tier) {
|
|
12899
|
+
case "premium":
|
|
12900
|
+
return color.red("$$$");
|
|
12901
|
+
case "standard":
|
|
12902
|
+
return color.amber("$$");
|
|
12903
|
+
case "budget":
|
|
12904
|
+
return color.green("$");
|
|
12905
|
+
default:
|
|
12906
|
+
return color.dim("?");
|
|
12907
|
+
}
|
|
12908
|
+
}
|
|
12909
|
+
function speedLabel(tier) {
|
|
12910
|
+
switch (tier) {
|
|
12911
|
+
case "fast":
|
|
12912
|
+
return color.green("\u26A1");
|
|
12913
|
+
case "normal":
|
|
12914
|
+
return color.amber("\u2192");
|
|
12915
|
+
case "slow":
|
|
12916
|
+
return color.red("\u{1F422}");
|
|
12917
|
+
default:
|
|
12918
|
+
return color.dim("?");
|
|
12919
|
+
}
|
|
12920
|
+
}
|
|
12921
|
+
function scoreBar(score, max) {
|
|
12922
|
+
const pct2 = Math.min(1, Math.max(0, score / max));
|
|
12923
|
+
const filled = Math.round(pct2 * 10);
|
|
12924
|
+
const bar = color.green("\u2588".repeat(filled)) + color.dim("\u2591".repeat(10 - filled));
|
|
12925
|
+
return `${bar} ${score}/${max}`;
|
|
12926
|
+
}
|
|
12927
|
+
var ROLE_CATEGORY = {
|
|
12928
|
+
"security-scanner": "security",
|
|
12929
|
+
"security-reviewer": "security",
|
|
12930
|
+
"bug-hunter": "debugging",
|
|
12931
|
+
debugger: "debugging",
|
|
12932
|
+
tracer: "debugging",
|
|
12933
|
+
planner: "planning",
|
|
12934
|
+
architect: "planning",
|
|
12935
|
+
"refactor-planner": "planning",
|
|
12936
|
+
test: "testing",
|
|
12937
|
+
e2e: "testing",
|
|
12938
|
+
document: "docs",
|
|
12939
|
+
simplifier: "docs",
|
|
12940
|
+
"code-reviewer": "review",
|
|
12941
|
+
critic: "review",
|
|
12942
|
+
executor: "coding",
|
|
12943
|
+
refactor: "refactoring",
|
|
12944
|
+
migration: "coding",
|
|
12945
|
+
frontend: "frontend",
|
|
12946
|
+
backend: "backend",
|
|
12947
|
+
api: "backend",
|
|
12948
|
+
auth: "backend",
|
|
12949
|
+
designer: "frontend",
|
|
12950
|
+
analyst: "data",
|
|
12951
|
+
data: "data",
|
|
12952
|
+
database: "data",
|
|
12953
|
+
explore: "planning",
|
|
12954
|
+
search: "planning",
|
|
12955
|
+
researcher: "planning"
|
|
12956
|
+
};
|
|
12957
|
+
function findProfile(pid, mid) {
|
|
12958
|
+
for (const p of MODEL_PROFILES) {
|
|
12959
|
+
if (p.provider === pid && p.pattern.test(mid)) return p;
|
|
12960
|
+
}
|
|
12961
|
+
return void 0;
|
|
12962
|
+
}
|
|
12963
|
+
function scoreModel(pid, mid, category, ctxWindow) {
|
|
12964
|
+
const profile = findProfile(pid, mid);
|
|
12965
|
+
let score = 50;
|
|
12966
|
+
if (profile) {
|
|
12967
|
+
if (profile.bestFor.includes(category)) score += 35;
|
|
12968
|
+
if (profile.avoidFor?.includes(category)) score -= 50;
|
|
12969
|
+
if (category === "planning" && profile.costTier === "premium") score += 15;
|
|
12970
|
+
if (profile.speedTier === "slow" && category === "planning") score += 10;
|
|
12971
|
+
if (profile.costTier === "budget" && category !== "planning" && category !== "security") score += 10;
|
|
12972
|
+
}
|
|
12973
|
+
if (ctxWindow > 2e5) score += 10;
|
|
12974
|
+
else if (ctxWindow > 1e5) score += 5;
|
|
12975
|
+
else if (ctxWindow > 32e3) score += 2;
|
|
12976
|
+
return { score, profile };
|
|
12977
|
+
}
|
|
12978
|
+
function rankModels(providers, hasKey, category, limit) {
|
|
12979
|
+
const candidates = [];
|
|
12980
|
+
for (const prov of providers) {
|
|
12981
|
+
if (!hasKey(prov.id)) continue;
|
|
12982
|
+
for (const m of prov.models ?? []) {
|
|
12983
|
+
const ctxWindow = m.capabilities?.contextWindow ?? 0;
|
|
12984
|
+
const { score, profile } = scoreModel(prov.id, m.id, category, ctxWindow);
|
|
12985
|
+
if (score > 0) {
|
|
12986
|
+
candidates.push({
|
|
12987
|
+
provider: prov.id,
|
|
12988
|
+
model: m.id,
|
|
12989
|
+
profile,
|
|
12990
|
+
score,
|
|
12991
|
+
ctxWindow,
|
|
12992
|
+
maxOutput: m.capabilities?.maxOutputTokens ?? 0,
|
|
12993
|
+
inputPrice: m.pricing?.input,
|
|
12994
|
+
outputPrice: m.pricing?.output
|
|
12995
|
+
});
|
|
12996
|
+
}
|
|
12997
|
+
}
|
|
12998
|
+
}
|
|
12999
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
13000
|
+
return candidates.slice(0, limit);
|
|
13001
|
+
}
|
|
13002
|
+
var EVAL_TASKS = {
|
|
13003
|
+
coding: {
|
|
13004
|
+
label: "Code Generation",
|
|
13005
|
+
prompt: "Write a TypeScript function parseCSV(input: string): { headers: string[]; rows: string[][] } that handles quoted fields, escaped quotes, and empty lines. Return an error string on malformed input. Keep under 40 lines."
|
|
13006
|
+
},
|
|
13007
|
+
planning: {
|
|
13008
|
+
label: "Architecture Planning",
|
|
13009
|
+
prompt: "Design the folder structure and key interfaces for a monorepo CLI tool with slash commands, model routing, subagent spawning, and config persistence. List packages, their responsibilities, and the 5 most important TypeScript interfaces."
|
|
13010
|
+
},
|
|
13011
|
+
security: {
|
|
13012
|
+
label: "Vulnerability Detection",
|
|
13013
|
+
prompt: 'Review this code for security issues:\n```ts\napp.get("/api/user", (req, res) => {\n const id = req.query.id;\n const user = db.query("SELECT * FROM users WHERE id = " + id);\n res.json(user);\n});\n\napp.post("/api/run", (req, res) => {\n const { cmd } = req.body;\n exec("echo " + cmd, (err, stdout) => res.send(stdout));\n});\n```\nList every vulnerability, its severity (critical/high/medium), and the exact fix.'
|
|
13014
|
+
},
|
|
13015
|
+
debugging: {
|
|
13016
|
+
label: "Bug Diagnosis",
|
|
13017
|
+
prompt: 'This async function has 2 bugs. Find and fix both:\n```ts\nasync function processBatch(items: string[]) {\n const results = [];\n for (const item of items) {\n const result = await fetch("https://api.example.com/" + item);\n results.push(result);\n }\n return results.map(r => r.json());\n}\n```\nExplain what each bug is, why it fails, and write the corrected version.'
|
|
13018
|
+
},
|
|
13019
|
+
testing: {
|
|
13020
|
+
label: "Test Authoring",
|
|
13021
|
+
prompt: 'Write vitest test cases for this deepMerge function:\n```ts\nfunction deepMerge(base: Record<string, unknown>, overrides: Record<string, unknown>): Record<string, unknown> {\n const merged = { ...base };\n for (const [key, val] of Object.entries(overrides)) {\n if (val === null) { delete merged[key]; continue; }\n if (typeof val === "object" && !Array.isArray(val) && typeof merged[key] === "object" && !Array.isArray(merged[key])) {\n merged[key] = deepMerge(merged[key] as Record<string, unknown>, val as Record<string, unknown>);\n } else { merged[key] = val; }\n }\n return merged;\n}\n```\nCover: happy path, edge cases, and error conditions.'
|
|
13022
|
+
},
|
|
13023
|
+
docs: {
|
|
13024
|
+
label: "Documentation",
|
|
13025
|
+
prompt: "Write TSDoc comments for this RateLimiter interface. Include @param, @returns, @throws, and @example for each method:\n```ts\ninterface RateLimiter {\n tryAcquire(key: string, maxPerWindow: number, windowMs: number): Promise<boolean>;\n getRemaining(key: string): Promise<number>;\n reset(key: string): Promise<void>;\n}\n```"
|
|
13026
|
+
},
|
|
13027
|
+
review: {
|
|
13028
|
+
label: "Code Review",
|
|
13029
|
+
prompt: 'Review this PR change:\n```diff\n async function loadConfig(path: string) {\n- const raw = await fs.readFile(path, "utf8");\n- return JSON.parse(raw);\n+ const raw = await fs.readFile(path);\n+ const config = JSON.parse(raw);\n+ process.env.API_KEY = config.apiKey;\n+ return config;\n }\n```\nList issues by severity (blocking / should-fix / nit) and explain your reasoning.'
|
|
13030
|
+
},
|
|
13031
|
+
refactoring: {
|
|
13032
|
+
label: "Refactoring",
|
|
13033
|
+
prompt: 'Refactor this nested condition into a cleaner pattern:\n```ts\nfunction getDiscount(user: { type: string; years: number; coupon?: string }): number {\n if (user.type === "premium") {\n if (user.years > 5) {\n if (user.coupon === "BLACKFRIDAY") return 0.5;\n return 0.3;\n }\n return 0.2;\n }\n if (user.type === "standard") {\n if (user.years > 3) return 0.15;\n return 0.1;\n }\n return 0;\n}\n```\nShow your refactored code and explain why your approach is cleaner.'
|
|
13034
|
+
}
|
|
13035
|
+
};
|
|
13036
|
+
var EVAL_CATEGORIES = Object.keys(EVAL_TASKS);
|
|
13037
|
+
function roleCat(role) {
|
|
13038
|
+
return ROLE_CATEGORY[role] ?? "general";
|
|
13039
|
+
}
|
|
13040
|
+
function createProviderForId(providerId, cfg) {
|
|
13041
|
+
const savedCfg = cfg.providers?.[providerId];
|
|
13042
|
+
const resolvedProviderId = savedCfg?.type ?? providerId;
|
|
13043
|
+
const cfgWithType = {
|
|
13044
|
+
...savedCfg ?? { type: providerId, apiKey: cfg.apiKey, baseUrl: cfg.baseUrl },
|
|
13045
|
+
type: resolvedProviderId
|
|
13046
|
+
};
|
|
13047
|
+
try {
|
|
13048
|
+
return makeProviderFromConfig(resolvedProviderId, cfgWithType);
|
|
13049
|
+
} catch {
|
|
13050
|
+
return void 0;
|
|
13051
|
+
}
|
|
13052
|
+
}
|
|
13053
|
+
async function rankResponses(provider, leaderModel, taskPrompt, responses) {
|
|
13054
|
+
const labelToIdx = /* @__PURE__ */ new Map();
|
|
13055
|
+
const responseBlock = responses.map((r, i) => {
|
|
13056
|
+
const label = String.fromCharCode(65 + i);
|
|
13057
|
+
labelToIdx.set(label, i);
|
|
13058
|
+
return `=== Response ${label} ===
|
|
13059
|
+
${r.text.slice(0, 800)}`;
|
|
13060
|
+
}).join("\n\n");
|
|
13061
|
+
const rankingPrompt = `Rank these responses from BEST (1) to WORST.
|
|
13062
|
+
|
|
13063
|
+
TASK:
|
|
13064
|
+
${taskPrompt.slice(0, 600)}
|
|
13065
|
+
|
|
13066
|
+
RESPONSES:
|
|
13067
|
+
${responseBlock}
|
|
13068
|
+
|
|
13069
|
+
Output ONLY a ranked list, one per line:
|
|
13070
|
+
1. Response X \u2014 brief reason
|
|
13071
|
+
2. Response Y \u2014 brief reason`;
|
|
13072
|
+
try {
|
|
13073
|
+
const resp = await provider.complete(
|
|
13074
|
+
{
|
|
13075
|
+
model: leaderModel,
|
|
13076
|
+
system: [{ type: "text", text: "You are an expert evaluator. Rank responses concisely. Output ONLY the ranked list." }],
|
|
13077
|
+
messages: [{ role: "user", content: [{ type: "text", text: rankingPrompt }] }],
|
|
13078
|
+
maxTokens: 400
|
|
13079
|
+
},
|
|
13080
|
+
{ signal: AbortSignal.timeout(3e4) }
|
|
13081
|
+
);
|
|
13082
|
+
const text = resp.content[0] && "text" in resp.content[0] ? resp.content[0].text : "";
|
|
13083
|
+
const rankings = [];
|
|
13084
|
+
for (const line of text.split("\n")) {
|
|
13085
|
+
const m = line.match(/^\s*(\d+)[\.\)]\s*Response\s+([A-Z])/i);
|
|
13086
|
+
if (m) {
|
|
13087
|
+
const label = m[2].toUpperCase();
|
|
13088
|
+
const idx = labelToIdx.get(label);
|
|
13089
|
+
if (idx !== void 0 && !rankings.includes(idx)) {
|
|
13090
|
+
rankings.push(idx);
|
|
13091
|
+
}
|
|
13092
|
+
}
|
|
13093
|
+
}
|
|
13094
|
+
return rankings.length > 0 ? rankings : responses.map((_, i) => i);
|
|
13095
|
+
} catch {
|
|
13096
|
+
return responses.map((_, i) => i);
|
|
13097
|
+
}
|
|
13098
|
+
}
|
|
13099
|
+
async function readProviders(cachePath2) {
|
|
13100
|
+
if (!cachePath2) {
|
|
13101
|
+
return `${color.red("Models cache not available")}.`;
|
|
13102
|
+
}
|
|
13103
|
+
try {
|
|
13104
|
+
const raw = await fsp4.readFile(cachePath2, "utf8");
|
|
13105
|
+
const parsed = JSON.parse(raw);
|
|
13106
|
+
const payload = parsed.payload ?? parsed;
|
|
13107
|
+
return Object.entries(payload).map(([id, p]) => ({
|
|
13108
|
+
id: p.id ?? id,
|
|
13109
|
+
name: p.name ?? id,
|
|
13110
|
+
family: p.npm ?? id,
|
|
13111
|
+
models: Object.values(p.models ?? {}).map((m) => ({
|
|
13112
|
+
id: m.id,
|
|
13113
|
+
name: m.name,
|
|
13114
|
+
capabilities: {
|
|
13115
|
+
contextWindow: m.limit?.context,
|
|
13116
|
+
maxOutputTokens: m.limit?.output
|
|
13117
|
+
},
|
|
13118
|
+
pricing: m.cost
|
|
13119
|
+
}))
|
|
13120
|
+
}));
|
|
13121
|
+
} catch {
|
|
13122
|
+
return `${color.amber("Models cache not available")}. Run wstack sync-models.`;
|
|
13123
|
+
}
|
|
13124
|
+
}
|
|
13125
|
+
function checkHasKey(pid, config) {
|
|
13126
|
+
if (pid === config.provider && config.provider) return true;
|
|
13127
|
+
const pc = config.providers?.[pid];
|
|
13128
|
+
if (!pc) return false;
|
|
13129
|
+
if (typeof pc.apiKey === "string" && pc.apiKey.length > 0) return true;
|
|
13130
|
+
if (Array.isArray(pc.apiKeys) && pc.apiKeys.some((k) => k?.apiKey)) return true;
|
|
13131
|
+
return false;
|
|
13132
|
+
}
|
|
13133
|
+
var modeldiagCmd = async (args, deps) => {
|
|
13134
|
+
const sub = args[0]?.toLowerCase() || "full";
|
|
13135
|
+
const cacheResult = await readProviders(deps.paths.modelsCache);
|
|
13136
|
+
if (typeof cacheResult === "string") {
|
|
13137
|
+
deps.renderer.write(`${cacheResult}
|
|
13138
|
+
`);
|
|
13139
|
+
return cacheResult.includes(color.red("")) ? 1 : 0;
|
|
13140
|
+
}
|
|
13141
|
+
const providers = cacheResult;
|
|
13142
|
+
const config = deps.config;
|
|
13143
|
+
const modelMatrix = config.modelMatrix ?? {};
|
|
13144
|
+
function hasKey(pid) {
|
|
13145
|
+
return checkHasKey(pid, config);
|
|
13146
|
+
}
|
|
13147
|
+
function writeLine(line = "") {
|
|
13148
|
+
deps.renderer.write(`${line}
|
|
13149
|
+
`);
|
|
13150
|
+
}
|
|
13151
|
+
if (sub === "keys") {
|
|
13152
|
+
writeLine(`${color.bold("API Key Status")}`);
|
|
13153
|
+
writeLine();
|
|
13154
|
+
for (const prov of providers) {
|
|
13155
|
+
const k = hasKey(prov.id);
|
|
13156
|
+
writeLine(` ${checkMark(k)} ${color.bold(prov.id.padEnd(18))} ${color.dim(prov.name)}`);
|
|
13157
|
+
}
|
|
13158
|
+
writeLine();
|
|
13159
|
+
writeLine(`${color.dim(`Leader: ${config.provider}/${config.model}`)}`);
|
|
13160
|
+
return 0;
|
|
13161
|
+
}
|
|
13162
|
+
if (sub === "caps") {
|
|
13163
|
+
writeLine(`${color.bold("Model Capabilities")} ${color.dim("\u2014 matched to known profiles")}`);
|
|
13164
|
+
writeLine();
|
|
13165
|
+
for (const prov of providers) {
|
|
13166
|
+
if (!hasKey(prov.id)) continue;
|
|
13167
|
+
writeLine(` ${color.bold(prov.id)} ${color.dim(`(${prov.name})`)}`);
|
|
13168
|
+
const tiers = { premium: [], standard: [], budget: [], unknown: [] };
|
|
13169
|
+
for (const m of prov.models ?? []) {
|
|
13170
|
+
const profile = findProfile(prov.id, m.id);
|
|
13171
|
+
tiers[profile?.costTier ?? "unknown"].push(m);
|
|
13172
|
+
}
|
|
13173
|
+
for (const tier of ["premium", "standard", "budget", "unknown"]) {
|
|
13174
|
+
const tierModels = tiers[tier];
|
|
13175
|
+
if (tierModels.length === 0) continue;
|
|
13176
|
+
const label = tier === "unknown" ? color.dim("unmatched") : `${costLabel(tier)} ${tier}`;
|
|
13177
|
+
writeLine(` ${label}`);
|
|
13178
|
+
for (const m of tierModels) {
|
|
13179
|
+
const cap = m.capabilities;
|
|
13180
|
+
const ctx = cap?.contextWindow ?? 0;
|
|
13181
|
+
const maxOut = cap?.maxOutputTokens ?? 0;
|
|
13182
|
+
const profile = findProfile(prov.id, m.id);
|
|
13183
|
+
const family = profile ? `${speedLabel(profile.speedTier)} ${color.green(profile.family)}` : color.dim("no profile match");
|
|
13184
|
+
const pricing = m.pricing ? `${color.dim("in")}${fmtPrice2(m.pricing.input)} ${color.dim("out")}${fmtPrice2(m.pricing.output)}` : color.dim("pricing ?");
|
|
13185
|
+
writeLine(
|
|
13186
|
+
` ${color.cyan(m.id.padEnd(34))}${ctx > 0 ? `ctx ${fmtTokens2(ctx).padEnd(6)}` : color.dim("ctx ? ")}${maxOut > 0 ? `out ${fmtTokens2(maxOut).padEnd(6)}` : " "}${family} ${pricing}`
|
|
13187
|
+
);
|
|
13188
|
+
}
|
|
13189
|
+
}
|
|
13190
|
+
writeLine();
|
|
13191
|
+
}
|
|
13192
|
+
writeLine(color.dim("Prices in USD per 1M tokens (input/output). ctx = context window, out = max output."));
|
|
13193
|
+
return 0;
|
|
13194
|
+
}
|
|
13195
|
+
async function renderSuggest() {
|
|
13196
|
+
writeLine();
|
|
13197
|
+
writeLine(`${color.bold("Agent \u2192 Model Suggestions")} ${color.amber("(heuristic \u2014 untested)")}`);
|
|
13198
|
+
writeLine(color.dim('These are profile-based best guesses. Test them with wstack modeldiag bench <role> "<prompt>".'));
|
|
13199
|
+
writeLine();
|
|
13200
|
+
const keyedProviders = providers.filter((p) => hasKey(p.id));
|
|
13201
|
+
if (keyedProviders.length === 0) {
|
|
13202
|
+
writeLine(` ${color.amber("No providers have API keys configured. Add keys with wstack auth.")}`);
|
|
13203
|
+
} else {
|
|
13204
|
+
const roles = [
|
|
13205
|
+
"security-scanner",
|
|
13206
|
+
"bug-hunter",
|
|
13207
|
+
"planner",
|
|
13208
|
+
"architect",
|
|
13209
|
+
"refactor-planner",
|
|
13210
|
+
"test",
|
|
13211
|
+
"document",
|
|
13212
|
+
"code-reviewer",
|
|
13213
|
+
"executor",
|
|
13214
|
+
"debugger"
|
|
13215
|
+
];
|
|
13216
|
+
for (const role of roles) {
|
|
13217
|
+
if (modelMatrix[role]) {
|
|
13218
|
+
const entry = modelMatrix[role];
|
|
13219
|
+
const p = entry.provider ?? config.provider;
|
|
13220
|
+
writeLine(` ${color.dim(role.padEnd(20))} \u2192 ${color.cyan(`${p}/${entry.model}`)} ${color.dim("(user-configured)")}`);
|
|
13221
|
+
continue;
|
|
13222
|
+
}
|
|
13223
|
+
const cat = roleCat(role);
|
|
13224
|
+
const ranked = rankModels(providers, hasKey, cat, 3);
|
|
13225
|
+
if (ranked.length === 0) {
|
|
13226
|
+
writeLine(` ${color.dim(role.padEnd(20))} \u2192 ${color.dim("no candidates")}`);
|
|
13227
|
+
continue;
|
|
13228
|
+
}
|
|
13229
|
+
const best = ranked[0];
|
|
13230
|
+
const family = best.profile ? ` ${color.dim(`(${best.profile.family})`)}` : "";
|
|
13231
|
+
const bar = scoreBar(best.score, 110);
|
|
13232
|
+
writeLine(
|
|
13233
|
+
` ${color.amber(role.padEnd(20))} \u2192 ${color.cyan(`${best.provider}/${best.model}`)}${family}`
|
|
13234
|
+
);
|
|
13235
|
+
writeLine(` ${" ".repeat(22)} ${bar} ${color.dim(cat)}`);
|
|
13236
|
+
if (ranked.length > 1 && ranked[1].score >= best.score - 15) {
|
|
13237
|
+
for (const alt of ranked.slice(1)) {
|
|
13238
|
+
const af = alt.profile ? ` (${alt.profile.family})` : "";
|
|
13239
|
+
writeLine(` ${" ".repeat(22)} ${color.dim(`${alt.provider}/${alt.model}${af} score ${alt.score}`)}`);
|
|
13240
|
+
}
|
|
13241
|
+
}
|
|
13242
|
+
}
|
|
13243
|
+
writeLine();
|
|
13244
|
+
writeLine(` ${color.bold("leader".padEnd(20))} \u2192 ${color.cyan(`${config.provider}/${config.model}`)}`);
|
|
13245
|
+
}
|
|
13246
|
+
}
|
|
13247
|
+
if (sub === "suggest") {
|
|
13248
|
+
await renderSuggest();
|
|
13249
|
+
writeLine();
|
|
13250
|
+
writeLine(color.dim("Pin a suggestion: wstack setmodel set <role> <provider>/<model>"));
|
|
13251
|
+
writeLine(color.dim('Test candidates: wstack modeldiag bench <role> "<test prompt>"'));
|
|
13252
|
+
return 0;
|
|
13253
|
+
}
|
|
13254
|
+
if (sub === "test") {
|
|
13255
|
+
writeLine(`${color.bold("Connectivity Test")}`);
|
|
13256
|
+
writeLine();
|
|
13257
|
+
const keyed = providers.filter((p) => hasKey(p.id));
|
|
13258
|
+
if (keyed.length === 0) {
|
|
13259
|
+
writeLine(` ${color.amber("No providers have API keys. Add keys with wstack auth.")}`);
|
|
13260
|
+
return 0;
|
|
13261
|
+
}
|
|
13262
|
+
for (const prov of keyed) {
|
|
13263
|
+
writeLine(` ${color.cyan("\u27F3")} ${prov.id}... ${color.dim("(capability scan, no API call)")}`);
|
|
13264
|
+
const profile = findProfile(prov.id, config.model ?? "");
|
|
13265
|
+
const firstModel = prov.models?.[0]?.id ?? config.model ?? "?";
|
|
13266
|
+
const cap = prov.models?.[0]?.capabilities;
|
|
13267
|
+
const ctx = cap?.contextWindow ?? 0;
|
|
13268
|
+
writeLine(` ${checkMark(true)} provider: ${prov.id}`);
|
|
13269
|
+
writeLine(` ${checkMark(ctx > 0)} context: ${ctx > 0 ? fmtTokens2(ctx) : "unknown"}`);
|
|
13270
|
+
writeLine(` ${checkMark(!!profile)} profile: ${profile?.family ?? "no match"}`);
|
|
13271
|
+
writeLine(` model: ${color.cyan(firstModel)}`);
|
|
13272
|
+
writeLine();
|
|
13273
|
+
}
|
|
13274
|
+
writeLine(color.dim("Full API connectivity test requires an active session (costs tokens)."));
|
|
13275
|
+
writeLine(color.dim('Use wstack modeldiag bench <role> "<prompt>" to test models with real API calls.'));
|
|
13276
|
+
return 0;
|
|
13277
|
+
}
|
|
13278
|
+
if (sub === "bench") {
|
|
13279
|
+
const benchArgs = args.slice(1);
|
|
13280
|
+
if (benchArgs.length < 2) {
|
|
13281
|
+
writeLine(`${color.amber("Usage:")} wstack modeldiag bench <role> "<test prompt>" [--providers=p1,p2]`);
|
|
13282
|
+
writeLine();
|
|
13283
|
+
writeLine(color.dim('Example: wstack modeldiag bench verify "Write a function that checks if a string is a palindrome"'));
|
|
13284
|
+
writeLine(color.dim("Tests the top 5 candidate models for the role with your prompt and reports results."));
|
|
13285
|
+
writeLine(color.dim("Add --providers=anthropic,google to test across multiple providers."));
|
|
13286
|
+
return 0;
|
|
13287
|
+
}
|
|
13288
|
+
const providersEqIdx = benchArgs.findIndex((a) => a.startsWith("--providers="));
|
|
13289
|
+
let providerFilter;
|
|
13290
|
+
if (providersEqIdx >= 0) {
|
|
13291
|
+
providerFilter = benchArgs[providersEqIdx].replace("--providers=", "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
13292
|
+
benchArgs.splice(providersEqIdx, 1);
|
|
13293
|
+
}
|
|
13294
|
+
const benchRole = benchArgs[0];
|
|
13295
|
+
const benchPrompt = benchArgs.slice(1).join(" ");
|
|
13296
|
+
const cat = roleCat(benchRole);
|
|
13297
|
+
const candidates = rankModels(providers, hasKey, cat, 5);
|
|
13298
|
+
if (candidates.length === 0) {
|
|
13299
|
+
writeLine(`${color.amber("No candidate models found")} for role "${benchRole}" (category: ${cat}).`);
|
|
13300
|
+
return 0;
|
|
13301
|
+
}
|
|
13302
|
+
let targetCandidates;
|
|
13303
|
+
if (providerFilter && providerFilter.length > 0) {
|
|
13304
|
+
targetCandidates = candidates.filter((c) => providerFilter.includes(c.provider));
|
|
13305
|
+
if (targetCandidates.length === 0) {
|
|
13306
|
+
writeLine(`${color.amber("No candidates match the specified providers")}: ${providerFilter.join(", ")}`);
|
|
13307
|
+
writeLine(`Candidate providers: ${[...new Set(candidates.map((c) => c.provider))].join(", ")}`);
|
|
13308
|
+
return 0;
|
|
13309
|
+
}
|
|
13310
|
+
} else {
|
|
13311
|
+
targetCandidates = candidates;
|
|
13312
|
+
}
|
|
13313
|
+
writeLine(`${color.bold("Model Benchmark")} \u2014 ${color.amber(benchRole)} ${color.dim(`(category: ${cat})`)}`);
|
|
13314
|
+
writeLine(`${color.dim("Prompt:")} "${benchPrompt.slice(0, 120)}${benchPrompt.length > 120 ? "\u2026" : ""}"`);
|
|
13315
|
+
writeLine();
|
|
13316
|
+
writeLine(
|
|
13317
|
+
` ${color.dim("# model".padEnd(52))} ${color.dim("score".padEnd(12))} ${color.dim("latency".padEnd(10))} ${color.dim("tokens".padEnd(14))} ${color.dim("first line")}`
|
|
13318
|
+
);
|
|
13319
|
+
writeLine(` ${color.dim("\u2500".repeat(108))}`);
|
|
13320
|
+
const providerInstances = /* @__PURE__ */ new Map();
|
|
13321
|
+
for (const pid of [...new Set(targetCandidates.map((c) => c.provider))]) {
|
|
13322
|
+
const prov = createProviderForId(pid, config);
|
|
13323
|
+
if (prov) providerInstances.set(pid, prov);
|
|
13324
|
+
}
|
|
13325
|
+
let idx = 0;
|
|
13326
|
+
for (const c of targetCandidates.slice(0, 20)) {
|
|
13327
|
+
idx++;
|
|
13328
|
+
const label = `${idx}`.padStart(2);
|
|
13329
|
+
const modelKey = `${c.provider}/${c.model}`;
|
|
13330
|
+
const prov = providerInstances.get(c.provider);
|
|
13331
|
+
if (!prov) {
|
|
13332
|
+
writeLine(
|
|
13333
|
+
` ${label} ${color.red(modelKey.padEnd(50))} ${scoreBar(c.score, 110).slice(0, 11)} ${color.red("NO PROVIDER")}`
|
|
13334
|
+
);
|
|
13335
|
+
continue;
|
|
13336
|
+
}
|
|
13337
|
+
try {
|
|
13338
|
+
const start = Date.now();
|
|
13339
|
+
const resp = await prov.complete(
|
|
13340
|
+
{
|
|
13341
|
+
model: c.model,
|
|
13342
|
+
messages: [{ role: "user", content: [{ type: "text", text: benchPrompt }] }],
|
|
13343
|
+
maxTokens: 256
|
|
13344
|
+
},
|
|
13345
|
+
{ signal: AbortSignal.timeout(3e4) }
|
|
13346
|
+
);
|
|
13347
|
+
const latency = Date.now() - start;
|
|
13348
|
+
const firstText = resp.content[0] && "text" in resp.content[0] ? resp.content[0].text : "";
|
|
13349
|
+
const firstLineClean = firstText.replace(/\n/g, " ").slice(0, 80) || color.dim("(empty)");
|
|
13350
|
+
const provColor = c.provider === config.provider ? color.green : color.cyan;
|
|
13351
|
+
const usage = resp.usage;
|
|
13352
|
+
writeLine(
|
|
13353
|
+
` ${label} ${provColor(modelKey.padEnd(50))} ${scoreBar(c.score, 110).slice(0, 11)} ${color.amber(fmtMs(latency).padEnd(8))} ${color.dim(`in${usage?.input ?? "?"}/out${usage?.output ?? "?"}`.padEnd(12))} ${firstLineClean}`
|
|
13354
|
+
);
|
|
13355
|
+
} catch (err) {
|
|
13356
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
13357
|
+
writeLine(
|
|
13358
|
+
` ${label} ${color.red(modelKey.padEnd(50))} ${scoreBar(c.score, 110).slice(0, 11)} ${color.red("FAILED")} ${color.dim(errMsg.slice(0, 40))}`
|
|
13359
|
+
);
|
|
13360
|
+
}
|
|
13361
|
+
}
|
|
13362
|
+
const testedProviders = [...new Set(targetCandidates.map((c) => c.provider))];
|
|
13363
|
+
writeLine();
|
|
13364
|
+
writeLine(
|
|
13365
|
+
color.dim(`Tested ${idx} model(s) across ${testedProviders.length} provider(s): ${testedProviders.join(", ")}.`)
|
|
13366
|
+
);
|
|
13367
|
+
writeLine(color.dim("Pin the best: wstack setmodel set <role> <provider>/<model>"));
|
|
13368
|
+
return 0;
|
|
13369
|
+
}
|
|
13370
|
+
if (sub === "eval" || sub === "evall") {
|
|
13371
|
+
const evalArgs = args.slice(1);
|
|
13372
|
+
const providersEq = evalArgs.find((a) => a.startsWith("--providers="));
|
|
13373
|
+
const providerFilter = providersEq ? providersEq.replace("--providers=", "").split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
13374
|
+
const maxEq = evalArgs.find((a) => a.startsWith("--max="));
|
|
13375
|
+
const maxModels = maxEq ? Math.max(1, parseInt(maxEq.replace("--max=", ""), 10) || 2) : 2;
|
|
13376
|
+
const quick = evalArgs.includes("--quick");
|
|
13377
|
+
const modelsPerCat = quick ? 1 : maxModels;
|
|
13378
|
+
const roleFilter = evalArgs.find((a) => !a.startsWith("--"));
|
|
13379
|
+
const targetCategories = roleFilter ? EVAL_CATEGORIES.includes(roleCat(roleFilter)) ? [roleCat(roleFilter)] : [] : EVAL_CATEGORIES;
|
|
13380
|
+
if (targetCategories.length === 0 && roleFilter) {
|
|
13381
|
+
writeLine(`${color.amber("Unknown role/category")}: "${roleFilter}". Try: ${EVAL_CATEGORIES.join(", ")}`);
|
|
13382
|
+
return 1;
|
|
13383
|
+
}
|
|
13384
|
+
const keyedProviderIds2 = providers.filter((p) => hasKey(p.id)).map((p) => p.id);
|
|
13385
|
+
let targetProviderIds;
|
|
13386
|
+
if (providerFilter && providerFilter.length > 0) {
|
|
13387
|
+
const unknown = providerFilter.filter((pid) => !keyedProviderIds2.includes(pid));
|
|
13388
|
+
targetProviderIds = providerFilter.filter((pid) => keyedProviderIds2.includes(pid));
|
|
13389
|
+
if (targetProviderIds.length === 0) {
|
|
13390
|
+
const noKeyMsg = unknown.length > 0 ? `None of the specified providers (${unknown.join(", ")}) have API keys. Add keys with wstack auth.` : "None of the specified providers have API keys configured.";
|
|
13391
|
+
writeLine(`${color.amber(noKeyMsg)}`);
|
|
13392
|
+
return 0;
|
|
13393
|
+
}
|
|
13394
|
+
} else if (keyedProviderIds2.length === 0) {
|
|
13395
|
+
writeLine(`${color.amber("No providers have API keys. Add keys with wstack auth.")}`);
|
|
13396
|
+
return 0;
|
|
13397
|
+
} else if (keyedProviderIds2.length === 1) {
|
|
13398
|
+
targetProviderIds = keyedProviderIds2;
|
|
13399
|
+
} else {
|
|
13400
|
+
const providerList = keyedProviderIds2.map((pid, i) => {
|
|
13401
|
+
const info = providers.find((p) => p.id === pid);
|
|
13402
|
+
return ` ${color.cyan(String(i + 1))}) ${color.bold(pid.padEnd(16))} ${color.dim(info?.name ?? "")}`;
|
|
13403
|
+
}).join("\n");
|
|
13404
|
+
deps.renderer.write(`
|
|
13405
|
+
${color.bold("Select providers to evaluate")}
|
|
13406
|
+
|
|
13407
|
+
${providerList}
|
|
13408
|
+
|
|
13409
|
+
${color.dim('Enter numbers or provider IDs (comma-separated, or "all"):')}
|
|
13410
|
+
`);
|
|
13411
|
+
const input = await deps.reader.readLine(" > ");
|
|
13412
|
+
const selected = input.trim().toLowerCase();
|
|
13413
|
+
if (selected === "" || selected === "all") {
|
|
13414
|
+
targetProviderIds = keyedProviderIds2;
|
|
13415
|
+
} else {
|
|
13416
|
+
targetProviderIds = [];
|
|
13417
|
+
for (const part of selected.split(",").map((s) => s.trim())) {
|
|
13418
|
+
const idx = parseInt(part, 10);
|
|
13419
|
+
if (idx >= 1 && idx <= keyedProviderIds2.length) {
|
|
13420
|
+
const pid = keyedProviderIds2[idx - 1];
|
|
13421
|
+
if (!targetProviderIds.includes(pid)) targetProviderIds.push(pid);
|
|
13422
|
+
} else if (keyedProviderIds2.includes(part)) {
|
|
13423
|
+
if (!targetProviderIds.includes(part)) targetProviderIds.push(part);
|
|
13424
|
+
}
|
|
13425
|
+
}
|
|
13426
|
+
}
|
|
13427
|
+
if (targetProviderIds.length === 0) {
|
|
13428
|
+
writeLine(color.dim("No providers selected."));
|
|
13429
|
+
return 0;
|
|
13430
|
+
}
|
|
13431
|
+
}
|
|
13432
|
+
const leaderModel = config.model ?? "unknown";
|
|
13433
|
+
const unknownProviders = providerFilter ? providerFilter.filter((pid) => !keyedProviderIds2.includes(pid)) : [];
|
|
13434
|
+
const warningLine = unknownProviders.length > 0 ? ` ${color.amber("\u26A0 skipped (no key):")} ${unknownProviders.join(", ")}
|
|
13435
|
+
` : "";
|
|
13436
|
+
writeLine(`${color.bold("Model Competency Evaluation")}`);
|
|
13437
|
+
writeLine(color.dim(`Providers: ${targetProviderIds.join(", ")} | ${targetCategories.length} cats | ${modelsPerCat} model(s)/cat/provider`));
|
|
13438
|
+
writeLine(warningLine);
|
|
13439
|
+
writeLine(color.dim(`Leader (ranker): ${config.provider}/${leaderModel}`));
|
|
13440
|
+
writeLine();
|
|
13441
|
+
const collected = /* @__PURE__ */ new Map();
|
|
13442
|
+
let total = 0;
|
|
13443
|
+
let ok = 0;
|
|
13444
|
+
for (const pid of targetProviderIds) {
|
|
13445
|
+
const prov = createProviderForId(pid, config);
|
|
13446
|
+
if (!prov) {
|
|
13447
|
+
writeLine(color.dim(` \u2298 ${pid}: provider unavailable, skipping`));
|
|
13448
|
+
continue;
|
|
13449
|
+
}
|
|
13450
|
+
for (const cat of targetCategories) {
|
|
13451
|
+
const task = EVAL_TASKS[cat];
|
|
13452
|
+
if (!task) continue;
|
|
13453
|
+
const candidates = rankModels(providers, hasKey, cat, modelsPerCat).filter((c) => c.provider === pid);
|
|
13454
|
+
if (candidates.length === 0) continue;
|
|
13455
|
+
if (!collected.has(cat)) collected.set(cat, /* @__PURE__ */ new Map());
|
|
13456
|
+
for (const c of candidates) {
|
|
13457
|
+
total++;
|
|
13458
|
+
const modelKey = `${pid}/${c.model}`;
|
|
13459
|
+
try {
|
|
13460
|
+
const start = Date.now();
|
|
13461
|
+
const resp = await prov.complete(
|
|
13462
|
+
{
|
|
13463
|
+
model: c.model,
|
|
13464
|
+
system: [{ type: "text", text: "Be thorough and correct." }],
|
|
13465
|
+
messages: [{ role: "user", content: [{ type: "text", text: task.prompt }] }],
|
|
13466
|
+
maxTokens: 1024
|
|
13467
|
+
},
|
|
13468
|
+
{ signal: AbortSignal.timeout(45e3) }
|
|
13469
|
+
);
|
|
13470
|
+
const respText = resp.content[0] && "text" in resp.content[0] ? resp.content[0].text : "";
|
|
13471
|
+
const respUsage = resp.usage;
|
|
13472
|
+
collected.get(cat).set(modelKey, {
|
|
13473
|
+
model: modelKey,
|
|
13474
|
+
latency: Date.now() - start,
|
|
13475
|
+
tokens: (respUsage?.input ?? 0) + (respUsage?.output ?? 0),
|
|
13476
|
+
text: respText
|
|
13477
|
+
});
|
|
13478
|
+
ok++;
|
|
13479
|
+
} catch {
|
|
13480
|
+
collected.get(cat).set(modelKey, {
|
|
13481
|
+
model: modelKey,
|
|
13482
|
+
latency: -1,
|
|
13483
|
+
tokens: 0,
|
|
13484
|
+
text: ""
|
|
13485
|
+
});
|
|
13486
|
+
}
|
|
13487
|
+
}
|
|
13488
|
+
}
|
|
13489
|
+
}
|
|
13490
|
+
writeLine(`${color.dim(`Phase 1: ${ok}/${total} calls succeeded`)}`);
|
|
13491
|
+
writeLine();
|
|
13492
|
+
if (collected.size === 0) {
|
|
13493
|
+
writeLine(color.amber("No responses collected. Check provider configuration."));
|
|
13494
|
+
return 0;
|
|
13495
|
+
}
|
|
13496
|
+
const leaderProvider = createProviderForId(config.provider, config);
|
|
13497
|
+
if (leaderProvider) {
|
|
13498
|
+
writeLine(`${color.bold("Phase 2")} \u2014 ${color.dim("leader ranks responses")}`);
|
|
13499
|
+
writeLine();
|
|
13500
|
+
}
|
|
13501
|
+
const rankings = /* @__PURE__ */ new Map();
|
|
13502
|
+
for (const [cat, responses] of collected) {
|
|
13503
|
+
const valid = Array.from(responses.values()).filter((r) => r.latency >= 0);
|
|
13504
|
+
if (valid.length < 2) {
|
|
13505
|
+
if (valid.length === 1) {
|
|
13506
|
+
const m = valid[0].model;
|
|
13507
|
+
if (!rankings.has(m)) rankings.set(m, /* @__PURE__ */ new Map());
|
|
13508
|
+
rankings.get(m).set(cat, { rank: 1, total: 1 });
|
|
13509
|
+
}
|
|
13510
|
+
continue;
|
|
13511
|
+
}
|
|
13512
|
+
const task = EVAL_TASKS[cat];
|
|
13513
|
+
if (leaderProvider) {
|
|
13514
|
+
const ranked = await rankResponses(leaderProvider, leaderModel, task.prompt, valid);
|
|
13515
|
+
for (let i = 0; i < valid.length; i++) {
|
|
13516
|
+
const m = valid[ranked[i] ?? i].model;
|
|
13517
|
+
if (!rankings.has(m)) rankings.set(m, /* @__PURE__ */ new Map());
|
|
13518
|
+
rankings.get(m).set(cat, { rank: i + 1, total: valid.length });
|
|
13519
|
+
}
|
|
13520
|
+
} else {
|
|
13521
|
+
for (const r of valid) {
|
|
13522
|
+
if (!rankings.has(r.model)) rankings.set(r.model, /* @__PURE__ */ new Map());
|
|
13523
|
+
rankings.get(r.model).set(cat, { rank: 1, total: valid.length });
|
|
13524
|
+
}
|
|
13525
|
+
}
|
|
13526
|
+
}
|
|
13527
|
+
writeLine(`${color.bold("Competency Report")}`);
|
|
13528
|
+
writeLine();
|
|
13529
|
+
const allModels = [.../* @__PURE__ */ new Set([...rankings.keys()])].sort();
|
|
13530
|
+
const catList = [...collected.keys()];
|
|
13531
|
+
const modelColWidth = Math.max(24, ...allModels.map((m) => m.length)) + 2;
|
|
13532
|
+
const cw = 12;
|
|
13533
|
+
writeLine(
|
|
13534
|
+
` ${color.dim("model".padEnd(modelColWidth))}` + catList.map((c) => color.dim((EVAL_TASKS[c]?.label ?? c).slice(0, cw).padEnd(cw + 2))).join("")
|
|
13535
|
+
);
|
|
13536
|
+
writeLine(` ${color.dim("\u2500".repeat(modelColWidth + catList.length * (cw + 2)))}`);
|
|
13537
|
+
for (const model of allModels) {
|
|
13538
|
+
const mr = rankings.get(model);
|
|
13539
|
+
const provFromModel = model.split("/")[0] ?? "";
|
|
13540
|
+
const modelColor = provFromModel === config.provider ? color.cyan : color.green;
|
|
13541
|
+
let row = ` ${modelColor(model.padEnd(modelColWidth))}`;
|
|
13542
|
+
for (const cat of catList) {
|
|
13543
|
+
const e = mr.get(cat);
|
|
13544
|
+
if (e) {
|
|
13545
|
+
const pct2 = Math.round((1 - (e.rank - 1) / Math.max(1, e.total - 1)) * 100);
|
|
13546
|
+
const pc = pct2 >= 80 ? color.green : pct2 >= 50 ? color.amber : color.red;
|
|
13547
|
+
row += `${pc(`#${e.rank} ${pct2}%`.padEnd(cw + 2))}`;
|
|
13548
|
+
} else {
|
|
13549
|
+
row += color.dim("\u2014".padEnd(cw + 2));
|
|
13550
|
+
}
|
|
13551
|
+
}
|
|
13552
|
+
writeLine(row);
|
|
13553
|
+
}
|
|
13554
|
+
writeLine();
|
|
13555
|
+
writeLine(color.dim("#1 100% = best in category. \u2014 = not tested."));
|
|
13556
|
+
writeLine();
|
|
13557
|
+
writeLine(color.dim("Pin: wstack setmodel set <role> <provider>/<model>"));
|
|
13558
|
+
writeLine(color.dim("Full: wstack modeldiag eval Providers: wstack modeldiag eval --providers=id1,id2"));
|
|
13559
|
+
writeLine(color.dim("Max: wstack modeldiag eval --max=3 Quick: wstack modeldiag eval --quick"));
|
|
13560
|
+
return 0;
|
|
13561
|
+
}
|
|
13562
|
+
writeLine(`${color.bold("API Key Status")}`);
|
|
13563
|
+
writeLine();
|
|
13564
|
+
for (const prov of providers) {
|
|
13565
|
+
const k = hasKey(prov.id);
|
|
13566
|
+
writeLine(` ${checkMark(k)} ${color.bold(prov.id.padEnd(18))} ${color.dim(prov.name)}`);
|
|
13567
|
+
}
|
|
13568
|
+
writeLine();
|
|
13569
|
+
writeLine(`${color.dim(`Leader: ${config.provider}/${config.model}`)}`);
|
|
13570
|
+
writeLine();
|
|
13571
|
+
writeLine(`${color.bold("Model Capabilities")} ${color.dim("\u2014 matched to known profiles")}`);
|
|
13572
|
+
writeLine();
|
|
13573
|
+
for (const prov of providers) {
|
|
13574
|
+
if (!hasKey(prov.id)) continue;
|
|
13575
|
+
writeLine(` ${color.bold(prov.id)} ${color.dim(`(${prov.name})`)}`);
|
|
13576
|
+
const tiers = { premium: [], standard: [], budget: [], unknown: [] };
|
|
13577
|
+
for (const m of prov.models ?? []) {
|
|
13578
|
+
const profile = findProfile(prov.id, m.id);
|
|
13579
|
+
tiers[profile?.costTier ?? "unknown"].push(m);
|
|
13580
|
+
}
|
|
13581
|
+
for (const tier of ["premium", "standard", "budget", "unknown"]) {
|
|
13582
|
+
const tierModels = tiers[tier];
|
|
13583
|
+
if (tierModels.length === 0) continue;
|
|
13584
|
+
const label = tier === "unknown" ? color.dim("unmatched") : `${costLabel(tier)} ${tier}`;
|
|
13585
|
+
writeLine(` ${label}`);
|
|
13586
|
+
for (const m of tierModels) {
|
|
13587
|
+
const cap = m.capabilities;
|
|
13588
|
+
const ctx = cap?.contextWindow ?? 0;
|
|
13589
|
+
const maxOut = cap?.maxOutputTokens ?? 0;
|
|
13590
|
+
const profile = findProfile(prov.id, m.id);
|
|
13591
|
+
const family = profile ? `${speedLabel(profile.speedTier)} ${color.green(profile.family)}` : color.dim("no profile match");
|
|
13592
|
+
const pricing = m.pricing ? `${color.dim("in")}${fmtPrice2(m.pricing.input)} ${color.dim("out")}${fmtPrice2(m.pricing.output)}` : color.dim("pricing ?");
|
|
13593
|
+
writeLine(
|
|
13594
|
+
` ${color.cyan(m.id.padEnd(34))}${ctx > 0 ? `ctx ${fmtTokens2(ctx).padEnd(6)}` : color.dim("ctx ? ")}${maxOut > 0 ? `out ${fmtTokens2(maxOut).padEnd(6)}` : " "}${family} ${pricing}`
|
|
13595
|
+
);
|
|
13596
|
+
}
|
|
13597
|
+
}
|
|
13598
|
+
writeLine();
|
|
13599
|
+
}
|
|
13600
|
+
await renderSuggest();
|
|
13601
|
+
writeLine();
|
|
13602
|
+
writeLine(color.dim("Pin a suggestion: wstack setmodel set <role> <provider>/<model>"));
|
|
13603
|
+
writeLine(color.dim('Test candidates: wstack modeldiag bench <role> "<test prompt>"'));
|
|
13604
|
+
return 0;
|
|
13605
|
+
};
|
|
12653
13606
|
var versionCmd = async (_args, deps) => {
|
|
12654
13607
|
deps.renderer.write(
|
|
12655
13608
|
`WrongStack ${CLI_VERSION} (apiVersion ${API_VERSION}, node ${process.version}, ${os2.platform()})
|
|
@@ -12729,7 +13682,8 @@ var subcommands = {
|
|
|
12729
13682
|
usage: usageCmd,
|
|
12730
13683
|
version: versionCmd,
|
|
12731
13684
|
help: helpCmd,
|
|
12732
|
-
projects: projectsCmd
|
|
13685
|
+
projects: projectsCmd,
|
|
13686
|
+
modeldiag: modeldiagCmd
|
|
12733
13687
|
};
|
|
12734
13688
|
|
|
12735
13689
|
// src/utils.ts
|
|
@@ -12752,22 +13706,22 @@ function fmtDuration(ms) {
|
|
|
12752
13706
|
const remMin = m - h * 60;
|
|
12753
13707
|
return `${h}h${remMin}m`;
|
|
12754
13708
|
}
|
|
12755
|
-
function fmtTaskResultLine(r,
|
|
13709
|
+
function fmtTaskResultLine(r, color58) {
|
|
12756
13710
|
const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
|
|
12757
13711
|
const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
|
|
12758
13712
|
const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
|
|
12759
13713
|
const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
|
|
12760
|
-
const errKindChip = errKind ?
|
|
12761
|
-
const errSnip = errMsg || errKind ? `${errKindChip}${
|
|
13714
|
+
const errKindChip = errKind ? color58.dim(` [${errKind}]`) : "";
|
|
13715
|
+
const errSnip = errMsg || errKind ? `${errKindChip}${color58.dim(errTail)}` : "";
|
|
12762
13716
|
switch (r.status) {
|
|
12763
13717
|
case "success":
|
|
12764
|
-
return { mark:
|
|
13718
|
+
return { mark: color58.green("\u2713"), stats, tail: "" };
|
|
12765
13719
|
case "timeout":
|
|
12766
|
-
return { mark:
|
|
13720
|
+
return { mark: color58.yellow("\u23F1"), stats: `${color58.yellow("timeout")} ${stats}`, tail: errSnip };
|
|
12767
13721
|
case "stopped":
|
|
12768
|
-
return { mark:
|
|
13722
|
+
return { mark: color58.dim("\u2298"), stats: `${color58.dim("stopped")} ${stats}`, tail: errSnip };
|
|
12769
13723
|
case "failed":
|
|
12770
|
-
return { mark:
|
|
13724
|
+
return { mark: color58.red("\u2717"), stats: `${color58.red("failed")} ${stats}`, tail: errSnip };
|
|
12771
13725
|
}
|
|
12772
13726
|
}
|
|
12773
13727
|
|
|
@@ -12785,7 +13739,7 @@ function resolveBundledSkillsDir() {
|
|
|
12785
13739
|
try {
|
|
12786
13740
|
const req2 = createRequire(import.meta.url);
|
|
12787
13741
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
12788
|
-
return
|
|
13742
|
+
return path9.join(path9.dirname(corePkg), "skills");
|
|
12789
13743
|
} catch {
|
|
12790
13744
|
return void 0;
|
|
12791
13745
|
}
|
|
@@ -13035,7 +13989,7 @@ async function boot(argv) {
|
|
|
13035
13989
|
} catch {
|
|
13036
13990
|
}
|
|
13037
13991
|
printLaunchHints(renderer, flags, {
|
|
13038
|
-
cursorFile:
|
|
13992
|
+
cursorFile: path9.join(wpaths.cacheDir, "hint-cursor")
|
|
13039
13993
|
});
|
|
13040
13994
|
}
|
|
13041
13995
|
return {
|
|
@@ -13056,7 +14010,7 @@ async function boot(argv) {
|
|
|
13056
14010
|
}
|
|
13057
14011
|
async function checkGitInCwd(opts) {
|
|
13058
14012
|
const { cwd, renderer, reader } = opts;
|
|
13059
|
-
const cwdGit =
|
|
14013
|
+
const cwdGit = path9.join(cwd, ".git");
|
|
13060
14014
|
let hasCwdGit = false;
|
|
13061
14015
|
try {
|
|
13062
14016
|
await fsp4.access(cwdGit);
|
|
@@ -13097,10 +14051,10 @@ async function checkGitInCwd(opts) {
|
|
|
13097
14051
|
}
|
|
13098
14052
|
}
|
|
13099
14053
|
}
|
|
13100
|
-
const parentDir =
|
|
14054
|
+
const parentDir = path9.dirname(cwd);
|
|
13101
14055
|
if (parentDir !== cwd) {
|
|
13102
14056
|
try {
|
|
13103
|
-
await fsp4.access(
|
|
14057
|
+
await fsp4.access(path9.join(parentDir, ".git"));
|
|
13104
14058
|
renderer.write(
|
|
13105
14059
|
` ${color.dim("\u2139")} A ${color.bold(".git")} repo exists in the parent directory: ${color.dim(parentDir)}
|
|
13106
14060
|
`
|
|
@@ -14296,7 +15250,10 @@ async function execute(deps) {
|
|
|
14296
15250
|
logLevel: cfg.log?.level ?? "info",
|
|
14297
15251
|
auditLevel: cfg.session?.auditLevel ?? "standard",
|
|
14298
15252
|
indexOnStart: cfg.indexing?.onSessionStart !== false,
|
|
14299
|
-
maxIterations: cfg.tools?.maxIterations ?? 500
|
|
15253
|
+
maxIterations: cfg.tools?.maxIterations ?? 500,
|
|
15254
|
+
debugStream: cfg.debugStream ?? false,
|
|
15255
|
+
configScope: cfg.configScope ?? "global",
|
|
15256
|
+
enhanceDelayMs: cfg.autonomy?.enhanceDelayMs ?? 6e4
|
|
14300
15257
|
};
|
|
14301
15258
|
},
|
|
14302
15259
|
async saveSettings(s) {
|
|
@@ -14305,6 +15262,7 @@ async function execute(deps) {
|
|
|
14305
15262
|
{
|
|
14306
15263
|
configStore,
|
|
14307
15264
|
globalConfigPath: wpaths.globalConfig,
|
|
15265
|
+
inProjectConfigPath: wpaths.inProjectConfig,
|
|
14308
15266
|
vault: { encrypt: (v) => v, decrypt: (v) => v, isEncrypted: () => false }
|
|
14309
15267
|
},
|
|
14310
15268
|
(autonomy) => {
|
|
@@ -14318,8 +15276,10 @@ async function execute(deps) {
|
|
|
14318
15276
|
a["confirmExit"] = s.confirmExit ?? true;
|
|
14319
15277
|
}
|
|
14320
15278
|
);
|
|
14321
|
-
if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.nextPrediction !== void 0) {
|
|
14322
|
-
const
|
|
15279
|
+
if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.nextPrediction !== void 0 || s.debugStream !== void 0 || s.configScope !== void 0 || s.enhanceDelayMs !== void 0) {
|
|
15280
|
+
const configScope = s.configScope ?? (configStore.get().configScope ?? "global");
|
|
15281
|
+
const targetPath = configScope === "project" && wpaths.inProjectConfig ? wpaths.inProjectConfig : wpaths.globalConfig;
|
|
15282
|
+
const raw = await fsp4.readFile(targetPath, "utf8").catch(() => "{}");
|
|
14323
15283
|
const parsed = JSON.parse(raw);
|
|
14324
15284
|
const vault = { encrypt: (v) => v, decrypt: (v) => v, isEncrypted: () => false };
|
|
14325
15285
|
const decrypted = decryptConfigSecrets$1(parsed, vault);
|
|
@@ -14361,8 +15321,26 @@ async function execute(deps) {
|
|
|
14361
15321
|
tools.maxIterations = s.maxIterations;
|
|
14362
15322
|
decrypted.tools = tools;
|
|
14363
15323
|
}
|
|
14364
|
-
|
|
14365
|
-
|
|
15324
|
+
if (s.debugStream !== void 0) {
|
|
15325
|
+
decrypted.debugStream = s.debugStream;
|
|
15326
|
+
const { setDebugStreamEnabled } = await import('@wrongstack/providers');
|
|
15327
|
+
setDebugStreamEnabled(s.debugStream);
|
|
15328
|
+
}
|
|
15329
|
+
if (s.configScope !== void 0) {
|
|
15330
|
+
decrypted.configScope = s.configScope;
|
|
15331
|
+
}
|
|
15332
|
+
if (s.enhanceDelayMs !== void 0) {
|
|
15333
|
+
const autonomy = decrypted.autonomy ?? {};
|
|
15334
|
+
autonomy.enhanceDelayMs = s.enhanceDelayMs;
|
|
15335
|
+
decrypted.autonomy = autonomy;
|
|
15336
|
+
}
|
|
15337
|
+
const toWrite = targetPath === wpaths.globalConfig ? decrypted : filterSafeForProject(decrypted);
|
|
15338
|
+
const encrypted = encryptConfigSecrets$1(toWrite, vault);
|
|
15339
|
+
if (targetPath !== wpaths.globalConfig) {
|
|
15340
|
+
await fsp4.mkdir(path9.dirname(targetPath), { recursive: true }).catch(() => {
|
|
15341
|
+
});
|
|
15342
|
+
}
|
|
15343
|
+
await atomicWrite(targetPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
14366
15344
|
configStore.update({
|
|
14367
15345
|
...s.nextPrediction !== void 0 ? { nextPrediction: s.nextPrediction } : {},
|
|
14368
15346
|
...s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 ? { features: decrypted.features } : {},
|
|
@@ -14370,7 +15348,10 @@ async function execute(deps) {
|
|
|
14370
15348
|
...s.logLevel !== void 0 ? { log: decrypted.log } : {},
|
|
14371
15349
|
...s.auditLevel !== void 0 ? { session: decrypted.session } : {},
|
|
14372
15350
|
...s.indexOnStart !== void 0 ? { indexing: decrypted.indexing } : {},
|
|
14373
|
-
...s.maxIterations !== void 0 ? { tools: decrypted.tools } : {}
|
|
15351
|
+
...s.maxIterations !== void 0 ? { tools: decrypted.tools } : {},
|
|
15352
|
+
...s.debugStream !== void 0 ? { debugStream: s.debugStream } : {},
|
|
15353
|
+
...s.configScope !== void 0 ? { configScope: s.configScope } : {},
|
|
15354
|
+
...s.enhanceDelayMs !== void 0 ? { autonomy: { ...configStore.get().autonomy ?? {}, enhanceDelayMs: s.enhanceDelayMs } } : {}
|
|
14374
15355
|
});
|
|
14375
15356
|
}
|
|
14376
15357
|
if (s.streamFleet !== void 0) {
|
|
@@ -14445,6 +15426,16 @@ async function execute(deps) {
|
|
|
14445
15426
|
getModeLabel: () => {
|
|
14446
15427
|
const metaMode = context.meta?.["mode"];
|
|
14447
15428
|
return typeof metaMode === "string" ? metaMode : modeId ?? "default";
|
|
15429
|
+
},
|
|
15430
|
+
registerDebugStreamCallback: (cb) => {
|
|
15431
|
+
void import('@wrongstack/providers').then(
|
|
15432
|
+
({ setDebugStreamCallback }) => setDebugStreamCallback(cb)
|
|
15433
|
+
);
|
|
15434
|
+
},
|
|
15435
|
+
restoreDebugStreamCallback: () => {
|
|
15436
|
+
void import('@wrongstack/providers').then(
|
|
15437
|
+
({ setDebugStreamCallback, defaultDebugStreamCallback }) => setDebugStreamCallback(defaultDebugStreamCallback)
|
|
15438
|
+
);
|
|
14448
15439
|
}
|
|
14449
15440
|
});
|
|
14450
15441
|
} finally {
|
|
@@ -14475,7 +15466,7 @@ async function execute(deps) {
|
|
|
14475
15466
|
supportsVision,
|
|
14476
15467
|
attachments,
|
|
14477
15468
|
effectiveMaxContext,
|
|
14478
|
-
projectName:
|
|
15469
|
+
projectName: path9.basename(projectRoot) || void 0,
|
|
14479
15470
|
projectRoot,
|
|
14480
15471
|
getAutonomy,
|
|
14481
15472
|
onAutonomy,
|
|
@@ -14503,7 +15494,7 @@ async function execute(deps) {
|
|
|
14503
15494
|
supportsVision,
|
|
14504
15495
|
attachments,
|
|
14505
15496
|
effectiveMaxContext,
|
|
14506
|
-
projectName:
|
|
15497
|
+
projectName: path9.basename(projectRoot) || void 0,
|
|
14507
15498
|
getAutonomy,
|
|
14508
15499
|
onAutonomy,
|
|
14509
15500
|
getNextPredict,
|
|
@@ -14635,7 +15626,7 @@ var MultiAgentHost = class {
|
|
|
14635
15626
|
doneCondition: { type: "all_tasks_done" },
|
|
14636
15627
|
maxConcurrent: this.opts.maxConcurrent ?? 4
|
|
14637
15628
|
};
|
|
14638
|
-
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ?
|
|
15629
|
+
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path9.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
|
|
14639
15630
|
this.director = new Director({
|
|
14640
15631
|
config: coordinatorConfig,
|
|
14641
15632
|
manifestPath: this.opts.manifestPath,
|
|
@@ -15104,16 +16095,16 @@ var MultiAgentHost = class {
|
|
|
15104
16095
|
if (this.director) return this.director;
|
|
15105
16096
|
this.opts.directorMode = true;
|
|
15106
16097
|
if (this.opts.fleetRoot && !this.opts.manifestPath) {
|
|
15107
|
-
this.opts.manifestPath =
|
|
16098
|
+
this.opts.manifestPath = path9.join(this.opts.fleetRoot, "fleet.json");
|
|
15108
16099
|
}
|
|
15109
16100
|
if (this.opts.fleetRoot && !this.opts.sharedScratchpadPath) {
|
|
15110
|
-
this.opts.sharedScratchpadPath =
|
|
16101
|
+
this.opts.sharedScratchpadPath = path9.join(this.opts.fleetRoot, "shared");
|
|
15111
16102
|
}
|
|
15112
16103
|
if (this.opts.fleetRoot && !this.opts.sessionsRoot) {
|
|
15113
|
-
this.opts.sessionsRoot =
|
|
16104
|
+
this.opts.sessionsRoot = path9.join(this.opts.fleetRoot, "subagents");
|
|
15114
16105
|
}
|
|
15115
16106
|
if (this.opts.fleetRoot && !this.opts.stateCheckpointPath) {
|
|
15116
|
-
this.opts.stateCheckpointPath =
|
|
16107
|
+
this.opts.stateCheckpointPath = path9.join(this.opts.fleetRoot, "director-state.json");
|
|
15117
16108
|
}
|
|
15118
16109
|
await this.ensureDirector();
|
|
15119
16110
|
return this.director ?? null;
|
|
@@ -15285,11 +16276,11 @@ var SessionStats = class {
|
|
|
15285
16276
|
if (e.name === "bash") this.bashCommands++;
|
|
15286
16277
|
else if (e.name === "fetch") this.fetches++;
|
|
15287
16278
|
if (!e.ok) return;
|
|
15288
|
-
const
|
|
15289
|
-
if (e.name === "read" &&
|
|
15290
|
-
else if (e.name === "edit" &&
|
|
15291
|
-
else if (e.name === "write" &&
|
|
15292
|
-
this.writtenPaths.add(
|
|
16279
|
+
const path27 = typeof input?.path === "string" ? input.path : void 0;
|
|
16280
|
+
if (e.name === "read" && path27) this.readPaths.add(path27);
|
|
16281
|
+
else if (e.name === "edit" && path27) this.editedPaths.add(path27);
|
|
16282
|
+
else if (e.name === "write" && path27) {
|
|
16283
|
+
this.writtenPaths.add(path27);
|
|
15293
16284
|
const content = typeof input?.content === "string" ? input.content : "";
|
|
15294
16285
|
this.bytesWritten += Buffer.byteLength(content, "utf8");
|
|
15295
16286
|
}
|
|
@@ -15956,9 +16947,13 @@ function parseModelRef(ref) {
|
|
|
15956
16947
|
}
|
|
15957
16948
|
return { model: trimmed };
|
|
15958
16949
|
}
|
|
15959
|
-
function
|
|
16950
|
+
function shouldFallback(err) {
|
|
16951
|
+
if (err instanceof StreamHangError) {
|
|
16952
|
+
return 599;
|
|
16953
|
+
}
|
|
15960
16954
|
if (!(err instanceof ProviderError)) return null;
|
|
15961
16955
|
const s = err.status;
|
|
16956
|
+
if (s === 0) return s;
|
|
15962
16957
|
if (s === 429 || s === 529 || s >= 500) return s;
|
|
15963
16958
|
return null;
|
|
15964
16959
|
}
|
|
@@ -15990,7 +16985,7 @@ function createFallbackModelExtension(deps) {
|
|
|
15990
16985
|
const cfg = deps.getConfig();
|
|
15991
16986
|
const chain = cfg.fallbackModels ?? [];
|
|
15992
16987
|
for (const ref of chain) {
|
|
15993
|
-
const status =
|
|
16988
|
+
const status = shouldFallback(lastErr);
|
|
15994
16989
|
if (status === null) break;
|
|
15995
16990
|
const parsed = parseModelRef(ref);
|
|
15996
16991
|
if (!parsed.model) continue;
|
|
@@ -16107,7 +17102,7 @@ function setupMetrics(params) {
|
|
|
16107
17102
|
const dumpMetrics = () => {
|
|
16108
17103
|
if (!metricsSink) return;
|
|
16109
17104
|
try {
|
|
16110
|
-
const out =
|
|
17105
|
+
const out = path9.join(wpaths.projectSessions, "metrics.json");
|
|
16111
17106
|
const snap = metricsSink.snapshot();
|
|
16112
17107
|
writeFileSync(out, JSON.stringify(snap, null, 2));
|
|
16113
17108
|
} catch {
|
|
@@ -16182,7 +17177,7 @@ async function setupCodebaseIndexing(deps) {
|
|
|
16182
17177
|
if (tool?.mutating && FILE_EDIT_TOOLS.has(tool.name)) {
|
|
16183
17178
|
const fp = payload.toolUse.input?.file_path;
|
|
16184
17179
|
if (typeof fp === "string" && fp.length > 0) {
|
|
16185
|
-
const abs =
|
|
17180
|
+
const abs = path9.resolve(payload.ctx.cwd, fp);
|
|
16186
17181
|
if (isIndexableFile(abs)) {
|
|
16187
17182
|
enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
|
|
16188
17183
|
}
|
|
@@ -16197,11 +17192,11 @@ async function setupCodebaseIndexing(deps) {
|
|
|
16197
17192
|
let watcher;
|
|
16198
17193
|
if (idx.watchExternal) {
|
|
16199
17194
|
try {
|
|
16200
|
-
watcher =
|
|
17195
|
+
watcher = fs14.watch(projectRoot, { recursive: true }, (_event, filename) => {
|
|
16201
17196
|
if (!filename) return;
|
|
16202
17197
|
const rel = filename.toString();
|
|
16203
17198
|
if (isIgnored(rel)) return;
|
|
16204
|
-
const abs =
|
|
17199
|
+
const abs = path9.resolve(projectRoot, rel);
|
|
16205
17200
|
if (!isIndexableFile(abs)) return;
|
|
16206
17201
|
enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
|
|
16207
17202
|
});
|
|
@@ -16490,9 +17485,9 @@ async function setupSession(params) {
|
|
|
16490
17485
|
const sessionRef = { current: session };
|
|
16491
17486
|
await recoveryLock.write(session?.id).catch(() => void 0);
|
|
16492
17487
|
const attachments = new DefaultAttachmentStore({
|
|
16493
|
-
spoolDir:
|
|
17488
|
+
spoolDir: path9.join(wpaths.projectSessions, session?.id, "attachments")
|
|
16494
17489
|
});
|
|
16495
|
-
const queueStore = new QueueStore({ dir:
|
|
17490
|
+
const queueStore = new QueueStore({ dir: path9.join(wpaths.projectSessions, session?.id) });
|
|
16496
17491
|
const ctxSignal = new AbortController().signal;
|
|
16497
17492
|
const context = new Context({
|
|
16498
17493
|
systemPrompt,
|
|
@@ -16505,7 +17500,7 @@ async function setupSession(params) {
|
|
|
16505
17500
|
model: config.model
|
|
16506
17501
|
});
|
|
16507
17502
|
if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
|
|
16508
|
-
const todosCheckpointPath =
|
|
17503
|
+
const todosCheckpointPath = path9.join(wpaths.projectSessions, `${session?.id}.todos.json`);
|
|
16509
17504
|
if (resumeId) {
|
|
16510
17505
|
try {
|
|
16511
17506
|
const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
|
|
@@ -16523,15 +17518,15 @@ async function setupSession(params) {
|
|
|
16523
17518
|
todosCheckpointPath,
|
|
16524
17519
|
session?.id
|
|
16525
17520
|
);
|
|
16526
|
-
const planPath =
|
|
17521
|
+
const planPath = path9.join(wpaths.projectSessions, `${session?.id}.plan.json`);
|
|
16527
17522
|
context.state.setMeta("plan.path", planPath);
|
|
16528
|
-
const taskPath =
|
|
17523
|
+
const taskPath = path9.join(wpaths.projectSessions, `${session?.id}.tasks.json`);
|
|
16529
17524
|
context.state.setMeta("task.path", taskPath);
|
|
16530
17525
|
let dirState;
|
|
16531
17526
|
if (resumeId) {
|
|
16532
17527
|
try {
|
|
16533
|
-
const fleetRoot =
|
|
16534
|
-
dirState = await loadDirectorState(
|
|
17528
|
+
const fleetRoot = path9.join(wpaths.projectSessions, session?.id);
|
|
17529
|
+
dirState = await loadDirectorState(path9.join(fleetRoot, "director-state.json"));
|
|
16535
17530
|
if (dirState) {
|
|
16536
17531
|
const tCounts = {};
|
|
16537
17532
|
for (const t of dirState.tasks) tCounts[t.status] = (tCounts[t.status] ?? 0) + 1;
|
|
@@ -16571,7 +17566,7 @@ function resolveBundledSkillsDir2() {
|
|
|
16571
17566
|
try {
|
|
16572
17567
|
const req2 = createRequire(import.meta.url);
|
|
16573
17568
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
16574
|
-
return
|
|
17569
|
+
return path9.join(path9.dirname(corePkg), "skills");
|
|
16575
17570
|
} catch {
|
|
16576
17571
|
return void 0;
|
|
16577
17572
|
}
|
|
@@ -16682,6 +17677,9 @@ async function main(argv) {
|
|
|
16682
17677
|
updateInfo
|
|
16683
17678
|
} = ctx;
|
|
16684
17679
|
updateInfo = await printUpdateNotice(updateInfo);
|
|
17680
|
+
const { setDebugStreamEnabled, setDebugStreamCallback, defaultDebugStreamCallback } = await import('@wrongstack/providers');
|
|
17681
|
+
if (config.debugStream) setDebugStreamEnabled(true);
|
|
17682
|
+
setDebugStreamCallback(defaultDebugStreamCallback);
|
|
16685
17683
|
const pathResolver = new DefaultPathResolver(cwd);
|
|
16686
17684
|
const events = new EventBus();
|
|
16687
17685
|
events.setLogger(logger);
|
|
@@ -16768,7 +17766,7 @@ async function main(argv) {
|
|
|
16768
17766
|
modeId,
|
|
16769
17767
|
modePrompt,
|
|
16770
17768
|
modelCapabilities,
|
|
16771
|
-
planPath: () => sessionRef.current ?
|
|
17769
|
+
planPath: () => sessionRef.current ? path9.join(wpaths.projectSessions, `${sessionRef.current.id}.plan.json`) : void 0,
|
|
16772
17770
|
contributors: [
|
|
16773
17771
|
// Injects the ETERNAL AUTONOMY block when the user has activated
|
|
16774
17772
|
// a long-running autonomy engine. Without this, the per-iteration
|
|
@@ -17158,12 +18156,12 @@ async function main(argv) {
|
|
|
17158
18156
|
}
|
|
17159
18157
|
}
|
|
17160
18158
|
};
|
|
17161
|
-
const fleetRoot = directorMode ?
|
|
17162
|
-
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] :
|
|
17163
|
-
const sharedScratchpadPath = directorMode ?
|
|
17164
|
-
const subagentSessionsRoot = directorMode ?
|
|
17165
|
-
const stateCheckpointPath = directorMode ?
|
|
17166
|
-
const fleetRootForPromotion =
|
|
18159
|
+
const fleetRoot = directorMode ? path9.join(wpaths.projectSessions, session.id) : void 0;
|
|
18160
|
+
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path9.join(expectDefined(fleetRoot), "fleet.json") : void 0;
|
|
18161
|
+
const sharedScratchpadPath = directorMode ? path9.join(expectDefined(fleetRoot), "shared") : void 0;
|
|
18162
|
+
const subagentSessionsRoot = directorMode ? path9.join(expectDefined(fleetRoot), "subagents") : void 0;
|
|
18163
|
+
const stateCheckpointPath = directorMode ? path9.join(expectDefined(fleetRoot), "director-state.json") : void 0;
|
|
18164
|
+
const fleetRootForPromotion = path9.join(wpaths.projectSessions, session.id);
|
|
17167
18165
|
const brainQueue = new BrainDecisionQueue(events);
|
|
17168
18166
|
const brain = new ObservableBrainArbiter(
|
|
17169
18167
|
new HumanEscalatingBrainArbiter(new DefaultBrainArbiter(), brainQueue),
|
|
@@ -17301,6 +18299,13 @@ async function main(argv) {
|
|
|
17301
18299
|
enhanceController,
|
|
17302
18300
|
llmProvider: provider,
|
|
17303
18301
|
llmModel: config.model,
|
|
18302
|
+
createProvider: (pid) => {
|
|
18303
|
+
try {
|
|
18304
|
+
return buildProviderForId(pid);
|
|
18305
|
+
} catch {
|
|
18306
|
+
return void 0;
|
|
18307
|
+
}
|
|
18308
|
+
},
|
|
17304
18309
|
statuslineConfig: statuslineConfigDeps,
|
|
17305
18310
|
statuslineHiddenItems: [...currentHiddenItems],
|
|
17306
18311
|
setStatuslineHiddenItems,
|
|
@@ -17521,7 +18526,7 @@ async function main(argv) {
|
|
|
17521
18526
|
return director.spawn(cfg);
|
|
17522
18527
|
},
|
|
17523
18528
|
onFleetLog: async (subagentId, mode) => {
|
|
17524
|
-
const subagentsRoot =
|
|
18529
|
+
const subagentsRoot = path9.join(fleetRootForPromotion, "subagents");
|
|
17525
18530
|
let runDirs;
|
|
17526
18531
|
try {
|
|
17527
18532
|
runDirs = await fsp4.readdir(subagentsRoot);
|
|
@@ -17530,7 +18535,7 @@ async function main(argv) {
|
|
|
17530
18535
|
}
|
|
17531
18536
|
const found = [];
|
|
17532
18537
|
for (const runId of runDirs) {
|
|
17533
|
-
const runDir =
|
|
18538
|
+
const runDir = path9.join(subagentsRoot, runId);
|
|
17534
18539
|
let files;
|
|
17535
18540
|
try {
|
|
17536
18541
|
files = await fsp4.readdir(runDir);
|
|
@@ -17539,7 +18544,7 @@ async function main(argv) {
|
|
|
17539
18544
|
}
|
|
17540
18545
|
for (const f of files) {
|
|
17541
18546
|
if (!f.endsWith(".jsonl")) continue;
|
|
17542
|
-
const full =
|
|
18547
|
+
const full = path9.join(runDir, f);
|
|
17543
18548
|
try {
|
|
17544
18549
|
const stat4 = await fsp4.stat(full);
|
|
17545
18550
|
found.push({
|
|
@@ -17640,7 +18645,7 @@ async function main(argv) {
|
|
|
17640
18645
|
}
|
|
17641
18646
|
const dir = await multiAgentHost.ensureDirector();
|
|
17642
18647
|
if (!dir) return "Director is not available.";
|
|
17643
|
-
const dirStatePath =
|
|
18648
|
+
const dirStatePath = path9.join(fleetRootForPromotion, "director-state.json");
|
|
17644
18649
|
const prior = await loadDirectorState(dirStatePath);
|
|
17645
18650
|
if (!prior) {
|
|
17646
18651
|
return "No prior director-state.json found \u2014 nothing to retry.";
|
|
@@ -17709,9 +18714,9 @@ async function main(argv) {
|
|
|
17709
18714
|
for (const tool of director2.tools(FLEET_ROSTER)) {
|
|
17710
18715
|
toolRegistry.register(tool);
|
|
17711
18716
|
}
|
|
17712
|
-
const mp =
|
|
17713
|
-
const sp =
|
|
17714
|
-
const ss =
|
|
18717
|
+
const mp = path9.join(fleetRootForPromotion, "fleet.json");
|
|
18718
|
+
const sp = path9.join(fleetRootForPromotion, "shared");
|
|
18719
|
+
const ss = path9.join(fleetRootForPromotion, "subagents");
|
|
17715
18720
|
const lines = [
|
|
17716
18721
|
`${color.green("\u2713")} Promoted to director mode.`,
|
|
17717
18722
|
` Roster: ${Object.keys(FLEET_ROSTER).join(", ")}`,
|