oh-my-claude-sisyphus 3.7.15 → 3.8.1

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 (79) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +9 -4
  4. package/agents/AGENTS.md +8 -8
  5. package/agents/architect.md +32 -0
  6. package/agents/build-fixer.md +35 -0
  7. package/agents/code-reviewer.md +51 -0
  8. package/agents/executor-high.md +49 -0
  9. package/agents/explore-high.md +39 -0
  10. package/agents/explore-medium.md +33 -0
  11. package/bridge/mcp-server.cjs +57 -1
  12. package/dist/__tests__/hooks/learner/bridge.test.js +13 -7
  13. package/dist/__tests__/hooks/learner/bridge.test.js.map +1 -1
  14. package/dist/__tests__/hooks.test.js +338 -83
  15. package/dist/__tests__/hooks.test.js.map +1 -1
  16. package/dist/__tests__/installer.test.js +32 -16
  17. package/dist/__tests__/installer.test.js.map +1 -1
  18. package/dist/__tests__/lsp-servers.test.d.ts +2 -0
  19. package/dist/__tests__/lsp-servers.test.d.ts.map +1 -0
  20. package/dist/__tests__/lsp-servers.test.js +118 -0
  21. package/dist/__tests__/lsp-servers.test.js.map +1 -0
  22. package/dist/__tests__/task-continuation.test.d.ts +2 -0
  23. package/dist/__tests__/task-continuation.test.d.ts.map +1 -0
  24. package/dist/__tests__/task-continuation.test.js +740 -0
  25. package/dist/__tests__/task-continuation.test.js.map +1 -0
  26. package/dist/hooks/bridge.js +3 -3
  27. package/dist/hooks/bridge.js.map +1 -1
  28. package/dist/hooks/clear-suggestions/constants.d.ts +54 -0
  29. package/dist/hooks/clear-suggestions/constants.d.ts.map +1 -0
  30. package/dist/hooks/clear-suggestions/constants.js +102 -0
  31. package/dist/hooks/clear-suggestions/constants.js.map +1 -0
  32. package/dist/hooks/clear-suggestions/index.d.ts +61 -0
  33. package/dist/hooks/clear-suggestions/index.d.ts.map +1 -0
  34. package/dist/hooks/clear-suggestions/index.js +276 -0
  35. package/dist/hooks/clear-suggestions/index.js.map +1 -0
  36. package/dist/hooks/clear-suggestions/triggers.d.ts +65 -0
  37. package/dist/hooks/clear-suggestions/triggers.d.ts.map +1 -0
  38. package/dist/hooks/clear-suggestions/triggers.js +222 -0
  39. package/dist/hooks/clear-suggestions/triggers.js.map +1 -0
  40. package/dist/hooks/clear-suggestions/types.d.ts +92 -0
  41. package/dist/hooks/clear-suggestions/types.d.ts.map +1 -0
  42. package/dist/hooks/clear-suggestions/types.js +9 -0
  43. package/dist/hooks/clear-suggestions/types.js.map +1 -0
  44. package/dist/hooks/index.d.ts +1 -0
  45. package/dist/hooks/index.d.ts.map +1 -1
  46. package/dist/hooks/index.js +3 -0
  47. package/dist/hooks/index.js.map +1 -1
  48. package/dist/hooks/keyword-detector/__tests__/index.test.js +51 -51
  49. package/dist/hooks/keyword-detector/__tests__/index.test.js.map +1 -1
  50. package/dist/hooks/keyword-detector/index.d.ts +1 -1
  51. package/dist/hooks/keyword-detector/index.d.ts.map +1 -1
  52. package/dist/hooks/keyword-detector/index.js +18 -5
  53. package/dist/hooks/keyword-detector/index.js.map +1 -1
  54. package/dist/hooks/persistent-mode/index.d.ts.map +1 -1
  55. package/dist/hooks/persistent-mode/index.js +6 -3
  56. package/dist/hooks/persistent-mode/index.js.map +1 -1
  57. package/dist/hooks/todo-continuation/index.d.ts +111 -3
  58. package/dist/hooks/todo-continuation/index.d.ts.map +1 -1
  59. package/dist/hooks/todo-continuation/index.js +204 -23
  60. package/dist/hooks/todo-continuation/index.js.map +1 -1
  61. package/dist/installer/index.d.ts +1 -1
  62. package/dist/installer/index.d.ts.map +1 -1
  63. package/dist/installer/index.js +1 -1
  64. package/dist/installer/index.js.map +1 -1
  65. package/dist/tools/lsp/client.d.ts.map +1 -1
  66. package/dist/tools/lsp/client.js +15 -1
  67. package/dist/tools/lsp/client.js.map +1 -1
  68. package/dist/tools/lsp/servers.d.ts.map +1 -1
  69. package/dist/tools/lsp/servers.js +62 -1
  70. package/dist/tools/lsp/servers.js.map +1 -1
  71. package/docs/CLAUDE.md +83 -0
  72. package/package.json +1 -1
  73. package/scripts/keyword-detector.mjs +203 -83
  74. package/scripts/post-tool-verifier.mjs +39 -1
  75. package/templates/hooks/keyword-detector.sh +197 -31
  76. package/templates/hooks/persistent-mode.mjs +57 -7
  77. package/templates/hooks/persistent-mode.sh +62 -5
  78. package/templates/hooks/stop-continuation.mjs +65 -8
  79. package/templates/hooks/stop-continuation.sh +57 -4
