feed-the-machine 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +170 -170
  3. package/bin/brain.py +1340 -0
  4. package/bin/convert_claude_skills_to_codex.py +490 -0
  5. package/bin/generate-manifest.mjs +463 -463
  6. package/bin/harden_codex_skills.py +141 -0
  7. package/bin/install.mjs +491 -491
  8. package/bin/migrate-eng-buddy-data.py +875 -0
  9. package/bin/playbook_engine/__init__.py +1 -0
  10. package/bin/playbook_engine/conftest.py +8 -0
  11. package/bin/playbook_engine/extractor.py +33 -0
  12. package/bin/playbook_engine/manager.py +102 -0
  13. package/bin/playbook_engine/models.py +84 -0
  14. package/bin/playbook_engine/registry.py +35 -0
  15. package/bin/playbook_engine/test_extractor.py +72 -0
  16. package/bin/playbook_engine/test_integration.py +129 -0
  17. package/bin/playbook_engine/test_manager.py +85 -0
  18. package/bin/playbook_engine/test_models.py +166 -0
  19. package/bin/playbook_engine/test_registry.py +67 -0
  20. package/bin/playbook_engine/test_tracer.py +86 -0
  21. package/bin/playbook_engine/tracer.py +93 -0
  22. package/bin/tasks_db.py +456 -0
  23. package/docs/HOOKS.md +243 -243
  24. package/docs/INBOX.md +233 -233
  25. package/ftm/SKILL.md +125 -122
  26. package/ftm-audit/SKILL.md +623 -623
  27. package/ftm-audit/references/protocols/PROJECT-PATTERNS.md +91 -91
  28. package/ftm-audit/references/protocols/RUNTIME-WIRING.md +66 -66
  29. package/ftm-audit/references/protocols/WIRING-CONTRACTS.md +135 -135
  30. package/ftm-audit/references/strategies/AUTO-FIX-STRATEGIES.md +69 -69
  31. package/ftm-audit/references/templates/REPORT-FORMAT.md +96 -96
  32. package/ftm-audit/scripts/run-knip.sh +23 -23
  33. package/ftm-audit.yml +2 -2
  34. package/ftm-brainstorm/SKILL.md +1003 -498
  35. package/ftm-brainstorm/evals/evals.json +180 -100
  36. package/ftm-brainstorm/evals/promptfoo.yaml +109 -109
  37. package/ftm-brainstorm/references/agent-prompts.md +552 -224
  38. package/ftm-brainstorm/references/plan-template.md +209 -121
  39. package/ftm-brainstorm.yml +2 -2
  40. package/ftm-browse/SKILL.md +454 -454
  41. package/ftm-browse/daemon/browser-manager.ts +206 -206
  42. package/ftm-browse/daemon/bun.lock +30 -30
  43. package/ftm-browse/daemon/cli.ts +347 -347
  44. package/ftm-browse/daemon/commands.ts +410 -410
  45. package/ftm-browse/daemon/main.ts +357 -357
  46. package/ftm-browse/daemon/package.json +17 -17
  47. package/ftm-browse/daemon/server.ts +189 -189
  48. package/ftm-browse/daemon/snapshot.ts +519 -519
  49. package/ftm-browse/daemon/tsconfig.json +22 -22
  50. package/ftm-browse.yml +4 -4
  51. package/ftm-capture/SKILL.md +370 -370
  52. package/ftm-capture.yml +4 -4
  53. package/ftm-codex-gate/SKILL.md +361 -361
  54. package/ftm-codex-gate.yml +2 -2
  55. package/ftm-config/SKILL.md +422 -345
  56. package/ftm-config.default.yml +125 -82
  57. package/ftm-config.yml +44 -2
  58. package/ftm-council/SKILL.md +416 -416
  59. package/ftm-council/references/prompts/CLAUDE-INVESTIGATION.md +60 -60
  60. package/ftm-council/references/prompts/CODEX-INVESTIGATION.md +58 -58
  61. package/ftm-council/references/prompts/GEMINI-INVESTIGATION.md +58 -58
  62. package/ftm-council/references/prompts/REBUTTAL-TEMPLATE.md +57 -57
  63. package/ftm-council/references/protocols/PREREQUISITES.md +47 -47
  64. package/ftm-council/references/protocols/STEP-0-FRAMING.md +46 -46
  65. package/ftm-council.yml +2 -2
  66. package/ftm-dashboard/SKILL.md +163 -163
  67. package/ftm-dashboard.yml +4 -4
  68. package/ftm-debug/SKILL.md +1037 -1037
  69. package/ftm-debug/references/phases/PHASE-0-INTAKE.md +58 -58
  70. package/ftm-debug/references/phases/PHASE-1-TRIAGE.md +46 -46
  71. package/ftm-debug/references/phases/PHASE-2-WAR-ROOM-AGENTS.md +279 -279
  72. package/ftm-debug/references/phases/PHASE-3-TO-6-EXECUTION.md +436 -436
  73. package/ftm-debug/references/protocols/BLACKBOARD.md +86 -86
  74. package/ftm-debug/references/protocols/EDGE-CASES.md +103 -103
  75. package/ftm-debug.yml +2 -2
  76. package/ftm-diagram/SKILL.md +277 -277
  77. package/ftm-diagram.yml +2 -2
  78. package/ftm-executor/SKILL.md +777 -777
  79. package/ftm-executor/references/STYLE-TEMPLATE.md +73 -73
  80. package/ftm-executor/references/phases/PHASE-0-VERIFICATION.md +62 -62
  81. package/ftm-executor/references/phases/PHASE-2-AGENT-ASSEMBLY.md +34 -34
  82. package/ftm-executor/references/phases/PHASE-3-WORKTREES.md +38 -38
  83. package/ftm-executor/references/phases/PHASE-4-5-AUDIT.md +72 -72
  84. package/ftm-executor/references/phases/PHASE-4-DISPATCH.md +66 -66
  85. package/ftm-executor/references/phases/PHASE-5-5-CODEX-GATE.md +73 -73
  86. package/ftm-executor/references/protocols/DOCUMENTATION-BOOTSTRAP.md +36 -36
  87. package/ftm-executor/references/protocols/MODEL-PROFILE.md +59 -59
  88. package/ftm-executor/references/protocols/PROGRESS-TRACKING.md +66 -66
  89. package/ftm-executor/runtime/ftm-runtime.mjs +252 -252
  90. package/ftm-executor/runtime/package.json +8 -8
  91. package/ftm-executor.yml +2 -2
  92. package/ftm-git/SKILL.md +441 -441
  93. package/ftm-git/evals/evals.json +26 -26
  94. package/ftm-git/evals/promptfoo.yaml +75 -75
  95. package/ftm-git/hooks/post-commit-experience.sh +92 -92
  96. package/ftm-git/references/patterns/SECRET-PATTERNS.md +104 -104
  97. package/ftm-git/references/protocols/REMEDIATION.md +139 -139
  98. package/ftm-git/scripts/pre-commit-secrets.sh +110 -110
  99. package/ftm-git.yml +2 -2
  100. package/ftm-inbox/backend/__pycache__/main.cpython-314.pyc +0 -0
  101. package/ftm-inbox/backend/adapters/_retry.py +64 -64
  102. package/ftm-inbox/backend/adapters/base.py +230 -230
  103. package/ftm-inbox/backend/adapters/freshservice.py +104 -104
  104. package/ftm-inbox/backend/adapters/gmail.py +125 -125
  105. package/ftm-inbox/backend/adapters/jira.py +136 -136
  106. package/ftm-inbox/backend/adapters/registry.py +192 -192
  107. package/ftm-inbox/backend/adapters/slack.py +110 -110
  108. package/ftm-inbox/backend/db/connection.py +54 -54
  109. package/ftm-inbox/backend/db/schema.py +78 -78
  110. package/ftm-inbox/backend/executor/__init__.py +7 -7
  111. package/ftm-inbox/backend/executor/engine.py +149 -149
  112. package/ftm-inbox/backend/executor/step_runner.py +98 -98
  113. package/ftm-inbox/backend/main.py +103 -103
  114. package/ftm-inbox/backend/models/__init__.py +1 -1
  115. package/ftm-inbox/backend/models/unified_task.py +36 -36
  116. package/ftm-inbox/backend/planner/__init__.py +6 -6
  117. package/ftm-inbox/backend/planner/__pycache__/__init__.cpython-314.pyc +0 -0
  118. package/ftm-inbox/backend/planner/__pycache__/generator.cpython-314.pyc +0 -0
  119. package/ftm-inbox/backend/planner/__pycache__/schema.cpython-314.pyc +0 -0
  120. package/ftm-inbox/backend/planner/generator.py +127 -127
  121. package/ftm-inbox/backend/planner/schema.py +34 -34
  122. package/ftm-inbox/backend/requirements.txt +5 -5
  123. package/ftm-inbox/backend/routes/__pycache__/plan.cpython-314.pyc +0 -0
  124. package/ftm-inbox/backend/routes/execute.py +186 -186
  125. package/ftm-inbox/backend/routes/health.py +52 -52
  126. package/ftm-inbox/backend/routes/inbox.py +68 -68
  127. package/ftm-inbox/backend/routes/plan.py +271 -271
  128. package/ftm-inbox/bin/launchagent.mjs +91 -91
  129. package/ftm-inbox/bin/setup.mjs +188 -188
  130. package/ftm-inbox/bin/start.sh +10 -10
  131. package/ftm-inbox/bin/status.sh +17 -17
  132. package/ftm-inbox/bin/stop.sh +8 -8
  133. package/ftm-inbox/config.example.yml +55 -55
  134. package/ftm-inbox/package-lock.json +2898 -2898
  135. package/ftm-inbox/package.json +26 -26
  136. package/ftm-inbox/postcss.config.js +6 -6
  137. package/ftm-inbox/src/app.css +199 -199
  138. package/ftm-inbox/src/app.html +18 -18
  139. package/ftm-inbox/src/lib/api.ts +166 -166
  140. package/ftm-inbox/src/lib/components/ExecutionLog.svelte +81 -81
  141. package/ftm-inbox/src/lib/components/InboxFeed.svelte +143 -143
  142. package/ftm-inbox/src/lib/components/PlanStep.svelte +271 -271
  143. package/ftm-inbox/src/lib/components/PlanView.svelte +206 -206
  144. package/ftm-inbox/src/lib/components/StreamPanel.svelte +99 -99
  145. package/ftm-inbox/src/lib/components/TaskCard.svelte +190 -190
  146. package/ftm-inbox/src/lib/components/ui/EmptyState.svelte +63 -63
  147. package/ftm-inbox/src/lib/components/ui/KawaiiCard.svelte +86 -86
  148. package/ftm-inbox/src/lib/components/ui/PillButton.svelte +106 -106
  149. package/ftm-inbox/src/lib/components/ui/StatusBadge.svelte +67 -67
  150. package/ftm-inbox/src/lib/components/ui/StreamDrawer.svelte +149 -149
  151. package/ftm-inbox/src/lib/components/ui/ThemeToggle.svelte +80 -80
  152. package/ftm-inbox/src/lib/theme.ts +47 -47
  153. package/ftm-inbox/src/routes/+layout.svelte +76 -76
  154. package/ftm-inbox/src/routes/+page.svelte +401 -401
  155. package/ftm-inbox/svelte.config.js +12 -12
  156. package/ftm-inbox/tailwind.config.ts +63 -63
  157. package/ftm-inbox/tsconfig.json +13 -13
  158. package/ftm-inbox/vite.config.ts +6 -6
  159. package/ftm-intent/SKILL.md +241 -241
  160. package/ftm-intent.yml +2 -2
  161. package/ftm-manifest.json +3794 -3794
  162. package/ftm-map/SKILL.md +291 -291
  163. package/ftm-map/scripts/db.py +712 -712
  164. package/ftm-map/scripts/index.py +415 -415
  165. package/ftm-map/scripts/parser.py +224 -224
  166. package/ftm-map/scripts/queries/go-tags.scm +20 -20
  167. package/ftm-map/scripts/queries/javascript-tags.scm +35 -35
  168. package/ftm-map/scripts/queries/python-tags.scm +31 -31
  169. package/ftm-map/scripts/queries/ruby-tags.scm +19 -19
  170. package/ftm-map/scripts/queries/rust-tags.scm +37 -37
  171. package/ftm-map/scripts/queries/typescript-tags.scm +41 -41
  172. package/ftm-map/scripts/query.py +301 -301
  173. package/ftm-map/scripts/ranker.py +377 -377
  174. package/ftm-map/scripts/requirements.txt +5 -5
  175. package/ftm-map/scripts/setup-hooks.sh +27 -27
  176. package/ftm-map/scripts/setup.sh +56 -56
  177. package/ftm-map/scripts/test_db.py +364 -364
  178. package/ftm-map/scripts/test_parser.py +174 -174
  179. package/ftm-map/scripts/test_query.py +183 -183
  180. package/ftm-map/scripts/test_ranker.py +199 -199
  181. package/ftm-map/scripts/views.py +591 -591
  182. package/ftm-map.yml +2 -2
  183. package/ftm-mind/SKILL.md +201 -1943
  184. package/ftm-mind/evals/promptfoo.yaml +142 -142
  185. package/ftm-mind/references/blackboard-protocol.md +110 -0
  186. package/ftm-mind/references/blackboard-schema.md +328 -328
  187. package/ftm-mind/references/complexity-guide.md +110 -110
  188. package/ftm-mind/references/complexity-sizing.md +138 -0
  189. package/ftm-mind/references/decide-act-protocol.md +172 -0
  190. package/ftm-mind/references/direct-execution.md +51 -0
  191. package/ftm-mind/references/environment-discovery.md +77 -0
  192. package/ftm-mind/references/event-registry.md +319 -319
  193. package/ftm-mind/references/mcp-inventory.md +300 -296
  194. package/ftm-mind/references/ops-routing.md +47 -0
  195. package/ftm-mind/references/orient-protocol.md +234 -0
  196. package/ftm-mind/references/personality.md +40 -0
  197. package/ftm-mind/references/protocols/COMPLEXITY-SIZING.md +72 -72
  198. package/ftm-mind/references/protocols/MCP-HEURISTICS.md +32 -32
  199. package/ftm-mind/references/protocols/PLAN-APPROVAL.md +80 -80
  200. package/ftm-mind/references/reflexion-protocol.md +249 -249
  201. package/ftm-mind/references/routing/SCENARIOS.md +22 -22
  202. package/ftm-mind/references/routing-scenarios.md +35 -35
  203. package/ftm-mind.yml +2 -2
  204. package/ftm-ops.yml +4 -0
  205. package/ftm-pause/SKILL.md +395 -395
  206. package/ftm-pause/references/protocols/SKILL-RESTORE-PROTOCOLS.md +186 -186
  207. package/ftm-pause/references/protocols/VALIDATION.md +80 -80
  208. package/ftm-pause.yml +2 -2
  209. package/ftm-researcher/SKILL.md +275 -275
  210. package/ftm-researcher/evals/agent-diversity.yaml +17 -17
  211. package/ftm-researcher/evals/synthesis-quality.yaml +12 -12
  212. package/ftm-researcher/evals/trigger-accuracy.yaml +39 -39
  213. package/ftm-researcher/references/adaptive-search.md +116 -116
  214. package/ftm-researcher/references/agent-prompts.md +193 -193
  215. package/ftm-researcher/references/council-integration.md +193 -193
  216. package/ftm-researcher/references/output-format.md +203 -203
  217. package/ftm-researcher/references/synthesis-pipeline.md +165 -165
  218. package/ftm-researcher/scripts/score_credibility.py +234 -234
  219. package/ftm-researcher/scripts/validate_research.py +92 -92
  220. package/ftm-researcher.yml +2 -2
  221. package/ftm-resume/SKILL.md +518 -518
  222. package/ftm-resume/references/protocols/VALIDATION.md +172 -172
  223. package/ftm-resume.yml +2 -2
  224. package/ftm-retro/SKILL.md +380 -380
  225. package/ftm-retro/references/protocols/SCORING-RUBRICS.md +89 -89
  226. package/ftm-retro/references/templates/REPORT-FORMAT.md +109 -109
  227. package/ftm-retro.yml +2 -2
  228. package/ftm-routine/SKILL.md +170 -170
  229. package/ftm-routine.yml +4 -4
  230. package/ftm-state/blackboard/capabilities.json +5 -5
  231. package/ftm-state/blackboard/capabilities.schema.json +27 -27
  232. package/ftm-state/blackboard/context.json +37 -23
  233. package/ftm-state/blackboard/experiences/doom-statusline-fix.json +26 -0
  234. package/ftm-state/blackboard/experiences/hackathon-pages-site.json +26 -0
  235. package/ftm-state/blackboard/experiences/hindsight-sso-kickoff.json +42 -0
  236. package/ftm-state/blackboard/experiences/index.json +58 -9
  237. package/ftm-state/blackboard/experiences/learning-ragnarok-api-access.json +23 -0
  238. package/ftm-state/blackboard/experiences/nordlayer-members-auto-assign.json +26 -0
  239. package/ftm-state/blackboard/experiences/saml2aws-stale-session-fix.json +41 -0
  240. package/ftm-state/blackboard/patterns.json +6 -6
  241. package/ftm-state/schemas/context.schema.json +130 -130
  242. package/ftm-state/schemas/experience-index.schema.json +77 -77
  243. package/ftm-state/schemas/experience.schema.json +78 -78
  244. package/ftm-state/schemas/patterns.schema.json +44 -44
  245. package/ftm-upgrade/SKILL.md +194 -194
  246. package/ftm-upgrade/scripts/check-version.sh +76 -76
  247. package/ftm-upgrade/scripts/upgrade.sh +143 -143
  248. package/ftm-upgrade.yml +2 -2
  249. package/ftm-verify.yml +2 -2
  250. package/ftm.yml +2 -2
  251. package/hooks/ftm-auto-log.sh +137 -0
  252. package/hooks/ftm-blackboard-enforcer.sh +93 -93
  253. package/hooks/ftm-discovery-reminder.sh +90 -90
  254. package/hooks/ftm-drafts-gate.sh +61 -61
  255. package/hooks/ftm-event-logger.mjs +107 -107
  256. package/hooks/ftm-install-hooks.sh +240 -0
  257. package/hooks/ftm-learning-capture.sh +117 -0
  258. package/hooks/ftm-map-autodetect.sh +79 -79
  259. package/hooks/ftm-pending-sync-check.sh +22 -22
  260. package/hooks/ftm-plan-gate.sh +92 -92
  261. package/hooks/ftm-post-commit-trigger.sh +57 -57
  262. package/hooks/ftm-post-compaction.sh +138 -0
  263. package/hooks/ftm-pre-compaction.sh +147 -0
  264. package/hooks/ftm-session-end.sh +52 -0
  265. package/hooks/ftm-session-snapshot.sh +213 -0
  266. package/hooks/settings-template.json +81 -81
  267. package/install.sh +363 -363
  268. package/package.json +84 -84
  269. package/uninstall.sh +25 -25
