gitnexus 1.3.10 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +22 -2
  2. package/dist/cli/ai-context.d.ts +2 -1
  3. package/dist/cli/ai-context.js +33 -6
  4. package/dist/cli/analyze.d.ts +2 -0
  5. package/dist/cli/analyze.js +20 -2
  6. package/dist/cli/index.js +2 -0
  7. package/dist/cli/setup.js +17 -19
  8. package/dist/cli/skill-gen.d.ts +26 -0
  9. package/dist/cli/skill-gen.js +549 -0
  10. package/dist/core/graph/types.d.ts +5 -2
  11. package/dist/core/ingestion/call-processor.d.ts +5 -5
  12. package/dist/core/ingestion/call-processor.js +173 -260
  13. package/dist/core/ingestion/constants.d.ts +16 -0
  14. package/dist/core/ingestion/constants.js +16 -0
  15. package/dist/core/ingestion/entry-point-scoring.d.ts +2 -1
  16. package/dist/core/ingestion/entry-point-scoring.js +81 -22
  17. package/dist/core/ingestion/export-detection.d.ts +18 -0
  18. package/dist/core/ingestion/export-detection.js +230 -0
  19. package/dist/core/ingestion/framework-detection.d.ts +5 -1
  20. package/dist/core/ingestion/framework-detection.js +39 -8
  21. package/dist/core/ingestion/heritage-processor.d.ts +13 -4
  22. package/dist/core/ingestion/heritage-processor.js +92 -28
  23. package/dist/core/ingestion/import-processor.d.ts +17 -19
  24. package/dist/core/ingestion/import-processor.js +170 -695
  25. package/dist/core/ingestion/language-config.d.ts +46 -0
  26. package/dist/core/ingestion/language-config.js +167 -0
  27. package/dist/core/ingestion/mro-processor.d.ts +45 -0
  28. package/dist/core/ingestion/mro-processor.js +369 -0
  29. package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
  30. package/dist/core/ingestion/named-binding-extraction.js +363 -0
  31. package/dist/core/ingestion/parsing-processor.d.ts +1 -10
  32. package/dist/core/ingestion/parsing-processor.js +41 -177
  33. package/dist/core/ingestion/pipeline.js +41 -26
  34. package/dist/core/ingestion/process-processor.js +2 -1
  35. package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
  36. package/dist/core/ingestion/resolvers/csharp.js +109 -0
  37. package/dist/core/ingestion/resolvers/go.d.ts +19 -0
  38. package/dist/core/ingestion/resolvers/go.js +42 -0
  39. package/dist/core/ingestion/resolvers/index.d.ts +16 -0
  40. package/dist/core/ingestion/resolvers/index.js +11 -0
  41. package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
  42. package/dist/core/ingestion/resolvers/jvm.js +87 -0
  43. package/dist/core/ingestion/resolvers/php.d.ts +15 -0
  44. package/dist/core/ingestion/resolvers/php.js +35 -0
  45. package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
  46. package/dist/core/ingestion/resolvers/rust.js +73 -0
  47. package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
  48. package/dist/core/ingestion/resolvers/standard.js +145 -0
  49. package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
  50. package/dist/core/ingestion/resolvers/utils.js +120 -0
  51. package/dist/core/ingestion/symbol-resolver.d.ts +32 -0
  52. package/dist/core/ingestion/symbol-resolver.js +83 -0
  53. package/dist/core/ingestion/symbol-table.d.ts +12 -1
  54. package/dist/core/ingestion/symbol-table.js +19 -12
  55. package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
  56. package/dist/core/ingestion/tree-sitter-queries.js +114 -9
  57. package/dist/core/ingestion/type-env.d.ts +27 -0
  58. package/dist/core/ingestion/type-env.js +86 -0
  59. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
  60. package/dist/core/ingestion/type-extractors/c-cpp.js +60 -0
  61. package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
  62. package/dist/core/ingestion/type-extractors/csharp.js +89 -0
  63. package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
  64. package/dist/core/ingestion/type-extractors/go.js +105 -0
  65. package/dist/core/ingestion/type-extractors/index.d.ts +21 -0
  66. package/dist/core/ingestion/type-extractors/index.js +29 -0
  67. package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
  68. package/dist/core/ingestion/type-extractors/jvm.js +121 -0
  69. package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
  70. package/dist/core/ingestion/type-extractors/php.js +31 -0
  71. package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
  72. package/dist/core/ingestion/type-extractors/python.js +41 -0
  73. package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
  74. package/dist/core/ingestion/type-extractors/rust.js +39 -0
  75. package/dist/core/ingestion/type-extractors/shared.d.ts +17 -0
  76. package/dist/core/ingestion/type-extractors/shared.js +97 -0
  77. package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
  78. package/dist/core/ingestion/type-extractors/swift.js +43 -0
  79. package/dist/core/ingestion/type-extractors/types.d.ts +14 -0
  80. package/dist/core/ingestion/type-extractors/types.js +1 -0
  81. package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
  82. package/dist/core/ingestion/type-extractors/typescript.js +46 -0
  83. package/dist/core/ingestion/utils.d.ts +67 -0
  84. package/dist/core/ingestion/utils.js +691 -4
  85. package/dist/core/ingestion/workers/parse-worker.d.ts +20 -3
  86. package/dist/core/ingestion/workers/parse-worker.js +84 -345
  87. package/dist/core/ingestion/workers/worker-pool.js +8 -0
  88. package/dist/core/kuzu/csv-generator.js +19 -3
  89. package/dist/core/kuzu/kuzu-adapter.js +5 -2
  90. package/dist/core/kuzu/schema.d.ts +3 -3
  91. package/dist/core/kuzu/schema.js +16 -1
  92. package/dist/core/search/bm25-index.js +2 -1
  93. package/dist/mcp/core/kuzu-adapter.js +6 -18
  94. package/dist/mcp/tools.js +12 -3
  95. package/hooks/claude/gitnexus-hook.cjs +149 -66
  96. package/package.json +1 -1
  97. package/skills/gitnexus-cli.md +1 -1
