designpaca/AGENTS.md
Yun Chan 6229538d29
Some checks failed
ci / build (push) Failing after 14m38s
docs: add AGENTS.md guide for AI agents
Document the monorepo's packaging, build/test/release order, and
non-obvious gotchas (build before test, LF-only text, no ad-hoc
worktrees) so future agents do not rediscover them by trial and error.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4.1-flash
2026-09-13 07:28:12 +09:00

9.2 KiB
Raw Permalink Blame History

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

  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 worktrees (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.

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.