# Quickstart

URL: /docs/quickstart
Markdown: /docs/quickstart.md

> Build the Sakura Lang compiler from source, then check, test and run a first program.

> **Pre-alpha: build from source**
>
> The compiler runs the core language and substantial M3 functionality on JavaScript, including contracts, std handlers, permissions, scaffolding, LSP and MCP. No release or installer is published. See the [roadmap](/docs/roadmap) for remaining M3 gates and planned M4/M5 work.

## Install from source [#install-from-source]

You need [rustup](https://rustup.rs) and [Node.js](https://nodejs.org) (`sakura run` and `sakura test` execute the generated JavaScript with `node`; CI uses Node 26). The repository pins its Rust toolchain in `compiler/rust-toolchain.toml`, so rustup fetches the right version on first build.

```bash
git clone https://github.com/icantcodefyi/sakura-lang
cd sakura-lang/compiler
cargo install --locked --path crates/sakura_cli
sakura --version
```

```text
sakura 0.0.0
```

The standard library is embedded in the binary, so `sakura` works from any directory. To try it without installing, run `cargo run -p sakura_cli -- <command>` inside `compiler/`.

## Run an example [#run-an-example]

`compiler/examples/` holds the design's example programs as projects. All twelve pass the configured run/test/check modes listed on the [roadmap](/docs/roadmap):

```bash
sakura run compiler/examples/counter
sakura test compiler/examples/typed_errors
```

```text
1. milk
2. eggs
1. bread
```

`counter` is a user-defined effect whose handler keeps state; `typed_errors` recovers from typed failures with `try`/`catch`. The examples README records each mode; `agent_loop` and `secrets` currently have check-only coverage.

## Create a project [#create-a-project]

Run `sakura new cli hello` to scaffold a project (`lib`, `agent`, and `api` templates also exist), or write the two files below by hand. A project is a `sakura.toml` manifest and a `src/` directory, and one file is one module.

```toml title="hello/sakura.toml"
[package]
name = "hello"
version = "0.1.0"
edition = "2026"

[permissions]
```

`[permissions]` is the ceiling on what the program may do (design §6). This program only prints, so it is empty. `sakura run` enforces startup grants and passes the table as `SAKURA_PERMISSIONS` for host-handler enforcement. Missing required root authority exits with 75.

```sakura title="hello/src/main.sakura"
/// Build a greeting for a person.
fn greet(name: String) -> String { "Hello, {name}" }

pub fn main() -> Unit ! {Console} { Console.print(greet("world")) }

test "greets by name" { expect_eq(greet("Ada"), "Hello, Ada") }

property "always starts with Hello"(name: String) {
  expect(String.starts_with(greet(name), "Hello, "))
}
```

`greet` has no `!`, so it is pure. `main` declares `Console`, the only effect the program performs. This is the canonical layout: `sakura fmt` keeps a short body on its signature's line.

## Check, test and run [#check-test-and-run]

Run these from `hello/`. With no path, each command uses `src/`.

```bash
sakura check
```

```text
no problems
```

`sakura check` parses, resolves names, checks types and effects, and checks that every `match` is exhaustive. It generates no code, and it accepts typed holes.

```bash
sakura test
```

```text
test greets by name ... ok
test always starts with Hello ... ok

2 passed, 0 failed, 0 skipped (seed 0)
```

`sakura test` runs every `test` and `property` block under deterministic handlers (a fixed clock, a seeded random source, captured console output) with the network denied. `--seed 7` changes the property inputs, `--filter greets` runs matching tests only, and `--json` writes one record per test.

```bash
sakura run
```

```text
Hello, world
```

`sakura run` checks the program, compiles it to JavaScript under `target/sakura/js/`, and runs `main` with `node`. Arguments after `--` go to the program. The exit code is the program's: `0` for success, `1` when `main` fails with an uncaught typed error, `70` for a defect such as integer overflow, with one JSON `defect` record on stderr.

`sakura fmt` rewrites files in the one canonical format; `sakura fmt --check` changes nothing and exits `1` if a file is not formatted.

## Check it, as an agent would [#check-it-as-an-agent-would]

Suppose `greet` starts printing without declaring it:

```sakura title="hello/src/main.sakura"
/// Build a greeting for a person.
fn greet(name: String) -> String {
  Console.print("greeting {name}")
  "Hello, {name}"
}

pub fn main() -> Unit ! {Console} { Console.print(greet("world")) }
```

```text
error[V3001]: function `greet` performs `Console`, which its signature does not allow (allowed: `{}`)
  ┌─ src/main.sakura:2:4
  │
2 │ fn greet(name: String) -> String {
  │    ^^^^^
3 │   Console.print("greeting {name}")
  │   -------------------------------- `Console.print` performs `Console` here
  │
  = expected: {}
  =    found: {Console}
  = help: Add `Console` to the signature
  = help: Handle the effect locally with `with`
  = see `sakura explain V3001`

1 error
```

`sakura check --json` writes the same error as JSON Lines, one record per line, followed by a `summary` record. The `diagnostic` record, pretty-printed with hashes shortened:

```json
{
  "schema_version": "1.0",
  "kind": "diagnostic",
  "id": "d:e8f9a0e1d729d3d1",
  "code": "V3001",
  "severity": "error",
  "phase": "effect",
  "message": "function `greet` performs `Console`, which its signature does not allow (allowed: `{}`)",
  "snapshot": "sha256:9470da24…",
  "symbol_id": null,
  "spans": [
    { "uri": "file:///…/hello/src/main.sakura", "content_hash": "sha256:f1ee9f10…",
      "start_byte": 38, "end_byte": 43, "role": "primary" },
    { "uri": "file:///…/hello/src/main.sakura", "content_hash": "sha256:f1ee9f10…",
      "start_byte": 72, "end_byte": 104, "role": "cause",
      "label": "`Console.print` performs `Console` here" }
  ],
  "related_ids": [],
  "expected": { "render": "{}", "row": { "labels": [], "tail": null } },
  "actual": { "render": "{Console}", "row": { "labels": [{ "effect": "std::Console", "args": [] }], "tail": null } },
  "obligation": null,
  "counterexample": null,
  "explanation_uri": "sakura://explain/V3001",
  "fixes": [
    { "id": "f:e8f9a0e1d729d3d1.0", "title": "Add `Console` to the signature",
      "applicability": "suggested", "impact": "interface_change",
      "edits": [{ "span": { "uri": "file:///…/hello/src/main.sakura", "content_hash": "sha256:f1ee9f10…",
                            "start_byte": 67, "end_byte": 67, "role": "primary" },
                  "replacement": " ! {Console}" }] },
    { "id": "f:e8f9a0e1d729d3d1.1", "title": "Handle the effect locally with `with`",
      "applicability": "incomplete", "impact": "local",
      "edits": [/* wraps the body in `with ?handler { … }` */] }
  ]
}
```

Spans are zero-based, half-open UTF-8 byte offsets, and an edit applies only while `content_hash` still matches. The first fix changes the function's interface, so it is `suggested`, never `machine_applicable`: an agent must not widen a signature silently. `sakura explain V3001` prints the rationale with a wrong and a fixed program. See [Diagnostics](/docs/agents/diagnostics) for the format.

A typed hole is how an agent asks the compiler what fits:

```sakura
fn unfinished(x: Int) -> Int { ?result }
```

```text
hole ?result: expected Int, effects {}; locals [x: Int]; candidates [x, Int.abs(?i), Int.clamp(?i, ?lo, ?hi), …]
```

`sakura check` accepts holes; `sakura build` and `sakura run` reject them with `V2050`. See [Typed holes](/docs/agents/typed-holes).

## Commands [#commands]

| Command | Today |
| --- | --- |
| `sakura check [--json] [paths]` | Parse, resolve, type and effect check, exhaustiveness. Holes allowed |
| `sakura build [paths]` | Check, then generate JavaScript. Holes rejected (`V2050`) |
| `sakura run [path] [-- args]` | Build and run `main` with `node` |
| `sakura test [--json] [--seed N] [--filter text] [--update-snapshots]` | Run tests and properties with deterministic handlers |
| `sakura fmt [--check] [paths]` | Canonical format. There are no options |
| `sakura explain V3001` | Rationale, causes and repairs for a diagnostic code |
| `sakura prompt` | Print the language guide for agents ([llms.txt](https://github.com/icantcodefyi/sakura-lang/blob/master/docs/llms.txt)) |

Also available: `sakura new`, `sakura init`, `sakura fix`, `sakura check --frozen-specs all [--since rev]`, `sakura lsp`, and `sakura mcp serve`. `sakura doc` and trust-site `sakura audit` are M3 completion work. Package commands remain planned; verification/WASM are M4 and native is M5.

## Next [#next]

Start the [Language Tour](/docs/tour/basics).
