ucn 3.4.4 → 3.4.6
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 +87 -41
- package/cli/index.js +7 -1
- package/core/discovery.js +2 -1
- package/core/project.js +6 -7
- package/mcp/server.js +1566 -0
- package/package.json +7 -2
- package/test/mcp-edge-cases.js +490 -0
- package/test/parser.test.js +239 -0
- package/test/reliability-test-prompt.md +0 -58
package/README.md
CHANGED
|
@@ -134,45 +134,31 @@ EXTERNAL:
|
|
|
134
134
|
fs, path, crypto
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
##
|
|
138
|
-
|
|
139
|
-
JavaScript, TypeScript, Python, Go, Rust, Java
|
|
140
|
-
|
|
141
|
-
## Install
|
|
137
|
+
## Workflows
|
|
142
138
|
|
|
139
|
+
**Investigating a bug:**
|
|
143
140
|
```bash
|
|
144
|
-
|
|
141
|
+
ucn about problematic_function # Understand it fully
|
|
142
|
+
ucn trace problematic_function --depth=2 # See what it calls
|
|
145
143
|
```
|
|
146
144
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
To use UCN as a skill in Claude Code:
|
|
150
|
-
|
|
145
|
+
**Before modifying a function:**
|
|
151
146
|
```bash
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
#
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
# If cloned from git:
|
|
158
|
-
git clone https://github.com/mleoca/ucn.git
|
|
159
|
-
cp -r ucn/.claude/skills/ucn ~/.claude/skills/
|
|
147
|
+
ucn impact the_function # Who will break?
|
|
148
|
+
ucn smart the_function # See it + its helpers
|
|
149
|
+
# ... make your changes ...
|
|
150
|
+
ucn verify the_function # Did all call sites survive?
|
|
160
151
|
```
|
|
161
152
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
To use UCN as a skill in OpenAI Codex:
|
|
165
|
-
|
|
153
|
+
**Periodic cleanup:**
|
|
166
154
|
```bash
|
|
167
|
-
|
|
155
|
+
ucn deadcode --exclude=test # What can be deleted?
|
|
156
|
+
ucn toc # Project overview
|
|
157
|
+
```
|
|
168
158
|
|
|
169
|
-
|
|
170
|
-
cp -r "$(npm root -g)/ucn/.claude/skills/ucn" ~/.agents/skills/
|
|
159
|
+
## Supported Languages
|
|
171
160
|
|
|
172
|
-
|
|
173
|
-
git clone https://github.com/mleoca/ucn.git
|
|
174
|
-
cp -r ucn/.claude/skills/ucn ~/.agents/skills/
|
|
175
|
-
```
|
|
161
|
+
JavaScript, TypeScript, Python, Go, Rust, Java
|
|
176
162
|
|
|
177
163
|
## Usage
|
|
178
164
|
|
|
@@ -257,6 +243,7 @@ Common Flags:
|
|
|
257
243
|
--clear-cache Clear cache before running
|
|
258
244
|
--no-follow-symlinks Don't follow symbolic links
|
|
259
245
|
-i, --interactive Keep index in memory for multiple queries
|
|
246
|
+
--mcp Start as MCP server (stdio transport)
|
|
260
247
|
|
|
261
248
|
Quick Start:
|
|
262
249
|
ucn toc # See project structure (compact)
|
|
@@ -267,26 +254,85 @@ Quick Start:
|
|
|
267
254
|
ucn --interactive # Multiple queries
|
|
268
255
|
```
|
|
269
256
|
|
|
270
|
-
##
|
|
257
|
+
## Install
|
|
271
258
|
|
|
272
|
-
**Investigating a bug:**
|
|
273
259
|
```bash
|
|
274
|
-
|
|
275
|
-
ucn trace problematic_function --depth=2 # See what it calls
|
|
260
|
+
npm install -g ucn
|
|
276
261
|
```
|
|
277
262
|
|
|
278
|
-
|
|
263
|
+
### MCP Server
|
|
264
|
+
|
|
265
|
+
UCN includes a built-in [MCP](https://modelcontextprotocol.io) server, so any MCP-compatible AI client can use it as a tool. It exposes 27 tools (`ucn_about`, `ucn_context`, `ucn_impact`, `ucn_smart`, `ucn_trace`, `ucn_find`, `ucn_usages`, `ucn_toc`, `ucn_deadcode`, `ucn_fn`, `ucn_class`, `ucn_verify`, `ucn_imports`, `ucn_exporters`, `ucn_tests`, `ucn_related`, `ucn_graph`, `ucn_file_exports`, `ucn_search`, `ucn_plan`, `ucn_typedef`, `ucn_stacktrace`, `ucn_example`, `ucn_expand`, `ucn_lines`, `ucn_api`, `ucn_stats`).
|
|
266
|
+
|
|
267
|
+
**One-line setup** (for clients that support it):
|
|
268
|
+
|
|
269
|
+
| Client | Command |
|
|
270
|
+
|--------|---------|
|
|
271
|
+
| Claude Code | `claude mcp add ucn -- npx -y ucn --mcp` |
|
|
272
|
+
| OpenAI Codex CLI | `codex mcp add ucn -- npx -y ucn --mcp` |
|
|
273
|
+
| VS Code Copilot | `code --add-mcp '{"name":"ucn","command":"npx","args":["-y","ucn","--mcp"]}'` |
|
|
274
|
+
|
|
275
|
+
**Manual config** — add to the appropriate config file for your client:
|
|
276
|
+
|
|
277
|
+
| Client | Config file |
|
|
278
|
+
|--------|-------------|
|
|
279
|
+
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) |
|
|
280
|
+
| Cursor | `~/.cursor/mcp.json` or `.cursor/mcp.json` in project |
|
|
281
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
|
|
282
|
+
| Cline | VS Code sidebar > MCP Servers > Configure |
|
|
283
|
+
| Claude Code | `~/.claude/mcp-config.json` |
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"mcpServers": {
|
|
288
|
+
"ucn": {
|
|
289
|
+
"command": "npx",
|
|
290
|
+
"args": ["-y", "ucn", "--mcp"]
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
<details>
|
|
297
|
+
<summary>VS Code Copilot uses a slightly different format (<code>.vscode/mcp.json</code>)</summary>
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"servers": {
|
|
302
|
+
"ucn": {
|
|
303
|
+
"type": "stdio",
|
|
304
|
+
"command": "npx",
|
|
305
|
+
"args": ["-y", "ucn", "--mcp"]
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
</details>
|
|
311
|
+
|
|
312
|
+
### Claude Code Skill (alternative to MCP)
|
|
313
|
+
|
|
279
314
|
```bash
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
#
|
|
283
|
-
|
|
315
|
+
mkdir -p ~/.claude/skills
|
|
316
|
+
|
|
317
|
+
# If installed via npm:
|
|
318
|
+
cp -r "$(npm root -g)/ucn/.claude/skills/ucn" ~/.claude/skills/
|
|
319
|
+
|
|
320
|
+
# If cloned from git:
|
|
321
|
+
git clone https://github.com/mleoca/ucn.git
|
|
322
|
+
cp -r ucn/.claude/skills/ucn ~/.claude/skills/
|
|
284
323
|
```
|
|
285
324
|
|
|
286
|
-
|
|
325
|
+
### Codex Skill (alternative to MCP)
|
|
326
|
+
|
|
287
327
|
```bash
|
|
288
|
-
|
|
289
|
-
|
|
328
|
+
mkdir -p ~/.agents/skills
|
|
329
|
+
|
|
330
|
+
# If installed via npm:
|
|
331
|
+
cp -r "$(npm root -g)/ucn/.claude/skills/ucn" ~/.agents/skills/
|
|
332
|
+
|
|
333
|
+
# If cloned from git:
|
|
334
|
+
git clone https://github.com/mleoca/ucn.git
|
|
335
|
+
cp -r ucn/.claude/skills/ucn ~/.agents/skills/
|
|
290
336
|
```
|
|
291
337
|
|
|
292
338
|
## License
|
package/cli/index.js
CHANGED
|
@@ -22,6 +22,10 @@ const output = require('../core/output');
|
|
|
22
22
|
|
|
23
23
|
const rawArgs = process.argv.slice(2);
|
|
24
24
|
|
|
25
|
+
// MCP server mode — launch server and skip CLI
|
|
26
|
+
if (rawArgs.includes('--mcp')) {
|
|
27
|
+
require('../mcp/server.js');
|
|
28
|
+
} else {
|
|
25
29
|
// Support -- to separate flags from positional arguments
|
|
26
30
|
const doubleDashIdx = rawArgs.indexOf('--');
|
|
27
31
|
const args = doubleDashIdx === -1 ? rawArgs : rawArgs.slice(0, doubleDashIdx);
|
|
@@ -78,7 +82,7 @@ if (fileArgIdx !== -1 && args[fileArgIdx + 1]) {
|
|
|
78
82
|
|
|
79
83
|
// Known flags for validation
|
|
80
84
|
const knownFlags = new Set([
|
|
81
|
-
'--help', '-h',
|
|
85
|
+
'--help', '-h', '--mcp',
|
|
82
86
|
'--json', '--verbose', '--no-quiet', '--quiet',
|
|
83
87
|
'--code-only', '--with-types', '--top-level', '--exact',
|
|
84
88
|
'--no-cache', '--clear-cache', '--include-tests',
|
|
@@ -2542,3 +2546,5 @@ if (flags.interactive) {
|
|
|
2542
2546
|
} else {
|
|
2543
2547
|
main();
|
|
2544
2548
|
}
|
|
2549
|
+
|
|
2550
|
+
} // end of --mcp else block
|
package/core/discovery.js
CHANGED
|
@@ -298,7 +298,8 @@ function shouldIgnore(name, ignores, parentDir) {
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
// Check conditional ignores (only if parentDir provided)
|
|
301
|
-
|
|
301
|
+
// Use Array.isArray to avoid matching Object.prototype properties (e.g. dir named "constructor")
|
|
302
|
+
if (parentDir && Array.isArray(CONDITIONAL_IGNORES[name])) {
|
|
302
303
|
const markers = CONDITIONAL_IGNORES[name];
|
|
303
304
|
for (const marker of markers) {
|
|
304
305
|
if (fs.existsSync(path.join(parentDir, marker))) {
|
package/core/project.js
CHANGED
|
@@ -1803,7 +1803,7 @@ class ProjectIndex {
|
|
|
1803
1803
|
// Find all test files
|
|
1804
1804
|
const testFiles = [];
|
|
1805
1805
|
for (const [filePath, fileEntry] of this.files) {
|
|
1806
|
-
if (isTestFile(
|
|
1806
|
+
if (isTestFile(fileEntry.relativePath, fileEntry.language)) {
|
|
1807
1807
|
testFiles.push({ path: filePath, entry: fileEntry });
|
|
1808
1808
|
}
|
|
1809
1809
|
}
|
|
@@ -2108,14 +2108,13 @@ class ProjectIndex {
|
|
|
2108
2108
|
continue;
|
|
2109
2109
|
}
|
|
2110
2110
|
|
|
2111
|
+
const fileEntry = this.files.get(symbol.file);
|
|
2112
|
+
const lang = fileEntry?.language;
|
|
2113
|
+
|
|
2111
2114
|
// Skip test files unless requested
|
|
2112
|
-
if (!options.includeTests && isTestFile(symbol.
|
|
2115
|
+
if (!options.includeTests && isTestFile(symbol.relativePath, lang)) {
|
|
2113
2116
|
continue;
|
|
2114
2117
|
}
|
|
2115
|
-
|
|
2116
|
-
// Check if exported
|
|
2117
|
-
const fileEntry = this.files.get(symbol.file);
|
|
2118
|
-
const lang = fileEntry?.language;
|
|
2119
2118
|
const mods = symbol.modifiers || [];
|
|
2120
2119
|
|
|
2121
2120
|
// Language-specific entry points (called by runtime, no AST-visible callers)
|
|
@@ -3653,7 +3652,7 @@ class ProjectIndex {
|
|
|
3653
3652
|
totalState += state.length;
|
|
3654
3653
|
totalLines += fileEntry.lines;
|
|
3655
3654
|
totalDynamic += fileEntry.dynamicImports || 0;
|
|
3656
|
-
if (isTestFile(
|
|
3655
|
+
if (isTestFile(fileEntry.relativePath, fileEntry.language)) totalTests += 1;
|
|
3657
3656
|
|
|
3658
3657
|
const entry = {
|
|
3659
3658
|
file: fileEntry.relativePath,
|