gitnexus 1.6.10-rc.22 → 1.6.10-rc.24

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.
package/README.md CHANGED
@@ -233,7 +233,7 @@ gitnexus analyze --embeddings # Enable embedding generation (slower, better s
233
233
  gitnexus embeddings install # Fetch the optional local embedding stack on demand (--cuda, --force)
234
234
  gitnexus analyze --skills # Generate repo-specific skill files from detected communities
235
235
  gitnexus analyze --skip-agents-md # Preserve custom AGENTS.md/CLAUDE.md gitnexus section edits
236
- gitnexus analyze --skip-skills # Skip installing .claude/skills/gitnexus/ skill files
236
+ gitnexus analyze --skip-skills # Skip installing standard .claude/skills/gitnexus-* skill files
237
237
  gitnexus analyze --skip-git # Index folders that are not Git repositories
238
238
  gitnexus analyze --workers <n> # Parse worker pool size (>=1; default: cores-1, capped at 16)
239
239
  gitnexus analyze --verbose # Log skipped files when parsers are unavailable
@@ -328,7 +328,7 @@ GitNexus ships with skill files that teach AI agents how to use the tools effect
328
328
  - **Guide** — GitNexus tool/resource/schema reference for the agent
329
329
  - **CLI** — Run analyze/status/clean/wiki commands on request
330
330
 
331
- Installed automatically by both `gitnexus analyze` (per-repo) and `gitnexus setup` (global). Run `gitnexus analyze --skills` to additionally generate repo-specific skills for each detected functional area under `.claude/skills/generated/`.
331
+ Installed automatically by both `gitnexus analyze` (per-repo) and `gitnexus setup` (global). Run `gitnexus analyze --skills` to additionally generate each detected functional area as a direct project skill under `.claude/skills/gitnexus-area-<name>/`.
332
332
 
333
333
  ## Requirements
334
334
 
