Skip to main content

What's new in CLI v2.1

dci v2.1 teaches the CLI your resource names: reports, budgets, and allocations can now be addressed — and tab-completed — by name instead of ID, and the completion scripts ship in every install channel. The v2.1.1 patch hardens completion with a hard guarantee: a Tab press can never touch the network or open a browser. v2.1 builds on the v2.0 ground-up refresh, summarized below.

Call resources by name

Anywhere a command takes a report, budget, or allocation ID, a name now works — quoted or not:

dci get-report "Monthly AWS Spend" # exact, case-insensitive, or unique-substring match
dci get-budget Team Backyard # quoting optional — the words are rejoined
dci open report Monthly AWS Spend # open, update-*, delete-* accept names too

Matching is forgiving but never guesses: exact name first, then case-insensitive, then a unique substring, then close typos. IDs still work everywhere and skip the lookup entirely.

  • Ambiguity is an error, not a guess. If several resources share the name, the command fails (exit 2) listing every match as name (id) — re-run with the exact ID.
  • Unmatched names point you at the data. NAME_NOT_FOUND (exit 20) names the list-* command to browse.
  • Destructive commands confirm the true target. dci delete-report "old draft" resolves first, and the confirmation shows the resolved name and ID — a fuzzy match can never delete the wrong thing silently.

When you need precise control:

dci get-report --id <value> # treat the argument as a literal ID, skip the lookup
dci get-report --name <value> # force a name lookup even if it looks like an ID
DCI_NO_RESOLVE=1 dci ... # disable resolution entirely (for scripts)

Tab completes your data

Shell completion now offers your actual resource names, not just commands and flags:

$ dci get-report Mon<TAB>
Monthly AWS Spend Monthly GCP Spend

Names are served from a local cache that refreshes in the background — the Tab path itself never waits on the network. The first Tab in a fresh session tells you what's happening instead of staying silent, and the notice matches your session state: a warming cache says press Tab again in a few seconds, an expired session points at dci validate, and a signed-out CLI points at dci login. Set DCI_ACTIVE_HELP=0 to suppress the notices.

Type names bare when completing — the shell escapes spaces for you (dci get-report Monthly\ AWS\ Spend); completing inside an open quote is not supported by shell completion frameworks.

Completion installs itself now. Homebrew and the Linux .deb/.rpm packages install the bash, zsh, and fish scripts automatically; Scoop prints the one PowerShell profile line to add (WinGet users use the same line). Manual setup via source <(dci completion zsh) also works now — the generated script registers itself when sourced, which it previously did not.

Hardened in v2.1.1

  • A Tab press can never open a browser or touch the network. Completion requests that can't be answered from local caches now return a short notice instead of falling through to a network fetch — previously, an expired local cache could escalate a Tab press all the way to an interactive sign-in.
  • Partial command names complete. dci st<TAB> completes status.
  • get-anomaly works with its UUID-shaped IDs — v2.1.0's name resolution mistook them for names and failed the lookup.
  • Multi-word names work unquoted in dci open, matching the other commands.

Earlier: the v2.0 refresh

v2.0 was a ground-up refresh of the CLI experience for its two audiences: humans reading cloud costs in a terminal and AI agents driving the CLI programmatically. For the full gallery of terminal input/output comparisons, see CLI v2.0: before and after.

One breaking change for scripts (v1.x → v2.x)

v2.0 unifies exit codes across all modes. Scripts that check $? != 0 are unaffected; scripts that branch on specific codes need the new taxonomy. Details in For scripts below.

For humans

Reports render as reports

Report results used to print as a flat database dump — one row per group × time period, with raw floats and epoch timestamps. In v2.0 the interactive table view pivots by default: groups as rows, time as columns, ranked by spend, with totals, a first→last trend column, and amounts carrying the report's currency sign, rounded to whole units.

Before (v1.x):

$ dci query < monthly.json
║ cost │ month │ service_description │ timestamp │ year ║
║ 239927.13841529994 │ 05 │ analytics │ 1.7462e+09 │ 2026 ║
║ 291018.65484701947 │ 06 │ analytics │ 1.7488e+09 │ 2026 ║
║ 345927.05937314965 │ 07 │ analytics │ 1.7514e+09 │ 2026 ║
║ 48875.47185098996 │ 05 │ pipelines │ 1.7462e+09 │ 2026 ║
...12 interleaved rows...

