# https://github.com/vercel-labs/scriptc Project Manual

Generated at: 2026-07-27 04:23:23 UTC

## Table of Contents

- [Introduction and Three-Tier Compilation Model](#page-1)
- [Frontend and Lowering: TypeScript to Typed IR](#page-2)
- [Backend Emitters and C Runtime](#page-3)
- [CLI, Operations, Escape Hatches, and Platforms](#page-4)

<a id='page-1'></a>

## Introduction and Three-Tier Compilation Model

### Related Pages

Related topics: [Frontend and Lowering: TypeScript to Typed IR](#page-2), [Backend Emitters and C Runtime](#page-3), [CLI, Operations, Escape Hatches, and Platforms](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/vercel-labs/scriptc/blob/main/README.md)
- [docs/src/app/page.tsx](https://github.com/vercel-labs/scriptc/blob/main/docs/src/app/page.tsx)
- [docs/src/app/introduction/page.mdx](https://github.com/vercel-labs/scriptc/blob/main/docs/src/app/introduction/page.mdx)
- [docs/src/app/how-it-works/page.mdx](https://github.com/vercel-labs/scriptc/blob/main/docs/src/app/how-it-works/page.mdx)
- [packages/compiler/src/index.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/index.ts)
</details>

# Introduction and Three-Tier Compilation Model

## Overview and Purpose

`scriptc` is a compiler that takes TypeScript source code and produces standalone native executables. Unlike a traditional transpiler that emits JavaScript or a bytecode runtime, `scriptc` lowers a TypeScript program all the way down to machine code, so the output binary has no runtime dependency on Node.js, Deno, Bun, or V8. Source: [README.md:1-40]()

The compiler is organised as a workspace of focused packages rather than a single monolithic binary. The CLI package drives the build, the compiler package owns the front-to-back pipeline, and a small `tsc`-based workspace handles the build itself. Source: [packages/compiler/src/index.ts:1-60]()

The promise of the project is captured in the landing page headline: write TypeScript, ship a native binary. Source: [docs/src/app/page.tsx:1-80]()

## The Three-Tier Compilation Model

Compilation in `scriptc` is split into three cleanly separated tiers. Each tier has a single responsibility, an isolated directory inside `packages/`, and a public surface that the next tier consumes. Source: [docs/src/app/how-it-works/page.mdx:1-60]()

```mermaid
flowchart LR
  A[TypeScript Source] --> B[Tier 1: Frontend]
  B --> C[Tier 2: IR / Lowering]
  C --> D[Tier 3: Native Codegen]
  D --> E[Native Executable]
```

### Tier 1 — TypeScript Frontend

The first tier reuses the TypeScript compiler itself to parse, bind, and type-check the input program. Rather than reinvent a type system, `scriptc` shells out to `tsc`'s `createProgram` API and walks the resulting `TypeChecker` output to obtain a fully type-resolved AST. Source: [packages/compiler/src/index.ts:60-140]()

This is the same machinery that powers IDEs, which is why `scriptc` accepts a `tsconfig.json` and understands the same project references and path aliases a developer would expect. The type information collected at this stage is consumed by the next tier to drive accurate lowering. Source: [docs/src/app/how-it-works/page.mdx:60-120]()

### Tier 2 — Intermediate Representation and Lowering

The second tier walks the typed AST and lowers it into a language-agnostic intermediate representation. At this stage, TypeScript-specific constructs (e.g. generics, structural types, `as` casts) are erased, control-flow is normalised, and each top-level statement is mapped to a primitive IR construct. Source: [packages/compiler/src/index.ts:140-240]()

The IR is intentionally close to LLVM IR so that the backend tier can be implemented as a relatively direct emitter rather than as a full optimiser. Keeping the IR minimal also makes it cheap to add new front-ends in the future — a direction raised by community members asking about JavaScript support in issue #13. Source: [docs/src/app/introduction/page.mdx:1-60]()

### Tier 3 — Native Code Generation

The third tier takes the IR and produces an executable. Generation is delegated to `clang` from an LLVM toolchain, which links against the standard runtime and emits a host-target binary by default. Cross-target builds (for example Windows `.exe` from a POSIX host) are supported by passing an explicit target triple. Source: [packages/compiler/src/index.ts:240-340]()

Windows-specific behaviour — slash-normalised paths, the required `.exe` suffix, and shell-quoting of the workspace build command — lives in this tier and is what was fixed in v0.0.17 to make `scriptc run` work on Windows 10/11 without WSL. Source: [README.md:40-120]()

## CLI Entry Point and Lifecycle

The user-facing binary is `scriptc`. Its two most important subcommands are `scriptc build`, which produces a native executable from one or more entry files, and `scriptc run`, which builds and immediately executes the resulting program in a single invocation. Source: [README.md:60-160]()

Under the hood, `scriptc run` performs three steps: it resolves the project using TypeScript's virtual filesystem, runs the full three-tier pipeline, and then `exec`s the freshly built binary — replacing the current process on POSIX or invoking it through a shell on Windows. Source: [docs/src/app/how-it-works/page.mdx:120-200]()

## Current Limitations and Roadmap

The project is published as `0.0.x` and is explicitly pre-stable. The native runtime currently covers the TypeScript subset used by the included examples; language features outside that subset may lower to runtime stubs rather than compiled code. Source: [docs/src/app/introduction/page.mdx:60-140]()

Two open community threads map directly onto the tiers. Issue #10 reports that `scriptc run` fails on Windows because the TypeScript virtual filesystem does not see slash-normalised paths; the fix landed in v0.0.17 by normalising paths in the frontend before they reach `createProgram`. Issue #13 requests broader JavaScript support, which would require a parallel or shared IR lowering path in Tier 2 since JavaScript lacks the static type information that Tier 1 currently relies on. Source: [README.md:40-120]()

---

<a id='page-2'></a>

## Frontend and Lowering: TypeScript to Typed IR

### Related Pages

Related topics: [Introduction and Three-Tier Compilation Model](#page-1), [Backend Emitters and C Runtime](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [packages/compiler/src/frontend/program.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/frontend/program.ts)
- [packages/compiler/src/frontend/types.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/frontend/types.ts)
- [packages/compiler/src/frontend/ts7/program.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/frontend/ts7/program.ts)
- [packages/compiler/src/frontend/ts7/adapter.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/frontend/ts7/adapter.ts)
- [packages/compiler/src/frontend/ts7/ast.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/frontend/ts7/ast.ts)
- [packages/compiler/src/frontend/ts7/checker.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/frontend/ts7/checker.ts)
</details>

# Frontend and Lowering: TypeScript to Typed IR

## Purpose and Scope

The `frontend` package is the entry point of the scriptc compiler pipeline. It consumes a TypeScript source program, runs the TypeScript compiler API against it, and produces a fully typed intermediate representation (IR) that the rest of the compiler — codegen, optimization, and backend emission — consumes. The frontend is intentionally the only layer that depends on the TypeScript compiler API; everything downstream is expressed in terms of the scriptc IR so the project can swap in alternative frontends (for example, a future JavaScript / ECMAScript frontend as discussed in issue #13) without rewriting the backend.

Two distinct concerns live under `packages/compiler/src/frontend`:

1. A frontend-agnostic façade that exposes the typed IR (in `program.ts` and `types.ts`).
2. A concrete TypeScript 7–based implementation under `ts7/` that adapts the TypeScript compiler API to that façade.

The "lowering" half of the topic is the act of converting TypeScript's checker-resolved AST, types, and symbols into scriptc's own IR nodes — a representation that retains type information but is no longer bound to TypeScript's internal data structures.

Source: [packages/compiler/src/frontend/program.ts]()
Source: [packages/compiler/src/frontend/types.ts]()
Source: [packages/compiler/src/frontend/ts7/program.ts]()

## High-Level Workflow

The frontend pipeline is a linear transformation:

1. **Program creation** — the ts7 program module wires up a TypeScript `System` (virtual filesystem), configures compiler options, and calls `createProgram` to parse and resolve the user's source files.
2. **AST capture** — the scriptc AST is built by walking the TypeScript AST and producing IR nodes that mirror the structural shape of the source while stripping away TypeScript-internal markers.
3. **Type resolution** — the checker module invokes the TypeScript type checker, then folds each node's resolved type into the IR, producing a typed IR where every binding carries a scriptc type descriptor.
4. **Adapter export** — the adapter module re-exports the typed IR through the frontend-agnostic façade so downstream packages (ir, codegen, runtime) can consume it without importing TypeScript.

The split between the ts7 implementation and the generic façade is deliberate: it isolates the dependency on the TypeScript compiler API to a single directory, making the rest of the compiler independent of the parser choice.

Source: [packages/compiler/src/frontend/ts7/program.ts]()
Source: [packages/compiler/src/frontend/ts7/adapter.ts]()
Source: [packages/compiler/src/frontend/ts7/ast.ts]()
Source: [packages/compiler/src/frontend/ts7/checker.ts]()

## ts7 Frontend Implementation

### Program construction

`ts7/program.ts` is responsible for turning a user-supplied source path into a `ts.Program`. It constructs a `ts.System` backed by the scriptc virtual filesystem so that `createProgram` resolves imports and `tsconfig.json` files consistently across platforms. This is the layer that the v0.0.17 release notes call out for Windows fixes: the virtual filesystem now normalizes path separators before the TypeScript program sees them, and default executable names carry the `.exe` suffix that `createProgram` expects on Windows. Without this normalization, Windows users hit the `"ts7 createProgram: project failed to open"` error reported in issue #10.

Source: [packages/compiler/src/frontend/ts7/program.ts]()

### AST module

`ts7/ast.ts` walks the TypeScript AST and produces scriptc IR nodes. The output is a structurally faithful copy of the source — declarations, expressions, statements, and modifiers — but expressed in the IR's own node types rather than `ts.Node` subclasses. This module is the lowest layer of the lowering: it is purely structural and does not yet attach type information.

Source: [packages/compiler/src/frontend/ts7/ast.ts]()

### Checker module

`ts7/checker.ts` is where type information is folded into the AST. It asks the TypeScript checker for the resolved type of each binding and expression, then maps those `ts.Type` instances into the scriptc type system defined in `frontend/types.ts`. The result is a typed IR where every node carries a `scriptcType` descriptor suitable for downstream code generation. Because the checker relies on the TypeScript API for inference, this is also the layer most affected by upstream changes in TypeScript's checker — and the reason the project is pinned to a specific compiler version.

Source: [packages/compiler/src/frontend/ts7/checker.ts]()
Source: [packages/compiler/src/frontend/types.ts]()

### Adapter

`ts7/adapter.ts` is the boundary between the TypeScript-specific implementation and the rest of the compiler. It exports the typed IR through the generic `frontend` façade (re-exported from `frontend/program.ts`) and is the only module that downstream packages are expected to import indirectly. Keeping this boundary explicit is what makes the JavaScript frontend discussed in issue #13 plausible — a future `ts7/js` (or similarly named) directory could implement the same adapter contract without types being re-inferred from scratch.

Source: [packages/compiler/src/frontend/ts7/adapter.ts]()
Source: [packages/compiler/src/frontend/program.ts]()

## Typed IR and Frontend Façade

`frontend/types.ts` defines the scriptc type model that the checker emits into the IR. It is deliberately narrower than the TypeScript type system: it captures the subset that scriptc can faithfully lower to native code (primitives, references, functions, structs, tuples, unions as tagged variants, and so on). Types that fall outside this subset lower to runtime checks or are rejected with a diagnostic.

`frontend/program.ts` exposes the compiled `Program` value to the rest of the compiler. It is the public entry point that the CLI's `scriptc run` command invokes; it in turn delegates to the ts7 adapter. Centralizing this in one file means that swapping the parser, adjusting diagnostics, or adding new top-level compiler options does not require touching the CLI.

Source: [packages/compiler/src/frontend/types.ts]()
Source: [packages/compiler/src/frontend/program.ts]()

## Architecture Summary

| Layer | Module | Responsibility |
| --- | --- | --- |
| Public façade | `frontend/program.ts` | Exposes the typed IR to the CLI and downstream packages |
| IR type model | `frontend/types.ts` | Defines the scriptc type system used by the IR |
| Parser setup | `ts7/program.ts` | Builds the `ts.Program` over the virtual filesystem |
| AST lowering | `ts7/ast.ts` | Converts `ts.Node` tree into IR nodes |
| Type lowering | `ts7/checker.ts` | Maps `ts.Type` into scriptc types and attaches them to IR |
| Boundary | `ts7/adapter.ts` | Re-exports the typed IR through the frontend façade |

Source: [packages/compiler/src/frontend/program.ts]()
Source: [packages/compiler/src/frontend/types.ts]()
Source: [packages/compiler/src/frontend/ts7/program.ts]()
Source: [packages/compiler/src/frontend/ts7/adapter.ts]()
Source: [packages/compiler/src/frontend/ts7/ast.ts]()
Source: [packages/compiler/src/frontend/ts7/checker.ts]()

## Community-Relevant Notes

- **Windows path handling** (issue #10, fix in v0.0.17): the virtual filesystem layer in `ts7/program.ts` had to normalize path separators before `createProgram` could resolve the project. This is the most user-visible frontend bug to date and is pinned by a Windows CI lane.
- **JavaScript support** (issue #13): the author of JS² notes that scriptc is TypeScript-only today. The frontend/adapter split is designed so a JavaScript frontend could reuse the IR and backend by providing a new `ts7` (or `js`) directory that implements the same adapter contract — the structural placeholders are already there.

---

<a id='page-3'></a>

## Backend Emitters and C Runtime

### Related Pages

Related topics: [Frontend and Lowering: TypeScript to Typed IR](#page-2), [CLI, Operations, Escape Hatches, and Platforms](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [packages/compiler/src/backend/cc.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/backend/cc.ts)
- [packages/compiler/src/backend/emission/emitter.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/backend/emission/emitter.ts)
- [packages/compiler/src/backend/emission/emit-exprs.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/backend/emission/emit-exprs.ts)
- [packages/compiler/src/backend/emission/emit-stmts.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/backend/emission/emit-stmts.ts)
- [packages/compiler/src/backend/emission/emit-async.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/backend/emission/emit-async.ts)
- [packages/compiler/src/backend/emission/emit-island.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/backend/emission/emit-island.ts)
</details>

# Backend Emitters and C Runtime

The backend is the final stage of the scriptc compilation pipeline. After the frontend has produced a typed intermediate representation (IR) from a TypeScript program, the backend emitters translate that IR into C source code, prepend a runtime header, and write a self-contained `.c` file that can be compiled with `clang` into a native executable. The accompanying C runtime supplies the language primitives that TypeScript assumes but C does not natively provide: object allocation, property access, `console` output, and async/await scheduling.

## Pipeline Position and Entry Point

The top-level `compileCC` function lives in `packages/compiler/src/backend/cc.ts`. It receives the compiled TypeScript IR along with metadata describing the target program, and orchestrates the rest of the backend steps. It instantiates an `Emitter`, feeds it the IR, then writes the resulting C text to disk and invokes `clang` to produce a native binary. The execution flow looks roughly like this:

| Stage | Module | Responsibility |
|-------|--------|----------------|
| 1. Setup | `cc.ts` | Build the `Emitter` with the active target and configuration |
| 2. Statements | `emit-stmts.ts` | Emit top-level and function-level statements |
| 3. Expressions | `emit-exprs.ts` | Emit expressions and operator lowering |
| 4. Async | `emit-async.ts` | Lower `async`/`await`/`Promise` into runtime continuations |
| 5. Islands | `emit-island.ts` | Inline raw C "islands" supplied by the user via the `/*-*/` syntax |
| 6. Wrap-up | `cc.ts` | Concatenate runtime + emitted code, invoke `clang` |

## The Emitter Class

`packages/compiler/src/backend/emission/emitter.ts` defines the `Emitter` class, which is the single object passed through every emission helper. It holds:

- A buffer (`TextWriter`) accumulating C source as the traversal proceeds.
- The current target triple and runtime version, which decide whether Windows-style executable suffixes or path separators are used (relevant to issue #10's Windows fix in v0.0.17).
- A symbol table mapping IR identifiers to C identifiers.
- Helper methods such as `temp()`, `label()`, and `emit()` that other emitters call.

Every other emission file — `emit-exprs`, `emit-stmts`, `emit-async`, `emit-island` — exports functions that take the `Emitter` plus a typed IR node and append C source to the buffer. This keeps individual emitters small and focused on a single syntactic category while sharing the same output stream. `Source: [packages/compiler/src/backend/emission/emitter.ts:1-80]()`

## Statement and Expression Emission

`emit-stmts.ts` is responsible for control flow: `if`/`else`, `while`, `for`, `for...of`, `for...in`, `switch`, `try`/`catch`, `return`, `throw`, and function/arrow declarations. Each TS construct is lowered to a C equivalent. Closures are lowered to structs that capture their free variables; loops are emitted using C99 `for` syntax; labelled `break`/`continue` is lowered to C `goto`. `Source: [packages/compiler/src/backend/emission/emit-stmts.ts:1-60]()`

`emit-exprs.ts` handles the value world: binary and unary operators, calls, member access, literals, template strings, array/object literals, and the implicit conversions between TypeScript's boxed types (`number`, `string`, `boolean`, `null`, `undefined`) and C primitives. Property access on dynamic objects is lowered to runtime calls (for example `sc_get(obj, key)`); arithmetic on `bigint` is routed through dedicated runtime helpers so that it does not collide with the platform `long long`. `Source: [packages/compiler/src/backend/emission/emit-exprs.ts:1-80]()`

## Async, Promises, and the Runtime

`emit-async.ts` is the most interesting file from a runtime perspective. TypeScript's `async`/`await` model has no direct C analog, so the emitter transforms each `async` function into a state-machine function: every `await` becomes a switch case on a `state` variable, and the body is rewritten into a loop that resumes when the awaited promise settles. Each `await` is also a potential suspension point for the runtime scheduler, which multiplexes many such state machines on top of a small thread pool. `Source: [packages/compiler/src/backend/emission/emit-async.ts:1-80]()`

The supporting runtime lives in `packages/compiler/src/backend/c/` (adjacent to the emission files) and is concatenated in front of the emitted code by `cc.ts`. It exposes the C functions that the emitters call:

- Object allocation and a stop-and-copy garbage collector (`sc_alloc`, `sc_collect`).
- Property tables and dynamic dispatch (`sc_get`, `sc_set`, `sc_call`).
- A `console_log` family used by the `console` global.
- Promise primitives used by `emit-async` (`sc_promise_new`, `sc_promise_then`, `sc_await`).

Because the runtime is a single C snippet prepended to user code, the final binary is fully self-contained — no shared library, no installed package. This is also why Windows support in v0.0.17 had to ensure both the runtime headers and the produced executable respected `.exe` suffixes and forward-slash paths: the entire toolchain assumes a single, portable textual artifact.

## Islands and Native Escape Hatches

`emit-island.ts` handles "islands" — literal C source that the author embeds directly in a TypeScript file via the `/*-*/` comment syntax. Islands allow escape-hatch access to the C runtime and to libc without leaving the scriptc toolchain, and they are how the standard library (in `packages/compiler/src/stdlib/`) bridges to native I/O, networking, and other POSIX facilities. The emitter copies the island text verbatim into the output buffer, surrounded by helpers that allocate identifiers unique to the surrounding scope. `Source: [packages/compiler/src/backend/emission/emit-island.ts:1-40]()`

## Summary

The backend emitters form a coordinated set: `emitter.ts` owns the shared context, `emit-stmts.ts` and `emit-exprs.ts` lower the bulk of TypeScript syntax, `emit-async.ts` translates the async model into a runtime-scheduled state machine, and `emit-island.ts` preserves user-supplied native code. Together with the prepended C runtime, they turn a TypeScript program into a single C file that `clang` can compile directly into a native executable on Linux, macOS, and (since v0.0.17) Windows.

---

<a id='page-4'></a>

## CLI, Operations, Escape Hatches, and Platforms

### Related Pages

Related topics: [Introduction and Three-Tier Compilation Model](#page-1), [Frontend and Lowering: TypeScript to Typed IR](#page-2), [Backend Emitters and C Runtime](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [packages/cli/src/main.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/cli/src/main.ts)
- [packages/cli/src/paths.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/cli/src/paths.ts)
- [packages/compiler/src/ffi/profile.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/ffi/profile.ts)
- [packages/compiler/surface-manifest.json](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/surface-manifest.json)
- [packages/compiler/src/library/sidecar.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/library/sidecar.ts)
- [packages/compiler/src/library/sidecar-validate.ts](https://github.com/vercel-labs/scriptc/blob/main/packages/compiler/src/library/sidecar-validate.ts)
</details>

# CLI, Operations, Escape Hatches, and Platforms

The scriptc toolchain is delivered as a Node.js CLI fronted by the compiler package. This page covers the four interlocking concerns that govern how a developer interacts with scriptc from a shell: the CLI entry point, the path/operations layer that bridges shells and the TypeScript virtual filesystem, the FFI profile that selects a target platform, and the escape hatches (sidecars and the surface manifest) that let advanced users escape the generated runtime.

## CLI Entry Point and Subcommands

The CLI is bootstrapped from `packages/cli/src/main.ts`, which acts as the dispatcher for top-level subcommands such as `scriptc run`, `scriptc build`, and the workspace orchestration commands documented in the release notes. Source: [packages/cli/src/main.ts]().

The entry point resolves the project's TypeScript program through `ts.createProgram`, which is why Windows shells historically failed with `ts7 createProgram: project failed to open` when paths were not normalized (issue #10). The CLI delegates slash normalization to the operations layer rather than relying on the host shell, keeping the program construction deterministic across PowerShell, Git Bash (MINGW64), and POSIX shells.

## Path Operations and Cross-Platform Behavior

All filesystem operations used during compilation are funneled through `packages/cli/src/paths.ts`. This module normalizes absolute and relative paths to forward slashes before they enter TypeScript's virtual filesystem, applies the `.exe` suffix when constructing default executable names for any Windows build (native or cross-target), and rewrites shell-quoted arguments so the workspace build command survives Windows quoting semantics. Source: [packages/cli/src/paths.ts]().

These fixes are explicitly enumerated in the v0.0.17 changelog: "TypeScript's virtual filesystem now sees consistently slash-normalized Windows paths, default executable names carry the required `.exe` suffix for both native and cross-target Windows builds, and the workspace build command survives Windows shell quoting." The CI lane added in the same release pins these regressions so future Windows breakage fails fast rather than slipping into a published CLI build. Source: [packages/compiler/src/paths.ts]() (call-out referenced in release notes).

The diagram below shows the path flow from shell invocation to the compiler's filesystem view.

```mermaid
flowchart LR
  Shell[Shell - PowerShell / bash / zsh] --> CLI[main.ts dispatcher]
  CLI --> Paths[paths.ts normalize]
  Paths --> VFS[TypeScript virtual FS - forward slashes]
  VFS --> Program[ts.createProgram]
  Program --> Codegen[IR / codegen]
  Codegen --> Exe[Native executable .exe on Windows]
```

## FFI Profiles and Platform Targeting

The compiler selects how to lower foreign-function interface calls through `packages/compiler/src/ffi/profile.ts`. A profile is a named bundle of calling-convention metadata, library search rules, and type-marshalling directives keyed to a target triple (for example `x86_64-pc-windows-msvc`, `aarch64-apple-darwin`, or `wasm32-wasi`). The CLI passes the resolved profile to the codegen pipeline so that imported symbols from a C/C++ sidecar resolve against the correct ABI. Source: [packages/compiler/src/ffi/profile.ts]().

Because Windows ships multiple C runtimes (MSVC vs. MinGW), the profile module also resolves the toolchain binary (clang, link, lib) and propagates any required `.lib` import library references. The CLI relies on this profile to decide whether to emit a `.exe` artifact or an undecorated ELF/Mach-O binary, aligning with the path-suffix logic in `paths.ts`. Source: [packages/compiler/src/ffi/profile.ts]().

## Escape Hatches: Sidecars and the Surface Manifest

When a generated program must interoperate with a hand-written native library, scriptc offers two escape hatches that bypass the normal IR lowering.

The first is the **sidecar** mechanism in `packages/compiler/src/library/sidecar.ts`. A sidecar is an external library (typically a `.so`, `.dylib`, `.dll`, or `.exe` plus headers) that the generated binary loads at runtime. The module loads the declared sidecar, resolves its exported symbols against the FFI profile, and arranges for the appropriate loader path on the target platform. Source: [packages/compiler/src/library/sidecar.ts]().

The second layer is validation through `packages/compiler/src/library/sidecar-validate.ts`, which runs before code generation and checks that the requested symbols, argument types, and calling conventions match the sidecar's actual ABI. Mismatches surface as compile-time errors rather than runtime crashes, giving users a deterministic failure path. Source: [packages/compiler/src/library/sidecar-validate.ts]().

The third escape hatch is the **surface manifest**, declared at `packages/compiler/surface-manifest.json`. This file enumerates the compiler's externally observable surface (public IR nodes, supported FFI types, CLI flags, and version metadata). Tooling authors and integrators consult this manifest to detect breaking changes and to drive code generation in downstream clients. Source: [packages/compiler/surface-manifest.json]().

## Related Community Signals

The Windows fix in v0.0.17 directly addresses issue #10, where native Windows shells could not run `scriptc run` because `ts.createProgram` rejected mixed-slash paths. A separate request, issue #13, asks for JavaScript/ECMAScript support; this sits at the boundary of the escape hatches because adding a JS IR layer would require entries in the surface manifest and likely a new FFI profile for JS runtimes. Neither request changes the CLI dispatch model described here, but both extend it.

## Summary

- `main.ts` is the single CLI entry point and selects subcommands.
- `paths.ts` normalizes paths so the TypeScript virtual FS behaves identically on Windows and POSIX.
- `ffi/profile.ts` selects ABI, toolchain, and executable-suffix rules per target.
- `sidecar.ts`, `sidecar-validate.ts`, and `surface-manifest.json` provide the documented escape hatches for native interop and downstream tooling.

Together, these modules turn the CLI into a portable operations layer with predictable failure modes and well-marked extension points.

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: vercel-labs/scriptc

Summary: Found 9 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Installation risk - Installation risk requires verification.

## 1. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/vercel-labs/scriptc/issues/3

## 2. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/vercel-labs/scriptc/issues/10

## 3. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://news.ycombinator.com/item?id=49063175

## 4. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://news.ycombinator.com/item?id=49063175

## 5. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://news.ycombinator.com/item?id=49063175

## 6. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://news.ycombinator.com/item?id=49063175

## 7. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/vercel-labs/scriptc/issues/13

## 8. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://news.ycombinator.com/item?id=49063175

## 9. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://news.ycombinator.com/item?id=49063175

<!-- canonical_name: vercel-labs/scriptc; human_manual_source: deepwiki_human_wiki -->
