Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,7 @@ access.
|---|---|
| Human-readable default | Query commands (`search`, `definition`, `references`, `callers`, `callees`, `symbols`, `files`, `excerpt`, `map`, `inspect`, `outline`, `suggestions`) default to **human-readable output**. |
| `--json` | Emits JSON lines output, one JSON object per line, designed for easy parsing by AI agents. |
| Delegated audit command identity | `audit` delegates recipe execution to `search` internally while retaining the public `audit` identity in human usage, recovery hints, and generated replay commands. Explicit `audit --json` usage errors emit stable versioned command-error objects with `command: "audit"` and no human-readable `usage`; direct `search` errors retain `search` identity. |
| `definition --json` miss | A default-format definition lookup that finds no matching symbol emits the shared versioned `E018_QUERY_NOT_FOUND` command-error object and exits `2`, with or without `--body`; it never succeeds with empty stdout. Bounded-envelope controls move the object to `metadata.error` and keep `results` empty instead of projecting it as a location row. The object is preflighted against `--max-json-bytes`; an impossible cap returns a usage error without oversized stdout. `--count` still returns its structured zero-count object, and explicit location formats retain their existing format-specific empty-result output. |
| Raw discovery JSON shape | `symbols` and `files` build each result row through the same DTO path for array, NDJSON, and envelope output. `symbols --json=array` therefore preserves `exact_index_available` just like NDJSON. Every cardinality and `--max-json-bytes` path keeps the selected flat shape: zero-result NDJSON is an empty stream, `--json=array` is always an array, and byte-capped output omits whole trailing rows without changing the top-level type. Bounded projections keep rows in `results`, pagination facts in `metadata`, and exact-query readiness in `metadata.response_context`; they never reuse a result row as response context. Use `--format compact` or `--json-envelope` when truncation and freshness metadata must accompany the results. |
| Generated-code filtering metadata | DB-backed discovery `query_context` always reports `include_generated`, `generated_code_policy`, and `generated_file_filter_available`. The `files --count --json` and every JSON `map` summary (including `issue-drafts`) also report `generated_file_count_excluded` and `generated_file_count_excluded_authoritative`. The excluded count is `0` when generated files are included. For a legacy DB without `files.generated` when filtering is requested, the policy is `unavailable`, the count is `null`, and the authoritative/available flags are `false` rather than claiming that an unavailable filter ran; explicit `--include-generated` remains `include` with an authoritative excluded count of `0`. Byte-capped and uncapped raw discovery arrays retain SQLite trust diagnostics even when the query returns no result rows. |
Expand Down Expand Up @@ -5043,6 +5044,7 @@ help はすべてこのレジストリを参照します。field 名は大文字
|---|---|
| human-readable default | query command(`search`、`definition`、`references`、`callers`、`callees`、`symbols`、`files`、`excerpt`、`map`、`inspect`、`outline`、`suggestions`)は既定で**人間向け出力**です。 |
| `--json` | JSON lines output(1 行 1 JSON object)に切り替えます。AI agent が容易に parse できるよう設計されています。 |
| 委譲された audit command identity | `audit` は内部で recipe 実行を `search` へ委譲しますが、人間向け usage、復旧 hint、生成する replay command では公開された `audit` identity を維持します。明示的な `audit --json` の usage error は `command: "audit"` を持つ安定した version 付き command-error object を出力し、人間向けの `usage` を含めません。直接の `search` error は `search` identity を維持します。 |
| `definition --json` の未検出 | 既定 format の definition lookup で一致する symbol がない場合、`--body` の有無にかかわらず、共通の versioned `E018_QUERY_NOT_FOUND` command-error object を出力して終了コード `2` を返します。空の stdout のまま成功することはありません。bounded-envelope control の使用時は object を location row として projection せず `metadata.error` に移し、`results` は空のままにします。この object は `--max-json-bytes` に対して事前検査され、収まらない上限では oversized stdout を出さず usage error を返します。`--count` は引き続き構造化された 0 件 object を返し、明示的な location format も既存の format 固有の empty-result output を維持します。 |
| raw discovery JSON shape | `symbols` と `files` は、array、NDJSON、envelope の各出力で同じ DTO 経路から result row を構築します。そのため `symbols --json=array` も NDJSON と同様に `exact_index_available` を保持します。結果件数や `--max-json-bytes` の有無にかかわらず選択した flat shape を維持し、0 件の NDJSON は空 stream、`--json=array` は常に array となり、byte cap 到達時は top-level type を変えずに末尾の完全な row を省略します。bounded projection は row を `results`、pagination fact を `metadata`、exact-query readiness を `metadata.response_context` に保持し、result row を response context として再利用しません。truncation / freshness metadata も結果と一緒に必要な場合は `--format compact` または `--json-envelope` を使用します。 |
| generated-code filtering metadata | DB-backed discovery の `query_context` は常に `include_generated`、`generated_code_policy`、`generated_file_filter_available` を返します。`files --count --json` と `issue-drafts` を含むすべての JSON `map` summary は、`generated_file_count_excluded` と `generated_file_count_excluded_authoritative` も返します。generated file を含める場合、除外数は `0` です。`files.generated` が無い legacy DB で filter が要求された場合、未実行の filter を実行済みと誤認させないよう、policy は `unavailable`、count は `null`、authoritative / available flag は `false` になります。明示的な `--include-generated` は `include` のままで、authoritative な除外数 `0` を返します。byte cap の有無にかかわらず、raw discovery array は query result row が 0 件でも SQLite trust diagnostics を維持します。 |
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ The default NDJSON output of `search`, `symbols`, and `files` always ends with a

Recipe/audit compact pagination returns an opaque `next_cursor`. Replay it unchanged with the same child query and filters as `--cursor <next_cursor>`; a search-score cursor can begin with `-`, and that leading minus is part of the cursor value.

`audit` delegates recipe execution to the search engine but retains its public command name in usage errors, recovery hints, and generated replay commands. With an explicit `--json`, audit usage errors return versioned command-error objects with `command: "audit"` and omit human-readable `usage`; direct `search` diagnostics retain the `search` identity.

Ad-hoc `search --format issue-drafts` computes `source_total_count`, `returned_count`, `omitted_count`, and `truncated` from the complete filtered population before applying `--first-per-file`, deterministic `--sample`, and the effective `--limit` / `--total-limit`. Guarded searches retain their candidate safety cap instead of attempting an unbounded count; they omit `source_total_count` and expose `source_minimum_count`, `source_total_count_authoritative: false`, and `source_fetch_limit`. Source metadata also records selector values, and the shell-safe replay command preserves every result-changing selector so rerunning it reproduces the same selected evidence.

When the byte cap omits rows, these commands return partial-result exit code `11`; pass `--allow-partial` to opt into exit code `0` while retaining the same terminal metadata. Ordinary `--limit` truncation remains a successful, explicitly described stream. Array and compact outputs keep their documented whole-response behavior; check `cdidx <command> --help` before relying on partial output.
Expand Down Expand Up @@ -536,6 +538,8 @@ JSON 形式では `--max-json-bytes` を文書全体の UTF-8 byte cap として

recipe / audit の compact pagination は opaque な `next_cursor` を返します。同じ child query と filter を指定し、値を変更せず `--cursor <next_cursor>` として再利用してください。search-score cursor は `-` で始まる場合があり、その先頭のマイナス記号も cursor 値の一部です。

`audit` は内部で recipe 実行を search engine へ委譲しますが、usage error、復旧 hint、生成する replay command では公開 command 名を維持します。明示的な `--json` では、audit の usage error は `command: "audit"` を持つ version 付き command-error object を返し、人間向けの `usage` を含めません。直接の `search` diagnostic は `search` identity を維持します。

ad-hoc の `search --format issue-drafts` は、filter 済み母集団全体から `source_total_count`、`returned_count`、`omitted_count`、`truncated` を算出してから、`--first-per-file`、決定的な `--sample`、有効な `--limit` / `--total-limit` を適用します。guard 付き検索は非上限 count を試みず candidate safety cap を維持し、`source_total_count` を省略して `source_minimum_count`、`source_total_count_authoritative: false`、`source_fetch_limit` を公開します。source metadata は selector 値も保持し、shell-safe な replay command は結果を変えるすべての selector を維持するため、再実行時に同じ evidence 集合を再現できます。

