Skip to content

refactor(claude): make ctm pure-overlay; never mutate Claude config files#21

Merged
aksOps merged 1 commit intomainfrom
refactor/ctm-pure-overlay
May 9, 2026
Merged

refactor(claude): make ctm pure-overlay; never mutate Claude config files#21
aksOps merged 1 commit intomainfrom
refactor/ctm-pure-overlay

Conversation

@aksOps
Copy link
Copy Markdown
Contributor

@aksOps aksOps commented May 9, 2026

Summary

ctm previously wrote three Claude-owned config files at install / first-run time:

  • ~/.claude.jsonremoteControlAtStartup=true
  • ~/.claude/settings.jsontui="fullscreen", viewMode="focus"
  • ~/.config/ctm/env.sh — sourced by the launching shell

This 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 --settings only 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-built export … prelude

Direct claude invocations (outside ctm) are now completely unaffected by ctm's preferences.

Changes

(1) Move UI / remote-control defaults into the overlay

  • Add tui, viewMode, remoteControlAtStartup to buildSampleOverlay() in cmd/overlay.go.
  • Delete:
    • claude.EnsureTUIFullscreen + EnsureViewModeFocus (mutated ~/.claude/settings.json)
    • claude.EnsureRemoteControlAtStartup + ClaudeJSONPath (mutated ~/.claude.json)
    • claude.patchJSONFile (no remaining callers)
    • All matching tests
  • Drop the ensureClaude*Default helpers + call sites in cmd/bootstrap.go and cmd/install.go.

(2) Replace env.sh with claude-env.json

  • New internal/config/claude_env.go:
    • LoadClaudeEnv(path) — strict JSON load with 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 and sampleEnvFile/writeEnvFile deleted.
  • Sample claude-env.json pre-seeds CLAUDE_CODE_NO_FLICKER and CTM_STATUSLINE_DUMP (the same two vars the old env.sh shipped).

Backward compat

  • claude.SettingsJSONPath + ReadEffortLevel are kept — read-only, used by the statusline renderer for the effortLevel field.
  • Existing users: ctm auto-creates claude-env.json on next launch via ensureOverlaySidecars. Their stale ~/.config/ctm/env.sh becomes inert (per the hard-cutover plan; not auto-deleted).

Stats

17 files changed, +384 / −925.

Test plan

  • go vet -tags sqlite_fts5 ./... — clean
  • go build -tags sqlite_fts5 ./... — clean
  • go test -tags sqlite_fts5 -race -count=1 ./... — all 27 packages pass
  • CI: build-and-test, SonarCloud, CodeQL, Best Practices JSON Lint (no .bestpractices.json change so should no-op), Scorecard

Out-of-scope follow-up

internal/serve/server_extra_test.go:TestRunAdoptsUUIDsFromLogDir flaked 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

…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>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 9, 2026

@aksOps aksOps merged commit f56fff1 into main May 9, 2026
11 checks passed
@aksOps aksOps deleted the refactor/ctm-pure-overlay branch May 9, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant