Last updated: Sep 1, 2025, 01:10 PM UTC

claudecodeui Submodule → In‑Repo Migration (History Preserved)

This document describes how to replace the claudecodeui git submodule with a regular in‑repo directory while preserving its history, and how to update Docker, scripts, and GitHub Actions accordingly. It includes step‑by‑step commands, validation, and rollback.

Decisions (Confirmed)

  • History mode: Preserve full commit history (no --squash).
  • Import source: main branch from https://github.com/wapdat/sasha-studio.git.
  • Path: Keep directory name claudecodeui/ as‑is.
  • Dockerfile: Use claudecodeui/Dockerfile.sliplane for all builds.
  • Node version: Keep Node v20 (from claudecodeui/.nvmrc) for CI and Docker.
  • Registry: Continue publishing to GHCR using default GITHUB_TOKEN.
  • Upstream sync: No need to keep a persistent subtree remote configured after migration.
  • Build scope now: Minimal changes to simplify build; defer extra npm ci/lint/test enhancements to a second phase.

Objectives

  • Remove submodule complexity and errors; make claudecodeui/ a normal folder.
  • Preserve claudecodeui commit history (via git subtree).
  • Keep dev/Docker build parity and GHCR release flow.
  • Update CI/CD and scripts to stop depending on submodules.

Scope

  • Repo structure remains: UI + server in claudecodeui/; docs under docs/.
  • Docker images and tags remain the same (VERSION‑driven).
  • No change to app behavior; this is an operational migration.

Summary Of Changes (already applied in this repo)

  • GitHub Actions:
    • Updated .github/workflows/docker-build.yml to remove submodule checkout.
    • Added .github/workflows/ci.yml to build UI and docs on push/PR.
  • Scripts:
    • docker-build-ghcr.sh: removed submodule push logic; now commits VERSION only.
    • docker-dev.sh: removed submodule awareness; supports running from UI dir.

You can proceed with the history‑preserving import and cutover steps below.


Phase 1 — Discovery

  1. Locate submodule info and current SHA:
git config -f .gitmodules --get submodule.claudecodeui.url
git submodule status -- claudecodeui
  1. Scan for submodule usage in scripts/CI:
rg -n "submodule|recurse-submodules|git submodule" -S
  1. Confirm build flows still expected post‑migration:
  • Local dev: ./dev.sh (server+UI at :3007) / cd claudecodeui && npm run dev.
  • Docker dev: ./docker-dev.sh (container at :3006).
  • GHCR: ./docker-build-ghcr.sh [patch|minor|major].

Phase 2 — History Strategy (Chosen: Preserve via Subtree)

We will import the external UI repository into ./claudecodeui/ using git subtree, preserving the full commit history under that path (no --squash). If you prefer a snapshot, see Appendix B.


Phase 3 — Remove Submodule Wiring (Precondition: Commit Local Changes)

Create a feature branch for the migration:

git checkout -b feat/inline-claudecodeui

Precondition — ensure all local changes (including inside the current submodule working tree) are committed to the main repo before proceeding. This avoids losing any untracked work.

Remove the submodule (gitlink + metadata):

git rm -f claudecodeui
rm -rf .git/modules/claudecodeui
sed -i.bak '/submodule "claudecodeui"/,+3d' .gitmodules || true
git add .gitmodules || true
git commit -m "chore(repo): remove claudecodeui submodule wiring"

Validate the worktree no longer has a submodule at that path.


Phase 4 — Import claudecodeui With git subtree

Add the UI repo as a remote:

git remote add claudecodeui-remote <CLAUDECODEUI_REMOTE_URL>
git fetch claudecodeui-remote

Import to ./claudecodeui/ with full history from main:

# Preserve full history (no --squash)
git subtree add --prefix=claudecodeui claudecodeui-remote main

If you later decide to re‑sync from upstream:

git subtree pull --prefix=claudecodeui claudecodeui-remote main

Resolve any conflicts, then commit.


Phase 5 — Fix Paths & Config (if needed)

