# sexc > sexc is a package manager and build system for C, written entirely in C. It is inspired by npm and pnpm: packages have a sexc.json manifest, a repo can be a workspace of many packages, and dependencies are strict. Anyone can register, publish and install packages on the registry at https://sexc.dev. Every command has machine-readable output, and an MCP server exposes them all as tools. Status: early but working. The build system, workspaces, tests, the CLI, the MCP server and the registry (accounts, tokens, publishing, installing, a lockfile and a shared store) all work today. ## Quickstart - Build sexc from source with only a C compiler: `cc -o bootstrap bootstrap.c && ./bootstrap` - `sexc init mylib` creates a package; `sexc build` builds; `sexc test` tests; `sexc run -- args` runs. - `sexc help --json` describes every command, flag, default and exit code as JSON. ## Commands - `sexc build [packages...] [-j N] [--profile P | --release] [-v] [--json]`: build packages and their dependencies, incrementally and in parallel. Writes compile_commands.json. - `sexc test [packages...] [--timeout S] [--json]`: build, then compile and run every program in each package's tests/ folder. - `sexc run [-- args...]`: build an executable package and run it. - `sexc ls [--json]`: list the workspace's packages. - `sexc graph [--json]`: dependency order and edges. - `sexc init [--name N] [--kind library|executable|ptx|wasm]`: create a package. - `sexc clean [--all]`: remove build output. - `sexc help [command] [--json]`, `sexc version`. - `sexc install [--frozen]`: resolve every registry dependency, write `sexc-lock.json`, fetch into the store, link `.sexc/deps`. `--frozen` fails instead of changing the lock (use it in CI). - `sexc add [@range]... [--to ]` (default range `^`), `sexc remove ... [--from ]`, `sexc update [pkg...]`, `sexc outdated`, `sexc why `. - `sexc pack [pkg] [--out DIR]`: write the .tgz exactly as publish would. `sexc publish [pkg...] [-r] [--dry-run]`: `-r` publishes every non-private workspace package in dependency order, skipping versions already published. `workspace:*` dependencies are rewritten to `^` in the published manifest. - `sexc yank @ [--reason R]`, `sexc unyank @`. - `sexc register --username U --email E --password-stdin`, `sexc login --username U --password-stdin` (or `--token T`), `sexc logout`, `sexc whoami`. - `sexc search `, `sexc info [@version]`. - `sexc token list`, `sexc token create [--name N] [--scope read|publish] --password-stdin`, `sexc token revoke `. - Registry commands take `--registry URL` (default https://sexc.dev). Environment: `SEXC_REGISTRY`, `SEXC_TOKEN` (overrides the credentials file), `SEXC_PASSWORD`, `SEXC_CONFIG_DIR` (default `~/.sexc`, holds `credentials`, mode 0600), `SEXC_STORE` (default `~/.sexc/store`), `SEXC_CURL` (the curl used for HTTPS). - Every command takes `-C ` (before or after the command). Nothing ever prompts. With `--json`, stdout holds exactly one JSON document and stderr stays empty; failures have `"ok": false` and an `errors` array, and compiler messages are parsed into `diagnostics` (file, line, column, severity, message). - Exit codes: 0 ok, 1 failed (a build, a test, a version conflict, an integrity mismatch, a version that already exists), 2 bad usage, 3 workspace or manifest problem (including a dependency that isn't installed), 4 network or registry error. ## sexc.json (a package) - `name` (required): lowercase, digits, `-`, `.`, `_`, optional `@scope/`. Example: `@you/thing`. - `version` (required): semver, like `1.2.3`. - `description`, `license`, `author`, `homepage`, `repository`, `keywords`: metadata. - `kind`: `library` (default), `executable`, `ptx` or `wasm`. A library with no sources is header-only (it lends only its `include/`). - `ptx`: GPU code. Its sources and every library it depends on are compiled by clang for `nvptx64-nvidia-cuda` (freestanding, no libc; the profile's sanitizer and `-g` flags are dropped), linked with llvm-link, and turned into one module, `.sexc/out//ptx/.ptx`. Kernels use `__attribute__((nvptx_kernel))`. `ptx_arch` picks the GPU (default `sm_86`; `$SEXC_PTX_ARCH` overrides). - `wasm`: a freestanding module, `.sexc/out//wasm/.wasm`, linked by `wasm-ld --no-entry --export-dynamic`; the package's own non-static functions are exported, its libraries' aren't. `ldflags` go to wasm-ld. - `targets` (libraries): any of `host`, `nvptx64`, `wasm32` (default all). A library is compiled for a device target only when a ptx or wasm package depends on it (into `obj//`). Dependencies must build for everything their dependent builds for. - Tests are host programs for every kind; a ptx or wasm package's tests get the module's path in `$SEXC_ARTIFACT`. Tools: clang, llvm-link, wasm-ld from PATH, `-21`..`-15`, or `/usr/lib/llvm-*/bin`; override with `$SEXC_CLANG`, `$SEXC_LLVM_LINK`, `$SEXC_WASM_LD`. - `bin`: an executable's output name (default: the name without its scope). - `main`: the source holding main() (default `src/main.c`); left out when linking tests. - `sources`: globs (default `["src/*.c"]`). `tests`: globs (default `["tests/*.c"]`). - `include`: public headers folder (default `include`, when it exists). - `dependencies`: `{"@scope/name": "^1.2.0"}` (npm-style ranges: `^`, `~`, `1.x`, `>=1 <2`, `||`, exact) or `"workspace:*"` for a package in the same workspace. A package only sees the headers of what it declares (and what those declare). - `files`: globs limiting what `sexc pack` includes (sexc.json, README, LICENSE and CHANGELOG are always in). By default every file except hidden ones. - `cflags`, `ldflags`: extra flags for this package. - `private`: true to never publish. ## sexc.json (a workspace root) - `workspaces`: globs of package folders, e.g. `["packages/*", "apps/*"]`. - `cflags`, `ldflags`: shared by every package. - `profiles`: `{"debug": {"cflags": [], "ldflags": []}, "release": {...}}`; pick one with `--profile`. Defaults: debug (-g -O0) and release (-O2 -DNDEBUG). - A folder with only a package sexc.json is a workspace of one. ## Installing and the lockfile - A build holds exactly one version of each package (C has one symbol namespace), so the resolver picks the highest version that satisfies every range in the graph, or fails naming who wants what. - `sexc-lock.json` at the workspace root records each package's exact version, integrity (`sha256:`) and dependencies. Commit it. `sexc build`, `test` and `run` install from it automatically when `.sexc/deps` is missing something. - Packages are unpacked once per machine into `~/.sexc/store/v1/sha256//` (read-only) and symlinked as `.sexc/deps/@scope+name@version`. They build with your compiler and profile into your `.sexc/out`, with `-Wno-error`; their `ldflags` are added when linking. - Put the flags your code needs (e.g. `-D_XOPEN_SOURCE=700`) in the package's own `cflags`: a workspace root's flags are not published. ## MCP - `sexc-mcp` is an MCP server on stdio. Each sexc command is a tool named `sexc_`, with an input schema generated from `sexc help --json`, and each returns the command's JSON report. - Claude Code: `claude mcp add sexc -- /path/to/sexc-mcp --dir /path/to/workspace` ## Writing tests - A test is a C program in tests/; exit 0 means pass. In this repo, include `sexc/test.h` and use `SX_CHECK`, `SX_CHECK_STR`, `SX_CHECK_INT`, ending with `return sx_test_done();`. ## The sexc repo - Every line of code in the sexc repo is C, including the website (sexc.dev is rendered by a design system written in C, with no JavaScript). See AGENTS.md. ## Registry API (https://sexc.dev/v1/) - JSON in and out. Errors are `{"ok": false, "error": "", "code": ""}` with 400, 401, 403, 404, 409, 413 or 429; successes have `"ok": true`. Full reference: https://sexc.dev/docs/api.txt - Auth: `Authorization: Bearer sexc_...` (a token). Only `POST /v1/tokens` takes a password, as `Authorization: Basic base64(username:password)`. Give agents tokens, never passwords; make them at https://sexc.dev/settings/tokens or with `sexc token create`. - `POST /v1/accounts` `{"username","email","password"}`: sign up (username `[a-z0-9-]`, password 10+ characters). - `POST /v1/tokens` `{"name","scope":"publish"|"read"}`: a token, shown once. `GET /v1/tokens`, `DELETE /v1/tokens/:id`, `GET /v1/whoami`. - `GET /v1/packages/:name`: the packument (`latest`, `owners`, `readme`, and `versions` keyed by version: `manifest`, `dependencies`, `integrity`, `size`, `tarball`, `published`, `publisher`, `yanked`). `GET /v1/packages/:name/:version`: one version. - `PUT /v1/packages/:name` `{"version","integrity":"sha256:","tarball":""}`: publish (a publish token; body up to 16 MiB). `POST /v1/packages/:name/:version/yank` `{"reason"}` and `/unyank`. - `GET /v1/tarballs/:name/:version.tgz`, `GET /v1/search?q=&limit=&offset=`, `GET /v1/users/:username`. - Names go in paths literally: `/v1/packages/@ajax/strbuf`. `@scope/` must be your username; unscoped names are first come, first served. Versions are immutable. - Tarballs: entries under `package/`, regular files only (no directories, links or `..`), at most 10 MiB compressed, 50 MiB unpacked, 10 000 files. `package/sexc.json` must match the name and version; every dependency must already be published with a version matching its range; `workspace:` ranges and `"private": true` are refused. - Every registry page has a `.json` twin with the same data: `/packages.json?q=`, `/packages/@ajax/strbuf.json`, `/packages/@ajax/strbuf/v/1.2.0.json`, `/packages/@ajax/strbuf/v/1.2.0/files.json`, `/~ajax.json`. `/llms-full.txt` is this file plus every docs page.