@wrongstack/cli 0.63.4 → 0.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import { color, writeErr, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, renderTaskGraph, DefaultSecretScrubber, atomicWrite, DefaultPathResolver, TOKENS, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, SpecVersioning, 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, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, bootConfig as bootConfig$1, 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, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, InputBuilder, FsError } from '@wrongstack/core';
2
3
  import * as path8 from 'path';
3
4
  import { join } from 'path';
4
- import * as fsp3 from 'fs/promises';
5
- import { color, writeErr, DefaultTaskStore, TaskTracker, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, renderTaskGraph, SpecVersioning, DefaultSecretScrubber, atomicWrite, DefaultPathResolver, TOKENS, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, HookRegistry, HookRunner, SlashCommandRegistry, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, DefaultLogger, DefaultModelsRegistry, isStdinTTY, writeOut, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, resolveContextWindowPolicy, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, bootConfig as bootConfig$1, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, DefaultPluginAPI, 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, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, InputBuilder, FsError } from '@wrongstack/core';
5
+ import * as fsp4 from 'fs/promises';
6
6
  import { createRequire } from 'module';
7
7
  import * as os2 from 'os';
8
8
  import os2__default from 'os';
9
9
  import * as crypto2 from 'crypto';
10
10
  import { randomUUID } from 'crypto';
11
+ import { findFreePort, createHttpServer, openBrowser, registerInstance, unregisterInstance } from '@wrongstack/webui/server';
11
12
  import { DefaultSecretVault, decryptConfigSecrets, encryptConfigSecrets, isSecretField } from '@wrongstack/core/security';
12
13
  import { WebSocketServer, WebSocket } from 'ws';
13
14
  import { MCPRegistry, MCPServer, serveHttp, serveStdio } from '@wrongstack/mcp';
@@ -52,32 +53,8 @@ var __copyProps = (to, from, except, desc) => {
52
53
  return to;
53
54
  };
54
55
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
55
-
56
- // src/slash-commands/sdd.ts
57
- var sdd_exports = {};
58
- __export(sdd_exports, {
59
- advanceToNextTask: () => advanceToNextTask,
60
- autoDetectTaskCompletion: () => autoDetectTaskCompletion,
61
- buildSddCommand: () => buildSddCommand,
62
- getActiveBuilder: () => getActiveBuilder,
63
- getActiveSDDContext: () => getActiveSDDContext,
64
- getActiveSDDPhase: () => getActiveSDDPhase,
65
- getCurrentExecutingContext: () => getCurrentExecutingContext,
66
- getCurrentTask: () => getCurrentTask,
67
- getTaskGraphId: () => getTaskGraphId,
68
- getTaskListText: () => getTaskListText,
69
- getTaskProgress: () => getTaskProgress,
70
- getTaskTracker: () => getTaskTracker,
71
- markTaskCompleted: () => markTaskCompleted,
72
- renderTaskListWithProgress: () => renderTaskListWithProgress,
73
- trySaveImplementationPlan: () => trySaveImplementationPlan,
74
- trySaveSpecFromAIOutput: () => trySaveSpecFromAIOutput,
75
- trySaveTasksFromAIOutput: () => trySaveTasksFromAIOutput
76
- });
77
56
  function getSessionState(ctx) {
78
- if (!ctx) {
79
- return sddState;
80
- }
57
+ if (!ctx) return sddState;
81
58
  let state = ctx.meta[SDD_META_KEY];
82
59
  if (!state) {
83
60
  state = new SDDState();
@@ -85,19 +62,103 @@ function getSessionState(ctx) {
85
62
  }
86
63
  return state;
87
64
  }
88
- function getActiveSDDContext() {
89
- return sddState.getContext();
90
- }
91
- function getActiveSDDPhase() {
92
- return sddState.getPhase();
65
+ var SDD_META_KEY, SDDState, sddState;
66
+ var init_state = __esm({
67
+ "src/slash-commands/sdd/state.ts"() {
68
+ SDD_META_KEY = "sdd.state";
69
+ SDDState = class {
70
+ builder = null;
71
+ taskStore = null;
72
+ taskTracker = null;
73
+ taskGraphId = null;
74
+ sessionStartTime = Date.now();
75
+ phaseStartTime = Date.now();
76
+ versioning = null;
77
+ getBuilder() {
78
+ return this.builder;
79
+ }
80
+ setBuilder(b) {
81
+ this.builder = b;
82
+ }
83
+ getTaskStore() {
84
+ return this.taskStore;
85
+ }
86
+ setTaskStore(s) {
87
+ this.taskStore = s;
88
+ }
89
+ getTaskTracker() {
90
+ return this.taskTracker;
91
+ }
92
+ setTaskTracker(t) {
93
+ this.taskTracker = t;
94
+ }
95
+ getTaskGraphId() {
96
+ return this.taskGraphId;
97
+ }
98
+ setTaskGraphId(id) {
99
+ this.taskGraphId = id;
100
+ }
101
+ getSessionStartTime() {
102
+ return this.sessionStartTime;
103
+ }
104
+ setSessionStartTime(t) {
105
+ this.sessionStartTime = t;
106
+ }
107
+ setPhaseStartTime(t) {
108
+ this.phaseStartTime = t;
109
+ }
110
+ getPhaseStartTime() {
111
+ return this.phaseStartTime;
112
+ }
113
+ getSessionElapsed() {
114
+ return Date.now() - this.sessionStartTime;
115
+ }
116
+ getPhaseElapsed() {
117
+ return Date.now() - this.phaseStartTime;
118
+ }
119
+ getVersioning() {
120
+ if (this.versioning === null) this.versioning = new SpecVersioning();
121
+ return this.versioning;
122
+ }
123
+ clearTaskState() {
124
+ this.taskStore = null;
125
+ this.taskTracker = null;
126
+ this.taskGraphId = null;
127
+ }
128
+ getContext() {
129
+ if (!this.builder) return null;
130
+ const session = this.builder.getSession();
131
+ if (session.phase === "done") return null;
132
+ return this.builder.getAIPrompt();
133
+ }
134
+ getPhase() {
135
+ return this.builder?.getPhase() ?? null;
136
+ }
137
+ };
138
+ sddState = new SDDState();
139
+ }
140
+ });
141
+ function formatElapsed(ms) {
142
+ if (ms < 1e3) return `${ms}ms`;
143
+ const s = Math.floor(ms / 1e3);
144
+ if (s < 60) return `${s}s`;
145
+ const m = Math.floor(s / 60);
146
+ const remS = s % 60;
147
+ if (m < 60) return remS > 0 ? `${m}m ${remS}s` : `${m}m`;
148
+ const h = Math.floor(m / 60);
149
+ const remM = m % 60;
150
+ return `${h}h ${remM}m`;
93
151
  }
94
- async function trySaveSpecFromAIOutput(aiOutput) {
95
- const builder = sddState.getBuilder();
96
- if (!builder) return false;
97
- const spec = builder.tryParseSpecFromOutput(aiOutput);
98
- if (!spec) return false;
99
- builder.setSpec(spec);
100
- return true;
152
+ function addTaskToTracker(tracker, task) {
153
+ tracker.addNode({
154
+ title: String(task.title),
155
+ description: String(task.description ?? ""),
156
+ type: ["feature", "bugfix", "refactor", "docs", "test", "chore"].includes(String(task.type)) ? String(task.type) : "feature",
157
+ priority: ["critical", "high", "medium", "low"].includes(String(task.priority)) ? String(task.priority) : "medium",
158
+ status: "pending",
159
+ estimateHours: Number(task.estimateHours) || 2,
160
+ tags: Array.isArray(task.tags) ? task.tags.map(String) : []
161
+ });
101
162
  }
102
163
  async function trySaveTasksFromAIOutput(aiOutput) {
103
164
  const builder = sddState.getBuilder();
@@ -117,45 +178,13 @@ async function trySaveTasksFromAIOutput(aiOutput) {
117
178
  if (validTasks.length === 0) return false;
118
179
  const existingTracker = sddState.getTaskTracker();
119
180
  if (existingTracker) {
120
- for (const task of validTasks) {
121
- const title = String(task.title);
122
- const description = String(task.description ?? "");
123
- const type = ["feature", "bugfix", "refactor", "docs", "test", "chore"].includes(String(task.type)) ? String(task.type) : "feature";
124
- const priority = ["critical", "high", "medium", "low"].includes(String(task.priority)) ? String(task.priority) : "medium";
125
- const estimateHours = Number(task.estimateHours) || 2;
126
- const tags = Array.isArray(task.tags) ? task.tags.map(String) : [];
127
- existingTracker.addNode({
128
- title,
129
- description,
130
- type,
131
- priority,
132
- status: "pending",
133
- estimateHours,
134
- tags
135
- });
136
- }
181
+ for (const task of validTasks) addTaskToTracker(existingTracker, task);
137
182
  return true;
138
183
  }
139
184
  const store = new DefaultTaskStore();
140
185
  const tracker = new TaskTracker({ store });
141
186
  const graph = await tracker.createGraph(session.spec.id, session.spec.title);
142
- for (const task of validTasks) {
143
- const title = String(task.title);
144
- const description = String(task.description ?? "");
145
- const type = ["feature", "bugfix", "refactor", "docs", "test", "chore"].includes(String(task.type)) ? String(task.type) : "feature";
146
- const priority = ["critical", "high", "medium", "low"].includes(String(task.priority)) ? String(task.priority) : "medium";
147
- const estimateHours = Number(task.estimateHours) || 2;
148
- const tags = Array.isArray(task.tags) ? task.tags.map(String) : [];
149
- tracker.addNode({
150
- title,
151
- description,
152
- type,
153
- priority,
154
- status: "pending",
155
- estimateHours,
156
- tags
157
- });
158
- }
187
+ for (const task of validTasks) addTaskToTracker(tracker, task);
159
188
  sddState.setTaskStore(store);
160
189
  sddState.setTaskTracker(tracker);
161
190
  sddState.setTaskGraphId(graph.id);
@@ -173,15 +202,7 @@ function getCurrentTask() {
173
202
  const nodes = tracker.getAllNodes({ status: ["in_progress"] });
174
203
  if (nodes.length === 0) return null;
175
204
  const n = nodes[0];
176
- return {
177
- id: n.id,
178
- title: n.title,
179
- description: n.description,
180
- priority: n.priority,
181
- estimateHours: n.estimateHours ?? 0,
182
- tags: n.tags ?? [],
183
- startedAt: n.startedAt
184
- };
205
+ return { id: n.id, title: n.title, description: n.description, priority: n.priority, estimateHours: n.estimateHours ?? 0, tags: n.tags ?? [], startedAt: n.startedAt };
185
206
  }
186
207
  function advanceToNextTask() {
187
208
  const tracker = sddState.getTaskTracker();
@@ -195,27 +216,15 @@ function advanceToNextTask() {
195
216
  }
196
217
  return false;
197
218
  }
198
- function formatElapsed(ms) {
199
- if (ms < 1e3) return `${ms}ms`;
200
- const s = Math.floor(ms / 1e3);
201
- if (s < 60) return `${s}s`;
202
- const m = Math.floor(s / 60);
203
- const remS = s % 60;
204
- if (m < 60) return remS > 0 ? `${m}m ${remS}s` : `${m}m`;
205
- const h = Math.floor(m / 60);
206
- const remM = m % 60;
207
- return `${h}h ${remM}m`;
208
- }
209
219
  function getTaskListText() {
210
220
  const tracker = sddState.getTaskTracker();
211
221
  if (!tracker) return null;
212
222
  const nodes = tracker.getAllNodes();
213
223
  if (nodes.length === 0) return null;
214
- const lines = nodes.map((n, i) => {
224
+ return nodes.map((n, i) => {
215
225
  const status = n.status === "completed" ? "\u2705" : n.status === "in_progress" ? "\u{1F504}" : "\u23F3";
216
226
  return `${i + 1}. ${status} [${n.priority}] ${n.title}`;
217
- });
218
- return lines.join("\n");
227
+ }).join("\n");
219
228
  }
220
229
  function renderTaskListWithProgress() {
221
230
  const tracker = sddState.getTaskTracker();
@@ -224,20 +233,8 @@ function renderTaskListWithProgress() {
224
233
  if (nodes.length === 0) return null;
225
234
  const progress = tracker.getProgress();
226
235
  const phase = sddState.getPhase();
227
- const phaseLabel = {
228
- questioning: "\u2753 Questioning",
229
- spec_review: "\u{1F4CB} Spec Review",
230
- implementation: "\u{1F3D7}\uFE0F Implementation",
231
- task_review: "\u{1F4DD} Task Review",
232
- executing: "\u26A1 Executing",
233
- done: "\u2705 Done"
234
- };
235
- const lines = [
236
- `**${phaseLabel[phase ?? ""] ?? phase} \u2014 Task Status**`,
237
- "",
238
- renderProgress(progress),
239
- ""
240
- ];
236
+ const phaseLabel = { questioning: "\u2753 Questioning", spec_review: "\u{1F4CB} Spec Review", implementation: "\u{1F3D7}\uFE0F Implementation", task_review: "\u{1F4DD} Task Review", executing: "\u26A1 Executing", done: "\u2705 Done" };
237
+ const lines = [`**${phaseLabel[phase ?? ""] ?? phase} \u2014 Task Status**`, "", renderProgress(progress), ""];
241
238
  const sorted = [...nodes].sort((a, b) => {
242
239
  const order = { in_progress: 0, pending: 1, review: 2, blocked: 3, failed: 4, completed: 5 };
243
240
  return (order[a.status] ?? 6) - (order[b.status] ?? 6);
@@ -247,9 +244,7 @@ function renderTaskListWithProgress() {
247
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";
248
245
  const title = n.title.length > 50 ? n.title.slice(0, 49) + "\u2026" : n.title;
249
246
  let elapsed = "";
250
- if (n.status === "in_progress" && n.startedAt) {
251
- elapsed = ` \xB7 ${formatElapsed(Date.now() - n.startedAt)}`;
252
- }
247
+ if (n.status === "in_progress" && n.startedAt) elapsed = ` \xB7 ${formatElapsed(Date.now() - n.startedAt)}`;
253
248
  lines.push(`${i + 1}. ${status} ${title}${elapsed}`);
254
249
  }
255
250
  return lines.join("\n");
@@ -273,13 +268,114 @@ function markTaskCompleted(taskTitle) {
273
268
  const tracker = sddState.getTaskTracker();
274
269
  if (!tracker) return false;
275
270
  const nodes = tracker.getAllNodes({ status: ["pending", "in_progress"] });
276
- const match = nodes.find(
277
- (n) => n.title.toLowerCase().includes(taskTitle.toLowerCase()) || taskTitle.toLowerCase().includes(n.title.toLowerCase())
278
- );
271
+ const match = nodes.find((n) => n.title.toLowerCase().includes(taskTitle.toLowerCase()) || taskTitle.toLowerCase().includes(n.title.toLowerCase()));
279
272
  if (!match) return false;
280
273
  tracker.updateNodeStatus(match.id, "completed");
281
274
  return true;
282
275
  }
276
+ function getTaskGraphId() {
277
+ return sddState.getTaskGraphId();
278
+ }
279
+ function getTaskTrackerExport() {
280
+ return sddState.getTaskTracker();
281
+ }
282
+ var init_task_manager = __esm({
283
+ "src/slash-commands/sdd/task-manager.ts"() {
284
+ init_state();
285
+ }
286
+ });
287
+ function getActiveBuilder() {
288
+ return sddState.getBuilder();
289
+ }
290
+ function getActiveSDDContext() {
291
+ return sddState.getContext();
292
+ }
293
+ function getActiveSDDPhase() {
294
+ return sddState.getPhase();
295
+ }
296
+ async function findSpec(store, idOrTitle) {
297
+ if (!idOrTitle) return null;
298
+ const byId = await store.load(idOrTitle);
299
+ if (byId) return byId;
300
+ const all = await store.list();
301
+ const match = all.find(
302
+ (e) => e.id.startsWith(idOrTitle) || e.title.toLowerCase().includes(idOrTitle.toLowerCase())
303
+ );
304
+ if (match) return store.load(match.id);
305
+ return null;
306
+ }
307
+ async function gatherProjectContext2(projectRoot) {
308
+ const parts = [];
309
+ try {
310
+ const pkgPath = path8.join(projectRoot, "package.json");
311
+ const pkgRaw = await fsp4.readFile(pkgPath, "utf8");
312
+ const pkg = JSON.parse(pkgRaw);
313
+ parts.push(`Project: ${String(pkg.name ?? "unknown")}`);
314
+ parts.push(`Description: ${String(pkg.description ?? "none")}`);
315
+ if (pkg.dependencies) {
316
+ const deps = Object.keys(pkg.dependencies);
317
+ parts.push(`Dependencies: ${deps.slice(0, 20).join(", ")}${deps.length > 20 ? "..." : ""}`);
318
+ }
319
+ if (pkg.devDependencies) {
320
+ const devDeps = Object.keys(pkg.devDependencies);
321
+ parts.push(`Dev Dependencies: ${devDeps.slice(0, 15).join(", ")}${devDeps.length > 15 ? "..." : ""}`);
322
+ }
323
+ } catch {
324
+ }
325
+ try {
326
+ const tsconfigPath = path8.join(projectRoot, "tsconfig.json");
327
+ await fsp4.access(tsconfigPath);
328
+ parts.push("Language: TypeScript");
329
+ } catch {
330
+ }
331
+ try {
332
+ const srcDir = path8.join(projectRoot, "src");
333
+ const entries = await fsp4.readdir(srcDir, { withFileTypes: true });
334
+ const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
335
+ if (dirs.length > 0) parts.push(`Source structure: src/${dirs.join(", src/")}`);
336
+ } catch {
337
+ }
338
+ return parts.join("\n");
339
+ }
340
+ var init_project_context = __esm({
341
+ "src/slash-commands/sdd/project-context.ts"() {
342
+ init_state();
343
+ }
344
+ });
345
+
346
+ // src/slash-commands/sdd/spec-detection.ts
347
+ function isExplanatoryText(text) {
348
+ const lower = text.toLowerCase();
349
+ return lower.startsWith("i'") || lower.startsWith("i will") || lower.startsWith("let me") || lower.startsWith("here's my") || lower.startsWith("here is my") || lower.startsWith("i'm going to") || lower.startsWith("first, let me") || lower.startsWith("sure") || lower.startsWith("of course") || lower.startsWith("okay") || lower.startsWith("ok,") || lower.startsWith("sounds good") || lower.startsWith("no problem") || text.split("\n").length < 3 && !text.includes(".");
350
+ }
351
+ async function trySaveSpecFromAIOutput(aiOutput) {
352
+ const builder = sddState.getBuilder();
353
+ if (!builder) return false;
354
+ const spec = builder.tryParseSpecFromOutput(aiOutput);
355
+ if (!spec) return false;
356
+ builder.setSpec(spec);
357
+ return true;
358
+ }
359
+ function trySaveImplementationPlan(aiOutput) {
360
+ const builder = sddState.getBuilder();
361
+ if (!builder) return false;
362
+ const session = builder.getSession();
363
+ if (session.phase !== "implementation") return false;
364
+ const current = session.implementation ?? "";
365
+ const jsonMatch = aiOutput.match(/```json\s*\[/);
366
+ if (jsonMatch?.index && jsonMatch.index > 0) {
367
+ const plan = aiOutput.substring(0, jsonMatch.index).trim();
368
+ if (plan.length > 50 && plan !== current && !isExplanatoryText(plan)) {
369
+ builder.setImplementation(plan);
370
+ return true;
371
+ }
372
+ }
373
+ if (aiOutput.length > 100 && !aiOutput.includes("```json") && aiOutput !== current && !isExplanatoryText(aiOutput)) {
374
+ builder.setImplementation(aiOutput.trim());
375
+ return true;
376
+ }
377
+ return false;
378
+ }
283
379
  function autoDetectTaskCompletion(aiOutput) {
284
380
  const tracker = sddState.getTaskTracker();
285
381
  if (!tracker) return 0;
@@ -300,9 +396,7 @@ function autoDetectTaskCompletion(aiOutput) {
300
396
  completed++;
301
397
  }
302
398
  } else {
303
- const match = pending.find(
304
- (n) => n.title.toLowerCase().includes(target.toLowerCase()) || target.toLowerCase().includes(n.title.toLowerCase())
305
- );
399
+ const match = pending.find((n) => n.title.toLowerCase().includes(target.toLowerCase()) || target.toLowerCase().includes(n.title.toLowerCase()));
306
400
  if (match && match.status !== "completed") {
307
401
  tracker.updateNodeStatus(match.id, "completed");
308
402
  completed++;
@@ -313,9 +407,7 @@ function autoDetectTaskCompletion(aiOutput) {
313
407
  const checkmarkMatch = trimmed.match(/^✅\s*(?:Task:\s*)?(.+)/i);
314
408
  if (checkmarkMatch?.[1]) {
315
409
  const title = checkmarkMatch[1].trim();
316
- const match = pending.find(
317
- (n) => n.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(n.title.toLowerCase())
318
- );
410
+ const match = pending.find((n) => n.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(n.title.toLowerCase()));
319
411
  if (match && match.status !== "completed") {
320
412
  tracker.updateNodeStatus(match.id, "completed");
321
413
  completed++;
@@ -337,9 +429,7 @@ function autoDetectTaskCompletion(aiOutput) {
337
429
  const completedMatch = trimmed.match(/^(?:Completed|Done|Finished)\s*[:]\s*(.+)/i);
338
430
  if (completedMatch?.[1]) {
339
431
  const title = completedMatch[1].trim();
340
- const match = pending.find(
341
- (n) => n.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(n.title.toLowerCase())
342
- );
432
+ const match = pending.find((n) => n.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(n.title.toLowerCase()));
343
433
  if (match && match.status !== "completed") {
344
434
  tracker.updateNodeStatus(match.id, "completed");
345
435
  completed++;
@@ -348,33 +438,132 @@ function autoDetectTaskCompletion(aiOutput) {
348
438
  }
349
439
  return completed;
350
440
  }
351
- function trySaveImplementationPlan(aiOutput) {
352
- const builder = sddState.getBuilder();
353
- if (!builder) return false;
354
- const session = builder.getSession();
355
- if (session.phase !== "implementation") return false;
356
- const current = session.implementation ?? "";
357
- const jsonMatch = aiOutput.match(/```json\s*\[/);
358
- if (jsonMatch?.index && jsonMatch.index > 0) {
359
- const plan = aiOutput.substring(0, jsonMatch.index).trim();
360
- if (plan.length > 50 && plan !== current && !isExplanatoryText(plan)) {
361
- builder.setImplementation(plan);
362
- return true;
363
- }
364
- }
365
- if (aiOutput.length > 100 && !aiOutput.includes("```json") && aiOutput !== current && !isExplanatoryText(aiOutput)) {
366
- builder.setImplementation(aiOutput.trim());
367
- return true;
441
+ var init_spec_detection = __esm({
442
+ "src/slash-commands/sdd/spec-detection.ts"() {
443
+ init_state();
368
444
  }
369
- return false;
370
- }
371
- function isExplanatoryText(text) {
372
- const lower = text.toLowerCase();
373
- return lower.startsWith("i'") || lower.startsWith("i will") || lower.startsWith("let me") || lower.startsWith("here's my") || lower.startsWith("here is my") || lower.startsWith("i'm going to") || lower.startsWith("first, let me") || lower.startsWith("sure") || lower.startsWith("of course") || lower.startsWith("okay") || lower.startsWith("ok,") || lower.startsWith("sounds good") || lower.startsWith("no problem") || // Skip if mostly code-like with minimal prose
374
- text.split("\n").length < 3 && !text.includes(".");
445
+ });
446
+
447
+ // src/slash-commands/sdd/rendering.ts
448
+ function sddHelp() {
449
+ return [
450
+ "",
451
+ "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557",
452
+ "\u2551 \u{1F680} SDD \u2014 AI-Driven Spec Builder \u2551",
453
+ "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D",
454
+ "",
455
+ " \u250C\u2500 \u{1F195} Start \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
456
+ " \u2502 /sdd new [title] Start a new spec session \u2502",
457
+ " \u2502 /sdd new --force Start fresh (skip resume check) \u2502",
458
+ " \u2502 /sdd resume Resume a saved session \u2502",
459
+ " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
460
+ "",
461
+ " \u250C\u2500 \u{1F504} Flow \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
462
+ " \u2502 /sdd approve Approve current phase \u2502",
463
+ " \u2502 /sdd spec Show current session's spec \u2502",
464
+ " \u2502 /sdd plan Show implementation plan \u2502",
465
+ " \u2502 /sdd execute Execute generated tasks \u2502",
466
+ " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
467
+ "",
468
+ " \u250C\u2500 \u23F8 Goal Lifecycle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
469
+ " \u2502 /sdd goal Show current goal + journal \u2502",
470
+ " \u2502 /sdd goal set <text> Set autonomous mission \u2502",
471
+ " \u2502 /sdd goal pause Pause at end of current iteration \u2502",
472
+ " \u2502 /sdd goal resume Resume a paused goal \u2502",
473
+ " \u2502 /sdd goal journal [N] Show recent journal entries \u2502",
474
+ " \u2502 /sdd goal clear Clear goal + stop eternal mode \u2502",
475
+ " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
476
+ "",
477
+ " \u250C\u2500 \u{1F4E1} Eternal Stage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
478
+ " \u2502 decide \u2192 execute \u2192 reflect \u2192 sleep | paused | stopped \u2502",
479
+ " \u2502 Stage shown in real-time during /sdd goal mode \u2502",
480
+ " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
481
+ "",
482
+ " \u250C\u2500 \u{1F527} Task Lifecycle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
483
+ " \u2502 /sdd tasks Show task list + progress bar \u2502",
484
+ " \u2502 /sdd next Show next executable task \u2502",
485
+ " \u2502 /sdd done <N> Complete a task \u2502",
486
+ " \u2502 /sdd skip <N> Skip a task (back to pending) \u2502",
487
+ " \u2502 /sdd fail <N> Mark task as failed \u2502",
488
+ " \u2502 /sdd review <N> Send task to review \u2502",
489
+ " \u2502 /sdd edit <N> <txt> Edit task title or description \u2502",
490
+ " \u2502 /sdd undo Undo last completion \u2502",
491
+ " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
492
+ "",
493
+ " \u250C\u2500 \u{1F4CA} Session Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
494
+ " \u2502 /sdd status Full session status + tasks preview \u2502",
495
+ " \u2502 /sdd cancel Cancel session \u2502",
496
+ " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
497
+ "",
498
+ " \u250C\u2500 \u{1F4C1} Spec History \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
499
+ " \u2502 /sdd list List saved specs \u2502",
500
+ " \u2502 /sdd show <id> Show spec details \u2502",
501
+ " \u2502 /sdd templates List available templates \u2502",
502
+ " \u2502 /sdd from <tmpl> Create from template \u2502",
503
+ " \u2502 /sdd version <id> Show version history \u2502",
504
+ " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
505
+ "",
506
+ " \u250C\u2500 \u{1F4A1} Quick Start \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
507
+ " \u2502 \u2502",
508
+ " \u2502 1. /sdd new Auth System \u2502",
509
+ " \u2502 \u2192 AI starts asking questions \u2502",
510
+ " \u2502 \u2502",
511
+ " \u2502 2. Just type your answers naturally \u2502",
512
+ " \u2502 \u2192 AI continues the interview \u2502",
513
+ " \u2502 \u2502",
514
+ " \u2502 3. AI generates spec (auto-detected) \u2502",
515
+ " \u2502 \u2192 /sdd approve \u2502",
516
+ " \u2502 \u2502",
517
+ " \u2502 3. AI generates implementation + tasks \u2502",
518
+ " \u2502 \u2192 /sdd approve \u2502",
519
+ " \u2502 \u2502",
520
+ " \u2502 4. AI executes tasks one by one \u2502",
521
+ " \u2502 \u2192 /sdd tasks (view progress) \u2502",
522
+ " \u2502 \u2192 /sdd done 1 (mark task complete) \u2502",
523
+ " \u2502 \u2502",
524
+ " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
525
+ "",
526
+ " Tip: tasks are shown with progress bar after each AI turn.",
527
+ ""
528
+ ].join("\n");
375
529
  }
376
- function getActiveBuilder() {
377
- return sddState.getBuilder();
530
+ var init_rendering = __esm({
531
+ "src/slash-commands/sdd/rendering.ts"() {
532
+ }
533
+ });
534
+
535
+ // src/slash-commands/sdd.ts
536
+ var sdd_exports = {};
537
+ __export(sdd_exports, {
538
+ SDDState: () => SDDState,
539
+ advanceToNextTask: () => advanceToNextTask,
540
+ autoDetectTaskCompletion: () => autoDetectTaskCompletion,
541
+ buildSddCommand: () => buildSddCommand,
542
+ findSpec: () => findSpec,
543
+ formatElapsed: () => formatElapsed,
544
+ gatherProjectContext: () => gatherProjectContext2,
545
+ getActiveBuilder: () => getActiveBuilder,
546
+ getActiveSDDContext: () => getActiveSDDContext,
547
+ getActiveSDDPhase: () => getActiveSDDPhase,
548
+ getCurrentExecutingContext: () => getCurrentExecutingContext,
549
+ getCurrentTask: () => getCurrentTask,
550
+ getSessionState: () => getSessionState,
551
+ getTaskGraphId: () => getTaskGraphId,
552
+ getTaskListText: () => getTaskListText,
553
+ getTaskProgress: () => getTaskProgress,
554
+ getTaskTracker: () => getTaskTracker,
555
+ getTaskTrackerExport: () => getTaskTrackerExport,
556
+ isExplanatoryText: () => isExplanatoryText,
557
+ markTaskCompleted: () => markTaskCompleted,
558
+ renderProgress: () => renderProgress,
559
+ renderTaskListWithProgress: () => renderTaskListWithProgress,
560
+ sddState: () => sddState,
561
+ trySaveImplementationPlan: () => trySaveImplementationPlan,
562
+ trySaveSpecFromAIOutput: () => trySaveSpecFromAIOutput,
563
+ trySaveTasksFromAIOutput: () => trySaveTasksFromAIOutput
564
+ });
565
+ function getTaskTracker() {
566
+ return getTaskTrackerExport();
378
567
  }
379
568
  function buildSddCommand(opts) {
380
569
  const sessionState = getSessionState(opts.context);
@@ -400,7 +589,7 @@ function buildSddCommand(opts) {
400
589
  if (!sessionState.getBuilder() && !forceFlag) {
401
590
  const sessionPath = opts.paths.projectSddSession;
402
591
  try {
403
- await fsp3.access(sessionPath);
592
+ await fsp4.access(sessionPath);
404
593
  const projectContext2 = await gatherProjectContext2(opts.context?.projectRoot ?? process.cwd());
405
594
  const tempBuilder = new AISpecBuilder({
406
595
  store: specStore,
@@ -1051,16 +1240,16 @@ Start executing the tasks one by one.`
1051
1240
  const sessionPath = opts.paths.projectSddSession;
1052
1241
  let deletedFromDisk = false;
1053
1242
  try {
1054
- await fsp3.unlink(sessionPath);
1243
+ await fsp4.unlink(sessionPath);
1055
1244
  deletedFromDisk = true;
1056
1245
  } catch {
1057
1246
  }
1058
1247
  try {
1059
- await fsp3.rm(opts.paths.projectSpecs, { recursive: true, force: true });
1248
+ await fsp4.rm(opts.paths.projectSpecs, { recursive: true, force: true });
1060
1249
  } catch {
1061
1250
  }
1062
1251
  try {
1063
- await fsp3.rm(opts.paths.projectTaskGraphs, { recursive: true, force: true });
1252
+ await fsp4.rm(opts.paths.projectTaskGraphs, { recursive: true, force: true });
1064
1253
  } catch {
1065
1254
  }
1066
1255
  const cancelBuilder = sddState.getBuilder();
@@ -1298,228 +1487,29 @@ ${lines.join("\n")}`
1298
1487
  return { message: lines.join("\n") };
1299
1488
  } catch {
1300
1489
  return { message: "Could not analyze critical path." };
1301
- }
1302
- }
1303
- default:
1304
- return {
1305
- message: `Unknown command "${verb}".
1306
-
1307
- ${sddHelp()}`
1308
- };
1309
- }
1310
- }
1311
- };
1312
- }
1313
- function sddHelp() {
1314
- return [
1315
- "",
1316
- "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557",
1317
- "\u2551 \u{1F680} SDD \u2014 AI-Driven Spec Builder \u2551",
1318
- "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D",
1319
- "",
1320
- " \u250C\u2500 \u{1F195} Start \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
1321
- " \u2502 /sdd new [title] Start a new spec session \u2502",
1322
- " \u2502 /sdd new --force Start fresh (skip resume check) \u2502",
1323
- " \u2502 /sdd resume Resume a saved session \u2502",
1324
- " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
1325
- "",
1326
- " \u250C\u2500 \u{1F504} Flow \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
1327
- " \u2502 /sdd approve Approve current phase \u2502",
1328
- " \u2502 /sdd spec Show current session's spec \u2502",
1329
- " \u2502 /sdd plan Show implementation plan \u2502",
1330
- " \u2502 /sdd execute Execute generated tasks \u2502",
1331
- " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
1332
- "",
1333
- " \u250C\u2500 \u23F8 Goal Lifecycle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
1334
- " \u2502 /sdd goal Show current goal + journal \u2502",
1335
- " \u2502 /sdd goal set <text> Set autonomous mission \u2502",
1336
- " \u2502 /sdd goal pause Pause at end of current iteration \u2502",
1337
- " \u2502 /sdd goal resume Resume a paused goal \u2502",
1338
- " \u2502 /sdd goal journal [N] Show recent journal entries \u2502",
1339
- " \u2502 /sdd goal clear Clear goal + stop eternal mode \u2502",
1340
- " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
1341
- "",
1342
- " \u250C\u2500 \u{1F4E1} Eternal Stage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
1343
- " \u2502 decide \u2192 execute \u2192 reflect \u2192 sleep | paused | stopped \u2502",
1344
- " \u2502 Stage shown in real-time during /sdd goal mode \u2502",
1345
- " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
1346
- "",
1347
- " \u250C\u2500 \u{1F527} Task Lifecycle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
1348
- " \u2502 /sdd tasks Show task list + progress bar \u2502",
1349
- " \u2502 /sdd next Show next executable task \u2502",
1350
- " \u2502 /sdd done <N> Complete a task \u2502",
1351
- " \u2502 /sdd skip <N> Skip a task (back to pending) \u2502",
1352
- " \u2502 /sdd fail <N> Mark task as failed \u2502",
1353
- " \u2502 /sdd review <N> Send task to review \u2502",
1354
- " \u2502 /sdd edit <N> <txt> Edit task title or description \u2502",
1355
- " \u2502 /sdd undo Undo last completion \u2502",
1356
- " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
1357
- "",
1358
- " \u250C\u2500 \u{1F4CA} Session Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
1359
- " \u2502 /sdd status Full session status + tasks preview \u2502",
1360
- " \u2502 /sdd cancel Cancel session \u2502",
1361
- " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
1362
- "",
1363
- " \u250C\u2500 \u{1F4C1} Spec History \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
1364
- " \u2502 /sdd list List saved specs \u2502",
1365
- " \u2502 /sdd show <id> Show spec details \u2502",
1366
- " \u2502 /sdd templates List available templates \u2502",
1367
- " \u2502 /sdd from <tmpl> Create from template \u2502",
1368
- " \u2502 /sdd version <id> Show version history \u2502",
1369
- " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
1370
- "",
1371
- " \u250C\u2500 \u{1F4A1} Quick Start \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
1372
- " \u2502 \u2502",
1373
- " \u2502 1. /sdd new Auth System \u2502",
1374
- " \u2502 \u2192 AI starts asking questions \u2502",
1375
- " \u2502 \u2502",
1376
- " \u2502 2. Just type your answers naturally \u2502",
1377
- " \u2502 \u2192 AI continues the interview \u2502",
1378
- " \u2502 \u2502",
1379
- " \u2502 3. AI generates spec (auto-detected) \u2502",
1380
- " \u2502 \u2192 /sdd approve \u2502",
1381
- " \u2502 \u2502",
1382
- " \u2502 3. AI generates implementation + tasks \u2502",
1383
- " \u2502 \u2192 /sdd approve \u2502",
1384
- " \u2502 \u2502",
1385
- " \u2502 4. AI executes tasks one by one \u2502",
1386
- " \u2502 \u2192 /sdd tasks (view progress) \u2502",
1387
- " \u2502 \u2192 /sdd done 1 (mark task complete) \u2502",
1388
- " \u2502 \u2502",
1389
- " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
1390
- "",
1391
- " Tip: tasks are shown with progress bar after each AI turn.",
1392
- ""
1393
- ].join("\n");
1394
- }
1395
- async function gatherProjectContext2(projectRoot) {
1396
- const parts = [];
1397
- try {
1398
- const pkgPath = path8.join(projectRoot, "package.json");
1399
- const pkgRaw = await fsp3.readFile(pkgPath, "utf8");
1400
- const pkg = JSON.parse(pkgRaw);
1401
- parts.push(`Project: ${String(pkg.name ?? "unknown")}`);
1402
- parts.push(`Description: ${String(pkg.description ?? "none")}`);
1403
- if (pkg.dependencies) {
1404
- const deps = Object.keys(pkg.dependencies);
1405
- parts.push(`Dependencies: ${deps.slice(0, 20).join(", ")}${deps.length > 20 ? "..." : ""}`);
1406
- }
1407
- if (pkg.devDependencies) {
1408
- const devDeps = Object.keys(pkg.devDependencies);
1409
- parts.push(`Dev Dependencies: ${devDeps.slice(0, 15).join(", ")}${devDeps.length > 15 ? "..." : ""}`);
1410
- }
1411
- } catch {
1412
- }
1413
- try {
1414
- const tsconfigPath = path8.join(projectRoot, "tsconfig.json");
1415
- await fsp3.access(tsconfigPath);
1416
- parts.push("Language: TypeScript");
1417
- } catch {
1418
- }
1419
- try {
1420
- const srcDir = path8.join(projectRoot, "src");
1421
- const entries = await fsp3.readdir(srcDir, { withFileTypes: true });
1422
- const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
1423
- if (dirs.length > 0) {
1424
- parts.push(`Source structure: src/${dirs.join(", src/")}`);
1425
- }
1426
- } catch {
1427
- }
1428
- return parts.join("\n");
1429
- }
1430
- async function findSpec(store, idOrTitle) {
1431
- if (!idOrTitle) return null;
1432
- const byId = await store.load(idOrTitle);
1433
- if (byId) return byId;
1434
- const all = await store.list();
1435
- const match = all.find(
1436
- (e) => e.id.startsWith(idOrTitle) || e.title.toLowerCase().includes(idOrTitle.toLowerCase())
1437
- );
1438
- if (match) return store.load(match.id);
1439
- return null;
1440
- }
1441
- function getTaskGraphId() {
1442
- return sddState.getTaskGraphId();
1443
- }
1444
- function getTaskTracker() {
1445
- return sddState.getTaskTracker();
1490
+ }
1491
+ }
1492
+ default:
1493
+ return {
1494
+ message: `Unknown command "${verb}".
1495
+
1496
+ ${sddHelp()}`
1497
+ };
1498
+ }
1499
+ }
1500
+ };
1446
1501
  }
1447
- var SDD_META_KEY, SDDState, sddState;
1448
1502
  var init_sdd = __esm({
1449
1503
  "src/slash-commands/sdd.ts"() {
1450
- SDD_META_KEY = "sdd.state";
1451
- SDDState = class {
1452
- builder = null;
1453
- taskStore = null;
1454
- taskTracker = null;
1455
- taskGraphId = null;
1456
- sessionStartTime = Date.now();
1457
- phaseStartTime = Date.now();
1458
- versioning = null;
1459
- getBuilder() {
1460
- return this.builder;
1461
- }
1462
- setBuilder(b) {
1463
- this.builder = b;
1464
- }
1465
- getTaskStore() {
1466
- return this.taskStore;
1467
- }
1468
- setTaskStore(s) {
1469
- this.taskStore = s;
1470
- }
1471
- getTaskTracker() {
1472
- return this.taskTracker;
1473
- }
1474
- setTaskTracker(t) {
1475
- this.taskTracker = t;
1476
- }
1477
- getTaskGraphId() {
1478
- return this.taskGraphId;
1479
- }
1480
- setTaskGraphId(id) {
1481
- this.taskGraphId = id;
1482
- }
1483
- getSessionStartTime() {
1484
- return this.sessionStartTime;
1485
- }
1486
- setSessionStartTime(t) {
1487
- this.sessionStartTime = t;
1488
- }
1489
- setPhaseStartTime(t) {
1490
- this.phaseStartTime = t;
1491
- }
1492
- getPhaseStartTime() {
1493
- return this.phaseStartTime;
1494
- }
1495
- getSessionElapsed() {
1496
- return Date.now() - this.sessionStartTime;
1497
- }
1498
- getPhaseElapsed() {
1499
- return Date.now() - this.phaseStartTime;
1500
- }
1501
- getVersioning() {
1502
- if (this.versioning === null) {
1503
- this.versioning = new SpecVersioning();
1504
- }
1505
- return this.versioning;
1506
- }
1507
- clearTaskState() {
1508
- this.taskStore = null;
1509
- this.taskTracker = null;
1510
- this.taskGraphId = null;
1511
- }
1512
- getContext() {
1513
- if (!this.builder) return null;
1514
- const session = this.builder.getSession();
1515
- if (session.phase === "done") return null;
1516
- return this.builder.getAIPrompt();
1517
- }
1518
- getPhase() {
1519
- return this.builder?.getPhase() ?? null;
1520
- }
1521
- };
1522
- sddState = new SDDState();
1504
+ init_state();
1505
+ init_task_manager();
1506
+ init_project_context();
1507
+ init_state();
1508
+ init_spec_detection();
1509
+ init_task_manager();
1510
+ init_project_context();
1511
+ init_task_manager();
1512
+ init_rendering();
1523
1513
  }
1524
1514
  });
1525
1515
  function normalizeKeys(cfg) {
@@ -1600,7 +1590,7 @@ function isNewer(a, b) {
1600
1590
  }
1601
1591
  async function readCache(homeFn = defaultHomeDir2) {
1602
1592
  try {
1603
- const raw = await fsp3.readFile(cachePath(homeFn), "utf8");
1593
+ const raw = await fsp4.readFile(cachePath(homeFn), "utf8");
1604
1594
  const entry = JSON.parse(raw);
1605
1595
  if (Date.now() - entry.timestamp > CACHE_TTL_MS) return null;
1606
1596
  return entry;
@@ -1611,8 +1601,8 @@ async function readCache(homeFn = defaultHomeDir2) {
1611
1601
  async function writeCache(entry, homeFn = defaultHomeDir2) {
1612
1602
  try {
1613
1603
  const dir = path8.dirname(cachePath(homeFn));
1614
- await fsp3.mkdir(dir, { recursive: true });
1615
- await fsp3.writeFile(cachePath(homeFn), JSON.stringify(entry, null, 2), "utf8");
1604
+ await fsp4.mkdir(dir, { recursive: true });
1605
+ await fsp4.writeFile(cachePath(homeFn), JSON.stringify(entry, null, 2), "utf8");
1616
1606
  } catch {
1617
1607
  }
1618
1608
  }
@@ -1694,13 +1684,63 @@ __export(webui_server_exports, {
1694
1684
  runWebUI: () => runWebUI
1695
1685
  });
1696
1686
  async function runWebUI(opts) {
1697
- const port = opts.port ?? 3457;
1687
+ const host = "127.0.0.1";
1688
+ const requestedWsPort = opts.port ?? 3457;
1689
+ const requestedHttpPort = opts.httpPort ?? 3456;
1690
+ const strictPort = process.env["WEBUI_STRICT_PORT"] === "1" || process.env["WEBUI_STRICT_PORT"] === "true";
1691
+ let httpPort = requestedHttpPort;
1692
+ let wsPort = requestedWsPort;
1693
+ if (!strictPort) {
1694
+ httpPort = await findFreePort(host, requestedHttpPort);
1695
+ wsPort = await findFreePort(host, requestedWsPort, { exclude: /* @__PURE__ */ new Set([httpPort]) });
1696
+ }
1697
+ const port = wsPort;
1698
+ const rateLimitMax = Number.parseInt(process.env["WEBUI_RATE_LIMIT"] ?? "0", 10);
1698
1699
  const clients = /* @__PURE__ */ new Map();
1700
+ const pendingConfirms = /* @__PURE__ */ new Map();
1699
1701
  const secretScrubber = new DefaultSecretScrubber();
1700
1702
  let abortController = null;
1701
1703
  const authToken = crypto2.randomBytes(16).toString("hex");
1702
- const wss = new WebSocketServer({ port, host: "127.0.0.1", maxPayload: 1 * 1024 * 1024 });
1703
- console.log(`[WebUI] WebSocket server starting on ws://127.0.0.1:${port}`);
1704
+ const wss = new WebSocketServer({ port, host, maxPayload: 1 * 1024 * 1024 });
1705
+ console.log(`[WebUI] WebSocket server starting on ws://${host}:${port}`);
1706
+ let httpServer = null;
1707
+ try {
1708
+ const requireFromHere = createRequire(import.meta.url);
1709
+ const serverEntry = requireFromHere.resolve("@wrongstack/webui/server");
1710
+ const distDir = path8.resolve(path8.dirname(serverEntry), "..");
1711
+ httpServer = createHttpServer({ host, distDir, wsPort });
1712
+ const openUrl = `http://${host}:${httpPort}`;
1713
+ httpServer.listen(httpPort, host, () => {
1714
+ console.log(
1715
+ `
1716
+ \u25B8 WebUI ready \u2014 open \x1B[1m${openUrl}\x1B[0m in your browser
1717
+ (same agent as this terminal \xB7 ws:${wsPort})
1718
+ `
1719
+ );
1720
+ if (opts.open) openBrowser(openUrl);
1721
+ });
1722
+ } catch (err) {
1723
+ console.warn(
1724
+ `[WebUI] Frontend not served (run \`pnpm --filter @wrongstack/webui build\`): ${err instanceof Error ? err.message : String(err)}. WS bridge still active on ws://${host}:${wsPort}.`
1725
+ );
1726
+ }
1727
+ const registryBaseDir = opts.globalConfigPath ? path8.dirname(opts.globalConfigPath) : void 0;
1728
+ if (opts.projectRoot) {
1729
+ void registerInstance(
1730
+ {
1731
+ pid: process.pid,
1732
+ httpPort,
1733
+ wsPort,
1734
+ host,
1735
+ projectRoot: opts.projectRoot,
1736
+ projectName: path8.basename(opts.projectRoot) || opts.projectRoot,
1737
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1738
+ url: `http://${host}:${httpPort}`
1739
+ },
1740
+ registryBaseDir
1741
+ ).catch(() => {
1742
+ });
1743
+ }
1704
1744
  const eventUnsubscribers = [];
1705
1745
  function setupEvents() {
1706
1746
  for (const unsub of eventUnsubscribers) unsub();
@@ -1794,6 +1834,88 @@ async function runWebUI(opts) {
1794
1834
  });
1795
1835
  })
1796
1836
  );
1837
+ eventUnsubscribers.push(
1838
+ opts.events.on("tool.confirm_needed", (e) => {
1839
+ const id = e.toolUseId ?? `confirm_${Date.now()}`;
1840
+ pendingConfirms.set(id, e.resolve);
1841
+ broadcast({
1842
+ type: "tool.confirm_needed",
1843
+ payload: {
1844
+ id,
1845
+ toolName: e.tool?.name ?? "unknown",
1846
+ input: secretScrubber.scrubObject(e.input),
1847
+ suggestedPattern: e.suggestedPattern
1848
+ }
1849
+ });
1850
+ })
1851
+ );
1852
+ const forwardSubagent = (kind, payload) => broadcast({ type: "subagent.event", payload: { kind, ...payload } });
1853
+ eventUnsubscribers.push(
1854
+ opts.events.on(
1855
+ "subagent.spawned",
1856
+ (e) => forwardSubagent("spawned", {
1857
+ subagentId: e.subagentId,
1858
+ taskId: e.taskId,
1859
+ name: e.name,
1860
+ provider: e.provider,
1861
+ model: e.model,
1862
+ description: e.description
1863
+ })
1864
+ ),
1865
+ opts.events.on(
1866
+ "subagent.task_started",
1867
+ (e) => forwardSubagent("task_started", {
1868
+ subagentId: e.subagentId,
1869
+ taskId: e.taskId,
1870
+ description: e.description
1871
+ })
1872
+ ),
1873
+ opts.events.on(
1874
+ "subagent.tool_executed",
1875
+ (e) => forwardSubagent("tool_executed", {
1876
+ subagentId: e.subagentId,
1877
+ toolName: e.name,
1878
+ durationMs: e.durationMs,
1879
+ ok: e.ok
1880
+ })
1881
+ ),
1882
+ opts.events.on(
1883
+ "subagent.iteration_summary",
1884
+ (e) => forwardSubagent("iteration_summary", {
1885
+ subagentId: e.subagentId,
1886
+ iteration: e.iteration,
1887
+ toolCalls: e.toolCalls,
1888
+ costUsd: e.costUsd,
1889
+ currentTool: e.currentTool
1890
+ })
1891
+ ),
1892
+ opts.events.on(
1893
+ "subagent.budget_extended",
1894
+ (e) => forwardSubagent("budget_extended", {
1895
+ subagentId: e.subagentId,
1896
+ totalExtensions: e.totalExtensions
1897
+ })
1898
+ ),
1899
+ opts.events.on(
1900
+ "subagent.ctx_pct",
1901
+ (e) => forwardSubagent("ctx_pct", {
1902
+ subagentId: e.subagentId,
1903
+ load: e.load,
1904
+ tokens: e.tokens,
1905
+ maxContext: e.maxContext
1906
+ })
1907
+ ),
1908
+ opts.events.on(
1909
+ "subagent.task_completed",
1910
+ (e) => forwardSubagent("task_completed", {
1911
+ subagentId: e.subagentId,
1912
+ status: e.status,
1913
+ iterations: e.iterations,
1914
+ toolCalls: e.toolCalls,
1915
+ error: e.error ? { kind: e.error.kind, message: e.error.message } : void 0
1916
+ })
1917
+ )
1918
+ );
1797
1919
  if (opts.subscribeEternalIteration) {
1798
1920
  eventUnsubscribers.push(
1799
1921
  opts.subscribeEternalIteration((entry) => {
@@ -1814,10 +1936,11 @@ async function runWebUI(opts) {
1814
1936
  );
1815
1937
  }
1816
1938
  }
1817
- return new Promise((resolve3) => {
1939
+ return new Promise((resolve4) => {
1818
1940
  wss.on("listening", () => {
1819
- console.log(`[WebUI] WebSocket server running on ws://127.0.0.1:${port}`);
1941
+ console.log(`[WebUI] WebSocket server running on ws://${host}:${port}`);
1820
1942
  setupEvents();
1943
+ opts.onListening?.({ httpPort, wsPort, host });
1821
1944
  });
1822
1945
  wss.on("connection", (ws, req2) => {
1823
1946
  const isLoopback = (hostname) => hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
@@ -1870,17 +1993,19 @@ async function runWebUI(opts) {
1870
1993
  let msgCount = 0;
1871
1994
  let windowResetAt = Date.now() + 6e4;
1872
1995
  ws.on("message", async (data) => {
1873
- const now = Date.now();
1874
- if (now > windowResetAt) {
1875
- msgCount = 0;
1876
- windowResetAt = now + 6e4;
1877
- }
1878
- if (++msgCount > 60) {
1879
- send(ws, {
1880
- type: "error",
1881
- payload: { phase: "rate_limit", message: "Too many messages. Please wait." }
1882
- });
1883
- return;
1996
+ if (rateLimitMax > 0) {
1997
+ const now = Date.now();
1998
+ if (now > windowResetAt) {
1999
+ msgCount = 0;
2000
+ windowResetAt = now + 6e4;
2001
+ }
2002
+ if (++msgCount > rateLimitMax) {
2003
+ send(ws, {
2004
+ type: "error",
2005
+ payload: { phase: "rate_limit", message: "Too many messages. Please wait." }
2006
+ });
2007
+ return;
2008
+ }
1884
2009
  }
1885
2010
  try {
1886
2011
  const msg = JSON.parse(data.toString());
@@ -1892,6 +2017,12 @@ async function runWebUI(opts) {
1892
2017
  ws.on("close", () => {
1893
2018
  console.log("[WebUI] Client disconnected");
1894
2019
  clients.delete(ws);
2020
+ if (clients.size === 0 && pendingConfirms.size > 0) {
2021
+ for (const [id, resolve5] of pendingConfirms) {
2022
+ resolve5("no");
2023
+ pendingConfirms.delete(id);
2024
+ }
2025
+ }
1895
2026
  });
1896
2027
  send(ws, {
1897
2028
  type: "session.start",
@@ -1913,9 +2044,12 @@ async function runWebUI(opts) {
1913
2044
  ws.close();
1914
2045
  }
1915
2046
  clients.clear();
2047
+ void unregisterInstance(process.pid, registryBaseDir).catch(() => {
2048
+ });
2049
+ httpServer?.close();
1916
2050
  wss.close(() => {
1917
2051
  console.log("[WebUI] Server stopped");
1918
- resolve3();
2052
+ resolve4();
1919
2053
  });
1920
2054
  }
1921
2055
  process.on("SIGINT", shutdown);
@@ -1940,6 +2074,15 @@ async function runWebUI(opts) {
1940
2074
  case "ping":
1941
2075
  send(ws, { type: "pong", payload: {} });
1942
2076
  break;
2077
+ case "tool.confirm_result": {
2078
+ const { id, decision } = msg.payload;
2079
+ const resolve4 = pendingConfirms.get(id);
2080
+ if (resolve4) {
2081
+ pendingConfirms.delete(id);
2082
+ resolve4(decision);
2083
+ }
2084
+ break;
2085
+ }
1943
2086
  case "providers.list":
1944
2087
  await handleProvidersList(ws);
1945
2088
  break;
@@ -2221,7 +2364,7 @@ async function runWebUI(opts) {
2221
2364
  if (!opts.globalConfigPath) return {};
2222
2365
  let raw;
2223
2366
  try {
2224
- raw = await fsp3.readFile(opts.globalConfigPath, "utf8");
2367
+ raw = await fsp4.readFile(opts.globalConfigPath, "utf8");
2225
2368
  } catch {
2226
2369
  return {};
2227
2370
  }
@@ -2241,7 +2384,7 @@ async function runWebUI(opts) {
2241
2384
  let raw;
2242
2385
  let fileExists = true;
2243
2386
  try {
2244
- raw = await fsp3.readFile(opts.globalConfigPath, "utf8");
2387
+ raw = await fsp4.readFile(opts.globalConfigPath, "utf8");
2245
2388
  } catch (err) {
2246
2389
  if (err.code !== "ENOENT") {
2247
2390
  throw new Error(
@@ -2279,6 +2422,25 @@ var init_webui_server = __esm({
2279
2422
  init_provider_config_utils();
2280
2423
  }
2281
2424
  });
2425
+ var req = createRequire(import.meta.url);
2426
+ function readOwnVersion() {
2427
+ const candidates = ["../package.json", "../../package.json"];
2428
+ for (const rel of candidates) {
2429
+ try {
2430
+ const pkg = req(rel);
2431
+ if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
2432
+ } catch {
2433
+ }
2434
+ }
2435
+ return "dev";
2436
+ }
2437
+ var CLI_VERSION = readOwnVersion();
2438
+ var API_VERSION = "0.0.0";
2439
+ try {
2440
+ const corePkg = req("@wrongstack/core/package.json");
2441
+ if (corePkg.wrongstackApiVersion) API_VERSION = corePkg.wrongstackApiVersion;
2442
+ } catch {
2443
+ }
2282
2444
 
2283
2445
  // src/slash-commands/commit-llm.ts
2284
2446
  async function generateCommitMessageWithLLM(diff, opts) {
@@ -2396,6 +2558,7 @@ function positiveNumber(value) {
2396
2558
  var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
2397
2559
  "yolo",
2398
2560
  "yolo-destructive",
2561
+ "confirm-destructive",
2399
2562
  "force-all-yolo",
2400
2563
  "verbose",
2401
2564
  "trace",
@@ -2414,6 +2577,7 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
2414
2577
  "prompt",
2415
2578
  "metrics",
2416
2579
  "webui",
2580
+ "open",
2417
2581
  "no-check",
2418
2582
  "no-models-refresh",
2419
2583
  "director",
@@ -2530,7 +2694,7 @@ function parseSpawnFlags(input) {
2530
2694
  }
2531
2695
  async function pathExists(file) {
2532
2696
  try {
2533
- await fsp3.access(file);
2697
+ await fsp4.access(file);
2534
2698
  return true;
2535
2699
  } catch {
2536
2700
  return false;
@@ -2565,7 +2729,7 @@ function parseMakeTargets(makefile) {
2565
2729
  async function detectProjectFacts(root) {
2566
2730
  const facts = { hints: [] };
2567
2731
  try {
2568
- const pkg = JSON.parse(await fsp3.readFile(path8.join(root, "package.json"), "utf8"));
2732
+ const pkg = JSON.parse(await fsp4.readFile(path8.join(root, "package.json"), "utf8"));
2569
2733
  const scripts = pkg.scripts ?? {};
2570
2734
  const pm = await detectPackageManager(root, pkg.packageManager);
2571
2735
  if (hasUsableScript(scripts, "build")) facts.build = `${pm} run build`;
@@ -2603,7 +2767,7 @@ async function detectProjectFacts(root) {
2603
2767
  } catch {
2604
2768
  }
2605
2769
  try {
2606
- const makefile = await fsp3.readFile(path8.join(root, "Makefile"), "utf8");
2770
+ const makefile = await fsp4.readFile(path8.join(root, "Makefile"), "utf8");
2607
2771
  const targets = parseMakeTargets(makefile);
2608
2772
  facts.build ??= targets.has("build") ? "make build" : "make";
2609
2773
  if (targets.has("test")) facts.test ??= "make test";
@@ -2975,7 +3139,7 @@ function formatPhaseList(graph) {
2975
3139
  }
2976
3140
  async function gatherProjectContext(projectRoot) {
2977
3141
  try {
2978
- const raw = await fsp3.readFile(path8.join(projectRoot, "package.json"), "utf8");
3142
+ const raw = await fsp4.readFile(path8.join(projectRoot, "package.json"), "utf8");
2979
3143
  const pkg = JSON.parse(raw);
2980
3144
  const parts = [
2981
3145
  `Project: ${String(pkg.name ?? "unknown")}`,
@@ -3570,7 +3734,7 @@ async function persistContextConfig(opts, patch) {
3570
3734
  if (!opts.configStore || !opts.paths) return "Cannot persist context settings: config store not available.";
3571
3735
  let raw = "{}";
3572
3736
  try {
3573
- raw = await fsp3.readFile(opts.paths.globalConfig, "utf8");
3737
+ raw = await fsp4.readFile(opts.paths.globalConfig, "utf8");
3574
3738
  } catch (err) {
3575
3739
  if (err.code !== "ENOENT") {
3576
3740
  return `Could not read ${opts.paths.globalConfig}: ${err.message}`;
@@ -4988,8 +5152,8 @@ function buildInitCommand(opts) {
4988
5152
  const file = path8.join(dir, "AGENTS.md");
4989
5153
  const detected = await detectProjectFacts(ctx.projectRoot);
4990
5154
  const body = renderAgentsTemplate(detected);
4991
- await fsp3.mkdir(dir, { recursive: true });
4992
- await fsp3.writeFile(file, body, "utf8");
5155
+ await fsp4.mkdir(dir, { recursive: true });
5156
+ await fsp4.writeFile(file, body, "utf8");
4993
5157
  if (detected.hints.length > 0) {
4994
5158
  const msg2 = `Wrote ${file}
4995
5159
  Pre-filled: ${detected.hints.join(", ")}. Edit the file with project context and instructions the system prompt should carry.`;
@@ -5202,7 +5366,7 @@ function stateBadge(state) {
5202
5366
  }
5203
5367
  async function readConfig(path25) {
5204
5368
  try {
5205
- return JSON.parse(await fsp3.readFile(path25, "utf8"));
5369
+ return JSON.parse(await fsp4.readFile(path25, "utf8"));
5206
5370
  } catch {
5207
5371
  return {};
5208
5372
  }
@@ -5213,8 +5377,8 @@ function isMcpServerRecord(value) {
5213
5377
  async function writeConfig(path25, cfg) {
5214
5378
  const raw = JSON.stringify(cfg, null, 2);
5215
5379
  const tmp = path25 + ".tmp";
5216
- await fsp3.writeFile(tmp, raw, "utf8");
5217
- await fsp3.rename(tmp, path25);
5380
+ await fsp4.writeFile(tmp, raw, "utf8");
5381
+ await fsp4.rename(tmp, path25);
5218
5382
  }
5219
5383
 
5220
5384
  // src/slash-commands/mcp.ts
@@ -5406,7 +5570,7 @@ async function patchGlobalConfig(globalConfigPath, mutate) {
5406
5570
  let raw = "{}";
5407
5571
  let fileExists = true;
5408
5572
  try {
5409
- raw = await fsp3.readFile(globalConfigPath, "utf8");
5573
+ raw = await fsp4.readFile(globalConfigPath, "utf8");
5410
5574
  } catch (err) {
5411
5575
  if (err.code !== "ENOENT") throw err;
5412
5576
  fileExists = false;
@@ -5911,7 +6075,7 @@ async function patchGlobalConfig2(globalConfigPath, mutate) {
5911
6075
  let raw = "{}";
5912
6076
  let fileExists = true;
5913
6077
  try {
5914
- raw = await fsp3.readFile(globalConfigPath, "utf8");
6078
+ raw = await fsp4.readFile(globalConfigPath, "utf8");
5915
6079
  } catch (err) {
5916
6080
  if (err.code !== "ENOENT") throw err;
5917
6081
  fileExists = false;
@@ -6090,7 +6254,7 @@ async function persistAutonomySetting(deps, mutator) {
6090
6254
  let raw;
6091
6255
  let fileExists = true;
6092
6256
  try {
6093
- raw = await fsp3.readFile(deps.globalConfigPath, "utf8");
6257
+ raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
6094
6258
  } catch (err) {
6095
6259
  if (err.code !== "ENOENT") {
6096
6260
  throw new Error(`Could not read ${deps.globalConfigPath}: ${err.message}`);
@@ -6317,7 +6481,7 @@ function resolveConfigPath() {
6317
6481
  async function loadStatuslineConfig() {
6318
6482
  const p = resolveConfigPath();
6319
6483
  try {
6320
- const raw = await fsp3.readFile(p, "utf8");
6484
+ const raw = await fsp4.readFile(p, "utf8");
6321
6485
  return { ...DEFAULTS, ...JSON.parse(raw) };
6322
6486
  } catch {
6323
6487
  return { ...DEFAULTS };
@@ -6326,7 +6490,7 @@ async function loadStatuslineConfig() {
6326
6490
  async function saveStatuslineConfig(cfg) {
6327
6491
  const p = resolveConfigPath();
6328
6492
  try {
6329
- await fsp3.mkdir(path8.dirname(p), { recursive: true });
6493
+ await fsp4.mkdir(path8.dirname(p), { recursive: true });
6330
6494
  await atomicWrite(p, JSON.stringify(cfg, null, 2));
6331
6495
  } catch (err) {
6332
6496
  throw new FsError({
@@ -6529,13 +6693,13 @@ function buildYoloCommand(opts) {
6529
6693
  description: "Toggle or query YOLO (auto-approve) mode.",
6530
6694
  help: [
6531
6695
  "Usage:",
6532
- " /yolo Show current YOLO status",
6533
- " /yolo on Enable YOLO mode (auto-approve normal project work)",
6534
- " /yolo off Disable YOLO mode (restore permission prompts)",
6535
- " /yolo toggle Toggle YOLO mode",
6696
+ " /yolo Show current YOLO status",
6697
+ " /yolo on Enable YOLO mode (auto-approve all tool calls)",
6698
+ " /yolo off Disable YOLO mode (restore permission prompts)",
6699
+ " /yolo destructive Toggle destructive confirmation gate (YOLO mode only)",
6536
6700
  "",
6537
- "YOLO mode auto-approves normal in-project tool calls, including simple shell commands.",
6538
- "Clearly destructive calls may still ask unless --yolo-destructive is enabled."
6701
+ "YOLO mode auto-approves everything, including destructive calls.",
6702
+ "Use /yolo destructive to re-enable confirmation for risky operations."
6539
6703
  ].join("\n"),
6540
6704
  async run(args) {
6541
6705
  const arg = args.trim().toLowerCase();
@@ -6635,13 +6799,13 @@ var MANIFESTS = [
6635
6799
  ];
6636
6800
  async function detectProjectKind(projectRoot) {
6637
6801
  try {
6638
- await fsp3.access(path8.join(projectRoot, ".wrongstack", "AGENTS.md"));
6802
+ await fsp4.access(path8.join(projectRoot, ".wrongstack", "AGENTS.md"));
6639
6803
  return "initialized";
6640
6804
  } catch {
6641
6805
  }
6642
6806
  for (const m of MANIFESTS) {
6643
6807
  try {
6644
- await fsp3.access(path8.join(projectRoot, m));
6808
+ await fsp4.access(path8.join(projectRoot, m));
6645
6809
  return "project";
6646
6810
  } catch {
6647
6811
  }
@@ -6653,8 +6817,8 @@ async function scaffoldAgentsMd(projectRoot) {
6653
6817
  const file = path8.join(dir, "AGENTS.md");
6654
6818
  const facts = await detectProjectFacts(projectRoot);
6655
6819
  const body = renderAgentsTemplate(facts);
6656
- await fsp3.mkdir(dir, { recursive: true });
6657
- await fsp3.writeFile(file, body, "utf8");
6820
+ await fsp4.mkdir(dir, { recursive: true });
6821
+ await fsp4.writeFile(file, body, "utf8");
6658
6822
  return file;
6659
6823
  }
6660
6824
  async function runProjectCheck(opts) {
@@ -6697,7 +6861,7 @@ async function runProjectCheck(opts) {
6697
6861
  const gitDir = path8.join(projectRoot, ".git");
6698
6862
  let hasGit = false;
6699
6863
  try {
6700
- await fsp3.access(gitDir);
6864
+ await fsp4.access(gitDir);
6701
6865
  hasGit = true;
6702
6866
  } catch {
6703
6867
  }
@@ -6717,10 +6881,10 @@ async function runProjectCheck(opts) {
6717
6881
  if (answer2 === "y" || answer2 === "yes") {
6718
6882
  try {
6719
6883
  const { spawn: spawn3 } = await import('child_process');
6720
- await new Promise((resolve3, reject) => {
6884
+ await new Promise((resolve4, reject) => {
6721
6885
  const child = spawn3("git", ["init"], { cwd: projectRoot });
6722
6886
  child.on("error", reject);
6723
- child.on("close", (code) => code === 0 ? resolve3() : reject(new Error(`git init failed with ${code}`)));
6887
+ child.on("close", (code) => code === 0 ? resolve4() : reject(new Error(`git init failed with ${code}`)));
6724
6888
  });
6725
6889
  renderer.write(` ${color.green("\u2713")} Git repository initialized
6726
6890
  `);
@@ -6837,7 +7001,7 @@ var ReadlineInputReader = class {
6837
7001
  }
6838
7002
  async loadHistory() {
6839
7003
  try {
6840
- const raw = await fsp3.readFile(this.historyFile, "utf8");
7004
+ const raw = await fsp4.readFile(this.historyFile, "utf8");
6841
7005
  this.history = raw.split("\n").filter(Boolean).slice(-1e3);
6842
7006
  } catch {
6843
7007
  this.history = [];
@@ -6845,8 +7009,8 @@ var ReadlineInputReader = class {
6845
7009
  }
6846
7010
  async saveHistory() {
6847
7011
  try {
6848
- await fsp3.mkdir(path8.dirname(this.historyFile), { recursive: true });
6849
- await fsp3.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
7012
+ await fsp4.mkdir(path8.dirname(this.historyFile), { recursive: true });
7013
+ await fsp4.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
6850
7014
  } catch {
6851
7015
  }
6852
7016
  }
@@ -6864,28 +7028,34 @@ var ReadlineInputReader = class {
6864
7028
  async readLine(prompt) {
6865
7029
  if (this.history.length === 0) await this.loadHistory();
6866
7030
  while (this.pending) {
6867
- await new Promise((resolve3) => setTimeout(resolve3, 50));
7031
+ await new Promise((resolve4) => setTimeout(resolve4, 50));
6868
7032
  }
6869
7033
  this.pending = true;
6870
7034
  try {
6871
- const rl = this.ensure();
6872
- if (rl.closed || rl._flushed) {
6873
- rl.close();
7035
+ if (this.rl) {
7036
+ const old = this.rl;
6874
7037
  this.rl = void 0;
7038
+ await new Promise((resolve4) => {
7039
+ if (old.closed) {
7040
+ resolve4();
7041
+ } else {
7042
+ old.once("close", resolve4);
7043
+ old.close();
7044
+ }
7045
+ });
6875
7046
  }
6876
7047
  const fresh = this.ensure();
6877
- return new Promise((resolve3) => {
7048
+ return new Promise((resolve4) => {
6878
7049
  fresh.question(prompt ?? "> ", (line) => {
6879
7050
  if (line.trim()) {
6880
7051
  this.history.push(line);
6881
7052
  void this.saveHistory();
6882
7053
  }
6883
- resolve3(line);
7054
+ resolve4(line);
6884
7055
  });
6885
- fresh.once("close", () => resolve3(""));
7056
+ fresh.once("close", () => resolve4(""));
6886
7057
  }).then((result) => {
6887
7058
  this.rl?.close();
6888
- this.rl = void 0;
6889
7059
  return result;
6890
7060
  });
6891
7061
  } finally {
@@ -6894,7 +7064,7 @@ var ReadlineInputReader = class {
6894
7064
  }
6895
7065
  async readKey(prompt, options) {
6896
7066
  writeOut(prompt);
6897
- return new Promise((resolve3) => {
7067
+ return new Promise((resolve4) => {
6898
7068
  const stdin = process.stdin;
6899
7069
  const wasRaw = stdin.isRaw;
6900
7070
  const wasPaused = stdin.isPaused();
@@ -6905,7 +7075,7 @@ var ReadlineInputReader = class {
6905
7075
  if (key === "") {
6906
7076
  cleanup();
6907
7077
  writeOut("\n");
6908
- resolve3("");
7078
+ resolve4("");
6909
7079
  return;
6910
7080
  }
6911
7081
  const opt = options.find(
@@ -6915,12 +7085,12 @@ var ReadlineInputReader = class {
6915
7085
  cleanup();
6916
7086
  writeOut(`${opt.key}
6917
7087
  `);
6918
- resolve3(opt.value);
7088
+ resolve4(opt.value);
6919
7089
  }
6920
7090
  };
6921
7091
  const onClose = () => {
6922
7092
  cleanup();
6923
- resolve3("");
7093
+ resolve4("");
6924
7094
  };
6925
7095
  const cleanup = () => {
6926
7096
  stdin.off("data", onData);
@@ -6948,7 +7118,7 @@ var ReadlineInputReader = class {
6948
7118
  this.rl?.close();
6949
7119
  this.rl = void 0;
6950
7120
  writeOut(prompt);
6951
- return new Promise((resolve3) => {
7121
+ return new Promise((resolve4) => {
6952
7122
  let buf = "";
6953
7123
  const wasRaw = stdin.isRaw;
6954
7124
  setRawMode(stdin, true);
@@ -6966,7 +7136,7 @@ var ReadlineInputReader = class {
6966
7136
  cleanup();
6967
7137
  writeOut(` ${dim(`[${buf.length} chars]`)}
6968
7138
  `);
6969
- resolve3(buf);
7139
+ resolve4(buf);
6970
7140
  return;
6971
7141
  }
6972
7142
  if (ch === "") {
@@ -7066,7 +7236,7 @@ async function buildPickableProviders(modelsRegistry, config) {
7066
7236
  var defaultUidFn = () => os2__default.userInfo().uid;
7067
7237
  async function getFileUid(filePath) {
7068
7238
  try {
7069
- const stat4 = await fsp3.stat(filePath);
7239
+ const stat4 = await fsp4.stat(filePath);
7070
7240
  return stat4.uid;
7071
7241
  } catch {
7072
7242
  return void 0;
@@ -7106,7 +7276,7 @@ async function safeDelete(filePath) {
7106
7276
  const filename = path8.basename(filePath);
7107
7277
  try {
7108
7278
  assertSafeToDelete(filename, dir);
7109
- await fsp3.unlink(filePath);
7279
+ await fsp4.unlink(filePath);
7110
7280
  } catch (err) {
7111
7281
  if (err instanceof Error && err.message.startsWith("Refusing")) {
7112
7282
  writeErr(`[config-history] SAFETY: ${err.message}
@@ -7164,7 +7334,7 @@ function entryId(ts) {
7164
7334
  }
7165
7335
  async function ensureHistoryDir(homeFn = defaultHomeDir) {
7166
7336
  try {
7167
- await fsp3.mkdir(historyDir(homeFn), { recursive: true });
7337
+ await fsp4.mkdir(historyDir(homeFn), { recursive: true });
7168
7338
  } catch (err) {
7169
7339
  throw new FsError({
7170
7340
  message: err instanceof Error ? err.message : String(err),
@@ -7176,7 +7346,7 @@ async function ensureHistoryDir(homeFn = defaultHomeDir) {
7176
7346
  }
7177
7347
  async function readIndex(homeFn = defaultHomeDir) {
7178
7348
  try {
7179
- const raw = await fsp3.readFile(historyIndexPath(homeFn), "utf8");
7349
+ const raw = await fsp4.readFile(historyIndexPath(homeFn), "utf8");
7180
7350
  return JSON.parse(raw);
7181
7351
  } catch {
7182
7352
  return { version: 1, entries: [] };
@@ -7201,7 +7371,7 @@ async function backupCurrent(homeFn = defaultHomeDir) {
7201
7371
  const ts = Date.now();
7202
7372
  let content;
7203
7373
  try {
7204
- content = await fsp3.readFile(cfg, "utf8");
7374
+ content = await fsp4.readFile(cfg, "utf8");
7205
7375
  } catch {
7206
7376
  }
7207
7377
  if (content !== void 0) {
@@ -7219,7 +7389,7 @@ async function backupCurrent(homeFn = defaultHomeDir) {
7219
7389
  }
7220
7390
  try {
7221
7391
  const dir = path8.join(homeFn(), ".wrongstack");
7222
- const files = await fsp3.readdir(dir);
7392
+ const files = await fsp4.readdir(dir);
7223
7393
  const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
7224
7394
  for (const f of baks.slice(10)) {
7225
7395
  await safeDelete(path8.join(dir, f));
@@ -7239,7 +7409,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
7239
7409
  diffSummary: diffSummary(oldCfg, newCfg)
7240
7410
  };
7241
7411
  try {
7242
- await fsp3.writeFile(
7412
+ await fsp4.writeFile(
7243
7413
  path8.join(historyDir(homeFn), `${id}.json`),
7244
7414
  JSON.stringify(entry, null, 2),
7245
7415
  "utf8"
@@ -7263,7 +7433,7 @@ async function listHistory(homeFn = defaultHomeDir) {
7263
7433
  }
7264
7434
  async function getHistoryEntry(id, homeFn = defaultHomeDir) {
7265
7435
  try {
7266
- const raw = await fsp3.readFile(path8.join(historyDir(homeFn), `${id}.json`), "utf8");
7436
+ const raw = await fsp4.readFile(path8.join(historyDir(homeFn), `${id}.json`), "utf8");
7267
7437
  return JSON.parse(raw);
7268
7438
  } catch {
7269
7439
  return null;
@@ -7278,7 +7448,7 @@ async function restoreFromHistory(id, homeFn = defaultHomeDir) {
7278
7448
  await backupCurrent(homeFn);
7279
7449
  let oldCfg = {};
7280
7450
  try {
7281
- const raw = await fsp3.readFile(configPath(homeFn), "utf8");
7451
+ const raw = await fsp4.readFile(configPath(homeFn), "utf8");
7282
7452
  oldCfg = JSON.parse(raw);
7283
7453
  } catch {
7284
7454
  }
@@ -7300,13 +7470,13 @@ async function restoreLast(homeFn = defaultHomeDir) {
7300
7470
  const cfg = configPath(homeFn);
7301
7471
  let oldCfg = {};
7302
7472
  try {
7303
- const raw = await fsp3.readFile(cfg, "utf8");
7473
+ const raw = await fsp4.readFile(cfg, "utf8");
7304
7474
  oldCfg = JSON.parse(raw);
7305
7475
  } catch {
7306
7476
  }
7307
7477
  let lastCfg = {};
7308
7478
  try {
7309
- const raw = await fsp3.readFile(last, "utf8");
7479
+ const raw = await fsp4.readFile(last, "utf8");
7310
7480
  lastCfg = JSON.parse(raw);
7311
7481
  } catch {
7312
7482
  return { ok: false, error: "No prior backup found" };
@@ -8770,7 +8940,7 @@ async function readKeyInput(deps, intent) {
8770
8940
  async function loadProviders(deps) {
8771
8941
  let raw;
8772
8942
  try {
8773
- raw = await fsp3.readFile(deps.globalConfigPath, "utf8");
8943
+ raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
8774
8944
  } catch (err) {
8775
8945
  if (err.code !== "ENOENT") {
8776
8946
  deps.renderer.writeWarning(
@@ -8795,7 +8965,7 @@ async function mutateProviders(deps, mutator) {
8795
8965
  let raw;
8796
8966
  let fileExists = true;
8797
8967
  try {
8798
- raw = await fsp3.readFile(deps.globalConfigPath, "utf8");
8968
+ raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
8799
8969
  } catch (err) {
8800
8970
  if (err.code !== "ENOENT") {
8801
8971
  throw new Error(
@@ -8870,8 +9040,9 @@ var updateCmd = async (args, deps) => {
8870
9040
  deps.renderer.write(`Updating wrongstack from v${info.current} to v${info.latest}...
8871
9041
  `);
8872
9042
  try {
8873
- const result = await new Promise((resolve3, reject) => {
8874
- const child = spawn("npm", ["install", "-g", "wrongstack@latest"], {
9043
+ const result = await new Promise((resolve4, reject) => {
9044
+ const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
9045
+ const child = spawn(npmCommand, ["install", "-g", "wrongstack@latest"], {
8875
9046
  cwd,
8876
9047
  stdio: "pipe"
8877
9048
  });
@@ -8880,7 +9051,7 @@ var updateCmd = async (args, deps) => {
8880
9051
  _stderr += d;
8881
9052
  });
8882
9053
  child.on("error", reject);
8883
- child.on("close", (code) => resolve3({ code: code ?? 0 }));
9054
+ child.on("close", (code) => resolve4({ code: code ?? 0 }));
8884
9055
  });
8885
9056
  if (result.code === 0) {
8886
9057
  deps.renderer.write(`
@@ -8906,27 +9077,6 @@ Update failed: ${msg}
8906
9077
  return 1;
8907
9078
  }
8908
9079
  };
8909
- var req = createRequire(import.meta.url);
8910
- function readOwnVersion() {
8911
- const candidates = ["../package.json", "../../package.json"];
8912
- for (const rel of candidates) {
8913
- try {
8914
- const pkg = req(rel);
8915
- if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
8916
- } catch {
8917
- }
8918
- }
8919
- return "dev";
8920
- }
8921
- var CLI_VERSION = readOwnVersion();
8922
- var API_VERSION = "0.0.0";
8923
- try {
8924
- const corePkg = req("@wrongstack/core/package.json");
8925
- if (corePkg.wrongstackApiVersion) API_VERSION = corePkg.wrongstackApiVersion;
8926
- } catch {
8927
- }
8928
-
8929
- // src/subcommands/handlers/diag-doctor.ts
8930
9080
  var diagCmd = async (_args, deps) => {
8931
9081
  const cfg = deps.config;
8932
9082
  const age = await deps.modelsRegistry.ageSeconds();
@@ -9009,7 +9159,7 @@ var doctorCmd = async (_args, deps) => {
9009
9159
  });
9010
9160
  }
9011
9161
  try {
9012
- await fsp3.access(deps.paths.secretsKey);
9162
+ await fsp4.access(deps.paths.secretsKey);
9013
9163
  checks.push({ name: "secret vault", status: "ok", detail: deps.paths.secretsKey });
9014
9164
  } catch {
9015
9165
  checks.push({
@@ -9019,10 +9169,10 @@ var doctorCmd = async (_args, deps) => {
9019
9169
  });
9020
9170
  }
9021
9171
  try {
9022
- await fsp3.mkdir(deps.paths.projectSessions, { recursive: true });
9172
+ await fsp4.mkdir(deps.paths.projectSessions, { recursive: true });
9023
9173
  const probe = path8.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
9024
- await fsp3.writeFile(probe, "");
9025
- await fsp3.unlink(probe);
9174
+ await fsp4.writeFile(probe, "");
9175
+ await fsp4.unlink(probe);
9026
9176
  checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
9027
9177
  } catch (err) {
9028
9178
  checks.push({
@@ -9123,8 +9273,8 @@ var exportCmd = async (args, deps) => {
9123
9273
  return 1;
9124
9274
  }
9125
9275
  if (output) {
9126
- await fsp3.mkdir(path8.dirname(path8.resolve(deps.cwd, output)), { recursive: true });
9127
- await fsp3.writeFile(path8.resolve(deps.cwd, output), rendered, "utf8");
9276
+ await fsp4.mkdir(path8.dirname(path8.resolve(deps.cwd, output)), { recursive: true });
9277
+ await fsp4.writeFile(path8.resolve(deps.cwd, output), rendered, "utf8");
9128
9278
  deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
9129
9279
  `);
9130
9280
  } else {
@@ -9191,13 +9341,13 @@ var initCmd = async (_args, deps) => {
9191
9341
  } else {
9192
9342
  deps.renderer.writeInfo(`Found API key in env (${provider.envVars.join(" / ")}).`);
9193
9343
  }
9194
- await fsp3.mkdir(deps.paths.globalRoot, { recursive: true });
9344
+ await fsp4.mkdir(deps.paths.globalRoot, { recursive: true });
9195
9345
  const config = { version: 1, provider: providerId, model: modelId };
9196
9346
  if (apiKey) config.apiKey = apiKey;
9197
9347
  const vault = new DefaultSecretVault({ keyFile: deps.paths.secretsKey });
9198
9348
  const encrypted = encryptConfigSecrets(config, vault);
9199
9349
  await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
9200
- await fsp3.mkdir(path8.join(deps.projectRoot, ".wrongstack"), { recursive: true });
9350
+ await fsp4.mkdir(path8.join(deps.projectRoot, ".wrongstack"), { recursive: true });
9201
9351
  const agentsFile = path8.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
9202
9352
  const projectFacts = await detectProjectFacts(deps.projectRoot);
9203
9353
  await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
@@ -9337,8 +9487,8 @@ async function serveMcpStdio(deps) {
9337
9487
  log(
9338
9488
  `wrongstack MCP server ready at ${handle2.url} \u2014 exposing ${allowed.length} tool(s) (${mode})${token ? " [token auth]" : ""}.`
9339
9489
  );
9340
- await new Promise((resolve3) => {
9341
- const stop = () => resolve3();
9490
+ await new Promise((resolve4) => {
9491
+ const stop = () => resolve4();
9342
9492
  process.once("SIGINT", stop);
9343
9493
  process.once("SIGTERM", stop);
9344
9494
  });
@@ -9423,7 +9573,7 @@ async function addMcpServer(args, deps) {
9423
9573
  serverCfg.enabled = enable;
9424
9574
  let existing = {};
9425
9575
  try {
9426
- existing = JSON.parse(await fsp3.readFile(deps.paths.globalConfig, "utf8"));
9576
+ existing = JSON.parse(await fsp4.readFile(deps.paths.globalConfig, "utf8"));
9427
9577
  } catch {
9428
9578
  }
9429
9579
  const mcpServers = existing.mcpServers ?? {};
@@ -9443,7 +9593,7 @@ async function addMcpServer(args, deps) {
9443
9593
  async function removeMcpServer(name, deps) {
9444
9594
  let existing = {};
9445
9595
  try {
9446
- existing = JSON.parse(await fsp3.readFile(deps.paths.globalConfig, "utf8"));
9596
+ existing = JSON.parse(await fsp4.readFile(deps.paths.globalConfig, "utf8"));
9447
9597
  } catch {
9448
9598
  deps.renderer.writeError("No config file found.\n");
9449
9599
  return 1;
@@ -9564,7 +9714,7 @@ function renderConfiguredPlugins(config) {
9564
9714
  }
9565
9715
  async function readConfig2(file) {
9566
9716
  try {
9567
- return JSON.parse(await fsp3.readFile(file, "utf8"));
9717
+ return JSON.parse(await fsp4.readFile(file, "utf8"));
9568
9718
  } catch {
9569
9719
  return {};
9570
9720
  }
@@ -9657,7 +9807,7 @@ var usageCmd = async (_args, deps) => {
9657
9807
  var projectsCmd = async (_args, deps) => {
9658
9808
  const projectsRoot = path8.join(deps.paths.globalRoot, "projects");
9659
9809
  try {
9660
- const entries = await fsp3.readdir(projectsRoot);
9810
+ const entries = await fsp4.readdir(projectsRoot);
9661
9811
  if (entries.length === 0) {
9662
9812
  deps.renderer.write("No projects tracked.\n");
9663
9813
  return 0;
@@ -9665,7 +9815,7 @@ var projectsCmd = async (_args, deps) => {
9665
9815
  for (const hash of entries) {
9666
9816
  try {
9667
9817
  const meta = JSON.parse(
9668
- await fsp3.readFile(path8.join(projectsRoot, hash, "meta.json"), "utf8")
9818
+ await fsp4.readFile(path8.join(projectsRoot, hash, "meta.json"), "utf8")
9669
9819
  );
9670
9820
  deps.renderer.write(
9671
9821
  ` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
@@ -9875,7 +10025,7 @@ async function mutateModelsConfig(deps, mutator) {
9875
10025
  let fileExists = true;
9876
10026
  let raw;
9877
10027
  try {
9878
- raw = await fsp3.readFile(configPath2, "utf8");
10028
+ raw = await fsp4.readFile(configPath2, "utf8");
9879
10029
  } catch (err) {
9880
10030
  if (err.code !== "ENOENT") throw err;
9881
10031
  fileExists = false;
@@ -10036,7 +10186,7 @@ var sessionsFleetCmd = async (args, deps) => {
10036
10186
  async function listFleetRuns(deps) {
10037
10187
  let entries = [];
10038
10188
  try {
10039
- entries = await fsp3.readdir(deps.paths.projectSessions);
10189
+ entries = await fsp4.readdir(deps.paths.projectSessions);
10040
10190
  } catch {
10041
10191
  deps.renderer.writeError(`Cannot read projectSessions: ${deps.paths.projectSessions}
10042
10192
  `);
@@ -10047,7 +10197,7 @@ async function listFleetRuns(deps) {
10047
10197
  const runDir = path8.join(deps.paths.projectSessions, id);
10048
10198
  let stat4;
10049
10199
  try {
10050
- stat4 = await fsp3.stat(runDir);
10200
+ stat4 = await fsp4.stat(runDir);
10051
10201
  } catch {
10052
10202
  continue;
10053
10203
  }
@@ -10057,18 +10207,18 @@ async function listFleetRuns(deps) {
10057
10207
  let subagentCount = 0;
10058
10208
  let subagentsDir;
10059
10209
  try {
10060
- await fsp3.access(path8.join(runDir, "fleet.json"));
10210
+ await fsp4.access(path8.join(runDir, "fleet.json"));
10061
10211
  manifest = true;
10062
10212
  } catch {
10063
10213
  }
10064
10214
  try {
10065
- await fsp3.access(path8.join(runDir, "checkpoint.json"));
10215
+ await fsp4.access(path8.join(runDir, "checkpoint.json"));
10066
10216
  checkpoint = true;
10067
10217
  } catch {
10068
10218
  }
10069
10219
  try {
10070
10220
  subagentsDir = path8.join(runDir, "subagents");
10071
- const files = await fsp3.readdir(subagentsDir);
10221
+ const files = await fsp4.readdir(subagentsDir);
10072
10222
  subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
10073
10223
  } catch {
10074
10224
  }
@@ -10099,7 +10249,7 @@ async function showFleetRun(runId, deps) {
10099
10249
  const runDir = path8.join(deps.paths.projectSessions, runId);
10100
10250
  let stat4;
10101
10251
  try {
10102
- stat4 = await fsp3.stat(runDir);
10252
+ stat4 = await fsp4.stat(runDir);
10103
10253
  } catch {
10104
10254
  deps.renderer.writeError(`Fleet run not found: ${runId}
10105
10255
  `);
@@ -10116,7 +10266,7 @@ Fleet Run: ${runId}
10116
10266
  const manifestPath = path8.join(runDir, "fleet.json");
10117
10267
  let manifestData = null;
10118
10268
  try {
10119
- manifestData = await fsp3.readFile(manifestPath, "utf8");
10269
+ manifestData = await fsp4.readFile(manifestPath, "utf8");
10120
10270
  const manifest = JSON.parse(manifestData);
10121
10271
  const subagents = manifest.subagents ?? [];
10122
10272
  const tasks = manifest.tasks ?? [];
@@ -10132,12 +10282,12 @@ Fleet Run: ${runId}
10132
10282
  const checkpointPath = path8.join(runDir, "checkpoint.json");
10133
10283
  let checkpointData = null;
10134
10284
  try {
10135
- checkpointData = await fsp3.readFile(checkpointPath, "utf8");
10285
+ checkpointData = await fsp4.readFile(checkpointPath, "utf8");
10136
10286
  const snap = JSON.parse(checkpointData);
10137
10287
  const lockPath = `${checkpointPath}.lock`;
10138
10288
  let lockStatus = color.dim("\u25CB no lock");
10139
10289
  try {
10140
- const lockRaw = await fsp3.readFile(lockPath, "utf8");
10290
+ const lockRaw = await fsp4.readFile(lockPath, "utf8");
10141
10291
  const lock = JSON.parse(lockRaw);
10142
10292
  lockStatus = `${color.yellow("\u25B8")} lock held by pid ${lock.pid} on ${lock.hostname} (started ${lock.startedAt})`;
10143
10293
  } catch {
@@ -10179,7 +10329,7 @@ Fleet Run: ${runId}
10179
10329
  const subagentsDir = path8.join(runDir, "subagents");
10180
10330
  let subagentFiles = [];
10181
10331
  try {
10182
- subagentFiles = await fsp3.readdir(subagentsDir);
10332
+ subagentFiles = await fsp4.readdir(subagentsDir);
10183
10333
  subagentFiles = subagentFiles.filter((f) => f.endsWith(".jsonl"));
10184
10334
  } catch {
10185
10335
  }
@@ -10191,7 +10341,7 @@ Fleet Run: ${runId}
10191
10341
  const filePath = path8.join(subagentsDir, f);
10192
10342
  let size;
10193
10343
  try {
10194
- const s = await fsp3.stat(filePath);
10344
+ const s = await fsp4.stat(filePath);
10195
10345
  size = s.size;
10196
10346
  } catch {
10197
10347
  size = 0;
@@ -10207,7 +10357,7 @@ Fleet Run: ${runId}
10207
10357
  }
10208
10358
  const sharedDir = path8.join(runDir, "shared");
10209
10359
  try {
10210
- const files = await fsp3.readdir(sharedDir);
10360
+ const files = await fsp4.readdir(sharedDir);
10211
10361
  deps.renderer.write(`
10212
10362
  Shared scratchpad: ${files.length} file(s)
10213
10363
  `);
@@ -10700,10 +10850,13 @@ var helpCmd = async (_args, deps) => {
10700
10850
  " wstack version Print version",
10701
10851
  "",
10702
10852
  color.bold("Common flags"),
10703
- " --yolo Auto-approve normal in-project tool calls",
10704
- " --yolo-destructive Also auto-approve clearly destructive YOLO-gated calls",
10705
- " --force-all-yolo Deprecated alias for --yolo-destructive",
10853
+ " --yolo Auto-approve all tool calls (including destructive)",
10854
+ " --confirm-destructive In YOLO mode, still prompt for destructive operations",
10855
+ " --yolo-destructive Deprecated \u2014 YOLO now auto-approves everything by default",
10706
10856
  " --tui / --no-tui Force or disable TUI mode",
10857
+ " --webui [--port <n>] [--open] Serve the browser UI + WS bridge (prints a URL,",
10858
+ " --open pops the browser; shares this terminal's",
10859
+ " agent; auto-picks free ports)",
10707
10860
  ' --eternal "<mission>" Start an eternal-autonomy loop',
10708
10861
  " --no-hints Hide launch hints"
10709
10862
  ];
@@ -10932,6 +11085,10 @@ async function boot(argv) {
10932
11085
  return 2;
10933
11086
  }
10934
11087
  }
11088
+ if (flags["webui"]) {
11089
+ flags["tui"] = false;
11090
+ flags["no-tui"] = true;
11091
+ }
10935
11092
  if (isInteractiveTTY) {
10936
11093
  let modePinned;
10937
11094
  if (flags["no-tui"]) modePinned = "repl";
@@ -11370,7 +11527,7 @@ async function runRepl(opts) {
11370
11527
  `[eternal] ${err instanceof Error ? err.message : String(err)}`
11371
11528
  );
11372
11529
  }
11373
- await new Promise((resolve3) => setTimeout(resolve3, 250));
11530
+ await new Promise((resolve4) => setTimeout(resolve4, 250));
11374
11531
  continue;
11375
11532
  }
11376
11533
  } else if (opts.getAutonomy?.() === "eternal-parallel") {
@@ -11416,7 +11573,7 @@ async function runRepl(opts) {
11416
11573
  `[parallel] ${err instanceof Error ? err.message : String(err)}`
11417
11574
  );
11418
11575
  }
11419
- await new Promise((resolve3) => setTimeout(resolve3, 250));
11576
+ await new Promise((resolve4) => setTimeout(resolve4, 250));
11420
11577
  continue;
11421
11578
  }
11422
11579
  }
@@ -11895,10 +12052,10 @@ var EMPTY = "\u2591";
11895
12052
  function renderContextChip(used, max) {
11896
12053
  const ratio = Math.max(0, Math.min(1, used / max));
11897
12054
  const pct2 = Math.round(ratio * 100);
11898
- const bar = renderProgress2(ratio, 6);
12055
+ const bar = renderProgress3(ratio, 6);
11899
12056
  return `${bar} ${pct2}% (${fmtTok(used)}/${fmtTok(max)})`;
11900
12057
  }
11901
- function renderProgress2(ratio, width) {
12058
+ function renderProgress3(ratio, width) {
11902
12059
  const clamped = Math.max(0, Math.min(1, ratio));
11903
12060
  const filled = clamped === 0 ? 0 : Math.max(1, Math.round(clamped * width));
11904
12061
  const capped = Math.min(width, filled);
@@ -12066,7 +12223,7 @@ async function execute(deps) {
12066
12223
  ) + "\n"
12067
12224
  );
12068
12225
  }
12069
- } else if (flags.tui && !flags["no-tui"]) {
12226
+ } else if (flags.tui && !flags["no-tui"] && !flags.webui) {
12070
12227
  agent.disableInteractiveConfirmation();
12071
12228
  const { runTui } = await import('@wrongstack/tui');
12072
12229
  renderer.setSilent(true);
@@ -12255,12 +12412,15 @@ async function execute(deps) {
12255
12412
  renderer.setSilent(false);
12256
12413
  }
12257
12414
  } else if (flags.webui) {
12415
+ agent.disableInteractiveConfirmation();
12258
12416
  const { runWebUI: runWebUI2 } = await Promise.resolve().then(() => (init_webui_server(), webui_server_exports));
12259
12417
  const webuiPromise = runWebUI2({
12260
12418
  agent,
12261
12419
  events,
12262
12420
  session,
12263
12421
  port: Number.parseInt(String(flags.port ?? "3457"), 10),
12422
+ projectRoot,
12423
+ open: !!flags.open,
12264
12424
  modelsRegistry,
12265
12425
  globalConfigPath: wpaths.globalConfig,
12266
12426
  subscribeEternalIteration
@@ -12335,6 +12495,22 @@ async function execute(deps) {
12335
12495
  }
12336
12496
  return code;
12337
12497
  }
12498
+ function buildRoutingRunner(config, host) {
12499
+ const standardRunner = makeAgentSubagentRunner({
12500
+ factory: host.makeSubagentFactory(config),
12501
+ fleetBus: host.getDirector()?.fleet ?? NULL_FLEET_BUS
12502
+ });
12503
+ return async (task, ctx) => {
12504
+ const subCfg = ctx.config;
12505
+ if (subCfg.provider === "acp") {
12506
+ const cacheKey = subCfg.role ?? subCfg.name ?? subCfg.id;
12507
+ return host.buildACPRunner(cacheKey).then((r) => r(task, ctx));
12508
+ }
12509
+ return standardRunner(task, ctx);
12510
+ };
12511
+ }
12512
+
12513
+ // src/fleet/host.ts
12338
12514
  var MultiAgentHost = class {
12339
12515
  constructor(deps, opts = {}) {
12340
12516
  this.deps = deps;
@@ -12966,20 +13142,6 @@ var MultiAgentHost = class {
12966
13142
  }
12967
13143
  }
12968
13144
  };
12969
- function buildRoutingRunner(config, host) {
12970
- const standardRunner = makeAgentSubagentRunner({
12971
- factory: host.makeSubagentFactory(config),
12972
- fleetBus: host.getDirector()?.fleet ?? NULL_FLEET_BUS
12973
- });
12974
- return async (task, ctx) => {
12975
- const subCfg = ctx.config;
12976
- if (subCfg.provider === "acp") {
12977
- const cacheKey = subCfg.role ?? subCfg.name ?? subCfg.id;
12978
- return host.buildACPRunner(cacheKey).then((r) => r(task, ctx));
12979
- }
12980
- return standardRunner(task, ctx);
12981
- };
12982
- }
12983
13145
  function makePromptDelegate(reader) {
12984
13146
  return async (tool, input, suggestedPattern) => {
12985
13147
  writeOut("\x07");
@@ -13191,7 +13353,7 @@ function samplePaths(set) {
13191
13353
  }
13192
13354
  var WORKTREE_PHASE_CONCURRENCY = 4;
13193
13355
  function gitText(args, cwd) {
13194
- return new Promise((resolve3) => {
13356
+ return new Promise((resolve4) => {
13195
13357
  let out = "";
13196
13358
  const child = spawn("git", args, {
13197
13359
  cwd,
@@ -13204,8 +13366,8 @@ function gitText(args, cwd) {
13204
13366
  child.stderr?.on("data", (c) => {
13205
13367
  out += c.toString();
13206
13368
  });
13207
- child.on("error", () => resolve3({ code: 1, out }));
13208
- child.on("close", (code) => resolve3({ code: code ?? 1, out: out.trim() }));
13369
+ child.on("error", () => resolve4({ code: 1, out }));
13370
+ child.on("close", (code) => resolve4({ code: code ?? 1, out: out.trim() }));
13209
13371
  });
13210
13372
  }
13211
13373
  async function isGitRepo(cwd) {
@@ -13213,7 +13375,7 @@ async function isGitRepo(cwd) {
13213
13375
  return code === 0 && out.trim() === "true";
13214
13376
  }
13215
13377
  function runCmd(cmd, args, cwd, shell = false) {
13216
- return new Promise((resolve3) => {
13378
+ return new Promise((resolve4) => {
13217
13379
  let out = "";
13218
13380
  const child = spawn(cmd, args, {
13219
13381
  cwd,
@@ -13227,8 +13389,8 @@ function runCmd(cmd, args, cwd, shell = false) {
13227
13389
  child.stderr?.on("data", (c) => {
13228
13390
  out += c.toString();
13229
13391
  });
13230
- child.on("error", (e) => resolve3({ code: 1, out: `${out}${String(e)}` }));
13231
- child.on("close", (code) => resolve3({ code: code ?? 1, out: out.trim() }));
13392
+ child.on("error", (e) => resolve4({ code: 1, out: `${out}${String(e)}` }));
13393
+ child.on("close", (code) => resolve4({ code: code ?? 1, out: out.trim() }));
13232
13394
  });
13233
13395
  }
13234
13396
  function detectPackageManager2(root) {
@@ -13612,22 +13774,15 @@ function renderContextChip2(ctx) {
13612
13774
  const ratio = Math.max(0, Math.min(1, ctx.used / ctx.max));
13613
13775
  const pct2 = Math.round(ratio * 100);
13614
13776
  const chipColor = ratio >= 0.85 ? color.red : ratio >= 0.65 ? color.yellow : color.cyan;
13615
- const bar = renderProgress3(ratio, 8);
13777
+ const bar = renderProgress4(ratio, 8);
13616
13778
  return color.dim("ctx ") + chipColor(bar) + chipColor(` ${pct2}%`) + color.dim(` (${fmtTok(ctx.used)}/${fmtTok(ctx.max)})`);
13617
13779
  }
13618
- function renderProgress3(ratio, width) {
13780
+ function renderProgress4(ratio, width) {
13619
13781
  const clamped = Math.max(0, Math.min(1, ratio));
13620
13782
  const filled = clamped === 0 ? 0 : Math.max(1, Math.round(clamped * width));
13621
13783
  const capped = Math.min(width, filled);
13622
13784
  return FILLED2.repeat(capped) + EMPTY2.repeat(width - capped);
13623
13785
  }
13624
- var createSessionEventBridge = (_writer, level) => ({
13625
- append: async (_e) => {
13626
- },
13627
- level: level ?? "standard",
13628
- allows: () => true
13629
- });
13630
- var resolveAuditLevel = (cfg) => cfg?.session?.auditLevel ?? "standard";
13631
13786
  function setupPipelines(params) {
13632
13787
  const { events, logger } = params;
13633
13788
  const pipelines = createDefaultPipelines();
@@ -13882,7 +14037,7 @@ function setupMetrics(params) {
13882
14037
  name: "session-store",
13883
14038
  check: async () => {
13884
14039
  try {
13885
- await fsp3.access(wpaths.projectSessions);
14040
+ await fsp4.access(wpaths.projectSessions);
13886
14041
  return { status: "healthy" };
13887
14042
  } catch (e) {
13888
14043
  return { status: "unhealthy", detail: e instanceof Error ? e.message : "access denied" };
@@ -14265,21 +14420,6 @@ async function promptRecovery(reader, renderer, abandoned, autoRecover) {
14265
14420
  );
14266
14421
  return answer;
14267
14422
  }
14268
- var isMain = import.meta.url === `file://${process.argv[1]?.replace(/\\/g, "/")}` || process.argv[1]?.endsWith("/cli/dist/index.js") || process.argv[1]?.endsWith("\\cli\\dist\\index.js");
14269
- function runAsMain(mainFn) {
14270
- if (!isMain) return;
14271
- mainFn(process.argv.slice(2)).then(
14272
- (c) => {
14273
- process.exitCode = c;
14274
- setTimeout(() => process.exit(c), 500).unref();
14275
- },
14276
- (err) => {
14277
- writeErr((err instanceof Error ? err.stack : String(err)) + "\n");
14278
- process.exitCode = 1;
14279
- setTimeout(() => process.exit(1), 500).unref();
14280
- }
14281
- );
14282
- }
14283
14423
  async function launchEternalFromFlag(deps) {
14284
14424
  const { eternalFlag } = deps;
14285
14425
  if (eternalFlag.length === 0) return false;
@@ -14315,22 +14455,7 @@ async function launchEternalFromFlag(deps) {
14315
14455
  return true;
14316
14456
  }
14317
14457
 
14318
- // src/index.ts
14319
- var createSessionEventBridge2 = (_writer, level, _opts) => ({
14320
- append: async (_e) => {
14321
- },
14322
- level: level ?? "standard",
14323
- allows: () => true
14324
- });
14325
- var resolveAuditLevel2 = (cfg) => cfg?.session?.auditLevel ?? "standard";
14326
- var resolveSessionLoggingConfig = (cfg) => ({
14327
- auditLevel: resolveAuditLevel2(cfg),
14328
- sampling: {
14329
- toolProgress: {
14330
- sampleRate: cfg?.session?.sampling?.toolProgress?.sampleRate ?? 8
14331
- }
14332
- }
14333
- });
14458
+ // src/cli-main.ts
14334
14459
  async function main(argv) {
14335
14460
  const ctx = await boot(argv);
14336
14461
  if (typeof ctx === "number") return ctx;
@@ -14358,6 +14483,7 @@ async function main(argv) {
14358
14483
  permission: {
14359
14484
  yolo: config.yolo,
14360
14485
  yoloDestructive: flags["yolo-destructive"] === true || flags["force-all-yolo"] === true,
14486
+ confirmDestructive: flags["confirm-destructive"] === true,
14361
14487
  promptDelegate: makePromptDelegate(reader)
14362
14488
  },
14363
14489
  compactor: {
@@ -14548,9 +14674,13 @@ async function main(argv) {
14548
14674
  const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
14549
14675
  const priorFleetState = sessResult.priorFleetState;
14550
14676
  const sessionConfig = resolveSessionLoggingConfig(config);
14551
- const sessionBridge = createSessionEventBridge2(
14677
+ const sessionBridge = createSessionEventBridge(
14552
14678
  session,
14553
- sessionConfig.auditLevel);
14679
+ sessionConfig.auditLevel,
14680
+ {
14681
+ sampling: sessionConfig.sampling
14682
+ }
14683
+ );
14554
14684
  const stats = new SessionStats(events, tokenCounter);
14555
14685
  const errorRing = [];
14556
14686
  events.on("error", (e) => {
@@ -15146,7 +15276,7 @@ async function main(argv) {
15146
15276
  const subagentsRoot = path8.join(fleetRootForPromotion, "subagents");
15147
15277
  let runDirs;
15148
15278
  try {
15149
- runDirs = await fsp3.readdir(subagentsRoot);
15279
+ runDirs = await fsp4.readdir(subagentsRoot);
15150
15280
  } catch {
15151
15281
  return "No fleet transcripts on disk \u2014 no subagents have been spawned for this session.";
15152
15282
  }
@@ -15155,7 +15285,7 @@ async function main(argv) {
15155
15285
  const runDir = path8.join(subagentsRoot, runId);
15156
15286
  let files;
15157
15287
  try {
15158
- files = await fsp3.readdir(runDir);
15288
+ files = await fsp4.readdir(runDir);
15159
15289
  } catch {
15160
15290
  continue;
15161
15291
  }
@@ -15163,7 +15293,7 @@ async function main(argv) {
15163
15293
  if (!f.endsWith(".jsonl")) continue;
15164
15294
  const full = path8.join(runDir, f);
15165
15295
  try {
15166
- const stat4 = await fsp3.stat(full);
15296
+ const stat4 = await fsp4.stat(full);
15167
15297
  found.push({
15168
15298
  runId,
15169
15299
  subagentId: f.replace(/\.jsonl$/, ""),
@@ -15204,7 +15334,7 @@ async function main(argv) {
15204
15334
  ].join("\n");
15205
15335
  }
15206
15336
  const t = matches[0];
15207
- const raw = await fsp3.readFile(t.file, "utf8");
15337
+ const raw = await fsp4.readFile(t.file, "utf8");
15208
15338
  if (mode === "raw") return raw;
15209
15339
  const lines = raw.split("\n").filter((l) => l.trim());
15210
15340
  const counts = {};
@@ -15460,7 +15590,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
15460
15590
  const { spawn: spawn3 } = await import('child_process');
15461
15591
  const cwd2 = projectRoot;
15462
15592
  const statusResult = await new Promise(
15463
- (resolve3, reject) => {
15593
+ (resolve4, reject) => {
15464
15594
  const child = spawn3("git", ["status", "--porcelain"], {
15465
15595
  cwd: cwd2,
15466
15596
  stdio: ["ignore", "pipe", "pipe"]
@@ -15470,7 +15600,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
15470
15600
  stdout += d;
15471
15601
  });
15472
15602
  child.on("error", reject);
15473
- child.on("close", (code) => resolve3({ stdout, code: code ?? 0 }));
15603
+ child.on("close", (code) => resolve4({ stdout, code: code ?? 0 }));
15474
15604
  }
15475
15605
  );
15476
15606
  if (statusResult.stdout.trim().length > 0) {
@@ -15655,6 +15785,23 @@ Restart WrongStack to load or unload plugin code in this session.`;
15655
15785
  skillLoader: config.features.skills ? skillLoader : void 0
15656
15786
  });
15657
15787
  }
15788
+ var isMain = import.meta.url === `file://${process.argv[1]?.replace(/\\/g, "/")}` || process.argv[1]?.endsWith("/cli/dist/index.js") || process.argv[1]?.endsWith("\\cli\\dist\\index.js");
15789
+ function runAsMain(mainFn) {
15790
+ if (!isMain) return;
15791
+ mainFn(process.argv.slice(2)).then(
15792
+ (c) => {
15793
+ process.exitCode = c;
15794
+ setTimeout(() => process.exit(c), 500).unref();
15795
+ },
15796
+ (err) => {
15797
+ writeErr((err instanceof Error ? err.stack : String(err)) + "\n");
15798
+ process.exitCode = 1;
15799
+ setTimeout(() => process.exit(1), 500).unref();
15800
+ }
15801
+ );
15802
+ }
15803
+
15804
+ // src/index.ts
15658
15805
  runAsMain(main);
15659
15806
 
15660
15807
  export { CLI_VERSION, main };