Skip to content

Commit 95319ce

Browse files
authored
feat(zod): add use-zod skill plugin (#168)
* feat(zod): add use-zod skill plugin Hand-written workflow skill for Zod schema validation, mirroring the mastra plugin pattern from #161. Single trigger-heavy SKILL.md plus three references (versions, schemas, parsing-and-errors) covering both Zod v3 and v4 with inline migration callouts. Key v3 → v4 differences surfaced: - err.format() / err.flatten() → z.treeifyError() / z.flattenError() - separate { message, errorMap } → unified { error } param - z.lazy + ZodType<T> annotation → property getters for recursion - .superRefine() (deprecated) → .check() - new in v4: z.codec, z.prettifyError, .overwrite, zod/mini No MCP server (zod has no official docs MCP server). Skills-only plugin with skills: ['./skills/']. * chore: register zod plugin in marketplace and release-please - .claude-plugin/marketplace.json: add zod entry pointing to ./plugins/zod - release-please-config.json: add plugins/zod with simple release-type and extra-files jsonpath $.version for plugin.json bumps - README.md: append Zod section after Portless mirroring existing entries * feat(zod): use ask CLI as primary source for version-accurate docs Mirrors the use-better-auth pattern: lead with `ask src zod` / `ask docs zod` to read the lockfile-pinned source (with comments and tests), demoting node_modules/zod/dist to a fallback for environments where ask isn't installed. - New 'Finding Documentation' section in SKILL.md showing the SRC=$(ask src zod) idiom for reading docs, verifying symbols, and finding canonical examples in tests - versions.md 'When in doubt' switched to ask-first verification with explicit @Version pinning examples for v4.3.6 and v3.25.76 - 'When typecheck or runtime fails' debug snippet now greps $(ask src zod)/packages/zod/src instead of node_modules/zod/dist * fix(zod): correct .superRefine vs .check guidance The v4 docs (api.mdx@v4.3.6) present .superRefine() as the standard recommended API for multi-issue refinements, with .check() as a lower-level, more verbose alternative for performance-sensitive paths. The skill had this inverted in 6 places, claiming .superRefine was deprecated. - SKILL.md: rewrite the 'training-data miswritten' bullet to describe .superRefine as recommended, .check as lower-level - SKILL.md: drop the bogus 'flagged as deprecated' debug bullet - SKILL.md: include both .superRefine and .check (low-level) in the v4 row of the version-detection table - versions.md: rewrite the multi-issue refinement cheatsheet row - versions.md: rewrite the 'New in v4' bullet for .check - schemas.md: invert the multi-issue refinement example so .superRefine leads as canonical, .check follows as lower-level - schemas.md: drop the 'avoid .superRefine in v4' bullet entirely Caught by review-documentation-analyzer in /review:code-review-loop iteration 1. * chore: apply AI code review suggestions Apply 3 of 4 review thread suggestions on PR #168: - plugin.json: author restructured to upstream-org form ('Zod' / https://github.com/colinhacks/zod) per the mastra and better-auth precedent (@gemini-code-assist) - SKILL.md: Zod 4 release year corrected from 2026 to 2025 (v4.0.1 published 2025-07-10) (@cubic-dev-ai) - versions.md: recursive-schema example fixed — replaced undefined 'Self' placeholder with a self-referencing 'Tree' variable so the snippet works as written (@cubic-dev-ai) Skipped: @cubic-dev-ai's claim that '.loose()' is not a v4 object API. Verified false against zod@v4.3.6 classic/schemas.ts:1269 ('inst.loose = () => inst.clone(...)'). The .loose() instance method exists alongside z.looseObject({...}) — both are valid.
1 parent 90e363d commit 95319ce

8 files changed

Lines changed: 979 additions & 0 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,14 @@
600600
"keywords": ["portless", "localhost", "dev-server", "proxy"],
601601
"tags": ["tooling", "dev-server"],
602602
"source": "./plugins/portless"
603+
},
604+
{
605+
"name": "zod",
606+
"description": "TypeScript-first schema validation with static type inference - version-aware skill for Zod v3 and v4",
607+
"category": "development",
608+
"keywords": ["zod", "validation", "schema", "typescript"],
609+
"tags": ["validation", "typescript"],
610+
"source": "./plugins/zod"
603611
}
604612
]
605613
}

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ Replace port numbers with stable, named local URLs. For humans and agents.
302302

