Skip to content

codeburn optimize --apply: execute config-class fixes with undo #604

Description

@iamtoruk

codeburn optimize --apply: execute config-class fixes with undo

Part of the acting-layer epic. Depends on the action journal issue; do not start before it lands.

What to build

codeburn optimize --apply turns the config-class findings that optimize already prints into applied, journaled, undoable changes. Behavioral findings (session openers, prompts) are explicitly not appliable here; they belong to the guard issue.

Step 1: stable finding ids

WasteFinding (src/optimize.ts) has no id today. Add id: FindingId to the type and to every detector, and include it in the JSON report shape (OptimizeJsonReport.findings[]). Ids are kebab-case and stable across runs:

read-edit-ratio, mcp-low-coverage, mcp-project-scope, low-worth-sessions,
context-heavy-sessions, cost-outliers, redundant-rereads, unused-skills,
unused-agents, unused-commands, build-folder-reads, bash-output-cap,
claude-md-too-long, warmup-heavy, retry-heavy-capabilities

(Match the actual detector list in the file; the names above cover the ones visible in current output, extend if the code has more.)

Step 2: appliable plans

Add src/act/plans.ts with planFor(finding): ActionPlan | null. A plan describes concrete file mutations for the journal framework (see src/act/types.ts). Appliable finding ids and their exact semantics:

mcp-low-coverage and mcp-project-scope (kind: mcp-remove / mcp-project-scope)

  • Locate each server by reading, in order: project .mcp.json (mcpServers key), project .claude/settings.json and .claude/settings.local.json (mcpServers), user ~/.claude.json (top-level mcpServers and per-project projects[<path>].mcpServers). Handle only servers found in these files; if a server comes from somewhere else (plugin, managed config), mark it not-appliable with a note in the plan preview.
  • Remove: delete the server entry from the file(s) it appears in.
  • Project-scope: remove the entry from the global location and add it verbatim to <project>/.mcp.json for each project the finding lists as a keeper (create the file with { "mcpServers": {} } scaffold if missing).
  • JSON edits must preserve the rest of the document; parse, mutate, JSON.stringify(obj, null, 2) plus trailing newline. If a target file fails to parse, skip that server and surface the parse error in the summary rather than aborting the whole apply.

unused-skills, unused-agents, unused-commands (kind: archive-*)

  • Move each directory or file to a sibling .archived/ directory (~/.claude/skills/.archived/<name> etc., matching the paths the detector reports). Create .archived/ on demand. Name collision in the archive: suffix -2, -3.

claude-md-rule destinations (kind: claude-md-rule)

  • For findings whose fix is { type: 'paste', destination: 'claude-md' }: append to the project CLAUDE.md (or ~/.claude/CLAUDE.md when the finding is global) wrapped in markers:
<!-- codeburn:begin <finding-id> -->
<rule text>
<!-- codeburn:end <finding-id> -->
  • Idempotent: if a block with the same finding-id already exists, replace its contents instead of appending a duplicate. Undo removes the whole block (the journal backup already guarantees byte-exact restore; the marker discipline is for humans and idempotency).

bash-output-cap (kind: shell-config)

  • Append the suggested export to the user's shell rc (detect from $SHELL: .zshrc or .bashrc) inside the same marker scheme using # comments.

Everything else returns null (not appliable) and is shown greyed out with "manual" in the picker.

Step 3: UX

codeburn optimize --apply            # plan + interactive confirm
codeburn optimize --apply --yes      # apply all appliable without prompting
codeburn optimize --apply --dry-run  # print the plan and exit 0, change nothing
codeburn optimize --apply --only mcp-low-coverage,unused-skills

Flow: run the normal optimize scan, list appliable findings numbered with estimated savings and the exact files each plan touches, prompt Apply all / pick numbers / quit (plain readline; do not pull in a new TUI dependency for this). After applying, print a summary with each action's journal id and the line Undo anytime: codeburn act undo <id>.

--apply combined with --json is an error (exit 2) to keep the JSON contract stable.

Tests (tests/optimize-apply.test.ts)

Fixture home dir with crafted ~/.claude.json, project .mcp.json, skills tree, CLAUDE.md:

  • mcp remove deletes exactly the named server from the right file, other keys untouched, undo restores byte-identical
  • project-scope moves the entry global -> project .mcp.json, creates the file when missing
  • server defined in an unparseable file is reported and skipped, remaining plans still apply
  • archive moves and undo round-trip, collision gets -2 suffix
  • CLAUDE.md marker block: fresh append, second apply replaces in place (no duplicate), undo removes the block
  • --dry-run leaves the fixture tree byte-identical (hash the tree before/after)
  • every finding in a full optimize run has a stable non-empty id (regression guard for step 1)

Acceptance

  • On a machine with real findings, codeburn optimize --apply --dry-run prints a correct plan and --apply + codeburn act undo --last round-trips cleanly.
  • optimize --json now carries finding ids; no other JSON shape changes.
  • Zero new dependencies, all existing tests green.

Part of epic #602.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions