Skip to content

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.

Terminal window
npm install -g @xriptjs/cli

Or run without installing:

Terminal window
npx xript <command>
CommandDescription
xript validateCheck manifests against the xript spec schema
xript typegenGenerate TypeScript definitions from a manifest
xript docgenGenerate markdown documentation from a manifest
xript initScaffold a new xript app or mod project
xript sanitizeStrip dangerous content from HTML fragments
xript scanRead @xript annotations from TypeScript source into a manifest

Checks that a manifest conforms to the xript specification schema. Catches structural errors, invalid field names, wrong types, and missing required fields.

Terminal window
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.

The --cross flag validates that a mod’s fragments target valid slots in the host app:

Terminal window
xript validate --cross app-manifest.json mod-manifest.json

Cross-validation checks:

  • Every fragment targets a slot that exists in the app manifest
  • Every fragment’s format is in the slot’s accepts list
  • Every capability the mod requests is defined in the app manifest
$ 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'
CodeMeaning
0All manifests are valid
1One or more manifests have errors
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");

Reads a manifest and produces TypeScript definition files. Gives extenders autocomplete, type checking, and inline documentation.

Terminal window
xript typegen <manifest>
xript typegen <manifest> --output types.d.ts
xript typegen <manifest> -o types.d.ts

Prints to stdout by default. Use --output / -o to write to a file.

ManifestTypeScript
"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.

When the manifest declares slots, the generator produces additional types:

  • FragmentProxytoggle, addClass, removeClass, setText, setAttr, replaceChildren
  • hooks.fragment — typed lifecycle registration: mount, unmount, update, suspend, resume
  • XriptSlots — describes available slots with accepted formats, multiplicity, and styling modes
  • Function descriptions become the main JSDoc comment
  • Parameter descriptions become @param tags
  • Capability requirements become @remarks Requires capability annotations
  • Deprecation notices become @deprecated tags

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;
import { generateTypes, generateTypesFromFile } from "@xriptjs/typegen";
const dts = generateTypes(manifest);
const { content } = await generateTypesFromFile("./manifest.json");

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.

Terminal window
xript docgen <manifest> --output docs/api/
xript docgen <manifest> -o docs/api/
xript docgen <manifest> -o docs/ --link-format no-extension
xript docgen <manifest> -o docs/ --frontmatter "layout: ../layouts/Doc.astro"
FlagDescription
--output, -oOutput directory (required)
--link-format no-extensionStrip .md extensions from generated links (for static site generators)
--frontmatterInject YAML frontmatter into all generated pages

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)
$ 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.md
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.

Terminal window
xript init [directory]
xript init [directory] --yes
xript init [directory] --mod
xript init [directory] --mod --yes
FlagDescriptionDefault
[directory]Target directory for the projectCurrent directory
--yes, -ySkip prompts, use defaultsfalse
--modScaffold a mod project instead of an appfalse
--tier <2|3>Adoption tier (app projects only)2
--typescriptGenerate TypeScript output(default)
--javascriptGenerate JavaScript output
$ xript init my-mod
Project 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 demo

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.json

The generated fragment includes data-bind for value display and data-if for conditional visibility. The entry script demonstrates the sandbox fragment API.

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.

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" });

Cleans HTML fragment content before it reaches the host. Pure string-based with no DOM dependency — runs inside QuickJS WASM, Node, Deno, browsers, wherever.

Terminal window
xript sanitize <file>
xript sanitize <file> --validate
xript sanitize <file> --quiet
FlagDescription
--validateShow what was stripped alongside the sanitized output
--quietOutput sanitized HTML only, no diagnostics

Structural and presentational elements: div, span, p, h1h6, 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.

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.

All xript runtime implementations must produce identical sanitized output. The conformance test suite lives at spec/sanitizer-tests.json with 45 test cases.

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 => true

Reads @xript and @xript-cap JSDoc annotations from TypeScript source files and generates manifest bindings and capabilities. Optionally merges scanned results into an existing manifest.

Terminal window
xript scan <directory>
xript scan <directory> --output bindings.json
xript scan <directory> --manifest manifest.json
xript scan <directory> --manifest manifest.json --write
FlagDescription
--manifest, -mMerge scanned bindings into an existing manifest
--output, -oWrite scanned bindings to a file (instead of stdout)
--writeWrite merged manifest back to disk (requires --manifest)

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 { ... }
SourceManifest field
@xript <path>Binding path (dot nesting)
@xript-cap <name>capability
JSDoc descriptiondescription
@param tags + TypeScript typesparams array
Return type / @returnsreturns
async keyword or Promise<>async: true
@deprecateddeprecated

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).

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-cap but not defined in the manifest — are auto-generated with risk: "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 --write
Added 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.json
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