{
  "markdown": "# clawops\n\n[![npm version](https://img.shields.io/npm/v/@clawops/cli)](https://www.npmjs.com/package/@clawops/cli)\n[![npm downloads](https://img.shields.io/npm/dm/@clawops/cli)](https://www.npmjs.com/package/@clawops/cli)\n\nMCP-native infrastructure ops for OpenClaw — with read-only mode, destructive-action confirmation, and audit logs built in.\n\n**clawops** is a CLI and [MCP](https://modelcontextprotocol.io/) server for deploying and operating\nself-hosted [OpenClaw](https://github.com/openclaw/openclaw) instances. Provision on AWS, GCP,\nAzure, or any Linux VM — then manage day-to-day operations from the terminal, or let Claude Code\nand Cursor drive them through typed MCP tools with explicit safety controls.\n\n---\n\n## What's new in v1.7.3\n\n**README maintenance.** The release-notes section had grown to four versions and was missing\nv1.7.2 entirely. It now carries the current line only; [`CHANGELOG.md`](CHANGELOG.md) remains the\nfull history.\n\n---\n\n## What's new in v1.7.2\n\n**Requires OpenClaw >= 2026.9.2.** This line deploys the 2.0 runtime contract: a writable state\ndirectory holding config, SQLite and plugins; `gateway.mode` written into the config; no\n`--allow-unconfigured`. A pre-2.0 OpenClaw understands none of it, so `doctor`, `plan`, `up` and\n`apply` refuse anything below the floor. For OpenClaw `<= 2026.7.1-2`, use\n`npm install -g @clawops/cli@legacy`.\n\nThe support range in `spec/openclaw-versions.yaml` was previously unbounded *and read by no code*,\nso any OpenClaw release was accepted. Moving tags are now resolved to a concrete version **before**\nthe range check, and an unresolved tag is refused rather than assumed safe — `latest` and `stable`\nboth point at 2.0 today. The default is now a concrete pin.\n\n`clawops doctor --stack <name>` reports the version a deployed gateway is actually running, so an\nexisting deployment that already picked up 2.0 through a moving tag can be identified.\n\n**`clawops config set` now applies.** It never has. The config clawops mounted was read by nothing\non either OpenClaw line — models, channels and auth mode were silently discarded. Setting\n`OPENCLAW_CONFIG_PATH` fixes it, with four guards: `gateway.port` normalisation (with a warning),\nan argv `--port` pin, a parse check, and a post-restart health gate. The MCP `gateway restart`\ntool, which dropped the config mount entirely, now matches the CLI path.\n\n**A fresh local deployment starts.** OpenClaw refuses a non-loopback bind without auth and always\nbinds `0.0.0.0` in a container, and the bootstrap never supplied a token — so the gateway exited 78\nand systemd restart-looped. A token is now generated once, kept in a `0600` env file, and passed\nvia `--env-file`, never on the command line.\n\n**Ollama is reachable.** `localhost` inside the container is the container. clawops now passes\n`--add-host=host.docker.internal:host-gateway` and defaults the Ollama address to match.\n\n**Packaging.** `spec/` was missing from the published files, and neither it nor\n`bootstrap.sh.tmpl` resolved from the bundle — so `clawops plan` and `clawops up --provider local`\nfailed from an npm install. Both are now shipped and resolved correctly.\n\n**SSH host keys.** The verifier read each `known_hosts` line's *key type* as the key, so any\nstandard entry failed permanently with `Host denied`. Standard entries now parse, including\ncomma-separated host lists, `[host]:port`, hashed hostnames, `@revoked` / `@cert-authority`\nmarkers, and wildcard and negated patterns.\n\n> **Behaviour change:** a host covered by a wildcard whose key does not match is now refused where\n> it previously connected. Ignoring wildcards meant trust-on-first-use accepted a key your own\n> `known_hosts` contradicted. This matches OpenSSH.\n\n---\n\n\n## Who this is for\n\n- **OpenClaw users** who want the simplest path to self-hosting across cloud or local VMs, with\n  reliable deploy, status checks, logs, backups, and upgrades in a single CLI.\n- **Claude Code / Cursor / MCP users** looking for a real-world reference implementation of safe\n  infrastructure operations through MCP — typed tool schemas, read-only mode, destructive-action\n  confirmation, and audit logs.\n- **Self-hosted AI and local-first developers** who want to run their own AI assistant without\n  committing to Kubernetes, a managed SaaS platform, or a single cloud provider.\n\n---\n\n## What clawops does\n\n- Provisions and tears down OpenClaw infrastructure on **AWS, GCP, Azure, and local VMs** using\n  the Pulumi Automation API (embedded — no `pulumi` binary required).\n- Manages day-to-day operations: status, logs, SSH, tunnels, config, agents, gateway, backups.\n- Exposes every operation as a **typed MCP tool** so AI agents can drive ops safely.\n- Enforces a **plan → review → apply** discipline for cloud deployments.\n- Emits **JSON output everywhere** (`--json`) for scripting and automation.\n- Never stores cloud credentials — reads them from your environment's existing CLI profiles.\n\n## What clawops does not do\n\n- **No high availability or clustering.** Optimized for single-node deployments.\n- **No Kubernetes.** It deploys to VMs, not container orchestration platforms.\n- **No OpenClaw skill/agent authoring.** clawops manages infrastructure; what runs on it is up to\n  you and OpenClaw.\n- **No TLS or domain automation** (yet). Bring your own reverse proxy or see\n  [`docs/limitations.md`](docs/limitations.md) for the manual path.\n- **No credential storage.** Cloud credentials must be configured in your environment before using\n  clawops. They are never written to `~/.clawops/config.json`.\n- **No native Windows.** WSL2 is fully supported; see [`docs/support-matrix.md`](docs/support-matrix.md).\n\n---\n\n## Quick Start\n\n```bash\nnpm install -g @clawops/cli\nclawops setup\n```\n\n`clawops setup` is an interactive wizard that gets OpenClaw running in about 2 minutes. It\nhandles everything in one flow — no config files to write by hand, no commands to memorize.\n\n### What the wizard does\n\n**Step 1 — Choose a deployment target**\n\nPick an existing server you can SSH into (Linux or macOS), or a new cloud VM on AWS, GCP, or\nAzure. Cloud deployments walk you through authenticating with the provider CLI if you aren't\nalready signed in.\n\n**Step 2 — Pick an LLM provider**\n\nChoose from Anthropic, OpenAI, Amazon Bedrock, Ollama, or others. The wizard prompts for your\nAPI key and saves it locally (in `~/.clawops/secrets/`, chmod 600) — it is never sent anywhere\nexcept to OpenClaw on the target host when the config is applied.\n\n**Step 3 — Add chat integrations (optional)**\n\nSelect any combination of Discord, Telegram, Slack, WhatsApp, or Teams. The wizard collects each\nintegration's bot token the same way as the API key — paste it in, reference an env var, or point\nto a file.\n\n**Step 4 — Wire your AI editor**\n\nSelect which AI apps should have access to clawops — Claude Desktop, Claude Code, Cursor,\nWindsurf, VS Code, and Zed are all supported. The wizard writes an MCP server entry into each\napp's config file using the absolute binary path so the app can launch it independently.\n\n**Step 5 — Deploy**\n\nThe wizard bootstraps OpenClaw on the target host over SSH (installs Docker, pulls the image,\nstarts the container), applies your LLM and integration config, generates a gateway auth token,\nand prints a direct dashboard URL:\n\n```\n✔ All done! OpenClaw is running.\nℹ Open dashboard: http://192.168.1.50:18789?token=<your-token>\nℹ Token saved to ~/.clawops/secrets/GATEWAY_TOKEN_my-stack\n```\n\n**Prerequisites:** Node.js ≥ 22, an SSH key, and either an SSH-reachable Linux/macOS host or a\ncloud account with CLI credentials configured (`aws configure`, `gcloud auth login`, or `az login`).\n\nFor a full narrated walkthrough with example output, see [`docs/demo-script.md`](docs/demo-script.md).\n\n---\n\n### Manual setup — existing server\n\nIf you prefer step-by-step control, or are adding clawops to an already-running deployment:\n\n```bash\nnpm install -g @clawops/cli\n\nclawops doctor   # verify environment\n\nclawops init --provider local --host 192.168.1.50 --user ubuntu --key-path ~/.ssh/id_ed25519\nclawops up       # installs Docker + OpenClaw over SSH\nclawops status\n```\n\nSee [`docs/examples/local-vm.md`](docs/examples/local-vm.md) for SSH prerequisites, firewall\nsetup, and troubleshooting.\n\n### Manual setup — cloud (AWS)\n\n```bash\nnpm install -g @clawops/cli\n\n# Requires AWS credentials in your environment (AWS_PROFILE or ~/.aws/credentials)\nclawops init --provider aws\n\n# Edit ~/.clawops/config.json — set stateUrl to your S3 bucket\n\nclawops plan --provider aws --stack default --out /tmp/plan.json\nclawops apply /tmp/plan.json\n```\n\n---\n\n## Connect an AI editor\n\nThe `setup` wizard handles this automatically (Step 4). To wire or re-wire editors at any time:\n\n```bash\nclawops mcp install\n```\n\nThis opens the same interactive checkbox used in the wizard — select Claude Desktop, Claude Code,\nCursor, Windsurf, VS Code, or Zed and clawops writes the MCP entry into each app's config using\nthe correct absolute binary path.\n\nTo add the entry manually instead, paste this into your editor's MCP config:\n\n```json\n{\n  \"mcpServers\": {\n    \"clawops\": {\n      \"command\": \"/path/to/clawops\",\n      \"args\": [\"mcp\", \"serve\", \"--read-only\"]\n    }\n  }\n}\n```\n\nReplace `/path/to/clawops` with the output of `which clawops`. Config file locations:\n\n| App | Path |\n|---|---|\n| Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |\n| Claude Desktop (Linux) | `~/.config/Claude/claude_desktop_config.json` |\n| Claude Code | `~/.claude.json` |\n| Cursor | `~/.cursor/mcp.json` |\n| Windsurf | `~/.codeium/windsurf/mcp_config.json` |\n| VS Code (macOS) | `~/Library/Application Support/Code/User/mcp.json` |\n| VS Code (Linux) | `~/.config/Code/User/mcp.json` |\n| Zed | `~/.config/zed/settings.json` (key: `context_servers`) |\n\n**Start with `--read-only`** — it enables status, logs, config reads, and diagnostics while\nblocking mutations. Remove it only after reviewing\n[`docs/security/mcp-safety.md`](docs/security/mcp-safety.md).\n\nDestructive tools (`clawops_destroy`, `clawops_up`, `clawops_config_set`, etc.) require explicit\nconfirmation before executing — they will never run silently.\n\nFor HTTP mode setup see [`docs/mcp/`](docs/mcp/).\n\n---\n\n## Day-to-day operations\n\n```bash\nclawops status              # Stack outputs: IP, gateway URL, SSH info\nclawops logs -f             # Tail OpenClaw logs over SSH\nclawops ssh                 # Interactive SSH session\nclawops ssh --command \"docker ps\"\n\nclawops config get maxAgents\nclawops config set maxAgents 8\n\nclawops tunnel              # Port-forward gateway UI to localhost\n\nclawops destroy --yes       # Destroy cloud-provider stack\nclawops down --yes          # Destroy local-provider stack\n```\n\n---\n\n## Commands\n\n| Command | Description |\n|---|---|\n| `setup` | First-run wizard — guided LLM, integrations, and deploy-plan generation |\n| `init` | Register a stack in `~/.clawops/config.json` without provisioning |\n| `up` | Provision or update stack (`--dry-run` for preview) |\n| `down` | Destroy local-provider stack (requires `--yes`; `--dry-run` shows current outputs) |\n| `destroy` | Destroy cloud-provider stack with confirmation prompt (`--dry-run` shows current outputs) |\n| `status` | Show stack outputs: IP, gateway URL, region, provisioned time |\n| `plan` | Generate a deploy-plan JSON artifact (dry-run safe) |\n| `apply` | Apply a previously reviewed plan file (`--dry-run` validates and shows diff without applying) |\n| `ssh` | Interactive SSH session or run a remote command |\n| `logs` | Stream OpenClaw logs (`-f`, `--tail N`, `--since 5m`) |\n| `tunnel` | Local port-forward to gateway UI over SSH |\n| `config` | Get/set remote OpenClaw config values (`--dry-run` shows would-write JSON) |\n| `agents` | List OpenClaw agents, or stream one agent's logs |\n| `gateway` | Restart the OpenClaw gateway service |\n| `backup` | Create an OpenClaw state backup (`restore` returns in clawops 2.x — see [limitations](docs/limitations.md#backup-and-restore)) |\n| `stacks` | List named stacks and their state |\n| `doctor` | Check Node version, config, SSH key, provider credentials, and Pulumi home |\n| `secret` | Manage secrets: `list`, `set`, `delete`, `rotate`, `audit` |\n| `monitor` | Live dashboard: gateway health, container stats, log tail, stack picker |\n| `mcp serve` | Start the embedded MCP server (stdio or HTTP) |\n| `mcp install` | Interactively wire clawops into AI editors |\n| `mcp wire` | Wire the gateway's AI as an MCP client of clawops |\n| `help` | List all commands and global flags |\n| `harden` | Apply security hardening to a deployed stack (SSH, UFW, fail2ban, unattended-upgrades, Docker socket; AWS: SG audit, SSM check, Flow Logs, GuardDuty) |\n| `bug` | Open a pre-filled GitHub issue with system context from `doctor` |\n\nFull flag reference: `clawops <command> --help`\n\n---\n\n## Plan → Apply workflow\n\nFor non-local providers, clawops enforces a review-before-apply discipline:\n\n```bash\n# 1. Generate a plan — runs `pulumi preview` internally, produces JSON\nclawops plan --provider aws --region us-east-1 --out /tmp/plan.json\n\n# 2. Review plan.json — the `diff` field shows projected changes at plan-generation time\ncat /tmp/plan.json | jq .diff\n\n# 3. Apply — reads and validates the plan file, then runs `pulumi up`\nclawops apply /tmp/plan.json\n\n# Without --yes, apply prompts: \"Continue? (y/N)\"\nclawops apply /tmp/plan.json --yes    # skip prompt in automation\n```\n\nThe plan JSON conforms to `spec/deploy-plan.schema.json` (AJV-validated) and captures reviewed\nintent: provider, region, instance type, CIDR ranges, and OpenClaw version. `apply` re-runs\n`pulumi up` using those parameters against the current live state — it does not replay a locked\nexecution artifact. Review and apply in the same session to minimize drift risk.\n\nSee [`docs/plan-apply.md`](docs/plan-apply.md) for full semantics, drift guidance, and the safe CI pattern.\n\n---\n\n## MCP server\n\nclawops ships an embedded [MCP](https://modelcontextprotocol.io/) server. Claude Code, Cursor, and\nany MCP-compatible agent can drive deployments without leaving the chat interface.\n\n### Wire your editor\n\n```bash\nclawops mcp install   # interactive checkbox — writes config for selected apps\n```\n\nThe wizard resolves the absolute binary path automatically so app launchers can find `clawops`\nwithout inheriting your shell's `PATH`. See [Connect an AI editor](#connect-an-ai-editor) above\nfor manual config paths.\n\n### Wire the gateway AI\n\nThe OpenClaw gateway runs its own AI agent. Once wired, that agent can call clawops directly\ninstead of guessing at infrastructure state:\n\n```bash\nclawops mcp wire --stack prod   # write MCP client entry into gateway config + restart\n```\n\nRequires OpenClaw ≥ 2026.4 on the gateway. The `clawops setup` wizard offers this step\nautomatically after a successful deploy.\n\n### Stdio mode (Claude Code / Cursor / VS Code)\n\nStart the server manually or confirm your config is correct:\n\n```bash\nclawops mcp serve --read-only   # safe for first evaluation\nclawops mcp serve               # full mode — enables provisioning, config write, ssh exec\n```\n\n### HTTP mode (remote / multi-client)\n\n```bash\nclawops mcp serve --http 3333 --bind 127.0.0.1\n# MCP HTTP server listening on 127.0.0.1:3333\n```\n\nDo not bind to a non-loopback address without additional authentication controls in front of it.\n\n### Available tools\n\n| Tool | Toolset | Description |\n|---|---|---|\n| `clawops_status` | read | Show stack outputs |\n| `clawops_logs_tail` | read | Tail OpenClaw logs |\n| `clawops_config_get` | read | Read a remote config value |\n| `clawops_agents_list` | read | List running agents |\n| `clawops_task_status` | read | Poll a long-running task |\n| `clawops_stacks_list` | admin | List all stacks and their state |\n| `clawops_up` | cli | Provision or update a stack |\n| `clawops_plan` | cli | Generate a deploy plan |\n| `clawops_apply` | cli | Apply a plan file |\n| `clawops_ssh_exec` | cli | Run a command over SSH |\n| `clawops_config_set` | cli | Write a remote config value |\n| `clawops_destroy` | cli | Destroy a stack (elicits confirmation) |\n| `clawops_workflow_deploy_app` | workflow | End-to-end deploy: plan → confirm → apply → status |\n\n`read` toolset tools are available in `--read-only` mode. All other toolsets require full mode.\nDestructive tools require explicit confirmation (elicitation) unless `yes: true` is passed.\n\nSee [`docs/security/tool-risk-matrix.md`](docs/security/tool-risk-matrix.md) for the full risk\nclassification of every tool.\n\n---\n\n## Configuration\n\nConfig lives at `~/.clawops/config.json` (override with `$CLAWOPS_HOME`).\n\n```json\n{\n  \"version\": 1,\n  \"defaults\": {\n    \"provider\": \"aws\",\n    \"stack\": \"default\"\n  },\n  \"stacks\": {\n    \"default\": {\n      \"provider\": \"aws\",\n      \"region\": \"us-east-1\",\n      \"stateUrl\": \"s3://my-clawops-state\"\n    }\n  },\n  \"ssh\": {\n    \"keyPath\": \"~/.clawops/id_ed25519\",\n    \"knownHostsPath\": \"~/.clawops/known_hosts\"\n  }\n}\n```\n\n**Cloud credentials are never stored in config** — clawops reads them from the environment:\n\n| Provider | Credential source |\n|---|---|\n| AWS | `AWS_PROFILE` or standard AWS credential chain (`~/.aws/credentials`) |\n| GCP | `GOOGLE_APPLICATION_CREDENTIALS` or `gcloud auth application-default login` |\n| Azure | `AZURE_CLIENT_ID` / `AZURE_CLIENT_SECRET` or `az login` |\n| Local | SSH host + key configured in `stacks[name].localOpts` |\n\n---\n\n## Known limitations\n\nSee [`docs/limitations.md`](docs/limitations.md) for the full list. Key points:\n\n- **Single-node deployments only** — not a high-availability or clustering platform.\n- **`clawops apply` is not an immutable plan execution** — see [`docs/plan-apply.md`](docs/plan-apply.md).\n- **No TLS/domain automation** in the current release.\n- **MCP tools execute privileged operations** — use `--read-only` for first evaluation.\n\n---\n\n## Architecture\n\n```\nclawops\n├── src/cli/          citty-based commands (one file per verb)\n├── src/config/       ~/.clawops/config.json management\n├── src/providers/    Cloud adapters (AWS, GCP, Azure, local)\n│   ├── aws/          Pulumi inline program + ProviderAdapter\n│   ├── gcp/\n│   ├── azure/\n│   └── local/        SSH bootstrap (no Pulumi)\n├── src/pulumi/       Pulumi Automation API wrapper + output helpers\n├── src/transport/    SSH client (ssh2) + connection pool + tunnels\n├── src/mcp/          MCP server, tool handlers, progress tracking\n├── src/plan/         Maker plan generation, AJV validation, apply\n├── src/output/       ASCII table, spinner, JSON, human-readable output\n├── src/errors/       Typed error hierarchy with exit codes\n└── spec/             Machine-readable ground truth (JSON Schema, YAML)\n```\n\nKey design decisions:\n\n- **Pulumi Automation API (embedded):** no `pulumi` binary required; Pulumi home is sandboxed to `~/.clawops/.pulumi`; stack programs are inline TypeScript closures\n- **State in cloud blob storage:** GCS (`gs://`), S3 (`s3://`), Azure Blob — no local state files, no `pulumi.yaml`\n- **SSH via `ssh2`:** never shells out to `/usr/bin/ssh`; TOFU host verification against `~/.clawops/known_hosts`; connection pool with 5-min idle TTL\n- **Plan → apply discipline:** every non-local deployment goes through `generatePlan()` → review → `applyPlan()`; destructive changes always require human review of the plan JSON\n- **MCP-first:** every CLI operation has a typed MCP tool; schemas generated from `spec/mcp-tools.yaml`; all destructive tools use elicitation\n\nSee [`docs/architecture.md`](docs/architecture.md) for a full narrative, and [`docs/decisions/`](docs/decisions/) for ADRs.\n\n### Cloud provider stacks\n\nEach cloud provider is an inline Pulumi program that creates the resources below. All three share the same outputs (`publicIp`, `gatewayUrl`, `sshHost`, `sshPort`, `sshUser`) consumed by the SSH and config-overlay layers.\n\n#### AWS\n\n```mermaid\nflowchart LR\n    subgraph NET[\"Networking\"]\n        VPC[\"VPC (10.0.0.0/16)\"]\n        IGW[Internet Gateway]\n        SUBNET[\"Subnet (10.0.1.0/24)\"]\n        RT[Route Table]\n        SG[\"Security Group (ports 22, 18789)\"]\n    end\n    subgraph IAM[\"IAM\"]\n        ROLE[IAM Role]\n        SSM[SSM Policy Attachment]\n        BED[\"Bedrock Policy Attachment (optional)\"]\n        IP[Instance Profile]\n    end\n    subgraph COMPUTE[\"Compute\"]\n        KP[EC2 Key Pair]\n        EC2[\"EC2 Instance (Ubuntu 22.04, IMDSv2)\"]\n        EIP[Elastic IP]\n    end\n```\n\n[Detailed diagram →](docs/providers/aws.md#stack-diagram)\n\n#### GCP\n\n```mermaid\nflowchart LR\n    subgraph NET[\"Networking\"]\n        NW[VPC Network]\n        SN[\"Subnetwork (10.0.0.0/24)\"]\n        FW1[\"Firewall: SSH port 22 (conditional)\"]\n        FW2[\"Firewall: Gateway port 18789 (conditional)\"]\n        ADDR[Static External IP]\n    end\n    subgraph COMPUTE[\"Compute\"]\n        VM[\"Compute Instance (Debian 12, 20 GB)\"]\n    end\n```\n\n[Detailed diagram →](docs/providers/gcp.md#stack-diagram)\n\n#### Azure\n\n```mermaid\nflowchart LR\n    RG[Resource Group]\n    subgraph NET[\"Networking\"]\n        VNET[\"Virtual Network (10.0.0.0/16)\"]\n        SUBNET[\"Subnet (10.0.1.0/24)\"]\n        NSG[\"Network Security Group (ports 22, 18789)\"]\n        PIP[\"Public IP Address (Static)\"]\n        NIC[Network Interface]\n    end\n    subgraph COMPUTE[\"Compute\"]\n        VM[\"VM (Ubuntu 22.04, managed identity)\"]\n    end\n    subgraph KV[\"Key Vault (optional)\"]\n        VAULT[\"Key Vault (RBAC, name max 24 chars)\"]\n        RA[\"Role Assignment (Secrets User)\"]\n        SECRET[\"Secret: gateway-token\"]\n    end\n```\n\n[Detailed diagram →](docs/providers/azure.md#stack-diagram)\n\n---\n\n## Development\n\n### Setup\n\n```bash\ngit clone https://github.com/dfridkin/clawops.git\ncd clawops\n# Node 22+ required; use nvm: nvm use\npnpm install\npnpm dev doctor        # verify toolchain\n```\n\n### Scripts\n\n```bash\npnpm dev                   # run CLI from src/ via tsx\npnpm build                 # tsup → dist/\npnpm test                  # vitest (786 tests, ~4s)\npnpm test:changed          # vitest --changed (fast edit loop)\npnpm test:integration      # Docker-based SSH integration tests\npnpm typecheck             # tsc --noEmit\npnpm lint                  # eslint src/ tests/ scripts/ (--max-warnings=0)\npnpm gen:schemas           # regenerate src/providers/types.ts + src/mcp/tools/_generated.ts\npnpm gen:schemas --check   # CI guard: committed generated files match spec\npnpm changeset             # record a release note before merging\n```\n\n### Project layout\n\n| Path | Purpose |\n|---|---|\n| `spec/` | Machine-readable ground truth: JSON Schema, YAML. **Treat as source of truth.** |\n| `SPEC.md` | Full technical specification (milestones, rules, schemas) |\n| `DESIGN_RULES.md` | 25 normative rules (R1–R25) referenced throughout the codebase |\n| `docs/architecture.md` | Narrative system overview |\n| `docs/plan-apply.md` | Plan/apply semantics, drift guidance, CI pattern |\n| `docs/ci.md` | CI integration guide: OIDC, env vars, plan → apply in CI |\n| `docs/security/` | MCP safety model, tool risk matrix, redaction, audit logs |\n| `docs/providers/matrix.md` | Per-provider capability matrix |\n| `docs/decisions/` | Architecture Decision Records |\n| `.claude/skills/` | Invokable procedures: `/add-provider`, `/release`, `/tdd`, `/mcp-tool` |\n| `.claude/rules/` | Path-scoped lint rules loaded by Claude Code |\n\n### Code generation\n\nTwo files are generated from `spec/` and must not be hand-edited:\n\n- `src/providers/types.ts` — `ProviderAdapter` interface from `spec/providers.schema.json`\n- `src/mcp/tools/_generated.ts` — Zod schemas and type exports from `spec/mcp-tools.yaml`\n\nRun `pnpm gen:schemas` after modifying either spec file. CI enforces this with `--check`.\n\n### Adding a provider\n\nUse the `/add-provider` skill in Claude Code, or follow [`src/providers/CLAUDE.md`](src/providers/CLAUDE.md). Every adapter must satisfy `ProviderAdapter` in `src/providers/types.ts` — do not relax the schema to fit the adapter.\n\n### Adding an MCP tool\n\nUse the `/mcp-tool` skill. The skill adds the tool to `spec/mcp-tools.yaml`, runs `pnpm gen:schemas`, creates the handler in `src/mcp/tools/<toolset>/<name>.ts`, and wires it into the registry. All four annotation hints (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`) are required on every tool.\n\n### Conventional commits\n\n```\nfeat(scope): description\nfix(scope): description\ndocs / refactor / chore / test / perf / ci\n```\n\nUse `pnpm changeset` to record a release note before merging a `feat` or `fix`.\n\n---\n\n## Milestones\n\n| Milestone | Status | What ships |\n|---|---|---|\n| M0 — Scaffold | ✅ | Tooling, CI, stubs, generated types |\n| M1 — GCP MVP | ✅ | `init` / `up` / `down` / `status` / `ssh` / `logs` on GCP |\n| M2 — Remote Mgmt | ✅ | `tunnel`, `config`, `agents`, `gateway`; SSH connection pool |\n| M3 — AWS + Azure | ✅ | AWS EC2 + Azure VM adapters; `stacks list` |\n| M4 — Local VM | ✅ | Local adapter (SSH bootstrap, no Pulumi); `doctor` |\n| M5 — MCP Layer | ✅ | `mcp serve` (stdio), all CLI ops as MCP tools, progress tracking |\n| M6 — Plan/Apply | ✅ | `plan` + `apply`; deploy-plan schema; MCP HTTP transport; `workflow_deploy_app` |\n| M7 — v1.0 Polish | ✅ | Full `doctor` surface; `destroy` command; `--dry-run` across commands; CI guide |\n\nSee [`docs/roadmap.md`](docs/roadmap.md) for the public roadmap and upcoming work.\n\n---\n\n## License\n\nMPL-2.0 — see [LICENSE](LICENSE).\n",
  "bytes": 25314,
  "sha": "87703156b489a6888e792a415920b495a7fec8df66d91bb15722c403dafd73c0",
  "repo_slug": "dfridkin/clawops",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_dfridkin_clawops_57748911/readme"
}