@exero1/claudecontext 0.1.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.
Files changed (78) hide show
  1. package/README.md +286 -0
  2. package/dist/installer/install.d.ts +12 -0
  3. package/dist/installer/install.d.ts.map +1 -0
  4. package/dist/installer/install.js +714 -0
  5. package/dist/installer/install.js.map +1 -0
  6. package/dist/src/cache/budget.d.ts +48 -0
  7. package/dist/src/cache/budget.d.ts.map +1 -0
  8. package/dist/src/cache/budget.js +55 -0
  9. package/dist/src/cache/budget.js.map +1 -0
  10. package/dist/src/cache/compressor.d.ts +21 -0
  11. package/dist/src/cache/compressor.d.ts.map +1 -0
  12. package/dist/src/cache/compressor.js +89 -0
  13. package/dist/src/cache/compressor.js.map +1 -0
  14. package/dist/src/cache/levels.d.ts +16 -0
  15. package/dist/src/cache/levels.d.ts.map +1 -0
  16. package/dist/src/cache/levels.js +41 -0
  17. package/dist/src/cache/levels.js.map +1 -0
  18. package/dist/src/cache/manager.d.ts +38 -0
  19. package/dist/src/cache/manager.d.ts.map +1 -0
  20. package/dist/src/cache/manager.js +196 -0
  21. package/dist/src/cache/manager.js.map +1 -0
  22. package/dist/src/cli.d.ts +3 -0
  23. package/dist/src/cli.d.ts.map +1 -0
  24. package/dist/src/cli.js +279 -0
  25. package/dist/src/cli.js.map +1 -0
  26. package/dist/src/detection/areas.d.ts +13 -0
  27. package/dist/src/detection/areas.d.ts.map +1 -0
  28. package/dist/src/detection/areas.js +96 -0
  29. package/dist/src/detection/areas.js.map +1 -0
  30. package/dist/src/detection/task.d.ts +28 -0
  31. package/dist/src/detection/task.d.ts.map +1 -0
  32. package/dist/src/detection/task.js +77 -0
  33. package/dist/src/detection/task.js.map +1 -0
  34. package/dist/src/gating/gate.d.ts +38 -0
  35. package/dist/src/gating/gate.d.ts.map +1 -0
  36. package/dist/src/gating/gate.js +74 -0
  37. package/dist/src/gating/gate.js.map +1 -0
  38. package/dist/src/graph/edges.d.ts +41 -0
  39. package/dist/src/graph/edges.d.ts.map +1 -0
  40. package/dist/src/graph/edges.js +115 -0
  41. package/dist/src/graph/edges.js.map +1 -0
  42. package/dist/src/graph/indexer.d.ts +38 -0
  43. package/dist/src/graph/indexer.d.ts.map +1 -0
  44. package/dist/src/graph/indexer.js +228 -0
  45. package/dist/src/graph/indexer.js.map +1 -0
  46. package/dist/src/graph/traversal.d.ts +25 -0
  47. package/dist/src/graph/traversal.d.ts.map +1 -0
  48. package/dist/src/graph/traversal.js +173 -0
  49. package/dist/src/graph/traversal.js.map +1 -0
  50. package/dist/src/index.d.ts +3 -0
  51. package/dist/src/index.d.ts.map +1 -0
  52. package/dist/src/index.js +82 -0
  53. package/dist/src/index.js.map +1 -0
  54. package/dist/src/indexing/codebase.d.ts +30 -0
  55. package/dist/src/indexing/codebase.d.ts.map +1 -0
  56. package/dist/src/indexing/codebase.js +127 -0
  57. package/dist/src/indexing/codebase.js.map +1 -0
  58. package/dist/src/markdown/writer.d.ts +34 -0
  59. package/dist/src/markdown/writer.d.ts.map +1 -0
  60. package/dist/src/markdown/writer.js +96 -0
  61. package/dist/src/markdown/writer.js.map +1 -0
  62. package/dist/src/server.d.ts +15 -0
  63. package/dist/src/server.d.ts.map +1 -0
  64. package/dist/src/server.js +520 -0
  65. package/dist/src/server.js.map +1 -0
  66. package/dist/src/storage/db.d.ts +123 -0
  67. package/dist/src/storage/db.d.ts.map +1 -0
  68. package/dist/src/storage/db.js +318 -0
  69. package/dist/src/storage/db.js.map +1 -0
  70. package/dist/src/utils/glob.d.ts +11 -0
  71. package/dist/src/utils/glob.d.ts.map +1 -0
  72. package/dist/src/utils/glob.js +20 -0
  73. package/dist/src/utils/glob.js.map +1 -0
  74. package/hooks/post-write.mjs +57 -0
  75. package/hooks/pre-compact.mjs +44 -0
  76. package/hooks/pre-tool-use.mjs +87 -0
  77. package/hooks/session-start.mjs +54 -0
  78. package/package.json +51 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../src/gating/gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { Db, Edge, EdgeType, NodeType } from '../storage/db.js';
