@launch11/srgical 0.0.4 → 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.
@@ -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, "\\$&");
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadPlanningState = loadPlanningState;
4
+ exports.savePlanningState = savePlanningState;
5
+ exports.markPlanningPackAuthored = markPlanningPackAuthored;
6
+ exports.ensurePlanningPackState = ensurePlanningPackState;
7
+ exports.inferLegacyPackMode = inferLegacyPackMode;
8
+ const workspace_1 = require("./workspace");
9
+ async function loadPlanningState(workspaceRoot, options = {}) {
10
+ const paths = (0, workspace_1.getPlanningPackPaths)(workspaceRoot, options);
11
+ if (!(await (0, workspace_1.fileExists)(paths.planningState))) {
12
+ return null;
13
+ }
14
+ try {
15
+ const parsed = JSON.parse(await (0, workspace_1.readText)(paths.planningState));
16
+ if (parsed.version !== 1 ||
17
+ typeof parsed.planId !== "string" ||
18
+ typeof parsed.createdAt !== "string" ||
19
+ typeof parsed.updatedAt !== "string" ||
20
+ (parsed.packMode !== "scaffolded" && parsed.packMode !== "authored")) {
21
+ return null;
22
+ }
23
+ return {
24
+ version: 1,
25
+ planId: parsed.planId,
26
+ createdAt: parsed.createdAt,
27
+ updatedAt: parsed.updatedAt,
28
+ packMode: parsed.packMode
29
+ };
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ async function savePlanningState(workspaceRoot, packMode, options = {}) {
36
+ const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot, options);
37
+ const existing = await loadPlanningState(workspaceRoot, options);
38
+ const now = new Date().toISOString();
39
+ const state = {
40
+ version: 1,
41
+ planId: paths.planId,
42
+ createdAt: existing?.createdAt ?? now,
43
+ updatedAt: now,
44
+ packMode
45
+ };
46
+ await (0, workspace_1.writeText)(paths.planningState, JSON.stringify(state, null, 2));
47
+ return state;
48
+ }
49
+ async function markPlanningPackAuthored(workspaceRoot, options = {}) {
50
+ return savePlanningState(workspaceRoot, "authored", options);
51
+ }
52
+ async function ensurePlanningPackState(workspaceRoot, packMode, options = {}) {
53
+ const existing = await loadPlanningState(workspaceRoot, options);
54
+ if (existing) {
55
+ return existing;
56
+ }
57
+ return savePlanningState(workspaceRoot, packMode, options);
58
+ }
59
+ function inferLegacyPackMode(position) {
60
+ return position.lastCompleted === "BOOT-001" && position.nextRecommended === "PLAN-001" ? "scaffolded" : "authored";
61
+ }
@@ -38,8 +38,8 @@ ${renderTranscript(messages)}
38
38
 
39
39
  Respond as the planning partner.`;
40
40
  }
41
- async function buildPackWriterPrompt(messages, workspaceRoot) {
42
- const repoTruth = await buildRepoTruthSnapshot(workspaceRoot);
41
+ async function buildPackWriterPrompt(messages, workspaceRoot, options = {}) {
42
+ const repoTruth = await buildRepoTruthSnapshot(workspaceRoot, options);
43
43
  return `You are writing a planning pack for the current repository.
44
44
 
45
45
  Read the conversation transcript below and update or create the following files under .srgical/:
@@ -74,8 +74,8 @@ Conversation transcript:
74
74
  ${renderTranscript(messages)}
75
75
  `;
76
76
  }
77
- async function buildRepoTruthSnapshot(workspaceRoot) {
78
- const paths = (0, workspace_1.getPlanningPackPaths)(workspaceRoot);
77
+ async function buildRepoTruthSnapshot(workspaceRoot, options = {}) {
78
+ const paths = (0, workspace_1.getPlanningPackPaths)(workspaceRoot, options);
79
79
  const [packageSummary, topLevelEntries, sourceFiles, docFiles, readmeSnippet, foundationSnippet, adrSnippet, planSnippet, contextSnippet, trackerSnippet, nextPromptSnippet] = await Promise.all([
80
80
  summarizePackageManifest(workspaceRoot),
81
81
  listDirectoryEntries(workspaceRoot),
@@ -13,11 +13,11 @@ exports.DEFAULT_STUDIO_MESSAGES = [
13
13
  content: "Describe what you are building, what is already true in the repo, or the next decision you need to make. I will help turn it into a disciplined `.srgical/` execution pack, and `/write` will update the repo files when you are ready."
14
14
  }
15
15
  ];
16
- async function loadStudioSession(workspaceRoot) {
17
- return (await loadStudioSessionState(workspaceRoot)).messages;
16
+ async function loadStudioSession(workspaceRoot, options = {}) {
17
+ return (await loadStudioSessionState(workspaceRoot, options)).messages;
18
18
  }
19
- async function loadStudioSessionState(workspaceRoot) {
20
- const paths = (0, workspace_1.getPlanningPackPaths)(workspaceRoot);
19
+ async function loadStudioSessionState(workspaceRoot, options = {}) {
20
+ const paths = (0, workspace_1.getPlanningPackPaths)(workspaceRoot, options);
21
21
  const exists = await (0, workspace_1.fileExists)(paths.studioSession);
22
22
  if (!exists) {
23
23
  return createDefaultSessionState();
@@ -35,25 +35,25 @@ async function loadStudioSessionState(workspaceRoot) {
35
35
  return createDefaultSessionState();
36
36
  }
37
37
  }
38
- async function saveStudioSession(workspaceRoot, messages) {
39
- const currentState = await loadStudioSessionState(workspaceRoot);
38
+ async function saveStudioSession(workspaceRoot, messages, options = {}) {
39
+ const currentState = await loadStudioSessionState(workspaceRoot, options);
40
40
  await writeStudioSession(workspaceRoot, {
41
41
  messages,
42
42
  activeAgentId: currentState.activeAgentId
43
- });
43
+ }, options);
44
44
  }
45
- async function loadStoredActiveAgentId(workspaceRoot) {
46
- return (await loadStudioSessionState(workspaceRoot)).activeAgentId;
45
+ async function loadStoredActiveAgentId(workspaceRoot, options = {}) {
46
+ return (await loadStudioSessionState(workspaceRoot, options)).activeAgentId;
47
47
  }
48
- async function saveStoredActiveAgentId(workspaceRoot, activeAgentId) {
49
- const currentState = await loadStudioSessionState(workspaceRoot);
48
+ async function saveStoredActiveAgentId(workspaceRoot, activeAgentId, options = {}) {
49
+ const currentState = await loadStudioSessionState(workspaceRoot, options);
50
50
  await writeStudioSession(workspaceRoot, {
51
51
  messages: currentState.messages,
52
52
  activeAgentId
53
- });
53
+ }, options);
54
54
  }
55
- async function writeStudioSession(workspaceRoot, state) {
56
- const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot);
55
+ async function writeStudioSession(workspaceRoot, state, options = {}) {
56
+ const paths = await (0, workspace_1.ensurePlanningDir)(workspaceRoot, options);
57
57
  const payload = {
58
58
  version: 2,
59
59
  updatedAt: new Date().toISOString(),
@@ -56,7 +56,8 @@ Define and ship a local-first CLI that helps a user:
56
56
  - the UI feels like a sharp creative control room
57
57
  `;
58
58
  }
59
- function buildContextTemplate() {
59
+ function buildContextTemplate(paths) {
60
+ const planDir = `\`${paths.relativeDir}/\``;
60
61
  return `# Agent Context Kickoff
61
62
 
62
63
  Updated: ${new Date().toISOString()}
@@ -64,7 +65,7 @@ Updated By: srgical
64
65
 
65
66
  ## Mission
66
67
 
67
- Continue the current project from the planning pack in \`.srgical/\`. Read the stable plan, the tracker, and the next
68
+ Continue the current project from the planning pack in ${planDir}. Read the stable plan, the tracker, and the next
68
69
  agent prompt before making changes.
69
70
 
70
71
  ## Working Agreements
@@ -86,6 +87,7 @@ agent prompt before making changes.
86
87
  ### ${new Date().toISOString().slice(0, 10)} - BOOT-001 - srgical
87
88
 
88
89
  - Created the initial \`.srgical/\` planning pack.
90
+ - Active planning directory: \`${paths.relativeDir}\`.
89
91
  - Validation: confirmed the four planning-pack files were written.
90
92
  - Blockers: none.
91
93
  - Next recommended work: \`PLAN-001\`.
@@ -138,7 +140,8 @@ Updated By: srgical
138
140
  | EXEC-001 | pending | PLAN-001 | Execute the next eligible implementation slice from the tracker. | The selected slice is complete, validated, and logged. | Pending tracker detail. |
139
141
  `;
140
142
  }
141
- function buildNextPromptTemplate() {
143
+ function buildNextPromptTemplate(paths) {
144
+ const planDir = paths.relativeDir;
142
145
  return `# Next Agent Prompt
143
146
 
144
147
  You are continuing the current project from the existing repo state. Do not restart product design or casually rewrite
@@ -146,9 +149,9 @@ the whole codebase.
146
149
 
147
150
  ## Read Order
148
151
 
149
- 1. Read \`.srgical/02-agent-context-kickoff.md\`.
150
- 2. Read \`.srgical/01-product-plan.md\`.
151
- 3. Read \`.srgical/03-detailed-implementation-plan.md\`.
152
+ 1. Read \`${planDir}/02-agent-context-kickoff.md\`.
153
+ 2. Read \`${planDir}/01-product-plan.md\`.
154
+ 3. Read \`${planDir}/03-detailed-implementation-plan.md\`.
152
155
  4. Execute only the next eligible step block.
153
156
 
154
157
  ## What To Determine Before Editing
@@ -167,10 +170,10 @@ the whole codebase.
167
170
 
168
171
  ## Required Updates After Execution
169
172
 
170
- 1. Update \`.srgical/03-detailed-implementation-plan.md\`.
173
+ 1. Update \`${planDir}/03-detailed-implementation-plan.md\`.
171
174
  2. Mark finished steps \`done\` only if validation passed.
172
175
  3. Update the \`Current Position\` section.
173
- 4. Append a dated handoff entry to \`.srgical/02-agent-context-kickoff.md\`.
176
+ 4. Append a dated handoff entry to \`${planDir}/02-agent-context-kickoff.md\`.
174
177
 
175
178
  ## Stop Conditions
176
179
 
@@ -182,8 +185,8 @@ the whole codebase.
182
185
  function getInitialTemplates(paths) {
183
186
  return {
184
187
  [paths.plan]: buildPlanTemplate(paths.root),
185
- [paths.context]: buildContextTemplate(),
188
+ [paths.context]: buildContextTemplate(paths),
186
189
  [paths.tracker]: buildTrackerTemplate(),
187
- [paths.nextPrompt]: buildNextPromptTemplate()
190
+ [paths.nextPrompt]: buildNextPromptTemplate(paths)
188
191
  };
189
192
  }
@@ -3,40 +3,78 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PLAN_DIR = void 0;
6
+ exports.ACTIVE_PLAN_FILE = exports.NAMED_PLANS_DIR = exports.DEFAULT_PLAN_ID = exports.PLAN_DIR = void 0;
7
7
  exports.resolveWorkspace = resolveWorkspace;
8
+ exports.normalizePlanId = normalizePlanId;
9
+ exports.getPlanningRoot = getPlanningRoot;
8
10
  exports.getPlanningPackPaths = getPlanningPackPaths;
9
11
  exports.ensurePlanningDir = ensurePlanningDir;
12
+ exports.ensurePlanningRoot = ensurePlanningRoot;
10
13
  exports.fileExists = fileExists;
11
14
  exports.planningPackExists = planningPackExists;
15
+ exports.listPlanningDirectories = listPlanningDirectories;
16
+ exports.readActivePlanId = readActivePlanId;
17
+ exports.saveActivePlanId = saveActivePlanId;
18
+ exports.resolvePlanId = resolvePlanId;
12
19
  exports.readText = readText;
13
20
  exports.writeText = writeText;
14
21
  exports.isGitRepo = isGitRepo;
15
22
  const promises_1 = require("node:fs/promises");
16
23
  const node_path_1 = __importDefault(require("node:path"));
17
24
  exports.PLAN_DIR = ".srgical";
25
+ exports.DEFAULT_PLAN_ID = "default";
26
+ exports.NAMED_PLANS_DIR = "plans";
27
+ exports.ACTIVE_PLAN_FILE = "active-plan.txt";
18
28
  function resolveWorkspace(input) {
19
29
  return node_path_1.default.resolve(input ?? process.cwd());
20
30
  }
21
- function getPlanningPackPaths(root) {
22
- const dir = node_path_1.default.join(root, exports.PLAN_DIR);
31
+ function normalizePlanId(value) {
32
+ const normalized = (value ?? exports.DEFAULT_PLAN_ID)
33
+ .trim()
34
+ .toLowerCase()
35
+ .replace(/[^a-z0-9]+/g, "-")
36
+ .replace(/^-+|-+$/g, "");
37
+ return normalized.length > 0 ? normalized : exports.DEFAULT_PLAN_ID;
38
+ }
39
+ function getPlanningRoot(root) {
40
+ return node_path_1.default.join(root, exports.PLAN_DIR);
41
+ }
42
+ function getPlanningPackPaths(root, options = {}) {
43
+ const planningRoot = getPlanningRoot(root);
44
+ const planId = normalizePlanId(options.planId);
45
+ const isDefaultPlan = planId === exports.DEFAULT_PLAN_ID;
46
+ const dir = isDefaultPlan ? planningRoot : node_path_1.default.join(planningRoot, exports.NAMED_PLANS_DIR, planId);
47
+ const relativeDir = node_path_1.default.relative(root, dir).replace(/\\/g, "/") || exports.PLAN_DIR;
23
48
  return {
24
49
  root,
50
+ planningRoot,
51
+ planId,
52
+ isDefaultPlan,
25
53
  dir,
54
+ relativeDir,
26
55
  plan: node_path_1.default.join(dir, "01-product-plan.md"),
27
56
  context: node_path_1.default.join(dir, "02-agent-context-kickoff.md"),
28
57
  tracker: node_path_1.default.join(dir, "03-detailed-implementation-plan.md"),
29
58
  nextPrompt: node_path_1.default.join(dir, "04-next-agent-prompt.md"),
30
59
  studioSession: node_path_1.default.join(dir, "studio-session.json"),
31
60
  executionState: node_path_1.default.join(dir, "execution-state.json"),
32
- executionLog: node_path_1.default.join(dir, "execution-log.md")
61
+ executionLog: node_path_1.default.join(dir, "execution-log.md"),
62
+ planningState: node_path_1.default.join(dir, "planning-state.json"),
63
+ autoRunState: node_path_1.default.join(dir, "auto-run-state.json"),
64
+ activePlanFile: node_path_1.default.join(planningRoot, exports.ACTIVE_PLAN_FILE)
33
65
  };
34
66
  }
35
- async function ensurePlanningDir(root) {
36
- const paths = getPlanningPackPaths(root);
67
+ async function ensurePlanningDir(root, options = {}) {
68
+ const paths = getPlanningPackPaths(root, options);
69
+ await (0, promises_1.mkdir)(paths.planningRoot, { recursive: true });
37
70
  await (0, promises_1.mkdir)(paths.dir, { recursive: true });
38
71
  return paths;
39
72
  }
73
+ async function ensurePlanningRoot(root) {
74
+ const planningRoot = getPlanningRoot(root);
75
+ await (0, promises_1.mkdir)(planningRoot, { recursive: true });
76
+ return planningRoot;
77
+ }
40
78
  async function fileExists(filePath) {
41
79
  try {
42
80
  await (0, promises_1.access)(filePath);
@@ -46,8 +84,8 @@ async function fileExists(filePath) {
46
84
  return false;
47
85
  }
48
86
  }
49
- async function planningPackExists(root) {
50
- const paths = getPlanningPackPaths(root);
87
+ async function planningPackExists(root, options = {}) {
88
+ const paths = getPlanningPackPaths(root, options);
51
89
  const checks = await Promise.all([
52
90
  fileExists(paths.plan),
53
91
  fileExists(paths.context),
@@ -56,6 +94,72 @@ async function planningPackExists(root) {
56
94
  ]);
57
95
  return checks.every(Boolean);
58
96
  }
97
+ async function listPlanningDirectories(root) {
98
+ const planningRoot = getPlanningRoot(root);
99
+ const namedPlansRoot = node_path_1.default.join(planningRoot, exports.NAMED_PLANS_DIR);
100
+ const planIds = new Set();
101
+ if (await hasDefaultPlanPresence(root)) {
102
+ planIds.add(exports.DEFAULT_PLAN_ID);
103
+ }
104
+ try {
105
+ const entries = await (0, promises_1.readdir)(namedPlansRoot, { withFileTypes: true });
106
+ for (const entry of entries) {
107
+ if (!entry.isDirectory()) {
108
+ continue;
109
+ }
110
+ planIds.add(normalizePlanId(entry.name));
111
+ }
112
+ }
113
+ catch {
114
+ // ignore missing named-plan directory
115
+ }
116
+ const activePlanId = await readActivePlanId(root);
117
+ if (activePlanId) {
118
+ planIds.add(activePlanId);
119
+ }
120
+ return Array.from(planIds)
121
+ .sort((left, right) => {
122
+ if (left === exports.DEFAULT_PLAN_ID) {
123
+ return -1;
124
+ }
125
+ if (right === exports.DEFAULT_PLAN_ID) {
126
+ return 1;
127
+ }
128
+ return left.localeCompare(right);
129
+ })
130
+ .map((planId) => {
131
+ const paths = getPlanningPackPaths(root, { planId });
132
+ return {
133
+ planId,
134
+ dir: paths.dir,
135
+ relativeDir: paths.relativeDir,
136
+ isDefaultPlan: paths.isDefaultPlan
137
+ };
138
+ });
139
+ }
140
+ async function readActivePlanId(root) {
141
+ const markerPath = getPlanningPackPaths(root).activePlanFile;
142
+ if (!(await fileExists(markerPath))) {
143
+ return null;
144
+ }
145
+ try {
146
+ return normalizePlanId(await readText(markerPath));
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ }
152
+ async function saveActivePlanId(root, planId) {
153
+ const normalizedPlanId = normalizePlanId(planId);
154
+ const planningRoot = await ensurePlanningRoot(root);
155
+ await writeText(node_path_1.default.join(planningRoot, exports.ACTIVE_PLAN_FILE), normalizedPlanId);
156
+ }
157
+ async function resolvePlanId(root, requestedPlanId) {
158
+ if (requestedPlanId && requestedPlanId.trim().length > 0) {
159
+ return normalizePlanId(requestedPlanId);
160
+ }
161
+ return (await readActivePlanId(root)) ?? exports.DEFAULT_PLAN_ID;
162
+ }
59
163
  async function readText(filePath) {
60
164
  return (0, promises_1.readFile)(filePath, "utf8");
61
165
  }
@@ -65,3 +169,18 @@ async function writeText(filePath, content) {
65
169
  async function isGitRepo(root) {
66
170
  return fileExists(node_path_1.default.join(root, ".git"));
67
171
  }
172
+ async function hasDefaultPlanPresence(root) {
173
+ const paths = getPlanningPackPaths(root, { planId: exports.DEFAULT_PLAN_ID });
174
+ const checks = await Promise.all([
175
+ fileExists(paths.plan),
176
+ fileExists(paths.context),
177
+ fileExists(paths.tracker),
178
+ fileExists(paths.nextPrompt),
179
+ fileExists(paths.studioSession),
180
+ fileExists(paths.executionState),
181
+ fileExists(paths.executionLog),
182
+ fileExists(paths.planningState),
183
+ fileExists(paths.autoRunState)
184
+ ]);
185
+ return checks.some(Boolean);
186
+ }
package/dist/index.js CHANGED
@@ -1,43 +1,67 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
3
  Object.defineProperty(exports, "__esModule", { value: true });
7
- const node_fs_1 = require("node:fs");
8
- const node_path_1 = __importDefault(require("node:path"));
9
4
  const commander_1 = require("commander");
5
+ const about_1 = require("./commands/about");
6
+ const changelog_1 = require("./commands/changelog");
10
7
  const doctor_1 = require("./commands/doctor");
11
8
  const init_1 = require("./commands/init");
12
9
  const run_next_1 = require("./commands/run-next");
13
10
  const studio_1 = require("./commands/studio");
11
+ const version_1 = require("./commands/version");
12
+ const package_info_1 = require("./core/package-info");
14
13
  const program = new commander_1.Command();
15
- const packageVersion = readPackageVersion();
14
+ const packageInfo = (0, package_info_1.readInstalledPackageInfo)();
15
+ if (isStandaloneVersionRequest(process.argv.slice(2))) {
16
+ (0, version_1.runVersionCommand)();
17
+ process.exit(0);
18
+ }
16
19
  program
17
20
  .name("srgical")
18
21
  .description("Local-first AI planning and execution orchestration.")
19
- .version(packageVersion);
22
+ .version(packageInfo.version, "-V, --version", "Show installed version and release info.");
23
+ program
24
+ .command("version")
25
+ .description("Show installed version and release info.")
26
+ .action(() => {
27
+ (0, version_1.runVersionCommand)();
28
+ });
29
+ program
30
+ .command("about")
31
+ .description("Show package, release, and supported-agent information.")
32
+ .action(() => {
33
+ (0, about_1.runAboutCommand)();
34
+ });
35
+ program
36
+ .command("changelog")
37
+ .description("Show where to find upgrade notes for the installed version.")
38
+ .action(() => {
39
+ (0, changelog_1.runChangelogCommand)();
40
+ });
20
41
  program
21
42
  .command("doctor")
22
43
  .description("Inspect the workspace and local agent availability.")
23
44
  .argument("[workspace]", "Workspace path")
24
- .action(async (workspace) => {
25
- await (0, doctor_1.runDoctorCommand)(workspace);
45
+ .option("--plan <id>", "Planning pack id to inspect")
46
+ .action(async (workspace, options) => {
47
+ await (0, doctor_1.runDoctorCommand)(workspace, { planId: options.plan });
26
48
  });
27
49
  program
28
50
  .command("init")
29
51
  .description("Create a local .srgical planning pack scaffold.")
30
52
  .argument("[workspace]", "Workspace path")
31
53
  .option("-f, --force", "Overwrite an existing planning pack")
54
+ .option("--plan <id>", "Named planning pack id to create or overwrite")
32
55
  .action(async (workspace, options) => {
33
- await (0, init_1.runInitCommand)(workspace, Boolean(options.force));
56
+ await (0, init_1.runInitCommand)(workspace, Boolean(options.force), options.plan);
34
57
  });
35
58
  program
36
59
  .command("studio")
37
60
  .description("Open the planning studio.")
38
61
  .argument("[workspace]", "Workspace path")
39
- .action(async (workspace) => {
40
- await (0, studio_1.runStudioCommand)(workspace);
62
+ .option("--plan <id>", "Planning pack id to open")
63
+ .action(async (workspace, options) => {
64
+ await (0, studio_1.runStudioCommand)(workspace, { planId: options.plan });
41
65
  });
42
66
  program
43
67
  .command("run-next")
@@ -45,16 +69,23 @@ program
45
69
  .argument("[workspace]", "Workspace path")
46
70
  .option("--dry-run", "Preview the current execution prompt without invoking the active agent")
47
71
  .option("--agent <id>", "Temporarily override the active agent for this run only")
72
+ .option("--plan <id>", "Planning pack id to execute")
73
+ .option("--auto", "Continue executing eligible execution steps until a stop condition is reached")
74
+ .option("--max-steps <n>", "Maximum number of auto iterations to attempt", Number)
48
75
  .action(async (workspace, options) => {
49
- await (0, run_next_1.runRunNextCommand)(workspace, { dryRun: Boolean(options.dryRun), agent: options.agent });
76
+ await (0, run_next_1.runRunNextCommand)(workspace, {
77
+ dryRun: Boolean(options.dryRun),
78
+ agent: options.agent,
79
+ planId: options.plan,
80
+ auto: Boolean(options.auto),
81
+ maxSteps: options.maxSteps
82
+ });
50
83
  });
51
84
  program.parseAsync(process.argv).catch((error) => {
52
85
  const message = error instanceof Error ? error.message : String(error);
53
86
  process.stderr.write(`${message}\n`);
54
87
  process.exitCode = 1;
55
88
  });
56
- function readPackageVersion() {
57
- const packageJsonPath = node_path_1.default.resolve(__dirname, "..", "package.json");
58
- const packageJson = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, "utf8"));
59
- return packageJson.version ?? "0.0.0";
89
+ function isStandaloneVersionRequest(args) {
90
+ return args.length === 1 && (args[0] === "--version" || args[0] === "-V");
60
91
  }