GitNexus/gitnexus/src/cli/list.ts
Gergő Magyar 7eaeb0a0c4
Some checks are pending
Devcontainer Smoke / Config-transform unit tests (push) Waiting to run
Devcontainer Smoke / Build devcontainer image (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
feat: multi-branch indexing and branch-scoped querying (#2106) (#2137)
* 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)
2026-06-10 10:24:40 +01:00

65 lines
2.5 KiB
TypeScript

/**
* List Command
*
* Shows all indexed repositories from the global registry.
*/
import { listRegisteredRepos } from '../storage/repo-manager.js';
import { t } from './i18n/index.js';
export const listCommand = async () => {
const entries = await listRegisteredRepos({ validate: true });
if (entries.length === 0) {
console.log(t('common.notIndexed'));
console.log(t('common.runAnalyze'));
return;
}
console.log(`\n ${t('list.title', { count: entries.length })}\n`);
// Count occurrences of each name so colliding entries can be
// disambiguated in the header (#829). Unique-name entries render
// identically to pre-#829 output; only collisions gain a suffix.
const nameCounts = new Map<string, number>();
for (const e of entries) {
const key = e.name.toLowerCase();
nameCounts.set(key, (nameCounts.get(key) ?? 0) + 1);
}
for (const entry of entries) {
const indexedDate = new Date(entry.indexedAt).toLocaleString();
const stats = entry.stats || {};
const commitShort = entry.lastCommit?.slice(0, 7) || t('list.unknown');
const hasCollision = (nameCounts.get(entry.name.toLowerCase()) ?? 0) > 1;
const header = hasCollision ? `${entry.name} (${entry.path})` : entry.name;
console.log(` ${header}`);
console.log(` ${t('common.path')}: ${entry.path}`);
console.log(` ${t('list.indexed')}: ${indexedDate}`);
console.log(` ${t('list.commit')}: ${commitShort}`);
if (entry.branch) console.log(` ${t('list.branch')}: ${entry.branch}`);
console.log(
` ${t('list.stats')}: ${t('list.statsValue', {
files: stats.files ?? 0,
symbols: stats.nodes ?? 0,
edges: stats.edges ?? 0,
})}`,
);
if (stats.communities) console.log(` ${t('list.clusters')}: ${stats.communities}`);
if (stats.processes) console.log(` ${t('list.processes')}: ${stats.processes}`);
// Per-branch indexes (#2106). Only rendered when extra branches were
// indexed for this path, so single-branch output is unchanged.
if (entry.branches && entry.branches.length > 0) {
console.log(` ${t('list.branchIndexes')}:`);
for (const b of entry.branches) {
const bCommit = b.lastCommit?.slice(0, 7) || t('list.unknown');
const bIndexed = new Date(b.indexedAt).toLocaleString();
console.log(
` ${t('list.branchLine', { branch: b.branch, commit: bCommit, indexed: bIndexed })}`,
);
}
}
console.log('');
}
};