JSON envelope
Scribe targets AI coding agents as a first-class consumer. Every migrated command emits a versioned envelope on stdout (or stderr on error) so an agent can parse the result without scraping human-formatted text.
Scribe targets AI coding agents as a first-class consumer. Every migrated command emits a versioned envelope on stdout (or stderr on error) so an agent can parse the result without scraping human-formatted text.
Envelope shape
{
"status": "ok",
"format_version": "1",
"data": { /* command payload */ },
"meta": {
"duration_ms": 12,
"bootstrap_ms": 3,
"command": "scribe sync",
"scribe_version": "dev"
}
}Status values:
ok— fully succeededpartial_success— some items failed; inspectdata.summary.failedor item-level errors. Exit code is10.error— full failure. Body lives undererror.{code,message,retryable,remediation,exit_code}instead ofdata.
format_version lets parsers reject envelopes they don’t understand. The current schema is "1". Breaking shape changes will bump it.
meta.duration_ms measures leaf RunE execution. meta.bootstrap_ms covers first-run, store migration, builtins apply, and embedded-agent refresh work that ran before the leaf command.
Detection
Scribe auto-detects non-TTY environments. When stdout is not a TTY (piped, redirected, captured by a subprocess), commands emit JSON by default. Pass --json to force it even in interactive shells. CI=true also forces JSON.
scribe list # JSON when piped, TUI on TTY
scribe list --json # always JSON
CI=true scribe list # always JSONLive examples
scribe list --json (skill array trimmed for brevity):
{
"status": "ok",
"format_version": "1",
"data": {
"packages": [
{ "name": "superpowers", "revision": 1, "path": "/Users/me/.scribe/packages/superpowers", "sources": ["obra/superpowers"] }
],
"skills": [
{
"name": "add-init",
"description": "Create a new /init-* command.",
"revision": 1,
"content_hash": "e42bc8ef",
"targets": ["claude", "codex", "cursor", "gemini"],
"managed": true,
"path": "/Users/me/.scribe/skills/add-init"
}
]
},
"meta": {
"duration_ms": 478,
"bootstrap_ms": 6,
"command": "scribe list",
"scribe_version": "dev"
}
}scribe sync --json with one failed install:
{
"status": "partial_success",
"format_version": "1",
"data": {
"reconcile": { "installed": 2, "relinked": 0, "removed": 2, "conflicts_count": 0 },
"summary": { "failed": 1, "installed": 0, "skipped": 73, "updated": 0 }
},
"meta": {
"duration_ms": 8999,
"bootstrap_ms": 4,
"command": "scribe sync",
"scribe_version": "dev"
}
}scribe status --json:
{
"status": "ok",
"format_version": "1",
"data": {
"version": "dev",
"registries": ["Artistfy/hq", "anthropics/skills", "Naoray/skills"],
"installed_count": 129,
"last_sync": "2026-04-30T10:10:47Z"
},
"meta": { "duration_ms": 2, "bootstrap_ms": 3, "command": "scribe status", "scribe_version": "dev" }
}Exit codes
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | general operational failure |
| 2 | usage or invalid flags |
| 3 | not found (command, registry, skill, schema) |
| 4 | permission or authentication failure |
| 5 | conflict requiring user action |
| 6 | network or remote service failure |
| 7 | temporarily unavailable local dependency |
| 8 | validation failure |
| 9 | user canceled |
| 10 | partial success — inspect data.summary.failed or item-level errors |
Field projection (--fields)
Read-only tabular commands accept --fields name,version,... to project specific columns. Fields not listed in the schema are silently ignored. Use scribe schema <command> --json | jq '.data.output_schema' to enumerate available fields before calling.
scribe list --json --fields name,managed,targetsSchema introspection
Scribe ships JSON Schema (Draft 2020-12) for every migrated command’s input flags and output payload.
scribe schema list --json
scribe schema --all --json | jq 'keys' # list migrated commands
scribe schema sync --json | jq '.data.output_schema'Use the schema before composing calls so flag typos and shape drift fail loudly.
Pre-envelope commands
Some wave-3 commands still reject --json with the error JSON_NOT_SUPPORTED and exit code 2. Their pre-envelope shape is unchanged. The remediation field points at the relevant migration todo so you can track when they land.
{
"status": "error",
"format_version": "1",
"error": {
"code": "JSON_NOT_SUPPORTED",
"message": "scribe registry list does not support --json yet",
"retryable": false,
"remediation": "scribe schema --all --json | jq 'keys' lists JSON-capable commands",
"exit_code": 2
},
"meta": {}
}Migration notes
Previously top-level keys are now nested under data:
jq '.skills'→jq '.data.skills'jq '.summary'→jq '.data.summary'jq '.installed_count'→jq '.data.installed_count'
status becomes "partial_success" when data.summary.failed > 0. Pair the status check with the exit code (10) for double safety in shell pipelines.
Spec deviations
--fields name,versionis a separate flag rather than overloaded--json name,version. Keeps boolean--json=truecompatibility intact.meta.duration_msis leaf execution only;meta.bootstrap_msis everything before that. Sum them for wall time.