Metadata-Version: 2.1
Name: maybeai-sheet-cli
Version: 0.25.1
Summary: CLI for common MaybeAI spreadsheet operations
Project-URL: Homepage, https://github.com/OmniMCP-AI/maybeai-sheet-cli
Project-URL: Repository, https://github.com/OmniMCP-AI/maybeai-sheet-cli
Project-URL: Issues, https://github.com/OmniMCP-AI/maybeai-sheet-cli/issues
Author: OmniMCP-AI
License: Proprietary
Keywords: cli,excel,maybeai,spreadsheet
Classifier: Environment :: Console
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.27
Requires-Dist: pydantic<3,>=2.7
Requires-Dist: pyyaml<7,>=6
Requires-Dist: rich<14,>=13.7
Requires-Dist: typer<1,>=0.12
Description-Content-Type: text/markdown

# maybeai-sheet-cli

CLI for MaybeAI spreadsheet operations.

`mbs` wraps the MaybeAI spreadsheet HTTP APIs behind a stable command-line interface for humans, CI jobs, and agents.

## Install

On Debian/Ubuntu and other **PEP 668** systems, do **not** use bare `pip install` on system Python. Use **pipx** (recommended for CLIs):

```bash
sudo apt install -y pipx python3-venv
pipx ensurepath
# re-login or: source ~/.bashrc

pipx install maybeai-sheet-cli
mbs --version
```

For local venvs or macOS/dev machines, plain pip is fine:

```bash
pip install maybeai-sheet-cli
```

Upgrade to the latest release:

```bash
mbs update          # when installed via pip or pipx
pipx upgrade maybeai-sheet-cli   # alternative on pipx installs
```

Install a specific version:

```bash
mbs update --version 0.3.2
pipx install maybeai-sheet-cli==0.3.2 --force   # pipx reinstall pin
```

Uninstall:

```bash
pipx uninstall maybeai-sheet-cli
# not: pip uninstall -U  (-U is invalid for uninstall)
```

## Requirements

- Python 3.10+
- `MAYBEAI_API_TOKEN`

## Configure

Set your API token:

```bash
export MAYBEAI_API_TOKEN="YOUR_TOKEN"
```

Optional global flags:

- `--base-url` to override the default MaybeAI API host, `https://a-play-be.maybeai.cn`
- `--output json|table|yaml`
- `--timeout <seconds>`
- `--verbose`

Alias note:

- Prefer nested worksheet media/layout commands such as `mbs worksheet style ...`
  or `mbs excel-worksheet style ...`
- The CLI also exposes compatibility shortcuts `mbs style ...`, `mbs image ...`,
  `mbs chart ...`, and `mbs dashboard ...` for the same command groups

## Quick Start

The examples below follow the target protocol names.

List user-visible workbooks:

```bash
mbs workbook list-user-workbooks --limit 20
```

Read a worksheet:

```bash
mbs excel-worksheet read --doc-id abc123 --worksheet-name Sheet1
mbs excel-worksheet read --doc-id abc123 --worksheet-name Sheet1 --range A1:H1
```

Replace worksheet data while keeping row 1, existing header order, styles, and
formula columns:

```bash
mbs sheet update-data-keep-headers \
  --doc-id abc123 \
  --worksheet-name Students \
  --data students.json \
  --verify
```

`students.json` must be a non-empty JSON array of objects whose keys exactly
match existing worksheet headers:

```json
[
  {"Name": "Alice", "Score": 88},
  {"Name": "Bob", "Score": 92}
]
```

The command reads the worksheet headers before writing and rejects unknown
keys. It preserves formula columns and rebuilds the workbook calculation chain
by default. A formula column is preserved only when its key is omitted from the
first input object and row 2 contains its formula template.

Use `--no-preserve-formulas` to replace formula columns from JSON,
`--skip-recalculation` to skip rebuilding the calculation chain, or
`--start-row N` to begin below row 2. `--dry-run` validates and prints the
request without updating data. `--verify` reads back the header, updated rows,
and one trailing row; it cannot be combined with `--dry-run`.

This full-refresh endpoint parses numeric-looking strings as Excel values. A
long value such as `"46215.95520833333"` can read back as `"46215.95521"`.
Use `sheet write-range` / `range write` with RAW value handling when exact text
preservation matters more than header-keyed replacement semantics.

List worksheets:

```bash
mbs workbook list-worksheets --doc-id abc123
```

Inspect the first worksheet-backed table:

```bash
mbs excel-table metadata --doc-id abc123 --worksheet-name Sheet1 --table-id <persistent-table-id>
```

Read, sample, or append rows to a persistent worksheet-backed table:

```bash
mbs excel-table schema --doc-id abc123 --worksheet-name Sheet1 --table-id <persistent-table-id>
mbs excel-table read --doc-id abc123 --worksheet-name Sheet1 --table-id <persistent-table-id> --limit 100
mbs excel-table sample --doc-id abc123 --worksheet-name Sheet1 --table-id <persistent-table-id> --limit 10
mbs excel-table insert --doc-id abc123 --worksheet-name Sheet1 --table-id <persistent-table-id> --rows rows.json
```

`--table-id` is the persistent ID returned by `metadata`, not a table sequence
number. `excel-table` does not accept `--range`. For `insert`, `rows.json` is a
non-empty object array whose keys exactly match the current table headers; one
file can append multiple rows. After structural row changes, read `metadata`
again before the next insert because the table range may have shifted.

Create a workbook:

```bash
mbs workbook create --title "Board Pack"
```

Import a workbook-style `.xlsx` through the default Sheet path:

```bash
mbs workbook import ./report.xlsx
```

Import a large table-like `.xlsx` into Base Mode:

```bash
mbs workbook import ./large-table.xlsx --engine base
```

Import a mixed workbook and let the backend select engines per worksheet:

```bash
mbs workbook import ./mixed-workbook.xlsx --engine auto
mbs workbook import ./mixed-workbook.xlsx --engine "base,sheet,sheet,base"
```

