- SKILL.md: clarify --url only resolves /sheets/ and /spreadsheets/ links; /wiki/ links must be resolved via wiki +node-get first (confirm obj_type=sheet, use obj_token)
- formula-translation: document IMPORTRANGE cross-workbook limits (max 5-level nesting, 100 refs per sheet)
- write-cells: document rich_text cells for hyperlinks, @mentions and @docs
E2E test cases repeatedly trip on this:
$ lark-cli sheets +cells-batch-set-style \
--ranges '["7f8fba!A2:B3","7f8fba!C2:D3"]' --font-color '#3366FF' ...
→ tool "batch_update" failed: [900015206]
sheet "7f8fba" not found. Available sheets: [{id: "7f8fba", name: "Sheet1"}]
Callers paste the hex sheet-id (e.g. "7f8fba") from a spreadsheet URL /
+sheet-create response straight into the --ranges sheet prefix. The four
batch shortcuts (+cells-batch-set-style / +cells-batch-clear /
+dropdown-update / +dropdown-delete) fan each range out into a
batch_update sub-op (set_cell_range / clear_cell_range) and pass the
prefix through as sheet_name; the server only matches sheet_name
literally, so the lookup fails.
The set_cell_range tool schema is explicit: sheet_id is the
reference_id and "must be correct or it errors"; sheet_name is the
display name. CLI can't disambiguate purely from the literal because
users can rename sheets to anything (including six-char hex strings).
Cleanest fix is at the source: each batch shortcut's --ranges flag
description now states explicitly that the prefix must be the sheet
display name and that the sheet reference_id is rejected, so agents
reading the reference don't try the id form in the first place.
No Go changes; these files are regenerated from the upstream Lark Base
Shortcut-flags table via the sheet-skill-spec sync chain.
Server tools-schema.json for get_cell_ranges, get_range_as_csv and set_range_from_csv
does not accept a sheet prefix on --range / --start-cell; the sheet is selected via
--sheet-id / --sheet-name. +csv-put --start-cell also now states it must be a single
cell (no range notation).
Synced from spec repo.
Align +dropdown-get with its get_cell_ranges siblings (+cells-get / +csv-get):
sheet selection is now via --sheet-id / --sheet-name (XOR) and --range is a
bare A1 reference. The previous shape required the sheet prefix inside --range
(e.g. "Sheet1!A2:A100") and was the odd one out among the read-data wrappers;
callers pasting the sheet-id form straight from the URL hit a misleading
"sheet not found, sheetId: , sheetName: <id>" error because the prefix was
unconditionally treated as sheet_name.
Flag schema + skill reference regenerated from the upstream Lark Base
Shortcut-flags table.
+float-image-create now accepts a local file via --image (XOR with
--image-token / --image-uri): the CLI uploads it as a sheet_image and
embeds the returned file_token, removing the previous "upload elsewhere
to get a token first" workaround. Path safety is checked in Validate,
--dry-run previews the extra upload step, and +batch-update rejects
--image (no upload phase). +float-image-update is unchanged (it does not
register --image).
Also syncs the lark-sheets skill docs/flag-defs from sheet-skill-spec:
the new --image flag, partial-merge / border-per-side / bare sheet-prefix
clarifications, and refreshed dropdown --colors/--highlight descriptions
(already pending in the source Base table).
The unsafe-path check only ran at Execute (via FileIO.Stat), so --dry-run
printed a misleading success preview for an absolute / out-of-cwd --image
path that a real run would then reject. Move the path-safety check into
Validate (validate.SafeLocalFlagPath), so --dry-run and Execute fail
identically and both name the real --image flag. File existence stays
deferred to Execute, so legitimate relative paths still preview cleanly.
Add TestCellsSetImage_DryRunRejectsUnsafePath.
Mirror sheet-skill-spec generated/ into shortcuts/sheets/data/ and
skills/lark-sheets/ for the +dropdown-set / +dropdown-update path. No
hand edits in this repo.
The +dropdown flag desc and the Dropdown 配色 narrative now match the
server-side enable_highlight default flip (true) and the tri-state
--highlight semantics introduced in the sibling commit:
* --highlight desc: 不传 = 开(按内置 10 色色板循环上色),
--highlight=false 关闭得到纯白下拉
* --colors desc: 单独传即生效(高亮默认开),--highlight=false 时忽略
* write-cells reference: 三种意图三条线(默认色板 / 指定颜色 /
纯白下拉)+ 新增 --highlight=false 示例
Source upstream: sheet-skill-spec MR !8.
The server-side default for data_validation.enable_highlight flipped from
false to true (aligning with the UI behavior). With the previous code path
if runtime.Bool("highlight") { dv["enable_highlight"] = true }
omitting --highlight and passing --highlight=false both produced the same
"enable_highlight key absent" body, leaving CLI users with no way to opt
out of the (now-default) highlighting.
Switch to runtime.Changed() so the translator can distinguish all three
input shapes:
- omitted -> no enable_highlight key (server applies default=true)
- --highlight=true -> enable_highlight: true (explicit no-op vs default)
- --highlight=false -> enable_highlight: false (the only opt-out path)
flagView already exposes Changed() and mapFlagView (the +batch-update
sub-op adapter) implements it via raw-key presence — same pattern other
translators use for "Changed-only" branching (e.g. omit target_index
unless --index was set), so no interface surface change is needed.
Test coverage:
- TestDropdownSet_HighlightTriState pins all four shapes (omit / presence
form / explicit true / explicit false) and asserts the enable_highlight
key's presence/value
- TestBatchOp_BodyMatchesStandalone adds a --highlight=false sub-op case
so the batch sub-op path produces a body byte-identical to the
standalone +dropdown-set --highlight=false body
Two test fallouts from rebasing onto upstream 4be06c8 (which independently
re-fixed +workbook-create and +dim-move with a more thorough approach):
- shortcuts/sheets/lark_sheet_workbook_test.go: our PR's earlier
TestWorkbookCreate_DryRun "with headers and data → 2-step plan" subtest
asserted the expedient sheet_name="Sheet1" / no-sheet_id wire body that
matched our dropped fix#1 implementation. Upstream's fix#1 resolves
the workbook's first sheet via get_workbook_structure and fills with
the real sheet_id instead. Reset this file to upstream's version — our
superseded assertions disappear, upstream's tests cover the new wire
shape.
- shortcuts/sheets/execute_paths_test.go: TestExecute_RangeSort fixture
still used the legacy {col, order} sort-key shape because the rebase
resolution picked the upstream version of this file wholesale (it
contained other unrelated changes). Re-apply just the one fixture
update to {column, ascending} so fix#5's CLI-side rejection logic
exercises a valid input — server-side sort_conditions has required
fields `column` (string) and `ascending` (bool); the historical
{col, order} vocabulary was never accepted.
go build ./... + go test ./shortcuts/sheets/... -count=1 both green.
The earlier commit 49104ec swapped --colors out of parseJSONFlag's "Used
by" example list when it deleted the flag (item #2 there removed --colors
/ --highlight from +dropdown-set/-update). Subsequent commits 8672d8e /
538eb2e / fb90c8b reinstated --colors (and added --source-range) but did
not roll back this docstring tweak — leaving an orphan reference to
--properties where --colors used to be.
This restores the example list to its pre-49104ec form so the docstring
matches what the helper actually services on this branch's HEAD.
Pure docstring change — function behavior unaffected, no test movement.
Mirrors sheet-skill-spec MR !7 60df610 — narrative now describes how the
flags interact (XOR, colors length rule, highlight gating, sheet-prefix
read-back gotcha) without exposing the underlying data_validation field
names or server-side normalization details that agents don't act on.
No Go-side change, no shortcut behavior change.
Previously +dropdown-set / +dropdown-update only emitted
data_validation.type=list — agents wanting listFromRange (dropdown options
sourced from existing cells, kept in sync with that range) had to drop down
to +cells-set and hand-build a data_validation map. The flag now exposes it
natively as --source-range, paired with --options under XOR.
CLI changes:
- shortcuts/sheets/lark_sheet_write_cells.go:
* new dropdownTypeAndItems(runtime) — central XOR resolver: rejects 0 or
2 of {--options, --source-range}, returns (sourceSize, partial dv with
type+items|range filled in). Source size = options length for list
mode, rangeDimensions(--source-range) cell count for listFromRange.
* buildDropdownValidation rewritten to call the resolver, then layer
--colors / --multiple / --highlight on top — semantics unchanged
for callers, just two modes instead of one.
* validateDropdownOptions / -Colors renamed to validateDropdownSourceOrOptions
so the XOR + length check fires at +dropdown-update Validate time too.
* --colors length error message generalized: "must not exceed dropdown
source size (N)" (covers both modes).
- shortcuts/sheets/lark_sheet_batch_update.go: rename call site.
- shortcuts/sheets/lark_sheet_write_cells_test.go: 4 new tests —
ListFromRange (happy path: range + items absent + colors + highlight all
emit), ListFromRange_ColorsLongerThanCells (overflow against T1:T3 cell
count), XorBothSet, XorNeitherSet. Updated the existing
ColorsLongerThanOptions assertion to match the new "source size" wording.
Spec-driven changes (synced via npm run sync:cli from sheet-skill-spec
MR !7 2c298b6):
- shortcuts/sheets/data/flag-defs.json: --options Required flips to xor on
+dropdown-set/-update; new --source-range row gains long-form description
pointing at server data_validation.range + the XOR semantics.
- skills/lark-sheets/references/lark-sheets-write-cells.md: "Dropdown 配色"
section reorganized into "Dropdown 选项 + 配色" — XOR comparison table
(list vs listFromRange), shared config flag table (--highlight /
--colors), explicit length rule covering both modes, side-by-side
minimal examples, server-range-normalization gotcha callout.
- skills/lark-sheets/references/lark-sheets-batch-update.md pointer updated
to mention both modes + that +dropdown-delete is unaffected.
PPE smoke (ppe_lark_cli_sheet) on UFJxszjrZhZ1LVtc9FdcICSbn6b C column:
- +cells-set C1 → "性别" (bold + centered): updated_cells_count=1
- +dropdown-set --range C2:C21 --source-range "Sheet1!T1:T3" --colors
'["#cce8ff","#ffd6e7","#e6e6e6"]' --highlight: updated_cells_count=20
- read-back: data_validation.type=listFromRange + range=$T$1:$T$3 (server
normalizes the prefix away on storage; highlight_colors /
enable_highlight not echoed by get_cell_ranges, see byted-sheet read
projection TODO).
- error-path replay (both XOR violations + colors > source-size) all
rejected at Validate stage with the expected messages.
Catches up to sheet-skill-spec's 2026-05-25 base sync (MR !7) after
rebasing onto upstream feat/lark-sheets-refactor (12 new upstream commits
including the lark-sheets skill refactor + tools-schema migration).
Spec changes flowing in:
- highlight_colors description loosened: length may be **shorter than**
--options (server cycles remaining slots through a built-in 10-color
palette); previously the tool errored on any length mismatch.
- shortcuts/sheets/data/flag-schemas.json: mass re-mirror — generator now
emits `type` before `properties` and adds explicit `additionalProperties:
false` on object schemas (cosmetic, no behavior change).
- skills/lark-sheets/references/lark-sheets-{batch-update,chart,write-cells}.md:
--options gains the type='list' tag; data_validation inline field-count
goes 7 → 9 (catches up the highlight schema in the summary); chart
position / size marked optional per upstream.
Go-side adjustment:
- buildDropdownValidation / validateDropdownOptionsColors: change the
--colors length check from strict-equal to "must not exceed --options"
to match the relaxed schema.
- TestDropdownSet_ColorsLengthMismatch -> TestDropdownSet_ColorsLongerThanOptions
(now hits the overflow path with 3 colors vs 2 options).
- New TestDropdownSet_ColorsShorterAccepted: 2 colors vs 4 options is
legal and forwarded as-is.
Reverses the --colors / --highlight removal from 7932ab2 (item #2 of the
batch-1 schema-alignment commit). That commit dropped both flags after the
test report flagged data_validation.colors / highlight_options as "unexpected
property" — at the time the canonical set_cell_range.data_validation schema
listed only help_text / items / operator / range / support_multiple_values /
type / values, so the flags had no server-side target and the removal was
correct.
Since then, set_cell_range.data_validation has gained two fields explicitly
modelling the dropdown highlight UI (mcp-tools.json in sheet-skill-spec
2026-05-22 base sync):
enable_highlight (bool) — show pill backgrounds
highlight_colors (string[]) — hex pill colors, length must match items
So the flags are back, but rewired:
--colors -> data_validation.highlight_colors (was: colors)
--highlight -> data_validation.enable_highlight (was: highlight_options)
--options -> items and --multiple -> support_multiple_values renames from
7932ab2 are kept.
Changes:
- buildDropdownValidation: re-add --colors / --highlight handling against
the new field names; --colors length check stays inline (so dropdownSetInput
Validate path catches it via validateViaInput, no separate guard needed).
- validateDropdownOptions -> validateDropdownOptionsColors: restore the
Validate-time --colors length check on +dropdown-update / +dropdown-delete
(called from lark_sheet_batch_update.go).
- TestDropdownSet_CellsShape: extend to assert highlight_colors /
enable_highlight emitted; assert legacy `colors` / `highlight_options`
absent.
- TestDropdownSet_ColorsLengthMismatch: new — covers the early Validate
error path.
- TestDropdownUpdate_BatchPayload: extend to cover dropdownBatchInput
propagation of --colors / --highlight through batch_update.
- skills/lark-sheets/references/lark-sheets-{write-cells,batch-update}.md,
shortcuts/sheets/data/flag-defs.json, flag-schemas.json: synced from
sheet-skill-spec generate output (MR !7).
Upstream sheet-skill-spec base table deleted the --colors and --highlight
flags on +dropdown-set / +dropdown-update (the corresponding wire fields
data_validation.colors / .highlight_options were never accepted by the
server schema; see prior fix in this branch). Re-running the sync from
canonical-spec brings the CLI flag-defs and skill reference docs back in
line with the Go-side handling that already drops these fields.
Generated by `npm run sync:cli` in sheet-skill-spec @ ac7acef.
These md files are sync targets generated from sheet-skill-spec; editing
them here gets clobbered on the next sync, same as data/flag-defs.json.
The --colors / --highlight row removals belong on the upstream base
table → canonical-spec sync, not here.
Restore the previous --colors / --highlight rows in both
lark-sheets-write-cells.md (+dropdown-set) and lark-sheets-batch-update.md
(+dropdown-update). The Go-side change in buildDropdownValidation still
drops the wire fields, so:
- users passing --colors / --highlight today see the flag accepted
silently (no effect on the wire) until upstream removes the flag;
- after upstream removal + sync, both flag declarations, ref docs, and
Go-side handling will be in sync.
Functional fixes (#1 workbook-create, #3 dropdown-get, #4 dim-move,
#5 range-sort) and dropdown wire-shape rename (#2) are unaffected.
data/flag-defs.json is regenerated from the upstream sheet-skill-spec
canonical-spec; editing it here gets clobbered on the next sync. The
schema realignment for +dropdown-set / -update --colors / --highlight
removal needs to land on the base table first, then flow back through
sheet-skill-spec → larksuite-cli sync, not via a direct CLI-side edit.
Restore the previous flag entries verbatim. The Go-side change in
buildDropdownValidation still drops the wire fields, so:
- users passing --colors / --highlight today see the flag accepted
silently (no effect on the wire) until the upstream removal lands;
- after upstream removal + sync, both the flag declarations and the
Go-side handling will be in sync.
Functional fixes (#1 workbook-create, #3 dropdown-get, #4 dim-move,
#5 range-sort) and dropdown wire-shape rename (#2) are unaffected.
Five separate E2E failures in shortcuts/sheets/ that all trace back to a
CLI ↔ server contract mismatch. Each is independently scoped; bundling
them because they share the test-report citation and the same one-line
fix shape in most cases.
buildInitialFillInput sent {"sheet_id": ""} on the secondary
set_cell_range call after creating the workbook. The empty value was a
holdover from "...otherwise server picks first sheet" — but
set_cell_range rejects an empty selector with
"sheet_id or sheet_name is required" rather than falling back to the
default sheet.
Use sheet_name "Sheet1" instead. POST /sheets/v3/spreadsheets always
creates that sheet on workbook creation, and set_cell_range accepts
sheet_name as an equivalent selector — saves an extra
get_workbook_structure round-trip just to learn the auto-generated id.
buildDropdownValidation emitted four fields that don't exist in the
canonical set_cell_range.data_validation schema:
- "values" (options list) → renamed to "items"
- "multiple_values" → renamed to "support_multiple_values"
- "colors" (per-option color) → removed (not in schema; flag also
removed from data/flag-defs.json
for +dropdown-set / -update)
- "highlight_options" → removed (not in schema; flag also
removed)
The canonical schema lives at sheet-skill-spec/canonical-spec/tool-
schemas/mcp-tools.json (set_cell_range tool, data_validation property);
the colors / highlight knobs were CLI inventions the server never
accepted, so removing the flags is correct (renaming would leave the
flags broken). Skill reference docs (write-cells.md, batch-update.md)
synced.
validateDropdownOptionsColors lost its colors check; renamed to
validateDropdownOptions to reflect the narrower contract.
dropdownGetInput sent "Sheet1!C2:C6" verbatim as a ranges[] entry.
get_cell_ranges expects sheet_id / sheet_name as separate fields and
ranges entries without the sheet prefix; the server bounced with
"sheet not found, sheetId:" (empty).
Use the existing splitSheetPrefixedRange helper (declared in
lark_sheet_batch_update.go) to break "Sheet1!C2:C6" into ("Sheet1",
"C2:C6"), then thread the sheet name through sheetSelectorForToolInput
exactly like +cells-get does.
The shortcut was POSTing to /sheets/v2/spreadsheets/{token}/dimension_
range, which is the v2 insert-dimension endpoint and requires a top-
level {"dimension": {...}} body. Move uses a separate endpoint:
POST /sheets/v2/spreadsheets/{token}/move_dimension
body: { "source": {...}, "destination_index": N }
(camelCase "destinationIndex" → snake_case "destination_index" to
match the v2 contract.) Both DryRun and Execute updated, plus the
TestDimMove_DryRun and TestExecute_DimMove assertions.
transform_range.sort_conditions[i] requires both `column` (string) and
`ascending` (bool); rangeSortInput passed the --sort-keys array through
to the server unvalidated, so missing fields surfaced as opaque
"required property X missing" errors with no per-item context.
Walk the parsed array client-side, reject with item-pointing messages.
Test fixtures and a contract-test fixture switched from the historical
{col, order} vocabulary (which the server has never accepted) to the
correct {column, ascending}.
Server-schema citations and test-report case mapping in this branch's
plan file.
+workbook-create: the v3 create response does not echo the default sheet's id, so the initial-fill set_cell_range was sent with an empty sheet_id and rejected ("sheet_id or sheet_name is required"). Resolve the workbook's first sheet via get_workbook_structure before filling.
+dim-move: the move request was POSTed to the v2 dimension_range endpoint (the add/update/delete surface, which requires a `dimension` object) and rejected with "[9499] Missing required parameter: Dimension". Switch to the native v3 move_dimension endpoint (sheet_id in path; snake_case source.{major_dimension,start_index,end_index} + destination_index). CLI --end and v3 end_index are both 0-based inclusive, so they pass through unchanged.
Refine reference docs from upstream sheet-skill-spec (core-operations,
formula-translation, visual-standards, SKILL guidance). Description-only;
no flag or CLI behavior change.
Trim and refine reference docs from upstream sheet-skill-spec
(condense core-operations workflow, tidy write-cells / range-operations /
float-image / SKILL guidance). Description-only; no flag or CLI behavior
change.
Regenerate flag-schemas.json from upstream sheet-skill-spec: the chart
properties schema now marks position and size as required, and the chart
reference doc reflects the same. flag-schemas.json is print-schema-only
(no client-side validation), so this is a generated-artifact + doc sync
with no CLI behavior change.
Refine reference docs and flag-defs descriptions from upstream
sheet-skill-spec (--depth wording for +dim-group / +dim-ungroup,
plus assorted reference clarifications). Description-only; no CLI
behavior or flag surface change.
Sync skills/lark-sheets references from spec: drop --cell-limit / --max-rows
guidance; 大表分批读 switches to --range row windows + --max-chars auto cap + has_more.
Mirrors sheet-skill-spec 58e7456 and handler change 2befc49.
Drop --cell-limit (+cells-get) and --max-rows (+csv-get) from the CLI surface
and pin the underlying tool's cell_limit / max_rows to a very large sentinel so
the tool's own defaults never truncate before --max-chars. --max-chars stays the
only knob (default 200000, unchanged).
- lark_sheet_read_data.go: add unboundedReadLimit (1e9); cellsGetInput pins
cell_limit, csvGetInput pins max_rows; --max-chars still passed through
- data/flag-defs.json: synced from spec (drops the two flags)
- tests: spot-check moved to --max-chars; dry-run wantInput asserts cell_limit /
max_rows are pinned high
Mirrors sheet-skill-spec (Base flag records removed).
go build ./... + go test ./shortcuts/sheets/ green.
Sync lark-sheets skill references and flag schemas from upstream
sheet-skill-spec, and wire the newly-specced --copy-to-range flag into
+cells-set: it passes copy_to_range to the set_cell_range tool so a
template block written via --cells fans out across a larger range with
auto-shifted formula refs.
Pull latest spec from sheet-skill-spec (PR ee/sheet-skill-spec!6 + earlier
develop commits) into skills/lark-sheets/ and shortcuts/sheets/data/.
Audit findings now reflected in CLI docs:
- A2 +cond-format-create example: --rule-type duplicate → duplicateValues
- A3 +cond-format-create Validate: cellValue/formula → cellIs/expression
- A5 +csv-put examples: --range → --start-cell; drop redundant --allow-overwrite
- A7 +sparkline-create: Validate / Examples aligned with real schema
(config/sparklines), executable JSON example added
- B13 cross-doc dead links: lark_sheet_*/cli-shortcuts.md → lark-sheets-*.md
- C2 +csv-put: `=` literal warning next to Examples
- CC5 +rows-resize/+cols-resize --type auto: single point of truth in
range-operations reference
flag-defs.json description / required sync (from base):
- A4 +float-image-update: image-name/position-*/size-* required → optional
(patch mode)
- A8 +dim-move --start/--end description cleanup
- B3 +pivot-create --properties: data_range → source (real field name)
Also picks up the +cells-batch-clear shortcut doc (introduced in spec
develop). Go-side implementation for that shortcut is intentionally not
in this PR — docs-only preview; runtime dispatch will land in a follow-up.
`go test ./shortcuts/sheets/...` passes.
manage_sparkline_object uses two layers of IDs: --group-id picks the
sparkline group, and properties.sparklines[i].sparkline_id picks each
item inside the group. The server contract requires sparkline_id on
every update item (server maps each entry back to an existing
sparkline by this id). Agents that called +sparkline-update without
the per-item ids hit an opaque server-side rejection that didn't
mention sparkline_id at all, then got stuck in a try-fail-list-retry
loop.
Pre-check CLI-side in objectUpdateInput via a new validateUpdateInput
hook on objectCRUDSpec. sparklineSpec wires validateSparklineUpdateItems,
which walks properties.sparklines[] and rejects with a message that
points at +sparkline-list:
+sparkline-update properties.sparklines[N] missing sparkline_id
(run `+sparkline-list --group-id <id>` first to read sparkline_id
for each item, then echo each id back on the corresponding update
entry)
Scope is update-only. config-only updates (properties.config without
sparklines) stay legal — the validator skips when sparklines is
absent. Delete is not pre-checked: objectDeleteInput doesn't pass
properties through, so the partial-delete branch can't be reached
today (separate follow-up).
Tests:
- TestObjectCRUDShortcuts_DryRun: positive case for update with
sparkline_id present.
- TestSparklineUpdate_MissingSparklineID: standalone path — error
contains both "missing sparkline_id" and "+sparkline-list".
- TestBatchOp_RejectsBadSubOpInput: batch sub-op missing sparkline_id
rejected with the same friendly error.
Docs synced from sheet-skill-spec (canonical change committed there):
skills/lark-sheets/references/lark-sheets-sparkline.md documents the
two-layer id model, the three "+sparkline-list first" cases, and both
delete modes.
- skills/lark-shared/SKILL.md: drop the generic "prefer stdin" section
- skills/lark-sheets/SKILL.md: add expanded stdin guidance (use stdin over @file abs paths; don't cd or write into the project dir)
- skills/lark-sheets/references/lark-sheets-sparkline.md: document the group_id / sparkline_id two-tier model with worked examples
Clear content/formats across many sheet-prefixed ranges in a single atomic
batch_update (one clear_cell_range op per range), mirroring the existing
+cells-batch-set-style / +dropdown-{update,delete} fan-out wrappers. The
--scope to clear_type normalization is shared with standalone +cells-clear
(normalizeClearType) so the two stay in lockstep.
high-risk-write (requires --yes); rejected as a batch sub-op like the other
fan-out wrappers. flag-defs/flag-schemas and skill docs updated to match.
Two latent bugs in the object_crud translator surfaced during BOE smoke
testing of +batch-update. Both are schema-alignment fixes against
manage_conditional_format_object / manage_filter_object as declared in
sheet-skill-spec/canonical-spec/tool-schemas/mcp-tools.json.
#4 +cond-format: rule_type path + enum vocabulary
---------------------------------------------------
condFormatEnhance used to write the user's --rule-type value into
`properties.rule.type` (nested under a `rule` object). The server
schema actually puts it at flat `properties.rule_type` and silently
drops the nested form — so every conditional-format create/update
secretly built the wrong document.
Worse, the CLI enum exposed via flag-defs.json was its own invented
vocabulary (cellValue / formula / duplicate / unique / topBottom /
aboveBelowAverage / dataBar / colorScale / iconSet / textContains /
dateOccurring / blankCell / errorCell) — none of those values were
the strings the server accepts.
Fix:
- condFormatEnhance now writes `properties.rule_type = <value>`
directly (no nested `rule` object).
- Synced flag-defs.json + lark-sheets-conditional-format.md enum
vocabulary from base to match the server: duplicateValues,
uniqueValues, cellIs, containsText, timePeriod, containsBlanks,
notContainsBlanks, dataBar, colorScale, rank, aboveAverage,
expression, iconSet.
- ⚠️ Breaking: scripts passing the old CLI-invented enum values
(e.g. --rule-type cellValue) now get a cobra "invalid value …
allowed: …" error listing the new vocabulary. No alias layer.
- TestObjectCRUDShortcuts_DryRun's +cond-format-update case updated
to assert the flat properties.rule_type shape + new enum.
#5 +filter-{update,delete}: auto-inject filter_id = sheet_id
-------------------------------------------------------------
manage_filter_object's contract is "filter_id === sheet_id" for the
sheet-scoped filter (per per-tool description in mcp-tools.json),
and update / delete operations MUST carry filter_id. Standalone
filterUpdateInput / filterDeleteInput never set it, so the server
rejected with "filter_id is required for update/delete operation"
on every call — both standalone AND inside +batch-update.
Fix:
- filterUpdateInput / filterDeleteInput now set
input["filter_id"] = sheetID.
- Because filter_id must equal sheet_id (not sheet_name), update /
delete reject when only --sheet-name is given — there's no
network lookup available inside the builder. The friendly error
points at +workbook-info for resolving sheet-name → sheet-id.
- create still omits filter_id (server requires that — id is
server-allocated on creation).
- New tests:
* TestObjectCRUDShortcuts_DryRun gains a +filter-update happy-path
case asserting filter_id is auto-injected + --range hoisting.
* +filter-delete case updated to assert filter_id presence.
* TestBatchOp_RejectsBadSubOpInput gains two cases asserting both
+filter-update and +filter-delete reject --sheet-name-only with
the friendly error.
Docs (#2 + #3 + #8) synced from sheet-skill-spec
-------------------------------------------------
Companion doc fixes that landed via npm run generate:cli + sync:cli
in sheet-skill-spec; included here because the regenerated flag-defs
and references markdown are byte-tracked in this repo:
- #2: lark-sheets-sheet-structure.md — +dim-{hide,unhide,group,
ungroup} --start/--end desc changed from "(0-based, inclusive)" to
"(0-based)" / "(exclusive)" to match the half-open range semantics
the code has always implemented (requireDimRange: end > start;
dimRange uses end - 1 for column end letters).
- #3: lark-sheets-workbook.md — +sheet-move section gains a note
about the batch-internal requirement to pass --sheet-id AND
--source-index explicitly (sheetMoveBatchInput's constraint).
- #8: lark-sheets-pivot-table.md — +pivot-create --properties
example drops the stale data_range field (the actual server
schema uses --source as a hoisted flag; properties only carries
rows / columns / values / filters / show_*_grand_total).
Sub-ops that omit --sheet-id (or any other required flag) used to slip
past CLI validation — Validate ran only against the standalone shortcut
path, and batchOpDispatch's translators built bodies from whatever
flagView returned, so a structurally broken sub-op surfaced as an opaque
server "sheet undefined not found" after a network round-trip.
Push each batchable shortcut's check trio down into its xxxInput builder:
1. resolveSpreadsheetToken — stays in Validate (batch already does it
once at the top level; sub-ops don't repeat).
2. requireSheetSelector(sheetID, sheetName) — new helper; flagView-
agnostic XOR + control-char check, called at the top of every
xxxInput.
3. shortcut-specific required / range / enum checks (--dimension,
--range, --start <= --end, --type pixel needs --size,
--float-image-id, image-token XOR image-uri, ...) — moved out of
Validate into the builder body.
All ~30 batchable xxxInput builders now return (map, error). Standalone
Validate shrinks to validateViaInput(xxxInput); DryRun / Execute
propagate the error. batch_op_dispatch entries drop the noErrTranslate
wrapper and pass the builder directly — its error bubbles up wrapped
with "operations[N] (+shortcut):" context.
Tests:
- TestBatchOp_ErrorEquivalence (7 cases): XOR / logical-constraint
errors fire identically from standalone and batch sub-op paths.
- TestBatchOp_RejectsBadSubOpInput (8 cases): cobra-required flags that
standalone catches via MarkFlagRequired now also get rejected CLI-side
on the batch path (where cobra is not in the loop).
- TestBatchOp_BodyMatchesStandalone (~40 cases) and
TestBatchOp_DispatchCoversReportedBugs continue to pass — bodies stay
byte-identical.
- BOE smoke (spreadsheet ICFwstkUGheyfptGWS2bB7RgcDf, sheet 51991c):
+batch-update with a sub-op missing --sheet-id now returns
"operations[0] (+dim-insert): specify at least one of --sheet-id or
--sheet-name" before any network call.
sheetMoveBatchInput (xiongyuanwen's batch-only explicit-source-index
requirement) is preserved — it's an orthogonal batch-specific constraint
not affected by this push-down.
sheet-skill-spec now declares +cells-get --range as a single string
(was string_array) and +csv-get --range as required. Match the
flag→body translators:
- +cells-get wraps the single --range into the tool's `ranges` array
and validates with Str() instead of StrArray(), which silently
returned nil against the now-String flag and broke the command.
- +csv-get gains a trim-based required-range guard.
Update read-data dry-run tests to single-range form and add a guard
test for the empty --range path.
Sub-ops previously near-passed-through their input, so any shortcut whose
standalone translator renames fields broke inside a batch: +range-copy lost
range/destination_range (transform_range errored "range missing") and
+rows-resize lost range/resize_height ("No resize operation specified").
Introduce a flagView interface (satisfied by *common.RuntimeContext) and a
map-backed mapFlagView, then route every batchable sub-op through the SAME
*Input builder the standalone shortcut uses. mapFlagView seeds flag-defs.json
defaults for value reads while keeping Changed() user-driven, so a sub-op body
is byte-identical to the standalone body — locked by a batch-vs-standalone
contract test over all ~40 batchable shortcuts.
Also fix single-row/column resize: start==end now formats as "23:23" / "C:C"
(resize_range rejects a bare "23"); dimRangeFull keeps both sides while
dimRange's collapse stays for modify_sheet_structure consumers.
Users now hand +batch-update --operations a CLI-shape array
([{shortcut, input}, ...]) and the binary translates each sub-op to the
underlying MCP batch_update shape ({tool_name, input(+operation)}) via
a new dispatch table in shortcuts/sheets/batch_op_dispatch.go.
Dispatch table covers 50 batchable write shortcuts. Excluded by design:
- all read ops
- fan-out wrappers (+batch-update self, +cells-batch-set-style,
+dropdown-update, +dropdown-delete) — nesting these = nested batch
- +dim-move — single shortcut uses legacy v2 /dimension_range endpoint,
not MCP, can't be batched
- +cells-set-image — multi-step image upload, not atomic-batch friendly
- +workbook-create — new workbook, not batch-on-existing semantics
Translator also rejects sub-ops that hand-fill input.operation (implied
by shortcut name) or input.excel_id / spreadsheet_token / url (set
once at +batch-update top level).
+dim-freeze always injects operation=freeze; the count==0 unfreeze
path of the single shortcut is intentionally not supported in batch —
callers should use the single shortcut for unfreeze.
Tests cover: end-to-end translation, --continue-on-error propagation,
13 rejection cases (banned shortcuts, malformed shapes, reserved keys).
Sync'd from sheet-skill-spec: skills/lark-sheets/references/
lark-sheets-batch-update.md + shortcuts/sheets/data/flag-schemas.json
pick up the corrected enum (+cells-set-style / +dropdown-set added,
+dim-move removed).
Pulled from sheet-skill-spec:
- skills/lark-sheets/references/lark-sheets-batch-update.md: --operations
now documents the {shortcut, input} form; tool_name references gone
- shortcuts/sheets/data/flag-schemas.json: --operations resolves to the
CLI-side array<{shortcut(enum), input}> schema, sourced from spec's
canonical-spec/tool-schemas/cli-schemas.json (cli: prefix). +dropdown
--options also drilled one level deeper
NOTE: the binary still raw-passes --operations to MCP batch_update which
expects {tool_name, input}. A follow-up will add a shortcut→tool_name
translation layer (with per-shortcut operation field) before the docs
become actionable.
Replace flag-descriptions.en.json with the richer flag-defs.json (full
flag definitions: type / default / enum / input / hidden / required /
kind) synced from sheet-skill-spec. Add flagsFor(command) to materialize
each shortcut's []common.Flag straight from the JSON, skipping
system-kind flags the framework injects.
Migrate every sheets shortcut (including the CRUD/list/dim/merge/
visibility factories) to Flags: flagsFor("+command"), dropping all
hand-written flag literals plus the now-dead publicTokenFlags /
publicSheetFlags / styleFlatFlags helpers and enum vars. A coverage test
locks the Go-flags-match-JSON contract.
Align Go with the new spec where they diverged: +cells-get --ranges →
--range, font-size int → float64, +filter-view-create --range now
required, +sheet-create row/col-count defaults 200/20.