gitnexus 1.6.8-rc.26 → 1.6.8-rc.27

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.
package/README.md CHANGED
@@ -134,7 +134,7 @@ Your AI agent gets these tools automatically:
134
134
  | `rename` | Multi-file coordinated rename with graph + text search | Optional |
135
135
  | `cypher` | Raw Cypher graph queries | Optional |
136
136
 
137
- > With one indexed repo, the `repo` param is optional. With multiple, specify which: `query({query: "auth", repo: "my-app"})`.
137
+ > With one indexed repo, the `repo` param is optional. With multiple, specify which: `query({search_query: "auth", repo: "my-app"})`.
138
138
 
139
139
  ## MCP Resources
140
140
 
@@ -134,7 +134,7 @@ This project is indexed by GitNexus as **${projectName}**${noStats ? '' : ` (${s
134
134
  - **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run \`impact({target: "symbolName", direction: "upstream"})\` and report the blast radius (direct callers, affected processes, risk level) to the user.
135
135
  - **MUST run \`detect_changes()\` before committing** to verify your changes only affect expected symbols and execution flows. For regression review, compare against the default branch: \`detect_changes({scope: "compare", base_ref: ${JSON.stringify(markdownSafeBranch(defaultBranch))}})\`.
136
136
  - **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
137
- - When exploring unfamiliar code, use \`query({query: "concept"})\` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
137
+ - When exploring unfamiliar code, use \`query({search_query: "concept"})\` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
138
138
  - When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use \`context({name: "symbolName"})\`.
139
139
  - For security review, \`explain({target: "fileOrSymbol"})\` lists taint findings (source→sink flows; needs \`analyze --pdg\`).
140
140
 
@@ -483,7 +483,7 @@ const renderSkillMarkdown = (community, projectName, members, files, entryPoints
483
483
  lines.push('## How to Explore');
484
484
  lines.push('');
485
485
  lines.push(`1. \`context({name: "${firstEntry}"})\` \u2014 see callers and callees`);
486
- lines.push(`2. \`query({query: "${community.label.toLowerCase()}"})\` \u2014 find related execution flows`);
486
+ lines.push(`2. \`query({search_query: "${community.label.toLowerCase()}"})\` \u2014 find related execution flows`);
487
487
  lines.push('3. Read key files listed above for implementation details');
488
488
  lines.push('4. `explain({target: "<file or symbol>"})` — persisted taint findings (source→sink data flows), when indexed with `--pdg`');
489
489
  lines.push('');
package/dist/cli/tool.js CHANGED
@@ -62,7 +62,8 @@ export async function queryCommand(queryText, options) {
62
62
  }
63
63
  const backend = await getBackend();
