Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 32 additions & 51 deletions .agents/skills/godaddy-cli/SKILL.md

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This skill is for the TypeScript CLI; we're not changing that, so we don't need this to be updated.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ tags: [godaddy, cli, commerce, applications, deploy]

# Using the GoDaddy CLI

The `godaddy` CLI is an agent-first tool. Every command returns a single JSON envelope to stdout. There is no plain text mode, no `--json` flag, and no table output. Parse stdout as JSON.
The `godaddy` CLI is an agent-first tool. Regular executable commands in JSON mode return a single GoDaddy JSON envelope to stdout. Help and version output remain text, non-JSON formats pass through in their native shape, and streaming commands are outside the regular envelope adapter.

## Quick Start

Expand All @@ -32,18 +32,21 @@ godaddy application info <name>

## Output Contract

### Every response is JSON
### Regular command responses are JSON

Every command writes exactly one JSON object to stdout followed by a newline. Parse it directly. Debug/verbose messages go to stderr only.
Regular commands write one JSON object to stdout followed by a newline. This includes error envelopes, which are written to stdout while retaining a non-zero exit code. Help, version, and non-JSON diagnostics retain their text format and original stream.

### Success

```json
{
"ok": true,
"command": "godaddy application list",
"result": { ... },
"next_actions": [ ... ]
"command": "gddy env get",
"result": {
"apiUrl": "https://api.godaddy.com",
"env": "prod"
},
"next_actions": []
}
```

Expand All @@ -52,19 +55,16 @@ Every command writes exactly one JSON object to stdout followed by a newline. Pa
```json
{
"ok": false,
"command": "godaddy application info demo",
"command": "gddy env get",
"error": {
"message": "Application 'demo' not found",
"code": "NOT_FOUND"
"message": "unknown environment \"not-an-environment\"; known: ote, prod",
"code": "ERROR"
},
"fix": "Use discovery commands such as: godaddy application list or godaddy actions list.",
"next_actions": [ ... ]
"next_actions": []
}
```

Check `ok` first. On failure, read `error.code` for programmatic handling and `fix` for the suggested recovery step.

Error codes: `NOT_FOUND`, `AUTH_REQUIRED`, `VALIDATION_ERROR`, `NETWORK_ERROR`, `CONFIG_ERROR`, `SECURITY_BLOCKED`, `COMMAND_NOT_FOUND`, `UNSUPPORTED_OPTION`, `UNEXPECTED_ERROR`.
Check `ok` first. On failure, read `error.code` and `error.message`; error codes are command-specific.

### next_actions (HATEOAS)

Expand Down Expand Up @@ -124,12 +124,14 @@ If `truncated` is `true`, the complete data is at the `full_output` file path. R

| Flag | Alias | Effect |
|------|-------|--------|
| `--pretty` | | Pretty-print JSON with 2-space indentation |
| `--env <env>` | `-e` | Override environment for this command (`ote` or `prod`) |
| `--verbose` | `-v` | Log HTTP requests/responses to stderr |
| `--debug` | `-vv` | Full verbose output to stderr |
| `--env <env>` | | Override environment for this command (`ote` or `prod`) |
| `--debug` | | Enable debug logging on stderr |
| `--output <format>` | `-o` | Select `json`, `human`, or `toon` output |
| `--json` | | Shorthand for `--output json` |
| `--human` | | Shorthand for `--output human` |
| `--toon` | | Shorthand for `--output toon` |

These can appear anywhere in the command. They do not affect the JSON structure — only formatting and stderr diagnostics.
These options can appear anywhere in the command. The GoDaddy adapter applies to regular commands only when cli-engine renders JSON. JSON envelopes use two-space indentation by default; `--pretty` is not registered.

## Discovery

Expand Down Expand Up @@ -246,11 +248,8 @@ All `add` commands accept `--config <path>` and `--environment <env>` to target
godaddy application release <name> --release-version 1.0.0
godaddy application release <name> --release-version 1.0.0 --description "Initial release"

# Deploy
# Deploy (this command streams progress)
godaddy application deploy <name>

