@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,827 @@
1
+ #!/usr/bin/env bash
2
+ # claude-kit init — scaffold a tailored .claude/ into a target project.
3
+ # Substitutes {{VARS}} and resolves <!-- IF:FLAG --> blocks from a role profile.
4
+ #
5
+ # Usage:
6
+ # scripts/init.sh --profile software --name "My App" [flags]
7
+ # scripts/init.sh --upgrade --target <dir> # merge new features into an existing setup
8
+ #
9
+ # Flags (all optional except --profile; sensible defaults derived otherwise):
10
+ # --profile <software|content|research|minimal> role profile (required unless --upgrade)
11
+ # --target <dir> project root to scaffold into (default: $PWD)
12
+ # --name <str> human project name (default: target basename)
13
+ # --slug <str> short slug / mempalace wing (default: lowercased basename)
14
+ # --repo <owner/repo> GitHub repo (default: <owner>/<slug>)
15
+ # --owner-login <login> GitHub user/org login (default: gh api user)
16
+ # --owner-name <str> human owner name (default: owner-login)
17
+ # --project-number <int> Projects v2 number (enables board) (default: none -> board off)
18
+ # --plans <mdx|markdown|none> plan file format (default: profile default)
19
+ # --plans-dir <dir> where plans live (default: per format)
20
+ # --memory <on|off> enable MemPalace memory + hooks (default: profile default)
21
+ # --speckit <on|off> add the Spec-Driven Development flow (default: off)
22
+ # --prepush "<command>" install an opt-in pre-push gate that runs <command> before
23
+ # `git push` and blocks on failure (default: none -> no gate)
24
+ # --local <on|off> local model layer (mlx_lm.server) for NL chores at $0 API cost
25
+ # (config + SessionStart status hook) (default: off)
26
+ # --lang <str> working language (default: English)
27
+ # --upgrade merge new kit features into an existing .claude/ (preserves your edits)
28
+ # --dry-run print the scaffold plan and exit, writing nothing
29
+ # --force overwrite without prompting
30
+ set -euo pipefail
31
+
32
+ # ============================================================================
33
+ # TERMINAL IDENTITY — init banner + golden-angle plant animation #
34
+ # Canonical brief: the cckit docs ( #
35
+ # terminal-identity brief) #
36
+ # Sources kit-sigil.sh for palette/mode detection when available. #
37
+ # ============================================================================
38
+
39
+ # _kit_banner_detect_mode — populate KIT_COLOR, KIT_UNICODE, and palette vars.
40
+ # Tries to source the plugin's kit-sigil template (plain bash, no {{VARS}});
41
+ # falls back to inline detection if the template is unavailable or fails.
42
+ _kit_banner_detect_mode() {
43
+ # Try the plugin's sigil template (lives beside this script in the plugin tree)
44
+ local _sigil
45
+ _sigil="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/templates/lib/kit-sigil.sh.tmpl"
46
+ if [ -f "$_sigil" ]; then
47
+ # shellcheck disable=SC1090
48
+ if source "$_sigil" 2>/dev/null; then
49
+ kit_color_mode 2>/dev/null && return 0
50
+ fi
51
+ fi
52
+ # Inline fallback — matches the logic in kit-sigil.sh exactly.
53
+ local _loc="${LC_ALL:-${LC_CTYPE:-${LANG:-}}}"
54
+ if [ "${KIT_ASCII:-}" = "1" ]; then
55
+ KIT_UNICODE=0
56
+ elif printf '%s' "$_loc" | grep -qiE 'utf-?8'; then
57
+ KIT_UNICODE=1
58
+ else
59
+ KIT_UNICODE=0
60
+ fi
61
+ if [ "${FORCE_COLOR:-}" = "1" ]; then
62
+ KIT_COLOR=1
63
+ elif [ -t 1 ] && [ -z "${NO_COLOR+x}" ]; then
64
+ KIT_COLOR=1
65
+ else
66
+ KIT_COLOR=0
67
+ fi
68
+ if [ "$KIT_COLOR" = 1 ]; then
69
+ KIT_AZAFRAN=$'\033[38;2;201;122;44m'
70
+ KIT_INK=$'\033[2m'
71
+ KIT_POLVO=$'\033[38;2;168;160;151m'
72
+ KIT_RESET=$'\033[0m'
73
+ else
74
+ KIT_AZAFRAN=''; KIT_INK=''; KIT_POLVO=''; KIT_RESET=''
75
+ fi
76
+ }
77
+
78
+ # _kit_print_static_banner — the V4 box-drawing CLAUDE KIT banner (§7 exact form).
79
+ # Always printed: after the animation on TTY, or alone when non-TTY / ASCII mode.
80
+ # Reads: KIT_COLOR KIT_UNICODE KIT_AZAFRAN KIT_INK KIT_POLVO KIT_RESET
81
+ # Arg 1 (optional): version string, no "v" prefix (e.g. "0.7.0").
82
+ _kit_print_static_banner() {
83
+ local _ver="${1:-}"
84
+ printf '\n'
85
+ if [ "${KIT_UNICODE:-0}" = "1" ]; then
86
+ # V4 box-drawing letterforms — ink-dim body, single Azafran seed as I-dot of KIT.
87
+ # Brief §7 exact layout (three rows of box-drawing, then lockup + tagline):
88
+ #
89
+ # ┌─┐┬ ┌─┐┬ ┬┌┬┐┌─┐ ┬┌─┬┌┬┐
90
+ # │ │ ├─┤│ │ ││├┤ ├┴┐│ │ ● <- Azafran seed (I-dot of KIT)
91
+ # └─┘┴─┘┴ ┴└─┘─┴┘└─┘ ┴ ┴┴ ┴
92
+ #
93
+ # ⡶ / claude-kit agentic workflows for your repo
94
+ # v0.7.0 · the seed is planted
95
+ printf ' %s%s%s\n' \
96
+ "${KIT_INK}" \
97
+ '┌─┐┬ ┌─┐┬ ┬┌┬┐┌─┐ ┬┌─┬┌┬┐' \
98
+ "${KIT_RESET}"
99
+ # Row 2: letterforms + Azafran seed to the right (the I-dot of KIT)
100
+ printf ' %s%s%s %s%s%s\n' \
101
+ "${KIT_INK}" '│ │ ├─┤│ │ ││├┤ ├┴┐│ │ ' "${KIT_RESET}" \
102
+ "${KIT_AZAFRAN}" '●' "${KIT_RESET}"
103
+ printf ' %s%s%s\n' \
104
+ "${KIT_INK}" \
105
+ '└─┘┴─┘┴ ┴└─┘─┴┘└─┘ ┴ ┴┴ ┴' \
106
+ "${KIT_RESET}"
107
+ printf '\n'
108
+ # Lockup line: seed-head leader + wordmark + tagline
109
+ printf ' %s%s%s%s%s\n' \
110
+ "${KIT_AZAFRAN}" '⡶' "${KIT_RESET}" \
111
+ "${KIT_INK}" " / claude-kit agentic workflows for your repo${KIT_RESET}"
112
+ if [ -n "$_ver" ]; then
113
+ printf ' %s%s%s\n' "${KIT_POLVO}" "v${_ver} · the seed is planted" "${KIT_RESET}"
114
+ else
115
+ printf ' %s%s%s\n' "${KIT_POLVO}" '· the seed is planted' "${KIT_RESET}"
116
+ fi
117
+ else
118
+ # ASCII fallback — no box-drawing, no Braille (KIT_ASCII=1 or non-UTF-8 locale).
119
+ printf ' CLAUDE KIT\n'
120
+ printf ' ----------\n'
121
+ printf '\n'
122
+ printf ' o / claude-kit agentic workflows for your repo\n'
123
+ if [ -n "$_ver" ]; then
124
+ printf ' v%s . the seed is planted\n' "$_ver"
125
+ else
126
+ printf ' . the seed is planted\n'
127
+ fi
128
+ fi
129
+ printf '\n'
130
+ }
131
+
132
+ # _kit_plant_animation — golden-angle Vogel seed-head plant animation (§7 beat 1).
133
+ #
134
+ # Guard: runs ONLY when stdout is a TTY AND KIT_UNICODE=1 (full Unicode, not ASCII mode).
135
+ # Uses ANSI cursor-up to repaint a 4-row Braille grid in-place as seeds enter.
136
+ #
137
+ # Geometry: n=13, fixed scale, 10x16 dot grid => 5 Braille cols x 4 rows.
138
+ # Seed n at angle n*137.50776 deg, radius sqrt(n). Coordinates locked to the
139
+ # full n=13 scatter so each seed lands in its final position (no rescaling).
140
+ # Pre-computed in Python from the Vogel rule; see brief §2 for derivation.
141
+ #
142
+ # Animation frames (seeds visible: 1, 2, 4, 7, 13):
143
+ # f1 = seed n=0 only (center)
144
+ # f2 = seeds n=0,1
145
+ # f4 = seeds n=0..3
146
+ # f7 = seeds n=0..6
147
+ # ff = seeds n=0..12 (full n=13 hero)
148
+ #
149
+ # Braille rows — each string is 5 Braille chars (U+2800-28FF).
150
+ # Row 0 = top, row 3 = bottom. Center seed (n=0) lives at row 1, col 2.
151
+ _kit_plant_animation() {
152
+ [ -t 1 ] || return 0 # non-TTY: skip, banner will print statically
153
+ [ "${KIT_UNICODE:-0}" = "1" ] || return 0 # ASCII mode: skip
154
+
155
+ local ind=' ' # indent to align with banner text
156
+
157
+ # Frame data — pre-computed Braille rows (5 chars each, 4 rows per frame).
158
+ # Stored as positional arrays via function-local vars (portable; no declare -a needed).
159
+ # Frame f1: seed n=0 (center seed only)
160
+ local f1_0='⠀⠀⠀⠀⠀' f1_1='⠀⠀⢀⠀⠀' f1_2='⠀⠀⠀⠀⠀' f1_3='⠀⠀⠀⠀⠀'
161
+ # Frame f2: seeds n=0,1
162
+ local f2_0='⠀⠀⠀⠀⠀' f2_1='⠀⠀⢀⠀⠀' f2_2='⠀⠀⠂⠀⠀' f2_3='⠀⠀⠀⠀⠀'
163
+ # Frame f4: seeds n=0..3
164
+ local f4_0='⠀⠀⠀⠀⠀' f4_1='⠀⠀⢈⠀⠀' f4_2='⠀⠀⠂⠄⠀' f4_3='⠀⠀⠀⠀⠀'
165
+ # Frame f7: seeds n=0..6
166
+ local f7_0='⠀⠀⠀⠀⠀' f7_1='⠀⠄⢈⠐⠀' f7_2='⠀⠀⠂⠄⠀' f7_3='⠀⠀⠁⠀⠀'
167
+ # Frame ff: seeds n=0..12 (full n=13 hero)
168
+ local ff_0='⢀⠠⠀⠐⠀' ff_1='⠀⠄⢈⠐⠀' ff_2='⠠⠀⠂⠄⠂' ff_3='⠀⠀⠁⠄⠀'
169
+
170
+ # Print one frame: 4 Braille rows in ink-dim.
171
+ # Args: row0 row1 row2 row3
172
+ _print_frame() {
173
+ printf '%s%s%s\n' "${KIT_INK}${ind}" "$1" "${KIT_RESET}"
174
+ printf '%s%s%s\n' "${KIT_INK}${ind}" "$2" "${KIT_RESET}"
175
+ printf '%s%s%s\n' "${KIT_INK}${ind}" "$3" "${KIT_RESET}"
176
+ printf '%s%s%s\n' "${KIT_INK}${ind}" "$4" "${KIT_RESET}"
177
+ }
178
+
179
+ # Cursor up N lines (ANSI CSI A).
180
+ _cur_up() { printf '\033[%dA' "$1"; }
181
+
182
+ # Portable fractional sleep — silently ignores if the system only takes integers.
183
+ _psleep() { sleep "$1" 2>/dev/null || true; }
184
+
185
+ # Print a leading blank line and the first frame (no repaint yet).
186
+ printf '\n'
187
+ _print_frame "$f1_0" "$f1_1" "$f1_2" "$f1_3"
188
+
189
+ # Subsequent frames: cursor-up 4, repaint in-place.
190
+ _psleep 0.12
191
+ _cur_up 4; _print_frame "$f2_0" "$f2_1" "$f2_2" "$f2_3"
192
+
193
+ _psleep 0.12
194
+ _cur_up 4; _print_frame "$f4_0" "$f4_1" "$f4_2" "$f4_3"
195
+
196
+ _psleep 0.12
197
+ _cur_up 4; _print_frame "$f7_0" "$f7_1" "$f7_2" "$f7_3"
198
+
199
+ _psleep 0.18
200
+ _cur_up 4; _print_frame "$ff_0" "$ff_1" "$ff_2" "$ff_3"
201
+
202
+ # Brief pause before the banner snaps in, then clear the animation grid.
203
+ _psleep 0.30
204
+ # Move up past the 4 grid rows + the blank line printed before the grid (5 lines total),
205
+ # then erase from cursor to end of screen so the banner replaces the grid cleanly.
206
+ _cur_up 5
207
+ printf '\033[J' # CSI J — erase from cursor to end of screen
208
+ }
209
+
210
+ # _kit_init_banner — entry point for a fresh init run (not --upgrade, not --dry-run).
211
+ # Arg 1 (optional): plugin version string without "v" prefix.
212
+ _kit_init_banner() {
213
+ _kit_banner_detect_mode
214
+ _kit_plant_animation # no-op when non-TTY / KIT_ASCII=1 / non-UTF-8
215
+ _kit_print_static_banner "${1:-}" # always prints (static or after animation)
216
+ }
217
+
218
+ # ============================================================================
219
+ # END TERMINAL IDENTITY #
220
+ # ============================================================================
221
+
222
+ KIT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
223
+
224
+ # ---- preflight: run kit-doctor for Tier 1 hard deps (jq, perl, gh, git) ----
225
+ # Skip during --upgrade (already initialized) and --dry-run (read-only; doctor
226
+ # would be redundant). In interactive runs the doctor auto-installs what it can;
227
+ # if Tier 1 deps are still missing after it runs, init exits with a clear report.
228
+ _DOCTOR="$KIT_ROOT/scripts/kit-doctor.sh"
229
+ _INIT_SKIP_PREFLIGHT=0
230
+ for _ia in "$@"; do [[ "$_ia" == "--upgrade" || "$_ia" == "--dry-run" ]] && _INIT_SKIP_PREFLIGHT=1; done
231
+
232
+ if [[ $_INIT_SKIP_PREFLIGHT -eq 0 && -x "$_DOCTOR" ]]; then
233
+ # Pass --no-install if init itself was called with --no-install equivalent
234
+ bash "$_DOCTOR" || {
235
+ echo "" >&2
236
+ echo "x kit-doctor reported unresolved issues — fix them and re-run /kit-init." >&2
237
+ exit 1
238
+ }
239
+ else
240
+ # Fallback bare checks (upgrade/dry-run path: doctor was already run on init)
241
+ command -v jq >/dev/null 2>&1 || { echo "x jq is required." >&2; exit 1; }
242
+ command -v perl >/dev/null 2>&1 || { echo "x perl is required." >&2; exit 1; }
243
+ fi
244
+
245
+ # ---- parse flags ---------------------------------------------------------
246
+ PROFILE=""; TARGET="$PWD"; NAME=""; SLUG=""; REPO=""; GH_OWNER=""; OWNER_NAME=""
247
+ PROJECT_NUMBER=""; PLANS_FORMAT=""; PLANS_DIR=""; MEMORY=""; LANG_PREF="English"; FORCE=0
248
+ SPECKIT=""; PREPUSH_CMD=""; LOCAL_LAYER=""; LOCAL_EXPLICIT=0; DRY_RUN=0; UPGRADE=0
249
+ SKILL_PREFIX="" # namespace applied to kit-namespaced skill templates (from kit.config .skillPrefix on upgrade)
250
+ while [[ $# -gt 0 ]]; do
251
+ case "$1" in
252
+ --profile) PROFILE="$2"; shift 2 ;;
253
+ --target) TARGET="$2"; shift 2 ;;
254
+ --name) NAME="$2"; shift 2 ;;
255
+ --slug) SLUG="$2"; shift 2 ;;
256
+ --repo) REPO="$2"; shift 2 ;;
257
+ --owner-login) GH_OWNER="$2"; shift 2 ;;
258
+ --owner-name) OWNER_NAME="$2"; shift 2 ;;
259
+ --project-number) PROJECT_NUMBER="$2"; shift 2 ;;
260
+ --plans) PLANS_FORMAT="$2"; shift 2 ;;
261
+ --plans-dir) PLANS_DIR="$2"; shift 2 ;;
262
+ --memory) MEMORY="$2"; shift 2 ;;
263
+ --speckit) SPECKIT="$2"; shift 2 ;;
264
+ --prepush) PREPUSH_CMD="$2"; shift 2 ;;
265
+ --local) LOCAL_LAYER="$2"; LOCAL_EXPLICIT=1; shift 2 ;;
266
+ --lang) LANG_PREF="$2"; shift 2 ;;
267
+ --force) FORCE=1; shift ;;
268
+ --upgrade) UPGRADE=1; shift ;;
269
+ --dry-run) DRY_RUN=1; shift ;;
270
+ -h|--help) sed -n '2,31p' "$0"; exit 0 ;;
271
+ *) echo "x unknown flag: $1" >&2; exit 1 ;;
272
+ esac
273
+ done
274
+
275
+ # Installed plugin version -> recorded into kit.config as kitVersion (drives /kit-update checks).
276
+ PLUGIN_VERSION="$(jq -r '.version // "0.0.0"' "$KIT_ROOT/.claude-plugin/plugin.json" 2>/dev/null || echo "0.0.0")"
277
+
278
+ # ---- upgrade mode: backfill inputs from the project's existing config ----
279
+ if [[ $UPGRADE -eq 1 ]]; then
280
+ UP_TARGET="$(cd "${TARGET:-$PWD}" && pwd)"
281
+ UP_CFG="$UP_TARGET/.claude/kit.config.json"
282
+ [[ -f "$UP_CFG" ]] || { echo "x --upgrade needs $UP_CFG (run /kit-init first)."; exit 1; }
283
+
284
+ # ---- guard: never apply the kit over its OWN home, or downgrade ----------
285
+ # The kit is DEVELOPED in-tree in its home repo (packages/claude-kit-plugin/). Running
286
+ # --upgrade there would copy the installed (usually OLDER) plugin snapshot over the live
287
+ # source — the recurring "every time I improve the kit, /kit-update fights me" footgun.
288
+ # Refuse cleanly (exit 0, not an error) so the home repo edits the source directly.
289
+ if [[ -f "$UP_TARGET/packages/claude-kit-plugin/.claude-plugin/plugin.json" ]]; then
290
+ echo "x $UP_TARGET is claude-kit's home repo -- edit packages/claude-kit-plugin/ directly; /kit-update is for consumer projects, not the kit's source."
291
+ exit 0
292
+ fi
293
+ # Nothing to do when the installed plugin is the SAME version as the project (avoids the
294
+ # confusing re-scan + any stray re-add). A genuinely-behind consumer (kitVersion < plugin)
295
+ # still upgrades normally.
296
+ _have_kv="$(jq -r '.kitVersion // "0.0.0"' "$UP_CFG" 2>/dev/null || echo 0.0.0)"
297
+ if [[ "$_have_kv" == "$PLUGIN_VERSION" ]]; then
298
+ echo "x already at $PLUGIN_VERSION -- nothing to upgrade. (To pull a newer release, refresh the plugin cache: claude plugin update.)"
299
+ exit 0
300
+ fi
301
+
302
+ [[ -z "$PROFILE" ]] && PROFILE="$(jq -r '.profile // "minimal"' "$UP_CFG")"
303
+ [[ -z "$NAME" ]] && NAME="$(jq -r '.project.name // ""' "$UP_CFG")"
304
+ [[ -z "$SLUG" ]] && SLUG="$(jq -r '.project.slug // ""' "$UP_CFG")"
305
+ [[ -z "$REPO" ]] && REPO="$(jq -r '.github.repo // ""' "$UP_CFG")"
306
+ [[ -z "$GH_OWNER" ]] && GH_OWNER="$(jq -r '.github.owner // ""' "$UP_CFG")"
307
+ [[ -z "$MEMORY" ]] && MEMORY="$(jq -r 'if .memory.enabled then "on" else "off" end' "$UP_CFG")"
308
+ [[ -z "$SPECKIT" ]] && SPECKIT="$(jq -r 'if (.specKit.enabled // false) then "on" else "off" end' "$UP_CFG")"
309
+ [[ -z "$LOCAL_LAYER" ]] && LOCAL_LAYER="$(jq -r 'if (.local.enabled // false) then "on" else "off" end' "$UP_CFG")"
310
+ [[ -z "$PLANS_FORMAT" ]] && PLANS_FORMAT="$(jq -r '.plans.format // ""' "$UP_CFG")"
311
+ [[ "$LANG_PREF" == "English" ]] && LANG_PREF="$(jq -r '.project.language // "English"' "$UP_CFG")"
312
+ # Skill-name prefix: projects that namespace their kit skills (e.g. "kit-task-close") set
313
+ # `skillPrefix` so upgrade scaffolds namespaced templates to the matching name instead of
314
+ # creating bare-name duplicates. Empty = bare names (default).
315
+ [[ -z "$SKILL_PREFIX" ]] && SKILL_PREFIX="$(jq -r '.skillPrefix // ""' "$UP_CFG")"
316
+ _UPNUM="$(jq -r '.github.projectNumber // empty' "$UP_CFG")"
317
+ [[ -z "$PROJECT_NUMBER" && -n "$_UPNUM" && "$_UPNUM" != "null" ]] && PROJECT_NUMBER="$_UPNUM"
318
+ _UPPP="$(jq -r '.prePush.command // ""' "$UP_CFG")"
319
+ [[ -z "$PREPUSH_CMD" && -n "$_UPPP" && "$_UPPP" != "null" ]] && PREPUSH_CMD="$_UPPP"
320
+
321
+ # ---- upgrade exclusion registry (#334) ----------------------------------
322
+ # `upgrade.removed[]` + keys of `upgrade.renamed{}` are paths the project
323
+ # deleted/renamed ON PURPOSE. The upgrade must never re-add them ("add missing
324
+ # files" doesn't know the project's deliberate state). Read from the EXISTING
325
+ # config (pre-merge) into a newline-delimited list consumed by _upgrade_excluded.
326
+ UPGRADE_EXCLUDE="$(jq -r '((.upgrade.removed // []) + ((.upgrade.renamed // {}) | keys)) | .[]' "$UP_CFG" 2>/dev/null || true)"
327
+
328
+ # ---- dirty working tree guard (upgrade mode only) -----------------------
329
+ # Refuse to write files into a project with uncommitted changes.
330
+ # Safe no-op when git is unavailable or UP_TARGET is not a git repo.
331
+ # Pass --force to skip at your own risk.
332
+ if [[ $DRY_RUN -eq 0 && $FORCE -eq 0 ]]; then
333
+ if git -C "$UP_TARGET" rev-parse --git-dir >/dev/null 2>&1; then
334
+ _dirty="$(git -C "$UP_TARGET" status --porcelain 2>/dev/null || true)"
335
+ if [[ -n "$_dirty" ]]; then
336
+ echo "x working tree is dirty -- commit, stash, or discard before running /kit-update" >&2
337
+ echo " (pass --force to override at your own risk)" >&2
338
+ exit 1
339
+ fi
340
+ fi
341
+ fi
342
+ fi
343
+
344
+ [[ -z "$PROFILE" ]] && { echo "x --profile is required (software|content|research|minimal)"; exit 1; }
345
+ PROFILE_FILE="$KIT_ROOT/profiles/$PROFILE.json"
346
+ [[ -f "$PROFILE_FILE" ]] || { echo "x no such profile: $PROFILE ($PROFILE_FILE)"; exit 1; }
347
+
348
+ # ---- resolve target + defaults ------------------------------------------
349
+ mkdir -p "$TARGET"
350
+ TARGET="$(cd "$TARGET" && pwd)"
351
+ BASENAME="$(basename "$TARGET")"
352
+ [[ -z "$NAME" ]] && NAME="$BASENAME"
353
+ [[ -z "$SLUG" ]] && SLUG="$(echo "$BASENAME" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed -E 's/^-|-$//g')"
354
+ if [[ -z "$GH_OWNER" ]]; then GH_OWNER="$(gh api user --jq .login 2>/dev/null || echo "")"; fi
355
+ [[ -z "$OWNER_NAME" ]] && OWNER_NAME="${GH_OWNER:-owner}"
356
+ [[ -z "$REPO" && -n "$GH_OWNER" ]] && REPO="$GH_OWNER/$SLUG"
357
+ [[ -z "$REPO" ]] && REPO="$SLUG"
358
+
359
+ # profile defaults
360
+ [[ -z "$PLANS_FORMAT" ]] && PLANS_FORMAT="$(jq -r '.defaults.plans_format' "$PROFILE_FILE")"
361
+ [[ -z "$MEMORY" ]] && MEMORY="$(jq -r '.defaults.memory' "$PROFILE_FILE")"
362
+ [[ -z "$SPECKIT" ]] && SPECKIT="$(jq -r '.defaults.speckit // "off"' "$PROFILE_FILE")"
363
+ [[ -z "$LOCAL_LAYER" ]] && LOCAL_LAYER="$(jq -r '.defaults.local // "off"' "$PROFILE_FILE")"
364
+ if [[ -z "$PLANS_DIR" ]]; then
365
+ case "$PLANS_FORMAT" in
366
+ mdx) PLANS_DIR="apps/plans/src/briefs" ;;
367
+ *) PLANS_DIR="docs/plans" ;;
368
+ esac
369
+ fi
370
+
371
+ # arrays from profile
372
+ ROLES_NL="$(jq -r '.roles[]' "$PROFILE_FILE")"
373
+ MS_NL="$(jq -r '.milestones[]' "$PROFILE_FILE")"
374
+ AGENTS="$(jq -r '.agents[]' "$PROFILE_FILE")"
375
+ SKILLS="$(jq -r '.skills[]' "$PROFILE_FILE")"
376
+ RULES="$(jq -r '.rules[]' "$PROFILE_FILE")"
377
+
378
+ # Kit-namespaced skill set: templates whose names take $SKILL_PREFIX (the workflow skills:
379
+ # task-*, effort-*). Content skills (morning-briefing, karpathy-…) are NOT listed and stay bare.
380
+ # Listed in templates/skills/NAMESPACED (one name per line) so the marker lives outside the artifact.
381
+ NAMESPACED_SKILLS="$(grep -vE '^\s*(#|$)' "$KIT_ROOT/templates/skills/NAMESPACED" 2>/dev/null || true)"
382
+ _is_namespaced() { # <skill-name> -> 0 if it takes the prefix
383
+ printf '%s\n' "$NAMESPACED_SKILLS" | grep -qxF "$1"
384
+ }
385
+ # Rewrite a freshly-scaffolded namespaced skill so its own name: + sibling /command refs + sigil
386
+ # label carry $SKILL_PREFIX (template ships bare; the project namespaces). The (?![\w-]) guard stops
387
+ # /task-pr matching inside /task-pr-merge. perl is a Tier-1 dep (kit-doctor).
388
+ _prefix_skill_file() { # <file>
389
+ local f="$1" n
390
+ printf '%s\n' "$NAMESPACED_SKILLS" | while IFS= read -r n; do
391
+ [[ -n "$n" ]] || continue
392
+ P="$SKILL_PREFIX" N="$n" perl -i -pe '
393
+ my ($p,$n)=($ENV{P},$ENV{N});
394
+ s{^name:\h*\Q$n\E\h*$}{name: $p$n};
395
+ s{/\Q$n\E(?![\w-])}{/$p$n}g;
396
+ s{kit_sigil "\Q$n\E"}{kit_sigil "$p$n"}g;
397
+ ' "$f"
398
+ done
399
+ }
400
+
401
+ ROLES_JSON="$(printf '%s' "$ROLES_NL" | jq -Rs 'split("\n")|map(select(length>0))')"
402
+ MS_JSON="$(printf '%s' "$MS_NL" | jq -Rs 'split("\n")|map(select(length>0))')"
403
+ ROLES_HUMAN="$(printf '%s' "$ROLES_NL" | paste -sd '·' - | sed 's/·/ · /g')"
404
+ MILESTONES_HUMAN="$(printf '%s' "$MS_NL" | paste -sd '·' - | sed 's/·/ · /g')"
405
+
406
+ # flags / booleans
407
+ if [[ -n "$PROJECT_NUMBER" ]]; then PROJECTS_V2="true"; PNUM_JSON="$PROJECT_NUMBER"; else PROJECTS_V2="false"; PNUM_JSON="null"; PROJECT_NUMBER="null"; fi
408
+ case "$MEMORY" in on|true|yes) MEMORY_BOOL="true";; *) MEMORY_BOOL="false";; esac
409
+ if [[ "$PLANS_FORMAT" == "none" ]]; then PLANS_ON="false"; else PLANS_ON="true"; fi
410
+ if echo "$ROLES_NL" | grep -qx "Designer"; then DESIGN_ON="true"; else DESIGN_ON="false"; fi
411
+ CLAUDE_PROJECT_SLUG="$(echo "$TARGET" | sed 's#/#-#g')"
412
+
413
+ # spec kit
414
+ case "$SPECKIT" in on|true|yes) SPECKIT_ON="true";; *) SPECKIT_ON="false";; esac
415
+ SPECKIT_JSON="$SPECKIT_ON"
416
+
417
+ # pre-push gate (opt-in: enabled when a check command is provided)
418
+ if [[ -n "$PREPUSH_CMD" ]]; then PREPUSH_ENABLED="true"; else PREPUSH_ENABLED="false"; fi
419
+
420
+ # local model layer (opt-in: mlx_lm.server for NL chores)
421
+ case "$LOCAL_LAYER" in on|true|yes) LOCAL_ON="true";; *) LOCAL_ON="false";; esac
422
+
423
+ # stack-gated build skills present in this profile?
424
+ if echo "$SKILLS" | grep -qE '^(feature-build-refine|supabase-patterns)$'; then STACK_SKILLS_ON="true"; else STACK_SKILLS_ON="false"; fi
425
+
426
+ # FLAGS_ON drives <!-- IF:X --> resolution
427
+ FLAGS_ON=""
428
+ [[ "$MEMORY_BOOL" == "true" ]] && FLAGS_ON="${FLAGS_ON}MEMORY,"
429
+ [[ "$PROJECTS_V2" == "true" ]] && FLAGS_ON="${FLAGS_ON}PROJECTS_V2,"
430
+ [[ "$PLANS_ON" == "true" ]] && FLAGS_ON="${FLAGS_ON}PLANS,"
431
+ [[ "$DESIGN_ON" == "true" ]] && FLAGS_ON="${FLAGS_ON}DESIGN,"
432
+ [[ "$SPECKIT_ON" == "true" ]] && FLAGS_ON="${FLAGS_ON}SPECKIT,"
433
+ [[ "$STACK_SKILLS_ON" == "true" ]] && FLAGS_ON="${FLAGS_ON}STACK_SKILLS,"
434
+
435
+ # ---- build agent/skill tables -------------------------------------------
436
+ short_desc() { sed -n 's/^description: //p' "$1" | head -1 | cut -c1-92; }
437
+ AGENT_TABLE="| Agent | Role |
438
+ | ----- | ---- |"
439
+ for a in $AGENTS; do
440
+ f="$KIT_ROOT/templates/agents/$a.md"
441
+ [[ -f "$f" ]] || { echo " missing agent template: $a" >&2; continue; }
442
+ AGENT_TABLE="$AGENT_TABLE
443
+ | \`$a\` | $(short_desc "$f") |"
444
+ done
445
+ SKILL_TABLE="| Skill | When |
446
+ | ----- | ---- |"
447
+ for s in $SKILLS; do
448
+ f="$KIT_ROOT/templates/skills/$s/SKILL.md"
449
+ [[ -f "$f" ]] || { echo " missing skill template: $s" >&2; continue; }
450
+ SKILL_TABLE="$SKILL_TABLE
451
+ | \`$s\` | $(short_desc "$f") |"
452
+ done
453
+
454
+ # ---- export vars for substitution ---------------------------------------
455
+ export PROJECT_NAME="$NAME" PROJECT_SLUG="$SLUG" OWNER_NAME="$OWNER_NAME" COMMS_LANG="$LANG_PREF"
456
+ export PROFILE GH_REPO="$REPO" GH_OWNER="$GH_OWNER" PROJECT_NUMBER PROJECT_BOARD_TITLE="$NAME"
457
+ export PLANS_DIR PLANS_FORMAT KNOWLEDGE_DIR="knowledge" WING="$SLUG" CLAUDE_PROJECT_SLUG
458
+ export MILESTONES_HUMAN ROLES_HUMAN AGENT_TABLE SKILL_TABLE
459
+ export KIT_FLAGS_ON="$FLAGS_ON"
460
+
461
+ # emit: resolve IF blocks, then substitute {{VARS}}
462
+ emit() { # <src> <dest>
463
+ mkdir -p "$(dirname "$2")"
464
+ perl -0777 -pe '
465
+ my %on = map { $_=>1 } grep { length } split /,/, ($ENV{KIT_FLAGS_ON}//"");
466
+ 1 while s{<!-- IF:(\w+) -->(.*?)<!-- /IF:\1 -->}{ $on{$1} ? $2 : "" }gse;
467
+ s/\{\{(\w+)\}\}/ exists $ENV{$1} ? $ENV{$1} : "{{$1}}" /ge;
468
+ ' "$1" > "$2"
469
+ }
470
+
471
+ # _upgrade_excluded: true when <rel> was removed/renamed by the project on purpose
472
+ # (registered in kit.config.json `upgrade.removed[]` / `upgrade.renamed{}`). Matches an
473
+ # entry exactly OR as a directory prefix (entry "/.claude/skills/foo" covers its SKILL.md
474
+ # + references/). Only meaningful in --upgrade; always false otherwise. (#334)
475
+ _upgrade_excluded() { # <rel>
476
+ [[ $UPGRADE -eq 1 ]] || return 1
477
+ local rel="$1" e
478
+ while IFS= read -r e; do
479
+ [[ -z "$e" ]] && continue
480
+ [[ "$rel" == "$e" || "$rel" == "$e"/* ]] && return 0
481
+ done <<< "${UPGRADE_EXCLUDE:-}"
482
+ return 1
483
+ }
484
+
485
+ # safe_write: in --upgrade, never overwrite an existing file (preserve user edits) and never
486
+ # re-add a config-excluded path — only add genuinely new files. In normal mode it is just emit.
487
+ # Tracks added / preserved / skipped-by-config paths for the upgrade summary. (#334)
488
+ safe_write() { # <src> <dest>
489
+ local rel="${2#"$TARGET"/}"
490
+ if _upgrade_excluded "$rel"; then
491
+ UPGRADE_SKIPPED+=("$rel"); return 0
492
+ fi
493
+ if [[ $UPGRADE -eq 1 && -e "$2" ]]; then
494
+ UPGRADE_PRESERVED+=("$rel"); return 0
495
+ fi
496
+ emit "$1" "$2"
497
+ [[ $UPGRADE -eq 1 ]] && UPGRADE_ADDED+=("$rel")
498
+ return 0
499
+ }
500
+
501
+ # safe_copy: verbatim (non-templated) counterpart of safe_write for kit-owned files copied
502
+ # as-is (scripts/, skill references). Honors the SAME upgrade contract — existing files are
503
+ # PRESERVED, config-excluded paths are SKIPPED — fixing the downgrade where scripts/ used to
504
+ # be unconditionally overwritten with older templates. Pass "exec" as $3 to chmod +x. (#334)
505
+ safe_copy() { # <src> <dest> [exec]
506
+ local rel="${2#"$TARGET"/}"
507
+ [[ -f "$1" ]] || return 0
508
+ if _upgrade_excluded "$rel"; then
509
+ UPGRADE_SKIPPED+=("$rel"); return 0
510
+ fi
511
+ if [[ $UPGRADE -eq 1 && -e "$2" ]]; then
512
+ UPGRADE_PRESERVED+=("$rel"); return 0
513
+ fi
514
+ mkdir -p "$(dirname "$2")"
515
+ cp "$1" "$2"
516
+ [[ "${3:-}" == "exec" ]] && chmod +x "$2" 2>/dev/null
517
+ [[ $UPGRADE -eq 1 ]] && UPGRADE_ADDED+=("$rel")
518
+ return 0
519
+ }
520
+
521
+ # ---- dry run: print the plan and exit, writing nothing -------------------
522
+ if [[ $DRY_RUN -eq 1 ]]; then
523
+ echo "-> DRY RUN — nothing will be written. Plan for $TARGET:"
524
+ [[ $UPGRADE -eq 1 ]] && echo " (UPGRADE: existing files are PRESERVED; only genuinely-missing files are added; config keys merged.)"
525
+ [[ $UPGRADE -eq 1 && -n "${UPGRADE_EXCLUDE:-}" ]] && { echo " (SKIPPED by kit.config upgrade.removed/renamed — never re-added:)"; printf '%s\n' "${UPGRADE_EXCLUDE}" | sed 's/^/ - /'; }
526
+ echo ""
527
+ echo " Profile : $PROFILE"
528
+ echo " Project : $NAME ($SLUG)"
529
+ echo " Repo : $REPO"
530
+ echo " Board : $([[ "$PROJECTS_V2" == "true" ]] && echo "Projects v2 #$PROJECT_NUMBER" || echo "off (gh issues only)")"
531
+ echo " Memory : $MEMORY_BOOL Plans: $PLANS_FORMAT Lang: $LANG_PREF"
532
+ echo " Spec Kit: $SPECKIT_ON Pre-push gate: $PREPUSH_ENABLED Local model: $LOCAL_ON kitVersion: $PLUGIN_VERSION"
533
+ echo ""
534
+ echo " Would write:"
535
+ echo " CLAUDE.md"
536
+ echo " .claude/kit.config.json"
537
+ echo " .claude/settings.local.json"
538
+ echo " .claude/hooks/repo-hygiene.sh (SessionStart: read-only repo-hygiene report)"
539
+ echo " .claude/hooks/guard-base-branch-commit.sh (PreToolUse Bash: block commits to base branch)"
540
+ echo " .claude/hooks/kit_version_check.sh (SessionStart: update check)"
541
+ for r in $RULES; do
542
+ case "$r" in
543
+ mempalace) [[ "$MEMORY_BOOL" == "true" ]] || continue ;;
544
+ design-routing) [[ "$DESIGN_ON" == "true" ]] || continue ;;
545
+ plan-output-format) [[ "$PLANS_ON" == "true" ]] || continue ;;
546
+ esac
547
+ [[ -f "$KIT_ROOT/templates/rules/$r.md" ]] && echo " .claude/rules/$r.md"
548
+ done
549
+ for a in $AGENTS; do
550
+ [[ -f "$KIT_ROOT/templates/agents/$a.md" ]] && echo " .claude/agents/$a/AGENT.md"
551
+ done
552
+ for s in $SKILLS; do
553
+ _dn="$s"; { [[ -n "$SKILL_PREFIX" ]] && _is_namespaced "$s"; } && _dn="${SKILL_PREFIX}${s}"
554
+ [[ -f "$KIT_ROOT/templates/skills/$s/SKILL.md" ]] && echo " .claude/skills/$_dn/SKILL.md"
555
+ [[ -d "$KIT_ROOT/templates/skills/$s/references" ]] && echo " .claude/skills/$_dn/references/"
556
+ done
557
+ echo " scripts/ (kit-config.sh, gh-project.sh, kit-version-check.sh, setup-labels.sh, setup-milestones.sh, capture-project-ids.sh, task-sync.sh, knowledge-lint.sh)"
558
+ echo " knowledge/INDEX.md (knowledge-base manifest — rules/knowledge-base.md)"
559
+ echo " .claude/lib/kit-sigil.sh (claude-kit attribution sigil helper, sourced by task-* footers)"
560
+ [[ "$MEMORY_BOOL" == "true" ]] && echo " .claude/hooks/mempal_session_start.sh + mempal_save.sh + mempal_precompact.sh + mempal_followup.sh (SessionStart / Stop / PreCompact / SessionEnd)"
561
+ [[ "$PREPUSH_ENABLED" == "true" ]] && echo " .claude/hooks/prepush_gate.sh (PreToolUse Bash gate: $PREPUSH_CMD)"
562
+ [[ "$LOCAL_ON" == "true" ]] && echo " .claude/hooks/kit-local-status.sh (SessionStart: announce local model layer when alive)"
563
+ echo ""
564
+ echo " Onboarding would offer (opt-in): gh auth$([[ "$MEMORY_BOOL" == "true" ]] && echo " · MemPalace (mempalace-mcp)") · gws (if you use Google Workspace) · local model layer (--local on: NL chores at \$0 via mlx_lm.server)"
565
+ echo ""
566
+ echo "Re-run without --dry-run to write these files."
567
+ exit 0
568
+ fi
569
+
570
+ # ---- guard ---------------------------------------------------------------
571
+ if [[ -e "$TARGET/.claude/kit.config.json" && $FORCE -eq 0 && $UPGRADE -eq 0 ]]; then
572
+ echo " $TARGET/.claude already initialized. Re-run with --upgrade to merge in new features (preserves your edits), or --force to overwrite generated files."
573
+ exit 1
574
+ fi
575
+
576
+ # ---- init banner (fresh init only; not on --upgrade) ---------------------
577
+ # Beat 1: golden-angle plant animation (TTY + Unicode only, else skipped).
578
+ # Beat 2: V4 box-drawing CLAUDE KIT banner snap (always).
579
+ if [[ $UPGRADE -eq 0 ]]; then
580
+ _kit_init_banner "$PLUGIN_VERSION"
581
+ fi
582
+
583
+ UPGRADE_ADDED=(); UPGRADE_PRESERVED=(); UPGRADE_SKIPPED=()
584
+ echo "-> $([[ $UPGRADE -eq 1 ]] && echo Upgrading || echo Scaffolding) $PROFILE profile $([[ $UPGRADE -eq 1 ]] && echo in || echo into) $TARGET"
585
+ mkdir -p "$TARGET/.claude/agents" "$TARGET/.claude/skills" "$TARGET/.claude/rules" "$TARGET/scripts/lib"
586
+
587
+ # ---- kit.config.json (built with jq for correctness) --------------------
588
+ KITCFG="$TARGET/.claude/kit.config.json"
589
+ jq -n \
590
+ --arg kitver "$PLUGIN_VERSION" \
591
+ --arg name "$NAME" --arg slug "$SLUG" --arg owner "$OWNER_NAME" --arg lang "$LANG_PREF" --arg path "$TARGET" \
592
+ --arg profile "$PROFILE" --argjson roles "$ROLES_JSON" \
593
+ --arg repo "$REPO" --arg ghowner "$GH_OWNER" \
594
+ --argjson pv2 "$PROJECTS_V2" --argjson pnum "$PNUM_JSON" --arg ptitle "$NAME" \
595
+ --argjson milestones "$MS_JSON" \
596
+ --arg pfmt "$PLANS_FORMAT" --arg pdir "$PLANS_DIR" \
597
+ --argjson mem "$MEMORY_BOOL" --arg wing "$SLUG" --arg cps "$CLAUDE_PROJECT_SLUG" \
598
+ --argjson speckit "$SPECKIT_JSON" \
599
+ --argjson ppen "$PREPUSH_ENABLED" --arg ppcmd "$PREPUSH_CMD" \
600
+ --argjson localon "$LOCAL_ON" \
601
+ '{kitVersion:$kitver,
602
+ project:{name:$name,slug:$slug,owner:$owner,language:$lang,path:$path},
603
+ profile:$profile, roles:$roles,
604
+ github:{repo:$repo,owner:$ghowner,projectsV2:$pv2,projectNumber:$pnum,projectTitle:$ptitle},
605
+ milestones:$milestones,
606
+ plans:{format:$pfmt,dir:$pdir},
607
+ knowledge:{dir:"knowledge"},
608
+ specKit:{enabled:$speckit},
609
+ prePush:{enabled:$ppen,command:$ppcmd},
610
+ local:{enabled:$localon,port:8080,model:"mlx-community/Qwen3-8B-4bit"},
611
+ memory:{enabled:$mem,provider:"mempalace",wing:$wing,claudeProjectSlug:$cps}}' \
612
+ > "$KITCFG.kit-new"
613
+ if [[ $UPGRADE -eq 1 && -f "$KITCFG" ]]; then
614
+ # deep-merge: existing values win (preserve user choices), new keys added, version bumped.
615
+ jq -s --arg kitver "$PLUGIN_VERSION" '.[0] * .[1] | .kitVersion = $kitver' "$KITCFG.kit-new" "$KITCFG" \
616
+ > "$KITCFG.merged" && mv "$KITCFG.merged" "$KITCFG"
617
+ rm -f "$KITCFG.kit-new"
618
+ # Existing values win in the merge — but an EXPLICIT --local flag is a user decision now,
619
+ # so it overrides the preserved value (port/model stay as the user configured them).
620
+ if [[ $LOCAL_EXPLICIT -eq 1 ]]; then
621
+ jq --argjson v "$LOCAL_ON" '.local = ((.local // {port:8080,model:"mlx-community/Qwen3-8B-4bit"}) | .enabled = $v)' \
622
+ "$KITCFG" > "$KITCFG.tmp" && mv "$KITCFG.tmp" "$KITCFG"
623
+ fi
624
+ echo " + .claude/kit.config.json (merged · kitVersion -> $PLUGIN_VERSION)"
625
+ else
626
+ mv "$KITCFG.kit-new" "$KITCFG"
627
+ echo " + .claude/kit.config.json"
628
+ fi
629
+
630
+ # ---- CLAUDE.md -----------------------------------------------------------
631
+ safe_write "$KIT_ROOT/templates/CLAUDE.md.tmpl" "$TARGET/CLAUDE.md"
632
+ echo " + CLAUDE.md"
633
+
634
+ # ---- rules (filtered by enabled flags) ----------------------------------
635
+ for r in $RULES; do
636
+ # Skip rules whose feature is disabled for this project.
637
+ case "$r" in
638
+ mempalace) [[ "$MEMORY_BOOL" == "true" ]] || continue ;;
639
+ design-routing) [[ "$DESIGN_ON" == "true" ]] || continue ;;
640
+ plan-output-format) [[ "$PLANS_ON" == "true" ]] || continue ;;
641
+ esac
642
+ src="$KIT_ROOT/templates/rules/$r.md"
643
+ [[ -f "$src" ]] || { echo " missing rule: $r"; continue; }
644
+ safe_write "$src" "$TARGET/.claude/rules/$r.md"
645
+ echo " + .claude/rules/$r.md"
646
+ done
647
+
648
+ # ---- agents --------------------------------------------------------------
649
+ for a in $AGENTS; do
650
+ src="$KIT_ROOT/templates/agents/$a.md"
651
+ [[ -f "$src" ]] || continue
652
+ safe_write "$src" "$TARGET/.claude/agents/$a/AGENT.md"
653
+ echo " + .claude/agents/$a/AGENT.md"
654
+ done
655
+
656
+ # ---- skills --------------------------------------------------------------
657
+ for s in $SKILLS; do
658
+ src="$KIT_ROOT/templates/skills/$s/SKILL.md"
659
+ [[ -f "$src" ]] || continue
660
+ # Namespaced skills take $SKILL_PREFIX so we scaffold to the project's name (kit-task-close)
661
+ # instead of a bare-name duplicate (task-close) alongside it.
662
+ dest_name="$s"
663
+ if [[ -n "$SKILL_PREFIX" ]] && _is_namespaced "$s"; then
664
+ dest_name="${SKILL_PREFIX}${s}"
665
+ fi
666
+ safe_write "$src" "$TARGET/.claude/skills/$dest_name/SKILL.md"
667
+ # If prefixed AND this was a FRESH scaffold (frontmatter still bare), rewrite name + sibling refs.
668
+ # An existing (preserved) skill already reads `name: $dest_name`, so the guard skips it.
669
+ if [[ "$dest_name" != "$s" && -f "$TARGET/.claude/skills/$dest_name/SKILL.md" ]] \
670
+ && grep -qE "^name:[[:space:]]*${s}[[:space:]]*\$" "$TARGET/.claude/skills/$dest_name/SKILL.md"; then
671
+ _prefix_skill_file "$TARGET/.claude/skills/$dest_name/SKILL.md"
672
+ fi
673
+ echo " + .claude/skills/$dest_name/SKILL.md"
674
+ # bundled references for multi-file skills (copied verbatim, no templating).
675
+ # Per-file safe_copy so the upgrade contract (preserve existing, skip excluded) applies.
676
+ if [[ -d "$KIT_ROOT/templates/skills/$s/references" ]]; then
677
+ for _ref in "$KIT_ROOT/templates/skills/$s/references/"*; do
678
+ [[ -f "$_ref" ]] || continue
679
+ safe_copy "$_ref" "$TARGET/.claude/skills/$dest_name/references/$(basename "$_ref")"
680
+ done
681
+ echo " + .claude/skills/$dest_name/references/"
682
+ fi
683
+ done
684
+
685
+ # ---- scripts (libs + setup + sync; kit-owned) ---------------------------
686
+ # safe_copy honors the upgrade contract: on --upgrade an EXISTING script is PRESERVED, never
687
+ # overwritten with an older template (the #334 downgrade that erased #307/#313/#314). Fresh
688
+ # inits get the current scripts; to refresh a kit-owned script later, delete it then re-upgrade.
689
+ safe_copy "$KIT_ROOT/scripts/lib/kit-config.sh" "$TARGET/scripts/lib/kit-config.sh"
690
+ safe_copy "$KIT_ROOT/scripts/lib/gh-project.sh" "$TARGET/scripts/lib/gh-project.sh"
691
+ safe_copy "$KIT_ROOT/scripts/lib/worktree-issue.sh" "$TARGET/scripts/lib/worktree-issue.sh"
692
+ safe_copy "$KIT_ROOT/scripts/lib/role-identity.sh" "$TARGET/scripts/lib/role-identity.sh"
693
+ safe_copy "$KIT_ROOT/scripts/lib/kit-local.sh" "$TARGET/scripts/lib/kit-local.sh"
694
+ safe_copy "$KIT_ROOT/scripts/lib/engine-adapter.sh" "$TARGET/scripts/lib/engine-adapter.sh"
695
+ safe_copy "$KIT_ROOT/scripts/lib/effort-metrics.sh" "$TARGET/scripts/lib/effort-metrics.sh"
696
+ safe_copy "$KIT_ROOT/scripts/lib/gh-log.sh" "$TARGET/scripts/lib/gh-log.sh"
697
+ safe_copy "$KIT_ROOT/scripts/lib/kit-events.sh" "$TARGET/scripts/lib/kit-events.sh"
698
+ safe_copy "$KIT_ROOT/scripts/lib/worktree-start.sh" "$TARGET/scripts/lib/worktree-start.sh"
699
+ safe_copy "$KIT_ROOT/scripts/kit-version-check.sh" "$TARGET/scripts/kit-version-check.sh" exec
700
+ for sc in setup-labels.sh setup-milestones.sh capture-project-ids.sh task-sync.sh knowledge-lint.sh; do
701
+ safe_copy "$KIT_ROOT/scripts/$sc" "$TARGET/scripts/$sc" exec
702
+ done
703
+ echo " + scripts/ (libs + setup + task-sync + version-check + knowledge-lint)"
704
+
705
+ # ---- knowledge base (governed dir: INDEX manifest; upgrade-safe) ---------
706
+ mkdir -p "$TARGET/knowledge"
707
+ safe_write "$KIT_ROOT/templates/knowledge-INDEX.md.tmpl" "$TARGET/knowledge/INDEX.md"
708
+ echo " + knowledge/INDEX.md (manifest — see .claude/rules/knowledge-base.md)"
709
+
710
+ # ---- .claude/lib (kit-sigil attribution helper; all projects; idempotent) ----
711
+ # Sourced by the task-* skill footers + repo-hygiene to emit one "claude-kit"
712
+ # attribution sigil per kit-action boundary (see knowledge/brand.md §6).
713
+ mkdir -p "$TARGET/.claude/lib"
714
+ safe_write "$KIT_ROOT/templates/lib/kit-sigil.sh.tmpl" "$TARGET/.claude/lib/kit-sigil.sh"
715
+ chmod +x "$TARGET/.claude/lib/kit-sigil.sh" 2>/dev/null || true
716
+ echo " + .claude/lib/kit-sigil.sh"
717
+
718
+ # ---- settings.local.json (+ hooks, additive) ----------------------------
719
+ mkdir -p "$TARGET/.claude"
720
+ safe_write "$KIT_ROOT/templates/settings/settings.local.json.tmpl" "$TARGET/.claude/settings.local.json"
721
+ SETTINGS="$TARGET/.claude/settings.local.json"
722
+ merge_settings() { jq "$@" "$SETTINGS" > "$SETTINGS.tmp" && mv "$SETTINGS.tmp" "$SETTINGS"; }
723
+ HOOK_NOTE=""
724
+
725
+ if [[ "$MEMORY_BOOL" == "true" && $UPGRADE -eq 0 ]]; then
726
+ mkdir -p "$TARGET/.claude/hooks"
727
+ emit "$KIT_ROOT/templates/hooks/mempal_session_start.sh.tmpl" "$TARGET/.claude/hooks/mempal_session_start.sh"
728
+ emit "$KIT_ROOT/templates/hooks/mempal_save.sh.tmpl" "$TARGET/.claude/hooks/mempal_save.sh"
729
+ emit "$KIT_ROOT/templates/hooks/mempal_precompact.sh.tmpl" "$TARGET/.claude/hooks/mempal_precompact.sh"
730
+ emit "$KIT_ROOT/templates/hooks/mempal_followup.sh.tmpl" "$TARGET/.claude/hooks/mempal_followup.sh"
731
+ chmod +x "$TARGET/.claude/hooks/mempal_session_start.sh" "$TARGET/.claude/hooks/mempal_save.sh" "$TARGET/.claude/hooks/mempal_precompact.sh" "$TARGET/.claude/hooks/mempal_followup.sh"
732
+ merge_settings --arg start "$TARGET/.claude/hooks/mempal_session_start.sh" --arg save "$TARGET/.claude/hooks/mempal_save.sh" --arg pre "$TARGET/.claude/hooks/mempal_precompact.sh" --arg fup "$TARGET/.claude/hooks/mempal_followup.sh" \
733
+ '.hooks.SessionStart = [{matcher:"",hooks:[{type:"command",command:$start,timeout:30}]}]
734
+ | .hooks.Stop = [{matcher:"",hooks:[{type:"command",command:$save,timeout:30}]}]
735
+ | .hooks.PreCompact = [{matcher:"",hooks:[{type:"command",command:$pre,timeout:30}]}]
736
+ | .hooks.SessionEnd = [{matcher:"",hooks:[{type:"command",command:$fup,timeout:30}]}]'
737
+ HOOK_NOTE="$HOOK_NOTE + MemPalace"
738
+ fi
739
+
740
+ if [[ "$PREPUSH_ENABLED" == "true" && $UPGRADE -eq 0 ]]; then
741
+ mkdir -p "$TARGET/.claude/hooks"
742
+ emit "$KIT_ROOT/templates/hooks/prepush_gate.sh.tmpl" "$TARGET/.claude/hooks/prepush_gate.sh"
743
+ chmod +x "$TARGET/.claude/hooks/prepush_gate.sh"
744
+ merge_settings --arg gate "$TARGET/.claude/hooks/prepush_gate.sh" \
745
+ '.hooks.PreToolUse = [{matcher:"Bash",hooks:[{type:"command",command:$gate,timeout:120}]}]'
746
+ HOOK_NOTE="$HOOK_NOTE + pre-push gate"
747
+ fi
748
+
749
+ # ---- local model layer SessionStart hook (opt-in; idempotent, also on --upgrade) ----
750
+ if [[ "$LOCAL_ON" == "true" ]]; then
751
+ mkdir -p "$TARGET/.claude/hooks"
752
+ safe_write "$KIT_ROOT/templates/hooks/kit-local-status.sh.tmpl" "$TARGET/.claude/hooks/kit-local-status.sh"
753
+ chmod +x "$TARGET/.claude/hooks/kit-local-status.sh" 2>/dev/null || true
754
+ merge_settings --arg ls "$TARGET/.claude/hooks/kit-local-status.sh" \
755
+ '.hooks.SessionStart = (((.hooks.SessionStart // []) | map(select((.hooks[0].command // "") != $ls))) + [{matcher:"",hooks:[{type:"command",command:$ls,timeout:10}]}])'
756
+ HOOK_NOTE="$HOOK_NOTE + local-status"
757
+ fi
758
+
759
+ # ---- repo-hygiene SessionStart hook (all projects; read-only; idempotent) ----
760
+ mkdir -p "$TARGET/.claude/hooks"
761
+ safe_write "$KIT_ROOT/templates/hooks/repo-hygiene.sh.tmpl" "$TARGET/.claude/hooks/repo-hygiene.sh"
762
+ chmod +x "$TARGET/.claude/hooks/repo-hygiene.sh" 2>/dev/null || true
763
+ merge_settings --arg rh "$TARGET/.claude/hooks/repo-hygiene.sh" \
764
+ '.hooks.SessionStart = (((.hooks.SessionStart // []) | map(select((.hooks[0].command // "") != $rh))) + [{matcher:"",hooks:[{type:"command",command:$rh,timeout:10}]}])'
765
+ HOOK_NOTE="$HOOK_NOTE + repo-hygiene"
766
+
767
+ # ---- base-branch commit guard PreToolUse(Bash) hook (all projects; idempotent) ----
768
+ mkdir -p "$TARGET/.claude/hooks"
769
+ safe_write "$KIT_ROOT/templates/hooks/guard-base-branch-commit.sh.tmpl" "$TARGET/.claude/hooks/guard-base-branch-commit.sh"
770
+ chmod +x "$TARGET/.claude/hooks/guard-base-branch-commit.sh" 2>/dev/null || true
771
+ merge_settings --arg gd "$TARGET/.claude/hooks/guard-base-branch-commit.sh" \
772
+ '.hooks.PreToolUse = (((.hooks.PreToolUse // []) | map(select((.hooks[0].command // "") != $gd))) + [{matcher:"Bash",hooks:[{type:"command",command:$gd,timeout:10}]}])'
773
+ HOOK_NOTE="$HOOK_NOTE + commit-guard"
774
+
775
+ # ---- version-check SessionStart hook (all projects; idempotent) ---------
776
+ mkdir -p "$TARGET/.claude/hooks"
777
+ safe_write "$KIT_ROOT/templates/hooks/kit_version_check.sh.tmpl" "$TARGET/.claude/hooks/kit_version_check.sh"
778
+ chmod +x "$TARGET/.claude/hooks/kit_version_check.sh" 2>/dev/null || true
779
+ merge_settings --arg vc "$TARGET/.claude/hooks/kit_version_check.sh" \
780
+ '.hooks.SessionStart = (((.hooks.SessionStart // []) | map(select((.hooks[0].command // "") != $vc))) + [{matcher:"",hooks:[{type:"command",command:$vc,timeout:10}]}])'
781
+ HOOK_NOTE="$HOOK_NOTE + version-check"
782
+
783
+ # ---- recommended .gitignore entries (idempotent) ------------------------
784
+ # kit.manifest.json is kit-managed bookkeeping (content hashes + timestamps, re-stamped on every
785
+ # wire) — tracking it would dirty the tree on every session/upgrade, the exact churn #334 removes.
786
+ GI="$TARGET/.gitignore"
787
+ for _ig in ".claude/settings.local.json" ".claude/kit.manifest.json" ".kann/"; do
788
+ if [[ -f "$GI" ]]; then grep -qxF "$_ig" "$GI" || printf '%s\n' "$_ig" >> "$GI"; else printf '%s\n' "$_ig" > "$GI"; fi
789
+ done
790
+
791
+ echo " + .claude/settings.local.json${HOOK_NOTE:+ (}${HOOK_NOTE# + }${HOOK_NOTE:+)}"
792
+
793
+ # ---- wire (converge) -----------------------------------------------------
794
+ # Runs on BOTH scaffold and --upgrade so every update re-wires (fixes the class of bug where
795
+ # /kit-update refreshed files but never re-ran the wiring → statusline/settings drifted). #369
796
+ if [[ -f "$KIT_ROOT/scripts/kit-wire.sh" ]]; then
797
+ if ( cd "$TARGET" && CLAUDE_PLUGIN_ROOT="$KIT_ROOT" KIT_ASSUME_YES=1 bash "$KIT_ROOT/scripts/kit-wire.sh" >/dev/null 2>&1 ); then
798
+ echo " + wired: statusline shim + settings.statusLine (kit-wire)"
799
+ fi
800
+ fi
801
+
802
+ # ---- summary -------------------------------------------------------------
803
+ echo ""
804
+ echo "claude-kit $([[ $UPGRADE -eq 1 ]] && echo upgraded || echo scaffolded) — profile: $PROFILE"
805
+ echo " Project : $NAME ($SLUG)"
806
+ echo " Repo : $REPO"
807
+ echo " Board : $([[ "$PROJECTS_V2" == "true" ]] && echo "Projects v2 #$PROJECT_NUMBER" || echo "off (gh issues only)")"
808
+ echo " Agents : $(echo $AGENTS | tr '\n' ' ')"
809
+ echo " Memory : $MEMORY_BOOL Plans: $PLANS_FORMAT Lang: $LANG_PREF kitVersion: $PLUGIN_VERSION"
810
+ echo " Spec Kit: $SPECKIT_ON Pre-push gate: $PREPUSH_ENABLED Local model: $LOCAL_ON Stack skills self-gate by package.json"
811
+
812
+ if [[ $UPGRADE -eq 1 ]]; then
813
+ echo ""
814
+ echo " Upgrade -> kitVersion $PLUGIN_VERSION"
815
+ if [[ ${#UPGRADE_ADDED[@]} -gt 0 ]]; then echo " Added (new in this kit version):"; printf ' + %s\n' "${UPGRADE_ADDED[@]}"; fi
816
+ if [[ ${#UPGRADE_PRESERVED[@]} -gt 0 ]]; then echo " Preserved (your edits, untouched):"; printf ' = %s\n' "${UPGRADE_PRESERVED[@]}"; fi
817
+ if [[ ${#UPGRADE_SKIPPED[@]} -gt 0 ]]; then echo " Skipped (removed/renamed via kit.config upgrade):"; printf ' - %s\n' "${UPGRADE_SKIPPED[@]}"; fi
818
+ else
819
+ echo ""
820
+ echo "Next steps:"
821
+ echo " 1. Review CLAUDE.md and .claude/kit.config.json"
822
+ echo " 2. ./scripts/setup-labels.sh && ./scripts/setup-milestones.sh # seed the repo"
823
+ [[ "$PROJECTS_V2" == "true" ]] && echo " 3. ./scripts/capture-project-ids.sh # cache Projects v2 field IDs"
824
+ [[ "$LOCAL_ON" == "true" ]] && echo " 4. /kit-doctor # local model layer: installs mlx-lm (uv tool install) + starts mlx_lm.server"
825
+ fi
826
+
827
+ exit 0