feed-the-machine 1.6.1 → 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
|
@@ -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
|
package/hooks/ftm-plan-gate.sh
CHANGED
|
@@ -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 ""
|