Metadata-Version: 2.1
Name: maybeai-sheet-cli
Version: 0.26
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.

Store, preview, and materialize a worksheet SQL Config:

```bash
mbs sql config set --doc-id abc123 --worksheet-name SqlResult --sql-file result.sql --auto-refresh
mbs sql preview --doc-id abc123 --worksheet-name SqlResult --sql-file result.sql --output table
mbs sql overwrite --doc-id abc123 --worksheet-name SqlResult --confirm-overwrite
```

Use `mbs sql query --doc-id abc123 --sql-file result.sql --limit 100 --output table`
for a bounded, read-only raw SQL query that does not persist Config or write a
worksheet.

### 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 grant --doc-id abc123 --email teammate@example.com --permission 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 history 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`.


## Command Tree

This tree lists the documented executable command surface of the current CLI. Use
`mbs <group> --help` for the authoritative flags of a specific installed
version.

```text
mbs
├── update
│   └── Check for or install the latest maybeai-sheet-cli release
│
├── workbook
│   ├── create                 Create a workbook with an optional first worksheet and data
│   ├── create-from-file       Import an .xlsx file as a new workbook
│   ├── import                 Import a local file, remote URL, or worksheet into a workbook
│   ├── copy                   Create a workbook copy with a new title
│   ├── export                 Download a workbook as an .xlsx file
│   ├── calculate              Recalculate formulas across every worksheet
│   ├── list-user-workbooks    List the caller's workbooks with a bounded result
│   ├── list-worksheets        List worksheets and their engines in one workbook
│   ├── search                 Search the caller's workbooks by query
│   └── metadata               Inspect workbook routing and worksheet metadata
│
├── worksheet
│   ├── import                 Import file data or a source worksheet into an existing workbook
│   ├── convert-to-base        Convert one Sheet-backed worksheet to Base
│   ├── rename                 Rename a worksheet
│   ├── delete                 Delete a worksheet with explicit confirmation
│   ├── copy                   Copy a worksheet within the same workbook
│   └── move                   Move a worksheet to a zero-based position
│
├── excel-worksheet
│   ├── read                   Read worksheet cells, optionally from a bounded A1 range
│   ├── check-error            Scan a worksheet range for formula-like errors
│   ├── calculate              Recalculate formulas in one worksheet
│   ├── list-table             Detect persistent content-backed tables in a worksheet
│   ├── create                 Create an Excel-style worksheet
│   ├── metadata               Read metadata for one Excel-style worksheet
│   ├── range
│   │   ├── read               Read a bounded A1 range
│   │   ├── write              Write a JSON value matrix into an A1 range
│   │   ├── clear              Clear values from an A1 range
│   │   ├── search             Search worksheet values with bounded matches
│   │   ├── set-formula        Set the formula in one cell
│   │   ├── calculate          Evaluate one formula in a cell without persisting it
│   │   └── lineage            Trace formula dependencies for a cell or range
│   ├── row
│   │   ├── insert             Insert blank rows at a row index
│   │   ├── delete             Delete rows with explicit confirmation
│   │   └── move               Move a contiguous row block
│   ├── column
│   │   ├── insert             Insert blank columns at a column index
│   │   ├── delete             Delete columns with explicit confirmation
│   │   ├── move               Move a contiguous column block
│   │   └── width              Set the width of one or more columns
│   ├── style
│   │   ├── cell
│   │   │   └── batch-set      Apply one style payload to a cell range batch
│   │   ├── format             Set field or cell number format, colors, and width
│   │   ├── beautify           Apply automatic report and table styling
│   │   ├── columns-width      Set a range of column widths
│   │   ├── rows-height        Set a range of row heights
│   │   ├── freeze-panes       Freeze rows or columns at an A1 boundary
│   │   ├── gridlines
│   │   │   └── toggle         Show or hide worksheet gridlines
│   │   ├── auto-filter
│   │   │   ├── set            Set the persisted worksheet auto-filter range
│   │   │   └── remove         Remove the persisted worksheet auto-filter
│   │   ├── filter-values      Apply values to an existing worksheet filter
│   │   ├── conditional-formats
│   │   │   └── set            Set worksheet conditional-format rules from JSON
│   │   └── worksheet
│   │       ├── plan           Generate a worksheet style plan without writing
│   │       └── apply          Apply a worksheet style plan
│   ├── image
│   │   ├── list               List images anchored in a worksheet
│   │   ├── read               Download one worksheet image
│   │   ├── insert             Insert an image file at a cell anchor
│   │   ├── replace            Replace an existing image in place
│   │   ├── set                Move, resize, or update an image
│   │   └── delete             Delete one worksheet image
│   ├── chart
│   │   ├── list               List charts in a worksheet
│   │   ├── get                Read one chart specification and source binding
│   │   ├── create-config      Create one chart from a JSON specification
│   │   ├── update             Update one chart from a JSON specification
│   │   └── delete             Delete one chart
│   ├── dashboard
│   │   ├── manifest           Inspect dashboard worksheet metadata and charts
│   │   ├── create-config      Create a dashboard worksheet and charts from a specification
│   │   ├── validate           Validate a dashboard specification locally
│   │   └── refresh            Upsert dashboard charts and verify their inventory
│   └── media
│       └── check              Report whether a worksheet contains images or charts
│
├── excel-table
│   ├── headers                Read the header row of a persistent table
│   ├── metadata               Resolve persistent table identity and current range
│   ├── schema                 Return the table's columns inferred from persistent data
│   ├── read                   Read persistent table data with a bounded row limit
│   ├── sample                 Return a bounded, header-aware table sample
│   └── insert                 Append JSON row objects that match the current headers
│
├── base-table
│   ├── create                 Create a Base-backed table from JSON row data
│   ├── create-from-query      Run SQL and materialize its result as a Base table
│   ├── create-from-range      Read a worksheet range and create a Base table
│   ├── metadata               Read Base table metadata, dimensions, and optional headers
│   ├── schema                 Read native Base field schema
│   ├── sample                 Read bounded native Base fields and records
│   ├── read                   Read native Base fields and records
│   ├── record
│   │   ├── create             Create one to 1,000 native Base records
│   │   ├── replace            Replace the complete native Base record set
│   │   └── upsert             Update or create records by a field ID key
│   ├── field
│   │   ├── list               List native Base fields
│   │   ├── metadata           Read Base field display metadata and samples
│   │   └── update             Update a native Base field's metadata or style
│   └── formula
│       ├── compile            Validate a Base column formula expression
│       ├── set                Set a Base formula on one field
│       └── recalculate        Recalculate Base formula fields for a worksheet
│
├── db-table (compatibility)
│   ├── read                   Read legacy Base-backed rows by table name
│   └── field
│       └── batch-update       Apply multiple legacy Base field style updates
│
├── pivot
│   ├── read                   Read a pivot result from a JSON specification
│   ├── preview                Compute a pivot result without writing cells
│   ├── upsert                 Write or update a persisted pivot at an anchor cell
│   └── delete                 Delete a persisted pivot with explicit confirmation
│
├── sql
│   ├── config
│   │   ├── get                Read the stored SQL Config for a worksheet
│   │   ├── set                Store SQL Config and optionally enable auto-refresh
│   │   └── delete             Delete a worksheet's stored SQL Config
│   ├── preview                Preview SQL output for a result worksheet
│   ├── query                  Run a bounded, read-only raw SQL query
│   ├── overwrite              Materialize SQL output into a worksheet
│   └── migration
│       ├── preview            Scan legacy SQL formulas without modifying the workbook
│       └── commit             Migrate approved legacy SQL formulas to SQL Config
│
├── history
│   ├── list                   List bounded workbook version history
│   ├── read                   Read a worksheet range from one history version
│   └── restore                Restore a workbook to one history version
│
├── share
│   ├── visibility             Get or set workbook public/private visibility
│   ├── grant                  Grant a collaborator a workbook permission
│   ├── permission             Read the caller's workbook permission
│   ├── remove                 Remove a collaborator's workbook access
│   └── list                   List workbook collaborators and permissions
│
└── formula
    ├── read                   Read formulas and values from a worksheet range
    ├── set                    Persist one formula in a worksheet cell
    ├── batch-set              Persist multiple formulas from JSON
    ├── calculate              Calculate one formula at a worksheet cell
    ├── recalculate            Recalculate formulas across a workbook
    └── lineage                Trace formula dependencies for a cell or range
```

