apply-template
Apply ai-env Template
Apply the agentic development environment template to the current repository.
Argument: optional path to ai-env clone. No argument = clone from GitHub (primary path).
Phase 0 — Pre-flight
Clean working tree check
Run git status --porcelain. If there are uncommitted changes, warn the user and ask whether to proceed or stash first. A dirty tree makes it hard to review what the template changed.
Phase 1 — Source Resolution & Discovery
Locate template source
- If an argument was provided, use that path and verify it's a clean git checkout
- If NO argument was provided, clone from GitHub:
git clone --depth 1 https://github.com/camacho/ai-env.git /tmp/ai-env-templateand use that - If the default local path
~/projects/camacho/ai-envexists and no argument was given, prefer the GitHub clone (ensures latest version) - If all fail, tell the user and stop
Read manifest
Read apply-template.manifest.json from the template source directory. This classifies every file as copy_if_absent, smart_merge, or skip.
Scan target
Check which copy_if_absent and smart_merge files already exist in the current repo (cwd).
For copy_if_absent files that already exist in the target: these will be smart-merged (not skipped) to pick up any new template additions while preserving target customizations.
Present plan
Show the user a summary:
Template Application Plan:
Source: <path>
Target: <cwd>
Copy (new): N files — <list>
Smart merge (new): N files — <list of smart_merge files not in target>
Smart merge (both): N files — <list of smart_merge files in both>
Merge (existing copy_if_absent): N files — <list>
Skipped: N files (template-specific)
Proceed?
Wait for confirmation before continuing.
Phase 2 — Copy
For each entry in copy_if_absent:
- Directories (entries ending with
/):mkdir -pthe target directory, then copy all files recursively. Skip individual files that already exist. - Files: if the file does NOT exist in the target, copy it verbatim. If it exists, apply the same smart merge logic as Phase 3 (the file has diverged from template — preserve target intent while adding template additions).
Also ensure these directories exist even if empty:
ai-workspace/plans/ai-workspace/decisions/
Phase 3 — Smart Merge
For each file in smart_merge, read BOTH the template version and the target version (if it exists). If the target file doesn't exist, just copy the template version. If both exist, merge according to the rules below.
AGENTS.md — Semantic Merge
- Understand intent: read both versions completely. Identify the purpose of each section.
- Keep from target:
## Stack,## Commands,## Architecture,## Gotchas— preserve these sections entirely - Add from template if missing:
## Agent Roles & Dispatch,## Protected Files,## Conventions,## Workflow Reference,## Context Loading Rules - Resolve duplicates: if both have a section with the same heading, favor the template version for structural/convention sections, favor the target for project-specific sections
- Update: the title line — replace the project name with the target repo's directory name
- Preserve: any custom sections the target has that aren't in the template
- Cohesion check: after merge, read the result and verify sections don't contradict each other
tsconfig.json — Deep Merge
- Start from template base as the foundation
- Deep merge
compilerOptions: overlay target's compilerOptions on top of template's. Target wins on collision. - Preserve from target:
include,exclude,references, and any other top-level keys - Inject if missing: these strict options from the template:
strict,noUncheckedIndexedAccess,verbatimModuleSyntax,exactOptionalPropertyTypes,noImplicitOverride,noFallthroughCasesInSwitch - Surface conflicts: if template and target have contradictory values for the same option (e.g.,
strict: truevsstrict: false), present the conflict to the user for resolution - Cohesion test: after merge, verify
compilerOptionsdon't have contradictory flags (e.g.,module: "commonjs"withverbatimModuleSyntax: true)
.claude/settings.json
- Preserve from target: all existing
permissions.allowandpermissions.denyentries,defaultMode - Union merge:
permissions.denyarrays — add template entries not already present (deduplicate) - Union merge hooks: for each hook event (PreToolUse, PostToolUse, etc.), keep all target hooks. Add template hooks only if no hook with the same
matcheralready exists in the target - Preserve: any other keys the target has (disabledMcpjsonServers, enabledPlugins, etc.)
package.json
- Preserve from target:
name,version,description,main,type,dependencies,repository,license,author— everything except scripts and devDependencies - Merge scripts: add any scripts from template that don't exist in target. Target wins on collision.
- Merge devDependencies: add packages from template that aren't in target's devDependencies (commitlint packages, lefthook, @biomejs/biome, typescript). Never remove or downgrade existing deps.
- Add if missing:
engines,packageManager
biome.json
- Preserve from target: all existing rules,
filesconfig,formatterconfig - Add if missing: linter rules from template that don't exist in target
- Never remove any rule the target already has
.gitignore
- Union merge: combine every unique line from both files, deduplicated
- Preserve: section comments (lines starting with
#) and blank line groupings from both files - Order: target's lines first, then new lines from template appended at the end under a
# ai-env templatecomment
skills-lock.json — Union Merge
- Union merge
.skillsobject: combine all skill entries from both template and target. Each entry hassource(git URL or local path, depending onsourceType),sourceType("git"or"local"), andcomputedHash(SHA-256 integrity hash). - Template wins on conflict: if the same skill name exists in both, use the template's entry (
source,sourceType,computedHashall come from the template) - Never remove: target skills not in the template are always preserved — projects may have custom skills
- Top-level
version: keep the higher integer from either side (this is the lockfile schema version, not a skill version — individual skills do not have version fields) - Post-merge: after writing the merged lockfile, instruct: "Run
npx skills install -a claude-code -a codexto materialize any newly added skills for both agents"
Example merge logic:
Template: Target:
{ "version": 1, "skills": { { "version": 1, "skills": {
"validate": { "validate": {
"source": "git@...ai-skills.git", "source": "git@...ai-skills.git",
"sourceType": "git", "sourceType": "git",
"computedHash": "abc..." "computedHash": "abc..."
}, },
"reflect": { "custom-lint": {
"source": "git@...ai-skills.git", "source": "local",
"sourceType": "git", "sourceType": "local",
"computedHash": "def..." "computedHash": "xyz..."
} }
}} }}
↓ merged result:
{ "version": 1, "skills": {
"validate": { ... }, ← kept (same in both)
"custom-lint": { ... }, ← preserved (target-only)
"reflect": { ... } ← added from template
}}
Directories: .claude/agents/, .claude/rules/, .claude/skills/
- Recurse into each directory
- New files: copy from template
- Existing files: apply semantic merge — understand the intent of both versions, preserve target customizations, add missing template sections
Phase 4 — Post-Apply
-
Create
ai-workspace/.template-versionwith content:version: <manifest version> applied: <ISO 8601 timestamp> source: <template source path> -
Dependency check: if devDependencies were added to package.json, detect the package manager (pnpm-lock.yaml → pnpm, yarn.lock → yarn, package-lock.json → npm) and ask: "New devDependencies added. Run
<pm> install?" -
Workflow warning: if any
.github/workflows/files were copied, check for the secret:if command -v gh &> /dev/null && gh secret list 2>/dev/null | grep -q "CLAUDE_CODE_OAUTH_TOKEN"; then # Secret exists — no warning needed else warn: "GitHub workflows added. You'll need to set CLAUDE_CODE_OAUTH_TOKEN as a repo secret (Settings → Secrets → Actions)." fi -
Hook warning: if
.claude/settings.jsonreferences hook scripts at~/.claude/hooks/, check if each script exists on disk. Warn about any missing ones. -
Dotfiles sync: Ask the user if they want to install user-level configs:
ai-env includes user-level configs for Claude and Codex (hooks, skills, profiles). Install them? Run: $AI_ENV_ROOT/dotfiles/sync.sh push (where $AI_ENV_ROOT is the template source directory) This copies dotfiles/claude/ → ~/.claude/ and dotfiles/codex/ → ~/.codex/If they agree, run
"$TEMPLATE_SOURCE/dotfiles/sync.sh" pushfrom the template source directory (not cwd). -
Auto-add post-setup todos via TodoWrite:
- "Update AGENTS.md — fill in Stack, Commands, Architecture, Gotchas for your project"
- "Update .claude/agents/ descriptions to reference your project"
- "Run
$TEMPLATE_SOURCE/dotfiles/sync.sh pushto deploy user-level configs (if not done above)" - "Review .claude/settings.json hooks and adjust paths for your environment"
-
Summary: print a list of all files created, merged, and skipped.
Phase 5 — Context Migration (prompted, not automatic)
This phase scans for ALL existing AI context, memory, and configuration in the target repo and walks the user through migration interactively. Never delete or overwrite context automatically — every action requires explicit user confirmation.
Discovery scan
Check ALL of the following locations. If none have content, skip this phase entirely with a message: "No existing AI context found — skipping migration."
Project-scoped (in repo):
ai-workspace/MEMORY.md— project memory (check if it has content beyond the seed template)ai-workspace/plans/— existing plans (especially.done.mdcompleted plans)ai-workspace/decisions/— existing ADRsai-workspace/scratchpad.md— working notesai-workspace/workflow.md— custom workflow (may differ from template).claude/skills/— pre-existing skills (not from this template run).claude/agents/— pre-existing agent definitions.claude/rules/— pre-existing rules.claude/settings.json— project permissions, hooks, deny rules.claude/settings.local.json— local-only overrides (not checked in).codex/config.toml— Codex configuration with profilesCLAUDE.md— existing Claude instruction fileAGENTS.md— existing universal instruction fileGEMINI.md— Gemini-specific instructions (if present).cursorrulesor.cursor/rules/— Cursor-specific rules (if present).claudeignore/.codexignore— existing context exclusions.github/workflows/claude.ymlorclaude-code-review.yml— existing CI workflows
User-scoped (outside repo — read-only, inform user):
~/.claude/projects/<path-encoded-cwd>/memory/— Claude Code auto-memories for this project- Path encoding: replace
/with-, e.g.,-Users-patrickcamacho-projects-myapp - Each
.mdfile has frontmatter withname,description,type(user/feedback/project/reference)
- Path encoding: replace
~/.claude/projects/<path-encoded-cwd>/MEMORY.md— auto-memory index for this project~/.claude/CLAUDE.md— user's global instructions (may reference project patterns)~/.claude/hooks/— global hook scripts that project hooks may duplicate~/.claude/agents/— global agent definitions that project agents may shadow~/.claude/commands/— global slash commands (catchup, reflect, etc.)~/.claude/plugins/— installed plugins (checkenabledPluginsin settings)
Present findings
Show the user a categorized summary:
Context Migration — Found <N> items across <M> locations:
Memory:
- ai-workspace/MEMORY.md (42 lines, last updated 2026-03-15)
- ~/.claude/projects/.../memory/ (6 files: 2 user, 2 feedback, 2 project)
Plans & Decisions:
- ai-workspace/plans/001-feature.done.md (completed, immutable)
- ai-workspace/decisions/001-architecture.md
Skills:
- .claude/skills/deploy/SKILL.md (custom, not in template)
- .claude/skills/validate/SKILL.md (CONFLICTS with template version)
Agents & Rules:
- .claude/agents/implementer.md (customized from template)
- .claude/rules/feature-branches.md (matches template)
Configuration:
- .claude/settings.json (12 allow rules, 3 hooks, 2 deny rules)
- .codex/config.toml (custom profiles: review, deploy)
Instruction Files:
- CLAUDE.md (project-specific, references @AGENTS.md)
- AGENTS.md (customized — 5 sections differ from template)
CI Workflows:
- .github/workflows/claude.yml (existing, may need permission updates)
User-scoped (informational):
- ~/.claude/projects/.../memory/feedback_testing.md
- ~/.claude/hooks/post-edit-format.sh (also referenced in project settings)
Wait for user to review before proceeding to migration steps.
1. Memory — ai-workspace/MEMORY.md
If both template and target have ai-workspace/MEMORY.md with real content:
Merge strategy (semantic, not line-by-line):
- Read both files completely. The template MEMORY.md has this seed structure:
# Project Memory > Living document updated by /reflect after each task. 200-line limit. > Last updated: — ## Key Decisions ## Implementation Status ## Gotchas - Preserve ALL content from the target's MEMORY.md — this is accumulated project knowledge
- If the target is missing sections from the template seed (Key Decisions, Implementation Status, Gotchas), add them as empty sections at the end
- If the target has extra sections not in the template, keep them
- Update the header comment to include the template's
> Living document...line if missing - Present the merged result and ask the user to confirm
If only target has content: keep it, add missing template sections. If only template has content: copy the seed template (fresh start).
2. Memory — Claude Code auto-memories (~/.claude/projects/)
These are user-scoped and managed by Claude Code's built-in memory system. They live outside the repo and are NOT migrated automatically. Instead, inform the user:
- List each memory file with its
name,type, anddescriptionfrom frontmatter - Explain: "These are Claude Code's per-project memories stored at
~/.claude/projects/.../memory/. They persist across sessions and inform Claude's behavior for this project." - Ask: "Are any of these stale or incorrect? I can flag them for your review but won't modify them during template application."
- If any
projectorreferencetype memories contain information that should be inai-workspace/MEMORY.mdinstead (so other agents can see it), suggest the user move that content manually
3. Plans and decisions
- Completed plans (
*.done.md): Always preserve — these are immutable records. Inform the user they exist. - Active plans (no
.done.mdsuffix): Ask if still in progress or should be marked done. - ADRs: Always preserve — immutable after commit. Check if any reference patterns that the template changes (warn if so).
- Templates (
TEMPLATE.md): Replace with template version if different (these are scaffolding, not content). - scratchpad.md: If non-empty, ask user whether to preserve, archive, or reset.
- workflow.md: If target has a custom workflow that differs from template, present both and ask which to keep (or merge).
4. Skills
For each skill directory in .claude/skills/:
- Not in template (custom skill): Keep as-is. Inform user it's preserved.
- Same name as template skill (e.g., both have
validate/SKILL.md):- Diff the two versions
- Present the differences to the user
- Options: keep target version, replace with template, or merge (user reviews merged output)
- Template has new skills not in target: These were already copied in Phase 2. Just inform the user.
5. Agents, rules, and commands
For each file in .claude/agents/ and .claude/rules/:
- Same file exists in both: Diff them. If target has customizations (model changes, extra instructions, project-specific context), present the diff. Default: keep target version for agents (they're often customized), keep template version for rules (they're more structural).
- New from template: Already copied in Phase 2. Inform user.
- Target-only (custom): Preserve. Inform user.
Check for shadowing between global and project agents:
- If
~/.claude/agents/architect.mdand.claude/agents/architect.mdboth exist, note that the project version takes precedence.
6. Settings and permissions
For .claude/settings.json:
- This was already smart-merged in Phase 3. Summarize what was added/changed.
- Specifically call out:
- Hooks: List which hooks exist in both global (
~/.claude/settings.json) and project. Per the hook placement rule: global hooks = personal workflow, project hooks = codebase contract. Warn if the same hook appears in both (unnecessary duplication of expensive hooks). - Deny rules: Any new deny patterns from the template
- Allow rules: Any new allow patterns (user should review these)
- Plugins: If
enabledPluginsordisabledMcpjsonServersare present, list them
- Hooks: List which hooks exist in both global (
For .claude/settings.local.json:
- Never touch. Inform user it exists and what it contains.
For .codex/config.toml:
- If target has custom profiles, preserve them. Merge any new template profiles that don't collide.
7. Instruction files (CLAUDE.md, AGENTS.md, GEMINI.md, .cursorrules)
- AGENTS.md: Already merged in Phase 3. Summarize what sections were added/preserved.
- CLAUDE.md: If target has a custom CLAUDE.md that doesn't just
@AGENTS.md:- Read it. Identify project-specific instructions vs boilerplate.
- Preserve project-specific content. Add template boilerplate sections if missing.
- GEMINI.md: If present, inform user. Template doesn't provide one — this is the user's custom file. Preserve.
- .cursorrules: If present, inform user that AGENTS.md is the preferred universal format. Ask if
.cursorrulescontent should be merged into AGENTS.md or kept separately.
8. CI workflows
If .github/workflows/claude.yml or claude-code-review.yml existed before template application:
- Diff with template versions
- Call out permission differences (
contents: readvswrite, missingallowed_tools) - Present upgrade path if target workflows have weaker permissions
9. Context exclusions (.claudeignore, .codexignore)
.claudeignore: Handle separately from.gitignore. Check if target has one. If so, union-merge (same strategy as.gitignore). If not, copy template version..codexignore: If present, preserve. Template doesn't provide one.
Summary prompt
After presenting all findings, ask:
Ready to apply these migration decisions? (y/n)
You can also:
- "skip" to skip migration entirely (all files from Phases 2-4 are already applied)
- "details <item>" to see the full diff for any item above
Key principle: This phase is informational and advisory. The user has final say on every decision. Present what exists, explain what the template expects, and recommend — but never act without confirmation.
Re-application
When running on a repo that already has ai-workspace/.template-version:
copy_if_absentfiles are smart-merged if present (picks up template additions while preserving customizations)smart_mergefiles are always re-merged (this is how template updates flow)- If the user passes
--force, overwritecopy_if_absentfiles completely instead of merging