depwire-cli 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +65 -21
- package/README.md +120 -10
- package/dist/{chunk-2XOJSBSD.js → chunk-V3SB37U3.js} +92 -21
- package/dist/index.js +22 -8
- package/dist/mcpb-entry.js +1 -1
- package/package.json +6 -5
package/LICENSE
CHANGED
|
@@ -1,21 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
Parameters
|
|
4
|
+
|
|
5
|
+
Licensor: ATEF ATAYA LLC
|
|
6
|
+
Licensed Work: Depwire
|
|
7
|
+
The Licensed Work is (c) 2026 ATEF ATAYA LLC
|
|
8
|
+
Additional Use Grant: You may make use of the Licensed Work, provided that
|
|
9
|
+
you may not use the Licensed Work for a Code Analysis
|
|
10
|
+
Service. A "Code Analysis Service" is a commercial
|
|
11
|
+
offering that allows third parties to access the
|
|
12
|
+
functionality of the Licensed Work by providing
|
|
13
|
+
dependency graph analysis, code visualization, or
|
|
14
|
+
MCP-based code context as a managed service.
|
|
15
|
+
|
|
16
|
+
Change Date: February 25, 2029
|
|
17
|
+
|
|
18
|
+
Change License: Apache License, Version 2.0
|
|
19
|
+
|
|
20
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
21
|
+
please contact: atef@depwire.dev
|
|
22
|
+
|
|
23
|
+
Notice
|
|
24
|
+
|
|
25
|
+
Business Source License 1.1
|
|
26
|
+
|
|
27
|
+
Terms
|
|
28
|
+
|
|
29
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
30
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
31
|
+
Licensor may make an Additional Use Grant, above, permitting limited production
|
|
32
|
+
use.
|
|
33
|
+
|
|
34
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
35
|
+
available distribution of a specific version of the Licensed Work, whichever
|
|
36
|
+
comes first, the Licensor hereby grants you rights under the terms of the
|
|
37
|
+
Change License, and the rights granted in the paragraph above terminate.
|
|
38
|
+
|
|
39
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
40
|
+
currently in effect as described in this License, you must purchase a
|
|
41
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
42
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
43
|
+
|
|
44
|
+
All copies of the original and modified Licensed Work, and derivative works of
|
|
45
|
+
the Licensed Work, are subject to this License. This License applies separately
|
|
46
|
+
for each version of the Licensed Work and the Change Date may vary for each
|
|
47
|
+
version of the Licensed Work released by Licensor.
|
|
48
|
+
|
|
49
|
+
You must conspicuously display this License on each original or modified copy
|
|
50
|
+
of the Licensed Work. If you receive the Licensed Work in original or modified
|
|
51
|
+
form from a third party, the terms and conditions set forth in this License
|
|
52
|
+
apply to your use of that work.
|
|
53
|
+
|
|
54
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
55
|
+
terminate your rights under this License for the current and all other versions
|
|
56
|
+
of the Licensed Work.
|
|
57
|
+
|
|
58
|
+
This License does not grant you any right in any trademark or logo of Licensor
|
|
59
|
+
or its affiliates (provided that you may use a trademark or logo of Licensor
|
|
60
|
+
as expressly required by this License).
|
|
61
|
+
|
|
62
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN
|
|
63
|
+
"AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS
|
|
64
|
+
OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY,
|
|
65
|
+
FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE.
|
package/README.md
CHANGED
|
@@ -15,8 +15,23 @@ Depwire analyzes codebases to build a cross-reference graph showing how every fi
|
|
|
15
15
|
### CLI Usage
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
|
|
19
|
-
npx depwire
|
|
18
|
+
# Visualization (opens in browser)
|
|
19
|
+
npx depwire-cli viz ./my-project
|
|
20
|
+
|
|
21
|
+
# Parse and export as JSON
|
|
22
|
+
npx depwire-cli parse ./my-project
|
|
23
|
+
|
|
24
|
+
# Exclude test files and node_modules
|
|
25
|
+
npx depwire-cli parse ./my-project --exclude "**/*.test.*" "**/node_modules/**"
|
|
26
|
+
|
|
27
|
+
# Show detailed parsing progress
|
|
28
|
+
npx depwire-cli parse ./my-project --verbose
|
|
29
|
+
|
|
30
|
+
# Export with pretty-printed JSON and statistics
|
|
31
|
+
npx depwire-cli parse ./my-project --pretty --stats
|
|
32
|
+
|
|
33
|
+
# Custom output file
|
|
34
|
+
npx depwire-cli parse ./my-project -o my-graph.json
|
|
20
35
|
```
|
|
21
36
|
|
|
22
37
|
### Claude Desktop
|
|
@@ -28,7 +43,7 @@ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_
|
|
|
28
43
|
"mcpServers": {
|
|
29
44
|
"depwire": {
|
|
30
45
|
"command": "npx",
|
|
31
|
-
"args": ["-y", "depwire", "mcp"]
|
|
46
|
+
"args": ["-y", "depwire-cli", "mcp"]
|
|
32
47
|
}
|
|
33
48
|
}
|
|
34
49
|
}
|
|
@@ -43,7 +58,7 @@ Connect to /path/to/my/project and show me the architecture.
|
|
|
43
58
|
|
|
44
59
|
Settings → Features → Experimental → Enable MCP → Add Server:
|
|
45
60
|
- Command: `npx`
|
|
46
|
-
- Args: `-y depwire mcp /path/to/project`
|
|
61
|
+
- Args: `-y depwire-cli mcp /path/to/project`
|
|
47
62
|
|
|
48
63
|
## Available MCP Tools
|
|
49
64
|
|
|
@@ -58,6 +73,7 @@ Settings → Features → Experimental → Enable MCP → Add Server:
|
|
|
58
73
|
| `get_architecture_summary` | High-level project overview |
|
|
59
74
|
| `list_files` | List all files with stats |
|
|
60
75
|
| `get_symbol_info` | Look up any symbol's details |
|
|
76
|
+
| `visualize_graph` | Generate interactive arc diagram visualization |
|
|
61
77
|
|
|
62
78
|
## Supported Languages
|
|
63
79
|
|
|
@@ -71,7 +87,20 @@ Settings → Features → Experimental → Enable MCP → Add Server:
|
|
|
71
87
|
## Visualization
|
|
72
88
|
|
|
73
89
|
```bash
|
|
90
|
+
# Open visualization on default port (3456)
|
|
74
91
|
depwire viz ./my-project
|
|
92
|
+
|
|
93
|
+
# Custom port
|
|
94
|
+
depwire viz ./my-project --port 8080
|
|
95
|
+
|
|
96
|
+
# Exclude test files from visualization
|
|
97
|
+
depwire viz ./my-project --exclude "**/*.test.*"
|
|
98
|
+
|
|
99
|
+
# Verbose mode with detailed parsing logs
|
|
100
|
+
depwire viz ./my-project --verbose
|
|
101
|
+
|
|
102
|
+
# Don't auto-open browser
|
|
103
|
+
depwire viz ./my-project --no-open
|
|
75
104
|
```
|
|
76
105
|
|
|
77
106
|
Opens an interactive arc diagram in your browser:
|
|
@@ -79,8 +108,9 @@ Opens an interactive arc diagram in your browser:
|
|
|
79
108
|
- Hover to explore connections
|
|
80
109
|
- Click to filter by file
|
|
81
110
|
- Search by filename
|
|
82
|
-
- Live refresh when files change
|
|
111
|
+
- **Live refresh when files change** — Edit code and see the graph update in real-time
|
|
83
112
|
- Export as SVG or PNG
|
|
113
|
+
- **Port collision handling** — Automatically finds an available port if default is in use
|
|
84
114
|
|
|
85
115
|
## How It Works
|
|
86
116
|
|
|
@@ -92,14 +122,83 @@ Opens an interactive arc diagram in your browser:
|
|
|
92
122
|
## Installation
|
|
93
123
|
|
|
94
124
|
```bash
|
|
95
|
-
npm install -g depwire
|
|
125
|
+
npm install -g depwire-cli
|
|
96
126
|
```
|
|
97
127
|
|
|
98
128
|
Or use directly with `npx`:
|
|
99
129
|
```bash
|
|
100
|
-
npx depwire --help
|
|
130
|
+
npx depwire-cli --help
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## CLI Reference
|
|
134
|
+
|
|
135
|
+
### `depwire parse <directory>`
|
|
136
|
+
|
|
137
|
+
Parse a project and export the dependency graph as JSON.
|
|
138
|
+
|
|
139
|
+
**Options:**
|
|
140
|
+
- `-o, --output <path>` — Output file path (default: `depwire-output.json`)
|
|
141
|
+
- `--exclude <patterns...>` — Glob patterns to exclude (e.g., `"**/*.test.*" "dist/**"`)
|
|
142
|
+
- `--verbose` — Show detailed parsing progress (logs each file as it's parsed)
|
|
143
|
+
- `--pretty` — Pretty-print JSON output with indentation
|
|
144
|
+
- `--stats` — Print summary statistics (file count, symbol count, edges, timing)
|
|
145
|
+
|
|
146
|
+
**Examples:**
|
|
147
|
+
```bash
|
|
148
|
+
# Basic parse
|
|
149
|
+
depwire parse ./src
|
|
150
|
+
|
|
151
|
+
# Exclude test files and build outputs
|
|
152
|
+
depwire parse ./src --exclude "**/*.test.*" "**/*.spec.*" "dist/**" "build/**"
|
|
153
|
+
|
|
154
|
+
# Full verbosity with stats
|
|
155
|
+
depwire parse ./src --verbose --stats --pretty -o graph.json
|
|
101
156
|
```
|
|
102
157
|
|
|
158
|
+
### `depwire viz <directory>`
|
|
159
|
+
|
|
160
|
+
Start visualization server and open arc diagram in browser.
|
|
161
|
+
|
|
162
|
+
**Options:**
|
|
163
|
+
- `--port <number>` — Port number (default: 3456, auto-increments if in use)
|
|
164
|
+
- `--exclude <patterns...>` — Glob patterns to exclude
|
|
165
|
+
- `--verbose` — Show detailed parsing progress
|
|
166
|
+
- `--no-open` — Don't automatically open browser
|
|
167
|
+
|
|
168
|
+
**Examples:**
|
|
169
|
+
```bash
|
|
170
|
+
# Basic visualization
|
|
171
|
+
depwire viz ./src
|
|
172
|
+
|
|
173
|
+
# Custom port without auto-open
|
|
174
|
+
depwire viz ./src --port 8080 --no-open
|
|
175
|
+
|
|
176
|
+
# Exclude test files with verbose logging
|
|
177
|
+
depwire viz ./src --exclude "**/*.test.*" --verbose
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### `depwire mcp [directory]`
|
|
181
|
+
|
|
182
|
+
Start MCP server for AI tool integration (Cursor, Claude Desktop).
|
|
183
|
+
|
|
184
|
+
**Examples:**
|
|
185
|
+
```bash
|
|
186
|
+
# Start MCP server on current directory
|
|
187
|
+
depwire mcp
|
|
188
|
+
|
|
189
|
+
# Start on specific project
|
|
190
|
+
depwire mcp /path/to/project
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Error Handling
|
|
194
|
+
|
|
195
|
+
Depwire gracefully handles parse errors:
|
|
196
|
+
- **Malformed files** — Skipped with warning, parsing continues
|
|
197
|
+
- **Large files** — Files over 1MB are automatically skipped
|
|
198
|
+
- **Port collisions** — Auto-increments to next available port (3456 → 3457 → 3458...)
|
|
199
|
+
- **Protected paths** — Blocks access to sensitive directories (.ssh, .aws, /etc)
|
|
200
|
+
|
|
201
|
+
|
|
103
202
|
## Example Workflows
|
|
104
203
|
|
|
105
204
|
### Refactoring with AI
|
|
@@ -158,20 +257,31 @@ See [SECURITY.md](SECURITY.md) for full details.
|
|
|
158
257
|
|
|
159
258
|
## Contributing
|
|
160
259
|
|
|
161
|
-
Contributions welcome!
|
|
260
|
+
Contributions welcome! Please note:
|
|
162
261
|
|
|
163
262
|
1. Fork the repository
|
|
164
263
|
2. Create a feature branch
|
|
165
264
|
3. Add tests for new functionality
|
|
166
265
|
4. Submit a pull request
|
|
266
|
+
5. Sign the CLA (handled automatically on your first PR)
|
|
267
|
+
|
|
268
|
+
All contributors must sign the Contributor License Agreement before their PR can be merged.
|
|
167
269
|
|
|
168
270
|
## License
|
|
169
271
|
|
|
170
|
-
|
|
272
|
+
Depwire is licensed under the [Business Source License 1.1](LICENSE).
|
|
273
|
+
|
|
274
|
+
- **Use it freely** for personal projects, internal company use, and development
|
|
275
|
+
- **Cannot** be offered as a hosted/managed service to third parties
|
|
276
|
+
- **Converts** to Apache 2.0 on February 25, 2029
|
|
277
|
+
|
|
278
|
+
For commercial licensing inquiries: atef@depwire.dev
|
|
171
279
|
|
|
172
280
|
## Credits
|
|
173
281
|
|
|
174
|
-
Built
|
|
282
|
+
Built by [ATEF ATAYA LLC](https://depwire.dev)
|
|
283
|
+
|
|
284
|
+
Powered by:
|
|
175
285
|
- [tree-sitter](https://tree-sitter.github.io/tree-sitter/) — Fast, reliable parsing
|
|
176
286
|
- [graphology](https://graphology.github.io/) — Powerful graph data structure
|
|
177
287
|
- [D3.js](https://d3js.org/) — Data visualization
|
|
@@ -158,7 +158,7 @@ var tsxParser = new Parser();
|
|
|
158
158
|
tsxParser.setLanguage(TypeScript.tsx);
|
|
159
159
|
function parseTypeScriptFile(filePath, sourceCode, projectRoot) {
|
|
160
160
|
const parser2 = filePath.endsWith(".tsx") ? tsxParser : tsParser;
|
|
161
|
-
const tree = parser2.parse(sourceCode);
|
|
161
|
+
const tree = parser2.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
162
162
|
const context = {
|
|
163
163
|
filePath,
|
|
164
164
|
projectRoot,
|
|
@@ -620,7 +620,7 @@ import { existsSync as existsSync2 } from "fs";
|
|
|
620
620
|
var pyParser = new Parser2();
|
|
621
621
|
pyParser.setLanguage(Python);
|
|
622
622
|
function parsePythonFile(filePath, sourceCode, projectRoot) {
|
|
623
|
-
const tree = pyParser.parse(sourceCode);
|
|
623
|
+
const tree = pyParser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
624
624
|
const context = {
|
|
625
625
|
filePath,
|
|
626
626
|
projectRoot,
|
|
@@ -989,7 +989,7 @@ import { join as join4, dirname as dirname3, extname as extname2 } from "path";
|
|
|
989
989
|
var jsParser = new Parser3();
|
|
990
990
|
jsParser.setLanguage(JavaScript);
|
|
991
991
|
function parseJavaScriptFile(filePath, sourceCode, projectRoot) {
|
|
992
|
-
const tree = jsParser.parse(sourceCode);
|
|
992
|
+
const tree = jsParser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
993
993
|
const context = {
|
|
994
994
|
filePath,
|
|
995
995
|
projectRoot,
|
|
@@ -1493,7 +1493,7 @@ import { join as join5, dirname as dirname4 } from "path";
|
|
|
1493
1493
|
var parser = new Parser4();
|
|
1494
1494
|
parser.setLanguage(Go);
|
|
1495
1495
|
function parseGoFile(filePath, sourceCode, projectRoot) {
|
|
1496
|
-
const tree = parser.parse(sourceCode);
|
|
1496
|
+
const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
1497
1497
|
const moduleName = readGoModuleName(projectRoot);
|
|
1498
1498
|
const context = {
|
|
1499
1499
|
filePath,
|
|
@@ -1924,6 +1924,7 @@ function getParserForFile(filePath) {
|
|
|
1924
1924
|
}
|
|
1925
1925
|
|
|
1926
1926
|
// src/parser/index.ts
|
|
1927
|
+
import { minimatch } from "minimatch";
|
|
1927
1928
|
var MAX_FILE_SIZE = 1e6;
|
|
1928
1929
|
function shouldParseFile(fullPath) {
|
|
1929
1930
|
try {
|
|
@@ -1937,27 +1938,58 @@ function shouldParseFile(fullPath) {
|
|
|
1937
1938
|
return false;
|
|
1938
1939
|
}
|
|
1939
1940
|
}
|
|
1940
|
-
function parseProject(projectRoot) {
|
|
1941
|
+
function parseProject(projectRoot, options) {
|
|
1941
1942
|
const files = scanDirectory(projectRoot);
|
|
1942
1943
|
const parsedFiles = [];
|
|
1944
|
+
let skippedFiles = 0;
|
|
1945
|
+
let errorFiles = 0;
|
|
1943
1946
|
for (const file of files) {
|
|
1944
1947
|
try {
|
|
1945
1948
|
const fullPath = join6(projectRoot, file);
|
|
1949
|
+
if (options?.exclude) {
|
|
1950
|
+
const shouldExclude = options.exclude.some(
|
|
1951
|
+
(pattern) => minimatch(file, pattern, { matchBase: true })
|
|
1952
|
+
);
|
|
1953
|
+
if (shouldExclude) {
|
|
1954
|
+
if (options.verbose) {
|
|
1955
|
+
console.error(`[Parser] Excluded: ${file}`);
|
|
1956
|
+
}
|
|
1957
|
+
skippedFiles++;
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1946
1961
|
if (!shouldParseFile(fullPath)) {
|
|
1962
|
+
skippedFiles++;
|
|
1947
1963
|
continue;
|
|
1948
1964
|
}
|
|
1965
|
+
if (options?.verbose) {
|
|
1966
|
+
console.error(`[Parser] Parsing: ${file}`);
|
|
1967
|
+
}
|
|
1949
1968
|
const parser2 = getParserForFile(file);
|
|
1950
1969
|
if (!parser2) {
|
|
1951
1970
|
console.error(`No parser found for file: ${file}`);
|
|
1971
|
+
skippedFiles++;
|
|
1952
1972
|
continue;
|
|
1953
1973
|
}
|
|
1954
1974
|
const sourceCode = readFileSync3(fullPath, "utf-8");
|
|
1955
1975
|
const parsed = parser2.parseFile(file, sourceCode, projectRoot);
|
|
1956
1976
|
parsedFiles.push(parsed);
|
|
1957
1977
|
} catch (err) {
|
|
1978
|
+
errorFiles++;
|
|
1958
1979
|
console.error(`Error parsing file ${file}:`, err instanceof Error ? err.message : err);
|
|
1959
1980
|
}
|
|
1960
1981
|
}
|
|
1982
|
+
if (options?.verbose || errorFiles > 0) {
|
|
1983
|
+
console.error(`
|
|
1984
|
+
[Parser] Summary:`);
|
|
1985
|
+
console.error(` Parsed: ${parsedFiles.length} files`);
|
|
1986
|
+
if (skippedFiles > 0) {
|
|
1987
|
+
console.error(` Skipped: ${skippedFiles} files`);
|
|
1988
|
+
}
|
|
1989
|
+
if (errorFiles > 0) {
|
|
1990
|
+
console.error(` Errors: ${errorFiles} files`);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1961
1993
|
return parsedFiles;
|
|
1962
1994
|
}
|
|
1963
1995
|
|
|
@@ -2240,7 +2272,7 @@ function prepareVizData(graph, projectRoot) {
|
|
|
2240
2272
|
import chokidar from "chokidar";
|
|
2241
2273
|
function watchProject(projectRoot, callbacks) {
|
|
2242
2274
|
console.error(`[Watcher] Creating watcher for: ${projectRoot}`);
|
|
2243
|
-
const
|
|
2275
|
+
const watcherOptions = {
|
|
2244
2276
|
ignored: [
|
|
2245
2277
|
"**/node_modules/**",
|
|
2246
2278
|
"**/vendor/**",
|
|
@@ -2251,19 +2283,36 @@ function watchProject(projectRoot, callbacks) {
|
|
|
2251
2283
|
"**/coverage/**",
|
|
2252
2284
|
"**/.next/**",
|
|
2253
2285
|
"**/.turbo/**",
|
|
2254
|
-
"
|
|
2255
|
-
//
|
|
2286
|
+
"**/.DS_Store",
|
|
2287
|
+
// macOS metadata
|
|
2288
|
+
"**/.env",
|
|
2289
|
+
// Environment files
|
|
2290
|
+
"**/.env.*",
|
|
2291
|
+
// Environment variants
|
|
2292
|
+
"**/.eslintcache",
|
|
2293
|
+
// ESLint cache
|
|
2294
|
+
"**/.vscode/**",
|
|
2295
|
+
// VS Code settings
|
|
2296
|
+
"**/.idea/**"
|
|
2297
|
+
// IntelliJ IDEA settings
|
|
2256
2298
|
],
|
|
2257
2299
|
ignoreInitial: true,
|
|
2258
2300
|
// Don't fire events for existing files
|
|
2259
2301
|
persistent: true,
|
|
2260
2302
|
followSymlinks: false,
|
|
2303
|
+
usePolling: true,
|
|
2304
|
+
// Use polling for macOS reliability
|
|
2305
|
+
interval: 1e3,
|
|
2306
|
+
// Poll every second
|
|
2307
|
+
atomic: true,
|
|
2308
|
+
// Handle atomic writes (VS Code, Sublime, etc.)
|
|
2261
2309
|
awaitWriteFinish: {
|
|
2262
2310
|
stabilityThreshold: 300,
|
|
2263
2311
|
// Wait 300ms after last change before firing
|
|
2264
2312
|
pollInterval: 100
|
|
2265
2313
|
}
|
|
2266
|
-
}
|
|
2314
|
+
};
|
|
2315
|
+
const watcher = chokidar.watch(projectRoot, watcherOptions);
|
|
2267
2316
|
console.error("[Watcher] Attaching event listeners...");
|
|
2268
2317
|
watcher.on("change", (absolutePath) => {
|
|
2269
2318
|
const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go"];
|
|
@@ -2305,9 +2354,6 @@ function watchProject(projectRoot, callbacks) {
|
|
|
2305
2354
|
}
|
|
2306
2355
|
console.error(`[Watcher] Watching ${fileCount} TypeScript/JavaScript/Python/Go files in ${dirs.length} directories`);
|
|
2307
2356
|
});
|
|
2308
|
-
watcher.on("all", (event, path) => {
|
|
2309
|
-
console.error(`[Watcher] ALL event: ${event} ${path}`);
|
|
2310
|
-
});
|
|
2311
2357
|
return watcher;
|
|
2312
2358
|
}
|
|
2313
2359
|
|
|
@@ -2320,7 +2366,31 @@ import { WebSocketServer } from "ws";
|
|
|
2320
2366
|
var __filename = fileURLToPath(import.meta.url);
|
|
2321
2367
|
var __dirname = dirname5(__filename);
|
|
2322
2368
|
var activeServer = null;
|
|
2323
|
-
function
|
|
2369
|
+
async function findAvailablePort(startPort, maxAttempts = 10) {
|
|
2370
|
+
const net = await import("net");
|
|
2371
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2372
|
+
const testPort = startPort + attempt;
|
|
2373
|
+
const isAvailable = await new Promise((resolve3) => {
|
|
2374
|
+
const server = net.createServer();
|
|
2375
|
+
server.once("error", () => {
|
|
2376
|
+
resolve3(false);
|
|
2377
|
+
});
|
|
2378
|
+
server.once("listening", () => {
|
|
2379
|
+
server.close();
|
|
2380
|
+
resolve3(true);
|
|
2381
|
+
});
|
|
2382
|
+
server.listen(testPort, "127.0.0.1");
|
|
2383
|
+
});
|
|
2384
|
+
if (isAvailable) {
|
|
2385
|
+
if (attempt > 0) {
|
|
2386
|
+
console.error(`Port ${startPort} in use, using port ${testPort} instead`);
|
|
2387
|
+
}
|
|
2388
|
+
return testPort;
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
throw new Error(`No available ports found between ${startPort} and ${startPort + maxAttempts - 1}`);
|
|
2392
|
+
}
|
|
2393
|
+
async function startVizServer(initialVizData, graph, projectRoot, port = 3333, shouldOpen = true, options) {
|
|
2324
2394
|
if (activeServer) {
|
|
2325
2395
|
console.error(`Visualization server already running at ${activeServer.url}`);
|
|
2326
2396
|
return {
|
|
@@ -2329,6 +2399,7 @@ function startVizServer(initialVizData, graph, projectRoot, port = 3333, shouldO
|
|
|
2329
2399
|
alreadyRunning: true
|
|
2330
2400
|
};
|
|
2331
2401
|
}
|
|
2402
|
+
const availablePort = await findAvailablePort(port);
|
|
2332
2403
|
const app = express();
|
|
2333
2404
|
let vizData = initialVizData;
|
|
2334
2405
|
const publicDir = join7(__dirname, "viz", "public");
|
|
@@ -2336,12 +2407,12 @@ function startVizServer(initialVizData, graph, projectRoot, port = 3333, shouldO
|
|
|
2336
2407
|
app.get("/api/graph", (req, res) => {
|
|
2337
2408
|
res.json(vizData);
|
|
2338
2409
|
});
|
|
2339
|
-
const server = app.listen(
|
|
2340
|
-
const url2 = `http://127.0.0.1:${
|
|
2410
|
+
const server = app.listen(availablePort, "127.0.0.1", () => {
|
|
2411
|
+
const url2 = `http://127.0.0.1:${availablePort}`;
|
|
2341
2412
|
console.error(`
|
|
2342
2413
|
Depwire visualization running at ${url2}`);
|
|
2343
2414
|
console.error("Press Ctrl+C to stop\n");
|
|
2344
|
-
activeServer = { server, port, url: url2 };
|
|
2415
|
+
activeServer = { server, port: availablePort, url: url2 };
|
|
2345
2416
|
if (shouldOpen) {
|
|
2346
2417
|
open(url2);
|
|
2347
2418
|
}
|
|
@@ -2365,7 +2436,7 @@ Depwire visualization running at ${url2}`);
|
|
|
2365
2436
|
onFileChanged: async (filePath) => {
|
|
2366
2437
|
console.error(`File changed: ${filePath} \u2014 re-parsing project...`);
|
|
2367
2438
|
try {
|
|
2368
|
-
const parsedFiles = parseProject(projectRoot);
|
|
2439
|
+
const parsedFiles = parseProject(projectRoot, options);
|
|
2369
2440
|
const newGraph = buildGraph(parsedFiles);
|
|
2370
2441
|
graph.clear();
|
|
2371
2442
|
newGraph.forEachNode((node, attrs) => {
|
|
@@ -2384,7 +2455,7 @@ Depwire visualization running at ${url2}`);
|
|
|
2384
2455
|
onFileAdded: async (filePath) => {
|
|
2385
2456
|
console.error(`File added: ${filePath} \u2014 re-parsing project...`);
|
|
2386
2457
|
try {
|
|
2387
|
-
const parsedFiles = parseProject(projectRoot);
|
|
2458
|
+
const parsedFiles = parseProject(projectRoot, options);
|
|
2388
2459
|
const newGraph = buildGraph(parsedFiles);
|
|
2389
2460
|
graph.clear();
|
|
2390
2461
|
newGraph.forEachNode((node, attrs) => {
|
|
@@ -2403,7 +2474,7 @@ Depwire visualization running at ${url2}`);
|
|
|
2403
2474
|
onFileDeleted: (filePath) => {
|
|
2404
2475
|
console.error(`File deleted: ${filePath} \u2014 re-parsing project...`);
|
|
2405
2476
|
try {
|
|
2406
|
-
const parsedFiles = parseProject(projectRoot);
|
|
2477
|
+
const parsedFiles = parseProject(projectRoot, options);
|
|
2407
2478
|
const newGraph = buildGraph(parsedFiles);
|
|
2408
2479
|
graph.clear();
|
|
2409
2480
|
newGraph.forEachNode((node, attrs) => {
|
|
@@ -2429,7 +2500,7 @@ Depwire visualization running at ${url2}`);
|
|
|
2429
2500
|
process.exit(0);
|
|
2430
2501
|
});
|
|
2431
2502
|
});
|
|
2432
|
-
const url = `http://127.0.0.1:${
|
|
2503
|
+
const url = `http://127.0.0.1:${availablePort}`;
|
|
2433
2504
|
return { server, url, alreadyRunning: false };
|
|
2434
2505
|
}
|
|
2435
2506
|
|
|
@@ -3220,7 +3291,7 @@ function handleListFiles(directory, graph) {
|
|
|
3220
3291
|
}
|
|
3221
3292
|
async function handleVisualizeGraph(highlight, maxFiles, state) {
|
|
3222
3293
|
const vizData = prepareVizData(state.graph, state.projectRoot);
|
|
3223
|
-
const { url, alreadyRunning } = startVizServer(
|
|
3294
|
+
const { url, alreadyRunning } = await startVizServer(
|
|
3224
3295
|
vizData,
|
|
3225
3296
|
state.graph,
|
|
3226
3297
|
state.projectRoot,
|
package/dist/index.js
CHANGED
|
@@ -11,12 +11,13 @@ import {
|
|
|
11
11
|
startVizServer,
|
|
12
12
|
updateFileInGraph,
|
|
13
13
|
watchProject
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-V3SB37U3.js";
|
|
15
15
|
|
|
16
16
|
// src/index.ts
|
|
17
17
|
import { Command } from "commander";
|
|
18
|
-
import { resolve } from "path";
|
|
18
|
+
import { resolve, dirname, join } from "path";
|
|
19
19
|
import { writeFileSync, readFileSync, existsSync } from "fs";
|
|
20
|
+
import { fileURLToPath } from "url";
|
|
20
21
|
|
|
21
22
|
// src/graph/serializer.ts
|
|
22
23
|
import { DirectedGraph } from "graphology";
|
|
@@ -85,14 +86,21 @@ function importFromJSON(json) {
|
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
// src/index.ts
|
|
89
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
90
|
+
var __dirname = dirname(__filename);
|
|
91
|
+
var packageJsonPath = join(__dirname, "../package.json");
|
|
92
|
+
var packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
88
93
|
var program = new Command();
|
|
89
|
-
program.name("depwire").description("Code cross-reference graph builder for TypeScript projects").version(
|
|
90
|
-
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").action(async (directory, options) => {
|
|
94
|
+
program.name("depwire").description("Code cross-reference graph builder for TypeScript projects").version(packageJson.version);
|
|
95
|
+
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) => {
|
|
91
96
|
const startTime = Date.now();
|
|
92
97
|
try {
|
|
93
98
|
const projectRoot = resolve(directory);
|
|
94
99
|
console.log(`Parsing project: ${projectRoot}`);
|
|
95
|
-
const parsedFiles = parseProject(projectRoot
|
|
100
|
+
const parsedFiles = parseProject(projectRoot, {
|
|
101
|
+
exclude: options.exclude,
|
|
102
|
+
verbose: options.verbose
|
|
103
|
+
});
|
|
96
104
|
console.log(`Parsed ${parsedFiles.length} files`);
|
|
97
105
|
const graph = buildGraph(parsedFiles);
|
|
98
106
|
const projectGraph = exportToJSON(graph, projectRoot);
|
|
@@ -171,17 +179,23 @@ Total Transitive Dependents: ${impact.transitiveDependents.length}`);
|
|
|
171
179
|
process.exit(1);
|
|
172
180
|
}
|
|
173
181
|
});
|
|
174
|
-
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").action(async (directory, options) => {
|
|
182
|
+
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) => {
|
|
175
183
|
try {
|
|
176
184
|
const projectRoot = resolve(directory);
|
|
177
185
|
console.log(`Parsing project: ${projectRoot}`);
|
|
178
|
-
const parsedFiles = parseProject(projectRoot
|
|
186
|
+
const parsedFiles = parseProject(projectRoot, {
|
|
187
|
+
exclude: options.exclude,
|
|
188
|
+
verbose: options.verbose
|
|
189
|
+
});
|
|
179
190
|
console.log(`Parsed ${parsedFiles.length} files`);
|
|
180
191
|
const graph = buildGraph(parsedFiles);
|
|
181
192
|
const vizData = prepareVizData(graph, projectRoot);
|
|
182
193
|
console.log(`Found ${vizData.stats.totalSymbols} symbols, ${vizData.stats.totalCrossFileEdges} cross-file edges`);
|
|
183
194
|
const port = parseInt(options.port, 10);
|
|
184
|
-
startVizServer(vizData, graph, projectRoot, port, options.open
|
|
195
|
+
await startVizServer(vizData, graph, projectRoot, port, options.open, {
|
|
196
|
+
exclude: options.exclude,
|
|
197
|
+
verbose: options.verbose
|
|
198
|
+
});
|
|
185
199
|
} catch (err) {
|
|
186
200
|
console.error("Error starting visualization:", err);
|
|
187
201
|
process.exit(1);
|
package/dist/mcpb-entry.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "depwire-cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Code cross-reference visualization and AI context engine for TypeScript. Analyzes codebases to show dependencies, enables AI tools with MCP, and renders beautiful arc diagrams.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
"cross-reference"
|
|
32
32
|
],
|
|
33
33
|
"author": "Depwire",
|
|
34
|
-
"license": "
|
|
34
|
+
"license": "BSL-1.1",
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
37
|
-
"url": "git+https://github.com/depwire
|
|
37
|
+
"url": "git+https://github.com/depwire/depwire.git"
|
|
38
38
|
},
|
|
39
39
|
"bugs": {
|
|
40
|
-
"url": "https://github.com/depwire
|
|
40
|
+
"url": "https://github.com/depwire/depwire/issues"
|
|
41
41
|
},
|
|
42
|
-
"homepage": "https://
|
|
42
|
+
"homepage": "https://depwire.dev",
|
|
43
43
|
"files": [
|
|
44
44
|
"dist",
|
|
45
45
|
"README.md",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"express": "5.2.1",
|
|
57
57
|
"graphology": "0.26.0",
|
|
58
58
|
"graphology-types": "0.24.8",
|
|
59
|
+
"minimatch": "^10.2.4",
|
|
59
60
|
"open": "11.0.0",
|
|
60
61
|
"simple-git": "3.31.1",
|
|
61
62
|
"tree-sitter": "0.21.1",
|