devsmind-mcp 2.2.2 → 2.3.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 (44) hide show
  1. package/README.md +234 -606
  2. package/dist/cli/analyze.d.ts +13 -0
  3. package/dist/cli/analyze.js +143 -0
  4. package/dist/cli/analyze.js.map +1 -0
  5. package/dist/cli/index.js +62 -0
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/integrations/memory.js +7 -2
  8. package/dist/cli/integrations/memory.js.map +1 -1
  9. package/dist/cli/integrations/prompt.d.ts +2 -0
  10. package/dist/cli/integrations/prompt.js +21 -7
  11. package/dist/cli/integrations/prompt.js.map +1 -1
  12. package/dist/cli/rule.js +35 -54
  13. package/dist/cli/rule.js.map +1 -1
  14. package/dist/cli/sync.d.ts +7 -0
  15. package/dist/cli/sync.js +40 -7
  16. package/dist/cli/sync.js.map +1 -1
  17. package/dist/cli/workflow.d.ts +8 -0
  18. package/dist/cli/workflow.js +156 -0
  19. package/dist/cli/workflow.js.map +1 -0
  20. package/dist/db/analyze.d.ts +67 -0
  21. package/dist/db/analyze.js +163 -0
  22. package/dist/db/analyze.js.map +1 -0
  23. package/dist/db/database.d.ts +151 -2
  24. package/dist/db/database.js +682 -62
  25. package/dist/db/database.js.map +1 -1
  26. package/dist/db/schema.d.ts +28 -1
  27. package/dist/db/schema.js +34 -0
  28. package/dist/db/schema.js.map +1 -1
  29. package/dist/db/staging.d.ts +4 -0
  30. package/dist/db/staging.js +16 -2
  31. package/dist/db/staging.js.map +1 -1
  32. package/dist/db/workflow-import.d.ts +22 -0
  33. package/dist/db/workflow-import.js +116 -0
  34. package/dist/db/workflow-import.js.map +1 -0
  35. package/dist/mcp/server.d.ts +1 -1
  36. package/dist/mcp/server.js +369 -11
  37. package/dist/mcp/server.js.map +1 -1
  38. package/dist/utils/config.d.ts +2 -0
  39. package/dist/utils/config.js +11 -0
  40. package/dist/utils/config.js.map +1 -1
  41. package/dist/utils/git.d.ts +14 -0
  42. package/dist/utils/git.js +43 -0
  43. package/dist/utils/git.js.map +1 -0
  44. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
