gitnexus 1.3.3 → 1.3.5

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 (49) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/ai-context.js +23 -52
  3. package/dist/cli/analyze.js +4 -1
  4. package/dist/cli/index.js +1 -0
  5. package/dist/cli/mcp.js +11 -22
  6. package/dist/cli/serve.d.ts +1 -0
  7. package/dist/cli/serve.js +2 -1
  8. package/dist/cli/setup.js +2 -2
  9. package/dist/cli/wiki.js +6 -2
  10. package/dist/config/supported-languages.d.ts +3 -1
  11. package/dist/config/supported-languages.js +2 -2
  12. package/dist/core/embeddings/embedder.js +40 -1
  13. package/dist/core/graph/types.d.ts +2 -0
  14. package/dist/core/ingestion/call-processor.js +39 -0
  15. package/dist/core/ingestion/entry-point-scoring.js +49 -1
  16. package/dist/core/ingestion/framework-detection.d.ts +15 -4
  17. package/dist/core/ingestion/framework-detection.js +146 -5
  18. package/dist/core/ingestion/import-processor.js +140 -0
  19. package/dist/core/ingestion/parsing-processor.js +61 -8
  20. package/dist/core/ingestion/process-processor.js +7 -1
  21. package/dist/core/ingestion/tree-sitter-queries.d.ts +2 -0
  22. package/dist/core/ingestion/tree-sitter-queries.js +414 -282
  23. package/dist/core/ingestion/utils.js +8 -0
  24. package/dist/core/ingestion/workers/parse-worker.d.ts +3 -0
  25. package/dist/core/ingestion/workers/parse-worker.js +240 -0
  26. package/dist/core/kuzu/csv-generator.js +4 -2
  27. package/dist/core/kuzu/kuzu-adapter.d.ts +9 -0
  28. package/dist/core/kuzu/kuzu-adapter.js +68 -9
  29. package/dist/core/kuzu/schema.d.ts +6 -6
  30. package/dist/core/kuzu/schema.js +16 -0
  31. package/dist/core/tree-sitter/parser-loader.js +4 -0
  32. package/dist/core/wiki/generator.js +2 -2
  33. package/dist/mcp/local/local-backend.js +25 -13
  34. package/dist/mcp/server.d.ts +9 -0
  35. package/dist/mcp/server.js +13 -2
  36. package/dist/mcp/staleness.js +2 -2
  37. package/dist/server/api.d.ts +7 -5
  38. package/dist/server/api.js +145 -127
  39. package/dist/server/mcp-http.d.ts +13 -0
  40. package/dist/server/mcp-http.js +100 -0
  41. package/package.json +6 -2
  42. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  43. package/skills/gitnexus-cli.md +82 -0
  44. package/skills/{debugging.md → gitnexus-debugging.md} +12 -8
  45. package/skills/{exploring.md → gitnexus-exploring.md} +10 -7
  46. package/skills/gitnexus-guide.md +64 -0
  47. package/skills/{impact-analysis.md → gitnexus-impact-analysis.md} +14 -11
  48. package/skills/gitnexus-pr-review.md +163 -0
  49. package/skills/{refactoring.md → gitnexus-refactoring.md} +15 -7
package/README.md CHANGED
@@ -156,7 +156,7 @@ GitNexus supports indexing multiple repositories. Each `gitnexus analyze` regist
156
156
 
157
157
  ## Supported Languages
158
158
 
159
- TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust
159
+ TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust, PHP, Swift
160
160
 
161
161
  ## Agent Skills
162
162
 