Import a remote Excel file directly into a new workbook. `--filename` is optional;
the CLI uses the URL path filename when available and otherwise falls back to
`upload.xlsx`:

```bash
mbs workbook import "https://static.example.com/imports/report.xlsx" --engine auto
mbs workbook import "https://static.example.com/download?id=123" --source-type xlsx --filename "销售报表.xlsx" --engine auto
```

Remote Excel URL import calls `/api/v1/excel/import_by_url` and only supports
creating a new workbook. It cannot be combined with `--doc-id`, worksheet
selection options, or a comma-separated per-worksheet engine list.

Import CSV, TSV, or a public Google Sheet through the import-source preview flow:

```bash
mbs workbook import ./orders.csv
mbs workbook import ./orders.tsv
mbs workbook import ./orders.csv --engine base
mbs workbook import ./orders.tsv --engine sheet
mbs workbook import "https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit#gid=0"
mbs workbook import "https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit#gid=0" --engine base
mbs workbook import "https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit#gid=0" --source-worksheet-name "1店" --worksheet-name "Store 1" --engine sheet
mbs workbook import ./orders.csv --preview-only --output json
```

Import worksheets from a local Excel file, CSV/TSV file, or public Google Sheet
into an existing workbook with `worksheet import --strategy create`. When
`--source-worksheet-name` is omitted, the CLI imports all source worksheets/tabs
returned by preview. Repeat `--source-worksheet-name` to import only selected
worksheets/tabs. If any requested worksheet does not exist, the CLI reports the
missing name and the available worksheet names. `--target-worksheet-name` is
only valid when importing exactly one worksheet.

```bash
# Import all worksheets/tabs into an existing workbook.
mbs worksheet import ./report.xlsx --strategy create --doc-id target123 --engine base --verify
mbs worksheet import ./orders.csv --strategy create --doc-id target123 --engine base --verify
mbs worksheet import "https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit#gid=0" --strategy create --doc-id target123 --engine sheet --verify

# Import one worksheet/tab, optionally renaming the target worksheet.
mbs worksheet import ./report.xlsx --strategy create --doc-id target123 --source-worksheet-name "联盟" --engine sheet --verify
mbs worksheet import ./report.xlsx --strategy create --doc-id target123 --source-worksheet-name "联盟" --target-worksheet-name "联盟导入" --engine base --verify
mbs worksheet import ./orders.csv --strategy create --doc-id target123 --source-worksheet-name "orders" --target-worksheet-name "Orders Imported" --engine base --verify
mbs worksheet import "https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit#gid=0" --strategy create --doc-id target123 --source-worksheet-name "1店" --engine sheet --verify

# Import multiple selected worksheets/tabs by repeating the source option.
mbs worksheet import ./report.xlsx --strategy create --doc-id target123 --source-worksheet-name "联盟" --source-worksheet-name "订单" --engine base --verify
mbs worksheet import ./orders.tsv --strategy create --doc-id target123 --source-worksheet-name "orders" --source-worksheet-name "refunds" --engine base --verify
mbs worksheet import "https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit#gid=0" --strategy create --doc-id target123 --source-worksheet-name "1店" --source-worksheet-name "2店" --engine sheet --verify
```

Replace all data rows in an existing worksheet from a JSON object array while
keeping row 1, the existing column order, styles, and formula columns:

```bash
mbs worksheet import ./rows.json \
  --strategy replace \
  --doc-id target123 \
  --worksheet-name Students \
  --verify
```

`rows.json` must be a non-empty array of objects whose keys exactly match the
existing worksheet headers:

```json
[
  {"Name": "Alice", "Score": 88},
  {"Name": "Bob", "Score": 92}
]
```

The replace strategy calls `/api/v1/excel/update_data_keep_headers`. It
preserves formula columns by default. Use `--dry-run` for preflight validation,
`--no-preserve-formulas` to replace formula columns, `--skip-recalculation` to
defer recalculation, or `--start-row N` to start below row 2. `--dry-run` and
`--verify` cannot be combined.

### Convert a Sheet Worksheet to Base

The routed backend supports a one-way migration from a Sheet-backed worksheet
to Base with `POST /api/v1/excel/convert_to_base_mode`. It accepts a workbook
`document_id` and, with `scope: "worksheet"`, either a `worksheet_name` or a
`gid`. There is no corresponding Base-to-Sheet conversion endpoint.

Use the first-class command and always run the dry run first:

```bash
# Validate a name-targeted conversion without changing the workbook.
mbs worksheet convert-to-base \
  --doc-id abc123 \
  --worksheet-name Orders \
  --dry-run

# Execute the same migration by gid after reviewing the dry-run result.
mbs worksheet convert-to-base \
  --doc-id abc123 \
  --gid 7 \
  --recalculate \
  --yes \
  --verify

# Confirm that the target worksheet now reports data_engine: base.
mbs workbook list-worksheets --doc-id abc123 --output json
```

This is a data migration, not a metadata toggle. The routed endpoint defaults
`scrub_source_workbook` to `true`: after a successful conversion it removes
the old Sheet-engine cell content for the converted worksheet while preserving
styles. To retain that source content during the migration, explicitly pass
`--keep-sheet-source`. The conversion requires editor access. The command maps
to `convert_to_base_mode` with `scope: "worksheet"`; `--yes` executes the
migration and `--verify` confirms `data_engine: base`.

Import source worksheets from another Maybe Sheet workbook with
`worksheet import --strategy create`. The default `--transfer-mode values`
materializes raw Base/db-table surfaces.
Target table names default to `R_{sanitized_worksheet_name}`. Repeat
`--source-worksheet-name` to import selected worksheets, or omit it to read
source worksheet metadata and import all worksheets.

