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
@@ -0,0 +1,147 @@
1
+ #!/bin/bash
2
+ # ftm-pre-compaction.sh
3
+ # Hook: Pre-compaction memory flush for ftm
4
+ # Trigger: UserPromptSubmit
5
+ #
6
+ # Fires on every user message during an active ftm session.
7
+ # Reads the session JSONL to track token usage. When context approaches
8
+ # the limit (default: 150K of 200K tokens), injects a flush-first prefix
9
+ # telling Claude to write daily log + task state before responding.
10
+ #
11
+ # This approximates OpenClaw's pre-compaction flush, adapted for Claude Code's
12
+ # hook system (which can inject text but can't inject silent LLM turns).
13
+ #
14
+ # THRESHOLD: 150K tokens = 75% of 200K context window
15
+ # COOLDOWN: Only re-fires after 15K more tokens consumed (prevents spamming)
16
+ #
17
+ # FILES:
18
+ # ~/.claude/ftm-state/blackboard/context.json - session gate
19
+ # ~/.claude/ftm-state/.last-flush-tokens - token count at last flush (cooldown)
20
+
21
+ FTM_STATE="$HOME/.claude/ftm-state"
22
+ CONTEXT_JSON="$FTM_STATE/blackboard/context.json"
23
+ LAST_FLUSH_FILE="$FTM_STATE/.last-flush-tokens"
24
+
25
+ THRESHOLD=150000 # Flush when total context exceeds this
26
+ COOLDOWN=15000 # Only re-flush after this many additional tokens
27
+
28
+ # 1. Only run during active ftm sessions
29
+ FTM_ACTIVE=$(python3 -c "
30
+ import json, sys
31
+ try:
32
+ with open('$CONTEXT_JSON') as f:
33
+ d = json.load(f)
34
+ task = d.get('current_task', {})
35
+ status = task.get('status', '')
36
+ print('1' if status not in ('', 'completed', 'none') else '0')
37
+ except Exception:
38
+ print('0')
39
+ " 2>/dev/null)
40
+
41
+ if [ "$FTM_ACTIVE" != "1" ]; then
42
+ exit 0
43
+ fi
44
+
45
+ # 2. Read hook payload from stdin (JSON: { session_id, prompt, transcript_path, ... })
46
+ PAYLOAD=$(cat)
47
+ if [ -z "$PAYLOAD" ]; then
48
+ exit 0
49
+ fi
50
+
51
+ # 3. Extract session_id and transcript_path from payload
52
+ SESSION_ID=$(echo "$PAYLOAD" | python3 -c "
53
+ import sys, json
54
+ try:
55
+ d = json.load(sys.stdin)
56
+ print(d.get('session_id', ''))
57
+ except:
58
+ print('')
59
+ " 2>/dev/null)
60
+
61
+ TRANSCRIPT_PATH=$(echo "$PAYLOAD" | python3 -c "
62
+ import sys, json
63
+ try:
64
+ d = json.load(sys.stdin)
65
+ print(d.get('transcript_path', ''))
66
+ except:
67
+ print('')
68
+ " 2>/dev/null)
69
+
70
+ # 4. Find the session JSONL (try transcript_path first, then search by session_id)
71
+ JSONL_FILE=""
72
+ if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
73
+ JSONL_FILE="$TRANSCRIPT_PATH"
74
+ elif [ -n "$SESSION_ID" ]; then
75
+ JSONL_FILE=$(find "$HOME/.claude/projects/" -name "${SESSION_ID}.jsonl" 2>/dev/null | head -1)
76
+ fi
77
+
78
+ if [ -z "$JSONL_FILE" ] || [ ! -f "$JSONL_FILE" ]; then
79
+ exit 0
80
+ fi
81
+
82
+ # 5. Parse JSONL to find latest total token usage
83
+ TOTAL_TOKENS=$(python3 -c "
84
+ import json, sys, os
85
+
86
+ path = sys.argv[1]
87
+ try:
88
+ with open(path) as f:
89
+ lines = f.readlines()
90
+ except:
91
+ print(0)
92
+ sys.exit(0)
93
+
94
+ # Scan in reverse for the latest usage entry
95
+ for line in reversed(lines):
96
+ try:
97
+ obj = json.loads(line)
98
+ msg = obj.get('message', {})
99
+ if not isinstance(msg, dict):
100
+ continue
101
+ usage = msg.get('usage', {})
102
+ if isinstance(usage, dict) and 'input_tokens' in usage:
103
+ total = (
104
+ usage.get('input_tokens', 0) +
105
+ usage.get('cache_read_input_tokens', 0) +
106
+ usage.get('cache_creation_input_tokens', 0)
107
+ )
108
+ print(total)
109
+ sys.stdout.flush()
110
+ os._exit(0)
111
+ except Exception:
112
+ pass
113
+
114
+ print(0)
115
+ " "$JSONL_FILE" 2>/dev/null)
116
+
117
+ TOTAL_TOKENS=${TOTAL_TOKENS:-0}
118
+
119
+ # 6. Check if we're over the threshold
120
+ if [ "$TOTAL_TOKENS" -lt "$THRESHOLD" ] 2>/dev/null; then
121
+ exit 0
122
+ fi
123
+
124
+ # 7. Cooldown check: only flush if we've consumed COOLDOWN more tokens since last flush
125
+ LAST_FLUSH_TOKENS=$(cat "$LAST_FLUSH_FILE" 2>/dev/null || echo "0")
126
+ TOKENS_SINCE_FLUSH=$(( TOTAL_TOKENS - LAST_FLUSH_TOKENS ))
127
+
128
+ if [ "$TOKENS_SINCE_FLUSH" -lt "$COOLDOWN" ] 2>/dev/null; then
129
+ exit 0
130
+ fi
131
+
132
+ # 8. Record this flush point
133
+ echo "$TOTAL_TOKENS" > "$LAST_FLUSH_FILE"
134
+
135
+ # 9. Inject the flush-first prefix into the user's message
136
+ TODAY=$(date +%Y-%m-%d)
137
+ PCT=$((TOTAL_TOKENS * 100 / 200000))
138
+
139
+ echo ""
140
+ echo "[PRE-COMPACTION MEMORY FLUSH — ${TOTAL_TOKENS} tokens (~${PCT}% of 200K) — ${TODAY}]:"
141
+ echo "Context is getting full. BEFORE responding: write any unsaved state to disk."
142
+ echo "1. Append unsaved session progress to the ftm daily log for ${TODAY}"
143
+ echo "2. Update ~/.claude/ftm-state/blackboard/context.json with current task status"
144
+ echo "3. If important facts/solutions discovered this session, append to relevant knowledge files"
145
+ echo "Do this silently — do NOT announce the flush to the user. Then respond normally."
146
+ echo "If there is genuinely nothing new to record, skip the writes and proceed directly."
147
+ echo ""
@@ -0,0 +1,52 @@
1
+ #!/bin/bash
2
+ # ftm-session-end.sh
3
+ # Hook: Deactivate ftm session tracking when conversation ends
4
+ # Trigger: SessionEnd
5
+ #
6
+ # IMPORTANT: Must be listed AFTER ftm-session-snapshot.sh in settings.json
7
+ # (in a separate matcher entry). The snapshot hook reads context.json status
8
+ # to gate itself — this hook marks the session completed.
9
+
10
+ FTM_STATE="$HOME/.claude/ftm-state"
11
+ CONTEXT_JSON="$FTM_STATE/blackboard/context.json"
12
+
13
+ # Check if an active session exists
14
+ IS_ACTIVE=$(python3 -c "
15
+ import json, sys
16
+ try:
17
+ with open('$CONTEXT_JSON') as f:
18
+ d = json.load(f)
19
+ task = d.get('current_task', {})
20
+ status = task.get('status', '')
21
+ print('1' if status not in ('', 'completed', 'none') else '0')
22
+ except Exception:
23
+ print('0')
24
+ " 2>/dev/null)
25
+
26
+ if [ "$IS_ACTIVE" != "1" ]; then
27
+ exit 0
28
+ fi
29
+
30
+ # Mark session as completed in context.json
31
+ python3 -c "
32
+ import json, sys
33
+ from datetime import datetime
34
+
35
+ try:
36
+ with open('$CONTEXT_JSON') as f:
37
+ d = json.load(f)
38
+
39
+ if 'current_task' in d and isinstance(d['current_task'], dict):
40
+ d['current_task']['status'] = 'completed'
41
+
42
+ if 'session_metadata' in d and isinstance(d['session_metadata'], dict):
43
+ d['session_metadata']['last_updated'] = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
44
+
45
+ with open('$CONTEXT_JSON', 'w') as f:
46
+ json.dump(d, f, indent=2)
47
+ except Exception as e:
48
+ sys.stderr.write(f'ftm-session-end: failed to update context.json: {e}\n')
49
+ sys.exit(1)
50
+ " 2>/dev/null
51
+
52
+ echo "ftm session tracking deactivated (session ended)"
@@ -0,0 +1,213 @@
1
+ #!/bin/bash
2
+ # ftm-session-snapshot.sh
3
+ # Hook: Session snapshot on conversation end
4
+ # Trigger: SessionEnd
5
+ #
6
+ # Captures the last 15 meaningful user/assistant exchanges as a markdown snapshot
7
+ # when an ftm session ends. Fills the gap where sessions that never hit the
8
+ # compaction token threshold would lose all conversational context.
9
+ #
10
+ # Inspired by OpenClaw's session snapshot mechanism, which fires on /new or /reset.
11
+ # We fire on SessionEnd since Claude Code doesn't expose /new or /reset as hook events.
12
+ #
13
+ # Filtering:
14
+ # - Keeps: user and assistant messages with text content
15
+ # - Skips: tool_use blocks, tool_result blocks, system messages, slash commands
16
+ # - Truncates: messages > 2000 chars (long pastes don't need full capture)
17
+ #
18
+ # Filename: YYYY-MM-DDTHH-MM-<topic-slug>.md (topic derived from last user message)
19
+ #
20
+ # IMPORTANT: Must be listed BEFORE ftm-session-end.sh in settings.json.
21
+ # ftm-session-end.sh updates context.json status — this hook reads it.
22
+ #
23
+ # OUTPUT: ~/.claude/ftm-state/sessions/
24
+ # MINIMUM: 3 meaningful messages required to write a snapshot (skip trivial sessions)
25
+ #
26
+ # FILES:
27
+ # ~/.claude/ftm-state/blackboard/context.json - session gate (read only, not modified)
28
+ # ~/.claude/ftm-state/sessions/ - snapshot output directory
29
+
30
+ FTM_STATE="$HOME/.claude/ftm-state"
31
+ CONTEXT_JSON="$FTM_STATE/blackboard/context.json"
32
+ SESSIONS_DIR="$FTM_STATE/sessions"
33
+ MAX_MESSAGES=15
34
+ MIN_MESSAGES=3
35
+
36
+ # 1. Only run during active ftm sessions
37
+ FTM_ACTIVE=$(python3 -c "
38
+ import json, sys
39
+ try:
40
+ with open('$CONTEXT_JSON') as f:
41
+ d = json.load(f)
42
+ task = d.get('current_task', {})
43
+ status = task.get('status', '')
44
+ print('1' if status not in ('', 'completed', 'none') else '0')
45
+ except Exception:
46
+ print('0')
47
+ " 2>/dev/null)
48
+
49
+ if [ "$FTM_ACTIVE" != "1" ]; then
50
+ exit 0
51
+ fi
52
+
53
+ # 2. Read hook payload from stdin
54
+ PAYLOAD=$(cat)
55
+ if [ -z "$PAYLOAD" ]; then
56
+ exit 0
57
+ fi
58
+
59
+ # 3. Extract session_id and transcript_path
60
+ SESSION_ID=$(echo "$PAYLOAD" | python3 -c "
61
+ import sys, json
62
+ try:
63
+ d = json.load(sys.stdin)
64
+ print(d.get('session_id', ''))
65
+ except:
66
+ print('')
67
+ " 2>/dev/null)
68
+
69
+ TRANSCRIPT_PATH=$(echo "$PAYLOAD" | python3 -c "
70
+ import sys, json
71
+ try:
72
+ d = json.load(sys.stdin)
73
+ print(d.get('transcript_path', ''))
74
+ except:
75
+ print('')
76
+ " 2>/dev/null)
77
+
78
+ # 4. Find session JSONL
79
+ JSONL_FILE=""
80
+ if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
81
+ JSONL_FILE="$TRANSCRIPT_PATH"
82
+ elif [ -n "$SESSION_ID" ]; then
83
+ JSONL_FILE=$(find "$HOME/.claude/projects/" -name "${SESSION_ID}.jsonl" 2>/dev/null | head -1)
84
+ fi
85
+
86
+ if [ -z "$JSONL_FILE" ] || [ ! -f "$JSONL_FILE" ]; then
87
+ exit 0
88
+ fi
89
+
90
+ # 5. Ensure sessions directory exists
91
+ mkdir -p "$SESSIONS_DIR"
92
+
93
+ # 6. Parse JSONL, build snapshot, write file — all in one Python block
94
+ export JSONL_FILE SESSION_ID SESSIONS_DIR MAX_MESSAGES MIN_MESSAGES
95
+ python3 << 'PYEOF'
96
+ import json, sys, os, re
97
+ from datetime import datetime
98
+
99
+ jsonl_path = os.environ['JSONL_FILE']
100
+ session_id = os.environ.get('SESSION_ID', 'unknown')
101
+ sessions_dir = os.environ['SESSIONS_DIR']
102
+ max_msgs = int(os.environ.get('MAX_MESSAGES', '15'))
103
+ min_msgs = int(os.environ.get('MIN_MESSAGES', '3'))
104
+
105
+ # --- Parse JSONL ---
106
+ try:
107
+ with open(jsonl_path) as f:
108
+ lines = f.readlines()
109
+ except Exception:
110
+ sys.exit(0)
111
+
112
+ messages = []
113
+ for line in lines:
114
+ line = line.strip()
115
+ if not line:
116
+ continue
117
+ try:
118
+ obj = json.loads(line)
119
+ except Exception:
120
+ continue
121
+
122
+ msg = obj.get('message', {})
123
+ if not isinstance(msg, dict):
124
+ continue
125
+
126
+ role = msg.get('role', '')
127
+ if role not in ('user', 'assistant'):
128
+ continue
129
+
130
+ content = msg.get('content', '')
131
+
132
+ # Extract text content only — skip tool_use, tool_result, image blocks
133
+ if isinstance(content, list):
134
+ text_parts = []
135
+ for block in content:
136
+ if isinstance(block, dict):
137
+ if block.get('type') == 'text':
138
+ t = block.get('text', '').strip()
139
+ if t:
140
+ text_parts.append(t)
141
+ elif isinstance(block, str):
142
+ t = block.strip()
143
+ if t:
144
+ text_parts.append(t)
145
+ text = '\n'.join(text_parts)
146
+ elif isinstance(content, str):
147
+ text = content.strip()
148
+ else:
149
+ continue
150
+
151
+ if not text:
152
+ continue
153
+
154
+ # Skip slash commands
155
+ if role == 'user' and text.lstrip().startswith('/'):
156
+ continue
157
+
158
+ messages.append({'role': role, 'text': text})
159
+
160
+ # Take last max_msgs meaningful messages
161
+ recent = messages[-max_msgs:]
162
+
163
+ # Require minimum message count — skip trivial sessions
164
+ if len(recent) < min_msgs:
165
+ sys.exit(0)
166
+
167
+ # --- Derive topic slug from last user message with some substance ---
168
+ topic = 'session'
169
+ for m in reversed(recent):
170
+ if m['role'] == 'user' and len(m['text']) > 15:
171
+ raw = m['text'][:80].lower()
172
+ raw = re.sub(r'[^a-z0-9\s]', ' ', raw)
173
+ raw = re.sub(r'\s+', '-', raw.strip())
174
+ raw = re.sub(r'-+', '-', raw).strip('-')[:50].rstrip('-')
175
+ if raw:
176
+ topic = raw
177
+ break
178
+
179
+ # --- Build filename and output path ---
180
+ now = datetime.now()
181
+ timestamp_file = now.strftime('%Y-%m-%dT%H-%M')
182
+ timestamp_display = now.strftime('%Y-%m-%d %H:%M')
183
+ filename = f'{timestamp_file}-{topic}.md'
184
+ output_path = os.path.join(sessions_dir, filename)
185
+
186
+ # --- Write snapshot markdown ---
187
+ lines_out = [
188
+ f'# Session Snapshot — {timestamp_display}',
189
+ '',
190
+ f'**Messages captured**: {len(recent)} (last {len(recent)} of session)',
191
+ f'**Session**: {session_id[:8]}...',
192
+ '',
193
+ '---',
194
+ '',
195
+ ]
196
+
197
+ for msg in recent:
198
+ role_label = '**You**' if msg['role'] == 'user' else '**Claude**'
199
+ text = msg['text']
200
+ # Truncate very long messages to keep snapshot lean
201
+ if len(text) > 2000:
202
+ text = text[:2000] + '\n\n[... truncated — full content in daily log ...]'
203
+ lines_out.append(f'{role_label}: {text}')
204
+ lines_out.append('')
205
+
206
+ try:
207
+ with open(output_path, 'w') as f:
208
+ f.write('\n'.join(lines_out))
209
+ except Exception:
210
+ pass
211
+ PYEOF
212
+
213
+ exit 0
@@ -1,81 +1,81 @@
1
- {
2
- "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
- "_comment": "FTM hooks configuration template. Merge these entries into your ~/.claude/settings.json under the 'hooks' key. install.sh can do this automatically with --setup-hooks.",
4
- "hooks": {
5
- "PreToolUse": [
6
- {
7
- "matcher": "Edit|Write",
8
- "hooks": [
9
- {
10
- "type": "command",
11
- "command": "~/.claude/hooks/ftm-plan-gate.sh",
12
- "timeout": 5
13
- }
14
- ]
15
- },
16
- {
17
- "matcher": "mcp__slack__slack_post_message|mcp__slack__slack_reply_to_thread|mcp__gmail__send_email",
18
- "hooks": [
19
- {
20
- "type": "command",
21
- "command": "~/.claude/hooks/ftm-drafts-gate.sh",
22
- "timeout": 5
23
- }
24
- ]
25
- }
26
- ],
27
- "UserPromptSubmit": [
28
- {
29
- "hooks": [
30
- {
31
- "type": "command",
32
- "command": "~/.claude/hooks/ftm-discovery-reminder.sh",
33
- "timeout": 5
34
- },
35
- {
36
- "type": "command",
37
- "command": "~/.claude/hooks/ftm-pending-sync-check.sh",
38
- "timeout": 5
39
- },
40
- {
41
- "type": "command",
42
- "command": "~/.claude/hooks/ftm-map-autodetect.sh",
43
- "timeout": 5
44
- }
45
- ]
46
- }
47
- ],
48
- "PostToolUse": [
49
- {
50
- "matcher": "",
51
- "hooks": [
52
- {
53
- "type": "command",
54
- "command": "node ~/.claude/hooks/ftm-event-logger.mjs"
55
- }
56
- ]
57
- },
58
- {
59
- "matcher": "Bash|mcp__git__git_commit",
60
- "hooks": [
61
- {
62
- "type": "command",
63
- "command": "~/.claude/hooks/ftm-post-commit-trigger.sh",
64
- "timeout": 5
65
- }
66
- ]
67
- }
68
- ],
69
- "Stop": [
70
- {
71
- "hooks": [
72
- {
73
- "type": "command",
74
- "command": "~/.claude/hooks/ftm-blackboard-enforcer.sh",
75
- "timeout": 5
76
- }
77
- ]
78
- }
79
- ]
80
- }
81
- }
1
+ {
2
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
+ "_comment": "FTM hooks configuration template. Merge these entries into your ~/.claude/settings.json under the 'hooks' key. install.sh can do this automatically with --setup-hooks.",
4
+ "hooks": {
5
+ "PreToolUse": [
6
+ {
7
+ "matcher": "Edit|Write",
8
+ "hooks": [
9
+ {
10
+ "type": "command",
11
+ "command": "~/.claude/hooks/ftm-plan-gate.sh",
12
+ "timeout": 5
13
+ }
14
+ ]
15
+ },
16
+ {
17
+ "matcher": "mcp__slack__slack_post_message|mcp__slack__slack_reply_to_thread|mcp__gmail__send_email",
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "~/.claude/hooks/ftm-drafts-gate.sh",
22
+ "timeout": 5
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "UserPromptSubmit": [
28
+ {
29
+ "hooks": [
30
+ {
31
+ "type": "command",
32
+ "command": "~/.claude/hooks/ftm-discovery-reminder.sh",
33
+ "timeout": 5
34
+ },
35
+ {
36
+ "type": "command",
37
+ "command": "~/.claude/hooks/ftm-pending-sync-check.sh",
38
+ "timeout": 5
39
+ },
40
+ {
41
+ "type": "command",
42
+ "command": "~/.claude/hooks/ftm-map-autodetect.sh",
43
+ "timeout": 5
44
+ }
45
+ ]
46
+ }
47
+ ],
48
+ "PostToolUse": [
49
+ {
50
+ "matcher": "",
51
+ "hooks": [
52
+ {
53
+ "type": "command",
54
+ "command": "node ~/.claude/hooks/ftm-event-logger.mjs"
55
+ }
56
+ ]
57
+ },
58
+ {
59
+ "matcher": "Bash|mcp__git__git_commit",
60
+ "hooks": [
61
+ {
62
+ "type": "command",
63
+ "command": "~/.claude/hooks/ftm-post-commit-trigger.sh",
64
+ "timeout": 5
65
+ }
66
+ ]
67
+ }
68
+ ],
69
+ "Stop": [
70
+ {
71
+ "hooks": [
72
+ {
73
+ "type": "command",
74
+ "command": "~/.claude/hooks/ftm-blackboard-enforcer.sh",
75
+ "timeout": 5
76
+ }
77
+ ]
78
+ }
79
+ ]
80
+ }
81
+ }