@@ -29,12 +29,8 @@ function generateGitNexusContent(projectName, stats) {
29
29
 
30
30
  This project is indexed by GitNexus as **${projectName}** (${stats.nodes || 0} symbols, ${stats.edges || 0} relationships, ${stats.processes || 0} execution flows).
31
31
 
32
- GitNexus provides a knowledge graph over this codebase — call chains, blast radius, execution flows, and semantic search.
33
-
34
32
  ## Always Start Here
35
33
 
36
- For any task involving code understanding, debugging, impact analysis, or refactoring, you must:
37
-
38
34
  1. **Read \`gitnexus://repo/{name}/context\`** — codebase overview + check index freshness
39
35
  2. **Match your task to a skill below** and **read that skill file**
40
36
  3. **Follow the skill's workflow and checklist**
@@ -45,45 +41,12 @@ For any task involving code understanding, debugging, impact analysis, or refact
45
41
 
46
42
  | Task | Read this skill file |
47
43
  |------|---------------------|
48
- | Understand architecture / "How does X work?" | \`.claude/skills/gitnexus/exploring/SKILL.md\` |
49
- | Blast radius / "What breaks if I change X?" | \`.claude/skills/gitnexus/impact-analysis/SKILL.md\` |
50
- | Trace bugs / "Why is X failing?" | \`.claude/skills/gitnexus/debugging/SKILL.md\` |
51
- | Rename / extract / split / refactor | \`.claude/skills/gitnexus/refactoring/SKILL.md\` |
52
-
53
- ## Tools Reference
54
-
55
- | Tool | What it gives you |
56
- |------|-------------------|
57
- | \`query\` | Process-grouped code intelligence — execution flows related to a concept |
58
- | \`context\` | 360-degree symbol view — categorized refs, processes it participates in |
59
- | \`impact\` | Symbol blast radius — what breaks at depth 1/2/3 with confidence |
60
- | \`detect_changes\` | Git-diff impact — what do your current changes affect |
61
- | \`rename\` | Multi-file coordinated rename with confidence-tagged edits |
62
- | \`cypher\` | Raw graph queries (read \`gitnexus://repo/{name}/schema\` first) |
63
- | \`list_repos\` | Discover indexed repos |
64
-
65
- ## Resources Reference
66
-
67
- Lightweight reads (~100-500 tokens) for navigation:
68
-
69
- | Resource | Content |
70
- |----------|---------|
71
- | \`gitnexus://repo/{name}/context\` | Stats, staleness check |
72
- | \`gitnexus://repo/{name}/clusters\` | All functional areas with cohesion scores |
73
- | \`gitnexus://repo/{name}/cluster/{clusterName}\` | Area members |
74
- | \`gitnexus://repo/{name}/processes\` | All execution flows |
75
- | \`gitnexus://repo/{name}/process/{processName}\` | Step-by-step trace |
76
- | \`gitnexus://repo/{name}/schema\` | Graph schema for Cypher |
77
-
78
- ## Graph Schema
79
-
80
- **Nodes:** File, Function, Class, Interface, Method, Community, Process
81
- **Edges (via CodeRelation.type):** CALLS, IMPORTS, EXTENDS, IMPLEMENTS, DEFINES, MEMBER_OF, STEP_IN_PROCESS
82
-
83
- \`\`\`cypher
84
- MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"})
85
- RETURN caller.name, caller.filePath
86
- \`\`\`
44
+ | Understand architecture / "How does X work?" | \`.claude/skills/gitnexus/gitnexus-exploring/SKILL.md\` |
45
+ | Blast radius / "What breaks if I change X?" | \`.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md\` |
46
+ | Trace bugs / "Why is X failing?" | \`.claude/skills/gitnexus/gitnexus-debugging/SKILL.md\` |
47
+ | Rename / extract / split / refactor | \`.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md\` |
48
+ | Tools, resources, schema reference | \`.claude/skills/gitnexus/gitnexus-guide/SKILL.md\` |
49
+ | Index, status, clean, wiki CLI commands | \`.claude/skills/gitnexus/gitnexus-cli/SKILL.md\` |
87
50
 
88
51
  ${GITNEXUS_END_MARKER}`;
89
52
  }