```bash
mbs worksheet import \
  --strategy create \
  --transfer-mode values \
  --doc-id target123 \
  --engine base \
  --source-doc-id source123 \
  --source-worksheet-name "1店" \
  --verify \
  --output json

mbs worksheet import \
  --strategy create \
  --transfer-mode values \
  --doc-id target123 \
  --engine base \
  --source-doc-id source123 \
  --source-worksheet-name "1店" \
  --source-worksheet-name "2店" \
  --verify \
  --output json
```

Use `--transfer-mode native` to preserve each source worksheet's registered engine.
The CLI reads `/api/v1/excel_v2/worksheet/metadata`, selects the requested
worksheets (or all worksheets when none are specified), and calls
`/api/v1/excel/copy_worksheet` once per worksheet. Do not pass `--engine` in
native mode; Base worksheets remain Base and Sheet worksheets remain Excel.

| Mode | Engine behavior | Source handling |
|---|---|---|
| `values` (default) | Only `base`; omitted means Base | Reads `FORMATTED_VALUE` and creates raw `R_*` Base surfaces |
| `native` | Do not pass `--engine`; metadata selects `base` or `sheet` per worksheet | Copies values/formulas and engine-supported worksheet fidelity without cross-engine conversion |

Native mode is only valid with `--source-doc-id` or `--source-url` between
existing Maybe Sheet workbooks. It rejects `--name`, explicit `--engine`, and
raw-table conflict flags. Empty worksheets can be copied natively.

```bash
# Copy selected Base and Sheet worksheets into the target workbook.
mbs worksheet import \
  --strategy create \
  --transfer-mode native \
  --doc-id target123 \
  --source-doc-id source123 \
  --source-worksheet-name "工作表3" \
  --source-worksheet-name "工作簿1" \
  --verify \
  --output json

# Copy every source worksheet, preserving each worksheet engine.
mbs worksheet import \
  --strategy create \
  --transfer-mode native \
  --doc-id target123 \
  --source-doc-id source123 \
  --verify \
  --output json
```

For one native worksheet, `--target-worksheet-name` may override the requested
target name. Backend conflict handling can still return a suffixed final name,
which the CLI reports in `result.imported_details`.

Large native copies may exceed the default 30-second HTTP timeout. Use
`--timeout 600` when needed. A timeout error identifies the current worksheet,
engine, and previously confirmed worksheets. Because the server may complete
after the client disconnects, inspect target metadata before retrying; then
retry only unconfirmed worksheets to avoid duplicate `(2)` names. Add
`--verbose` to print per-worksheet copy progress.

After a successful raw worksheet import, trust `worksheet import` stdout and
`--verify` as the table-creation evidence. If you need a spot check, sample one
representative created table instead of looping over every `R_*` table:

```bash
mbs db-table sample --doc-id target123 --name R_order_lines_store_1 --limit 2 --output json
```

Append rows to a persistent worksheet-backed table, then read back the affected
range or table:

```bash
mbs excel-table insert --doc-id abc123 --worksheet-name Sheet1 --table-id <persistent-table-id> --rows rows.json
mbs excel-table read --doc-id abc123 --worksheet-name Sheet1 --table-id <persistent-table-id> --limit 20 --output table
```

Grant workbook access:

```bash
mbs share create --doc-id abc123 --email teammate@example.com --role viewer
```

Rename a worksheet and inspect a destructive row delete before sending it:

```bash
mbs excel-worksheet rename --doc-id abc123 --worksheet-name Old --new-name New
mbs excel-worksheet row delete --doc-id abc123 --worksheet-name Sheet1 --row 10 --dry-run
mbs excel-worksheet row delete --doc-id abc123 --worksheet-name Sheet1 --row 10 --yes
```

Search, style, inspect history, and export:

```bash
mbs excel-worksheet range search --doc-id abc123 --worksheet-name Sheet1 --query revenue --max-results 20
mbs excel-worksheet range lineage --doc-id abc123 --worksheet-name Sheet1 --range B14:D14 --format tree
mbs excel-worksheet style freeze-panes --doc-id abc123 --worksheet-name Sheet1 --cell B2
mbs worksheet style format --doc-id abc123 --worksheet-name Sheet1 --field amount --formatter "#,##0.00"
mbs db-table field batch-update --doc-id abc123 --name orders_large --updates field-updates.json
mbs style beautify --doc-id abc123 --worksheet-name Sheet1
mbs style beautify --doc-id abc123 --worksheet-name Sheet1 --table-id 2
mbs style columns-width --doc-id abc123 --worksheet-name Sheet1 --start-column M --end-column M --width 144
mbs excel-worksheet style worksheet plan --doc-id abc123 --worksheet-name Sheet1 --mode auto_detect --spec worksheet_style.json
mbs excel-worksheet style worksheet apply --doc-id abc123 --worksheet-name Sheet1 --mode auto_detect --spec worksheet_style.json
mbs excel-worksheet image list --doc-id abc123 --worksheet-name Dashboard
mbs worksheet image list --doc-id abc123 --worksheet-name Dashboard
mbs worksheet image set --doc-id abc123 --worksheet-name Dashboard --old-cell B3 --cell B3 --format picture-format.json --width 120 --height 91
mbs excel-worksheet chart list --doc-id abc123 --worksheet-name Dashboard
mbs excel-worksheet dashboard manifest --doc-id abc123 --worksheet-name Dashboard
mbs workbook calculate --doc-id abc123
mbs excel-worksheet calculate --doc-id abc123 --worksheet-name Sheet1
mbs excel-worksheet range calculate --doc-id abc123 --worksheet-name Sheet1 --cell E2 --formula "=SUM(B2:D2)"
mbs version list --doc-id abc123 --limit 10
mbs workbook export --doc-id abc123 --out workbook.xlsx
```

The worksheet media/layout groups are available in both forms:

```bash
mbs excel-worksheet chart list --doc-id abc123 --worksheet-name Dashboard
mbs chart list --doc-id abc123 --worksheet-name Dashboard
```

Prefer the nested `excel-worksheet ...` form in docs and agent prompts when you
want the object model to stay explicit.

