@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,663 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-doctor — onboarding preflight: deps + brew bootstrap + gh auth/scopes + SSH guided
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# scripts/kit-doctor.sh # detect + auto-install + report
|
|
6
|
+
# scripts/kit-doctor.sh --dry-run # report only — no installs, no auth changes
|
|
7
|
+
# scripts/kit-doctor.sh --no-install # check and auth only — skip package installs
|
|
8
|
+
# scripts/kit-doctor.sh --dismiss-local # silence the "local layer down" session notice
|
|
9
|
+
# # until the next x.y kit update
|
|
10
|
+
#
|
|
11
|
+
# Tiers:
|
|
12
|
+
# Tier 0 Homebrew (macOS bootstrap — requires sudo password)
|
|
13
|
+
# Tier 1 git, gh, jq, perl — hard deps, auto-install via brew
|
|
14
|
+
# Tier 2 node + pnpm, vercel, turbo — project-specific, auto-install if applicable
|
|
15
|
+
# Local mlx-lm via uv tool install + mlx_lm.server auto-start (when .local.enabled)
|
|
16
|
+
# Auth gh auth status + scope:project, git config name/email
|
|
17
|
+
# SSH optional guided flow (ed25519, pbcopy, link to github settings)
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
# Own directory — so we can find sibling scripts (kit-export-project.sh) regardless of cwd.
|
|
21
|
+
_export_dir_doctor="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
22
|
+
|
|
23
|
+
# ---- flags ------------------------------------------------------------------
|
|
24
|
+
DRY_RUN=0; NO_INSTALL=0; DISMISS_LOCAL=0
|
|
25
|
+
for _a in "$@"; do
|
|
26
|
+
case "$_a" in
|
|
27
|
+
--dry-run) DRY_RUN=1 ;;
|
|
28
|
+
--no-install) NO_INSTALL=1 ;;
|
|
29
|
+
--dismiss-local) DISMISS_LOCAL=1 ;;
|
|
30
|
+
-h|--help)
|
|
31
|
+
sed -n '2,12p' "$0" | grep '^#' | sed 's/^# \?//'
|
|
32
|
+
exit 0 ;;
|
|
33
|
+
esac
|
|
34
|
+
done
|
|
35
|
+
|
|
36
|
+
# ---- --dismiss-local: record the dismiss and exit (no preflight run) ---------
|
|
37
|
+
# Writes .local.dismissed = current kitVersion to kit.config.json; the SessionStart
|
|
38
|
+
# notice stays silent until the kit's x.y core moves past it (issue #313).
|
|
39
|
+
if [[ $DISMISS_LOCAL -eq 1 ]]; then
|
|
40
|
+
_kitcfg="${TARGET:-$PWD}/.claude/kit.config.json"
|
|
41
|
+
if ! command -v jq >/dev/null 2>&1 || [[ ! -f "$_kitcfg" ]]; then
|
|
42
|
+
echo "kit-doctor: cannot dismiss — need jq + $_kitcfg" >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
_kv="$(jq -r '.kitVersion // "0.0.0"' "$_kitcfg")"
|
|
46
|
+
_tmp="$(mktemp)"
|
|
47
|
+
jq --arg v "$_kv" '.local.dismissed = $v' "$_kitcfg" > "$_tmp" && mv "$_tmp" "$_kitcfg"
|
|
48
|
+
echo "local layer notice dismissed (kit $_kv) — reappears on the next x.y kit update"
|
|
49
|
+
echo "re-enable earlier: remove .local.dismissed from .claude/kit.config.json"
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# ---- color/unicode detection (matches kit-sigil.sh pattern) -----------------
|
|
54
|
+
_loc="${LC_ALL:-${LC_CTYPE:-${LANG:-}}}"
|
|
55
|
+
if [ "${KIT_ASCII:-}" = "1" ]; then KIT_UNICODE=0
|
|
56
|
+
elif printf '%s' "$_loc" | grep -qiE 'utf-?8'; then KIT_UNICODE=1
|
|
57
|
+
else KIT_UNICODE=0; fi
|
|
58
|
+
|
|
59
|
+
if [ "${FORCE_COLOR:-}" = "1" ]; then KIT_COLOR=1
|
|
60
|
+
elif [ -t 1 ] && [ -z "${NO_COLOR+x}" ]; then KIT_COLOR=1
|
|
61
|
+
else KIT_COLOR=0; fi
|
|
62
|
+
|
|
63
|
+
if [ "$KIT_COLOR" = 1 ]; then
|
|
64
|
+
C_AZAFRAN=$'\033[38;2;201;122;44m'
|
|
65
|
+
C_SUCCESS=$'\033[38;2;21;128;61m'
|
|
66
|
+
C_FAIL=$'\033[38;2;192;50;43m'
|
|
67
|
+
C_WARN=$'\033[38;2;180;83;9m'
|
|
68
|
+
C_DIM=$'\033[2m'
|
|
69
|
+
C_BOLD=$'\033[1m'
|
|
70
|
+
C_RESET=$'\033[0m'
|
|
71
|
+
else
|
|
72
|
+
C_AZAFRAN=''; C_SUCCESS=''; C_FAIL=''; C_WARN=''; C_DIM=''; C_BOLD=''; C_RESET=''
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [ "$KIT_UNICODE" = 1 ]; then
|
|
76
|
+
MARK_OK="${C_SUCCESS}✓${C_RESET}"
|
|
77
|
+
MARK_FAIL="${C_FAIL}✗${C_RESET}"
|
|
78
|
+
MARK_WARN="${C_WARN}!${C_RESET}"
|
|
79
|
+
MARK_SKIP="${C_DIM}-${C_RESET}"
|
|
80
|
+
MARK_SEED="${C_AZAFRAN}⡶${C_RESET}"
|
|
81
|
+
else
|
|
82
|
+
MARK_OK="ok"; MARK_FAIL="FAIL"; MARK_WARN="warn"; MARK_SKIP="-"; MARK_SEED="o"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# ---- report accumulators ----------------------------------------------------
|
|
86
|
+
CHECKS_OK=0; CHECKS_FAIL=0; CHECKS_WARN=0
|
|
87
|
+
ACTIONS_TAKEN=() # things the doctor installed/fixed
|
|
88
|
+
ACTIONS_NEEDED=() # things the user must do manually
|
|
89
|
+
|
|
90
|
+
row() { # <mark> <label> <detail>
|
|
91
|
+
printf ' %-3s %-28s %s\n' "$1" "$2" "${3:-}"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# ---- macOS detection --------------------------------------------------------
|
|
95
|
+
IS_MACOS=0
|
|
96
|
+
[[ "$(uname -s 2>/dev/null)" == "Darwin" ]] && IS_MACOS=1
|
|
97
|
+
|
|
98
|
+
# ---- helpers ----------------------------------------------------------------
|
|
99
|
+
has_cmd() { command -v "$1" >/dev/null 2>&1; }
|
|
100
|
+
|
|
101
|
+
# brew_install <pkg> [<cmd-to-check>] — install via brew unless dry-run/no-install
|
|
102
|
+
# Returns 0 if the command is now available, 1 if not.
|
|
103
|
+
brew_install() {
|
|
104
|
+
local pkg="$1" cmd="${2:-$1}"
|
|
105
|
+
if [[ $DRY_RUN -eq 1 || $NO_INSTALL -eq 1 ]]; then
|
|
106
|
+
return 1
|
|
107
|
+
fi
|
|
108
|
+
if ! has_cmd brew; then return 1; fi
|
|
109
|
+
brew install "$pkg" >/dev/null 2>&1 && has_cmd "$cmd"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# ============================================================================
|
|
113
|
+
# BANNER
|
|
114
|
+
# ============================================================================
|
|
115
|
+
printf '\n'
|
|
116
|
+
printf ' %s%s%s kit-doctor\n' "$MARK_SEED" "$C_BOLD" " / claude-kit${C_RESET}"
|
|
117
|
+
printf ' %s\n' "${C_DIM}onboarding preflight${C_RESET}${DRY_RUN:+${C_WARN} [dry-run]${C_RESET}}${NO_INSTALL:+${C_DIM} [no-install]${C_RESET}}"
|
|
118
|
+
printf '\n'
|
|
119
|
+
|
|
120
|
+
# ============================================================================
|
|
121
|
+
# TIER 0 — Homebrew bootstrap (macOS only)
|
|
122
|
+
# ============================================================================
|
|
123
|
+
printf ' %s%sTier 0 — bootstrap%s\n' "$C_BOLD" "" "$C_RESET"
|
|
124
|
+
|
|
125
|
+
if [[ $IS_MACOS -eq 0 ]]; then
|
|
126
|
+
row "$MARK_SKIP" "Homebrew" "not macOS — skipping"
|
|
127
|
+
elif has_cmd brew; then
|
|
128
|
+
row "$MARK_OK" "Homebrew" "$(brew --version 2>/dev/null | head -1)"
|
|
129
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
130
|
+
else
|
|
131
|
+
if [[ $DRY_RUN -eq 1 || $NO_INSTALL -eq 1 ]]; then
|
|
132
|
+
row "$MARK_FAIL" "Homebrew" "missing — install: https://brew.sh"
|
|
133
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
134
|
+
ACTIONS_NEEDED+=("Install Homebrew: https://brew.sh")
|
|
135
|
+
else
|
|
136
|
+
printf '\n'
|
|
137
|
+
printf ' %s Homebrew not found — running the official installer (will ask for sudo password)...\n' "$MARK_WARN"
|
|
138
|
+
printf ' %sInstaller URL: https://brew.sh%s\n\n' "$C_DIM" "$C_RESET"
|
|
139
|
+
if /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"; then
|
|
140
|
+
# Add brew to PATH for the rest of this session (Apple Silicon path)
|
|
141
|
+
[[ -f /opt/homebrew/bin/brew ]] && eval "$(/opt/homebrew/bin/brew shellenv)" 2>/dev/null || true
|
|
142
|
+
[[ -f /usr/local/bin/brew ]] && eval "$(/usr/local/bin/brew shellenv)" 2>/dev/null || true
|
|
143
|
+
row "$MARK_OK" "Homebrew" "installed $(brew --version 2>/dev/null | head -1)"
|
|
144
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
145
|
+
ACTIONS_TAKEN+=("Installed Homebrew")
|
|
146
|
+
else
|
|
147
|
+
row "$MARK_FAIL" "Homebrew" "install failed — see https://brew.sh"
|
|
148
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
149
|
+
ACTIONS_NEEDED+=("Manually install Homebrew: https://brew.sh")
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
fi
|
|
153
|
+
printf '\n'
|
|
154
|
+
|
|
155
|
+
# ============================================================================
|
|
156
|
+
# TIER 1 — hard deps
|
|
157
|
+
# ============================================================================
|
|
158
|
+
printf ' %s%sTier 1 — hard deps%s\n' "$C_BOLD" "" "$C_RESET"
|
|
159
|
+
|
|
160
|
+
# ---- git -------------------------------------------------------------------
|
|
161
|
+
if has_cmd git; then
|
|
162
|
+
_gitver="$(git --version 2>/dev/null | awk '{print $3}')"
|
|
163
|
+
row "$MARK_OK" "git" "$_gitver"
|
|
164
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
165
|
+
elif brew_install git; then
|
|
166
|
+
row "$MARK_OK" "git" "installed $(git --version 2>/dev/null | awk '{print $3}')"
|
|
167
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
168
|
+
ACTIONS_TAKEN+=("Installed git via brew")
|
|
169
|
+
else
|
|
170
|
+
row "$MARK_FAIL" "git" "missing — brew install git"
|
|
171
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
172
|
+
ACTIONS_NEEDED+=("Install git: brew install git")
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# ---- gh (min version 2.29 for Projects v2) ----------------------------------
|
|
176
|
+
GH_OK=0
|
|
177
|
+
if has_cmd gh; then
|
|
178
|
+
_ghver="$(gh --version 2>/dev/null | head -1 | awk '{print $3}')"
|
|
179
|
+
# Check for Projects v2 support (gh >= 2.29.0)
|
|
180
|
+
_ghmaj="${_ghver%%.*}"
|
|
181
|
+
_ghmin="${_ghver#*.}"; _ghmin="${_ghmin%%.*}"
|
|
182
|
+
if [[ "${_ghmaj:-0}" -gt 2 ]] || \
|
|
183
|
+
[[ "${_ghmaj:-0}" -eq 2 && "${_ghmin:-0}" -ge 29 ]]; then
|
|
184
|
+
row "$MARK_OK" "gh" "$_ghver (Projects v2 ok)"
|
|
185
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
186
|
+
GH_OK=1
|
|
187
|
+
else
|
|
188
|
+
row "$MARK_WARN" "gh" "$_ghver — upgrade needed (>=2.29 for Projects v2)"
|
|
189
|
+
CHECKS_WARN=$((CHECKS_WARN + 1))
|
|
190
|
+
if [[ $DRY_RUN -eq 0 && $NO_INSTALL -eq 0 ]] && has_cmd brew; then
|
|
191
|
+
brew upgrade gh >/dev/null 2>&1 && GH_OK=1 && ACTIONS_TAKEN+=("Upgraded gh")
|
|
192
|
+
fi
|
|
193
|
+
[[ $GH_OK -eq 0 ]] && ACTIONS_NEEDED+=("Upgrade gh: brew upgrade gh")
|
|
194
|
+
fi
|
|
195
|
+
elif brew_install gh; then
|
|
196
|
+
_ghver="$(gh --version 2>/dev/null | head -1 | awk '{print $3}')"
|
|
197
|
+
row "$MARK_OK" "gh" "installed $\_ghver"
|
|
198
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
199
|
+
GH_OK=1
|
|
200
|
+
ACTIONS_TAKEN+=("Installed gh via brew")
|
|
201
|
+
else
|
|
202
|
+
row "$MARK_FAIL" "gh" "missing — brew install gh"
|
|
203
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
204
|
+
ACTIONS_NEEDED+=("Install gh: brew install gh")
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
# ---- jq -------------------------------------------------------------------
|
|
208
|
+
if has_cmd jq; then
|
|
209
|
+
row "$MARK_OK" "jq" "$(jq --version 2>/dev/null)"
|
|
210
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
211
|
+
elif brew_install jq; then
|
|
212
|
+
row "$MARK_OK" "jq" "installed $(jq --version 2>/dev/null)"
|
|
213
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
214
|
+
ACTIONS_TAKEN+=("Installed jq via brew")
|
|
215
|
+
else
|
|
216
|
+
row "$MARK_FAIL" "jq" "missing — brew install jq"
|
|
217
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
218
|
+
ACTIONS_NEEDED+=("Install jq: brew install jq")
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# ---- perl -----------------------------------------------------------------
|
|
222
|
+
if has_cmd perl; then
|
|
223
|
+
row "$MARK_OK" "perl" "$(perl --version 2>/dev/null | head -2 | tail -1 | tr -d '()' | awk '{print $2}')"
|
|
224
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
225
|
+
elif brew_install perl; then
|
|
226
|
+
row "$MARK_OK" "perl" "installed"
|
|
227
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
228
|
+
ACTIONS_TAKEN+=("Installed perl via brew")
|
|
229
|
+
else
|
|
230
|
+
row "$MARK_FAIL" "perl" "missing — brew install perl"
|
|
231
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
232
|
+
ACTIONS_NEEDED+=("Install perl: brew install perl")
|
|
233
|
+
fi
|
|
234
|
+
printf '\n'
|
|
235
|
+
|
|
236
|
+
# ============================================================================
|
|
237
|
+
# TIER 2 — project deps (context-aware)
|
|
238
|
+
# ============================================================================
|
|
239
|
+
printf ' %s%sTier 2 — project deps%s\n' "$C_BOLD" "" "$C_RESET"
|
|
240
|
+
|
|
241
|
+
# ---- node + pnpm (check package.json for pinned version) -------------------
|
|
242
|
+
_pkg_json="${TARGET:-$PWD}/package.json"
|
|
243
|
+
_pkg_mgr=""
|
|
244
|
+
[[ -f "$_pkg_json" ]] && _pkg_mgr="$(jq -r '.packageManager // ""' "$_pkg_json" 2>/dev/null || true)"
|
|
245
|
+
|
|
246
|
+
if has_cmd node; then
|
|
247
|
+
row "$MARK_OK" "node" "$(node --version 2>/dev/null)"
|
|
248
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
249
|
+
elif brew_install node; then
|
|
250
|
+
row "$MARK_OK" "node" "installed $(node --version 2>/dev/null)"
|
|
251
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
252
|
+
ACTIONS_TAKEN+=("Installed node via brew")
|
|
253
|
+
else
|
|
254
|
+
row "$MARK_FAIL" "node" "missing — brew install node"
|
|
255
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
256
|
+
ACTIONS_NEEDED+=("Install node: brew install node")
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
if has_cmd pnpm; then
|
|
260
|
+
row "$MARK_OK" "pnpm" "$(pnpm --version 2>/dev/null)"
|
|
261
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
262
|
+
elif [[ -n "$_pkg_mgr" && "$_pkg_mgr" == pnpm* ]]; then
|
|
263
|
+
# Pinned in package.json — enable via corepack
|
|
264
|
+
if has_cmd corepack || has_cmd node; then
|
|
265
|
+
if [[ $DRY_RUN -eq 0 && $NO_INSTALL -eq 0 ]]; then
|
|
266
|
+
corepack enable pnpm 2>/dev/null && \
|
|
267
|
+
corepack prepare "$_pkg_mgr" --activate 2>/dev/null && \
|
|
268
|
+
ACTIONS_TAKEN+=("Enabled pnpm via corepack ($_pkg_mgr)") || true
|
|
269
|
+
fi
|
|
270
|
+
if has_cmd pnpm; then
|
|
271
|
+
row "$MARK_OK" "pnpm" "$(pnpm --version 2>/dev/null) (via corepack)"
|
|
272
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
273
|
+
else
|
|
274
|
+
row "$MARK_FAIL" "pnpm" "missing — corepack enable pnpm"
|
|
275
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
276
|
+
ACTIONS_NEEDED+=("Enable pnpm: corepack enable pnpm && corepack prepare $_pkg_mgr --activate")
|
|
277
|
+
fi
|
|
278
|
+
else
|
|
279
|
+
row "$MARK_FAIL" "pnpm" "missing — npm install -g pnpm"
|
|
280
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
281
|
+
ACTIONS_NEEDED+=("Install pnpm: npm install -g pnpm")
|
|
282
|
+
fi
|
|
283
|
+
elif brew_install pnpm; then
|
|
284
|
+
row "$MARK_OK" "pnpm" "installed $(pnpm --version 2>/dev/null)"
|
|
285
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
286
|
+
ACTIONS_TAKEN+=("Installed pnpm via brew")
|
|
287
|
+
else
|
|
288
|
+
row "$MARK_WARN" "pnpm" "not found (needed for turbo monorepo)"
|
|
289
|
+
CHECKS_WARN=$((CHECKS_WARN + 1))
|
|
290
|
+
ACTIONS_NEEDED+=("Install pnpm: brew install pnpm")
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
# ---- node_modules (check if pnpm install needed) ----------------------------
|
|
294
|
+
_nm="${TARGET:-$PWD}/node_modules"
|
|
295
|
+
if [[ -d "$_nm" ]]; then
|
|
296
|
+
row "$MARK_OK" "node_modules" "present"
|
|
297
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
298
|
+
elif [[ -f "$_pkg_json" ]]; then
|
|
299
|
+
if [[ $DRY_RUN -eq 0 && $NO_INSTALL -eq 0 ]] && has_cmd pnpm; then
|
|
300
|
+
printf '\n %s node_modules missing — running pnpm install...\n' "$MARK_WARN"
|
|
301
|
+
if pnpm install --frozen-lockfile 2>/dev/null || pnpm install; then
|
|
302
|
+
row "$MARK_OK" "node_modules" "installed via pnpm install"
|
|
303
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
304
|
+
ACTIONS_TAKEN+=("Ran pnpm install")
|
|
305
|
+
else
|
|
306
|
+
row "$MARK_FAIL" "node_modules" "pnpm install failed — run manually"
|
|
307
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
308
|
+
ACTIONS_NEEDED+=("Run pnpm install")
|
|
309
|
+
fi
|
|
310
|
+
else
|
|
311
|
+
row "$MARK_WARN" "node_modules" "missing — run pnpm install"
|
|
312
|
+
CHECKS_WARN=$((CHECKS_WARN + 1))
|
|
313
|
+
ACTIONS_NEEDED+=("Run pnpm install")
|
|
314
|
+
fi
|
|
315
|
+
else
|
|
316
|
+
row "$MARK_SKIP" "node_modules" "no package.json found — skipping"
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
# ---- vercel (only if .vercel/ linked or VERCEL env) -------------------------
|
|
320
|
+
_has_vercel_link=0
|
|
321
|
+
[[ -d "${TARGET:-$PWD}/.vercel" ]] && _has_vercel_link=1
|
|
322
|
+
|
|
323
|
+
if [[ $_has_vercel_link -eq 1 ]] || [[ -n "${VERCEL:-}" ]]; then
|
|
324
|
+
if has_cmd vercel; then
|
|
325
|
+
row "$MARK_OK" "vercel" "$(vercel --version 2>/dev/null | head -1)"
|
|
326
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
327
|
+
elif brew_install vercel vercel; then
|
|
328
|
+
row "$MARK_OK" "vercel" "installed $(vercel --version 2>/dev/null | head -1)"
|
|
329
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
330
|
+
ACTIONS_TAKEN+=("Installed vercel CLI via brew")
|
|
331
|
+
elif [[ $DRY_RUN -eq 0 && $NO_INSTALL -eq 0 ]] && has_cmd npm; then
|
|
332
|
+
npm install -g vercel >/dev/null 2>&1 && \
|
|
333
|
+
ACTIONS_TAKEN+=("Installed vercel CLI via npm") || true
|
|
334
|
+
if has_cmd vercel; then
|
|
335
|
+
row "$MARK_OK" "vercel" "installed $(vercel --version 2>/dev/null | head -1)"
|
|
336
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
337
|
+
else
|
|
338
|
+
row "$MARK_FAIL" "vercel" "missing — npm install -g vercel"
|
|
339
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
340
|
+
ACTIONS_NEEDED+=("Install vercel: npm install -g vercel")
|
|
341
|
+
fi
|
|
342
|
+
else
|
|
343
|
+
row "$MARK_FAIL" "vercel" "missing — npm install -g vercel"
|
|
344
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
345
|
+
ACTIONS_NEEDED+=("Install vercel: npm install -g vercel")
|
|
346
|
+
fi
|
|
347
|
+
else
|
|
348
|
+
row "$MARK_SKIP" "vercel" "no .vercel/ project link — skipping"
|
|
349
|
+
fi
|
|
350
|
+
|
|
351
|
+
# turbo + playwright: dev deps only — never global; check node_modules
|
|
352
|
+
if [[ -d "$_nm" ]]; then
|
|
353
|
+
if [[ -x "$_nm/.bin/turbo" ]]; then
|
|
354
|
+
row "$MARK_OK" "turbo" "dev dep present"
|
|
355
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
356
|
+
else
|
|
357
|
+
row "$MARK_SKIP" "turbo" "not in node_modules (run pnpm install)"
|
|
358
|
+
fi
|
|
359
|
+
if [[ -x "$_nm/.bin/playwright" ]]; then
|
|
360
|
+
row "$MARK_OK" "playwright" "dev dep present"
|
|
361
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
362
|
+
else
|
|
363
|
+
row "$MARK_SKIP" "playwright" "not in node_modules (run pnpm install)"
|
|
364
|
+
fi
|
|
365
|
+
fi
|
|
366
|
+
printf '\n'
|
|
367
|
+
|
|
368
|
+
# ============================================================================
|
|
369
|
+
# LOCAL MODEL LAYER (opt-in — only checked when .local.enabled is true)
|
|
370
|
+
# ============================================================================
|
|
371
|
+
# Same gating principle as vercel: silence when the project didn't opt in.
|
|
372
|
+
# Install path (#313): `uv tool install mlx-lm` — isolated tool venv, PEP 668-safe
|
|
373
|
+
# (Homebrew python is externally-managed; plain pip/pip3 fails there). Fallback
|
|
374
|
+
# pipx; NEVER suggest plain pip. When mlx-lm is present but the server is down,
|
|
375
|
+
# the doctor starts it in background + health-checks the port. --dry-run stays
|
|
376
|
+
# 100% read-only: it reports what it would do, installs and starts nothing.
|
|
377
|
+
_kitcfg="${TARGET:-$PWD}/.claude/kit.config.json"
|
|
378
|
+
_local_on="false"
|
|
379
|
+
[[ -f "$_kitcfg" ]] && _local_on="$(jq -r '.local.enabled // false' "$_kitcfg" 2>/dev/null || echo false)"
|
|
380
|
+
if [[ "$_local_on" == "true" ]]; then
|
|
381
|
+
printf ' %s%sLocal model layer (mlx_lm.server)%s\n' "$C_BOLD" "" "$C_RESET"
|
|
382
|
+
_lport="$(jq -r '.local.port // 8080' "$_kitcfg" 2>/dev/null || echo 8080)"
|
|
383
|
+
_lmodel="$(jq -r '.local.model // "mlx-community/Qwen3-8B-4bit"' "$_kitcfg" 2>/dev/null || echo "mlx-community/Qwen3-8B-4bit")"
|
|
384
|
+
|
|
385
|
+
# Apple silicon — MLX requirement; gate install/start on it
|
|
386
|
+
_mlx_capable=0
|
|
387
|
+
if [[ $IS_MACOS -eq 1 && "$(uname -m 2>/dev/null)" == "arm64" ]]; then
|
|
388
|
+
row "$MARK_OK" "apple silicon" "$(sysctl -n machdep.cpu.brand_string 2>/dev/null || echo arm64)"
|
|
389
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
390
|
+
_mlx_capable=1
|
|
391
|
+
else
|
|
392
|
+
row "$MARK_WARN" "apple silicon" "MLX needs an Apple silicon Mac — local layer will stay dormant here"
|
|
393
|
+
CHECKS_WARN=$((CHECKS_WARN + 1))
|
|
394
|
+
fi
|
|
395
|
+
|
|
396
|
+
# mlx-lm binary — uv/pipx install to ~/.local/bin, which may not be on PATH yet
|
|
397
|
+
_mlx_find() {
|
|
398
|
+
if has_cmd mlx_lm.server; then command -v mlx_lm.server
|
|
399
|
+
elif [[ -x "$HOME/.local/bin/mlx_lm.server" ]]; then echo "$HOME/.local/bin/mlx_lm.server"
|
|
400
|
+
fi
|
|
401
|
+
return 0 # never trip set -e via the $(...) assignment
|
|
402
|
+
}
|
|
403
|
+
_mlx_bin="$(_mlx_find)"
|
|
404
|
+
|
|
405
|
+
if [[ -n "$_mlx_bin" ]]; then
|
|
406
|
+
row "$MARK_OK" "mlx-lm" "installed ($_mlx_bin)"
|
|
407
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
408
|
+
elif [[ $_mlx_capable -eq 0 ]]; then
|
|
409
|
+
row "$MARK_SKIP" "mlx-lm" "not Apple silicon — skipping install"
|
|
410
|
+
elif [[ $DRY_RUN -eq 1 || $NO_INSTALL -eq 1 ]]; then
|
|
411
|
+
row "$MARK_FAIL" "mlx-lm" "missing — uv tool install mlx-lm"
|
|
412
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
413
|
+
ACTIONS_NEEDED+=("Install the local model runtime: uv tool install mlx-lm")
|
|
414
|
+
else
|
|
415
|
+
# ensure an installer: uv preferred (brew-installable), pipx as fallback
|
|
416
|
+
if ! has_cmd uv && ! has_cmd pipx; then
|
|
417
|
+
brew_install uv uv && ACTIONS_TAKEN+=("Installed uv via brew") || true
|
|
418
|
+
fi
|
|
419
|
+
if has_cmd uv; then
|
|
420
|
+
printf ' %s mlx-lm missing — installing via uv tool install (isolated venv)...\n' "$MARK_WARN"
|
|
421
|
+
uv tool install mlx-lm >/dev/null 2>&1 || true
|
|
422
|
+
_mlx_installer="uv tool install"
|
|
423
|
+
elif has_cmd pipx; then
|
|
424
|
+
printf ' %s mlx-lm missing — installing via pipx (isolated venv)...\n' "$MARK_WARN"
|
|
425
|
+
pipx install mlx-lm >/dev/null 2>&1 || true
|
|
426
|
+
_mlx_installer="pipx"
|
|
427
|
+
fi
|
|
428
|
+
_mlx_bin="$(_mlx_find)"
|
|
429
|
+
if [[ -n "$_mlx_bin" ]]; then
|
|
430
|
+
row "$MARK_OK" "mlx-lm" "installed via ${_mlx_installer:-uv} ($_mlx_bin)"
|
|
431
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
432
|
+
ACTIONS_TAKEN+=("Installed mlx-lm via ${_mlx_installer:-uv}")
|
|
433
|
+
else
|
|
434
|
+
row "$MARK_FAIL" "mlx-lm" "install failed — uv tool install mlx-lm"
|
|
435
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
436
|
+
ACTIONS_NEEDED+=("Install the local model runtime: uv tool install mlx-lm")
|
|
437
|
+
fi
|
|
438
|
+
fi
|
|
439
|
+
|
|
440
|
+
# server alive on the configured port — auto-start when down + installed
|
|
441
|
+
_lurl="http://127.0.0.1:${_lport}/v1/models"
|
|
442
|
+
if curl -sf -m 1 "$_lurl" >/dev/null 2>&1; then
|
|
443
|
+
row "$MARK_OK" "server" "alive @ :${_lport} (${_lmodel##*/}) — NL chores run at \$0"
|
|
444
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
445
|
+
elif [[ -z "$_mlx_bin" ]]; then
|
|
446
|
+
row "$MARK_WARN" "server" "not running — install mlx-lm first, then re-run kit-doctor"
|
|
447
|
+
CHECKS_WARN=$((CHECKS_WARN + 1))
|
|
448
|
+
ACTIONS_NEEDED+=("Start the local model server: mlx_lm.server --model ${_lmodel} --port ${_lport}")
|
|
449
|
+
elif [[ $DRY_RUN -eq 1 ]]; then
|
|
450
|
+
row "$MARK_WARN" "server" "not running — would start mlx_lm.server in background (first run downloads ~4.5 GB)"
|
|
451
|
+
CHECKS_WARN=$((CHECKS_WARN + 1))
|
|
452
|
+
ACTIONS_NEEDED+=("Start the local model server: mlx_lm.server --model ${_lmodel} --port ${_lport}")
|
|
453
|
+
else
|
|
454
|
+
_llog="$HOME/.claude/kit-local-server.log"
|
|
455
|
+
mkdir -p "$HOME/.claude" 2>/dev/null || _llog="${TMPDIR:-/tmp}/kit-local-server.log"
|
|
456
|
+
# first-run warning: model not in the HF cache yet → server stays "loading" for a while
|
|
457
|
+
_hfdir="$HOME/.cache/huggingface/hub/models--${_lmodel//\//--}"
|
|
458
|
+
[[ -d "$_hfdir" ]] || printf ' %s first run: the model downloads now (~4.5 GB) — the server may take several minutes to come up\n' "$MARK_WARN"
|
|
459
|
+
printf ' %s server down — starting mlx_lm.server in background (log: %s)...\n' "$MARK_WARN" "$_llog"
|
|
460
|
+
nohup "$_mlx_bin" --model "$_lmodel" --port "$_lport" >>"$_llog" 2>&1 &
|
|
461
|
+
_mlx_pid=$!
|
|
462
|
+
_lup=0
|
|
463
|
+
for _i in $(seq 1 30); do
|
|
464
|
+
if curl -sf -m 1 "$_lurl" >/dev/null 2>&1; then _lup=1; break; fi
|
|
465
|
+
kill -0 "$_mlx_pid" 2>/dev/null || break
|
|
466
|
+
sleep 1
|
|
467
|
+
done
|
|
468
|
+
if [[ $_lup -eq 1 ]]; then
|
|
469
|
+
row "$MARK_OK" "server" "started @ :${_lport} (${_lmodel##*/}) — NL chores run at \$0"
|
|
470
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
471
|
+
ACTIONS_TAKEN+=("Started mlx_lm.server @ :${_lport} (pid $_mlx_pid)")
|
|
472
|
+
elif kill -0 "$_mlx_pid" 2>/dev/null; then
|
|
473
|
+
row "$MARK_WARN" "server" "starting (pid $_mlx_pid) — model still downloading/loading; check: curl :${_lport}/v1/models"
|
|
474
|
+
CHECKS_WARN=$((CHECKS_WARN + 1))
|
|
475
|
+
ACTIONS_TAKEN+=("Started mlx_lm.server (pid $_mlx_pid) — still loading, log: $_llog")
|
|
476
|
+
else
|
|
477
|
+
row "$MARK_FAIL" "server" "failed to start — see $_llog"
|
|
478
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
479
|
+
ACTIONS_NEEDED+=("Start the local model server manually: mlx_lm.server --model ${_lmodel} --port ${_lport} (log: $_llog)")
|
|
480
|
+
fi
|
|
481
|
+
fi
|
|
482
|
+
printf '\n'
|
|
483
|
+
fi
|
|
484
|
+
|
|
485
|
+
# ============================================================================
|
|
486
|
+
# AUTH + CONFIG
|
|
487
|
+
# ============================================================================
|
|
488
|
+
printf ' %s%sAuth + config%s\n' "$C_BOLD" "" "$C_RESET"
|
|
489
|
+
|
|
490
|
+
# ---- gh auth status ---------------------------------------------------------
|
|
491
|
+
GH_AUTHED=0
|
|
492
|
+
if ! has_cmd gh; then
|
|
493
|
+
row "$MARK_SKIP" "gh auth" "gh not found — install first"
|
|
494
|
+
else
|
|
495
|
+
if gh auth status >/dev/null 2>&1; then
|
|
496
|
+
_ghuser="$(gh api user --jq .login 2>/dev/null || echo "unknown")"
|
|
497
|
+
row "$MARK_OK" "gh auth" "logged in as @$_ghuser"
|
|
498
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
499
|
+
GH_AUTHED=1
|
|
500
|
+
else
|
|
501
|
+
if [[ $DRY_RUN -eq 1 ]]; then
|
|
502
|
+
row "$MARK_FAIL" "gh auth" "not authenticated — gh auth login"
|
|
503
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
504
|
+
ACTIONS_NEEDED+=("Authenticate: gh auth login")
|
|
505
|
+
else
|
|
506
|
+
printf '\n %s gh not authenticated — launching gh auth login (device flow)...\n\n' "$MARK_WARN"
|
|
507
|
+
if gh auth login --git-credential-helper; then
|
|
508
|
+
_ghuser="$(gh api user --jq .login 2>/dev/null || echo "unknown")"
|
|
509
|
+
row "$MARK_OK" "gh auth" "logged in as @$_ghuser"
|
|
510
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
511
|
+
GH_AUTHED=1
|
|
512
|
+
ACTIONS_TAKEN+=("Authenticated gh (device flow)")
|
|
513
|
+
else
|
|
514
|
+
row "$MARK_FAIL" "gh auth" "login failed — run: gh auth login"
|
|
515
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
516
|
+
ACTIONS_NEEDED+=("Authenticate: gh auth login")
|
|
517
|
+
fi
|
|
518
|
+
fi
|
|
519
|
+
fi
|
|
520
|
+
fi
|
|
521
|
+
|
|
522
|
+
# ---- gh scope: project (Projects v2 silently fails without it) --------------
|
|
523
|
+
GH_SCOPE_PROJECT=0
|
|
524
|
+
if [[ $GH_AUTHED -eq 1 ]] && has_cmd gh; then
|
|
525
|
+
_token_scopes="$(gh auth status 2>&1 | grep -i 'token scopes\|scopes:' | head -1 || true)"
|
|
526
|
+
if echo "$_token_scopes" | grep -qi 'project'; then
|
|
527
|
+
row "$MARK_OK" "gh scope:project" "present"
|
|
528
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
529
|
+
GH_SCOPE_PROJECT=1
|
|
530
|
+
else
|
|
531
|
+
if [[ $DRY_RUN -eq 1 ]]; then
|
|
532
|
+
row "$MARK_FAIL" "gh scope:project" "missing — gh auth refresh -s project"
|
|
533
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
534
|
+
ACTIONS_NEEDED+=("Add scope: gh auth refresh -s project")
|
|
535
|
+
else
|
|
536
|
+
printf '\n %s scope \"project\" missing — running gh auth refresh (will open browser)...\n\n' "$MARK_WARN"
|
|
537
|
+
if gh auth refresh -s project; then
|
|
538
|
+
row "$MARK_OK" "gh scope:project" "added"
|
|
539
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
540
|
+
GH_SCOPE_PROJECT=1
|
|
541
|
+
ACTIONS_TAKEN+=("Added gh scope:project via auth refresh")
|
|
542
|
+
else
|
|
543
|
+
row "$MARK_FAIL" "gh scope:project" "refresh failed — gh auth refresh -s project"
|
|
544
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
545
|
+
ACTIONS_NEEDED+=("Add scope: gh auth refresh -s project")
|
|
546
|
+
fi
|
|
547
|
+
fi
|
|
548
|
+
fi
|
|
549
|
+
elif [[ $GH_AUTHED -eq 0 ]]; then
|
|
550
|
+
row "$MARK_SKIP" "gh scope:project" "gh not authed — re-run after login"
|
|
551
|
+
fi
|
|
552
|
+
|
|
553
|
+
# ---- git config user.name + user.email -------------------------------------
|
|
554
|
+
_git_name="$(git config --global user.name 2>/dev/null || true)"
|
|
555
|
+
if [[ -n "$_git_name" ]]; then
|
|
556
|
+
row "$MARK_OK" "git user.name" "$_git_name"
|
|
557
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
558
|
+
else
|
|
559
|
+
row "$MARK_FAIL" "git user.name" "not set — git config --global user.name \"Your Name\""
|
|
560
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
561
|
+
ACTIONS_NEEDED+=("Set git identity: git config --global user.name \"Your Name\"")
|
|
562
|
+
fi
|
|
563
|
+
|
|
564
|
+
_git_email="$(git config --global user.email 2>/dev/null || true)"
|
|
565
|
+
if [[ -n "$_git_email" ]]; then
|
|
566
|
+
row "$MARK_OK" "git user.email" "$_git_email"
|
|
567
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
568
|
+
else
|
|
569
|
+
row "$MARK_FAIL" "git user.email" "not set — git config --global user.email \"you@example.com\""
|
|
570
|
+
CHECKS_FAIL=$((CHECKS_FAIL + 1))
|
|
571
|
+
ACTIONS_NEEDED+=("Set git email: git config --global user.email \"you@example.com\"")
|
|
572
|
+
fi
|
|
573
|
+
printf '\n'
|
|
574
|
+
|
|
575
|
+
# ============================================================================
|
|
576
|
+
# SSH (optional guided flow)
|
|
577
|
+
# ============================================================================
|
|
578
|
+
printf ' %s%sSSH (optional — GitHub HTTPS auth already configured by gh)%s\n' "$C_BOLD" "" "$C_RESET"
|
|
579
|
+
|
|
580
|
+
_ssh_result=0
|
|
581
|
+
ssh -T git@github.com -o ConnectTimeout=5 -o BatchMode=yes 2>&1 | grep -q "successfully authenticated" && _ssh_result=1 || true
|
|
582
|
+
|
|
583
|
+
if [[ $_ssh_result -eq 1 ]]; then
|
|
584
|
+
row "$MARK_OK" "SSH to github.com" "key already accepted"
|
|
585
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
586
|
+
else
|
|
587
|
+
row "$MARK_SKIP" "SSH to github.com" "no key — HTTPS is used by default (gh auth sets it up)"
|
|
588
|
+
|
|
589
|
+
# Show guided SSH setup only if user explicitly wants SSH
|
|
590
|
+
if [[ "${KIT_DOCTOR_SSH:-}" == "1" && $DRY_RUN -eq 0 ]]; then
|
|
591
|
+
_ssh_key="$HOME/.ssh/id_ed25519_kit"
|
|
592
|
+
if [[ ! -f "$_ssh_key" ]]; then
|
|
593
|
+
printf '\n Generating ed25519 SSH key at %s...\n' "$_ssh_key"
|
|
594
|
+
ssh-keygen -t ed25519 -f "$_ssh_key" -C "claude-kit onboarding" -N "" 2>/dev/null
|
|
595
|
+
ACTIONS_TAKEN+=("Generated SSH key $_ssh_key")
|
|
596
|
+
fi
|
|
597
|
+
if has_cmd pbcopy; then
|
|
598
|
+
pbcopy < "${_ssh_key}.pub"
|
|
599
|
+
printf ' Public key copied to clipboard.\n'
|
|
600
|
+
else
|
|
601
|
+
cat "${_ssh_key}.pub"
|
|
602
|
+
fi
|
|
603
|
+
printf '\n Paste the public key at: https://github.com/settings/ssh/new\n'
|
|
604
|
+
ACTIONS_NEEDED+=("Add SSH public key: https://github.com/settings/ssh/new")
|
|
605
|
+
else
|
|
606
|
+
printf ' %sTip: set KIT_DOCTOR_SSH=1 and re-run to generate an ed25519 key.%s\n' "$C_DIM" "$C_RESET"
|
|
607
|
+
fi
|
|
608
|
+
fi
|
|
609
|
+
printf '\n'
|
|
610
|
+
|
|
611
|
+
# ============================================================================
|
|
612
|
+
# SURFACE PORTABILITY (connection test — terminal / Cowork / claude.ai, #376)
|
|
613
|
+
# ============================================================================
|
|
614
|
+
# A kit project should run on all three Claude surfaces with no hand edits. Terminal + Cowork read
|
|
615
|
+
# CLAUDE.md + .claude/ natively; claude.ai needs the kit-export-project transform. This test asks
|
|
616
|
+
# the same question kit-export-project --verify does: is the PORTABLE surface self-sufficient — does
|
|
617
|
+
# every file CLAUDE.md leans on carry tier-A (portable) semantics, not a tier-B CLI-only shim that
|
|
618
|
+
# would silently no-op off-terminal? Read-only; gated on a kit project (CLAUDE.md present).
|
|
619
|
+
_kit_target="${TARGET:-$PWD}"
|
|
620
|
+
_export_script="$_export_dir_doctor/kit-export-project.sh"
|
|
621
|
+
if [[ -f "$_kit_target/CLAUDE.md" && -f "$_export_script" ]]; then
|
|
622
|
+
printf ' %s%sSurface portability (terminal / Cowork / claude.ai)%s\n' "$C_BOLD" "" "$C_RESET"
|
|
623
|
+
if ( cd "$_kit_target" && bash "$_export_script" --verify ) >/dev/null 2>&1; then
|
|
624
|
+
row "$MARK_OK" "portable surface" "tier-A self-sufficient — runs in terminal/Cowork/claude.ai unchanged"
|
|
625
|
+
CHECKS_OK=$((CHECKS_OK + 1))
|
|
626
|
+
else
|
|
627
|
+
row "$MARK_WARN" "portable surface" "defect(s) — run: kit-export-project.sh --verify"
|
|
628
|
+
CHECKS_WARN=$((CHECKS_WARN + 1))
|
|
629
|
+
ACTIONS_NEEDED+=("Inspect portability: scripts/kit-export-project.sh --verify (then export for claude.ai with no args)")
|
|
630
|
+
fi
|
|
631
|
+
printf '\n'
|
|
632
|
+
fi
|
|
633
|
+
|
|
634
|
+
# ============================================================================
|
|
635
|
+
# SUMMARY
|
|
636
|
+
# ============================================================================
|
|
637
|
+
printf ' %s%sSummary%s\n' "$C_BOLD" "" "$C_RESET"
|
|
638
|
+
printf ' %-4s %s checked ok\n' "" "$CHECKS_OK"
|
|
639
|
+
[[ $CHECKS_WARN -gt 0 ]] && printf ' %-4s %s warnings\n' "" "$CHECKS_WARN"
|
|
640
|
+
[[ $CHECKS_FAIL -gt 0 ]] && printf ' %-4s %s failed\n' "" "$CHECKS_FAIL"
|
|
641
|
+
|
|
642
|
+
if [[ ${#ACTIONS_TAKEN[@]} -gt 0 ]]; then
|
|
643
|
+
printf '\n %s%sInstalled / fixed:%s\n' "$C_BOLD" "" "$C_RESET"
|
|
644
|
+
for _a in "${ACTIONS_TAKEN[@]}"; do
|
|
645
|
+
printf ' %s %s\n' "$MARK_OK" "$_a"
|
|
646
|
+
done
|
|
647
|
+
fi
|
|
648
|
+
|
|
649
|
+
if [[ ${#ACTIONS_NEEDED[@]} -gt 0 ]]; then
|
|
650
|
+
printf '\n %s%sAction required:%s\n' "$C_BOLD" "" "$C_RESET"
|
|
651
|
+
for _a in "${ACTIONS_NEEDED[@]}"; do
|
|
652
|
+
printf ' %s %s\n' "$MARK_FAIL" "$_a"
|
|
653
|
+
done
|
|
654
|
+
fi
|
|
655
|
+
printf '\n'
|
|
656
|
+
|
|
657
|
+
# ---- onboarding page -------------------------------------------------------
|
|
658
|
+
_admin_url="${CCKIT_ADMIN_URL:-http://localhost:3001}"
|
|
659
|
+
printf ' %sOnboarding guide (web): %s/onboarding%s\n' "$C_DIM" "$_admin_url" "$C_RESET"
|
|
660
|
+
printf '\n'
|
|
661
|
+
|
|
662
|
+
# Exit non-zero when critical checks failed
|
|
663
|
+
[[ $CHECKS_FAIL -gt 0 ]] && exit 1 || exit 0
|