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
6 changes: 5 additions & 1 deletion USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,7 @@ cdidx ./myproject
cdidx ./myproject --rebuild # full rebuild from scratch
cdidx ./myproject --verbose # show per-file details
cdidx ./myproject --duration-format seconds # show elapsed time as seconds
cdidx ./myproject --notify=osc9 # terminal notification after long runs
cdidx ./myproject --watch # stay running and reindex on file changes
cdidx ./myproject --watch --debounce 200 # coalesce bursts within a 200 ms window
```
Expand Down Expand Up @@ -678,7 +679,9 @@ Done.

During long-running indexing on an interactive terminal, `Indexing...` stays live as a spinner instead of dropping to a fixed line until the next 50-file progress update. Warnings still print immediately, but the spinner resumes right after each warning so the run does not look frozen. When stdout is redirected (for example `cdidx . > out.txt`), cdidx prints a single `Indexing...` line to stdout, keeps warnings on stderr, and emits only line-based progress updates to stdout.

Human output formats elapsed index time with unit labels by default: milliseconds under 1 second, seconds under 1 minute, minutes/seconds under 1 hour, and hours/minutes/seconds after that. Use `--duration-format seconds` for decimal seconds or `--duration-format hms` for the legacy `HH:MM:SS` display. JSON output continues to expose raw `elapsed_ms` for machine consumers.
Human output uses invariant numeric formatting (`.` decimal separator and `,` thousands separators) regardless of the process locale, matching JSON's culture-independent contract. Elapsed index time uses unit labels by default: milliseconds under 1 second, seconds under 1 minute, minutes/seconds under 1 hour, and hours/minutes/seconds after that. Use `--duration-format seconds` for decimal seconds or `--duration-format hms` for the legacy `HH:MM:SS` display. JSON output continues to expose raw `elapsed_ms` for machine consumers.

For index runs that take at least five seconds, `--notify=<auto|bell|osc9|desktop|none>` controls a completion signal on stderr. `auto` rings the terminal bell only for interactive terminals and stays silent for redirected output; `desktop` currently maps to OSC 9 terminal notification text for terminals that support it. `CDIDX_NOTIFY` sets the same default, and `--quiet` suppresses completion notifications.

Machine-readable output also reports the post-run readiness bits directly:

Expand Down Expand Up @@ -2323,6 +2326,7 @@ name-based tools では `exactName` を使い、`exact` は後方互換 client
| Search snippet lines | `8`(`--snippet-lines`、最大 `20`) | CLI help と search runner |
| Max line width | `512`(`--max-line-width`、`0` で無効) | `LineWidthFormatter.DefaultMaxLineWidth` |
| Index max file size | `CDIDX_MAX_FILE_BYTES` 未設定時は `4MiB` | index runner help |
| Index completion notification | `auto`(interactive terminal は bell、redirected output は none)。`--notify` / `CDIDX_NOTIFY` で上書き | index runner help |
| Watch debounce | `500` ms(`--debounce`) | index watch runner |
| Status stale-after hint | `24h`。`--stale-after` / `CDIDX_STALE_AFTER` / `.cdidxrc.json` で上書き | status runner |
| Color mode | `auto`。`--color` / `CLICOLOR_FORCE` / `NO_COLOR` / `CLICOLOR=0` で上書き | `ConsoleUi` |
Expand Down
15 changes: 15 additions & 0 deletions changelog.d/unreleased/1629.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
category: fixed
issues:
- 1629
affected:
- src/CodeIndex/Cli/ConsoleUi.cs
---

## English

- **Console width detection no longer hides unexpected failures (#1629)** — width probing now catches only documented console exceptions, records fallback use, honors `COLUMNS` after failed probing, and emits a one-time verbose trace.

## 日本語

- **console width 検出が想定外の失敗を隠さないようになりました (#1629)** — 幅取得は既知の console 例外だけを捕捉し、fallback 使用を記録し、失敗時に `COLUMNS` を優先し、verbose で一度だけ trace を出します。
18 changes: 18 additions & 0 deletions changelog.d/unreleased/1662.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
category: fixed
issues:
- 1662
affected:
- src/CodeIndex/Cli/ConsoleUi.cs
- src/CodeIndex/Cli/IndexCommandRunner.FullScan.cs
- src/CodeIndex/Cli/IndexCommandRunner.Update.cs
- USER_GUIDE.md
---

## English

- **Human CLI index output now uses invariant numeric formatting (#1662)** — progress and index summaries consistently use culture-independent decimal and thousands separators to match JSON-facing expectations.

## 日本語

- **人間向け CLI index 出力が invariant な数値形式を使うようになりました (#1662)** — progress と index summary は JSON の期待と揃うよう、culture に依存しない小数点と桁区切りを一貫して使います。
20 changes: 20 additions & 0 deletions changelog.d/unreleased/1835.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
category: added
issues:
- 1835
affected:
- src/CodeIndex/Cli/ConsoleUi.cs
- src/CodeIndex/Cli/IndexCommandRunner.Parse.cs
- src/CodeIndex/Cli/IndexCommandRunner.FullScan.cs
- src/CodeIndex/Cli/IndexCommandRunner.Update.cs
- src/CodeIndex/Cli/CliFlagSchema.cs
- USER_GUIDE.md
---

## English

- **Added long index completion notifications (#1835)** — `cdidx index` now supports `--notify=<auto|bell|osc9|desktop|none>` plus `CDIDX_NOTIFY`, with quiet/json-safe suppression and a five-second threshold for human runs.

## 日本語

- **長い index 完了通知を追加しました (#1835)** — `cdidx index` は `--notify=<auto|bell|osc9|desktop|none>` と `CDIDX_NOTIFY` に対応し、人間向け実行では5秒以上の run だけ通知し、quiet/json では抑制します。
17 changes: 17 additions & 0 deletions changelog.d/unreleased/1963.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
category: added
issues:
- 1963
affected:
- src/CodeIndex/Cli/ConsoleUi.cs
- src/CodeIndex/Cli/IndexCommandRunner.FullScan.cs
- USER_GUIDE.md
---

## English

- **Added post-index next-step guidance (#1963)** — successful human full-scan output now prints concise search, definition, MCP, database, exclusion, and language-summary next steps while preserving JSON and quiet output.

## 日本語

- **index 後の next-step guidance を追加しました (#1963)** — 成功した人間向け full-scan 出力に、search / definition / MCP / database / 除外設定 / language summary の簡潔な案内を出し、JSON と quiet 出力は維持します。
1 change: 1 addition & 0 deletions src/CodeIndex/Cli/CliFlagSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ private static IReadOnlyList<CliFlag> BuildAll()
new() { Name = "--silent", Description = "Alias for --quiet", Commands = Set(AllCommands.ToArray()) },
new() { Name = "--profile", Description = "Emit SQL timing and EXPLAIN QUERY PLAN profile JSON after the normal result", Commands = Set(ProfileCommands) },
new() { Name = "--verbose", Description = "Emit query debug diagnostics to stderr, or _debug JSON when combined with --json", Commands = Set(VerboseQueryCommands.Concat(new[] { "index" }).ToArray()) },
new() { Name = "--notify", ValuePlaceholder = "<auto|bell|osc9|desktop|none>", Description = "Signal long index completion; desktop currently emits OSC 9 terminal notification", Commands = Set("index") },
new() { Name = "--slow-query-ms", ValuePlaceholder = "<n>", Description = "Log profiled SQL statements at or above this millisecond threshold", Commands = Set(ProfileCommands) },
new() { Name = "--trace", ValuePlaceholder = "<none|stderr|file>", Description = "Emit one structured JSON query trace line to stderr or a daily log file", Commands = Set(TraceCommands) },
new() { Name = "--limit", ValuePlaceholder = "<n>", Description = "Max results", Commands = Set(LimitCapableCommands) },
Expand Down
103 changes: 98 additions & 5 deletions src/CodeIndex/Cli/ConsoleUi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ public enum DurationOutputFormat
Hms = 2,
}

public enum CompletionNotificationMode
{
Auto = 0,
None = 1,
Bell = 2,
Osc9 = 3,
}

/// <summary>
/// Console UI helpers: spinner, progress bar, banner, and easter egg messages.
/// コンソールUIヘルパー: スピナー、プログレスバー、バナー、イースターエッグメッセージ。
Expand All @@ -60,7 +68,7 @@ public static class ConsoleUi

private static readonly (string Command, string Usage)[] CommandUsageLines =
[
("index", "cdidx index <projectPath> [--db <path>] [--rebuild] [--optimize] [--verbose] [--dry-run] [--force] [--quiet] [--json] [--memory-trace] [--duration-format <auto|seconds|hms>] [--max-file-bytes <bytes>] [--follow-symlinks <none|internal|all>] [--include-symbol-kind <kind>[,<kind>]] [--exclude-symbol-kind <kind>[,<kind>]] [--watch [--debounce <ms>]]"),
("index", "cdidx index <projectPath> [--db <path>] [--rebuild] [--optimize] [--verbose] [--dry-run] [--force] [--quiet] [--json] [--memory-trace] [--duration-format <auto|seconds|hms>] [--notify <auto|bell|osc9|desktop|none>] [--max-file-bytes <bytes>] [--follow-symlinks <none|internal|all>] [--include-symbol-kind <kind>[,<kind>]] [--exclude-symbol-kind <kind>[,<kind>]] [--watch [--debounce <ms>]]"),
("hooks", "cdidx hooks <install|uninstall|status> [--project <path>] [--force] [--json]"),
("backfill-fold", "cdidx backfill-fold [--db <path>] [--dry-run] [--json]"),
("optimize", "cdidx optimize [--db <path>] [--json]"),
Expand Down Expand Up @@ -130,6 +138,12 @@ internal static string Counted(int count, string singular, string? plural = null
return $"{formatted} {(count == 1 ? singular : plural ?? singular + "s")}";
}

internal static string FormatNumber(long value, string format = "N0")
=> value.ToString(format, CultureInfo.InvariantCulture);

internal static string FormatNumber(int value, string format = "N0")
=> value.ToString(format, CultureInfo.InvariantCulture);

internal static string FoundSummary(int count, string singular, string? plural = null)
{
plural ??= singular + "s";
Expand Down Expand Up @@ -367,6 +381,9 @@ public static string[] GetSpinnerFrames(string? easterEgg)
// Track last progress line length for clearing / クリア用に最後のプログレス行の長さを記録
private static int _lastProgressLineLength;
private static bool _asciiOutputForced;
private static bool _widthDetectionFailed;
private static bool _widthDetectionTraceWritten;
private static bool _traceWidthDetectionFailures;

/// <summary>
/// Set progress bar spinner theme (reuses GetSpinnerFrames).
Expand Down Expand Up @@ -424,7 +441,9 @@ internal static string FormatProgressLine(int current, int total, int windowWidt
{
const int barWidth = 32;
var pct = (double)current / total;
var percentAndCounts = $"{pct * 100,5:F1}% [{current:N0}/{total:N0}]";
var percentAndCounts = string.Create(
CultureInfo.InvariantCulture,
$"{pct * 100,5:F1}% [{current:N0}/{total:N0}]");

if (useUnicodeGlyphs && windowWidth < 40)
return percentAndCounts;
Expand Down Expand Up @@ -507,6 +526,55 @@ public static void PrintBanner()
Console.WriteLine(banner);
}

public static void PrintIndexCompleteSummary(
string projectRoot,
string resolvedDbPath,
bool incremental,
int filesScanned,
IReadOnlyDictionary<string, int> languageCounts)
{
Console.WriteLine(incremental ? "Next steps (incremental):" : "Next steps:");
Console.WriteLine(" - Search code: cdidx search \"authenticate\" --path src/");
Console.WriteLine(" - Find a definition: cdidx definition SymbolName");
Console.WriteLine($" - Start MCP: cdidx mcp --db {QuoteForDisplay(resolvedDbPath)}");
Console.WriteLine($" - Database: {resolvedDbPath}");
Console.WriteLine(" - Exclude paths with .gitignore or .cdidxignore, then rerun cdidx index .");
Console.WriteLine($" - Scanned {Counted(filesScanned, "file", format: "N0")} under {projectRoot}");
if (languageCounts.Count > 0)
{
var summary = string.Join(
", ",
languageCounts
.OrderByDescending(static pair => pair.Value)
.ThenBy(static pair => pair.Key, StringComparer.Ordinal)
.Take(6)
.Select(static pair => $"{pair.Key} {pair.Value.ToString("N0", CultureInfo.InvariantCulture)}"));
Console.WriteLine($" - Languages: {summary}");
}
Console.WriteLine();
}

public static void EmitCompletionNotification(CompletionNotificationMode mode, string message)
{
var resolved = mode == CompletionNotificationMode.Auto
? ShouldUseInteractiveConsole() ? CompletionNotificationMode.Bell : CompletionNotificationMode.None
: mode;
if (resolved == CompletionNotificationMode.None)
return;

var safeMessage = message.Replace('\r', ' ').Replace('\n', ' ');
if (resolved == CompletionNotificationMode.Osc9)
Console.Error.Write($"\u001b]9;{safeMessage}\a");
else
Console.Error.Write('\a');
Console.Error.Flush();
}

private static string QuoteForDisplay(string value)
=> value.IndexOfAny([' ', '\t', '"']) < 0
? value
: $"\"{value.Replace("\"", "\\\"", StringComparison.Ordinal)}\"";

// --- Easter eggs / イースターエッグ ---

/// <summary>
Expand Down Expand Up @@ -799,6 +867,7 @@ private static void PrintFlagReference(Action<string> WriteHelpLine)
Console.WriteLine(" --memory-trace Include phase memory samples in index JSON output");
Console.WriteLine(" --quiet, -q, --silent Suppress informational stderr output; errors still print (also honors CDIDX_QUIET=1)");
Console.WriteLine(" --duration-format <format> Index elapsed time format: `auto` (default), `seconds`, or `hms`; JSON keeps raw elapsed_ms");
WriteHelpLine(" --notify <mode> Long index completion signal: auto, bell, osc9, desktop, or none (also honors CDIDX_NOTIFY; quiet/json suppress it)");
WriteHelpLine(" --max-file-bytes <bytes> Index only files up to this size (default: 4MiB; also honors CDIDX_MAX_FILE_BYTES; accepts K/M/G suffixes)");
WriteHelpLine(" --parallelism <n> Full-scan extraction workers (default: CPU count capped at 16; also honors CDIDX_INDEX_PARALLELISM)");
WriteHelpLine(" --follow-symlinks <mode> Directory symlink policy: none (default), internal, or all");
Expand Down Expand Up @@ -2027,6 +2096,10 @@ private static bool IsUnicodeLocale(string locale)

internal static bool IsAsciiOutputForced() => _asciiOutputForced;

internal static bool WidthDetectionFailed => _widthDetectionFailed;

internal static void SetWidthDetectionTracing(bool enabled) => _traceWidthDetectionFailures = enabled;

/// <summary>
/// Get console window width safely (some environments throw IOException).
/// コンソール幅を安全に取得する(一部環境ではIOExceptionが発生する)。
Expand All @@ -2039,12 +2112,32 @@ internal static int GetWindowWidth()
try
{
var w = Console.WindowWidth;
return w > 0 ? w : 80;
if (w > 0)
return w;
}
catch (IOException ex)
{
return GetFallbackWindowWidth(ex);
}
catch
catch (NotSupportedException ex)
{
return 80;
return GetFallbackWindowWidth(ex);
}

return GetFallbackWindowWidth(null);
}

private static int GetFallbackWindowWidth(Exception? exception)
{
_widthDetectionFailed = true;
if (_traceWidthDetectionFailures && !_widthDetectionTraceWritten)
{
var suffix = exception == null ? string.Empty : $" ({exception.GetType().Name}: {exception.Message})";
Console.Error.WriteLine($"cdidx: console width detection failed; using COLUMNS or 80 columns{suffix}");
_widthDetectionTraceWritten = true;
}

return TryGetColumnsEnvironmentWidth(out var columnsWidth) ? columnsWidth : 80;
}

private static bool TryGetColumnsEnvironmentWidth(out int width)
Expand Down
Loading
Loading