>- Use the frozen-class/visitor pattern for discriminated unions that have multiple dispatch sites. Use when creating a new set of variants (commands, IR nodes, factory calls) that will be switched over in 2+ places, or when refactoring an existing union type that has grown multiple switch sites.
npx skills add https://github.com/prisma/prisma --skill ast-visitor-pattern
When a discriminated union has 3+ variants and 2+ dispatch sites (renderers, serializers, classifiers, etc.), replace plain union + switch with frozen subclasses and a visitor interface. This makes adding a new variant a compiler error at every consumer, instead of a silent omission.
Four pieces, always in the same file:
// 1. Abstract base (not exported — consumers use the union type)
abstract class FooNode {
abstract readonly kind: string;
abstract accept<R>(visitor: FooVisitor<R>): R;
protected freeze(): void { Object.freeze(this); }
}
// 2. Visitor interface
export interface FooVisitor<R> {
bar(node: BarNode): R;
baz(node: BazNode): R;
}
// 3. Concrete subclasses — readonly fields, freeze() in constructor
export class BarNode extends FooNode {
readonly kind = 'bar' as const;
readonly value: string;
constructor(value: string) {
super();
this.value = value;
this.freeze();
}
accept<R>(visitor: FooVisitor<R>): R { return visitor.bar(this); }
}
export class BazNode extends FooNode {
readonly kind = 'baz' as const;
readonly count: number;
constructor(count: number) {
super();
this.count = count;
this.freeze();
}
accept<R>(visitor: FooVisitor<R>): R { return visitor.baz(this); }
}
// 4. Union type
export type Foo = BarNode | BazNode;
Define a visitor object (or class) per concern:
const renderVisitor: FooVisitor<string> = {
bar(node) { return node.value; },
baz(node) { return String(node.count); },
};
function render(node: Foo): string {
return node.accept(renderVisitor);
}
This holds everywhere a node is built — tests and production construction surfaces (contract-free factories, builders). A factory must return new BarNode(...), never Object.freeze({ kind: 'bar', value: 'x' }). A frozen plain object has no prototype, so instanceof fails, accept() is missing, and a downstream shallow-copy ({ ...node }) silently strips the type back to an anonymous bag; constructor-time invariants are skipped too.
// ✅
const call = new BarNode('x');
export function bar(value: string): BarNode { return new BarNode(value); }
// ❌
const call: Foo = { kind: 'bar', value: 'x' };
export function bar(value: string): Foo { return Object.freeze({ kind: 'bar', value }); }
MongoAstNode / MongoDdlCommandVisitor — packages/2-mongo-family/4-query/query-ast/src/ddl-commands.tsOpFactoryCall / OpFactoryCallVisitor — packages/3-mongo-target/1-mongo-target/src/core/op-factory-call.tsUse when receiving code review feedback, before implementing suggestions, especially if feedback seems unclear or technically questionable - requires technical rigor and verification, not performative agreement or blind implementation
Use when completing tasks, implementing major features, or before merging to verify work meets requirements
Execute git commit with conventional commit message analysis, intelligent staging, and message generation. Use when user asks to commit changes, create a git commit, or mentions "/commit". Supports: (1) Auto-detecting type and scope from changes, (2) Generating conventional commit messages from diff, (3) Interactive commit with optional type/scope/description overrides, (4) Intelligent file staging for logical grouping
Comprehensive GitHub code review with AI-powered swarm coordination
Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
Use this skill to review code. It supports both local changes (staged or working tree) and remote Pull Requests (by ID or URL). It focuses on correctness, maintainability, and adherence to project standards.
Refactor bloated AGENTS.md, CLAUDE.md, or similar agent instruction files to follow progressive disclosure principles. Splits monolithic files into organized, linked documentation.
Create high-quality git commits: review/stage intended changes, split into logical commits, and write clear commit messages (including Conventional Commits). Use when the user asks to commit, craft a commit message, stage changes, or split work into multiple commits.
Take prisma/ast-visitor-pattern from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.