feat: codex-only ctm — drop claude, daemon, and web UI#23
Merged
Conversation
Adds the Agent interface and a thread-safe in-process registry. Each agent CLI (claude, codex, future opencode) will implement Agent in its own package and call agent.Register() from init(). This commit defines the abstraction only; no agents are registered yet and no cmd/* code consumes it. Subsequent tasks (02-06) relocate internal/claude/ under internal/agent/claude/ and make it implement Agent, then refactor cmd/* to call agent.For(sess.Agent). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure rename: internal/claude → internal/agent/claude. Package name stays `claude` (Go decouples package name from directory path). Import paths updated in cmd/attach.go, internal/health/claude_check.go, and internal/session/spawn.go. No behavior change. All 895 tests pass; build clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps sessions.json schema_version 1 → 2. Adds two Session fields: - Agent: immutable per-session tag identifying the driving CLI (claude | codex | future opencode). Set at creation; never mutated. Empty value on read normalizes to "claude" via the new Session.NormalizeAgent() helper. - AgentSessionID: opaque per-agent backend session/thread ID. For claude == UUID. For codex it is the thread UUID discovered post- spawn from the rollout file (Task 12). Migration step stampAgentClaude backfills agent="claude" on rows missing the field. Idempotent and stable against repeat runs. Save() normalizes empty Agent to "claude". Strict registry-based unknown-agent rejection is deferred to Task 06, after claude has registered itself with the agent.For() lookup table. 112 session tests pass (4 new); 899 total in 28 packages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the direct TestNormalizeAgent_DefaultsClaude test covering the
read-side helper landed in Task 03. Verifies:
- zero-value Session.Agent normalizes to "claude"
- explicit "codex" passes through
- forward-compat: any non-empty value passes through (e.g., a
future "opencode" entry)
No production code change — cmd/* read paths will start calling
NormalizeAgent() in Task 06's refactor. Test docs the contract those
callers rely on.
900 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
internal/agent/claude/claude.go wraps the existing BuildCommand,
OverlayPathIfExists, and process-discovery helpers in an Agent
implementation registered via init().
The interface methods all delegate to the unchanged package-level
functions — no behavior change. Anything that links this package now
gets agent.For("claude") working out of the box.
CTM_CLAUDE_BIN env var lets integration tests override the binary
path (matches the pattern documented for codex in agent.go).
7 new claude tests; 907 total pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds OpenAI's `codex` CLI as a first-class Agent implementation alongside the existing claude impl, plus the post-spawn thread-UUID discovery flow that lets future reattach use `codex resume <uuid>` instead of `--last`. - internal/agent/codex/: Agent impl, BuildCommand (codex resume <id> with `|| codex` fallback, --sandbox danger-full-access for yolo), process check, and DiscoverSessionID polling ~/.codex/sessions/ <YYYY>/<MM>/<DD>/rollout-…-<uuid>.jsonl files newer than spawnStart. 16 unit tests cover registration, BuildCommand shapes, env-export prefix, shell quoting, and discovery (happy path, pre-spawn ignored, newest-wins, missing dir, non-rollout files). - internal/procscan/: shared /proc walker (FindChild + IsAlive) extracted so internal/health and per-agent process.go files don't duplicate the logic. - internal/health/agent_check.go: agent-agnostic CheckAgentProcess(tc, sessionName, procName) — supersedes the claude-only CheckClaudeProcess. - internal/agent/agent.go: Agent interface gains DiscoverSessionID method. SpawnSpec docs updated to describe codex semantics. - main.go: blank import internal/agent/codex so the binary registers the codex agent at link time. - internal/session/spawn_discovery_test.go: end-to-end test of the post-spawn goroutine (real Store + fake tmux + simulated rollout file). Uses OnDiscoveryComplete callback for deterministic sync. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Switches the on-disk + in-memory default agent from claude to codex,
adds the v2→v3 migration that rewrites legacy claude rows, and wires
the post-spawn UUID discovery into Store.Save via a background
goroutine.
- internal/session/state.go:
* SchemaVersion bumped to 3.
* DefaultAgent constant = "codex".
* NormalizeAgent and Save both remap empty + legacy "claude" → codex
so a stale in-memory Session never surfaces as an agent.For miss.
* New rewriteClaudeToCodex migration step (v2→v3) for on-disk rows.
* New Store.UpdateAgentSessionID(name, id) — atomic, locked,
idempotent — for the discovery flow to stamp results.
- internal/session/spawn.go:
* Yolo dispatches via agent.For(opts.Agent) (DefaultAgent on empty)
instead of importing internal/agent/claude directly.
* SpawnOpts gains Agent + OnDiscoveryComplete fields.
* After tmux create + Store.Save, fires a discoverAndStamp goroutine
when the Store also implements AgentSessionStamper. The goroutine
runs Agent.DiscoverSessionID and stamps the result; failures and
timeouts log at slog.Debug only — discovery is opportunistic, not
load-bearing.
- Tests rewritten for the new defaults: TestNormalizeAgent_DefaultsCodex
(covers empty + legacy "claude" remap + forward-compat passthrough),
TestSession_EmptyAgentDefaultsCodexOnSave,
TestSession_LegacyClaudeRewrittenOnSave,
TestMigration_V1ToV3_RewritesAllToCodex (full v1→v2→v3 path),
TestMigration_V2ToV3_RewritesClaudeRowsOnly (isolated v2→v3 step
preserves non-claude agents). spawn_test.go gets a blank import of
internal/agent/codex so Yolo can resolve "codex" in the registry.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… serve callsites
Replaces direct imports of internal/agent/claude in the cmd/* layer
with agent.For() lookups, swaps every "claude" default-name literal
for session.DefaultAgent (codex), and removes the daemon coupling
(proc.EnsureServeRunning + fireServeEvent) that the upcoming
internal/serve removal will deprecate. Tests follow the same swap.
- cmd/attach.go: drops internal/serve/proc + internal/agent/claude
imports; uses agent.For(sess.NormalizeAgent()) to resolve the
per-session impl; passes the resolved Agent's BuildCommand on
recreate/respawn; CheckAgentProcess via the agent's ProcessName.
- cmd/check.go: agent-aware process check (skips when sessErr != nil
since the agent identity is unknown without a stored row).
- cmd/yolo.go / yolo_runners.go: drops fireServeEvent and
proc.EnsureServeRunning callsites; resolveSimpleName falls back to
session.DefaultAgent.
- cmd/new.go, cmd/kill.go: drop serve-event firing (kept fireHook so
user-defined shell hooks still run).
- cmd/hooks_dispatch.go: keeps fireHook + yoloIntent; deletes
fireServeEvent + the internal/serve/proc import.
- cmd/doctor.go + internal/doctor/doctor.go: deps list flips
{tmux, claude, git} → {tmux, codex, git}. cmd/root.go's Short/Long
rebrand to "Codex Tmux Manager".
- cmd/install.go: drops the cc-sessions migration step and the prose
block describing the (now-removed) ~/.config/ctm/claude-overlay.json
injection.
- integration_test.go: removes TestIntegration_MigrateFromCC since the
cc migration is gone.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ude package Removes every CLI surface that only existed to manage Claude Code state. With the codex agent shipped (and the session layer now routing through the registry), the claude implementation becomes inert: deleting it trims ~40 files of code with no functional regression for codex users. - cmd/overlay.go + test: claude-overlay.json mechanism (--settings layer on top of ~/.claude/settings.json). Codex reads ~/.codex/config.toml natively; ctm no longer maintains a parallel layer. - cmd/statusline.go + test: parsed claude's statusLine JSON payload to render Opus/effort/context/quota. Codex doesn't expose equivalent telemetry — remove rather than ship a misleading half-impl. - cmd/logs.go + 2 tests: viewer for claude PostToolUse JSONL logs. Source data is gone; viewer goes too. - cmd/log_tool_use.go + test: PostToolUse hook target. - internal/config/claude_env.go + test: claude-env.json shell-prelude loader (was the early-bind path for CLAUDE_CODE_NO_FLICKER, etc.). - internal/health/claude_check.go + test: superseded by the agent-agnostic CheckAgentProcess landed in the codex commit. - internal/shell/migrate.go + test: cc-sessions → ctm migration for the pre-claude-code "cc" CLI. With claude itself gone, this legacy path has no audience. - internal/agent/claude/* (8 files): the Agent implementation we're replacing. registry_test.go's stubAgent already implements DiscoverSessionID so the registry tests still pass. - cmd/bootstrap.go: drops ensureOverlaySidecars + pruneSessionLogs + the logrotate import. ensureSetup now does only config + tmux.conf + aliases. - cmd/bootstrap_test.go: rewritten to assert the new minimal contract (config.json + tmux.conf created; no overlay/env sidecars leak). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…2 migration ctm is now a pure CLI. The embedded React UI was already disconnected from any new data path; this commit removes the HTTP daemon that fed it, along with everything the daemon required: - internal/serve/** (104 files): HTTP server, SSE event bus, API endpoints, ingest tailer / cost store / search store, attention thresholds, webhook dispatcher, V27 single-user auth. - cmd/serve.go: the daemon entry-point. - cmd/auth.go: `ctm auth reset` — only useful for daemon credentials. - internal/config/config.go: drops ClaudeOverlayPath, ClaudeEnvPath, ServeConfig, AttentionThresholds, AllowedOriginsPath, and all serve port / attention default constants. Existing user config.json files with a "serve" block hit the jsonstrict strip-to-.bak self-heal path the first time the post-v0.3 binary loads them — documented unknown-key behaviour, no migration required. - SchemaVersion bumped to 2 with rewriteRequiredPathClaude — a defensive migration that rewrites literal "claude" entries in required_in_path to "codex" so existing installs don't see a spurious doctor warning. Idempotent; preserves user additions beyond the original defaults. - internal/config/config_test.go: drops the Serve/AttentionThresholds test bloc and the legacy hardcoded SchemaVersion==1 assertions; adds four migration tests (happy path, user additions preserved, idempotent, full migrate.Run plan) plus reflect+migrate imports. - go.mod + go.sum: `go mod tidy` drops fsnotify, mattn/go-sqlite3, and golang.org/x/crypto (all daemon-only deps). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The React + Vite + Playwright dashboard at ui/ was the consumer of the HTTP daemon removed in the previous commit. With no consumer left, the entire frontend tree comes out (~150 files: src/, e2e/, public/, plus package.json / pnpm-lock / tsconfig / vite / vitest / eslint / etc.). - Makefile: rewritten to a CLI-only build pack. Drops UI_DIR / UI_DIST / EMBED_DIST variables, the `ui` / `dev` / `e2e` targets, and the pnpm/vite/playwright dependencies of `regression`. Drops the `sqlite_fts5` build tag (was for the deleted daemon's search store). `regression` now runs go build / go test / go test -race / govulncheck across the full module. - .gitignore: drops `internal/serve/dist/` and `ui/coverage/` (paths that no longer exist); adds `.claude/*.local.md` so ralph-loop iteration snapshots and similar runtime state stay out of history. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…xtures Cosmetic-only sweep across Go source where comments and test fixtures still mentioned claude after the codex pivot. No behavior changes. - internal/fsutil/atomic.go: package doc no longer references the deleted internal/claude/jsonpatch.go. - internal/output/format_test.go: TestError + TestInfo fixtures swapped from "claude" to "codex" — agent-name strings are arbitrary here; matching the new default keeps future readers from inferring claude is supported. - internal/tmux/client.go: SendEnter docstring's TUI example. - internal/tmux/config.go: tmux.conf focus-events comment. - internal/tmux/client_test.go: shellCmd fixtures rewritten to `codex resume <id> || codex` form (the function under test passes the string verbatim — codex/claude/anything works, but the new literal documents the current spawn shape). - sonar-project.properties: comment references list codex as the expected `$PATH` binary; coverage-exclusion roster trimmed to files that still exist post-daemon removal. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…TRIBUTING, .bestpractices.json; trim CI User-facing surface flipped to the post-codex-pivot, post-daemon-removal, CLI-only state. - README.md: tagline "Claude Tmux Manager" → "Codex Tmux Manager". Drops the entire overlay/statusline/logs sections plus all daemon / web-UI / pnpm / vite / playwright references. Statusline mock collapses to `codex 0.130.0 ~/projects/ctm ●` with a one-line caption explaining codex doesn't expose telemetry. Resume bullet rewritten to `codex resume <id> || codex` with `codex resume --last` callout; YOLO bullet to `codex --sandbox danger-full-access`. Codex CLI link points to https://github.com/openai/codex. - CHANGELOG.md: new [0.3.0] 2026-05-14 BREAKING entry documenting the daemon + web-UI removal. New [Unreleased] entry covers the v2 config-schema migration, the .bestpractices.json refresh (manual re-ingest still required), and the codex thread-UUID discovery fix. Pre-existing 0.2.0 + 0.1.0 history left untouched. - SECURITY.md: drops bearer-token / argon2id / Origin allow-list / reverse-proxy paragraphs; "Security architecture quick reference" now states explicitly that ctm has no network listener post-v0.3. - CONTRIBUTING.md: drops pnpm/vite/playwright/eslint guidance and the sqlite_fts5 build-tag mention; keeps Go-side workflow. - .bestpractices.json: surgical refresh — description + justification swap, drops sqlite_fts5 + pnpm + non-existent CodeQL refs, flips the four argon2id-coupled crypto criteria to N/A, reframes dynamic-analysis around the post-spawn discovery goroutine. Maintainer still needs to click "Save (and continue) 🤖" on bestpractices.dev project 12716 to trigger re-ingestion. - .github/workflows/ci.yml + release.yml + sonar.yml: drop pnpm/Node setup, `make ui`, UI typecheck, vitest coverage, playwright steps. Header comments trimmed. - .github/workflows/sonar-bulk-accept.yml: drops every TypeScript rule bucket (S6759/S6819/S3358/S6571/S6754/S6479/S3735/S1874/ S7763/S7718/S6772/S6582/S4624/S6822/S1871). Keeps Go-side buckets and the godre:S8239 false-positive bucket. Co-Authored-By: Claude Opus 4.7 <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 pivots from a Claude-Code-specific tmux wrapper to a CLI-only codex
session manager. Three axes of change:
Codex becomes the only built-in agent. Adds the
codexAgentimplementation (
internal/agent/codex/) with post-spawn thread-UUIDdiscovery so reattach uses
codex resume <uuid>instead of--last.Deletes
internal/agent/claude/. Sessions schema bumps to v3,migrating any legacy
agent="claude"rows toagent="codex".ctm serveHTTP daemon and the React web dashboard are gonecompletely. All of
internal/serve/,cmd/serve.go,cmd/auth.go,the
ui/tree, and the Makefile UI pipeline come out — net−44k lines. ctm now binds no ports and ships no embedded assets.
Hook fan-out via
~/.config/ctm/config.jsonhooksmap remains(in-process via
internal/hooks).Codex equivalents replace claude-only ergonomics. YOLO swaps
--dangerously-skip-permissionsfor--sandbox danger-full-access.Doctor's required-binaries list flips claude → codex. config.json
schema v2 rewrites
required_in_path: [\"claude\", …]→[\"codex\", …]on first run, preserving any user additions. Statusline / overlay
/ PostToolUse-log subcommands are removed (no codex equivalent).
The five
feat(codex): 0Nfoundation commits already in the branch(interface, registry, schema v2, default-agent guard, claude impl)
were a no-claude-yet stepping stone — this PR builds on them and
finishes the migration.
Verification
go build ./...— greengo test ./...— 336 / 336 pass across 19 packagesgovulncheck ./...— 0 user-code vulnerabilitiesgo test -race ./...— green\"claude\"→ctm doctorruns the migration and reportsrequired_in_path: codex, node, go, bunandschema_version: 2on disk. Codex resolved at
/home/dev/.nvm/.../bin/codex. None ofserve,auth,overlay,statusline,logs,log-tool-useappear in the command list.
Test plan
make regressionfrom a fresh checkout (build + test + race + govulncheck)ctm yolo <name>with codex on$PATH— confirm tmux sessioncreated, codex spawns inside, sessions.json populated with
agent: codexandagent_session_idfilled within ~5s of spawnctm(default attach) — confirm reattach usescodex resume <agent_session_id>(visible in pane shell history)~/.config/ctm/config.jsonschema v1 +required_in_path: [\"claude\", …]— confirm first invocationafter upgrade migrates to v2 and rewrites claude → codex
agent: \"claude\"rows — confirm migration to v3 + rewrite to
codexctm doctorshows[OK] codexinstead of[OK] claudein theDependencies section
bestpractices.dev project 12716
to refresh the badge with the post-codex-pivot
.bestpractices.jsonBreaking changes
ctm serve,ctm auth,ctm overlay,ctm statusline,ctm logs,ctm log-tool-usesubcommands removed./api/*), SSE event bus, webhook delivery, allowed-originsfile, single-user auth — all gone.
🤖 Generated with Claude Code