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
63 changes: 50 additions & 13 deletions USER_GUIDE.md

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions changelog.d/unreleased/3667.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
category: changed
issues:
- 3667
affected:
- src/CodeIndex/Cli/SearchAuditRecipes.cs
- src/CodeIndex/Cli/QueryCommandRunner.cs
- tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs
- USER_GUIDE.md
---

## English

- **Recipe JSON output now includes bounded run summaries (#3667)** — recipe JSON and compact responses report per-query limits, emitted/truncated counts, minimum omitted counts, and cursor availability so large audit runs can be paged intentionally.

## 日本語

- **recipe JSON output に bounded run summary を追加しました (#3667)** — recipe JSON と compact response は query ごとの limit、emitted/truncated count、最小 omitted count、cursor 利用可否を返し、大きな audit run を意図的に page できるようにしました。
18 changes: 18 additions & 0 deletions changelog.d/unreleased/3668.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
category: changed
issues:
- 3668
affected:
- src/CodeIndex/Cli/QueryCommandRunner.cs
- src/CodeIndex/Cli/SearchAuditRecipes.cs
- src/CodeIndex/Cli/SuggestionsCommandRunner.cs
- src/CodeIndex/Cli/JsonOutputContracts.cs
- tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs
- tests/CodeIndex.Tests/ProgramCliTests.cs
- USER_GUIDE.md
---
## English
- **Issue-draft exports now include triage metadata (#3668)** - Search and suggestion issue-draft JSON now expose severity, confidence, evidence count, and duplicate guidance, and generated bodies include the same metadata before filing guidance.

## 日本語
- **Issue draft export に triage metadata を追加 (#3668)** - search と suggestions の issue-draft JSON が severity、confidence、evidence count、duplicate guidance を出力し、生成 body にも起票前確認用の同じ metadata を含めるようになりました。
16 changes: 16 additions & 0 deletions changelog.d/unreleased/3807.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: changed
issues:
- 3807
affected:
- src/CodeIndex/Cli/SearchAuditRecipes.cs
- tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs
---

## English

- **External search recipes now honor declared defaults and hide raw source paths in diagnostics (#3807)** — `default_scope`, `default_path_patterns`, and `default_exclude_paths` are applied to recipe runs, while load diagnostics identify configured recipe sources by ordinal label instead of echoing filesystem paths.

## 日本語

- **外部検索レシピの defaults を反映し、診断で生の source path を隠すようになりました (#3807)** — `default_scope`、`default_path_patterns`、`default_exclude_paths` を recipe 実行に適用し、読み込み診断では filesystem path をそのまま出さず番号付き source label で識別します。
21 changes: 21 additions & 0 deletions changelog.d/unreleased/3826-3674.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
category: changed
issues:
- 3826
- 3674
affected:
- src/CodeIndex/Cli/CdidxConfigFile.cs
- src/CodeIndex/Cli/SearchAuditRecipes.cs
- src/CodeIndex/Cli/QueryCommandRunner.cs
- tests/CodeIndex.Tests/CdidxConfigFileTests.cs
- tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs
- USER_GUIDE.md
---

## English

- **Search recipe and config discovery boundaries are stricter (#3826, #3674)** — external recipe sources are read through a bounded helper, recipe queries can declare severity plus query-local include/exclude globs while still honoring explicit user `--path` filters, built-in audit recipes cover additional IO/sleep/HTTP patterns, and project config discovery stops at repository/workspace boundaries.

## 日本語

- **検索レシピと設定探索の境界を強化しました (#3826, #3674)** — 外部 recipe source は bounded helper 経由で読み込み、recipe query は severity と query-local include/exclude glob を宣言しつつ明示的なユーザー `--path` filter も尊重し、built-in audit recipe は追加の IO/sleep/HTTP pattern を扱い、project config discovery は repository/workspace boundary で停止します。
22 changes: 22 additions & 0 deletions changelog.d/unreleased/3827.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
category: changed
issues:
- 3827
affected:
- src/CodeIndex/Cli/IssueDuplicatePreflight.cs
- src/CodeIndex/Cli/QueryCommandRunner.cs
- src/CodeIndex/Cli/SuggestionsCommandRunner.cs
- src/CodeIndex/Cli/CliFlagSchema.cs
- src/CodeIndex/Cli/ConsoleUi.cs
- tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs
- tests/CodeIndex.Tests/ProgramCliTests.cs
- tests/CodeIndex.Tests/ConsoleUiTests.cs
- USER_GUIDE.md
---
## English
- **Machine-readable search exports carry richer metadata (#3827)** - Search CSV/TSV output now includes query, language, visibility, enclosing symbol, match line, and recipe/query columns, while SARIF output includes rule metadata, help text, result levels, and normalized artifact URIs.
- **Issue-draft duplicate preflight is tunable (#3827)** - Search and suggestion issue-draft exports accept duplicate confidence presets or an explicit score threshold and report the effective confidence/minimum score in JSON.

## 日本語
- **機械可読 search export の metadata を拡充 (#3827)** - search の CSV/TSV 出力に query、language、visibility、enclosing symbol、match line、recipe/query 列を追加し、SARIF 出力には rule metadata、help text、result level、正規化済み artifact URI を含めるようにしました。
- **Issue draft の duplicate preflight を調整可能にしました (#3827)** - search と suggestions の issue-draft export が duplicate confidence preset または明示的な score threshold を受け取り、有効な confidence / minimum score を JSON に出力します。
27 changes: 27 additions & 0 deletions src/CodeIndex/Cli/CdidxConfigFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ internal static class CdidxConfigFile
private static readonly IReadOnlyList<string> KnownMcpKeys = new[] { "tools", "rate_limit" };
private static readonly IReadOnlyList<string> KnownMcpToolsKeys = new[] { "allow", "deny" };
private static readonly IReadOnlyList<string> KnownMcpRateLimitKeys = new[] { "rps", "burst", "bucket_idle_seconds" };
private static readonly IReadOnlyList<string> ConfigDiscoveryBoundaryDirectories = new[] { ".git", ".hg", ".svn" };
private static readonly IReadOnlyList<string> ConfigDiscoveryBoundaryFiles = new[]
{
WorkspaceManifestLoader.FileName,
WorkspaceManifestLoader.DotFileName,
};

internal sealed record LoadResult(string? Path, string? Error)
{
Expand Down Expand Up @@ -465,11 +471,32 @@ private static (IReadOnlyDictionary<string, string> Settings, IReadOnlyDictionar
var candidate = Path.Combine(current.FullName, FileName);
if (File.Exists(LongPath.EnsureWindowsPrefix(candidate)))
return candidate;
if (IsConfigDiscoveryBoundary(current))
break;
current = current.Parent;
}
return null;
}

private static bool IsConfigDiscoveryBoundary(DirectoryInfo directory)
{
foreach (var directoryName in ConfigDiscoveryBoundaryDirectories)
{
var path = Path.Combine(directory.FullName, directoryName);
if (Directory.Exists(LongPath.EnsureWindowsPrefix(path)))
return true;
}

foreach (var fileName in ConfigDiscoveryBoundaryFiles)
{
var path = Path.Combine(directory.FullName, fileName);
if (File.Exists(LongPath.EnsureWindowsPrefix(path)))
return true;
}

return false;
}

private static string ResolveConfigWorkspaceRoot(string configPath)
{
var fullConfigPath = Path.GetFullPath(configPath);
Expand Down
2 changes: 2 additions & 0 deletions src/CodeIndex/Cli/CliFlagSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ private static IReadOnlyList<CliFlag> BuildAll()
new() { Name = "--named-query", ValuePlaceholder = "<name>=<query>", Description = "Search: add one named ad hoc batch query", Commands = Set("search") },
new() { Name = "--open-issues", ValuePlaceholder = "<path|github|github:owner/name>", Description = "Preflight issue drafts against open issue JSON or GitHub open issues", Commands = Set("search", "suggestions") },
new() { Name = "--repo", ValuePlaceholder = "<owner/name>", Description = "Issue-drafts: GitHub repository for --open-issues github", Commands = Set("search", "suggestions") },
new() { Name = "--duplicate-confidence", ValuePlaceholder = "<low|medium|high>", Description = "Issue-drafts: preset duplicate-preflight match threshold", Commands = Set("search", "suggestions") },
new() { Name = "--duplicate-threshold", ValuePlaceholder = "<score>", Description = "Issue-drafts: explicit duplicate-preflight minimum score from 0 to 1", Commands = Set("search", "suggestions") },
new() { Name = "--issue-title", ValuePlaceholder = "<title>", Description = "Search issue-drafts: override the title for an ad hoc search draft", Commands = Set("search") },
new() { Name = "--issue-label", ValuePlaceholder = "<label>", Description = "Search issue-drafts: add a label hint; repeat or comma-separate values", Commands = Set("search") },
new() { Name = "--cursor", ValuePlaceholder = "<cursor>", Description = "Search recipe: pagination cursor returned as next_cursor", Commands = Set("search") },
Expand Down
4 changes: 2 additions & 2 deletions src/CodeIndex/Cli/ConsoleUi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ private static readonly (string Command, string Usage)[] CommandUsageLines =
("index-commits", "cdidx index <projectPath> --commits <commit-ref> [commit-ref ...] [--db <path>] [--verbose] [--dry-run] [--json] [--memory-trace] [--duration-format <auto|seconds|hms>] [--max-file-bytes <bytes>] [--include-symbol-kind <kind>[,<kind>]] [--exclude-symbol-kind <kind>[,<kind>]]"),
("index-changed-between", "cdidx index <projectPath> --changed-between <old-ref> <new-ref> [--db <path>] [--verbose] [--dry-run] [--json] [--memory-trace] [--duration-format <auto|seconds|hms>] [--max-file-bytes <bytes>] [--include-symbol-kind <kind>[,<kind>]] [--exclude-symbol-kind <kind>[,<kind>]]"),
("index-files", "cdidx index <projectPath> --files <path> [path ...] [--db <path>] [--verbose] [--dry-run] [--json] [--memory-trace] [--duration-format <auto|seconds|hms>] [--max-file-bytes <bytes>] [--include-symbol-kind <kind>[,<kind>]] [--exclude-symbol-kind <kind>[,<kind>]]"),
("search", "cdidx search <query>|--query <query>|-- <query>|--recipe <name|name/query>|--list-recipes|--named-query <name>=<query> [--named-query <name>=<query> ...] [--include-query <name>] [--exclude-query <name>] [--cursor <cursor>] [--audit-scope <source|all>] [--show-excluded] [--db <path>] [--json[=ndjson|array]] [--pretty] [--format <text|json|count|compact|csv|tsv|lsp|qf|sarif|issue-drafts>] [--open-issues <path|github|github:owner/name>] [--repo <owner/name>] [--issue-title <title>] [--issue-label <label>] [--verbose] [--limit <n>|--top <n>|--max-results <n>] [--lang <lang>] [--path <glob>] [--exclude-path <glob>] [--exclude-tests] [--exclude-comments] [--exclude-strings] [--exclude-fixtures] [--snippet-lines <n>] [--snippet-focus <leftmost|quality|proximity>] [--max-line-width <n>] [--fts] [--exact|--exact-substring] [--prefix] [--count] [--group-by <file|symbol>] [--since <datetime>] [--no-dedup] [--no-visibility-rank] [--require-before <query>] [--require-after <query>] [--reject-before <query>] [--reject-after <query>] [--guard-window <n>]"),
("search", "cdidx search <query>|--query <query>|-- <query>|--recipe <name|name/query>|--list-recipes|--named-query <name>=<query> [--named-query <name>=<query> ...] [--include-query <name>] [--exclude-query <name>] [--cursor <cursor>] [--audit-scope <source|all>] [--show-excluded] [--db <path>] [--json[=ndjson|array]] [--pretty] [--format <text|json|count|compact|csv|tsv|lsp|qf|sarif|issue-drafts>] [--open-issues <path|github|github:owner/name>] [--repo <owner/name>] [--duplicate-confidence <low|medium|high>|--duplicate-threshold <score>] [--issue-title <title>] [--issue-label <label>] [--verbose] [--limit <n>|--top <n>|--max-results <n>] [--lang <lang>] [--path <glob>] [--exclude-path <glob>] [--exclude-tests] [--exclude-comments] [--exclude-strings] [--exclude-fixtures] [--snippet-lines <n>] [--snippet-focus <leftmost|quality|proximity>] [--max-line-width <n>] [--fts] [--exact|--exact-substring] [--prefix] [--count] [--group-by <file|symbol>] [--since <datetime>] [--no-dedup] [--no-visibility-rank] [--require-before <query>] [--require-after <query>] [--reject-before <query>] [--reject-after <query>] [--guard-window <n>]"),
("definition", "cdidx definition <query>|--query <query>|-- <query> [--db <path>] [--json] [--format <text|json|count|compact|csv|tsv|lsp|qf|sarif>] [--verbose] [--limit <n>|--top <n>] [--lang <lang>] [--kind <kind>] [--visibility <v[,v]>] [--exclude-visibility <v[,v]>] [--path <glob>] [--exclude-path <glob>] [--exclude-tests] [--body] [--exact|--exact-name] [--count] [--since <datetime>]"),
("goto", "cdidx goto <query>|--query <query>|-- <query> [--db <path>] [--json] [--limit <n>|--top <n>] [--lang <lang>] [--kind <kind>] [--path <glob>] [--exclude-path <glob>] [--exclude-tests] [--exact|--exact-name] [--all]"),
("references", "cdidx references <query>|--query <query>|-- <query> [--db <path>] [--json] [--format <text|json|count|compact|csv|tsv|lsp|qf|sarif>] [--verbose] [--limit <n>|--top <n>] [--lang <lang>] [--kind <kind>] [--path <glob>] [--exclude-path <glob>] [--exclude-tests] [--body] [--snippet-lines <n>] [--max-line-width <n>] [--exact|--exact-name] [--count]"),
Expand All @@ -114,7 +114,7 @@ private static readonly (string Command, string Usage)[] CommandUsageLines =
("deps", "cdidx deps [--db <path>] [--json] [--format <dot|graphml|json-graph|edgelist>] [--verbose] [--limit <n>|--top <n>] [--lang <lang>] [--path <glob>] [--exclude-path <glob>] [--exclude-tests] [--reverse] [--cycles]"),
("unused", "cdidx unused [--db <path>] [--json] [--compact] [--verbose] [--limit <n>|--top <n>] [--kind <kind>] [--bucket <bucket>] [--min-confidence <medium|low>] [--visibility <v[,v]>] [--exclude-visibility <v[,v]>] [--lang <lang>] [--path <glob>] [--exclude-path <glob>] [--exclude-tests] [--count] [--by-bucket]"),
("hotspots", "cdidx hotspots [--db <path>] [--json] [--verbose] [--limit <n>|--top <n>] [--kind <kind>] [--visibility <v[,v]>] [--exclude-visibility <v[,v]>] [--lang <lang>] [--path <glob>] [--exclude-path <glob>] [--exclude-tests] [--count] [--group-by <symbol|file|statement>] [--group-by-name]"),
("suggestions", "cdidx suggestions <list|show|export> [id] [--db <path>] [--json] [--status <all|submitted|unsubmitted>] [--language <lang>] [--category <category>] [--since <datetime>] [--agent <name>] [--limit <n>] [--offset <n>] [--format <json|markdown|issue-drafts>] [--open-issues <path|github|github:owner/name>] [--repo <owner/name>]"),
("suggestions", "cdidx suggestions <list|show|export> [id] [--db <path>] [--json] [--status <all|submitted|unsubmitted>] [--language <lang>] [--category <category>] [--since <datetime>] [--agent <name>] [--limit <n>] [--offset <n>] [--format <json|markdown|issue-drafts>] [--open-issues <path|github|github:owner/name>] [--repo <owner/name>] [--duplicate-confidence <low|medium|high>|--duplicate-threshold <score>]"),
("export", "cdidx export <archive> [--db <path>] [--json]"),
("export", "cdidx export ctags [--output <path>] [--db <path>] [--json] [--lang <lang>] [--path <glob>] [--exclude-path <glob>] [--exclude-tests]"),
("import", "cdidx import <archive> [--db <path>] [--prune-paths] [--dry-run|--check] [--json]"),
Expand Down
36 changes: 32 additions & 4 deletions src/CodeIndex/Cli/IssueDuplicatePreflight.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ internal sealed class IssueDuplicatePreflight
internal const int MaxOpenIssueNumberLength = 32;
internal const int MaxTitleTokenizationInputLength = MaxOpenIssueTitleLength;
internal const int MaxGitHubRepositoryLength = 200;
internal const double LowDuplicateThreshold = 0.35;
internal const double DefaultDuplicateThreshold = 0.45;
internal const double HighDuplicateThreshold = 0.7;
internal const string LowDuplicateConfidence = "low";
internal const string DefaultDuplicateConfidence = "medium";
internal const string HighDuplicateConfidence = "high";
internal const string CustomDuplicateConfidence = "custom";
private const int GitHubOpenIssuesPerPage = 100;
private const int MaxGitHubOpenIssuePages = (MaxOpenIssueCount / GitHubOpenIssuesPerPage) + 1;
private const string GitHubSourceName = "github";
Expand Down Expand Up @@ -129,7 +136,26 @@ public static bool TryLoad(string? source, string? repository, out IssueDuplicat
return TryLoadFromGitHub(normalizedRepository, out preflight, out error);
}

public static bool TryNormalizeDuplicateConfidence(string value, out string confidence)
{
confidence = value.Trim().ToLowerInvariant();
return confidence is LowDuplicateConfidence or DefaultDuplicateConfidence or HighDuplicateConfidence;
}

public static double ThresholdForDuplicateConfidence(string confidence) => confidence switch
{
LowDuplicateConfidence => LowDuplicateThreshold,
HighDuplicateConfidence => HighDuplicateThreshold,
_ => DefaultDuplicateThreshold,
};

public List<SuggestionIssueDraftDuplicateMatchJsonResult> FindMatches(string draftTitle, IReadOnlyList<string> draftLabels)
=> FindMatches(draftTitle, draftLabels, DefaultDuplicateThreshold);

public List<SuggestionIssueDraftDuplicateMatchJsonResult> FindMatches(
string draftTitle,
IReadOnlyList<string> draftLabels,
double minimumScore)
{
if (!Checked || _issues.Count == 0)
return [];
Expand All @@ -153,13 +179,14 @@ public List<SuggestionIssueDraftDuplicateMatchJsonResult> FindMatches(string dra
string? reason = null;
if (normalizedIssueTitle.Length > 0 && normalizedIssueTitle == normalizedDraftTitle)
{
reason = "title_exact";
score = 1.0;
if (score >= minimumScore)
reason = "title_exact";
}
else if (overlappingLabels.Count > 0)
{
score = ScoreTitleSimilarity(draftTokens, TokenizeTitle(issue.Title));
if (score >= 0.45)
if (score >= minimumScore)
{
reason = "title_label_similarity";
}
Expand All @@ -168,8 +195,9 @@ public List<SuggestionIssueDraftDuplicateMatchJsonResult> FindMatches(string dra
&& (normalizedIssueTitle.Contains(normalizedDraftTitle, StringComparison.Ordinal)
|| normalizedDraftTitle.Contains(normalizedIssueTitle, StringComparison.Ordinal)))
{
reason = "title_label_contains";
score = Math.Max(score, 0.45);
score = Math.Max(score, DefaultDuplicateThreshold);
if (score >= minimumScore)
reason = "title_label_contains";
}
}

Expand Down
1 change: 1 addition & 0 deletions src/CodeIndex/Cli/JsonOutputContracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ internal sealed record VersionInfoJsonResult(
[JsonSerializable(typeof(SearchIssueDraftExportJsonResult))]
[JsonSerializable(typeof(SearchIssueDraftJsonResult))]
[JsonSerializable(typeof(SearchIssueDraftSourceJsonResult))]
[JsonSerializable(typeof(IssueDraftTriageMetadataJsonResult))]
[JsonSerializable(typeof(SearchNextMatchHint))]
[JsonSerializable(typeof(SearchResult))]
[JsonSerializable(typeof(SearchTermOccurrence))]
Expand Down
Loading
Loading