2
+ /**
3
+ * Edge weight decay formula — half-life ~70 days.
4
+ * Applied lazily on next write, never on read.
5
+ */
6
+ export declare function decayWeight(weight: number, lastReinforced: number, now?: number): number;
7
+ export interface EdgeCreateOptions {
8
+ fromNode: string;
9
+ fromType: NodeType;
10
+ toNode: string;
11
+ toType: NodeType;
12
+ edgeType: EdgeType;
13
+ weight?: number;
14
+ }
15
+ /**
16
+ * Create or reinforce an edge.
17
+ * If the edge already exists: apply lazy decay first, then reinforce (+0.1, capped at 1.0).
18
+ * If new: insert with weight 1.0.
19
+ */
20
+ export declare function upsertEdge(db: Db, opts: EdgeCreateOptions): void;
21
+ /**
22
+ * Create a bidirectional edge pair (e.g. co_modified).
23
+ */
24
+ export declare function upsertBidirectionalEdge(db: Db, opts: EdgeCreateOptions): void;
25
+ /**
26
+ * Apply lazy weight decay to ALL edges in the database.
27
+ * Called during pre-compact sweep (not during reads).
28
+ */
29
+ export declare function applyDecayToAll(db: Db): {
30
+ updated: number;
31
+ pruned: number;
32
+ };
33
+ /**
34
+ * Get all direct neighbors of a node, filtered by edge type.
35
+ */
36
+ export declare function getNeighbors(db: Db, node: string, edgeTypes?: EdgeType[], limit?: number): Edge[];
37
+ /**
38
+ * Get all neighbors of multiple nodes (for 1-hop bundle expansion).
39
+ */
40
+ export declare function getNeighborsForNodes(db: Db, nodes: string[], edgeTypes?: EdgeType[], limitPerNode?: number): Map<string, Edge[]>;
41
+ //# sourceMappingURL=edges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edges.d.ts","sourceRoot":"","sources":["../../../src/graph/edges.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAErE;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,MAAM,CAG5F;AAOD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAmChE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAS7E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA4B3E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,QAAQ,EAAE,EACtB,KAAK,SAAK,GACT,IAAI,EAAE,CAMR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,MAAM,EAAE,EACf,SAAS,CAAC,EAAE,QAAQ,EAAE,EACtB,YAAY,SAAK,GAChB,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAMrB"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Edge weight decay formula — half-life ~70 days.
3
+ * Applied lazily on next write, never on read.
4
+ */
5
+ export function decayWeight(weight, lastReinforced, now = Date.now()) {
6
+ const daysSince = (now - lastReinforced) / (24 * 60 * 60 * 1000);
7
+ return weight * Math.exp(-0.01 * daysSince);
8
+ }
9
+ // Use \x01 (SOH) delimiter — safe in node:sqlite, cannot appear in file paths or edge type names
10
+ function edgeId(fromNode, toNode, edgeType) {
11
+ return `${edgeType}\x01${fromNode}\x01${toNode}`;
12
+ }
13
+ /**
14
+ * Create or reinforce an edge.
15
+ * If the edge already exists: apply lazy decay first, then reinforce (+0.1, capped at 1.0).
16
+ * If new: insert with weight 1.0.
17
+ */
18
+ export function upsertEdge(db, opts) {
19
+ const id = edgeId(opts.fromNode, opts.toNode, opts.edgeType);
20
+ const now = Date.now();
21
+ const existing = db.getEdge(id);
22
+ if (existing) {
23
+ // Apply lazy decay then reinforce
24
+ const decayed = decayWeight(existing.weight, existing.last_reinforced, now);
25
+ const newWeight = Math.min(1.0, decayed + 0.1);
26
+ const newCount = existing.co_occurrence_count + 1;
27
+ const newConfidence = Math.min(1.0, newCount / (newCount + 2)); // Bayesian-style confidence
28
+ db.upsertEdge({
29
+ ...existing,
30
+ weight: newWeight,
31
+ last_reinforced: now,
32
+ session_count: existing.session_count + 1,
33
+ co_occurrence_count: newCount,
34
+ confidence: newConfidence,
35
+ });
36
+ }
37
+ else {
38
+ db.upsertEdge({
39
+ id,
40
+ from_node: opts.fromNode,
41
+ from_type: opts.fromType,
42
+ to_node: opts.toNode,
43
+ to_type: opts.toType,
44
+ edge_type: opts.edgeType,
45
+ weight: opts.weight ?? 1.0,
46
+ created_at: now,
47
+ last_reinforced: now,
48
+ session_count: 1,
49
+ co_occurrence_count: 1,
50
+ confidence: 1.0,
51
+ });
52
+ }
53
+ }
54
+ /**
55
+ * Create a bidirectional edge pair (e.g. co_modified).
56
+ */
57
+ export function upsertBidirectionalEdge(db, opts) {
58
+ upsertEdge(db, opts);
59
+ upsertEdge(db, {
60
+ ...opts,
61
+ fromNode: opts.toNode,
62
+ fromType: opts.toType,
63
+ toNode: opts.fromNode,
64
+ toType: opts.fromType,
65
+ });
66
+ }
67
+ /**
68
+ * Apply lazy weight decay to ALL edges in the database.
69
+ * Called during pre-compact sweep (not during reads).
70
+ */
71
+ export function applyDecayToAll(db) {
72
+ const now = Date.now();
73
+ const all = db.getAllEdgesForDecay();
74
+ const updates = [];
75
+ for (const edge of all) {
76
+ const newWeight = decayWeight(edge.weight, edge.last_reinforced, now);
77
+ if (newWeight !== edge.weight) {
78
+ updates.push({ id: edge.id, weight: newWeight, last_reinforced: edge.last_reinforced });
79
+ }
80
+ }
81
+ if (updates.length > 0) {
82
+ // Apply decayed weights first, preserving original last_reinforced so pruning
83
+ // can correctly check edge age in the same pass.
84
+ db.bulkUpdateEdgeWeights(updates);
85
+ }
86
+ // Prune BEFORE resetting last_reinforced so the age check uses original timestamps.
87
+ const pruned = db.pruneWeakEdges(0.1, 90);
88
+ if (updates.length > 0) {
89
+ // Now reset last_reinforced to now to prevent double-applying decay on next sweep.
90
+ const withNow = updates.map(u => ({ ...u, last_reinforced: now }));
91
+ db.bulkUpdateEdgeWeights(withNow);
92
+ }
93
+ return { updated: updates.length, pruned };
94
+ }
95
+ /**
96
+ * Get all direct neighbors of a node, filtered by edge type.
97
+ */
98
+ export function getNeighbors(db, node, edgeTypes, limit = 20) {
99
+ const edges = db.getEdgesFrom(node, limit * 2);
100
+ const filtered = edgeTypes
101
+ ? edges.filter(e => edgeTypes.includes(e.edge_type))
102
+ : edges;
103
+ return filtered.slice(0, limit);
104
+ }
105
+ /**
106
+ * Get all neighbors of multiple nodes (for 1-hop bundle expansion).
107
+ */
108
+ export function getNeighborsForNodes(db, nodes, edgeTypes, limitPerNode = 10) {
109
+ const result = new Map();
110
+ for (const node of nodes) {
111
+ result.set(node, getNeighbors(db, node, edgeTypes, limitPerNode));
112
+ }
113
+ return result;
114
+ }
115
+ //# sourceMappingURL=edges.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edges.js","sourceRoot":"","sources":["../../../src/graph/edges.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { Db } from '../storage/db.js';
2
+ export interface AreaDef {
3
+ name: string;
4
+ globs: string[];
5
+ docPath: string;
6
+ }
7
+ /**
8
+ * Extract import paths from JS/TS source using acorn.
9
+ * Falls back to regex if acorn parse fails (e.g., JSX, complex TS generics).
10
+ */
11
+ export declare function extractImports(source: string): string[];
12
+ /**
13
+ * Resolve a relative import path to an absolute file path.
14
+ * Handles .js extension aliasing TypeScript files.
15
+ * Returns null if the import is a bare specifier (npm package).
16
+ */
17
+ export declare function resolveImport(importPath: string, sourceFile: string, projectRoot: string): string | null;
18
+ /**
19
+ * Index a single file: create import edges for all its imports.
20
+ */
21
+ export declare function indexFileImports(db: Db, filePath: string, projectRoot: string): void;
22
+ /**
23
+ * Index a file's subsystem membership: create subsystem_of edges.
24
+ * filePath may be relative (relative to projectRoot) or absolute.
25
+ * Glob matching uses the relative path; fromNode is always stored as absolute.
26
+ */
27
+ export declare function indexFileSubsystem(db: Db, filePath: string, areas: AreaDef[], projectRoot: string): void;
28
+ /**
29
+ * Walk a directory and collect all JS/TS source files.
30
+ */
31
+ export declare function collectSourceFiles(dir: string, projectRoot: string, maxDepth?: number, depth?: number): string[];
32
+ /**
33
+ * Full project index: scan all source files, create imports + subsystem_of edges.
34
+ */
35
+ export declare function indexProject(db: Db, projectRoot: string, areas: AreaDef[]): {
36
+ indexed: number;
37
+ };
38
+ //# sourceMappingURL=indexer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../../../src/graph/indexer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAI3C,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAqCvD;AAiDD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA4BxG;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CA2BpF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAgBxG;AAOD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,SAAI,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE,CAgCtG;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAO/F"}
@@ -0,0 +1,228 @@
1
+ import { parse as acornParse } from 'acorn';
2
+ import { simple as acornWalk } from 'acorn-walk';
3
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
4
+ import { join, resolve, relative, extname, dirname } from 'node:path';
5
+ import { upsertEdge } from './edges.js';
6
+ import { matchGlob } from '../utils/glob.js';
7
+ /**
8
+ * Extract import paths from JS/TS source using acorn.
9
+ * Falls back to regex if acorn parse fails (e.g., JSX, complex TS generics).
10
+ */
11
+ export function extractImports(source) {
12
+ const imports = [];
13
+ // Try acorn parse (strip TS type annotations first with regex to improve parse rate)
14
+ const stripped = stripTypeAnnotations(source);
15
+ try {
16
+ const ast = acornParse(stripped, {
17
+ ecmaVersion: 'latest',
18
+ sourceType: 'module',
19
+ });
20
+ acornWalk(ast, {
21
+ ImportDeclaration(node) {
22
+ const n = node;
23
+ if (typeof n.source?.value === 'string')
24
+ imports.push(n.source.value);
25
+ },
26
+ ExportNamedDeclaration(node) {
27
+ const n = node;
28
+ if (n.source && typeof n.source.value === 'string')
29
+ imports.push(n.source.value);
30
+ },
31
+ ExportAllDeclaration(node) {
32
+ const n = node;
33
+ if (typeof n.source?.value === 'string')
34
+ imports.push(n.source.value);
35
+ },
36
+ });
37
+ }
38
+ catch {
39
+ // Acorn failed — regex will handle it below
40
+ }
41
+ // Always run regex to catch require() and dynamic imports acorn misses in CommonJS files
42
+ for (const i of extractImportsRegex(source)) {
43
+ if (!imports.includes(i))
44
+ imports.push(i);
45
+ }
46
+ return imports;
47
+ }
48
+ function stripTypeAnnotations(source) {
49
+ // Strip TypeScript-specific syntax that acorn can't parse.
50
+ // IMPORTANT: Preserve string literals first (e.g. 'node:fs') so the colon-strip
51
+ // regex doesn't corrupt protocol-style import specifiers.
52
+ const stringLiterals = [];
53
+ const withPlaceholders = source.replace(/(["'])(?:[^\\]|\\.)*?\1/g, (match) => {
54
+ const idx = stringLiterals.length;
55
+ stringLiterals.push(match);
56
+ return `"__CC_STR_${idx}__"`;
57
+ });
58
+ const stripped = withPlaceholders
59
+ // Remove type/interface declarations
60
+ .replace(/^\s*(export\s+)?(declare\s+)?(type|interface)\s+\w[^{=;]*(=|{)[^;]*;?\s*$/gm, '')
61
+ // Remove generic type params (both upper and lowercase: <T>, <string>, <MyType>)
62
+ .replace(/<[A-Za-z_$][A-Za-z0-9,\s<>[\]|&?]*>/g, '')
63
+ // Remove type annotations after colon (handles primitives: string, number, boolean, any, etc.)
64
+ .replace(/:\s*[A-Za-z_$][A-Za-z0-9<>[\]|&?,\s]*/g, '')
65
+ // Remove 'as TypeName' casts
66
+ .replace(/\bas\s+[A-Za-z_$][A-Za-z0-9<>[\]|&?,\s]*/g, '');
67
+ // Restore string literals
68
+ return stripped.replace(/"__CC_STR_(\d+)__"/g, (_, idx) => {
69
+ return stringLiterals[parseInt(idx, 10)] ?? '""';
70
+ });
71
+ }
72
+ function extractImportsRegex(source) {
73
+ const imports = [];
74
+ // Match: import ... from '...' or require('...')
75
+ const staticImport = /import\s+(?:[^'"]+\s+from\s+)?['"]([^'"]+)['"]/g;
76
+ const dynamicImport = /import\(['"]([^'"]+)['"]\)/g;
77
+ const requireCall = /require\(['"]([^'"]+)['"]\)/g;
78
+ let m;
79
+ while ((m = staticImport.exec(source)) !== null) {
80
+ if (m[1])
81
+ imports.push(m[1]);
82
+ }
83
+ while ((m = dynamicImport.exec(source)) !== null) {
84
+ if (m[1])
85
+ imports.push(m[1]);
86
+ }
87
+ while ((m = requireCall.exec(source)) !== null) {
88
+ if (m[1])
89
+ imports.push(m[1]);
90
+ }
91
+ return imports;
92
+ }
93
+ /**
94
+ * Resolve a relative import path to an absolute file path.
95
+ * Handles .js extension aliasing TypeScript files.
96
+ * Returns null if the import is a bare specifier (npm package).
97
+ */
98
+ export function resolveImport(importPath, sourceFile, projectRoot) {
99
+ // Bare specifier = npm package, skip
100
+ if (!importPath.startsWith('.') && !importPath.startsWith('/'))
101
+ return null;
102
+ const sourceDir = dirname(sourceFile);
103
+ const resolved = resolve(sourceDir, importPath);
104
+ // Strip .js extension to get the base path (TS files are imported as .js in ESM)
105
+ const base = resolved.replace(/\.js$/, '');
106
+ // Try extensions in priority order
107
+ const candidates = [
108
+ base + '.ts',
109
+ base + '.tsx',
110
+ base + '.js',
111
+ base + '.jsx',
112
+ base + '/index.ts',
113
+ base + '/index.js',
114
+ resolved, // exact match as-is (e.g. already .ts, or extensionless)
115
+ ];
116
+ for (const candidate of candidates) {
117
+ if (existsSync(candidate)) {
118
+ return relative(projectRoot, candidate);
119
+ }
120
+ }
121
+ return null;
122
+ }
123
+ /**
124
+ * Index a single file: create import edges for all its imports.
125
+ */
126
+ export function indexFileImports(db, filePath, projectRoot) {
127
+ const absolutePath = resolve(projectRoot, filePath);
128
+ if (!existsSync(absolutePath))
129
+ return;
130
+ const ext = extname(absolutePath);
131
+ if (!['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].includes(ext))
132
+ return;
133
+ let source;
134
+ try {
135
+ source = readFileSync(absolutePath, 'utf8');
136
+ }
137
+ catch {
138
+ return;
139
+ }
140
+ const importPaths = extractImports(source);
141
+ for (const importPath of importPaths) {
142
+ const resolvedRelative = resolveImport(importPath, absolutePath, projectRoot);
143
+ if (resolvedRelative) {
144
+ upsertEdge(db, {
145
+ fromNode: absolutePath,
146
+ fromType: 'file',
147
+ toNode: resolve(projectRoot, resolvedRelative),
148
+ toType: 'file',
149
+ edgeType: 'imports',
150
+ });
151
+ }
152
+ }
153
+ }
154
+ /**
155
+ * Index a file's subsystem membership: create subsystem_of edges.
156
+ * filePath may be relative (relative to projectRoot) or absolute.
157
+ * Glob matching uses the relative path; fromNode is always stored as absolute.
158
+ */
159
+ export function indexFileSubsystem(db, filePath, areas, projectRoot) {
160
+ const absolutePath = resolve(projectRoot, filePath);
161
+ const relativePath = relative(projectRoot, absolutePath);
162
+ for (const area of areas) {
163
+ const matches = area.globs.some(glob => matchGlob(relativePath, glob));
164
+ if (matches) {
165
+ upsertEdge(db, {
166
+ fromNode: absolutePath,
167
+ fromType: 'file',
168
+ toNode: area.name,
169
+ toType: 'area',
170
+ edgeType: 'subsystem_of',
171
+ weight: 1.0,
172
+ });
173
+ }
174
+ }
175
+ }
176
+ const SKIP_DIRS = new Set([
177
+ 'node_modules', '.git', 'dist', 'build', '.next', '.nuxt',
178
+ 'coverage', '.cache', '__pycache__', '.claudecontext',
179
+ ]);
180
+ /**
181
+ * Walk a directory and collect all JS/TS source files.
182
+ */
183
+ export function collectSourceFiles(dir, projectRoot, maxDepth = 6, depth = 0) {
184
+ if (depth > maxDepth)
185
+ return [];
186
+ const files = [];
187
+ let entries;
188
+ try {
189
+ entries = readdirSync(dir);
190
+ }
191
+ catch {
192
+ return [];
193
+ }
194
+ for (const entry of entries) {
195
+ if (SKIP_DIRS.has(entry))
196
+ continue;
197
+ const fullPath = join(dir, entry);
198
+ let stat;
199
+ try {
200
+ stat = statSync(fullPath);
201
+ }
202
+ catch {
203
+ continue;
204
+ }
205
+ if (stat.isDirectory()) {
206
+ files.push(...collectSourceFiles(fullPath, projectRoot, maxDepth, depth + 1));
207
+ }
208
+ else {
209
+ const ext = extname(entry);
210
+ if (['.ts', '.tsx', '.js', '.jsx', '.mjs'].includes(ext)) {
211
+ files.push(relative(projectRoot, fullPath));
212
+ }
213
+ }
214
+ }
215
+ return files;
216
+ }
217
+ /**
218
+ * Full project index: scan all source files, create imports + subsystem_of edges.
219
+ */
220
+ export function indexProject(db, projectRoot, areas) {
221
+ const files = collectSourceFiles(projectRoot, projectRoot);
222
+ for (const file of files) {
223
+ indexFileImports(db, file, projectRoot);
224
+ indexFileSubsystem(db, file, areas, projectRoot);
225
+ }
226
+ return { indexed: files.length };
227
+ }
228
+ //# sourceMappingURL=indexer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../../src/graph/indexer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Db } from '../storage/db.js';
2
+ export interface TraversalNode {
3
+ node: string;
4
+ nodeType: string;
5
+ content: string;
6
+ tokenCount: number;
7
+ discoveredVia: string;
8
+ weight: number;
9
+ }
10
+ export interface TraversalResult {
11
+ nodes: TraversalNode[];
12
+ tokensUsed: number;
13
+ }
14
+ /**
15
+ * V1 traversal: 1-hop direct neighbor fetch.
16
+ * Fetches direct imports/subsystem_of/co_modified neighbors of the given root files.
17
+ * Budget-bounded — stops adding nodes when tokenBudget is exhausted.
18
+ */
19
+ export declare function expandOneHop(db: Db, projectRoot: string, rootFiles: string[], tokenBudget: number): TraversalResult;
20
+ /**
21
+ * V2 traversal scaffold: priority-queue BFS up to maxDepth hops.
22
+ * Gated behind depth > 1. Only used in V2.
23
+ */
24
+ export declare function expandBFS(db: Db, projectRoot: string, rootFiles: string[], tokenBudget: number, maxDepth: number): TraversalResult;
25
+ //# sourceMappingURL=traversal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traversal.d.ts","sourceRoot":"","sources":["../../../src/graph/traversal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAQ,MAAM,kBAAkB,CAAC;AAmBjD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,EAAE,EACN,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,MAAM,GAClB,eAAe,CAoDjB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,EAAE,EAAE,EAAE,EACN,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,eAAe,CA6EjB"}
@@ -0,0 +1,173 @@
1
+ import { estimateTokens } from '../cache/budget.js';
2
+ import { readFileSync, existsSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ /**
5
+ * Composite priority: edge weight × frequency boost × recency decay of destination file.
6
+ * Higher touch_count and more recently touched files rank higher.
7
+ */
8
+ function filePriority(db, edge) {
9
+ const fi = db.getFile(edge.to_node);
10
+ const tc = fi?.touch_count ?? 0;
11
+ const lt = fi?.last_touched ?? 0;
12
+ const daysSince = (Date.now() - lt) / 86_400_000;
13
+ const recency = Math.exp(-0.05 * daysSince);
14
+ const freq = Math.log(tc + 1) / Math.log(10);
15
+ return edge.weight * (1 + freq) * recency;
16
+ }
17
+ /**
18
+ * V1 traversal: 1-hop direct neighbor fetch.
19
+ * Fetches direct imports/subsystem_of/co_modified neighbors of the given root files.
20
+ * Budget-bounded — stops adding nodes when tokenBudget is exhausted.
21
+ */
22
+ export function expandOneHop(db, projectRoot, rootFiles, tokenBudget) {
23
+ const visited = new Set(rootFiles);
24
+ const nodes = [];
25
+ let tokensUsed = 0;
26
+ // Collect all neighbor edges from root files, sorted by weight descending
27
+ const candidateEdges = [];
28
+ for (const file of rootFiles) {
29
+ const edges = db.getEdgesFrom(file, 50);
30
+ candidateEdges.push(...edges);
31
+ }
32
+ candidateEdges.sort((a, b) => filePriority(db, b) - filePriority(db, a));
33
+ for (const edge of candidateEdges) {
34
+ if (tokensUsed >= tokenBudget)
35
+ break;
36
+ if (visited.has(edge.to_node))
37
+ continue;
38
+ visited.add(edge.to_node);
39
+ const content = loadNodeContent(edge.to_node, edge.to_type, projectRoot);
40
+ if (!content)
41
+ continue;
42
+ const tokenCount = estimateTokens(content);
43
+ if (tokensUsed + tokenCount > tokenBudget) {
44
+ // Try to fit a truncated version
45
+ const remaining = tokenBudget - tokensUsed;
46
+ if (remaining < 100)
47
+ break; // Not worth including a tiny snippet
48
+ const truncated = content.slice(0, Math.floor(remaining * 3.5 / 1.15));
49
+ nodes.push({
50
+ node: edge.to_node,
51
+ nodeType: edge.to_type,
52
+ content: truncated + '\n[... truncated ...]',
53
+ tokenCount: estimateTokens(truncated),
54
+ discoveredVia: `via ${edge.edge_type} from ${edge.from_node} (weight=${edge.weight.toFixed(2)})`,
55
+ weight: edge.weight,
56
+ });
57
+ tokensUsed += estimateTokens(truncated);
58
+ break;
59
+ }
60
+ nodes.push({
61
+ node: edge.to_node,
62
+ nodeType: edge.to_type,
63
+ content,
64
+ tokenCount,
65
+ discoveredVia: `via ${edge.edge_type} from ${edge.from_node} (weight=${edge.weight.toFixed(2)})`,
66
+ weight: edge.weight,
67
+ });
68
+ tokensUsed += tokenCount;
69
+ }
70
+ return { nodes, tokensUsed };
71
+ }
72
+ /**
73
+ * V2 traversal scaffold: priority-queue BFS up to maxDepth hops.
74
+ * Gated behind depth > 1. Only used in V2.
75
+ */
76
+ export function expandBFS(db, projectRoot, rootFiles, tokenBudget, maxDepth) {
77
+ if (maxDepth <= 1) {
78
+ return expandOneHop(db, projectRoot, rootFiles, tokenBudget);
79
+ }
80
+ const visited = new Set(rootFiles);
81
+ const nodes = [];
82
+ let tokensUsed = 0;
83
+ const queue = [];
84
+ const pushNeighbors = (files, currentDepth) => {
85
+ for (const file of files) {
86
+ const edges = db.getEdgesFrom(file, 30);
87
+ for (const edge of edges) {
88
+ if (!visited.has(edge.to_node)) {
89
+ queue.push({
90
+ priority: filePriority(db, edge),
91
+ depth: currentDepth,
92
+ edge,
93
+ });
94
+ }
95
+ }
96
+ }
97
+ // Sort descending by priority (simple sort — for production, use a heap)
98
+ queue.sort((a, b) => b.priority - a.priority);
99
+ };
100
+ pushNeighbors(rootFiles, 1);
101
+ while (queue.length > 0 && tokensUsed < tokenBudget) {
102
+ const item = queue.shift();
103
+ if (item.depth > maxDepth)
104
+ continue;
105
+ if (visited.has(item.edge.to_node))
106
+ continue;
107
+ visited.add(item.edge.to_node);
108
+ const content = loadNodeContent(item.edge.to_node, item.edge.to_type, projectRoot);
109
+ if (!content)
110
+ continue;
111
+ const tokenCount = estimateTokens(content);
112
+ if (tokensUsed + tokenCount > tokenBudget) {
113
+ const remaining = tokenBudget - tokensUsed;
114
+ if (remaining < 100)
115
+ break;
116
+ const truncated = content.slice(0, Math.floor(remaining * 3.5 / 1.15));
117
+ nodes.push({
118
+ node: item.edge.to_node,
119
+ nodeType: item.edge.to_type,
120
+ content: truncated + '\n[... truncated ...]',
121
+ tokenCount: estimateTokens(truncated),
122
+ discoveredVia: `via ${item.edge.edge_type} from ${item.edge.from_node} (hop ${item.depth}, weight=${item.edge.weight.toFixed(2)})`,
123
+ weight: item.edge.weight,
124
+ });
125
+ tokensUsed += estimateTokens(truncated);
126
+ break;
127
+ }
128
+ nodes.push({
129
+ node: item.edge.to_node,
130
+ nodeType: item.edge.to_type,
131
+ content,
132
+ tokenCount,
133
+ discoveredVia: `via ${item.edge.edge_type} from ${item.edge.from_node} (hop ${item.depth}, weight=${item.edge.weight.toFixed(2)})`,
134
+ weight: item.edge.weight,
135
+ });
136
+ tokensUsed += tokenCount;
137
+ // Expand neighbors at next depth
138
+ if (item.depth < maxDepth) {
139
+ pushNeighbors([item.edge.to_node], item.depth + 1);
140
+ }
141
+ }
142
+ return { nodes, tokensUsed };
143
+ }
144
+ /**
145
+ * Load the content of a graph node (file or area doc).
146
+ */
147
+ function loadNodeContent(node, nodeType, projectRoot) {
148
+ if (nodeType === 'file') {
149
+ const fullPath = resolve(projectRoot, node);
150
+ if (!existsSync(fullPath))
151
+ return null;
152
+ try {
153
+ return readFileSync(fullPath, 'utf8');
154
+ }
155
+ catch {
156
+ return null;
157
+ }
158
+ }
159
+ if (nodeType === 'area') {
160
+ // Area docs are in docs/context/<area>.md
161
+ const docPath = resolve(projectRoot, 'docs', 'context', `${node}.md`);
162
+ if (!existsSync(docPath))
163
+ return null;
164
+ try {
165
+ return readFileSync(docPath, 'utf8');
166
+ }
167
+ catch {
168
+ return null;
169
+ }
170
+ }
171
+ return null;
172
+ }
173
+ //# sourceMappingURL=traversal.js.map