# Hc

> Hunk-based atomic git commits for AI agents. Splits large diffs into precise, atomic commits by selecting specific diff hunks per commit.

- Skill: `deligoez/hc` (Agent Skill)
- Install (CLI): `npx skillmds@latest add deligoez/hc`
- Raw SKILL.md: https://api.skillmd.com/api/skills/deligoez/hc/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: deligoez (https://skillmd.com/u/deligoez)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/deligoez/hc

---


# hc -- Hunk Commits Skill

Hunk-based atomic commits for AI agents. One JSON plan, N commits. You assign hunks, hc handles all git mechanics (staging, committing, and -- for existing commits -- conflict-free history splitting). Works from any subdirectory of the repo; paths are always repo-root-relative.

## Activation

This skill activates when:
- A unit of work (one change + its passing test) is completed -- commit BEFORE starting the next unit, not at the end of the whole task
- The agent needs to create atomic commits from uncommitted changes
- The user asks for hunk-level commit granularity
- The agent has written multiple logical changes (e.g., several tests, feature + test, refactor + fix)
- The user asks to split, break up, or re-granularize EXISTING commits (use `hc log` + `hc rewrite`)

## Workflow

```bash
# Step 1: See what changed. ONE call gives you everything:
# indices, headers, enclosing function (section), and the changed lines (content).
# Do NOT run 'git diff' separately -- hc diff --json already includes hunk content.
hc diff --json

# Step 2 (RECOMMENDED): start from the draft plan -- never from a blank page.
hc plan > draft.json
```

`hc plan` emits the finest mechanical split: one commit per file, split further by enclosing section when a file's hunks span several. Test files (by filename/directory convention) are labeled `TODO test (...)` -- those entries follow the test rules below: one commit per NEW test, never merged. Every message is a `TODO (...)` placeholder and **`hc run` refuses TODO messages**, so each entry must be reviewed. Take the draft even when `hc diff --json` already gave you the whole picture: what it buys is not insight but **enumeration** -- every file and every hunk index is already assigned, which is the part hand-written plans get wrong once a diff passes a handful of files. You are editing a draft, not accepting a proposal. Your review job, per entry:

1. **Write a real commit message.** While writing it, sanity-check the entry is one idea.
2. **MERGE entries that belong together** -- mechanical sweeps, inseparable changes, one idea that happens to span sections. Merging is a conscious act; splitting is the default you inherit. Never merge `TODO test` entries for separate new tests.
3. **Drop untracked entries** you don't want committed.

```bash
hc run draft.json
```

Hand-written plans (heredoc) remain fine for small diffs -- but read each hunk's `section`/`content` before bundling, and heed the `review granularity` warning `hc run` emits when one commit bundles hunks from multiple sections of a file:

```bash
hc run - <<'PLAN'
{"commits":[{"message":"feat(auth): add login","files":[{"path":"auth.go","hunks":[0,1]}]}]}
PLAN
```

`hc run` is atomic at the plan level: the whole plan is validated (including a simulated apply of every commit) before the first real commit is created. A validation failure means nothing changed -- fix the plan and retry. `--dry-run` exists but is rarely needed; `hc run` performs the same validation anyway. If a run does stop part-way (exit 3 -- a lock, a hook, external interference), do NOT rewrite the plan: `hc run --continue` finishes it. See *Error Recovery*.

## Reading the diff

Each hunk in `hc diff --json` carries what you need to classify it -- never guess from headers alone:

- `content` -- the changed lines, `+`/`-` prefixed. Diffs use `-U0`, so this is exactly the change, no context lines. Very large hunks (>100 lines) arrive capped to a head/tail sample with `content_truncated: true` and `content_omitted_lines` -- the counts and fingerprint still identify the hunk; you almost never need the elided middle.
- `section` -- which section this hunk touches. Git labels a hunk with the declaration BEFORE its first changed line, so a hunk that edits a signature would read as the PREVIOUS function; hc re-attributes those to the function they declare, and hunks that only add or remove imports claim no section at all (they ride with the code that needs them). Both corrections apply to the per-file `sections` array and the `review granularity` warning too, so a signature change or an import riding along no longer looks like two ideas.
- **Docs and prose files (`.md`, `.rst`, `.txt`, ...) take sections from HEADINGS only.** A paragraph is never a section, however much it looks like code -- ``Thrown when `definition()` is not implemented`` is a sentence, not a declaration. Map the repo's docs to get headings instead (see *Teaching git where a section starts* below); unmapped, a prose file has no sections and falls back to the gap heuristic.
- Per-file `sections` -- the distinct sections the file's hunks touch, in order. **More than one entry = probably more than one idea**: plan hunk-level splits.
- **Signal hierarchy for "is this one idea?":** different files > different sections > distant regions. Non-adjacent changes are separate hunks and therefore split CANDIDATES -- nearby hunks in the SAME section are usually one idea, but far-apart regions in a sectionless file (configs, docs, top-level code) usually are not. `hc plan` encodes exactly this: sections first, then a ~8-unchanged-line gap fallback (skipped for scattered-many files like lockfiles, which are one mechanical change).
- `index` -- what you reference in the plan.
- Top-level `untracked` -- plain untracked paths (compact string array). They carry no hunks and never enter coverage validation; plan a path only to commit that new file.
- Top-level `warnings` -- non-fatal issues (e.g. pre-staged changes that `hc run` will reject). Always check it.

