Package source shape
SkillAI & modelsNo Storybook, the component list comes from the package's shipped .d.ts exports, and there is no reference render to verify against.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Package source shape skill
About this skill
A filesystem-first library for prompt engineering, context engineering, agent control, reusable AI workflows, research methodology, and LLM tooling, with an optional desktop application for fast search and library management.
What this skill tells your AI
The instructions your AI receives, as published by justinlietz93/perfect_prompts in Prompts/System_Prompts_Leaks/Anthropic/claude-code/skills/design-sync/non-storybook/SKILL.md and read by ahel’s review.
No Storybook — the component list comes from the package's shipped .d.ts exports, and there is no reference render to verify against. Preview quality therefore comes from two layers: the converter ships every component fully functional (bundle + .d.ts + .prompt.md) with an honest floor card, and rich previews are authored — by you, from the repo's own usage examples — for the components the user scopes in (§4). Authored previews are graded on an absolute rubric (§4.3) and reviewed by the user (§4.4); the floor card is never a failure, just an unauthored component.
2. Explore, then write config (continued)
-
The converter needs the built
dist/entry + its.d.tstree. Check whether the entry (frompackage.jsonmodule/main/exports['.']) already exists — install may have built it viaprepare. If missing:- Run
<pm> run build. Nobuildscript → tryprepare/prepack. In a monorepo, build the package and its workspace dependencies from the repo root:turbo build --filter=<pkg>orpnpm -F "<pkg>..." build(the trailing...is required — bare-F <pkg>skips dependencies and you'll seeCannot find module '@scope/tokens'). Some build scripts fork a watcher and exit 0 early — after the command returns,lsthe expected output (dist/, build/esm/, or whateverpackage.jsonmodule/mainpoints at) and confirm it's populated before continuing. If it's empty, check for a--watchflag in the script and use the one-shot variant, or poll the output dir. - Still missing →
AskUserQuestion("What command builds this package?", options = anyscripts.*containingtsc|tsup|rollup|vite build|esbuild|swc, plus freeform). Record the answer asbuildCmdin the config. - User says there's no build → the converter will synthesize an entry from
src/(last resort —.d.tscontracts will be weaker; recommend adding a build).
- Run
-
Check what's already in the project.
DesignSync(list_files)on the target (the base skill §1 already picked the upload path: pinned-at-run-start → atomic; otherwise empty → incremental, non-empty → atomic). If it has files, fetch the small verification anchor:DesignSync(get_file, path: "_ds_sync.json")and save it locally (.design-sync/.cache/remote-sync.json) — never download_ds_bundle.jsfor this. The driver run (the "Re-syncs are one command" block,--remotepointing at the saved anchor) diffs it into.sync-diff.jsonwith TWO partitions answering different questions. Verification (unchanged/changed/added): which components need capture + grading —unchangedwere verified at the last upload and skip §4 entirely. Upload (upload.components/upload.deletePaths/upload.bundle/upload.styling): which files the project is missing — sourceHashes-based, so.d.ts/.prompt.md-only edits, regroups (old paths land indeletePaths), and bundle-only changes still ship even when no render changed. Never scope uploads by the verification partition. No sidecar in the project (never synced, or shape change) → no anchor → full first-sync scope; iflist_filesshowed the project NON-empty, deletes can't be derived — review its file list once for files this build doesn't produce; those reviewed paths go into the upload plan'sdeletesat §5. -
Confirm the plan AND the preview scope with the user before building.
AskUserQuestionwith: the component list you found (or a count + a few names if it's long), which files the tokens/CSS are coming from, and which build command you'll run. The build can take minutes and burn tokens — aligning now avoids re-running because it was pointed at the wrong package or missed half the components.- Preview scope (this shape's cost slider — all N components import fully functional either way; this only decides which get authored preview cards): (a) author rich previews for the core components — the user picks them, or you propose ~20–40 from docs prominence; (b) author everything (significantly longer — state the estimate from N × a few minutes each); (c) floor cards everywhere for now (fastest; previews can be authored incrementally on any later re-sync — authored files and grades carry forward).
- If the project already has components from a prior sync (step 4), also offer: full re-verify + re-upload (
--force-equivalent) or changed-components-only (the verdict's worklist; default). The precise partition exists only after the driver runs — state it then ("N verified-by-upload, M to verify: [names]") before starting §4 work, and check in with the user if it's surprisingly large.
-
Write
.design-sync/config.jsonand commit it — re-sync reuses it so output is reproducible. OnlypkgandglobalNameare required. If the file already exists, read it first and preservedtsPropsFor,libOverrides, andoverrides— only add to those fields, never replace them. They accumulate fixes from prior verify-loop iterations. Also Read.design-sync/NOTES.mdbefore anything else — it holds repo-specific gotchas a prior sync recorded.Field Value pkg/globalNamepackage name (required) and the window.*global to assign (auto-derived frompkgwhen omitted)projectIdthe claude.ai/design project this repo syncs to — recorded automatically in §1, the moment the target is settled (the atomic upload's post-verify record is a backstop); re-syncs fetch their verification anchor ( _ds_sync.json) from it without askingshape'storybook'or'package'— pins the source shape (overrides auto-detection). Written on first run.buildCmdthe discovered build command — tells Claude what to re-run before the converter on re-sync srcDirsource root when not src//lib//components/tsconfigpath to tsconfig.json— esbuild readscompilerOptions.pathsso@/…path aliases resolve in synth-entry modeextraEntriespackage names to merge into window.<globalName>alongside the DS entry (e.g. the DS's separate icon package). Sibling icon packages under the same scope are auto-detected ([ICON_PKG]).componentSrcMapsparse {Name: path}— non-null pins/adds a component's src path;nullexcludes a.d.ts-exported internaldtsPropsFor{Name: "prop?: Type; …"}— hand-written<Name>Propsbody when auto-extraction fails (complex generics, cross-package types)cssEntry/tokensPkg/tokensGlobstylesheet + token files docsDirdirectory (package-relative; may point outside, e.g. ../../apps/docs) holding per-component.md/.mdxdocs. Auto-detected asdocs/ordocumentation/under the package.docsMapsparse {Name: path | null}— explicit doc path per component (overrides discovery);nullexcludes. Exceptions only, never an enumeration: setdocsDirand let discovery bind docs; add entries only for misses, exclusions, regroup stubs, or[DOCS_AMBIGUOUS]pins. A map that names every component duplicates what discovery already does and rots on every component add.readmeHeaderstring path relative to the config home (the directory containing .design-sync/) of a repo-committed file prepended verbatim to the generated README — the conventions-header slot (see base SKILL.md "Author the conventions header").guidelinesGlobstring or string[] (package-relative) of design-guideline .mdfiles to copy intoguidelines/. Default['docs/guides/**/*.md', 'docs/*.md', 'guides/**/*.md'].extraFontspaths (package-relative; may point outside the package, e.g. a sibling typography package) to @font-face.cssfiles or bare.woff2/.ttf/.otffor brand families the DS expects its host app to provide. CSS entries are parsed and their local font files copied tofonts/; bare font files are copied as-is. Use when validate prints[FONT_MISSING].runtimeFontPrefixesstring[] — family-name prefixes for fonts the host app serves at runtime from a font service (via a <script>or JS loader, so there's no@font-faceto ship). Suppresses[FONT_MISSING]for matching families. Use when the brand font is never meant to ship with the bundle.replaces{<raw-element>: [<ComponentName>, …]}— extends the adherence-config raw-element maplibOverrides{"<name>.mjs": "<one-line reason>"}— declares which.design-sync/overrides/*.mjsfiles this repo forks and why (see §Troubleshooting). Cross-checked at build time.providerwrapper for previews that need context (see §Troubleshooting). Literal propsare for small scalars and stable snippets; for data that already exists in the repo (locale JSON, theme objects), prefer{"$ref": "<export>"}backed by a 2-line module added viaextraEntries— an inlined copy duplicates into every card and silently rots when the source file changes, so anything sizable or evolving belongs behind a$ref. Repo-owned modules need an explicit.//../package-relative path inextraEntries(workspace-bounded); bare names resolve fromnode_modules.Top-level config keys are validated strictly: an unknown or removed key fails the run immediately with the fix named in the message (
✗ config: …). That is the migration path when the schema changes — fix the config as the message says; the scripts carry no compat code..design-sync/NOTES.mdis where repo-specific quirks live (workspace build order, flaky stories, odd entry paths, anything a future re-sync should know). Write it as multi-line markdown — one bullet per gotcha. Append to it whenever the user tells you about an issue or you learn something during the verify loop, so the next sync picks it up without the user repeating themselves. Before finishing, also write the forward-looking part — a Re-sync risks section listing what can silently go stale (data inlined into config, neutralized or owned previews tied to upstream code), what was only partially verified, and what the build assumed (toolchain version, network-fetched assets). Fixes record what you did; this section tells the next run what to watch. Commit it alongside the config. -
Run the converter. For large DSes (200+ components) the ts-morph
.d.tsparse can take several minutes —[DTS]progress lines on stderr show it's working. Stage scripts into.ds-sync/and install converter deps there (isolated from the repo's lockfile/package manager):
mkdir -p .ds-sync && cp -r "<skill-base-dir>"/package-build.mjs "<skill-base-dir>"/package-validate.mjs "<skill-base-dir>"/package-capture.mjs "<skill-base-dir>"/resync.mjs "<skill-base-dir>"/lib "<skill-base-dir>"/storybook .ds-sync/
echo '{"name":"ds-sync-deps","private":true}' > .ds-sync/package.json
(cd .ds-sync && npm i esbuild ts-morph @types/react)
node .ds-sync/package-build.mjs --config .design-sync/config.json --node-modules <pkg-node-modules> \
--entry ./dist/index.es.js --out ./ds-bundle
node .ds-sync/package-validate.mjs ./ds-bundle
Add .ds-sync/, ds-bundle/, .design-sync/.cache/, .design-sync/learnings/, and .design-sync/node_modules (the fork symlink — recreated per clone, never committed) to .gitignore (staged scripts + their node_modules, regenerated build output, machine state incl. generated previews — .design-sync/previews/ holds ONLY files you author — and fan-out scratch). The durable set — everything under .design-sync/ that isn't gitignored above (today: config.json, NOTES.md, conventions.md, previews/, overrides/; the rule, not the list, is the contract — a future durable file is in the set by construction) — IS committed. Verification state is NOT in git: cross-machine carry-forward comes from the uploaded project's _ds_sync.json (step 4), and verdicts live in the gitignored .cache/.
Run build and validate as separate commands and check each exit code — a chained build && validate in the background exits non-zero with no visible log when the build step fails.
Backgrounding rules:
- Headless /
-psession: run both synchronously (norun_in_background). There is no task-notification re-invocation in headless mode, so a backgrounded run is never resumed. - Interactive session: backgrounding the build is fine — through your shell tool's background mode only (it completes with a task notification you can wait on). Never use a bare
&— nothing tracks it, the notification never comes, and you'll idle forever. - Don't poll in a foreground loop:
pgrep -f '<script-name>'matches its own command line and spins to timeout while the finished build's notification sits queued. - A backgrounded task running well past its estimate: Read its output file once. A build sitting in watch mode never exits — kill it and use the one-shot variant (step 3). Otherwise keep waiting for the notification.
In a monorepo, point --node-modules at the DS package's own node_modules (where its react resolves) — not the repo root — unless hoisting leaves it sparse (yarn's node-modules linker keeps react only at the repo root): if react/ or react-dom/ is missing inside it, pass the repo-root node_modules instead. In the DS's own repo node_modules/<pkg> usually doesn't exist (npm won't self-install), hence --entry.
@types/react is required for prop extraction — without it React.ComponentPropsWithoutRef<…> and similar utility types resolve to any and the emitted <Name>.d.ts loses inherited props (converter prints [DTS_REACT]).
If building the monorepo is complex, npm install <your-pkg>@latest react react-dom into a scratch dir and pass --node-modules <scratch>/node_modules — uses your published dist with flattened deps.
What the converter emits
Per component, under components/<group>/<Name>/: <Name>.jsx (one-line re-export stub), <Name>.d.ts (props interface from the shipped types), <Name>.prompt.md, and <Name>.html (the preview card). You don't write any of these — the converter does.
<Name>.prompt.md is the matched per-component doc when one exists (sibling <Name>.md/.mdx → cfg.docsDir lookup → <Name>.stories.mdx; frontmatter category sets the component's <group>). To regroup a component that has no real doc, point cfg.docsMap at a stub .md whose only content is ---\ncategory: <Group>\n---. Otherwise it's synthesized from the .d.ts props body, the leading JSDoc, and any examples in .design-sync/previews/<Name>.tsx. [DOCS_UNMAPPED] lists components that didn't match.
<Name>.html renders the component from window.<GLOBAL>.<Name> via its compiled preview .tsx (each named export = one labeled cell, individually addressable as ?story=<Export>). When no compiled preview exists — nothing authored, or the .tsx failed to compile — the html is the floor card: one render attempt with the .d.ts crash-prevention props that swaps to a deliberate typographic block (name + "preview not yet authored") if the root comes up empty. The floor card is honest, not broken; the fix for a component that deserves better is authoring its preview (§4.2). Hand-edits to a .html are overwritten on rebuild — previews live in the .tsx.
.design-sync/previews/ (committed): one <Name>.tsx per authored component — files you write, no marker, this directory holds nothing machine-made. In this shape there is no generated tier: a component either has an authored preview or ships the floor card. (One transitional edge: a leftover .design-sync/.cache/previews/<Name>.tsx that was hand-edited under its marker is preserved with a warning and still compiles as the preview — a take-ownership ramp, but gitignored, so move it into previews/ minus its marker line or it vanishes on a fresh clone.) Ownership is by location: the converter never writes or deletes anything in previews/. Commit previews/ with the rest of the durable set (the durable-set rule above: everything under .design-sync/ not gitignored).
3. Self-heal loop
package-validate.mjs's render check needs playwright + chromium — make §4.1's install-or-skip decision BEFORE the first validate run (without a browser it fails [RENDER_SKIPPED]; --no-render-check downgrades that to a loud warning once the user has accepted an unverified bundle). It emits [TAG]-prefixed diagnostics on stderr. For each error: match the tag in this table → apply the fix → rebuild → re-validate. Repeat until it exits 0. Lines printed as hypothesis: under an error are leads, not instructions: run their verify step first, and if it doesn't confirm, drop the hypothesis and diagnose from the error text itself. A few stories that genuinely can't render statically (interaction-driven, data-fetching) go in cfg.overrides.<Component>.skip.
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 23
- Forks
- 1
- Last commit
- Aug 2026
ahel review
K1binfo
installs-packages
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Item type
- skill
- Key
non-storybook- Source
- github.com/justinlietz93/perfect_prompts