byte cap により行を省略した場合、これらのコマンドは partial-result 終了コード `11` を返します。同じ終端 metadata を維持したまま終了コード `0` を明示的に許容するには `--allow-partial` を指定します。通常の `--limit` による切り詰めは、理由が明示された成功 stream のままです。array / compact 出力は文書化済みの whole-response 挙動を維持します。部分出力へ依存する前に `cdidx <command> --help` を確認してください。
Expand Down
4 changes: 4 additions & 0 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,8 @@ Use `docs/test-doc-maintenance-plan.md` before moving oversized suites or adding
- `QueryCommandRunnerTests.RunSearch_UnknownRecipeQuerySuggestsOnlyFromActiveRecipe_Issue4862`, `RunSearch_RecipeQuerySuggestionHandlesZeroOneAndManyQueries_Issue4862`, and `RunSearch_ExternalRecipeAliasesResolveAndSuggestCanonicalActiveQueries_Issue4862`
keep human/JSON usage errors, canonical names, current/deprecated aliases, deterministic typo correction, active-recipe isolation, normalized filter preservation, and shell-safe replay quoting in one recipe-selection contract. Suggestions and replay selectors must remain canonical names from the active recipe even when an alias is the nearest match.
Include/exclude typo recovery must preserve selector semantics, unresolved repeated selectors, and raw FTS mode. Missing close matches and recipe-name corrections with child selectors must not offer a replay that broadens the selection, while aliases that collide with canonical names or multiple query owners must be removed with bounded diagnostics.
- `QueryCommandRunnerTests.RunAudit_UsageErrorsRetainPublicCommandContext_Issue4875` and `RunAudit_RecipeReplayCommandsRetainPublicCommandContext_Issue4875`
keep audit-alias parse, validation, selection, and conflict errors on `audit` usage; require explicit `--json` failures to expose `command: "audit"` without human-readable usage; preserve direct `search` controls; and keep issue-draft and compact replay commands on the public positional audit syntax.
- `QueryCommandRunnerTests.RunReferences_ExactJson_StylesheetAndSqlFixturesShareIndexedWorkspace`
keeps SCSS variable/mixin/extend references beside SQL multiline MERGE hints, non-ASCII identifiers, quoted-string masking, and temporary-table body boundaries in one multi-file workspace with one CLI index. Use language-specific queries and unique per-file sentinels so count, kind, and line assertions remain independently diagnostic.
- `QueryCommandRunnerTests.RunReferences_ExactJson_CSharpQueryBoundariesAndVisualBasicQuerySyntaxShareIndexedWorkspace`
Expand Down Expand Up @@ -1441,6 +1443,8 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
- `QueryCommandRunnerTests.RunSearch_UnknownRecipeQuerySuggestsOnlyFromActiveRecipe_Issue4862`、`RunSearch_RecipeQuerySuggestionHandlesZeroOneAndManyQueries_Issue4862`、`RunSearch_ExternalRecipeAliasesResolveAndSuggestCanonicalActiveQueries_Issue4862`
は human / JSON usage error、canonical 名、現行 / deprecated alias、決定的な typo correction、active recipe への限定、正規化済み filter の保持、shell-safe な replay quoting を1つの recipe-selection 契約として維持します。alias が最も近い候補でも、suggestion と replay selector は active recipe の canonical 名でなければなりません。
include / exclude の typo recovery は selector の意味、未解決の繰り返し selector、raw FTS mode を保持します。近い候補がない場合、および child selector を伴う recipe 名訂正では selection を広げる replay を提示せず、canonical 名または複数の query owner と衝突する alias は上限付き diagnostic とともに除去しなければなりません。
- `QueryCommandRunnerTests.RunAudit_UsageErrorsRetainPublicCommandContext_Issue4875` と `RunAudit_RecipeReplayCommandsRetainPublicCommandContext_Issue4875`
は audit alias の parse、validation、selection、conflict error が `audit` usage を維持すること、明示的な `--json` の失敗が人間向け usage を含めずに `command: "audit"` を公開すること、直接の `search` control を維持すること、issue-draft と compact の replay command が公開されている audit の positional syntax を使うことを検証します。
- `QueryCommandRunnerTests.RunReferences_ExactJson_StylesheetAndSqlFixturesShareIndexedWorkspace`
は SCSS の variable / mixin / extend 参照と、SQL の multiline MERGE hint、非 ASCII identifier、quoted-string masking、temporary-table body boundary を、CLI index 1回の multi-file workspace で共有します。言語別 query と file ごとの固有 sentinel を使い、件数・kind・line assertion の診断性を独立に保ってください。
- `QueryCommandRunnerTests.RunReferences_ExactJson_CSharpQueryBoundariesAndVisualBasicQuerySyntaxShareIndexedWorkspace`
Expand Down
26 changes: 26 additions & 0 deletions changelog.d/unreleased/4875.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
category: fixed
issues:
- 4875
affected:
- src/CodeIndex/Cli/QueryCommandRunner.cs
- src/CodeIndex/Cli/QueryCommandRunner.SearchAliases.cs
- src/CodeIndex/Cli/QueryCommandRunner.Search.cs
- src/CodeIndex/Cli/QueryCommandRunner.OptionValidation.cs
- src/CodeIndex/Cli/QueryCommandRunner.Validation.cs
- src/CodeIndex/Cli/QueryCommandRunner.SearchRecipes.cs
- src/CodeIndex/Cli/QueryCommandRunner.Ndjson.cs
- src/CodeIndex/Cli/CommandErrorWriter.cs
- tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs
- README.md
- DEVELOPER_GUIDE.md
- TESTING_GUIDE.md
---

## English

- **Audit alias diagnostics now retain the public command identity (#4875)** — delegated parse, validation, recipe-selection, and conflict errors use `audit` usage and recovery hints, explicit JSON failures expose `command: "audit"` without human usage text, and generated replay commands keep the public positional audit syntax while direct search behavior remains unchanged.

## 日本語

- **Audit alias の診断が公開 command identity を維持するようになりました (#4875)** — 委譲された parse、validation、recipe selection、conflict error は `audit` の usage と復旧 hint を使い、明示的な JSON failure は人間向け usage text を含めずに `command: "audit"` を公開します。生成する replay command は公開された audit の positional syntax を維持し、直接の search の挙動は変わりません。
76 changes: 54 additions & 22 deletions src/CodeIndex/Cli/CommandErrorWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,31 +49,23 @@ internal static int WriteJsonOrHuman(
string? category = null,
string? command = null,
string? path = null,
JsonObject? additionalJsonProperties = null)
JsonObject? additionalJsonProperties = null,
bool omitNullUsage = false)
{
if (json)
{
var (resolvedErrorCode, resolvedCategory) = ResolveMachineContract(exitCode, errorCode, category);
var payload = JsonSerializer.SerializeToNode(
new CommandErrorJsonResult(
"error",
message,
hint ?? DefaultHint,
resolvedErrorCode,
path,
resolvedCategory,
command,
exitCode,
usage),
CliJsonSerializerContextFactory.Create(jsonOptions).CommandErrorJsonResult)!.AsObject();
if (additionalJsonProperties != null)
{
foreach (var property in additionalJsonProperties)
{
if (!payload.ContainsKey(property.Key))
payload[property.Key] = property.Value?.DeepClone();
}
}
var payload = BuildJsonPayload(
jsonOptions,
message,
exitCode,
hint,
usage,
errorCode,
category,
command,
path,
additionalJsonProperties,
omitNullUsage);

WriteStdout(payload.ToJsonString(jsonOptions));
return exitCode;
Expand All @@ -83,6 +75,46 @@ internal static int WriteJsonOrHuman(
return exitCode;
}

internal static JsonObject BuildJsonPayload(
JsonSerializerOptions jsonOptions,
string message,
int exitCode,
string? hint = null,
string? usage = null,
string? errorCode = null,
string? category = null,
string? command = null,
string? path = null,
JsonObject? additionalJsonProperties = null,
bool omitNullUsage = false)
{
var (resolvedErrorCode, resolvedCategory) = ResolveMachineContract(exitCode, errorCode, category);
var payload = JsonSerializer.SerializeToNode(
new CommandErrorJsonResult(
"error",
message,
hint ?? DefaultHint,
resolvedErrorCode,
path,
resolvedCategory,
command,
exitCode,
usage),
CliJsonSerializerContextFactory.Create(jsonOptions).CommandErrorJsonResult)!.AsObject();
if (omitNullUsage && usage == null)
payload.Remove("usage");
if (additionalJsonProperties != null)
{
foreach (var property in additionalJsonProperties)
{
if (!payload.ContainsKey(property.Key))
payload[property.Key] = property.Value?.DeepClone();
}
}

return payload;
}

internal static (string ErrorCode, string Category) ResolveMachineContract(
int exitCode,
string? errorCode = null,
Expand Down
Loading
Loading