feed-the-machine 1.3.0 → 1.3.1

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.
@@ -18,6 +18,7 @@ Two-level diagram system: a root subway map of modules and per-module street map
18
18
 
19
19
  ---
20
20
 
21
+
21
22
  ## Graph-Powered Mode (ftm-map integration)
22
23
 
23
24
  Before running the standard analysis, check if the project has a code knowledge graph:
@@ -225,7 +226,6 @@ Add `sendEmail` node with edges to `src/notifications/DIAGRAM.mmd`.
225
226
  **View current architecture:**
226
227
  > "show architecture"
227
228
  Read and display `ARCHITECTURE.mmd` + list available module diagrams.
228
-
229
229
  ---
230
230
 
231
231
  ### Auto-Invocation by ftm-executor
@@ -23,10 +23,10 @@ description: Autonomous plan execution engine. Takes any plan document and execu
23
23
 
24
24
  Before starting, load context from the blackboard:
25
25
 
26
- 1. Read `/Users/kioja.kudumu/.claude/ftm-state/blackboard/context.json` — check current_task, recent_decisions, active_constraints
27
- 2. Read `/Users/kioja.kudumu/.claude/ftm-state/blackboard/experiences/index.json` — filter entries by task_type matching plan tasks and tags overlapping with the plan domain
26
+ 1. Read `~/.claude/ftm-state/blackboard/context.json` — check current_task, recent_decisions, active_constraints
27
+ 2. Read `~/.claude/ftm-state/blackboard/experiences/index.json` — filter entries by task_type matching plan tasks and tags overlapping with the plan domain
28
28
  3. Load top 3-5 matching experience files for relevant lessons on agent performance and timing
29
- 4. Read `/Users/kioja.kudumu/.claude/ftm-state/blackboard/patterns.json` — check execution_patterns for agent performance and timing accuracy patterns
29
+ 4. Read `~/.claude/ftm-state/blackboard/patterns.json` — check execution_patterns for agent performance and timing accuracy patterns
30
30
 
31
31
  If index.json is empty or no matches found, proceed normally without experience-informed shortcuts.
32
32
 
@@ -648,12 +648,12 @@ Use `ftm-executor` when: human says "just go" and trusts the plan.
648
648
 
649
649
  After completing, update the blackboard:
650
650
 
651
- 1. Update `/Users/kioja.kudumu/.claude/ftm-state/blackboard/context.json`:
651
+ 1. Update `~/.claude/ftm-state/blackboard/context.json`:
652
652
  - Set current_task status to "complete"
653
653
  - Append decision summary to recent_decisions (cap at 10)
654
654
  - Update session_metadata.skills_invoked and last_updated
655
- 2. Write an experience file to `/Users/kioja.kudumu/.claude/ftm-state/blackboard/experiences/YYYY-MM-DD_task-slug.json` capturing task_type, agent team used, wave count, audit outcomes, and lessons learned
656
- 3. Update `/Users/kioja.kudumu/.claude/ftm-state/blackboard/experiences/index.json` with the new entry
655
+ 2. Write an experience file to `~/.claude/ftm-state/blackboard/experiences/YYYY-MM-DD_task-slug.json` capturing task_type, agent team used, wave count, audit outcomes, and lessons learned
656
+ 3. Update `~/.claude/ftm-state/blackboard/experiences/index.json` with the new entry
657
657
  4. Emit `task_completed` event
658
658
 
659
659
  ## Requirements
package/ftm-git/SKILL.md CHANGED
@@ -14,6 +14,7 @@ description: Secret scanning and credential safety gate for git operations. Prev
14
14
  ### Listens To
15
15
  - `code_changed` — run a quick scan on modified files before they get staged
16
16
  - `code_committed` — verify the commit doesn't contain secrets (post-commit safety net)
17
+ - `push_requested` — block push and run full scan if not already cleared
17
18
 
18
19
  ## Blackboard Read
19
20
 
@@ -38,7 +39,7 @@ Yesterday we pushed API keys to the repo. That's the kind of mistake that leads
38
39
 
39
40
  ## Phase -1: Install Git Hook (First Invocation Only)
40
41
 
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 this skill is not invoked, or someone runs git directly from the terminal, the hook catches it.
42
+ 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
 
43
44
  **Check if the hook is already installed:**
44
45
 
@@ -87,30 +88,104 @@ Before scanning, figure out what needs scanning and why you were invoked.
87
88
 
88
89
  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
 
90
- Read `references/patterns/SECRET-PATTERNS.md` for the full Tier 1 and Tier 2 pattern library, the false positive suppression list, severity classifications, and per-finding record format.
91
+ ### Tier 1: High-Confidence Patterns (almost certainly real secrets)
91
92
 
