@codragraph/cli 2.1.0 → 2.1.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.
Files changed (67) hide show
  1. package/README.md +58 -20
  2. package/dist/_shared/cgdb/schema-constants.d.ts +2 -2
  3. package/dist/_shared/cgdb/schema-constants.d.ts.map +1 -1
  4. package/dist/_shared/cgdb/schema-constants.js +3 -0
  5. package/dist/_shared/cgdb/schema-constants.js.map +1 -1
  6. package/dist/_shared/feature-clusters.d.ts +99 -0
  7. package/dist/_shared/feature-clusters.d.ts.map +1 -0
  8. package/dist/_shared/feature-clusters.js +2 -0
  9. package/dist/_shared/feature-clusters.js.map +1 -0
  10. package/dist/_shared/graph/types.d.ts +16 -2
  11. package/dist/_shared/graph/types.d.ts.map +1 -1
  12. package/dist/_shared/index.d.ts +1 -0
  13. package/dist/_shared/index.d.ts.map +1 -1
  14. package/dist/_shared/index.js.map +1 -1
  15. package/dist/_shared/pipeline.d.ts +1 -1
  16. package/dist/_shared/pipeline.d.ts.map +1 -1
  17. package/dist/cli/ai-context.js +4 -0
  18. package/dist/cli/analyze.js +27 -24
  19. package/dist/cli/index.js +37 -0
  20. package/dist/cli/setup.js +9 -5
  21. package/dist/cli/tool.d.ts +25 -0
  22. package/dist/cli/tool.js +74 -0
  23. package/dist/config/supported-languages.d.ts +3 -3
  24. package/dist/config/supported-languages.js +3 -3
  25. package/dist/core/cgdb/cgdb-adapter.js +19 -3
  26. package/dist/core/cgdb/csv-generator.js +33 -2
  27. package/dist/core/cgdb/schema.d.ts +2 -1
  28. package/dist/core/cgdb/schema.js +55 -0
  29. package/dist/core/embeddings/embedder.js +4 -2
  30. package/dist/core/graphstore/index.d.ts +1 -1
  31. package/dist/core/graphstore/index.js +1 -1
  32. package/dist/core/group/service.d.ts +16 -0
  33. package/dist/core/group/service.js +360 -0
  34. package/dist/core/ingestion/emit-references.d.ts +1 -1
  35. package/dist/core/ingestion/emit-references.js +1 -1
  36. package/dist/core/ingestion/feature-cluster-processor.d.ts +62 -0
  37. package/dist/core/ingestion/feature-cluster-processor.js +626 -0
  38. package/dist/core/ingestion/finalize-orchestrator.js +1 -1
  39. package/dist/core/ingestion/model/registration-table.js +1 -0
  40. package/dist/core/ingestion/model/resolve.d.ts +2 -2
  41. package/dist/core/ingestion/model/resolve.js +3 -3
  42. package/dist/core/ingestion/model/semantic-model.d.ts +1 -1
  43. package/dist/core/ingestion/model/semantic-model.js +1 -1
  44. package/dist/core/ingestion/model/symbol-table.d.ts +1 -1
  45. package/dist/core/ingestion/model/symbol-table.js +1 -1
  46. package/dist/core/ingestion/pipeline-phases/feature-clusters.d.ts +17 -0
  47. package/dist/core/ingestion/pipeline-phases/feature-clusters.js +88 -0
  48. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  49. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  50. package/dist/core/ingestion/pipeline.d.ts +4 -0
  51. package/dist/core/ingestion/pipeline.js +9 -5
  52. package/dist/core/run-analyze.d.ts +1 -0
  53. package/dist/core/run-analyze.js +12 -6
  54. package/dist/mcp/core/embedder.js +5 -2
  55. package/dist/mcp/local/local-backend.d.ts +12 -0
  56. package/dist/mcp/local/local-backend.js +381 -3
  57. package/dist/mcp/resources.js +139 -0
  58. package/dist/mcp/tools.js +174 -2
  59. package/dist/server/api.js +116 -0
  60. package/dist/storage/repo-manager.d.ts +6 -1
  61. package/dist/storage/repo-manager.js +5 -1
  62. package/dist/types/pipeline.d.ts +2 -0
  63. package/package.json +13 -4
  64. package/scripts/build.js +13 -12
  65. package/skills/codragraph-cli.md +17 -1
  66. package/skills/codragraph-guide.md +6 -2
  67. package/skills/codragraph-onboarding.md +2 -2
package/dist/cli/index.js CHANGED
@@ -135,6 +135,43 @@ program
135
135
  .description('Execute raw Cypher query against the knowledge graph')
136
136
  .option('-r, --repo <name>', 'Target repository')
137
137
  .action(createLazyAction(() => import('./tool.js'), 'cypherCommand'));
138
+ program
139
+ .command('feature-clusters')
140
+ .description('List human-facing feature clusters for targeted context building')
141
+ .option('-r, --repo <name>', 'Target repository')
142
+ .option('-l, --limit <n>', 'Max feature clusters to return (default: 100)')
143
+ .action(createLazyAction(() => import('./tool.js'), 'featureClustersCommand'));
144
+ program
145
+ .command('cluster-query [query]')
146
+ .description('Alias for feature-clusters: list product/domain clusters')
147
+ .option('-r, --repo <name>', 'Target repository')
148
+ .option('-l, --limit <n>', 'Max feature clusters to return (default: 100)')
149
+ .action(createLazyAction(() => import('./tool.js'), 'clusterQueryCommand'));
150
+ program
151
+ .command('feature-context <name>')
152
+ .description('Show members, line ranges, and dependencies for a feature cluster')
153
+ .option('-r, --repo <name>', 'Target repository')
154
+ .option('-l, --limit <n>', 'Max members to return (default: 100)')
155
+ .action(createLazyAction(() => import('./tool.js'), 'featureContextCommand'));
156
+ program
157
+ .command('cluster-context <name>')
158
+ .description('Alias for feature-context: show a feature cluster context pack')
159
+ .option('-r, --repo <name>', 'Target repository')
160
+ .option('-l, --limit <n>', 'Max members to return (default: 100)')
161
+ .action(createLazyAction(() => import('./tool.js'), 'clusterContextCommand'));
162
+ program
163
+ .command('context-pack <name>')
164
+ .description('Generate the compact agent context pack for a feature cluster')
165
+ .option('-r, --repo <name>', 'Target repository')
166
+ .option('-l, --limit <n>', 'Max members to return (default: 100)')
167
+ .action(createLazyAction(() => import('./tool.js'), 'contextPackCommand'));
168
+ program
169
+ .command('cluster-impact <name>')
170
+ .description('Feature-level blast radius analysis for a cluster')
171
+ .option('-d, --direction <dir>', 'upstream, downstream, or both', 'upstream')
172
+ .option('-r, --repo <name>', 'Target repository')
173
+ .option('-l, --limit <n>', 'Max context-pack members to include (default: 100)')
174
+ .action(createLazyAction(() => import('./tool.js'), 'clusterImpactCommand'));
138
175
  program
