# GJC Dev-Environment Migration — Mac → VPS (continue everything as-is)

> **PURPOSE (read this first, gajae-code).** The operator is moving an in-progress development setup from a macOS machine to a Linux VPS and wants to keep working **exactly as before** — same Claude Code + Codex + gajae-code (gjc) agents, the same skills, the same plugins, the same MCP servers, the same memory and hooks. Your job when this document is opened from the VPS `~/dev` folder is to **execute this runbook top to bottom on the VPS**, pausing only at the human-auth and choice gates marked ⛔. There are two transports (Phase 2): reconstruct-from-sources + re-auth, or restore a full secret-carrying bundle for a true zero-re-auth drop-in. Secrets, when carried, travel ONLY in the private scp'd bundle — this public guide URL never holds a credential. When done, the operator opens their project and continues.

- **This link is public — it contains ZERO secrets.** The guide only orchestrates. Secrets travel a different way (see below), never through this URL.
- **Idempotent.** Safe to re-run; steps check before acting.
- **Two transports, pick one at Phase 2:**
  - **(A) Reconstruct-from-sources** — install tools, clone the skills repo, reinstall plugins from marketplaces, then **re-authenticate** every credential on the VPS (Phase 5). Nothing secret moves. Cleanest, but you log in again.
  - **(B) Full bundle (carries ALL secrets, zero re-auth)** — on the Mac, `bundle.sh` packs the entire environment **including auth tokens, API keys, credentials, SSH keys, and memories** into `gjc-env-bundle.tar.gz`. You move that tarball to the VPS **over SSH only** (`scp`/`rsync`), run `restore.sh`, and the agents continue with the Mac's exact credentials — no re-login. The tarball is the secret channel; **this public guide URL is not**. Never upload the bundle anywhere web-facing; delete it after restore.

---

## Environment being reproduced (as detected on the source Mac)

| Component | Source of truth | Notes |
|---|---|---|
| gajae-code (`gjc`) | `bun add -g @gajae-code/coding-agent` | runs on **bun**; was v0.9.0 |
| Claude Code (`claude`) | `npm i -g @anthropic-ai/claude-code` | was v2.1.x |
| Codex (`codex`) | `npm i -g @openai/codex` | was v0.134.x |
| Personal skills | GitHub `doublek777/skills` | `./install.sh` symlinks into `~/.gjc/agent/skills` and `~/.claude/skills` |
| gjc plugins | marketplace `fivetaku/gptaku_plugins` | insane-research/-search/-design/-review, docs-guide, dd, goaljaby, git-teacher, vibe-sunsang, pumasi, show-me-the-prd, kkirikkiri, skillers-suda, nopal |
| Claude plugins | marketplaces below | fablize, codex(@openai-codex), everything-claude-code, lazyweb, vercel, caveman, legal |
| MCP servers | reconstruct | playwright (gjc+codex), context7, node_repl (codex) |
| Runtime deps | distro pkgs | node 24, bun 1.3, git, ripgrep, python3, build tools |

Claude plugin marketplaces to re-add: `fivetaku/fablize`, `anthropics/claude-plugins-official`, `anthropics/knowledge-work-plugins`, `affaan-m/everything-claude-code`, plus the `caveman`, `lazyweb`, `openai-codex` marketplaces the operator uses.

---

## Phase 0 — VPS prerequisites  ⛔ operator provides: SSH access, sudo, the VPS OS/arch

Run on the VPS shell.

```bash
# Confirm OS/arch first — the agent adapts package commands to the distro.
uname -a; cat /etc/os-release 2>/dev/null | head -3
mkdir -p ~/dev
```

Install base tooling (Debian/Ubuntu shown; translate for other distros):

```bash
sudo apt-get update
sudo apt-get install -y git curl ripgrep python3 python3-pip build-essential ca-certificates unzip
# Node 24 (NodeSource) — Claude Code & Codex are npm packages
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt-get install -y nodejs
# bun — gjc runs on bun
curl -fsSL https://bun.sh/install | bash
# make bun available in this shell
export BUN_INSTALL="$HOME/.bun"; export PATH="$BUN_INSTALL/bin:$PATH"
grep -q '.bun/bin' ~/.bashrc || echo 'export BUN_INSTALL="$HOME/.bun"; export PATH="$BUN_INSTALL/bin:$PATH"' >> ~/.bashrc
node -v && npm -v && bun --version
```

Install the three agents:

```bash
bun add -g @gajae-code/coding-agent      # gjc
npm i -g @anthropic-ai/claude-code       # claude
npm i -g @openai/codex                   # codex
gjc --version && claude --version && codex --version
```

---

## Phase 1 — GitHub access  ⛔ operator authenticates

Needed to clone the skills repo and the dev project.

```bash
# Option A: GitHub CLI
sudo apt-get install -y gh && gh auth login    # follow prompts (device code)
# Option B: SSH deploy key
ssh-keygen -t ed25519 -C "vps-$(hostname)" -f ~/.ssh/id_ed25519 -N ""
cat ~/.ssh/id_ed25519.pub   # ← operator adds this to GitHub → Settings → SSH keys
```

