feed-the-machine 1.6.1 → 1.7.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.
Files changed (272) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +262 -170
  3. package/bin/__pycache__/tasks_db.cpython-314.pyc +0 -0
  4. package/bin/brain.py +1340 -0
  5. package/bin/convert_claude_skills_to_codex.py +490 -0
  6. package/bin/generate-manifest.mjs +463 -463
  7. package/bin/harden_codex_skills.py +141 -0
  8. package/bin/install.mjs +491 -491
  9. package/bin/migrate-eng-buddy-data.py +875 -0
  10. package/bin/playbook_engine/__init__.py +1 -0
  11. package/bin/playbook_engine/conftest.py +8 -0
  12. package/bin/playbook_engine/extractor.py +33 -0
  13. package/bin/playbook_engine/manager.py +102 -0
  14. package/bin/playbook_engine/models.py +84 -0
  15. package/bin/playbook_engine/registry.py +35 -0
  16. package/bin/playbook_engine/test_extractor.py +72 -0
  17. package/bin/playbook_engine/test_integration.py +129 -0
  18. package/bin/playbook_engine/test_manager.py +85 -0
  19. package/bin/playbook_engine/test_models.py +166 -0
  20. package/bin/playbook_engine/test_registry.py +67 -0
  21. package/bin/playbook_engine/test_tracer.py +86 -0
  22. package/bin/playbook_engine/tracer.py +93 -0
  23. package/bin/tasks_db.py +456 -0
  24. package/docs/HOOKS.md +243 -243
  25. package/docs/INBOX.md +233 -233
  26. package/ftm/SKILL.md +125 -122
  27. package/ftm-audit/SKILL.md +673 -623
  28. package/ftm-audit/references/protocols/PROJECT-PATTERNS.md +91 -91
  29. package/ftm-audit/references/protocols/RUNTIME-WIRING.md +66 -66
  30. package/ftm-audit/references/protocols/WIRING-CONTRACTS.md +135 -135
  31. package/ftm-audit/references/strategies/AUTO-FIX-STRATEGIES.md +69 -69
  32. package/ftm-audit/references/templates/REPORT-FORMAT.md +96 -96
  33. package/ftm-audit/scripts/run-knip.sh +23 -23
  34. package/ftm-audit.yml +2 -2
  35. package/ftm-brainstorm/SKILL.md +1003 -498
  36. package/ftm-brainstorm/evals/evals.json +180 -100
  37. package/ftm-brainstorm/evals/promptfoo.yaml +109 -109
  38. package/ftm-brainstorm/references/agent-prompts.md +552 -224
  39. package/ftm-brainstorm/references/plan-template.md +209 -121
  40. package/ftm-brainstorm.yml +2 -2
  41. package/ftm-browse/SKILL.md +454 -454
  42. package/ftm-browse/daemon/browser-manager.ts +206 -206
  43. package/ftm-browse/daemon/bun.lock +30 -30
  44. package/ftm-browse/daemon/cli.ts +347 -347
  45. package/ftm-browse/daemon/commands.ts +410 -410
  46. package/ftm-browse/daemon/main.ts +357 -357
  47. package/ftm-browse/daemon/package.json +17 -17
  48. package/ftm-browse/daemon/server.ts +189 -189
  49. package/ftm-browse/daemon/snapshot.ts +519 -519
  50. package/ftm-browse/daemon/tsconfig.json +22 -22
  51. package/ftm-browse.yml +4 -4
  52. package/ftm-capture/SKILL.md +370 -370
  53. package/ftm-capture.yml +4 -4
  54. package/ftm-codex-gate/SKILL.md +361 -361
  55. package/ftm-codex-gate.yml +2 -2
  56. package/ftm-config/SKILL.md +422 -345
  57. package/ftm-config.default.yml +125 -82
  58. package/ftm-config.yml +44 -2
  59. package/ftm-council/SKILL.md +416 -416
  60. package/ftm-council/references/prompts/CLAUDE-INVESTIGATION.md +60 -60
  61. package/ftm-council/references/prompts/CODEX-INVESTIGATION.md +58 -58
  62. package/ftm-council/references/prompts/GEMINI-INVESTIGATION.md +58 -58
  63. package/ftm-council/references/prompts/REBUTTAL-TEMPLATE.md +57 -57
  64. package/ftm-council/references/protocols/PREREQUISITES.md +47 -47
  65. package/ftm-council/references/protocols/STEP-0-FRAMING.md +46 -46
  66. package/ftm-council-chat.yml +2 -0
  67. package/ftm-council.yml +2 -2
  68. package/ftm-dashboard/SKILL.md +163 -163
  69. package/ftm-dashboard.yml +4 -4
  70. package/ftm-debug/SKILL.md +1037 -1037
  71. package/ftm-debug/references/phases/PHASE-0-INTAKE.md +58 -58
  72. package/ftm-debug/references/phases/PHASE-1-TRIAGE.md +46 -46
  73. package/ftm-debug/references/phases/PHASE-2-WAR-ROOM-AGENTS.md +279 -279
  74. package/ftm-debug/references/phases/PHASE-3-TO-6-EXECUTION.md +436 -436
  75. package/ftm-debug/references/protocols/BLACKBOARD.md +86 -86
  76. package/ftm-debug/references/protocols/EDGE-CASES.md +103 -103
  77. package/ftm-debug.yml +2 -2
  78. package/ftm-diagram/SKILL.md +277 -277
  79. package/ftm-diagram.yml +2 -2
  80. package/ftm-executor/SKILL.md +777 -777
  81. package/ftm-executor/references/STYLE-TEMPLATE.md +73 -73
  82. package/ftm-executor/references/phases/PHASE-0-VERIFICATION.md +62 -62
  83. package/ftm-executor/references/phases/PHASE-2-AGENT-ASSEMBLY.md +34 -34
  84. package/ftm-executor/references/phases/PHASE-3-WORKTREES.md +38 -38
  85. package/ftm-executor/references/phases/PHASE-4-5-AUDIT.md +81 -72
  86. package/ftm-executor/references/phases/PHASE-4-DISPATCH.md +66 -66
  87. package/ftm-executor/references/phases/PHASE-5-5-CODEX-GATE.md +73 -73
  88. package/ftm-executor/references/protocols/DOCUMENTATION-BOOTSTRAP.md +36 -36
  89. package/ftm-executor/references/protocols/MODEL-PROFILE.md +59 -59
  90. package/ftm-executor/references/protocols/PROGRESS-TRACKING.md +66 -66
  91. package/ftm-executor/runtime/ftm-runtime.mjs +252 -252
  92. package/ftm-executor/runtime/package.json +8 -8
  93. package/ftm-executor.yml +2 -2
  94. package/ftm-git/SKILL.md +441 -441
  95. package/ftm-git/evals/evals.json +26 -26
  96. package/ftm-git/evals/promptfoo.yaml +75 -75
  97. package/ftm-git/hooks/post-commit-experience.sh +92 -92
  98. package/ftm-git/references/patterns/SECRET-PATTERNS.md +104 -104
  99. package/ftm-git/references/protocols/REMEDIATION.md +139 -139
  100. package/ftm-git/scripts/pre-commit-secrets.sh +110 -110
  101. package/ftm-git.yml +2 -2
  102. package/ftm-inbox/backend/__pycache__/main.cpython-314.pyc +0 -0
  103. package/ftm-inbox/backend/adapters/_retry.py +64 -64
  104. package/ftm-inbox/backend/adapters/base.py +230 -230
  105. package/ftm-inbox/backend/adapters/freshservice.py +104 -104
  106. package/ftm-inbox/backend/adapters/gmail.py +125 -125
  107. package/ftm-inbox/backend/adapters/jira.py +136 -136
  108. package/ftm-inbox/backend/adapters/registry.py +192 -192
  109. package/ftm-inbox/backend/adapters/slack.py +110 -110
  110. package/ftm-inbox/backend/db/connection.py +54 -54
  111. package/ftm-inbox/backend/db/schema.py +78 -78
  112. package/ftm-inbox/backend/executor/__init__.py +7 -7
  113. package/ftm-inbox/backend/executor/engine.py +149 -149
  114. package/ftm-inbox/backend/executor/step_runner.py +98 -98
  115. package/ftm-inbox/backend/main.py +103 -103
  116. package/ftm-inbox/backend/models/__init__.py +1 -1
  117. package/ftm-inbox/backend/models/unified_task.py +36 -36
  118. package/ftm-inbox/backend/planner/__init__.py +6 -6
  119. package/ftm-inbox/backend/planner/__pycache__/__init__.cpython-314.pyc +0 -0
  120. package/ftm-inbox/backend/planner/__pycache__/generator.cpython-314.pyc +0 -0
  121. package/ftm-inbox/backend/planner/__pycache__/schema.cpython-314.pyc +0 -0
  122. package/ftm-inbox/backend/planner/generator.py +127 -127
  123. package/ftm-inbox/backend/planner/schema.py +34 -34
  124. package/ftm-inbox/backend/requirements.txt +5 -5
  125. package/ftm-inbox/backend/routes/__pycache__/plan.cpython-314.pyc +0 -0
  126. package/ftm-inbox/backend/routes/execute.py +186 -186
  127. package/ftm-inbox/backend/routes/health.py +52 -52
  128. package/ftm-inbox/backend/routes/inbox.py +68 -68
  129. package/ftm-inbox/backend/routes/plan.py +271 -271
  130. package/ftm-inbox/bin/launchagent.mjs +91 -91
  131. package/ftm-inbox/bin/setup.mjs +188 -188
  132. package/ftm-inbox/bin/start.sh +10 -10
  133. package/ftm-inbox/bin/status.sh +17 -17
  134. package/ftm-inbox/bin/stop.sh +8 -8
  135. package/ftm-inbox/config.example.yml +55 -55
  136. package/ftm-inbox/package-lock.json +2898 -2898
  137. package/ftm-inbox/package.json +26 -26
  138. package/ftm-inbox/postcss.config.js +6 -6
  139. package/ftm-inbox/src/app.css +199 -199
  140. package/ftm-inbox/src/app.html +18 -18
  141. package/ftm-inbox/src/lib/api.ts +166 -166
  142. package/ftm-inbox/src/lib/components/ExecutionLog.svelte +81 -81
  143. package/ftm-inbox/src/lib/components/InboxFeed.svelte +143 -143
  144. package/ftm-inbox/src/lib/components/PlanStep.svelte +271 -271
  145. package/ftm-inbox/src/lib/components/PlanView.svelte +206 -206
  146. package/ftm-inbox/src/lib/components/StreamPanel.svelte +99 -99
  147. package/ftm-inbox/src/lib/components/TaskCard.svelte +190 -190
  148. package/ftm-inbox/src/lib/components/ui/EmptyState.svelte +63 -63
  149. package/ftm-inbox/src/lib/components/ui/KawaiiCard.svelte +86 -86
  150. package/ftm-inbox/src/lib/components/ui/PillButton.svelte +106 -106
  151. package/ftm-inbox/src/lib/components/ui/StatusBadge.svelte +67 -67
  152. package/ftm-inbox/src/lib/components/ui/StreamDrawer.svelte +149 -149
  153. package/ftm-inbox/src/lib/components/ui/ThemeToggle.svelte +80 -80
  154. package/ftm-inbox/src/lib/theme.ts +47 -47
  155. package/ftm-inbox/src/routes/+layout.svelte +76 -76
  156. package/ftm-inbox/src/routes/+page.svelte +401 -401
  157. package/ftm-inbox/svelte.config.js +12 -12
  158. package/ftm-inbox/tailwind.config.ts +63 -63
  159. package/ftm-inbox/tsconfig.json +13 -13
  160. package/ftm-inbox/vite.config.ts +6 -6
  161. package/ftm-intent/SKILL.md +241 -241
  162. package/ftm-intent.yml +2 -2
  163. package/ftm-manifest.json +3794 -3794
  164. package/ftm-map/SKILL.md +291 -291
  165. package/ftm-map/scripts/db.py +712 -712
  166. package/ftm-map/scripts/index.py +415 -415
  167. package/ftm-map/scripts/parser.py +224 -224
  168. package/ftm-map/scripts/queries/go-tags.scm +20 -20
  169. package/ftm-map/scripts/queries/javascript-tags.scm +35 -35
  170. package/ftm-map/scripts/queries/python-tags.scm +31 -31
  171. package/ftm-map/scripts/queries/ruby-tags.scm +19 -19
  172. package/ftm-map/scripts/queries/rust-tags.scm +37 -37
  173. package/ftm-map/scripts/queries/typescript-tags.scm +41 -41
  174. package/ftm-map/scripts/query.py +301 -301
  175. package/ftm-map/scripts/ranker.py +377 -377
  176. package/ftm-map/scripts/requirements.txt +5 -5
  177. package/ftm-map/scripts/setup-hooks.sh +27 -27
  178. package/ftm-map/scripts/setup.sh +56 -56
  179. package/ftm-map/scripts/test_db.py +364 -364
  180. package/ftm-map/scripts/test_parser.py +174 -174
  181. package/ftm-map/scripts/test_query.py +183 -183
  182. package/ftm-map/scripts/test_ranker.py +199 -199
  183. package/ftm-map/scripts/views.py +591 -591
  184. package/ftm-map.yml +2 -2
  185. package/ftm-mind/SKILL.md +201 -1943
  186. package/ftm-mind/evals/promptfoo.yaml +142 -142
  187. package/ftm-mind/references/blackboard-protocol.md +110 -0
  188. package/ftm-mind/references/blackboard-schema.md +328 -328
  189. package/ftm-mind/references/complexity-guide.md +110 -110
  190. package/ftm-mind/references/complexity-sizing.md +138 -0
  191. package/ftm-mind/references/decide-act-protocol.md +172 -0
  192. package/ftm-mind/references/direct-execution.md +51 -0
  193. package/ftm-mind/references/environment-discovery.md +77 -0
  194. package/ftm-mind/references/event-registry.md +319 -319
  195. package/ftm-mind/references/mcp-inventory.md +300 -296
  196. package/ftm-mind/references/ops-routing.md +47 -0
  197. package/ftm-mind/references/orient-protocol.md +234 -0
  198. package/ftm-mind/references/personality.md +40 -0
  199. package/ftm-mind/references/protocols/COMPLEXITY-SIZING.md +72 -72
  200. package/ftm-mind/references/protocols/MCP-HEURISTICS.md +32 -32
  201. package/ftm-mind/references/protocols/PLAN-APPROVAL.md +80 -80
  202. package/ftm-mind/references/reflexion-protocol.md +249 -249
  203. package/ftm-mind/references/routing/SCENARIOS.md +22 -22
  204. package/ftm-mind/references/routing-scenarios.md +35 -35
  205. package/ftm-mind.yml +2 -2
  206. package/ftm-ops.yml +4 -0
  207. package/ftm-pause/SKILL.md +395 -395
  208. package/ftm-pause/references/protocols/SKILL-RESTORE-PROTOCOLS.md +186 -186
  209. package/ftm-pause/references/protocols/VALIDATION.md +80 -80
  210. package/ftm-pause.yml +2 -2
  211. package/ftm-researcher/SKILL.md +275 -275
  212. package/ftm-researcher/evals/agent-diversity.yaml +17 -17
  213. package/ftm-researcher/evals/synthesis-quality.yaml +12 -12
  214. package/ftm-researcher/evals/trigger-accuracy.yaml +39 -39
  215. package/ftm-researcher/references/adaptive-search.md +116 -116
  216. package/ftm-researcher/references/agent-prompts.md +193 -193
  217. package/ftm-researcher/references/council-integration.md +193 -193
  218. package/ftm-researcher/references/output-format.md +203 -203
  219. package/ftm-researcher/references/synthesis-pipeline.md +165 -165
  220. package/ftm-researcher/scripts/score_credibility.py +234 -234
  221. package/ftm-researcher/scripts/validate_research.py +92 -92
  222. package/ftm-researcher.yml +2 -2
  223. package/ftm-resume/SKILL.md +518 -518
  224. package/ftm-resume/references/protocols/VALIDATION.md +172 -172
  225. package/ftm-resume.yml +2 -2
  226. package/ftm-retro/SKILL.md +380 -380
  227. package/ftm-retro/references/protocols/SCORING-RUBRICS.md +89 -89
  228. package/ftm-retro/references/templates/REPORT-FORMAT.md +109 -109
  229. package/ftm-retro.yml +2 -2
  230. package/ftm-routine/SKILL.md +170 -170
  231. package/ftm-routine.yml +4 -4
  232. package/ftm-state/blackboard/capabilities.json +5 -5
  233. package/ftm-state/blackboard/capabilities.schema.json +27 -27
  234. package/ftm-state/blackboard/context.json +37 -23
  235. package/ftm-state/blackboard/experiences/doom-statusline-fix.json +26 -0
  236. package/ftm-state/blackboard/experiences/hackathon-pages-site.json +26 -0
  237. package/ftm-state/blackboard/experiences/hindsight-sso-kickoff.json +42 -0
  238. package/ftm-state/blackboard/experiences/index.json +58 -9
  239. package/ftm-state/blackboard/experiences/learning-ragnarok-api-access.json +23 -0
  240. package/ftm-state/blackboard/experiences/nordlayer-members-auto-assign.json +26 -0
  241. package/ftm-state/blackboard/experiences/saml2aws-stale-session-fix.json +41 -0
  242. package/ftm-state/blackboard/patterns.json +6 -6
  243. package/ftm-state/schemas/context.schema.json +130 -130
  244. package/ftm-state/schemas/experience-index.schema.json +77 -77
  245. package/ftm-state/schemas/experience.schema.json +78 -78
  246. package/ftm-state/schemas/patterns.schema.json +44 -44
  247. package/ftm-upgrade/SKILL.md +194 -194
  248. package/ftm-upgrade/scripts/check-version.sh +76 -76
  249. package/ftm-upgrade/scripts/upgrade.sh +143 -143
  250. package/ftm-upgrade.yml +2 -2
  251. package/ftm-verify.yml +2 -2
  252. package/ftm.yml +2 -2
  253. package/hooks/ftm-auto-log.sh +137 -0
  254. package/hooks/ftm-blackboard-enforcer.sh +93 -93
  255. package/hooks/ftm-discovery-reminder.sh +90 -90
  256. package/hooks/ftm-drafts-gate.sh +61 -61
  257. package/hooks/ftm-event-logger.mjs +107 -107
  258. package/hooks/ftm-install-hooks.sh +240 -0
  259. package/hooks/ftm-learning-capture.sh +117 -0
  260. package/hooks/ftm-map-autodetect.sh +79 -79
  261. package/hooks/ftm-pending-sync-check.sh +22 -22
  262. package/hooks/ftm-plan-gate.sh +92 -92
  263. package/hooks/ftm-post-commit-trigger.sh +57 -57
  264. package/hooks/ftm-post-compaction.sh +138 -0
  265. package/hooks/ftm-pre-compaction.sh +147 -0
  266. package/hooks/ftm-session-end.sh +52 -0
  267. package/hooks/ftm-session-snapshot.sh +213 -0
  268. package/hooks/ftm-task-loader.sh +100 -0
  269. package/hooks/settings-template.json +91 -81
  270. package/install.sh +363 -363
  271. package/package.json +84 -84
  272. 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