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
8 changes: 6 additions & 2 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1323,6 +1323,8 @@ Supported schema (top-level keys are snake_case; nested indexing kind keys keep
"global_tool_log_dir": "./.cdidx/logs", // → CDIDX_GLOBAL_TOOL_LOG_DIR
"stale_after": "2h", // → CDIDX_STALE_AFTER
"suggestion_dedup_threshold": 0.85, // → CDIDX_SUGGESTION_DEDUP_THRESHOLD
"suggestion_max_age_days": 365, // → CDIDX_SUGGESTION_MAX_AGE_DAYS
"suggestion_max_count": 5000, // → CDIDX_SUGGESTION_MAX_COUNT
"indexing": {
"includeKinds": ["class"], // → CDIDX_INDEX_INCLUDE_SYMBOL_KINDS
"excludeKinds": ["test_method"] // → CDIDX_INDEX_EXCLUDE_SYMBOL_KINDS
Expand All @@ -1340,7 +1342,7 @@ Supported schema (top-level keys are snake_case; nested indexing kind keys keep
}
```

JSON5-style line comments (`//`) and trailing commas are accepted so the file stays human-editable. The optional `$schema` key is ignored at runtime; it is honored only so editors that recognize JSON Schema references can offer completion. Setting `disable_persistent_log` to `false` is a no-op (absence already means "logging enabled") — only `true` exports `CDIDX_DISABLE_PERSISTENT_LOG=1`. `stale_after` uses the same compact duration format as `status --check --stale-after`: `30m`, `2h`, or `7d`. `suggestion_dedup_threshold` sets the MCP suggestion fuzzy-deduplication cutoff as a number from `0` to `1`; the built-in default is `0.85`, and `cdidx mcp --suggestion-dedup-threshold <0..1>` overrides it for one MCP session. `indexing.includeKinds` and `indexing.excludeKinds` set the default symbol-kind filter for `cdidx index`; CLI flags `--include-symbol-kind <kind>[,<kind>]` and `--exclude-symbol-kind <kind>[,<kind>]` override those env-backed defaults for a single run.
JSON5-style line comments (`//`) and trailing commas are accepted so the file stays human-editable. The optional `$schema` key is ignored at runtime; it is honored only so editors that recognize JSON Schema references can offer completion. Setting `disable_persistent_log` to `false` is a no-op (absence already means "logging enabled") — only `true` exports `CDIDX_DISABLE_PERSISTENT_LOG=1`. `stale_after` uses the same compact duration format as `status --check --stale-after`: `30m`, `2h`, or `7d`. `suggestion_dedup_threshold` sets the MCP suggestion fuzzy-deduplication cutoff as a number from `0` to `1`; the built-in default is `0.85`, and `cdidx mcp --suggestion-dedup-threshold <0..1>` overrides it for one MCP session. `suggestion_max_age_days` and `suggestion_max_count` bound the live `.cdidx/suggestions-*.json` store; pruned records are appended to `.cdidx/suggestions-*.archive.jsonl`. `indexing.includeKinds` and `indexing.excludeKinds` set the default symbol-kind filter for `cdidx index`; CLI flags `--include-symbol-kind <kind>[,<kind>]` and `--exclude-symbol-kind <kind>[,<kind>]` override those env-backed defaults for a single run.

## How it works

Expand Down Expand Up @@ -3302,6 +3304,8 @@ MCP ツールで catch-all まで突き抜けた例外(想定外の SQLite 例
"global_tool_log_dir": "./.cdidx/logs", // → CDIDX_GLOBAL_TOOL_LOG_DIR
"stale_after": "2h", // → CDIDX_STALE_AFTER
"suggestion_dedup_threshold": 0.85, // → CDIDX_SUGGESTION_DEDUP_THRESHOLD
"suggestion_max_age_days": 365, // → CDIDX_SUGGESTION_MAX_AGE_DAYS
"suggestion_max_count": 5000, // → CDIDX_SUGGESTION_MAX_COUNT
"indexing": {
"includeKinds": ["class"], // → CDIDX_INDEX_INCLUDE_SYMBOL_KINDS
"excludeKinds": ["test_method"] // → CDIDX_INDEX_EXCLUDE_SYMBOL_KINDS
Expand All @@ -3319,7 +3323,7 @@ MCP ツールで catch-all まで突き抜けた例外(想定外の SQLite 例
}
```

人手で編集しやすいよう JSON5 形式の行コメント(`//`)と末尾カンマを許容します。任意の `$schema` キーはランタイムでは無視され、JSON Schema 参照をサポートするエディタが補完を提供するためだけに認識されます。`disable_persistent_log` を `false` に設定しても何も起きません(不在のままで "ログ有効" が既定)— `true` の場合のみ `CDIDX_DISABLE_PERSISTENT_LOG=1` を export します。`stale_after` は `status --check --stale-after` と同じ compact duration 形式(`30m` / `2h` / `7d`)です。`suggestion_dedup_threshold` は MCP suggestion の fuzzy deduplication しきい値を `0` から `1` の数値で設定します。組み込み既定値は `0.85` で、`cdidx mcp --suggestion-dedup-threshold <0..1>` は 1 回の MCP session だけこの値を上書きします。`indexing.includeKinds` と `indexing.excludeKinds` は `cdidx index` の symbol-kind filter 既定値を設定し、CLI フラグ `--include-symbol-kind <kind>[,<kind>]` / `--exclude-symbol-kind <kind>[,<kind>]` はその env 経由の既定値を 1 回の実行だけ上書きします。
人手で編集しやすいよう JSON5 形式の行コメント(`//`)と末尾カンマを許容します。任意の `$schema` キーはランタイムでは無視され、JSON Schema 参照をサポートするエディタが補完を提供するためだけに認識されます。`disable_persistent_log` を `false` に設定しても何も起きません(不在のままで "ログ有効" が既定)— `true` の場合のみ `CDIDX_DISABLE_PERSISTENT_LOG=1` を export します。`stale_after` は `status --check --stale-after` と同じ compact duration 形式(`30m` / `2h` / `7d`)です。`suggestion_dedup_threshold` は MCP suggestion の fuzzy deduplication しきい値を `0` から `1` の数値で設定します。組み込み既定値は `0.85` で、`cdidx mcp --suggestion-dedup-threshold <0..1>` は 1 回の MCP session だけこの値を上書きします。`suggestion_max_age_days` と `suggestion_max_count` は live の `.cdidx/suggestions-*.json` store の上限を設定し、prune された record は `.cdidx/suggestions-*.archive.jsonl` に追記されます。`indexing.includeKinds` と `indexing.excludeKinds` は `cdidx index` の symbol-kind filter 既定値を設定し、CLI フラグ `--include-symbol-kind <kind>[,<kind>]` / `--exclude-symbol-kind <kind>[,<kind>]` はその env 経由の既定値を 1 回の実行だけ上書きします。

## 動作の仕組み

Expand Down
16 changes: 16 additions & 0 deletions changelog.d/unreleased/1614.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: fixed
issues:
- 1614
affected:
- src/CodeIndex/Cli/GitHubIssueReporter.cs
- tests/CodeIndex.Tests/GitHubIssueReporterTests.cs
---

## English

- **Sanitized `suggest_improvement` GitHub issue titles (#1614)** — outbound suggestion issue titles now strip markdown link/image syntax characters and defensively cap category text before posting.

## 日本語

- **`suggest_improvement` の GitHub Issue タイトルを sanitize しました (#1614)** — 外部送信用の提案 Issue タイトルから markdown のリンク/画像構文文字を除去し、category 文字列も防御的に上限を設けてから投稿します。
16 changes: 16 additions & 0 deletions changelog.d/unreleased/1796.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: fixed
issues:
- 1796
affected:
- src/CodeIndex/Cli/SuggestionStore.cs
- tests/CodeIndex.Tests/SuggestionStoreTests.cs
---

## English

- **Redacted sensitive suggestion text before persistence (#1796)** — `SuggestionStore` now masks common credential and token patterns before writing local suggestion history or submitting to GitHub, with stderr warnings that name only the redaction types.

## 日本語

- **提案テキストを保存前に redaction するようにしました (#1796)** — `SuggestionStore` はローカルの提案履歴保存や GitHub 送信の前に一般的な credential/token パターンをマスクし、stderr には redaction 種別だけを警告します。
17 changes: 17 additions & 0 deletions changelog.d/unreleased/1921.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
category: fixed
issues:
- 1921
affected:
- src/CodeIndex/Mcp/McpToolHandlers.cs
- src/CodeIndex/Mcp/McpToolDefinitions.cs
- tests/CodeIndex.Tests/McpServerTests.cs
---

## English

- **Validated `suggest_improvement` write targets before locking (#1921)** — the MCP tool now probes the resolved `.cdidx` directory for writability before entering the suggestion-store lock and includes `cdidx_dir` in success and duplicate payloads.

## 日本語

- **`suggest_improvement` の書き込み先を lock 前に検証するようにしました (#1921)** — MCP tool は suggestion-store lock に入る前に解決済み `.cdidx` ディレクトリへ書き込み probe を行い、成功/重複 payload に `cdidx_dir` を含めます。
19 changes: 19 additions & 0 deletions changelog.d/unreleased/1940.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
category: fixed
issues:
- 1940
affected:
- src/CodeIndex/Cli/SuggestionStore.cs
- src/CodeIndex/Cli/CdidxConfigFile.cs
- tests/CodeIndex.Tests/SuggestionStoreTests.cs
- tests/CodeIndex.Tests/CdidxConfigFileTests.cs
- USER_GUIDE.md
---

## English

- **Bounded live suggestion history (#1940)** — `SuggestionStore` now prunes stale or over-limit records into `.cdidx/suggestions-*.archive.jsonl`, with configurable `suggestion_max_age_days` and `suggestion_max_count` settings.

## 日本語

- **live の提案履歴に上限を設けました (#1940)** — `SuggestionStore` は古いまたは上限超過の record を `.cdidx/suggestions-*.archive.jsonl` に退避し、`suggestion_max_age_days` と `suggestion_max_count` で設定できます。
36 changes: 36 additions & 0 deletions src/CodeIndex/Cli/CdidxConfigFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ internal static class CdidxConfigFile
"graph",
"folding",
"suggestion_dedup_threshold",
"suggestion_max_age_days",
"suggestion_max_count",
"mcp",
};

Expand Down Expand Up @@ -165,6 +167,20 @@ internal static LoadResult LoadAndApply(
pending.Add((SuggestionStore.DedupThresholdEnvironmentVariable, value!));
}

if (root.TryGetProperty("suggestion_max_age_days", out var suggestionMaxAgeDays))
{
if (!TryReadPositiveIntegerAsString(suggestionMaxAgeDays, "suggestion_max_age_days", path, out var value, out var err))
return new LoadResult(Path: path, Error: err);
pending.Add((SuggestionStore.MaxAgeDaysEnvironmentVariable, value!));
}

if (root.TryGetProperty("suggestion_max_count", out var suggestionMaxCount))
{
if (!TryReadPositiveIntegerAsString(suggestionMaxCount, "suggestion_max_count", path, out var value, out var err))
return new LoadResult(Path: path, Error: err);
pending.Add((SuggestionStore.MaxCountEnvironmentVariable, value!));
}

if (root.TryGetProperty("indexing", out var indexing))
{
if (indexing.ValueKind != JsonValueKind.Object)
Expand Down Expand Up @@ -443,6 +459,26 @@ private static bool TryReadNumberAsString(JsonElement element, string key, strin
return true;
}

private static bool TryReadPositiveIntegerAsString(JsonElement element, string key, string path, out string? value, out string? error)
{
value = null;
error = null;
if (element.ValueKind != JsonValueKind.Number)
{
error = $"[cdidx] {path}: `{key}` must be a number.";
return false;
}

if (!element.TryGetInt32(out var parsed) || parsed <= 0)
{
error = $"[cdidx] {path}: `{key}` must be a positive integer.";
return false;
}

value = parsed.ToString(System.Globalization.CultureInfo.InvariantCulture);
return true;
}

private static bool TryReadSearchInteger(JsonElement element, string key, string optionName, bool allowZero, string path, out string? value, out string? error)
{
value = null;
Expand Down
24 changes: 22 additions & 2 deletions src/CodeIndex/Cli/GitHubIssueReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,15 @@ internal static string ScrubInlineCode(string text)

internal static string BuildIssueTitle(string category, string description)
{
var prefix = $"[AI Suggestion] {category}: ";
var sanitizedCategory = SanitizeIssueTitleText(category);
if (sanitizedCategory.Length > 40)
sanitizedCategory = sanitizedCategory[..40].TrimEnd();

var prefix = $"[AI Suggestion] {sanitizedCategory}: ";
if (prefix.Length >= MaxGitHubIssueTitleLength)
return prefix[..MaxGitHubIssueTitleLength];

var scrubbedForTitle = ScrubInlineCode(description).Replace("\r", " ").Replace("\n", " ").Trim();
var scrubbedForTitle = SanitizeIssueTitleText(ScrubInlineCode(description));
var maxDescriptionLength = MaxGitHubIssueTitleLength - prefix.Length;
var shortDesc = TruncateWithEllipsis(scrubbedForTitle, Math.Min(63, maxDescriptionLength));
var title = prefix + shortDesc;
Expand All @@ -450,6 +454,22 @@ internal static string BuildIssueTitle(string category, string description)
: title[..MaxGitHubIssueTitleLength];
}

internal static string SanitizeIssueTitleText(string value)
{
if (string.IsNullOrEmpty(value))
return string.Empty;

var builder = new StringBuilder(value.Length);
foreach (var c in value.Replace("\r", " ").Replace("\n", " "))
{
if (c is '[' or ']' or '(' or ')' or '`')
continue;
builder.Append(c);
}

return builder.ToString().Trim();
}

private static string TruncateWithEllipsis(string value, int maxLength)
{
if (value.Length <= maxLength)
Expand Down
Loading
Loading