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.
Files changed (269) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +170 -170
  3. package/bin/brain.py +1340 -0
  4. package/bin/convert_claude_skills_to_codex.py +490 -0
  5. package/bin/generate-manifest.mjs +463 -463
  6. package/bin/harden_codex_skills.py +141 -0
  7. package/bin/install.mjs +491 -491
  8. package/bin/migrate-eng-buddy-data.py +875 -0
  9. package/bin/playbook_engine/__init__.py +1 -0
  10. package/bin/playbook_engine/conftest.py +8 -0
  11. package/bin/playbook_engine/extractor.py +33 -0
  12. package/bin/playbook_engine/manager.py +102 -0
  13. package/bin/playbook_engine/models.py +84 -0
  14. package/bin/playbook_engine/registry.py +35 -0
  15. package/bin/playbook_engine/test_extractor.py +72 -0
  16. package/bin/playbook_engine/test_integration.py +129 -0
  17. package/bin/playbook_engine/test_manager.py +85 -0
  18. package/bin/playbook_engine/test_models.py +166 -0
  19. package/bin/playbook_engine/test_registry.py +67 -0
  20. package/bin/playbook_engine/test_tracer.py +86 -0
  21. package/bin/playbook_engine/tracer.py +93 -0
  22. package/bin/tasks_db.py +456 -0
  23. package/docs/HOOKS.md +243 -243
  24. package/docs/INBOX.md +233 -233
  25. package/ftm/SKILL.md +125 -122
  26. package/ftm-audit/SKILL.md +623 -623
  27. package/ftm-audit/references/protocols/PROJECT-PATTERNS.md +91 -91
  28. package/ftm-audit/references/protocols/RUNTIME-WIRING.md +66 -66
  29. package/ftm-audit/references/protocols/WIRING-CONTRACTS.md +135 -135
  30. package/ftm-audit/references/strategies/AUTO-FIX-STRATEGIES.md +69 -69
  31. package/ftm-audit/references/templates/REPORT-FORMAT.md +96 -96
  32. package/ftm-audit/scripts/run-knip.sh +23 -23
  33. package/ftm-audit.yml +2 -2
  34. package/ftm-brainstorm/SKILL.md +1003 -498
  35. package/ftm-brainstorm/evals/evals.json +180 -100
  36. package/ftm-brainstorm/evals/promptfoo.yaml +109 -109
  37. package/ftm-brainstorm/references/agent-prompts.md +552 -224
  38. package/ftm-brainstorm/references/plan-template.md +209 -121
  39. package/ftm-brainstorm.yml +2 -2
  40. package/ftm-browse/SKILL.md +454 -454
  41. package/ftm-browse/daemon/browser-manager.ts +206 -206
  42. package/ftm-browse/daemon/bun.lock +30 -30
  43. package/ftm-browse/daemon/cli.ts +347 -347
  44. package/ftm-browse/daemon/commands.ts +410 -410
  45. package/ftm-browse/daemon/main.ts +357 -357
  46. package/ftm-browse/daemon/package.json +17 -17
  47. package/ftm-browse/daemon/server.ts +189 -189
  48. package/ftm-browse/daemon/snapshot.ts +519 -519
  49. package/ftm-browse/daemon/tsconfig.json +22 -22
  50. package/ftm-browse.yml +4 -4
  51. package/ftm-capture/SKILL.md +370 -370
  52. package/ftm-capture.yml +4 -4
  53. package/ftm-codex-gate/SKILL.md +361 -361
  54. package/ftm-codex-gate.yml +2 -2
  55. package/ftm-config/SKILL.md +422 -345
  56. package/ftm-config.default.yml +125 -82
  57. package/ftm-config.yml +44 -2
  58. package/ftm-council/SKILL.md +416 -416
  59. package/ftm-council/references/prompts/CLAUDE-INVESTIGATION.md +60 -60
  60. package/ftm-council/references/prompts/CODEX-INVESTIGATION.md +58 -58
  61. package/ftm-council/references/prompts/GEMINI-INVESTIGATION.md +58 -58
  62. package/ftm-council/references/prompts/REBUTTAL-TEMPLATE.md +57 -57
  63. package/ftm-council/references/protocols/PREREQUISITES.md +47 -47
  64. package/ftm-council/references/protocols/STEP-0-FRAMING.md +46 -46
  65. package/ftm-council.yml +2 -2
  66. package/ftm-dashboard/SKILL.md +163 -163
  67. package/ftm-dashboard.yml +4 -4
  68. package/ftm-debug/SKILL.md +1037 -1037
  69. package/ftm-debug/references/phases/PHASE-0-INTAKE.md +58 -58
  70. package/ftm-debug/references/phases/PHASE-1-TRIAGE.md +46 -46
  71. package/ftm-debug/references/phases/PHASE-2-WAR-ROOM-AGENTS.md +279 -279
  72. package/ftm-debug/references/phases/PHASE-3-TO-6-EXECUTION.md +436 -436
  73. package/ftm-debug/references/protocols/BLACKBOARD.md +86 -86
  74. package/ftm-debug/references/protocols/EDGE-CASES.md +103 -103
  75. package/ftm-debug.yml +2 -2
  76. package/ftm-diagram/SKILL.md +277 -277
  77. package/ftm-diagram.yml +2 -2
  78. package/ftm-executor/SKILL.md +777 -777
  79. package/ftm-executor/references/STYLE-TEMPLATE.md +73 -73
  80. package/ftm-executor/references/phases/PHASE-0-VERIFICATION.md +62 -62
  81. package/ftm-executor/references/phases/PHASE-2-AGENT-ASSEMBLY.md +34 -34
  82. package/ftm-executor/references/phases/PHASE-3-WORKTREES.md +38 -38
  83. package/ftm-executor/references/phases/PHASE-4-5-AUDIT.md +72 -72
  84. package/ftm-executor/references/phases/PHASE-4-DISPATCH.md +66 -66
  85. package/ftm-executor/references/phases/PHASE-5-5-CODEX-GATE.md +73 -73
  86. package/ftm-executor/references/protocols/DOCUMENTATION-BOOTSTRAP.md +36 -36
  87. package/ftm-executor/references/protocols/MODEL-PROFILE.md +59 -59
  88. package/ftm-executor/references/protocols/PROGRESS-TRACKING.md +66 -66
  89. package/ftm-executor/runtime/ftm-runtime.mjs +252 -252
  90. package/ftm-executor/runtime/package.json +8 -8
  91. package/ftm-executor.yml +2 -2
  92. package/ftm-git/SKILL.md +441 -441
  93. package/ftm-git/evals/evals.json +26 -26
  94. package/ftm-git/evals/promptfoo.yaml +75 -75
  95. package/ftm-git/hooks/post-commit-experience.sh +92 -92
  96. package/ftm-git/references/patterns/SECRET-PATTERNS.md +104 -104
  97. package/ftm-git/references/protocols/REMEDIATION.md +139 -139
  98. package/ftm-git/scripts/pre-commit-secrets.sh +110 -110
  99. package/ftm-git.yml +2 -2
  100. package/ftm-inbox/backend/__pycache__/main.cpython-314.pyc +0 -0
  101. package/ftm-inbox/backend/adapters/_retry.py +64 -64
  102. package/ftm-inbox/backend/adapters/base.py +230 -230
  103. package/ftm-inbox/backend/adapters/freshservice.py +104 -104
  104. package/ftm-inbox/backend/adapters/gmail.py +125 -125
  105. package/ftm-inbox/backend/adapters/jira.py +136 -136
  106. package/ftm-inbox/backend/adapters/registry.py +192 -192
  107. package/ftm-inbox/backend/adapters/slack.py +110 -110
  108. package/ftm-inbox/backend/db/connection.py +54 -54
  109. package/ftm-inbox/backend/db/schema.py +78 -78
  110. package/ftm-inbox/backend/executor/__init__.py +7 -7
  111. package/ftm-inbox/backend/executor/engine.py +149 -149
  112. package/ftm-inbox/backend/executor/step_runner.py +98 -98
  113. package/ftm-inbox/backend/main.py +103 -103
  114. package/ftm-inbox/backend/models/__init__.py +1 -1
  115. package/ftm-inbox/backend/models/unified_task.py +36 -36
  116. package/ftm-inbox/backend/planner/__init__.py +6 -6
  117. package/ftm-inbox/backend/planner/__pycache__/__init__.cpython-314.pyc +0 -0
  118. package/ftm-inbox/backend/planner/__pycache__/generator.cpython-314.pyc +0 -0
  119. package/ftm-inbox/backend/planner/__pycache__/schema.cpython-314.pyc +0 -0
  120. package/ftm-inbox/backend/planner/generator.py +127 -127
  121. package/ftm-inbox/backend/planner/schema.py +34 -34
  122. package/ftm-inbox/backend/requirements.txt +5 -5
  123. package/ftm-inbox/backend/routes/__pycache__/plan.cpython-314.pyc +0 -0
  124. package/ftm-inbox/backend/routes/execute.py +186 -186
  125. package/ftm-inbox/backend/routes/health.py +52 -52
  126. package/ftm-inbox/backend/routes/inbox.py +68 -68
  127. package/ftm-inbox/backend/routes/plan.py +271 -271
  128. package/ftm-inbox/bin/launchagent.mjs +91 -91
  129. package/ftm-inbox/bin/setup.mjs +188 -188
  130. package/ftm-inbox/bin/start.sh +10 -10
  131. package/ftm-inbox/bin/status.sh +17 -17
  132. package/ftm-inbox/bin/stop.sh +8 -8
  133. package/ftm-inbox/config.example.yml +55 -55
  134. package/ftm-inbox/package-lock.json +2898 -2898
  135. package/ftm-inbox/package.json +26 -26
  136. package/ftm-inbox/postcss.config.js +6 -6
  137. package/ftm-inbox/src/app.css +199 -199
  138. package/ftm-inbox/src/app.html +18 -18
  139. package/ftm-inbox/src/lib/api.ts +166 -166
  140. package/ftm-inbox/src/lib/components/ExecutionLog.svelte +81 -81
  141. package/ftm-inbox/src/lib/components/InboxFeed.svelte +143 -143
  142. package/ftm-inbox/src/lib/components/PlanStep.svelte +271 -271
  143. package/ftm-inbox/src/lib/components/PlanView.svelte +206 -206
  144. package/ftm-inbox/src/lib/components/StreamPanel.svelte +99 -99
  145. package/ftm-inbox/src/lib/components/TaskCard.svelte +190 -190
  146. package/ftm-inbox/src/lib/components/ui/EmptyState.svelte +63 -63
  147. package/ftm-inbox/src/lib/components/ui/KawaiiCard.svelte +86 -86
  148. package/ftm-inbox/src/lib/components/ui/PillButton.svelte +106 -106
  149. package/ftm-inbox/src/lib/components/ui/StatusBadge.svelte +67 -67
  150. package/ftm-inbox/src/lib/components/ui/StreamDrawer.svelte +149 -149
  151. package/ftm-inbox/src/lib/components/ui/ThemeToggle.svelte +80 -80
  152. package/ftm-inbox/src/lib/theme.ts +47 -47
  153. package/ftm-inbox/src/routes/+layout.svelte +76 -76
  154. package/ftm-inbox/src/routes/+page.svelte +401 -401
  155. package/ftm-inbox/svelte.config.js +12 -12
  156. package/ftm-inbox/tailwind.config.ts +63 -63
  157. package/ftm-inbox/tsconfig.json +13 -13
  158. package/ftm-inbox/vite.config.ts +6 -6
  159. package/ftm-intent/SKILL.md +241 -241
  160. package/ftm-intent.yml +2 -2
  161. package/ftm-manifest.json +3794 -3794
  162. package/ftm-map/SKILL.md +291 -291
  163. package/ftm-map/scripts/db.py +712 -712
  164. package/ftm-map/scripts/index.py +415 -415
  165. package/ftm-map/scripts/parser.py +224 -224
  166. package/ftm-map/scripts/queries/go-tags.scm +20 -20
  167. package/ftm-map/scripts/queries/javascript-tags.scm +35 -35
  168. package/ftm-map/scripts/queries/python-tags.scm +31 -31
  169. package/ftm-map/scripts/queries/ruby-tags.scm +19 -19
  170. package/ftm-map/scripts/queries/rust-tags.scm +37 -37
  171. package/ftm-map/scripts/queries/typescript-tags.scm +41 -41
  172. package/ftm-map/scripts/query.py +301 -301
  173. package/ftm-map/scripts/ranker.py +377 -377
  174. package/ftm-map/scripts/requirements.txt +5 -5
  175. package/ftm-map/scripts/setup-hooks.sh +27 -27
  176. package/ftm-map/scripts/setup.sh +56 -56
  177. package/ftm-map/scripts/test_db.py +364 -364
  178. package/ftm-map/scripts/test_parser.py +174 -174
  179. package/ftm-map/scripts/test_query.py +183 -183
  180. package/ftm-map/scripts/test_ranker.py +199 -199
  181. package/ftm-map/scripts/views.py +591 -591
  182. package/ftm-map.yml +2 -2
  183. package/ftm-mind/SKILL.md +201 -1943
  184. package/ftm-mind/evals/promptfoo.yaml +142 -142
  185. package/ftm-mind/references/blackboard-protocol.md +110 -0
  186. package/ftm-mind/references/blackboard-schema.md +328 -328
  187. package/ftm-mind/references/complexity-guide.md +110 -110
  188. package/ftm-mind/references/complexity-sizing.md +138 -0
  189. package/ftm-mind/references/decide-act-protocol.md +172 -0
  190. package/ftm-mind/references/direct-execution.md +51 -0
  191. package/ftm-mind/references/environment-discovery.md +77 -0
  192. package/ftm-mind/references/event-registry.md +319 -319
  193. package/ftm-mind/references/mcp-inventory.md +300 -296
  194. package/ftm-mind/references/ops-routing.md +47 -0
  195. package/ftm-mind/references/orient-protocol.md +234 -0
  196. package/ftm-mind/references/personality.md +40 -0
  197. package/ftm-mind/references/protocols/COMPLEXITY-SIZING.md +72 -72
  198. package/ftm-mind/references/protocols/MCP-HEURISTICS.md +32 -32
  199. package/ftm-mind/references/protocols/PLAN-APPROVAL.md +80 -80
  200. package/ftm-mind/references/reflexion-protocol.md +249 -249
  201. package/ftm-mind/references/routing/SCENARIOS.md +22 -22
  202. package/ftm-mind/references/routing-scenarios.md +35 -35
  203. package/ftm-mind.yml +2 -2
  204. package/ftm-ops.yml +4 -0
  205. package/ftm-pause/SKILL.md +395 -395
  206. package/ftm-pause/references/protocols/SKILL-RESTORE-PROTOCOLS.md +186 -186
  207. package/ftm-pause/references/protocols/VALIDATION.md +80 -80
  208. package/ftm-pause.yml +2 -2
  209. package/ftm-researcher/SKILL.md +275 -275
  210. package/ftm-researcher/evals/agent-diversity.yaml +17 -17
  211. package/ftm-researcher/evals/synthesis-quality.yaml +12 -12
  212. package/ftm-researcher/evals/trigger-accuracy.yaml +39 -39
  213. package/ftm-researcher/references/adaptive-search.md +116 -116
  214. package/ftm-researcher/references/agent-prompts.md +193 -193
  215. package/ftm-researcher/references/council-integration.md +193 -193
  216. package/ftm-researcher/references/output-format.md +203 -203
  217. package/ftm-researcher/references/synthesis-pipeline.md +165 -165
  218. package/ftm-researcher/scripts/score_credibility.py +234 -234
  219. package/ftm-researcher/scripts/validate_research.py +92 -92
  220. package/ftm-researcher.yml +2 -2
  221. package/ftm-resume/SKILL.md +518 -518
  222. package/ftm-resume/references/protocols/VALIDATION.md +172 -172
  223. package/ftm-resume.yml +2 -2
  224. package/ftm-retro/SKILL.md +380 -380
  225. package/ftm-retro/references/protocols/SCORING-RUBRICS.md +89 -89
  226. package/ftm-retro/references/templates/REPORT-FORMAT.md +109 -109
  227. package/ftm-retro.yml +2 -2
  228. package/ftm-routine/SKILL.md +170 -170
  229. package/ftm-routine.yml +4 -4
  230. package/ftm-state/blackboard/capabilities.json +5 -5
  231. package/ftm-state/blackboard/capabilities.schema.json +27 -27
  232. package/ftm-state/blackboard/context.json +37 -23
  233. package/ftm-state/blackboard/experiences/doom-statusline-fix.json +26 -0
  234. package/ftm-state/blackboard/experiences/hackathon-pages-site.json +26 -0
  235. package/ftm-state/blackboard/experiences/hindsight-sso-kickoff.json +42 -0
  236. package/ftm-state/blackboard/experiences/index.json +58 -9
  237. package/ftm-state/blackboard/experiences/learning-ragnarok-api-access.json +23 -0
  238. package/ftm-state/blackboard/experiences/nordlayer-members-auto-assign.json +26 -0
  239. package/ftm-state/blackboard/experiences/saml2aws-stale-session-fix.json +41 -0
  240. package/ftm-state/blackboard/patterns.json +6 -6
  241. package/ftm-state/schemas/context.schema.json +130 -130
  242. package/ftm-state/schemas/experience-index.schema.json +77 -77
  243. package/ftm-state/schemas/experience.schema.json +78 -78
  244. package/ftm-state/schemas/patterns.schema.json +44 -44
  245. package/ftm-upgrade/SKILL.md +194 -194
  246. package/ftm-upgrade/scripts/check-version.sh +76 -76
  247. package/ftm-upgrade/scripts/upgrade.sh +143 -143
  248. package/ftm-upgrade.yml +2 -2
  249. package/ftm-verify.yml +2 -2
  250. package/ftm.yml +2 -2
  251. package/hooks/ftm-auto-log.sh +137 -0
  252. package/hooks/ftm-blackboard-enforcer.sh +93 -93
  253. package/hooks/ftm-discovery-reminder.sh +90 -90
  254. package/hooks/ftm-drafts-gate.sh +61 -61
  255. package/hooks/ftm-event-logger.mjs +107 -107
  256. package/hooks/ftm-install-hooks.sh +240 -0
  257. package/hooks/ftm-learning-capture.sh +117 -0
  258. package/hooks/ftm-map-autodetect.sh +79 -79
  259. package/hooks/ftm-pending-sync-check.sh +22 -22
  260. package/hooks/ftm-plan-gate.sh +92 -92
  261. package/hooks/ftm-post-commit-trigger.sh +57 -57
  262. package/hooks/ftm-post-compaction.sh +138 -0
  263. package/hooks/ftm-pre-compaction.sh +147 -0
  264. package/hooks/ftm-session-end.sh +52 -0
  265. package/hooks/ftm-session-snapshot.sh +213 -0
  266. package/hooks/settings-template.json +81 -81
  267. package/install.sh +363 -363
  268. package/package.json +84 -84
  269. package/uninstall.sh +25 -25
