Conversation
A developer shouldn't have to know 'ckb init' and 'ckb index' exist just to get an MCP server running. 'ckb setup' now does the whole job for project-scope tools: it inits .ckb/ if missing and builds the index if none exists yet, using the same code paths as 'ckb init'/'ckb index' (not a shell-out). Missing SCIP indexers, undetectable languages, or indexing failures are reported as one-line notes and never fail setup — Git-based features still work without SCIP. --no-index opts out. That required index.go's runIndex to stop calling os.Exit directly: it's refactored into performIndex(), which returns a structured indexResult instead, so both 'ckb index' and 'ckb setup' can react to the same outcomes (indexed / up-to-date / indexer missing / language undetected / ...) without one call path killing the process out from under the other. Generated MCP configs now include --watch by default (opt out with --no-watch) so the server keeps the index fresh for the session, instead of requiring a separate 'ckb mcp --watch' flag nobody would think to add. Along the way, fixed writeOpenCodeConfig's npx branch, which rebuilt the command array from scratch and silently dropped --preset/--watch for OpenCode + npx setups. Adds Codex CLI as a supported tool (global-only — Codex reads ~/.codex/config.toml, and there's no project-level config location documented anywhere in this repo). Since config.toml is a file Codex itself owns and may hand-edit, the merge is a surgical textual upsert of just the [mcp_servers.ckb] table rather than a decode/re-encode round-trip through a TOML library, which would reformat the whole file and drop comments. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codex CLI supports trusted project-local .codex/config.toml in addition to the user-global ~/.codex/config.toml, so marking it SupportsProject: false was wrong — it forced --global and made `ckb setup --tool=codex` skip the whole init/index auto-readiness path that every other project-scope tool gets. Default (non --global) setup now writes <repo>/.codex/config.toml; --global still writes the user-global file. README/npm README updated (dropped the "no per-project config" claim) and now document how `ckb mcp` resolves the repo from cwd when running under the global config. Also hardens the textual TOML upsert used to merge CKB's [mcp_servers.ckb] table into Codex's config.toml: - header matching is now regex/parser-based, tolerant of inline comments ([mcp_servers.ckb] # ...) and quoted keys ([mcp_servers."ckb"]) — previously an exact string match, so either form was treated as "table not found" and a second, TOML-illegal duplicate header got appended - only the bare command/args keys are rewritten; any subtable of ckb (e.g. [mcp_servers.ckb.env], holding user env vars) is left completely untouched instead of being deleted by the old next-header-wins block boundary - CRLF line endings are preserved if the existing file uses them - the result is validated as TOML before writing; an unparsable existing file is left untouched with manual-edit instructions instead of being blindly edited - writes are atomic (temp file + rename) Confirms Codex review findings P0 (scope) and P1 (TOML header/comment false-negative); see /tmp/codex-review/setup-zero-config.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
runWatchLoop silently skipped every tick when index metadata didn't
exist yet ("no metadata yet, skip"), so a project that had never been
indexed — including one where 'ckb setup' skipped indexing because it
was interrupted or the indexer wasn't installed at the time — would
never get indexed by --watch either, no matter how long the MCP server
ran. Language is now resolved via the saved project.json, or a fresh
auto-detect when that doesn't exist yet, so a genuinely first-time
index can be built from a watch tick.
Also fixes an unbounded retry: on a stale index with an indexer that's
missing or persistently failing, the old loop retried on every single
tick (every 10s by default) forever, spawning the same failing process
indefinitely and spamming the log. triggerReindex now returns a
sentinel (errIndexerUnavailable / errRepoTooLarge) for permanent
conditions — missing indexer binary, undetectable/ambiguous language,
or a repo over the SCIP auto-index threshold — which disables watch
mode for the rest of the process after logging once. Transient
failures (indexer crashes, etc.) get exponential backoff
(watchBackoff, capped at 10 min) and disable themselves after 5
consecutive failures rather than retrying forever; a restart of the
MCP server re-enables it.
watchBackoff is a pure function so the backoff policy itself is
unit-tested without needing a fake clock/ticker.
Confirms Codex review finding P2 (watch retry loop never gives up) and
the "does watch actually index when metadata is missing" question
raised for the setup-blocks-on-indexing fix; see
/tmp/codex-review/setup-zero-config.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ensureProjectReady() (init + index) ran before configureTool() wrote the MCP config, so a slow indexer — or a repo just under the 50,000 file auto-SCIP threshold, or any indexer that simply takes a while — blocked 'ckb setup' from ever reaching the config-write step. Ctrl-C during that wait, or any indexing failure, meant no config at all, contradicting the low-friction goal of this change: the agent should be usable (Git-based features at minimum) the moment config is written, independent of how indexing goes. configureTool now runs first; ensureProjectReady (init + index) runs after, with a note that it's safe to Ctrl-C at that point since the config is already saved. That message is only accurate because the prior commit (fix(mcp): watch loop builds missing index...) made --watch actually build a missing/interrupted index on its own tick instead of silently skipping when there's no metadata yet. Confirms Codex review finding P1 (setup blocks on foreground indexing before config is written); see /tmp/codex-review/setup-zero-config.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… repo
ensureCkbInitialized called runInit(nil, nil), which read the
package-level initNoActivate flag var — always false unless the
'ckb init' CLI command itself set it via --no-activate. Since 'ckb
setup' never touches that flag, every auto-init it triggered called
registry.SetDefault(), silently making whatever project you ran
'ckb setup' in your new global default repo — clobbering the default
for every other session/terminal using CKB on a different project.
Refactored runInit into a thin cobra wrapper around runInitCore(opts
initOptions), so callers other than the CLI pass their intent
explicitly instead of relying on shared global flag state.
ensureCkbInitialized now calls runInitCore(initOptions{NoActivate:
true}) — the repo still gets registered (so it shows up in 'ckb repo
list' etc.), it just doesn't become active. 'ckb init' itself is
unaffected; its flags still flow into the same options struct.
Confirms Codex review finding P2 (setup changes global default repo);
see /tmp/codex-review/setup-zero-config.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codex spawns [mcp_servers.*] commands directly (no shell), and npx on Windows is npx.cmd, which only resolves through cmd.exe — this repo's own Windows guidance already says as much for every other tool's config, but the Codex writer always emitted command = "npx" verbatim, which would silently fail to launch on Windows. No other writer in this repo applies this wrapping automatically either (grepped for it — none do); they all rely on the README's manual "Windows" section. Rather than bolt automatic wrapping onto runSetup's shared ckbCommand/ckbArgs (which the OpenCode writer specifically re-derives its own npx array from when useNpx is set, so mutating the shared command there would double-wrap), this is scoped narrowly to the Codex writer via a small pure function, codexWindowsWrap(goos, command, args), so it's unit-testable without depending on the host OS and doesn't touch any other tool's output. README/npm README gain a TOML-flavored example in the Windows section (previously only JSON, which doesn't transfer directly to Codex's config.toml) and a note that --npx handles this automatically. Confirms Codex review's "Windows guidance says npx needs cmd /c... while the Codex writer always emits command = npx" finding; see /tmp/codex-review/setup-zero-config.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
triggerReindex (the ckb mcp --watch background loop) ran indexer.Command verbatim, bypassing everything performIndex does to build a real command: C++'s --compdb-path flag, Ruby's bundle-aware prefix, PHP's prerequisite check, and a custom scip.indexPath's --output flag. That made setup+watch silently fail to produce a usable SCIP index for C++ repos and for any repo with a non-default scip.indexPath, while still reporting success. Extract buildIndexPlan (plus resolveIndexPath/resolveIndexLanguage) as the single place that resolves language, manifest, and the exact indexer command/dir/output-path. performIndex and triggerReindex both call it now, so the two paths can't drift again. triggerReindex also verifies the resolved index file actually exists after the indexer exits 0, before writing any metadata — previously a silent no-op indexer (or one whose --output wasn't honored) was recorded as a successful reindex. Tests cover a fake indexer for: a custom scip.indexPath output (command gets --output, file lands at the configured path) and a missing output file (indexer exits 0, triggerReindex still reports failure and writes no metadata). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
writeCodexConfig always wrote through writeFileAtomic with a hardcoded 0644, so re-running 'ckb setup --tool=codex' (global or project) against a config.toml the user had locked down to 0600 silently widened it back to world-readable — a real risk since [mcp_servers.*.env] subtables can hold secrets other tools stash alongside CKB's entry. writeCodexConfig now takes the scope (global bool, already known at the configureTool call site) and picks the file mode as: existing file's own mode if one exists (never widened or narrowed), else 0600 for a freshly created global ~/.codex/config.toml, else 0644 for a freshly created project .codex/config.toml (matching every other project config CKB writes). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rs.ckb
Valid TOML like:
[mcp_servers]
ckb = { command = "old", args = ["mcp"] }
or the fully dotted equivalent (mcp_servers.ckb.command = ...) has no
[mcp_servers.ckb] header line. upsertTOMLTable only recognizes that header
shape, so it didn't find an existing ckb table and appended one — but TOML
forbids extending an inline table with a later header, so the generated
file failed writeCodexConfig's own validate-before-write check and setup
aborted with a raw TOML parse error instead of a clear explanation.
writeCodexConfig now decodes the existing file and checks two independent
signals: does mcp_servers.ckb resolve to a value at all (BurntSushi folds
headers/inline-tables/dotted-keys into the same map shape), and does a
[mcp_servers.ckb]-shaped header line actually exist in the text. When ckb
exists per the parsed structure but no such header line does, the entry
must be inline-table or dotted-key form — refuse to touch the file and
return an error with the exact [mcp_servers.ckb] snippet to paste by hand,
so setup exits non-zero for Codex specifically with an actionable message
instead of a cryptic parse failure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…odex TOML upsertTOMLTable replaced command/args by filtering the existing bare-key block line-by-line on key name. Two cases broke: - A trailing inline comment on the replaced line (command = "npx" # pinned for CI) was dropped along with the whole line — the surgical-edit contract was "preserve everything except command/args," but the comment riding on that same line got thrown away too. - A multiline args array (args = [\n "mcp",\n "--watch",\n]) only had its opening line recognized as "the args key" — the continuation lines don't look like "key = value", so they fell through as ordinary "kept" lines, left stranded next to the newly-inserted single-line args and corrupting the table. Added tomlValueSpanEnd (bracket-depth line scan, ignoring quoted strings and comments, that only continues past the key's own line if it left an unclosed "[") to find the whole span a key's value occupies, and tomlTrailingComment to extract a comment from the span's last line and reattach it to the replacement. Both command and args now go through the same span-aware removal instead of a single-line key-name filter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
'ckb setup' ran a full foreground SCIP index for every project-scope setup
(any repo under the 50k-file threshold), even though the generated MCP
config already runs with --watch by default. That meant every setup paid
the indexer's full cost (seconds to tens of minutes) before returning,
despite the watch loop being fully capable of building the same index on
its own once the AI tool actually starts the server.
ensureProjectReady now skips the foreground index when watch mode is
enabled (the default), printing a one-line note that indexing happens in
the background when the agent starts, plus how to force it
('ckb index'). --no-watch has no watch loop to fall back on, so it still
indexes in the foreground — otherwise nothing would ever build it. A new
--index-now flag forces the foreground index regardless of --watch, for
anyone who wants it ready before setup exits.
Complementary fix: runWatchLoop only checked staleness on ticker.C, so the
first check waited a full poll interval (up to 5 minutes) after the MCP
server started — a real regression for a project that now skips indexing
at setup time. Extracted the per-tick logic into watchTick and call it once
immediately before entering the ticker loop, so a fresh project gets
indexed the moment the agent starts the server, not minutes later.
Updated the setup command's help text and the README/npm README setup
walkthroughs to describe the non-blocking default and --index-now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
🟢 Change Impact Analysis
Blast Radius: 0 modules, 0 files, 0 unique callers 📝 Changed Symbols (7)
Recommendations
Generated by CKB |
CKB Analysis
Risk factors: Medium-sized PR with 11 files • High churn: 3365 lines changed • Touches 10 hotspot(s) 👥 Suggested: @lisa.welsch1985@gmail.com (91%), @talantyyr@gmail.com (45%), @lisa@tastehub.io (27%)
🎯 Change Impact Analysis · 🟢 LOW · 7 changed → 0 affected
Symbols changed in this PR:
Recommendations:
💣 Blast radius · 0 symbols · 4 tests · 0 consumersTests that may break:
🔥 Hotspots · 10 volatile files
📦 Modules · 1 at risk
📊 Complexity · 4 violations
💡 Quick wins · 10 suggestions
📚 Stale docs · 203 broken references
Generated by CKB · Run details |
🔐 Security Audit Results❌ Security gate FAILED - 4 HIGH severity gosec finding(s)
🛡️ SAST AnalysisFound 28 issue(s) across 1 scanner(s) DetailsGosec (28 findings)
📦 Dependency VulnerabilitiesFound 21 vulnerability(ies) across 2 scanner(s) DetailsTrivy (14 findings)
OSV-Scanner (7 findings)
📜 License IssuesFound 142 non-permissive license(s) Details
Generated by CKB Security Audit | View Details | Security Tab |
CKB Review: 🟡 WARN — 61/10011 files (+3365 changes) · 4 modules ·
Top Risks
Findings (10 actionable, 23 informational)
Code Health — 1 degradedDegraded:
^1 File could not be parsed by tree-sitter New files: 4 (avg health: 82) 1 degraded · 0 improved · avg -0.3 Estimated review: not feasible as a single PR (11 files, 3365 lines) Reviewers: lisa.welsch1985 (91%) · talantyyr (45%) · lisa (27%) |
ckb setupnow gets a repo ready without any manual steps, and Codex is a supported target.Setup
--watch, and the watch loop builds the index when the agent starts the server.--index-nowforces a foreground index. With--no-watchit still indexes in the foreground.Codex
config.tomlrobustly: inline comments and multiline arrays survive, and the file mode is kept on atomic rewrite.mcp_servers.ckbwhen it's written as an inline table or dotted keys, instead of breaking it.cmd /c.MCP watch loop
ckb index.Rebased onto develop after #245. The only conflict was the tool count in CLAUDE.md (now 111 + Codex).
go build,go vet, gofmt,sync-version --checkandgo test ./...pass.🤖 Generated with Claude Code