Skip to content

feat: centralize skill definitions with build-time sync#214

Closed
L1nusB wants to merge 2 commits into
abhigyanpatwari:mainfrom
L1nusB:sync_skills_across_integration
Closed

feat: centralize skill definitions with build-time sync#214
L1nusB wants to merge 2 commits into
abhigyanpatwari:mainfrom
L1nusB:sync_skills_across_integration

Conversation

@L1nusB

@L1nusB L1nusB commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

Problem

GitNexus agent skills exist as 26 files across 4 locations in the monorepo (gitnexus/skills/, .claude/skills/gitnexus/, gitnexus-claude-plugin/skills/, gitnexus-cursor-integration/skills/). There is no mechanism to keep them in sync, and drift has already occurred:

  • Cursor integration had independently rewritten frontmatter descriptions for all 5 of its skills
  • .claude/gitnexus-cli was missing a content sentence about PostToolUse hooks present in the source
  • .claude copies had prettified table formatting diverging from the source
  • gitnexus-pr-review was missing from the runtime installer's SKILL_NAMES array in setup.ts, meaning it was never installed to user machines
  • Cursor integration was missing 2 of 7 skills (gitnexus-cli, gitnexus-guide) with no documented rationale

Solution

Single source of truth

gitnexus/skills/*.md is the canonical location. All other copies are derived.

Build-time sync script

gitnexus/scripts/sync-skills.ts reads canonical skills and generates derived SKILL.md files into each integration directory:

gitnexus/skills/*.md ← single source of truth ├─→ .claude/skills/gitnexus/*/SKILL.md
 ├─→ gitnexus-claude-plugin/skills/*/SKILL.md
 └─→ gitnexus-cursor-integration/skills/*/SKILL.md

Per-target manifests

Each target directory has a skills.manifest.json declaring which skills to include, making subsets explicit and reviewable.

CI enforcement

A skill-sync-check job in .github/workflows/ci.yml runs the sync script then git diff --exit-code — PRs with stale derived copies fail CI.

Content transformations

  • YAML frontmatter is stripped from derived copies (consumed only by setup.ts at runtime)
  • An <!-- AUTO-GENERATED ... DO NOT EDIT --> header is prepended
  • Trailing whitespace is normalized
  • Companion files (mcp.json) are never touched

Additional fixes

  • SKILL_NAMES in setup.ts updated to include all 7 skills (was missing gitnexus-pr-review)
  • Cursor integration now includes all 7 skills (was missing gitnexus-cli and gitnexus-guide)
  • All drifted content reconciled to canonical source

Test coverage

35 tests across 10 groups (T1–T10) in gitnexus/test/unit/sync-skills.test.ts:

Group Coverage
T1 — Source Discovery Discovers all skills, ignores non-skill files, handles empty/missing dirs
T2 — Allowlist Filtering Full/subset/empty allowlists, missing skill validation, deduplication
T3 — Content Transformation Frontmatter stripping, header generation, trailing whitespace normalization
T4 — Path Generation Flat .md{name}/SKILL.md, multi-target independence
T5 — Idempotency Skip detection, write detection, double-run produces zero writes
T6 — Companion Preservation mcp.json and other files are never in operation list
T7 — Error Handling Unreadable files, malformed frontmatter
T8 — Integration Validates against real gitnexus/skills/ directory
T9 — SKILL_NAMES Parity Runtime installer matches canonical source
T10 — Manifest Validation Valid/invalid/malformed manifests

How to use

# Sync all derived skill files from canonical source
npm run sync:skills

# Dry-run to see what would change
npm run sync:skills -- --dry-run

# CI check (sync + git diff --exit-code)
npm run sync:skills:check

Checklist

  • All 874 unit tests pass
  • npm run sync:skills is idempotent (0 writes on second run)
  • CI drift check integrated (skill-sync-check job)
  • SKILL_NAMES updated to include all 7 skills
  • Each target has a skills.manifest.json with explicit allowlist
  • mcp.json companion files untouched
  • Cursor integration now includes all 7 skills
  • No derived SKILL.md differs from what the sync script generates

@abhigyanpatwari

Copy link
Copy Markdown
Owner

🟢 GitNexus Blast Radius: LOW

Metric Count
Changed symbols 0
Direct dependents (d=1) 0
Indirect (d=2) 0
Transitive (d=3) 0
Flows impacted 0
Total affected 0

