CodeVetter · Repo Unpack · Contributor handoff
DietrichGebert/ponytail
This repo edits its own dogma: AGENTS.md demands the smallest correct diff and `ponytail:` comments marking deliberate ceilings — match that style. The golden rules: SKILL.md and AGENTS.md are the sources of truth and every copy must stay in sync (CI enforces it); all shared logic lives in the CJS hooks/ modules so adapters stay thin; hooks must never hang or crash a session (fail-open, BOM-strip, timeouts); and any version bump touches 8 files. Always run `npm test` plus the two check scripts before claiming done.
- Read first, in order: AGENTS.md (the ruleset and the repo's own working style), skills/ponytail/SKILL.md (canonical long-form rules), hooks/ponytail-runtime.js + ponytail-config.js + ponytail-instructions.js (the shared core every adapter calls), then docs/agent-portability.md for the host→file map.
- After editing AGENTS.md or any rule copy (.cursor/rules/ponytail.mdc, .windsurf, .clinerules, .agents, .qoder, .github/copilot-instructions.md, .kiro), run `node scripts/check-rule-copies.js` — it fails CI on drift; after touching SKILL.md verify the 9 INVARIANTS phrases still appear verbatim.
- Version bumps touch 8 files: .claude-plugin/plugin.json, .codex-plugin/plugin.json, .devin-plugin/plugin.json, .github/plugin/plugin.json, .qoder-plugin/plugin.json, gemini-extension.json, package.json, ponytail-mcp/package.json — run `node scripts/check-versions.js` to verify they agree.
- Test command: `npm test` (root node:test suite + pi-extension + ponytail-mcp); the MCP suite needs `npm install --prefix ponytail-mcp` first (only directory with real deps: @modelcontextprotocol/sdk + zod).
- Hook contract traps: strip UTF-8 BOM before JSON.parse everywhere; never block on stdin without the 1s unref'd timeout + error handler (Windows swallows piped input, #443); native Claude SubagentStart requires hookSpecificOutput JSON not raw stdout; Cursor treats empty stdout as no-op and raw text as a parse error.
- Mode semantics: 'review' is session-only and must never be a default (#377) — validate against RUNTIME_MODES for defaults and VALID_MODES for persisted values; isDeactivationCommand only fires on the whole-message phrase, not substring matches.
- Safe edit boundaries: per-host manifests and hook JSONs are isolated (edit one without touching others); the shared hooks/ modules and skills/ are high blast-radius — a bug there affects every host. Anything in .openclaw/skills/ is generated — edit skills/ and rerun scripts/build-openclaw-skills.js instead.
- House style: CommonJS in hooks/ and scripts/ (ESM only where the host requires it — ponytail.mjs and pi-extension/index.js bridge via createRequire), no dependencies outside ponytail-mcp, `ponytail:` comments mark deliberate simplifications with a named ceiling and upgrade path, and non-trivial logic leaves one runnable check behind.
- Issue-number archaeology matters: comments cite GitHub issues (#34 CLAUDE_CONFIG_DIR, #252 subagents, #377 review default, #443 Windows stdin, #483 nudge-once, #506 matcher, #528 VS Code Copilot, #573 Codex JSON shape, #817 Cursor) — when changing that code, re-check the referenced constraint or a regression test will catch it.
- Windows is a first-class target: tests set both HOME and USERPROFILE, paths go through os.homedir(), cursor-hooks.js normalizes to forward slashes, the statusline has a .ps1 twin, and hooks-windows.test.js covers platform edges — don't introduce POSIX-only assumptions.