@@ -108,53 +108,33 @@ describe('Keyword Detector', () => {
108
108
  expect(detected[0].keyword).toBe('ultrathink');
109
109
  });
110
110
  it('should detect think keyword', () => {
111
- const detected = detectKeywordsWithType('Let me think about it');
111
+ const detected = detectKeywordsWithType('Let me think hard about it');
112
112
  expect(detected).toHaveLength(1);
113
113
  expect(detected[0].type).toBe('ultrathink');
114
- expect(detected[0].keyword).toBe('think');
114
+ expect(detected[0].keyword).toBe('think hard');
115
115
  });
116
- it('should detect search keywords', () => {
117
- const searchTerms = ['search', 'find', 'locate', 'lookup', 'explore'];
118
- for (const term of searchTerms) {
119
- const detected = detectKeywordsWithType(`Please ${term} this file`);
120
- expect(detected).toHaveLength(1);
121
- expect(detected[0].type).toBe('search');
122
- expect(detected[0].keyword).toBe(term);
123
- }
124
- });
125
- it('should detect search patterns', () => {
116
+ it('should detect deepsearch keywords for codebase search', () => {
126
117
  const patterns = [
127
- 'where is the config',
128
- 'show me all files',
129
- 'list all functions'
118
+ 'search the codebase',
119
+ 'find in codebase',
120
+ 'search code for pattern'
130
121
  ];
131
122
  for (const pattern of patterns) {
132
123
  const detected = detectKeywordsWithType(pattern);
133
124
  expect(detected.length).toBeGreaterThan(0);
134
- const hasSearchType = detected.some(d => d.type === 'search');
135
- expect(hasSearchType).toBe(true);
136
- }
137
- });
138
- it('should detect analyze keywords', () => {
139
- const analyzeTerms = ['analyze', 'investigate', 'examine', 'debug'];
140
- for (const term of analyzeTerms) {
141
- const detected = detectKeywordsWithType(`Please ${term} this code`);
142
- expect(detected).toHaveLength(1);
143
- expect(detected[0].type).toBe('analyze');
144
- expect(detected[0].keyword).toBe(term);
125
+ expect(detected[0].type).toBe('deepsearch');
145
126
  }
146
127
  });
147
- it('should detect analyze patterns', () => {
128
+ it('should detect analyze keywords with restricted patterns', () => {
148
129
  const patterns = [
149
- 'why is this failing',
150
- 'how does this work',
151
- 'how to implement this'
130
+ 'deep analyze this code',
131
+ 'investigate the bug',
132
+ 'debug the issue'
152
133
  ];
153
134
  for (const pattern of patterns) {
154
135
  const detected = detectKeywordsWithType(pattern);
155
136
  expect(detected.length).toBeGreaterThan(0);
156
- const hasAnalyzeType = detected.some(d => d.type === 'analyze');
157
- expect(hasAnalyzeType).toBe(true);
137
+ expect(detected[0].type).toBe('analyze');
158
138
  }
159
139
  });
160
140
  it('should be case insensitive', () => {
@@ -173,27 +153,215 @@ describe('Keyword Detector', () => {
173
153
  expect(detected[0].keyword).toBe('ultrawork');
174
154
  });
175
155
  it('should include position information', () => {
176
- const detected = detectKeywordsWithType('Start search here');
177
- expect(detected[0].position).toBe(6); // Position of 'search'
156
+ const detected = detectKeywordsWithType('Start search the codebase here');
157
+ expect(detected[0].position).toBeGreaterThanOrEqual(0);
178
158
  });
179
159
  it('should return empty array for no matches', () => {
180
160
  const detected = detectKeywordsWithType('Just plain text');
181
161
  expect(detected).toEqual([]);
182
162
  });
183
163
  it('should detect multiple different keyword types', () => {
184
- const text = 'search and analyze this code';
164
+ const text = 'search the codebase and investigate the bug';
185
165
  const detected = detectKeywordsWithType(text);
186
166
  expect(detected.length).toBeGreaterThanOrEqual(2);
187
167
  const types = detected.map(d => d.type);
188
- expect(types).toContain('search');
168
+ expect(types).toContain('deepsearch');
189
169
  expect(types).toContain('analyze');
190
170
  });
171
+ // New keyword types tests
172
+ it('should detect cancel keyword', () => {
173
+ const detected = detectKeywordsWithType('stop this task');
174
+ expect(detected).toHaveLength(1);
175
+ expect(detected[0].type).toBe('cancel');
176
+ expect(detected[0].keyword).toBe('stop');
177
+ });
178
+ it('should detect cancel keyword variations', () => {
179
+ const cancelTerms = ['stop', 'cancel', 'abort'];
180
+ for (const term of cancelTerms) {
181
+ const detected = detectKeywordsWithType(`Please ${term} the process`);
182
+ expect(detected).toHaveLength(1);
183
+ expect(detected[0].type).toBe('cancel');
184
+ expect(detected[0].keyword).toBe(term);
185
+ }
186
+ });
187
+ it('should detect ultrapilot keyword', () => {
188
+ const detected = detectKeywordsWithType('use ultrapilot for this');
189
+ expect(detected).toHaveLength(1);
190
+ expect(detected[0].type).toBe('ultrapilot');
191
+ expect(detected[0].keyword).toBe('ultrapilot');
192
+ });
193
+ it('should detect ultrapilot patterns', () => {
194
+ const patterns = [
195
+ 'ultrapilot this project',
196
+ 'parallel build the app',
197
+ 'swarm build the system'
198
+ ];
199
+ for (const pattern of patterns) {
200
+ const detected = detectKeywordsWithType(pattern);
201
+ expect(detected.length).toBeGreaterThan(0);
202
+ const hasUltrapilot = detected.some(d => d.type === 'ultrapilot');
203
+ expect(hasUltrapilot).toBe(true);
204
+ }
205
+ });
206
+ it('should detect ecomode keyword', () => {
207
+ const detected = detectKeywordsWithType('use ecomode for this');
208
+ expect(detected).toHaveLength(1);
209
+ expect(detected[0].type).toBe('ecomode');
210
+ expect(detected[0].keyword).toBe('ecomode');
211
+ });
212
+ it('should detect ecomode variations', () => {
213
+ const ecoTerms = ['eco', 'ecomode', 'efficient', 'save-tokens', 'budget'];
214
+ for (const term of ecoTerms) {
215
+ const detected = detectKeywordsWithType(`Use ${term} mode`);
216
+ expect(detected).toHaveLength(1);
217
+ expect(detected[0].type).toBe('ecomode');
218
+ expect(detected[0].keyword).toBe(term);
219
+ }
220
+ });
221
+ it('should detect swarm keyword', () => {
222
+ const detected = detectKeywordsWithType('swarm 5 agents to fix this');
223
+ expect(detected).toHaveLength(1);
224
+ expect(detected[0].type).toBe('swarm');
225
+ expect(detected[0].keyword).toBe('swarm 5 agents');
226
+ });
227
+ it('should detect coordinated agents pattern', () => {
228
+ const detected = detectKeywordsWithType('use coordinated agents');
229
+ expect(detected).toHaveLength(1);
230
+ expect(detected[0].type).toBe('swarm');
231
+ expect(detected[0].keyword).toBe('coordinated agents');
232
+ });
233
+ it('should detect pipeline keyword', () => {
234
+ const detected = detectKeywordsWithType('pipeline this task');
235
+ expect(detected).toHaveLength(1);
236
+ expect(detected[0].type).toBe('pipeline');
237
+ expect(detected[0].keyword).toBe('pipeline');
238
+ });
239
+ it('should detect chain agents pattern', () => {
240
+ const detected = detectKeywordsWithType('chain agents together');
241
+ expect(detected).toHaveLength(1);
242
+ expect(detected[0].type).toBe('pipeline');
243
+ expect(detected[0].keyword).toBe('chain agents');
244
+ });
245
+ it('should detect ralplan keyword', () => {
246
+ const detected = detectKeywordsWithType('ralplan this feature');
247
+ expect(detected).toHaveLength(1);
248
+ expect(detected[0].type).toBe('ralplan');
249
+ expect(detected[0].keyword).toBe('ralplan');
250
+ });
251
+ it('should detect plan patterns', () => {
252
+ const patterns = [
253
+ 'plan this feature',
254
+ 'plan the refactoring'
255
+ ];
256
+ for (const pattern of patterns) {
257
+ const detected = detectKeywordsWithType(pattern);
258
+ expect(detected.length).toBeGreaterThan(0);
259
+ const hasPlan = detected.some(d => d.type === 'plan');
260
+ expect(hasPlan).toBe(true);
261
+ }
262
+ });
263
+ it('should detect tdd keyword', () => {
264
+ const detected = detectKeywordsWithType('use tdd for this');
265
+ expect(detected).toHaveLength(1);
266
+ expect(detected[0].type).toBe('tdd');
267
+ expect(detected[0].keyword).toBe('tdd');
268
+ });
269
+ it('should detect tdd patterns', () => {
270
+ const patterns = [
271
+ 'test first development',
272
+ 'red green refactor'
273
+ ];
274
+ for (const pattern of patterns) {
275
+ const detected = detectKeywordsWithType(pattern);
276
+ expect(detected.length).toBeGreaterThan(0);
277
+ const hasTDD = detected.some(d => d.type === 'tdd');
278
+ expect(hasTDD).toBe(true);
279
+ }
280
+ });
281
+ it('should detect research keyword', () => {
282
+ const detected = detectKeywordsWithType('research this topic');
283
+ expect(detected).toHaveLength(1);
284
+ expect(detected[0].type).toBe('research');
285
+ expect(detected[0].keyword).toBe('research');
286
+ });
287
+ it('should detect research patterns', () => {
288
+ const patterns = [
289
+ 'analyze data from the file',
290
+ 'run statistics on this'
291
+ ];
292
+ for (const pattern of patterns) {
293
+ const detected = detectKeywordsWithType(pattern);
294
+ expect(detected.length).toBeGreaterThan(0);
295
+ const hasResearch = detected.some(d => d.type === 'research');
296
+ expect(hasResearch).toBe(true);
297
+ }
298
+ });
299
+ it('should detect deepsearch keyword', () => {
300
+ const detected = detectKeywordsWithType('deepsearch for the pattern');
301
+ expect(detected).toHaveLength(1);
302
+ expect(detected[0].type).toBe('deepsearch');
303
+ expect(detected[0].keyword).toBe('deepsearch');
304
+ });
305
+ it('should detect deepsearch patterns', () => {
306
+ const patterns = [
307
+ 'search the codebase for errors',
308
+ 'search codebase for pattern',
309
+ 'find in codebase',
310
+ 'find in all files'
311
+ ];
312
+ for (const pattern of patterns) {
313
+ const detected = detectKeywordsWithType(pattern);
314
+ expect(detected.length).toBeGreaterThan(0);
315
+ const hasDeepsearch = detected.some(d => d.type === 'deepsearch');
316
+ expect(hasDeepsearch).toBe(true);
317
+ }
318
+ });
319
+ it('should NOT detect deepsearch for generic find', () => {
320
+ const patterns = [
321
+ 'find the file',
322
+ 'find this function',
323
+ 'search for help'
324
+ ];
325
+ for (const pattern of patterns) {
326
+ const detected = detectKeywordsWithType(pattern);
327
+ const hasDeepsearch = detected.some(d => d.type === 'deepsearch');
328
+ expect(hasDeepsearch).toBe(false);
329
+ }
330
+ });
331
+ it('should detect analyze patterns with restrictions', () => {
332
+ const patterns = [
333
+ 'deep analyze this code',
334
+ 'investigate the bug',
335
+ 'investigate this issue',
336
+ 'debug the problem',
337
+ 'debug this error'
338
+ ];
339
+ for (const pattern of patterns) {
340
+ const detected = detectKeywordsWithType(pattern);
341
+ expect(detected.length).toBeGreaterThan(0);
342
+ const hasAnalyze = detected.some(d => d.type === 'analyze');
343
+ expect(hasAnalyze).toBe(true);
344
+ }
345
+ });
346
+ it('should NOT detect analyze for generic patterns', () => {
347
+ const patterns = [
348
+ 'how to do this',
349
+ 'understand this code',
350
+ 'review this code',
351
+ 'analyze without context'
352
+ ];
353
+ for (const pattern of patterns) {
354
+ const detected = detectKeywordsWithType(pattern);
355
+ const hasAnalyze = detected.some(d => d.type === 'analyze');
356
+ expect(hasAnalyze).toBe(false);
357
+ }
358
+ });
191
359
  });
192
360
  describe('hasKeyword', () => {
193
361
  it('should return true when keyword exists', () => {
194
362
  expect(hasKeyword('use ultrawork mode')).toBe(true);
195
- expect(hasKeyword('search for files')).toBe(true);
196
- expect(hasKeyword('analyze this')).toBe(true);
363
+ expect(hasKeyword('search the codebase')).toBe(true);
364
+ expect(hasKeyword('investigate the bug')).toBe(true);
197
365
  });
198
366
  it('should return false when no keyword exists', () => {
199
367
  expect(hasKeyword('just normal text')).toBe(false);
@@ -204,7 +372,7 @@ describe('Keyword Detector', () => {
204
372
  expect(hasKeyword(text)).toBe(false);
205
373
  });
206
374
  it('should detect keywords outside code blocks', () => {
207
- const text = 'Please search\n```\nsome code\n```\nfor this';
375
+ const text = 'Please search the codebase\n```\nsome code\n```\nfor this';
208
376
  expect(hasKeyword(text)).toBe(true);
209
377
  });
210
378
  it('should handle empty string', () => {
@@ -219,20 +387,20 @@ describe('Keyword Detector', () => {
219
387
  expect(primary).not.toBeNull();
220
388
  expect(primary.type).toBe('ultrawork');
221
389
  });
222
- it('should return ultrathink when no ultrawork', () => {
223
- const text = 'search and think about this';
390
+ it('should return ultrathink when present', () => {
391
+ const text = 'think hard about this problem';
224
392
  const primary = getPrimaryKeyword(text);
225
393
  expect(primary).not.toBeNull();
226
394
  expect(primary.type).toBe('ultrathink');
227
395
  });
228
- it('should return search when only search keyword', () => {
229
- const text = 'find all files';
396
+ it('should return deepsearch for codebase search', () => {
397
+ const text = 'find in codebase';
230
398
  const primary = getPrimaryKeyword(text);
231
399
  expect(primary).not.toBeNull();
232
- expect(primary.type).toBe('search');
400
+ expect(primary.type).toBe('deepsearch');
233
401
  });
234
402
  it('should return analyze when only analyze keyword', () => {
235
- const text = 'investigate this issue';
403
+ const text = 'investigate the issue';
236
404
  const primary = getPrimaryKeyword(text);
237
405
  expect(primary).not.toBeNull();
238
406
  expect(primary.type).toBe('analyze');
@@ -242,18 +410,89 @@ describe('Keyword Detector', () => {
242
410
  expect(primary).toBeNull();
243
411
  });
244
412
  it('should ignore code blocks', () => {
245
- const text = '```\nultrawork code\n```\nsearch this';
413
+ const text = '```\nultrawork code\n```\nsearch the codebase';
246
414
  const primary = getPrimaryKeyword(text);
247
415
  expect(primary).not.toBeNull();
248
- expect(primary.type).toBe('search');
416
+ expect(primary.type).toBe('deepsearch');
249
417
  });
250
418
  it('should return first detected when same priority', () => {
251
- // Both search and analyze have same priority
252
- const text = 'search and analyze';
419
+ // deepsearch has higher priority than analyze in the priority list
420
+ const text = 'search the codebase and investigate the bug';
253
421
  const primary = getPrimaryKeyword(text);
254
422
  expect(primary).not.toBeNull();
255
- // Should return search as it comes first in priority list
256
- expect(primary.type).toBe('search');
423
+ // Should return deepsearch as it comes first in priority list
424
+ expect(primary.type).toBe('deepsearch');
425
+ });
426
+ // New priority tests for new keywords
427
+ it('should give cancel highest priority', () => {
428
+ const primary = getPrimaryKeyword('stop searching for files');
429
+ expect(primary).not.toBeNull();
430
+ expect(primary.type).toBe('cancel');
431
+ });
432
+ it('should give cancel priority over analyze', () => {
433
+ const primary = getPrimaryKeyword('cancel this investigation');
434
+ expect(primary).not.toBeNull();
435
+ expect(primary.type).toBe('cancel');
436
+ });
437
+ it('should prioritize cancel over all other keywords', () => {
438
+ const primary = getPrimaryKeyword('stop ultrawork and search');
439
+ expect(primary).not.toBeNull();
440
+ expect(primary.type).toBe('cancel');
441
+ });
442
+ it('should prioritize ralph after cancel', () => {
443
+ const primary = getPrimaryKeyword('ralph mode for the task');
444
+ expect(primary).not.toBeNull();
445
+ expect(primary.type).toBe('ralph');
446
+ });
447
+ it('should prioritize ultrapilot correctly', () => {
448
+ const primary = getPrimaryKeyword('ultrapilot this task');
449
+ expect(primary).not.toBeNull();
450
+ expect(primary.type).toBe('ultrapilot');
451
+ });
452
+ it('should prioritize ecomode correctly', () => {
453
+ const primary = getPrimaryKeyword('use efficient mode for this');
454
+ expect(primary).not.toBeNull();
455
+ expect(primary.type).toBe('ecomode');
456
+ });
457
+ it('should prioritize swarm correctly', () => {
458
+ const primary = getPrimaryKeyword('swarm 5 agents for this');
459
+ expect(primary).not.toBeNull();
460
+ expect(primary.type).toBe('swarm');
461
+ });
462
+ it('should prioritize pipeline correctly', () => {
463
+ const primary = getPrimaryKeyword('pipeline the task');
464
+ expect(primary).not.toBeNull();
465
+ expect(primary.type).toBe('pipeline');
466
+ });
467
+ it('should prioritize ralplan over plan', () => {
468
+ const primary = getPrimaryKeyword('ralplan this project');
469
+ expect(primary).not.toBeNull();
470
+ expect(primary.type).toBe('ralplan');
471
+ });
472
+ it('should detect plan correctly', () => {
473
+ const primary = getPrimaryKeyword('plan this feature');
474
+ expect(primary).not.toBeNull();
475
+ expect(primary.type).toBe('plan');
476
+ });
477
+ it('should prioritize tdd correctly', () => {
478
+ const primary = getPrimaryKeyword('tdd for this feature');
479
+ expect(primary).not.toBeNull();
480
+ expect(primary.type).toBe('tdd');
481
+ });
482
+ it('should prioritize research correctly', () => {
483
+ const primary = getPrimaryKeyword('research this topic');
484
+ expect(primary).not.toBeNull();
485
+ expect(primary.type).toBe('research');
486
+ });
487
+ it('should prioritize deepsearch over generic search', () => {
488
+ const primary = getPrimaryKeyword('search the codebase');
489
+ expect(primary).not.toBeNull();
490
+ expect(primary.type).toBe('deepsearch');
491
+ });
492
+ it('should prioritize analyze with restricted pattern', () => {
493
+ const primary = getPrimaryKeyword('investigate the bug');
494
+ expect(primary).not.toBeNull();
495
+ expect(primary.type).toBe('analyze');
257
496
  });
258
497
  });
259
498
  });
@@ -263,7 +502,8 @@ describe('Todo Continuation', () => {
263
502
  const result = {
264
503
  count: 0,
265
504
  todos: [],
266
- total: 5
505
+ total: 5,
506
+ source: 'todo'
267
507
  };
268
508
  expect(formatTodoStatus(result)).toBe('All tasks complete (5 total)');
269
509
  });
@@ -271,7 +511,8 @@ describe('Todo Continuation', () => {
271
511
  const result = {
272
512
  count: 3,
273
513
  todos: [],
274
- total: 10
514
+ total: 10,
515
+ source: 'todo'
275
516
  };
276
517
  expect(formatTodoStatus(result)).toBe('7/10 completed, 3 remaining');
277
518
  });
@@ -279,7 +520,8 @@ describe('Todo Continuation', () => {
279
520
  const result = {
280
521
  count: 0,
281
522
  todos: [],
282
- total: 0
523
+ total: 0,
524
+ source: 'none'
283
525
  };
284
526
  expect(formatTodoStatus(result)).toBe('All tasks complete (0 total)');
285
527
  });
@@ -287,7 +529,8 @@ describe('Todo Continuation', () => {
287
529
  const result = {
288
530
  count: 5,
289
531
  todos: [],
290
- total: 5
532
+ total: 5,
533
+ source: 'todo'
291
534
  };
292
535
  expect(formatTodoStatus(result)).toBe('0/5 completed, 5 remaining');
293
536
  });
@@ -295,7 +538,8 @@ describe('Todo Continuation', () => {
295
538
  const result = {
296
539
  count: 1,
297
540
  todos: [],
298
- total: 10
541
+ total: 10,
542
+ source: 'todo'
299
543
  };
300
544
  expect(formatTodoStatus(result)).toBe('9/10 completed, 1 remaining');
301
545
  });
@@ -310,7 +554,8 @@ describe('Todo Continuation', () => {
310
554
  const result = {
311
555
  count: 3,
312
556
  todos,
313
- total: 3
557
+ total: 3,
558
+ source: 'todo'
314
559
  };
315
560
  const next = getNextPendingTodo(result);
316
561
  expect(next).not.toBeNull();
@@ -326,7 +571,8 @@ describe('Todo Continuation', () => {
326
571
  const result = {
327
572
  count: 2,
328
573
  todos: todos.filter(t => t.status !== 'completed'),
329
- total: 3
574
+ total: 3,
575
+ source: 'todo'
330
576
  };
331
577
  const next = getNextPendingTodo(result);
332
578
  expect(next).not.toBeNull();
@@ -337,7 +583,8 @@ describe('Todo Continuation', () => {
337
583
  const result = {
338
584
  count: 0,
339
585
  todos: [],
340
- total: 0
586
+ total: 0,
587
+ source: 'none'
341
588
  };
342
589
  const next = getNextPendingTodo(result);
343
590
  expect(next).toBeNull();
@@ -346,7 +593,8 @@ describe('Todo Continuation', () => {
346
593
  const result = {
347
594
  count: 0,
348
595
  todos: [],
349
- total: 3
596
+ total: 3,
597
+ source: 'todo'
350
598
  };
351
599
  const next = getNextPendingTodo(result);
352
600
  expect(next).toBeNull();
@@ -359,7 +607,8 @@ describe('Todo Continuation', () => {
359
607
  const result = {
360
608
  count: 2,
361
609
  todos,
362
- total: 2
610
+ total: 2,
611
+ source: 'todo'
363
612
  };
364
613
  const next = getNextPendingTodo(result);
365
614
  expect(next).not.toBeNull();
@@ -373,7 +622,8 @@ describe('Todo Continuation', () => {
373
622
  const result = {
374
623
  count: 2,
375
624
  todos,
376
- total: 2
625
+ total: 2,
626
+ source: 'todo'
377
627
  };
378
628
  const next = getNextPendingTodo(result);
379
629
  expect(next).not.toBeNull();
@@ -387,7 +637,8 @@ describe('Todo Continuation', () => {
387
637
  const result = {
388
638
  count: 1,
389
639
  todos: [todos[1]],
390
- total: 2
640
+ total: 2,
641
+ source: 'todo'
391
642
  };
392
643
  const next = getNextPendingTodo(result);
393
644
  expect(next).not.toBeNull();
@@ -403,7 +654,8 @@ describe('Todo Continuation', () => {
403
654
  const result = {
404
655
  count: 4,
405
656
  todos,
406
- total: 4
657
+ total: 4,
658
+ source: 'todo'
407
659
  };
408
660
  const next = getNextPendingTodo(result);
409
661
  expect(next).not.toBeNull();
@@ -456,7 +708,8 @@ describe('Todo Continuation', () => {
456
708
  const result = {
457
709
  count: todos.length,
458
710
  todos,
459
- total: 5
711
+ total: 5,
712
+ source: 'todo'
460
713
  };
461
714
  expect(result.count).toBe(result.todos.length);
462
715
  expect(result.total).toBeGreaterThanOrEqual(result.count);
@@ -466,7 +719,8 @@ describe('Todo Continuation', () => {
466
719
  const result = {
467
720
  count: 0,
468
721
  todos: [],
469
- total: 3
722
+ total: 3,
723
+ source: 'todo'
470
724
  };
471
725
  expect(result.count).toBeLessThanOrEqual(result.total);
472
726
  });
@@ -549,7 +803,8 @@ describe('Hook Output Structure', () => {
549
803
  const result = {
550
804
  count: 2,
551
805
  todos: [],
552
- total: 5
806
+ total: 5,
807
+ source: 'todo'
553
808
  };
554
809
  const status = formatTodoStatus(result);
555
810
  const message = `Todo Status: ${status}`;
@@ -561,7 +816,7 @@ describe('Hook Output Structure', () => {
561
816
  describe('Integration: Keyword Detection with Code Blocks', () => {
562
817
  it('should detect keywords outside code and ignore inside', () => {
563
818
  const text = `
564
- Please search for files
819
+ Please search the codebase
565
820
 
566
821
  \`\`\`javascript
567
822
  // This search should be ignored
@@ -570,25 +825,25 @@ function search() {
570
825
  }
571
826
  \`\`\`
572
827
 
573
- Now analyze the results
828
+ Now investigate the bug
574
829
  `;
575
830
  const detected = detectKeywordsWithType(removeCodeBlocks(text));
576
831
  const types = detected.map(d => d.type);
577
- expect(types).toContain('search');
832
+ expect(types).toContain('deepsearch');
578
833
  expect(types).toContain('analyze');
579
834
  // Should only detect the ones outside code blocks
580
- expect(detected.filter(d => d.type === 'search')).toHaveLength(1);
835
+ expect(detected.filter(d => d.type === 'deepsearch')).toHaveLength(1);
581
836
  expect(detected.filter(d => d.type === 'analyze')).toHaveLength(1);
582
837
  });
583
838
  it('should handle inline code with keywords', () => {
584
- const text = 'Use the `search` command to find files';
839
+ const text = 'Use the `deepsearch` command to find in codebase';
585
840
  const cleanText = removeCodeBlocks(text);
586
841
  const detected = detectKeywordsWithType(cleanText);
587
- // The word 'find' should still be detected
588
- expect(detected.some(d => d.type === 'search')).toBe(true);
842
+ // The phrase 'find in codebase' should still be detected
843
+ expect(detected.some(d => d.type === 'deepsearch')).toBe(true);
589
844
  });
590
845
  it('should prioritize ultrawork even with other keywords', () => {
591
- const text = 'search, analyze, and use ultrawork mode';
846
+ const text = 'search the codebase, investigate the bug, and use ultrawork mode';
592
847
  const primary = getPrimaryKeyword(text);
593
848
  expect(primary).not.toBeNull();
594
849
  expect(primary.type).toBe('ultrawork');
@@ -615,33 +870,33 @@ describe('Edge Cases', () => {
615
870
  });
616
871
  describe('Whitespace handling', () => {
617
872
  it('should detect keywords with extra whitespace', () => {
618
- const text = ' search for files ';
873
+ const text = ' search the codebase ';
619
874
  expect(hasKeyword(text)).toBe(true);
620
875
  });
621
876
  it('should handle newlines and tabs', () => {
622
- const text = 'search\n\tfor\r\nfiles';
877
+ const text = 'search\n\tthe\r\ncodebase';
623
878
  const detected = detectKeywordsWithType(text);
624
- expect(detected.some(d => d.type === 'search')).toBe(true);
879
+ expect(detected.some(d => d.type === 'deepsearch')).toBe(true);
625
880
  });
626
881
  });
627
882
  describe('Unicode and special characters', () => {
628
883
  it('should handle unicode characters', () => {
629
- const text = 'search for files with émojis 🔍';
884
+ const text = 'search the codebase with émojis 🔍';
630
885
  expect(hasKeyword(text)).toBe(true);
631
886
  });
632
887
  it('should handle mixed scripts', () => {
633
- const text = 'Please search 搜索 искать';
888
+ const text = 'Please search the codebase 搜索 искать';
634
889
  const detected = detectKeywordsWithType(text);
635
- expect(detected.some(d => d.keyword === 'search')).toBe(true);
890
+ expect(detected.some(d => d.type === 'deepsearch')).toBe(true);
636
891
  });
637
892
  });
638
893
  describe('Very long inputs', () => {
639
894
  it('should handle long text efficiently', () => {
640
- const longText = 'plain text '.repeat(1000) + ' search here';
895
+ const longText = 'plain text '.repeat(1000) + ' search the codebase';
641
896
  expect(hasKeyword(longText)).toBe(true);
642
897
  });
643
898
  it('should handle many code blocks', () => {
644
- const manyBlocks = '```code```\n'.repeat(100) + 'search here';
899
+ const manyBlocks = '```code```\n'.repeat(100) + 'search the codebase';
645
900
  const cleaned = removeCodeBlocks(manyBlocks);
646
901
  expect(hasKeyword(cleaned)).toBe(true);
647
902
  });