139
176
  .command('detect-changes')
140
177
  .alias('detect_changes')
package/dist/cli/setup.js CHANGED
@@ -11,12 +11,16 @@ import os from 'os';
11
11
  import { execFile, execFileSync } from 'child_process';
12
12
  import { promisify } from 'util';
13
13
  import { fileURLToPath } from 'url';
14
+ import { createRequire } from 'module';
14
15
  import { glob } from 'glob';
15
16
  import { parseTree, modify, applyEdits } from 'jsonc-parser';
16
17
  import { getGlobalDir } from '../storage/repo-manager.js';
17
18
  const __filename = fileURLToPath(import.meta.url);
18
19
  const __dirname = path.dirname(__filename);
19
20
  const execFileAsync = promisify(execFile);
21
+ const require = createRequire(import.meta.url);
22
+ const pkg = require('../../package.json');
23
+ const CLI_PACKAGE_SPEC = `@codragraph/cli@${pkg.version}`;
20
24
  /**
21
25
  * Resolve the absolute path to the `codragraph` binary if it's installed
22
26
  * globally (or via npm -g / yarn global). Returns null when not found.
@@ -59,7 +63,7 @@ function resolveCodragraphBin() {
59
63
  * The MCP server entry for all editors.
60
64
  *
61
65
  * Prefers the globally-installed `codragraph` binary (starts in ~1 s) over
62
- * `npx -y @codragraph/cli@latest` (cold-cache install of native deps can take
66
+ * `npx -y @codragraph/cli@<version>` (cold-cache install of native deps can take
63
67
  * >60 s, exceeding Claude Code's 30 s MCP connection timeout).
64
68
  *
65
69
  * Falls back to npx when the binary isn't on PATH — e.g. first-time
@@ -84,12 +88,12 @@ function getMcpEntry() {
84
88
  if (process.platform === 'win32') {
85
89
  return {
86
90
  command: 'cmd',
87
- args: ['/c', 'npx', '-y', '@codragraph/cli@latest', 'mcp'],
91
+ args: ['/c', 'npx', '-y', CLI_PACKAGE_SPEC, 'mcp'],
88
92
  };
89
93
  }
90
94
  return {
91
95
  command: 'npx',
92
- args: ['-y', '@codragraph/cli@latest', 'mcp'],
96
+ args: ['-y', CLI_PACKAGE_SPEC, 'mcp'],
93
97
  };
94
98
  }
95
99
  /**
@@ -107,10 +111,10 @@ function getOpenCodeMcpEntry() {
107
111
  if (process.platform === 'win32') {
108
112
  return {
109
113
  type: 'local',
110
- command: ['cmd', '/c', 'npx', '-y', '@codragraph/cli@latest', 'mcp'],
114
+ command: ['cmd', '/c', 'npx', '-y', CLI_PACKAGE_SPEC, 'mcp'],
111
115
  };
112
116
  }
113
- return { type: 'local', command: ['npx', '-y', '@codragraph/cli@latest', 'mcp'] };
117
+ return { type: 'local', command: ['npx', '-y', CLI_PACKAGE_SPEC, 'mcp'] };
114
118
  }
115
119
  /**
116
120
  * Merge codragraph entry into an existing MCP config JSON object.
@@ -36,6 +36,31 @@ export declare function impactCommand(target: string, options?: {
36
36
  export declare function cypherCommand(query: string, options?: {
37
37
  repo?: string;
38
38
  }): Promise<void>;
39
+ export declare function featureClustersCommand(options?: {
40
+ repo?: string;
41
+ limit?: string;
42
+ }): Promise<void>;
43
+ export declare function clusterQueryCommand(query?: string, options?: {
44
+ repo?: string;
45
+ limit?: string;
46
+ }): Promise<void>;
47
+ export declare function featureContextCommand(name: string, options?: {
48
+ repo?: string;
49
+ limit?: string;
50
+ }): Promise<void>;
51
+ export declare function clusterContextCommand(name: string, options?: {
52
+ repo?: string;
53
+ limit?: string;
54
+ }): Promise<void>;
55
+ export declare function contextPackCommand(name: string, options?: {
56
+ repo?: string;
57
+ limit?: string;
58
+ }): Promise<void>;
59
+ export declare function clusterImpactCommand(name: string, options?: {
60
+ direction?: string;
61
+ repo?: string;
62
+ limit?: string;
63
+ }): Promise<void>;
39
64
  export declare function detectChangesCommand(options?: {
40
65
  scope?: string;
41
66
  baseRef?: string;
package/dist/cli/tool.js CHANGED
@@ -128,6 +128,80 @@ export async function cypherCommand(query, options) {
128
128
  });
129
129
  output(result);
130
130
  }
131
+ export async function featureClustersCommand(options) {
132
+ const backend = await getBackend();
133
+ const result = await backend.callTool('feature_clusters', {
134
+ repo: options?.repo,
135
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
136
+ });
137
+ output(result);
138
+ }
139
+ export async function clusterQueryCommand(query, options) {
140
+ const backend = await getBackend();
141
+ const result = await backend.callTool('cluster_query', {
142
+ query,
143
+ repo: options?.repo,
144
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
145
+ });
146
+ output(result);
147
+ }
148
+ export async function featureContextCommand(name, options) {
149
+ if (!name?.trim()) {
150
+ console.error('Usage: codragraph feature-context <name>');
151
+ process.exit(1);
152
+ }
153
+ const backend = await getBackend();
154
+ const result = await backend.callTool('feature_context', {
155
+ name,
156
+ repo: options?.repo,
157
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
158
+ });
159
+ output(result);
160
+ emitTokenStats(result);
161
+ }
162
+ export async function clusterContextCommand(name, options) {
163
+ if (!name?.trim()) {
164
+ console.error('Usage: codragraph cluster-context <name>');
165
+ process.exit(1);
166
+ }
167
+ const backend = await getBackend();
168
+ const result = await backend.callTool('cluster_context', {
169
+ name,
170
+ repo: options?.repo,
171
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
172
+ });
173
+ output(result);
174
+ emitTokenStats(result);
175
+ }
176
+ export async function contextPackCommand(name, options) {
177
+ if (!name?.trim()) {
178
+ console.error('Usage: codragraph context-pack <name>');
179
+ process.exit(1);
180
+ }
181
+ const backend = await getBackend();
182
+ const result = await backend.callTool('context_pack', {
183
+ name,
184
+ repo: options?.repo,
185
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
186
+ });
187
+ output(result);
188
+ emitTokenStats(result);
189
+ }
190
+ export async function clusterImpactCommand(name, options) {
191
+ if (!name?.trim()) {
192
+ console.error('Usage: codragraph cluster-impact <name>');
193
+ process.exit(1);
194
+ }
195
+ const backend = await getBackend();
196
+ const result = await backend.callTool('cluster_impact', {
197
+ name,
198
+ direction: options?.direction,
199
+ repo: options?.repo,
200
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
201
+ });
202
+ output(result);
203
+ emitTokenStats(result);
204
+ }
131
205
  function formatDetectChangesResult(result) {
132
206
  if (result?.error)
133
207
  return `Error: ${result.error}`;
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Re-export SupportedLanguages from codragraph-shared (single source of truth).
2
+ * Re-export SupportedLanguages from @codragraph/shared (single source of truth).
3
3
  *
4
4
  * HOW TO ADD A NEW LANGUAGE:
5
5
  *
6
- * 1. Add the enum member in codragraph-shared/src/languages.ts
6
+ * 1. Add the enum member in packages/shared/src/languages.ts
7
7
  * 2. Run `tsc --noEmit` — compiler errors guide you to every dispatch table
8
8
  * 3. Use the checklist in each ingestion file for what to add
9
- * 4. Add tree-sitter-<lang> to codragraph/package.json dependencies
9
+ * 4. Add tree-sitter-<lang> to packages/core/package.json dependencies
10
10
  * 5. Add file extension mapping in utils.ts getLanguageFromFilename()
11
11
  * 6. Run full test suite
12
12
  */
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Re-export SupportedLanguages from codragraph-shared (single source of truth).
2
+ * Re-export SupportedLanguages from @codragraph/shared (single source of truth).
3
3
  *