For simple report/table polishing, prefer `mbs style beautify`. It first reads
worksheet metadata, then routes by engine: Sheet worksheets get a frozen
header row, persisted auto-filter, header styling, and semantic column styles
for date, currency, percent, integer, status, and text columns; Base
worksheets get equivalent field formatter/style metadata updates through the
batched `/db_table/field/batch_update` route. The semantic classifier uses
weighted Chinese/English header keywords, common business
abbreviations such as AOV/GMV/ARR/MRR, Base field display metadata, and Excel
sample values. Use `--table-id` to beautify one Excel table within a multi-table
worksheet instead of styling every detected table. It includes finance/e-commerce terms such as net income, EBITDA,
cash flow, receivables, payment amount, ad spend, CTR/CVR, refund rate, UV/PV,
inventory, fulfillment status, 毛利率, 广告花费, 退款率, and 履约状态. Use
`--dry-run --output json` to inspect each column's category, format, confidence,
and reasons before applying styles.

For explicit Base/db-table field updates, use one JSON array with
`mbs db-table field batch-update` instead of many single-field calls:

```json
[
  {"name": "revenue", "logical_type": "number", "property": {"formatter": "$#,##0.00"}},
  {"name": "margin_rate", "logical_type": "number", "property": {"formatter": "0.00%"}}
]
```

```bash
mbs db-table field batch-update --doc-id abc123 --name orders_large --updates field-updates.json --verify
```

For chart specs authored as `type: "json"`, keep metadata `title` when the chart
needs an internal or custom-rendered title, and hide the visible container title
with `hide_title: true` or `spec.style.showContainerTitle: false` rather than
deleting `title`.

## Relationship
1 workbok
 |                         \
N db_worksheet(Base)   N excel_worksheets
                            |                        \
                      M(N>1) db_worksheet(db_table)   J(<N) excel_table

CLI command names normalize these relationship labels to hyphenated groups:
`excel_worksheet` becomes `excel-worksheet`, `excel_table` becomes
`excel-table`, and `db_worksheet(db_table)` becomes `db-table`.


## Full Command Tree

The reviewed tree below is the source of truth for the protocol shape. Product
model labels in this block use underscores; CLI names in examples and future
router code should normalize them to hyphenated command groups such as
`excel-worksheet`, `excel-table`, and `db-table`. This tree includes
implemented, planned, deferred, and not-recommended command surfaces.

Status legend for the full tree:

- `NOW`: released in the current CLI under this command shape.
- `API`: route exists or is expected, but the target command wrapper/refactor is
  not released under this shape yet.
- `BASE`: Base Mode-specific; defer until exposed through the public API.
- `PLAN`: needs a backend/product contract before implementation.
- `DEFER`: intentionally not part of the clean agent-first surface yet.
- `SKIP`: not recommended for this CLI.

