Skip to content
Merged
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
50 changes: 38 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,42 @@ ep-1 production active ethereum/mainnet shared yes https://ep-1.examp
ep-2 — paused solana/mainnet dedicated no https://ep-2.example —
showing 1–2 of 2

# LLM-optimized TOON format (non-TTY default)
# Piped / non-TTY output defaults to JSON
$ qn endpoint list | cat
data[2]{id,name,label,status,chain,network,is_dedicated,is_flat_rate,http_url,wss_url,tags,is_multichain}:
"ep-1","ep-1","production",active,ethereum,mainnet,false,false,"https://ep-1.example",null,"prod, eu",false
"ep-2","ep-2",null,paused,solana,mainnet,true,false,"https://ep-2.example",null,"",false
pagination:
total: 2
limit: 20
offset: 0
error: null
{
"data": [
{
"id": "ep-1",
"name": "ep-1",
"label": "production",
"status": "active",
"chain": "ethereum",
"network": "mainnet",
"is_dedicated": false,
"is_flat_rate": false,
"http_url": "https://ep-1.example",
"wss_url": null,
"tags": ["prod", "eu"],
"is_multichain": false
},
{
"id": "ep-2",
"name": "ep-2",
"label": null,
"status": "paused",
"chain": "solana",
"network": "mainnet",
"is_dedicated": true,
"is_flat_rate": false,
"http_url": "https://ep-2.example",
"wss_url": null,
"tags": [],
"is_multichain": false
}
],
"pagination": { "total": 2, "limit": 20, "offset": 0 },
"error": null
}
```

## Installation
Expand Down Expand Up @@ -139,10 +165,10 @@ Pick a format with `--format <FMT>` (alias `-o <FMT>`):
| `--format` | Best for |
| --- | --- |
| `table` | Humans on a TTY. Pretty UTF-8 tables with optional color. Default when stdout is a terminal. |
| `json` | Scripts and pipelines (`jq`, `gron`, …). |
| `json` | Scripts and pipelines (`jq`, `gron`, …). Default when stdout is **not** a terminal (piped / agent invocations). |
| `yaml` | Same shape as JSON, easier to skim by eye. |
| `md` | GitHub-flavored markdown — paste into PRs, issues, docs. |
| `toon` | [Token-Oriented Object Notation](https://github.com/toon-format/toon-rust) — compact serialization optimized for LLM prompts. Default when stdout is **not** a terminal (piped / agent invocations). |
| `toon` | [Token-Oriented Object Notation](https://github.com/toon-format/toon-rust) — compact serialization optimized for LLM prompts. |

Other output flags:

Expand All @@ -159,7 +185,7 @@ format = "yaml" # default --format value
wide = true # always show extra columns in table/md output
```

CLI flags win over config values. Built-in defaults: `format = "table"` when stdout is a TTY, `"toon"` otherwise; `wide = false`.
CLI flags win over config values. Built-in defaults: `format = "table"` when stdout is a TTY, `"json"` otherwise; `wide = false`.