4
4
  * HOW TO ADD A NEW LANGUAGE:
5
5
  *
6
- * 1. Add the enum member in codragraph-shared/src/languages.ts
6
+ * 1. Add the enum member in packages/shared/src/languages.ts
7
7
  * 2. Run `tsc --noEmit` — compiler errors guide you to every dispatch table
8
8
  * 3. Use the checklist in each ingestion file for what to add
9
- * 4. Add tree-sitter-<lang> to codragraph/package.json dependencies
9
+ * 4. Add tree-sitter-<lang> to packages/core/package.json dependencies
10
10
  * 5. Add file extension mapping in utils.ts getLanguageFromFilename()
11
11
  * 6. Run full test suite
12
12
  */
@@ -507,7 +507,8 @@ const getCopyQuery = (table, filePath) => {
507
507
  // RFC 0001 Phase 2: every content-bearing table also lists
508
508
  // `contentEncoding` immediately after `content` to match the schema +
509
509
  // CSV layout. Tables without a content column (Folder, Community,
510
- // Process, Route, Tool) are unchanged.
510
+ // Process, Route, Tool) are unchanged. FeatureCluster carries rich metadata
511
+ // but still has no content/file snippet column.
511
512
  if (table === 'File') {
512
513
  return `COPY ${t}(id, name, filePath, content, contentEncoding) FROM "${filePath}" ${COPY_CSV_OPTS}`;
513
514
  }
@@ -520,6 +521,9 @@ const getCopyQuery = (table, filePath) => {
520
521
  if (table === 'Process') {
521
522
  return `COPY ${t}(id, label, heuristicLabel, processType, stepCount, communities, entryPointId, terminalId) FROM "${filePath}" ${COPY_CSV_OPTS}`;
522
523
  }
524
+ if (table === 'FeatureCluster') {
525
+ return `COPY ${t}(id, name, slug, featureKind, summary, description, repo, service, signals, memberCount, entryPointIds, routes, tools, testCoverageHints, lastIndexedCommit, confidence, source) FROM "${filePath}" ${COPY_CSV_OPTS}`;
526
+ }
523
527
  if (table === 'Section') {
524
528
  return `COPY ${t}(id, name, filePath, startLine, endLine, level, content, contentEncoding, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
525
529
  }
@@ -557,6 +561,9 @@ export const insertNodeToCgdb = async (label, properties, dbPath) => {
557
561
  return 'NULL';
558
562
  if (typeof v === 'number')
559
563
  return String(v);
564
+ if (Array.isArray(v)) {
565
+ return `[${v.map((item) => `'${String(item).replace(/\\/g, '\\\\').replace(/'/g, "''")}'`).join(',')}]`;
566
+ }
560
567
  // Escape backslashes first (for Windows paths), then single quotes
561
568
  return `'${String(v).replace(/\\/g, '\\\\').replace(/'/g, "''").replace(/\n/g, '\\n').replace(/\r/g, '\\r')}'`;
562
569
  };
@@ -575,6 +582,9 @@ export const insertNodeToCgdb = async (label, properties, dbPath) => {
575
582
  : '';
576
583
  query = `CREATE (n:Section {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, level: ${properties.level || 1}, content: ${escapeValue(properties.content || '')}${descPart}})`;
577
584
  }
585
+ else if (label === 'FeatureCluster') {
586
+ query = `CREATE (n:FeatureCluster {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, slug: ${escapeValue(properties.slug)}, featureKind: ${escapeValue(properties.featureKind || 'feature')}, summary: ${escapeValue(properties.summary || '')}, description: ${escapeValue(properties.description || '')}, repo: ${escapeValue(properties.repo || '')}, service: ${escapeValue(properties.service || '')}, signals: ${escapeValue(properties.signals || [])}, memberCount: ${properties.memberCount || 0}, entryPointIds: ${escapeValue(properties.entryPointIds || [])}, routes: ${escapeValue(properties.routes || [])}, tools: ${escapeValue(properties.tools || [])}, testCoverageHints: ${escapeValue(properties.testCoverageHints || [])}, lastIndexedCommit: ${escapeValue(properties.lastIndexedCommit || '')}, confidence: ${properties.confidence || 0}, source: ${escapeValue(properties.source || 'heuristic')}})`;
587
+ }
578
588
  else if (TABLES_WITH_EXPORTED.has(label)) {
579
589
  const descPart = properties.description
580
590
  ? `, description: ${escapeValue(properties.description)}`
@@ -635,6 +645,9 @@ export const batchInsertNodesToCgdb = async (nodes, dbPath) => {
635
645
  return 'NULL';
636
646
  if (typeof v === 'number')
637
647
  return String(v);
648
+ if (Array.isArray(v)) {
649
+ return `[${v.map((item) => `'${String(item).replace(/\\/g, '\\\\').replace(/'/g, "''")}'`).join(',')}]`;
650
+ }
638
651
  // Escape backslashes first (for Windows paths), then single quotes, then newlines
639
652
  return `'${String(v).replace(/\\/g, '\\\\').replace(/'/g, "''").replace(/\n/g, '\\n').replace(/\r/g, '\\r')}'`;
640
653
  };
