feed-the-machine 1.2.0 → 1.3.1
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/bin/install.mjs +272 -25
- package/ftm-audit/SKILL.md +383 -57
- package/ftm-brainstorm/SKILL.md +119 -51
- package/ftm-config/SKILL.md +1 -1
- package/ftm-council/SKILL.md +259 -31
- package/ftm-dashboard/SKILL.md +10 -10
- package/ftm-debug/SKILL.md +861 -54
- package/ftm-diagram/SKILL.md +1 -1
- package/ftm-executor/SKILL.md +6 -6
- package/ftm-git/SKILL.md +209 -22
- package/ftm-inbox/bin/start.sh +1 -1
- package/ftm-inbox/bin/status.sh +1 -1
- package/ftm-inbox/bin/stop.sh +1 -1
- package/ftm-intent/SKILL.md +0 -1
- package/ftm-mind/SKILL.md +861 -11
- package/ftm-mind/references/event-registry.md +30 -0
- package/ftm-pause/SKILL.md +256 -37
- package/ftm-resume/SKILL.md +380 -75
- package/ftm-retro/SKILL.md +164 -27
- package/ftm-upgrade/SKILL.md +4 -4
- package/hooks/ftm-blackboard-enforcer.sh +29 -27
- package/hooks/ftm-plan-gate.sh +21 -25
- package/install.sh +244 -112
- package/package.json +1 -1
package/install.sh
CHANGED
|
@@ -3,11 +3,13 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
# FTM Skills Installer
|
|
5
5
|
# Creates symlinks from this repo into ~/.claude/skills/ so slash commands work.
|
|
6
|
-
#
|
|
6
|
+
# Installs hooks, merges them into settings.json, and verifies the result.
|
|
7
|
+
# Safe to re-run — idempotent.
|
|
7
8
|
#
|
|
8
9
|
# Usage:
|
|
9
|
-
# ./install.sh #
|
|
10
|
-
# ./install.sh --
|
|
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
|
|
11
13
|
|
|
12
14
|
REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
13
15
|
SKILLS_DIR="$HOME/.claude/skills"
|
|
@@ -16,13 +18,64 @@ CONFIG_DIR="$HOME/.claude"
|
|
|
16
18
|
HOOKS_DIR="$HOME/.claude/hooks"
|
|
17
19
|
SETTINGS_FILE="$CONFIG_DIR/settings.json"
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
NO_HOOKS=false
|
|
22
|
+
SKIP_MERGE=false
|
|
20
23
|
for arg in "$@"; do
|
|
21
24
|
case "$arg" in
|
|
22
|
-
--
|
|
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) ;;
|
|
23
29
|
esac
|
|
24
30
|
done
|
|
25
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 ""
|
|
26
79
|
echo "Installing FTM skills from: $REPO_DIR"
|
|
27
80
|
echo "Linking into: $SKILLS_DIR"
|
|
28
81
|
echo ""
|
|
@@ -64,7 +117,12 @@ for dir in "$REPO_DIR"/ftm*/; do
|
|
|
64
117
|
echo " LINK $name/"
|
|
65
118
|
done
|
|
66
119
|
|
|
67
|
-
SKILL_COUNT
|
|
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
|
|
68
126
|
echo ""
|
|
69
127
|
echo " $SKILL_COUNT skills linked."
|
|
70
128
|
|
|
@@ -98,134 +156,208 @@ fi
|
|
|
98
156
|
|
|
99
157
|
# --- Hooks ---
|
|
100
158
|
|
|
101
|
-
|
|
102
|
-
echo "Installing hooks..."
|
|
103
|
-
|
|
104
|
-
if [ -d "$REPO_DIR/hooks" ]; then
|
|
105
|
-
mkdir -p "$HOOKS_DIR"
|
|
106
|
-
HOOK_COUNT=0
|
|
107
|
-
|
|
108
|
-
# Install shell hooks
|
|
109
|
-
for hook in "$REPO_DIR/hooks"/ftm-*.sh; do
|
|
110
|
-
[ -f "$hook" ] || continue
|
|
111
|
-
name=$(basename "$hook")
|
|
112
|
-
target="$HOOKS_DIR/$name"
|
|
113
|
-
if [ -f "$target" ]; then
|
|
114
|
-
cp "$hook" "$target"
|
|
115
|
-
chmod +x "$target"
|
|
116
|
-
echo " UPDATE $name"
|
|
117
|
-
else
|
|
118
|
-
cp "$hook" "$target"
|
|
119
|
-
chmod +x "$target"
|
|
120
|
-
echo " INSTALL $name"
|
|
121
|
-
fi
|
|
122
|
-
HOOK_COUNT=$((HOOK_COUNT + 1))
|
|
123
|
-
done
|
|
124
|
-
|
|
125
|
-
# Install Node.js hooks
|
|
126
|
-
for hook in "$REPO_DIR/hooks"/ftm-*.mjs; do
|
|
127
|
-
[ -f "$hook" ] || continue
|
|
128
|
-
name=$(basename "$hook")
|
|
129
|
-
target="$HOOKS_DIR/$name"
|
|
130
|
-
if [ -f "$target" ]; then
|
|
131
|
-
cp "$hook" "$target"
|
|
132
|
-
echo " UPDATE $name"
|
|
133
|
-
else
|
|
134
|
-
cp "$hook" "$target"
|
|
135
|
-
echo " INSTALL $name"
|
|
136
|
-
fi
|
|
137
|
-
HOOK_COUNT=$((HOOK_COUNT + 1))
|
|
138
|
-
done
|
|
159
|
+
HOOK_COUNT=0
|
|
139
160
|
|
|
161
|
+
if [ "$NO_HOOKS" = true ]; then
|
|
140
162
|
echo ""
|
|
141
|
-
echo "
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
# --- Hook Config Merge (--setup-hooks) ---
|
|
145
|
-
|
|
146
|
-
if [ "$SETUP_HOOKS" = true ]; then
|
|
163
|
+
echo "Skipping hooks (--no-hooks)."
|
|
164
|
+
else
|
|
147
165
|
echo ""
|
|
148
|
-
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
|
|
149
187
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
155
202
|
|
|
156
|
-
|
|
157
|
-
echo "
|
|
158
|
-
exit 1
|
|
203
|
+
echo ""
|
|
204
|
+
echo " $HOOK_COUNT hooks installed to $HOOKS_DIR"
|
|
159
205
|
fi
|
|
160
206
|
|
|
161
|
-
#
|
|
162
|
-
EXPANDED_TEMPLATE=$(sed "s|~/.claude|$HOME/.claude|g" "$TEMPLATE")
|
|
207
|
+
# --- Hook Config Merge (default behavior now) ---
|
|
163
208
|
|
|
164
|
-
if [
|
|
165
|
-
|
|
166
|
-
echo "
|
|
167
|
-
echo "
|
|
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."
|
|
168
213
|
else
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
BACKUP="$SETTINGS_FILE.ftm-backup-$(date +%Y%m%d%H%M%S)"
|
|
172
|
-
cp "$SETTINGS_FILE" "$BACKUP"
|
|
173
|
-
echo " BACKUP $BACKUP"
|
|
214
|
+
echo ""
|
|
215
|
+
echo "Registering hooks in settings.json..."
|
|
174
216
|
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
177
246
|
|
|
178
|
-
|
|
179
|
-
|
|
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
|
|
180
278
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
: # hooks key exists
|
|
184
|
-
else
|
|
185
|
-
EXISTING=$(echo "$EXISTING" | jq '. + {hooks: {}}')
|
|
279
|
+
echo ""
|
|
280
|
+
echo " Hooks are active."
|
|
186
281
|
fi
|
|
282
|
+
fi
|
|
283
|
+
fi
|
|
187
284
|
|
|
188
|
-
|
|
189
|
-
for EVENT in PreToolUse UserPromptSubmit PostToolUse Stop; do
|
|
190
|
-
TEMPLATE_ENTRIES=$(echo "$TEMPLATE_HOOKS" | jq --arg e "$EVENT" '.[$e] // []')
|
|
191
|
-
EXISTING_ENTRIES=$(echo "$EXISTING" | jq --arg e "$EVENT" '.hooks[$e] // []')
|
|
285
|
+
# --- Verification ---
|
|
192
286
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
ALREADY_PRESENT=false
|
|
287
|
+
echo ""
|
|
288
|
+
echo "Verifying installation..."
|
|
196
289
|
|
|
197
|
-
|
|
198
|
-
cmd_basename=$(basename "$cmd")
|
|
199
|
-
if echo "$EXISTING_ENTRIES" | jq -r '.[].hooks[]?.command // empty' 2>/dev/null | grep -q "$cmd_basename"; then
|
|
200
|
-
ALREADY_PRESENT=true
|
|
201
|
-
break
|
|
202
|
-
fi
|
|
203
|
-
done
|
|
290
|
+
ERRORS=0
|
|
204
291
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
|
209
306
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
215
314
|
|
|
216
|
-
|
|
217
|
-
|
|
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))
|
|
218
339
|
fi
|
|
219
340
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
|
227
351
|
fi
|
|
228
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
|
|
229
361
|
echo ""
|
|
230
|
-
echo "
|
|
362
|
+
echo "Restart Claude Code (or start a new session) to pick up the skills."
|
|
231
363
|
echo "Try: /ftm help"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "feed-the-machine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "A unified intelligence layer for Claude Code — 22 skills with OODA-based reasoning, persistent memory, multi-model deliberation, and optional operator cockpit inbox",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "kkudumu",
|