code-graph-context 2.14.1 → 3.0.1

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 (38) hide show
  1. package/README.md +2 -2
  2. package/dist/core/embeddings/natural-language-to-cypher.service.js +2 -4
  3. package/dist/mcp/constants.js +56 -228
  4. package/dist/mcp/handlers/swarm/abandon.handler.js +61 -0
  5. package/dist/mcp/handlers/swarm/advance.handler.js +78 -0
  6. package/dist/mcp/handlers/swarm/claim.handler.js +61 -0
  7. package/dist/mcp/handlers/swarm/index.js +5 -0
  8. package/dist/mcp/handlers/swarm/queries.js +140 -0
  9. package/dist/mcp/handlers/swarm/release.handler.js +41 -0
  10. package/dist/mcp/handlers/swarm-worker.handler.js +2 -13
  11. package/dist/mcp/tools/detect-dead-code.tool.js +33 -65
  12. package/dist/mcp/tools/detect-duplicate-code.tool.js +44 -53
  13. package/dist/mcp/tools/impact-analysis.tool.js +1 -1
  14. package/dist/mcp/tools/index.js +9 -9
  15. package/dist/mcp/tools/list-projects.tool.js +2 -2
  16. package/dist/mcp/tools/list-watchers.tool.js +2 -5
  17. package/dist/mcp/tools/natural-language-to-cypher.tool.js +2 -2
  18. package/dist/mcp/tools/parse-typescript-project.tool.js +7 -17
  19. package/dist/mcp/tools/search-codebase.tool.js +11 -26
  20. package/dist/mcp/tools/session-bookmark.tool.js +7 -11
  21. package/dist/mcp/tools/session-cleanup.tool.js +2 -6
  22. package/dist/mcp/tools/session-note.tool.js +6 -21
  23. package/dist/mcp/tools/session-recall.tool.js +293 -0
  24. package/dist/mcp/tools/session-save.tool.js +280 -0
  25. package/dist/mcp/tools/start-watch-project.tool.js +1 -1
  26. package/dist/mcp/tools/swarm-advance-task.tool.js +56 -0
  27. package/dist/mcp/tools/swarm-claim-task.tool.js +24 -388
  28. package/dist/mcp/tools/swarm-cleanup.tool.js +3 -7
  29. package/dist/mcp/tools/swarm-complete-task.tool.js +14 -17
  30. package/dist/mcp/tools/swarm-get-tasks.tool.js +8 -26
  31. package/dist/mcp/tools/swarm-message.tool.js +10 -25
  32. package/dist/mcp/tools/swarm-pheromone.tool.js +7 -25
  33. package/dist/mcp/tools/swarm-post-task.tool.js +7 -19
  34. package/dist/mcp/tools/swarm-release-task.tool.js +53 -0
  35. package/dist/mcp/tools/swarm-sense.tool.js +10 -30
  36. package/dist/mcp/tools/traverse-from-node.tool.js +19 -41
  37. package/dist/mcp/utils.js +41 -1
  38. package/package.json +3 -3
package/README.md CHANGED
@@ -674,7 +674,7 @@ code-graph-context stop # Stop Neo4j container
674
674
  ## Contributing
675
675
 
