# EX Script — Toolchain

## One binary, one command per task

The entire toolchain ships as the `ex` command. There is no plugin system, no
per-tool config files, no version skew between formatter and compiler — they
share the same AST and the same version.

```
ex init            create a new project (zero-config default)
ex run             check + build + run the current project
ex build           emit the production bundle
ex test            discover and run tests (unit, integration, async)
ex fmt             format the project (deterministic, idempotent)
ex lint            check the project against built-in rules
ex migrate         convert a TypeScript project to EX Script
ex add             add a package dependency
ex install         install dependencies from the manifest (locked)
ex doc             generate API documentation from source
ex explain <code>  explain an error code in detail
ex new <name>      generate a new file/module scaffold
ex info            show toolchain and project info
```

## `ex init`

Creates:

```
my-project/
├── project.xan          # manifest (optional; defaults shown commented)
├── src/
│   └── main.xan         # hello world with a test
└── ex.lock             # empty lockfile (created on first install)
```

That's the whole project. No tsconfig, no bundler config, no test config, no
linter config. The manifest, when needed, is one file:

```
# project.xan
name = "my-project"
version = "0.1.0"
entry = "src/main.xan"
deps = []
workspaces = []
```

## `ex fmt`

Canonical formatting, enforced by the formatter and applied to every `.xan`
file. Rules are minimal and machine-enforceable:

- 2-space indentation; braces on the same line (`} else {` on one line).
- One statement per line; no trailing whitespace; single final newline.
- Spaces around binary operators and inside interpolations; no spaces inside
  `( )`, `[ ]` in index position; one space after commas.
- Long lines are wrapped by the formatter with the standard continuation
  indent; the formatter never produces lines longer than 100 columns.
- Formatting is idempotent: `ex fmt` on formatted code is a no-op (tested).

## `ex lint`

Built-in rules (each `ex lint --fix`-able where marked):

| Rule | What it catches |
|---|---|
| `unused-var` | declared but never used (fix: remove) |
| `unused-import` | imported but never referenced (fix: remove) |
| `prefer-immutable` | `let mut` that is never reassigned → `let` (fix) |
| `no-dynamic` | `Any` values flowing into typed positions without a boundary (schema/narrow) |
| `no-throw` | `throw` used in EX code (use `raise`/`Result`) |
| `no-null` | `null` literal used (does not exist; use optional) |
| `no-assert` | `as` used where `.require`/`schema` fit better |
| `shadowing` | shadowing a name from an outer scope |
| `todo` | `TODO`/`FIXME` comments |

`ex lint` exits non-zero on violations; severity and rule toggles live in the
manifest when the defaults don't fit.

## `ex test`

- Discovers `test "name" { ... }` blocks in `src/`, `tests/`, and `test` folders.
- Runs tests in parallel (async tests included), in random order per run
  (`--seed` to fix).
- Assertions via `expect`: `.eq`, `.ne`, `.truthy`, `.falsy`, `.raises`,
  `.fails`, `.contains`, `.match` (regex), `.approx` (float).
- Snapshots: `expect(x).snapshot()` records/compares against
  `tests/snapshots/` (planned for beta; `--update` flag).
- Coverage: `ex test --coverage` (planned; V8 coverage data, `tests/coverage/`).
- Property-based tests: `test "invariant" { forAll(items, ...) }` (planned).
- Mocks: the stdlib provides `test.mock(obj, fn)` recording/restoring wrappers.

## `ex doc`

- Parses the checked AST and emits `docs/api/` with: per-module pages,
  function signatures, schemas with constraints, examples from doc comments,
  type information, and cross-links. Source links are planned with source maps.
- Doc comments: `///` lines above declarations.

## `ex build`

- Emits `dist/main.js` (bundle) and `dist/type-table.json` (package's public
  type table, consumed by dependents).
- `--prod` enables dead-code elimination and minification (via the stdlib
  pipeline; a bundler is not required).
- `--watch` rebuilds changed modules incrementally (planned).

## `ex explain`

Every diagnostic code is documented:

```
$ ex explain E1007
E1007 — optional value used where a guaranteed value is required
...
```

## The shared core

All tools share: lexer, parser, AST, checker, formatter, and the span data.
The linter and formatter never re-parse the user's files; they consume the same
AST the compiler produced. This is why the toolchain is consistent by
construction.

## Language server (planned, beta)

`ex lsp` — LSP server providing: completion, hover types, go-to-definition,
find-references, diagnostics on save, document symbols, rename, formatting,
code actions (apply the "Possible fixes" suggestions). The compiler is designed
for this: checker results are a side table over an immutable AST, so the server
can re-check on change without re-parsing unchanged files.

## Debugger integration (planned)

Source maps let Node's inspector show `.xan` source; `ex debug` wraps
`node --inspect` and forwards breakpoints by line.