Most imports already reference in‑repo paths (e.g., claudecodeui/server/...). After subtree import:

  • Ensure .gitignore does not ignore claudecodeui/.
  • Keep Node v20 via claudecodeui/.nvmrc (CI uses it).
  • Keep environment usage (.env.local for local only; no secrets committed).

Phase 6 — Dockerfiles & Scripts

  • Dockerfile path remains claudecodeui/Dockerfile.sliplane.
  • Ensure build context includes claudecodeui/ (not excluded in .dockerignore).
  • Scripts (already updated here):
    • docker-build-ghcr.sh — commits VERSION only; no submodule push.
    • docker-dev.sh — no submodule checks; builds using local code.

Out‑of‑scope (Phase 2 later): npm ci/lint/test improvements will be added in a follow‑up to keep this migration minimal.


Phase 7 — GitHub Actions (applied)

  1. Build & Push Docker (updated): .github/workflows/docker-build.yml
  • Uses actions/checkout@v4 without submodules.
  • Builds with claudecodeui/Dockerfile.sliplane.
  • Tags include VERSION + semver + latest.
  1. CI (new): .github/workflows/ci.yml
  • Job ui-build: Node v20 from .nvmrc, npm ci, npm run build, upload dist.
  • Job docs-build: installs doc builder and runs npx @knowcode/doc-builder -c doc-builder.config.js.

Triggers remain minimal: PRs build artifacts; pushes to main build and publish Docker images. No submodule tokens required.


Phase 8 — Validation Checklist

  • Local:
    • ./dev.sh → app at http://localhost:3007
    • cd claudecodeui && npm ci && npm run build passes
  • Docker:
    • ./docker-dev.sh → app at http://localhost:3006
    • Verify file browser, markdown preview, binary content endpoint
  • CI:
    • PR triggers ci.yml (UI & Docs) and docker-build.yml on main push
  • Versioning:
    • ./docker-build-ghcr.sh patch bumps VERSION, pushes, and triggers Docker build

Phase 9 — Cutover

  1. Open PR with:
  • Submodule removal commit(s)
  • Subtree import commit
  • CI/scripts updates (already in this repo)
  • Rationale & rollback section
  1. After merge:
  • Validate Actions runs (UI build, docs build, Docker image publish)
  • Optionally tag or use ./docker-build-ghcr.sh to release a version
  • Remove submodule references from CLAUDE.md and related docs (included in PR)

Phase 10 — Rollback Plan

  • If needed, revert the subtree import and submodule removal commits to restore the original state.
  • Keep a branch with subtree history for future reattempt.

Appendix A — Command Cheat Sheet

# Inspect submodule
git config -f .gitmodules --get submodule.claudecodeui.url
git submodule status -- claudecodeui

# Remove submodule
git rm -f claudecodeui
rm -rf .git/modules/claudecodeui
sed -i.bak '/submodule "claudecodeui"/,+3d' .gitmodules || true
git add .gitmodules || true
git commit -m "chore(repo): remove claudecodeui submodule wiring"

# Import via subtree (history preserved, full history)
git remote add claudecodeui-remote <REMOTE_URL>
git fetch claudecodeui-remote
git subtree add --prefix=claudecodeui claudecodeui-remote main

# Optional future sync (if you choose to keep the remote)
git subtree pull --prefix=claudecodeui claudecodeui-remote main

Appendix B — Alternative: Vendor Snapshot (No History)

If you choose not to preserve history:

git rm -f claudecodeui
rm -rf .git/modules/claudecodeui .gitmodules

# Copy the submodule working tree contents into ./claudecodeui/
cp -R /path/to/checkedout/claudecodeui ./claudecodeui
git add claudecodeui
git commit -m "chore(repo): vendor claudecodeui snapshot into repo"

Pros: simplest. Cons: lose UI history inside this repo.


Notes

  • Docker image and VERSION tagging remain unchanged.
  • If any docs refer to “submodules”, update those references after cutover.
  • The shared docs root detection (e.g., SHARED_DOCS_ROOT) remains unaffected by this migration.