Open source · Early access
ShotScript

TypeScript with 74% less syntax, bad patterns removed, and one way to do each thing — simpler to learn, less for AI to get wrong, easier to review.

The problem What is ShotScript Before / after Install Claude Code

The problem

Too many ways to write the same thing.

TypeScript gives you three ways to declare variables, four ways to write functions, five ways to handle errors. More forms means harder reads, harder reviews, and more ways for LLMs to generate subtly wrong code.

5 error patterns — throw, try/catch, rejected Promise, error-first callback, return tuple PromiseResult<T>
3 absent-value forms — ?, | undefined, | null | null
4 function forms — arrow, declaration, expression, method function f() {}
3 type-shape constructs — type, interface, class type T = {}
3 variable declaration forms — var, let, const const
6 conditional forms — ternary, &&, ||, ??, ?., switch expression if / else
7 OOP constructs — class, extends, implements, abstract, private, protected, new banned
6 type escape hatches — as, any, !, @ts-ignore, @ts-expect-error, satisfies banned
6 advanced type features — conditional types, mapped types, template literal types, infer, index signatures, variadic tuples banned
ShotScript removes ~79% of TypeScript's ways to write the same thing and standardises the rest to one. Your files are unchanged — it's a list of rules, not a compiler.

What is ShotScript

Five tools. One package.

ShotScript is not a language or a compiler. It's a set of tools that constrain TypeScript to one way of doing things.

ShotScript

Before / after

One codebase, rewritten.

Interface, class, try/catch, arrow functions, ternaries, any — common TypeScript, with every ShotScript rule applied.

TypeScript
// ❌ interface · optional fields
interface BaseUser {
  id: number
  name: string
}

// ❌ intersection · optional fields
type User = BaseUser & {
  avatar?: string
  role?: 'admin' | 'member'
}

// ❌ class · constructor · private · this
class UserService {
  private cache: Map<number, User> = new Map()
  constructor(private readonly db: Database) {}

  // ❌ optional param · try/catch · throw · as
  async findUser(id?: number): Promise<User | undefined> {
    if (!id) return undefined
    try {
      const res = await fetch(`/users/${id}`)
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      return res.json() as User
    } catch (e) { return undefined }
  }
}

// ❌ arrow fn · ternary · optional chaining · any
const label = (u?: User): string =>
  u?.avatar ? `<img src="${u.avatar}">` : u?.name ?? 'Unknown'

// ❌ let · any · anonymous callback
let names: any[] = users.map(u => u.name)
ShotScript
import type { PromiseResult } from 'shotscript/std'
import { safeFetch, jsonParse } from 'shotscript/std'

type BaseUser = {
  readonly id: number
  readonly name: string
}

// nested property, not & — fields stay in one place
type User = {
  readonly base: BaseUser
  readonly avatar: string | null
  readonly role: 'admin' | 'member' | null
}

type UserService = {
  readonly db: Database
  readonly cache: Map<number, User>
}

async function findUser(
  svc: UserService,
  id: number,
): PromiseResult<User> {
  const [res, err] = await safeFetch(`/users/${id}`)
  if (err !== null) return [null, err]
  return jsonParse<User>(await res.text())
}

function label(user: User): string {
  if (user.avatar !== null) {
    return `<img src="${user.avatar}">`
  }
  return user.base.name
}

function toName(user: User): string { return user.base.name }
const names: string[] = users.map(toName)

Install

One package. Five tools.

Install with AI — paste into Claude Code, Cursor, or any AI assistant
Help me set up ShotScript. Walk me through this step by step: Step 1 — Detect context. Check whether the current directory contains a package.json. Step 2a — New project (no package.json): Ask me "What would you like to build?" Based on my answer, pick the most appropriate minimal TypeScript starter (Node + tsx for scripts, Hono/Express for APIs, Vite + React for web apps, etc.), scaffold it, then continue. Step 2b — Existing project: Continue. Step 3 — Install ShotScript. Run npm install --save-dev shotscript. Then add { "name": "shotscript/plugin" } to the plugins array in compilerOptions in tsconfig.json, set extends in tsconfig.json to "shotscript/tsconfig/shotscript.json", add "shotscript/fmt" to extends in biome.json (create it if missing), prepend shotscript 'src/**/*.{ts,tsx}' to the build script in package.json so lint runs at build time, and run npx shotscript init to write the ShotScript coding guide to AGENTS.md (merged into any existing file) and install the slash commands. Step 4 — Offer migration. If existing TypeScript source files are present, ask me whether to migrate them with /shotscript-migrate. Skip otherwise. Step 5 — Confirm. Show me the final tsconfig.json and biome.json so I can verify. About /shotscript-migrate →
Manual setup
$ npm install --save-dev shotscript
ShotScriptLint — enforce at build time
// package.json
{
  "scripts": {
    "build": "shotscript 'src/**/*.{ts,tsx}' && tsc ..."
  }
}
AGENTS.md — AI coding guide

Copies the ShotScript coding rules into your project so every AI assistant codes in ShotScript style automatically. Merge into an existing AGENTS.md if you already have one.

$ npx shotscript init
ShotScriptLint — editor plugin (VS Code)

Surfaces lint errors inline in VS Code via the TypeScript language server. Other editors (Zed, Neovim, etc.) may not fully support tsserver plugins even when using the TS language server — the build step above is the reliable enforcement for all environments.

// tsconfig.json
{
  "compilerOptions": {
    "plugins": [{ "name": "shotscript/plugin" }]
  }
}
ShotScriptFmt — biome config
// biome.json
{
  "extends": ["shotscript/fmt"]
}
ShotScriptTyping — tsconfig strict mode
// tsconfig.json
{
  "extends": "shotscript/tsconfig/shotscript.json"
}
ShotScriptStd — stdlib import
import { safeFetch, jsonParse } from 'shotscript/std'
import type { Result, PromiseResult } from 'shotscript/std'

Claude Code

AI-native compliance.

Three Claude Code slash commands — fix violations in-place, migrate existing codebases end-to-end, and explain any rule on demand. One install command, no manual rewrites.

ShotScriptSkills →