package/README.md CHANGED
@@ -139,7 +139,8 @@ Your AI agent gets these tools automatically:
139
139
  gitnexus setup # Configure MCP for your editors (one-time)
140
140
  gitnexus analyze [path] # Index a repository (or update stale index)
141
141
  gitnexus analyze --force # Force full re-index
142
- gitnexus analyze --skip-embeddings # Skip embedding generation (faster)
142
+ gitnexus analyze --embeddings # Enable embedding generation (slower, better search)
143
+ gitnexus analyze --verbose # Log skipped files when parsers are unavailable
143
144
  gitnexus mcp # Start MCP server (stdio) — serves all indexed repos
144
145
  gitnexus serve # Start local HTTP server (multi-repo) for web UI
145
146
  gitnexus list # List all indexed repositories
@@ -156,7 +157,26 @@ GitNexus supports indexing multiple repositories. Each `gitnexus analyze` regist
156
157
 
157
158
  ## Supported Languages
158
159
 
159
- TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust, PHP, Swift
160
+ TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust, PHP, Kotlin, Swift
161
+
162
+ ### Language Feature Matrix
163
+
164
+ | Language | Imports | Types | Exports | Named Bindings | Config | Frameworks | Entry Points | Heritage |
165
+ |----------|---------|-------|---------|----------------|--------|------------|-------------|----------|
166
+ | TypeScript | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
167
+ | JavaScript | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
168
+ | Python | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
169
+ | C# | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
170
+ | Java | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ |
171
+ | Kotlin | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ |
172
+ | Go | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | ✓ |
173
+ | Rust | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ |
174
+ | PHP | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — |
175
+ | Swift | — | ✓ | ✓ | — | ✓ | ✓ | ✓ | ✓ |
176
+ | C | — | ✓ | ✓ | — | — | ✓ | ✓ | ✓ |
177
+ | C++ | — | ✓ | ✓ | — | — | ✓ | ✓ | ✓ |
178
+
179
+ **Imports** — cross-file import resolution · **Types** — type annotation extraction · **Exports** — public/exported symbol detection · **Named Bindings** — `import { X }` tracking · **Config** — language toolchain config parsing (tsconfig, go.mod, etc.) · **Frameworks** — AST-based framework pattern detection · **Entry Points** — entry point scoring heuristics · **Heritage** — class inheritance / interface implementation
160
180
 
161
181
  ## Agent Skills
162
182
 
@@ -5,6 +5,7 @@
5
5
  * AGENTS.md is the standard read by Cursor, Windsurf, OpenCode, Cline, etc.
6
6
  * CLAUDE.md is for Claude Code which only reads that file.
7
7
  */