# Deploy with streaming progress (NDJSON)
godaddy application deploy <name> --follow
```

Release and deploy accept `--config <path>` and `--environment <env>`.
Expand Down Expand Up @@ -321,28 +320,10 @@ Returns up to 50 events inline; use `full_output` path for the complete list (19

## NDJSON Streaming

When `--follow` is used (currently on `deploy`), output is multiple JSON lines instead of one envelope. Each line has a `type` field:

```
{"type":"start","command":"godaddy application deploy my-app --follow","ts":"..."}
{"type":"step","name":"security-scan","status":"started","ts":"..."}
{"type":"step","name":"security-scan","status":"completed","ts":"..."}
{"type":"step","name":"bundle","status":"started","extension_name":"my-widget","ts":"..."}
{"type":"progress","name":"bundle","percent":50,"ts":"..."}
{"type":"step","name":"bundle","status":"completed","ts":"..."}
{"type":"result","ok":true,"command":"...","result":{...},"next_actions":[...]}
```

The **last line is always terminal** (`type: "result"` or `type: "error"`). It has the same shape as a standard envelope. If you only care about the final outcome, read the last line.

Stream event types:
| Type | Meaning | Terminal? |
|------|---------|-----------|
| `start` | Stream begun | No |
| `step` | Step lifecycle (started/completed/failed) | No |
| `progress` | Progress update (percent, message) | No |
| `result` | Success envelope | Yes |
| `error` | Error envelope | Yes |
Streaming commands are not adapted by the regular-command output envelope.
Their terminal `result` and `error` event contract is tracked separately and is
not guaranteed by the current binary. Do not assume the final line has the
regular envelope shape.

## Typical Workflows

Expand All @@ -361,7 +342,7 @@ godaddy application add action --name my-action \ # 4. Add action
godaddy application validate my-app # 5. Validate
godaddy application release my-app \ # 6. Release
--release-version 1.0.0
godaddy application deploy my-app --follow # 7. Deploy
godaddy application deploy my-app # 7. Deploy
godaddy application enable my-app --store-id <storeId> # 8. Enable
```

