depwire-cli 0.6.0 → 0.6.2
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 +73 -40
- package/dist/{chunk-AFJFZXCI.js → chunk-VNUOE5VC.js} +69 -35
- package/dist/index.js +28 -18
- package/dist/mcpb-entry.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,26 +61,30 @@ npx depwire-cli --help
|
|
|
61
61
|
### CLI Usage
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
#
|
|
65
|
-
|
|
64
|
+
# Auto-detects project root from current directory
|
|
65
|
+
depwire viz
|
|
66
|
+
depwire parse
|
|
67
|
+
depwire docs
|
|
68
|
+
depwire health
|
|
66
69
|
|
|
67
|
-
#
|
|
70
|
+
# Or specify a directory explicitly
|
|
71
|
+
npx depwire-cli viz ./my-project
|
|
68
72
|
npx depwire-cli parse ./my-project
|
|
69
73
|
|
|
70
74
|
# Exclude test files and node_modules
|
|
71
|
-
npx depwire-cli parse
|
|
75
|
+
npx depwire-cli parse --exclude "**/*.test.*" "**/node_modules/**"
|
|
72
76
|
|
|
73
77
|
# Show detailed parsing progress
|
|
74
|
-
npx depwire-cli parse
|
|
78
|
+
npx depwire-cli parse --verbose
|
|
75
79
|
|
|
76
80
|
# Export with pretty-printed JSON and statistics
|
|
77
|
-
npx depwire-cli parse
|
|
81
|
+
npx depwire-cli parse --pretty --stats
|
|
78
82
|
|
|
79
83
|
# Generate codebase documentation
|
|
80
|
-
npx depwire-cli docs
|
|
84
|
+
npx depwire-cli docs --verbose --stats
|
|
81
85
|
|
|
82
86
|
# Custom output file
|
|
83
|
-
npx depwire-cli parse
|
|
87
|
+
npx depwire-cli parse -o my-graph.json
|
|
84
88
|
```
|
|
85
89
|
|
|
86
90
|
### Claude Desktop
|
|
@@ -98,16 +102,20 @@ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_
|
|
|
98
102
|
}
|
|
99
103
|
```
|
|
100
104
|
|
|
105
|
+
**Depwire auto-detects your project root. No path configuration needed.**
|
|
106
|
+
|
|
101
107
|
Then in chat:
|
|
102
108
|
```
|
|
103
|
-
|
|
109
|
+
Show me the architecture.
|
|
104
110
|
```
|
|
105
111
|
|
|
106
112
|
### Cursor
|
|
107
113
|
|
|
108
114
|
Settings → Features → Experimental → Enable MCP → Add Server:
|
|
109
115
|
- Command: `npx`
|
|
110
|
-
- Args: `-y depwire-cli mcp
|
|
116
|
+
- Args: `-y depwire-cli mcp`
|
|
117
|
+
|
|
118
|
+
**Depwire auto-detects your project root from the current working directory.**
|
|
111
119
|
|
|
112
120
|
## Available MCP Tools
|
|
113
121
|
|
|
@@ -143,20 +151,23 @@ Settings → Features → Experimental → Enable MCP → Add Server:
|
|
|
143
151
|

|
|
144
152
|
|
|
145
153
|
```bash
|
|
146
|
-
#
|
|
154
|
+
# Auto-detects project root (run from anywhere in your project)
|
|
155
|
+
depwire viz
|
|
156
|
+
|
|
157
|
+
# Or specify a directory explicitly
|
|
147
158
|
depwire viz ./my-project
|
|
148
159
|
|
|
149
160
|
# Custom port
|
|
150
|
-
depwire viz
|
|
161
|
+
depwire viz --port 8080
|
|
151
162
|
|
|
152
163
|
# Exclude test files from visualization
|
|
153
|
-
depwire viz
|
|
164
|
+
depwire viz --exclude "**/*.test.*"
|
|
154
165
|
|
|
155
166
|
# Verbose mode with detailed parsing logs
|
|
156
|
-
depwire viz
|
|
167
|
+
depwire viz --verbose
|
|
157
168
|
|
|
158
169
|
# Don't auto-open browser
|
|
159
|
-
depwire viz
|
|
170
|
+
depwire viz --no-open
|
|
160
171
|
```
|
|
161
172
|
|
|
162
173
|
Opens an interactive arc diagram in your browser:
|
|
@@ -177,10 +188,12 @@ Opens an interactive arc diagram in your browser:
|
|
|
177
188
|
|
|
178
189
|
## CLI Reference
|
|
179
190
|
|
|
180
|
-
### `depwire parse
|
|
191
|
+
### `depwire parse [directory]`
|
|
181
192
|
|
|
182
193
|
Parse a project and export the dependency graph as JSON.
|
|
183
194
|
|
|
195
|
+
**Directory argument is optional** — Depwire auto-detects your project root by looking for `package.json`, `tsconfig.json`, `go.mod`, `pyproject.toml`, `setup.py`, or `.git`.
|
|
196
|
+
|
|
184
197
|
**Options:**
|
|
185
198
|
- `-o, --output <path>` — Output file path (default: `depwire-output.json`)
|
|
186
199
|
- `--exclude <patterns...>` — Glob patterns to exclude (e.g., `"**/*.test.*" "dist/**"`)
|
|
@@ -190,20 +203,25 @@ Parse a project and export the dependency graph as JSON.
|
|
|
190
203
|
|
|
191
204
|
**Examples:**
|
|
192
205
|
```bash
|
|
193
|
-
#
|
|
206
|
+
# Auto-detect project root
|
|
207
|
+
depwire parse
|
|
208
|
+
|
|
209
|
+
# Explicit directory
|
|
194
210
|
depwire parse ./src
|
|
195
211
|
|
|
196
212
|
# Exclude test files and build outputs
|
|
197
|
-
depwire parse
|
|
213
|
+
depwire parse --exclude "**/*.test.*" "**/*.spec.*" "dist/**" "build/**"
|
|
198
214
|
|
|
199
215
|
# Full verbosity with stats
|
|
200
|
-
depwire parse
|
|
216
|
+
depwire parse --verbose --stats --pretty -o graph.json
|
|
201
217
|
```
|
|
202
218
|
|
|
203
|
-
### `depwire viz
|
|
219
|
+
### `depwire viz [directory]`
|
|
204
220
|
|
|
205
221
|
Start visualization server and open arc diagram in browser.
|
|
206
222
|
|
|
223
|
+
**Directory argument is optional** — Auto-detects project root.
|
|
224
|
+
|
|
207
225
|
**Options:**
|
|
208
226
|
- `--port <number>` — Port number (default: 3456, auto-increments if in use)
|
|
209
227
|
- `--exclude <patterns...>` — Glob patterns to exclude
|
|
@@ -212,33 +230,40 @@ Start visualization server and open arc diagram in browser.
|
|
|
212
230
|
|
|
213
231
|
**Examples:**
|
|
214
232
|
```bash
|
|
215
|
-
#
|
|
233
|
+
# Auto-detect and visualize
|
|
234
|
+
depwire viz
|
|
235
|
+
|
|
236
|
+
# Explicit directory
|
|
216
237
|
depwire viz ./src
|
|
217
238
|
|
|
218
239
|
# Custom port without auto-open
|
|
219
|
-
depwire viz
|
|
240
|
+
depwire viz --port 8080 --no-open
|
|
220
241
|
|
|
221
242
|
# Exclude test files with verbose logging
|
|
222
|
-
depwire viz
|
|
243
|
+
depwire viz --exclude "**/*.test.*" --verbose
|
|
223
244
|
```
|
|
224
245
|
|
|
225
246
|
### `depwire mcp [directory]`
|
|
226
247
|
|
|
227
248
|
Start MCP server for AI tool integration (Cursor, Claude Desktop).
|
|
228
249
|
|
|
250
|
+
**Directory argument is optional** — Auto-detects project root and connects automatically.
|
|
251
|
+
|
|
229
252
|
**Examples:**
|
|
230
253
|
```bash
|
|
231
|
-
#
|
|
254
|
+
# Auto-detect and connect (recommended)
|
|
232
255
|
depwire mcp
|
|
233
256
|
|
|
234
|
-
#
|
|
257
|
+
# Explicit directory
|
|
235
258
|
depwire mcp /path/to/project
|
|
236
259
|
```
|
|
237
260
|
|
|
238
|
-
### `depwire docs
|
|
261
|
+
### `depwire docs [directory]`
|
|
239
262
|
|
|
240
263
|
Generate comprehensive codebase documentation from your dependency graph.
|
|
241
264
|
|
|
265
|
+
**Directory argument is optional** — Auto-detects project root.
|
|
266
|
+
|
|
242
267
|
**Options:**
|
|
243
268
|
- `--output <path>` — Output directory (default: `.depwire/` inside project)
|
|
244
269
|
- `--format <type>` — Output format: `markdown` or `json` (default: `markdown`)
|
|
@@ -253,23 +278,26 @@ Generate comprehensive codebase documentation from your dependency graph.
|
|
|
253
278
|
|
|
254
279
|
**Examples:**
|
|
255
280
|
```bash
|
|
256
|
-
#
|
|
281
|
+
# Auto-detect and generate all docs
|
|
282
|
+
depwire docs
|
|
283
|
+
|
|
284
|
+
# Explicit directory
|
|
257
285
|
depwire docs ./my-project
|
|
258
286
|
|
|
259
287
|
# Show generation progress and stats
|
|
260
|
-
depwire docs
|
|
288
|
+
depwire docs --verbose --stats
|
|
261
289
|
|
|
262
290
|
# Regenerate existing docs
|
|
263
|
-
depwire docs
|
|
291
|
+
depwire docs --update
|
|
264
292
|
|
|
265
293
|
# Generate specific docs only
|
|
266
|
-
depwire docs
|
|
294
|
+
depwire docs --include architecture,dependencies
|
|
267
295
|
|
|
268
296
|
# Custom output directory
|
|
269
|
-
depwire docs
|
|
297
|
+
depwire docs --output ./docs
|
|
270
298
|
|
|
271
299
|
# Regenerate only conventions doc
|
|
272
|
-
depwire docs
|
|
300
|
+
depwire docs --update --only conventions
|
|
273
301
|
```
|
|
274
302
|
|
|
275
303
|
**Generated Documents (12 total):**
|
|
@@ -291,10 +319,12 @@ depwire docs ./my-project --update --only conventions
|
|
|
291
319
|
|
|
292
320
|
Documents are stored in `.depwire/` with `metadata.json` tracking generation timestamps for staleness detection.
|
|
293
321
|
|
|
294
|
-
### `depwire health
|
|
322
|
+
### `depwire health [directory]`
|
|
295
323
|
|
|
296
324
|
Analyze dependency architecture health and get a 0-100 score across 6 quality dimensions.
|
|
297
325
|
|
|
326
|
+
**Directory argument is optional** — Auto-detects project root.
|
|
327
|
+
|
|
298
328
|
**Options:**
|
|
299
329
|
- `--json` — Output as JSON (for CI/automation)
|
|
300
330
|
- `--verbose` — Show detailed per-dimension breakdown
|
|
@@ -309,14 +339,17 @@ Analyze dependency architecture health and get a 0-100 score across 6 quality di
|
|
|
309
339
|
|
|
310
340
|
**Examples:**
|
|
311
341
|
```bash
|
|
312
|
-
#
|
|
313
|
-
depwire health
|
|
342
|
+
# Auto-detect and analyze
|
|
343
|
+
depwire health
|
|
344
|
+
|
|
345
|
+
# Explicit directory
|
|
346
|
+
depwire health ./my-project
|
|
314
347
|
|
|
315
348
|
# Detailed breakdown
|
|
316
|
-
depwire health
|
|
349
|
+
depwire health --verbose
|
|
317
350
|
|
|
318
351
|
# JSON output for CI
|
|
319
|
-
depwire health
|
|
352
|
+
depwire health --json
|
|
320
353
|
```
|
|
321
354
|
|
|
322
355
|
**Output:**
|
|
@@ -345,7 +378,7 @@ Depwire gracefully handles parse errors:
|
|
|
345
378
|
```
|
|
346
379
|
# In Claude Desktop or Cursor with Depwire MCP:
|
|
347
380
|
|
|
348
|
-
"
|
|
381
|
+
"Analyze the impact of renaming UserService to UserRepository"
|
|
349
382
|
|
|
350
383
|
# Depwire responds with:
|
|
351
384
|
# - All files that import UserService
|
|
@@ -357,7 +390,7 @@ Depwire gracefully handles parse errors:
|
|
|
357
390
|
### Understanding a New Codebase
|
|
358
391
|
|
|
359
392
|
```
|
|
360
|
-
"
|
|
393
|
+
"Show me the architecture summary"
|
|
361
394
|
|
|
362
395
|
# Depwire responds with:
|
|
363
396
|
# - Language breakdown
|
|
@@ -370,7 +403,7 @@ Depwire gracefully handles parse errors:
|
|
|
370
403
|
|
|
371
404
|
```bash
|
|
372
405
|
# Check what your changes affect before committing
|
|
373
|
-
depwire viz
|
|
406
|
+
depwire viz
|
|
374
407
|
# Review the arc diagram — red arcs show files you touched
|
|
375
408
|
```
|
|
376
409
|
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// src/parser/index.ts
|
|
2
|
-
import { readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
3
|
-
import { join as join6 } from "path";
|
|
4
|
-
|
|
5
1
|
// src/utils/files.ts
|
|
6
2
|
import { readdirSync, statSync, existsSync, lstatSync } from "fs";
|
|
7
3
|
import { join, relative } from "path";
|
|
@@ -50,6 +46,42 @@ function fileExists(filePath) {
|
|
|
50
46
|
return false;
|
|
51
47
|
}
|
|
52
48
|
}
|
|
49
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
50
|
+
const projectMarkers = [
|
|
51
|
+
"package.json",
|
|
52
|
+
// Node.js
|
|
53
|
+
"tsconfig.json",
|
|
54
|
+
// TypeScript
|
|
55
|
+
"go.mod",
|
|
56
|
+
// Go
|
|
57
|
+
"pyproject.toml",
|
|
58
|
+
// Python (modern)
|
|
59
|
+
"setup.py",
|
|
60
|
+
// Python (legacy)
|
|
61
|
+
".git"
|
|
62
|
+
// Any git repo
|
|
63
|
+
];
|
|
64
|
+
let currentDir = startDir;
|
|
65
|
+
const rootDir = "/";
|
|
66
|
+
while (currentDir !== rootDir) {
|
|
67
|
+
for (const marker of projectMarkers) {
|
|
68
|
+
const markerPath = join(currentDir, marker);
|
|
69
|
+
if (existsSync(markerPath)) {
|
|
70
|
+
return currentDir;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const parentDir = join(currentDir, "..");
|
|
74
|
+
if (parentDir === currentDir) {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
currentDir = parentDir;
|
|
78
|
+
}
|
|
79
|
+
return startDir;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/parser/index.ts
|
|
83
|
+
import { readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
84
|
+
import { join as join6 } from "path";
|
|
53
85
|
|
|
54
86
|
// src/parser/detect.ts
|
|
55
87
|
import { extname as extname3 } from "path";
|
|
@@ -2988,8 +3020,8 @@ function calculateDepthScore(graph) {
|
|
|
2988
3020
|
}
|
|
2989
3021
|
|
|
2990
3022
|
// src/health/index.ts
|
|
2991
|
-
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync6 } from "fs";
|
|
2992
|
-
import { join as join9 } from "path";
|
|
3023
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync6, mkdirSync } from "fs";
|
|
3024
|
+
import { join as join9, dirname as dirname7 } from "path";
|
|
2993
3025
|
function calculateHealthScore(graph, projectRoot) {
|
|
2994
3026
|
const coupling = calculateCouplingScore(graph);
|
|
2995
3027
|
const cohesion = calculateCohesionScore(graph);
|
|
@@ -3111,6 +3143,7 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
3111
3143
|
if (history.length > 50) {
|
|
3112
3144
|
history = history.slice(-50);
|
|
3113
3145
|
}
|
|
3146
|
+
mkdirSync(dirname7(historyFile), { recursive: true });
|
|
3114
3147
|
writeFileSync(historyFile, JSON.stringify(history, null, 2), "utf-8");
|
|
3115
3148
|
}
|
|
3116
3149
|
function loadHealthHistory(projectRoot) {
|
|
@@ -3127,11 +3160,11 @@ function loadHealthHistory(projectRoot) {
|
|
|
3127
3160
|
}
|
|
3128
3161
|
|
|
3129
3162
|
// src/docs/generator.ts
|
|
3130
|
-
import { writeFileSync as writeFileSync3, mkdirSync, existsSync as existsSync9 } from "fs";
|
|
3163
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync9 } from "fs";
|
|
3131
3164
|
import { join as join12 } from "path";
|
|
3132
3165
|
|
|
3133
3166
|
// src/docs/architecture.ts
|
|
3134
|
-
import { dirname as
|
|
3167
|
+
import { dirname as dirname8 } from "path";
|
|
3135
3168
|
|
|
3136
3169
|
// src/docs/templates.ts
|
|
3137
3170
|
function header(text, level = 1) {
|
|
@@ -3282,7 +3315,7 @@ function generateModuleStructure(graph) {
|
|
|
3282
3315
|
function getDirectoryStats(graph) {
|
|
3283
3316
|
const dirMap = /* @__PURE__ */ new Map();
|
|
3284
3317
|
graph.forEachNode((node, attrs) => {
|
|
3285
|
-
const dir =
|
|
3318
|
+
const dir = dirname8(attrs.filePath);
|
|
3286
3319
|
if (dir === ".") return;
|
|
3287
3320
|
if (!dirMap.has(dir)) {
|
|
3288
3321
|
dirMap.set(dir, {
|
|
@@ -3307,7 +3340,7 @@ function getDirectoryStats(graph) {
|
|
|
3307
3340
|
});
|
|
3308
3341
|
const filesPerDir = /* @__PURE__ */ new Map();
|
|
3309
3342
|
graph.forEachNode((node, attrs) => {
|
|
3310
|
-
const dir =
|
|
3343
|
+
const dir = dirname8(attrs.filePath);
|
|
3311
3344
|
if (!filesPerDir.has(dir)) {
|
|
3312
3345
|
filesPerDir.set(dir, /* @__PURE__ */ new Set());
|
|
3313
3346
|
}
|
|
@@ -3322,8 +3355,8 @@ function getDirectoryStats(graph) {
|
|
|
3322
3355
|
graph.forEachEdge((edge, attrs, source, target) => {
|
|
3323
3356
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
3324
3357
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
3325
|
-
const sourceDir =
|
|
3326
|
-
const targetDir =
|
|
3358
|
+
const sourceDir = dirname8(sourceAttrs.filePath);
|
|
3359
|
+
const targetDir = dirname8(targetAttrs.filePath);
|
|
3327
3360
|
if (sourceDir !== targetDir) {
|
|
3328
3361
|
if (!dirEdges.has(sourceDir)) {
|
|
3329
3362
|
dirEdges.set(sourceDir, { in: 0, out: 0 });
|
|
@@ -4304,7 +4337,7 @@ function detectCyclesDetailed(graph) {
|
|
|
4304
4337
|
}
|
|
4305
4338
|
|
|
4306
4339
|
// src/docs/onboarding.ts
|
|
4307
|
-
import { dirname as
|
|
4340
|
+
import { dirname as dirname9 } from "path";
|
|
4308
4341
|
function generateOnboarding(graph, projectRoot, version) {
|
|
4309
4342
|
let output = "";
|
|
4310
4343
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -4363,7 +4396,7 @@ function generateQuickOrientation(graph) {
|
|
|
4363
4396
|
const primaryLang = Object.entries(languages2).sort((a, b) => b[1] - a[1])[0];
|
|
4364
4397
|
const dirs = /* @__PURE__ */ new Set();
|
|
4365
4398
|
graph.forEachNode((node, attrs) => {
|
|
4366
|
-
const dir =
|
|
4399
|
+
const dir = dirname9(attrs.filePath);
|
|
4367
4400
|
if (dir !== ".") {
|
|
4368
4401
|
const topLevel = dir.split("/")[0];
|
|
4369
4402
|
dirs.add(topLevel);
|
|
@@ -4467,7 +4500,7 @@ function generateModuleMap(graph) {
|
|
|
4467
4500
|
function getDirectoryStats2(graph) {
|
|
4468
4501
|
const dirMap = /* @__PURE__ */ new Map();
|
|
4469
4502
|
graph.forEachNode((node, attrs) => {
|
|
4470
|
-
const dir =
|
|
4503
|
+
const dir = dirname9(attrs.filePath);
|
|
4471
4504
|
if (dir === ".") return;
|
|
4472
4505
|
if (!dirMap.has(dir)) {
|
|
4473
4506
|
dirMap.set(dir, {
|
|
@@ -4482,7 +4515,7 @@ function getDirectoryStats2(graph) {
|
|
|
4482
4515
|
});
|
|
4483
4516
|
const filesPerDir = /* @__PURE__ */ new Map();
|
|
4484
4517
|
graph.forEachNode((node, attrs) => {
|
|
4485
|
-
const dir =
|
|
4518
|
+
const dir = dirname9(attrs.filePath);
|
|
4486
4519
|
if (!filesPerDir.has(dir)) {
|
|
4487
4520
|
filesPerDir.set(dir, /* @__PURE__ */ new Set());
|
|
4488
4521
|
}
|
|
@@ -4497,8 +4530,8 @@ function getDirectoryStats2(graph) {
|
|
|
4497
4530
|
graph.forEachEdge((edge, attrs, source, target) => {
|
|
4498
4531
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
4499
4532
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
4500
|
-
const sourceDir =
|
|
4501
|
-
const targetDir =
|
|
4533
|
+
const sourceDir = dirname9(sourceAttrs.filePath);
|
|
4534
|
+
const targetDir = dirname9(targetAttrs.filePath);
|
|
4502
4535
|
if (sourceDir !== targetDir) {
|
|
4503
4536
|
if (!dirEdges.has(sourceDir)) {
|
|
4504
4537
|
dirEdges.set(sourceDir, { in: 0, out: 0 });
|
|
@@ -4579,7 +4612,7 @@ function detectClusters(graph) {
|
|
|
4579
4612
|
const dirFiles = /* @__PURE__ */ new Map();
|
|
4580
4613
|
const fileEdges = /* @__PURE__ */ new Map();
|
|
4581
4614
|
graph.forEachNode((node, attrs) => {
|
|
4582
|
-
const dir =
|
|
4615
|
+
const dir = dirname9(attrs.filePath);
|
|
4583
4616
|
if (!dirFiles.has(dir)) {
|
|
4584
4617
|
dirFiles.set(dir, /* @__PURE__ */ new Set());
|
|
4585
4618
|
}
|
|
@@ -4633,8 +4666,8 @@ function inferClusterName(files) {
|
|
|
4633
4666
|
if (sortedWords.length > 0 && sortedWords[0][1] > 1) {
|
|
4634
4667
|
return capitalizeFirst2(sortedWords[0][0]);
|
|
4635
4668
|
}
|
|
4636
|
-
const commonDir =
|
|
4637
|
-
if (files.every((f) =>
|
|
4669
|
+
const commonDir = dirname9(files[0]);
|
|
4670
|
+
if (files.every((f) => dirname9(f) === commonDir)) {
|
|
4638
4671
|
return capitalizeFirst2(commonDir.split("/").pop() || "Core");
|
|
4639
4672
|
}
|
|
4640
4673
|
return "Core";
|
|
@@ -4692,7 +4725,7 @@ function generateDepwireUsage(projectRoot) {
|
|
|
4692
4725
|
}
|
|
4693
4726
|
|
|
4694
4727
|
// src/docs/files.ts
|
|
4695
|
-
import { dirname as
|
|
4728
|
+
import { dirname as dirname10, basename as basename3 } from "path";
|
|
4696
4729
|
function generateFiles(graph, projectRoot, version) {
|
|
4697
4730
|
let output = "";
|
|
4698
4731
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -4796,7 +4829,7 @@ function generateDirectoryBreakdown(graph) {
|
|
|
4796
4829
|
const fileStats = getFileStats2(graph);
|
|
4797
4830
|
const dirMap = /* @__PURE__ */ new Map();
|
|
4798
4831
|
for (const file of fileStats) {
|
|
4799
|
-
const dir =
|
|
4832
|
+
const dir = dirname10(file.filePath);
|
|
4800
4833
|
const topDir = dir === "." ? "." : dir.split("/")[0];
|
|
4801
4834
|
if (!dirMap.has(topDir)) {
|
|
4802
4835
|
dirMap.set(topDir, {
|
|
@@ -5439,7 +5472,7 @@ function generateRecommendations(graph) {
|
|
|
5439
5472
|
}
|
|
5440
5473
|
|
|
5441
5474
|
// src/docs/tests.ts
|
|
5442
|
-
import { basename as basename4, dirname as
|
|
5475
|
+
import { basename as basename4, dirname as dirname11 } from "path";
|
|
5443
5476
|
function generateTests(graph, projectRoot, version) {
|
|
5444
5477
|
let output = "";
|
|
5445
5478
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -5468,7 +5501,7 @@ function getFileCount8(graph) {
|
|
|
5468
5501
|
}
|
|
5469
5502
|
function isTestFile(filePath) {
|
|
5470
5503
|
const fileName = basename4(filePath).toLowerCase();
|
|
5471
|
-
const dirPath =
|
|
5504
|
+
const dirPath = dirname11(filePath).toLowerCase();
|
|
5472
5505
|
if (dirPath.includes("test") || dirPath.includes("spec") || dirPath.includes("__tests__")) {
|
|
5473
5506
|
return true;
|
|
5474
5507
|
}
|
|
@@ -5527,12 +5560,12 @@ function generateTestFileInventory(graph) {
|
|
|
5527
5560
|
}
|
|
5528
5561
|
function matchTestToSource(testFile) {
|
|
5529
5562
|
const testFileName = basename4(testFile);
|
|
5530
|
-
const testDir =
|
|
5563
|
+
const testDir = dirname11(testFile);
|
|
5531
5564
|
let sourceFileName = testFileName.replace(/\.test\./g, ".").replace(/\.spec\./g, ".").replace(/_test\./g, ".").replace(/_spec\./g, ".");
|
|
5532
5565
|
const possiblePaths = [];
|
|
5533
5566
|
possiblePaths.push(testDir + "/" + sourceFileName);
|
|
5534
5567
|
if (testDir.endsWith("/test") || testDir.endsWith("/tests") || testDir.endsWith("/__tests__")) {
|
|
5535
|
-
const parentDir =
|
|
5568
|
+
const parentDir = dirname11(testDir);
|
|
5536
5569
|
possiblePaths.push(parentDir + "/" + sourceFileName);
|
|
5537
5570
|
}
|
|
5538
5571
|
if (testDir.includes("test")) {
|
|
@@ -5729,7 +5762,7 @@ function generateTestStatistics(graph) {
|
|
|
5729
5762
|
`;
|
|
5730
5763
|
const dirTestCoverage = /* @__PURE__ */ new Map();
|
|
5731
5764
|
for (const sourceFile of sourceFiles) {
|
|
5732
|
-
const dir =
|
|
5765
|
+
const dir = dirname11(sourceFile).split("/")[0];
|
|
5733
5766
|
if (!dirTestCoverage.has(dir)) {
|
|
5734
5767
|
dirTestCoverage.set(dir, { total: 0, tested: 0 });
|
|
5735
5768
|
}
|
|
@@ -5752,7 +5785,7 @@ function generateTestStatistics(graph) {
|
|
|
5752
5785
|
}
|
|
5753
5786
|
|
|
5754
5787
|
// src/docs/history.ts
|
|
5755
|
-
import { dirname as
|
|
5788
|
+
import { dirname as dirname12 } from "path";
|
|
5756
5789
|
import { execSync } from "child_process";
|
|
5757
5790
|
function generateHistory(graph, projectRoot, version) {
|
|
5758
5791
|
let output = "";
|
|
@@ -6033,7 +6066,7 @@ function generateFeatureClusters(graph) {
|
|
|
6033
6066
|
const dirFiles = /* @__PURE__ */ new Map();
|
|
6034
6067
|
const fileEdges = /* @__PURE__ */ new Map();
|
|
6035
6068
|
graph.forEachNode((node, attrs) => {
|
|
6036
|
-
const dir =
|
|
6069
|
+
const dir = dirname12(attrs.filePath);
|
|
6037
6070
|
if (!dirFiles.has(dir)) {
|
|
6038
6071
|
dirFiles.set(dir, /* @__PURE__ */ new Set());
|
|
6039
6072
|
}
|
|
@@ -6115,7 +6148,7 @@ function capitalizeFirst3(str) {
|
|
|
6115
6148
|
}
|
|
6116
6149
|
|
|
6117
6150
|
// src/docs/current.ts
|
|
6118
|
-
import { dirname as
|
|
6151
|
+
import { dirname as dirname13 } from "path";
|
|
6119
6152
|
function generateCurrent(graph, projectRoot, version) {
|
|
6120
6153
|
let output = "";
|
|
6121
6154
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -6253,7 +6286,7 @@ function generateCompleteFileIndex(graph) {
|
|
|
6253
6286
|
fileInfos.sort((a, b) => a.filePath.localeCompare(b.filePath));
|
|
6254
6287
|
const dirGroups = /* @__PURE__ */ new Map();
|
|
6255
6288
|
for (const info of fileInfos) {
|
|
6256
|
-
const dir =
|
|
6289
|
+
const dir = dirname13(info.filePath);
|
|
6257
6290
|
const topDir = dir === "." ? "root" : dir.split("/")[0];
|
|
6258
6291
|
if (!dirGroups.has(topDir)) {
|
|
6259
6292
|
dirGroups.set(topDir, []);
|
|
@@ -7033,7 +7066,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
7033
7066
|
const errors = [];
|
|
7034
7067
|
try {
|
|
7035
7068
|
if (!existsSync9(options.outputDir)) {
|
|
7036
|
-
|
|
7069
|
+
mkdirSync2(options.outputDir, { recursive: true });
|
|
7037
7070
|
if (options.verbose) {
|
|
7038
7071
|
console.log(`Created output directory: ${options.outputDir}`);
|
|
7039
7072
|
}
|
|
@@ -7239,7 +7272,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
7239
7272
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7240
7273
|
|
|
7241
7274
|
// src/mcp/tools.ts
|
|
7242
|
-
import { dirname as
|
|
7275
|
+
import { dirname as dirname14, join as join14 } from "path";
|
|
7243
7276
|
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
|
|
7244
7277
|
|
|
7245
7278
|
// src/mcp/connect.ts
|
|
@@ -8004,7 +8037,7 @@ function handleGetArchitectureSummary(graph) {
|
|
|
8004
8037
|
const dirMap = /* @__PURE__ */ new Map();
|
|
8005
8038
|
const languageBreakdown = {};
|
|
8006
8039
|
fileSummary.forEach((f) => {
|
|
8007
|
-
const dir = f.filePath.includes("/") ?
|
|
8040
|
+
const dir = f.filePath.includes("/") ? dirname14(f.filePath) : ".";
|
|
8008
8041
|
if (!dirMap.has(dir)) {
|
|
8009
8042
|
dirMap.set(dir, { fileCount: 0, symbolCount: 0 });
|
|
8010
8043
|
}
|
|
@@ -8237,6 +8270,7 @@ async function startMcpServer(state) {
|
|
|
8237
8270
|
}
|
|
8238
8271
|
|
|
8239
8272
|
export {
|
|
8273
|
+
findProjectRoot,
|
|
8240
8274
|
parseProject,
|
|
8241
8275
|
buildGraph,
|
|
8242
8276
|
getImpact,
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
buildGraph,
|
|
4
4
|
calculateHealthScore,
|
|
5
5
|
createEmptyState,
|
|
6
|
+
findProjectRoot,
|
|
6
7
|
generateDocs,
|
|
7
8
|
getArchitectureSummary,
|
|
8
9
|
getHealthTrend,
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
startVizServer,
|
|
15
16
|
updateFileInGraph,
|
|
16
17
|
watchProject
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-VNUOE5VC.js";
|
|
18
19
|
|
|
19
20
|
// src/index.ts
|
|
20
21
|
import { Command } from "commander";
|
|
@@ -212,10 +213,10 @@ var packageJsonPath = join(__dirname, "../package.json");
|
|
|
212
213
|
var packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
213
214
|
var program = new Command();
|
|
214
215
|
program.name("depwire").description("Code cross-reference graph builder for TypeScript projects").version(packageJson.version);
|
|
215
|
-
program.command("parse").description("Parse a TypeScript project and build dependency graph").argument("
|
|
216
|
+
program.command("parse").description("Parse a TypeScript project and build dependency graph").argument("[directory]", "Project directory to parse (defaults to current directory or auto-detected project root)").option("-o, --output <path>", "Output JSON file path", "depwire-output.json").option("--pretty", "Pretty-print JSON output").option("--stats", "Print summary statistics").option("--exclude <patterns...>", 'Glob patterns to exclude (e.g., "**/*.test.*" "dist/**")').option("--verbose", "Show detailed parsing progress").action(async (directory, options) => {
|
|
216
217
|
const startTime = Date.now();
|
|
217
218
|
try {
|
|
218
|
-
const projectRoot = resolve(directory);
|
|
219
|
+
const projectRoot = directory ? resolve(directory) : findProjectRoot();
|
|
219
220
|
console.log(`Parsing project: ${projectRoot}`);
|
|
220
221
|
const parsedFiles = await parseProject(projectRoot, {
|
|
221
222
|
exclude: options.exclude,
|
|
@@ -299,9 +300,9 @@ Total Transitive Dependents: ${impact.transitiveDependents.length}`);
|
|
|
299
300
|
process.exit(1);
|
|
300
301
|
}
|
|
301
302
|
});
|
|
302
|
-
program.command("viz").description("Launch interactive arc diagram visualization").argument("
|
|
303
|
+
program.command("viz").description("Launch interactive arc diagram visualization").argument("[directory]", "Project directory to visualize (defaults to current directory or auto-detected project root)").option("-p, --port <number>", "Server port", "3333").option("--no-open", "Don't auto-open browser").option("--exclude <patterns...>", 'Glob patterns to exclude (e.g., "**/*.test.*" "dist/**")').option("--verbose", "Show detailed parsing progress").action(async (directory, options) => {
|
|
303
304
|
try {
|
|
304
|
-
const projectRoot = resolve(directory);
|
|
305
|
+
const projectRoot = directory ? resolve(directory) : findProjectRoot();
|
|
305
306
|
console.log(`Parsing project: ${projectRoot}`);
|
|
306
307
|
const parsedFiles = await parseProject(projectRoot, {
|
|
307
308
|
exclude: options.exclude,
|
|
@@ -321,25 +322,34 @@ program.command("viz").description("Launch interactive arc diagram visualization
|
|
|
321
322
|
process.exit(1);
|
|
322
323
|
}
|
|
323
324
|
});
|
|
324
|
-
program.command("mcp").description("Start MCP server for AI coding tools").argument("[directory]", "Project directory to analyze (optional - use connect_repo tool to connect later)").action(async (directory) => {
|
|
325
|
+
program.command("mcp").description("Start MCP server for AI coding tools").argument("[directory]", "Project directory to analyze (optional - auto-detects project root or use connect_repo tool to connect later)").action(async (directory) => {
|
|
325
326
|
try {
|
|
326
327
|
const state = createEmptyState();
|
|
328
|
+
let projectRootToConnect = null;
|
|
327
329
|
if (directory) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const
|
|
330
|
+
projectRootToConnect = resolve(directory);
|
|
331
|
+
} else {
|
|
332
|
+
const detectedRoot = findProjectRoot();
|
|
333
|
+
const cwd = process.cwd();
|
|
334
|
+
if (detectedRoot !== cwd || existsSync(join(cwd, "package.json")) || existsSync(join(cwd, "tsconfig.json")) || existsSync(join(cwd, "go.mod")) || existsSync(join(cwd, "pyproject.toml")) || existsSync(join(cwd, "setup.py")) || existsSync(join(cwd, ".git"))) {
|
|
335
|
+
projectRootToConnect = detectedRoot;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (projectRootToConnect) {
|
|
339
|
+
console.error(`Parsing project: ${projectRootToConnect}`);
|
|
340
|
+
const parsedFiles = await parseProject(projectRootToConnect);
|
|
331
341
|
console.error(`Parsed ${parsedFiles.length} files`);
|
|
332
342
|
const graph = buildGraph(parsedFiles);
|
|
333
343
|
console.error(`Built graph: ${graph.order} symbols, ${graph.size} edges`);
|
|
334
344
|
state.graph = graph;
|
|
335
|
-
state.projectRoot =
|
|
336
|
-
state.projectName =
|
|
345
|
+
state.projectRoot = projectRootToConnect;
|
|
346
|
+
state.projectName = projectRootToConnect.split("/").pop() || "project";
|
|
337
347
|
console.error("Starting file watcher...");
|
|
338
|
-
state.watcher = watchProject(
|
|
348
|
+
state.watcher = watchProject(projectRootToConnect, {
|
|
339
349
|
onFileChanged: async (filePath) => {
|
|
340
350
|
console.error(`File changed: ${filePath}`);
|
|
341
351
|
try {
|
|
342
|
-
await updateFileInGraph(state.graph,
|
|
352
|
+
await updateFileInGraph(state.graph, projectRootToConnect, filePath);
|
|
343
353
|
console.error(`Graph updated for ${filePath}`);
|
|
344
354
|
} catch (error) {
|
|
345
355
|
console.error(`Failed to update graph: ${error}`);
|
|
@@ -348,7 +358,7 @@ program.command("mcp").description("Start MCP server for AI coding tools").argum
|
|
|
348
358
|
onFileAdded: async (filePath) => {
|
|
349
359
|
console.error(`File added: ${filePath}`);
|
|
350
360
|
try {
|
|
351
|
-
await updateFileInGraph(state.graph,
|
|
361
|
+
await updateFileInGraph(state.graph, projectRootToConnect, filePath);
|
|
352
362
|
console.error(`Graph updated for ${filePath}`);
|
|
353
363
|
} catch (error) {
|
|
354
364
|
console.error(`Failed to update graph: ${error}`);
|
|
@@ -374,10 +384,10 @@ program.command("mcp").description("Start MCP server for AI coding tools").argum
|
|
|
374
384
|
process.exit(1);
|
|
375
385
|
}
|
|
376
386
|
});
|
|
377
|
-
program.command("docs").description("Generate comprehensive codebase documentation").argument("
|
|
387
|
+
program.command("docs").description("Generate comprehensive codebase documentation").argument("[directory]", "Project directory to document (defaults to current directory or auto-detected project root)").option("-o, --output <path>", "Output directory (default: .depwire/ inside project)").option("--format <type>", "Output format: markdown | json", "markdown").option("--gitignore", "Add .depwire/ to .gitignore automatically").option("--no-gitignore", "Don't modify .gitignore").option("--include <docs>", "Comma-separated list of docs to generate (default: all)", "all").option("--update", "Regenerate existing docs").option("--only <docs>", "Used with --update, regenerate only specific docs").option("--verbose", "Show generation progress").option("--stats", "Show generation statistics at the end").option("--exclude <patterns...>", 'Glob patterns to exclude (e.g., "**/*.test.*" "dist/**")').action(async (directory, options) => {
|
|
378
388
|
const startTime = Date.now();
|
|
379
389
|
try {
|
|
380
|
-
const projectRoot = resolve(directory);
|
|
390
|
+
const projectRoot = directory ? resolve(directory) : findProjectRoot();
|
|
381
391
|
const outputDir = options.output ? resolve(options.output) : join(projectRoot, ".depwire");
|
|
382
392
|
const includeList = options.include.split(",").map((s) => s.trim());
|
|
383
393
|
const onlyList = options.only ? options.only.split(",").map((s) => s.trim()) : void 0;
|
|
@@ -471,9 +481,9 @@ ${pattern}
|
|
|
471
481
|
console.error(`Warning: Failed to update .gitignore: ${err}`);
|
|
472
482
|
}
|
|
473
483
|
}
|
|
474
|
-
program.command("health
|
|
484
|
+
program.command("health").description("Analyze dependency architecture health (0-100 score)").argument("[directory]", "Project directory to analyze (defaults to current directory or auto-detected project root)").option("--json", "Output as JSON").option("--verbose", "Show detailed breakdown").action(async (directory, options) => {
|
|
475
485
|
try {
|
|
476
|
-
const projectRoot = resolve(
|
|
486
|
+
const projectRoot = directory ? resolve(directory) : findProjectRoot();
|
|
477
487
|
const startTime = Date.now();
|
|
478
488
|
const parsedFiles = await parseProject(projectRoot);
|
|
479
489
|
const graph = buildGraph(parsedFiles);
|
package/dist/mcpb-entry.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "depwire-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Code cross-reference visualization and AI context engine for TypeScript, JavaScript, Python, and Go. Zero native dependencies — works on Windows, macOS, and Linux.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|