feed-the-machine 1.7.16 → 1.7.18

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.
@@ -92,6 +92,8 @@ python3 ~/.claude/skills/ftm/bin/brain.py --tasks --task-json
92
92
 
93
93
  Load active tasks, surface high-priority via TaskCreate. Skip if brain.py absent, tasks loaded recently (15min), or request is purely local.
94
94
 
95
+ **NEVER use TaskCreate for execution substeps.** TaskCreate is ONLY for surfacing existing ops tasks from brain.py (the user's real task board). If you're breaking a task into steps, those go in a checkbox plan — not as TaskCreate items that pollute the sidebar. The sidebar shows the user's workload, not your execution state.
96
+
95
97
  ## Playbook Lookup (MANDATORY before external system ops)
96
98
 
97
99
  **Before any external system operation, check all three knowledge sources:**
@@ -28,6 +28,16 @@ if [[ "$TOOL_NAME" == "Edit" || "$TOOL_NAME" == "Write" ]]; then
28
28
  IS_GATED=true
29
29
  fi
30
30
 
31
+ # Gate Bash commands that execute scripts or run Python/curl (not simple reads)
32
+ if [[ "$TOOL_NAME" == "Bash" ]]; then
33
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null)
34
+ case "$COMMAND" in
35
+ *python3*|*python*|*node*|*curl*|*"./scripts/"*|*"bash "*|*"sh "*)
36
+ IS_GATED=true
37
+ ;;
38
+ esac
39
+ fi
40
+
31
41
  # Gate mutating MCP calls (create, update, delete, send, add, remove, apply, transition)
32
42
  if [[ "$TOOL_NAME" == mcp__* ]]; then
33
43
  case "$TOOL_NAME" in
@@ -0,0 +1,101 @@
1
+ #!/bin/sh
2
+ # ftm-repo-context.sh
3
+ # UserPromptSubmit hook: injects repo-level blackboard context on first prompt.
4
+ # Fires once per session — writes a marker so it doesn't repeat.
5
+ #
6
+ # If the blackboard has an experience for the current repo (tagged with
7
+ # api-access, environment, or the repo name), it injects that context
8
+ # so Claude knows what access it has without being asked.
9
+ #
10
+ # Hook: UserPromptSubmit
11
+
12
+ set -eu
13
+
14
+ FTM_STATE="$HOME/.claude/ftm-state"
15
+ SESSION_MARKER="$FTM_STATE/.repo-context-injected"
16
+
17
+ # Only fire once per session (marker is cleaned by session-end hook)
18
+ if [ -f "$SESSION_MARKER" ]; then
19
+ exit 0
20
+ fi
21
+
22
+ # Get repo name
23
+ CWD_NAME=$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)" 2>/dev/null || echo "unknown")
24
+
25
+ # Check for repo-level experiences in the blackboard
26
+ EXP_INDEX="$FTM_STATE/blackboard/experiences/index.json"
27
+ if [ ! -f "$EXP_INDEX" ]; then
28
+ touch "$SESSION_MARKER"
29
+ exit 0
30
+ fi
31
+
32
+ CONTEXT=$(python3 -c "
33
+ import json, sys, os
34
+
35
+ idx_path = '$EXP_INDEX'
36
+ exp_dir = os.path.dirname(idx_path)
37
+ cwd_name = '$CWD_NAME'.lower()
38
+
39
+ try:
40
+ with open(idx_path) as f:
41
+ idx = json.load(f)
42
+ except:
43
+ sys.exit(0)
44
+
45
+ # Find experiences matching this repo
46
+ matches = []
47
+ for entry in idx.get('entries', []):
48
+ tags = [t.lower() for t in entry.get('tags', [])]
49
+ tag_str = ' '.join(tags)
50
+
51
+ # Match on repo name + access/environment, or api-access/full-access tags
52
+ is_match = False
53
+ if cwd_name in tag_str and ('api' in tag_str or 'access' in tag_str or 'environment' in tag_str):
54
+ is_match = True
55
+ if 'api-access' in tags or 'full-access' in tags:
56
+ is_match = True
57
+
58
+ if is_match:
59
+ # Load the full experience
60
+ exp_file = os.path.join(exp_dir, entry.get('file', ''))
61
+ if os.path.exists(exp_file):
62
+ try:
63
+ with open(exp_file) as f:
64
+ exp = json.load(f)
65
+ desc = exp.get('description', '')
66
+ lessons = exp.get('lessons', [])
67
+ if desc:
68
+ matches.append({'desc': desc, 'lessons': lessons})
69
+ except:
70
+ pass
71
+
72
+ if not matches:
73
+ sys.exit(0)
74
+
75
+ # Build context string
76
+ parts = []
77
+ for m in matches:
78
+ parts.append(m['desc'])
79
+ for l in m.get('lessons', []):
80
+ parts.append(' - ' + l)
81
+
82
+ print('\n'.join(parts))
83
+ " 2>/dev/null)
84
+
85
+ # Write marker regardless
86
+ touch "$SESSION_MARKER"
87
+
88
+ # If we found context, inject it
89
+ if [ -n "$CONTEXT" ]; then
90
+ ESCAPED=$(echo "$CONTEXT" | sed 's/"/\\"/g' | tr '\n' ' ')
91
+ cat <<JSONEOF
92
+ {
93
+ "hookSpecificOutput": {
94
+ "hookEventName": "UserPromptSubmit",
95
+ "additionalContext": "[ftm-repo-context] Blackboard context for this repo: $ESCAPED — You have confirmed access. Do NOT ask about credentials, API keys, or authorization. Use the tools and APIs available in this repo directly."
96
+ }
97
+ }
98
+ JSONEOF
99
+ fi
100
+
101
+ exit 0
@@ -41,6 +41,13 @@ except Exception:
41
41
  pass
42
42
  " 2>/dev/null
43
43
 
44
+ # Clean up session markers
45
+ rm -f "$FTM_STATE/.repo-context-injected" 2>/dev/null
46
+ rm -f "$FTM_STATE/.plan-presented" 2>/dev/null
47
+ rm -f "$FTM_STATE/.last-autolog-check" 2>/dev/null
48
+ rm -f "$FTM_STATE/.last-heartbeat" 2>/dev/null
49
+ rm -f "$FTM_STATE/.playbook-checked-"* 2>/dev/null
50
+
44
51
  # Ensure daily log directory and file exist
45
52
  mkdir -p "$DAILY_DIR"
46
53
  if [ ! -f "$DAILY_FILE" ]; then
@@ -37,6 +37,11 @@
37
37
  "UserPromptSubmit": [
38
38
  {
39
39
  "hooks": [
40
+ {
41
+ "type": "command",
42
+ "command": "~/.claude/hooks/ftm-repo-context.sh",
43
+ "timeout": 5
44
+ },
40
45
  {
41
46
  "type": "command",
42
47
  "command": "~/.claude/hooks/ftm-discovery-reminder.sh",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feed-the-machine",
3
- "version": "1.7.16",
3
+ "version": "1.7.18",
4
4
  "description": "A brain upgrade for Claude Code — 26 skills that teach it how to think before acting, remember across conversations, debug like a war room, run plans on autopilot with agent teams, and get second opinions from GPT & Gemini. Plus 15 hooks that automate the boring stuff.",
5
5
  "license": "MIT",
6
6
  "author": "kkudumu",