Changed: None detected
Flows hit: None

View full blast radius graph →


Generated by GitNexus — code intelligence powered by knowledge graphs

@vercel

vercel Bot commented Mar 7, 2026

Copy link
Copy Markdown

@L1nusB is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

@xkonjin xkonjin left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick review pass:

  • Main risk area here is input validation, path handling, and malformed payload behavior.
  • Good to see test coverage move with the code; I’d still make sure it exercises the unhappy path around input validation, path handling, and malformed payload behavior rather than only the happy path.
  • Before merge, I’d smoke-test the behavior touched by SKILL.md, SKILL.md, SKILL.md (+33 more) with malformed input / retry / rollback cases, since that’s where this class of change usually breaks.

@reversTeam reversTeam left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work, @L1nusB. This is a well-thought-out solution to the skill drift problem. Key observations:

Architecture: Single source of truth in gitnexus/skills/*.md with build-time sync to 3 targets is the right call. The skills.manifest.json per target makes subset decisions explicit and reviewable, and the CI enforcement via git diff --exit-code closes the loop.

Code quality:

  • planSync() as a pure function with injected readFile is very testable — good design.
  • discoverSkillNames() in setup.ts replacing the hardcoded SKILL_NAMES array fixes the gitnexus-pr-review omission and prevents future drift.
  • The lazy initialization pattern for SKILL_NAMES is clean.
  • Error handling improvements (propagating real I/O errors vs. silently swallowing ENOENT) are a nice touch.

Testing: 35 tests across 10 groups (T1-T10) is thorough. The analyze-skills-notice.test.ts tests for the migration path are a good addition — testing both the early-return and full-analyze code paths.

Migration path: The checkStaleProjectSkills() deprecation notice in analyze + cleanupProjectLocalSkills() in setup is a thoughtful migration strategy — warn first, clean up when the user explicitly runs setup.

Minor notes:

  • The 439-line docs/skill-sync.md design doc is comprehensive but quite large for a doc that ships with the repo. Consider whether this could live in a PR description or wiki instead, since most of it documents the decision-making process rather than ongoing usage.
  • The Cursor integration now gets all 7 skills (previously 5) — worth calling out in release notes.

LGTM — this eliminates a real maintenance burden.

@L1nusB L1nusB force-pushed the sync_skills_across_integration branch from d5900f9 to 5c4348e Compare March 11, 2026 09:27
@L1nusB

L1nusB commented Mar 11, 2026

Copy link
Copy Markdown
Contributor Author

Rebase cleanup and squashing into a single commit.
The CI action now also follows the same pattern as the others

@github-actions

github-actions Bot commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

CI Report

Some checks failed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
❌ Tests failure unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
4707 4659 2 46 184s

2 failed / 4659 passed

46 test(s) skipped — expand for details
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature
  • Swift constructor-inferred type resolution > detects User and Repo classes, both with save methods
  • Swift constructor-inferred type resolution > resolves user.save() to Models/User.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > resolves repo.save() to Models/Repo.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > emits exactly 2 save() CALLS edges (one per receiver type)
  • Swift self resolution > detects User and Repo classes, each with a save function
  • Swift self resolution > resolves self.save() inside User.process to User.save, not Repo.save
  • Swift parent resolution > detects BaseModel and User classes plus Serializable protocol
  • Swift parent resolution > emits EXTENDS edge: User → BaseModel
  • Swift parent resolution > emits IMPLEMENTS edge: User → Serializable (protocol conformance)
  • Swift cross-file User.init() inference > resolves user.save() via User.init(name:) inference
  • Swift cross-file User.init() inference > resolves user.greet() via User.init(name:) inference
  • Swift return type inference > detects User class and getUser function
  • Swift return type inference > detects save function on User (Swift class methods are Function nodes)
  • Swift return type inference > resolves user.save() to User#save via return type of getUser() -> User
  • Swift return-type inference via function return type > resolves user.save() to User#save via return type of getUser()
  • Swift return-type inference via function return type > user.save() does NOT resolve to Repo#save
  • Swift return-type inference via function return type > resolves repo.save() to Repo#save via return type of getRepo()
  • Swift implicit imports (cross-file visibility) > detects UserService class in Models.swift
  • Swift implicit imports (cross-file visibility) > resolves UserService() constructor call across files (no explicit import)
  • Swift implicit imports (cross-file visibility) > resolves service.fetchUser() member call across files
  • Swift implicit imports (cross-file visibility) > creates IMPORTS edges between files in the same module
  • Swift extension deduplication > detects Product class
  • Swift extension deduplication > resolves Product() constructor despite extension creating duplicate class node
  • Swift extension deduplication > resolves product.save() to Product.swift (primary definition)
  • Swift constructor call fallback (no new keyword) > resolves OCRService() as constructor call across files
  • Swift constructor call fallback (no new keyword) > resolves ocr.recognize() member call via constructor-inferred type
  • Swift export visibility (internal vs private) > resolves PublicService() constructor across files
  • Swift export visibility (internal vs private) > resolves internalHelper() across files (internal = module-scoped)
  • Swift if let / guard let binding resolution > detects User and Repo classes
  • Swift if let / guard let binding resolution > resolves user.save() inside if-let to User#save
  • Swift if let / guard let binding resolution > resolves repo.save() inside guard-let to Repo#save
  • Swift if let / guard let binding resolution > user.save() in if-let does NOT resolve to Repo#save
  • Swift await / try expression unwrapping > resolves user.save() via await fetchUser() return type
  • Swift await / try expression unwrapping > resolves repo.save() via try parseRepo() return type
  • Swift await / try expression unwrapping > detects fetchUser and parseRepo as functions
  • Swift for-in loop element type inference > detects User and Repo classes
  • Swift for-in loop element type inference > creates implicit import edges between files
  • Swift field-type resolution > detects classes and their properties
  • Swift field-type resolution > emits HAS_PROPERTY edges from class to field
  • Swift field-type resolution > resolves field-chain call user.address.save() → Address#save
  • Swift field-type resolution > emits ACCESSES edges for field reads in chains
  • Swift field-type resolution > populates field metadata (visibility, declaredType) on Property nodes
  • Swift call-result binding > resolves call-result-bound method call user.save() → User#save
  • Swift call-result binding > getUser() is present as a defined function
  • Swift call-result binding > emits processUser -> getUser CALLS edge for let-assigned free function call

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 70.92% 13285/18732 70.85% 📈 +0.1 🟢 ██████████████░░░░░░
Branches 60.13% 8773/14589 60.09% 📈 +0.0 🟢 ████████████░░░░░░░░
Functions 75.67% 1176/1554 75.59% 📈 +0.1 🟢 ███████████████░░░░░
Lines 73.12% 12069/16505 73.06% 📈 +0.1 🟢 ██████████████░░░░░░

📋 View full run · Generated by CI

@magyargergo

Copy link
Copy Markdown
Collaborator

⚠️ Upcoming Prettier formatting — rebase instructions

PR #563 adds Prettier as the code formatter for the repo. When it merges, the bulk format commit will touch ~350 files (style-only: whitespace, quotes, trailing commas). Your branch will likely conflict.

After #563 merges, rebase your branch:

git fetch origin
git checkout <your-branch>
git rebase origin/main

# Conflicts will be formatting-only — accept your version:
git checkout --theirs .
git add .
git rebase --continue

# Then re-format your branch to match the new style:
npx prettier --write .
git add -A
git commit -m "style: apply prettier formatting"
git push --force-with-lease

New setup step: Run npm install at the repo root (not just in gitnexus/) to get prettier + activate the pre-commit hook. The hook auto-formats staged files on every commit going forward.

L1nusB added 2 commits April 2, 2026 12:09
…tegration targets

Add a build-time sync system that keeps skill files in parity across
.claude/skills/gitnexus, gitnexus-claude-plugin/skills, and
gitnexus-cursor-integration/skills from a single canonical source.

- Canonical source: gitnexus/src/skills/ with per-target manifests
- sync-skills script: reads manifests, copies files, validates parity
- CI enforcement: ci-skill-sync.yml reusable workflow fails if files drift
- Wired into CI gate and PR metadata alongside quality, unit, integration
@L1nusB L1nusB force-pushed the sync_skills_across_integration branch from 5c4348e to 56b8e33 Compare April 2, 2026 10:16
@L1nusB L1nusB requested a review from xkonjin April 2, 2026 10:16
@magyargergo

Copy link
Copy Markdown
Collaborator

Please submit a new PR if this is still relevant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants