|
root / docs / superpowers / specs
specs Plain Text 331 lines 18.9 KB
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# isurus-hg plugin — design

**Date:** 2026-05-01
**Status:** approved (pending user review of this document)
**Authors:** Chris Tusa, with Claude (brainstorming session)

## Context

Isurus (Leafscale's Mercurial forge product) is developed in Mercurial 7.x. Claude Code's general competence covers `git` workflows well but is uneven on `hg` — bookmarks, named branches, phases, and Isurus's house conventions in particular are easy to get wrong without explicit guidance. The Isurus repo's `CLAUDE.md` already says "never use git commands," but that instruction is enforced only by the model reading and remembering it; in practice, `git status` slips out occasionally during long sessions.

This plugin makes Claude fluent in Mercurial (with an Isurus-specific overlay) and adds a hook that prevents accidental `git` commands inside `.hg` repos.

## Goals

1. Teach Claude to do common Mercurial workflows correctly in any hg repo.
2. Encode Isurus's house conventions (non-publishing repo, bookmarks-as-PRs, commit message style, pre-PR Makefile checklist) so Claude follows them automatically when working in Isurus.
3. Mechanically prevent the most common failure mode: reaching for `git` commands inside an hg repo.
4. Keep the generic Mercurial knowledge separable from the Isurus-specific overlay so the plugin is useful for any hg user (and is publishable externally if/when desired).

## Non-goals (deferred to phase 2 or beyond)

- Isurus forge integration (PRs, code review, CI, issues) via MCP — would benefit from a stable skill foundation first.
- `evolve` + `topics` workflow support — not enabled in Isurus today.
- Slash commands for common workflows — easy to add once recurring patterns are visible.
- Subagents (e.g. an `hg-operator` for risky multi-step operations) — complexity unjustified until skills prove insufficient.
- Formal eval framework via `skill-creator` — manual scenarios are sufficient until real drift appears.
- CI for the plugin itself — solo project; on-demand shell tests are enough.
- `CHANGELOG`, `CONTRIBUTING`, `SECURITY` files — YAGNI for solo development.

## Architecture

A standard Claude Code plugin layout. Six skills (5 generic + 1 Isurus overlay), one PreToolUse hook scoped to `Bash`, and a manifest. No build step, no runtime dependencies beyond shell tools and Mercurial 7.x.

### File layout

```
.claude-plugin/
  plugin.json
hooks/
  hooks.json
  block-git-in-hg.sh
  test/
    run.sh
    cases/
      01-git-in-hg-blocks.sh
      02-git-outside-repo-allows.sh
      03-git-inside-nested-git-allows.sh
      04-hg-command-allows.sh
      05-env-var-prefix-stripped.sh
      06-non-git-command-allows.sh
      07-cd-then-git-allows.sh
skills/
  mercurial-basics/SKILL.md
  mercurial-bookmarks-and-prs/SKILL.md
  mercurial-sync-and-merge/SKILL.md
  mercurial-history-rewriting/SKILL.md
  mercurial-finishing-a-branch/SKILL.md
  isurus-conventions/SKILL.md
scripts/
  check-skills.sh
tests/
  MANUAL.md
docs/
  superpowers/
    specs/
      2026-05-01-isurus-hg-plugin-design.md   # this document
README.md
.hgignore
.gitignore
```

### Repository hosting

The plugin lives in Mercurial (eating Isurus's own dogfood) with a git mirror via the `hg-git` extension for Claude Code marketplace consumption. The hg side is canonical; the git side is generated mechanically from hg history. `.hgignore` excludes `.git/` and `.gitignore` excludes `.hg/` so the two metadata directories don't fight.

## Components

### Skills

Each skill is a single `SKILL.md` with frontmatter (`name`, `description`) followed by instructions. The `description` is the activation trigger; descriptions are concrete and scoped so skills load only when their domain is in play and don't pollute non-Mercurial work.

Each skill body opens with a short "When this applies / when it doesn't" paragraph to prevent misuse.

#### Generic Mercurial skills (5)

| Skill | Activation trigger (frontmatter description) | Body covers |
|---|---|---|
| **mercurial-basics** | "Use when working in any Mercurial repository for inspection or local commits — `hg status`, `diff`, `log`, `add`, `commit`, `parents`, `heads`, `branches`, `bookmarks`, `phase`. Triggers on any `hg ...` command or when `.hg/` is in the working directory." | Core inspection commands; commit hygiene including *no `-u` flag* (identity from `~/.hgrc`); commit message format (imperative summary <72 chars, body explains *why*, "category: summary" prefix); `.hgignore` syntax; reading `hg log` output; phase awareness as a foundational concept. |
| **mercurial-bookmarks-and-prs** | "Use when starting feature work, switching between in-progress work, or pushing a feature for review in a Mercurial repo. Bookmarks are the unit of feature work and the unit of pull requests in this workflow." | Create/list/delete bookmarks; bookmark vs named branch (when to use which); `hg push -B name`; switching active bookmarks; what happens to a bookmark when you commit; deleting after merge; recovering when a bookmark is in the wrong place. |
| **mercurial-sync-and-merge** | "Use when pulling changes from a remote, updating to a different revision, dealing with multiple heads, or resolving merge conflicts in a Mercurial repo." | `pull`, `pull -u`, `update`, what creates multiple heads, how to merge them, `hg merge`, `hg resolve` (mark/unmark/list/--all), aborting a merge, fast-forward-style updates vs explicit merges. |
| **mercurial-history-rewriting** | "Use when rewriting Mercurial history — amending commits, uncommitting, splitting commits, or recovering from a mistake. Critical: only safe on draft changesets, never on public ones." | `hg amend`, `hg uncommit`, `hg rollback` (and why it's mostly deprecated), splitting a commit (`hg uncommit` + selective `hg add`), draft-vs-public phase rules, safe recovery patterns; explicit refusal to rewrite public history. Calls out absence of evolve/topics today and notes those would unlock more (deferred to phase 2). |
| **mercurial-finishing-a-branch** | "Use when an implementation is complete and ready to be proposed as a pull request — runs the pre-PR checklist (build, vet, format, test) and walks through pushing the bookmark and opening a PR. The Mercurial counterpart to superpowers' `finishing-a-development-branch`." | End-to-end flow: confirm clean working dir, run project's pre-PR checks (detects a `Makefile` and uses `make check` / `make fmt` / `make vet` if present, else falls back to language defaults), squash/amend if commits are messy, `hg push -B`, instructions for opening the PR in the forge UI. References `mercurial-history-rewriting` for amend cleanup and `isurus-conventions` if active. |

#### Isurus overlay skill (1)

| Skill | Activation trigger | Body covers |
|---|---|---|
| **isurus-conventions** | "Use when working in the Isurus repository (or any repo identified as Isurus by the presence of `Isurus` in `README.md` or `cmd/isurus/`). Encodes Isurus-specific Mercurial conventions: non-publishing repo, bookmarks-as-PRs, commit message style, pre-PR Makefile checklist, opening PRs in the Isurus web UI." | Conventions distilled from Isurus's `CLAUDE.md` and `CONTRIBUTING.md`: non-publishing means amend during review is safe; clone URL pattern (`ssh://hg@hg.leafscale.com/...`); commit message conventions used in this repo (`category: summary`); `make check` / `make fmt` / `make vet` are the pre-PR gates; PRs are opened in the Isurus web UI after `hg push -B`. Short — mostly facts, not procedures. The procedures live in the generic skills; this overlays the project-specific values. |

#### Skill design principles

- Activation precision: descriptions explicitly mention `.hg/` presence or specific `hg` commands so skills load only when actually working with Mercurial.
- Each skill targets ~150–400 lines. Crossing 400 is a signal to spin out a sub-skill.
- Cross-references are explicit. `mercurial-finishing-a-branch` references the other skills it relies on rather than duplicating their content.
- `isurus-conventions` is small and additive — it doesn't repeat what the generic skills say; it supplies the project's specific values (URLs, command names, gate commands).

### Hook: `block-git-in-hg.sh`

A `PreToolUse` hook scoped to the `Bash` tool. Configuration in `hooks/hooks.json`:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/block-git-in-hg.sh" }
        ]
      }
    ]
  }
}
```

#### Detection logic

1. Read tool-call JSON from stdin; extract `tool_input.command` and `cwd`.
2. Normalize the command: trim leading whitespace; skip leading `VAR=value` env assignments.
3. If the first token is not `git`, exit 0 (allow).
4. Determine VCS context for `cwd`:
   - **Bare git repo check:** if `cwd` itself contains `HEAD`, `refs/`, and `objects/` (the bare-repo signature), treat as a git context → exit 0 (allow).
   - Otherwise walk upward from `cwd` looking for the closest `.hg/` or `.git/`:
     - `.git/` found first (or only `.git/` exists) → exit 0 (allow). Real git repo, real git command.
     - `.hg/` found first → block (exit 2 with stderr message).
     - Neither found → exit 0 (allow). Outside any repo (e.g. `git --version`).

This rule handles Isurus's `internal/import/testdata/*/repo.git/` fixtures via the bare-repo check: those directories *are* the git metadata (no `.git/` subdirectory), so the bare-repo signature matches and `git` commands inside them pass through even though an `.hg/` exists higher up the tree.

#### git → hg suggestion map

| `git` | suggested `hg` |
|---|---|
| `status` | `hg status` |
| `add` | `hg add` |
| `commit -m "..."` | `hg commit -m "..."` *(no `-u`)* |
| `log` | `hg log` |
| `diff` | `hg diff` |
| `branch` | `hg bookmarks` *(in this workflow)* |
| `checkout <name>` | `hg update <name>` |
| `pull` | `hg pull -u` |
| `push` | `hg push -B <bookmark>` |
| `blame` | `hg annotate` |

Anything not in the table gets a generic message: "This is a Mercurial repository. The `mercurial-basics` skill has the equivalent commands."

#### Sample blocked-call output

```
git command blocked: this is a Mercurial repository (.hg/ found at /home/ctusa/repos/isurus-project/isurus).

You ran:    git status
Use:        hg status

See the mercurial-basics skill for the full command map.
```

#### Acknowledged limitations (not addressed in v1)

- Compound commands like `cd /tmp && git init` are only inspected at the leading token; if intent is to run git in a different directory, prefix with `cd` and the hook won't block (intended escape valve).
- Subshells / pipes (`git status | grep modified`): leading token is still `git`, will be blocked. Correct behavior.
- Quoted commands inside `bash -c "..."`: not inspected. Acceptable hole; rare in practice.
- No allowlist mechanism. Override by commenting out the hook entry. YAGNI on building an allowlist before there's a real use case.

### Manifest: `.claude-plugin/plugin.json`

```json
{
  "name": "isurus-hg",
  "version": "0.1.0",
  "description": "Mercurial fluency for Claude Code, with an Isurus-conventions overlay and a hook that prevents accidental git use in hg repos.",
  "author": {
    "name": "Chris Tusa",
    "email": "chris.tusa@leafscale.com",
    "url": "https://leafscale.com"
  },
  "homepage": "https://www.leafscale.com/products/isurus",
  "repository": "https://leafscale.isurus.dev/isurus/isurus-claude-plugin",
  "license": "MIT",
  "keywords": ["mercurial", "hg", "isurus", "version-control"]
}
```

Version starts at `0.1.0` — pre-1.0 signals "stabilizing" and lets skill copy evolve without breaking semver promises.

### `README.md` (root)

Three sections:
1. **What it does** — one paragraph + bulleted list of the 6 skills and the hook.
2. **Install** — `--plugin-dir` flag for development/testing, plus a local-marketplace recipe for persistent use, with a "remote marketplace coming soon" note for after the git mirror is set up.
3. **Hooks behavior** — one paragraph explaining the git-blocking hook so a user understands why their `git status` got rejected.

No `CHANGELOG`, `CONTRIBUTING`, or `SECURITY` files for v1.

### Licensing

MIT. The plugin is mostly generic Mercurial knowledge plus an overlay describing how to use Isurus; making it freely redistributable supports adoption signal and onboarding for the Isurus forge, and avoids friction for outside contributors who don't have a Leafscale CLA.

## Testing & validation

### 1. Hook script — automated shell tests

The hook is a pure function: JSON in, exit code + message out. Tests live under `hooks/test/` and exercise the script in isolation, no Claude in the loop.

Each case file (`hooks/test/cases/NN-description.sh`) builds a temp directory with the right `.hg/` or `.git/` skeleton, pipes synthetic tool-call JSON through `block-git-in-hg.sh`, and asserts on exit code and stderr substring. ~10 lines per case. `run.sh` (~20 lines) iterates the cases and prints PASS/FAIL.

Initial cases:

1. `01-git-in-hg-blocks.sh` — `git status` inside an `.hg` repo blocks with exit 2 and includes the `hg status` suggestion.
2. `02-git-outside-repo-allows.sh` — `git --version` in `/tmp` (no `.hg` or `.git` ancestor) exits 0.
3. `03-git-inside-nested-git-allows.sh` — `git log` inside a regular `.git` working dir nested under an `.hg` directory exits 0 (closer `.git` wins). Also covers the bare-repo case (`HEAD` + `refs/` + `objects/` present in `cwd`) inside an `.hg` ancestor.
4. `04-hg-command-allows.sh` — `hg status` inside an `.hg` repo exits 0 (not a git command).
5. `05-env-var-prefix-stripped.sh` — `FOO=bar git status` inside an `.hg` repo blocks (env prefix correctly skipped).
6. `06-non-git-command-allows.sh` — `ls` inside an `.hg` repo exits 0.
7. `07-cd-then-git-allows.sh` — `cd /tmp && git init` inside an `.hg` repo exits 0 (documents the escape-valve limitation).

No frameworks or dependencies beyond `bash` and `mktemp`.

### 2. Skill content — lint-style validation

`scripts/check-skills.sh` walks `skills/*/SKILL.md` and:

- Validates frontmatter parses and contains `name` + `description`.
- Warns if a file exceeds 400 lines.
- Greps for `[link](path)` references and confirms the path exists.
- Optionally extracts fenced `hg` examples and runs them in `--dry-run` mode where supported, to catch typos.

Shell + a small Python helper for YAML parsing. Run manually before commits.

### 3. Skill behavior — manual scenario checklist

`tests/MANUAL.md` lists scenarios to walk through after meaningful changes:

- *Fresh empty hg repo:* ask Claude to commit a file → verify `mercurial-basics` activates, no `-u` flag used, message format follows the rules.
- *Isurus repo:* ask Claude to start a feature → verify `mercurial-bookmarks-and-prs` and `isurus-conventions` both load, bookmark created, no named-branch confusion.
- *Mid-stream conflict:* set up two heads in a scratch repo, ask Claude to merge → verify `mercurial-sync-and-merge` activates with correct `hg resolve` flow.
- *History rewrite request on public changeset:* ask Claude to amend a public commit → verify `mercurial-history-rewriting` refuses and explains why.
- *git in Isurus:* run `git status` in the Isurus repo → verify hook blocks with the right suggestion.
- *git in testdata:* run `git log` inside `internal/import/testdata/foo/repo.git/` → verify hook *allows* (closer `.git/`).

Each scenario is one paragraph. ~6 scenarios for v1.

## Acceptance criteria for v1

- All 6 `SKILL.md` files exist with valid frontmatter and pass `scripts/check-skills.sh`.
- All 7 hook test cases pass `hooks/test/run.sh`.
- All 6 manual scenarios in `tests/MANUAL.md` pass when walked through against the real Isurus repo.
- A second hg repo (any non-Isurus hg repo, even a throwaway `~/scratch-hg`) demonstrates that `isurus-conventions` does *not* activate but the generic skills do.

## Rollout

### Install for v1 (local development)

> **Correction (post-v0.1.0):** earlier revisions of this spec described an `~/.claude/settings.json` `"plugins"` block as the install mechanism. That key is not a documented Claude Code feature and is silently ignored. The verified mechanisms below replace it.

Two supported mechanisms.

**1. Session-only (recommended for development and the manual-scenarios walk-through):**

```sh
claude --plugin-dir /home/ctusa/repos/isurus-project/isurus-claude-plugin
```

Loads the plugin for that session. Repeat the flag to load multiple plugins. Takes precedence over installed marketplace plugins.

**2. Persistent (recommended once dogfooding is stable):** create a local marketplace.

Place a `marketplace.json` in any parent directory of the plugin checkout (suggested: `<parent>/.claude-plugin/marketplace.json`):

```json
{
  "name": "local-isurus",
  "owner": { "name": "Chris Tusa" },
  "plugins": [
    {
      "name": "isurus-hg",
      "source": "./isurus-claude-plugin",
      "description": "Mercurial fluency for Claude Code, with an Isurus-conventions overlay."
    }
  ]
}
```

Then from inside Claude Code:

```
/plugin marketplace add /home/ctusa/repos/isurus-project
/plugin install isurus-hg@local-isurus
```

Either mechanism activates the 6 skills and registers the hook on `Bash` tool calls.

For other Leafscale devs (once dogfooding is solid): use Option 2 with their own checkout path, or — when the git mirror is set up — point a marketplace at the published git URL instead of a local path.

### Initial dogfooding plan

1. **Day 1:** install locally, run hook test suite, walk the 6 manual scenarios. Fix anything obviously broken.
2. **Week 1:** use it in real Isurus work. Note any moment where Claude reaches for the wrong skill, misses a convention, or the hook fires when it shouldn't. Each note → either a skill tweak or a hook test case.
3. **Week 2:** re-walk the manual scenarios. If they all pass clean and Week 1 produced fewer than ~3 issues, tag `0.2.0`.
4. **Decision point:** continue dogfooding solo, or invite one other Leafscale dev to install via path entry and report.

### Git mirror setup (post-v1, before marketplace publication)

1. `pip install hg-git` (or use OS package).
2. Add to `~/.hgrc`: `hggit = ` under `[extensions]`.
3. Create a bare git repo to serve as the mirror.
4. Add to the plugin repo's `.hg/hgrc`: a `[paths]` entry like `git-mirror = git+ssh://...`.
5. `hg push git-mirror` after each release tag — manually at first, automated via a commit hook later if it sticks.
6. The git mirror's `marketplace.json` entry can then be added to a Claude Code marketplace.

### Versioning policy

- **0.x:** skill copy and hook behavior may change without notice between versions. Dogfooding phase.
- **1.0.0:** declared once skills have been stable for two consecutive weeks of real use with no edits.
- After 1.0: breaking changes (renamed skills, changed hook behavior) bump the major; new skills or expanded coverage bump the minor; copy fixes and bugfixes bump the patch.
- Tags as `hg tag v0.1.0` etc. Once the git mirror is up, tags propagate automatically.

## Open questions

None blocking. Everything not decided in this spec is explicitly deferred to phase 2 (see Non-goals) and will be revisited based on dogfooding outcomes.