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
package/ftm-git/SKILL.md CHANGED
@@ -1,441 +1,441 @@
1
- ---
2
- name: ftm-git
3
- description: Secret scanning and credential safety gate for git operations. Prevents API keys, tokens, passwords, and other secrets from ever being committed or pushed to remote repositories. Scans staged files, working tree, and git history for hardcoded credentials using regex pattern matching, then auto-remediates by extracting secrets to gitignored .env files and replacing hardcoded values with env var references. Use when user says "scan for secrets", "check for keys", "audit credentials", "ftm-git", "secret scan", "remove api keys", "check before push", or any time git commit/push operations are about to happen. Also auto-invoked by ftm-executor and ftm-mind before any commit or push operation. Even if the user just says "commit this" or "push to remote", this skill MUST run first. Do NOT use for general git workflow operations like branching or merging — that's git-workflow territory. This skill is specifically the security gate.
4
- ---
5
-
6
- ## Events
7
-
8
- ### Emits
9
- - `secrets_found` — when scan detects hardcoded credentials in staged files or working tree
10
- - `secrets_clear` — when scan completes with no findings (safe to proceed with commit/push)
11
- - `secrets_remediated` — when auto-fix successfully extracts secrets to .env and refactors source files
12
- - `task_completed` — when full scan + remediation cycle finishes
13
-
14
- ### Listens To
15
- - `code_changed` — run a quick scan on modified files before they get staged
16
- - `code_committed` — verify the commit doesn't contain secrets (post-commit safety net)
17
-
18
- ## Blackboard Read
19
-
20
- Before starting, load context from the blackboard:
21
-
22
- 1. Read `~/.claude/ftm-state/blackboard/context.json` — check current_task, recent_decisions, active_constraints
23
- 2. Read `~/.claude/ftm-state/blackboard/experiences/index.json` — filter entries by task_type="security" or tags matching "secrets", "credentials", "api-keys", or "git-safety"
24
- 3. Load top 3-5 matching experience files for previously found secret patterns and effective remediation strategies
25
- 4. Read `~/.claude/ftm-state/blackboard/patterns.json` — check recurring_issues for repeated secret leaks and execution_patterns for which files/directories tend to accumulate secrets
26
-
27
- If index.json is empty or no matches found, proceed normally without experience-informed shortcuts.
28
-
29
- # FTM Git — Secret Scanning & Credential Safety Gate
30
-
31
- This skill exists because secrets pushed to GitHub are compromised the instant they hit the remote — even if you force-push a clean history seconds later. Bots scrape public repos continuously, and private repos are one permissions mistake away from exposure. The only safe secret is one that never enters git history.
32
-
33
- This is not a nice-to-have audit. This is a hard gate. Nothing gets committed or pushed until this skill says it's clean.
34
-
35
- ## Why This Matters
36
-
37
- Yesterday we pushed API keys to the repo. That's the kind of mistake that leads to compromised accounts, unexpected bills, and emergency credential rotations. This skill makes it structurally impossible for that to happen again by scanning every file that's about to be committed and blocking the operation if secrets are present — then auto-fixing what it can.
38
-
39
- ## Phase -1: Install Git Hook (First Invocation Only)
40
-
41
- The first time ftm-git runs in a repo, install a pre-commit hook as a hard safety net. This hook runs independently of Claude — it's a shell script that blocks `git commit` if staged files contain Tier 1 secret patterns. Even if Claude forgets to invoke this skill, or someone runs git directly from the terminal, the hook catches it.
42
-
43
- **Check if the hook is already installed:**
44
-
45
- ```bash
46
- # Look for ftm-git marker in existing pre-commit hook
47
- grep -q "ftm-git" .git/hooks/pre-commit 2>/dev/null
48
- ```
49
-
50
- **If not installed**, copy the hook script:
51
-
52
- ```bash
53
- cp ~/.claude/skills/ftm-git/scripts/pre-commit-secrets.sh .git/hooks/pre-commit
54
- chmod +x .git/hooks/pre-commit
55
- ```
56
-
57
- **If a pre-commit hook already exists** (from husky, pre-commit framework, etc.), don't overwrite it. Instead, append the ftm-git scan to the end of the existing hook:
58
-
59
- ```bash
60
- echo "" >> .git/hooks/pre-commit
61
- echo "# --- ftm-git secret scanner ---" >> .git/hooks/pre-commit
62
- cat ~/.claude/skills/ftm-git/scripts/pre-commit-secrets.sh >> .git/hooks/pre-commit
63
- ```
64
-
65
- Tell the user: "Installed ftm-git pre-commit hook. Commits with hardcoded secrets will be blocked automatically, even outside of Claude."
66
-
67
- This only needs to happen once per repo. On subsequent invocations, skip this phase.
68
-
69
- ## Phase 0: Determine Scan Scope
70
-
71
- Before scanning, figure out what needs scanning and why you were invoked.
72
-
73
- **Invocation context determines scope:**
74
-
75
- | Context | Scope |
76
- |---|---|
77
- | Pre-commit (explicit or auto-triggered) | Staged files (`git diff --cached --name-only`) + any files about to be staged |
78
- | Pre-push | All commits not yet on remote (`git log @{upstream}..HEAD --name-only`) |
79
- | Manual invocation ("scan for secrets") | Full working tree sweep |
80
- | Post-commit safety net | The commit that just landed (`git diff-tree --no-commit-id -r HEAD`) |
81
-
82
- **Always also check these regardless of invocation context:**
83
- - Any `.env` file that is NOT in `.gitignore` — this is itself a finding
84
- - Any file matching `*credentials*`, `*secret*`, `*token*` in the filename
85
-
86
- ## Phase 1: Pattern Scan
87
-
88
- Scan the in-scope files using regex patterns. The goal is zero false negatives — a few false positives are acceptable and will be filtered in Phase 2.
89
-
90
- ### Tier 1: High-Confidence Patterns (almost certainly real secrets)
91
-
92
- These patterns have distinctive prefixes or structures that make false positives rare:
93
-
94
- ```
95
- # AWS
96
- AKIA[0-9A-Z]{16} # AWS Access Key ID
97
- amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} # AWS MWS
98
-
99
- # GitHub
100
- ghp_[A-Za-z0-9_]{36} # GitHub PAT (classic)
101
- gho_[A-Za-z0-9_]{36} # GitHub OAuth
102
- ghu_[A-Za-z0-9_]{36} # GitHub user token
103
- ghs_[A-Za-z0-9_]{36} # GitHub server token
104
- github_pat_[A-Za-z0-9_]{82} # GitHub fine-grained PAT
105
-
106
- # Slack
107
- xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24} # Slack bot token
108
- xoxp-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack user token
109
- xoxa-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack app token
110
- xoxr-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack refresh token
111
-
112
- # Google
113
- AIza[0-9A-Za-z\-_]{35} # Google API key
114
-
115
- # Stripe
116
- sk_live_[0-9a-zA-Z]{24,} # Stripe secret key (live)
117
- sk_test_[0-9a-zA-Z]{24,} # Stripe secret key (test)
118
- rk_live_[0-9a-zA-Z]{24,} # Stripe restricted key
119
-
120
- # Other services
121
- SG\.[A-Za-z0-9\-_]{22}\.[A-Za-z0-9\-_]{43} # SendGrid
122
- SK[0-9a-fA-F]{32} # Twilio
123
- npm_[A-Za-z0-9]{36} # npm token
124
- pypi-[A-Za-z0-9\-_]{100,} # PyPI token
125
- glpat-[A-Za-z0-9\-_]{20,} # GitLab PAT
126
- -----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY----- # Private keys
127
- ```
128
-
129
- ### Tier 2: Context-Dependent Patterns (need surrounding context to confirm)
130
-
131
- These match common assignment patterns. Check that the value isn't a placeholder, empty string, or env var reference before flagging:
132
-
133
- ```
134
- # Generic key/secret assignments — flag if value looks real (not placeholder)
135
- (api_key|apikey|api-key)\s*[:=]\s*["']?[A-Za-z0-9\-_]{16,}["']?
136
- (secret|secret_key|client_secret)\s*[:=]\s*["']?[A-Za-z0-9\-_]{16,}["']?
137
- (password|passwd|pwd)\s*[:=]\s*["']?[^\s"']{8,}["']?
138
- (token|access_token|auth_token)\s*[:=]\s*["']?[A-Za-z0-9\-_.]{16,}["']?
139
- (database_url|db_url|connection_string)\s*[:=]\s*["']?[^\s"']{20,}["']?
140
-
141
- # Bearer tokens in code
142
- bearer\s+[A-Za-z0-9\-._~+/]{20,}
143
-
144
- # Webhook URLs with tokens
145
- https://hooks\.slack\.com/services/T[A-Z0-9]{8,}/B[A-Z0-9]{8,}/[a-zA-Z0-9]{24}
146
- ```
147
-
148
- ### What to Ignore (false positive suppression)
149
-
150
- Skip matches that are clearly not real secrets:
151
-
152
- - Values that are `""`, `''`, `None`, `null`, `undefined`, `TODO`, `CHANGEME`, `your-key-here`, `xxx`, `placeholder`, `example`, `test`, `dummy`, `fake`, `sample`
153
- - References to environment variables: `os.environ[`, `process.env.`, `ENV[`, `${`, `os.getenv(`
154
- - Lines that are comments (`#`, `//`, `/*`, `--`)
155
- - Files in `node_modules/`, `.git/`, `vendor/`, `__pycache__/`, `dist/`, `build/`
156
- - Files that are themselves `.env.example`, `.env.sample`, `.env.template`
157
- - Lock files (`package-lock.json`, `yarn.lock`, `Gemfile.lock`, `poetry.lock`)
158
- - Test fixtures where the "secret" is obviously fake (e.g., `test_api_key = "sk_test_abc123"` in a test file — but still flag `sk_live_*` in test files, those are real)
159
-
160
- ### Running the Scan
161
-
162
- Use the Grep tool to search in-scope files for each pattern. Run Tier 1 patterns in parallel since they're independent. For Tier 2, check surrounding context before confirming.
163
-
164
- For each finding, record:
165
- - **file**: absolute path
166
- - **line**: line number
167
- - **pattern**: which pattern matched
168
- - **tier**: 1 or 2
169
- - **value_preview**: first 8 chars + `...` + last 4 chars (never log the full secret)
170
- - **context**: the surrounding code (with the secret value masked)
171
-
172
- ## Phase 2: Validate Findings
173
-
174
- For each Tier 2 match, read the surrounding context (5 lines before and after) and determine:
175
-
176
- 1. **Is the value a real secret or a placeholder?** — Check against the ignore list above.
177
- 2. **Is it already using an env var?** — If the code does `key = os.environ.get("API_KEY", "sk_live_abc...")`, the hardcoded value is a fallback default. Still a finding — fallback defaults with real secrets are dangerous.
178
- 3. **Is it in a file that should be gitignored?** — If the secret is in `.env` and `.env` is in `.gitignore`, it's fine. If `.env` is NOT in `.gitignore`, that's a separate finding.
179
-
180
- After validation, produce a findings list sorted by severity:
181
-
182
- | Severity | Meaning |
183
- |---|---|
184
- | **CRITICAL** | Tier 1 match (high-confidence secret) in a tracked or staged file |
185
- | **HIGH** | Tier 2 confirmed match in a tracked or staged file |
186
- | **MEDIUM** | `.env` file not in `.gitignore`, or secret in a fallback default |
187
- | **LOW** | Secret in a gitignored file but the gitignore rule might be fragile |
188
-
189
- If zero findings after validation: emit `secrets_clear` and proceed. The commit/push is safe.
190
-
191
- If any CRITICAL or HIGH findings: **STOP. The commit/push is BLOCKED.** Say this explicitly to the user before doing anything else:
192
-
193
- ```
194
- ftm-git: BLOCKED — <N> secret(s) found. Commit/push halted. Attempting auto-remediation...
195
- ```
196
-
197
- Then proceed to Phase 3 to fix. The commit/push does NOT happen until Phase 3 completes and a re-scan in Phase 3 Step 5 comes back clean. This is non-negotiable — even if you can fix the secrets, the user needs to see that the operation was blocked and why.
198
-
199
- ## Phase 3: Auto-Remediate
200
-
201
- For each finding, apply the appropriate fix automatically. The goal is to make the code safe without breaking functionality.
202
-
203
- ### Step 1: Ensure .env infrastructure exists
204
-
205
- Check for a `.env` file in the project root. If it doesn't exist, create one with a header comment:
206
-
207
- ```
208
- # Environment variables — DO NOT COMMIT THIS FILE
209
- # Copy .env.example for the template, fill in real values locally
210
- ```
211
-
212
- Check `.gitignore` for `.env` coverage. If missing, add:
213
- ```
214
- # Environment files with secrets
215
- .env
216
- .env.local
217
- .env.production
218
- .env.staging
219
- .env.*.local
220
- ```
221
-
222
- ### Step 2: Extract secrets to .env
223
-
224
- For each finding:
225
-
226
- 1. **Choose an env var name** — derive it from the context. If the code says `STRIPE_API_KEY = "sk_live_..."`, the env var is `STRIPE_API_KEY`. If it says `api_key: "AIza..."`, infer from the file/service context (e.g., `GOOGLE_API_KEY`). Use SCREAMING_SNAKE_CASE.
227
-
228
- 2. **Add to .env** — append `VAR_NAME=<actual-secret-value>` to `.env`. If the var already exists, don't duplicate it.
229
-
230
- 3. **Add to .env.example** — create or update `.env.example` with `VAR_NAME=your-value-here` so other developers know the variable exists without seeing the real value.
231
-
232
- ### Step 3: Refactor source files
233
-
234
- Replace the hardcoded secret with an env var reference. Match the language/framework:
235
-
236
- | Language | Pattern |
237
- |---|---|
238
- | Python | `os.environ["VAR_NAME"]` or `os.getenv("VAR_NAME")` (match existing style in file) |
239
- | JavaScript/TypeScript | `process.env.VAR_NAME` |
240
- | Ruby | `ENV["VAR_NAME"]` or `ENV.fetch("VAR_NAME")` |
241
- | Go | `os.Getenv("VAR_NAME")` |
242
- | Java | `System.getenv("VAR_NAME")` |
243
- | Shell/Bash | `$VAR_NAME` or `${VAR_NAME}` |
244
- | YAML/JSON config | `${VAR_NAME}` (if the framework supports interpolation) or add a comment pointing to the env var |
245
-
246
- If the file doesn't already import the env-reading module (e.g., `import os` in Python, `require('dotenv').config()` in Node), add the import. Check if the project uses `python-dotenv`, `dotenv` (Node), or similar — if so, use the project's existing pattern for loading env vars.
247
-
248
- ### Step 4: Unstage remediated files
249
-
250
- After refactoring, make sure the `.env` file (with real secrets) is NOT staged:
251
-
252
- ```bash
253
- git reset HEAD .env 2>/dev/null # unstage if accidentally staged
254
- ```
255
-
256
- Stage the refactored source files (which now reference env vars instead of hardcoded secrets):
257
-
258
- ```bash
259
- git add <refactored-files>
260
- ```
261
-
262
- ### Step 5: Verify the fix
263
-
264
- Re-run Phase 1 scan on the refactored files to confirm the secrets are gone. If any remain, loop back and fix. Do not proceed until the scan is clean.
265
-
266
- ## Phase 4: Report
267
-
268
- After remediation (or if the scan was clean from the start), produce a summary:
269
-
270
- **Clean scan:**
271
- ```
272
- ftm-git: Clean scan. 0 secrets found in <N> files scanned. Safe to commit.
273
- ```
274
-
275
- **After remediation:**
276
- ```
277
- ftm-git: Found <N> hardcoded secrets. Auto-remediated:
278
-
279
- CRITICAL: sk_live_**** in src/payments.py:42 -> STRIPE_SECRET_KEY
280
- HIGH: AIza**** in config/google.ts:18 -> GOOGLE_API_KEY
281
- MEDIUM: .env was not in .gitignore -> added
282
-
283
- Actions taken:
284
- - Extracted <N> secrets to .env (gitignored)
285
- - Created/updated .env.example with placeholder vars
286
- - Refactored <N> source files to use env var references
287
- - Updated .gitignore
288
-
289
- Verify the app still works with the new env var setup, then commit.
290
- ```
291
-
292
- **Blocked (auto-fix not possible):**
293
-
294
- Some secrets can't be auto-fixed — for example, a private key embedded in a binary file, or a secret in a format the skill can't safely refactor. In these cases:
295
-
296
- ```
297
- ftm-git: BLOCKED. Found secrets that require manual remediation:
298
-
299
- CRITICAL: Private key in assets/cert.pem:1
300
- -> Move this file outside the repo and reference via path env var
301
-
302
- Action required: Fix the above manually, then run ftm-git again.
303
- ```
304
-
305
- ## Phase 5: Git History Check (Manual Invocation Only)
306
-
307
- When explicitly asked to do a deep scan (e.g., "scan the repo history for secrets"), also check past commits. This is expensive so it only runs on explicit request, not as part of the pre-commit gate.
308
-
309
- ```bash
310
- git log --all --diff-filter=A --name-only --pretty=format:"%H" -- "*.env" "*.pem" "*.key" "*credentials*" "*secret*"
311
- ```
312
-
313
- For each historically added sensitive file, check if it's still in the current tree. If it was added and later removed, warn that the secret is still in git history and suggest:
314
-
315
- 1. Rotate the credential immediately (it's compromised)
316
- 2. Use `git filter-repo` or BFG Repo Cleaner to purge from history if needed
317
-
318
- ## Operating Principles
319
-
320
- 1. **Block first, fix second.** Never let a secret through while figuring out the fix. The commit waits.
321
- 2. **Zero false negatives over zero false positives.** It's better to flag something that turns out to be harmless than to miss a real key.
322
- 3. **Never log full secrets.** In all output, mask secret values. Show only enough to identify which secret it is (first 8 + last 4 chars).
323
- 4. **Env vars are the escape hatch.** The remediation pattern is always: secret goes to gitignored .env, code references the env var.
324
- 5. **Existing patterns win.** If the project already uses dotenv, Vault, AWS Secrets Manager, or any other secret management system, match that pattern rather than introducing a new one.
325
- 6. **Test files are not exempt.** A real `sk_live_*` key in a test file is just as dangerous as one in production code. Only `sk_test_*` with obviously fake values get a pass.
326
-
327
- ## Integration Points
328
-
329
- ### With ftm-executor
330
- ftm-executor should invoke ftm-git before every commit operation in its task execution loop. If ftm-git emits `secrets_found`, the executor must pause and remediate before proceeding.
331
-
332
- ### With ftm-mind
333
- When ftm-mind routes a commit or push request, it should run ftm-git as a prerequisite gate. The commit/push only proceeds after `secrets_clear` or `secrets_remediated`.
334
-
335
- ### With git-workflow agent
336
- The git-workflow agent should check with ftm-git before executing any commit or push command. If you're about to run `git commit` or `git push`, ftm-git goes first.
337
-
338
- ## Post-Commit Experience Recording
339
-
340
- FTM includes a post-commit hook that guarantees every commit produces an experience entry in the blackboard.
341
-
342
- ### How It Works
343
-
344
- 1. After every `git commit`, the hook checks if an experience was recorded in the last 2 minutes
345
- 2. If yes (the LLM already recorded a detailed experience) → skip, no duplicate
346
- 3. If no → create a minimal experience from commit metadata (hash, message, files, branch)
347
- 4. Update the experience index
348
-
349
- ### Installation
350
-
351
- The hook is at `ftm-git/hooks/post-commit-experience.sh`. To install:
352
-
353
- ```bash
354
- cp ~/.claude/skills/ftm-git/hooks/post-commit-experience.sh .git/hooks/post-commit
355
- chmod +x .git/hooks/post-commit
356
- ```
357
-
358
- Or add to your project's husky config if using husky.
359
-
360
- ### Minimal vs Rich Experiences
361
-
362
- - **Minimal** (from hook): commit metadata only, confidence 0.5, tags: `auto-recorded`
363
- - **Rich** (from LLM): full task context, lessons learned, higher confidence, domain-specific tags
364
-
365
- The hook ensures no commit goes unrecorded, while the LLM produces richer entries during active sessions.
366
-
367
- ## Blackboard Write
368
-
369
- After completing, update the blackboard:
370
-
371
- 1. Update `~/.claude/ftm-state/blackboard/context.json`:
372
- - Set current_task status to "complete"
373
- - Append scan summary to recent_decisions (cap at 10)
374
- - Update session_metadata.skills_invoked and last_updated
375
- 2. Write an experience file to `experiences/YYYY-MM-DD_secret-scan-<slug>.json` with:
376
- - Number of files scanned
377
- - Findings by severity
378
- - Remediation actions taken
379
- - Which patterns matched (to improve future scans)
380
- 3. Update `experiences/index.json` with the new entry
381
- 4. Emit `secrets_clear` or `secrets_remediated` or `secrets_blocked`
382
-
383
- ## Requirements
384
-
385
- - tool: `git` | required | staged file inspection, commit history scanning
386
- - reference: `references/patterns/SECRET-PATTERNS.md` | required | Tier 1/2 patterns, severity table, ignore list
387
- - reference: `references/protocols/REMEDIATION.md` | required | remediation protocol, env var patterns, report formats
388
- - reference: `~/.claude/skills/ftm-git/scripts/pre-commit-secrets.sh` | required | pre-commit hook script for installation
389
- - reference: `~/.claude/skills/ftm-git/hooks/post-commit-experience.sh` | optional | post-commit experience recorder hook
390
-
391
- ## Risk
392
-
393
- - level: medium_write
394
- - scope: modifies source files to replace hardcoded secrets with env var references; creates/updates .env and .env.example files; installs git hooks in .git/hooks/; re-stages files after remediation
395
- - rollback: git checkout on refactored source files; manually remove added .env and .gitignore entries; remove hook from .git/hooks/pre-commit
396
-
397
- ## Approval Gates
398
-
399
- - trigger: CRITICAL or HIGH severity secret found | action: BLOCK commit/push immediately, announce "BLOCKED — N secret(s) found", then attempt auto-remediation
400
- - trigger: auto-remediation proposed for a finding | action: show proposed change (file, variable name, env var name) before applying
401
- - trigger: re-scan after remediation still finds secrets | action: report remaining findings to user, do not proceed with commit
402
- - complexity_routing: micro → auto | small → auto | medium → auto | large → auto | xl → auto
403
-
404
- ## Fallbacks
405
-
406
- - condition: .env file does not exist | action: create .env and .env.example and add .env to .gitignore before extracting secrets
407
- - condition: .gitignore does not exist | action: create .gitignore with .env entry before remediation
408
- - condition: language detection fails for env var pattern | action: extract secret to .env but flag source file refactoring as MANUAL_INTERVENTION_NEEDED
409
- - condition: pre-commit hook already exists | action: append ftm-git scan to existing hook rather than overwriting
410
-
411
- ## Capabilities
412
-
413
- - cli: `git` | required | staged file listing, diff inspection, commit history traversal
414
-
415
- ## Event Payloads
416
-
417
- ### secrets_found
418
- - skill: string — "ftm-git"
419
- - findings_count: number — total secrets detected
420
- - critical_count: number — CRITICAL severity findings
421
- - high_count: number — HIGH severity findings
422
- - files_affected: string[] — files containing secrets
423
- - blocked: boolean — whether commit/push was halted
424
-
425
- ### secrets_clear
426
- - skill: string — "ftm-git"
427
- - files_scanned: number — total files checked
428
- - scope: string — "staged" | "working_tree" | "history" | "pre-push"
429
-
430
- ### secrets_remediated
431
- - skill: string — "ftm-git"
432
- - findings_remediated: number — secrets successfully extracted
433
- - env_vars_added: string[] — environment variable names created
434
- - files_refactored: string[] — source files updated to use env vars
435
- - manual_needed: number — findings requiring manual intervention
436
-
437
- ### task_completed
438
- - skill: string — "ftm-git"
439
- - outcome: string — "clear" | "remediated" | "blocked"
440
- - files_scanned: number — total files scanned
441
- - duration_ms: number — total scan and remediation time
1
+ ---
2
+ name: ftm-git
3
+ description: Secret scanning and credential safety gate for git operations. Prevents API keys, tokens, passwords, and other secrets from ever being committed or pushed to remote repositories. Scans staged files, working tree, and git history for hardcoded credentials using regex pattern matching, then auto-remediates by extracting secrets to gitignored .env files and replacing hardcoded values with env var references. Use when user says "scan for secrets", "check for keys", "audit credentials", "ftm-git", "secret scan", "remove api keys", "check before push", or any time git commit/push operations are about to happen. Also auto-invoked by ftm-executor and ftm-mind before any commit or push operation. Even if the user just says "commit this" or "push to remote", this skill MUST run first. Do NOT use for general git workflow operations like branching or merging — that's git-workflow territory. This skill is specifically the security gate.
4
+ ---
5
+
6
+ ## Events
7
+
8
+ ### Emits
9
+ - `secrets_found` — when scan detects hardcoded credentials in staged files or working tree
10
+ - `secrets_clear` — when scan completes with no findings (safe to proceed with commit/push)
11
+ - `secrets_remediated` — when auto-fix successfully extracts secrets to .env and refactors source files
12
+ - `task_completed` — when full scan + remediation cycle finishes
13
+
14
+ ### Listens To
15
+ - `code_changed` — run a quick scan on modified files before they get staged
16
+ - `code_committed` — verify the commit doesn't contain secrets (post-commit safety net)
17
+
18
+ ## Blackboard Read
19
+
20
+ Before starting, load context from the blackboard:
21
+
22
+ 1. Read `~/.claude/ftm-state/blackboard/context.json` — check current_task, recent_decisions, active_constraints
23
+ 2. Read `~/.claude/ftm-state/blackboard/experiences/index.json` — filter entries by task_type="security" or tags matching "secrets", "credentials", "api-keys", or "git-safety"
24
+ 3. Load top 3-5 matching experience files for previously found secret patterns and effective remediation strategies
25
+ 4. Read `~/.claude/ftm-state/blackboard/patterns.json` — check recurring_issues for repeated secret leaks and execution_patterns for which files/directories tend to accumulate secrets
26
+
27
+ If index.json is empty or no matches found, proceed normally without experience-informed shortcuts.
28
+
29
+ # FTM Git — Secret Scanning & Credential Safety Gate
30
+
31
+ This skill exists because secrets pushed to GitHub are compromised the instant they hit the remote — even if you force-push a clean history seconds later. Bots scrape public repos continuously, and private repos are one permissions mistake away from exposure. The only safe secret is one that never enters git history.
32
+
33
+ This is not a nice-to-have audit. This is a hard gate. Nothing gets committed or pushed until this skill says it's clean.
34
+
35
+ ## Why This Matters
36
+
37
+ Yesterday we pushed API keys to the repo. That's the kind of mistake that leads to compromised accounts, unexpected bills, and emergency credential rotations. This skill makes it structurally impossible for that to happen again by scanning every file that's about to be committed and blocking the operation if secrets are present — then auto-fixing what it can.
38
+
39
+ ## Phase -1: Install Git Hook (First Invocation Only)
40
+
41
+ The first time ftm-git runs in a repo, install a pre-commit hook as a hard safety net. This hook runs independently of Claude — it's a shell script that blocks `git commit` if staged files contain Tier 1 secret patterns. Even if Claude forgets to invoke this skill, or someone runs git directly from the terminal, the hook catches it.
42
+
43
+ **Check if the hook is already installed:**
44
+
45
+ ```bash
46
+ # Look for ftm-git marker in existing pre-commit hook
47
+ grep -q "ftm-git" .git/hooks/pre-commit 2>/dev/null
48
+ ```
49
+
50
+ **If not installed**, copy the hook script:
51
+
52
+ ```bash
53
+ cp ~/.claude/skills/ftm-git/scripts/pre-commit-secrets.sh .git/hooks/pre-commit
54
+ chmod +x .git/hooks/pre-commit
55
+ ```
56
+
57
+ **If a pre-commit hook already exists** (from husky, pre-commit framework, etc.), don't overwrite it. Instead, append the ftm-git scan to the end of the existing hook:
58
+
59
+ ```bash
60
+ echo "" >> .git/hooks/pre-commit
61
+ echo "# --- ftm-git secret scanner ---" >> .git/hooks/pre-commit
62
+ cat ~/.claude/skills/ftm-git/scripts/pre-commit-secrets.sh >> .git/hooks/pre-commit
63
+ ```
64
+
65
+ Tell the user: "Installed ftm-git pre-commit hook. Commits with hardcoded secrets will be blocked automatically, even outside of Claude."
66
+
67
+ This only needs to happen once per repo. On subsequent invocations, skip this phase.
68
+
69
+ ## Phase 0: Determine Scan Scope
70
+
71
+ Before scanning, figure out what needs scanning and why you were invoked.
72
+
73
+ **Invocation context determines scope:**
74
+
75
+ | Context | Scope |
76
+ |---|---|
77
+ | Pre-commit (explicit or auto-triggered) | Staged files (`git diff --cached --name-only`) + any files about to be staged |
78
+ | Pre-push | All commits not yet on remote (`git log @{upstream}..HEAD --name-only`) |
79
+ | Manual invocation ("scan for secrets") | Full working tree sweep |
80
+ | Post-commit safety net | The commit that just landed (`git diff-tree --no-commit-id -r HEAD`) |
81
+
82
+ **Always also check these regardless of invocation context:**
83
+ - Any `.env` file that is NOT in `.gitignore` — this is itself a finding
84
+ - Any file matching `*credentials*`, `*secret*`, `*token*` in the filename
85
+
86
+ ## Phase 1: Pattern Scan
87
+
88
+ Scan the in-scope files using regex patterns. The goal is zero false negatives — a few false positives are acceptable and will be filtered in Phase 2.
89
+
90
+ ### Tier 1: High-Confidence Patterns (almost certainly real secrets)
91
+
92
+ These patterns have distinctive prefixes or structures that make false positives rare:
93
+
94
+ ```
95
+ # AWS
96
+ AKIA[0-9A-Z]{16} # AWS Access Key ID
97
+ amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} # AWS MWS
98
+
99
+ # GitHub
100
+ ghp_[A-Za-z0-9_]{36} # GitHub PAT (classic)
101
+ gho_[A-Za-z0-9_]{36} # GitHub OAuth
102
+ ghu_[A-Za-z0-9_]{36} # GitHub user token
103
+ ghs_[A-Za-z0-9_]{36} # GitHub server token
104
+ github_pat_[A-Za-z0-9_]{82} # GitHub fine-grained PAT
105
+
106
+ # Slack
107
+ xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24} # Slack bot token
108
+ xoxp-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack user token
109
+ xoxa-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack app token
110
+ xoxr-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack refresh token
111
+
112
+ # Google
113
+ AIza[0-9A-Za-z\-_]{35} # Google API key
114
+
115
+ # Stripe
116
+ sk_live_[0-9a-zA-Z]{24,} # Stripe secret key (live)
117
+ sk_test_[0-9a-zA-Z]{24,} # Stripe secret key (test)
118
+ rk_live_[0-9a-zA-Z]{24,} # Stripe restricted key
119
+
120
+ # Other services
121
+ SG\.[A-Za-z0-9\-_]{22}\.[A-Za-z0-9\-_]{43} # SendGrid
122
+ SK[0-9a-fA-F]{32} # Twilio
123
+ npm_[A-Za-z0-9]{36} # npm token
124
+ pypi-[A-Za-z0-9\-_]{100,} # PyPI token
125
+ glpat-[A-Za-z0-9\-_]{20,} # GitLab PAT
126
+ -----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY----- # Private keys
127
+ ```
128
+
129
+ ### Tier 2: Context-Dependent Patterns (need surrounding context to confirm)
130
+
131
+ These match common assignment patterns. Check that the value isn't a placeholder, empty string, or env var reference before flagging:
132
+
133
+ ```
134
+ # Generic key/secret assignments — flag if value looks real (not placeholder)
135
+ (api_key|apikey|api-key)\s*[:=]\s*["']?[A-Za-z0-9\-_]{16,}["']?
136
+ (secret|secret_key|client_secret)\s*[:=]\s*["']?[A-Za-z0-9\-_]{16,}["']?
137
+ (password|passwd|pwd)\s*[:=]\s*["']?[^\s"']{8,}["']?
138
+ (token|access_token|auth_token)\s*[:=]\s*["']?[A-Za-z0-9\-_.]{16,}["']?
139
+ (database_url|db_url|connection_string)\s*[:=]\s*["']?[^\s"']{20,}["']?
140
+
141
+ # Bearer tokens in code
142
+ bearer\s+[A-Za-z0-9\-._~+/]{20,}
143
+
144
+ # Webhook URLs with tokens
145
+ https://hooks\.slack\.com/services/T[A-Z0-9]{8,}/B[A-Z0-9]{8,}/[a-zA-Z0-9]{24}
146
+ ```
147
+
148
+ ### What to Ignore (false positive suppression)
149
+
150
+ Skip matches that are clearly not real secrets:
151
+
152
+ - Values that are `""`, `''`, `None`, `null`, `undefined`, `TODO`, `CHANGEME`, `your-key-here`, `xxx`, `placeholder`, `example`, `test`, `dummy`, `fake`, `sample`
153
+ - References to environment variables: `os.environ[`, `process.env.`, `ENV[`, `${`, `os.getenv(`
154
+ - Lines that are comments (`#`, `//`, `/*`, `--`)
155
+ - Files in `node_modules/`, `.git/`, `vendor/`, `__pycache__/`, `dist/`, `build/`
156
+ - Files that are themselves `.env.example`, `.env.sample`, `.env.template`
157
+ - Lock files (`package-lock.json`, `yarn.lock`, `Gemfile.lock`, `poetry.lock`)
158
+ - Test fixtures where the "secret" is obviously fake (e.g., `test_api_key = "sk_test_abc123"` in a test file — but still flag `sk_live_*` in test files, those are real)
159
+
160
+ ### Running the Scan
161
+
162
+ Use the Grep tool to search in-scope files for each pattern. Run Tier 1 patterns in parallel since they're independent. For Tier 2, check surrounding context before confirming.
163
+
164
+ For each finding, record:
165
+ - **file**: absolute path
166
+ - **line**: line number
167
+ - **pattern**: which pattern matched
168
+ - **tier**: 1 or 2
169
+ - **value_preview**: first 8 chars + `...` + last 4 chars (never log the full secret)
170
+ - **context**: the surrounding code (with the secret value masked)
171
+
172
+ ## Phase 2: Validate Findings
173
+
174
+ For each Tier 2 match, read the surrounding context (5 lines before and after) and determine:
175
+
176
+ 1. **Is the value a real secret or a placeholder?** — Check against the ignore list above.
177
+ 2. **Is it already using an env var?** — If the code does `key = os.environ.get("API_KEY", "sk_live_abc...")`, the hardcoded value is a fallback default. Still a finding — fallback defaults with real secrets are dangerous.
178
+ 3. **Is it in a file that should be gitignored?** — If the secret is in `.env` and `.env` is in `.gitignore`, it's fine. If `.env` is NOT in `.gitignore`, that's a separate finding.
179
+
180
+ After validation, produce a findings list sorted by severity:
181
+
182
+ | Severity | Meaning |
183
+ |---|---|
184
+ | **CRITICAL** | Tier 1 match (high-confidence secret) in a tracked or staged file |
185
+ | **HIGH** | Tier 2 confirmed match in a tracked or staged file |
186
+ | **MEDIUM** | `.env` file not in `.gitignore`, or secret in a fallback default |
187
+ | **LOW** | Secret in a gitignored file but the gitignore rule might be fragile |
188
+
189
+ If zero findings after validation: emit `secrets_clear` and proceed. The commit/push is safe.
190
+
191
+ If any CRITICAL or HIGH findings: **STOP. The commit/push is BLOCKED.** Say this explicitly to the user before doing anything else:
192
+
193
+ ```
194
+ ftm-git: BLOCKED — <N> secret(s) found. Commit/push halted. Attempting auto-remediation...
195
+ ```
196
+
197
+ Then proceed to Phase 3 to fix. The commit/push does NOT happen until Phase 3 completes and a re-scan in Phase 3 Step 5 comes back clean. This is non-negotiable — even if you can fix the secrets, the user needs to see that the operation was blocked and why.
198
+
199
+ ## Phase 3: Auto-Remediate
200
+
201
+ For each finding, apply the appropriate fix automatically. The goal is to make the code safe without breaking functionality.
202
+
203
+ ### Step 1: Ensure .env infrastructure exists
204
+
205
+ Check for a `.env` file in the project root. If it doesn't exist, create one with a header comment:
206
+
207
+ ```
208
+ # Environment variables — DO NOT COMMIT THIS FILE
209
+ # Copy .env.example for the template, fill in real values locally
210
+ ```
211
+
212
+ Check `.gitignore` for `.env` coverage. If missing, add:
213
+ ```
214
+ # Environment files with secrets
215
+ .env
216
+ .env.local
217
+ .env.production
218
+ .env.staging
219
+ .env.*.local
220
+ ```
221
+
222
+ ### Step 2: Extract secrets to .env
223
+
224
+ For each finding:
225
+
226
+ 1. **Choose an env var name** — derive it from the context. If the code says `STRIPE_API_KEY = "sk_live_..."`, the env var is `STRIPE_API_KEY`. If it says `api_key: "AIza..."`, infer from the file/service context (e.g., `GOOGLE_API_KEY`). Use SCREAMING_SNAKE_CASE.
227
+
228
+ 2. **Add to .env** — append `VAR_NAME=<actual-secret-value>` to `.env`. If the var already exists, don't duplicate it.
229
+
230
+ 3. **Add to .env.example** — create or update `.env.example` with `VAR_NAME=your-value-here` so other developers know the variable exists without seeing the real value.
231
+
232
+ ### Step 3: Refactor source files
233
+
234
+ Replace the hardcoded secret with an env var reference. Match the language/framework:
235
+
236
+ | Language | Pattern |
237
+ |---|---|
238
+ | Python | `os.environ["VAR_NAME"]` or `os.getenv("VAR_NAME")` (match existing style in file) |
239
+ | JavaScript/TypeScript | `process.env.VAR_NAME` |
240
+ | Ruby | `ENV["VAR_NAME"]` or `ENV.fetch("VAR_NAME")` |
241
+ | Go | `os.Getenv("VAR_NAME")` |
242
+ | Java | `System.getenv("VAR_NAME")` |
243
+ | Shell/Bash | `$VAR_NAME` or `${VAR_NAME}` |
244
+ | YAML/JSON config | `${VAR_NAME}` (if the framework supports interpolation) or add a comment pointing to the env var |
245
+
246
+ If the file doesn't already import the env-reading module (e.g., `import os` in Python, `require('dotenv').config()` in Node), add the import. Check if the project uses `python-dotenv`, `dotenv` (Node), or similar — if so, use the project's existing pattern for loading env vars.
247
+
248
+ ### Step 4: Unstage remediated files
249
+
250
+ After refactoring, make sure the `.env` file (with real secrets) is NOT staged:
251
+
252
+ ```bash
253
+ git reset HEAD .env 2>/dev/null # unstage if accidentally staged
254
+ ```
255
+
256
+ Stage the refactored source files (which now reference env vars instead of hardcoded secrets):
257
+
258
+ ```bash
259
+ git add <refactored-files>
260
+ ```
261
+
262
+ ### Step 5: Verify the fix
263
+
264
+ Re-run Phase 1 scan on the refactored files to confirm the secrets are gone. If any remain, loop back and fix. Do not proceed until the scan is clean.
265
+
266
+ ## Phase 4: Report
267
+
268
+ After remediation (or if the scan was clean from the start), produce a summary:
269
+
270
+ **Clean scan:**
271
+ ```
272
+ ftm-git: Clean scan. 0 secrets found in <N> files scanned. Safe to commit.
273
+ ```
274
+
275
+ **After remediation:**
276
+ ```
277
+ ftm-git: Found <N> hardcoded secrets. Auto-remediated:
278
+
279
+ CRITICAL: sk_live_**** in src/payments.py:42 -> STRIPE_SECRET_KEY
280
+ HIGH: AIza**** in config/google.ts:18 -> GOOGLE_API_KEY
281
+ MEDIUM: .env was not in .gitignore -> added
282
+
283
+ Actions taken:
284
+ - Extracted <N> secrets to .env (gitignored)
285
+ - Created/updated .env.example with placeholder vars
286
+ - Refactored <N> source files to use env var references
287
+ - Updated .gitignore
288
+
289
+ Verify the app still works with the new env var setup, then commit.
290
+ ```
291
+
292
+ **Blocked (auto-fix not possible):**
293
+
294
+ Some secrets can't be auto-fixed — for example, a private key embedded in a binary file, or a secret in a format the skill can't safely refactor. In these cases:
295
+
296
+ ```
297
+ ftm-git: BLOCKED. Found secrets that require manual remediation:
298
+
299
+ CRITICAL: Private key in assets/cert.pem:1
300
+ -> Move this file outside the repo and reference via path env var
301
+
302
+ Action required: Fix the above manually, then run ftm-git again.
303
+ ```
304
+
305
+ ## Phase 5: Git History Check (Manual Invocation Only)
306
+
307
+ When explicitly asked to do a deep scan (e.g., "scan the repo history for secrets"), also check past commits. This is expensive so it only runs on explicit request, not as part of the pre-commit gate.
308
+
309
+ ```bash
310
+ git log --all --diff-filter=A --name-only --pretty=format:"%H" -- "*.env" "*.pem" "*.key" "*credentials*" "*secret*"
311
+ ```
312
+
313
+ For each historically added sensitive file, check if it's still in the current tree. If it was added and later removed, warn that the secret is still in git history and suggest:
314
+
315
+ 1. Rotate the credential immediately (it's compromised)
316
+ 2. Use `git filter-repo` or BFG Repo Cleaner to purge from history if needed
317
+
318
+ ## Operating Principles
319
+
320
+ 1. **Block first, fix second.** Never let a secret through while figuring out the fix. The commit waits.
321
+ 2. **Zero false negatives over zero false positives.** It's better to flag something that turns out to be harmless than to miss a real key.
322
+ 3. **Never log full secrets.** In all output, mask secret values. Show only enough to identify which secret it is (first 8 + last 4 chars).
323
+ 4. **Env vars are the escape hatch.** The remediation pattern is always: secret goes to gitignored .env, code references the env var.
324
+ 5. **Existing patterns win.** If the project already uses dotenv, Vault, AWS Secrets Manager, or any other secret management system, match that pattern rather than introducing a new one.
325
+ 6. **Test files are not exempt.** A real `sk_live_*` key in a test file is just as dangerous as one in production code. Only `sk_test_*` with obviously fake values get a pass.
326
+
327
+ ## Integration Points
328
+
329
+ ### With ftm-executor
330
+ ftm-executor should invoke ftm-git before every commit operation in its task execution loop. If ftm-git emits `secrets_found`, the executor must pause and remediate before proceeding.
331
+
332
+ ### With ftm-mind
333
+ When ftm-mind routes a commit or push request, it should run ftm-git as a prerequisite gate. The commit/push only proceeds after `secrets_clear` or `secrets_remediated`.
334
+
335
+ ### With git-workflow agent
336
+ The git-workflow agent should check with ftm-git before executing any commit or push command. If you're about to run `git commit` or `git push`, ftm-git goes first.
337
+
338
+ ## Post-Commit Experience Recording
339
+
340
+ FTM includes a post-commit hook that guarantees every commit produces an experience entry in the blackboard.
341
+
342
+ ### How It Works
343
+
344
+ 1. After every `git commit`, the hook checks if an experience was recorded in the last 2 minutes
345
+ 2. If yes (the LLM already recorded a detailed experience) → skip, no duplicate
346
+ 3. If no → create a minimal experience from commit metadata (hash, message, files, branch)
347
+ 4. Update the experience index
348
+
349
+ ### Installation
350
+
351
+ The hook is at `ftm-git/hooks/post-commit-experience.sh`. To install:
352
+
353
+ ```bash
354
+ cp ~/.claude/skills/ftm-git/hooks/post-commit-experience.sh .git/hooks/post-commit
355
+ chmod +x .git/hooks/post-commit
356
+ ```
357
+
358
+ Or add to your project's husky config if using husky.
359
+
360
+ ### Minimal vs Rich Experiences
361
+
362
+ - **Minimal** (from hook): commit metadata only, confidence 0.5, tags: `auto-recorded`
363
+ - **Rich** (from LLM): full task context, lessons learned, higher confidence, domain-specific tags
364
+
365
+ The hook ensures no commit goes unrecorded, while the LLM produces richer entries during active sessions.
366
+
367
+ ## Blackboard Write
368
+
369
+ After completing, update the blackboard:
370
+
371
+ 1. Update `~/.claude/ftm-state/blackboard/context.json`:
372
+ - Set current_task status to "complete"
373
+ - Append scan summary to recent_decisions (cap at 10)
374
+ - Update session_metadata.skills_invoked and last_updated
375
+ 2. Write an experience file to `experiences/YYYY-MM-DD_secret-scan-<slug>.json` with:
376
+ - Number of files scanned
377
+ - Findings by severity
378
+ - Remediation actions taken
379
+ - Which patterns matched (to improve future scans)
380
+ 3. Update `experiences/index.json` with the new entry
381
+ 4. Emit `secrets_clear` or `secrets_remediated` or `secrets_blocked`
382
+
383
+ ## Requirements
384
+
385
+ - tool: `git` | required | staged file inspection, commit history scanning
386
+ - reference: `references/patterns/SECRET-PATTERNS.md` | required | Tier 1/2 patterns, severity table, ignore list
387
+ - reference: `references/protocols/REMEDIATION.md` | required | remediation protocol, env var patterns, report formats
388
+ - reference: `~/.claude/skills/ftm-git/scripts/pre-commit-secrets.sh` | required | pre-commit hook script for installation
389
+ - reference: `~/.claude/skills/ftm-git/hooks/post-commit-experience.sh` | optional | post-commit experience recorder hook
390
+
391
+ ## Risk
392
+
393
+ - level: medium_write
394
+ - scope: modifies source files to replace hardcoded secrets with env var references; creates/updates .env and .env.example files; installs git hooks in .git/hooks/; re-stages files after remediation
395
+ - rollback: git checkout on refactored source files; manually remove added .env and .gitignore entries; remove hook from .git/hooks/pre-commit
396
+
397
+ ## Approval Gates
398
+
399
+ - trigger: CRITICAL or HIGH severity secret found | action: BLOCK commit/push immediately, announce "BLOCKED — N secret(s) found", then attempt auto-remediation
400
+ - trigger: auto-remediation proposed for a finding | action: show proposed change (file, variable name, env var name) before applying
401
+ - trigger: re-scan after remediation still finds secrets | action: report remaining findings to user, do not proceed with commit
402
+ - complexity_routing: micro → auto | small → auto | medium → auto | large → auto | xl → auto
403
+
404
+ ## Fallbacks
405
+
406
+ - condition: .env file does not exist | action: create .env and .env.example and add .env to .gitignore before extracting secrets
407
+ - condition: .gitignore does not exist | action: create .gitignore with .env entry before remediation
408
+ - condition: language detection fails for env var pattern | action: extract secret to .env but flag source file refactoring as MANUAL_INTERVENTION_NEEDED
409
+ - condition: pre-commit hook already exists | action: append ftm-git scan to existing hook rather than overwriting
410
+
411
+ ## Capabilities
412
+
413
+ - cli: `git` | required | staged file listing, diff inspection, commit history traversal
414
+
415
+ ## Event Payloads
416
+
417
+ ### secrets_found
418
+ - skill: string — "ftm-git"
419
+ - findings_count: number — total secrets detected
420
+ - critical_count: number — CRITICAL severity findings
421
+ - high_count: number — HIGH severity findings
422
+ - files_affected: string[] — files containing secrets
423
+ - blocked: boolean — whether commit/push was halted
424
+
425
+ ### secrets_clear
426
+ - skill: string — "ftm-git"
427
+ - files_scanned: number — total files checked
428
+ - scope: string — "staged" | "working_tree" | "history" | "pre-push"
429
+
430
+ ### secrets_remediated
431
+ - skill: string — "ftm-git"
432
+ - findings_remediated: number — secrets successfully extracted
433
+ - env_vars_added: string[] — environment variable names created
434
+ - files_refactored: string[] — source files updated to use env vars
435
+ - manual_needed: number — findings requiring manual intervention
436
+
437
+ ### task_completed
438
+ - skill: string — "ftm-git"
439
+ - outcome: string — "clear" | "remediated" | "blocked"
440
+ - files_scanned: number — total files scanned
441
+ - duration_ms: number — total scan and remediation time