gitnexus 1.6.4-rc.44 → 1.6.4-rc.46

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.
@@ -5,7 +5,7 @@
5
5
  import { execFileSync } from 'node:child_process';
6
6
  import path from 'path';
7
7
  import { readRegistry } from '../storage/repo-manager.js';
8
- import { getGitRoot, getCurrentCommit, getRemoteUrl } from '../storage/git.js';
8
+ import { findGitRootByDotGit, getCurrentCommit, getRemoteUrl } from '../storage/git.js';
9
9
  /**
10
10
  * Check how many commits the index is behind HEAD (synchronous; uses git CLI).
11
11
  */
@@ -90,9 +90,10 @@ export async function checkCwdMatch(cwd) {
90
90
  }
91
91
  if (bestPath)
92
92
  return { match: 'path', entry: bestPath };
93
- // 2) Sibling-by-remote: locate the cwd's git root, get its remote
94
- // URL, and look for any registered entry with the same fingerprint.
95
- const cwdGitRoot = getGitRoot(cwdResolved);
93
+ // 2) Sibling-by-remote: locate the cwd's git root using only ancestor
94
+ // `.git` checks before shelling out. This keeps MCP startup from
95
+ // running git in an unrelated launch cwd such as $HOME (#1138).
96
+ const cwdGitRoot = findGitRootByDotGit(cwdResolved);
96
97
  if (!cwdGitRoot)
97
98
  return { match: 'none' };
98
99
  const cwdRemote = getRemoteUrl(cwdGitRoot);
@@ -130,6 +130,7 @@ export function createMCPServer(backend) {
130
130
  name: tool.name,
131
131
  description: tool.description,
132
132
  inputSchema: tool.inputSchema,
133
+ annotations: tool.annotations,
133
134
  })),
134
135
  }));
135
136
  // Handle tool calls — append next-step hints to guide agent workflow
@@ -4,9 +4,11 @@
4
4
  * Defines the tools that GitNexus exposes to external AI agents.
5
5
  * All tools support an optional `repo` parameter for multi-repo setups.
6
6
  */
