@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 +729 -582
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
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
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
return
|
|
374
|
-
|
|
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
|
-
|
|
377
|
-
|
|
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
|
|
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
|
|
1243
|
+
await fsp4.unlink(sessionPath);
|
|
1055
1244
|
deletedFromDisk = true;
|
|
1056
1245
|
} catch {
|
|
1057
1246
|
}
|
|
1058
1247
|
try {
|
|
1059
|
-
await
|
|
1248
|
+
await fsp4.rm(opts.paths.projectSpecs, { recursive: true, force: true });
|
|
1060
1249
|
} catch {
|
|
1061
1250
|
}
|
|
1062
1251
|
try {
|
|
1063
|
-
await
|
|
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
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
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
|
|
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
|
|
1615
|
-
await
|
|
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
|
|
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
|
|
1703
|
-
console.log(`[WebUI] WebSocket server starting on ws
|
|
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((
|
|
1939
|
+
return new Promise((resolve4) => {
|
|
1818
1940
|
wss.on("listening", () => {
|
|
1819
|
-
console.log(`[WebUI] WebSocket server running on ws
|
|
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
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4992
|
-
await
|
|
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
|
|
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
|
|
5217
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
6533
|
-
" /yolo on
|
|
6534
|
-
" /yolo off
|
|
6535
|
-
" /yolo
|
|
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
|
|
6538
|
-
"
|
|
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
|
|
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
|
|
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
|
|
6657
|
-
await
|
|
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
|
|
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((
|
|
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 ?
|
|
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
|
|
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
|
|
6849
|
-
await
|
|
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((
|
|
7031
|
+
await new Promise((resolve4) => setTimeout(resolve4, 50));
|
|
6868
7032
|
}
|
|
6869
7033
|
this.pending = true;
|
|
6870
7034
|
try {
|
|
6871
|
-
|
|
6872
|
-
|
|
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((
|
|
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
|
-
|
|
7054
|
+
resolve4(line);
|
|
6884
7055
|
});
|
|
6885
|
-
fresh.once("close", () =>
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
7088
|
+
resolve4(opt.value);
|
|
6919
7089
|
}
|
|
6920
7090
|
};
|
|
6921
7091
|
const onClose = () => {
|
|
6922
7092
|
cleanup();
|
|
6923
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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((
|
|
8874
|
-
const
|
|
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) =>
|
|
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
|
|
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
|
|
9172
|
+
await fsp4.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
9023
9173
|
const probe = path8.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
9024
|
-
await
|
|
9025
|
-
await
|
|
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
|
|
9127
|
-
await
|
|
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
|
|
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
|
|
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((
|
|
9341
|
-
const stop = () =>
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
10210
|
+
await fsp4.access(path8.join(runDir, "fleet.json"));
|
|
10061
10211
|
manifest = true;
|
|
10062
10212
|
} catch {
|
|
10063
10213
|
}
|
|
10064
10214
|
try {
|
|
10065
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
10704
|
-
" --
|
|
10705
|
-
" --
|
|
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((
|
|
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((
|
|
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 =
|
|
12055
|
+
const bar = renderProgress3(ratio, 6);
|
|
11899
12056
|
return `${bar} ${pct2}% (${fmtTok(used)}/${fmtTok(max)})`;
|
|
11900
12057
|
}
|
|
11901
|
-
function
|
|
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((
|
|
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", () =>
|
|
13208
|
-
child.on("close", (code) =>
|
|
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((
|
|
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) =>
|
|
13231
|
-
child.on("close", (code) =>
|
|
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 =
|
|
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
|
|
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
|
|
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/
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
(
|
|
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) =>
|
|
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 };
|