# AI Agent Guide for Rip **Purpose:** This document helps AI assistants understand and work with the Rip language compiler and its ecosystem of packages. **What is Rip:** An elegant reactive language that compiles to modern JavaScript (ES2022), featuring zero dependencies, self-hosting capability, and built-in reactivity primitives. Detailed subsystem notes live in nested `AGENTS.md` files in the relevant directories (`src/`, `docs/`, `packages/ui/`, `packages/vscode/`, `packages/stamp/`, `test/types/`). --- ## Quick Start ### Essential Commands ```bash # Debug any code echo 'your code' | ./bin/rip -t # Tokens (lexer) echo 'your code' | ./bin/rip -s # S-expressions (parser) echo 'your code' | ./bin/rip -c # JavaScript (codegen) # Run tests bun run test bun test/runner.js test/rip/FILE.rip # Rebuild parser (after grammar changes) bun run parser # Build browser bundle bun run build # Serve an app (watches *.rip, HTTPS, mDNS) rip server # Schema migrations (diff declared :models vs the database) rip schema status [models.rip] rip schema make [models.rip] [--allow-lossy] [--allow-destructive] rip schema migrate [models.rip] # Interactive REPL (toggle .tokens, .sexp, .js modes) ./bin/rip ``` ### File Editing Rules | File | Can Edit? | Notes | | ------------------------- | --------- | ------------------------------------------------------ | | `src/compiler.js` | Yes | Code emitter (`CodeEmitter`); main compiler work | | `src/lexer.js` | Yes | Lexer and rewriter | | `src/types.js` | Yes | Type system sidecar | | `src/components.js` | Yes | Component system sidecar | | `src/schema/` | Yes | Schema feature subdirectory (`schema` keyword) — entry `schema.js`, runtime fragments, loaders, `dts.js`. Imported from sibling modules as `./schema/schema.js`. | | `src/grammar/grammar.rip` | Carefully | Run `bun run parser` after changes | | `src/parser.js` | Never | Generated file | | `src/sourcemaps.js` | Yes | Source map generator | | `src/browser.js` | Yes | Browser entry point | | `rip-loader.js` | Yes | Bun plugin for `.rip` compilation and import rewriting | | `src/grammar/solar.rip` | Never | Given parser generator | | `test/rip/*.rip` | Yes | Test files | ### Critical Rules - **Never guess what code does — verify it.** Before claiming something is unnecessary, redundant, or works a certain way, read the source. This applies to Rip internals, packages, and external dependencies alike. If you can't verify, say so. - **Never edit `src/parser.js`** — it is generated (`bun run test:parser-fresh`, part of `test:all`, regenerates it and fails on any hand-edit or staleness) - **Never edit `src/grammar/solar.rip`** — it is given - **Never commit without running tests** — `bun run test` must pass - **Never add dependencies** — zero dependencies is a core principle - **Never read or execute scripts directly** — use `bun run ` - **Never write `x ? y` in Rip** — binary existential was removed; use `x ?? y` or full ternary `x ? y : z` - **Never write `await fn(args)` in `.rip` source when `fn!` will do** — the dammit operator is the idiomatic form. `fetch! url` compiles to `await fetch(url)`; `User.find! 1` to `await User.find(1)`; `user.save!` to `await user.save()`. Reserve raw `await` for JS interop in `.js` files, tests that document the await→! equivalence, and the rare cases where `fn!` is ambiguous with a dammit-returning expression. - Run `bun run parser` after grammar changes - Run `bun run build` after codegen, `components.js`, `browser.js`, or `app.rip` changes - Run `bun run build:schema-runtime` after editing any `src/schema/runtime-*.js` fragment (CI's `test:schema-fresh` fails on staleness) - **Every fenced `coffee` block in `docs/RIP-SCHEMA.md` must compile** — `bun run test:docs` enforces it (part of `test:all`). Annotate intentional exceptions with `` (fragments/templates) or `` (documented compile errors) on the line above the fence. When editing that doc, run `bun run test:docs` before committing. - Run `bun run bump` for the standard release flow - **In a typed Rip codebase, always run `rip check` after edits.** A project is typed if its `package.json` has `rip.strict: true` or `rip.checkAll: true`, or any `.rip` files use `:` type annotations. `rip check` catches both type errors and shadow-TypeScript emitter bugs that the runtime won't surface. - **If a `@rip-lang/*` package advertises a typed public API, keep it honest with `rip check --audit`.** A package "claims to be typed" if its entry file has `:` type annotations on exported symbols, or if downstream typed code imports it. For those, run `rip check --audit` before publishing or merging API changes — it runs the normal type check and walks the public surface for `any` leaks. A clean exit (0) means the source type-checks and every export is fully typed for consumers. Untyped packages with no typed consumers can stick with plain `rip check`. - **When working on the compiler, lexer, or source-map machinery, use `rip check --sourcemap`** to verify that every identifier round-trips through the generated source map (i.e. hover and go-to-def work in editors). This is a compiler-development diagnostic, not a package-quality check — gaps usually mean the audit's skip list is incomplete or that codegen lost a binding, both of which are compiler-side concerns. ## Compilation Pipeline ```text Rip Source -> Lexer -> emitTypes -> Parser -> S-Expressions -> CodeEmitter -> JavaScript (types.js) (arrays + .loc) + source map ↓ file.d.ts (when types: "emit") ``` **Key insight:** S-expressions are simple arrays like `["=", "x", 42]`, not large AST objects. Detailed compiler, lexer, and component internals are in `src/AGENTS.md`. --- ## Common Tasks ### Fix a Bug in Codegen ```bash echo 'failing code' | ./bin/rip -s rg "GENERATORS" src/compiler.js bun run test ``` ### Add a Grammar Rule ```bash # Edit src/grammar/grammar.rip bun run parser # Add codegen in src/compiler.js if needed bun run test ``` ## Testing Test helpers: ```coffee test "name", "x = 42; x", 42 code "name", "x + y", "(x + y)" fail "name", "invalid syntax" ``` Test files live in `test/rip/`. Component testing notes are in `src/AGENTS.md`. Type-system and audit guidance is in `packages/vscode/AGENTS.md` and `test/types/AGENTS.md`. --- ## Packages The `packages/` directory contains optional packages written in Rip, with zero dependencies, running on Bun. ### Package export style **Prefer named-only exports** for new `@rip-lang/*` packages. The rest of the typical package surface (types, classes, helpers) is already named-only, so adding a default for the main value just creates a second way to import the same thing. **Hard rule:** if a package's entry point exports a typed value — e.g. `export http = makeInstance()` where `makeInstance` returns a typed instance — do **not** also re-export it as default. The default-export binding loses the inferred type through Rip's DTS pipeline, so downstream `import http from '@rip-lang/http'` sees `any` while `import { http }` correctly sees `HttpInstance`. Named-only avoids that footgun entirely. A few existing packages still ship `export default` (`@rip-lang/time`, `packages/script`). That's tolerated for backwards compatibility — don't churn published APIs solely to enforce the style — but don't add new defaults. **Annotate the public surface at minimum.** Ideally every binding in a package is typed, but realistically that bar is hard to enforce. The practical contract: every `export` must reach consumers with a fully-typed signature (what `rip check --audit` measures). Always annotate: - exported `def`/`class`/`type`/`interface` declarations and their parameters/return types - the return type of any exported function whose body assembles a shape that needs to match a public contract (e.g. `makeInstance(): HttpInstance` in [packages/http/http.rip](packages/http/http.rip)) - fields of exported types/interfaces Internal helpers, locals, and private types may rely on inference — but annotate them too when it helps readability or pins down a tricky shape. ### Browser-safe packages Packages that should be auto-discovered and bundled into the browser bundle by `@rip-lang/server`'s `serve()` middleware must opt in via `package.json`: ```json { "name": "@rip-lang/http", "rip": { "browser": true } } ``` With this flag set, the `serve()` middleware walks the app's declared `@rip-lang/*` dependencies, compiles each package's entry, and stores its modules under the `_pkg//` bundle prefix (see [packages/app/AGENTS.md](packages/app/AGENTS.md)). Bare specifiers like `import { http } from '@rip-lang/http'` are rewritten at compile time to the bundle-relative path. Server-only packages (`@rip-lang/server`, `@rip-lang/db`, anything that touches `node:*`, `Bun.*`, or the filesystem) must **not** set this flag. If a browser-bound component file imports a package that lacks `rip.browser: true`, the bundler errors with *"package X is not browser-safe (declares no `rip.browser` entry)"* rather than producing a broken bundle. Rule of thumb: if every public API in the package works in a browser (uses only `fetch`, DOM, or pure JS), set `rip.browser: true`. Otherwise leave it off. ### @rip-lang/server Sinatra-style web framework with magic `@` context, validation helpers, file serving, middleware composition, multi-worker process management, hot reloading, automatic HTTPS, mDNS, and request queueing. Key ideas: - handlers use `@req`, `@json()`, `@send()`, `@session` - `read()` validates params and body - `post '/x', input: SomeSchema, ->` validates the JSON body through a Rip schema before the handler runs (400 with structured issues; parsed value at `@input`) and contributes to the auto-generated `GET /openapi.json` - `@send(path, type?)` serves files - `use()` composes middleware ```coffee import { get, use, start, notFound } from '@rip-lang/server' get '/', -> { message: 'Hello!' } get '/css/*', -> @send "public/#{@req.path.slice(5)}" notFound -> @send 'index.html', 'text/html; charset=UTF-8' start port: 3000 ``` ### Rip App Application framework built into Rip — stash, resource, timing, components store, file-based router, renderer, launch, and shared ARIA helpers. Lives in `packages/app/index.rip`, compiled into `rip.min.js` so a single `