92
- **Core Tier 1 patterns** (the most common memorize these, consult the reference for the full set):
93
+ These patterns have distinctive prefixes or structures that make false positives rare:
93
94
 
94
95
  ```
95
- AKIA[0-9A-Z]{16} # AWS Access Key ID
96
- ghp_[A-Za-z0-9_]{36} # GitHub PAT (classic)
97
- sk_live_[0-9a-zA-Z]{24,} # Stripe secret key (live)
98
- AIza[0-9A-Za-z\-_]{35} # Google API key
99
- xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24} # Slack bot token
100
- -----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY----- # Private keys
96
+ # AWS
97
+ AKIA[0-9A-Z]{16} # AWS Access Key ID
98
+ amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} # AWS MWS
99
+
100
+ # GitHub
101
+ ghp_[A-Za-z0-9_]{36} # GitHub PAT (classic)
102
+ gho_[A-Za-z0-9_]{36} # GitHub OAuth
103
+ ghu_[A-Za-z0-9_]{36} # GitHub user token
104
+ ghs_[A-Za-z0-9_]{36} # GitHub server token
105
+ github_pat_[A-Za-z0-9_]{82} # GitHub fine-grained PAT
106
+
107
+ # Slack
108
+ xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24} # Slack bot token
109
+ xoxp-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack user token
110
+ xoxa-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack app token
111
+ xoxr-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack refresh token
112
+
113
+ # Google
114
+ AIza[0-9A-Za-z\-_]{35} # Google API key
115
+
116
+ # Stripe
117
+ sk_live_[0-9a-zA-Z]{24,} # Stripe secret key (live)
118
+ sk_test_[0-9a-zA-Z]{24,} # Stripe secret key (test)
119
+ rk_live_[0-9a-zA-Z]{24,} # Stripe restricted key
120
+
121
+ # Other services
122
+ SG\.[A-Za-z0-9\-_]{22}\.[A-Za-z0-9\-_]{43} # SendGrid
123
+ SK[0-9a-fA-F]{32} # Twilio
124
+ npm_[A-Za-z0-9]{36} # npm token
125
+ pypi-[A-Za-z0-9\-_]{100,} # PyPI token
126
+ glpat-[A-Za-z0-9\-_]{20,} # GitLab PAT
127
+ -----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY----- # Private keys
101
128
  ```
102
129
 
103
- Run Tier 1 patterns in parallel since they're independent. For Tier 2, check surrounding context before confirming.
130
+ ### Tier 2: Context-Dependent Patterns (need surrounding context to confirm)
131
+
132
+ These match common assignment patterns. Check that the value isn't a placeholder, empty string, or env var reference before flagging:
133
+
134
+ ```
135
+ # Generic key/secret assignments — flag if value looks real (not placeholder)
136
+ (api_key|apikey|api-key)\s*[:=]\s*["']?[A-Za-z0-9\-_]{16,}["']?
137
+ (secret|secret_key|client_secret)\s*[:=]\s*["']?[A-Za-z0-9\-_]{16,}["']?
138
+ (password|passwd|pwd)\s*[:=]\s*["']?[^\s"']{8,}["']?
139
+ (token|access_token|auth_token)\s*[:=]\s*["']?[A-Za-z0-9\-_.]{16,}["']?
140
+ (database_url|db_url|connection_string)\s*[:=]\s*["']?[^\s"']{20,}["']?
141
+
142
+ # Bearer tokens in code
143
+ bearer\s+[A-Za-z0-9\-._~+/]{20,}
144
+
145
+ # Webhook URLs with tokens
146
+ https://hooks\.slack\.com/services/T[A-Z0-9]{8,}/B[A-Z0-9]{8,}/[a-zA-Z0-9]{24}
147
+ ```
148
+
149
+ ### What to Ignore (false positive suppression)
150
+
151
+ Skip matches that are clearly not real secrets:
152
+
153
+ - Values that are `""`, `''`, `None`, `null`, `undefined`, `TODO`, `CHANGEME`, `your-key-here`, `xxx`, `placeholder`, `example`, `test`, `dummy`, `fake`, `sample`
154
+ - References to environment variables: `os.environ[`, `process.env.`, `ENV[`, `${`, `os.getenv(`
155
+ - Lines that are comments (`#`, `//`, `/*`, `--`)
156
+ - Files in `node_modules/`, `.git/`, `vendor/`, `__pycache__/`, `dist/`, `build/`
157
+ - Files that are themselves `.env.example`, `.env.sample`, `.env.template`
158
+ - Lock files (`package-lock.json`, `yarn.lock`, `Gemfile.lock`, `poetry.lock`)
159
+ - 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)
160
+
161
+ ### Running the Scan
162
+
163
+ 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.
164
+
165
+ For each finding, record:
166
+ - **file**: absolute path
167
+ - **line**: line number
168
+ - **pattern**: which pattern matched
169
+ - **tier**: 1 or 2
170
+ - **value_preview**: first 8 chars + `...` + last 4 chars (never log the full secret)
171
+ - **context**: the surrounding code (with the secret value masked)
104
172
 
