From 266410826f31ed5c1f9e643e11adb736eee2a021 Mon Sep 17 00:00:00 2001 From: kuishou68 Date: Fri, 2 Oct 2026 10:17:55 +0800 Subject: [PATCH] feat: allow omitting declaration locations --- apps/cli/README.md | 3 ++- apps/cli/src/args.ts | 4 ++++ apps/cli/src/index.ts | 4 +++- apps/cli/src/render.ts | 31 +++++++++++++++++-------------- apps/cli/test/args.test.ts | 8 ++++++++ apps/cli/test/render.test.ts | 7 +++++++ 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/apps/cli/README.md b/apps/cli/README.md index db73144..8ed1e22 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -94,7 +94,8 @@ Rerunning after the connection recovers retries failed work while reusing valid answers; `--no-cache` additionally bypasses those valid cached answers. The summary comes first, followed by file and declaration locations and selected -source. Locations are reading leads, not a checklist. Omitted excerpts are marked; +source. Use `--no-locations` when the trailing declaration section is not useful to +your agent. Locations are reading leads, not a checklist. Omitted excerpts are marked; `--max-source-bytes 0` includes all selected source. An incomplete result can still be useful. Read what it supplies, then fill specific gaps with ordinary tools. Application output goes to stdout; `jg` does not create a report file. diff --git a/apps/cli/src/args.ts b/apps/cli/src/args.ts index ee4700a..96ee4fb 100644 --- a/apps/cli/src/args.ts +++ b/apps/cli/src/args.ts @@ -21,6 +21,7 @@ export type Command = maxRequests: number; maxSourceBytes: number; maxOutputBytes: number; + noLocations: boolean; policy: NonNullable; }; @@ -46,6 +47,7 @@ export function parseCommand(args: string[]): Command { "max-requests": { type: "string" }, "max-source-bytes": { type: "string" }, "max-output-bytes": { type: "string" }, + "no-locations": { type: "boolean" }, hidden: { type: "boolean" }, "no-ignore": { type: "boolean" }, "include-dependencies": { type: "boolean" }, @@ -167,6 +169,7 @@ export function parseCommand(args: string[]): Command { maxRequests, maxSourceBytes, maxOutputBytes, + noLocations: values["no-locations"] ?? false, policy, }; } @@ -234,6 +237,7 @@ Search options: --max-requests N Provider request ceiling (default: 1000) --max-source-bytes N Source allocation; 0 means unlimited (default: ${DEFAULT_MAX_SOURCE_BYTES}) --max-output-bytes N Total stdout cap; 0 means unlimited (default: ${DEFAULT_MAX_OUTPUT_BYTES}) + --no-locations Omit the trailing declaration locations section --hidden Include hidden paths --no-ignore Disable .gitignore/.ignore patterns --include-dependencies Include dependency and build directories diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index 3ca6bf7..feda9e9 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -132,7 +132,9 @@ async function main() { evaluator, ); if (pipeClosed) return; - await write(renderResult(result, command.maxSourceBytes, command.maxOutputBytes)); + await write( + renderResult(result, command.maxSourceBytes, command.maxOutputBytes, !command.noLocations), + ); process.exitCode = result.status === "interrupted" ? 130 : result.status === "incomplete" ? 2 : 0; } diff --git a/apps/cli/src/render.ts b/apps/cli/src/render.ts index e0f9727..6a42664 100644 --- a/apps/cli/src/render.ts +++ b/apps/cli/src/render.ts @@ -16,6 +16,7 @@ export function renderResult( result: RetrievalResult, maxSourceBytes = DEFAULT_MAX_SOURCE_BYTES, maxOutputBytes = DEFAULT_MAX_OUTPUT_BYTES, + includeLocations = true, ): string { let remaining = maxSourceBytes || Infinity; const files = [...result.files] @@ -60,7 +61,7 @@ export function renderResult( ...files.flatMap(({ file, excerpts, omitted }) => [ `- ${quote(file.path)} — ${file.roles.join(", ") || "relevant; role uncertain"}; ${excerpts.length ? "source below" : omitted ? "source omitted" : "locations only"}`, ]), - "End file list. Declaration locations follow source.", + includeLocations ? "End file list. Declaration locations follow source." : "End file list.", ]; for (const { file, excerpts } of files) for (const { range, source, partial, sourceByteStart, sourceByteEnd } of excerpts) { @@ -87,19 +88,21 @@ export function renderResult( for (const line of sourceLines) lines.push(line); lines.push(fence); } - lines.push("", "Declaration locations:"); - for (const { file, omitted } of files) { - lines.push( - `- ${quote(file.path)}`, - ...[...file.leads] - .sort((a, b) => a.range.startLine - b.range.startLine) - .map((lead) => ` ${lead.name}@${lead.range.startLine}-${lead.range.endLine}`), - ...(file.callLeads ?? []).map( - (call) => - ` Possible local call ${call.caller} -> ${call.name}: lines ${call.range.startLine}-${call.range.endLine}${call.unknownEarlierBases.length ? `; earlier base(s) ${call.unknownEarlierBases.map(quote).join(", ")} not inspected` : ""}; runtime dispatch not verified.`, - ), - ...(omitted ? [" Some source omitted; locations remain available."] : []), - ); + if (includeLocations) { + lines.push("", "Declaration locations:"); + for (const { file, omitted } of files) { + lines.push( + `- ${quote(file.path)}`, + ...[...file.leads] + .sort((a, b) => a.range.startLine - b.range.startLine) + .map((lead) => ` ${lead.name}@${lead.range.startLine}-${lead.range.endLine}`), + ...(file.callLeads ?? []).map( + (call) => + ` Possible local call ${call.caller} -> ${call.name}: lines ${call.range.startLine}-${call.range.endLine}${call.unknownEarlierBases.length ? `; earlier base(s) ${call.unknownEarlierBases.map(quote).join(", ")} not inspected` : ""}; runtime dispatch not verified.`, + ), + ...(omitted ? [" Some source omitted; locations remain available."] : []), + ); + } } const output = lines.join("\n") + "\n\nEnd context.\n"; const outputBytes = Buffer.byteLength(output); diff --git a/apps/cli/test/args.test.ts b/apps/cli/test/args.test.ts index 8ffabf8..74d0c9f 100644 --- a/apps/cli/test/args.test.ts +++ b/apps/cli/test/args.test.ts @@ -1,6 +1,14 @@ import { expect, test } from "bun:test"; import { parseCommand } from "../src/args"; +test("search can omit declaration locations without changing policy", () => { + expect(parseCommand(["question", "--no-locations"])).toMatchObject({ + kind: "search", + noLocations: true, + policy: {}, + }); +}); + test("search accepts independent policy flags and a dash-prefixed root after --", () => { expect( parseCommand([ diff --git a/apps/cli/test/render.test.ts b/apps/cli/test/render.test.ts index 1f442fc..e4169cf 100644 --- a/apps/cli/test/render.test.ts +++ b/apps/cli/test/render.test.ts @@ -45,6 +45,13 @@ test("source precedes detailed locations and remains verbatim", () => { expect(output.endsWith("\nEnd context.\n")).toBe(true); }); +test("declaration locations can be omitted while source remains available", () => { + const output = renderResult(result(), 0, 0, false); + expect(output).toContain('Source block "one.py"'); + expect(output).toContain("End file list.\n"); + expect(output).not.toContain("Declaration locations:"); +}); + test("explicit byte limits preserve all locations and mark omissions without clipping UTF-8", () => { const value = result(); value.files.push({