Expand All @@ -373,7 +354,7 @@ godaddy application update my-app --description "New" # 2. Update
godaddy application validate my-app # 3. Validate
godaddy application release my-app \ # 4. Bump version
--release-version 1.1.0
godaddy application deploy my-app --follow # 5. Deploy
godaddy application deploy my-app # 5. Deploy
```

### Diagnose failures
Expand All @@ -388,10 +369,10 @@ godaddy application info <n> # 5. App status?

## Parsing Tips

1. **Always parse stdout as JSON.** The only non-JSON output is `--help` text.
1. **Parse regular command stdout as JSON when using JSON mode.** Help, version, and explicitly selected non-JSON formats are not GoDaddy envelopes.
2. **Check `ok` first.** Branch on `true`/`false` before reading `result` or `error`.
3. **Use `next_actions`** to discover what to do next. Fill template params from context.
4. **Exit code**: 0 = success, 1 = error. But always prefer the JSON `ok` field.
5. **stderr is diagnostic only.** Verbose/debug output goes there. Never parse stderr for data.
4. **Exit code**: 0 = success, non-zero = error. Check the JSON `ok` field as well.
5. **Error envelopes use stdout.** stderr is reserved for tracing and non-envelope diagnostics.
6. **Truncated lists**: check `truncated` field. Read `full_output` file for complete data.
7. **Streaming**: for `--follow` commands, parse each line as an independent JSON object. The last line is the final result.
7. **Streaming**: do not assume a terminal public envelope until that separate contract is implemented.
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,26 @@ platform (`gddy.exe` on Windows). If you'd rather install by hand, download the

## Output Contract

All executable commands emit JSON envelopes:
Regular executable commands in JSON mode emit GoDaddy JSON envelopes:

```json
{"ok":true,"command":"gddy env get","result":{"environment":"ote"},"next_actions":[...]}
{"command":"gddy env get","next_actions":[],"ok":true,"result":{"apiUrl":"https://api.godaddy.com","env":"prod"}}
```

```json
{"ok":false,"command":"gddy application info demo","error":{"message":"Application 'demo' not found","code":"NOT_FOUND"},"fix":"Use discovery commands such as: gddy application list or gddy actions list.","next_actions":[...]}
{"command":"gddy env get","error":{"code":"ERROR","message":"unknown environment \"not-an-environment\"; known: ote, prod"},"next_actions":[],"ok":false}
```

`--help` remains standard CLI help text.
`--output` has been removed; all executable command paths return JSON envelopes.
Use `--pretty` to format envelopes with 2-space indentation for human readability.
Long-running operations can stream typed NDJSON events with `--follow`, ending with a terminal `result` or `error` event.
Regular command envelopes, including errors, are written to stdout. Failed
commands retain a non-zero exit code. `--help` and `--version` remain standard
CLI text, and non-JSON diagnostics retain their original stream.

JSON is the default for non-interactive output. Use `--output
<json|human|toon>` to select a format; `--json`, `--human`, and `--toon` are
shorthands. Human and TOON output pass through without GoDaddy JSON-envelope
adaptation. JSON envelopes use two-space indentation by default. `--pretty` is
not registered. Streaming commands are outside this regular-command adapter;
terminal `result` and `error` events are tracked separately.

## Root Discovery

Expand All @@ -61,9 +67,10 @@ Returns environment/auth snapshots and the full command tree.

## Global Options

- `-e, --env <environment>`: validate target environment (`ote`, `prod`)
- `--env <environment>`: validate target environment (`ote`, `prod`)
- `--debug`: enable debug logging (stderr only)
- `--pretty`: pretty-print JSON envelopes (2-space indentation)
- `--output <json|human|toon>`: select the output format
- `--json`, `--human`, `--toon`: output-format shorthands

## Commands

Expand Down Expand Up @@ -95,7 +102,7 @@ Returns environment/auth snapshots and the full command tree.
- `gddy application init [--name <name>] [--description <description>] [--url <url>] [--proxy-url <proxyUrl>] [--scopes <scopes>] [--config <path>] [--environment <env>]`
- `--url` and `--proxy-url` must be publicly-resolvable `http(s)` URLs. `localhost`, loopback (`127.0.0.1`, `::1`), link-local, and RFC1918 private IPs are rejected. For local development, expose a tunnel (e.g. [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/), [ngrok](https://ngrok.com/)) and register the tunnel hostname.
- `gddy application release <name> --release-version <version> [--description <description>] [--config <path>] [--environment <env>]`
- `gddy application deploy <name> [--config <path>] [--environment <env>] [--follow]`
- `gddy application deploy <name> [--config <path>] [--environment <env>]`

#### Application Add

Expand Down
48 changes: 46 additions & 2 deletions rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod environments;
mod extension;
mod hosting;
mod next_action;
mod output_envelope;
mod output_schema;
mod pat;
mod payments;
Expand All @@ -19,7 +20,7 @@ mod scopes;
mod update;
mod webhook;

use std::{process::ExitCode, sync::Arc};
use std::{ffi::OsString, io::Write, process::ExitCode, sync::Arc};

use clap::Arg;
use cli_engine::{BuildInfo, Cli, CliConfig};
Expand Down Expand Up @@ -112,5 +113,48 @@ async fn main() -> ExitCode {
.with_module(webhook::module()),
);

cli.execute().await
let args = std::env::args_os().collect::<Vec<OsString>>();
let command = output_envelope::command_path(cli.root_command(), &args);
let mut engine_stdout = Vec::new();
let mut engine_stderr = Vec::new();

let exit_code = match cli
.execute_from(args, &mut engine_stdout, &mut engine_stderr)
.await
{
Ok(code) => code,
Err(error) => {
tracing::error!(%error, "failed to execute command");
return ExitCode::from(1);
}
};

let is_success = exit_code == ExitCode::SUCCESS;
let rendered = if is_success {
&engine_stdout
} else {
&engine_stderr
};
let rendered = String::from_utf8_lossy(rendered);

if let Some(adapted) = output_envelope::adapt(&rendered, &command, is_success) {
if std::io::stdout()
.lock()
.write_all(adapted.as_bytes())
.is_err()
{
return ExitCode::from(1);
}
} else {
let write_result = if is_success {
std::io::stdout().lock().write_all(&engine_stdout)
} else {
std::io::stderr().lock().write_all(&engine_stderr)
};
if write_result.is_err() {
return ExitCode::from(1);
}
}

exit_code
}
Loading