105
173
  ## Phase 2: Validate Findings
106
174
 
107
175
  For each Tier 2 match, read the surrounding context (5 lines before and after) and determine:
108
176
 
109
- 1. **Is the value a real secret or a placeholder?** — Check against the ignore list in `references/patterns/SECRET-PATTERNS.md`.
177
+ 1. **Is the value a real secret or a placeholder?** — Check against the ignore list above.
110
178
  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.
111
179
  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.
112
180
 
113
- After validation, produce a findings list sorted by severity (CRITICAL → HIGH → MEDIUM → LOW). See `references/patterns/SECRET-PATTERNS.md` for the severity table.
181
+ After validation, produce a findings list sorted by severity:
182
+
183
+ | Severity | Meaning |
184
+ |---|---|
185
+ | **CRITICAL** | Tier 1 match (high-confidence secret) in a tracked or staged file |
186
+ | **HIGH** | Tier 2 confirmed match in a tracked or staged file |
187
+ | **MEDIUM** | `.env` file not in `.gitignore`, or secret in a fallback default |
188
+ | **LOW** | Secret in a gitignored file but the gitignore rule might be fragile |
114
189
 
115
190
  If zero findings after validation: emit `secrets_clear` and proceed. The commit/push is safe.
116
191
 
@@ -120,23 +195,135 @@ If any CRITICAL or HIGH findings: **STOP. The commit/push is BLOCKED.** Say this
120
195
  ftm-git: BLOCKED — <N> secret(s) found. Commit/push halted. Attempting auto-remediation...
