@wrongstack/cli 0.77.0 → 0.84.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/README.md +1 -2
- package/dist/index.js +518 -224
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { color, writeErr, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, renderTaskGraph, DefaultSecretScrubber,
|
|
2
|
+
import { color, writeErr, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, renderTaskGraph, DefaultSecretScrubber, DefaultPathResolver, TOKENS, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, SpecVersioning, atomicWrite, DefaultLogger, DefaultModelsRegistry, isStdinTTY, writeOut, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, 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, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, emptyGoal, buildGoalPreamble, formatGoal, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, AGENT_CATALOG, matrixKeyKind, onResize, ERROR_CODES, InputBuilder, FsError } from '@wrongstack/core';
|
|
3
3
|
import * as path8 from 'path';
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import * as fsp4 from 'fs/promises';
|
|
6
|
+
import { DefaultSecretVault, decryptConfigSecrets, encryptConfigSecrets, isSecretField } from '@wrongstack/core/security';
|
|
6
7
|
import { createRequire } from 'module';
|
|
7
8
|
import * as os2 from 'os';
|
|
8
9
|
import os2__default from 'os';
|
|
9
10
|
import * as crypto2 from 'crypto';
|
|
10
11
|
import { randomUUID } from 'crypto';
|
|
11
12
|
import { findFreePort, createHttpServer, openBrowser, registerInstance, unregisterInstance } from '@wrongstack/webui/server';
|
|
12
|
-
import { DefaultSecretVault, decryptConfigSecrets, encryptConfigSecrets, isSecretField } from '@wrongstack/core/security';
|
|
13
13
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
14
14
|
import { MCPRegistry, MCPServer, serveHttp, serveStdio } from '@wrongstack/mcp';
|
|
15
15
|
import { capabilitiesFor, buildProviderFactoriesFromRegistry, makeProviderFromConfig } from '@wrongstack/providers';
|
|
@@ -132,6 +132,12 @@ var init_state = __esm({
|
|
|
132
132
|
sddState = new SDDState();
|
|
133
133
|
}
|
|
134
134
|
});
|
|
135
|
+
function expectDefined3(value) {
|
|
136
|
+
if (value === null || value === void 0) {
|
|
137
|
+
throw new Error("Expected value to be defined");
|
|
138
|
+
}
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
135
141
|
function formatElapsed(ms) {
|
|
136
142
|
if (ms < 1e3) return `${ms}ms`;
|
|
137
143
|
const s = Math.floor(ms / 1e3);
|
|
@@ -195,7 +201,7 @@ function getCurrentTask() {
|
|
|
195
201
|
if (!tracker) return null;
|
|
196
202
|
const nodes = tracker.getAllNodes({ status: ["in_progress"] });
|
|
197
203
|
if (nodes.length === 0) return null;
|
|
198
|
-
const n = nodes[0];
|
|
204
|
+
const n = expectDefined3(nodes[0]);
|
|
199
205
|
return { id: n.id, title: n.title, description: n.description, priority: n.priority, estimateHours: n.estimateHours ?? 0, tags: n.tags ?? [], startedAt: n.startedAt };
|
|
200
206
|
}
|
|
201
207
|
function advanceToNextTask() {
|
|
@@ -234,7 +240,7 @@ function renderTaskListWithProgress() {
|
|
|
234
240
|
return (order[a.status] ?? 6) - (order[b.status] ?? 6);
|
|
235
241
|
});
|
|
236
242
|
for (let i = 0; i < sorted.length; i++) {
|
|
237
|
-
const n = sorted[i];
|
|
243
|
+
const n = expectDefined3(sorted[i]);
|
|
238
244
|
const status = n.status === "completed" ? "\u2705" : n.status === "in_progress" ? "\u{1F504}" : n.status === "failed" ? "\u274C" : n.status === "blocked" ? "\u{1F6AB}" : n.status === "review" ? "\u{1F441}" : "\u23F3";
|
|
239
245
|
const title = n.title.length > 50 ? n.title.slice(0, 49) + "\u2026" : n.title;
|
|
240
246
|
let elapsed = "";
|
|
@@ -248,7 +254,7 @@ function getCurrentExecutingContext() {
|
|
|
248
254
|
if (!tracker) return null;
|
|
249
255
|
const nodes = tracker.getAllNodes({ status: ["in_progress"] });
|
|
250
256
|
if (nodes.length === 0) return null;
|
|
251
|
-
const n = nodes[0];
|
|
257
|
+
const n = expectDefined3(nodes[0]);
|
|
252
258
|
const elapsed = n.startedAt ? ` \xB7 elapsed: ${formatElapsed(Date.now() - n.startedAt)}` : "";
|
|
253
259
|
const progress = tracker.getProgress();
|
|
254
260
|
return [
|
|
@@ -556,6 +562,12 @@ __export(sdd_exports, {
|
|
|
556
562
|
trySaveSpecFromAIOutput: () => trySaveSpecFromAIOutput,
|
|
557
563
|
trySaveTasksFromAIOutput: () => trySaveTasksFromAIOutput
|
|
558
564
|
});
|
|
565
|
+
function expectDefined4(value) {
|
|
566
|
+
if (value === null || value === void 0) {
|
|
567
|
+
throw new Error("Expected value to be defined");
|
|
568
|
+
}
|
|
569
|
+
return value;
|
|
570
|
+
}
|
|
559
571
|
function getTaskTracker() {
|
|
560
572
|
return getTaskTrackerExport();
|
|
561
573
|
}
|
|
@@ -563,6 +575,7 @@ function buildSddCommand(opts) {
|
|
|
563
575
|
const sessionState = getSessionState(opts.context);
|
|
564
576
|
return {
|
|
565
577
|
name: "sdd",
|
|
578
|
+
category: "Agent",
|
|
566
579
|
description: "AI-driven SDD: /sdd [new|approve|execute|cancel|status|list|show|templates]",
|
|
567
580
|
async run(args) {
|
|
568
581
|
if (!opts.paths) return { message: "SDD not available \u2014 paths not configured." };
|
|
@@ -620,7 +633,7 @@ function buildSddCommand(opts) {
|
|
|
620
633
|
}));
|
|
621
634
|
sddState.setSessionStartTime(Date.now());
|
|
622
635
|
sddState.setPhaseStartTime(Date.now());
|
|
623
|
-
const builder = sddState.getBuilder();
|
|
636
|
+
const builder = expectDefined4(sddState.getBuilder());
|
|
624
637
|
builder.startSession(title);
|
|
625
638
|
const aiPrompt = builder.getAIPrompt();
|
|
626
639
|
return {
|
|
@@ -862,7 +875,7 @@ Start executing the tasks one by one.`
|
|
|
862
875
|
return (order[a.status] ?? 6) - (order[b.status] ?? 6);
|
|
863
876
|
});
|
|
864
877
|
for (let i = 0; i < sorted.length; i++) {
|
|
865
|
-
const n = sorted[i];
|
|
878
|
+
const n = expectDefined4(sorted[i]);
|
|
866
879
|
const status = n.status === "completed" ? "\u2705" : n.status === "in_progress" ? "\u{1F504}" : n.status === "failed" ? "\u274C" : n.status === "blocked" ? "\u{1F6AB}" : n.status === "review" ? "\u{1F441}" : "\u23F3";
|
|
867
880
|
const num = `${i + 1}`.padStart(3);
|
|
868
881
|
const prio = n.priority.slice(0, 4).padEnd(5);
|
|
@@ -870,7 +883,7 @@ Start executing the tasks one by one.`
|
|
|
870
883
|
const elapsed = n.status === "in_progress" && n.startedAt ? ` (${formatElapsed(Date.now() - n.startedAt)})` : "";
|
|
871
884
|
lines.push(` ${num} ${status} ${prio} ${title}${elapsed}`);
|
|
872
885
|
if (n.description && n.status !== "completed") {
|
|
873
|
-
const first = n.description.split("\n")[0];
|
|
886
|
+
const first = expectDefined4(n.description.split("\n")[0]);
|
|
874
887
|
const truncated = first.length > 42 ? first.slice(0, 41) + "\u2026" : first;
|
|
875
888
|
lines.push(` \u21B3 ${truncated}`);
|
|
876
889
|
}
|
|
@@ -1037,7 +1050,7 @@ Start executing the tasks one by one.`
|
|
|
1037
1050
|
if (completed.length === 0) {
|
|
1038
1051
|
return { message: "No completed tasks to undo." };
|
|
1039
1052
|
}
|
|
1040
|
-
const last = completed[completed.length - 1];
|
|
1053
|
+
const last = expectDefined4(completed[completed.length - 1]);
|
|
1041
1054
|
undoTracker.updateNodeStatus(last.id, "pending");
|
|
1042
1055
|
const progress = undoTracker.getProgress();
|
|
1043
1056
|
return {
|
|
@@ -1087,7 +1100,7 @@ Start executing the tasks one by one.`
|
|
|
1087
1100
|
` \u{1F504} ${next.title}`
|
|
1088
1101
|
];
|
|
1089
1102
|
if (next.description) {
|
|
1090
|
-
const first = next.description.split("\n")[0];
|
|
1103
|
+
const first = expectDefined4(next.description.split("\n")[0]);
|
|
1091
1104
|
lines.push(` \u21B3 ${first}`);
|
|
1092
1105
|
}
|
|
1093
1106
|
const taskElapsed = next.startedAt ? ` \u23F1 ${formatElapsed(Date.now() - next.startedAt)}` : "";
|
|
@@ -1199,7 +1212,7 @@ Start executing the tasks one by one.`
|
|
|
1199
1212
|
return (order[a.status] ?? 6) - (order[b.status] ?? 6);
|
|
1200
1213
|
});
|
|
1201
1214
|
for (let i = 0; i < sorted2.length; i++) {
|
|
1202
|
-
const n = sorted2[i];
|
|
1215
|
+
const n = expectDefined4(sorted2[i]);
|
|
1203
1216
|
const status = n.status === "completed" ? "\u2705" : n.status === "in_progress" ? "\u{1F504}" : n.status === "failed" ? "\u274C" : n.status === "blocked" ? "\u{1F6AB}" : n.status === "review" ? "\u{1F441}" : "\u23F3";
|
|
1204
1217
|
lines2.push(`${i + 1}. ${status} [${n.priority}] ${n.title}`);
|
|
1205
1218
|
}
|
|
@@ -1224,7 +1237,7 @@ Start executing the tasks one by one.`
|
|
|
1224
1237
|
return (order[a.status] ?? 6) - (order[b.status] ?? 6);
|
|
1225
1238
|
});
|
|
1226
1239
|
for (let i = 0; i < sorted.length; i++) {
|
|
1227
|
-
const n = sorted[i];
|
|
1240
|
+
const n = expectDefined4(sorted[i]);
|
|
1228
1241
|
const status = n.status === "completed" ? "\u2705" : n.status === "in_progress" ? "\u{1F504}" : n.status === "failed" ? "\u274C" : n.status === "blocked" ? "\u{1F6AB}" : n.status === "review" ? "\u{1F441}" : "\u23F3";
|
|
1229
1242
|
lines.push(`${i + 1}. ${status} [${n.priority}] ${n.title}`);
|
|
1230
1243
|
}
|
|
@@ -1272,7 +1285,7 @@ Start executing the tasks one by one.`
|
|
|
1272
1285
|
maxQuestions: 10,
|
|
1273
1286
|
sessionPath
|
|
1274
1287
|
}));
|
|
1275
|
-
const resumeBuilder = sddState.getBuilder();
|
|
1288
|
+
const resumeBuilder = expectDefined4(sddState.getBuilder());
|
|
1276
1289
|
const loaded = await resumeBuilder.loadSession();
|
|
1277
1290
|
if (!loaded) {
|
|
1278
1291
|
sddState.setBuilder(null);
|
|
@@ -1506,6 +1519,12 @@ var init_sdd = __esm({
|
|
|
1506
1519
|
init_rendering();
|
|
1507
1520
|
}
|
|
1508
1521
|
});
|
|
1522
|
+
function expectDefined7(value) {
|
|
1523
|
+
if (value === null || value === void 0) {
|
|
1524
|
+
throw new Error("Expected value to be defined");
|
|
1525
|
+
}
|
|
1526
|
+
return value;
|
|
1527
|
+
}
|
|
1509
1528
|
function normalizeKeys(cfg) {
|
|
1510
1529
|
if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
|
|
1511
1530
|
return cfg.apiKeys.map((k) => ({ ...k }));
|
|
@@ -1523,7 +1542,7 @@ function writeKeysBack(cfg, keys) {
|
|
|
1523
1542
|
return;
|
|
1524
1543
|
}
|
|
1525
1544
|
cfg.apiKeys = keys;
|
|
1526
|
-
const active = keys.find((k) => k.label === cfg.activeKey) ?? keys[0];
|
|
1545
|
+
const active = keys.find((k) => k.label === cfg.activeKey) ?? expectDefined7(keys[0]);
|
|
1527
1546
|
cfg.apiKey = active.apiKey;
|
|
1528
1547
|
if (!cfg.activeKey || !keys.some((k) => k.label === cfg.activeKey)) {
|
|
1529
1548
|
cfg.activeKey = active.label;
|
|
@@ -1543,6 +1562,61 @@ function maskedKey(key) {
|
|
|
1543
1562
|
function nowIso() {
|
|
1544
1563
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1545
1564
|
}
|
|
1565
|
+
async function loadConfigProviders(configPath2, vault, opts) {
|
|
1566
|
+
const warn = opts?.warn;
|
|
1567
|
+
let raw;
|
|
1568
|
+
try {
|
|
1569
|
+
raw = await fsp4.readFile(configPath2, "utf8");
|
|
1570
|
+
} catch (err) {
|
|
1571
|
+
if (err.code !== "ENOENT") {
|
|
1572
|
+
warn?.(`Could not read ${configPath2}: ${err.message}. Treating as empty.`);
|
|
1573
|
+
}
|
|
1574
|
+
return {};
|
|
1575
|
+
}
|
|
1576
|
+
let parsed;
|
|
1577
|
+
try {
|
|
1578
|
+
parsed = JSON.parse(raw);
|
|
1579
|
+
} catch (err) {
|
|
1580
|
+
warn?.(`Config at ${configPath2} is not valid JSON: ${err.message}`);
|
|
1581
|
+
return {};
|
|
1582
|
+
}
|
|
1583
|
+
const decrypted = decryptConfigSecrets(parsed, vault);
|
|
1584
|
+
return decrypted.providers ?? {};
|
|
1585
|
+
}
|
|
1586
|
+
async function mutateConfigProviders(configPath2, vault, mutator) {
|
|
1587
|
+
let raw;
|
|
1588
|
+
let fileExists = true;
|
|
1589
|
+
try {
|
|
1590
|
+
raw = await fsp4.readFile(configPath2, "utf8");
|
|
1591
|
+
} catch (err) {
|
|
1592
|
+
if (err.code !== "ENOENT") {
|
|
1593
|
+
throw new Error(
|
|
1594
|
+
`Refusing to mutate ${configPath2}: ${err.message}`,
|
|
1595
|
+
{ cause: err }
|
|
1596
|
+
);
|
|
1597
|
+
}
|
|
1598
|
+
fileExists = false;
|
|
1599
|
+
raw = "{}";
|
|
1600
|
+
}
|
|
1601
|
+
let parsed;
|
|
1602
|
+
try {
|
|
1603
|
+
parsed = JSON.parse(raw);
|
|
1604
|
+
} catch (err) {
|
|
1605
|
+
if (fileExists) {
|
|
1606
|
+
throw new Error(
|
|
1607
|
+
`Refusing to overwrite corrupt config at ${configPath2} (${err.message}). Fix or move the file aside before retrying.`,
|
|
1608
|
+
{ cause: err }
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1611
|
+
parsed = {};
|
|
1612
|
+
}
|
|
1613
|
+
const decrypted = decryptConfigSecrets(parsed, vault);
|
|
1614
|
+
const providers = decrypted.providers ?? {};
|
|
1615
|
+
mutator(providers);
|
|
1616
|
+
decrypted.providers = providers;
|
|
1617
|
+
const encrypted = encryptConfigSecrets(decrypted, vault);
|
|
1618
|
+
await atomicWrite(configPath2, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
1619
|
+
}
|
|
1546
1620
|
var init_provider_config_utils = __esm({
|
|
1547
1621
|
"src/provider-config-utils.ts"() {
|
|
1548
1622
|
}
|
|
@@ -2263,7 +2337,7 @@ async function runWebUI(opts) {
|
|
|
2263
2337
|
const keys = normalizeKeys(existing);
|
|
2264
2338
|
const existingIdx = keys.findIndex((k) => k.label === label);
|
|
2265
2339
|
if (existingIdx >= 0) {
|
|
2266
|
-
keys[existingIdx] = { ...keys[existingIdx], apiKey, createdAt: nowIso() };
|
|
2340
|
+
keys[existingIdx] = { ...expectDefined7(keys[existingIdx]), apiKey, createdAt: nowIso() };
|
|
2267
2341
|
} else {
|
|
2268
2342
|
keys.push({ label, apiKey, createdAt: nowIso() });
|
|
2269
2343
|
}
|
|
@@ -2290,7 +2364,7 @@ async function runWebUI(opts) {
|
|
|
2290
2364
|
} else {
|
|
2291
2365
|
writeKeysBack(existing, keys);
|
|
2292
2366
|
if (existing.activeKey === label) {
|
|
2293
|
-
existing.activeKey = keys[0]
|
|
2367
|
+
existing.activeKey = keys[0]?.label;
|
|
2294
2368
|
}
|
|
2295
2369
|
providers[providerId] = existing;
|
|
2296
2370
|
}
|
|
@@ -2354,58 +2428,20 @@ async function runWebUI(opts) {
|
|
|
2354
2428
|
sendResult(ws, false, err instanceof Error ? err.message : String(err));
|
|
2355
2429
|
}
|
|
2356
2430
|
}
|
|
2431
|
+
function getVault() {
|
|
2432
|
+
const keyFile = path8.join(path8.dirname(opts.globalConfigPath ?? ""), ".key");
|
|
2433
|
+
return new DefaultSecretVault({ keyFile });
|
|
2434
|
+
}
|
|
2357
2435
|
async function loadSavedProviders() {
|
|
2358
2436
|
if (!opts.globalConfigPath) return {};
|
|
2359
|
-
|
|
2360
|
-
try {
|
|
2361
|
-
raw = await fsp4.readFile(opts.globalConfigPath, "utf8");
|
|
2362
|
-
} catch {
|
|
2363
|
-
return {};
|
|
2364
|
-
}
|
|
2365
|
-
let parsed = {};
|
|
2366
|
-
try {
|
|
2367
|
-
parsed = JSON.parse(raw);
|
|
2368
|
-
} catch {
|
|
2369
|
-
return {};
|
|
2370
|
-
}
|
|
2371
|
-
if (!parsed.providers) return {};
|
|
2372
|
-
const keyFile = path8.join(path8.dirname(opts.globalConfigPath), ".key");
|
|
2373
|
-
const vault = new DefaultSecretVault({ keyFile });
|
|
2374
|
-
return decryptConfigSecrets(parsed.providers, vault);
|
|
2437
|
+
return loadConfigProviders(opts.globalConfigPath, getVault());
|
|
2375
2438
|
}
|
|
2376
2439
|
async function saveProviders(providers) {
|
|
2377
2440
|
if (!opts.globalConfigPath) return;
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
} catch (err) {
|
|
2383
|
-
if (err.code !== "ENOENT") {
|
|
2384
|
-
throw new Error(
|
|
2385
|
-
`Refusing to mutate ${opts.globalConfigPath}: ${err.message}`,
|
|
2386
|
-
{ cause: err }
|
|
2387
|
-
);
|
|
2388
|
-
}
|
|
2389
|
-
fileExists = false;
|
|
2390
|
-
raw = "{}";
|
|
2391
|
-
}
|
|
2392
|
-
let parsed;
|
|
2393
|
-
try {
|
|
2394
|
-
parsed = JSON.parse(raw);
|
|
2395
|
-
} catch (err) {
|
|
2396
|
-
if (fileExists) {
|
|
2397
|
-
throw new Error(
|
|
2398
|
-
`Refusing to overwrite corrupt config at ${opts.globalConfigPath} (${err.message}). Fix or move the file aside before retrying.`,
|
|
2399
|
-
{ cause: err }
|
|
2400
|
-
);
|
|
2401
|
-
}
|
|
2402
|
-
parsed = {};
|
|
2403
|
-
}
|
|
2404
|
-
parsed.providers = providers;
|
|
2405
|
-
const keyFile = path8.join(path8.dirname(opts.globalConfigPath), ".key");
|
|
2406
|
-
const vault = new DefaultSecretVault({ keyFile });
|
|
2407
|
-
const encrypted = encryptConfigSecrets(parsed, vault);
|
|
2408
|
-
await atomicWrite(opts.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
2441
|
+
await mutateConfigProviders(opts.globalConfigPath, getVault(), (existing) => {
|
|
2442
|
+
for (const key of Object.keys(existing)) delete existing[key];
|
|
2443
|
+
Object.assign(existing, providers);
|
|
2444
|
+
});
|
|
2409
2445
|
}
|
|
2410
2446
|
function sendResult(ws, success, message) {
|
|
2411
2447
|
send(ws, { type: "key.operation_result", payload: { success, message } });
|
|
@@ -2437,6 +2473,12 @@ try {
|
|
|
2437
2473
|
}
|
|
2438
2474
|
|
|
2439
2475
|
// src/slash-commands/commit-llm.ts
|
|
2476
|
+
function expectDefined(value) {
|
|
2477
|
+
if (value === null || value === void 0) {
|
|
2478
|
+
throw new Error("Expected value to be defined");
|
|
2479
|
+
}
|
|
2480
|
+
return value;
|
|
2481
|
+
}
|
|
2440
2482
|
async function generateCommitMessageWithLLM(diff, opts) {
|
|
2441
2483
|
const systemPrompt = "You are a helpful assistant that generates concise, conventional-commit-formatted git commit messages. Analyze the provided diff and output ONLY the commit message (no explanation, no quotes). Format: <type>(<scope>): <short description> \u2014 <type> is one of: feat, fix, docs, style, refactor, test, chore, perf, ci, build, temp. If the diff contains multiple unrelated changes, pick the most important one. Keep the description under 72 characters. Example: feat(cli): add /commit LLM integration";
|
|
2442
2484
|
const userPrompt = `Here is the git diff:
|
|
@@ -2458,7 +2500,7 @@ ${diff}`;
|
|
|
2458
2500
|
clearTimeout(timeout);
|
|
2459
2501
|
const rawContent = resp.content;
|
|
2460
2502
|
const text = Array.isArray(rawContent) ? rawContent[0]?.text ?? "" : typeof rawContent === "object" && rawContent !== null ? rawContent.text ?? "" : String(rawContent);
|
|
2461
|
-
const message = text.trim().split("\n")[0];
|
|
2503
|
+
const message = expectDefined(text.trim().split("\n")[0]);
|
|
2462
2504
|
if (message.length > 0 && message.length < 200) {
|
|
2463
2505
|
return message;
|
|
2464
2506
|
}
|
|
@@ -2564,8 +2606,6 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
2564
2606
|
"no-tui",
|
|
2565
2607
|
"no-recovery",
|
|
2566
2608
|
"recover",
|
|
2567
|
-
"no-alt-screen",
|
|
2568
|
-
"alt-screen",
|
|
2569
2609
|
"output-json",
|
|
2570
2610
|
"prompt",
|
|
2571
2611
|
"metrics",
|
|
@@ -2665,7 +2705,7 @@ function parseSpawnFlags(input) {
|
|
|
2665
2705
|
else {
|
|
2666
2706
|
m = consume(/^--tools=(\S+)\s*/);
|
|
2667
2707
|
if (m)
|
|
2668
|
-
opts.tools = m[1]
|
|
2708
|
+
opts.tools = m[1]?.split(",").map((t) => t.trim()).filter(Boolean);
|
|
2669
2709
|
else {
|
|
2670
2710
|
m = consume(/^-p\s+(\S+)\s*/);
|
|
2671
2711
|
if (m) opts.provider = m[1];
|
|
@@ -2888,6 +2928,7 @@ function estimateTokens(messages) {
|
|
|
2888
2928
|
function buildAutonomyCommand(opts) {
|
|
2889
2929
|
return {
|
|
2890
2930
|
name: "autonomy",
|
|
2931
|
+
category: "Agent",
|
|
2891
2932
|
description: "Toggle or query autonomy mode (self-driving agent).",
|
|
2892
2933
|
help: [
|
|
2893
2934
|
"Usage:",
|
|
@@ -3146,6 +3187,7 @@ async function gatherProjectContext(projectRoot) {
|
|
|
3146
3187
|
function buildAutoPhaseCommand(opts) {
|
|
3147
3188
|
return {
|
|
3148
3189
|
name: "autophase",
|
|
3190
|
+
category: "Agent",
|
|
3149
3191
|
description: "Autonomous phase-based workflow \u2014 plans a project into phases of todos and builds it with the LLM.",
|
|
3150
3192
|
help: [
|
|
3151
3193
|
"Usage:",
|
|
@@ -3253,6 +3295,7 @@ function buildAutoPhaseCommand(opts) {
|
|
|
3253
3295
|
function buildBtwCommand(opts) {
|
|
3254
3296
|
return {
|
|
3255
3297
|
name: "btw",
|
|
3298
|
+
category: "Agent",
|
|
3256
3299
|
description: 'Drop a "by the way" note for the running agent without interrupting it \u2014 delivered at the next step',
|
|
3257
3300
|
argsHint: "<note>",
|
|
3258
3301
|
help: [
|
|
@@ -3287,6 +3330,7 @@ function buildBtwCommand(opts) {
|
|
|
3287
3330
|
function buildClearCommand(opts) {
|
|
3288
3331
|
return {
|
|
3289
3332
|
name: "clear",
|
|
3333
|
+
category: "Session",
|
|
3290
3334
|
description: "Reset the session and start a new one.",
|
|
3291
3335
|
help: [
|
|
3292
3336
|
"Usage:",
|
|
@@ -3323,6 +3367,7 @@ function buildClearCommand(opts) {
|
|
|
3323
3367
|
function buildCodebaseReindexCommand(opts) {
|
|
3324
3368
|
return {
|
|
3325
3369
|
name: "codebase-reindex",
|
|
3370
|
+
category: "Inspect",
|
|
3326
3371
|
aliases: ["reindex"],
|
|
3327
3372
|
description: "Rebuild the codebase symbol index used by codebase-search.",
|
|
3328
3373
|
argsHint: "[force]",
|
|
@@ -3354,6 +3399,7 @@ ${color.yellow(` ${r.errors.length} file(s) had errors`)}` : "");
|
|
|
3354
3399
|
function buildCollabCommand(opts) {
|
|
3355
3400
|
return {
|
|
3356
3401
|
name: "collab",
|
|
3402
|
+
category: "Agent",
|
|
3357
3403
|
description: "Live collaboration helpers (status / invite / history).",
|
|
3358
3404
|
async run(args, ctx) {
|
|
3359
3405
|
const parts = args.split(/\s+/).filter(Boolean);
|
|
@@ -3545,6 +3591,7 @@ function helpCommand() {
|
|
|
3545
3591
|
function buildCompactCommand(opts) {
|
|
3546
3592
|
return {
|
|
3547
3593
|
name: "compact",
|
|
3594
|
+
category: "Session",
|
|
3548
3595
|
description: "Compact the context window.",
|
|
3549
3596
|
help: [
|
|
3550
3597
|
"Usage:",
|
|
@@ -3572,6 +3619,7 @@ function buildCompactCommand(opts) {
|
|
|
3572
3619
|
function buildContextCommand(opts) {
|
|
3573
3620
|
return {
|
|
3574
3621
|
name: "context",
|
|
3622
|
+
category: "Inspect",
|
|
3575
3623
|
aliases: ["ctx"],
|
|
3576
3624
|
description: "Show context window summary.",
|
|
3577
3625
|
help: [
|
|
@@ -3818,6 +3866,7 @@ function pct(n) {
|
|
|
3818
3866
|
function buildDiagCommand(opts) {
|
|
3819
3867
|
return {
|
|
3820
3868
|
name: "diag",
|
|
3869
|
+
category: "Inspect",
|
|
3821
3870
|
description: "Show runtime diagnostics (provider, tokens, tools, MCP).",
|
|
3822
3871
|
async run() {
|
|
3823
3872
|
if (!opts.onDiag) return { message: "Diag not available in this context." };
|
|
@@ -3828,6 +3877,7 @@ function buildDiagCommand(opts) {
|
|
|
3828
3877
|
function buildStatsCommand(opts) {
|
|
3829
3878
|
return {
|
|
3830
3879
|
name: "stats",
|
|
3880
|
+
category: "Inspect",
|
|
3831
3881
|
description: "Show session report: tokens, requests, tools, files, cost.",
|
|
3832
3882
|
async run() {
|
|
3833
3883
|
if (!opts.onStats) return { message: "Stats not available in this context." };
|
|
@@ -3865,6 +3915,33 @@ async function persistAutonomySetting(deps, mutator) {
|
|
|
3865
3915
|
await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
3866
3916
|
deps.configStore.update({ autonomy: decrypted.autonomy });
|
|
3867
3917
|
}
|
|
3918
|
+
async function persistConfigSetting(deps, mutator) {
|
|
3919
|
+
let raw;
|
|
3920
|
+
let fileExists = true;
|
|
3921
|
+
try {
|
|
3922
|
+
raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
|
|
3923
|
+
} catch (err) {
|
|
3924
|
+
if (err.code !== "ENOENT") {
|
|
3925
|
+
throw new Error(`Could not read ${deps.globalConfigPath}: ${err.message}`);
|
|
3926
|
+
}
|
|
3927
|
+
fileExists = false;
|
|
3928
|
+
raw = "{}";
|
|
3929
|
+
}
|
|
3930
|
+
let parsed;
|
|
3931
|
+
try {
|
|
3932
|
+
parsed = JSON.parse(raw);
|
|
3933
|
+
} catch (err) {
|
|
3934
|
+
if (fileExists) {
|
|
3935
|
+
throw new Error(`Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`);
|
|
3936
|
+
}
|
|
3937
|
+
parsed = {};
|
|
3938
|
+
}
|
|
3939
|
+
const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
|
|
3940
|
+
mutator(decrypted);
|
|
3941
|
+
const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
|
|
3942
|
+
await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
3943
|
+
deps.configStore.update(decrypted);
|
|
3944
|
+
}
|
|
3868
3945
|
async function persistTelegramConfig(deps, mutator) {
|
|
3869
3946
|
let raw;
|
|
3870
3947
|
let fileExists = true;
|
|
@@ -3907,6 +3984,7 @@ function buildEnhanceCommand(opts) {
|
|
|
3907
3984
|
const controller = opts.enhanceController;
|
|
3908
3985
|
return {
|
|
3909
3986
|
name: "enhance",
|
|
3987
|
+
category: "Config",
|
|
3910
3988
|
description: 'Toggle prompt refinement ("did you mean this?") before sending.',
|
|
3911
3989
|
help: [
|
|
3912
3990
|
"Usage:",
|
|
@@ -4635,6 +4713,7 @@ function categoryLabel(cli) {
|
|
|
4635
4713
|
function buildFixCommand(opts) {
|
|
4636
4714
|
return {
|
|
4637
4715
|
name: "fix",
|
|
4716
|
+
category: "Agent",
|
|
4638
4717
|
description: "Classify a bug/error (any language), activate the right skill, and fix it \u2014 inline or via subagent.",
|
|
4639
4718
|
argsHint: "<error message or problem description>",
|
|
4640
4719
|
help: `
|
|
@@ -4800,6 +4879,7 @@ var PHASE_ORDER = [
|
|
|
4800
4879
|
function buildFleetCommand(opts) {
|
|
4801
4880
|
return {
|
|
4802
4881
|
name: "fleet",
|
|
4882
|
+
category: "Agent",
|
|
4803
4883
|
description: "Inspect and control the agent fleet (subagents, parallel slots).",
|
|
4804
4884
|
help: [
|
|
4805
4885
|
"Usage:",
|
|
@@ -5104,6 +5184,7 @@ var KNOWN_VERBS = /* @__PURE__ */ new Set([
|
|
|
5104
5184
|
function buildGoalCommand(opts) {
|
|
5105
5185
|
return {
|
|
5106
5186
|
name: "goal",
|
|
5187
|
+
category: "Agent",
|
|
5107
5188
|
description: "Set, inspect, or clear the long-running autonomous mission used by /autonomy eternal.",
|
|
5108
5189
|
help: [
|
|
5109
5190
|
"Usage:",
|
|
@@ -5257,6 +5338,7 @@ ${lines.join("\n")}`;
|
|
|
5257
5338
|
function buildHelpCommand(opts) {
|
|
5258
5339
|
return {
|
|
5259
5340
|
name: "help",
|
|
5341
|
+
category: "App",
|
|
5260
5342
|
description: "Show available slash commands. Pass a name for detailed help.",
|
|
5261
5343
|
help: [
|
|
5262
5344
|
"Usage:",
|
|
@@ -5317,6 +5399,7 @@ function buildHelpCommand(opts) {
|
|
|
5317
5399
|
function buildInitCommand(opts) {
|
|
5318
5400
|
return {
|
|
5319
5401
|
name: "init",
|
|
5402
|
+
category: "Config",
|
|
5320
5403
|
description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
|
|
5321
5404
|
async run(_args, ctx) {
|
|
5322
5405
|
const dir = path8.join(ctx.projectRoot, ".wrongstack");
|
|
@@ -5341,11 +5424,17 @@ No project type auto-detected. Edit the file with project context and instructio
|
|
|
5341
5424
|
}
|
|
5342
5425
|
};
|
|
5343
5426
|
}
|
|
5427
|
+
function expectDefined2(value) {
|
|
5428
|
+
if (value === null || value === void 0) {
|
|
5429
|
+
throw new Error("Expected value to be defined");
|
|
5430
|
+
}
|
|
5431
|
+
return value;
|
|
5432
|
+
}
|
|
5344
5433
|
function parseMcpArgs(args) {
|
|
5345
5434
|
const trimmed = args.trim();
|
|
5346
5435
|
if (!trimmed || trimmed === "list") return { action: "list", name: "" };
|
|
5347
5436
|
const parts = trimmed.split(/\s+/);
|
|
5348
|
-
const action = parts[0];
|
|
5437
|
+
const action = expectDefined2(parts[0]);
|
|
5349
5438
|
const name = parts[1] ?? "";
|
|
5350
5439
|
const enable = parts.includes("--enable") || parts.includes("-e");
|
|
5351
5440
|
switch (action) {
|
|
@@ -5483,7 +5572,7 @@ async function runEnable(name, configured, configPath2, mcpRegistry) {
|
|
|
5483
5572
|
const mcpServers = {
|
|
5484
5573
|
...full.mcpServers ?? {}
|
|
5485
5574
|
};
|
|
5486
|
-
mcpServers[name] = { ...mcpServers[name], enabled: true };
|
|
5575
|
+
mcpServers[name] = { ...cfg, ...mcpServers[name] ?? {}, enabled: true };
|
|
5487
5576
|
full.mcpServers = mcpServers;
|
|
5488
5577
|
await writeConfig(configPath2, full);
|
|
5489
5578
|
try {
|
|
@@ -5502,7 +5591,7 @@ async function runDisable(name, configured, configPath2, mcpRegistry) {
|
|
|
5502
5591
|
const mcpServers = {
|
|
5503
5592
|
...full.mcpServers ?? {}
|
|
5504
5593
|
};
|
|
5505
|
-
mcpServers[name] = { ...mcpServers[name], enabled: false };
|
|
5594
|
+
mcpServers[name] = { ...cfg, ...mcpServers[name] ?? {}, enabled: false };
|
|
5506
5595
|
full.mcpServers = mcpServers;
|
|
5507
5596
|
await writeConfig(configPath2, full);
|
|
5508
5597
|
return `${color.yellow("Disabled")} "${name}" and stopped.`;
|
|
@@ -5556,6 +5645,7 @@ async function writeConfig(path26, cfg) {
|
|
|
5556
5645
|
function buildMcpSlashCommand(opts) {
|
|
5557
5646
|
return {
|
|
5558
5647
|
name: "mcp",
|
|
5648
|
+
category: "Config",
|
|
5559
5649
|
description: "Manage MCP servers: /mcp [list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
|
|
5560
5650
|
aliases: ["mcp-servers"],
|
|
5561
5651
|
argsHint: "[list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
|
|
@@ -5590,6 +5680,7 @@ function buildMcpSlashCommand(opts) {
|
|
|
5590
5680
|
function buildMemoryCommand(opts) {
|
|
5591
5681
|
return {
|
|
5592
5682
|
name: "memory",
|
|
5683
|
+
category: "Inspect",
|
|
5593
5684
|
description: "Inspect or edit persistent memory: /memory [show|remember <text>|forget <query>|clear]",
|
|
5594
5685
|
async run(args) {
|
|
5595
5686
|
const store = opts.memoryStore;
|
|
@@ -5643,8 +5734,7 @@ ${color.bold(color.amber("WrongStack") + color.dim(" \u2014 Mode Selection"))}
|
|
|
5643
5734
|
`);
|
|
5644
5735
|
lines.push(color.dim(" \u2191\u2193 navigate Enter select q quit\n"));
|
|
5645
5736
|
lines.push("");
|
|
5646
|
-
for (
|
|
5647
|
-
const m = modes[i];
|
|
5737
|
+
for (const [i, m] of modes.entries()) {
|
|
5648
5738
|
const mark = m.id === active?.id ? color.green(" [active]") : "";
|
|
5649
5739
|
const prefix = i === currentCursor ? color.bold("\u276F ") : " ";
|
|
5650
5740
|
const name = i === currentCursor ? color.bold(m.name) : m.name;
|
|
@@ -5676,6 +5766,7 @@ ${color.bold(color.amber("WrongStack") + color.dim(" \u2014 Mode Selection"))}
|
|
|
5676
5766
|
function buildModeCommand(opts) {
|
|
5677
5767
|
return {
|
|
5678
5768
|
name: "mode",
|
|
5769
|
+
category: "Config",
|
|
5679
5770
|
description: "Switch or view the current mode",
|
|
5680
5771
|
help: [
|
|
5681
5772
|
"Usage:",
|
|
@@ -5794,7 +5885,7 @@ function parseFlags(tokens) {
|
|
|
5794
5885
|
let maxOutput;
|
|
5795
5886
|
let i = 0;
|
|
5796
5887
|
while (i < tokens.length) {
|
|
5797
|
-
const t = tokens[i];
|
|
5888
|
+
const t = tokens[i] ?? "";
|
|
5798
5889
|
if (t.startsWith("--")) {
|
|
5799
5890
|
const key = t.slice(2);
|
|
5800
5891
|
switch (key) {
|
|
@@ -5864,6 +5955,7 @@ function buildModelsCommand(opts) {
|
|
|
5864
5955
|
].join("\n");
|
|
5865
5956
|
return {
|
|
5866
5957
|
name: "models",
|
|
5958
|
+
category: "Config",
|
|
5867
5959
|
description: "Manage custom model definitions.",
|
|
5868
5960
|
help,
|
|
5869
5961
|
async run(args) {
|
|
@@ -5890,7 +5982,10 @@ function buildModelsCommand(opts) {
|
|
|
5890
5982
|
return {
|
|
5891
5983
|
message: [
|
|
5892
5984
|
`${color.bold("Custom Models")} ${color.dim(`(${ids.length})`)}`,
|
|
5893
|
-
...ids.sort().map((id) =>
|
|
5985
|
+
...ids.sort().map((id) => {
|
|
5986
|
+
const def = models[id];
|
|
5987
|
+
return def ? fmtModel(id, def) : void 0;
|
|
5988
|
+
}).filter((line) => line !== void 0)
|
|
5894
5989
|
].join("\n")
|
|
5895
5990
|
};
|
|
5896
5991
|
}
|
|
@@ -5955,6 +6050,7 @@ function buildModelsCommand(opts) {
|
|
|
5955
6050
|
function buildNextCommand(opts) {
|
|
5956
6051
|
return {
|
|
5957
6052
|
name: "next",
|
|
6053
|
+
category: "Config",
|
|
5958
6054
|
description: "Toggle next-task prediction \u2014 show likely next steps after each turn.",
|
|
5959
6055
|
argsHint: "[on|off|toggle]",
|
|
5960
6056
|
help: [
|
|
@@ -6006,6 +6102,7 @@ function buildNextCommand(opts) {
|
|
|
6006
6102
|
function buildPluginCommand(opts) {
|
|
6007
6103
|
return {
|
|
6008
6104
|
name: "plugin",
|
|
6105
|
+
category: "Config",
|
|
6009
6106
|
aliases: ["plugins"],
|
|
6010
6107
|
description: "Manage plugins: /plugin [list|status|official|install <alias>|enable <name>|disable <name>|remove <name>]",
|
|
6011
6108
|
argsHint: "[list|status|official|install <alias>|enable <name>|disable <name>|remove <name>]",
|
|
@@ -6033,12 +6130,86 @@ function buildPluginCommand(opts) {
|
|
|
6033
6130
|
}
|
|
6034
6131
|
};
|
|
6035
6132
|
}
|
|
6133
|
+
function buildPruneCommand(opts) {
|
|
6134
|
+
return {
|
|
6135
|
+
name: "prune",
|
|
6136
|
+
category: "Session",
|
|
6137
|
+
description: "Delete old sessions. /prune (default 30d), /prune 7, /prune --rebuild-index.",
|
|
6138
|
+
help: "Usage:\n /prune Delete sessions older than 30 days.\n /prune 14 Delete sessions older than 14 days.\n /prune --dry-run Show what would be deleted without deleting.\n /prune --rebuild-index Rebuild the session index from disk.",
|
|
6139
|
+
async run(args) {
|
|
6140
|
+
const parts = args.split(/\s+/).filter(Boolean);
|
|
6141
|
+
const rebuildIndex = parts.includes("--rebuild-index") || parts.includes("--rebuild");
|
|
6142
|
+
const dryRun = parts.includes("--dry-run");
|
|
6143
|
+
if (rebuildIndex) {
|
|
6144
|
+
if (!opts.sessionStore?.rebuildIndex) {
|
|
6145
|
+
return {
|
|
6146
|
+
message: color.yellow(
|
|
6147
|
+
"Session store does not support index rebuild."
|
|
6148
|
+
)
|
|
6149
|
+
};
|
|
6150
|
+
}
|
|
6151
|
+
const count = await opts.sessionStore.rebuildIndex();
|
|
6152
|
+
return {
|
|
6153
|
+
message: count === 0 ? color.dim("No sessions found to index.") : `Session index rebuilt: ${color.green(String(count))} session${count === 1 ? "" : "s"} indexed.`
|
|
6154
|
+
};
|
|
6155
|
+
}
|
|
6156
|
+
let maxAgeDays = 30;
|
|
6157
|
+
const numPart = parts.find((p) => /^\d+$/.test(p));
|
|
6158
|
+
if (numPart) {
|
|
6159
|
+
maxAgeDays = Math.max(1, Math.min(365, Number.parseInt(numPart, 10)));
|
|
6160
|
+
}
|
|
6161
|
+
if (dryRun) {
|
|
6162
|
+
if (!opts.sessionStore) {
|
|
6163
|
+
return { message: color.yellow("No session store configured.") };
|
|
6164
|
+
}
|
|
6165
|
+
const cutoff = Date.now() - maxAgeDays * 864e5;
|
|
6166
|
+
const list = await opts.sessionStore.list(1e3);
|
|
6167
|
+
const stale = list.filter((s) => new Date(s.startedAt).getTime() < cutoff);
|
|
6168
|
+
if (stale.length === 0) {
|
|
6169
|
+
return {
|
|
6170
|
+
message: color.dim(
|
|
6171
|
+
`No sessions older than ${maxAgeDays} day${maxAgeDays === 1 ? "" : "s"}.`
|
|
6172
|
+
)
|
|
6173
|
+
};
|
|
6174
|
+
}
|
|
6175
|
+
const lines = stale.map(
|
|
6176
|
+
(s) => ` ${color.dim(s.id)} ${color.dim(s.startedAt.slice(0, 10))} ${s.title}`
|
|
6177
|
+
);
|
|
6178
|
+
return {
|
|
6179
|
+
message: [
|
|
6180
|
+
color.bold(
|
|
6181
|
+
`Would delete ${stale.length} session${stale.length === 1 ? "" : "s"} (dry run, maxAge=${maxAgeDays}d):`
|
|
6182
|
+
),
|
|
6183
|
+
...lines,
|
|
6184
|
+
"",
|
|
6185
|
+
color.dim("Run /prune without --dry-run to actually delete.")
|
|
6186
|
+
].join("\n")
|
|
6187
|
+
};
|
|
6188
|
+
}
|
|
6189
|
+
if (!opts.sessionStore) {
|
|
6190
|
+
return { message: color.yellow("No session store configured.") };
|
|
6191
|
+
}
|
|
6192
|
+
const deleted = await opts.sessionStore.prune(maxAgeDays);
|
|
6193
|
+
if (deleted === 0) {
|
|
6194
|
+
return {
|
|
6195
|
+
message: color.dim(
|
|
6196
|
+
`No sessions older than ${maxAgeDays} day${maxAgeDays === 1 ? "" : "s"}.`
|
|
6197
|
+
)
|
|
6198
|
+
};
|
|
6199
|
+
}
|
|
6200
|
+
return {
|
|
6201
|
+
message: `Pruned ${color.green(String(deleted))} session${deleted === 1 ? "" : "s"} older than ${color.cyan(String(maxAgeDays))} day${maxAgeDays === 1 ? "" : "s"}.`
|
|
6202
|
+
};
|
|
6203
|
+
}
|
|
6204
|
+
};
|
|
6205
|
+
}
|
|
6036
6206
|
|
|
6037
6207
|
// src/slash-commands/index.ts
|
|
6038
6208
|
init_sdd();
|
|
6039
6209
|
function buildSaveCommand(opts) {
|
|
6040
6210
|
return {
|
|
6041
6211
|
name: "save",
|
|
6212
|
+
category: "Session",
|
|
6042
6213
|
description: "Save current session (auto by default; this forces flush).",
|
|
6043
6214
|
async run(_args, ctx) {
|
|
6044
6215
|
await ctx.session.append({
|
|
@@ -6053,6 +6224,7 @@ function buildSaveCommand(opts) {
|
|
|
6053
6224
|
function buildLoadCommand(opts) {
|
|
6054
6225
|
return {
|
|
6055
6226
|
name: "resume",
|
|
6227
|
+
category: "Session",
|
|
6056
6228
|
aliases: ["load", "sessions"],
|
|
6057
6229
|
description: "List recent sessions, show incomplete ones (--incomplete), or plan a recovery (--recover <id>).",
|
|
6058
6230
|
async run(args) {
|
|
@@ -6136,15 +6308,30 @@ function buildLoadCommand(opts) {
|
|
|
6136
6308
|
if (!opts.sessionStore) return { message: "No session store configured." };
|
|
6137
6309
|
const list = await opts.sessionStore.list(10);
|
|
6138
6310
|
if (list.length === 0) return { message: "No saved sessions." };
|
|
6139
|
-
const lines = list.map(
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
${
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
`)
|
|
6147
|
-
|
|
6311
|
+
const lines = list.map((s) => {
|
|
6312
|
+
const parts2 = [];
|
|
6313
|
+
parts2.push(color.dim(`${s.tokenTotal.toLocaleString()} tok`));
|
|
6314
|
+
if (s.toolCallCount) {
|
|
6315
|
+
const toolStr = `${s.toolCallCount} call${s.toolCallCount === 1 ? "" : "s"}`;
|
|
6316
|
+
parts2.push(s.toolErrorCount ? color.yellow(toolStr) : color.cyan(toolStr));
|
|
6317
|
+
}
|
|
6318
|
+
if (s.iterationCount) parts2.push(color.dim(`${s.iterationCount} iter`));
|
|
6319
|
+
if (s.outcome) {
|
|
6320
|
+
const badge = s.outcome === "completed" ? color.green("\u2713") : s.outcome === "aborted" ? color.yellow("\u26A0") : s.outcome === "error" ? color.red("\u2717") : color.dim("?");
|
|
6321
|
+
parts2.push(badge);
|
|
6322
|
+
}
|
|
6323
|
+
const stat4 = parts2.join(" ");
|
|
6324
|
+
const date = color.dim(s.startedAt.slice(0, 16).replace("T", " "));
|
|
6325
|
+
return ` ${s.id.padEnd(42)} ${date} ${stat4}
|
|
6326
|
+
${color.dim(s.title)}`;
|
|
6327
|
+
});
|
|
6328
|
+
const msg = [
|
|
6329
|
+
color.bold(`Recent sessions (${list.length}):`),
|
|
6330
|
+
...lines,
|
|
6331
|
+
"",
|
|
6332
|
+
color.dim(`Resume: wstack resume ${list[0]?.id ?? "<id>"}`),
|
|
6333
|
+
color.dim("Tip: /resume --incomplete \u2014 list crashed sessions")
|
|
6334
|
+
].join("\n");
|
|
6148
6335
|
opts.renderer.write(msg);
|
|
6149
6336
|
return { message: msg };
|
|
6150
6337
|
}
|
|
@@ -6153,6 +6340,7 @@ ${color.dim("Tip: /resume --incomplete \u2014 list sessions that crashed mid-ite
|
|
|
6153
6340
|
function buildExitCommand(opts) {
|
|
6154
6341
|
return {
|
|
6155
6342
|
name: "exit",
|
|
6343
|
+
category: "App",
|
|
6156
6344
|
aliases: ["quit", "q"],
|
|
6157
6345
|
description: "Exit the REPL.",
|
|
6158
6346
|
async run() {
|
|
@@ -6209,6 +6397,12 @@ function summariseEvent(ev) {
|
|
|
6209
6397
|
return color.dim("\u2026");
|
|
6210
6398
|
}
|
|
6211
6399
|
}
|
|
6400
|
+
function expectDefined5(value) {
|
|
6401
|
+
if (value === null || value === void 0) {
|
|
6402
|
+
throw new Error("Expected value to be defined");
|
|
6403
|
+
}
|
|
6404
|
+
return value;
|
|
6405
|
+
}
|
|
6212
6406
|
var noOpVault3 = {
|
|
6213
6407
|
encrypt: (v) => v,
|
|
6214
6408
|
decrypt: (v) => v,
|
|
@@ -6301,7 +6495,7 @@ function buildSetModelCommand(opts) {
|
|
|
6301
6495
|
for (const k of keys.sort()) {
|
|
6302
6496
|
const kind = matrixKeyKind(k);
|
|
6303
6497
|
const tag = kind === "unknown" ? color.red("?") : color.dim(kind);
|
|
6304
|
-
lines.push(` ${color.amber(k.padEnd(22))} \u2192 ${fmtEntry(matrix[k])} ${tag}`);
|
|
6498
|
+
lines.push(` ${color.amber(k.padEnd(22))} \u2192 ${fmtEntry(expectDefined5(matrix[k]))} ${tag}`);
|
|
6305
6499
|
}
|
|
6306
6500
|
}
|
|
6307
6501
|
lines.push("", color.dim(" /setmodel list for valid keys \xB7 /setmodel help for usage"));
|
|
@@ -6309,6 +6503,7 @@ function buildSetModelCommand(opts) {
|
|
|
6309
6503
|
}
|
|
6310
6504
|
return {
|
|
6311
6505
|
name: "setmodel",
|
|
6506
|
+
category: "Config",
|
|
6312
6507
|
description: "View or change the leader model and the per-task model matrix.",
|
|
6313
6508
|
help,
|
|
6314
6509
|
async run(args) {
|
|
@@ -6439,6 +6634,7 @@ function buildSettingsCommand(opts) {
|
|
|
6439
6634
|
" /settings Show current settings",
|
|
6440
6635
|
" /settings delay <seconds> Auto-proceed delay in auto mode (0 disables)",
|
|
6441
6636
|
" /settings mode <off|suggest|auto> Default autonomy mode at startup",
|
|
6637
|
+
" /settings hints on|off Show or suppress rotating launch hints",
|
|
6442
6638
|
" /settings defaults Show built-in default values",
|
|
6443
6639
|
"",
|
|
6444
6640
|
"Settings are persisted to ~/.wrongstack/config.json."
|
|
@@ -6447,18 +6643,21 @@ function buildSettingsCommand(opts) {
|
|
|
6447
6643
|
const autonomy = opts.configStore.get().autonomy;
|
|
6448
6644
|
const delay = autonomy?.autoProceedDelayMs ?? 45e3;
|
|
6449
6645
|
const mode = autonomy?.defaultMode ?? "off";
|
|
6646
|
+
const hints = opts.configStore.get().hints !== false;
|
|
6450
6647
|
return [
|
|
6451
6648
|
`${color.bold("WrongStack")} ${color.dim("\u2014 Settings")}`,
|
|
6452
6649
|
"",
|
|
6453
6650
|
` auto-proceed delay: ${color.cyan(formatDelay(delay))} ${color.dim("change: /settings delay <seconds>")}`,
|
|
6454
6651
|
` default autonomy mode: ${color.cyan(mode)} ${color.dim("change: /settings mode off|suggest|auto")}`,
|
|
6652
|
+
` launch hints: ${hints ? color.cyan("on") : color.dim("off")} ${color.dim("change: /settings hints on|off")}`,
|
|
6455
6653
|
"",
|
|
6456
6654
|
color.dim(" Persisted to ~/.wrongstack/config.json \xB7 /settings help for more")
|
|
6457
6655
|
].join("\n");
|
|
6458
6656
|
}
|
|
6459
6657
|
return {
|
|
6460
6658
|
name: "settings",
|
|
6461
|
-
|
|
6659
|
+
category: "Config",
|
|
6660
|
+
description: "View or change settings (auto-proceed delay, default autonomy mode, launch hints).",
|
|
6462
6661
|
help,
|
|
6463
6662
|
async run(args) {
|
|
6464
6663
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
@@ -6479,6 +6678,7 @@ function buildSettingsCommand(opts) {
|
|
|
6479
6678
|
"",
|
|
6480
6679
|
` auto-proceed delay: ${color.cyan("45s")} ${color.dim("(WRONGSTACK_AUTO_PROCEED_DELAY_MS env)")}`,
|
|
6481
6680
|
` default autonomy mode: ${color.cyan("off")}`,
|
|
6681
|
+
` launch hints: ${color.cyan("on")}`,
|
|
6482
6682
|
` iteration timeout: ${color.cyan("5 min")}`,
|
|
6483
6683
|
` session timeout: ${color.cyan("30 min")}`,
|
|
6484
6684
|
` max iterations: ${color.cyan("100")}`
|
|
@@ -6521,8 +6721,19 @@ function buildSettingsCommand(opts) {
|
|
|
6521
6721
|
});
|
|
6522
6722
|
return { message: `${color.green("\u2713")} default autonomy \u2192 ${color.bold(raw)}` };
|
|
6523
6723
|
}
|
|
6724
|
+
if (sub === "hints") {
|
|
6725
|
+
const raw = (parts[1] ?? "").toLowerCase();
|
|
6726
|
+
if (!["on", "off"].includes(raw)) {
|
|
6727
|
+
return { message: `${color.amber("Usage:")} /settings hints on|off` };
|
|
6728
|
+
}
|
|
6729
|
+
const on = raw === "on";
|
|
6730
|
+
await persistConfigSetting(persistDeps, (cfg) => {
|
|
6731
|
+
cfg.hints = on;
|
|
6732
|
+
});
|
|
6733
|
+
return { message: `${color.green("\u2713")} launch hints \u2192 ${on ? color.cyan("on") : color.dim("off")}` };
|
|
6734
|
+
}
|
|
6524
6735
|
return {
|
|
6525
|
-
message: `${color.red("Unknown setting")} "${sub}". Try ${color.dim("/settings")}, ${color.dim("/settings delay <s>")},
|
|
6736
|
+
message: `${color.red("Unknown setting")} "${sub}". Try ${color.dim("/settings")}, ${color.dim("/settings delay <s>")}, ${color.dim("/settings mode <m>")}, or ${color.dim("/settings hints on|off")}.`
|
|
6526
6737
|
};
|
|
6527
6738
|
} catch (err) {
|
|
6528
6739
|
return {
|
|
@@ -6556,6 +6767,7 @@ var HELP = [
|
|
|
6556
6767
|
function buildTelegramSetupCommand(opts) {
|
|
6557
6768
|
return {
|
|
6558
6769
|
name: "telegram-setup",
|
|
6770
|
+
category: "Config",
|
|
6559
6771
|
aliases: ["tg-setup"],
|
|
6560
6772
|
description: "Configure Telegram bot token and default chat. /telegram-setup <token> [chatId]",
|
|
6561
6773
|
argsHint: "[botToken] [chatId]",
|
|
@@ -6594,7 +6806,7 @@ function buildTelegramSetupCommand(opts) {
|
|
|
6594
6806
|
);
|
|
6595
6807
|
return { message: lines.join("\n") };
|
|
6596
6808
|
}
|
|
6597
|
-
const botToken = parts[0];
|
|
6809
|
+
const botToken = parts[0] ?? "";
|
|
6598
6810
|
const chatId = parts[1];
|
|
6599
6811
|
if (!/^\d+:[A-Za-z0-9_-]+$/.test(botToken)) {
|
|
6600
6812
|
return {
|
|
@@ -6688,6 +6900,7 @@ ${color.dim("No default chat set. You can add it later: /telegram-setup <token>
|
|
|
6688
6900
|
function buildSpawnCommand(opts) {
|
|
6689
6901
|
return {
|
|
6690
6902
|
name: "spawn",
|
|
6903
|
+
category: "Agent",
|
|
6691
6904
|
description: "Spawn an isolated subagent to handle a task.",
|
|
6692
6905
|
async run(args) {
|
|
6693
6906
|
const { description, opts: parsed } = parseSpawnFlags(args.trim());
|
|
@@ -6708,6 +6921,7 @@ function buildSpawnCommand(opts) {
|
|
|
6708
6921
|
function buildAgentsCommand(opts) {
|
|
6709
6922
|
return {
|
|
6710
6923
|
name: "agents",
|
|
6924
|
+
category: "Agent",
|
|
6711
6925
|
description: "Show status of spawned subagents. /agents monitor opens the agents monitor overlay. /agents on|off toggles the overlay.",
|
|
6712
6926
|
help: [
|
|
6713
6927
|
"Usage: /agents [monitor|on|off|stream on|stream off]",
|
|
@@ -6745,6 +6959,7 @@ function buildAgentsCommand(opts) {
|
|
|
6745
6959
|
function buildDirectorCommand(opts) {
|
|
6746
6960
|
return {
|
|
6747
6961
|
name: "director",
|
|
6962
|
+
category: "Agent",
|
|
6748
6963
|
description: "Promote this session to director mode, enabling fleet orchestration tools. Only works before any subagents are spawned.",
|
|
6749
6964
|
async run() {
|
|
6750
6965
|
if (!opts.onDirector) return { message: "Director promotion is not available in this session." };
|
|
@@ -6797,6 +7012,7 @@ async function saveStatuslineConfig(cfg) {
|
|
|
6797
7012
|
function buildStatuslineCommand(deps) {
|
|
6798
7013
|
return {
|
|
6799
7014
|
name: "statusline",
|
|
7015
|
+
category: "Config",
|
|
6800
7016
|
aliases: ["sl"],
|
|
6801
7017
|
description: "Customize status bar chips: /statusline [item] [on|off] or /statusline reset",
|
|
6802
7018
|
help: [
|
|
@@ -6874,15 +7090,22 @@ function findTodo(todos, query) {
|
|
|
6874
7090
|
if (item) return { idx, item };
|
|
6875
7091
|
}
|
|
6876
7092
|
const byId = todos.findIndex((t) => t.id === query);
|
|
6877
|
-
if (byId >= 0)
|
|
7093
|
+
if (byId >= 0) {
|
|
7094
|
+
const item = todos[byId];
|
|
7095
|
+
if (item) return { idx: byId, item };
|
|
7096
|
+
}
|
|
6878
7097
|
const q = query.toLowerCase();
|
|
6879
7098
|
const byContent = todos.findIndex((t) => t.content.toLowerCase().includes(q));
|
|
6880
|
-
if (byContent >= 0)
|
|
7099
|
+
if (byContent >= 0) {
|
|
7100
|
+
const item = todos[byContent];
|
|
7101
|
+
if (item) return { idx: byContent, item };
|
|
7102
|
+
}
|
|
6881
7103
|
return null;
|
|
6882
7104
|
}
|
|
6883
7105
|
function buildTodosCommand(opts) {
|
|
6884
7106
|
return {
|
|
6885
7107
|
name: "todos",
|
|
7108
|
+
category: "Inspect",
|
|
6886
7109
|
description: "Inspect or edit the live todo list: /todos [show|clear|add|done|remove|rm <id|index>]",
|
|
6887
7110
|
async run(args) {
|
|
6888
7111
|
const ctx = opts.context;
|
|
@@ -6949,6 +7172,7 @@ function buildTodosCommand(opts) {
|
|
|
6949
7172
|
function buildToolsCommand(opts) {
|
|
6950
7173
|
return {
|
|
6951
7174
|
name: "tools",
|
|
7175
|
+
category: "Inspect",
|
|
6952
7176
|
description: "List registered tools.",
|
|
6953
7177
|
async run() {
|
|
6954
7178
|
const all = opts.toolRegistry.listWithOwner();
|
|
@@ -6968,6 +7192,7 @@ ${lines.join("\n")}
|
|
|
6968
7192
|
function buildWorktreeCommand(opts) {
|
|
6969
7193
|
return {
|
|
6970
7194
|
name: "worktree",
|
|
7195
|
+
category: "Config",
|
|
6971
7196
|
aliases: ["wt"],
|
|
6972
7197
|
description: "Inspect/manage git worktrees used for AutoPhase per-phase isolation.",
|
|
6973
7198
|
argsHint: "[list | merge <branch> | prune | clean]",
|
|
@@ -7011,6 +7236,7 @@ function buildWorktreeCommand(opts) {
|
|
|
7011
7236
|
function buildYoloCommand(opts) {
|
|
7012
7237
|
return {
|
|
7013
7238
|
name: "yolo",
|
|
7239
|
+
category: "Config",
|
|
7014
7240
|
description: "Toggle or query YOLO (auto-approve) mode.",
|
|
7015
7241
|
help: [
|
|
7016
7242
|
"Usage:",
|
|
@@ -7068,6 +7294,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
7068
7294
|
buildCodebaseReindexCommand(opts),
|
|
7069
7295
|
buildToolsCommand(opts),
|
|
7070
7296
|
buildPluginCommand(opts),
|
|
7297
|
+
buildPruneCommand(opts),
|
|
7071
7298
|
buildMcpSlashCommand(opts),
|
|
7072
7299
|
buildDiagCommand(opts),
|
|
7073
7300
|
buildStatsCommand(opts),
|
|
@@ -7424,15 +7651,20 @@ var ReadlineInputReader = class {
|
|
|
7424
7651
|
});
|
|
7425
7652
|
}
|
|
7426
7653
|
const fresh = this.ensure();
|
|
7654
|
+
this.installPromptGuard(fresh);
|
|
7427
7655
|
return new Promise((resolve5) => {
|
|
7656
|
+
const settle = (line) => {
|
|
7657
|
+
setOutputLineGuard(null);
|
|
7658
|
+
resolve5(line);
|
|
7659
|
+
};
|
|
7428
7660
|
fresh.question(prompt ?? "> ", (line) => {
|
|
7429
7661
|
if (line.trim()) {
|
|
7430
7662
|
this.history.push(line);
|
|
7431
7663
|
void this.saveHistory();
|
|
7432
7664
|
}
|
|
7433
|
-
|
|
7665
|
+
settle(line);
|
|
7434
7666
|
});
|
|
7435
|
-
fresh.once("close", () =>
|
|
7667
|
+
fresh.once("close", () => settle(""));
|
|
7436
7668
|
}).then((result) => {
|
|
7437
7669
|
this.rl?.close();
|
|
7438
7670
|
return result;
|
|
@@ -7441,7 +7673,38 @@ var ReadlineInputReader = class {
|
|
|
7441
7673
|
this.pending = false;
|
|
7442
7674
|
}
|
|
7443
7675
|
}
|
|
7676
|
+
/**
|
|
7677
|
+
* Install the out-of-band write guard for the active prompt. When a log
|
|
7678
|
+
* line or other async output lands while the user is mid-type, the guard
|
|
7679
|
+
* clears the draft row, lets the message print, then repaints the prompt
|
|
7680
|
+
* and the in-progress draft (cursor preserved) via readline's own
|
|
7681
|
+
* refresh. Without it, each async write leaves the half-typed line
|
|
7682
|
+
* stranded as a fresh scrollback row.
|
|
7683
|
+
*
|
|
7684
|
+
* No-op on non-TTY output (piped/redirected) — there's no draft to
|
|
7685
|
+
* protect and the ANSI clear/repaint would be noise in a file.
|
|
7686
|
+
*/
|
|
7687
|
+
installPromptGuard(rl) {
|
|
7688
|
+
const out = process.stdout;
|
|
7689
|
+
if (!out.isTTY) {
|
|
7690
|
+
setOutputLineGuard(null);
|
|
7691
|
+
return;
|
|
7692
|
+
}
|
|
7693
|
+
setOutputLineGuard({
|
|
7694
|
+
suspend() {
|
|
7695
|
+
readline.cursorTo(out, 0);
|
|
7696
|
+
readline.clearLine(out, 0);
|
|
7697
|
+
},
|
|
7698
|
+
resume() {
|
|
7699
|
+
try {
|
|
7700
|
+
rl.prompt(true);
|
|
7701
|
+
} catch {
|
|
7702
|
+
}
|
|
7703
|
+
}
|
|
7704
|
+
});
|
|
7705
|
+
}
|
|
7444
7706
|
async readKey(prompt, options) {
|
|
7707
|
+
setOutputLineGuard(null);
|
|
7445
7708
|
writeOut(prompt);
|
|
7446
7709
|
return new Promise((resolve5) => {
|
|
7447
7710
|
const stdin = process.stdin;
|
|
@@ -7494,6 +7757,7 @@ var ReadlineInputReader = class {
|
|
|
7494
7757
|
async readSecret(prompt) {
|
|
7495
7758
|
const stdin = process.stdin;
|
|
7496
7759
|
if (!stdin.isTTY) return this.readLine(prompt);
|
|
7760
|
+
setOutputLineGuard(null);
|
|
7497
7761
|
this.rl?.close();
|
|
7498
7762
|
this.rl = void 0;
|
|
7499
7763
|
writeOut(prompt);
|
|
@@ -7556,6 +7820,7 @@ var ReadlineInputReader = class {
|
|
|
7556
7820
|
});
|
|
7557
7821
|
}
|
|
7558
7822
|
async close() {
|
|
7823
|
+
setOutputLineGuard(null);
|
|
7559
7824
|
await this.saveHistory();
|
|
7560
7825
|
this.rl?.close();
|
|
7561
7826
|
this.rl = void 0;
|
|
@@ -7874,14 +8139,20 @@ async function restoreLast(homeFn = defaultHomeDir) {
|
|
|
7874
8139
|
}
|
|
7875
8140
|
|
|
7876
8141
|
// src/picker.ts
|
|
8142
|
+
function expectDefined6(value) {
|
|
8143
|
+
if (value === null || value === void 0) {
|
|
8144
|
+
throw new Error("Expected value to be defined");
|
|
8145
|
+
}
|
|
8146
|
+
return value;
|
|
8147
|
+
}
|
|
7877
8148
|
var theme = { primary: color.amber };
|
|
7878
8149
|
async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os2__default.homedir()) {
|
|
7879
8150
|
try {
|
|
7880
|
-
const { atomicWrite:
|
|
7881
|
-
const
|
|
8151
|
+
const { atomicWrite: atomicWrite14 } = await import('@wrongstack/core');
|
|
8152
|
+
const fs26 = await import('fs/promises');
|
|
7882
8153
|
let existing = {};
|
|
7883
8154
|
try {
|
|
7884
|
-
const raw = await
|
|
8155
|
+
const raw = await fs26.readFile(configPath2, "utf8");
|
|
7885
8156
|
existing = JSON.parse(raw);
|
|
7886
8157
|
} catch {
|
|
7887
8158
|
}
|
|
@@ -7893,7 +8164,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
|
|
|
7893
8164
|
} catch (err) {
|
|
7894
8165
|
console.warn("[picker] backupCurrent failed:", err);
|
|
7895
8166
|
}
|
|
7896
|
-
await
|
|
8167
|
+
await atomicWrite14(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
7897
8168
|
try {
|
|
7898
8169
|
await appendHistory(
|
|
7899
8170
|
oldCfg,
|
|
@@ -7995,7 +8266,7 @@ ${color.bold(theme.primary("WrongStack") + color.dim(" \u2014 Provider & Model S
|
|
|
7995
8266
|
for (const p of list) {
|
|
7996
8267
|
const envFound = p.envVars.some((v) => !!process.env[v]);
|
|
7997
8268
|
const entry = config?.providers?.[p.id];
|
|
7998
|
-
const configKey = typeof entry?.apiKey === "string" && entry.apiKey.length > 0 || Array.isArray(entry?.apiKeys) && entry
|
|
8269
|
+
const configKey = typeof entry?.apiKey === "string" && entry.apiKey.length > 0 || Array.isArray(entry?.apiKeys) && entry?.apiKeys?.some((k) => k?.apiKey);
|
|
7999
8270
|
const marker = envFound ? color.green("\u25CF") : configKey ? color.cyan("\u25C9") : color.dim("\u25CB");
|
|
8000
8271
|
const isDefault = p.id === defaultProvider;
|
|
8001
8272
|
if (isDefault) defaultIdx = idx;
|
|
@@ -8069,7 +8340,7 @@ async function pickModel(provider, registry, renderer, reader, defaultModel) {
|
|
|
8069
8340
|
while (offset < models.length) {
|
|
8070
8341
|
const page = models.slice(offset, offset + pageSize);
|
|
8071
8342
|
for (let i = 0; i < page.length; i++) {
|
|
8072
|
-
const m = page[i];
|
|
8343
|
+
const m = expectDefined6(page[i]);
|
|
8073
8344
|
const num = offset + i + 1;
|
|
8074
8345
|
const ctx = m.limit?.context ? `${(m.limit.context / 1e3).toFixed(0)}k`.padStart(6) : " ?";
|
|
8075
8346
|
const cost = m.cost?.input !== void 0 ? `$${m.cost.input}/$${m.cost.output ?? "?"}` : "";
|
|
@@ -8126,7 +8397,7 @@ async function resolveModelSelection(answer, models, provider, _registry, render
|
|
|
8126
8397
|
const idx = Number.parseInt(answer, 10);
|
|
8127
8398
|
let modelId;
|
|
8128
8399
|
if (!Number.isNaN(idx) && idx >= 1 && idx <= models.length) {
|
|
8129
|
-
modelId = models[idx - 1]
|
|
8400
|
+
modelId = models[idx - 1]?.id;
|
|
8130
8401
|
} else {
|
|
8131
8402
|
const lower = answer.toLowerCase();
|
|
8132
8403
|
const match = models.find((m) => m.id.toLowerCase() === lower);
|
|
@@ -8135,7 +8406,7 @@ async function resolveModelSelection(answer, models, provider, _registry, render
|
|
|
8135
8406
|
} else {
|
|
8136
8407
|
const partial = models.filter((m) => m.id.toLowerCase().includes(lower));
|
|
8137
8408
|
if (partial.length === 1) {
|
|
8138
|
-
modelId = partial[0]
|
|
8409
|
+
modelId = partial[0]?.id;
|
|
8139
8410
|
} else if (partial.length > 1) {
|
|
8140
8411
|
renderer.writeError(`"${answer}" matches multiple models. Be more specific.`);
|
|
8141
8412
|
return void 0;
|
|
@@ -8460,6 +8731,7 @@ var TerminalRenderer = class {
|
|
|
8460
8731
|
if (this.silent) return;
|
|
8461
8732
|
if (result.delegateSummaries) {
|
|
8462
8733
|
for (const { summary, ok } of result.delegateSummaries) {
|
|
8734
|
+
if (!summary) continue;
|
|
8463
8735
|
this.writeAgentSummary(summary, ok);
|
|
8464
8736
|
}
|
|
8465
8737
|
return;
|
|
@@ -8752,7 +9024,7 @@ ${color.amber("?")} Pick: `)).trim().toLowerCase();
|
|
|
8752
9024
|
}
|
|
8753
9025
|
const idx = Number.parseInt(choice, 10);
|
|
8754
9026
|
if (!Number.isNaN(idx) && idx >= 1 && idx <= ids.length) {
|
|
8755
|
-
const pid = ids[idx - 1];
|
|
9027
|
+
const pid = expectDefined7(ids[idx - 1]);
|
|
8756
9028
|
await manageProvider(pid, deps);
|
|
8757
9029
|
continue;
|
|
8758
9030
|
}
|
|
@@ -8778,9 +9050,11 @@ ${color.bold("WrongStack")} ${color.dim("\u2014 API keys")}
|
|
|
8778
9050
|
let idx = 1;
|
|
8779
9051
|
for (const id of ids) {
|
|
8780
9052
|
const cfg = providers[id];
|
|
9053
|
+
if (!cfg) continue;
|
|
8781
9054
|
const keys = normalizeKeys(cfg);
|
|
8782
9055
|
const active = activeLabel(cfg, keys);
|
|
8783
|
-
const
|
|
9056
|
+
const firstKey = keys[0];
|
|
9057
|
+
const summary = keys.length === 0 ? color.dim("(no keys)") : keys.length === 1 ? maskedKey(firstKey?.apiKey ?? "") : `${color.dim(`${keys.length} keys`)} ${color.dim("active:")} ${color.bold(active ?? "?")} ${maskedKey(keys.find((k) => k.label === active)?.apiKey ?? firstKey?.apiKey ?? "")}`;
|
|
8784
9058
|
const fam = cfg.family ? color.dim(`[${cfg.family}]`) : "";
|
|
8785
9059
|
const aliasHint = cfg.type && cfg.type !== id ? color.dim(`\u2192 ${cfg.type}`) : "";
|
|
8786
9060
|
renderer.write(
|
|
@@ -8840,7 +9114,7 @@ ${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.am
|
|
|
8840
9114
|
deps.renderer.write(color.dim(" (no keys saved)\n"));
|
|
8841
9115
|
} else {
|
|
8842
9116
|
for (let i = 0; i < keys.length; i++) {
|
|
8843
|
-
const k = keys[i];
|
|
9117
|
+
const k = expectDefined7(keys[i]);
|
|
8844
9118
|
const marker = k.label === active ? color.green("\u25CF") : color.dim("\u25CB");
|
|
8845
9119
|
deps.renderer.write(
|
|
8846
9120
|
` ${color.dim(`${i + 1}.`.padStart(4))} ${marker} ${k.label.padEnd(20)} ${maskedKey(k.apiKey)} ${color.dim(k.createdAt)}
|
|
@@ -8902,7 +9176,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
|
|
|
8902
9176
|
deps.renderer.writeError(`Usage: u <1-${keys.length}>`);
|
|
8903
9177
|
continue;
|
|
8904
9178
|
}
|
|
8905
|
-
const target = keys[arg - 1];
|
|
9179
|
+
const target = expectDefined7(keys[arg - 1]);
|
|
8906
9180
|
const newKey = await readKeyInput(deps, `Updated key for ${target.label}`);
|
|
8907
9181
|
if (!newKey) continue;
|
|
8908
9182
|
await mutateProviders(deps, (all) => {
|
|
@@ -8922,7 +9196,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
|
|
|
8922
9196
|
deps.renderer.writeError(`Usage: d <1-${keys.length}>`);
|
|
8923
9197
|
continue;
|
|
8924
9198
|
}
|
|
8925
|
-
const target = keys[arg - 1];
|
|
9199
|
+
const target = expectDefined7(keys[arg - 1]);
|
|
8926
9200
|
const confirm = (await deps.reader.readLine(
|
|
8927
9201
|
` ${color.amber("?")} Delete key "${target.label}" (${maskedKey(target.apiKey)})? ${color.dim("[y/N/q]")} `
|
|
8928
9202
|
)).trim().toLowerCase();
|
|
@@ -8998,7 +9272,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
|
|
|
8998
9272
|
deps.renderer.writeError(`Usage: s <1-${keys.length}>`);
|
|
8999
9273
|
continue;
|
|
9000
9274
|
}
|
|
9001
|
-
const target = keys[arg - 1];
|
|
9275
|
+
const target = expectDefined7(keys[arg - 1]);
|
|
9002
9276
|
await mutateProviders(deps, (all) => {
|
|
9003
9277
|
const p = all[providerId];
|
|
9004
9278
|
if (!p) return;
|
|
@@ -9321,66 +9595,22 @@ async function readKeyInput(deps, intent) {
|
|
|
9321
9595
|
}
|
|
9322
9596
|
return key;
|
|
9323
9597
|
}
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
} catch (err) {
|
|
9329
|
-
if (err.code !== "ENOENT") {
|
|
9330
|
-
deps.renderer.writeWarning(
|
|
9331
|
-
`Could not read ${deps.globalConfigPath}: ${err.message}. Treating as empty.`
|
|
9332
|
-
);
|
|
9333
|
-
}
|
|
9334
|
-
return {};
|
|
9335
|
-
}
|
|
9336
|
-
let parsed = {};
|
|
9337
|
-
try {
|
|
9338
|
-
parsed = JSON.parse(raw);
|
|
9339
|
-
} catch (err) {
|
|
9340
|
-
deps.renderer.writeWarning(
|
|
9341
|
-
`Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`
|
|
9342
|
-
);
|
|
9343
|
-
return {};
|
|
9344
|
-
}
|
|
9345
|
-
const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
|
|
9346
|
-
return decrypted.providers ?? {};
|
|
9598
|
+
function loadProviders(deps) {
|
|
9599
|
+
return loadConfigProviders(deps.globalConfigPath, deps.vault, {
|
|
9600
|
+
warn: (msg) => deps.renderer.writeWarning(msg)
|
|
9601
|
+
});
|
|
9347
9602
|
}
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
let fileExists = true;
|
|
9351
|
-
try {
|
|
9352
|
-
raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
|
|
9353
|
-
} catch (err) {
|
|
9354
|
-
if (err.code !== "ENOENT") {
|
|
9355
|
-
throw new Error(
|
|
9356
|
-
`Refusing to mutate ${deps.globalConfigPath}: ${err.message}`,
|
|
9357
|
-
{ cause: err }
|
|
9358
|
-
);
|
|
9359
|
-
}
|
|
9360
|
-
fileExists = false;
|
|
9361
|
-
raw = "{}";
|
|
9362
|
-
}
|
|
9363
|
-
let parsed;
|
|
9364
|
-
try {
|
|
9365
|
-
parsed = JSON.parse(raw);
|
|
9366
|
-
} catch (err) {
|
|
9367
|
-
if (fileExists) {
|
|
9368
|
-
throw new Error(
|
|
9369
|
-
`Refusing to overwrite corrupt config at ${deps.globalConfigPath} (${err.message}). Fix or move the file aside before retrying.`,
|
|
9370
|
-
{ cause: err }
|
|
9371
|
-
);
|
|
9372
|
-
}
|
|
9373
|
-
parsed = {};
|
|
9374
|
-
}
|
|
9375
|
-
const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
|
|
9376
|
-
const providers = decrypted.providers ?? {};
|
|
9377
|
-
mutator(providers);
|
|
9378
|
-
decrypted.providers = providers;
|
|
9379
|
-
const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
|
|
9380
|
-
await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
9603
|
+
function mutateProviders(deps, mutator) {
|
|
9604
|
+
return mutateConfigProviders(deps.globalConfigPath, deps.vault, mutator);
|
|
9381
9605
|
}
|
|
9382
9606
|
|
|
9383
9607
|
// src/subcommands/handlers/auth.ts
|
|
9608
|
+
function expectDefined8(value) {
|
|
9609
|
+
if (value === null || value === void 0) {
|
|
9610
|
+
throw new Error("Expected value to be defined");
|
|
9611
|
+
}
|
|
9612
|
+
return value;
|
|
9613
|
+
}
|
|
9384
9614
|
var authCmd = async (args, deps) => {
|
|
9385
9615
|
const flags = parseAuthFlags(args);
|
|
9386
9616
|
const menuDeps = {
|
|
@@ -9392,7 +9622,7 @@ var authCmd = async (args, deps) => {
|
|
|
9392
9622
|
};
|
|
9393
9623
|
if (flags.positional.length === 0) return runAuthMenu(menuDeps);
|
|
9394
9624
|
return runAuthDirect(menuDeps, {
|
|
9395
|
-
providerId: flags.positional[0],
|
|
9625
|
+
providerId: expectDefined8(flags.positional[0]),
|
|
9396
9626
|
label: flags.label,
|
|
9397
9627
|
family: flags.family,
|
|
9398
9628
|
baseUrl: flags.baseUrl,
|
|
@@ -9615,6 +9845,12 @@ var doctorCmd = async (_args, deps) => {
|
|
|
9615
9845
|
deps.renderer.write(color.green("All checks passed.\n"));
|
|
9616
9846
|
return 0;
|
|
9617
9847
|
};
|
|
9848
|
+
function expectDefined9(value) {
|
|
9849
|
+
if (value === null || value === void 0) {
|
|
9850
|
+
throw new Error("Expected value to be defined");
|
|
9851
|
+
}
|
|
9852
|
+
return value;
|
|
9853
|
+
}
|
|
9618
9854
|
var exportCmd = async (args, deps) => {
|
|
9619
9855
|
if (!deps.sessionStore) {
|
|
9620
9856
|
deps.renderer.writeError("No session store configured.");
|
|
@@ -9626,7 +9862,7 @@ var exportCmd = async (args, deps) => {
|
|
|
9626
9862
|
let includeDiagnostics = true;
|
|
9627
9863
|
let sessionId;
|
|
9628
9864
|
for (let i = 0; i < args.length; i++) {
|
|
9629
|
-
const a = args[i];
|
|
9865
|
+
const a = expectDefined9(args[i]);
|
|
9630
9866
|
if (a === "--format" || a === "-f") {
|
|
9631
9867
|
const v = args[++i];
|
|
9632
9868
|
if (v !== "markdown" && v !== "json" && v !== "text") {
|
|
@@ -9888,6 +10124,12 @@ async function serveMcpStdio(deps) {
|
|
|
9888
10124
|
}
|
|
9889
10125
|
|
|
9890
10126
|
// src/subcommands/handlers/mcp.ts
|
|
10127
|
+
function expectDefined10(value) {
|
|
10128
|
+
if (value === null || value === void 0) {
|
|
10129
|
+
throw new Error("Expected value to be defined");
|
|
10130
|
+
}
|
|
10131
|
+
return value;
|
|
10132
|
+
}
|
|
9891
10133
|
var BUILT_IN_MCP = allServers();
|
|
9892
10134
|
var mcpCmd = async (args, deps) => {
|
|
9893
10135
|
const sub = args[0];
|
|
@@ -9938,7 +10180,7 @@ async function addMcpServer(args, deps) {
|
|
|
9938
10180
|
`);
|
|
9939
10181
|
if (Object.keys(deps.config.mcpServers ?? {}).length === 0)
|
|
9940
10182
|
for (const k of Object.keys(BUILT_IN_MCP)) {
|
|
9941
|
-
const s = BUILT_IN_MCP[k];
|
|
10183
|
+
const s = expectDefined10(BUILT_IN_MCP[k]);
|
|
9942
10184
|
deps.renderer.write(` ${k.padEnd(20)} ${s.description}
|
|
9943
10185
|
`);
|
|
9944
10186
|
}
|
|
@@ -10216,6 +10458,12 @@ var projectsCmd = async (_args, deps) => {
|
|
|
10216
10458
|
return 0;
|
|
10217
10459
|
}
|
|
10218
10460
|
};
|
|
10461
|
+
function expectDefined11(value) {
|
|
10462
|
+
if (value === null || value === void 0) {
|
|
10463
|
+
throw new Error("Expected value to be defined");
|
|
10464
|
+
}
|
|
10465
|
+
return value;
|
|
10466
|
+
}
|
|
10219
10467
|
var providersCmd = async (args, deps) => {
|
|
10220
10468
|
const showAll = args.includes("--all");
|
|
10221
10469
|
const showUnsupported = args.includes("--unsupported");
|
|
@@ -10263,14 +10511,14 @@ ${color.dim(`Current: ${deps.config.provider ?? "<unset>"} / ${deps.config.model
|
|
|
10263
10511
|
function parseFlags2(args) {
|
|
10264
10512
|
const flags = {};
|
|
10265
10513
|
for (let i = 0; i < args.length; i++) {
|
|
10266
|
-
const a = args[i];
|
|
10514
|
+
const a = expectDefined11(args[i]);
|
|
10267
10515
|
if (a.startsWith("--")) {
|
|
10268
10516
|
const eq = a.indexOf("=");
|
|
10269
10517
|
if (eq !== -1) {
|
|
10270
10518
|
flags[a.slice(2, eq)] = a.slice(eq + 1);
|
|
10271
10519
|
} else {
|
|
10272
10520
|
const name = a.slice(2);
|
|
10273
|
-
if (i + 1 < args.length && !args[i + 1]
|
|
10521
|
+
if (i + 1 < args.length && !args[i + 1]?.startsWith("--")) {
|
|
10274
10522
|
flags[name] = args[++i] ?? "";
|
|
10275
10523
|
} else {
|
|
10276
10524
|
flags[name] = true;
|
|
@@ -10283,11 +10531,11 @@ function parseFlags2(args) {
|
|
|
10283
10531
|
function positionals(args) {
|
|
10284
10532
|
const out = [];
|
|
10285
10533
|
for (let i = 0; i < args.length; i++) {
|
|
10286
|
-
const a = args[i];
|
|
10534
|
+
const a = expectDefined11(args[i]);
|
|
10287
10535
|
if (a.startsWith("--")) {
|
|
10288
10536
|
const eq = a.indexOf("=");
|
|
10289
10537
|
if (eq === -1) {
|
|
10290
|
-
if (i + 1 < args.length && !args[i + 1]
|
|
10538
|
+
if (i + 1 < args.length && !args[i + 1]?.startsWith("--")) {
|
|
10291
10539
|
i++;
|
|
10292
10540
|
}
|
|
10293
10541
|
}
|
|
@@ -10438,7 +10686,7 @@ function parseSizeFlag(raw) {
|
|
|
10438
10686
|
const s = raw.trim().toLowerCase();
|
|
10439
10687
|
const match = /^(\d+(?:\.\d+)?)\s*(k|m|b)?$/.exec(s);
|
|
10440
10688
|
if (!match) return void 0;
|
|
10441
|
-
const num = Number.parseFloat(match[1]);
|
|
10689
|
+
const num = Number.parseFloat(expectDefined11(match[1]));
|
|
10442
10690
|
const unit = match[2];
|
|
10443
10691
|
if (unit === "b") return Math.round(num * 1e9);
|
|
10444
10692
|
if (unit === "m") return Math.round(num * 1e6);
|
|
@@ -10759,6 +11007,12 @@ Fleet Run: ${runId}
|
|
|
10759
11007
|
}
|
|
10760
11008
|
|
|
10761
11009
|
// src/subcommands/handlers/sessions-config.ts
|
|
11010
|
+
function expectDefined12(value) {
|
|
11011
|
+
if (value === null || value === void 0) {
|
|
11012
|
+
throw new Error("Expected value to be defined");
|
|
11013
|
+
}
|
|
11014
|
+
return value;
|
|
11015
|
+
}
|
|
10762
11016
|
var sessionsCmd = async (args, deps) => {
|
|
10763
11017
|
const sub = args[0];
|
|
10764
11018
|
if (sub === "fleet") {
|
|
@@ -10804,7 +11058,7 @@ var configCmd = async (args, deps) => {
|
|
|
10804
11058
|
};
|
|
10805
11059
|
function extractArg(args, key) {
|
|
10806
11060
|
const idx = args.indexOf(key);
|
|
10807
|
-
if (idx !== -1 && args[idx + 1] !== void 0) return args[idx + 1];
|
|
11061
|
+
if (idx !== -1 && args[idx + 1] !== void 0) return expectDefined12(args[idx + 1]);
|
|
10808
11062
|
const eq = key.startsWith("--") ? args.find((a) => a.startsWith(`${key}=`)) : null;
|
|
10809
11063
|
if (eq) return eq.slice(eq.indexOf("=") + 1);
|
|
10810
11064
|
return null;
|
|
@@ -10855,7 +11109,7 @@ async function runHistory(args, deps) {
|
|
|
10855
11109
|
}
|
|
10856
11110
|
async function runRestore(args, deps) {
|
|
10857
11111
|
const latest = args.includes("--latest") || args.includes("-l");
|
|
10858
|
-
const id = extractArg(args, "--id") ?? (args[0] && !args[0]
|
|
11112
|
+
const id = extractArg(args, "--id") ?? (args[0] && !args[0]?.startsWith("-") ? args[0] : null);
|
|
10859
11113
|
if (latest) {
|
|
10860
11114
|
const result2 = await restoreLast();
|
|
10861
11115
|
if (!result2.ok) {
|
|
@@ -10880,6 +11134,12 @@ async function runRestore(args, deps) {
|
|
|
10880
11134
|
`);
|
|
10881
11135
|
return 0;
|
|
10882
11136
|
}
|
|
11137
|
+
function expectDefined13(value) {
|
|
11138
|
+
if (value === null || value === void 0) {
|
|
11139
|
+
throw new Error("Expected value to be defined");
|
|
11140
|
+
}
|
|
11141
|
+
return value;
|
|
11142
|
+
}
|
|
10883
11143
|
function parseRewindFlags(args) {
|
|
10884
11144
|
const flags = {};
|
|
10885
11145
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -10894,7 +11154,7 @@ function parseRewindFlags(args) {
|
|
|
10894
11154
|
}
|
|
10895
11155
|
function findSessionId(args) {
|
|
10896
11156
|
for (let i = 0; i < args.length; i++) {
|
|
10897
|
-
const a = args[i];
|
|
11157
|
+
const a = expectDefined13(args[i]);
|
|
10898
11158
|
if (a === "--last" || a === "--to") {
|
|
10899
11159
|
i++;
|
|
10900
11160
|
continue;
|
|
@@ -10919,13 +11179,18 @@ var rewindCmd = async (args, deps) => {
|
|
|
10919
11179
|
deps.renderer.writeError("No sessions found.");
|
|
10920
11180
|
return 1;
|
|
10921
11181
|
}
|
|
10922
|
-
sessionId = sessions[0]
|
|
11182
|
+
sessionId = sessions[0]?.id;
|
|
11183
|
+
}
|
|
11184
|
+
if (!sessionId) {
|
|
11185
|
+
deps.renderer.writeError("No sessions found.");
|
|
11186
|
+
return 1;
|
|
10923
11187
|
}
|
|
11188
|
+
const targetSessionId = sessionId;
|
|
10924
11189
|
if (flags.list) {
|
|
10925
|
-
deps.renderer.write(`Session: ${color.bold(
|
|
11190
|
+
deps.renderer.write(`Session: ${color.bold(targetSessionId)}
|
|
10926
11191
|
|
|
10927
11192
|
`);
|
|
10928
|
-
const checkpoints = await rewind.listCheckpoints(
|
|
11193
|
+
const checkpoints = await rewind.listCheckpoints(targetSessionId);
|
|
10929
11194
|
if (checkpoints.length === 0) {
|
|
10930
11195
|
deps.renderer.write("No checkpoints in this session.\n");
|
|
10931
11196
|
return 0;
|
|
@@ -10942,7 +11207,7 @@ var rewindCmd = async (args, deps) => {
|
|
|
10942
11207
|
let result;
|
|
10943
11208
|
if (flags.all) {
|
|
10944
11209
|
deps.renderer.write("Rewinding to session start...\n");
|
|
10945
|
-
result = await rewind.rewindToStart(
|
|
11210
|
+
result = await rewind.rewindToStart(targetSessionId);
|
|
10946
11211
|
} else if (flags.last) {
|
|
10947
11212
|
const n = Number.parseInt(flags.last, 10);
|
|
10948
11213
|
if (Number.isNaN(n) || n < 1) {
|
|
@@ -10951,7 +11216,7 @@ var rewindCmd = async (args, deps) => {
|
|
|
10951
11216
|
}
|
|
10952
11217
|
deps.renderer.write(`Rewinding last ${n} prompt(s)...
|
|
10953
11218
|
`);
|
|
10954
|
-
result = await rewind.rewindLastN(
|
|
11219
|
+
result = await rewind.rewindLastN(targetSessionId, n);
|
|
10955
11220
|
} else if (flags.to) {
|
|
10956
11221
|
const idx = Number.parseInt(flags.to, 10);
|
|
10957
11222
|
if (Number.isNaN(idx) || idx < 0) {
|
|
@@ -10960,7 +11225,7 @@ var rewindCmd = async (args, deps) => {
|
|
|
10960
11225
|
}
|
|
10961
11226
|
deps.renderer.write(`Rewinding to checkpoint ${idx}...
|
|
10962
11227
|
`);
|
|
10963
|
-
result = await rewind.rewindToCheckpoint(
|
|
11228
|
+
result = await rewind.rewindToCheckpoint(targetSessionId, idx);
|
|
10964
11229
|
} else {
|
|
10965
11230
|
deps.renderer.write("Usage: ws rewind --all | --last N | --to <index> [--list] [--resume]\n");
|
|
10966
11231
|
deps.renderer.write(" --all Rewind to session start\n");
|
|
@@ -10974,7 +11239,7 @@ var rewindCmd = async (args, deps) => {
|
|
|
10974
11239
|
deps.renderer.write("No files to revert.\n");
|
|
10975
11240
|
if (flags.resume) {
|
|
10976
11241
|
const store = new DefaultSessionStore({ dir: sessionsDir });
|
|
10977
|
-
const resumed = await store.resume(
|
|
11242
|
+
const resumed = await store.resume(targetSessionId);
|
|
10978
11243
|
const toIdx = result.toPromptIndex;
|
|
10979
11244
|
await resumed.writer.truncateToCheckpoint(toIdx);
|
|
10980
11245
|
await resumed.writer.close();
|
|
@@ -10992,7 +11257,7 @@ Reverted ${result.revertedFiles.length} file(s):
|
|
|
10992
11257
|
}
|
|
10993
11258
|
if (flags.resume) {
|
|
10994
11259
|
const store = new DefaultSessionStore({ dir: sessionsDir });
|
|
10995
|
-
const resumed = await store.resume(
|
|
11260
|
+
const resumed = await store.resume(targetSessionId);
|
|
10996
11261
|
const toIdx = result.toPromptIndex;
|
|
10997
11262
|
const removed = await resumed.writer.truncateToCheckpoint(toIdx);
|
|
10998
11263
|
await resumed.writer.close();
|
|
@@ -11141,10 +11406,10 @@ var auditCmd = async (args, deps) => {
|
|
|
11141
11406
|
return verify.ok ? 0 : 1;
|
|
11142
11407
|
};
|
|
11143
11408
|
async function listAudits(log, dir, deps) {
|
|
11144
|
-
const
|
|
11409
|
+
const fs26 = await import('fs/promises');
|
|
11145
11410
|
let entries;
|
|
11146
11411
|
try {
|
|
11147
|
-
entries = await
|
|
11412
|
+
entries = await fs26.readdir(dir);
|
|
11148
11413
|
} catch {
|
|
11149
11414
|
deps.renderer.write(
|
|
11150
11415
|
color.dim(`No sessions dir found at ${dir}. Run a session first.`) + "\n"
|
|
@@ -11295,22 +11560,22 @@ function fmtDuration(ms) {
|
|
|
11295
11560
|
const remMin = m - h * 60;
|
|
11296
11561
|
return `${h}h${remMin}m`;
|
|
11297
11562
|
}
|
|
11298
|
-
function fmtTaskResultLine(r,
|
|
11563
|
+
function fmtTaskResultLine(r, color52) {
|
|
11299
11564
|
const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
|
|
11300
11565
|
const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
|
|
11301
11566
|
const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
|
|
11302
11567
|
const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
|
|
11303
|
-
const errKindChip = errKind ?
|
|
11304
|
-
const errSnip = errMsg || errKind ? `${errKindChip}${
|
|
11568
|
+
const errKindChip = errKind ? color52.dim(` [${errKind}]`) : "";
|
|
11569
|
+
const errSnip = errMsg || errKind ? `${errKindChip}${color52.dim(errTail)}` : "";
|
|
11305
11570
|
switch (r.status) {
|
|
11306
11571
|
case "success":
|
|
11307
|
-
return { mark:
|
|
11572
|
+
return { mark: color52.green("\u2713"), stats, tail: "" };
|
|
11308
11573
|
case "timeout":
|
|
11309
|
-
return { mark:
|
|
11574
|
+
return { mark: color52.yellow("\u23F1"), stats: `${color52.yellow("timeout")} ${stats}`, tail: errSnip };
|
|
11310
11575
|
case "stopped":
|
|
11311
|
-
return { mark:
|
|
11576
|
+
return { mark: color52.dim("\u2298"), stats: `${color52.dim("stopped")} ${stats}`, tail: errSnip };
|
|
11312
11577
|
case "failed":
|
|
11313
|
-
return { mark:
|
|
11578
|
+
return { mark: color52.red("\u2717"), stats: `${color52.red("failed")} ${stats}`, tail: errSnip };
|
|
11314
11579
|
}
|
|
11315
11580
|
}
|
|
11316
11581
|
|
|
@@ -11359,8 +11624,8 @@ async function boot(argv) {
|
|
|
11359
11624
|
file: wpaths.logFile,
|
|
11360
11625
|
// Suppress stderr output in TUI mode: plugin/library log messages
|
|
11361
11626
|
// (e.g. Telegram "getUpdates failed") write directly to stderr and
|
|
11362
|
-
// bypass Ink, which breaks the Static/live boundary
|
|
11363
|
-
//
|
|
11627
|
+
// bypass Ink, which breaks the Static/live boundary.
|
|
11628
|
+
// Logs still go to the disk file for post-hoc debugging.
|
|
11364
11629
|
stderr: !flags.tui
|
|
11365
11630
|
});
|
|
11366
11631
|
const renderer = new TerminalRenderer();
|
|
@@ -11411,7 +11676,7 @@ async function boot(argv) {
|
|
|
11411
11676
|
[...builtinToolsPack.tools ?? []],
|
|
11412
11677
|
builtinToolsPack.name
|
|
11413
11678
|
);
|
|
11414
|
-
const code = await subcommands[first](positional.slice(1), {
|
|
11679
|
+
const code = await subcommands[first]?.(positional.slice(1), {
|
|
11415
11680
|
config,
|
|
11416
11681
|
renderer,
|
|
11417
11682
|
reader,
|
|
@@ -11903,6 +12168,12 @@ async function predictNextTasks(input, opts) {
|
|
|
11903
12168
|
}
|
|
11904
12169
|
}
|
|
11905
12170
|
init_sdd();
|
|
12171
|
+
function expectDefined14(value) {
|
|
12172
|
+
if (value === null || value === void 0) {
|
|
12173
|
+
throw new Error("Expected value to be defined");
|
|
12174
|
+
}
|
|
12175
|
+
return value;
|
|
12176
|
+
}
|
|
11906
12177
|
async function runRepl(opts) {
|
|
11907
12178
|
if (opts.banner !== false) printBanner(opts.renderer, opts.projectName);
|
|
11908
12179
|
await renderGoalBanner(opts);
|
|
@@ -12418,7 +12689,7 @@ async function renderGoalBanner(opts) {
|
|
|
12418
12689
|
color.dim("Goal: ") + stateColor(summary) + color.dim(` [${goal.goalState}] (iter ${goal.iterations})`) + "\n"
|
|
12419
12690
|
);
|
|
12420
12691
|
if (goal.journal.length > 0) {
|
|
12421
|
-
const lastEntry = goal.journal[goal.journal.length - 1];
|
|
12692
|
+
const lastEntry = expectDefined14(goal.journal[goal.journal.length - 1]);
|
|
12422
12693
|
const statusIcon = lastEntry.status === "success" ? "\u2713" : lastEntry.status === "failure" ? "\u2717" : lastEntry.status === "aborted" ? "\u2298" : lastEntry.status === "skipped" ? "\u229D" : "\xB7";
|
|
12423
12694
|
opts.renderer.write(
|
|
12424
12695
|
color.dim(` Last: ${statusIcon} ${lastEntry.task} (${lastEntry.status})`) + "\n"
|
|
@@ -12872,20 +13143,10 @@ async function execute(deps) {
|
|
|
12872
13143
|
titleAnimation: config.autonomy?.["terminalTitleAnimation"] ?? true,
|
|
12873
13144
|
// Completion chime: terminal bell when agent finishes.
|
|
12874
13145
|
chime: config.autonomy?.["chime"] ?? false,
|
|
12875
|
-
//
|
|
13146
|
+
// Normal exit.
|
|
12876
13147
|
confirmExit: config.autonomy?.["confirmExit"] ?? true,
|
|
12877
|
-
// Default OFF so the terminal's native scrollback works for chat
|
|
12878
|
-
// history out of the box. Users who hit resize/overlay-leak
|
|
12879
|
-
// artifacts can opt back into alt-screen with `--alt-screen`.
|
|
12880
|
-
// `--no-alt-screen` still wins when both are passed.
|
|
12881
|
-
altScreen: flags["alt-screen"] === true && flags["no-alt-screen"] !== true,
|
|
12882
13148
|
director,
|
|
12883
13149
|
fleetRoster,
|
|
12884
|
-
onAfterExit: () => {
|
|
12885
|
-
writeOut(
|
|
12886
|
-
color.dim(`Session saved: ${session.id} \u2014 resume with `) + color.cyan(`wstack resume ${session.id}`) + "\n"
|
|
12887
|
-
);
|
|
12888
|
-
},
|
|
12889
13150
|
onClearHistory: (dispatch) => {
|
|
12890
13151
|
dispatch({ type: "clearHistory" });
|
|
12891
13152
|
dispatch({ type: "resetContextChip" });
|
|
@@ -13030,6 +13291,12 @@ async function execute(deps) {
|
|
|
13030
13291
|
}
|
|
13031
13292
|
return code;
|
|
13032
13293
|
}
|
|
13294
|
+
function expectDefined15(value) {
|
|
13295
|
+
if (value === null || value === void 0) {
|
|
13296
|
+
throw new Error("Expected value to be defined");
|
|
13297
|
+
}
|
|
13298
|
+
return value;
|
|
13299
|
+
}
|
|
13033
13300
|
function buildRoutingRunner(config, host) {
|
|
13034
13301
|
const standardRunner = makeAgentSubagentRunner({
|
|
13035
13302
|
factory: host.makeSubagentFactory(config),
|
|
@@ -13038,7 +13305,7 @@ function buildRoutingRunner(config, host) {
|
|
|
13038
13305
|
return async (task, ctx) => {
|
|
13039
13306
|
const subCfg = ctx.config;
|
|
13040
13307
|
if (subCfg.provider === "acp") {
|
|
13041
|
-
const cacheKey = subCfg.role ?? subCfg.name ?? subCfg.id;
|
|
13308
|
+
const cacheKey = subCfg.role ?? subCfg.name ?? expectDefined15(subCfg.id);
|
|
13042
13309
|
return host.buildACPRunner(cacheKey).then((r) => r(task, ctx));
|
|
13043
13310
|
}
|
|
13044
13311
|
return standardRunner(task, ctx);
|
|
@@ -13481,6 +13748,7 @@ var MultiAgentHost = class {
|
|
|
13481
13748
|
*/
|
|
13482
13749
|
async _spawnAndAssign(subagentConfig) {
|
|
13483
13750
|
const taskId = randomUUID();
|
|
13751
|
+
if (!this.director) throw new Error("Director is not initialized");
|
|
13484
13752
|
const subagentId = await this.director.spawn(subagentConfig);
|
|
13485
13753
|
await this.director.assign({ id: taskId, description: "", subagentId });
|
|
13486
13754
|
return { subagentId, taskId };
|
|
@@ -14259,9 +14527,18 @@ var Spinner = class {
|
|
|
14259
14527
|
context;
|
|
14260
14528
|
out;
|
|
14261
14529
|
enabled;
|
|
14262
|
-
|
|
14530
|
+
/**
|
|
14531
|
+
* @param out Stream the spinner writes to (default stderr).
|
|
14532
|
+
* @param opts.enabled Hard override. When `false`, every method becomes a
|
|
14533
|
+
* no-op regardless of TTY state — used to silence the spinner when the Ink
|
|
14534
|
+
* TUI owns the screen (the spinner writes to stderr on an 80ms timer, which
|
|
14535
|
+
* interleaves with Ink's stdout cursor math and corrupts the live region:
|
|
14536
|
+
* it leaks the input row into scrollback and paints a stray
|
|
14537
|
+
* "thinking… ctx" tracker at the very bottom). Defaults to TTY detection.
|
|
14538
|
+
*/
|
|
14539
|
+
constructor(out = process.stderr, opts) {
|
|
14263
14540
|
this.out = out;
|
|
14264
|
-
this.enabled = Boolean(out.isTTY) && !process.env.NO_COLOR;
|
|
14541
|
+
this.enabled = (opts?.enabled ?? Boolean(out.isTTY)) && !process.env.NO_COLOR;
|
|
14265
14542
|
}
|
|
14266
14543
|
start(label) {
|
|
14267
14544
|
if (!this.enabled || this.active) return;
|
|
@@ -14917,8 +15194,17 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
|
|
|
14917
15194
|
}
|
|
14918
15195
|
return { resolvedProvider, provider, providerRegistry };
|
|
14919
15196
|
}
|
|
15197
|
+
function expectDefined16(value) {
|
|
15198
|
+
if (value === null || value === void 0) {
|
|
15199
|
+
throw new Error("Expected value to be defined");
|
|
15200
|
+
}
|
|
15201
|
+
return value;
|
|
15202
|
+
}
|
|
14920
15203
|
async function setupSession(params) {
|
|
14921
15204
|
const { config, wpaths, projectRoot, cwd, sessionStore, systemPrompt, provider, tokenCounter, renderer, flags, onRecovery } = params;
|
|
15205
|
+
sessionStore.prune(30).then((count) => {
|
|
15206
|
+
if (count > 0) renderer.writeInfo(`Pruned ${count} old session${count === 1 ? "" : "s"}.`);
|
|
15207
|
+
}).catch(() => void 0);
|
|
14922
15208
|
let resumeId = typeof flags["resume"] === "string" ? flags["resume"] : void 0;
|
|
14923
15209
|
const recoveryLock = new RecoveryLock({ dir: wpaths.projectSessions, sessionStore });
|
|
14924
15210
|
if (!resumeId && !flags["no-recovery"]) {
|
|
@@ -14951,13 +15237,13 @@ async function setupSession(params) {
|
|
|
14951
15237
|
session = await sessionStore.create({ id: "", title: "", model: config.model, provider: config.provider });
|
|
14952
15238
|
}
|
|
14953
15239
|
const sessionRef = { current: session };
|
|
14954
|
-
await recoveryLock.write(session
|
|
14955
|
-
const attachments = new DefaultAttachmentStore({ spoolDir: path8.join(wpaths.projectSessions, session
|
|
14956
|
-
const queueStore = new QueueStore({ dir: path8.join(wpaths.projectSessions, session
|
|
15240
|
+
await recoveryLock.write(session?.id).catch(() => void 0);
|
|
15241
|
+
const attachments = new DefaultAttachmentStore({ spoolDir: path8.join(wpaths.projectSessions, session?.id, "attachments") });
|
|
15242
|
+
const queueStore = new QueueStore({ dir: path8.join(wpaths.projectSessions, session?.id) });
|
|
14957
15243
|
const ctxSignal = new AbortController().signal;
|
|
14958
|
-
const context = new Context({ systemPrompt, provider, session, signal: ctxSignal, tokenCounter, cwd, projectRoot, model: config.model });
|
|
15244
|
+
const context = new Context({ systemPrompt, provider, session: expectDefined16(session), signal: ctxSignal, tokenCounter, cwd, projectRoot, model: config.model });
|
|
14959
15245
|
if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
|
|
14960
|
-
const todosCheckpointPath = path8.join(wpaths.projectSessions, `${session
|
|
15246
|
+
const todosCheckpointPath = path8.join(wpaths.projectSessions, `${session?.id}.todos.json`);
|
|
14961
15247
|
if (resumeId) {
|
|
14962
15248
|
try {
|
|
14963
15249
|
const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
|
|
@@ -14968,13 +15254,13 @@ async function setupSession(params) {
|
|
|
14968
15254
|
} catch {
|
|
14969
15255
|
}
|
|
14970
15256
|
}
|
|
14971
|
-
const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session
|
|
14972
|
-
const planPath = path8.join(wpaths.projectSessions, `${session
|
|
15257
|
+
const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session?.id);
|
|
15258
|
+
const planPath = path8.join(wpaths.projectSessions, `${session?.id}.plan.json`);
|
|
14973
15259
|
context.state.setMeta("plan.path", planPath);
|
|
14974
15260
|
let dirState;
|
|
14975
15261
|
if (resumeId) {
|
|
14976
15262
|
try {
|
|
14977
|
-
const fleetRoot = path8.join(wpaths.projectSessions, session
|
|
15263
|
+
const fleetRoot = path8.join(wpaths.projectSessions, session?.id);
|
|
14978
15264
|
dirState = await loadDirectorState(path8.join(fleetRoot, "director-state.json"));
|
|
14979
15265
|
if (dirState) {
|
|
14980
15266
|
const tCounts = {};
|
|
@@ -14994,7 +15280,7 @@ async function setupSession(params) {
|
|
|
14994
15280
|
} catch {
|
|
14995
15281
|
}
|
|
14996
15282
|
}
|
|
14997
|
-
return { session, sessionRef, context, restoredMessages, attachments, recoveryLock, queueStore, planPath, detachTodosCheckpoint, priorFleetState: dirState ?? void 0 };
|
|
15283
|
+
return { session: expectDefined16(session), sessionRef, context, restoredMessages, attachments, recoveryLock, queueStore, planPath, detachTodosCheckpoint, priorFleetState: dirState ?? void 0 };
|
|
14998
15284
|
}
|
|
14999
15285
|
function resolveBundledSkillsDir2() {
|
|
15000
15286
|
try {
|
|
@@ -15093,6 +15379,12 @@ async function launchEternalFromFlag(deps) {
|
|
|
15093
15379
|
}
|
|
15094
15380
|
|
|
15095
15381
|
// src/cli-main.ts
|
|
15382
|
+
function expectDefined17(value) {
|
|
15383
|
+
if (value === null || value === void 0) {
|
|
15384
|
+
throw new Error("Expected value to be defined");
|
|
15385
|
+
}
|
|
15386
|
+
return value;
|
|
15387
|
+
}
|
|
15096
15388
|
async function main(argv) {
|
|
15097
15389
|
const ctx = await boot(argv);
|
|
15098
15390
|
if (typeof ctx === "number") return ctx;
|
|
@@ -15228,7 +15520,8 @@ async function main(argv) {
|
|
|
15228
15520
|
});
|
|
15229
15521
|
return ms;
|
|
15230
15522
|
})();
|
|
15231
|
-
const
|
|
15523
|
+
const tuiOwnsScreen = flags.tui === true && flags["no-tui"] !== true;
|
|
15524
|
+
const spinner = new Spinner(process.stderr, { enabled: !tuiOwnsScreen });
|
|
15232
15525
|
let lastInputTokens = 0;
|
|
15233
15526
|
events.on("provider.response", (e) => {
|
|
15234
15527
|
lastInputTokens = e.usage?.input ?? 0;
|
|
@@ -15310,7 +15603,9 @@ async function main(argv) {
|
|
|
15310
15603
|
const planPath = sessResult.planPath;
|
|
15311
15604
|
const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
|
|
15312
15605
|
const priorFleetState = sessResult.priorFleetState;
|
|
15313
|
-
const sessionConfig = resolveSessionLoggingConfig(
|
|
15606
|
+
const sessionConfig = resolveSessionLoggingConfig(
|
|
15607
|
+
config
|
|
15608
|
+
);
|
|
15314
15609
|
const sessionBridge = createSessionEventBridge(
|
|
15315
15610
|
session,
|
|
15316
15611
|
sessionConfig.auditLevel,
|
|
@@ -15360,7 +15655,6 @@ async function main(argv) {
|
|
|
15360
15655
|
}).catch(() => {
|
|
15361
15656
|
});
|
|
15362
15657
|
});
|
|
15363
|
-
const tuiOwnsScreen = flags.tui === true && flags["no-tui"] !== true;
|
|
15364
15658
|
if (!tuiOwnsScreen) {
|
|
15365
15659
|
events.on("delegate.started", (e) => {
|
|
15366
15660
|
const task = e.task.length > 100 ? `${e.task.slice(0, 99)}\u2026` : e.task;
|
|
@@ -15571,10 +15865,10 @@ async function main(argv) {
|
|
|
15571
15865
|
}
|
|
15572
15866
|
};
|
|
15573
15867
|
const fleetRoot = directorMode ? path8.join(wpaths.projectSessions, session.id) : void 0;
|
|
15574
|
-
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path8.join(fleetRoot, "fleet.json") : void 0;
|
|
15575
|
-
const sharedScratchpadPath = directorMode ? path8.join(fleetRoot, "shared") : void 0;
|
|
15576
|
-
const subagentSessionsRoot = directorMode ? path8.join(fleetRoot, "subagents") : void 0;
|
|
15577
|
-
const stateCheckpointPath = directorMode ? path8.join(fleetRoot, "director-state.json") : void 0;
|
|
15868
|
+
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path8.join(expectDefined17(fleetRoot), "fleet.json") : void 0;
|
|
15869
|
+
const sharedScratchpadPath = directorMode ? path8.join(expectDefined17(fleetRoot), "shared") : void 0;
|
|
15870
|
+
const subagentSessionsRoot = directorMode ? path8.join(expectDefined17(fleetRoot), "subagents") : void 0;
|
|
15871
|
+
const stateCheckpointPath = directorMode ? path8.join(expectDefined17(fleetRoot), "director-state.json") : void 0;
|
|
15578
15872
|
const fleetRootForPromotion = path8.join(wpaths.projectSessions, session.id);
|
|
15579
15873
|
const brainQueue = new BrainDecisionQueue(events);
|
|
15580
15874
|
const brain = new ObservableBrainArbiter(
|
|
@@ -15993,7 +16287,7 @@ async function main(argv) {
|
|
|
15993
16287
|
...matches.map((m) => ` ${m.subagentId} (${m.runId})`)
|
|
15994
16288
|
].join("\n");
|
|
15995
16289
|
}
|
|
15996
|
-
const t = matches[0];
|
|
16290
|
+
const t = expectDefined17(matches[0]);
|
|
15997
16291
|
const raw = await fsp4.readFile(t.file, "utf8");
|
|
15998
16292
|
if (mode === "raw") return raw;
|
|
15999
16293
|
const lines = raw.split("\n").filter((l) => l.trim());
|