feed-the-machine 1.5.0 → 1.6.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 (224) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +170 -170
  3. package/bin/generate-manifest.mjs +463 -463
  4. package/bin/install.mjs +491 -491
  5. package/docs/HOOKS.md +243 -243
  6. package/docs/INBOX.md +233 -233
  7. package/ftm/SKILL.md +122 -122
  8. package/ftm-audit/SKILL.md +623 -541
  9. package/ftm-audit/references/protocols/PROJECT-PATTERNS.md +91 -91
  10. package/ftm-audit/references/protocols/RUNTIME-WIRING.md +66 -66
  11. package/ftm-audit/references/protocols/WIRING-CONTRACTS.md +135 -135
  12. package/ftm-audit/references/strategies/AUTO-FIX-STRATEGIES.md +69 -69
  13. package/ftm-audit/references/templates/REPORT-FORMAT.md +96 -96
  14. package/ftm-audit/scripts/run-knip.sh +23 -23
  15. package/ftm-audit.yml +2 -2
  16. package/ftm-brainstorm/SKILL.md +498 -498
  17. package/ftm-brainstorm/evals/evals.json +100 -100
  18. package/ftm-brainstorm/evals/promptfoo.yaml +109 -109
  19. package/ftm-brainstorm/references/agent-prompts.md +224 -224
  20. package/ftm-brainstorm/references/plan-template.md +121 -121
  21. package/ftm-brainstorm.yml +2 -2
  22. package/ftm-browse/SKILL.md +454 -454
  23. package/ftm-browse/daemon/browser-manager.ts +206 -206
  24. package/ftm-browse/daemon/bun.lock +30 -30
  25. package/ftm-browse/daemon/cli.ts +347 -347
  26. package/ftm-browse/daemon/commands.ts +410 -410
  27. package/ftm-browse/daemon/main.ts +357 -357
  28. package/ftm-browse/daemon/package.json +17 -17
  29. package/ftm-browse/daemon/server.ts +189 -189
  30. package/ftm-browse/daemon/snapshot.ts +519 -519
  31. package/ftm-browse/daemon/tsconfig.json +22 -22
  32. package/ftm-browse.yml +4 -4
  33. package/ftm-capture/SKILL.md +370 -370
  34. package/ftm-capture.yml +4 -4
  35. package/ftm-codex-gate/SKILL.md +361 -361
  36. package/ftm-codex-gate.yml +2 -2
  37. package/ftm-config/SKILL.md +345 -345
  38. package/ftm-config.default.yml +82 -80
  39. package/ftm-config.yml +2 -2
  40. package/ftm-council/SKILL.md +416 -416
  41. package/ftm-council/references/prompts/CLAUDE-INVESTIGATION.md +60 -60
  42. package/ftm-council/references/prompts/CODEX-INVESTIGATION.md +58 -58
  43. package/ftm-council/references/prompts/GEMINI-INVESTIGATION.md +58 -58
  44. package/ftm-council/references/prompts/REBUTTAL-TEMPLATE.md +57 -57
  45. package/ftm-council/references/protocols/PREREQUISITES.md +47 -47
  46. package/ftm-council/references/protocols/STEP-0-FRAMING.md +46 -46
  47. package/ftm-council.yml +2 -2
  48. package/ftm-dashboard/SKILL.md +163 -163
  49. package/ftm-dashboard.yml +4 -4
  50. package/ftm-debug/SKILL.md +1037 -1037
  51. package/ftm-debug/references/phases/PHASE-0-INTAKE.md +58 -58
  52. package/ftm-debug/references/phases/PHASE-1-TRIAGE.md +46 -46
  53. package/ftm-debug/references/phases/PHASE-2-WAR-ROOM-AGENTS.md +279 -279
  54. package/ftm-debug/references/phases/PHASE-3-TO-6-EXECUTION.md +436 -436
  55. package/ftm-debug/references/protocols/BLACKBOARD.md +86 -86
  56. package/ftm-debug/references/protocols/EDGE-CASES.md +103 -103
  57. package/ftm-debug.yml +2 -2
  58. package/ftm-diagram/SKILL.md +277 -277
  59. package/ftm-diagram.yml +2 -2
  60. package/ftm-executor/SKILL.md +777 -767
  61. package/ftm-executor/references/STYLE-TEMPLATE.md +73 -73
  62. package/ftm-executor/references/phases/PHASE-0-VERIFICATION.md +62 -62
  63. package/ftm-executor/references/phases/PHASE-2-AGENT-ASSEMBLY.md +34 -34
  64. package/ftm-executor/references/phases/PHASE-3-WORKTREES.md +38 -38
  65. package/ftm-executor/references/phases/PHASE-4-5-AUDIT.md +72 -72
  66. package/ftm-executor/references/phases/PHASE-4-DISPATCH.md +66 -66
  67. package/ftm-executor/references/phases/PHASE-5-5-CODEX-GATE.md +73 -73
  68. package/ftm-executor/references/protocols/DOCUMENTATION-BOOTSTRAP.md +36 -36
  69. package/ftm-executor/references/protocols/MODEL-PROFILE.md +59 -44
  70. package/ftm-executor/references/protocols/PROGRESS-TRACKING.md +66 -66
  71. package/ftm-executor/runtime/ftm-runtime.mjs +252 -252
  72. package/ftm-executor/runtime/package.json +8 -8
  73. package/ftm-executor.yml +2 -2
  74. package/ftm-git/SKILL.md +441 -441
  75. package/ftm-git/evals/evals.json +26 -26
  76. package/ftm-git/evals/promptfoo.yaml +75 -75
  77. package/ftm-git/hooks/post-commit-experience.sh +92 -92
  78. package/ftm-git/references/patterns/SECRET-PATTERNS.md +104 -104
  79. package/ftm-git/references/protocols/REMEDIATION.md +139 -139
  80. package/ftm-git/scripts/pre-commit-secrets.sh +110 -110
  81. package/ftm-git.yml +2 -2
  82. package/ftm-inbox/backend/adapters/_retry.py +64 -64
  83. package/ftm-inbox/backend/adapters/base.py +230 -230
  84. package/ftm-inbox/backend/adapters/freshservice.py +104 -104
  85. package/ftm-inbox/backend/adapters/gmail.py +125 -125
  86. package/ftm-inbox/backend/adapters/jira.py +136 -136
  87. package/ftm-inbox/backend/adapters/registry.py +192 -192
  88. package/ftm-inbox/backend/adapters/slack.py +110 -110
  89. package/ftm-inbox/backend/db/connection.py +54 -54
  90. package/ftm-inbox/backend/db/schema.py +78 -78
  91. package/ftm-inbox/backend/executor/__init__.py +7 -7
  92. package/ftm-inbox/backend/executor/engine.py +149 -149
  93. package/ftm-inbox/backend/executor/step_runner.py +98 -98
  94. package/ftm-inbox/backend/main.py +103 -103
  95. package/ftm-inbox/backend/models/__init__.py +1 -1
  96. package/ftm-inbox/backend/models/unified_task.py +36 -36
  97. package/ftm-inbox/backend/planner/__init__.py +6 -6
  98. package/ftm-inbox/backend/planner/generator.py +127 -127
  99. package/ftm-inbox/backend/planner/schema.py +34 -34
  100. package/ftm-inbox/backend/requirements.txt +5 -5
  101. package/ftm-inbox/backend/routes/execute.py +186 -186
  102. package/ftm-inbox/backend/routes/health.py +52 -52
  103. package/ftm-inbox/backend/routes/inbox.py +68 -68
  104. package/ftm-inbox/backend/routes/plan.py +271 -271
  105. package/ftm-inbox/bin/launchagent.mjs +91 -91
  106. package/ftm-inbox/bin/setup.mjs +188 -188
  107. package/ftm-inbox/bin/start.sh +10 -10
  108. package/ftm-inbox/bin/status.sh +17 -17
  109. package/ftm-inbox/bin/stop.sh +8 -8
  110. package/ftm-inbox/config.example.yml +55 -55
  111. package/ftm-inbox/package-lock.json +2898 -2898
  112. package/ftm-inbox/package.json +26 -26
  113. package/ftm-inbox/postcss.config.js +6 -6
  114. package/ftm-inbox/src/app.css +199 -199
  115. package/ftm-inbox/src/app.html +18 -18
  116. package/ftm-inbox/src/lib/api.ts +166 -166
  117. package/ftm-inbox/src/lib/components/ExecutionLog.svelte +81 -81
  118. package/ftm-inbox/src/lib/components/InboxFeed.svelte +143 -143
  119. package/ftm-inbox/src/lib/components/PlanStep.svelte +271 -271
  120. package/ftm-inbox/src/lib/components/PlanView.svelte +206 -206
  121. package/ftm-inbox/src/lib/components/StreamPanel.svelte +99 -99
  122. package/ftm-inbox/src/lib/components/TaskCard.svelte +190 -190
  123. package/ftm-inbox/src/lib/components/ui/EmptyState.svelte +63 -63
  124. package/ftm-inbox/src/lib/components/ui/KawaiiCard.svelte +86 -86
  125. package/ftm-inbox/src/lib/components/ui/PillButton.svelte +106 -106
  126. package/ftm-inbox/src/lib/components/ui/StatusBadge.svelte +67 -67
  127. package/ftm-inbox/src/lib/components/ui/StreamDrawer.svelte +149 -149
  128. package/ftm-inbox/src/lib/components/ui/ThemeToggle.svelte +80 -80
  129. package/ftm-inbox/src/lib/theme.ts +47 -47
  130. package/ftm-inbox/src/routes/+layout.svelte +76 -76
  131. package/ftm-inbox/src/routes/+page.svelte +401 -401
  132. package/ftm-inbox/svelte.config.js +12 -12
  133. package/ftm-inbox/tailwind.config.ts +63 -63
  134. package/ftm-inbox/tsconfig.json +13 -13
  135. package/ftm-inbox/vite.config.ts +6 -6
  136. package/ftm-intent/SKILL.md +241 -241
  137. package/ftm-intent.yml +2 -2
  138. package/ftm-manifest.json +3794 -3794
  139. package/ftm-map/SKILL.md +291 -291
  140. package/ftm-map/scripts/db.py +712 -712
  141. package/ftm-map/scripts/index.py +415 -415
  142. package/ftm-map/scripts/parser.py +224 -224
  143. package/ftm-map/scripts/queries/go-tags.scm +20 -20
  144. package/ftm-map/scripts/queries/javascript-tags.scm +35 -35
  145. package/ftm-map/scripts/queries/python-tags.scm +31 -31
  146. package/ftm-map/scripts/queries/ruby-tags.scm +19 -19
  147. package/ftm-map/scripts/queries/rust-tags.scm +37 -37
  148. package/ftm-map/scripts/queries/typescript-tags.scm +41 -41
  149. package/ftm-map/scripts/query.py +301 -301
  150. package/ftm-map/scripts/ranker.py +377 -377
  151. package/ftm-map/scripts/requirements.txt +5 -5
  152. package/ftm-map/scripts/setup-hooks.sh +27 -27
  153. package/ftm-map/scripts/setup.sh +56 -56
  154. package/ftm-map/scripts/test_db.py +364 -364
  155. package/ftm-map/scripts/test_parser.py +174 -174
  156. package/ftm-map/scripts/test_query.py +183 -183
  157. package/ftm-map/scripts/test_ranker.py +199 -199
  158. package/ftm-map/scripts/views.py +591 -591
  159. package/ftm-map.yml +2 -2
  160. package/ftm-mind/SKILL.md +1943 -1943
  161. package/ftm-mind/evals/promptfoo.yaml +142 -142
  162. package/ftm-mind/references/blackboard-schema.md +328 -328
  163. package/ftm-mind/references/complexity-guide.md +110 -110
  164. package/ftm-mind/references/event-registry.md +319 -319
  165. package/ftm-mind/references/mcp-inventory.md +296 -296
  166. package/ftm-mind/references/protocols/COMPLEXITY-SIZING.md +72 -72
  167. package/ftm-mind/references/protocols/MCP-HEURISTICS.md +32 -32
  168. package/ftm-mind/references/protocols/PLAN-APPROVAL.md +80 -80
  169. package/ftm-mind/references/reflexion-protocol.md +249 -249
  170. package/ftm-mind/references/routing/SCENARIOS.md +22 -22
  171. package/ftm-mind/references/routing-scenarios.md +35 -35
  172. package/ftm-mind.yml +2 -2
  173. package/ftm-pause/SKILL.md +395 -395
  174. package/ftm-pause/references/protocols/SKILL-RESTORE-PROTOCOLS.md +186 -186
  175. package/ftm-pause/references/protocols/VALIDATION.md +80 -80
  176. package/ftm-pause.yml +2 -2
  177. package/ftm-researcher/SKILL.md +275 -275
  178. package/ftm-researcher/evals/agent-diversity.yaml +17 -17
  179. package/ftm-researcher/evals/synthesis-quality.yaml +12 -12
  180. package/ftm-researcher/evals/trigger-accuracy.yaml +39 -39
  181. package/ftm-researcher/references/adaptive-search.md +116 -116
  182. package/ftm-researcher/references/agent-prompts.md +193 -193
  183. package/ftm-researcher/references/council-integration.md +193 -193
  184. package/ftm-researcher/references/output-format.md +203 -203
  185. package/ftm-researcher/references/synthesis-pipeline.md +165 -165
  186. package/ftm-researcher/scripts/score_credibility.py +234 -234
  187. package/ftm-researcher/scripts/validate_research.py +92 -92
  188. package/ftm-researcher.yml +2 -2
  189. package/ftm-resume/SKILL.md +518 -518
  190. package/ftm-resume/references/protocols/VALIDATION.md +172 -172
  191. package/ftm-resume.yml +2 -2
  192. package/ftm-retro/SKILL.md +380 -380
  193. package/ftm-retro/references/protocols/SCORING-RUBRICS.md +89 -89
  194. package/ftm-retro/references/templates/REPORT-FORMAT.md +109 -109
  195. package/ftm-retro.yml +2 -2
  196. package/ftm-routine/SKILL.md +170 -170
  197. package/ftm-routine.yml +4 -4
  198. package/ftm-state/blackboard/capabilities.json +5 -5
  199. package/ftm-state/blackboard/capabilities.schema.json +27 -27
  200. package/ftm-state/blackboard/context.json +23 -23
  201. package/ftm-state/blackboard/experiences/index.json +9 -9
  202. package/ftm-state/blackboard/patterns.json +6 -6
  203. package/ftm-state/schemas/context.schema.json +130 -130
  204. package/ftm-state/schemas/experience-index.schema.json +77 -77
  205. package/ftm-state/schemas/experience.schema.json +78 -78
  206. package/ftm-state/schemas/patterns.schema.json +44 -44
  207. package/ftm-upgrade/SKILL.md +194 -194
  208. package/ftm-upgrade/scripts/check-version.sh +76 -76
  209. package/ftm-upgrade/scripts/upgrade.sh +143 -143
  210. package/ftm-upgrade.yml +2 -2
  211. package/ftm-verify.yml +2 -2
  212. package/ftm.yml +2 -2
  213. package/hooks/ftm-blackboard-enforcer.sh +93 -93
  214. package/hooks/ftm-discovery-reminder.sh +90 -90
  215. package/hooks/ftm-drafts-gate.sh +61 -61
  216. package/hooks/ftm-event-logger.mjs +107 -107
  217. package/hooks/ftm-map-autodetect.sh +79 -79
  218. package/hooks/ftm-pending-sync-check.sh +22 -22
  219. package/hooks/ftm-plan-gate.sh +92 -92
  220. package/hooks/ftm-post-commit-trigger.sh +57 -57
  221. package/hooks/settings-template.json +81 -81
  222. package/install.sh +363 -363
  223. package/package.json +84 -84
  224. package/uninstall.sh +25 -25
