@dreamtree-org/graphify 1.2.0 → 1.4.0
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/dist/{chunk-ZK3TA6FW.js → chunk-7LTO76UD.js} +46 -7
- package/dist/chunk-7LTO76UD.js.map +1 -0
- package/dist/{chunk-NS5HB65S.js → chunk-QEB7A5KB.js} +9 -4
- package/dist/{chunk-NS5HB65S.js.map → chunk-QEB7A5KB.js.map} +1 -1
- package/dist/cli/index.cjs +72 -16
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/index.cjs +204 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +125 -4
- package/dist/index.d.ts +125 -4
- package/dist/index.js +145 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.cjs +43 -6
- package/dist/mcp/server.cjs.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZK3TA6FW.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
runPipeline,
|
|
14
14
|
saveResult,
|
|
15
15
|
validateRules
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-QEB7A5KB.js";
|
|
17
17
|
import {
|
|
18
18
|
affectedBy,
|
|
19
19
|
buildContextPack,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
shortestPath,
|
|
26
26
|
testsForChangedFiles,
|
|
27
27
|
testsForNode
|
|
28
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-7LTO76UD.js";
|
|
29
29
|
import {
|
|
30
30
|
validateDsn,
|
|
31
31
|
validateUrl
|
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
ExtractionCache: () => ExtractionCache,
|
|
35
35
|
ExtractionResultSchema: () => ExtractionResultSchema,
|
|
36
36
|
ExtractionValidationError: () => ExtractionValidationError,
|
|
37
|
+
LocalFileGraphStore: () => LocalFileGraphStore,
|
|
37
38
|
affectedBy: () => affectedBy,
|
|
38
39
|
analyze: () => analyze,
|
|
39
40
|
buildContextPack: () => buildContextPack,
|
|
@@ -41,7 +42,9 @@ __export(index_exports, {
|
|
|
41
42
|
checkRules: () => checkRules,
|
|
42
43
|
cluster: () => cluster,
|
|
43
44
|
collectFiles: () => collectFiles,
|
|
45
|
+
deserializeGraph: () => deserializeGraph,
|
|
44
46
|
diffGraphs: () => diffGraphs,
|
|
47
|
+
expandRetrieval: () => expandRetrieval,
|
|
45
48
|
explainNode: () => explainNode,
|
|
46
49
|
exportGraph: () => exportGraph,
|
|
47
50
|
extract: () => extract,
|
|
@@ -64,6 +67,7 @@ __export(index_exports, {
|
|
|
64
67
|
runPipeline: () => runPipeline,
|
|
65
68
|
saveResult: () => saveResult,
|
|
66
69
|
scoreNodes: () => scoreNodes,
|
|
70
|
+
serializeGraph: () => serializeGraph,
|
|
67
71
|
shortestPath: () => shortestPath,
|
|
68
72
|
testsForChangedFiles: () => testsForChangedFiles,
|
|
69
73
|
testsForNode: () => testsForNode,
|
|
@@ -2570,7 +2574,7 @@ function renderReport(graph, analysis, now = /* @__PURE__ */ new Date()) {
|
|
|
2570
2574
|
|
|
2571
2575
|
// src/export.ts
|
|
2572
2576
|
var import_node_module2 = require("module");
|
|
2573
|
-
var
|
|
2577
|
+
var fs12 = __toESM(require("fs/promises"), 1);
|
|
2574
2578
|
var path6 = __toESM(require("path"), 1);
|
|
2575
2579
|
|
|
2576
2580
|
// src/security.ts
|
|
@@ -2645,6 +2649,44 @@ function escapeHtml(text) {
|
|
|
2645
2649
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2646
2650
|
}
|
|
2647
2651
|
|
|
2652
|
+
// src/store/localFile.ts
|
|
2653
|
+
var fs11 = __toESM(require("fs/promises"), 1);
|
|
2654
|
+
|
|
2655
|
+
// src/store/serialize.ts
|
|
2656
|
+
var import_graphology3 = __toESM(require("graphology"), 1);
|
|
2657
|
+
function serializeGraph(graph) {
|
|
2658
|
+
return graph.export();
|
|
2659
|
+
}
|
|
2660
|
+
function deserializeGraph(data) {
|
|
2661
|
+
return import_graphology3.default.from(data);
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
// src/store/localFile.ts
|
|
2665
|
+
var LocalFileGraphStore = class {
|
|
2666
|
+
async load(ref) {
|
|
2667
|
+
let jsonPath;
|
|
2668
|
+
try {
|
|
2669
|
+
jsonPath = validateGraphPath("graph.json", ref);
|
|
2670
|
+
} catch (error) {
|
|
2671
|
+
if (error.message.startsWith("Base directory does not exist")) return null;
|
|
2672
|
+
throw error;
|
|
2673
|
+
}
|
|
2674
|
+
let raw;
|
|
2675
|
+
try {
|
|
2676
|
+
raw = await fs11.readFile(jsonPath, "utf-8");
|
|
2677
|
+
} catch (error) {
|
|
2678
|
+
if (error.code === "ENOENT") return null;
|
|
2679
|
+
throw error;
|
|
2680
|
+
}
|
|
2681
|
+
return deserializeGraph(JSON.parse(raw));
|
|
2682
|
+
}
|
|
2683
|
+
async save(ref, graph) {
|
|
2684
|
+
await fs11.mkdir(ref, { recursive: true });
|
|
2685
|
+
const jsonPath = validateGraphPath("graph.json", ref);
|
|
2686
|
+
await fs11.writeFile(jsonPath, JSON.stringify(serializeGraph(graph), null, 2), "utf-8");
|
|
2687
|
+
}
|
|
2688
|
+
};
|
|
2689
|
+
|
|
2648
2690
|
// src/export.ts
|
|
2649
2691
|
var require3 = (0, import_node_module2.createRequire)(importMetaUrl);
|
|
2650
2692
|
function resolveVisNetworkAssets() {
|
|
@@ -2711,8 +2753,8 @@ community: ${communityLabel}` : ""}`,
|
|
|
2711
2753
|
async function renderHtml(graph) {
|
|
2712
2754
|
const { jsPath, cssPath } = resolveVisNetworkAssets();
|
|
2713
2755
|
const [visJs, visCss] = await Promise.all([
|
|
2714
|
-
|
|
2715
|
-
|
|
2756
|
+
fs12.readFile(jsPath, "utf-8"),
|
|
2757
|
+
fs12.readFile(cssPath, "utf-8")
|
|
2716
2758
|
]);
|
|
2717
2759
|
const { nodes, edges } = buildVisNodesAndEdges(graph);
|
|
2718
2760
|
const dataJson = safeInlineJson({ nodes, edges });
|
|
@@ -2753,16 +2795,15 @@ ${visJs}
|
|
|
2753
2795
|
}
|
|
2754
2796
|
async function exportGraph(graph, options, report) {
|
|
2755
2797
|
const outDir = path6.resolve(options.outDir);
|
|
2756
|
-
await
|
|
2757
|
-
|
|
2758
|
-
await fs11.writeFile(jsonPath, JSON.stringify(graph.toJSON(), null, 2), "utf-8");
|
|
2798
|
+
await fs12.mkdir(outDir, { recursive: true });
|
|
2799
|
+
await new LocalFileGraphStore().save(outDir, graph);
|
|
2759
2800
|
if (options.html !== false) {
|
|
2760
2801
|
const htmlPath = validateGraphPath("graph.html", outDir);
|
|
2761
|
-
await
|
|
2802
|
+
await fs12.writeFile(htmlPath, await renderHtml(graph), "utf-8");
|
|
2762
2803
|
}
|
|
2763
2804
|
if (report !== void 0) {
|
|
2764
2805
|
const reportPath = validateGraphPath("GRAPH_REPORT.md", outDir);
|
|
2765
|
-
await
|
|
2806
|
+
await fs12.writeFile(reportPath, report, "utf-8");
|
|
2766
2807
|
}
|
|
2767
2808
|
const notImplemented = [];
|
|
2768
2809
|
if (options.svg) notImplemented.push("--svg");
|
|
@@ -2775,12 +2816,12 @@ async function exportGraph(graph, options, report) {
|
|
|
2775
2816
|
}
|
|
2776
2817
|
|
|
2777
2818
|
// src/pipeline.ts
|
|
2778
|
-
var
|
|
2819
|
+
var fs14 = __toESM(require("fs/promises"), 1);
|
|
2779
2820
|
var path8 = __toESM(require("path"), 1);
|
|
2780
2821
|
|
|
2781
2822
|
// src/extractionCache.ts
|
|
2782
2823
|
var import_node_crypto3 = require("crypto");
|
|
2783
|
-
var
|
|
2824
|
+
var fs13 = __toESM(require("fs/promises"), 1);
|
|
2784
2825
|
var path7 = __toESM(require("path"), 1);
|
|
2785
2826
|
var ExtractionCache = class {
|
|
2786
2827
|
dir;
|
|
@@ -2792,7 +2833,7 @@ var ExtractionCache = class {
|
|
|
2792
2833
|
}
|
|
2793
2834
|
async get(key) {
|
|
2794
2835
|
try {
|
|
2795
|
-
const raw = await
|
|
2836
|
+
const raw = await fs13.readFile(path7.join(this.dir, `${key}.json`), "utf-8");
|
|
2796
2837
|
const parsed = JSON.parse(raw);
|
|
2797
2838
|
if (!Array.isArray(parsed.nodes) || !Array.isArray(parsed.edges)) return null;
|
|
2798
2839
|
return parsed;
|
|
@@ -2801,8 +2842,8 @@ var ExtractionCache = class {
|
|
|
2801
2842
|
}
|
|
2802
2843
|
}
|
|
2803
2844
|
async put(key, extraction) {
|
|
2804
|
-
await
|
|
2805
|
-
await
|
|
2845
|
+
await fs13.mkdir(this.dir, { recursive: true });
|
|
2846
|
+
await fs13.writeFile(path7.join(this.dir, `${key}.json`), JSON.stringify(extraction), "utf-8");
|
|
2806
2847
|
}
|
|
2807
2848
|
};
|
|
2808
2849
|
|
|
@@ -2824,7 +2865,7 @@ async function runPipeline(root, options = {}) {
|
|
|
2824
2865
|
for (const relFile of manifest.files.code.sort((a, b) => a.localeCompare(b))) {
|
|
2825
2866
|
const filePath = path8.relative(process.cwd(), path8.join(resolvedRoot, relFile)) || relFile;
|
|
2826
2867
|
try {
|
|
2827
|
-
const key = cache.key(relFile, await
|
|
2868
|
+
const key = cache.key(relFile, await fs14.readFile(path8.join(resolvedRoot, relFile)));
|
|
2828
2869
|
let extraction = options.update ? await cache.get(key) : null;
|
|
2829
2870
|
if (extraction) {
|
|
2830
2871
|
cacheHits++;
|
|
@@ -2874,19 +2915,23 @@ async function runPipeline(root, options = {}) {
|
|
|
2874
2915
|
},
|
|
2875
2916
|
report
|
|
2876
2917
|
);
|
|
2918
|
+
if (options.store) {
|
|
2919
|
+
const storeRef = options.storeRef ?? outDir;
|
|
2920
|
+
progress(`Saving graph to store (${storeRef}) ...`);
|
|
2921
|
+
await options.store.save(storeRef, graph);
|
|
2922
|
+
}
|
|
2877
2923
|
progress("Done.");
|
|
2878
2924
|
return { manifest, graph, analysis, report };
|
|
2879
2925
|
}
|
|
2880
2926
|
|
|
2881
2927
|
// src/graphStore.ts
|
|
2882
|
-
var fs14 = __toESM(require("fs/promises"), 1);
|
|
2883
2928
|
var path9 = __toESM(require("path"), 1);
|
|
2884
|
-
var import_graphology3 = __toESM(require("graphology"), 1);
|
|
2885
2929
|
async function loadGraph(outDir = path9.join(process.cwd(), "graphify-out")) {
|
|
2886
|
-
const
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2930
|
+
const graph = await new LocalFileGraphStore().load(outDir);
|
|
2931
|
+
if (graph === null) {
|
|
2932
|
+
throw new Error(`No graph found in ${outDir} \u2014 run \`graphify <path>\` to build one first.`);
|
|
2933
|
+
}
|
|
2934
|
+
return graph;
|
|
2890
2935
|
}
|
|
2891
2936
|
|
|
2892
2937
|
// src/query.ts
|
|
@@ -3156,6 +3201,141 @@ function explainNode(graph, query) {
|
|
|
3156
3201
|
};
|
|
3157
3202
|
}
|
|
3158
3203
|
|
|
3204
|
+
// src/retrieval.ts
|
|
3205
|
+
var DEFAULT_TOKEN_BUDGET2 = 2e3;
|
|
3206
|
+
var DEFAULT_MAX_HOPS = 2;
|
|
3207
|
+
function resolveSeeds(graph, hits) {
|
|
3208
|
+
const seeds = /* @__PURE__ */ new Map();
|
|
3209
|
+
for (const hit of hits) {
|
|
3210
|
+
let id = null;
|
|
3211
|
+
if (hit.nodeId !== void 0 && graph.hasNode(hit.nodeId)) {
|
|
3212
|
+
id = hit.nodeId;
|
|
3213
|
+
} else if (hit.text !== void 0) {
|
|
3214
|
+
id = resolveNode(graph, hit.text)?.id ?? null;
|
|
3215
|
+
}
|
|
3216
|
+
const key = id ?? hit.nodeId ?? hit.text ?? "";
|
|
3217
|
+
if (key === "") continue;
|
|
3218
|
+
const existing = seeds.get(key);
|
|
3219
|
+
if (existing) {
|
|
3220
|
+
if (hit.score !== void 0 && (existing.score === void 0 || hit.score > existing.score)) {
|
|
3221
|
+
existing.score = hit.score;
|
|
3222
|
+
}
|
|
3223
|
+
continue;
|
|
3224
|
+
}
|
|
3225
|
+
seeds.set(key, { id: key, score: hit.score, inGraph: id !== null, text: hit.text });
|
|
3226
|
+
}
|
|
3227
|
+
return [...seeds.values()];
|
|
3228
|
+
}
|
|
3229
|
+
function expandRetrieval(graph, hits, options = {}) {
|
|
3230
|
+
const tokenBudget = options.tokenBudget ?? DEFAULT_TOKEN_BUDGET2;
|
|
3231
|
+
const maxHops = options.maxHops ?? DEFAULT_MAX_HOPS;
|
|
3232
|
+
const relations = options.relations ? new Set(options.relations) : null;
|
|
3233
|
+
const seeds = resolveSeeds(graph, hits);
|
|
3234
|
+
const graphSeeds = seeds.filter((s) => s.inGraph);
|
|
3235
|
+
const seedScore = new Map(graphSeeds.map((s) => [s.id, s.score]));
|
|
3236
|
+
const hops = /* @__PURE__ */ new Map();
|
|
3237
|
+
const via = /* @__PURE__ */ new Map();
|
|
3238
|
+
const frontier = [];
|
|
3239
|
+
let spentTokens = 0;
|
|
3240
|
+
for (const seed of graphSeeds) {
|
|
3241
|
+
hops.set(seed.id, 0);
|
|
3242
|
+
frontier.push(seed.id);
|
|
3243
|
+
spentTokens += nodeTokenCost(graph, seed.id);
|
|
3244
|
+
}
|
|
3245
|
+
let truncated = false;
|
|
3246
|
+
while (frontier.length > 0) {
|
|
3247
|
+
const current = frontier.shift();
|
|
3248
|
+
const currentHops = hops.get(current);
|
|
3249
|
+
if (currentHops >= maxHops) continue;
|
|
3250
|
+
const candidates = [];
|
|
3251
|
+
graph.forEachEdge(current, (_key, attrs, source, target) => {
|
|
3252
|
+
const relation = String(attrs.relation);
|
|
3253
|
+
if (relations && !relations.has(relation)) return;
|
|
3254
|
+
const neighbor = source === current ? target : source;
|
|
3255
|
+
candidates.push({
|
|
3256
|
+
neighbor,
|
|
3257
|
+
edge: { source, target, relation, confidence: String(attrs.confidence) }
|
|
3258
|
+
});
|
|
3259
|
+
});
|
|
3260
|
+
candidates.sort((a, b) => a.neighbor.localeCompare(b.neighbor));
|
|
3261
|
+
for (const { neighbor, edge } of candidates) {
|
|
3262
|
+
if (hops.has(neighbor)) continue;
|
|
3263
|
+
const cost = nodeTokenCost(graph, neighbor);
|
|
3264
|
+
if (spentTokens + cost > tokenBudget) {
|
|
3265
|
+
truncated = true;
|
|
3266
|
+
continue;
|
|
3267
|
+
}
|
|
3268
|
+
spentTokens += cost;
|
|
3269
|
+
hops.set(neighbor, currentHops + 1);
|
|
3270
|
+
via.set(neighbor, edge);
|
|
3271
|
+
frontier.push(neighbor);
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
const degree = (id) => graph.degree(id);
|
|
3275
|
+
const nodes = [...hops.entries()].map(([id, hopCount]) => {
|
|
3276
|
+
const attrs = graph.getNodeAttributes(id);
|
|
3277
|
+
return {
|
|
3278
|
+
id,
|
|
3279
|
+
label: attrs.label ?? id,
|
|
3280
|
+
...attrs.kind !== void 0 ? { kind: String(attrs.kind) } : {},
|
|
3281
|
+
sourceFile: attrs.sourceFile ?? "",
|
|
3282
|
+
sourceLocation: attrs.sourceLocation ?? "",
|
|
3283
|
+
hops: hopCount,
|
|
3284
|
+
...via.has(id) ? { via: via.get(id) } : {},
|
|
3285
|
+
...hopCount === 0 && seedScore.get(id) !== void 0 ? { seedScore: seedScore.get(id) } : {}
|
|
3286
|
+
};
|
|
3287
|
+
});
|
|
3288
|
+
for (const seed of seeds) {
|
|
3289
|
+
if (seed.inGraph) continue;
|
|
3290
|
+
nodes.push({
|
|
3291
|
+
id: seed.id,
|
|
3292
|
+
label: seed.text ?? seed.id,
|
|
3293
|
+
sourceFile: "",
|
|
3294
|
+
sourceLocation: "",
|
|
3295
|
+
hops: 0,
|
|
3296
|
+
...seed.score !== void 0 ? { seedScore: seed.score } : {}
|
|
3297
|
+
});
|
|
3298
|
+
}
|
|
3299
|
+
nodes.sort((a, b) => {
|
|
3300
|
+
if (a.hops !== b.hops) return a.hops - b.hops;
|
|
3301
|
+
if (a.hops === 0) {
|
|
3302
|
+
return (b.seedScore ?? -Infinity) - (a.seedScore ?? -Infinity) || a.id.localeCompare(b.id);
|
|
3303
|
+
}
|
|
3304
|
+
const degreeDiff = (graph.hasNode(b.id) ? degree(b.id) : 0) - (graph.hasNode(a.id) ? degree(a.id) : 0);
|
|
3305
|
+
return degreeDiff || a.id.localeCompare(b.id);
|
|
3306
|
+
});
|
|
3307
|
+
const included = /* @__PURE__ */ new Set([...hops.keys()]);
|
|
3308
|
+
const edges = [];
|
|
3309
|
+
graph.forEachEdge((_key, attrs, source, target) => {
|
|
3310
|
+
if (!included.has(source) || !included.has(target)) return;
|
|
3311
|
+
const relation = String(attrs.relation);
|
|
3312
|
+
if (relations && !relations.has(relation)) return;
|
|
3313
|
+
edges.push({ source, target, relation, confidence: String(attrs.confidence) });
|
|
3314
|
+
});
|
|
3315
|
+
edges.sort(
|
|
3316
|
+
(a, b) => a.source.localeCompare(b.source) || a.target.localeCompare(b.target) || a.relation.localeCompare(b.relation)
|
|
3317
|
+
);
|
|
3318
|
+
const communities = /* @__PURE__ */ new Map();
|
|
3319
|
+
for (const id of included) {
|
|
3320
|
+
const attrs = graph.getNodeAttributes(id);
|
|
3321
|
+
const community = attrs.community;
|
|
3322
|
+
if (community === void 0) continue;
|
|
3323
|
+
const entry = communities.get(community) ?? {
|
|
3324
|
+
id: community,
|
|
3325
|
+
label: attrs.communityLabel ?? String(community),
|
|
3326
|
+
seedCount: 0
|
|
3327
|
+
};
|
|
3328
|
+
if (hops.get(id) === 0) entry.seedCount += 1;
|
|
3329
|
+
communities.set(community, entry);
|
|
3330
|
+
}
|
|
3331
|
+
return {
|
|
3332
|
+
nodes,
|
|
3333
|
+
edges,
|
|
3334
|
+
communities: [...communities.values()].sort((a, b) => b.seedCount - a.seedCount || a.id - b.id),
|
|
3335
|
+
truncated
|
|
3336
|
+
};
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3159
3339
|
// src/impact.ts
|
|
3160
3340
|
var DEPENDENCY_RELATIONS = /* @__PURE__ */ new Set([
|
|
3161
3341
|
"calls",
|
|
@@ -3951,6 +4131,7 @@ function checkRules(graph, config) {
|
|
|
3951
4131
|
ExtractionCache,
|
|
3952
4132
|
ExtractionResultSchema,
|
|
3953
4133
|
ExtractionValidationError,
|
|
4134
|
+
LocalFileGraphStore,
|
|
3954
4135
|
affectedBy,
|
|
3955
4136
|
analyze,
|
|
3956
4137
|
buildContextPack,
|
|
@@ -3958,7 +4139,9 @@ function checkRules(graph, config) {
|
|
|
3958
4139
|
checkRules,
|
|
3959
4140
|
cluster,
|
|
3960
4141
|
collectFiles,
|
|
4142
|
+
deserializeGraph,
|
|
3961
4143
|
diffGraphs,
|
|
4144
|
+
expandRetrieval,
|
|
3962
4145
|
explainNode,
|
|
3963
4146
|
exportGraph,
|
|
3964
4147
|
extract,
|
|
@@ -3981,6 +4164,7 @@ function checkRules(graph, config) {
|
|
|
3981
4164
|
runPipeline,
|
|
3982
4165
|
saveResult,
|
|
3983
4166
|
scoreNodes,
|
|
4167
|
+
serializeGraph,
|
|
3984
4168
|
shortestPath,
|
|
3985
4169
|
testsForChangedFiles,
|
|
3986
4170
|
testsForNode,
|