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.
- package/.github/workflows/codeql.yml +1 -6
- package/.github/workflows/docker-publish.yml +15 -49
- package/.github/workflows/lint-and-test.yml +1 -1
- package/.github/workflows/secrets-scanning.yml +4 -3
- package/.github/workflows/security-update.yml +3 -3
- package/CHANGELOG.md +213 -0
- package/CONTRIBUTING.md +132 -97
- package/DOCKER_README.md +184 -235
- package/Dockerfile +27 -24
- package/README.md +218 -190
- package/SECURITY.md +27 -35
- package/dist/cli.js +16 -1
- package/dist/cli.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +5 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +133 -73
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/constants/icons.d.ts +2 -2
- package/dist/constants/icons.d.ts.map +1 -1
- package/dist/constants/icons.js +7 -6
- package/dist/constants/icons.js.map +1 -1
- package/dist/database/SqliteAdapter.d.ts +37 -24
- package/dist/database/SqliteAdapter.d.ts.map +1 -1
- package/dist/database/SqliteAdapter.js +319 -157
- package/dist/database/SqliteAdapter.js.map +1 -1
- package/dist/database/schema.d.ts +45 -0
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/database/schema.js +92 -0
- package/dist/database/schema.js.map +1 -0
- package/dist/filtering/ToolFilter.d.ts +1 -1
- package/dist/filtering/ToolFilter.d.ts.map +1 -1
- package/dist/filtering/ToolFilter.js +13 -2
- package/dist/filtering/ToolFilter.js.map +1 -1
- package/dist/github/GitHubIntegration.d.ts.map +1 -1
- package/dist/github/GitHubIntegration.js +1 -3
- package/dist/github/GitHubIntegration.js.map +1 -1
- package/dist/handlers/prompts/github.d.ts +12 -0
- package/dist/handlers/prompts/github.d.ts.map +1 -0
- package/dist/handlers/prompts/github.js +178 -0
- package/dist/handlers/prompts/github.js.map +1 -0
- package/dist/handlers/prompts/index.d.ts +23 -2
- package/dist/handlers/prompts/index.d.ts.map +1 -1
- package/dist/handlers/prompts/index.js +7 -432
- package/dist/handlers/prompts/index.js.map +1 -1
- package/dist/handlers/prompts/workflow.d.ts +12 -0
- package/dist/handlers/prompts/workflow.d.ts.map +1 -0
- package/dist/handlers/prompts/workflow.js +277 -0
- package/dist/handlers/prompts/workflow.js.map +1 -0
- package/dist/handlers/resources/core.d.ts +11 -0
- package/dist/handlers/resources/core.d.ts.map +1 -0
- package/dist/handlers/resources/core.js +433 -0
- package/dist/handlers/resources/core.js.map +1 -0
- package/dist/handlers/resources/github.d.ts +11 -0
- package/dist/handlers/resources/github.d.ts.map +1 -0
- package/dist/handlers/resources/github.js +314 -0
- package/dist/handlers/resources/github.js.map +1 -0
- package/dist/handlers/resources/graph.d.ts +11 -0
- package/dist/handlers/resources/graph.d.ts.map +1 -0
- package/dist/handlers/resources/graph.js +204 -0
- package/dist/handlers/resources/graph.js.map +1 -0
- package/dist/handlers/resources/index.d.ts +5 -20
- package/dist/handlers/resources/index.d.ts.map +1 -1
- package/dist/handlers/resources/index.js +16 -1278
- package/dist/handlers/resources/index.js.map +1 -1
- package/dist/handlers/resources/shared.d.ts +60 -0
- package/dist/handlers/resources/shared.d.ts.map +1 -0
- package/dist/handlers/resources/shared.js +49 -0
- package/dist/handlers/resources/shared.js.map +1 -0
- package/dist/handlers/resources/team.d.ts +13 -0
- package/dist/handlers/resources/team.d.ts.map +1 -0
- package/dist/handlers/resources/team.js +119 -0
- package/dist/handlers/resources/team.js.map +1 -0
- package/dist/handlers/resources/templates.d.ts +13 -0
- package/dist/handlers/resources/templates.d.ts.map +1 -0
- package/dist/handlers/resources/templates.js +310 -0
- package/dist/handlers/resources/templates.js.map +1 -0
- package/dist/handlers/tools/admin.d.ts +8 -0
- package/dist/handlers/tools/admin.d.ts.map +1 -0
- package/dist/handlers/tools/admin.js +270 -0
- package/dist/handlers/tools/admin.js.map +1 -0
- package/dist/handlers/tools/analytics.d.ts +8 -0
- package/dist/handlers/tools/analytics.d.ts.map +1 -0
- package/dist/handlers/tools/analytics.js +256 -0
- package/dist/handlers/tools/analytics.js.map +1 -0
- package/dist/handlers/tools/backup.d.ts +8 -0
- package/dist/handlers/tools/backup.d.ts.map +1 -0
- package/dist/handlers/tools/backup.js +224 -0
- package/dist/handlers/tools/backup.js.map +1 -0
- package/dist/handlers/tools/core.d.ts +9 -0
- package/dist/handlers/tools/core.d.ts.map +1 -0
- package/dist/handlers/tools/core.js +326 -0
- package/dist/handlers/tools/core.js.map +1 -0
- package/dist/handlers/tools/export.d.ts +8 -0
- package/dist/handlers/tools/export.d.ts.map +1 -0
- package/dist/handlers/tools/export.js +89 -0
- package/dist/handlers/tools/export.js.map +1 -0
- package/dist/handlers/tools/github/helpers.d.ts +34 -0
- package/dist/handlers/tools/github/helpers.d.ts.map +1 -0
- package/dist/handlers/tools/github/helpers.js +52 -0
- package/dist/handlers/tools/github/helpers.js.map +1 -0
- package/dist/handlers/tools/github/insights-tools.d.ts +8 -0
- package/dist/handlers/tools/github/insights-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/insights-tools.js +104 -0
- package/dist/handlers/tools/github/insights-tools.js.map +1 -0
- package/dist/handlers/tools/github/issue-tools.d.ts +8 -0
- package/dist/handlers/tools/github/issue-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/issue-tools.js +359 -0
- package/dist/handlers/tools/github/issue-tools.js.map +1 -0
- package/dist/handlers/tools/github/kanban-tools.d.ts +8 -0
- package/dist/handlers/tools/github/kanban-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/kanban-tools.js +108 -0
- package/dist/handlers/tools/github/kanban-tools.js.map +1 -0
- package/dist/handlers/tools/github/milestone-tools.d.ts +9 -0
- package/dist/handlers/tools/github/milestone-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/milestone-tools.js +302 -0
- package/dist/handlers/tools/github/milestone-tools.js.map +1 -0
- package/dist/handlers/tools/github/mutation-tools.d.ts +12 -0
- package/dist/handlers/tools/github/mutation-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/mutation-tools.js +15 -0
- package/dist/handlers/tools/github/mutation-tools.js.map +1 -0
- package/dist/handlers/tools/github/read-tools.d.ts +8 -0
- package/dist/handlers/tools/github/read-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/read-tools.js +260 -0
- package/dist/handlers/tools/github/read-tools.js.map +1 -0
- package/dist/handlers/tools/github/schemas.d.ts +467 -0
- package/dist/handlers/tools/github/schemas.d.ts.map +1 -0
- package/dist/handlers/tools/github/schemas.js +335 -0
- package/dist/handlers/tools/github/schemas.js.map +1 -0
- package/dist/handlers/tools/github.d.ts +14 -0
- package/dist/handlers/tools/github.d.ts.map +1 -0
- package/dist/handlers/tools/github.js +28 -0
- package/dist/handlers/tools/github.js.map +1 -0
- package/dist/handlers/tools/index.d.ts +15 -20
- package/dist/handlers/tools/index.d.ts.map +1 -1
- package/dist/handlers/tools/index.js +117 -2909
- package/dist/handlers/tools/index.js.map +1 -1
- package/dist/handlers/tools/relationships.d.ts +8 -0
- package/dist/handlers/tools/relationships.d.ts.map +1 -0
- package/dist/handlers/tools/relationships.js +308 -0
- package/dist/handlers/tools/relationships.js.map +1 -0
- package/dist/handlers/tools/schemas.d.ts +108 -0
- package/dist/handlers/tools/schemas.d.ts.map +1 -0
- package/dist/handlers/tools/schemas.js +122 -0
- package/dist/handlers/tools/schemas.js.map +1 -0
- package/dist/handlers/tools/search.d.ts +8 -0
- package/dist/handlers/tools/search.d.ts.map +1 -0
- package/dist/handlers/tools/search.js +282 -0
- package/dist/handlers/tools/search.js.map +1 -0
- package/dist/handlers/tools/team.d.ts +11 -0
- package/dist/handlers/tools/team.d.ts.map +1 -0
- package/dist/handlers/tools/team.js +239 -0
- package/dist/handlers/tools/team.js.map +1 -0
- package/dist/server/McpServer.d.ts +4 -0
- package/dist/server/McpServer.d.ts.map +1 -1
- package/dist/server/McpServer.js +48 -297
- package/dist/server/McpServer.js.map +1 -1
- package/dist/server/Scheduler.d.ts +91 -0
- package/dist/server/Scheduler.d.ts.map +1 -0
- package/dist/server/Scheduler.js +201 -0
- package/dist/server/Scheduler.js.map +1 -0
- package/dist/transports/http.d.ts +66 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +519 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/types/entities.d.ts +101 -0
- package/dist/types/entities.d.ts.map +1 -0
- package/dist/types/entities.js +5 -0
- package/dist/types/entities.js.map +1 -0
- package/dist/types/filtering.d.ts +34 -0
- package/dist/types/filtering.d.ts.map +1 -0
- package/dist/types/filtering.js +5 -0
- package/dist/types/filtering.js.map +1 -0
- package/dist/types/github.d.ts +166 -0
- package/dist/types/github.d.ts.map +1 -0
- package/dist/types/github.js +5 -0
- package/dist/types/github.js.map +1 -0
- package/dist/types/index.d.ts +35 -292
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error-helpers.d.ts +37 -0
- package/dist/utils/error-helpers.d.ts.map +1 -0
- package/dist/utils/error-helpers.js +47 -0
- package/dist/utils/error-helpers.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +6 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/security-utils.d.ts +0 -21
- package/dist/utils/security-utils.d.ts.map +1 -1
- package/dist/utils/security-utils.js +0 -47
- package/dist/utils/security-utils.js.map +1 -1
- package/dist/vector/VectorSearchManager.d.ts.map +1 -1
- package/dist/vector/VectorSearchManager.js +9 -32
- package/dist/vector/VectorSearchManager.js.map +1 -1
- package/docker-compose.yml +11 -2
- package/hooks/README.md +107 -0
- package/hooks/cursor/hooks.json +10 -0
- package/hooks/cursor/memory-journal.mdc +22 -0
- package/hooks/cursor/session-end.sh +19 -0
- package/hooks/kilo-code/session-end-mode.json +11 -0
- package/hooks/kiro/session-end.md +13 -0
- package/mcp-config-example.json +1 -0
- package/package.json +11 -9
- package/playwright.config.ts +29 -0
- package/releases/v4.5.0.md +116 -0
- package/releases/v5.0.0.md +105 -0
- package/scripts/generate-server-instructions.ts +176 -0
- package/scripts/server-instructions-function-body.ts +77 -0
- package/server.json +3 -3
- package/src/cli.ts +45 -1
- package/src/constants/ServerInstructions.ts +133 -73
- package/src/constants/icons.ts +8 -7
- package/src/constants/server-instructions.md +268 -0
- package/src/database/SqliteAdapter.ts +358 -192
- package/src/database/schema.ts +125 -0
- package/src/filtering/ToolFilter.ts +13 -2
- package/src/github/GitHubIntegration.ts +1 -3
- package/src/handlers/prompts/github.ts +209 -0
- package/src/handlers/prompts/index.ts +10 -499
- package/src/handlers/prompts/workflow.ts +314 -0
- package/src/handlers/resources/core.ts +528 -0
- package/src/handlers/resources/github.ts +358 -0
- package/src/handlers/resources/graph.ts +254 -0
- package/src/handlers/resources/index.ts +23 -1570
- package/src/handlers/resources/shared.ts +103 -0
- package/src/handlers/resources/team.ts +133 -0
- package/src/handlers/resources/templates.ts +374 -0
- package/src/handlers/tools/admin.ts +285 -0
- package/src/handlers/tools/analytics.ts +301 -0
- package/src/handlers/tools/backup.ts +242 -0
- package/src/handlers/tools/core.ts +350 -0
- package/src/handlers/tools/export.ts +115 -0
- package/src/handlers/tools/github/helpers.ts +86 -0
- package/src/handlers/tools/github/insights-tools.ts +119 -0
- package/src/handlers/tools/github/issue-tools.ts +439 -0
- package/src/handlers/tools/github/kanban-tools.ts +134 -0
- package/src/handlers/tools/github/milestone-tools.ts +392 -0
- package/src/handlers/tools/github/mutation-tools.ts +17 -0
- package/src/handlers/tools/github/read-tools.ts +328 -0
- package/src/handlers/tools/github/schemas.ts +369 -0
- package/src/handlers/tools/github.ts +36 -0
- package/src/handlers/tools/index.ts +144 -3325
- package/src/handlers/tools/relationships.ts +358 -0
- package/src/handlers/tools/schemas.ts +132 -0
- package/src/handlers/tools/search.ts +343 -0
- package/src/handlers/tools/team.ts +273 -0
- package/src/server/McpServer.ts +63 -358
- package/src/server/Scheduler.ts +278 -0
- package/src/transports/http.ts +635 -0
- package/src/types/entities.ts +145 -0
- package/src/types/filtering.ts +54 -0
- package/src/types/github.ts +180 -0
- package/src/types/index.ts +67 -375
- package/src/utils/error-helpers.ts +52 -0
- package/src/utils/logger.ts +6 -3
- package/src/utils/security-utils.ts +0 -52
- package/src/vector/VectorSearchManager.ts +9 -33
- package/tests/constants/icons.test.ts +1 -2
- package/tests/constants/server-instructions.test.ts +30 -4
- package/tests/database/sqlite-adapter.test.ts +91 -7
- package/tests/e2e/auth.spec.ts +154 -0
- package/tests/e2e/health.spec.ts +63 -0
- package/tests/e2e/protocols.spec.ts +134 -0
- package/tests/e2e/resources.spec.ts +103 -0
- package/tests/e2e/scheduler.spec.ts +79 -0
- package/tests/e2e/security.spec.ts +91 -0
- package/tests/e2e/sessions.spec.ts +95 -0
- package/tests/e2e/stateless.spec.ts +121 -0
- package/tests/e2e/tools.spec.ts +111 -0
- package/tests/filtering/tool-filter.test.ts +46 -0
- package/tests/handlers/error-path-coverage.test.ts +324 -0
- package/tests/handlers/github-resource-handlers.test.ts +453 -0
- package/tests/handlers/github-tool-handlers.test.ts +899 -0
- package/tests/handlers/prompt-handler-coverage.test.ts +106 -0
- package/tests/handlers/prompt-handlers.test.ts +40 -0
- package/tests/handlers/resource-handler-coverage.test.ts +181 -0
- package/tests/handlers/resource-handlers.test.ts +33 -9
- package/tests/handlers/search-tool-handlers.test.ts +272 -0
- package/tests/handlers/targeted-gap-closure.test.ts +387 -0
- package/tests/handlers/team-resource-handlers.test.ts +156 -0
- package/tests/handlers/team-tool-handlers.test.ts +301 -0
- package/tests/handlers/tool-handler-coverage.test.ts +469 -0
- package/tests/handlers/tool-handlers.test.ts +2 -2
- package/tests/security/sql-injection.test.ts +3 -54
- package/tests/server/mcp-server.test.ts +503 -8
- package/tests/server/scheduler.test.ts +400 -0
- package/tests/transports/http-transport.test.ts +620 -0
- package/tests/vector/vector-search-manager.test.ts +60 -0
- package/vitest.config.ts +4 -1
- package/.memory-journal-team.db +0 -0
- 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
|
+
})
|