## Command Examples

These examples use commands available in the current CLI.

```bash
# update
mbs update --check

# workbook
mbs workbook create --title "Board Pack" --sheet-name Summary --data rows.json
mbs workbook create-from-file ./report.xlsx --engine base
mbs workbook import ./report.xlsx
mbs workbook import ./orders.csv --engine base
mbs workbook import "https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit#gid=0" --engine sheet
mbs workbook copy --doc-id abc123 --title "Budget Copy"
mbs workbook export --doc-id abc123 --out workbook.xlsx
mbs workbook calculate --doc-id abc123
mbs workbook list-user-workbooks --limit 20
mbs workbook list-worksheets --doc-id abc123
mbs workbook search --query "Budget" --limit 20
mbs workbook metadata --doc-id abc123

# worksheet
mbs worksheet import ./report.xlsx --strategy create --doc-id target123 --source-worksheet-name "联盟" --target-worksheet-name "联盟导入" --engine base --verify
mbs worksheet import ./report.xlsx --strategy create --doc-id target123 --source-worksheet-name "联盟" --source-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 ./rows.json --strategy replace --doc-id target123 --worksheet-name Students --verify
mbs worksheet import --strategy create --doc-id target123 --source-doc-id source123 --source-worksheet-name "1店" --engine base --verify --output json
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
mbs worksheet rename --doc-id abc123 --worksheet-name Old --new-name New
mbs worksheet delete --doc-id abc123 --worksheet-name Sheet1 --yes
mbs worksheet copy --doc-id abc123 --worksheet-name Source --new-name Copy
mbs worksheet move --doc-id abc123 --worksheet-name Sheet1 --index 0

# excel-worksheet
mbs excel-worksheet read --doc-id abc123 --worksheet-name Actuals
mbs excel-worksheet read --doc-id abc123 --worksheet-name Actuals --range A1:D20
mbs excel-worksheet check-error --doc-id abc123 --worksheet-name Model --range B2:H80
mbs excel-worksheet calculate --doc-id abc123 --worksheet-name Model
mbs excel-worksheet create --doc-id abc123 --name Actuals
mbs excel-worksheet metadata --doc-id abc123 --worksheet-name Actuals
mbs excel-worksheet list-table --doc-id abc123 --gid 3

# excel-worksheet range
mbs excel-worksheet range read --doc-id abc123 --worksheet-name Actuals --range A1:D20  # advanced structured alias
mbs excel-worksheet range calculate --doc-id abc123 --worksheet-name Model --cell E2 --formula "=SUM(B2:D2)"
mbs excel-worksheet range set-formula --doc-id abc123 --worksheet-name Model --cell E2 --formula "=SUM(B2:D2)"
mbs excel-worksheet range lineage --doc-id abc123 --worksheet-name Model --range B14:D14 --format tree

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

# base-table
mbs base-table create --doc-id abc123 --name Orders --rows orders.json
mbs base-table create-from-query --doc-id abc123 --name OrderSummary --sql-file order_summary.sql
mbs base-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
mbs base-table metadata --doc-id abc123 --name orders-large --include-headers
mbs base-table schema --doc-id abc123 --table-id tbl_orders
mbs base-table sample --doc-id abc123 --table-id tbl_orders --limit 50
mbs base-table read --doc-id abc123 --table-id tbl_orders --limit 100
mbs base-table record create --doc-id abc123 --table-id tbl_orders --records records.json --expected-revision 7  # create 1 to 1,000 records
mbs base-table record replace --doc-id abc123 --table-id tbl_orders --records records.json --expected-revision 7
mbs base-table record upsert --doc-id abc123 --table-id tbl_orders --key-field-id fld_sku --records records.json
mbs base-table field list --doc-id abc123 --table-id tbl_orders
mbs base-table field metadata --doc-id abc123 --name ActualsTable
mbs base-table field update --doc-id abc123 --table-id tbl_orders --field-id fld_amount --formatter "#,##0.00" --expected-revision 7 --verify
mbs base-table field update --doc-id abc123 --name Orders --field amount --formatter "#,##0.00" --verify  # resolves native table and field IDs
mbs base-table formula compile --doc-id abc123 --table-id tbl_orders --field-id fld_total --expression '{fld_qty} * {fld_price}'
mbs base-table formula set --doc-id abc123 --table-id tbl_orders --field-id fld_total --expression '{fld_qty} * {fld_price}' --result-type number
mbs base-table formula recalculate --doc-id abc123 --table-id tbl_orders --worksheet-name Orders

# db-table
mbs db-table read --doc-id abc123 --name orders-large --limit 100 --offset 200
mbs db-table field batch-update --doc-id abc123 --name ActualsTable --updates field-updates.json

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

# excel-worksheet column
mbs excel-worksheet column width --doc-id abc123 --worksheet-name Actuals --column B --width 120

# formula
mbs formula read --doc-id abc123 --worksheet-name Model --range A1:E20
mbs formula set --doc-id abc123 --worksheet-name Model --cell E2 --formula "=SUM(B2:D2)"
mbs formula batch-set --doc-id abc123 --operations formulas.json --recalculate-mode worksheet
mbs formula calculate --doc-id abc123 --worksheet-name Model --cell E2 --formula "=SUM(B2:D2)" --no-save-result
mbs formula recalculate --doc-id abc123 --worksheet-name Model
mbs formula lineage --doc-id abc123 --worksheet-name Model --cell E2 --format tree

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

# 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
mbs excel-worksheet dashboard manifest --doc-id abc123 --worksheet-name Dashboard
mbs excel-worksheet dashboard create-config --doc-id abc123 --spec dashboard.json --create-worksheet
mbs excel-worksheet dashboard refresh --doc-id abc123 --spec dashboard.json

# 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
mbs pivot read --doc-id abc123 --spec pivot.json
mbs pivot upsert --doc-id abc123 --target-worksheet-name PivotResult --anchor-cell A1 --spec pivot.json
mbs pivot delete --doc-id abc123 --worksheet-name PivotResult --anchor-cell A1 --dry-run
mbs pivot delete --doc-id abc123 --worksheet-name PivotResult --anchor-cell A1 --yes

# sql
mbs sql config get --doc-id abc123 --worksheet-name SqlResult
mbs sql config set --doc-id abc123 --worksheet-name SqlResult --sql-file result.sql --auto-refresh
mbs sql config delete --doc-id abc123 --worksheet-name SqlResult
mbs sql preview --doc-id abc123 --worksheet-name SqlResult --sql-file result.sql --output table
mbs sql query --doc-id abc123 --sql-file result.sql --limit 100 --output table
mbs sql overwrite --doc-id abc123 --worksheet-name SqlResult --confirm-overwrite
mbs sql migration preview --doc-id abc123
mbs sql migration commit --doc-id abc123 --candidate-id <candidate-id> --allow-manual-candidates

# 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
mbs excel-worksheet style cell batch-set --doc-id abc123 --worksheet-name Actuals --range A1:G1 --style header_style.json
mbs worksheet style format --doc-id abc123 --worksheet-name Actuals --field amount --formatter "#,##0.00" --color "#E11D48"
mbs style beautify --doc-id abc123
mbs style beautify --doc-id abc123 --worksheet-name Actuals --table-id 2
mbs style columns-width --doc-id abc123 --worksheet-name Actuals --start-column M --end-column M --width 144
mbs excel-worksheet style conditional-formats set --doc-id abc123 --worksheet-name Actuals --spec conditional_formats.json
mbs excel-worksheet style worksheet apply --doc-id abc123 --worksheet-name Actuals --mode auto_detect --spec worksheet_style.json

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

# history version
mbs history list --doc-id abc123 --limit 10
mbs history read --doc-id abc123 --version v42 --worksheet-name Sheet1 --range A1:D20
mbs history restore --doc-id abc123 --version 42 --reason "Rollback invalid import" --yes

# share
mbs share grant --doc-id abc123 --email teammate@example.com --permission viewer
```

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 legacy row and style commands.
Use `base-table` for Base table lifecycle, metadata, schema, samples, native
records, and Formula operations; `db-table create*`, `metadata`, `schema`, and
`sample` remain available for script compatibility. 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` keeps reads bounded. `base-table sample` and `read` use native
`table/read`, with a default limit of 100 rows and a maximum of 1,000. `base-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. `base-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.

Relationship guide:

- Use `workbook` for file-level creation, upload/import, export, and discovery.
- 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 `excel-table headers` for the agent-friendly header shortcut. It resolves
  `--table-id` and returns the selected table's current headers.
- Use `base-table` for Base table creation, native records, and Formula fields.
  Creation uses a human-readable `--name`; native record and formula operations
  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.
- 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 `history` 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
```
