Drizzle Orm Expert
davila7/claude-code-templates
Expert in Drizzle ORM for TypeScript — schema design, relational queries, migrations, and serverless database integration.
Explains which of the three PSL syntax tree layers to use with Prisma's parser output, and how to move between them without hand-written walks.
$ npx skills add prisma/orm --skill psl-ast-layers -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install prisma/orm psl-ast-layers --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/prisma/orm.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills-contrib/psl-ast-layers .claude/skills/psl-ast-layers && 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 "psl-ast-layers" agent skill from https://github.com/prisma/orm/tree/main/skills-contrib/psl-ast-layers into .claude/skills/psl-ast-layers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "psl-ast-layers", 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/prisma/orm/tree/main/skills-contrib/psl-ast-layersType 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 prisma/orm --skill psl-ast-layers -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install prisma/orm psl-ast-layers --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/prisma/orm.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills-contrib/psl-ast-layers .agents/skills/psl-ast-layers && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "psl-ast-layers" agent skill from https://github.com/prisma/orm/tree/main/skills-contrib/psl-ast-layers into .agents/skills/psl-ast-layers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "psl-ast-layers", 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 prisma/orm --skill psl-ast-layers -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install prisma/orm psl-ast-layers --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/prisma/orm.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills-contrib/psl-ast-layers .cursor/skills/psl-ast-layers && 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 "psl-ast-layers" agent skill from https://github.com/prisma/orm/tree/main/skills-contrib/psl-ast-layers into .cursor/skills/psl-ast-layers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "psl-ast-layers", 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/prisma/orm.git --path skills-contrib/psl-ast-layers--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 prisma/orm --skill psl-ast-layers -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install prisma/orm psl-ast-layers --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/prisma/orm.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills-contrib/psl-ast-layers .gemini/skills/psl-ast-layers && 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 "psl-ast-layers" agent skill from https://github.com/prisma/orm/tree/main/skills-contrib/psl-ast-layers into .gemini/skills/psl-ast-layers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "psl-ast-layers", 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 prisma/orm psl-ast-layersInstalls 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 prisma/orm --skill psl-ast-layers -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/prisma/orm.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills-contrib/psl-ast-layers .github/skills/psl-ast-layers && 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 "psl-ast-layers" agent skill from https://github.com/prisma/orm/tree/main/skills-contrib/psl-ast-layers into .github/skills/psl-ast-layers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "psl-ast-layers", 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 prisma/orm --skill psl-ast-layers -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install prisma/orm psl-ast-layers --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/prisma/orm.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills-contrib/psl-ast-layers .opencode/skills/psl-ast-layers && 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 "psl-ast-layers" agent skill from https://github.com/prisma/orm/tree/main/skills-contrib/psl-ast-layers into .opencode/skills/psl-ast-layers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "psl-ast-layers", 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.
psl-ast-layersExplains which of the three PSL syntax tree layers to use with Prisma's parser output, and how to move between them without hand-written walks.
The PSL parser produces a three-layer syntax tree, and each layer has one job. The green tree, `GreenNode` and `GreenToken`, is immutable, position-independent storage and is never used in consumer code. The red tree, `SyntaxNode` and `SyntaxToken`, adds offsets and parents for navigation outside the current node. The typed AST, with classes such as `ModelDeclarationAst` and `FieldDeclarationAst`, exposes getters like `name()` and `fields()` and is the default choice.
Everything is exported from `@internal/psl-parser/syntax`, and `parse(source)` returns the document, diagnostics and source file, so work starts in the typed layer. When you know the node, call its getters rather than digging through children. To move outward or sideways, use the red tree helpers such as `findAncestor`, `tokenAtOffset`, `nextSiblingOrToken` and the trivia-aware `nonTriviaSibling`, then re-enter the typed layer with a static `cast`. Do not write your own whitespace or comment-skipping loops. The guidance applies to PSL interpreters, helpers in the parser package, the language server and formatters.
4 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 09aaa4f. 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.
No scripts in the folder and no shell commands in SKILL.md (its code samples are typescript).
From 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 no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
PSL Syntax Tree Layers loads about 2.3k tokens when it runs. Until then it costs about 81 tokens; SKILL.md has 783 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 prisma/orm at commit 09aaa4f, republished under its Apache-2.0 licence (© prisma). 783 words, ~2,255 tokens.
.claude/skills/psl-ast-layers/SKILL.md (or your agent's skills folder).The PSL parser (packages/1-framework/2-authoring/psl-parser) produces a three-layer syntax tree. Each layer has exactly one job — pick the right one and the code stays lossless, typed, and cheap.
| Layer | Types | Job | Use in consumer code? |
|---|---|---|---|
| Green tree | GreenNode, GreenToken (syntax/green.ts) | Immutable, position-independent storage. Foundation only. | Never |
| Red tree | SyntaxNode, SyntaxToken (syntax/red.ts, syntax/navigation.ts) | Navigation with offsets and parents: findAncestor(), tokenAtOffset(), nextToken/prevToken, nonTriviaSibling() | Navigation outside the current node |
| Typed AST | ModelDeclarationAst, FieldDeclarationAst, … (syntax/ast/) | Structural information about a known node via getters (name(), fields(), lbrace(), value()) | Default choice |
Everything is exported from @internal/psl-parser/syntax. parse(source) returns { document: DocumentAst, diagnostics, sourceFile } — you start in the typed layer.
You know what the node is (you hold a ModelDeclarationAst, a FieldAttributeAst, …) and want its parts → call the typed getters. Never dig through children yourself.
You need to move outward or sideways (find the enclosing model, the previous declaration, the token after the cursor) → use the red tree's navigation helpers, then immediately re-enter the typed layer with a static cast:
// enclosing model (tests the node itself first, then walks ancestors)
const model = node.syntax.findAncestor(ModelDeclarationAst.cast);
// enclosing model OR composite type — combine casts with any(…)
const owner = node.syntax.findAncestor(
any(ModelDeclarationAst.cast, CompositeTypeDeclarationAst.cast),
);Sideways and token-level movement all have dedicated helpers — do not hand-roll the walks:
nextSiblingOrToken / prevSiblingOrToken — adjacent element within the same parent (works from both nodes and tokens)token.nextToken / token.prevToken — document order, crossing node boundariesnonTriviaSibling(element, 'next' | 'prev'), skipTriviaToken(token, direction), isTrivia(token) (from syntax/navigation.ts) — trivia-aware movement; never write your own whitespace/comment-skipping loopYou genuinely don't know the node's type yet (e.g. resolving a cursor position in the language server) → anchor on the red tree, then cast back into the typed layer immediately:
// cursor → token: seam-aware, no descendant scanning
const token = document.syntax.tokenAtOffset(offset).leftBiased();
const attr = token?.parent.findAncestor(FieldAttributeAst.cast);
// selection range → smallest enclosing element
const covering = document.syntax.coveringElement(start, end);tokenAtOffset returns a TokenAtOffset that models the offset-on-a-seam case explicitly — pick leftBiased() or rightBiased() deliberately (completions usually want left, hover often wants right). Reach for a manual descendants() walk only when no offset anchors the search, and even then the loop body's first move is a cast (castExpression(child), ModelDeclarationAst.cast(child), …).
Green tree → only inside psl-parser itself (parser, GreenNodeBuilder, red-tree internals). If consumer code touches node.green, that's a bug.
Every typed AST class exposes readonly syntax: SyntaxNode (the AstNode interface), so switching layers is always one property access away — there is no excuse to stay in the wrong layer.
If a typed AST class lacks a getter for the structure you need, add the getter to the class in syntax/ast/ (test-first, exported via exports/syntax.ts) rather than hand-rolling child iteration at the call site. The helpers findChildToken, findFirstChild, and filterChildren from ast-helpers.ts are the building blocks for those getters — they belong inside AST classes, not scattered through consumer code.
Never round-trip through text: neither printSyntax(node) nor slicing the SourceFile by offsets, followed by string matching / regex / re-parsing. The tree already holds the structure; text extraction throws away parsing work and breaks on comments, whitespace, and escapes.
// BAD: stringify then string-hack
const text = printSyntax(attr.syntax);
const isUnique = text.includes('@unique');
// BAD: slicing the source file by offsets
const raw = source.slice(node.syntax.offset, node.syntax.offset + node.syntax.textLength);
const name = raw.split(' ')[1];
// GOOD: ask the tree
const isUnique = attr.name()?.identifier()?.token()?.text === 'unique';
const name = model.name()?.token()?.text;Same rule for values: StringLiteralExprAst.value() returns the decoded string (escapes resolved, quotes stripped); slicing quotes off raw text yields wrong results for \n, \u…., etc.
printSyntax and SourceFile offsets have legitimate uses — producing output for humans: error-message snippets, formatter output, positionAt for LSP ranges. Extracting structural facts from that text is the anti-pattern.
node.green exists so the red tree can do its job. Consumer code must not inspect green children, kinds, or text — green elements have no offsets and no parents, so any information you pull from them is positionally blind and will not survive refactors of the storage layer.
// BAD: peeking into green storage
const first = model.syntax.green.children[0];
if (first?.type === 'token' && first.text === 'model') { … }
// GOOD: red/typed access
const keyword = model.keyword(); // SyntaxToken with a real offsetchildren(), childNodes(), descendants(), fields(), attributes(), declarations() are lazy generators on purpose. Materializing them just to index or filter allocates for nothing and hides intent.
// BAD: collect then poke
const fields = Array.from(model.fields());
const idField = fields.filter((f) => f.name()?.token()?.text === 'id')[0];
// GOOD: iterate lazily, stop early
let idField: FieldDeclarationAst | undefined;
for (const field of model.fields()) {
if (field.name()?.token()?.text === 'id') {
idField = field;
break;
}
}If you already know the node is a ModelDeclarationAst, iterating its red children to find tokens or sub-nodes manually re-implements the typed getters — badly.
// BAD: manual token hunt on a known node
let lbrace: SyntaxToken | undefined;
for (const child of model.syntax.children()) {
if (child instanceof SyntaxToken && child.kind === 'LBrace') {
lbrace = child;
break;
}
}
// GOOD: the getter already exists
const lbrace = model.lbrace();Likewise use field.typeAnnotation(), attr.argList()?.args(), kv.value() — and if the getter you want is missing, add it to the AST class (see above) instead of spelunking.
The same rule applies to navigation: a hand-written ancestor loop, whitespace-skipping loop, or offset-scanning descendants() walk re-implements findAncestor, skipTriviaToken / nonTriviaSibling, or tokenAtOffset / coveringElement. Use the helper.
parse(source) → ParseResult { document, diagnostics, sourceFile }SomeAst.cast(syntaxNode) (returns undefined on kind mismatch), castExpression(node) for expression unions, any(CastA, CastB, …) to combine casts into one predicateastNode.syntaxfindAncestor(cast) (checks self first), ancestors(), parentnextSiblingOrToken / prevSiblingOrToken; trivia-aware: nonTriviaSibling, skipTriviaToken, isTriviatoken.nextToken / token.prevToken (crosses node boundaries); subtree edges: node.firstToken / node.lastTokentokenAtOffset(offset) (seam-aware TokenAtOffset), coveringElement(start, end), endOffset, isInside(offset) / isOutside(offset)sourceFile.positionAt(token.offset) / sourceFile.offsetAt(position) — offsets live only on red SyntaxToken / SyntaxNode, never greenfindChildToken, findFirstChild, filterChildren, any, and the BracedBlock interface (for lbrace()/rbrace() blocks) in syntax/ast-helpers.ts© prisma, Apache-2.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 skills-contrib/psl-ast-layers of prisma/orm.
Open the folder on GitHubat commit 09aaa4f
PSL Syntax Tree Layers 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 |
|---|---|---|---|---|---|---|
| PSL Syntax Tree Layers this skillprisma/orm | 48k | — | ~2.3k | Automated safety check: Pass | Apache-2.0 | |
| Drizzle Orm Expertdavila7/claude-code-templates | 32k | 3 repos | ~2.6k | Automated safety check: Pass | MIT | |
| Drizzle Ormericrisco/rsc-harness | 167 | — | ~2.6k | Automated safety check: Pass | MIT | |
| Prisma Ormericrisco/rsc-harness | 167 | — | ~3k | Automated safety check: Pass | MIT | |
| Twenty Syncable Entity Typestwentyhq/twenty | 58k | — | ~2.8k | Automated safety check: Pass | Custom licence | |
| Typescriptericrisco/rsc-harness | 167 | — | ~2.8k | Automated safety check: Pass | MIT |
davila7/claude-code-templates
Expert in Drizzle ORM for TypeScript — schema design, relational queries, migrations, and serverless database integration.
ericrisco/rsc-harness
A skill your agent uses when modeling data or querying with Drizzle ORM in TypeScript — pgTable schema in .ts, type-safe select/insert/relational queries, drizzle-kit migrations.
ericrisco/rsc-harness
A skill your agent uses when modeling data or writing type-safe queries with Prisma ORM in TypeScript — schema.prisma, prisma.config.ts, the generated Prisma Client, and Prisma Migrate, including…
twentyhq/twenty
Walks through the first of six steps for adding a syncable entity to Twenty's server: metadata name, TypeORM entity, flat types and central constants.
ericrisco/rsc-harness
A skill your agent uses when writing or fixing TypeScript type code (.ts/.tsx/.d.ts), modeling data with generics/unions/branded types, diagnosing narrowing failures, or configuring tsconfig and the…
latitude-dev/latitude-llm
Configure Better Auth server and client, set up database adapters, manage sessions, add plugins, and handle environment variables.
prisma/orm
Runs a loop on a GitHub pull request: fetch review state, triage comments into actions, implement them and resolve threads, repeating until nothing actionable is left.
prisma/orm
Fetches a pull request's canonical review state as JSON, validates it, and renders markdown, a text summary and triage target files from it using bundled scripts.
prisma/orm
Implements triaged pull request review actions, commits focused fixes, posts status replies on GitHub and resolves the threads.
prisma/orm
Runs the triage step of the review-framework loop: reads fetched PR review state, builds `review-actions.json`, validates it and renders `review-actions.md`.
prisma/orm
Replaces a plain TypeScript union plus switch statements with frozen subclasses and a visitor interface when several places dispatch on the same variants.
prisma/orm
Guides an outside contributor through opening a prisma/orm pull request from a fork that follows CONTRIBUTING.md and passes review on the first round.
Works with
Categories
Explains which of the three PSL syntax tree layers to use with Prisma's parser output, and how to move between them without hand-written walks. The PSL parser produces a three-layer syntax tree, and each layer has one job. The green tree, `GreenNode` and `GreenToken`, is immutable, position-independent storage and is never used in consumer code.
PSL Syntax Tree Layers fits situations like: writing a PSL interpreter or formatter on top of the parser output; resolving a cursor position to a node in the language server; finding the enclosing model or the neighboring declaration of a node.
Run `npx skills add prisma/orm --skill psl-ast-layers -a claude-code`. Or copy the skill folder (skills-contrib/psl-ast-layers in prisma/orm) into .claude/skills/psl-ast-layers in your project. Claude Code loads it when a task matches its description.
Run `npx skills add prisma/orm --skill psl-ast-layers -a codex`. Or copy the skill folder (skills-contrib/psl-ast-layers in prisma/orm) into .agents/skills/psl-ast-layers 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 prisma/orm --skill psl-ast-layers -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/psl-ast-layers, .gemini/skills/psl-ast-layers, .github/skills/psl-ast-layers and .opencode/skills/psl-ast-layers in your project.
SKILL.md names no scripts, command-line tools or credentials: PSL Syntax Tree Layers is instructions for the agent only. Our summary lists: The Prisma ORM repository with the psl-parser package.
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.
PSL Syntax Tree Layers is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.3k tokens (SKILL.md is roughly 9k 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 PSL Syntax Tree Layers: Drizzle Orm Expert (davila7/claude-code-templates, 32k stars), Drizzle Orm (ericrisco/rsc-harness, 167 stars), Prisma Orm (ericrisco/rsc-harness, 167 stars) and Twenty Syncable Entity Types (twentyhq/twenty, 58k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
prisma (a GitHub organization, an official publisher) maintains it in prisma/orm, which has 47,696 GitHub stars. The repository holds 20 skills in this directory. The repository was last updated on October 8, 2026.
Source: prisma/orm on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.