@graphmemory/server 1.1.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 (123) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +216 -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/embed.js +47 -0
  7. package/dist/api/rest/files.js +64 -0
  8. package/dist/api/rest/graph.js +71 -0
  9. package/dist/api/rest/index.js +371 -0
  10. package/dist/api/rest/knowledge.js +239 -0
  11. package/dist/api/rest/skills.js +285 -0
  12. package/dist/api/rest/tasks.js +273 -0
  13. package/dist/api/rest/tools.js +157 -0
  14. package/dist/api/rest/validation.js +196 -0
  15. package/dist/api/rest/websocket.js +71 -0
  16. package/dist/api/tools/code/get-file-symbols.js +30 -0
  17. package/dist/api/tools/code/get-symbol.js +22 -0
  18. package/dist/api/tools/code/list-files.js +18 -0
  19. package/dist/api/tools/code/search-code.js +27 -0
  20. package/dist/api/tools/code/search-files.js +22 -0
  21. package/dist/api/tools/context/get-context.js +19 -0
  22. package/dist/api/tools/docs/cross-references.js +76 -0
  23. package/dist/api/tools/docs/explain-symbol.js +55 -0
  24. package/dist/api/tools/docs/find-examples.js +52 -0
  25. package/dist/api/tools/docs/get-node.js +24 -0
  26. package/dist/api/tools/docs/get-toc.js +22 -0
  27. package/dist/api/tools/docs/list-snippets.js +46 -0
  28. package/dist/api/tools/docs/list-topics.js +18 -0
  29. package/dist/api/tools/docs/search-files.js +22 -0
  30. package/dist/api/tools/docs/search-snippets.js +43 -0
  31. package/dist/api/tools/docs/search.js +27 -0
  32. package/dist/api/tools/file-index/get-file-info.js +21 -0
  33. package/dist/api/tools/file-index/list-all-files.js +28 -0
  34. package/dist/api/tools/file-index/search-all-files.js +24 -0
  35. package/dist/api/tools/knowledge/add-attachment.js +31 -0
  36. package/dist/api/tools/knowledge/create-note.js +20 -0
  37. package/dist/api/tools/knowledge/create-relation.js +29 -0
  38. package/dist/api/tools/knowledge/delete-note.js +19 -0
  39. package/dist/api/tools/knowledge/delete-relation.js +23 -0
  40. package/dist/api/tools/knowledge/find-linked-notes.js +25 -0
  41. package/dist/api/tools/knowledge/get-note.js +20 -0
  42. package/dist/api/tools/knowledge/list-notes.js +18 -0
  43. package/dist/api/tools/knowledge/list-relations.js +17 -0
  44. package/dist/api/tools/knowledge/remove-attachment.js +19 -0
  45. package/dist/api/tools/knowledge/search-notes.js +25 -0
  46. package/dist/api/tools/knowledge/update-note.js +34 -0
  47. package/dist/api/tools/skills/add-attachment.js +31 -0
  48. package/dist/api/tools/skills/bump-usage.js +19 -0
  49. package/dist/api/tools/skills/create-skill-link.js +25 -0
  50. package/dist/api/tools/skills/create-skill.js +26 -0
  51. package/dist/api/tools/skills/delete-skill-link.js +23 -0
  52. package/dist/api/tools/skills/delete-skill.js +20 -0
  53. package/dist/api/tools/skills/find-linked-skills.js +25 -0
  54. package/dist/api/tools/skills/get-skill.js +21 -0
  55. package/dist/api/tools/skills/link-skill.js +23 -0
  56. package/dist/api/tools/skills/list-skills.js +20 -0
  57. package/dist/api/tools/skills/recall-skills.js +18 -0
  58. package/dist/api/tools/skills/remove-attachment.js +19 -0
  59. package/dist/api/tools/skills/search-skills.js +25 -0
  60. package/dist/api/tools/skills/update-skill.js +58 -0
  61. package/dist/api/tools/tasks/add-attachment.js +31 -0
  62. package/dist/api/tools/tasks/create-task-link.js +25 -0
  63. package/dist/api/tools/tasks/create-task.js +26 -0
  64. package/dist/api/tools/tasks/delete-task-link.js +23 -0
  65. package/dist/api/tools/tasks/delete-task.js +20 -0
  66. package/dist/api/tools/tasks/find-linked-tasks.js +25 -0
  67. package/dist/api/tools/tasks/get-task.js +20 -0
  68. package/dist/api/tools/tasks/link-task.js +23 -0
  69. package/dist/api/tools/tasks/list-tasks.js +25 -0
  70. package/dist/api/tools/tasks/move-task.js +38 -0
  71. package/dist/api/tools/tasks/remove-attachment.js +19 -0
  72. package/dist/api/tools/tasks/search-tasks.js +25 -0
  73. package/dist/api/tools/tasks/update-task.js +58 -0
  74. package/dist/cli/index.js +617 -0
  75. package/dist/cli/indexer.js +275 -0
  76. package/dist/graphs/attachment-types.js +74 -0
  77. package/dist/graphs/code-types.js +10 -0
  78. package/dist/graphs/code.js +204 -0
  79. package/dist/graphs/docs.js +231 -0
  80. package/dist/graphs/file-index-types.js +10 -0
  81. package/dist/graphs/file-index.js +310 -0
  82. package/dist/graphs/file-lang.js +119 -0
  83. package/dist/graphs/knowledge-types.js +32 -0
  84. package/dist/graphs/knowledge.js +768 -0
  85. package/dist/graphs/manager-types.js +87 -0
  86. package/dist/graphs/skill-types.js +10 -0
  87. package/dist/graphs/skill.js +1016 -0
  88. package/dist/graphs/task-types.js +17 -0
  89. package/dist/graphs/task.js +972 -0
  90. package/dist/lib/access.js +67 -0
  91. package/dist/lib/embedder.js +235 -0
  92. package/dist/lib/events-log.js +401 -0
  93. package/dist/lib/file-import.js +328 -0
  94. package/dist/lib/file-mirror.js +461 -0
  95. package/dist/lib/frontmatter.js +17 -0
  96. package/dist/lib/jwt.js +146 -0
  97. package/dist/lib/mirror-watcher.js +637 -0
  98. package/dist/lib/multi-config.js +393 -0
  99. package/dist/lib/parsers/code.js +214 -0
  100. package/dist/lib/parsers/codeblock.js +33 -0
  101. package/dist/lib/parsers/docs.js +199 -0
  102. package/dist/lib/parsers/languages/index.js +15 -0
  103. package/dist/lib/parsers/languages/registry.js +68 -0
  104. package/dist/lib/parsers/languages/types.js +2 -0
  105. package/dist/lib/parsers/languages/typescript.js +306 -0
  106. package/dist/lib/project-manager.js +458 -0
  107. package/dist/lib/promise-queue.js +22 -0
  108. package/dist/lib/search/bm25.js +167 -0
  109. package/dist/lib/search/code.js +103 -0
  110. package/dist/lib/search/docs.js +106 -0
  111. package/dist/lib/search/file-index.js +31 -0
  112. package/dist/lib/search/files.js +61 -0
  113. package/dist/lib/search/knowledge.js +101 -0
  114. package/dist/lib/search/skills.js +104 -0
  115. package/dist/lib/search/tasks.js +103 -0
  116. package/dist/lib/team.js +89 -0
  117. package/dist/lib/watcher.js +67 -0
  118. package/dist/ui/assets/index-D6oxrVF7.js +1759 -0
  119. package/dist/ui/assets/index-kKd4mVrh.css +1 -0
  120. package/dist/ui/favicon.svg +1 -0
  121. package/dist/ui/icons.svg +24 -0
  122. package/dist/ui/index.html +14 -0
  123. package/package.json +89 -0
@@ -0,0 +1,28 @@
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_all_files', {
7
+ description: 'List all indexed project files and directories with optional filters. ' +
8
+ 'When directory is set, returns immediate children (files + subdirectories) — use this to browse the project tree. ' +
9
+ 'Without directory, returns all files matching filters (no directories in flat listing). ' +
10
+ 'Returns an array of { filePath, kind, fileName, extension, language, mimeType, size, fileCount }. ' +
11
+ 'Use search_all_files for semantic search or get_file_info for detailed metadata on a specific path.',
12
+ inputSchema: {
13
+ directory: zod_1.z.string().optional()
14
+ .describe('List immediate children of this directory (e.g. ".", "src/lib"). Default: lists all files'),
15
+ extension: zod_1.z.string().optional()
16
+ .describe('Filter by extension (e.g. ".ts", ".md", ".png")'),
17
+ language: zod_1.z.string().optional()
18
+ .describe('Filter by language (e.g. "typescript", "markdown", "json")'),
19
+ filter: zod_1.z.string().optional()
20
+ .describe('Substring filter on file path (case-insensitive)'),
21
+ limit: zod_1.z.number().optional().default(50)
22
+ .describe('Max results (default 50)'),
23
+ },
24
+ }, async ({ directory, extension, language, filter, limit }) => {
25
+ const results = mgr.listAllFiles({ directory, extension, language, filter, limit });
26
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
27
+ });
28
+ }
@@ -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('search_all_files', {
7
+ description: 'Semantic search over all indexed project files by file path. ' +
8
+ 'Finds the most relevant files by matching query against file path embeddings using vector similarity. ' +
9
+ 'Searches file nodes only (not directories). ' +
10
+ 'Returns an array sorted by relevance score (0–1), each with: ' +
11
+ 'filePath, fileName, extension, language, size, score. ' +
12
+ 'Use this to discover which project files are relevant to a topic.',
13
+ inputSchema: {
14
+ query: zod_1.z.string().describe('Search query'),
15
+ topK: zod_1.z.number().optional().default(10)
16
+ .describe('Max results (default 10)'),
17
+ minScore: zod_1.z.number().optional().default(0.3)
18
+ .describe('Minimum cosine similarity score (default 0.3)'),
19
+ },
20
+ }, async ({ query, topK, minScore }) => {
21
+ const results = await mgr.search(query, { topK, minScore });
22
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
23
+ });
24
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.register = register;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const zod_1 = require("zod");
10
+ function register(server, mgr) {
11
+ server.registerTool('add_note_attachment', {
12
+ description: 'Attach a file to a note. Provide the absolute path to a local file. ' +
13
+ 'The file is copied into the note directory (.notes/{noteId}/). ' +
14
+ 'Returns attachment metadata (filename, mimeType, size).',
15
+ inputSchema: {
16
+ noteId: zod_1.z.string().describe('ID of the note to attach the file to'),
17
+ filePath: zod_1.z.string().describe('Absolute path to the file on disk'),
18
+ },
19
+ }, async ({ noteId, filePath }) => {
20
+ if (!fs_1.default.existsSync(filePath)) {
21
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'File not found' }) }], isError: true };
22
+ }
23
+ const data = fs_1.default.readFileSync(filePath);
24
+ const filename = path_1.default.basename(filePath);
25
+ const meta = mgr.addAttachment(noteId, filename, data);
26
+ if (!meta) {
27
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Note not found or no project dir' }) }], isError: true };
28
+ }
29
+ return { content: [{ type: 'text', text: JSON.stringify(meta, null, 2) }] };
30
+ });
31
+ }
@@ -0,0 +1,20 @@
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('create_note', {
7
+ description: 'Create a new note or fact in the knowledge graph. ' +
8
+ 'The note is automatically embedded for semantic search. ' +
9
+ 'Returns the generated noteId (slug from title). ' +
10
+ 'Use create_relation to link notes together.',
11
+ inputSchema: {
12
+ title: zod_1.z.string().describe('Short title for the note, e.g. "Auth uses JWT tokens"'),
13
+ content: zod_1.z.string().describe('Full text content of the note'),
14
+ tags: zod_1.z.array(zod_1.z.string()).optional().describe('Optional tags for filtering, e.g. ["architecture", "decision"]'),
15
+ },
16
+ }, async ({ title, content, tags }) => {
17
+ const noteId = await mgr.createNote(title, content, tags ?? []);
18
+ return { content: [{ type: 'text', text: JSON.stringify({ noteId }, null, 2) }] };
19
+ });
20
+ }
@@ -0,0 +1,29 @@
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('create_relation', {
7
+ description: 'Create a directed relation from a note to another note or to a node in the docs/code/files graph. ' +
8
+ 'The kind is a free-form string describing the relationship, ' +
9
+ 'e.g. "relates_to", "depends_on", "contradicts", "supports", "part_of", "references". ' +
10
+ 'Set targetGraph to "docs", "code", "files", or "tasks" to link to a doc chunk, code symbol, file/directory, or task.',
11
+ inputSchema: {
12
+ fromId: zod_1.z.string().describe('Source note ID'),
13
+ toId: zod_1.z.string().describe('Target note ID, or target node ID in docs/code/files/tasks graph'),
14
+ kind: zod_1.z.string().describe('Relation type, e.g. "depends_on", "references"'),
15
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'tasks']).optional()
16
+ .describe('Set to "docs", "code", "files", or "tasks" to create a cross-graph link instead of note-to-note'),
17
+ projectId: zod_1.z.string().optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
18
+ },
19
+ }, async ({ fromId, toId, kind, targetGraph, projectId }) => {
20
+ const created = mgr.createRelation(fromId, toId, kind, targetGraph, projectId);
21
+ if (!created) {
22
+ const msg = targetGraph
23
+ ? 'Could not create cross-graph relation — note not found, target not found in ' + targetGraph + ' graph, or relation already exists.'
24
+ : 'Could not create relation — one or both notes not found, or relation already exists.';
25
+ return { content: [{ type: 'text', text: msg }], isError: true };
26
+ }
27
+ return { content: [{ type: 'text', text: JSON.stringify({ fromId, toId, kind, targetGraph, created: true }, null, 2) }] };
28
+ });
29
+ }
@@ -0,0 +1,19 @@
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('delete_note', {
7
+ description: 'Delete a note from the knowledge graph. ' +
8
+ 'Also removes all relations (edges) connected to this note.',
9
+ inputSchema: {
10
+ noteId: zod_1.z.string().describe('ID of the note to delete'),
11
+ },
12
+ }, async ({ noteId }) => {
13
+ const deleted = mgr.deleteNote(noteId);
14
+ if (!deleted) {
15
+ return { content: [{ type: 'text', text: `Note not found: ${noteId}` }], isError: true };
16
+ }
17
+ return { content: [{ type: 'text', text: JSON.stringify({ noteId, deleted: true }, null, 2) }] };
18
+ });
19
+ }
@@ -0,0 +1,23 @@
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('delete_relation', {
7
+ description: 'Delete a directed relation between a note and another note or a cross-graph target. ' +
8
+ 'Set targetGraph to "docs", "code", "files", or "tasks" when deleting a cross-graph link.',
9
+ inputSchema: {
10
+ fromId: zod_1.z.string().describe('Source note ID'),
11
+ toId: zod_1.z.string().describe('Target note ID, or target node ID in docs/code/files/tasks graph'),
12
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'tasks']).optional()
13
+ .describe('Set to "docs", "code", "files", or "tasks" when deleting a cross-graph link'),
14
+ projectId: zod_1.z.string().optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
15
+ },
16
+ }, async ({ fromId, toId, targetGraph, projectId }) => {
17
+ const deleted = mgr.deleteRelation(fromId, toId, targetGraph, projectId);
18
+ if (!deleted) {
19
+ return { content: [{ type: 'text', text: 'Relation not found.' }], isError: true };
20
+ }
21
+ return { content: [{ type: 'text', text: JSON.stringify({ fromId, toId, targetGraph, deleted: true }, null, 2) }] };
22
+ });
23
+ }
@@ -0,0 +1,25 @@
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('find_linked_notes', {
7
+ description: 'Find all notes in the knowledge graph that link to a specific node in the docs, code, files, or tasks graph. ' +
8
+ 'This is a reverse lookup — given a target (e.g. a file, a code symbol, or a doc section), ' +
9
+ 'returns all notes that reference it via cross-graph relations. ' +
10
+ 'Returns an array of { noteId, title, kind, tags }. ' +
11
+ 'Use get_note to fetch full content of a returned note.',
12
+ inputSchema: {
13
+ targetId: zod_1.z.string().describe('Target node ID in the external graph (e.g. "src/config.ts", "src/auth.ts::login", "docs/api.md::Setup")'),
14
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'tasks']).describe('Which graph the target belongs to: "docs", "code", "files", or "tasks"'),
15
+ kind: zod_1.z.string().optional().describe('Filter by relation kind (e.g. "references", "depends_on"). If omitted, returns all relations.'),
16
+ projectId: zod_1.z.string().optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
17
+ },
18
+ }, async ({ targetId, targetGraph, kind, projectId }) => {
19
+ const results = mgr.findLinkedNotes(targetGraph, targetId, kind, projectId);
20
+ if (results.length === 0) {
21
+ return { content: [{ type: 'text', text: `No notes linked to ${targetGraph}::${targetId}` }] };
22
+ }
23
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
24
+ });
25
+ }
@@ -0,0 +1,20 @@
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_note', {
7
+ description: 'Return the full content of a note by its ID. ' +
8
+ 'Returns id, title, content, tags, createdAt, updatedAt, and relations (including cross-graph links from/to tasks, docs, code, files).',
9
+ inputSchema: {
10
+ noteId: zod_1.z.string().describe('Note ID, e.g. "auth-uses-jwt-tokens"'),
11
+ },
12
+ }, async ({ noteId }) => {
13
+ const note = mgr.getNote(noteId);
14
+ if (!note) {
15
+ return { content: [{ type: 'text', text: `Note not found: ${noteId}` }], isError: true };
16
+ }
17
+ const { embedding: _embedding, ...rest } = note;
18
+ return { content: [{ type: 'text', text: JSON.stringify(rest, null, 2) }] };
19
+ });
20
+ }
@@ -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_notes', {
7
+ description: 'List notes in the knowledge graph. ' +
8
+ 'Optionally filter by title/id substring and/or tag. ' +
9
+ 'Returns an array of { id, title, tags, updatedAt } sorted by most recently updated.',
10
+ inputSchema: {
11
+ filter: zod_1.z.string().optional().describe('Case-insensitive substring to match against note title or ID'),
12
+ tag: zod_1.z.string().optional().describe('Filter by tag (exact match, case-insensitive)'),
13
+ limit: zod_1.z.number().optional().describe('Maximum number of results (default 20)'),
14
+ },
15
+ }, async ({ filter, tag, limit }) => ({
16
+ content: [{ type: 'text', text: JSON.stringify(mgr.listNotes(filter, tag, limit), null, 2) }],
17
+ }));
18
+ }
@@ -0,0 +1,17 @@
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_relations', {
7
+ description: 'List all relations (incoming and outgoing) for a note. ' +
8
+ 'Returns an array of { fromId, toId, kind, targetGraph? }. ' +
9
+ 'Cross-graph links include targetGraph ("docs", "code", "files", or "tasks") and resolve the real node ID.',
10
+ inputSchema: {
11
+ noteId: zod_1.z.string().describe('Note ID to list relations for'),
12
+ },
13
+ }, async ({ noteId }) => {
14
+ const relations = mgr.listRelations(noteId);
15
+ return { content: [{ type: 'text', text: JSON.stringify(relations, null, 2) }] };
16
+ });
17
+ }
@@ -0,0 +1,19 @@
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('remove_note_attachment', {
7
+ description: 'Remove an attachment from a note. The file is deleted from disk.',
8
+ inputSchema: {
9
+ noteId: zod_1.z.string().describe('ID of the note'),
10
+ filename: zod_1.z.string().describe('Filename of the attachment to remove'),
11
+ },
12
+ }, async ({ noteId, filename }) => {
13
+ const ok = mgr.removeAttachment(noteId, filename);
14
+ if (!ok) {
15
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Attachment not found' }) }], isError: true };
16
+ }
17
+ return { content: [{ type: 'text', text: JSON.stringify({ deleted: filename }) }] };
18
+ });
19
+ }
@@ -0,0 +1,25 @@
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_notes', {
7
+ description: 'Semantic search over the knowledge graph (facts and notes). ' +
8
+ 'Finds the most relevant notes using vector similarity, then expands results ' +
9
+ 'by traversing relations between notes (graph walk). ' +
10
+ 'Returns an array sorted by relevance score (0–1), each with: ' +
11
+ 'id, title, content, tags, score.',
12
+ inputSchema: {
13
+ query: zod_1.z.string().describe('Natural language search query'),
14
+ topK: zod_1.z.number().optional().describe('How many top similar notes to use as seeds (default 5)'),
15
+ bfsDepth: zod_1.z.number().optional().describe('How many hops to follow relations from each seed (default 1; 0 = no expansion)'),
16
+ maxResults: zod_1.z.number().optional().describe('Maximum number of results to return (default 20)'),
17
+ minScore: zod_1.z.number().min(0).max(1).optional().describe('Minimum relevance score 0–1 (default 0.5)'),
18
+ bfsDecay: zod_1.z.number().min(0).max(1).optional().describe('Score multiplier per hop (default 0.8)'),
19
+ searchMode: zod_1.z.enum(['hybrid', 'vector', 'keyword']).optional().describe('Search mode: hybrid (default, BM25 + vector), vector (embedding only), keyword (BM25 only)'),
20
+ },
21
+ }, async ({ query, topK, bfsDepth, maxResults, minScore, bfsDecay, searchMode }) => {
22
+ const results = await mgr.searchNotes(query, { topK, bfsDepth, maxResults, minScore, bfsDecay, searchMode });
23
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
24
+ });
25
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.register = register;
4
+ const zod_1 = require("zod");
5
+ const manager_types_1 = require("../../../graphs/manager-types");
6
+ function register(server, mgr) {
7
+ server.registerTool('update_note', {
8
+ description: 'Update an existing note in the knowledge graph. ' +
9
+ 'Only the provided fields are changed; others remain unchanged. ' +
10
+ 'Re-embeds automatically if title or content changes. ' +
11
+ 'Pass expectedVersion to enable optimistic locking.',
12
+ inputSchema: {
13
+ noteId: zod_1.z.string().describe('ID of the note to update'),
14
+ title: zod_1.z.string().optional().describe('New title'),
15
+ content: zod_1.z.string().optional().describe('New content'),
16
+ tags: zod_1.z.array(zod_1.z.string()).optional().describe('New tags (replaces existing)'),
17
+ expectedVersion: zod_1.z.number().int().positive().optional().describe('Current version for optimistic locking — request fails with version_conflict if the note has been updated since'),
18
+ },
19
+ }, async ({ noteId, title, content, tags, expectedVersion }) => {
20
+ try {
21
+ const updated = await mgr.updateNote(noteId, { title, content, tags }, expectedVersion);
22
+ if (!updated) {
23
+ return { content: [{ type: 'text', text: `Note not found: ${noteId}` }], isError: true };
24
+ }
25
+ return { content: [{ type: 'text', text: JSON.stringify({ noteId, updated: true }, null, 2) }] };
26
+ }
27
+ catch (err) {
28
+ if (err instanceof manager_types_1.VersionConflictError) {
29
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'version_conflict', current: err.current, expected: err.expected }) }], isError: true };
30
+ }
31
+ throw err;
32
+ }
33
+ });
34
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.register = register;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const zod_1 = require("zod");
10
+ function register(server, mgr) {
11
+ server.registerTool('add_skill_attachment', {
12
+ description: 'Attach a file to a skill. Provide the absolute path to a local file. ' +
13
+ 'The file is copied into the skill directory (.skills/{skillId}/). ' +
14
+ 'Returns attachment metadata (filename, mimeType, size).',
15
+ inputSchema: {
16
+ skillId: zod_1.z.string().describe('ID of the skill to attach the file to'),
17
+ filePath: zod_1.z.string().describe('Absolute path to the file on disk'),
18
+ },
19
+ }, async ({ skillId, filePath }) => {
20
+ if (!fs_1.default.existsSync(filePath)) {
21
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'File not found' }) }], isError: true };
22
+ }
23
+ const data = fs_1.default.readFileSync(filePath);
24
+ const filename = path_1.default.basename(filePath);
25
+ const meta = mgr.addAttachment(skillId, filename, data);
26
+ if (!meta) {
27
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Skill not found or no project dir' }) }], isError: true };
28
+ }
29
+ return { content: [{ type: 'text', text: JSON.stringify(meta, null, 2) }] };
30
+ });
31
+ }
@@ -0,0 +1,19 @@
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('bump_skill_usage', {
7
+ description: 'Record that a skill was used. Increments usageCount and updates lastUsedAt timestamp. ' +
8
+ 'Call this after successfully applying a skill.',
9
+ inputSchema: {
10
+ skillId: zod_1.z.string().describe('Skill ID to bump usage for'),
11
+ },
12
+ }, async ({ skillId }) => {
13
+ const ok = mgr.bumpUsage(skillId);
14
+ if (!ok) {
15
+ return { content: [{ type: 'text', text: `Skill "${skillId}" not found.` }], isError: true };
16
+ }
17
+ return { content: [{ type: 'text', text: JSON.stringify({ skillId, bumped: true }, null, 2) }] };
18
+ });
19
+ }
@@ -0,0 +1,25 @@
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('create_skill_link', {
7
+ description: 'Link a skill to a node in the docs, code, files, knowledge, or tasks graph. ' +
8
+ 'Creates a cross-graph relation from the skill to the target node. ' +
9
+ 'The kind is a free-form string, e.g. "references", "implements", "documents".',
10
+ inputSchema: {
11
+ skillId: zod_1.z.string().describe('Source skill ID'),
12
+ targetId: zod_1.z.string().describe('Target node ID in the external graph (e.g. "src/auth.ts::login", "api.md::Setup", "my-note", "my-task")'),
13
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'knowledge', 'tasks'])
14
+ .describe('Which graph the target belongs to'),
15
+ kind: zod_1.z.string().describe('Relation type, e.g. "references", "implements", "documents"'),
16
+ projectId: zod_1.z.string().optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
17
+ },
18
+ }, async ({ skillId, targetId, targetGraph, kind, projectId }) => {
19
+ const created = mgr.createCrossLink(skillId, targetId, targetGraph, kind, projectId);
20
+ if (!created) {
21
+ return { content: [{ type: 'text', text: `Could not create cross-graph link — skill not found, target not found in ${targetGraph} graph, or link already exists.` }], isError: true };
22
+ }
23
+ return { content: [{ type: 'text', text: JSON.stringify({ skillId, targetId, targetGraph, kind, created: true }, null, 2) }] };
24
+ });
25
+ }
@@ -0,0 +1,26 @@
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('create_skill', {
7
+ description: 'Create a new skill (reusable recipe/procedure) in the skill graph. ' +
8
+ 'The skill is automatically embedded for semantic search. ' +
9
+ 'Returns the generated skillId (slug from title). ' +
10
+ 'Use link_skill to connect skills, or create_skill_link to link to docs/code/files/knowledge/tasks.',
11
+ inputSchema: {
12
+ title: zod_1.z.string().describe('Short title for the skill, e.g. "Deploy to staging"'),
13
+ description: zod_1.z.string().describe('Full description of the skill (markdown)'),
14
+ steps: zod_1.z.array(zod_1.z.string()).optional().describe('Ordered steps to execute this skill (default [])'),
15
+ triggers: zod_1.z.array(zod_1.z.string()).optional().describe('Conditions or cues that suggest using this skill (default [])'),
16
+ inputHints: zod_1.z.array(zod_1.z.string()).optional().describe('Expected inputs or prerequisites (default [])'),
17
+ filePatterns: zod_1.z.array(zod_1.z.string()).optional().describe('Glob patterns for files this skill applies to (default [])'),
18
+ tags: zod_1.z.array(zod_1.z.string()).optional().describe('Optional tags for filtering, e.g. ["deploy", "ci"]'),
19
+ source: zod_1.z.enum(['user', 'learned']).optional().describe('How this skill was created (default "user")'),
20
+ confidence: zod_1.z.number().min(0).max(1).optional().describe('Confidence score 0–1 (default 1)'),
21
+ },
22
+ }, async ({ title, description, steps, triggers, inputHints, filePatterns, tags, source, confidence }) => {
23
+ const skillId = await mgr.createSkill(title, description, steps ?? [], triggers ?? [], inputHints ?? [], filePatterns ?? [], tags ?? [], source ?? 'user', confidence ?? 1);
24
+ return { content: [{ type: 'text', text: JSON.stringify({ skillId }, null, 2) }] };
25
+ });
26
+ }
@@ -0,0 +1,23 @@
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('delete_skill_link', {
7
+ description: 'Remove a cross-graph link from a skill to a node in the docs, code, files, knowledge, or tasks graph. ' +
8
+ 'Orphaned proxy nodes are cleaned up automatically.',
9
+ inputSchema: {
10
+ skillId: zod_1.z.string().describe('Source skill ID'),
11
+ targetId: zod_1.z.string().describe('Target node ID in the external graph'),
12
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'knowledge', 'tasks'])
13
+ .describe('Which graph the target belongs to'),
14
+ projectId: zod_1.z.string().optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
15
+ },
16
+ }, async ({ skillId, targetId, targetGraph, projectId }) => {
17
+ const deleted = mgr.deleteCrossLink(skillId, targetId, targetGraph, projectId);
18
+ if (!deleted) {
19
+ return { content: [{ type: 'text', text: 'Cross-graph link not found.' }], isError: true };
20
+ }
21
+ return { content: [{ type: 'text', text: JSON.stringify({ skillId, targetId, targetGraph, deleted: true }, null, 2) }] };
22
+ });
23
+ }
@@ -0,0 +1,20 @@
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('delete_skill', {
7
+ description: 'Delete a skill and all its edges (relations, cross-graph links). ' +
8
+ 'Orphaned proxy nodes are cleaned up automatically. ' +
9
+ 'This action is irreversible.',
10
+ inputSchema: {
11
+ skillId: zod_1.z.string().describe('Skill ID to delete'),
12
+ },
13
+ }, async ({ skillId }) => {
14
+ const deleted = mgr.deleteSkill(skillId);
15
+ if (!deleted) {
16
+ return { content: [{ type: 'text', text: `Skill "${skillId}" not found.` }], isError: true };
17
+ }
18
+ return { content: [{ type: 'text', text: JSON.stringify({ skillId, deleted: true }, null, 2) }] };
19
+ });
20
+ }
@@ -0,0 +1,25 @@
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('find_linked_skills', {
7
+ description: 'Find all skills that link to a specific node in the docs, code, files, knowledge, or tasks graph. ' +
8
+ 'This is a reverse lookup — given a target, returns all skills that reference it. ' +
9
+ 'Returns an array of { skillId, title, kind, source, confidence, tags }. ' +
10
+ 'Use get_skill to fetch full content of a returned skill.',
11
+ inputSchema: {
12
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'knowledge', 'tasks'])
13
+ .describe('Which graph the target belongs to'),
14
+ targetNodeId: zod_1.z.string().describe('Target node ID in the external graph'),
15
+ kind: zod_1.z.string().optional().describe('Filter by relation kind. If omitted, returns all relations.'),
16
+ projectId: zod_1.z.string().optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
17
+ },
18
+ }, async ({ targetGraph, targetNodeId, kind, projectId }) => {
19
+ const results = mgr.findLinkedSkills(targetGraph, targetNodeId, kind, projectId);
20
+ if (results.length === 0) {
21
+ return { content: [{ type: 'text', text: `No skills linked to ${targetGraph}::${targetNodeId}` }] };
22
+ }
23
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
24
+ });
25
+ }
@@ -0,0 +1,21 @@
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_skill', {
7
+ description: 'Get full details of a skill by ID, including steps, triggers, inputHints, filePatterns, ' +
8
+ 'usage stats, and cross-graph links. ' +
9
+ 'Returns: id, title, description, steps, triggers, inputHints, filePatterns, tags, ' +
10
+ 'source, confidence, usageCount, lastUsedAt, createdAt, updatedAt, crossLinks[].',
11
+ inputSchema: {
12
+ skillId: zod_1.z.string().describe('Skill ID to retrieve'),
13
+ },
14
+ }, async ({ skillId }) => {
15
+ const skill = mgr.getSkill(skillId);
16
+ if (!skill) {
17
+ return { content: [{ type: 'text', text: `Skill "${skillId}" not found.` }], isError: true };
18
+ }
19
+ return { content: [{ type: 'text', text: JSON.stringify(skill, null, 2) }] };
20
+ });
21
+ }
@@ -0,0 +1,23 @@
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('link_skill', {
7
+ description: 'Create a directed relation between two skills. ' +
8
+ '"depends_on": fromId depends on toId. ' +
9
+ '"related_to": free association between skills. ' +
10
+ '"variant_of": fromId is a variant of toId.',
11
+ inputSchema: {
12
+ fromId: zod_1.z.string().describe('Source skill ID'),
13
+ toId: zod_1.z.string().describe('Target skill ID'),
14
+ kind: zod_1.z.enum(['depends_on', 'related_to', 'variant_of']).describe('Relation type'),
15
+ },
16
+ }, async ({ fromId, toId, kind }) => {
17
+ const created = mgr.linkSkills(fromId, toId, kind);
18
+ if (!created) {
19
+ return { content: [{ type: 'text', text: 'Could not create relation — one or both skills not found, or relation already exists.' }], isError: true };
20
+ }
21
+ return { content: [{ type: 'text', text: JSON.stringify({ fromId, toId, kind, created: true }, null, 2) }] };
22
+ });
23
+ }
@@ -0,0 +1,20 @@
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_skills', {
7
+ description: 'List skills with optional filters. ' +
8
+ 'Returns an array of { id, title, description, tags, source, confidence, usageCount, lastUsedAt, createdAt, updatedAt }. ' +
9
+ 'Use search_skills for semantic search.',
10
+ inputSchema: {
11
+ source: zod_1.z.enum(['user', 'learned']).optional().describe('Filter by source'),
12
+ tag: zod_1.z.string().optional().describe('Filter by tag (exact match, case-insensitive)'),
13
+ filter: zod_1.z.string().optional().describe('Substring match on title or ID'),
14
+ limit: zod_1.z.number().optional().describe('Max results (default 50)'),
15
+ },
16
+ }, async ({ source, tag, filter, limit }) => {
17
+ const results = mgr.listSkills({ source, tag, filter, limit });
18
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
19
+ });
20
+ }
@@ -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('recall_skills', {
7
+ description: 'Recall relevant skills for a given task context. Like search_skills but with lower ' +
8
+ 'minScore default (0.3) for higher recall. Use at the start of a task to find applicable recipes.',
9
+ inputSchema: {
10
+ context: zod_1.z.string().describe('Description of the current task or context to match skills against'),
11
+ topK: zod_1.z.number().optional().describe('How many top similar skills to use as seeds (default 5)'),
12
+ minScore: zod_1.z.number().min(0).max(1).optional().describe('Minimum relevance score 0–1 (default 0.3)'),
13
+ },
14
+ }, async ({ context, topK, minScore }) => {
15
+ const results = await mgr.searchSkills(context, { topK, minScore: minScore ?? 0.3 });
16
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
17
+ });
18
+ }