@jeiemgi/cckit 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +22 -0
- package/AGENTS.md +101 -0
- package/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/README.md +143 -0
- package/SECURITY.md +22 -0
- package/bin/cckit +215 -0
- package/cckit.config.json +34 -0
- package/commands/kit-add.md +42 -0
- package/commands/kit-docs.md +45 -0
- package/commands/kit-doctor.md +52 -0
- package/commands/kit-export-project.md +58 -0
- package/commands/kit-export-training.md +49 -0
- package/commands/kit-init.md +126 -0
- package/commands/kit-routines.md +59 -0
- package/commands/kit-update.md +132 -0
- package/docs/kit-annotate/01-explainer.html +225 -0
- package/docs/kit-annotate/02-implementation-plan.html +196 -0
- package/docs/media/.onboarding-capture.cast +5 -0
- package/docs/media/README.md +43 -0
- package/docs/media/build-demo.sh +63 -0
- package/docs/media/build-kit-init.sh +51 -0
- package/docs/media/build-onboarding.sh +51 -0
- package/docs/media/kit-dry-run.cast +107 -0
- package/docs/media/kit-dry-run.gif +0 -0
- package/docs/media/kit-init.cast +56 -0
- package/docs/media/kit-init.gif +0 -0
- package/docs/media/kit-onboarding.cast +148 -0
- package/docs/media/kit-onboarding.gif +0 -0
- package/githooks/pre-commit +18 -0
- package/kit.config.schema.json +105 -0
- package/package.json +54 -0
- package/privacy-denylist.example +8 -0
- package/profiles/automation.json +36 -0
- package/profiles/content.json +41 -0
- package/profiles/minimal.json +31 -0
- package/profiles/research.json +37 -0
- package/profiles/software.json +32 -0
- package/scripts/annotate-setup.sh +149 -0
- package/scripts/autopilot.sh +50 -0
- package/scripts/capture-project-ids.sh +53 -0
- package/scripts/check.sh +66 -0
- package/scripts/contribute.sh +48 -0
- package/scripts/debug.sh +54 -0
- package/scripts/init-upgrade-test.sh +99 -0
- package/scripts/init.sh +827 -0
- package/scripts/install.sh +24 -0
- package/scripts/kit-add-test.sh +62 -0
- package/scripts/kit-add.sh +115 -0
- package/scripts/kit-adopt-test.sh +61 -0
- package/scripts/kit-adopt.sh +122 -0
- package/scripts/kit-bump-version.sh +79 -0
- package/scripts/kit-digest.sh +126 -0
- package/scripts/kit-doctor.sh +663 -0
- package/scripts/kit-export-project-test.sh +82 -0
- package/scripts/kit-export-project.sh +245 -0
- package/scripts/kit-export-training-test.sh +51 -0
- package/scripts/kit-export-training.sh +175 -0
- package/scripts/kit-migrate-test.sh +80 -0
- package/scripts/kit-migrate.sh +190 -0
- package/scripts/kit-onboard-test.sh +63 -0
- package/scripts/kit-onboard.sh +69 -0
- package/scripts/kit-promote-test.sh +54 -0
- package/scripts/kit-promote.sh +102 -0
- package/scripts/kit-remove-test.sh +61 -0
- package/scripts/kit-remove.sh +84 -0
- package/scripts/kit-routines.sh +322 -0
- package/scripts/kit-version-check.sh +91 -0
- package/scripts/kit-wire-test.sh +54 -0
- package/scripts/kit-wire.sh +132 -0
- package/scripts/knowledge-lint.sh +96 -0
- package/scripts/lib/cckit-output.sh +36 -0
- package/scripts/lib/effort-metrics.sh +452 -0
- package/scripts/lib/effort-ops-test.sh +83 -0
- package/scripts/lib/effort-ops.sh +132 -0
- package/scripts/lib/effort-plan.sh +104 -0
- package/scripts/lib/effort.sh +191 -0
- package/scripts/lib/engine-adapter.sh +92 -0
- package/scripts/lib/gh-log.sh +58 -0
- package/scripts/lib/gh-project.sh +212 -0
- package/scripts/lib/handoff.sh +35 -0
- package/scripts/lib/kit-cli-test.sh +42 -0
- package/scripts/lib/kit-cli.sh +32 -0
- package/scripts/lib/kit-config-resolve.sh +145 -0
- package/scripts/lib/kit-config.sh +88 -0
- package/scripts/lib/kit-engine-test.sh +107 -0
- package/scripts/lib/kit-events.sh +62 -0
- package/scripts/lib/kit-gc.sh +117 -0
- package/scripts/lib/kit-interview-test.sh +77 -0
- package/scripts/lib/kit-interview.sh +203 -0
- package/scripts/lib/kit-local.sh +79 -0
- package/scripts/lib/kit-manifest.sh +127 -0
- package/scripts/lib/kit-mode-test.sh +49 -0
- package/scripts/lib/kit-mode.sh +67 -0
- package/scripts/lib/kit-operate.sh +105 -0
- package/scripts/lib/kit-profile-test.sh +62 -0
- package/scripts/lib/kit-profile.sh +115 -0
- package/scripts/lib/kit-task-ops-test.sh +63 -0
- package/scripts/lib/kit-task-ops.sh +341 -0
- package/scripts/lib/pr-evidence.sh +173 -0
- package/scripts/lib/project-scan.sh +16 -0
- package/scripts/lib/react-detect.sh +78 -0
- package/scripts/lib/role-identity.sh +47 -0
- package/scripts/lib/secret-guard.sh +96 -0
- package/scripts/lib/toon.sh +35 -0
- package/scripts/lib/ui.sh +42 -0
- package/scripts/lib/version-bump.sh +59 -0
- package/scripts/lib/worktree-issue-test.sh +45 -0
- package/scripts/lib/worktree-issue.sh +73 -0
- package/scripts/lib/worktree-start.sh +280 -0
- package/scripts/orchestrate.sh +160 -0
- package/scripts/portable-test.sh +53 -0
- package/scripts/publish.sh +94 -0
- package/scripts/setup-labels.sh +25 -0
- package/scripts/setup-milestones.sh +17 -0
- package/scripts/showcase.sh +64 -0
- package/scripts/status.sh +44 -0
- package/scripts/task-sync.sh +59 -0
- package/scripts/test.sh +48 -0
- package/scripts/web-install.sh +22 -0
- package/skills/kit-annotate/SKILL.md +107 -0
- package/skills/kit-autopilot/SKILL.md +108 -0
- package/skills/kit-contribute/SKILL.md +134 -0
- package/skills/kit-customize/SKILL.md +134 -0
- package/skills/kit-dev/SKILL.md +67 -0
- package/skills/kit-digest/SKILL.md +41 -0
- package/skills/kit-effort-close/SKILL.md +156 -0
- package/skills/kit-effort-new/SKILL.md +173 -0
- package/skills/kit-effort-pr/SKILL.md +139 -0
- package/skills/kit-effort-start/SKILL.md +85 -0
- package/skills/kit-gc/SKILL.md +80 -0
- package/skills/kit-onboard/SKILL.md +50 -0
- package/skills/kit-security-sweep/SKILL.md +57 -0
- package/skills/kit-ship/SKILL.md +43 -0
- package/skills/kit-task-close/SKILL.md +66 -0
- package/skills/kit-task-new/SKILL.md +51 -0
- package/skills/kit-task-pr/SKILL.md +43 -0
- package/skills/kit-task-pr-auto/SKILL.md +27 -0
- package/skills/kit-task-pr-merge/SKILL.md +53 -0
- package/skills/kit-task-start/SKILL.md +76 -0
- package/skills/kit-task-sync/SKILL.md +37 -0
- package/templates/CLAUDE.md.tmpl +106 -0
- package/templates/agents/analyst.md +55 -0
- package/templates/agents/auto-dev.md +93 -0
- package/templates/agents/backend.md +59 -0
- package/templates/agents/designer.md +73 -0
- package/templates/agents/devops.md +57 -0
- package/templates/agents/editor.md +48 -0
- package/templates/agents/frontend.md +81 -0
- package/templates/agents/generalist.md +46 -0
- package/templates/agents/local-delegate.md +70 -0
- package/templates/agents/n8n.md +65 -0
- package/templates/agents/pm.md +69 -0
- package/templates/agents/qa.md +66 -0
- package/templates/agents/researcher.md +57 -0
- package/templates/agents/security.md +65 -0
- package/templates/agents/tech-lead.md +75 -0
- package/templates/hooks/guard-base-branch-commit.sh.tmpl +45 -0
- package/templates/hooks/kit-local-status.sh.tmpl +34 -0
- package/templates/hooks/kit_version_check.sh.tmpl +6 -0
- package/templates/hooks/mempal_followup.sh.tmpl +97 -0
- package/templates/hooks/mempal_precompact.sh.tmpl +4 -0
- package/templates/hooks/mempal_save.sh.tmpl +4 -0
- package/templates/hooks/mempal_session_start.sh.tmpl +8 -0
- package/templates/hooks/prepush_gate.sh.tmpl +36 -0
- package/templates/hooks/repo-hygiene.sh.tmpl +72 -0
- package/templates/kit.config.json.tmpl +32 -0
- package/templates/knowledge-INDEX.md.tmpl +12 -0
- package/templates/lib/kit-sigil.sh.tmpl +124 -0
- package/templates/rules/branch-naming.md +104 -0
- package/templates/rules/communication-style.md +22 -0
- package/templates/rules/delegation-brief.md +40 -0
- package/templates/rules/design-routing.md +35 -0
- package/templates/rules/effort-model.md +122 -0
- package/templates/rules/knowledge-base.md +41 -0
- package/templates/rules/mempalace.md +110 -0
- package/templates/rules/plan-output-format.md +58 -0
- package/templates/rules/react-annotate.md +69 -0
- package/templates/rules/risk-tiered-review.md +62 -0
- package/templates/rules/skill-gaps.md +48 -0
- package/templates/rules/task-management.md +42 -0
- package/templates/settings/settings.local.json.tmpl +27 -0
- package/templates/skills/NAMESPACED +13 -0
- package/templates/skills/copywriting/SKILL.md +252 -0
- package/templates/skills/copywriting/references/copy-frameworks.md +344 -0
- package/templates/skills/copywriting/references/natural-transitions.md +272 -0
- package/templates/skills/feature-build-refine/SKILL.md +367 -0
- package/templates/skills/karpathy-guidelines/SKILL.md +69 -0
- package/templates/skills/morning-briefing/SKILL.md +46 -0
- package/templates/skills/speckit/SKILL.md +239 -0
- package/templates/skills/supabase-patterns/SKILL.md +88 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-routines.sh — suggested-routines cron catalogue: accept / remove / list / verify (#377).
|
|
3
|
+
#
|
|
4
|
+
# THE PHILOSOPHY: the kit SUGGESTS routines; the user accepts; nothing is created without opt-in.
|
|
5
|
+
# Accepted routines are stored in kit.config.json (.routines[]) and wired as OS cron jobs
|
|
6
|
+
# (crontab entries) scoped to the project directory. Each entry is tagged with the kit's sentinel
|
|
7
|
+
# comment so it can be identified and removed cleanly. The script NEVER auto-accepts anything.
|
|
8
|
+
#
|
|
9
|
+
# Operations:
|
|
10
|
+
# kit-routines.sh list # show the catalogue with status (accepted / available)
|
|
11
|
+
# kit-routines.sh accept <id> [--dir D] # suggest -> add cron + record in kit.config.json
|
|
12
|
+
# kit-routines.sh remove <id> [--dir D] # remove cron + clear from kit.config.json
|
|
13
|
+
# kit-routines.sh verify [--dir D] # check: config routines have matching cron entries
|
|
14
|
+
# kit-routines.sh remove-all [--dir D] # remove every kit-managed cron for this project (used by kit-remove)
|
|
15
|
+
# KIT_DRY_RUN=1 kit-routines.sh ... # preview any write op, write nothing
|
|
16
|
+
# KIT_ASSUME_YES=1 kit-routines.sh ... # non-interactive (CI / batch)
|
|
17
|
+
#
|
|
18
|
+
# Cron sentinel format — one line per routine:
|
|
19
|
+
# # kit-managed:<project_slug>:<routine_id> <cron_expr> <command>
|
|
20
|
+
# This makes grep-based remove exact and never matches unrelated crontab lines.
|
|
21
|
+
#
|
|
22
|
+
# Requires: jq (catalogue parse + config patch), crontab (OS-level cron).
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
_rt_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
26
|
+
# shellcheck source=/dev/null
|
|
27
|
+
. "$_rt_dir/lib/kit-cli.sh" # kit_is_main, kit_say, kit_warn, kit_die
|
|
28
|
+
|
|
29
|
+
# Plugin root — prefer CLAUDE_PLUGIN_ROOT, fall back to two levels up.
|
|
30
|
+
_kit_rt_plugin_root() {
|
|
31
|
+
if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then printf '%s\n' "$CLAUDE_PLUGIN_ROOT"; return 0; fi
|
|
32
|
+
( CDPATH='' cd -- "$_rt_dir/.." && pwd )
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_kit_rt_catalogue() {
|
|
36
|
+
local f; f="$(_kit_rt_plugin_root)/routines/catalogue.json"
|
|
37
|
+
[ -f "$f" ] || kit_die "routines catalogue not found: $f"
|
|
38
|
+
cat "$f"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_kit_rt_dry() { [ -n "${KIT_DRY_RUN:-}" ]; }
|
|
42
|
+
_kit_rt_yes() { [ -n "${KIT_ASSUME_YES:-}" ]; }
|
|
43
|
+
_kit_rt_say() { printf '%s\n' "$*" >&2; }
|
|
44
|
+
|
|
45
|
+
# ── project-slug helper ─────────────────────────────────────────────────────
|
|
46
|
+
# Derive a stable slug for cron sentinel tagging: use kit.config.json project.slug,
|
|
47
|
+
# fall back to the current directory name.
|
|
48
|
+
_kit_rt_slug() {
|
|
49
|
+
local dir="${1:-$PWD}" slug
|
|
50
|
+
slug="$(jq -r '.project.slug // empty' "$dir/.claude/kit.config.json" 2>/dev/null || true)"
|
|
51
|
+
[ -n "$slug" ] && { printf '%s\n' "$slug"; return 0; }
|
|
52
|
+
basename "$dir"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# ── catalogue helpers ────────────────────────────────────────────────────────
|
|
56
|
+
# kit_routines_get_by_id <id> -> JSON object or empty
|
|
57
|
+
kit_routines_get_by_id() {
|
|
58
|
+
_kit_rt_catalogue | jq --arg id "$1" '.routines[] | select(.id==$id)'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# kit_routines_accepted <dir> -> space-separated list of accepted routine ids from kit.config.json
|
|
62
|
+
kit_routines_accepted() {
|
|
63
|
+
local dir="${1:-$PWD}" cfg
|
|
64
|
+
cfg="$dir/.claude/kit.config.json"
|
|
65
|
+
[ -f "$cfg" ] || { printf ''; return 0; }
|
|
66
|
+
jq -r '(.routines // [])[]' "$cfg" 2>/dev/null || true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# ── crontab helpers ──────────────────────────────────────────────────────────
|
|
70
|
+
# Sentinel comment identifies a kit-managed line: "# kit-managed:<slug>:<id>"
|
|
71
|
+
_kit_rt_sentinel() { printf '# kit-managed:%s:%s' "$1" "$2"; }
|
|
72
|
+
|
|
73
|
+
# _kit_rt_cron_line <sentinel> <cron_expr> <command_expanded> -> the full crontab line pair
|
|
74
|
+
_kit_rt_cron_line() {
|
|
75
|
+
local sentinel="$1" expr="$2" cmd="$3"
|
|
76
|
+
printf '%s\n%s %s\n' "$sentinel" "$expr" "$cmd"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# _kit_rt_expand_command <command_template> <scripts_dir> -> expanded command string
|
|
80
|
+
_kit_rt_expand_command() {
|
|
81
|
+
local tmpl="$1" scripts="$2"
|
|
82
|
+
printf '%s' "$tmpl" | sed "s|\${KIT_SCRIPTS}|$scripts|g"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# kit_routines_cron_has <sentinel> -> rc 0 if present in crontab
|
|
86
|
+
kit_routines_cron_has() {
|
|
87
|
+
local sentinel="$1"
|
|
88
|
+
crontab -l 2>/dev/null | grep -qF "$sentinel"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# kit_routines_cron_add <sentinel> <cron_line_pair>
|
|
92
|
+
# Appends the two-line pair (sentinel + cron) to the crontab if not already present.
|
|
93
|
+
kit_routines_cron_add() {
|
|
94
|
+
local sentinel="$1" pair="$2" tmp
|
|
95
|
+
if kit_routines_cron_has "$sentinel"; then
|
|
96
|
+
_kit_rt_say " = cron already present ($sentinel)"; return 0
|
|
97
|
+
fi
|
|
98
|
+
if _kit_rt_dry; then
|
|
99
|
+
_kit_rt_say " [dry-run] would add cron: $sentinel"; return 0
|
|
100
|
+
fi
|
|
101
|
+
tmp="$(mktemp)"
|
|
102
|
+
{ crontab -l 2>/dev/null; printf '\n%s\n' "$pair"; } > "$tmp" || { rm -f "$tmp"; return 1; }
|
|
103
|
+
crontab "$tmp"; local rc=$?; rm -f "$tmp"
|
|
104
|
+
[ "$rc" -eq 0 ] && _kit_rt_say " + cron added: $sentinel" || { _kit_rt_say " error: crontab write failed"; return 1; }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# kit_routines_cron_remove <sentinel>
|
|
108
|
+
# Removes every line whose PRECEDING line is the sentinel comment (the comment + cron line pair).
|
|
109
|
+
kit_routines_cron_remove() {
|
|
110
|
+
local sentinel="$1" tmp
|
|
111
|
+
if ! kit_routines_cron_has "$sentinel"; then
|
|
112
|
+
_kit_rt_say " = cron not found (already removed): $sentinel"; return 0
|
|
113
|
+
fi
|
|
114
|
+
if _kit_rt_dry; then
|
|
115
|
+
_kit_rt_say " [dry-run] would remove cron: $sentinel"; return 0
|
|
116
|
+
fi
|
|
117
|
+
tmp="$(mktemp)"
|
|
118
|
+
# Strip the sentinel line AND the following non-empty cron line.
|
|
119
|
+
crontab -l 2>/dev/null | awk -v s="$sentinel" '
|
|
120
|
+
/^[[:space:]]*$/ { print; next }
|
|
121
|
+
$0 == s { skip=1; next }
|
|
122
|
+
skip { skip=0; next }
|
|
123
|
+
{ print }
|
|
124
|
+
' > "$tmp" || { rm -f "$tmp"; return 1; }
|
|
125
|
+
crontab "$tmp"; local rc=$?; rm -f "$tmp"
|
|
126
|
+
[ "$rc" -eq 0 ] && _kit_rt_say " - cron removed: $sentinel" || { _kit_rt_say " error: crontab write failed"; return 1; }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# ── config helpers ───────────────────────────────────────────────────────────
|
|
130
|
+
# kit_routines_config_add <id> <dir>
|
|
131
|
+
# Adds the routine id to kit.config.json .routines[] (idempotent).
|
|
132
|
+
kit_routines_config_add() {
|
|
133
|
+
local id="$1" dir="${2:-$PWD}" cfg tmp
|
|
134
|
+
cfg="$dir/.claude/kit.config.json"
|
|
135
|
+
_kit_rt_dry && { _kit_rt_say " [dry-run] would add '$id' to $cfg .routines[]"; return 0; }
|
|
136
|
+
[ -f "$cfg" ] || printf '{}\n' > "$cfg"
|
|
137
|
+
tmp="$(mktemp)"
|
|
138
|
+
jq --arg id "$id" '.routines = ((.routines // []) + [$id] | unique)' "$cfg" > "$tmp" \
|
|
139
|
+
&& mv "$tmp" "$cfg" || { rm -f "$tmp"; return 1; }
|
|
140
|
+
_kit_rt_say " + recorded '$id' in $cfg"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# kit_routines_config_remove <id> <dir>
|
|
144
|
+
kit_routines_config_remove() {
|
|
145
|
+
local id="$1" dir="${2:-$PWD}" cfg tmp
|
|
146
|
+
cfg="$dir/.claude/kit.config.json"
|
|
147
|
+
[ -f "$cfg" ] || return 0
|
|
148
|
+
_kit_rt_dry && { _kit_rt_say " [dry-run] would remove '$id' from $cfg .routines[]"; return 0; }
|
|
149
|
+
tmp="$(mktemp)"
|
|
150
|
+
jq --arg id "$id" '.routines = ((.routines // []) | map(select(. != $id)))' "$cfg" > "$tmp" \
|
|
151
|
+
&& mv "$tmp" "$cfg" || { rm -f "$tmp"; return 1; }
|
|
152
|
+
_kit_rt_say " - removed '$id' from $cfg"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# ── public operations ────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
# kit_routines_list [dir] — print the catalogue table with acceptance status.
|
|
158
|
+
kit_routines_list() {
|
|
159
|
+
local dir="${1:-$PWD}"
|
|
160
|
+
command -v jq >/dev/null 2>&1 || kit_die "jq required"
|
|
161
|
+
local cat accepted
|
|
162
|
+
cat="$(_kit_rt_catalogue)"
|
|
163
|
+
accepted="$(kit_routines_accepted "$dir")"
|
|
164
|
+
|
|
165
|
+
printf '%-22s %-8s %-8s %-9s %s\n' "ID" "CADENCE" "STATUS" "COST" "DESCRIPTION" >&2
|
|
166
|
+
printf '%s\n' "-----------------------------------------------------------------------" >&2
|
|
167
|
+
|
|
168
|
+
local n; n="$(printf '%s' "$cat" | jq '.routines | length')"
|
|
169
|
+
local i=0
|
|
170
|
+
while [ "$i" -lt "$n" ]; do
|
|
171
|
+
local id cad cost desc status
|
|
172
|
+
id="$(printf '%s' "$cat" | jq -r ".routines[$i].id")"
|
|
173
|
+
cad="$(printf '%s' "$cat" | jq -r ".routines[$i].cadence")"
|
|
174
|
+
cost="$(printf '%s' "$cat" | jq -r ".routines[$i].cost")"
|
|
175
|
+
desc="$(printf '%s' "$cat" | jq -r ".routines[$i].description" | cut -c1-50)"
|
|
176
|
+
if printf '%s\n' "$accepted" | grep -qx "$id" 2>/dev/null; then
|
|
177
|
+
status="accepted"
|
|
178
|
+
else
|
|
179
|
+
status="available"
|
|
180
|
+
fi
|
|
181
|
+
printf '%-22s %-8s %-8s %-9s %s\n' "$id" "$cad" "$status" "$cost" "$desc" >&2
|
|
182
|
+
i=$((i+1))
|
|
183
|
+
done
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
# kit_routines_accept <id> [dir]
|
|
187
|
+
# Opt-in: look up the routine in the catalogue, add cron entry, record in config.
|
|
188
|
+
kit_routines_accept() {
|
|
189
|
+
local id="$1" dir="${2:-$PWD}"
|
|
190
|
+
command -v jq >/dev/null 2>&1 || kit_die "jq required"
|
|
191
|
+
command -v crontab >/dev/null 2>&1 || kit_die "crontab required"
|
|
192
|
+
|
|
193
|
+
dir="$(cd "$dir" 2>/dev/null && pwd)" || kit_die "no such dir: $dir"
|
|
194
|
+
|
|
195
|
+
local entry; entry="$(kit_routines_get_by_id "$id")"
|
|
196
|
+
[ -n "$entry" ] || kit_die "unknown routine '$id' (run: kit-routines.sh list)"
|
|
197
|
+
|
|
198
|
+
local scripts; scripts="$(cd "$_rt_dir" && pwd)"
|
|
199
|
+
local cron_expr cmd_tmpl cmd_expanded sentinel pair slug
|
|
200
|
+
cron_expr="$(printf '%s' "$entry" | jq -r '.cron')"
|
|
201
|
+
cmd_tmpl="$(printf '%s' "$entry" | jq -r '.command')"
|
|
202
|
+
cmd_expanded="$(_kit_rt_expand_command "$cmd_tmpl" "$scripts")"
|
|
203
|
+
slug="$(_kit_rt_slug "$dir")"
|
|
204
|
+
sentinel="$(_kit_rt_sentinel "$slug" "$id")"
|
|
205
|
+
pair="$(_kit_rt_cron_line "$sentinel" "$cron_expr" "$cmd_expanded")"
|
|
206
|
+
|
|
207
|
+
# One confirm unless non-interactive.
|
|
208
|
+
if ! _kit_rt_dry && ! _kit_rt_yes; then
|
|
209
|
+
local name; name="$(printf '%s' "$entry" | jq -r '.name')"
|
|
210
|
+
printf 'Accept routine "%s" (%s, %s)? [y/N] ' "$name" "$id" "$cron_expr" >&2
|
|
211
|
+
local reply=""; IFS= read -r reply || true
|
|
212
|
+
case "$reply" in y|Y|yes|YES) ;; *) _kit_rt_say " skipped."; return 0;; esac
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
kit_routines_cron_add "$sentinel" "$pair" || return 1
|
|
216
|
+
kit_routines_config_add "$id" "$dir" || return 1
|
|
217
|
+
_kit_rt_say "routine '$id' accepted."
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# kit_routines_remove <id> [dir]
|
|
221
|
+
kit_routines_remove() {
|
|
222
|
+
local id="$1" dir="${2:-$PWD}"
|
|
223
|
+
command -v jq >/dev/null 2>&1 || kit_die "jq required"
|
|
224
|
+
command -v crontab >/dev/null 2>&1 || kit_die "crontab required"
|
|
225
|
+
|
|
226
|
+
dir="$(cd "$dir" 2>/dev/null && pwd)" || kit_die "no such dir: $dir"
|
|
227
|
+
local slug; slug="$(_kit_rt_slug "$dir")"
|
|
228
|
+
local sentinel; sentinel="$(_kit_rt_sentinel "$slug" "$id")"
|
|
229
|
+
|
|
230
|
+
kit_routines_cron_remove "$sentinel" || return 1
|
|
231
|
+
kit_routines_config_remove "$id" "$dir" || return 1
|
|
232
|
+
_kit_rt_say "routine '$id' removed."
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
# kit_routines_verify [dir]
|
|
236
|
+
# Check: every id in kit.config.json .routines[] has a matching crontab entry.
|
|
237
|
+
# rc 0 = in sync; rc 1 = drift (missing or orphan crons).
|
|
238
|
+
kit_routines_verify() {
|
|
239
|
+
local dir="${1:-$PWD}"
|
|
240
|
+
command -v jq >/dev/null 2>&1 || kit_die "jq required"
|
|
241
|
+
|
|
242
|
+
dir="$(cd "$dir" 2>/dev/null && pwd)" || kit_die "no such dir: $dir"
|
|
243
|
+
local accepted; accepted="$(kit_routines_accepted "$dir")"
|
|
244
|
+
local slug; slug="$(_kit_rt_slug "$dir")"
|
|
245
|
+
|
|
246
|
+
if [ -z "$accepted" ]; then
|
|
247
|
+
_kit_rt_say "kit-routines verify: no routines accepted — nothing to check."
|
|
248
|
+
return 0
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
local drift=0 id sentinel
|
|
252
|
+
while IFS= read -r id; do
|
|
253
|
+
[ -z "$id" ] && continue
|
|
254
|
+
sentinel="$(_kit_rt_sentinel "$slug" "$id")"
|
|
255
|
+
if ! kit_routines_cron_has "$sentinel"; then
|
|
256
|
+
_kit_rt_say " ! cron missing for routine '$id' (sentinel: $sentinel)"
|
|
257
|
+
drift=1
|
|
258
|
+
else
|
|
259
|
+
_kit_rt_say " = cron present: $id"
|
|
260
|
+
fi
|
|
261
|
+
done <<EOF
|
|
262
|
+
$accepted
|
|
263
|
+
EOF
|
|
264
|
+
|
|
265
|
+
if [ "$drift" -eq 1 ]; then
|
|
266
|
+
_kit_rt_say "kit-routines verify: drift detected — run 'kit-routines.sh accept <id>' to repair."
|
|
267
|
+
return 1
|
|
268
|
+
fi
|
|
269
|
+
_kit_rt_say "kit-routines verify: all crons in sync."
|
|
270
|
+
return 0
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# kit_routines_remove_all [dir]
|
|
274
|
+
# Remove every kit-managed cron for this project. Called by kit-remove on uninstall.
|
|
275
|
+
kit_routines_remove_all() {
|
|
276
|
+
local dir="${1:-$PWD}"
|
|
277
|
+
command -v jq >/dev/null 2>&1 || kit_die "jq required"
|
|
278
|
+
command -v crontab >/dev/null 2>&1 || { _kit_rt_say "kit-routines: crontab not found — skip cron cleanup."; return 0; }
|
|
279
|
+
|
|
280
|
+
dir="$(cd "$dir" 2>/dev/null && pwd)" || kit_die "no such dir: $dir"
|
|
281
|
+
local accepted; accepted="$(kit_routines_accepted "$dir")"
|
|
282
|
+
local slug; slug="$(_kit_rt_slug "$dir")"
|
|
283
|
+
|
|
284
|
+
if [ -z "$accepted" ]; then
|
|
285
|
+
_kit_rt_say "kit-routines remove-all: no routines to remove."; return 0
|
|
286
|
+
fi
|
|
287
|
+
|
|
288
|
+
local id
|
|
289
|
+
while IFS= read -r id; do
|
|
290
|
+
[ -z "$id" ] && continue
|
|
291
|
+
kit_routines_cron_remove "$(_kit_rt_sentinel "$slug" "$id")"
|
|
292
|
+
done <<EOF
|
|
293
|
+
$accepted
|
|
294
|
+
EOF
|
|
295
|
+
_kit_rt_say "kit-routines remove-all: done."
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
# ── CLI ──────────────────────────────────────────────────────────────────────
|
|
299
|
+
if kit_is_main; then
|
|
300
|
+
command -v jq >/dev/null 2>&1 || { echo "kit-routines: jq required" >&2; exit 1; }
|
|
301
|
+
_sub="${1:-}"; shift 2>/dev/null || true
|
|
302
|
+
_id=""; _dir="$PWD"
|
|
303
|
+
while [ $# -gt 0 ]; do case "$1" in
|
|
304
|
+
--dir) _dir="$2"; shift 2;;
|
|
305
|
+
--dry-run) export KIT_DRY_RUN=1; shift;;
|
|
306
|
+
--yes|-y) export KIT_ASSUME_YES=1; shift;;
|
|
307
|
+
-h|--help)
|
|
308
|
+
echo "usage: kit-routines.sh list|accept <id>|remove <id>|verify|remove-all [--dir D] [--dry-run] [--yes]"
|
|
309
|
+
exit 0;;
|
|
310
|
+
-*) kit_die "unknown flag: $1";;
|
|
311
|
+
*) [ -z "$_id" ] && _id="$1" || kit_die "unexpected arg: $1"; shift;;
|
|
312
|
+
esac; done
|
|
313
|
+
case "$_sub" in
|
|
314
|
+
list) kit_routines_list "$_dir";;
|
|
315
|
+
accept) [ -n "$_id" ] || kit_die "usage: kit-routines.sh accept <id>"; kit_routines_accept "$_id" "$_dir";;
|
|
316
|
+
remove) [ -n "$_id" ] || kit_die "usage: kit-routines.sh remove <id>"; kit_routines_remove "$_id" "$_dir";;
|
|
317
|
+
verify) kit_routines_verify "$_dir";;
|
|
318
|
+
remove-all) kit_routines_remove_all "$_dir";;
|
|
319
|
+
-h|--help) echo "usage: kit-routines.sh list|accept <id>|remove <id>|verify|remove-all [--dir D] [--dry-run] [--yes]"; exit 0;;
|
|
320
|
+
*) kit_die "usage: kit-routines.sh list|accept <id>|remove <id>|verify|remove-all [--dir D] [--dry-run] [--yes]";;
|
|
321
|
+
esac
|
|
322
|
+
fi
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# claude-kit — CLI-style update check. Compares a kit-initialized project's recorded kitVersion
|
|
3
|
+
# against the installed plugin version, and prints a one-line notice when the project is behind.
|
|
4
|
+
# Safe no-op when it can't determine either side. Used by the SessionStart hook and /kit-update.
|
|
5
|
+
#
|
|
6
|
+
# Usage: kit-version-check.sh [--target DIR] [--plugin-root DIR] [--quiet]
|
|
7
|
+
set -uo pipefail
|
|
8
|
+
|
|
9
|
+
TARGET="$PWD"; PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-}"; QUIET=0
|
|
10
|
+
while [[ $# -gt 0 ]]; do
|
|
11
|
+
case "$1" in
|
|
12
|
+
--target) TARGET="$2"; shift 2 ;;
|
|
13
|
+
--plugin-root) PLUGIN_ROOT="$2"; shift 2 ;;
|
|
14
|
+
--quiet) QUIET=1; shift ;;
|
|
15
|
+
*) shift ;;
|
|
16
|
+
esac
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
20
|
+
cfg="$TARGET/.claude/kit.config.json"
|
|
21
|
+
[[ -f "$cfg" ]] || exit 0
|
|
22
|
+
|
|
23
|
+
# Locate the installed plugin's plugin.json: explicit root → env → best-effort glob.
|
|
24
|
+
pj=""
|
|
25
|
+
[[ -n "$PLUGIN_ROOT" && -f "$PLUGIN_ROOT/.claude-plugin/plugin.json" ]] && pj="$PLUGIN_ROOT/.claude-plugin/plugin.json"
|
|
26
|
+
if [[ -z "$pj" ]]; then
|
|
27
|
+
pj="$(ls -t "$HOME"/.claude/plugins/*/claude-kit*/.claude-plugin/plugin.json \
|
|
28
|
+
"$HOME"/.claude/plugins/*/*/claude-kit*/.claude-plugin/plugin.json \
|
|
29
|
+
"$HOME"/.claude/plugins/*/*/claude-kit*/*/.claude-plugin/plugin.json \
|
|
30
|
+
"$HOME"/.claude/plugins/*/*/*/claude-kit*/.claude-plugin/plugin.json 2>/dev/null | head -1 || true)"
|
|
31
|
+
fi
|
|
32
|
+
[[ -n "$pj" && -f "$pj" ]] || exit 0
|
|
33
|
+
|
|
34
|
+
have="$(jq -r '.kitVersion // "0.0.0"' "$cfg" 2>/dev/null || echo 0.0.0)"
|
|
35
|
+
latest="$(jq -r '.version // "0.0.0"' "$pj" 2>/dev/null || echo 0.0.0)"
|
|
36
|
+
|
|
37
|
+
# Branded one-line status — ALWAYS shown (the banner is never silenced unless --quiet).
|
|
38
|
+
# Renders the seed-head sigil when the brand lib is present; plain text otherwise.
|
|
39
|
+
SIGIL="$TARGET/.claude/lib/kit-sigil.sh"
|
|
40
|
+
banner() { # banner <skill-slot> <note>
|
|
41
|
+
[[ $QUIET -eq 1 ]] && return 0
|
|
42
|
+
if [[ -f "$SIGIL" ]]; then
|
|
43
|
+
# shellcheck source=/dev/null
|
|
44
|
+
source "$SIGIL"; kit_sigil "$1" "$2"
|
|
45
|
+
else
|
|
46
|
+
echo "claude-kit${1:+ · $1}${2:+ · $2}"
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Up to date — still announce it (no longer a silent no-op).
|
|
51
|
+
if [[ "$have" == "$latest" ]]; then
|
|
52
|
+
banner "v$latest" "al día · up to date"
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Is `latest` strictly newer than `have`? We can't lean on `sort -V` for the whole string:
|
|
57
|
+
# neither GNU nor BSD `sort -V` implements SemVer pre-release precedence (both rank
|
|
58
|
+
# `0.7.0-beta.1` ABOVE `0.7.0`, which is wrong — a pre-release must be LOWER than its release).
|
|
59
|
+
# So we split each version into a numeric CORE (x.y.z) and an optional PRE (after the first `-`),
|
|
60
|
+
# and compare per SemVer §11:
|
|
61
|
+
# 1. cores differ → the higher core wins (sort -V is reliable on pure numeric cores)
|
|
62
|
+
# 2. cores equal:
|
|
63
|
+
# stable vs pre → stable is newer (a release outranks any pre-release of same core)
|
|
64
|
+
# pre vs pre → compare the pre identifiers (sort -V, e.g. beta.1 < beta.2)
|
|
65
|
+
# POSIX-ish: only bash parameter expansion + a tiny `sort -V` on suffix-free strings.
|
|
66
|
+
ver_is_newer() { # ver_is_newer HAVE LATEST → exit 0 if LATEST strictly newer than HAVE
|
|
67
|
+
local h="$1" l="$2"
|
|
68
|
+
[[ "$h" == "$l" ]] && return 1
|
|
69
|
+
local hcore="${h%%-*}" lcore="${l%%-*}" hpre="" lpre="" top
|
|
70
|
+
[[ "$h" == *-* ]] && hpre="${h#*-}"
|
|
71
|
+
[[ "$l" == *-* ]] && lpre="${l#*-}"
|
|
72
|
+
if [[ "$hcore" != "$lcore" ]]; then
|
|
73
|
+
top="$(printf '%s\n%s\n' "$hcore" "$lcore" | sort -V | tail -1)"
|
|
74
|
+
[[ "$top" == "$lcore" ]]; return
|
|
75
|
+
fi
|
|
76
|
+
# cores equal — decide on the pre-release component
|
|
77
|
+
[[ -z "$lpre" && -n "$hpre" ]] && return 0 # latest = release, have = pre → newer
|
|
78
|
+
[[ -n "$lpre" && -z "$hpre" ]] && return 1 # latest = pre, have = release → not newer
|
|
79
|
+
# both pre-release on the same core
|
|
80
|
+
top="$(printf '%s\n%s\n' "$hpre" "$lpre" | sort -V | tail -1)"
|
|
81
|
+
[[ "$top" == "$lpre" && "$hpre" != "$lpre" ]]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if ver_is_newer "$have" "$latest"; then
|
|
85
|
+
banner "update available" "$have -> $latest · run /kit-update to merge new features (your edits are preserved)"
|
|
86
|
+
exit 10 # signal: behind
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# have is NEWER than the installed plugin — local dev ahead of the release.
|
|
90
|
+
banner "v$have" "ahead of plugin $latest"
|
|
91
|
+
exit 0
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-wire-test.sh — self-test for kit-wire (#369). Runs under bash AND zsh.
|
|
3
|
+
# Run: bash scripts/kit-wire-test.sh
|
|
4
|
+
PLUGIN_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
5
|
+
|
|
6
|
+
if [ -n "${KIT_WIRE_TEST_INNER:-}" ]; then
|
|
7
|
+
set -u
|
|
8
|
+
fail=0
|
|
9
|
+
t() { if [ "$2" != "$3" ]; then echo "FAIL($KIT_WIRE_TEST_INNER): $1 -> got '[$2]' want '[$3]'"; fail=1; else echo "ok($KIT_WIRE_TEST_INNER): $1"; fi; }
|
|
10
|
+
|
|
11
|
+
proj="$(mktemp -d)"; trap 'rm -rf "$proj"' EXIT
|
|
12
|
+
cd "$proj"
|
|
13
|
+
export CLAUDE_PLUGIN_ROOT="$PLUGIN_DIR" # so the shim + plugin-root resolve to our statusline
|
|
14
|
+
export KIT_ASSUME_YES=1
|
|
15
|
+
|
|
16
|
+
. "$PLUGIN_DIR/scripts/kit-wire.sh"
|
|
17
|
+
|
|
18
|
+
# converge
|
|
19
|
+
kit_wire >/dev/null 2>&1
|
|
20
|
+
t "shim created" "$([ -f .claude/statusline.sh ] && echo yes || echo no)" "yes"
|
|
21
|
+
t "shim is a shim" "$(grep -c 'claude-kit statusline shim' .claude/statusline.sh)" "1"
|
|
22
|
+
t "settings wired" "$(jq -r '.statusLine.command' .claude/settings.json 2>/dev/null)" '$CLAUDE_PROJECT_DIR/.claude/statusline.sh'
|
|
23
|
+
t "shim tracked" "$(. "$PLUGIN_DIR/scripts/lib/kit-manifest.sh"; kit_manifest_verify .claude/statusline.sh)" "intact"
|
|
24
|
+
t "settings tracked" "$(. "$PLUGIN_DIR/scripts/lib/kit-manifest.sh"; kit_manifest_verify .claude/settings.json)" "intact"
|
|
25
|
+
|
|
26
|
+
# idempotent: second converge changes nothing, shim still intact
|
|
27
|
+
kit_wire >/dev/null 2>&1
|
|
28
|
+
t "idempotent shim" "$(. "$PLUGIN_DIR/scripts/lib/kit-manifest.sh"; kit_manifest_verify .claude/statusline.sh)" "intact"
|
|
29
|
+
|
|
30
|
+
# --check is clean after converge (rc 0)
|
|
31
|
+
if kit_wire_check >/dev/null 2>&1; then t "check clean rc" "0" "0"; else t "check clean rc" "$?" "0"; fi
|
|
32
|
+
|
|
33
|
+
# hook executability: drop a non-exec hook, converge, assert it became executable
|
|
34
|
+
mkdir -p .claude/hooks; printf '#!/usr/bin/env bash\n:\n' > .claude/hooks/demo.sh; chmod -x .claude/hooks/demo.sh
|
|
35
|
+
kit_wire >/dev/null 2>&1
|
|
36
|
+
t "hook chmod +x" "$([ -x .claude/hooks/demo.sh ] && echo yes || echo no)" "yes"
|
|
37
|
+
|
|
38
|
+
# drift detection: user removes the shim -> --check reports drift (rc 1)
|
|
39
|
+
rm -f .claude/statusline.sh
|
|
40
|
+
if kit_wire_check >/dev/null 2>&1; then t "check detects drift" "0" "1"; else t "check detects drift" "1" "1"; fi
|
|
41
|
+
|
|
42
|
+
[ "$fail" -eq 0 ] && echo "ALL OK($KIT_WIRE_TEST_INNER)"
|
|
43
|
+
exit "$fail"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
rc=0; ran=0
|
|
47
|
+
for sh in bash zsh; do
|
|
48
|
+
command -v "$sh" >/dev/null 2>&1 || continue
|
|
49
|
+
ran=$((ran+1)); echo "--- $sh ---"
|
|
50
|
+
rcflag=""; [ "$sh" = "zsh" ] && rcflag="--no-rcs"
|
|
51
|
+
KIT_WIRE_TEST_INNER="$sh" PATH="$PATH" PLUGIN_DIR="$PLUGIN_DIR" "$sh" $rcflag "$0" || rc=1
|
|
52
|
+
done
|
|
53
|
+
[ "$ran" -eq 0 ] && { echo "no shell"; exit 1; }
|
|
54
|
+
exit "$rc"
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-wire.sh — idempotent "converge" that installs/repairs everything the kit must WIRE into a
|
|
3
|
+
# project, and keeps it wired across updates (#369). Fixes the live class of bug where /kit-update
|
|
4
|
+
# refreshes files but never re-runs the wiring, so settings drift and engine assets go stale.
|
|
5
|
+
#
|
|
6
|
+
# WHAT IT CONVERGES (each recorded in the F0 ownership manifest, so update/uninstall stay safe):
|
|
7
|
+
# 1. statusline shim .claude/statusline.sh -> a STABLE file that exec's the plugin's
|
|
8
|
+
# versioned kit-statusline.sh. The shim never changes, so a plugin bump
|
|
9
|
+
# updates the statusline everywhere with zero local edits (kills the
|
|
10
|
+
# "template drifted older than the project copy" bug).
|
|
11
|
+
# 2. settings statusLine ensures .claude/settings.json points its statusLine at the shim.
|
|
12
|
+
# 3. hook executability chmod +x on managed hooks so they actually run.
|
|
13
|
+
#
|
|
14
|
+
# Run: scripts/kit-wire.sh # converge (interactive confirms unless KIT_ASSUME_YES)
|
|
15
|
+
# scripts/kit-wire.sh --check # report drift only, write nothing (rc 1 if drift) — self-heal
|
|
16
|
+
# KIT_ASSUME_YES=1 scripts/kit-wire.sh # non-interactive (init/update/CI)
|
|
17
|
+
#
|
|
18
|
+
# Idempotent: a second run is a no-op. Honors KIT_DRY_RUN. Requires: jq.
|
|
19
|
+
|
|
20
|
+
set -uo pipefail
|
|
21
|
+
_wire_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
22
|
+
# shellcheck source=/dev/null
|
|
23
|
+
. "$_wire_dir/lib/kit-operate.sh" # pulls in kit-manifest.sh too
|
|
24
|
+
# shellcheck source=/dev/null
|
|
25
|
+
. "$_wire_dir/lib/kit-cli.sh" # kit_is_main
|
|
26
|
+
|
|
27
|
+
# Where the running engine lives. As a Claude Code plugin, CLAUDE_PLUGIN_ROOT is set; otherwise
|
|
28
|
+
# fall back to this script's plugin dir (two levels up from scripts/).
|
|
29
|
+
kit_plugin_root() {
|
|
30
|
+
if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then printf '%s\n' "$CLAUDE_PLUGIN_ROOT"; return 0; fi
|
|
31
|
+
( CDPATH='' cd -- "$_wire_dir/.." && pwd )
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# The shim is intentionally tiny and STABLE — its content never encodes a version, so it survives
|
|
35
|
+
# plugin bumps. It resolves the newest installed kit plugin by glob and exec's its statusline.
|
|
36
|
+
# (Falls back to a co-located plugin root for dev/source checkouts.)
|
|
37
|
+
_kit_shim_content() {
|
|
38
|
+
cat <<'SHIM'
|
|
39
|
+
#!/usr/bin/env bash
|
|
40
|
+
# claude-kit statusline shim — DO NOT EDIT. Managed by `kit-wire` (kit.manifest.json).
|
|
41
|
+
# Stable indirection: resolves the installed kit plugin and exec's its versioned statusline,
|
|
42
|
+
# so a plugin update changes the statusline with no edit here. Fails soft (exit 0).
|
|
43
|
+
set -uo pipefail
|
|
44
|
+
_p="${CLAUDE_PLUGIN_ROOT:-}"
|
|
45
|
+
if [ -z "$_p" ]; then
|
|
46
|
+
_p="$(ls -d "$HOME"/.claude/plugins/cache/*/claude-kit*/ 2>/dev/null | sort -V | tail -1)"
|
|
47
|
+
fi
|
|
48
|
+
_sl="$_p/statusline/kit-statusline.sh"
|
|
49
|
+
[ -f "$_sl" ] || _sl="$_p/templates/statusline/kit-statusline.sh"
|
|
50
|
+
if [ -f "$_sl" ]; then exec bash "$_sl" "$@"; fi
|
|
51
|
+
cat >/dev/null 2>&1; exit 0 # no engine found: consume stdin, render nothing, never error
|
|
52
|
+
SHIM
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Ensure .claude/settings.json has statusLine -> the shim. Uses jq; creates the file if absent.
|
|
56
|
+
_kit_wire_settings() {
|
|
57
|
+
local settings=".claude/settings.json" shim=".claude/statusline.sh" cmd tmp cur want
|
|
58
|
+
cmd="\$CLAUDE_PROJECT_DIR/$shim"
|
|
59
|
+
if _kit_op_dry; then
|
|
60
|
+
cur="$(jq -r '.statusLine.command // empty' "$settings" 2>/dev/null || true)"
|
|
61
|
+
[ "$cur" = "$cmd" ] && { _kit_op_say "= settings.statusLine already wired"; return 0; }
|
|
62
|
+
_kit_op_say "~ would set settings.statusLine -> $cmd"; return 10
|
|
63
|
+
fi
|
|
64
|
+
[ -f "$settings" ] || printf '{}\n' > "$settings"
|
|
65
|
+
cur="$(jq -r '.statusLine.command // empty' "$settings" 2>/dev/null || true)"
|
|
66
|
+
if [ "$cur" = "$cmd" ]; then _kit_op_say "= settings.statusLine already wired"; return 0; fi
|
|
67
|
+
tmp="$(mktemp)" || return 1
|
|
68
|
+
jq --arg c "$cmd" '.statusLine = {type:"command", command:$c}' "$settings" > "$tmp" \
|
|
69
|
+
&& mv "$tmp" "$settings" || { rm -f "$tmp"; return 1; }
|
|
70
|
+
kit_manifest_record "$settings" B wire >/dev/null 2>&1 || true
|
|
71
|
+
_kit_op_say "+ wired settings.statusLine -> $cmd"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# chmod +x any managed hooks present under .claude/hooks (so they actually run).
|
|
75
|
+
_kit_wire_hook_exec() {
|
|
76
|
+
local h changed=0
|
|
77
|
+
[ -d ".claude/hooks" ] || return 0
|
|
78
|
+
for h in .claude/hooks/*.sh; do
|
|
79
|
+
[ -f "$h" ] || continue
|
|
80
|
+
if [ ! -x "$h" ]; then
|
|
81
|
+
_kit_op_dry && { _kit_op_say "~ would chmod +x $h"; changed=1; continue; }
|
|
82
|
+
chmod +x "$h" && _kit_op_say "+ chmod +x $h" && changed=1
|
|
83
|
+
fi
|
|
84
|
+
done
|
|
85
|
+
return 0
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
kit_wire() {
|
|
89
|
+
command -v jq >/dev/null 2>&1 || { echo "kit-wire: jq required" >&2; return 1; }
|
|
90
|
+
local proot shim=".claude/statusline.sh"
|
|
91
|
+
proot="$(kit_plugin_root)"
|
|
92
|
+
_kit_op_say "kit-wire: plugin root = $proot"
|
|
93
|
+
mkdir -p .claude
|
|
94
|
+
# 1. statusline shim (tier B — CLI only). kit_op_write_content runs the conffiles machine.
|
|
95
|
+
_kit_shim_content | kit_op_write_content "$shim" B wire || return 1
|
|
96
|
+
[ -f "$shim" ] && [ -z "${KIT_DRY_RUN:-}" ] && chmod +x "$shim"
|
|
97
|
+
# 2. settings statusLine -> shim
|
|
98
|
+
_kit_wire_settings || true
|
|
99
|
+
# 3. hook executability
|
|
100
|
+
_kit_wire_hook_exec || true
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# --check: report drift, write nothing, rc 1 if anything is out of sync (for SessionStart self-heal)
|
|
104
|
+
# Also verifies that every accepted routine in kit.config.json has a matching cron entry.
|
|
105
|
+
kit_wire_check() {
|
|
106
|
+
local drift=0
|
|
107
|
+
# 1. standard wire drift
|
|
108
|
+
KIT_DRY_RUN=1 kit_wire >/tmp/.kitwire.$$ 2>&1
|
|
109
|
+
if grep -qE '^[~+]|would' /tmp/.kitwire.$$; then
|
|
110
|
+
sed 's/^/ /' /tmp/.kitwire.$$ >&2; drift=1
|
|
111
|
+
fi
|
|
112
|
+
rm -f /tmp/.kitwire.$$
|
|
113
|
+
# 2. routines: check accepted crons are installed (fail-soft — missing crontab is not an error)
|
|
114
|
+
if command -v crontab >/dev/null 2>&1; then
|
|
115
|
+
local rt_script; rt_script="$(kit_plugin_root)/scripts/kit-routines.sh"
|
|
116
|
+
if [ -f "$rt_script" ]; then
|
|
117
|
+
bash "$rt_script" verify 2>/tmp/.kiwire-rt.$$ || { sed 's/^/ /' /tmp/.kiwire-rt.$$ >&2; drift=1; }
|
|
118
|
+
rm -f /tmp/.kiwire-rt.$$
|
|
119
|
+
fi
|
|
120
|
+
fi
|
|
121
|
+
return "$drift"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# CLI (direct execution only; zsh-safe guard)
|
|
125
|
+
# CLI (direct execution only)
|
|
126
|
+
if kit_is_main; then
|
|
127
|
+
case "${1:-}" in
|
|
128
|
+
--check) kit_wire_check;;
|
|
129
|
+
-h|--help) echo "usage: kit-wire.sh [--check] (env: KIT_ASSUME_YES, KIT_DRY_RUN)";;
|
|
130
|
+
*) kit_wire;;
|
|
131
|
+
esac
|
|
132
|
+
fi
|