```text
mbs
├── update
│   └── Update maybeai-sheet-cli from PyPI.
│
├── workbook
│   ├── metadata               API    Inspect workbook profile and routing metadata.(workshets)
│   ├── source                 PLAN   Similar to NotebookLM source (menu on the left).
│   ├── create                 NOW    Create a new workbook.
│   ├── create-from-file       NOW    Import an .xlsx file.
│   ├── import                 NOW    File import or cross-workbook worksheet -> raw Base surface import.
│   ├── list-user-workbooks    API    List user workbooks with bounded output.
│   ├── list-worksheets        API    List worksheets, including engine.
│   ├── search                 NOW    Search user workbooks with bounded output.
│   ├── calculate              NOW    Recalculate formulas across the workbook.
│   ├── export                 API    Export workbook bytes.
│   ├── copy                   API    Copy a workbook.
│   ├── upload                 API    Upload a file through workbook import flow.
│   ├── update                 API    Update a stored workbook file not change doc_id
│   ├── export                 API    Export workbook bytes to --out.
│   ├── delete                 API    Delete a stored file.
│   └── rename                 API    Rename a stored file.
│
├── excel_worksheet
│   ├── read                   API    Read the full range of the worksheet.
│   ├── check-error            API    Read worksheet cells and scan for formula-style errors.
│   ├── calculate              NOW    Recalculate formulas in one worksheet.
│   ├── list-table             API    Detect content-backed tables in a worksheet.
│   ├── create                 API    Create a worksheet, optionally with starter values.
│   ├── update                 API    Update worksheet metadata.
│   ├── sample                 PLAN   Return bounded worksheet/range sample rows.
│   ├── delete                 API    Delete a worksheet; requires --yes unless --dry-run.
│   ├── rename                 API    Rename a worksheet.
│   ├── move                   API    Move a worksheet to a zero-based index.
│   ├── copy                   API    Copy a worksheet.
│   └── metadata               API    Include table meta: range, header, table id, data summary.
│
├── excel_table
│   ├── create                 PLAN   Create a table view from a worksheet range.
│   ├── update                 PLAN   Update table view metadata or range.
│   ├── delete                 PLAN   Delete a table view; requires --yes unless --dry-run.
│   ├── schema                 API    Infer table schema from a resolved table range.
│   ├── sample                 API    Return bounded table sample rows.
│   ├── read                   API    Read header-aware table rows.
│   ├── insert                 API    Insert table rows.
│   └── metadata               API    Include table meta: range, header, table id, data summary.
│
├── excel_worksheet range
│   ├── read                   API    Read a bounded A1 range.
│   ├── write                  API    Write a JSON matrix into an A1 range.
│   ├── clear                  API    Clear an A1 range.
│   ├── read-many              DEFER  Legacy alias only until a real batch API exists.
│   ├── copy                   API    Copy a range.
│   ├── search                 API    Search a worksheet with bounded results.
│   ├── used-range             PLAN   Return worksheet used range after a routed contract exists.
│   ├── set-formula            API    Set one formula cell.
│   ├── calculate              API    Calculate one formula cell.
│   └── lineage                API    Trace cell/range lineage.
│
├── db_worksheet(AKA db_table)
│   ├── create                 API    Create a Base-backed table from JSON row data.
│   ├── create-from-query      API    Calculate SQL and create a Base-backed table from tabular results.
│   ├── create-from-range      API    Read a source worksheet range and create a Base-backed table on a target workbook.
│   ├── update                 PLAN   Update Base-backed rows from JSON row data.
│   ├── delete                 PLAN   Delete Base-backed rows/table; requires --yes unless --dry-run.
│   ├── schema                 API    Infer table schema from a bounded Base read.
│   ├── sample                 API    Return bounded table sample rows.
│   ├── read                   API    Read Base-backed table rows.
│   ├── insert                 API    Insert Base-backed table rows.
│   └── metadata               API    Include table meta, backend id, schema, and data summary.
│
├── excel_worksheet row
│   ├── insert                 API    Insert rows.
│   ├── delete                 API    Delete rows; requires --yes unless --dry-run.
│   └── move                   API    Move rows.
│
├── excel_worksheet column
│   ├── insert                 API    Insert columns.
│   ├── delete                 API    Delete columns; requires --yes unless --dry-run.
│   ├── move                   API    Move columns.
│   ├── width                  API    Set column width.
│   └── append                 API    Append columns.
│
├── excel_worksheet chart
│   ├── list                   NOW    List charts in one worksheet.
│   ├── get                    NOW    Read one chart spec and source binding.
│   ├── preview                PLAN   Preview one chart from its spec/source binding.
│   ├── create-config          NOW    Create one chart from a chart spec.
│   ├── update                 NOW    Update one chart from a chart spec.
│   ├── refresh                PLAN   Refresh one chart from its current source binding.
│   └── delete                 NOW    Delete one chart.
│
├── excel_worksheet dashboard
│   ├── manifest               NOW    Aggregate worksheet metadata and chart inventory for one dashboard sheet.
│   ├── create-config          NOW    Create a dashboard worksheet and compose charts from one spec.
│   ├── validate               NOW    Validate dashboard layout, worksheet target, and chart source fields locally.
│   └── refresh                NOW    Upsert dashboard charts from one spec and re-verify chart inventory.
│
├── pivot
│   ├── read                   API    Read a pivot result from a JSON spec.
│   ├── preview                API    Preview pivot output without writing cells.
│   ├── upsert                 API    Write or overwrite a persisted pivot table at an anchor cell.
│   └── delete                 API    Delete a persisted pivot table by worksheet and anchor cell.
│
│
├── excel-worksheet style
│   ├── cell batch-set          NOW    Apply cell style batches from JSON.
│   ├── format                  NOW    Set engine-routed field/cell format, color, background, or width.
│   ├── columns-width           NOW    Set column widths.
│   ├── rows-height             NOW    Set row heights.
│   ├── freeze-panes            NOW    Freeze rows/columns using an A1 boundary.
│   ├── gridlines toggle        NOW    Toggle worksheet gridlines.
│   ├── auto-filter set         NOW    Set worksheet auto-filter range.
│   ├── auto-filter remove      NOW    Remove worksheet auto-filter.
│   ├── filter-values           NOW    Apply filter values.
│   ├── conditional-formats set NOW    Set conditional formatting rules.
│   ├── worksheet plan          NOW    Generate a worksheet styling plan.
│   └── worksheet apply         NOW    Apply a worksheet styling plan.
│
├── excel_worksheet image
│   ├── list                   NOW    List worksheet images from worksheet formatting metadata.
│   ├── read                   NOW    Read one worksheet image by cell or picture id.
│   ├── replace                NOW    Replace one worksheet image in place.
│   ├── insert                 NOW    Add an image file.
│   ├── set                    NOW    Move/resize/update an image with chart-compatible format anchors.
│   └── delete                 NOW    Delete an image.
│
├── excel_worksheet media
│   └── check                  NOW    Check whether one worksheet contains image and chart objects.
│
├── version
│   ├── list                   API    List workbook history with bounded output.
│   └── restore                PLAN   Restore a workbook history version after a routed contract exists.
│
│
├── share
│   ├── create                 API    Create or grant sharing access.
│   ├── remove                 API    Remove a user's access.
│   └── modify                 API    Modify sharing access.
```

Full tree examples:

These examples cover every top-level category. Commands marked `API`, `PLAN`,
`BASE`, or `DEFER` show the intended CLI shape, but are not guaranteed to be
available until their status moves to `NOW`. They use hyphenated CLI command
names even when the reviewed protocol tree uses underscore model labels.

```bash
# update
mbs update --check

# workbook
mbs workbook metadata --doc-id abc123  # API
mbs workbook list-user-workbooks --limit 20  # API
mbs workbook list-worksheets --doc-id abc123  # API
mbs workbook import ./report.xlsx  # NOW
mbs workbook import ./orders.csv --engine base  # NOW
mbs workbook import "https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit#gid=0" --engine sheet  # NOW
mbs worksheet import ./report.xlsx --strategy create --doc-id target123 --source-worksheet-name "联盟" --target-worksheet-name "联盟导入" --engine base --verify  # NOW
mbs worksheet import ./report.xlsx --strategy create --doc-id target123 --source-worksheet-name "联盟" --source-worksheet-name "订单" --engine base --verify  # NOW
mbs worksheet import ./orders.csv --strategy create --doc-id target123 --source-worksheet-name "orders" --target-worksheet-name "Orders Imported" --engine base --verify  # NOW
mbs worksheet import ./rows.json --strategy replace --doc-id target123 --worksheet-name Students --verify  # NOW
mbs worksheet import --strategy create --doc-id target123 --source-doc-id source123 --source-worksheet-name "1店" --engine base --verify --output json  # NOW
mbs worksheet import --strategy create --transfer-mode native --doc-id target123 --source-doc-id source123 --source-worksheet-name "工作表3" --source-worksheet-name "工作簿1" --verify --output json  # NOW
mbs workbook export --doc-id abc123 --out workbook.xlsx  # API
mbs workbook copy --doc-id abc123 --name "Budget Copy"  # API
mbs workbook delete --doc-id abc123 --dry-run  # API

# excel-worksheet
mbs excel-worksheet read --doc-id abc123 --worksheet-name Actuals  # API
mbs excel-worksheet read --doc-id abc123 --worksheet-name Actuals --range A1:D20  # API
mbs excel-worksheet check-error --doc-id abc123 --worksheet-name Model --range B2:H80  # API
mbs excel-worksheet create --doc-id abc123 --name Actuals  # API
mbs excel-worksheet update --doc-id abc123 --worksheet-name Actuals --title "Actuals FY26"  # API
mbs excel-worksheet metadata --doc-id abc123 --worksheet-name Actuals  # API
mbs excel-worksheet list-table --doc-id abc123 --gid 3  # API
mbs excel-worksheet sample --doc-id abc123 --worksheet-name Actuals --range A1:Z5000 --limit 50  # PLAN

# excel-worksheet range
mbs excel-worksheet range read --doc-id abc123 --worksheet-name Actuals --range A1:D20  # API, advanced structured alias
mbs excel-worksheet range used-range --doc-id abc123 --worksheet-name Actuals  # PLAN
mbs excel-worksheet range lineage --doc-id abc123 --worksheet-name Model --range B14:D14 --format tree  # API

# excel-table
mbs table headers --doc-id abc123 --worksheet-name Orders --table-id <persistent-table-id>  # NOW
mbs table headers --doc-id abc123 --gid 3 --table-id <persistent-table-id>  # NOW
mbs excel-table metadata --doc-id abc123 --worksheet-name Orders --table-id <persistent-table-id>  # API
mbs excel-table schema --doc-id abc123 --worksheet-name Orders --table-id <persistent-table-id>  # API
mbs excel-table sample --doc-id abc123 --worksheet-name Orders --table-id <persistent-table-id> --limit 50  # API
mbs excel-table read --doc-id abc123 --worksheet-name Orders --table-id <persistent-table-id> --limit 50  # API
mbs excel-table insert --doc-id abc123 --worksheet-name Orders --table-id <persistent-table-id> --rows rows.json  # API
mbs excel-table style columns-width --doc-id abc123 --worksheet-name Orders --table-id <persistent-table-id> --column B --width 120  # API

# base-table
mbs base-table read --doc-id abc123 --table-id tbl_orders --limit 100  # API
mbs base-table record replace --doc-id abc123 --table-id tbl_orders --records records.json --expected-revision 7  # API
mbs base-table record upsert --doc-id abc123 --table-id tbl_orders --key-field-id fld_sku --records records.json  # API
mbs base-table field list --doc-id abc123 --table-id tbl_orders  # API
mbs base-table formula compile --doc-id abc123 --table-id tbl_orders --field-id fld_total --expression '{fld_qty} * {fld_price}'  # API
mbs base-table formula set --doc-id abc123 --table-id tbl_orders --field-id fld_total --expression '{fld_qty} * {fld_price}' --result-type number  # API
mbs base-table formula recalculate --doc-id abc123 --table-id tbl_orders --worksheet-name Orders  # API

# db-table
mbs db-table metadata --doc-id abc123 --name orders-large  # API
mbs db-table metadata --doc-id abc123 --name orders-large --include-headers  # API
mbs db-table schema --doc-id abc123 --name orders-large  # API
mbs db-table sample --doc-id target123 --name R_order_lines_store_1 --limit 2 --output json  # API, representative post-import spot check
mbs db-table read --doc-id abc123 --name orders-large --limit 100 --offset 200  # API
mbs db-table insert --doc-id abc123 --name orders-large --rows rows.json  # API
mbs db-table create --doc-id abc123 --name Orders --rows orders.json  # API
mbs db-table create-from-query --doc-id abc123 --name OrderSummary --sql-file order_summary.sql  # API
mbs db-table create-from-range --doc-id target123 --name R_OrderLines_Store1 --source-doc-id source123 --worksheet-name "1店" --range A2:AR423 --header-row 0 --use-header-names --if-exists adopt --verify  # API
mbs db-table update --doc-id abc123 --name Orders --key order_id --rows orders_patch.json  # API
mbs db-table update --doc-id abc123 --name Orders --row-index 42 --rows row_patch.json  # PLAN
mbs db-table update --doc-id abc123 --name Orders --where '{"status":"open"}' --rows status_patch.json  # PLAN
mbs db-table update --doc-id abc123 --name Orders --column-index 3 --column-name status  # PLAN
mbs db-table delete --doc-id abc123 --name Orders --where '{"order_id":"A1"}' --yes  # PLAN
mbs db-table delete --doc-id abc123 --name Orders --row-index 42 --count 10 --yes  # PLAN
mbs db-table delete --doc-id abc123 --name Orders --column-index 3 --yes  # PLAN
mbs db-table delete --doc-id abc123 --name Orders --column-name status --yes  # PLAN

# row
mbs excel-worksheet row insert --doc-id abc123 --worksheet-name Actuals --row 10 --count 2  # API

# column
mbs excel-worksheet column width --doc-id abc123 --worksheet-name Actuals --column B --width 120  # API
mbs excel-worksheet column append --doc-id abc123 --worksheet-name Actuals --values columns.json  # API

# cell
mbs excel-worksheet range lineage --doc-id abc123 --worksheet-name Model --range B14:D14 --format tree  # API

# formula
mbs workbook calculate --doc-id abc123  # API
mbs excel-worksheet calculate --doc-id abc123 --worksheet-name Model  # API
mbs excel-worksheet range calculate --doc-id abc123 --worksheet-name Model --cell E2 --formula "=SUM(B2:D2)"  # API
mbs excel-worksheet range set-formula --doc-id abc123 --worksheet-name Model --cell E2 --formula "=SUM(B2:D2)"  # API

# chart
mbs excel-worksheet chart list --doc-id abc123 --worksheet-name Dashboard  # NOW
mbs excel-worksheet chart get --doc-id abc123 --worksheet-name Dashboard --cell J2  # NOW
mbs excel-worksheet chart create-config --doc-id abc123 --worksheet-name Dashboard --cell J2 --spec chart.json  # NOW
mbs excel-worksheet chart update --doc-id abc123 --worksheet-name Dashboard --cell J2 --chart-id rId1 --spec chart.json  # NOW
mbs excel-worksheet chart delete --doc-id abc123 --worksheet-name Dashboard --chart-id rId1  # NOW
mbs excel-worksheet media check --doc-id abc123 --worksheet-name Dashboard  # NOW

# Detailed chart/image/media demos live in docs/chart-media-demos.md:
# - type=html chart
# - type=json ECharts chart
# - SQL-backed html/json charts
# - multiple SQL data_sources
# - dashboard filter event -> SQL/data_sources refresh
# - media check after add_picture/add_chart/upsert_charts

# dashboard
mbs excel-worksheet dashboard validate --spec dashboard.json  # NOW
mbs excel-worksheet dashboard manifest --doc-id abc123 --worksheet-name Dashboard  # NOW
mbs excel-worksheet dashboard create-config --doc-id abc123 --spec dashboard.json --create-worksheet  # NOW
mbs excel-worksheet dashboard refresh --doc-id abc123 --spec dashboard.json  # NOW

# dashboard create-config creates chart objects; dashboard refresh batch-upserts
# chart objects via /api/v1/excel/upsert_charts. Every chart entry must set type.
# For type=html, chart.html is required; chart.data_sources.<name> must be a SQL string,
# not an object such as {"sql":"..."}.
# For large renderer dependencies, reference approved CDN packages with <script src>
# such as jsdelivr/unpkg/cdnjs/d3js; do not inline full ECharts/D3 bundles into chart.html.
# See docs/chart-media-demos.md for copyable dashboard.json examples.

# pivot
mbs pivot preview --doc-id abc123 --gid 1 --spec pivot.json  # API
mbs pivot read --doc-id abc123 --spec pivot.json  # API
mbs pivot upsert --doc-id abc123 --target-worksheet-name PivotResult --anchor-cell A1 --spec pivot.json  # API
mbs pivot delete --doc-id abc123 --worksheet-name PivotResult --anchor-cell A1 --dry-run  # API
mbs pivot delete --doc-id abc123 --worksheet-name PivotResult --anchor-cell A1 --yes  # API

# recommended pivot.json shape
# {
#   "worksheet_name": "SourceData",
#   "range_address": "A1:C100",
#   "row_fields": ["Region"],
#   "column_fields": ["Category"],
#   "metrics": [
#     { "aggregate": "sum", "value_field": "Amount", "label": "Total Amount" }
#   ],
#   "show_row_totals": true,
#   "show_column_totals": true
# }

# style
mbs excel-worksheet style freeze-panes --doc-id abc123 --worksheet-name Actuals --cell B2  # NOW
mbs excel-worksheet style cell batch-set --doc-id abc123 --worksheet-name Actuals --range A1:G1 --style header_style.json  # NOW
mbs worksheet style format --doc-id abc123 --worksheet-name Actuals --field amount --formatter "#,##0.00" --color "#E11D48"  # NOW
mbs db-table field batch-update --doc-id abc123 --name ActualsTable --updates field-updates.json  # NOW
mbs style beautify --doc-id abc123  # NOW
mbs style beautify --doc-id abc123 --worksheet-name Actuals --table-id 2  # NOW
mbs style columns-width --doc-id abc123 --worksheet-name Actuals --start-column M --end-column M --width 144  # NOW
mbs excel-worksheet style conditional-formats set --doc-id abc123 --worksheet-name Actuals --spec conditional_formats.json  # NOW
mbs excel-worksheet style worksheet apply --doc-id abc123 --worksheet-name Actuals --mode auto_detect --spec worksheet_style.json  # NOW

# image
mbs excel-worksheet image list --doc-id abc123 --worksheet-name Dashboard  # NOW
mbs worksheet image list --doc-id abc123 --worksheet-name Dashboard  # NOW, alias
mbs excel-worksheet image read --doc-id abc123 --worksheet-name Dashboard --cell A1 --out logo.png  # NOW
mbs excel-worksheet image insert --doc-id abc123 --worksheet-name Dashboard --cell A1 --file logo.png --format picture-format.json  # NOW
mbs excel-worksheet image set --doc-id abc123 --worksheet-name Dashboard --old-cell A1 --cell B3 --format picture-format.json --width 120 --height 91  # NOW
mbs excel-worksheet image replace --doc-id abc123 --worksheet-name Dashboard --cell A1 --file logo_v2.png --format picture-format.json  # NOW
mbs excel-worksheet image delete --doc-id abc123 --worksheet-name Dashboard --cell A1  # NOW

# version
mbs version list --doc-id abc123 --limit 10  # API
mbs version restore --doc-id abc123 --version-id v42 --dry-run  # PLAN

# share
mbs share create --doc-id abc123 --email teammate@example.com --role viewer  # API
```

Copyable chart, image, media-check, SQL `data_sources`, and interactive filter
examples are in [docs/chart-media-demos.md](docs/chart-media-demos.md).

`base-table` is the native Base record and Formula interface. Use logical
`--table-id` values with field-id JSON objects in `--records`; use
`record upsert --key-field-id` for key-based update-or-create and
`--expected-revision` when the caller already holds a revision. Base Formula is
column-level: `base-table formula compile` and `set` require `--field-id` and
`--expression`, never an A1 cell. Formula recalculation requires the logical
`--worksheet-name` as well as `--table-id`.

`db-table` remains a compatibility group for its existing metadata, import, and
style commands. Migrate Base record reads and writes to `base-table`; in
particular, replace `db-table range set-formula` with `base-table formula set`.
Likewise, `worksheet import --strategy replace` rejects a Base target instead of
performing a Sheet-style write; use `base-table record replace` for that target.

`db-table read`, `schema`, and `sample` keep reads bounded. The default read
limit is 100 rows, and `--limit` / `--offset` are capped at 10,000 to avoid
accidental large Base scans. `db-table create-from-query` is CLI-composed: it
executes raw SQL through `sql/result/query`, then creates a Base-backed table
through `db_table/create`. SQL sources are inferred from `FROM` / `JOIN`;
`--gid` remains optional compatibility context, while `--name` is always the
new Base table name. When SQL execution returns numeric-looking strings
such as `12.50` or `1.23e+06`,
create-from-query infers them as numbers; pure integer strings are inferred as
numbers only when they have fewer than 10 digits, keeping long identifiers as
text. `db-table
create-from-range` is also CLI-composed: it reads a source worksheet range,
reshapes header/data rows, then creates a Base on the target workbook.
Use a keyset-style backend contract for deeper pagination when it becomes
available.

