Skip to content

Commit 1976c5c

Browse files
authored
Merge pull request #2738 from Widthdom/fix-issue1708-1709-1710
Add workspace manifests and active workspace commands
2 parents 1b5aa22 + 11ee938 commit 1976c5c

15 files changed

Lines changed: 627 additions & 2 deletions

DEVELOPER_GUIDE.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ The lock files for projects with zero direct `PackageReference` entries (e.g. `t
116116
| DTOs | `Models/FileRecord.cs`, `Models/ChunkRecord.cs`, `Models/SymbolRecord.cs`, `Models/ReferenceRecord.cs` | Records shared by indexing, storage, query, and MCP layers. |
117117
| Tests | `tests/CodeIndex.Tests/*Tests.cs`, `TestProjectHelper.cs`, `TestConsoleLock.cs` | Focused unit/integration coverage for chunking, extraction, DB reads/writes, CLI behavior, MCP behavior, git helpers, and shared test harness utilities. |
118118

119+
### Workspaces
120+
121+
`cdidx.workspace.json` and `.cdidx-workspace.json` declare monorepo members without adding a YAML dependency. The supported schema is additive: `members` is an array of member paths relative to the manifest directory, `index_strategy` is `per_member` or `single`, `default_db_name` overrides `codeindex.db`, and `shared_ignores` is reserved for shared ignore policy. `cdidx workspace list` and `cdidx workspace status` report member DB paths.
122+
123+
`cdidx workspace use <name>` writes the active workspace to the per-user config directory. Query DB resolution keeps existing precedence: explicit `--db`, then explicit `--data-dir` / `CDIDX_DATA_DIR`, then active workspace state, then ancestor/CWD discovery.
124+
119125
### Observability
120126

121127
CodeIndex exposes an opt-in `ActivitySource` named `CodeIndex`. MCP JSON-RPC frames create `mcp.request` server spans and SQLite commands routed through tracked database helpers create `db.query` spans. MCP callers can pass W3C trace context as `params._meta.traceparent`; when present, the MCP span uses that trace as its parent. No exporter dependency is bundled, so spans are emitted only when the host process installs an OpenTelemetry/Diagnostics listener.

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ Output controls:
7979
| Need | Option |
8080
|---|---|
8181
| Owner-only persistent stderr logs on POSIX | Global tool stderr logs are forced to `0600` permissions on every open, including existing date-stamped log files. Use `--log-format text|json`, `--log-retain-count <N>`, `--log-max-size-mb <N>`, or the matching `CDIDX_LOG_*` environment variables to make lifecycle logs JSONL-friendly and rotate them for aggregation. |
82-
| Checked-in configuration | Use `.cdidx/config.json` for repository defaults such as `search.limit`, `search.snippet_lines`, and `search.max_line_width`; run `cdidx validate-config` to validate the discovered file. |
82+
| Checked-in configuration | Use `.cdidx/config.json` for repository defaults such as `search.limit`, `search.snippet_lines`, and `search.max_line_width`; run `cdidx validate-config` to validate the discovered file and `cdidx config show` to inspect precedence. |
83+
| Workspaces | Use `cdidx.workspace.json` or `.cdidx-workspace.json` to declare monorepo members, `cdidx workspace list` to inspect them, and `cdidx workspace use <name>` / `cdidx workspace current` for a persisted active workspace. |
8384
| ASCII-only terminal output | Use `--ascii`, `CDIDX_ASCII=1`, `NO_UNICODE`, `TERM=dumb`, accessibility env hints, or a non-UTF-8 locale. Spinners use pipe, slash, dash, and backslash frames; progress bars use `#` / `-`; very narrow terminals fall back to percentage-only progress. |
8485
| Color and terminal capability | `--color auto` emits ANSI only for capable interactive terminals; `TERM=dumb`, `CI=true`, missing Unix terminal hints, `NO_COLOR`, or `CLICOLOR=0` disable ANSI/progress control sequences. `--palette basic|256|truecolor` can override the `COLORTERM` / `TERM` color-depth detection. |
8586
| UTF-8 JSON pipelines | CLI `--json` output is written as UTF-8 without a BOM and never includes ANSI escape sequences, even when color is forced for human output. |
@@ -329,7 +330,8 @@ extractor fixture を確認できます。詳細は
329330
| 目的 | option / 動作 |
330331
|---|---|
331332
| POSIX の persistent stderr log を owner-only にする | global tool stderr log は開くたびに `0600` 権限へ補正され、既存の日付付き log file も同じ扱いになります。 |
332-
| checked-in configuration | repository 既定値には `.cdidx/config.json` を使えます。例: `search.limit``search.snippet_lines``search.max_line_width`。検出された file は `cdidx validate-config` で検証できます。 |
333+
| checked-in configuration | repository 既定値には `.cdidx/config.json` を使えます。例: `search.limit``search.snippet_lines``search.max_line_width`。検出された file は `cdidx validate-config` で検証でき、`cdidx config show` で優先順位を確認できます。 |
334+
| workspaces | monorepo member は `cdidx.workspace.json` または `.cdidx-workspace.json` で宣言し、`cdidx workspace list` で確認できます。`cdidx workspace use <name>` / `cdidx workspace current` は永続 active workspace を扱います。 |
333335
| ASCII-only 端末で崩れない表示にする | `--ascii``CDIDX_ASCII=1``NO_UNICODE``TERM=dumb`、accessibility 系の環境変数、非 UTF-8 locale を使います。スピナーは pipe、slash、dash、backslash の frame、進捗バーは `#` / `-` になり、幅が非常に狭い端末では percentage-only になります。 |
334336
| color と端末 capability | `--color auto` は対応する interactive terminal でだけ ANSI を出力します。`TERM=dumb``CI=true`、Unix で端末 hint が無い場合、`NO_COLOR``CLICOLOR=0` では ANSI / progress 制御シーケンスを抑止します。`--palette basic|256|truecolor``COLORTERM` / `TERM` による color-depth 判定を上書きできます。 |
335337
| UTF-8 JSON pipeline | CLI の `--json` 出力は BOM なし UTF-8 で書き出され、human output 向けに色を強制していても ANSI escape sequence を含みません。 |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
category: added
3+
issues:
4+
- 1708
5+
---
6+
7+
## English
8+
9+
- Added JSON workspace manifests (`cdidx.workspace.json` / `.cdidx-workspace.json`) and `cdidx workspace list/status` for monorepo member topology.
10+
11+
## 日本語
12+
13+
- monorepo member 構成を宣言する JSON workspace manifest (`cdidx.workspace.json` / `.cdidx-workspace.json`) と `cdidx workspace list/status` を追加しました。
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
category: added
3+
issues:
4+
- 1709
5+
---
6+
7+
## English
8+
9+
- Added `cdidx config show` to report discovered workspace config, active workspace state, and config precedence.
10+
11+
## 日本語
12+
13+
- 検出された workspace config、active workspace state、config 優先順位を表示する `cdidx config show` を追加しました。
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
category: added
3+
issues:
4+
- 1710
5+
---
6+
7+
## English
8+
9+
- Added persisted active workspace state with `cdidx workspace use/current` and query DB resolution after explicit flags and environment settings.
10+
11+
## 日本語
12+
13+
- `cdidx workspace use/current` による永続 active workspace state と、明示 flag / 環境設定の後に使われる query DB 解決を追加しました。

examples/cdidx.workspace.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"members": [
3+
"src/CodeIndex",
4+
"tests/CodeIndex.Tests"
5+
],
6+
"index_strategy": "per_member",
7+
"default_db_name": "codeindex.db",
8+
"shared_ignores": [
9+
"bin/",
10+
"obj/"
11+
]
12+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System.Text.Json;
2+
using CodeIndex.Indexer;
3+
4+
namespace CodeIndex.Cli;
5+
6+
internal sealed record ActiveWorkspaceState(string Name, string Root, string DbPath);
7+
8+
internal static class ActiveWorkspace
9+
{
10+
internal const string EnvironmentVariable = "CDIDX_ACTIVE_WORKSPACE";
11+
12+
internal static string StatePath
13+
{
14+
get
15+
{
16+
var configHome = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME");
17+
var root = string.IsNullOrWhiteSpace(configHome)
18+
? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config")
19+
: configHome;
20+
return Path.Combine(root, "cdidx", "active.json");
21+
}
22+
}
23+
24+
internal static ActiveWorkspaceState? Load()
25+
{
26+
var envPath = Environment.GetEnvironmentVariable(EnvironmentVariable);
27+
if (!string.IsNullOrWhiteSpace(envPath))
28+
return new ActiveWorkspaceState("env", Path.GetDirectoryName(Path.GetFullPath(envPath)) ?? Environment.CurrentDirectory, Path.GetFullPath(envPath));
29+
30+
var path = StatePath;
31+
if (!File.Exists(LongPath.EnsureWindowsPrefix(path)))
32+
return null;
33+
34+
try
35+
{
36+
using var document = JsonDocument.Parse(File.ReadAllText(LongPath.EnsureWindowsPrefix(path)));
37+
var root = document.RootElement;
38+
var name = ReadString(root, "name") ?? "default";
39+
var workspaceRoot = ReadString(root, "root") ?? Environment.CurrentDirectory;
40+
var dbPath = ReadString(root, "db_path");
41+
if (string.IsNullOrWhiteSpace(dbPath))
42+
return null;
43+
return new ActiveWorkspaceState(name, Path.GetFullPath(workspaceRoot), Path.GetFullPath(dbPath));
44+
}
45+
catch (Exception ex) when (ex is JsonException or IOException or UnauthorizedAccessException or ArgumentException or NotSupportedException)
46+
{
47+
return null;
48+
}
49+
}
50+
51+
internal static void Save(ActiveWorkspaceState state)
52+
{
53+
Directory.CreateDirectory(Path.GetDirectoryName(StatePath)!);
54+
var payload = new ActiveWorkspaceState(state.Name, Path.GetFullPath(state.Root), Path.GetFullPath(state.DbPath));
55+
File.WriteAllText(StatePath, JsonSerializer.Serialize(payload, ProgramRunner.CreateDefaultJsonOptions()));
56+
}
57+
58+
private static string? ReadString(JsonElement element, string name)
59+
=> element.TryGetProperty(name, out var value) && value.ValueKind == JsonValueKind.String ? value.GetString() : null;
60+
}

src/CodeIndex/Cli/CdidxConfigFile.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,32 @@ internal static int RunValidate(string[] args, JsonSerializerOptions jsonOptions
360360
return CommandExitCodes.Success;
361361
}
362362

363+
internal static int RunShow(string[] args, JsonSerializerOptions jsonOptions)
364+
{
365+
var json = args.Contains("--json", StringComparer.Ordinal);
366+
args = args.Where(a => a != "--json").ToArray();
367+
if (args.Length > 0)
368+
return CommandErrorWriter.WriteJsonOrHuman(json, jsonOptions, "config show does not accept positional arguments.", CommandExitCodes.UsageError, "run `cdidx config show` from the workspace whose config should be shown.");
369+
370+
var path = FindConfigFile(Environment.CurrentDirectory);
371+
var active = ActiveWorkspace.Load();
372+
var payload = new ConfigShowJsonResult(
373+
path,
374+
active,
375+
["cli", "env", "config_file", "active_workspace", "cwd_default"],
376+
[ProjectConfigRelativePath, FileName]);
377+
if (json)
378+
Console.WriteLine(JsonSerializer.Serialize(payload, jsonOptions));
379+
else
380+
{
381+
Console.WriteLine($"Config path : {path ?? "(none)"}");
382+
Console.WriteLine($"Active workspace : {(active == null ? "(none)" : active.Name + " -> " + active.DbPath)}");
383+
Console.WriteLine("Precedence : CLI > env > config file > active workspace > CWD default");
384+
}
385+
386+
return CommandExitCodes.Success;
387+
}
388+
363389
private static bool ValidateOptionalObject(JsonElement root, string key, IReadOnlyList<string> knownKeys, string path, out string? error)
364390
{
365391
error = null;

src/CodeIndex/Cli/ConsoleUi.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ private static readonly (string Command, string Usage)[] CommandUsageLines =
8282
("inspect", "cdidx inspect <query>|--query <query>|-- <query> [--db <path>] [--json] [--verbose] [--limit <n>|--top <n>] [--lang <lang>] [--path <glob>] [--exclude-path <glob>] [--exclude-tests] [--body] [--max-line-width <n>] [--exact|--exact-name]"),
8383
("outline", "cdidx outline <path> [--db <path>] [--json] [--verbose]"),
8484
("status", "cdidx status [--db <path>] [--json] [--verbose] [--check[=workspace,fold,graph,issues,hotspot,csharp,sql,newer]] [--stale-after <duration>] [--explain <field>] [--log-path] [--config] [--check-updates]"),
85+
("workspace", "cdidx workspace <list|status|use|current> [name] [--json]"),
86+
("config", "cdidx config show [--json]"),
8587
("validate-config", "cdidx validate-config"),
8688
("db", "cdidx db --integrity-check [--db <path>] [--json]"),
8789
("diff", "cdidx diff <db1> <db2> [--json] [--summary-only] [--detailed] [--limit <n>]"),
@@ -744,6 +746,8 @@ private static void PrintCommandSummary()
744746
Console.WriteLine(" inspect <query> Bundle definition, graph, and nearby symbol context");
745747
Console.WriteLine(" outline <path> Show a file outline ordered by line, start column, kind, and name");
746748
Console.WriteLine(" status Show database statistics; add --check for freshness, --config for effective config, --explain <field> for readiness, or --log-path for logs");
749+
Console.WriteLine(" workspace List manifest members and manage the active workspace");
750+
Console.WriteLine(" config show Show resolved workspace config and precedence");
747751
Console.WriteLine(" upgrade Check for and install the latest release via install.sh");
748752
Console.WriteLine(" validate-config Validate .cdidx/config.json or .cdidxrc.json");
749753
Console.WriteLine(" db --integrity-check Run SQLite `PRAGMA integrity_check` and report findings");

src/CodeIndex/Cli/DbPathResolver.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public static class DbPathResolver
1414
public const string DataDirEnvironmentVariable = "CDIDX_DATA_DIR";
1515
public const string DataDirSourceFlag = "flag";
1616
public const string DataDirSourceEnv = "env";
17+
public const string DataDirSourceActiveWorkspace = "active_workspace";
1718
public const string DataDirSourceXdg = "xdg";
1819
public const string DataDirSourceWorkspace = "workspace";
1920

@@ -68,6 +69,10 @@ internal static DbPathResolution ResolveDataDirForQuery(string workspacePath, st
6869
if (!string.IsNullOrWhiteSpace(environmentDataDir))
6970
return BuildDataDirResolution(environmentDataDir, DataDirSourceEnv);
7071

72+
var active = ActiveWorkspace.Load();
73+
if (active != null)
74+
return new DbPathResolution(active.DbPath, Path.GetDirectoryName(active.DbPath), DataDirSourceActiveWorkspace);
75+
7176
if (!string.IsNullOrWhiteSpace(xdgDataHome))
7277
{
7378
var ancestorXdgDataDir = TryResolveOutermostAncestorXdgDataDir(fullWorkspacePath, xdgDataHome);

0 commit comments

Comments
 (0)