devlensio 0.3.0 → 0.4.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.
@@ -42,7 +42,6 @@ export function computeClusters(allNodes, allEdges, nodeScores) {
42
42
  queue.push({ nodeId: id, clusterId, rank: 0 });
43
43
  }
44
44
  }
45
- console.log(clusterId, rootId);
46
45
  };
47
46
  // 3. PHASE 1: Seed Entry Points (Structural Entry)
48
47
  allNodes
@@ -0,0 +1,38 @@
1
+ import type { CycleGroup } from "../summarizer/types.js";
2
+ import { CodeEdge, CodeNode, EdgeType } from "../types.js";
3
+ export interface GraphIndex {
4
+ nodesById: Map<string, CodeNode>;
5
+ forward: Map<string, CodeEdge[]>;
6
+ reverse: Map<string, CodeEdge[]>;
7
+ nodesByFilePath: Map<string, string[]>;
8
+ }
9
+ export declare function buildGraphIndex(nodes: CodeNode[], edges: CodeEdge[]): GraphIndex;
10
+ export interface TraversalHit {
11
+ nodeId: string;
12
+ hop: number;
13
+ viaEdge: EdgeType;
14
+ }
15
+ export interface TraversalResult {
16
+ seedId: string;
17
+ direction: "upstream" | "downstream";
18
+ hits: TraversalHit[];
19
+ truncated: boolean;
20
+ stoppedAtRadius: number;
21
+ hop1Count: number;
22
+ radiusUsed: number;
23
+ radiusWasExplicit: boolean;
24
+ }
25
+ export interface TraversalOpts {
26
+ radius?: number;
27
+ edgeTypes?: EdgeType[];
28
+ }
29
+ export declare function getBlastRadius(index: GraphIndex, seedId: string, opts?: TraversalOpts): TraversalResult;
30
+ export declare function getKHop(index: GraphIndex, seedId: string, opts?: TraversalOpts): TraversalResult;
31
+ export interface SubgraphResult {
32
+ seedNodeId: string;
33
+ clusterId: string;
34
+ nodes: CodeNode[];
35
+ edges: CodeEdge[];
36
+ }
37
+ export declare function getSubgraph(allNodes: CodeNode[], allEdges: CodeEdge[], nodeScores: Record<string, number>, seedNodeId: string): SubgraphResult | undefined;
38
+ export declare function findCycles(nodes: CodeNode[], edges: CodeEdge[]): CycleGroup[];
@@ -0,0 +1,99 @@
1
+ import { computeClusters } from "../clustering/index.js";
2
+ import { buildTopologicalOrder } from "../summarizer/topological.js";
3
+ export function buildGraphIndex(nodes, edges) {
4
+ const nodesById = new Map();
5
+ const forward = new Map();
6
+ const reverse = new Map();
7
+ const nodesByFilePath = new Map();
8
+ for (const node of nodes) {
9
+ nodesById.set(node.id, node);
10
+ const fp = node.filePath || "external";
11
+ if (!nodesByFilePath.has(fp))
12
+ nodesByFilePath.set(fp, []);
13
+ nodesByFilePath.get(fp).push(node.id);
14
+ }
15
+ for (const edge of edges) {
16
+ if (!forward.has(edge.from))
17
+ forward.set(edge.from, []);
18
+ forward.get(edge.from).push(edge);
19
+ if (!reverse.has(edge.to))
20
+ reverse.set(edge.to, []);
21
+ reverse.get(edge.to).push(edge);
22
+ }
23
+ return { nodesById, forward, reverse, nodesByFilePath };
24
+ }
25
+ const DEFAULT_RADIUS = 2;
26
+ const HOP1_CAP = 100;
27
+ function bfsTraverse(index, seedId, direction, opts = {}) {
28
+ const explicit = opts.radius !== undefined;
29
+ const maxRadius = opts.radius ?? DEFAULT_RADIUS;
30
+ const edgeFilter = opts.edgeTypes ? new Set(opts.edgeTypes) : null;
31
+ // downstream (callees) walks forward edges; upstream (callers) walks reverse
32
+ const adj = direction === "downstream" ? index.forward : index.reverse;
33
+ const visited = new Set([seedId]); // seed excluded from results
34
+ const hits = [];
35
+ let queue = [seedId];
36
+ let hop1Count = 0;
37
+ let truncated = false;
38
+ let stoppedAtRadius = 0;
39
+ for (let hop = 1; hop <= maxRadius; hop++) {
40
+ const nextqueue = [];
41
+ for (const nodeId of queue) {
42
+ for (const edge of adj.get(nodeId) ?? []) {
43
+ if (edgeFilter && !edgeFilter.has(edge.type))
44
+ continue;
45
+ const neighborId = direction === "downstream" ? edge.to : edge.from;
46
+ if (visited.has(neighborId))
47
+ continue;
48
+ visited.add(neighborId);
49
+ hits.push({ nodeId: neighborId, hop, viaEdge: edge.type });
50
+ nextqueue.push(neighborId);
51
+ }
52
+ }
53
+ if (hop === 1) {
54
+ hop1Count = nextqueue.length;
55
+ // Cap applies ONLY on the default (non-explicit) path:
56
+ // huge fanout → return hop-1 hits, skip deeper hops, flag truncated.
57
+ if (!explicit && hop1Count >= HOP1_CAP) {
58
+ truncated = true;
59
+ stoppedAtRadius = 1;
60
+ break;
61
+ }
62
+ }
63
+ stoppedAtRadius = hop;
64
+ queue = nextqueue;
65
+ if (queue.length === 0)
66
+ break; // nothing more to expand
67
+ }
68
+ return {
69
+ seedId, direction, hits, truncated, stoppedAtRadius, hop1Count,
70
+ radiusUsed: maxRadius, radiusWasExplicit: explicit,
71
+ };
72
+ }
73
+ // Upstream / dependents — "if I change this node, what is affected"
74
+ export function getBlastRadius(index, seedId, opts) {
75
+ return bfsTraverse(index, seedId, "upstream", opts);
76
+ }
77
+ // Downstream / dependencies — "what this node calls/uses"
78
+ export function getKHop(index, seedId, opts) {
79
+ return bfsTraverse(index, seedId, "downstream", opts);
80
+ }
81
+ export function getSubgraph(allNodes, allEdges, nodeScores, seedNodeId) {
82
+ const { clusterMembership } = computeClusters(allNodes, allEdges, nodeScores);
83
+ const clusterId = clusterMembership[seedNodeId];
84
+ if (!clusterId)
85
+ return undefined; // seed not found / not clustered
86
+ const memberIds = new Set();
87
+ for (const [nodeId, cId] of Object.entries(clusterMembership)) {
88
+ if (cId === clusterId)
89
+ memberIds.add(nodeId);
90
+ }
91
+ const nodes = allNodes.filter(n => memberIds.has(n.id));
92
+ const edges = allEdges.filter(e => memberIds.has(e.from) && memberIds.has(e.to));
93
+ return { seedNodeId, clusterId, nodes, edges };
94
+ }
95
+ // Cyclic dependency groups — reuses the connected-component detection that
96
+ // already runs inside the topological sort (src/summarizer/topological.ts).
97
+ export function findCycles(nodes, edges) {
98
+ return buildTopologicalOrder(nodes, edges).cycleGroups;
99
+ }
package/dist/index.d.ts CHANGED
@@ -16,3 +16,5 @@ export { runSummarization } from "./summarizer/index.js";
16
16
  export { computeClusters } from "./clustering/index.js";