---

## Phase 2 — Choose transport

- **(A) Reconstruct from sources.** Continue to Phase 3, then re-authenticate at Phase 5. Personal skills come from the git repo; plugins from their marketplaces; hand-authored config is re-created. Nothing secret crosses. Use this if you'd rather log in fresh on the VPS.
- **(B) Full bundle restore — carries ALL secrets, no re-auth (matches "continue exactly as-is").** On the **Mac**, from this `env-migration/` folder:
  ```bash
  bash ~/skills/env-migration/bundle.sh            # → ~/gjc-env-bundle.tar.gz  (FULL, contains secrets; --slim to drop sessions; --encrypt for at-rest crypto)
  scp ~/gjc-env-bundle.tar.gz  user@vps:~/         # SSH only — never a web link
  ```
  On the **VPS**:
  ```bash
  tar xzf ~/gjc-env-bundle.tar.gz -C ~             # → ~/bundle/
  bash ~/skills/env-migration/restore.sh ~/bundle  # lays back config+secrets+memories, fixes perms
  rm -rf ~/bundle ~/gjc-env-bundle.tar.gz          # delete the secret bundle after restore
  ```
  Then do Phase 0 (tools, if not yet), Phase 3 (skills repo), Phase 4 (plugins), Phase 6 (project + verify). **Skip Phase 5 re-auth — credentials are already in place.**

> Want a true drop-in with zero re-login? Use (B). Want a clean slate with fresh logins? Use (A).

---

## Phase 3 — Personal skills (the fable-* pipeline + others)

```bash
git clone https://github.com/doublek777/skills ~/skills   # or git@github.com:doublek777/skills.git
cd ~/skills && ./install.sh
# Verify the symlinks resolved into the gjc user skill dir
ls -la ~/.gjc/agent/skills | grep fable
```

`install.sh` links every `skills/<name>` into `~/.gjc/agent/skills/`. For Claude Code, also link the ones the operator uses there:

```bash
mkdir -p ~/.claude/skills
for s in fable-project-plan fable-frontend-design fable-project-review fable-service-verify project-review; do
  ln -sfn ~/skills/skills/$s ~/.claude/skills/$s
done
```

---

## Phase 4 — Plugins & MCP

### 4a. gjc plugins (gptaku marketplace)

```bash
gjc plugin marketplace add https://github.com/fivetaku/gptaku_plugins.git
for p in insane-research insane-search insane-design insane-review docs-guide dd goaljaby \
         git-teacher vibe-sunsang pumasi show-me-the-prd kkirikkiri skillers-suda nopal; do
  gjc plugin install "$p@gptaku-plugins" || echo "check name: $p"
done
gjc plugin list
```
(If a subcommand name differs on this gjc version, run `gjc plugin --help` / `gjc --help` and adapt; the marketplace + plugin names above are authoritative.)

### 4b. Claude plugins

```bash
claude plugin marketplace add fivetaku/fablize
claude plugin marketplace add anthropics/claude-plugins-official
claude plugin marketplace add anthropics/knowledge-work-plugins
claude plugin marketplace add affaan-m/everything-claude-code
# (add the caveman / lazyweb / openai-codex marketplaces the operator uses, by their repo)
claude plugin install fablize@fablize
claude plugin install codex@openai-codex
claude plugin install everything-claude-code@everything-claude-code
claude plugin install lazyweb@lazyweb
claude plugin install vercel@claude-plugins-official
claude plugin install caveman@caveman
claude plugin install legal@knowledge-work-plugins
claude plugin list
```

### 4c. fablize always-on (make Opus run like Fable)

```bash
FB=$(claude plugin list 2>/dev/null | grep -o '/[^ ]*fablize/[0-9.]*' | head -1)
[ -n "$FB" ] && bash "$FB/setup/setup.sh" global   # injects the operating block into ~/.claude/CLAUDE.md
# Register the early-stop Stop hook if not already present (see the operator's Mac settings.json for the exact shape):
#   ~/.claude/settings.json → hooks.Stop → bash "$FB/hooks/finish-the-work.sh"
```

### 4d. MCP servers  ⛔ re-enter any MCP secrets

Re-add the MCP servers the operator ran. None of these carry secrets except where noted:

- **playwright** (gjc + codex): `npx -y @playwright/mcp@latest` — then `npx playwright install --with-deps chromium` on the VPS for headless browsing.
- **context7** (codex): needs the operator's context7 API key ⛔.
- **node_repl** (codex): local, no secret.

