@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.
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeAutoRun = executeAutoRun;
4
+ exports.requestAutoRunStop = requestAutoRunStop;
5
+ const execution_state_1 = require("./execution-state");
6
+ const execution_controls_1 = require("./execution-controls");
7
+ const planning_pack_state_1 = require("./planning-pack-state");
8
+ const auto_run_state_1 = require("./auto-run-state");
9
+ const workspace_1 = require("./workspace");
10
+ const agent_1 = require("./agent");
11
+ const DEFAULT_AUTO_MAX_STEPS = 10;
12
+ async function executeAutoRun(workspaceRoot, options) {
13
+ const planOptions = { planId: options.planId };
14
+ const initialState = await (0, planning_pack_state_1.readPlanningPackState)(workspaceRoot, planOptions);
15
+ assertAutoRunnable(initialState);
16
+ const maxSteps = sanitizeMaxSteps(options.maxSteps);
17
+ let autoRunState = await (0, auto_run_state_1.updateAutoRunState)(workspaceRoot, {
18
+ status: "running",
19
+ startedAt: new Date().toISOString(),
20
+ endedAt: null,
21
+ source: options.source,
22
+ maxSteps,
23
+ stepsAttempted: 0,
24
+ lastStartedStepId: initialState.currentPosition.nextRecommended,
25
+ lastObservedNextStepId: initialState.currentPosition.nextRecommended,
26
+ stopReason: null
27
+ }, planOptions);
28
+ await emit(options, `Auto mode started for plan \`${initialState.planId}\` with max ${maxSteps} step${maxSteps === 1 ? "" : "s"}.`);
29
+ for (let iteration = 0; iteration < maxSteps; iteration += 1) {
30
+ const requestedStopState = await (0, auto_run_state_1.loadAutoRunState)(workspaceRoot, planOptions);
31
+ if (requestedStopState?.status === "stop_requested") {
32
+ autoRunState = await finalizeAutoRun(workspaceRoot, "stopped", "Stop requested by user.", options, {
33
+ stepsAttempted: iteration
34
+ });
35
+ return {
36
+ finalState: autoRunState,
37
+ summary: autoRunState.stopReason ?? "Auto run stopped."
38
+ };
39
+ }
40
+ const beforeState = await (0, planning_pack_state_1.readPlanningPackState)(workspaceRoot, planOptions);
41
+ const naturalStop = describeNaturalStop(beforeState, iteration, maxSteps);
42
+ if (naturalStop) {
43
+ autoRunState = await finalizeAutoRun(workspaceRoot, naturalStop.status, naturalStop.reason, options, {
44
+ stepsAttempted: iteration,
45
+ lastObservedNextStepId: beforeState.currentPosition.nextRecommended
46
+ });
47
+ return {
48
+ finalState: autoRunState,
49
+ summary: naturalStop.reason
50
+ };
51
+ }
52
+ const stepLabel = (0, execution_controls_1.formatStepLabel)(beforeState.nextStepSummary, beforeState.currentPosition.nextRecommended) ?? "unknown step";
53
+ await emit(options, `Auto iteration ${iteration + 1}/${maxSteps}: ${stepLabel}`);
54
+ autoRunState = await (0, auto_run_state_1.updateAutoRunState)(workspaceRoot, {
55
+ status: "running",
56
+ source: options.source,
57
+ maxSteps,
58
+ stepsAttempted: iteration + 1,
59
+ lastStartedStepId: beforeState.currentPosition.nextRecommended,
60
+ lastObservedNextStepId: beforeState.currentPosition.nextRecommended,
61
+ stopReason: null
62
+ }, planOptions);
63
+ const iterationResult = await executeIteration(workspaceRoot, beforeState, options);
64
+ if (!iterationResult.success) {
65
+ autoRunState = await finalizeAutoRun(workspaceRoot, "failed", iterationResult.summary, options, {
66
+ stepsAttempted: iteration + 1,
67
+ lastStartedStepId: beforeState.currentPosition.nextRecommended,
68
+ lastObservedNextStepId: iterationResult.afterState.currentPosition.nextRecommended
69
+ });
70
+ return {
71
+ finalState: autoRunState,
72
+ summary: iterationResult.summary
73
+ };
74
+ }
75
+ await emit(options, iterationResult.summary);
76
+ }
77
+ autoRunState = await finalizeAutoRun(workspaceRoot, "stopped", `Reached the auto-run step cap (${maxSteps}).`, options, { stepsAttempted: maxSteps });
78
+ return {
79
+ finalState: autoRunState,
80
+ summary: autoRunState.stopReason ?? `Reached the auto-run step cap (${maxSteps}).`
81
+ };
82
+ }
83
+ async function requestAutoRunStop(workspaceRoot, options = {}) {
84
+ const current = await (0, auto_run_state_1.loadAutoRunState)(workspaceRoot, options);
85
+ const status = current?.status === "running" ? "stop_requested" : "stopped";
86
+ return (0, auto_run_state_1.updateAutoRunState)(workspaceRoot, {
87
+ status,
88
+ stopReason: status === "stop_requested" ? "Stop requested by user." : "No active auto run was in progress."
89
+ }, options);
90
+ }
91
+ function sanitizeMaxSteps(value) {
92
+ if (!value || !Number.isFinite(value) || value < 1) {
93
+ return DEFAULT_AUTO_MAX_STEPS;
94
+ }
95
+ return Math.floor(value);
96
+ }
97
+ function assertAutoRunnable(state) {
98
+ if (!state.packPresent) {
99
+ throw new Error("Auto mode requires an existing planning pack.");
100
+ }
101
+ if (state.packMode !== "authored") {
102
+ throw new Error("Auto mode only runs authored plans. Finish planning and `/write` the pack first.");
103
+ }
104
+ if (!(0, planning_pack_state_1.isExecutionReadyState)(state)) {
105
+ throw new Error("Auto mode requires a queued execution step in the selected plan.");
106
+ }
107
+ }
108
+ function describeNaturalStop(state, iteration, maxSteps) {
109
+ if (!state.currentPosition.nextRecommended) {
110
+ return {
111
+ status: "completed",
112
+ reason: "Auto mode completed because no next recommended step remains."
113
+ };
114
+ }
115
+ if (!state.nextStepSummary) {
116
+ return {
117
+ status: "failed",
118
+ reason: `Auto mode stopped because the tracker could not summarize \`${state.currentPosition.nextRecommended}\`.`
119
+ };
120
+ }
121
+ if (state.nextStepSummary.status.toLowerCase() === "blocked") {
122
+ return {
123
+ status: "failed",
124
+ reason: `Auto mode stopped because ${state.nextStepSummary.id} is blocked.`
125
+ };
126
+ }
127
+ if (!(0, planning_pack_state_1.isExecutionReadyState)(state)) {
128
+ return {
129
+ status: "stopped",
130
+ reason: `Auto mode stopped because ${state.nextStepSummary.id} is outside execution scope.`
131
+ };
132
+ }
133
+ if (iteration >= maxSteps) {
134
+ return {
135
+ status: "stopped",
136
+ reason: `Reached the auto-run step cap (${maxSteps}).`
137
+ };
138
+ }
139
+ return null;
140
+ }
141
+ async function executeIteration(workspaceRoot, beforeState, options) {
142
+ const planOptions = { planId: options.planId };
143
+ const paths = (0, workspace_1.getPlanningPackPaths)(workspaceRoot, planOptions);
144
+ const prompt = await (0, workspace_1.readText)(paths.nextPrompt);
145
+ const targetStepId = beforeState.currentPosition.nextRecommended;
146
+ const stepLabel = (0, execution_controls_1.formatStepLabel)(beforeState.nextStepSummary, beforeState.currentPosition.nextRecommended);
147
+ let firstAttempt;
148
+ try {
149
+ const result = await (0, agent_1.runNextPrompt)(workspaceRoot, prompt, {
150
+ agentId: options.agentId,
151
+ planId: beforeState.planId
152
+ });
153
+ firstAttempt = { ok: true, message: result };
154
+ }
155
+ catch (error) {
156
+ firstAttempt = { ok: false, message: error instanceof Error ? error.message : String(error) };
157
+ }
158
+ const firstAfterState = await (0, planning_pack_state_1.readPlanningPackState)(workspaceRoot, planOptions);
159
+ const firstReconciled = hasTrackerAdvanced(beforeState, firstAfterState, targetStepId);
160
+ if (firstReconciled) {
161
+ const summary = firstAttempt.ok
162
+ ? firstAttempt.message
163
+ : `Reconciled success after an execution error because the tracker advanced for ${targetStepId ?? "the active step"}.`;
164
+ await (0, execution_state_1.saveExecutionState)(workspaceRoot, "success", options.source, summary, planOptions);
165
+ await (0, execution_state_1.appendExecutionLog)(workspaceRoot, "success", options.source, summary, {
166
+ planId: beforeState.planId,
167
+ stepLabel
168
+ });
169
+ return {
170
+ success: true,
171
+ summary: `Completed ${stepLabel ?? targetStepId ?? "the current step"} and advanced the tracker.`,
172
+ afterState: firstAfterState
173
+ };
174
+ }
175
+ await emit(options, `No tracker advancement detected for ${stepLabel ?? targetStepId ?? "the current step"}; retrying once.`);
176
+ try {
177
+ const retryResult = await (0, agent_1.runNextPrompt)(workspaceRoot, prompt, {
178
+ agentId: options.agentId,
179
+ planId: beforeState.planId
180
+ });
181
+ const retryAfterState = await (0, planning_pack_state_1.readPlanningPackState)(workspaceRoot, planOptions);
182
+ if (hasTrackerAdvanced(beforeState, retryAfterState, targetStepId)) {
183
+ await (0, execution_state_1.saveExecutionState)(workspaceRoot, "success", options.source, retryResult, planOptions);
184
+ await (0, execution_state_1.appendExecutionLog)(workspaceRoot, "success", options.source, retryResult, {
185
+ planId: beforeState.planId,
186
+ stepLabel
187
+ });
188
+ return {
189
+ success: true,
190
+ summary: `Completed ${stepLabel ?? targetStepId ?? "the current step"} after one reconciliation retry.`,
191
+ afterState: retryAfterState
192
+ };
193
+ }
194
+ const failureSummary = `Auto mode failed because ${targetStepId ?? "the current step"} did not advance after one retry.`;
195
+ await (0, execution_state_1.saveExecutionState)(workspaceRoot, "failure", options.source, failureSummary, planOptions);
196
+ await (0, execution_state_1.appendExecutionLog)(workspaceRoot, "failure", options.source, failureSummary, {
197
+ planId: beforeState.planId,
198
+ stepLabel
199
+ });
200
+ return {
201
+ success: false,
202
+ summary: failureSummary,
203
+ afterState: retryAfterState
204
+ };
205
+ }
206
+ catch (error) {
207
+ const retryAfterState = await (0, planning_pack_state_1.readPlanningPackState)(workspaceRoot, planOptions);
208
+ if (hasTrackerAdvanced(beforeState, retryAfterState, targetStepId)) {
209
+ const summary = `Reconciled success after a retry failure because the tracker advanced for ${targetStepId ?? "the active step"}.`;
210
+ await (0, execution_state_1.saveExecutionState)(workspaceRoot, "success", options.source, summary, planOptions);
211
+ await (0, execution_state_1.appendExecutionLog)(workspaceRoot, "success", options.source, summary, {
212
+ planId: beforeState.planId,
213
+ stepLabel
214
+ });
215
+ return {
216
+ success: true,
217
+ summary: `Completed ${stepLabel ?? targetStepId ?? "the current step"} after reconciliation.`,
218
+ afterState: retryAfterState
219
+ };
220
+ }
221
+ const failureSummary = `Auto mode failed because ${targetStepId ?? "the current step"} did not advance after one retry. Reason: ${error instanceof Error ? error.message : String(error)}`;
222
+ await (0, execution_state_1.saveExecutionState)(workspaceRoot, "failure", options.source, failureSummary, planOptions);
223
+ await (0, execution_state_1.appendExecutionLog)(workspaceRoot, "failure", options.source, failureSummary, {
224
+ planId: beforeState.planId,
225
+ stepLabel
226
+ });
227
+ return {
228
+ success: false,
229
+ summary: failureSummary,
230
+ afterState: retryAfterState
231
+ };
232
+ }
233
+ }
234
+ function hasTrackerAdvanced(beforeState, afterState, targetStepId) {
235
+ if (!targetStepId) {
236
+ return false;
237
+ }
238
+ if (afterState.currentPosition.nextRecommended && afterState.currentPosition.nextRecommended !== targetStepId) {
239
+ return true;
240
+ }
241
+ if (afterState.currentPosition.lastCompleted === targetStepId) {
242
+ return true;
243
+ }
244
+ if (afterState.nextStepSummary && afterState.nextStepSummary.id === targetStepId) {
245
+ const status = afterState.nextStepSummary.status.toLowerCase();
246
+ if (status === "done" || status === "skipped") {
247
+ return true;
248
+ }
249
+ }
250
+ return false;
251
+ }
252
+ async function finalizeAutoRun(workspaceRoot, status, reason, options, updates = {}) {
253
+ const nextState = await (0, auto_run_state_1.updateAutoRunState)(workspaceRoot, {
254
+ ...updates,
255
+ status,
256
+ endedAt: new Date().toISOString(),
257
+ stopReason: reason
258
+ }, { planId: options.planId });
259
+ await (0, execution_state_1.appendExecutionLog)(workspaceRoot, status === "failed" ? "failure" : "success", options.source, reason, {
260
+ planId: nextState.planId,
261
+ stepLabel: "auto-run summary"
262
+ });
263
+ await emit(options, reason);
264
+ return nextState;
265
+ }
266
+ async function emit(options, line) {
267
+ if (options.onMessage) {
268
+ await options.onMessage(line);
269
+ }
270
+ }
@@ -40,7 +40,7 @@ async function detectClaude() {
40
40
  };
41
41
  }
42
42
  }
43
- async function requestPlannerReply(workspaceRoot, messages) {
43
+ async function requestPlannerReply(workspaceRoot, messages, _options = {}) {
44
44
  const result = await runClaudeExec({
45
45
  cwd: workspaceRoot,
46
46
  prompt: (0, prompts_1.buildPlannerPrompt)(messages, workspaceRoot),
@@ -49,16 +49,16 @@ async function requestPlannerReply(workspaceRoot, messages) {
49
49
  });
50
50
  return result.lastMessage.trim();
51
51
  }
52
- async function writePlanningPack(workspaceRoot, messages) {
53
- const planningEpoch = await (0, planning_epochs_1.preparePlanningPackForWrite)(workspaceRoot);
52
+ async function writePlanningPack(workspaceRoot, messages, options = {}) {
53
+ const planningEpoch = await (0, planning_epochs_1.preparePlanningPackForWrite)(workspaceRoot, options);
54
54
  const claudeStatus = await detectClaude();
55
55
  if (!claudeStatus.available) {
56
- return appendPlanningEpochSummary(planningEpoch, await (0, local_pack_1.writePlanningPackFallback)(workspaceRoot, messages, claudeStatus.error ?? "Claude Code CLI is unavailable", "Claude Code"));
56
+ return appendPlanningEpochSummary(planningEpoch, await (0, local_pack_1.writePlanningPackFallback)(workspaceRoot, messages, claudeStatus.error ?? "Claude Code CLI is unavailable", "Claude Code", options));
57
57
  }
58
58
  try {
59
59
  const result = await runClaudeExec({
60
60
  cwd: workspaceRoot,
61
- prompt: await (0, prompts_1.buildPackWriterPrompt)(messages, workspaceRoot),
61
+ prompt: await (0, prompts_1.buildPackWriterPrompt)(messages, workspaceRoot, options),
62
62
  permissionMode: "acceptEdits",
63
63
  allowedTools: CLAUDE_WRITE_ALLOW_TOOLS,
64
64
  maxTurns: 24
@@ -68,7 +68,7 @@ async function writePlanningPack(workspaceRoot, messages) {
68
68
  catch (error) {
69
69
  if (isClaudeUnavailableError(error)) {
70
70
  const message = error instanceof Error ? error.message : "Claude Code CLI is unavailable";
71
- return appendPlanningEpochSummary(planningEpoch, await (0, local_pack_1.writePlanningPackFallback)(workspaceRoot, messages, message, "Claude Code"));
71
+ return appendPlanningEpochSummary(planningEpoch, await (0, local_pack_1.writePlanningPackFallback)(workspaceRoot, messages, message, "Claude Code", options));
72
72
  }
73
73
  const message = error instanceof Error ? error.message : String(error);
74
74
  const epochSummary = (0, planning_epochs_1.formatPlanningEpochSummary)(planningEpoch);
@@ -78,7 +78,7 @@ async function writePlanningPack(workspaceRoot, messages) {
78
78
  throw error;
79
79
  }
80
80
  }
81
- async function runNextPrompt(workspaceRoot, prompt) {
81
+ async function runNextPrompt(workspaceRoot, prompt, _options = {}) {
82
82
  const result = await runClaudeExec({
83
83
  cwd: workspaceRoot,
84
84
  prompt,
@@ -37,7 +37,7 @@ async function detectCodex() {
37
37
  };
38
38
  }
39
39
  }
40
- async function requestPlannerReply(workspaceRoot, messages) {
40
+ async function requestPlannerReply(workspaceRoot, messages, _options = {}) {
41
41
  const result = await runCodexExec({
42
42
  cwd: workspaceRoot,
43
43
  prompt: (0, prompts_1.buildPlannerPrompt)(messages, workspaceRoot),
@@ -47,16 +47,16 @@ async function requestPlannerReply(workspaceRoot, messages) {
47
47
  });
48
48
  return result.lastMessage.trim();
49
49
  }
50
- async function writePlanningPack(workspaceRoot, messages) {
51
- const planningEpoch = await (0, planning_epochs_1.preparePlanningPackForWrite)(workspaceRoot);
50
+ async function writePlanningPack(workspaceRoot, messages, options = {}) {
51
+ const planningEpoch = await (0, planning_epochs_1.preparePlanningPackForWrite)(workspaceRoot, options);
52
52
  const codexStatus = await detectCodex();
53
53
  if (!codexStatus.available) {
54
- return appendPlanningEpochSummary(planningEpoch, await (0, local_pack_1.writePlanningPackFallback)(workspaceRoot, messages, codexStatus.error ?? "Codex is unavailable", "Codex"));
54
+ return appendPlanningEpochSummary(planningEpoch, await (0, local_pack_1.writePlanningPackFallback)(workspaceRoot, messages, codexStatus.error ?? "Codex is unavailable", "Codex", options));
55
55
  }
56
56
  try {
57
57
  const result = await runCodexExec({
58
58
  cwd: workspaceRoot,
59
- prompt: await (0, prompts_1.buildPackWriterPrompt)(messages, workspaceRoot),
59
+ prompt: await (0, prompts_1.buildPackWriterPrompt)(messages, workspaceRoot, options),
60
60
  allowWrite: true,
61
61
  skipGitRepoCheck: true,
62
62
  ephemeral: false
@@ -66,7 +66,7 @@ async function writePlanningPack(workspaceRoot, messages) {
66
66
  catch (error) {
67
67
  if (isCodexUnavailableError(error)) {
68
68
  const message = error instanceof Error ? error.message : "Codex is unavailable";
69
- return appendPlanningEpochSummary(planningEpoch, await (0, local_pack_1.writePlanningPackFallback)(workspaceRoot, messages, message, "Codex"));
69
+ return appendPlanningEpochSummary(planningEpoch, await (0, local_pack_1.writePlanningPackFallback)(workspaceRoot, messages, message, "Codex", options));
70
70
  }
71
71
  const message = error instanceof Error ? error.message : String(error);
72
72
  const epochSummary = (0, planning_epochs_1.formatPlanningEpochSummary)(planningEpoch);
@@ -76,7 +76,7 @@ async function writePlanningPack(workspaceRoot, messages) {
76
76
  throw error;
77
77
  }
78
78
  }
79
- async function runNextPrompt(workspaceRoot, prompt) {
79
+ async function runNextPrompt(workspaceRoot, prompt, _options = {}) {
80
80
  const result = await runCodexExec({
81
81
  cwd: workspaceRoot,
82
82
  prompt,
@@ -5,8 +5,8 @@ exports.saveExecutionState = saveExecutionState;
5
5
  exports.appendExecutionLog = appendExecutionLog;
6
6
  const promises_1 = require("node:fs/promises");
7
7
  const workspace_1 = require("./workspace");
8
- async function loadExecutionState(workspaceRoot) {
9
- const paths = (0, workspace_1.getPlanningPackPaths)(workspaceRoot);
8
+ async function loadExecutionState(workspaceRoot, options = {}) {
9
+ const paths = (0, workspace_1.getPlanningPackPaths)(workspaceRoot, options);
10
10
  const exists = await (0, workspace_1.fileExists)(paths.executionState);
11
11
  if (!exists) {
12
12
  return null;
@@ -33,8 +33,8 @@ async function loadExecutionState(workspaceRoot) {
33
33
  return null;
34
34
  }
35
35
  }
36
- async function saveExecutionState(workspaceRoot, status, source, summary) {
37
- const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot);
36
+ async function saveExecutionState(workspaceRoot, status, source, summary, options = {}) {
37
+ const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot, options);
38
38
  const payload = {
39
39
  version: 1,
40
40
  updatedAt: new Date().toISOString(),
@@ -45,7 +45,7 @@ async function saveExecutionState(workspaceRoot, status, source, summary) {
45
45
  await (0, workspace_1.writeText)(paths.executionState, JSON.stringify(payload, null, 2));
46
46
  }
47
47
  async function appendExecutionLog(workspaceRoot, status, source, summary, options = {}) {
48
- const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot);
48
+ const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot, options);
49
49
  const entry = buildExecutionLogEntry(status, source, summary, options);
50
50
  const exists = await (0, workspace_1.fileExists)(paths.executionLog);
51
51
  if (!exists) {
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.writePlanningPackFallback = writePlanningPackFallback;
4
+ const planning_state_1 = require("./planning-state");
4
5
  const templates_1 = require("./templates");
5
6
  const workspace_1 = require("./workspace");
6
- async function writePlanningPackFallback(workspaceRoot, messages, reason, agentLabel = "Codex") {
7
- const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot);
7
+ async function writePlanningPackFallback(workspaceRoot, messages, reason, agentLabel = "Codex", options = {}) {
8
+ const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot, options);
8
9
  const templates = (0, templates_1.getInitialTemplates)(paths);
9
10
  const createdFiles = [];
10
11
  const preservedFiles = [];
@@ -30,6 +31,7 @@ async function writePlanningPackFallback(workspaceRoot, messages, reason, agentL
30
31
  ? `Tracker remains on next recommended step: ${currentPosition.nextRecommended}`
31
32
  : "Tracker next step is not yet available."
32
33
  ];
34
+ await (0, planning_state_1.ensurePlanningPackState)(workspaceRoot, "scaffolded", options);
33
35
  return summary.join("\n");
34
36
  }
35
37
  function buildFallbackEntry(messages, reason, createdFiles, preservedFiles, currentPosition, agentLabel) {
@@ -106,17 +108,18 @@ function appendFallbackEntry(context, entry) {
106
108
  return `${context.trimEnd()}\n\n## Handoff Log\n\n${entry}\n`;
107
109
  }
108
110
  function toPackLabel(paths, filePath) {
111
+ const prefix = `${paths.relativeDir}/`;
109
112
  if (filePath === paths.plan) {
110
- return ".srgical/01-product-plan.md";
113
+ return `${prefix}01-product-plan.md`;
111
114
  }
112
115
  if (filePath === paths.context) {
113
- return ".srgical/02-agent-context-kickoff.md";
116
+ return `${prefix}02-agent-context-kickoff.md`;
114
117
  }
115
118
  if (filePath === paths.tracker) {
116
- return ".srgical/03-detailed-implementation-plan.md";
119
+ return `${prefix}03-detailed-implementation-plan.md`;
117
120
  }
118
121
  if (filePath === paths.nextPrompt) {
119
- return ".srgical/04-next-agent-prompt.md";
122
+ return `${prefix}04-next-agent-prompt.md`;
120
123
  }
121
124
  return filePath;
122
125
  }
@@ -10,6 +10,7 @@ exports.listArchivedPackFileNames = listArchivedPackFileNames;
10
10
  const promises_1 = require("node:fs/promises");
11
11
  const node_path_1 = __importDefault(require("node:path"));
12
12
  const planning_pack_state_1 = require("./planning-pack-state");
13
+ const planning_state_1 = require("./planning-state");
13
14
  const templates_1 = require("./templates");
14
15
  const workspace_1 = require("./workspace");
15
16
  const ARCHIVED_PACK_FILE_NAMES = [
@@ -18,11 +19,13 @@ const ARCHIVED_PACK_FILE_NAMES = [
18
19
  "03-detailed-implementation-plan.md",
19
20
  "04-next-agent-prompt.md",
20
21
  "studio-session.json",
22
+ "planning-state.json",
23
+ "auto-run-state.json",
21
24
  "execution-state.json",
22
25
  "execution-log.md"
23
26
  ];
24
- async function preparePlanningPackForWrite(workspaceRoot) {
25
- const packState = await (0, planning_pack_state_1.readPlanningPackState)(workspaceRoot);
27
+ async function preparePlanningPackForWrite(workspaceRoot, options = {}) {
28
+ const packState = await (0, planning_pack_state_1.readPlanningPackState)(workspaceRoot, options);
26
29
  if (!packState.packPresent || packState.currentPosition.nextRecommended) {
27
30
  return {
28
31
  archived: false,
@@ -30,7 +33,7 @@ async function preparePlanningPackForWrite(workspaceRoot) {
30
33
  archivedFiles: []
31
34
  };
32
35
  }
33
- const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot);
36
+ const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot, options);
34
37
  const archiveDirName = await nextPlanningEpochName(paths.dir);
35
38
  const archiveDir = node_path_1.default.join(paths.dir, archiveDirName);
36
39
  await (0, promises_1.mkdir)(archiveDir, { recursive: false });
@@ -69,7 +72,9 @@ async function archiveActivePlanningFiles(paths, archiveDir) {
69
72
  async function resetActivePlanningPack(paths) {
70
73
  const templates = (0, templates_1.getInitialTemplates)(paths);
71
74
  await Promise.all(Object.entries(templates).map(([filePath, content]) => (0, workspace_1.writeText)(filePath, content)));
75
+ await (0, planning_state_1.savePlanningState)(paths.root, "scaffolded", { planId: paths.planId });
72
76
  await Promise.all([
77
+ (0, promises_1.rm)(paths.autoRunState, { force: true }),
73
78
  (0, promises_1.rm)(paths.executionState, { force: true }),
74
79
  (0, promises_1.rm)(paths.executionLog, { force: true })
75
80
  ]);
@@ -81,6 +86,8 @@ function listArchivablePaths(paths) {
81
86
  paths.tracker,
82
87
  paths.nextPrompt,
83
88
  paths.studioSession,
89
+ paths.planningState,
90
+ paths.autoRunState,
84
91
  paths.executionState,
85
92
  paths.executionLog
86
93
  ];
@@ -1,17 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.readPlanningPackState = readPlanningPackState;
4
+ exports.isExecutionStepSummary = isExecutionStepSummary;
5
+ exports.isExecutionReadyState = isExecutionReadyState;
6
+ const auto_run_state_1 = require("./auto-run-state");
4
7
  const execution_state_1 = require("./execution-state");
8
+ const planning_state_1 = require("./planning-state");
9
+ const studio_session_1 = require("./studio-session");
5
10
  const workspace_1 = require("./workspace");
6
- const workspace_2 = require("./workspace");
7
- async function readPlanningPackState(workspaceRoot) {
8
- const packPresent = await (0, workspace_1.planningPackExists)(workspaceRoot);
11
+ async function readPlanningPackState(workspaceRoot, options = {}) {
12
+ const paths = (0, workspace_1.getPlanningPackPaths)(workspaceRoot, options);
13
+ const packPresent = await (0, workspace_1.planningPackExists)(workspaceRoot, options);
9
14
  const currentPosition = emptyCurrentPosition();
10
15
  let nextStepSummary = null;
11
16
  let trackerReadable = false;
12
17
  if (packPresent) {
13
18
  try {
14
- const tracker = await (0, workspace_1.readText)((0, workspace_2.getPlanningPackPaths)(workspaceRoot).tracker);
19
+ const tracker = await (0, workspace_1.readText)(paths.tracker);
15
20
  Object.assign(currentPosition, parseCurrentPosition(tracker));
16
21
  nextStepSummary = parseNextStepSummary(tracker, currentPosition.nextRecommended);
17
22
  trackerReadable = currentPosition.lastCompleted !== null || currentPosition.nextRecommended !== null;
@@ -20,25 +25,132 @@ async function readPlanningPackState(workspaceRoot) {
20
25
  trackerReadable = false;
21
26
  }
22
27
  }
23
- const lastExecution = await (0, execution_state_1.loadExecutionState)(workspaceRoot);
28
+ const [lastExecution, planningState, autoRun, docsPresent, studioSession] = await Promise.all([
29
+ (0, execution_state_1.loadExecutionState)(workspaceRoot, options),
30
+ (0, planning_state_1.loadPlanningState)(workspaceRoot, options),
31
+ (0, auto_run_state_1.loadAutoRunState)(workspaceRoot, options),
32
+ countPresentDocs(paths),
33
+ (0, studio_session_1.loadStudioSessionState)(workspaceRoot, options)
34
+ ]);
35
+ const readiness = buildReadiness(studioSession.messages, nextStepSummary);
36
+ const packMode = planningState?.packMode ?? (0, planning_state_1.inferLegacyPackMode)(currentPosition);
37
+ const executionActivated = Boolean(lastExecution ||
38
+ (autoRun && autoRun.status !== "idle") ||
39
+ (isExecutionStepSummary(nextStepSummary) && currentPosition.lastCompleted !== "PLAN-001"));
40
+ const mode = derivePlanningMode({
41
+ packPresent,
42
+ packMode,
43
+ readiness,
44
+ nextStepSummary,
45
+ autoRun,
46
+ executionActivated
47
+ });
24
48
  return {
49
+ planId: paths.planId,
50
+ packDir: paths.relativeDir,
25
51
  packPresent,
26
52
  trackerReadable,
53
+ docsPresent,
27
54
  currentPosition,
28
55
  nextStepSummary,
29
- lastExecution
56
+ lastExecution,
57
+ planningState,
58
+ packMode,
59
+ readiness,
60
+ autoRun,
61
+ executionActivated,
62
+ mode,
63
+ hasFailureOverlay: lastExecution?.status === "failure"
30
64
  };
31
65
  }
66
+ function isExecutionStepSummary(step) {
67
+ if (!step) {
68
+ return false;
69
+ }
70
+ const phase = step.phase?.toLowerCase() ?? "";
71
+ return phase.includes("delivery") || phase.includes("execution") || /^exec[-\d]/i.test(step.id);
72
+ }
73
+ function isExecutionReadyState(state) {
74
+ return state.packPresent && state.packMode === "authored" && isExecutionStepSummary(state.nextStepSummary);
75
+ }
76
+ function derivePlanningMode(input) {
77
+ if (!input.packPresent) {
78
+ return "No Pack";
79
+ }
80
+ if (input.autoRun?.status === "running" || input.autoRun?.status === "stop_requested") {
81
+ return "Auto Running";
82
+ }
83
+ if (input.packMode === "scaffolded") {
84
+ return input.readiness.readyToWrite ? "Ready to Write" : "Gathering Context";
85
+ }
86
+ if (!isExecutionStepSummary(input.nextStepSummary)) {
87
+ return "Plan Written - Needs Step";
88
+ }
89
+ return input.executionActivated ? "Execution Active" : "Ready to Execute";
90
+ }
91
+ function buildReadiness(messages, nextStepSummary) {
92
+ const meaningfulMessages = messages.filter((message) => message.content.trim().length > 0);
93
+ const userMessages = meaningfulMessages.filter((message) => message.role === "user");
94
+ const assistantMessages = meaningfulMessages.filter((message) => message.role === "assistant" &&
95
+ !studio_session_1.DEFAULT_STUDIO_MESSAGES.some((defaultMessage) => defaultMessage.content === message.content));
96
+ const transcript = meaningfulMessages.map((message) => message.content.toLowerCase()).join("\n");
97
+ const checks = [
98
+ {
99
+ id: "goal",
100
+ label: "Goal captured",
101
+ passed: userMessages.length > 0
102
+ },
103
+ {
104
+ id: "repo",
105
+ label: "Repo context captured",
106
+ passed: /repo|current|existing|already|codebase|today|currently|workspace/.test(transcript)
107
+ },
108
+ {
109
+ id: "constraints",
110
+ label: "Constraints or decisions captured",
111
+ passed: /constraint|must|should|can't|cannot|need|require|locked|decision|prefer|non-negotiable/.test(transcript)
112
+ },
113
+ {
114
+ id: "execution",
115
+ label: "First executable slice captured",
116
+ passed: isExecutionStepSummary(nextStepSummary) || /step|slice|execute|execution|implement|delivery|tracker/.test(transcript)
117
+ }
118
+ ];
119
+ const score = checks.filter((check) => check.passed).length;
120
+ const readyToWrite = score >= 3 && userMessages.length > 0 && assistantMessages.length > 0;
121
+ return {
122
+ checks,
123
+ score,
124
+ total: checks.length,
125
+ readyToWrite,
126
+ missingLabels: checks.filter((check) => !check.passed).map((check) => check.label)
127
+ };
128
+ }
129
+ async function countPresentDocs(paths) {
130
+ const checks = await Promise.all([
131
+ (0, workspace_1.fileExists)(paths.plan),
132
+ (0, workspace_1.fileExists)(paths.context),
133
+ (0, workspace_1.fileExists)(paths.tracker),
134
+ (0, workspace_1.fileExists)(paths.nextPrompt)
135
+ ]);
136
+ return checks.filter(Boolean).length;
137
+ }
32
138
  function parseCurrentPosition(tracker) {
33
139
  return {
34
140
  lastCompleted: readCurrentPositionValue(tracker, "Last Completed"),
35
- nextRecommended: readCurrentPositionValue(tracker, "Next Recommended"),
141
+ nextRecommended: normalizeStepReference(readCurrentPositionValue(tracker, "Next Recommended")),
36
142
  updatedAt: readCurrentPositionValue(tracker, "Updated At")
37
143
  };
38
144
  }
39
145
  function readCurrentPositionValue(tracker, label) {
40
- const match = tracker.match(new RegExp(`- ${escapeRegExp(label)}: \`([^\`]+)\``));
41
- return match?.[1] ?? null;
146
+ const match = tracker.match(new RegExp(`- ${escapeRegExp(label)}: (?:\\\`([^\\\`]+)\\\`|([^\\n]+))`));
147
+ return match?.[1]?.trim() ?? match?.[2]?.trim() ?? null;
148
+ }
149
+ function normalizeStepReference(value) {
150
+ if (!value) {
151
+ return null;
152
+ }
153
+ return value.toLowerCase() === "none queued" ? null : value;
42
154
  }
43
155
  function escapeRegExp(value) {
44
156
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");