devsmind-mcp 4.1.1 → 4.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.
- package/README.md +66 -17
- package/dist/cli/activity.js +1 -1
- package/dist/cli/activity.js.map +1 -1
- package/dist/cli/add-repo.d.ts +20 -0
- package/dist/cli/add-repo.js +171 -0
- package/dist/cli/add-repo.js.map +1 -0
- package/dist/cli/analyze.js +11 -1
- package/dist/cli/analyze.js.map +1 -1
- package/dist/cli/branch.d.ts +24 -0
- package/dist/cli/branch.js +148 -0
- package/dist/cli/branch.js.map +1 -0
- package/dist/cli/index.js +56 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +27 -39
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/integrations/memory-topics.js +1 -1
- package/dist/cli/integrations/memory-topics.js.map +1 -1
- package/dist/cli/integrations/prompt.d.ts +9 -0
- package/dist/cli/integrations/prompt.js +26 -1
- package/dist/cli/integrations/prompt.js.map +1 -1
- package/dist/cli/integrations/registry.d.ts +23 -7
- package/dist/cli/integrations/registry.js +69 -8
- package/dist/cli/integrations/registry.js.map +1 -1
- package/dist/cli/integrations/skill.d.ts +16 -10
- package/dist/cli/integrations/skill.js +108 -22
- package/dist/cli/integrations/skill.js.map +1 -1
- package/dist/cli/llm-client.js +15 -7
- package/dist/cli/llm-client.js.map +1 -1
- package/dist/cli/prune.js +3 -15
- package/dist/cli/prune.js.map +1 -1
- package/dist/cli/rule.js +2 -2
- package/dist/cli/rule.js.map +1 -1
- package/dist/cli/runner.d.ts +8 -0
- package/dist/cli/runner.js +12 -5
- package/dist/cli/runner.js.map +1 -1
- package/dist/cli/sync.d.ts +16 -0
- package/dist/cli/sync.js +80 -0
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/view.js +3 -19
- package/dist/cli/view.js.map +1 -1
- package/dist/db/analyze.d.ts +5 -0
- package/dist/db/analyze.js +23 -0
- package/dist/db/analyze.js.map +1 -1
- package/dist/db/database.d.ts +56 -0
- package/dist/db/database.js +124 -6
- package/dist/db/database.js.map +1 -1
- package/dist/db/grep.js +1 -1
- package/dist/db/grep.js.map +1 -1
- package/dist/db/indexer.d.ts +1 -1
- package/dist/db/indexer.js +3 -3
- package/dist/db/indexer.js.map +1 -1
- package/dist/mcp/server.js +359 -60
- package/dist/mcp/server.js.map +1 -1
- package/dist/utils/config.d.ts +76 -6
- package/dist/utils/config.js +110 -12
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/devsmind-branch.d.ts +55 -0
- package/dist/utils/devsmind-branch.js +244 -0
- package/dist/utils/devsmind-branch.js.map +1 -0
- package/dist/utils/scanner.js +1 -1
- package/dist/utils/scanner.js.map +1 -1
- package/package.json +1 -1
package/dist/db/database.js
CHANGED
|
@@ -2692,6 +2692,31 @@ class DevMindDatabase {
|
|
|
2692
2692
|
this.writeWorkflowToDisk(workflowId);
|
|
2693
2693
|
return { id, workflow_id: workflowId, step_id: opts.stepId || null, type: opts.type, source_name: opts.sourceName, file_path: filePath, created_at: now };
|
|
2694
2694
|
}
|
|
2695
|
+
/**
|
|
2696
|
+
* Copies an EXISTING file's bytes into `.devmind/workflows/<workflowId>/<artifactId>_<name>` and
|
|
2697
|
+
* records the DB row — same storage as addWorkflowArtifact, but the source is a file already on
|
|
2698
|
+
* disk rather than a string already in memory, so this is copyFileSync (binary-safe: PDFs,
|
|
2699
|
+
* docx, images) instead of writeFileSync(..., 'utf-8').
|
|
2700
|
+
*/
|
|
2701
|
+
addWorkflowArtifactFromFile(workflowId, opts) {
|
|
2702
|
+
if (!this.getWorkflow(workflowId))
|
|
2703
|
+
throw new Error(`Workflow not found: ${workflowId}`);
|
|
2704
|
+
const id = crypto.randomUUID();
|
|
2705
|
+
const now = new Date().toISOString();
|
|
2706
|
+
const sourceName = path.basename(opts.sourcePath);
|
|
2707
|
+
const safeName = sourceName.replace(/[^a-zA-Z0-9._-]/g, '_') || 'artifact';
|
|
2708
|
+
const dir = path.join(this.workflowsDir(), workflowId);
|
|
2709
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
2710
|
+
const filePath = path.join(dir, `${id}_${safeName}`);
|
|
2711
|
+
fs.copyFileSync(opts.sourcePath, filePath);
|
|
2712
|
+
this.db.prepare(`
|
|
2713
|
+
INSERT INTO workflow_artifacts (id, workflow_id, step_id, type, source_name, file_path, created_at)
|
|
2714
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
2715
|
+
`).run(id, workflowId, opts.stepId || null, opts.type, sourceName, filePath, now);
|
|
2716
|
+
this.db.prepare(`UPDATE workflows SET updated_at = ? WHERE id = ?`).run(now, workflowId);
|
|
2717
|
+
this.writeWorkflowToDisk(workflowId);
|
|
2718
|
+
return { id, workflow_id: workflowId, step_id: opts.stepId || null, type: opts.type, source_name: sourceName, file_path: filePath, created_at: now };
|
|
2719
|
+
}
|
|
2695
2720
|
/**
|
|
2696
2721
|
* The workflow's story: its steps in order, plus the docs attached to it.
|
|
2697
2722
|
*
|
|
@@ -2822,6 +2847,58 @@ class DevMindDatabase {
|
|
|
2822
2847
|
}
|
|
2823
2848
|
return { spurious, missingFile };
|
|
2824
2849
|
}
|
|
2850
|
+
/**
|
|
2851
|
+
* Finds `.devmind/graph`/`.devmind/vectors` JSON output paths that a live node's file_path
|
|
2852
|
+
* still maps to, but that are occupied by a stray DIRECTORY instead of a file — the case
|
|
2853
|
+
* `isDegenerateDiskJsonPath`'s existsSync+isDirectory branch refuses to write over. Left
|
|
2854
|
+
* behind by some earlier node whose file_path once collapsed onto that exact path (see that
|
|
2855
|
+
* method's comment for how); once the directory exists, `writeGraphToDisk`/`writeVectorsToDisk`
|
|
2856
|
+
* can only skip-and-warn forever, since neither one ever deletes anything. This is what
|
|
2857
|
+
* `devsmind analyze --fix` uses to actually remove the blocker and re-trigger the write, so a
|
|
2858
|
+
* perfectly healthy node's file stops being warned about on every future sync.
|
|
2859
|
+
*
|
|
2860
|
+
* Deliberately mirrors ONLY the third of `isDegenerateDiskJsonPath`'s three checks (never the
|
|
2861
|
+
* first two — an empty/trailing-slash/`..`-collapsing diskRelPath means the node's OWN
|
|
2862
|
+
* file_path is degenerate, already covered by `missingFile` above and fixed by deprecating the
|
|
2863
|
+
* node, not by deleting anything on disk). Skipping those here is required for safety: a
|
|
2864
|
+
* collapsed diskRelPath would make `targetPath` resolve to `graphDir`/`vectorsDir` itself (or
|
|
2865
|
+
* an ancestor of it), and blindly reporting that as "stray" would recommend deleting the whole
|
|
2866
|
+
* output tree.
|
|
2867
|
+
*/
|
|
2868
|
+
findStrayOutputDirs() {
|
|
2869
|
+
const rows = this.db.prepare('SELECT DISTINCT file_path FROM nodes').all();
|
|
2870
|
+
const filePaths = new Set();
|
|
2871
|
+
for (const row of rows) {
|
|
2872
|
+
if (row.file_path) {
|
|
2873
|
+
for (const p of row.file_path.split(',').map(s => s.trim()).filter(Boolean)) {
|
|
2874
|
+
filePaths.add(p);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
const workspaceRoot = (0, config_1.canonicalizePath)(path.dirname(this.dbPath));
|
|
2879
|
+
const dirsByKind = [
|
|
2880
|
+
{ dir: path.join(workspaceRoot, 'graph'), kind: 'graph' },
|
|
2881
|
+
{ dir: path.join(workspaceRoot, 'vectors'), kind: 'vectors' },
|
|
2882
|
+
];
|
|
2883
|
+
const results = [];
|
|
2884
|
+
for (const filePath of filePaths) {
|
|
2885
|
+
const absPath = (0, config_1.canonicalizePath)(filePath);
|
|
2886
|
+
const repoRelPath = this.toRepoRelativePath(absPath);
|
|
2887
|
+
const diskRelPath = repoRelPath.replace(/^\{([^}]+)\}/, '$1').replace(/\.[^/.]+$/, '.json');
|
|
2888
|
+
if (!diskRelPath || diskRelPath.endsWith('/') || diskRelPath.endsWith('\\'))
|
|
2889
|
+
continue;
|
|
2890
|
+
for (const { dir, kind } of dirsByKind) {
|
|
2891
|
+
const targetPath = path.join(dir, diskRelPath);
|
|
2892
|
+
const relToContainer = path.relative(dir, targetPath);
|
|
2893
|
+
if (relToContainer === '' || relToContainer.startsWith('..'))
|
|
2894
|
+
continue;
|
|
2895
|
+
if (fs.existsSync(targetPath) && fs.statSync(targetPath).isDirectory()) {
|
|
2896
|
+
results.push({ file_path: filePath, target: targetPath, kind });
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
return results;
|
|
2901
|
+
}
|
|
2825
2902
|
pruneSpuriousNodes(workspaceRoot) {
|
|
2826
2903
|
const { spurious, missingFile } = this.findSpuriousAndMissingFileNodes(workspaceRoot);
|
|
2827
2904
|
const candidates = [...spurious, ...missingFile];
|
|
@@ -3393,6 +3470,28 @@ class DevMindDatabase {
|
|
|
3393
3470
|
return true;
|
|
3394
3471
|
return fs.existsSync(targetPath) && fs.statSync(targetPath).isDirectory();
|
|
3395
3472
|
}
|
|
3473
|
+
/**
|
|
3474
|
+
* True when `filePath` ITSELF can never resolve to a valid `graph/`/`vectors/` JSON target —
|
|
3475
|
+
* i.e. `isDegenerateDiskJsonPath`'s first two checks (empty/trailing-slash/`..`-collapsing
|
|
3476
|
+
* `diskRelPath`), never its third (existsSync+isDirectory, which is about a stray directory
|
|
3477
|
+
* left on disk, not about the file_path being bad — that case is exactly what
|
|
3478
|
+
* `findStrayOutputDirs` fixes, and can legitimately happen for a perfectly healthy file_path,
|
|
3479
|
+
* so it must NOT be treated as permanent here). Deliberately does no filesystem I/O — pure
|
|
3480
|
+
* string logic, cheap enough to call for every distinct file_path a full `syncToDisk` touches.
|
|
3481
|
+
* Used to recognize a file_path that's structurally unfixable no matter how many times a write
|
|
3482
|
+
* is retried, as opposed to one that's merely blocked by removable disk cruft.
|
|
3483
|
+
*/
|
|
3484
|
+
isFilePathStructurallyDegenerate(filePath) {
|
|
3485
|
+
const workspaceRoot = (0, config_1.canonicalizePath)(path.dirname(this.dbPath));
|
|
3486
|
+
const absPath = (0, config_1.canonicalizePath)(filePath);
|
|
3487
|
+
const repoRelPath = this.toRepoRelativePath(absPath);
|
|
3488
|
+
const diskRelPath = repoRelPath.replace(/^\{([^}]+)\}/, '$1').replace(/\.[^/.]+$/, '.json');
|
|
3489
|
+
if (!diskRelPath || diskRelPath.endsWith('/') || diskRelPath.endsWith('\\'))
|
|
3490
|
+
return true;
|
|
3491
|
+
const graphDir = path.join(workspaceRoot, 'graph');
|
|
3492
|
+
const relToContainer = path.relative(graphDir, path.join(graphDir, diskRelPath));
|
|
3493
|
+
return relToContainer === '' || relToContainer.startsWith('..');
|
|
3494
|
+
}
|
|
3396
3495
|
writeGraphToDisk(filePath) {
|
|
3397
3496
|
try {
|
|
3398
3497
|
if (!filePath)
|
|
@@ -3538,16 +3637,35 @@ class DevMindDatabase {
|
|
|
3538
3637
|
* the DB, or after a past write silently failed (e.g. the directory-typed file_path bug
|
|
3539
3638
|
* `isDegenerateDiskJsonPath` now catches) — so leaving vectors out of the force-resync
|
|
3540
3639
|
* defeated half the point of running it.
|
|
3640
|
+
*
|
|
3641
|
+
* A file_path shared by at least one ACTIVE node is always re-attempted, deprecated or not —
|
|
3642
|
+
* a deprecated node sharing a real file with a live one still needs its `deprecated:1` flag
|
|
3643
|
+
* kept current on disk. A file_path held ONLY by deprecated nodes is skipped when it's also
|
|
3644
|
+
* `isFilePathStructurallyDegenerate` (a directory rather than a file — `deprecateNode` already
|
|
3645
|
+
* wrote it once, at the moment it was deprecated, so nothing new would come of retrying):
|
|
3646
|
+
* without this, a node deprecated for exactly this reason produced the identical "Skipped
|
|
3647
|
+
* writing graph JSON" warning on every single future `sync`, forever, since deprecating a node
|
|
3648
|
+
* never clears its (already-garbage) file_path — indistinguishable from the bug never having
|
|
3649
|
+
* been fixed at all.
|
|
3541
3650
|
*/
|
|
3542
3651
|
syncToDisk() {
|
|
3543
3652
|
try {
|
|
3544
|
-
const rows = this.db.prepare('SELECT DISTINCT file_path FROM nodes').all();
|
|
3545
|
-
const
|
|
3653
|
+
const rows = this.db.prepare('SELECT DISTINCT file_path, deprecated FROM nodes').all();
|
|
3654
|
+
const activeFilePaths = new Set();
|
|
3655
|
+
const allFilePaths = new Set();
|
|
3546
3656
|
for (const row of rows) {
|
|
3547
|
-
if (row.file_path)
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3657
|
+
if (!row.file_path)
|
|
3658
|
+
continue;
|
|
3659
|
+
for (const p of row.file_path.split(',').map(s => s.trim()).filter(Boolean)) {
|
|
3660
|
+
allFilePaths.add(p);
|
|
3661
|
+
if (!row.deprecated)
|
|
3662
|
+
activeFilePaths.add(p);
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
const filePaths = new Set();
|
|
3666
|
+
for (const p of allFilePaths) {
|
|
3667
|
+
if (activeFilePaths.has(p) || !this.isFilePathStructurallyDegenerate(p)) {
|
|
3668
|
+
filePaths.add(p);
|
|
3551
3669
|
}
|
|
3552
3670
|
}
|
|
3553
3671
|
for (const filePath of filePaths) {
|