- 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.
10 KiB
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
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)
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
- Build before test — always. CLI tests execute
dist/index.jsand SKIP silently when dist is missing. CI explicitly greps for# SKIPand fails the build, because green output with 0 passing tests has actually happened. If you run tests without building, you are not testing anything. - Line endings are load-bearing.
.gitattributesforces LF on everything;bundle-skill.mjsre-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. tsupruns withclean: falsebecausebundle-skill.mjspopulatesdist/skill/before tsup runs; cleaning would delete the bundled skill. If you touch the build config, preserve this ordering.- Version stamping.
dist/skill/.designpaca_versionmust equalpackages/cli/package.jsonversion.verify-package.mjs(prepublishOnly + CI) enforces it. All three packages move together via changesetsfixedgroup. - Release = tag push, nothing else.
git tag vX.Y.Z && git push origin main --tagstriggers Forgejo Actions (self-hosted runner, host mode, nouses:actions — everything isrun:). 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. - CI runner has no guaranteed Node: don't add
uses:steps to workflows; use plainrun:(see existing workflows). DESIGNPACA_STATE_DIRredirects designpaca's own state dir (~/.designpacamanifest) for CI/tests without changing user-facing install paths. CLI tests already setHOME/USERPROFILEto a tempdir; keep that isolation in new tests.- 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. - Never create release/dev checkouts outside this repo root. Past release runs used ad-hoc sibling
git worktrees (D:/workspace/designpaca-release-v0.x.x,designpaca-deploy-<sha>). These look like stray copies, carry fullnode_modules, and are actually owned by the main repo's.git/worktrees/. Deleting the folder by hand leaves aprunableghost ingit worktree list; deleting the main.gitbreaks them. If a separate checkout is genuinely required, place it under the repo (e.g..worktrees/) or a dedicated_worktrees/root, and always finish withgit worktree remove <path>(use--forceonly for disposable release checkouts) — neverrm -rf. Prefer committing on a branch in this working tree when possible. - 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.mjsnow fails if the## 인터뷰 게이트section, the "명시적으로 의존한다" sentence, or theharness.md/brief-interview.mdlinks disappear, or if a "~때만 묻는다" condition returns. Any change to when the skill asks must be stated in its changeset and re-checked withbuild/eval/interview/on Claude Code and Codex. - Keep tool names harness-neutral in the skill body. Every target adapter copies
SKILL.mdverbatim, so a Claude-only tool name (e.g.AskUserQuestion) reaches Codex, Cursor, Gemini, and others unchanged. Put per-harness tool names, limits, and fallbacks inreferences/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 implementsdetect()(any trace on system → preselected in TUI) andplan()which returns a pureInstallPlan(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()rewritesreferences/...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.mdetc.) hash only the content inside<!-- designpaca:start/end -->blocks; the rest of the user's document is untouchable.
- User-modified files (drift detected via stored sha256) are skipped unless
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.tsextensions (import x from "./foo.ts"). Tests import fromnode:test+node:assert/strict, run vianode --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 inskill-source.ts). - Skill docs discipline: every
references/*.mdfile 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. Runnode build/ci/lint-skill.mjs packages/skillafter editing skill docs. - Changesets: add a changeset for user-visible changes;
pnpm changeset/pnpm version.@designpaca/siteis ignored by changesets.