`db-table update` currently supports keyed row updates through the routed
`update_range_by_lookup` backend contract:

```bash
mbs db-table update --doc-id abc123 --name Orders --key order_id --rows orders_patch.json
```

The `--key` column must be present in each row object and is used to match target
rows, for example `order_id`, `sku`, or `id`. Patch files may include only the
columns that should change; omitted columns are left unchanged. Blank values
overwrite existing values only when `--override` is passed. Positional updates
with `--row-index`, column-level updates with `--column-index` / `--column-name`,
and JSON-filter updates with `--where` remain planned until the backend exposes a
stable native db-table contract for those modes.

Refactor planning is tracked separately from this README:
`docs/superpowers/plans/2026-07-01-mbs-two-engine-command-refactor.md`.

Not recommended as first-class CLI commands:

```text
validation create/update/delete                  PLAN
protection protect/unprotect                     PLAN
sort range/table                                 PLAN
comment create/update/delete                     PLAN
named-range create/update/delete                 PLAN
macro/vba                                        SKIP
power-query                                      SKIP
power-pivot                                      SKIP
workbook diff                                    PLAN
dependency graph beyond cell lineage             PLAN
```

Relationship guide:

- Use `workbook` for file-level creation, upload/import, and manifest checks.
- Use `excel-worksheet read` for full worksheet used-range inspection.
- Use `excel-worksheet` for Excel tab lifecycle operations.
- Use `excel-worksheet range` for coordinate-based reads, writes, formulas, and
  cell lineage.
- Use `excel-table` for worksheet-backed table views. Table IDs are persistent
  registry IDs, rather than scan-order ordinals. When `--table-id` is omitted,
  the CLI selects the first active table returned for the worksheet. Use
  `metadata` to refresh the current range, `schema` or `read` to inspect
  headers, and `insert` to append one or more complete row objects.
- Use `table headers` for the agent-friendly header shortcut. It resolves
  `--table-id` and returns the selected table's current headers.
- Use `base-table` for native Base records and Formula fields. These are not
  worksheet ranges: use logical `--table-id` and `--field-id` contracts instead
  of A1 coordinates. Upsert discovers and uses backend `record_id` values.
- Use `db-table` only for its remaining compatibility commands, which continue
  to use human-readable `--name` or backend ids. `db-table create-from-query`
  executes raw SQL before creating a Base-backed table.
- Use `excel-worksheet row` and `excel-worksheet column` for structural
  row/column changes.
- Use `excel-worksheet style` for worksheet layout operations like freeze
  panes, filter ranges, widths, heights, gridlines, conditional formats, and
  worksheet style planning/apply. `mbs worksheet style ...` and `mbs style ...`
  expose the same style commands. For Base worksheets, `style format`,
  `style columns-width`, and `style cell batch-set` automatically update field
  metadata (`formatter`, text color, background color, and width) instead of
  requiring agents to choose Base-specific commands.
- Use `excel-worksheet chart` for list/get/create/update/delete of individual
  source-backed visualizations.
- Use `excel-worksheet dashboard` for local validation and worksheet-level chart
  orchestration such as manifest, create-config, and refresh.
- Use `pivot` for persisted pivot tables. Prefer `pivot preview` before writing,
  then `pivot upsert` with an explicit `--anchor-cell`; use `pivot delete`
  instead of manually clearing a pivot spill range.
- Use `excel-worksheet image` for list/read/insert/set/replace/delete image
  workflows.
- Use `version` for workbook history inspection and restore.
- Use `share` for visibility and collaborator access.

Targeting notes:

- Most commands accept `--doc-id`, `--url`, or `--uri`.
- Prefer `--worksheet-name` when the backend route supports names.
- Use `--table-id` when operating on an `excel-table`. It is the stable ID from
  persistent table metadata and remains valid across registry refreshes. Do not
  reuse a previously observed `range_address` after row insertion or deletion;
  resolve metadata again before an insert.
- Use logical `--table-id` for `base-table`; use `--field-id` for Base Formula
  commands and field-id keyed objects in `--records` for Base record commands.
- Use `--name` or backend ids only for remaining `db-table` compatibility commands.
- Use `--gid` only when a route specifically requires gid targeting.

## Input Rules

- Use `--doc-id`, `--url`, or `--uri` to identify a workbook.
- Use `--worksheet-name` when the endpoint supports it.
- Use `--table-id` when operating on an `excel-table`; omit it only to select
  the first active persistent table for the target worksheet.
- Use logical `--table-id` for `base-table`; use `--field-id` for Base Formula
  commands and field-id keyed objects in `--records` for Base record commands.
- Use `--name` or backend ids only for remaining `db-table` compatibility commands.
- Use `--gid` for gid-specific sheet targeting.
- File inputs like `--rows`, `--values`, `--targets`, and `--operations` must be JSON.

## Output

Default output is JSON. `base` is optimized for sheet-like payloads; `yaml` is also supported.

## Development

```bash
python3 -m venv .venv
. .venv/bin/activate
pip install -U pip
pip install -e .
```

Run tests:

```bash
python -m unittest discover -s tests -v
```
