memory-journal-mcp 4.3.0 → 4.4.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 (109) hide show
  1. package/.dockerignore +131 -122
  2. package/.gitattributes +29 -0
  3. package/.github/workflows/docker-publish.yml +1 -1
  4. package/.github/workflows/lint-and-test.yml +1 -2
  5. package/.github/workflows/secrets-scanning.yml +0 -1
  6. package/.github/workflows/security-update.yml +6 -6
  7. package/.vscode/settings.json +17 -15
  8. package/CHANGELOG.md +1065 -11
  9. package/DOCKER_README.md +51 -33
  10. package/Dockerfile +14 -12
  11. package/README.md +68 -33
  12. package/SECURITY.md +225 -220
  13. package/dist/cli.js +7 -0
  14. package/dist/cli.js.map +1 -1
  15. package/dist/constants/ServerInstructions.d.ts +1 -1
  16. package/dist/constants/ServerInstructions.d.ts.map +1 -1
  17. package/dist/constants/ServerInstructions.js +70 -26
  18. package/dist/constants/ServerInstructions.js.map +1 -1
  19. package/dist/constants/icons.d.ts +2 -0
  20. package/dist/constants/icons.d.ts.map +1 -1
  21. package/dist/constants/icons.js +6 -0
  22. package/dist/constants/icons.js.map +1 -1
  23. package/dist/database/SqliteAdapter.d.ts +51 -10
  24. package/dist/database/SqliteAdapter.d.ts.map +1 -1
  25. package/dist/database/SqliteAdapter.js +143 -43
  26. package/dist/database/SqliteAdapter.js.map +1 -1
  27. package/dist/filtering/ToolFilter.d.ts +1 -1
  28. package/dist/filtering/ToolFilter.d.ts.map +1 -1
  29. package/dist/filtering/ToolFilter.js +7 -1
  30. package/dist/filtering/ToolFilter.js.map +1 -1
  31. package/dist/github/GitHubIntegration.d.ts +74 -2
  32. package/dist/github/GitHubIntegration.d.ts.map +1 -1
  33. package/dist/github/GitHubIntegration.js +508 -7
  34. package/dist/github/GitHubIntegration.js.map +1 -1
  35. package/dist/handlers/prompts/index.js +1 -0
  36. package/dist/handlers/prompts/index.js.map +1 -1
  37. package/dist/handlers/resources/index.d.ts.map +1 -1
  38. package/dist/handlers/resources/index.js +257 -13
  39. package/dist/handlers/resources/index.js.map +1 -1
  40. package/dist/handlers/tools/index.d.ts.map +1 -1
  41. package/dist/handlers/tools/index.js +595 -8
  42. package/dist/handlers/tools/index.js.map +1 -1
  43. package/dist/server/McpServer.d.ts +2 -0
  44. package/dist/server/McpServer.d.ts.map +1 -1
  45. package/dist/server/McpServer.js +69 -26
  46. package/dist/server/McpServer.js.map +1 -1
  47. package/dist/types/index.d.ts +97 -0
  48. package/dist/types/index.d.ts.map +1 -1
  49. package/dist/types/index.js.map +1 -1
  50. package/dist/utils/logger.d.ts +1 -0
  51. package/dist/utils/logger.d.ts.map +1 -1
  52. package/dist/utils/logger.js +8 -1
  53. package/dist/utils/logger.js.map +1 -1
  54. package/dist/utils/progress-utils.d.ts +18 -3
  55. package/dist/utils/progress-utils.d.ts.map +1 -1
  56. package/dist/utils/progress-utils.js.map +1 -1
  57. package/dist/utils/security-utils.d.ts +91 -0
  58. package/dist/utils/security-utils.d.ts.map +1 -0
  59. package/dist/utils/security-utils.js +184 -0
  60. package/dist/utils/security-utils.js.map +1 -0
  61. package/dist/vector/VectorSearchManager.d.ts +2 -1
  62. package/dist/vector/VectorSearchManager.d.ts.map +1 -1
  63. package/dist/vector/VectorSearchManager.js +100 -34
  64. package/dist/vector/VectorSearchManager.js.map +1 -1
  65. package/docker-compose.yml +46 -37
  66. package/mcp-config-example.json +0 -2
  67. package/package.json +21 -14
  68. package/releases/v4.3.1.md +69 -0
  69. package/releases/v4.4.0.md +120 -0
  70. package/server.json +3 -3
  71. package/src/cli.ts +11 -0
  72. package/src/constants/ServerInstructions.ts +70 -26
  73. package/src/constants/icons.ts +7 -0
  74. package/src/database/SqliteAdapter.ts +165 -44
  75. package/src/filtering/ToolFilter.ts +7 -1
  76. package/src/github/GitHubIntegration.ts +588 -8
  77. package/src/handlers/prompts/index.ts +1 -0
  78. package/src/handlers/resources/index.ts +318 -12
  79. package/src/handlers/tools/index.ts +686 -13
  80. package/src/server/McpServer.ts +79 -37
  81. package/src/types/index.ts +98 -0
  82. package/src/utils/logger.ts +10 -1
  83. package/src/utils/progress-utils.ts +17 -6
  84. package/src/utils/security-utils.ts +205 -0
  85. package/src/vector/VectorSearchManager.ts +110 -39
  86. package/tests/constants/icons.test.ts +102 -0
  87. package/tests/constants/server-instructions.test.ts +549 -0
  88. package/tests/database/sqlite-adapter.bench.ts +63 -0
  89. package/tests/database/sqlite-adapter.test.ts +555 -0
  90. package/tests/filtering/tool-filter.test.ts +266 -0
  91. package/tests/github/github-integration.test.ts +1024 -0
  92. package/tests/handlers/github-resource-handlers.test.ts +473 -0
  93. package/tests/handlers/github-tool-handlers.test.ts +556 -0
  94. package/tests/handlers/prompt-handlers.test.ts +91 -0
  95. package/tests/handlers/resource-handlers.test.ts +339 -0
  96. package/tests/handlers/tool-handlers.test.ts +497 -0
  97. package/tests/handlers/vector-tool-handlers.test.ts +238 -0
  98. package/tests/security/sql-injection.test.ts +347 -0
  99. package/tests/server/mcp-server.bench.ts +55 -0
  100. package/tests/server/mcp-server.test.ts +675 -0
  101. package/tests/utils/logger.test.ts +180 -0
  102. package/tests/utils/mcp-logger.test.ts +212 -0
  103. package/tests/utils/progress-utils.test.ts +156 -0
  104. package/tests/utils/security-utils.test.ts +82 -0
  105. package/tests/vector/vector-search-manager.test.ts +335 -0
  106. package/tests/vector/vector-search.bench.ts +53 -0
  107. package/vitest.config.ts +15 -0
  108. package/.github/workflows/DOCKER_DEPLOYMENT_SETUP.md +0 -387
  109. package/.github/workflows/dependabot-auto-merge.yml +0 -42