@@ -661,6 +674,9 @@ export const batchInsertNodesToCgdb = async (nodes, dbPath) => {
661
674
  : '';
662
675
  query = `MERGE (n:Section {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.level = ${properties.level || 1}, n.content = ${escapeValue(properties.content || '')}${descPart}`;
663
676
  }
677
+ else if (label === 'FeatureCluster') {
678
+ query = `MERGE (n:FeatureCluster {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.slug = ${escapeValue(properties.slug)}, n.featureKind = ${escapeValue(properties.featureKind || 'feature')}, n.summary = ${escapeValue(properties.summary || '')}, n.description = ${escapeValue(properties.description || '')}, n.repo = ${escapeValue(properties.repo || '')}, n.service = ${escapeValue(properties.service || '')}, n.signals = ${escapeValue(properties.signals || [])}, n.memberCount = ${properties.memberCount || 0}, n.entryPointIds = ${escapeValue(properties.entryPointIds || [])}, n.routes = ${escapeValue(properties.routes || [])}, n.tools = ${escapeValue(properties.tools || [])}, n.testCoverageHints = ${escapeValue(properties.testCoverageHints || [])}, n.lastIndexedCommit = ${escapeValue(properties.lastIndexedCommit || '')}, n.confidence = ${properties.confidence || 0}, n.source = ${escapeValue(properties.source || 'heuristic')}`;
679
+ }
664
680
  else if (TABLES_WITH_EXPORTED.has(label)) {
665
681
  const descPart = properties.description
666
682
  ? `, n.description = ${escapeValue(properties.description)}`
@@ -1103,8 +1119,8 @@ export const deleteNodesForFile = async (filePath, dbPath) => {
1103
1119
  // Delete nodes from each table that has filePath
1104
1120
  // DETACH DELETE removes the node and all its relationships
1105
1121
  for (const tableName of NODE_TABLES) {
1106
- // Skip tables that don't have filePath (Community, Process)
1107
- if (tableName === 'Community' || tableName === 'Process')
1122
+ // Skip tables that don't have filePath (Community, Process, FeatureCluster)
1123
+ if (tableName === 'Community' || tableName === 'Process' || tableName === 'FeatureCluster')
1108
1124
  continue;
1109
1125
  try {
1110
1126
  // First count how many we'll delete
@@ -40,6 +40,13 @@ export const escapeCSVNumber = (value, defaultValue = -1) => {
40
40
  return String(defaultValue);
41
41
  return String(value);
42
42
  };
43
+ const toCgdbStringArray = (value) => {
44
+ const values = Array.isArray(value) ? value : [];
45
+ return `[${values
46
+ .map((item) => String(item).replace(/\\/g, '\\\\').replace(/'/g, "''").replace(/,/g, '\\,'))
47
+ .map((item) => `'${item}'`)
48
+ .join(',')}]`;
49
+ };
43
50
  // ============================================================================
44
51
  // CONTENT EXTRACTION (lazy — reads from disk on demand)
45
52
  // ============================================================================
@@ -231,6 +238,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir, compress) =>
231
238
  const codeElemWriter = new BufferedCSVWriter(path.join(csvDir, 'codeelement.csv'), codeElementHeader);
232
239
  const communityWriter = new BufferedCSVWriter(path.join(csvDir, 'community.csv'), 'id,label,heuristicLabel,keywords,description,enrichedBy,cohesion,symbolCount');
233
240
  const processWriter = new BufferedCSVWriter(path.join(csvDir, 'process.csv'), 'id,label,heuristicLabel,processType,stepCount,communities,entryPointId,terminalId');
241
+ const featureClusterWriter = new BufferedCSVWriter(path.join(csvDir, 'featurecluster.csv'), 'id,name,slug,featureKind,summary,description,repo,service,signals,memberCount,entryPointIds,routes,tools,testCoverageHints,lastIndexedCommit,confidence,source');
234
242
  // Section nodes have an extra 'level' column
235
243
  const sectionWriter = new BufferedCSVWriter(path.join(csvDir, 'section.csv'), 'id,name,filePath,startLine,endLine,level,content,contentEncoding,description');
236
244
  // Route nodes for API endpoint mapping
@@ -315,8 +323,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir, compress) =>
315
323
  break;
316
324
  }
317
325
  case 'Process': {
318
- const communities = node.properties.communities || [];
319
- const communitiesStr = `[${communities.map((c) => `'${c.replace(/'/g, "''")}'`).join(',')}]`;
326
+ const communitiesStr = toCgdbStringArray(node.properties.communities);
320
327
  await processWriter.addRow([
321
328
  escapeCSVField(node.id),
322
329
  escapeCSVField(node.properties.name || ''),
@@ -329,6 +336,28 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir, compress) =>
329
336
  ].join(','));
330
337
  break;
331
338
  }
339
+ case 'FeatureCluster': {
340
+ await featureClusterWriter.addRow([
341
+ escapeCSVField(node.id),
342
+ escapeCSVField(node.properties.name || ''),
343
+ escapeCSVField(node.properties.slug || ''),
344
+ escapeCSVField(node.properties.featureKind || 'feature'),
345
+ escapeCSVField(node.properties.summary || ''),
346
+ escapeCSVField(node.properties.description || ''),
347
+ escapeCSVField(node.properties.repo || ''),
348
+ escapeCSVField(node.properties.service || ''),
349
+ escapeCSVField(toCgdbStringArray(node.properties.signals)),
350
+ escapeCSVNumber(node.properties.memberCount, 0),
351
+ escapeCSVField(toCgdbStringArray(node.properties.entryPointIds)),
352
+ escapeCSVField(toCgdbStringArray(node.properties.routes)),
353
+ escapeCSVField(toCgdbStringArray(node.properties.tools)),
354
+ escapeCSVField(toCgdbStringArray(node.properties.testCoverageHints)),
355
+ escapeCSVField(node.properties.lastIndexedCommit || ''),
356
+ escapeCSVNumber(node.properties.confidence, 0),
357
+ escapeCSVField(node.properties.source || 'heuristic'),
358
+ ].join(','));
359
+ break;
360
+ }
332
361
  case 'Method': {
333
362
  const content = await extractContent(node, contentCache);
334
363
  const { wireContent, tag } = applyEncoding(content, compress);
@@ -441,6 +470,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir, compress) =>
441
470
  codeElemWriter,
442
471
  communityWriter,
443
472
  processWriter,
473
+ featureClusterWriter,
444
474
  sectionWriter,
445
475
  routeWriter,
446
476
  toolWriter,
@@ -473,6 +503,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir, compress) =>
473
503
  ['CodeElement', codeElemWriter],
474
504
  ['Community', communityWriter],
475
505
  ['Process', processWriter],
506
+ ['FeatureCluster', featureClusterWriter],
476
507
  ['Section', sectionWriter],
477
508
  ['Route', routeWriter],
478
509
  ['Tool', toolWriter],