- import { DbNode, DbHistory, DbConnection } from './schema';
1
+ import { DbNode, DbHistory, DbConnection, DbWorkflow, DbWorkflowStep, DbWorkflowArtifact } from './schema';
2
+ import { ProjectContext } from '../utils/config';
2
3
  export interface ReasoningObject {
3
4
  what_changed: string;
4
5
  why: string;
@@ -48,6 +49,7 @@ export declare class DevMindDatabase {
48
49
  private context;
49
50
  constructor(dbPath: string);
50
51
  private initSchema;
52
+ getContext(): ProjectContext | null;
51
53
  getSystemMeta(key: string): string | null;
52
54
  setSystemMeta(key: string, value: string): void;
53
55
  getNodesByFilePath(filePath: string): DbNode[];
@@ -88,7 +90,8 @@ export declare class DevMindDatabase {
88
90
  getNode(id: string): DbNode | null;
89
91
  deleteNode(id: string): void;
90
92
  deprecateNode(id: string): void;
91
- renameNode(oldId: string, newId: string, newName?: string): void;
93
+ /** `newFilePath`: pass when the rename is a file move (analyze's rename migration), leave undefined for a pure symbol-id rename where the file itself is unchanged. */
94
+ renameNode(oldId: string, newId: string, newName?: string, newFilePath?: string): void;
92
95
  /**
93
96
  * Rewrites a history/[id].json file's identifying fields (node_id, node_metadata) in
94
97
  * place, leaving code_snapshot/reasoning/timestamps untouched. Used after a rename so
@@ -224,6 +227,135 @@ export declare class DevMindDatabase {
224
227
  }): DbNode[];
225
228
  getAllConnections(): DbConnection[];
226
229
  getAllHistory(): DbHistory[];
230
+ /** Nodes whose total (in + out) connection degree meets/exceeds `threshold` — architectural bottleneck candidates. */
231
+ getGodEntities(threshold?: number): {
232
+ id: string;
233
+ name: string;
234
+ file_path: string;
235
+ degree: number;
236
+ }[];
237
+ /** DFS cycle detection over the connection graph, capped at `maxCycles` reported paths. */
238
+ getCircularDependencies(maxCycles?: number): string[][];
239
+ /** node_connections rows whose source or target no longer exists in `nodes` (broken by a non-transactional delete, or a sync race). */
240
+ getDanglingEdges(): DbConnection[];
241
+ /** Deletes a single dangling `node_connections` row. The edge itself is invalid data — no history/graph JSON to rewrite. */
242
+ deleteDanglingEdge(sourceId: string, targetId: string): void;
243
+ /** Node ids that differ only by case — a real collision risk on Windows's case-insensitive filesystem. */
244
+ getDuplicateNodeIds(): {
245
+ lowerId: string;
246
+ ids: string[];
247
+ }[];
248
+ /** History rows whose flattened `reasoning` text has no non-empty `Developer:` line — can't be attributed to anyone. */
249
+ getHistoryMissingDeveloper(): {
250
+ id: string;
251
+ node_id: string;
252
+ updated_at: string;
253
+ }[];
254
+ /**
255
+ * History rows with a blank code snapshot — usually a silent AST extraction failure.
256
+ * The `history.code_snapshot` DB column is always written as `''` (the real content
257
+ * lives only in the per-row JSON on disk, see `populateHistoryFromDisk`), so this
258
+ * must read through the populated rows rather than querying the column directly.
259
+ */
260
+ getEmptyCodeSnapshots(): {
261
+ id: string;
262
+ node_id: string;
263
+ updated_at: string;
264
+ }[];
265
+ private workflowsDir;
266
+ /** Serializes the workflow + its steps + artifact index to disk so teammates can sync it via git. */
267
+ private writeWorkflowToDisk;
268
+ createWorkflow(name: string, description: string): DbWorkflow;
269
+ getWorkflow(id: string): DbWorkflow | null;
270
+ getActiveWorkflow(): DbWorkflow | null;
271
+ listWorkflows(status?: 'active' | 'paused' | 'completed'): DbWorkflow[];
272
+ /** Pauses the currently active workflow (if any) and clears the active pointer. */
273
+ pauseWorkflow(): DbWorkflow | null;
274
+ /** Resumes `id`, auto-pausing whatever was previously active (only one workflow is active at a time). */
275
+ resumeWorkflow(id: string): DbWorkflow;
276
+ completeWorkflow(id: string): DbWorkflow;
277
+ addWorkflowStep(workflowId: string, opts: {
278
+ summary: string;
279
+ pendingTasks?: string;
280
+ historyIds?: string[];
281
+ sessionId?: string;
282
+ }): DbWorkflowStep;
283
+ /** Writes `content` to `.devmind/workflows/<workflowId>/<artifactId>_<sourceName>` and records the DB row. */
284
+ addWorkflowArtifact(workflowId: string, opts: {
285
+ stepId?: string;
286
+ type: string;
287
+ sourceName: string;
288
+ content: string;
289
+ }): DbWorkflowArtifact;
290
+ getWorkflowContext(id: string, opts?: {
291
+ includeArtifactContent?: boolean;
292
+ }): {
293
+ workflow: DbWorkflow;
294
+ steps: DbWorkflowStep[];
295
+ artifacts: (DbWorkflowArtifact & {
296
+ content?: string;
297
+ })[];
298
+ };
299
+ /**
300
+ * Returns steps for a workflow with optional pagination.
301
+ * Use `last_n` to get only the most recent N steps (tail), or `limit`/`offset` for
302
+ * arbitrary pagination. Without any option, all steps are returned.
303
+ */
304
+ getWorkflowSteps(workflowId: string, opts?: {
305
+ limit?: number;
306
+ offset?: number;
307
+ last_n?: number;
308
+ }): DbWorkflowStep[];
309
+ /**
310
+ * Reads a single workflow artifact's file content from disk.
311
+ * Accepts either an artifact_id or a source_name (first match used).
312
+ */
313
+ readWorkflowArtifact(workflowId: string, artifactId: string): {
314
+ artifact: DbWorkflowArtifact;
315
+ content: string;
316
+ };
317
+ /**
318
+ * Full-text keyword search across all workflows' step summaries, pending_tasks,
319
+ * and artifact source names. Optionally also searches artifact file content.
320
+ * Returns a list of matches grouped by workflow.
321
+ */
322
+ searchWorkflows(query: string, opts?: {
323
+ include_artifact_content?: boolean;
324
+ status?: 'active' | 'paused' | 'completed';
325
+ }): Array<{
326
+ workflow: DbWorkflow;
327
+ matched_steps: DbWorkflowStep[];
328
+ matched_artifacts: (DbWorkflowArtifact & {
329
+ content_snippet?: string;
330
+ })[];
331
+ }>;
332
+ /**
333
+ * Imports an existing flow/architecture doc as a paused workflow (not active — importing
334
+ * a doc isn't the same as declaring active work). Idempotent on `name`: re-importing the
335
+ * same doc overwrites its existing `imported_doc` artifact file in place instead of
336
+ * creating a duplicate workflow every time the source docs are re-imported.
337
+ */
338
+ importWorkflowDoc(name: string, description: string, content: string, sourceFileName: string): {
339
+ workflow: DbWorkflow;
340
+ created: boolean;
341
+ };
342
+ private static readonly SPURIOUS_NODE_NAMES;
343
+ /**
344
+ * Read-only detection shared by `pruneSpuriousNodes` (which acts on it) and `devsmind
345
+ * analyze`'s dry-run report (which just lists it). Never mutates the DB.
346
+ */
347
+ findSpuriousAndMissingFileNodes(workspaceRoot: string): {
348
+ spurious: {
349
+ id: string;
350
+ name: string;
351
+ file_path: string;
352
+ }[];
353
+ missingFile: {
354
+ id: string;
355
+ name: string;
356
+ file_path: string;
357
+ }[];
358
+ };
227
359
  pruneSpuriousNodes(workspaceRoot: string): {
228
360
  prunedCount: number;
229
361
  prunedNodes: string[];
@@ -231,9 +363,26 @@ export declare class DevMindDatabase {
231
363
  private populateHistoryFromDisk;
232
364
  private writeHistoryToDisk;
233
365
  toRepoRelativePath(absolutePath: string): string;
366
+ /**
367
+ * Rejects a resolved path that escapes its expected root (e.g. via a stored
368
+ * `{repo}/../../..` path traveling outside the repo) by clamping it back to
369
+ * the root itself. node_id/file_path values flow in from AI-supplied tool
370
+ * calls, so a resolve must never be trusted to stay inside root on its own.
371
+ */
372
+ private clampToRoot;
373
+ /**
374
+ * True if `absPath` sits inside a configured repo root or the workspace root itself.
375
+ * Used to reject `stage_change`/`update_history` file paths that would otherwise let a
376
+ * tool call read/write any file on disk (absolute path, or a `../` escape) instead of
377
+ * just repo source — nothing upstream of this validates that the AI-supplied path is
378
+ * actually inside the project.
379
+ */
380
+ isPathAllowed(absPath: string): boolean;
234
381
  toAbsolutePath(repoRelativePath: string): string;
235
382
  syncFromDisk(): void;
236
383
  /** Escape LIKE metacharacters so a path is matched literally (use with ESCAPE '\\'). */
237
384
  private likeEscape;
238
385
  writeGraphToDisk(filePath: string): void;
386
+ /** Force-syncs all database nodes and workflows to disk JSON files. */
387
+ syncToDisk(): void;
239
388
  }