oh-my-claude-sisyphus 2.5.0 → 2.6.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 (80) hide show
  1. package/dist/__tests__/hooks.test.js +255 -1
  2. package/dist/__tests__/hooks.test.js.map +1 -1
  3. package/dist/__tests__/installer.test.js +1 -1
  4. package/dist/__tests__/notepad.test.d.ts +2 -0
  5. package/dist/__tests__/notepad.test.d.ts.map +1 -0
  6. package/dist/__tests__/notepad.test.js +374 -0
  7. package/dist/__tests__/notepad.test.js.map +1 -0
  8. package/dist/__tests__/ralph-prd.test.d.ts +2 -0
  9. package/dist/__tests__/ralph-prd.test.d.ts.map +1 -0
  10. package/dist/__tests__/ralph-prd.test.js +308 -0
  11. package/dist/__tests__/ralph-prd.test.js.map +1 -0
  12. package/dist/__tests__/ralph-progress.test.d.ts +2 -0
  13. package/dist/__tests__/ralph-progress.test.d.ts.map +1 -0
  14. package/dist/__tests__/ralph-progress.test.js +312 -0
  15. package/dist/__tests__/ralph-progress.test.js.map +1 -0
  16. package/dist/__tests__/skills.test.js +5 -3
  17. package/dist/__tests__/skills.test.js.map +1 -1
  18. package/dist/agents/definitions.d.ts +4 -0
  19. package/dist/agents/definitions.d.ts.map +1 -1
  20. package/dist/agents/definitions.js +147 -3
  21. package/dist/agents/definitions.js.map +1 -1
  22. package/dist/agents/index.d.ts +1 -0
  23. package/dist/agents/index.d.ts.map +1 -1
  24. package/dist/agents/index.js +2 -0
  25. package/dist/agents/index.js.map +1 -1
  26. package/dist/agents/prometheus.js +2 -2
  27. package/dist/agents/prometheus.js.map +1 -1
  28. package/dist/cli/index.js +0 -0
  29. package/dist/features/builtin-skills/skills.d.ts.map +1 -1
  30. package/dist/features/builtin-skills/skills.js +61 -0
  31. package/dist/features/builtin-skills/skills.js.map +1 -1
  32. package/dist/features/magic-keywords.js +1 -1
  33. package/dist/hooks/index.d.ts +5 -1
  34. package/dist/hooks/index.d.ts.map +1 -1
  35. package/dist/hooks/index.js +15 -1
  36. package/dist/hooks/index.js.map +1 -1
  37. package/dist/hooks/notepad/index.d.ts +114 -0
  38. package/dist/hooks/notepad/index.d.ts.map +1 -0
  39. package/dist/hooks/notepad/index.js +372 -0
  40. package/dist/hooks/notepad/index.js.map +1 -0
  41. package/dist/hooks/persistent-mode/index.d.ts +5 -0
  42. package/dist/hooks/persistent-mode/index.d.ts.map +1 -1
  43. package/dist/hooks/persistent-mode/index.js +71 -5
  44. package/dist/hooks/persistent-mode/index.js.map +1 -1
  45. package/dist/hooks/ralph-loop/index.d.ts +48 -0
  46. package/dist/hooks/ralph-loop/index.d.ts.map +1 -1
  47. package/dist/hooks/ralph-loop/index.js +127 -0
  48. package/dist/hooks/ralph-loop/index.js.map +1 -1
  49. package/dist/hooks/ralph-prd/index.d.ts +130 -0
  50. package/dist/hooks/ralph-prd/index.d.ts.map +1 -0
  51. package/dist/hooks/ralph-prd/index.js +310 -0
  52. package/dist/hooks/ralph-prd/index.js.map +1 -0
  53. package/dist/hooks/ralph-progress/index.d.ts +102 -0
  54. package/dist/hooks/ralph-progress/index.d.ts.map +1 -0
  55. package/dist/hooks/ralph-progress/index.js +408 -0
  56. package/dist/hooks/ralph-progress/index.js.map +1 -0
  57. package/dist/hooks/sisyphus-orchestrator/index.d.ts.map +1 -1
  58. package/dist/hooks/sisyphus-orchestrator/index.js +26 -0
  59. package/dist/hooks/sisyphus-orchestrator/index.js.map +1 -1
  60. package/dist/hooks/ultraqa-loop/index.d.ts +94 -0
  61. package/dist/hooks/ultraqa-loop/index.d.ts.map +1 -0
  62. package/dist/hooks/ultraqa-loop/index.js +216 -0
  63. package/dist/hooks/ultraqa-loop/index.js.map +1 -0
  64. package/dist/installer/hooks.d.ts +28 -0
  65. package/dist/installer/hooks.d.ts.map +1 -1
  66. package/dist/installer/hooks.js +262 -2
  67. package/dist/installer/hooks.js.map +1 -1
  68. package/dist/installer/index.d.ts +1 -1
  69. package/dist/installer/index.d.ts.map +1 -1
  70. package/dist/installer/index.js +426 -12
  71. package/dist/installer/index.js.map +1 -1
  72. package/package.json +1 -1
  73. package/scripts/persistent-mode.mjs +167 -6
  74. package/scripts/post-tool-verifier.mjs +62 -1
  75. package/scripts/session-start.mjs +22 -0
  76. package/scripts/test-max-attempts.ts +94 -0
  77. package/scripts/test-mutual-exclusion.ts +152 -0
  78. package/scripts/test-notepad-integration.ts +495 -0
  79. package/scripts/test-remember-tags.ts +121 -0
  80. package/scripts/test-session-injection.ts +41 -0
@@ -0,0 +1,308 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { readPrd, writePrd, findPrdPath, getPrdStatus, markStoryComplete, markStoryIncomplete, getStory, getNextStory, createPrd, createSimplePrd, initPrd, formatPrdStatus, formatStory, PRD_FILENAME } from '../hooks/ralph-prd/index.js';
6
+ describe('Ralph PRD Module', () => {
7
+ let testDir;
8
+ beforeEach(() => {
9
+ // Create a unique temp directory for each test
10
+ testDir = join(tmpdir(), `ralph-prd-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
11
+ mkdirSync(testDir, { recursive: true });
12
+ });
13
+ afterEach(() => {
14
+ // Clean up test directory
15
+ if (existsSync(testDir)) {
16
+ rmSync(testDir, { recursive: true, force: true });
17
+ }
18
+ });
19
+ describe('findPrdPath', () => {
20
+ it('should return null when no prd.json exists', () => {
21
+ expect(findPrdPath(testDir)).toBeNull();
22
+ });
23
+ it('should find prd.json in root directory', () => {
24
+ const prdPath = join(testDir, PRD_FILENAME);
25
+ writeFileSync(prdPath, '{}');
26
+ expect(findPrdPath(testDir)).toBe(prdPath);
27
+ });
28
+ it('should find prd.json in .sisyphus directory', () => {
29
+ const sisyphusDir = join(testDir, '.sisyphus');
30
+ mkdirSync(sisyphusDir, { recursive: true });
31
+ const prdPath = join(sisyphusDir, PRD_FILENAME);
32
+ writeFileSync(prdPath, '{}');
33
+ expect(findPrdPath(testDir)).toBe(prdPath);
34
+ });
35
+ it('should prefer root over .sisyphus', () => {
36
+ const rootPath = join(testDir, PRD_FILENAME);
37
+ const sisyphusDir = join(testDir, '.sisyphus');
38
+ mkdirSync(sisyphusDir, { recursive: true });
39
+ const sisyphusPath = join(sisyphusDir, PRD_FILENAME);
40
+ writeFileSync(rootPath, '{"source": "root"}');
41
+ writeFileSync(sisyphusPath, '{"source": "sisyphus"}');
42
+ expect(findPrdPath(testDir)).toBe(rootPath);
43
+ });
44
+ });
45
+ describe('readPrd / writePrd', () => {
46
+ const samplePrd = {
47
+ project: 'TestProject',
48
+ branchName: 'ralph/test-feature',
49
+ description: 'Test feature description',
50
+ userStories: [
51
+ {
52
+ id: 'US-001',
53
+ title: 'First story',
54
+ description: 'As a user, I want to test',
55
+ acceptanceCriteria: ['Criterion 1', 'Criterion 2'],
56
+ priority: 1,
57
+ passes: false
58
+ },
59
+ {
60
+ id: 'US-002',
61
+ title: 'Second story',
62
+ description: 'As a user, I want more tests',
63
+ acceptanceCriteria: ['Criterion A'],
64
+ priority: 2,
65
+ passes: true
66
+ }
67
+ ]
68
+ };
69
+ it('should return null when reading non-existent prd', () => {
70
+ expect(readPrd(testDir)).toBeNull();
71
+ });
72
+ it('should write and read prd correctly', () => {
73
+ expect(writePrd(testDir, samplePrd)).toBe(true);
74
+ const read = readPrd(testDir);
75
+ expect(read).toEqual(samplePrd);
76
+ });
77
+ it('should create .sisyphus directory when writing', () => {
78
+ writePrd(testDir, samplePrd);
79
+ expect(existsSync(join(testDir, '.sisyphus'))).toBe(true);
80
+ });
81
+ it('should return null for malformed JSON', () => {
82
+ const prdPath = join(testDir, PRD_FILENAME);
83
+ writeFileSync(prdPath, 'not valid json');
84
+ expect(readPrd(testDir)).toBeNull();
85
+ });
86
+ it('should return null for missing userStories', () => {
87
+ const prdPath = join(testDir, PRD_FILENAME);
88
+ writeFileSync(prdPath, JSON.stringify({ project: 'Test' }));
89
+ expect(readPrd(testDir)).toBeNull();
90
+ });
91
+ });
92
+ describe('getPrdStatus', () => {
93
+ it('should correctly calculate status for mixed completion', () => {
94
+ const prd = {
95
+ project: 'Test',
96
+ branchName: 'test',
97
+ description: 'Test',
98
+ userStories: [
99
+ { id: 'US-001', title: 'A', description: '', acceptanceCriteria: [], priority: 1, passes: true },
100
+ { id: 'US-002', title: 'B', description: '', acceptanceCriteria: [], priority: 2, passes: false },
101
+ { id: 'US-003', title: 'C', description: '', acceptanceCriteria: [], priority: 3, passes: false }
102
+ ]
103
+ };
104
+ const status = getPrdStatus(prd);
105
+ expect(status.total).toBe(3);
106
+ expect(status.completed).toBe(1);
107
+ expect(status.pending).toBe(2);
108
+ expect(status.allComplete).toBe(false);
109
+ expect(status.nextStory?.id).toBe('US-002');
110
+ expect(status.incompleteIds).toEqual(['US-002', 'US-003']);
111
+ });
112
+ it('should return allComplete true when all stories pass', () => {
113
+ const prd = {
114
+ project: 'Test',
115
+ branchName: 'test',
116
+ description: 'Test',
117
+ userStories: [
118
+ { id: 'US-001', title: 'A', description: '', acceptanceCriteria: [], priority: 1, passes: true },
119
+ { id: 'US-002', title: 'B', description: '', acceptanceCriteria: [], priority: 2, passes: true }
120
+ ]
121
+ };
122
+ const status = getPrdStatus(prd);
123
+ expect(status.allComplete).toBe(true);
124
+ expect(status.nextStory).toBeNull();
125
+ expect(status.incompleteIds).toEqual([]);
126
+ });
127
+ it('should sort pending stories by priority', () => {
128
+ const prd = {
129
+ project: 'Test',
130
+ branchName: 'test',
131
+ description: 'Test',
132
+ userStories: [
133
+ { id: 'US-001', title: 'Low', description: '', acceptanceCriteria: [], priority: 3, passes: false },
134
+ { id: 'US-002', title: 'High', description: '', acceptanceCriteria: [], priority: 1, passes: false },
135
+ { id: 'US-003', title: 'Med', description: '', acceptanceCriteria: [], priority: 2, passes: false }
136
+ ]
137
+ };
138
+ const status = getPrdStatus(prd);
139
+ expect(status.nextStory?.id).toBe('US-002'); // Highest priority (1)
140
+ });
141
+ it('should handle empty stories array', () => {
142
+ const prd = {
143
+ project: 'Test',
144
+ branchName: 'test',
145
+ description: 'Test',
146
+ userStories: []
147
+ };
148
+ const status = getPrdStatus(prd);
149
+ expect(status.total).toBe(0);
150
+ expect(status.allComplete).toBe(true);
151
+ expect(status.nextStory).toBeNull();
152
+ });
153
+ });
154
+ describe('markStoryComplete / markStoryIncomplete', () => {
155
+ beforeEach(() => {
156
+ const prd = {
157
+ project: 'Test',
158
+ branchName: 'test',
159
+ description: 'Test',
160
+ userStories: [
161
+ { id: 'US-001', title: 'A', description: '', acceptanceCriteria: [], priority: 1, passes: false }
162
+ ]
163
+ };
164
+ writePrd(testDir, prd);
165
+ });
166
+ it('should mark story as complete', () => {
167
+ expect(markStoryComplete(testDir, 'US-001', 'Done!')).toBe(true);
168
+ const prd = readPrd(testDir);
169
+ expect(prd?.userStories[0].passes).toBe(true);
170
+ expect(prd?.userStories[0].notes).toBe('Done!');
171
+ });
172
+ it('should mark story as incomplete', () => {
173
+ markStoryComplete(testDir, 'US-001');
174
+ expect(markStoryIncomplete(testDir, 'US-001', 'Needs rework')).toBe(true);
175
+ const prd = readPrd(testDir);
176
+ expect(prd?.userStories[0].passes).toBe(false);
177
+ expect(prd?.userStories[0].notes).toBe('Needs rework');
178
+ });
179
+ it('should return false for non-existent story', () => {
180
+ expect(markStoryComplete(testDir, 'US-999')).toBe(false);
181
+ });
182
+ it('should return false when no prd exists', () => {
183
+ rmSync(join(testDir, '.sisyphus'), { recursive: true, force: true });
184
+ expect(markStoryComplete(testDir, 'US-001')).toBe(false);
185
+ });
186
+ });
187
+ describe('getStory / getNextStory', () => {
188
+ beforeEach(() => {
189
+ const prd = {
190
+ project: 'Test',
191
+ branchName: 'test',
192
+ description: 'Test',
193
+ userStories: [
194
+ { id: 'US-001', title: 'First', description: '', acceptanceCriteria: [], priority: 1, passes: true },
195
+ { id: 'US-002', title: 'Second', description: '', acceptanceCriteria: [], priority: 2, passes: false }
196
+ ]
197
+ };
198
+ writePrd(testDir, prd);
199
+ });
200
+ it('should get story by ID', () => {
201
+ const story = getStory(testDir, 'US-001');
202
+ expect(story?.title).toBe('First');
203
+ });
204
+ it('should return null for non-existent story', () => {
205
+ expect(getStory(testDir, 'US-999')).toBeNull();
206
+ });
207
+ it('should get next incomplete story', () => {
208
+ const story = getNextStory(testDir);
209
+ expect(story?.id).toBe('US-002');
210
+ });
211
+ });
212
+ describe('createPrd / createSimplePrd', () => {
213
+ it('should create PRD with auto-assigned priorities', () => {
214
+ const prd = createPrd('Project', 'branch', 'Description', [
215
+ { id: 'US-001', title: 'A', description: '', acceptanceCriteria: [] },
216
+ { id: 'US-002', title: 'B', description: '', acceptanceCriteria: [] }
217
+ ]);
218
+ expect(prd.userStories[0].priority).toBe(1);
219
+ expect(prd.userStories[1].priority).toBe(2);
220
+ expect(prd.userStories[0].passes).toBe(false);
221
+ expect(prd.userStories[1].passes).toBe(false);
222
+ });
223
+ it('should respect provided priorities', () => {
224
+ const prd = createPrd('Project', 'branch', 'Description', [
225
+ { id: 'US-001', title: 'A', description: '', acceptanceCriteria: [], priority: 10 },
226
+ { id: 'US-002', title: 'B', description: '', acceptanceCriteria: [] }
227
+ ]);
228
+ expect(prd.userStories[0].priority).toBe(10);
229
+ expect(prd.userStories[1].priority).toBe(2); // Auto-assigned
230
+ });
231
+ it('should create simple PRD with single story', () => {
232
+ const prd = createSimplePrd('Project', 'branch', 'Implement feature X');
233
+ expect(prd.userStories.length).toBe(1);
234
+ expect(prd.userStories[0].id).toBe('US-001');
235
+ expect(prd.userStories[0].description).toBe('Implement feature X');
236
+ expect(prd.userStories[0].acceptanceCriteria.length).toBeGreaterThan(0);
237
+ });
238
+ it('should truncate long titles in simple PRD', () => {
239
+ const longTask = 'A'.repeat(100);
240
+ const prd = createSimplePrd('Project', 'branch', longTask);
241
+ expect(prd.userStories[0].title.length).toBeLessThanOrEqual(53); // 50 + "..."
242
+ expect(prd.userStories[0].title.endsWith('...')).toBe(true);
243
+ });
244
+ });
245
+ describe('initPrd', () => {
246
+ it('should initialize PRD in directory', () => {
247
+ expect(initPrd(testDir, 'Project', 'branch', 'Description')).toBe(true);
248
+ const prd = readPrd(testDir);
249
+ expect(prd?.project).toBe('Project');
250
+ expect(prd?.userStories.length).toBe(1);
251
+ });
252
+ it('should initialize PRD with custom stories', () => {
253
+ const stories = [
254
+ { id: 'US-001', title: 'A', description: '', acceptanceCriteria: [] },
255
+ { id: 'US-002', title: 'B', description: '', acceptanceCriteria: [] }
256
+ ];
257
+ expect(initPrd(testDir, 'Project', 'branch', 'Description', stories)).toBe(true);
258
+ const prd = readPrd(testDir);
259
+ expect(prd?.userStories.length).toBe(2);
260
+ });
261
+ });
262
+ describe('formatPrdStatus / formatStory', () => {
263
+ it('should format status correctly', () => {
264
+ const status = {
265
+ total: 3,
266
+ completed: 1,
267
+ pending: 2,
268
+ allComplete: false,
269
+ nextStory: { id: 'US-002', title: 'Next', description: '', acceptanceCriteria: [], priority: 2, passes: false },
270
+ incompleteIds: ['US-002', 'US-003']
271
+ };
272
+ const formatted = formatPrdStatus(status);
273
+ expect(formatted).toContain('1/3');
274
+ expect(formatted).toContain('US-002');
275
+ expect(formatted).toContain('US-003');
276
+ });
277
+ it('should format complete status', () => {
278
+ const status = {
279
+ total: 2,
280
+ completed: 2,
281
+ pending: 0,
282
+ allComplete: true,
283
+ nextStory: null,
284
+ incompleteIds: []
285
+ };
286
+ const formatted = formatPrdStatus(status);
287
+ expect(formatted).toContain('COMPLETE');
288
+ });
289
+ it('should format story correctly', () => {
290
+ const story = {
291
+ id: 'US-001',
292
+ title: 'Test Story',
293
+ description: 'As a user, I want to test',
294
+ acceptanceCriteria: ['Criterion 1', 'Criterion 2'],
295
+ priority: 1,
296
+ passes: false,
297
+ notes: 'Some notes'
298
+ };
299
+ const formatted = formatStory(story);
300
+ expect(formatted).toContain('US-001');
301
+ expect(formatted).toContain('Test Story');
302
+ expect(formatted).toContain('PENDING');
303
+ expect(formatted).toContain('Criterion 1');
304
+ expect(formatted).toContain('Some notes');
305
+ });
306
+ });
307
+ });
308
+ //# sourceMappingURL=ralph-prd.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ralph-prd.test.js","sourceRoot":"","sources":["../../src/__tests__/ralph-prd.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ralph-progress.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ralph-progress.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ralph-progress.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,312 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { readProgress, readProgressRaw, parseProgress, initProgress, appendProgress, addPattern, getPatterns, getRecentLearnings, formatPatternsForContext, formatProgressForContext, getProgressContext, PROGRESS_FILENAME, PATTERNS_HEADER, ENTRY_SEPARATOR } from '../hooks/ralph-progress/index.js';
6
+ describe('Ralph Progress Module', () => {
7
+ let testDir;
8
+ beforeEach(() => {
9
+ // Create a unique temp directory for each test
10
+ testDir = join(tmpdir(), `ralph-progress-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
11
+ mkdirSync(testDir, { recursive: true });
12
+ });
13
+ afterEach(() => {
14
+ // Clean up test directory
15
+ if (existsSync(testDir)) {
16
+ rmSync(testDir, { recursive: true, force: true });
17
+ }
18
+ });
19
+ describe('initProgress', () => {
20
+ it('should create progress.txt in .sisyphus directory', () => {
21
+ expect(initProgress(testDir)).toBe(true);
22
+ expect(existsSync(join(testDir, '.sisyphus', PROGRESS_FILENAME))).toBe(true);
23
+ });
24
+ it('should include started timestamp', () => {
25
+ initProgress(testDir);
26
+ const content = readProgressRaw(testDir);
27
+ expect(content).toContain('Started:');
28
+ });
29
+ it('should include patterns header', () => {
30
+ initProgress(testDir);
31
+ const content = readProgressRaw(testDir);
32
+ expect(content).toContain(PATTERNS_HEADER);
33
+ });
34
+ it('should include entry separator', () => {
35
+ initProgress(testDir);
36
+ const content = readProgressRaw(testDir);
37
+ expect(content).toContain(ENTRY_SEPARATOR);
38
+ });
39
+ });
40
+ describe('readProgressRaw / readProgress', () => {
41
+ it('should return null when no progress file exists', () => {
42
+ expect(readProgressRaw(testDir)).toBeNull();
43
+ expect(readProgress(testDir)).toBeNull();
44
+ });
45
+ it('should read progress from root directory', () => {
46
+ writeFileSync(join(testDir, PROGRESS_FILENAME), '# Test');
47
+ expect(readProgressRaw(testDir)).toBe('# Test');
48
+ });
49
+ it('should read progress from .sisyphus directory', () => {
50
+ const sisyphusDir = join(testDir, '.sisyphus');
51
+ mkdirSync(sisyphusDir, { recursive: true });
52
+ writeFileSync(join(sisyphusDir, PROGRESS_FILENAME), '# Test');
53
+ expect(readProgressRaw(testDir)).toBe('# Test');
54
+ });
55
+ });
56
+ describe('parseProgress', () => {
57
+ it('should parse patterns from progress file', () => {
58
+ const content = `# Progress Log
59
+ Started: 2025-01-01
60
+
61
+ ${PATTERNS_HEADER}
62
+ - Pattern one
63
+ - Pattern two
64
+
65
+ ${ENTRY_SEPARATOR}
66
+ `;
67
+ const parsed = parseProgress(content);
68
+ expect(parsed.patterns.length).toBe(2);
69
+ expect(parsed.patterns[0].pattern).toBe('Pattern one');
70
+ expect(parsed.patterns[1].pattern).toBe('Pattern two');
71
+ });
72
+ it('should parse started timestamp', () => {
73
+ const content = `# Progress Log
74
+ Started: 2025-01-01T10:00:00Z
75
+
76
+ ${PATTERNS_HEADER}
77
+ ${ENTRY_SEPARATOR}
78
+ `;
79
+ const parsed = parseProgress(content);
80
+ expect(parsed.startedAt).toBe('2025-01-01T10:00:00Z');
81
+ });
82
+ it('should parse entries', () => {
83
+ const content = `# Progress Log
84
+ Started: 2025-01-01
85
+
86
+ ${PATTERNS_HEADER}
87
+ ${ENTRY_SEPARATOR}
88
+
89
+ ## [2025-01-01 10:00] - US-001
90
+ - Implemented feature A
91
+ - Fixed bug B
92
+ - **Learnings:**
93
+ - Use pattern X for Y
94
+
95
+ ${ENTRY_SEPARATOR}
96
+ `;
97
+ const parsed = parseProgress(content);
98
+ expect(parsed.entries.length).toBe(1);
99
+ expect(parsed.entries[0].storyId).toBe('US-001');
100
+ expect(parsed.entries[0].implementation).toContain('Implemented feature A');
101
+ expect(parsed.entries[0].learnings).toContain('Use pattern X for Y');
102
+ });
103
+ it('should handle multiple entries', () => {
104
+ const content = `# Progress Log
105
+ Started: 2025-01-01
106
+
107
+ ${PATTERNS_HEADER}
108
+ ${ENTRY_SEPARATOR}
109
+
110
+ ## [2025-01-01 10:00] - US-001
111
+ - First implementation
112
+
113
+ ${ENTRY_SEPARATOR}
114
+
115
+ ## [2025-01-01 11:00] - US-002
116
+ - Second implementation
117
+
118
+ ${ENTRY_SEPARATOR}
119
+ `;
120
+ const parsed = parseProgress(content);
121
+ expect(parsed.entries.length).toBe(2);
122
+ expect(parsed.entries[0].storyId).toBe('US-001');
123
+ expect(parsed.entries[1].storyId).toBe('US-002');
124
+ });
125
+ it('should handle empty content', () => {
126
+ const parsed = parseProgress('');
127
+ expect(parsed.patterns).toEqual([]);
128
+ expect(parsed.entries).toEqual([]);
129
+ expect(parsed.startedAt).toBe('');
130
+ });
131
+ it('should handle malformed content gracefully', () => {
132
+ const content = `Random text
133
+ No structure here
134
+ Just garbage`;
135
+ const parsed = parseProgress(content);
136
+ expect(parsed.patterns).toEqual([]);
137
+ expect(parsed.entries).toEqual([]);
138
+ });
139
+ });
140
+ describe('appendProgress', () => {
141
+ beforeEach(() => {
142
+ initProgress(testDir);
143
+ });
144
+ it('should append progress entry', () => {
145
+ const result = appendProgress(testDir, {
146
+ storyId: 'US-001',
147
+ implementation: ['Did thing A', 'Did thing B'],
148
+ filesChanged: ['file1.ts', 'file2.ts'],
149
+ learnings: ['Learned pattern X']
150
+ });
151
+ expect(result).toBe(true);
152
+ const content = readProgressRaw(testDir);
153
+ expect(content).toContain('US-001');
154
+ expect(content).toContain('Did thing A');
155
+ expect(content).toContain('file1.ts');
156
+ expect(content).toContain('Learned pattern X');
157
+ });
158
+ it('should create progress file if not exists', () => {
159
+ rmSync(join(testDir, '.sisyphus'), { recursive: true, force: true });
160
+ const result = appendProgress(testDir, {
161
+ storyId: 'US-001',
162
+ implementation: ['Test'],
163
+ filesChanged: [],
164
+ learnings: []
165
+ });
166
+ expect(result).toBe(true);
167
+ expect(existsSync(join(testDir, '.sisyphus', PROGRESS_FILENAME))).toBe(true);
168
+ });
169
+ it('should include timestamp', () => {
170
+ appendProgress(testDir, {
171
+ storyId: 'US-001',
172
+ implementation: ['Test'],
173
+ filesChanged: [],
174
+ learnings: []
175
+ });
176
+ const content = readProgressRaw(testDir);
177
+ // Should have a date pattern like [2025-01-18 12:00]
178
+ expect(content).toMatch(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}\]/);
179
+ });
180
+ });
181
+ describe('addPattern', () => {
182
+ beforeEach(() => {
183
+ initProgress(testDir);
184
+ });
185
+ it('should add pattern to progress file', () => {
186
+ const result = addPattern(testDir, 'Use X for Y');
187
+ expect(result).toBe(true);
188
+ const patterns = getPatterns(testDir);
189
+ expect(patterns).toContain('Use X for Y');
190
+ });
191
+ it('should remove placeholder when adding first pattern', () => {
192
+ const result = addPattern(testDir, 'First pattern');
193
+ expect(result).toBe(true);
194
+ const content = readProgressRaw(testDir);
195
+ expect(content).not.toContain('No patterns discovered yet');
196
+ });
197
+ it('should handle multiple patterns', () => {
198
+ addPattern(testDir, 'Pattern 1');
199
+ addPattern(testDir, 'Pattern 2');
200
+ addPattern(testDir, 'Pattern 3');
201
+ const patterns = getPatterns(testDir);
202
+ expect(patterns.length).toBe(3);
203
+ });
204
+ it('should create progress file if not exists', () => {
205
+ rmSync(join(testDir, '.sisyphus'), { recursive: true, force: true });
206
+ const result = addPattern(testDir, 'New pattern');
207
+ expect(result).toBe(true);
208
+ expect(existsSync(join(testDir, '.sisyphus', PROGRESS_FILENAME))).toBe(true);
209
+ });
210
+ it('should recover when directory is deleted', () => {
211
+ // Remove directory completely - the function should recover
212
+ rmSync(testDir, { recursive: true, force: true });
213
+ // With recursive: true in mkdirSync, it should recreate and succeed
214
+ const result = addPattern(testDir, 'Pattern');
215
+ expect(result).toBe(true);
216
+ // Verify the pattern was actually added
217
+ const patterns = getPatterns(testDir);
218
+ expect(patterns).toContain('Pattern');
219
+ });
220
+ });
221
+ describe('getPatterns / getRecentLearnings', () => {
222
+ beforeEach(() => {
223
+ initProgress(testDir);
224
+ addPattern(testDir, 'Pattern A');
225
+ addPattern(testDir, 'Pattern B');
226
+ appendProgress(testDir, {
227
+ storyId: 'US-001',
228
+ implementation: ['Test'],
229
+ filesChanged: [],
230
+ learnings: ['Learning 1', 'Learning 2']
231
+ });
232
+ appendProgress(testDir, {
233
+ storyId: 'US-002',
234
+ implementation: ['Test'],
235
+ filesChanged: [],
236
+ learnings: ['Learning 3']
237
+ });
238
+ });
239
+ it('should get all patterns', () => {
240
+ const patterns = getPatterns(testDir);
241
+ expect(patterns).toContain('Pattern A');
242
+ expect(patterns).toContain('Pattern B');
243
+ });
244
+ it('should get recent learnings', () => {
245
+ const learnings = getRecentLearnings(testDir, 5);
246
+ expect(learnings).toContain('Learning 1');
247
+ expect(learnings).toContain('Learning 2');
248
+ expect(learnings).toContain('Learning 3');
249
+ });
250
+ it('should limit learnings', () => {
251
+ const learnings = getRecentLearnings(testDir, 1);
252
+ // Should only get learnings from the last entry
253
+ expect(learnings).toContain('Learning 3');
254
+ expect(learnings).not.toContain('Learning 1');
255
+ });
256
+ });
257
+ describe('formatPatternsForContext / formatProgressForContext', () => {
258
+ beforeEach(() => {
259
+ initProgress(testDir);
260
+ addPattern(testDir, 'Use X for Y');
261
+ appendProgress(testDir, {
262
+ storyId: 'US-001',
263
+ implementation: ['Did something'],
264
+ filesChanged: [],
265
+ learnings: ['Important learning']
266
+ });
267
+ });
268
+ it('should format patterns with tags', () => {
269
+ const formatted = formatPatternsForContext(testDir);
270
+ expect(formatted).toContain('<codebase-patterns>');
271
+ expect(formatted).toContain('</codebase-patterns>');
272
+ expect(formatted).toContain('Use X for Y');
273
+ });
274
+ it('should return empty string when no patterns', () => {
275
+ rmSync(join(testDir, '.sisyphus'), { recursive: true, force: true });
276
+ const formatted = formatPatternsForContext(testDir);
277
+ expect(formatted).toBe('');
278
+ });
279
+ it('should format progress with tags', () => {
280
+ const formatted = formatProgressForContext(testDir, 5);
281
+ expect(formatted).toContain('<recent-progress>');
282
+ expect(formatted).toContain('</recent-progress>');
283
+ expect(formatted).toContain('US-001');
284
+ });
285
+ it('should return empty string when no progress', () => {
286
+ rmSync(join(testDir, '.sisyphus'), { recursive: true, force: true });
287
+ const formatted = formatProgressForContext(testDir);
288
+ expect(formatted).toBe('');
289
+ });
290
+ });
291
+ describe('getProgressContext', () => {
292
+ it('should return combined context', () => {
293
+ initProgress(testDir);
294
+ addPattern(testDir, 'Pattern');
295
+ appendProgress(testDir, {
296
+ storyId: 'US-001',
297
+ implementation: ['Test'],
298
+ filesChanged: [],
299
+ learnings: ['Learning']
300
+ });
301
+ const context = getProgressContext(testDir);
302
+ expect(context).toContain('<codebase-patterns>');
303
+ expect(context).toContain('<learnings>');
304
+ expect(context).toContain('<recent-progress>');
305
+ });
306
+ it('should return empty string when no progress', () => {
307
+ const context = getProgressContext(testDir);
308
+ expect(context).toBe('');
309
+ });
310
+ });
311
+ });
312
+ //# sourceMappingURL=ralph-progress.test.js.map