**File states and what to plan:**

| Diff entry looks like | State | Plan entry |
|---|---|---|
| `hunks: [...]` | Modified file | `{"path": ..., "hunks": [...]}` or omit `hunks` for whole file |
| Path in top-level `untracked` array | New file | Only if it should be committed: full-file `{"path": ...}` (no hunk indices exist). Otherwise ignore -- untracked files never enter coverage validation |
| `is_deleted: true` | Deleted file | `{"path": ...}` (full-file stages the deletion) |
| `is_binary: true` | Binary file | Full-file only; `hunks` is a validation error |
| `hunks: []`, no flags | Mode-only change (e.g. chmod +x) | Full-file: `{"path": ...}` |
| Old path deleted + new path untracked | Rename/move | TWO entries: `{"path": "old"}` and `{"path": "new"}` (may share a commit); git shows it as a rename in history automatically. **If you renamed with `git mv`, run `git reset HEAD` first** -- `git mv` STAGES the rename, and hc reads unstaged changes only, so until you unstage it `hc diff` cannot see the rename at all and `hc run` rejects the plan |
| `is_intent_to_add: true` (new file WITH hunks) | Stale `git add -N` from another tool | Nothing -- hc skips it from coverage and warns; plan its path only if you want it committed |

**Hunk boundaries are git's:** `-U0` merges edits on adjacent lines into ONE hunk, and hc cannot split inside a hunk. If two logical changes ended up in the same hunk, either commit them together or make the edits in separate passes next time.

## Teaching git where a section starts

Sections are git's, not hc's: hc reads the funcname git puts after the trailing `@@`. So a repo teaches git its boundaries **once**, in `.gitattributes`, and `hc diff --json`, `hc plan`, `hc split --hunks` and the `review granularity` warning all sharpen together. Three instances of one rule:

| `.gitattributes` line | Gives you | Without it |
|---|---|---|
| `*.php diff=php` (likewise `ruby`, `python`, `elixir`, ... -- `git help attributes` lists the built-ins) | Methods as sections in languages whose declarations are indented, which git's default regex does not see | The whole class reads as one section, and a new file stays one hunk |
| `*.md diff=markdown` | Headings as sections in docs and specs | A prose file has NO sections at all, and `hc plan` falls back to the contiguity-gap heuristic |
| `tests/**/*.php diff=phptest` plus `git config diff.phptest.xfuncname '^\s*(public\s+)?function\s+(test\|it_)\w+'` | Your own boundary rule -- here, only test methods open a section | git's per-language default applies |

hc honors whatever git reports and has no AST or language plugin; none is planned. If a repo already carries the first line, the other two are the same move.


## Plan Format

```json
{
  "commits": [
    {
      "message": "feat(auth): add login endpoint",
      "files": [
        {"path": "auth.go", "hunks": [0, 1]},
        {"path": "handler.go"}
      ]
    }
  ],
  "allow_unplanned": ["experiments/**"]
}
```

| Field | Type | Description |
|-------|------|-------------|
| `commits` | array | Ordered list of commits (required) |
| `commits[].message` | string | Non-empty commit message |
| `commits[].files` | array | Files in this commit (at least one) |
| `commits[].files[].path` | string | Relative path from repo root |
| `commits[].files[].hunks` | int[] | Hunk indices from `hc diff`. Omit to stage the whole file. |
| `allow_unplanned` | string[] | Globs excluded from coverage validation (`*` = one level, `**` = recursive) |

## Ticket / Prefix Conventions

