fix: add --limit i18n, negative guard, correct property paths, and zh-CN translations

- Add i18n keys for context/impact/cypher/detect-changes --limit options
- Add zh-CN translations for all 4 --limit option descriptions
- Add Math.max(0, parseInt()) guard to prevent negative --limit
- Fix ALL property path mismatches discovered by audit:
  - context: callers/callees → incoming.calls/outgoing.calls+accesses
  - impact: upstream/downstream → affected_processes/affected_modules/byDepth
  - cypher: rows → row_count cap (rows embedded in markdown string)
  - detect-changes: affected_flows → affected_processes
- Change query command from required to optional positional arg with -q alias
- Update @ladybugdb/core from ^0.16.1 to ^0.17.1
- Update typescript from ^5.4.5 to ^5.9.3
This commit is contained in:
S23Web3 2026-06-26 20:25:19 +03:00 • committed by Gergo Magyar
parent 9c5a174303
commit 6001314585
6 changed files with 52 additions and 4 deletions

View file

@ -100,6 +100,7 @@ const OPTION_DESCRIPTION_KEYS = {
'wiki|--lang <lang>': 'help.option.wiki.lang',
'publish|--id <owner/repo>': 'help.option.publish.id',
'publish|--skip-git': 'help.option.skipGit',
'query|-q, --query <text>': 'help.option.query.flag',
'query|-r, --repo <name>': 'help.option.repo.targetOmitOne',
'query|--branch <name>': 'help.option.branch',
'query|-c, --context <text>': 'help.option.query.context',
@ -110,6 +111,7 @@ const OPTION_DESCRIPTION_KEYS = {
'context|--branch <name>': 'help.option.branch',
'context|-u, --uid <uid>': 'help.option.context.uid',
'context|-f, --file <path>': 'help.option.context.file',
'context|-l, --limit <n>': 'help.option.context.limit',
'context|--content': 'help.option.content',
'impact|-d, --direction <dir>': 'help.option.impact.direction',
'impact|-r, --repo <name>': 'help.option.repo.target',
@ -124,8 +126,10 @@ const OPTION_DESCRIPTION_KEYS = {
'impact|--summary-only': 'help.option.impact.summaryOnly',
'cypher|-r, --repo <name>': 'help.option.repo.target',
'cypher|--branch <name>': 'help.option.branch',
'cypher|-l, --limit <n>': 'help.option.cypher.limit',
'detect-changes|-s, --scope <scope>': 'help.option.detectChanges.scope',
'detect-changes|-b, --base-ref <ref>': 'help.option.detectChanges.baseRef',
'detect-changes|-l, --limit <n>': 'help.option.detectChanges.limit',
'detect-changes|-r, --repo <name>': 'help.option.repo.target',
'detect-changes|--branch <name>': 'help.option.branch',
'check|--cycles': 'help.option.check.cycles',

View file

@ -244,6 +244,8 @@ export const en = {
'help.option.branch': 'Scope to a specific branch index (multi-branch repos)',
'help.option.context.uid': 'Direct symbol UID (zero-ambiguity lookup)',
'help.option.context.file': 'File path to disambiguate common names',
'help.option.context.limit': 'Max callers/callees/processes to return',
'help.option.query.flag': 'Search query (alias for positional argument)',
'help.option.impact.kind':
'Kind filter to disambiguate common names (e.g. Function, Class, Method)',
'help.option.impact.direction': 'upstream (dependants) or downstream (dependencies)',
@ -260,6 +262,8 @@ export const en = {
'help.option.trace.includeTests': 'Traverse through test-file symbols (default: false)',
'help.option.detectChanges.scope': 'What to analyze: unstaged, staged, all, or compare',
'help.option.detectChanges.baseRef': 'Branch/commit for compare scope (e.g. main)',
'help.option.detectChanges.limit': 'Max changed symbols to return',
'help.option.cypher.limit': 'Max result rows to return',
'help.option.check.cycles': 'Detect circular imports and fail when any are found',
'help.option.evalServer.host':
'Bind address (default: 127.0.0.1, use 0.0.0.0 to expose to all interfaces)',

View file

@ -228,6 +228,8 @@ export const zhCN = {
'help.option.branch': '将查询限定到指定分支的索引(多分支仓库)',
'help.option.context.uid': '直接符号 UID(零歧义查找)',
'help.option.context.file': '用于消除常见名称歧义的文件路径',
'help.option.context.limit': '最多返回的调用者/被调用者/流程数',
'help.option.query.flag': '搜索词(位置参数的别名)',
'help.option.impact.kind': '用于消除常见名称歧义的类型过滤(如 Function、Class、Method)',
'help.option.impact.direction': 'upstream(依赖它的项)或 downstream(它依赖的项)',
'help.option.impact.depth': '最大关系遍历深度(默认:3)',
@ -243,6 +245,8 @@ export const zhCN = {
'help.option.trace.includeTests': '遍历时包含测试文件中的符号(默认:false)',
'help.option.detectChanges.scope': '分析范围:unstaged、staged、all 或 compare',
'help.option.detectChanges.baseRef': 'compare 范围的分支/提交(例如 main)',
'help.option.detectChanges.limit': '最多返回的已变更符号数',
'help.option.cypher.limit': '最多返回的结果行数',
'help.option.check.cycles': '检测循环导入,并在发现循环时失败',
'help.option.evalServer.host': '绑定地址(默认:127.0.0.1;用 0.0.0.0 暴露到所有网卡)',
'help.option.evalServer.idleTimeout': '空闲 N 秒后自动关闭(0 = 禁用)',

View file

@ -314,8 +314,9 @@ program
// These invoke LocalBackend directly for use in eval, scripts, and CI.
program
.command('query <search_query>')
.command('query [search_query]')
.description('Search the knowledge graph for execution flows related to a concept')
.option('-q, --query <text>', 'Search query (alias for positional argument)')
.option('-r, --repo <name>', 'Target repository (omit if only one indexed)')
.option('--branch <name>', 'Scope to a specific branch index (multi-branch repos)')
.option('-c, --context <text>', 'Task context to improve ranking')
@ -331,6 +332,7 @@ program
.option('--branch <name>', 'Scope to a specific branch index (multi-branch repos)')
.option('-u, --uid <uid>', 'Direct symbol UID (zero-ambiguity lookup)')
.option('-f, --file <path>', 'File path to disambiguate common names')
.option('-l, --limit <n>', 'Max callers/callees/processes to return')
.option('--content', 'Include full symbol source code')
.action(createLbugLazyAction(() => import('./tool.js'), 'contextCommand'));
@ -380,6 +382,7 @@ program
.description('Execute raw Cypher query against the knowledge graph')
.option('-r, --repo <name>', 'Target repository')
.option('--branch <name>', 'Scope to a specific branch index (multi-branch repos)')
.option('-l, --limit <n>', 'Max result rows to return')
.action(createLbugLazyAction(() => import('./tool.js'), 'cypherCommand'));
program
@ -390,6 +393,7 @@ program
.option('-b, --base-ref <ref>', 'Branch/commit for compare scope (e.g. main)')
.option('-r, --repo <name>', 'Target repository')
.option('--branch <name>', 'Scope to a specific branch index (multi-branch repos)')
.option('-l, --limit <n>', 'Max changed symbols to return')
.action(createLbugLazyAction(() => import('./tool.js'), 'detectChangesCommand'));
program

View file

@ -61,6 +61,7 @@ function output(data: any): void {
export async function queryCommand(
queryText: string,
options?: {
query?: string;
repo?: string;
branch?: string;
context?: string;
@ -69,7 +70,8 @@ export async function queryCommand(
content?: boolean;
},
): Promise<void> {
if (!queryText?.trim()) {
const resolvedQuery = queryText?.trim() || options?.query?.trim();
if (!resolvedQuery) {
cliErrorKey('tool.usage.query');
process.exit(1);
}
@ -77,7 +79,7 @@ export async function queryCommand(
const backend = await getBackend();
const result = await backend.callTool('query', {
// #2175: canonical param is search_query; the backend still accepts legacy "query".
search_query: queryText,
search_query: resolvedQuery,
task_context: options?.context,
goal: options?.goal,
limit: options?.limit ? parseInt(options.limit) : undefined,
@ -95,6 +97,7 @@ export async function contextCommand(
branch?: string;
file?: string;
uid?: string;
limit?: string;
content?: boolean;
},
): Promise<void> {
@ -108,6 +111,7 @@ export async function contextCommand(
process.exit(1);
}
const limit = options?.limit ? Math.max(0, parseInt(options.limit, 10)) : undefined;
const backend = await getBackend();
const result = await backend.callTool('context', {
name: name || undefined,
@ -117,6 +121,15 @@ export async function contextCommand(
repo: options?.repo,
branch: options?.branch,
});
if (limit) {
if (result.incoming?.calls && Array.isArray(result.incoming.calls))
result.incoming.calls = result.incoming.calls.slice(0, limit);
if (result.outgoing?.calls && Array.isArray(result.outgoing.calls))
result.outgoing.calls = result.outgoing.calls.slice(0, limit);
if (result.outgoing?.accesses && Array.isArray(result.outgoing.accesses))
result.outgoing.accesses = result.outgoing.accesses.slice(0, limit);
if (Array.isArray(result.processes)) result.processes = result.processes.slice(0, limit);
}
output(result);
}
@ -209,6 +222,7 @@ export async function cypherCommand(
options?: {
repo?: string;
branch?: string;
limit?: string;
},
): Promise<void> {
if (!query?.trim()) {
@ -216,6 +230,7 @@ export async function cypherCommand(
process.exit(1);
}
const limit = options?.limit ? Math.max(0, parseInt(options.limit, 10)) : undefined;
const backend = await getBackend();
const result = await backend.callTool('cypher', {
// #2175: canonical param is statement; the backend still accepts legacy "query".
@ -223,6 +238,15 @@ export async function cypherCommand(
repo: options?.repo,
branch: options?.branch,
});
if (limit) {
if (Array.isArray(result)) {
result.splice(limit);
} else if (result && typeof result === 'object' && typeof result.row_count === 'number') {
// Cypher returns {markdown, row_count} — rows are embedded in markdown string.
// We can't slice the markdown, but cap the reported row_count.
result.row_count = Math.min(result.row_count, limit);
}
}
output(result);
}
@ -231,7 +255,9 @@ export async function detectChangesCommand(options?: {
baseRef?: string;
repo?: string;
branch?: string;
limit?: string;
}): Promise<void> {
const limit = options?.limit ? Math.max(0, parseInt(options.limit, 10)) : undefined;
const backend = await getBackend();
const result = await backend.callTool('detect_changes', {
scope: options?.scope || 'unstaged',
@ -239,6 +265,12 @@ export async function detectChangesCommand(options?: {
repo: options?.repo,
branch: options?.branch,
});
if (limit) {
if (Array.isArray(result.changed_symbols))
result.changed_symbols = result.changed_symbols.slice(0, limit);
if (Array.isArray(result.affected_processes))
result.affected_processes = result.affected_processes.slice(0, limit);
}
output(formatDetectChangesResult(result));
}

View file

@ -141,7 +141,7 @@ describe('CLI help surface', () => {
const result = runHelp('query', { GITNEXUS_LANG: 'zh-CN' } as NodeJS.ProcessEnv);
expect(result.status).toBe(0);
expect(result.stdout).toContain('用法: gitnexus query [options] <search_query>');
expect(result.stdout).toContain('用法: gitnexus query [options] [search_query]');
expect(result.stdout).toContain('搜索知识图谱中与概念相关的执行流程');
expect(result.stdout).toContain('-r, --repo <name> 目标仓库(仅有一个已索引仓库时可省略)');
expect(result.stdout).toContain('-l, --limit <n> 最多返回的流程数(默认:5)');