Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"run": "npm ci"
},
{
"run": "fjs t"
"run": "fjs test"
}
]
},
Expand Down Expand Up @@ -102,7 +102,7 @@
"run": "npm ci"
},
{
"run": "fjs t"
"run": "fjs test"
}
]
},
Expand Down Expand Up @@ -143,7 +143,7 @@
"run": "npm ci"
},
{
"run": "fjs t"
"run": "fjs test"
}
]
},
Expand Down Expand Up @@ -184,7 +184,7 @@
"run": "npm ci"
},
{
"run": "fjs t"
"run": "fjs test"
}
]
},
Expand Down Expand Up @@ -238,7 +238,7 @@
"run": "npm ci"
},
{
"run": "fjs t"
"run": "fjs test"
}
]
},
Expand Down Expand Up @@ -279,7 +279,7 @@
"run": "npm ci"
},
{
"run": "fjs t"
"run": "fjs test"
}
]
},
Expand Down Expand Up @@ -395,7 +395,7 @@
"uses": "actions/checkout@v7.0.1"
},
{
"run": "deno run -A --minimum-dependency-age=0 npm:functionalscript@0.41.0 t"
"run": "deno run -A --minimum-dependency-age=0 npm:functionalscript@0.41.0 test"
},
{
"run": "deno install --frozen"
Expand Down Expand Up @@ -424,7 +424,7 @@
"run": "bun install --frozen-lockfile"
},
{
"run": "bunx functionalscript@0.41.0 t"
"run": "bunx functionalscript@0.41.0 test"
},
{
"run": "bun test --coverage"
Expand All @@ -450,7 +450,7 @@
"run": "npm ci"
},
{
"run": "fjs t"
"run": "fjs test"
},
{
"run": "node --test"
Expand Down
34 changes: 17 additions & 17 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,20 @@ below Node `26.0.0`, so `node --test` and `npm run cov` correctly handle
Every row below runs the same suite; pick the first one that fits your
environment.

| Command | Runtime | Needs internet | Notes |
| -------------------------------------- | -------- | -------------- | ------------------------------------------ |
| `npm test` | Node 22+ | no | `tsc` + the repo's runner. |
| `npm start t` | Node 22+ | no | The repo's runner, no type-check step. |
| `node --test` | Node 22+ | no | Node's native test runner. |
| `npm run cov` | Node 22+ | no | `node --test` plus coverage. |
| `deno task fjs t` | Deno | no | The repo's runner under Deno. |
| `deno task test` / `deno task cov` | Deno | no | Deno's native test runner / coverage. |
| `bun fjs/module.ts t` | Bun | no | The repo's runner under Bun. |
| `bun test` | Bun | no | Bun's native test runner. |
| `fjs t` | Node 22+ | to install | After `npm install -g functionalscript`. |
| `npx functionalscript t` | Node 22+ | yes | No install step. |
| `deno run -A npm:functionalscript t` | Deno | yes | No install step. |
| `bunx functionalscript t` | Bun | yes | No install step. |
| Command | Runtime | Needs internet | Notes |
| --------------------------------------- | -------- | -------------- | ---------------------------------------- |
| `npm test` | Node 22+ | no | `tsc` + the repo's runner. |
| `npm start test` | Node 22+ | no | The repo's runner, no type-check step. |
| `node --test` | Node 22+ | no | Node's native test runner. |
| `npm run cov` | Node 22+ | no | `node --test` plus coverage. |
| `deno task fjs test` | Deno | no | The repo's runner under Deno. |
| `deno task test` / `deno task cov` | Deno | no | Deno's native test runner / coverage. |
| `bun fjs/module.ts test` | Bun | no | The repo's runner under Bun. |
| `bun test` | Bun | no | Bun's native test runner. |
| `fjs test` | Node 22+ | to install | After `npm install -g functionalscript`. |
| `npx functionalscript test` | Node 22+ | yes | No install step. |
| `deno run -A npm:functionalscript test` | Deno | yes | No install step. |
| `bunx functionalscript test` | Bun | yes | No install step. |

The last four rows run a **published** FunctionalScript rather than this working
tree's version. `npx`, `deno run`, and `bunx` resolve the latest release each
Expand All @@ -87,7 +87,7 @@ CI exercises these same combinations — see the `node22`, `node24`, `node26`,
and pinned runtime versions.

To run only the tests under a subtree, `cd` into that directory and run the
runner from there (e.g. `cd fjs/base64 && fjs t`). Module discovery starts at
runner from there (e.g. `cd fjs/base64 && fjs test`). Module discovery starts at
the current working directory, and results are reported per test.

### 1.5 Updating dependencies
Expand Down Expand Up @@ -124,7 +124,7 @@ cargo fmt -- --check # verify formatting
4. Run the full check set before submitting:
```bash
npx tsc # type-check with the repo's TypeScript
fjs t # or any equivalent from §1.4
fjs test # or any equivalent from §1.4
cargo test # only if you touched Rust
cargo clippy
cargo fmt -- --check
Expand All @@ -141,7 +141,7 @@ cargo fmt -- --check # verify formatting
### 3.1 Commands

- `npx tsc` — type-check using the repository's version of TypeScript.
- `fjs t` (or any equivalent from [§1.4](#14-ways-to-run-the-functionalscript-test-suite))
- `fjs test` (or any equivalent from [§1.4](#14-ways-to-run-the-functionalscript-test-suite))
— test FunctionalScript (`.f.ts` / `.f.mjs`) files.
- `cargo test`, `cargo clippy`, `cargo fmt -- --check` — the Rust crate.

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ history.

## Unreleased

- `ci`: generated workflows invoke the CLI by full command name (`fjs test`,
`functionalscript@<version> test`) instead of the `t` alias
[#1450](https://github.com/functionalscript/functionalscript/pull/1450)

## 0.43.1

- `vnd.fjs.revision` gains optional flat subject-to-content lock maps; Evo
Expand Down
74 changes: 53 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,39 +28,71 @@ Install FunctionalScript via npm:
npm install -g functionalscript
```

The `fjs` CLI provides several commands:
or run the CLI without installing it, with `npx functionalscript <command>`.

| Command | Description |
|------------------|-----------------------------------------------------------|
| `fjs test` / `t` | Run the FunctionalScript test suite |
| `fjs compile` / `c` | Compile a `.f.ts` module to JavaScript |
| `fjs run` / `r` | Run a FunctionalScript module as a Node program |
| `fjs cas` / `s` | Content-addressable storage (`add`, `get`, `list`) |
| `fjs mcp` / `m` | Run an MCP server over stdio exposing the CAS as tools |
| `fjs ci` / `i` | Generate the GitHub Actions CI workflow |
### Compiling a module

### Content-Addressable Storage (CAS)
A FunctionalScript module is already a valid JavaScript module, so nothing has to
be compiled in order to *run* it. The compiler serves the other direction: it
evaluates a module and emits the data it exports, with every `import` resolved.

FunctionalScript ships a built-in CAS for storing and retrieving blobs by their cryptographic hash:
`m.f.js`:

```bash
fjs cas add myfile.txt # store a file, print its cBase32 hash
fjs cas get <hash> # restore a blob by hash
fjs cas list # list all stored hashes
```js
export default "text"
```

Blobs are stored under `~/.cas/` and addressed by their SHA-256 hash encoded in cBase32.
`input.f.js`:

### MCP Server
```js
import c from "./m.f.js"
const a = 1
export default [a, a, c, { x: c }]
```

The CAS is also exposed as an [MCP](https://modelcontextprotocol.io/) server so LLM agents can read and write blobs without a shell:
The output file extension picks the format:

```bash
# register with Claude CLI
claude mcp add cas -- npx functionalscript m
fjs compile input.f.js output.f.js # JavaScript
fjs compile input.f.js output.json # JSON
```

`output.f.js` preserves the object graph — a value referenced more than once
stays shared and is emitted as a `const`:

```js
const c0 = "text"
export default [1,1,c0,{"x":c0}]
```

`output.json` is a tree, so shared values are expanded, and types that JSON
cannot express (`bigint`, `undefined`) are not available:

```json
[1,1,"text",{"x":"text"}]
```

See [`fjs/mcp/README.md`](fjs/mcp/README.md) for details on the `cas_add`, `cas_get`, and `cas_list` tools.
The compiler currently accepts `import` statements, `const` declarations, and
data expressions (objects, arrays, strings, numbers, `bigint`, booleans, `null`,
`undefined`). Functions and computed expressions are not supported yet. See
[fjs/djs/README.md](fjs/djs/README.md) for the data language and its roadmap, and
[fjs/fsc/README.md](fjs/fsc/README.md) for the compiler itself.

### The `fjs` CLI

| Command | Description | Documentation |
|---------------|----------------------------------------------------------------|--------------------------------------------------------|
| `fjs test` | Run the FunctionalScript test suite | [fjs/emergent_testing](fjs/emergent_testing/README.md) |
| `fjs compile` | Compile a FunctionalScript module to JavaScript or JSON | [fjs/djs](fjs/djs/README.md) |
| `fjs run` | Run a FunctionalScript module as a Node program | [fjs/README.md](fjs/README.md) |
| `fjs cas` | Content-addressable storage (`add`, `get`, `list`) | [fjs/cas/README.md](fjs/cas/README.md) |
| `fjs mcp` | [MCP](https://modelcontextprotocol.io/) server over stdio, exposing the CAS and Evo as tools | [fjs/mcp/README.md](fjs/mcp/README.md) |
| `fjs ci` | Generate the GitHub Actions CI workflow | [fjs/ci/README.md](fjs/ci/README.md) |

Run `fjs help` to print the available commands, or see
[fjs/README.md](fjs/README.md) for the full CLI reference. Commands also accept
short aliases, which `fjs help` prints; the documentation spells them out
instead.

## Vision

Expand Down
47 changes: 31 additions & 16 deletions fjs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ node ./fjs/module.ts <command> [args]
or via `npx` without a global install:

```sh
npx fjs <command> [args]
npx functionalscript <command> [args]
```

`npx` resolves the **package** name, not the bin name: `fjs` is an unrelated
package on npm, so `npx fjs …` would run somebody else's code.

or, once the package is installed globally:

```sh
Expand All @@ -20,37 +23,49 @@ fjs <command> [args]

## Commands

| Command | Alias | Description |
|---------|-------|-------------|
| `test` | `t` | Run the FunctionalScript test suite |
| `compile`| `c` | Compile a FunctionalScript module to JavaScript |
| `cas` | `s` | Content-addressable storage operations |
| `mcp` | `m` | Run an MCP server over stdio exposing the CAS as tools |
| `ci` | `i` | Generate the GitHub Actions CI workflow |
| `run` | `r` | Run a FunctionalScript module as a program |
| `help` | `h`, `?` | Print available commands |
| Command | Description | Documentation |
|---------|-------------|---------------|
| `test` | Run the FunctionalScript test suite | [emergent_testing](emergent_testing/README.md) |
| `compile`| Compile a FunctionalScript module to JavaScript or JSON | [djs](djs/README.md), [fsc](fsc/README.md) |
| `cas` | Content-addressable storage operations (`add`, `get`, `list`) | [cas](cas/README.md) |
| `mcp` | Run an MCP server over stdio exposing the CAS and Evo as tools | [mcp](mcp/README.md) |
| `ci` | Generate the GitHub Actions CI workflow | [ci](ci/README.md) |
| `run` | Run a FunctionalScript module as a program | [below](#fjs-run--running-a-module-as-a-program) |
| `help` | Print available commands | |

Each command also accepts a short alias (`fjs t` for `fjs test`, and so on).
`fjs help` prints them; this documentation uses the full names throughout.

## `fjs compile` — compiling a module

```sh
fjs compile <input> <output>
```

The output extension picks the format: `.json` emits a tree (shared values are
expanded), anything else emits a JavaScript module that preserves sharing by
naming reused values as `const`s. Imports are resolved and inlined in both
cases. See [djs/README.md](djs/README.md) for the accepted subset.

## `fjs ci` — generating the standard CI workflow

```sh
fjs ci
fjs i
```

`fjs ci` runs the built-in CI generator from `fjs/ci/module.f.ts`, writing
`.github/workflows/ci.yml`. It is the standard entry point for projects that want
FunctionalScript's default workflow. Projects with custom CI setup code should keep
using `fjs r <custom-ci-module>`, so their module can call `ci(setup)` with its own
using `fjs run <custom-ci-module>`, so their module can call `ci(setup)` with its own
extra runtime steps.

## `fjs run` — running a module as a program

```sh
fjs run <module> [args...]
fjs r <module> [args...]
```

`fjs r` dynamically imports `<module>` and calls its `main` export as a
`fjs run` dynamically imports `<module>` and calls its `main` export as a
`NodeProgram`:

```ts
Expand All @@ -59,7 +74,7 @@ fjs r <module> [args...]

### Convention: `export const main`

A module intended to be run with `fjs r` must export a named `main` constant
A module intended to be run with `fjs run` must export a named `main` constant
of type `NodeProgram`:

```ts
Expand All @@ -82,7 +97,7 @@ This mirrors:
Any arguments after `<module>` are forwarded to `main` via `options.args`:

```sh
fjs r ./my-tool.f.ts foo bar # options.args === ['foo', 'bar']
fjs run ./my-tool.f.ts foo bar # options.args === ['foo', 'bar']
```


Expand Down
2 changes: 1 addition & 1 deletion fjs/cas/evo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ any other reason, a blob that is not a revision — comes back as
- [`fjs/mcp/evo`](../../mcp/evo/) — MCP tool definitions (`evo_list` / `evo_head` /
`evo_revision` / `evo_add`) for agents, served by the same process as
[`fjs/mcp`](../../mcp/)'s `cas_add`/`cas_get`/`cas_list` — one
`~/.cas/` store, one Evo cache, one server (`npx functionalscript m`).
`~/.cas/` store, one Evo cache, one server (`npx functionalscript mcp`).

## In-memory cache is per process

Expand Down
10 changes: 5 additions & 5 deletions fjs/ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ Windows and macOS jobs install.
### Expected package scripts

The generated platform jobs run `npm ci`, install the pinned FunctionalScript
package globally, and run `fjs t`. Canonical Node jobs run on Ubuntu ARM and are
package globally, and run `fjs test`. Canonical Node jobs run on Ubuntu ARM and are
split by Node version:

- Node 22 runs `npm ci`, installs the pinned FunctionalScript package globally,
and runs `fjs t`.
and runs `fjs test`.
- Node 24 runs `npm ci` and `node --test`.
- Node 26 runs `npm ci`, `npm run ci-update`, `git add -A && git diff --cached --exit-code`,
`npx tsc`, `npm run cov`, and `npm pack`.
Expand All @@ -87,7 +87,7 @@ and `ci-update`. A typical FunctionalScript project can define them like this:
```json
{
"scripts": {
"test": "tsc && fjs t",
"test": "tsc && fjs test",
"cov": "node --test --experimental-test-coverage --test-coverage-include=**/module.f.ts",
"ci-update": "fjs ci"
}
Expand Down Expand Up @@ -119,13 +119,13 @@ import 'functionalscript/fjs/emergent_testing/all.test.js'
```

Without that file, third-party test runners discover no FunctionalScript proofs
and will report zero tests. `fjs t` is the exception: it discovers proof modules
and will report zero tests. `fjs test` is the exception: it discovers proof modules
directly and does not need `all.test.ts`.

**Note,** `npm run ci-update` in this repository runs the same built-in command through the
checked-in Node entry point, which avoids relying on the package bin before the
package has been installed. Custom projects that need different runtime setup steps
should use `fjs r <custom-ci-module>` and call `ci(setup)` directly instead of
should use `fjs run <custom-ci-module>` and call `ci(setup)` directly instead of
modifying the built-in command.

The built-in command does not read `package.json` to customize generated steps.
Expand Down
2 changes: 1 addition & 1 deletion fjs/ci/bun/module.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export const bunSteps = (version: string): readonly MetaStep[] => [
install(uses('oven-sh/setup-bun', { 'bun-version': bun })),
install({ run: `bun install -g functionalscript@${version}` }),
test({ run: 'bun install --frozen-lockfile' }),
test({ run: `bunx functionalscript@${version} t` }),
test({ run: `bunx functionalscript@${version} test` }),
test({ run: 'bun test --coverage' }),
]
Loading
Loading