* feat(git): add getCurrentBranch + resolveRefToCommit helpers (#2106)
* feat(storage): branch-scoped getStoragePaths + branchSlug + resolveBranchPlacement (#2106)
* feat(analyze): branch-aware indexing — per-branch slot, no overwrite (#2106)
* feat(registry): nest non-primary branches under one path entry (#2106)
* feat(mcp): optional branch scope on query tools + list_repos branches (#2106)
* feat(cli): --branch on analyze + query/context/impact/cypher/detect-changes (#2106)
* feat(cli): branch-aware list/status + per-branch staleness meta (#2106)
* fix(review): apply autofix feedback
- guard analyze against --branch != checked-out branch (prevents writing one
branch's working tree into another branch's index slot)
- fix branch-handle pool reinit thrash (track observed indexedAt by lbugPath,
since applyBranchScope returns fresh handles)
- remove dead resolveRefToCommit helper (staleness uses HEAD vs branch meta)
- RepoListing.branches -> Omit<BranchSummary,'stats'> for type cohesion
- add tests: branchSlug traversal containment, --branch mismatch reject,
callTool branch threading, legacy-entry branch routing, status detached/stale
* fix(review): address tri-review findings (#2106)
- P1 data-loss: a detached-HEAD re-analyze (CI's actions/checkout default) no
longer strips the primary's meta.branch stamp; preserve it so a later branch
analyze cannot claim & overwrite the flat/primary index. +cascade integration test
- P2: capture validateBranchName's trimmed return for --branch so a
whitespace-padded value no longer false-rejects on-branch or ghosts an index
- F1: on a lost/rebuilt registry, a branch run reconstructs the primary
top-level entry from the flat meta, not the feature branch's meta
* fix(storage): only trust a non-empty-string flatMeta.branch (#2106 R5)
* fix(analyze): warn when the default branch is not the primary index (#2106 R8)
* fix(mcp): resolve --branch <primary> on a legacy unstamped flat index (#2106 R4)
* feat(cli): gitnexus clean --branch to remove a single branch index (#2106 R7)
* fix(mcp): evict orphaned branch pools on unregister/clean (#2106 R3)
* fix(analyze): union per-branch cache keys so a branch switch keeps shards (#2106 R6)
* fix(analyze): normalize the auto-detected branch label via sanitizeDetectedBranch (#2106 R1)
* fix(cli): skip AGENTS.md base_ref refresh for a non-primary branch fast path (#2106 R2)
* fix(storage): atomic writeRegistry + re-read-before-write to narrow the registry race (#2106 R9)
* refactor(storage): extract branch primitives to branch-index.ts (#2106 R10)
* feat(mcp): paginate list_repos to avoid client token truncation (#2119)
list_repos returned every indexed repository in one unpaginated array,
which large/LLM MCP clients truncate by token limit — so agents with
hundreds of indexed repos could not enumerate them all (the data
transmits fully; the consuming client drops it).
Add bounded limit/offset pagination to the list_repos tool:
- result changes from a bare array to
{ repositories, pagination: { total, limit, offset, returned,
hasMore, nextOffset } }; default page 50, max 200 (shared constants)
- reject malformed limit/offset; clamp limit above the max
- deterministic order (lower-cased name, then path) over one registry
snapshot per call, so paging never skips or duplicates an entry
- covers both stdio and remote /api/mcp (shared createMCPServer/callTool)
The internal listRepos() method (5 callers), GET /api/repos, and the
`gitnexus list` CLI are unchanged. The array->object tool-result shape
is a deliberate contract change, documented in CHANGELOG.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mcp): reject list_repos limit above the max instead of clamping (#2119)
parseListReposPagination silently clamped limit>max to the maximum while
throwing on every other out-of-bounds value (limit<1, offset<0, non-integer,
NaN). A client that advanced offset by its requested limit (rather than
pagination.nextOffset) then silently skipped repositories and saw
hasMore:false — defeating the "never skips" guarantee. Reject an over-max
limit too, so validation is symmetric and a caller never gets a smaller page
than it asked for without a clear error. Updates the schema/description, the
helper + ListReposPagination JSDoc, the guide note, and the two clamp tests.
Resolves the cross-engine-corroborated P2 (Codex + adversarial lane) and the
maintainability lane's clamp-vs-throw inconsistency from the PR #2120 review.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(mcp): name the list_repos return type and mark the parser @internal
Extract the inline listRepos() element shape into an exported RepoListing
interface and use it for both listRepos() and listReposPage().repositories,
replacing the opaque Awaited<ReturnType<LocalBackend['listRepos']>> expression
the maintainability review flagged. Tag parseListReposPagination @internal
(it is exported only for unit testing). Pure type/JSDoc change; no behavior.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(eval-server): type formatListReposResult to the paginated shape
Narrow formatListReposResult's parameter from `any` to
{ repositories: RepoListing[]; pagination?: ListReposPagination } and drop the
dead bare-array branch — after #2119 callTool('list_repos') always returns the
paginated object, so the Array.isArray shim was unreachable. Add a list_repos
continuation hint to the eval-server's getNextStepHint (parity with the MCP
server), and cover the previously-untested non-empty + hasMore:false formatter
branch. Migrates the two bare-array formatter tests to the object shape.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(mcp): harden list_repos pagination coverage
- Exercise the #2054 sibling-clone guarantee through the real callTool tool
path (in the #2054 describe, which has temp-dir cleanup), proving siblings
and remoteUrl survive listReposPage's sort+slice — not only listRepos().
- Assert total + limit on the middle-page test (a total miscalculation at a
non-zero offset would otherwise slip past it).
- Cover the benign boundaries: negative-zero offset (accepted as page 0) and a
MAX_SAFE_INTEGER offset (empty page).
- Replace the integration test's '\n\n---' split with a string-aware brace
scan, so a repo path containing braces can never truncate the JSON parse.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(skills): sync the list_repos pagination example to the guide mirrors
The .claude and gitnexus-claude-plugin guide mirrors only carried the one-line
table note; add the full "Paginating list_repos" section (shape + multi-page
traversal example + notes) so all three guide copies are byte-consistent with
the canonical gitnexus/skills/gitnexus-guide.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: drop list_repos CHANGELOG entries from this PR
Restore gitnexus/CHANGELOG.md to match main so this PR contributes no
changelog change; the changelog is curated separately from feature PRs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(mcp): add tool safety annotations
* test(mcp): address PR #1127 review follow-ups
- Replace private `_requestHandlers` SDK access in server.test.ts with
`Client` + `InMemoryTransport.createLinkedPair()` for the tools/list
annotation propagation test. The new path uses supported public APIs
and surfaces SDK changes loudly instead of silently degrading.
- Extract `OPEN_WORLD_READ_ONLY_TOOLS` set in tools.test.ts so future
read-only open-world tools can be added without rewriting the
invariant; preserves the current "only `query` is open-world" guard.
- Add inline rationale on `group_sync` annotations explaining the
conservative `idempotentHint: false` (writes contracts.json on every
call even when output is deterministic).
No runtime behavior change. Annotations themselves and tools/list shape
are unchanged.
---------
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
Wire extractors into the sync pipeline with service boundary detection.
GroupService provides high-level API for all group operations.
- Sync pipeline: orchestrates extraction (HTTP, gRPC, topics) with
service boundary assignment and exact matching
- GroupService: groupList, groupSync, groupContracts, groupQuery,
groupStatus (groupImpact deferred to cross-repo follow-up PR)
- CLI: group create/add/remove/list/sync/contracts/query/status
- MCP tools: group_list, group_sync, group_contracts, group_query,
group_status
- Monorepo fixture: 3 services (auth/orders/gateway) connected via
gRPC + Kafka + HTTP — all intra-repo cross-links discovered
- Documentation: CLI commands and MCP tools added to both READMEs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: configure prettier with pre-commit hook integration
Add prettier, lint-staged, and prettier-plugin-tailwindcss at the repo
root with husky pre-commit hook integration. Moves husky from
gitnexus/ to root package.json for reliable hook installation.
- Root package.json with prepare/format/format:check scripts
- .prettierrc with endOfLine:lf and tailwindStylesheet for TW v4
- .prettierignore excluding fixtures, vendor, generated, *.d.ts, *.md
- .gitattributes enforcing LF line endings for Windows consistency
- Pre-commit hook uses direct node_modules/.bin/ paths (no npx)
* style: apply prettier formatting to entire codebase
One-time bulk format. No logic changes.
Use .git-blame-ignore-revs to skip this commit in git blame.
* chore: add .git-blame-ignore-revs for prettier format commit
* perf: pre-commit hook runs only tests related to staged files
Use vitest --related to scope test execution to tests that import
the changed files, instead of running the full suite on every commit.
* perf: remove vitest from pre-commit hook, keep in CI only
Pre-commit now runs lint-staged + tsc only. Tests run in CI
(ci-tests.yml) where they belong — keeps commits fast.
* ci: add prettier format check to quality workflow
PRs will now fail if code isn't formatted with prettier.
- 59 test files covering unit and integration tests
- vitest config with coverage thresholds and fork pooling
- Test fixtures (mini-repo + multi-language sample code)
- Add vitest + coverage-v8 to devDependencies
- Add test scripts (test, test:integration, test:all, test:watch, test:coverage)
- Move typescript to devDependencies where it belongs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>