`qn` follows the [Command Line Interface Guidelines](https://clig.dev/): data on stdout, diagnostics on stderr, meaningful exit codes (0 success, 2 API error, 3 network error, 4 auth/config, 5 needs confirmation), and a documented `-h`/`--help` at every subcommand level.

Expand Down
5 changes: 3 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ pub struct Cli {
#[arg(long, global = true, value_name = "PATH")]
pub config_file: Option<std::path::PathBuf>,

/// Output format. `table` is the default human view; the others are
/// Output format. `table` is the human view; the others are
/// pipeline-friendly serialized forms. If unset, falls back to the
/// `[output] format = "…"` value in ~/.config/qn/config.toml, then `table`.
/// `[output] format = "…"` value in ~/.config/qn/config.toml, then the
/// TTY-aware default: `table` when stdout is a terminal, `json` otherwise.
#[arg(short = 'o', long = "format", global = true, value_enum)]
pub format: Option<Format>,

Expand Down
4 changes: 2 additions & 2 deletions src/commands/agent/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ network call.

## 2. Output contract

- Default format is `table` on a TTY and **`toon`** when stdout is not a TTY (piped).
- Default format is `table` on a TTY and **`json`** when stdout is not a TTY (piped).
- Data goes to **stdout**; diagnostics, prompts, and ✓ confirmations go to **stderr**.
- Formats: `table`, `md`, `json`, `yaml`, `toon`. The structured forms
(`json`/`yaml`/`toon`) always include every field — `--wide` is not needed and
Expand Down Expand Up @@ -169,7 +169,7 @@ qn kv set list

- Mutations are never retried; re-running a failed create can double-provision (§5).
- No account-wide wipe command exists by design (§4).
- Piped output defaults to `toon`, not `json` (§2).
- Piped output defaults to `json`; pass `-o toon` for the compact LLM form (§2).
- `--base-url` overrides the API host; it exists for testing.
- For *this* command, `-o yaml`/`-o toon`/`-o table` print Markdown (with a note on
stderr); `-o json` produces the `{version, guide}` envelope.
Expand Down
16 changes: 11 additions & 5 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub struct GlobalArgs {
/// (`~/.config/qn/config.toml`).
pub config_file: Option<std::path::PathBuf>,
/// `None` means the user didn't pass `--format`; resolve via config file
/// (then the TTY-aware default: `Table` on a TTY, `Toon` off) when we
/// (then the TTY-aware default: `Table` on a TTY, `Json` off) when we
/// build the [`Ctx`].
pub format: Option<Format>,
pub wide: bool,
Expand All @@ -38,7 +38,7 @@ pub struct GlobalArgs {

impl GlobalArgs {
/// Resolve the output format: CLI flag > config file > TTY-aware default
/// (`Table` on a TTY, `Toon` off).
/// (`Table` on a TTY, `Json` off).
/// Used by [`Ctx::from_global`] and `auth` (which doesn't build a Ctx).
pub fn resolve_format(&self, stdout_is_tty: bool) -> Format {
self.resolve_output(stdout_is_tty).0
Expand All @@ -47,7 +47,7 @@ impl GlobalArgs {
/// Resolve `(format, wide)` together so we only read the config file once.
///
/// For each: CLI flag > config file > built-in default. The format default
/// is TTY-aware: `Table` when stdout is a terminal, `Toon` otherwise (so
/// is TTY-aware: `Table` when stdout is a terminal, `Json` otherwise (so
/// agents / piped callers get a structured format by default). `--wide` is
/// purely additive — the flag sets it true; the config file can also set
/// it true; otherwise it's false.
Expand Down Expand Up @@ -85,7 +85,7 @@ fn resolve_output_inner(
let format = flag_format.or(cfg_format).unwrap_or(if stdout_is_tty {
Format::Table
} else {
Format::Toon
Format::Json
});
let wide = flag_wide || cfg_wide;
(format, wide)
Expand Down Expand Up @@ -240,8 +240,14 @@ mod tests {
}

#[test]
fn default_is_toon_when_stdout_is_not_a_tty() {
fn default_is_json_when_stdout_is_not_a_tty() {
let (f, _) = resolve_output_inner(None, false, None, false, false);
assert_eq!(f, Format::Json);
}

#[test]
fn config_toon_overrides_non_tty_default() {
let (f, _) = resolve_output_inner(None, false, Some(Format::Toon), false, false);
assert_eq!(f, Format::Toon);
}

Expand Down
2 changes: 1 addition & 1 deletion src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! Five formats, selected by the global `--format/-o` flag. When the flag and
//! the config file both leave the format unset, the default is TTY-aware:
//! `table` when stdout is a terminal (interactive use), `toon` otherwise
//! `table` when stdout is a terminal (interactive use), `json` otherwise
//! (piped / agent invocations). See [`crate::context::GlobalArgs::resolve_output`].
//!
//! - `table`: comfy-table with UTF-8 borders for humans on a TTY.
Expand Down
Loading