* feat(storage): add configurable index storage and content retention tiers
Rebase #3060 onto current origin/main. Keep GITNEXUS_STORAGE_PATH,
GITNEXUS_STORAGE_ROOT, and GITNEXUS_CONTENT_RETENTION, and fold in
main's FTS skip, embed-session, and help-text updates.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3060)
Keep legacy registry rows on the local storage fallback, resolve
symlinks before the destructive-path guard, and align hook lookup
with CLI branch slugs, branch-slot metadata, and longest-path match.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3060)
Only list swept upload directories after a successful removal so
callers cannot treat a permission or transient rm failure as gone.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3060)
Document that getStoragePath may consult registered storage while
this module still does not mutate the global registry.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(storage): close review findings for external indexes and retention
Re-inspect ownership under the analyze lock, fail-closed when the
registry file is missing, and keep skip-git hook discovery plus
retention fields on HTTP/MCP list surfaces. /api/file stays 410
unless contentRetention is full.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(autofix): apply prettier + eslint fixes via /autofix command
* Address PR review feedback (#3060)
Treat lock-only index dirs as empty, honor HTTP --force storage policy, and prefer registered plus branch-aware slots in hooks and augment.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3060)
Keep hook fallbacks inside the current worktree, compare foreign-local slots canonically, and make storage fixtures survive ownership validation.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix macOS hook test expecting realpath'd registry paths.
resolveHookRepo returns the written registry path, not a filesystem realpath, so the assertion must match that.
* Address gitnexus-check warnings on hook install docs and slot tests.
The Cursor troubleshooting list omitted registry-query.cjs, and the writable-slot test only checked that isDirectory exists instead of that the path is a directory.
* Align the HTTP catalog source-scan with skippable resolveRepo validation.
resolveRepo lists fresh repos with validate: options.validateStorage !== false so DELETE can skip prune; the test still required a literal validate: true.
* Harden storage path sinks so CodeQL path-injection and ReDoS alerts clear.
Contain every filesystem probe inside the resolved storage slot with the inline path.relative idiom, reject filesystem-root slots, and trim slot basenames in linear time.
* Settle bridge stamps before writing so CI size/mtime matches stay stable.
LadybugDB can still flush into bridge.lbug after close+rename; persist whole-millisecond mtimes and wait for consecutive stats to agree so a freshly written pair matches.
* Type the settled bridge stat as fs.Stats so tsc does not see bigint.
Awaited<ReturnType<typeof fsp.stat>> collapsed the bigint overload and broke prepare/typecheck on CI.
* Keep the bridge mtime stamp exact so same-size swaps still fail the pair check.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Wrap the bridge stamp predicate so prettier --check stays green.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Require a quiet interval before stamping a settled bridge file.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Reuse shared storage and settle helpers instead of local copies.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: preserve customized SKILL.md files during analyze
Skip overwriting standard GitNexus skill files when they already differ from the bundled template, and keep divergent nested leftovers instead of deleting them.
Fixes#3080
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: note --skip-agents-md does not skip skills
Point operators at --skip-skills so the AGENTS/CLAUDE skip flag is not mistaken for a full agent-file freeze.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(review): treat unreadable SKILL.md as an error, not missing
Only ENOENT counts as absent so EACCES cannot fall through to recursive rm. Strengthen the skipSkills nested leftover test with a bundle-identical file that install would otherwise delete.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): prettier-format ai-context unit tests
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: clarify --skip-agents-md vs community --skills
Co-authored-by: Cursor <cursoragent@cursor.com>
* test: pin GITNEXUS_LANG=en for skip-agents-md help assertions
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: preserve customized skills in analyze summaries and setup
Report preserved vs written skills, keep leftover nested directories that
contain extra files, and skip setup overwrite of divergent SKILL.md.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3124)
Preserve operator-edited files inside directory skills during setup, and
restore GITNEXUS_TEST_SKILLS_ROOT after the Antigravity skill tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3124)
Copy directory-skill companions even when SKILL.md is customized, while
still skipping files whose bytes already differ from the bundle.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Address review finding (LOW — docs/help staleness): the --skip-skills help
text and README omitted that skills also mirror to .agents/skills/ when
.agents/ exists.
- index.ts + i18n (en/zh): --skip-skills now reads "directly under
.claude/skills/ and .agents/skills/".
- skip-git-cli.test.ts: assert the help text covers .agents/skills/.
Co-Authored-By: Claude <noreply@anthropic.com>
The skip-git ignore test asserted the error payload while relying on
exit 0; since the output() guard an error payload also exits 1, so the
test now captures the payload from the exec failure and pins both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(cli): add --skip-skills and --index-only flags to analyze command
The `installSkills()` call in `generateAIContextFiles()` runs
unconditionally, injecting 6 skill files into `.claude/skills/gitnexus/`
even when `--skip-agents-md` is passed. This is problematic for bulk
indexing operations on read-only mirrors or third-party repos.
Add two new flags:
- `--skip-skills`: suppress standard GitNexus skill file injection
- `--index-only`: pure index mode that suppresses all file injection
(AGENTS.md, CLAUDE.md, and skills), writing only to `.gitnexus/`
This gives users three levels of control:
- `--skip-agents-md` — suppress only root context files
- `--skip-skills` — suppress only skill injection
- `--index-only` — suppress everything (pure indexing)
Discovery context: while bulk-indexing 176 repos with
`--skip-agents-md`, all 144 indexed repos were contaminated with
`.claude/skills/gitnexus/` files requiring manual cleanup.
* fix(cli): address PR #742 review — gate community skills, drop dangling refs, add tests
Bot review (#742) flagged three issues with the original commit:
1. `--index-only --skills` still wrote community-derived skill files
to `.claude/skills/generated/`. The `--skills` branch in analyze.ts
was not gated by `skipAll`, so the "skip all file injection" contract
was violated. Gate `generateSkillFiles()` with `!skipAll` so
`--index-only` truly wins over `--skills`.
2. `--skip-skills` without `--skip-agents-md` produced AGENTS.md /
CLAUDE.md that still referenced `.claude/skills/gitnexus/*/SKILL.md`
files that were never installed — every agent load incurred 6
failed reads. Pass `skipSkills` through to `generateGitNexusContent()`
and omit the standard-skill rows (and the entire `## CLI` heading
when the table is empty). Community skills, when present via
`--skills`, are unaffected.
3. No filesystem tests for `skipSkills` / `indexOnly`. Add three
regression guards to `test/unit/ai-context.test.ts`:
- `.claude/skills/gitnexus/` is NOT created when skipSkills=true
- Nothing is written when both skipAgentsMd and skipSkills are true
(the resolved-flag state from --index-only)
- AGENTS.md/CLAUDE.md routing table omits standard skill references
when skipSkills=true, but preserves the load-bearing imperative
sections (Always Do / Never Do / Resources)
* test(cli): PR 1485 review follow-ups (help text, gate test, --skip-skills docs)
- Assert --skip-skills and --index-only in analyze --help (skip-git-cli.test.ts).
- Export shouldGenerateCommunitySkillFiles; unit-test index-only+skills gate.
- Clarify --skip-skills does not suppress --skills community files; --index-only for full skip.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(cli): warn when --index-only silently overrides --skills
Address review findings on PR 1485 follow-ups:
- analyze.ts emits a one-line note when both --index-only and --skills
are set, so users see why a pipeline re-index ran with no skill files
written.
- index.ts --skills help text now flags the --index-only override.
- shouldGenerateCommunitySkillFiles JSDoc documents the dual role of
the gate (community skills + AGENTS.md/CLAUDE.md re-generation).
- skip-git-cli.test.ts pins the override-warning surface end-to-end.
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(cli): keep GitNexus ignores inside .gitnexus
Avoid mutating analyzed repositories' root .gitignore while keeping generated GitNexus state untracked via .gitnexus/.gitignore.
Made-with: Cursor
* fix(cli): also use git info exclude for GitNexus storage
When an analyzed repo has a real .git directory, add .gitnexus/ to .git/info/exclude so local Git metadata ignores generated storage without touching root .gitignore.
Made-with: Cursor
* fix(cli): keep skip-git subdir indexes ignored
Ensure full analyze always writes the internal GitNexus ignore file so parent Git repositories stay clean for --skip-git subdirectory indexes.
Made-with: Cursor
Addresses all review items from @magyargergo and Copilot:
1. **Rename --no-git to --skip-git**: Commander.js treats --no-X flags
as negation of --X (stores as options.git = false, not options.noGit).
--skip-git maps correctly to options.skipGit.
2. **Fix false " Already up to date\ on non-git folders**: When
currentCommit is empty string, skip the cache check — we cannot
detect changes without git, so always rebuild.
3. **Replace isGitRepo() with hasGitDir()**: Use filesystem check
(statSync on .git) instead of shelling out to git CLI. Consistent,
faster, and works when git is not installed.
4. **Fix misleading warning**: Message now only fires when .git
directory is actually absent (not when git CLI fails).
5. **Add CLI integration tests**: Verify Commander maps --skip-git
correctly and that non-git folders are rejected without the flag.