@mndrk/memx 0.3.2 → 0.3.4

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 (39) hide show
  1. package/README.md +98 -77
  2. package/coverage/clover.xml +1160 -0
  3. package/coverage/coverage-final.json +3 -0
  4. package/coverage/lcov-report/base.css +224 -0
  5. package/coverage/lcov-report/block-navigation.js +87 -0
  6. package/coverage/lcov-report/favicon.png +0 -0
  7. package/coverage/lcov-report/index.html +131 -0
  8. package/coverage/lcov-report/index.js.html +7255 -0
  9. package/coverage/lcov-report/mcp.js.html +1009 -0
  10. package/coverage/lcov-report/prettify.css +1 -0
  11. package/coverage/lcov-report/prettify.js +2 -0
  12. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  13. package/coverage/lcov-report/sorter.js +210 -0
  14. package/coverage/lcov.info +2017 -0
  15. package/index.js +651 -243
  16. package/package.json +24 -2
  17. package/test/additional.test.js +373 -0
  18. package/test/branches.test.js +247 -0
  19. package/test/commands.test.js +663 -0
  20. package/test/context.test.js +185 -0
  21. package/test/coverage.test.js +366 -0
  22. package/test/dispatch.test.js +220 -0
  23. package/test/edge-coverage.test.js +250 -0
  24. package/test/edge.test.js +434 -0
  25. package/test/final-coverage.test.js +316 -0
  26. package/test/final-edges.test.js +199 -0
  27. package/test/init-local.test.js +316 -0
  28. package/test/init.test.js +122 -0
  29. package/test/interactive.test.js +229 -0
  30. package/test/main-dispatch.test.js +164 -0
  31. package/test/main-full.test.js +590 -0
  32. package/test/main.test.js +197 -0
  33. package/test/mcp-server.test.js +320 -0
  34. package/test/mcp.test.js +288 -0
  35. package/test/more.test.js +312 -0
  36. package/test/new.test.js +175 -0
  37. package/test/skill.test.js +247 -0
  38. package/test/tasks-interactive.test.js +243 -0
  39. package/test/utils.test.js +367 -0
@@ -0,0 +1,434 @@
1
+ /**
2
+ * Edge case tests to increase coverage
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+
9
+ // Mock child_process before requiring index.js
10
+ jest.mock('child_process', () => ({
11
+ execSync: jest.fn(),
12
+ spawnSync: jest.fn(() => ({ status: 0, stdout: '', stderr: '' })),
13
+ spawn: jest.fn()
14
+ }));
15
+
16
+ // Mock readline
17
+ jest.mock('readline', () => ({
18
+ createInterface: jest.fn(() => ({
19
+ question: jest.fn((q, cb) => cb('')),
20
+ close: jest.fn(),
21
+ on: jest.fn()
22
+ }))
23
+ }));
24
+
25
+ const {
26
+ cmdStatus,
27
+ cmdGoal,
28
+ cmdNext,
29
+ cmdCheckpoint,
30
+ cmdLearn,
31
+ cmdContext,
32
+ cmdProgress,
33
+ cmdConstraint,
34
+ cmdCriteria,
35
+ writeMemFile,
36
+ readMemFile,
37
+ parseFrontmatter,
38
+ serializeFrontmatter,
39
+ } = require('../index.js');
40
+
41
+ const { spawnSync, execSync } = require('child_process');
42
+
43
+ const testDir = path.join(os.tmpdir(), 'memx-test-edge-' + Date.now());
44
+ let memDir;
45
+
46
+ beforeEach(() => {
47
+ memDir = path.join(testDir, '.mem');
48
+ fs.mkdirSync(memDir, { recursive: true });
49
+ fs.mkdirSync(path.join(memDir, '.git'), { recursive: true });
50
+ spawnSync.mockReset();
51
+ spawnSync.mockReturnValue({ status: 0, stdout: '', stderr: '' });
52
+ execSync.mockClear();
53
+ jest.spyOn(console, 'log').mockImplementation();
54
+ });
55
+
56
+ afterEach(() => {
57
+ if (fs.existsSync(testDir)) {
58
+ fs.rmSync(testDir, { recursive: true });
59
+ }
60
+ jest.restoreAllMocks();
61
+ });
62
+
63
+ describe('cmdStatus edge cases', () => {
64
+ test('shows status with all sections', () => {
65
+ spawnSync.mockReturnValue({ status: 0, stdout: 'task/test', stderr: '' });
66
+
67
+ writeMemFile(memDir, 'goal.md', `---
68
+ task: test
69
+ status: active
70
+ created: 2024-01-01
71
+ ---
72
+
73
+ # Goal
74
+
75
+ Build something amazing
76
+
77
+ ## Definition of Done
78
+
79
+ - [x] First criterion
80
+ - [ ] Second criterion
81
+
82
+ ## Progress: 50%`);
83
+
84
+ writeMemFile(memDir, 'state.md', `---
85
+ status: active
86
+ ---
87
+
88
+ # State
89
+
90
+ ## Next Step
91
+
92
+ Continue building
93
+
94
+ ## Checkpoints
95
+
96
+ - [x] Started
97
+ - [x] First milestone`);
98
+
99
+ const consoleSpy = jest.spyOn(console, 'log');
100
+ cmdStatus(memDir);
101
+
102
+ const output = consoleSpy.mock.calls.map(c => c[0]).join('\n');
103
+ expect(output).toContain('test');
104
+ });
105
+
106
+ test('shows blocked status', () => {
107
+ spawnSync.mockReturnValue({ status: 0, stdout: 'task/blocked-test', stderr: '' });
108
+
109
+ writeMemFile(memDir, 'state.md', `---
110
+ status: blocked
111
+ blocker: Waiting for review
112
+ ---
113
+
114
+ # State`);
115
+
116
+ writeMemFile(memDir, 'goal.md', `---
117
+ task: blocked-test
118
+ ---
119
+
120
+ # Goal
121
+
122
+ Test`);
123
+
124
+ const consoleSpy = jest.spyOn(console, 'log');
125
+ cmdStatus(memDir);
126
+
127
+ const output = consoleSpy.mock.calls.map(c => c[0]).join('\n');
128
+ expect(output).toContain('blocked');
129
+ });
130
+
131
+ test('shows done status', () => {
132
+ spawnSync.mockReturnValue({ status: 0, stdout: 'task/done-test', stderr: '' });
133
+
134
+ writeMemFile(memDir, 'state.md', `---
135
+ status: done
136
+ ---
137
+
138
+ # State`);
139
+
140
+ writeMemFile(memDir, 'goal.md', `---
141
+ task: done-test
142
+ ---
143
+
144
+ # Goal
145
+
146
+ Completed task`);
147
+
148
+ const consoleSpy = jest.spyOn(console, 'log');
149
+ cmdStatus(memDir);
150
+
151
+ expect(consoleSpy).toHaveBeenCalled();
152
+ });
153
+ });
154
+
155
+ describe('cmdGoal edge cases', () => {
156
+ test('updates existing goal text', () => {
157
+ writeMemFile(memDir, 'goal.md', `---
158
+ task: test
159
+ ---
160
+
161
+ # Goal
162
+
163
+ Old goal text
164
+
165
+ ## Definition of Done
166
+
167
+ - [ ] Criterion`);
168
+
169
+ cmdGoal(['New', 'goal', 'text'], memDir);
170
+
171
+ const content = readMemFile(memDir, 'goal.md');
172
+ expect(content).toContain('New goal text');
173
+ });
174
+ });
175
+
176
+ describe('cmdNext edge cases', () => {
177
+ test('updates existing next step', () => {
178
+ writeMemFile(memDir, 'state.md', `---
179
+ status: active
180
+ ---
181
+
182
+ # State
183
+
184
+ ## Next Step
185
+
186
+ Old next step
187
+
188
+ ## Checkpoints
189
+
190
+ - [x] Started`);
191
+
192
+ cmdNext(['New', 'next', 'step'], memDir);
193
+
194
+ const content = readMemFile(memDir, 'state.md');
195
+ expect(content).toContain('New next step');
196
+ });
197
+ });
198
+
199
+ describe('cmdCheckpoint edge cases', () => {
200
+ test('adds checkpoint with existing ones', () => {
201
+ writeMemFile(memDir, 'state.md', `---
202
+ status: active
203
+ ---
204
+
205
+ # State
206
+
207
+ ## Next Step
208
+
209
+ Continue
210
+
211
+ ## Checkpoints
212
+
213
+ - [x] First checkpoint`);
214
+
215
+ cmdCheckpoint(['Second', 'checkpoint'], memDir);
216
+
217
+ const content = readMemFile(memDir, 'state.md');
218
+ expect(content).toContain('Second checkpoint');
219
+ expect(content).toContain('First checkpoint');
220
+ });
221
+ });
222
+
223
+ describe('cmdLearn edge cases', () => {
224
+ test('learns to task memory', () => {
225
+ writeMemFile(memDir, 'memory.md', '# Learnings\n\n');
226
+
227
+ cmdLearn(['Task', 'specific', 'insight'], memDir);
228
+
229
+ const content = readMemFile(memDir, 'memory.md');
230
+ expect(content).toContain('Task specific insight');
231
+ });
232
+
233
+ test('creates memory.md if not exists', () => {
234
+ cmdLearn(['First', 'learning'], memDir);
235
+
236
+ const content = readMemFile(memDir, 'memory.md');
237
+ expect(content).toContain('First learning');
238
+ });
239
+ });
240
+
241
+ describe('cmdContext edge cases', () => {
242
+ test('outputs full context', () => {
243
+ spawnSync.mockReturnValue({ status: 0, stdout: 'task/context-test', stderr: '' });
244
+
245
+ writeMemFile(memDir, 'goal.md', `---
246
+ task: context-test
247
+ created: 2024-01-01
248
+ ---
249
+
250
+ # Goal
251
+
252
+ Test goal for context
253
+
254
+ ## Constraints
255
+
256
+ - Be fast
257
+
258
+ ## Definition of Done
259
+
260
+ - [ ] Done criterion`);
261
+
262
+ writeMemFile(memDir, 'state.md', `---
263
+ status: active
264
+ ---
265
+
266
+ # State
267
+
268
+ ## Next Step
269
+
270
+ Work on it
271
+
272
+ ## Checkpoints
273
+
274
+ - [x] Started`);
275
+
276
+ writeMemFile(memDir, 'memory.md', `# Learnings
277
+
278
+ - 2024-01-01: First learning`);
279
+
280
+ writeMemFile(memDir, 'playbook.md', `# Playbook
281
+
282
+ - Global rule`);
283
+
284
+ const consoleSpy = jest.spyOn(console, 'log');
285
+ cmdContext(memDir);
286
+
287
+ const output = consoleSpy.mock.calls.map(c => c[0]).join('\n');
288
+ expect(output).toContain('context-test');
289
+ expect(output).toContain('Test goal for context');
290
+ expect(output).toContain('First learning');
291
+ expect(output).toContain('Global rule');
292
+ });
293
+ });
294
+
295
+ describe('cmdProgress edge cases', () => {
296
+ test('calculates and updates progress', () => {
297
+ writeMemFile(memDir, 'goal.md', `# Goal
298
+
299
+ Test
300
+
301
+ ## Definition of Done
302
+
303
+ - [x] First
304
+ - [x] Second
305
+ - [ ] Third
306
+ - [ ] Fourth
307
+
308
+ ## Progress: 0%`);
309
+
310
+ cmdProgress([], memDir);
311
+
312
+ const content = readMemFile(memDir, 'goal.md');
313
+ expect(content).toContain('50%');
314
+ });
315
+
316
+ test('shows 100% when all done', () => {
317
+ writeMemFile(memDir, 'goal.md', `# Goal
318
+
319
+ Test
320
+
321
+ ## Definition of Done
322
+
323
+ - [x] First
324
+ - [x] Second
325
+
326
+ ## Progress: 0%`);
327
+
328
+ cmdProgress([], memDir);
329
+
330
+ const content = readMemFile(memDir, 'goal.md');
331
+ expect(content).toContain('100%');
332
+ });
333
+ });
334
+
335
+ describe('cmdConstraint edge cases', () => {
336
+ test('lists multiple constraints', () => {
337
+ writeMemFile(memDir, 'goal.md', `# Goal
338
+
339
+ Test
340
+
341
+ ## Constraints
342
+
343
+ - No breaking changes
344
+ - Test coverage > 80%
345
+ - Performance < 100ms`);
346
+
347
+ const consoleSpy = jest.spyOn(console, 'log');
348
+ cmdConstraint([], memDir);
349
+
350
+ const output = consoleSpy.mock.calls.map(c => c[0]).join('\n');
351
+ expect(output).toContain('No breaking changes');
352
+ expect(output).toContain('Test coverage');
353
+ expect(output).toContain('Performance');
354
+ });
355
+ });
356
+
357
+ describe('cmdCriteria edge cases', () => {
358
+ test('shows criteria when present', () => {
359
+ writeMemFile(memDir, 'goal.md', `# Goal
360
+
361
+ Test
362
+
363
+ ## Definition of Done
364
+
365
+ - [ ] First criterion
366
+ - [x] Second criterion done
367
+ - [ ] Third criterion
368
+
369
+ ## Progress: 33%`);
370
+
371
+ const consoleSpy = jest.spyOn(console, 'log');
372
+ cmdCriteria([], memDir);
373
+
374
+ const output = consoleSpy.mock.calls.map(c => c[0]).join('\n');
375
+ expect(output).toContain('criteria');
376
+ });
377
+ });
378
+
379
+ describe('parseFrontmatter edge cases', () => {
380
+ test('parses frontmatter with dates', () => {
381
+ const content = `---
382
+ task: test
383
+ created: 2024-01-15
384
+ status: active
385
+ ---
386
+
387
+ # Goal
388
+
389
+ Body text`;
390
+
391
+ const result = parseFrontmatter(content);
392
+ expect(result.frontmatter.task).toBe('test');
393
+ expect(result.frontmatter.created).toBe('2024-01-15');
394
+ expect(result.frontmatter.status).toBe('active');
395
+ expect(result.body).toContain('Body text');
396
+ });
397
+
398
+ test('handles frontmatter with boolean-like values', () => {
399
+ const content = `---
400
+ active: true
401
+ archived: false
402
+ ---
403
+
404
+ Content`;
405
+
406
+ const result = parseFrontmatter(content);
407
+ // Simple YAML parser keeps values as strings
408
+ expect(result.frontmatter.active).toBe('true');
409
+ expect(result.frontmatter.archived).toBe('false');
410
+ });
411
+ });
412
+
413
+ describe('serializeFrontmatter edge cases', () => {
414
+ test('serializes frontmatter with date', () => {
415
+ const result = serializeFrontmatter(
416
+ { task: 'test', created: '2024-01-15' },
417
+ '# Goal\n\nBody'
418
+ );
419
+
420
+ expect(result).toContain('task: test');
421
+ expect(result).toContain('created: 2024-01-15');
422
+ expect(result).toContain('# Goal');
423
+ });
424
+
425
+ test('serializes frontmatter with status', () => {
426
+ const result = serializeFrontmatter(
427
+ { status: 'blocked', blocker: 'Waiting for API' },
428
+ '# State'
429
+ );
430
+
431
+ expect(result).toContain('status: blocked');
432
+ expect(result).toContain('blocker: Waiting for API');
433
+ });
434
+ });