303303
**Install:** `/plugin install portless@pleaseai` | **Source:** [plugins/portless](https://github.com/pleaseai/claude-code-plugins/tree/main/plugins/portless)
304304

305+
#### Zod
306+
TypeScript-first schema validation with static type inference — version-aware skill covering Zod v3 and v4 differences (entry points, error formatting, refinements).
307+
308+
**Install:** `/plugin install zod@pleaseai` | **Source:** [plugins/zod](https://github.com/pleaseai/claude-code-plugins/tree/main/plugins/zod)
309+
305310
## Quick Start
306311

307312
The fastest way to get started — install the marketplace and let the plugin recommender auto-detect what you need:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "zod",
3+
"version": "1.0.0",
4+
"description": "TypeScript-first schema validation with static type inference - version-aware skill for Zod v3 and v4",
5+
"author": {
6+
"name": "Zod",
7+
"url": "https://github.com/colinhacks/zod"
8+
},
9+
"homepage": "https://zod.dev",
10+
"repository": "https://github.com/colinhacks/zod",
11+
"license": "MIT",
12+
"keywords": [
13+
"zod",
14+
"validation",
15+
"schema",
16+
"typescript"
17+
],
18+
"skills": [
19+
"./skills/"
20+
]
21+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
name: use-zod
3+
description: 'Answer questions about the Zod schema validation library and help build schemas, parsers, refinements, transforms, codecs, and error formatters. Use when developers: (1) ask about Zod APIs like `z.object`, `z.string`, `z.array`, `z.union`, `z.discriminatedUnion`, `parse`, `safeParse`, `z.infer`; (2) define request/response/form schemas in TypeScript; (3) handle `ZodError` or customize error messages; (4) migrate between Zod v3 and v4 (entry-point split, `formatError` → `treeifyError`/`prettifyError`, unified `error` param replacing `message`/`errorMap`). Triggers on: "zod", "z.object", "z.string", "z.array", "z.union", "z.infer", "z.input", "z.output", "ZodError", "$ZodError", "safeParse", "parseAsync", "z.codec", "treeifyError", "prettifyError", "flattenError", "discriminatedUnion", "zod/v4", "zod/v3", "zod/mini", "z.coerce", "superRefine".'
4+
---
5+
6+
## Prerequisites
7+
8+
Verify the `ask` CLI is available (`which ask`). It is the primary tool for reading the exact version installed in this project — it resolves the version from the lockfile, fetches docs/source once, and caches them at `~/.ask/`. If `ask` is not installed, fall back to `node_modules/zod/` and the official site at https://zod.dev (which tracks the latest published v4, not necessarily the installed version).
9+
10+
Before writing Zod code, verify the installed version and entry points:
11+
12+
```bash
13+
# installed version — drives everything below
14+
cat node_modules/zod/package.json 2>/dev/null | jq -r .version
15+
16+
# subpath exports — confirms which import paths resolve (zod, zod/mini, zod/v3, zod/v4)
17+
cat node_modules/zod/package.json 2>/dev/null | jq '.exports | keys'
18+
```
19+
20+
If `zod` is missing, install only what the task requires:
21+
22+
```bash
23+
# v4 (current default since zod@4.0.0)
24+
pnpm add zod # or: bun add zod / npm i zod / yarn add zod
25+
26+
# pin to v3 only when the project explicitly requires it
27+
pnpm add zod@^3
28+
```
29+
30+
Detect the package manager from the lockfile (`pnpm-lock.yaml` → pnpm, `bun.lockb` → bun, `package-lock.json` → npm, `yarn.lock` → yarn).
31+
32+
## Critical: Do Not Trust Internal Knowledge
33+
34+
Zod 4 (released 2025) was a major rewrite. Many APIs that were canonical in v3 are now deprecated, renamed, or removed. Examples that are commonly miswritten from training data:
35+
36+
- `err.format()` / `err.flatten()` (v3 instance methods) — in v4 these are top-level functions: `z.treeifyError(err)` / `z.flattenError(err)`. `z.formatError()` exists but is **deprecated** in favour of `z.treeifyError()`.
37+
- `z.string({ message, errorMap })` (v3) — v4 unifies these into a single `error` param: `z.string({ error: "Bad!" })` or `z.string({ error: (iss) => "..." })`.
38+
- `.superRefine()` is **still the recommended** v4 API for multi-issue refinements. `.check()` exists as a lower-level, more verbose alternative for performance-sensitive paths — not as a replacement.
39+
- `error instanceof z.ZodError` — works for the regular `zod` package; for `zod/mini` use `error instanceof z.core.$ZodError` (the parent class).
40+
- Codecs (`z.codec(...)`) — only exist in `zod@4.1+`. Do not suggest them on v3 or earlier 4.x.
41+
42+
When working with Zod:
43+
44+
1. Resolve the installed version against the local checkout with `ask` (see [Finding Documentation](#finding-documentation) below).
45+
2. Verify every API name, method signature, and option shape against the source or bundled `.d.ts` before generating code. Never invent method names.
46+
3. Cross-reference upstream docs **at the matching version pin** ([`references/versions.md`](references/versions.md) has the v4.3.6 / v3.25.76 links) — not `main`, which tracks the latest release.
47+
4. Run typecheck after every change. Zod schemas are heavily inferred and silent type drift is rare.
48+
5. Surface API trade-offs to the user instead of silently emitting either pattern (e.g. `.superRefine` is recommended in v4; `.check()` is a lower-level alternative for performance-sensitive paths — clarify when relevant).
49+
50+
If documentation cannot be found locally or remotely to back an answer, say so explicitly.
51+
52+
## Finding Documentation
53+
54+
Resolve the source checkout and docs directory once with `ask`; reuse the paths across reads:
55+
56+
```bash
57+
SRC=$(ask src zod) # checkout root
58+
DOCS=$(ask docs zod | head -n1) # candidate docs dir
59+
```
60+
61+
Both pin to the version in the project's lockfile. To inspect a specific version regardless of the project, append `@version`:
62+
63+
```bash
64+
SRC_V4=$(ask src zod@4.3.6)
65+
SRC_V3=$(ask src zod@3.25.76)
66+
```
67+
68+
### Read the README and docs content
69+
70+
```bash
71+
cat "$DOCS/README.md"
72+
ls "$SRC/packages/docs/content" # v4 docs source (mdx)
73+
cat "$SRC/packages/docs/content/api.mdx" # full API reference
74+
cat "$SRC/packages/docs/content/error-formatting.mdx"
75+
cat "$SRC/packages/docs/content/error-customization.mdx"
76+
cat "$SRC/packages/docs/content/codecs.mdx" # v4.1+ only
77+
```
78+
79+
### Verify a symbol exists in the installed version
80+
81+
```bash
82+
# top-level functions (v4): treeifyError, prettifyError, flattenError, codec, config
83+
rg -n "^export (function|const) (treeifyError|prettifyError|flattenError|codec|config)\\b" "$SRC/packages/zod/src"
84+
85+
# instance methods on schemas
86+
rg -n "(\\.refine|\\.check|\\.superRefine|\\.overwrite|\\.transform|\\.parseAsync)\\b" "$SRC/packages/zod/src"
87+
88+
# subpath exports
89+
cat "$SRC/packages/zod/package.json" | jq '.exports | keys'
90+
```
91+
92+
### Find canonical example shapes (tests are the most reliable source)
93+
94+
```bash
95+
fd -e test.ts . "$SRC/packages/zod/tests"
96+
rg -n "discriminatedUnion|z\\.codec|treeifyError" "$SRC/packages/zod/tests"
97+
```
98+
99+
### Fallback when `ask` is unavailable
100+
101+
```bash
102+
SRC=./node_modules/zod
103+
ls $SRC/dist
104+
rg "treeifyError" $SRC/dist # confirm v4 helpers shipped in this build
105+
cat $SRC/package.json | jq .version
106+
```
107+
108+
Use https://zod.dev only to cross-reference — it always tracks the latest published v4.
109+
110+
## Version detection — branch v4 vs v3 paths
111+
112+
```bash
113+
node -e "const v=require('zod/package.json').version; console.log(v.startsWith('4.')?'v4':v.startsWith('3.')?'v3':v)"
114+
```
115+
116+
| Detected | Default import | Errors API | Refinement API | Codecs |
117+
| --- | --- | --- | --- | --- |
118+
| v4 (≥4.0.0) | `import * as z from "zod"` | `z.treeifyError`, `z.prettifyError`, `z.flattenError` | `.refine()`, `.superRefine()`, `.check()` (low-level) | `z.codec()` (4.1+) |
119+
| v3 (≥3.0, <4.0) | `import { z } from "zod"` | `err.format()`, `err.flatten()` | `.refine()`, `.superRefine()` ||
120+
| v3.25.x bridge | `import * as z from "zod/v4"` opt-in to v4 alongside v3 | per the chosen path | per the chosen path ||
121+
122+
`zod@3.25` shipped both v3 (default) and v4 (under `zod/v4`) in the same package to ease migration. From `zod@4.0.0` onward, the root export is v4 and `zod/v3` is the back-compat path. See [`references/versions.md`](references/versions.md).
123+
124+
## Entry points (v4)
125+
126+
| Import | Use when |
127+
| --- | --- |
128+
| `import * as z from "zod"` | Default. Standard ergonomic API with chainable methods (`z.string().min(5)`). |
129+
| `import * as z from "zod/mini"` | Bundle-size-sensitive frontend code. Functional API: `z.string().check(z.minLength(5))`. ~64% smaller for trivial schemas. |
130+
| `import * as z from "zod/v3"` | Legacy code on v3 that you can't migrate yet, while consuming a `zod@4` package. |
131+
| `import * as z from "zod/v4-mini"` (within `zod@3.25`) | Forward-compat path for projects pinned to v3 that want to start adopting Mini. |
132+
133+
`zod/mini` and `zod` interop: schemas from one cannot be passed to the other's parse functions. Pick one per project unless you have a deliberate reason to mix.
134+
135+
## Authoring schemas
136+
137+
Concise cookbook of common patterns, each tagged with the version it applies to: [`references/schemas.md`](references/schemas.md).
138+
139+
Rules of thumb:
140+
141+
- **`z.object` is non-strict by default** — extra keys are stripped. Use `z.strictObject({...})` to reject extra keys, or `.passthrough()` (v3) / `.loose()` (v4) to preserve them.
142+
- **`.optional()` vs `.nullable()` vs `.nullish()`**`optional` allows `undefined`, `nullable` allows `null`, `nullish` allows both.
143+
- **Always export the type** with `z.infer<typeof Schema>`. Use `z.input` and `z.output` separately when the schema transforms (input ≠ output, e.g. `z.string().transform(s => s.length)`).
144+
- **Discriminated unions need a literal discriminator**`z.discriminatedUnion("type", [...])` is dramatically faster and produces better error messages than `z.union(...)` when shapes share a tag field.
145+
- **Recursive schemas** use different patterns per version: v4 uses object property **getters** (`get children() { return z.array(Self); }`); v3 uses `z.lazy(() => Schema)` plus an explicit `z.ZodType<Node>` annotation. See [`references/schemas.md`](references/schemas.md#recursive-schemas).
146+
147+
## Parsing & error handling
148+
149+
Concise reference: [`references/parsing-and-errors.md`](references/parsing-and-errors.md).
150+
151+
Quick map:
152+
153+
- `parse(input)` — throws on invalid; returns typed deep clone.
154+
- `safeParse(input)` — returns `{ success: true, data } | { success: false, error: ZodError }`.
155+
- `parseAsync` / `safeParseAsync` — required when the schema contains async refinements, transforms, or codecs.
156+
- `try { ... } catch (e) { if (e instanceof z.ZodError) e.issues }` — every error has an `.issues` array of `{ code, path, message, expected?, ... }`.
157+
158+
The `formatError``treeifyError` rename is the single most common source of broken v3-era examples. Surface it whenever rewriting v3 error-handling code.
159+
160+
## When typecheck or runtime fails
161+
162+
Before searching source code, check the most common Zod failure modes:
163+
164+
1. **`Invalid input: expected X`** with no `path` — top-level shape mismatch; verify the schema matches the expected outer type.
165+
2. **`Cannot read property 'parseAsync' of undefined`** — usually an import-path mismatch (`zod` vs `zod/mini`); methods on Mini schemas live on top-level functions instead.
166+
3. **`Type 'ZodError' is not assignable to type '$ZodError'`** — mixing `zod` and `zod/mini` schemas in the same code path.
167+
4. **Async refinement throws "Synchronous parsing not supported"** — switch the call site from `parse` to `parseAsync` (or `safeParse` to `safeParseAsync`).
168+
5. **Custom error not surfacing** — confirm you're using the unified `error` param (v4) and not the legacy `message`/`errorMap` shape (v3).
169+
170+
If the symptom is not listed, resolve the source and grep the error string:
171+
172+
```bash
173+
rg -n "error string fragment" "$(ask src zod)/packages/zod/src"
174+
# fallback: rg -n "error string fragment" node_modules/zod/dist
175+
```
176+
177+
## References
178+
179+
- [`references/versions.md`](references/versions.md) — entry points, version detection, v3 ↔ v4 API rename cheatsheet, links to upstream docs at version pins (v4.3.6, v3.25.76)
180+
- [`references/schemas.md`](references/schemas.md) — primitives, objects, arrays, unions, discriminated unions, recursion, refinements, transforms, codecs (v4.1+) — each example tagged `// v4`, `// v3`, or `// both`
181+
- [`references/parsing-and-errors.md`](references/parsing-and-errors.md)`parse` vs `safeParse` vs `parseAsync`, `ZodError` shape, `treeifyError`/`prettifyError`/`flattenError` (v4), `format`/`flatten` (v3), error customization

0 commit comments

Comments
 (0)