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
11 changes: 9 additions & 2 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ Interactive terminal controls are allowed only when stdout is not redirected or

Query commands that accept path filters (`search`, `definition`, `references`, `callers`, `callees`, `symbols`, `files`, `find`, `map`, `inspect`, `deps`, `impact`, `unused`, `hotspots`, and `validate`) expand `--project` into the matching project directory glob before hitting `DbReader`, so all existing SQL path predicates keep working. `index --project` expands to the files under the selected project directory and reuses the existing `--files` update path, but rejects expansions above 65,536 files for one project or 131,072 unique files across all requested projects with an explicit-files recovery hint.

`cdidx batch` is a CLI-side query loop for editor integrations and scripts that need several query commands against the same DB without spawning `cdidx` repeatedly. It opens one `DbContext` / `DbReader`, reads newline-delimited JSON string arrays from stdin, and dispatches only query commands through the existing `QueryCommandRunner` paths so output and validation stay identical to the standalone command shape.
`cdidx batch` is a CLI-side query loop for editor integrations and scripts that need several query commands against the same DB without spawning `cdidx` repeatedly. It opens one `DbContext` / `DbReader`, reads newline-delimited JSON string arrays from stdin, caps each decoded string argument at 8,192 characters, and dispatches only query commands through the existing `QueryCommandRunner` paths so output and validation stay identical to the standalone command shape.

Editor integrations can request standard location shapes directly. `definition`, `references`, `search`, `find`, and `validate` accept `--format <text|json|lsp|qf|sarif>`; `lsp` emits LSP `Location` arrays, `qf` emits Vim quickfix lines, and `sarif` emits SARIF 2.1.0. `goto <symbol>` returns the single unambiguous definition as one LSP `Location`, while `goto --all <symbol>` returns all matching locations.

Expand Down Expand Up @@ -779,6 +779,11 @@ WHERE fts_chunks MATCH 'content:authenticate'

### How the search works

Literal-safe `search` queries are bounded in the reader before FTS5
sanitization: maximum 1000 characters and 128 whitespace terms. Keep this guard
in `DbReader` so CLI, MCP, and direct reader callers share the same failure mode;
raw `--fts` queries continue to use the raw FTS complexity limits instead.