@@ -81,22 +81,22 @@ export function generateGitNexusContent(projectName, stats, opts = {}) {
81
81
  const { generatedSkills, groupNames, noStats, skipSkills, runnerPath = '.gitnexus/run.cjs', defaultBranch = 'main', hasPdg = false, } = opts;
82
82
  const generatedRows = generatedSkills && generatedSkills.length > 0
83
83
  ? generatedSkills
84
- .map((s) => `| Work in the ${s.label} area (${s.symbolCount} symbols) | \`.claude/skills/generated/${s.name}/SKILL.md\` |`)
84
+ .map((s) => `| Work in the ${s.label} area (${s.symbolCount} symbols) | \`.claude/skills/${s.name}/SKILL.md\` |`)
85
85
  .join('\n')
86
86
  : '';
87
87
  // Standard skill rows reference files installed by installSkills(). When
88
88
  // --skip-skills suppresses that install, these rows must be omitted — else
89
89
  // AGENTS.md/CLAUDE.md would direct agents to read files that don't exist.
90
- // Community skills (generatedRows) live in .claude/skills/generated/ and
90
+ // Community skills (generatedRows) live directly under .claude/skills/ and
91
91
  // are independent of --skip-skills, so they remain when present.
92
92
  const standardSkillsRows = skipSkills
93
93
  ? ''
94
- : `| Understand architecture / "How does X work?" | \`.claude/skills/gitnexus/gitnexus-exploring/SKILL.md\` |
95
- | Blast radius / "What breaks if I change X?" | \`.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md\` |
96
- | Trace bugs / "Why is X failing?" | \`.claude/skills/gitnexus/gitnexus-debugging/SKILL.md\` |
97
- | Rename / extract / split / refactor | \`.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md\` |
98
- | Tools, resources, schema reference | \`.claude/skills/gitnexus/gitnexus-guide/SKILL.md\` |
99
- | Index, status, clean, wiki CLI commands | \`.claude/skills/gitnexus/gitnexus-cli/SKILL.md\` |`;
94
+ : `| Understand architecture / "How does X work?" | \`.claude/skills/gitnexus-exploring/SKILL.md\` |
95
+ | Blast radius / "What breaks if I change X?" | \`.claude/skills/gitnexus-impact-analysis/SKILL.md\` |
96
+ | Trace bugs / "Why is X failing?" | \`.claude/skills/gitnexus-debugging/SKILL.md\` |
97
+ | Rename / extract / split / refactor | \`.claude/skills/gitnexus-refactoring/SKILL.md\` |
98
+ | Tools, resources, schema reference | \`.claude/skills/gitnexus-guide/SKILL.md\` |
99
+ | Index, status, clean, wiki CLI commands | \`.claude/skills/gitnexus-cli/SKILL.md\` |`;
100
100
  const tableBody = [standardSkillsRows, generatedRows].filter(Boolean).join('\n');
101
101
  const skillsTable = tableBody
102
102
  ? `| Task | Read this skill file |
@@ -253,11 +253,12 @@ async function upsertGitNexusSection(filePath, content, projectName, stats, noSt
253
253
  return 'appended';
254
254
  }
255
255
  /**
256
- * Install GitNexus skills to .claude/skills/gitnexus/
256
+ * Install GitNexus skills as direct children of .claude/skills/
257
257
  * Works natively with Claude Code, Cursor, and GitHub Copilot
258
258
  */
259
259
  async function installSkills(repoPath) {
260
- const skillsDir = path.join(repoPath, '.claude', 'skills', 'gitnexus');
260
+ const skillsDir = path.join(repoPath, '.claude', 'skills');
261
+ const legacySkillsDir = path.join(skillsDir, 'gitnexus');
261
262
  const installedSkills = [];
262
263
  // Skill definitions bundled with the package
263
264
  const skills = [
@@ -314,6 +315,15 @@ Use GitNexus tools to accomplish this task.
314
315
  }
315
316
  await fs.writeFile(skillPath, skillContent, 'utf-8');
316
317
  installedSkills.push(skill.name);
318
+ // Previous releases installed these known standard skills one level too
319
+ // deep. Remove only the child owned by this installer; unknown siblings
320
+ // under the legacy grouping directory may be user-authored and survive.
321
+ try {
322
+ await fs.rm(path.join(legacySkillsDir, skill.name), { recursive: true, force: true });
323
+ }
324
+ catch (err) {
325
+ logger.warn({ err }, `Warning: Could not remove legacy skill ${skill.name}:`);
326
+ }
317
327
  }
318
328
  catch (err) {
319
329
  // Skip on error, don't fail the whole process
@@ -366,15 +376,15 @@ export async function generateAIContextFiles(repoPath, storagePath, projectName,
366
376
  createdFiles.push('AGENTS.md (skipped via --skip-agents-md)');
367
377
  createdFiles.push('CLAUDE.md (skipped via --skip-agents-md)');
368
378
  }
369
- // Install skills to .claude/skills/gitnexus/ (unless --skip-skills)
379
+ // Install standard skills directly under .claude/skills/ (unless --skip-skills)
370
380
  if (!options?.skipSkills) {
371
381
  const installedSkills = await installSkills(repoPath);
372
382
  if (installedSkills.length > 0) {
373
- createdFiles.push(`.claude/skills/gitnexus/ (${installedSkills.length} skills)`);
383
+ createdFiles.push(`.claude/skills/gitnexus-*/ (${installedSkills.length} skills)`);
374
384
  }
375
385
  }
376
386
  else {
377
- createdFiles.push('.claude/skills/gitnexus/ (skipped via --skip-skills)');
387
+ createdFiles.push('.claude/skills/gitnexus-*/ (skipped via --skip-skills)');
378
388
  }
379
389
  return { files: createdFiles };
380
390
  }
@@ -55,7 +55,7 @@ export interface AnalyzeOptions {
55
55
  * default-on case.
56
56
  */
57
57
  stats?: boolean;
58
- /** Skip installing standard GitNexus skill files to .claude/skills/gitnexus/. */
58
+ /** Skip installing standard GitNexus skill files directly under .claude/skills/. */
59
59
  skipSkills?: boolean;
60
60
  /**
61
61
  * Default branch for the generated regression-compare example (#243). From
@@ -151,7 +151,7 @@ export declare const en: {
151
151
  readonly 'help.option.analyze.skills': "Generate repo-specific skill files from detected communities (no-op when --index-only is also set).";
152
152
  readonly 'help.option.analyze.skipAgentsMd': "Skip updating the gitnexus section in AGENTS.md and CLAUDE.md";
153
153
  readonly 'help.option.analyze.noStats': "Omit volatile file/symbol counts from AGENTS.md and CLAUDE.md";
154
- readonly 'help.option.analyze.skipSkills': "Skip installing standard GitNexus skill files under .claude/skills/gitnexus/. Does not suppress community skills from --skills (those use .claude/skills/generated/). Use --index-only to skip all AI-context file injection.";
154
+ readonly 'help.option.analyze.skipSkills': "Skip installing standard GitNexus skill files directly under .claude/skills/. Does not suppress community skills from --skills (those use .claude/skills/gitnexus-area-*). Use --index-only to skip all AI-context file injection.";
155
155
  readonly 'help.option.analyze.indexOnly': "Pure index mode: skip all file injection (AGENTS.md, CLAUDE.md, skills)";
156
156
  readonly 'help.option.skipGit': "Treat the provided path/cwd as the index root and skip parent git-root discovery";
157
157
  readonly 'help.option.analyze.name': "Register this repo under a custom name in ~/.gitnexus/registry.json (disambiguates repos whose paths share a basename, e.g. two different .../app folders)";
@@ -151,7 +151,7 @@ export const en = {
151
151
  'help.option.analyze.skills': 'Generate repo-specific skill files from detected communities (no-op when --index-only is also set).',
152
152
  'help.option.analyze.skipAgentsMd': 'Skip updating the gitnexus section in AGENTS.md and CLAUDE.md',
153
153
  'help.option.analyze.noStats': 'Omit volatile file/symbol counts from AGENTS.md and CLAUDE.md',
154
- 'help.option.analyze.skipSkills': 'Skip installing standard GitNexus skill files under .claude/skills/gitnexus/. Does not suppress community skills from --skills (those use .claude/skills/generated/). Use --index-only to skip all AI-context file injection.',
154
+ 'help.option.analyze.skipSkills': 'Skip installing standard GitNexus skill files directly under .claude/skills/. Does not suppress community skills from --skills (those use .claude/skills/gitnexus-area-*). Use --index-only to skip all AI-context file injection.',
155
155
  'help.option.analyze.indexOnly': 'Pure index mode: skip all file injection (AGENTS.md, CLAUDE.md, skills)',
156
156
  'help.option.skipGit': 'Treat the provided path/cwd as the index root and skip parent git-root discovery',
157
157
  'help.option.analyze.name': 'Register this repo under a custom name in ~/.gitnexus/registry.json (disambiguates repos whose paths share a basename, e.g. two different .../app folders)',
@@ -152,7 +152,7 @@ export declare const cliResources: {
152
152
  readonly 'help.option.analyze.skills': "Generate repo-specific skill files from detected communities (no-op when --index-only is also set).";
153
153
  readonly 'help.option.analyze.skipAgentsMd': "Skip updating the gitnexus section in AGENTS.md and CLAUDE.md";
154
154
  readonly 'help.option.analyze.noStats': "Omit volatile file/symbol counts from AGENTS.md and CLAUDE.md";
155
- readonly 'help.option.analyze.skipSkills': "Skip installing standard GitNexus skill files under .claude/skills/gitnexus/. Does not suppress community skills from --skills (those use .claude/skills/generated/). Use --index-only to skip all AI-context file injection.";
155
+ readonly 'help.option.analyze.skipSkills': "Skip installing standard GitNexus skill files directly under .claude/skills/. Does not suppress community skills from --skills (those use .claude/skills/gitnexus-area-*). Use --index-only to skip all AI-context file injection.";
156
156
  readonly 'help.option.analyze.indexOnly': "Pure index mode: skip all file injection (AGENTS.md, CLAUDE.md, skills)";
157
157
  readonly 'help.option.skipGit': "Treat the provided path/cwd as the index root and skip parent git-root discovery";
158
158
  readonly 'help.option.analyze.name': "Register this repo under a custom name in ~/.gitnexus/registry.json (disambiguates repos whose paths share a basename, e.g. two different .../app folders)";
@@ -151,7 +151,7 @@ export const zhCN = {
151
151
  'help.option.analyze.skills': '根据检测到的社区生成仓库专属 skill 文件(同时设置 --index-only 时无效)。',
152
152
  'help.option.analyze.skipAgentsMd': '跳过更新 AGENTS.md 和 CLAUDE.md 中的 gitnexus 区块',
153
153
  'help.option.analyze.noStats': '从 AGENTS.md 和 CLAUDE.md 中省略易变的文件/符号计数',
154
- 'help.option.analyze.skipSkills': '跳过安装 .claude/skills/gitnexus/ 下的标准 GitNexus skill 文件。不抑制 --skills 生成的社区 skill(位于 .claude/skills/generated/)。使用 --index-only 可跳过所有 AI 上下文文件注入。',
154
+ 'help.option.analyze.skipSkills': '跳过直接安装在 .claude/skills/ 下的标准 GitNexus skill 文件。不抑制 --skills 生成的社区 skill(位于 .claude/skills/gitnexus-area-*)。使用 --index-only 可跳过所有 AI 上下文文件注入。',
155
155
  'help.option.analyze.indexOnly': '纯索引模式:跳过所有文件注入(AGENTS.md、CLAUDE.md、skills)',
156
156
  'help.option.skipGit': '将提供的路径/cwd 视为索引根目录,并跳过向上查找 git 根目录',
157
157
  'help.option.analyze.name': '在 ~/.gitnexus/registry.json 中使用自定义名称注册该仓库(用于区分路径 basename 相同的仓库,例如两个不同的 .../app 目录)',
package/dist/cli/index.js CHANGED
@@ -53,8 +53,8 @@ program
53
53
  'Without this flag, analyze always updates the workspace index, which follows the ' +
54
54
  'checked-out working tree. Distinct from --default-branch (cosmetic base_ref).')
55
55
  .option('--no-stats', 'Omit volatile file/symbol counts from AGENTS.md and CLAUDE.md')
56
- .option('--skip-skills', 'Skip installing standard GitNexus skill files under .claude/skills/gitnexus/. ' +
57
- 'Does not suppress community skills from --skills (those use .claude/skills/generated/). ' +
56
+ .option('--skip-skills', 'Skip installing standard GitNexus skill files directly under .claude/skills/. ' +
57
+ 'Does not suppress community skills from --skills (those use .claude/skills/gitnexus-area-*). ' +
58
58
  'Use --index-only to skip all AI-context file injection.')
59
59
  .option('--index-only', 'Pure index mode: skip all file injection (AGENTS.md, CLAUDE.md, skills)')
60
60
  .option('--skip-git', 'Treat the provided path/cwd as the index root and skip parent git-root discovery')
@@ -8,6 +8,9 @@
8
8
  */
9
9
  import fs from 'fs/promises';
10
10
  import path from 'path';
11
+ const GENERATED_SKILL_PREFIX = 'gitnexus-area-';
12
+ const MAX_SKILL_NAME_LENGTH = 64;
13
+ const MAX_COMMUNITY_NAME_LENGTH = MAX_SKILL_NAME_LENGTH - GENERATED_SKILL_PREFIX.length;
11
14
  // ============================================================================
12
15
  // MAIN EXPORT
13
16
  // ============================================================================
@@ -20,7 +23,27 @@ import path from 'path';
20
23
  */
21
24
  export const generateSkillFiles = async (repoPath, projectName, pipelineResult) => {
22
25
  const { communityResult, processResult, graph } = pipelineResult;
23
- const outputDir = path.join(repoPath, '.claude', 'skills', 'generated');
26
+ const outputDir = path.join(repoPath, '.claude', 'skills');
27
+ const legacyOutputDir = path.join(outputDir, 'generated');
28
+ // Community skills used to live under an undiscoverable `generated/`
29
+ // grouping directory. Clear that GitNexus-owned legacy output and
30
+ // stale direct outputs in the reserved namespace, while preserving every
31
+ // unrelated project skill under .claude/skills/.
32
+ try {
33
+ const entries = await fs.readdir(outputDir, { withFileTypes: true });
34
+ await Promise.all(entries
35
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith(GENERATED_SKILL_PREFIX))
36
+ .map((entry) => fs.rm(path.join(outputDir, entry.name), { recursive: true, force: true })));
37
+ }
38
+ catch {
39
+ /* output root may not exist yet */
40
+ }
41
+ try {
42
+ await fs.rm(legacyOutputDir, { recursive: true, force: true });
43
+ }
44
+ catch {
45
+ /* legacy output may not exist */
46
+ }
24
47
  if (!communityResult || !communityResult.memberships.length) {
25
48
  console.log('\n Skills: no communities detected, skipping skill generation');
26
49
  return { skills: [], outputPath: outputDir };
@@ -47,13 +70,8 @@ export const generateSkillFiles = async (repoPath, projectName, pipelineResult)
47
70
  // Step 3: Build lookup maps
48
71
  const membershipsByComm = buildMembershipMap(communityResult.memberships);
49
72
  const nodeIdToCommunityLabel = buildNodeCommunityLabelMap(communityResult.memberships, communities);
50
- // Step 4: Clear and recreate output directory
51
- try {
52
- await fs.rm(outputDir, { recursive: true, force: true });
53
- }
54
- catch {
55
- /* may not exist */
56
- }
73
+ // Step 4: Ensure the shared project-skill root exists. Never clear it: it
74
+ // also contains user-authored and standard GitNexus skills.
57
75
  await fs.mkdir(outputDir, { recursive: true });
58
76
  // Step 5: Generate skill files
59
77
  const skills = [];
@@ -74,14 +92,15 @@ export const generateSkillFiles = async (repoPath, projectName, pipelineResult)
74
92
  // Generate kebab name
75
93
  const kebabName = toKebabName(community.label, usedNames);
76
94
  usedNames.add(kebabName);
95
+ const skillName = `${GENERATED_SKILL_PREFIX}${kebabName}`;
77
96
  // Generate SKILL.md content
78
- const content = renderSkillMarkdown(community, projectName, members, files, entryPoints, flows, connections, kebabName);
97
+ const content = renderSkillMarkdown(community, projectName, members, files, entryPoints, flows, connections, skillName);
79
98
  // Write file
80
- const skillDir = path.join(outputDir, kebabName);
99
+ const skillDir = path.join(outputDir, skillName);
81
100
  await fs.mkdir(skillDir, { recursive: true });
82
101
  await fs.writeFile(path.join(skillDir, 'SKILL.md'), content, 'utf-8');
83
102
  const info = {
84
- name: kebabName,
103
+ name: skillName,
85
104
  label: community.label,
86
105
  symbolCount: community.symbolCount,
87
106
  fileCount: files.length,
@@ -89,7 +108,7 @@ export const generateSkillFiles = async (repoPath, projectName, pipelineResult)
89
108
  skills.push(info);
90
109
  console.log(` \u2713 ${community.label} (${community.symbolCount} symbols, ${files.length} files)`);
91
110
  }
92
- console.log(`\n ${skills.length} skills generated \u2192 .claude/skills/generated/`);
111
+ console.log(`\n ${skills.length} skills generated \u2192 .claude/skills/${GENERATED_SKILL_PREFIX}*/`);
93
112
  return { skills, outputPath: outputDir };
94
113
  };
95
114
  // ============================================================================
@@ -380,10 +399,10 @@ const gatherCrossConnections = (rawIds, ownLabel, membershipsByComm, nodeIdToCom
380
399
  * @param {MemberSymbol[]} entryPoints - Exported entry point symbols
381
400
  * @param {ProcessNode[]} flows - Execution flows touching this community
382
401
  * @param {CrossConnection[]} connections - Cross-community connections
383
- * @param {string} kebabName - Kebab-case name for the skill
402
+ * @param {string} skillName - Namespaced kebab-case name for the skill
384
403
  * @returns {string} Full SKILL.md content
385
404
  */
386
- const renderSkillMarkdown = (community, projectName, members, files, entryPoints, flows, connections, kebabName) => {
405
+ const renderSkillMarkdown = (community, projectName, members, files, entryPoints, flows, connections, skillName) => {
387
406
  const cohesionPct = Math.round(community.cohesion * 100);
388
407
  // Dominant directory: most common top-level directory
389
408
  const dominantDir = getDominantDirectory(files);
@@ -396,7 +415,7 @@ const renderSkillMarkdown = (community, projectName, members, files, entryPoints
396
415
  const lines = [];
397
416
  // Frontmatter
398
417
  lines.push('---');
399
- lines.push(`name: ${kebabName}`);
418
+ lines.push(`name: ${skillName}`);
400
419
  lines.push(`description: "Skill for the ${community.label} area of ${projectName}. ${community.symbolCount} symbols across ${files.length} files."`);
401
420
  lines.push('---');
402
421
  lines.push('');
@@ -496,20 +515,21 @@ const renderSkillMarkdown = (community, projectName, members, files, entryPoints
496
515
  * @brief Convert a community label to a kebab-case directory name
497
516
  * @param {string} label - The community label
498
517
  * @param {Set<string>} usedNames - Already-used names for collision detection
499
- * @returns {string} Unique kebab-case name capped at 50 characters
518
+ * @returns {string} Unique kebab-case name that leaves room for the GitNexus prefix
500
519
  */
501
520
  const toKebabName = (label, usedNames) => {
502
521
  let name = label
503
522
  .toLowerCase()
504
523
  .replace(/[^a-z0-9]+/g, '-')
505
524
  .replace(/^-+|-+$/g, '')
506
- .slice(0, 50);
525
+ .slice(0, MAX_COMMUNITY_NAME_LENGTH);
507
526
  if (!name)
508
527
  name = 'skill';
509
528
  let candidate = name;
510
529
  let counter = 2;
511
530
  while (usedNames.has(candidate)) {
512
- candidate = `${name}-${counter}`;
531
+ const suffix = `-${counter}`;
532
+ candidate = `${name.slice(0, MAX_COMMUNITY_NAME_LENGTH - suffix.length)}${suffix}`;
513
533
  counter++;
514
534
  }
515
535
  return candidate;
@@ -47,7 +47,7 @@ export interface AnalyzeOptions {
47
47
  skipAgentsMd?: boolean;
48
48
  /** Omit volatile symbol/relationship counts from AGENTS.md and CLAUDE.md. */
49
49
  noStats?: boolean;
50
- /** Skip installing standard GitNexus skill files to .claude/skills/gitnexus/. */
50
+ /** Skip installing standard GitNexus skill files directly under .claude/skills/. */
51
51
  skipSkills?: boolean;
52
52
  /**
53
53
  * Build the CFG/PDG substrate (#2081 M1). Forwarded to `PipelineOptions.pdg`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.10-rc.22",
3
+ "version": "1.6.10-rc.24",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",