7
+ import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
7
8
  export interface ToolDefinition {
8
9
  name: string;
9
10
  description: string;
11
+ annotations: ToolAnnotations;
10
12
  inputSchema: {
11
13
  type: 'object';
12
14
  properties: Record<string, {
package/dist/mcp/tools.js CHANGED
@@ -4,6 +4,24 @@
4
4
  * Defines the tools that GitNexus exposes to external AI agents.
5
5
  * All tools support an optional `repo` parameter for multi-repo setups.
6
6
  */
7
+ const READ_ONLY_TOOL_ANNOTATIONS = {
8
+ readOnlyHint: true,
9
+ destructiveHint: false,
10
+ idempotentHint: true,
11
+ openWorldHint: false,
12
+ };
13
+ const QUERY_TOOL_ANNOTATIONS = {
14
+ readOnlyHint: true,
15
+ destructiveHint: false,
16
+ idempotentHint: true,
17
+ openWorldHint: true,
18
+ };
19
+ const DESTRUCTIVE_TOOL_ANNOTATIONS = {
20
+ readOnlyHint: false,
21
+ destructiveHint: true,
22
+ idempotentHint: false,
23
+ openWorldHint: false,
24
+ };
7
25
  export const GITNEXUS_TOOLS = [
8
26
  {
9
27
  name: 'list_repos',
@@ -16,6 +34,7 @@ AFTER THIS: READ gitnexus://repo/{name}/context for the repo you want to work wi
16
34
 
17
35
  When multiple repos are indexed, you MUST specify the "repo" parameter
18
36
  on other tools (query, context, impact, etc.) to target the correct one.`,
37
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
19
38
  inputSchema: {
20
39
  type: 'object',
21
40
  properties: {},
@@ -40,6 +59,7 @@ Hybrid ranking: BM25 keyword + semantic vector search, ranked by Reciprocal Rank
40
59
  GROUP MODE: set "repo" to "@<groupName>" to search all member repos in that group (merged via RRF), or "@<groupName>/<groupRepoPath>" to run against a single member (same path keys as in group.yaml). If you use "@<groupName>" only, the member repo defaults to the lexicographically first key in group.yaml "repos". Prefer resources for contracts/status (see migration from legacy group_* tools).
41
60
 
42
61
  SERVICE: optional monorepo path prefix (POSIX-style, case-sensitive segments). When "repo" starts with "@", only processes whose symbols fall under that prefix are included. For a normal indexed repo name (no leading @), this field is currently ignored by the server.`,
62
+ annotations: QUERY_TOOL_ANNOTATIONS,
43
63
  inputSchema: {
44
64
  type: 'object',
45
65
  properties: {
@@ -130,6 +150,7 @@ TIPS:
130
150
  - Community = auto-detected functional area (Leiden algorithm). Properties: heuristicLabel, cohesion, symbolCount, keywords, description, enrichedBy
131
151
  - Process = execution flow trace from entry point to terminal. Properties: heuristicLabel, processType, stepCount, communities, entryPointId, terminalId
132
152
  - Use heuristicLabel (not label) for human-readable community/process names`,
153
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
133
154
  inputSchema: {
134
155
  type: 'object',
135
156
  properties: {
@@ -157,6 +178,7 @@ NOTE: ACCESSES edges (field read/write tracking) are included in context results
157
178
  GROUP MODE: set "repo" to "@<groupName>" to run context in each member repo (aggregated list), or "@<groupName>/<groupRepoPath>" for one member. If you use "@<groupName>" only, the member defaults to the lexicographically first key in group.yaml "repos".
158
179
 
159
180
  SERVICE: optional monorepo path prefix (case-sensitive path segments). When "repo" starts with "@", prefix-matches resolved symbol file paths; when a hit is outside the prefix, that member returns an empty payload for the symbol. Ignored for a normal indexed repo name.`,
181
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
160
182
  inputSchema: {
161
183
  type: 'object',
162
184
  properties: {
@@ -197,6 +219,7 @@ WHEN TO USE: Before committing — to understand what your changes affect. Pre-c
197
219
  AFTER THIS: Review affected processes. Use context() on high-risk symbols. READ gitnexus://repo/{name}/process/{name} for full traces.
198
220
 
199
221
  Returns: changed symbols, affected processes, and a risk summary.`,
222
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
200
223
  inputSchema: {
201
224
  type: 'object',
202
225
  properties: {
@@ -229,6 +252,7 @@ AFTER THIS: Run detect_changes() to verify no unexpected side effects.
229
252
  Each edit is tagged with confidence:
230
253
  - "graph": found via knowledge graph relationships (high confidence, safe to accept)
231
254
  - "text_search": found via regex text search (lower confidence, review carefully)`,
255
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
232
256
  inputSchema: {
233
257
  type: 'object',
234
258
  properties: {
@@ -282,6 +306,7 @@ Confidence: 1.0 = certain, <0.8 = fuzzy match
282
306
  GROUP MODE: set "repo" to "@<groupName>" for cross-repo impact anchored at the default member (lexicographically first key in group.yaml "repos"), or "@<groupName>/<groupRepoPath>" to choose the member (same path keys as in group.yaml). Phase-1 walk runs in that member; cross-boundary fan-out uses the group bridge.
283
307
 
284
308
  SERVICE: optional monorepo path prefix (case-sensitive path segments). When "repo" starts with "@", scopes the local impact walk and cross-repo symbol paths to files under that prefix; ignored for a normal indexed repo name.`,
309
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
285
310
  inputSchema: {
286
311
  type: 'object',
287
312
  properties: {
@@ -366,6 +391,7 @@ WHEN TO USE: Understanding API consumption patterns, finding orphaned routes. Fo
366
391
  AFTER THIS: Use impact() on specific route handlers to see full blast radius.
367
392
 
368
393
  Returns: route nodes with their handlers, middleware wrapper chains (e.g., withAuth, withRateLimit), and consumers.`,
394
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
369
395
  inputSchema: {
370
396
  type: 'object',
371
397
  properties: {
@@ -388,6 +414,7 @@ Returns: route nodes with their handlers, middleware wrapper chains (e.g., withA
388
414
  WHEN TO USE: Understanding tool APIs, finding tool implementations, impact analysis for tool changes.
389
415
 
390
416
  Returns: tool nodes with their handler files and descriptions.`,
417
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
391
418
  inputSchema: {
392
419
  type: 'object',
393
420
  properties: {
@@ -405,6 +432,7 @@ WHEN TO USE: Detecting mismatches between what an API route returns and what con
405
432
  REQUIRES: Route nodes with responseKeys (extracted from .json({...}) calls during indexing).
406
433
 
407
434
  Returns routes that have both detected response keys AND consumers. Shows top-level keys each endpoint returns (e.g., data, pagination, error) and what keys each consumer accesses. Reports MISMATCH status when a consumer accesses keys not present in the route's response shape.`,
435
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
408
436
  inputSchema: {
409
437
  type: 'object',
410
438
  properties: {
@@ -429,6 +457,7 @@ WHEN TO USE: BEFORE modifying any API route handler. Shows what consumers depend
429
457
  Risk levels: LOW (0-3 consumers), MEDIUM (4-9 or any mismatches), HIGH (10+ consumers or mismatches with 4+ consumers). Mismatches with confidence "low" indicate the consumer file fetches multiple routes — property attribution is approximate.
430
458
 
431
459
  Returns: single route object when one match, or { routes: [...], total: N } for multiple matches. Combines route_map, shape_check, and impact data.`,
460
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
432
461
  inputSchema: {
433
462
  type: 'object',
434
463
  properties: {
@@ -444,6 +473,7 @@ Returns: single route object when one match, or { routes: [...], total: N } for
444
473
  description: `List all configured repository groups, or return details for one group (repos, manifest links).
445
474
 
446
475
  WHEN TO USE: Discover groups before group_sync. Optional "name" returns a single group's config.`,
476
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
447
477
  inputSchema: {
448
478
  type: 'object',
449
479
  properties: {
@@ -457,6 +487,9 @@ WHEN TO USE: Discover groups before group_sync. Optional "name" returns a single
457
487
  description: `Rebuild the Contract Registry (contracts.json) for a group: extract HTTP contracts, apply manifest links, exact-match cross-links.
458
488
 
459
489
  WHEN TO USE: After changing group.yaml or re-indexing member repos.`,
490
+ // Writes contracts.json on every call; conservatively non-idempotent
491
+ // even though output is deterministic for identical input.
492
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
460
493
  inputSchema: {
461
494
  type: 'object',
462
495
  properties: {
@@ -28,6 +28,13 @@ export declare const getRemoteUrl: (repoPath: string) => string | undefined;
28
28
  * Find the git repository root from any path inside the repo
29
29
  */
30
30
  export declare const getGitRoot: (fromPath: string) => string | null;
31
+ /**
32
+ * Find a git root by checking only `.git` entries on the ancestor chain.
33
+ *
34
+ * Unlike `getGitRoot`, this does not spawn `git`, so MCP can cheaply decide
35
+ * whether a launch cwd is a worktree before running any subprocess there.
36
+ */
37
+ export declare const findGitRootByDotGit: (fromPath: string) => string | null;
31
38
  /**
32
39
  * Check whether a directory contains a .git entry (file or folder).
33
40
  *
@@ -91,6 +91,35 @@ export const getGitRoot = (fromPath) => {
91
91
  return null;
92
92
  }
93
93
  };
94
+ /**
95
+ * Find a git root by checking only `.git` entries on the ancestor chain.
96
+ *
97
+ * Unlike `getGitRoot`, this does not spawn `git`, so MCP can cheaply decide
98
+ * whether a launch cwd is a worktree before running any subprocess there.
99
+ */
100
+ export const findGitRootByDotGit = (fromPath) => {
101
+ let current = path.resolve(fromPath);
102
+ try {
103
+ if (!statSync(current).isDirectory()) {
104
+ current = path.dirname(current);
105
+ }
106
+ }
107
+ catch {
108
+ return null;
109
+ }
110
+ while (true) {
111
+ try {
112
+ statSync(path.join(current, '.git'));
113
+ return current;
114
+ }
115
+ catch {
116
+ const parent = path.dirname(current);
117
+ if (parent === current)
118
+ return null;
119
+ current = parent;
120
+ }
121
+ }
122
+ };
94
123
  /**
95
124
  * Check whether a directory contains a .git entry (file or folder).
96
125
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.4-rc.44",
3
+ "version": "1.6.4-rc.46",
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",