diff --git a/.agents/skills/developing-vm-cli/SKILL.md b/.agents/skills/developing-vm-cli/SKILL.md new file mode 100644 index 0000000..562b4fe --- /dev/null +++ b/.agents/skills/developing-vm-cli/SKILL.md @@ -0,0 +1,49 @@ +--- +name: developing-vm-cli +description: "Develop the guest CLI (claw) that runs inside the VM. Use when working on vm-cli commands, the JSON output protocol, doctor checks, CapabilityContext implementation, or the capability registry." +--- + +# Developing the Guest CLI (claw) + +`claw` is a compiled TypeScript binary deployed into the VM at +`/usr/local/bin/claw`. The host CLI invokes it via `driver.exec()` and +gets structured JSON back. + +## Package structure + +- `bin/claw.ts` — entry point (commander dispatch) +- `src/exec.ts` — execa wrapper +- `src/output.ts` — JSON envelope helpers (`log`, `ok`, `fail`) +- `src/capabilities/registry.ts` — static capability registry + + dependency resolution +- `src/capabilities/context.ts` — `CapabilityContext` implementation + (wires to vm-cli tool modules) +- `src/commands/provision/` — provision subcommands (delegates to + capability runner) +- `src/commands/doctor.ts` — health checks with lifecycle-based warnings +- `src/tools/` — system primitives backing `CapabilityContext` + +## JSON output protocol + +Every command outputs a JSON envelope to stdout: +`{ status, data, errors }`. Progress messages go to stderr. The `--json` +flag enables JSON mode; without it, commands print human-readable output. + +## Doctor checks + +Each check declares `availableAfter` — the lifecycle phase after which +it should pass. The `--after` flag tells doctor which phase has been +reached. Checks whose phase hasn't been reached are warnings; all others +are errors. + +## Build + +```bash +bun run build:claw # -> dist/claw (linux-arm64 binary) +``` + +## Full reference + +See [references/vm-cli.md](references/vm-cli.md) for the complete +documentation including the doctor checks table, build/deployment +details, and system primitives. diff --git a/.agents/skills/developing-vm-cli/references/vm-cli.md b/.agents/skills/developing-vm-cli/references/vm-cli.md new file mode 120000 index 0000000..4bc8f1e --- /dev/null +++ b/.agents/skills/developing-vm-cli/references/vm-cli.md @@ -0,0 +1 @@ +../../../docs/vm-cli.md \ No newline at end of file diff --git a/.agents/skills/extending-capabilities/SKILL.md b/.agents/skills/extending-capabilities/SKILL.md new file mode 100644 index 0000000..6260d43 --- /dev/null +++ b/.agents/skills/extending-capabilities/SKILL.md @@ -0,0 +1,44 @@ +--- +name: extending-capabilities +description: "Write and extend VM provisioning capabilities for clawctl. Use when adding new tools to the VM, modifying the provisioning pipeline, writing CapabilityDef modules, or working with lifecycle hooks and CapabilityContext SDK." +--- + +# Extending Capabilities + +Capabilities are the extension mechanism for VM provisioning. Each +capability is a `CapabilityDef` module that declares what it installs, +when it runs (lifecycle phase + hook timing), and what health checks it +provides. + +## Key concepts + +- **CapabilityDef** — a declarative constant with `name`, `version`, + `hooks` (keyed by lifecycle phase), optional `dependsOn`, and optional + `migrations`. +- **Lifecycle phases** — `provision-system`, `provision-tools`, + `provision-openclaw`, `provision-workspace`, `bootstrap`. Each phase + supports `pre:`, main, and `post:` timing slots. +- **CapabilityContext SDK** — sandboxed interface (`ctx.exec()`, + `ctx.fs`, `ctx.apt`, `ctx.systemd`, `ctx.net`, `ctx.profile`, + `ctx.agentsMd`) that capabilities use for system access. Never import + vm-cli internals directly. +- **Steps** return `ProvisionResult` (`installed` / `unchanged` / + `failed`) and must be idempotent. +- **Doctor checks** are declared via `doctorChecks` on hooks, using + `availableAfter` for lifecycle-aware warnings. +- **State tracking** — `capability-state.json` records installed + versions; version changes trigger migrations or re-provision. + +## Adding a new capability + +1. Create a module in `packages/capabilities/src/capabilities/` +2. Export a `CapabilityDef` constant +3. Export it from `packages/capabilities/src/index.ts` +4. Register it in `packages/vm-cli/src/capabilities/registry.ts` + +## Full reference + +See [references/capabilities.md](references/capabilities.md) for the +complete documentation including hook timing, runner mechanics, state +tracking, migrations, core vs optional capabilities, and AGENTS.md +managed sections. diff --git a/.agents/skills/extending-capabilities/references/capabilities.md b/.agents/skills/extending-capabilities/references/capabilities.md new file mode 120000 index 0000000..904084a --- /dev/null +++ b/.agents/skills/extending-capabilities/references/capabilities.md @@ -0,0 +1 @@ +../../../docs/capabilities.md \ No newline at end of file diff --git a/.agents/skills/managing-provisioning/SKILL.md b/.agents/skills/managing-provisioning/SKILL.md new file mode 100644 index 0000000..4139b14 --- /dev/null +++ b/.agents/skills/managing-provisioning/SKILL.md @@ -0,0 +1,40 @@ +--- +name: managing-provisioning +description: "Understand and modify the VM provisioning workflow. Use when working on the host-side provisioning sequence, understanding what each phase installs, debugging provisioning failures, or modifying the idempotency/verification flow." +--- + +# Managing Provisioning + +Provisioning installs all system packages, user tools, and OpenClaw +inside the VM. It's driven by the `claw` binary and orchestrated by the +host via `host-core/src/provision.ts`. + +## Provisioning sequence + +1. Create project directory + git repo on the host +2. Create/start the Lima VM +3. Deploy `claw` binary into the VM +4. `sudo claw provision system` — APT packages, Node.js, systemd linger, Tailscale +5. `claw provision tools` — Homebrew, 1Password CLI, shell profile +6. `claw provision openclaw` — OpenClaw CLI, env vars, gateway stub +7. `claw provision workspace` — skills, workspace files +8. `claw doctor` — verify provisioning +9. `openclaw onboard` — interactive or headless +10. `claw provision bootstrap` — post-onboard hooks (AGENTS.md sections) + +## Key properties + +- **Idempotent** — each capability step checks current state before + acting; re-running is a fast no-op +- **Capability-driven** — provisioning logic lives in `CapabilityDef` + modules, not in the host or provision commands +- **Verifiable** — `claw doctor` checks health with lifecycle-aware + warnings (`availableAfter`) +- **State-tracked** — `data/capability-state.json` records installed + versions; supports migrations + +## Full reference + +See [references/vm-provisioning.md](references/vm-provisioning.md) for +the complete documentation including what each phase installs, +verification details, re-running provisioning, and troubleshooting. diff --git a/.agents/skills/managing-provisioning/references/vm-provisioning.md b/.agents/skills/managing-provisioning/references/vm-provisioning.md new file mode 120000 index 0000000..6e99d72 --- /dev/null +++ b/.agents/skills/managing-provisioning/references/vm-provisioning.md @@ -0,0 +1 @@ +../../../docs/vm-provisioning.md \ No newline at end of file diff --git a/.agents/skills/understanding-architecture/SKILL.md b/.agents/skills/understanding-architecture/SKILL.md new file mode 100644 index 0000000..e939d5b --- /dev/null +++ b/.agents/skills/understanding-architecture/SKILL.md @@ -0,0 +1,45 @@ +--- +name: understanding-architecture +description: "Understand clawctl's architecture and design decisions. Use when exploring the codebase structure, understanding package responsibilities, learning about the VM backend, mount system, onboarding flow, or making cross-cutting changes." +--- + +# Understanding the Architecture + +`clawctl` is a CLI for creating and managing OpenClaw gateways on macOS. +Each instance is a Lima VM provisioned and managed from the host. + +## Package responsibilities + +| Package | Role | +| -------------- | --------------------------------------------------- | +| `types` | Shared types, schemas, constants | +| `templates` | Lima config generators (lima-yaml.ts) | +| `capabilities` | Capability definitions + runner + state tracking | +| `host-core` | Host-side library (drivers, provisioning, registry) | +| `cli` | Host CLI (Ink wizard + commands) | +| `vm-cli` | Guest CLI (claw) — runs inside the VM | + +## Key design decisions + +- **Internal CLI (`claw`)** — compiled TypeScript binary in the VM; + same language on both sides; structured JSON output +- **Capabilities** — self-contained provisioning modules with lifecycle + hooks, dependency resolution, state tracking, and migrations +- **vz backend** — Apple Virtualization.framework for near-native + performance on Apple Silicon +- **virtiofs mounts** — project dir (read-only) + data dir (writable) + survive VM rebuilds +- **Delegate to OpenClaw** — wrap their installer/onboarding, don't + reimplement + +## Two modes + +- **Interactive wizard** — Ink UI collects config, steps advance + via `onComplete` callbacks +- **Headless** — config-file-driven, no prompts + +## Full reference + +See [references/architecture.md](references/architecture.md) for the +complete documentation including directory structure, component +relationships, onboarding approach, and error handling. diff --git a/.agents/skills/understanding-architecture/references/architecture.md b/.agents/skills/understanding-architecture/references/architecture.md new file mode 120000 index 0000000..12790e3 --- /dev/null +++ b/.agents/skills/understanding-architecture/references/architecture.md @@ -0,0 +1 @@ +../../../docs/architecture.md \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index d80e928..6e9235e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,15 +29,16 @@ lifecycle (VM, networking, credentials); OpenClaw manages the agents inside. ## Workspace Structure -Bun workspaces monorepo with five packages: +Bun workspaces monorepo with six packages: ``` packages/ - types/ @clawctl/types — Shared types, schemas, constants, pure functions - templates/ @clawctl/templates — Pure script/config generators (string in → string out) - host-core/ @clawctl/host-core — Host-side VM management library (drivers, exec, provision) - cli/ @clawctl/cli — Host CLI (commands + Ink wizard UI) - vm-cli/ @clawctl/vm-cli — Guest CLI (claw) — runs inside the VM + types/ @clawctl/types — Shared types, schemas, constants, pure functions + templates/ @clawctl/templates — Pure script/config generators (string in → string out) + capabilities/ @clawctl/capabilities — Capability definitions, runner, state tracking + host-core/ @clawctl/host-core — Host-side VM management library (drivers, exec, provision) + cli/ @clawctl/cli — Host CLI (commands + Ink wizard UI) + vm-cli/ @clawctl/vm-cli — Guest CLI (claw) — runs inside the VM ``` ### Key Directories @@ -47,8 +48,9 @@ packages/ - `packages/cli/src/steps/` — wizard step components (each self-contained, calls onComplete) - `packages/cli/src/components/` — reusable UI components (spinner, progress, log output) - `packages/host-core/src/` — non-UI logic (drivers, exec, provision, registry) +- `packages/capabilities/src/` — capability definitions (CapabilityDef modules) and runner - `packages/vm-cli/bin/claw.ts` — guest CLI entry point (compiled to binary, deployed into VM) -- `packages/vm-cli/src/tools/` — typed wrappers for system tools (apt, systemd, node, etc.) +- `packages/vm-cli/src/capabilities/` — capability registry and CapabilityContext implementation - `packages/vm-cli/src/commands/` — guest CLI commands (provision, doctor, checkpoint) - `packages/templates/src/` — template generators, one file per generated artifact - `packages/types/src/` — shared types, schemas, constants @@ -69,16 +71,18 @@ packages/ - `claw` is both the agent's management CLI and the VM's installer. All VM-side setup — system packages, tools, OpenClaw, workspace skills — is provisioned - through `claw` stages. The host deploys the binary and invokes stages; `claw` - does the work inside the VM. See `docs/vm-cli.md` for the full architecture. -- Tool wrappers in `packages/vm-cli/src/tools/` — one module per system tool, - plain functions (not classes). Provision stages import and compose them. -- **Operational functions** (`apt.install()`, `systemd.enable()`) throw on failure. - **Provision functions** (`apt.ensure()`, `homebrew.provision()`) catch errors and - return `ProvisionResult` with `status: "failed"`. -- Provisioning stages are declarative constants, not imperative functions — see - `stages.ts` for the pattern. Doctor checks use lifecycle phases (`availableAfter`) - rather than hardcoded `warn` flags. + through **capabilities**. The host deploys the binary and invokes provisioning + phases; `claw` delegates to the capability runner. See `docs/capabilities.md` + for the extension system and `docs/vm-cli.md` for the guest CLI architecture. +- Capabilities are declared in `packages/capabilities/src/capabilities/` as + `CapabilityDef` modules. Each capability hooks into lifecycle phases and + receives a `CapabilityContext` SDK for system access. +- Remaining tool modules in `packages/vm-cli/src/tools/` (`curl.ts`, `fs.ts`, + `systemd.ts`, `shell-profile.ts`, `openclaw.ts`) back the `CapabilityContext` + implementation — capabilities use them indirectly via the context, not by + direct import. +- Doctor checks are declared on capabilities via `doctorChecks` and use + lifecycle phases (`availableAfter`) rather than hardcoded `warn` flags. - The `claw` binary is compiled with `bun run build:claw` and deployed into the VM at `/usr/local/bin/claw` during provisioning. diff --git a/docs/architecture.md b/docs/architecture.md index d8ab482..7081f90 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -69,6 +69,19 @@ are `cli/` (host-side) and `vm-cli/` (guest-side): packages/ types/ Shared types, schemas, constants templates/ Lima config generators (lima-yaml.ts) + capabilities/ Capability definitions + runner + src/ + capabilities/ Individual capability modules + system-base/ APT packages, Node.js, systemd linger + homebrew/ Homebrew + shell profile + openclaw/ OpenClaw CLI + gateway stub + one-password/ 1Password CLI + skills + AGENTS.md section + checkpoint.ts Checkpoint skill + AGENTS.md section + tailscale.ts Tailscale installer + runner.ts Phase runner (executes resolved hooks) + state.ts State tracking (capability-state.json) + util.ts Hook key parsing + index.ts Public exports host-core/ Host-side library src/ drivers/ VM backend abstraction @@ -95,22 +108,21 @@ packages/ src/ exec.ts execa wrapper for guest-side commands output.ts JSON envelope helpers (log, ok, fail) + capabilities/ + registry.ts Static capability registry + dependency resolution + context.ts CapabilityContext implementation (wires to vm-cli tools) commands/ - provision/ Declarative stages (system.ts, tools.ts, openclaw.ts) + runner (stages.ts) + provision/ Provision subcommands (delegates to capability runner) doctor.ts Health checks with lifecycle-based warnings checkpoint.ts Signal host to commit data changes - tools/ One module per system tool + tools/ System primitives backing CapabilityContext types.ts ProvisionResult interface - apt.ts apt-get operations - systemd.ts systemctl + loginctl operations - node.ts Node.js via NodeSource - tailscale.ts Tailscale installer - homebrew.ts Homebrew (Linuxbrew) - op-cli.ts 1Password CLI arm64 binary - openclaw.ts OpenClaw CLI + gateway stub fs.ts File system helpers (ensureLineInFile, ensureDir) curl.ts Download helpers shell-profile.ts Login profile management + systemd.ts systemctl + loginctl operations + openclaw.ts OpenClaw CLI queries (isInstalled, version, doctor) + provision-config.ts Provision config reader ``` ## Key Design Decisions @@ -130,8 +142,8 @@ driver.exec(vmName, "claw doctor --json --after provision-openclaw") This gives us the same language and type system on both sides of the VM boundary. Provisioning logic is testable TypeScript, errors are returned as structured JSON instead of parsed from log output, and every operation -is idempotent by construction (each tool module checks current state -before acting). Doctor checks declare which lifecycle phase they require +is idempotent by construction (capabilities check current state before +acting). Doctor checks declare which lifecycle phase they require (`availableAfter`), so the host can distinguish expected warnings from real failures based on how far provisioning has progressed. @@ -139,8 +151,8 @@ The `claw` binary is compiled with `bun run build:claw` (linux-arm64) and deployed during the provisioning sequence. In development, `bin/clawctl-dev` auto-builds it before running the host CLI. -See `docs/vm-cli.md` for the full architecture of the guest CLI and its -tool abstraction layer. +See `docs/vm-cli.md` for the guest CLI architecture and +`docs/capabilities.md` for the capability extension system. ### vz virtualization backend diff --git a/docs/capabilities.md b/docs/capabilities.md index a17af7b..d913aa1 100644 --- a/docs/capabilities.md +++ b/docs/capabilities.md @@ -282,3 +282,22 @@ hooks. These integrate with `claw doctor` and follow the same warning if its phase hasn't been reached yet, an error otherwise. See `docs/vm-cli.md` for the full doctor checks table. + +## Agent Skills (SKILL.md files) + +Capabilities can install **Agent Skills** — structured instructions that +coding agents discover and load on demand. Skills follow the +[Agent Skills specification](https://agentskills.io/specification) and +live in the workspace's `.agents/skills/` directory. + +A skill is a directory containing a `SKILL.md` file (YAML frontmatter + +Markdown body) plus optional `references/`, `scripts/`, and `assets/` +subdirectories. Agents load only the `name` and `description` at startup; +the full body and referenced files are loaded when the skill is activated. + +Existing capability-installed skills: + +- **`checkpoint`** (`checkpoint.ts`) — installs the checkpoint skill + into the workspace during `provision-workspace` +- **`one-password`** (`one-password/skill.ts`) — installs the 1Password + skill with wrapper scripts during `provision-tools` diff --git a/docs/cli-wizard-flow.md b/docs/cli-wizard-flow.md index f0b3485..6d47517 100644 --- a/docs/cli-wizard-flow.md +++ b/docs/cli-wizard-flow.md @@ -49,7 +49,7 @@ Completed fields show with a green checkmark above the current prompt. **Component**: `src/steps/host-setup.tsx` **Step indicator**: Step 3/8 -This step only appears if Lima was not found in Step 1. It runs `brew install lima` via the `installLima()` function in `src/lib/homebrew.ts`. +This step only appears if Lima was not found in Step 1. It runs `brew install lima` via `installFormula()` from `host-core/src/homebrew.ts`. **Flow logic**: diff --git a/docs/project-directory.md b/docs/project-directory.md index bda4a17..e412626 100644 --- a/docs/project-directory.md +++ b/docs/project-directory.md @@ -8,6 +8,7 @@ When the CLI runs, it creates a project directory with everything needed to mana / clawctl.json Instance config (sanitized, no secrets) data/ Writable persistent mount (survives VM rebuilds) + capability-state.json Tracks installed capability versions .git/ Git repository .gitignore Ignores VM images, credentials, .DS_Store ``` @@ -78,9 +79,15 @@ clawctl create --config config.json ## Provisioning -Provisioning scripts are generated from TypeScript templates and written directly into the VM as ephemeral temp files during bootstrap. They are not persisted in the project directory. See [vm-provisioning.md](./vm-provisioning.md) for details on what is installed. +Provisioning is handled by capabilities — self-contained modules that +declare what they install, when they run, and what health checks they +provide. The host deploys the `claw` binary into the VM and invokes +provisioning phases; capabilities do the work inside the VM. -To add new provisioning steps, modify the template generators in `src/templates/` and recreate the VM. +Installed capability versions are tracked in `data/capability-state.json`. +To add new provisioning steps, write a `CapabilityDef` module. See +[capabilities.md](./capabilities.md) for the extension system and +[vm-provisioning.md](./vm-provisioning.md) for the provisioning sequence. ## data/ diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 2870532..abe7aa1 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -75,8 +75,9 @@ prompt — if it appears stuck, give it time. ### 1Password CLI version -The `op` version is pinned in `src/templates/installers/op-cli.ts`. To update, -change `OP_VERSION` there and recreate the VM. +The `op` version is pinned in +`packages/capabilities/src/capabilities/one-password/op-cli.ts`. To +update, change `OP_VERSION` there, rebuild `claw`, and re-provision. ### General provisioning failures diff --git a/docs/vm-cli.md b/docs/vm-cli.md index d42e54a..c7989e8 100644 --- a/docs/vm-cli.md +++ b/docs/vm-cli.md @@ -17,8 +17,9 @@ This gives us: - **Structured output** — every command returns a JSON envelope (`{ status, data, errors }`) so the host can programmatically react to results instead of parsing log lines. -- **Idempotent tool wrappers** — each system tool (apt, systemd, node, - etc.) has a typed module that checks current state before acting. +- **Capability-driven provisioning** — provisioning is handled by + `CapabilityDef` modules that hook into lifecycle phases. See + `docs/capabilities.md` for the extension system. - **A health-check surface** — `claw doctor` runs inside the VM with full access to the guest filesystem, systemd, and PATH. The host calls it to verify provisioning and diagnose issues. @@ -82,18 +83,14 @@ packages/vm-cli/ index.ts Registers provision subcommands (system, tools, openclaw, workspace, bootstrap) doctor.ts Health checks (mounts, env, PATH, services, openclaw) checkpoint.ts Signal host to commit data changes - tools/ One module per system tool + tools/ System primitives backing CapabilityContext types.ts ProvisionResult interface fs.ts ensureLineInFile, ensureDir curl.ts downloadFile, downloadAndRun shell-profile.ts ensureInBashrc, ensureInProfile, ensurePath - apt.ts isInstalled, update, install, ensure systemd.ts findDefaultUser, enableLinger, isEnabled, isActive, ... - node.ts isInstalled, version, provision - tailscale.ts isInstalled, provision - homebrew.ts isInstalled, install, provision - op-cli.ts isInstalled, provision - openclaw.ts isInstalled, version, doctor, provision, provisionGatewayStub + openclaw.ts isInstalled, version, doctor + provision-config.ts Provision config reader ``` Provisioning logic (what gets installed, in what order) lives in the @@ -102,126 +99,24 @@ The provision subcommands are thin — they resolve hooks from the registry and delegate to the capability runner. See `docs/capabilities.md` for the full extension system. -## Tool abstraction layer (`tools/`) +## System primitives (`tools/`) -Each module in `tools/` wraps all interactions with one system tool. -Provisioning commands never call `exec()` directly — they compose tool -functions. +The remaining modules in `tools/` are low-level system primitives that +back the `CapabilityContext` implementation. Capabilities access them +indirectly via the context SDK — not by direct import. -### Two kinds of functions +| Module | Purpose | +| --------------------- | --------------------------------------------------------- | +| `types.ts` | `ProvisionResult` interface | +| `fs.ts` | `ensureLineInFile`, `ensureDir` | +| `curl.ts` | `downloadFile`, `downloadAndRun` | +| `shell-profile.ts` | `ensureInBashrc`, `ensureInProfile`, `ensurePath` | +| `systemd.ts` | systemctl + loginctl operations | +| `openclaw.ts` | OpenClaw CLI queries (`isInstalled`, `version`, `doctor`) | +| `provision-config.ts` | Provision config reader | -**Operational functions** are building blocks. They do one thing and -throw on failure: - -```typescript -// tools/apt.ts -export async function install(packages: string[]): Promise { - const result = await exec("apt-get", ["install", "-y", "-qq", ...packages]); - if (result.exitCode !== 0) { - throw new Error(`apt-get install failed: ${result.stderr}`); - } -} -``` - -**Provision functions** are what stages call. They check current -state, act if needed, catch errors, and return a `ProvisionResult`: - -```typescript -// tools/apt.ts -export async function ensure(packages: string[]): Promise { - try { - const toInstall = []; - for (const pkg of packages) { - if (!(await isInstalled(pkg))) toInstall.push(pkg); - } - if (toInstall.length === 0) { - return { name: "apt-packages", status: "unchanged" }; - } - await update(); - await install(toInstall); - return { name: "apt-packages", status: "installed" }; - } catch (err) { - return { name: "apt-packages", status: "failed", error: String(err) }; - } -} -``` - -### ProvisionResult - -```typescript -interface ProvisionResult { - name: string; - status: "installed" | "unchanged" | "failed"; - detail?: string; - error?: string; -} -``` - -Three states: `"installed"` (something changed), `"unchanged"` (already -in the desired state), `"failed"` (error caught and returned). - -### Cross-tool composition - -Tools can use each other. For example, `openclaw.provisionGatewayStub()` -internally calls `systemd.isEnabled()`, `systemd.daemonReload()`, -`systemd.enable()`, and `fs.ensureDir()`. The stage definition doesn't -need to know these details: - -```typescript -// commands/provision/openclaw.ts — the stage -{ name: "openclaw", label: "OpenClaw", run: () => openclaw.provision() }, -{ name: "env-vars", label: "Environment variables", run: () => openclaw.provisionEnvVars() }, -{ name: "npm-global-path", label: "npm-global PATH", run: () => openclaw.provisionNpmGlobalPath() }, -{ name: "gateway-stub", label: "Gateway service stub", run: () => openclaw.provisionGatewayStub() }, -``` - -### Provisioning stages - -Each provisioning stage is a declarative `ProvisionStage` constant — a -named list of steps with `run` functions. A shared `runStage()` handles -all the boilerplate (numbered logging, collecting results, checking -failures, ok/fail output): - -```typescript -// commands/provision/system.ts -export const systemStage: ProvisionStage = { - name: "system", - phase: "provision-system", - steps: [ - { name: "apt-packages", label: "APT packages", run: () => apt.ensure(APT_PACKAGES) }, - { name: "nodejs", label: "Node.js", run: () => node.provision() }, - { name: "systemd-linger", label: "systemd linger", run: () => systemd.provisionLinger() }, - { name: "tailscale", label: "Tailscale", run: () => tailscale.provision() }, - ], -}; -``` - -The runner produces numbered, structured output: - -``` -=== system provisioning === -[1/4] APT packages - ✓ installed — build-essential, git, curl -[2/4] Node.js - ✓ unchanged — v22.22.1 -[3/4] systemd linger - ✓ installed — lima -[4/4] Tailscale - ✓ unchanged -=== system provisioning complete (4 steps) === -``` - -### Adding a new tool - -1. Create `tools/.ts` with operational functions + a `provision()` - function that returns `ProvisionResult`. -2. Import it in the appropriate stage definition - (`commands/provision/system.ts`, `tools.ts`, or `openclaw.ts`). -3. If `doctor.ts` should check for it, use the tool's `isInstalled()` - or other query functions there. - -Constants (URLs, versions) go in the tool module — not centralized — -unless they're shared across multiple tools. +For the provisioning extension system (how to add new tools, define +hooks, declare doctor checks), see `docs/capabilities.md`. ## JSON output protocol diff --git a/docs/vm-provisioning.md b/docs/vm-provisioning.md index fecb03f..8f6ad8b 100644 --- a/docs/vm-provisioning.md +++ b/docs/vm-provisioning.md @@ -5,8 +5,8 @@ inside the VM. It's driven by the `claw` binary — a compiled TypeScript CLI deployed into the VM during setup. The host CLI invokes it via `driver.exec()` and gets structured JSON results back. -For the full architecture of the internal CLI and its tool abstraction -layer, see `docs/vm-cli.md`. +For the guest CLI architecture, see `docs/vm-cli.md`. For the capability +extension system that drives provisioning, see `docs/capabilities.md`. ## Provisioning sequence @@ -78,18 +78,18 @@ not an error. ## Idempotency -Every tool module checks current state before acting: +Every capability step checks current state before acting. Steps receive +a `CapabilityContext` SDK and return a `ProvisionResult`: -- `apt.isInstalled(pkg)` checks `dpkg -l` before installing -- `node.isInstalled()` + `node.version()` checks before running NodeSource -- `homebrew.isInstalled()` checks before running the installer -- `systemd.isEnabled(service)` checks before writing the unit file -- `openclaw.isInstalled()` checks before running the installer +- **`unchanged`** — already in the desired state (idempotent skip) +- **`installed`** — something changed +- **`failed`** — error caught and returned -A provision function returns `{ status: "unchanged" }` if everything is -already in place, `{ status: "installed" }` if it made changes, or -`{ status: "failed" }` if something went wrong. Re-running provisioning -on an already-provisioned VM is a fast no-op. +Re-running provisioning on an already-provisioned VM is a fast no-op. +The runner also tracks installed capability versions in +`data/capability-state.json` and supports migrations when a capability's +declared version changes. See `docs/capabilities.md` for the +CapabilityContext SDK and step function patterns. ## Verification (`claw doctor`) @@ -149,8 +149,8 @@ packages. On first run this can take several minutes. `NONINTERACTIVE=1` ensures it does not prompt. **1Password CLI version**: The version is pinned in -`packages/vm-cli/src/tools/op-cli.ts`. To update, change `OP_VERSION` -there, rebuild `claw`, and re-provision. +`packages/capabilities/src/capabilities/one-password/op-cli.ts`. To +update, change `OP_VERSION` there, rebuild `claw`, and re-provision. **Gateway service not active**: This is expected after provisioning. The stub service runs `/bin/true` and exits immediately. The real diff --git a/tasks/2026-03-16_1538_docs-update-post-capabilities/TASK.md b/tasks/2026-03-16_1538_docs-update-post-capabilities/TASK.md new file mode 100644 index 0000000..0661e4a --- /dev/null +++ b/tasks/2026-03-16_1538_docs-update-post-capabilities/TASK.md @@ -0,0 +1,48 @@ +# Docs Update: Post-Capabilities Refactoring + Agent Skills + +## Status: Resolved + +## Scope + +Fix stale documentation references left over from the capabilities refactoring +(deleted `tools/` modules, old `stages.ts` pattern) and create Agent Skills +(`.agents/skills/`) for progressive discovery of developer docs. + +Does NOT cover: changing any runtime code, modifying capabilities themselves, +or adding new capabilities. + +## Plan + +1. Fix stale docs in CLAUDE.md, architecture.md, vm-cli.md, vm-provisioning.md, + project-directory.md, capabilities.md +2. Create `.agents/skills/` with four skills (symlinks to docs/) +3. Verify no stale references remain; validate formatting + +## Steps + +- [x] Update CLAUDE.md (six packages, capabilities package, rewrite claw conventions) +- [x] Update docs/architecture.md (directory tree, tool abstraction references) +- [x] Update docs/vm-cli.md (remove tool abstraction section, add system primitives) +- [x] Update docs/vm-provisioning.md (capabilities references, idempotency, 1Password path) +- [x] Update docs/project-directory.md (capability-state.json, provisioning section) +- [x] Update docs/capabilities.md (add Agent Skills section) +- [x] Create .agents/skills/ with four skill directories + SKILL.md + symlinks +- [x] Grep for stale references +- [x] Run format check +- [x] Fix bonus stale refs in docs/troubleshooting.md and docs/cli-wizard-flow.md + +## Notes + +- Also caught stale references in `docs/troubleshooting.md` (op-cli.ts path) + and `docs/cli-wizard-flow.md` (homebrew.ts path) that weren't in the original + plan. Fixed them since they were easy wins. + +## Outcome + +- Updated 8 docs files to replace all references to deleted tool modules, + old stages pattern, and "five packages" with current capabilities-based + architecture +- Added Agent Skills section to capabilities.md +- Created 4 Agent Skills in `.agents/skills/` with SKILL.md files following + the Agent Skills spec, plus symlinks to canonical docs +- All formatting passes, no stale references remain in docs/