@jeiemgi/cckit 0.1.6

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.
Files changed (191) hide show
  1. package/.claude-plugin/plugin.json +22 -0
  2. package/AGENTS.md +101 -0
  3. package/LICENSE-APACHE +202 -0
  4. package/LICENSE-MIT +21 -0
  5. package/README.md +143 -0
  6. package/SECURITY.md +22 -0
  7. package/bin/cckit +215 -0
  8. package/cckit.config.json +34 -0
  9. package/commands/kit-add.md +42 -0
  10. package/commands/kit-docs.md +45 -0
  11. package/commands/kit-doctor.md +52 -0
  12. package/commands/kit-export-project.md +58 -0
  13. package/commands/kit-export-training.md +49 -0
  14. package/commands/kit-init.md +126 -0
  15. package/commands/kit-routines.md +59 -0
  16. package/commands/kit-update.md +132 -0
  17. package/docs/kit-annotate/01-explainer.html +225 -0
  18. package/docs/kit-annotate/02-implementation-plan.html +196 -0
  19. package/docs/media/.onboarding-capture.cast +5 -0
  20. package/docs/media/README.md +43 -0
  21. package/docs/media/build-demo.sh +63 -0
  22. package/docs/media/build-kit-init.sh +51 -0
  23. package/docs/media/build-onboarding.sh +51 -0
  24. package/docs/media/kit-dry-run.cast +107 -0
  25. package/docs/media/kit-dry-run.gif +0 -0
  26. package/docs/media/kit-init.cast +56 -0
  27. package/docs/media/kit-init.gif +0 -0
  28. package/docs/media/kit-onboarding.cast +148 -0
  29. package/docs/media/kit-onboarding.gif +0 -0
  30. package/githooks/pre-commit +18 -0
  31. package/kit.config.schema.json +105 -0
  32. package/package.json +54 -0
  33. package/privacy-denylist.example +8 -0
  34. package/profiles/automation.json +36 -0
  35. package/profiles/content.json +41 -0
  36. package/profiles/minimal.json +31 -0
  37. package/profiles/research.json +37 -0
  38. package/profiles/software.json +32 -0
  39. package/scripts/annotate-setup.sh +149 -0
  40. package/scripts/autopilot.sh +50 -0
  41. package/scripts/capture-project-ids.sh +53 -0
  42. package/scripts/check.sh +66 -0
  43. package/scripts/contribute.sh +48 -0
  44. package/scripts/debug.sh +54 -0
  45. package/scripts/init-upgrade-test.sh +99 -0
  46. package/scripts/init.sh +827 -0
  47. package/scripts/install.sh +24 -0
  48. package/scripts/kit-add-test.sh +62 -0
  49. package/scripts/kit-add.sh +115 -0
  50. package/scripts/kit-adopt-test.sh +61 -0
  51. package/scripts/kit-adopt.sh +122 -0
  52. package/scripts/kit-bump-version.sh +79 -0
  53. package/scripts/kit-digest.sh +126 -0
  54. package/scripts/kit-doctor.sh +663 -0
  55. package/scripts/kit-export-project-test.sh +82 -0
  56. package/scripts/kit-export-project.sh +245 -0
  57. package/scripts/kit-export-training-test.sh +51 -0
  58. package/scripts/kit-export-training.sh +175 -0
  59. package/scripts/kit-migrate-test.sh +80 -0
  60. package/scripts/kit-migrate.sh +190 -0
  61. package/scripts/kit-onboard-test.sh +63 -0
  62. package/scripts/kit-onboard.sh +69 -0
  63. package/scripts/kit-promote-test.sh +54 -0
  64. package/scripts/kit-promote.sh +102 -0
  65. package/scripts/kit-remove-test.sh +61 -0
  66. package/scripts/kit-remove.sh +84 -0
  67. package/scripts/kit-routines.sh +322 -0
  68. package/scripts/kit-version-check.sh +91 -0
  69. package/scripts/kit-wire-test.sh +54 -0
  70. package/scripts/kit-wire.sh +132 -0
  71. package/scripts/knowledge-lint.sh +96 -0
  72. package/scripts/lib/cckit-output.sh +36 -0
  73. package/scripts/lib/effort-metrics.sh +452 -0
  74. package/scripts/lib/effort-ops-test.sh +83 -0
  75. package/scripts/lib/effort-ops.sh +132 -0
  76. package/scripts/lib/effort-plan.sh +104 -0
  77. package/scripts/lib/effort.sh +191 -0
  78. package/scripts/lib/engine-adapter.sh +92 -0
  79. package/scripts/lib/gh-log.sh +58 -0
  80. package/scripts/lib/gh-project.sh +212 -0
  81. package/scripts/lib/handoff.sh +35 -0
  82. package/scripts/lib/kit-cli-test.sh +42 -0
  83. package/scripts/lib/kit-cli.sh +32 -0
  84. package/scripts/lib/kit-config-resolve.sh +145 -0
  85. package/scripts/lib/kit-config.sh +88 -0
  86. package/scripts/lib/kit-engine-test.sh +107 -0
  87. package/scripts/lib/kit-events.sh +62 -0
  88. package/scripts/lib/kit-gc.sh +117 -0
  89. package/scripts/lib/kit-interview-test.sh +77 -0
  90. package/scripts/lib/kit-interview.sh +203 -0
  91. package/scripts/lib/kit-local.sh +79 -0
  92. package/scripts/lib/kit-manifest.sh +127 -0
  93. package/scripts/lib/kit-mode-test.sh +49 -0
  94. package/scripts/lib/kit-mode.sh +67 -0
  95. package/scripts/lib/kit-operate.sh +105 -0
  96. package/scripts/lib/kit-profile-test.sh +62 -0
  97. package/scripts/lib/kit-profile.sh +115 -0
  98. package/scripts/lib/kit-task-ops-test.sh +63 -0
  99. package/scripts/lib/kit-task-ops.sh +341 -0
  100. package/scripts/lib/pr-evidence.sh +173 -0
  101. package/scripts/lib/project-scan.sh +16 -0
  102. package/scripts/lib/react-detect.sh +78 -0
  103. package/scripts/lib/role-identity.sh +47 -0
  104. package/scripts/lib/secret-guard.sh +96 -0
  105. package/scripts/lib/toon.sh +35 -0
  106. package/scripts/lib/ui.sh +42 -0
  107. package/scripts/lib/version-bump.sh +59 -0
  108. package/scripts/lib/worktree-issue-test.sh +45 -0
  109. package/scripts/lib/worktree-issue.sh +73 -0
  110. package/scripts/lib/worktree-start.sh +280 -0
  111. package/scripts/orchestrate.sh +160 -0
  112. package/scripts/portable-test.sh +53 -0
  113. package/scripts/publish.sh +94 -0
  114. package/scripts/setup-labels.sh +25 -0
  115. package/scripts/setup-milestones.sh +17 -0
  116. package/scripts/showcase.sh +64 -0
  117. package/scripts/status.sh +44 -0
  118. package/scripts/task-sync.sh +59 -0
  119. package/scripts/test.sh +48 -0
  120. package/scripts/web-install.sh +22 -0
  121. package/skills/kit-annotate/SKILL.md +107 -0
  122. package/skills/kit-autopilot/SKILL.md +108 -0
  123. package/skills/kit-contribute/SKILL.md +134 -0
  124. package/skills/kit-customize/SKILL.md +134 -0
  125. package/skills/kit-dev/SKILL.md +67 -0
  126. package/skills/kit-digest/SKILL.md +41 -0
  127. package/skills/kit-effort-close/SKILL.md +156 -0
  128. package/skills/kit-effort-new/SKILL.md +173 -0
  129. package/skills/kit-effort-pr/SKILL.md +139 -0
  130. package/skills/kit-effort-start/SKILL.md +85 -0
  131. package/skills/kit-gc/SKILL.md +80 -0
  132. package/skills/kit-onboard/SKILL.md +50 -0
  133. package/skills/kit-security-sweep/SKILL.md +57 -0
  134. package/skills/kit-ship/SKILL.md +43 -0
  135. package/skills/kit-task-close/SKILL.md +66 -0
  136. package/skills/kit-task-new/SKILL.md +51 -0
  137. package/skills/kit-task-pr/SKILL.md +43 -0
  138. package/skills/kit-task-pr-auto/SKILL.md +27 -0
  139. package/skills/kit-task-pr-merge/SKILL.md +53 -0
  140. package/skills/kit-task-start/SKILL.md +76 -0
  141. package/skills/kit-task-sync/SKILL.md +37 -0
  142. package/templates/CLAUDE.md.tmpl +106 -0
  143. package/templates/agents/analyst.md +55 -0
  144. package/templates/agents/auto-dev.md +93 -0
  145. package/templates/agents/backend.md +59 -0
  146. package/templates/agents/designer.md +73 -0
  147. package/templates/agents/devops.md +57 -0
  148. package/templates/agents/editor.md +48 -0
  149. package/templates/agents/frontend.md +81 -0
  150. package/templates/agents/generalist.md +46 -0
  151. package/templates/agents/local-delegate.md +70 -0
  152. package/templates/agents/n8n.md +65 -0
  153. package/templates/agents/pm.md +69 -0
  154. package/templates/agents/qa.md +66 -0
  155. package/templates/agents/researcher.md +57 -0
  156. package/templates/agents/security.md +65 -0
  157. package/templates/agents/tech-lead.md +75 -0
  158. package/templates/hooks/guard-base-branch-commit.sh.tmpl +45 -0
  159. package/templates/hooks/kit-local-status.sh.tmpl +34 -0
  160. package/templates/hooks/kit_version_check.sh.tmpl +6 -0
  161. package/templates/hooks/mempal_followup.sh.tmpl +97 -0
  162. package/templates/hooks/mempal_precompact.sh.tmpl +4 -0
  163. package/templates/hooks/mempal_save.sh.tmpl +4 -0
  164. package/templates/hooks/mempal_session_start.sh.tmpl +8 -0
  165. package/templates/hooks/prepush_gate.sh.tmpl +36 -0
  166. package/templates/hooks/repo-hygiene.sh.tmpl +72 -0
  167. package/templates/kit.config.json.tmpl +32 -0
  168. package/templates/knowledge-INDEX.md.tmpl +12 -0
  169. package/templates/lib/kit-sigil.sh.tmpl +124 -0
  170. package/templates/rules/branch-naming.md +104 -0
  171. package/templates/rules/communication-style.md +22 -0
  172. package/templates/rules/delegation-brief.md +40 -0
  173. package/templates/rules/design-routing.md +35 -0
  174. package/templates/rules/effort-model.md +122 -0
  175. package/templates/rules/knowledge-base.md +41 -0
  176. package/templates/rules/mempalace.md +110 -0
  177. package/templates/rules/plan-output-format.md +58 -0
  178. package/templates/rules/react-annotate.md +69 -0
  179. package/templates/rules/risk-tiered-review.md +62 -0
  180. package/templates/rules/skill-gaps.md +48 -0
  181. package/templates/rules/task-management.md +42 -0
  182. package/templates/settings/settings.local.json.tmpl +27 -0
  183. package/templates/skills/NAMESPACED +13 -0
  184. package/templates/skills/copywriting/SKILL.md +252 -0
  185. package/templates/skills/copywriting/references/copy-frameworks.md +344 -0
  186. package/templates/skills/copywriting/references/natural-transitions.md +272 -0
  187. package/templates/skills/feature-build-refine/SKILL.md +367 -0
  188. package/templates/skills/karpathy-guidelines/SKILL.md +69 -0
  189. package/templates/skills/morning-briefing/SKILL.md +46 -0
  190. package/templates/skills/speckit/SKILL.md +239 -0
  191. package/templates/skills/supabase-patterns/SKILL.md +88 -0