@@ -1,93 +1,93 @@
1
- #!/usr/bin/env bash
2
- # ftm-blackboard-enforcer.sh
3
- # Stop hook that nudges Claude to record an experience if meaningful work
4
- # was done but no blackboard entry was written.
5
- #
6
- # Uses additionalContext (not "decision: block") so Claude can still act on
7
- # the reminder. A blocking stop creates a deadlock — Claude can't write files
8
- # after the user ends the conversation.
9
- #
10
- # "Meaningful work" = 3+ edits tracked by the edit counter,
11
- # or ftm skills were invoked (checked via context.json).
12
- #
13
- # Hook: Stop
14
-
15
- set -euo pipefail
16
-
17
- # shellcheck disable=SC2034
18
- INPUT=$(cat)
19
-
20
- STATE_DIR="$HOME/.claude/ftm-state"
21
- BB_DIR="$STATE_DIR/blackboard"
22
- EDIT_COUNTER="$STATE_DIR/.edit-count"
23
- CONTEXT_FILE="$BB_DIR/context.json"
24
- EXPERIENCES_DIR="$BB_DIR/experiences"
25
-
26
- # Check 1: Were there meaningful edits this session?
27
- # Edit counter contains just a number now (no session ID).
28
- # If the counter file is recent (< 4 hours) and >= 3, count as meaningful.
29
- HAD_EDITS=false
30
- if [[ -f "$EDIT_COUNTER" ]]; then
31
- COUNTER_AGE=$(( $(date +%s) - $(stat -c %Y "$EDIT_COUNTER" 2>/dev/null || stat -f %m "$EDIT_COUNTER" 2>/dev/null || echo "0") ))
32
- if [[ "$COUNTER_AGE" -lt 14400 ]]; then
33
- STORED_COUNT=$(cat "$EDIT_COUNTER" 2>/dev/null || echo "0")
34
- if [[ "$STORED_COUNT" -ge 3 ]]; then
35
- HAD_EDITS=true
36
- fi
37
- fi
38
- fi
39
-
40
- # Check 2: Were ftm skills invoked this session?
41
- HAD_SKILLS=false
42
- if [[ -f "$CONTEXT_FILE" ]]; then
43
- SKILLS_COUNT=$(jq -r '.session_metadata.skills_invoked | length' "$CONTEXT_FILE" 2>/dev/null || echo "0")
44
- if [[ "$SKILLS_COUNT" -gt 0 ]]; then
45
- HAD_SKILLS=true
46
- fi
47
- fi
48
-
49
- # If no meaningful work detected, allow stop quietly
50
- if [[ "$HAD_EDITS" == "false" && "$HAD_SKILLS" == "false" ]]; then
51
- # Clean up session markers
52
- rm -f "$EDIT_COUNTER" "$STATE_DIR/.plan-presented" 2>/dev/null
53
- exit 0
54
- fi
55
-
56
- # Check 3: Was an experience recorded today?
57
- TODAY=$(date +%Y-%m-%d)
58
- HAS_EXPERIENCE=false
59
-
60
- if [[ -d "$EXPERIENCES_DIR" ]]; then
61
- TODAY_EXPERIENCE=$(find "$EXPERIENCES_DIR" -name "${TODAY}*" -type f 2>/dev/null | head -1)
62
- if [[ -n "$TODAY_EXPERIENCE" ]]; then
63
- HAS_EXPERIENCE=true
64
- fi
65
- fi
66
-
67
- # Also check if context.json was updated today (recent_decisions not empty)
68
- if [[ -f "$CONTEXT_FILE" ]]; then
69
- DECISIONS_COUNT=$(jq -r '.recent_decisions | length' "$CONTEXT_FILE" 2>/dev/null || echo "0")
70
- LAST_UPDATED=$(jq -r '.session_metadata.last_updated // ""' "$CONTEXT_FILE" 2>/dev/null || echo "")
71
- if [[ "$DECISIONS_COUNT" -gt 0 && -n "$LAST_UPDATED" ]]; then
72
- if [[ "$LAST_UPDATED" == *"$TODAY"* ]]; then
73
- HAS_EXPERIENCE=true
74
- fi
75
- fi
76
- fi
77
-
78
- if [[ "$HAS_EXPERIENCE" == "true" ]]; then
79
- # Blackboard was written, clean up and allow stop
80
- rm -f "$EDIT_COUNTER" "$STATE_DIR/.plan-presented" 2>/dev/null
81
- exit 0
82
- fi
83
-
84
- # Work was done but no blackboard write — nudge (don't block)
85
- cat <<'JSON'
86
- {
87
- "stopReason": "[ftm-blackboard-enforcer] You did meaningful work this session but did not record an experience to the blackboard. Before finishing, please: (1) Update ~/.claude/ftm-state/blackboard/context.json with current_task status and recent_decisions. (2) Write an experience file to ~/.claude/ftm-state/blackboard/experiences/ with task_type, tags, outcome, and lessons. (3) Update ~/.claude/ftm-state/blackboard/experiences/index.json with the new entry. This is how ftm learns — skipping it means the next session starts from zero."
88
- }
89
- JSON
90
-
91
- # Clean up session markers regardless — don't let stale state carry over
92
- rm -f "$EDIT_COUNTER" "$STATE_DIR/.plan-presented" 2>/dev/null
93
- exit 0
1
+ #!/usr/bin/env bash
2
+ # ftm-blackboard-enforcer.sh
3
+ # Stop hook that nudges Claude to record an experience if meaningful work
4
+ # was done but no blackboard entry was written.
5
+ #
6
+ # Uses additionalContext (not "decision: block") so Claude can still act on
7
+ # the reminder. A blocking stop creates a deadlock — Claude can't write files
8
+ # after the user ends the conversation.
9
+ #
10
+ # "Meaningful work" = 3+ edits tracked by the edit counter,
11
+ # or ftm skills were invoked (checked via context.json).
12
+ #
13
+ # Hook: Stop
14
+
15
+ set -euo pipefail
16
+
17
+ # shellcheck disable=SC2034
18
+ INPUT=$(cat)
19
+
20
+ STATE_DIR="$HOME/.claude/ftm-state"
21
+ BB_DIR="$STATE_DIR/blackboard"
22
+ EDIT_COUNTER="$STATE_DIR/.edit-count"
23
+ CONTEXT_FILE="$BB_DIR/context.json"
24
+ EXPERIENCES_DIR="$BB_DIR/experiences"
25
+
26
+ # Check 1: Were there meaningful edits this session?
27
+ # Edit counter contains just a number now (no session ID).
28
+ # If the counter file is recent (< 4 hours) and >= 3, count as meaningful.
29
+ HAD_EDITS=false
30
+ if [[ -f "$EDIT_COUNTER" ]]; then
31
+ COUNTER_AGE=$(( $(date +%s) - $(stat -c %Y "$EDIT_COUNTER" 2>/dev/null || stat -f %m "$EDIT_COUNTER" 2>/dev/null || echo "0") ))
32
+ if [[ "$COUNTER_AGE" -lt 14400 ]]; then
33
+ STORED_COUNT=$(cat "$EDIT_COUNTER" 2>/dev/null || echo "0")
34
+ if [[ "$STORED_COUNT" -ge 3 ]]; then
35
+ HAD_EDITS=true
36
+ fi
37
+ fi
38
+ fi
39
+
40
+ # Check 2: Were ftm skills invoked this session?
41
+ HAD_SKILLS=false
42
+ if [[ -f "$CONTEXT_FILE" ]]; then
43
+ SKILLS_COUNT=$(jq -r '.session_metadata.skills_invoked | length' "$CONTEXT_FILE" 2>/dev/null || echo "0")
44
+ if [[ "$SKILLS_COUNT" -gt 0 ]]; then
45
+ HAD_SKILLS=true
46
+ fi
47
+ fi
48
+
49
+ # If no meaningful work detected, allow stop quietly
50
+ if [[ "$HAD_EDITS" == "false" && "$HAD_SKILLS" == "false" ]]; then
51
+ # Clean up session markers
52
+ rm -f "$EDIT_COUNTER" "$STATE_DIR/.plan-presented" 2>/dev/null
53
+ exit 0
54
+ fi
55
+
56
+ # Check 3: Was an experience recorded today?
57
+ TODAY=$(date +%Y-%m-%d)
58
+ HAS_EXPERIENCE=false
59
+
60
+ if [[ -d "$EXPERIENCES_DIR" ]]; then
61
+ TODAY_EXPERIENCE=$(find "$EXPERIENCES_DIR" -name "${TODAY}*" -type f 2>/dev/null | head -1)
62
+ if [[ -n "$TODAY_EXPERIENCE" ]]; then
63
+ HAS_EXPERIENCE=true
64
+ fi
65
+ fi
66
+
67
+ # Also check if context.json was updated today (recent_decisions not empty)
68
+ if [[ -f "$CONTEXT_FILE" ]]; then
69
+ DECISIONS_COUNT=$(jq -r '.recent_decisions | length' "$CONTEXT_FILE" 2>/dev/null || echo "0")
70
+ LAST_UPDATED=$(jq -r '.session_metadata.last_updated // ""' "$CONTEXT_FILE" 2>/dev/null || echo "")
71
+ if [[ "$DECISIONS_COUNT" -gt 0 && -n "$LAST_UPDATED" ]]; then
72
+ if [[ "$LAST_UPDATED" == *"$TODAY"* ]]; then
73
+ HAS_EXPERIENCE=true
74
+ fi
75
+ fi
76
+ fi
77
+
78
+ if [[ "$HAS_EXPERIENCE" == "true" ]]; then
79
+ # Blackboard was written, clean up and allow stop
80
+ rm -f "$EDIT_COUNTER" "$STATE_DIR/.plan-presented" 2>/dev/null
81
+ exit 0
82
+ fi
83
+
84
+ # Work was done but no blackboard write — nudge (don't block)
85
+ cat <<'JSON'
86
+ {
87
+ "stopReason": "[ftm-blackboard-enforcer] You did meaningful work this session but did not record an experience to the blackboard. Before finishing, please: (1) Update ~/.claude/ftm-state/blackboard/context.json with current_task status and recent_decisions. (2) Write an experience file to ~/.claude/ftm-state/blackboard/experiences/ with task_type, tags, outcome, and lessons. (3) Update ~/.claude/ftm-state/blackboard/experiences/index.json with the new entry. This is how ftm learns — skipping it means the next session starts from zero."
88
+ }
89
+ JSON
90
+
91
+ # Clean up session markers regardless — don't let stale state carry over
92
+ rm -f "$EDIT_COUNTER" "$STATE_DIR/.plan-presented" 2>/dev/null
93
+ exit 0
@@ -1,90 +1,90 @@
1
- #!/usr/bin/env bash
2
- # ftm-discovery-reminder.sh
3
- # UserPromptSubmit hook that detects when a user's prompt mentions
4
- # external systems, migrations, rerouting, or stakeholder coordination.
5
- # Injects a reminder about the discovery interview before Claude starts working.
6
- #
7
- # This is a soft nudge, not a block — it adds additionalContext that
8
- # reminds Claude about the ftm-mind discovery interview protocol.
9
- #
10
- # Hook: UserPromptSubmit
11
-
12
- set -euo pipefail
13
-
14
- INPUT=$(cat)
15
- PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""')
16
-
17
- # Lowercase the prompt for matching
18
- PROMPT_LOWER=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]')
19
-
20
- # Patterns that indicate external system work requiring discovery interview
21
- EXTERNAL_PATTERNS=(
22
- "reroute"
23
- "migrate"
24
- "migration"
25
- "move.*to.*board"
26
- "move.*to.*project"
27
- "point.*to.*new"
28
- "update.*integration"
29
- "change.*endpoint"
30
- "switch.*from.*to"
31
- "redirect.*to"
32
- "jira.*automation"
33
- "freshservice.*automation"
34
- "update.*workflow"
35
- "change.*routing"
36
- "slack.*message.*to"
37
- "email.*to"
38
- "draft.*message"
39
- "notify.*about"
40
- "check.*with"
41
- "coordinate.*with"
42
- "ask.*about"
43
- "tell.*about.*change"
44
- )
45
-
46
- # Patterns that indicate the user already provided comprehensive context
47
- # (e.g., pasted a Slack thread, gave detailed instructions)
48
- CONTEXT_SIGNALS=(
49
- "here's the slack"
50
- "here's the thread"
51
- "here's what they said"
52
- "per the conversation"
53
- "just do it"
54
- "no questions"
55
- "skip the interview"
56
- "don't ask"
57
- )
58
-
59
- # Check if user already provided enough context
60
- for signal in "${CONTEXT_SIGNALS[@]}"; do
61
- if [[ "$PROMPT_LOWER" == *"$signal"* ]]; then
62
- exit 0 # User explicitly provided context or asked to skip
63
- fi
64
- done
65
-
66
- # Check for external system patterns
67
- MATCHED=false
68
- MATCHED_PATTERN=""
69
- for pattern in "${EXTERNAL_PATTERNS[@]}"; do
70
- if echo "$PROMPT_LOWER" | grep -qE "$pattern"; then
71
- MATCHED=true
72
- MATCHED_PATTERN="$pattern"
73
- break
74
- fi
75
- done
76
-
77
- if [[ "$MATCHED" == "false" ]]; then
78
- exit 0 # No external system work detected
79
- fi
80
-
81
- # Inject discovery interview reminder
82
- cat <<JSON
83
- {
84
- "hookSpecificOutput": {
85
- "hookEventName": "UserPromptSubmit",
86
- "additionalContext": "[ftm-discovery-reminder] This request involves external systems or stakeholder coordination (matched: ${MATCHED_PATTERN}). Before generating a plan or starting work, run the Discovery Interview from ftm-mind section 10. Ask 2-4 focused questions about: (1) who else needs to know, (2) downstream dependencies, (3) anything to leave as-is, (4) timeline/approval constraints. The user's answers should feed into the plan. Skip the interview ONLY if the user already provided comprehensive context in their message or explicitly said to skip it."
87
- }
88
- }
89
- JSON
90
- exit 0
1
+ #!/usr/bin/env bash
2
+ # ftm-discovery-reminder.sh
3
+ # UserPromptSubmit hook that detects when a user's prompt mentions
4
+ # external systems, migrations, rerouting, or stakeholder coordination.
5
+ # Injects a reminder about the discovery interview before Claude starts working.
6
+ #
7
+ # This is a soft nudge, not a block — it adds additionalContext that
8
+ # reminds Claude about the ftm-mind discovery interview protocol.
9
+ #
10
+ # Hook: UserPromptSubmit
11
+
12
+ set -euo pipefail
13
+
14
+ INPUT=$(cat)
15
+ PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""')
16
+
17
+ # Lowercase the prompt for matching
18
+ PROMPT_LOWER=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]')
19
+
20
+ # Patterns that indicate external system work requiring discovery interview
21
+ EXTERNAL_PATTERNS=(
22
+ "reroute"
23
+ "migrate"
24
+ "migration"
25
+ "move.*to.*board"
26
+ "move.*to.*project"
27
+ "point.*to.*new"
28
+ "update.*integration"
29
+ "change.*endpoint"
30
+ "switch.*from.*to"
31
+ "redirect.*to"
32
+ "jira.*automation"
33
+ "freshservice.*automation"
34
+ "update.*workflow"
35
+ "change.*routing"
36
+ "slack.*message.*to"
37
+ "email.*to"
38
+ "draft.*message"
39
+ "notify.*about"
40
+ "check.*with"
41
+ "coordinate.*with"
42
+ "ask.*about"
43
+ "tell.*about.*change"
44
+ )
45
+
46
+ # Patterns that indicate the user already provided comprehensive context
47
+ # (e.g., pasted a Slack thread, gave detailed instructions)
48
+ CONTEXT_SIGNALS=(
49
+ "here's the slack"
50
+ "here's the thread"
51
+ "here's what they said"
52
+ "per the conversation"
53
+ "just do it"
54
+ "no questions"
55
+ "skip the interview"
56
+ "don't ask"
57
+ )
58
+
59
+ # Check if user already provided enough context
60
+ for signal in "${CONTEXT_SIGNALS[@]}"; do
61
+ if [[ "$PROMPT_LOWER" == *"$signal"* ]]; then
62
+ exit 0 # User explicitly provided context or asked to skip
63
+ fi
64
+ done
65
+
66
+ # Check for external system patterns
67
+ MATCHED=false
68
+ MATCHED_PATTERN=""
69
+ for pattern in "${EXTERNAL_PATTERNS[@]}"; do
70
+ if echo "$PROMPT_LOWER" | grep -qE "$pattern"; then
71
+ MATCHED=true
72
+ MATCHED_PATTERN="$pattern"
73
+ break
74
+ fi
75
+ done
76
+
77
+ if [[ "$MATCHED" == "false" ]]; then
78
+ exit 0 # No external system work detected
79
+ fi
80
+
81
+ # Inject discovery interview reminder
82
+ cat <<JSON
83
+ {
84
+ "hookSpecificOutput": {
85
+ "hookEventName": "UserPromptSubmit",
86
+ "additionalContext": "[ftm-discovery-reminder] This request involves external systems or stakeholder coordination (matched: ${MATCHED_PATTERN}). Before generating a plan or starting work, run the Discovery Interview from ftm-mind section 10. Ask 2-4 focused questions about: (1) who else needs to know, (2) downstream dependencies, (3) anything to leave as-is, (4) timeline/approval constraints. The user's answers should feed into the plan. Skip the interview ONLY if the user already provided comprehensive context in their message or explicitly said to skip it."
87
+ }
88
+ }
89
+ JSON
90
+ exit 0
@@ -1,61 +1,61 @@
1
- #!/usr/bin/env bash
2
- # ftm-drafts-gate.sh
3
- # PreToolUse hook for Slack/Gmail send tools.
4
- #
5
- # Before allowing a message to be sent via Slack or Gmail, checks that
6
- # a corresponding draft exists in .ftm-drafts/ (project root) or
7
- # ~/.claude/ftm-drafts/ (global fallback).
8
- #
9
- # If no draft exists, blocks the send and tells Claude to save the draft first.
10
- #
11
- # Hook: PreToolUse (matcher: mcp__slack__slack_post_message|mcp__slack__slack_reply_to_thread|mcp__gmail__send_email)
12
-
13
- set -euo pipefail
14
-
15
- INPUT=$(cat)
16
- TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
17
-
18
- # Only gate send tools (not reads/searches)
19
- case "$TOOL_NAME" in
20
- *slack_post_message*|*slack_reply_to_thread*|*send_email*)
21
- ;;
22
- *)
23
- exit 0 # Not a send tool, allow
24
- ;;
25
- esac
26
-
27
- # Check for drafts in project .ftm-drafts/ or global ~/.claude/ftm-drafts/
28
- CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
29
- PROJECT_DRAFTS="${CWD}/.ftm-drafts"
30
- GLOBAL_DRAFTS="$HOME/.claude/ftm-drafts"
31
-
32
- HAS_RECENT_DRAFT=false
33
-
34
- # Check both locations for any draft file modified in the last 30 minutes
35
- for DRAFTS_DIR in "$PROJECT_DRAFTS" "$GLOBAL_DRAFTS"; do
36
- if [[ -d "$DRAFTS_DIR" ]]; then
37
- # Find draft files modified in the last 30 minutes
38
- RECENT=$(find "$DRAFTS_DIR" -name "*.md" -mmin -30 2>/dev/null | head -1)
39
- if [[ -n "$RECENT" ]]; then
40
- HAS_RECENT_DRAFT=true
41
- break
42
- fi
43
- fi
44
- done
45
-
46
- if [[ "$HAS_RECENT_DRAFT" == "true" ]]; then
47
- # Draft exists, allow the send (the external-action-guard will still prompt for approval)
48
- exit 0
49
- fi
50
-
51
- # No draft found — block and tell Claude to save one first
52
- cat <<JSON
53
- {
54
- "hookSpecificOutput": {
55
- "hookEventName": "PreToolUse",
56
- "permissionDecision": "deny",
57
- "permissionDecisionReason": "[ftm-drafts-gate] No draft found in .ftm-drafts/ before sending. Save the message to .ftm-drafts/ first (using the draft-before-send protocol from ftm-mind section 3.5), then retry the send. This creates an audit trail and lets the user review before sending."
58
- }
59
- }
60
- JSON
61
- exit 0
1
+ #!/usr/bin/env bash
2
+ # ftm-drafts-gate.sh
3
+ # PreToolUse hook for Slack/Gmail send tools.
4
+ #
5
+ # Before allowing a message to be sent via Slack or Gmail, checks that
6
+ # a corresponding draft exists in .ftm-drafts/ (project root) or
7
+ # ~/.claude/ftm-drafts/ (global fallback).
8
+ #
9
+ # If no draft exists, blocks the send and tells Claude to save the draft first.
10
+ #
11
+ # Hook: PreToolUse (matcher: mcp__slack__slack_post_message|mcp__slack__slack_reply_to_thread|mcp__gmail__send_email)
12
+
13
+ set -euo pipefail
14
+
15
+ INPUT=$(cat)
16
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
17
+
18
+ # Only gate send tools (not reads/searches)
19
+ case "$TOOL_NAME" in
20
+ *slack_post_message*|*slack_reply_to_thread*|*send_email*)
21
+ ;;
22
+ *)
23
+ exit 0 # Not a send tool, allow
24
+ ;;
25
+ esac
26
+
27
+ # Check for drafts in project .ftm-drafts/ or global ~/.claude/ftm-drafts/
28
+ CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
29
+ PROJECT_DRAFTS="${CWD}/.ftm-drafts"
30
+ GLOBAL_DRAFTS="$HOME/.claude/ftm-drafts"
31
+
32
+ HAS_RECENT_DRAFT=false
33
+
34
+ # Check both locations for any draft file modified in the last 30 minutes
35
+ for DRAFTS_DIR in "$PROJECT_DRAFTS" "$GLOBAL_DRAFTS"; do
36
+ if [[ -d "$DRAFTS_DIR" ]]; then
37
+ # Find draft files modified in the last 30 minutes
38
+ RECENT=$(find "$DRAFTS_DIR" -name "*.md" -mmin -30 2>/dev/null | head -1)
39
+ if [[ -n "$RECENT" ]]; then
40
+ HAS_RECENT_DRAFT=true
41
+ break
42
+ fi
43
+ fi
44
+ done
45
+
46
+ if [[ "$HAS_RECENT_DRAFT" == "true" ]]; then
47
+ # Draft exists, allow the send (the external-action-guard will still prompt for approval)
48
+ exit 0
49
+ fi
50
+
51
+ # No draft found — block and tell Claude to save one first
52
+ cat <<JSON
53
+ {
54
+ "hookSpecificOutput": {
55
+ "hookEventName": "PreToolUse",
56
+ "permissionDecision": "deny",
57
+ "permissionDecisionReason": "[ftm-drafts-gate] No draft found in .ftm-drafts/ before sending. Save the message to .ftm-drafts/ first (using the draft-before-send protocol from ftm-mind section 3.5), then retry the send. This creates an audit trail and lets the user review before sending."
58
+ }
59
+ }
60
+ JSON
61
+ exit 0