@@ -1,76 +1,76 @@
1
- #!/usr/bin/env bash
2
- # check-version.sh — Check for feed-the-machine updates via GitHub Releases
3
- # Outputs: UP_TO_DATE | UPGRADE_AVAILABLE <version> <changelog_url> | CHECK_FAILED <reason>
4
-
5
- set -uo pipefail
6
-
7
- CACHE_DIR="${HOME}/.cache/ftm-brain"
8
- CACHE_FILE="${CACHE_DIR}/version-check"
9
- VERSION_FILE="${HOME}/.claude/skills/ftm-version.txt"
10
- REPO="kkudumu/ftm-brain"
11
-
12
- # Ensure cache directory exists
13
- mkdir -p "${CACHE_DIR}"
14
-
15
- # Check if cache is fresh (less than 60 minutes old)
16
- if [ -f "${CACHE_FILE}" ]; then
17
- STALE=$(find "${CACHE_FILE}" -mmin +60 2>/dev/null | wc -l | tr -d ' ')
18
- if [ "${STALE}" = "0" ]; then
19
- # Cache is fresh — return cached result
20
- cat "${CACHE_FILE}"
21
- exit 0
22
- fi
23
- fi
24
-
25
- # Check that gh CLI is installed
26
- if ! command -v gh >/dev/null 2>&1; then
27
- RESULT="CHECK_FAILED gh_not_installed"
28
- printf '%s\n' "${RESULT}" | tee "${CACHE_FILE}"
29
- exit 0
30
- fi
31
-
32
- # Query latest release from GitHub
33
- RELEASE_OUTPUT=$(gh release list -R "${REPO}" --limit 1 2>&1)
34
- GH_EXIT=$?
35
-
36
- if [ ${GH_EXIT} -ne 0 ]; then
37
- # Distinguish between network and repo errors
38
- if printf '%s' "${RELEASE_OUTPUT}" | grep -qi "could not resolve\|network\|timeout\|no such host"; then
39
- RESULT="CHECK_FAILED no_internet"
40
- elif printf '%s' "${RELEASE_OUTPUT}" | grep -qi "not found\|404\|Could not find"; then
41
- RESULT="CHECK_FAILED repo_not_found"
42
- else
43
- REASON=$(printf '%s' "${RELEASE_OUTPUT}" | tr '\n' ' ' | cut -c1-80)
44
- RESULT="CHECK_FAILED ${REASON}"
45
- fi
46
- printf '%s\n' "${RESULT}" | tee "${CACHE_FILE}"
47
- exit 0
48
- fi
49
-
50
- # Parse latest release tag (first column of gh release list output)
51
- LATEST_TAG=$(printf '%s' "${RELEASE_OUTPUT}" | awk 'NR==1{print $1}')
52
-
53
- if [ -z "${LATEST_TAG}" ]; then
54
- RESULT="CHECK_FAILED no_releases_found"
55
- printf '%s\n' "${RESULT}" | tee "${CACHE_FILE}"
56
- exit 0
57
- fi
58
-
59
- # Read current installed version
60
- if [ -f "${VERSION_FILE}" ]; then
61
- CURRENT_VERSION=$(tr -d '[:space:]' < "${VERSION_FILE}")
62
- else
63
- CURRENT_VERSION="unknown"
64
- fi
65
-
66
- # Compare versions
67
- if [ "${CURRENT_VERSION}" = "${LATEST_TAG}" ]; then
68
- RESULT="UP_TO_DATE"
69
- else
70
- CHANGELOG_URL="https://github.com/${REPO}/releases/tag/${LATEST_TAG}"
71
- RESULT="UPGRADE_AVAILABLE ${LATEST_TAG} ${CHANGELOG_URL}"
72
- fi
73
-
74
- # Write to cache and output
75
- printf '%s\n' "${RESULT}" | tee "${CACHE_FILE}"
76
- exit 0
1
+ #!/usr/bin/env bash
2
+ # check-version.sh — Check for feed-the-machine updates via GitHub Releases
3
+ # Outputs: UP_TO_DATE | UPGRADE_AVAILABLE <version> <changelog_url> | CHECK_FAILED <reason>
4
+
5
+ set -uo pipefail
6
+
7
+ CACHE_DIR="${HOME}/.cache/ftm-brain"
8
+ CACHE_FILE="${CACHE_DIR}/version-check"
9
+ VERSION_FILE="${HOME}/.claude/skills/ftm-version.txt"
10
+ REPO="kkudumu/ftm-brain"
11
+
12
+ # Ensure cache directory exists
13
+ mkdir -p "${CACHE_DIR}"
14
+
15
+ # Check if cache is fresh (less than 60 minutes old)
16
+ if [ -f "${CACHE_FILE}" ]; then
17
+ STALE=$(find "${CACHE_FILE}" -mmin +60 2>/dev/null | wc -l | tr -d ' ')
18
+ if [ "${STALE}" = "0" ]; then
19
+ # Cache is fresh — return cached result
20
+ cat "${CACHE_FILE}"
21
+ exit 0
22
+ fi
23
+ fi
24
+
25
+ # Check that gh CLI is installed
26
+ if ! command -v gh >/dev/null 2>&1; then
27
+ RESULT="CHECK_FAILED gh_not_installed"
28
+ printf '%s\n' "${RESULT}" | tee "${CACHE_FILE}"
29
+ exit 0
30
+ fi
31
+
32
+ # Query latest release from GitHub
33
+ RELEASE_OUTPUT=$(gh release list -R "${REPO}" --limit 1 2>&1)
34
+ GH_EXIT=$?
35
+
36
+ if [ ${GH_EXIT} -ne 0 ]; then
37
+ # Distinguish between network and repo errors
38
+ if printf '%s' "${RELEASE_OUTPUT}" | grep -qi "could not resolve\|network\|timeout\|no such host"; then
39
+ RESULT="CHECK_FAILED no_internet"
40
+ elif printf '%s' "${RELEASE_OUTPUT}" | grep -qi "not found\|404\|Could not find"; then
41
+ RESULT="CHECK_FAILED repo_not_found"
42
+ else
43
+ REASON=$(printf '%s' "${RELEASE_OUTPUT}" | tr '\n' ' ' | cut -c1-80)
44
+ RESULT="CHECK_FAILED ${REASON}"
45
+ fi
46
+ printf '%s\n' "${RESULT}" | tee "${CACHE_FILE}"
47
+ exit 0
48
+ fi
49
+
50
+ # Parse latest release tag (first column of gh release list output)
51
+ LATEST_TAG=$(printf '%s' "${RELEASE_OUTPUT}" | awk 'NR==1{print $1}')
52
+
53
+ if [ -z "${LATEST_TAG}" ]; then
54
+ RESULT="CHECK_FAILED no_releases_found"
55
+ printf '%s\n' "${RESULT}" | tee "${CACHE_FILE}"
56
+ exit 0
57
+ fi
58
+
59
+ # Read current installed version
60
+ if [ -f "${VERSION_FILE}" ]; then
61
+ CURRENT_VERSION=$(tr -d '[:space:]' < "${VERSION_FILE}")
62
+ else
63
+ CURRENT_VERSION="unknown"
64
+ fi
65
+
66
+ # Compare versions
67
+ if [ "${CURRENT_VERSION}" = "${LATEST_TAG}" ]; then
68
+ RESULT="UP_TO_DATE"
69
+ else
70
+ CHANGELOG_URL="https://github.com/${REPO}/releases/tag/${LATEST_TAG}"
71
+ RESULT="UPGRADE_AVAILABLE ${LATEST_TAG} ${CHANGELOG_URL}"
72
+ fi
73
+
74
+ # Write to cache and output
75
+ printf '%s\n' "${RESULT}" | tee "${CACHE_FILE}"
76
+ exit 0
@@ -1,143 +1,143 @@
1
- #!/usr/bin/env bash
2
- # upgrade.sh — Download and install latest feed-the-machine release
3
- # Usage: upgrade.sh [--version <tag>]
4
-
5
- set -uo pipefail
6
-
7
- REPO="kkudumu/ftm-brain"
8
- SKILLS_DIR="${HOME}/.claude/skills"
9
- VERSION_FILE="${SKILLS_DIR}/ftm-version.txt"
10
- DOWNLOAD_DIR="/tmp/ftm-upgrade"
11
- CACHE_FILE="${HOME}/.cache/ftm-brain/version-check"
12
-
13
- # ── Helpers ──────────────────────────────────────────────────────────────────
14
-
15
- log() { printf '[ftm-upgrade] %s\n' "$*"; }
16
- die() { printf '[ftm-upgrade] ERROR: %s\n' "$*" >&2; exit 1; }
17
-
18
- # ── Preflight checks ─────────────────────────────────────────────────────────
19
-
20
- if ! command -v gh >/dev/null 2>&1; then
21
- die "GitHub CLI (gh) is not installed. Install it with: brew install gh"
22
- fi
23
-
24
- if ! command -v tar >/dev/null 2>&1; then
25
- die "tar is required but not found."
26
- fi
27
-
28
- # ── Determine target version ─────────────────────────────────────────────────
29
-
30
- TARGET_VERSION=""
31
- while [ $# -gt 0 ]; do
32
- case "$1" in
33
- --version) TARGET_VERSION="$2"; shift 2 ;;
34
- *) die "Unknown argument: $1" ;;
35
- esac
36
- done
37
-
38
- if [ -z "${TARGET_VERSION}" ]; then
39
- log "Fetching latest release tag..."
40
- TARGET_VERSION=$(gh release list -R "${REPO}" --limit 1 2>&1 | awk 'NR==1{print $1}')
41
- if [ -z "${TARGET_VERSION}" ]; then
42
- die "Could not determine latest release. Check GitHub access and repository name."
43
- fi
44
- fi
45
-
46
- log "Target version: ${TARGET_VERSION}"
47
-
48
- # ── Read current version ─────────────────────────────────────────────────────
49
-
50
- if [ -f "${VERSION_FILE}" ]; then
51
- CURRENT_VERSION=$(tr -d '[:space:]' < "${VERSION_FILE}")
52
- else
53
- CURRENT_VERSION="(not installed)"
54
- fi
55
-
56
- log "Current version: ${CURRENT_VERSION}"
57
-
58
- if [ "${CURRENT_VERSION}" = "${TARGET_VERSION}" ]; then
59
- log "Already on the latest version (${TARGET_VERSION}). Nothing to do."
60
- exit 0
61
- fi
62
-
63
- # ── Download ──────────────────────────────────────────────────────────────────
64
-
65
- rm -rf "${DOWNLOAD_DIR}"
66
- mkdir -p "${DOWNLOAD_DIR}"
67
-
68
- log "Downloading release archive for ${TARGET_VERSION}..."
69
-
70
- gh release download "${TARGET_VERSION}" \
71
- -R "${REPO}" \
72
- --archive tar.gz \
73
- -D "${DOWNLOAD_DIR}" 2>&1 || die "Download failed. Check your internet connection and repository access."
74
-
75
- # Find the downloaded archive
76
- ARCHIVE=$(find "${DOWNLOAD_DIR}" -maxdepth 1 -name '*.tar.gz' | head -1)
77
- if [ -z "${ARCHIVE}" ]; then
78
- die "No archive found in ${DOWNLOAD_DIR} after download."
79
- fi
80
-
81
- log "Downloaded: ${ARCHIVE}"
82
-
83
- # ── Extract ───────────────────────────────────────────────────────────────────
84
-
85
- EXTRACT_DIR="${DOWNLOAD_DIR}/extracted"
86
- mkdir -p "${EXTRACT_DIR}"
87
-
88
- log "Extracting archive..."
89
- tar -xzf "${ARCHIVE}" -C "${EXTRACT_DIR}" 2>&1 || die "Extraction failed. Archive may be corrupt."
90
-
91
- # GitHub archives typically extract into a directory named <repo>-<tag>/
92
- REPO_DIR=$(find "${EXTRACT_DIR}" -maxdepth 1 -mindepth 1 -type d | head -1)
93
- if [ -z "${REPO_DIR}" ]; then
94
- die "Could not find extracted repository directory."
95
- fi
96
-
97
- log "Extracted to: ${REPO_DIR}"
98
-
99
- # ── Copy skill files ──────────────────────────────────────────────────────────
100
-
101
- # Look for a skills/ subdirectory in the archive; fall back to root
102
- SOURCE_SKILLS=""
103
- if [ -d "${REPO_DIR}/skills" ]; then
104
- SOURCE_SKILLS="${REPO_DIR}/skills"
105
- elif [ -d "${REPO_DIR}" ]; then
106
- # The repo root might itself contain SKILL.md files and .yml files
107
- SOURCE_SKILLS="${REPO_DIR}"
108
- fi
109
-
110
- if [ -z "${SOURCE_SKILLS}" ]; then
111
- die "No skills directory found in release archive."
112
- fi
113
-
114
- log "Copying skill files from ${SOURCE_SKILLS} to ${SKILLS_DIR}..."
115
-
116
- # Use rsync if available for cleaner copy, else cp
117
- if command -v rsync >/dev/null 2>&1; then
118
- rsync -av --exclude='.git' "${SOURCE_SKILLS}/" "${SKILLS_DIR}/" 2>&1
119
- else
120
- cp -R "${SOURCE_SKILLS}/." "${SKILLS_DIR}/" 2>&1
121
- fi
122
-
123
- # ── Update version file ───────────────────────────────────────────────────────
124
-
125
- printf '%s\n' "${TARGET_VERSION}" > "${VERSION_FILE}"
126
-
127
- # Invalidate version cache so next check-version.sh run is fresh
128
- rm -f "${CACHE_FILE}"
129
-
130
- # ── Summary ───────────────────────────────────────────────────────────────────
131
-
132
- log ""
133
- log "Upgrade complete."
134
- log " ${CURRENT_VERSION} → ${TARGET_VERSION}"
135
- log " Changelog: https://github.com/${REPO}/releases/tag/${TARGET_VERSION}"
136
- log ""
137
- log "Installed files updated in: ${SKILLS_DIR}"
138
-
139
- # ── Cleanup ───────────────────────────────────────────────────────────────────
140
-
141
- rm -rf "${DOWNLOAD_DIR}"
142
-
143
- exit 0
1
+ #!/usr/bin/env bash
2
+ # upgrade.sh — Download and install latest feed-the-machine release
3
+ # Usage: upgrade.sh [--version <tag>]
4
+
5
+ set -uo pipefail
6
+
7
+ REPO="kkudumu/ftm-brain"
8
+ SKILLS_DIR="${HOME}/.claude/skills"
9
+ VERSION_FILE="${SKILLS_DIR}/ftm-version.txt"
10
+ DOWNLOAD_DIR="/tmp/ftm-upgrade"
11
+ CACHE_FILE="${HOME}/.cache/ftm-brain/version-check"
12
+
13
+ # ── Helpers ──────────────────────────────────────────────────────────────────
14
+
15
+ log() { printf '[ftm-upgrade] %s\n' "$*"; }
16
+ die() { printf '[ftm-upgrade] ERROR: %s\n' "$*" >&2; exit 1; }
17
+
18
+ # ── Preflight checks ─────────────────────────────────────────────────────────
19
+
20
+ if ! command -v gh >/dev/null 2>&1; then
21
+ die "GitHub CLI (gh) is not installed. Install it with: brew install gh"
22
+ fi
23
+
24
+ if ! command -v tar >/dev/null 2>&1; then
25
+ die "tar is required but not found."
26
+ fi
27
+
28
+ # ── Determine target version ─────────────────────────────────────────────────
29
+
30
+ TARGET_VERSION=""
31
+ while [ $# -gt 0 ]; do
32
+ case "$1" in
33
+ --version) TARGET_VERSION="$2"; shift 2 ;;
34
+ *) die "Unknown argument: $1" ;;
35
+ esac
36
+ done
37
+
38
+ if [ -z "${TARGET_VERSION}" ]; then
39
+ log "Fetching latest release tag..."
40
+ TARGET_VERSION=$(gh release list -R "${REPO}" --limit 1 2>&1 | awk 'NR==1{print $1}')
41
+ if [ -z "${TARGET_VERSION}" ]; then
42
+ die "Could not determine latest release. Check GitHub access and repository name."
43
+ fi
44
+ fi
45
+
46
+ log "Target version: ${TARGET_VERSION}"
47
+
48
+ # ── Read current version ─────────────────────────────────────────────────────
49
+
50
+ if [ -f "${VERSION_FILE}" ]; then
51
+ CURRENT_VERSION=$(tr -d '[:space:]' < "${VERSION_FILE}")
52
+ else
53
+ CURRENT_VERSION="(not installed)"
54
+ fi
55
+
56
+ log "Current version: ${CURRENT_VERSION}"
57
+
58
+ if [ "${CURRENT_VERSION}" = "${TARGET_VERSION}" ]; then
59
+ log "Already on the latest version (${TARGET_VERSION}). Nothing to do."
60
+ exit 0
61
+ fi
62
+
63
+ # ── Download ──────────────────────────────────────────────────────────────────
64
+
65
+ rm -rf "${DOWNLOAD_DIR}"
66
+ mkdir -p "${DOWNLOAD_DIR}"
67
+
68
+ log "Downloading release archive for ${TARGET_VERSION}..."
69
+
70
+ gh release download "${TARGET_VERSION}" \
71
+ -R "${REPO}" \
72
+ --archive tar.gz \
73
+ -D "${DOWNLOAD_DIR}" 2>&1 || die "Download failed. Check your internet connection and repository access."
74
+
75
+ # Find the downloaded archive
76
+ ARCHIVE=$(find "${DOWNLOAD_DIR}" -maxdepth 1 -name '*.tar.gz' | head -1)
77
+ if [ -z "${ARCHIVE}" ]; then
78
+ die "No archive found in ${DOWNLOAD_DIR} after download."
79
+ fi
80
+
81
+ log "Downloaded: ${ARCHIVE}"
82
+
83
+ # ── Extract ───────────────────────────────────────────────────────────────────
84
+
85
+ EXTRACT_DIR="${DOWNLOAD_DIR}/extracted"
86
+ mkdir -p "${EXTRACT_DIR}"
87
+
88
+ log "Extracting archive..."
89
+ tar -xzf "${ARCHIVE}" -C "${EXTRACT_DIR}" 2>&1 || die "Extraction failed. Archive may be corrupt."
90
+
91
+ # GitHub archives typically extract into a directory named <repo>-<tag>/
92
+ REPO_DIR=$(find "${EXTRACT_DIR}" -maxdepth 1 -mindepth 1 -type d | head -1)
93
+ if [ -z "${REPO_DIR}" ]; then
94
+ die "Could not find extracted repository directory."
95
+ fi
96
+
97
+ log "Extracted to: ${REPO_DIR}"
98
+
99
+ # ── Copy skill files ──────────────────────────────────────────────────────────
100
+
101
+ # Look for a skills/ subdirectory in the archive; fall back to root
102
+ SOURCE_SKILLS=""
103
+ if [ -d "${REPO_DIR}/skills" ]; then
104
+ SOURCE_SKILLS="${REPO_DIR}/skills"
105
+ elif [ -d "${REPO_DIR}" ]; then
106
+ # The repo root might itself contain SKILL.md files and .yml files
107
+ SOURCE_SKILLS="${REPO_DIR}"
108
+ fi
109
+
110
+ if [ -z "${SOURCE_SKILLS}" ]; then
111
+ die "No skills directory found in release archive."
112
+ fi
113
+
114
+ log "Copying skill files from ${SOURCE_SKILLS} to ${SKILLS_DIR}..."
115
+
116
+ # Use rsync if available for cleaner copy, else cp
117
+ if command -v rsync >/dev/null 2>&1; then
118
+ rsync -av --exclude='.git' "${SOURCE_SKILLS}/" "${SKILLS_DIR}/" 2>&1
119
+ else
120
+ cp -R "${SOURCE_SKILLS}/." "${SKILLS_DIR}/" 2>&1
121
+ fi
122
+
123
+ # ── Update version file ───────────────────────────────────────────────────────
124
+
125
+ printf '%s\n' "${TARGET_VERSION}" > "${VERSION_FILE}"
126
+
127
+ # Invalidate version cache so next check-version.sh run is fresh
128
+ rm -f "${CACHE_FILE}"
129
+
130
+ # ── Summary ───────────────────────────────────────────────────────────────────
131
+
132
+ log ""
133
+ log "Upgrade complete."
134
+ log " ${CURRENT_VERSION} → ${TARGET_VERSION}"
135
+ log " Changelog: https://github.com/${REPO}/releases/tag/${TARGET_VERSION}"
136
+ log ""
137
+ log "Installed files updated in: ${SKILLS_DIR}"
138
+
139
+ # ── Cleanup ───────────────────────────────────────────────────────────────────
140
+
141
+ rm -rf "${DOWNLOAD_DIR}"
142
+
143
+ exit 0
package/ftm-upgrade.yml CHANGED
@@ -1,2 +1,2 @@
1
- name: ftm-upgrade
2
- description: Self-upgrade mechanism for ftm skills ecosystem. Checks GitHub Releases for new versions, caches results for 60min, shows changelog, and copies updated files. Use when user says "upgrade", "update skills", "new version", "check for updates", "ftm upgrade".
1
+ name: ftm-upgrade
2
+ description: Self-upgrade mechanism for ftm skills ecosystem. Checks GitHub Releases for new versions, caches results for 60min, shows changelog, and copies updated files. Use when user says "upgrade", "update skills", "new version", "check for updates", "ftm upgrade".
package/ftm-verify.yml CHANGED
@@ -1,2 +1,2 @@
1
- name: ftm-verify
2
- description: Comprehensive post-execution verification and auto-remediation engine using dual-model adversarial analysis. Replaces ftm-retro. After ftm-executor completes a plan, this skill runs two independent verification passes in parallel — Codex (OpenAI) and Gemini (Google) — each reading the entire codebase to check plan fulfillment, documentation fidelity, build health, test quality, and wiring integrity. Falls back to Claude subagents if either CLI is unavailable. Reconciles findings from both models, auto-remediates with parallel fix agents, and reports what was found, disagreed on, and fixed. Use when user says "verify", "is the plan done", "check everything", "verify plan", "ftm-verify", "did we miss anything", "is it complete", "validate the build", "check the plan", "verify execution", "post-execution check", or after any ftm-executor run completes. Also triggers on "retro", "retrospective", "how did that go", "execution review" since this skill supersedes ftm-retro. Even if the user just says "are we good?" after a plan execution — this is the skill.
1
+ name: ftm-verify
2
+ description: Comprehensive post-execution verification and auto-remediation engine using dual-model adversarial analysis. Replaces ftm-retro. After ftm-executor completes a plan, this skill runs two independent verification passes in parallel — Codex (OpenAI) and Gemini (Google) — each reading the entire codebase to check plan fulfillment, documentation fidelity, build health, test quality, and wiring integrity. Falls back to Claude subagents if either CLI is unavailable. Reconciles findings from both models, auto-remediates with parallel fix agents, and reports what was found, disagreed on, and fixed. Use when user says "verify", "is the plan done", "check everything", "verify plan", "ftm-verify", "did we miss anything", "is it complete", "validate the build", "check the plan", "verify execution", "post-execution check", or after any ftm-executor run completes. Also triggers on "retro", "retrospective", "how did that go", "execution review" since this skill supersedes ftm-retro. Even if the user just says "are we good?" after a plan execution — this is the skill.
package/ftm.yml CHANGED
@@ -1,2 +1,2 @@
1
- name: ftm
2
- description: Universal entry point for all ftm skills. Routes freeform text to the right ftm skill automatically. Use when user says "/ftm" followed by anything. ftm-mind is the default cognitive entry point for all unclassified input (OODA reasoning). Explicit prefixes route directly: brainstorm, execute, debug, audit, council, intent, diagram, codex-gate, pause, resume, browse, upgrade, retro, config, mind. Also use when user is unsure which ftm skill they need.
1
+ name: ftm
2
+ description: Universal entry point for all ftm skills. Routes freeform text to the right ftm skill automatically. Use when user says "/ftm" followed by anything. ftm-mind is the default cognitive entry point for all unclassified input (OODA reasoning). Explicit prefixes route directly: brainstorm, execute, debug, audit, council, intent, diagram, codex-gate, pause, resume, browse, upgrade, retro, config, mind. Also use when user is unsure which ftm skill they need.
@@ -0,0 +1,137 @@
1
+ #!/bin/bash
2
+ # Hook: Auto-log progress + heartbeat for ftm
3
+ # Triggers: UserPromptSubmit while an ftm session is active
4
+ #
5
+ # 1. Auto-log: reminds Claude to log progress when user reports completing something
6
+ # 2. Task inbox: surfaces unreviewed Slack/email tasks every 10 min
7
+ # 3. Heartbeat: every 30 min, scans task state for urgent items to surface
8
+ #
9
+ # Heartbeat inspired by OpenClaw's HEARTBEAT.md pattern (heartbeat.ts):
10
+ # Periodically checks task state and prompts Claude to surface time-sensitive
11
+ # items without the user asking.
12
+ #
13
+ # FILES:
14
+ # ~/.claude/ftm-state/blackboard/context.json - session gate (active_task check)
15
+ # ~/.claude/ftm-state/.last-heartbeat - timestamp of last heartbeat
16
+ # ~/.claude/ftm-state/HEARTBEAT.md - user-maintained alert/task config
17
+
18
+ FTM_STATE="$HOME/.claude/ftm-state"
19
+ CONTEXT_JSON="$FTM_STATE/blackboard/context.json"
20
+
21
+ # Check if an ftm session is active by reading context.json
22
+ FTM_ACTIVE=$(python3 -c "
23
+ import json, sys
24
+ try:
25
+ with open('$CONTEXT_JSON') as f:
26
+ d = json.load(f)
27
+ task = d.get('current_task', {})
28
+ status = task.get('status', '')
29
+ print('1' if status not in ('', 'completed', 'none') else '0')
30
+ except Exception:
31
+ print('0')
32
+ " 2>/dev/null)
33
+
34
+ if [ "$FTM_ACTIVE" != "1" ]; then
35
+ exit 0 # Not in active ftm session, skip
36
+ fi
37
+
38
+ # Read payload from stdin (JSON) — extract prompt for action pattern matching
39
+ STDIN_DATA=$(cat)
40
+ USER_MESSAGE=$(echo "$STDIN_DATA" | python3 -c "
41
+ import sys, json
42
+ try:
43
+ d = json.load(sys.stdin)
44
+ print(d.get('prompt', ''))
45
+ except:
46
+ print('')
47
+ " 2>/dev/null)
48
+ # Fallback: treat stdin as raw message if JSON parse failed
49
+ if [ -z "$USER_MESSAGE" ]; then
50
+ USER_MESSAGE="$STDIN_DATA"
51
+ fi
52
+
53
+ # Action indicators (what users say when they've done something)
54
+ ACTION_PATTERNS=(
55
+ "^[Ii] (did|completed?|finished|fixed|sent|responded|closed|created|updated|deployed|merged|pushed|committed|tested|reviewed)"
56
+ "^[Jj]ust (did|completed?|finished|fixed|sent|responded|closed|created|updated|deployed|merged|pushed|committed|tested|reviewed)"
57
+ "^[Dd]one"
58
+ "^[Ff]inished"
59
+ "^[Cc]ompleted?"
60
+ "^[Ss]ent (email|message|response)"
61
+ "^[Rr]esponded to"
62
+ "^[Mm]erged"
63
+ "^[Pp]ushed to"
64
+ "^[Cc]ommitted"
65
+ "^[Dd]eployed"
66
+ "^[Ff]ixed"
67
+ "^[Cc]losed (ticket|issue|task)"
68
+ "[Tt]ask.*complete"
69
+ "[Tt]icket.*closed"
70
+ )
71
+
72
+ # Check if message matches any action pattern
73
+ SHOULD_LOG=false
74
+ for pattern in "${ACTION_PATTERNS[@]}"; do
75
+ if echo "$USER_MESSAGE" | grep -qE "$pattern"; then
76
+ SHOULD_LOG=true
77
+ break
78
+ fi
79
+ done
80
+
81
+ # Also check for follow-up questions after taking action
82
+ if echo "$USER_MESSAGE" | grep -qE "(I|i) .* (what|should|next|now)\?"; then
83
+ SHOULD_LOG=true
84
+ fi
85
+
86
+ # If action detected, output logging reminder for Claude to see
87
+ if [ "$SHOULD_LOG" = true ]; then
88
+ echo ""
89
+ echo "[Auto-log] Detected progress update. Please log this to the ftm daily log."
90
+ echo ""
91
+ fi
92
+
93
+ # --- Dashboard sync: surface task-state changes made outside the chat ---
94
+ CLAUDE_SYNC_FILE="$FTM_STATE/blackboard/.runtime/claude-sync-events.txt"
95
+ if [ -s "$CLAUDE_SYNC_FILE" ]; then
96
+ echo ""
97
+ echo "[Dashboard sync] Recent dashboard updates were written to ftm-state:"
98
+ head -20 "$CLAUDE_SYNC_FILE"
99
+ echo "Please reload task state from ftm-state and treat those updates as authoritative."
100
+ echo ""
101
+ : > "$CLAUDE_SYNC_FILE"
102
+ fi
103
+
104
+ # --- Heartbeat: periodic task/urgency check ---
105
+ # Fires every 30 minutes. Prompts Claude to scan for time-sensitive items.
106
+ HEARTBEAT_INTERVAL=1800 # 30 minutes in seconds
107
+ LAST_HEARTBEAT_FILE="$FTM_STATE/.last-heartbeat"
108
+ HEARTBEAT_MD="$FTM_STATE/HEARTBEAT.md"
109
+
110
+ SHOULD_HEARTBEAT=false
111
+ if [ -f "$LAST_HEARTBEAT_FILE" ]; then
112
+ LAST_BEAT=$(cat "$LAST_HEARTBEAT_FILE" 2>/dev/null || echo 0)
113
+ NOW=$(date +%s)
114
+ ELAPSED=$(( NOW - LAST_BEAT ))
115
+ if [ "$ELAPSED" -ge "$HEARTBEAT_INTERVAL" ]; then
116
+ SHOULD_HEARTBEAT=true
117
+ fi
118
+ else
119
+ # First message of session — no heartbeat on first message, just record time
120
+ echo "$(date +%s)" > "$LAST_HEARTBEAT_FILE"
121
+ fi
122
+
123
+ if [ "$SHOULD_HEARTBEAT" = true ]; then
124
+ echo "$(date +%s)" > "$LAST_HEARTBEAT_FILE"
125
+ echo ""
126
+ echo "[HEARTBEAT — $(date '+%H:%M') check-in]: 30 minutes have passed. Briefly scan for anything time-sensitive:"
127
+ echo "- Check ftm-state/blackboard/context.json for deadlines or blockers that need attention."
128
+ if [ -f "$HEARTBEAT_MD" ]; then
129
+ HB_CONTENT=$(cat "$HEARTBEAT_MD" 2>/dev/null)
130
+ HB_ACTIONABLE=$(echo "$HB_CONTENT" | grep -v "^#" | grep -v "^[[:space:]]*$" | head -3)
131
+ if [ -n "$HB_ACTIONABLE" ]; then
132
+ echo "- HEARTBEAT.md has tasks configured — read it and follow any instructions."
133
+ fi
134
+ fi
135
+ echo "If nothing urgent, proceed normally. If something needs attention, surface it briefly."
136
+ echo ""
137
+ fi