gitnexus 1.6.12-rc.32 → 1.6.12-rc.33

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.
@@ -497,6 +497,23 @@ export declare class LocalBackend {
497
497
  * - `remoteUrl`: the canonical origin URL recorded at index time.
498
498
  */
499
499
  listRepos(): Promise<RepoListing[]>;
500
+ /**
501
+ * Lightweight registry count for schema-introspection callers that only
502
+ * need to know "one repo or many?" without paying the full staleness fan-out
503
+ * cost that listRepos() incurs. Uses the same validated registry
504
+ * `refreshRepos` / `selectToolRepository` see (`validate: true` prunes
505
+ * entries whose metadata is provably gone) so tools/list cannot advertise a
506
+ * multi-repo schema for ENOENT ghosts. No git processes are spawned.
507
+ */
508
+ countRepos(): Promise<number>;
509
+ /**
510
+ * In-memory validated registry size after the last `refreshRepos` / init /
511
+ * `selectToolRepository` refresh. `countRepos()` does not populate this map.
512
+ * Schema introspection uses this after a refreshed cwd probe so cardinality
513
+ * and the probe share one snapshot — without putting `refreshRepos()` (and
514
+ * its kuzu cleanup) on the 0–1 `countRepos` path.
515
+ */
516
+ cachedRepoCount(): number;
500
517
  /**
501
518
  * Paginated view over {@link listRepos} for the `list_repos` MCP tool (#2119).
502
519
  *
@@ -1841,6 +1841,28 @@ export class LocalBackend {
1841
1841
  };
1842
1842
  });
1843
1843
  }
1844
+ /**
1845
+ * Lightweight registry count for schema-introspection callers that only
1846
+ * need to know "one repo or many?" without paying the full staleness fan-out
1847
+ * cost that listRepos() incurs. Uses the same validated registry
1848
+ * `refreshRepos` / `selectToolRepository` see (`validate: true` prunes
1849
+ * entries whose metadata is provably gone) so tools/list cannot advertise a
1850
+ * multi-repo schema for ENOENT ghosts. No git processes are spawned.
1851
+ */
1852
+ async countRepos() {
1853
+ const entries = await listRegisteredRepos({ validate: true });
1854
+ return entries.length;
1855
+ }
1856
+ /**
1857
+ * In-memory validated registry size after the last `refreshRepos` / init /
1858
+ * `selectToolRepository` refresh. `countRepos()` does not populate this map.
1859
+ * Schema introspection uses this after a refreshed cwd probe so cardinality
1860
+ * and the probe share one snapshot — without putting `refreshRepos()` (and
1861
+ * its kuzu cleanup) on the 0–1 `countRepos` path.
1862
+ */
1863
+ cachedRepoCount() {
1864
+ return this.repos.size;
1865
+ }
1844
1866
  /**
1845
1867
  * Paginated view over {@link listRepos} for the `list_repos` MCP tool (#2119).
1846
1868
  *
@@ -148,26 +148,28 @@ export class McpRepositoryPolicy {
148
148
  mutatingRequiresRepo: requiresRepo,
149
149
  };
150
150
  }
151
- // One fresh listing supplies both schema decisions. Besides keeping the
152
- // advertised contract internally consistent, this avoids doing two full
153
- // per-repo staleness fan-outs for every tools/list request.
154
- const visibleRepos = await this.listAllowedRepos(backend);
155
- if (visibleRepos.length <= 1) {
151
+ // Validated registry cardinality only — no listRepos() staleness git.
152
+ // The 0–1 arm stays a cheap countRepos() (no refreshRepos / kuzu cleanup).
153
+ const repoCount = await backend.countRepos();
154
+ if (repoCount <= 1) {
156
155
  return { readOnlyRequiresRepo: false, mutatingRequiresRepo: false };
157
156
  }
158
157
  try {
159
- // listAllowedRepos() refreshed this backend immediately above. Resolve
160
- // against that exact cache snapshot instead of racing another registry
161
- // read; only read-only schemas may advertise the cwd-derived default.
158
+ // countRepos() does not refresh the backend; this cwd probe must.
162
159
  await backend.selectToolRepository(undefined, undefined, {
163
160
  allowCwdDefault: true,
164
- refreshRegistry: false,
161
+ refreshRegistry: true,
165
162
  });
166
- return { readOnlyRequiresRepo: false, mutatingRequiresRepo: true };
167
163
  }
168
164
  catch {
169
165
  return { readOnlyRequiresRepo: true, mutatingRequiresRepo: true };
170
166
  }
167
+ // The probe just refreshed. If that snapshot is now a singleton, match the
168
+ // <=1 arm rather than advertising a split mutating-only schema.
169
+ if (backend.cachedRepoCount() <= 1) {
170
+ return { readOnlyRequiresRepo: false, mutatingRequiresRepo: false };
171
+ }
172
+ return { readOnlyRequiresRepo: false, mutatingRequiresRepo: true };
171
173
  }
172
174
  async listReposPage(backend, params) {
173
175
  const { limit, offset } = parseListReposPagination(params, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.12-rc.32",
3
+ "version": "1.6.12-rc.33",
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",