After (v2.0):

$ dci query < monthly.json
║ service_description │ 2026-05 │ 2026-06 │ 2026-07 │ total │ trend ║
║ analytics │ $239,927 │ $291,019 │ $345,927 │ $876,873 │ +44% ║
║ pipelines │ $48,875 │ $70,157 │ $95,806 │ $214,839 │ +96% ║
║ storage │ $83,219 │ $70,721 │ $53,925 │ $207,865 │ -35% ║
║ TOTAL │ $521,554 │ $567,513 │ $638,837 │ $1,727,905 │ +22% ║

In interactive terminals the pivot also shades period cells by magnitude — a heatmap that makes hot spots and movers readable at a glance (NO_COLOR or --heatmap=false disables it). Prefer the old layout? --flat restores flat rows, and --raw-numbers restores exact unformatted values. Hourly reports keep the hour on every row, and ranges with too many periods to scan stay flat unless you pass --pivot. See Running queries for all the result-shaping flags.

Listings you can actually read

Wide listings (anomalies return sixteen columns) used to crush every cell into stubs and print unset values as a literal <nil>. v2.0 keeps the columns that render readably — identity (id, name) and date columns always survive — and tells you exactly what was hidden and how to get it back (-C, -M wrap, -W). Timestamps everywhere display as 2026-08-10 22:25 instead of epoch numbers or T00:00:00Z noise, budget amounts show their currency ($4,900), and an empty list simply prints No results.

Mistakes fail fast, with help

$ dci list-bugets # v1.x: 258 lines of help, exit code 0
$ dci list-bugets # v2.0:
Error: unknown command "list-bugets" (did you mean "list-budgets"?)

Interactive errors are now a single plain-text line — no more triple-printed messages with a usage dump. Setting an invalid customer context is rejected when you set it, instead of silently breaking every later command with 403s.

Jump to the console

dci open deep-links into the Cloud Intelligence™ console — dci open report <id>, budget, or allocation opens the resource in your browser (agents and pipes get the URL printed instead). dci docs prints every documentation entry point, and CSV export (--output csv) finally exists for spreadsheets.

For AI agents

v2.0 treats agents as first-class users. If you point Claude Code, Cursor, Codex, Gemini, Kiro, or opencode at dci, install the embedded skill with dci skill <agent> — it now ships a FinOps-baseline workflow (spend review → budgets → alerts → allocations) and a question-to-command map.

  • Bounded output. An unlimited grouped query could return half a megabyte of rows straight into the context window. Agent mode now caps report results at 500 rows with explicit rowsOmitted/rowsTotal markers; --max-rows 0 restores everything.
  • Terse help. --help used to embed complete request/response schemas (query --help was ~15 KB). It's now ~2.4 KB, with schemas one flag away via --help-full.
  • Honest failure semantics. Successful deletes are no longer misreported as retryable upstream errors, client errors are never marked retryable, and unknown request-body fields fail fast with the valid field list instead of being silently ignored by the API.
  • No hangs. Without credentials in CI or a sandbox, commands fail immediately with AUTHENTICATION_REQUIRED and a pointer to DCI_API_KEY instead of waiting on a browser login.
  • More context in results. Report results carry an explicit currency field when the query config specifies one, and the pivot's trend column gives agents the derived movement signal without recomputing it. --rows keyed returns rows as schema-named objects — no more zipping positional arrays. Identical queries return identical row order.

For scripts

Exit codes are stable and identical in every mode:

CodeMeaning
0Success
2Usage error (unknown command, flag, or body field)
10 / 11Authentication / permission
20 / 21Not found / conflict
30Validation error or unconfirmed destructive command
40 / 41 / 50Server error / network error / rate limited

Previously, human and pipe invocations exited 1 for most failures, 4 for 404s — and a mistyped command exited 0. If a brand-new API field is rejected against a stale cached spec, retry with --rsh-no-cache or set DCI_SKIP_BODY_VALIDATION=1. Empty collections now serialize as [] instead of null, and old command names (e.g. id-of-asset) keep working as aliases for their new names (update-asset).

Upgrade

brew update && brew upgrade dci # macOS — or winget/scoop/apt/rpm per the install guide
dci upgrade # check your version from any install
dci skill update # refresh installed agent skills after upgrading

For installation, authentication, and the complete flag reference, start at the CLI guide. The cheat sheet has the one-page command summary.