From 3cd25d963058de5c0d7e1ec32220af1e93a8ca3b Mon Sep 17 00:00:00 2001 From: Mofei Zhu Date: Tue, 29 Sep 2026 12:12:18 +0300 Subject: [PATCH 1/3] Reorganize README around what a reader does first --- README.md | 162 +++++++++++++++++++++-------------------- tests/source_guards.rs | 2 +- 2 files changed, 85 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 23a1de5..8251524 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,17 @@ mapbox styles list - [Install script](#install-script) - [Download the archive yourself](#download-the-archive-yourself) - [Build from source](#build-from-source) + - [Uninstall](#uninstall) +- [Authentication](#authentication) + - [Named profiles](#named-profiles) - [Commands](#commands) - - [Auth](#auth) - - [Named profiles](#named-profiles) - [API commands](#api-commands) - - [Agent skills](#agent-skills) - [Shell completion](#shell-completion) - - [Generate Skills](#generate-skills) - [Tileset CLI](#tileset-cli) -- [Usage](#usage) +- [For AI agents](#for-ai-agents) + - [Agent skills](#agent-skills) + - [Generate skills](#generate-skills) +- [Global options](#global-options) - [Dry runs](#dry-runs) - [Timeouts](#timeouts) - [Extra query parameters](#extra-query-parameters) @@ -32,11 +34,11 @@ mapbox styles list - [Output format](#output-format) - [`--schema`](#--schema) - [Confirmation and `--yes`](#confirmation-and---yes) +- [Updates, history and logs](#updates-history-and-logs) - [Update notices](#update-notices) - [Command history](#command-history) - [Diagnostic logs](#diagnostic-logs) - - [Privacy](#privacy) -- [Uninstall](#uninstall) +- [Privacy](#privacy) - [Contributing](#contributing) ## Install @@ -162,9 +164,22 @@ cargo build --release ./target/release/mapbox --help ``` -## Commands +### Uninstall + +```sh +mapbox uninstall +``` + +Removes only the `mapbox` binary. Credentials, profiles, and the separate +`tilesets` binary are untouched. Run +[`mapbox auth logout`](./docs/commands.md#mapbox-auth-logout) first if you +want those gone too. Asks for confirmation like any destructive command +(`--yes`/`MAPBOX_YES` skips it); `--dry-run` previews without deleting. + +Installed with Homebrew? Use `brew uninstall mapbox` instead, so Homebrew +knows it is gone. -### Auth +## Authentication ```sh mapbox auth login # opens a browser (OAuth/PKCE) @@ -180,7 +195,7 @@ permissions. There is no OS keychain integration. Override them with `MAPBOX_CONFIG_DIR` to move the whole store, which a container usually wants. -#### Named profiles +### Named profiles `--profile ` keeps a separate credential set per account: @@ -190,6 +205,8 @@ mapbox --profile android_app styles list mapbox auth profiles # which profiles are actually stored ``` +## Commands + ### API commands Each API is a top-level subcommand, one sub-subcommand per operation: @@ -230,6 +247,57 @@ Every request sends `User-Agent: mapbox-cli/` and nothing else about you or your machine. `MAPBOX_CLI_NO_TELEMETRY=1` keeps even future markers out of that header. +### Shell completion + +```sh +mapbox completion bash | zsh | fish | powershell +``` + +Prints a completion script on stdout. Nothing is written to disk, so put it +where your shell looks: + +```sh +mapbox completion bash > ~/.local/share/bash-completion/completions/mapbox +mapbox completion zsh > ~/.zfunc/_mapbox # a directory on $fpath +mapbox completion fish > ~/.config/fish/completions/mapbox.fish +source <(mapbox completion bash) # this shell only +``` + +```powershell +mapbox completion powershell >> $PROFILE +``` + +It completes commands, subcommands and flag names, generated from this +binary's own command tree. So it matches the build that printed it, and +nothing about it is maintained by hand. Values (style ids, usernames) are not +completed: that would mean an API request mid-keystroke. `--output` does not +apply, because the script is the result. + +### Tileset CLI + +```sh +mapbox tilesets-cli list +mapbox tilesets-cli upload-source data.geojson.ld +``` + +Forwards everything to the separately-installed [Tilesets +CLI](https://github.com/mapbox/tilesets-cli): + +```sh +pipx install mapbox-tilesets # Python 3.10+ +``` + +`--output` and `--yes` don't apply here. `tilesets` has its own flags, so use +`--force`/`-f` for its prompts. It needs a token too: `mapbox auth login` +covers it, or pass `--token`/`MAPBOX_ACCESS_TOKEN` the same way as every +other command. `mapbox auth whoami` shows which one is in play if a +tileset command answers for the wrong account. + +## For AI agents + +Two commands, for two different jobs: `agent-skills` installs guidance on +using Mapbox, and `generate-skills` writes a skill describing this CLI. + ### Agent skills ```sh @@ -270,36 +338,7 @@ There's no lock file: one tarball arrives before any record could be consulted, so comparing bytes answers exactly and leaves no state to keep in step with another tool's. -Different command from [`generate-skills`](#generate-skills) below, which -writes a skill describing *this CLI*. These are about using Mapbox. - -### Shell completion - -```sh -mapbox completion bash | zsh | fish | powershell -``` - -Prints a completion script on stdout. Nothing is written to disk, so put it -where your shell looks: - -```sh -mapbox completion bash > ~/.local/share/bash-completion/completions/mapbox -mapbox completion zsh > ~/.zfunc/_mapbox # a directory on $fpath -mapbox completion fish > ~/.config/fish/completions/mapbox.fish -source <(mapbox completion bash) # this shell only -``` - -```powershell -mapbox completion powershell >> $PROFILE -``` - -It completes commands, subcommands and flag names, generated from this -binary's own command tree. So it matches the build that printed it, and -nothing about it is maintained by hand. Values (style ids, usernames) are not -completed: that would mean an API request mid-keystroke. `--output` does not -apply, because the script is the result. - -### Generate Skills +### Generate skills ```sh mapbox generate-skills @@ -321,29 +360,9 @@ That removes every copy this command wrote, which is more than deleting the directories by hand usually catches — a default run writes for each agent on the machine, not just the one you had in mind. -### Tileset CLI - -```sh -mapbox tilesets-cli list -mapbox tilesets-cli upload-source data.geojson.ld -``` - -Forwards everything to the separately-installed [Tilesets -CLI](https://github.com/mapbox/tilesets-cli): - -```sh -pipx install mapbox-tilesets # Python 3.10+ -``` - -`--output` and `--yes` don't apply here. `tilesets` has its own flags, so use -`--force`/`-f` for its prompts. It needs a token too: `mapbox auth login` -covers it, or pass `--token`/`MAPBOX_ACCESS_TOKEN` the same way as every -other command. `mapbox auth whoami` shows which one is in play if a -tileset command answers for the wrong account. - -## Usage +## Global options -These apply globally, across every command, not just the API ones above. +These apply to every command, not just the API ones. ### Dry runs @@ -435,6 +454,8 @@ Continue? [y/N] n script deliberately run at a terminal. It does **not** apply to `auth login`, which always needs a person. +## Updates, history and logs + ### Update notices `mapbox` can't update itself, so when a newer release exists it says so once @@ -506,7 +527,7 @@ stays. A day of logs goes when that day of history does. Logging needs history: with history off it never runs, and with the `history` setting off `config set log on` refuses. -### Privacy +## Privacy **YOUR PRIVACY - COLLECTION OF TELEMETRY** @@ -548,21 +569,6 @@ For additional information on our data processing activities and your related rights, please see our Mapbox [Privacy Policy](https://www.mapbox.com/legal/privacy). -## Uninstall - -```sh -mapbox uninstall -``` - -Removes only the `mapbox` binary. Credentials, profiles, and the separate -`tilesets` binary are untouched. Run -[`mapbox auth logout`](./docs/commands.md#mapbox-auth-logout) first if you -want those gone too. Asks for confirmation like any destructive command -(`--yes`/`MAPBOX_YES` skips it); `--dry-run` previews without deleting. - -Installed with Homebrew? Use `brew uninstall mapbox` instead, so Homebrew -knows it is gone. - ## Contributing Running the tests, the lints they have to pass, versioning rules and how diff --git a/tests/source_guards.rs b/tests/source_guards.rs index 3526c4c..2b08b52 100644 --- a/tests/source_guards.rs +++ b/tests/source_guards.rs @@ -311,7 +311,7 @@ fn every_telemetry_marker_is_disclosed() { // The Privacy section alone: a marker named anywhere else in the README // is not a disclosure. let privacy = readme - .split_once("### Privacy") + .split_once("\n## Privacy\n") .expect("README.md has a Privacy section") .1; let privacy = privacy.split("\n## ").next().unwrap_or(privacy); From b27df01ff660d2b2d4cf339e7323ef0c7ca7b3df Mon Sep 17 00:00:00 2001 From: Mofei Zhu Date: Tue, 29 Sep 2026 12:20:25 +0300 Subject: [PATCH 2/3] Check README's commands against the binary The README listed four API groups (rasterarrays, static-images, static-tiles, tilequery) the binary no longer has, and never mentioned doctor, usage or the real static group. Nothing read the README, so nobody noticed. --- tests/docs_contract.rs | 140 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/tests/docs_contract.rs b/tests/docs_contract.rs index 467ac51..a8471ff 100644 --- a/tests/docs_contract.rs +++ b/tests/docs_contract.rs @@ -17,6 +17,12 @@ //! when the change is made, is whether the page has fallen behind our own //! binary. That's all this file claims to check. //! +//! README.md gets a lighter version of the same check: every `mapbox …` +//! invocation it spells must name a real command, and every top-level +//! command must appear in it. That one exists because the README's list of +//! API groups went on naming four groups the binary no longer had, and +//! nothing here read the README. +//! //! Three things it deliberately doesn't do, written down so the next //! reader doesn't have to re-derive the scope: //! @@ -378,3 +384,137 @@ fn every_flag_a_command_takes_is_named_in_its_own_section() { unmentioned.join("\n") ); } + +/// README.md, read the same way as the page. +fn readme() -> String { + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("README.md"); + std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {}: {e}", path.display())) +} + +/// Every `mapbox …` invocation in README.md, with its line number. +/// +/// Read from shell-language fenced blocks and from inline code spans. An +/// unlabeled fence is skipped: it holds printed output, like the update +/// notice's "A newer mapbox is available", which is prose, not a command. +fn readme_invocations(readme: &str) -> Vec<(usize, String)> { + const SHELLS: [&str; 3] = ["sh", "console", "powershell"]; + let mut found = Vec::new(); + let mut fence: Option = None; + + for (index, line) in readme.lines().enumerate() { + let number = index + 1; + if let Some(lang) = line.trim_start().strip_prefix("```") { + fence = match fence { + Some(_) => None, + None => Some(SHELLS.contains(&lang.trim())), + }; + continue; + } + match fence { + Some(true) => { + // `source <(mapbox completion bash)` and `$ mapbox …` both + // put something before the name, so match it mid-line. + for (at, _) in line.match_indices("mapbox ") { + let before = line[..at].chars().next_back(); + if before.is_none_or(|c| c == ' ' || c == '(') { + found.push((number, line[at..].to_string())); + } + } + } + Some(false) => {} + None => { + for (i, span) in line.split('`').enumerate() { + if i % 2 == 1 && span.starts_with("mapbox ") { + found.push((number, span.to_string())); + } + } + } + } + } + found +} + +/// A word that can be part of a command path, as opposed to a flag, a +/// placeholder, a pipe or a value like `data.geojson.ld`. +fn is_path_word(word: &str) -> bool { + !word.starts_with('-') + && word + .bytes() + .all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-') +} + +#[test] +fn every_command_the_readme_spells_exists() { + let schema = schema(); + let full: BTreeSet<&str> = commands(&schema).iter().map(name).collect(); + // Every group a command sits under, like `mapbox styles draft`, so an + // invocation may stop at a group without naming an operation. + let mut prefixes = BTreeSet::new(); + for command in &full { + let mut path = String::from("mapbox"); + for word in command.split_whitespace().skip(1) { + path = format!("{path} {word}"); + prefixes.insert(path.clone()); + } + } + + let mut unknown = Vec::new(); + for (line, invocation) in readme_invocations(&readme()) { + let mut words = invocation.split_whitespace().skip(1).peekable(); + // `mapbox --profile NAME styles list`: a leading global flag takes a + // value this check can't tell apart from a command, so the rest of + // the line isn't checked. + if words.peek().is_some_and(|word| word.starts_with('-')) { + continue; + } + let mut path = String::from("mapbox"); + for word in words.take_while(|word| is_path_word(word)) { + let longer = format!("{path} {word}"); + if prefixes.contains(&longer) { + path = longer; + } else if full.contains(path.as_str()) { + // A positional argument, like `completion bash`. + break; + } else { + unknown.push(format!("README.md:{line}: {longer}")); + break; + } + } + } + + assert!( + unknown.is_empty(), + "README.md names commands the CLI doesn't have:\n{}\n\ + `mapbox --schema` lists what it does have.{}", + unknown.join("\n"), + spec_revision_note() + ); +} + +#[test] +fn every_top_level_command_appears_in_the_readme() { + let schema = schema(); + let top_level: BTreeSet = commands(&schema) + .iter() + .filter_map(|command| { + let group = name(command).split_whitespace().nth(1)?; + Some(format!("mapbox {group}")) + }) + .collect(); + + let mentioned: BTreeSet = readme_invocations(&readme()) + .into_iter() + .filter_map(|(_, invocation)| { + let group = invocation.split_whitespace().nth(1)?; + Some(format!("mapbox {group}")) + }) + .collect(); + + let missing: Vec<&String> = top_level.difference(&mentioned).collect(); + assert!( + missing.is_empty(), + "README.md never shows these top-level commands: {missing:?}\n\ + Give each at least one example line, under Commands or wherever it \ + fits, linking to docs/commands.md for the detail." + ); +} From 4201078aa465f96254c0ad35a437015da1833997 Mon Sep 17 00:00:00 2001 From: Mofei Zhu Date: Tue, 29 Sep 2026 12:20:25 +0300 Subject: [PATCH 3/3] Fix README's command list and cut detail docs/commands.md already has --- CONTRIBUTING.md | 4 ++ README.md | 175 ++++++++++++++++++++---------------------------- 2 files changed, 76 insertions(+), 103 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49d7b78..8374056 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,6 +39,10 @@ and not a style note. and a fake server, so the suite runs offline and on a machine that has never logged in. +`scripts/test-install.sh` and `scripts/test-install.ps1` exercise the +installers end to end without touching the network, and +`scripts/test-completion.sh` does the same for the completion scripts. + Four rules the compiler holds rather than a reviewer, declared in `Cargo.toml` with the reasoning beside each: no `unsafe`, no `println!` (stdout belongs to `output::emit`, the single place `--output` is honored), diff --git a/README.md b/README.md index 8251524..44cb286 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ mapbox styles list - [Named profiles](#named-profiles) - [Commands](#commands) - [API commands](#api-commands) + - [Diagnostics and settings](#diagnostics-and-settings) - [Shell completion](#shell-completion) - [Tileset CLI](#tileset-cli) - [For AI agents](#for-ai-agents) @@ -84,11 +85,9 @@ curl -fsSL https://cli.mapbox.com/install.sh | MAPBOX_CLI_VERSION=0.3.0 sh $env:MAPBOX_CLI_VERSION = '0.3.0'; irm https://cli.mapbox.com/install.ps1 | iex ``` -`MAPBOX_INSTALL_DIR` chooses where the binary lands. - -`scripts/install.sh` and `scripts/install.ps1` here are those installers' -sources; `scripts/test-install.sh` and `scripts/test-install.ps1` exercise -them end to end without touching the network. +`MAPBOX_INSTALL_DIR` chooses where the binary lands. The scripts' sources +are [`scripts/install.sh`](./scripts/install.sh) and +[`scripts/install.ps1`](./scripts/install.ps1). ### Download the archive yourself @@ -135,15 +134,9 @@ if ((Get-FileHash -Algorithm SHA256 $artifact.file).Hash -ine $artifact.sha256) Expand-Archive $artifact.file -DestinationPath . ``` -`Invoke-WebRequest` and `Get-FileHash` rather than `curl` and `sha256sum`: -Windows PowerShell 5.1 — the edition that ships with Windows, `powershell.exe` -— aliases `curl` to `Invoke-WebRequest`, whose flags are nothing like real -curl's, so the commands above would silently run the wrong tool there even -though `curl.exe` itself has shipped in `System32` since Windows 10 1803. -PowerShell 7 (`pwsh`) dropped that alias, so `curl` there is the real thing — -but 5.1 is still what a plain "Windows PowerShell" shortcut opens on a -default install. `sha256sum` has the simpler problem: it isn't shipped at -all outside WSL or Git Bash. +This uses `Invoke-WebRequest` and `Get-FileHash` because Windows PowerShell +5.1, the edition that ships with Windows, aliases `curl` to +`Invoke-WebRequest`, and `sha256sum` isn't shipped outside WSL or Git Bash. Use `latest` in place of the version for whatever is current. Each archive holds one file, the `mapbox` executable. @@ -183,12 +176,13 @@ knows it is gone. ```sh mapbox auth login # opens a browser (OAuth/PKCE) -mapbox auth logout # removes stored credentials -mapbox auth refresh # force-refreshes the access token mapbox auth whoami # reports which token the next command will use -mapbox auth profiles # lists every stored profile, not just one +mapbox auth logout # removes stored credentials ``` +`auth refresh` and `auth profiles` complete the set; see +[docs/commands.md](./docs/commands.md#auth). + Credentials live in `~/.mapbox` as plain JSON with locked-down file permissions. There is no OS keychain integration. Override them with `--token`/`--username` or `MAPBOX_ACCESS_TOKEN`/`MAPBOX_USERNAME`, and set @@ -209,31 +203,19 @@ mapbox auth profiles # which profiles are actually stored ### API commands -Each API is a top-level subcommand, one sub-subcommand per operation: +Each Mapbox API is a command group, with one subcommand per operation: ```sh -mapbox accounts * -mapbox fonts * -mapbox geocoder * -mapbox rasterarrays * -mapbox search * -mapbox sprites * -mapbox static-images * -mapbox static-tiles * -mapbox styles * -mapbox tilequery * -mapbox tilesets * +mapbox accounts +mapbox fonts +mapbox geocoder +mapbox search +mapbox sprites +mapbox static +mapbox styles +mapbox tilesets ``` -A command group is not the same thing as a spec file: which one an operation -belongs to is decided per operation. So `sprites` and `tilesets` are each -assembled from operations declared by the Styles, Raster Tiles and Vector -Tiles specs. `mapbox tilesets` is also unrelated to `mapbox tilesets-cli`, -which proxies to the separate Python tool. - -An operation can nest one level deeper where a group reads better, as in -`mapbox styles draft get`, `draft update` and `draft delete`. - For example: ```sh @@ -241,20 +223,37 @@ mapbox styles get mapbox styles create --data '{"name": "My Style", "version": 8, ...}' ``` -[docs/commands.md](./docs/commands.md) lists every command. +Groups are assigned per operation, not per spec file, so `sprites` and +`tilesets` each collect operations from several specs. A few nest one level +deeper where that reads better, as in `mapbox styles draft get`. +`mapbox tilesets` is unrelated to [`mapbox tilesets-cli`](#tileset-cli). + +[docs/commands.md](./docs/commands.md) lists every command with its +parameters and sample output. Every request sends `User-Agent: mapbox-cli/` and nothing else about you or your machine. `MAPBOX_CLI_NO_TELEMETRY=1` keeps even future markers out of that header. -### Shell completion +### Diagnostics and settings ```sh -mapbox completion bash | zsh | fish | powershell +mapbox doctor # the token, proxies and settings the next command would use +mapbox usage # account usage per product, by day +mapbox config set update-check off # a setting that persists across shells +mapbox history list # recent runs, newest first ``` -Prints a completion script on stdout. Nothing is written to disk, so put it -where your shell looks: +`mapbox doctor` is the first thing to run when a command behaves +unexpectedly. It makes no request unless you pass `--verify`. See +[Doctor](./docs/commands.md#doctor), [Usage](./docs/commands.md#usage) and +[Config](./docs/commands.md#config) for details, and +[Command history](#command-history) below. + +### Shell completion + +Homebrew installs completions for you. Otherwise, `mapbox completion` +prints a script on stdout for you to put where your shell looks: ```sh mapbox completion bash > ~/.local/share/bash-completion/completions/mapbox @@ -267,11 +266,9 @@ source <(mapbox completion bash) # this shell only mapbox completion powershell >> $PROFILE ``` -It completes commands, subcommands and flag names, generated from this -binary's own command tree. So it matches the build that printed it, and -nothing about it is maintained by hand. Values (style ids, usernames) are not -completed: that would mean an API request mid-keystroke. `--output` does not -apply, because the script is the result. +It completes commands, subcommands and flags from this binary's own command +tree, so it always matches the build that printed it. Values such as style +IDs are not completed, since that would mean an API request mid-keystroke. ### Tileset CLI @@ -280,18 +277,17 @@ mapbox tilesets-cli list mapbox tilesets-cli upload-source data.geojson.ld ``` -Forwards everything to the separately-installed [Tilesets +Forwards everything to the separately installed [Tilesets CLI](https://github.com/mapbox/tilesets-cli): ```sh pipx install mapbox-tilesets # Python 3.10+ ``` -`--output` and `--yes` don't apply here. `tilesets` has its own flags, so use -`--force`/`-f` for its prompts. It needs a token too: `mapbox auth login` -covers it, or pass `--token`/`MAPBOX_ACCESS_TOKEN` the same way as every -other command. `mapbox auth whoami` shows which one is in play if a -tileset command answers for the wrong account. +It uses the same token as every other command. `--output` and `--yes` don't +apply here; `tilesets` has its own flags, so use `--force`/`-f` for its +prompts. If a tileset command answers for the wrong account, +`mapbox auth whoami` shows which token is in play. ## For AI agents @@ -302,41 +298,23 @@ using Mapbox, and `generate-skills` writes a skill describing this CLI. ```sh mapbox agent-skills list # what's published, and what's installed here -mapbox agent-skills install # all 20, into whichever agents you have +mapbox agent-skills install # every skill, for whichever agents you have mapbox agent-skills update # re-install what's here, report what changed mapbox agent-skills uninstall ``` Installs the [Mapbox Agent Skills](https://github.com/mapbox/mapbox-agent-skills): hand-written guidance for coding agents on cartography, token security, style -quality, geospatial operations and the mobile and web SDKs. No token needed, -and no Node. It's one tarball, extracted in the binary. - -Fifteen agents are supported: Claude Code, Codex, Cursor, Cline, Gemini CLI, -GitHub Copilot, Zed, OpenCode, Amp, Windsurf, Roo Code, Continue, Kiro CLI, -Qwen Code and Goose. By default, skills are installed for whichever of them -are found on the machine. - -| Flag | What it does | -| --- | --- | -| `--agent ` | Install for one agent. Repeatable. | -| `--global` | Write to the agent's home directory instead of this project. | -| `--dir ` | Write to a directory you name, for a Dockerfile or a CI job. | +quality, geospatial operations and the mobile and web SDKs. No token or Node +needed. -Most of these agents read the same `.agents/skills` directory, so asking for -several usually means a single write. - -`--ref ` installs a particular version and a SHA pins it; -`--dry-run` lists the files first. A skill directory that already exists stops -the install until `--force`, since it may hold your edits. - -`update` compares what's installed with what's published, byte for byte, and -rewrites only what differs, including restoring a file you edited. It never -installs a skill that wasn't already there. `uninstall ` removes the -directory, asking first at a terminal; it makes no network request at all. -There's no lock file: one tarball arrives before any record could be -consulted, so comparing bytes answers exactly and leaves no state to keep in -step with another tool's. +By default it installs into this project for each supported agent it finds +on the machine, including Claude Code, Codex, Cursor and GitHub Copilot. +`--agent` picks one, `--global` writes to the agent's home directory, and +`--dir` writes to a path you name, for a Dockerfile or a CI job. `update` +rewrites only files that differ from what's published, including files you +edited. The full list of agents and flags is in +[docs/commands.md](./docs/commands.md#agent-skills). ### Generate skills @@ -344,22 +322,16 @@ step with another tool's. mapbox generate-skills ``` -Writes the whole command surface as an [Agent -Skill](https://code.claude.com/docs/en/skills): `.claude/skills` for -Claude Code, `.agents/skills` for Codex. `--agent`, `--global`, `--dir`, -and `--service` narrow it; `--dry-run` lists files without writing them. - -Without `--global` it writes into the current project, once per agent it -finds, and it prints every directory it used. To take them out again: +Writes this CLI's whole command surface as an [Agent +Skill](https://code.claude.com/docs/en/skills), into the current project for +each agent it finds: `.claude/skills` for Claude Code, `.agents/skills` for +Codex. `--agent`, `--global`, `--dir` and `--service` narrow it, and +`--dry-run` lists the files first. To remove every copy it wrote: ```sh mapbox agent-skills uninstall mapbox-cli ``` -That removes every copy this command wrote, which is more than deleting the -directories by hand usually catches — a default run writes for each agent on -the machine, not just the one you had in mind. - ## Global options These apply to every command, not just the API ones. @@ -419,10 +391,8 @@ Two things are easy to lose an afternoon to: | `text` | Pretty-printed, readable | | `json` | Everything on stdout is JSON: one compact document per command | -`json` promises the shape, not the count. Every command today returns one -document. A command that streams would emit one per line (JSON Lines), but -that is a property of the command rather than of the flag, so there is no -`-o jsonl`. Nothing streams yet. +Every command prints one JSON document today. A command that streams would +print one per line (JSON Lines), which is why there is no `-o jsonl`. Errors always go to stderr and never appear in stdout. Under `json` they're one flat object: `code`, `message`, plus `fix`, `next_actions` and `docs` @@ -484,17 +454,16 @@ kept narrow: between runs. A build that names no release channel never checks at all, and `cargo build` produces one. -`mapbox config set update-check off` turns it off for good, in every shell — -see [Config](docs/commands.md#config) — rather than just the session an -environment variable happens to be set in. +`mapbox config set update-check off` turns it off in every shell, not just +the one an environment variable is set in. See +[Config](./docs/commands.md#config). ### Command history Each run appends one line to `~/.mapbox/history/.jsonl` (or under `$MAPBOX_CONFIG_DIR`), kept for 30 days and at most 10 MB, oldest dropped -first: which command ran (its command path, -like `search forward`), how it ended, how long it took and the request ids -support can look up. Argument values are never recorded — not what you +first: which command ran (its command path, like `search forward`), how it +ended, how long it took and the request ids support can look up. Argument values are never recorded — not what you searched for, not a file path, not a token. The files are readable only by you and never leave your machine.