@@ -138,20 +101,28 @@ async function installSkills(repoPath) {
138
101
  // Skill definitions bundled with the package
139
102
  const skills = [
140
103
  {
141
- name: 'exploring',
142
- description: 'Navigate unfamiliar code using GitNexus knowledge graph',
104
+ name: 'gitnexus-exploring',
105
+ description: 'Use when the user asks how code works, wants to understand architecture, trace execution flows, or explore unfamiliar parts of the codebase. Examples: "How does X work?", "What calls this function?", "Show me the auth flow"',
106
+ },
107
+ {
108
+ name: 'gitnexus-debugging',
109
+ description: 'Use when the user is debugging a bug, tracing an error, or asking why something fails. Examples: "Why is X failing?", "Where does this error come from?", "Trace this bug"',
110
+ },
111
+ {
112
+ name: 'gitnexus-impact-analysis',
113
+ description: 'Use when the user wants to know what will break if they change something, or needs safety analysis before editing code. Examples: "Is it safe to change X?", "What depends on this?", "What will break?"',
143
114
  },
144
115
  {
145
- name: 'debugging',
146
- description: 'Trace bugs through call chains using knowledge graph',
116
+ name: 'gitnexus-refactoring',
117
+ description: 'Use when the user wants to rename, extract, split, move, or restructure code safely. Examples: "Rename this function", "Extract this into a module", "Refactor this class", "Move this to a separate file"',
147
118
  },
148
119
  {
149
- name: 'impact-analysis',
150
- description: 'Analyze blast radius before making code changes',
120
+ name: 'gitnexus-guide',
121
+ description: 'Use when the user asks about GitNexus itself — available tools, how to query the knowledge graph, MCP resources, graph schema, or workflow reference. Examples: "What GitNexus tools are available?", "How do I use GitNexus?"',
151
122
  },
152
123
  {
153
- name: 'refactoring',
154
- description: 'Plan safe refactors using blast radius and dependency mapping',
124
+ name: 'gitnexus-cli',
125
+ description: 'Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki, or list indexed repos. Examples: "Index this repo", "Reanalyze the codebase", "Generate a wiki"',
155
126
  },
156
127
  ];
157
128
  for (const skill of skills) {
@@ -169,7 +140,7 @@ async function installSkills(repoPath) {
169
140
  catch {
170
141
  // Fallback: generate minimal skill content
171
142
  skillContent = `---
172
- name: gitnexus-${skill.name}
143
+ name: ${skill.name}
173
144
  description: ${skill.description}
174
145
  ---
175
146
 
@@ -9,7 +9,9 @@ import v8 from 'v8';
9
9
  import cliProgress from 'cli-progress';
10
10
  import { runPipelineFromRepo } from '../core/ingestion/pipeline.js';
11
11
  import { initKuzu, loadGraphToKuzu, getKuzuStats, executeQuery, executeWithReusedStatement, closeKuzu, createFTSIndex, loadCachedEmbeddings } from '../core/kuzu/kuzu-adapter.js';
12
- import { runEmbeddingPipeline } from '../core/embeddings/embedding-pipeline.js';
12
+ // Embedding imports are lazy (dynamic import) so onnxruntime-node is never
13
+ // loaded when embeddings are not requested. This avoids crashes on Node
14
+ // versions whose ABI is not yet supported by the native binary (#89).
13
15
  // disposeEmbedder intentionally not called — ONNX Runtime segfaults on cleanup (see #38)
14
16
  import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath } from '../storage/repo-manager.js';
15
17
  import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js';
@@ -231,6 +233,7 @@ export const analyzeCommand = async (inputPath, options) => {
231
233
  if (!embeddingSkipped) {
232
234
  updateBar(90, 'Loading embedding model...');
233
235
  const t0Emb = Date.now();
236
+ const { runEmbeddingPipeline } = await import('../core/embeddings/embedding-pipeline.js');
234
237
  await runEmbeddingPipeline(executeQuery, executeWithReusedStatement, (progress) => {
235
238
  const scaled = 90 + Math.round((progress.percent / 100) * 8);
236
239
  const label = progress.phase === 'loading-model' ? 'Loading embedding model...' : `Embedding ${progress.nodesProcessed || 0}/${progress.totalNodes || '?'}`;
package/dist/cli/index.js CHANGED
@@ -50,6 +50,7 @@ program
50
50
  .command('serve')
51
51
  .description('Start local HTTP server for web UI connection')
52
52
  .option('-p, --port <port>', 'Port number', '4747')
53
+ .option('--host <host>', 'Bind address (default: 127.0.0.1, use 0.0.0.0 for remote access)')
53
54
  .action(serveCommand);
54
55
  program
55
56
  .command('mcp')
package/dist/cli/mcp.js CHANGED
@@ -7,7 +7,6 @@
7
7
  */
8
8
  import { startMCPServer } from '../mcp/server.js';
9
9
  import { LocalBackend } from '../mcp/local/local-backend.js';
10
- import { listRegisteredRepos } from '../storage/repo-manager.js';
11
10
  export const mcpCommand = async () => {
12
11
  // Prevent unhandled errors from crashing the MCP server process.
13
12
  // KuzuDB lock conflicts and transient errors should degrade gracefully.
@@ -18,28 +17,18 @@ export const mcpCommand = async () => {
18
17
  const msg = reason instanceof Error ? reason.message : String(reason);
19
18
  console.error(`GitNexus MCP: unhandled rejection — ${msg}`);
20
19
  });
21
- // Load all registered repos
22
- const entries = await listRegisteredRepos({ validate: true });
23
- if (entries.length === 0) {
24
- console.error('');
25
- console.error(' GitNexus: No indexed repositories found.');
26
- console.error('');
27
- console.error(' To get started:');
28
- console.error(' 1. cd into a git repository');
29
- console.error(' 2. Run: gitnexus analyze');
30
- console.error(' 3. Restart your editor');
31
- console.error('');
32
- process.exit(1);
33
- }
34
- // Initialize multi-repo backend from registry
20
+ // Initialize multi-repo backend from registry.
21
+ // The server starts even with 0 repos — tools call refreshRepos() lazily,
22
+ // so repos indexed after the server starts are discovered automatically.
35
23
  const backend = new LocalBackend();
36
- const ok = await backend.init();
37
- if (!ok) {
38
- console.error('GitNexus: Failed to initialize backend from registry.');
39
- process.exit(1);
24
+ await backend.init();
25
+ const repos = await backend.listRepos();
26
+ if (repos.length === 0) {
27
+ console.error('GitNexus: No indexed repos yet. Run `gitnexus analyze` in a git repo — the server will pick it up automatically.');
28
+ }
29
+ else {
30
+ console.error(`GitNexus: MCP server starting with ${repos.length} repo(s): ${repos.map(r => r.name).join(', ')}`);
40
31
  }
41
- const repoNames = (await backend.listRepos()).map(r => r.name);
42
- console.error(`GitNexus: MCP server starting with ${repoNames.length} repo(s): ${repoNames.join(', ')}`);
43
- // Start MCP server (serves all repos)
32
+ // Start MCP server (serves all repos, discovers new ones lazily)
44
33
  await startMCPServer(backend);
45
34
  };
@@ -1,3 +1,4 @@
1
1
  export declare const serveCommand: (options?: {
2
2
  port?: string;
3
+ host?: string;
3
4
  }) => Promise<void>;
package/dist/cli/serve.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createServer } from '../server/api.js';
2
2
  export const serveCommand = async (options) => {
3
3
  const port = Number(options?.port ?? 4747);
4
- await createServer(port);
4
+ const host = options?.host ?? '127.0.0.1';
5
+ await createServer(port, host);
5
6
  };
package/dist/cli/setup.js CHANGED
@@ -198,7 +198,7 @@ async function setupOpenCode(result) {
198
198
  }
199
199
  }
200
200
  // ─── Skill Installation ───────────────────────────────────────────
201
- const SKILL_NAMES = ['exploring', 'debugging', 'impact-analysis', 'refactoring'];
201
+ const SKILL_NAMES = ['gitnexus-exploring', 'gitnexus-debugging', 'gitnexus-impact-analysis', 'gitnexus-refactoring', 'gitnexus-guide', 'gitnexus-cli'];
202
202
  /**
203
203
  * Install GitNexus skills to a target directory.
204
204
  * Each skill is installed as {targetDir}/gitnexus-{skillName}/SKILL.md
@@ -212,7 +212,7 @@ async function installSkillsTo(targetDir) {
212
212
  const installed = [];
213
213
  const skillsRoot = path.join(__dirname, '..', '..', 'skills');
214
214
  for (const skillName of SKILL_NAMES) {
215
- const skillDir = path.join(targetDir, `gitnexus-${skillName}`);
215
+ const skillDir = path.join(targetDir, skillName);
216
216
  try {
217
217
  // Try directory-based skill first (skills/{name}/SKILL.md)
218
218
  const dirSource = path.join(skillsRoot, skillName);
package/dist/cli/wiki.js CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import path from 'path';
8
8
  import readline from 'readline';
9
- import { execSync } from 'child_process';
9
+ import { execSync, execFileSync } from 'child_process';
10
10
  import cliProgress from 'cli-progress';
11
11
  import { getGitRoot, isGitRepo } from '../storage/git.js';
12
12
  import { getStoragePaths, loadMeta, loadCLIConfig, saveCLIConfig } from '../storage/repo-manager.js';
@@ -299,7 +299,11 @@ function hasGhCLI() {
299
299
  }
300
300
  function publishGist(htmlPath) {
301
301
  try {
302
- const output = execSync(`gh gist create "${htmlPath}" --desc "Repository Wiki — generated by GitNexus" --public`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
302
+ const output = execFileSync('gh', [
303
+ 'gist', 'create', htmlPath,
304
+ '--desc', 'Repository Wiki — generated by GitNexus',
305
+ '--public',
306
+ ], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
303
307
  // gh gist create prints the gist URL as the last line
304
308
  const lines = output.split('\n');
305
309
  const gistUrl = lines.find(l => l.includes('gist.github.com')) || lines[lines.length - 1];
@@ -7,5 +7,7 @@ export declare enum SupportedLanguages {
7
7
  CPlusPlus = "cpp",
8
8
  CSharp = "csharp",
9
9
  Go = "go",
10
- Rust = "rust"
10
+ Rust = "rust",
11
+ PHP = "php",
12
+ Swift = "swift"
11
13
  }
@@ -9,7 +9,7 @@ export var SupportedLanguages;
9
9
  SupportedLanguages["CSharp"] = "csharp";
10
10
  SupportedLanguages["Go"] = "go";
11
11
  SupportedLanguages["Rust"] = "rust";
12
- // PHP = 'php',
12
+ SupportedLanguages["PHP"] = "php";
13
13
  // Ruby = 'ruby',
14
- // Swift = 'swift',
14
+ SupportedLanguages["Swift"] = "swift";
15
15
  })(SupportedLanguages || (SupportedLanguages = {}));
@@ -13,7 +13,44 @@ if (!process.env.ORT_LOG_LEVEL) {
13
13
  process.env.ORT_LOG_LEVEL = '3';
14
14
  }
15
15
  import { pipeline, env } from '@huggingface/transformers';
16
+ import { existsSync } from 'fs';
17
+ import { execFileSync } from 'child_process';
18
+ import { join } from 'path';
16
19
  import { DEFAULT_EMBEDDING_CONFIG } from './types.js';
20
+ /**
21
+ * Check whether CUDA libraries are actually available on this system.
22
+ * ONNX Runtime's native layer crashes (uncatchable) if we attempt CUDA
23
+ * without the required shared libraries, so we probe first.
24
+ *
25
+ * Checks the dynamic linker cache (ldconfig) which covers all architectures
26
+ * and install paths, then falls back to CUDA_PATH / LD_LIBRARY_PATH env vars.
27
+ */
28
+ function isCudaAvailable() {
29
+ // Primary: query the dynamic linker cache — covers all architectures,
30
+ // distro layouts, and custom install paths registered with ldconfig
31
+ try {
32
+ const out = execFileSync('ldconfig', ['-p'], { timeout: 3000, encoding: 'utf-8' });
33
+ if (out.includes('libcublasLt.so.12'))
34
+ return true;
35
+ }
36
+ catch {
37
+ // ldconfig not available (e.g. non-standard container)
38
+ }
39
+ // Fallback: check CUDA_PATH and LD_LIBRARY_PATH for environments where
40
+ // ldconfig doesn't know about the CUDA install (conda, manual /opt/cuda, etc.)
41
+ for (const envVar of ['CUDA_PATH', 'LD_LIBRARY_PATH']) {
42
+ const val = process.env[envVar];
43
+ if (!val)
44
+ continue;
45
+ for (const dir of val.split(':').filter(Boolean)) {
46
+ if (existsSync(join(dir, 'lib64', 'libcublasLt.so.12')) ||
47
+ existsSync(join(dir, 'lib', 'libcublasLt.so.12')) ||
48
+ existsSync(join(dir, 'libcublasLt.so.12')))
49
+ return true;
50
+ }
51
+ }
52
+ return false;
53
+ }
17
54
  // Module-level state for singleton pattern
18
55
  let embedderInstance = null;
19
56
  let isInitializing = false;
@@ -45,8 +82,10 @@ export const initEmbedder = async (onProgress, config = {}, forceDevice) => {
45
82
  const finalConfig = { ...DEFAULT_EMBEDDING_CONFIG, ...config };
46
83
  // On Windows, use DirectML for GPU acceleration (via DirectX12)
47
84
  // CUDA is only available on Linux x64 with onnxruntime-node
85
+ // Probe for CUDA first — ONNX Runtime crashes (uncatchable native error)
86
+ // if we attempt CUDA without the required shared libraries
48
87
  const isWindows = process.platform === 'win32';
49
- const gpuDevice = isWindows ? 'dml' : 'cuda';
88
+ const gpuDevice = isWindows ? 'dml' : (isCudaAvailable() ? 'cuda' : 'cpu');
50
89
  let requestedDevice = forceDevice || (finalConfig.device === 'auto' ? gpuDevice : finalConfig.device);
51
90
  initPromise = (async () => {
52
91
  try {
@@ -6,6 +6,8 @@ export type NodeProperties = {
6
6
  endLine?: number;
7
7
  language?: string;
8
8
  isExported?: boolean;
9
+ astFrameworkMultiplier?: number;
10
+ astFrameworkReason?: string;
9
11
  heuristicLabel?: string;
10
12
  cohesion?: number;
11
13
  symbolCount?: number;
@@ -31,6 +31,9 @@ const FUNCTION_NODE_TYPES = new Set([
31
31
  // Rust
32
32
  'function_item',
33
33
  'impl_item', // Methods inside impl blocks
34
+ // Swift
35
+ 'init_declaration',
36
+ 'deinit_declaration',
34
37
  ]);
35
38
  /**
36
39
  * Walk up the AST from a node to find the enclosing function/method.
@@ -44,6 +47,11 @@ const findEnclosingFunction = (node, filePath, symbolTable) => {
44
47
  let funcName = null;
45
48
  let label = 'Function';
46
49
  // Different node types have different name locations
50
+ // Swift init/deinit — handle before generic cases (more specific)
51
+ if (current.type === 'init_declaration' || current.type === 'deinit_declaration') {
52
+ const funcName = current.type === 'init_declaration' ? 'init' : 'deinit';
53
+ return generateId('Constructor', `${filePath}:${funcName}`);
54
+ }
47
55
  if (current.type === 'function_declaration' ||
48
56
  current.type === 'function_definition' ||
49
57
  current.type === 'async_function_declaration' ||
@@ -282,6 +290,37 @@ const BUILT_IN_NAMES = new Set([
282
290
  'mutex_lock', 'mutex_unlock', 'mutex_init',
283
291
  'kfree', 'kmalloc', 'kzalloc', 'kcalloc', 'krealloc', 'kvmalloc', 'kvfree',
284
292
  'get', 'put',
293
+ // Swift/iOS built-ins and standard library
294
+ 'print', 'debugPrint', 'dump', 'fatalError', 'precondition', 'preconditionFailure',
295
+ 'assert', 'assertionFailure', 'NSLog',
296
+ 'abs', 'min', 'max', 'zip', 'stride', 'sequence', 'repeatElement',
297
+ 'swap', 'withUnsafePointer', 'withUnsafeMutablePointer', 'withUnsafeBytes',
298
+ 'autoreleasepool', 'unsafeBitCast', 'unsafeDowncast', 'numericCast',
299
+ 'type', 'MemoryLayout',
300
+ // Swift collection/string methods (common noise)
301
+ 'map', 'flatMap', 'compactMap', 'filter', 'reduce', 'forEach', 'contains',
302
+ 'first', 'last', 'prefix', 'suffix', 'dropFirst', 'dropLast',
303
+ 'sorted', 'reversed', 'enumerated', 'joined', 'split',
304
+ 'append', 'insert', 'remove', 'removeAll', 'removeFirst', 'removeLast',
305
+ 'isEmpty', 'count', 'index', 'startIndex', 'endIndex',
306
+ // UIKit/Foundation common methods (noise in call graph)
307
+ 'addSubview', 'removeFromSuperview', 'layoutSubviews', 'setNeedsLayout',
308
+ 'layoutIfNeeded', 'setNeedsDisplay', 'invalidateIntrinsicContentSize',
309
+ 'addTarget', 'removeTarget', 'addGestureRecognizer',
310
+ 'addConstraint', 'addConstraints', 'removeConstraint', 'removeConstraints',
311
+ 'NSLocalizedString', 'Bundle',
312
+ 'reloadData', 'reloadSections', 'reloadRows', 'performBatchUpdates',
313
+ 'register', 'dequeueReusableCell', 'dequeueReusableSupplementaryView',
314
+ 'beginUpdates', 'endUpdates', 'insertRows', 'deleteRows', 'insertSections', 'deleteSections',
315
+ 'present', 'dismiss', 'pushViewController', 'popViewController', 'popToRootViewController',
316
+ 'performSegue', 'prepare',
317
+ // GCD / async
318
+ 'DispatchQueue', 'async', 'sync', 'asyncAfter',
319
+ 'Task', 'withCheckedContinuation', 'withCheckedThrowingContinuation',
320
+ // Combine
321
+ 'sink', 'store', 'assign', 'receive', 'subscribe',
322
+ // Notification / KVO
323
+ 'addObserver', 'removeObserver', 'post', 'NotificationCenter',
285
324
  ]);
286
325
  const isBuiltInOrNoise = (name) => BUILT_IN_NAMES.has(name);
287
326
  /**
@@ -91,6 +91,45 @@ const ENTRY_POINT_PATTERNS = {
91
91
  /^Run$/, // Run methods
92
92
  /^Start$/, // Start methods
93
93
  ],
94
+ // Swift / iOS
95
+ 'swift': [
96
+ /^viewDidLoad$/, // UIKit lifecycle
97
+ /^viewWillAppear$/, // UIKit lifecycle
98
+ /^viewDidAppear$/, // UIKit lifecycle
99
+ /^viewWillDisappear$/, // UIKit lifecycle
100
+ /^viewDidDisappear$/, // UIKit lifecycle
101
+ /^application\(/, // AppDelegate methods
102
+ /^scene\(/, // SceneDelegate methods
103
+ /^body$/, // SwiftUI View.body
104
+ /Coordinator$/, // Coordinator pattern
105
+ /^sceneDidBecomeActive$/, // SceneDelegate lifecycle
106
+ /^sceneWillResignActive$/, // SceneDelegate lifecycle
107
+ /^didFinishLaunchingWithOptions$/, // AppDelegate
108
+ /ViewController$/, // ViewController classes
109
+ /^configure[A-Z]/, // Configuration methods
110
+ /^setup[A-Z]/, // Setup methods
111
+ /^makeBody$/, // SwiftUI ViewModifier
112
+ ],
113
+ // PHP / Laravel
114
+ 'php': [
115
+ /Controller$/, // UserController (class name convention)
116
+ /^handle$/, // Job::handle(), Listener::handle()
117
+ /^execute$/, // Command::execute()
118
+ /^boot$/, // ServiceProvider::boot()
119
+ /^register$/, // ServiceProvider::register()
120
+ /^__invoke$/, // Invokable controllers/actions
121
+ /^(index|show|store|update|destroy|create|edit)$/, // RESTful resource methods
122
+ /^(get|post|put|delete|patch)[A-Z]/, // Explicit HTTP method actions
123
+ /^run$/, // Command/Job run()
124
+ /^fire$/, // Event fire()
125
+ /^dispatch$/, // Dispatchable jobs
126
+ /Service$/, // UserService (Service layer)
127
+ /Repository$/, // UserRepository (Repository pattern)
128
+ /^find$/, // Repository::find()
129
+ /^findAll$/, // Repository::findAll()
130
+ /^save$/, // Repository::save()
131
+ /^delete$/, // Repository::delete()
132
+ ],
94
133
  };
95
134
  // ============================================================================
96
135
  // UTILITY PATTERNS - Functions that should be penalized
@@ -209,9 +248,18 @@ export function isTestFile(filePath) {
209
248
  p.includes('/src/test/') ||
210
249
  // Rust test patterns (inline tests are different, but test files)
211
250
  p.includes('/tests/') ||
251
+ // Swift/iOS test patterns
252
+ p.endsWith('tests.swift') ||
253
+ p.endsWith('test.swift') ||
254
+ p.includes('uitests/') ||
212
255
  // C# test patterns
213
256
  p.includes('.tests/') ||
214
- p.includes('tests.cs'));
257
+ p.includes('tests.cs') ||
258
+ // PHP/Laravel test patterns
259
+ p.endsWith('test.php') ||
260
+ p.endsWith('spec.php') ||
261
+ p.includes('/tests/feature/') ||
262
+ p.includes('/tests/unit/'));
215
263
  }
216
264
  /**
217
265
  * Check if a file path is likely a utility/helper file
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Framework Detection
3
3
  *
4
- * Detects frameworks from file path patterns and provides entry point multipliers.
5
- * This enables framework-aware entry point scoring.
4
+ * Detects frameworks from:
5
+ * 1) file path patterns
6
+ * 2) AST definition text (decorators/annotations/attributes)
7
+ * and provides entry point multipliers for process scoring.
6
8
  *
7
9
  * DESIGN: Returns null for unknown frameworks, which causes a 1.0 multiplier
8
10
  * (no bonus, no penalty) - same behavior as before this feature.
@@ -20,8 +22,8 @@ export interface FrameworkHint {
20
22
  */
21
23
  export declare function detectFrameworkFromPath(filePath: string): FrameworkHint | null;
22
24
  /**
23
- * Patterns that indicate entry points within code (for future AST-based detection)
24
- * These would require parsing decorators/annotations in the code itself.
25
+ * Patterns that indicate framework entry points within code definitions.
26
+ * These are matched against AST node text (class/method/function declaration text).
25
27
  */
26
28
  export declare const FRAMEWORK_AST_PATTERNS: {
27
29
  nestjs: string[];
@@ -32,7 +34,16 @@ export declare const FRAMEWORK_AST_PATTERNS: {
32
34
  jaxrs: string[];
33
35
  aspnet: string[];
34
36
  'go-http': string[];
37
+ laravel: string[];
35
38
  actix: string[];
36
39
  axum: string[];
37
40
  rocket: string[];
41
+ uikit: string[];
42
+ swiftui: string[];
43
+ combine: string[];
38
44
  };
45
+ /**
46
+ * Detect framework entry points from AST definition text (decorators/annotations/attributes).
47
+ * Returns null if no known pattern is found.
48
+ */
49
+ export declare function detectFrameworkFromAST(language: string, definitionText: string): FrameworkHint | null;