@@ -0,0 +1,156 @@
1
+ ---
2
+ name: kit-effort-close
3
+ description: Close an effort in ONE op — snapshot each sub-issue's diff BEFORE squash (work record), squash-merge the effort PR, close the parent + ALL native sub-issues, set the board Status=Done for the parent and every sub, then garbage-collect (switch to the base branch, pull, remove the effort worktree, delete local+remote branch, prune). Finally, flag any kit-managed files the effort touched for upstream contribution.
4
+ when_to_use: When the effort PR has been reviewed and is ready to land. This is the single close op for the effort lifecycle (effort-model.md) — board + record state are correct by construction. Replaces the separate merge / close / mark-done / gc steps.
5
+ ---
6
+
7
+ # kit-effort-close
8
+
9
+ Plugin-direct skill — runs straight from `${CLAUDE_PLUGIN_ROOT}` (no per-project `scripts/` checkout
10
+ needed). The verb logic (`effort_branch_num`, `effort_snapshot_subs`) lives in
11
+ `scripts/lib/effort.sh`, never re-authored inline (kit-engine-boundary #1/#2). Respects
12
+ `KIT_BASE_BRANCH` (default `main`) and the `KIT_PROJECTS_V2` board toggle.
13
+
14
+ The effort lifecycle's terminal op. Board state, issue state, and the work record are correct
15
+ **by construction** — this op owns them. Order matters: **snapshot before squash** (squash destroys
16
+ the per-sub-issue commit pairs that ARE the per-unit work record — effort-model.md "Trace hard rules").
17
+
18
+ ## Preconditions
19
+
20
+ 1. The effort PR exists, is reviewed, not in draft, and is mergeable (or rebaseable).
21
+ 2. You are on the `effort/<N>-<slug>` branch (its worktree), working tree clean.
22
+
23
+ If not, abort and route to `/kit-effort-pr` first.
24
+
25
+ ## Inputs
26
+
27
+ | Field | Required | Notes |
28
+ | ------------ | -------- | ------------------------------------------------------- |
29
+ | Issue number | optional | Parent #N — derived from `effort/<N>-<slug>` if omitted |
30
+ | PR number | optional | Defaults to the open PR for the effort branch |
31
+
32
+ ## Execution
33
+
34
+ ```bash
35
+ source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/kit-config.sh" && load_kit_config
36
+ [[ "$KIT_PROJECTS_V2" == "true" ]] && { source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/gh-project.sh"; load_project_ids; }
37
+ source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/effort.sh"
38
+ BASE="${KIT_BASE_BRANCH:-main}"
39
+
40
+ BRANCH=$(git rev-parse --abbrev-ref HEAD)
41
+
42
+ # 0. Resolve parent #N + the effort PR.
43
+ NUM="${INPUT_NUM:-}"
44
+ [[ -z "$NUM" ]] && NUM=$(effort_branch_num "$BRANCH")
45
+ [[ -z "$NUM" ]] && { echo "✗ Not on an effort branch (effort/<N>-<slug>) and no #N passed"; exit 1; }
46
+
47
+ if [[ -n "$(git status --porcelain)" ]]; then
48
+ echo "✗ Working tree dirty — commit via /kit-effort-pr first"; exit 1
49
+ fi
50
+
51
+ PR_NUM="${INPUT_PR:-}"
52
+ [[ -z "$PR_NUM" ]] && PR_NUM=$(gh pr list --repo "$KIT_REPO" --head "$BRANCH" --state open --json number --jq '.[0].number // empty')
53
+ [[ -z "$PR_NUM" ]] && { echo "✗ No open PR for $BRANCH — run /kit-effort-pr $NUM"; exit 1; }
54
+ echo "→ effort #$NUM · PR #$PR_NUM · branch $BRANCH"
55
+
56
+ # ── (a) SNAPSHOT SUB-DIFFS *BEFORE* SQUASH ───────────────────────────────────────────────
57
+ # Squash collapses the per-sub-issue commits; capture each commit's diff + its sub-issue pairing
58
+ # NOW, to a durable trace dir under the shared git-common-dir (survives the worktree prune below).
59
+ TRACE_DIR=$(effort_snapshot_subs "$NUM" "origin/$BASE")
60
+ echo " trace: ${TRACE_DIR:-<none>}"
61
+
62
+ # ── (b) SQUASH-MERGE THE EFFORT PR ───────────────────────────────────────────────────────
63
+ MERGE_INFO=$(gh pr view "$PR_NUM" --repo "$KIT_REPO" --json mergeable,isDraft --jq '{m:.mergeable,d:.isDraft}')
64
+ [[ "$(echo "$MERGE_INFO" | jq -r .d)" == "true" ]] && { echo "✗ PR #$PR_NUM is a draft — undraft first"; exit 1; }
65
+ if [[ "$(echo "$MERGE_INFO" | jq -r .m)" == "CONFLICTING" ]]; then
66
+ echo "→ Conflicts — rebasing onto $BASE..."
67
+ git fetch origin "$BASE" && git rebase "origin/$BASE" \
68
+ && git push --force-with-lease origin "$BRANCH" \
69
+ || { echo "✗ Rebase conflicts need manual resolution"; git rebase --abort 2>/dev/null; exit 1; }
70
+ sleep 3
71
+ fi
72
+ gh pr merge "$PR_NUM" --repo "$KIT_REPO" --squash || { echo "✗ Merge failed — see https://github.com/$KIT_REPO/pull/$PR_NUM"; exit 1; }
73
+ echo "✓ PR #$PR_NUM squash-merged"
74
+
75
+ # ── (c) CLOSE PARENT + ALL NATIVE SUB-ISSUES ─────────────────────────────────────────────
76
+ # Native sub-issues (GitHub parent/child). Collect them via GraphQL subIssues.
77
+ SUBS=$(gh api graphql -f query='
78
+ query($o:String!,$r:String!,$n:Int!){repository(owner:$o,name:$r){issue(number:$n){
79
+ subIssues(first:50){nodes{number state}}}}}' \
80
+ -F o="${KIT_REPO%/*}" -F r="${KIT_REPO#*/}" -F n="$NUM" \
81
+ --jq '.data.repository.issue.subIssues.nodes[]?.number' 2>/dev/null)
82
+
83
+ # The parent auto-closes on merge to the default branch via `Closes #N`; ensure it, plus close every sub.
84
+ for N in $NUM $SUBS; do
85
+ STATE=$(gh issue view "$N" --repo "$KIT_REPO" --json state --jq .state 2>/dev/null)
86
+ if [[ "$STATE" == "OPEN" ]]; then
87
+ gh issue close "$N" --repo "$KIT_REPO" \
88
+ --comment "Closed on merge of effort PR #$PR_NUM (parent #$NUM) — /kit-effort-close." \
89
+ && echo "✓ closed #$N"
90
+ else
91
+ echo " #$N already ${STATE:-unknown} — skipped"
92
+ fi
93
+ done
94
+
95
+ # ── (d) BOARD: STATUS=DONE FOR PARENT + ALL SUBS ─────────────────────────────────────────
96
+ # project_find_item_by_issue paginates the WHOLE board and fails loudly instead of returning empty.
97
+ if [[ "$KIT_PROJECTS_V2" == "true" ]]; then
98
+ for N in $NUM $SUBS; do
99
+ ITEM=$(project_find_item_by_issue "$N") || { echo " ⚠ board lookup failed for #$N (see stderr)"; continue; }
100
+ if [[ -n "$ITEM" ]]; then
101
+ project_set_single_select "$ITEM" "$STATUS_FIELD_ID" "$STATUS_OPT_DONE" && echo "✓ board Done: #$N"
102
+ else
103
+ echo " ⚠ #$N not on board — skipped"
104
+ fi
105
+ done
106
+ fi
107
+
108
+ # ── (e) GC: trunk + prune the effort worktree/branch ─────────────────────────────────────
109
+ WT=$(git worktree list --porcelain | awk -v b="refs/heads/$BRANCH" '/^worktree /{p=$2} /^branch /{if($2==b) print p}')
110
+ MAIN_WT=$(git worktree list --porcelain | awk '/^worktree /{print $2; exit}')
111
+ git -C "$MAIN_WT" checkout "$BASE" 2>/dev/null || git checkout "$BASE"
112
+ git -C "$MAIN_WT" pull origin "$BASE" 2>/dev/null || git pull origin "$BASE"
113
+ if [[ -n "$WT" && "$WT" != "$MAIN_WT" ]]; then
114
+ git worktree remove "$WT" --force && echo "✓ removed worktree $WT"
115
+ fi
116
+ git branch -D "$BRANCH" 2>/dev/null && echo "✓ deleted local branch $BRANCH"
117
+ git push origin --delete "$BRANCH" 2>/dev/null && echo "✓ deleted remote branch $BRANCH" || echo " (remote branch already gone)"
118
+ git worktree prune
119
+
120
+ # ── (f) KIT-SYNC DRIFT CHECK — kit ⇄ project stay in sync ─────────────────────────────────
121
+ # If this effort changed kit-managed files, they likely belong upstream (/kit-contribute) — a
122
+ # future /kit-update could otherwise clobber un-upstreamed fixes. Advisory; never blocks the close.
123
+ KIT_TOUCHED=$(git -C "$MAIN_WT" show --name-only --pretty=format: HEAD 2>/dev/null \
124
+ | grep -E '^(scripts/(lib/|kit$|kit-)|\.claude/(skills|rules|hooks|lib|agents)/)' | sort -u || true)
125
+ if [[ -n "$KIT_TOUCHED" ]]; then
126
+ echo ""
127
+ echo "⚠ kit-sync: this effort changed kit-managed files — review for upstream (/kit-contribute):"
128
+ printf ' %s\n' $KIT_TOUCHED
129
+ echo " kit ⇄ project must stay in sync; an un-upstreamed change can be clobbered by /kit-update."
130
+ fi
131
+
132
+ echo "✓ effort #$NUM closed — merged, parent+subs Done, trace at ${TRACE_DIR:-<none>}, worktree pruned"
133
+ ```
134
+
135
+ ## Output
136
+
137
+ - Pre-squash trace dir (`<git-common-dir>/traces/effort-<N>/` + `index.jsonl`)
138
+ - PR squash-merged; parent + all native sub-issues closed
139
+ - Board Status=Done for parent + every sub (if Projects v2 is on)
140
+ - On the base branch, up to date; effort worktree + local/remote branch removed and pruned
141
+ - A kit-sync warning if the effort touched kit-managed files
142
+
143
+ ## Rules
144
+
145
+ - **Snapshot BEFORE squash — absolute** (step a). Squash destroys the per-sub-issue commit pairs
146
+ that ARE the per-unit work record. Never reorder a/b. The trace lives under the **shared
147
+ git-common-dir** so it survives the worktree prune in step e.
148
+ - **One op owns board + issue + record state** — never rely on a separate, skippable "mark done"
149
+ (effort-model.md). All subs go Done here.
150
+ - Never merge a draft PR — abort with instructions to undraft.
151
+ - recover-before-prune (branch-naming.md): never remove a worktree with a staged-but-uncommitted
152
+ delta — the working tree must be clean (precondition) before this op proceeds.
153
+ - Scrub secrets from the trace if any commit diff could contain them (trace hygiene).
154
+ - Never force-push to trunk; never delete the default branch.
155
+ - **Heed step (f):** a kit-managed change that isn't contributed upstream is a latent regression
156
+ the next `/kit-update` can revert. Treat the warning as a to-do.
@@ -0,0 +1,173 @@
1
+ ---
2
+ name: kit-effort-new
3
+ description: Create an effort — a parent GitHub issue using the effort-model template (Goal / Scope / For agents / Verification) plus N native GitHub sub-issues linked via the sub-issues REST API, all added to the project board. The parent issue IS the plan.
4
+ when_to_use: When starting a new unit of work (a "plan"/effort) that decomposes into several sub-tasks. Replaces ad-hoc `gh issue create` for multi-part work. The effort splits into native sub-issues at scoping time; sequential subs commit on the effort branch, parallel subs use file-disjoint worktrees and merge in. See rules/effort-model.md.
5
+ ---
6
+
7
+ # kit-effort-new
8
+
9
+ Plugin-direct skill — runs straight from `${CLAUDE_PLUGIN_ROOT}` (no per-project `scripts/` checkout
10
+ needed). Helpers are sourced from the plugin; the verb logic lives in `scripts/lib/effort.sh`
11
+ (`effort_link_sub`), never re-authored inline (kit-engine-boundary #1/#2). Repo + board config come
12
+ from `load_kit_config` (`$KIT_REPO`, `KIT_PROJECTS_V2`). Reads `.claude/kit.config.json` from the
13
+ working directory.
14
+
15
+ Creates the **parent issue** (the plan) + **N native sub-issues** for one effort. See
16
+ `rules/effort-model.md` for the model and the parent-issue template. GitHub is the single source of
17
+ truth — the parent issue IS the plan (no separate plan file).
18
+
19
+ ## Inputs
20
+
21
+ | Field | Required | Notes |
22
+ | ------------ | -------- | ------------------------------------------------------------------------------------------- |
23
+ | Title | ✓ | The human **name only** (no `[Effort]`/number prefix) — the skill composes the board title. Concise, no jargon (`effort_title_lint`) |
24
+ | Role | ✓ | `tech-lead` / `frontend` / … → `role:<role>` label + board Role field |
25
+ | Flow | optional | Controlled-vocab flow (`EFFORT_FLOWS`) → `[Flow]` title tag + `flow:<flow>` label |
26
+ | Goal | ✓ | 1–2 lines — `## Goal` |
27
+ | Scope | ✓ | The sub-issue DAG — `## Scope` (mark each parallel \| sequential) |
28
+ | For agents | ✓ | Exact file paths / entry points — `## For agents` |
29
+ | Verification | ✓ | How we know it's done — `## Verification` |
30
+ | Sub-issues | ✓ | List of `name :: one-line desc` — one per concern (the skill numbers + prefixes them) |
31
+ | Depends on | optional | `#N` list → native GitHub `blocked_by` edge + a `## Relations` line (the visible chain) |
32
+ | Priority | optional | `p0`–`p3` → `priority:<p>` label (default `p1`) |
33
+ | Milestone | optional | Inherited onto the parent (sub-issues inherit it too) |
34
+
35
+ ## Execution
36
+
37
+ ```bash
38
+ source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/kit-config.sh" && load_kit_config
39
+ [[ "$KIT_PROJECTS_V2" == "true" ]] && { source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/gh-project.sh"; load_project_ids; }
40
+ source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/role-identity.sh" 2>/dev/null || true
41
+ source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/effort.sh"
42
+ source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/effort-metrics.sh" # effort_ctx_bucket
43
+
44
+ # TITLE = the human NAME only; the board title is composed as "[Effort] <N> · [<FLOW>] <TITLE>"
45
+ # (N is injected after creation — it's the issue's own number). FLOW is an optional controlled-vocab tag.
46
+ FLOW_TAG=""; [[ -n "${FLOW:-}" ]] && FLOW_TAG="[$FLOW] "
47
+
48
+ # 0. Guard the title content BEFORE creating anything: concise, no jargon, valid flow tag.
49
+ # Lint with a placeholder number — the rule is about the NAME, not the (not-yet-assigned) N.
50
+ effort_title_lint "[Effort] 0 · ${FLOW_TAG}${TITLE}" \
51
+ || { echo "✗ fix the effort title (above) before creating — concise / no-jargon / valid flow" >&2; return 1; }
52
+
53
+ # 1. Compose the parent body (the 4 sections) + an optional ## Relations chain.
54
+ RELATIONS=""
55
+ if [[ -n "${DEPENDS_ON:-}" ]]; then
56
+ RELATIONS=$'\n\n## Relations\n'
57
+ for d in ${DEPENDS_ON//,/ }; do d="${d#\#}"; RELATIONS+="- Depends on #$d"$'\n'; done
58
+ fi
59
+ PARENT_BODY=$(cat <<EOF
60
+ ## Goal
61
+
62
+ $GOAL
63
+
64
+ ## Scope
65
+
66
+ $SCOPE
67
+
68
+ ## For agents
69
+
70
+ $FOR_AGENTS
71
+
72
+ ## Verification
73
+
74
+ $VERIFICATION$RELATIONS
75
+ EOF
76
+ )
77
+
78
+ # 2. Create the parent issue (the plan). kind:task is the effort default unless overridden. Labels
79
+ # add `ctx:*` (session weight, first-pass from the sub count; refined at kit-effort-start) + an
80
+ # optional `flow:<flow>` tag.
81
+ SUBCOUNT=$(printf '%s\n' "$SUBS" | grep -c . 2>/dev/null || echo 1); [[ "$SUBCOUNT" -ge 1 ]] || SUBCOUNT=1
82
+ CTX="$(effort_ctx_bucket 1 "$SUBCOUNT")"
83
+ FLOW_LABEL=""; [[ -n "${FLOW:-}" ]] && FLOW_LABEL=",flow:$(printf '%s' "$FLOW" | tr '[:upper:]' '[:lower:]')"
84
+ PARENT_URL=$(gh issue create --repo "$KIT_REPO" \
85
+ --title "[Effort] PLACEHOLDER · ${FLOW_TAG}${TITLE}" \
86
+ --body "$PARENT_BODY" \
87
+ --label "$CTX,kind:task,priority:${PRIORITY:-p1},role:$ROLE$FLOW_LABEL" \
88
+ ${MILESTONE:+--milestone "$MILESTONE"})
89
+ PARENT_NUM=$(basename "$PARENT_URL")
90
+ # Inject the real effort id (== the issue number) into the title now that we have it.
91
+ gh issue edit "$PARENT_NUM" --repo "$KIT_REPO" --title "[Effort] $PARENT_NUM · ${FLOW_TAG}${TITLE}" >/dev/null
92
+ echo "✓ parent #$PARENT_NUM — $PARENT_URL"
93
+
94
+ # 2b. Native dependency edges from DEPENDS_ON — the visible board chain.
95
+ if [[ -n "${DEPENDS_ON:-}" ]]; then
96
+ for d in ${DEPENDS_ON//,/ }; do d="${d#\#}"; effort_set_blocked_by "$PARENT_NUM" "$d"; done
97
+ fi
98
+
99
+ # 3. Add the parent to the board, set Status=Todo + Role (if Projects v2 is enabled).
100
+ if [[ "$KIT_PROJECTS_V2" == "true" ]]; then
101
+ PARENT_NODE=$(gh api "repos/$KIT_REPO/issues/$PARENT_NUM" --jq .node_id)
102
+ PARENT_ITEM=$(project_add_item "$PARENT_NODE" 2>/dev/null || echo "")
103
+ if [[ -n "$PARENT_ITEM" ]]; then
104
+ project_set_single_select "$PARENT_ITEM" "$STATUS_FIELD_ID" "$STATUS_OPT_TODO"
105
+ ROLE_OPT=$(role_option_id "$ROLE" 2>/dev/null || echo "")
106
+ [[ -n "$ROLE_OPT" ]] && project_set_single_select "$PARENT_ITEM" "$ROLE_FIELD_ID" "$ROLE_OPT"
107
+ fi
108
+ fi
109
+
110
+ # 4. Create each sub-issue, link it natively under the parent, add it to the board.
111
+ # SUBS is a newline-delimited list of "name :: one-line description". The skill numbers them
112
+ # 1..k and composes the "[Effort <parent>] <M> · <name>" title (effort-model.md), linting each.
113
+ # Sub-issue bodies are intentionally short: the parent carries the narrative.
114
+ SUB_M=0
115
+ printf '%s\n' "$SUBS" | while IFS= read -r line; do
116
+ [[ -z "$line" ]] && continue
117
+ SUB_M=$((SUB_M + 1))
118
+ SUB_NAME="${line%% :: *}"
119
+ SUB_DESC="${line#* :: }"; [[ "$SUB_DESC" == "$line" ]] && SUB_DESC=""
120
+ SUB_TITLE="[Effort $PARENT_NUM] $SUB_M · $SUB_NAME"
121
+ effort_title_lint "$SUB_TITLE" \
122
+ || { echo " ✗ sub title fails the rule (above) — rename and re-run: $SUB_TITLE" >&2; continue; }
123
+ SUB_URL=$(gh issue create --repo "$KIT_REPO" \
124
+ --title "$SUB_TITLE" \
125
+ --body "$SUB_DESC
126
+
127
+ Sub-issue of #$PARENT_NUM (effort)." \
128
+ --label "kind:task,priority:${PRIORITY:-p1},role:$ROLE" \
129
+ ${MILESTONE:+--milestone "$MILESTONE"})
130
+ SUB_NUM=$(basename "$SUB_URL")
131
+ echo " ✓ sub #$SUB_NUM — $SUB_TITLE"
132
+
133
+ # Native parent/child link via the sub-issues REST API (child DB id, not number).
134
+ effort_link_sub "$PARENT_NUM" "$SUB_NUM"
135
+
136
+ # Board: add sub, Status=Todo + Role.
137
+ if [[ "$KIT_PROJECTS_V2" == "true" ]]; then
138
+ SUB_NODE=$(gh api "repos/$KIT_REPO/issues/$SUB_NUM" --jq .node_id)
139
+ SUB_ITEM=$(project_add_item "$SUB_NODE" 2>/dev/null || echo "")
140
+ if [[ -n "$SUB_ITEM" ]]; then
141
+ project_set_single_select "$SUB_ITEM" "$STATUS_FIELD_ID" "$STATUS_OPT_TODO"
142
+ ROLE_OPT=$(role_option_id "$ROLE" 2>/dev/null || echo "")
143
+ [[ -n "$ROLE_OPT" ]] && project_set_single_select "$SUB_ITEM" "$ROLE_FIELD_ID" "$ROLE_OPT"
144
+ fi
145
+ fi
146
+ done
147
+
148
+ echo "✓ effort #$PARENT_NUM created — next: /kit-effort-start $PARENT_NUM"
149
+ ```
150
+
151
+ ## Output
152
+
153
+ - Parent issue URL + number (the plan)
154
+ - One line per sub-issue (number + title + native-link confirmation)
155
+ - Parent + all subs on the board at Status=Todo with the Role field set (if Projects v2 is on)
156
+ - Suggested next: `/kit-effort-start <parent>`
157
+
158
+ ## Rules
159
+
160
+ - **The parent issue IS the plan** — never write a separate plan file; see `plan-output-format.md`.
161
+ - **Titles pass `effort_title_lint`** — concise, plain-language outcome, optional `[Flow]` tag from
162
+ the controlled vocabulary; no jargon / glyphs / code identifiers / parentheses / sub-clauses /
163
+ >6 words. The skill refuses to create an effort whose title fails. Detail goes in the body.
164
+ - **Carry the flow + the chain** — set `Flow` so the title shows `[Flow]` and the `flow:<flow>` label
165
+ is applied; pass `Depends on #N` so the native GitHub `blocked_by` edge + a `## Relations` line make
166
+ the chain visible on the board. `ctx:*` (session weight) is applied automatically.
167
+ - The parent body MUST carry all four template sections (Goal / Scope / For agents / Verification) —
168
+ a missing section is a review blocker.
169
+ - Sub-issues are **native** GitHub sub-issues (`POST …/issues/{parent}/sub_issues` with the child's
170
+ **database id**, resolved via `gh api repos/$KIT_REPO/issues/$N --jq .id`) — not just a checklist.
171
+ - Sub-issue bodies stay short (one line) — the parent holds the narrative.
172
+ - Never invent scope: ask for the sub-issue list if not provided.
173
+ - Scrub secrets from any pasted path/snippet before it enters the issue body (trace hygiene).
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: kit-effort-pr
3
+ description: Open the ONE pull request for an effort — `effort/<N>` → base branch — with a rich human-facing review body plus a `## For agents` section listing the file paths/entry points. Title `[Role] [#N] <title>`. Inherits labels + milestone from the parent issue and moves it to In Review on the board.
4
+ when_to_use: When an effort branch is ready for review (all its sub-issues built + merged into the effort branch). One effort = one PR (effort-model.md) — never open per-sub PRs to the base branch. Mirrors `kit-task-pr` for the effort lifecycle.
5
+ ---
6
+
7
+ # kit-effort-pr
8
+
9
+ Plugin-direct skill — runs straight from `${CLAUDE_PLUGIN_ROOT}` (no per-project `scripts/` checkout
10
+ needed). Helpers (config, role identity, board) are sourced from the plugin (kit-engine-boundary
11
+ #1/#2). Respects `KIT_BASE_BRANCH` (default `main`) and the `KIT_PROJECTS_V2` board toggle.
12
+
13
+ Opens the **single** PR for an effort. The PR carries the human-facing review write-up + a
14
+ `## For agents` section (retrieval context / the work record). The parent issue holds the plan;
15
+ this PR is where humans review the result. The title carries `[#N]` (the effort id) — mandatory
16
+ (effort-model.md, effort-id taxonomy).
17
+
18
+ ## Inputs
19
+
20
+ | Field | Required | Notes |
21
+ | -------------- | -------- | ----------------------------------------------------------- |
22
+ | Issue number | optional | The **parent** effort #N — derived from `effort/<N>-<slug>` |
23
+ | Summary | ✓ | The human review write-up (what changed + why) |
24
+ | For agents | ✓ | File paths / entry points — the PR's `## For agents` block |
25
+ | Commit message | optional | Defaults to `<role>: <issue title>` for any pending changes |
26
+
27
+ ## Execution
28
+
29
+ ```bash
30
+ source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/kit-config.sh" && load_kit_config
31
+ [[ "$KIT_PROJECTS_V2" == "true" ]] && { source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/gh-project.sh"; load_project_ids; }
32
+ source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/role-identity.sh" 2>/dev/null || true
33
+ BASE="${KIT_BASE_BRANCH:-main}"
34
+
35
+ # 1. Parse the parent effort number from the branch (effort/<N>-<slug>).
36
+ BRANCH=$(git rev-parse --abbrev-ref HEAD)
37
+ NUM="${INPUT_NUM:-}"
38
+ [[ -z "$NUM" ]] && NUM=$(echo "$BRANCH" | sed -nE 's#^effort/([0-9]+)-.*#\1#p')
39
+ [[ -z "$NUM" ]] && { echo "✗ Branch $BRANCH is not an effort branch (effort/<N>-<slug>)"; exit 1; }
40
+
41
+ # 2. Pull parent issue metadata.
42
+ META=$(gh issue view "$NUM" --repo "$KIT_REPO" --json title,labels,milestone)
43
+ TITLE=$(echo "$META" | jq -r .title)
44
+ LABELS=$(echo "$META" | jq -r '[.labels[].name] | join(",")')
45
+ MILESTONE=$(echo "$META" | jq -r '.milestone.title // ""')
46
+ ROLE=$(echo "$LABELS" | tr ',' '\n' | grep '^role:' | head -1 | cut -d: -f2)
47
+ ROLE_DISPLAY=$(role_display "$ROLE" 2>/dev/null || echo ""); [[ -z "$ROLE_DISPLAY" ]] && ROLE_DISPLAY="Tech Lead"
48
+
49
+ # 3. Commit any pending changes — role identity is a Co-authored-by TRAILER, never the git author
50
+ # (some deploy providers refuse commits whose author email matches no account).
51
+ COMMIT_MSG="${COMMIT_MSG:-$ROLE: $TITLE}"
52
+ if [[ -n "$(git status --porcelain)" ]]; then
53
+ git add -A
54
+ AUTHOR=$(role_git_author "$ROLE" 2>/dev/null || echo "")
55
+ if [[ -n "$AUTHOR" ]]; then
56
+ git commit -m "$COMMIT_MSG" -m "Co-authored-by: ${AUTHOR%%|*} <${AUTHOR##*|}>"
57
+ else
58
+ git commit -m "$COMMIT_MSG"
59
+ fi
60
+ fi
61
+
62
+ # 4. Push.
63
+ git push -u origin "$BRANCH"
64
+
65
+ # 5. Compose the PR body — rich human review + the agent-facing retrieval block.
66
+ PR_BODY=$(cat <<EOF
67
+ ## For the orchestrator
68
+
69
+ Effort #$NUM — one PR for the whole effort. Review: the summary below + the diff.
70
+
71
+ Closes #$NUM
72
+
73
+ ## Summary
74
+
75
+ $SUMMARY
76
+
77
+ ## For agents
78
+
79
+ $FOR_AGENTS
80
+
81
+ ## Verification
82
+
83
+ - [ ] Each sub-issue change is present in the diff
84
+ - [ ] No unrelated files in the diff
85
+ - [ ] Labels + milestone match the parent issue
86
+ - [ ] Local lint/typecheck pass
87
+
88
+ $(role_signature "$ROLE" 2>/dev/null || true)
89
+ EOF
90
+ )
91
+
92
+ # 6. Create the PR. Title: [Role] [#N] <title> — the [#N] effort id is mandatory.
93
+ URL=$(gh pr create --repo "$KIT_REPO" \
94
+ --base "$BASE" \
95
+ --head "$BRANCH" \
96
+ --title "[$ROLE_DISPLAY] [#$NUM] $TITLE" \
97
+ --body "$PR_BODY" \
98
+ ${LABELS:+--label "$LABELS"} \
99
+ ${MILESTONE:+--milestone "$MILESTONE"} \
100
+ --assignee "@me")
101
+ echo "✓ $URL"
102
+
103
+ # 7. Board (if Projects v2): add the PR (In Review) + inherit Role; move the parent issue to In Review.
104
+ if [[ "$KIT_PROJECTS_V2" == "true" ]]; then
105
+ PR_NUM=$(basename "$URL")
106
+ PR_NODE=$(gh api "repos/$KIT_REPO/pulls/$PR_NUM" --jq .node_id)
107
+ PR_ITEM=$(project_add_item "$PR_NODE" 2>/dev/null || echo "")
108
+ if [[ -n "$PR_ITEM" ]]; then
109
+ ROLE_OPT=$(role_option_id "$ROLE" 2>/dev/null || echo "")
110
+ [[ -n "$ROLE_OPT" ]] && project_set_single_select "$PR_ITEM" "$ROLE_FIELD_ID" "$ROLE_OPT"
111
+ project_set_single_select "$PR_ITEM" "$STATUS_FIELD_ID" "$STATUS_OPT_IN_REVIEW"
112
+ fi
113
+ # Parent issue → In Review (the finder paginates the whole board, not just the first page).
114
+ PARENT_ITEM=$(project_find_item_by_issue "$NUM")
115
+ [[ -n "$PARENT_ITEM" ]] && project_set_single_select "$PARENT_ITEM" "$STATUS_FIELD_ID" "$STATUS_OPT_IN_REVIEW"
116
+ fi
117
+
118
+ echo " Next: review, then /kit-effort-close $NUM"
119
+ ```
120
+
121
+ ## Output
122
+
123
+ - The single effort PR URL (title `[Role] [#N] <title>`)
124
+ - PR + parent issue both at In Review on the board (if Projects v2 is on)
125
+ - Suggested next: `/kit-effort-close <N>`
126
+
127
+ ## Rules
128
+
129
+ - **ONE PR per effort** — `effort/<N>` → base branch. Never open a PR from a `sub/<N><letter>`
130
+ branch to the base; sub-issues merge into the effort branch first (effort-model.md).
131
+ - The title MUST carry `[#N]` (the effort id) — a PR title without it is a review blocker
132
+ (effort-model.md, effort-id taxonomy).
133
+ - The body MUST include both a human `## Summary` and a `## For agents` (file paths) — the latter is
134
+ retrieval context + part of the work record.
135
+ - `Closes #$NUM` closes the **parent** on merge; the sub-issues are closed by `/kit-effort-close`
136
+ (so their pre-squash diffs are snapshotted first).
137
+ - Never invent a Summary or For-agents block — ask if missing.
138
+ - Labels + milestone mirror the parent issue; never strip them.
139
+ - Never push to a release branch directly.
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: kit-effort-start
3
+ description: Start an effort — create the `effort/<N>-<slug>` integration branch + isolated worktree from the base branch and mark the parent issue In Progress on the board. Sub-issues later branch from this effort branch or commit directly on it.
4
+ when_to_use: After `/kit-effort-new`, to begin building an effort. The effort branch is the single integration branch for the whole effort — its sub-issues merge into it, and exactly one PR opens from it (`/kit-effort-pr`). See rules/effort-model.md.
5
+ ---
6
+
7
+ # kit-effort-start
8
+
9
+ Plugin-direct skill — runs straight from `${CLAUDE_PLUGIN_ROOT}` (no per-project `scripts/` checkout
10
+ needed). Helpers are sourced from the plugin; repo state (`gh`, `git`, the board) is the project
11
+ you're standing in. Respects `KIT_BASE_BRANCH` (default `main`) so the same code serves any project.
12
+ Reads `.claude/kit.config.json` from the working directory.
13
+
14
+ ## Inputs
15
+
16
+ | Field | Required | Notes |
17
+ | ------------ | -------- | -------------------------------------------------------------- |
18
+ | Issue number | ✓ | The **parent** effort issue `N` |
19
+ | Slug | optional | Short kebab-case suffix; defaults to a slug of the issue title |
20
+
21
+ > **One effort = one branch = one worktree** (effort-model.md). The branch is `effort/<N>-<slug>`
22
+ > and the worktree is `.claude/worktrees/effort+<N>-<slug>`. Sub-issues either commit directly on
23
+ > this branch (sequential — one commit per sub-issue, that commit's diff IS the sub's work record)
24
+ > or use their own file-disjoint `sub/<N><letter>-<slug>` worktrees off this branch and merge in.
25
+ > Exactly ONE PR opens from `effort/<N>` (`/kit-effort-pr`).
26
+
27
+ ## Execution
28
+
29
+ ```bash
30
+ source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/kit-config.sh" && load_kit_config
31
+ [[ "$KIT_PROJECTS_V2" == "true" ]] && { source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/gh-project.sh"; load_project_ids; }
32
+ BASE="${KIT_BASE_BRANCH:-main}" # branch off the integration branch (override per project)
33
+
34
+ NUM=""; SLUG_OVERRIDE=""
35
+ for a in "$@"; do
36
+ [[ -z "$NUM" ]] && NUM="$a" || SLUG_OVERRIDE="$a"
37
+ done
38
+ [[ -n "$NUM" ]] || { echo "✗ kit-effort-start: parent issue number required"; exit 1; }
39
+
40
+ META=$(gh issue view "$NUM" --repo "$KIT_REPO" --json title,labels,projectItems)
41
+ TITLE=$(echo "$META" | jq -r .title)
42
+
43
+ if [[ -n "$SLUG_OVERRIDE" ]]; then SLUG="$SLUG_OVERRIDE"; else
44
+ SLUG=$(echo "$TITLE" | sed -E 's/^\[[^]]+\][[:space:]]*//' \
45
+ | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-|-$//g' | cut -c1-40)
46
+ fi
47
+
48
+ # The integration branch is ALWAYS effort/<N>-<slug> (branch-naming.md), regardless of the
49
+ # parent's kind:* label. The worktree dir still encodes #N so kit-gc protects it while #N is open.
50
+ BRANCH="effort/$NUM-$SLUG"
51
+ WT_DIR=".claude/worktrees/effort+${NUM}-${SLUG}"
52
+
53
+ git fetch origin "$BASE" --quiet
54
+ if git worktree list --porcelain | grep -q "/effort+${NUM}-${SLUG}$"; then
55
+ echo "✓ Worktree already exists: $WT_DIR (branch $BRANCH)"
56
+ else
57
+ git worktree add -B "$BRANCH" "$WT_DIR" "origin/$BASE"
58
+ fi
59
+
60
+ if [[ "$KIT_PROJECTS_V2" == "true" ]]; then
61
+ ITEM_ID=$(echo "$META" | jq -r --arg t "$KIT_PROJECT_TITLE" '.projectItems[]? | select(.title==$t) | .id')
62
+ [[ -n "$ITEM_ID" ]] && project_set_single_select "$ITEM_ID" "$STATUS_FIELD_ID" "$STATUS_OPT_IN_PROGRESS"
63
+ fi
64
+
65
+ echo "→ cd $WT_DIR # ALL effort work happens here; sub-issues merge into $BRANCH"
66
+ echo "✓ Worktree $WT_DIR on branch $BRANCH (from $BASE) — effort #$NUM In Progress"
67
+ echo " Next: build the sub-issues, then /kit-effort-pr $NUM"
68
+ ```
69
+
70
+ ## Output
71
+
72
+ - Worktree path + `effort/<N>-<slug>` branch — **work continues inside the worktree** (`cd $WT_DIR`)
73
+ - Parent issue #N now In Progress on the board (if Projects v2 is on)
74
+ - Reminder that exactly one PR opens from this branch (`/kit-effort-pr <N>`)
75
+
76
+ ## Rules
77
+
78
+ - The integration branch is **always** `effort/<N>-<slug>` (branch-naming.md, effort-model.md) —
79
+ never `task/` or `feat/` for the parent.
80
+ - Branch from the base branch (`KIT_BASE_BRANCH`) only — never from a feature branch.
81
+ - **Worktree always** — one branch per worktree, never share a checkout. The `effort+<N>-<slug>`
82
+ worktree-dir name encodes #N so `kit-gc` won't wipe it while the issue is open.
83
+ - Sub-issue branches are `sub/<N><letter>-<slug>` off THIS branch and **never open their own PR to
84
+ the base branch** — they merge into `effort/<N>`.
85
+ - Don't re-run for the parent if its worktree already exists — the skill reuses it.
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: kit-gc
3
+ description: Garbage-collect the repo — prune worktrees whose PR already merged, delete merged local + remote branches, surface orphan/unpushed commits and stale stashes, and flag issues whose PR merged but stayed open. Dry-run by default; destructive steps require explicit confirmation.
4
+ when_to_use: When the repo has accumulated stale branches, orphan worktrees, or stashes (the SessionStart hygiene hook flags this), or on demand to tidy before/after a chapter of work. Replaces the manual cleanup sweep.
5
+ ---
6
+
7
+ # kit-gc — repo garbage collector
8
+
9
+ Plugin-direct skill — helpers resolve from `${CLAUDE_PLUGIN_ROOT}`.
10
+
11
+ Cleans what accumulates: merged-but-undeleted branches (local + remote), orphan
12
+ worktrees, stale stashes, orphan unpushed commits, and stale-open issues.
13
+
14
+ **Safety contract:** read-only **analysis first** → present a plan → only delete
15
+ after the user confirms. Never touch: the base branch (`main`/`develop`), branches
16
+ with an **open PR**, worktrees of an open-PR branch, **a branch/worktree whose
17
+ associated issue is still OPEN**, or branches/commits whose work is **not on the base
18
+ branch's remote** (orphan work is surfaced, never auto-deleted).
19
+
20
+ > **Worktree↔issue association.** A branch `<kind>/<N>-<slug>` (or worktree dir
21
+ > `<kind>+<N>-<slug>`) belongs to issue **#N** — derived from the name, not registered.
22
+ > `gc` resolves #N and **refuses to remove anything whose issue is still open, even
23
+ > with no PR yet** — this protects in-progress worktrees that haven't opened a PR.
24
+ > Helper: `${CLAUDE_PLUGIN_ROOT}/scripts/lib/worktree-issue.sh` (`wt_issue_number`, `wt_protected_reason`).
25
+
26
+ ## Execution
27
+
28
+ ### 1. Refresh + analyze (read-only)
29
+
30
+ ```bash
31
+ # Read-only analysis lives in the CANONICAL Family-1 home — the skill is a thin caller (#419):
32
+ # scripts/lib/kit-gc.sh :: kit_gc_analyze (also what `kit gc` + the kit-ui cockpit run)
33
+ source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/kit-config.sh" && load_kit_config
34
+ KIT_GC_REPO="$KIT_REPO" source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/kit-gc.sh"
35
+ kit_gc_analyze # worktrees / branches / stashes, each tagged PROTECTED/SAFE/ACTIVE/ORPHAN
36
+ ```
37
+
38
+ Classify every branch/worktree (issue-open protection wins over everything except an explicit force):
39
+
40
+ | Bucket | Rule | Action |
41
+ | --- | --- | --- |
42
+ | **Protected (open issue)** | `wt_protected_reason` non-empty — associated issue **#N still OPEN** | **keep** — never prune, even with no PR / merged PR |
43
+ | **Safe to delete** | PR `MERGED`, level with its remote (no unpushed commits), **and** issue closed/absent | prune |
44
+ | **Active** | open PR, or the base branch, or a worktree branch with an open PR | keep |
45
+ | **Orphan** | local commits **not on the base branch's remote** and no merged PR | **surface, never auto-delete** — offer to open a recovery PR |
46
+
47
+ For squash-merged branches, `--is-ancestor` is unreliable — trust the **PR state**, then verify the branch is level with its remote (`git rev-list --count origin/<b>..<b>` = 0) before deleting.
48
+
49
+ ### 2. Present the plan
50
+
51
+ Print a table: worktrees to remove, branches to delete (local + remote), orphans to recover, stashes to drop, and stale-open issues (PR merged but issue still open). **Ask for confirmation** before any destructive step. Let the user veto per-bucket.
52
+
53
+ ### 3. Execute (after confirmation)
54
+
55
+ ```bash
56
+ # Worktrees whose PR merged
57
+ git worktree remove <path> --force && git worktree prune
58
+ # Merged branches — local then remote
59
+ git branch -D <branch>
60
+ git push origin --delete <branch>
61
+ # Stale-open issues (PR already merged)
62
+ gh issue close <n> --repo "$KIT_REPO" --reason completed --comment "Delivered via PR #<pr>."
63
+ # Stashes — only after showing each diff and getting an explicit OK
64
+ git stash drop 'stash@{n}' # or: git stash clear
65
+ ```
66
+
67
+ ### 4. Orphan handling
68
+
69
+ For each orphan branch with unique unpushed work: **do not delete**. Recover it
70
+ into a clean PR off the base branch (branch from `$BASE`, `git checkout <orphan> --
71
+ <paths>`, commit, push, open PR) so the user decides merge vs close.
72
+
73
+ ## Rules
74
+
75
+ - **Dry-run + confirm by default.** Never delete without showing the plan first.
76
+ - **Never** delete the base branch (`main`/`develop`) or an open-PR branch/worktree.
77
+ - **Never** remove a worktree/branch whose **associated issue is still open** (`wt_protected_reason` non-empty) — close the issue first, or pass an explicit per-item override.
78
+ - **Never** drop a stash or delete an orphan branch without explicit per-item consent (stashes and unpushed commits are irreversible).
79
+ - Verify a branch is **level with its remote** before deleting — an "ahead" branch may hold orphan work.
80
+ - Prefer enabling GitHub **"Automatically delete head branches"** so remote pruning stops being a manual step.