121
196
  ```
122
197
 
123
- Then proceed to Phase 3. The commit/push does NOT happen until Phase 3 completes and a re-scan comes back clean.
198
+ 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.
124
199
 
125
200
  ## Phase 3: Auto-Remediate
126
201
 
127
- Read `references/protocols/REMEDIATION.md` for the full step-by-step remediation protocol, language-specific env var patterns, report formats (clean/remediated/blocked), and the Phase 5 git history deep scan procedure.
202
+ For each finding, apply the appropriate fix automatically. The goal is to make the code safe without breaking functionality.
203
+
204
+ ### Step 1: Ensure .env infrastructure exists
128
205
 
129
- **Summary of steps:**
130
- 1. Ensure `.env` and `.gitignore` infrastructure exists
131
- 2. Extract each secret to `.env` with a SCREAMING_SNAKE_CASE var name
132
- 3. Add placeholder to `.env.example`
133
- 4. Refactor source files to reference the env var (match language pattern)
134
- 5. Unstage `.env`, re-stage refactored source files
135
- 6. Verify: re-run Phase 1 on refactored files — do not proceed until clean
206
+ Check for a `.env` file in the project root. If it doesn't exist, create one with a header comment:
207
+
208
+ ```
209
+ # Environment variables DO NOT COMMIT THIS FILE
210
+ # Copy .env.example for the template, fill in real values locally
211
+ ```
212
+
213
+ Check `.gitignore` for `.env` coverage. If missing, add:
214
+ ```
215
+ # Environment files with secrets
216
+ .env
217
+ .env.local
218
+ .env.production
219
+ .env.staging
220
+ .env.*.local
221
+ ```
222
+
223
+ ### Step 2: Extract secrets to .env
224
+
225
+ For each finding:
226
+
227
+ 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.
228
+
229
+ 2. **Add to .env** — append `VAR_NAME=<actual-secret-value>` to `.env`. If the var already exists, don't duplicate it.
230
+
231
+ 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.
232
+
233
+ ### Step 3: Refactor source files
234
+
235
+ Replace the hardcoded secret with an env var reference. Match the language/framework:
236
+
237
+ | Language | Pattern |
238
+ |---|---|
239
+ | Python | `os.environ["VAR_NAME"]` or `os.getenv("VAR_NAME")` (match existing style in file) |
240
+ | JavaScript/TypeScript | `process.env.VAR_NAME` |
241
+ | Ruby | `ENV["VAR_NAME"]` or `ENV.fetch("VAR_NAME")` |
242
+ | Go | `os.Getenv("VAR_NAME")` |
243
+ | Java | `System.getenv("VAR_NAME")` |
244
+ | Shell/Bash | `$VAR_NAME` or `${VAR_NAME}` |
245
+ | YAML/JSON config | `${VAR_NAME}` (if the framework supports interpolation) or add a comment pointing to the env var |
246
+
247
+ 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.
248
+
249
+ ### Step 4: Unstage remediated files
250
+
251
+ After refactoring, make sure the `.env` file (with real secrets) is NOT staged:
252
+
253
+ ```bash
254
+ git reset HEAD .env 2>/dev/null # unstage if accidentally staged
255
+ ```
256
+
257
+ Stage the refactored source files (which now reference env vars instead of hardcoded secrets):
258
+
259
+ ```bash
260
+ git add <refactored-files>
261
+ ```
262
+
263
+ ### Step 5: Verify the fix
264
+
265
+ 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.
136
266
 
137
267
  ## Phase 4: Report
138
268
 
139
- After remediation or clean scan, produce the summary. Read `references/protocols/REMEDIATION.md` for the exact report formats.
269
+ After remediation (or if the scan was clean from the start), produce a summary:
270
+
271
+ **Clean scan:**
272
+ ```
273
+ ftm-git: Clean scan. 0 secrets found in <N> files scanned. Safe to commit.
274
+ ```
275
+
276
+ **After remediation:**
277
+ ```
278
+ ftm-git: Found <N> hardcoded secrets. Auto-remediated:
279
+
280
+ CRITICAL: sk_live_**** in src/payments.py:42 -> STRIPE_SECRET_KEY
281
+ HIGH: AIza**** in config/google.ts:18 -> GOOGLE_API_KEY
282
+ MEDIUM: .env was not in .gitignore -> added
283
+
284
+ Actions taken:
285
+ - Extracted <N> secrets to .env (gitignored)
286
+ - Created/updated .env.example with placeholder vars
287
+ - Refactored <N> source files to use env var references
288
+ - Updated .gitignore
289
+
290
+ Verify the app still works with the new env var setup, then commit.
291
+ ```
292
+
293
+ **Blocked (auto-fix not possible):**
294
+
295
+ 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:
296
+
297
+ ```
298
+ ftm-git: BLOCKED. Found secrets that require manual remediation:
299
+
300
+ CRITICAL: Private key in assets/cert.pem:1
301
+ -> Move this file outside the repo and reference via path env var
302
+
303
+ Action required: Fix the above manually, then run ftm-git again.
304
+ ```
305
+
306
+ ## Phase 5: Git History Check (Manual Invocation Only)
307
+
308
+ 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.
309
+
310
+ ```bash
311
+ git log --all --diff-filter=A --name-only --pretty=format:"%H" -- "*.env" "*.pem" "*.key" "*credentials*" "*secret*"
312
+ ```
313
+
314
+ 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:
315
+
316
+ 1. Rotate the credential immediately (it's compromised)
317
+ 2. Use `git filter-repo` or BFG Repo Cleaner to purge from history if needed
318
+
319
+ ## Operating Principles
320
+
321
+ 1. **Block first, fix second.** Never let a secret through while figuring out the fix. The commit waits.
322
+ 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.
323
+ 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).
324
+ 4. **Env vars are the escape hatch.** The remediation pattern is always: secret goes to gitignored .env, code references the env var.
325
+ 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.
326
+ 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.
140
327
 
141
328
  ## Integration Points
142
329
 
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  # Start ftm-inbox backend + pollers
3
- cd "$(dirname "$0")/.."
3
+ cd "$(dirname "$0")/.." || exit
4
4
  PORT=${FTM_INBOX_PORT:-8042}
5
5
  echo "Starting ftm-inbox on port $PORT..."
6
6
  python3 -m uvicorn backend.main:app --host 0.0.0.0 --port $PORT &
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash
2
- if [ -f /tmp/ftm-inbox.pid ] && kill -0 $(cat /tmp/ftm-inbox.pid) 2>/dev/null; then
2
+ if [ -f /tmp/ftm-inbox.pid ] && kill -0 "$(cat /tmp/ftm-inbox.pid)" 2>/dev/null; then
3
3
  echo "ftm-inbox is running (PID: $(cat /tmp/ftm-inbox.pid))"
4
4
  # Show last poll times from DB if available
5
5
  CONFIG_DIR="$HOME/.claude/ftm-inbox"
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  if [ -f /tmp/ftm-inbox.pid ]; then
3
- kill $(cat /tmp/ftm-inbox.pid) 2>/dev/null
3
+ kill "$(cat /tmp/ftm-inbox.pid)" 2>/dev/null
4
4
  rm /tmp/ftm-inbox.pid
5
5
  echo "ftm-inbox stopped."
6
6
  else
@@ -190,7 +190,6 @@ When updating after changes:
190
190
  4. Write updates — add missing entries, remove stale entries, update changed fields
191
191
  5. If new modules were added, create their INTENT.md and add rows to root module map
192
192
  6. Report: list of files updated, entries added, entries removed, entries modified
193
-
194
193
  ---
195
194
 
196
195
  ### Auto-Invocation by ftm-executor