memory-journal-mcp 4.4.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (291) hide show
  1. package/.github/workflows/codeql.yml +1 -6
  2. package/.github/workflows/docker-publish.yml +15 -49
  3. package/.github/workflows/lint-and-test.yml +1 -1
  4. package/.github/workflows/secrets-scanning.yml +4 -3
  5. package/.github/workflows/security-update.yml +3 -3
  6. package/CHANGELOG.md +213 -0
  7. package/CONTRIBUTING.md +132 -97
  8. package/DOCKER_README.md +184 -235
  9. package/Dockerfile +27 -24
  10. package/README.md +218 -190
  11. package/SECURITY.md +27 -35
  12. package/dist/cli.js +16 -1
  13. package/dist/cli.js.map +1 -1
  14. package/dist/constants/ServerInstructions.d.ts +5 -1
  15. package/dist/constants/ServerInstructions.d.ts.map +1 -1
  16. package/dist/constants/ServerInstructions.js +133 -73
  17. package/dist/constants/ServerInstructions.js.map +1 -1
  18. package/dist/constants/icons.d.ts +2 -2
  19. package/dist/constants/icons.d.ts.map +1 -1
  20. package/dist/constants/icons.js +7 -6
  21. package/dist/constants/icons.js.map +1 -1
  22. package/dist/database/SqliteAdapter.d.ts +37 -24
  23. package/dist/database/SqliteAdapter.d.ts.map +1 -1
  24. package/dist/database/SqliteAdapter.js +319 -157
  25. package/dist/database/SqliteAdapter.js.map +1 -1
  26. package/dist/database/schema.d.ts +45 -0
  27. package/dist/database/schema.d.ts.map +1 -0
  28. package/dist/database/schema.js +92 -0
  29. package/dist/database/schema.js.map +1 -0
  30. package/dist/filtering/ToolFilter.d.ts +1 -1
  31. package/dist/filtering/ToolFilter.d.ts.map +1 -1
  32. package/dist/filtering/ToolFilter.js +13 -2
  33. package/dist/filtering/ToolFilter.js.map +1 -1
  34. package/dist/github/GitHubIntegration.d.ts.map +1 -1
  35. package/dist/github/GitHubIntegration.js +1 -3
  36. package/dist/github/GitHubIntegration.js.map +1 -1
  37. package/dist/handlers/prompts/github.d.ts +12 -0
  38. package/dist/handlers/prompts/github.d.ts.map +1 -0
  39. package/dist/handlers/prompts/github.js +178 -0
  40. package/dist/handlers/prompts/github.js.map +1 -0
  41. package/dist/handlers/prompts/index.d.ts +23 -2
  42. package/dist/handlers/prompts/index.d.ts.map +1 -1
  43. package/dist/handlers/prompts/index.js +7 -432
  44. package/dist/handlers/prompts/index.js.map +1 -1
  45. package/dist/handlers/prompts/workflow.d.ts +12 -0
  46. package/dist/handlers/prompts/workflow.d.ts.map +1 -0
  47. package/dist/handlers/prompts/workflow.js +277 -0
  48. package/dist/handlers/prompts/workflow.js.map +1 -0
  49. package/dist/handlers/resources/core.d.ts +11 -0
  50. package/dist/handlers/resources/core.d.ts.map +1 -0
  51. package/dist/handlers/resources/core.js +433 -0
  52. package/dist/handlers/resources/core.js.map +1 -0
  53. package/dist/handlers/resources/github.d.ts +11 -0
  54. package/dist/handlers/resources/github.d.ts.map +1 -0
  55. package/dist/handlers/resources/github.js +314 -0
  56. package/dist/handlers/resources/github.js.map +1 -0
  57. package/dist/handlers/resources/graph.d.ts +11 -0
  58. package/dist/handlers/resources/graph.d.ts.map +1 -0
  59. package/dist/handlers/resources/graph.js +204 -0
  60. package/dist/handlers/resources/graph.js.map +1 -0
  61. package/dist/handlers/resources/index.d.ts +5 -20
  62. package/dist/handlers/resources/index.d.ts.map +1 -1
  63. package/dist/handlers/resources/index.js +16 -1278
  64. package/dist/handlers/resources/index.js.map +1 -1
  65. package/dist/handlers/resources/shared.d.ts +60 -0
  66. package/dist/handlers/resources/shared.d.ts.map +1 -0
  67. package/dist/handlers/resources/shared.js +49 -0
  68. package/dist/handlers/resources/shared.js.map +1 -0
  69. package/dist/handlers/resources/team.d.ts +13 -0
  70. package/dist/handlers/resources/team.d.ts.map +1 -0
  71. package/dist/handlers/resources/team.js +119 -0
  72. package/dist/handlers/resources/team.js.map +1 -0
  73. package/dist/handlers/resources/templates.d.ts +13 -0
  74. package/dist/handlers/resources/templates.d.ts.map +1 -0
  75. package/dist/handlers/resources/templates.js +310 -0
  76. package/dist/handlers/resources/templates.js.map +1 -0
  77. package/dist/handlers/tools/admin.d.ts +8 -0
  78. package/dist/handlers/tools/admin.d.ts.map +1 -0
  79. package/dist/handlers/tools/admin.js +270 -0
  80. package/dist/handlers/tools/admin.js.map +1 -0
  81. package/dist/handlers/tools/analytics.d.ts +8 -0
  82. package/dist/handlers/tools/analytics.d.ts.map +1 -0
  83. package/dist/handlers/tools/analytics.js +256 -0
  84. package/dist/handlers/tools/analytics.js.map +1 -0
  85. package/dist/handlers/tools/backup.d.ts +8 -0
  86. package/dist/handlers/tools/backup.d.ts.map +1 -0
  87. package/dist/handlers/tools/backup.js +224 -0
  88. package/dist/handlers/tools/backup.js.map +1 -0
  89. package/dist/handlers/tools/core.d.ts +9 -0
  90. package/dist/handlers/tools/core.d.ts.map +1 -0
  91. package/dist/handlers/tools/core.js +326 -0
  92. package/dist/handlers/tools/core.js.map +1 -0
  93. package/dist/handlers/tools/export.d.ts +8 -0
  94. package/dist/handlers/tools/export.d.ts.map +1 -0
  95. package/dist/handlers/tools/export.js +89 -0
  96. package/dist/handlers/tools/export.js.map +1 -0
  97. package/dist/handlers/tools/github/helpers.d.ts +34 -0
  98. package/dist/handlers/tools/github/helpers.d.ts.map +1 -0
  99. package/dist/handlers/tools/github/helpers.js +52 -0
  100. package/dist/handlers/tools/github/helpers.js.map +1 -0
  101. package/dist/handlers/tools/github/insights-tools.d.ts +8 -0
  102. package/dist/handlers/tools/github/insights-tools.d.ts.map +1 -0
  103. package/dist/handlers/tools/github/insights-tools.js +104 -0
  104. package/dist/handlers/tools/github/insights-tools.js.map +1 -0
  105. package/dist/handlers/tools/github/issue-tools.d.ts +8 -0
  106. package/dist/handlers/tools/github/issue-tools.d.ts.map +1 -0
  107. package/dist/handlers/tools/github/issue-tools.js +359 -0
  108. package/dist/handlers/tools/github/issue-tools.js.map +1 -0
  109. package/dist/handlers/tools/github/kanban-tools.d.ts +8 -0
  110. package/dist/handlers/tools/github/kanban-tools.d.ts.map +1 -0
  111. package/dist/handlers/tools/github/kanban-tools.js +108 -0
  112. package/dist/handlers/tools/github/kanban-tools.js.map +1 -0
  113. package/dist/handlers/tools/github/milestone-tools.d.ts +9 -0
  114. package/dist/handlers/tools/github/milestone-tools.d.ts.map +1 -0
  115. package/dist/handlers/tools/github/milestone-tools.js +302 -0
  116. package/dist/handlers/tools/github/milestone-tools.js.map +1 -0
  117. package/dist/handlers/tools/github/mutation-tools.d.ts +12 -0
  118. package/dist/handlers/tools/github/mutation-tools.d.ts.map +1 -0
  119. package/dist/handlers/tools/github/mutation-tools.js +15 -0
  120. package/dist/handlers/tools/github/mutation-tools.js.map +1 -0
  121. package/dist/handlers/tools/github/read-tools.d.ts +8 -0
  122. package/dist/handlers/tools/github/read-tools.d.ts.map +1 -0
  123. package/dist/handlers/tools/github/read-tools.js +260 -0
  124. package/dist/handlers/tools/github/read-tools.js.map +1 -0
  125. package/dist/handlers/tools/github/schemas.d.ts +467 -0
  126. package/dist/handlers/tools/github/schemas.d.ts.map +1 -0
  127. package/dist/handlers/tools/github/schemas.js +335 -0
  128. package/dist/handlers/tools/github/schemas.js.map +1 -0
  129. package/dist/handlers/tools/github.d.ts +14 -0
  130. package/dist/handlers/tools/github.d.ts.map +1 -0
  131. package/dist/handlers/tools/github.js +28 -0
  132. package/dist/handlers/tools/github.js.map +1 -0
  133. package/dist/handlers/tools/index.d.ts +15 -20
  134. package/dist/handlers/tools/index.d.ts.map +1 -1
  135. package/dist/handlers/tools/index.js +117 -2909
  136. package/dist/handlers/tools/index.js.map +1 -1
  137. package/dist/handlers/tools/relationships.d.ts +8 -0
  138. package/dist/handlers/tools/relationships.d.ts.map +1 -0
  139. package/dist/handlers/tools/relationships.js +308 -0
  140. package/dist/handlers/tools/relationships.js.map +1 -0
  141. package/dist/handlers/tools/schemas.d.ts +108 -0
  142. package/dist/handlers/tools/schemas.d.ts.map +1 -0
  143. package/dist/handlers/tools/schemas.js +122 -0
  144. package/dist/handlers/tools/schemas.js.map +1 -0
  145. package/dist/handlers/tools/search.d.ts +8 -0
  146. package/dist/handlers/tools/search.d.ts.map +1 -0
  147. package/dist/handlers/tools/search.js +282 -0
  148. package/dist/handlers/tools/search.js.map +1 -0
  149. package/dist/handlers/tools/team.d.ts +11 -0
  150. package/dist/handlers/tools/team.d.ts.map +1 -0
  151. package/dist/handlers/tools/team.js +239 -0
  152. package/dist/handlers/tools/team.js.map +1 -0
  153. package/dist/server/McpServer.d.ts +4 -0
  154. package/dist/server/McpServer.d.ts.map +1 -1
  155. package/dist/server/McpServer.js +48 -297
  156. package/dist/server/McpServer.js.map +1 -1
  157. package/dist/server/Scheduler.d.ts +91 -0
  158. package/dist/server/Scheduler.d.ts.map +1 -0
  159. package/dist/server/Scheduler.js +201 -0
  160. package/dist/server/Scheduler.js.map +1 -0
  161. package/dist/transports/http.d.ts +66 -0
  162. package/dist/transports/http.d.ts.map +1 -0
  163. package/dist/transports/http.js +519 -0
  164. package/dist/transports/http.js.map +1 -0
  165. package/dist/types/entities.d.ts +101 -0
  166. package/dist/types/entities.d.ts.map +1 -0
  167. package/dist/types/entities.js +5 -0
  168. package/dist/types/entities.js.map +1 -0
  169. package/dist/types/filtering.d.ts +34 -0
  170. package/dist/types/filtering.d.ts.map +1 -0
  171. package/dist/types/filtering.js +5 -0
  172. package/dist/types/filtering.js.map +1 -0
  173. package/dist/types/github.d.ts +166 -0
  174. package/dist/types/github.d.ts.map +1 -0
  175. package/dist/types/github.js +5 -0
  176. package/dist/types/github.js.map +1 -0
  177. package/dist/types/index.d.ts +35 -292
  178. package/dist/types/index.d.ts.map +1 -1
  179. package/dist/types/index.js +2 -2
  180. package/dist/types/index.js.map +1 -1
  181. package/dist/utils/error-helpers.d.ts +37 -0
  182. package/dist/utils/error-helpers.d.ts.map +1 -0
  183. package/dist/utils/error-helpers.js +47 -0
  184. package/dist/utils/error-helpers.js.map +1 -0
  185. package/dist/utils/logger.d.ts.map +1 -1
  186. package/dist/utils/logger.js +6 -3
  187. package/dist/utils/logger.js.map +1 -1
  188. package/dist/utils/security-utils.d.ts +0 -21
  189. package/dist/utils/security-utils.d.ts.map +1 -1
  190. package/dist/utils/security-utils.js +0 -47
  191. package/dist/utils/security-utils.js.map +1 -1
  192. package/dist/vector/VectorSearchManager.d.ts.map +1 -1
  193. package/dist/vector/VectorSearchManager.js +9 -32
  194. package/dist/vector/VectorSearchManager.js.map +1 -1
  195. package/docker-compose.yml +11 -2
  196. package/hooks/README.md +107 -0
  197. package/hooks/cursor/hooks.json +10 -0
  198. package/hooks/cursor/memory-journal.mdc +22 -0
  199. package/hooks/cursor/session-end.sh +19 -0
  200. package/hooks/kilo-code/session-end-mode.json +11 -0
  201. package/hooks/kiro/session-end.md +13 -0
  202. package/mcp-config-example.json +1 -0
  203. package/package.json +11 -9
  204. package/playwright.config.ts +29 -0
  205. package/releases/v4.5.0.md +116 -0
  206. package/releases/v5.0.0.md +105 -0
  207. package/scripts/generate-server-instructions.ts +176 -0
  208. package/scripts/server-instructions-function-body.ts +77 -0
  209. package/server.json +3 -3
  210. package/src/cli.ts +45 -1
  211. package/src/constants/ServerInstructions.ts +133 -73
  212. package/src/constants/icons.ts +8 -7
  213. package/src/constants/server-instructions.md +268 -0
  214. package/src/database/SqliteAdapter.ts +358 -192
  215. package/src/database/schema.ts +125 -0
  216. package/src/filtering/ToolFilter.ts +13 -2
  217. package/src/github/GitHubIntegration.ts +1 -3
  218. package/src/handlers/prompts/github.ts +209 -0
  219. package/src/handlers/prompts/index.ts +10 -499
  220. package/src/handlers/prompts/workflow.ts +314 -0
  221. package/src/handlers/resources/core.ts +528 -0
  222. package/src/handlers/resources/github.ts +358 -0
  223. package/src/handlers/resources/graph.ts +254 -0
  224. package/src/handlers/resources/index.ts +23 -1570
  225. package/src/handlers/resources/shared.ts +103 -0
  226. package/src/handlers/resources/team.ts +133 -0
  227. package/src/handlers/resources/templates.ts +374 -0
  228. package/src/handlers/tools/admin.ts +285 -0
  229. package/src/handlers/tools/analytics.ts +301 -0
  230. package/src/handlers/tools/backup.ts +242 -0
  231. package/src/handlers/tools/core.ts +350 -0
  232. package/src/handlers/tools/export.ts +115 -0
  233. package/src/handlers/tools/github/helpers.ts +86 -0
  234. package/src/handlers/tools/github/insights-tools.ts +119 -0
  235. package/src/handlers/tools/github/issue-tools.ts +439 -0
  236. package/src/handlers/tools/github/kanban-tools.ts +134 -0
  237. package/src/handlers/tools/github/milestone-tools.ts +392 -0
  238. package/src/handlers/tools/github/mutation-tools.ts +17 -0
  239. package/src/handlers/tools/github/read-tools.ts +328 -0
  240. package/src/handlers/tools/github/schemas.ts +369 -0
  241. package/src/handlers/tools/github.ts +36 -0
  242. package/src/handlers/tools/index.ts +144 -3325
  243. package/src/handlers/tools/relationships.ts +358 -0
  244. package/src/handlers/tools/schemas.ts +132 -0
  245. package/src/handlers/tools/search.ts +343 -0
  246. package/src/handlers/tools/team.ts +273 -0
  247. package/src/server/McpServer.ts +63 -358
  248. package/src/server/Scheduler.ts +278 -0
  249. package/src/transports/http.ts +635 -0
  250. package/src/types/entities.ts +145 -0
  251. package/src/types/filtering.ts +54 -0
  252. package/src/types/github.ts +180 -0
  253. package/src/types/index.ts +67 -375
  254. package/src/utils/error-helpers.ts +52 -0
  255. package/src/utils/logger.ts +6 -3
  256. package/src/utils/security-utils.ts +0 -52
  257. package/src/vector/VectorSearchManager.ts +9 -33
  258. package/tests/constants/icons.test.ts +1 -2
  259. package/tests/constants/server-instructions.test.ts +30 -4
  260. package/tests/database/sqlite-adapter.test.ts +91 -7
  261. package/tests/e2e/auth.spec.ts +154 -0
  262. package/tests/e2e/health.spec.ts +63 -0
  263. package/tests/e2e/protocols.spec.ts +134 -0
  264. package/tests/e2e/resources.spec.ts +103 -0
  265. package/tests/e2e/scheduler.spec.ts +79 -0
  266. package/tests/e2e/security.spec.ts +91 -0
  267. package/tests/e2e/sessions.spec.ts +95 -0
  268. package/tests/e2e/stateless.spec.ts +121 -0
  269. package/tests/e2e/tools.spec.ts +111 -0
  270. package/tests/filtering/tool-filter.test.ts +46 -0
  271. package/tests/handlers/error-path-coverage.test.ts +324 -0
  272. package/tests/handlers/github-resource-handlers.test.ts +453 -0
  273. package/tests/handlers/github-tool-handlers.test.ts +899 -0
  274. package/tests/handlers/prompt-handler-coverage.test.ts +106 -0
  275. package/tests/handlers/prompt-handlers.test.ts +40 -0
  276. package/tests/handlers/resource-handler-coverage.test.ts +181 -0
  277. package/tests/handlers/resource-handlers.test.ts +33 -9
  278. package/tests/handlers/search-tool-handlers.test.ts +272 -0
  279. package/tests/handlers/targeted-gap-closure.test.ts +387 -0
  280. package/tests/handlers/team-resource-handlers.test.ts +156 -0
  281. package/tests/handlers/team-tool-handlers.test.ts +301 -0
  282. package/tests/handlers/tool-handler-coverage.test.ts +469 -0
  283. package/tests/handlers/tool-handlers.test.ts +2 -2
  284. package/tests/security/sql-injection.test.ts +3 -54
  285. package/tests/server/mcp-server.test.ts +503 -8
  286. package/tests/server/scheduler.test.ts +400 -0
  287. package/tests/transports/http-transport.test.ts +620 -0
  288. package/tests/vector/vector-search-manager.test.ts +60 -0
  289. package/vitest.config.ts +4 -1
  290. package/.memory-journal-team.db +0 -0
  291. package/.vscode/settings.json +0 -84
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Relationships Tool Group - 2 tools
3
+ *
4
+ * Tools: link_entries, visualize_relationships
5
+ */
6
+
7
+ import { z } from 'zod'
8
+ import type { ToolDefinition, ToolContext, RelationshipType } from '../../types/index.js'
9
+ import { formatHandlerError } from '../../utils/error-helpers.js'
10
+ import { RelationshipOutputSchema } from './schemas.js'
11
+
12
+ // ============================================================================
13
+ // Input Schemas
14
+ // ============================================================================
15
+
16
+ /** Strict schema — used inside handler for structured Zod errors */
17
+ const LinkEntriesSchema = z.object({
18
+ from_entry_id: z.number(),
19
+ to_entry_id: z.number(),
20
+ relationship_type: z
21
+ .enum([
22
+ 'evolves_from',
23
+ 'references',
24
+ 'implements',
25
+ 'clarifies',
26
+ 'response_to',
27
+ 'blocked_by',
28
+ 'resolved',
29
+ 'caused',
30
+ ])
31
+ .optional()
32
+ .default('references'),
33
+ description: z.string().optional(),
34
+ })
35
+
36
+ /** Relaxed schema — passed to SDK inputSchema so Zod enum errors reach the handler */
37
+ const LinkEntriesSchemaMcp = z.object({
38
+ from_entry_id: z.number(),
39
+ to_entry_id: z.number(),
40
+ relationship_type: z.string().optional().default('references'),
41
+ description: z.string().optional(),
42
+ })
43
+
44
+ const VisualizeInputSchema = z.object({
45
+ entry_id: z
46
+ .number()
47
+ .optional()
48
+ .describe('Specific entry ID to visualize (shows connected entries)'),
49
+ tags: z.array(z.string()).optional().describe('Filter entries by tags'),
50
+ depth: z.number().min(1).max(3).optional().default(2).describe('Relationship traversal depth'),
51
+ limit: z.number().max(500).optional().default(20).describe('Maximum entries to include'),
52
+ })
53
+
54
+ // ============================================================================
55
+ // Output Schemas
56
+ // ============================================================================
57
+
58
+ const LinkEntriesOutputSchema = z.object({
59
+ success: z.boolean().optional(),
60
+ relationship: RelationshipOutputSchema.optional(),
61
+ duplicate: z.boolean().optional().describe('True if relationship already existed'),
62
+ message: z.string().optional().describe('Additional context about the operation'),
63
+ error: z.string().optional(),
64
+ })
65
+
66
+ const VisualizationOutputSchema = z.object({
67
+ entry_count: z.number().optional(),
68
+ relationship_count: z.number().optional(),
69
+ root_entry: z.number().nullable().optional(),
70
+ depth: z.number().optional(),
71
+ mermaid: z.string().nullable().optional(),
72
+ message: z.string().optional(),
73
+ legend: z
74
+ .object({
75
+ blue: z.string(),
76
+ orange: z.string(),
77
+ arrows: z.record(z.string(), z.string()),
78
+ })
79
+ .optional(),
80
+ success: z.boolean().optional(),
81
+ error: z.string().optional(),
82
+ })
83
+
84
+ // ============================================================================
85
+ // Tool Definitions
86
+ // ============================================================================
87
+
88
+ export function getRelationshipTools(context: ToolContext): ToolDefinition[] {
89
+ const { db } = context
90
+ return [
91
+ {
92
+ name: 'link_entries',
93
+ title: 'Link Entries',
94
+ description: 'Create a relationship between two journal entries',
95
+ group: 'relationships',
96
+ inputSchema: LinkEntriesSchemaMcp,
97
+ outputSchema: LinkEntriesOutputSchema,
98
+ annotations: { readOnlyHint: false, idempotentHint: false },
99
+ handler: (params: unknown) => {
100
+ try {
101
+ const input = LinkEntriesSchema.parse(params)
102
+
103
+ // Check for existing duplicate relationship
104
+ const existingRelationships = db.getRelationships(input.from_entry_id)
105
+ const existing = existingRelationships.find(
106
+ (r) =>
107
+ r.toEntryId === input.to_entry_id &&
108
+ r.relationshipType === input.relationship_type
109
+ )
110
+
111
+ if (existing) {
112
+ return {
113
+ success: true,
114
+ relationship: existing,
115
+ duplicate: true,
116
+ message: 'Relationship already exists',
117
+ }
118
+ }
119
+
120
+ // linkEntries throws for nonexistent entries
121
+ const relationship = db.linkEntries(
122
+ input.from_entry_id,
123
+ input.to_entry_id,
124
+ input.relationship_type as RelationshipType,
125
+ input.description
126
+ )
127
+ return { success: true, relationship }
128
+ } catch (error) {
129
+ // Domain errors from db.linkEntries (nonexistent entries)
130
+ const input = (() => {
131
+ try {
132
+ return LinkEntriesSchema.parse(params)
133
+ } catch {
134
+ // If parse itself failed, use formatHandlerError
135
+ return null
136
+ }
137
+ })()
138
+
139
+ if (input) {
140
+ return {
141
+ success: false,
142
+ relationship: {
143
+ id: 0,
144
+ fromEntryId: input.from_entry_id,
145
+ toEntryId: input.to_entry_id,
146
+ relationshipType: input.relationship_type,
147
+ description: input.description ?? null,
148
+ createdAt: '',
149
+ },
150
+ message: error instanceof Error ? error.message : 'Unknown error',
151
+ }
152
+ }
153
+ return formatHandlerError(error)
154
+ }
155
+ },
156
+ },
157
+ {
158
+ name: 'visualize_relationships',
159
+ title: 'Visualize Relationships',
160
+ description: 'Generate a Mermaid diagram visualization of entry relationships',
161
+ group: 'relationships',
162
+ inputSchema: VisualizeInputSchema,
163
+ outputSchema: VisualizationOutputSchema,
164
+ annotations: { readOnlyHint: true, idempotentHint: true },
165
+ handler: (params: unknown) => {
166
+ try {
167
+ const input = VisualizeInputSchema.parse(params)
168
+
169
+ const rawDb = db.getRawDb()
170
+ let entriesResult
171
+
172
+ if (input.entry_id !== undefined) {
173
+ const entry = db.getEntryById(input.entry_id)
174
+ if (!entry) {
175
+ return {
176
+ entry_count: 0,
177
+ relationship_count: 0,
178
+ root_entry: input.entry_id,
179
+ depth: input.depth,
180
+ mermaid: null,
181
+ message: `Entry ${String(input.entry_id)} not found`,
182
+ }
183
+ }
184
+
185
+ entriesResult = rawDb.exec(
186
+ `
187
+ WITH RECURSIVE connected_entries(id, distance) AS (
188
+ SELECT id, 0 FROM memory_journal WHERE id = ? AND deleted_at IS NULL
189
+ UNION
190
+ SELECT DISTINCT
191
+ CASE
192
+ WHEN r.from_entry_id = ce.id THEN r.to_entry_id
193
+ ELSE r.from_entry_id
194
+ END,
195
+ ce.distance + 1
196
+ FROM connected_entries ce
197
+ JOIN relationships r ON r.from_entry_id = ce.id OR r.to_entry_id = ce.id
198
+ WHERE ce.distance < ?
199
+ )
200
+ SELECT DISTINCT mj.id, mj.entry_type, mj.content, mj.is_personal
201
+ FROM memory_journal mj
202
+ JOIN connected_entries ce ON mj.id = ce.id
203
+ WHERE mj.deleted_at IS NULL
204
+ LIMIT ?
205
+ `,
206
+ [input.entry_id, input.depth, input.limit]
207
+ )
208
+ } else if (input.tags && input.tags.length > 0) {
209
+ const placeholders = input.tags.map(() => '?').join(',')
210
+ entriesResult = rawDb.exec(
211
+ `
212
+ SELECT DISTINCT mj.id, mj.entry_type, mj.content, mj.is_personal
213
+ FROM memory_journal mj
214
+ WHERE mj.deleted_at IS NULL
215
+ AND mj.id IN (
216
+ SELECT et.entry_id FROM entry_tags et
217
+ JOIN tags t ON et.tag_id = t.id
218
+ WHERE t.name IN (${placeholders})
219
+ )
220
+ LIMIT ?
221
+ `,
222
+ [...input.tags, input.limit]
223
+ )
224
+ } else {
225
+ entriesResult = rawDb.exec(
226
+ `
227
+ SELECT DISTINCT mj.id, mj.entry_type, mj.content, mj.is_personal
228
+ FROM memory_journal mj
229
+ WHERE mj.deleted_at IS NULL
230
+ AND mj.id IN (
231
+ SELECT DISTINCT from_entry_id FROM relationships
232
+ UNION
233
+ SELECT DISTINCT to_entry_id FROM relationships
234
+ )
235
+ ORDER BY mj.id DESC
236
+ LIMIT ?
237
+ `,
238
+ [input.limit]
239
+ )
240
+ }
241
+
242
+ if (!entriesResult[0] || entriesResult[0].values.length === 0) {
243
+ return {
244
+ entry_count: 0,
245
+ relationship_count: 0,
246
+ root_entry: input.entry_id ?? null,
247
+ depth: input.depth,
248
+ mermaid: null,
249
+ message: 'No entries found with relationships matching your criteria',
250
+ }
251
+ }
252
+
253
+ // Build entries map
254
+ const entries: Record<
255
+ number,
256
+ {
257
+ id: number
258
+ entry_type: string
259
+ content: string
260
+ is_personal: boolean
261
+ }
262
+ > = {}
263
+ const cols = entriesResult[0].columns
264
+ for (const row of entriesResult[0].values) {
265
+ const id = row[cols.indexOf('id')] as number
266
+ entries[id] = {
267
+ id,
268
+ entry_type: row[cols.indexOf('entry_type')] as string,
269
+ content: row[cols.indexOf('content')] as string,
270
+ is_personal: Boolean(row[cols.indexOf('is_personal')]),
271
+ }
272
+ }
273
+
274
+ const entryIds = Object.keys(entries).map(Number)
275
+ const placeholders = entryIds.map(() => '?').join(',')
276
+
277
+ const relsResult = rawDb.exec(
278
+ `
279
+ SELECT from_entry_id, to_entry_id, relationship_type
280
+ FROM relationships
281
+ WHERE from_entry_id IN (${placeholders})
282
+ AND to_entry_id IN (${placeholders})
283
+ `,
284
+ [...entryIds, ...entryIds]
285
+ )
286
+
287
+ const relationships = relsResult[0]?.values ?? []
288
+
289
+ // Generate Mermaid diagram
290
+ let mermaid = '```mermaid\\ngraph TD\\n'
291
+
292
+ for (const [idStr, entry] of Object.entries(entries)) {
293
+ let contentPreview = entry.content.slice(0, 40).replace(/\\n/g, ' ')
294
+ if (entry.content.length > 40) contentPreview += '...'
295
+ contentPreview = contentPreview
296
+ .replace(/"/g, "'")
297
+ .replace(/\[/g, '(')
298
+ .replace(/\]/g, ')')
299
+ const entryTypeShort = entry.entry_type.slice(0, 20)
300
+ mermaid += ` E${idStr}["#${idStr}: ${contentPreview}<br/>${entryTypeShort}"]\\n`
301
+ }
302
+
303
+ mermaid += '\\n'
304
+
305
+ const relSymbols: Record<string, string> = {
306
+ references: '-->',
307
+ implements: '==>',
308
+ clarifies: '-.->',
309
+ evolves_from: '-->',
310
+ response_to: '<-->',
311
+ blocked_by: '--x',
312
+ resolved: '==>',
313
+ caused: '-.->',
314
+ }
315
+
316
+ for (const rel of relationships) {
317
+ const fromId = rel[0] as number
318
+ const toId = rel[1] as number
319
+ const relType = rel[2] as string
320
+ const arrow = relSymbols[relType] ?? '-->'
321
+ mermaid += ` E${String(fromId)} ${arrow}|${relType}| E${String(toId)}\\n`
322
+ }
323
+
324
+ mermaid += '\\n'
325
+ for (const [idStr, entry] of Object.entries(entries)) {
326
+ if (entry.is_personal) {
327
+ mermaid += ` style E${idStr} fill:#E3F2FD\\n`
328
+ } else {
329
+ mermaid += ` style E${idStr} fill:#FFF3E0\\n`
330
+ }
331
+ }
332
+ mermaid += '```'
333
+
334
+ return {
335
+ entry_count: Object.keys(entries).length,
336
+ relationship_count: relationships.length,
337
+ root_entry: input.entry_id ?? null,
338
+ depth: input.depth,
339
+ mermaid,
340
+ legend: {
341
+ blue: 'Personal entries',
342
+ orange: 'Project entries',
343
+ arrows: {
344
+ '-->': 'references / evolves_from',
345
+ '==>': 'implements / resolved',
346
+ '-.->': 'clarifies / caused',
347
+ '<-->': 'response_to',
348
+ '--x': 'blocked_by',
349
+ },
350
+ },
351
+ }
352
+ } catch (err) {
353
+ return formatHandlerError(err)
354
+ }
355
+ },
356
+ },
357
+ ]
358
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Shared Zod Schemas for Tool Handlers
3
+ *
4
+ * Contains cross-group schemas used by multiple tool modules:
5
+ * - EntryOutputSchema: used by core, search, export, admin
6
+ * - EntriesListOutputSchema: used by search, core
7
+ * - RelationshipOutputSchema: used by core, relationships
8
+ * - TagOutputSchema: used by core, analytics
9
+ * - ImportanceBreakdownSchema: used by core
10
+ * - Shared constants: ENTRY_TYPES, SIGNIFICANCE_TYPES, DATE_FORMAT_REGEX
11
+ *
12
+ * Group-specific output schemas stay colocated with their group files.
13
+ */
14
+
15
+ import { z } from 'zod'
16
+
17
+ // ============================================================================
18
+ // Shared Constants
19
+ // ============================================================================
20
+
21
+ /**
22
+ * Valid entry types (matches EntryType union in types/index.ts)
23
+ */
24
+ export const ENTRY_TYPES = [
25
+ 'personal_reflection',
26
+ 'project_decision',
27
+ 'technical_achievement',
28
+ 'bug_fix',
29
+ 'feature_implementation',
30
+ 'code_review',
31
+ 'meeting_notes',
32
+ 'learning',
33
+ 'research',
34
+ 'planning',
35
+ 'retrospective',
36
+ 'standup',
37
+ 'technical_note',
38
+ 'development_note',
39
+ 'enhancement',
40
+ 'milestone',
41
+ 'system_integration_test',
42
+ 'test_entry',
43
+ 'other',
44
+ ] as const
45
+
46
+ /**
47
+ * Valid significance types (matches SignificanceType union in types/index.ts)
48
+ */
49
+ export const SIGNIFICANCE_TYPES = [
50
+ 'milestone',
51
+ 'breakthrough',
52
+ 'technical_breakthrough',
53
+ 'decision',
54
+ 'lesson_learned',
55
+ 'blocker_resolved',
56
+ 'release',
57
+ ] as const
58
+
59
+ /** YYYY-MM-DD date format regex */
60
+ export const DATE_FORMAT_REGEX = /^\d{4}-\d{2}-\d{2}$/
61
+ export const DATE_FORMAT_MESSAGE = 'Date must be YYYY-MM-DD format'
62
+
63
+ // ============================================================================
64
+ // Cross-Group Output Schemas
65
+ // ============================================================================
66
+
67
+ /**
68
+ * Schema for a journal entry in output responses.
69
+ * Uses camelCase to match actual database output format.
70
+ */
71
+ export const EntryOutputSchema = z.object({
72
+ id: z.number(),
73
+ content: z.string(),
74
+ entryType: z.string(),
75
+ isPersonal: z.boolean(),
76
+ timestamp: z.string(),
77
+ tags: z.array(z.string()).optional(),
78
+ significanceType: z.string().nullable().optional(),
79
+ autoContext: z.string().nullable().optional(),
80
+ deletedAt: z.string().nullable().optional(),
81
+ projectNumber: z.number().nullable().optional(),
82
+ projectOwner: z.string().nullable().optional(),
83
+ issueNumber: z.number().nullable().optional(),
84
+ issueUrl: z.string().nullable().optional(),
85
+ prNumber: z.number().nullable().optional(),
86
+ prUrl: z.string().nullable().optional(),
87
+ prStatus: z.string().nullable().optional(),
88
+ workflowRunId: z.number().nullable().optional(),
89
+ workflowName: z.string().nullable().optional(),
90
+ workflowStatus: z.string().nullable().optional(),
91
+ })
92
+
93
+ /**
94
+ * Schema for list of entries with count.
95
+ * Used by get_recent_entries, search_entries, search_by_date_range.
96
+ */
97
+ export const EntriesListOutputSchema = z.object({
98
+ entries: z.array(EntryOutputSchema).optional(),
99
+ count: z.number().optional(),
100
+ success: z.boolean().optional(),
101
+ error: z.string().optional(),
102
+ })
103
+
104
+ /**
105
+ * Schema for a relationship between entries.
106
+ */
107
+ export const RelationshipOutputSchema = z.object({
108
+ id: z.number(),
109
+ fromEntryId: z.number(),
110
+ toEntryId: z.number(),
111
+ relationshipType: z.string(),
112
+ description: z.string().nullable().optional(),
113
+ createdAt: z.string(),
114
+ })
115
+
116
+ /**
117
+ * Importance score breakdown schema.
118
+ */
119
+ export const ImportanceBreakdownSchema = z.object({
120
+ significance: z.number(),
121
+ relationships: z.number(),
122
+ causal: z.number(),
123
+ recency: z.number(),
124
+ })
125
+
126
+ /**
127
+ * Tag with usage count.
128
+ */
129
+ export const TagOutputSchema = z.object({
130
+ name: z.string(),
131
+ count: z.number().nullable(),
132
+ })