64
64
  const result = await backend.callTool('query', {
65
- query: queryText,
65
+ // #2175: canonical param is search_query; the backend still accepts legacy "query".
66
+ search_query: queryText,
66
67
  task_context: options?.context,
67
68
  goal: options?.goal,
68
69
  limit: options?.limit ? parseInt(options.limit) : undefined,
@@ -154,7 +155,8 @@ export async function cypherCommand(query, options) {
154
155
  }
155
156
  const backend = await getBackend();
156
157
  const result = await backend.callTool('cypher', {
157
- query,
158
+ // #2175: canonical param is statement; the backend still accepts legacy "query".
159
+ statement: query,
158
160
  repo: options?.repo,
159
161
  branch: options?.branch,
160
162
  });
@@ -43,6 +43,23 @@ function looksLikeFilePath(target) {
43
43
  const lower = target.toLowerCase();
44
44
  return SOURCE_FILE_EXTENSIONS.some((ext) => lower.endsWith(ext));
45
45
  }
46
+ /**
47
+ * Resolve a string tool param from its canonical name or legacy alias (#2175).
48
+ * Returns the first NON-BLANK string of [canonical, legacy] — the canonical (new)
49
+ * name is preferred when it carries a real value, otherwise the legacy value is used.
50
+ * A blank/whitespace new value therefore does NOT clobber a valid legacy value (e.g. a
51
+ * gradually-migrating client that always emits the new key, blank when unset). A
52
+ * non-string value (the MCP envelope is not schema-validated, so clients can send any
53
+ * JSON type) and an all-blank input resolve to `undefined`, so the caller returns a
54
+ * friendly required-param error instead of throwing `TypeError` on `.trim()`.
55
+ */
56
+ function resolveAliasString(canonical, legacy) {
57
+ for (const value of [canonical, legacy]) {
58
+ if (typeof value === 'string' && value.trim())
59
+ return value;
60
+ }
61
+ return undefined;
62
+ }
46
63
  // AI context generation is CLI-only (gitnexus analyze)
47
64
  // import { generateAIContextFiles } from '../../cli/ai-context.js';
48
65
  /**
@@ -1030,6 +1047,15 @@ export class LocalBackend {
1030
1047
  return this.handleGroupTool(method, params || {});
1031
1048
  }
1032
1049
  const p = params && typeof params === 'object' ? params : {};
1050
+ // #2175: Claude Code drops a tool-call argument named exactly "query", so the
1051
+ // query/cypher tools advertise "search_query"/"statement" while still accepting the
1052
+ // legacy "query" key for backward compat. The alias is resolved with `?? ` (new name
1053
+ // wins) at every consumer site rather than by mutating params here, so precedence is
1054
+ // uniform and there is no hidden mutation: query()/cypher() read it directly, the
1055
+ // legacy "search" alias routes through query(), and the cross-repo group-forward
1056
+ // resolves it self-contained in callToolAtGroupRepo. This is permanent compatibility
1057
+ // — third-party MCP clients may legitimately send "query", so the alias is not slated
1058
+ // for removal even if Claude Code's argument handling later changes.
1033
1059
  if ((method === 'impact' || method === 'query' || method === 'context') &&
1034
1060
  typeof p.repo === 'string' &&
1035
1061
  p.repo.startsWith('@')) {
@@ -1118,14 +1144,18 @@ export class LocalBackend {
1118
1144
  * 4. Return: { processes, process_symbols, definitions }
1119
1145
  */
1120
1146
  async query(repo, params) {
1121
- if (!params.query?.trim()) {
1122
- return { error: 'query parameter is required and cannot be empty.' };
1147
+ // #2175: each consumer resolves the search_query/query alias itself (there is no
1148
+ // chokepoint mutation in callTool). This also serves the GroupService port, which
1149
+ // reaches query() carrying only the legacy `query` key.
1150
+ const rawQuery = resolveAliasString(params.search_query, params.query);
1151
+ if (!rawQuery?.trim()) {
1152
+ return { error: 'search_query (or legacy query) parameter is required and cannot be empty.' };
1123
1153
  }
1124
1154
  await this.ensureInitialized(repo);
1125
1155
  const processLimit = params.limit || 5;
1126
1156
  const maxSymbolsPerProcess = params.max_symbols || 10;
1127
1157
  const includeContent = params.include_content ?? false;
1128
- const searchQuery = params.query.trim();
1158
+ const searchQuery = rawQuery.trim();
1129
1159
  // Per-phase timing instrumentation (#553). Records wall time for each
1130
1160
  // observable sub-step of the search pipeline so production latency can
1131
1161
  // be aggregated offline for Pareto analysis and bottleneck detection.
@@ -1602,7 +1632,10 @@ export class LocalBackend {
1602
1632
  const repo = await this.resolveRepo(repoName);
1603
1633
  return this.cypher(repo, { query, params });
1604
1634
  }
1605
- async cypher(repo, request) {
1635
+ async cypher(repo,
1636
+ // #2175: "statement" is the advertised param; "query" is the legacy alias,
1637
+ // still accepted (and the field the internal executeCypher() passes). New wins.
1638
+ request) {
1606
1639
  await this.ensureInitialized(repo);
1607
1640
  if (!isLbugReady(repo.lbugPath)) {
1608
1641
  return { error: 'LadybugDB not ready. Index may be corrupted.' };
@@ -1612,8 +1645,14 @@ export class LocalBackend {
1612
1645
  error: '"params" must be a plain object with scalar values (string/number/boolean/null).',
1613
1646
  };
1614
1647
  }
1648
+ const cypherText = resolveAliasString(request.statement, request.query) ?? '';
1649
+ if (!cypherText.trim()) {
1650
+ // Mirror query()'s friendly required-param error instead of letting an empty
1651
+ // string fall through to a raw LadybugDB prepare error (#2175 review).
1652
+ return { error: 'statement (or legacy query) parameter is required and cannot be empty.' };
1653
+ }
1615
1654
  try {
1616
- const result = await executeParameterized(repo.lbugPath, request.query, request.params ?? {});
1655
+ const result = await executeParameterized(repo.lbugPath, cypherText, request.params ?? {});
1617
1656
  return result;
1618
1657
  }
1619
1658
  catch (err) {
@@ -3984,7 +4023,10 @@ export class LocalBackend {
3984
4023
  if (method === 'query') {
3985
4024
  const queryArgs = {
3986
4025
  name: groupName,
3987
- query: params.query,
4026
+ // #2175: resolve the search_query alias here (new name wins, same rule as the
4027
+ // local query() handler) so the group path is self-contained and does not depend
4028
+ // on params being normalized upstream. groupQuery() reads `query`.
4029
+ query: resolveAliasString(params.search_query, params.query),
3988
4030
  };
3989
4031
  if (typeof params.task_context === 'string')
3990
4032
  queryArgs.task_context = params.task_context;
@@ -234,7 +234,7 @@ async function getReposResource(backend) {
234
234
  if (repos.length > 1) {
235
235
  lines.push('');
236
236
  lines.push('# Multiple repos indexed. Use repo parameter in tool calls:');
237
- lines.push(`# query({query: "auth", repo: "${repos[0].name}"})`);
237
+ lines.push(`# query({search_query: "auth", repo: "${repos[0].name}"})`);
238
238
  }
239
239
  return lines.join('\n');
240
240
  }
package/dist/mcp/tools.js CHANGED
@@ -98,7 +98,14 @@ SERVICE: optional monorepo path prefix (POSIX-style, case-sensitive segments). W
98
98
  inputSchema: {
99
99
  type: 'object',
100
100
  properties: {
101
- query: { type: 'string', description: 'Natural language or keyword search query' },
101
+ // #2175: the legacy `query` key is still accepted by the handler
102
+ // (resolveAliasString in local-backend.ts), but is deliberately NOT named in the
103
+ // advertised property or its description — surfacing "query" in the schema an LLM
104
+ // reads would nudge it to send `query`, the exact argument Claude Code drops.
105
+ search_query: {
106
+ type: 'string',
107
+ description: 'Natural language or keyword search query.',
108
+ },
102
109
  task_context: {
103
110
  type: 'string',
104
111
  description: 'What you are working on (e.g., "adding OAuth support"). Helps ranking.',
@@ -136,7 +143,7 @@ SERVICE: optional monorepo path prefix (POSIX-style, case-sensitive segments). W
136
143
  description: 'Optional monorepo service root (relative path, "/" separators). In group mode (@repo), prefix-matches symbol file paths; ignored for a normal repo name. Empty string is rejected server-side.',
137
144
  },
138
145
  },
139
- required: ['query'],
146
+ required: ['search_query'],
140
147
  },
141
148
  },
142
149
  {
@@ -189,7 +196,14 @@ TIPS:
189
196
  inputSchema: {
190
197
  type: 'object',
191
198
  properties: {
192
- query: { type: 'string', description: 'Cypher query to execute' },
199
+ // #2175: the legacy `query` key is still accepted by the handler
200
+ // (resolveAliasString in local-backend.ts), but is deliberately NOT named in the
201
+ // advertised property or its description — surfacing "query" in the schema an LLM
202
+ // reads would nudge it to send `query`, the exact argument Claude Code drops.
203
+ statement: {
204
+ type: 'string',
205
+ description: 'Cypher statement to execute.',
206
+ },
193
207
  params: {
194
208
  type: 'object',
195
209
  description: 'Optional query parameters for placeholders (e.g. $name) to execute via prepared statement binding.',
@@ -199,7 +213,7 @@ TIPS:
199
213
  description: 'Repository name or path. Omit if only one repo is indexed.',
200
214
  },
201
215
  },
202
- required: ['query'],
216
+ required: ['statement'],
203
217
  },
204
218
  },
205
219
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.8-rc.26",
3
+ "version": "1.6.8-rc.27",
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",
@@ -16,10 +16,10 @@ description: "Use when the user is debugging a bug, tracing an error, or asking
16
16
  ## Workflow
17
17
 
18
18
  ```
19
- 1. query({query: "<error or symptom>"}) → Find related execution flows
19
+ 1. query({search_query: "<error or symptom>"}) → Find related execution flows
20
20
  2. context({name: "<suspect>"}) → See callers/callees/processes
21
21
  3. READ gitnexus://repo/{name}/process/{name} → Trace execution flow
22
- 4. cypher({query: "MATCH path..."}) → Custom traces if needed
22
+ 4. cypher({statement: "MATCH path..."}) → Custom traces if needed
23
23
  ```
24
24
 
25
25
  > If "Index is stale" → run `node .gitnexus/run.cjs analyze` in terminal.
@@ -51,7 +51,7 @@ description: "Use when the user is debugging a bug, tracing an error, or asking
51
51
  **query** — find code related to error:
52
52
 
53
53
  ```
54
- query({query: "payment validation error"})
54
+ query({search_query: "payment validation error"})
55
55
  → Processes: CheckoutFlow, ErrorHandling
56
56
  → Symbols: validatePayment, handlePaymentError, PaymentException
57
57
  ```
@@ -75,7 +75,7 @@ RETURN [n IN nodes(path) | n.name] AS chain
75
75
  ## Example: "Payment endpoint returns 500 intermittently"
76
76
 
77
77
  ```
78
- 1. query({query: "payment error handling"})
78
+ 1. query({search_query: "payment error handling"})
79
79
  → Processes: CheckoutFlow, ErrorHandling
80
80
  → Symbols: validatePayment, handlePaymentError
81
81
 
@@ -18,7 +18,7 @@ description: "Use when the user asks how code works, wants to understand archite
18
18
  ```
19
19
  1. READ gitnexus://repos → Discover indexed repos
20
20
  2. READ gitnexus://repo/{name}/context → Codebase overview, check staleness
21
- 3. query({query: "<what you want to understand>"}) → Find related execution flows
21
+ 3. query({search_query: "<what you want to understand>"}) → Find related execution flows
22
22
  4. context({name: "<symbol>"}) → Deep dive on specific symbol
23
23
  5. READ gitnexus://repo/{name}/process/{name} → Trace full execution flow
24
24
  ```
@@ -50,7 +50,7 @@ description: "Use when the user asks how code works, wants to understand archite
50
50
  **query** — find execution flows related to a concept:
51
51
 
52
52
  ```
53
- query({query: "payment processing"})
53
+ query({search_query: "payment processing"})
54
54
  → Processes: CheckoutFlow, RefundFlow, WebhookHandler
55
55
  → Symbols grouped by flow with file locations
56
56
  ```
@@ -68,7 +68,7 @@ context({name: "validateUser"})
68
68
 
69
69
  ```
70
70
  1. READ gitnexus://repo/my-app/context → 918 symbols, 45 processes
71
- 2. query({query: "payment processing"})
71
+ 2. query({search_query: "payment processing"})
72
72
  → CheckoutFlow: processPayment → validateCard → chargeStripe
73
73
  → RefundFlow: initiateRefund → calculateRefund → processRefund
74
74
  3. context({name: "processPayment"})
@@ -17,7 +17,7 @@ description: "Use when the user wants to rename, extract, split, move, or restru
17
17
 
18
18
  ```
19
19
  1. impact({target: "X", direction: "upstream"}) → Map all dependents
20
- 2. query({query: "X"}) → Find execution flows involving X
20
+ 2. query({search_query: "X"}) → Find execution flows involving X
21
21
  3. context({name: "X"}) → See all incoming/outgoing refs
22
22
  4. Plan update order: interfaces → implementations → callers → tests
23
23
  ```