popeye-cli 1.0.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 (209) hide show
  1. package/.env.example +25 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +320 -0
  4. package/dist/adapters/claude.d.ts +82 -0
  5. package/dist/adapters/claude.d.ts.map +1 -0
  6. package/dist/adapters/claude.js +230 -0
  7. package/dist/adapters/claude.js.map +1 -0
  8. package/dist/adapters/openai.d.ts +48 -0
  9. package/dist/adapters/openai.d.ts.map +1 -0
  10. package/dist/adapters/openai.js +257 -0
  11. package/dist/adapters/openai.js.map +1 -0
  12. package/dist/auth/claude.d.ts +44 -0
  13. package/dist/auth/claude.d.ts.map +1 -0
  14. package/dist/auth/claude.js +139 -0
  15. package/dist/auth/claude.js.map +1 -0
  16. package/dist/auth/index.d.ts +61 -0
  17. package/dist/auth/index.d.ts.map +1 -0
  18. package/dist/auth/index.js +141 -0
  19. package/dist/auth/index.js.map +1 -0
  20. package/dist/auth/keychain.d.ts +66 -0
  21. package/dist/auth/keychain.d.ts.map +1 -0
  22. package/dist/auth/keychain.js +125 -0
  23. package/dist/auth/keychain.js.map +1 -0
  24. package/dist/auth/openai-entry.d.ts +9 -0
  25. package/dist/auth/openai-entry.d.ts.map +1 -0
  26. package/dist/auth/openai-entry.js +410 -0
  27. package/dist/auth/openai-entry.js.map +1 -0
  28. package/dist/auth/openai.d.ts +71 -0
  29. package/dist/auth/openai.d.ts.map +1 -0
  30. package/dist/auth/openai.js +212 -0
  31. package/dist/auth/openai.js.map +1 -0
  32. package/dist/auth/server.d.ts +32 -0
  33. package/dist/auth/server.d.ts.map +1 -0
  34. package/dist/auth/server.js +213 -0
  35. package/dist/auth/server.js.map +1 -0
  36. package/dist/cli/commands/auth.d.ts +10 -0
  37. package/dist/cli/commands/auth.d.ts.map +1 -0
  38. package/dist/cli/commands/auth.js +162 -0
  39. package/dist/cli/commands/auth.js.map +1 -0
  40. package/dist/cli/commands/config.d.ts +10 -0
  41. package/dist/cli/commands/config.d.ts.map +1 -0
  42. package/dist/cli/commands/config.js +215 -0
  43. package/dist/cli/commands/config.js.map +1 -0
  44. package/dist/cli/commands/create.d.ts +10 -0
  45. package/dist/cli/commands/create.d.ts.map +1 -0
  46. package/dist/cli/commands/create.js +240 -0
  47. package/dist/cli/commands/create.js.map +1 -0
  48. package/dist/cli/commands/index.d.ts +10 -0
  49. package/dist/cli/commands/index.d.ts.map +1 -0
  50. package/dist/cli/commands/index.js +10 -0
  51. package/dist/cli/commands/index.js.map +1 -0
  52. package/dist/cli/commands/resume.d.ts +18 -0
  53. package/dist/cli/commands/resume.d.ts.map +1 -0
  54. package/dist/cli/commands/resume.js +241 -0
  55. package/dist/cli/commands/resume.js.map +1 -0
  56. package/dist/cli/commands/status.d.ts +18 -0
  57. package/dist/cli/commands/status.d.ts.map +1 -0
  58. package/dist/cli/commands/status.js +154 -0
  59. package/dist/cli/commands/status.js.map +1 -0
  60. package/dist/cli/index.d.ts +17 -0
  61. package/dist/cli/index.d.ts.map +1 -0
  62. package/dist/cli/index.js +71 -0
  63. package/dist/cli/index.js.map +1 -0
  64. package/dist/cli/interactive.d.ts +9 -0
  65. package/dist/cli/interactive.d.ts.map +1 -0
  66. package/dist/cli/interactive.js +330 -0
  67. package/dist/cli/interactive.js.map +1 -0
  68. package/dist/cli/output.d.ts +182 -0
  69. package/dist/cli/output.d.ts.map +1 -0
  70. package/dist/cli/output.js +355 -0
  71. package/dist/cli/output.js.map +1 -0
  72. package/dist/config/defaults.d.ts +57 -0
  73. package/dist/config/defaults.d.ts.map +1 -0
  74. package/dist/config/defaults.js +103 -0
  75. package/dist/config/defaults.js.map +1 -0
  76. package/dist/config/index.d.ts +138 -0
  77. package/dist/config/index.d.ts.map +1 -0
  78. package/dist/config/index.js +244 -0
  79. package/dist/config/index.js.map +1 -0
  80. package/dist/config/schema.d.ts +220 -0
  81. package/dist/config/schema.d.ts.map +1 -0
  82. package/dist/config/schema.js +141 -0
  83. package/dist/config/schema.js.map +1 -0
  84. package/dist/generators/index.d.ts +101 -0
  85. package/dist/generators/index.d.ts.map +1 -0
  86. package/dist/generators/index.js +200 -0
  87. package/dist/generators/index.js.map +1 -0
  88. package/dist/generators/python.d.ts +48 -0
  89. package/dist/generators/python.d.ts.map +1 -0
  90. package/dist/generators/python.js +262 -0
  91. package/dist/generators/python.js.map +1 -0
  92. package/dist/generators/templates/index.d.ts +6 -0
  93. package/dist/generators/templates/index.d.ts.map +1 -0
  94. package/dist/generators/templates/index.js +6 -0
  95. package/dist/generators/templates/index.js.map +1 -0
  96. package/dist/generators/templates/python.d.ts +53 -0
  97. package/dist/generators/templates/python.d.ts.map +1 -0
  98. package/dist/generators/templates/python.js +454 -0
  99. package/dist/generators/templates/python.js.map +1 -0
  100. package/dist/generators/templates/typescript.d.ts +53 -0
  101. package/dist/generators/templates/typescript.d.ts.map +1 -0
  102. package/dist/generators/templates/typescript.js +394 -0
  103. package/dist/generators/templates/typescript.js.map +1 -0
  104. package/dist/generators/typescript.d.ts +64 -0
  105. package/dist/generators/typescript.d.ts.map +1 -0
  106. package/dist/generators/typescript.js +271 -0
  107. package/dist/generators/typescript.js.map +1 -0
  108. package/dist/index.d.ts +7 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +12 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/state/index.d.ts +168 -0
  113. package/dist/state/index.d.ts.map +1 -0
  114. package/dist/state/index.js +338 -0
  115. package/dist/state/index.js.map +1 -0
  116. package/dist/state/persistence.d.ts +91 -0
  117. package/dist/state/persistence.d.ts.map +1 -0
  118. package/dist/state/persistence.js +201 -0
  119. package/dist/state/persistence.js.map +1 -0
  120. package/dist/types/cli.d.ts +132 -0
  121. package/dist/types/cli.d.ts.map +1 -0
  122. package/dist/types/cli.js +17 -0
  123. package/dist/types/cli.js.map +1 -0
  124. package/dist/types/consensus.d.ts +111 -0
  125. package/dist/types/consensus.d.ts.map +1 -0
  126. package/dist/types/consensus.js +29 -0
  127. package/dist/types/consensus.js.map +1 -0
  128. package/dist/types/index.d.ts +9 -0
  129. package/dist/types/index.d.ts.map +1 -0
  130. package/dist/types/index.js +13 -0
  131. package/dist/types/index.js.map +1 -0
  132. package/dist/types/project.d.ts +73 -0
  133. package/dist/types/project.d.ts.map +1 -0
  134. package/dist/types/project.js +55 -0
  135. package/dist/types/project.js.map +1 -0
  136. package/dist/types/workflow.d.ts +236 -0
  137. package/dist/types/workflow.d.ts.map +1 -0
  138. package/dist/types/workflow.js +74 -0
  139. package/dist/types/workflow.js.map +1 -0
  140. package/dist/workflow/consensus.d.ts +89 -0
  141. package/dist/workflow/consensus.d.ts.map +1 -0
  142. package/dist/workflow/consensus.js +220 -0
  143. package/dist/workflow/consensus.js.map +1 -0
  144. package/dist/workflow/execution-mode.d.ts +82 -0
  145. package/dist/workflow/execution-mode.d.ts.map +1 -0
  146. package/dist/workflow/execution-mode.js +346 -0
  147. package/dist/workflow/execution-mode.js.map +1 -0
  148. package/dist/workflow/index.d.ts +110 -0
  149. package/dist/workflow/index.d.ts.map +1 -0
  150. package/dist/workflow/index.js +283 -0
  151. package/dist/workflow/index.js.map +1 -0
  152. package/dist/workflow/plan-mode.d.ts +83 -0
  153. package/dist/workflow/plan-mode.d.ts.map +1 -0
  154. package/dist/workflow/plan-mode.js +241 -0
  155. package/dist/workflow/plan-mode.js.map +1 -0
  156. package/dist/workflow/test-runner.d.ts +87 -0
  157. package/dist/workflow/test-runner.d.ts.map +1 -0
  158. package/dist/workflow/test-runner.js +273 -0
  159. package/dist/workflow/test-runner.js.map +1 -0
  160. package/eslint.config.js +25 -0
  161. package/package.json +66 -0
  162. package/src/adapters/claude.ts +298 -0
  163. package/src/adapters/openai.ts +300 -0
  164. package/src/auth/claude.ts +166 -0
  165. package/src/auth/index.ts +171 -0
  166. package/src/auth/keychain.ts +138 -0
  167. package/src/auth/openai-entry.ts +410 -0
  168. package/src/auth/openai.ts +260 -0
  169. package/src/auth/server.ts +252 -0
  170. package/src/cli/commands/auth.ts +194 -0
  171. package/src/cli/commands/config.ts +241 -0
  172. package/src/cli/commands/create.ts +308 -0
  173. package/src/cli/commands/index.ts +10 -0
  174. package/src/cli/commands/resume.ts +304 -0
  175. package/src/cli/commands/status.ts +189 -0
  176. package/src/cli/index.ts +90 -0
  177. package/src/cli/interactive.ts +418 -0
  178. package/src/cli/output.ts +410 -0
  179. package/src/config/defaults.ts +114 -0
  180. package/src/config/index.ts +315 -0
  181. package/src/config/schema.ts +164 -0
  182. package/src/generators/index.ts +251 -0
  183. package/src/generators/python.ts +318 -0
  184. package/src/generators/templates/index.ts +6 -0
  185. package/src/generators/templates/python.ts +465 -0
  186. package/src/generators/templates/typescript.ts +417 -0
  187. package/src/generators/typescript.ts +340 -0
  188. package/src/index.ts +13 -0
  189. package/src/state/index.ts +454 -0
  190. package/src/state/persistence.ts +230 -0
  191. package/src/types/cli.ts +146 -0
  192. package/src/types/consensus.ts +116 -0
  193. package/src/types/index.ts +64 -0
  194. package/src/types/project.ts +85 -0
  195. package/src/types/workflow.ts +149 -0
  196. package/src/workflow/consensus.ts +299 -0
  197. package/src/workflow/execution-mode.ts +517 -0
  198. package/src/workflow/index.ts +396 -0
  199. package/src/workflow/plan-mode.ts +356 -0
  200. package/src/workflow/test-runner.ts +345 -0
  201. package/tests/adapters/openai.test.ts +145 -0
  202. package/tests/config/config.test.ts +208 -0
  203. package/tests/generators/generators.test.ts +185 -0
  204. package/tests/types/consensus.test.ts +152 -0
  205. package/tests/types/project.test.ts +134 -0
  206. package/tests/workflow/consensus.test.ts +221 -0
  207. package/tests/workflow/test-runner.test.ts +214 -0
  208. package/tsconfig.json +25 -0
  209. package/vitest.config.ts +22 -0