17
17
  export { EDGE_LABELS } from "./summarizer/prompts.js";
18
18
  export { CycleGroup, TopologicalResult } from "./summarizer/types.js";
19
+ export { buildGraphIndex, getBlastRadius, getKHop, getSubgraph, findCycles, } from "./graph/traversal.js";
20
+ export type { GraphIndex, TraversalHit, TraversalResult, TraversalOpts, SubgraphResult, } from "./graph/traversal.js";
package/dist/index.js CHANGED
@@ -19,3 +19,5 @@ export { analyzePipeline } from "./pipeline/index.js";
19
19
  export { runSummarization } from "./summarizer/index.js";
20
20
  export { computeClusters } from "./clustering/index.js";
21
21
  export { EDGE_LABELS } from "./summarizer/prompts.js";
22
+ // Graph traversal & queries (consumed by the MCP server)
23
+ export { buildGraphIndex, getBlastRadius, getKHop, getSubgraph, findCycles, } from "./graph/traversal.js";
package/package.json CHANGED
@@ -1,48 +1,48 @@
1
- {
2
- "name": "devlensio",
3
- "version": "0.3.0",
4
- "description": "Codebase intelligence engine for TypeScript/JavaScript/Reactjs/Nextjs repositories.",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "import": "./dist/index.js",
12
- "types": "./dist/index.d.ts"
13
- }
14
- },
15
- "scripts": {
16
- "dev": "bun --watch src/server/index.ts",
17
- "start": "bun src/server/index.ts",
18
- "build": "tsc --project tsconfig.build.json",
19
- "test": "bun test",
20
- "export-graph": "bun src/debug/exportGraph.ts"
21
- },
22
- "keywords": ["codebase", "graph", "ast", "typescript", "javascript", "devtools"],
23
- "author": "",
24
- "license": "AGPL-3.0",
25
- "files": [
26
- "dist/**/*"
27
- ],
28
- "dependencies": {
29
- "@anthropic-ai/sdk": "^0.104.2",
30
- "@google/genai": "^2.8.0",
31
- "dotenv": "^17.4.2",
32
- "openai": "^6.42.0",
33
- "simple-git": "^3.36.0",
34
- "ts-morph": "^28.0.0",
35
- "uuid": "^11.0.0"
36
- },
37
- "devDependencies": {
38
- "@types/bun": "^1.3.9",
39
- "@types/jest": "^30.0.0",
40
- "@types/node": "^25.3.0",
41
- "@types/uuid": "^11.0.0",
42
- "jest": "^30.2.0",
43
- "ts-jest": "^29.4.6",
44
- "ts-node": "^10.9.2",
45
- "ts-node-dev": "^2.0.0",
46
- "typescript": "^5.9.3"
47
- }
1
+ {
2
+ "name": "devlensio",
3
+ "version": "0.4.1",
4
+ "description": "Codebase intelligence engine for TypeScript/JavaScript/Reactjs/Nextjs repositories.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "dev": "bun --watch src/server/index.ts",
17
+ "start": "bun src/server/index.ts",
18
+ "build": "tsc --project tsconfig.build.json",
19
+ "test": "bun test",
20
+ "export-graph": "bun src/debug/exportGraph.ts"
21
+ },
22
+ "keywords": ["codebase", "graph", "ast", "typescript", "javascript", "devtools"],
23
+ "author": "",
24
+ "license": "AGPL-3.0",
25
+ "files": [
26
+ "dist/**/*"
27
+ ],
28
+ "dependencies": {
29
+ "@anthropic-ai/sdk": "^0.104.2",
30
+ "@google/genai": "^2.8.0",
31
+ "dotenv": "^17.4.2",
32
+ "openai": "^6.42.0",
33
+ "simple-git": "^3.36.0",
34
+ "ts-morph": "^28.0.0",
35
+ "uuid": "^11.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/bun": "^1.3.9",
39
+ "@types/jest": "^30.0.0",
40
+ "@types/node": "^25.3.0",
41
+ "@types/uuid": "^11.0.0",
42
+ "jest": "^30.2.0",
43
+ "ts-jest": "^29.4.6",
44
+ "ts-node": "^10.9.2",
45
+ "ts-node-dev": "^2.0.0",
46
+ "typescript": "^5.9.3"
47
+ }
48
48
  }