When you run:
```sql
SELECT f.path, c.start_line, c.content
Expand Down Expand Up @@ -2355,7 +2360,7 @@ override が文書化されていない限り ANSI/progress control を抑止す

path filter を受け付ける query コマンド(`search`, `definition`, `references`, `callers`, `callees`, `symbols`, `files`, `find`, `map`, `inspect`, `deps`, `impact`, `unused`, `hotspots`, `validate`)は、`--project` を対応する project directory glob に展開してから `DbReader` に渡す。これにより既存の SQL path predicate をそのまま利用できる。`index --project` は選択された project directory 配下のファイルに展開し、既存の `--files` 更新経路を再利用する。ただし 1 project で 65,536 files、requested projects 全体で 131,072 unique files を超える展開は拒否し、明示的な `--files` を使う recovery hint を返す。

`cdidx batch` は、同じ DB に複数の query command を投げる editor integration や script 向けの CLI 側 query loop である。1 つの `DbContext` / `DbReader` を開き、stdin から newline-delimited JSON 文字列配列を読み、query command だけを既存の `QueryCommandRunner` 経路へ dispatch するため、出力と validation は単発コマンドと同じ形を保つ。
`cdidx batch` は、同じ DB に複数の query command を投げる editor integration や script 向けの CLI 側 query loop である。1 つの `DbContext` / `DbReader` を開き、stdin から newline-delimited JSON 文字列配列を読み、デコード後の各文字列引数を 8,192 文字に制限し、query command だけを既存の `QueryCommandRunner` 経路へ dispatch するため、出力と validation は単発コマンドと同じ形を保つ。

editor integration は標準的な location 形状を直接要求できる。`definition`、`references`、`search`、`find`、`validate` は `--format <text|json|lsp|qf|sarif>` を受け付け、`lsp` は LSP `Location` 配列、`qf` は Vim quickfix 行、`sarif` は SARIF 2.1.0 を出力する。`goto <symbol>` は曖昧でない単一定義を 1 つの LSP `Location` として返し、`goto --all <symbol>` は一致する全 location を返す。

Expand Down Expand Up @@ -3011,6 +3016,8 @@ exact-match flag の互換性は [USER_GUIDE.md](USER_GUIDE.md#フラグ互換

`search`、`definition`、`references`、`callers`、`callees`、`symbols`、`files` は `--path`、繰り返し指定できる `--exclude-path`、`--exclude-tests` による絞り込みを共有します。読み取り層は tests や docs より source を優先し、`search` はシンボル名やパスがクエリと正確に一致する候補をさらに上位に出して、AIクライアントが実装ファイルへ早く到達できるようにします。

literal-safe な `search` query は reader 層で FTS5 sanitization 前に 1000 文字、128 whitespace term へ制限します。CLI、MCP、直接 reader caller の failure mode を揃えるため、この guard は `DbReader` に置きます。raw `--fts` query は別途 raw FTS complexity limit を使います。

`search --json` と MCP の `search` は、フルチャンクを `chunk_start_line`、`chunk_end_line`、`snippet_start_line`、`snippet_end_line`、`snippet`、`match_lines`、`highlights`、`context_before`、`context_after`、`truncated_line_count`、`dropped_match_line_count`、`truncation_context` を持つ軽量スニペットへ投影します。`--snippet-lines` で抜粋長を先に制限でき(デフォルト: 8、最大: 20)、`--max-line-width`(CLI)/ `maxLineWidth`(MCP)は `find` / `references` / `excerpt` / `inspect` と同じ共有 `LineWidthFormatter.ClampLine` 契約(デフォルト: 512、最大: 4096、`0` で切り詰め解除)で各スニペット行を最初のマッチトークン周辺にクランプするため、minified / transpiled / 生成された 1 行ファイル内の 1 ヒットで数百 KB を返さなくなります。クランプされた行はスニペットに `...(+N)...` マーカーが入り、`truncation_context.char_counts`、`truncation_context.total_chars`、`highlights[].truncated`、`highlights[].original_line_length`、`highlights[].truncated_char_counts` で AI クライアントがクランプの有無と省略文字数を検出できます。`highlights[].terms` は互換性のため distinct な term list のまま残し、`highlights[].term_occurrences` は一致ごとの `term`、1-based の `line` / `column`、`length` を記録します。exact substring search では `highlights[].literal_terms` と `highlights[].literal_term_occurrences`(MCP では camelCase)も追加され、広めの診断 token list を残したまま、要求された literal phrase だけを render できます。exact ではない記号の多い code phrase 検索では、FTS tokenization が記号を失いやすい場合に exact substring semantics で再検索できるよう、CLI JSON compact result に `exact_substring_hint`、MCP `search` に `recovery_hint` を追加します。`dropped_match_line_count` は選択された snippet window 外に落ちた一致行数を示します。

マッチ行がインデックス済みシンボル範囲内にある場合、`search --json` と MCP の `search` は任意フィールドの `enclosing_symbol_name`、`enclosing_symbol_kind`、`enclosing_symbol_start_line`、`enclosing_symbol_end_line`、`enclosing_container_name` も返します。
Expand Down
18 changes: 13 additions & 5 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,9 @@ Search normalizes literal FTS queries to Unicode NFC before matching. If every
literal token exceeds SQLite FTS5 unicode61's 1000-character token cap,
zero-result JSON includes `query_degraded_reason` and `tokens_dropped`. Index
validation reports long unbroken FTS tokens as `fts_token_too_long`.
Literal-safe `search` queries are capped at 1000 characters and 128 whitespace
terms. Oversized generated input is rejected before FTS5 sanitization; split it
into smaller searches or use narrower text.
Guard-aware search filters primary `search` matches by nearby literal guards:
`--require-before` / `--require-after` keep matches only when the guard query
appears in the selected line window, while `--reject-before` / `--reject-after`
Expand Down Expand Up @@ -927,8 +930,9 @@ For scripts or editor integrations that need several queries against the same
index, `cdidx batch --db <path>` keeps one SQLite connection open and reads one
JSON string array per stdin line. Each array starts with a query command name,
followed by that command's normal arguments. Each stdin line is capped at
1,048,576 characters, and each command can carry at most 256 arguments after
the command name:
1,048,576 characters, each decoded string argument is capped at 8,192
characters, and each command can carry at most 256 arguments after the command
name:

```bash
printf '%s\n' \
Expand Down Expand Up @@ -1067,7 +1071,7 @@ cdidx find "graph table" --path src/CodeIndex/Cli/QueryCommandRunner.cs
cdidx find "Graph Table" --path src/CodeIndex/Cli/QueryCommandRunner.cs --exact --before 1 --after 1 --json
```

`find` fills the gap between repo-wide `search` and line-number-based `excerpt`: when you already know the target file, it returns matching line numbers, columns, and short surrounding context from the indexed file without falling back to raw-text tools.
`find` fills the gap between repo-wide `search` and line-number-based `excerpt`: when you already know the target file, it returns matching line numbers, columns, and short surrounding context from the indexed file without falling back to raw-text tools. The query text is capped at 1,000 characters, matching `search`.

### List files

Expand Down Expand Up @@ -3174,6 +3178,9 @@ literal FTS クエリは照合前に Unicode NFC へ正規化されます。す
token が SQLite FTS5 unicode61 の 1000 文字 token 上限を超える場合、0 件
JSON には `query_degraded_reason` と `tokens_dropped` が含まれます。index
validation は長い連続 FTS token を `fts_token_too_long` として報告します。
literal-safe な `search` query は 1000 文字、128 whitespace term までです。
生成された大きすぎる入力は FTS5 sanitization 前に拒否されるため、小さな検索へ分割するか、
より狭い text にしてください。
guard-aware search は primary の `search` 一致を近傍の literal guard で絞り込みます:
`--require-before` / `--require-after` は指定行窓内に guard query がある場合だけ残し、
`--reject-before` / `--reject-after` は guard query がある一致を落とします。JSON の検索結果には
Expand Down Expand Up @@ -3221,7 +3228,8 @@ cdidx search "authenticate" --json --verbose
同じインデックスに対して複数の query を投げる script や editor integration では、
`cdidx batch --db <path>` を使うと 1 つの SQLite connection を開いたまま処理できます。
stdin の各行は JSON 文字列配列で、先頭に query command 名、その後ろに通常の引数を並べます。
各 stdin 行は 1,048,576 文字まで、各 command は command 名の後ろに最大 256 引数までです:
各 stdin 行は 1,048,576 文字まで、デコード後の各文字列引数は 8,192 文字まで、
各 command は command 名の後ろに最大 256 引数までです:

```bash
printf '%s\n' \
Expand Down Expand Up @@ -3358,7 +3366,7 @@ cdidx find "graph table" --path src/CodeIndex/Cli/QueryCommandRunner.cs
cdidx find "Graph Table" --path src/CodeIndex/Cli/QueryCommandRunner.cs --exact --before 1 --after 1 --json
```

`find` は、リポジトリ全体を対象にする `search` と、行番号が必要な `excerpt` の間を埋めるコマンドです。対象ファイルが既に分かっているときに、raw text ツールへ戻らずに、インデックス済みファイルから一致行番号・列番号・短い前後文脈を返します。
`find` は、リポジトリ全体を対象にする `search` と、行番号が必要な `excerpt` の間を埋めるコマンドです。対象ファイルが既に分かっているときに、raw text ツールへ戻らずに、インデックス済みファイルから一致行番号・列番号・短い前後文脈を返します。query text は `search` と同じく 1,000 文字までです。

### ファイル一覧

Expand Down
22 changes: 22 additions & 0 deletions changelog.d/unreleased/3081.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
category: fixed
issues:
- 3081
affected:
- src/CodeIndex/Database/DbSearchReader.cs
- src/CodeIndex/Database/SearchQueryLimitException.cs
- src/CodeIndex/Cli/QueryCommandRunner.cs
- src/CodeIndex/Mcp/McpToolHandlers.cs
- tests/CodeIndex.Tests/DbSearchReaderIssueTests.cs
- tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs
- USER_GUIDE.md
- DEVELOPER_GUIDE.md
---

## English

- **Literal search sanitization now rejects oversized generated input (#3081)** — literal-safe `search` queries are capped at 1,000 characters and 128 whitespace terms in the database reader before FTS5 sanitization, with CLI and MCP returning usage errors instead of falling through to database failures.

## 日本語

- **literal search sanitization が大きすぎる生成入力を拒否するようになりました (#3081)** — literal-safe な `search` query は FTS5 sanitization 前の database reader 層で 1,000 文字、128 whitespace term に制限され、CLI/MCP は database failure へ落とさず usage error を返します。
18 changes: 18 additions & 0 deletions changelog.d/unreleased/3100.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
category: fixed
issues:
- 3100
affected:
- src/CodeIndex/Cli/QueryCommandRunner.cs
- src/CodeIndex/Cli/ConsoleUi.cs
- tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs
- USER_GUIDE.md
---

## English

- **`find` now enforces the shared query length limit (#3100)** — `cdidx find` and `cdidx find --count` reject query text above 1,000 characters before opening the database reader.

## 日本語

- **`find` が共通の query 長上限を適用するようになりました (#3100)** — `cdidx find` と `cdidx find --count` は、query text が 1,000 文字を超える場合、database reader を開く前に拒否します。
18 changes: 18 additions & 0 deletions changelog.d/unreleased/3231.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
category: fixed
issues:
- 3231
affected:
- src/CodeIndex/Cli/QueryCommandRunner.cs
- tests/CodeIndex.Tests/QueryCommandRunnerTests.cs
- USER_GUIDE.md
- DEVELOPER_GUIDE.md
---

## English

- **Batch query arguments are now individually capped (#3231)** — `cdidx batch` rejects any decoded JSON string argument longer than 8,192 characters before dispatching the query command.

## 日本語

- **batch query の各引数に個別上限を追加しました (#3231)** — `cdidx batch` は、デコード後の JSON 文字列引数が 8,192 文字を超える場合、query command へ dispatch する前に拒否します。
2 changes: 1 addition & 1 deletion src/CodeIndex/Cli/ConsoleUi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ private static void PrintFlagReference(Action<string> WriteHelpLine)
Console.WriteLine(" --limit <n>, --top <n> Max results to return (default: 20)");
Console.WriteLine(" --lang <lang> Filter by language (aliases: bat, cmd, cshtml, razor, ts, tsx, cts, mts)");
Console.WriteLine(" --path <glob> Restrict matches to glob-style path patterns (* and ?)");
WriteHelpLine($" --query <query> Pass a query literal, useful when the query starts with '-' (`search` max {QueryLimits.MaxQueryLength} chars)");
WriteHelpLine($" --query <query> Pass a query literal, useful when the query starts with '-' (`search`/`find` max {QueryLimits.MaxQueryLength} chars)");
Console.WriteLine(" --exclude-path <glob> Exclude glob-style path patterns (* and ?) (repeatable)");
Console.WriteLine(" --exclude-tests Exclude likely test files");
Console.WriteLine(" --include-generated Include generated files in query results");
Expand Down
22 changes: 21 additions & 1 deletion src/CodeIndex/Cli/QueryCommandRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public static class QueryCommandRunner
internal const int MaxWorkspaceDependencyDatabasePairCount = MaxWorkspaceDependencyDatabaseCount * (MaxWorkspaceDependencyDatabaseCount - 1);
internal const int BatchMaxLineChars = 1024 * 1024;
internal const int BatchMaxArgumentCount = 256;
internal const int BatchMaxArgumentChars = 8192;
internal const int BatchMaxJsonDepth = 32;
internal const string DefaultLimitEnvironmentVariable = "CDIDX_DEFAULT_LIMIT";
internal const string DefaultSnippetLinesEnvironmentVariable = "CDIDX_DEFAULT_SNIPPET_LINES";
Expand Down Expand Up @@ -429,7 +430,13 @@ private static bool TryParseBatchLine(string line, int lineNumber, out string co
Console.Error.WriteLine($"Error: batch line {lineNumber} must contain only strings.");
return false;
}
values.Add(element.GetString() ?? string.Empty);
var value = element.GetString() ?? string.Empty;
if (value.Length > BatchMaxArgumentChars)
{
Console.Error.WriteLine($"Error: batch line {lineNumber} argument {values.Count + 1} exceeds the {BatchMaxArgumentChars} character limit.");
return false;
}
values.Add(value);
}

commandName = values[0];
Expand Down Expand Up @@ -2720,6 +2727,13 @@ public static int RunFind(string[] cmdArgs, JsonSerializerOptions jsonOptions)
Console.Error.WriteLine(FindUsage);
return CommandExitCodes.UsageError;
}
if (options.Query.Length > QueryLimits.MaxQueryLength)
{
Console.Error.WriteLine($"Error: {QueryLimits.FormatQueryTooLongError()}");
Console.Error.WriteLine("Hint: Shorten the find text or split generated input into smaller queries before running `cdidx find`.");
Console.Error.WriteLine(FindUsage);
return CommandExitCodes.UsageError;
}

if (options.PathPatterns.Count == 0)
{
Expand Down Expand Up @@ -7572,6 +7586,12 @@ private static int WithDb(
Console.Error.WriteLine("Hint: narrow the search with more specific query text, --lang, --path, or --exclude-tests, or reduce pagination offset before retrying guarded search.");
return CommandExitCodes.UsageError;
}
catch (SearchQueryLimitException ex)
{
Console.Error.WriteLine($"Error [{CommandErrorCodes.UsageError}]: {ex.Message}");
Console.Error.WriteLine("Hint: shorten the search text or split generated input into smaller literal queries.");
return CommandExitCodes.UsageError;
}
catch (Exception ex)
{
if (JsonOutputFailure.TryHandle(ex, out var exitCode))
Expand Down
Loading
Loading