@vibgrate/cli 2026.709.1 → 2026.709.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/DOCS.md +443 -295
- package/README.md +26 -19
- package/dist/baseline-VGDQ33ZE.js +6 -0
- package/dist/{baseline-7AFTGG3I.js.map → baseline-VGDQ33ZE.js.map} +1 -1
- package/dist/{chunk-MPKMAERO.js → chunk-3TCE6IGZ.js} +231 -38
- package/dist/chunk-3TCE6IGZ.js.map +1 -0
- package/dist/{chunk-IRZCYPFC.js → chunk-FU4MOQTC.js} +3 -3
- package/dist/{chunk-IRZCYPFC.js.map → chunk-FU4MOQTC.js.map} +1 -1
- package/dist/{chunk-PXZ5KPVN.js → chunk-PIWHQSAR.js} +3 -3
- package/dist/{chunk-PXZ5KPVN.js.map → chunk-PIWHQSAR.js.map} +1 -1
- package/dist/cli.js +32 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/package.json +3 -2
- package/dist/baseline-7AFTGG3I.js +0 -6
- package/dist/chunk-MPKMAERO.js.map +0 -1
package/README.md
CHANGED
|
@@ -269,42 +269,49 @@ See [docs/QUICKSTART-PROMPT.md](./docs/QUICKSTART-PROMPT.md) for the full prompt
|
|
|
269
269
|
|
|
270
270
|
| Command | Description |
|
|
271
271
|
| --- | --- |
|
|
272
|
-
| `vg build [path]` | Build / update the code map (incremental, deterministic) |
|
|
273
|
-
| `vg show <file>` | Explain a node: what it is, what it calls, what calls it |
|
|
274
272
|
| `vg ask "<question>"` | Query the map in natural language |
|
|
273
|
+
| `vg benchmark` | Reproducible build + memory + token-reduction benchmark (honest estimates) |
|
|
274
|
+
| `vg build [path]` | Build / update the code map (incremental, deterministic) |
|
|
275
|
+
| `vg bundle` | Build an air-gapped bundle (grammars + graph + library catalog) |
|
|
276
|
+
| `vg embed` | Precompute the semantic index for instant `vg ask` |
|
|
277
|
+
| `vg export` | Export the map (json / ndjson / graphml / dot / cypher / md / html / SBOM) |
|
|
278
|
+
| `vg facts <file>` | Deterministic facts for a node (contracts, invariants) |
|
|
279
|
+
| `vg guide <file>` | Cited standards / practices for a node (free pack) |
|
|
275
280
|
| `vg impact <file>` | What breaks if you change it — and the tests to run |
|
|
276
|
-
| `vg
|
|
277
|
-
| `vg tree <file>` | Call tree rooted at a node |
|
|
278
|
-
| `vg insights` | Overview: hubs, hotspots, untested paths |
|
|
281
|
+
| `vg install` / `vg uninstall` | Wire (or remove) **Vibgrate AI Context** + skill in your AI assistant |
|
|
279
282
|
| `vg lib <package>` | Version-correct, drift-annotated library docs |
|
|
283
|
+
| `vg map` / `vg hubs` / `vg areas` / `vg oddities` | Map insights: overview, most-depended-on code, natural groupings, cross-area smells |
|
|
284
|
+
| `vg models` | The local model fleet (Ollama / LM Studio / gguf), discovered offline |
|
|
285
|
+
| `vg path <from> <to>` | How A connects to B (shortest path) |
|
|
286
|
+
| `vg savings` | Local report of tokens/$ saved vs a grep baseline (estimates) |
|
|
280
287
|
| `vg serve` | Start **Vibgrate AI Context** (local-first MCP: code map + drift + version-correct docs) |
|
|
281
|
-
| `vg install` | Wire **Vibgrate AI Context** + skill into your AI assistant |
|
|
282
288
|
| `vg share` | Make the graph committable + auto-updating for your team |
|
|
289
|
+
| `vg show <file>` | Explain a node: what it is, what it calls, what calls it |
|
|
283
290
|
| `vg status` | Cache/freshness, counts, staleness |
|
|
284
|
-
| `vg facts <file>` | Deterministic facts for a node (contracts, invariants) |
|
|
285
291
|
| `vg tests <file>` | Which tests cover a node |
|
|
286
|
-
| `vg
|
|
287
|
-
| `vg
|
|
292
|
+
| `vg tree <file>` | Call tree rooted at a node |
|
|
293
|
+
| `vg unknowns` | What the graph cannot resolve, ranked by blast radius |
|
|
288
294
|
|
|
289
295
|
### Drift reporting
|
|
290
296
|
|
|
291
297
|
| Command | Description |
|
|
292
298
|
| --- | --- |
|
|
299
|
+
| `vg baseline [path]` | Create a drift baseline |
|
|
300
|
+
| `vg bisect <package> <constraint>` | The commit where a dependency crossed a version line (`--assert` to gate CI) |
|
|
301
|
+
| `vg drift` | What is outdated across dependencies (offline; `--online` for currency) |
|
|
302
|
+
| `vg dsn create` | Generate a DSN token |
|
|
303
|
+
| `vg fix` | Ranked, risk-tiered upgrade plans from the hosted planner — then apply the one you choose |
|
|
304
|
+
| `vg init [path]` | Initialise config and `.vibgrate/` |
|
|
305
|
+
| `vg login` / `vg logout` | Authenticate the CLI with your Vibgrate workspace (or clear stored credentials) |
|
|
306
|
+
| `vg push` | Upload scan results to Vibgrate Cloud |
|
|
307
|
+
| `vg report` | Generate a report from a scan artifact |
|
|
308
|
+
| `vg sbom export` / `delta` / `vex` | Export CycloneDX/SPDX SBOM, diff two artifacts, or emit an OpenVEX document |
|
|
293
309
|
| `vg scan [path]` | Scan for upgrade drift |
|
|
294
310
|
| `vg scan --vulns` | Also detect known vulnerabilities (OSV; offline via `--package-manifest`) |
|
|
295
311
|
| `vg scan --full` | Comprehensive scan: drift + vulnerabilities + a banned-dependency report |
|
|
296
|
-
| `vg why <package>` | Who introduced a dependency, its version history, and any open vulnerabilities |
|
|
297
|
-
| `vg bisect <package> <constraint>` | The commit where a dependency crossed a version line (`--assert` to gate CI) |
|
|
298
312
|
| `vg scan --push` | Scan and push results to Vibgrate Cloud |
|
|
299
|
-
| `vg baseline [path]` | Create a drift baseline |
|
|
300
|
-
| `vg report` | Generate a report from a scan artifact |
|
|
301
|
-
| `vg sbom export` | Export CycloneDX or SPDX SBOM |
|
|
302
|
-
| `vg sbom delta` | Compare two artifacts for SBOM drift |
|
|
303
|
-
| `vg vex` | Generate an OpenVEX document for attestation |
|
|
304
|
-
| `vg init [path]` | Initialise config and `.vibgrate/` |
|
|
305
|
-
| `vg push` | Upload scan results to Vibgrate Cloud |
|
|
306
|
-
| `vg dsn create` | Generate a DSN token |
|
|
307
313
|
| `vg update` | Check for and install updates |
|
|
314
|
+
| `vg why <package>` | Who introduced a dependency, its version history, and any open vulnerabilities |
|
|
308
315
|
|
|
309
316
|
```bash
|
|
310
317
|
vg scan [path] [--vulns] [--full] [--format text|json|sarif|md] [--out <file>] [--fail-on warn|error] \
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"baseline-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"baseline-VGDQ33ZE.js"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { hashString, langById, shortId, canonicalize, grammarSetVersion, hashBytes, langForExtension, setGrammarsOverride, parseSource } from './chunk-X5YT263H.js';
|
|
2
|
-
import { writeTextFile, pathExists, readJsonFile, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-
|
|
2
|
+
import { writeTextFile, pathExists, readJsonFile, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-PIWHQSAR.js';
|
|
3
3
|
import * as fs19 from 'fs';
|
|
4
|
-
import { execFileSync } from 'child_process';
|
|
4
|
+
import { spawnSync, execFileSync } from 'child_process';
|
|
5
5
|
import * as path19 from 'path';
|
|
6
6
|
import ignore from 'ignore';
|
|
7
7
|
import * as v8 from 'v8';
|
|
@@ -20,6 +20,7 @@ import chalk from 'chalk';
|
|
|
20
20
|
import * as zlib from 'zlib';
|
|
21
21
|
import { promisify } from 'util';
|
|
22
22
|
import { encode, decode } from 'gpt-tokenizer/encoding/cl100k_base';
|
|
23
|
+
import { Worker } from 'worker_threads';
|
|
23
24
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
24
25
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
25
26
|
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -5671,19 +5672,165 @@ function boundList(items, max) {
|
|
|
5671
5672
|
return { items: items.slice(0, max), total: items.length };
|
|
5672
5673
|
}
|
|
5673
5674
|
var NODE_EDGE_CAP = 50;
|
|
5674
|
-
var IGNORE_DIRS = /* @__PURE__ */ new Set([".git", ".vibgrate", "node_modules", "dist", "build", "out", "target", "vendor", "__pycache__"]);
|
|
5675
5675
|
var MAX_FILE_BYTES = 1e6;
|
|
5676
|
-
var MAX_FILES_SCANNED = 2e4;
|
|
5677
5676
|
var PREVIEW_CHARS = 120;
|
|
5678
|
-
|
|
5677
|
+
var NUL = "\0";
|
|
5678
|
+
var PARALLEL_MIN_BYTES = 64e6;
|
|
5679
|
+
var PARALLEL_MIN_FILES = 2e3;
|
|
5680
|
+
var ROW_CAP = 1e4;
|
|
5681
|
+
async function scanCandidates(root, files, needle, opts) {
|
|
5682
|
+
const needleLower = needle.toLowerCase();
|
|
5683
|
+
if (!needleLower || files.length === 0) return { hits: [], total: 0, truncated: false };
|
|
5684
|
+
if (!opts.collectAll) return scanInline(root, files, needleLower, opts.budget, true, void 0);
|
|
5685
|
+
if (files.length < PARALLEL_MIN_FILES && !process.env.VG_PARALLEL_MIN_BYTES) {
|
|
5686
|
+
return scanInline(root, files, needleLower, ROW_CAP, false, void 0);
|
|
5687
|
+
}
|
|
5688
|
+
const { over, sizes } = measure(root, files);
|
|
5689
|
+
if (!over) return scanInline(root, files, needleLower, ROW_CAP, false, sizes);
|
|
5690
|
+
return scanParallel(root, files, needleLower);
|
|
5691
|
+
}
|
|
5692
|
+
function parallelMinBytes() {
|
|
5693
|
+
const v = Number(process.env.VG_PARALLEL_MIN_BYTES);
|
|
5694
|
+
return Number.isFinite(v) && v >= 0 ? v : PARALLEL_MIN_BYTES;
|
|
5695
|
+
}
|
|
5696
|
+
function measure(root, files) {
|
|
5697
|
+
const threshold = parallelMinBytes();
|
|
5698
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
5699
|
+
let total = 0;
|
|
5700
|
+
for (const rel2 of files) {
|
|
5701
|
+
let size;
|
|
5702
|
+
try {
|
|
5703
|
+
size = fs19.statSync(path19.join(root, rel2)).size;
|
|
5704
|
+
} catch {
|
|
5705
|
+
continue;
|
|
5706
|
+
}
|
|
5707
|
+
sizes.set(rel2, size);
|
|
5708
|
+
if (size <= MAX_FILE_BYTES) total += size;
|
|
5709
|
+
if (total >= threshold) return { over: true, sizes };
|
|
5710
|
+
}
|
|
5711
|
+
return { over: false, sizes };
|
|
5712
|
+
}
|
|
5713
|
+
function matchLines(text, needleLower) {
|
|
5714
|
+
const low = text.toLowerCase();
|
|
5715
|
+
if (!low.includes(needleLower)) return [];
|
|
5716
|
+
const lowLines = low.split("\n");
|
|
5717
|
+
const rawLines = text.split("\n");
|
|
5718
|
+
const hits = [];
|
|
5719
|
+
for (let i = 0; i < lowLines.length; i++) {
|
|
5720
|
+
if (lowLines[i].includes(needleLower)) hits.push({ line: i + 1, preview: rawLines[i].trim().slice(0, PREVIEW_CHARS) });
|
|
5721
|
+
}
|
|
5722
|
+
return hits;
|
|
5723
|
+
}
|
|
5724
|
+
function readCandidate(abs, knownSize) {
|
|
5725
|
+
try {
|
|
5726
|
+
const size = knownSize ?? fs19.statSync(abs).size;
|
|
5727
|
+
if (size > MAX_FILE_BYTES) return null;
|
|
5728
|
+
const text = fs19.readFileSync(abs, "utf8");
|
|
5729
|
+
return text.includes(NUL) ? null : text;
|
|
5730
|
+
} catch {
|
|
5731
|
+
return null;
|
|
5732
|
+
}
|
|
5733
|
+
}
|
|
5734
|
+
function scanInline(root, files, needleLower, cap, stopAtCap, sizes) {
|
|
5735
|
+
const hits = [];
|
|
5736
|
+
let total = 0;
|
|
5737
|
+
let truncated = false;
|
|
5738
|
+
for (const rel2 of files) {
|
|
5739
|
+
const text = readCandidate(path19.join(root, rel2), sizes?.get(rel2));
|
|
5740
|
+
if (text === null) continue;
|
|
5741
|
+
for (const h of matchLines(text, needleLower)) {
|
|
5742
|
+
total++;
|
|
5743
|
+
if (hits.length < cap) hits.push({ file: rel2, line: h.line, preview: h.preview });
|
|
5744
|
+
else {
|
|
5745
|
+
truncated = true;
|
|
5746
|
+
if (stopAtCap) return { hits, total, truncated };
|
|
5747
|
+
}
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5750
|
+
return { hits, total, truncated };
|
|
5751
|
+
}
|
|
5752
|
+
async function scanParallel(root, files, needleLower) {
|
|
5753
|
+
const n = Math.min(files.length, Math.max(2, Math.min(8, os.cpus().length - 1)));
|
|
5754
|
+
const shards = Array.from({ length: n }, () => []);
|
|
5755
|
+
files.forEach((f, i) => shards[i % n].push(f));
|
|
5756
|
+
let results;
|
|
5757
|
+
try {
|
|
5758
|
+
results = await Promise.all(shards.map((shard) => runWorker(root, shard, needleLower)));
|
|
5759
|
+
} catch {
|
|
5760
|
+
return scanInline(root, files, needleLower, ROW_CAP, false, void 0);
|
|
5761
|
+
}
|
|
5762
|
+
const hits = [];
|
|
5763
|
+
let total = 0;
|
|
5764
|
+
let truncated = false;
|
|
5765
|
+
for (const r of results) {
|
|
5766
|
+
total += r.total;
|
|
5767
|
+
truncated = truncated || r.truncated;
|
|
5768
|
+
for (const h of r.hits) hits.push(h);
|
|
5769
|
+
}
|
|
5770
|
+
hits.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line);
|
|
5771
|
+
if (hits.length > ROW_CAP) {
|
|
5772
|
+
hits.length = ROW_CAP;
|
|
5773
|
+
truncated = true;
|
|
5774
|
+
}
|
|
5775
|
+
return { hits, total, truncated };
|
|
5776
|
+
}
|
|
5777
|
+
var WORKER_SOURCE = [
|
|
5778
|
+
"const { parentPort, workerData } = require('worker_threads');",
|
|
5779
|
+
"const fs = require('fs');",
|
|
5780
|
+
"const path = require('path');",
|
|
5781
|
+
`const MAX = ${MAX_FILE_BYTES};`,
|
|
5782
|
+
`const PREVIEW = ${PREVIEW_CHARS};`,
|
|
5783
|
+
`const CAP = ${ROW_CAP};`,
|
|
5784
|
+
"const NUL = String.fromCharCode(0);",
|
|
5785
|
+
"const NL = String.fromCharCode(10);",
|
|
5786
|
+
"const { root, files, needleLower } = workerData;",
|
|
5787
|
+
"const hits = []; let total = 0, truncated = false;",
|
|
5788
|
+
"for (const rel of files) {",
|
|
5789
|
+
" const abs = path.join(root, rel);",
|
|
5790
|
+
" let text;",
|
|
5791
|
+
' try { if (fs.statSync(abs).size > MAX) continue; text = fs.readFileSync(abs, "utf8"); } catch { continue; }',
|
|
5792
|
+
" if (text.includes(NUL)) continue;",
|
|
5793
|
+
" const low = text.toLowerCase();",
|
|
5794
|
+
" if (!low.includes(needleLower)) continue;",
|
|
5795
|
+
" const lowLines = low.split(NL); const rawLines = text.split(NL);",
|
|
5796
|
+
" for (let i = 0; i < lowLines.length; i++) {",
|
|
5797
|
+
" if (!lowLines[i].includes(needleLower)) continue;",
|
|
5798
|
+
" total++;",
|
|
5799
|
+
" if (hits.length < CAP) hits.push({ file: rel, line: i + 1, preview: rawLines[i].trim().slice(0, PREVIEW) });",
|
|
5800
|
+
" else truncated = true;",
|
|
5801
|
+
" }",
|
|
5802
|
+
"}",
|
|
5803
|
+
"parentPort.postMessage({ hits, total, truncated });"
|
|
5804
|
+
].join("\n");
|
|
5805
|
+
function runWorker(root, files, needleLower) {
|
|
5806
|
+
return new Promise((resolve12, reject) => {
|
|
5807
|
+
const w = new Worker(WORKER_SOURCE, { eval: true, workerData: { root, files, needleLower } });
|
|
5808
|
+
let settled = false;
|
|
5809
|
+
w.once("message", (m) => {
|
|
5810
|
+
settled = true;
|
|
5811
|
+
resolve12(m);
|
|
5812
|
+
});
|
|
5813
|
+
w.once("error", reject);
|
|
5814
|
+
w.once("exit", (code) => {
|
|
5815
|
+
if (!settled && code !== 0) reject(new Error(`literal-scan worker exited with code ${code}`));
|
|
5816
|
+
});
|
|
5817
|
+
});
|
|
5818
|
+
}
|
|
5819
|
+
|
|
5820
|
+
// src/engine/search.ts
|
|
5821
|
+
var IGNORE_DIRS = /* @__PURE__ */ new Set([".git", ".vibgrate", "node_modules", "dist", "build", "out", "target", "vendor", "__pycache__"]);
|
|
5822
|
+
var MAX_FILES_SCANNED = 2e4;
|
|
5823
|
+
async function searchSymbols(graph, root, query, limit) {
|
|
5679
5824
|
const q2 = query.trim();
|
|
5680
5825
|
if (!q2) return { matches: [], moreAvailable: false, hint: "query is required" };
|
|
5826
|
+
const isPhrase = /\s/.test(q2);
|
|
5681
5827
|
let nodes = findNodes(graph, q2).filter((n) => n.kind !== "file");
|
|
5682
5828
|
if (nodes.length === 0) {
|
|
5683
5829
|
const tokens = queryTokens(q2);
|
|
5684
5830
|
if (tokens.length >= 2) nodes = multiTokenNodes(graph, tokens);
|
|
5685
5831
|
}
|
|
5686
|
-
const
|
|
5832
|
+
const symbolCap = isPhrase ? Math.max(1, Math.floor(limit / 3)) : limit;
|
|
5833
|
+
const symbolHits = nodes.slice(0, symbolCap).map((n, i) => ({
|
|
5687
5834
|
kind: n.kind,
|
|
5688
5835
|
name: n.qualifiedName,
|
|
5689
5836
|
file: n.file,
|
|
@@ -5693,9 +5840,12 @@ function searchSymbols(graph, root, query, limit) {
|
|
|
5693
5840
|
const spare = limit - symbolHits.length;
|
|
5694
5841
|
const textHits = [];
|
|
5695
5842
|
let truncatedScan = false;
|
|
5843
|
+
let totalTextMatches;
|
|
5696
5844
|
if (spare > 0) {
|
|
5697
5845
|
const seen = new Set(symbolHits.map((h) => `${h.file}:${h.line}`));
|
|
5698
|
-
|
|
5846
|
+
const scan = await scanFiles(root, q2, spare, seen, textHits, isPhrase);
|
|
5847
|
+
truncatedScan = scan.truncated;
|
|
5848
|
+
if (isPhrase) totalTextMatches = scan.total;
|
|
5699
5849
|
}
|
|
5700
5850
|
const matches = [...symbolHits, ...textHits];
|
|
5701
5851
|
if (matches.length === 0) {
|
|
@@ -5705,7 +5855,14 @@ function searchSymbols(graph, root, query, limit) {
|
|
|
5705
5855
|
hint: "no symbol or text match \u2014 for meaning-level questions (symptoms, relationships, what-breaks-if) use query_graph"
|
|
5706
5856
|
};
|
|
5707
5857
|
}
|
|
5708
|
-
|
|
5858
|
+
const literalTruncated = totalTextMatches !== void 0 && totalTextMatches > textHits.length;
|
|
5859
|
+
const moreAvailable = isPhrase ? literalTruncated || truncatedScan : nodes.length > symbolHits.length || truncatedScan;
|
|
5860
|
+
const result = { matches, moreAvailable };
|
|
5861
|
+
if (totalTextMatches !== void 0) result.totalTextMatches = totalTextMatches;
|
|
5862
|
+
if (literalTruncated) {
|
|
5863
|
+
result.hint = `showing ${textHits.length} of ${totalTextMatches}${truncatedScan ? "+" : ""} literal matches \u2014 raise limit or narrow the query to get them all`;
|
|
5864
|
+
}
|
|
5865
|
+
return result;
|
|
5709
5866
|
}
|
|
5710
5867
|
function queryTokens(q2) {
|
|
5711
5868
|
return [...new Set(q2.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2))];
|
|
@@ -5722,9 +5879,26 @@ function multiTokenNodes(graph, tokens) {
|
|
|
5722
5879
|
}
|
|
5723
5880
|
return [...cov.values()].sort((a, b) => b.hits - a.hits || b.node.importance - a.node.importance).map((e) => e.node);
|
|
5724
5881
|
}
|
|
5725
|
-
function scanFiles(root, needle, budget, seen, out) {
|
|
5726
|
-
const
|
|
5727
|
-
|
|
5882
|
+
async function scanFiles(root, needle, budget, seen, out, countAll) {
|
|
5883
|
+
const listing = listCandidateFiles(root, needle);
|
|
5884
|
+
const outcome = await scanCandidates(root, listing.files, needle, { collectAll: countAll, budget });
|
|
5885
|
+
let overlap = 0;
|
|
5886
|
+
for (const h of outcome.hits) {
|
|
5887
|
+
if (seen.has(`${h.file}:${h.line}`)) {
|
|
5888
|
+
overlap++;
|
|
5889
|
+
continue;
|
|
5890
|
+
}
|
|
5891
|
+
seen.add(`${h.file}:${h.line}`);
|
|
5892
|
+
if (out.length < budget) out.push({ kind: "text", file: h.file, line: h.line, preview: h.preview });
|
|
5893
|
+
}
|
|
5894
|
+
return { total: outcome.total - overlap, truncated: listing.truncated || outcome.truncated };
|
|
5895
|
+
}
|
|
5896
|
+
function listCandidateFiles(root, needle) {
|
|
5897
|
+
return ripgrepCandidates(root, needle) ?? walkCandidates(root);
|
|
5898
|
+
}
|
|
5899
|
+
function walkCandidates(root) {
|
|
5900
|
+
const files = [];
|
|
5901
|
+
let truncated = false;
|
|
5728
5902
|
const stack = [root];
|
|
5729
5903
|
while (stack.length) {
|
|
5730
5904
|
const dir = stack.pop();
|
|
@@ -5734,7 +5908,6 @@ function scanFiles(root, needle, budget, seen, out) {
|
|
|
5734
5908
|
} catch {
|
|
5735
5909
|
continue;
|
|
5736
5910
|
}
|
|
5737
|
-
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
5738
5911
|
for (const e of entries) {
|
|
5739
5912
|
if (e.name.startsWith(".") || IGNORE_DIRS.has(e.name)) continue;
|
|
5740
5913
|
const abs = path19.join(dir, e.name);
|
|
@@ -5742,28 +5915,48 @@ function scanFiles(root, needle, budget, seen, out) {
|
|
|
5742
5915
|
stack.push(abs);
|
|
5743
5916
|
continue;
|
|
5744
5917
|
}
|
|
5745
|
-
if (
|
|
5746
|
-
|
|
5747
|
-
try {
|
|
5748
|
-
if (fs19.statSync(abs).size > MAX_FILE_BYTES) continue;
|
|
5749
|
-
text = fs19.readFileSync(abs, "utf8");
|
|
5750
|
-
} catch {
|
|
5918
|
+
if (files.length >= MAX_FILES_SCANNED) {
|
|
5919
|
+
truncated = true;
|
|
5751
5920
|
continue;
|
|
5752
5921
|
}
|
|
5753
|
-
|
|
5754
|
-
const rel2 = path19.relative(root, abs);
|
|
5755
|
-
const lines = text.split("\n");
|
|
5756
|
-
for (let i = 0; i < lines.length; i++) {
|
|
5757
|
-
if (!lines[i].toLowerCase().includes(lower)) continue;
|
|
5758
|
-
const key = `${rel2}:${i + 1}`;
|
|
5759
|
-
if (seen.has(key)) continue;
|
|
5760
|
-
seen.add(key);
|
|
5761
|
-
out.push({ kind: "text", file: rel2, line: i + 1, preview: lines[i].trim().slice(0, PREVIEW_CHARS) });
|
|
5762
|
-
if (out.length >= budget) return true;
|
|
5763
|
-
}
|
|
5922
|
+
files.push(path19.relative(root, abs));
|
|
5764
5923
|
}
|
|
5765
5924
|
}
|
|
5766
|
-
|
|
5925
|
+
files.sort();
|
|
5926
|
+
return { files, truncated };
|
|
5927
|
+
}
|
|
5928
|
+
var rgAvailable;
|
|
5929
|
+
function hasRipgrep() {
|
|
5930
|
+
if (process.env.VG_DISABLE_RIPGREP) return false;
|
|
5931
|
+
if (rgAvailable === void 0) {
|
|
5932
|
+
try {
|
|
5933
|
+
rgAvailable = spawnSync("rg", ["--version"], { stdio: "ignore", timeout: 3e3 }).status === 0;
|
|
5934
|
+
} catch {
|
|
5935
|
+
rgAvailable = false;
|
|
5936
|
+
}
|
|
5937
|
+
}
|
|
5938
|
+
return rgAvailable;
|
|
5939
|
+
}
|
|
5940
|
+
function ripgrepCandidates(root, needle) {
|
|
5941
|
+
if (!hasRipgrep()) return null;
|
|
5942
|
+
const globs = [...IGNORE_DIRS].flatMap((d) => ["--glob", `!**/${d}/**`, "--glob", `!${d}/**`]);
|
|
5943
|
+
const res = spawnSync(
|
|
5944
|
+
"rg",
|
|
5945
|
+
["--files-with-matches", "--fixed-strings", "--ignore-case", "--no-ignore", "--no-messages", ...globs, "--", needle, "."],
|
|
5946
|
+
{ cwd: root, encoding: "utf8", timeout: 15e3, maxBuffer: 32 * 1024 * 1024 }
|
|
5947
|
+
);
|
|
5948
|
+
if (res.error || res.status !== 0 && res.status !== 1 || res.signal) return null;
|
|
5949
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5950
|
+
for (const raw of res.stdout.split("\n")) {
|
|
5951
|
+
const rel2 = raw.startsWith("./") ? raw.slice(2) : raw;
|
|
5952
|
+
if (!rel2) continue;
|
|
5953
|
+
const segs = rel2.split("/");
|
|
5954
|
+
if (segs.some((s) => s.startsWith(".") || IGNORE_DIRS.has(s))) continue;
|
|
5955
|
+
seen.add(rel2);
|
|
5956
|
+
}
|
|
5957
|
+
const files = [...seen].sort();
|
|
5958
|
+
if (files.length > MAX_FILES_SCANNED) return { files: files.slice(0, MAX_FILES_SCANNED), truncated: true };
|
|
5959
|
+
return { files, truncated: false };
|
|
5767
5960
|
}
|
|
5768
5961
|
|
|
5769
5962
|
// src/mcp/tools.ts
|
|
@@ -5882,16 +6075,16 @@ var TOOLS = [
|
|
|
5882
6075
|
},
|
|
5883
6076
|
{
|
|
5884
6077
|
name: "search_symbols",
|
|
5885
|
-
description: "Find a known name or string fast: ranked symbol lookup
|
|
6078
|
+
description: "Find a known name or literal string fast: ranked symbol lookup, plus a complete literal file-search for any quoted/multi-word phrase (config keys, log lines, UI copy). A phrase query reports totalTextMatches so you know you have every occurrence \u2014 use it instead of grep. Use first for most discovery; use query_graph for meaning.",
|
|
5886
6079
|
inputSchema: obj(
|
|
5887
6080
|
{
|
|
5888
|
-
query: { type: "string", maxLength: 120 },
|
|
5889
|
-
limit: { type: "number", description: "default 8, max
|
|
6081
|
+
query: { type: "string", maxLength: 120, description: "a symbol name, or a literal phrase to sweep for every occurrence of" },
|
|
6082
|
+
limit: { type: "number", description: "default 8, max 50 (raise it to fetch a whole literal sweep in one call)" }
|
|
5890
6083
|
},
|
|
5891
6084
|
["query"]
|
|
5892
6085
|
),
|
|
5893
|
-
handler: (graph, args, ctx) => {
|
|
5894
|
-
const limit = Math.min(
|
|
6086
|
+
handler: async (graph, args, ctx) => {
|
|
6087
|
+
const limit = Math.min(50, numOr(args.limit, 8));
|
|
5895
6088
|
return searchSymbols(graph, ctx.root, String(args.query ?? ""), limit);
|
|
5896
6089
|
}
|
|
5897
6090
|
},
|
|
@@ -6506,7 +6699,7 @@ function createServer(source, opts = {}) {
|
|
|
6506
6699
|
// Routing guidance once at the server level (hosts that surface
|
|
6507
6700
|
// `instructions` get it at zero per-step schema cost): the flashlight
|
|
6508
6701
|
// vs the map.
|
|
6509
|
-
instructions: 'vg is a code map. Use search_symbols to find a known name or literal string fast. Use orient/query_graph for meaning: symptoms, relationships, and what-breaks-if. Responses are concise by default; pass response_format:"detailed" only when a node proves load-bearing. Navigate as little as possible: one good search/query usually locates the code. As soon as you have the file and line, read that file and make the edit \u2014 do not call further graph tools unless the edit fails or the match was wrong.'
|
|
6702
|
+
instructions: 'vg is a code map. Use search_symbols to find a known name or literal string fast \u2014 a multi-word/quoted phrase runs a complete literal sweep and reports totalTextMatches, so reach for it instead of grep even for plain-string "find every occurrence" lookups. Use orient/query_graph for meaning: symptoms, relationships, and what-breaks-if. Responses are concise by default; pass response_format:"detailed" only when a node proves load-bearing. Navigate as little as possible: one good search/query usually locates the code. As soon as you have the file and line, read that file and make the edit \u2014 do not call further graph tools unless the edit fails or the match was wrong.'
|
|
6510
6703
|
}
|
|
6511
6704
|
);
|
|
6512
6705
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
@@ -7131,5 +7324,5 @@ function spdx(ctx) {
|
|
|
7131
7324
|
}
|
|
7132
7325
|
|
|
7133
7326
|
export { ASSISTANTS, CLI_TOOL_ALIASES, CliError, ECOSYSTEMS, ExitCode, FREE_PACK, GraphIndex, GraphSource, NPX_INVOCATION, ResourceLimitError, SCHEMA_VERSION, SKIP_DIRS, SKIP_FILES, TOOLS, UsageError, addLibrary, analyze, applyCoverage, applyStaticTestLinkage, assessDocQuality, assistantById, availableRegionIds, buildFacts, buildGraph, buildModuleResolver, cacheDir, catalogPath, clearModelCache, clearStoredCredentials, cosine, countPending, countTokens, coveringTests, createServer, createWorkspaceDsn, credentialsPath, dashHostForIngestHost, decodeScipIndex, defaultGraphPath, detectRunner, detectServeLaunch, discover, discoverModels, driftCount, driftFor, dsnCommand, embeddingsCached, enrichOnline, ensureGitignored, epistemicBreakdown, exportGraph, fetchHostedDocs, findGitRoot, findNodes, formatForExt, getNodeEmbeddings, gitignoreEntryForCredentials, groundGraph, hasDrift, identifierParts, impactOf, indexFor, ingestHostForRegionId, installAssistant, inventory, isModelReady, isTestFile, libDir, libId, loadCatalog, loadCoverage, loadEmbedder, loadGraph, loadSnapshot, localApiSurface, localPackageDocs, modelCacheInfo, nodeById, nodeEmbedText, parseDsn, parseGraph, parseJsonc, probeFreshness, publishPrivateLibrary, pushCommand, queryGraph, queryGraphSemantic, readDoc, readSavings, readScanArtifact, readUsage, recordCliCall, recordSaving, refreshIfStale, relativeResolver, renderHtml, renderReport, resolveCliInvocation, resolveDsn, resolveEmbedModel, resolveIngestHost, resolveLib, resolveLimits, resolveOne, resolveVersion, saveCatalog, savingsRecorded, scipEdges, selectForBudget, serializeGraph, serveStdio, shortestPath, stableStringify, symbolsFromApi, testsToRun, unavailableMessage, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, vibgrateDir, writeArtifacts, writeNavigationConfig, writeSnapshot, writeStoredCredentials };
|
|
7134
|
-
//# sourceMappingURL=chunk-
|
|
7135
|
-
//# sourceMappingURL=chunk-
|
|
7327
|
+
//# sourceMappingURL=chunk-3TCE6IGZ.js.map
|
|
7328
|
+
//# sourceMappingURL=chunk-3TCE6IGZ.js.map
|