feed-the-machine 1.6.0 → 1.7.0
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/LICENSE +21 -21
- package/README.md +170 -170
- package/bin/brain.py +1340 -0
- package/bin/convert_claude_skills_to_codex.py +490 -0
- package/bin/generate-manifest.mjs +463 -463
- package/bin/harden_codex_skills.py +141 -0
- package/bin/install.mjs +491 -491
- package/bin/migrate-eng-buddy-data.py +875 -0
- package/bin/playbook_engine/__init__.py +1 -0
- package/bin/playbook_engine/conftest.py +8 -0
- package/bin/playbook_engine/extractor.py +33 -0
- package/bin/playbook_engine/manager.py +102 -0
- package/bin/playbook_engine/models.py +84 -0
- package/bin/playbook_engine/registry.py +35 -0
- package/bin/playbook_engine/test_extractor.py +72 -0
- package/bin/playbook_engine/test_integration.py +129 -0
- package/bin/playbook_engine/test_manager.py +85 -0
- package/bin/playbook_engine/test_models.py +166 -0
- package/bin/playbook_engine/test_registry.py +67 -0
- package/bin/playbook_engine/test_tracer.py +86 -0
- package/bin/playbook_engine/tracer.py +93 -0
- package/bin/tasks_db.py +456 -0
- package/docs/HOOKS.md +243 -243
- package/docs/INBOX.md +233 -233
- package/ftm/SKILL.md +125 -122
- package/ftm-audit/SKILL.md +623 -623
- package/ftm-audit/references/protocols/PROJECT-PATTERNS.md +91 -91
- package/ftm-audit/references/protocols/RUNTIME-WIRING.md +66 -66
- package/ftm-audit/references/protocols/WIRING-CONTRACTS.md +135 -135
- package/ftm-audit/references/strategies/AUTO-FIX-STRATEGIES.md +69 -69
- package/ftm-audit/references/templates/REPORT-FORMAT.md +96 -96
- package/ftm-audit/scripts/run-knip.sh +23 -23
- package/ftm-audit.yml +2 -2
- package/ftm-brainstorm/SKILL.md +1003 -498
- package/ftm-brainstorm/evals/evals.json +180 -100
- package/ftm-brainstorm/evals/promptfoo.yaml +109 -109
- package/ftm-brainstorm/references/agent-prompts.md +552 -224
- package/ftm-brainstorm/references/plan-template.md +209 -121
- package/ftm-brainstorm.yml +2 -2
- package/ftm-browse/SKILL.md +454 -454
- package/ftm-browse/daemon/browser-manager.ts +206 -206
- package/ftm-browse/daemon/bun.lock +30 -30
- package/ftm-browse/daemon/cli.ts +347 -347
- package/ftm-browse/daemon/commands.ts +410 -410
- package/ftm-browse/daemon/main.ts +357 -357
- package/ftm-browse/daemon/package.json +17 -17
- package/ftm-browse/daemon/server.ts +189 -189
- package/ftm-browse/daemon/snapshot.ts +519 -519
- package/ftm-browse/daemon/tsconfig.json +22 -22
- package/ftm-browse.yml +4 -4
- package/ftm-capture/SKILL.md +370 -370
- package/ftm-capture.yml +4 -4
- package/ftm-codex-gate/SKILL.md +361 -361
- package/ftm-codex-gate.yml +2 -2
- package/ftm-config/SKILL.md +422 -345
- package/ftm-config.default.yml +125 -82
- package/ftm-config.yml +44 -2
- package/ftm-council/SKILL.md +416 -416
- package/ftm-council/references/prompts/CLAUDE-INVESTIGATION.md +60 -60
- package/ftm-council/references/prompts/CODEX-INVESTIGATION.md +58 -58
- package/ftm-council/references/prompts/GEMINI-INVESTIGATION.md +58 -58
- package/ftm-council/references/prompts/REBUTTAL-TEMPLATE.md +57 -57
- package/ftm-council/references/protocols/PREREQUISITES.md +47 -47
- package/ftm-council/references/protocols/STEP-0-FRAMING.md +46 -46
- package/ftm-council.yml +2 -2
- package/ftm-dashboard/SKILL.md +163 -163
- package/ftm-dashboard.yml +4 -4
- package/ftm-debug/SKILL.md +1037 -1037
- package/ftm-debug/references/phases/PHASE-0-INTAKE.md +58 -58
- package/ftm-debug/references/phases/PHASE-1-TRIAGE.md +46 -46
- package/ftm-debug/references/phases/PHASE-2-WAR-ROOM-AGENTS.md +279 -279
- package/ftm-debug/references/phases/PHASE-3-TO-6-EXECUTION.md +436 -436
- package/ftm-debug/references/protocols/BLACKBOARD.md +86 -86
- package/ftm-debug/references/protocols/EDGE-CASES.md +103 -103
- package/ftm-debug.yml +2 -2
- package/ftm-diagram/SKILL.md +277 -277
- package/ftm-diagram.yml +2 -2
- package/ftm-executor/SKILL.md +777 -777
- package/ftm-executor/references/STYLE-TEMPLATE.md +73 -73
- package/ftm-executor/references/phases/PHASE-0-VERIFICATION.md +62 -62
- package/ftm-executor/references/phases/PHASE-2-AGENT-ASSEMBLY.md +34 -34
- package/ftm-executor/references/phases/PHASE-3-WORKTREES.md +38 -38
- package/ftm-executor/references/phases/PHASE-4-5-AUDIT.md +72 -72
- package/ftm-executor/references/phases/PHASE-4-DISPATCH.md +66 -66
- package/ftm-executor/references/phases/PHASE-5-5-CODEX-GATE.md +73 -73
- package/ftm-executor/references/protocols/DOCUMENTATION-BOOTSTRAP.md +36 -36
- package/ftm-executor/references/protocols/MODEL-PROFILE.md +59 -59
- package/ftm-executor/references/protocols/PROGRESS-TRACKING.md +66 -66
- package/ftm-executor/runtime/ftm-runtime.mjs +252 -252
- package/ftm-executor/runtime/package.json +8 -8
- package/ftm-executor.yml +2 -2
- package/ftm-git/SKILL.md +441 -441
- package/ftm-git/evals/evals.json +26 -26
- package/ftm-git/evals/promptfoo.yaml +75 -75
- package/ftm-git/hooks/post-commit-experience.sh +92 -92
- package/ftm-git/references/patterns/SECRET-PATTERNS.md +104 -104
- package/ftm-git/references/protocols/REMEDIATION.md +139 -139
- package/ftm-git/scripts/pre-commit-secrets.sh +110 -110
- package/ftm-git.yml +2 -2
- package/ftm-inbox/backend/__pycache__/main.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/adapters/_retry.py +64 -64
- package/ftm-inbox/backend/adapters/base.py +230 -230
- package/ftm-inbox/backend/adapters/freshservice.py +104 -104
- package/ftm-inbox/backend/adapters/gmail.py +125 -125
- package/ftm-inbox/backend/adapters/jira.py +136 -136
- package/ftm-inbox/backend/adapters/registry.py +192 -192
- package/ftm-inbox/backend/adapters/slack.py +110 -110
- package/ftm-inbox/backend/db/connection.py +54 -54
- package/ftm-inbox/backend/db/schema.py +78 -78
- package/ftm-inbox/backend/executor/__init__.py +7 -7
- package/ftm-inbox/backend/executor/engine.py +149 -149
- package/ftm-inbox/backend/executor/step_runner.py +98 -98
- package/ftm-inbox/backend/main.py +103 -103
- package/ftm-inbox/backend/models/__init__.py +1 -1
- package/ftm-inbox/backend/models/unified_task.py +36 -36
- package/ftm-inbox/backend/planner/__init__.py +6 -6
- package/ftm-inbox/backend/planner/__pycache__/__init__.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/planner/__pycache__/generator.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/planner/__pycache__/schema.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/planner/generator.py +127 -127
- package/ftm-inbox/backend/planner/schema.py +34 -34
- package/ftm-inbox/backend/requirements.txt +5 -5
- package/ftm-inbox/backend/routes/__pycache__/plan.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/routes/execute.py +186 -186
- package/ftm-inbox/backend/routes/health.py +52 -52
- package/ftm-inbox/backend/routes/inbox.py +68 -68
- package/ftm-inbox/backend/routes/plan.py +271 -271
- package/ftm-inbox/bin/launchagent.mjs +91 -91
- package/ftm-inbox/bin/setup.mjs +188 -188
- package/ftm-inbox/bin/start.sh +10 -10
- package/ftm-inbox/bin/status.sh +17 -17
- package/ftm-inbox/bin/stop.sh +8 -8
- package/ftm-inbox/config.example.yml +55 -55
- package/ftm-inbox/package-lock.json +2898 -2898
- package/ftm-inbox/package.json +26 -26
- package/ftm-inbox/postcss.config.js +6 -6
- package/ftm-inbox/src/app.css +199 -199
- package/ftm-inbox/src/app.html +18 -18
- package/ftm-inbox/src/lib/api.ts +166 -166
- package/ftm-inbox/src/lib/components/ExecutionLog.svelte +81 -81
- package/ftm-inbox/src/lib/components/InboxFeed.svelte +143 -143
- package/ftm-inbox/src/lib/components/PlanStep.svelte +271 -271
- package/ftm-inbox/src/lib/components/PlanView.svelte +206 -206
- package/ftm-inbox/src/lib/components/StreamPanel.svelte +99 -99
- package/ftm-inbox/src/lib/components/TaskCard.svelte +190 -190
- package/ftm-inbox/src/lib/components/ui/EmptyState.svelte +63 -63
- package/ftm-inbox/src/lib/components/ui/KawaiiCard.svelte +86 -86
- package/ftm-inbox/src/lib/components/ui/PillButton.svelte +106 -106
- package/ftm-inbox/src/lib/components/ui/StatusBadge.svelte +67 -67
- package/ftm-inbox/src/lib/components/ui/StreamDrawer.svelte +149 -149
- package/ftm-inbox/src/lib/components/ui/ThemeToggle.svelte +80 -80
- package/ftm-inbox/src/lib/theme.ts +47 -47
- package/ftm-inbox/src/routes/+layout.svelte +76 -76
- package/ftm-inbox/src/routes/+page.svelte +401 -401
- package/ftm-inbox/svelte.config.js +12 -12
- package/ftm-inbox/tailwind.config.ts +63 -63
- package/ftm-inbox/tsconfig.json +13 -13
- package/ftm-inbox/vite.config.ts +6 -6
- package/ftm-intent/SKILL.md +241 -241
- package/ftm-intent.yml +2 -2
- package/ftm-manifest.json +3794 -3794
- package/ftm-map/SKILL.md +291 -291
- package/ftm-map/scripts/db.py +712 -712
- package/ftm-map/scripts/index.py +415 -415
- package/ftm-map/scripts/parser.py +224 -224
- package/ftm-map/scripts/queries/go-tags.scm +20 -20
- package/ftm-map/scripts/queries/javascript-tags.scm +35 -35
- package/ftm-map/scripts/queries/python-tags.scm +31 -31
- package/ftm-map/scripts/queries/ruby-tags.scm +19 -19
- package/ftm-map/scripts/queries/rust-tags.scm +37 -37
- package/ftm-map/scripts/queries/typescript-tags.scm +41 -41
- package/ftm-map/scripts/query.py +301 -301
- package/ftm-map/scripts/ranker.py +377 -377
- package/ftm-map/scripts/requirements.txt +5 -5
- package/ftm-map/scripts/setup-hooks.sh +27 -27
- package/ftm-map/scripts/setup.sh +56 -56
- package/ftm-map/scripts/test_db.py +364 -364
- package/ftm-map/scripts/test_parser.py +174 -174
- package/ftm-map/scripts/test_query.py +183 -183
- package/ftm-map/scripts/test_ranker.py +199 -199
- package/ftm-map/scripts/views.py +591 -591
- package/ftm-map.yml +2 -2
- package/ftm-mind/SKILL.md +201 -1943
- package/ftm-mind/evals/promptfoo.yaml +142 -142
- package/ftm-mind/references/blackboard-protocol.md +110 -0
- package/ftm-mind/references/blackboard-schema.md +328 -328
- package/ftm-mind/references/complexity-guide.md +110 -110
- package/ftm-mind/references/complexity-sizing.md +138 -0
- package/ftm-mind/references/decide-act-protocol.md +172 -0
- package/ftm-mind/references/direct-execution.md +51 -0
- package/ftm-mind/references/environment-discovery.md +77 -0
- package/ftm-mind/references/event-registry.md +319 -319
- package/ftm-mind/references/mcp-inventory.md +300 -296
- package/ftm-mind/references/ops-routing.md +47 -0
- package/ftm-mind/references/orient-protocol.md +234 -0
- package/ftm-mind/references/personality.md +40 -0
- package/ftm-mind/references/protocols/COMPLEXITY-SIZING.md +72 -72
- package/ftm-mind/references/protocols/MCP-HEURISTICS.md +32 -32
- package/ftm-mind/references/protocols/PLAN-APPROVAL.md +80 -80
- package/ftm-mind/references/reflexion-protocol.md +249 -249
- package/ftm-mind/references/routing/SCENARIOS.md +22 -22
- package/ftm-mind/references/routing-scenarios.md +35 -35
- package/ftm-mind.yml +2 -2
- package/ftm-ops.yml +4 -0
- package/ftm-pause/SKILL.md +395 -395
- package/ftm-pause/references/protocols/SKILL-RESTORE-PROTOCOLS.md +186 -186
- package/ftm-pause/references/protocols/VALIDATION.md +80 -80
- package/ftm-pause.yml +2 -2
- package/ftm-researcher/SKILL.md +275 -275
- package/ftm-researcher/evals/agent-diversity.yaml +17 -17
- package/ftm-researcher/evals/synthesis-quality.yaml +12 -12
- package/ftm-researcher/evals/trigger-accuracy.yaml +39 -39
- package/ftm-researcher/references/adaptive-search.md +116 -116
- package/ftm-researcher/references/agent-prompts.md +193 -193
- package/ftm-researcher/references/council-integration.md +193 -193
- package/ftm-researcher/references/output-format.md +203 -203
- package/ftm-researcher/references/synthesis-pipeline.md +165 -165
- package/ftm-researcher/scripts/score_credibility.py +234 -234
- package/ftm-researcher/scripts/validate_research.py +92 -92
- package/ftm-researcher.yml +2 -2
- package/ftm-resume/SKILL.md +518 -518
- package/ftm-resume/references/protocols/VALIDATION.md +172 -172
- package/ftm-resume.yml +2 -2
- package/ftm-retro/SKILL.md +380 -380
- package/ftm-retro/references/protocols/SCORING-RUBRICS.md +89 -89
- package/ftm-retro/references/templates/REPORT-FORMAT.md +109 -109
- package/ftm-retro.yml +2 -2
- package/ftm-routine/SKILL.md +170 -170
- package/ftm-routine.yml +4 -4
- package/ftm-state/blackboard/capabilities.json +5 -5
- package/ftm-state/blackboard/capabilities.schema.json +27 -27
- package/ftm-state/blackboard/context.json +37 -23
- package/ftm-state/blackboard/experiences/doom-statusline-fix.json +26 -0
- package/ftm-state/blackboard/experiences/hackathon-pages-site.json +26 -0
- package/ftm-state/blackboard/experiences/hindsight-sso-kickoff.json +42 -0
- package/ftm-state/blackboard/experiences/index.json +58 -9
- package/ftm-state/blackboard/experiences/learning-ragnarok-api-access.json +23 -0
- package/ftm-state/blackboard/experiences/nordlayer-members-auto-assign.json +26 -0
- package/ftm-state/blackboard/experiences/saml2aws-stale-session-fix.json +41 -0
- package/ftm-state/blackboard/patterns.json +6 -6
- package/ftm-state/schemas/context.schema.json +130 -130
- package/ftm-state/schemas/experience-index.schema.json +77 -77
- package/ftm-state/schemas/experience.schema.json +78 -78
- package/ftm-state/schemas/patterns.schema.json +44 -44
- package/ftm-upgrade/SKILL.md +194 -194
- package/ftm-upgrade/scripts/check-version.sh +76 -76
- package/ftm-upgrade/scripts/upgrade.sh +143 -143
- package/ftm-upgrade.yml +2 -2
- package/ftm-verify.yml +2 -2
- package/ftm.yml +2 -2
- package/hooks/ftm-auto-log.sh +137 -0
- package/hooks/ftm-blackboard-enforcer.sh +93 -93
- package/hooks/ftm-discovery-reminder.sh +90 -90
- package/hooks/ftm-drafts-gate.sh +61 -61
- package/hooks/ftm-event-logger.mjs +107 -107
- package/hooks/ftm-install-hooks.sh +240 -0
- package/hooks/ftm-learning-capture.sh +117 -0
- package/hooks/ftm-map-autodetect.sh +79 -79
- package/hooks/ftm-pending-sync-check.sh +22 -22
- package/hooks/ftm-plan-gate.sh +92 -92
- package/hooks/ftm-post-commit-trigger.sh +57 -57
- package/hooks/ftm-post-compaction.sh +138 -0
- package/hooks/ftm-pre-compaction.sh +147 -0
- package/hooks/ftm-session-end.sh +52 -0
- package/hooks/ftm-session-snapshot.sh +213 -0
- package/hooks/settings-template.json +81 -81
- package/install.sh +363 -363
- package/package.json +84 -84
- package/uninstall.sh +25 -25
package/install.sh
CHANGED
|
@@ -1,363 +1,363 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
# FTM Skills Installer
|
|
5
|
-
# Creates symlinks from this repo into ~/.claude/skills/ so slash commands work.
|
|
6
|
-
# Installs hooks, merges them into settings.json, and verifies the result.
|
|
7
|
-
# Safe to re-run — idempotent.
|
|
8
|
-
#
|
|
9
|
-
# Usage:
|
|
10
|
-
# ./install.sh # Full install (skills + hooks + settings merge)
|
|
11
|
-
# ./install.sh --no-hooks # Skills and state only, skip hooks entirely
|
|
12
|
-
# ./install.sh --skip-merge # Install hook files but don't touch settings.json
|
|
13
|
-
|
|
14
|
-
REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
15
|
-
SKILLS_DIR="$HOME/.claude/skills"
|
|
16
|
-
STATE_DIR="$HOME/.claude/ftm-state"
|
|
17
|
-
CONFIG_DIR="$HOME/.claude"
|
|
18
|
-
HOOKS_DIR="$HOME/.claude/hooks"
|
|
19
|
-
SETTINGS_FILE="$CONFIG_DIR/settings.json"
|
|
20
|
-
|
|
21
|
-
NO_HOOKS=false
|
|
22
|
-
SKIP_MERGE=false
|
|
23
|
-
for arg in "$@"; do
|
|
24
|
-
case "$arg" in
|
|
25
|
-
--no-hooks) NO_HOOKS=true ;;
|
|
26
|
-
--skip-merge) SKIP_MERGE=true ;;
|
|
27
|
-
# Keep --setup-hooks for backwards compat (now a no-op since merge is default)
|
|
28
|
-
--setup-hooks) ;;
|
|
29
|
-
esac
|
|
30
|
-
done
|
|
31
|
-
|
|
32
|
-
WARN_COUNT=0
|
|
33
|
-
warn() {
|
|
34
|
-
echo " WARN: $1"
|
|
35
|
-
WARN_COUNT=$((WARN_COUNT + 1))
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
# --- Preflight Checks ---
|
|
39
|
-
|
|
40
|
-
echo "Preflight checks..."
|
|
41
|
-
|
|
42
|
-
# Check jq (required for hooks and settings merge)
|
|
43
|
-
if ! command -v jq &>/dev/null; then
|
|
44
|
-
if [ "$NO_HOOKS" = true ]; then
|
|
45
|
-
echo " jq not found (ok — hooks skipped)"
|
|
46
|
-
else
|
|
47
|
-
echo ""
|
|
48
|
-
echo " ERROR: jq is required for FTM hooks."
|
|
49
|
-
echo ""
|
|
50
|
-
echo " Install it:"
|
|
51
|
-
echo " macOS: brew install jq"
|
|
52
|
-
echo " Ubuntu: sudo apt-get install jq"
|
|
53
|
-
echo " Alpine: apk add jq"
|
|
54
|
-
echo ""
|
|
55
|
-
echo " Or skip hooks: ./install.sh --no-hooks"
|
|
56
|
-
exit 1
|
|
57
|
-
fi
|
|
58
|
-
else
|
|
59
|
-
echo " jq: $(jq --version)"
|
|
60
|
-
fi
|
|
61
|
-
|
|
62
|
-
# Check node (required for event logger hook)
|
|
63
|
-
if ! command -v node &>/dev/null; then
|
|
64
|
-
if [ "$NO_HOOKS" = true ]; then
|
|
65
|
-
echo " node not found (ok — hooks skipped)"
|
|
66
|
-
else
|
|
67
|
-
echo ""
|
|
68
|
-
echo " ERROR: Node.js is required for the FTM event logger hook."
|
|
69
|
-
echo ""
|
|
70
|
-
echo " Install it: https://nodejs.org/"
|
|
71
|
-
echo " Or skip hooks: ./install.sh --no-hooks"
|
|
72
|
-
exit 1
|
|
73
|
-
fi
|
|
74
|
-
else
|
|
75
|
-
echo " node: $(node --version)"
|
|
76
|
-
fi
|
|
77
|
-
|
|
78
|
-
echo ""
|
|
79
|
-
echo "Installing FTM skills from: $REPO_DIR"
|
|
80
|
-
echo "Linking into: $SKILLS_DIR"
|
|
81
|
-
echo ""
|
|
82
|
-
|
|
83
|
-
mkdir -p "$SKILLS_DIR"
|
|
84
|
-
|
|
85
|
-
# --- Skills ---
|
|
86
|
-
|
|
87
|
-
# Link all ftm*.yml files
|
|
88
|
-
for yml in "$REPO_DIR"/ftm*.yml; do
|
|
89
|
-
[ -f "$yml" ] || continue
|
|
90
|
-
name=$(basename "$yml")
|
|
91
|
-
# Skip ftm-config.default.yml — it's a template, not a skill
|
|
92
|
-
[[ "$name" == *".default."* ]] && continue
|
|
93
|
-
target="$SKILLS_DIR/$name"
|
|
94
|
-
if [ -L "$target" ]; then
|
|
95
|
-
rm "$target"
|
|
96
|
-
elif [ -f "$target" ]; then
|
|
97
|
-
echo " SKIP $name (real file exists — back it up first)"
|
|
98
|
-
continue
|
|
99
|
-
fi
|
|
100
|
-
ln -s "$yml" "$target"
|
|
101
|
-
echo " LINK $name"
|
|
102
|
-
done
|
|
103
|
-
|
|
104
|
-
# Link all ftm* directories (skills with SKILL.md)
|
|
105
|
-
for dir in "$REPO_DIR"/ftm*/; do
|
|
106
|
-
[ -d "$dir" ] || continue
|
|
107
|
-
name=$(basename "$dir")
|
|
108
|
-
[ "$name" = "ftm-state" ] && continue # state is handled separately
|
|
109
|
-
target="$SKILLS_DIR/$name"
|
|
110
|
-
if [ -L "$target" ]; then
|
|
111
|
-
rm "$target"
|
|
112
|
-
elif [ -d "$target" ]; then
|
|
113
|
-
echo " SKIP $name/ (real directory exists — back it up first)"
|
|
114
|
-
continue
|
|
115
|
-
fi
|
|
116
|
-
ln -s "$dir" "$target"
|
|
117
|
-
echo " LINK $name/"
|
|
118
|
-
done
|
|
119
|
-
|
|
120
|
-
SKILL_COUNT=0
|
|
121
|
-
for _f in "$REPO_DIR"/ftm*.yml; do
|
|
122
|
-
[ -e "$_f" ] || continue
|
|
123
|
-
case "$_f" in *.default.*) continue ;; esac
|
|
124
|
-
SKILL_COUNT=$((SKILL_COUNT + 1))
|
|
125
|
-
done
|
|
126
|
-
echo ""
|
|
127
|
-
echo " $SKILL_COUNT skills linked."
|
|
128
|
-
|
|
129
|
-
# --- Blackboard State ---
|
|
130
|
-
|
|
131
|
-
if [ -d "$REPO_DIR/ftm-state" ]; then
|
|
132
|
-
echo ""
|
|
133
|
-
mkdir -p "$STATE_DIR/blackboard/experiences"
|
|
134
|
-
for f in "$REPO_DIR/ftm-state/blackboard"/*.json; do
|
|
135
|
-
[ -f "$f" ] || continue
|
|
136
|
-
name=$(basename "$f")
|
|
137
|
-
target="$STATE_DIR/blackboard/$name"
|
|
138
|
-
if [ ! -f "$target" ]; then
|
|
139
|
-
cp "$f" "$target"
|
|
140
|
-
echo " INIT $name (blackboard template)"
|
|
141
|
-
fi
|
|
142
|
-
done
|
|
143
|
-
idx="$STATE_DIR/blackboard/experiences/index.json"
|
|
144
|
-
if [ ! -f "$idx" ]; then
|
|
145
|
-
cp "$REPO_DIR/ftm-state/blackboard/experiences/index.json" "$idx"
|
|
146
|
-
echo " INIT experiences/index.json (blackboard template)"
|
|
147
|
-
fi
|
|
148
|
-
fi
|
|
149
|
-
|
|
150
|
-
# --- Config ---
|
|
151
|
-
|
|
152
|
-
if [ ! -f "$CONFIG_DIR/ftm-config.yml" ] && [ -f "$REPO_DIR/ftm-config.default.yml" ]; then
|
|
153
|
-
cp "$REPO_DIR/ftm-config.default.yml" "$CONFIG_DIR/ftm-config.yml"
|
|
154
|
-
echo " INIT ftm-config.yml (from default template)"
|
|
155
|
-
fi
|
|
156
|
-
|
|
157
|
-
# --- Hooks ---
|
|
158
|
-
|
|
159
|
-
HOOK_COUNT=0
|
|
160
|
-
|
|
161
|
-
if [ "$NO_HOOKS" = true ]; then
|
|
162
|
-
echo ""
|
|
163
|
-
echo "Skipping hooks (--no-hooks)."
|
|
164
|
-
else
|
|
165
|
-
echo ""
|
|
166
|
-
echo "Installing hooks..."
|
|
167
|
-
|
|
168
|
-
if [ -d "$REPO_DIR/hooks" ]; then
|
|
169
|
-
mkdir -p "$HOOKS_DIR"
|
|
170
|
-
|
|
171
|
-
# Install shell hooks
|
|
172
|
-
for hook in "$REPO_DIR/hooks"/ftm-*.sh; do
|
|
173
|
-
[ -f "$hook" ] || continue
|
|
174
|
-
name=$(basename "$hook")
|
|
175
|
-
target="$HOOKS_DIR/$name"
|
|
176
|
-
if [ -f "$target" ]; then
|
|
177
|
-
cp "$hook" "$target"
|
|
178
|
-
chmod +x "$target"
|
|
179
|
-
echo " UPDATE $name"
|
|
180
|
-
else
|
|
181
|
-
cp "$hook" "$target"
|
|
182
|
-
chmod +x "$target"
|
|
183
|
-
echo " INSTALL $name"
|
|
184
|
-
fi
|
|
185
|
-
HOOK_COUNT=$((HOOK_COUNT + 1))
|
|
186
|
-
done
|
|
187
|
-
|
|
188
|
-
# Install Node.js hooks
|
|
189
|
-
for hook in "$REPO_DIR/hooks"/ftm-*.mjs; do
|
|
190
|
-
[ -f "$hook" ] || continue
|
|
191
|
-
name=$(basename "$hook")
|
|
192
|
-
target="$HOOKS_DIR/$name"
|
|
193
|
-
if [ -f "$target" ]; then
|
|
194
|
-
cp "$hook" "$target"
|
|
195
|
-
echo " UPDATE $name"
|
|
196
|
-
else
|
|
197
|
-
cp "$hook" "$target"
|
|
198
|
-
echo " INSTALL $name"
|
|
199
|
-
fi
|
|
200
|
-
HOOK_COUNT=$((HOOK_COUNT + 1))
|
|
201
|
-
done
|
|
202
|
-
|
|
203
|
-
echo ""
|
|
204
|
-
echo " $HOOK_COUNT hooks installed to $HOOKS_DIR"
|
|
205
|
-
fi
|
|
206
|
-
|
|
207
|
-
# --- Hook Config Merge (default behavior now) ---
|
|
208
|
-
|
|
209
|
-
if [ "$SKIP_MERGE" = true ]; then
|
|
210
|
-
echo ""
|
|
211
|
-
echo " Skipping settings.json merge (--skip-merge)."
|
|
212
|
-
echo " Add entries from hooks/settings-template.json to ~/.claude/settings.json manually."
|
|
213
|
-
else
|
|
214
|
-
echo ""
|
|
215
|
-
echo "Registering hooks in settings.json..."
|
|
216
|
-
|
|
217
|
-
TEMPLATE="$REPO_DIR/hooks/settings-template.json"
|
|
218
|
-
if [ ! -f "$TEMPLATE" ]; then
|
|
219
|
-
warn "hooks/settings-template.json not found — hooks installed but not registered"
|
|
220
|
-
else
|
|
221
|
-
# Expand ~ to $HOME in the template (jq doesn't expand shell paths)
|
|
222
|
-
EXPANDED_TEMPLATE=$(sed "s|~/.claude|$HOME/.claude|g" "$TEMPLATE")
|
|
223
|
-
|
|
224
|
-
if [ ! -f "$SETTINGS_FILE" ]; then
|
|
225
|
-
# No settings.json — create one from the template hooks section
|
|
226
|
-
echo "$EXPANDED_TEMPLATE" | jq '{hooks: .hooks}' > "$SETTINGS_FILE"
|
|
227
|
-
echo " CREATED $SETTINGS_FILE with FTM hooks"
|
|
228
|
-
else
|
|
229
|
-
# Merge FTM hooks into existing settings.json
|
|
230
|
-
BACKUP="$SETTINGS_FILE.ftm-backup-$(date +%Y%m%d%H%M%S)"
|
|
231
|
-
cp "$SETTINGS_FILE" "$BACKUP"
|
|
232
|
-
echo " BACKUP $BACKUP"
|
|
233
|
-
|
|
234
|
-
# Extract the hooks section from the template
|
|
235
|
-
TEMPLATE_HOOKS=$(echo "$EXPANDED_TEMPLATE" | jq '.hooks')
|
|
236
|
-
|
|
237
|
-
# Read existing settings
|
|
238
|
-
EXISTING=$(cat "$SETTINGS_FILE")
|
|
239
|
-
|
|
240
|
-
# Ensure hooks key exists
|
|
241
|
-
if echo "$EXISTING" | jq -e '.hooks' >/dev/null 2>&1; then
|
|
242
|
-
: # hooks key exists
|
|
243
|
-
else
|
|
244
|
-
EXISTING=$(echo "$EXISTING" | jq '. + {hooks: {}}')
|
|
245
|
-
fi
|
|
246
|
-
|
|
247
|
-
# Merge each hook event type
|
|
248
|
-
for EVENT in PreToolUse UserPromptSubmit PostToolUse Stop; do
|
|
249
|
-
TEMPLATE_ENTRIES=$(echo "$TEMPLATE_HOOKS" | jq --arg e "$EVENT" '.[$e] // []')
|
|
250
|
-
EXISTING_ENTRIES=$(echo "$EXISTING" | jq --arg e "$EVENT" '.hooks[$e] // []')
|
|
251
|
-
|
|
252
|
-
# Check if any FTM hooks are already present (by checking command paths)
|
|
253
|
-
FTM_COMMANDS=$(echo "$TEMPLATE_ENTRIES" | jq -r '.[].hooks[]?.command // empty' 2>/dev/null)
|
|
254
|
-
ALREADY_PRESENT=false
|
|
255
|
-
|
|
256
|
-
for cmd in $FTM_COMMANDS; do
|
|
257
|
-
cmd_basename=$(basename "$cmd")
|
|
258
|
-
if echo "$EXISTING_ENTRIES" | jq -r '.[].hooks[]?.command // empty' 2>/dev/null | grep -q "$cmd_basename"; then
|
|
259
|
-
ALREADY_PRESENT=true
|
|
260
|
-
break
|
|
261
|
-
fi
|
|
262
|
-
done
|
|
263
|
-
|
|
264
|
-
if [ "$ALREADY_PRESENT" = true ]; then
|
|
265
|
-
echo " SKIP $EVENT hooks (already configured)"
|
|
266
|
-
continue
|
|
267
|
-
fi
|
|
268
|
-
|
|
269
|
-
# Append template entries to existing
|
|
270
|
-
MERGED=$(jq -n --argjson existing "$EXISTING_ENTRIES" --argjson template "$TEMPLATE_ENTRIES" '$existing + $template')
|
|
271
|
-
EXISTING=$(echo "$EXISTING" | jq --arg e "$EVENT" --argjson m "$MERGED" '.hooks[$e] = $m')
|
|
272
|
-
echo " MERGE $EVENT hooks"
|
|
273
|
-
done
|
|
274
|
-
|
|
275
|
-
echo "$EXISTING" | jq '.' > "$SETTINGS_FILE"
|
|
276
|
-
echo " UPDATED $SETTINGS_FILE"
|
|
277
|
-
fi
|
|
278
|
-
|
|
279
|
-
echo ""
|
|
280
|
-
echo " Hooks are active."
|
|
281
|
-
fi
|
|
282
|
-
fi
|
|
283
|
-
fi
|
|
284
|
-
|
|
285
|
-
# --- Verification ---
|
|
286
|
-
|
|
287
|
-
echo ""
|
|
288
|
-
echo "Verifying installation..."
|
|
289
|
-
|
|
290
|
-
ERRORS=0
|
|
291
|
-
|
|
292
|
-
# Check skill symlinks resolve
|
|
293
|
-
BROKEN_LINKS=0
|
|
294
|
-
for link in "$SKILLS_DIR"/ftm*; do
|
|
295
|
-
[ -L "$link" ] || continue
|
|
296
|
-
if [ ! -e "$link" ]; then
|
|
297
|
-
warn "broken symlink: $link"
|
|
298
|
-
BROKEN_LINKS=$((BROKEN_LINKS + 1))
|
|
299
|
-
fi
|
|
300
|
-
done
|
|
301
|
-
if [ "$BROKEN_LINKS" -eq 0 ]; then
|
|
302
|
-
echo " Skills: $SKILL_COUNT linked, all symlinks valid"
|
|
303
|
-
else
|
|
304
|
-
ERRORS=$((ERRORS + 1))
|
|
305
|
-
fi
|
|
306
|
-
|
|
307
|
-
# Check blackboard state
|
|
308
|
-
if [ -f "$STATE_DIR/blackboard/context.json" ] && [ -f "$STATE_DIR/blackboard/patterns.json" ]; then
|
|
309
|
-
echo " Blackboard: initialized"
|
|
310
|
-
else
|
|
311
|
-
warn "blackboard state incomplete"
|
|
312
|
-
ERRORS=$((ERRORS + 1))
|
|
313
|
-
fi
|
|
314
|
-
|
|
315
|
-
# Check config
|
|
316
|
-
if [ -f "$CONFIG_DIR/ftm-config.yml" ]; then
|
|
317
|
-
echo " Config: present"
|
|
318
|
-
else
|
|
319
|
-
warn "ftm-config.yml missing"
|
|
320
|
-
ERRORS=$((ERRORS + 1))
|
|
321
|
-
fi
|
|
322
|
-
|
|
323
|
-
# Check hooks (if installed)
|
|
324
|
-
if [ "$NO_HOOKS" = false ] && [ "$HOOK_COUNT" -gt 0 ]; then
|
|
325
|
-
# Verify hook files exist and are executable
|
|
326
|
-
HOOK_OK=true
|
|
327
|
-
for hook in "$HOOKS_DIR"/ftm-*.sh; do
|
|
328
|
-
[ -f "$hook" ] || continue
|
|
329
|
-
if [ ! -x "$hook" ]; then
|
|
330
|
-
warn "$(basename "$hook") not executable"
|
|
331
|
-
HOOK_OK=false
|
|
332
|
-
fi
|
|
333
|
-
done
|
|
334
|
-
|
|
335
|
-
if [ "$HOOK_OK" = true ]; then
|
|
336
|
-
echo " Hooks: $HOOK_COUNT installed, all executable"
|
|
337
|
-
else
|
|
338
|
-
ERRORS=$((ERRORS + 1))
|
|
339
|
-
fi
|
|
340
|
-
|
|
341
|
-
# Verify settings.json has FTM hooks registered
|
|
342
|
-
if [ "$SKIP_MERGE" = false ] && [ -f "$SETTINGS_FILE" ]; then
|
|
343
|
-
FTM_REGISTERED=$(grep -c 'ftm-' "$SETTINGS_FILE" 2>/dev/null || echo "0")
|
|
344
|
-
if [ "$FTM_REGISTERED" -gt 0 ]; then
|
|
345
|
-
echo " Settings: $FTM_REGISTERED FTM entries in settings.json"
|
|
346
|
-
else
|
|
347
|
-
warn "no FTM hooks found in settings.json"
|
|
348
|
-
ERRORS=$((ERRORS + 1))
|
|
349
|
-
fi
|
|
350
|
-
fi
|
|
351
|
-
fi
|
|
352
|
-
|
|
353
|
-
# --- Summary ---
|
|
354
|
-
|
|
355
|
-
echo ""
|
|
356
|
-
if [ "$ERRORS" -eq 0 ] && [ "$WARN_COUNT" -eq 0 ]; then
|
|
357
|
-
echo "Done. $SKILL_COUNT skills, $HOOK_COUNT hooks. Everything checks out."
|
|
358
|
-
else
|
|
359
|
-
echo "Done. $SKILL_COUNT skills, $HOOK_COUNT hooks. $WARN_COUNT warning(s)."
|
|
360
|
-
fi
|
|
361
|
-
echo ""
|
|
362
|
-
echo "Restart Claude Code (or start a new session) to pick up the skills."
|
|
363
|
-
echo "Try: /ftm help"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# FTM Skills Installer
|
|
5
|
+
# Creates symlinks from this repo into ~/.claude/skills/ so slash commands work.
|
|
6
|
+
# Installs hooks, merges them into settings.json, and verifies the result.
|
|
7
|
+
# Safe to re-run — idempotent.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ./install.sh # Full install (skills + hooks + settings merge)
|
|
11
|
+
# ./install.sh --no-hooks # Skills and state only, skip hooks entirely
|
|
12
|
+
# ./install.sh --skip-merge # Install hook files but don't touch settings.json
|
|
13
|
+
|
|
14
|
+
REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
15
|
+
SKILLS_DIR="$HOME/.claude/skills"
|
|
16
|
+
STATE_DIR="$HOME/.claude/ftm-state"
|
|
17
|
+
CONFIG_DIR="$HOME/.claude"
|
|
18
|
+
HOOKS_DIR="$HOME/.claude/hooks"
|
|
19
|
+
SETTINGS_FILE="$CONFIG_DIR/settings.json"
|
|
20
|
+
|
|
21
|
+
NO_HOOKS=false
|
|
22
|
+
SKIP_MERGE=false
|
|
23
|
+
for arg in "$@"; do
|
|
24
|
+
case "$arg" in
|
|
25
|
+
--no-hooks) NO_HOOKS=true ;;
|
|
26
|
+
--skip-merge) SKIP_MERGE=true ;;
|
|
27
|
+
# Keep --setup-hooks for backwards compat (now a no-op since merge is default)
|
|
28
|
+
--setup-hooks) ;;
|
|
29
|
+
esac
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
WARN_COUNT=0
|
|
33
|
+
warn() {
|
|
34
|
+
echo " WARN: $1"
|
|
35
|
+
WARN_COUNT=$((WARN_COUNT + 1))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# --- Preflight Checks ---
|
|
39
|
+
|
|
40
|
+
echo "Preflight checks..."
|
|
41
|
+
|
|
42
|
+
# Check jq (required for hooks and settings merge)
|
|
43
|
+
if ! command -v jq &>/dev/null; then
|
|
44
|
+
if [ "$NO_HOOKS" = true ]; then
|
|
45
|
+
echo " jq not found (ok — hooks skipped)"
|
|
46
|
+
else
|
|
47
|
+
echo ""
|
|
48
|
+
echo " ERROR: jq is required for FTM hooks."
|
|
49
|
+
echo ""
|
|
50
|
+
echo " Install it:"
|
|
51
|
+
echo " macOS: brew install jq"
|
|
52
|
+
echo " Ubuntu: sudo apt-get install jq"
|
|
53
|
+
echo " Alpine: apk add jq"
|
|
54
|
+
echo ""
|
|
55
|
+
echo " Or skip hooks: ./install.sh --no-hooks"
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
else
|
|
59
|
+
echo " jq: $(jq --version)"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Check node (required for event logger hook)
|
|
63
|
+
if ! command -v node &>/dev/null; then
|
|
64
|
+
if [ "$NO_HOOKS" = true ]; then
|
|
65
|
+
echo " node not found (ok — hooks skipped)"
|
|
66
|
+
else
|
|
67
|
+
echo ""
|
|
68
|
+
echo " ERROR: Node.js is required for the FTM event logger hook."
|
|
69
|
+
echo ""
|
|
70
|
+
echo " Install it: https://nodejs.org/"
|
|
71
|
+
echo " Or skip hooks: ./install.sh --no-hooks"
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
else
|
|
75
|
+
echo " node: $(node --version)"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
echo ""
|
|
79
|
+
echo "Installing FTM skills from: $REPO_DIR"
|
|
80
|
+
echo "Linking into: $SKILLS_DIR"
|
|
81
|
+
echo ""
|
|
82
|
+
|
|
83
|
+
mkdir -p "$SKILLS_DIR"
|
|
84
|
+
|
|
85
|
+
# --- Skills ---
|
|
86
|
+
|
|
87
|
+
# Link all ftm*.yml files
|
|
88
|
+
for yml in "$REPO_DIR"/ftm*.yml; do
|
|
89
|
+
[ -f "$yml" ] || continue
|
|
90
|
+
name=$(basename "$yml")
|
|
91
|
+
# Skip ftm-config.default.yml — it's a template, not a skill
|
|
92
|
+
[[ "$name" == *".default."* ]] && continue
|
|
93
|
+
target="$SKILLS_DIR/$name"
|
|
94
|
+
if [ -L "$target" ]; then
|
|
95
|
+
rm "$target"
|
|
96
|
+
elif [ -f "$target" ]; then
|
|
97
|
+
echo " SKIP $name (real file exists — back it up first)"
|
|
98
|
+
continue
|
|
99
|
+
fi
|
|
100
|
+
ln -s "$yml" "$target"
|
|
101
|
+
echo " LINK $name"
|
|
102
|
+
done
|
|
103
|
+
|
|
104
|
+
# Link all ftm* directories (skills with SKILL.md)
|
|
105
|
+
for dir in "$REPO_DIR"/ftm*/; do
|
|
106
|
+
[ -d "$dir" ] || continue
|
|
107
|
+
name=$(basename "$dir")
|
|
108
|
+
[ "$name" = "ftm-state" ] && continue # state is handled separately
|
|
109
|
+
target="$SKILLS_DIR/$name"
|
|
110
|
+
if [ -L "$target" ]; then
|
|
111
|
+
rm "$target"
|
|
112
|
+
elif [ -d "$target" ]; then
|
|
113
|
+
echo " SKIP $name/ (real directory exists — back it up first)"
|
|
114
|
+
continue
|
|
115
|
+
fi
|
|
116
|
+
ln -s "$dir" "$target"
|
|
117
|
+
echo " LINK $name/"
|
|
118
|
+
done
|
|
119
|
+
|
|
120
|
+
SKILL_COUNT=0
|
|
121
|
+
for _f in "$REPO_DIR"/ftm*.yml; do
|
|
122
|
+
[ -e "$_f" ] || continue
|
|
123
|
+
case "$_f" in *.default.*) continue ;; esac
|
|
124
|
+
SKILL_COUNT=$((SKILL_COUNT + 1))
|
|
125
|
+
done
|
|
126
|
+
echo ""
|
|
127
|
+
echo " $SKILL_COUNT skills linked."
|
|
128
|
+
|
|
129
|
+
# --- Blackboard State ---
|
|
130
|
+
|
|
131
|
+
if [ -d "$REPO_DIR/ftm-state" ]; then
|
|
132
|
+
echo ""
|
|
133
|
+
mkdir -p "$STATE_DIR/blackboard/experiences"
|
|
134
|
+
for f in "$REPO_DIR/ftm-state/blackboard"/*.json; do
|
|
135
|
+
[ -f "$f" ] || continue
|
|
136
|
+
name=$(basename "$f")
|
|
137
|
+
target="$STATE_DIR/blackboard/$name"
|
|
138
|
+
if [ ! -f "$target" ]; then
|
|
139
|
+
cp "$f" "$target"
|
|
140
|
+
echo " INIT $name (blackboard template)"
|
|
141
|
+
fi
|
|
142
|
+
done
|
|
143
|
+
idx="$STATE_DIR/blackboard/experiences/index.json"
|
|
144
|
+
if [ ! -f "$idx" ]; then
|
|
145
|
+
cp "$REPO_DIR/ftm-state/blackboard/experiences/index.json" "$idx"
|
|
146
|
+
echo " INIT experiences/index.json (blackboard template)"
|
|
147
|
+
fi
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# --- Config ---
|
|
151
|
+
|
|
152
|
+
if [ ! -f "$CONFIG_DIR/ftm-config.yml" ] && [ -f "$REPO_DIR/ftm-config.default.yml" ]; then
|
|
153
|
+
cp "$REPO_DIR/ftm-config.default.yml" "$CONFIG_DIR/ftm-config.yml"
|
|
154
|
+
echo " INIT ftm-config.yml (from default template)"
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
# --- Hooks ---
|
|
158
|
+
|
|
159
|
+
HOOK_COUNT=0
|
|
160
|
+
|
|
161
|
+
if [ "$NO_HOOKS" = true ]; then
|
|
162
|
+
echo ""
|
|
163
|
+
echo "Skipping hooks (--no-hooks)."
|
|
164
|
+
else
|
|
165
|
+
echo ""
|
|
166
|
+
echo "Installing hooks..."
|
|
167
|
+
|
|
168
|
+
if [ -d "$REPO_DIR/hooks" ]; then
|
|
169
|
+
mkdir -p "$HOOKS_DIR"
|
|
170
|
+
|
|
171
|
+
# Install shell hooks
|
|
172
|
+
for hook in "$REPO_DIR/hooks"/ftm-*.sh; do
|
|
173
|
+
[ -f "$hook" ] || continue
|
|
174
|
+
name=$(basename "$hook")
|
|
175
|
+
target="$HOOKS_DIR/$name"
|
|
176
|
+
if [ -f "$target" ]; then
|
|
177
|
+
cp "$hook" "$target"
|
|
178
|
+
chmod +x "$target"
|
|
179
|
+
echo " UPDATE $name"
|
|
180
|
+
else
|
|
181
|
+
cp "$hook" "$target"
|
|
182
|
+
chmod +x "$target"
|
|
183
|
+
echo " INSTALL $name"
|
|
184
|
+
fi
|
|
185
|
+
HOOK_COUNT=$((HOOK_COUNT + 1))
|
|
186
|
+
done
|
|
187
|
+
|
|
188
|
+
# Install Node.js hooks
|
|
189
|
+
for hook in "$REPO_DIR/hooks"/ftm-*.mjs; do
|
|
190
|
+
[ -f "$hook" ] || continue
|
|
191
|
+
name=$(basename "$hook")
|
|
192
|
+
target="$HOOKS_DIR/$name"
|
|
193
|
+
if [ -f "$target" ]; then
|
|
194
|
+
cp "$hook" "$target"
|
|
195
|
+
echo " UPDATE $name"
|
|
196
|
+
else
|
|
197
|
+
cp "$hook" "$target"
|
|
198
|
+
echo " INSTALL $name"
|
|
199
|
+
fi
|
|
200
|
+
HOOK_COUNT=$((HOOK_COUNT + 1))
|
|
201
|
+
done
|
|
202
|
+
|
|
203
|
+
echo ""
|
|
204
|
+
echo " $HOOK_COUNT hooks installed to $HOOKS_DIR"
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
# --- Hook Config Merge (default behavior now) ---
|
|
208
|
+
|
|
209
|
+
if [ "$SKIP_MERGE" = true ]; then
|
|
210
|
+
echo ""
|
|
211
|
+
echo " Skipping settings.json merge (--skip-merge)."
|
|
212
|
+
echo " Add entries from hooks/settings-template.json to ~/.claude/settings.json manually."
|
|
213
|
+
else
|
|
214
|
+
echo ""
|
|
215
|
+
echo "Registering hooks in settings.json..."
|
|
216
|
+
|
|
217
|
+
TEMPLATE="$REPO_DIR/hooks/settings-template.json"
|
|
218
|
+
if [ ! -f "$TEMPLATE" ]; then
|
|
219
|
+
warn "hooks/settings-template.json not found — hooks installed but not registered"
|
|
220
|
+
else
|
|
221
|
+
# Expand ~ to $HOME in the template (jq doesn't expand shell paths)
|
|
222
|
+
EXPANDED_TEMPLATE=$(sed "s|~/.claude|$HOME/.claude|g" "$TEMPLATE")
|
|
223
|
+
|
|
224
|
+
if [ ! -f "$SETTINGS_FILE" ]; then
|
|
225
|
+
# No settings.json — create one from the template hooks section
|
|
226
|
+
echo "$EXPANDED_TEMPLATE" | jq '{hooks: .hooks}' > "$SETTINGS_FILE"
|
|
227
|
+
echo " CREATED $SETTINGS_FILE with FTM hooks"
|
|
228
|
+
else
|
|
229
|
+
# Merge FTM hooks into existing settings.json
|
|
230
|
+
BACKUP="$SETTINGS_FILE.ftm-backup-$(date +%Y%m%d%H%M%S)"
|
|
231
|
+
cp "$SETTINGS_FILE" "$BACKUP"
|
|
232
|
+
echo " BACKUP $BACKUP"
|
|
233
|
+
|
|
234
|
+
# Extract the hooks section from the template
|
|
235
|
+
TEMPLATE_HOOKS=$(echo "$EXPANDED_TEMPLATE" | jq '.hooks')
|
|
236
|
+
|
|
237
|
+
# Read existing settings
|
|
238
|
+
EXISTING=$(cat "$SETTINGS_FILE")
|
|
239
|
+
|
|
240
|
+
# Ensure hooks key exists
|
|
241
|
+
if echo "$EXISTING" | jq -e '.hooks' >/dev/null 2>&1; then
|
|
242
|
+
: # hooks key exists
|
|
243
|
+
else
|
|
244
|
+
EXISTING=$(echo "$EXISTING" | jq '. + {hooks: {}}')
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
# Merge each hook event type
|
|
248
|
+
for EVENT in PreToolUse UserPromptSubmit PostToolUse Stop; do
|
|
249
|
+
TEMPLATE_ENTRIES=$(echo "$TEMPLATE_HOOKS" | jq --arg e "$EVENT" '.[$e] // []')
|
|
250
|
+
EXISTING_ENTRIES=$(echo "$EXISTING" | jq --arg e "$EVENT" '.hooks[$e] // []')
|
|
251
|
+
|
|
252
|
+
# Check if any FTM hooks are already present (by checking command paths)
|
|
253
|
+
FTM_COMMANDS=$(echo "$TEMPLATE_ENTRIES" | jq -r '.[].hooks[]?.command // empty' 2>/dev/null)
|
|
254
|
+
ALREADY_PRESENT=false
|
|
255
|
+
|
|
256
|
+
for cmd in $FTM_COMMANDS; do
|
|
257
|
+
cmd_basename=$(basename "$cmd")
|
|
258
|
+
if echo "$EXISTING_ENTRIES" | jq -r '.[].hooks[]?.command // empty' 2>/dev/null | grep -q "$cmd_basename"; then
|
|
259
|
+
ALREADY_PRESENT=true
|
|
260
|
+
break
|
|
261
|
+
fi
|
|
262
|
+
done
|
|
263
|
+
|
|
264
|
+
if [ "$ALREADY_PRESENT" = true ]; then
|
|
265
|
+
echo " SKIP $EVENT hooks (already configured)"
|
|
266
|
+
continue
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
# Append template entries to existing
|
|
270
|
+
MERGED=$(jq -n --argjson existing "$EXISTING_ENTRIES" --argjson template "$TEMPLATE_ENTRIES" '$existing + $template')
|
|
271
|
+
EXISTING=$(echo "$EXISTING" | jq --arg e "$EVENT" --argjson m "$MERGED" '.hooks[$e] = $m')
|
|
272
|
+
echo " MERGE $EVENT hooks"
|
|
273
|
+
done
|
|
274
|
+
|
|
275
|
+
echo "$EXISTING" | jq '.' > "$SETTINGS_FILE"
|
|
276
|
+
echo " UPDATED $SETTINGS_FILE"
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
echo ""
|
|
280
|
+
echo " Hooks are active."
|
|
281
|
+
fi
|
|
282
|
+
fi
|
|
283
|
+
fi
|
|
284
|
+
|
|
285
|
+
# --- Verification ---
|
|
286
|
+
|
|
287
|
+
echo ""
|
|
288
|
+
echo "Verifying installation..."
|
|
289
|
+
|
|
290
|
+
ERRORS=0
|
|
291
|
+
|
|
292
|
+
# Check skill symlinks resolve
|
|
293
|
+
BROKEN_LINKS=0
|
|
294
|
+
for link in "$SKILLS_DIR"/ftm*; do
|
|
295
|
+
[ -L "$link" ] || continue
|
|
296
|
+
if [ ! -e "$link" ]; then
|
|
297
|
+
warn "broken symlink: $link"
|
|
298
|
+
BROKEN_LINKS=$((BROKEN_LINKS + 1))
|
|
299
|
+
fi
|
|
300
|
+
done
|
|
301
|
+
if [ "$BROKEN_LINKS" -eq 0 ]; then
|
|
302
|
+
echo " Skills: $SKILL_COUNT linked, all symlinks valid"
|
|
303
|
+
else
|
|
304
|
+
ERRORS=$((ERRORS + 1))
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
# Check blackboard state
|
|
308
|
+
if [ -f "$STATE_DIR/blackboard/context.json" ] && [ -f "$STATE_DIR/blackboard/patterns.json" ]; then
|
|
309
|
+
echo " Blackboard: initialized"
|
|
310
|
+
else
|
|
311
|
+
warn "blackboard state incomplete"
|
|
312
|
+
ERRORS=$((ERRORS + 1))
|
|
313
|
+
fi
|
|
314
|
+
|
|
315
|
+
# Check config
|
|
316
|
+
if [ -f "$CONFIG_DIR/ftm-config.yml" ]; then
|
|
317
|
+
echo " Config: present"
|
|
318
|
+
else
|
|
319
|
+
warn "ftm-config.yml missing"
|
|
320
|
+
ERRORS=$((ERRORS + 1))
|
|
321
|
+
fi
|
|
322
|
+
|
|
323
|
+
# Check hooks (if installed)
|
|
324
|
+
if [ "$NO_HOOKS" = false ] && [ "$HOOK_COUNT" -gt 0 ]; then
|
|
325
|
+
# Verify hook files exist and are executable
|
|
326
|
+
HOOK_OK=true
|
|
327
|
+
for hook in "$HOOKS_DIR"/ftm-*.sh; do
|
|
328
|
+
[ -f "$hook" ] || continue
|
|
329
|
+
if [ ! -x "$hook" ]; then
|
|
330
|
+
warn "$(basename "$hook") not executable"
|
|
331
|
+
HOOK_OK=false
|
|
332
|
+
fi
|
|
333
|
+
done
|
|
334
|
+
|
|
335
|
+
if [ "$HOOK_OK" = true ]; then
|
|
336
|
+
echo " Hooks: $HOOK_COUNT installed, all executable"
|
|
337
|
+
else
|
|
338
|
+
ERRORS=$((ERRORS + 1))
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
# Verify settings.json has FTM hooks registered
|
|
342
|
+
if [ "$SKIP_MERGE" = false ] && [ -f "$SETTINGS_FILE" ]; then
|
|
343
|
+
FTM_REGISTERED=$(grep -c 'ftm-' "$SETTINGS_FILE" 2>/dev/null || echo "0")
|
|
344
|
+
if [ "$FTM_REGISTERED" -gt 0 ]; then
|
|
345
|
+
echo " Settings: $FTM_REGISTERED FTM entries in settings.json"
|
|
346
|
+
else
|
|
347
|
+
warn "no FTM hooks found in settings.json"
|
|
348
|
+
ERRORS=$((ERRORS + 1))
|
|
349
|
+
fi
|
|
350
|
+
fi
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
# --- Summary ---
|
|
354
|
+
|
|
355
|
+
echo ""
|
|
356
|
+
if [ "$ERRORS" -eq 0 ] && [ "$WARN_COUNT" -eq 0 ]; then
|
|
357
|
+
echo "Done. $SKILL_COUNT skills, $HOOK_COUNT hooks. Everything checks out."
|
|
358
|
+
else
|
|
359
|
+
echo "Done. $SKILL_COUNT skills, $HOOK_COUNT hooks. $WARN_COUNT warning(s)."
|
|
360
|
+
fi
|
|
361
|
+
echo ""
|
|
362
|
+
echo "Restart Claude Code (or start a new session) to pick up the skills."
|
|
363
|
+
echo "Try: /ftm help"
|