AGENTS.md

Repository Guidelines

Skill bootstrap: skip the post-update-cleanup step

If you load the Impeccable skill from .agents/skills/impeccable/SKILL.md (or any sibling harness directory under this repo), it begins with a <post-update-cleanup> block that runs scripts/cleanup-deprecated.mjs and then self-deletes. Do not run it here, and do not delete the block. This repo is the source of truth for the skill; cleanup-deprecated assumes a consumer project and would touch legitimate source files. The block is regenerated from skill/SKILL.md on every bun run build, so any deletion churns back. Skip it silently and proceed with the task.

Project Structure & Module Organization

source/ is the source of truth. Author skills in skill/ and keep provider output in dist/ generated, not hand-edited. Build logic lives in scripts/, with provider configs in scripts/lib/transformers/. Runtime detection code ships from src/. The website lives in public/, local API/dev serving lives in server/, and regression coverage lives in tests/ with fixtures under tests/fixtures/.

Build, Test, and Development Commands

  • bun run dev - start the local Bun server.
  • bun run build - regenerate dist/, derived site assets, and validation output.
  • bun run rebuild - clean and rebuild everything from scratch.
  • bun test tests/build.test.js - run a focused Bun test.
  • bun run test - run the full Bun + Node test suite.
  • bun run test:live-e2e - opt-in live-mode E2E against framework fixtures (~2 min; needs npx playwright install chromium once).
  • bun run build:browser / bun run build:extension - rebuild browser-specific bundles.

Run bun run build after changing anything in source/, transformer code, or user-facing counts.

Sandbox gotchas for Codex agents

Some repo workflows need to run outside the sandbox in the desktop app:

  • GitHub SSH operations that depend on the 1Password SSH agent, such as gh pr checkout, may fail in the sandbox with sign_and_send_pubkey or no 1Password approval prompt. Rerun them outside the sandbox instead of falling back to unrelated workarounds.
  • bun run build rewrites committed harness directories such as .agents/skills/. In the sandbox, Bun can hit filesystem errors while removing/recreating those trees (for example EFAULT on .agents/skills). Rerun the build outside the sandbox before treating it as a real build failure.
  • Puppeteer/headless-Chrome tests, especially node --test tests/detect-antipatterns-browser.test.mjs and the browser portion of bun run test, can hang in the sandbox while launching Chrome. Run them outside the sandbox for authoritative results.
  • The jsdom fixture suite is intentionally run with Node, not Bun: use node --test tests/detect-antipatterns-fixtures.test.mjs or the bun run test script. A direct bun test tests/detect-antipatterns-fixtures.test.mjs can time out and is not the supported signal.

Coding Style & Naming Conventions

Use ESM, semicolons, and the existing two-space indentation style in JS, HTML, and CSS. Prefer small, single-purpose modules over large abstractions. Keep filenames descriptive and lowercase with hyphens where needed; skill entrypoints stay as SKILL.md, helper scripts use .js or .mjs. In source frontmatter, use clear kebab-case names and concise descriptions. There is no dedicated formatter or linter configured here, so match surrounding code closely.

Testing Guidelines

Tests use Bun’s test runner plus Node’s built-in --test. Name tests *.test.js or *.test.mjs and place new fixtures near the behavior they cover, usually under tests/fixtures/. Prefer targeted test runs while iterating, then finish with bun run test. If you change generated outputs or provider transforms, verify both source parsing and at least one affected provider path in dist/.

For changes to skill/scripts/live-*.{mjs,js}, also run bun run test:live-e2e (kept out of the default suite because it does real npm install per fixture and boots framework dev servers). Scope to one fixture with IMPECCABLE_E2E_ONLY=<fixture-name> while iterating; pass IMPECCABLE_E2E_DEBUG=1 for page-DOM and dev-server-log dumps on failure. Schema and authoring guide for new fixtures live in tests/framework-fixtures/README.md.

