@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,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_skill_attachment', {
7
+ description: 'Remove an attachment from a skill. The file is deleted from disk.',
8
+ inputSchema: {
9
+ skillId: zod_1.z.string().describe('ID of the skill'),
10
+ filename: zod_1.z.string().describe('Filename of the attachment to remove'),
11
+ },
12
+ }, async ({ skillId, filename }) => {
13
+ const ok = mgr.removeAttachment(skillId, 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_skills', {
7
+ description: 'Semantic search over the skill graph. ' +
8
+ 'Finds the most relevant skills using vector similarity, then expands results ' +
9
+ 'by traversing relations between skills (graph walk). ' +
10
+ 'Returns an array sorted by relevance score (0–1), each with: ' +
11
+ 'id, title, description, tags, source, confidence, 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 skills 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.searchSkills(query, { topK, bfsDepth, maxResults, minScore, bfsDecay, searchMode });
23
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
24
+ });
25
+ }
@@ -0,0 +1,58 @@
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_skill', {
8
+ description: 'Update an existing skill. Only provided fields are changed. ' +
9
+ 'Re-embeds automatically when title or description changes. ' +
10
+ 'Pass expectedVersion to enable optimistic locking.',
11
+ inputSchema: {
12
+ skillId: zod_1.z.string().describe('Skill ID to update'),
13
+ title: zod_1.z.string().optional().describe('New title'),
14
+ description: zod_1.z.string().optional().describe('New description'),
15
+ steps: zod_1.z.array(zod_1.z.string()).optional().describe('Replace steps array'),
16
+ triggers: zod_1.z.array(zod_1.z.string()).optional().describe('Replace triggers array'),
17
+ inputHints: zod_1.z.array(zod_1.z.string()).optional().describe('Replace inputHints array'),
18
+ filePatterns: zod_1.z.array(zod_1.z.string()).optional().describe('Replace filePatterns array'),
19
+ tags: zod_1.z.array(zod_1.z.string()).optional().describe('Replace tags array'),
20
+ source: zod_1.z.enum(['user', 'learned']).optional().describe('New source'),
21
+ confidence: zod_1.z.number().min(0).max(1).optional().describe('New confidence score 0–1'),
22
+ expectedVersion: zod_1.z.number().int().positive().optional().describe('Current version for optimistic locking — request fails with version_conflict if the skill has been updated since'),
23
+ },
24
+ }, async ({ skillId, title, description, steps, triggers, inputHints, filePatterns, tags, source, confidence, expectedVersion }) => {
25
+ const patch = {};
26
+ if (title !== undefined)
27
+ patch.title = title;
28
+ if (description !== undefined)
29
+ patch.description = description;
30
+ if (steps !== undefined)
31
+ patch.steps = steps;
32
+ if (triggers !== undefined)
33
+ patch.triggers = triggers;
34
+ if (inputHints !== undefined)
35
+ patch.inputHints = inputHints;
36
+ if (filePatterns !== undefined)
37
+ patch.filePatterns = filePatterns;
38
+ if (tags !== undefined)
39
+ patch.tags = tags;
40
+ if (source !== undefined)
41
+ patch.source = source;
42
+ if (confidence !== undefined)
43
+ patch.confidence = confidence;
44
+ try {
45
+ const updated = await mgr.updateSkill(skillId, patch, expectedVersion);
46
+ if (!updated) {
47
+ return { content: [{ type: 'text', text: `Skill "${skillId}" not found.` }], isError: true };
48
+ }
49
+ return { content: [{ type: 'text', text: JSON.stringify({ skillId, updated: true }, null, 2) }] };
50
+ }
51
+ catch (err) {
52
+ if (err instanceof manager_types_1.VersionConflictError) {
53
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'version_conflict', current: err.current, expected: err.expected }) }], isError: true };
54
+ }
55
+ throw err;
56
+ }
57
+ });
58
+ }
@@ -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_task_attachment', {
12
+ description: 'Attach a file to a task. Provide the absolute path to a local file. ' +
13
+ 'The file is copied into the task directory (.tasks/{taskId}/). ' +
14
+ 'Returns attachment metadata (filename, mimeType, size).',
15
+ inputSchema: {
16
+ taskId: zod_1.z.string().describe('ID of the task to attach the file to'),
17
+ filePath: zod_1.z.string().describe('Absolute path to the file on disk'),
18
+ },
19
+ }, async ({ taskId, 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(taskId, filename, data);
26
+ if (!meta) {
27
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Task 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,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_task_link', {
7
+ description: 'Link a task to a node in the docs, code, files, or knowledge graph. ' +
8
+ 'Creates a cross-graph relation from the task to the target node. ' +
9
+ 'The kind is a free-form string, e.g. "references", "fixes", "implements", "documents".',
10
+ inputSchema: {
11
+ taskId: zod_1.z.string().describe('Source task 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")'),
13
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'knowledge'])
14
+ .describe('Which graph the target belongs to'),
15
+ kind: zod_1.z.string().describe('Relation type, e.g. "references", "fixes", "implements"'),
16
+ projectId: zod_1.z.string().optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
17
+ },
18
+ }, async ({ taskId, targetId, targetGraph, kind, projectId }) => {
19
+ const created = mgr.createCrossLink(taskId, targetId, targetGraph, kind, projectId);
20
+ if (!created) {
21
+ return { content: [{ type: 'text', text: `Could not create cross-graph link — task not found, target not found in ${targetGraph} graph, or link already exists.` }], isError: true };
22
+ }
23
+ return { content: [{ type: 'text', text: JSON.stringify({ taskId, 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_task', {
7
+ description: 'Create a new task in the task graph. ' +
8
+ 'The task is automatically embedded for semantic search. ' +
9
+ 'Returns the generated taskId (slug from title). ' +
10
+ 'Use link_task to connect tasks, or create_task_link to link to docs/code/files/knowledge.',
11
+ inputSchema: {
12
+ title: zod_1.z.string().describe('Short title for the task, e.g. "Fix auth redirect loop"'),
13
+ description: zod_1.z.string().describe('Full description of the task (markdown)'),
14
+ priority: zod_1.z.enum(['critical', 'high', 'medium', 'low']).describe('Task priority'),
15
+ status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled']).optional()
16
+ .describe('Initial status (default "backlog")'),
17
+ tags: zod_1.z.array(zod_1.z.string()).optional().describe('Optional tags for filtering, e.g. ["bug", "auth"]'),
18
+ dueDate: zod_1.z.number().optional().describe('Due date as Unix timestamp in milliseconds'),
19
+ estimate: zod_1.z.number().optional().describe('Estimated effort in hours'),
20
+ assignee: zod_1.z.string().optional().describe('Team member ID to assign the task to'),
21
+ },
22
+ }, async ({ title, description, priority, status, tags, dueDate, estimate, assignee }) => {
23
+ const taskId = await mgr.createTask(title, description, status ?? 'backlog', priority, tags ?? [], dueDate ?? null, estimate ?? null, assignee ?? null);
24
+ return { content: [{ type: 'text', text: JSON.stringify({ taskId }, 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_task_link', {
7
+ description: 'Remove a cross-graph link from a task to a node in the docs, code, files, or knowledge graph. ' +
8
+ 'Orphaned proxy nodes are cleaned up automatically.',
9
+ inputSchema: {
10
+ taskId: zod_1.z.string().describe('Source task 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'])
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 ({ taskId, targetId, targetGraph, projectId }) => {
17
+ const deleted = mgr.deleteCrossLink(taskId, 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({ taskId, 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_task', {
7
+ description: 'Delete a task and all its edges (relations, cross-graph links). ' +
8
+ 'Orphaned proxy nodes are cleaned up automatically. ' +
9
+ 'This action is irreversible.',
10
+ inputSchema: {
11
+ taskId: zod_1.z.string().describe('Task ID to delete'),
12
+ },
13
+ }, async ({ taskId }) => {
14
+ const deleted = mgr.deleteTask(taskId);
15
+ if (!deleted) {
16
+ return { content: [{ type: 'text', text: `Task "${taskId}" not found.` }], isError: true };
17
+ }
18
+ return { content: [{ type: 'text', text: JSON.stringify({ taskId, 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_tasks', {
7
+ description: 'Find all tasks that link to a specific node in the docs, code, files, or knowledge graph. ' +
8
+ 'This is a reverse lookup — given a target, returns all tasks that reference it. ' +
9
+ 'Returns an array of { taskId, title, kind, status, priority, tags }. ' +
10
+ 'Use get_task to fetch full content of a returned task.',
11
+ inputSchema: {
12
+ targetId: zod_1.z.string().describe('Target node ID in the external graph'),
13
+ targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'knowledge'])
14
+ .describe('Which graph the target belongs to'),
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 ({ targetId, targetGraph, kind, projectId }) => {
19
+ const results = mgr.findLinkedTasks(targetGraph, targetId, kind, projectId);
20
+ if (results.length === 0) {
21
+ return { content: [{ type: 'text', text: `No tasks 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_task', {
7
+ description: 'Get full details of a task by ID, including subtasks, blockers, related tasks, and cross-graph links. ' +
8
+ 'Returns: id, title, description, status, priority, tags, dueDate, estimate, ' +
9
+ 'completedAt, createdAt, updatedAt, subtasks[], blockedBy[], blocks[], related[], crossLinks[].',
10
+ inputSchema: {
11
+ taskId: zod_1.z.string().describe('Task ID to retrieve'),
12
+ },
13
+ }, async ({ taskId }) => {
14
+ const task = mgr.getTask(taskId);
15
+ if (!task) {
16
+ return { content: [{ type: 'text', text: `Task "${taskId}" not found.` }], isError: true };
17
+ }
18
+ return { content: [{ type: 'text', text: JSON.stringify(task, null, 2) }] };
19
+ });
20
+ }
@@ -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_task', {
7
+ description: 'Create a directed relation between two tasks. ' +
8
+ '"subtask_of": fromId is a subtask of toId. ' +
9
+ '"blocks": fromId blocks toId. ' +
10
+ '"related_to": free association between tasks.',
11
+ inputSchema: {
12
+ fromId: zod_1.z.string().describe('Source task ID'),
13
+ toId: zod_1.z.string().describe('Target task ID'),
14
+ kind: zod_1.z.enum(['subtask_of', 'blocks', 'related_to']).describe('Relation type'),
15
+ },
16
+ }, async ({ fromId, toId, kind }) => {
17
+ const created = mgr.linkTasks(fromId, toId, kind);
18
+ if (!created) {
19
+ return { content: [{ type: 'text', text: 'Could not create relation — one or both tasks 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,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('list_tasks', {
7
+ description: 'List tasks with optional filters. ' +
8
+ 'Sorted by priority (critical first) then due date (earliest first, nulls last). ' +
9
+ 'Returns an array of { id, title, description, status, priority, tags, dueDate, estimate, completedAt, createdAt, updatedAt }. ' +
10
+ 'Use search_tasks for semantic search.',
11
+ inputSchema: {
12
+ status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled']).optional()
13
+ .describe('Filter by status'),
14
+ priority: zod_1.z.enum(['critical', 'high', 'medium', 'low']).optional()
15
+ .describe('Filter by priority'),
16
+ tag: zod_1.z.string().optional().describe('Filter by tag (exact match, case-insensitive)'),
17
+ filter: zod_1.z.string().optional().describe('Substring match on title or ID'),
18
+ assignee: zod_1.z.string().optional().describe('Filter by assignee (team member ID)'),
19
+ limit: zod_1.z.number().optional().describe('Max results (default 50)'),
20
+ },
21
+ }, async ({ status, priority, tag, filter, assignee, limit }) => {
22
+ const results = mgr.listTasks({ status, priority, tag, filter, assignee, limit });
23
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
24
+ });
25
+ }
@@ -0,0 +1,38 @@
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('move_task', {
8
+ description: 'Change a task status. Automatically manages completedAt: ' +
9
+ 'sets it when moving to "done" or "cancelled", clears it when reopening. ' +
10
+ 'Returns the updated task summary. ' +
11
+ 'Pass expectedVersion to enable optimistic locking.',
12
+ inputSchema: {
13
+ taskId: zod_1.z.string().describe('Task ID to move'),
14
+ status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled'])
15
+ .describe('New status'),
16
+ expectedVersion: zod_1.z.number().int().positive().optional().describe('Current version for optimistic locking — request fails with version_conflict if the task has been updated since'),
17
+ },
18
+ }, async ({ taskId, status, expectedVersion }) => {
19
+ try {
20
+ const moved = mgr.moveTask(taskId, status, expectedVersion);
21
+ if (!moved) {
22
+ return { content: [{ type: 'text', text: `Task "${taskId}" not found.` }], isError: true };
23
+ }
24
+ const task = mgr.getTask(taskId);
25
+ return { content: [{ type: 'text', text: JSON.stringify({
26
+ taskId: task.id,
27
+ status: task.status,
28
+ completedAt: task.completedAt,
29
+ }, null, 2) }] };
30
+ }
31
+ catch (err) {
32
+ if (err instanceof manager_types_1.VersionConflictError) {
33
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'version_conflict', current: err.current, expected: err.expected }) }], isError: true };
34
+ }
35
+ throw err;
36
+ }
37
+ });
38
+ }
@@ -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_task_attachment', {
7
+ description: 'Remove an attachment from a task. The file is deleted from disk.',
8
+ inputSchema: {
9
+ taskId: zod_1.z.string().describe('ID of the task'),
10
+ filename: zod_1.z.string().describe('Filename of the attachment to remove'),
11
+ },
12
+ }, async ({ taskId, filename }) => {
13
+ const ok = mgr.removeAttachment(taskId, 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_tasks', {
7
+ description: 'Semantic search over the task graph. ' +
8
+ 'Finds the most relevant tasks using vector similarity, then expands results ' +
9
+ 'by traversing relations between tasks (graph walk). ' +
10
+ 'Returns an array sorted by relevance score (0–1), each with: ' +
11
+ 'id, title, description, status, priority, 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 tasks 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.searchTasks(query, { topK, bfsDepth, maxResults, minScore, bfsDecay, searchMode });
23
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
24
+ });
25
+ }
@@ -0,0 +1,58 @@
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_task', {
8
+ description: 'Update an existing task. Only provided fields are changed. ' +
9
+ 'Re-embeds automatically when title or description changes. ' +
10
+ 'Status changes auto-manage completedAt (set on done/cancelled, cleared on reopen). ' +
11
+ 'Use move_task for a simpler status-only change. ' +
12
+ 'Pass expectedVersion to enable optimistic locking.',
13
+ inputSchema: {
14
+ taskId: zod_1.z.string().describe('Task ID to update'),
15
+ title: zod_1.z.string().optional().describe('New title'),
16
+ description: zod_1.z.string().optional().describe('New description'),
17
+ status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled']).optional()
18
+ .describe('New status'),
19
+ priority: zod_1.z.enum(['critical', 'high', 'medium', 'low']).optional().describe('New priority'),
20
+ tags: zod_1.z.array(zod_1.z.string()).optional().describe('Replace tags array'),
21
+ dueDate: zod_1.z.number().nullable().optional().describe('New due date (ms timestamp) or null to clear'),
22
+ estimate: zod_1.z.number().nullable().optional().describe('New estimate (hours) or null to clear'),
23
+ assignee: zod_1.z.string().nullable().optional().describe('Team member ID to assign, or null to unassign'),
24
+ expectedVersion: zod_1.z.number().int().positive().optional().describe('Current version for optimistic locking — request fails with version_conflict if the task has been updated since'),
25
+ },
26
+ }, async ({ taskId, title, description, status, priority, tags, dueDate, estimate, assignee, expectedVersion }) => {
27
+ const patch = {};
28
+ if (title !== undefined)
29
+ patch.title = title;
30
+ if (description !== undefined)
31
+ patch.description = description;
32
+ if (status !== undefined)
33
+ patch.status = status;
34
+ if (priority !== undefined)
35
+ patch.priority = priority;
36
+ if (tags !== undefined)
37
+ patch.tags = tags;
38
+ if (dueDate !== undefined)
39
+ patch.dueDate = dueDate;
40
+ if (estimate !== undefined)
41
+ patch.estimate = estimate;
42
+ if (assignee !== undefined)
43
+ patch.assignee = assignee;
44
+ try {
45
+ const updated = await mgr.updateTask(taskId, patch, expectedVersion);
46
+ if (!updated) {
47
+ return { content: [{ type: 'text', text: `Task "${taskId}" not found.` }], isError: true };
48
+ }
49
+ return { content: [{ type: 'text', text: JSON.stringify({ taskId, updated: true }, null, 2) }] };
50
+ }
51
+ catch (err) {
52
+ if (err instanceof manager_types_1.VersionConflictError) {
53
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'version_conflict', current: err.current, expected: err.expected }) }], isError: true };
54
+ }
55
+ throw err;
56
+ }
57
+ });
58
+ }