refactor(claude): make ctm pure-overlay; never mutate Claude config files#21
Merged
refactor(claude): make ctm pure-overlay; never mutate Claude config files#21
Conversation
…iles
ctm previously wrote three Claude-owned config files at install /
first-run time:
- ~/.claude.json: remoteControlAtStartup=true
- ~/.claude/settings.json: tui="fullscreen", viewMode="focus"
- ~/.config/ctm/env.sh: sourced by the launching shell
This commit reduces the surface to zero. ctm now never writes to any
Claude-owned config. All ctm-side defaults are delivered via
~/.config/ctm/claude-overlay.json (passed via 'claude --settings') and
~/.config/ctm/claude-env.json (read by ctm and exported into the
launch shell as a Go-built export prelude). Direct 'claude' invocations
outside ctm are now completely unaffected.
(1) Move tui, viewMode, remoteControlAtStartup into the overlay
template (cmd/overlay.go: buildSampleOverlay). Delete:
- claude.EnsureTUIFullscreen + EnsureViewModeFocus
- claude.EnsureRemoteControlAtStartup + ClaudeJSONPath
- claude.patchJSONFile (no remaining callers)
plus their tests. Drop the matching ensureClaude*Default helpers
and call sites in cmd/bootstrap.go + cmd/install.go.
(2) Replace bash-script env.sh with JSON-shaped claude-env.json.
New internal/config/claude_env.go provides:
- LoadClaudeEnv(path) — strict JSON load, key-name validation
- (ClaudeEnvFile).ShellExports() — alphabetised, single-quote-
escaped 'export K1=V1 K2=V2' string
- ClaudeEnvExports() — one-call convenience for the launch path
BuildCommand's last param changes from envFilePath to envExports
(a pre-built shell prelude); EnvFilePathIfExists removed. Sample
file pre-seeds CLAUDE_CODE_NO_FLICKER and CTM_STATUSLINE_DUMP
(the same two vars the old env.sh shipped).
claude.SettingsJSONPath + ReadEffortLevel are kept — read-only, used
by the statusline renderer.
Existing users: ctm auto-creates claude-env.json on next launch via
ensureOverlaySidecars; their stale env.sh becomes inert (per the
hard-cutover plan; not auto-deleted).
Verified: go vet -tags sqlite_fts5 ./..., go build, and
go test -tags sqlite_fts5 -race ./... — all 27 packages green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
ctm previously wrote three Claude-owned config files at install / first-run time:
~/.claude.json—remoteControlAtStartup=true~/.claude/settings.json—tui="fullscreen",viewMode="focus"~/.config/ctm/env.sh— sourced by the launching shellThis PR reduces ctm's footprint on Claude-owned config to zero. All ctm-side defaults are now delivered exclusively through:
~/.config/ctm/claude-overlay.json— passed to claude via--settingsonly when the session is launched through ctm~/.config/ctm/claude-env.json— read by ctm at every spawn and exported into the launch shell as a Go-builtexport …preludeDirect
claudeinvocations (outside ctm) are now completely unaffected by ctm's preferences.Changes
(1) Move UI / remote-control defaults into the overlay
tui,viewMode,remoteControlAtStartuptobuildSampleOverlay()incmd/overlay.go.claude.EnsureTUIFullscreen+EnsureViewModeFocus(mutated~/.claude/settings.json)claude.EnsureRemoteControlAtStartup+ClaudeJSONPath(mutated~/.claude.json)claude.patchJSONFile(no remaining callers)ensureClaude*Defaulthelpers + call sites incmd/bootstrap.goandcmd/install.go.(2) Replace
env.shwithclaude-env.jsoninternal/config/claude_env.go:LoadClaudeEnv(path)— strict JSON load with key-name validation(ClaudeEnvFile).ShellExports()— alphabetised, single-quote-escapedexport K1=V1 K2=V2stringClaudeEnvExports()— one-call convenience for the launch pathBuildCommand's last param changes fromenvFilePathtoenvExports(a pre-built shell prelude).EnvFilePathIfExistsandsampleEnvFile/writeEnvFiledeleted.claude-env.jsonpre-seedsCLAUDE_CODE_NO_FLICKERandCTM_STATUSLINE_DUMP(the same two vars the oldenv.shshipped).Backward compat
claude.SettingsJSONPath+ReadEffortLevelare kept — read-only, used by the statusline renderer for theeffortLevelfield.claude-env.jsonon next launch viaensureOverlaySidecars. Their stale~/.config/ctm/env.shbecomes inert (per the hard-cutover plan; not auto-deleted).Stats
17 files changed, +384 / −925.
Test plan
go vet -tags sqlite_fts5 ./...— cleango build -tags sqlite_fts5 ./...— cleango test -tags sqlite_fts5 -race -count=1 ./...— all 27 packages passbuild-and-test,SonarCloud,CodeQL,Best Practices JSON Lint(no .bestpractices.json change so should no-op), ScorecardOut-of-scope follow-up
internal/serve/server_extra_test.go:TestRunAdoptsUUIDsFromLogDirflaked once during local race-detector runs (2-second deadline polling tailer registration). Pre-existing — not touched by this PR. Worth quarantining or extending the deadline in a separate PR.🤖 Generated with Claude Code