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.
- package/LICENSE +21 -21
- package/README.md +170 -170
- package/bin/brain.py +1340 -0
- package/bin/convert_claude_skills_to_codex.py +490 -0
- package/bin/generate-manifest.mjs +463 -463
- package/bin/harden_codex_skills.py +141 -0
- package/bin/install.mjs +491 -491
- package/bin/migrate-eng-buddy-data.py +875 -0
- package/bin/playbook_engine/__init__.py +1 -0
- package/bin/playbook_engine/conftest.py +8 -0
- package/bin/playbook_engine/extractor.py +33 -0
- package/bin/playbook_engine/manager.py +102 -0
- package/bin/playbook_engine/models.py +84 -0
- package/bin/playbook_engine/registry.py +35 -0
- package/bin/playbook_engine/test_extractor.py +72 -0
- package/bin/playbook_engine/test_integration.py +129 -0
- package/bin/playbook_engine/test_manager.py +85 -0
- package/bin/playbook_engine/test_models.py +166 -0
- package/bin/playbook_engine/test_registry.py +67 -0
- package/bin/playbook_engine/test_tracer.py +86 -0
- package/bin/playbook_engine/tracer.py +93 -0
- package/bin/tasks_db.py +456 -0
- package/docs/HOOKS.md +243 -243
- package/docs/INBOX.md +233 -233
- package/ftm/SKILL.md +125 -122
- package/ftm-audit/SKILL.md +623 -623
- package/ftm-audit/references/protocols/PROJECT-PATTERNS.md +91 -91
- package/ftm-audit/references/protocols/RUNTIME-WIRING.md +66 -66
- package/ftm-audit/references/protocols/WIRING-CONTRACTS.md +135 -135
- package/ftm-audit/references/strategies/AUTO-FIX-STRATEGIES.md +69 -69
- package/ftm-audit/references/templates/REPORT-FORMAT.md +96 -96
- package/ftm-audit/scripts/run-knip.sh +23 -23
- package/ftm-audit.yml +2 -2
- package/ftm-brainstorm/SKILL.md +1003 -498
- package/ftm-brainstorm/evals/evals.json +180 -100
- package/ftm-brainstorm/evals/promptfoo.yaml +109 -109
- package/ftm-brainstorm/references/agent-prompts.md +552 -224
- package/ftm-brainstorm/references/plan-template.md +209 -121
- package/ftm-brainstorm.yml +2 -2
- package/ftm-browse/SKILL.md +454 -454
- package/ftm-browse/daemon/browser-manager.ts +206 -206
- package/ftm-browse/daemon/bun.lock +30 -30
- package/ftm-browse/daemon/cli.ts +347 -347
- package/ftm-browse/daemon/commands.ts +410 -410
- package/ftm-browse/daemon/main.ts +357 -357
- package/ftm-browse/daemon/package.json +17 -17
- package/ftm-browse/daemon/server.ts +189 -189
- package/ftm-browse/daemon/snapshot.ts +519 -519
- package/ftm-browse/daemon/tsconfig.json +22 -22
- package/ftm-browse.yml +4 -4
- package/ftm-capture/SKILL.md +370 -370
- package/ftm-capture.yml +4 -4
- package/ftm-codex-gate/SKILL.md +361 -361
- package/ftm-codex-gate.yml +2 -2
- package/ftm-config/SKILL.md +422 -345
- package/ftm-config.default.yml +125 -82
- package/ftm-config.yml +44 -2
- package/ftm-council/SKILL.md +416 -416
- package/ftm-council/references/prompts/CLAUDE-INVESTIGATION.md +60 -60
- package/ftm-council/references/prompts/CODEX-INVESTIGATION.md +58 -58
- package/ftm-council/references/prompts/GEMINI-INVESTIGATION.md +58 -58
- package/ftm-council/references/prompts/REBUTTAL-TEMPLATE.md +57 -57
- package/ftm-council/references/protocols/PREREQUISITES.md +47 -47
- package/ftm-council/references/protocols/STEP-0-FRAMING.md +46 -46
- package/ftm-council.yml +2 -2
- package/ftm-dashboard/SKILL.md +163 -163
- package/ftm-dashboard.yml +4 -4
- package/ftm-debug/SKILL.md +1037 -1037
- package/ftm-debug/references/phases/PHASE-0-INTAKE.md +58 -58
- package/ftm-debug/references/phases/PHASE-1-TRIAGE.md +46 -46
- package/ftm-debug/references/phases/PHASE-2-WAR-ROOM-AGENTS.md +279 -279
- package/ftm-debug/references/phases/PHASE-3-TO-6-EXECUTION.md +436 -436
- package/ftm-debug/references/protocols/BLACKBOARD.md +86 -86
- package/ftm-debug/references/protocols/EDGE-CASES.md +103 -103
- package/ftm-debug.yml +2 -2
- package/ftm-diagram/SKILL.md +277 -277
- package/ftm-diagram.yml +2 -2
- package/ftm-executor/SKILL.md +777 -777
- package/ftm-executor/references/STYLE-TEMPLATE.md +73 -73
- package/ftm-executor/references/phases/PHASE-0-VERIFICATION.md +62 -62
- package/ftm-executor/references/phases/PHASE-2-AGENT-ASSEMBLY.md +34 -34
- package/ftm-executor/references/phases/PHASE-3-WORKTREES.md +38 -38
- package/ftm-executor/references/phases/PHASE-4-5-AUDIT.md +72 -72
- package/ftm-executor/references/phases/PHASE-4-DISPATCH.md +66 -66
- package/ftm-executor/references/phases/PHASE-5-5-CODEX-GATE.md +73 -73
- package/ftm-executor/references/protocols/DOCUMENTATION-BOOTSTRAP.md +36 -36
- package/ftm-executor/references/protocols/MODEL-PROFILE.md +59 -59
- package/ftm-executor/references/protocols/PROGRESS-TRACKING.md +66 -66
- package/ftm-executor/runtime/ftm-runtime.mjs +252 -252
- package/ftm-executor/runtime/package.json +8 -8
- package/ftm-executor.yml +2 -2
- package/ftm-git/SKILL.md +441 -441
- package/ftm-git/evals/evals.json +26 -26
- package/ftm-git/evals/promptfoo.yaml +75 -75
- package/ftm-git/hooks/post-commit-experience.sh +92 -92
- package/ftm-git/references/patterns/SECRET-PATTERNS.md +104 -104
- package/ftm-git/references/protocols/REMEDIATION.md +139 -139
- package/ftm-git/scripts/pre-commit-secrets.sh +110 -110
- package/ftm-git.yml +2 -2
- package/ftm-inbox/backend/__pycache__/main.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/adapters/_retry.py +64 -64
- package/ftm-inbox/backend/adapters/base.py +230 -230
- package/ftm-inbox/backend/adapters/freshservice.py +104 -104
- package/ftm-inbox/backend/adapters/gmail.py +125 -125
- package/ftm-inbox/backend/adapters/jira.py +136 -136
- package/ftm-inbox/backend/adapters/registry.py +192 -192
- package/ftm-inbox/backend/adapters/slack.py +110 -110
- package/ftm-inbox/backend/db/connection.py +54 -54
- package/ftm-inbox/backend/db/schema.py +78 -78
- package/ftm-inbox/backend/executor/__init__.py +7 -7
- package/ftm-inbox/backend/executor/engine.py +149 -149
- package/ftm-inbox/backend/executor/step_runner.py +98 -98
- package/ftm-inbox/backend/main.py +103 -103
- package/ftm-inbox/backend/models/__init__.py +1 -1
- package/ftm-inbox/backend/models/unified_task.py +36 -36
- package/ftm-inbox/backend/planner/__init__.py +6 -6
- package/ftm-inbox/backend/planner/__pycache__/__init__.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/planner/__pycache__/generator.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/planner/__pycache__/schema.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/planner/generator.py +127 -127
- package/ftm-inbox/backend/planner/schema.py +34 -34
- package/ftm-inbox/backend/requirements.txt +5 -5
- package/ftm-inbox/backend/routes/__pycache__/plan.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/routes/execute.py +186 -186
- package/ftm-inbox/backend/routes/health.py +52 -52
- package/ftm-inbox/backend/routes/inbox.py +68 -68
- package/ftm-inbox/backend/routes/plan.py +271 -271
- package/ftm-inbox/bin/launchagent.mjs +91 -91
- package/ftm-inbox/bin/setup.mjs +188 -188
- package/ftm-inbox/bin/start.sh +10 -10
- package/ftm-inbox/bin/status.sh +17 -17
- package/ftm-inbox/bin/stop.sh +8 -8
- package/ftm-inbox/config.example.yml +55 -55
- package/ftm-inbox/package-lock.json +2898 -2898
- package/ftm-inbox/package.json +26 -26
- package/ftm-inbox/postcss.config.js +6 -6
- package/ftm-inbox/src/app.css +199 -199
- package/ftm-inbox/src/app.html +18 -18
- package/ftm-inbox/src/lib/api.ts +166 -166
- package/ftm-inbox/src/lib/components/ExecutionLog.svelte +81 -81
- package/ftm-inbox/src/lib/components/InboxFeed.svelte +143 -143
- package/ftm-inbox/src/lib/components/PlanStep.svelte +271 -271
- package/ftm-inbox/src/lib/components/PlanView.svelte +206 -206
- package/ftm-inbox/src/lib/components/StreamPanel.svelte +99 -99
- package/ftm-inbox/src/lib/components/TaskCard.svelte +190 -190
- package/ftm-inbox/src/lib/components/ui/EmptyState.svelte +63 -63
- package/ftm-inbox/src/lib/components/ui/KawaiiCard.svelte +86 -86
- package/ftm-inbox/src/lib/components/ui/PillButton.svelte +106 -106
- package/ftm-inbox/src/lib/components/ui/StatusBadge.svelte +67 -67
- package/ftm-inbox/src/lib/components/ui/StreamDrawer.svelte +149 -149
- package/ftm-inbox/src/lib/components/ui/ThemeToggle.svelte +80 -80
- package/ftm-inbox/src/lib/theme.ts +47 -47
- package/ftm-inbox/src/routes/+layout.svelte +76 -76
- package/ftm-inbox/src/routes/+page.svelte +401 -401
- package/ftm-inbox/svelte.config.js +12 -12
- package/ftm-inbox/tailwind.config.ts +63 -63
- package/ftm-inbox/tsconfig.json +13 -13
- package/ftm-inbox/vite.config.ts +6 -6
- package/ftm-intent/SKILL.md +241 -241
- package/ftm-intent.yml +2 -2
- package/ftm-manifest.json +3794 -3794
- package/ftm-map/SKILL.md +291 -291
- package/ftm-map/scripts/db.py +712 -712
- package/ftm-map/scripts/index.py +415 -415
- package/ftm-map/scripts/parser.py +224 -224
- package/ftm-map/scripts/queries/go-tags.scm +20 -20
- package/ftm-map/scripts/queries/javascript-tags.scm +35 -35
- package/ftm-map/scripts/queries/python-tags.scm +31 -31
- package/ftm-map/scripts/queries/ruby-tags.scm +19 -19
- package/ftm-map/scripts/queries/rust-tags.scm +37 -37
- package/ftm-map/scripts/queries/typescript-tags.scm +41 -41
- package/ftm-map/scripts/query.py +301 -301
- package/ftm-map/scripts/ranker.py +377 -377
- package/ftm-map/scripts/requirements.txt +5 -5
- package/ftm-map/scripts/setup-hooks.sh +27 -27
- package/ftm-map/scripts/setup.sh +56 -56
- package/ftm-map/scripts/test_db.py +364 -364
- package/ftm-map/scripts/test_parser.py +174 -174
- package/ftm-map/scripts/test_query.py +183 -183
- package/ftm-map/scripts/test_ranker.py +199 -199
- package/ftm-map/scripts/views.py +591 -591
- package/ftm-map.yml +2 -2
- package/ftm-mind/SKILL.md +201 -1943
- package/ftm-mind/evals/promptfoo.yaml +142 -142
- package/ftm-mind/references/blackboard-protocol.md +110 -0
- package/ftm-mind/references/blackboard-schema.md +328 -328
- package/ftm-mind/references/complexity-guide.md +110 -110
- package/ftm-mind/references/complexity-sizing.md +138 -0
- package/ftm-mind/references/decide-act-protocol.md +172 -0
- package/ftm-mind/references/direct-execution.md +51 -0
- package/ftm-mind/references/environment-discovery.md +77 -0
- package/ftm-mind/references/event-registry.md +319 -319
- package/ftm-mind/references/mcp-inventory.md +300 -296
- package/ftm-mind/references/ops-routing.md +47 -0
- package/ftm-mind/references/orient-protocol.md +234 -0
- package/ftm-mind/references/personality.md +40 -0
- package/ftm-mind/references/protocols/COMPLEXITY-SIZING.md +72 -72
- package/ftm-mind/references/protocols/MCP-HEURISTICS.md +32 -32
- package/ftm-mind/references/protocols/PLAN-APPROVAL.md +80 -80
- package/ftm-mind/references/reflexion-protocol.md +249 -249
- package/ftm-mind/references/routing/SCENARIOS.md +22 -22
- package/ftm-mind/references/routing-scenarios.md +35 -35
- package/ftm-mind.yml +2 -2
- package/ftm-ops.yml +4 -0
- package/ftm-pause/SKILL.md +395 -395
- package/ftm-pause/references/protocols/SKILL-RESTORE-PROTOCOLS.md +186 -186
- package/ftm-pause/references/protocols/VALIDATION.md +80 -80
- package/ftm-pause.yml +2 -2
- package/ftm-researcher/SKILL.md +275 -275
- package/ftm-researcher/evals/agent-diversity.yaml +17 -17
- package/ftm-researcher/evals/synthesis-quality.yaml +12 -12
- package/ftm-researcher/evals/trigger-accuracy.yaml +39 -39
- package/ftm-researcher/references/adaptive-search.md +116 -116
- package/ftm-researcher/references/agent-prompts.md +193 -193
- package/ftm-researcher/references/council-integration.md +193 -193
- package/ftm-researcher/references/output-format.md +203 -203
- package/ftm-researcher/references/synthesis-pipeline.md +165 -165
- package/ftm-researcher/scripts/score_credibility.py +234 -234
- package/ftm-researcher/scripts/validate_research.py +92 -92
- package/ftm-researcher.yml +2 -2
- package/ftm-resume/SKILL.md +518 -518
- package/ftm-resume/references/protocols/VALIDATION.md +172 -172
- package/ftm-resume.yml +2 -2
- package/ftm-retro/SKILL.md +380 -380
- package/ftm-retro/references/protocols/SCORING-RUBRICS.md +89 -89
- package/ftm-retro/references/templates/REPORT-FORMAT.md +109 -109
- package/ftm-retro.yml +2 -2
- package/ftm-routine/SKILL.md +170 -170
- package/ftm-routine.yml +4 -4
- package/ftm-state/blackboard/capabilities.json +5 -5
- package/ftm-state/blackboard/capabilities.schema.json +27 -27
- package/ftm-state/blackboard/context.json +37 -23
- package/ftm-state/blackboard/experiences/doom-statusline-fix.json +26 -0
- package/ftm-state/blackboard/experiences/hackathon-pages-site.json +26 -0
- package/ftm-state/blackboard/experiences/hindsight-sso-kickoff.json +42 -0
- package/ftm-state/blackboard/experiences/index.json +58 -9
- package/ftm-state/blackboard/experiences/learning-ragnarok-api-access.json +23 -0
- package/ftm-state/blackboard/experiences/nordlayer-members-auto-assign.json +26 -0
- package/ftm-state/blackboard/experiences/saml2aws-stale-session-fix.json +41 -0
- package/ftm-state/blackboard/patterns.json +6 -6
- package/ftm-state/schemas/context.schema.json +130 -130
- package/ftm-state/schemas/experience-index.schema.json +77 -77
- package/ftm-state/schemas/experience.schema.json +78 -78
- package/ftm-state/schemas/patterns.schema.json +44 -44
- package/ftm-upgrade/SKILL.md +194 -194
- package/ftm-upgrade/scripts/check-version.sh +76 -76
- package/ftm-upgrade/scripts/upgrade.sh +143 -143
- package/ftm-upgrade.yml +2 -2
- package/ftm-verify.yml +2 -2
- package/ftm.yml +2 -2
- package/hooks/ftm-auto-log.sh +137 -0
- package/hooks/ftm-blackboard-enforcer.sh +93 -93
- package/hooks/ftm-discovery-reminder.sh +90 -90
- package/hooks/ftm-drafts-gate.sh +61 -61
- package/hooks/ftm-event-logger.mjs +107 -107
- package/hooks/ftm-install-hooks.sh +240 -0
- package/hooks/ftm-learning-capture.sh +117 -0
- package/hooks/ftm-map-autodetect.sh +79 -79
- package/hooks/ftm-pending-sync-check.sh +22 -22
- package/hooks/ftm-plan-gate.sh +92 -92
- package/hooks/ftm-post-commit-trigger.sh +57 -57
- package/hooks/ftm-post-compaction.sh +138 -0
- package/hooks/ftm-pre-compaction.sh +147 -0
- package/hooks/ftm-session-end.sh +52 -0
- package/hooks/ftm-session-snapshot.sh +213 -0
- package/hooks/settings-template.json +81 -81
- package/install.sh +363 -363
- package/package.json +84 -84
- 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
|
+
}
|