feed-the-machine 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +268 -0
- package/bin/generate-manifest.mjs +210 -0
- package/bin/install.mjs +114 -0
- package/ftm/SKILL.md +88 -0
- package/ftm-audit/SKILL.md +146 -0
- package/ftm-audit/references/protocols/PROJECT-PATTERNS.md +91 -0
- package/ftm-audit/references/protocols/RUNTIME-WIRING.md +66 -0
- package/ftm-audit/references/protocols/WIRING-CONTRACTS.md +135 -0
- package/ftm-audit/references/strategies/AUTO-FIX-STRATEGIES.md +69 -0
- package/ftm-audit/references/templates/REPORT-FORMAT.md +96 -0
- package/ftm-audit/scripts/run-knip.sh +23 -0
- package/ftm-audit.yml +2 -0
- package/ftm-brainstorm/SKILL.md +379 -0
- package/ftm-brainstorm/evals/evals.json +100 -0
- package/ftm-brainstorm/evals/promptfoo.yaml +109 -0
- package/ftm-brainstorm/references/agent-prompts.md +224 -0
- package/ftm-brainstorm/references/plan-template.md +121 -0
- package/ftm-brainstorm.yml +2 -0
- package/ftm-browse/SKILL.md +415 -0
- package/ftm-browse/daemon/browser-manager.ts +206 -0
- package/ftm-browse/daemon/bun.lock +30 -0
- package/ftm-browse/daemon/cli.ts +347 -0
- package/ftm-browse/daemon/commands.ts +410 -0
- package/ftm-browse/daemon/main.ts +357 -0
- package/ftm-browse/daemon/package.json +17 -0
- package/ftm-browse/daemon/server.ts +189 -0
- package/ftm-browse/daemon/snapshot.ts +519 -0
- package/ftm-browse/daemon/tsconfig.json +22 -0
- package/ftm-browse.yml +4 -0
- package/ftm-codex-gate/SKILL.md +302 -0
- package/ftm-codex-gate.yml +2 -0
- package/ftm-config/SKILL.md +310 -0
- package/ftm-config.default.yml +80 -0
- package/ftm-config.yml +2 -0
- package/ftm-council/SKILL.md +132 -0
- package/ftm-council/references/prompts/CLAUDE-INVESTIGATION.md +60 -0
- package/ftm-council/references/prompts/CODEX-INVESTIGATION.md +58 -0
- package/ftm-council/references/prompts/GEMINI-INVESTIGATION.md +58 -0
- package/ftm-council/references/prompts/REBUTTAL-TEMPLATE.md +57 -0
- package/ftm-council/references/protocols/PREREQUISITES.md +47 -0
- package/ftm-council/references/protocols/STEP-0-FRAMING.md +46 -0
- package/ftm-council.yml +2 -0
- package/ftm-dashboard.yml +4 -0
- package/ftm-debug/SKILL.md +146 -0
- package/ftm-debug/references/phases/PHASE-0-INTAKE.md +58 -0
- package/ftm-debug/references/phases/PHASE-1-TRIAGE.md +46 -0
- package/ftm-debug/references/phases/PHASE-2-WAR-ROOM-AGENTS.md +279 -0
- package/ftm-debug/references/phases/PHASE-3-TO-6-EXECUTION.md +436 -0
- package/ftm-debug/references/protocols/BLACKBOARD.md +86 -0
- package/ftm-debug/references/protocols/EDGE-CASES.md +103 -0
- package/ftm-debug.yml +2 -0
- package/ftm-diagram/SKILL.md +233 -0
- package/ftm-diagram.yml +2 -0
- package/ftm-executor/SKILL.md +657 -0
- package/ftm-executor/references/STYLE-TEMPLATE.md +73 -0
- package/ftm-executor/references/phases/PHASE-0-VERIFICATION.md +62 -0
- package/ftm-executor/references/phases/PHASE-2-AGENT-ASSEMBLY.md +34 -0
- package/ftm-executor/references/phases/PHASE-3-WORKTREES.md +38 -0
- package/ftm-executor/references/phases/PHASE-4-5-AUDIT.md +72 -0
- package/ftm-executor/references/phases/PHASE-4-DISPATCH.md +66 -0
- package/ftm-executor/references/phases/PHASE-5-5-CODEX-GATE.md +73 -0
- package/ftm-executor/references/protocols/DOCUMENTATION-BOOTSTRAP.md +36 -0
- package/ftm-executor/references/protocols/MODEL-PROFILE.md +44 -0
- package/ftm-executor/references/protocols/PROGRESS-TRACKING.md +66 -0
- package/ftm-executor/runtime/ftm-runtime.mjs +252 -0
- package/ftm-executor/runtime/package.json +8 -0
- package/ftm-executor.yml +2 -0
- package/ftm-git/SKILL.md +195 -0
- package/ftm-git/evals/evals.json +26 -0
- package/ftm-git/evals/promptfoo.yaml +75 -0
- package/ftm-git/hooks/post-commit-experience.sh +92 -0
- package/ftm-git/references/patterns/SECRET-PATTERNS.md +104 -0
- package/ftm-git/references/protocols/REMEDIATION.md +139 -0
- package/ftm-git/scripts/pre-commit-secrets.sh +110 -0
- package/ftm-git.yml +2 -0
- package/ftm-intent/SKILL.md +198 -0
- package/ftm-intent.yml +2 -0
- package/ftm-map.yml +2 -0
- package/ftm-mind/SKILL.md +986 -0
- package/ftm-mind/evals/promptfoo.yaml +142 -0
- package/ftm-mind/references/blackboard-schema.md +328 -0
- package/ftm-mind/references/complexity-guide.md +110 -0
- package/ftm-mind/references/event-registry.md +299 -0
- package/ftm-mind/references/mcp-inventory.md +296 -0
- package/ftm-mind/references/protocols/COMPLEXITY-SIZING.md +72 -0
- package/ftm-mind/references/protocols/MCP-HEURISTICS.md +32 -0
- package/ftm-mind/references/protocols/PLAN-APPROVAL.md +80 -0
- package/ftm-mind/references/reflexion-protocol.md +249 -0
- package/ftm-mind/references/routing/SCENARIOS.md +22 -0
- package/ftm-mind/references/routing-scenarios.md +35 -0
- package/ftm-mind.yml +2 -0
- package/ftm-pause/SKILL.md +133 -0
- package/ftm-pause/references/protocols/SKILL-RESTORE-PROTOCOLS.md +186 -0
- package/ftm-pause/references/protocols/VALIDATION.md +80 -0
- package/ftm-pause.yml +2 -0
- package/ftm-researcher.yml +2 -0
- package/ftm-resume/SKILL.md +166 -0
- package/ftm-resume/references/protocols/VALIDATION.md +172 -0
- package/ftm-resume.yml +2 -0
- package/ftm-retro/SKILL.md +189 -0
- package/ftm-retro/references/protocols/SCORING-RUBRICS.md +89 -0
- package/ftm-retro/references/templates/REPORT-FORMAT.md +109 -0
- package/ftm-retro.yml +2 -0
- package/ftm-routine.yml +4 -0
- package/ftm-state/blackboard/context.json +23 -0
- package/ftm-state/blackboard/experiences/index.json +9 -0
- package/ftm-state/blackboard/patterns.json +6 -0
- package/ftm-state/schemas/context.schema.json +130 -0
- package/ftm-state/schemas/experience-index.schema.json +77 -0
- package/ftm-state/schemas/experience.schema.json +78 -0
- package/ftm-state/schemas/patterns.schema.json +44 -0
- package/ftm-upgrade/SKILL.md +153 -0
- package/ftm-upgrade/scripts/check-version.sh +76 -0
- package/ftm-upgrade/scripts/upgrade.sh +143 -0
- package/ftm-upgrade.yml +2 -0
- package/ftm.yml +2 -0
- package/install.sh +102 -0
- package/package.json +74 -0
- package/uninstall.sh +25 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Secret Patterns — Extended Pattern Library
|
|
2
|
+
|
|
3
|
+
Full regex pattern library for Phase 1 scanning. Tier 1 patterns are high-confidence; Tier 2 require surrounding context validation.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Tier 1: High-Confidence Patterns (almost certainly real secrets)
|
|
8
|
+
|
|
9
|
+
These patterns have distinctive prefixes or structures that make false positives rare. Run in parallel.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
# AWS
|
|
13
|
+
AKIA[0-9A-Z]{16} # AWS Access Key ID
|
|
14
|
+
amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} # AWS MWS
|
|
15
|
+
|
|
16
|
+
# GitHub
|
|
17
|
+
ghp_[A-Za-z0-9_]{36} # GitHub PAT (classic)
|
|
18
|
+
gho_[A-Za-z0-9_]{36} # GitHub OAuth
|
|
19
|
+
ghu_[A-Za-z0-9_]{36} # GitHub user token
|
|
20
|
+
ghs_[A-Za-z0-9_]{36} # GitHub server token
|
|
21
|
+
github_pat_[A-Za-z0-9_]{82} # GitHub fine-grained PAT
|
|
22
|
+
|
|
23
|
+
# Slack
|
|
24
|
+
xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24} # Slack bot token
|
|
25
|
+
xoxp-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack user token
|
|
26
|
+
xoxa-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack app token
|
|
27
|
+
xoxr-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack refresh token
|
|
28
|
+
|
|
29
|
+
# Google
|
|
30
|
+
AIza[0-9A-Za-z\-_]{35} # Google API key
|
|
31
|
+
|
|
32
|
+
# Stripe
|
|
33
|
+
sk_live_[0-9a-zA-Z]{24,} # Stripe secret key (live)
|
|
34
|
+
sk_test_[0-9a-zA-Z]{24,} # Stripe secret key (test)
|
|
35
|
+
rk_live_[0-9a-zA-Z]{24,} # Stripe restricted key
|
|
36
|
+
|
|
37
|
+
# Other services
|
|
38
|
+
SG\.[A-Za-z0-9\-_]{22}\.[A-Za-z0-9\-_]{43} # SendGrid
|
|
39
|
+
SK[0-9a-fA-F]{32} # Twilio
|
|
40
|
+
npm_[A-Za-z0-9]{36} # npm token
|
|
41
|
+
pypi-[A-Za-z0-9\-_]{100,} # PyPI token
|
|
42
|
+
glpat-[A-Za-z0-9\-_]{20,} # GitLab PAT
|
|
43
|
+
-----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY----- # Private keys
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Tier 2: Context-Dependent Patterns (need surrounding context to confirm)
|
|
49
|
+
|
|
50
|
+
These match common assignment patterns. Check that the value isn't a placeholder, empty string, or env var reference before flagging:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
# Generic key/secret assignments — flag if value looks real (not placeholder)
|
|
54
|
+
(api_key|apikey|api-key)\s*[:=]\s*["']?[A-Za-z0-9\-_]{16,}["']?
|
|
55
|
+
(secret|secret_key|client_secret)\s*[:=]\s*["']?[A-Za-z0-9\-_]{16,}["']?
|
|
56
|
+
(password|passwd|pwd)\s*[:=]\s*["']?[^\s"']{8,}["']?
|
|
57
|
+
(token|access_token|auth_token)\s*[:=]\s*["']?[A-Za-z0-9\-_.]{16,}["']?
|
|
58
|
+
(database_url|db_url|connection_string)\s*[:=]\s*["']?[^\s"']{20,}["']?
|
|
59
|
+
|
|
60
|
+
# Bearer tokens in code
|
|
61
|
+
bearer\s+[A-Za-z0-9\-._~+/]{20,}
|
|
62
|
+
|
|
63
|
+
# Webhook URLs with tokens
|
|
64
|
+
https://hooks\.slack\.com/services/T[A-Z0-9]{8,}/B[A-Z0-9]{8,}/[a-zA-Z0-9]{24}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## What to Ignore (false positive suppression)
|
|
70
|
+
|
|
71
|
+
Skip matches that are clearly not real secrets:
|
|
72
|
+
|
|
73
|
+
- Values that are `""`, `''`, `None`, `null`, `undefined`, `TODO`, `CHANGEME`, `your-key-here`, `xxx`, `placeholder`, `example`, `test`, `dummy`, `fake`, `sample`
|
|
74
|
+
- References to environment variables: `os.environ[`, `process.env.`, `ENV[`, `${`, `os.getenv(`
|
|
75
|
+
- Lines that are comments (`#`, `//`, `/*`, `--`)
|
|
76
|
+
- Files in `node_modules/`, `.git/`, `vendor/`, `__pycache__/`, `dist/`, `build/`
|
|
77
|
+
- Files that are themselves `.env.example`, `.env.sample`, `.env.template`
|
|
78
|
+
- Lock files (`package-lock.json`, `yarn.lock`, `Gemfile.lock`, `poetry.lock`)
|
|
79
|
+
- 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)
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Severity Classification
|
|
84
|
+
|
|
85
|
+
After validation, findings are sorted by severity:
|
|
86
|
+
|
|
87
|
+
| Severity | Meaning |
|
|
88
|
+
|---|---|
|
|
89
|
+
| **CRITICAL** | Tier 1 match (high-confidence secret) in a tracked or staged file |
|
|
90
|
+
| **HIGH** | Tier 2 confirmed match in a tracked or staged file |
|
|
91
|
+
| **MEDIUM** | `.env` file not in `.gitignore`, or secret in a fallback default |
|
|
92
|
+
| **LOW** | Secret in a gitignored file but the gitignore rule might be fragile |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Per-Finding Record Format
|
|
97
|
+
|
|
98
|
+
For each finding, record:
|
|
99
|
+
- **file**: absolute path
|
|
100
|
+
- **line**: line number
|
|
101
|
+
- **pattern**: which pattern matched
|
|
102
|
+
- **tier**: 1 or 2
|
|
103
|
+
- **value_preview**: first 8 chars + `...` + last 4 chars (never log the full secret)
|
|
104
|
+
- **context**: the surrounding code (with the secret value masked)
|
|
@@ -0,0 +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.
|
|
@@ -0,0 +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
|
package/ftm-git.yml
ADDED
|
@@ -0,0 +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.
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ftm-intent
|
|
3
|
+
description: Manages the hierarchical INTENT.md documentation layer — root index with architecture decisions and module map, plus per-module INTENT.md files with function-level entries (does/why/relationships/decisions). Use when creating or updating intent documentation, bootstrapping a new project's intent layer, or when user says "update intent", "document intent", "ftm-intent", "what does this function do". Auto-invoked by ftm-executor after every commit to keep intent documentation in sync with code changes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Events
|
|
7
|
+
|
|
8
|
+
### Emits
|
|
9
|
+
- `documentation_updated` — when one or more INTENT.md files are written or modified to reflect new or changed code
|
|
10
|
+
- `task_completed` — when the full intent sync pass completes (bootstrap or incremental)
|
|
11
|
+
|
|
12
|
+
### Listens To
|
|
13
|
+
- `code_committed` — fast-path: automatically sync INTENT.md entries for every changed function after each commit
|
|
14
|
+
|
|
15
|
+
# Intent Documentation Manager
|
|
16
|
+
|
|
17
|
+
Manages the hierarchical INTENT.md documentation layer. This is the contract layer that Codex reads during code review and that enables conflict detection between Claude's intent and Codex's fixes. The "Why" field is what prevents Codex from reverting deliberate design choices.
|
|
18
|
+
|
|
19
|
+
## Graph-Powered Mode (ftm-map integration)
|
|
20
|
+
|
|
21
|
+
Before running the standard analysis, check if the project has a code knowledge graph:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
if [ -f ".ftm-map/map.db" ]; then
|
|
25
|
+
# Use graph for faster, more consistent analysis
|
|
26
|
+
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/views.py generate-intent "$PROJECT_ROOT"
|
|
27
|
+
else
|
|
28
|
+
# Fall back to standard file-by-file analysis below
|
|
29
|
+
fi
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
When `.ftm-map/map.db` exists:
|
|
33
|
+
1. Delegate to `views.py generate-intent` which reads the graph and produces INTENT.md files
|
|
34
|
+
2. The graph path is faster (single DB query vs. reading every file) and more consistent (same analysis for every commit)
|
|
35
|
+
3. Supports `--files` flag for incremental: `views.py generate-intent --files changed1.ts,changed2.py`
|
|
36
|
+
|
|
37
|
+
When `.ftm-map/map.db` does NOT exist:
|
|
38
|
+
- Fall back to the existing Bootstrap/Incremental modes below
|
|
39
|
+
- The behavior is identical to the current skill — no breaking change
|
|
40
|
+
|
|
41
|
+
This integration means ftm-intent automatically gets better when ftm-map is available, without requiring migration.
|
|
42
|
+
|
|
43
|
+
## Two Modes of Operation
|
|
44
|
+
|
|
45
|
+
### Bootstrap Mode (no INTENT.md exists)
|
|
46
|
+
Scan the codebase from scratch and create the full hierarchy.
|
|
47
|
+
|
|
48
|
+
1. Use Glob to discover all source files and identify module boundaries
|
|
49
|
+
2. Use Read/Grep to understand key functions in each module
|
|
50
|
+
3. Create root INTENT.md at the project root
|
|
51
|
+
4. Create per-module INTENT.md files for each module directory
|
|
52
|
+
5. Populate all entries based on what the code actually does
|
|
53
|
+
|
|
54
|
+
### Incremental Mode (INTENT.md already exists)
|
|
55
|
+
Read the current state and update only what changed.
|
|
56
|
+
|
|
57
|
+
1. Read root INTENT.md and all relevant module INTENT.md files
|
|
58
|
+
2. Identify what's missing: new functions without entries, new modules without INTENT.md
|
|
59
|
+
3. Identify what's stale: entries for deleted or renamed functions
|
|
60
|
+
4. Update only the affected entries and module map rows — do not regenerate from scratch
|
|
61
|
+
|
|
62
|
+
## Root INTENT.md Template
|
|
63
|
+
|
|
64
|
+
Create at the project root. This is the "subway map" — high level routing to module detail.
|
|
65
|
+
|
|
66
|
+
```markdown
|
|
67
|
+
# [Project Name] — Intent
|
|
68
|
+
|
|
69
|
+
## Vision
|
|
70
|
+
[2-3 sentence summary of what this project does and why it exists]
|
|
71
|
+
|
|
72
|
+
## Architecture Decisions
|
|
73
|
+
| Decision | Choice | Reasoning |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| [decision point] | [what was chosen] | [why this was chosen over alternatives] |
|
|
76
|
+
|
|
77
|
+
## Module Map
|
|
78
|
+
| Module | Purpose | Key Relationships |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| [path/to/module] | [what this module does in one sentence] | [depends on X / depended by Y] |
|
|
81
|
+
|
|
82
|
+
## Cross-Cutting Decisions
|
|
83
|
+
- [pattern name]: [what it is and why it applies everywhere]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Rules for root INTENT.md:**
|
|
87
|
+
- Vision: Written once, updated only if the project's purpose changes
|
|
88
|
+
- Architecture Decisions: Add a row every time a non-obvious architectural choice is made
|
|
89
|
+
- Module Map: Add a row when a new module directory is created; remove when deleted; must stay in sync with actual filesystem
|
|
90
|
+
- Cross-Cutting Decisions: Patterns that apply across 3+ modules (error handling strategy, auth approach, data fetching pattern, etc.)
|
|
91
|
+
|
|
92
|
+
## Per-Module INTENT.md Template
|
|
93
|
+
|
|
94
|
+
Create inside each module directory (e.g., `src/auth/INTENT.md`). This is the "street map" — ground level function detail.
|
|
95
|
+
|
|
96
|
+
```markdown
|
|
97
|
+
# [Module Name] — Intent
|
|
98
|
+
|
|
99
|
+
## Functions
|
|
100
|
+
|
|
101
|
+
### functionName(param1: Type, param2: Type) → ReturnType
|
|
102
|
+
- **Does**: [one sentence — what it does, not how]
|
|
103
|
+
- **Why**: [why this function exists, what problem it solves, why this approach over alternatives]
|
|
104
|
+
- **Relationships**: [calls X, called by Y, reads from Z store, mutates W]
|
|
105
|
+
- **Decisions**: [deliberate choices that might look wrong to an outside reviewer — "uses polling instead of websockets because..."]
|
|
106
|
+
|
|
107
|
+
### anotherFunction(param: Type) → ReturnType
|
|
108
|
+
- **Does**: ...
|
|
109
|
+
- **Why**: ...
|
|
110
|
+
- **Relationships**: ...
|
|
111
|
+
- **Decisions**: ...
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Rules for per-module INTENT.md:**
|
|
115
|
+
- Every exported function MUST have an entry
|
|
116
|
+
- Every entry MUST have all four fields (Does / Why / Relationships / Decisions)
|
|
117
|
+
- If there are no deliberate decisions, write "None" in the Decisions field — do not omit the field
|
|
118
|
+
- Include the full function signature with types — this helps with quick lookup and makes entries grep-able
|
|
119
|
+
- Keep each field to one sentence. This is a contract, not prose documentation.
|
|
120
|
+
|
|
121
|
+
## When to Update
|
|
122
|
+
|
|
123
|
+
| Event | Action |
|
|
124
|
+
|---|---|
|
|
125
|
+
| New function created | Add entry to module's INTENT.md |
|
|
126
|
+
| Function behavior changed | Update Does / Why / Decisions fields |
|
|
127
|
+
| Function deleted | Remove entry from module's INTENT.md |
|
|
128
|
+
| New module directory created | Create module INTENT.md + add row to root module map |
|
|
129
|
+
| Module deleted | Remove module INTENT.md + remove row from root module map |
|
|
130
|
+
| Architecture decision made | Add row to root INTENT.md decisions table |
|
|
131
|
+
| Cross-cutting pattern established | Add entry to root Cross-Cutting Decisions section |
|
|
132
|
+
|
|
133
|
+
## The Why Field — Most Important
|
|
134
|
+
|
|
135
|
+
The "Why" field is what makes this system valuable. It is the explicit record of deliberate choices that might look like bugs or inefficiencies to a reviewer who wasn't there when the decision was made.
|
|
136
|
+
|
|
137
|
+
Good Why entries:
|
|
138
|
+
- "Exists because the provider SDK doesn't expose a batch endpoint — each call must be sequential"
|
|
139
|
+
- "Uses pessimistic locking instead of optimistic because this resource has high write contention in production"
|
|
140
|
+
- "Fetches on every render instead of caching because this data changes in real time and stale reads cause downstream errors"
|
|
141
|
+
|
|
142
|
+
Bad Why entries:
|
|
143
|
+
- "Needed for the feature to work"
|
|
144
|
+
- "Required by the system"
|
|
145
|
+
- "Called by the auth flow"
|
|
146
|
+
|
|
147
|
+
If you can't write a clear Why, it means the original reasoning wasn't captured. Try to infer it from surrounding code, comments, or git history. If it's truly unknown, write "Why unknown — inferred from usage: [your inference]".
|
|
148
|
+
|
|
149
|
+
## Format Contract
|
|
150
|
+
|
|
151
|
+
Codex reads INTENT.md files during code review to detect conflicts between stated intent and proposed changes. For this to work, the format must be consistent.
|
|
152
|
+
|
|
153
|
+
**Required format — do not deviate:**
|
|
154
|
+
- Section header: `### functionName(params) → ReturnType`
|
|
155
|
+
- Four bullet fields in order: `- **Does**:`, `- **Why**:`, `- **Relationships**:`, `- **Decisions**:`
|
|
156
|
+
- No prose paragraphs inside function entries
|
|
157
|
+
- No nested bullets inside a field — one sentence per field, always
|
|
158
|
+
|
|
159
|
+
If a function is complex enough that one sentence isn't enough, the function is probably doing too much. Document what it does at the boundary level, not the implementation level.
|
|
160
|
+
|
|
161
|
+
## Discovery Commands
|
|
162
|
+
|
|
163
|
+
Use these to find what needs to be documented:
|
|
164
|
+
|
|
165
|
+
- Find all module directories: `Glob("src/**/")` or `Glob("lib/**/")`
|
|
166
|
+
- Find existing INTENT.md files: `Glob("**/INTENT.md")`
|
|
167
|
+
- Find all exported functions in a module: `Grep("^export (function|const|async function)", path="src/module/")`
|
|
168
|
+
- Find functions called by a specific function: read the function body and trace calls
|
|
169
|
+
- Find what calls a specific function: `Grep("functionName", type="ts")` or equivalent
|
|
170
|
+
|
|
171
|
+
## Bootstrap Execution Order
|
|
172
|
+
|
|
173
|
+
When creating the intent layer from scratch:
|
|
174
|
+
|
|
175
|
+
1. Read the project root README or package.json to understand the project vision
|
|
176
|
+
2. Run `Glob("src/**/")` (or equivalent for the project structure) to discover modules
|
|
177
|
+
3. For each module, read key files to understand what functions exist and what they do
|
|
178
|
+
4. Draft root INTENT.md — vision, then module map (one row per module), then architecture decisions from what you observed, then cross-cutting patterns
|
|
179
|
+
5. For each module, draft module INTENT.md — one entry per exported function
|
|
180
|
+
6. Write all files
|
|
181
|
+
7. Report: list of files created, count of functions documented, any functions where Why was unclear
|
|
182
|
+
|
|
183
|
+
## Incremental Execution Order
|
|
184
|
+
|
|
185
|
+
When updating after changes:
|
|
186
|
+
|
|
187
|
+
1. Read root INTENT.md
|
|
188
|
+
2. Read the INTENT.md for affected modules (or all modules if unsure what changed)
|
|
189
|
+
3. Compare against current code — use Grep to find functions that don't have entries, entries that don't have corresponding functions
|
|
190
|
+
4. Write updates — add missing entries, remove stale entries, update changed fields
|
|
191
|
+
5. If new modules were added, create their INTENT.md and add rows to root module map
|
|
192
|
+
6. Report: list of files updated, entries added, entries removed, entries modified
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
### Auto-Invocation by ftm-executor
|
|
197
|
+
|
|
198
|
+
This skill's format is used by ftm-executor's documentation pipeline. After every commit during plan execution, agents update INTENT.md (or DIAGRAM.mmd) entries following this skill's templates. The updates are automatic and don't require explicit skill invocation — agents reference the format directly.
|
package/ftm-intent.yml
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
name: ftm-intent
|
|
2
|
+
description: Manages the hierarchical INTENT.md documentation layer — root index with architecture decisions and module map, plus per-module INTENT.md files with function-level entries (does/why/relationships/decisions). Use when creating or updating intent documentation, bootstrapping a new project's intent layer, or when user says "update intent", "document intent", "ftm-intent", "what does this function do". Auto-invoked by ftm-executor after every commit to keep intent documentation in sync with code changes.
|
package/ftm-map.yml
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
name: ftm-map
|
|
2
|
+
description: Persistent code knowledge graph powered by tree-sitter and SQLite with FTS5 full-text search. Builds structural dependency graphs, enables blast radius analysis, dependency chain queries, and keyword-based code search. Use when the user asks "what breaks if I change X", "what depends on Y", "blast radius of Z", "where do we handle auth", "map this codebase", "index this project", "what calls function X", "show dependencies for Y". Also triggered by ftm-intent and ftm-diagram for graph-powered view generation. Triggers on "blast radius", "what breaks", "what calls", "what depends on", "where do we", "map codebase", "index", "code graph", "dependency chain", "ftm-map".
|