designpaca/AGENTS.md
Yun Chan 6805fb2be7 feat(skill): absorb external design skills, restore interview gate, add review route
- Restore the step-0 interview as a mechanical gate the skill explicitly
  depends on; add harness.md (per-harness question tools, limits,
  fallbacks) and brief-interview.md (slots, question cards, rounds).
- Add 10 reference docs absorbed from external design skills
  (accessibility, interaction-feel, elevation, color, icons, product-copy,
  component-systems, critique, change-review, print-email) and extend
  existing references.
- Add a review-only route and two hard-gate clauses (truncated content
  reachability, three-flashes limit).
- design-gate: split tap targets into WCAG 2.5.8 and 44px contract layers,
  run axe-core when available, and fix false positives found on a real
  site (decorative alt="", stacked wordmark line count, url-only pages).
- lint-skill: fail if the interview gate section or its links disappear.
- Ship agents/openai.yaml and THIRD_PARTY_NOTICES.md.
2026-09-24 13:26:03 +09:00

98 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AGENTS.md — designpaca monorepo
Guidance for AI agents working in this repository. User-facing docs live in `README.md` (repo root, Korean) and `packages/cli/README.md` (npm page).
## What this repo is
A web-design pipeline skill plus its installer CLI. Users get everything with one `npx designpaca`. The actual product value is the skill document set; the CLI is the delivery vehicle.
```
packages/
skill/ @designpaca/skill — SKILL.md + references/. The product itself. Private (not published).
core/ @designpaca/core — install engine: target adapters, manifest, drift detection. Private.
cli/ designpaca — npx entry point + onboarding TUI. The ONLY published package.
apps/
site/ @designpaca/site — showcase site (Astro static → Cloudflare Pages). Private.
research/ Evidence base for the skill (~250 web sources). Docs, not code.
build/ci/ CI scripts (lint-skill, publish, release upload, node verify).
outputs/ Untracked scratch area for verification runs, screenshots, audits.
docs/ DEPLOYMENT_PLAN.md (release ops), project retrospectives.
```
Key packaging fact: when the CLI builds, `@designpaca/core` is bundled into the CLI (`tsup` with `noExternal`) and `packages/skill` is copied to `dist/skill/` by `packages/cli/scripts/bundle-skill.mjs`. Users never install core or skill separately.
## Commands
```bash
pnpm install # pnpm 10.x, Node >= 20.11
pnpm build # skill bundle + CLI build (packages only)
pnpm typecheck # tsc --noEmit in packages
pnpm test # node:test across all packages
pnpm dev:site # Astro dev server for the site
# Test the CLI the way npx actually runs it
pnpm build
cd packages/cli && npm pack
npx ./designpaca-<version>.tgz --help
# Install-roundtrip in an isolated home (do not touch your real ~/.designpaca)
HOME=/tmp/dp USERPROFILE=/tmp/dp node packages/cli/dist/index.js install -t claude-code -s user -y
# Skill docs validation (CI runs the same thing)
node build/ci/lint-skill.mjs packages/skill
```
### Site QA gate (apps/site)
```bash
cd apps/site
pnpm verify # full L0–L6 gate; exit 1 on any failure = do not deploy
pnpm test:design:gate # design gate only (gate.config.json)
pnpm build # prebuild generates showcase assets; postbuild runs no-hanja check
node tools/visual.mjs --update-baseline # refresh visual baselines, then commit
```
`tools/verify.mjs` chains ~17 layers (static checks, unit, responsive audit, design gate, visual regression, WebKit, exploratory, E2E). It needs a Chrome binary (config: `chromePath` in gate configs) and Playwright.
## Critical gotchas
1. **Build before test — always.** CLI tests execute `dist/index.js` and SKIP silently when dist is missing. CI explicitly greps for `# SKIP` and fails the build, because green output with 0 passing tests has actually happened. If you run tests without building, you are not testing anything.
2. **Line endings are load-bearing.** `.gitattributes` forces LF on everything; `bundle-skill.mjs` re-normalizes text to LF. Reason: CRLF breaks SKILL.md's YAML frontmatter in some parsers. Do not "fix" LF-only files, and don't bypass these guards.
3. **`tsup` runs with `clean: false`** because `bundle-skill.mjs` populates `dist/skill/` before tsup runs; cleaning would delete the bundled skill. If you touch the build config, preserve this ordering.
4. **Version stamping.** `dist/skill/.designpaca_version` must equal `packages/cli/package.json` version. `verify-package.mjs` (prepublishOnly + CI) enforces it. All three packages move together via changesets `fixed` group.
5. **Release = tag push, nothing else.** `git tag vX.Y.Z && git push origin main --tags` triggers Forgejo Actions (self-hosted runner, host mode, no `uses:` actions — everything is `run:`). Never push a tag without approval: publishing claims the npm name irreversibly. CI does lint-skill → typecheck → build → test → SKIP check → package verification → npm publish + Forgejo mirror → draft release → Cloudflare Pages deploy.
6. **CI runner has no guaranteed Node**: don't add `uses:` steps to workflows; use plain `run:` (see existing workflows).
7. **`DESIGNPACA_STATE_DIR`** redirects designpaca's own state dir (`~/.designpaca` manifest) for CI/tests without changing user-facing install paths. CLI tests already set `HOME`/`USERPROFILE` to a tempdir; keep that isolation in new tests.
8. **Git push goes over Tailscale**, not the domain: `ssh://git@100.116.83.60:2222/yunchan/designpaca.git` (git.chanpaca.net is behind Cloudflare, port blocked). Details in `.env.example`.
9. **Never create release/dev checkouts outside this repo root.** Past release runs used ad-hoc sibling `git worktree`s (`D:/workspace/designpaca-release-v0.x.x`, `designpaca-deploy-<sha>`). These look like stray copies, carry full `node_modules`, and are actually owned by the main repo's `.git/worktrees/`. Deleting the folder by hand leaves a `prunable` ghost in `git worktree list`; deleting the main `.git` breaks them. If a separate checkout is genuinely required, place it under the repo (e.g. `.worktrees/`) or a dedicated `_worktrees/` root, and always finish with `git worktree remove <path>` (use `--force` only for disposable release checkouts) — never `rm -rf`. Prefer committing on a branch in this working tree when possible.
10. **Never soften the step-0 interview gate.** v0.11.0 (`45ba92e`) rewrote "almost always ask" into "ask only when it would change the result", with an empty commit body and no changelog line. Combined with harness autonomy pressure (Claude Code auto mode, Codex "bias to action"), the skill stopped asking users. `build/ci/lint-skill.mjs` now fails if the `## 인터뷰 게이트` section, the "명시적으로 의존한다" sentence, or the `harness.md`/`brief-interview.md` links disappear, or if a "~때만 묻는다" condition returns. Any change to when the skill asks must be stated in its changeset and re-checked with `build/eval/interview/` on Claude Code and Codex.
11. **Keep tool names harness-neutral in the skill body.** Every target adapter copies `SKILL.md` verbatim, so a Claude-only tool name (e.g. `AskUserQuestion`) reaches Codex, Cursor, Gemini, and others unchanged. Put per-harness tool names, limits, and fallbacks in `references/harness.md`, not in the body.
## Architecture & data flow
### Install pipeline (core)
- `targets/*.ts` — one adapter per destination tool (claude-code, codex, cursor, zcode, agents, antigravity, gemini-cli, windsurf, copilot, agents-md). Each implements `detect()` (any trace on system → preselected in TUI) and `plan()` which returns a pure `InstallPlan` (list of file actions, **no disk writes**).
- Two adapter shapes: `skillDirActions()` copies the whole skill directory (Claude Code, Codex — SKILL.md used verbatim), `referenceActions()`/injection split the body from references (Cursor, AGENTS.md). For the latter, `rewriteRefPaths()` rewrites `references/...` links to the actual install location — without this the body points at nonexistent paths.
- `installer.ts` — `planInstall()` → `applyPlan()`. Safety semantics (do not break these):
- User-modified files (drift detected via stored sha256) are **skipped** unless `--force`; force still backs up to `.orig`.
- Skipped files stay in the manifest with their **original install-time hash** — using the current hash would make drift vanish and a later update silently overwrite user edits.
- Marker-injected files (`AGENTS.md` etc.) hash only the content inside `<!-- designpaca:start/end -->` blocks; the rest of the user's document is untouchable.
- `manifest.ts` — install records in `~/.designpaca/manifest.json`. Uninstall only removes recorded files; corrupted manifest degrades to empty rather than blocking installs.
- AGENTS.md adapter deliberately injects **a pointer only**, not the skill body — AGENTS.md loads unconditionally in every session and Codex truncates around 32 KiB. If you change this adapter, keep that reasoning in mind.
### CLI (packages/cli)
Hand-rolled argv parser (`src/index.ts`), commands in `src/commands/` (install, update, uninstall, doctor, list, tools), TUI on `@clack/prompts`. Unknown target → exit code 2. `tools` command downloads hundreds of MB so it only shows status without `--yes`. Update check runs **after** commands, never delays startup. Version comes from the bundled skill stamp, not package.json.
### Site (apps/site)
Astro static output. Showcases are self-contained static pages under `public/work/<name>/` with their own `tokens.css`/`styles/`; `gate.config.json` declares per-page contracts (views, selectors, token/hygiene files, color/radius literal budgets) consumed by `tools/design-gate.mjs`. `tools/no-hanja.mjs` forbids Hanja in deployed text (postbuild + verify). The design skill's own audit methodology (`packages/skill/references/audit-gate.md`) is implemented by these tools — treat gate failures as design regressions, not build noise.
## Conventions
- **Korean documentation and code comments.** Comments and docs are written in a declarative Korean style ("~한다"), often explaining *why* with references to measured incidents ("실측 사고"). Match this when editing existing files. Commit messages are English, imperative.
- **TypeScript**: ESM, strict, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`, `allowImportingTsExtensions` — imports use explicit `.ts` extensions (`import x from "./foo.ts"`). Tests import from `node:test` + `node:assert/strict`, run via `node --test --experimental-strip-types`.
- **Minimal dependencies**: core has zero runtime deps; CLI has only `@clack/prompts` + `picocolors`. Skill source deliberately avoids pulling in a YAML parser (shallow frontmatter regex in `skill-source.ts`).
- **Skill docs discipline**: every `references/*.md` file must be linked from SKILL.md's body (lint warns on dead docs), and every path the body links must exist (lint errors). Description length matters (80–700 chars) for trigger accuracy. Run `node build/ci/lint-skill.mjs packages/skill` after editing skill docs.
- **Changesets**: add a changeset for user-visible changes; `pnpm changeset` / `pnpm version`. `@designpaca/site` is ignored by changesets.