Set IMPECCABLE_E2E_AGENT=llm to swap the deterministic fake agent for a Claude-backed one (tests/live-e2e/agents/llm-agent.mjs, default Haiku 4.5, override via IMPECCABLE_E2E_LLM_MODEL). Requires ANTHROPIC_API_KEY; tests skip cleanly when it's unset. This path hits the API — use it for verification, not CI.

Anti-pattern detection rules

cli/engine/detect-antipatterns.mjs is the source of truth for the rule engine. It feeds the CLI, the site overlay (cli/engine/detect-antipatterns-browser.js, regenerated by bun run build:browser), the Chrome extension (extension/detector/, regenerated by bun run build:extension), and the homepage DETECTION_COUNT in site/public/js/generated/counts.js (regenerated by bun run build). After any rule change run all three builds plus bun run test so nothing drifts.

TDD order is non-negotiable:

  1. Add a fixture at tests/fixtures/antipatterns/{rule-id}.html with two columns (should-flag / should-pass), each case identified by a unique heading. ≥4 flag cases and ≥5 false-positive shapes. Use explicit pixel dimensions in CSS — jsdom does no layout.
  2. Add a failing test in tests/detect-antipatterns-fixtures.test.mjs using the snippet-substring pattern (regex /"([^"]+)"/ against SHOULD_FLAG / SHOULD_PASS lists).
  3. Add the rule entry to the ANTIPATTERNS array (id, category = slop or quality, name, description, optional skillSection / skillGuideline).
  4. Implement a pure checkXxx(opts) returning [{ id, snippet }] — no DOM access inside.
  5. Add two adapters that wrap the pure check: checkElementXxxDOM(el) for the browser (getComputedStyle + getBoundingClientRect) and checkElementXxx(el, tag, window) for jsdom (parseFloat(style.width) instead of layout). Wire both adapters into both element loops in cli/engine/detect-antipatterns.mjs (browser loop ~line 1837, jsdom loop in detectHtml ~line 2058). Forgetting one is the most common mistake.
  6. Verify on a live page at http://localhost:3000/fixtures/antipatterns/{rule-id}.html and on the homepage. The two adapter paths can disagree.

Conventions: wrap the identifying heading text in straight double quotes inside snippets so the fixture test can extract it. jsdom-specific helpers resolveBackground(), resolveGradientStops(), and parseGradientColors() exist because background: shorthand isn't decomposed and computed colors aren't normalized in jsdom — use them. Reference rules to copy from: side-tab (border), low-contrast (color+gradient), icon-tile-stack (sibling relationship), flat-type-hierarchy (page-level).

Commit & Pull Request Guidelines

Recent history favors short, imperative subjects such as Fix: ..., Add ..., Improve ..., or Bump .... Keep commits focused and explain the user-facing impact when it is not obvious. PRs should summarize what changed, list validation performed, and call out regenerated artifacts like dist/ or build/. Include screenshots for visible site/ changes and mention affected providers when transform behavior changes.

Releases

Tags are per-component because the three components ship independently: skill-v (.claude-plugin/plugin.json + .claude-plugin/marketplace.json), cli-v (package.json), ext-v (extension/manifest.json). Flow: bump the relevant manifest, add a changelog entry to site/pages/index.astro (skill = bare vX.Y.Z; CLI = CLI vX.Y.Z; extension = Extension vX.Y.Z — the prefix is how scripts/release.mjs finds the right block), commit, push, then bun run release:<skill|cli|ext> (or --dry-run first). The script refuses on a dirty tree, an unpushed HEAD, a missing changelog entry, or stale build outputs; skill and extension reruns of bun run build / bun run build:extension must produce zero diff. Skill releases attach dist/universal.zip; extension releases attach dist/extension.zip. CLI ships to npm via a separate npm publish, and the extension zip uploads to the Chrome Web Store manually — both reminded at the end of the script. Fix already-shipped notes with gh release edit <tag> --notes-file <md>.

Contributor Notes

Do not edit generated provider files directly unless you are intentionally patching generated output as part of a build-system change. Prefer fixing the root source in skill/, scripts/, or cli/, then regenerate artifacts.