@prih/mcp-graph-memory 1.0.3

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 (111) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +512 -0
  3. package/dist/api/index.js +473 -0
  4. package/dist/api/rest/code.js +78 -0
  5. package/dist/api/rest/docs.js +80 -0
  6. package/dist/api/rest/files.js +64 -0
  7. package/dist/api/rest/graph.js +56 -0
  8. package/dist/api/rest/index.js +117 -0
  9. package/dist/api/rest/knowledge.js +238 -0
  10. package/dist/api/rest/skills.js +284 -0
  11. package/dist/api/rest/tasks.js +272 -0
  12. package/dist/api/rest/tools.js +126 -0
  13. package/dist/api/rest/validation.js +191 -0
  14. package/dist/api/rest/websocket.js +65 -0
  15. package/dist/api/tools/code/get-file-symbols.js +30 -0
  16. package/dist/api/tools/code/get-symbol.js +22 -0
  17. package/dist/api/tools/code/list-files.js +18 -0
  18. package/dist/api/tools/code/search-code.js +27 -0
  19. package/dist/api/tools/code/search-files.js +22 -0
  20. package/dist/api/tools/context/get-context.js +19 -0
  21. package/dist/api/tools/docs/cross-references.js +76 -0
  22. package/dist/api/tools/docs/explain-symbol.js +55 -0
  23. package/dist/api/tools/docs/find-examples.js +52 -0
  24. package/dist/api/tools/docs/get-node.js +24 -0
  25. package/dist/api/tools/docs/get-toc.js +22 -0
  26. package/dist/api/tools/docs/list-snippets.js +46 -0
  27. package/dist/api/tools/docs/list-topics.js +18 -0
  28. package/dist/api/tools/docs/search-files.js +22 -0
  29. package/dist/api/tools/docs/search-snippets.js +43 -0
  30. package/dist/api/tools/docs/search.js +27 -0
  31. package/dist/api/tools/file-index/get-file-info.js +21 -0
  32. package/dist/api/tools/file-index/list-all-files.js +28 -0
  33. package/dist/api/tools/file-index/search-all-files.js +24 -0
  34. package/dist/api/tools/knowledge/add-attachment.js +31 -0
  35. package/dist/api/tools/knowledge/create-note.js +20 -0
  36. package/dist/api/tools/knowledge/create-relation.js +29 -0
  37. package/dist/api/tools/knowledge/delete-note.js +19 -0
  38. package/dist/api/tools/knowledge/delete-relation.js +23 -0
  39. package/dist/api/tools/knowledge/find-linked-notes.js +25 -0
  40. package/dist/api/tools/knowledge/get-note.js +20 -0
  41. package/dist/api/tools/knowledge/list-notes.js +18 -0
  42. package/dist/api/tools/knowledge/list-relations.js +17 -0
  43. package/dist/api/tools/knowledge/remove-attachment.js +19 -0
  44. package/dist/api/tools/knowledge/search-notes.js +25 -0
  45. package/dist/api/tools/knowledge/update-note.js +34 -0
  46. package/dist/api/tools/skills/add-attachment.js +31 -0
  47. package/dist/api/tools/skills/bump-usage.js +19 -0
  48. package/dist/api/tools/skills/create-skill-link.js +25 -0
  49. package/dist/api/tools/skills/create-skill.js +26 -0
  50. package/dist/api/tools/skills/delete-skill-link.js +23 -0
  51. package/dist/api/tools/skills/delete-skill.js +20 -0
  52. package/dist/api/tools/skills/find-linked-skills.js +25 -0
  53. package/dist/api/tools/skills/get-skill.js +21 -0
  54. package/dist/api/tools/skills/link-skill.js +23 -0
  55. package/dist/api/tools/skills/list-skills.js +20 -0
  56. package/dist/api/tools/skills/recall-skills.js +18 -0
  57. package/dist/api/tools/skills/remove-attachment.js +19 -0
  58. package/dist/api/tools/skills/search-skills.js +25 -0
  59. package/dist/api/tools/skills/update-skill.js +58 -0
  60. package/dist/api/tools/tasks/add-attachment.js +31 -0
  61. package/dist/api/tools/tasks/create-task-link.js +25 -0
  62. package/dist/api/tools/tasks/create-task.js +25 -0
  63. package/dist/api/tools/tasks/delete-task-link.js +23 -0
  64. package/dist/api/tools/tasks/delete-task.js +20 -0
  65. package/dist/api/tools/tasks/find-linked-tasks.js +25 -0
  66. package/dist/api/tools/tasks/get-task.js +20 -0
  67. package/dist/api/tools/tasks/link-task.js +23 -0
  68. package/dist/api/tools/tasks/list-tasks.js +24 -0
  69. package/dist/api/tools/tasks/move-task.js +38 -0
  70. package/dist/api/tools/tasks/remove-attachment.js +19 -0
  71. package/dist/api/tools/tasks/search-tasks.js +25 -0
  72. package/dist/api/tools/tasks/update-task.js +55 -0
  73. package/dist/cli/index.js +451 -0
  74. package/dist/cli/indexer.js +277 -0
  75. package/dist/graphs/attachment-types.js +74 -0
  76. package/dist/graphs/code-types.js +10 -0
  77. package/dist/graphs/code.js +172 -0
  78. package/dist/graphs/docs.js +198 -0
  79. package/dist/graphs/file-index-types.js +10 -0
  80. package/dist/graphs/file-index.js +310 -0
  81. package/dist/graphs/file-lang.js +119 -0
  82. package/dist/graphs/knowledge-types.js +32 -0
  83. package/dist/graphs/knowledge.js +764 -0
  84. package/dist/graphs/manager-types.js +87 -0
  85. package/dist/graphs/skill-types.js +10 -0
  86. package/dist/graphs/skill.js +1013 -0
  87. package/dist/graphs/task-types.js +17 -0
  88. package/dist/graphs/task.js +960 -0
  89. package/dist/lib/embedder.js +101 -0
  90. package/dist/lib/events-log.js +400 -0
  91. package/dist/lib/file-import.js +327 -0
  92. package/dist/lib/file-mirror.js +446 -0
  93. package/dist/lib/frontmatter.js +17 -0
  94. package/dist/lib/mirror-watcher.js +637 -0
  95. package/dist/lib/multi-config.js +254 -0
  96. package/dist/lib/parsers/code.js +246 -0
  97. package/dist/lib/parsers/codeblock.js +66 -0
  98. package/dist/lib/parsers/docs.js +196 -0
  99. package/dist/lib/project-manager.js +418 -0
  100. package/dist/lib/promise-queue.js +22 -0
  101. package/dist/lib/search/bm25.js +167 -0
  102. package/dist/lib/search/code.js +103 -0
  103. package/dist/lib/search/docs.js +108 -0
  104. package/dist/lib/search/file-index.js +31 -0
  105. package/dist/lib/search/files.js +61 -0
  106. package/dist/lib/search/knowledge.js +101 -0
  107. package/dist/lib/search/skills.js +104 -0
  108. package/dist/lib/search/tasks.js +103 -0
  109. package/dist/lib/watcher.js +67 -0
  110. package/package.json +83 -0
  111. package/ui/README.md +54 -0
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.attachmentFilenameSchema = exports.linkedQuerySchema = exports.graphExportSchema = exports.skillListSchema = exports.skillSearchSchema = exports.createSkillLinkSchema = exports.updateSkillSchema = exports.createSkillSchema = exports.fileListSchema = exports.listQuerySchema = exports.searchQuerySchema = exports.taskListSchema = exports.taskSearchSchema = exports.createTaskLinkSchema = exports.moveTaskSchema = exports.updateTaskSchema = exports.createTaskSchema = exports.noteListSchema = exports.noteSearchSchema = exports.createRelationSchema = exports.updateNoteSchema = exports.createNoteSchema = void 0;
4
+ exports.validateBody = validateBody;
5
+ exports.validateQuery = validateQuery;
6
+ const zod_1 = require("zod");
7
+ function validateBody(schema) {
8
+ return (req, _res, next) => {
9
+ req.body = schema.parse(req.body);
10
+ next();
11
+ };
12
+ }
13
+ function validateQuery(schema) {
14
+ return (req, _res, next) => {
15
+ req.validatedQuery = schema.parse(req.query);
16
+ next();
17
+ };
18
+ }
19
+ // ---------------------------------------------------------------------------
20
+ // Knowledge schemas
21
+ // ---------------------------------------------------------------------------
22
+ exports.createNoteSchema = zod_1.z.object({
23
+ title: zod_1.z.string().min(1).max(500),
24
+ content: zod_1.z.string().max(1_000_000),
25
+ tags: zod_1.z.array(zod_1.z.string().max(100)).max(100).optional().default([]),
26
+ });
27
+ exports.updateNoteSchema = zod_1.z.object({
28
+ title: zod_1.z.string().min(1).max(500).optional(),
29
+ content: zod_1.z.string().max(1_000_000).optional(),
30
+ tags: zod_1.z.array(zod_1.z.string().max(100)).max(100).optional(),
31
+ version: zod_1.z.number().int().positive().optional(),
32
+ });
33
+ exports.createRelationSchema = zod_1.z.object({
34
+ fromId: zod_1.z.string().min(1),
35
+ toId: zod_1.z.string().min(1),
36
+ kind: zod_1.z.string().min(1),
37
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'tasks', 'skills']).optional(),
38
+ projectId: zod_1.z.string().min(1).optional(),
39
+ });
40
+ exports.noteSearchSchema = zod_1.z.object({
41
+ q: zod_1.z.string().min(1).max(2000),
42
+ topK: zod_1.z.coerce.number().int().positive().max(500).optional(),
43
+ minScore: zod_1.z.coerce.number().min(0).max(1).optional(),
44
+ searchMode: zod_1.z.enum(['hybrid', 'vector', 'keyword']).optional(),
45
+ });
46
+ exports.noteListSchema = zod_1.z.object({
47
+ filter: zod_1.z.string().optional(),
48
+ tag: zod_1.z.string().optional(),
49
+ limit: zod_1.z.coerce.number().int().positive().optional(),
50
+ });
51
+ // ---------------------------------------------------------------------------
52
+ // Task schemas
53
+ // ---------------------------------------------------------------------------
54
+ exports.createTaskSchema = zod_1.z.object({
55
+ title: zod_1.z.string().min(1).max(500),
56
+ description: zod_1.z.string().max(500_000).default(''),
57
+ status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled']).default('todo'),
58
+ priority: zod_1.z.enum(['critical', 'high', 'medium', 'low']).default('medium'),
59
+ tags: zod_1.z.array(zod_1.z.string().max(100)).max(100).optional().default([]),
60
+ dueDate: zod_1.z.number().nullable().optional(),
61
+ estimate: zod_1.z.number().nullable().optional(),
62
+ });
63
+ exports.updateTaskSchema = zod_1.z.object({
64
+ title: zod_1.z.string().min(1).max(500).optional(),
65
+ description: zod_1.z.string().max(500_000).optional(),
66
+ status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled']).optional(),
67
+ priority: zod_1.z.enum(['critical', 'high', 'medium', 'low']).optional(),
68
+ tags: zod_1.z.array(zod_1.z.string().max(100)).max(100).optional(),
69
+ dueDate: zod_1.z.number().nullable().optional(),
70
+ estimate: zod_1.z.number().nullable().optional(),
71
+ version: zod_1.z.number().int().positive().optional(),
72
+ });
73
+ exports.moveTaskSchema = zod_1.z.object({
74
+ status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled']),
75
+ version: zod_1.z.number().int().positive().optional(),
76
+ });
77
+ exports.createTaskLinkSchema = zod_1.z.object({
78
+ fromId: zod_1.z.string().min(1),
79
+ toId: zod_1.z.string().min(1),
80
+ kind: zod_1.z.string().min(1),
81
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'knowledge', 'skills']).optional(),
82
+ projectId: zod_1.z.string().min(1).optional(),
83
+ });
84
+ exports.taskSearchSchema = zod_1.z.object({
85
+ q: zod_1.z.string().min(1).max(2000),
86
+ topK: zod_1.z.coerce.number().int().positive().max(500).optional(),
87
+ minScore: zod_1.z.coerce.number().min(0).max(1).optional(),
88
+ searchMode: zod_1.z.enum(['hybrid', 'vector', 'keyword']).optional(),
89
+ });
90
+ exports.taskListSchema = zod_1.z.object({
91
+ status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled']).optional(),
92
+ priority: zod_1.z.enum(['critical', 'high', 'medium', 'low']).optional(),
93
+ tag: zod_1.z.string().optional(),
94
+ filter: zod_1.z.string().optional(),
95
+ limit: zod_1.z.coerce.number().int().positive().optional(),
96
+ });
97
+ // ---------------------------------------------------------------------------
98
+ // Search schemas (docs, code, files)
99
+ // ---------------------------------------------------------------------------
100
+ exports.searchQuerySchema = zod_1.z.object({
101
+ q: zod_1.z.string().min(1).max(2000),
102
+ topK: zod_1.z.coerce.number().int().positive().max(500).optional(),
103
+ minScore: zod_1.z.coerce.number().min(0).max(1).optional(),
104
+ searchMode: zod_1.z.enum(['hybrid', 'vector', 'keyword']).optional(),
105
+ });
106
+ exports.listQuerySchema = zod_1.z.object({
107
+ filter: zod_1.z.string().optional(),
108
+ limit: zod_1.z.coerce.number().int().positive().optional(),
109
+ });
110
+ // ---------------------------------------------------------------------------
111
+ // File index schemas
112
+ // ---------------------------------------------------------------------------
113
+ exports.fileListSchema = zod_1.z.object({
114
+ directory: zod_1.z.string().optional(),
115
+ extension: zod_1.z.string().optional(),
116
+ language: zod_1.z.string().optional(),
117
+ filter: zod_1.z.string().optional(),
118
+ limit: zod_1.z.coerce.number().int().positive().optional(),
119
+ });
120
+ // ---------------------------------------------------------------------------
121
+ // Graph export schema
122
+ // ---------------------------------------------------------------------------
123
+ // ---------------------------------------------------------------------------
124
+ // Skill schemas
125
+ // ---------------------------------------------------------------------------
126
+ exports.createSkillSchema = zod_1.z.object({
127
+ title: zod_1.z.string().min(1).max(500),
128
+ description: zod_1.z.string().max(500_000).default(''),
129
+ steps: zod_1.z.array(zod_1.z.string().max(10_000)).max(100).optional().default([]),
130
+ triggers: zod_1.z.array(zod_1.z.string().max(500)).max(50).optional().default([]),
131
+ inputHints: zod_1.z.array(zod_1.z.string().max(500)).max(50).optional().default([]),
132
+ filePatterns: zod_1.z.array(zod_1.z.string().max(500)).max(50).optional().default([]),
133
+ tags: zod_1.z.array(zod_1.z.string().max(100)).max(100).optional().default([]),
134
+ source: zod_1.z.enum(['user', 'learned']).default('user'),
135
+ confidence: zod_1.z.number().min(0).max(1).default(1),
136
+ });
137
+ exports.updateSkillSchema = zod_1.z.object({
138
+ title: zod_1.z.string().min(1).max(500).optional(),
139
+ description: zod_1.z.string().max(500_000).optional(),
140
+ steps: zod_1.z.array(zod_1.z.string().max(10_000)).max(100).optional(),
141
+ triggers: zod_1.z.array(zod_1.z.string().max(500)).max(50).optional(),
142
+ inputHints: zod_1.z.array(zod_1.z.string().max(500)).max(50).optional(),
143
+ filePatterns: zod_1.z.array(zod_1.z.string().max(500)).max(50).optional(),
144
+ tags: zod_1.z.array(zod_1.z.string().max(100)).max(100).optional(),
145
+ source: zod_1.z.enum(['user', 'learned']).optional(),
146
+ confidence: zod_1.z.number().min(0).max(1).optional(),
147
+ version: zod_1.z.number().int().positive().optional(),
148
+ });
149
+ exports.createSkillLinkSchema = zod_1.z.object({
150
+ fromId: zod_1.z.string().min(1),
151
+ toId: zod_1.z.string().min(1),
152
+ kind: zod_1.z.string().min(1),
153
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'knowledge', 'tasks']).optional(),
154
+ projectId: zod_1.z.string().min(1).optional(),
155
+ });
156
+ exports.skillSearchSchema = zod_1.z.object({
157
+ q: zod_1.z.string().min(1).max(2000),
158
+ topK: zod_1.z.coerce.number().int().positive().max(500).optional(),
159
+ minScore: zod_1.z.coerce.number().min(0).max(1).optional(),
160
+ searchMode: zod_1.z.enum(['hybrid', 'vector', 'keyword']).optional(),
161
+ });
162
+ exports.skillListSchema = zod_1.z.object({
163
+ source: zod_1.z.enum(['user', 'learned']).optional(),
164
+ tag: zod_1.z.string().optional(),
165
+ filter: zod_1.z.string().optional(),
166
+ limit: zod_1.z.coerce.number().int().positive().optional(),
167
+ });
168
+ exports.graphExportSchema = zod_1.z.object({
169
+ scope: zod_1.z.enum(['all', 'docs', 'code', 'knowledge', 'tasks', 'files', 'skills']).default('all'),
170
+ });
171
+ // ---------------------------------------------------------------------------
172
+ // Attachment schemas
173
+ // ---------------------------------------------------------------------------
174
+ // ---------------------------------------------------------------------------
175
+ // Linked query schema (cross-graph reverse lookup)
176
+ // ---------------------------------------------------------------------------
177
+ exports.linkedQuerySchema = zod_1.z.object({
178
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'knowledge', 'tasks', 'skills']),
179
+ targetNodeId: zod_1.z.string().min(1).max(500),
180
+ kind: zod_1.z.string().max(100).optional(),
181
+ projectId: zod_1.z.string().max(200).optional(),
182
+ });
183
+ // ---------------------------------------------------------------------------
184
+ // Attachment schemas
185
+ // ---------------------------------------------------------------------------
186
+ /** Validates an attachment filename (path param). No path separators, no .., no null bytes. */
187
+ exports.attachmentFilenameSchema = zod_1.z.string()
188
+ .min(1)
189
+ .refine(s => !/[/\\]/.test(s), 'Filename must not contain path separators')
190
+ .refine(s => !s.includes('..'), 'Filename must not contain ..')
191
+ .refine(s => !s.includes('\0'), 'Filename must not contain null bytes');
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.attachWebSocket = attachWebSocket;
4
+ const ws_1 = require("ws");
5
+ /**
6
+ * Attach a WebSocket server to the HTTP server at /api/ws.
7
+ * Broadcasts all ProjectManager events to connected clients.
8
+ * Each event includes projectId — clients filter on their side.
9
+ */
10
+ function attachWebSocket(httpServer, projectManager) {
11
+ const wss = new ws_1.WebSocketServer({ noServer: true });
12
+ // Handle upgrade requests for /api/ws
13
+ httpServer.on('upgrade', (req, socket, head) => {
14
+ if (req.url !== '/api/ws') {
15
+ socket.destroy();
16
+ return;
17
+ }
18
+ wss.handleUpgrade(req, socket, head, (ws) => {
19
+ wss.emit('connection', ws, req);
20
+ });
21
+ });
22
+ // Broadcast helper
23
+ function broadcast(event) {
24
+ const msg = JSON.stringify(event);
25
+ for (const client of wss.clients) {
26
+ if (client.readyState === ws_1.WebSocket.OPEN) {
27
+ client.send(msg);
28
+ }
29
+ }
30
+ }
31
+ // Subscribe to ProjectManager events
32
+ const events = [
33
+ 'note:created', 'note:updated', 'note:deleted',
34
+ 'note:attachment:added', 'note:attachment:deleted',
35
+ 'task:created', 'task:updated', 'task:deleted', 'task:moved',
36
+ 'task:attachment:added', 'task:attachment:deleted',
37
+ 'skill:created', 'skill:updated', 'skill:deleted',
38
+ 'skill:attachment:added', 'skill:attachment:deleted',
39
+ 'project:indexed',
40
+ ];
41
+ for (const eventType of events) {
42
+ projectManager.on(eventType, (data) => {
43
+ broadcast({ projectId: data.projectId, type: eventType, data });
44
+ });
45
+ }
46
+ // Debounced graph:updated from indexer
47
+ let graphUpdateTimer;
48
+ let pendingGraphUpdates = new Map();
49
+ projectManager.on('graph:updated', (data) => {
50
+ const key = data.projectId;
51
+ if (!pendingGraphUpdates.has(key))
52
+ pendingGraphUpdates.set(key, []);
53
+ pendingGraphUpdates.get(key).push(data.file);
54
+ if (!graphUpdateTimer) {
55
+ graphUpdateTimer = setTimeout(() => {
56
+ for (const [projectId, files] of pendingGraphUpdates) {
57
+ broadcast({ projectId, type: 'graph:updated', data: { files } });
58
+ }
59
+ pendingGraphUpdates = new Map();
60
+ graphUpdateTimer = undefined;
61
+ }, 1000);
62
+ }
63
+ });
64
+ return wss;
65
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ function register(server, mgr) {
6
+ server.registerTool('get_file_symbols', {
7
+ description: 'Return all symbols (functions, classes, types, etc.) declared in a specific file. ' +
8
+ 'Use this to understand the structure of a file before reading individual symbols. ' +
9
+ 'Returns an array of { id, kind, name, signature, startLine, endLine, isExported }. ' +
10
+ 'Pass an id to get_symbol to fetch full content including body and docComment.',
11
+ inputSchema: {
12
+ fileId: zod_1.z.string().describe('File path relative to code dir, e.g. "src/lib/graph.ts"'),
13
+ },
14
+ }, async ({ fileId }) => {
15
+ const symbols = mgr.getFileSymbols(fileId);
16
+ if (symbols.length === 0) {
17
+ return { content: [{ type: 'text', text: `File not found: ${fileId}` }], isError: true };
18
+ }
19
+ const result = symbols.map(s => ({
20
+ id: s.id,
21
+ kind: s.kind,
22
+ name: s.name,
23
+ signature: s.signature,
24
+ startLine: s.startLine,
25
+ endLine: s.endLine,
26
+ isExported: s.isExported,
27
+ }));
28
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
29
+ });
30
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ function register(server, mgr) {
6
+ server.registerTool('get_symbol', {
7
+ description: 'Return the full content of a specific code symbol. ' +
8
+ 'Use this after search_code or get_file_symbols to read the full implementation. ' +
9
+ 'Node IDs have the form "fileId::symbolName" or "fileId::ClassName::methodName". ' +
10
+ 'Returns id, fileId, kind, name, signature, docComment, body, startLine, endLine, isExported, and crossLinks (notes/tasks linking to this symbol).',
11
+ inputSchema: {
12
+ nodeId: zod_1.z.string().describe('Symbol ID from search_code or get_file_symbols, e.g. "src/lib/graph.ts::updateFile"'),
13
+ },
14
+ }, async ({ nodeId }) => {
15
+ const symbol = mgr.getSymbol(nodeId);
16
+ if (!symbol) {
17
+ return { content: [{ type: 'text', text: `Symbol not found: ${nodeId}` }], isError: true };
18
+ }
19
+ const { embedding: _embedding, mtime: _mtime, ...rest } = symbol;
20
+ return { content: [{ type: 'text', text: JSON.stringify(rest, null, 2) }] };
21
+ });
22
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ function register(server, mgr) {
6
+ server.registerTool('list_files', {
7
+ description: 'List indexed source files in the code graph. ' +
8
+ 'Optionally filter by file name substring (case-insensitive) and limit results. ' +
9
+ 'Returns an array of { fileId, symbolCount }. ' +
10
+ 'Pass a fileId to get_file_symbols to see all its declarations.',
11
+ inputSchema: {
12
+ filter: zod_1.z.string().optional().describe('Case-insensitive substring to match against file paths, e.g. "graph" or "src/lib"'),
13
+ limit: zod_1.z.number().optional().describe('Maximum number of results to return (default 20)'),
14
+ },
15
+ }, async ({ filter, limit }) => ({
16
+ content: [{ type: 'text', text: JSON.stringify(mgr.listFiles(filter, limit), null, 2) }],
17
+ }));
18
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ function register(server, mgr) {
6
+ server.registerTool('search_code', {
7
+ description: 'Semantic search over the indexed source code. ' +
8
+ 'Finds the most relevant symbols (functions, classes, types) by matching ' +
9
+ 'against their signatures and doc comments using vector similarity, ' +
10
+ 'then expands results by following graph edges (imports, contains, extends). ' +
11
+ 'Returns an array sorted by relevance score (0–1), each with: ' +
12
+ 'id, fileId, kind, name, signature, docComment, startLine, endLine, score. ' +
13
+ 'Pass an id to get_symbol to read the full implementation.',
14
+ inputSchema: {
15
+ query: zod_1.z.string().describe('Natural language or code search query, e.g. "function that loads the graph from disk"'),
16
+ topK: zod_1.z.number().optional().describe('How many top similar symbols to use as seeds (default 5)'),
17
+ bfsDepth: zod_1.z.number().optional().describe('How many hops to follow graph edges from each seed (default 1; 0 = no expansion)'),
18
+ maxResults: zod_1.z.number().optional().describe('Maximum number of results to return (default 20)'),
19
+ minScore: zod_1.z.number().min(0).max(1).optional().describe('Minimum relevance score 0–1; lower values return more results (default 0.5)'),
20
+ bfsDecay: zod_1.z.number().min(0).max(1).optional().describe('Score multiplier per graph hop (default 0.8)'),
21
+ searchMode: zod_1.z.enum(['hybrid', 'vector', 'keyword']).optional().describe('Search mode: hybrid (default, BM25 + vector), vector (embedding only), keyword (BM25 only)'),
22
+ },
23
+ }, async ({ query, topK, bfsDepth, maxResults, minScore, bfsDecay, searchMode }) => {
24
+ const results = await mgr.search(query, { topK, bfsDepth, maxResults, minScore, bfsDecay, searchMode });
25
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
26
+ });
27
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ function register(server, mgr) {
6
+ server.registerTool('search_files', {
7
+ description: 'Semantic search over indexed source code files. ' +
8
+ 'Finds the most relevant files by matching query against file-level embeddings ' +
9
+ '(file path) using vector similarity. ' +
10
+ 'Returns an array sorted by relevance score (0–1), each with: ' +
11
+ 'fileId, symbolCount, score. ' +
12
+ 'Use this to discover which source files are relevant before diving into symbols with get_file_symbols or search_code.',
13
+ inputSchema: {
14
+ query: zod_1.z.string().describe('Natural language or path search query, e.g. "graph persistence" or "search module"'),
15
+ topK: zod_1.z.number().optional().describe('Maximum number of results to return (default 10)'),
16
+ minScore: zod_1.z.number().min(0).max(1).optional().describe('Minimum relevance score 0–1 (default 0.3)'),
17
+ },
18
+ }, async ({ query, topK, minScore }) => {
19
+ const results = await mgr.searchFiles(query, { topK, minScore });
20
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
21
+ });
22
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ function register(server, ctx) {
5
+ server.registerTool('get_context', {
6
+ description: 'Returns the current project and workspace context. ' +
7
+ 'Use this to discover which project you are connected to, whether it is part of a workspace, ' +
8
+ 'and which other projects are available in the workspace (for cross-graph links with projectId).',
9
+ inputSchema: {},
10
+ }, async () => {
11
+ const result = {
12
+ projectId: ctx?.projectId ?? null,
13
+ workspaceId: ctx?.workspaceId ?? null,
14
+ workspaceProjects: ctx?.workspaceProjects ?? null,
15
+ hasWorkspace: !!ctx?.workspaceId,
16
+ };
17
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
18
+ });
19
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ function register(server, docMgr, codeMgr) {
6
+ const docGraph = docMgr.graph;
7
+ const codeGraph = codeMgr.graph;
8
+ server.registerTool('cross_references', {
9
+ description: 'Find all references to a symbol across both code and documentation graphs. ' +
10
+ 'Returns: definitions (from CodeGraph — where the symbol is defined), ' +
11
+ 'documentation (text sections in docs that contain examples using the symbol), ' +
12
+ 'and examples (code blocks in docs that contain the symbol). ' +
13
+ 'This is the most comprehensive way to understand a symbol — combining source code, docs, and examples.',
14
+ inputSchema: {
15
+ symbol: zod_1.z.string().describe('Symbol name to look up, e.g. "createUser", "AuthService"'),
16
+ },
17
+ }, async ({ symbol }) => {
18
+ // 1. Search CodeGraph for definitions
19
+ const definitions = [];
20
+ codeGraph.forEachNode((id, attrs) => {
21
+ if (attrs.name === symbol) {
22
+ definitions.push({
23
+ id,
24
+ fileId: attrs.fileId,
25
+ kind: attrs.kind,
26
+ name: attrs.name,
27
+ signature: attrs.signature,
28
+ docComment: attrs.docComment,
29
+ startLine: attrs.startLine,
30
+ endLine: attrs.endLine,
31
+ });
32
+ }
33
+ });
34
+ // 2. Search DocGraph for code blocks containing the symbol
35
+ const examples = [];
36
+ const documentation = [];
37
+ const seenDocs = new Set();
38
+ docGraph.forEachNode((id, attrs) => {
39
+ if (attrs.symbols.length === 0)
40
+ return;
41
+ if (!attrs.symbols.includes(symbol))
42
+ return;
43
+ examples.push({
44
+ id,
45
+ fileId: attrs.fileId,
46
+ language: attrs.language,
47
+ symbols: attrs.symbols,
48
+ content: attrs.content,
49
+ });
50
+ // Find parent text section for documentation context
51
+ for (const neighbor of docGraph.inNeighbors(id)) {
52
+ if (seenDocs.has(neighbor))
53
+ continue;
54
+ const nAttrs = docGraph.getNodeAttributes(neighbor);
55
+ if (nAttrs.fileId === attrs.fileId && nAttrs.language === undefined && nAttrs.level < attrs.level) {
56
+ seenDocs.add(neighbor);
57
+ documentation.push({
58
+ id: neighbor,
59
+ fileId: nAttrs.fileId,
60
+ title: nAttrs.title,
61
+ content: nAttrs.content,
62
+ });
63
+ }
64
+ }
65
+ });
66
+ if (definitions.length === 0 && examples.length === 0) {
67
+ return { content: [{ type: 'text', text: `No references found for symbol: ${symbol}` }] };
68
+ }
69
+ return {
70
+ content: [{
71
+ type: 'text',
72
+ text: JSON.stringify({ definitions, documentation, examples }, null, 2),
73
+ }],
74
+ };
75
+ });
76
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ function register(server, mgr) {
6
+ const graph = mgr.graph;
7
+ server.registerTool('explain_symbol', {
8
+ description: 'Find documentation that explains a specific symbol. ' +
9
+ 'Searches code blocks in docs for the symbol, then returns both the code example ' +
10
+ 'and the surrounding text section that provides context/explanation. ' +
11
+ 'Use this to understand what a function, class, or type does based on documentation.',
12
+ inputSchema: {
13
+ symbol: zod_1.z.string().describe('Symbol name to look up, e.g. "createUser", "AuthService"'),
14
+ limit: zod_1.z.number().optional().describe('Max results to return (default 10)'),
15
+ },
16
+ }, async ({ symbol, limit = 10 }) => {
17
+ const results = [];
18
+ graph.forEachNode((id, attrs) => {
19
+ if (results.length >= limit)
20
+ return;
21
+ if (attrs.symbols.length === 0)
22
+ return;
23
+ if (!attrs.symbols.includes(symbol))
24
+ return;
25
+ // Find the parent text section
26
+ const parent = findParentTextSection(graph, id, attrs);
27
+ results.push({
28
+ codeBlock: {
29
+ id,
30
+ language: attrs.language,
31
+ symbols: attrs.symbols,
32
+ content: attrs.content,
33
+ },
34
+ explanation: parent
35
+ ? { id: parent.id, title: parent.title, content: parent.content }
36
+ : null,
37
+ fileId: attrs.fileId,
38
+ });
39
+ });
40
+ if (results.length === 0) {
41
+ return { content: [{ type: 'text', text: `No documentation found for symbol: ${symbol}` }] };
42
+ }
43
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
44
+ });
45
+ }
46
+ function findParentTextSection(graph, nodeId, attrs) {
47
+ // The code block's in-neighbor with same fileId, lower level, and no language = parent text section
48
+ for (const neighbor of graph.inNeighbors(nodeId)) {
49
+ const nAttrs = graph.getNodeAttributes(neighbor);
50
+ if (nAttrs.fileId === attrs.fileId && nAttrs.language === undefined && nAttrs.level < attrs.level) {
51
+ return { id: neighbor, title: nAttrs.title, content: nAttrs.content };
52
+ }
53
+ }
54
+ return null;
55
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ function register(server, mgr) {
6
+ const graph = mgr.graph;
7
+ server.registerTool('find_examples', {
8
+ description: 'Find code examples in documentation that contain a specific symbol (function, class, interface, etc.). ' +
9
+ 'Searches the `symbols` array extracted from fenced code blocks via AST parsing. ' +
10
+ 'Use this to find usage examples of a known symbol in the docs. ' +
11
+ 'Returns matching code block nodes with id, fileId, language, symbols, content, and the parent section context.',
12
+ inputSchema: {
13
+ symbol: zod_1.z.string().describe('Symbol name to search for, e.g. "createUser", "UserService"'),
14
+ limit: zod_1.z.number().optional().describe('Max results to return (default 20)'),
15
+ },
16
+ }, async ({ symbol, limit = 20 }) => {
17
+ const results = [];
18
+ graph.forEachNode((id, attrs) => {
19
+ if (results.length >= limit)
20
+ return;
21
+ if (attrs.symbols.length === 0)
22
+ return;
23
+ if (!attrs.symbols.includes(symbol))
24
+ return;
25
+ // Find parent text section (previous node with lower level and no language)
26
+ const parentId = findParentSection(graph, id, attrs);
27
+ results.push({
28
+ id,
29
+ fileId: attrs.fileId,
30
+ language: attrs.language,
31
+ symbols: attrs.symbols,
32
+ content: attrs.content,
33
+ parentId,
34
+ parentTitle: parentId ? graph.getNodeAttribute(parentId, 'title') : undefined,
35
+ });
36
+ });
37
+ if (results.length === 0) {
38
+ return { content: [{ type: 'text', text: `No code examples found containing symbol: ${symbol}` }] };
39
+ }
40
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
41
+ });
42
+ }
43
+ function findParentSection(graph, nodeId, attrs) {
44
+ // Walk inNeighbors (sibling edges point forward, so the parent is an inNeighbor)
45
+ for (const neighbor of graph.inNeighbors(nodeId)) {
46
+ const nAttrs = graph.getNodeAttributes(neighbor);
47
+ if (nAttrs.fileId === attrs.fileId && nAttrs.language === undefined && nAttrs.level < attrs.level) {
48
+ return neighbor;
49
+ }
50
+ }
51
+ return undefined;
52
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ function register(server, mgr) {
6
+ server.registerTool('get_node', {
7
+ description: 'Return the full content of a specific node (file root or section). ' +
8
+ 'Use this after search or get_toc to read the full text of a result. ' +
9
+ 'Node IDs have two forms: ' +
10
+ '"docs/auth.md" for the file root (intro text before any headings), ' +
11
+ '"docs/auth.md::Overview" for a named section. ' +
12
+ 'Returns id, fileId, title, content, level, links, mtime, and crossLinks (notes/tasks linking to this node).',
13
+ inputSchema: {
14
+ nodeId: zod_1.z.string().describe('Node ID from search results or get_toc, e.g. "docs/auth.md" or "docs/auth.md::Overview"'),
15
+ },
16
+ }, async ({ nodeId }) => {
17
+ const node = mgr.getNode(nodeId);
18
+ if (!node) {
19
+ return { content: [{ type: 'text', text: `Node not found: ${nodeId}` }], isError: true };
20
+ }
21
+ const { embedding: _embedding, ...rest } = node;
22
+ return { content: [{ type: 'text', text: JSON.stringify(rest, null, 2) }] };
23
+ });
24
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ function register(server, mgr) {
6
+ server.registerTool('get_toc', {
7
+ description: 'Return the table of contents (headings hierarchy) for a specific file. ' +
8
+ 'Use this to understand the structure of a file before deciding which sections to read. ' +
9
+ 'Returns an array of { id, title, level } objects. ' +
10
+ 'The id field is a node ID you can pass directly to get_node to fetch full content.',
11
+ inputSchema: {
12
+ fileId: zod_1.z.string().describe('File path relative to docs dir, e.g. "docs/auth.md"'),
13
+ },
14
+ }, async ({ fileId }) => {
15
+ const chunks = mgr.getFileChunks(fileId);
16
+ if (chunks.length === 0) {
17
+ return { content: [{ type: 'text', text: `File not found: ${fileId}` }], isError: true };
18
+ }
19
+ const toc = chunks.map(c => ({ id: c.id, title: c.title, level: c.level }));
20
+ return { content: [{ type: 'text', text: JSON.stringify(toc, null, 2) }] };
21
+ });
22
+ }