@anna-ai/cli
v0.1.12
Published
Anna App developer CLI: scaffold, validate, harness (Phase 2 MVP: init + validate).
Readme
@anna-ai/cli
Anna App developer CLI — scaffold, validate, run a local dev harness, drive
fixture-based regression tests, and program against the harness from
vitest/pytest. Phases 2–9 (MVP) are done.
Install
End-user install (after first npm publish):
npm i -g @anna-ai/cli
anna-app --helpThe dev and fixture replay commands transparently fetch the pinned Python
runtime via uvx (no matrix-nexus checkout required). Install
uv once:
curl -LsSf https://astral.sh/uv/install.sh | shLocal dev (this repo):
pnpm install
pnpm build
node dist/cli.js --helpCommands
anna-app init <dir> [--slug <slug>] [--template minimal] [--force]
Scaffolds a new Anna App project (manifest + bundle + a stdio plugin
template). __TOOL_ID__ becomes tool-dev-<slug>.
anna-app init my-app --slug my-app
cd my-app
anna-app validateanna-app validate [--manifest manifest.json] [--bundle bundle] [--strict]
Layered fail-fast checks: JSON Schema → ui static → cross-file tool_id
linter (with Levenshtein-1 typo detection) → --strict host_api ACL grep
of bundle JS/TS.
anna-app dev [--manifest …] [--bundle …] [--port 5180] [--matrix-nexus-root <path>] [--executa <spec>…]
Boots the local harness:
- Spawns the Python
anna-app-bridge(production dispatcher reused viaWindowStoreProtocol). - Serves a mock dashboard at
http://localhost:<port>/. - Auto-discovers
<manifest-dir>/executas/<name>/plugins (Python / Node.js / Go / pre-built binary) and registers them in the in-processExecutaPool. See "Multi-language executas" below. - Hot-reloads the bundle on disk changes (use
--no-watchto disable).
Multi-language executas
Each subdirectory of <manifest-dir>/executas/ is launched according to
the first sentinel that matches:
| # | Sentinel | Type | Default launch |
| - | ----------------------- | -------- | ------------------------------------------------------------- |
| 0 | executa.json | (any) | command field, else type-specific default below |
| 1 | pyproject.toml | python | uv run --project <dir> <tool_id> |
| 2 | package.json | node | node <bin[tool_id] \| bin \| main \| module> |
| 3 | go.mod (alone) | go | requires executa.json declaring type: "go" |
| 4 | bin/<dirname> exec | binary | runs the executable directly |
executa.json (recommended for clarity, required for Go and pre-built
binary tools):
{
"tool_id": "tool-yourhandle-foo-abcd1234",
"type": "python" | "node" | "go" | "binary",
"command": ["…"], // optional; full override of type defaults
"enabled": true // optional; default true. Set false to skip
// (useful when shipping multiple language
// flavours of the same tool_id).
}The --executa <spec> flag (repeatable) registers an out-of-tree
executa, or fully overrides auto-discovery for the run. Spec syntax:
# Auto-detect from the dir (same rules as in-tree discovery):
anna-app dev --executa dir=./vendor/external-tool
# Force type when there's no executa.json:
anna-app dev --executa dir=./executas/foo,type=go
# Fully explicit:
anna-app dev --executa dir=./executas/foo,tool_id=tool-h-foo-12345678,command="node plugin.js"For the full discovery / executa.json reference, see
anna-executa-examples/docs/multi-language-anna-apps.md.
Two runtime modes (auto-selected):
| Mode | When | Command |
| --- | --- | --- |
| uvx (default) | No matrix-nexus checkout nearby | uvx --from anna-app-runtime-local@<pinned> anna-app-bridge |
| nexus-source | --matrix-nexus-root set, $ANNA_NEXUS_ROOT set, or running from inside a checkout | uv run --project <root> python -m anna_app_runtime_local.bridge |
The pinned wheel version is exported as PINNED_RUNTIME_VERSION from
src/harness/bridge.ts — bump in lock-step with
packages/VERSIONS.md in matrix-nexus.
anna-app doctor [--matrix-nexus-root <path>]
Environment sanity check: uv on PATH, uvx cache state for the pinned
runtime, optional matrix-nexus checkout, dev signing key permissions.
Exit 0 if all required checks pass.
anna-app fixture verify <file.jsonl>
Strict syntactic + semantic validation of a recorded fixture (envelope ordering, response⇄request pairing, host_api allowlist).
anna-app fixture summarize <file.jsonl>
Pretty stats: counts per (ns, method), error breakdown, event totals.
anna-app fixture replay <file.jsonl> [--manifest …]
Dry-run replay (MVP): re-executes the recorded request sequence against
a fresh LocalDispatcherSession and diffs each response. Live executa
re-invocation is deferred to a future minor.
Programmatic harness (vitest)
import { mountBundle } from "@anna-ai/cli/test";
const h = await mountBundle({
manifest: "./examples/hello/manifest.json",
bundle: "./examples/hello/bundle",
});
await h.call("storage", "set", { key: "k", value: 1 });
const events = h.drainEvents();
await h.close();See src/test/index.ts for the full surface
(call, expectAcl, drainEvents, dispose).
Pytest plugin (anna-executa-test)
For executa authors. Lives in matrix-nexus at
packages/anna-executa-test/. Provides executa_session /
executa_invoke fixtures — see that package's README.
Schema bundle
@anna-ai/app-schema
is a regular npm dependency. Set ANNA_APP_SCHEMA_DIR to point at a
local checkout (e.g. matrix-nexus/packages/anna-app-schema/) to
iterate on the protocol without publishing.
Browser SDK
End-user app bundles load AnnaAppRuntime from
/static/anna-apps/_sdk/<version>/index.js. Both the matrix-nexus
production server and this CLI's harness serve that URL from the
public npm package @anna-ai/app-runtime,
which is declared as a normal dependency. No vendored copy, no sync
step.
Persistent storage (APS)
Anna 1.2+ exposes a per-user JSON-RPC storage surface under the
storage/* namespace. Plugins authored with this CLI can opt in by:
- Declaring
storage.user(or.app/.tool) in the manifest'shost_capabilitiesarray. - Negotiating
client_capabilities.storage = {}duringinitialize. - Asking the user to grant storage in the Anna admin panel.
See the protocol & best-practice guide at
anna-executa-examples/docs/persistent-storage.md
for wire format, error codes, and a worked OCR-cache example. The
local dev harness mocks APS by default so end-to-end tests do not
need network access.
Roadmap
| Phase | Status | Scope |
| --- | --- | --- |
| 2 — init + validate | ✅ MVP | Schema/ACL checks |
| 3 — dev harness | ✅ MVP | Stdio bridge, dashboard, hot-reload |
| 4 — @anna-ai/cli/test | ✅ MVP | Programmatic mountBundle |
| 5 — anna-executa-test (pytest) | ✅ MVP | Plugin shipped from matrix-nexus |
| 6 — fixture {verify,summarize,replay} | ✅ MVP (dry-run replay) | Live re-invoke deferred |
| 7 — Publish & distribution | ✅ done | uvx default + full [email protected] extraction; end-users no longer need a matrix-nexus checkout |
| 8 — manifest.dev block + init template + 4 docs | ✅ done | See matrix-nexus/docs/developers/apps/: local-dev.md, testing-bundle.md, testing-plugin.md, recording-replay.md |
| 9 — Reference example upgraded | ✅ done | anna-executa-examples/examples/anna-app-focus-flow ships tests/{bundle,plugin}/, fixtures, and .github/workflows/anna-app.yml |
Pinned versions
PINNED_RUNTIME_VERSION = "0.2.0a1"(src/harness/bridge.ts)- See
matrix-nexus/packages/VERSIONS.mdfor the full toolchain matrix.
Tests
pnpm test