feed-the-machine 1.7.0 → 1.7.2

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/README.md CHANGED
@@ -24,11 +24,23 @@ Think of it like this: regular AI is a blank whiteboard every time you walk into
24
24
 
25
25
  ## Install
26
26
 
27
+ **Everything** (26 skills + 15 hooks):
27
28
  ```bash
28
29
  npx feed-the-machine@latest
29
30
  ```
30
31
 
31
- That's it. Takes 30 seconds. Works with any existing Claude Code setup.
32
+ **Just the skills you want:**
33
+ ```bash
34
+ npx feed-the-machine --only ftm-council-chat,ftm-mind
35
+ ```
36
+ This always includes `ftm` (the router) and `ftm-config` as base dependencies.
37
+
38
+ **See what's available:**
39
+ ```bash
40
+ npx feed-the-machine --list
41
+ ```
42
+
43
+ Works with any existing Claude Code setup. After install, restart Claude Code or start a new session.
32
44
 
33
45
  ---
34
46
 
@@ -52,6 +64,20 @@ Describe something you're trying to figure out. It researches the web and GitHub
52
64
  ```
53
65
  Paste an error or describe weird behavior. It attacks the problem from multiple angles at once — way faster than stepping through it manually.
54
66
 
67
+ **Get a second opinion:**
68
+ ```
69
+ /ftm-council
70
+ ```
71
+ Not sure about an architecture choice or debugging approach? This sends the problem to Claude, GPT, and Gemini independently. They debate in rounds until at least two agree. Like calling three contractors instead of trusting the first quote.
72
+
73
+ **Open the chatroom:**
74
+ ```
75
+ /ftm-council-chat
76
+ ```
77
+ An AIM-styled browser chatroom where you, Claude, Codex, and Gemini all talk in real time. You're a full participant, not just watching.
78
+
79
+ <img src="docs/images/council-chat.png" alt="ftm-council-chat screenshot — AIM-styled chatroom with Claude, Codex, and Gemini debating Jira token types" width="800" />
80
+
55
81
  ---
56
82
 
57
83
  ## Before / After
@@ -98,26 +124,84 @@ Every task makes it smarter. Fix a bug? It remembers the root cause. Ship a feat
98
124
 
99
125
  ## What's Included
100
126
 
101
- FTM comes with 20+ specialized skills. You don't need to memorize them — just use `/ftm` and it picks the right ones automatically. But here's what's under the hood:
127
+ FTM ships with 26 skills and 15 automation hooks. You don't need to memorize them — just use `/ftm` and it picks the right ones automatically. But here's what's under the hood:
128
+
129
+ ### Core
130
+
131
+ | Skill | Plain English |
132
+ |-------|--------------|
133
+ | **ftm-mind** | The brain. Reads your input, pulls memory, sizes complexity, picks the right approach |
134
+ | **ftm-executor** | The hands. Takes the plan and does the work with parallel agents in isolated worktrees |
135
+ | **ftm-ops** | Your ops dashboard. Task management, capacity tracking, stakeholder comms, meeting intel, burnout detection |
136
+ | **ftm-config** | Settings. Choose which AI models handle planning vs execution vs review |
137
+
138
+ ### Thinking & Research
102
139
 
103
140
  | Skill | Plain English |
104
141
  |-------|--------------|
105
- | **ftm-mind** | The brain. Reads your input, pulls memory, picks the right approach, makes a plan |
106
- | **ftm-executor** | The hands. Takes the plan and actually does the work with parallel agents |
107
- | **ftm-debug** | Bug detective. Attacks problems from multiple angles simultaneously |
108
- | **ftm-brainstorm** | Thinking partner. Researches, challenges assumptions, surfaces options |
109
- | **ftm-council** | Second opinions. Asks Claude, GPT, and Gemini, then goes with the majority |
110
- | **ftm-browse** | Web automation. Opens browsers, fills forms, takes screenshots |
142
+ | **ftm-brainstorm** | Thinking partner. Researches the web and GitHub in parallel, challenges assumptions, surfaces options |
143
+ | **ftm-researcher** | Deep research engine. 7 specialized finder agents search in parallel, then adversarially review each other's findings |
144
+ | **ftm-council** | Second opinions. Sends the problem to Claude, GPT, and Gemini — goes with the majority |
145
+ | **ftm-council-chat** | AIM-styled chatroom. You, Claude, Codex, and Gemini all talking in a browser window |
146
+
147
+ ### Building & Debugging
148
+
149
+ | Skill | Plain English |
150
+ |-------|--------------|
151
+ | **ftm-debug** | Bug war room. Attacks problems from multiple angles simultaneously |
152
+ | **ftm-audit** | Wiring check. Makes sure all code is actually connected and working — static analysis + LLM audit |
153
+ | **ftm-codex-gate** | Code validation gate. Sends your code to GPT for adversarial review before it ships |
154
+ | **ftm-browse** | Web automation. Opens browsers, fills forms, takes screenshots, inspects pages |
111
155
  | **ftm-git** | Safety net. Scans for leaked passwords/keys before you push code |
112
- | **ftm-audit** | Quality check. Makes sure all the code is actually connected and working |
113
- | **ftm-map** | Codebase intelligence. Knows which files matter most and what breaks if you change something |
114
- | **ftm-intent** | Living docs. Keeps documentation updated automatically when code changes |
115
- | **ftm-diagram** | Architecture diagrams. Auto-generated and auto-updated |
116
- | **ftm-retro** | Self-improvement. Reviews its own work and learns from it |
117
- | **ftm-pause / resume** | Save and restore. Pick up exactly where you left off |
118
- | **ftm-config** | Settings. Choose which AI models to use for what |
156
+
157
+ ### Codebase Intelligence
158
+
159
+ | Skill | Plain English |
160
+ |-------|--------------|
161
+ | **ftm-map** | Code knowledge graph. Knows what depends on what and what breaks if you change something |
162
+ | **ftm-intent** | Living docs. Keeps function-level documentation updated automatically when code changes |
163
+ | **ftm-diagram** | Architecture diagrams. Auto-generated mermaid diagrams that stay current |
164
+
165
+ ### Quality & Learning
166
+
167
+ | Skill | Plain English |
168
+ |-------|--------------|
169
+ | **ftm-verify** | Post-execution verification. Two independent AI models audit the completed work, then auto-fix anything they find |
170
+ | **ftm-retro** | Self-assessment. Reviews its own execution and scores it across 5 dimensions |
171
+ | **ftm-capture** | Knowledge extraction. Turns what you just did into reusable routines and playbooks |
172
+ | **ftm-dashboard** | Analytics. Shows which skills you use, approval rates, and session stats |
173
+
174
+ ### Workflow & Session
175
+
176
+ | Skill | Plain English |
177
+ |-------|--------------|
178
+ | **ftm-routine** | Recurring workflows. Define multi-step routines in YAML and run them by name |
179
+ | **ftm-pause / resume** | Save and restore. Pick up exactly where you left off in a new conversation |
119
180
  | **ftm-upgrade** | Self-update. Stay current with one command |
120
181
 
182
+ ### Hooks (16 automations)
183
+
184
+ These run automatically in the background — no slash commands needed:
185
+
186
+ | Hook | What It Does |
187
+ |------|-------------|
188
+ | **event-logger** | Logs every tool use to a JSONL file with debouncing and 30-day rotation |
189
+ | **auto-log** | Detects when you report completing work and reminds Claude to log progress |
190
+ | **learning-capture** | Captures edits and task completions into a learning database for pattern recognition |
191
+ | **session-snapshot** | Saves the last 15 exchanges as a markdown snapshot when a session ends — crash recovery |
192
+ | **session-end** | Marks the current session as completed in the blackboard |
193
+ | **blackboard-enforcer** | Nudges Claude to record meaningful work to the blackboard before a session ends undocumented |
194
+ | **plan-gate** | Prevents code edits without a plan — soft-warns at first, blocks after 3+ edits |
195
+ | **task-loader** | Loads active tasks from your task database when a session starts |
196
+ | **pre-compaction** | Monitors token usage and flushes state to disk before context hits 75% capacity |
197
+ | **post-compaction** | After context compresses, reloads critical state from disk before responding |
198
+ | **map-autodetect** | Detects unmapped projects on first use and bootstraps the code knowledge graph |
199
+ | **post-commit-trigger** | After git commits, triggers code map sync and documentation updates |
200
+ | **pending-sync-check** | Surfaces out-of-session commits that need code map syncing |
201
+ | **discovery-reminder** | Reminds Claude to run a discovery interview for tasks involving external systems or migrations |
202
+ | **drafts-gate** | Blocks Slack and Gmail sends unless a draft file exists first — no accidental messages |
203
+ | **install-hooks** | Self-installer that registers all hooks into Claude Code's settings.json |
204
+
121
205
  ---
122
206
 
123
207
  ## The Secret Sauce
@@ -140,15 +224,33 @@ Three things make FTM different from "just using Claude Code":
140
224
  profile: balanced # quality | balanced | budget
141
225
 
142
226
  profiles:
143
- balanced:
227
+ quality:
144
228
  planning: opus # the deep thinker
145
- execution: sonnet # the fast worker
229
+ execution: opus # thorough but slower
146
230
  review: sonnet # the checker
231
+
232
+ balanced:
233
+ planning: opus
234
+ execution: sonnet # fast worker
235
+ review: sonnet
236
+
237
+ budget:
238
+ planning: sonnet
239
+ execution: sonnet
240
+ review: haiku # cheapest
241
+ ```
242
+
243
+ **Execution settings:**
244
+
245
+ ```yaml
246
+ execution:
247
+ agent_mode: bypassPermissions # how much autonomy agents get
248
+ max_parallel_agents: 5 # simultaneous agent workers
147
249
  ```
148
250
 
149
251
  **Optional extras** for the full experience:
150
252
 
151
- - [Codex CLI](https://github.com/openai/codex) — Powers the second-opinion council and code validation
253
+ - [Codex CLI](https://github.com/openai/codex) — Powers the council, code validation, and adversarial review
152
254
  - [Gemini CLI](https://github.com/google/gemini-cli) — Third voice in the council
153
255
  - Playwright MCP server (`npx @playwright/mcp@latest`) — Powers browser automation
154
256
 
@@ -158,13 +260,29 @@ Everything else runs on Claude Code alone.
158
260
 
159
261
  ```bash
160
262
  git clone https://github.com/kkudumu/feed-the-machine.git ~/feed-the-machine
161
- cd ~/feed-the-machine && ./install.sh
263
+ cd ~/feed-the-machine && ./install.sh # everything
264
+ # or: ./install.sh --only ftm-council-chat # just one skill
265
+ # or: ./install.sh --list # see what's available
162
266
  ```
163
267
 
164
268
  **Uninstall:** `./uninstall.sh` (removes skills, keeps your memory data)
165
269
 
166
270
  ---
167
271
 
272
+ ## Current Version
273
+
274
+ **v1.7.0** (April 2026) — [Full changelog](CHANGELOG.md)
275
+
276
+ Recent highlights:
277
+ - **ftm-ops**: Personal operations intelligence (task management, capacity tracking, burnout detection, stakeholder comms)
278
+ - **ftm-mind slimmed from 87KB to 10KB** via progressive disclosure
279
+ - **ftm-verify**: Dual-model adversarial verification (Codex + Gemini audit your completed work independently)
280
+ - **ftm-council-chat**: AIM-styled browser chatroom for multi-AI conversations
281
+ - **15 automation hooks** for event logging, learning capture, secret scanning, and session management
282
+ - **Configurable agent permissions** and parallel agent limits
283
+
284
+ ---
285
+
168
286
  ## License
169
287
 
170
288
  MIT
package/bin/install.mjs CHANGED
@@ -7,9 +7,12 @@
7
7
  * Safe to re-run — idempotent.
8
8
  *
9
9
  * Flags:
10
- * --with-inbox Also install the inbox service
11
- * --no-hooks Skip hooks entirely
12
- * --skip-merge Install hook files but don't touch settings.json
10
+ * --only skill1,skill2 Install specific skills (always includes ftm + ftm-config)
11
+ * --list List available skills with descriptions
12
+ * --with-inbox Also install the inbox service
13
+ * --no-hooks Skip hooks entirely
14
+ * --with-hooks Include hooks even with --only
15
+ * --skip-merge Install hook files but don't touch settings.json
13
16
  */
14
17
 
15
18
  import { existsSync, mkdirSync, readdirSync, lstatSync, readFileSync, writeFileSync, copyFileSync, symlinkSync, unlinkSync, chmodSync, cpSync } from "fs";
@@ -31,8 +34,25 @@ const INBOX_INSTALL_DIR = join(HOME, ".claude", "ftm-inbox");
31
34
 
32
35
  const ARGS = process.argv.slice(2);
33
36
  const WITH_INBOX = ARGS.includes("--with-inbox");
34
- const NO_HOOKS = ARGS.includes("--no-hooks");
35
37
  const SKIP_MERGE = ARGS.includes("--skip-merge");
38
+ const LIST_MODE = ARGS.includes("--list");
39
+ const WITH_HOOKS_FLAG = ARGS.includes("--with-hooks");
40
+
41
+ // Parse --only (supports --only=x,y and --only x,y)
42
+ const ONLY_RAW = ARGS.find(a => a.startsWith("--only="))?.split("=")[1]
43
+ || (ARGS.includes("--only") ? ARGS[ARGS.indexOf("--only") + 1] : null);
44
+
45
+ const ONLY_SKILLS = ONLY_RAW
46
+ ? new Set(["ftm", "ftm-config", ...ONLY_RAW.split(",").map(s => s.trim())])
47
+ : null;
48
+
49
+ // When --only is used, skip hooks unless --with-hooks or explicit --no-hooks
50
+ const NO_HOOKS = ARGS.includes("--no-hooks") || (ONLY_SKILLS && !WITH_HOOKS_FLAG);
51
+
52
+ function skillWanted(name) {
53
+ if (!ONLY_SKILLS) return true;
54
+ return ONLY_SKILLS.has(name);
55
+ }
36
56
 
37
57
  let warnCount = 0;
38
58
 
@@ -268,29 +288,58 @@ function verify(skillCount, hookCount) {
268
288
  return { errors };
269
289
  }
270
290
 
291
+ // --- List Mode ---
292
+
293
+ function listSkills() {
294
+ console.log("\nAvailable FTM skills:\n");
295
+ const ymlFiles = readdirSync(REPO_DIR).filter(
296
+ (f) => f.startsWith("ftm-") && f.endsWith(".yml") && !f.includes("config.default")
297
+ );
298
+ for (const yml of ymlFiles) {
299
+ const name = yml.replace(".yml", "");
300
+ const content = readFileSync(join(REPO_DIR, yml), "utf8");
301
+ const descMatch = content.match(/^description:\s*(.+)/m);
302
+ const desc = descMatch ? descMatch[1].slice(0, 80) : "";
303
+ console.log(` ${name.padEnd(22)} ${desc}`);
304
+ }
305
+ console.log("\nInstall specific skills: npx feed-the-machine --only ftm-council-chat,ftm-mind");
306
+ console.log("Install everything: npx feed-the-machine\n");
307
+ process.exit(0);
308
+ }
309
+
271
310
  // --- Main ---
272
311
 
273
312
  function main() {
313
+ if (LIST_MODE) {
314
+ listSkills();
315
+ }
316
+
274
317
  preflight();
275
318
 
276
- console.log(`Installing ftm skills from: ${REPO_DIR}`);
319
+ if (ONLY_SKILLS) {
320
+ const requested = [...ONLY_SKILLS].filter(s => s !== "ftm" && s !== "ftm-config").join(", ");
321
+ console.log(`Installing selected FTM skills: ${requested} (+ ftm, ftm-config)`);
322
+ } else {
323
+ console.log(`Installing all FTM skills from: ${REPO_DIR}`);
324
+ }
277
325
  console.log(`Linking into: ${SKILLS_DIR}`);
278
326
  console.log("");
279
327
 
280
328
  ensureDir(SKILLS_DIR);
281
329
 
282
- // Link all ftm*.yml files
330
+ // Link ftm*.yml files (filtered by --only if set)
283
331
  const ymlFiles = readdirSync(REPO_DIR).filter(
284
332
  (f) => f.startsWith("ftm") && f.endsWith(".yml") && !f.includes("config.default")
285
- );
333
+ ).filter((f) => skillWanted(f.replace(".yml", "")));
286
334
  for (const yml of ymlFiles) {
287
335
  safeSymlink(join(REPO_DIR, yml), join(SKILLS_DIR, yml));
288
336
  }
289
337
 
290
- // Link all ftm* directories (skills with SKILL.md)
338
+ // Link ftm* directories (filtered by --only if set)
291
339
  const dirs = readdirSync(REPO_DIR).filter((f) => {
292
340
  if (!f.startsWith("ftm")) return false;
293
341
  if (f === "ftm-state") return false;
342
+ if (!skillWanted(f)) return false;
294
343
  const fullPath = join(REPO_DIR, f);
295
344
  try {
296
345
  return lstatSync(fullPath).isDirectory();
Binary file
@@ -194,6 +194,51 @@ Append a row to the root INTENT.md module map table:
194
194
 
195
195
  ---
196
196
 
197
+ ## Layer 1.75: Filesystem Path Resolution Check
198
+
199
+ **Purpose:** Verify that all file paths and CLI commands referenced in SKILL.md and reference files actually resolve from the installed skill location. This catches the most dangerous class of wiring bug — code that passes every static check but fails at runtime because a path doesn't exist where the skill expects it.
200
+
201
+ **When to run:** After Layer 1.5, before Layer 2. Runs on ANY project that has `.md` files with path references (not just code projects). This is especially critical after tasks that change path references, move files, or update directory structures.
202
+
203
+ **Scope:** Scan all `.md` files in the changed diff for path references.
204
+
205
+ **Check protocol:**
206
+
207
+ 1. **Find changed markdown files:** `git diff HEAD~1 --name-only -- '*.md' '*.yml'`
208
+ 2. **For each changed file, extract path references** matching these patterns:
209
+ - Absolute paths: `~/.claude/`, `/Users/`, `/home/`
210
+ - CLI commands: `python3 <path>`, `bash <path>`, `node <path>`
211
+ - Skill-relative paths: `bin/`, `references/`, `scripts/`
212
+ - Config-referenced paths: any path that appears after a `paths.` config key
213
+ 3. **Resolve each path from the installed location:**
214
+ - For skill files (in `~/.claude/skills/<name>/`): resolve relative to the skill's install directory
215
+ - For absolute paths (`~/.claude/...`): resolve directly
216
+ - For CLI commands: extract the path argument, resolve it, then verify execution with `--help` or `--version`
217
+ 4. **Verify existence:** `test -e <resolved-path>` for each
218
+ 5. **For CLI commands:** additionally verify `python3 <path> --help 2>&1` exits without "No such file or directory"
219
+
220
+ **Finding types:**
221
+
222
+ | Finding | Severity | Auto-fixable? |
223
+ |---------|----------|---------------|
224
+ | `BROKEN_PATH` — path in .md file doesn't resolve from installed location | HARD FAIL | No — requires human decision on correct path |
225
+ | `BROKEN_CLI` — CLI command path doesn't exist or errors on execution | HARD FAIL | No — requires fixing the path or creating a symlink |
226
+ | `HARDCODED_USER_PATH` — absolute path contains a specific username (e.g., `/Users/kioja.kudumu/`) | WARN | Yes — replace with `~/` or `$HOME/` equivalent |
227
+ | `STALE_PATH_REFERENCE` — path references a directory/file that was moved or renamed in this diff | HARD FAIL | Yes — update to new path |
228
+
229
+ **Output format:**
230
+ ```
231
+ Layer 1.75 findings:
232
+ - [BROKEN_PATH] ftm-ops/SKILL.md:53 — `~/.claude/skills/ftm/bin/brain.py` does not exist (ftm/ points to router subdirectory, not repo root)
233
+ - [BROKEN_CLI] ftm-mind/references/orient-protocol.md:210 — `python3 ~/.claude/skills/eng-buddy/bin/brain.py` → file not found
234
+ - [HARDCODED_USER_PATH] ftm-mind/references/ops-routing.md:44 — `/Users/kioja.kudumu/.claude/eng-buddy/drafts/` contains hardcoded username
235
+ - [STALE_PATH_REFERENCE] ftm-ops/references/task-management.md:15 — `~/.claude/eng-buddy/active-tasks.md` was moved to `~/.claude/ftm-ops/active-tasks.md`
236
+ ```
237
+
238
+ **Why this layer exists:** In the v1.7.0 merge, 160+ path references were updated across 23 files. Every static check passed. The integration test verified brain.py worked from the repo path. But when a user invoked `/ftm-ops`, it called `python3 ~/.claude/skills/ftm/bin/brain.py` — which didn't exist because the `ftm` skill symlink pointed to the `ftm/` subdirectory (the router), not the repo root. A 2-second `test -e` would have caught this. This layer ensures it always does.
239
+
240
+ ---
241
+
197
242
  ## Layer 2: LLM Adversarial Audit
198
243
 
199
244
  **Mindset:** You are an adversary trying to PROVE code is dead. Not "confirm it works" — PROVE it's dead. Every new/modified export is guilty until proven innocent. You must find a complete chain from app entry point to the code in question, or it's flagged.
@@ -508,7 +553,8 @@ When invoked (manually via `/ftm-audit` or automatically post-task):
508
553
  1. Run Phase 0 (detect project patterns — framework, router, state, API layer)
509
554
  2. Run Layer 1 (knip static analysis)
510
555
  3. Run Layer 1.5 (documentation coverage check — INTENT.md entries for changed functions)
511
- 4. Run Layer 2 (LLM adversarial audit, calibrated to detected patterns)
556
+ 4. Run Layer 1.75 (filesystem path resolution verify all referenced paths exist from installed location)
557
+ 5. Run Layer 2 (LLM adversarial audit, calibrated to detected patterns)
512
558
  5. Combine findings, deduplicate
513
559
  6. Run Layer 3 (auto-fix) for each finding (including missing INTENT.md entries)
514
560
  7. Re-verify (re-run Layers 1+1.5+2)
@@ -540,6 +586,10 @@ After completing, update the blackboard:
540
586
  - Findings: [N]
541
587
  - [list each finding — missing entries, stale entries, missing module docs]
542
588
 
589
+ ### Layer 1.75: Path Resolution
590
+ - Findings: [N]
591
+ - [list each finding — broken paths, broken CLI commands, hardcoded user paths]
592
+
543
593
  ### Layer 2: Adversarial Audit
544
594
  - Findings: [N]
545
595
  - [list each finding with file:line and evidence]
@@ -0,0 +1,2 @@
1
+ name: ftm-council-chat
2
+ description: AIM-styled browser chatroom where Claude, Codex, and Gemini hold real-time conversations with the user as a full participant. Use when user says "chatroom", "aim chat", "council chat", "live debate", "open the chatroom", or "council-chat".
@@ -19,7 +19,16 @@ Before running ftm-audit, verify these four checks for every task:
19
19
 
20
20
  **Graceful degradation**: If ftm-browse binary is not installed, skip visual checks with a note: "Visual smoke test skipped — ftm-browse not installed." Do not fail the task.
21
21
 
22
- A task is NOT marked complete until checks 1–4 pass (check 5 is optional).
22
+ 6. **Path resolution check** If the task changed any file/CLI path references in SKILL.md or reference files:
23
+ - Extract all paths matching patterns: `~/.claude/`, `bin/`, `python3 `, `bash `
24
+ - Resolve each path from the installed skill location (`~/.claude/skills/<skill-name>/`)
25
+ - Verify the target exists: `test -e <resolved-path>`
26
+ - For CLI commands (`python3 <path>`), verify execution: `python3 <path> --help` exits 0
27
+ - Flag any path that doesn't resolve as a BLOCKER — the skill will fail at runtime
28
+
29
+ **Why this exists**: In the v1.7.0 eng-buddy merge, all brain.py references were updated from `~/.claude/skills/eng-buddy/bin/brain.py` to `~/.claude/skills/ftm/bin/brain.py`. The path existed in the repo but `~/.claude/skills/ftm/` pointed to the `ftm/` subdirectory (the router skill), not the repo root — so the path didn't resolve at runtime. This was caught by a user, not by the audit. A 2-second `test -e` check would have caught it.
30
+
31
+ A task is NOT marked complete until checks 1–4 pass (checks 5-6 are conditional).
23
32
 
24
33
  **Failure handling:**
25
34
  - Test failures → agent must fix before task completes
@@ -108,3 +108,22 @@ If `experiences/index.json` has no usable matches:
108
108
  - continue normally
109
109
  - lean harder on current repo state and direct inspection
110
110
  - record the resulting experience aggressively after completion
111
+
112
+ ## Recording Code Patterns and API Gotchas
113
+
114
+ When writing an experience after task completion, actively check for these:
115
+
116
+ **code_patterns** — If during the task you wrote code that interacts with an API, library, or module, save the **final working version** (not the failed attempts). Include imports, setup, and the actual call. This is the copy-pasteable snippet future sessions will use.
117
+
118
+ **api_gotchas** — If you hit errors because an API behaved differently than expected (wrong return type, unexpected method name, None instead of Response, objects instead of dicts), record each one. Format: what the module is, what's surprising, and what you'd wrongly assume.
119
+
120
+ **playbook_ref** — If a brain.py playbook was created (via ftm-capture or auto-playbook trigger), record the path so the experience and playbook cross-reference each other.
121
+
122
+ **When to populate these fields:**
123
+ - You hit 2+ errors on the same module before getting it right → record code_patterns + api_gotchas
124
+ - You used an API/library for the first time in this project → record code_patterns
125
+ - The auto-playbook hook fired → record all three fields
126
+
127
+ **When to skip:**
128
+ - Pure file editing, config changes, or git operations — no API interaction worth capturing
129
+ - The code pattern is already in an existing experience (check by module name before duplicating)
@@ -37,11 +37,7 @@ Going ahead unless you say otherwise.
37
37
 
38
38
  **Step 0: Discovery Interview (if applicable).** Before generating the plan, check whether a Discovery Interview is needed (see Orient reference). If the task involves external systems, stakeholder coordination, or unfamiliar code, run the interview FIRST.
39
39
 
40
- **Step 1: Generate the plan.** Build a numbered list of concrete steps. Each step has:
41
- - A number
42
- - A one-line description
43
- - The files that will be touched
44
- - The verification method
40
+ **Step 1: Generate the plan.** Build a numbered checkbox list. This format is **mandatory** — no narrative steps, no prose paragraphs. Every plan MUST use: `N. [ ] One-line action → target`. See `references/protocols/PLAN-APPROVAL.md` for the full format spec, examples for code/ops/comms/infra tasks, and the list of NEVER-produce anti-patterns.
45
41
 
46
42
  **Step 2: Parse the user's response.**
47
43
 
@@ -10,11 +10,30 @@ For **medium and large** tasks, present a numbered task list and wait for the us
10
10
 
11
11
  **Step 1: Generate the plan.**
12
12
 
13
- Build a numbered list of concrete steps based on Orient synthesis. Each step must have:
13
+ Build a numbered checkbox list. This format is **mandatory** — no narrative steps, no prose paragraphs, no bullet-point summaries. Every plan, whether it's code, ops, comms, or infrastructure, MUST use this exact format:
14
+
15
+ ```
16
+ N. [ ] One-line action → target (file, channel, system, or person)
17
+ ```
18
+
19
+ Each step must have:
14
20
  - A number
21
+ - A `[ ]` checkbox (literal characters, not rendered)
15
22
  - A one-line description of what will be done
16
- - The files that will be touched
17
- - The verification method (test, lint, visual check, or "self-evident")
23
+ - An arrow `→` pointing to the target: file path for code, channel/email for comms, system name for infra, or "self-evident" for simple actions
24
+ - If applicable, a verification method after the target: `verify: test / lint / visual check / confirmation`
25
+
26
+ **This applies to ALL task types, not just code:**
27
+ - Code tasks: `3. [ ] Add OAuth validation → src/middleware/oauth.ts verify: npm test`
28
+ - Ops/comms tasks: `1. [ ] Reply to Everett requesting domain mapping → Braintrust support thread`
29
+ - Infra tasks: `2. [ ] Create Freshservice webhook trigger → freshservice admin / workflows`
30
+ - Admin tasks: `4. [ ] Close out ITWORK2-9772 → Jira`
31
+
32
+ **NEVER produce:**
33
+ - Narrative paragraphs describing steps
34
+ - Numbered steps without `[ ]` checkboxes
35
+ - Steps with sub-bullets explaining details (put that in execution, not the plan)
36
+ - Headers like "Step 1:" or "### Step 1" — use the numbered checkbox format only
18
37
 
19
38
  Present it like this:
20
39
 
@@ -36,6 +55,21 @@ Approve all? Or tell me what to change.
36
55
  - "deny" or "stop" → cancel entirely
37
56
  ```
38
57
 
58
+ **Ops example:**
59
+
60
+ ```
61
+ Here's my plan for the Braintrust post-SSO setup:
62
+
63
+ 1. [ ] Reply to Everett requesting domain mapping + group mappings → Braintrust support thread
64
+ 2. [ ] Reply to Spencer with admin process answer → #proj-braintrust
65
+ 3. [ ] Request API key from Everett or Spencer → Braintrust org settings
66
+ 4. [ ] Build Freshservice webhook → Braintrust API integration → freshservice workflows + Lambda
67
+ 5. [ ] Reconcile existing users vs Okta groups → Braintrust API + Okta
68
+ 6. [ ] Close out ITWORK2-9772 → Jira
69
+
70
+ Approve all? Or tell me what to change.
71
+ ```
72
+
39
73
  **Step 2: Parse the user's response.**
40
74
 
41
75
  | User says | Action |
@@ -73,6 +73,56 @@
73
73
  "items": {
74
74
  "type": "string"
75
75
  }
76
+ },
77
+ "code_patterns": {
78
+ "type": "array",
79
+ "description": "Working code snippets discovered during this task — the final correct version, not the failed attempts. Include imports, setup, and the actual call pattern.",
80
+ "items": {
81
+ "type": "object",
82
+ "required": ["module", "code"],
83
+ "additionalProperties": false,
84
+ "properties": {
85
+ "module": {
86
+ "type": "string",
87
+ "description": "The primary module/library this pattern uses (e.g. shared_services.okta.groups)"
88
+ },
89
+ "code": {
90
+ "type": "string",
91
+ "description": "The working code snippet — complete and copy-pasteable"
92
+ },
93
+ "description": {
94
+ "type": "string",
95
+ "description": "One-line description of what this pattern does"
96
+ }
97
+ }
98
+ }
99
+ },
100
+ "api_gotchas": {
101
+ "type": "array",
102
+ "description": "Surprising API behaviors, wrong assumptions, and type mismatches discovered during this task. Things that caused errors and would cause them again without this note.",
103
+ "items": {
104
+ "type": "object",
105
+ "required": ["module", "gotcha"],
106
+ "additionalProperties": false,
107
+ "properties": {
108
+ "module": {
109
+ "type": "string",
110
+ "description": "The module/API where the gotcha was discovered"
111
+ },
112
+ "gotcha": {
113
+ "type": "string",
114
+ "description": "What's surprising or wrong — e.g. 'list_members() returns OktaUser objects, not dicts'"
115
+ },
116
+ "wrong_assumption": {
117
+ "type": "string",
118
+ "description": "What you'd naturally assume that turns out to be wrong"
119
+ }
120
+ }
121
+ }
122
+ },
123
+ "playbook_ref": {
124
+ "type": "string",
125
+ "description": "Path to the brain.py playbook entry if one was created from this experience, for cross-referencing"
76
126
  }
77
127
  }
78
128
  }
@@ -100,6 +100,139 @@ except Exception:
100
100
  fi
101
101
  fi
102
102
 
103
+ # --- Auto-Playbook Trigger: detect repeated errors then success on same module ---
104
+ ERROR_TRACKER="$FTM_STATE/.error-tracker.jsonl"
105
+
106
+ if [ "$TOOL_NAME" = "Bash" ]; then
107
+ # Extract module/import and error status from the tool result
108
+ ANALYSIS=$(printf '%s' "$PAYLOAD" | python3 -c '
109
+ import json, sys
110
+ try:
111
+ d = json.load(sys.stdin)
112
+ output = d.get("tool_result", "") or ""
113
+ tool_input = d.get("tool_input", {})
114
+ command = tool_input.get("command", "") if isinstance(tool_input, dict) else ""
115
+ is_error = "Error:" in output or "Traceback" in output or "AttributeError" in output or "TypeError" in output or "ImportError" in output or "ModuleNotFoundError" in output
116
+ # Extract the primary module from import statements in the command
117
+ module = ""
118
+ for line in command.split("\n"):
119
+ line = line.strip()
120
+ if line.startswith("from ") and " import " in line:
121
+ module = line.split("from ")[1].split(" import")[0].strip()
122
+ break
123
+ elif line.startswith("import "):
124
+ module = line.split("import ")[1].split()[0].strip()
125
+ break
126
+ import json as j
127
+ j.dump({"is_error": is_error, "module": module}, sys.stdout)
128
+ except Exception:
129
+ import json as j
130
+ j.dump({"is_error": False, "module": ""}, sys.stdout)
131
+ ' 2>/dev/null)
132
+
133
+ IS_ERROR=$(printf '%s' "$ANALYSIS" | python3 -c 'import json,sys; print("1" if json.load(sys.stdin).get("is_error") else "0")' 2>/dev/null)
134
+ MODULE=$(printf '%s' "$ANALYSIS" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("module",""))' 2>/dev/null)
135
+
136
+ if [ -n "$MODULE" ]; then
137
+ TIMESTAMP=$(date +%s)
138
+ if [ "$IS_ERROR" = "1" ]; then
139
+ # Append error event
140
+ echo "{\"ts\":$TIMESTAMP,\"module\":\"$MODULE\",\"type\":\"error\"}" >> "$ERROR_TRACKER"
141
+ else
142
+ # Success — check if this module had 3+ recent errors
143
+ if [ -f "$ERROR_TRACKER" ]; then
144
+ ERROR_COUNT=$(python3 -c "
145
+ import json, sys
146
+ count = 0
147
+ cutoff = $TIMESTAMP - 600 # last 10 minutes
148
+ for line in open('$ERROR_TRACKER'):
149
+ line = line.strip()
150
+ if not line: continue
151
+ try:
152
+ ev = json.loads(line)
153
+ if ev.get('module') == '$MODULE' and ev.get('type') == 'error' and ev.get('ts', 0) >= cutoff:
154
+ count += 1
155
+ except: pass
156
+ print(count)
157
+ " 2>/dev/null)
158
+
159
+ if [ "$ERROR_COUNT" -ge 3 ]; then
160
+ # Write a blackboard experience with the module and error count
161
+ SLUG=$(echo "$MODULE" | tr '.' '-' | tr '[:upper:]' '[:lower:]')
162
+ DATE_STAMP=$(date +%Y-%m-%d)
163
+ EXP_FILE="$FTM_STATE/blackboard/experiences/${DATE_STAMP}_auto-playbook-${SLUG}.json"
164
+ python3 -c "
165
+ import json, os, datetime
166
+ exp = {
167
+ 'task_type': 'api-discovery',
168
+ 'task_description': 'Auto-captured: $ERROR_COUNT errors on $MODULE before finding the working pattern',
169
+ 'lessons_learned': [
170
+ '$MODULE required $ERROR_COUNT attempts to get right — working pattern saved as playbook',
171
+ 'Check experience code_patterns before using this module in future sessions'
172
+ ],
173
+ 'code_patterns': [],
174
+ 'api_gotchas': [],
175
+ 'playbook_ref': None,
176
+ 'timestamp': datetime.datetime.utcnow().isoformat() + 'Z',
177
+ 'confidence': 0.7,
178
+ 'tags': ['auto-playbook', '$(echo "$MODULE" | tr "." " ")']
179
+ }
180
+ os.makedirs(os.path.dirname('$EXP_FILE'), exist_ok=True)
181
+ with open('$EXP_FILE', 'w') as f:
182
+ json.dump(exp, f, indent=2)
183
+
184
+ # Update index
185
+ idx_path = '$FTM_STATE/blackboard/experiences/index.json'
186
+ try:
187
+ with open(idx_path) as f:
188
+ idx = json.load(f)
189
+ except:
190
+ idx = {'entries': [], 'metadata': {'total_count': 0, 'last_updated': None, 'max_entries': 200, 'pruning_strategy': 'remove_oldest_low_confidence'}}
191
+
192
+ idx['entries'].append({
193
+ 'id': 'auto-playbook-${SLUG}-${DATE_STAMP}',
194
+ 'file': os.path.basename('$EXP_FILE'),
195
+ 'task_type': 'api-discovery',
196
+ 'tags': ['auto-playbook', '$(echo "$MODULE" | tr "." " ")'],
197
+ 'timestamp': exp['timestamp'],
198
+ 'confidence': 0.7
199
+ })
200
+ idx['metadata']['total_count'] = len(idx['entries'])
201
+ idx['metadata']['last_updated'] = exp['timestamp']
202
+ with open(idx_path, 'w') as f:
203
+ json.dump(idx, f, indent=2)
204
+ " 2>/dev/null
205
+
206
+ echo ""
207
+ echo "[Auto-Playbook] Detected $ERROR_COUNT errors on '$MODULE' followed by a success in this session."
208
+ echo "A skeleton experience has been saved to the blackboard at: $EXP_FILE"
209
+ echo "NOW you MUST do two things:"
210
+ echo "1. Proactively invoke ftm-capture to save the working code pattern as a playbook"
211
+ echo "2. Update the experience file at $EXP_FILE — fill in the code_patterns array with the working snippet and api_gotchas with every wrong assumption you hit"
212
+ echo "Tell the user: \"That was rough — I'm saving the working pattern so next time it's one clean shot.\""
213
+ echo ""
214
+
215
+ # Clear tracked errors for this module so we don't re-trigger
216
+ python3 -c "
217
+ import json
218
+ lines = []
219
+ for line in open('$ERROR_TRACKER'):
220
+ line = line.strip()
221
+ if not line: continue
222
+ try:
223
+ ev = json.loads(line)
224
+ if ev.get('module') != '$MODULE':
225
+ lines.append(line)
226
+ except: pass
227
+ with open('$ERROR_TRACKER', 'w') as f:
228
+ f.write('\n'.join(lines) + '\n' if lines else '')
229
+ " 2>/dev/null
230
+ fi
231
+ fi
232
+ fi
233
+ fi
234
+ fi
235
+
103
236
  # --- Feed Playbook Tracer if active trace exists ---
104
237
  ACTIVE_TRACE_FILE="$FTM_STATE/.active-trace-id"
105
238
  if [ -f "$ACTIVE_TRACE_FILE" ] && [ -f "$BRAIN_PY" ]; then
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env bash
2
+ # ftm-task-loader.sh
3
+ # PostToolUse hook that fires after the Skill tool is invoked.
4
+ # When ftm-ops or ftm is the invoked skill, loads tasks from brain.py
5
+ # and injects TaskCreate instructions as additionalContext.
6
+ #
7
+ # This is deterministic — brain.py runs in the hook (fast), and the
8
+ # model receives pre-parsed TaskCreate calls it must execute.
9
+ #
10
+ # Hook: PostToolUse (matcher: Skill)
11
+
12
+ set -euo pipefail
13
+
14
+ INPUT=$(cat)
15
+
16
+ # Extract the tool name and check it's the Skill tool
17
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
18
+ if [[ "$TOOL_NAME" != "Skill" ]]; then
19
+ exit 0
20
+ fi
21
+
22
+ # Extract which skill was invoked from the tool input
23
+ SKILL_NAME=$(echo "$INPUT" | jq -r '.tool_input.skill // ""' 2>/dev/null)
24
+
25
+ # Only fire for ftm-ops, ftm, or ftm-mind (which routes to ftm-ops for task requests)
26
+ case "$SKILL_NAME" in
27
+ ftm-ops|ftm|ftm-mind|eng-buddy) ;;
28
+ *) exit 0 ;;
29
+ esac
30
+
31
+ # Find brain.py — check multiple locations
32
+ BRAIN_PY=""
33
+ for candidate in \
34
+ "$HOME/.claude/skills/ftm/bin/brain.py" \
35
+ "$HOME/.claude/skills/ftm-ops/../bin/brain.py" \
36
+ "$HOME/Documents/Code/feed-the-machine/bin/brain.py"; do
37
+ if [[ -f "$candidate" ]]; then
38
+ BRAIN_PY="$candidate"
39
+ break
40
+ fi
41
+ done
42
+
43
+ if [[ -z "$BRAIN_PY" ]]; then
44
+ # brain.py not found — skip silently
45
+ exit 0
46
+ fi
47
+
48
+ # Run brain.py and capture task JSON
49
+ TASKS_JSON=$(python3 "$BRAIN_PY" --tasks --task-json 2>/dev/null) || exit 0
50
+
51
+ # Count active tasks
52
+ TASK_COUNT=$(echo "$TASKS_JSON" | python3 -c "
53
+ import sys, json
54
+ try:
55
+ tasks = json.load(sys.stdin)
56
+ active = [t for t in tasks if t.get('status') in ('pending', 'in_progress')]
57
+ print(len(active))
58
+ except:
59
+ print(0)
60
+ " 2>/dev/null)
61
+
62
+ if [[ "$TASK_COUNT" == "0" ]]; then
63
+ exit 0
64
+ fi
65
+
66
+ # Generate TaskCreate instructions from the task data
67
+ TASK_INSTRUCTIONS=$(echo "$TASKS_JSON" | python3 -c "
68
+ import sys, json
69
+ try:
70
+ tasks = json.load(sys.stdin)
71
+ active = [t for t in tasks if t.get('status') in ('pending', 'in_progress')]
72
+ lines = []
73
+ for t in active:
74
+ tid = t.get('id', t.get('legacy_number', '?'))
75
+ title = t.get('title', 'Untitled')
76
+ status = t.get('status', 'pending')
77
+ priority = t.get('priority', 'medium')
78
+ jira = t.get('jira_key', '')
79
+ desc_parts = [status, priority]
80
+ if jira:
81
+ desc_parts.append(jira)
82
+ desc = ' | '.join(desc_parts)
83
+ lines.append(f'TaskCreate(subject: \"#{tid} {title}\", description: \"{desc}\")')
84
+ print('\n'.join(lines))
85
+ except:
86
+ pass
87
+ " 2>/dev/null)
88
+
89
+ if [[ -z "$TASK_INSTRUCTIONS" ]]; then
90
+ exit 0
91
+ fi
92
+
93
+ # Output as additionalContext — the model MUST process this
94
+ cat <<EOF
95
+ [ftm-task-loader] Loaded $TASK_COUNT active tasks from tasks.db. You MUST now call TaskCreate for each one so they appear in Claude Code's sidebar task list. Do NOT skip this — rendering a table is not enough. Call each one:
96
+
97
+ $TASK_INSTRUCTIONS
98
+
99
+ Call all of these TaskCreate invocations NOW, before doing anything else. This is non-negotiable.
100
+ EOF
@@ -55,6 +55,16 @@
55
55
  }
56
56
  ]
57
57
  },
58
+ {
59
+ "matcher": "Skill",
60
+ "hooks": [
61
+ {
62
+ "type": "command",
63
+ "command": "~/.claude/hooks/ftm-task-loader.sh",
64
+ "timeout": 10
65
+ }
66
+ ]
67
+ },
58
68
  {
59
69
  "matcher": "Bash|mcp__git__git_commit",
60
70
  "hooks": [
package/install.sh CHANGED
@@ -7,9 +7,13 @@ set -euo pipefail
7
7
  # Safe to re-run — idempotent.
8
8
  #
9
9
  # Usage:
10
- # ./install.sh # Full install (skills + hooks + settings merge)
11
- # ./install.sh --no-hooks # Skills and state only, skip hooks entirely
12
- # ./install.sh --skip-merge # Install hook files but don't touch settings.json
10
+ # ./install.sh # Full install (all skills + hooks)
11
+ # ./install.sh --only ftm-council-chat # Install specific skill(s)
12
+ # ./install.sh --only ftm-mind,ftm-debug # Install multiple specific skills
13
+ # ./install.sh --list # List available skills
14
+ # ./install.sh --no-hooks # Skills only, skip hooks
15
+ # ./install.sh --only ftm-mind --with-hooks # Specific skills + all hooks
16
+ # ./install.sh --skip-merge # Install hook files but don't touch settings.json
13
17
 
14
18
  REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
15
19
  SKILLS_DIR="$HOME/.claude/skills"
@@ -20,21 +24,85 @@ SETTINGS_FILE="$CONFIG_DIR/settings.json"
20
24
 
21
25
  NO_HOOKS=false
22
26
  SKIP_MERGE=false
23
- for arg in "$@"; do
27
+ WITH_HOOKS=false
28
+ LIST_MODE=false
29
+ ONLY_SKILLS=""
30
+
31
+ i=1
32
+ while [ "$i" -le "$#" ]; do
33
+ arg="${!i}"
24
34
  case "$arg" in
25
35
  --no-hooks) NO_HOOKS=true ;;
26
36
  --skip-merge) SKIP_MERGE=true ;;
37
+ --with-hooks) WITH_HOOKS=true ;;
38
+ --list) LIST_MODE=true ;;
39
+ --only=*) ONLY_SKILLS="${arg#--only=}" ;;
40
+ --only)
41
+ i=$((i + 1))
42
+ ONLY_SKILLS="${!i}"
43
+ ;;
27
44
  # Keep --setup-hooks for backwards compat (now a no-op since merge is default)
28
45
  --setup-hooks) ;;
29
46
  esac
47
+ i=$((i + 1))
30
48
  done
31
49
 
50
+ # When --only is used, skip hooks by default unless --with-hooks
51
+ if [ -n "$ONLY_SKILLS" ] && [ "$WITH_HOOKS" != true ]; then
52
+ NO_HOOKS=true
53
+ fi
54
+
32
55
  WARN_COUNT=0
33
56
  warn() {
34
57
  echo " WARN: $1"
35
58
  WARN_COUNT=$((WARN_COUNT + 1))
36
59
  }
37
60
 
61
+ # --- List Mode ---
62
+
63
+ if [ "$LIST_MODE" = true ]; then
64
+ echo ""
65
+ echo "Available FTM skills:"
66
+ echo ""
67
+ for yml in "$REPO_DIR"/ftm-*.yml; do
68
+ [ -f "$yml" ] || continue
69
+ name=$(basename "$yml" .yml)
70
+ [[ "$name" == *".default"* ]] && continue
71
+ desc=$(grep '^description:' "$yml" | head -1 | sed 's/^description: *//' | cut -c1-80)
72
+ printf " %-22s %s\n" "$name" "$desc"
73
+ done
74
+ echo ""
75
+ echo "Install specific skills: ./install.sh --only ftm-council-chat,ftm-mind"
76
+ echo "Install everything: ./install.sh"
77
+ exit 0
78
+ fi
79
+
80
+ # --- Skill Filter ---
81
+
82
+ declare -a SKILL_FILTER=()
83
+ if [ -n "$ONLY_SKILLS" ]; then
84
+ # Always include base dependencies
85
+ SKILL_FILTER+=("ftm" "ftm-config")
86
+ IFS=',' read -ra REQUESTED <<< "$ONLY_SKILLS"
87
+ for s in "${REQUESTED[@]}"; do
88
+ s=$(echo "$s" | xargs) # trim whitespace
89
+ SKILL_FILTER+=("$s")
90
+ done
91
+ fi
92
+
93
+ skill_wanted() {
94
+ local name="$1"
95
+ if [ ${#SKILL_FILTER[@]} -eq 0 ]; then
96
+ return 0 # no filter = install all
97
+ fi
98
+ for wanted in "${SKILL_FILTER[@]}"; do
99
+ if [ "$name" = "$wanted" ]; then
100
+ return 0
101
+ fi
102
+ done
103
+ return 1
104
+ }
105
+
38
106
  # --- Preflight Checks ---
39
107
 
40
108
  echo "Preflight checks..."
@@ -76,7 +144,11 @@ else
76
144
  fi
77
145
 
78
146
  echo ""
79
- echo "Installing FTM skills from: $REPO_DIR"
147
+ if [ -n "$ONLY_SKILLS" ]; then
148
+ echo "Installing selected FTM skills: $ONLY_SKILLS (+ ftm, ftm-config)"
149
+ else
150
+ echo "Installing all FTM skills from: $REPO_DIR"
151
+ fi
80
152
  echo "Linking into: $SKILLS_DIR"
81
153
  echo ""
82
154
 
@@ -84,12 +156,13 @@ mkdir -p "$SKILLS_DIR"
84
156
 
85
157
  # --- Skills ---
86
158
 
87
- # Link all ftm*.yml files
159
+ # Link ftm*.yml files (filtered by --only if set)
88
160
  for yml in "$REPO_DIR"/ftm*.yml; do
89
161
  [ -f "$yml" ] || continue
90
162
  name=$(basename "$yml")
91
163
  # Skip ftm-config.default.yml — it's a template, not a skill
92
164
  [[ "$name" == *".default."* ]] && continue
165
+ skill_wanted "${name%.yml}" || continue
93
166
  target="$SKILLS_DIR/$name"
94
167
  if [ -L "$target" ]; then
95
168
  rm "$target"
@@ -101,11 +174,12 @@ for yml in "$REPO_DIR"/ftm*.yml; do
101
174
  echo " LINK $name"
102
175
  done
103
176
 
104
- # Link all ftm* directories (skills with SKILL.md)
177
+ # Link ftm* directories (filtered by --only if set)
105
178
  for dir in "$REPO_DIR"/ftm*/; do
106
179
  [ -d "$dir" ] || continue
107
180
  name=$(basename "$dir")
108
181
  [ "$name" = "ftm-state" ] && continue # state is handled separately
182
+ skill_wanted "$name" || continue
109
183
  target="$SKILLS_DIR/$name"
110
184
  if [ -L "$target" ]; then
111
185
  rm "$target"
@@ -121,6 +195,8 @@ SKILL_COUNT=0
121
195
  for _f in "$REPO_DIR"/ftm*.yml; do
122
196
  [ -e "$_f" ] || continue
123
197
  case "$_f" in *.default.*) continue ;; esac
198
+ _name=$(basename "$_f" .yml)
199
+ skill_wanted "$_name" || continue
124
200
  SKILL_COUNT=$((SKILL_COUNT + 1))
125
201
  done
126
202
  echo ""
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "feed-the-machine",
3
- "version": "1.7.0",
4
- "description": "A unified intelligence layer for Claude Code — 22 skills with OODA-based reasoning, persistent memory, multi-model deliberation, and optional operator cockpit inbox",
3
+ "version": "1.7.2",
4
+ "description": "A brain upgrade for Claude Code — 26 skills that teach it how to think before acting, remember across conversations, debug like a war room, run plans on autopilot with agent teams, and get second opinions from GPT & Gemini. Plus 15 hooks that automate the boring stuff.",
5
5
  "license": "MIT",
6
6
  "author": "kkudumu",
7
7
  "type": "module",
@@ -26,6 +26,7 @@
26
26
  "bin/",
27
27
  "hooks/",
28
28
  "docs/HOOKS.md",
29
+ "docs/images/",
29
30
  "install.sh",
30
31
  "uninstall.sh",
31
32
  "ftm.yml",