| name | skillbox-operator |
| type | operator |
| description | Manage the skillbox runtime graph from inside the box. Onboard projects via blueprints, orchestrate services, diagnose and heal runtime issues, and maintain box health through manage.py. Activate when the user asks about box state, wants to set up a project, needs service management, or when something in the environment is broken. |
Skillbox Operator
Default Marker
Start with a stable first progress message such as:
Using skillbox-operator to assess box state before making changes.
The Rule
Every operation follows the same discipline: assess, scope, dry-run, act, verify.
- Assess -- run
status and doctor to understand current state before doing anything
- Scope -- use
--client, --service, --profile to target exactly what you intend to change
- Dry-run -- run mutating commands with
--dry-run first to preview what will happen
- Act -- after confirming the dry-run output with the user, run without
--dry-run
- Verify -- run
doctor or status after the operation to confirm success
Never skip steps. Never run an unscoped mutation. Never act without assessing first.
Assess Before Operating
Before any operation, establish situational awareness:
python3 .env-manager/manage.py status --format json [--client <id>]
python3 .env-manager/manage.py doctor --format json [--client <id>]
Parse the JSON output. Key signals:
status.services[].state -- is the service running, stopped, or dead?
status.repos[].present -- is the repo cloned?
status.tasks[].state -- has the bootstrap task completed (ready) or is it pending?
status.checks[].ok -- are filesystem checks passing?
doctor[].status -- pass, warn, or fail
doctor[].code -- identifies which check failed
Fix fail results from doctor before attempting other operations.
Operations Reference
All commands: python3 .env-manager/manage.py <command> [flags]
Inspect (safe, no side effects)
render [--format json] [--client <id>]
status [--format json] [--client <id>]
doctor [--format json] [--client <id>]
logs [--service <id>] [--lines N] [--client <id>]
client-diff <id> --target-dir <repo> [--profile <id>]
Mutate (use --dry-run first, confirm with user)
sync [--dry-run] [--client <id>]
bootstrap [--dry-run] [--client <id>] [--task <id>]
up [--dry-run] [--client <id>] [--service <id>]
down [--dry-run] [--client <id>] [--service <id>]
restart [--dry-run] [--client <id>] [--service <id>]
context [--dry-run] [--client <id>]
Macro (runs a full workflow)
onboard <id> [--blueprint <name>] [--set KEY=VALUE ...] [--dry-run] [--format json]
Scaffold (creates new files)
client-init --list-blueprints [--format json]
client-init <id> --blueprint <name> --set KEY=VALUE [--set ...] [--dry-run]
Promotion review
client-project <id> [--profile <id>] [--format json]
client-diff <id> --target-dir <repo> [--profile <id>] [--format json]
client-publish <id> --target-dir <repo> [--commit] [--profile <id>]
Structured JSON Output
All commands with --format json include a next_actions list suggesting what to run next. On error, the JSON includes a structured error object:
{
"error": {
"type": "service_health_failure",
"message": "Service app-dev failed to become healthy.",
"recoverable": true,
"recovery_hint": "Check service logs for the root cause, then restart."
},
"next_actions": ["logs --format json", "doctor --format json"]
}
Use next_actions to decide what to do after each step. Use error.recovery_hint when an operation fails.
Exit Codes
| Code | Meaning |
|---|
| 0 | Success |
| 1 | Error (check error in JSON output) |
| 2 | Drift detected (doctor found failures, run sync to fix) |
Workflow: Onboard a New Project
Use the onboard macro to scaffold, sync, bootstrap, start services, generate context, and verify in one step:
python3 .env-manager/manage.py onboard <client-id> --blueprint <blueprint> --set KEY=VALUE --format json
Preview first with --dry-run. The output includes a steps array with status for each phase (scaffold, sync, bootstrap, up, context, verify).
To choose a blueprint, list what is available:
python3 .env-manager/manage.py client-init --list-blueprints --format json
Built-in blueprints and their required --set variables:
| Blueprint | Use case | Required variables |
|---|
git-repo | Clone a repo, set as cwd | PRIMARY_REPO_URL |
git-repo-http-service | Clone + managed dev server | PRIMARY_REPO_URL, SERVICE_COMMAND |
git-repo-http-service-bootstrap | Clone + install step + dev server | PRIMARY_REPO_URL, BOOTSTRAP_COMMAND, SERVICE_COMMAND |
For the hardened v1 release path, prefer git-repo-http-service-bootstrap.
That is the blessed onboarding path that pairs with private-init,
client-local planning roots under skillbox-config/clients/<client>/plans/,
and client-publish --acceptance.
All blueprints accept optional overrides for repo IDs, paths, ports, and healthcheck URLs. Run --list-blueprints --format json to see every variable, its default, and description.
For manual step-by-step control, use client-init, sync, bootstrap, up, and context individually.
Workflow: Diagnose and Fix
When something seems broken or the user reports a problem:
1. Doctor -- find structural issues:
python3 .env-manager/manage.py doctor --format json [--client <id>]
2. Status -- find runtime state issues:
python3 .env-manager/manage.py status --format json [--client <id>]
3. Logs -- find application errors:
python3 .env-manager/manage.py logs --service <id> --lines 100
4. Apply the right fix:
| Symptom | Fix |
|---|
| Missing directories or repos | sync [--client <id>] |
| Service not running | up --service <id> [--client <id>] |
| Service unhealthy or crashed | Read logs, fix the underlying issue, restart --service <id> |
| Bootstrap task incomplete | bootstrap --task <id> [--client <id>] |
| Skill install missing or stale | sync [--client <id>] |
| CLAUDE.md out of date | context [--client <id>] |
5. Verify after fixing: doctor --format json [--client <id>]
Workflow: Service Lifecycle
python3 .env-manager/manage.py up --client <id> [--service <id>]
python3 .env-manager/manage.py status --client <id> --format json
python3 .env-manager/manage.py logs --service <id> --lines 100
python3 .env-manager/manage.py restart --client <id> --service <id>
python3 .env-manager/manage.py down --client <id> [--service <id>]
Workflow: Box Lifecycle (DigitalOcean + Tailscale)
Create and destroy full infrastructure from the operator's machine using scripts/box.py.
Provisioning Credential Preflight
Before any real box up or operator_provision run, dry-run and inspect
credential_status:
python3 scripts/box.py up <client-id> --profile dev-small --dry-run --format json
If credential_status.ready is false, stop before provisioning and give the
operator this unblock packet:
I am blocked before provisioning because these operator-machine credentials are unset:
- SKILLBOX_DO_TOKEN
- SKILLBOX_DO_SSH_KEY_ID
- SKILLBOX_TS_AUTHKEY
Please add the missing KEY=value lines to .env.box at the skillbox repo root,
or export them in the shell that launches the agent:
SKILLBOX_DO_TOKEN=<DigitalOcean API token with droplet/volume/SSH-key access>
SKILLBOX_DO_SSH_KEY_ID=<DigitalOcean SSH key ID>
SKILLBOX_TS_AUTHKEY=<Tailscale auth key>
Then rerun:
python3 scripts/box.py up <client-id> --profile dev-small --dry-run --format json
SKILLBOX_DO_SSH_KEY_ID is the DigitalOcean SSH key ID, not the local public
key text. The operator can find it with doctl compute ssh-key list --format ID,Name,Fingerprint or in the DigitalOcean UI. SKILLBOX_TS_AUTHKEY must come
from the Tailscale admin console. Do not invent, echo, or commit these values;
.env.box is local operator state.
python3 scripts/box.py profiles --format json
python3 scripts/box.py up <client-id> --profile dev-small --dry-run --format json
python3 scripts/box.py up <client-id> --profile dev-small [--blueprint <name>] [--set KEY=VALUE] --format json
python3 scripts/box.py status <client-id> --format json
python3 scripts/box.py ssh <client-id>
python3 scripts/box.py down <client-id> --format json
python3 scripts/box.py list --format json
The dry-run returns credential_status.required, configured, and missing.
Missing credentials mean real provisioning is blocked until .env.box or the
agent-launching shell contains the required values.
box status includes phone_url / browser_url, magicdns_url, and
network_checks for public SSH, Tailnet ping, MagicDNS resolution, and port
reachability. In text mode, surface the line beginning Open this on phone:
when the operator wants to test from a phone browser.
Remote first-box acceptance runs a skill-availability gate after sync. Treat a
failure there as a hard blocker: declared runtime skills did not install into
the managed Claude/Codex skill roots and the agent session will not reliably
recognize the expected $skill invocations.
Remote boxes are marked with SKILLBOX_BOX_SELF=true, SKILLBOX_BOX_ID, and
the Tailscale hostname/IP in .env so in-box status surfaces can identify
self-targets instead of treating the same VPS as a separate remote launch
target.
Box profiles live in workspace/box-profiles/*.yaml and declare region, size, image, and ssh user.
Safety
- Always
--dry-run first for sync, up, down, restart, bootstrap, client-init, onboard, and box up/down.
- Always scope with
--client and --service when targeting a specific project or service. Never run unscoped down or restart unless the user explicitly asks to stop everything.
- Confirm with the user before
down (stops running processes), restart, box down (destroys infrastructure), or any edit to runtime YAML or overlay files.
- Never edit
.env without explicit user approval -- it may contain secrets or host-specific values.
- Use
--format json for inspection commands when you need to parse output programmatically. Use text format when showing results directly to the user.
- Read logs before escalating errors -- the answer is usually in the service output.
- Client IDs must be lowercase alphanumeric with single hyphens:
my-project, acme-api, personal.