@launch11/srgical 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -7
- package/dist/commands/doctor.js +60 -13
- package/dist/commands/init.js +10 -6
- package/dist/commands/run-next.js +41 -8
- package/dist/commands/studio.js +2 -2
- package/dist/core/agent.js +19 -19
- package/dist/core/augment.js +7 -7
- package/dist/core/auto-run-state.js +89 -0
- package/dist/core/auto-run.js +270 -0
- package/dist/core/claude.js +7 -7
- package/dist/core/codex.js +7 -7
- package/dist/core/execution-state.js +5 -5
- package/dist/core/local-pack.js +9 -6
- package/dist/core/planning-epochs.js +10 -3
- package/dist/core/planning-pack-state.js +121 -9
- package/dist/core/planning-state.js +61 -0
- package/dist/core/prompts.js +4 -4
- package/dist/core/studio-session.js +14 -14
- package/dist/core/templates.js +13 -10
- package/dist/core/workspace.js +127 -8
- package/dist/index.js +18 -6
- package/dist/ui/studio.js +329 -193
- package/package.json +1 -1
package/dist/ui/studio.js
CHANGED
|
@@ -11,17 +11,22 @@ exports.renderWorkspaceSelectionMessage = renderWorkspaceSelectionMessage;
|
|
|
11
11
|
exports.resolveStudioWorkspaceInput = resolveStudioWorkspaceInput;
|
|
12
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
13
|
const blessed_1 = __importDefault(require("blessed"));
|
|
14
|
+
const auto_run_1 = require("../core/auto-run");
|
|
14
15
|
const agent_1 = require("../core/agent");
|
|
15
16
|
const execution_controls_1 = require("../core/execution-controls");
|
|
16
17
|
const execution_state_1 = require("../core/execution-state");
|
|
17
18
|
const planning_pack_state_1 = require("../core/planning-pack-state");
|
|
19
|
+
const planning_state_1 = require("../core/planning-state");
|
|
18
20
|
const studio_session_1 = require("../core/studio-session");
|
|
21
|
+
const templates_1 = require("../core/templates");
|
|
19
22
|
const workspace_1 = require("../core/workspace");
|
|
20
|
-
const READY_FOOTER = "
|
|
23
|
+
const READY_FOOTER = " PgUp/PgDn scroll /agents choose tool /help commands ";
|
|
21
24
|
const ACTIVITY_FRAMES = ["[ ]", "[= ]", "[== ]", "[=== ]", "[ ===]", "[ ==]", "[ =]"];
|
|
22
25
|
async function launchStudio(options = {}) {
|
|
23
26
|
let workspace = (0, workspace_1.resolveWorkspace)(options.workspace);
|
|
24
|
-
let
|
|
27
|
+
let planId = await (0, workspace_1.resolvePlanId)(workspace, options.planId);
|
|
28
|
+
await (0, workspace_1.saveActivePlanId)(workspace, planId);
|
|
29
|
+
let messages = await (0, studio_session_1.loadStudioSession)(workspace, { planId });
|
|
25
30
|
const screen = blessed_1.default.screen({
|
|
26
31
|
smartCSR: true,
|
|
27
32
|
fullUnicode: true,
|
|
@@ -43,7 +48,7 @@ async function launchStudio(options = {}) {
|
|
|
43
48
|
top: 3,
|
|
44
49
|
left: 0,
|
|
45
50
|
width: "72%",
|
|
46
|
-
height: "100%-
|
|
51
|
+
height: "100%-8",
|
|
47
52
|
tags: true,
|
|
48
53
|
scrollable: true,
|
|
49
54
|
alwaysScroll: true,
|
|
@@ -74,7 +79,7 @@ async function launchStudio(options = {}) {
|
|
|
74
79
|
top: 3,
|
|
75
80
|
left: "72%",
|
|
76
81
|
width: "28%",
|
|
77
|
-
height: "100%-
|
|
82
|
+
height: "100%-8",
|
|
78
83
|
tags: true,
|
|
79
84
|
padding: {
|
|
80
85
|
top: 1,
|
|
@@ -94,12 +99,14 @@ async function launchStudio(options = {}) {
|
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
});
|
|
97
|
-
const input = blessed_1.default.
|
|
102
|
+
const input = blessed_1.default.textarea({
|
|
98
103
|
bottom: 1,
|
|
99
104
|
left: 0,
|
|
100
105
|
width: "100%",
|
|
101
|
-
height:
|
|
106
|
+
height: 4,
|
|
102
107
|
inputOnFocus: true,
|
|
108
|
+
keys: true,
|
|
109
|
+
mouse: true,
|
|
103
110
|
border: {
|
|
104
111
|
type: "line"
|
|
105
112
|
},
|
|
@@ -112,6 +119,7 @@ async function launchStudio(options = {}) {
|
|
|
112
119
|
}
|
|
113
120
|
}
|
|
114
121
|
});
|
|
122
|
+
configureComposer(input);
|
|
115
123
|
const footer = blessed_1.default.box({
|
|
116
124
|
bottom: 0,
|
|
117
125
|
left: 0,
|
|
@@ -139,12 +147,14 @@ async function launchStudio(options = {}) {
|
|
|
139
147
|
let planningPackSummary = "not checked";
|
|
140
148
|
let trackerSummary = "loading...";
|
|
141
149
|
let executionSummary = "never run";
|
|
150
|
+
let autoSummary = "idle";
|
|
142
151
|
function setSidebar(status) {
|
|
143
|
-
const planningPaths = (0, workspace_1.getPlanningPackPaths)(workspace);
|
|
152
|
+
const planningPaths = (0, workspace_1.getPlanningPackPaths)(workspace, { planId });
|
|
144
153
|
sidebar.setContent([
|
|
145
154
|
"{bold}Workspace{/bold}",
|
|
146
155
|
`root: ${workspace}`,
|
|
147
|
-
`plan
|
|
156
|
+
`plan: ${planId}`,
|
|
157
|
+
`plan dir: ${planningPaths.relativeDir}`,
|
|
148
158
|
"",
|
|
149
159
|
"{bold}Agent{/bold}",
|
|
150
160
|
agentSummary,
|
|
@@ -158,15 +168,8 @@ async function launchStudio(options = {}) {
|
|
|
158
168
|
"{bold}Last Run{/bold}",
|
|
159
169
|
executionSummary,
|
|
160
170
|
"",
|
|
161
|
-
"{bold}
|
|
162
|
-
|
|
163
|
-
"/agents list supported tools and the current session choice",
|
|
164
|
-
"/agent <id> switch the active agent for this workspace",
|
|
165
|
-
"/write put the current plan on disk",
|
|
166
|
-
"/preview inspect the next execution without invoking the active agent",
|
|
167
|
-
"/run execute .srgical/04-next-agent-prompt.md",
|
|
168
|
-
"/help show the workflow and key controls",
|
|
169
|
-
"/quit close the studio",
|
|
171
|
+
"{bold}Auto{/bold}",
|
|
172
|
+
autoSummary,
|
|
170
173
|
"",
|
|
171
174
|
"{bold}State{/bold}",
|
|
172
175
|
status ?? getActivityState()
|
|
@@ -239,18 +242,28 @@ async function launchStudio(options = {}) {
|
|
|
239
242
|
}
|
|
240
243
|
async function appendMessage(message) {
|
|
241
244
|
messages.push(message);
|
|
242
|
-
await (0, studio_session_1.saveStudioSession)(workspace, messages);
|
|
245
|
+
await (0, studio_session_1.saveStudioSession)(workspace, messages, { planId });
|
|
246
|
+
}
|
|
247
|
+
async function appendSystemMessage(content) {
|
|
248
|
+
await appendMessage({
|
|
249
|
+
role: "system",
|
|
250
|
+
content
|
|
251
|
+
});
|
|
252
|
+
renderTranscript();
|
|
253
|
+
setSidebar();
|
|
254
|
+
setFooter();
|
|
255
|
+
screen.render();
|
|
243
256
|
}
|
|
244
257
|
async function refreshEnvironment() {
|
|
245
258
|
const [storedAgentId, packState] = await Promise.all([
|
|
246
|
-
(0, studio_session_1.loadStoredActiveAgentId)(workspace),
|
|
247
|
-
(0, planning_pack_state_1.readPlanningPackState)(workspace)
|
|
259
|
+
(0, studio_session_1.loadStoredActiveAgentId)(workspace, { planId }),
|
|
260
|
+
(0, planning_pack_state_1.readPlanningPackState)(workspace, { planId })
|
|
248
261
|
]);
|
|
249
|
-
let agentState = await (0, agent_1.resolvePrimaryAgent)(workspace);
|
|
262
|
+
let agentState = await (0, agent_1.resolvePrimaryAgent)(workspace, { planId });
|
|
250
263
|
if (!storedAgentId) {
|
|
251
264
|
const availableAgents = agentState.statuses.filter((status) => status.available);
|
|
252
265
|
if (availableAgents.length === 1) {
|
|
253
|
-
agentState = await (0, agent_1.selectPrimaryAgent)(workspace, availableAgents[0].id);
|
|
266
|
+
agentState = await (0, agent_1.selectPrimaryAgent)(workspace, availableAgents[0].id, { planId });
|
|
254
267
|
}
|
|
255
268
|
}
|
|
256
269
|
latestPackState = packState;
|
|
@@ -259,112 +272,126 @@ async function launchStudio(options = {}) {
|
|
|
259
272
|
planningPackSummary = formatPlanningPackSummary(workspace, packState);
|
|
260
273
|
trackerSummary = formatTrackerSummary(packState.currentPosition);
|
|
261
274
|
executionSummary = formatExecutionSummary(packState.lastExecution);
|
|
275
|
+
autoSummary = formatAutoSummary(packState);
|
|
262
276
|
setSidebar();
|
|
263
277
|
setFooter();
|
|
264
278
|
renderTranscript();
|
|
265
279
|
screen.render();
|
|
266
280
|
}
|
|
281
|
+
async function switchPlan(nextPlanId) {
|
|
282
|
+
planId = (0, workspace_1.normalizePlanId)(nextPlanId);
|
|
283
|
+
await (0, workspace_1.saveActivePlanId)(workspace, planId);
|
|
284
|
+
messages = await (0, studio_session_1.loadStudioSession)(workspace, { planId });
|
|
285
|
+
await refreshEnvironment();
|
|
286
|
+
}
|
|
267
287
|
async function handleSlashCommand(command) {
|
|
268
288
|
if (command === "/quit") {
|
|
269
289
|
screen.destroy();
|
|
270
290
|
return;
|
|
271
291
|
}
|
|
272
292
|
if (command === "/workspace") {
|
|
273
|
-
const packState = latestPackState ?? (await (0, planning_pack_state_1.readPlanningPackState)(workspace));
|
|
274
|
-
await
|
|
275
|
-
role: "system",
|
|
276
|
-
content: renderWorkspaceSelectionMessage(workspace, packState)
|
|
277
|
-
});
|
|
278
|
-
renderTranscript();
|
|
279
|
-
setSidebar();
|
|
280
|
-
setFooter();
|
|
281
|
-
screen.render();
|
|
293
|
+
const packState = latestPackState ?? (await (0, planning_pack_state_1.readPlanningPackState)(workspace, { planId }));
|
|
294
|
+
await appendSystemMessage(renderWorkspaceSelectionMessage(workspace, packState));
|
|
282
295
|
return;
|
|
283
296
|
}
|
|
284
297
|
if (command.startsWith("/workspace")) {
|
|
285
298
|
const requestedWorkspace = command.slice("/workspace".length).trim();
|
|
286
299
|
if (!requestedWorkspace) {
|
|
287
|
-
const packState = latestPackState ?? (await (0, planning_pack_state_1.readPlanningPackState)(workspace));
|
|
288
|
-
await
|
|
289
|
-
role: "system",
|
|
290
|
-
content: renderWorkspaceSelectionMessage(workspace, packState)
|
|
291
|
-
});
|
|
292
|
-
renderTranscript();
|
|
293
|
-
setSidebar();
|
|
294
|
-
setFooter();
|
|
295
|
-
screen.render();
|
|
300
|
+
const packState = latestPackState ?? (await (0, planning_pack_state_1.readPlanningPackState)(workspace, { planId }));
|
|
301
|
+
await appendSystemMessage(renderWorkspaceSelectionMessage(workspace, packState));
|
|
296
302
|
return;
|
|
297
303
|
}
|
|
298
304
|
const nextWorkspace = resolveStudioWorkspaceInput(workspace, requestedWorkspace);
|
|
299
|
-
setSidebar("switching
|
|
300
|
-
setFooter(" Switching
|
|
305
|
+
setSidebar("switching workspace...");
|
|
306
|
+
setFooter(" Switching workspace... ");
|
|
301
307
|
screen.render();
|
|
302
308
|
workspace = nextWorkspace;
|
|
303
|
-
|
|
309
|
+
planId = await (0, workspace_1.resolvePlanId)(workspace);
|
|
310
|
+
await (0, workspace_1.saveActivePlanId)(workspace, planId);
|
|
311
|
+
messages = await (0, studio_session_1.loadStudioSession)(workspace, { planId });
|
|
304
312
|
await refreshEnvironment();
|
|
305
|
-
await
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
await appendSystemMessage([
|
|
314
|
+
`Now looking at ${workspace}.`,
|
|
315
|
+
"",
|
|
316
|
+
renderWorkspaceSelectionMessage(workspace, latestPackState ?? (await (0, planning_pack_state_1.readPlanningPackState)(workspace, { planId })))
|
|
317
|
+
].join("\n"));
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (command === "/plans") {
|
|
321
|
+
await appendSystemMessage(await renderPlansMessage(workspace, planId));
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (command === "/plan") {
|
|
325
|
+
await appendSystemMessage(renderPlanUsageMessage(planId, (0, workspace_1.getPlanningPackPaths)(workspace, { planId })));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (command.startsWith("/plan new ")) {
|
|
329
|
+
const requestedPlanId = command.slice("/plan new ".length).trim();
|
|
330
|
+
if (!requestedPlanId) {
|
|
331
|
+
await appendSystemMessage("Usage: `/plan new <id>`");
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
setSidebar("creating named plan...");
|
|
335
|
+
setFooter(" Creating named plan... ");
|
|
316
336
|
screen.render();
|
|
337
|
+
const createdPaths = await createPlanScaffold(workspace, requestedPlanId);
|
|
338
|
+
await switchPlan(createdPaths.planId);
|
|
339
|
+
await appendSystemMessage([
|
|
340
|
+
`Created and selected plan \`${createdPaths.planId}\`.`,
|
|
341
|
+
`Planning directory: ${createdPaths.relativeDir}`,
|
|
342
|
+
"Use `/readiness` while gathering context, then `/write` when the plan is ready."
|
|
343
|
+
].join("\n"));
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (command.startsWith("/plan ")) {
|
|
347
|
+
const requestedPlanId = command.slice("/plan ".length).trim();
|
|
348
|
+
if (!requestedPlanId) {
|
|
349
|
+
await appendSystemMessage(renderPlanUsageMessage(planId, (0, workspace_1.getPlanningPackPaths)(workspace, { planId })));
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
await switchPlan(requestedPlanId);
|
|
353
|
+
await appendSystemMessage(`Active plan set to \`${planId}\`.\nPlanning directory: ${(0, workspace_1.getPlanningPackPaths)(workspace, { planId }).relativeDir}`);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (command === "/status") {
|
|
357
|
+
const packState = latestPackState ?? (await (0, planning_pack_state_1.readPlanningPackState)(workspace, { planId }));
|
|
358
|
+
await appendSystemMessage(renderStatusMessage(workspace, packState));
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (command === "/readiness") {
|
|
362
|
+
const packState = latestPackState ?? (await (0, planning_pack_state_1.readPlanningPackState)(workspace, { planId }));
|
|
363
|
+
await appendSystemMessage(renderReadinessMessage(packState));
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (command === "/stop") {
|
|
367
|
+
const state = await (0, auto_run_1.requestAutoRunStop)(workspace, { planId });
|
|
368
|
+
await refreshEnvironment();
|
|
369
|
+
await appendSystemMessage(state.status === "stop_requested"
|
|
370
|
+
? "Stop requested. Auto mode will finish the current iteration before stopping."
|
|
371
|
+
: "No active auto run was in progress.");
|
|
317
372
|
return;
|
|
318
373
|
}
|
|
319
374
|
if (command === "/agents") {
|
|
320
|
-
const agentState = await (0, agent_1.resolvePrimaryAgent)(workspace);
|
|
321
|
-
await
|
|
322
|
-
role: "system",
|
|
323
|
-
content: renderAgentSelectionMessage(agentState.status, agentState.statuses)
|
|
324
|
-
});
|
|
325
|
-
renderTranscript();
|
|
326
|
-
setSidebar();
|
|
327
|
-
setFooter();
|
|
328
|
-
screen.render();
|
|
375
|
+
const agentState = await (0, agent_1.resolvePrimaryAgent)(workspace, { planId });
|
|
376
|
+
await appendSystemMessage(renderAgentSelectionMessage(agentState.status, agentState.statuses));
|
|
329
377
|
return;
|
|
330
378
|
}
|
|
331
379
|
if (command.startsWith("/agent")) {
|
|
332
380
|
const requestedId = command.slice("/agent".length).trim().toLowerCase();
|
|
333
381
|
if (!requestedId) {
|
|
334
|
-
const agentState = await (0, agent_1.resolvePrimaryAgent)(workspace);
|
|
335
|
-
await
|
|
336
|
-
role: "system",
|
|
337
|
-
content: [
|
|
338
|
-
buildAgentUsageMessage(),
|
|
339
|
-
"",
|
|
340
|
-
renderAgentSelectionMessage(agentState.status, agentState.statuses)
|
|
341
|
-
].join("\n")
|
|
342
|
-
});
|
|
343
|
-
renderTranscript();
|
|
344
|
-
setSidebar();
|
|
345
|
-
setFooter();
|
|
346
|
-
screen.render();
|
|
382
|
+
const agentState = await (0, agent_1.resolvePrimaryAgent)(workspace, { planId });
|
|
383
|
+
await appendSystemMessage([buildAgentUsageMessage(), "", renderAgentSelectionMessage(agentState.status, agentState.statuses)].join("\n"));
|
|
347
384
|
return;
|
|
348
385
|
}
|
|
349
386
|
setSidebar("updating active agent...");
|
|
350
387
|
setFooter(" Updating active agent selection... ");
|
|
351
388
|
screen.render();
|
|
352
389
|
try {
|
|
353
|
-
const agentState = await (0, agent_1.selectPrimaryAgent)(workspace, requestedId);
|
|
354
|
-
await
|
|
355
|
-
role: "system",
|
|
356
|
-
content: [
|
|
357
|
-
`Active agent set to ${agentState.status.label} for this workspace session.`,
|
|
358
|
-
"",
|
|
359
|
-
renderAgentSelectionMessage(agentState.status, agentState.statuses)
|
|
360
|
-
].join("\n")
|
|
361
|
-
});
|
|
390
|
+
const agentState = await (0, agent_1.selectPrimaryAgent)(workspace, requestedId, { planId });
|
|
391
|
+
await appendSystemMessage([`Active agent set to ${agentState.status.label} for plan \`${planId}\`.`, "", renderAgentSelectionMessage(agentState.status, agentState.statuses)].join("\n"));
|
|
362
392
|
}
|
|
363
393
|
catch (error) {
|
|
364
|
-
await
|
|
365
|
-
role: "system",
|
|
366
|
-
content: `Agent selection failed: ${error instanceof Error ? error.message : String(error)}`
|
|
367
|
-
});
|
|
394
|
+
await appendSystemMessage(`Agent selection failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
368
395
|
}
|
|
369
396
|
finally {
|
|
370
397
|
await refreshEnvironment();
|
|
@@ -372,68 +399,43 @@ async function launchStudio(options = {}) {
|
|
|
372
399
|
return;
|
|
373
400
|
}
|
|
374
401
|
if (command === "/help") {
|
|
375
|
-
await
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
].join("\n")
|
|
391
|
-
});
|
|
392
|
-
renderTranscript();
|
|
393
|
-
setSidebar();
|
|
394
|
-
setFooter();
|
|
395
|
-
screen.render();
|
|
402
|
+
await appendSystemMessage([
|
|
403
|
+
"Workflow:",
|
|
404
|
+
"1. Talk normally to sharpen the plan against the real repo.",
|
|
405
|
+
"2. Use `/plans`, `/plan`, and `/plan new <id>` to manage named planning packs in this workspace.",
|
|
406
|
+
"3. Use `/readiness` to see what context is still missing before you write the pack.",
|
|
407
|
+
"4. Run `/write` when the plan is ready to put on disk.",
|
|
408
|
+
"5. Run `/preview` for a safe execution preview, `/run` for one execution step, or `/auto [max]` for continuous execution.",
|
|
409
|
+
"6. Run `/stop` to stop auto mode after the current iteration.",
|
|
410
|
+
"",
|
|
411
|
+
"Controls:",
|
|
412
|
+
"- `Enter` sends the current message or command.",
|
|
413
|
+
"- `Shift+Enter`, `Alt+Enter`, or `Ctrl+J` inserts a new line when the terminal exposes those keys distinctly.",
|
|
414
|
+
"- `PageUp` and `PageDown` scroll the transcript.",
|
|
415
|
+
"- `/quit` closes the studio."
|
|
416
|
+
].join("\n"));
|
|
396
417
|
return;
|
|
397
418
|
}
|
|
398
419
|
if (command === "/preview") {
|
|
399
|
-
const paths = (0, workspace_1.getPlanningPackPaths)(workspace);
|
|
400
|
-
const packState = await (0, planning_pack_state_1.readPlanningPackState)(workspace);
|
|
420
|
+
const paths = (0, workspace_1.getPlanningPackPaths)(workspace, { planId });
|
|
421
|
+
const packState = await (0, planning_pack_state_1.readPlanningPackState)(workspace, { planId });
|
|
401
422
|
if (!packState.packPresent) {
|
|
402
|
-
await
|
|
403
|
-
role: "system",
|
|
404
|
-
content: "Execution preview unavailable: no .srgical planning pack was found yet."
|
|
405
|
-
});
|
|
406
|
-
renderTranscript();
|
|
407
|
-
setSidebar();
|
|
408
|
-
setFooter();
|
|
409
|
-
screen.render();
|
|
423
|
+
await appendSystemMessage("Execution preview unavailable: no planning pack was found for the selected plan yet.");
|
|
410
424
|
return;
|
|
411
425
|
}
|
|
412
426
|
const prompt = await (0, workspace_1.readText)(paths.nextPrompt);
|
|
413
|
-
await
|
|
414
|
-
role: "system",
|
|
415
|
-
content: (0, execution_controls_1.renderDryRunPreview)(prompt, packState.nextStepSummary, packState.currentPosition.nextRecommended).join("\n")
|
|
416
|
-
});
|
|
417
|
-
renderTranscript();
|
|
418
|
-
setSidebar();
|
|
419
|
-
setFooter();
|
|
420
|
-
screen.render();
|
|
427
|
+
await appendSystemMessage((0, execution_controls_1.renderDryRunPreview)(prompt, packState.nextStepSummary, packState.currentPosition.nextRecommended).join("\n"));
|
|
421
428
|
return;
|
|
422
429
|
}
|
|
423
430
|
if (command === "/write") {
|
|
424
431
|
startBusy("pack");
|
|
425
432
|
try {
|
|
426
|
-
const result = await (0, agent_1.writePlanningPack)(workspace, messages);
|
|
427
|
-
await
|
|
428
|
-
|
|
429
|
-
content: `Planning pack updated. Summary:\n${result}`
|
|
430
|
-
});
|
|
433
|
+
const result = await (0, agent_1.writePlanningPack)(workspace, messages, { planId });
|
|
434
|
+
await (0, planning_state_1.markPlanningPackAuthored)(workspace, { planId });
|
|
435
|
+
await appendSystemMessage(`Planning pack updated for \`${planId}\`. Summary:\n${result}`);
|
|
431
436
|
}
|
|
432
437
|
catch (error) {
|
|
433
|
-
await
|
|
434
|
-
role: "system",
|
|
435
|
-
content: `Pack generation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
436
|
-
});
|
|
438
|
+
await appendSystemMessage(`Pack generation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
437
439
|
}
|
|
438
440
|
finally {
|
|
439
441
|
stopBusy();
|
|
@@ -442,37 +444,32 @@ async function launchStudio(options = {}) {
|
|
|
442
444
|
return;
|
|
443
445
|
}
|
|
444
446
|
if (command === "/run") {
|
|
445
|
-
const paths = (0, workspace_1.getPlanningPackPaths)(workspace);
|
|
446
|
-
const packState = await (0, planning_pack_state_1.readPlanningPackState)(workspace);
|
|
447
|
+
const paths = (0, workspace_1.getPlanningPackPaths)(workspace, { planId });
|
|
448
|
+
const packState = await (0, planning_pack_state_1.readPlanningPackState)(workspace, { planId });
|
|
447
449
|
if (!(0, execution_controls_1.hasQueuedNextStep)(packState.currentPosition.nextRecommended)) {
|
|
448
|
-
await
|
|
449
|
-
role: "system",
|
|
450
|
-
content: (0, execution_controls_1.formatNoQueuedNextStepMessage)("studio")
|
|
451
|
-
});
|
|
452
|
-
renderTranscript();
|
|
453
|
-
setSidebar();
|
|
454
|
-
setFooter();
|
|
455
|
-
screen.render();
|
|
450
|
+
await appendSystemMessage((0, execution_controls_1.formatNoQueuedNextStepMessage)("studio"));
|
|
456
451
|
return;
|
|
457
452
|
}
|
|
458
453
|
startBusy("run");
|
|
459
454
|
try {
|
|
460
455
|
const prompt = await (0, workspace_1.readText)(paths.nextPrompt);
|
|
461
|
-
const result = await (0, agent_1.runNextPrompt)(workspace, prompt);
|
|
462
|
-
await (0, execution_state_1.saveExecutionState)(workspace, "success", "studio", result);
|
|
463
|
-
await
|
|
464
|
-
|
|
465
|
-
|
|
456
|
+
const result = await (0, agent_1.runNextPrompt)(workspace, prompt, { planId });
|
|
457
|
+
await (0, execution_state_1.saveExecutionState)(workspace, "success", "studio", result, { planId });
|
|
458
|
+
await (0, execution_state_1.appendExecutionLog)(workspace, "success", "studio", result, {
|
|
459
|
+
planId,
|
|
460
|
+
stepLabel: packState.nextStepSummary?.id ?? packState.currentPosition.nextRecommended
|
|
466
461
|
});
|
|
462
|
+
await appendSystemMessage(`Execution run finished. ${(0, agent_1.getPrimaryAgentAdapter)().label} summary:\n${result}`);
|
|
467
463
|
}
|
|
468
464
|
catch (error) {
|
|
469
465
|
const message = error instanceof Error ? error.message : String(error);
|
|
470
|
-
await (0, execution_state_1.saveExecutionState)(workspace, "failure", "studio", message);
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
content: (0, execution_controls_1.formatExecutionFailureMessage)(message, refreshedPackState.nextStepSummary, refreshedPackState.currentPosition.nextRecommended, "studio")
|
|
466
|
+
await (0, execution_state_1.saveExecutionState)(workspace, "failure", "studio", message, { planId });
|
|
467
|
+
await (0, execution_state_1.appendExecutionLog)(workspace, "failure", "studio", message, {
|
|
468
|
+
planId,
|
|
469
|
+
stepLabel: packState.nextStepSummary?.id ?? packState.currentPosition.nextRecommended
|
|
475
470
|
});
|
|
471
|
+
const refreshedPackState = await (0, planning_pack_state_1.readPlanningPackState)(workspace, { planId });
|
|
472
|
+
await appendSystemMessage((0, execution_controls_1.formatExecutionFailureMessage)(message, refreshedPackState.nextStepSummary, refreshedPackState.currentPosition.nextRecommended, "studio"));
|
|
476
473
|
}
|
|
477
474
|
finally {
|
|
478
475
|
stopBusy();
|
|
@@ -480,19 +477,36 @@ async function launchStudio(options = {}) {
|
|
|
480
477
|
}
|
|
481
478
|
return;
|
|
482
479
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
480
|
+
if (command.startsWith("/auto")) {
|
|
481
|
+
const requestedMax = command.slice("/auto".length).trim();
|
|
482
|
+
const maxSteps = requestedMax ? Number(requestedMax) : undefined;
|
|
483
|
+
startBusy("auto");
|
|
484
|
+
try {
|
|
485
|
+
const result = await (0, auto_run_1.executeAutoRun)(workspace, {
|
|
486
|
+
source: "studio",
|
|
487
|
+
planId,
|
|
488
|
+
maxSteps,
|
|
489
|
+
onMessage: async (line) => {
|
|
490
|
+
await appendSystemMessage(line);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
await appendSystemMessage(`Auto mode finished: ${result.summary}`);
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
await appendSystemMessage(`Auto mode failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
497
|
+
}
|
|
498
|
+
finally {
|
|
499
|
+
stopBusy();
|
|
500
|
+
await refreshEnvironment();
|
|
501
|
+
}
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
await appendSystemMessage(`Unknown command: ${command}`);
|
|
491
505
|
}
|
|
492
506
|
input.on("submit", async (value) => {
|
|
493
507
|
const text = value.trim();
|
|
494
508
|
input.clearValue();
|
|
495
|
-
if (!text || busy) {
|
|
509
|
+
if (!text || (busy && text !== "/stop")) {
|
|
496
510
|
screen.render();
|
|
497
511
|
return;
|
|
498
512
|
}
|
|
@@ -511,7 +525,7 @@ async function launchStudio(options = {}) {
|
|
|
511
525
|
setFooter();
|
|
512
526
|
screen.render();
|
|
513
527
|
try {
|
|
514
|
-
const reply = await (0, agent_1.requestPlannerReply)(workspace, messages);
|
|
528
|
+
const reply = await (0, agent_1.requestPlannerReply)(workspace, messages, { planId });
|
|
515
529
|
await appendMessage({
|
|
516
530
|
role: "assistant",
|
|
517
531
|
content: reply
|
|
@@ -533,6 +547,10 @@ async function launchStudio(options = {}) {
|
|
|
533
547
|
}
|
|
534
548
|
});
|
|
535
549
|
screen.key(["C-c"], () => {
|
|
550
|
+
if (busyMode === "auto") {
|
|
551
|
+
void (0, auto_run_1.requestAutoRunStop)(workspace, { planId });
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
536
554
|
stopBusy();
|
|
537
555
|
screen.destroy();
|
|
538
556
|
});
|
|
@@ -555,14 +573,7 @@ async function launchStudio(options = {}) {
|
|
|
555
573
|
if (!latestPackState || !isDefaultStudioSession(messages)) {
|
|
556
574
|
return;
|
|
557
575
|
}
|
|
558
|
-
await
|
|
559
|
-
role: "system",
|
|
560
|
-
content: renderWorkspaceSelectionMessage(workspace, latestPackState)
|
|
561
|
-
});
|
|
562
|
-
renderTranscript();
|
|
563
|
-
setSidebar();
|
|
564
|
-
setFooter();
|
|
565
|
-
screen.render();
|
|
576
|
+
await appendSystemMessage(renderWorkspaceSelectionMessage(workspace, latestPackState));
|
|
566
577
|
}
|
|
567
578
|
}
|
|
568
579
|
function describeBusyMode(mode) {
|
|
@@ -573,6 +584,8 @@ function describeBusyMode(mode) {
|
|
|
573
584
|
return `writing planning pack via ${(0, agent_1.getPrimaryAgentAdapter)().label}`;
|
|
574
585
|
case "run":
|
|
575
586
|
return `executing next-agent prompt via ${(0, agent_1.getPrimaryAgentAdapter)().label}`;
|
|
587
|
+
case "auto":
|
|
588
|
+
return `running auto mode via ${(0, agent_1.getPrimaryAgentAdapter)().label}`;
|
|
576
589
|
}
|
|
577
590
|
}
|
|
578
591
|
function formatElapsed(durationMs) {
|
|
@@ -604,6 +617,22 @@ function formatExecutionSummary(execution) {
|
|
|
604
617
|
const label = execution.status === "success" ? "success" : "failure";
|
|
605
618
|
return `${label} via ${execution.source}\n${execution.updatedAt}\n${execution.summary}`;
|
|
606
619
|
}
|
|
620
|
+
function formatAutoSummary(packState) {
|
|
621
|
+
if (!packState?.autoRun) {
|
|
622
|
+
return "idle";
|
|
623
|
+
}
|
|
624
|
+
const lines = [packState.autoRun.status];
|
|
625
|
+
if (packState.autoRun.maxSteps) {
|
|
626
|
+
lines.push(`steps: ${packState.autoRun.stepsAttempted}/${packState.autoRun.maxSteps}`);
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
lines.push(`steps: ${packState.autoRun.stepsAttempted}`);
|
|
630
|
+
}
|
|
631
|
+
if (packState.autoRun.stopReason) {
|
|
632
|
+
lines.push(packState.autoRun.stopReason);
|
|
633
|
+
}
|
|
634
|
+
return lines.join("\n");
|
|
635
|
+
}
|
|
607
636
|
function formatAgentSummary(activeAgent, statuses) {
|
|
608
637
|
return [`active: ${activeAgent.label}`, ...statuses.map((status) => formatAgentStatusLine(status, status.id === activeAgent.id))]
|
|
609
638
|
.join("\n");
|
|
@@ -614,32 +643,48 @@ function buildStudioHeaderContent(workspace, packState) {
|
|
|
614
643
|
return ` {bold}SRGICAL STUDIO{/bold} ${workspaceName} ${packLabel}`;
|
|
615
644
|
}
|
|
616
645
|
function formatPlanningPackSummary(workspace, packState) {
|
|
646
|
+
const planId = packState.planId ?? "default";
|
|
647
|
+
const readinessScore = packState.readiness?.score ?? 0;
|
|
648
|
+
const readinessTotal = packState.readiness?.total ?? 4;
|
|
649
|
+
const docsPresent = packState.docsPresent ?? (packState.packPresent ? 4 : 0);
|
|
617
650
|
const lines = [
|
|
618
651
|
`state: ${describePlanningPackState(packState)}`,
|
|
619
|
-
`
|
|
652
|
+
`plan: ${planId}`,
|
|
653
|
+
`dir: ${(0, workspace_1.getPlanningPackPaths)(workspace, { planId }).relativeDir}`,
|
|
654
|
+
`docs: ${docsPresent}/4`,
|
|
655
|
+
`readiness: ${readinessScore}/${readinessTotal}`
|
|
620
656
|
];
|
|
621
|
-
|
|
622
|
-
|
|
657
|
+
const mode = deriveDisplayMode(packState);
|
|
658
|
+
const readyToWrite = packState.readiness?.readyToWrite ?? false;
|
|
659
|
+
if (mode === "Gathering Context" || mode === "Ready to Write") {
|
|
660
|
+
lines.push(readyToWrite ? "next: /write will create or refresh the planning doc set" : "next: keep gathering context or run /readiness");
|
|
623
661
|
}
|
|
624
|
-
else if (
|
|
625
|
-
lines.push(
|
|
662
|
+
else if (mode === "Ready to Execute" || mode === "Execution Active" || mode === "Auto Running") {
|
|
663
|
+
lines.push("next: /preview, /run, or /auto when ready");
|
|
664
|
+
}
|
|
665
|
+
else if (!packState.packPresent) {
|
|
666
|
+
lines.push("next: /plan new <id> or /write to create the planning doc set");
|
|
626
667
|
}
|
|
627
668
|
else {
|
|
628
|
-
lines.push("next:
|
|
669
|
+
lines.push("next: add or queue the next execution-ready step");
|
|
629
670
|
}
|
|
630
671
|
return lines.join("\n");
|
|
631
672
|
}
|
|
632
673
|
function renderWorkspaceSelectionMessage(workspace, packState) {
|
|
674
|
+
const planId = packState.planId ?? "default";
|
|
633
675
|
return [
|
|
634
676
|
"Planning view:",
|
|
635
677
|
`- workspace: ${workspace}`,
|
|
636
|
-
`-
|
|
678
|
+
`- active plan: ${planId}`,
|
|
679
|
+
`- planning dir: ${(0, workspace_1.getPlanningPackPaths)(workspace, { planId }).relativeDir}`,
|
|
637
680
|
`- plan status: ${describePlanningPackState(packState)}`,
|
|
681
|
+
`- readiness: ${packState.readiness.score}/${packState.readiness.total}`,
|
|
638
682
|
"",
|
|
639
683
|
"Use `/workspace <path>` to switch repos.",
|
|
640
|
-
|
|
684
|
+
"Use `/plans` to inspect plan directories and `/plan <id>` to switch plans.",
|
|
685
|
+
!packState.packPresent || packState.mode === "Gathering Context" || packState.mode === "Ready to Write"
|
|
641
686
|
? "Use `/write` when you want to put the current plan on disk."
|
|
642
|
-
: "Use `/write` when you want to refresh the plan
|
|
687
|
+
: "Use `/write` when you want to refresh the selected plan from this transcript."
|
|
643
688
|
].join("\n");
|
|
644
689
|
}
|
|
645
690
|
function renderAgentSelectionMessage(activeAgent, statuses) {
|
|
@@ -698,20 +743,111 @@ function isDefaultStudioSession(messages) {
|
|
|
698
743
|
message.content === studio_session_1.DEFAULT_STUDIO_MESSAGES[index]?.content));
|
|
699
744
|
}
|
|
700
745
|
function describePlanningPackState(packState) {
|
|
746
|
+
return `${deriveDisplayMode(packState).toLowerCase()}${packState.hasFailureOverlay ? " (last run failed)" : ""}`;
|
|
747
|
+
}
|
|
748
|
+
function formatPlanningPackPill(packState) {
|
|
749
|
+
const planId = packState.planId ?? "default";
|
|
750
|
+
const mode = deriveDisplayMode(packState);
|
|
751
|
+
if (mode === "Auto Running") {
|
|
752
|
+
return `{#4de2c5-fg}PLAN ${planId.toUpperCase()} | AUTO RUNNING{/}`;
|
|
753
|
+
}
|
|
701
754
|
if (!packState.packPresent) {
|
|
702
|
-
return
|
|
755
|
+
return `{#ffb14a-fg}PLAN ${planId.toUpperCase()} | NO PACK{/}`;
|
|
703
756
|
}
|
|
704
|
-
if (
|
|
705
|
-
return
|
|
757
|
+
if (packState.hasFailureOverlay) {
|
|
758
|
+
return `{#ff7a59-fg}PLAN ${planId.toUpperCase()} | ${mode.toUpperCase()} | FAILED{/}`;
|
|
706
759
|
}
|
|
707
|
-
|
|
760
|
+
if (mode === "Ready to Execute" || mode === "Execution Active") {
|
|
761
|
+
return `{#4de2c5-fg}PLAN ${planId.toUpperCase()} | ${mode.toUpperCase()}{/}`;
|
|
762
|
+
}
|
|
763
|
+
return `{#ffb14a-fg}PLAN ${planId.toUpperCase()} | ${mode.toUpperCase()}{/}`;
|
|
708
764
|
}
|
|
709
|
-
function
|
|
765
|
+
function deriveDisplayMode(packState) {
|
|
766
|
+
if (packState.mode) {
|
|
767
|
+
return packState.mode;
|
|
768
|
+
}
|
|
710
769
|
if (!packState.packPresent) {
|
|
711
|
-
return "
|
|
770
|
+
return "No Pack";
|
|
771
|
+
}
|
|
772
|
+
if (packState.currentPosition?.nextRecommended) {
|
|
773
|
+
return "Execution Active";
|
|
774
|
+
}
|
|
775
|
+
return packState.trackerReadable ? "Plan Written - Needs Step" : "Gathering Context";
|
|
776
|
+
}
|
|
777
|
+
function configureComposer(input) {
|
|
778
|
+
const internals = input;
|
|
779
|
+
const originalListener = internals._listener;
|
|
780
|
+
if (!originalListener) {
|
|
781
|
+
return;
|
|
712
782
|
}
|
|
713
|
-
|
|
714
|
-
|
|
783
|
+
internals._listener = function patchedListener(ch, key) {
|
|
784
|
+
if (key.name === "enter" && !key.shift && !key.meta && !key.ctrl) {
|
|
785
|
+
if (typeof internals._done === "function") {
|
|
786
|
+
internals._done(null, internals.value ?? "");
|
|
787
|
+
}
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if ((key.name === "enter" && (key.shift || key.meta)) || (key.ctrl && key.name === "j")) {
|
|
791
|
+
originalListener.call(this, "\n", { ...key, name: "enter" });
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
originalListener.call(this, ch, key);
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function renderStatusMessage(workspace, packState) {
|
|
798
|
+
return [
|
|
799
|
+
`Plan: ${packState.planId}`,
|
|
800
|
+
`Planning dir: ${(0, workspace_1.getPlanningPackPaths)(workspace, { planId: packState.planId }).relativeDir}`,
|
|
801
|
+
`Mode: ${packState.mode}${packState.hasFailureOverlay ? " [last run failed]" : ""}`,
|
|
802
|
+
`Docs: ${packState.docsPresent}/4`,
|
|
803
|
+
`Readiness: ${packState.readiness.score}/${packState.readiness.total}${packState.readiness.readyToWrite ? " (ready to write)" : ""}`,
|
|
804
|
+
`Execution activated: ${packState.executionActivated ? "yes" : "no"}`,
|
|
805
|
+
`Auto mode: ${packState.autoRun?.status ?? "idle"}`,
|
|
806
|
+
`Next step: ${packState.nextStepSummary?.id ?? packState.currentPosition.nextRecommended ?? "none queued"}`
|
|
807
|
+
].join("\n");
|
|
808
|
+
}
|
|
809
|
+
function renderReadinessMessage(packState) {
|
|
810
|
+
return [
|
|
811
|
+
`Readiness for plan \`${packState.planId}\`: ${packState.readiness.score}/${packState.readiness.total}${packState.readiness.readyToWrite ? " (ready to write)" : ""}`,
|
|
812
|
+
"",
|
|
813
|
+
...packState.readiness.checks.map((check) => `- ${check.passed ? "[x]" : "[ ]"} ${check.label}`),
|
|
814
|
+
"",
|
|
815
|
+
packState.readiness.missingLabels.length > 0
|
|
816
|
+
? `Missing: ${packState.readiness.missingLabels.join(", ")}`
|
|
817
|
+
: "Missing: none",
|
|
818
|
+
packState.readiness.readyToWrite
|
|
819
|
+
? "Next: run `/write` to create or refresh the planning doc set."
|
|
820
|
+
: "Next: keep gathering repo truth, constraints, and the first execution slice."
|
|
821
|
+
].join("\n");
|
|
822
|
+
}
|
|
823
|
+
function renderPlanUsageMessage(currentPlanId, paths) {
|
|
824
|
+
return [
|
|
825
|
+
`Current plan: ${currentPlanId}`,
|
|
826
|
+
`Planning dir: ${paths.relativeDir}`,
|
|
827
|
+
"",
|
|
828
|
+
"Commands:",
|
|
829
|
+
"- `/plans` lists available plans",
|
|
830
|
+
"- `/plan <id>` switches the active plan",
|
|
831
|
+
"- `/plan new <id>` creates a named plan scaffold"
|
|
832
|
+
].join("\n");
|
|
833
|
+
}
|
|
834
|
+
async function renderPlansMessage(workspace, currentPlanId) {
|
|
835
|
+
const refs = await (0, workspace_1.listPlanningDirectories)(workspace);
|
|
836
|
+
if (refs.length === 0) {
|
|
837
|
+
return "No planning packs exist yet.\nUse `/plan new <id>` or `/write` to create one.";
|
|
715
838
|
}
|
|
716
|
-
|
|
839
|
+
const states = await Promise.all(refs.map((ref) => (0, planning_pack_state_1.readPlanningPackState)(workspace, { planId: ref.planId })));
|
|
840
|
+
return [
|
|
841
|
+
"Plans:",
|
|
842
|
+
...states.map((state) => `- ${state.planId}${state.planId === currentPlanId ? " [active]" : ""}: ${state.packDir} | ${state.mode} | docs ${state.docsPresent}/4 | readiness ${state.readiness.score}/${state.readiness.total} | auto ${state.autoRun?.status ?? "idle"}`)
|
|
843
|
+
].join("\n");
|
|
844
|
+
}
|
|
845
|
+
async function createPlanScaffold(workspace, requestedPlanId) {
|
|
846
|
+
const planId = (0, workspace_1.normalizePlanId)(requestedPlanId);
|
|
847
|
+
const paths = await (0, workspace_1.ensurePlanningDir)(workspace, { planId });
|
|
848
|
+
const templates = (0, templates_1.getInitialTemplates)(paths);
|
|
849
|
+
await Promise.all(Object.entries(templates).map(([filePath, content]) => (0, workspace_1.writeText)(filePath, content)));
|
|
850
|
+
await (0, planning_state_1.savePlanningState)(workspace, "scaffolded", { planId });
|
|
851
|
+
await (0, workspace_1.saveActivePlanId)(workspace, planId);
|
|
852
|
+
return paths;
|
|
717
853
|
}
|