feed-the-machine 1.3.0 → 1.4.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/ftm-audit/SKILL.md +383 -57
- package/ftm-brainstorm/SKILL.md +119 -51
- package/ftm-config/SKILL.md +1 -1
- package/ftm-council/SKILL.md +259 -31
- package/ftm-dashboard/SKILL.md +10 -10
- package/ftm-debug/SKILL.md +861 -54
- package/ftm-diagram/SKILL.md +1 -1
- package/ftm-executor/SKILL.md +6 -6
- package/ftm-git/SKILL.md +208 -22
- package/ftm-inbox/bin/start.sh +1 -1
- package/ftm-inbox/bin/status.sh +1 -1
- package/ftm-inbox/bin/stop.sh +1 -1
- package/ftm-intent/SKILL.md +0 -1
- package/ftm-map/SKILL.md +46 -14
- package/ftm-map/scripts/db.py +439 -118
- package/ftm-map/scripts/index.py +128 -54
- package/ftm-map/scripts/parser.py +89 -320
- package/ftm-map/scripts/queries/go-tags.scm +20 -0
- package/ftm-map/scripts/queries/javascript-tags.scm +19 -7
- package/ftm-map/scripts/queries/python-tags.scm +22 -8
- package/ftm-map/scripts/queries/ruby-tags.scm +19 -0
- package/ftm-map/scripts/queries/rust-tags.scm +37 -0
- package/ftm-map/scripts/queries/typescript-tags.scm +20 -8
- package/ftm-map/scripts/query.py +176 -24
- package/ftm-map/scripts/ranker.py +377 -0
- package/ftm-map/scripts/requirements.txt +3 -0
- package/ftm-map/scripts/setup.sh +11 -0
- package/ftm-map/scripts/test_db.py +355 -115
- package/ftm-map/scripts/test_parser.py +169 -101
- package/ftm-map/scripts/test_query.py +178 -61
- package/ftm-map/scripts/test_ranker.py +199 -0
- package/ftm-map/scripts/views.py +107 -61
- package/ftm-mind/SKILL.md +861 -11
- package/ftm-mind/references/event-registry.md +20 -0
- package/ftm-pause/SKILL.md +256 -37
- package/ftm-resume/SKILL.md +380 -75
- package/ftm-retro/SKILL.md +164 -27
- package/ftm-upgrade/SKILL.md +4 -4
- package/hooks/ftm-blackboard-enforcer.sh +2 -4
- package/install.sh +6 -1
- package/package.json +1 -1
- package/ftm-map/scripts/tests/fixtures/__init__.py +0 -0
- package/ftm-map/scripts/tests/fixtures/sample_project/api.ts +0 -16
- package/ftm-map/scripts/tests/fixtures/sample_project/auth.py +0 -15
- package/ftm-map/scripts/tests/fixtures/sample_project/utils.js +0 -16
package/ftm-diagram/SKILL.md
CHANGED
|
@@ -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
|
package/ftm-executor/SKILL.md
CHANGED
|
@@ -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
|
|
27
|
-
2. Read
|
|
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
|
|
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
|
|
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
|
|
656
|
-
3. Update
|
|
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
|
@@ -38,7 +38,7 @@ Yesterday we pushed API keys to the repo. That's the kind of mistake that leads
|
|
|
38
38
|
|
|
39
39
|
## Phase -1: Install Git Hook (First Invocation Only)
|
|
40
40
|
|
|
41
|
-
The first time ftm-git runs in a repo, install a pre-commit hook as a hard safety net. This hook runs independently of Claude — it's a shell script that blocks `git commit` if staged files contain Tier 1 secret patterns. Even if
|
|
41
|
+
The first time ftm-git runs in a repo, install a pre-commit hook as a hard safety net. This hook runs independently of Claude — it's a shell script that blocks `git commit` if staged files contain Tier 1 secret patterns. Even if Claude forgets to invoke this skill, or someone runs git directly from the terminal, the hook catches it.
|
|
42
42
|
|
|
43
43
|
**Check if the hook is already installed:**
|
|
44
44
|
|
|
@@ -87,30 +87,104 @@ Before scanning, figure out what needs scanning and why you were invoked.
|
|
|
87
87
|
|
|
88
88
|
Scan the in-scope files using regex patterns. The goal is zero false negatives — a few false positives are acceptable and will be filtered in Phase 2.
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
### Tier 1: High-Confidence Patterns (almost certainly real secrets)
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
These patterns have distinctive prefixes or structures that make false positives rare:
|
|
93
93
|
|
|
94
94
|
```
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
# AWS
|
|
96
|
+
AKIA[0-9A-Z]{16} # AWS Access Key ID
|
|
97
|
+
amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} # AWS MWS
|
|
98
|
+
|
|
99
|
+
# GitHub
|
|
100
|
+
ghp_[A-Za-z0-9_]{36} # GitHub PAT (classic)
|
|
101
|
+
gho_[A-Za-z0-9_]{36} # GitHub OAuth
|
|
102
|
+
ghu_[A-Za-z0-9_]{36} # GitHub user token
|
|
103
|
+
ghs_[A-Za-z0-9_]{36} # GitHub server token
|
|
104
|
+
github_pat_[A-Za-z0-9_]{82} # GitHub fine-grained PAT
|
|
105
|
+
|
|
106
|
+
# Slack
|
|
107
|
+
xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24} # Slack bot token
|
|
108
|
+
xoxp-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack user token
|
|
109
|
+
xoxa-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack app token
|
|
110
|
+
xoxr-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34} # Slack refresh token
|
|
111
|
+
|
|
112
|
+
# Google
|
|
113
|
+
AIza[0-9A-Za-z\-_]{35} # Google API key
|
|
114
|
+
|
|
115
|
+
# Stripe
|
|
116
|
+
sk_live_[0-9a-zA-Z]{24,} # Stripe secret key (live)
|
|
117
|
+
sk_test_[0-9a-zA-Z]{24,} # Stripe secret key (test)
|
|
118
|
+
rk_live_[0-9a-zA-Z]{24,} # Stripe restricted key
|
|
119
|
+
|
|
120
|
+
# Other services
|
|
121
|
+
SG\.[A-Za-z0-9\-_]{22}\.[A-Za-z0-9\-_]{43} # SendGrid
|
|
122
|
+
SK[0-9a-fA-F]{32} # Twilio
|
|
123
|
+
npm_[A-Za-z0-9]{36} # npm token
|
|
124
|
+
pypi-[A-Za-z0-9\-_]{100,} # PyPI token
|
|
125
|
+
glpat-[A-Za-z0-9\-_]{20,} # GitLab PAT
|
|
126
|
+
-----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY----- # Private keys
|
|
101
127
|
```
|
|
102
128
|
|
|
103
|
-
|
|
129
|
+
### Tier 2: Context-Dependent Patterns (need surrounding context to confirm)
|
|
130
|
+
|
|
131
|
+
These match common assignment patterns. Check that the value isn't a placeholder, empty string, or env var reference before flagging:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
# Generic key/secret assignments — flag if value looks real (not placeholder)
|
|
135
|
+
(api_key|apikey|api-key)\s*[:=]\s*["']?[A-Za-z0-9\-_]{16,}["']?
|
|
136
|
+
(secret|secret_key|client_secret)\s*[:=]\s*["']?[A-Za-z0-9\-_]{16,}["']?
|
|
137
|
+
(password|passwd|pwd)\s*[:=]\s*["']?[^\s"']{8,}["']?
|
|
138
|
+
(token|access_token|auth_token)\s*[:=]\s*["']?[A-Za-z0-9\-_.]{16,}["']?
|
|
139
|
+
(database_url|db_url|connection_string)\s*[:=]\s*["']?[^\s"']{20,}["']?
|
|
140
|
+
|
|
141
|
+
# Bearer tokens in code
|
|
142
|
+
bearer\s+[A-Za-z0-9\-._~+/]{20,}
|
|
143
|
+
|
|
144
|
+
# Webhook URLs with tokens
|
|
145
|
+
https://hooks\.slack\.com/services/T[A-Z0-9]{8,}/B[A-Z0-9]{8,}/[a-zA-Z0-9]{24}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### What to Ignore (false positive suppression)
|
|
149
|
+
|
|
150
|
+
Skip matches that are clearly not real secrets:
|
|
151
|
+
|
|
152
|
+
- Values that are `""`, `''`, `None`, `null`, `undefined`, `TODO`, `CHANGEME`, `your-key-here`, `xxx`, `placeholder`, `example`, `test`, `dummy`, `fake`, `sample`
|
|
153
|
+
- References to environment variables: `os.environ[`, `process.env.`, `ENV[`, `${`, `os.getenv(`
|
|
154
|
+
- Lines that are comments (`#`, `//`, `/*`, `--`)
|
|
155
|
+
- Files in `node_modules/`, `.git/`, `vendor/`, `__pycache__/`, `dist/`, `build/`
|
|
156
|
+
- Files that are themselves `.env.example`, `.env.sample`, `.env.template`
|
|
157
|
+
- Lock files (`package-lock.json`, `yarn.lock`, `Gemfile.lock`, `poetry.lock`)
|
|
158
|
+
- Test fixtures where the "secret" is obviously fake (e.g., `test_api_key = "sk_test_abc123"` in a test file — but still flag `sk_live_*` in test files, those are real)
|
|
159
|
+
|
|
160
|
+
### Running the Scan
|
|
161
|
+
|
|
162
|
+
Use the Grep tool to search in-scope files for each pattern. Run Tier 1 patterns in parallel since they're independent. For Tier 2, check surrounding context before confirming.
|
|
163
|
+
|
|
164
|
+
For each finding, record:
|
|
165
|
+
- **file**: absolute path
|
|
166
|
+
- **line**: line number
|
|
167
|
+
- **pattern**: which pattern matched
|
|
168
|
+
- **tier**: 1 or 2
|
|
169
|
+
- **value_preview**: first 8 chars + `...` + last 4 chars (never log the full secret)
|
|
170
|
+
- **context**: the surrounding code (with the secret value masked)
|
|
104
171
|
|
|
105
172
|
## Phase 2: Validate Findings
|
|
106
173
|
|
|
107
174
|
For each Tier 2 match, read the surrounding context (5 lines before and after) and determine:
|
|
108
175
|
|
|
109
|
-
1. **Is the value a real secret or a placeholder?** — Check against the ignore list
|
|
176
|
+
1. **Is the value a real secret or a placeholder?** — Check against the ignore list above.
|
|
110
177
|
2. **Is it already using an env var?** — If the code does `key = os.environ.get("API_KEY", "sk_live_abc...")`, the hardcoded value is a fallback default. Still a finding — fallback defaults with real secrets are dangerous.
|
|
111
178
|
3. **Is it in a file that should be gitignored?** — If the secret is in `.env` and `.env` is in `.gitignore`, it's fine. If `.env` is NOT in `.gitignore`, that's a separate finding.
|
|
112
179
|
|
|
113
|
-
After validation, produce a findings list sorted by severity
|
|
180
|
+
After validation, produce a findings list sorted by severity:
|
|
181
|
+
|
|
182
|
+
| Severity | Meaning |
|
|
183
|
+
|---|---|
|
|
184
|
+
| **CRITICAL** | Tier 1 match (high-confidence secret) in a tracked or staged file |
|
|
185
|
+
| **HIGH** | Tier 2 confirmed match in a tracked or staged file |
|
|
186
|
+
| **MEDIUM** | `.env` file not in `.gitignore`, or secret in a fallback default |
|
|
187
|
+
| **LOW** | Secret in a gitignored file but the gitignore rule might be fragile |
|
|
114
188
|
|
|
115
189
|
If zero findings after validation: emit `secrets_clear` and proceed. The commit/push is safe.
|
|
116
190
|
|
|
@@ -120,23 +194,135 @@ If any CRITICAL or HIGH findings: **STOP. The commit/push is BLOCKED.** Say this
|
|
|
120
194
|
ftm-git: BLOCKED — <N> secret(s) found. Commit/push halted. Attempting auto-remediation...
|
|
121
195
|
```
|
|
122
196
|
|
|
123
|
-
Then proceed to Phase 3. The commit/push does NOT happen until Phase 3 completes and a re-scan comes back clean.
|
|
197
|
+
Then proceed to Phase 3 to fix. The commit/push does NOT happen until Phase 3 completes and a re-scan in Phase 3 Step 5 comes back clean. This is non-negotiable — even if you can fix the secrets, the user needs to see that the operation was blocked and why.
|
|
124
198
|
|
|
125
199
|
## Phase 3: Auto-Remediate
|
|
126
200
|
|
|
127
|
-
|
|
201
|
+
For each finding, apply the appropriate fix automatically. The goal is to make the code safe without breaking functionality.
|
|
202
|
+
|
|
203
|
+
### Step 1: Ensure .env infrastructure exists
|
|
128
204
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
205
|
+
Check for a `.env` file in the project root. If it doesn't exist, create one with a header comment:
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
# Environment variables — DO NOT COMMIT THIS FILE
|
|
209
|
+
# Copy .env.example for the template, fill in real values locally
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Check `.gitignore` for `.env` coverage. If missing, add:
|
|
213
|
+
```
|
|
214
|
+
# Environment files with secrets
|
|
215
|
+
.env
|
|
216
|
+
.env.local
|
|
217
|
+
.env.production
|
|
218
|
+
.env.staging
|
|
219
|
+
.env.*.local
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Step 2: Extract secrets to .env
|
|
223
|
+
|
|
224
|
+
For each finding:
|
|
225
|
+
|
|
226
|
+
1. **Choose an env var name** — derive it from the context. If the code says `STRIPE_API_KEY = "sk_live_..."`, the env var is `STRIPE_API_KEY`. If it says `api_key: "AIza..."`, infer from the file/service context (e.g., `GOOGLE_API_KEY`). Use SCREAMING_SNAKE_CASE.
|
|
227
|
+
|
|
228
|
+
2. **Add to .env** — append `VAR_NAME=<actual-secret-value>` to `.env`. If the var already exists, don't duplicate it.
|
|
229
|
+
|
|
230
|
+
3. **Add to .env.example** — create or update `.env.example` with `VAR_NAME=your-value-here` so other developers know the variable exists without seeing the real value.
|
|
231
|
+
|
|
232
|
+
### Step 3: Refactor source files
|
|
233
|
+
|
|
234
|
+
Replace the hardcoded secret with an env var reference. Match the language/framework:
|
|
235
|
+
|
|
236
|
+
| Language | Pattern |
|
|
237
|
+
|---|---|
|
|
238
|
+
| Python | `os.environ["VAR_NAME"]` or `os.getenv("VAR_NAME")` (match existing style in file) |
|
|
239
|
+
| JavaScript/TypeScript | `process.env.VAR_NAME` |
|
|
240
|
+
| Ruby | `ENV["VAR_NAME"]` or `ENV.fetch("VAR_NAME")` |
|
|
241
|
+
| Go | `os.Getenv("VAR_NAME")` |
|
|
242
|
+
| Java | `System.getenv("VAR_NAME")` |
|
|
243
|
+
| Shell/Bash | `$VAR_NAME` or `${VAR_NAME}` |
|
|
244
|
+
| YAML/JSON config | `${VAR_NAME}` (if the framework supports interpolation) or add a comment pointing to the env var |
|
|
245
|
+
|
|
246
|
+
If the file doesn't already import the env-reading module (e.g., `import os` in Python, `require('dotenv').config()` in Node), add the import. Check if the project uses `python-dotenv`, `dotenv` (Node), or similar — if so, use the project's existing pattern for loading env vars.
|
|
247
|
+
|
|
248
|
+
### Step 4: Unstage remediated files
|
|
249
|
+
|
|
250
|
+
After refactoring, make sure the `.env` file (with real secrets) is NOT staged:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
git reset HEAD .env 2>/dev/null # unstage if accidentally staged
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Stage the refactored source files (which now reference env vars instead of hardcoded secrets):
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
git add <refactored-files>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Step 5: Verify the fix
|
|
263
|
+
|
|
264
|
+
Re-run Phase 1 scan on the refactored files to confirm the secrets are gone. If any remain, loop back and fix. Do not proceed until the scan is clean.
|
|
136
265
|
|
|
137
266
|
## Phase 4: Report
|
|
138
267
|
|
|
139
|
-
After remediation or
|
|
268
|
+
After remediation (or if the scan was clean from the start), produce a summary:
|
|
269
|
+
|
|
270
|
+
**Clean scan:**
|
|
271
|
+
```
|
|
272
|
+
ftm-git: Clean scan. 0 secrets found in <N> files scanned. Safe to commit.
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**After remediation:**
|
|
276
|
+
```
|
|
277
|
+
ftm-git: Found <N> hardcoded secrets. Auto-remediated:
|
|
278
|
+
|
|
279
|
+
CRITICAL: sk_live_**** in src/payments.py:42 -> STRIPE_SECRET_KEY
|
|
280
|
+
HIGH: AIza**** in config/google.ts:18 -> GOOGLE_API_KEY
|
|
281
|
+
MEDIUM: .env was not in .gitignore -> added
|
|
282
|
+
|
|
283
|
+
Actions taken:
|
|
284
|
+
- Extracted <N> secrets to .env (gitignored)
|
|
285
|
+
- Created/updated .env.example with placeholder vars
|
|
286
|
+
- Refactored <N> source files to use env var references
|
|
287
|
+
- Updated .gitignore
|
|
288
|
+
|
|
289
|
+
Verify the app still works with the new env var setup, then commit.
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Blocked (auto-fix not possible):**
|
|
293
|
+
|
|
294
|
+
Some secrets can't be auto-fixed — for example, a private key embedded in a binary file, or a secret in a format the skill can't safely refactor. In these cases:
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
ftm-git: BLOCKED. Found secrets that require manual remediation:
|
|
298
|
+
|
|
299
|
+
CRITICAL: Private key in assets/cert.pem:1
|
|
300
|
+
-> Move this file outside the repo and reference via path env var
|
|
301
|
+
|
|
302
|
+
Action required: Fix the above manually, then run ftm-git again.
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Phase 5: Git History Check (Manual Invocation Only)
|
|
306
|
+
|
|
307
|
+
When explicitly asked to do a deep scan (e.g., "scan the repo history for secrets"), also check past commits. This is expensive so it only runs on explicit request, not as part of the pre-commit gate.
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
git log --all --diff-filter=A --name-only --pretty=format:"%H" -- "*.env" "*.pem" "*.key" "*credentials*" "*secret*"
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
For each historically added sensitive file, check if it's still in the current tree. If it was added and later removed, warn that the secret is still in git history and suggest:
|
|
314
|
+
|
|
315
|
+
1. Rotate the credential immediately (it's compromised)
|
|
316
|
+
2. Use `git filter-repo` or BFG Repo Cleaner to purge from history if needed
|
|
317
|
+
|
|
318
|
+
## Operating Principles
|
|
319
|
+
|
|
320
|
+
1. **Block first, fix second.** Never let a secret through while figuring out the fix. The commit waits.
|
|
321
|
+
2. **Zero false negatives over zero false positives.** It's better to flag something that turns out to be harmless than to miss a real key.
|
|
322
|
+
3. **Never log full secrets.** In all output, mask secret values. Show only enough to identify which secret it is (first 8 + last 4 chars).
|
|
323
|
+
4. **Env vars are the escape hatch.** The remediation pattern is always: secret goes to gitignored .env, code references the env var.
|
|
324
|
+
5. **Existing patterns win.** If the project already uses dotenv, Vault, AWS Secrets Manager, or any other secret management system, match that pattern rather than introducing a new one.
|
|
325
|
+
6. **Test files are not exempt.** A real `sk_live_*` key in a test file is just as dangerous as one in production code. Only `sk_test_*` with obviously fake values get a pass.
|
|
140
326
|
|
|
141
327
|
## Integration Points
|
|
142
328
|
|
package/ftm-inbox/bin/start.sh
CHANGED
package/ftm-inbox/bin/status.sh
CHANGED
|
@@ -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"
|
package/ftm-inbox/bin/stop.sh
CHANGED
package/ftm-intent/SKILL.md
CHANGED
|
@@ -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
|
package/ftm-map/SKILL.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ftm-map
|
|
3
|
-
description: Persistent code knowledge graph powered by tree-sitter and SQLite with FTS5 full-text search. Builds structural dependency graphs for blast radius
|
|
3
|
+
description: Persistent code knowledge graph powered by tree-sitter and SQLite with FTS5 full-text search. Uses a v2 hybrid architecture combining file-level PageRank with symbol-level blast radius analysis. Builds structural dependency graphs for blast radius, dependency chains, context selection, and keyword search. Use when user asks "what breaks if I change X", "blast radius", "what depends on", "where do we handle", "map codebase", "index project", "what calls", "dependency chain", "what's relevant for", "context for", "ftm-map".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# ftm-map
|
|
7
7
|
|
|
8
|
-
Persistent code knowledge graph powered by tree-sitter and SQLite with FTS5 full-text search. Parses the local codebase into a
|
|
8
|
+
Persistent code knowledge graph powered by tree-sitter and SQLite with FTS5 full-text search. Uses a v2 hybrid architecture: file-level PageRank (via fast-pagerank with scipy sparse matrices) for broad relevance ranking, combined with symbol-level blast radius for precise impact analysis. Parses the local codebase using Aider-style def/ref extraction with tags.scm into a 5-table schema (files, symbols, refs, file_edges, symbol_edges) stored in `.ftm-map/map.db`, then answers structural queries (blast radius, dependency chains, context selection, symbol lookup) and keyword searches without re-reading the source tree on every question.
|
|
9
9
|
|
|
10
10
|
## Events
|
|
11
11
|
|
|
12
12
|
### Emits
|
|
13
13
|
- `map_updated` — when the graph database has been updated (bootstrap or incremental)
|
|
14
|
-
- Payload: `{ project_path, symbols_count, edges_count, files_parsed, duration_ms, mode }`
|
|
14
|
+
- Payload: `{ project_path, symbols_count, edges_count, file_edges_count, reference_count, files_parsed, duration_ms, mode }`
|
|
15
15
|
- `task_completed` — when any ftm-map operation finishes
|
|
16
16
|
|
|
17
17
|
### Listens To
|
|
@@ -43,8 +43,9 @@ Bootstrap: "map this codebase" / "index this project" / no map.db exists yet
|
|
|
43
43
|
Incremental: Triggered by code_committed event or PostToolUse hook
|
|
44
44
|
Parses only changed files and updates their graph entries.
|
|
45
45
|
|
|
46
|
-
Query: Structural or
|
|
46
|
+
Query: Structural, keyword, or context question about existing graph
|
|
47
47
|
Detects query type and runs appropriate script.
|
|
48
|
+
Includes context selection for token-budgeted file retrieval.
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
If `.ftm-map/map.db` does not exist when a query arrives, fall back to offering bootstrap (see Graceful Degradation below).
|
|
@@ -98,15 +99,24 @@ Trigger: user asks a structural or keyword question about the codebase.
|
|
|
98
99
|
| "find X in the codebase" | FTS5 keyword search | `--search "X"` |
|
|
99
100
|
| "tell me about function X" | symbol info | `--info X` |
|
|
100
101
|
| "show dependencies for X" | dependency chain | `--deps X` |
|
|
102
|
+
| "what's relevant for X" | context selection | `--context --seed-keywords X` |
|
|
103
|
+
| "context for X" | context selection | `--context --seed-keywords X` |
|
|
104
|
+
| "important files for X" | context selection | `--context --seed-files X` |
|
|
105
|
+
| "what should I look at for X" | context selection | `--context --seed-keywords X` |
|
|
106
|
+
| "show stats" / "how big is the index" | statistics | `--stats` |
|
|
101
107
|
|
|
102
108
|
### Execution
|
|
103
109
|
|
|
104
110
|
Run the appropriate query script with the venv python:
|
|
105
111
|
```
|
|
106
|
-
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --blast-radius <symbol>
|
|
107
|
-
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --deps <symbol>
|
|
108
|
-
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --search "<keywords>"
|
|
109
|
-
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --info <symbol>
|
|
112
|
+
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --blast-radius <symbol> --project-root .
|
|
113
|
+
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --deps <symbol> --project-root .
|
|
114
|
+
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --search "<keywords>" --project-root .
|
|
115
|
+
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --info <symbol> --project-root .
|
|
116
|
+
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --context --seed-files src/auth.py --token-budget 4000 --project-root .
|
|
117
|
+
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --context --seed-keywords authenticate --project-root .
|
|
118
|
+
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --context --seed-symbols handleAuth --token-budget 8000 --project-root .
|
|
119
|
+
ftm-map/scripts/.venv/bin/python3 ftm-map/scripts/query.py --stats --project-root .
|
|
110
120
|
```
|
|
111
121
|
|
|
112
122
|
### Output Formatting
|
|
@@ -151,9 +161,29 @@ Symbol: authenticateUser
|
|
|
151
161
|
Signature: authenticateUser(token: string, opts?: AuthOptions) → Promise<Session>
|
|
152
162
|
Callers: 3 direct, 5 transitive
|
|
153
163
|
Callees: validateToken, decodeJWT, createSession
|
|
164
|
+
References: 12 across codebase
|
|
154
165
|
Dependents: 8 symbols total
|
|
155
166
|
```
|
|
156
167
|
|
|
168
|
+
**Context selection** — PageRank-ranked files with token budget:
|
|
169
|
+
```
|
|
170
|
+
Context for "authenticate" (budget: 4000 tokens):
|
|
171
|
+
1. src/auth/index.ts score: 0.142 tokens: 850
|
|
172
|
+
2. src/handlers/auth.ts score: 0.098 tokens: 620
|
|
173
|
+
3. src/middleware/session.ts score: 0.076 tokens: 540
|
|
174
|
+
Total: 2010 / 4000 tokens
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Stats** — database overview:
|
|
178
|
+
```
|
|
179
|
+
Index statistics:
|
|
180
|
+
Files: 42
|
|
181
|
+
Symbols: 318
|
|
182
|
+
References: 1204
|
|
183
|
+
File edges: 86
|
|
184
|
+
Symbol edges: 542
|
|
185
|
+
```
|
|
186
|
+
|
|
157
187
|
## Graceful Degradation
|
|
158
188
|
|
|
159
189
|
If `.ftm-map/map.db` does not exist when a query is requested:
|
|
@@ -170,11 +200,12 @@ All heavy lifting is done by Python scripts in `ftm-map/scripts/`. The skill orc
|
|
|
170
200
|
| Script | Purpose |
|
|
171
201
|
|--------|---------|
|
|
172
202
|
| `setup.sh` | Creates virtualenv, installs tree-sitter and dependencies |
|
|
173
|
-
| `db.py` | SQLite schema,
|
|
174
|
-
| `parser.py` | tree-sitter
|
|
175
|
-
| `index.py` | Full bootstrap scan and incremental file indexing |
|
|
176
|
-
| `query.py` | Blast radius, dependency chain, FTS5
|
|
177
|
-
| `
|
|
203
|
+
| `db.py` | 5-table SQLite schema (files, symbols, refs, file_edges, symbol_edges), CRUD, graph traversal |
|
|
204
|
+
| `parser.py` | Aider-style def/ref extraction via tree-sitter tags.scm queries |
|
|
205
|
+
| `index.py` | Full bootstrap scan and incremental file indexing with Aider weight heuristics |
|
|
206
|
+
| `query.py` | Blast radius, dependency chain, FTS5 search, symbol info, context selection, stats |
|
|
207
|
+
| `ranker.py` | PageRank-based file ranking with fast-pagerank and scipy sparse matrices |
|
|
208
|
+
| `views.py` | INTENT.md and ARCHITECTURE.mmd generation from the 5-table graph |
|
|
178
209
|
|
|
179
210
|
Always use the venv python — never the system python — to ensure tree-sitter bindings are available:
|
|
180
211
|
```
|
|
@@ -215,6 +246,7 @@ After `map_updated` or session end:
|
|
|
215
246
|
- tool: `ftm-map/scripts/index.py` | required | bootstrap and incremental indexer
|
|
216
247
|
- tool: `ftm-map/scripts/query.py` | required | blast radius, dependency, and FTS5 search queries
|
|
217
248
|
- tool: `ftm-map/scripts/views.py` | required | INTENT.md and .mmd diagram generation from graph
|
|
249
|
+
- tool: `ftm-map/scripts/ranker.py` | required | PageRank file ranking with fast-pagerank and scipy
|
|
218
250
|
- tool: `git` | optional | changed file detection for incremental mode
|
|
219
251
|
- config: `~/.claude/ftm-config.yml` | optional | model profile and skills.ftm-map.enabled flag
|
|
220
252
|
|
|
@@ -255,5 +287,5 @@ After `map_updated` or session end:
|
|
|
255
287
|
### task_completed
|
|
256
288
|
- skill: string — "ftm-map"
|
|
257
289
|
- operation: string — "bootstrap" | "incremental" | "query"
|
|
258
|
-
- query_type: string | null — "blast-radius" | "deps" | "search" | "info" (for query mode)
|
|
290
|
+
- query_type: string | null — "blast-radius" | "deps" | "search" | "info" | "context" | "stats" (for query mode)
|
|
259
291
|
- duration_ms: number — total operation duration
|