puffer-feed
README.md
# puffer-feed
**One line connects everyone to [ThePuffer build tracker](https://puffer-tracker-lake.vercel.app).**
Hermes, Claude Code, Claude Desktop, a CI job, a git hook — anything — can push a
progress update to the tracker by *calling one tool* (or one shell command). No more
hand-editing `data.json`.
It's an **MCP server + a CLI + an opt-in hook**, all funneling through **one locked,
git-safe write-core** so concurrent writers can never clobber each other (the exact bug
that bit us when two agents wrote `data.json` at once).
```
puffer-feed/
├── src/write-core.mjs ← the ONLY thing that mutates data.json + pushes (lock + rebase + atomic write)
├── src/secrets.mjs ← rejects secrets / internal hosts before they hit the public page
├── mcp-server.mjs ← stdio MCP server (6 tools) — add one line to any MCP client
├── bin/puffer.mjs ← CLI for non-MCP callers (CI, cron, git hooks, shell)
├── hooks/puffer-autofeed.mjs ← opt-in Claude Code Stop hook (off by default)
└── test-core.mjs ← concurrency regression test (npm test)
```
> **Mental model: logging IS publishing.** A write goes live on the partner-facing site in
> ~15s. Keep it to **one entry per BIG step**, plain and non-technical — a business partner
> reads it, not engineers.
---
## Install (once)
```bash
git clone https://github.com/ElSalvatore-sys/puffer-feed.git ~/Developer/puffer-feed
cd ~/Developer/puffer-feed && npm install
```
It writes to a local clone of the tracker repo. Point it there with **`PUFFER_DIR`**
(defaults to `~/Developer/puffer-tracker`). You need push rights on that repo.
```bash
git clone https://github.com/ElSalvatore-sys/puffer-tracker.git ~/Developer/puffer-tracker
```
---
## Connect a client (the "one line")
### Claude Code — `~/.claude.json` (global `mcpServers`, or a project's `mcpServers`)
```json
"puffer": {
"command": "node",
"args": ["/Users/you/Developer/puffer-feed/mcp-server.mjs"],
"env": { "PUFFER_DIR": "/Users/you/Developer/puffer-tracker" }
}
```
### Claude Desktop — `~/Library/Application Support/Claude/claude_desktop_config.json` → `mcpServers`
*(same block; restart Claude Desktop to load)*
```json
"puffer": {
"command": "node",
"args": ["/Users/you/Developer/puffer-feed/mcp-server.mjs"],
"env": { "PUFFER_DIR": "/Users/you/Developer/puffer-tracker" }
}
```
### Hermes — `~/.hermes/config.yaml` under the existing `mcp_servers:`
```yaml
mcp_servers:
puffer:
command: node
args:
- /Users/you/Developer/puffer-feed/mcp-server.mjs
enabled: true
env:
PUFFER_DIR: /Users/you/Developer/puffer-tracker
```
*(Hermes has `mcp_reload_confirm: true` — it asks you to approve the reload once.)*
### CLI / CI / cron / git hooks (no MCP)
```bash
cd ~/Developer/puffer-feed && npm link # exposes `puffer` on PATH
PUFFER_DIR=~/Developer/puffer-tracker puffer log bloghead "Stripe payouts wired end to end"
```
Once connected, an agent just calls the tool: *"log a step on bloghead: payments are live."*
---
## Tools (MCP) / commands (CLI)
| Tool | What it does |
|------|--------------|
| `log_step` | Append one big-step entry. `project`, `title` required; `detail/status/mins/progress/push/redact` optional. **90% of usage.** |
| `get_recent` | Read-only. Last *n* entries — call before logging to avoid duplicates. |
| `list_projects` | Read-only. Keys, names, phases, % and step counts. |
| `set_project_field` | Set `phase` / `minor` / `roadmap` (structural — gated, see below). |
| `set_week_highlight` | Replace the "This week" banner (gated). |
| `add_audit_chapter` | Append a narrative chapter to a project's audit (gated). |
```bash
puffer log thebarapp "Digital menu module shipped" --detail "owner editor + public page" --mins 300 --status done --progress 86
puffer recent 10
puffer projects
puffer log dehgo "Drafted onboarding copy" --no-push # commit locally, push on next call
```
Structural tools (`set_project_field`, `set_week_highlight`, `add_audit_chapter`) are
**off unless `PUFFER_ALLOW_STRUCTURAL=1`** — keep them to the owner's machine; everyone
else just logs steps.
---
## Auto-feed hook (opt-in, off by default)
`hooks/puffer-autofeed.mjs` is a Claude Code **Stop** hook that does nothing unless
`PUFFER_AUTOFEED=1` **and** the session explicitly staged a step via a sentinel
(`.puffer-pending` JSON in the repo, or `PUFFER_LOG="project|title|detail"`). It never
fires per-turn. To enable, add to `~/.claude/settings.json`:
```json
{ "hooks": { "Stop": [ { "hooks": [
{ "type": "command", "command": "PUFFER_AUTOFEED=1 PUFFER_DIR=/Users/you/Developer/puffer-tracker node /Users/you/Developer/puffer-feed/hooks/puffer-autofeed.mjs" }
] } ] } }
```
The core enforces an anti-spam guard on hook-fed entries (min meaningful title, dedupe,
1-per-project-per-20-min throttle). Recommended posture: leave it off and let the agent
call `log_step` deliberately.
---
## Auto-log rule (drop into your CLAUDE.md)
The most reliable "hands-free" path isn't a dumb trigger — it's a one-paragraph rule that
tells the agent to call `log_step` at every big step. The canonical copy lives in
**[`CLAUDE-RULE.md`](./CLAUDE-RULE.md)** — paste it into your global `~/.claude/CLAUDE.md`
(covers every project at once) and/or each project's `CLAUDE.md`. It defines what counts
as a "big step", the project keys, and the partner-facing writing style.
---
## Why one locked write-core
Every writer — MCP, CLI, hook, even the screenshot agent (`commitPaths()`) — funnels
through `withLock()` → `fetch → rebase → derive id AFTER rebase → validate → atomic
write → git add data.json (never -A) → commit → push (rebuild + retry on non-fast-forward,
never force-push)`. That:
- **serializes concurrent writers** via an atomic `link()`-based lockfile in `.git/`
(pid-liveness stale-steal, ownership-checked release) — fixes the clobber bug directly;
- derives the new id **after** rebase so two writers never pick the same id;
- writes atomically (tmp + fsync + reparse + rename) so a reader never sees a torn file;
- **pathspec-scoped commit** (`git commit -- data.json`) so a pre-staged file in your
tracker clone can never be swept into the public push;
- scans every published field for secret shapes **before** publishing (reject by default —
rotate, don't "edit", since git history is permanent). Add your own internal hosts/IPs
to block with `PUFFER_INFRA_HOSTS="10.0.0.5,api.internal.example"` (kept out of this repo);
- returns a **truthful result** — callers say "live in ~15s" only when `pushed === true`.
Run the regression test (spawns 8 parallel writers against a throwaway local clone):
```bash
npm test
```
## License
MIT © 2026 ThePuffer / EA Solutions
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues