ucn 3.8.11 → 3.8.12

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.
@@ -0,0 +1,33 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ node-version: [18, 20, 22]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Configure git identity for tests
19
+ run: |
20
+ git config --global user.email "ci@github.com"
21
+ git config --global user.name "CI"
22
+
23
+ - name: Use Node.js ${{ matrix.node-version }}
24
+ uses: actions/setup-node@v4
25
+ with:
26
+ node-version: ${{ matrix.node-version }}
27
+ cache: npm
28
+
29
+ - name: Install dependencies
30
+ run: npm ci
31
+
32
+ - name: Run tests
33
+ run: npm test
@@ -0,0 +1,67 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags: ['v*']
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+ id-token: write # Required for MCP Registry OIDC authentication
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Configure git identity for tests
19
+ run: |
20
+ git config --global user.email "ci@github.com"
21
+ git config --global user.name "CI"
22
+
23
+ - uses: actions/setup-node@v4
24
+ with:
25
+ node-version: 22
26
+ cache: npm
27
+
28
+ - run: npm ci
29
+ - run: npm test
30
+
31
+ publish-npm:
32
+ needs: test
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - uses: actions/setup-node@v4
38
+ with:
39
+ node-version: 22
40
+ registry-url: https://registry.npmjs.org
41
+
42
+ - run: npm ci
43
+ - run: npm publish
44
+ env:
45
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
46
+
47
+ publish-mcp:
48
+ needs: publish-npm
49
+ runs-on: ubuntu-latest
50
+ permissions:
51
+ contents: read
52
+ id-token: write
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+
56
+ - uses: actions/setup-node@v4
57
+ with:
58
+ node-version: 22
59
+
60
+ - name: Install mcp-publisher
61
+ run: npm install -g mcp-publisher
62
+
63
+ - name: Authenticate with MCP Registry via OIDC
64
+ run: mcp-publisher login github-oidc
65
+
66
+ - name: Publish to MCP Registry
67
+ run: mcp-publisher publish
package/README.md CHANGED
@@ -188,7 +188,7 @@ function compareNames(a, b) {
188
188
 
189
189
  ## Testing and reliability
190
190
 
191
- - **Fast** - indexes its own ~25K-line codebase in under 100ms, cached after first run
191
+ - **Fast** - uses incremental cache for optimal performance
192
192
  - **Discipline** - every bug fix gets a regression test, test code is ~3x the source
193
193
  - **Coverage** - every command, every supported language, every surface (CLI, MCP, interactive)
194
194
  - **Systematic** - a harness exercises all command and flag combinations against real multi-language fixtures
@@ -345,7 +345,7 @@ Common Flags:
345
345
  --class-name=X Scope to specific class (e.g., --class-name=Repository)
346
346
  --include-methods Include method calls (obj.fn) in caller/callee analysis
347
347
  --include-uncertain Include ambiguous/uncertain matches
348
- --show-confidence Show confidence scores per caller/callee edge
348
+ --no-confidence Hide confidence scores (shown by default)
349
349
  --min-confidence=N Filter edges below confidence threshold (0.0-1.0)
350
350
  --include-exported Include exported symbols in deadcode
351
351
  --no-regex Force plain text search (regex is default)
@@ -361,6 +361,9 @@ Common Flags:
361
361
  --no-cache Disable caching
362
362
  --clear-cache Clear cache before running
363
363
  --base=<ref> Git ref for diff-impact (default: HEAD)
364
+ --staged Analyze staged changes (diff-impact)
365
+ --no-follow-symlinks Don't follow symbolic links
366
+ -i, --interactive Keep index in memory for multiple queries
364
367
  ```
365
368
 
366
369
  ---
package/core/project.js CHANGED
@@ -306,17 +306,32 @@ class ProjectIndex {
306
306
 
307
307
  // Detect bundled/minified files (webpack bundles, minified code)
308
308
  // These are build artifacts, not user-written source code
309
- const contentLines = content.split('\n');
309
+ // Count lines without splitting: count newlines + 1 (avoids allocating array)
310
+ let lineCount = 1;
311
+ let maxLineLen = 0;
312
+ let longLineCount = 0;
313
+ let lineStart = 0;
314
+ for (let ci = 0; ci < content.length; ci++) {
315
+ if (content.charCodeAt(ci) === 10) { // '\n'
316
+ const lineLen = ci - lineStart;
317
+ if (lineLen > maxLineLen) maxLineLen = lineLen;
318
+ if (lineLen > 1000) longLineCount++;
319
+ lineStart = ci + 1;
320
+ lineCount++;
321
+ }
322
+ }
323
+ // Handle last line (no trailing newline)
324
+ const lastLineLen = content.length - lineStart;
325
+ if (lastLineLen > maxLineLen) maxLineLen = lastLineLen;
326
+ if (lastLineLen > 1000) longLineCount++;
327
+
310
328
  const isBundled = (() => {
311
329
  // Webpack bundles contain __webpack_require__ or __webpack_modules__
312
330
  if (content.includes('__webpack_require__') || content.includes('__webpack_modules__')) return true;
313
331
  // Minified files: very few lines but large content (avg > 500 chars/line)
314
- if (contentLines.length > 0 && contentLines.length < 50 && content.length / contentLines.length > 500) return true;
332
+ if (lineCount > 0 && lineCount < 50 && content.length / lineCount > 500) return true;
315
333
  // Very long single lines (> 1000 chars) in most of the file suggest minification
316
- if (contentLines.length > 0) {
317
- const longLines = contentLines.filter(l => l.length > 1000).length;
318
- if (longLines > 0 && longLines / contentLines.length > 0.3) return true;
319
- }
334
+ if (lineCount > 0 && longLineCount > 0 && longLineCount / lineCount > 0.3) return true;
320
335
  return false;
321
336
  })();
322
337
 
@@ -324,7 +339,7 @@ class ProjectIndex {
324
339
  path: filePath,
325
340
  relativePath: path.relative(this.root, filePath),
326
341
  language,
327
- lines: contentLines.length,
342
+ lines: lineCount,
328
343
  hash,
329
344
  mtime: stat.mtimeMs,
330
345
  size: stat.size,
@@ -1778,8 +1793,14 @@ class ProjectIndex {
1778
1793
  getLineContent(filePath, lineNum) {
1779
1794
  try {
1780
1795
  const content = this._readFile(filePath);
1781
- const lines = content.split('\n');
1782
- return lines[lineNum - 1] || '';
1796
+ // Find the Nth line without splitting the entire file
1797
+ let start = 0;
1798
+ for (let i = 1; i < lineNum; i++) {
1799
+ start = content.indexOf('\n', start) + 1;
1800
+ if (start === 0) return '';
1801
+ }
1802
+ const end = content.indexOf('\n', start);
1803
+ return end === -1 ? content.slice(start) : content.slice(start, end);
1783
1804
  } catch (e) {
1784
1805
  return '';
1785
1806
  }