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,79 +1,79 @@
1
- #!/usr/bin/env bash
2
- # UserPromptSubmit hook: auto-detect unmapped projects when ftm skills are invoked.
3
- # Detects greenfield vs brownfield and injects instructions to bootstrap ftm-map.
4
- # Only fires once per project (writes .ftm-map/.offered marker).
5
-
6
- set -euo pipefail
7
-
8
- INPUT=$(cat)
9
- PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""' 2>/dev/null)
10
-
11
- # Quick exit if no prompt or jq unavailable
12
- [ -n "$PROMPT" ] || exit 0
13
-
14
- # Only fire on ftm-related invocations
15
- PROMPT_LOWER=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]')
16
- IS_FTM=false
17
- for trigger in "/ftm" "ftm-" "brainstorm" "research" "debug this" "audit" "deep dive" "investigate"; do
18
- if [[ "$PROMPT_LOWER" == *"$trigger"* ]]; then
19
- IS_FTM=true
20
- break
21
- fi
22
- done
23
- $IS_FTM || exit 0
24
-
25
- # Already mapped — nothing to do
26
- [ -f ".ftm-map/map.db" ] && exit 0
27
-
28
- # Already offered for this project — don't nag
29
- [ -f ".ftm-map/.offered" ] && exit 0
30
-
31
- # --- Greenfield vs Brownfield detection ---
32
-
33
- # Count source files (fast, capped at 500 to avoid slow ls on huge repos)
34
- SRC_COUNT=0
35
- if command -v find &>/dev/null; then
36
- SRC_COUNT=$(find . -maxdepth 4 \
37
- \( -name "*.py" -o -name "*.js" -o -name "*.ts" -o -name "*.tsx" \
38
- -o -name "*.jsx" -o -name "*.go" -o -name "*.rs" -o -name "*.swift" \
39
- -o -name "*.java" -o -name "*.rb" -o -name "*.sh" -o -name "*.mjs" \
40
- -o -name "*.cjs" \) \
41
- -not -path "*/node_modules/*" \
42
- -not -path "*/.venv/*" \
43
- -not -path "*/__pycache__/*" \
44
- -not -path "*/.git/*" \
45
- -not -path "*/.worktrees/*" \
46
- 2>/dev/null | head -500 | wc -l | tr -d ' ')
47
- fi
48
-
49
- # Git history depth
50
- COMMIT_COUNT=0
51
- if git rev-parse --is-inside-work-tree &>/dev/null 2>&1; then
52
- COMMIT_COUNT=$(git rev-list --count HEAD 2>/dev/null || echo "0")
53
- fi
54
-
55
- # Classify
56
- if [ "$SRC_COUNT" -le 5 ] && [ "$COMMIT_COUNT" -le 3 ]; then
57
- PROJECT_TYPE="greenfield"
58
- PROJECT_DESC="Greenfield project (~${SRC_COUNT} source files, ${COMMIT_COUNT} commits). Small enough to index instantly."
59
- elif [ "$SRC_COUNT" -le 50 ]; then
60
- PROJECT_TYPE="small-brownfield"
61
- PROJECT_DESC="Small brownfield project (~${SRC_COUNT} source files, ${COMMIT_COUNT} commits). Quick to index."
62
- elif [ "$SRC_COUNT" -le 200 ]; then
63
- PROJECT_TYPE="medium-brownfield"
64
- PROJECT_DESC="Medium brownfield project (~${SRC_COUNT} source files, ${COMMIT_COUNT} commits). Index takes ~30s."
65
- else
66
- PROJECT_TYPE="large-brownfield"
67
- PROJECT_DESC="Large brownfield project (~${SRC_COUNT} source files, ${COMMIT_COUNT} commits). Index may take 1-2 min."
68
- fi
69
-
70
- # Write marker so we don't ask again
71
- mkdir -p .ftm-map
72
- echo "$PROJECT_TYPE" > .ftm-map/.offered
73
-
74
- # Inject context
75
- cat <<EOJSON
76
- {
77
- "additionalContext": "[ftm-map-autodetect] This project has no code knowledge graph yet (.ftm-map/map.db missing). ${PROJECT_DESC} Before proceeding with the ftm skill, bootstrap the map: run \`ftm-map/scripts/setup.sh\` then \`ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/index.py --bootstrap .\` in the background. This enables structural queries (blast radius, dependency chains) and keeps INTENT.md/ARCHITECTURE.mmd in sync via the post-commit hook. If the user's request is time-sensitive, proceed with the skill and index in parallel."
78
- }
79
- EOJSON
1
+ #!/usr/bin/env bash
2
+ # UserPromptSubmit hook: auto-detect unmapped projects when ftm skills are invoked.
3
+ # Detects greenfield vs brownfield and injects instructions to bootstrap ftm-map.
4
+ # Only fires once per project (writes .ftm-map/.offered marker).
5
+
6
+ set -euo pipefail
7
+
8
+ INPUT=$(cat)
9
+ PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""' 2>/dev/null)
10
+
11
+ # Quick exit if no prompt or jq unavailable
12
+ [ -n "$PROMPT" ] || exit 0
13
+
14
+ # Only fire on ftm-related invocations
15
+ PROMPT_LOWER=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]')
16
+ IS_FTM=false
17
+ for trigger in "/ftm" "ftm-" "brainstorm" "research" "debug this" "audit" "deep dive" "investigate"; do
18
+ if [[ "$PROMPT_LOWER" == *"$trigger"* ]]; then
19
+ IS_FTM=true
20
+ break
21
+ fi
22
+ done
23
+ $IS_FTM || exit 0
24
+
25
+ # Already mapped — nothing to do
26
+ [ -f ".ftm-map/map.db" ] && exit 0
27
+
28
+ # Already offered for this project — don't nag
29
+ [ -f ".ftm-map/.offered" ] && exit 0
30
+
31
+ # --- Greenfield vs Brownfield detection ---
32
+
33
+ # Count source files (fast, capped at 500 to avoid slow ls on huge repos)
34
+ SRC_COUNT=0
35
+ if command -v find &>/dev/null; then
36
+ SRC_COUNT=$(find . -maxdepth 4 \
37
+ \( -name "*.py" -o -name "*.js" -o -name "*.ts" -o -name "*.tsx" \
38
+ -o -name "*.jsx" -o -name "*.go" -o -name "*.rs" -o -name "*.swift" \
39
+ -o -name "*.java" -o -name "*.rb" -o -name "*.sh" -o -name "*.mjs" \
40
+ -o -name "*.cjs" \) \
41
+ -not -path "*/node_modules/*" \
42
+ -not -path "*/.venv/*" \
43
+ -not -path "*/__pycache__/*" \
44
+ -not -path "*/.git/*" \
45
+ -not -path "*/.worktrees/*" \
46
+ 2>/dev/null | head -500 | wc -l | tr -d ' ')
47
+ fi
48
+
49
+ # Git history depth
50
+ COMMIT_COUNT=0
51
+ if git rev-parse --is-inside-work-tree &>/dev/null 2>&1; then
52
+ COMMIT_COUNT=$(git rev-list --count HEAD 2>/dev/null || echo "0")
53
+ fi
54
+
55
+ # Classify
56
+ if [ "$SRC_COUNT" -le 5 ] && [ "$COMMIT_COUNT" -le 3 ]; then
57
+ PROJECT_TYPE="greenfield"
58
+ PROJECT_DESC="Greenfield project (~${SRC_COUNT} source files, ${COMMIT_COUNT} commits). Small enough to index instantly."
59
+ elif [ "$SRC_COUNT" -le 50 ]; then
60
+ PROJECT_TYPE="small-brownfield"
61
+ PROJECT_DESC="Small brownfield project (~${SRC_COUNT} source files, ${COMMIT_COUNT} commits). Quick to index."
62
+ elif [ "$SRC_COUNT" -le 200 ]; then
63
+ PROJECT_TYPE="medium-brownfield"
64
+ PROJECT_DESC="Medium brownfield project (~${SRC_COUNT} source files, ${COMMIT_COUNT} commits). Index takes ~30s."
65
+ else
66
+ PROJECT_TYPE="large-brownfield"
67
+ PROJECT_DESC="Large brownfield project (~${SRC_COUNT} source files, ${COMMIT_COUNT} commits). Index may take 1-2 min."
68
+ fi
69
+
70
+ # Write marker so we don't ask again
71
+ mkdir -p .ftm-map
72
+ echo "$PROJECT_TYPE" > .ftm-map/.offered
73
+
74
+ # Inject context
75
+ cat <<EOJSON
76
+ {
77
+ "additionalContext": "[ftm-map-autodetect] This project has no code knowledge graph yet (.ftm-map/map.db missing). ${PROJECT_DESC} Before proceeding with the ftm skill, bootstrap the map: run \`ftm-map/scripts/setup.sh\` then \`ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/index.py --bootstrap .\` in the background. This enables structural queries (blast radius, dependency chains) and keeps INTENT.md/ARCHITECTURE.mmd in sync via the post-commit hook. If the user's request is time-sensitive, proceed with the skill and index in parallel."
78
+ }
79
+ EOJSON
@@ -1,22 +1,22 @@
1
- #!/usr/bin/env bash
2
- # UserPromptSubmit hook: checks for pending commit syncs from out-of-session commits
3
-
4
- PENDING_FILE="$HOME/.claude/ftm-state/.pending-commit-syncs"
5
-
6
- # Quick exit if no pending syncs
7
- [ -f "$PENDING_FILE" ] || { echo '{}'; exit 0; }
8
- [ -s "$PENDING_FILE" ] || { echo '{}'; exit 0; }
9
-
10
- # Count pending entries
11
- COUNT=$(wc -l < "$PENDING_FILE" | tr -d ' ')
12
-
13
- # Read all pending entries
14
- ENTRIES=$(cat "$PENDING_FILE")
15
-
16
- # Consume the file (mark as processed)
17
- rm -f "$PENDING_FILE"
18
-
19
- # Inject context
20
- cat <<EOJSON
21
- {"additionalContext": "There are $COUNT pending out-of-session commits that need ftm-map incremental sync. Run ftm-map incremental for each:\n$ENTRIES"}
22
- EOJSON
1
+ #!/usr/bin/env bash
2
+ # UserPromptSubmit hook: checks for pending commit syncs from out-of-session commits
3
+
4
+ PENDING_FILE="$HOME/.claude/ftm-state/.pending-commit-syncs"
5
+
6
+ # Quick exit if no pending syncs
7
+ [ -f "$PENDING_FILE" ] || { echo '{}'; exit 0; }
8
+ [ -s "$PENDING_FILE" ] || { echo '{}'; exit 0; }
9
+
10
+ # Count pending entries
11
+ COUNT=$(wc -l < "$PENDING_FILE" | tr -d ' ')
12
+
13
+ # Read all pending entries
14
+ ENTRIES=$(cat "$PENDING_FILE")
15
+
16
+ # Consume the file (mark as processed)
17
+ rm -f "$PENDING_FILE"
18
+
19
+ # Inject context
20
+ cat <<EOJSON
21
+ {"additionalContext": "There are $COUNT pending out-of-session commits that need ftm-map incremental sync. Run ftm-map incremental for each:\n$ENTRIES"}
22
+ EOJSON
@@ -1,92 +1,92 @@
1
- #!/usr/bin/env bash
2
- # ftm-plan-gate.sh
3
- # PreToolUse hook for Edit/Write tools.
4
- #
5
- # Checks if a plan has been presented this session before allowing code edits.
6
- # If no plan marker exists and the edit count is climbing, injects warnings
7
- # telling Claude to stop and present a plan first.
8
- #
9
- # The marker file (~/.claude/ftm-state/.plan-presented) is created by Claude
10
- # when it presents a plan. Any non-empty content counts as "plan presented".
11
- # The file is cleaned up by the blackboard enforcer at session end.
12
- #
13
- # Hook: PreToolUse (matcher: Edit|Write)
14
-
15
- set -euo pipefail
16
-
17
- INPUT=$(cat)
18
- TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
19
-
20
- # Only gate Edit and Write tools
21
- if [[ "$TOOL_NAME" != "Edit" && "$TOOL_NAME" != "Write" ]]; then
22
- exit 0
23
- fi
24
-
25
- STATE_DIR="$HOME/.claude/ftm-state"
26
- PLAN_MARKER="$STATE_DIR/.plan-presented"
27
- EDIT_COUNTER="$STATE_DIR/.edit-count"
28
-
29
- # Get the file being edited
30
- FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
31
-
32
- # Always allow edits to: skill files, ftm-state, drafts, .gitignore, markdown docs
33
- # These are "meta" edits that happen during planning/setup, not code grinding
34
- if [[ "$FILE_PATH" == *".claude/skills/"* ]] || \
35
- [[ "$FILE_PATH" == *".claude/ftm-state/"* ]] || \
36
- [[ "$FILE_PATH" == *".ftm-drafts/"* ]] || \
37
- [[ "$FILE_PATH" == *".gitignore" ]] || \
38
- [[ "$FILE_PATH" == *"INTENT.md"* ]] || \
39
- [[ "$FILE_PATH" == *"ARCHITECTURE.mmd"* ]] || \
40
- [[ "$FILE_PATH" == *"STYLE.md"* ]] || \
41
- [[ "$FILE_PATH" == *"DEBUG.md"* ]] || \
42
- [[ "$FILE_PATH" == *"PROGRESS.md"* ]] || \
43
- [[ "$FILE_PATH" == *"CLAUDE.md"* ]]; then
44
- exit 0
45
- fi
46
-
47
- # If plan marker exists (any content), allow edits
48
- if [[ -f "$PLAN_MARKER" ]] && [[ -s "$PLAN_MARKER" ]]; then
49
- exit 0
50
- fi
51
-
52
- # Reset edit counter if it's stale (older than 4 hours = likely a new session)
53
- if [[ -f "$EDIT_COUNTER" ]]; then
54
- COUNTER_AGE=$(( $(date +%s) - $(stat -c %Y "$EDIT_COUNTER" 2>/dev/null || echo "0") ))
55
- if [[ "$COUNTER_AGE" -gt 14400 ]]; then
56
- rm -f "$EDIT_COUNTER"
57
- fi
58
- fi
59
-
60
- # Count edits without a plan marker
61
- EDIT_COUNT=0
62
- if [[ -f "$EDIT_COUNTER" ]]; then
63
- EDIT_COUNT=$(cat "$EDIT_COUNTER" 2>/dev/null || echo "0")
64
- fi
65
-
66
- EDIT_COUNT=$((EDIT_COUNT + 1))
67
- echo "$EDIT_COUNT" > "$EDIT_COUNTER"
68
-
69
- # First 2 edits get a soft reminder (don't block — could be micro tasks)
70
- # After 3+ edits without a plan marker, escalate the warning
71
- if [[ $EDIT_COUNT -le 2 ]]; then
72
- cat <<'JSON'
73
- {
74
- "hookSpecificOutput": {
75
- "hookEventName": "PreToolUse",
76
- "additionalContext": "[ftm-plan-gate] You are editing files without having presented a plan this session. If this task is medium+ (touches 3+ files, involves external systems, or has stakeholder coordination), you MUST present a numbered plan and get user approval BEFORE editing code. If this is a micro/small task, you can proceed — but create the plan marker: write any content to ~/.claude/ftm-state/.plan-presented to acknowledge you've considered it."
77
- }
78
- }
79
- JSON
80
- exit 0
81
- fi
82
-
83
- # 3+ edits without a plan — stronger warning
84
- cat <<'JSON'
85
- {
86
- "hookSpecificOutput": {
87
- "hookEventName": "PreToolUse",
88
- "additionalContext": "[ftm-plan-gate WARNING] You have made 3+ file edits this session without presenting a plan. This is exactly the 'grinding without a plan' pattern that ftm-mind is supposed to prevent. STOP editing and do one of: (1) Present a numbered plan to the user and wait for approval, then write any content to ~/.claude/ftm-state/.plan-presented. (2) If the user explicitly said 'just do it' or this is genuinely a micro task, write the plan marker to acknowledge you've considered it. Do NOT continue editing without addressing this."
89
- }
90
- }
91
- JSON
92
- exit 0
1
+ #!/usr/bin/env bash
2
+ # ftm-plan-gate.sh
3
+ # PreToolUse hook for Edit/Write tools.
4
+ #
5
+ # Checks if a plan has been presented this session before allowing code edits.
6
+ # If no plan marker exists and the edit count is climbing, injects warnings
7
+ # telling Claude to stop and present a plan first.
8
+ #
9
+ # The marker file (~/.claude/ftm-state/.plan-presented) is created by Claude
10
+ # when it presents a plan. Any non-empty content counts as "plan presented".
11
+ # The file is cleaned up by the blackboard enforcer at session end.
12
+ #
13
+ # Hook: PreToolUse (matcher: Edit|Write)
14
+
15
+ set -euo pipefail
16
+
17
+ INPUT=$(cat)
18
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
19
+
20
+ # Only gate Edit and Write tools
21
+ if [[ "$TOOL_NAME" != "Edit" && "$TOOL_NAME" != "Write" ]]; then
22
+ exit 0
23
+ fi
24
+
25
+ STATE_DIR="$HOME/.claude/ftm-state"
26
+ PLAN_MARKER="$STATE_DIR/.plan-presented"
27
+ EDIT_COUNTER="$STATE_DIR/.edit-count"
28
+
29
+ # Get the file being edited
30
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
31
+
32
+ # Always allow edits to: skill files, ftm-state, drafts, .gitignore, markdown docs
33
+ # These are "meta" edits that happen during planning/setup, not code grinding
34
+ if [[ "$FILE_PATH" == *".claude/skills/"* ]] || \
35
+ [[ "$FILE_PATH" == *".claude/ftm-state/"* ]] || \
36
+ [[ "$FILE_PATH" == *".ftm-drafts/"* ]] || \
37
+ [[ "$FILE_PATH" == *".gitignore" ]] || \
38
+ [[ "$FILE_PATH" == *"INTENT.md"* ]] || \
39
+ [[ "$FILE_PATH" == *"ARCHITECTURE.mmd"* ]] || \
40
+ [[ "$FILE_PATH" == *"STYLE.md"* ]] || \
41
+ [[ "$FILE_PATH" == *"DEBUG.md"* ]] || \
42
+ [[ "$FILE_PATH" == *"PROGRESS.md"* ]] || \
43
+ [[ "$FILE_PATH" == *"CLAUDE.md"* ]]; then
44
+ exit 0
45
+ fi
46
+
47
+ # If plan marker exists (any content), allow edits
48
+ if [[ -f "$PLAN_MARKER" ]] && [[ -s "$PLAN_MARKER" ]]; then
49
+ exit 0
50
+ fi
51
+
52
+ # Reset edit counter if it's stale (older than 4 hours = likely a new session)
53
+ if [[ -f "$EDIT_COUNTER" ]]; then
54
+ COUNTER_AGE=$(( $(date +%s) - $(stat -c %Y "$EDIT_COUNTER" 2>/dev/null || echo "0") ))
55
+ if [[ "$COUNTER_AGE" -gt 14400 ]]; then
56
+ rm -f "$EDIT_COUNTER"
57
+ fi
58
+ fi
59
+
60
+ # Count edits without a plan marker
61
+ EDIT_COUNT=0
62
+ if [[ -f "$EDIT_COUNTER" ]]; then
63
+ EDIT_COUNT=$(cat "$EDIT_COUNTER" 2>/dev/null || echo "0")
64
+ fi
65
+
66
+ EDIT_COUNT=$((EDIT_COUNT + 1))
67
+ echo "$EDIT_COUNT" > "$EDIT_COUNTER"
68
+
69
+ # First 2 edits get a soft reminder (don't block — could be micro tasks)
70
+ # After 3+ edits without a plan marker, escalate the warning
71
+ if [[ $EDIT_COUNT -le 2 ]]; then
72
+ cat <<'JSON'
73
+ {
74
+ "hookSpecificOutput": {
75
+ "hookEventName": "PreToolUse",
76
+ "additionalContext": "[ftm-plan-gate] You are editing files without having presented a plan this session. If this task is medium+ (touches 3+ files, involves external systems, or has stakeholder coordination), you MUST present a numbered plan and get user approval BEFORE editing code. If this is a micro/small task, you can proceed — but create the plan marker: write any content to ~/.claude/ftm-state/.plan-presented to acknowledge you've considered it."
77
+ }
78
+ }
79
+ JSON
80
+ exit 0
81
+ fi
82
+
83
+ # 3+ edits without a plan — stronger warning
84
+ cat <<'JSON'
85
+ {
86
+ "hookSpecificOutput": {
87
+ "hookEventName": "PreToolUse",
88
+ "additionalContext": "[ftm-plan-gate WARNING] You have made 3+ file edits this session without presenting a plan. This is exactly the 'grinding without a plan' pattern that ftm-mind is supposed to prevent. STOP editing and do one of: (1) Present a numbered plan to the user and wait for approval, then write any content to ~/.claude/ftm-state/.plan-presented. (2) If the user explicitly said 'just do it' or this is genuinely a micro task, write the plan marker to acknowledge you've considered it. Do NOT continue editing without addressing this."
89
+ }
90
+ }
91
+ JSON
92
+ exit 0
@@ -1,57 +1,57 @@
1
- #!/usr/bin/env bash
2
- # PostToolUse hook: detect git commits and inject ftm-map update instructions
3
-
4
- set -euo pipefail
5
-
6
- # Read stdin into variable
7
- INPUT=$(cat)
8
-
9
- # Exit gracefully if stdin is empty or jq is unavailable
10
- if [ -z "$INPUT" ]; then
11
- echo "{}"
12
- exit 0
13
- fi
14
-
15
- if ! command -v jq &>/dev/null; then
16
- echo "{}"
17
- exit 0
18
- fi
19
-
20
- # Extract tool_name
21
- TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
22
-
23
- if [ -z "$TOOL_NAME" ]; then
24
- echo "{}"
25
- exit 0
26
- fi
27
-
28
- # Check if this is a commit operation
29
- IS_COMMIT=0
30
-
31
- if [ "$TOOL_NAME" = "mcp__git__git_commit" ]; then
32
- IS_COMMIT=1
33
- elif [ "$TOOL_NAME" = "Bash" ]; then
34
- COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
35
- if [ -n "$COMMAND" ] && echo "$COMMAND" | grep -qE '^\s*git\s+commit'; then
36
- IS_COMMIT=1
37
- fi
38
- fi
39
-
40
- # Exit quickly for non-commit operations
41
- if [ "$IS_COMMIT" -eq 0 ]; then
42
- echo "{}"
43
- exit 0
44
- fi
45
-
46
- # Only trigger for mapped projects
47
- if [ ! -f ".ftm-map/map.db" ]; then
48
- echo "{}"
49
- exit 0
50
- fi
51
-
52
- # Output additionalContext to trigger ftm-map update workflow
53
- cat <<'EOF'
54
- {
55
- "additionalContext": "A commit was just made. Run ftm-map incremental on changed files, then update INTENT.md and ARCHITECTURE.mmd via ftm-intent and ftm-diagram."
56
- }
57
- EOF
1
+ #!/usr/bin/env bash
2
+ # PostToolUse hook: detect git commits and inject ftm-map update instructions
3
+
4
+ set -euo pipefail
5
+
6
+ # Read stdin into variable
7
+ INPUT=$(cat)
8
+
9
+ # Exit gracefully if stdin is empty or jq is unavailable
10
+ if [ -z "$INPUT" ]; then
11
+ echo "{}"
12
+ exit 0
13
+ fi
14
+
15
+ if ! command -v jq &>/dev/null; then
16
+ echo "{}"
17
+ exit 0
18
+ fi
19
+
20
+ # Extract tool_name
21
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
22
+
23
+ if [ -z "$TOOL_NAME" ]; then
24
+ echo "{}"
25
+ exit 0
26
+ fi
27
+
28
+ # Check if this is a commit operation
29
+ IS_COMMIT=0
30
+
31
+ if [ "$TOOL_NAME" = "mcp__git__git_commit" ]; then
32
+ IS_COMMIT=1
33
+ elif [ "$TOOL_NAME" = "Bash" ]; then
34
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
35
+ if [ -n "$COMMAND" ] && echo "$COMMAND" | grep -qE '^\s*git\s+commit'; then
36
+ IS_COMMIT=1
37
+ fi
38
+ fi
39
+
40
+ # Exit quickly for non-commit operations
41
+ if [ "$IS_COMMIT" -eq 0 ]; then
42
+ echo "{}"
43
+ exit 0
44
+ fi
45
+
46
+ # Only trigger for mapped projects
47
+ if [ ! -f ".ftm-map/map.db" ]; then
48
+ echo "{}"
49
+ exit 0
50
+ fi
51
+
52
+ # Output additionalContext to trigger ftm-map update workflow
53
+ cat <<'EOF'
54
+ {
55
+ "additionalContext": "A commit was just made. Run ftm-map incremental on changed files, then update INTENT.md and ARCHITECTURE.mmd via ftm-intent and ftm-diagram."
56
+ }
57
+ EOF
@@ -0,0 +1,138 @@
1
+ #!/bin/bash
2
+ # ftm-post-compaction.sh
3
+ # Hook: Post-compaction context restoration for ftm
4
+ # Trigger: UserPromptSubmit
5
+ #
6
+ # Detects when Claude Code has just compacted the session (context was compressed).
7
+ # After compaction, Claude's in-memory context is reset to a summary — it loses
8
+ # the session's working state. This hook injects a re-initialization prompt that
9
+ # tells Claude to reload its state from disk before responding.
10
+ #
11
+ # Detection method: compaction DECREASES the session JSONL line count in-place
12
+ # (same session_id, fewer lines). We track {session_id, line_count} between
13
+ # invocations in .session-state to detect this.
14
+ #
15
+ # Ported from OpenClaw's post-compaction-context.ts pattern.
16
+ #
17
+ # FILES:
18
+ # ~/.claude/ftm-state/blackboard/context.json - session gate
19
+ # ~/.claude/ftm-state/.session-state - JSON: {session_id, line_count}
20
+
21
+ FTM_STATE="$HOME/.claude/ftm-state"
22
+ CONTEXT_JSON="$FTM_STATE/blackboard/context.json"
23
+ SESSION_STATE_FILE="$FTM_STATE/.session-state"
24
+
25
+ # 1. Only run during active ftm sessions
26
+ FTM_ACTIVE=$(python3 -c "
27
+ import json, sys
28
+ try:
29
+ with open('$CONTEXT_JSON') as f:
30
+ d = json.load(f)
31
+ task = d.get('current_task', {})
32
+ status = task.get('status', '')
33
+ print('1' if status not in ('', 'completed', 'none') else '0')
34
+ except Exception:
35
+ print('0')
36
+ " 2>/dev/null)
37
+
38
+ if [ "$FTM_ACTIVE" != "1" ]; then
39
+ exit 0
40
+ fi
41
+
42
+ # 2. Read hook payload from stdin
43
+ PAYLOAD=$(cat)
44
+ if [ -z "$PAYLOAD" ]; then
45
+ exit 0
46
+ fi
47
+
48
+ SESSION_ID=$(echo "$PAYLOAD" | python3 -c "
49
+ import sys, json
50
+ try:
51
+ d = json.load(sys.stdin)
52
+ print(d.get('session_id', ''))
53
+ except:
54
+ print('')
55
+ " 2>/dev/null)
56
+
57
+ TRANSCRIPT_PATH=$(echo "$PAYLOAD" | python3 -c "
58
+ import sys, json
59
+ try:
60
+ d = json.load(sys.stdin)
61
+ print(d.get('transcript_path', ''))
62
+ except:
63
+ print('')
64
+ " 2>/dev/null)
65
+
66
+ if [ -z "$SESSION_ID" ]; then
67
+ exit 0
68
+ fi
69
+
70
+ # 3. Find session JSONL
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
+ # 4. Get current line count
83
+ CURRENT_LINES=$(wc -l < "$JSONL_FILE" 2>/dev/null | tr -d ' ')
84
+ CURRENT_LINES=${CURRENT_LINES:-0}
85
+
86
+ # 5. Read stored state
87
+ STORED_STATE=$(cat "$SESSION_STATE_FILE" 2>/dev/null || echo '{}')
88
+ STORED_SESSION=$(echo "$STORED_STATE" | python3 -c "
89
+ import sys, json
90
+ try:
91
+ d = json.load(sys.stdin)
92
+ print(d.get('session_id', ''))
93
+ except:
94
+ print('')
95
+ " 2>/dev/null)
96
+ STORED_LINES=$(echo "$STORED_STATE" | python3 -c "
97
+ import sys, json
98
+ try:
99
+ d = json.load(sys.stdin)
100
+ print(d.get('line_count', 0))
101
+ except:
102
+ print(0)
103
+ " 2>/dev/null)
104
+
105
+ # 6. Always update stored state with current values
106
+ python3 -c "
107
+ import json
108
+ with open('$SESSION_STATE_FILE', 'w') as f:
109
+ json.dump({'session_id': '$SESSION_ID', 'line_count': $CURRENT_LINES}, f)
110
+ " 2>/dev/null
111
+
112
+ # 7. Detect compaction: same session, line count DECREASED
113
+ if [ "$STORED_SESSION" != "$SESSION_ID" ]; then
114
+ exit 0 # New session, not compaction
115
+ fi
116
+
117
+ if [ -z "$STORED_LINES" ] || [ "$STORED_LINES" = "0" ]; then
118
+ exit 0 # No baseline yet
119
+ fi
120
+
121
+ # Check if line count dropped (with a meaningful threshold to avoid noise)
122
+ DIFF=$(( STORED_LINES - CURRENT_LINES ))
123
+ if [ "$DIFF" -lt 10 ] 2>/dev/null; then
124
+ exit 0 # Line count didn't drop significantly — no compaction
125
+ fi
126
+
127
+ # 8. Compaction detected! Inject context restoration prompt
128
+ TODAY=$(date +%Y-%m-%d)
129
+
130
+ echo ""
131
+ echo "[POST-COMPACTION RESTORE — session context was just compacted]:"
132
+ echo "The conversation was just summarized and your in-session state was reset."
133
+ echo "State was preserved in ftm-state. BEFORE responding to the message below:"
134
+ echo "1. Read ~/.claude/ftm-state/blackboard/context.json (current task and session state)"
135
+ echo "2. Read any relevant daily log or session snapshot from ~/.claude/ftm-state/sessions/"
136
+ echo "Then respond as if you just loaded into a fresh ftm session."
137
+ echo "Do not announce this restoration to the user — just do it and proceed."
138
+ echo ""