@@ -20,6 +20,7 @@ export declare const METHOD_SCHEMA = "\nCREATE NODE TABLE Method (\n id STRING,
20
20
  export declare const CODE_ELEMENT_SCHEMA = "\nCREATE NODE TABLE CodeElement (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n isExported BOOLEAN,\n content STRING,\n contentEncoding STRING DEFAULT 'none',\n description STRING,\n PRIMARY KEY (id)\n)";
21
21
  export declare const COMMUNITY_SCHEMA = "\nCREATE NODE TABLE Community (\n id STRING,\n label STRING,\n heuristicLabel STRING,\n keywords STRING[],\n description STRING,\n enrichedBy STRING,\n cohesion DOUBLE,\n symbolCount INT32,\n PRIMARY KEY (id)\n)";
22
22
  export declare const PROCESS_SCHEMA = "\nCREATE NODE TABLE Process (\n id STRING,\n label STRING,\n heuristicLabel STRING,\n processType STRING,\n stepCount INT32,\n communities STRING[],\n entryPointId STRING,\n terminalId STRING,\n PRIMARY KEY (id)\n)";
23
+ export declare const FEATURE_CLUSTER_SCHEMA = "\nCREATE NODE TABLE FeatureCluster (\n id STRING,\n name STRING,\n slug STRING,\n featureKind STRING,\n summary STRING,\n description STRING,\n repo STRING,\n service STRING,\n signals STRING[],\n memberCount INT32,\n entryPointIds STRING[],\n routes STRING[],\n tools STRING[],\n testCoverageHints STRING[],\n lastIndexedCommit STRING,\n confidence DOUBLE,\n source STRING,\n PRIMARY KEY (id)\n)";
23
24
  export declare const STRUCT_SCHEMA: string;
24
25
  export declare const ENUM_SCHEMA: string;
25
26
  export declare const MACRO_SCHEMA: string;
@@ -42,7 +43,7 @@ export declare const MODULE_SCHEMA: string;
42
43
  export declare const ROUTE_SCHEMA = "\nCREATE NODE TABLE Route (\n id STRING,\n name STRING,\n filePath STRING,\n responseKeys STRING[],\n errorKeys STRING[],\n middleware STRING[],\n PRIMARY KEY (id)\n)";
43
44
  export declare const TOOL_SCHEMA = "\nCREATE NODE TABLE Tool (\n id STRING,\n name STRING,\n filePath STRING,\n description STRING,\n PRIMARY KEY (id)\n)";
44
45
  export declare const SECTION_SCHEMA = "\nCREATE NODE TABLE Section (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n level INT64,\n content STRING,\n contentEncoding STRING DEFAULT 'none',\n description STRING,\n PRIMARY KEY (id)\n)";