- **Same ticket for the whole run:** pass it once -- `hc run --prefix "WB-1234: " -` prepends it to every commit message (idempotent: already-prefixed messages are left alone). Write plain conventional messages and let the flag do the rest.
- **Different tickets per commit** (umbrella branch, many issues in one run): write the ticket directly into each commit message -- `"message": "WB-2940: feat(auth): add login"`. Per-commit prefixes are the plan author's job; hc keeps messages otherwise opaque.

## Anti-patterns -- do NOT do these

- **Do NOT put untracked paths into `allow_unplanned` or into commits "to satisfy coverage".** Coverage validation only covers files with hunks in the diff. Entries in the top-level `untracked` array require NOTHING from you; reference one only when you actually want that new file committed. If you think hc demanded an untracked file, re-read the error -- it was about a different (tracked or intent-to-add) file.
- **Do NOT run `git diff`, `git add`, or `git commit` alongside hc.** `hc diff --json` has everything; `hc run` does all staging.
- **Do NOT re-run `hc diff` between commits of one plan.** One read, one plan, one run.
- **Do NOT batch the whole task and commit once at the end.** Commit after every completed unit (change + green test); stacked uncommitted edits fuse into inseparable hunks.
- **Do NOT defer test-writing to the end of the task.** The test belongs to the unit it covers, written right after the change.
- **Do NOT bundle same-kind work across files** ("add tests for X, Y and Z" in one commit). Unless it is a mechanical sweep or an inseparable change, every file is its own commit.
- **Do NOT bundle a change and its test into one commit, and do NOT bundle several NEW tests into one `test:` commit.** Every newly written test function is its own commit (classify via `section`). Only MODIFICATIONS to existing tests may group, and only when one context drives them. "It would bloat the history" is never a reason to merge -- history size is not a problem.
- **Do NOT write `hunks: [all indices]` (or omit `hunks`) for a multi-hunk file without reading each hunk's `section`/`content`.** This is the most common under-split. Start from `hc plan` instead -- it pre-splits by section -- and treat `hc run`'s `review granularity` warning as a prompt to re-check.

## Commit Cadence -- commit as you work, not at the end

Agents batch an entire task and commit once at the end -- sometimes 20+ changed files deep. Do not. Beyond review pain, deferring commits **destroys splittability mechanically**: edits made on top of uncommitted edits in the same region FUSE into one hunk. Two ideas that touch nearby lines become a single inseparable hunk that no tool can pull apart afterwards. Committing between ideas is the only moment separation is free.

- **The unit of work is: one change + its test passing.** When a unit is done, STOP and commit it -- as SEPARATE commits: one code commit (`feat`/`fix`/...), then its `test` commit -- before starting the next unit.
- **Write the test immediately after the change it covers** -- while the context is fresh -- never as an end-of-task batch. The unit is not done until its test is green.
- **Overdue check:** if `hc diff --json` shows changes in more than ~5 files (or a file has accumulated hunks from more than one idea), you have waited too long. Commit the completed units now; keep only the in-progress one uncommitted.
- Long refactors/sweeps are still ONE unit (one commit at their natural end) -- cadence does not mean fragmenting a mechanical change.

## Commit Granularity -- the most important rule

Agents systematically err toward commits that are TOO BIG. Default to splitting.

**The default unit is ONE FILE PER COMMIT.** A commit containing two or more files must justify itself against exactly two exceptions:

1. **Mechanical sweep:** the SAME repetitive transformation applied across many files -- a lint/format run, a rename, a comment reword, a codemod, an import reorder. One commit, message names the sweep (`style: apply linter across services`). The test: the per-file diffs are interchangeable in kind; describing one describes all.
2. **Inseparable change:** files that cannot compile or pass independently -- a signature change plus its call sites, code plus the new type it requires. Keep this narrow: "related" is NOT "inseparable". Same feature, same ticket, same directory are NOT reasons to combine.

Everything else splits:

- **Same KIND of change across files is not a sweep.** "Fork the Store* action tests" over 9 files is 9 commits (`test: fork StoreOrderAction test`, `test: fork StorePaymentAction test`, ...) -- each file reviews and reverts on its own. A sweep transforms existing lines mechanically; writing/forking N distinct files is N pieces of work.
- **Split within a file too -- the most-skipped rule.** If a file's hunks carry separable ideas, give each its own commit. Check the file's `sections` array first: more than one section usually means more than one idea. Worked example: a state-machine file with 5 hunks across `region`, `isReadyForSubmission` and a new endpoint = 3 commits (imports ride with the code that needs them), NOT `"hunks": [0,1,2,3,4]` in one.
- **Type boundaries are commit boundaries.** feat / fix / test / refactor / docs / chore never share a commit.
- **Tests are ALWAYS their own commits, one commit per NEW test.** A change and its test never share a commit -- the test commit follows immediately after the code commit it covers. Every newly written test function is its own commit, even when several live in the same file (classify each test hunk by its `section` -- the test function name): N new tests = N commits. The one softening: MODIFICATIONS to existing tests may share a commit when a single context drives them (e.g. one behavior change forces updates across several existing tests) -- new tests never ride along with those either.
- **The litmus tests:** (a) would the commit message still be accurate for each file alone? Then each file is its own commit. (b) Could `git revert` of this commit undo exactly one decision?
- **New files can't be hunk-split in the WORKING TREE.** If a new file will contain several logical changes, prefer creating it in separate passes and committing between them. (Once committed, `hc log`/`hc split --hunks`/`hc rewrite` CAN split a new file per section -- see below -- but authoring in passes is still the cleaner path.)

**History size is NEVER a problem -- internalize this.** Do not fear high commit counts: 30 one-file commits are better than 6 bundles, and 10 single-test commits are better than one `test: add tests` blob. There is no such thing as "too many commits" from correct splitting; hc executes large plans cheaply. When torn between merging and splitting, ALWAYS split.

## Commit Ordering

Order commits so the history builds cleanly:
1. Infrastructure / types / helpers with no dependencies first
2. Code that uses them second
3. Each test commit immediately after the code commit it covers (never in the same commit; see the granularity rules)

Goal: each commit should compile and pass tests on its own. hc creates commits strictly in plan order.

## Plan Writing Rules

1. **Run `hc diff --json` once, immediately before planning.** Classify each hunk from its `content` and `section`.
2. **Assign EVERY hunk to exactly one commit.** Complete coverage is validated; unassigned hunks are a hard error.
3. **Use original indices everywhere.** Even in later commits, reference hunks by their position in that one `hc diff` output -- hc rebuilds staged content from those original coordinates, so line-number shifts from earlier commits never matter.
4. **Match the plan entry to the file state** (see the table above): untracked/binary/mode-only/deleted are full-file; renames need both paths.
5. **Conventional commit messages** following the project's convention.
6. **Use `allow_unplanned` sparingly** -- only for TRACKED files with WIP changes that must stay uncommitted. Untracked and intent-to-add files never need it: they are only committed when you explicitly plan their path. `*` matches one path level; use `dir/**` for recursive.

## Common Patterns

**One commit per NEW test -- the rule for newly written tests (same file, classified via `section`):**
```json
{
  "commits": [
    {"message": "test(auth): add token expiry test", "files": [{"path": "auth_test.go", "hunks": [0]}]},
    {"message": "test(auth): add token refresh test", "files": [{"path": "auth_test.go", "hunks": [1]}]},
    {"message": "test(auth): add token revoke test", "files": [{"path": "auth_test.go", "hunks": [2]}]}
  ]
}
```

**Modified existing tests may group by driving context** (one behavior change forced all three updates -- new tests still commit separately):
```json
{
  "commits": [
    {"message": "test(auth): update expiry fixtures for new token TTL", "files": [{"path": "auth_test.go", "hunks": [0, 1, 2]}]}
  ]
}
```

**Feature + tests, one file per commit (default granularity):**
```json
{
  "commits": [
    {"message": "feat(auth): add refresh endpoint", "files": [{"path": "auth.go", "hunks": [0, 1]}]},
    {"message": "feat(auth): route refresh endpoint", "files": [{"path": "handler.go"}]},
    {"message": "test(auth): cover refresh endpoint", "files": [{"path": "auth_test.go"}]},
    {"message": "test(auth): cover refresh routing", "files": [{"path": "handler_test.go"}]}
  ]
}
```

**Mechanical sweep -- the one legitimate many-files commit:**
```json
{
  "commits": [
    {"message": "style: apply linter across services", "files": [
      {"path": "svc/a.go"}, {"path": "svc/b.go"}, {"path": "svc/c.go"}
    ]}
  ]
}
```

**Partial commit with WIP excluded:**
```json
{
  "allow_unplanned": ["experiments/**"],
  "commits": [
    {"message": "fix(db): close connections on timeout", "files": [{"path": "db.go", "hunks": [0]}]},
    {"message": "test(db): cover connection close on timeout", "files": [{"path": "db_test.go"}]}
  ]
}
```

