github-issue-builder 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kulovema2012
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # github-issue-builder
2
+
3
+ An agent skill for **Claude Code** and **Codex**. It turns a rough idea (one line, a voice-note ramble, a screenshot, a half-written ticket) into a GitHub issue that an AI coding agent can pick up and finish without coming back with questions. That includes exactly which branch and folder (worktree) to work in, and where its pull request goes.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npx github-issue-builder install # both tools, user scope (~/.claude/skills and ~/.agents/skills)
9
+ npx github-issue-builder install --only claude
10
+ npx github-issue-builder install --scope project --project ./my-repo
11
+ npx github-issue-builder verify # check the installed copies match the package
12
+ npx github-issue-builder uninstall # removes the skill, keeping a backup
13
+ ```
14
+
15
+ Add `--dry-run` to see what would change.
16
+
17
+ **Updating.** Run `npx github-issue-builder@latest install` again. The installer:
18
+ - backs up any existing copy to `~/.github-issue-builder-backups/` and replaces it, or says "already up to date";
19
+ - removes old copies from `~/.codex/skills` (and from `$CODEX_HOME/skills`), so Codex doesn't load the skill twice. Pass `--keep-legacy` to leave them;
20
+ - warns if your **claude.ai account** also has this skill (synced into `~/.claude/skills/synced/`). Only claude.ai can update that copy, so replace or delete it in claude.ai → Settings → Capabilities → Skills.
21
+
22
+ `verify` fails while an outdated duplicate is still around. To file issues directly, the skill uses an authenticated [`gh`](https://cli.github.com/) CLI or a GitHub connector. Without one, it hands you the drafts to copy and paste.
23
+
24
+ ## Usage
25
+
26
+ Ask your agent something like "make an issue for …", "write a ticket: …", or paste any raw idea meant for GitHub. The skill:
27
+
28
+ 1. **Captures the idea**: who has what problem, what change fixes it, and its type (feature, bug, improvement, chore, docs).
29
+ 2. **Looks at the repo**: README, agent docs, build and test commands, the default branch, duplicate issues and existing labels.
30
+ 3. **Asks up to 4 clarifying questions, in one round**, and only when the answer changes the issue. One of them is the question behind most disappointing features: *is this a one-off, or will there be more of them?*
31
+ 4. **Checks the size**: one issue, or an epic plus child issues with a dependency graph and a parallel build order.
32
+ 5. **Writes the draft**: summary, spec, out of scope, testable acceptance criteria (Given/When/Then only where needed), branch and worktree setup, a step-by-step plan with a check after each step, a test plan, and risks and open questions.
33
+ 6. **Delivers it**: files the issue with `gh` after you confirm, creates the epic branch for epics, and replaces the placeholders with real issue numbers.
34
+
35
+ Branch conventions: single issues branch from the default branch. For an epic, the skill creates `epic/<n>-<slug>`, and each child branches from it in its own worktree (`../wt-<n>`). Child PRs merge into the epic branch, and the final epic PR closes every child issue together.
36
+
37
+ ## Tested
38
+
39
+ The "one-off or pattern?" question came from real client feedback: a single "lifetime package" feature that should have been a reusable package system. It was kept only after a blind A/B test. Six raw ideas, including a bug and a chore as controls, were run twice through each version and judged without the judges knowing which version wrote which draft. The new version won 9 of 12 pairs.
40
+
41
+ ## License
42
+
43
+ MIT
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+ // github-issue-builder: installs the github-issue-builder skill for Claude Code and Codex.
3
+ //
4
+ // npx github-issue-builder install [--scope user|project] [--project DIR] [--only claude|codex] [--dry-run] [--home DIR]
5
+ // [--keep-legacy] [--codex-home DIR]
6
+ // npx github-issue-builder verify [--scope user|project] [--project DIR] [--only claude|codex] [--home DIR]
7
+ // npx github-issue-builder uninstall [--scope user|project] [--project DIR] [--only claude|codex] [--dry-run] [--home DIR]
8
+ //
9
+ // Claude Code reads skills from .claude/skills and Codex from .agents/skills. The skill is identical for both,
10
+ // so the same folder is copied to each. An existing copy is moved to a timestamped backup before it is replaced.
11
+ // Old copies in <codex home>/skills are backed up and removed too (--keep-legacy skips that), and copies synced
12
+ // from a claude.ai account are reported, since only claude.ai can update them.
13
+ import fs from 'node:fs';
14
+ import os from 'node:os';
15
+ import path from 'node:path';
16
+ import crypto from 'node:crypto';
17
+ import { fileURLToPath } from 'node:url';
18
+
19
+ const NAME = 'github-issue-builder';
20
+ const BUNDLE = path.dirname(fileURLToPath(import.meta.url));
21
+ const SRC = path.join(BUNDLE, 'payload', NAME);
22
+ const argv = process.argv.slice(2);
23
+ const command = argv[0] === undefined || argv[0].startsWith('--') ? 'install' : argv[0];
24
+ const hasFlag = (name) => argv.includes(name);
25
+ const optionValue = (name) => {
26
+ const i = argv.indexOf(name);
27
+ return i === -1 ? undefined : argv[i + 1];
28
+ };
29
+
30
+ const DRY = hasFlag('--dry-run');
31
+ const ONLY = optionValue('--only');
32
+ const SCOPE = optionValue('--scope') ?? 'user';
33
+ const HOME = path.resolve(optionValue('--home') ?? os.homedir());
34
+ const ROOT = SCOPE === 'project' ? path.resolve(optionValue('--project') ?? process.cwd()) : HOME;
35
+ const BACKUP_DIR = path.join(HOME, `.${NAME}-backups`, new Date().toISOString().replace(/[:.]/g, '-'));
36
+
37
+ if (!['user', 'project'].includes(SCOPE)) fail(`--scope must be user or project, got ${SCOPE}`);
38
+ if (ONLY && !['claude', 'codex'].includes(ONLY)) fail(`--only must be claude or codex, got ${ONLY}`);
39
+
40
+ const KEEP_LEGACY = hasFlag('--keep-legacy');
41
+
42
+ const TARGETS = [
43
+ { tool: 'Claude Code', key: 'claude', dir: path.join(ROOT, '.claude', 'skills', NAME) },
44
+ { tool: 'Codex', key: 'codex', dir: path.join(ROOT, '.agents', 'skills', NAME) },
45
+ ].filter((t) => !ONLY || t.key === ONLY);
46
+
47
+ // Older Codex versions (and some setups) read skills from <codex home>/skills. A copy left there makes Codex load
48
+ // the skill twice, old and new, so install and uninstall move it to the backup folder. Orca and other launchers
49
+ // point CODEX_HOME somewhere else, so that home is checked too.
50
+ function legacyCodexCopies() {
51
+ if (ONLY === 'claude') return [];
52
+ // With --home (e.g. a test sandbox) the real CODEX_HOME is ignored so nothing outside that home is touched.
53
+ const codexHome = optionValue('--codex-home') ?? (optionValue('--home') ? undefined : process.env.CODEX_HOME);
54
+ const homes = SCOPE === 'project'
55
+ ? [path.join(ROOT, '.codex')]
56
+ : [path.join(HOME, '.codex'), codexHome].filter(Boolean);
57
+ const seen = new Set();
58
+ return homes
59
+ .map((h) => path.join(path.resolve(h), 'skills', NAME))
60
+ .filter((d) => !seen.has(d.toLowerCase()) && seen.add(d.toLowerCase()) && exists(d));
61
+ }
62
+
63
+ // Skills added to a claude.ai account are synced into ~/.claude/skills/synced/<account>/<name>. Claude Code loads
64
+ // them next to local skills, and the sync restores anything deleted locally, so they can only be reported.
65
+ function syncedClaudeCopies() {
66
+ if (ONLY === 'codex' || SCOPE === 'project') return [];
67
+ const root = path.join(HOME, '.claude', 'skills', 'synced');
68
+ if (!exists(root)) return [];
69
+ return fs.readdirSync(root, { withFileTypes: true })
70
+ .filter((e) => e.isDirectory())
71
+ .map((e) => path.join(root, e.name, NAME))
72
+ .filter((d) => exists(path.join(d, 'SKILL.md')));
73
+ }
74
+
75
+ function warnSynced(copies) {
76
+ for (const d of copies) {
77
+ console.log(`! claude.ai account copy found: ${d}`);
78
+ console.log(' Claude Code loads it alongside this install and the claude.ai sync restores it if deleted here.');
79
+ console.log(` To finish updating, open claude.ai > Settings > Capabilities > Skills and replace or delete "${NAME}".`);
80
+ }
81
+ }
82
+
83
+ function fail(msg) {
84
+ console.error(`${NAME}: ${msg}`);
85
+ process.exit(1);
86
+ }
87
+
88
+ function listFiles(dir, base = dir) {
89
+ const out = [];
90
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
91
+ if (entry.name === '__pycache__') continue;
92
+ const full = path.join(dir, entry.name);
93
+ if (entry.isDirectory()) out.push(...listFiles(full, base));
94
+ else out.push(path.relative(base, full));
95
+ }
96
+ return out.sort();
97
+ }
98
+
99
+ const digest = (file) => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
100
+
101
+ function exists(p) {
102
+ try {
103
+ fs.lstatSync(p);
104
+ return true;
105
+ } catch {
106
+ return false;
107
+ }
108
+ }
109
+
110
+ function backup(dest) {
111
+ // Keep the backup inside BACKUP_DIR even for folders outside ROOT (e.g. a CODEX_HOME under AppData).
112
+ let rel = path.relative(ROOT, dest);
113
+ if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.join('_outside', dest.replace(/^[A-Za-z]:/, (d) => d[0]));
114
+ const to = path.join(BACKUP_DIR, rel);
115
+ if (DRY) return console.log(` would back up ${dest} -> ${to}`);
116
+ fs.mkdirSync(path.dirname(to), { recursive: true });
117
+ const stat = fs.lstatSync(dest);
118
+ if (stat.isSymbolicLink()) {
119
+ // A link (e.g. a junction to a shared skills folder): remove only the link, never its target.
120
+ fs.writeFileSync(`${to}.link.txt`, fs.readlinkSync(dest));
121
+ fs.rmSync(dest, { recursive: false, force: true });
122
+ } else {
123
+ fs.cpSync(dest, to, { recursive: true });
124
+ fs.rmSync(dest, { recursive: true, force: true });
125
+ }
126
+ console.log(` backed up existing copy to ${to}`);
127
+ }
128
+
129
+ function sameAsPackage(dir, files) {
130
+ try {
131
+ return fs.statSync(dir).isDirectory() &&
132
+ files.every((f) => exists(path.join(dir, f)) && digest(path.join(dir, f)) === digest(path.join(SRC, f)));
133
+ } catch {
134
+ return false; // e.g. a dangling link
135
+ }
136
+ }
137
+
138
+ function install() {
139
+ const files = listFiles(SRC);
140
+ console.log(`${DRY ? '[dry run] ' : ''}Installing ${NAME} (${SCOPE} scope, ${files.length} files)`);
141
+ for (const t of TARGETS) {
142
+ console.log(`- ${t.tool}: ${t.dir}`);
143
+ if (exists(t.dir)) {
144
+ if (sameAsPackage(t.dir, files)) {
145
+ console.log(' already up to date');
146
+ continue;
147
+ }
148
+ backup(t.dir);
149
+ if (!DRY) fs.cpSync(SRC, t.dir, { recursive: true });
150
+ console.log(` ${DRY ? 'would update' : 'updated'} (the previous copy is in the backup above)`);
151
+ continue;
152
+ }
153
+ if (!DRY) fs.cpSync(SRC, t.dir, { recursive: true });
154
+ console.log(` ${DRY ? 'would install' : 'installed'}`);
155
+ }
156
+ for (const d of legacyCodexCopies()) {
157
+ if (KEEP_LEGACY) {
158
+ console.log(`! old Codex copy left in place (--keep-legacy): ${d}`);
159
+ continue;
160
+ }
161
+ console.log(`- Old Codex copy: ${d}`);
162
+ backup(d);
163
+ console.log(` ${DRY ? 'would remove' : 'removed'} so Codex loads only the new copy`);
164
+ }
165
+ warnSynced(syncedClaudeCopies());
166
+ if (!DRY) console.log(`Done. Restart Claude Code / Codex, then ask an agent to "make an issue for <idea>" or run \`${NAME} verify\`.`);
167
+ }
168
+
169
+ function verify() {
170
+ const files = listFiles(SRC);
171
+ let ok = true;
172
+ for (const t of TARGETS) {
173
+ const missing = files.filter((f) => !exists(path.join(t.dir, f)));
174
+ const changed = files.filter((f) => !missing.includes(f) && digest(path.join(t.dir, f)) !== digest(path.join(SRC, f)));
175
+ const good = missing.length === 0 && changed.length === 0;
176
+ ok &&= good;
177
+ console.log(`${good ? 'OK ' : 'FAIL'} ${t.tool}: ${t.dir}`);
178
+ for (const f of missing) console.log(` missing: ${f}`);
179
+ for (const f of changed) console.log(` differs from package: ${f}`);
180
+ }
181
+ for (const d of legacyCodexCopies()) {
182
+ ok = false;
183
+ console.log(`FAIL old Codex copy still present (Codex loads it too): ${d} (run \`${NAME} install\` to remove it)`);
184
+ }
185
+ const synced = syncedClaudeCopies();
186
+ for (const d of synced) {
187
+ const current = sameAsPackage(d, listFiles(SRC));
188
+ if (!current) ok = false;
189
+ console.log(`${current ? 'OK ' : 'FAIL'} claude.ai account copy ${current ? 'matches the package' : 'is an older version'}: ${d}`);
190
+ }
191
+ if (synced.some((d) => !sameAsPackage(d, listFiles(SRC)))) warnSynced(synced);
192
+ process.exit(ok ? 0 : 1);
193
+ }
194
+
195
+ function uninstall() {
196
+ for (const t of TARGETS) {
197
+ if (!exists(t.dir)) {
198
+ console.log(`- ${t.tool}: not installed`);
199
+ continue;
200
+ }
201
+ console.log(`- ${t.tool}: ${t.dir}`);
202
+ backup(t.dir);
203
+ }
204
+ if (!KEEP_LEGACY) {
205
+ for (const d of legacyCodexCopies()) {
206
+ console.log(`- Old Codex copy: ${d}`);
207
+ backup(d);
208
+ }
209
+ }
210
+ warnSynced(syncedClaudeCopies());
211
+ console.log(DRY ? '[dry run] nothing removed' : 'Removed. Backups are kept so nothing is lost.');
212
+ }
213
+
214
+ if (!fs.existsSync(path.join(SRC, 'SKILL.md'))) fail(`payload missing: ${SRC}`);
215
+ const commands = { install, verify, uninstall };
216
+ if (!Object.hasOwn(commands, command)) fail(`unknown command "${command}" (install | verify | uninstall)`);
217
+ commands[command]();
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "github-issue-builder",
3
+ "version": "1.0.0",
4
+ "description": "Agent skill for Claude Code and Codex: turn a rough feature or bug idea into an agent-ready GitHub issue (or epic + child issues) with spec, acceptance criteria, plan, test plan and branch/worktree setup",
5
+ "type": "module",
6
+ "bin": {
7
+ "github-issue-builder": "github-issue-builder.mjs"
8
+ },
9
+ "files": [
10
+ "github-issue-builder.mjs",
11
+ "payload/",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "keywords": [
19
+ "claude-code",
20
+ "codex",
21
+ "skill",
22
+ "agent-skills",
23
+ "github",
24
+ "github-issues",
25
+ "issue-template",
26
+ "git-worktree"
27
+ ],
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/kulovema2012/github-issue-builder.git"
32
+ },
33
+ "homepage": "https://github.com/kulovema2012/github-issue-builder#readme",
34
+ "bugs": {
35
+ "url": "https://github.com/kulovema2012/github-issue-builder/issues"
36
+ }
37
+ }
@@ -0,0 +1,293 @@
1
+ ---
2
+ name: "github-issue-builder"
3
+ description: "Turn a rough feature/bug idea into a GitHub issue (or epic + child issues) with spec, acceptance criteria, agent-ready plan, test plan, and branch/worktree setup, then optionally file it. Use for 'make an issue', 'write a ticket', or any raw idea meant for GitHub."
4
+ ---
5
+
6
+ # GitHub Issue Builder
7
+
8
+ Take a rough idea (one line, a voice-note ramble, a screenshot, a half-written ticket) and turn it into a GitHub issue that an AI coding agent (Codex, Claude Code) can pick up and finish without coming back with questions — including exactly which branch and folder (worktree) to work in, and where its pull request goes.
9
+
10
+ The issue is always written in **English**, in plain, easy-to-read language. Short sentences. No clever or cryptic phrasing. The reader may be an agent with no memory of the conversation, so everything it needs must be in the issue itself.
11
+
12
+ ## Workflow
13
+
14
+ 1. Capture the idea
15
+ 2. Look at the repo (when one is reachable)
16
+ 3. Ask 2-4 clarifying questions (only if needed)
17
+ 4. Check the size — one issue or an epic with child issues?
18
+ 5. Write the draft(s)
19
+ 6. Show the draft, then create it on GitHub (issues + epic branch) or hand it over for copy-paste
20
+
21
+ ### 1. Capture the idea
22
+
23
+ Restate the idea to yourself as: *who* has *what problem*, and *what change* fixes it. Decide the type: `feature`, `bug`, `improvement`, `chore`, or `docs`. A bug needs different information (steps to reproduce, expected vs actual) than a feature does.
24
+
25
+ ### 2. Look at the repo
26
+
27
+ A plan that names real files and real test commands is far more useful to an agent than a generic one, so check for repo context before writing:
28
+
29
+ - If the user named a repo or the working directory is a git repo: read the README, the top-level layout, contributing/agent docs (`CONTRIBUTING.md`, `AGENTS.md`, `CLAUDE.md`), the package/build file to learn the test and lint commands, and grep for the code areas the idea touches.
30
+ - If `gh` is available and authenticated: `gh repo view <repo> --json defaultBranchRef` gives the default branch name (don't assume `main`), `gh issue list --search "<keywords>"` spots duplicates, and `gh label list` shows which labels exist.
31
+ - Keep this quick — you are looking for the files, patterns and commands the plan should point at, not reviewing the codebase.
32
+
33
+ If no repo is reachable, continue without it, assume the default branch is `main`, and mark any file paths in the plan as guesses (see the writing rules).
34
+
35
+ If you find an existing issue that already covers the idea, tell the user and ask whether to update that one instead of creating a duplicate.
36
+
37
+ ### 3. Ask clarifying questions
38
+
39
+ Only ask when the answer would change the issue. Ask **at most 4** questions, in one round, using the multiple-choice question tool when available (offer concrete options, recommended one first). Good targets:
40
+
41
+ - **Who and why** — who uses this, and what problem it solves (if the idea only states a solution)
42
+ - **Scope edges** — which obvious neighbouring features are in or out
43
+ - **What "done" looks like** — the one behaviour that must work for the user to be happy
44
+ - **One-off or pattern** — when the request names a single instance of something that usually comes in plurals (one package, one plan, one report, one role), ask whether it stays the only one or more are coming. The answer decides between a one-off and a small reusable version, such as packages kept in a list an admin can add to. This is the most common reason a finished feature disappoints ("but I wanted more than one"), so ask it whenever it applies and the request doesn't already answer it.
45
+ - **Hard constraints** — must not break X, must use library Y, deadline, platform
46
+ - For bugs: steps to reproduce, expected vs actual result, environment
47
+
48
+ Do not ask about things you can decide sensibly yourself (naming, file layout, which test framework the repo already uses). Do not ask about things the repo already answers.
49
+
50
+ If the idea is already clear, skip this step. If the user is not around to answer, pick the most reasonable reading, and list each assumption under *Risks & open questions* so a human can correct it. Put an unanswered one-off-or-pattern question first there, phrased so it can be answered in one word, because it changes the scope more than anything else.
51
+
52
+ ### 4. Check the size
53
+
54
+ An issue is too big when any of these is true:
55
+
56
+ - It contains several deliverables that could ship and be reviewed separately
57
+ - The plan would need more than about 8 steps
58
+ - It touches several unrelated parts of the system (e.g. new DB schema + new admin UI + public API + billing)
59
+ - An agent would likely need more than one focused session / one reasonably sized PR
60
+
61
+ When it is too big, **propose a split** before writing: one epic issue plus child issues that each deliver one working, testable slice. For each child, work out what it depends on:
62
+
63
+ - **Depends on nothing** → can start as soon as the epic branch exists
64
+ - **Depends on another child** → must wait until that child is merged into the epic branch
65
+ - Children with no dependency on each other can be built **in parallel**, each in its own worktree
66
+
67
+ Show the proposed split as a short list with the dependencies and the build order (which children can run in parallel), and let the user confirm or adjust it. Then write the epic and every child with the templates below.
68
+
69
+ ### 5. Write the draft
70
+
71
+ Use the templates in the next sections. Then reread each draft as if you were the agent that has to implement it with nothing else to go on: Is anything ambiguous? Could two people read a criterion and disagree about whether it passes? Is it clear which branch to start from and where the PR goes? Fix those spots before showing it.
72
+
73
+ Issue numbers don't exist until the issues are created, so in the draft use placeholders such as `#<epic>` and `#<data-model>` and name branches with a slug only (e.g. `epic/<epic>-order-export`). Replace them with real numbers after creation (step 6).
74
+
75
+ ### 6. Deliver
76
+
77
+ Show the full draft(s) (title + body) to the user in Markdown code blocks so they are easy to copy. Then:
78
+
79
+ **If you can create issues** (an authenticated `gh` CLI, or a GitHub connector/MCP tool), ask "Create this on `<owner/repo>`?" and confirm the repo. On yes:
80
+
81
+ *Single issue*
82
+ 1. Write the body to a file and run `gh issue create --repo <owner/repo> --title "<title>" --body-file <file>` (a body file avoids shell-escaping problems with backticks and quotes).
83
+ 2. Replace the `<n>` placeholders in its Branch & worktree section with the real number (`gh issue edit <n> --body-file <file>`).
84
+ 3. Don't create the working branch — the agent creates it from the latest default branch when it starts.
85
+
86
+ *Epic + children*
87
+ 1. Create the epic issue first, then each child issue with `Part of #<epic>` at the top of its body.
88
+ 2. Create the **epic branch** on GitHub from the default branch — this is the only branch the skill creates:
89
+ ```
90
+ SHA=$(gh api repos/<owner>/<repo>/git/ref/heads/<default-branch> --jq .object.sha)
91
+ gh api repos/<owner>/<repo>/git/refs -f ref=refs/heads/epic/<epic>-<slug> -f sha="$SHA"
92
+ ```
93
+ 3. Edit every issue body so all placeholders become real issue numbers and real branch names (epic child list, `Depends on #…`, branch names, the final `Closes …` line).
94
+ 4. **Don't create the child branches or worktrees.** Each agent creates its own when it starts, from the latest epic branch. If they were created now, later children would miss the code merged by earlier ones.
95
+
96
+ For both: only add labels that already exist in the repo (`gh label list`); mention any suggested label that doesn't exist instead of creating it. Reply with the created issue URL(s), the epic branch name, and which child issue(s) can start right away.
97
+
98
+ **If you cannot create issues** (no `gh`, not authenticated, no connector): hand over the drafts as copy-paste text, plus the one command to create the epic branch if there is one, and say in one line what would let you file them directly next time (e.g. "run `gh auth login`" or connecting GitHub).
99
+
100
+ Never create an issue or branch without the user's go-ahead — once created it is visible to everyone on that repo.
101
+
102
+ ## Branch and worktree conventions
103
+
104
+ These keep every agent's work separated and make the flow predictable. Use them unless the repo already has its own convention (check `CONTRIBUTING.md` and recent branch names) — then follow the repo.
105
+
106
+ | Thing | Name | Created by | Starts from | Merges into |
107
+ |---|---|---|---|---|
108
+ | Single issue branch | `<n>-<short-slug>` | the agent, when it starts | default branch | default branch |
109
+ | Epic branch | `epic/<epic>-<short-slug>` | this skill, when filing | default branch | default branch (once, at the end) |
110
+ | Child issue branch | `<n>-<short-slug>` | the agent, when it starts | latest epic branch | epic branch |
111
+ | Worktree folder | `../wt-<n>` (next to the repo folder) | the agent, when it starts | — | removed after merge |
112
+
113
+ Why an epic branch: all children share one goal, so their work collects in one place and the default branch never gets a half-built feature. Children with no dependency on each other can run in parallel in separate worktrees.
114
+
115
+ Important GitHub behaviour to reflect in the issues: `Closes #n` in a PR only auto-closes the issue when the PR merges into the **default** branch. Child PRs merge into the epic branch, so their issues stay open. The final epic → default-branch PR must list `Closes #<child1>, #<child2>, …, #<epic>` so they all close together.
116
+
117
+ ## Issue template (single issue or child issue)
118
+
119
+ **Title:** a short imperative sentence under ~70 characters that says what changes, e.g. `Add CSV export to the orders table`. If the repo's existing issues use a prefix style (like `feat:` or `[Bug]`), follow it.
120
+
121
+ **Body:**
122
+
123
+ ```markdown
124
+ <Child issues only: first line> Part of #<epic>
125
+
126
+ ## Summary
127
+ <2-4 sentences: who has what problem, and what this issue changes to fix it. A reader should understand the point from this section alone.>
128
+
129
+ ## Background
130
+ <Optional. Links, related issues, current behaviour, screenshots, why now. Delete the section if there is nothing useful to say.>
131
+
132
+ ## Spec
133
+ <What the finished change does, described as behaviour, not code. Cover what applies:>
134
+ - **User-facing behaviour:** what the user sees and does, step by step
135
+ - **Inputs / outputs:** fields, formats, limits, defaults
136
+ - **Data / API changes:** new endpoints, schema or config changes, migrations
137
+ - **Edge cases:** empty state, errors, permissions, large inputs, concurrency
138
+ - **Constraints:** things that must not change or break
139
+
140
+ ## Out of scope
141
+ - <Things someone might reasonably expect this issue to do, that it deliberately does not do. Each one short. Point to a follow-up issue if one exists.>
142
+
143
+ ## Acceptance criteria
144
+ - [ ] <One observable, testable behaviour per line.>
145
+ - [ ] <...>
146
+
147
+ **Key scenarios**
148
+ <Only for the behaviours that are tricky or easy to get wrong — usually 1-3. Skip this sub-section if none.>
149
+
150
+ **Scenario: <name>**
151
+ - **Given** <starting state>
152
+ - **When** <action>
153
+ - **Then** <observable result>
154
+
155
+ ## Branch & worktree
156
+ - **Depends on:** <#n — do not start until it is merged into the epic branch> / <nothing — can start now>
157
+ - **Start from:** `<epic/<epic>-<slug>` for a child | `<default-branch>` for a single issue>
158
+ - **Your branch:** `<n>-<slug>`
159
+ - **Set up (run from the repo folder):**
160
+ ```
161
+ git fetch origin
162
+ git worktree add ../wt-<n> -b <n>-<slug> origin/<start-from branch>
163
+ cd ../wt-<n>
164
+ ```
165
+ - **Open your pull request into:** `<epic branch for a child | default branch for a single issue>` — <child: "not into <default-branch>">
166
+ - **PR description:** <child: "Part of #<epic>. Implements #<n>." | single issue: "Closes #<n>">
167
+ - **After merge:** `git worktree remove ../wt-<n>` and delete the branch `<n>-<slug>`
168
+
169
+ ## Implementation plan
170
+ <Ordered, small steps an AI coding agent can follow one at a time. Each step leaves the project building and its tests passing.>
171
+
172
+ 1. **<Step goal>**
173
+ - Files: `<path>` <mark `(verify)` if not confirmed in the repo>
174
+ - Do: <what to change, in a sentence or two>
175
+ - Check: `<command to run>` / <what to confirm>
176
+ 2. ...
177
+
178
+ **Notes for the implementer**
179
+ - Commands: install `<...>`, test `<...>`, lint `<...>`
180
+ - Follow the existing pattern in `<file>` for <...>
181
+ - Do not <constraint, e.g. change the public API of X>
182
+
183
+ ## Test plan
184
+ - **Unit:** <what to cover>
185
+ - **Integration / end-to-end:** <what to cover, if relevant>
186
+ - **Manual check:** <short steps a human can do to see it working>
187
+
188
+ ## Risks & open questions
189
+ - <Unknowns, risky parts, decisions still pending, and any assumptions made while writing this issue.>
190
+ ```
191
+
192
+ For a **bug**, add a `## Bug details` section right after Summary with *Steps to reproduce*, *Expected result*, *Actual result*, and *Environment* (version, OS/browser, logs). The first acceptance criterion should be "the steps to reproduce no longer produce the bug", and the plan should start by writing a failing test that reproduces it.
193
+
194
+ ## Epic template
195
+
196
+ ```markdown
197
+ ## Summary
198
+ <The overall goal, who it's for, and why it's split into several issues.>
199
+
200
+ ## Spec
201
+ <High-level behaviour of the finished feature. Details live in the child issues.>
202
+
203
+ ## Out of scope
204
+ - <...>
205
+
206
+ ## Acceptance criteria (whole feature)
207
+ - [ ] <End-to-end behaviours that are only true once all children are merged.>
208
+
209
+ ## Epic branch
210
+ - Branch: `epic/<epic>-<slug>` — created from `<default-branch>`
211
+ - Every child issue branches from this epic branch and opens its PR into it.
212
+ - `<default-branch>` is not touched until the final merge below.
213
+
214
+ ## Child issues (build order)
215
+ | Issue | What it delivers | Depends on | Can run in parallel with |
216
+ |---|---|---|---|
217
+ | #<a> | <...> | — | — |
218
+ | #<b> | <...> | #<a> | #<c> |
219
+ | #<c> | <...> | #<a> | #<b> |
220
+
221
+ - [ ] #<a> <title>
222
+ - [ ] #<b> <title>
223
+ - [ ] #<c> <title>
224
+
225
+ ## Keeping the epic branch up to date
226
+ If `<default-branch>` gets other changes while this epic is in progress, merge it into the epic branch every few days so the final merge stays small:
227
+ ```
228
+ git fetch origin
229
+ git switch epic/<epic>-<slug>
230
+ git merge origin/<default-branch>
231
+ git push
232
+ ```
233
+
234
+ ## Finishing the epic
235
+ - [ ] All child PRs merged into `epic/<epic>-<slug>`
236
+ - [ ] Whole-feature acceptance criteria above pass on the epic branch
237
+ - [ ] Open the final PR: `epic/<epic>-<slug>` → `<default-branch>`, with this description line: `Closes #<a>, #<b>, #<c>, #<epic>`
238
+ - [ ] After it merges, delete the branch `epic/<epic>-<slug>`
239
+
240
+ ## Risks & open questions
241
+ - <...>
242
+ ```
243
+
244
+ ## Writing rules
245
+
246
+ **Acceptance criteria**
247
+ - Describe *what* is observable, not *how* it is built. "Clicking Export downloads a CSV with one row per visible order" — not "add an `exportCsv()` function".
248
+ - One behaviour per line, specific enough that two people would agree whether it passes. Replace words like "fast", "user-friendly", "properly" with something measurable or remove them.
249
+ - Include the unhappy paths that matter: errors, empty data, missing permissions, invalid input.
250
+ - Use Given/When/Then only where a checklist line would be ambiguous (multi-step flows, state-dependent behaviour, tricky edge cases). Most criteria should stay as plain checklist items.
251
+ - Aim for roughly 4-10 criteria. More than ~12 is usually a sign the issue should be split.
252
+
253
+ **Implementation plan (for AI agents)**
254
+ - Order steps so each one can be finished, built, and tested before the next — e.g. data model → logic → API → UI → docs. An agent works best with small verified steps rather than one big change.
255
+ - Name real files and real commands when you have seen them in the repo. When you haven't, still suggest the likely location but add `(verify)` so the agent checks before editing — an agent will trust a confident wrong path.
256
+ - Each step gets a concrete *Check*: a test command, a build, or a specific thing to observe.
257
+ - Tell the agent which existing code to copy patterns from; it keeps the change consistent with the codebase.
258
+ - Keep steps to what the issue needs. Don't add refactors, extra features, or "nice to have" steps — put those under Out of scope or Risks.
259
+
260
+ **Branch & worktree section**
261
+ - Always present, in every single and child issue. Say plainly where the PR goes; for child issues add "not into `<default-branch>`", because opening the PR against the default branch is the most common mistake.
262
+ - If the issue depends on another, say so in the first line so an agent doesn't start too early.
263
+
264
+ **General**
265
+ - Don't invent facts about the repo, the users, or the business. If something is unknown, say so under *Risks & open questions*.
266
+ - Keep the issue self-contained. Summarise anything important from the conversation instead of writing "as discussed".
267
+ - Delete template sections that truly don't apply rather than filling them with "N/A" — except Out of scope, Acceptance criteria, Branch & worktree, Test plan, and Risks & open questions, which are always present (write "None known" in Risks if that is true).
268
+
269
+ ## Example
270
+
271
+ **Input idea:** "users should be able to export their orders to excel"
272
+
273
+ After a quick repo look (React frontend in `web/`, Express API in `api/`, orders page at `web/src/pages/Orders.tsx`), good questions would be: *Which orders — all, or only the current filtered view? CSV or real .xlsx? Who can export — every user or admins only?* Suppose the answers are: current filtered view, CSV is fine, any logged-in user.
274
+
275
+ This fits in one issue, so no epic. **Title:** `Add CSV export of the filtered orders list`
276
+
277
+ **Sample acceptance criteria:**
278
+ - [ ] The Orders page shows an **Export CSV** button to any logged-in user
279
+ - [ ] Clicking it downloads `orders-YYYY-MM-DD.csv` containing exactly the orders matching the current filters
280
+ - [ ] The CSV has a header row and the columns: Order ID, Date, Customer, Status, Total
281
+ - [ ] With no matching orders, the button is disabled and shows a tooltip "No orders to export"
282
+ - [ ] Exporting 10,000 orders completes without the page freezing
283
+
284
+ **Sample out of scope:** Excel (.xlsx) format; scheduled or emailed exports; exporting order line items.
285
+
286
+ **Sample Branch & worktree (after the issue was created as #57):**
287
+ - Depends on: nothing — can start now
288
+ - Start from: `main`
289
+ - Your branch: `57-orders-csv-export`
290
+ - Set up: `git fetch origin && git worktree add ../wt-57 -b 57-orders-csv-export origin/main && cd ../wt-57`
291
+ - Open your pull request into: `main`
292
+ - PR description: `Closes #57`
293
+ - After merge: `git worktree remove ../wt-57` and delete the branch