Add them via each agent's MCP config:
- gjc: `~/.gjc/agent/mcp.json`  (`{"mcpServers": {"playwright": {"command": "npx", "args": ["-y","@playwright/mcp@latest"]}}}`)
- codex: `~/.codex/config.toml` `[mcp_servers.playwright]` etc.
Confirm with `gjc mcp list` / `codex mcp list` (or the agent's help) after adding.

---

## Phase 5 — Authenticate secrets  ⛔⛔ (SKIP entirely if you used transport B / full bundle)

**Transport B (full bundle):** credentials, API keys, `~/.codex/auth.json`, Claude credentials, gjc auth, and SSH keys were already restored by `restore.sh` — the agents are logged in. Skip to Phase 6. (If the bundle carried `shell-env-exports.txt`, make sure those `export` lines are in `~/.bashrc`.)

**Transport A (reconstruct):** nothing came over the link — authenticate each on the VPS:

```bash
claude            # first run → login (Anthropic account / API key)
codex login       # OpenAI / ChatGPT auth  (also enables /codex:review used by fable-project-review)
gjc               # first run → set model provider auth as on the Mac (see ~/.gjc/agent/models.yml shape)
```

Provider API keys the setup may need (export in `~/.bashrc`, or the agent's own auth store — never in this doc):
`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and any others (xAI, etc.) the operator used. Put the private ones in the shell profile or each tool's credential store, not in any repo.

GitHub (if not done in Phase 1), and Cloudflare token only if the workflow needs it (`CLOUDFLARE_API_TOKEN`).

---

## Phase 6 — All dev projects + verification

The operator wants **every project under `~/dev` set up exactly as-is**. There are git-backed projects AND non-git folders, and several git projects have **uncommitted local changes** — so how they arrive depends on the transport:

### Transport B (full bundle) — everything, exactly as-is (recommended for "그대로")
`restore.sh` already laid the entire `~/dev` tree back — all projects, uncommitted changes, local branches, and the non-git folders (`master`, `supabase`, `xmap-private`) that a clone would miss. Just rebuild dependencies per project:
```bash
for p in ~/dev/*/; do [ -f "$p/package.json" ] && (cd "$p" && (pnpm install || npm install || bun install)); done
```

### Transport A (reconstruct) — clone from remotes (⚠️ pushed commits only)
This gets committed+pushed state only; **uncommitted work does NOT come this way**, and non-git folders are skipped. Before relying on A, commit+push on the Mac (or use the `project-freeze` skill) — otherwise use transport B.
```bash
cd ~/dev
git clone https://github.com/doublek777/act-place.git     actplace
git clone https://github.com/doublek777/joseonsaju.git    joseonsaju
git clone https://github.com/doublek777/lauchpack.git     launchpack
git clone https://github.com/doublek777/matgim.git        matgim
git clone https://github.com/doublek777/scally.git        navertracker
git clone https://github.com/doublek777/xmap.git          xmap
# NOT in git (bundle-only): master, supabase, xmap-private — carry via transport B if needed
for p in ~/dev/*/; do [ -f "$p/package.json" ] && (cd "$p" && (pnpm install || npm install || bun install)); done
```
> As of the source-Mac snapshot, uncommitted changes existed in actplace (~90), matgim (~150), navertracker (~36), xmap (~15). If those matter, use transport B or commit+push first.

### Verify the whole environment (both transports)
```bash
gjc --version && claude --version && codex --version
ls ~/.gjc/agent/skills | grep -c fable                     # expect 4  (skills present)
gjc plugin list && claude plugin list                      # plugins present
gjc mcp list 2>/dev/null; codex mcp list 2>/dev/null       # MCP present
ls ~/.gjc/agent/memories ~/.codex/memories 2>/dev/null     # memory present (transport B)
ls ~/.gjc/agent/hooks ~/.claude/hooks 2>/dev/null          # hooks present
grep -c FABLIZE ~/.claude/CLAUDE.md 2>/dev/null            # fablize block present
ls ~/dev                                                    # all projects present
```
Smoke test: launch `gjc` in a project, run `/skill:fable-project-plan` (or open the existing plan), confirm skills load, plugins resolve, memory is there, and Codex review delegation works (`/codex:review` from Claude). Then continue the actual work.

---

## Coverage summary — what transfers

| Thing | Transport A (reconstruct) | Transport B (full bundle) |
|---|---|---|
| Agents (gjc/claude/codex) | installed fresh | installed fresh |
| **Skills** (fable-* + others) | ✅ git repo + `install.sh` | ✅ git repo + `install.sh` |
| **Plugins** (gptaku + claude) | ✅ reinstalled from marketplaces | ✅ reinstalled + manifests restored |
| **MCP** servers | ✅ re-added | ✅ config restored |
| **Memory** (memories/, dbs) | ❌ fresh | ✅ carried |
| **Hooks** | re-created | ✅ carried |
| **All ~/dev projects** | ⚠️ pushed git only | ✅ exact working trees incl. uncommitted + non-git dirs |
| **Secrets/auth** | re-login (Phase 5) | ✅ carried (no re-login) |

**For a true "everything as-is including memory, hooks, and all projects" migration, use Transport B (full bundle).** Transport A is the clean-slate option and loses memory + uncommitted work.

## What is intentionally NOT carried
- Regenerable bulk: `node_modules`, build outputs (`.next`/`.turbo`/`dist`), caches, sqlite WAL/SHM sidecars → rebuilt on the VPS (`install`, first run).
- Machine-specific paths / the macOS cmux CLI shims.
- Nothing is ever placed on the public guide URL — secrets and project code travel only in the private scp'd bundle.
