# Migrate

> >

- **Type:** Skill
- **Install:** `agentstack add skill-ramboz-jig-migrate`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [ramboz](https://agentstack.voostack.com/s/ramboz)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [ramboz](https://github.com/ramboz)
- **Source:** https://github.com/ramboz/jig/tree/main/skills/migrate

## Install

```sh
agentstack add skill-ramboz-jig-migrate
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

> Spec 008 created this skill from scratch. The deterministic filesystem
> walk + verdict logic + report rendering live in `migrate.py`; this
> SKILL.md drives the judgment layer (when to invoke, how to interpret
> ambiguities, what operations to suggest in what order).

## What this skill does

Closes the "already spec-driven" gap that `scaffold-init` doesn't handle:
projects that organically grew the same workflow jig codifies but landed
on different conventions (folder names, filename prefixes, slice
topology). Direct example: a project with `docs/slices/` (flat) and
`docs/decisions/` (validator-style) — `scaffold-init` would not detect
it as scaffolded and would either refuse confusingly or pollute the
tree.

`migrate` flips that around: detect existing shape first, then propose
a migration plan, then (in later slices) apply the rename / restructure
operations.

`migrate.py` exposes four subcommands:

- `report` — strictly read-only inventory + plan.
- `rename-decisions` — applies ADR-0004's rename. Idempotent; refuses
  on conflict; has a `--dry-run` mode; `--host claude|codex` selects
  whether cross-reference rewrites scan `CLAUDE.md`/`.claude/` or
  `AGENTS.md`/`.codex/`.
- `split-slices` — extracts embedded slice sections into sibling
  slice files.
- `copy-machinery` — copies jig runtime machinery into the target's
  host-local scaffold runtime; `--host claude` writes `.claude/`, and
  `--host codex` writes `.codex/`.

## How to use

### Run the migration report

```bash
python3 "${CLAUDE_PLUGIN_ROOT}/skills/migrate/migrate.py" report \
  
```

- `` — path to the project root (e.g. `/path/to/repo`,
  `.` for cwd).

### Run the rename-decisions migration

Once `report` has been reviewed and the verdict is `adoptable`, the
recommended sequence is:

```bash
# 1. Preview the plan (no writes).
python3 "${CLAUDE_PLUGIN_ROOT}/skills/migrate/migrate.py" \
  rename-decisions  --dry-run

# 2. After reviewing the planned operations, apply them.
python3 "${CLAUDE_PLUGIN_ROOT}/skills/migrate/migrate.py" \
  rename-decisions 
```

What it does, in display order:

1. `docs/adrs/` → `docs/decisions/` (directory rename, atomic).
2. Per-file renames: `NNN-.md` → `adr-NNNN-.md`
   (pad 3-digit to 4-digit; add `adr-` prefix where missing).
3. Cross-reference rewrites in text files under `docs/`, `CLAUDE.md`,
   and `.claude/` by default. With `--host codex`, rewrites scan
   `docs/`, `AGENTS.md`, and `.codex/` instead. The helper itself
   (`migrate.py` and its fixtures) is never rewritten.

Refusal cases (exit 2, no mutations):

- Both `docs/adrs/` and `docs/decisions/` present (manual merge first).
- Two source files normalize to the same target name (collision).
- `` missing, not a directory, or unreadable.

No-op cases (exit 0):

- Neither dir present, OR all files already on the canonical shape —
  emits "already aligned: nothing to do" and returns.

### Run the copy-machinery operation

`copy-machinery` brings a migrated project to scaffold-mode parity —
the same host-local runtime shape `/jig:scaffold-init` produces by
default for greenfield projects. See the dedicated section
[`## Copying machinery into your project`](#copying-machinery-into-your-project)
below for the full description.

Quick reference:

```bash
python3 "${CLAUDE_PLUGIN_ROOT}/skills/migrate/migrate.py" \
  copy-machinery 
```

### Exit codes

- `0` — verdict is `adoptable` OR `not-yet-spec-driven` (the report is
  the deliverable regardless of verdict).
- `1` — verdict is `partial` (borderline; report still emits, but
  `/jig:scaffold-init` may be a better fit).
- `2` — user error (missing argument, dir doesn't exist, target is a
  file not a directory).

### Verdict logic

`migrate` counts four migration triggers in ``:

1. Spec-or-slice dir (`docs/specs/` or `docs/slices/`).
2. Decision-or-ADR dir (`docs/decisions/` or `docs/adrs/`).
3. Workflow doc (`docs/workflow.md`).
4. Architecture doc (`docs/architecture.md`).

| Triggers | Verdict | Recommendation |
|---|---|---|
| 3 or 4 | `adoptable` | Proceed with the suggested operations |
| 2 | `partial` | Borderline — `scaffold-init` may be a better fit |
| 0 or 1 | `not-yet-spec-driven` | Run `/jig:scaffold-init` instead |

### Report structure

Six sections, in fixed order:

1. **Inventory** — table of detected artifacts (paths + counts + shape
   notes). Inventories everything the helper found, including items
   that are inventoried-only (spikes, custom skills, custom agents).
2. **Mapping** — table of "current path/name → jig target name". For
   ADR files this includes the 3-digit-to-4-digit pad and the `adr-`
   prefix add (per ADR-0004). For flat slices, the row points to the
   Ambiguities section because no automated topology mapping exists
   in slice 008-01.
3. **Conflicts** — situations that block specific migration operations
   (e.g. `docs/adrs/` AND `docs/decisions/` both present means
   `rename-decisions` refuses on this project). Empty if no conflicts.
4. **Ambiguities** — judgment calls the user must make. Common entries:
   "flat slices reference M1–M6 milestones — map each to a parent
   spec?"; "custom skills overlap jig's stock set — replace or
   layer?"; "CLAUDE.md is 59KB with sprint log — port subset or
   leave?".
5. **Contract surfaces detected** — *(added by spec 022-02)* flags
   external-interface artifacts already on disk and prose API contracts
   that would benefit from standard schemas (OpenAPI / JSON Schema /
   AsyncAPI / `.proto` / GraphQL SDL). Four detection types: (a)
   existing schema artifacts, (b) prose API contracts in canonical
   doc files, (c) env-contract triple (markdown + `.env.example` +
   checker), (d) hand-typed boundary types (e.g.
   `problem-details.ts`). Each detected surface gets a one-line
   classification + recommendation; "No contract surfaces detected"
   prose when empty. Companion to the `/jig:contracts` skill's
   per-surface recommendation table.
6. **Operations** — ordered list of `migrate.py ` calls
   the user should run, with `--dry-run` first. For slice 008-01,
   the only operations mentioned are future subcommands marked
   `(slice 008-NN, not yet implemented)` — so the report's main
   value right now is the first five sections.

## When to invoke

Auto-trigger phrases (in this SKILL.md's description):

- "migrate this project to jig"
- "adopt jig here"
- "this repo already has specs — set up jig"
- "scaffold-init refused — what now"
- "introduce jig to an existing codebase"

Typical session flow:

1. User invokes the skill against an existing project.
2. Helper runs `migrate.py report `.
3. SKILL.md (this file's body) helps interpret ambiguities: what's a
   real conflict vs. just an open question? What's safe to defer?
4. User makes the judgment calls flagged in Ambiguities.
5. Once future slices land (008-02 `rename-decisions`, 008-04
   `slice-to-spec`), the user runs those operations from the report's
   suggested order, with `--dry-run` first.

## End-to-end example

```bash
# 1. Inventory the project.
python3 .../migrate.py report /path/to/existing-project

# Expected output (when 3+ triggers are present):
#
#   # Migration report — `/path/to/existing-project`
#
#   **Verdict:** adoptable
#
#   _Three or more migration triggers detected. Proceed with the
#   operations below._
#
#   ## Inventory
#
#   | Path | Count | Note |
#   |------|-------|------|
#   | `docs/slices/` | 27 | flat slice files (validator-style) |
#   | `docs/decisions/` | 22 | decision records (ADR-0004 aligned) |
#   | `docs/spikes/` | 4 | spike memos (inventoried only) |
#   | `docs/workflow.md` | 1 | workflow doc present |
#   | `docs/architecture.md` | 1 | architecture doc present |
#   | `CLAUDE.md` | 1 | 59231 bytes (larger than baseline) |
#
#   ## Mapping
#
#   | Current | jig target | Note |
#   |---------|------------|------|
#   | `docs/decisions/` | `docs/decisions/` | kept (already aligned) |
#   | `docs/decisions/adr-001-foo.md` | `docs/decisions/adr-0001-foo.md` |
#       pad to 4-digit + ensure `adr-` prefix |
#   | `docs/slices/slice-NN-*.md` (27 files) | topology question |
#       no automated mapping in 008-01 |
#
#   ## Conflicts
#
#   _None detected._
#
#   ## Ambiguities
#
#   - **Flat slices reference 6 milestone(s) (M1, M2, M3, M4, M5, M6).**
#     Under jig's nested model, each could become a parent spec...
#
#   ## Operations
#
#   Suggested order (each operation is `--dry-run` first):
#
#   1. **`migrate.py rename-decisions `** (slice 008-02, not yet
#      implemented) — apply ADR-0004 rename...
#   2. **`migrate.py slice-to-spec `** (slice 008-04, not yet
#      implemented) — interactively map flat slices...
```

## Copying machinery into your project

`copy-machinery` brings a migrated project to scaffold-mode parity —
the same host-local runtime shape `/jig:scaffold-init` produces by
default for greenfield projects. After running it, the project owns its
own copy of jig's skills, agents, hook scripts, and hook registration.
The dev can edit those files in their own repo, and they ride along
under version control.

When to use it:

- **After `rename-decisions`** has applied ADR-0004 to existing ADRs.
- **After `split-slices`** has split any monolithic `spec.md` files
  with embedded slices into the file-per-slice layout.
- **Standalone**, when a project already has spec-driven layout but
  the dev wants the machinery in their tree (rather than only in the
  installed plugin under `${CLAUDE_PLUGIN_ROOT}`).

`migrate.py report` will surface this subcommand in the Operations
section when the verdict is `adoptable` or `partial` and the default
host scan does not find pre-existing jig-managed skills. Codex users can
also run the explicit `copy-machinery --host codex` command when they
want Codex runtime machinery even if the conservative report wording is
not the deciding signal.

Host selection:

- `--host claude` writes Claude scaffold machinery under `.claude/`
  and is the source-checkout default.
- `--host codex` writes Codex scaffold machinery under `.codex/`.
- `--host auto` is the CLI default; helpers copied under `.codex/skills/`
  infer Codex, and all other invocations infer Claude.

How to run it:

```bash
python3 "${CLAUDE_PLUGIN_ROOT}/skills/migrate/migrate.py" \
  copy-machinery 
```

What it does:

1. Copies skills into the host runtime (`.claude/skills/jig-/`
   or `.codex/skills/jig-/`), rewriting helper paths in SKILL.md
   bodies to that runtime.
2. Copies agents into the host runtime (`.claude/agents/jig-*.md` for
   Claude, `.codex/agents/jig-*.toml` for Codex).
3. Copies hook scripts into the host runtime, pinning each script's mode
   to `0o755`.
4. Generates or merges host hook registration. `--host claude` uses
   `.claude/settings.json`, with per-entry `metadata.managed_by_jig:
   true` markers. `--host codex` uses `.codex/hooks.json`, with a
   schema-clean top-level `hooks` object.

Subsequent runs are idempotent: re-running `copy-machinery` overwrites
the copied files in place and updates jig-managed hook registration. On
`--host claude`, non-jig hooks in `.claude/settings.json` survive
untouched. On `--host codex`, jig-managed `.codex/hooks.json` is
regenerated as a whole because Codex hook registration is a single file
and jig ownership is detected from generated jig hook command paths.

### Refusal: unmanaged hooks

If the host hook configuration already exists and is not jig-managed,
`copy-machinery` exits non-zero (exit code 3) and emits the
`UnmanagedHooksError` refuse-message to stderr — no filesystem writes
occur. For `--host claude`, this means `.claude/settings.json` has
hooks under `hooks.` but none carry the `managed_by_jig` marker.
For `--host codex`, this means `.codex/hooks.json` exists and does not look
like a jig-generated hook config. This matches the same safety stance
`scaffold-init` enforces.

The documented escape is `--force`:

```bash
python3 "${CLAUDE_PLUGIN_ROOT}/skills/migrate/migrate.py" \
  copy-machinery  --force
```

With `--force`, `--host claude` appends jig's hooks alongside existing
entries, which survive unchanged. With `--force`, `--host codex`
replaces an unmanaged `.codex/hooks.json` with jig's generated hook
registration. Use the Codex force path only when you are sure the
existing hook config should be replaced or has been backed up.

### Relationship to scaffold-mode

`migrate.py copy-machinery` is the migration-path equivalent of
`scaffold-init --with-machinery` (default since slice 016-03). Both
end up calling the same host-aware `copy_machinery(plugin, target, *,
force, host)` façade in `scaffold.py`, so the resulting host runtime
shape is equivalent regardless of which adoption path produced it.
Closing this gap for Claude was spec 021's reason for being; spec 059-01
extends the same adoption path to Codex.

## Agentic slice-to-spec migration

For projects where `migrate.py report` returns **Verdict: adoptable**
AND the inventory shows flat slice files under `docs/slices/` rather
than nested `docs/specs/NNN-slug/spec.md` form, there is no
deterministic helper to do the grouping (slice 008-04 was deferred
deliberately — see Non-goals on spec 020). Instead, the LLM driving
the migration follows the algorithm below. Output is a new
`docs/specs/` tree; originals stay where they are until the caller
verifies and chooses to clean up.

### When to invoke

After `migrate.py rename-decisions` (so ADR filenames are jig-shaped),
when the report's Ambiguities section names
**"Flat slices reference N milestone(s)"** or similar. The follow-up
is this agentic workflow. Do NOT run it if specs already live under
`docs/specs/NNN-slug/spec.md` — that's already the jig shape.

### Algorithm

For each migration, in order:

1. **Read milestone summaries.** Walk
   `docs/milestones/*.md` (or whatever the source's milestone-doc
   convention is). For each milestone, capture the title, scope,
   and slice list. These become the basis for spec naming.
2. **Decide milestone → spec mapping.** Each milestone becomes one
   spec folder. Naming convention:
   `docs/specs/NNN-mM-/` where:
   - `NNN` is a 3-digit spec number (start at 001, ascend per
     milestone order — match the project's chronology when
     possible);
   - `mM` is the milestone tag lowercased (e.g. `m1`, `m4.5`);
   - `` is a slug of the milestone's headline
     ("EDS thin E2E" → `eds-thin-e2e`).
   Skip milestones that have no slices.
3. **For each source slice file:**
   1. Read its body. Locate the milestone tag (prose
      `- **Milestone:** M1`, frontmatter `milestone: M1`, or
      filename prefix — adapt to what the source uses).
   2. Locate its status. The source's vocabulary is usually
      4-state — translate per the table below.
   3. Locate the heading. Source shape is usually
      `# Slice NN — Title` (H1, single number, no spec prefix).
      Transform to `## Slice NNN-NN — ` (H2, jig
      spec-slice fragment, slug-form title). Use the target spec's
      `NNN` from step 2 and the original slice number for the
      second `NN`.
   4. Prepend a frontmatter block (frontmatter shape per spec 015
      + 018):
      ```
      ---
      status: 
      dependencies: []
      last_verified:
      ---
      ```
      Leave `dependencies: []` for now — backfilling structured
      deps from prose "Depends on" lines is per-slice judgment
      work, not bulk-migration scope.
   5. Preserve the original slice body verbatim AFTER the new
      heading (Status / Milestone / Depends on / Estimated size
      prose lines included — they're harmless trailing context).
4. **Write new files** under
   `docs/specs/NNN-mM-/`:
   - `spec.md` synthesized from the milestone summary (header +
     overview + `## Decomposition` + `## Slices` link list).
   - One `slice-NN-.md` per source slice. Keep
     the original filename's shortname (no re-slugging) so existing
     cross-references resolve.
5. **Do NOT delete originals.** The source `docs/slices/` and
   milestone summaries stay in

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [ramboz](https://github.com/ramboz)
- **Source:** [ramboz/jig](https://github.com/ramboz/jig)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-ramboz-jig-migrate
- Seller: https://agentstack.voostack.com/s/ramboz
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
