@rasensio/aidlc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +251 -0
  2. package/capabilities/claude-code.yaml +9 -0
  3. package/capabilities/codex.yaml +8 -0
  4. package/capabilities/cursor.yaml +8 -0
  5. package/capabilities/kiro.yaml +9 -0
  6. package/capabilities/windsurf.yaml +8 -0
  7. package/ci/aidlc-gate.yml +100 -0
  8. package/dist/cli.d.ts +11 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +39 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands/add-action.d.ts +83 -0
  13. package/dist/commands/add-action.d.ts.map +1 -0
  14. package/dist/commands/add-action.js +437 -0
  15. package/dist/commands/add-action.js.map +1 -0
  16. package/dist/commands/continue.d.ts +23 -0
  17. package/dist/commands/continue.d.ts.map +1 -0
  18. package/dist/commands/continue.js +238 -0
  19. package/dist/commands/continue.js.map +1 -0
  20. package/dist/commands/discover.d.ts +19 -0
  21. package/dist/commands/discover.d.ts.map +1 -0
  22. package/dist/commands/discover.js +85 -0
  23. package/dist/commands/discover.js.map +1 -0
  24. package/dist/commands/docs.d.ts +25 -0
  25. package/dist/commands/docs.d.ts.map +1 -0
  26. package/dist/commands/docs.js +282 -0
  27. package/dist/commands/docs.js.map +1 -0
  28. package/dist/commands/gate.d.ts +25 -0
  29. package/dist/commands/gate.d.ts.map +1 -0
  30. package/dist/commands/gate.js +168 -0
  31. package/dist/commands/gate.js.map +1 -0
  32. package/dist/commands/init.d.ts +24 -0
  33. package/dist/commands/init.d.ts.map +1 -0
  34. package/dist/commands/init.js +187 -0
  35. package/dist/commands/init.js.map +1 -0
  36. package/dist/commands/review.d.ts +60 -0
  37. package/dist/commands/review.d.ts.map +1 -0
  38. package/dist/commands/review.js +264 -0
  39. package/dist/commands/review.js.map +1 -0
  40. package/dist/commands/setup.d.ts +24 -0
  41. package/dist/commands/setup.d.ts.map +1 -0
  42. package/dist/commands/setup.js +206 -0
  43. package/dist/commands/setup.js.map +1 -0
  44. package/dist/commands/start.d.ts +27 -0
  45. package/dist/commands/start.d.ts.map +1 -0
  46. package/dist/commands/start.js +243 -0
  47. package/dist/commands/start.js.map +1 -0
  48. package/dist/commands/status.d.ts +25 -0
  49. package/dist/commands/status.d.ts.map +1 -0
  50. package/dist/commands/status.js +263 -0
  51. package/dist/commands/status.js.map +1 -0
  52. package/dist/commands/transition.d.ts +30 -0
  53. package/dist/commands/transition.d.ts.map +1 -0
  54. package/dist/commands/transition.js +353 -0
  55. package/dist/commands/transition.js.map +1 -0
  56. package/dist/compile/adapters/claude-code.d.ts +24 -0
  57. package/dist/compile/adapters/claude-code.d.ts.map +1 -0
  58. package/dist/compile/adapters/claude-code.js +271 -0
  59. package/dist/compile/adapters/claude-code.js.map +1 -0
  60. package/dist/compile/adapters/codex.d.ts +19 -0
  61. package/dist/compile/adapters/codex.d.ts.map +1 -0
  62. package/dist/compile/adapters/codex.js +157 -0
  63. package/dist/compile/adapters/codex.js.map +1 -0
  64. package/dist/compile/adapters/cursor.d.ts +21 -0
  65. package/dist/compile/adapters/cursor.d.ts.map +1 -0
  66. package/dist/compile/adapters/cursor.js +181 -0
  67. package/dist/compile/adapters/cursor.js.map +1 -0
  68. package/dist/compile/adapters/index.d.ts +16 -0
  69. package/dist/compile/adapters/index.d.ts.map +1 -0
  70. package/dist/compile/adapters/index.js +26 -0
  71. package/dist/compile/adapters/index.js.map +1 -0
  72. package/dist/compile/adapters/kiro.d.ts +24 -0
  73. package/dist/compile/adapters/kiro.d.ts.map +1 -0
  74. package/dist/compile/adapters/kiro.js +222 -0
  75. package/dist/compile/adapters/kiro.js.map +1 -0
  76. package/dist/compile/adapters/windsurf.d.ts +25 -0
  77. package/dist/compile/adapters/windsurf.d.ts.map +1 -0
  78. package/dist/compile/adapters/windsurf.js +242 -0
  79. package/dist/compile/adapters/windsurf.js.map +1 -0
  80. package/dist/compile/install-plan.d.ts +49 -0
  81. package/dist/compile/install-plan.d.ts.map +1 -0
  82. package/dist/compile/install-plan.js +112 -0
  83. package/dist/compile/install-plan.js.map +1 -0
  84. package/dist/compile/loaders.d.ts +81 -0
  85. package/dist/compile/loaders.d.ts.map +1 -0
  86. package/dist/compile/loaders.js +293 -0
  87. package/dist/compile/loaders.js.map +1 -0
  88. package/dist/compile/merger.d.ts +32 -0
  89. package/dist/compile/merger.d.ts.map +1 -0
  90. package/dist/compile/merger.js +60 -0
  91. package/dist/compile/merger.js.map +1 -0
  92. package/dist/core/gate.d.ts +36 -0
  93. package/dist/core/gate.d.ts.map +1 -0
  94. package/dist/core/gate.js +97 -0
  95. package/dist/core/gate.js.map +1 -0
  96. package/dist/core/index.d.ts +7 -0
  97. package/dist/core/index.d.ts.map +1 -0
  98. package/dist/core/index.js +7 -0
  99. package/dist/core/index.js.map +1 -0
  100. package/dist/core/lifecycle.d.ts +89 -0
  101. package/dist/core/lifecycle.d.ts.map +1 -0
  102. package/dist/core/lifecycle.js +356 -0
  103. package/dist/core/lifecycle.js.map +1 -0
  104. package/dist/core/template-resolver.d.ts +56 -0
  105. package/dist/core/template-resolver.d.ts.map +1 -0
  106. package/dist/core/template-resolver.js +261 -0
  107. package/dist/core/template-resolver.js.map +1 -0
  108. package/dist/core/types.d.ts +527 -0
  109. package/dist/core/types.d.ts.map +1 -0
  110. package/dist/core/types.js +8 -0
  111. package/dist/core/types.js.map +1 -0
  112. package/dist/discover/scanner.d.ts +58 -0
  113. package/dist/discover/scanner.d.ts.map +1 -0
  114. package/dist/discover/scanner.js +772 -0
  115. package/dist/discover/scanner.js.map +1 -0
  116. package/dist/events/event-bus.d.ts +53 -0
  117. package/dist/events/event-bus.d.ts.map +1 -0
  118. package/dist/events/event-bus.js +263 -0
  119. package/dist/events/event-bus.js.map +1 -0
  120. package/dist/events/plugin-hooks.d.ts +44 -0
  121. package/dist/events/plugin-hooks.d.ts.map +1 -0
  122. package/dist/events/plugin-hooks.js +225 -0
  123. package/dist/events/plugin-hooks.js.map +1 -0
  124. package/dist/orchestrator/budget-packer.d.ts +57 -0
  125. package/dist/orchestrator/budget-packer.d.ts.map +1 -0
  126. package/dist/orchestrator/budget-packer.js +70 -0
  127. package/dist/orchestrator/budget-packer.js.map +1 -0
  128. package/dist/orchestrator/config.d.ts +43 -0
  129. package/dist/orchestrator/config.d.ts.map +1 -0
  130. package/dist/orchestrator/config.js +109 -0
  131. package/dist/orchestrator/config.js.map +1 -0
  132. package/dist/orchestrator/endpoint-clients.d.ts +23 -0
  133. package/dist/orchestrator/endpoint-clients.d.ts.map +1 -0
  134. package/dist/orchestrator/endpoint-clients.js +129 -0
  135. package/dist/orchestrator/endpoint-clients.js.map +1 -0
  136. package/dist/orchestrator/orchestrator-loop.d.ts +93 -0
  137. package/dist/orchestrator/orchestrator-loop.d.ts.map +1 -0
  138. package/dist/orchestrator/orchestrator-loop.js +185 -0
  139. package/dist/orchestrator/orchestrator-loop.js.map +1 -0
  140. package/dist/security/confirmation.d.ts +90 -0
  141. package/dist/security/confirmation.d.ts.map +1 -0
  142. package/dist/security/confirmation.js +140 -0
  143. package/dist/security/confirmation.js.map +1 -0
  144. package/dist/setup/setup-compute.d.ts +54 -0
  145. package/dist/setup/setup-compute.d.ts.map +1 -0
  146. package/dist/setup/setup-compute.js +66 -0
  147. package/dist/setup/setup-compute.js.map +1 -0
  148. package/dist/state/snapshot-store.d.ts +79 -0
  149. package/dist/state/snapshot-store.d.ts.map +1 -0
  150. package/dist/state/snapshot-store.js +164 -0
  151. package/dist/state/snapshot-store.js.map +1 -0
  152. package/dist/state/state-recovery.d.ts +87 -0
  153. package/dist/state/state-recovery.d.ts.map +1 -0
  154. package/dist/state/state-recovery.js +274 -0
  155. package/dist/state/state-recovery.js.map +1 -0
  156. package/dist/state/transition-log.d.ts +40 -0
  157. package/dist/state/transition-log.d.ts.map +1 -0
  158. package/dist/state/transition-log.js +83 -0
  159. package/dist/state/transition-log.js.map +1 -0
  160. package/dist/state/yaml-helpers.d.ts +53 -0
  161. package/dist/state/yaml-helpers.d.ts.map +1 -0
  162. package/dist/state/yaml-helpers.js +110 -0
  163. package/dist/state/yaml-helpers.js.map +1 -0
  164. package/examples/.gitkeep +0 -0
  165. package/guidance/accessibility.md +67 -0
  166. package/guidance/api-conventions.md +124 -0
  167. package/guidance/index.yaml +7 -0
  168. package/guidance/secure-defaults.md +71 -0
  169. package/package.json +52 -0
  170. package/phases/.gitkeep +0 -0
  171. package/skills/00-overview.md +74 -0
  172. package/skills/10-ideation.md +45 -0
  173. package/skills/20-requirements.md +54 -0
  174. package/skills/30-design.md +57 -0
  175. package/skills/40-implementation.md +64 -0
  176. package/skills/50-testing.md +60 -0
  177. package/skills/60-deployment.md +63 -0
  178. package/skills/70-maintenance.md +56 -0
  179. package/skills/80-review.md +85 -0
  180. package/skills/81-continue.md +57 -0
  181. package/skills/82-add-action.md +71 -0
  182. package/templates/.gitkeep +0 -0
  183. package/templates/bugfix.yaml +14 -0
  184. package/templates/full-feature.yaml +44 -0
  185. package/templates/micro-task.yaml +11 -0
  186. package/templates/quick-feature.yaml +20 -0
  187. package/templates/spike.yaml +15 -0
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Orchestrator loop — drives a lifecycle instance through phases via a bare LLM endpoint.
3
+ *
4
+ * This module is optional (Req 19.12). It imports endpoint-clients and budget-packer
5
+ * but does NOT import into the CLI unless the `aidlc run` command is invoked.
6
+ *
7
+ * Loop:
8
+ * 1. Load instance state, resolve template, compute nextStep
9
+ * 2. Pack context items into the budget using packContext
10
+ * 3. Build the prompt (system message + context + task)
11
+ * 4. Call the LLM endpoint
12
+ * 5. Process the response (log; artifact processing is a future enhancement)
13
+ * 6. If autoAdvance, attempt transition
14
+ * 7. Repeat until complete or maxIterations reached
15
+ *
16
+ * Requirements: 19.1, 19.6, 19.12
17
+ */
18
+ import { nextStep } from '../core/lifecycle.js';
19
+ import { packContext, estimateTokens } from './budget-packer.js';
20
+ // ---------------------------------------------------------------------------
21
+ // Prompt Building
22
+ // ---------------------------------------------------------------------------
23
+ /**
24
+ * Build the system prompt from the current phase skill.
25
+ *
26
+ * The skill body forms the system message. If no skill is found, this is
27
+ * a hard error (Req 19.5) — callers must handle null skill before calling.
28
+ */
29
+ export function buildSystemPrompt(skillContent, phase) {
30
+ return [
31
+ `You are an AI assistant operating in AIDLC orchestrator mode.`,
32
+ `Current phase: ${phase}`,
33
+ '',
34
+ '--- Phase Skill ---',
35
+ skillContent,
36
+ ].join('\n');
37
+ }
38
+ /**
39
+ * Build the user message from packed context and the current task.
40
+ */
41
+ export function buildUserMessage(packResult, step, instanceName) {
42
+ const parts = [];
43
+ // Include packed context
44
+ if (packResult.included.length > 0) {
45
+ parts.push('--- Context ---');
46
+ for (const item of packResult.included) {
47
+ const content = packResult.usedSummary.includes(item.id)
48
+ ? (item.summary ?? item.content)
49
+ : item.content;
50
+ parts.push(`[${item.id}]`);
51
+ parts.push(content);
52
+ parts.push('');
53
+ }
54
+ }
55
+ // Include current task directive
56
+ parts.push('--- Task ---');
57
+ parts.push(`Instance: ${instanceName}`);
58
+ switch (step.kind) {
59
+ case 'artifact':
60
+ parts.push(`Produce the artifact: ${step.artifact} (phase: ${step.phase})`);
61
+ parts.push('Write the artifact content. When done, indicate completion.');
62
+ break;
63
+ case 'phase':
64
+ parts.push(`Begin phase: ${step.phase}`);
65
+ parts.push('Prepare to work on this phase. Produce the first required artifact.');
66
+ break;
67
+ case 'complete':
68
+ parts.push('All phases are complete. Summarize the lifecycle outcome.');
69
+ break;
70
+ }
71
+ return parts.join('\n');
72
+ }
73
+ // ---------------------------------------------------------------------------
74
+ // Orchestrator Loop
75
+ // ---------------------------------------------------------------------------
76
+ /**
77
+ * Run the orchestrator loop for a lifecycle instance.
78
+ *
79
+ * The loop repeats until:
80
+ * - The lifecycle instance reaches completion (nextStep returns 'complete')
81
+ * - maxIterations is reached (safety cap)
82
+ * - An error occurs (endpoint failure, missing skill, etc.)
83
+ *
84
+ * @param opts - Orchestrator configuration and endpoint client.
85
+ * @param loader - Context loader for state, templates, and files.
86
+ * @returns OrchestratorResult describing the outcome.
87
+ */
88
+ export async function runOrchestratorLoop(opts, loader) {
89
+ const maxIterations = opts.maxIterations ?? 50;
90
+ let iterations = 0;
91
+ let currentPhase = '';
92
+ try {
93
+ // Main loop
94
+ while (iterations < maxIterations) {
95
+ iterations++;
96
+ // Step 1: Load instance state, resolve template, compute nextStep
97
+ const state = loader.loadInstanceState(opts.instance);
98
+ const phaseStates = loader.loadPhaseStates(opts.instance);
99
+ const template = loader.loadTemplate(state.template);
100
+ currentPhase = state.current_phase;
101
+ const step = nextStep(state, phaseStates, template);
102
+ // If already complete, we're done
103
+ if (step.kind === 'complete') {
104
+ return {
105
+ iterations,
106
+ finalPhase: currentPhase,
107
+ completed: true,
108
+ };
109
+ }
110
+ // Step 2: Load and validate the phase skill (Req 19.5 — hard error if missing)
111
+ const targetPhase = step.kind === 'artifact' ? step.phase : step.phase;
112
+ const skillContent = loader.loadPhaseSkill(targetPhase);
113
+ if (skillContent === null) {
114
+ return {
115
+ iterations,
116
+ finalPhase: currentPhase,
117
+ completed: false,
118
+ error: `phase skill missing or unreadable for phase "${targetPhase}" — cannot construct prompt (Req 19.5)`,
119
+ };
120
+ }
121
+ // Check that the skill fits within the budget (Req 25.8)
122
+ const skillTokens = estimateTokens(skillContent);
123
+ if (skillTokens > opts.contextBudget) {
124
+ return {
125
+ iterations,
126
+ finalPhase: currentPhase,
127
+ completed: false,
128
+ error: `phase skill for "${targetPhase}" exceeds context budget (${skillTokens} tokens > ${opts.contextBudget} budget)`,
129
+ };
130
+ }
131
+ // Step 3: Pack context items into the remaining budget
132
+ const contextItems = loader.loadContextItems(opts.instance, targetPhase);
133
+ const remainingBudget = opts.contextBudget - skillTokens;
134
+ const packResult = packContext(contextItems, remainingBudget);
135
+ // Step 4: Build the prompt
136
+ const systemPrompt = buildSystemPrompt(skillContent, targetPhase);
137
+ const userMessage = buildUserMessage(packResult, step, opts.instance);
138
+ const messages = [
139
+ { role: 'system', content: systemPrompt },
140
+ { role: 'user', content: userMessage },
141
+ ];
142
+ // Step 5: Call the LLM endpoint
143
+ const response = await opts.client.chat(messages, { model: opts.model });
144
+ // Step 6: Process the response (placeholder — log but don't parse file ops)
145
+ loader.processResponse(opts.instance, targetPhase, response.content);
146
+ // Step 7: If autoAdvance, attempt transition when step indicates readiness
147
+ if (opts.autoAdvance) {
148
+ // Re-check state after processing to see if we should advance
149
+ const updatedStep = nextStep(loader.loadInstanceState(opts.instance), loader.loadPhaseStates(opts.instance), template);
150
+ if (updatedStep.kind === 'complete') {
151
+ return {
152
+ iterations,
153
+ finalPhase: currentPhase,
154
+ completed: true,
155
+ };
156
+ }
157
+ if (updatedStep.kind === 'phase') {
158
+ // Attempt phase transition
159
+ const newPhase = loader.attemptTransition(opts.instance, currentPhase);
160
+ if (newPhase !== null) {
161
+ currentPhase = newPhase;
162
+ }
163
+ }
164
+ }
165
+ }
166
+ // maxIterations reached
167
+ return {
168
+ iterations,
169
+ finalPhase: currentPhase,
170
+ completed: false,
171
+ error: `maximum iterations reached (${maxIterations})`,
172
+ };
173
+ }
174
+ catch (err) {
175
+ // Endpoint failure or other error (Req 19.11 — persist state, report error)
176
+ const message = err instanceof Error ? err.message : String(err);
177
+ return {
178
+ iterations,
179
+ finalPhase: currentPhase,
180
+ completed: false,
181
+ error: message,
182
+ };
183
+ }
184
+ }
185
+ //# sourceMappingURL=orchestrator-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator-loop.js","sourceRoot":"","sources":["../../src/orchestrator/orchestrator-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAUH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AA2DjE,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,YAAoB,EAAE,KAAa;IACnE,OAAO;QACL,+DAA+D;QAC/D,kBAAkB,KAAK,EAAE;QACzB,EAAE;QACF,qBAAqB;QACrB,YAAY;KACb,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAsB,EACtB,IAAc,EACd,YAAoB;IAEpB,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,yBAAyB;IACzB,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;YACvC,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtD,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC;gBAChC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,aAAa,YAAY,EAAE,CAAC,CAAC;IAExC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,UAAU;YACb,KAAK,CAAC,IAAI,CAAC,yBAAyB,IAAI,CAAC,QAAQ,YAAY,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;YAC5E,KAAK,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;YAC1E,MAAM;QACR,KAAK,OAAO;YACV,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAC;YAClF,MAAM;QACR,KAAK,UAAU;YACb,KAAK,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;YACxE,MAAM;IACV,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAsB,EACtB,MAAqB;IAErB,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;IAC/C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,YAAY,GAAG,EAAE,CAAC;IAEtB,IAAI,CAAC;QACH,YAAY;QACZ,OAAO,UAAU,GAAG,aAAa,EAAE,CAAC;YAClC,UAAU,EAAE,CAAC;YAEb,kEAAkE;YAClE,MAAM,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACtD,MAAM,WAAW,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACrD,YAAY,GAAG,KAAK,CAAC,aAAa,CAAC;YAEnC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;YAEpD,kCAAkC;YAClC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC7B,OAAO;oBACL,UAAU;oBACV,UAAU,EAAE,YAAY;oBACxB,SAAS,EAAE,IAAI;iBAChB,CAAC;YACJ,CAAC;YAED,+EAA+E;YAC/E,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YACvE,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,WAAqB,CAAC,CAAC;YAElE,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;gBAC1B,OAAO;oBACL,UAAU;oBACV,UAAU,EAAE,YAAY;oBACxB,SAAS,EAAE,KAAK;oBAChB,KAAK,EAAE,gDAAgD,WAAW,wCAAwC;iBAC3G,CAAC;YACJ,CAAC;YAED,yDAAyD;YACzD,MAAM,WAAW,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;YACjD,IAAI,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrC,OAAO;oBACL,UAAU;oBACV,UAAU,EAAE,YAAY;oBACxB,SAAS,EAAE,KAAK;oBAChB,KAAK,EAAE,oBAAoB,WAAW,6BAA6B,WAAW,aAAa,IAAI,CAAC,aAAa,UAAU;iBACxH,CAAC;YACJ,CAAC;YAED,uDAAuD;YACvD,MAAM,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAqB,CAAC,CAAC;YACnF,MAAM,eAAe,GAAG,IAAI,CAAC,aAAa,GAAG,WAAW,CAAC;YACzD,MAAM,UAAU,GAAG,WAAW,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;YAE9D,2BAA2B;YAC3B,MAAM,YAAY,GAAG,iBAAiB,CAAC,YAAY,EAAE,WAAqB,CAAC,CAAC;YAC5E,MAAM,WAAW,GAAG,gBAAgB,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAEtE,MAAM,QAAQ,GAAkB;gBAC9B,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE;gBACzC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE;aACvC,CAAC;YAEF,gCAAgC;YAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAEzE,4EAA4E;YAC5E,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAqB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;YAE/E,2EAA2E;YAC3E,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrB,8DAA8D;gBAC9D,MAAM,WAAW,GAAG,QAAQ,CAC1B,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,EACvC,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,EACrC,QAAQ,CACT,CAAC;gBAEF,IAAI,WAAW,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;oBACpC,OAAO;wBACL,UAAU;wBACV,UAAU,EAAE,YAAY;wBACxB,SAAS,EAAE,IAAI;qBAChB,CAAC;gBACJ,CAAC;gBAED,IAAI,WAAW,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBACjC,2BAA2B;oBAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;oBACvE,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;wBACtB,YAAY,GAAG,QAAQ,CAAC;oBAC1B,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,wBAAwB;QACxB,OAAO;YACL,UAAU;YACV,UAAU,EAAE,YAAY;YACxB,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,+BAA+B,aAAa,GAAG;SACvD,CAAC;IACJ,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,4EAA4E;QAC5E,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO;YACL,UAAU;YACV,UAAU,EAAE,YAAY;YACxB,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,OAAO;SACf,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Sensitive-operation confirmation and cancellation logging.
3
+ *
4
+ * Provides utilities for gating sensitive operations behind user confirmation
5
+ * and logging cancellation events to an append-only NDJSON log file.
6
+ *
7
+ * The cancellation log lives at `.aidlc/state/<instance>/cancellations.log`
8
+ * and follows the same append-only NDJSON pattern as transitions.log.
9
+ *
10
+ * Requirements: 13.1, 13.3, 13.6, 13.7
11
+ */
12
+ /** Describes a sensitive operation requiring confirmation. */
13
+ export interface SensitiveOperation {
14
+ /** What's being done (e.g., "delete production database"). */
15
+ operation: string;
16
+ /** What it affects (e.g., "users table in prod-db"). */
17
+ impact: string;
18
+ /** Whether the operation can be undone. */
19
+ reversible: boolean;
20
+ }
21
+ /** Result of a confirmation request. */
22
+ export interface ConfirmationResult {
23
+ /** Whether the user confirmed the operation. */
24
+ confirmed: boolean;
25
+ /** Reason for cancellation (present when confirmed is false). */
26
+ reason?: string;
27
+ }
28
+ /** A single entry in the cancellation log (NDJSON). */
29
+ export interface CancellationEntry {
30
+ /** ISO 8601 UTC timestamp of the cancellation. */
31
+ at: string;
32
+ /** The operation that was cancelled. */
33
+ operation: string;
34
+ /** What the operation would have affected. */
35
+ impact: string;
36
+ /** Reason for cancellation. */
37
+ reason: string;
38
+ }
39
+ /**
40
+ * Request confirmation for a sensitive operation.
41
+ *
42
+ * In non-interactive mode, always declines with a standard reason.
43
+ * In interactive mode, the caller is responsible for prompting the user
44
+ * and passing the result through; this function handles the non-interactive
45
+ * gate and structures the result.
46
+ *
47
+ * @param op - The sensitive operation details.
48
+ * @param opts - Options: `nonInteractive` forces automatic decline.
49
+ * @returns A ConfirmationResult indicating whether to proceed.
50
+ */
51
+ export declare function requestConfirmation(op: SensitiveOperation, opts?: {
52
+ nonInteractive?: boolean;
53
+ }): ConfirmationResult;
54
+ /**
55
+ * Append a cancellation entry to the instance's cancellations.log.
56
+ *
57
+ * Creates the file and parent directories if they do not exist.
58
+ * Uses O_APPEND for atomic line appends under concurrent writes.
59
+ *
60
+ * @param stateDir - Absolute path to the `.aidlc/state/` directory.
61
+ * @param instance - Instance name (directory namespace).
62
+ * @param entry - The cancellation entry to log.
63
+ */
64
+ export declare function logCancellation(stateDir: string, instance: string, entry: CancellationEntry): void;
65
+ /**
66
+ * Read all cancellation entries from the instance's cancellations.log.
67
+ *
68
+ * - Returns an empty array if the file does not exist.
69
+ * - Skips empty lines.
70
+ * - Skips malformed JSON lines (logs a warning to stderr).
71
+ *
72
+ * @param stateDir - Absolute path to the `.aidlc/state/` directory.
73
+ * @param instance - Instance name (directory namespace).
74
+ * @returns Array of parsed CancellationEntry records.
75
+ */
76
+ export declare function readCancellations(stateDir: string, instance: string): CancellationEntry[];
77
+ /**
78
+ * Combined helper: request confirmation for a sensitive operation, and if
79
+ * declined, log the cancellation event automatically.
80
+ *
81
+ * @param op - The sensitive operation.
82
+ * @param stateDir - Absolute path to `.aidlc/state/`.
83
+ * @param instance - Lifecycle instance name.
84
+ * @param opts - Options: `nonInteractive` forces automatic decline.
85
+ * @returns The ConfirmationResult (callers should check `.confirmed`).
86
+ */
87
+ export declare function confirmOrLog(op: SensitiveOperation, stateDir: string, instance: string, opts?: {
88
+ nonInteractive?: boolean;
89
+ }): ConfirmationResult;
90
+ //# sourceMappingURL=confirmation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirmation.d.ts","sourceRoot":"","sources":["../../src/security/confirmation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAgBH,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wCAAwC;AACxC,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,uDAAuD;AACvD,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,EAAE,EAAE,MAAM,CAAC;IACX,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,kBAAkB,EACtB,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,kBAAkB,CAYpB;AAaD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,iBAAiB,GACvB,IAAI,CAUN;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,iBAAiB,EAAE,CA+BrB;AAMD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,kBAAkB,EACtB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,kBAAkB,CAcpB"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Sensitive-operation confirmation and cancellation logging.
3
+ *
4
+ * Provides utilities for gating sensitive operations behind user confirmation
5
+ * and logging cancellation events to an append-only NDJSON log file.
6
+ *
7
+ * The cancellation log lives at `.aidlc/state/<instance>/cancellations.log`
8
+ * and follows the same append-only NDJSON pattern as transitions.log.
9
+ *
10
+ * Requirements: 13.1, 13.3, 13.6, 13.7
11
+ */
12
+ import { readFileSync, openSync, writeSync, closeSync, constants, mkdirSync, } from 'node:fs';
13
+ import { dirname, join } from 'node:path';
14
+ // ---------------------------------------------------------------------------
15
+ // Confirmation check (Req 13.6)
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * Request confirmation for a sensitive operation.
19
+ *
20
+ * In non-interactive mode, always declines with a standard reason.
21
+ * In interactive mode, the caller is responsible for prompting the user
22
+ * and passing the result through; this function handles the non-interactive
23
+ * gate and structures the result.
24
+ *
25
+ * @param op - The sensitive operation details.
26
+ * @param opts - Options: `nonInteractive` forces automatic decline.
27
+ * @returns A ConfirmationResult indicating whether to proceed.
28
+ */
29
+ export function requestConfirmation(op, opts) {
30
+ if (opts?.nonInteractive) {
31
+ return {
32
+ confirmed: false,
33
+ reason: `Non-interactive mode: declined sensitive operation "${op.operation}"`,
34
+ };
35
+ }
36
+ // In interactive mode, we return confirmed: true.
37
+ // The actual interactive prompt is handled by the CLI layer (e.g., @clack/prompts).
38
+ // This function serves as the programmatic gate that the CLI calls after prompting.
39
+ return { confirmed: true };
40
+ }
41
+ // ---------------------------------------------------------------------------
42
+ // Cancellation log writer (Req 13.7)
43
+ // ---------------------------------------------------------------------------
44
+ /**
45
+ * Build the path to the cancellations.log file for an instance.
46
+ */
47
+ function cancellationsLogPath(stateDir, instance) {
48
+ return join(stateDir, instance, 'cancellations.log');
49
+ }
50
+ /**
51
+ * Append a cancellation entry to the instance's cancellations.log.
52
+ *
53
+ * Creates the file and parent directories if they do not exist.
54
+ * Uses O_APPEND for atomic line appends under concurrent writes.
55
+ *
56
+ * @param stateDir - Absolute path to the `.aidlc/state/` directory.
57
+ * @param instance - Instance name (directory namespace).
58
+ * @param entry - The cancellation entry to log.
59
+ */
60
+ export function logCancellation(stateDir, instance, entry) {
61
+ const logPath = cancellationsLogPath(stateDir, instance);
62
+ mkdirSync(dirname(logPath), { recursive: true });
63
+ const line = JSON.stringify(entry) + '\n';
64
+ const fd = openSync(logPath, constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT);
65
+ try {
66
+ writeSync(fd, line);
67
+ }
68
+ finally {
69
+ closeSync(fd);
70
+ }
71
+ }
72
+ // ---------------------------------------------------------------------------
73
+ // Cancellation log reader
74
+ // ---------------------------------------------------------------------------
75
+ /**
76
+ * Read all cancellation entries from the instance's cancellations.log.
77
+ *
78
+ * - Returns an empty array if the file does not exist.
79
+ * - Skips empty lines.
80
+ * - Skips malformed JSON lines (logs a warning to stderr).
81
+ *
82
+ * @param stateDir - Absolute path to the `.aidlc/state/` directory.
83
+ * @param instance - Instance name (directory namespace).
84
+ * @returns Array of parsed CancellationEntry records.
85
+ */
86
+ export function readCancellations(stateDir, instance) {
87
+ const logPath = cancellationsLogPath(stateDir, instance);
88
+ let raw;
89
+ try {
90
+ raw = readFileSync(logPath, 'utf8');
91
+ }
92
+ catch (err) {
93
+ const code = err.code;
94
+ if (code === 'ENOENT') {
95
+ return [];
96
+ }
97
+ throw err;
98
+ }
99
+ const lines = raw.split('\n');
100
+ const entries = [];
101
+ for (let i = 0; i < lines.length; i++) {
102
+ const line = lines[i].trim();
103
+ if (line === '')
104
+ continue;
105
+ try {
106
+ entries.push(JSON.parse(line));
107
+ }
108
+ catch {
109
+ process.stderr.write(`cancellations.log: skipping malformed line ${i + 1}: ${line.slice(0, 80)}\n`);
110
+ }
111
+ }
112
+ return entries;
113
+ }
114
+ // ---------------------------------------------------------------------------
115
+ // Convenience: confirm-or-log helper
116
+ // ---------------------------------------------------------------------------
117
+ /**
118
+ * Combined helper: request confirmation for a sensitive operation, and if
119
+ * declined, log the cancellation event automatically.
120
+ *
121
+ * @param op - The sensitive operation.
122
+ * @param stateDir - Absolute path to `.aidlc/state/`.
123
+ * @param instance - Lifecycle instance name.
124
+ * @param opts - Options: `nonInteractive` forces automatic decline.
125
+ * @returns The ConfirmationResult (callers should check `.confirmed`).
126
+ */
127
+ export function confirmOrLog(op, stateDir, instance, opts) {
128
+ const result = requestConfirmation(op, opts);
129
+ if (!result.confirmed) {
130
+ const entry = {
131
+ at: new Date().toISOString(),
132
+ operation: op.operation,
133
+ impact: op.impact,
134
+ reason: result.reason ?? 'User declined',
135
+ };
136
+ logCancellation(stateDir, instance, entry);
137
+ }
138
+ return result;
139
+ }
140
+ //# sourceMappingURL=confirmation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirmation.js","sourceRoot":"","sources":["../../src/security/confirmation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Pure, idempotent setup computation.
3
+ *
4
+ * `computeSetup(existing, answers)` merges an existing config with user
5
+ * answers. The function is idempotent: applying the same answers twice
6
+ * yields the same result without duplicating array entries.
7
+ *
8
+ * Requirements: 21.10
9
+ */
10
+ import type { CycleScope } from '../core/types.js';
11
+ /** Answers collected from the setup wizard (interactive or CLI flags). */
12
+ export interface SetupAnswers {
13
+ /** e.g., "web-app", "library", "cli", "api" */
14
+ projectType?: string;
15
+ /** e.g., "simple", "moderate", "complex" */
16
+ complexity?: string;
17
+ /** Platforms to support (e.g., ["claude-code", "kiro"]) */
18
+ platforms?: string[];
19
+ /** Built-in actions to enable (e.g., ["git-branch", "doc-update"]) */
20
+ actions?: string[];
21
+ /** Default workflow template name */
22
+ template?: string;
23
+ /** Default lifecycle scope */
24
+ scope?: CycleScope;
25
+ }
26
+ /** Configuration produced/consumed by the setup wizard. */
27
+ export interface SetupConfig {
28
+ version: number;
29
+ defaults: {
30
+ scope: CycleScope;
31
+ template: string;
32
+ };
33
+ platforms: string[];
34
+ actions: string[];
35
+ stalled_threshold_days: number;
36
+ claim_timeout_minutes: number;
37
+ }
38
+ /**
39
+ * Compute the setup result by merging existing config with new answers.
40
+ *
41
+ * Idempotent: `computeSetup(computeSetup(config, answers), answers)` equals
42
+ * `computeSetup(config, answers)`.
43
+ *
44
+ * Merge rules:
45
+ * - `platforms`: sorted union of existing + answers (no duplicates)
46
+ * - `actions`: sorted union of existing + answers (no duplicates)
47
+ * - `defaults.scope`: answers overrides existing if provided
48
+ * - `defaults.template`: answers overrides existing if provided
49
+ *
50
+ * Scalar config values (`version`, `stalled_threshold_days`,
51
+ * `claim_timeout_minutes`) carry forward from existing or use defaults.
52
+ */
53
+ export declare function computeSetup(existing: SetupConfig | null, answers: SetupAnswers): SetupConfig;
54
+ //# sourceMappingURL=setup-compute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-compute.d.ts","sourceRoot":"","sources":["../../src/setup/setup-compute.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMnD,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QACR,KAAK,EAAE,UAAU,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAkCD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,WAAW,GAAG,IAAI,EAC5B,OAAO,EAAE,YAAY,GACpB,WAAW,CAcb"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Pure, idempotent setup computation.
3
+ *
4
+ * `computeSetup(existing, answers)` merges an existing config with user
5
+ * answers. The function is idempotent: applying the same answers twice
6
+ * yields the same result without duplicating array entries.
7
+ *
8
+ * Requirements: 21.10
9
+ */
10
+ // ---------------------------------------------------------------------------
11
+ // Defaults
12
+ // ---------------------------------------------------------------------------
13
+ const DEFAULT_CONFIG = {
14
+ version: 1,
15
+ defaults: {
16
+ scope: 'micro',
17
+ template: 'micro-task',
18
+ },
19
+ platforms: [],
20
+ actions: [],
21
+ stalled_threshold_days: 7,
22
+ claim_timeout_minutes: 60,
23
+ };
24
+ // ---------------------------------------------------------------------------
25
+ // Helpers
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Compute the sorted union of two string arrays without duplicates.
29
+ */
30
+ function sortedUnion(a, b) {
31
+ const set = new Set([...a, ...b]);
32
+ return [...set].sort();
33
+ }
34
+ // ---------------------------------------------------------------------------
35
+ // Public API
36
+ // ---------------------------------------------------------------------------
37
+ /**
38
+ * Compute the setup result by merging existing config with new answers.
39
+ *
40
+ * Idempotent: `computeSetup(computeSetup(config, answers), answers)` equals
41
+ * `computeSetup(config, answers)`.
42
+ *
43
+ * Merge rules:
44
+ * - `platforms`: sorted union of existing + answers (no duplicates)
45
+ * - `actions`: sorted union of existing + answers (no duplicates)
46
+ * - `defaults.scope`: answers overrides existing if provided
47
+ * - `defaults.template`: answers overrides existing if provided
48
+ *
49
+ * Scalar config values (`version`, `stalled_threshold_days`,
50
+ * `claim_timeout_minutes`) carry forward from existing or use defaults.
51
+ */
52
+ export function computeSetup(existing, answers) {
53
+ const base = existing ?? DEFAULT_CONFIG;
54
+ return {
55
+ version: base.version,
56
+ defaults: {
57
+ scope: answers.scope ?? base.defaults.scope,
58
+ template: answers.template ?? base.defaults.template,
59
+ },
60
+ platforms: sortedUnion(base.platforms, answers.platforms ?? []),
61
+ actions: sortedUnion(base.actions, answers.actions ?? []),
62
+ stalled_threshold_days: base.stalled_threshold_days,
63
+ claim_timeout_minutes: base.claim_timeout_minutes,
64
+ };
65
+ }
66
+ //# sourceMappingURL=setup-compute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-compute.js","sourceRoot":"","sources":["../../src/setup/setup-compute.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqCH,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E,MAAM,cAAc,GAAgB;IAClC,OAAO,EAAE,CAAC;IACV,QAAQ,EAAE;QACR,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,YAAY;KACvB;IACD,SAAS,EAAE,EAAE;IACb,OAAO,EAAE,EAAE;IACX,sBAAsB,EAAE,CAAC;IACzB,qBAAqB,EAAE,EAAE;CAC1B,CAAC;AAEF,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;GAEG;AACH,SAAS,WAAW,CAAC,CAAW,EAAE,CAAW;IAC3C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAClC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY,CAC1B,QAA4B,EAC5B,OAAqB;IAErB,MAAM,IAAI,GAAG,QAAQ,IAAI,cAAc,CAAC;IAExC,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,QAAQ,EAAE;YACR,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK;YAC3C,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ;SACrD;QACD,SAAS,EAAE,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;QAC/D,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QACzD,sBAAsB,EAAE,IAAI,CAAC,sBAAsB;QACnD,qBAAqB,EAAE,IAAI,CAAC,qBAAqB;KAClD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Snapshot store with atomic writes and per-instance namespaces.
3
+ *
4
+ * All instance state lives under `.aidlc/state/<instance-name>/`:
5
+ * - `instance.yaml` — instance metadata
6
+ * - `phase-<name>.yaml` — per-phase state (one per applicable phase)
7
+ *
8
+ * Write semantics:
9
+ * - Snapshot files use write-to-tmp + rename for atomicity (Req 23.5).
10
+ * Readers never observe partial writes.
11
+ * - Instance directories are fully isolated — edits to one instance
12
+ * never touch another instance's files (Req 23.1, 23.3, 6.1).
13
+ *
14
+ * Requirements: 4.1, 6.1, 23.1, 23.3, 23.5
15
+ */
16
+ import type { SchemaValidator } from './yaml-helpers.js';
17
+ /**
18
+ * Write content to a file atomically using tmp-file + rename.
19
+ *
20
+ * 1. Write to `<filePath>.tmp.<pid>` — unique per process, avoids collisions.
21
+ * 2. Rename the tmp file to the target path — atomic on POSIX filesystems.
22
+ *
23
+ * Parent directories are created if they don't exist.
24
+ */
25
+ export declare function atomicWrite(filePath: string, content: string): void;
26
+ /**
27
+ * Compute the absolute directory path for an instance namespace.
28
+ *
29
+ * @param stateDir - Root state directory (e.g., `/project/.aidlc/state`).
30
+ * @param instance - Instance name (URL-safe directory name).
31
+ * @returns Absolute path: `<stateDir>/<instance>/`.
32
+ */
33
+ export declare function instancePath(stateDir: string, instance: string): string;
34
+ /**
35
+ * Create the namespace directory for a new lifecycle instance.
36
+ *
37
+ * @param stateDir - Root state directory.
38
+ * @param name - Instance name.
39
+ * @returns The absolute path to the created directory.
40
+ */
41
+ export declare function createInstanceDir(stateDir: string, name: string): string;
42
+ /**
43
+ * List all instance namespace directories under the state root.
44
+ *
45
+ * Returns an array of instance names (directory basenames).
46
+ * Only directories are included — stray files at the state root are ignored.
47
+ *
48
+ * @param stateDir - Root state directory.
49
+ * @returns Array of instance names (directory names).
50
+ */
51
+ export declare function listInstanceDirs(stateDir: string): string[];
52
+ /**
53
+ * Save a snapshot file atomically within an instance namespace.
54
+ *
55
+ * Serializes `data` to deterministic YAML and writes it using
56
+ * tmp-file + rename to guarantee atomic writes (Req 23.5).
57
+ *
58
+ * @param stateDir - Root state directory.
59
+ * @param instance - Instance name.
60
+ * @param fileName - File name within the instance directory (e.g., `instance.yaml`).
61
+ * @param data - Data to serialize and persist.
62
+ */
63
+ export declare function saveSnapshot(stateDir: string, instance: string, fileName: string, data: unknown): void;
64
+ /**
65
+ * Load and parse a snapshot file from an instance namespace.
66
+ *
67
+ * Optionally validates the parsed data using a schema validator.
68
+ * Throws `YamlLoadError` on missing files, parse errors, or validation failures
69
+ * — always naming the file in the error message (Req 4.7).
70
+ *
71
+ * @param stateDir - Root state directory.
72
+ * @param instance - Instance name.
73
+ * @param fileName - File name within the instance directory.
74
+ * @param validate - Optional schema validator.
75
+ * @returns The parsed (and validated) data cast to `T`.
76
+ * @throws {YamlLoadError} If the file is missing, corrupt, or fails validation.
77
+ */
78
+ export declare function loadSnapshot<T = unknown>(stateDir: string, instance: string, fileName: string, validate?: SchemaValidator<T>): T;
79
+ //# sourceMappingURL=snapshot-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-store.d.ts","sourceRoot":"","sources":["../../src/state/snapshot-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAiBzD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAKnE;AAMD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvE;AAMD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIxE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB3D;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAItG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EACtC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,GAC5B,CAAC,CA6BH"}