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 CHANGED
@@ -61,26 +61,30 @@ npx depwire-cli --help
61
61
  ### CLI Usage
62
62
 
63
63
  ```bash
64
- # Visualization (opens in browser)
65
- npx depwire-cli viz ./my-project
64
+ # Auto-detects project root from current directory
65
+ depwire viz
66
+ depwire parse
67
+ depwire docs
68
+ depwire health
66
69
 
67
- # Parse and export as JSON
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 ./my-project --exclude "**/*.test.*" "**/node_modules/**"
75
+ npx depwire-cli parse --exclude "**/*.test.*" "**/node_modules/**"
72
76
 
73
77
  # Show detailed parsing progress
74
- npx depwire-cli parse ./my-project --verbose
78
+ npx depwire-cli parse --verbose
75
79
 
76
80
  # Export with pretty-printed JSON and statistics
77
- npx depwire-cli parse ./my-project --pretty --stats
81
+ npx depwire-cli parse --pretty --stats
78
82
 
79
83
  # Generate codebase documentation
80
- npx depwire-cli docs ./my-project --verbose --stats
84
+ npx depwire-cli docs --verbose --stats
81
85
 
82
86
  # Custom output file
83
- npx depwire-cli parse ./my-project -o my-graph.json
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
- Connect to /path/to/my/project and show me the architecture.
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 /path/to/project`
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
  ![Interactive Arc Diagram](./assets/graph.gif)
144
152
 
145
153
  ```bash
146
- # Open visualization on default port (3456)
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 ./my-project --port 8080
161
+ depwire viz --port 8080
151
162
 
152
163
  # Exclude test files from visualization
153
- depwire viz ./my-project --exclude "**/*.test.*"
164
+ depwire viz --exclude "**/*.test.*"
154
165
 
155
166
  # Verbose mode with detailed parsing logs
156
- depwire viz ./my-project --verbose
167
+ depwire viz --verbose
157
168
 
158
169
  # Don't auto-open browser
159
- depwire viz ./my-project --no-open
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 <directory>`
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
- # Basic parse
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 ./src --exclude "**/*.test.*" "**/*.spec.*" "dist/**" "build/**"
213
+ depwire parse --exclude "**/*.test.*" "**/*.spec.*" "dist/**" "build/**"
198
214
 
199
215
  # Full verbosity with stats
200
- depwire parse ./src --verbose --stats --pretty -o graph.json
216
+ depwire parse --verbose --stats --pretty -o graph.json
201
217
  ```
202
218
 
203
- ### `depwire viz <directory>`
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
- # Basic visualization
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 ./src --port 8080 --no-open
240
+ depwire viz --port 8080 --no-open
220
241
 
221
242
  # Exclude test files with verbose logging
222
- depwire viz ./src --exclude "**/*.test.*" --verbose
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
- # Start MCP server on current directory
254
+ # Auto-detect and connect (recommended)
232
255
  depwire mcp
233
256
 
234
- # Start on specific project
257
+ # Explicit directory
235
258
  depwire mcp /path/to/project
236
259
  ```
237
260
 
238
- ### `depwire docs <directory>`
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
- # Generate all docs (outputs to .depwire/ by default)
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 ./my-project --verbose --stats
288
+ depwire docs --verbose --stats
261
289
 
262
290
  # Regenerate existing docs
263
- depwire docs ./my-project --update
291
+ depwire docs --update
264
292
 
265
293
  # Generate specific docs only
266
- depwire docs ./my-project --include architecture,dependencies
294
+ depwire docs --include architecture,dependencies
267
295
 
268
296
  # Custom output directory
269
- depwire docs ./my-project --output ./docs
297
+ depwire docs --output ./docs
270
298
 
271
299
  # Regenerate only conventions doc
272
- depwire docs ./my-project --update --only conventions
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 <directory>`
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
- # Analyze current directory
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 . --verbose
349
+ depwire health --verbose
317
350
 
318
351
  # JSON output for CI
319
- depwire health . --json
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
- "Connect to /Users/me/my-app and analyze the impact of renaming UserService to UserRepository"
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
- "Connect to https://github.com/t3-oss/create-t3-app and give me an architecture summary"
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 . --open
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 dirname7 } from "path";
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 = dirname7(attrs.filePath);
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 = dirname7(attrs.filePath);
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 = dirname7(sourceAttrs.filePath);
3326
- const targetDir = dirname7(targetAttrs.filePath);
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 dirname8 } from "path";
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 = dirname8(attrs.filePath);
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 = dirname8(attrs.filePath);
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 = dirname8(attrs.filePath);
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 = dirname8(sourceAttrs.filePath);
4501
- const targetDir = dirname8(targetAttrs.filePath);
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 = dirname8(attrs.filePath);
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 = dirname8(files[0]);
4637
- if (files.every((f) => dirname8(f) === commonDir)) {
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 dirname9, basename as basename3 } from "path";
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 = dirname9(file.filePath);
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 dirname10 } from "path";
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 = dirname10(filePath).toLowerCase();
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 = dirname10(testFile);
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 = dirname10(testDir);
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 = dirname10(sourceFile).split("/")[0];
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 dirname11 } from "path";
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 = dirname11(attrs.filePath);
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 dirname12 } from "path";
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 = dirname12(info.filePath);
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
- mkdirSync(options.outputDir, { recursive: true });
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 dirname13, join as join14 } from "path";
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("/") ? dirname13(f.filePath) : ".";
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-AFJFZXCI.js";
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("<directory>", "Project directory to parse").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
+ 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("<directory>", "Project directory to visualize").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
+ 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
- const projectRoot = resolve(directory);
329
- console.error(`Parsing project: ${projectRoot}`);
330
- const parsedFiles = await parseProject(projectRoot);
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 = projectRoot;
336
- state.projectName = projectRoot.split("/").pop() || "project";
345
+ state.projectRoot = projectRootToConnect;
346
+ state.projectName = projectRootToConnect.split("/").pop() || "project";
337
347
  console.error("Starting file watcher...");
338
- state.watcher = watchProject(projectRoot, {
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, projectRoot, filePath);
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, projectRoot, filePath);
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("<directory>", "Project directory to document").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) => {
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 <dir>").description("Analyze dependency architecture health (0-100 score)").option("--json", "Output as JSON").option("--verbose", "Show detailed breakdown").action(async (dir, options) => {
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(dir);
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);
@@ -6,7 +6,7 @@ import {
6
6
  startMcpServer,
7
7
  updateFileInGraph,
8
8
  watchProject
9
- } from "./chunk-AFJFZXCI.js";
9
+ } from "./chunk-VNUOE5VC.js";
10
10
 
11
11
  // src/mcpb-entry.ts
12
12
  import { resolve } from "path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "depwire-cli",
3
- "version": "0.6.0",
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": {