@@ -0,0 +1,338 @@
1
+ /**
2
+ * State management module
3
+ * Provides high-level API for managing project state
4
+ */
5
+ import { v4 as uuidv4 } from 'uuid';
6
+ import { loadState, saveState, stateExists, deleteState, backupState, } from './persistence.js';
7
+ // Re-export persistence utilities
8
+ export * from './persistence.js';
9
+ /**
10
+ * Create a new project state
11
+ *
12
+ * @param spec - The project specification
13
+ * @param projectDir - The project root directory
14
+ * @returns The newly created project state
15
+ */
16
+ export async function createProject(spec, projectDir) {
17
+ // Check if project already exists
18
+ if (await stateExists(projectDir)) {
19
+ throw new Error(`Project already exists at ${projectDir}. Use loadProject() instead.`);
20
+ }
21
+ const now = new Date().toISOString();
22
+ const state = {
23
+ id: uuidv4(),
24
+ name: spec.name || 'untitled-project',
25
+ idea: spec.idea,
26
+ language: spec.language,
27
+ openaiModel: spec.openaiModel,
28
+ phase: 'plan',
29
+ status: 'pending',
30
+ milestones: [],
31
+ currentMilestone: null,
32
+ currentTask: null,
33
+ consensusHistory: [],
34
+ createdAt: now,
35
+ updatedAt: now,
36
+ };
37
+ await saveState(projectDir, state);
38
+ return state;
39
+ }
40
+ /**
41
+ * Load an existing project
42
+ *
43
+ * @param projectDir - The project root directory
44
+ * @returns The project state
45
+ * @throws Error if project doesn't exist
46
+ */
47
+ export async function loadProject(projectDir) {
48
+ const state = await loadState(projectDir);
49
+ if (!state) {
50
+ throw new Error(`No project found at ${projectDir}. Use createProject() first.`);
51
+ }
52
+ return state;
53
+ }
54
+ /**
55
+ * Check if a project exists at the given directory
56
+ *
57
+ * @param projectDir - The project root directory
58
+ * @returns True if project exists
59
+ */
60
+ export async function projectExists(projectDir) {
61
+ return stateExists(projectDir);
62
+ }
63
+ /**
64
+ * Update project state with partial updates
65
+ *
66
+ * @param projectDir - The project root directory
67
+ * @param updates - Partial state updates
68
+ * @returns The updated state
69
+ */
70
+ export async function updateState(projectDir, updates) {
71
+ const current = await loadProject(projectDir);
72
+ const updated = {
73
+ ...current,
74
+ ...updates,
75
+ updatedAt: new Date().toISOString(),
76
+ };
77
+ await saveState(projectDir, updated);
78
+ return updated;
79
+ }
80
+ /**
81
+ * Set the current workflow phase
82
+ *
83
+ * @param projectDir - The project root directory
84
+ * @param phase - The new phase
85
+ * @returns The updated state
86
+ */
87
+ export async function setPhase(projectDir, phase) {
88
+ return updateState(projectDir, { phase });
89
+ }
90
+ /**
91
+ * Add milestones to the project
92
+ *
93
+ * @param projectDir - The project root directory
94
+ * @param milestones - Milestones to add
95
+ * @returns The updated state
96
+ */
97
+ export async function addMilestones(projectDir, milestones) {
98
+ const current = await loadProject(projectDir);
99
+ const newMilestones = milestones.map((m, index) => ({
100
+ ...m,
101
+ id: `milestone-${current.milestones.length + index + 1}`,
102
+ }));
103
+ return updateState(projectDir, {
104
+ milestones: [...current.milestones, ...newMilestones],
105
+ });
106
+ }
107
+ /**
108
+ * Add tasks to a milestone
109
+ *
110
+ * @param projectDir - The project root directory
111
+ * @param milestoneId - The milestone ID
112
+ * @param tasks - Tasks to add
113
+ * @returns The updated state
114
+ */
115
+ export async function addTasks(projectDir, milestoneId, tasks) {
116
+ const current = await loadProject(projectDir);
117
+ const milestoneIndex = current.milestones.findIndex((m) => m.id === milestoneId);
118
+ if (milestoneIndex === -1) {
119
+ throw new Error(`Milestone ${milestoneId} not found`);
120
+ }
121
+ const milestone = current.milestones[milestoneIndex];
122
+ const newTasks = tasks.map((t, index) => ({
123
+ ...t,
124
+ id: `${milestoneId}-task-${milestone.tasks.length + index + 1}`,
125
+ status: 'pending',
126
+ }));
127
+ const updatedMilestones = [...current.milestones];
128
+ updatedMilestones[milestoneIndex] = {
129
+ ...milestone,
130
+ tasks: [...milestone.tasks, ...newTasks],
131
+ };
132
+ return updateState(projectDir, { milestones: updatedMilestones });
133
+ }
134
+ /**
135
+ * Update a task's status
136
+ *
137
+ * @param projectDir - The project root directory
138
+ * @param taskId - The task ID
139
+ * @param status - The new status
140
+ * @param additionalUpdates - Additional task updates
141
+ * @returns The updated state
142
+ */
143
+ export async function updateTaskStatus(projectDir, taskId, status, additionalUpdates = {}) {
144
+ const current = await loadProject(projectDir);
145
+ const updatedMilestones = current.milestones.map((milestone) => ({
146
+ ...milestone,
147
+ tasks: milestone.tasks.map((task) => task.id === taskId ? { ...task, status, ...additionalUpdates } : task),
148
+ }));
149
+ // Update milestone status if all tasks are complete
150
+ const updatedMilestonesWithStatus = updatedMilestones.map((milestone) => {
151
+ const allComplete = milestone.tasks.every((t) => t.status === 'complete');
152
+ const anyInProgress = milestone.tasks.some((t) => t.status === 'in-progress');
153
+ let milestoneStatus = 'pending';
154
+ if (allComplete) {
155
+ milestoneStatus = 'complete';
156
+ }
157
+ else if (anyInProgress || milestone.tasks.some((t) => t.status === 'complete')) {
158
+ milestoneStatus = 'in-progress';
159
+ }
160
+ return { ...milestone, status: milestoneStatus };
161
+ });
162
+ return updateState(projectDir, { milestones: updatedMilestonesWithStatus });
163
+ }
164
+ /**
165
+ * Set the current milestone being worked on
166
+ *
167
+ * @param projectDir - The project root directory
168
+ * @param milestoneId - The milestone ID (or null)
169
+ * @returns The updated state
170
+ */
171
+ export async function setCurrentMilestone(projectDir, milestoneId) {
172
+ if (milestoneId) {
173
+ const current = await loadProject(projectDir);
174
+ const milestone = current.milestones.find((m) => m.id === milestoneId);
175
+ if (!milestone) {
176
+ throw new Error(`Milestone ${milestoneId} not found`);
177
+ }
178
+ }
179
+ return updateState(projectDir, { currentMilestone: milestoneId });
180
+ }
181
+ /**
182
+ * Set the current task being worked on
183
+ *
184
+ * @param projectDir - The project root directory
185
+ * @param taskId - The task ID (or null)
186
+ * @returns The updated state
187
+ */
188
+ export async function setCurrentTask(projectDir, taskId) {
189
+ return updateState(projectDir, { currentTask: taskId });
190
+ }
191
+ /**
192
+ * Record a consensus iteration
193
+ *
194
+ * @param projectDir - The project root directory
195
+ * @param iteration - The consensus iteration to record
196
+ * @returns The updated state
197
+ */
198
+ export async function recordConsensusIteration(projectDir, iteration) {
199
+ const current = await loadProject(projectDir);
200
+ return updateState(projectDir, {
201
+ consensusHistory: [...current.consensusHistory, iteration],
202
+ });
203
+ }
204
+ /**
205
+ * Store the approved plan
206
+ *
207
+ * @param projectDir - The project root directory
208
+ * @param plan - The approved plan content
209
+ * @returns The updated state
210
+ */
211
+ export async function storePlan(projectDir, plan) {
212
+ return updateState(projectDir, { plan });
213
+ }
214
+ /**
215
+ * Store the expanded specification
216
+ *
217
+ * @param projectDir - The project root directory
218
+ * @param specification - The expanded specification
219
+ * @returns The updated state
220
+ */
221
+ export async function storeSpecification(projectDir, specification) {
222
+ return updateState(projectDir, { specification });
223
+ }
224
+ /**
225
+ * Mark the project as complete
226
+ *
227
+ * @param projectDir - The project root directory
228
+ * @returns The updated state
229
+ */
230
+ export async function completeProject(projectDir) {
231
+ return updateState(projectDir, {
232
+ status: 'complete',
233
+ phase: 'complete',
234
+ });
235
+ }
236
+ /**
237
+ * Mark the project as failed
238
+ *
239
+ * @param projectDir - The project root directory
240
+ * @param error - The error message
241
+ * @returns The updated state
242
+ */
243
+ export async function failProject(projectDir, error) {
244
+ return updateState(projectDir, {
245
+ status: 'failed',
246
+ error,
247
+ });
248
+ }
249
+ /**
250
+ * Get project progress summary
251
+ *
252
+ * @param projectDir - The project root directory
253
+ * @returns Progress summary
254
+ */
255
+ export async function getProgress(projectDir) {
256
+ const state = await loadProject(projectDir);
257
+ const totalMilestones = state.milestones.length;
258
+ const completedMilestones = state.milestones.filter((m) => m.status === 'complete').length;
259
+ const allTasks = state.milestones.flatMap((m) => m.tasks);
260
+ const totalTasks = allTasks.length;
261
+ const completedTasks = allTasks.filter((t) => t.status === 'complete').length;
262
+ const percentComplete = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;
263
+ return {
264
+ totalMilestones,
265
+ completedMilestones,
266
+ totalTasks,
267
+ completedTasks,
268
+ percentComplete,
269
+ };
270
+ }
271
+ /**
272
+ * Get the next pending task
273
+ *
274
+ * @param projectDir - The project root directory
275
+ * @returns The next task to work on, or null if none
276
+ */
277
+ export async function getNextTask(projectDir) {
278
+ const state = await loadProject(projectDir);
279
+ for (const milestone of state.milestones) {
280
+ if (milestone.status === 'complete')
281
+ continue;
282
+ const pendingTask = milestone.tasks.find((t) => t.status === 'pending');
283
+ if (pendingTask) {
284
+ return { milestone, task: pendingTask };
285
+ }
286
+ }
287
+ return null;
288
+ }
289
+ /**
290
+ * Reset project to a specific phase
291
+ *
292
+ * @param projectDir - The project root directory
293
+ * @param phase - The phase to reset to
294
+ * @returns The updated state
295
+ */
296
+ export async function resetToPhase(projectDir, phase) {
297
+ // Create backup before reset
298
+ await backupState(projectDir);
299
+ const current = await loadProject(projectDir);
300
+ const updates = {
301
+ phase,
302
+ status: 'pending',
303
+ error: undefined,
304
+ };
305
+ if (phase === 'plan') {
306
+ // Reset everything
307
+ updates.milestones = [];
308
+ updates.currentMilestone = null;
309
+ updates.currentTask = null;
310
+ updates.plan = undefined;
311
+ }
312
+ else if (phase === 'execution') {
313
+ // Reset task progress but keep milestones
314
+ updates.milestones = current.milestones.map((m) => ({
315
+ ...m,
316
+ status: 'pending',
317
+ tasks: m.tasks.map((t) => ({
318
+ ...t,
319
+ status: 'pending',
320
+ testsPassed: undefined,
321
+ error: undefined,
322
+ })),
323
+ }));
324
+ updates.currentMilestone = null;
325
+ updates.currentTask = null;
326
+ }
327
+ return updateState(projectDir, updates);
328
+ }
329
+ /**
330
+ * Delete a project
331
+ *
332
+ * @param projectDir - The project root directory
333
+ * @returns True if project was deleted
334
+ */
335
+ export async function deleteProject(projectDir) {
336
+ return deleteState(projectDir);
337
+ }
338
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * State persistence module
3
+ * Handles atomic read/write operations for project state
4
+ */
5
+ import { type ProjectState } from '../types/workflow.js';
6
+ /**
7
+ * Default state directory name
8
+ */
9
+ export declare const STATE_DIR = ".popeye";
10
+ /**
11
+ * State file name
12
+ */
13
+ export declare const STATE_FILE = "state.json";
14
+ /**
15
+ * Get the state directory path for a project
16
+ *
17
+ * @param projectDir - The project root directory
18
+ * @returns The state directory path
19
+ */
20
+ export declare function getStateDir(projectDir: string): string;
21
+ /**
22
+ * Get the state file path for a project
23
+ *
24
+ * @param projectDir - The project root directory
25
+ * @returns The state file path
26
+ */
27
+ export declare function getStatePath(projectDir: string): string;
28
+ /**
29
+ * Ensure the state directory exists
30
+ *
31
+ * @param projectDir - The project root directory
32
+ */
33
+ export declare function ensureStateDir(projectDir: string): Promise<void>;
34
+ /**
35
+ * Check if a state file exists for a project
36
+ *
37
+ * @param projectDir - The project root directory
38
+ * @returns True if state file exists
39
+ */
40
+ export declare function stateExists(projectDir: string): Promise<boolean>;
41
+ /**
42
+ * Load project state from disk
43
+ *
44
+ * @param projectDir - The project root directory
45
+ * @returns The project state or null if not found
46
+ */
47
+ export declare function loadState(projectDir: string): Promise<ProjectState | null>;
48
+ /**
49
+ * Save project state to disk using atomic write
50
+ * Uses temp file + rename pattern to prevent corruption
51
+ *
52
+ * @param projectDir - The project root directory
53
+ * @param state - The state to save
54
+ */
55
+ export declare function saveState(projectDir: string, state: ProjectState): Promise<void>;
56
+ /**
57
+ * Delete project state
58
+ *
59
+ * @param projectDir - The project root directory
60
+ * @returns True if state was deleted
61
+ */
62
+ export declare function deleteState(projectDir: string): Promise<boolean>;
63
+ /**
64
+ * Create a backup of the current state
65
+ *
66
+ * @param projectDir - The project root directory
67
+ * @returns The backup file path or null if no state exists
68
+ */
69
+ export declare function backupState(projectDir: string): Promise<string | null>;
70
+ /**
71
+ * List all state backups
72
+ *
73
+ * @param projectDir - The project root directory
74
+ * @returns List of backup file paths
75
+ */
76
+ export declare function listBackups(projectDir: string): Promise<string[]>;
77
+ /**
78
+ * Restore state from a backup
79
+ *
80
+ * @param backupPath - Path to the backup file
81
+ * @param projectDir - The project root directory
82
+ */
83
+ export declare function restoreFromBackup(backupPath: string, projectDir: string): Promise<ProjectState>;
84
+ /**
85
+ * Clean up old backups, keeping only the most recent N
86
+ *
87
+ * @param projectDir - The project root directory
88
+ * @param keepCount - Number of backups to keep (default 5)
89
+ */
90
+ export declare function cleanupBackups(projectDir: string, keepCount?: number): Promise<void>;
91
+ //# sourceMappingURL=persistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../src/state/persistence.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE7E;;GAEG;AACH,eAAO,MAAM,SAAS,YAAY,CAAC;AAEnC;;GAEG;AACH,eAAO,MAAM,UAAU,eAAe,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQtE;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAqBhF;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBtF;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWtE;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAe5E;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAavE;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,CAAC,CAWvB;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAU7F"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * State persistence module
3
+ * Handles atomic read/write operations for project state
4
+ */
5
+ import { promises as fs } from 'node:fs';
6
+ import path from 'node:path';
7
+ import { ProjectStateSchema } from '../types/workflow.js';
8
+ /**
9
+ * Default state directory name
10
+ */
11
+ export const STATE_DIR = '.popeye';
12
+ /**
13
+ * State file name
14
+ */
15
+ export const STATE_FILE = 'state.json';
16
+ /**
17
+ * Get the state directory path for a project
18
+ *
19
+ * @param projectDir - The project root directory
20
+ * @returns The state directory path
21
+ */
22
+ export function getStateDir(projectDir) {
23
+ return path.join(projectDir, STATE_DIR);
24
+ }
25
+ /**
26
+ * Get the state file path for a project
27
+ *
28
+ * @param projectDir - The project root directory
29
+ * @returns The state file path
30
+ */
31
+ export function getStatePath(projectDir) {
32
+ return path.join(getStateDir(projectDir), STATE_FILE);
33
+ }
34
+ /**
35
+ * Ensure the state directory exists
36
+ *
37
+ * @param projectDir - The project root directory
38
+ */
39
+ export async function ensureStateDir(projectDir) {
40
+ const stateDir = getStateDir(projectDir);
41
+ await fs.mkdir(stateDir, { recursive: true });
42
+ }
43
+ /**
44
+ * Check if a state file exists for a project
45
+ *
46
+ * @param projectDir - The project root directory
47
+ * @returns True if state file exists
48
+ */
49
+ export async function stateExists(projectDir) {
50
+ try {
51
+ const statePath = getStatePath(projectDir);
52
+ await fs.access(statePath);
53
+ return true;
54
+ }
55
+ catch {
56
+ return false;
57
+ }
58
+ }
59
+ /**
60
+ * Load project state from disk
61
+ *
62
+ * @param projectDir - The project root directory
63
+ * @returns The project state or null if not found
64
+ */
65
+ export async function loadState(projectDir) {
66
+ try {
67
+ const statePath = getStatePath(projectDir);
68
+ const content = await fs.readFile(statePath, 'utf-8');
69
+ const data = JSON.parse(content);
70
+ // Validate with Zod schema
71
+ const result = ProjectStateSchema.safeParse(data);
72
+ if (!result.success) {
73
+ console.error('Invalid state file format:', result.error.message);
74
+ return null;
75
+ }
76
+ return result.data;
77
+ }
78
+ catch (error) {
79
+ if (error.code === 'ENOENT') {
80
+ return null;
81
+ }
82
+ throw error;
83
+ }
84
+ }
85
+ /**
86
+ * Save project state to disk using atomic write
87
+ * Uses temp file + rename pattern to prevent corruption
88
+ *
89
+ * @param projectDir - The project root directory
90
+ * @param state - The state to save
91
+ */
92
+ export async function saveState(projectDir, state) {
93
+ // Ensure state directory exists
94
+ await ensureStateDir(projectDir);
95
+ const statePath = getStatePath(projectDir);
96
+ const tempPath = `${statePath}.tmp.${Date.now()}`;
97
+ // Update the updatedAt timestamp
98
+ const stateToSave = {
99
+ ...state,
100
+ updatedAt: new Date().toISOString(),
101
+ };
102
+ // Validate before saving
103
+ const result = ProjectStateSchema.safeParse(stateToSave);
104
+ if (!result.success) {
105
+ throw new Error(`Invalid state: ${result.error.message}`);
106
+ }
107
+ // Write to temp file
108
+ const content = JSON.stringify(stateToSave, null, 2);
109
+ await fs.writeFile(tempPath, content, 'utf-8');
110
+ // Atomic rename
111
+ await fs.rename(tempPath, statePath);
112
+ }
113
+ /**
114
+ * Delete project state
115
+ *
116
+ * @param projectDir - The project root directory
117
+ * @returns True if state was deleted
118
+ */
119
+ export async function deleteState(projectDir) {
120
+ try {
121
+ const statePath = getStatePath(projectDir);
122
+ await fs.unlink(statePath);
123
+ return true;
124
+ }
125
+ catch (error) {
126
+ if (error.code === 'ENOENT') {
127
+ return false;
128
+ }
129
+ throw error;
130
+ }
131
+ }
132
+ /**
133
+ * Create a backup of the current state
134
+ *
135
+ * @param projectDir - The project root directory
136
+ * @returns The backup file path or null if no state exists
137
+ */
138
+ export async function backupState(projectDir) {
139
+ const state = await loadState(projectDir);
140
+ if (!state) {
141
+ return null;
142
+ }
143
+ const stateDir = getStateDir(projectDir);
144
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
145
+ const backupPath = path.join(stateDir, `state.backup.${timestamp}.json`);
146
+ const content = JSON.stringify(state, null, 2);
147
+ await fs.writeFile(backupPath, content, 'utf-8');
148
+ return backupPath;
149
+ }
150
+ /**
151
+ * List all state backups
152
+ *
153
+ * @param projectDir - The project root directory
154
+ * @returns List of backup file paths
155
+ */
156
+ export async function listBackups(projectDir) {
157
+ try {
158
+ const stateDir = getStateDir(projectDir);
159
+ const files = await fs.readdir(stateDir);
160
+ return files
161
+ .filter((f) => f.startsWith('state.backup.') && f.endsWith('.json'))
162
+ .map((f) => path.join(stateDir, f))
163
+ .sort()
164
+ .reverse(); // Most recent first
165
+ }
166
+ catch {
167
+ return [];
168
+ }
169
+ }
170
+ /**
171
+ * Restore state from a backup
172
+ *
173
+ * @param backupPath - Path to the backup file
174
+ * @param projectDir - The project root directory
175
+ */
176
+ export async function restoreFromBackup(backupPath, projectDir) {
177
+ const content = await fs.readFile(backupPath, 'utf-8');
178
+ const data = JSON.parse(content);
179
+ const result = ProjectStateSchema.safeParse(data);
180
+ if (!result.success) {
181
+ throw new Error(`Invalid backup file: ${result.error.message}`);
182
+ }
183
+ await saveState(projectDir, result.data);
184
+ return result.data;
185
+ }
186
+ /**
187
+ * Clean up old backups, keeping only the most recent N
188
+ *
189
+ * @param projectDir - The project root directory
190
+ * @param keepCount - Number of backups to keep (default 5)
191
+ */
192
+ export async function cleanupBackups(projectDir, keepCount = 5) {
193
+ const backups = await listBackups(projectDir);
194
+ if (backups.length <= keepCount) {
195
+ return;
196
+ }
197
+ // Delete old backups
198
+ const toDelete = backups.slice(keepCount);
199
+ await Promise.all(toDelete.map((backup) => fs.unlink(backup)));
200
+ }
201
+ //# sourceMappingURL=persistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../src/state/persistence.ts"],"names":[],"mappings":"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"}