## Splitting Existing Commits (`hc log` + `hc rewrite`)

Already-made commits that are too coarse (pre-hc history, or over-grouped runs) can be split retroactively.

**Fast path -- file-level splitting (the common case):**

```bash
# 1. Survey the range cheaply: per-file flags + hunk_count, NO hunk content.
hc log <base>..HEAD --files-only --json

# 2. Generate the default one-file-per-commit plan (multi-file commits split,
#    single-file commits and merges left as-is). Prints the plan; applies nothing.
hc split <base>..HEAD > plan.json

# 3. REVIEW the draft -- this is your semantic job:
#    - DELETE rewrites that are mechanical sweeps (lint/rename/codemod): they should stay one commit.
#    - Refine messages ({subject} ({basename}) is only a default; --message-template "{subject} :: {path}" etc.).
#    - Add within-file hunk splits where one file carries separable ideas (see below).

# 4. Validate, then apply.
hc rewrite --dry-run --summary plan.json
hc rewrite plan.json
```

**Hunk-level splitting within a file** works exactly like `hc run`: read that commit's entry from `hc log <range> --json` (WITH content) and assign hunk indices across replacement commits:

```json
{"rewrites": [{"commit": "a1b2c3d4e5f6", "commits": [
  {"message": "feat: edit A", "files": [{"path": "f.go", "hunks": [0]}]},
  {"message": "fix: edit B",  "files": [{"path": "f.go", "hunks": [1, 2]}]}
]}]}
```

**New files split per section too.** A commit that ADDED a whole file (e.g. a fresh test suite committed in one go) is not stuck as one hunk: `hc log --json` exposes the file as per-section synthetic hunks, `hc split --hunks` proposes one commit per section, and `hc rewrite` stages any subset -- so "one commit per NEW test" is enforceable retroactively on freshly-authored branches. Semantics:

- **In TEST files the split is per-TEST, not per-function:** only test-like functions (name conventions `test*`/`it_*`/`should_*`/..., or a `#[Test]`/`@test` attribute above) open groups; helpers, `setUp` and other support functions fold into the preceding group. Attribute/docblock/comment lines ride with the function they decorate. Non-test files split per function.
- **Intermediate commits stay syntactically valid.** The file's trailing closing scaffold (a class's closing brace) is exposed as a final `closing scaffold` hunk -- **assign it to the FIRST commit** (as `hc split --hunks` drafts do): later hunks insert before it by construction, so every intermediate file is closed. Preamble rides with the first group automatically.
- Section detection uses git's own funcname machinery, so a language whose declarations git does not recognize by default (indented methods: PHP, Ruby, ...) needs its `.gitattributes` line or the new file stays ONE hunk -- and a custom `xfuncname` driver redefines the boundaries with no hc involvement. See *Teaching git where a section starts*.
- Plain-text/config new files (no function-like sections) keep their single whole-file hunk.

Rules:

1. **`commit`** is a SHA (12-char prefix from `hc log`/`hc split` is fine). Commits NOT listed in `rewrites` are kept as they are -- their SHAs still change because ancestry changes, but message/author/date/content stay identical.
2. **Coverage applies per commit:** the replacement commits together must cover EVERY hunk of the original commit exactly once (same guarantee as `hc run`; no `allow_unplanned` here). **Hunk indices are per-file within the commit** (each file's hunks start at 0); for whole files just omit `hunks`.
3. **Granularity rules apply unchanged:** default one file per replacement commit; split a file's hunks further when they carry separable ideas; keep mechanical sweeps together (delete them from `hc split` drafts).
4. **Conflict-free by construction:** each split must reproduce the original commit's tree byte-for-byte (hc verifies it; the result reports `"tree_identical": true`), so downstream commits re-parent cleanly -- no rebase conflicts, and the working tree is never touched (uncommitted changes are safe).
5. **Do NOT re-run tests/builds after a rewrite.** The final tree is byte-identical, so every build/test result is unchanged by construction -- `tree_identical: true` in the result is the only verification needed.
6. **Merges mid-range are fine:** an untouched merge is preserved (re-parented with its other parents intact). Only SPLITTING a merge (or the root commit) is refused.
7. **Protect other people's history:** `--protect origin/develop` (repeatable) refuses any rewrite of commits reachable from that ref -- use it whenever the branch builds on shared history, instead of eyeballing the range.
8. **Safety rails:** the old head is saved at `refs/hc/backup/<branch>` (restore with `git reset --hard <backup-ref>`); commits already on a remote are refused unless `--force` (then `git push --force-with-lease`); requires a checked-out branch (no detached HEAD).
9. **`--dry-run`** builds and validates the whole new history (including tree invariants) without moving the branch -- it works even on pushed history without `--force`. Add `--summary` to get counts (`{split, replacements, kept, total_after}`) without the full replacement list.
10. Exit codes match `hc run`: 2 = plan problem, nothing changed; the branch only ever moves in one final atomic step.

## Error Recovery

Every error is JSON with `error`, `code`, and `hint` fields. Exit codes tell you the recovery path:

| Exit | Meaning | Recovery |
|------|---------|----------|
| 2 | Validation error. **No git state changed.** | Fix the plan per the `hint`, retry the same `hc run`. |
| 3 | Execution error mid-plan. Some commits exist. | Do NOT rewrite the plan -- follow the four steps below. |

**After exit 3 the recovery is mechanical.** The commits already created stay created, and `hc run --continue` finishes the rest of the SAME plan with the SAME hunk indices: hc re-derives the original diff from the commit the plan was written against, so index 1 still means the hunk you assigned to index 1.

1. Read `commits[]` -- entries with `"status": "committed"` are done, with their SHAs.
2. Clear what the `hint` names: wait out a lock, fix the pre-commit hook, free the disk.
3. Run `git reset HEAD` **if anything is still staged** -- `--continue` needs a clean index. hc leaves staging in place after a `git commit` failure by design, and it can also fail to clear it after a staging failure: that reset needs the same lock that just refused the staging call. When that happens the `hint` says so.
4. `hc run --continue` -- no plan argument, no `--prefix`; both come from the record the stopped run left behind.

**Never commit manually to "finish" the failed commit.** That moves HEAD, `--continue` refuses a record whose HEAD moved, and you are back to rewriting the plan by hand -- exactly what `--continue` exists to avoid.

Re-plan from `hc diff --json` only when hc refuses with `HEAD has moved since the interrupted run stopped`, or with `working tree content of <file> does not match the captured diff` (fixing the cause edited a file the plan covers).

Common validation errors:
- `staging area is not clean` -- something is pre-staged. Run `git reset HEAD`, then retry.
- `hunks [...] not assigned to any commit` / `has changes but is not in the plan` -- add the listed hunks/file to a commit or use `allow_unplanned`.
- `hunk index N out of range` -- the diff changed since you read it. Re-run `hc diff --json` and re-plan.
- `git commit failed` (exit 3) -- usually a pre-commit hook. Staging is intact; follow the four steps above.
- `Unable to create '<path>/index.lock': File exists` (exit 3) -- another git process (an editor, another agent, a background indexer) held the repository lock for the whole 2 s hc retries. The plan was never wrong: wait for that process, then continue. Raise the budget with `HC_LOCK_TIMEOUT=10s` if it keeps happening.

## Key Commands

| Command | Purpose |
|---------|---------|
| `hc diff --json` | Indexed hunks WITH content and section -- everything needed to plan |
| `hc plan > draft.json` | Draft plan: file-first + section-split, TODO messages (run refuses TODOs) |
| `hc diff` | Same, compact TTY view (no content) |
| `hc run - <<'PLAN' ... PLAN` | Execute plan from stdin (preferred) |
| `hc run plan.json` | Execute plan from file |
| `hc run --prefix "WB-1234: " -` | Prepend a uniform prefix to every commit message |
| `hc run --dry-run -` | Validate only (rarely needed; `run` validates first anyway) |
| `hc run --continue` | Finish a plan a stopped run left part-way -- no plan argument, same hunk indices |
| `hc log <base>..HEAD --files-only --json` | Cheap per-commit file survey (no hunk content) |
| `hc log <base>..HEAD --json` | Per-commit indexed hunks WITH content (for hunk-level splits) |
| `hc split <base>..HEAD` | Emit the default one-file-per-commit rewrite plan (review, then pipe) |
| `hc split --hunks <range>` | Same, plus within-file splits grouped by section (draft heuristic) |
| `hc rewrite - <<'PLAN' ... PLAN` | Split existing commits; conflict-free, backup ref kept |
| `hc rewrite --dry-run --summary -` | Validate a rewrite (counts only) without moving the branch |
| `hc --version` | Show version |

## Installation

```bash
# Install the binary
brew install deligoez/tap/hc   # alias for the deligoez-hc formula; installs the 'hc' binary

# Install this skill for Claude Code
npx skills add -g deligoez/hc
```

