---
name: gitflow
description: GitFlow workflow — routes to colocated CLI commands (skills/gitflow/cli/) for deterministic execution
argument-hint: "[-a] [-f|-r|-h|-s] <action> [name|version]"
cli: cli/
disable-model-invocation: true
allowed-tools: [Read, Glob, Grep, Bash, AskUserQuestion]  # Bash: CLI invocation
---

## Current state (auto-injected)
- Branch: !`git branch --show-current 2>/dev/null || echo "UNAVAILABLE"`
- Status: !`git status --short 2>/dev/null | head -10`
- Config: !`cat .claude/gitflow/config.json 2>/dev/null || echo "no config found"`

<objective>
Route GitFlow operations to colocated CLI commands in `skills/gitflow/cli/`.
Each CLI is self-contained (Zod types, validate, execute, orchestrate) and
returns a JSON envelope. You parse flags, call the RIGHT CLI, and interpret the
JSON for the user. The CLIs enforce the branch rules; you must not bypass them.
</objective>

<branch_rules>
The invariants the CLIs enforce — keep them in mind when interpreting results:

| Type | Base (branch FROM) | Target (PR/merge INTO) |
|------|--------------------|------------------------|
| feature | develop | **develop** (never main) |
| release | **develop** | main (+ merge-back develop, tag) |
| hotfix | main | main (+ merge-back develop, tag) |

A feature can NEVER target main. A release is cut from develop, then PR'd to main.
</branch_rules>

<flags>
**Mode flags — mutually exclusive, each ALWAYS routes to `start`:**
| Short | Meaning | Spec built for `start` |
|-------|---------|------------------------|
| `-f <name>` | feature | `{"type":"feature","name":"<name>"}` |
| `-r <version>` | release | `{"type":"release","name":"<version>"}` |
| `-h <name>` | hotfix | `{"type":"hotfix","name":"<name>"}` |

**Behavior flags:**
| Short | Meaning |
|-------|---------|
| `-a` | auto: create branch → commit → open PR, then **STOP** for review. NEVER runs `merge`/`finish`, NEVER auto-merges to main. |
| `-s` | status only |

⚠️ `-a` is NOT "do everything to main". For a release it means: create
`release/<version>` from develop and open its PR — then STOP. Merging the PR
(to main) and `finish` (tag + merge-back) are ALWAYS deliberate human steps.

⚠️ A mode flag (`-f`/`-r`/`-h`) ALWAYS means `start` for that type. NEVER map
`-r` to cleanup, abort, or any other action.

⚠️ `-r` REQUIRES a version. If none is given (e.g. bare `-r`, or `-r -a`), ASK
the user for it (AskUserQuestion) — never invent a version and never treat a
following flag like `-a` as the version. Versions keep their dots (`3.53.0` →
`release/3.53.0`); the CLI does the naming, don't pre-mangle it.
</flags>

<routing>

| User input            | CLI command                                                                  |
|-----------------------|------------------------------------------------------------------------------|
| `init <url>`          | 2-step deterministic flow — see `<init_flow>` (propose → confirm → create)    |
| `-f <name>`           | `npx --prefer-offline tsx skills/gitflow/cli/start/index.ts --spec '{"type":"feature","name":"<name>"}' --json` |
| `-r <version>`        | `npx --prefer-offline tsx skills/gitflow/cli/start/index.ts --spec '{"type":"release","name":"<version>"}' --json` |
| `-h <name>`           | `npx --prefer-offline tsx skills/gitflow/cli/start/index.ts --spec '{"type":"hotfix","name":"<name>"}' --json` |

**`start` spec fields:** `type` (required: "feature"|"release"|"hotfix"), `name` (required: branch/version name), `noPush` (bool, default: false), `dryRun` (bool, default: false).

| `commit [msg]`        | `npx --prefer-offline tsx skills/gitflow/cli/commit/index.ts --spec '{"message":"<msg>"}' --json`  |

**`commit` spec fields:** `message` (optional), `push` (bool, default: false), `noEfcore` (bool, default: false), `confirmDestructive` (bool, default: false, set after user confirms).

| `sync`                | `npx --prefer-offline tsx skills/gitflow/cli/sync/index.ts --spec '{}' --json`                    |

**`sync` spec fields:** `rebase` (bool, default: false).

| `pr`                  | `npx --prefer-offline tsx skills/gitflow/cli/pr/index.ts --spec '{}' --json`                      |

**`pr` spec fields:** `draft` (bool, default: false).

| `merge`               | `npx --prefer-offline tsx skills/gitflow/cli/merge/index.ts --spec '{}' --json`                   |

**`merge` spec fields:** `squash` (bool, optional), `merge` (bool, optional).

| `finish`              | `npx --prefer-offline tsx skills/gitflow/cli/finish/index.ts --spec '{}' --json`                  |

