@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.
- package/.claude-plugin/plugin.json +22 -0
- package/AGENTS.md +101 -0
- package/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/README.md +143 -0
- package/SECURITY.md +22 -0
- package/bin/cckit +215 -0
- package/cckit.config.json +34 -0
- package/commands/kit-add.md +42 -0
- package/commands/kit-docs.md +45 -0
- package/commands/kit-doctor.md +52 -0
- package/commands/kit-export-project.md +58 -0
- package/commands/kit-export-training.md +49 -0
- package/commands/kit-init.md +126 -0
- package/commands/kit-routines.md +59 -0
- package/commands/kit-update.md +132 -0
- package/docs/kit-annotate/01-explainer.html +225 -0
- package/docs/kit-annotate/02-implementation-plan.html +196 -0
- package/docs/media/.onboarding-capture.cast +5 -0
- package/docs/media/README.md +43 -0
- package/docs/media/build-demo.sh +63 -0
- package/docs/media/build-kit-init.sh +51 -0
- package/docs/media/build-onboarding.sh +51 -0
- package/docs/media/kit-dry-run.cast +107 -0
- package/docs/media/kit-dry-run.gif +0 -0
- package/docs/media/kit-init.cast +56 -0
- package/docs/media/kit-init.gif +0 -0
- package/docs/media/kit-onboarding.cast +148 -0
- package/docs/media/kit-onboarding.gif +0 -0
- package/githooks/pre-commit +18 -0
- package/kit.config.schema.json +105 -0
- package/package.json +54 -0
- package/privacy-denylist.example +8 -0
- package/profiles/automation.json +36 -0
- package/profiles/content.json +41 -0
- package/profiles/minimal.json +31 -0
- package/profiles/research.json +37 -0
- package/profiles/software.json +32 -0
- package/scripts/annotate-setup.sh +149 -0
- package/scripts/autopilot.sh +50 -0
- package/scripts/capture-project-ids.sh +53 -0
- package/scripts/check.sh +66 -0
- package/scripts/contribute.sh +48 -0
- package/scripts/debug.sh +54 -0
- package/scripts/init-upgrade-test.sh +99 -0
- package/scripts/init.sh +827 -0
- package/scripts/install.sh +24 -0
- package/scripts/kit-add-test.sh +62 -0
- package/scripts/kit-add.sh +115 -0
- package/scripts/kit-adopt-test.sh +61 -0
- package/scripts/kit-adopt.sh +122 -0
- package/scripts/kit-bump-version.sh +79 -0
- package/scripts/kit-digest.sh +126 -0
- package/scripts/kit-doctor.sh +663 -0
- package/scripts/kit-export-project-test.sh +82 -0
- package/scripts/kit-export-project.sh +245 -0
- package/scripts/kit-export-training-test.sh +51 -0
- package/scripts/kit-export-training.sh +175 -0
- package/scripts/kit-migrate-test.sh +80 -0
- package/scripts/kit-migrate.sh +190 -0
- package/scripts/kit-onboard-test.sh +63 -0
- package/scripts/kit-onboard.sh +69 -0
- package/scripts/kit-promote-test.sh +54 -0
- package/scripts/kit-promote.sh +102 -0
- package/scripts/kit-remove-test.sh +61 -0
- package/scripts/kit-remove.sh +84 -0
- package/scripts/kit-routines.sh +322 -0
- package/scripts/kit-version-check.sh +91 -0
- package/scripts/kit-wire-test.sh +54 -0
- package/scripts/kit-wire.sh +132 -0
- package/scripts/knowledge-lint.sh +96 -0
- package/scripts/lib/cckit-output.sh +36 -0
- package/scripts/lib/effort-metrics.sh +452 -0
- package/scripts/lib/effort-ops-test.sh +83 -0
- package/scripts/lib/effort-ops.sh +132 -0
- package/scripts/lib/effort-plan.sh +104 -0
- package/scripts/lib/effort.sh +191 -0
- package/scripts/lib/engine-adapter.sh +92 -0
- package/scripts/lib/gh-log.sh +58 -0
- package/scripts/lib/gh-project.sh +212 -0
- package/scripts/lib/handoff.sh +35 -0
- package/scripts/lib/kit-cli-test.sh +42 -0
- package/scripts/lib/kit-cli.sh +32 -0
- package/scripts/lib/kit-config-resolve.sh +145 -0
- package/scripts/lib/kit-config.sh +88 -0
- package/scripts/lib/kit-engine-test.sh +107 -0
- package/scripts/lib/kit-events.sh +62 -0
- package/scripts/lib/kit-gc.sh +117 -0
- package/scripts/lib/kit-interview-test.sh +77 -0
- package/scripts/lib/kit-interview.sh +203 -0
- package/scripts/lib/kit-local.sh +79 -0
- package/scripts/lib/kit-manifest.sh +127 -0
- package/scripts/lib/kit-mode-test.sh +49 -0
- package/scripts/lib/kit-mode.sh +67 -0
- package/scripts/lib/kit-operate.sh +105 -0
- package/scripts/lib/kit-profile-test.sh +62 -0
- package/scripts/lib/kit-profile.sh +115 -0
- package/scripts/lib/kit-task-ops-test.sh +63 -0
- package/scripts/lib/kit-task-ops.sh +341 -0
- package/scripts/lib/pr-evidence.sh +173 -0
- package/scripts/lib/project-scan.sh +16 -0
- package/scripts/lib/react-detect.sh +78 -0
- package/scripts/lib/role-identity.sh +47 -0
- package/scripts/lib/secret-guard.sh +96 -0
- package/scripts/lib/toon.sh +35 -0
- package/scripts/lib/ui.sh +42 -0
- package/scripts/lib/version-bump.sh +59 -0
- package/scripts/lib/worktree-issue-test.sh +45 -0
- package/scripts/lib/worktree-issue.sh +73 -0
- package/scripts/lib/worktree-start.sh +280 -0
- package/scripts/orchestrate.sh +160 -0
- package/scripts/portable-test.sh +53 -0
- package/scripts/publish.sh +94 -0
- package/scripts/setup-labels.sh +25 -0
- package/scripts/setup-milestones.sh +17 -0
- package/scripts/showcase.sh +64 -0
- package/scripts/status.sh +44 -0
- package/scripts/task-sync.sh +59 -0
- package/scripts/test.sh +48 -0
- package/scripts/web-install.sh +22 -0
- package/skills/kit-annotate/SKILL.md +107 -0
- package/skills/kit-autopilot/SKILL.md +108 -0
- package/skills/kit-contribute/SKILL.md +134 -0
- package/skills/kit-customize/SKILL.md +134 -0
- package/skills/kit-dev/SKILL.md +67 -0
- package/skills/kit-digest/SKILL.md +41 -0
- package/skills/kit-effort-close/SKILL.md +156 -0
- package/skills/kit-effort-new/SKILL.md +173 -0
- package/skills/kit-effort-pr/SKILL.md +139 -0
- package/skills/kit-effort-start/SKILL.md +85 -0
- package/skills/kit-gc/SKILL.md +80 -0
- package/skills/kit-onboard/SKILL.md +50 -0
- package/skills/kit-security-sweep/SKILL.md +57 -0
- package/skills/kit-ship/SKILL.md +43 -0
- package/skills/kit-task-close/SKILL.md +66 -0
- package/skills/kit-task-new/SKILL.md +51 -0
- package/skills/kit-task-pr/SKILL.md +43 -0
- package/skills/kit-task-pr-auto/SKILL.md +27 -0
- package/skills/kit-task-pr-merge/SKILL.md +53 -0
- package/skills/kit-task-start/SKILL.md +76 -0
- package/skills/kit-task-sync/SKILL.md +37 -0
- package/templates/CLAUDE.md.tmpl +106 -0
- package/templates/agents/analyst.md +55 -0
- package/templates/agents/auto-dev.md +93 -0
- package/templates/agents/backend.md +59 -0
- package/templates/agents/designer.md +73 -0
- package/templates/agents/devops.md +57 -0
- package/templates/agents/editor.md +48 -0
- package/templates/agents/frontend.md +81 -0
- package/templates/agents/generalist.md +46 -0
- package/templates/agents/local-delegate.md +70 -0
- package/templates/agents/n8n.md +65 -0
- package/templates/agents/pm.md +69 -0
- package/templates/agents/qa.md +66 -0
- package/templates/agents/researcher.md +57 -0
- package/templates/agents/security.md +65 -0
- package/templates/agents/tech-lead.md +75 -0
- package/templates/hooks/guard-base-branch-commit.sh.tmpl +45 -0
- package/templates/hooks/kit-local-status.sh.tmpl +34 -0
- package/templates/hooks/kit_version_check.sh.tmpl +6 -0
- package/templates/hooks/mempal_followup.sh.tmpl +97 -0
- package/templates/hooks/mempal_precompact.sh.tmpl +4 -0
- package/templates/hooks/mempal_save.sh.tmpl +4 -0
- package/templates/hooks/mempal_session_start.sh.tmpl +8 -0
- package/templates/hooks/prepush_gate.sh.tmpl +36 -0
- package/templates/hooks/repo-hygiene.sh.tmpl +72 -0
- package/templates/kit.config.json.tmpl +32 -0
- package/templates/knowledge-INDEX.md.tmpl +12 -0
- package/templates/lib/kit-sigil.sh.tmpl +124 -0
- package/templates/rules/branch-naming.md +104 -0
- package/templates/rules/communication-style.md +22 -0
- package/templates/rules/delegation-brief.md +40 -0
- package/templates/rules/design-routing.md +35 -0
- package/templates/rules/effort-model.md +122 -0
- package/templates/rules/knowledge-base.md +41 -0
- package/templates/rules/mempalace.md +110 -0
- package/templates/rules/plan-output-format.md +58 -0
- package/templates/rules/react-annotate.md +69 -0
- package/templates/rules/risk-tiered-review.md +62 -0
- package/templates/rules/skill-gaps.md +48 -0
- package/templates/rules/task-management.md +42 -0
- package/templates/settings/settings.local.json.tmpl +27 -0
- package/templates/skills/NAMESPACED +13 -0
- package/templates/skills/copywriting/SKILL.md +252 -0
- package/templates/skills/copywriting/references/copy-frameworks.md +344 -0
- package/templates/skills/copywriting/references/natural-transitions.md +272 -0
- package/templates/skills/feature-build-refine/SKILL.md +367 -0
- package/templates/skills/karpathy-guidelines/SKILL.md +69 -0
- package/templates/skills/morning-briefing/SKILL.md +46 -0
- package/templates/skills/speckit/SKILL.md +239 -0
- 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.
|