@@ -1,139 +1,139 @@
1
- # Remediation Protocol — Auto-Fix Steps
2
-
3
- Detailed remediation steps for secrets found during Phase 1–2 scanning. Apply in sequence for each finding.
4
-
5
- ---
6
-
7
- ## Phase 3: Auto-Remediate
8
-
9
- For each finding, apply the appropriate fix automatically. The goal is to make the code safe without breaking functionality.
10
-
11
- ### Step 1: Ensure .env infrastructure exists
12
-
13
- Check for a `.env` file in the project root. If it doesn't exist, create one with a header comment:
14
-
15
- ```
16
- # Environment variables — DO NOT COMMIT THIS FILE
17
- # Copy .env.example for the template, fill in real values locally
18
- ```
19
-
20
- Check `.gitignore` for `.env` coverage. If missing, add:
21
- ```
22
- # Environment files with secrets
23
- .env
24
- .env.local
25
- .env.production
26
- .env.staging
27
- .env.*.local
28
- ```
29
-
30
- ### Step 2: Extract secrets to .env
31
-
32
- For each finding:
33
-
34
- 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.
35
-
36
- 2. **Add to .env** — append `VAR_NAME=<actual-secret-value>` to `.env`. If the var already exists, don't duplicate it.
37
-
38
- 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.
39
-
40
- ### Step 3: Refactor source files
41
-
42
- Replace the hardcoded secret with an env var reference. Match the language/framework:
43
-
44
- | Language | Pattern |
45
- |---|---|
46
- | Python | `os.environ["VAR_NAME"]` or `os.getenv("VAR_NAME")` (match existing style in file) |
47
- | JavaScript/TypeScript | `process.env.VAR_NAME` |
48
- | Ruby | `ENV["VAR_NAME"]` or `ENV.fetch("VAR_NAME")` |
49
- | Go | `os.Getenv("VAR_NAME")` |
50
- | Java | `System.getenv("VAR_NAME")` |
51
- | Shell/Bash | `$VAR_NAME` or `${VAR_NAME}` |
52
- | YAML/JSON config | `${VAR_NAME}` (if the framework supports interpolation) or add a comment pointing to the env var |
53
-
54
- 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.
55
-
56
- ### Step 4: Unstage remediated files
57
-
58
- After refactoring, make sure the `.env` file (with real secrets) is NOT staged:
59
-
60
- ```bash
61
- git reset HEAD .env 2>/dev/null # unstage if accidentally staged
62
- ```
63
-
64
- Stage the refactored source files (which now reference env vars instead of hardcoded secrets):
65
-
66
- ```bash
67
- git add <refactored-files>
68
- ```
69
-
70
- ### Step 5: Verify the fix
71
-
72
- 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.
73
-
74
- ---
75
-
76
- ## Phase 4: Report
77
-
78
- After remediation (or if the scan was clean from the start), produce a summary:
79
-
80
- **Clean scan:**
81
- ```
82
- ftm-git: Clean scan. 0 secrets found in <N> files scanned. Safe to commit.
83
- ```
84
-
85
- **After remediation:**
86
- ```
87
- ftm-git: Found <N> hardcoded secrets. Auto-remediated:
88
-
89
- CRITICAL: sk_live_**** in src/payments.py:42 -> STRIPE_SECRET_KEY
90
- HIGH: AIza**** in config/google.ts:18 -> GOOGLE_API_KEY
91
- MEDIUM: .env was not in .gitignore -> added
92
-
93
- Actions taken:
94
- - Extracted <N> secrets to .env (gitignored)
95
- - Created/updated .env.example with placeholder vars
96
- - Refactored <N> source files to use env var references
97
- - Updated .gitignore
98
-
99
- Verify the app still works with the new env var setup, then commit.
100
- ```
101
-
102
- **Blocked (auto-fix not possible):**
103
-
104
- 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:
105
-
106
- ```
107
- ftm-git: BLOCKED. Found secrets that require manual remediation:
108
-
109
- CRITICAL: Private key in assets/cert.pem:1
110
- -> Move this file outside the repo and reference via path env var
111
-
112
- Action required: Fix the above manually, then run ftm-git again.
113
- ```
114
-
115
- ---
116
-
117
- ## Phase 5: Git History Check (Manual Invocation Only)
118
-
119
- 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.
120
-
121
- ```bash
122
- git log --all --diff-filter=A --name-only --pretty=format:"%H" -- "*.env" "*.pem" "*.key" "*credentials*" "*secret*"
123
- ```
124
-
125
- 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:
126
-
127
- 1. Rotate the credential immediately (it's compromised)
128
- 2. Use `git filter-repo` or BFG Repo Cleaner to purge from history if needed
129
-
130
- ---
131
-
132
- ## Operating Principles
133
-
134
- 1. **Block first, fix second.** Never let a secret through while figuring out the fix. The commit waits.
135
- 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.
136
- 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).
137
- 4. **Env vars are the escape hatch.** The remediation pattern is always: secret goes to gitignored .env, code references the env var.
138
- 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.
139
- 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.
1
+ # Remediation Protocol — Auto-Fix Steps
2
+
3
+ Detailed remediation steps for secrets found during Phase 1–2 scanning. Apply in sequence for each finding.
4
+
5
+ ---
6
+
7
+ ## Phase 3: Auto-Remediate
8
+
9
+ For each finding, apply the appropriate fix automatically. The goal is to make the code safe without breaking functionality.
10
+
11
+ ### Step 1: Ensure .env infrastructure exists
12
+
13
+ Check for a `.env` file in the project root. If it doesn't exist, create one with a header comment:
14
+
15
+ ```
16
+ # Environment variables — DO NOT COMMIT THIS FILE
17
+ # Copy .env.example for the template, fill in real values locally
18
+ ```
19
+
20
+ Check `.gitignore` for `.env` coverage. If missing, add:
21
+ ```
22
+ # Environment files with secrets
23
+ .env
24
+ .env.local
25
+ .env.production
26
+ .env.staging
27
+ .env.*.local
28
+ ```
29
+
30
+ ### Step 2: Extract secrets to .env
31
+
32
+ For each finding:
33
+
34
+ 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.
35
+
36
+ 2. **Add to .env** — append `VAR_NAME=<actual-secret-value>` to `.env`. If the var already exists, don't duplicate it.
37
+
38
+ 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.
39
+
40
+ ### Step 3: Refactor source files
41
+
42
+ Replace the hardcoded secret with an env var reference. Match the language/framework:
43
+
44
+ | Language | Pattern |
45
+ |---|---|
46
+ | Python | `os.environ["VAR_NAME"]` or `os.getenv("VAR_NAME")` (match existing style in file) |
47
+ | JavaScript/TypeScript | `process.env.VAR_NAME` |
48
+ | Ruby | `ENV["VAR_NAME"]` or `ENV.fetch("VAR_NAME")` |
49
+ | Go | `os.Getenv("VAR_NAME")` |
50
+ | Java | `System.getenv("VAR_NAME")` |
51
+ | Shell/Bash | `$VAR_NAME` or `${VAR_NAME}` |
52
+ | YAML/JSON config | `${VAR_NAME}` (if the framework supports interpolation) or add a comment pointing to the env var |
53
+
54
+ 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.
55
+
56
+ ### Step 4: Unstage remediated files
57
+
58
+ After refactoring, make sure the `.env` file (with real secrets) is NOT staged:
59
+
60
+ ```bash
61
+ git reset HEAD .env 2>/dev/null # unstage if accidentally staged
62
+ ```
63
+
64
+ Stage the refactored source files (which now reference env vars instead of hardcoded secrets):
65
+
66
+ ```bash
67
+ git add <refactored-files>
68
+ ```
69
+
70
+ ### Step 5: Verify the fix
71
+
72
+ 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.
73
+
74
+ ---
75
+
76
+ ## Phase 4: Report
77
+
78
+ After remediation (or if the scan was clean from the start), produce a summary:
79
+
80
+ **Clean scan:**
81
+ ```
82
+ ftm-git: Clean scan. 0 secrets found in <N> files scanned. Safe to commit.
83
+ ```
84
+
85
+ **After remediation:**
86
+ ```
87
+ ftm-git: Found <N> hardcoded secrets. Auto-remediated:
88
+
89
+ CRITICAL: sk_live_**** in src/payments.py:42 -> STRIPE_SECRET_KEY
90
+ HIGH: AIza**** in config/google.ts:18 -> GOOGLE_API_KEY
91
+ MEDIUM: .env was not in .gitignore -> added
92
+
93
+ Actions taken:
94
+ - Extracted <N> secrets to .env (gitignored)
95
+ - Created/updated .env.example with placeholder vars
96
+ - Refactored <N> source files to use env var references
97
+ - Updated .gitignore
98
+
99
+ Verify the app still works with the new env var setup, then commit.
100
+ ```
101
+
102
+ **Blocked (auto-fix not possible):**
103
+
104
+ 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:
105
+
106
+ ```
107
+ ftm-git: BLOCKED. Found secrets that require manual remediation:
108
+
109
+ CRITICAL: Private key in assets/cert.pem:1
110
+ -> Move this file outside the repo and reference via path env var
111
+
112
+ Action required: Fix the above manually, then run ftm-git again.
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Phase 5: Git History Check (Manual Invocation Only)
118
+
119
+ 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.
120
+
121
+ ```bash
122
+ git log --all --diff-filter=A --name-only --pretty=format:"%H" -- "*.env" "*.pem" "*.key" "*credentials*" "*secret*"
123
+ ```
124
+
125
+ 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:
126
+
127
+ 1. Rotate the credential immediately (it's compromised)
128
+ 2. Use `git filter-repo` or BFG Repo Cleaner to purge from history if needed
129
+
130
+ ---
131
+
132
+ ## Operating Principles
133
+
134
+ 1. **Block first, fix second.** Never let a secret through while figuring out the fix. The commit waits.
135
+ 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.
136
+ 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).
137
+ 4. **Env vars are the escape hatch.** The remediation pattern is always: secret goes to gitignored .env, code references the env var.
138
+ 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.
139
+ 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.
@@ -1,110 +1,110 @@
1
- #!/usr/bin/env bash
2
- # ftm-git pre-commit hook — blocks commits containing hardcoded secrets
3
- # Installed by the ftm-git skill on first invocation. Safe to remove with:
4
- # rm .git/hooks/pre-commit (or edit to remove the ftm-git section)
5
- #
6
- # This hook scans staged files only (fast). The full ftm-git skill does
7
- # deeper scanning with context validation and auto-remediation — this is
8
- # the safety net that catches what slips through.
9
-
10
- set -euo pipefail
11
-
12
- RED='\033[0;31m'
13
- YELLOW='\033[0;33m'
14
- NC='\033[0m'
15
-
16
- # Get list of staged files (excluding deletions)
17
- STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null || true)
18
- if [ -z "$STAGED_FILES" ]; then
19
- exit 0
20
- fi
21
-
22
- # Skip binary files, lock files, and vendored directories
23
- FILTERED_FILES=""
24
- for f in $STAGED_FILES; do
25
- case "$f" in
26
- node_modules/*|vendor/*|.git/*|__pycache__/*|dist/*|build/*) continue ;;
27
- package-lock.json|yarn.lock|Gemfile.lock|poetry.lock|pnpm-lock.yaml) continue ;;
28
- *.png|*.jpg|*.gif|*.ico|*.woff|*.woff2|*.ttf|*.eot|*.pdf|*.zip|*.tar*) continue ;;
29
- .env.example|.env.sample|.env.template) continue ;;
30
- *) FILTERED_FILES="$FILTERED_FILES $f" ;;
31
- esac
32
- done
33
-
34
- if [ -z "$FILTERED_FILES" ]; then
35
- exit 0
36
- fi
37
-
38
- FOUND=0
39
- FINDINGS=""
40
-
41
- # Tier 1: High-confidence patterns — these almost never false-positive
42
- # We scan staged content (not working tree) to catch exactly what would be committed
43
- PATTERNS=(
44
- 'AKIA[0-9A-Z]{16}' # AWS Access Key ID
45
- 'ghp_[A-Za-z0-9_]{36}' # GitHub PAT
46
- 'gho_[A-Za-z0-9_]{36}' # GitHub OAuth
47
- 'ghu_[A-Za-z0-9_]{36}' # GitHub user token
48
- 'ghs_[A-Za-z0-9_]{36}' # GitHub server token
49
- 'github_pat_[A-Za-z0-9_]{82}' # GitHub fine-grained PAT
50
- 'xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}' # Slack bot token
51
- 'xoxp-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34}' # Slack user token
52
- 'xoxa-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34}' # Slack app token
53
- 'AIza[0-9A-Za-z\-_]{35}' # Google API key
54
- 'sk_live_[0-9a-zA-Z]{24,}' # Stripe live secret
55
- 'sk_test_[0-9a-zA-Z]{24,}' # Stripe test secret
56
- 'rk_live_[0-9a-zA-Z]{24,}' # Stripe restricted
57
- 'SG\.[A-Za-z0-9\-_]{22}\.[A-Za-z0-9\-_]{43}' # SendGrid
58
- 'SK[0-9a-fA-F]{32}' # Twilio
59
- 'npm_[A-Za-z0-9]{36}' # npm token
60
- 'glpat-[A-Za-z0-9\-_]{20,}' # GitLab PAT
61
- '-----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY-----' # Private keys
62
- )
63
-
64
- for f in $FILTERED_FILES; do
65
- # Get the staged version of the file (what would actually be committed)
66
- CONTENT=$(git show ":$f" 2>/dev/null || true)
67
- if [ -z "$CONTENT" ]; then
68
- continue
69
- fi
70
-
71
- for pattern in "${PATTERNS[@]}"; do
72
- MATCHES=$(echo "$CONTENT" | grep -nE "$pattern" 2>/dev/null || true)
73
- if [ -n "$MATCHES" ]; then
74
- while IFS= read -r match; do
75
- LINE_NUM=$(echo "$match" | cut -d: -f1)
76
- # Mask the secret value in output (show first 8 chars only)
77
- MASKED=$(echo "$match" | cut -d: -f2- | sed -E 's/([A-Za-z0-9_\-]{8})[A-Za-z0-9_\-]{8,}/\1****/g')
78
- FINDINGS="${FINDINGS}\n ${RED}BLOCKED${NC} $f:$LINE_NUM $MASKED"
79
- FOUND=$((FOUND + 1))
80
- done <<< "$MATCHES"
81
- fi
82
- done
83
- done
84
-
85
- # Also check: is a .env file being committed?
86
- for f in $STAGED_FILES; do
87
- case "$f" in
88
- .env|.env.local|.env.production|.env.staging|.env.*.local)
89
- FINDINGS="${FINDINGS}\n ${YELLOW}WARNING${NC} $f is staged — this file typically contains secrets and should be gitignored"
90
- FOUND=$((FOUND + 1))
91
- ;;
92
- esac
93
- done
94
-
95
- if [ "$FOUND" -gt 0 ]; then
96
- echo ""
97
- echo -e "${RED}ftm-git: COMMIT BLOCKED — $FOUND secret(s) detected in staged files${NC}"
98
- echo ""
99
- echo -e "$FINDINGS"
100
- echo ""
101
- echo "To fix: run /ftm-git for auto-remediation, or manually:"
102
- echo " 1. Move secrets to .env (gitignored)"
103
- echo " 2. Replace hardcoded values with env var references"
104
- echo " 3. Stage the cleaned files and commit again"
105
- echo ""
106
- echo "To bypass (NOT recommended): git commit --no-verify"
107
- exit 1
108
- fi
109
-
110
- exit 0
1
+ #!/usr/bin/env bash
2
+ # ftm-git pre-commit hook — blocks commits containing hardcoded secrets
3
+ # Installed by the ftm-git skill on first invocation. Safe to remove with:
4
+ # rm .git/hooks/pre-commit (or edit to remove the ftm-git section)
5
+ #
6
+ # This hook scans staged files only (fast). The full ftm-git skill does
7
+ # deeper scanning with context validation and auto-remediation — this is
8
+ # the safety net that catches what slips through.
9
+
10
+ set -euo pipefail
11
+
12
+ RED='\033[0;31m'
13
+ YELLOW='\033[0;33m'
14
+ NC='\033[0m'
15
+
16
+ # Get list of staged files (excluding deletions)
17
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null || true)
18
+ if [ -z "$STAGED_FILES" ]; then
19
+ exit 0
20
+ fi
21
+
22
+ # Skip binary files, lock files, and vendored directories
23
+ FILTERED_FILES=""
24
+ for f in $STAGED_FILES; do
25
+ case "$f" in
26
+ node_modules/*|vendor/*|.git/*|__pycache__/*|dist/*|build/*) continue ;;
27
+ package-lock.json|yarn.lock|Gemfile.lock|poetry.lock|pnpm-lock.yaml) continue ;;
28
+ *.png|*.jpg|*.gif|*.ico|*.woff|*.woff2|*.ttf|*.eot|*.pdf|*.zip|*.tar*) continue ;;
29
+ .env.example|.env.sample|.env.template) continue ;;
30
+ *) FILTERED_FILES="$FILTERED_FILES $f" ;;
31
+ esac
32
+ done
33
+
34
+ if [ -z "$FILTERED_FILES" ]; then
35
+ exit 0
36
+ fi
37
+
38
+ FOUND=0
39
+ FINDINGS=""
40
+
41
+ # Tier 1: High-confidence patterns — these almost never false-positive
42
+ # We scan staged content (not working tree) to catch exactly what would be committed
43
+ PATTERNS=(
44
+ 'AKIA[0-9A-Z]{16}' # AWS Access Key ID
45
+ 'ghp_[A-Za-z0-9_]{36}' # GitHub PAT
46
+ 'gho_[A-Za-z0-9_]{36}' # GitHub OAuth
47
+ 'ghu_[A-Za-z0-9_]{36}' # GitHub user token
48
+ 'ghs_[A-Za-z0-9_]{36}' # GitHub server token
49
+ 'github_pat_[A-Za-z0-9_]{82}' # GitHub fine-grained PAT
50
+ 'xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}' # Slack bot token
51
+ 'xoxp-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34}' # Slack user token
52
+ 'xoxa-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34}' # Slack app token
53
+ 'AIza[0-9A-Za-z\-_]{35}' # Google API key
54
+ 'sk_live_[0-9a-zA-Z]{24,}' # Stripe live secret
55
+ 'sk_test_[0-9a-zA-Z]{24,}' # Stripe test secret
56
+ 'rk_live_[0-9a-zA-Z]{24,}' # Stripe restricted
57
+ 'SG\.[A-Za-z0-9\-_]{22}\.[A-Za-z0-9\-_]{43}' # SendGrid
58
+ 'SK[0-9a-fA-F]{32}' # Twilio
59
+ 'npm_[A-Za-z0-9]{36}' # npm token
60
+ 'glpat-[A-Za-z0-9\-_]{20,}' # GitLab PAT
61
+ '-----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY-----' # Private keys
62
+ )
63
+
64
+ for f in $FILTERED_FILES; do
65
+ # Get the staged version of the file (what would actually be committed)
66
+ CONTENT=$(git show ":$f" 2>/dev/null || true)
67
+ if [ -z "$CONTENT" ]; then
68
+ continue
69
+ fi
70
+
71
+ for pattern in "${PATTERNS[@]}"; do
72
+ MATCHES=$(echo "$CONTENT" | grep -nE "$pattern" 2>/dev/null || true)
73
+ if [ -n "$MATCHES" ]; then
74
+ while IFS= read -r match; do
75
+ LINE_NUM=$(echo "$match" | cut -d: -f1)
76
+ # Mask the secret value in output (show first 8 chars only)
77
+ MASKED=$(echo "$match" | cut -d: -f2- | sed -E 's/([A-Za-z0-9_\-]{8})[A-Za-z0-9_\-]{8,}/\1****/g')
78
+ FINDINGS="${FINDINGS}\n ${RED}BLOCKED${NC} $f:$LINE_NUM $MASKED"
79
+ FOUND=$((FOUND + 1))
80
+ done <<< "$MATCHES"
81
+ fi
82
+ done
83
+ done
84
+
85
+ # Also check: is a .env file being committed?
86
+ for f in $STAGED_FILES; do
87
+ case "$f" in
88
+ .env|.env.local|.env.production|.env.staging|.env.*.local)
89
+ FINDINGS="${FINDINGS}\n ${YELLOW}WARNING${NC} $f is staged — this file typically contains secrets and should be gitignored"
90
+ FOUND=$((FOUND + 1))
91
+ ;;
92
+ esac
93
+ done
94
+
95
+ if [ "$FOUND" -gt 0 ]; then
96
+ echo ""
97
+ echo -e "${RED}ftm-git: COMMIT BLOCKED — $FOUND secret(s) detected in staged files${NC}"
98
+ echo ""
99
+ echo -e "$FINDINGS"
100
+ echo ""
101
+ echo "To fix: run /ftm-git for auto-remediation, or manually:"
102
+ echo " 1. Move secrets to .env (gitignored)"
103
+ echo " 2. Replace hardcoded values with env var references"
104
+ echo " 3. Stage the cleaned files and commit again"
105
+ echo ""
106
+ echo "To bypass (NOT recommended): git commit --no-verify"
107
+ exit 1
108
+ fi
109
+
110
+ exit 0
package/ftm-git.yml CHANGED
@@ -1,2 +1,2 @@
1
- name: ftm-git
2
- 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.
1
+ name: ftm-git
2
+ 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.
@@ -1,64 +1,64 @@
1
- """
2
- Exponential-backoff retry decorator for HTTP adapter calls.
3
-
4
- Usage:
5
- @retry(max_attempts=3, base_delay=1.0)
6
- def poll(self) -> list[dict]:
7
- ...
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- import functools
13
- import logging
14
- import time
15
- from typing import Callable, TypeVar
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
- F = TypeVar("F", bound=Callable)
20
-
21
-
22
- def retry(
23
- max_attempts: int = 3,
24
- base_delay: float = 1.0,
25
- backoff_factor: float = 2.0,
26
- exceptions: tuple[type[Exception], ...] = (Exception,),
27
- ) -> Callable[[F], F]:
28
- """
29
- Decorator: retry on exception with exponential backoff.
30
-
31
- Args:
32
- max_attempts: Total number of attempts (including the first).
33
- base_delay: Initial sleep duration in seconds.
34
- backoff_factor: Multiplier applied to delay after each failure.
35
- exceptions: Exception types that trigger a retry.
36
- """
37
-
38
- def decorator(func: F) -> F:
39
- @functools.wraps(func)
40
- def wrapper(*args, **kwargs):
41
- delay = base_delay
42
- last_exc: Exception | None = None
43
- for attempt in range(1, max_attempts + 1):
44
- try:
45
- return func(*args, **kwargs)
46
- except exceptions as exc:
47
- last_exc = exc
48
- if attempt == max_attempts:
49
- break
50
- logger.warning(
51
- "%s failed (attempt %d/%d): %s — retrying in %.1fs",
52
- func.__qualname__,
53
- attempt,
54
- max_attempts,
55
- exc,
56
- delay,
57
- )
58
- time.sleep(delay)
59
- delay *= backoff_factor
60
- raise last_exc # type: ignore[misc]
61
-
62
- return wrapper # type: ignore[return-value]
63
-
64
- return decorator
1
+ """
2
+ Exponential-backoff retry decorator for HTTP adapter calls.
3
+
4
+ Usage:
5
+ @retry(max_attempts=3, base_delay=1.0)
6
+ def poll(self) -> list[dict]:
7
+ ...
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import functools
13
+ import logging
14
+ import time
15
+ from typing import Callable, TypeVar
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ F = TypeVar("F", bound=Callable)
20
+
21
+
22
+ def retry(
23
+ max_attempts: int = 3,
24
+ base_delay: float = 1.0,
25
+ backoff_factor: float = 2.0,
26
+ exceptions: tuple[type[Exception], ...] = (Exception,),
27
+ ) -> Callable[[F], F]:
28
+ """
29
+ Decorator: retry on exception with exponential backoff.
30
+
31
+ Args:
32
+ max_attempts: Total number of attempts (including the first).
33
+ base_delay: Initial sleep duration in seconds.
34
+ backoff_factor: Multiplier applied to delay after each failure.
35
+ exceptions: Exception types that trigger a retry.
36
+ """
37
+
38
+ def decorator(func: F) -> F:
39
+ @functools.wraps(func)
40
+ def wrapper(*args, **kwargs):
41
+ delay = base_delay
42
+ last_exc: Exception | None = None
43
+ for attempt in range(1, max_attempts + 1):
44
+ try:
45
+ return func(*args, **kwargs)
46
+ except exceptions as exc:
47
+ last_exc = exc
48
+ if attempt == max_attempts:
49
+ break
50
+ logger.warning(
51
+ "%s failed (attempt %d/%d): %s — retrying in %.1fs",
52
+ func.__qualname__,
53
+ attempt,
54
+ max_attempts,
55
+ exc,
56
+ delay,
57
+ )
58
+ time.sleep(delay)
59
+ delay *= backoff_factor
60
+ raise last_exc # type: ignore[misc]
61
+
62
+ return wrapper # type: ignore[return-value]
63
+
64
+ return decorator