CLI
The xript CLI is a single command with subcommands for every tool in the xript ecosystem. Validate manifests, generate TypeScript definitions, produce markdown documentation, scaffold new projects, sanitize HTML fragments, and scan annotated source code into manifests.
Install
Section titled “Install”npm install -g @xriptjs/cliOr run without installing:
npx xript <command>Commands
Section titled “Commands”| Command | Description |
|---|---|
xript validate | Check manifests against the xript spec schema |
xript typegen | Generate TypeScript definitions from a manifest |
xript docgen | Generate markdown documentation from a manifest |
xript init | Scaffold a new xript app or mod project |
xript sanitize | Strip dangerous content from HTML fragments |
xript scan | Read @xript annotations from TypeScript source into a manifest |
validate
Section titled “validate”Checks that a manifest conforms to the xript specification schema. Catches structural errors, invalid field names, wrong types, and missing required fields.
xript validate <manifest...>Accepts one or more manifest files. The validator auto-detects whether each file is an app manifest or a mod manifest based on its structure.
Cross-validation
Section titled “Cross-validation”The --cross flag validates that a mod’s fragments target valid slots in the host app:
xript validate --cross app-manifest.json mod-manifest.jsonCross-validation checks:
- Every fragment targets a slot that exists in the app manifest
- Every fragment’s format is in the slot’s
acceptslist - Every capability the mod requests is defined in the app manifest
Example
Section titled “Example”$ xript validate manifest.json ✓ manifest.json is valid
$ xript validate broken.json ✗ broken.json has errors: - /name: must match pattern "^[a-z][a-z0-9-]*$" - /bindings/doStuff: must have required property 'description'Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
0 | All manifests are valid |
1 | One or more manifests have errors |
Programmatic API
Section titled “Programmatic API”import { validateManifest, validateModManifest, crossValidate, validateManifestFile,} from "@xriptjs/validate";
const result = validateManifest({ xript: "0.3", name: "my-app", bindings: { greet: { description: "Greets." } }, slots: [{ id: "sidebar", accepts: ["text/html"] }],});
const modResult = await validateModManifest({ xript: "0.3", name: "my-mod", version: "1.0.0", fragments: [{ id: "panel", slot: "sidebar", format: "text/html", source: "<p>hi</p>" }],});
const crossResult = await crossValidate(appManifest, modManifest);
const fileResult = await validateManifestFile("./manifest.json");typegen
Section titled “typegen”Reads a manifest and produces TypeScript definition files. Gives extenders autocomplete, type checking, and inline documentation.
xript typegen <manifest>xript typegen <manifest> --output types.d.tsxript typegen <manifest> -o types.d.tsPrints to stdout by default. Use --output / -o to write to a file.
Type mapping
Section titled “Type mapping”| Manifest | TypeScript |
|---|---|
"string" | string |
"number" | number |
"boolean" | boolean |
"void" | void |
"string[]" | string[] |
{ "array": "Position" } | Position[] |
{ "union": ["string", "number"] } | string | number |
{ "map": "number" } | Record<string, number> |
{ "optional": "string" } | string | undefined |
Custom object types become interface declarations. Enum types become string literal unions.
Fragment API types
Section titled “Fragment API types”When the manifest declares slots, the generator produces additional types:
FragmentProxy—toggle,addClass,removeClass,setText,setAttr,replaceChildrenhooks.fragment— typed lifecycle registration:mount,unmount,update,suspend,resumeXriptSlots— describes available slots with accepted formats, multiplicity, and styling modes
Generated JSDoc
Section titled “Generated JSDoc”- Function descriptions become the main JSDoc comment
- Parameter descriptions become
@paramtags - Capability requirements become
@remarks Requires capabilityannotations - Deprecation notices become
@deprecatedtags
Example
Section titled “Example”Given a manifest with a greet binding and a Position type:
// Auto-generated by @xriptjs/typegen
/** A 2D position. */interface Position { /** Horizontal coordinate. */ x: number; /** Vertical coordinate. */ y: number;}
/** * Returns a greeting. * @param name - The name to greet. */declare function greet(name: string): string;Programmatic API
Section titled “Programmatic API”import { generateTypes, generateTypesFromFile } from "@xriptjs/typegen";
const dts = generateTypes(manifest);
const { content } = await generateTypesFromFile("./manifest.json");docgen
Section titled “docgen”Reads a manifest and produces a structured set of markdown documentation pages. Every binding, type, and capability gets its own page with signatures, parameter tables, and examples.
xript docgen <manifest> --output docs/api/xript docgen <manifest> -o docs/api/xript docgen <manifest> -o docs/ --link-format no-extensionxript docgen <manifest> -o docs/ --frontmatter "layout: ../layouts/Doc.astro"Options
Section titled “Options”| Flag | Description |
|---|---|
--output, -o | Output directory (required) |
--link-format no-extension | Strip .md extensions from generated links (for static site generators) |
--frontmatter | Inject YAML frontmatter into all generated pages |
Output structure
Section titled “Output structure”The generator creates these page types:
- Index (
index.md) — overview of the entire API surface: global functions, namespaces, types, capabilities table - Binding pages (
bindings/*.md) — one per top-level binding with signatures, parameter tables, return types, capability requirements - Type pages (
types/*.md) — one per custom type with field tables and TypeScript definitions - Fragment API (
fragment-api.md) — lifecycle hooks and proxy operations (when manifest has slots)
Example
Section titled “Example”$ xript docgen manifest.json -o api-docs/✓ Generated 10 documentation pages to /path/to/api-docs api-docs/index.md api-docs/bindings/log.md api-docs/bindings/player.md api-docs/types/Position.md api-docs/types/Item.mdProgrammatic API
Section titled “Programmatic API”import { generateDocs, generateDocsFromFile, writeDocsToDirectory } from "@xriptjs/docgen";
const result = generateDocs(manifest);
const result = await generateDocsFromFile("./manifest.json");
const written = await writeDocsToDirectory(result, "./api-docs/");Scaffolds new xript projects with a manifest, package configuration, and a demo script.
xript init [directory]xript init [directory] --yesxript init [directory] --modxript init [directory] --mod --yesOptions
Section titled “Options”| Flag | Description | Default |
|---|---|---|
[directory] | Target directory for the project | Current directory |
--yes, -y | Skip prompts, use defaults | false |
--mod | Scaffold a mod project instead of an app | false |
--tier <2|3> | Adoption tier (app projects only) | 2 |
--typescript | Generate TypeScript output | (default) |
--javascript | Generate JavaScript output |
Interactive mode
Section titled “Interactive mode”$ xript init my-modProject name (my-mod):Tier: 2 (bindings) or 3 (full scripting)? (2):Language: typescript or javascript? (typescript):
✓ Created my-mod in /path/to/my-mod
manifest.json package.json src/demo.ts tsconfig.json
Next steps: cd my-mod npm install npm run demoMod scaffolding
Section titled “Mod scaffolding”Use --mod to scaffold a mod project with a mod manifest, fragment HTML, and entry script:
my-health-panel/├── mod-manifest.json├── src/│ └── mod.ts├── fragments/│ └── panel.html└── package.jsonThe generated fragment includes data-bind for value display and data-if for conditional visibility. The entry script demonstrates the sandbox fragment API.
Adoption tiers
Section titled “Adoption tiers”Tier 2 (Bindings): Simple host bindings with capability gating. Good for apps that need a few extension points like custom formatting or data transformations.
Tier 3 (Full Scripting): Namespace bindings, hooks, custom types, and capabilities. Designed for apps with rich extension APIs like game engines or extensible platforms.
Tier 1 (expression-only) is simple enough that no scaffolding is needed.
Programmatic API
Section titled “Programmatic API”import { writeProject, generateProjectFiles } from "@xriptjs/init";
const result = await writeProject("./my-mod", { name: "my-mod", tier: 2, language: "typescript",});
const files = generateProjectFiles({ name: "my-app", tier: 3, language: "typescript" });sanitize
Section titled “sanitize”Cleans HTML fragment content before it reaches the host. Pure string-based with no DOM dependency — runs inside QuickJS WASM, Node, Deno, browsers, wherever.
xript sanitize <file>xript sanitize <file> --validatexript sanitize <file> --quietOptions
Section titled “Options”| Flag | Description |
|---|---|
--validate | Show what was stripped alongside the sanitized output |
--quiet | Output sanitized HTML only, no diagnostics |
What gets preserved
Section titled “What gets preserved”Structural and presentational elements: div, span, p, h1–h6, ul/ol/li, table family, details/summary, section, article, header, footer, a, img, br, hr, and more.
Safe attributes: class, id, data-* (including data-bind and data-if), aria-*, role, style (sanitized), src/href (safe URIs only), alt, width, height, tabindex, hidden.
Scoped <style> blocks with dangerous CSS properties stripped.
What gets stripped
Section titled “What gets stripped”Elements removed entirely (including children): script, iframe, object, embed, form, base, link, meta, title, noscript, applet.
Document wrappers unwrapped (children preserved): html, head, body.
Attributes stripped: all on* event attributes, formaction, action, method.
URIs stripped: javascript:, vbscript:, dangerous data: URIs. Safe image data: URIs on src are preserved.
CSS stripped: url() references, expression(), -moz-binding, behavior: properties.
Conformance
Section titled “Conformance”All xript runtime implementations must produce identical sanitized output. The conformance test suite lives at spec/sanitizer-tests.json with 45 test cases.
Programmatic API
Section titled “Programmatic API”import { sanitizeHTML, sanitizeHTMLDetailed, validateFragment } from "@xriptjs/sanitize";
const clean = sanitizeHTML('<div onclick="evil()">safe text</div>');// => '<div>safe text</div>'
const result = sanitizeHTMLDetailed('<script>alert("xss")</script><p>safe</p>');// result.html => '<p>safe</p>'// result.strippedElements => ['script']
const validation = validateFragment('<div data-bind="health">0</div>');// validation.valid => trueReads @xript and @xript-cap JSDoc annotations from TypeScript source files and generates manifest bindings and capabilities. Optionally merges scanned results into an existing manifest.
xript scan <directory>xript scan <directory> --output bindings.jsonxript scan <directory> --manifest manifest.jsonxript scan <directory> --manifest manifest.json --writeOptions
Section titled “Options”| Flag | Description |
|---|---|
--manifest, -m | Merge scanned bindings into an existing manifest |
--output, -o | Write scanned bindings to a file (instead of stdout) |
--write | Write merged manifest back to disk (requires --manifest) |
Annotations
Section titled “Annotations”Annotate exported functions with @xript to declare them as bindings. Dot-delimited paths create namespace nesting.
/** * Get value from the data store. * * @xript data.get */export function getData(path: string): DataResult { ... }Use @xript-cap to gate a binding behind a capability:
/** * Transfer currency between players. * * @xript economy.transfer * @xript-cap modify-state */export function transferCurrency(fromId: string, toId: string, amount: number): void { ... }Standard @deprecated tags are also recognized:
/** * @xript player.getHealth * @deprecated Use player.stats.get("health") instead */export function getPlayerHealth(): number { ... }What gets extracted
Section titled “What gets extracted”| Source | Manifest field |
|---|---|
@xript <path> | Binding path (dot nesting) |
@xript-cap <name> | capability |
| JSDoc description | description |
@param tags + TypeScript types | params array |
Return type / @returns | returns |
async keyword or Promise<> | async: true |
@deprecated | deprecated |
Files scanned: all *.ts in the target directory, excluding node_modules/, *.test.ts, *.spec.ts, and *.d.ts. Requires ts-morph (the CLI prompts if it’s missing).
Merge mode
Section titled “Merge mode”With --manifest, the scanner compares scanned bindings against the existing manifest:
- Added bindings are inserted
- Removed bindings are warned about but not deleted
- Unchanged bindings are left untouched (manual edits preserved)
- Capability gaps — capabilities referenced in
@xript-capbut not defined in the manifest — are auto-generated withrisk: "low"
Without --write, merge mode previews the result to stdout. With --write, it updates the manifest file on disk.
$ xript scan src/ -m manifest.json --writeAdded 2 binding(s): data.get, data.set! 1 binding(s) in manifest but not in source: legacy.old! 2 capability gap(s): storage, network✓ Updated manifest.jsonProgrammatic API
Section titled “Programmatic API”import { scanDirectory, mergeIntoManifest } from "@xriptjs/cli/scan";
const scanned = await scanDirectory("./src");// scanned.bindings — nested binding tree// scanned.capabilities — auto-generated capabilities// scanned.diagnostics — errors and warnings
const merged = await mergeIntoManifest(existingManifest, scanned);// merged.manifest — the merged result// merged.added — new binding paths// merged.removed — paths in manifest but not source// merged.capabilityGaps — capabilities referenced but not defined