**`finish` spec fields:** `branch` (string, optional, defaults to current branch).

| `status` or `-s`      | `npx --prefer-offline tsx skills/gitflow/cli/status/index.ts --spec '{}' --json`                  |

**`status` spec fields:** `verbose` (bool, default: false).

| `cleanup`             | `npx --prefer-offline tsx skills/gitflow/cli/cleanup/index.ts --spec '{}' --json`                 |

**`cleanup` spec fields:** `force` (bool, default: false), `dryRun` (bool, default: false), `staleDays` (int, optional, positive).

| `abort`               | `npx --prefer-offline tsx skills/gitflow/cli/abort/index.ts --spec '{"git":true}' --json`         |
| `generate-msg`        | `npx --prefer-offline tsx skills/gitflow/cli/generate-msg/index.ts --spec '{}' --json`            |

**`cleanup`/`abort` ONLY run when the user types `cleanup`/`abort` explicitly.**
Never reach them from a `-f`/`-r`/`-h` invocation.

</routing>

<execution_discipline>
- **Without `-a` (default): run ONE step, then STOP and wait for the user.** Never
  chain steps automatically. The user drives the pace.
- **With `-a` (auto): run `start → commit → pr`, then STOP at the review gate.**
  Auto mode NEVER runs `merge` or `finish` — merging (especially the PR to **main**)
  and finalizing (tag + merge-back) are deliberate HUMAN steps after review.
  So `-r <version> -a` CREATES `release/<version>` (from develop) and opens its PR;
  it must NEVER jump straight to a merge on main. `-a` with no migrations/commits
  still creates the branch and stops.
- After review, the human runs `merge`, then `finish` — explicitly.
- Typical manual flow: `-f x` → `commit` → `pr` → (review) → `merge` → `finish`.
</execution_discipline>

<efcore_handling>
The commit / pr CLIs enforce EF Core migration rules (config-driven via
`config.efcore`; 4.x defaults: on). Handle their JSON like this:

- **`commit` returns `requiresConfirmation: true`** (a destructive migration —
  DropTable/DropColumn/DeleteData). Do NOT silently proceed and do NOT silently
  block — ASK:
  ```yaml
  AskUserQuestion:
    header: "Migration"
    question: "Destructive migration detected ({destructiveOps}). Continue?"
    options:
      - label: "Yes, I understand the risks"
      - label: "No, let me review"
  ```
  If yes → re-run commit with `confirmDestructive:true` in the spec. If no → stop.
- **`commit` fails with an incomplete-migration error** → the changeset is missing
  Migration.cs / Designer.cs / ModelSnapshot.cs. Tell the user to regenerate.
- **`pr` fails asking to squash** (feature with >1 migration) → tell the user to run
  `/efcore squash`, then retry `pr`.
- Always surface any `warnings[]` from a result to the user.
</efcore_handling>

<incident_guard>
If a `pr` or `merge` result reports a target that contradicts the branch type
(e.g. a feature PR targeting main), the CLI returns `success:false` with an
explicit error. SURFACE it loudly and tell the user to ABANDON the PR — do not
treat it as success. This is the guard against the feature→main incident; never
work around it.
</incident_guard>

<generate_msg_flow>

`generate-msg` is a two-phase process: YOU analyze, then the CLI formalizes.

**Phase 1 — Analysis (you do this):**

1. Run `git status --porcelain -uall` to get the list of changed files
2. Run `git diff --stat` to get an overview of changes
3. For each changed file, use the **Read** tool to understand the content
4. For modified files, run `git diff -- <file>` to see what changed precisely
5. Group changes by theme/module and identify the main intent
6. Determine:
   - `scope`: the primary module or component affected
   - `summary`: one-line description of what changed and WHY (max 72 chars, English)
   - Per-file: status (added/modified/deleted/renamed) + one-line summary

**Phase 2 — Formalization (CLI does this):**

Call the CLI with your analysis as structured JSON:

```bash
npx --prefer-offline tsx skills/gitflow/cli/generate-msg/index.ts --spec '{
  "branch": "<current branch>",
  "branchType": "<feature|release|hotfix|develop|main|other>",
  "scope": "<main module>",
  "summary": "<one-line summary>",
  "files": [
    {"path": "src/main/router.ts", "status": "modified", "summary": "add 7 new git operation routes"}
  ]
}' --json
```

The CLI returns a formatted conventional commit message in Markdown.

**Output:** The CLI result JSON has a `message` field. Output that message on the last line prefixed with:
```
COMMIT_MSG: <the message field from CLI result>
```

</generate_msg_flow>

<interpretation>

When the CLI returns JSON:
1. Check `success` field
2. If `false`: show `error` to the user, suggest the fix (and see `<incident_guard>` / `<efcore_handling>` for the special cases)
3. If `true`: summarize key fields in readable format
4. If `requiresConfirmation: true`: follow `<efcore_handling>`
5. If `needsInput: true` (init only): ask the user the required questions
6. Always surface `warnings[]` and show the **next step** suggestion

</interpretation>

<init_flow>

`init <url>` builds the worktree architecture in ONE deterministic CLI call — no
per-step work, no Opus in the loop. The CLI does everything: bare-clone into
`<root>/.git`, import `main` + `develop` as worktrees (`01-Main` / `02-Develop`),
create `features/releases/hotfixes`, and write the canonical config to
`<develop>/.claude/gitflow/config.json`. You only confirm name + layout first.

**Step 1 — propose (instant, touches nothing on disk):**
```bash
npx --prefer-offline tsx skills/gitflow/cli/init/index.ts --spec '{"url":"<url>","propose":true}' --json
```
Returns `proposal.name` (derived from the URL), `proposal.alternates`, `proposal.source`, `proposal.exists`.

**Step 2 — confirm with the user. Ask ONLY what a user can actually answer:**
- **Project name** — default `proposal.name`
- **Location** — where the project root goes. Default `<source>/<name>` (source = cwd). To clone INTO an existing folder whose name differs from the project (e.g. a client folder `D:/clients/TPF`), pass `root` directly. If `proposal.exists` is true the folder already holds a git repo → pick another root or pass `force: true`.
- **Folder naming** — the user's choice. Present it CONCRETELY, never as the git-jargon "organized / simple / disabled":

  ```yaml
  AskUserQuestion:
    - header: "Dossiers"
      question: "Nommage des dossiers worktree ?"
      multiSelect: false
      options:
        - label: "Numéroté — 01-Main / 02-Develop (recommandé)"
          description: "Préfixe 01-/02- (standard SmartStack)"
        - label: "Sans numéro — main / develop"
          description: "Dossiers au nom des branches, sans préfixe 01-/02-"
  ```

  → "Numéroté" sets `numbered: true`; "Sans numéro" sets `numbered: false`. That's the whole choice. The config's internal `mode` is DERIVED from it (numbered → `organized`, plain → `simple`) — never ask about `mode` directly.

**Before Step 3 — RECAP and get an explicit yes.** Show the resolved root, the detected branches, and the exact tree that will be created, using the folder names the user just chose (numbered shown here; plain = `main` / `develop`):
```
<root>/.git                          (bare repo)
<root>/01-Main      (or main)        ← <mainBranch>   (main/master, NOT the repo's default)
<root>/02-Develop   (or develop)     ← develop
<root>/{features, releases, hotfixes}
<root>/02-Develop/.claude/gitflow/config.json   (or develop/.claude/…)
```
Only run Step 3 once the user confirms the path + naming + structure.

**Step 3 — create (one deterministic call, does all the work):**
```bash
npx --prefer-offline tsx skills/gitflow/cli/init/index.ts --spec '{"url":"<url>","name":"<name>","source":"<source>","numbered":<true|false>}' --json
# …or with an explicit root (folder name ≠ project name):
# --spec '{"url":"<url>","name":"<name>","root":"D:/clients/TPF","numbered":true,"force":false}'
```
On success, `structure` lists the created `root` / `main` / `develop` / `configPath`.
Show them and suggest `cd <develop>` as the next step.

If `url` is missing the CLI returns `needsInput` with the reason — ask the user for it and retry.

</init_flow>

<workflows>

```
FEATURE:  -f → commit* → sync → pr ‖STOP review‖ → merge → finish          (target develop)
RELEASE:  -r <ver> → commit* → sync → pr ‖STOP review‖ → merge → finish     (+ tag + merge-back)
HOTFIX:   -h → commit* → sync → pr ‖STOP review‖ → merge → finish           (+ tag + merge-back)
```
`-a` automates ONLY up to `pr` (the ‖STOP‖). `merge` and `finish` are always
human-triggered after review — auto mode never crosses that line.

Release branches off develop, hotfix off main; both PR to main and merge back
to develop with a `v{version}` tag on finish.

</workflows>

<cli_structure>

Each CLI follows the Skill+CLI 4-file pattern:
```
cli/<command>/
├── types.ts      ← Zod schemas (input spec + output result)
├── validate.ts   ← Pure validation (no I/O, no git)
├── execute.ts    ← Execution (uses ../lib/, no validation)
└── index.ts      ← Orchestration: parse args → validate → execute → JSON
```

Shared utilities in `cli/lib/` (git, branch, config, worktree, provider, version, efcore, output, platform, paths).

</cli_structure>

<flags_cli>

| Flag | Description |
|------|-------------|
| `--json` | All commands support JSON output |
| `--spec` | JSON input spec (required for all commands) |
| `--workdir` | Override working directory |

</flags_cli>
