@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
package/scripts/init.sh
ADDED
|
@@ -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
|