45
- export declare const RELATION_SCHEMA = "\nCREATE REL TABLE CodeRelation (\n FROM File TO File,\n FROM File TO Folder,\n FROM File TO Function,\n FROM File TO Class,\n FROM File TO Interface,\n FROM File TO Method,\n FROM File TO CodeElement,\n FROM File TO `Struct`,\n FROM File TO `Enum`,\n FROM File TO `Macro`,\n FROM File TO `Typedef`,\n FROM File TO `Union`,\n FROM File TO `Namespace`,\n FROM File TO `Trait`,\n FROM File TO `Impl`,\n FROM File TO `TypeAlias`,\n FROM File TO `Const`,\n FROM File TO `Static`,\n FROM File TO `Variable`,\n FROM File TO `Property`,\n FROM File TO `Record`,\n FROM File TO `Delegate`,\n FROM File TO `Annotation`,\n FROM File TO `Constructor`,\n FROM File TO `Template`,\n FROM File TO `Module`,\n FROM File TO Section,\n FROM Folder TO Folder,\n FROM Folder TO File,\n FROM Function TO Function,\n FROM Function TO Method,\n FROM Function TO Class,\n FROM Function TO Community,\n FROM Function TO `Macro`,\n FROM Function TO `Struct`,\n FROM Function TO `Template`,\n FROM Function TO `Enum`,\n FROM Function TO `Namespace`,\n FROM Function TO `TypeAlias`,\n FROM Function TO `Module`,\n FROM Function TO `Impl`,\n FROM Function TO Interface,\n FROM Function TO `Constructor`,\n FROM Function TO `Const`,\n FROM Function TO `Typedef`,\n FROM Function TO `Union`,\n FROM Function TO `Property`,\n FROM Function TO CodeElement,\n FROM Class TO Method,\n FROM Class TO Function,\n FROM Class TO Class,\n FROM Class TO Interface,\n FROM Class TO Community,\n FROM Class TO `Template`,\n FROM Class TO `TypeAlias`,\n FROM Class TO `Struct`,\n FROM Class TO `Enum`,\n FROM Class TO `Annotation`,\n FROM Class TO `Constructor`,\n FROM Class TO `Trait`,\n FROM Class TO `Macro`,\n FROM Class TO `Impl`,\n FROM Class TO `Union`,\n FROM Class TO `Namespace`,\n FROM Class TO `Typedef`,\n FROM Class TO `Property`,\n FROM Method TO Function,\n FROM Method TO Method,\n FROM Method TO Class,\n FROM Method TO Community,\n FROM Method TO `Template`,\n FROM Method TO `Struct`,\n FROM Method TO `TypeAlias`,\n FROM Method TO `Enum`,\n FROM Method TO `Macro`,\n FROM Method TO `Namespace`,\n FROM Method TO `Module`,\n FROM Method TO `Impl`,\n FROM Method TO Interface,\n FROM Method TO `Constructor`,\n FROM Method TO `Property`,\n FROM Method TO CodeElement,\n FROM `Template` TO `Template`,\n FROM `Template` TO Function,\n FROM `Template` TO Method,\n FROM `Template` TO Class,\n FROM `Template` TO `Struct`,\n FROM `Template` TO `TypeAlias`,\n FROM `Template` TO `Enum`,\n FROM `Template` TO `Macro`,\n FROM `Template` TO Interface,\n FROM `Template` TO `Constructor`,\n FROM `Module` TO `Module`,\n FROM Section TO Section,\n FROM Section TO File,\n FROM File TO Route,\n FROM Function TO Route,\n FROM Method TO Route,\n FROM File TO Tool,\n FROM Function TO Tool,\n FROM Method TO Tool,\n FROM CodeElement TO Community,\n FROM Interface TO Community,\n FROM Interface TO Function,\n FROM Interface TO Method,\n FROM Interface TO Class,\n FROM Interface TO Interface,\n FROM Interface TO `TypeAlias`,\n FROM Interface TO `Struct`,\n FROM Interface TO `Constructor`,\n FROM Interface TO `Property`,\n FROM `Struct` TO Community,\n FROM `Struct` TO `Trait`,\n FROM `Struct` TO `Struct`,\n FROM `Struct` TO Class,\n FROM `Struct` TO `Enum`,\n FROM `Struct` TO Function,\n FROM `Struct` TO Method,\n FROM `Struct` TO Interface,\n FROM `Struct` TO `Constructor`,\n FROM `Struct` TO `Property`,\n FROM `Enum` TO `Enum`,\n FROM `Enum` TO Community,\n FROM `Enum` TO Class,\n FROM `Enum` TO Interface,\n FROM `Macro` TO Community,\n FROM `Macro` TO Function,\n FROM `Macro` TO Method,\n FROM `Module` TO Function,\n FROM `Module` TO Method,\n FROM `Typedef` TO Community,\n FROM `Union` TO Community,\n FROM `Namespace` TO Community,\n FROM `Namespace` TO `Struct`,\n FROM `Trait` TO Method,\n FROM `Trait` TO `Constructor`,\n FROM `Trait` TO `Property`,\n FROM `Trait` TO Community,\n FROM `Impl` TO Method,\n FROM `Impl` TO `Constructor`,\n FROM `Impl` TO `Property`,\n FROM `Impl` TO Community,\n FROM `Impl` TO `Trait`,\n FROM `Impl` TO `Struct`,\n FROM `Impl` TO `Impl`,\n FROM `TypeAlias` TO Community,\n FROM `TypeAlias` TO `Trait`,\n FROM `TypeAlias` TO Class,\n FROM `Const` TO Community,\n FROM `Static` TO Community,\n FROM `Variable` TO Community,\n FROM `Property` TO Community,\n FROM `Record` TO Method,\n FROM `Record` TO `Constructor`,\n FROM `Record` TO `Property`,\n FROM `Record` TO Community,\n FROM `Delegate` TO Community,\n FROM `Annotation` TO Community,\n FROM `Constructor` TO Community,\n FROM `Constructor` TO Interface,\n FROM `Constructor` TO Class,\n FROM `Constructor` TO Method,\n FROM `Constructor` TO Function,\n FROM `Constructor` TO `Constructor`,\n FROM `Constructor` TO `Struct`,\n FROM `Constructor` TO `Macro`,\n FROM `Constructor` TO `Template`,\n FROM `Constructor` TO `TypeAlias`,\n FROM `Constructor` TO `Enum`,\n FROM `Constructor` TO `Annotation`,\n FROM `Constructor` TO `Impl`,\n FROM `Constructor` TO `Namespace`,\n FROM `Constructor` TO `Module`,\n FROM `Constructor` TO `Property`,\n FROM `Constructor` TO `Typedef`,\n FROM `Template` TO Community,\n FROM `Module` TO Community,\n FROM Function TO Process,\n FROM Method TO Process,\n FROM Class TO Process,\n FROM Interface TO Process,\n FROM `Struct` TO Process,\n FROM `Constructor` TO Process,\n FROM `Module` TO Process,\n FROM `Macro` TO Process,\n FROM `Impl` TO Process,\n FROM `Typedef` TO Process,\n FROM `TypeAlias` TO Process,\n FROM `Enum` TO Process,\n FROM `Union` TO Process,\n FROM `Namespace` TO Process,\n FROM `Trait` TO Process,\n FROM `Const` TO Process,\n FROM `Static` TO Process,\n FROM `Variable` TO Process,\n FROM `Property` TO Process,\n FROM `Record` TO Process,\n FROM `Delegate` TO Process,\n FROM `Annotation` TO Process,\n FROM `Template` TO Process,\n FROM CodeElement TO Process,\n FROM Route TO Process,\n FROM Tool TO Process,\n type STRING,\n confidence DOUBLE,\n reason STRING,\n step INT32\n)";
46
+ export declare const RELATION_SCHEMA = "\nCREATE REL TABLE CodeRelation (\n FROM File TO File,\n FROM File TO Folder,\n FROM File TO Function,\n FROM File TO Class,\n FROM File TO Interface,\n FROM File TO Method,\n FROM File TO CodeElement,\n FROM File TO `Struct`,\n FROM File TO `Enum`,\n FROM File TO `Macro`,\n FROM File TO `Typedef`,\n FROM File TO `Union`,\n FROM File TO `Namespace`,\n FROM File TO `Trait`,\n FROM File TO `Impl`,\n FROM File TO `TypeAlias`,\n FROM File TO `Const`,\n FROM File TO `Static`,\n FROM File TO `Variable`,\n FROM File TO `Property`,\n FROM File TO `Record`,\n FROM File TO `Delegate`,\n FROM File TO `Annotation`,\n FROM File TO `Constructor`,\n FROM File TO `Template`,\n FROM File TO `Module`,\n FROM File TO Section,\n FROM Folder TO Folder,\n FROM Folder TO File,\n FROM Function TO Function,\n FROM Function TO Method,\n FROM Function TO Class,\n FROM Function TO Community,\n FROM Function TO `Macro`,\n FROM Function TO `Struct`,\n FROM Function TO `Template`,\n FROM Function TO `Enum`,\n FROM Function TO `Namespace`,\n FROM Function TO `TypeAlias`,\n FROM Function TO `Module`,\n FROM Function TO `Impl`,\n FROM Function TO Interface,\n FROM Function TO `Constructor`,\n FROM Function TO `Const`,\n FROM Function TO `Typedef`,\n FROM Function TO `Union`,\n FROM Function TO `Property`,\n FROM Function TO CodeElement,\n FROM Class TO Method,\n FROM Class TO Function,\n FROM Class TO Class,\n FROM Class TO Interface,\n FROM Class TO Community,\n FROM Class TO `Template`,\n FROM Class TO `TypeAlias`,\n FROM Class TO `Struct`,\n FROM Class TO `Enum`,\n FROM Class TO `Annotation`,\n FROM Class TO `Constructor`,\n FROM Class TO `Trait`,\n FROM Class TO `Macro`,\n FROM Class TO `Impl`,\n FROM Class TO `Union`,\n FROM Class TO `Namespace`,\n FROM Class TO `Typedef`,\n FROM Class TO `Property`,\n FROM Method TO Function,\n FROM Method TO Method,\n FROM Method TO Class,\n FROM Method TO Community,\n FROM Method TO `Template`,\n FROM Method TO `Struct`,\n FROM Method TO `TypeAlias`,\n FROM Method TO `Enum`,\n FROM Method TO `Macro`,\n FROM Method TO `Namespace`,\n FROM Method TO `Module`,\n FROM Method TO `Impl`,\n FROM Method TO Interface,\n FROM Method TO `Constructor`,\n FROM Method TO `Property`,\n FROM Method TO CodeElement,\n FROM `Template` TO `Template`,\n FROM `Template` TO Function,\n FROM `Template` TO Method,\n FROM `Template` TO Class,\n FROM `Template` TO `Struct`,\n FROM `Template` TO `TypeAlias`,\n FROM `Template` TO `Enum`,\n FROM `Template` TO `Macro`,\n FROM `Template` TO Interface,\n FROM `Template` TO `Constructor`,\n FROM `Module` TO `Module`,\n FROM Section TO Section,\n FROM Section TO File,\n FROM File TO Route,\n FROM Function TO Route,\n FROM Method TO Route,\n FROM File TO Tool,\n FROM Function TO Tool,\n FROM Method TO Tool,\n FROM CodeElement TO Community,\n FROM Interface TO Community,\n FROM Interface TO Function,\n FROM Interface TO Method,\n FROM Interface TO Class,\n FROM Interface TO Interface,\n FROM Interface TO `TypeAlias`,\n FROM Interface TO `Struct`,\n FROM Interface TO `Constructor`,\n FROM Interface TO `Property`,\n FROM `Struct` TO Community,\n FROM `Struct` TO `Trait`,\n FROM `Struct` TO `Struct`,\n FROM `Struct` TO Class,\n FROM `Struct` TO `Enum`,\n FROM `Struct` TO Function,\n FROM `Struct` TO Method,\n FROM `Struct` TO Interface,\n FROM `Struct` TO `Constructor`,\n FROM `Struct` TO `Property`,\n FROM `Enum` TO `Enum`,\n FROM `Enum` TO Community,\n FROM `Enum` TO Class,\n FROM `Enum` TO Interface,\n FROM `Macro` TO Community,\n FROM `Macro` TO Function,\n FROM `Macro` TO Method,\n FROM `Module` TO Function,\n FROM `Module` TO Method,\n FROM `Typedef` TO Community,\n FROM `Union` TO Community,\n FROM `Namespace` TO Community,\n FROM `Namespace` TO `Struct`,\n FROM `Trait` TO Method,\n FROM `Trait` TO `Constructor`,\n FROM `Trait` TO `Property`,\n FROM `Trait` TO Community,\n FROM `Impl` TO Method,\n FROM `Impl` TO `Constructor`,\n FROM `Impl` TO `Property`,\n FROM `Impl` TO Community,\n FROM `Impl` TO `Trait`,\n FROM `Impl` TO `Struct`,\n FROM `Impl` TO `Impl`,\n FROM `TypeAlias` TO Community,\n FROM `TypeAlias` TO `Trait`,\n FROM `TypeAlias` TO Class,\n FROM `Const` TO Community,\n FROM `Static` TO Community,\n FROM `Variable` TO Community,\n FROM `Property` TO Community,\n FROM `Record` TO Method,\n FROM `Record` TO `Constructor`,\n FROM `Record` TO `Property`,\n FROM `Record` TO Community,\n FROM `Delegate` TO Community,\n FROM `Annotation` TO Community,\n FROM `Constructor` TO Community,\n FROM `Constructor` TO Interface,\n FROM `Constructor` TO Class,\n FROM `Constructor` TO Method,\n FROM `Constructor` TO Function,\n FROM `Constructor` TO `Constructor`,\n FROM `Constructor` TO `Struct`,\n FROM `Constructor` TO `Macro`,\n FROM `Constructor` TO `Template`,\n FROM `Constructor` TO `TypeAlias`,\n FROM `Constructor` TO `Enum`,\n FROM `Constructor` TO `Annotation`,\n FROM `Constructor` TO `Impl`,\n FROM `Constructor` TO `Namespace`,\n FROM `Constructor` TO `Module`,\n FROM `Constructor` TO `Property`,\n FROM `Constructor` TO `Typedef`,\n FROM `Template` TO Community,\n FROM `Module` TO Community,\n FROM Function TO Process,\n FROM Method TO Process,\n FROM Class TO Process,\n FROM Interface TO Process,\n FROM `Struct` TO Process,\n FROM `Constructor` TO Process,\n FROM `Module` TO Process,\n FROM `Macro` TO Process,\n FROM `Impl` TO Process,\n FROM `Typedef` TO Process,\n FROM `TypeAlias` TO Process,\n FROM `Enum` TO Process,\n FROM `Union` TO Process,\n FROM `Namespace` TO Process,\n FROM `Trait` TO Process,\n FROM `Const` TO Process,\n FROM `Static` TO Process,\n FROM `Variable` TO Process,\n FROM `Property` TO Process,\n FROM `Record` TO Process,\n FROM `Delegate` TO Process,\n FROM `Annotation` TO Process,\n FROM `Template` TO Process,\n FROM CodeElement TO Process,\n FROM Route TO Process,\n FROM Tool TO Process,\n FROM File TO FeatureCluster,\n FROM Function TO FeatureCluster,\n FROM Method TO FeatureCluster,\n FROM Class TO FeatureCluster,\n FROM Interface TO FeatureCluster,\n FROM CodeElement TO FeatureCluster,\n FROM Section TO FeatureCluster,\n FROM Route TO FeatureCluster,\n FROM Tool TO FeatureCluster,\n FROM Process TO FeatureCluster,\n FROM `Struct` TO FeatureCluster,\n FROM `Enum` TO FeatureCluster,\n FROM `Macro` TO FeatureCluster,\n FROM `Typedef` TO FeatureCluster,\n FROM `Union` TO FeatureCluster,\n FROM `Namespace` TO FeatureCluster,\n FROM `Trait` TO FeatureCluster,\n FROM `Impl` TO FeatureCluster,\n FROM `TypeAlias` TO FeatureCluster,\n FROM `Const` TO FeatureCluster,\n FROM `Static` TO FeatureCluster,\n FROM `Variable` TO FeatureCluster,\n FROM `Property` TO FeatureCluster,\n FROM `Record` TO FeatureCluster,\n FROM `Delegate` TO FeatureCluster,\n FROM `Annotation` TO FeatureCluster,\n FROM `Constructor` TO FeatureCluster,\n FROM `Template` TO FeatureCluster,\n FROM `Module` TO FeatureCluster,\n FROM FeatureCluster TO FeatureCluster,\n type STRING,\n confidence DOUBLE,\n reason STRING,\n step INT32\n)";
46
47
  export declare const EMBEDDING_DIMS: number;
