diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6ca0303 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,96 @@ +# 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-.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-`). 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 ` (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 `` 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//` 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.