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/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
- # Safe to re-run idempotent. Run after cloning or adding new skills.
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 # Install skills, hooks, and state templates
10
- # ./install.sh --setup-hooks # Also merge hook config into settings.json
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
- SETUP_HOOKS=false
21
+ NO_HOOKS=false
22
+ SKIP_MERGE=false
20
23
  for arg in "$@"; do
21
24
  case "$arg" in
22
- --setup-hooks) SETUP_HOOKS=true ;;
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=$(ls "$REPO_DIR"/ftm*.yml 2>/dev/null | grep -v '.default.' | wc -l | tr -d ' ')
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
- echo ""
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 " $HOOK_COUNT hooks installed to $HOOKS_DIR"
142
- fi
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 "Setting up hook configuration in settings.json..."
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
- TEMPLATE="$REPO_DIR/hooks/settings-template.json"
151
- if [ ! -f "$TEMPLATE" ]; then
152
- echo " ERROR: hooks/settings-template.json not found"
153
- exit 1
154
- fi
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
- if ! command -v jq &>/dev/null; then
157
- echo " ERROR: jq is required for --setup-hooks. Install with: brew install jq"
158
- exit 1
203
+ echo ""
204
+ echo " $HOOK_COUNT hooks installed to $HOOKS_DIR"
159
205
  fi
160
206
 
161
- # Expand ~ to $HOME in the template (jq doesn't expand shell paths)
162
- EXPANDED_TEMPLATE=$(sed "s|~/.claude|$HOME/.claude|g" "$TEMPLATE")
207
+ # --- Hook Config Merge (default behavior now) ---
163
208
 
164
- if [ ! -f "$SETTINGS_FILE" ]; then
165
- # No settings.json — create one from the template hooks section
166
- echo "$EXPANDED_TEMPLATE" | jq '{hooks: .hooks}' > "$SETTINGS_FILE"
167
- echo " CREATED $SETTINGS_FILE with FTM hooks"
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
- # Merge FTM hooks into existing settings.json
170
- # Strategy: for each hook event type, append FTM entries that don't already exist
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
- # Extract the hooks section from the template
176
- TEMPLATE_HOOKS=$(echo "$EXPANDED_TEMPLATE" | jq '.hooks')
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
- # Read existing settings
179
- EXISTING=$(cat "$SETTINGS_FILE")
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
- # Ensure hooks key exists
182
- if echo "$EXISTING" | jq -e '.hooks' >/dev/null 2>&1; then
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
- # Merge each hook event type
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
- # Check if any FTM hooks are already present (by checking command paths)
194
- FTM_COMMANDS=$(echo "$TEMPLATE_ENTRIES" | jq -r '.[].hooks[]?.command // empty' 2>/dev/null)
195
- ALREADY_PRESENT=false
287
+ echo ""
288
+ echo "Verifying installation..."
196
289
 
197
- for cmd in $FTM_COMMANDS; do
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
- if [ "$ALREADY_PRESENT" = true ]; then
206
- echo " SKIP $EVENT hooks (already configured)"
207
- continue
208
- fi
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
- # Append template entries to existing
211
- MERGED=$(jq -n --argjson existing "$EXISTING_ENTRIES" --argjson template "$TEMPLATE_ENTRIES" '$existing + $template')
212
- EXISTING=$(echo "$EXISTING" | jq --arg e "$EVENT" --argjson m "$MERGED" '.hooks[$e] = $m')
213
- echo " MERGE $EVENT hooks"
214
- done
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
- echo "$EXISTING" | jq '.' > "$SETTINGS_FILE"
217
- echo " UPDATED $SETTINGS_FILE"
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
- echo ""
221
- echo " Hooks are now active. See docs/HOOKS.md for details."
222
- else
223
- echo ""
224
- echo " To activate hooks, run: ./install.sh --setup-hooks"
225
- echo " Or manually add entries from hooks/settings-template.json to ~/.claude/settings.json"
226
- echo " See docs/HOOKS.md for details."
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 "Done. $SKILL_COUNT skills, $HOOK_COUNT hooks."
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.2.0",
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",