gitnexus 1.6.6-rc.96 → 1.6.6-rc.97
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 +7 -0
- package/dist/cli/help-i18n.js +3 -0
- package/dist/cli/i18n/en.d.ts +3 -1
- package/dist/cli/i18n/en.js +3 -1
- package/dist/cli/i18n/resources.d.ts +5 -1
- package/dist/cli/i18n/zh-CN.d.ts +2 -0
- package/dist/cli/i18n/zh-CN.js +3 -1
- package/dist/cli/index.js +4 -1
- package/dist/cli/tool.d.ts +4 -1
- package/dist/cli/tool.js +27 -4
- package/dist/mcp/local/local-backend.js +17 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -170,6 +170,13 @@ gitnexus clean --all --force # Delete all indexes
|
|
|
170
170
|
gitnexus wiki [path] # Generate LLM-powered docs from knowledge graph
|
|
171
171
|
gitnexus wiki --model <model> # Wiki with custom LLM model (default: gpt-4o-mini)
|
|
172
172
|
|
|
173
|
+
# Direct graph queries — the same tools the MCP server exposes, no MCP daemon needed
|
|
174
|
+
gitnexus query "<concept>" # Process-grouped hybrid search
|
|
175
|
+
gitnexus context <symbol> [--uid <uid> | --file <path>] # 360° symbol view; flags disambiguate a shared name
|
|
176
|
+
gitnexus impact <symbol> [--uid <uid> | --file <path> | --kind <kind>] # Blast radius; flags disambiguate a shared name
|
|
177
|
+
gitnexus detect-changes # Map the working-tree diff to affected symbols and execution flows
|
|
178
|
+
gitnexus cypher "<query>" # Run a raw Cypher query against the knowledge graph
|
|
179
|
+
|
|
173
180
|
# Repository groups (multi-repo / monorepo service tracking)
|
|
174
181
|
gitnexus group create <name> # Create a repository group
|
|
175
182
|
gitnexus group add <group> <groupPath> <registryName> # Add a repo to a group. <groupPath> is a hierarchy path (e.g. hr/hiring/backend); <registryName> is the repo's name from the registry (see `gitnexus list`)
|
package/dist/cli/help-i18n.js
CHANGED
|
@@ -97,6 +97,9 @@ const OPTION_DESCRIPTION_KEYS = {
|
|
|
97
97
|
'context|--content': 'help.option.content',
|
|
98
98
|
'impact|-d, --direction <dir>': 'help.option.impact.direction',
|
|
99
99
|
'impact|-r, --repo <name>': 'help.option.repo.target',
|
|
100
|
+
'impact|-u, --uid <uid>': 'help.option.context.uid',
|
|
101
|
+
'impact|-f, --file <path>': 'help.option.context.file',
|
|
102
|
+
'impact|--kind <kind>': 'help.option.impact.kind',
|
|
100
103
|
'impact|--depth <n>': 'help.option.impact.depth',
|
|
101
104
|
'impact|--include-tests': 'help.option.impact.includeTests',
|
|
102
105
|
'impact|--limit <n>': 'help.option.impact.limit',
|
package/dist/cli/i18n/en.d.ts
CHANGED
|
@@ -41,8 +41,9 @@ export declare const en: {
|
|
|
41
41
|
readonly 'tool.noIndexed': "GitNexus: No indexed repositories found. Run: gitnexus analyze";
|
|
42
42
|
readonly 'tool.usage.query': "Usage: gitnexus query <search_query>";
|
|
43
43
|
readonly 'tool.usage.context': "Usage: gitnexus context <symbol_name> [--uid <uid>] [--file <path>]";
|
|
44
|
-
readonly 'tool.usage.impact': "Usage: gitnexus impact <symbol_name> [--direction upstream|downstream]";
|
|
44
|
+
readonly 'tool.usage.impact': "Usage: gitnexus impact <symbol_name> [--uid <uid>] [--file <path>] [--kind <kind>] [--direction upstream|downstream]";
|
|
45
45
|
readonly 'tool.usage.cypher': "Usage: gitnexus cypher <cypher_query>";
|
|
46
|
+
readonly 'tool.warn.unknownKind': "--kind '{{kind}}' is not a known symbol kind (e.g. Function, Class, Method); it will not narrow the result.";
|
|
46
47
|
readonly 'tool.detectChanges.noChanges': "No changes detected.";
|
|
47
48
|
readonly 'tool.detectChanges.changesSummary': "Changes: {{files}} files, {{symbols}} symbols";
|
|
48
49
|
readonly 'tool.detectChanges.affectedProcesses': "Affected processes: {{count}}";
|
|
@@ -177,6 +178,7 @@ export declare const en: {
|
|
|
177
178
|
readonly 'help.option.repo.target': "Target repository";
|
|
178
179
|
readonly 'help.option.context.uid': "Direct symbol UID (zero-ambiguity lookup)";
|
|
179
180
|
readonly 'help.option.context.file': "File path to disambiguate common names";
|
|
181
|
+
readonly 'help.option.impact.kind': "Kind filter to disambiguate common names (e.g. Function, Class, Method)";
|
|
180
182
|
readonly 'help.option.impact.direction': "upstream (dependants) or downstream (dependencies)";
|
|
181
183
|
readonly 'help.option.impact.depth': "Max relationship depth (default: 3)";
|
|
182
184
|
readonly 'help.option.impact.includeTests': "Include test files in results";
|
package/dist/cli/i18n/en.js
CHANGED
|
@@ -41,8 +41,9 @@ export const en = {
|
|
|
41
41
|
'tool.noIndexed': 'GitNexus: No indexed repositories found. Run: gitnexus analyze',
|
|
42
42
|
'tool.usage.query': 'Usage: gitnexus query <search_query>',
|
|
43
43
|
'tool.usage.context': 'Usage: gitnexus context <symbol_name> [--uid <uid>] [--file <path>]',
|
|
44
|
-
'tool.usage.impact': 'Usage: gitnexus impact <symbol_name> [--direction upstream|downstream]',
|
|
44
|
+
'tool.usage.impact': 'Usage: gitnexus impact <symbol_name> [--uid <uid>] [--file <path>] [--kind <kind>] [--direction upstream|downstream]',
|
|
45
45
|
'tool.usage.cypher': 'Usage: gitnexus cypher <cypher_query>',
|
|
46
|
+
'tool.warn.unknownKind': "--kind '{{kind}}' is not a known symbol kind (e.g. Function, Class, Method); it will not narrow the result.",
|
|
46
47
|
'tool.detectChanges.noChanges': 'No changes detected.',
|
|
47
48
|
'tool.detectChanges.changesSummary': 'Changes: {{files}} files, {{symbols}} symbols',
|
|
48
49
|
'tool.detectChanges.affectedProcesses': 'Affected processes: {{count}}',
|
|
@@ -177,6 +178,7 @@ export const en = {
|
|
|
177
178
|
'help.option.repo.target': 'Target repository',
|
|
178
179
|
'help.option.context.uid': 'Direct symbol UID (zero-ambiguity lookup)',
|
|
179
180
|
'help.option.context.file': 'File path to disambiguate common names',
|
|
181
|
+
'help.option.impact.kind': 'Kind filter to disambiguate common names (e.g. Function, Class, Method)',
|
|
180
182
|
'help.option.impact.direction': 'upstream (dependants) or downstream (dependencies)',
|
|
181
183
|
'help.option.impact.depth': 'Max relationship depth (default: 3)',
|
|
182
184
|
'help.option.impact.includeTests': 'Include test files in results',
|
|
@@ -42,8 +42,9 @@ export declare const cliResources: {
|
|
|
42
42
|
readonly 'tool.noIndexed': "GitNexus: No indexed repositories found. Run: gitnexus analyze";
|
|
43
43
|
readonly 'tool.usage.query': "Usage: gitnexus query <search_query>";
|
|
44
44
|
readonly 'tool.usage.context': "Usage: gitnexus context <symbol_name> [--uid <uid>] [--file <path>]";
|
|
45
|
-
readonly 'tool.usage.impact': "Usage: gitnexus impact <symbol_name> [--direction upstream|downstream]";
|
|
45
|
+
readonly 'tool.usage.impact': "Usage: gitnexus impact <symbol_name> [--uid <uid>] [--file <path>] [--kind <kind>] [--direction upstream|downstream]";
|
|
46
46
|
readonly 'tool.usage.cypher': "Usage: gitnexus cypher <cypher_query>";
|
|
47
|
+
readonly 'tool.warn.unknownKind': "--kind '{{kind}}' is not a known symbol kind (e.g. Function, Class, Method); it will not narrow the result.";
|
|
47
48
|
readonly 'tool.detectChanges.noChanges': "No changes detected.";
|
|
48
49
|
readonly 'tool.detectChanges.changesSummary': "Changes: {{files}} files, {{symbols}} symbols";
|
|
49
50
|
readonly 'tool.detectChanges.affectedProcesses': "Affected processes: {{count}}";
|
|
@@ -178,6 +179,7 @@ export declare const cliResources: {
|
|
|
178
179
|
readonly 'help.option.repo.target': "Target repository";
|
|
179
180
|
readonly 'help.option.context.uid': "Direct symbol UID (zero-ambiguity lookup)";
|
|
180
181
|
readonly 'help.option.context.file': "File path to disambiguate common names";
|
|
182
|
+
readonly 'help.option.impact.kind': "Kind filter to disambiguate common names (e.g. Function, Class, Method)";
|
|
181
183
|
readonly 'help.option.impact.direction': "upstream (dependants) or downstream (dependencies)";
|
|
182
184
|
readonly 'help.option.impact.depth': "Max relationship depth (default: 3)";
|
|
183
185
|
readonly 'help.option.impact.includeTests': "Include test files in results";
|
|
@@ -253,6 +255,7 @@ export declare const cliResources: {
|
|
|
253
255
|
'tool.usage.context': string;
|
|
254
256
|
'tool.usage.impact': string;
|
|
255
257
|
'tool.usage.cypher': string;
|
|
258
|
+
'tool.warn.unknownKind': string;
|
|
256
259
|
'tool.detectChanges.noChanges': string;
|
|
257
260
|
'tool.detectChanges.changesSummary': string;
|
|
258
261
|
'tool.detectChanges.affectedProcesses': string;
|
|
@@ -387,6 +390,7 @@ export declare const cliResources: {
|
|
|
387
390
|
'help.option.repo.target': string;
|
|
388
391
|
'help.option.context.uid': string;
|
|
389
392
|
'help.option.context.file': string;
|
|
393
|
+
'help.option.impact.kind': string;
|
|
390
394
|
'help.option.impact.direction': string;
|
|
391
395
|
'help.option.impact.depth': string;
|
|
392
396
|
'help.option.impact.includeTests': string;
|
package/dist/cli/i18n/zh-CN.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export declare const zhCN: {
|
|
|
43
43
|
'tool.usage.context': string;
|
|
44
44
|
'tool.usage.impact': string;
|
|
45
45
|
'tool.usage.cypher': string;
|
|
46
|
+
'tool.warn.unknownKind': string;
|
|
46
47
|
'tool.detectChanges.noChanges': string;
|
|
47
48
|
'tool.detectChanges.changesSummary': string;
|
|
48
49
|
'tool.detectChanges.affectedProcesses': string;
|
|
@@ -177,6 +178,7 @@ export declare const zhCN: {
|
|
|
177
178
|
'help.option.repo.target': string;
|
|
178
179
|
'help.option.context.uid': string;
|
|
179
180
|
'help.option.context.file': string;
|
|
181
|
+
'help.option.impact.kind': string;
|
|
180
182
|
'help.option.impact.direction': string;
|
|
181
183
|
'help.option.impact.depth': string;
|
|
182
184
|
'help.option.impact.includeTests': string;
|
package/dist/cli/i18n/zh-CN.js
CHANGED
|
@@ -41,8 +41,9 @@ export const zhCN = {
|
|
|
41
41
|
'tool.noIndexed': 'GitNexus:未找到已索引仓库。请运行:gitnexus analyze',
|
|
42
42
|
'tool.usage.query': '用法:gitnexus query <搜索词>',
|
|
43
43
|
'tool.usage.context': '用法:gitnexus context <符号名> [--uid <uid>] [--file <路径>]',
|
|
44
|
-
'tool.usage.impact': '用法:gitnexus impact <符号名> [--direction upstream|downstream]',
|
|
44
|
+
'tool.usage.impact': '用法:gitnexus impact <符号名> [--uid <uid>] [--file <路径>] [--kind <类型>] [--direction upstream|downstream]',
|
|
45
45
|
'tool.usage.cypher': '用法:gitnexus cypher <Cypher 查询>',
|
|
46
|
+
'tool.warn.unknownKind': "--kind '{{kind}}' 不是已知的符号类型(如 Function、Class、Method),不会用于缩小结果范围。",
|
|
46
47
|
'tool.detectChanges.noChanges': '未检测到变更。',
|
|
47
48
|
'tool.detectChanges.changesSummary': '变更:{{files}} 个文件,{{symbols}} 个符号',
|
|
48
49
|
'tool.detectChanges.affectedProcesses': '受影响流程:{{count}}',
|
|
@@ -177,6 +178,7 @@ export const zhCN = {
|
|
|
177
178
|
'help.option.repo.target': '目标仓库',
|
|
178
179
|
'help.option.context.uid': '直接符号 UID(零歧义查找)',
|
|
179
180
|
'help.option.context.file': '用于消除常见名称歧义的文件路径',
|
|
181
|
+
'help.option.impact.kind': '用于消除常见名称歧义的类型过滤(如 Function、Class、Method)',
|
|
180
182
|
'help.option.impact.direction': 'upstream(依赖它的项)或 downstream(它依赖的项)',
|
|
181
183
|
'help.option.impact.depth': '最大关系遍历深度(默认:3)',
|
|
182
184
|
'help.option.impact.includeTests': '在结果中包含测试文件',
|
package/dist/cli/index.js
CHANGED
|
@@ -142,10 +142,13 @@ program
|
|
|
142
142
|
.option('--content', 'Include full symbol source code')
|
|
143
143
|
.action(createLbugLazyAction(() => import('./tool.js'), 'contextCommand'));
|
|
144
144
|
program
|
|
145
|
-
.command('impact
|
|
145
|
+
.command('impact [target]')
|
|
146
146
|
.description('Blast radius analysis: what breaks if you change a symbol')
|
|
147
147
|
.option('-d, --direction <dir>', 'upstream (dependants) or downstream (dependencies)', 'upstream')
|
|
148
148
|
.option('-r, --repo <name>', 'Target repository')
|
|
149
|
+
.option('-u, --uid <uid>', 'Direct symbol UID (zero-ambiguity lookup)')
|
|
150
|
+
.option('-f, --file <path>', 'File path to disambiguate common names')
|
|
151
|
+
.option('--kind <kind>', 'Kind filter to disambiguate common names (e.g. Function, Class, Method)')
|
|
149
152
|
.option('--depth <n>', 'Max relationship depth (default: 3)')
|
|
150
153
|
.option('--include-tests', 'Include test files in results')
|
|
151
154
|
.option('--limit <n>', 'Max symbols per depth level (default: 100)')
|
package/dist/cli/tool.d.ts
CHANGED
|
@@ -27,9 +27,12 @@ export declare function contextCommand(name: string, options?: {
|
|
|
27
27
|
uid?: string;
|
|
28
28
|
content?: boolean;
|
|
29
29
|
}): Promise<void>;
|
|
30
|
-
export declare function impactCommand(target
|
|
30
|
+
export declare function impactCommand(target?: string, options?: {
|
|
31
31
|
direction?: string;
|
|
32
32
|
repo?: string;
|
|
33
|
+
uid?: string;
|
|
34
|
+
file?: string;
|
|
35
|
+
kind?: string;
|
|
33
36
|
depth?: string;
|
|
34
37
|
includeTests?: boolean;
|
|
35
38
|
limit?: string;
|
package/dist/cli/tool.js
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* See the output() function for details (#324).
|
|
16
16
|
*/
|
|
17
17
|
import { writeSync } from 'node:fs';
|
|
18
|
-
import { LocalBackend } from '../mcp/local/local-backend.js';
|
|
19
|
-
import { cliErrorKey } from './cli-message.js';
|
|
18
|
+
import { LocalBackend, VALID_NODE_LABELS } from '../mcp/local/local-backend.js';
|
|
19
|
+
import { cliErrorKey, cliWarnKey } from './cli-message.js';
|
|
20
20
|
import { formatDetectChangesResult } from './detect-changes-format.js';
|
|
21
21
|
let _backend = null;
|
|
22
22
|
async function getBackend() {
|
|
@@ -72,6 +72,11 @@ export async function queryCommand(queryText, options) {
|
|
|
72
72
|
output(result);
|
|
73
73
|
}
|
|
74
74
|
export async function contextCommand(name, options) {
|
|
75
|
+
// Reject a `--`-prefixed uid swallowed from a following flag (see impactCommand).
|
|
76
|
+
if (options?.uid?.startsWith('--')) {
|
|
77
|
+
cliErrorKey('tool.usage.context');
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
75
80
|
if (!name?.trim() && !options?.uid) {
|
|
76
81
|
cliErrorKey('tool.usage.context');
|
|
77
82
|
process.exit(1);
|
|
@@ -87,10 +92,25 @@ export async function contextCommand(name, options) {
|
|
|
87
92
|
output(result);
|
|
88
93
|
}
|
|
89
94
|
export async function impactCommand(target, options) {
|
|
90
|
-
|
|
95
|
+
// A `--`-prefixed uid means Commander swallowed a following flag as the uid
|
|
96
|
+
// value (e.g. `impact --uid --file x` → uid === '--file'). Reject it rather
|
|
97
|
+
// than forwarding a garbage uid that would silently resolve to not-found.
|
|
98
|
+
if (options?.uid?.startsWith('--')) {
|
|
91
99
|
cliErrorKey('tool.usage.impact');
|
|
92
100
|
process.exit(1);
|
|
93
101
|
}
|
|
102
|
+
// Target is an optional positional: a uid alone is enough to resolve (parity
|
|
103
|
+
// with `context [name]`). Only error when neither a target nor a uid is given.
|
|
104
|
+
if (!target?.trim() && !options?.uid) {
|
|
105
|
+
cliErrorKey('tool.usage.impact');
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
// Soft-validate --kind: an unknown kind is a no-op hint (the backend scores
|
|
109
|
+
// it but it matches nothing), so warn and proceed rather than rejecting —
|
|
110
|
+
// parity with the lenient MCP surface and forward-compatible with new labels.
|
|
111
|
+
if (options?.kind && !VALID_NODE_LABELS.has(options.kind)) {
|
|
112
|
+
cliWarnKey('tool.warn.unknownKind', { kind: options.kind });
|
|
113
|
+
}
|
|
94
114
|
try {
|
|
95
115
|
const backend = await getBackend();
|
|
96
116
|
const rawLimit = parseInt(options?.limit ?? '', 10);
|
|
@@ -98,7 +118,10 @@ export async function impactCommand(target, options) {
|
|
|
98
118
|
const parsedLimit = Number.isFinite(rawLimit) ? rawLimit : undefined;
|
|
99
119
|
const parsedOffset = Number.isFinite(rawOffset) ? rawOffset : undefined;
|
|
100
120
|
const result = await backend.callTool('impact', {
|
|
101
|
-
target,
|
|
121
|
+
target: target || undefined,
|
|
122
|
+
target_uid: options?.uid,
|
|
123
|
+
file_path: options?.file,
|
|
124
|
+
kind: options?.kind,
|
|
102
125
|
direction: options?.direction || 'upstream',
|
|
103
126
|
maxDepth: options?.depth ? parseInt(options.depth, 10) : undefined,
|
|
104
127
|
includeTests: options?.includeTests ?? false,
|
|
@@ -2379,8 +2379,14 @@ export class LocalBackend {
|
|
|
2379
2379
|
const rawOffset = typeof opts.offset === 'number' && Number.isFinite(opts.offset) ? opts.offset : 0;
|
|
2380
2380
|
const paginationOffset = Math.max(0, Math.trunc(rawOffset));
|
|
2381
2381
|
const summaryOnly = opts.summaryOnly ?? false;
|
|
2382
|
-
|
|
2383
|
-
|
|
2382
|
+
// Bind the BFS frontier query's filters as parameters (#1907 review F5):
|
|
2383
|
+
// node ids and relation types as bound lists, the confidence floor as a
|
|
2384
|
+
// bound number — no string interpolation reaches the query text. Preserve
|
|
2385
|
+
// the original "no confidence clause when minConfidence <= 0" behavior: an
|
|
2386
|
+
// unconditional `>= 0` would wrongly exclude NULL-confidence edges that the
|
|
2387
|
+
// unfiltered query includes.
|
|
2388
|
+
const safeMinConfidence = Number.isFinite(minConfidence) ? minConfidence : 0;
|
|
2389
|
+
const confidenceFilter = safeMinConfidence > 0 ? ' AND r.confidence >= $minConfidence' : '';
|
|
2384
2390
|
const symId = sym.id || sym[0];
|
|
2385
2391
|
const impacted = [];
|
|
2386
2392
|
const visited = new Set([symId]);
|
|
@@ -2449,13 +2455,17 @@ export class LocalBackend {
|
|
|
2449
2455
|
}
|
|
2450
2456
|
for (let depth = 1; depth <= maxDepth && frontier.length > 0; depth++) {
|
|
2451
2457
|
const nextFrontier = [];
|
|
2452
|
-
// Batch frontier nodes into a single Cypher query per depth level
|
|
2453
|
-
|
|
2458
|
+
// Batch frontier nodes into a single Cypher query per depth level.
|
|
2459
|
+
// ids/types/confidence are bound parameters (see above) — no interpolation.
|
|
2454
2460
|
const query = direction === 'upstream'
|
|
2455
|
-
? `MATCH (caller)-[r:CodeRelation]->(n) WHERE n.id IN
|
|
2456
|
-
: `MATCH (n)-[r:CodeRelation]->(callee) WHERE n.id IN
|
|
2461
|
+
? `MATCH (caller)-[r:CodeRelation]->(n) WHERE n.id IN $frontierIds AND r.type IN $relTypes${confidenceFilter} RETURN n.id AS sourceId, caller.id AS id, caller.name AS name, labels(caller)[0] AS type, caller.filePath AS filePath, r.type AS relType, r.confidence AS confidence`
|
|
2462
|
+
: `MATCH (n)-[r:CodeRelation]->(callee) WHERE n.id IN $frontierIds AND r.type IN $relTypes${confidenceFilter} RETURN n.id AS sourceId, callee.id AS id, callee.name AS name, labels(callee)[0] AS type, callee.filePath AS filePath, r.type AS relType, r.confidence AS confidence`;
|
|
2457
2463
|
try {
|
|
2458
|
-
const related = await
|
|
2464
|
+
const related = await executeParameterized(repo.id, query, {
|
|
2465
|
+
frontierIds: frontier,
|
|
2466
|
+
relTypes: relationTypes,
|
|
2467
|
+
...(safeMinConfidence > 0 ? { minConfidence: safeMinConfidence } : {}),
|
|
2468
|
+
});
|
|
2459
2469
|
for (const rel of related) {
|
|
2460
2470
|
const relId = rel.id || rel[1];
|
|
2461
2471
|
const filePath = rel.filePath || rel[4] || '';
|
package/package.json
CHANGED