@@ -0,0 +1,549 @@
1
+ /**
2
+ * Server Instructions Tests
3
+ *
4
+ * Tests the generateInstructions function at all instruction levels
5
+ * with comprehensive content validation for all tools, resources, and field notes.
6
+ */
7
+
8
+ import { describe, it, expect } from 'vitest'
9
+ import { generateInstructions } from '../../src/constants/ServerInstructions.js'
10
+ import { TOOL_GROUPS, getAllToolNames } from '../../src/filtering/ToolFilter.js'
11
+
12
+ /** Full tool set based on TOOL_GROUPS for realistic testing */
13
+ const ALL_TOOLS = new Set(getAllToolNames())
14
+
15
+ /** Minimal tool set for basic testing */
16
+ const TEST_TOOLS = new Set(['create_entry', 'search_entries', 'backup_journal'])
17
+
18
+ /** Minimal resources for testing */
19
+ const TEST_RESOURCES = [{ uri: 'memory://health', name: 'health', description: 'Health check' }]
20
+
21
+ /** Minimal prompts for testing */
22
+ const TEST_PROMPTS = [{ name: 'test-prompt', description: 'A test prompt' }]
23
+
24
+ /** Helper to generate full-level instructions with all tools */
25
+ function fullInstructions(): string {
26
+ return generateInstructions(ALL_TOOLS, TEST_RESOURCES, TEST_PROMPTS, undefined, 'full')
27
+ }
28
+
29
+ describe('generateInstructions', () => {
30
+ describe('essential level', () => {
31
+ it('should return non-empty string', () => {
32
+ const result = generateInstructions(
33
+ TEST_TOOLS,
34
+ TEST_RESOURCES,
35
+ TEST_PROMPTS,
36
+ undefined,
37
+ 'essential'
38
+ )
39
+ expect(result.length).toBeGreaterThan(0)
40
+ })
41
+
42
+ it('should include core behaviors', () => {
43
+ const result = generateInstructions(
44
+ TEST_TOOLS,
45
+ TEST_RESOURCES,
46
+ TEST_PROMPTS,
47
+ undefined,
48
+ 'essential'
49
+ )
50
+ expect(result).toContain('memory://briefing')
51
+ expect(result).toContain('Session Start')
52
+ })
53
+
54
+ it('should include Quick Access table', () => {
55
+ const result = generateInstructions(
56
+ TEST_TOOLS,
57
+ TEST_RESOURCES,
58
+ TEST_PROMPTS,
59
+ undefined,
60
+ 'essential'
61
+ )
62
+ expect(result).toContain('Quick Access')
63
+ expect(result).toContain('memory://health')
64
+ expect(result).toContain('semantic_search')
65
+ expect(result).toContain('get-context-bundle')
66
+ })
67
+
68
+ it('should include all three Behaviors bullets', () => {
69
+ const result = generateInstructions(
70
+ TEST_TOOLS,
71
+ TEST_RESOURCES,
72
+ TEST_PROMPTS,
73
+ undefined,
74
+ 'essential'
75
+ )
76
+ expect(result).toContain('Create entries for')
77
+ expect(result).toContain('Search before')
78
+ expect(result).toContain('Link entries')
79
+ })
80
+
81
+ it('should not include tool parameter reference', () => {
82
+ const result = generateInstructions(
83
+ TEST_TOOLS,
84
+ TEST_RESOURCES,
85
+ TEST_PROMPTS,
86
+ undefined,
87
+ 'essential'
88
+ )
89
+ expect(result).not.toContain('Tool Parameter Reference')
90
+ })
91
+
92
+ it('should not include GitHub Integration heading', () => {
93
+ const result = generateInstructions(
94
+ TEST_TOOLS,
95
+ TEST_RESOURCES,
96
+ TEST_PROMPTS,
97
+ undefined,
98
+ 'essential'
99
+ )
100
+ expect(result).not.toContain('## GitHub Integration')
101
+ })
102
+ })
103
+
104
+ describe('standard level', () => {
105
+ it('should include GitHub instructions', () => {
106
+ const result = generateInstructions(
107
+ TEST_TOOLS,
108
+ TEST_RESOURCES,
109
+ TEST_PROMPTS,
110
+ undefined,
111
+ 'standard'
112
+ )
113
+ expect(result).toContain('GitHub')
114
+ })
115
+
116
+ it('should include GitHub integration patterns', () => {
117
+ const result = generateInstructions(
118
+ TEST_TOOLS,
119
+ TEST_RESOURCES,
120
+ TEST_PROMPTS,
121
+ undefined,
122
+ 'standard'
123
+ )
124
+ expect(result).toContain('issue_number')
125
+ expect(result).toContain('pr_number')
126
+ expect(result).toContain('actions-failure-digest')
127
+ expect(result).toContain('get_kanban_board')
128
+ expect(result).toContain('get_github_milestones')
129
+ expect(result).toContain('memory://github/milestones')
130
+ })
131
+
132
+ it('should not include tool parameter reference', () => {
133
+ const result = generateInstructions(
134
+ TEST_TOOLS,
135
+ TEST_RESOURCES,
136
+ TEST_PROMPTS,
137
+ undefined,
138
+ 'standard'
139
+ )
140
+ expect(result).not.toContain('Tool Parameter Reference')
141
+ })
142
+ })
143
+
144
+ describe('full level', () => {
145
+ it('should include tool parameter reference', () => {
146
+ const result = fullInstructions()
147
+ expect(result).toContain('Tool Parameter Reference')
148
+ })
149
+
150
+ it('should include active tools listing', () => {
151
+ const result = fullInstructions()
152
+ expect(result).toContain('Active Tools')
153
+ })
154
+
155
+ it('should include prompts section', () => {
156
+ const result = fullInstructions()
157
+ expect(result).toContain('Prompts')
158
+ expect(result).toContain('test-prompt')
159
+ })
160
+
161
+ it('should include key resources section', () => {
162
+ const result = fullInstructions()
163
+ expect(result).toContain('Key Resources')
164
+ })
165
+ })
166
+
167
+ describe('full level — entry operations tools', () => {
168
+ it('should list all 7 entry operation tools', () => {
169
+ const result = fullInstructions()
170
+ expect(result).toContain('create_entry')
171
+ expect(result).toContain('create_entry_minimal')
172
+ expect(result).toContain('get_entry_by_id')
173
+ expect(result).toContain('get_recent_entries')
174
+ expect(result).toContain('update_entry')
175
+ expect(result).toContain('delete_entry')
176
+ expect(result).toContain('list_tags')
177
+ })
178
+
179
+ it('should document correct get_recent_entries default limit of 5', () => {
180
+ const result = fullInstructions()
181
+ expect(result).toContain('`limit` (default 5)')
182
+ })
183
+
184
+ it('should document get_entry_by_id include_relationships param', () => {
185
+ const result = fullInstructions()
186
+ expect(result).toContain('include_relationships')
187
+ })
188
+
189
+ it('should document create_entry GitHub linking params', () => {
190
+ const result = fullInstructions()
191
+ expect(result).toContain('issue_number')
192
+ expect(result).toContain('pr_number')
193
+ expect(result).toContain('project_number')
194
+ expect(result).toContain('significance_type')
195
+ })
196
+ })
197
+
198
+ describe('full level — search tools', () => {
199
+ it('should list all 4 search tools', () => {
200
+ const result = fullInstructions()
201
+ expect(result).toContain('search_entries')
202
+ expect(result).toContain('search_by_date_range')
203
+ expect(result).toContain('semantic_search')
204
+ expect(result).toContain('get_vector_index_stats')
205
+ })
206
+
207
+ it('should document correct similarity_threshold default of 0.25', () => {
208
+ const result = fullInstructions()
209
+ expect(result).toContain('similarity_threshold` (default 0.25)')
210
+ })
211
+
212
+ it('should document search_entries filter params', () => {
213
+ const result = fullInstructions()
214
+ // search_entries has optional query and filter params
215
+ expect(result).toContain('workflow_run_id')
216
+ })
217
+
218
+ it('should document hint_on_empty param for semantic_search', () => {
219
+ const result = fullInstructions()
220
+ expect(result).toContain('hint_on_empty')
221
+ })
222
+ })
223
+
224
+ describe('full level — relationship tools', () => {
225
+ it('should list both relationship tools', () => {
226
+ const result = fullInstructions()
227
+ expect(result).toContain('link_entries')
228
+ expect(result).toContain('visualize_relationships')
229
+ })
230
+
231
+ it('should list all 8 relationship types including causal', () => {
232
+ const result = fullInstructions()
233
+ expect(result).toContain('evolves_from')
234
+ expect(result).toContain('references')
235
+ expect(result).toContain('implements')
236
+ expect(result).toContain('clarifies')
237
+ expect(result).toContain('response_to')
238
+ expect(result).toContain('blocked_by')
239
+ expect(result).toContain('resolved')
240
+ expect(result).toContain('caused')
241
+ })
242
+
243
+ it('should document visualize_relationships optional params', () => {
244
+ const result = fullInstructions()
245
+ // visualize_relationships has all optional params
246
+ expect(result).toContain('`tags` (array)')
247
+ expect(result).toContain('`depth` (1-3, default 2)')
248
+ expect(result).toContain('`limit` (default 20)')
249
+ })
250
+ })
251
+
252
+ describe('full level — github tools', () => {
253
+ it('should list all 5 GitHub read tools', () => {
254
+ const result = fullInstructions()
255
+ expect(result).toContain('get_github_context')
256
+ expect(result).toContain('get_github_issues')
257
+ expect(result).toContain('get_github_prs')
258
+ expect(result).toContain('get_github_issue')
259
+ expect(result).toContain('get_github_pr')
260
+ })
261
+
262
+ it('should document get_repo_insights tool', () => {
263
+ const result = fullInstructions()
264
+ expect(result).toContain('get_repo_insights')
265
+ expect(result).toContain('stars/traffic/referrers/paths/all')
266
+ })
267
+
268
+ it('should document owner/repo auto-detection', () => {
269
+ const result = fullInstructions()
270
+ expect(result).toContain('detectedOwner')
271
+ expect(result).toContain('detectedRepo')
272
+ })
273
+ })
274
+
275
+ describe('full level — issue lifecycle tools', () => {
276
+ it('should include Issue Lifecycle Tools section', () => {
277
+ const result = fullInstructions()
278
+ expect(result).toContain('Issue Lifecycle Tools')
279
+ })
280
+
281
+ it('should document create_github_issue_with_entry', () => {
282
+ const result = fullInstructions()
283
+ expect(result).toContain('create_github_issue_with_entry')
284
+ expect(result).toContain('milestone_number')
285
+ expect(result).toContain('initial_status')
286
+ })
287
+
288
+ it('should document close_github_issue_with_entry', () => {
289
+ const result = fullInstructions()
290
+ expect(result).toContain('close_github_issue_with_entry')
291
+ expect(result).toContain('move_to_done')
292
+ expect(result).toContain('resolution_notes')
293
+ })
294
+ })
295
+
296
+ describe('full level — kanban tools', () => {
297
+ it('should list both kanban tools', () => {
298
+ const result = fullInstructions()
299
+ expect(result).toContain('get_kanban_board')
300
+ expect(result).toContain('move_kanban_item')
301
+ })
302
+
303
+ it('should document kanban resources', () => {
304
+ const result = fullInstructions()
305
+ expect(result).toContain('memory://kanban/{project_number}')
306
+ })
307
+
308
+ it('should document default status columns', () => {
309
+ const result = fullInstructions()
310
+ expect(result).toContain('Backlog')
311
+ expect(result).toContain('In progress')
312
+ expect(result).toContain('Done')
313
+ })
314
+ })
315
+
316
+ describe('full level — milestone tools', () => {
317
+ it('should list all 5 milestone tools', () => {
318
+ const result = fullInstructions()
319
+ expect(result).toContain('get_github_milestones')
320
+ expect(result).toContain('get_github_milestone')
321
+ expect(result).toContain('create_github_milestone')
322
+ expect(result).toContain('update_github_milestone')
323
+ expect(result).toContain('delete_github_milestone')
324
+ })
325
+
326
+ it('should document milestone resources', () => {
327
+ const result = fullInstructions()
328
+ expect(result).toContain('memory://github/milestones')
329
+ expect(result).toContain('memory://milestones/{number}')
330
+ })
331
+ })
332
+
333
+ describe('full level — admin tools', () => {
334
+ it('should list all admin tools with correct param names', () => {
335
+ const result = fullInstructions()
336
+ expect(result).toContain('backup_journal')
337
+ expect(result).toContain('list_backups')
338
+ expect(result).toContain('cleanup_backups')
339
+ expect(result).toContain('restore_backup')
340
+ expect(result).toContain('add_to_vector_index')
341
+ expect(result).toContain('rebuild_vector_index')
342
+ })
343
+
344
+ it('should use correct param name for backup_journal (name, not backup_name)', () => {
345
+ const result = fullInstructions()
346
+ expect(result).toContain('`name` (custom backup name)')
347
+ })
348
+
349
+ it('should use correct param name for restore_backup (filename, not backup_filename)', () => {
350
+ const result = fullInstructions()
351
+ expect(result).toContain('`filename`, `confirm: true`')
352
+ })
353
+
354
+ it('should document cleanup_backups keep_count param', () => {
355
+ const result = fullInstructions()
356
+ expect(result).toContain('keep_count')
357
+ })
358
+ })
359
+
360
+ describe('full level — export tools', () => {
361
+ it('should document export_entries with all optional params', () => {
362
+ const result = fullInstructions()
363
+ expect(result).toContain('export_entries')
364
+ expect(result).toContain('entry_types')
365
+ })
366
+ })
367
+
368
+ describe('full level — entry types', () => {
369
+ it('should list all 13 entry types', () => {
370
+ const result = fullInstructions()
371
+ expect(result).toContain('personal_reflection')
372
+ expect(result).toContain('project_decision')
373
+ expect(result).toContain('technical_achievement')
374
+ expect(result).toContain('bug_fix')
375
+ expect(result).toContain('feature_implementation')
376
+ expect(result).toContain('code_review')
377
+ expect(result).toContain('meeting_notes')
378
+ expect(result).toContain('learning')
379
+ expect(result).toContain('research')
380
+ expect(result).toContain('planning')
381
+ expect(result).toContain('retrospective')
382
+ expect(result).toContain('standup')
383
+ expect(result).toContain('other')
384
+ })
385
+ })
386
+
387
+ describe('full level — field notes', () => {
388
+ it('should include autoContext field note', () => {
389
+ const result = fullInstructions()
390
+ expect(result).toContain('autoContext')
391
+ })
392
+
393
+ it('should include memory://tags vs list_tags note', () => {
394
+ const result = fullInstructions()
395
+ expect(result).toContain('memory://tags` vs `list_tags')
396
+ })
397
+
398
+ it('should include tag naming guidance', () => {
399
+ const result = fullInstructions()
400
+ expect(result).toContain('Tag naming')
401
+ expect(result).toContain('merge_tags')
402
+ })
403
+
404
+ it('should include prStatus field note', () => {
405
+ const result = fullInstructions()
406
+ expect(result).toContain('prStatus')
407
+ })
408
+
409
+ it('should include restore_backup behavior note', () => {
410
+ const result = fullInstructions()
411
+ expect(result).toContain('restore_backup` behavior')
412
+ })
413
+
414
+ it('should include semantic search thresholds note with correct default', () => {
415
+ const result = fullInstructions()
416
+ expect(result).toContain('Default similarity threshold is 0.25')
417
+ })
418
+
419
+ it('should include causal relationship types note', () => {
420
+ const result = fullInstructions()
421
+ expect(result).toContain('Causal relationship types')
422
+ })
423
+
424
+ it('should include enhanced analytics note', () => {
425
+ const result = fullInstructions()
426
+ expect(result).toContain('decisionDensity')
427
+ expect(result).toContain('relationshipComplexity')
428
+ expect(result).toContain('activityTrend')
429
+ expect(result).toContain('causalMetrics')
430
+ })
431
+
432
+ it('should include importance scores note with importanceBreakdown', () => {
433
+ const result = fullInstructions()
434
+ expect(result).toContain('importanceBreakdown')
435
+ expect(result).toContain('significance (30%)')
436
+ expect(result).toContain('relationships (35%)')
437
+ })
438
+
439
+ it('should include inactiveThresholdDays note', () => {
440
+ const result = fullInstructions()
441
+ expect(result).toContain('inactiveThresholdDays')
442
+ })
443
+
444
+ it('should include GitHub metadata in entries note', () => {
445
+ const result = fullInstructions()
446
+ expect(result).toContain('GitHub metadata in entries')
447
+ expect(result).toContain('issueNumber')
448
+ expect(result).toContain('workflowRunId')
449
+ })
450
+
451
+ it('should include delete_entry soft-deleted note', () => {
452
+ const result = fullInstructions()
453
+ expect(result).toContain('delete_entry` on soft-deleted')
454
+ })
455
+ })
456
+
457
+ describe('full level — key resources table', () => {
458
+ const EXPECTED_RESOURCE_URIS = [
459
+ 'memory://health',
460
+ 'memory://briefing',
461
+ 'memory://instructions',
462
+ 'memory://statistics',
463
+ 'memory://recent',
464
+ 'memory://tags',
465
+ 'memory://significant',
466
+ 'memory://graph/recent',
467
+ 'memory://graph/actions',
468
+ 'memory://actions/recent',
469
+ 'memory://team/recent',
470
+ 'memory://github/status',
471
+ 'memory://github/milestones',
472
+ 'memory://github/insights',
473
+ 'memory://kanban/{n}',
474
+ 'memory://kanban/{n}/diagram',
475
+ 'memory://milestones/{n}',
476
+ ]
477
+
478
+ it.each(EXPECTED_RESOURCE_URIS)('should include resource URI %s', (uri) => {
479
+ const result = fullInstructions()
480
+ expect(result).toContain(uri)
481
+ })
482
+ })
483
+
484
+ describe('tool count consistency', () => {
485
+ it('should have 39 tools across all groups', () => {
486
+ const allToolNames = getAllToolNames()
487
+ expect(allToolNames.length).toBe(39)
488
+ })
489
+
490
+ it('should show correct active tool count for all tools', () => {
491
+ const result = fullInstructions()
492
+ expect(result).toContain(`Active Tools (${String(ALL_TOOLS.size)})`)
493
+ })
494
+
495
+ it('should list all 8 tool groups in active tools', () => {
496
+ const result = fullInstructions()
497
+ const groups = Object.keys(TOOL_GROUPS)
498
+ for (const group of groups) {
499
+ expect(result).toContain(`**${group}**`)
500
+ }
501
+ })
502
+ })
503
+
504
+ describe('latest entry snapshot', () => {
505
+ it('should include latest entry when provided', () => {
506
+ const result = generateInstructions(
507
+ TEST_TOOLS,
508
+ TEST_RESOURCES,
509
+ TEST_PROMPTS,
510
+ {
511
+ id: 42,
512
+ timestamp: '2026-02-27',
513
+ entryType: 'decision',
514
+ content: 'Important decision about architecture',
515
+ },
516
+ 'essential'
517
+ )
518
+ expect(result).toContain('#42')
519
+ expect(result).toContain('decision')
520
+ expect(result).toContain('Important decision')
521
+ })
522
+
523
+ it('should truncate long content with ellipsis', () => {
524
+ const longContent = 'A'.repeat(200)
525
+ const result = generateInstructions(
526
+ TEST_TOOLS,
527
+ TEST_RESOURCES,
528
+ TEST_PROMPTS,
529
+ {
530
+ id: 1,
531
+ timestamp: '2026-02-27',
532
+ entryType: 'note',
533
+ content: longContent,
534
+ },
535
+ 'essential'
536
+ )
537
+ expect(result).toContain('...')
538
+ })
539
+ })
540
+
541
+ describe('default level', () => {
542
+ it('should default to standard level', () => {
543
+ const result = generateInstructions(TEST_TOOLS, TEST_RESOURCES, TEST_PROMPTS)
544
+ // Standard includes GitHub but not tool parameter reference
545
+ expect(result).toContain('GitHub')
546
+ expect(result).not.toContain('Tool Parameter Reference')
547
+ })
548
+ })
549
+ })
@@ -0,0 +1,63 @@
1
+ import { bench, beforeAll, afterAll } from 'vitest'
2
+ import { SqliteAdapter } from '../../src/database/SqliteAdapter.js'
3
+ import * as fs from 'node:fs'
4
+ import * as path from 'node:path'
5
+
6
+ let db: SqliteAdapter
7
+ const testDbPath = path.join(process.cwd(), 'benchmark-adapter.db')
8
+
9
+ beforeAll(async () => {
10
+ db = new SqliteAdapter(testDbPath)
11
+ await db.initialize()
12
+
13
+ // Setup initial data for read benchmarks
14
+ for (let i = 0; i < 1000; i++) {
15
+ db.createEntry({
16
+ content: `Initial data entry ${i} for benchmarking read operations. This is some dummy content to make the entry a bit larger. ${Math.random().toString(36).substring(7)}`,
17
+ entryType: i % 2 === 0 ? 'personal_reflection' : 'decision',
18
+ tags: [`tag-${i % 10}`],
19
+ isPersonal: i % 3 === 0,
20
+ })
21
+ }
22
+ })
23
+
24
+ afterAll(() => {
25
+ db.close()
26
+ try {
27
+ if (fs.existsSync(testDbPath)) {
28
+ fs.unlinkSync(testDbPath)
29
+ }
30
+ } catch {
31
+ // Ignore cleanup errors
32
+ }
33
+ })
34
+
35
+ bench('createEntry', () => {
36
+ db.createEntry({
37
+ content: 'Benchmark test entry content that represents a typical journal entry size.',
38
+ entryType: 'decision',
39
+ tags: ['benchmark', 'test'],
40
+ })
41
+ })
42
+
43
+ bench('getRecentEntries (limit 50)', () => {
44
+ db.getRecentEntries(50)
45
+ })
46
+
47
+ bench('updateEntry', () => {
48
+ // Pick a random ID between 1 and 1000
49
+ const randomId = Math.floor(Math.random() * 1000) + 1
50
+ db.updateEntry(randomId, {
51
+ content: `Updated benchmark content ${Date.now()}`,
52
+ })
53
+ })
54
+
55
+ bench('searchEntries (content match)', () => {
56
+ db.searchEntries('dummy content', { limit: 20 })
57
+ })
58
+
59
+ bench('calculateImportance', () => {
60
+ // Pick a random ID between 1 and 1000
61
+ const randomId = Math.floor(Math.random() * 1000) + 1
62
+ db.calculateImportance(randomId)
63
+ })