47
48
  /** HNSW vector index name for the CodeEmbedding table. */
48
49
  export declare const EMBEDDING_INDEX_NAME = "code_embedding_idx";
@@ -139,6 +139,30 @@ CREATE NODE TABLE Process (
139
139
  PRIMARY KEY (id)
140
140
  )`;
141
141
  // ============================================================================
142
+ // FEATURE CLUSTER NODE TABLE (human-facing feature/domain clusters)
143
+ // ============================================================================
144
+ export const FEATURE_CLUSTER_SCHEMA = `
145
+ CREATE NODE TABLE FeatureCluster (
146
+ id STRING,
147
+ name STRING,
148
+ slug STRING,
149
+ featureKind STRING,
150
+ summary STRING,
151
+ description STRING,
152
+ repo STRING,
153
+ service STRING,
154
+ signals STRING[],
155
+ memberCount INT32,
156
+ entryPointIds STRING[],
157
+ routes STRING[],
158
+ tools STRING[],
159
+ testCoverageHints STRING[],
160
+ lastIndexedCommit STRING,
161
+ confidence DOUBLE,
162
+ source STRING,
163
+ PRIMARY KEY (id)
164
+ )`;
165
+ // ============================================================================
142
166
  // MULTI-LANGUAGE NODE TABLE SCHEMAS
143
167
  // ============================================================================
144
168
  // Generic code element with startLine/endLine for C, C++, Rust, Go, Java, C#
@@ -417,6 +441,36 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
417
441
  FROM CodeElement TO Process,
418
442
  FROM Route TO Process,
419
443
  FROM Tool TO Process,
444
+ FROM File TO FeatureCluster,
445
+ FROM Function TO FeatureCluster,
446
+ FROM Method TO FeatureCluster,
447
+ FROM Class TO FeatureCluster,
448
+ FROM Interface TO FeatureCluster,
449
+ FROM CodeElement TO FeatureCluster,
450
+ FROM Section TO FeatureCluster,
451
+ FROM Route TO FeatureCluster,
452
+ FROM Tool TO FeatureCluster,
453
+ FROM Process TO FeatureCluster,
454
+ FROM \`Struct\` TO FeatureCluster,
455
+ FROM \`Enum\` TO FeatureCluster,
456
+ FROM \`Macro\` TO FeatureCluster,
457
+ FROM \`Typedef\` TO FeatureCluster,
458
+ FROM \`Union\` TO FeatureCluster,
459
+ FROM \`Namespace\` TO FeatureCluster,
460
+ FROM \`Trait\` TO FeatureCluster,
461
+ FROM \`Impl\` TO FeatureCluster,
462
+ FROM \`TypeAlias\` TO FeatureCluster,
463
+ FROM \`Const\` TO FeatureCluster,
464
+ FROM \`Static\` TO FeatureCluster,
465
+ FROM \`Variable\` TO FeatureCluster,
466
+ FROM \`Property\` TO FeatureCluster,
467
+ FROM \`Record\` TO FeatureCluster,
468
+ FROM \`Delegate\` TO FeatureCluster,
469
+ FROM \`Annotation\` TO FeatureCluster,
470
+ FROM \`Constructor\` TO FeatureCluster,
471
+ FROM \`Template\` TO FeatureCluster,
472
+ FROM \`Module\` TO FeatureCluster,
473
+ FROM FeatureCluster TO FeatureCluster,
420
474
  type STRING,
421
475
  confidence DOUBLE,
422
476
  reason STRING,
@@ -471,6 +525,7 @@ export const NODE_SCHEMA_QUERIES = [
471
525
  CODE_ELEMENT_SCHEMA,
472
526
  COMMUNITY_SCHEMA,
473
527
  PROCESS_SCHEMA,
528
+ FEATURE_CLUSTER_SCHEMA,
474
529
  // Multi-language support
475
530
  STRUCT_SCHEMA,
476
531
  ENUM_SCHEMA,
@@ -16,6 +16,7 @@ import { pipeline, env } from '@huggingface/transformers';
16
16
  import { existsSync } from 'fs';
17
17
  import { execFileSync } from 'child_process';
18
18
  import { join, dirname } from 'path';
19
+ import { homedir } from 'os';
19
20
  import { createRequire } from 'module';
20
21
  import { DEFAULT_EMBEDDING_CONFIG } from './types.js';
21
22
  import { isHttpMode, getHttpDimensions, httpEmbed } from './http-client.js';
@@ -134,8 +135,9 @@ export const initEmbedder = async (onProgress, config = {}, forceDevice) => {
134
135
  // Default cache to user-writable location. transformers.js defaults to
135
136
  // ./node_modules/.cache inside its own install dir, which is unwritable
136
137
  // when codragraph is installed globally (e.g. /usr/lib/node_modules/).
137
- // Respect HF_HOME if set, otherwise fall back to ~/.cache/huggingface.
138
- env.cacheDir = process.env.HF_HOME ?? `${process.env.HOME}/.cache/huggingface`;
138
+ // Respect HF_HOME if set, otherwise fall back to a user-writable cache
139
+ // path using Node's OS-aware home directory resolution.
140
+ env.cacheDir = process.env.HF_HOME ?? join(homedir(), '.cache', 'huggingface');
139
141
  const isDev = process.env.NODE_ENV === 'development';
140
142
  if (isDev) {
141
143
  console.log(`🧠 Loading embedding model: ${finalConfig.modelId}`);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Codragraph-side glue for the Phase 4 versioned graph store.
2
+ * CodraGraph-side glue for the Phase 4 versioned graph store.
3
3
  *
4
4
  * The graphstore package itself is engine-agnostic; everything that
5
5
  * touches LadybugDB lives here, in codragraph/. Best-effort by design:
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Codragraph-side glue for the Phase 4 versioned graph store.
2
+ * CodraGraph-side glue for the Phase 4 versioned graph store.
3
3
  *
4
4
  * The graphstore package itself is engine-agnostic; everything that
5
5
  * touches LadybugDB lives here, in codragraph/. Best-effort by design:
@@ -41,6 +41,19 @@ export interface GroupToolPort {
41
41
  file_path?: string;
42
42
  include_content?: boolean;
43
43
  }): Promise<unknown>;
44
+ featureClusters?(repo: GroupRepoHandle, params: {
45
+ query?: string;
46
+ limit?: number;
47
+ }): Promise<unknown>;
48
+ featureContext?(repo: GroupRepoHandle, params: {
49
+ name: string;
50
+ limit?: number;
51
+ }): Promise<unknown>;
52
+ featureImpact?(repo: GroupRepoHandle, params: {
53
+ name: string;
54
+ direction?: 'upstream' | 'downstream' | 'both';
55
+ limit?: number;
56
+ }): Promise<unknown>;
44
57
  }
45
58
  export declare class GroupService {
46
59
  private readonly port;
@@ -51,5 +64,8 @@ export declare class GroupService {
51
64
  groupImpact(params: Record<string, unknown>): Promise<unknown>;
52
65
  groupContext(params: Record<string, unknown>): Promise<GroupContextResult>;
53
66
  groupQuery(params: Record<string, unknown>): Promise<unknown>;
67
+ groupFeatureClusters(params: Record<string, unknown>): Promise<unknown>;
68
+ groupFeatureContext(params: Record<string, unknown>): Promise<unknown>;
69
+ groupFeatureImpact(params: Record<string, unknown>): Promise<unknown>;
54
70
  groupStatus(params: Record<string, unknown>): Promise<unknown>;
55
71
  }