devsmind-mcp 2.0.2 → 2.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.
- package/LICENSE +21 -21
- package/README.md +176 -73
- package/dist/cli/index.js +46 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +2 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/rule.js +31 -8
- package/dist/cli/rule.js.map +1 -1
- package/dist/cli/runner.d.ts +24 -0
- package/dist/cli/runner.js +502 -376
- package/dist/cli/runner.js.map +1 -1
- package/dist/db/database.d.ts +66 -0
- package/dist/db/database.js +329 -28
- package/dist/db/database.js.map +1 -1
- package/dist/db/edges.d.ts +42 -0
- package/dist/db/edges.js +162 -0
- package/dist/db/edges.js.map +1 -0
- package/dist/db/indexer.d.ts +3 -3
- package/dist/db/indexer.js +8 -8
- package/dist/db/indexer.js.map +1 -1
- package/dist/db/schema.js +39 -39
- package/dist/db/staging.d.ts +39 -0
- package/dist/db/staging.js +137 -0
- package/dist/db/staging.js.map +1 -0
- package/dist/mcp/server.js +168 -200
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/visualizer_2d.html +635 -635
- package/dist/mcp/visualizer_3d.html +613 -628
- package/dist/utils/ast.d.ts +38 -0
- package/dist/utils/ast.js +1079 -0
- package/dist/utils/ast.js.map +1 -0
- package/package.json +4 -4
package/dist/mcp/server.js
CHANGED
|
@@ -52,8 +52,39 @@ const database_1 = require("../db/database");
|
|
|
52
52
|
const visualizer_1 = require("./visualizer");
|
|
53
53
|
const indexer_1 = require("../db/indexer");
|
|
54
54
|
const scanner_1 = require("../utils/scanner");
|
|
55
|
+
const staging_1 = require("../db/staging");
|
|
55
56
|
// ─── Port: devs→D(4)E(5)=45 + mind→M(13)=13 → 4513 ─────────────────────────
|
|
56
57
|
exports.DEVSMIND_PORT = 4513;
|
|
58
|
+
// Shared node-type taxonomy description, reused by update_history and stage_change.
|
|
59
|
+
const NODE_TYPE_DESCRIPTION = 'The type of node. Be highly specific and framework-aware. Choose from the taxonomy below (or use a custom value if nothing fits).\n\n' +
|
|
60
|
+
'UNIVERSAL: function | method | class | abstract_class | interface | type_alias | enum | constant | variable | module | namespace | decorator\n\n' +
|
|
61
|
+
'NESTJS: nest_module | nest_controller | nest_service | nest_provider | nest_guard | nest_interceptor | nest_pipe | nest_filter | nest_decorator | nest_middleware | nest_gateway | nest_resolver | nest_schema | nest_dto\n\n' +
|
|
62
|
+
'EXPRESS/FASTIFY/KOA/HONO: route_handler | middleware | router\n\n' +
|
|
63
|
+
'SPRING (Java): spring_controller | spring_service | spring_repository | spring_component | spring_bean | spring_config | spring_entity\n\n' +
|
|
64
|
+
'DJANGO/FASTAPI (Python): django_view | django_model | django_serializer | django_form | django_signal | fastapi_router | fastapi_dependency\n\n' +
|
|
65
|
+
'GO: go_handler | go_middleware | go_struct | go_interface | go_func\n\n' +
|
|
66
|
+
'RUST: rust_struct | rust_impl | rust_trait | rust_enum | rust_fn | rust_macro\n\n' +
|
|
67
|
+
'REACT: react_component | react_hook | react_context | react_hoc | react_page\n\n' +
|
|
68
|
+
'NEXT.JS: next_page | next_layout | next_api_route | next_server_action | next_middleware\n\n' +
|
|
69
|
+
'VUE: vue_component | vue_composable | vue_directive | vue_store_module\n\n' +
|
|
70
|
+
'ANGULAR: ng_component | ng_service | ng_directive | ng_pipe | ng_module | ng_guard | ng_interceptor | ng_resolver\n\n' +
|
|
71
|
+
'SVELTE: svelte_component | svelte_store | svelte_action\n\n' +
|
|
72
|
+
'ORM — PRISMA: prisma_model | prisma_query | prisma_migration\n' +
|
|
73
|
+
'ORM — TYPEORM: typeorm_entity | typeorm_repository | typeorm_migration\n' +
|
|
74
|
+
'ORM — MONGOOSE: mongoose_model | mongoose_schema\n' +
|
|
75
|
+
'ORM — SQLALCHEMY: sqlalchemy_model | sqlalchemy_query\n' +
|
|
76
|
+
'ORM — SEQUELIZE: sequelize_model | sequelize_migration\n\n' +
|
|
77
|
+
'REST/API: api_endpoint | rest_controller\n' +
|
|
78
|
+
'GRAPHQL: graphql_resolver | graphql_query | graphql_mutation | graphql_subscription | graphql_schema | graphql_directive\n' +
|
|
79
|
+
'GRPC/PROTO: grpc_service | grpc_method | proto_message\n' +
|
|
80
|
+
'WEBSOCKET: ws_gateway | ws_handler\n' +
|
|
81
|
+
'MESSAGE QUEUE: mq_producer | mq_consumer | mq_handler\n\n' +
|
|
82
|
+
'CONFIG/AUTH: config_loader | env_config | feature_flag | auth_guard | auth_strategy | jwt_util | permission_policy\n' +
|
|
83
|
+
'OBSERVABILITY: logger | metric | trace_span\n' +
|
|
84
|
+
'CLI: cli_command | cli_option\n' +
|
|
85
|
+
'SCRIPTS: build_script | migration_script | seed_script\n' +
|
|
86
|
+
'TESTS: test_suite | test_case | test_helper | mock | fixture\n' +
|
|
87
|
+
'UTILITY: util_function | helper | transformer | validator | formatter';
|
|
57
88
|
// Cache database connections by their resolved path to avoid re-opening constantly
|
|
58
89
|
const dbCache = new Map();
|
|
59
90
|
// Walk up from a start directory to find a .devmind folder containing config.json
|
|
@@ -162,7 +193,7 @@ function createMcpServer() {
|
|
|
162
193
|
},
|
|
163
194
|
{
|
|
164
195
|
name: 'get_node_code',
|
|
165
|
-
description: 'Returns only the latest code snapshot stored for a node. Token-efficient alternative to get_node_history when you only need the current code. Returns null if no snapshot exists — in that case you MUST read the file, then
|
|
196
|
+
description: 'Returns only the latest code snapshot stored for a node. Token-efficient alternative to get_node_history when you only need the current code. Returns null if no snapshot exists — in that case you MUST read the file, then stage_change + commit_changes to store the code so future agents benefit from the cache.',
|
|
166
197
|
inputSchema: {
|
|
167
198
|
type: 'object',
|
|
168
199
|
properties: {
|
|
@@ -178,103 +209,10 @@ function createMcpServer() {
|
|
|
178
209
|
required: ['devmind_path', 'node_id']
|
|
179
210
|
}
|
|
180
211
|
},
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
type: 'object',
|
|
186
|
-
properties: {
|
|
187
|
-
devmind_path: {
|
|
188
|
-
type: 'string',
|
|
189
|
-
description: 'Absolute path to the .devmind directory'
|
|
190
|
-
},
|
|
191
|
-
node_id: {
|
|
192
|
-
type: 'string',
|
|
193
|
-
description: 'Unique identifier for the node (e.g. function/method identifier)'
|
|
194
|
-
},
|
|
195
|
-
file_path: {
|
|
196
|
-
type: 'string',
|
|
197
|
-
description: 'Source file path where the node is located'
|
|
198
|
-
},
|
|
199
|
-
code_snapshot: {
|
|
200
|
-
type: 'string',
|
|
201
|
-
description: 'Full source code content of the node at this moment'
|
|
202
|
-
},
|
|
203
|
-
reasoning: {
|
|
204
|
-
type: 'object',
|
|
205
|
-
description: 'Structured details about this change',
|
|
206
|
-
properties: {
|
|
207
|
-
what_changed: {
|
|
208
|
-
type: 'string',
|
|
209
|
-
description: 'Brief description of the modified code'
|
|
210
|
-
},
|
|
211
|
-
why: { type: 'string', description: 'The reason this change was made' },
|
|
212
|
-
goal: { type: 'string', description: 'What was being achieved' },
|
|
213
|
-
requirement: {
|
|
214
|
-
type: 'string',
|
|
215
|
-
description: 'Ticket / issue / user request ID if applicable'
|
|
216
|
-
},
|
|
217
|
-
previous_state: {
|
|
218
|
-
type: 'string',
|
|
219
|
-
description: 'What the code looked like before and why it was a problem'
|
|
220
|
-
},
|
|
221
|
-
decision: {
|
|
222
|
-
type: 'string',
|
|
223
|
-
description: 'Architectural or implementation decision and why'
|
|
224
|
-
},
|
|
225
|
-
developer: { type: 'string', description: 'Name of the developer' },
|
|
226
|
-
model: { type: 'string', description: 'AI model name used' }
|
|
227
|
-
},
|
|
228
|
-
required: ['what_changed', 'why', 'goal']
|
|
229
|
-
},
|
|
230
|
-
name: {
|
|
231
|
-
type: 'string',
|
|
232
|
-
description: 'Display name of the node (optional, will be inferred if omitted)'
|
|
233
|
-
},
|
|
234
|
-
type: {
|
|
235
|
-
type: 'string',
|
|
236
|
-
description: '(optional, defaults to function) The type of node. Be highly specific and framework-aware. Choose from the taxonomy below (or use a custom value if nothing fits).\n\n' +
|
|
237
|
-
'UNIVERSAL: function | method | class | abstract_class | interface | type_alias | enum | constant | variable | module | namespace | decorator\n\n' +
|
|
238
|
-
'NESTJS: nest_module | nest_controller | nest_service | nest_provider | nest_guard | nest_interceptor | nest_pipe | nest_filter | nest_decorator | nest_middleware | nest_gateway | nest_resolver | nest_schema | nest_dto\n\n' +
|
|
239
|
-
'EXPRESS/FASTIFY/KOA/HONO: route_handler | middleware | router\n\n' +
|
|
240
|
-
'SPRING (Java): spring_controller | spring_service | spring_repository | spring_component | spring_bean | spring_config | spring_entity\n\n' +
|
|
241
|
-
'DJANGO/FASTAPI (Python): django_view | django_model | django_serializer | django_form | django_signal | fastapi_router | fastapi_dependency\n\n' +
|
|
242
|
-
'GO: go_handler | go_middleware | go_struct | go_interface | go_func\n\n' +
|
|
243
|
-
'RUST: rust_struct | rust_impl | rust_trait | rust_enum | rust_fn | rust_macro\n\n' +
|
|
244
|
-
'REACT: react_component | react_hook | react_context | react_hoc | react_page\n\n' +
|
|
245
|
-
'NEXT.JS: next_page | next_layout | next_api_route | next_server_action | next_middleware\n\n' +
|
|
246
|
-
'VUE: vue_component | vue_composable | vue_directive | vue_store_module\n\n' +
|
|
247
|
-
'ANGULAR: ng_component | ng_service | ng_directive | ng_pipe | ng_module | ng_guard | ng_interceptor | ng_resolver\n\n' +
|
|
248
|
-
'SVELTE: svelte_component | svelte_store | svelte_action\n\n' +
|
|
249
|
-
'ORM — PRISMA: prisma_model | prisma_query | prisma_migration\n' +
|
|
250
|
-
'ORM — TYPEORM: typeorm_entity | typeorm_repository | typeorm_migration\n' +
|
|
251
|
-
'ORM — MONGOOSE: mongoose_model | mongoose_schema\n' +
|
|
252
|
-
'ORM — SQLALCHEMY: sqlalchemy_model | sqlalchemy_query\n' +
|
|
253
|
-
'ORM — SEQUELIZE: sequelize_model | sequelize_migration\n\n' +
|
|
254
|
-
'REST/API: api_endpoint | rest_controller\n' +
|
|
255
|
-
'GRAPHQL: graphql_resolver | graphql_query | graphql_mutation | graphql_subscription | graphql_schema | graphql_directive\n' +
|
|
256
|
-
'GRPC/PROTO: grpc_service | grpc_method | proto_message\n' +
|
|
257
|
-
'WEBSOCKET: ws_gateway | ws_handler\n' +
|
|
258
|
-
'MESSAGE QUEUE: mq_producer | mq_consumer | mq_handler\n\n' +
|
|
259
|
-
'CONFIG/AUTH: config_loader | env_config | feature_flag | auth_guard | auth_strategy | jwt_util | permission_policy\n' +
|
|
260
|
-
'OBSERVABILITY: logger | metric | trace_span\n' +
|
|
261
|
-
'CLI: cli_command | cli_option\n' +
|
|
262
|
-
'SCRIPTS: build_script | migration_script | seed_script\n' +
|
|
263
|
-
'TESTS: test_suite | test_case | test_helper | mock | fixture\n' +
|
|
264
|
-
'UTILITY: util_function | helper | transformer | validator | formatter'
|
|
265
|
-
},
|
|
266
|
-
signature: {
|
|
267
|
-
type: 'string',
|
|
268
|
-
description: 'Function parameter types and return type signature (optional)'
|
|
269
|
-
},
|
|
270
|
-
session_id: {
|
|
271
|
-
type: 'string',
|
|
272
|
-
description: 'Session identifier to associate with this history update (optional)'
|
|
273
|
-
}
|
|
274
|
-
},
|
|
275
|
-
required: ['devmind_path', 'node_id', 'file_path', 'code_snapshot', 'reasoning']
|
|
276
|
-
}
|
|
277
|
-
},
|
|
212
|
+
// NOTE: `update_history`, `add_node`, and `add_connection` are intentionally NOT listed
|
|
213
|
+
// here. They are deprecated in favour of `stage_change` + `commit_changes` (to avoid
|
|
214
|
+
// confusing the AI with overlapping write tools), but their handlers are retained below
|
|
215
|
+
// so any direct/legacy call still works.
|
|
278
216
|
// ────────────────── Indexing tools ─────────────────────────────────────────
|
|
279
217
|
{
|
|
280
218
|
name: 'index_start',
|
|
@@ -331,63 +269,47 @@ function createMcpServer() {
|
|
|
331
269
|
}
|
|
332
270
|
},
|
|
333
271
|
{
|
|
334
|
-
name: '
|
|
335
|
-
description: '
|
|
272
|
+
name: 'stage_change',
|
|
273
|
+
description: 'Stage ONE changed code node (function/class/method/etc.) into a buffer without writing to the graph yet. Call this once for EVERY file/entity you touched during a task — passing only the code and reasoning; you do NOT reason about connections here. When you are done with all the files, call commit_changes ONCE — it creates every node, writes every history entry, and resolves all connections between them via local AST in a single pass (so a call from one changed file into another resolves correctly no matter which order you staged them). Staging is buffered on disk, so it survives a context reset. ⚠️ YOU MUST CALL commit_changes at the end, or nothing is written to the graph.',
|
|
336
274
|
inputSchema: {
|
|
337
275
|
type: 'object',
|
|
338
276
|
properties: {
|
|
339
277
|
devmind_path: { type: 'string', description: 'Absolute path to the .devmind directory' },
|
|
340
|
-
node_id: { type: 'string', description: 'Unique identifier e.g. "CartService.applyPromoCode" or "calculateDiscount"' },
|
|
341
|
-
|
|
342
|
-
type:
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
'
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
'
|
|
351
|
-
'
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
'ORM — PRISMA: prisma_model | prisma_query | prisma_migration\n' +
|
|
358
|
-
'ORM — TYPEORM: typeorm_entity | typeorm_repository | typeorm_migration\n' +
|
|
359
|
-
'ORM — MONGOOSE: mongoose_model | mongoose_schema\n' +
|
|
360
|
-
'ORM — SQLALCHEMY: sqlalchemy_model | sqlalchemy_query\n' +
|
|
361
|
-
'ORM — SEQUELIZE: sequelize_model | sequelize_migration\n\n' +
|
|
362
|
-
'REST/API: api_endpoint | rest_controller\n' +
|
|
363
|
-
'GRAPHQL: graphql_resolver | graphql_query | graphql_mutation | graphql_subscription | graphql_schema | graphql_directive\n' +
|
|
364
|
-
'GRPC/PROTO: grpc_service | grpc_method | proto_message\n' +
|
|
365
|
-
'WEBSOCKET: ws_gateway | ws_handler\n' +
|
|
366
|
-
'MESSAGE QUEUE: mq_producer | mq_consumer | mq_handler\n\n' +
|
|
367
|
-
'CONFIG/AUTH: config_loader | env_config | feature_flag | auth_guard | auth_strategy | jwt_util | permission_policy\n' +
|
|
368
|
-
'OBSERVABILITY: logger | metric | trace_span\n' +
|
|
369
|
-
'CLI: cli_command | cli_option\n' +
|
|
370
|
-
'SCRIPTS: build_script | migration_script | seed_script\n' +
|
|
371
|
-
'TESTS: test_suite | test_case | test_helper | mock | fixture\n' +
|
|
372
|
-
'UTILITY: util_function | helper | transformer | validator | formatter'
|
|
278
|
+
node_id: { type: 'string', description: 'Unique identifier for the node (e.g. "CartService.applyPromoCode" or "calculateDiscount")' },
|
|
279
|
+
file_path: { type: 'string', description: 'Source file path where the node is located' },
|
|
280
|
+
code_snapshot: { type: 'string', description: 'Full source code content of the node at this moment' },
|
|
281
|
+
reasoning: {
|
|
282
|
+
type: 'object',
|
|
283
|
+
description: 'Structured details about this change',
|
|
284
|
+
properties: {
|
|
285
|
+
what_changed: { type: 'string', description: 'Brief description of the modified code' },
|
|
286
|
+
why: { type: 'string', description: 'The reason this change was made' },
|
|
287
|
+
goal: { type: 'string', description: 'What was being achieved' },
|
|
288
|
+
requirement: { type: 'string', description: 'Ticket / issue / user request ID if applicable' },
|
|
289
|
+
previous_state: { type: 'string', description: 'What the code looked like before and why it was a problem' },
|
|
290
|
+
decision: { type: 'string', description: 'Architectural or implementation decision and why' },
|
|
291
|
+
developer: { type: 'string', description: 'Name of the developer' },
|
|
292
|
+
model: { type: 'string', description: 'AI model name used' }
|
|
293
|
+
},
|
|
294
|
+
required: ['what_changed', 'why', 'goal']
|
|
373
295
|
},
|
|
374
|
-
|
|
375
|
-
|
|
296
|
+
name: { type: 'string', description: 'Display name of the node (optional, inferred if omitted)' },
|
|
297
|
+
type: { type: 'string', description: '(optional, defaults to function) ' + NODE_TYPE_DESCRIPTION },
|
|
298
|
+
signature: { type: 'string', description: 'Parameter types + return type signature (optional)' },
|
|
299
|
+
session_id: { type: 'string', description: 'Session identifier to associate with this change (optional)' }
|
|
376
300
|
},
|
|
377
|
-
required: ['devmind_path', 'node_id', '
|
|
301
|
+
required: ['devmind_path', 'node_id', 'file_path', 'code_snapshot', 'reasoning']
|
|
378
302
|
}
|
|
379
303
|
},
|
|
380
304
|
{
|
|
381
|
-
name: '
|
|
382
|
-
description: '
|
|
305
|
+
name: 'commit_changes',
|
|
306
|
+
description: 'Commit all buffered stage_change entries in one atomic pass: creates/updates every staged node, writes every history snapshot, then resolves all connections between the staged nodes (and into the existing graph) via local AST — auto-creating any referenced-but-missing target nodes. Clears the buffer on success. Call this exactly once after you have finished staging every file you touched.',
|
|
383
307
|
inputSchema: {
|
|
384
308
|
type: 'object',
|
|
385
309
|
properties: {
|
|
386
|
-
devmind_path: { type: 'string', description: 'Absolute path to the .devmind directory' }
|
|
387
|
-
source_node_id: { type: 'string', description: 'The node that calls or uses the target' },
|
|
388
|
-
target_node_id: { type: 'string', description: 'The node being called or used' }
|
|
310
|
+
devmind_path: { type: 'string', description: 'Absolute path to the .devmind directory' }
|
|
389
311
|
},
|
|
390
|
-
required: ['devmind_path'
|
|
312
|
+
required: ['devmind_path']
|
|
391
313
|
}
|
|
392
314
|
},
|
|
393
315
|
{
|
|
@@ -515,6 +437,20 @@ function createMcpServer() {
|
|
|
515
437
|
required: ['devmind_path', 'query']
|
|
516
438
|
}
|
|
517
439
|
},
|
|
440
|
+
{
|
|
441
|
+
name: 'search_code',
|
|
442
|
+
description: 'Regex or string search over cached codebase code snapshots. Returns matches grouped by DevsMind Node ID, file path, and matching lines, along with matching statistics (ratio, count). Prefer this over direct grep search.',
|
|
443
|
+
inputSchema: {
|
|
444
|
+
type: 'object',
|
|
445
|
+
properties: {
|
|
446
|
+
devmind_path: { type: 'string', description: 'Absolute path to the .devmind directory' },
|
|
447
|
+
query: { type: 'string', description: 'Regex or substring pattern to search for in code' },
|
|
448
|
+
is_regex: { type: 'boolean', description: 'Whether the query is a regex pattern (default: false)' },
|
|
449
|
+
case_insensitive: { type: 'boolean', description: 'Perform case-insensitive search (default: true)' }
|
|
450
|
+
},
|
|
451
|
+
required: ['devmind_path', 'query']
|
|
452
|
+
}
|
|
453
|
+
},
|
|
518
454
|
{
|
|
519
455
|
name: 'get_orphaned_nodes',
|
|
520
456
|
description: 'Find disconnected code nodes in the graph that have no incoming or outgoing connections.',
|
|
@@ -600,7 +536,7 @@ function createMcpServer() {
|
|
|
600
536
|
content: [{ type: 'text', text: JSON.stringify({
|
|
601
537
|
exists: false,
|
|
602
538
|
node_id: nodeId,
|
|
603
|
-
message: 'No code snapshot found. Read the source file, then
|
|
539
|
+
message: 'No code snapshot found. Read the source file, then stage_change + commit_changes to cache the code so future agents skip the file read entirely.'
|
|
604
540
|
}) }]
|
|
605
541
|
};
|
|
606
542
|
}
|
|
@@ -615,54 +551,32 @@ function createMcpServer() {
|
|
|
615
551
|
}
|
|
616
552
|
case 'update_history': {
|
|
617
553
|
const devmindPath = resolveDevmindPath(args.devmind_path);
|
|
618
|
-
const rawNodeId = String(args.node_id);
|
|
619
554
|
const filePath = String(args.file_path);
|
|
620
|
-
const codeSnapshot = String(args.code_snapshot);
|
|
621
|
-
const reasoning = args.reasoning;
|
|
622
555
|
const db = getDatabase(devmindPath);
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
let nodeType = args.type ? String(args.type) : undefined;
|
|
628
|
-
const signature = args.signature ? String(args.signature) : undefined;
|
|
629
|
-
const sessionId = args.session_id ? String(args.session_id) : undefined;
|
|
630
|
-
// Infer name and type if not provided
|
|
631
|
-
if (!nodeName) {
|
|
632
|
-
nodeName = rawNodeId.includes('.') ? rawNodeId.split('.').pop() : rawNodeId;
|
|
633
|
-
}
|
|
634
|
-
if (!nodeType) {
|
|
635
|
-
nodeType = rawNodeId.includes('.') ? 'method' : 'function';
|
|
636
|
-
}
|
|
637
|
-
// 1. Ensure node exists (Upsert)
|
|
638
|
-
db.upsertNode({
|
|
639
|
-
id: nodeId,
|
|
640
|
-
name: nodeName,
|
|
641
|
-
type: nodeType,
|
|
556
|
+
// Single-shot path: stage one entry and commit it immediately, so a lone edit still
|
|
557
|
+
// gets its node, history, AND outgoing edges resolved via the shared commit logic.
|
|
558
|
+
const entry = {
|
|
559
|
+
node_id: String(args.node_id),
|
|
642
560
|
file_path: filePath,
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
}
|
|
561
|
+
code_snapshot: String(args.code_snapshot),
|
|
562
|
+
reasoning: args.reasoning,
|
|
563
|
+
name: args.name ? String(args.name) : undefined,
|
|
564
|
+
type: args.type ? String(args.type) : undefined,
|
|
565
|
+
signature: args.signature ? String(args.signature) : undefined,
|
|
566
|
+
session_id: args.session_id ? String(args.session_id) : undefined
|
|
567
|
+
};
|
|
568
|
+
const summary = (0, staging_1.commitStagedChanges)(db, devmindPath, [entry]);
|
|
569
|
+
const nodeId = entry.node_id.includes('#') ? entry.node_id : `${db.toRepoRelativePath(filePath)}#${entry.node_id}`;
|
|
652
570
|
return {
|
|
653
571
|
content: [
|
|
654
572
|
{
|
|
655
573
|
type: 'text',
|
|
656
574
|
text: JSON.stringify({
|
|
657
575
|
success: true,
|
|
658
|
-
message: 'History updated
|
|
659
|
-
node: { id: nodeId
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
session_id: historyEntry.session_id,
|
|
663
|
-
created_at: historyEntry.created_at,
|
|
664
|
-
updated_at: historyEntry.updated_at
|
|
665
|
-
}
|
|
576
|
+
message: 'History updated and connections resolved.',
|
|
577
|
+
node: { id: nodeId },
|
|
578
|
+
edges_added: summary.edges_added,
|
|
579
|
+
missing_nodes_filled: summary.missing_filled
|
|
666
580
|
}, null, 2)
|
|
667
581
|
}
|
|
668
582
|
]
|
|
@@ -683,37 +597,30 @@ function createMcpServer() {
|
|
|
683
597
|
content: [{
|
|
684
598
|
type: 'text',
|
|
685
599
|
text: JSON.stringify({
|
|
686
|
-
message: 'Indexing session started.
|
|
600
|
+
message: 'Indexing session started. Extract nodes with stage_change (one call per entity), then call commit_changes to write them all and resolve connections automatically via AST. Call index_checkpoint every 10 files.',
|
|
687
601
|
scratchpad: pad,
|
|
688
602
|
repos: repoSummaries,
|
|
689
603
|
total_files,
|
|
690
604
|
instructions: [
|
|
691
605
|
'⚠️⚠️⚠️ CRITICAL INSTRUCTION FOR THE INDEXING AGENT — MUST READ ⚠️⚠️⚠️ ',
|
|
692
|
-
'YOU MUST EXPLICITLY CALL THE "
|
|
606
|
+
'YOU MUST EXPLICITLY CALL THE "stage_change" MCP TOOL FOR EVERY ENTITY YOU EXTRACT, THEN "commit_changes" TO WRITE THEM.',
|
|
693
607
|
'DO NOT JUST PRINT THE RESULTS AS TEXT IN THE CHAT WINDOW. PRINTING RESULTS WITHOUT CALLING THE MCP TOOLS DOES NOT WRITE THEM TO THE DATABASE AND MAKES THE ENTIRE INDEXING RUN A WASTE OF TIME AND TOKENS.',
|
|
694
608
|
'NEVER WRITE OR EXECUTE EXTERNAL SCRIPTS (like Python, Node.js, Bash, etc.) to automate or lazy load indexing. You must read files and call the MCP tools step-by-step natively in the chat. This ensures progress is tracked in the SQLite scratchpad database and can be resumed/continued in subsequent chats if context limits are hit.',
|
|
695
609
|
'ONCE YOU START INDEXING, DO NOT STOP or pause to ask for confirmation between checkpoints. Keep executing and indexing files continuously until the codebase is fully indexed or your context token limit is reached.',
|
|
696
610
|
'IF YOU ENCOUNTER CONTEXT RESETS, RESUME WORK BY CALLING "index_continue" AND CONTINUOUSLY COMMIT PROGRESS BY CALLING "index_checkpoint" EVERY 10 FILES.',
|
|
697
611
|
'',
|
|
698
612
|
'📋 CODE EXCLUSION & PRECISION RULES:',
|
|
699
|
-
'1. EXCLUDE Language Globals / Built-ins: Do NOT
|
|
700
|
-
'2. EXCLUDE Primitive/Native Types: Do NOT
|
|
701
|
-
'3. EXCLUDE External / Third-party Modules: Do NOT
|
|
702
|
-
'4. INTERNAL ENTITIES ONLY: Only
|
|
613
|
+
'1. EXCLUDE Language Globals / Built-ins: Do NOT stage nodes for Promise, Map, Set, JSON, console, Error, Object, Array, RegExp, Date, Math, etc.',
|
|
614
|
+
'2. EXCLUDE Primitive/Native Types: Do NOT stage nodes for string, number, boolean, any, void, unknown, never, null, undefined, dict, list, etc.',
|
|
615
|
+
'3. EXCLUDE External / Third-party Modules: Do NOT stage nodes for lodash, express, react, @nestjs/common, etc.',
|
|
616
|
+
'4. INTERNAL ENTITIES ONLY: Only stage nodes for constructs defined inside this codebase.',
|
|
703
617
|
'',
|
|
704
|
-
'📋
|
|
705
|
-
'PHASE 1: NODE & CODE EXTRACTION',
|
|
618
|
+
'📋 STAGE → COMMIT INDEXING PROTOCOL:',
|
|
706
619
|
'1. For each file in each repo: read it, extract ALL defined nodes — functions, methods, classes, interfaces, types, DTOs, routing handlers, schemas, resolvers, etc.',
|
|
707
|
-
'2. Call
|
|
708
|
-
'3. Call
|
|
709
|
-
'4.
|
|
710
|
-
'',
|
|
711
|
-
'PHASE 2: CONNECTION RESOLUTION (LINKING)',
|
|
712
|
-
'1. Once all files have been indexed in Phase 1, get the list of all nodes using list_nodes.',
|
|
713
|
-
'2. Iterate through all nodes. For each node, review its code snapshot and identify which other active codebase nodes it calls/references.',
|
|
714
|
-
'3. Call add_connection (source_node_id, target_node_id) to link them.',
|
|
715
|
-
'4. Call index_checkpoint every 10 nodes to save progress.',
|
|
716
|
-
'5. When all connections have been linked, call index_complete.',
|
|
620
|
+
'2. Call stage_change for EVERY entity found — pass its node_id, file_path, code_snapshot, reasoning, and the most specific taxonomy type. You do NOT need to figure out connections; commit_changes resolves them from the code via AST.',
|
|
621
|
+
'3. Call index_checkpoint every 10 files to save progress.',
|
|
622
|
+
'4. Every ~50 entities (or at the end of a repo), call commit_changes to flush the staged buffer — it creates all nodes, writes all history, and resolves all connections (including into already-committed nodes) in one pass. Committing in batches keeps the buffer small.',
|
|
623
|
+
'5. When the whole codebase is staged and committed, call index_complete.',
|
|
717
624
|
'6. AFTER index_complete, CALL "recheck_graph" to automatically prune any spurious, built-in, or orphaned nodes and ensure high graph precision.'
|
|
718
625
|
]
|
|
719
626
|
}, null, 2)
|
|
@@ -793,6 +700,56 @@ function createMcpServer() {
|
|
|
793
700
|
}]
|
|
794
701
|
};
|
|
795
702
|
}
|
|
703
|
+
case 'stage_change': {
|
|
704
|
+
const devmindPath = resolveDevmindPath(args.devmind_path);
|
|
705
|
+
const entry = {
|
|
706
|
+
node_id: String(args.node_id),
|
|
707
|
+
file_path: String(args.file_path),
|
|
708
|
+
code_snapshot: String(args.code_snapshot),
|
|
709
|
+
reasoning: args.reasoning,
|
|
710
|
+
name: args.name ? String(args.name) : undefined,
|
|
711
|
+
type: args.type ? String(args.type) : undefined,
|
|
712
|
+
signature: args.signature ? String(args.signature) : undefined,
|
|
713
|
+
session_id: args.session_id ? String(args.session_id) : undefined
|
|
714
|
+
};
|
|
715
|
+
const pendingCount = (0, staging_1.stageEntry)(devmindPath, entry);
|
|
716
|
+
return {
|
|
717
|
+
content: [{
|
|
718
|
+
type: 'text',
|
|
719
|
+
text: JSON.stringify({
|
|
720
|
+
staged: true,
|
|
721
|
+
node_id: entry.node_id,
|
|
722
|
+
pending_count: pendingCount,
|
|
723
|
+
reminder: 'Call commit_changes once you have staged every touched file, or nothing is written to the graph.'
|
|
724
|
+
})
|
|
725
|
+
}]
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
case 'commit_changes': {
|
|
729
|
+
const devmindPath = resolveDevmindPath(args.devmind_path);
|
|
730
|
+
const db = getDatabase(devmindPath);
|
|
731
|
+
const entries = (0, staging_1.readStaged)(devmindPath);
|
|
732
|
+
if (entries.length === 0) {
|
|
733
|
+
return {
|
|
734
|
+
content: [{ type: 'text', text: JSON.stringify({ committed: false, message: 'Nothing staged. Call stage_change first.' }) }]
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
const summary = (0, staging_1.commitStagedChanges)(db, devmindPath, entries);
|
|
738
|
+
(0, staging_1.clearStaged)(devmindPath);
|
|
739
|
+
return {
|
|
740
|
+
content: [{
|
|
741
|
+
type: 'text',
|
|
742
|
+
text: JSON.stringify({
|
|
743
|
+
committed: true,
|
|
744
|
+
message: `✅ Committed ${summary.nodes} node(s), ${summary.history_entries} history entr(ies), ${summary.edges_added} connection(s) resolved` +
|
|
745
|
+
(summary.missing_filled > 0 ? `, ${summary.missing_filled} missing node(s) auto-created.` : '.'),
|
|
746
|
+
...summary
|
|
747
|
+
}, null, 2)
|
|
748
|
+
}]
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
// ── Deprecated write handlers: NOT advertised in ListTools (superseded by
|
|
752
|
+
// stage_change/commit_changes), but retained so any direct/legacy call still works. ──
|
|
796
753
|
case 'add_node': {
|
|
797
754
|
const devmindPath = resolveDevmindPath(args.devmind_path);
|
|
798
755
|
const rawNodeId = String(args.node_id);
|
|
@@ -924,6 +881,17 @@ function createMcpServer() {
|
|
|
924
881
|
content: [{ type: 'text', text: JSON.stringify(decisions, null, 2) }]
|
|
925
882
|
};
|
|
926
883
|
}
|
|
884
|
+
case 'search_code': {
|
|
885
|
+
const devmindPath = resolveDevmindPath(args.devmind_path);
|
|
886
|
+
const query = String(args.query);
|
|
887
|
+
const isRegex = args.is_regex === true;
|
|
888
|
+
const caseInsensitive = args.case_insensitive !== false;
|
|
889
|
+
const db = getDatabase(devmindPath);
|
|
890
|
+
const results = db.searchCode({ query, is_regex: isRegex, case_insensitive: caseInsensitive });
|
|
891
|
+
return {
|
|
892
|
+
content: [{ type: 'text', text: JSON.stringify(results, null, 2) }]
|
|
893
|
+
};
|
|
894
|
+
}
|
|
927
895
|
case 'get_orphaned_nodes': {
|
|
928
896
|
const devmindPath = resolveDevmindPath(args.devmind_path);
|
|
929
897
|
const db = getDatabase(devmindPath);
|