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
87 changes: 87 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Contributing to the Akua CLI

This file covers building, testing, and releasing this repository's source.
If you only want to use the `akua` executable, see [README.md](README.md)
instead.

## Prerequisites

[mise](https://mise.jdx.dev/) manages the pinned Bun toolchain:

```sh
mise install
bun install --frozen-lockfile
mise run check
mise run build:binary
./dist/akua --version
./dist/akua --help
./dist/akua commands --limit 1
```

`mise run check` runs the drift check, typecheck/build, and tests — the same
gate CI runs. Run it before opening a PR.

## Command generation

The command surface is generated from the public source of truth,
`https://api.akua.dev/v1/openapi.json`:

```sh
mise run spec:fetch # fetch and stably format openapi/public.json
mise run generate # regenerate the command registry and typed API bindings
mise run generate:check # fail if committed generated output has drifted
```

Generation is deterministic and operationId-driven; only operations marked
`x-platform-visibility: PUBLIC` are included, and registry rows are sorted by
operationId. The generated outputs are `src/generated/commands.gen.ts`,
`src/generated/openapi-api.gen.ts`, and
`src/generated/public-operation-executor.gen.ts`. Never hand-edit generated
files; run `mise run generate` and commit the result.

See [docs/architecture.md](docs/architecture.md) for the full command
derivation rules, the API/auth/config model, output modes, exit codes, and
the rest of the CLI's design contract.

## Testing

```sh
bun test
```

`mise run check` (drift check, build, tests) is the required gate before
release changes; see [docs/architecture.md](docs/architecture.md#testing-strategy)
for what current test coverage includes.

## Release process

`mise run release:package` cross-compiles all five targets, creates archives
and checksums in `dist/release`, and verifies their manifest.
`mise run release:verify` re-verifies an already-packaged release directory.
`mise run release:smoke` extracts and runs the artifact for the current
supported host. CI repeats native smoke tests on every platform in the
release matrix (macOS arm64/x64, glibc Linux arm64/x64, Windows x64).

Release Please creates the version tag and GitHub Release. Its own workflow
then calls artifact publication directly, so publication does not depend on a
tag event that GitHub may suppress for job-token-created tags. Uploads do not
clobber existing assets. Only after downloading and re-verifying the published
assets does the workflow dispatch the Homebrew manifest URL. The
`HOMEBREW_TAP_TOKEN` secret must be a fine-grained credential scoped only to
the tap repository's dispatch permission; failures remain visible as release
job failures.

`akua-dev/homebrew-tap` owns the `akua` formula, formula tests, and the
reviewed formula-update PR. This repository requests a formula PR only after
every archive has passed a native install smoke test and all published assets
have passed post-upload verification; it never pushes formula commits itself.

`scripts/release.ts` is the source of truth for target IDs, Bun targets,
archive names, executable names, SHA-256 files, and release manifests.

## Repository-specific engineering rules

See [AGENTS.md](AGENTS.md) for durable, repository-wide engineering rules
(Effect v4 production code conventions, the release contract, ownership
boundaries, and the public API command contract) that apply to any change in
`src/` or `scripts/`.
209 changes: 44 additions & 165 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
# Akua CLI

`akua` is the public Akua Cloud command-line interface. It is a self-contained
Bun/TypeScript executable for humans, automation, and coding agents. The current
MVP implements local token authentication, browser/device login, adaptive
structured output, and executable public operationId-driven commands. Effect
CLI renders the interactive command tree and validates its documented flags;
the generated typed Effect API and its static executor are derived from OpenAPI
for every public operation.
`akua` drives Akua Cloud from a terminal: create Kubernetes clusters, add
machines, package an application, and install it, all from one command. It is
a single self-contained executable, built for three audiences — a person
typing commands interactively, a CI pipeline calling it non-interactively, and
a coding agent driving it programmatically — and every command adapts its
output to whichever one is running it.

Every command is generated directly from Akua's public API, so the CLI never
drifts out of sync with what the platform can actually do.

The canonical executable is `akua`; there is no `cnap` compatibility binary.

## Install

GitHub Releases and Homebrew are the supported install channels. Every GitHub
archive contains the `akua` executable and its adjacent target-native package
runtime, and has an adjacent SHA-256 file. A release also publishes
`checksums.txt`, a complete release manifest, and the exact Homebrew
artifact/checksum mapping.

### Homebrew

```sh
brew install akua-dev/tap/akua
akua --version
Expand All @@ -35,115 +29,21 @@ brew update
brew upgrade akua
```

The formula is maintained in `akua-dev/homebrew-tap`. A CLI release requests a
reviewed formula PR only after every archive has passed a native install smoke
test and all published assets have passed post-upload verification.
The formula is maintained in `akua-dev/homebrew-tap`.

### GitHub Release: macOS or Linux
No Homebrew? See [manual install](docs/install.md) for checksummed release
archives.

This copy-paste example installs v0.9.0 into `~/.local/bin`. Change `VERSION`
when selecting a newer release.

```sh
set -eu
VERSION=0.9.0
case "$(uname -s)-$(uname -m)" in
Darwin-arm64) TARGET=darwin-arm64 ;;
Darwin-x86_64) TARGET=darwin-x64 ;;
Linux-arm64|Linux-aarch64) TARGET=linux-arm64 ;;
Linux-x86_64) TARGET=linux-x64 ;;
*) echo "Unsupported platform: $(uname -s)-$(uname -m)" >&2; exit 1 ;;
esac
ASSET="akua-v${VERSION}-${TARGET}.tar.gz"
BASE="https://github.com/akua-dev/cli/releases/download/v${VERSION}"
curl --fail --location --remote-name "${BASE}/${ASSET}"
curl --fail --location --remote-name "${BASE}/${ASSET}.sha256"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum --check "${ASSET}.sha256"
else
shasum -a 256 --check "${ASSET}.sha256"
fi
INSTALL_ROOT="$HOME/.local/libexec/akua-v${VERSION}"
mkdir -p "$INSTALL_ROOT" "$HOME/.local/bin"
tar -xzf "$ASSET" -C "$INSTALL_ROOT"
ln -sfn "$INSTALL_ROOT/akua" "$HOME/.local/bin/akua"
"$HOME/.local/bin/akua" --version
"$HOME/.local/bin/akua" --help
"$HOME/.local/bin/akua" commands --limit 1
"$HOME/.local/bin/akua" pkg version
```

Ensure `~/.local/bin` is on `PATH`. Manual upgrades repeat these steps with a
newer `VERSION`, replacing `~/.local/bin/akua`. The CLI does not self-update.

### GitHub Release: Windows x64

Run in PowerShell. This installs v0.9.0 into `%USERPROFILE%\bin`; add that
directory to the user `PATH` if it is not already present.

```powershell
$ErrorActionPreference = "Stop"
$Version = "0.9.0"
$Asset = "akua-v$Version-windows-x64.zip"
$Base = "https://github.com/akua-dev/cli/releases/download/v$Version"
Invoke-WebRequest "$Base/$Asset" -OutFile $Asset
Invoke-WebRequest "$Base/$Asset.sha256" -OutFile "$Asset.sha256"
$Expected = ((Get-Content "$Asset.sha256") -split "\s+")[0].ToLower()
$Actual = (Get-FileHash $Asset -Algorithm SHA256).Hash.ToLower()
if ($Actual -ne $Expected) { throw "SHA-256 mismatch for $Asset" }
Expand-Archive $Asset -DestinationPath .\akua-release -Force
$InstallRoot = "$HOME\libexec\akua-v$Version"
New-Item -ItemType Directory -Force "$InstallRoot" | Out-Null
Copy-Item .\akua-release\* "$InstallRoot" -Recurse -Force
New-Item -ItemType Directory -Force "$HOME\bin" | Out-Null
Copy-Item "$InstallRoot\akua.exe" "$HOME\bin\akua.exe" -Force
Copy-Item "$InstallRoot\node_modules" "$HOME\bin\node_modules" -Recurse -Force
& "$HOME\bin\akua.exe" --version
& "$HOME\bin\akua.exe" --help
& "$HOME\bin\akua.exe" commands --limit 1
& "$HOME\bin\akua.exe" pkg version
```

### Supported release artifacts

| Platform | Architecture | Asset | Runtime baseline |
| --- | --- | --- | --- |
| macOS | Apple Silicon arm64 | `akua-v0.9.0-darwin-arm64.tar.gz` | Bun darwin arm64 |
| macOS | Intel x64 | `akua-v0.9.0-darwin-x64.tar.gz` | Bun darwin x64 |
| Linux | glibc arm64 | `akua-v0.9.0-linux-arm64.tar.gz` | Bun linux arm64 |
| Linux | glibc x64 | `akua-v0.9.0-linux-x64.tar.gz` | Bun linux x64 baseline |
| Windows | x64 | `akua-v0.9.0-windows-x64.zip` | Bun windows x64 baseline |

Linux musl, Windows arm64, and other systems are not in the tested release
contract. x64 Linux and Windows use Bun's baseline target for older CPUs. Unix
archives preserve executable mode `0755`; the Windows ZIP contains `akua.exe`.
All archives also contain the target-native package runtime under
`node_modules/@akua-dev`. Keep that directory adjacent to the executable. Bun is
not required on the target machine.

To audit a whole release, download `checksums.txt` plus the archives and run
`sha256sum --check checksums.txt` on Linux or `shasum -a 256 --check
checksums.txt` on macOS. The adjacent `<asset>.sha256` files support single-asset
verification. Release assets are never replaced in place; a changed binary
requires a new version.

## First use and authentication

Inspect the installed surface first:
## First commands

```sh
akua # complete interactive command tree
akua auth --help # authentication subcommands and options
akua workspaces --help # generated resource commands
akua commands --limit 5
akua commands --limit 5 # discover the full command surface
```

For CI and coding agents, prefer an ephemeral environment credential:

```sh
export AKUA_API_TOKEN='sk_akua_...'
akua auth status
```
## Sign in

For an interactive browser/device login:

Expand All @@ -159,6 +59,14 @@ the verification in any browser instead.
akua auth login --no-browser
```

For CI and coding agents, prefer an ephemeral environment credential instead of
an interactive login:

```sh
export AKUA_API_TOKEN='sk_akua_...'
akua auth status
```

For a local persisted token without an interactive login:

```sh
Expand All @@ -173,10 +81,11 @@ akua auth logout
removes only the stored `token`, also preserving unknown config keys, and cannot
clear `AKUA_API_TOKEN` from the parent process.

## Human and agent output
## Built for humans, CI, and agents

An interactive TTY defaults to human prose. The CLI defaults to compact agent
output when any of these signals are active:
An interactive TTY defaults to human prose. The CLI switches to compact agent
output automatically when any of these signals are active, so an agent gets
usable output without extra flags:

- `AGENT=true` or `AGENT=<name>` (for example `AGENT=codex`);
- a detected provider environment such as Codex, Claude Code, Cursor, Aider,
Expand All @@ -200,65 +109,35 @@ The supported modes are `human`, `agent`, `json`, and `quiet`. Success data is
written to stdout; progress and warnings belong on stderr. Unknown commands,
flags, and output modes fail loudly with stable nonzero exit codes.

## OpenAPI command generation
## Discover and run commands

The public source of truth is
`https://api.akua.dev/v1/openapi.json`. The workflow is deliberately explicit:
Every public Akua operation is available as a generated command, kept current
with the API automatically. Discover the current surface instead of relying on
a fixed list:

```sh
mise run spec:fetch # fetch and stably format openapi/public.json
mise run generate # derive command registry and typed Effect API
mise run generate:check # fail if committed generated output has drifted
mise run check # drift check, typecheck/build, and tests
akua commands --json
akua commands --resource workspaces
akua commands --operation-id workspaces.list
```

Generation is deterministic and operationId-driven. Only operations marked
`x-platform-visibility: PUBLIC` are included. For example,
`operationId: workspaces.list` becomes `akua workspaces list`; registry rows are
sorted by operationId. The generated outputs are
`src/generated/commands.gen.ts`, `src/generated/openapi-api.gen.ts`, and
`src/generated/public-operation-executor.gen.ts`.

Generated API commands execute through the typed Effect client and accept one
JSON object from stdin or a named file. Its only keys are `path`, `query`,
`headers`, and `body`:
For example, `operationId: workspaces.list` becomes `akua workspaces list`.
Generated commands accept one JSON object from stdin or a named file. Its only
keys are `path`, `query`, `headers`, and `body`:

```sh
printf '{"query":{"limit":5}}' | akua workspaces list --input -
akua machines create --input - < ./machine.json
```

Request input is schema-validated before transport and never included in
diagnostics. The CLI remains provider-neutral: it has no provider-specific commands,
Request input is schema-validated before it is sent and never included in
diagnostics. The CLI stays provider-neutral: it has no provider-specific commands,
flags, environment variables, or credential loaders. Provider-specific values
belong only in the generated public API request body.
belong only in the generated request body.

## Development and release validation
## Learn more

Prerequisites are [mise](https://mise.jdx.dev/) and the pinned Bun toolchain:

```sh
mise install
bun install --frozen-lockfile
mise run check
mise run build:binary
./dist/akua --version
./dist/akua --help
./dist/akua commands --limit 1
```
The full command reference and platform guides live at
[docs.akua.dev](https://docs.akua.dev).

`mise run release:package` cross-compiles all five targets, creates archives and
checksums in `dist/release`, and verifies their manifest. `mise run
release:smoke` extracts and runs the artifact for the current supported host.
CI repeats native smoke tests on every platform in the table.

Release Please creates the version tag and GitHub Release. Its own workflow then
calls artifact publication directly, so publication does not depend on a tag
event that GitHub may suppress for job-token-created tags. Uploads do not
clobber existing assets. Only after downloading and re-verifying the published
assets does the workflow dispatch the Homebrew manifest URL. The
`HOMEBREW_TAP_TOKEN` secret must be a fine-grained credential scoped only to the
tap repository's dispatch permission; failures remain visible as release job
failures.

See [docs/architecture.md](docs/architecture.md) for the broader CLI contract.
Contributing to this repo? See [CONTRIBUTING.md](CONTRIBUTING.md).
Loading
Loading