676
676
  ```bash
677
- git clone https://github.com/drewdrewH/code-graph-context.git
677
+ git clone https://github.com/andrew-hernandez-paragon/code-graph-context.git
678
678
  cd code-graph-context
679
679
  npm install
680
680
  npm run build
@@ -693,6 +693,6 @@ MIT - see [LICENSE](LICENSE)
693
693
 
694
694
  ## Links
695
695
 
696
- - [Issues](https://github.com/drewdrewH/code-graph-context/issues)
696
+ - [Issues](https://github.com/andrew-hernandez-paragon/code-graph-context/issues)
697
697
  - [MCP Documentation](https://modelcontextprotocol.io/docs)
698
698
  - [Neo4j](https://neo4j.com/)
@@ -77,7 +77,7 @@ CONTAINS (file→declaration), HAS_MEMBER (class→method/property), HAS_PARAMET
77
77
  try {
78
78
  const content = fs.readFileSync(this.schemaPath, 'utf-8');
79
79
  const schema = JSON.parse(content);
80
- if (!schema || !schema.nodeTypes) {
80
+ if (!schema?.nodeTypes) {
81
81
  return 'No schema available.';
82
82
  }
83
83
  // Format node types with properties
@@ -93,9 +93,7 @@ CONTAINS (file→declaration), HAS_MEMBER (class→method/property), HAS_PARAMET
93
93
  .join('\n') ?? 'none';
94
94
  // Format semantic types
95
95
  const semanticTypeList = schema.semanticTypes?.map((s) => s.type) ?? [];
96
- const semTypeLines = schema.semanticTypes
97
- ?.map((s) => ` ${s.type} (on ${s.label}, ${s.count} nodes)`)
98
- .join('\n') ?? 'none';
96
+ const semTypeLines = schema.semanticTypes?.map((s) => ` ${s.type} (on ${s.label}, ${s.count} nodes)`).join('\n') ?? 'none';
99
97
  // Format common patterns
100
98
  const patternLines = schema.commonPatterns
101
99
  ?.map((p) => ` (${p.from})-[:${p.relationship}]->(${p.to}) × ${p.count}`)
@@ -36,6 +36,8 @@ export const TOOL_NAMES = {
36
36
  swarmCleanup: 'swarm_cleanup',
37
37
  swarmPostTask: 'swarm_post_task',
38
38
  swarmClaimTask: 'swarm_claim_task',
39
+ swarmReleaseTask: 'swarm_release_task',
40
+ swarmAdvanceTask: 'swarm_advance_task',
39
41
  swarmCompleteTask: 'swarm_complete_task',
40
42
  swarmGetTasks: 'swarm_get_tasks',
41
43
  saveSessionBookmark: 'save_session_bookmark',
@@ -44,327 +46,153 @@ export const TOOL_NAMES = {
44
46
  recallSessionNotes: 'recall_session_notes',
45
47
  cleanupSession: 'cleanup_session',
46
48
  swarmMessage: 'swarm_message',
49
+ sessionSave: 'session_save',
50
+ sessionRecall: 'session_recall',
47
51
  };
48
52
  // Tool Metadata
49
53
  export const TOOL_METADATA = {
50
54
  [TOOL_NAMES.hello]: {
51
55
  title: 'Hello Tool',
52
- description: 'Test tool that says hello',
56
+ description: 'Diagnostic tool. Use only to verify the MCP server is running.',
53
57
  },
54
58
  [TOOL_NAMES.searchCodebase]: {
55
59
  title: 'Search Codebase',
56
- description: `Semantic search for code, functions, classes, implementations. Returns normalized JSON with source code.
60
+ description: `Primary tool for finding code. Use this first for any code exploration query. Combines semantic vector search with dependency graph traversal from the best match.
57
61
 
58
- Use list_projects first to get project name/ID.
59
-
60
- Parameters:
61
- - query: Natural language description of what you're looking for
62
- - maxDepth (default: 3): Relationship hops to traverse
63
- - includeCode (default: true): Set false for structure only
64
- - snippetLength (default: 700): Code snippet length
65
- - maxNodesPerChain (default: 5): Chains per depth level
66
-
67
- If output too large: reduce maxDepth, set includeCode=false, or reduce snippetLength.`,
62
+ Returns normalized JSON with nodes map and relationship chains. If output too large: reduce maxDepth, set includeCode=false, or reduce snippetLength.`,
68
63
  },
69
64
  [TOOL_NAMES.naturalLanguageToCypher]: {
70
65
  title: 'Natural Language to Cypher',
71
- description: `Convert natural language to Cypher for complex queries search_codebase can't handle.
72
-
73
- Use list_projects first to get project name.
66
+ description: `Advanced query tool. Use only when search_codebase cannot answer the question — aggregate queries ('how many services exist'), complex relationship patterns, or bulk property filtering. Requires OPENAI_API_KEY.
74
67
 
75
68
  **Node types:** SourceFile, Class, Function, Method, Interface, Property, Parameter, Import, Export, Enum, TypeAlias
76
-
77
69
  **Key relationships:** CONTAINS, HAS_MEMBER, HAS_PARAMETER, IMPORTS, EXTENDS, IMPLEMENTS, CALLS, TYPED_AS
78
-
79
- **NestJS:** Use semanticType property (e.g., semanticType='NestController'), not decorators. Relationships: INJECTS, EXPOSES, MODULE_IMPORTS/PROVIDES/EXPORTS
80
-
81
- **Tips:** Use concrete properties (filePath, name) not abstract concepts. Import nodes store file paths, not module names.`,
70
+ **NestJS:** Use semanticType property (e.g., semanticType='NestController'). Relationships: INJECTS, EXPOSES, MODULE_IMPORTS/PROVIDES/EXPORTS
71
+ **Tips:** Use concrete properties (filePath, name) not abstract concepts.`,
82
72
  },
83
73
  [TOOL_NAMES.traverseFromNode]: {
84
74
  title: 'Traverse from Node',
85
- description: `Explore connections from a node ID (from search_codebase results).
75
+ description: `Follow-up exploration tool. Use after search_codebase when you have a specific node ID and want to explore its relationships in more depth or different directions.
86
76
 
87
- Parameters:
88
- - nodeId (required): Starting node ID
89
- - maxDepth (default: 3): Relationship hops (1-10)
90
- - includeCode (default: true): Set false for structure only
91
- - summaryOnly: true for file paths and stats only
92
- - maxNodesPerChain (default: 5): Chains per depth level
93
- - maxTotalNodes: Cap unique nodes returned`,
77
+ Accepts nodeId or filePath as starting point. Set summaryOnly=true for file paths and stats only.`,
94
78
  },
95
79
  [TOOL_NAMES.parseTypescriptProject]: {
96
80
  title: 'Parse TypeScript Project',
97
- description: `Parse a TypeScript/NestJS project and build a code graph in Neo4j.
98
-
99
- **IMPORTANT: Always use async mode for parsing:**
100
- - Set async: true to avoid timeouts on large codebases
101
- - Use check_parse_status to monitor progress
81
+ description: `Setup tool. Parse a TypeScript/NestJS project and build a code graph in Neo4j. Run once per project, then use search_codebase to query.
102
82
 
103
- **Workflow:**
104
- 1. Call with async: true and projectPath
105
- 2. Note the returned jobId
106
- 3. Poll check_parse_status({ jobId }) until completed
107
- 4. Use list_projects to confirm the project was added
108
-
109
- **Parameters:**
110
- - projectPath (required): Absolute path to the project root
111
- - async (recommended: true): Run parsing in background
112
- - clearExisting: Set true to replace existing graph for this project
113
- - projectId: Optional custom ID (auto-generated from path if omitted)
114
-
115
- **Example:**
116
- parse_typescript_project({ projectPath: "/path/to/project", async: true })
117
- → Returns jobId for polling`,
83
+ **Always use async mode:** set async=true, then poll check_parse_status with the returned jobId. Set clearExisting=true to replace an existing graph.`,
118
84
  },
119
85
  [TOOL_NAMES.testNeo4jConnection]: {
120
86
  title: 'Test Neo4j Connection & APOC',
121
- description: 'Test connection to Neo4j database and verify APOC plugin is available',
87
+ description: 'Diagnostic tool. Use only to verify Neo4j connectivity and APOC plugin availability.',
122
88
  },
123
89
  [TOOL_NAMES.impactAnalysis]: {
124
90
  title: 'Impact Analysis',
125
- description: `Analyze the impact of modifying a code node. Shows what depends on this node and helps assess risk before making changes.
126
-
127
- **Before analyzing:**
128
- Use list_projects to see available projects and get the project name.
129
-
130
- Returns:
131
- - Risk level (LOW/MEDIUM/HIGH/CRITICAL) based on dependency count and relationship types
132
- - Direct dependents: nodes that directly reference the target
133
- - Transitive dependents: nodes affected through dependency chains
134
- - Affected files: list of files that would need review
135
- - Critical paths: high-risk dependency chains
91
+ description: `Risk assessment tool. Use before modifying shared code to understand what depends on a node and the blast radius of changes.
136
92
 
137
- Parameters:
138
- - nodeId: Node ID from search_codebase or traverse_from_node results
139
- - filePath: Alternative - analyze all exports from a file
140
- - maxDepth: How far to trace transitive dependencies (default: 4)
141
-
142
- Use this before refactoring to understand blast radius of changes.`,
93
+ Returns risk level (LOW/MEDIUM/HIGH/CRITICAL), direct and transitive dependents, affected files, and critical paths. Accepts nodeId or filePath.`,
143
94
  },
144
95
  [TOOL_NAMES.checkParseStatus]: {
145
96
  title: 'Check Parse Status',
146
- description: `Check the status of an async parsing job started with parse_typescript_project({ async: true }).
147
-
148
- Returns:
149
- - Job status (pending/running/completed/failed)
150
- - Progress: phase, files processed, chunks completed
151
- - Nodes and edges imported so far
152
- - Final result on completion or error message on failure
153
-
154
- Use this to poll for progress when parsing large codebases asynchronously.`,
97
+ description: `Setup tool. Poll the status of an async parsing job started with parse_typescript_project. Returns job status, progress, and final result.`,
155
98
  },
156
99
  [TOOL_NAMES.listProjects]: {
157
100
  title: 'List Projects',
158
- description: `List all parsed projects in the database with their IDs, names, and paths.
159
-
160
- Returns:
161
- - projectId: The full project ID (e.g., "proj_a1b2c3d4e5f6")
162
- - name: Friendly project name from package.json (e.g., "backend")
163
- - path: Full filesystem path to the project
164
- - updatedAt: When the project was last parsed
165
-
166
- Use the name or path in other tools instead of the cryptic projectId.`,
101
+ description: `Utility tool. Lists all parsed projects with IDs, names, and paths. Most tools accept project names or paths directly, so this is rarely needed.`,
167
102
  },
168
103
  [TOOL_NAMES.startWatchProject]: {
169
104
  title: 'Start Watch Project',
170
- description: `Watch project for .ts file changes and auto-update graph.
171
-
172
- Parameters: projectPath (required), tsconfigPath (required), debounceMs (default: 1000ms).
173
-
174
- Auto-excludes node_modules, dist, build, .git. Use list_watchers to see active, stop_watch_project to stop.`,
105
+ description: `File watcher tool. Watch a project for .ts file changes and auto-update the graph. Auto-excludes node_modules, dist, build, .git.`,
175
106
  },
176
107
  [TOOL_NAMES.stopWatchProject]: {
177
108
  title: 'Stop Watch Project',
178
- description: `Stop watching a project. Requires projectId.`,
109
+ description: `File watcher tool. Stop watching a project for file changes.`,
179
110
  },
180
111
  [TOOL_NAMES.listWatchers]: {
181
112
  title: 'List Watchers',
182
- description: `List active file watchers with status, pending changes, last update time.`,
113
+ description: `File watcher tool. List active file watchers with status and pending changes.`,
183
114
  },
184
115
  [TOOL_NAMES.detectDeadCode]: {
185
116
  title: 'Detect Dead Code',
186
- description: `Find unused exports, uncalled methods, orphan interfaces. Use list_projects first.
117
+ description: `Code quality tool. Find unused exports, uncalled methods, and orphan interfaces. Returns items with confidence scores grouped by type and category.
187
118
 
188
- Returns risk level, dead code items with confidence (HIGH/MEDIUM/LOW), grouped by type and category.
189
-
190
- Key parameters:
191
- - projectId (required)
192
- - filterCategory: library-export, ui-component, internal-unused, all (default: all)
193
- - minConfidence: LOW/MEDIUM/HIGH (default: LOW)
194
- - summaryOnly: true for stats only
195
- - excludePatterns, excludeSemanticTypes: Additional exclusions
196
-
197
- Auto-excludes NestJS entry points (controllers, modules, guards, etc.). Use filterCategory=internal-unused for actionable cleanup.`,
119
+ Auto-excludes NestJS entry points. Use filterCategory=internal-unused for actionable cleanup.`,
198
120
  },
199
121
  [TOOL_NAMES.detectDuplicateCode]: {
200
122
  title: 'Detect Duplicate Code',
201
- description: `Find duplicates using structural (AST hash) and semantic (embedding) analysis. Use list_projects first.
202
-
203
- Parameters:
204
- - projectId (required)
205
- - type: structural, semantic, or all (default: all)
206
- - minSimilarity: 0.5-1.0 (default: 0.80). 0.90+ = almost certain duplicates
207
- - scope: methods, functions, classes, or all (default: all)
208
- - summaryOnly: true for stats only
209
- - includeCode: Include source snippets (default: false)`,
123
+ description: `Code quality tool. Find duplicates using structural (AST hash) and semantic (embedding) analysis. Returns grouped results with similarity scores.`,
210
124
  },
211
125
  [TOOL_NAMES.swarmPheromone]: {
212
126
  title: 'Swarm Pheromone',
213
- description: `Mark a code node with a pheromone for coordination. Types: exploring (2min), modifying (10min), claiming (1hr), completed (24hr), warning (permanent), blocked (5min), proposal (1hr), needs_review (30min), session_context (8hr).
214
-
215
- Workflow states (exploring/claiming/modifying/completed/blocked) are mutually exclusive per agent+node. Flag types (warning/proposal/needs_review/session_context) can coexist. Use remove:true to delete. Pheromones decay automatically.`,
127
+ description: `Swarm coordination tool. Mark a code node with a pheromone to signal activity. Workflow states (exploring/claiming/modifying/completed/blocked) are mutually exclusive per agent+node. Flag types (warning/proposal/needs_review/session_context) can coexist. Pheromones decay automatically.`,
216
128
  },
217
129
  [TOOL_NAMES.swarmSense]: {
218
130
  title: 'Swarm Sense',
219
- description: `Query active pheromones to see what other agents are doing. Filter by swarmId, types, nodeIds, agentIds. Use excludeAgentId to see only others' activity.
220
-
221
- Returns pheromones with current intensity after decay. minIntensity default: 0.3. Add includeStats:true for summary counts.`,
131
+ description: `Swarm coordination tool. Query active pheromones to see what other agents are doing. Filter by swarmId, types, nodeIds, agentIds. Returns pheromones with current intensity after decay.`,
222
132
  },
223
133
  [TOOL_NAMES.swarmCleanup]: {
224
134
  title: 'Swarm Cleanup',
225
- description: `Bulk delete pheromones, tasks, and messages. Specify swarmId, agentId, or all:true. Warning pheromones preserved by default (override with keepTypes:[]). Messages and tasks are deleted when swarmId is provided. Use dryRun:true to preview.`,
135
+ description: `Swarm orchestration tool. Bulk delete pheromones, tasks, and messages for a swarm or agent. Warning pheromones preserved by default. Use dryRun=true to preview.`,
226
136
  },
227
137
  [TOOL_NAMES.swarmPostTask]: {
228
138
  title: 'Swarm Post Task',
229
- description: `Post a task to the swarm queue. Required: projectId, swarmId, title, description, type, createdBy.
230
-
231
- Types: implement, refactor, fix, test, review, document, investigate, plan. Priority: critical, high, normal, low, backlog.
232
-
233
- Use dependencies array for task ordering. Tasks with incomplete deps auto-block until ready.`,
139
+ description: `Swarm orchestration tool. Post a task to the swarm queue. Use dependencies array for task ordering — tasks with incomplete deps auto-block until ready.`,
234
140
  },
235
141
  [TOOL_NAMES.swarmClaimTask]: {
236
142
  title: 'Swarm Claim Task',
237
- description: `Claim a task from the swarm task queue.
238
-
239
- **Actions:** claim_and_start (default, recommended), claim, start, release, abandon, force_start
240
-
241
- **Flow:** claim_and_start → do work → swarm_complete_task
143
+ description: `Swarm orchestration tool. Claim a task from the queue. Without taskId, claims highest-priority available task. Set startImmediately=false to claim without starting.
242
144
 
243
- Without taskId, claims highest-priority available task. Use types/minPriority to filter.
244
-
245
- Recovery: Use abandon to release stuck tasks, force_start to recover from failed start.`,
145
+ Flow: swarm_claim_task do work → swarm_complete_task. Use swarm_release_task to give up work, swarm_advance_task for state transitions.`,
146
+ },
147
+ [TOOL_NAMES.swarmReleaseTask]: {
148
+ title: 'Swarm Release Task',
149
+ description: `Swarm orchestration tool. Release or abandon a claimed task. Use when an agent can no longer complete a task. Set trackAbandonment=true to record the abandonment for retry tracking.`,
150
+ },
151
+ [TOOL_NAMES.swarmAdvanceTask]: {
152
+ title: 'Swarm Advance Task',
153
+ description: `Swarm orchestration tool. Start or force-start a claimed task. Use after claiming with startImmediately=false, or set force=true to recover from a stuck claimed state.`,
246
154
  },
247
155
  [TOOL_NAMES.swarmCompleteTask]: {
248
156
  title: 'Swarm Complete Task',
249
- description: `Mark a task as completed, failed, or request review.
250
-
251
- **Actions:** complete, fail, request_review, approve, reject, retry
252
-
253
- Required: summary (for complete/request_review), reason (for fail), reviewerId (for approve/reject).
254
-
255
- Complete unblocks dependent tasks. Failed tasks can be retried if retryable=true.`,
157
+ description: `Swarm orchestration tool. Mark a task as completed, failed, or request review. Completing unblocks dependent tasks. Failed tasks can be retried if retryable=true.`,
256
158
  },
257
159
  [TOOL_NAMES.swarmGetTasks]: {
258
160
  title: 'Swarm Get Tasks',
259
- description: `Query tasks with filters. Use taskId for single task, or filter by swarmId, statuses, types, claimedBy, createdBy, minPriority.
260
-
261
- Sort by: priority (default), created, updated. Add includeStats:true for aggregate counts.`,
161
+ description: `Swarm orchestration tool. Query tasks with filters. Use taskId for a single task, or filter by swarmId, statuses, types, claimedBy. Add includeStats=true for aggregate counts.`,
262
162
  },
263
163
  [TOOL_NAMES.saveSessionBookmark]: {
264
164
  title: 'Save Session Bookmark',
265
- description: `Save current session context as a bookmark for cross-session continuity.
266
-
267
- Records your working set (code node IDs), task context, findings, and next steps so a future session can resume exactly where you left off.
268
-
269
- Parameters:
270
- - projectId (required): Project ID, name, or path
271
- - sessionId (required): Unique session/conversation ID for recovery
272
- - agentId (required): Your agent identifier
273
- - summary (required, min 10 chars): Brief description of current work state
274
- - workingSetNodeIds (required): Code node IDs you are focused on
275
- - taskContext (required): High-level task being worked on
276
- - findings: Key discoveries or decisions made so far
277
- - nextSteps: What to do next when resuming
278
- - metadata: Additional structured data
279
-
280
- Returns bookmarkId for use with restore_session_bookmark.`,
165
+ description: `Session persistence tool. Save current working set, task context, findings, and next steps as a bookmark for cross-session continuity. Use session_recall to resume.`,
281
166
  },
282
167
  [TOOL_NAMES.restoreSessionBookmark]: {
283
168
  title: 'Restore Session Bookmark',
284
- description: `Restore a previously saved session bookmark to resume work.
285
-
286
- Retrieves the bookmark, fetches working set code nodes (with source), and returns any session notes. Use after conversation compaction or when resuming a task in a new session.
287
-
288
- Parameters:
289
- - projectId (required): Project ID, name, or path
290
- - sessionId: Specific session to restore (latest bookmark if omitted)
291
- - agentId: Filter by agent ID (any agent if omitted)
292
- - includeCode (default: true): Include source code for working set nodes
293
- - snippetLength (default: 500): Max characters per code snippet
294
-
295
- Returns: bookmark data, working set nodes, session notes, and staleNodeIds (nodes no longer in graph after re-parse).`,
169
+ description: `Session persistence tool. Restore a previously saved bookmark to resume work. Returns bookmark data, working set nodes with source code, session notes, and stale node IDs.`,
296
170
  },
297
171
  [TOOL_NAMES.saveSessionNote]: {
298
172
  title: 'Save Session Note',
299
- description: `Save an observation, decision, insight, or risk as a durable session note linked to code nodes.
300
-
301
- Notes survive session compaction and are recalled by restore_session_bookmark or recall_session_notes.
302
-
303
- Parameters:
304
- - projectId (required): Project ID, name, or path
305
- - sessionId (required): Session/conversation identifier
306
- - agentId (required): Your agent identifier
307
- - topic (required, 3-100 chars): Short topic label
308
- - content (required, min 10 chars): Full observation text
309
- - category (required): architectural, bug, insight, decision, risk, or todo
310
- - severity (default: info): info, warning, or critical
311
- - aboutNodeIds: Code node IDs this note is about (creates [:ABOUT] links)
312
- - expiresInHours: Auto-expire after N hours (omit for permanent)
313
-
314
- Returns noteId, hasEmbedding (enables semantic recall), and expiresAt.`,
173
+ description: `Session persistence tool. Save an observation, decision, or risk as a durable note linked to code nodes. Notes survive session compaction and are searchable via session_recall.`,
315
174
  },
316
175
  [TOOL_NAMES.recallSessionNotes]: {
317
176
  title: 'Recall Session Notes',
318
- description: `Search and retrieve saved session notes. Supports semantic vector search (when query provided) or filter-based search.
319
-
320
- Parameters:
321
- - projectId (required): Project ID, name, or path
322
- - query: Natural language search — triggers semantic vector search when provided
323
- - category: Filter by architectural, bug, insight, decision, risk, todo
324
- - severity: Filter by info, warning, or critical
325
- - sessionId: Filter by session ID
326
- - agentId: Filter by agent ID
327
- - limit (default: 10, max: 50): Maximum notes to return
328
- - minSimilarity (default: 0.3): Minimum similarity for vector search
329
-
330
- Returns notes with topic, content, category, severity, relevance score (vector mode), and linked aboutNodes.`,
177
+ description: `Session persistence tool. Search and retrieve saved session notes. Provide query for semantic vector search, or filter by category/severity/sessionId/agentId.`,
331
178
  },
332
179
  [TOOL_NAMES.cleanupSession]: {
333
180
  title: 'Cleanup Session',
334
- description: `Clean up expired session notes and old session bookmarks.
335
-
336
- Removes:
337
- - Expired SessionNote nodes (past expiresAt) and their edges
338
- - Old SessionBookmark nodes, keeping only the most recent N per session (default: 3)
339
-
340
- Parameters:
341
- - projectId (required): Project ID, name, or path
342
- - keepBookmarks (default: 3): Number of most recent bookmarks to keep per session
343
- - dryRun (default: false): Preview what would be deleted without deleting
344
-
345
- Returns counts of deleted notes, bookmarks, and edges.`,
181
+ description: `Session persistence tool. Remove expired session notes and old bookmarks, keeping the most recent per session.`,
182
+ },
183
+ [TOOL_NAMES.sessionSave]: {
184
+ title: 'Session Save',
185
+ description: `Session persistence tool. Save session context — auto-detects bookmark vs note based on input. Provide workingSetNodeIds for a bookmark, topic+content for a note, or both for a bookmark with an attached note.`,
186
+ },
187
+ [TOOL_NAMES.sessionRecall]: {
188
+ title: 'Session Recall',
189
+ description: `Session persistence tool. Retrieve saved session context. Provide query for semantic note search, or sessionId to restore the latest bookmark and all notes for that session.`,
346
190
  },
347
191
  [TOOL_NAMES.swarmMessage]: {
348
192
  title: 'Swarm Message',
349
- description: `Direct agent-to-agent messaging via Neo4j. Unlike pheromones (passive/decay-based), messages are explicit and delivered when agents claim tasks.
350
-
351
- **Actions:**
352
- - send: Post a message to a specific agent or broadcast to all agents in a swarm
353
- - read: Retrieve unread messages (or all messages) for an agent
354
- - acknowledge: Mark messages as read
355
-
356
- **Categories:** blocked, conflict, finding, request, alert, handoff
357
-
358
- **Key behavior:**
359
- - Messages sent to a specific agent are delivered when that agent calls swarm_claim_task
360
- - Broadcast messages (toAgentId omitted) are visible to all agents in the swarm
361
- - Messages auto-expire after 4 hours (configurable via ttlMs)
362
- - Use this for critical coordination signals that agents MUST see, not optional context
193
+ description: `Swarm coordination tool. Direct agent-to-agent messaging. Unlike pheromones (passive/decay-based), messages are explicit and delivered when agents claim tasks. Use for critical coordination signals.
363
194
 
364
- **Examples:**
365
- - Agent finds a breaking type error: send alert to all
366
- - Agent is blocked on a file another agent owns: send blocked to that agent
367
- - Agent discovers context another agent needs: send finding to that agent`,
195
+ Actions: send (post or broadcast), read (retrieve), acknowledge (mark read). Categories: blocked, conflict, finding, request, alert, handoff.`,
368
196
  },
369
197
  };
370
198
  // Default Values
@@ -0,0 +1,61 @@
1
+ import { GET_TASK_STATE_QUERY } from './queries.js';
2
+ /**
3
+ * Query to abandon a task — releases it with tracking for debugging.
4
+ * More explicit than release: tracks abandon history (count, previous claimant).
5
+ */
6
+ const ABANDON_TASK_QUERY = `
7
+ MATCH (t:SwarmTask {id: $taskId, projectId: $projectId})
8
+ WHERE t.claimedBy = $agentId
9
+ AND t.status IN ['claimed', 'in_progress']
10
+
11
+ // Track abandon history
12
+ SET t.status = 'available',
13
+ t.previousClaimedBy = t.claimedBy,
14
+ t.claimedBy = null,
15
+ t.claimedAt = null,
16
+ t.startedAt = null,
17
+ t.updatedAt = timestamp(),
18
+ t.abandonedBy = $agentId,
19
+ t.abandonedAt = timestamp(),
20
+ t.abandonReason = $reason,
21
+ t.abandonCount = COALESCE(t.abandonCount, 0) + 1
22
+
23
+ RETURN t.id as id,
24
+ t.title as title,
25
+ t.status as status,
26
+ t.abandonCount as abandonCount,
27
+ t.abandonReason as abandonReason
28
+ `;
29
+ export class SwarmAbandonHandler {
30
+ neo4jService;
31
+ constructor(neo4jService) {
32
+ this.neo4jService = neo4jService;
33
+ }
34
+ async abandon(projectId, taskId, agentId, reason) {
35
+ const result = await this.neo4jService.run(ABANDON_TASK_QUERY, {
36
+ taskId,
37
+ projectId,
38
+ agentId,
39
+ reason: reason || 'No reason provided',
40
+ });
41
+ if (result.length === 0) {
42
+ const stateResult = await this.neo4jService.run(GET_TASK_STATE_QUERY, {
43
+ taskId,
44
+ projectId,
45
+ });
46
+ return { error: true, data: stateResult[0] };
47
+ }
48
+ const row = result[0];
49
+ const abandonCount = typeof row.abandonCount === 'object' ? row.abandonCount.toNumber() : row.abandonCount;
50
+ return {
51
+ error: false,
52
+ data: {
53
+ id: row.id,
54
+ title: row.title,
55
+ status: row.status,
56
+ abandonCount,
57
+ abandonReason: row.abandonReason,
58
+ },
59
+ };
60
+ }
61
+ }
@@ -0,0 +1,78 @@
1
+ import { GET_TASK_STATE_QUERY } from './queries.js';
2
+ /**
3
+ * Query to start working on a claimed task (transition to in_progress)
4
+ */
5
+ const START_TASK_QUERY = `
6
+ MATCH (t:SwarmTask {id: $taskId, projectId: $projectId})
7
+ WHERE t.status = 'claimed' AND t.claimedBy = $agentId
8
+
9
+ SET t.status = 'in_progress',
10
+ t.startedAt = timestamp(),
11
+ t.updatedAt = timestamp()
12
+
13
+ RETURN t.id as id,
14
+ t.status as status,
15
+ t.claimedBy as claimedBy,
16
+ t.startedAt as startedAt
17
+ `;
18
+ /**
19
+ * Query to force-start a task stuck in claimed state.
20
+ * Allows recovery when the normal start action fails.
21
+ */
22
+ const FORCE_START_QUERY = `
23
+ MATCH (t:SwarmTask {id: $taskId, projectId: $projectId})
24
+ WHERE t.claimedBy = $agentId
25
+ AND t.status IN ['claimed', 'available']
26
+
27
+ SET t.status = 'in_progress',
28
+ t.claimedBy = $agentId,
29
+ t.claimedAt = COALESCE(t.claimedAt, timestamp()),
30
+ t.startedAt = timestamp(),
31
+ t.updatedAt = timestamp(),
32
+ t.forceStarted = true,
33
+ t.forceStartReason = $reason
34
+
35
+ RETURN t.id as id,
36
+ t.title as title,
37
+ t.status as status,
38
+ t.claimedBy as claimedBy,
39
+ t.startedAt as startedAt,
40
+ t.forceStarted as forceStarted
41
+ `;
42
+ export class SwarmAdvanceHandler {
43
+ neo4jService;
44
+ constructor(neo4jService) {
45
+ this.neo4jService = neo4jService;
46
+ }
47
+ async start(projectId, taskId, agentId) {
48
+ const result = await this.neo4jService.run(START_TASK_QUERY, {
49
+ taskId,
50
+ projectId,
51
+ agentId,
52
+ });
53
+ if (result.length === 0) {
54
+ const stateResult = await this.neo4jService.run(GET_TASK_STATE_QUERY, {
55
+ taskId,
56
+ projectId,
57
+ });
58
+ return { error: true, data: stateResult[0] };
59
+ }
60
+ return { error: false, data: result[0] };
61
+ }
62
+ async forceStart(projectId, taskId, agentId, reason) {
63
+ const result = await this.neo4jService.run(FORCE_START_QUERY, {
64
+ taskId,
65
+ projectId,
66
+ agentId,
67
+ reason: reason || 'Recovering from stuck state',
68
+ });
69
+ if (result.length === 0) {
70
+ const stateResult = await this.neo4jService.run(GET_TASK_STATE_QUERY, {
71
+ taskId,
72
+ projectId,
73
+ });
74
+ return { error: true, data: stateResult[0] };
75
+ }
76
+ return { error: false, data: result[0] };
77
+ }
78
+ }
@@ -0,0 +1,61 @@
1
+ import { TASK_PRIORITIES } from '../../tools/swarm-constants.js';
2
+ import { CLAIM_TASK_BY_ID_QUERY, CLAIM_NEXT_TASK_QUERY, GET_TASK_STATE_QUERY } from './queries.js';
3
+ /** Maximum retries when racing for a task */
4
+ const MAX_CLAIM_RETRIES = 3;
5
+ /** Delay between retries (ms) */
6
+ const RETRY_DELAY_BASE_MS = 50;
7
+ export class SwarmClaimHandler {
8
+ neo4jService;
9
+ constructor(neo4jService) {
10
+ this.neo4jService = neo4jService;
11
+ }
12
+ /**
13
+ * Claim a specific task by ID.
14
+ */
15
+ async claimById(projectId, taskId, agentId, targetStatus) {
16
+ const result = await this.neo4jService.run(CLAIM_TASK_BY_ID_QUERY, {
17
+ taskId,
18
+ projectId,
19
+ agentId,
20
+ targetStatus,
21
+ });
22
+ if (result.length === 0) {
23
+ const stateResult = await this.neo4jService.run(GET_TASK_STATE_QUERY, {
24
+ taskId,
25
+ projectId,
26
+ });
27
+ return { error: true, data: stateResult[0], retryAttempts: 0 };
28
+ }
29
+ return { error: false, data: result[0], retryAttempts: 0 };
30
+ }
31
+ /**
32
+ * Auto-select and claim the highest priority available task.
33
+ * Includes retry logic with exponential backoff for race conditions.
34
+ */
35
+ async claimNext(projectId, swarmId, agentId, targetStatus, filters) {
36
+ const minPriorityScore = filters?.minPriority ? TASK_PRIORITIES[filters.minPriority] : null;
37
+ let result = [];
38
+ let retryCount = 0;
39
+ while (retryCount < MAX_CLAIM_RETRIES) {
40
+ result = await this.neo4jService.run(CLAIM_NEXT_TASK_QUERY, {
41
+ projectId,
42
+ swarmId,
43
+ agentId,
44
+ types: filters?.types || null,
45
+ minPriority: minPriorityScore,
46
+ targetStatus,
47
+ });
48
+ if (result.length > 0) {
49
+ break;
50
+ }
51
+ retryCount++;
52
+ if (retryCount < MAX_CLAIM_RETRIES) {
53
+ await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_BASE_MS * Math.pow(2, retryCount - 1)));
54
+ }
55
+ }
56
+ if (result.length === 0) {
57
+ return { error: false, data: null, retryAttempts: retryCount };
58
+ }
59
+ return { error: false, data: result[0], retryAttempts: retryCount };
60
+ }
61
+ }
@@ -0,0 +1,5 @@
1
+ export { SwarmClaimHandler } from './claim.handler.js';
2
+ export { SwarmReleaseHandler } from './release.handler.js';
3
+ export { SwarmAbandonHandler } from './abandon.handler.js';
4
+ export { SwarmAdvanceHandler } from './advance.handler.js';
5
+ export { GET_TASK_STATE_QUERY, CLAIM_TASK_BY_ID_QUERY, CLAIM_NEXT_TASK_QUERY } from './queries.js';