8
+ import { type GeneratedSkillInfo } from './skill-gen.js';
8
9
  interface RepoStats {
9
10
  files?: number;
10
11
  nodes?: number;
@@ -16,7 +17,7 @@ interface RepoStats {
16
17
  /**
17
18
  * Generate AI context files after indexing
18
19
  */
19
- export declare function generateAIContextFiles(repoPath: string, _storagePath: string, projectName: string, stats: RepoStats): Promise<{
20
+ export declare function generateAIContextFiles(repoPath: string, _storagePath: string, projectName: string, stats: RepoStats, generatedSkills?: GeneratedSkillInfo[]): Promise<{
20
21
  files: string[];
21
22
  }>;
22
23
  export {};
@@ -24,7 +24,18 @@ const GITNEXUS_END_MARKER = '<!-- gitnexus:end -->';
24
24
  * - Exact tool commands with parameters — vague directives get ignored
25
25
  * - Self-review checklist — forces model to verify its own work
26
26
  */
27
- function generateGitNexusContent(projectName, stats) {
27
+ function generateGitNexusContent(projectName, stats, generatedSkills) {
28
+ const generatedRows = (generatedSkills && generatedSkills.length > 0)
29
+ ? generatedSkills.map(s => `| Work in the ${s.label} area (${s.symbolCount} symbols) | \`.claude/skills/generated/${s.name}/SKILL.md\` |`).join('\n')
30
+ : '';
31
+ const skillsTable = `| Task | Read this skill file |
32
+ |------|---------------------|
33
+ | Understand architecture / "How does X work?" | \`.claude/skills/gitnexus/gitnexus-exploring/SKILL.md\` |
34
+ | Blast radius / "What breaks if I change X?" | \`.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md\` |
35
+ | Trace bugs / "Why is X failing?" | \`.claude/skills/gitnexus/gitnexus-debugging/SKILL.md\` |
36
+ | Rename / extract / split / refactor | \`.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md\` |
37
+ | Tools, resources, schema reference | \`.claude/skills/gitnexus/gitnexus-guide/SKILL.md\` |
38
+ | Index, status, clean, wiki CLI commands | \`.claude/skills/gitnexus/gitnexus-cli/SKILL.md\` |${generatedRows ? '\n' + generatedRows : ''}`;
28
39
  return `${GITNEXUS_START_MARKER}
29
40
  # GitNexus — Code Intelligence
30
41
 
@@ -96,11 +107,27 @@ Before completing any code modification task, verify:
96
107
  3. \`gitnexus_detect_changes()\` confirms changes match expected scope
97
108
  4. All d=1 (WILL BREAK) dependents were updated
98
109
 
110
+ ## Keeping the Index Fresh
111
+
112
+ After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it:
113
+
114
+ \`\`\`bash
115
+ npx gitnexus analyze
116
+ \`\`\`
117
+
118
+ If the index previously included embeddings, preserve them by adding \`--embeddings\`:
119
+
120
+ \`\`\`bash
121
+ npx gitnexus analyze --embeddings
122
+ \`\`\`
123
+
124
+ To check whether embeddings exist, inspect \`.gitnexus/meta.json\` — the \`stats.embeddings\` field shows the count (0 means no embeddings). **Running analyze without \`--embeddings\` will delete any previously generated embeddings.**
125
+
126
+ > Claude Code users: A PostToolUse hook handles this automatically after \`git commit\` and \`git merge\`.
127
+
99
128
  ## CLI
100
129
 
101
- - Re-index: \`npx gitnexus analyze\`
102
- - Check freshness: \`npx gitnexus status\`
103
- - Generate docs: \`npx gitnexus wiki\`
130
+ ${skillsTable}
104
131
 
105
132
  ${GITNEXUS_END_MARKER}`;
106
133
  }
@@ -218,8 +245,8 @@ Use GitNexus tools to accomplish this task.
218
245
  /**
219
246
  * Generate AI context files after indexing
220
247
  */
221
- export async function generateAIContextFiles(repoPath, _storagePath, projectName, stats) {
222
- const content = generateGitNexusContent(projectName, stats);
248
+ export async function generateAIContextFiles(repoPath, _storagePath, projectName, stats, generatedSkills) {
249
+ const content = generateGitNexusContent(projectName, stats, generatedSkills);
223
250
  const createdFiles = [];
224
251
  // Create AGENTS.md (standard for Cursor, Windsurf, OpenCode, Cline, etc.)
225
252
  const agentsPath = path.join(repoPath, 'AGENTS.md');
@@ -6,5 +6,7 @@
6
6
  export interface AnalyzeOptions {
7
7
  force?: boolean;
8
8
  embeddings?: boolean;
9
+ skills?: boolean;
10
+ verbose?: boolean;
9
11
  }
10
12
  export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptions) => Promise<void>;
@@ -16,6 +16,7 @@ import { initKuzu, loadGraphToKuzu, getKuzuStats, executeQuery, executeWithReuse
16
16
  import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath } from '../storage/repo-manager.js';
17
17
  import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js';
18
18
  import { generateAIContextFiles } from './ai-context.js';
19
+ import { generateSkillFiles } from './skill-gen.js';
19
20
  import fs from 'fs/promises';
20
21
  const HEAP_MB = 8192;
21
22
  const HEAP_FLAG = `--max-old-space-size=${HEAP_MB}`;
@@ -58,6 +59,9 @@ const PHASE_LABELS = {
58
59
  export const analyzeCommand = async (inputPath, options) => {
59
60
  if (ensureHeap())
60
61
  return;
62
+ if (options?.verbose) {
63
+ process.env.GITNEXUS_VERBOSE = '1';
64
+ }
61
65
  console.log('\n GitNexus Analyzer\n');
62
66
  let repoPath;
63
67
  if (inputPath) {
@@ -80,7 +84,7 @@ export const analyzeCommand = async (inputPath, options) => {
80
84
  const { storagePath, kuzuPath } = getStoragePaths(repoPath);
81
85
  const currentCommit = getCurrentCommit(repoPath);
82
86
  const existingMeta = await loadMeta(storagePath);
83
- if (existingMeta && !options?.force && existingMeta.lastCommit === currentCommit) {
87
+ if (existingMeta && !options?.force && !options?.skills && existingMeta.lastCommit === currentCommit) {
84
88
  console.log(' Already up to date\n');
85
89
  return;
86
90
  }
@@ -242,6 +246,13 @@ export const analyzeCommand = async (inputPath, options) => {
242
246
  }
243
247
  // ── Phase 5: Finalize (98–100%) ───────────────────────────────────
244
248
  updateBar(98, 'Saving metadata...');
249
+ // Count embeddings in the index (cached + newly generated)
250
+ let embeddingCount = 0;
251
+ try {
252
+ const embResult = await executeQuery(`MATCH (e:CodeEmbedding) RETURN count(e) AS cnt`);
253
+ embeddingCount = embResult?.[0]?.cnt ?? 0;
254
+ }
255
+ catch { /* table may not exist if embeddings never ran */ }
245
256
  const meta = {
246
257
  repoPath,
247
258
  lastCommit: currentCommit,
@@ -252,6 +263,7 @@ export const analyzeCommand = async (inputPath, options) => {
252
263
  edges: stats.edges,
253
264
  communities: pipelineResult.communityResult?.stats.totalCommunities,
254
265
  processes: pipelineResult.processResult?.stats.totalProcesses,
266
+ embeddings: embeddingCount,
255
267
  },
256
268
  };
257
269
  await saveMeta(storagePath, meta);
@@ -267,6 +279,12 @@ export const analyzeCommand = async (inputPath, options) => {
267
279
  }
268
280
  aggregatedClusterCount = Array.from(groups.values()).filter(count => count >= 5).length;
269
281
  }
282
+ let generatedSkills = [];
283
+ if (options?.skills && pipelineResult.communityResult) {
284
+ updateBar(99, 'Generating skill files...');
285
+ const skillResult = await generateSkillFiles(repoPath, projectName, pipelineResult);
286
+ generatedSkills = skillResult.skills;
287
+ }
270
288
  const aiContext = await generateAIContextFiles(repoPath, storagePath, projectName, {
271
289
  files: pipelineResult.totalFileCount,
272
290
  nodes: stats.nodes,
@@ -274,7 +292,7 @@ export const analyzeCommand = async (inputPath, options) => {
274
292
  communities: pipelineResult.communityResult?.stats.totalCommunities,
275
293
  clusters: aggregatedClusterCount,
276
294
  processes: pipelineResult.processResult?.stats.totalProcesses,
277
- });
295
+ }, generatedSkills);
278
296
  await closeKuzu();
279
297
  // Note: we intentionally do NOT call disposeEmbedder() here.
280
298
  // ONNX Runtime's native cleanup segfaults on macOS and some Linux configs.
package/dist/cli/index.js CHANGED
@@ -20,6 +20,8 @@ program
20
20
  .description('Index a repository (full analysis)')
21
21
  .option('-f, --force', 'Force full re-index even if up to date')
22
22
  .option('--embeddings', 'Enable embedding generation for semantic search (off by default)')
23
+ .option('--skills', 'Generate repo-specific skill files from detected communities')
24
+ .option('-v, --verbose', 'Enable verbose ingestion warnings (default: false)')
23
25
  .action(createLazyAction(() => import('./analyze.js'), 'analyzeCommand'));
24
26
  program
25
27
  .command('serve')
package/dist/cli/setup.js CHANGED
@@ -147,36 +147,34 @@ async function installClaudeCodeHooks(result) {
147
147
  // even when it's no longer inside the npm package tree
148
148
  const resolvedCli = path.join(__dirname, '..', 'cli', 'index.js');
149
149
  const normalizedCli = path.resolve(resolvedCli).replace(/\\/g, '/');
150
- content = content.replace("let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');", `let cliPath = '${normalizedCli}';`);
150
+ const jsonCli = JSON.stringify(normalizedCli);
151
+ content = content.replace("let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');", `let cliPath = ${jsonCli};`);
151
152
  await fs.writeFile(dest, content, 'utf-8');
152
153
  }
153
154
  catch {
154
155
  // Script not found in source — skip
155
156
  }
156
- const hookCmd = `node "${path.join(destHooksDir, 'gitnexus-hook.cjs').replace(/\\/g, '/')}"`;
157
+ const hookPath = path.join(destHooksDir, 'gitnexus-hook.cjs').replace(/\\/g, '/');
158
+ const hookCmd = `node "${hookPath.replace(/"/g, '\\"')}"`;
157
159
  // Merge hook config into ~/.claude/settings.json
158
160
  const existing = await readJsonFile(settingsPath) || {};
159
161
  if (!existing.hooks)
160
162
  existing.hooks = {};
161
- // NOTE: SessionStart hooks are broken on Windows (Claude Code bug #23576).
162
- // Session context is delivered via CLAUDE.md / skills instead.
163
- // Add PreToolUse hook if not already present
164
- if (!existing.hooks.PreToolUse)
165
- existing.hooks.PreToolUse = [];
166
- const hasPreToolHook = existing.hooks.PreToolUse.some((h) => h.hooks?.some((hh) => hh.command?.includes('gitnexus')));
167
- if (!hasPreToolHook) {
168
- existing.hooks.PreToolUse.push({
169
- matcher: 'Grep|Glob|Bash',
170
- hooks: [{
171
- type: 'command',
172
- command: hookCmd,
173
- timeout: 8000,
174
- statusMessage: 'Enriching with GitNexus graph context...',
175
- }],
176
- });
163
+ function ensureHookEntry(eventName, matcher, timeout, statusMessage) {
164
+ if (!existing.hooks[eventName])
165
+ existing.hooks[eventName] = [];
166
+ const hasHook = existing.hooks[eventName].some((h) => h.hooks?.some(hh => hh.command?.includes('gitnexus-hook')));
167
+ if (!hasHook) {
168
+ existing.hooks[eventName].push({
169
+ matcher,
170
+ hooks: [{ type: 'command', command: hookCmd, timeout, statusMessage }],
171
+ });
172
+ }
177
173
  }
174
+ ensureHookEntry('PreToolUse', 'Grep|Glob|Bash', 10, 'Enriching with GitNexus graph context...');
175
+ ensureHookEntry('PostToolUse', 'Bash', 10, 'Checking GitNexus index freshness...');
178
176
  await writeJsonFile(settingsPath, existing);
179
- result.configured.push('Claude Code hooks (PreToolUse)');
177
+ result.configured.push('Claude Code hooks (PreToolUse, PostToolUse)');
180
178
  }
181
179
  catch (err) {
182
180
  result.errors.push(`Claude Code hooks: ${err.message}`);
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Skill File Generator
3
+ *
4
+ * Generates repo-specific SKILL.md files from detected Leiden communities.
5
+ * Each significant community becomes a skill that describes a functional area
6
+ * of the codebase, including key files, entry points, execution flows, and
7
+ * cross-community connections.
8
+ */
9
+ import { PipelineResult } from '../types/pipeline.js';
10
+ export interface GeneratedSkillInfo {
11
+ name: string;
12
+ label: string;
13
+ symbolCount: number;
14
+ fileCount: number;
15
+ }
16
+ /**
17
+ * @brief Generate repo-specific skill files from detected communities
18
+ * @param {string} repoPath - Absolute path to the repository root
19
+ * @param {string} projectName - Human-readable project name
20
+ * @param {PipelineResult} pipelineResult - In-memory pipeline data with communities, processes, graph
21
+ * @returns {Promise<{ skills: GeneratedSkillInfo[], outputPath: string }>} Generated skill metadata
22
+ */
23
+ export declare const generateSkillFiles: (repoPath: string, projectName: string, pipelineResult: PipelineResult) => Promise<{
24
+ skills: GeneratedSkillInfo[];
25
+ outputPath: string;
26
+ }>;