Mastra Hono
jwynia/agent-skills
Develop AI agents, tools, and workflows with Mastra v1 Beta and Hono servers.
Zod schema validation, type-safe development, and strict TypeScript patterns.
$ npx skills add shepherdjerred/monorepo --skill zod-patterns -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install shepherdjerred/monorepo zod-patterns --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .claude/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/zod-patterns .claude/skills/zod-patterns && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "zod-patterns" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/zod-patterns into .claude/skills/zod-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "zod-patterns", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/zod-patternsType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add shepherdjerred/monorepo --skill zod-patterns -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install shepherdjerred/monorepo zod-patterns --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .agents/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/zod-patterns .agents/skills/zod-patterns && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "zod-patterns" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/zod-patterns into .agents/skills/zod-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "zod-patterns", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add shepherdjerred/monorepo --skill zod-patterns -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install shepherdjerred/monorepo zod-patterns --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/zod-patterns .cursor/skills/zod-patterns && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "zod-patterns" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/zod-patterns into .cursor/skills/zod-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "zod-patterns", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/shepherdjerred/monorepo.git --path packages/dotfiles/dot_agents/skills/zod-patterns--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add shepherdjerred/monorepo --skill zod-patterns -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install shepherdjerred/monorepo zod-patterns --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/zod-patterns .gemini/skills/zod-patterns && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "zod-patterns" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/zod-patterns into .gemini/skills/zod-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "zod-patterns", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install shepherdjerred/monorepo zod-patternsInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add shepherdjerred/monorepo --skill zod-patterns -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .github/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/zod-patterns .github/skills/zod-patterns && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "zod-patterns" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/zod-patterns into .github/skills/zod-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "zod-patterns", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add shepherdjerred/monorepo --skill zod-patterns -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install shepherdjerred/monorepo zod-patterns --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/zod-patterns .opencode/skills/zod-patterns && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "zod-patterns" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/zod-patterns into .opencode/skills/zod-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "zod-patterns", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
zod-patternsZod schema validation, type-safe development, and strict TypeScript patterns.
Zod Patterns is an agent skill from shepherdjerred/monorepo. Zod schema validation, type-safe development, and strict TypeScript patterns. When user works with Zod, validates data, creates schemas, handles form validation, mentions z.object/z.string patterns, needs runtime validation, type-safe code, or strict TypeScript configuration.
Its SKILL.md is about 5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Frontend & Design, covering Forms and validation. It works with Zod and TypeScript. The repository describes itself as: Monorepo for all of my projects. The licence is GPL-3.0.
10 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 46ddf2f. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
bunFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
API_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Zod Patterns loads about 5k tokens when it runs. Until then it costs about 72 tokens; SKILL.md has 382 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from shepherdjerred/monorepo at commit 46ddf2f, republished under its GPL-3.0 licence (© shepherdjerred). 382 words, ~5,034 tokens.
.claude/skills/zod-patterns/SKILL.md (or your agent's skills folder).z.array() 7.4x faster, z.object() 6.5x faster (compared to Zod v3)z.looseObject(): Replaces .passthrough()z.strictObject(): Replaces .strict()# Standard Zod v4 (recommended)
bun add zod
# Minimal bundle for tree-shaking (1.9KB gzipped)
bun add @zod/miniRequires TypeScript 5.5+ with "strict": true in tsconfig.
// Tree-shakable imports (only includes what you use)
import { z } from "@zod/mini";
// Same API as full Zod
const UserSchema = z.object({
id: z.string(),
email: z.string().email(),
});
// Results in significantly smaller bundles
// Full Zod: ~5KB | @zod/mini: ~1.9KB (tree-shaken)For library authors targeting v4: import { z } from "zod/v4/core";
import { z } from "zod";
// Define a schema
const UserSchema = z.object({
name: z.string(),
email: z.string().email(),
age: z.number().int().positive(),
});
// Parse data (throws on invalid)
const user = UserSchema.parse({
name: "Alice",
email: "alice@example.com",
age: 25,
});
// Safe parse (returns result object)
const result = UserSchema.safeParse(data);
if (result.success) {
console.log(result.data); // typed as User
} else {
console.log(result.error.issues); // validation errors
}// Infer TypeScript type from schema
type User = z.infer<typeof UserSchema>;
// { name: string; email: string; age: number }
// For schemas with transforms - input vs output types
type UserInput = z.input<typeof UserSchema>;
type UserOutput = z.output<typeof UserSchema>;z.string(); // any string
z.string().min(1); // non-empty
z.string().max(100); // max length
z.string().length(5); // exact length
z.string().email(); // email format
z.string().url(); // URL format
z.string().uuid(); // UUID format
z.string().cuid(); // CUID format
z.string().cuid2(); // CUID2 format
z.string().ulid(); // ULID format
z.string().regex(/^[a-z]+$/); // custom regex
z.string().includes("@"); // contains substring
z.string().startsWith("http"); // starts with
z.string().endsWith(".com"); // ends with
z.string().datetime(); // ISO datetime
z.string().date(); // ISO date
z.string().time(); // ISO time
z.string().ip(); // IP address
z.string().trim(); // trim whitespace (transform)
z.string().toLowerCase(); // lowercase (transform)
z.string().toUpperCase(); // uppercase (transform)z.number(); // any number
z.number().int(); // integer only
z.number().positive(); // > 0
z.number().nonnegative(); // >= 0
z.number().negative(); // < 0
z.number().nonpositive(); // <= 0
z.number().min(5); // >= 5
z.number().max(100); // <= 100
z.number().gt(5); // > 5
z.number().gte(5); // >= 5 (alias for min)
z.number().lt(100); // < 100
z.number().lte(100); // <= 100 (alias for max)
z.number().multipleOf(5); // divisible by 5
z.number().finite(); // not Infinity
z.number().safe(); // within safe integer rangez.boolean(); // true or false
z.bigint(); // BigInt values
z.date(); // Date objects
z.undefined(); // undefined only
z.null(); // null only
z.void(); // undefined (for function returns)
z.any(); // bypass validation
z.unknown(); // any, but type-safe usage
z.never(); // always failsAutomatically convert input types before validation:
// String coercion
z.coerce.string(); // String(input)
z.coerce.number(); // Number(input)
z.coerce.boolean(); // Boolean(input)
z.coerce.bigint(); // BigInt(input)
z.coerce.date(); // new Date(input)
// With validation
const ageSchema = z.coerce.number().int().positive();
ageSchema.parse("25"); // 25
ageSchema.parse("abc"); // throws - NaN is not positive
// Common pitfall: empty string becomes 0
z.coerce.number().parse(""); // 0 (might not be desired)
// Fix: preprocess to handle empty strings
const safeNumber = z.preprocess(
(val) => (val === "" ? undefined : val),
z.coerce.number(),
);const PersonSchema = z.object({
name: z.string(),
age: z.number(),
email: z.string().email().optional(),
});
// By default, unknown keys are stripped
PersonSchema.parse({ name: "Bob", age: 30, extra: "ignored" });
// { name: "Bob", age: 30 }// Standard - strips unknown keys
z.object({ name: z.string() });
// Loose - passes through unknown keys
z.looseObject({ name: z.string() });
// Strict - rejects unknown keys
z.strictObject({ name: z.string() });const UserSchema = z.object({
id: z.string().uuid(),
name: z.string(),
email: z.string().email(),
password: z.string(),
createdAt: z.date(),
});
// Pick specific fields
const PublicUser = UserSchema.pick({ id: true, name: true, email: true });
// Omit sensitive fields
const SafeUser = UserSchema.omit({ password: true });
// Make all fields optional
const PartialUser = UserSchema.partial();
// Make specific fields optional
const UpdateUser = UserSchema.partial({ name: true, email: true });
// Make all fields required
const RequiredUser = UserSchema.required();
// Extend with additional fields
const AdminSchema = UserSchema.extend({
role: z.literal("admin"),
permissions: z.array(z.string()),
});
// Merge schemas (Zod 4 - use extend instead)
const Combined = BaseSchema.extend(ExtraSchema.shape);const AddressSchema = z.object({
street: z.string(),
city: z.string(),
country: z.string(),
zip: z.string().optional(),
});
const CompanySchema = z.object({
name: z.string(),
address: AddressSchema,
employees: z.array(
z.object({
name: z.string(),
department: z.string(),
}),
),
});z.array(z.string()); // string[]
z.array(z.string()).min(1); // at least 1 element
z.array(z.string()).max(10); // at most 10 elements
z.array(z.string()).length(5); // exactly 5 elements
z.array(z.string()).nonempty(); // same as .min(1)
// Access element schema
const arr = z.array(z.string());
arr.element; // z.string()// Fixed-length array with specific types
const PointSchema = z.tuple([z.number(), z.number()]);
type Point = z.infer<typeof PointSchema>; // [number, number]
// With rest elements
const ArgsSchema = z.tuple([z.string(), z.number()]).rest(z.boolean());
// [string, number, ...boolean[]]// Record<string, T>
z.record(z.string(), z.number()); // { [key: string]: number }
z.record(z.number()); // shorthand for string keys
// Map<K, V>
z.map(z.string(), z.number()); // Map<string, number>
// Set<T>
z.set(z.string()); // Set<string>
z.set(z.number()).min(1).max(10); // with size constraints// Basic union
const StringOrNumber = z.union([z.string(), z.number()]);
type SN = z.infer<typeof StringOrNumber>; // string | number
// Shorthand
const StringOrNumber2 = z.string().or(z.number());// More efficient parsing with discriminator key
const ResultSchema = z.discriminatedUnion("status", [
z.object({
status: z.literal("success"),
data: z.object({ id: z.string() }),
}),
z.object({
status: z.literal("error"),
message: z.string(),
}),
]);
type Result = z.infer<typeof ResultSchema>;
// { status: "success"; data: { id: string } } | { status: "error"; message: string }
// TypeScript narrows based on discriminator
const result = ResultSchema.parse(data);
if (result.status === "success") {
console.log(result.data.id); // typed correctly
}// Zod enum (recommended)
const StatusSchema = z.enum(["pending", "active", "inactive"]);
type Status = z.infer<typeof StatusSchema>; // "pending" | "active" | "inactive"
// Access values
StatusSchema.options; // ["pending", "active", "inactive"]
StatusSchema.enum; // { pending: "pending", active: "active", ... }
// Extract or exclude values
StatusSchema.extract(["pending", "active"]); // only these
StatusSchema.exclude(["inactive"]); // all except these
// Native enum
enum NativeStatus {
Pending,
Active,
Inactive,
}
const NativeStatusSchema = z.nativeEnum(NativeStatus);z.literal("hello"); // exactly "hello"
z.literal(42); // exactly 42
z.literal(true); // exactly true
z.null(); // exactly null
z.undefined(); // exactly undefined// Optional - allows undefined
z.string().optional(); // string | undefined
// Nullable - allows null
z.string().nullable(); // string | null
// Nullish - allows null or undefined
z.string().nullish(); // string | null | undefined
// Default values
z.string().default("N/A"); // static default
z.number().default(() => Math.random()); // dynamic default
z.date().default(() => new Date()); // current date
// Catch - use default on any parse error
z.number().catch(0); // returns 0 on error
z.string().catch((ctx) => `Error: ${ctx.error.message}`);
// Unwrap optional/nullable
const optStr = z.string().optional();
optStr.unwrap(); // z.string()// Basic refine
const PasswordSchema = z
.string()
.min(8, "Password must be at least 8 characters")
.refine((val) => /[A-Z]/.test(val), "Password must contain uppercase letter")
.refine((val) => /[0-9]/.test(val), "Password must contain number");
// With custom error path
const FormSchema = z
.object({
password: z.string(),
confirmPassword: z.string(),
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ["confirmPassword"], // error appears on this field
});const ComplexSchema = z.string().superRefine((val, ctx) => {
if (val.length < 8) {
ctx.addIssue({
code: z.ZodIssueCode.too_small,
minimum: 8,
type: "string",
inclusive: true,
message: "Too short",
});
}
if (!/[A-Z]/.test(val)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Missing uppercase",
});
}
});// Transform output type
const TrimmedString = z.string().transform((val) => val.trim());
const NumberFromString = z
.string()
.transform((val) => parseInt(val, 10))
.pipe(z.number().int());
const DateFromISO = z
.string()
.datetime()
.transform((val) => new Date(val));
// Transform with validation
const PositiveFromString = z
.string()
.transform((val, ctx) => {
const num = parseInt(val, 10);
if (isNaN(num)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Not a number",
});
return z.NEVER;
}
return num;
})
.pipe(z.number().positive());// Transform BEFORE parsing
const TrimmedEmail = z.preprocess(
(val) => (typeof val === "string" ? val.trim().toLowerCase() : val),
z.string().email(),
);
// Handle empty strings
const OptionalNumber = z.preprocess(
(val) => (val === "" ? undefined : val),
z.coerce.number().optional(),
);// Chain multiple schemas
const ParsedInt = z
.string()
.transform((val) => parseInt(val, 10))
.pipe(z.number().int().positive());
// With intermediate validation
const EmailDomain = z
.string()
.email()
.pipe(z.string().endsWith("@company.com"));// Per-validator messages
z.string().min(5, "Must be at least 5 characters");
z.string().min(5, { message: "Too short" });
// Schema-level errors
z.string("Expected a string");
z.string({
error: (issue) =>
issue.input === undefined ? "Field is required" : "Must be a string",
});const result = UserSchema.safeParse(input);
if (!result.success) {
// Formatted errors
const formatted = result.error.format();
// { name: { _errors: ["Required"] }, email: { _errors: ["Invalid email"] } }
// Flattened errors
const flat = result.error.flatten();
// { formErrors: [], fieldErrors: { name: ["Required"], email: ["Invalid email"] } }
// Raw issues
result.error.issues.forEach((issue) => {
console.log(issue.path, issue.message, issue.code);
});
}// Per-parse error customization
schema.parse(data, {
error: (issue) => `Custom error: ${issue.code}`,
});
// Global configuration
z.config({
customError: (issue) => {
if (issue.code === "invalid_type") {
return `Expected ${issue.expected}, got ${issue.received}`;
}
return issue.message;
},
});
// Localization
import { en } from "zod/v4/locales/en";
z.config(en());const UserId = z.string().uuid().brand<"UserId">();
const PostId = z.string().uuid().brand<"PostId">();
type UserId = z.infer<typeof UserId>;
type PostId = z.infer<typeof PostId>;
function getUser(id: UserId) {
/* ... */
}
const userId = UserId.parse("550e8400-e29b-41d4-a716-446655440000");
const postId = PostId.parse("550e8400-e29b-41d4-a716-446655440000");
getUser(userId); // OK
getUser(postId); // Type error!interface Category {
name: string;
subcategories: Category[];
}
const CategorySchema: z.ZodType<Category> = z.lazy(() =>
z.object({
name: z.string(),
subcategories: z.array(CategorySchema),
}),
);// Generic JSON value
const JsonValue: z.ZodType<unknown> = z.lazy(() =>
z.union([
z.string(),
z.number(),
z.boolean(),
z.null(),
z.array(JsonValue),
z.record(JsonValue),
]),
);const FnSchema = z.function().args(z.string(), z.number()).returns(z.boolean());
type Fn = z.infer<typeof FnSchema>;
// (args_0: string, args_1: number) => boolean
const validated = FnSchema.implement((name, age) => {
return age > 18;
});const UniqueEmailSchema = z
.string()
.email()
.refine(async (email) => {
const exists = await checkEmailExists(email);
return !exists;
}, "Email already taken");
// Must use async parse
const result = await UniqueEmailSchema.safeParseAsync("test@example.com");import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
const FormSchema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(8, "At least 8 characters"),
});
function MyForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(FormSchema),
});
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("email")} />
{errors.email && <span>{errors.email.message}</span>}
{/* ... */}
</form>
);
}// Hono example
import { zValidator } from "@hono/zod-validator";
const CreateUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
app.post("/users", zValidator("json", CreateUserSchema), async (c) => {
const data = c.req.valid("json");
// data is fully typed
});const EnvSchema = z.object({
DATABASE_URL: z.string().url(),
PORT: z.coerce.number().default(3000),
NODE_ENV: z
.enum(["development", "production", "test"])
.default("development"),
API_KEY: z.string().min(1),
});
export const env = EnvSchema.parse(process.env);Prefer Zod validation over typeof, instanceof, or type guard functions.
// ❌ Avoid typeof operator
if (typeof value === "string") {
return value.toUpperCase();
}
// ✅ Prefer Zod validation
const result = z.string().safeParse(value);
if (result.success) {
return result.data.toUpperCase();
}
// ❌ Avoid instanceof
if (err instanceof Error) {
console.log(err.message);
}
// ✅ Prefer Zod
const result = z.instanceof(Error).safeParse(err);
if (result.success) {
console.log(result.data.message);
}
// ❌ Avoid Number.isInteger
if (Number.isInteger(value)) {
return value * 2;
}
// ✅ Prefer Zod
const result = z.number().int().safeParse(value);
if (result.success) {
return result.data * 2;
}// ❌ Avoid type guard functions
function isUser(value: unknown): value is User {
return typeof value === "object" && value !== null && "email" in value;
}
// ✅ Prefer Zod schema validation
const UserSchema = z.object({
email: z.string().email(),
name: z.string(),
});
const result = UserSchema.safeParse(value);
if (result.success) {
const user = result.data; // Type-safe!
}Only as unknown and as const are allowed — all other as casts are banned.
// ❌ Never do this - bypasses type safety
const user = data as User;
const id = value as string;
// ✅ Cast to unknown first, then validate
const data = response as unknown;
const result = UserSchema.safeParse(data);
if (result.success) {
const user = result.data;
}
// ✅ Use 'as const' for literal types
const STATUSES = ["pending", "approved", "rejected"] as const;
type Status = (typeof STATUSES)[number];Why no type assertions? They bypass TypeScript's type checking, don't perform runtime validation, can cause runtime crashes, and hide bugs instead of catching them. Zod gives you both compile-time AND runtime safety.
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noPropertyAccessFromIndexSignature": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noFallthroughCasesInSwitch": true,
"forceConsistentCasingInFileNames": true
}
}// ✅ Prefer type
type User = {
id: string;
email: string;
};
type Admin = User & {
permissions: string[];
};
// ❌ Avoid interface
interface User {
id: string;
email: string;
}Why? Types are more flexible (unions, intersections) and consistent with Zod's inferred types.
safeParse() - avoid throwing in production codeextend, pick, omit for reuse© shepherdjerred, GPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in packages/dotfiles/dot_agents/skills/zod-patterns of shepherdjerred/monorepo.
Open the folder on GitHubat commit 46ddf2f
Zod Patterns next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Zod Patterns this skillshepherdjerred/monorepo | 112 | — | ~5k | Automated safety check: Pass | GPL-3.0 | |
| Mastra Honojwynia/agent-skills | 165 | — | ~2.9k | Automated safety check: Pass | MIT | |
| Desloppifydatabuddy-analytics/Databuddy | 1.2k | — | ~2.7k | Automated safety check: Pass | AGPL-3.0 | |
| Zod Validation Expertdavila7/claude-code-templates | 32k | 3 repos | ~2.4k | Automated safety check: Pass | MIT | |
| Shadcn UIjh941213/my-cc-harness | 126 | 4 repos | ~9.7k | Automated safety check: Notes | MIT | |
| Typescript Patternssoftspark/ai-toolkit | 179 | — | ~1.6k | Automated safety check: Pass | Apache-2.0 |
jwynia/agent-skills
Develop AI agents, tools, and workflows with Mastra v1 Beta and Hono servers.
databuddy-analytics/Databuddy
Reduce codebase slop by deleting code, flattening abstractions, and replacing custom helpers/types/assertions with native SDK/npm helpers or straightforward schemas (for example Zod).
davila7/claude-code-templates
Expert in Zod — TypeScript-first schema validation. An agent skill from davila7/claude-code-templates.
jh941213/my-cc-harness
Complete shadcn/ui component library guide including installation, configuration, and implementation of accessible React components.
softspark/ai-toolkit
TypeScript types: generics, discriminated unions, Zod, satisfies, branded types.
bobmatnyc/claude-mpm
Advanced TypeScript patterns and best practices for 2025. An agent skill from bobmatnyc/claude-mpm.
shepherdjerred/monorepo
Bun runtime APIs and current operational patterns for files, processes, modules, networking, databases, tests, and deployment.
shepherdjerred/monorepo
Current Bun test runner guidance for discovery, isolation, parallelism, sharding, changed tests, mocks, timers, snapshots, coverage, DOM Testing Library, and integration teardown.
shepherdjerred/monorepo
Current Bun workspace guidance for isolated and hoisted linkers, catalogs, filters, scripts, dependency classes, lockfiles, lifecycle trust, caches, publishing, TypeScript package exports, and…
shepherdjerred/monorepo
This skill should be used when the user asks to "deep research", "research this topic", "investigate thoroughly", "do a deep dive on", "comprehensive research on", "find everything about", "survey…
shepherdjerred/monorepo
This skill should be used when the user asks to "create a Figma design", "design in Figma", "make a Figma mockup", "create an app icon", "design UI", "render JSX to Figma", "export from Figma"…
shepherdjerred/monorepo
Current Fish shell scripting, functions, abbreviations, completions, variables, events, configuration, plugins, testing, and safety guidance.
Works with
Categories
Zod schema validation, type-safe development, and strict TypeScript patterns. Zod Patterns is an agent skill from shepherdjerred/monorepo. Zod schema validation, type-safe development, and strict TypeScript patterns.
Zod Patterns fits situations like: creates schemas; handles form validation; mentions z.object/z.string patterns; needs runtime validation.
Run `npx skills add shepherdjerred/monorepo --skill zod-patterns -a claude-code`. Or copy the skill folder (packages/dotfiles/dot_agents/skills/zod-patterns in shepherdjerred/monorepo) into .claude/skills/zod-patterns in your project. Claude Code loads it when a task matches its description.
Run `npx skills add shepherdjerred/monorepo --skill zod-patterns -a codex`. Or copy the skill folder (packages/dotfiles/dot_agents/skills/zod-patterns in shepherdjerred/monorepo) into .agents/skills/zod-patterns in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add shepherdjerred/monorepo --skill zod-patterns -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/zod-patterns, .gemini/skills/zod-patterns, .github/skills/zod-patterns and .opencode/skills/zod-patterns in your project.
Going by SKILL.md and its folder, Zod Patterns needs the command-line tools its instructions call (bun) and credentials named API_KEY.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Zod Patterns is published under the GPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5k tokens (SKILL.md is roughly 20k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Zod Patterns: Mastra Hono (jwynia/agent-skills, 165 stars), Desloppify (databuddy-analytics/Databuddy, 1.2k stars), Zod Validation Expert (davila7/claude-code-templates, 32k stars) and Shadcn UI (jh941213/my-cc-harness, 126 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
shepherdjerred (a GitHub user) maintains it in shepherdjerred/monorepo, which has 112 GitHub stars. The repository holds 63 skills in this directory. The repository was last updated on October 7, 2026.
Source: shepherdjerred/monorepo on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.