mirror of
https://github.com/larksuite/cli.git
synced 2026-08-03 08:32:46 +08:00
feat/lark-sheets-develop
64 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f6353368d7 |
fix(sheets): drop the Go 1.24-only t.Chdir, and gofmt the ergonomics test
CI's fast-gate failed on the receipt test added in
|
||
|
|
c4270566ba |
test(sheets): pin the flat style flags' wire mapping, found by coverage
Coverage over the changed lines showed six of buildCellStyleFromFlags' eleven branches never executed. TestStylesAcceptance_VocabularyParity walks the same vocabulary but drives the PAYLOAD path; the flat --font-color / --number-format / … flags are a separate hand-written mapping that nothing exercised. The failure it admits is quiet: write the wrong wire key and the request still succeeds, the sheet just does not change. Verified the current mapping is correct for all eleven fields before pinning it, and confirmed the test is sensitive (renaming font_color's wire key now fails). Derived from flag-defs, so a newly declared style flag is covered as soon as it exists. |
||
|
|
75e8063bf2 |
test(sheets): cover --flag-name dotted-path slicing, which had none
Mutation testing found the whole feature unpinned: disabling the implicit items/oneOf descent, or the explicit "items" segment, broke no test. It is how SKILL.md tells agents to pull one subtree out of chart-create's ~1,750-line properties schema instead of paging the full dump, so a regression just pushes them back to full dumps — invisible to every output-correctness test. Covers the one-segment walk, implicit descent through array items, the explicit items segment, the underscore spelling of the flag name, that a slice is strictly smaller than the full schema, and that a miss lists the keys actually reachable at that level. Also resyncs the two read-helper script fixes from sheet-skill-spec. |
||
|
|
01ae4cc521 |
test(sheets): pin three guarantees that mutation testing showed nothing held
Third review pass, run as mutation testing rather than reading: break a behaviour, see whether anything fails. Most of the suite has teeth (removing the coalesce reordering guard, the descending sort in +dim-delete --ranges, the sub-op vocabulary check, the cells-vs-range dimension check, the border weight normalization, the retired-enum tolerance and the batch freeze-collision warning all get caught). Three did not. - coalesceStyleStamps' adjacency rule. Widening the touch test from +1 to +2 fuses ranges separated by one row, painting cells the caller never named, and passed every existing case — they are all contiguous, so they only constrain the rule from one side. Added the negative cases plus a coverage property: the set of cells the expanded stamps touch must equal the set the spec named. Coalescing rewrites a declarative spec, so this is the invariant that matters, not any single fusion. - The --output-path receipt. Hard-coding complete:true passed the whole suite, yet the receipt is the only completeness signal on that path (the data went to a file) and the skill docs tell agents to read it before using the file. Now driven end to end for clean / per-range-truncated / has_more reads, with the file contents and bytes_written checked against the receipt. - +table-get's whole-workbook char budget. Removing the per-sheet clamp lets each sheet spend --max-chars in full — a 30-sheet workbook pulls 30x what was allowed, with every individual request looking compliant. The outer loop's exhaustion check is a different mechanism and kept working, which is why nothing failed. Now asserted on the wire: sheet 2 must ask for less than sheet 1. |
||
|
|
99ac5d6906 |
fix(sheets): resync the profile-script hidden-column fix, drop a tautological test
Skill/data resync from the spec repo's matching commit: safe_append_col now
steps over hidden columns (see sheet-skill-spec), and the +batch-update
shortcut enum names +styles-put among the excluded fan-out wrappers.
Also fixes the --max-chars 0 test added in
|
||
|
|
fd97e65b55 |
docs(sheets): record the live +dim-insert side check that the code assumed
The previous commit sent `side` explicitly on +dim-insert and justified it as guarding a real hazard: that omitting it left placement to an undocumented backend default, which if it were "after" would insert on the wrong side of --position. Running it settles the question the other way. On a sheet with row2 red and row3 blue, inserting at --position 3 places the blank at row 3 under all four spellings — no `side` field at all (the pre-fix binary), omitted, `after`, and `before` — and the blank inherits the following row's blue under omitted/`after`, the preceding row's red under `before`. So the backend already defaults `side` to "before", the pre-existing behaviour was correct, and the flag's documented "omit == after" held on its own. Sending the field explicitly is kept, but as belt-and-braces: it stops the documented default from depending on an undocumented server-side one, and the placement is byte-identical either way. Comments and the test doc now say that instead of claiming a bug that was not there. |
||
|
|
4e2c6a2096 |
docs(sheets): resync skill refs and flag data from sheet-skill-spec
Regenerated from the spec repo's matching commit: the +styles-put failure guidance (operations[N] indexes are internal, read back and re-send instead), --dry-run rather than --yes in the two canonical +batch-update snippets, and the read-cap wording for --max-chars 0 / --output-path's JSON payload. |
||
|
|
f619e34d38 |
fix(sheets): close review gaps in freeze, styles and error reporting
Review of the aggregate diff turned up nine places where the surface did not do what this PR says it does. Each is small; the theme they share is that a prescription pointed somewhere the caller could not follow. Contradictions with this PR's own retirements: - The six --frozen-* unknown-flag hints prescribed --dimension row --count N. Those flags are hidden from --help, so the hint named a flag missing from the valid-flags list printed beside it, and following it earned a deprecation note steering back. They now prescribe --rows / --cols, as does the "nothing to freeze" error. - A Sheet! range prefix was only stripped when the styles item carried a name. +workbook-create --values items need none, so row_sizes like "Sheet1!2:3" still failed there as a malformed range — the exact bug this PR reports as fixed across all three --styles carriers. Stripping is now unconditional; only the "names a different sheet" report needs a name to compare against. - Two +dim-freeze sub-ops in one batch cancel each other, and only the CLI can see it (a batch cannot read current state, and +styles-put is not batchable). Per-op "equivalent to --rows 1" notes never said so. A collision note now names the colliding ops, the state actually reached, and the single sub-op that holds both axes. dimFreezeAxes/dimFreezeSpelling became the shared mapping so the request body, the deprecation note and this one cannot drift. - +dim-insert with --inherit-style omitted sent no `side`, so "omitting is the same as after" held only if the backend happened to default it to before — and if it defaulted to after, the insert would land on the wrong side of --position, silently breaking the command's stated contract. It is now sent explicitly; TestDimInsertOmittedMatchesAfter pins the two bodies together. Errors that misdescribed themselves: - "resend only operations[N:]" was emitted for every batch_update caller, but only +batch-update's array is caller-written. +styles-put coalesces and +dim-delete --ranges deliberately re-sorts descending, so the index names nothing the caller can locate. Those callers now get a read-back procedure. - A sub-op carrying both an alias and its target (size + width on +cols-resize) was reported as an unknown input key: the alias branch fell through, and the conflict check never fired because keys are walked in sorted order and "size" sorts first. Identical values now drop the alias; differing ones name both spellings. - Folding per-item failures into one error dropped each inner Hint, so the more mistakes a payload had, the less guidance it got — including the +workbook-info pointer this PR had just added. A lone issue inherits the hint; a folded list inlines each. - --max-chars 0 sent no cap, which makes the read tool apply its own ~50000 fallback: asking for no limit produced the smallest one. It now resolves to the same ceiling as leaving the flag alone. Also: --inherit-style before anchors one row/column earlier, so its dry-run showed a position the caller never typed; a note explains it is not an off-by-one. The style vocabulary now walks sorted keys everywhere, since every one of those loops can abort and map order decided which of several bad fields got reported. |
||
|
|
50b1f54bfa |
fix(sheets): make a retired enum value read as absent on both paths
Clearing a retired value left the two paths disagreeing about Changed(): the standalone path went through cobra's Set, which marks a flag as changed, while the batch path deletes the raw key and so reports false. --inherit-style is unaffected because its only consumers switch on the value, but retiredEnumValues is a generic table — the next flag added to it whose logic reads Changed() rather than the value would behave differently standalone than inside +batch-update, and the body-parity contract only catches such a split once it reaches the request body. Both paths now report the flag as absent, pinned by a test that asserts Changed() directly instead of only comparing bodies. |
||
|
|
f8ed143bde |
fix(sheets): accept the retired --inherit-style none instead of failing
Dropping "none" from the +dim-insert enum was the only value removal in the whole flag surface, and it broke callers: "none" was valid AND the default before the side mapping was corrected, so existing scripts and any agent carrying older docs pass it. A retired value is now cleared rather than rejected, making the call byte-identical to omitting the flag — on the standalone path and inside +batch-update alike, since +dim-insert is batchable and a divergence there would be invisible. It stays out of the published enum so the docs and --help do not start teaching the redundant spelling again, and a genuinely invalid value is still rejected. The test mounts the shortcut through Shortcuts() rather than the exported var: the rewrite rides the PostMount ergonomics layer, which the raw var does not carry, so a test built on it would not exercise what ships. |
||
|
|
47f4fda1d3 |
fix(sheets): steer legacy +dim-freeze on dry-run and inside +batch-update
The phase-1 deprecation note for --dimension/--count only fired from the standalone Execute, missing the two paths that matter most. --dry-run is how a caller previews before committing to a spelling, so a note that arrives only on execute arrives too late. It now rides along as the dry-run warning_message. +batch-update never reached the note at all, yet the batch is where the legacy form does the most damage: freeze is full-state replacement, so two per-axis sub-ops both report success while only the last axis stays frozen, and +styles-put — the other way to set both axes — is not batchable. Sub-op notes are rendered from the same helper as the standalone one, prefixed with the operations[i] index, and DryRun and Execute now share one batchWarnings collector so they cannot drift apart on which advisories they report. |
||
|
|
7866801115 |
chore: tag phase-2 deprecation sites with a greppable marker
Three surfaces are in phase-1 deprecation — executable but superseded — and each described that state in its own prose, so there was no way to enumerate what is pending removal. DEPRECATED(phase-2) tags all three uniformly, each carrying its removal checklist including the cross-repo spec step. |
||
|
|
3624499bcb |
feat(sheets): +dim-freeze --rows/--cols for both axes in one call
Freeze is full-state replacement server-side, so --dimension/--count unfreezes the axis it does not name. Two calls cannot hold both axes, and inside +batch-update there was no way at all: sub-ops are a static array that cannot read the current state, and +styles-put is not batchable. --rows/--cols state the complete freeze state in one operation, so the standalone command and the batch sub-op keep producing identical bodies. The legacy pair still works and prints the exact --rows/--cols equivalent on use, but is hidden from --help and from the skill docs. |
||
|
|
13d4350557 |
fix(sheets): unify --styles range prefixes, merge freeze into one op
A "Sheet!" prefix on a --styles range was only normalized for +styles-put cell_styles. row_sizes like "Sheet1!2:3" therefore failed parseA1Range on all three carriers, while a prefix naming another sheet was rejected only after the section parsers had already reported their own errors. Normalization now runs in the shared item parser, so +workbook-create, +table-put and +styles-put behave alike; a foreign prefix is stripped and reported alongside the item's other issues instead of masking them. Prefix stripping for visual ops moved into workbookCreateVisualOpInput, which every caller goes through. Freeze rows and columns now travel in one operation: the backend replaces the whole freeze state, so two per-axis operations left only the last axis frozen. |
||
|
|
daeab10755 |
fix(sheets): completeness classifier, freeze alias determinism, color arrays
Fifth-round review findings, each reproduced before fixing:
P1:
- readResultTruncated missed ranges[] (the shape +cells-get / +csv-get
return), so a clipped read could be written to a file and reported as
complete:true. It now checks every level — top, ranges[], sheets[], and
nesting — under all three flag names the tools use. readSheetAsSpec also
keeps the truncation flag on the empty-grid path, where a read clipped
before its first row used to be reported as a complete empty sheet.
- freeze accepted "cols" and "columns" as aliases and wrote both through a
Go map range, so {"cols":1,"columns":2} froze 1 or 2 columns depending on
randomized iteration order (observed both across 2000 runs). Conflicting
values now reject; identical values and either alias alone still pass.
Iteration is sorted so error text is stable too.
P2:
- the --styles / --writes aggregators returned the raw inner error when
there was exactly one issue, leaving Param empty until a second issue
appeared. They now attribute the outer flag and keep the cause in every
case. checkBatchStampBudget takes the caller's flag name instead of
always blaming --ranges.
- chart color normalization lost key context when descending into arrays,
so schema-supported colorTheme / colorScale / highlight_colors lists kept
bare hex and the server rejected them. isColorKey also covers plural and
color-prefixed names; the strict hex value check keeps that safe.
- detect_subtables split records on any physical line starting with
[row=N], so that text inside a multi-line quoted cell forged a record
boundary and polluted row numbers. Splitting is now quote-state aware,
and annotated mode is entered only when the first meaningful line really
carries a prefix.
- docs: --output-path no longer described as unlimited anywhere; the
read-data reference documents the per-sheet truncated flag and the
offload receipt's complete / unread_sheets fields.
|
||
|
|
f5e0d14ca9 |
fix(sheets): key uniqueness, order-safe style coalescing, bounded read budget
Fourth-round review findings, all reproduced before fixing:
P1 — silent wrong results:
- batch/writes key canonicalization now guarantees ONE logical key per flag.
Both sheet-id and sheet_id used to pass the vocabulary check and survive
into the body, where the flag view resolves either spelling — so one value
silently won and the write could land on the wrong sheet. Conflicting
values now reject with a typed error; identical values collapse.
- coalesceStyleStamps no longer groups globally by style content. Grouping
reordered same-style stamps around differing ones, turning red → blue →
red into red → blue and silently changing the final color. Two stamps are
merged only when nothing between them touches the cells whose write would
move earlier; adjacent and disjoint-intermediate merges still happen.
- --output-path is no longer described or treated as an unlimited read. The
cap bounds the WHOLE read: +table-get spends one budget across all sheets
instead of per sheet, names sheets left unread, and the stdout receipt
now reports complete / truncated so a caller need not reopen the file.
dry-run echoes the same max_chars / cell_limit execute sends.
P2 — contract violations:
- style payloads (--styles, typed --cells) assert the scalar types declared
in flag-defs, so {"font_weight": true} is rejected instead of reaching
the server as a boolean.
- aggregated --styles / --writes errors bind their flag as Param and keep
the first underlying error as Cause, so agents read the typed envelope
instead of parsing prose.
- detect/profile scripts handle hidden COLUMNS like hidden rows: visible-
column adjacency stops splitting A|C into two components, and profile
reports data_col_segments plus a data_range_has_col_gaps warning so a
caller cannot write gap-free data back as one contiguous range.
|
||
|
|
240e523dbf |
fix(sheets): drop remaining atomic claims from --help text and errors
The earlier atomic-wording cleanup covered spec-managed docs and flag descriptions but missed the Go-side Description / Tips fields (which render in --help) and two sub-op rejection messages. All of these batch_update-backed paths are fail-fast without rollback, so they now say so instead of promising atomicity. |
||
|
|
3788b6f601 |
fix: revert retry-command renderer and temp @file exception, bound read offload
Third-round review (comprehensive CR on PR #2091) — close the P1s by reverting the two global protocol changes and tightening the rest: - cmdutil confirm (F1): drop the argv-rebuilt "re-run:" retry line and every heuristic behind it; the hint is the plain "add --yes to confirm" again. argv cannot faithfully reproduce the invocation (pipelines, stdin, redirections, env, executable path), POSIX quoting breaks on PowerShell/cmd.exe, and the name-based secret guard both leaked free-form payloads and false-positived on ordinary token locators (--spreadsheet-token). Callers append --yes to their own saved argv after user consent, per the lark-shared protocol. - localfileio/validate/cmdutil (F4): remove the SafeTempAbsInputPath @file exception — TMPDIR-defined trust roots are not a security boundary (TMPDIR=/etc widened the allowed region) and the fast path bypassed the caller's FileIO provider. @file is strictly cwd-relative again; out-of-tree content goes through stdin. - csv guard (F5): stop splicing the untrusted --csv value into command-shaped hint text; prescriptions use <path> placeholders, the value is only named as quoted data. - read offload (F8): --output-path now raises max_chars to a bounded 20M-char default instead of the 1e9 sentinel — the read path is not streaming, so the cap is the OOM guard; an explicit --max-chars still overrides. - batch-update tips + skill (F2, synced from sheet-skill-spec): replace "always pass --yes" / "首次调用就带 --yes" with the consent protocol (dry-run, show the plan, get explicit approval, then append --yes). - skill docs (F12, synced): scripts/lark_*.py are an optional enhancement — binary-embedded skills ship without scripts/, so the docs now say so and point at the CLI-equivalent fallback paths. |
||
|
|
0e95848dd7 |
fix: address second-round PR #2091 review findings
Reviewer findings (fangshuyu-768) plus one CodeRabbit follow-up: - sheets batch recovery: thread the batch's continue_on_error mode into flattenToolErrorMsg — a single listed failure only implies "nothing after it ran" under fail-fast; under continue-on-error the tail already executed, so prescribing operations[i:] would double-apply it - cmdutil confirm: gate the retry line on value shape as well as flag name — free-form payloads (--sql, --json, non-ASCII, over-long) fall back to the plain add-yes hint instead of copying potential passwords or PII into the error envelope - sheets read offload: map FileIO.Save failures through WrapSaveErrorTyped so an escaping --output-path stays a validation error with its path-validation cause, not internal/unknown - styles-put: reject ranges whose sheet prefix names a different sheet than their item (silent strip retargeted them); reject unknown top-level item keys with a did-you-mean (typo'd freezee was silently dropped) — shared with +workbook-create / +table-put - e2e: cover --inherit-style after in the live dim workflow - docs/flag descs (synced from sheet-skill-spec): batch_update-backed paths no longer claim atomic/transactional semantics — reworded to fail-fast + no-rollback across +styles-put, +cells-set --writes, +dim-delete --ranges, resize maps and the related references |
||
|
|
a5032bbb55 |
fix: address PR #2091 review findings across path safety, batch dispatch and read caps
Review fixes from CodeRabbit / code-quality on PR #2091: - cmdutil: route the @/tmp fast-path read through vfs.ReadFile so fakes keep intercepting it; suppress the confirmation retry line when argv carries a credential-bearing flag (secrets stay out of error envelopes); extract requireConfirmationFor and pin the composed hint in tests - localfileio: reject a degenerate TMPDIR (e.g. "/") so the temp-dir read exception cannot widen into accepting arbitrary absolute paths - sheets batch dispatch: duplicate canonical + variant input keys (sheetName/sheet_name, ranges/range) now reject with a prescriptive error instead of silently overwriting the canonical value - sheets flag ergonomics: curated unknown-flag hints match underscore spellings (--frozen_rows hits the --frozen-rows entry) - sheets read offload: an explicit --max-chars survives --output-path instead of being replaced by the unbounded sentinel - chart: declare --print-example in flag-defs.json (single source with the generated reference tables) instead of imperative registration; assert the validation Param in the unknown-type test - e2e: add live +dim-insert / +dim-delete workflow coverage (insert lands before position, --range delete, atomic scattered --ranges) - docs (synced from sheet-skill-spec): +chart-create flags table gains --print-example, freeze docs state the at-least-one-positive rule, read-data table gets its MD058 blank line, detect-subtables explains its fallback except |
||
|
|
d8f6154e2f | chore: add license headers to sheets skill scripts, drop redundant loop-var copies | ||
|
|
d219d61be0 |
Merge remote-tracking branch 'origin/main' into feat/lark-sheets-develop
# Conflicts: # internal/validate/path.go # internal/vfs/localfileio/path.go |
||
|
|
f79908483d |
fix(sheets): reject path-shaped --csv values instead of writing them into the sheet
A +csv-put --csv value naming a file that doesn't resolve used to be written into the anchor cell as literal text, with a success exit code — a wrong value in the sheet that nothing surfaces, which costs more than a rejection. The common source is an absolute path: @ only reads relative paths, so the caller drops the @ and retries, and the path string lands in A1 (07-28 root-cause audit, finding D1). The existing guard only caught values naming a file that does exist (the forgotten-@ case). The guard now also rejects a value that is unmistakably path-shaped: no separator or whitespace, pure ASCII, and either a .csv/.tsv extension or an explicit ./ ../ / ~/ prefix. All three conditions are required — that is what keeps prose that merely mentions a filename, N/A, README.md and CJK content out of it, the misjudgments that retired the previous name-shape heuristic. This flips one pinned case: a bare "nope.csv" was previously written verbatim and now errs with the fix inlined. To make the shape check safe for correct invocations, resolveInputFlags now records which flags had their value replaced from @file or stdin, exposed as RuntimeContext.InputResolvedFromSource; the guard skips resolved values entirely. By Validate time a piped value is indistinguishable from a typed one, so without the origin bit the guard would re-reject a correct `--csv @file` whose content happens to look like a path — and stdin, which the error text prescribes for verbatim writes, would not actually escape it. The @@ escape stays inline and guarded. This origin bit is the one common-layer addition; it carries no domain logic. |
||
|
|
0b59556207 |
feat(sheets): name the enum, the required selector and the missing envelope in errors and help
Second batch from the 07-28 root-cause analysis, all aimed at the retry that
follows a rejection.
Enum-bearing type mismatches now answer with the allowed values instead of a
whole-payload skeleton. --border-styles with weight:1 used to reply "expected
type string, got number; expected shape: {"bottom": {…}, "left": {…}, …}",
which never mentions thin/medium/thick; the skeleton is for container-shape
confusion, so a field that declares an enum falls through to the hint that
names it. The --border-styles help now inlines both vocabularies (style is the
line type, weight the thickness and a string, not a pixel number), spells the
{all:{…}} shorthand, and states that no --border-all / --border-top /
--border-color exist. --word-wrap additionally accepts the Google Sheets
wrapStrategy words wrap and clip.
The sheet selector states that one of the pair is required rather than only
that they are mutually exclusive, in help across all shortcuts that take it,
and the rejection hints where the name comes from: a fresh workbook has one
sheet named Sheet1, any other needs a +workbook-info lookup. Eval traces
recover on the very next call, so the gap was which name to pass.
A --sheets payload written as a bare array now says the top level must be
{"sheets":[…]} instead of quoting Go's "cannot unmarshal array into Go value
of type struct { Sheets []sheets.tableSheetIn }", which names the internal
type rather than the fix. The skeleton hint is unchanged.
|
||
|
|
91743bba99 |
fix(sheets): make border vocabulary normalization reachable, prescribe flag and style-field fixes
Three fixes from the 07-28 root-cause analysis of failed agent traces, all
aimed at the first-try success rate rather than the recovery loop.
Border acceptance layer was unreachable on two of its three carrier paths.
expandBorderAllShorthand already moves a weight word out of the style slot
({"style":"thin"} -> style solid + weight thin), but on --border-styles and
typed --cells it ran after parseJSONFlag's schema check, so the enum error
fired first and the rewrite never happened. Move it ahead of validation via
the jsonFlagNormalizers seam; --styles already validated post-expansion and
is unchanged. An explicitly conflicting weight still takes the enum error.
Unknown-flag prescriptions for the names agents reach for most: +cells-set
--values, +dim-freeze --frozen-row-count and siblings, +cells-set-style
--font-bold / --bg-color / --wrap-strategy and the whole --border-* family
(no such flags; borders take one composite --border-styles). +sheet-rename
--new-name / --name alias to --title, matching +sheet-create.
Unsupported cell_styles field names now name the right field instead of the
nearest string: bold / font_bold -> font_weight, text_align ->
horizontal_alignment, a nested openpyxl-style font object -> the flat font_*
fields. Where no prescription applies, the edit-distance fallback is capped
at two edits, so a concept-swap neighbour (font_bold -> font_color, three
edits) stays silent rather than sending the retry the wrong way; a curated
prescription also drops the contradicting machine-readable suggestions.
|
||
|
|
1b173e1953 | fix(sheets): recognize OFL0X local office tokens (#2063) | ||
|
|
ca7135f582 |
fix(sheets): correct +dim-insert --inherit-style side mapping, drop redundant none
The flag name was inverted relative to actual behavior, and `none` was redundant. - Map --inherit-style onto the modify_sheet_structure backend so the name matches behavior (verified on a live sheet): `before` inherits the preceding row/column (side=after, position-1); `after` inherits the following (side=before). Insertion always lands before --position. - Warn only when `before` is used at the first row/column (no preceding dimension to copy from); `after` has no such edge. - Drop the `none` enum value: it was identical to omitting the flag and misleadingly implied "no inheritance" (the backend always copies a neighbour). Omitting the flag inherits the following row/column; `none` is now rejected by enum validation. Clear formats afterwards for a blank one. - Regenerate flag-defs, update tests and the skill reference. |
||
|
|
7b58ba1b1d |
feat(sheets): batch-update contract hardening and style-vocabulary acceptance (#2028)
* feat(sheets): harden +batch-update sub-op contract (P0-1/2/3/5)
Eval-driven fixes for the top +batch-update error clusters (137 errors
across 7 eval batches, attribution in the optimization plan doc):
- Reject unknown sub-op input keys with did-you-mean + full key contract
instead of silently ignoring them (silent ignore surfaced as misleading
'missing required flag' errors — the largest cluster, ~35 hits).
Habitual spellings are rewritten in place: camelCase -> snake_case,
commandFlagAliases (new: size -> width/height on the resize pair, the
pre-July vocabulary and the --styles protocol spelling, 15+ hits),
single-entry ranges unwraps onto range.
- Aggregate per-op validation errors into one pass (each op's first
error) instead of fail-fast first-error-only; single-error batches
keep the standalone-shaped error (contract tests unchanged).
- Precheck cells matrix vs range locally: empty cells (prescribes
+cells-clear) and row/column count mismatches no longer reach the
server mid-batch.
- Correct the atomicity story: execution is fail-fast without rollback
(verified against live batches; docs promised a rollback that does
not happen). Partial-failure errors now spell out that succeeded
operations stay applied and prescribe resending only the failed
tail, preventing double-apply on retry.
* feat(cli): accept @file payload reads from the system temp dir (P0-6)
Agents stage generated payloads (batch operations JSON, CSV) in /tmp as
a matter of course; the relative-to-cwd-only policy pushed every
--operations @/tmp/ops.json through an extra python/stdin round trip
(recurring friction cluster in eval traces).
Scope is deliberately narrow: a new SafeTempAbsInputPath accepts an
absolute READ path only when it resolves (symlinks included) under the
canonical os.TempDir(), and only the @file expansion in
cmdutil.ReadInputFile uses it. SafeInputPath stays strict — uploads,
drive sync and the CI quality gates treat 'absolute paths rejected' as
a load-bearing invariant. Absolute paths outside the temp dir now get
a prescriptive error naming all three options (relative path, temp-dir
path, stdin).
* feat(sheets): add +styles-put, +dim-delete --ranges, freeze in --styles
Batch-B of the +batch-update overhaul (attribution: ~73% of real batch
calls were pure formatting finishers hand-built as operations arrays).
- +styles-put: declarative visual spec for existing spreadsheets.
Reuses the workbook-create/table-put --styles parser (identical
vocabulary and aggregate-all-issues errors), expands client-side into
ONE atomic batch_update per spec: cell_merges -> cell_styles ->
row_sizes -> col_sizes -> freeze. Style stamps are safe to re-run.
Verified live: 6/6 sub-ops applied, frozen rows / merges / row
heights confirmed by read-back.
- freeze section added to the shared --styles pipeline ({rows, cols}),
so +workbook-create and +table-put gain it too — closes the one gap
that still forced a separate +dim-freeze call.
- +dim-delete --ranges: scattered row/column ranges in one atomic
batch, ordered DESCENDING so earlier deletions never shift later
indexes (the recurring failure of hand-built dim-delete batches);
same-dimension and non-overlap enforced, nesting inside
+batch-update rejected with a prescription.
- +cells-batch-set-style enters phase-1 deprecation: kept working,
docs point at +styles-put, an in-band note steers new usage.
Skill docs regenerated from sheet-skill-spec (new
lark-sheets-styles-put reference, three-way dispatch in guideline 6,
fail-fast-no-rollback wording).
* feat(sheets): accept height/width as one-way aliases for size in --styles row/col_sizes
size stays the canonical dimension key: it keeps row_sizes and col_sizes
items shape-uniform (the array name already carries the dimension), it is
what shipped with workbook-create/table-put and what models demonstrably
converge on, and it matches the dimension-neutral precedent of comparable
APIs. The Excel-vocabulary words are accepted silently only where
unambiguous — height inside row_sizes, width inside col_sizes; the wrong
dimension's word gets a targeted error instead of a rewrite, and giving
both size and the alias is rejected. Shared parser, so +workbook-create /
+table-put / +styles-put all gain it.
* chore(sheets): sync skill docs — +cells-batch-set-style fully exits the skill surface
Regenerated from sheet-skill-spec: the deprecated command no longer
appears anywhere in SKILL.md or the references (multi-range styling
routes to +styles-put); its flag-defs entry is untouched, so the command
and --help keep working for compatibility callers, with the in-band
supersedence note steering them to +styles-put.
* fix(sheets): forgive habitual vocabulary on the --styles payload path
07-20 rerun attribution: the batch-update dispatch worked (calls 105->25,
errors 21->8; +styles-put adopted by 16/35 tasks) but +styles-put itself
hit a 56% stateful error rate — the redesign moved traffic from the flag
path onto the payload path, and the round-2 forgiveness layers (key
aliases, enum-value canonicalization) only existed on the flag path.
Both dominant clusters are fixed in the shared styles pipeline, so
+styles-put / +table-put / +workbook-create / typed --cells all gain it:
- border family folding (largest cluster, up to 88 issues in one retry):
borders/border objects, border_top..right objects, border_style/color/
weight scalars, and flattened border_<side>_<attr> keys all fold into
the canonical nested border_styles; border_style:"thin" reads as a
thin solid line (weight vocabulary in the style slot).
- enum VALUE canonicalization inside cell_styles (~10 server-side
round trips: vertical_alignment "center" -> "middle"), sourced from
the +cells-set-style flag enums; off-enum values now fail client-side
with a did-you-mean instead of failing the whole batch server-side.
- wrap family: wrap_text/text_wrap -> word_wrap, boolean -> enum.
- bare-string cell_merges entries read as {range, merge_type:all}.
- fore_color gets a prescription (openpyxl fgColor is the FILL color;
a silent pick could color the wrong thing).
Replayed the eval-failing payload shapes live: 2/2 applied.
* feat(sheets): resize type optional in --styles; acceptance-surface contract tests
- {range, size} in row/col_sizes now means a pixel resize (type stays
for standard/auto) — the payload path matches the flag path, where
--width never required --type.
- Two closure tests turn the --styles acceptance surface into a locked
contract instead of open-ended patching:
* vocabulary parity — every +cells-set-style flag (iterated from
flag-defs) must be accepted verbatim by the payload path, so a
future flag can never again ship without payload-path support;
* prior corpus — every model spelling observed across the 07-08..07-20
eval batches must either normalize to canonical or produce a
targeted prescription; silent ignoring and bare rejection both fail
the suite. New eval finding -> add a corpus row -> fix -> locked.
Skill docs regenerated: the border shorthand ({style,weight,color} on
all four sides) is now the teaching form, border_styles demoted to
per-side differences; resize examples drop the type ceremony.
* fix(sheets): close the 07-21 rerun residuals — full-form thin, range coalescing, typed-cells style key
Valid rerun (skill injection verified at ~31k chars/task): +styles-put
stateful error rate fell 56.5% -> 34.3% and batch-update stayed at its
post-dispatch low. Three residual clusters, all closed:
- weight vocabulary in the FULL nested form's style slot
(border_styles.<side>.style:"thin" — 8 tasks, the dominant residual;
the earlier rewrite only covered the shorthand scalar path). Now
normalized in expandBorderAllShorthand, the single border touchpoint
shared by the flag, typed-cells and styles-payload paths.
- per-row specs blowing the 100-op cap (184/203/861-op expansions):
coalesceStyleStamps fuses identical-style entries into rectangles
(vertical fixpoint merge on same column span, horizontal on same row
span) before the cap — a declarative spec describes intent, execution
shape is the CLI's to optimize. Cap message now also routes
alternating-row banding to +cond-format-create.
- typed --cells habitual keys (recurring server-side 900015206 in both
reruns): cells[][].style object rewrites to cell_styles;
cells[][].type gets a prescription instead of a server round trip.
The content-in-styles message is also neutral now (was
workbook-create-specific).
Corpus + coalescing + typed-cells tests added; live replay: full-form
thin accepted and 3 same-style rows fused, all applied.
* refactor(sheets): give the style-vocabulary acceptance layer its own home
Pure mechanical move, zero behavior change (locked by the acceptance
contract tests). The acceptance layer had grown as an accretion across
helpers.go and lark_sheet_workbook.go — deliberate design (one canonical
form + wide acceptance, per the divergent-priors evidence), accidental
placement.
style_vocab.go now holds the whole subsystem — flag-path style builders,
key aliases, enum-value canonicalization, border folding/normalization,
typed-cells cell-object rewrites — under a header that states the design
contract: rewrites must be unambiguous, ambiguity prescribes, silence
and bare rejection are both bugs, closure is enforced by the parity +
prior-corpus tests. helpers.go shrinks back to generic plumbing
(818 -> 542 lines).
The other two acceptance surfaces keep their own homes: cobra flag
ergonomics in flag_ergonomics.go, batch sub-op key vocabulary in
batch_op_dispatch.go.
* fix(sheets): enforce the cells-vs-range match on single-cell ranges too
The precheck deliberately skipped bare single-cell ranges when server
behavior was unverified; the 07-21 rerun supplied the evidence (12 rows
against range "A1" failing server-side with row count 1) — +cells-set
has no anchor semantics, the strict match applies everywhere. Corpus
updated accordingly.
* feat(sheets): make +csv-get --range optional — omitted reads the whole sheet
The tool requires a range but clips past-grid references and reports the
clip in actual_range, so the CLI sends an over-wide whole-columns range
(A:ZZZ) when --range is omitted: one call reads the entire sheet, no
workbook-info pre-flight to size it first. Eval evidence: 'required
flag(s) range not set' was the most-missed required flag on +csv-get
(4 tasks in the 07-21 batch) — the models' intent was always 'read it
all'. Blank --range now means the same as omitting it.
Skill docs updated (quick-reference row, read-data full-read example,
flag desc); round-3 backlog item A5.
* feat(sheets): add editing rule #10 — never fabricate missing values
补齐 / 扩展 / 按原表格式续填时,查不到或无法确定的值一律留空 +
备注注明,禁止用推算 / 估算 / 凭空数据充数;原表已示范缺失值写法时照抄。
Synced from sheet-skill-spec (SoT).
* fix(sheets): accept side-first border word order and wrap_strategy
07-21 evening batch (first run carrying the previous fixes — thin
full-form / style/type keys / csv-get range all at zero): the corpus
loop caught the next spelling permutations. bottom_border /
bottom_border_style (side-first word order, alongside the border_bottom
family already folded) and wrap_strategy (the Google Sheets API word)
now normalize; three corpus rows lock them.
* feat(sheets): universal did-you-mean on unknown style fields; formalize the silent-alias admission bar
The alias table was drifting toward per-permutation entries with fast-
falling marginal value (first aliases covered 15+ errors each, the last
ones 1-2). The asymmetry that matters: rejection is universal, aliases
are per-word. So:
- unknown style fields now reject with did-you-mean + the full canonical
field list (this error had neither — the actual reason word-order
permutations turned into multi-issue retry loops). Any future
permutation costs one self-healing retry, zero new code, and corrects
the whole session (silent aliases never correct the model in-session).
- the acceptance-layer contract now states the admission bar for silent
aliases: real external vocabularies only (Excel/openpyxl, CSS, Google
Sheets API — a finite set), recurring across batches or ≥3 tasks,
zero ambiguity. Permutations go to the universal rejection. Existing
permutation aliases are grandfathered.
* feat(sheets): +cells-set --writes — scattered multi-region writes in one atomic call
The last compressible batch-update scenario: eval traces show 'fix all
broken formulas across ranges/sheets' (~6 calls / 3 tasks per batch)
still hand-assembled as +batch-update operations arrays. --writes takes
[{sheet_name|sheet_id, range, cells}, ...] (up to 100 items, cross-sheet)
and fans it into ONE atomic batch_update of set_cell_range ops.
Design decisions:
- the sheet selector LIVES IN EACH ITEM — no top-level fallback, no
precedence table; same convention models already learned from
+batch-update sub-ops and +styles-put items. A top-level
--sheet-name/--sheet-id with --writes is rejected with the fix.
- every item runs the exact standalone pipeline via a per-item flag
view: key vocabulary (camelCase, aliases, did-you-mean), the style
acceptance layer for inline cell_styles, matrix precheck, schema
validation; item errors aggregate so one retry fixes all.
- XOR with --range/--cells/--copy-to-range; top-level --allow-overwrite
propagates to items that don't override it.
- nesting inside +batch-update rejected (expands into its own batch).
- predictable prior handled: --styles on +cells-set now hints the
layering (range-level styling -> +styles-put; per-cell styles ride in
the cells objects) instead of a bare unknown-flag error.
Live smoke: value + formula regions in one call, 2/2 applied.
Expected effect: batch-update calls drop another ~30% to its
heterogeneous-atomic-chain steady state.
* chore(sheets): bump lark-sheets skill version to 3.1.0
Version roll-up for the batch-update optimization series: cells-set
--writes, universal did-you-mean on style fields, border word-order and
wrap_strategy acceptance, editing rule #10, and optional +csv-get
--range.
|
||
|
|
765b097d44 |
Merge pull request #2027 from larksuite/feat/error-schema-hints
Feat/error schema hints |
||
|
|
4a5e2c519a |
feat(sheets): 校验失败全量收集报错(一次报出所有错误)
为什么:fail-fast 单错报错导致「挤牙膏」修复回路——修一处、重试、 撞下一处。评测实测(turbo 三批 247 次失败)约 32% 的失败轮次是 挤牙膏,其中 local→local 类(39 次)本可一次报出。 怎么改: - validateAgainstSchema 重构为 collectSchemaErrors 收集器版:命中 错误后继续遍历,全部问题一次报出(每条带各自的教学信息) - translateBatchOperations 同步做 op 级聚合:多个坏 op 一条报错 编号列出,不再 fail-fast 在第一个 - 三条护栏:单错误输出逐字不变(向后兼容);显示 cap 5 条 + 收集 到 6 即全树短路(病态大数组不爆炸);类型错节点不下钻、oneOf 用一次性探测器(防级联噪音/误报泄漏) 验证:gofmt/vet 干净,go test -count=1 全过(既有测试零改动, 新增 5 个聚合测试:多错编号、cap 截断、oneOf 不泄漏、batch 双 op 聚合、单 op 保持原文)。 |
||
|
|
6ff10229fd |
fix: standardize CLI shortcut text in English (#1942)
* fix: standardize CLI shortcut text in English - translate Docs create and update help descriptions - remove localized permission annotations - replace Chinese examples and fallback text - use English labels for Docs IM Markdown resources - update regression tests for English output * test: strengthen English output contracts |
||
|
|
67fc870582 |
feat(sheets): add --output-path for full-read file offload on cells/csv/table-get
Reads are capped by max_chars (default 500000; the backend tool also truncates at ~50000 when unset). Add --output-path to +cells-get / +csv-get / +table-get: when set, the result is written to a cwd-relative path as JSON and the char cap is lifted to unbounded, so a large sheet lands on disk in full instead of being clipped for stdout. +table-get previously never sent max_chars, so it silently dropped rows past the backend ~50000 default with no signal. It now takes --max-chars (default 500000, sent explicitly) and surfaces truncated / truncation_warning when the read is clipped, steering callers to --output-path for a lossless full read. |
||
|
|
af8e027269 |
feat(sheets): support --include truncation on +cells-get
Map the new `truncation` value in --include to include_truncation_info on the get_cell_ranges tool input, so +cells-get can return per-cell isRowTruncated / isColTruncated. Flag metadata and reference synced from sheet-skill-spec; flag_defs_gen.go regenerated. |
||
|
|
2efadec335 |
feat(sheets): cut agent error rate and --help lookups (#1911)
## Background Round 2 of eval-driven sheets optimization, rebased onto the latest `feat/lark-sheets-develop` (`8897196d`). ## Changes - **feat(sheets): cut agent error rate and --help lookups (eval round 2)** — targets the top failure modes from round 2 evals, reducing agent error rate and the number of `--help` lookups. - **chore(sheets): sync skill docs and flag data from sheet-skill-spec** — syncs skill docs and flag data from sheet-skill-spec. ## Notes - During rebase, the "import mislabeled .xls workbooks by sniffing content" fix already existed on the target branch (identical patch-id), so it was auto-skipped — no duplicate. - The target branch was force-rewritten and advanced in the meantime; the two new commits were cleanly replayed onto the new tip via `--onto` with no conflicts. One hunk touching the `--type` description in `lark-sheets-workbook.md` was auto-dropped because upstream already has the same end state — no content lost. |
||
|
|
6c14c425fc | docs(sheets): use English placeholder in table-get guidance (#1936) | ||
|
|
5fb70d326a |
feat(sheets): 校验失败报错内联 schema 提示(报错即教学)
为什么:豆包 Excel Agent 案例中模型 7 次参数错误,报错只说"错了"不说 "怎么改对",模型反复试错并静默降级交付(5 张饼图丢数据标签)。 怎么改: - strict unexpected-property 报错附该节点合法 key 列表(cap 15 截断) + did-you-mean(复用 internal/suggest) - required-missing 报错附缺失字段的 type/description/enum 一行提示 - 深层 type mismatch 报错附该字段 enum/description 后缀 - +batch-update 顶层 --sheet-id/--sheet-name(含下划线拼写)特判, 直接指明 per-op locator 契约,不给误导性 fuzzy 建议 - batch sub-op input 拒收 cell_styles/styles/cell_merges 包裹结构, 报错教学扁平 flags 写法 - 守护测试锁定 wrappedSubOpInputKeys 与 batchOpDispatch 的互斥假设 约束:不改 legacy 报错措辞前缀、保留 --print-schema 指针、不动宽松 AdditionalProperties 设计(内嵌 schema 当前无 strict 节点,该路径为预置)。 验证:gofmt -l 无输出、go vet 干净、go test -count=1 ./shortcuts/sheets/ ./internal/suggest/ 全部通过。 |
||
|
|
37d490a198 |
fix: unify dry-run output contract (#1870)
* fix: unify dry-run output contract
* fix: address dry-run review feedback
* fix(dryrun): tighten preview contract and unify data shape
- transcribe HTTP method verbatim in previews (HEAD/OPTIONS were
reported as GET); reject an empty method in api with a typed error
- unify the dry-run data payload across api/service/shortcut paths:
{api, context?: {app_id, user_open_id}}; drop data.as — the envelope
top-level identity is the single identity source
- mark pretty dry-run stdout with '# dry-run: request not sent' so logs
that drop stderr still show it was a preview
- extract the shared preview builder, collapse PrintDryRunWithFile's
loose params into FileUploadMeta, and fail loudly on nil previews
- revert description-marker identity parsing: stale prose must not
override corrected accessTokens (blocks legal user calls on
images.create); identity gating keys off accessTokens only
- pin the new contracts with tests: verbatim method, three-way context
parity, nil-preview error, empty-context omission, marker line
* docs(agents): add typed-data, faithful-transcription, and contract-test conventions
- typed struct at the boundary over map[string]interface{} threading;
distinct types where values could swap silently (internal/meta.Token)
- transcribe input verbatim in previews/transformations; reject
unhonorable flag combinations with typed errors instead of silently
substituting behavior
- contract tests must fail when the implementation is reverted
* test: migrate dry-run tests grown on main to the envelope format
main gained raw-format dry-run readers while the PR was in flight
(wiki drive export #1802, drive list comments #1845, slash commands,
sheets history, docs fetch, mail draft-send/triage, vc meeting events).
Migrate them to the envelope accessors (clie2e.DryRunGet / data-wrapped
decoders) and drop the now-redundant DryRunData extractions in files
unified on DryRunGet.
---------
Co-authored-by: guokexin.02 <264159873+Tantanz20020918@users.noreply.github.com>
|
||
|
|
e79d49e7e4 |
Merge lark sheets development branch (#1833)
* feat(sheets): support font_family in cell styles (#1549) Add a font_family field to cell_styles so a cell's font name can be set and read back through every style entry point: - +cells-set (--cells JSON) and +cells-set-style / +cells-batch-set-style gain a font_family field / --font-family flat flag - +workbook-create / +table-put --styles accept font_family in cell_styles - +cells-get returns font_family helpers.go buildCellStyleFromFlags reads the --font-family flag; lark_sheet_workbook.go allows font_family in the --styles cell_styles whitelist; data/ + skills/ are synced from sheet-skill-spec. * docs(sheets): inline editing rules into SKILL.md and clarify flag descriptions - Move cross-cutting editing rules and execution notes into the root SKILL.md and drop the now-redundant core-operations reference - Clarify flag descriptions: offset must be explicit inside +batch-update, range prefixes written bare (no quotes), chart requires a dim index, untyped --values lose date/number types, ungroup level semantics - Sync the corresponding reference docs * feat(sheets): add --type bitable to +sheet-create for creating bitable sub-sheets (#1520) * perf(sheets): cap fan-out cell-matrix materialization to prevent OOM (#1578) * perf(sheets): cap fan-out cell-matrix materialization to prevent OOM The +cells-set-style / +dropdown-set / +cells-batch-set-style / +dropdown-update shortcuts expand a single A1 range into a rows×cols matrix of per-cell maps client-side (the backing set_cell_range tool takes an explicit cells matrix). rangeDimensions() had no upper bound, so a tiny input like "A1:Z100000" balloons into ~2.6M heap maps (~900MB, doubled again by json.Marshal) and can OOM the process before the request is even sent. Add a 50000-cell safety cap (checkStampMatrixBudget) gating every fan-out materialization point, matching the documented but never-wired --max-cells default. Oversized ranges now fail fast with a clear validation error instead of allocating. Also preallocate the per-op slices now that the range count is known up front. Adds benchmarks + a boundary test as regression guards. * perf(sheets): cap table-put/batch fan-out materialization (siblings of the cell-matrix cap) The single-range fan-out cap (maxStampMatrixCells) left three sibling ingress paths uncapped, each able to materialize an unbounded matrix or op set in memory before the request leaves: - +table-put / +workbook-create --sheets/--values: buildSheetMatrix builds the whole rows×cols matrix before slicing it into per-write batches; tablePutMaxCellsPerWrite only bounds the batch size, not the total input. Add tablePayload.checkCellBudget (1M-cell guardrail), enforced in validate() and in buildValuesPayload (the --values path bypasses validate()). - batch fan-out (+cells-batch-set-style / +dropdown-update): per-range checkStampMatrixBudget can't stop many ranges from summing past the cap. Add an aggregate cell budget (checkBatchStampBudget) and a shared maxBatchRanges (100) count cap in validateDropdownRanges — covering all fan-out commands and replacing the now-redundant +dropdown-delete count check. - +batch-update: cap --operations at maxBatchOperations (100) in translateBatchOperations. Adds boundary regression tests for each cap. go vet + gofmt clean; full shortcuts/sheets + backward suites green. * test(sheets): measure table-put matrix materialization cost Add BenchmarkBuildSheetMatrix_* and TestTablePutMatrixPeakMemory mirroring the fan-out probes. Confirms the +table-put/+workbook-create ingress has the same OOM profile as the single-range stamp: 2.6M cells → ~917 MB / 5.3M allocs (+875 MB resident heap) materialized before the first write — now rejected up front by checkCellBudget. * feat(pivot): lark-sheets pivot reference 补 +pivot-list info 说明与落点覆盖校验 +pivot-list 返回 info(page_range/content_range/error_state 等): 1) 判断目标单元格在透视表内(改配置 +pivot-update)还是区域外(改值 +cells-set); 2) 透视表展开后会覆盖已有数据,落点强烈优先默认自动新建子表; 3) 创建后用 info.error_state / content_range 校验有没有覆盖/冲突。 * feat(sheets): add +formula-verify shortcut for verify_formula tool Wraps the new verify_formula read tool in a CLI shortcut so AI agents can run write-then-zero-error verification end-to-end: lark-cli sheets +formula-verify --url <url> Scans formulas + cell error states across one or more sub-sheets and returns a JSON status report (success / errors_found / partial). Aggregates all 7 Excel error categories (#REF! / #DIV/0! / #VALUE! / #NAME? / #NULL! / #NUM! / #N/A) plus compile failures into one envelope; the tool always reports every error in the scan window — callers needing a subset filter the returned error_summary client-side. The internal scan cap is hidden from callers; when it trips the response sets has_more=true and includes a warning_message asking the caller to narrow --range / split --sheet-id and continue. Flags follow the lark-sheets convention: - --url / --spreadsheet-token (XOR public) - --sheet-id / --sheet-name (repeat or comma-separate; mutually exclusive) - --range (repeatable A1) - --max-locations (default 20) - --exit-on-error (CI gate: status='errors_found' → exit 2 with failed_precondition) Generated artifacts (skills/lark-sheets/{SKILL.md, references/ lark-sheets-formula-verify.md}, shortcuts/sheets/data/flag-defs.json, shortcuts/sheets/flag_defs_gen.go) are mirrored from sheet-skill-spec generated/ via 'npm run sync:cli'. shortcuts.go registers FormulaVerify alongside the other lark_sheet_formula_verify skill shortcuts so +formula-verify is discoverable from 'lark-cli sheets --help'. Tests cover the dry-run wire shape (excel_id + sheet_ids/sheet_names/ ranges/max_locations packing), the read scope (invoke_read URL), the mutually-exclusive selector validation, the non-positive --max-locations guard, and the --exit-on-error status matrix (success/partial/errors_found/unknown). * feat(sheets): add +history-list / +history-revert / +history-revert-status shortcuts BE-1 + BE-2 (larksuite/cli lark-sheets) for spec sheet-history-revert. Three thin callTool wrappers over facade-agg history tools, following the existing sheets Validate/DryRun/Execute + --url/--spreadsheet-token(/--token) locator convention: - +history-list (read, history_list): passes the tool output through verbatim; facade-agg already does the minor_histories/4-field/RFC3339 transform. - +history-revert (write, history_revert): --history-version-id required, enforced at Validate stage with a typed *errs.ValidationError (no request on missing); returns the async receipt. - +history-revert-status (read, history_revert_status): polls in-progress / success / failure. Flags declared inline (not via *_gen.go) — flag_defs_gen.go / data/flag-defs.json are synced from sheet-skill-spec (BE-3) and must not be hand-edited. Notes: - history_revert / history_revert_status depend on facade-agg's downstream RPC wiring, a DEFERRED follow-up; the tools return a "not wired yet" guard today. These CLI wrappers are correct and go live when the backend follow-up lands. +history-list is fully functional now. - TestFlagDefsGen_MatchesJSON fails on baseline (pre-existing BE-3 gen/json drift); resolves once BE-3 sync:cli regenerates flag defs for these shortcuts. Validation: go build ./shortcuts/sheets/... PASS; new tests (TestHistoryShortcuts_DryRun, TestHistoryRevert_MissingVersionID) PASS. Spec source: active@2acd94a24ac3f835357a274a02344f78435bcc1c39ad0d695ce587f0cbddfb21 * chore(sheets): sync lark_sheet_history skill + flag defs from sheet-skill-spec (BE-3) Synced artifacts for the history shortcuts from ee/sheet-skill-spec (SSOT), landed surgically (history-only) to avoid regressing this branch's newer skills/lark-sheets content: - skills/lark-sheets/references/lark-sheets-history.md (new, mirrored). - skills/lark-sheets/SKILL.md: + Lark Sheet History references-table row only. - shortcuts/sheets/data/flag-defs.json: + 3 history shortcuts (additive; no existing entries touched). - shortcuts/sheets/flag_defs_gen.go: regenerated via go generate ./shortcuts/sheets/... (this also resolves the pre-existing flag-defs/gen drift — TestFlagDefsGen_MatchesJSON now passes). NOT a full mirror: the rest of skills/lark-sheets/ + flag-schemas.json on this branch (feat/lark-sheets-develop) are NEWER than the sheet-skill-spec worktree's canonical (e.g. /wiki/ URL support, schema_version 3). A wholesale sync:cli would have reverted them, so only the history delta is taken here. Full re-sync should happen once sheet-skill-spec canonical is realigned with this branch. Validation: go generate clean; go test ./shortcuts/sheets/ (TestFlagDefsGen_MatchesJSON, TestHistory*) PASS. Spec source: active@2acd94a24ac3f835357a274a02344f78435bcc1c39ad0d695ce587f0cbddfb21 * fix(sheets): +history-revert-status keys on --transaction-id, not version id BE-2 gap surfaced by PPE E2E: +history-revert-status sent history_version_id, but the facade-agg history_revert_status tool keys on transaction_id (the async receipt returned by +history-revert), so it returned "[40400] transaction_id is required". Give the status shortcut its own --transaction-id flag + input (excel_id + transaction_id); revert keeps --history-version-id. Tests updated. * fix(sheets): align history flag-defs with inline shortcuts (green TestFlagsFor) TestFlagsFor_EveryRegisteredCommandHasDefs was RED: generated flag-defs drifted from the hand-written history shortcuts. - +history-revert-status: flag-defs had --history-version-id; the BE-2 fix switched the shortcut to --transaction-id. Updated the entry to transaction-id. - +history-revert / -status --history-version-id were marked required="required", but the inline flags are cobra-optional (requiredness enforced in Validate). Set required="optional" to match. Regenerated flag_defs_gen.go. NOTE: canonical source is sheet-skill-spec (BE-3); apply the same change upstream or the next sync:cli will regress this. * chore(sheets): sync lark-sheets-history reference from spec (BE-2 transaction-id) Mirror the upstream BE-2 fix in canonical-spec/references/lark_sheet_history/ cli-reference.md: +history-revert-status now uses --transaction-id (taken from the async receipt returned by +history-revert), and +history-revert's --history-version-id flips required→optional (Validate enforces requiredness at runtime). This file is the only history-only delta from the upstream sheet-skill-spec sync; the rest of skills/lark-sheets/ stays on the cli's newer baseline (/wiki/ URL support, +cells-set-image / +float-image-create, etc.) to match commit 8ae516db's history-only mirror policy. Spec source companion change: feat/sheet-history-revert in ee/sheet-skill-spec, canonical-spec/{tool-shortcut-map.json,references/ lark_sheet_history/cli-reference.md}. * feat(sheets): +history-list --end-version for backward pagination Spec follow-up sheet-history-revert: thread the history_list pagination contract through the +history-list shortcut. - shortcuts/sheets/lark_sheet_history_list.go: + --end-version (int, optional). Mapped to the tool input's `end_version` only when explicitly set (so the server treats absence as "first page / latest"), via runtime.Changed / runtime.Int (matches the +formula-verify --max-locations precedent). + Tip: pass next_end_version from the response on the next call; capture exits the pagination loop when the server omits the field. - shortcuts/sheets/lark_sheet_history_test.go: + dry-run case asserting --end-version 12345 lands as input.end_version=12345 (post-JSON unmarshal float64). - skills/lark-sheets/references/lark-sheets-history.md: synced from ee/sheet-skill-spec (commit 39c6b61). Adds the "倒序分页" caveat row + --end-version flag + pagination Examples line. Drops the internal MajorHistory.Version implementation detail per spec follow-up. - shortcuts/sheets/data/flag-defs.json: synced from spec (+history-list +--end-version int optional). - shortcuts/sheets/flag_defs_gen.go: regenerated via `go generate ./shortcuts/sheets/...`. Companion changes: - ee/sheet-skill-spec MR !37: spec-tables + tool-schemas pagination contract (commits 09e8604, 39c6b61). - ee/sheet-facade-agg MR !1028: history_list tool plumbs end_version, emits next_end_version + has_more (omitted at earliest page), defaults PageSize=20 to datarpc. Validation: - go build ./shortcuts/sheets/... PASS - go test ./shortcuts/sheets/... PASS (sheets + backward) - TestHistoryShortcuts_DryRun (5 cases incl. new --end-version case): PASS - TestHistoryRevert_MissingRequiredFlag: PASS - TestFlagsFor_EveryRegisteredCommandHasDefs: PASS - TestFlagDefsGen_MatchesJSON: PASS * fix(sheets): make +history-revert --history-version-id cobra-required + revert max-cells default drift Two issues surfaced during MR !37 review: 1) +history-revert --history-version-id requiredness was set as "optional" in the spec table (BE-2 fix dc5fe0ea) so cobra wouldn't block before Validate. Per upstream review the flag should be required-by-cobra so the user gets the standard "required flag(s)" gate immediately and the runtime contract matches the JSON shape. - shortcuts/sheets/lark_sheet_history_revert.go: historyVersionIDFlag now sets Required: true. Validate keeps a trim/empty-string guard so '--history-version-id ""' still fails as a typed *errs.ValidationError (cobra accepts empty strings as "set"). - shortcuts/sheets/data/flag-defs.json: +history-revert --history-version-id required: optional -> required. - shortcuts/sheets/flag_defs_gen.go: regenerated. - shortcuts/sheets/lark_sheet_history_test.go: TestHistoryRevert_MissingRequiredFlag split into per-shortcut subtests; +history-revert asserts cobra's "required flag(s)" contract (raw err — the test rig calls cmd.Execute directly so it doesn't see the cmd dispatcher's typed envelope wrap); +history-revert-status keeps the typed *errs.ValidationError contract (its --transaction-id stays cobra-optional + Validate-enforced). 2) max-cells safety cap was accidentally rewritten from 200000 to 50000 by the last sync from sheet-skill-spec (the spec canonical side fell out of date — fixed separately on the spec MR follow-up). Restore desc: "Safety cap; default 200000" / default: "200000" so +cells-get / +csv-get keep the documented cap. Validation: - go test ./shortcuts/sheets/... PASS - TestHistoryRevert_MissingRequiredFlag (both subtests) PASS - TestHistoryShortcuts_DryRun (incl. +history-list pagination case) PASS - TestFlagsFor_EveryRegisteredCommandHasDefs PASS - TestFlagDefsGen_MatchesJSON PASS * fix(sheets): make +history-revert-status --transaction-id cobra-required (match +history-revert) Companion to commit 6ca35b06: same gating model now applies to both history receipts. - shortcuts/sheets/lark_sheet_history_revert.go: transactionIDFlag.Required=true. Validate keeps a trim/empty-string guard for '--transaction-id ""'. - shortcuts/sheets/data/flag-defs.json: +history-revert-status --transaction-id required: optional -> required (synced from sheet-skill-spec @9ca814d). - shortcuts/sheets/flag_defs_gen.go: regenerated. - shortcuts/sheets/lark_sheet_history_test.go: TestHistoryRevert_MissingRequiredFlag/+history-revert-status moved to the cobra "required flag(s)" text contract (the test rig invokes the shortcut via cmd.Execute, which sees the raw cobra error directly without the dispatcher's typed wrap). Drop now-unused `errors` and `errs` imports. Validation: - go test ./shortcuts/sheets/... PASS (sheets + backward) - TestFlagsFor_EveryRegisteredCommandHasDefs: PASS - TestFlagDefsGen_MatchesJSON: PASS - TestHistoryRevert_MissingRequiredFlag (both subtests): PASS * docs(sheets): sync history skill reference required badges from spec Companion to commit 9fa73312 (transaction-id) and 6ca35b06 (history-version-id): the two flag tables in skills/lark-sheets/references/lark-sheets-history.md still showed 'optional' even though the canonical contract — and shortcuts/sheets/data/ flag-defs.json — already moved to 'required'. The earlier syncs only picked up the data file from spec; the skill markdown drift slipped through. Pull in the spec-side regenerated reference (ee/sheet-skill-spec @9ca814d) so the human-readable doc matches the wire contract. * fix(sheets): lower cells-set --max-cells default to 50000 * docs(sheets): clarify workbook-import over read-then-recreate in skill * docs(sheets): bump lark-sheets skill version to 3.0.1 * docs(sheets): clarify number-vs-text typing and copy-to-range template guidance in references * docs(sheets): type by data nature, add pre-write reference column and chart/cond-format/filter rows - SKILL.md quick-reference: add a "read before acting" column pointing each intent at its reference doc; add chart / cond-format / filter rows. - Reframe number-vs-text decision to follow the data's nature (measure vs identifier), not whether the current task happens to sort/sum; a leaderboard/report "display only" use does not make a percentage text. - write-cells reference: mirror the same rule and the +cells-set fallback for layouts +table-put cannot express. * docs(sheets): tighten number-vs-text guidance and dedupe write-cells reference * Feat/lark sheets develop wzz (#1719) * feat(sheets): add +changeset-get shortcut for changeset review Wrap the get_changeset read tool: fetch the raw changeset (edit actions) between two versions to review whether an AI edit fulfilled the request. --start-revision required, --end-revision optional (defaults to latest), gap capped at 100. Adds flag-defs entry + regenerated gen, the ChangesetGet shortcut + tests, and skill docs. * feat(sheets): add +get-revision shortcut Return a spreadsheet's current document revision without pulling the full sub-sheet listing. +get-revision is a read-only derivative over get_workbook_structure (the lightest read — token only, no range) that projects the response down to the single revision field. Adds flag-defs entries and a unit test for the projection helper. * feat: 同步 spec 修改 * feat(sheets): rename +get-revision to +revision-get * feat: 移除 ppe 环境请求头 --------- Co-authored-by: wenzhuozhen <wenzhuozhen@bytedance.com> * docs(sheets): dedupe +changeset-get flag def and skill reference entry * feat(sheets): accept local_office_ token prefix for image parent_type The synthetic token prefix for imported office spreadsheets is being renamed from fake_office_ to local_office_. Accept either prefix when mapping a spreadsheet token to the drive media parent_type so image uploads keep working across the rename (main package and backward compat copy). * fix(sheets): replace undefined common.FlagErrorf with sheetsValidationForFlag changesetRevisions called common.FlagErrorf, which does not exist, breaking the build. Use sheetsValidationForFlag so the errors carry the offending flag param like the rest of the sheets validation paths. Also reword two doc comments in lark_sheet_history_revert.go that used '' for an empty shell string: gofmt (Go 1.19+) rewrites '' in doc comments to a curly quote, leaving the file permanently unformatted. * fix(sheets): satisfy errs-no-bare-wrap forbidigo and errorlint rules from main main introduced the errs-no-bare-wrap forbidigo rule and errorlint coverage that flag 27 issues in existing sheets code after the merge: - Replace direct *errs.ValidationError type assertions with errors.As in sheetsInputStatError and validateSheetMediaUploadFile so wrapped errors still match (errorlint). - Type the embedded flag-schemas.json parse failure as an InternalError with cause; it reaches the user directly via --print-schema. - Annotate genuine intermediate errors (recursive schema validator, batch sub-op raw type checks, A1 range/position parsers) with //nolint:forbidigo; every caller wraps them into typed flag validation errors. * docs: tighten formula verify workflow guidance * docs: align formula verify refs with file names * feat(sheets): let typed writes style blank cells past the data extent +workbook-create / +table-put apply cell_styles by writing them into the in-memory matrix, whose size was fixed to the data (cols × rows). A style range reaching past that extent was rejected as "outside the write range", so blank cells (reserved regions, decorative headers, empty borders) could not be styled on the typed --sheets path — only the untyped --values path padded for it. Pad the matrix down/right to cover every cell_styles range before applying (empty cells appended for the uncovered positions), mirroring the --values behavior. writeSheetData now derives the written width/range from the padded matrix; both dry-run previews and sheetCreateDims account for the style extent so the physical grid and the plan match Execute. Ranges above/left of the write anchor stay rejected (the matrix only grows down/right). * docs(sheets): warn that +csv-put silently coerces numeric-looking labels Add guidance that +csv-put numericizes date-like/ID-like columns whose values are all digits (12.10 becomes 12.1 losing the trailing zero, 001 becomes 1 losing the leading zero); recommend +table-put with dtypes=object/datetime64 or +cells-set + number_format="@". Also fix the batch-update example to use sheet_name instead of sheet_id. * docs(sheets): steer import-vs-append onto sheet-copy for existing workbooks * docs(sheets): warn that cells-clear --scope all is irreversibly destructive * docs(sheets): sync chart schema and labels guidance (#1716) * chore(sheets): update chart flag schema * docs(sheets): clarify chart labels field is presence-toggle, not value-toggle Synced from sheet-skill-spec. Chart labels (plotArea.plot.labels and per-series labels) are toggled by object existence — passing labels at all turns data labels on, even when value/category/series/percentage are all false (server falls back to showing value). Models repeatedly try `{ value: false, category: false, series: false }` to disable, which silently shows the value fallback. The reference doc now spells out both directions: pass labels to show, omit the whole labels field to hide. Also picks up earlier spec-side drift not yet propagated: - pivot-table reference: +pivot-list info return + overlap validation - flag-defs: cell-matrix fan-out cap default 200000 -> 50000 (#1578) * feat(sheets): drop pre-refactor aliases from `sheets --help` listing The refactored + commands have been the default for over a month. Hide the deprecated pre-refactor aliases from `sheets --help` via a custom cobra usage template that skips the deprecated group. Aliases stay registered and executable: their own `sheets <alias> --help` still shows the (→ +new-command) pointer, unknown-subcommand suggestions still span them, and execution still returns the _notice. * feat(sheets): let +csv-put fall back to piped stdin when --csv is omitted Agents routinely redirect a CSV into stdin but forget the `--csv -`, so `+csv-put ... < data.csv` failed its first try on a missing --csv and cost an extra round-trip (error, then --help, then retry). Relax --csv's cobra required-gate in the shortcut's PostMount and install a PreRunE that defaults an omitted --csv to "-" when stdin is a non-interactive pipe, so the standard stdin-resolution path reads it. The pipe guard keeps an interactive terminal from blocking on stdin, and a genuine miss (no piped data) still surfaces csvPutInput's typed "--csv is required" instead of cobra's bare "required flag(s) ... not set". Scoped entirely to the sheets domain — no changes to the shared runner or the flag schema. * feat(sheets): rework +rows-resize / +cols-resize to --height / --width 从上游 sheet-skill-spec 同步:+cols-resize 用 --width、+rows-resize 用 --height 直接给像素值, --type 变为可选(省略等价于 pixel)。--type standard/auto 走非像素模式,不能与像素 flag 同传; --type pixel 与 --width/--height 共存时视为等价形式。--size 已删除。 * docs(sheets): 更新 lark-sheets skill 版本至 3.0.2 将 SKILL.md 版本号从 3.0.1 升至 3.0.2,同步近期 sheets 命令改动(+rows-resize/+cols-resize 改 --height/--width、 +csv-put 支持 stdin 回退等)后的技能版本。 * feat(sheets): add --widths / --heights map form for per-column/row sizes 从上游 sheet-skill-spec 同步:+cols-resize --widths / +rows-resize --heights 接收 JSON map(键为单行列或闭区间,值为像素或 "standard"/"auto"),CLI 按起始位置排序后 展开为一次原子 batch_update 的多个 resize_range 操作,多列不同宽 / 多行不同高一次 调用完成,不再需要 +batch-update。map 形态与 --range/--width/--height/--type 互斥, 不可作为 +batch-update 子操作嵌入(batch_update 不支持嵌套)。列宽 < 20px 拒绝并提示 Excel 字符单位换算(px ≈ 字符数×8+16);--print-schema --flag-name widths/heights 可查 schema。 * fix(sheets): sync flag input/enum fixes from sheet-skill-spec 上游修复 spec-table 的 Input/Enum 字符串惯例后重新生成:--widths/--heights 现在带 file/stdin 输入声明,+sheet-create --type 的枚举正确进入 flag defs 与文档。 * feat(sheets): add sheets-scoped flag ergonomics via PostMount Two recovery loops from the edit-eval traces burn agent round-trips: hallucinated flag names (--cols for --range) whose unknown-flag error only points at --help, and enum values imported from CSS/Excel vocabulary ("center" for the vertical alignment Lark spells "middle"). - unknown-flag errors now inline the full valid-flag list (semantic guesses aren't rankable by edit distance; kills the --help round trip) - enum values with an unambiguous canonical form (casing, known alias) are normalized in place and the call proceeds; edit-distance typos stay errors with a did-you-mean hint and are never auto-applied Both ride the existing PostMount composition (same pattern as withTokenAlias), so the common framework is untouched and no other domain's behavior shifts. * feat(sheets): make validation errors prescriptive for hot failure modes Driven by the edit-eval-extra-35Q reports: ~70% of lark-cli sheets errors were missing-required / JSON-shape / wrong-value classes whose messages said what broke but not how to fix it, pushing agents into --help / --print-schema probe loops. - composite JSON shape errors inline a compact skeleton auto-generated from the schema (e.g. --cells -> [[{"value": ...}]]) when the type mismatch is shallow container confusion - +batch-update: missing 'shortcut' shows the entry template; a disallowed shortcut inlines the full allow-list; exceeding the 100-op cap says how many batches to split into; sub-op translator failures append the shortcut's complete input-key contract - +table-put: dtypes/formats keys that miss every column call out the A1-letter habit and inline the declared column names; empty cells in a date-typed column name the three ways out - schema enum errors suggest across casing, vocabulary aliases, and edit distance * fix(common): steer rejected @file paths to stdin instead of cd The absolute-path rejection hint said "cd to the target directory first" - advice the lark-sheets skill explicitly tells agents not to follow (it pollutes the working directory). The stdin-contention hint also demonstrated @file with an absolute path, which would itself be rejected. - @file failures on stdin-capable flags now show the equivalent stdin invocation (--csv - < /tmp/x.csv) - the path error recommends a relative path or stdin, not cd - the stdin-contention example uses a relative @file path Message-text only; no control-flow change for any domain. * chore(sheets): suppress forbidigo on csv-put stdin pipe detection os.Stdin.Stat is intentional here - pipe detection needs the real process fd; IOStreams.In is a plain io.Reader without Stat. Clears the lint failure left by the stdin-fallback commit. * fix(sheets): pass spreadsheet token to changeset tool (#1839) * fix(sheets): hide bitable sheet creation (#1843) * fix(sheets): resolve revision wiki URLs * fix(sheets): reject overlapping resize ranges * fix(sheets): address remaining review feedback * fix(sheets): avoid credential scanner false positive * fix(sheets): import mislabeled .xls workbooks by sniffing content Local .xls files that are actually OOXML (an .xlsx exported or renamed to .xls) failed +workbook-import with a cryptic backend "xml_version_not_support" because the CLI trusted the file name extension. +workbook-import now sniffs the file's leading magic bytes (PK -> xlsx, OLE2 -> xls) and passes the true extension to the drive import core via a new optional ImportParams.FileExtension override, correcting both the file_extension and the staged media file name (the latter avoids the backend's "import file extension not match", code 1069910). A declared Excel file whose bytes match neither container is rejected locally with a prescriptive error instead of the opaque backend failure. The drive import core gains only the neutral FileExtension override (empty = infer from the file name, i.e. unchanged behavior for drive +import); all Excel sniffing/correction policy lives in the sheets shortcut. * fix(ci): keep semantic waiver fixture active * fix(sheets): close remaining safety gaps * fix(sheets): align history shortcuts with generated flags Use generated flag defs for history revert commands, enforce control-character validation, and sync the refreshed lark-sheets references from sheet-skill-spec. * fix(sheets): require confirmation for history revert * fix(sheets): require explicit csv input --------- Co-authored-by: xiongyuanwen-byted <xiongyuanwen@bytedance.com> Co-authored-by: wuyanchun.anunwu <wuyanchun.anunwu@bytedance.com> Co-authored-by: wenzhuozhen <wenzhuozhen@bytedance.com> |
||
|
|
4c31323de1 |
feat(sheets): use office_sheet_file parent_type for imported office spreadsheets (#1606)
Image uploads to a spreadsheet hard-coded parent_type=sheet_image at every entry point. Imported "office" spreadsheets carry a token prefixed with "fake_office_", for which the drive backend requires parent_type=office_sheet_file. Funnel the parent_type selection through a single sheets-domain helper so the rule lives in one place and every image-upload path (float-image, +cells-set-image, backward +media-upload, and every dry-run preview) stays consistent. - Add sheetMediaParentType(token) in the sheets domain: returns office_sheet_file for fake_office_-prefixed tokens, otherwise sheet_image. - Add an uploadSheetImage(...) collector that builds the DriveMediaUploadAllConfig (including parent_type) once, replacing the per-call-site hand-rolled configs. - Route both main-domain image entries through the collector — float-image local upload and +cells-set-image — covering Execute and the dry-run preview body/desc. - Cover the backward +media-upload entry: single-part, multipart (>20MB), and both dry-run bodies. backward is a separate package and an intentional verbatim mirror of shortcuts/sheets/, so it keeps its own copy of the helper rather than importing the main domain. - Leave the shared common.UploadDriveMediaAllTyped upload layer untouched — the fake_office_ rule is sheets-specific and must not leak into mail/slides/doc/drive/base. Tests: - Pure-function TestSheetMediaParentType (5 cases incl. prefix-only and mid-string non-match). - Main-domain dry-run TestCellsSetImage_DryRunOfficeParentType and TestUploadSheetImage_ParentType / _FileOpenError that exercise the Execute path on the wire, asserting parent_type via the captured multipart body and typed validation metadata (errs.ProblemOf category/subtype, fs.ErrNotExist cause preserved) on file open errors. decodeSheetMediaMultipartBody fails fast on NextPart / ReadFrom errors rather than silently producing a partial body. - backward TestSheetMediaUploadExecuteOfficeParentType (real multipart wire) and TestSheetMediaUploadDryRunSmallFileOfficeParentType (small-file dry-run preview for fake_office_). - cli_e2e tests/cli_e2e/sheets/sheets_image_upload_dryrun_test.go: --dry-run end-to-end across +media-upload and +cells-set-image, native and fake_office_ tokens, asserting api.0 is POST upload_all with parent_type=sheet_image / office_sheet_file and parent_node = token. |
||
|
|
bd898a1d74 |
feat(sheets): typed table I/O & error contract, workbook import/export, skill refresh (#1355)
* feat(sheets): add +sheet-show-gridline / +sheet-hide-gridline shortcuts
* docs(sheets): strengthen lark-sheets references for common editing pitfalls
Add targeted guidance to six lark-sheets references to reduce frequent
mistakes when editing spreadsheets through the CLI:
- write-cells: sanity-check units / dimension conversion / quantity factors
before formula writes (formulas can run clean yet be off by a factor);
keep derived output off original data columns to avoid clobbering source
- core-operations: prefer live formulas for derived values even when "live
update" is not explicitly requested; scope rewrite/transform precisely so
rows/columns that should stay unchanged are kept 1:1; treat header-stated
format rules as checklist items; confirm the artifact file actually exists
before finishing; write back bare values from local scripts
- visual-standards: apply border/header formatting on explicit request and
identify the real header row; keep font size consistent with the source
- range-operations: keep total column width within A4 for printing
- read-data: dedup/compare long numbers via raw values, not csv formatted
display (scientific notation collapses distinct numbers and causes false
duplicates)
- chart: format date/number axes via source-cell number_format; place charts
outside the data area so they do not cover existing data
* feat(sheets): implement table-put/table-get and sync skill specs
- Add lark_sheet_table_io.go with +table-put / +table-get and tests
- Refactor read-data; extend workbook; register new shortcuts
- Sync generated flag defs/schemas (go:embed) from sheet-skill-spec
- Sync skill references (write-cells numeric-column guidance, plus
read-data / workbook / chart updates)
* docs(sheets): surface typed-write path at the write-decision point
Quick-ref table (SKILL.md, the first decision point) had no +table-put and
gated typed writes on "DataFrame", so a model holding a Counter/list/dict
would fall back to +csv-put and silently lose number/date fidelity.
- split csv-put row to plain-text values (no numeric/date semantics)
- add +table-put row for typed writes into an existing sheet
- add +workbook-create --sheets row for create + typed write in one shot
- add judgment note: number/amount/date/percent/count -> +table-put
(or +workbook-create --sheets when the workbook does not exist yet);
plain text -> +csv-put
- reframe write-cells scenario row to lead with numeric semantics
- point new-table writes at +workbook-create --sheets (one shot) instead
of the create-empty-then-table-put two-step
Synced from sheet-skill-spec canonical (generate:cli + sync:cli).
* docs(sheets): sync SKILL.md (drop "not for local Excel" caveat)
Mirror the upstream sheet-skill-spec change removing the "not applicable to local Excel files" tail from the sheets skill and reference descriptions.
* docs(sheets): sync SKILL.md (drop "Feishu sheets only" caveat)
Mirror the upstream sheet-skill-spec change removing the "applies to Feishu sheets only" tail from the 14 sheet reference descriptions.
* feat(sheets): add +workbook-import wrapping the drive import core
Import a local xlsx/xls/csv as a new spreadsheet by delegating to the shared drive import flow with the target type pinned to sheet. Refactor drive +import to expose ImportParams / ValidateImport / PlanImportDryRun / RunImport (behavior unchanged, existing drive tests still cover it); sheets reuses them. Regenerate flag_defs_gen.go and sync the spec mirror.
* refactor(sheets): reuse the drive export core in +workbook-export
Replace +workbook-export's parallel export-task implementation with the shared drive ExportParams/RunExport core (pinned to type=sheet). Drops ~90 lines of duplicated poll/download code; +workbook-export now inherits drive's ctx cancellation, resume-on-timeout, filename sanitize/overwrite, and the full set of export status labels. The output contract aligns with drive's (adds ready/downloaded/doc_type; saved_path preserved). Also normalize an empty drive --output-dir to "." so drive +export behavior is unchanged, and fix the sheets export e2e to call +workbook-export instead of a nonexistent +export.
* docs(sheets): keep original column widths; align chart axis with requested metric
- range-operations: only widen new / overflowing columns; never recompute or
shrink the widths of existing columns (any blanket resize, even by 1px,
breaks the original visual format)
- chart: when the user asks for a share / percentage, the value axis should be
a percentage (pie, or stack.percentage on bar/column) rather than raw counts
* docs(sheets): reword guidance to avoid eval-specific phrasing
Replace scoring-framework wording in the examples with plain functional
consequences (e.g. "not delivered", "goes stale when the source changes",
"breaks the original visual format"), so the references stay agent-facing.
* docs: add lark sheets financial modeling guidance
* docs(sheets): align write-cells reference with the generated output
Bring the hand-applied write-cells example in line with the spec-generated
reference so the CLI mirror is byte-identical to the canonical source.
* docs(sheets): align +csv-put help with formula support
Sync the formula-support wording from sheet-skill-spec (flag-defs, skill
references) and update the hand-authored cobra Description and comment for
+csv-put. +csv-put evaluates a leading-= cell as a formula via
set_range_from_csv; descriptions only, no behavior change.
* docs(sheets): fix invalid +dim-insert example in chart reference
The chart reference's placement example used non-existent flags
--dimension/--start/--end for +dim-insert. The real signature is
--position (required) + --count (required); copying the example
fails Validate with "--position is required". Replace it with
+dim-insert --position V --count 6 (insert 6 columns before V,
i.e. after U), aligning with the sheet-structure reference.
* docs(sheets): chart coordinate base / quoting + filter condition enums
Sync three reference-doc corrections from the spec source:
1. chart: label position.row as 0-based (first row = row:0), distinct
from the 1-based row numbers used by A1 ranges and +dim-insert
--position, removing the row-base ambiguity.
2. chart: convert the three runnable examples whose JSON contains a
quoted sheet prefix ('Sheet1'!A1) from inline single-quoted
--properties '{...}' to a stdin heredoc (--properties - <<'JSON').
Inside an inline single-quoted string bash strips the inner quotes
around the sheet name (and splits names with spaces into words),
corrupting the JSON; a quoted heredoc delimiter performs no shell
substitution and preserves it. Adds a short note on the pitfall.
3. filter / filter-view: add the full conditions[].type x compare_type
enum table (text / number / multiValue / color and their respective
compare_type values and values shape), and call out the
equals/notEquals (with s) vs equal/notEqual (no s) gotcha. The docs
previously only showed two values via examples.
* docs(sheets): label +sheet-create --index as 0-based
The base flag description for +sheet-create's --index omitted the
coordinate base, while its siblings +sheet-move ("Target position
(0-based)") and +sheet-copy already state 0-based. Align the description
so the index base is unambiguous. Synced from the spec source
(flag-defs.json + workbook reference).
* fix(sheets): regenerate flag defs and fix asasalint in table io
* feat(sheets): add counta to chart aggregateType enum
Add `counta` (count non-empty cells, incl. text) to manage_chart_object
dim2.series[].aggregateType in the chart flag schema. `count` only counts
numeric cells, so counting occurrences of a text/category column renders an
empty chart; `counta` enables category frequency counts. Synced from the
sheet-skill-spec canonical schema.
* feat(sheets): make --target-position and --range mutually exclusive on +pivot-create
Both flags map to the same wire field (properties.range), so passing
non-default values for both is ambiguous. Mirror the
--target-sheet-id / --target-sheet-name mutex pattern: --target-position
takes priority over --range, and supplying both with non-default values
is rejected up front with a typed FlagErrorf. --target-position=A1 is
the documented default and is treated as "not set".
Add a symmetric validateCreateInput hook on objectCRUDSpec (alongside
the existing validateUpdateInput), wire it into objectCreateInput, and
inject the pivot-specific check on pivotSpec.
* feat(sheets): rework +workbook-create flags and --styles
- --values builds a type-less typed payload, writing through --sheets' batched set_cell_range path (raw passthrough preserves auto-detect; large tables batch; big ints via json.Number)
- drop --headers (subsumed by --values first row) and --header-style (typed header no longer auto-bold; use --styles instead)
- styles: deep-merge overlapping cell_styles/border_styles fields (was wholesale-replace which dropped fields); add manual border_styles validation (style/weight enums + sides) since --styles is on parseJSONFlagSkip and bypasses the schema validator
- regenerate flag-defs/flag-schemas/skills mirror from sheet-skill-spec (--styles flag + full per-side border schema)
* fix(sheets): add mention_type enum to set_cell_range cells schema
Constrain rich_text mention_type to the proto MENTION_FILE_TYPE set so a
file @mention with an out-of-enum value (e.g. 6 = cloud shared folder) is
rejected by the schema validator before it reaches the server and fails
pb serialization ("mentionFileInfo.fileType: enum value expected").
- data/flag-schemas.json: mention_type gains enum + per-value description
- lark_sheet_write_cells_test.go: cover reject (6) + allow (0 / 2 / 22)
* feat(sheets): implement pandas-split --sheets protocol for +table-put/+table-get/+workbook-create
Synced from sheet-skill-spec canonical (cli:table_put schema +
references). +table-put/+workbook-create accept the new shape via a
tableSheetIn -> tableSheetSpec normalize step (dtype string -> internal
type/format mapping). +table-get emits the same shape so the writer's
df_to_sheet and the reader's sheet_to_df round-trip cleanly.
isoDateToSerial now accepts the full ISO datetime form
(2024-01-15T00:00:00.000, including timezone suffixes) emitted by
df.to_json(date_format="iso"), not just yyyy-mm-dd. End-to-end verified
by the spec repo's contracts/python_helper_roundtrip script against a
real Lark spreadsheet on pandas 2.2 and 3.0.
* feat(sheets): add --dataframe Arrow IPC input for +table-put/+table-get/+workbook-create
Introduce a binary-typed twin of --sheets: --dataframe accepts an Arrow IPC
(Feather v2) payload that pandas' df.to_feather() writes, deriving dtypes and
per-column number formats from the Arrow schema. The two producers are mutually
exclusive and funnel through a shared resolver so +table-put and
+workbook-create stay in lockstep; +table-get gains --dataframe-out for
single-sheet reads. Also auto-grow a sub-sheet's row/column count before
writing so blocks past the backend's default 200x20 bounds no longer fail with
range-exceeds-sheet-bounds.
* docs(lark-sheets): remove financial modeling standards reference
Drop the lark-sheets-financial-modeling-standards.md reference doc and all
pointers to it from SKILL.md, core-operations, and visual-standards. Bump
skill version to 3.0.0.
* docs(lark-sheets): clarify cell-image vs float-image routing and fix reference self-references
Synced from sheet-skill-spec.
- Add a binding-based decision (does the image belong to a record and move with its row?) to route +cells-set-image vs +float-image-create across the SKILL entry, float-image and write-cells references.
- Add routing rows to the SKILL command cheat-sheet and warn against defaulting to float-image out of familiarity.
- Replace mislabeled 本 skill / 子 skill / 跨 skill wording in references with 本文 / reference names, matching the existing convention.
* feat(sheets): add --styles to +table-put for one-step typed write with styling
+table-put now accepts --styles (same shape as +workbook-create's --styles):
cell_styles merge into the set_cell_range matrix, while cell_merges /
row_sizes / col_sizes apply as their own tool calls after the write. The
styles payload is name-matched against the written sheets and validated up
front, so a malformed or mismatched style fails before any write lands.
Also points +sheet-create users to +table-put (auto-creates missing sheets)
when they need data/styles, via a runtime Tip and the lark-sheets skill
references. Flag is sourced from the upstream Base table and regenerated
through sheet-skill-spec (flag-defs.json / flag-schemas.json / gen file).
Adds unit tests (dry-run styles, name-mismatch reject, execute) and a
dry-run E2E (tests/cli_e2e/sheets/sheets_table_put_dryrun_test.go).
* docs(lark-sheets): point read-data to +sheet-info for hidden row/col identification
skip-hidden defaults to false (lossless reads), but the read primitives don't mark which rows/cols are hidden. Cross-reference +sheet-info --include hidden_rows,hidden_cols + row_indices/col_indices so agents can identify hidden ranges when they need to filter or interpret hidden data.
Synced from sheet-skill-spec.
* feat(sheets): document link requirement for @document mentions in cells flag schema
@document mentions (mention_type != 0) must pass link (doc URL) to render a
clickable card; @user mentions (mention_type=0) don't need it. Synced from the
upstream tools-schema.
* fix(sheets): reject cond-format attrs whose shape mismatches rule_type
A conditional-format rule created with --rule-type colorScale but
cellIs-shaped attrs ({compare_type,value}, no color) was accepted by
the CLI and written through to the server, producing a color-less
color-scale segment. That dirty data crashes the frontend on snapshot
deserialization, so the spreadsheet can no longer be opened (5005).
The per-entry schema check can't catch this: properties.attrs.items is
a oneOf over all nine attr shapes and passes as soon as any branch
matches, blind to the sibling rule_type — {compare_type,value} matches
the cellIs branch even when rule_type says colorScale. The tool side
maps attrs blindly by rule_type and only validates dataBar count and
iconSet ordering, so the gap reaches the data layer.
Add a cross-field validator (validateCondFormatAttrs) wired into both
create and update via the new objectCRUDSpec.validateCreateInput hook
(twin of validateUpdateInput). It enforces, per rule_type, the keys
every attrs entry must carry — mirroring the tool's converter contract
— and treats an empty required string (notably color) as missing.
Rule types that take no attrs (duplicateValues / uniqueValues /
containsBlanks / notContainsBlanks) and updates that omit rule_type are
left to the server.
* test(sheets): guard condFormatAttrsRequired against flag-schemas drift
Add TestCondFormatAttrsRequired_MatchesSchemaOneOf, comparing the
hand-maintained condFormatAttrsRequired table against the embedded
flag-schemas.json attrs oneOf (multiset of required-key sets, for both
create and update). The cross-field validator only holds if its
per-rule_type required keys mirror the schema branches, and the two
share no compile-time link — this pins them together so a future schema
sync that adds/drops a required key can't silently desync the table.
* fix(sheets): default +table-get to full used range, not A1 current region
+table-get without --range anchored its current_region probe at A1, so an
internal blank row or column silently truncated everything past it — agents
then treated the partial data as complete (the pro016 / pro025 incident).
- Probe the used range over the full physical grid (row_count × column_count
from the workbook structure) so it spans internal blank rows/columns; fall
back to the legacy A1 anchor when dimensions are unknown.
- Emit the actually-read `range` on every sheet so callers can detect
truncation (get_cell_ranges has no has_more flag).
- Fix the same A1-anchor bug in append mode's last-data-row probe, which could
otherwise overwrite data past an internal blank row.
- Add unit + dry-run/live E2E coverage; refresh synced skill docs.
* docs(sheets): fix csv-get current_region guidance to cross-check row_count
current_region is a blank-row/column-bounded block, not the true sheet extent:
an internal blank row truncates it, so it can miss rows past the gap. The
read-data reference previously called it the "真实数据边界" and told agents to
prefer it over row_count — which drove the "read only to current_region's last
row, miss the tail" failure.
- current_region: warn it can be both smaller (internal blank rows truncate)
and larger (trailing summary/signature rows) than the real data range.
- csv-get output contract: clarify its row_count/col_count is the returned size
(= actual_range), not the physical sheet size; has_more only reflects the
current range, not whether the whole sheet was read.
- "确定数据范围的正确流程": add a step to cross-check against +workbook-info's
physical row_count and probe past current_region's last row for data beyond an
internal blank row.
* fix(sheets): collapse duplicate validateCreateInput from bad merge resolution
A prior merge kept both branches' independently-added validateCreateInput
fields on objectCRUDSpec with conflicting signatures (pivot's
func(rt, input) and cond-format's func(input)), plus both call sites in
objectCreateInput, which failed to compile (validateCreateInput redeclared).
Collapse to the single richer func(rt flagView, input) signature and one
call site. cond-format's validateCondFormatAttrs (func(input), still shared
with validateUpdateInput) is wrapped in a closure that ignores rt. Both
behaviors are preserved: pivot --target-position/--range mutex and
cond-format attrs-shape-vs-rule_type validation.
* refactor(sheets): migrate legacy error helpers to typed errs in sheets domain
golangci-lint forbidigo (errs-no-legacy-helper / errs-no-bare-wrap) flagged
the table I/O, workbook, and dataframe shortcuts that landed on this branch:
93 common.FlagErrorf and 48 fmt.Errorf calls.
- Replace every common.FlagErrorf with common.ValidationErrorf (typed
*errs.ValidationError, same signature) across workbook / table_io /
dataframe / object_crud.
- writeDataframeOut's two final --dataframe-out write failures become typed
errs.NewInternalError(SubtypeFileIO, ...).WithCause(err).
- applyWorkbookCreateVisualOps now passes the typed callTool error through
unchanged (re-wrapping would downgrade classification) and attaches the
failing op as a recovery hint only when none is set.
- The remaining fmt.Errorf are genuine intermediate errors that the command
layer re-wraps into typed validation errors (buildTypedCell / Arrow
decode-encode) or surfaces as a partial_success message string
(writeTypedSheets via tablePutPartial); each carries a //nolint:forbidigo
with that reason, per the lint guidance.
No behavior change: error messages and partial-success shapes are preserved;
gofmt, go vet, golangci-lint (0 issues) and sheets tests all pass.
* fix(shortcuts): clarify single-stdin constraint in flag help and error hint
Input flags advertised '(supports @file, - for stdin)' per flag, leading
AI agents to write '--a - <x --b - <y' where the second '<' silently
clobbers the first and the first flag reads the wrong payload. A process
has a single stdin, so at most one flag per call can use '-'.
- Reword the generated help hint to '- reads stdin (one flag per call;
use @file for others)'.
- Add an actionable .WithHint to the stdin-conflict validation error
pointing callers to @file for the extra flags.
- Assert the new hint in TestResolveInputFlags_DuplicateStdin.
* feat(sheets): +cells-get/+csv-get --max-chars 默认值 200000 → 500000
放宽默认防爆上限。flag_defs_gen.go 由 go generate 重生;flag_defs_test.go
的 expected default 同步;flag-schemas.json schema_version 2 → 3 是上游
spec-tables 架构调整带来的元数据 bump,与本业务改动无关、go:embed 不解析
该字段、无功能影响。
Synced from sheet-skill-spec@93f7a78.
* docs(lark-sheets): sync from spec — +csv-put 含逗号公式正例 + 收敛警示标签
源同步自 sheet-skill-spec:write-cells 补含逗号公式 RFC 4180 转义正例与结构化写入优先指引;全 reference 收敛「高频致命错误」类标签。
* docs(lark-sheets): sync from spec — --max-chars 放出为可见 flag + 落盘优先指引
源同步自 sheet-skill-spec:--max-chars 放出(默认 500000,可调小避免大输出被 Bash/终端转存为文件、改 has_more 分页);read-data 增「大数据优先落盘」指引。
* feat(sheets): 写操作报错增强 + --token 别名
- 复合 JSON shape 校验失败时报错附 --print-schema 提示,agent 可直接拿到精确结构(pro26 头号:+cells-set --cells 反复猜 shape)
- JSON 解析失败且该 flag 支持 stdin 时提示改用 stdin(公式/引号/逗号内联到 shell 被转义弄坏 JSON)
- --token 作为 --spreadsheet-token 的解析期别名:复用 sheets 已有 PostMount 钩子 + pflag normalize,仅 sheets 包,common 零改动
* docs(lark-sheets): sync from spec — set+H 改单引号 / 速查表补臆造命令名 / workbook-import 引导
* fix(sheets): migrate +table-put to typed error contract
The merge from main brought in #1449 (retire legacy error envelopes),
which removed output.ExitError / output.ErrDetail and forbids
constructing them. Port tablePutPartial off the legacy envelope:
- no sheets written -> typed errs.APIError (plain failure)
- some sheets written -> ok:false result via runtime.OutPartialFailure
carrying written_sheets, returning the partial-failure exit signal
Also fix two drifts the same merge introduced:
- regenerate flag_defs_gen.go to match the committed flag-defs.json
- update the --max-chars flag test to assert visible (no longer hidden)
* docs(lark-sheets): sync from spec — set+H 告诫通则化(移入 stdin 段)
* feat(sheets): styles 接受 halign/valign 等对齐字段别名
把模型常幻觉的 horizontal_align / halign / vertical_align / valign 映射到
规范字段 horizontal_alignment / vertical_alignment,覆盖 --styles 与 typed
--cells;与规范字段冲突时报错而非静默择一。同步 lark-sheets skill 文档补
对齐字段说明 + --print-schema --flag-name styles 提示。
* feat(sheets): resolve wiki URLs to the backing spreadsheet for --url
Sheets shortcuts only accepted /sheets/ and /spreadsheets/ URLs via --url.
A /wiki/<node_token> URL was rejected with "must be a spreadsheet URL"
because the wiki node_token is not a spreadsheet token: resolving it to the
backing spreadsheet needs a wiki get_node call, which Validate/DryRun (kept
network-free) must not make.
Mirror the existing slides/doc/drive two-stage pattern:
- parseSpreadsheetRef classifies --url / --spreadsheet-token network-free
into a sheet token or an (unresolved) wiki node_token.
- resolveSpreadsheetTokenExec (Execute only) resolves a /wiki/ node_token
via wiki get_node, verifies obj_type=sheet, and returns the obj_token.
The wiki:node:read scope is enforced on this path only, so non-wiki
invocations are unaffected.
- resolveSpreadsheetToken stays network-free for Validate/DryRun, passing
the node_token through unchanged.
All 47 Execute paths (including +batch-update and +workbook-export) switch
to the Exec resolver; Validate/DryRun keep the network-free one. No tool
schema change: the CLI feeds the resolved spreadsheet token as excel_id, so
this is a pure CLI-layer change.
Tested: unit (parse classification + wiki get_node e2e via httpmock) and
live end-to-end against a real wiki spreadsheet (read: +workbook-info,
+cells-get, +csv-get; write: +sheet-create, +sheet-rename, +csv-put).
* docs(sheets): note --url accepts wiki URLs (synced from spec)
* fix(sheets): match --url path segment via url.Parse, not substring
parseSpreadsheetRef classified /wiki/ with strings.Index over the whole URL, so a /sheets/ link whose query or fragment merely contained /wiki/ (e.g. .../sheets/sht?from=/wiki/x) was hijacked into a get_node call. Now parse the URL and match /sheets/, /spreadsheets/, /wiki/ only as a path prefix, mirroring slides parsePresentationRef which already fixed this class. Drop the substring helpers. Also align wiki resolution with slides: CallAPITyped (typed error + log_id) and classify an incomplete get_node response as InternalError instead of a --url validation error. Add regression tests for query/fragment /wiki/ and incomplete node.
* fix(sheets): satisfy errorlint/copyloopvar + regen flag defs
- helpers_test.go: drop the Go 1.22+ redundant `tc := tc` loop copy
(copyloopvar).
- lark_sheet_dataframe.go, lark_sheet_table_io.go: switch the
intermediate-error fmt.Errorf calls from %v to %w so errorlint passes.
Behavior unchanged — these errors are always rewrapped into typed
validation errors at the command layer.
- flag_defs_gen.go: regenerate from data/flag-defs.json (drift from the
wiki-URL merge).
* ci: allow Apache Arrow module in license check
Arrow is Apache-2.0 overall, but it vendors c-ares (LicenseRef-C-Ares,
ISC-like) inside the module which go-licenses classifies as Unknown and
the strict disallowed_types=...,unknown gate rejects.
Pass --ignore github.com/apache/arrow/go/v17 since Arrow is required by
sheets +table-put / +table-get / +workbook-create --dataframe (Arrow IPC
ingest) and the vendored c-ares is not redistributed by us.
* fix(sheets): resolve wiki URL in +range-move/+range-copy Execute
transformExecuteFn (the named Execute helper shared by +range-move and +range-copy) still called the network-free resolveSpreadsheetToken, so a /wiki/ URL reached transform_range as an unresolved node_token and failed. #1519's sweep over Execute hooks only rewrote inline closures; this is the only Execute backed by a named helper. Switch it to resolveSpreadsheetTokenExec (Validate/DryRun stay network-free) and add a +range-move wiki-URL regression test.
* refactor(sheets): drop +table-put manual capacity grow; rely on set_cell_range auto-grow
set_cell_range now auto-grows the sub-sheet to fit the write, so the
ensureSheetCapacity helper (and its modify_sheet_structure dim-insert
call before each write) is no longer needed. This also closes a data-
safety hole flagged in review: inserting before the last existing row
could push real data down into the area set_cell_range was about to
write, and allow_overwrite=false could not protect against it because
the structural insert had already mutated the sheet by the time the
write-collision check ran.
Verified end-to-end against a real spreadsheet: +table-put writing
300x25 into a fresh Sheet1 (default 200x20) succeeds in one write and
the sheet ends up 301x25.
* fix(sheets): close --dataframe stdin guard hole
--dataframe is binary and bypasses the common Input resolver, which is
where the existing single-stdin guard lives. Result: an invocation like
+table-put --dataframe - --styles - was accepted, then one of the two
consumers raced for stdin and the other silently saw an empty stream.
Add a stdinConsumed marker on RuntimeContext that both consumers share:
common.resolveInputFlags sets it when an Input flag uses '-', and
readDataframeBytes both checks and sets it. A second consumer is
rejected up front with an actionable hint pointing at @file.
Flagged in code review (lark_sheet_dataframe.go:93).
* fix(sheets): harden +table-put / +table-get input validation and round-trip safety
Four review-flagged correctness gaps in table I/O, all bundled because
they touch the same file:
1. --sheets accepted trailing data after the first JSON value
(json.Decoder does not surface that, unlike json.Unmarshal). A new
decoderExpectEOF helper rejects e.g. `--sheets '{...} oops'` with a
typed validation error instead of letting the leading object pass
through and surface as a confusing downstream failure.
2. +table-get with a duplicate header (e.g. `amount, amount`) used to
read back successfully — the dtypes map silently collapsed to one
entry — and only failed later on +table-put because the writer
rejects duplicate column names. Fail fast at read time with an
actionable hint to rename or pass --no-header. --no-header mode is
exempt (fallback col<N> names are always unique).
3. +table-put dry-run rendered an invalid range like A1:C0 when
header=false with rows=[]. tablePutFullRange returns "" for an
empty matrix or zero columns instead of building a degenerate
rectangle.
4. +table-get with --sheet-id and a get_workbook_structure miss (read
failure or selector mismatch) used to return a target with
name="", which then broke +table-get → +table-put round-trip (the
writer requires a non-empty sheet name). Fall back to using the id
as the name.
End-to-end verified against a real spreadsheet: trailing data, duplicate
header, and --no-header fallback all behave as advertised.
* fix(sheets): apply +workbook-create style-only ops instead of silently dropping them
A +workbook-create call carrying only cell_merges / row_sizes / col_sizes
(no --values / --sheets and no cell_styles) used to create the workbook
but silently drop the requested visual ops. Two reasons, both fixed:
- workbookCreateStyleDimensions only counted cell_styles when computing
the write extent, so cell_merges / row_sizes / col_sizes always
contributed 0 → buildValuesPayload returned a nil payload → Execute
skipped writeTypedSheets entirely → no visual ops ran. Extend the
helper to fold the merge / resize ranges in.
- Pure row_sizes / col_sizes payloads can never expand a cell rectangle
(they are dimension ranges, not cell ranges), so even with the extent
fix Execute would still skip the write path. Add a no-data branch:
when payload == nil but a styles item is present, look up the default
sheet and apply visual ops directly via applyWorkbookCreateVisualOps.
The dry-run plan mirrors this so the preview shows the visual ops.
Also picks up the --values trailing-JSON-data EOF check (mirror of the
--sheets one in lark_sheet_table_io.go).
End-to-end verified against a real spreadsheet: a cell_merges-only
+workbook-create now produces a sheet with merged_cells_count: 1.
* fix(sheets): preserve causes and render messages cleanly for typed validation errors
common.ValidationErrorf goes through fmt.Sprintf, which does not support
%w — the seven call sites that used `%w` were rendering the cause as
literal `%!w(*fmt.wrapError=&{...})` and dropping the cause from the
typed-error chain (so callers couldn't errors.As back to the underlying
error).
Switch each to `%v` for clean rendering and attach the cause via
.WithCause(err) so the typed contract is preserved. Touched call sites:
- lark_sheet_dataframe.go: --dataframe Arrow decode / stdin read / file
read failures (3 call sites).
- lark_sheet_table_io.go: --sheets invalid JSON, payload-validate
per-cell coercion error, buildSheetMatrix per-cell error,
--dataframe-out arrow encode failure (4 call sites).
End-to-end verified against a real spreadsheet: both invalid-JSON and
typed-cell errors now render readable messages instead of %!w(...).
* sync(sheets): pick up +sheet-{show,hide}-gridline in +batch-update schema
Mirror of the sheet-skill-spec change adding the two gridline shortcuts
to cli-schemas.json batch_update.operations.shortcut enum. Synced from
the upstream canonical via generate:cli + sync:cli.
Verified end-to-end on a real spreadsheet — +batch-update with a
+sheet-hide-gridline op passes schema validation and the backend run
returns succeeded: 1.
* sync(sheets): pick up +workbook-export UX clarification from spec
Mirror of the sheet-skill-spec update that documents +workbook-export's
default-no-download behavior and its relationship to drive +export
--doc-type sheet. Synced from canonical via generate:cli + sync:cli +
go generate.
End-to-end verified against a real spreadsheet:
- Omit --output-path → ok:true, downloaded:false, file_token returned
- Pass --output-path ./crfix_test.xlsx → ok:true, file saved
(17892 bytes), saved_path returned
The --help output for +workbook-export now states the default behavior
and points callers at `drive +export --doc-type sheet` when they need
the --output-dir / --file-name / --overwrite split.
* test(sheets): assert typed errs.Problem instead of err.Error() substrings
Per the coding guideline "Error-path tests must assert typed metadata via
errs.ProblemOf (category / subtype / param) and cause preservation, not
message substrings alone." — sweep through every error-path assertion in
the sheets domain and replace the
`strings.Contains(stdout+stderr+err.Error(), ...)` pattern with two
small helpers landed in helpers_test.go:
requireProblem(t, err, wantCategory, wantSubtype, msgContains)
-> *errs.Problem
requireValidation(t, err, msgContains)
-> *errs.ValidationError // shorthand for CategoryValidation +
// SubtypeInvalidArgument; lets callers
// also assert .Param / .Params / .Cause
~60 assertion sites across 18 test files now check the typed envelope
shape, with message-substring checks moved onto the returned Problem
(.Message / .Hint / .Param). The substring is preserved as a sanity
check rather than the sole assertion, so a category drift like
validation → internal would now fail loudly instead of slipping past.
Cases intentionally left as substring (each with a one-line reason):
- Errors that come straight from cobra's native flag parser (untyped
*errors.errorString — e.g. "required flag(s) ... not set", mutually-
exclusive groups). Re-typing these needs a custom FlagErrorFunc and
is out of scope here.
- Intermediate errors from decodeArrowToSheet that the caller wraps
into a typed envelope (`//nolint:forbidigo` reason). Those unit
tests assert the unwrapped intermediate directly.
One production tweak:
- shortcuts/sheets/flag_schema.go: printFlagSchemaFor returns typed
*errs.ValidationError (with WithParam("--flag-name") on the
unknown-flag branch) instead of raw fmt.Errorf. The framework
already wraps this when called via --print-schema, so user-facing
behaviour is unchanged; direct callers (and tests) now get the
typed envelope.
Verified: go test ./shortcuts/sheets/... passes; golangci-lint
--new-from-rev=origin/main reports 0 issues.
* test(common): assert typed errs.Problem instead of err.Error() substrings
Mirror of the sweep just landed in shortcuts/sheets: replace error-path
substring assertions with typed-envelope checks via two small helpers
landed in a new shortcuts/common/typed_error_assertions_test.go:
requireProblem(t, err, wantCategory, wantSubtype, msgContains)
-> *errs.Problem
requireValidation(t, err, msgContains)
-> *errs.ValidationError // shorthand for CategoryValidation +
// SubtypeInvalidArgument; lets callers
// also assert .Param / .Params / .Cause
8 sites moved to typed assertions across runner_jq_test.go,
mcp_client_test.go, drive_media_upload_typed_test.go, and
runner_input_test.go (the input tests already used a typed-param helper;
this just retargets the substring follow-up onto the typed Message).
Sites intentionally left as substring + comment (production returns raw
fmt.Errorf, not a typed envelope):
- runner_botinfo_test.go (6 sites): BotInfo / fetchBotInfo wrap upstream
errors with fmt.Errorf so the SDK-level message ([99991], 403,
invalid character, etc.) shows through.
- runner_args_test.go (4 sites in 2 tests): rejectPositionalArgs returns
raw fmt.Errorf to satisfy cobra's PositionalArgs contract.
- permission_grant_test.go (2 sites): assert on stderr / hint strings,
not error messages — already out of the err.Error() substring class.
No production code changes.
Verified: go test ./shortcuts/common/... passes;
golangci-lint --new-from-rev=origin/main ./shortcuts/common/... reports
0 issues.
* fix(sheets): plug four +table-put / +table-get correctness gaps flagged in CR
Four review-flagged bugs, all in lark_sheet_table_io.go (bundled because
they touch the same file and the same +table-put / +table-get domain):
1. +table-get --dry-run dropped the --sheet-id / --sheet-name selector
from the get_cell_ranges body, while Execute always passed it. Agents
that validate the dry-run shape and then run live would see a request
shape mismatch. The dry-run now calls sheetSelectorForToolInput so
the body matches Execute.
2. isDateNumberFormat used a simple `strings.ContainsRune(_, 'y')` so
number formats like "JPY #,##0" (a currency prefix that happens to
contain a lone 'Y') were misread as date formats — round-tripping
integer cells out as ISO dates. The detector is now token-aware:
it skips quoted "...", `\\x`-escaped, and `[...]` bracket sections,
and only fires on an unescaped `yy` (a real Excel year token).
3. sheetCreateDims sized new append-mode sheets by `headerOn(s)` only,
but writeSheetData forces a header on empty append sheets when
Header == nil. Near 50000 rows / 200 cols this created the sheet one
row short and the follow-up set_cell_range bounced off the backend
ceiling. Size now matches the forced-header logic exactly.
4. tableGetTargets fallback paths (read-failure / selector mismatch on
--sheet-id) returned a target with name="" — already corrected for
--sheet-id structure-success path in
|
||
|
|
c5b5aece33 |
refactor: retire legacy error envelopes and enforce typed contract (#1449)
* refactor: retire legacy error envelopes and enforce typed contract
Consolidate all command error reporting onto the typed errs.* contract, remove
the legacy error surface that predated it, and tighten the lint guards so the
contract holds across the whole repository going forward.
Every failure now reaches stderr as one envelope shape: a category, an
optional subtype, a human- and agent-readable message, and a recovery hint,
with invalid parameters listed under `params`. The legacy ExitError envelope,
its constructors, and the boundary bridge that promoted untyped config and
authorization errors are deleted, leaving a single path from error to wire.
Predicate commands keep their silent-exit behavior through a dedicated signal
that carries only an exit code.
Infrastructure paths that still emitted ad-hoc envelopes — flag parsing,
unknown commands and subcommands, plugin and policy guards, confirmation
prompts, and auth/config failures — now classify into the same taxonomy.
Business, API, auth, and config exit codes are preserved; the one behavioral
change is that Cobra usage failures (missing required flag, unknown command,
bad arguments) now emit the typed validation envelope and exit 2, matching the
explicit flag and subcommand guards, instead of Cobra's plain-text exit 1.
Enforcement is repo-wide rather than per-path:
- The errscontract guards run by default everywhere instead of through a
migration allowlist, so legacy envelopes cannot be reintroduced anywhere.
- errorlint runs across the whole repository: every error wrap must use %w and
every comparison must use errors.Is/errors.As, so interior wraps stay legal
but can no longer break the chain the typed boundary relies on.
- The errs-no-bare-wrap guard is keyed by structural prefix instead of an
explicit per-domain allowlist, so new shortcut domains are covered without
editing a list. It runs where forbidigo is enabled (the shortcut domains and
the auth/config/service command groups); repo-wide chain integrity for the
remaining command paths is carried by errorlint above.
* test: align cli_e2e success assertions to the ok envelope
The api and service success path now emits the {"ok":true} envelope, so the
cli_e2e workflow assertions that still expected the old {"code":0} shape via
AssertStdoutStatus(t, 0) fail once they run with live credentials. Switch those
workflow assertions to AssertStdoutStatus(t, true); the fake-payload helper test
in core_test.go keeps its code-shape assertion.
|
||
|
|
b07be60068 |
feat(sheets): emit typed error envelopes across the sheets domain (#1348)
Emit structured validation, API, network, file, and internal error envelopes for Sheets shortcuts so users and agents can recover from failed spreadsheet workflows using stable type, subtype, param, and code fields. Add Sheets domain errscontract and golangci guards to prevent legacy envelope and common helper regressions. |
||
|
|
eed711bb11 |
feat(sheets): guard +csv-put --csv against a path passed without @ (#1337)
+csv-put --csv data.csv (a forgotten @) was silently written as one-cell content, because any string parses as valid CSV — unlike malformed JSON it never errored, so the filename landed in the sheet instead of the file's contents. +csv-put's Validate now rejects a --csv value when it names a real file in the cwd subtree (guardCSVValueIsNotFilePath; fileIO.Stat, fail-open), hinting to use --csv @file or stdin (--csv -). Scoped to --csv only — no framework or other-flag change. Checking real existence (not name shape) lets inline content that merely ends in a filename pass through. Adds TestGuardCSVValueIsNotFilePath. |
||
|
|
b07a6003f9 |
feat(sheets): spec-driven shortcut refactor with backward-compatible package (#1220)
* refactor(sheets): rebuild lark-sheets on sheet-skill-spec canonical + One-OpenAPI
Restart lark-sheets as a spec-driven downstream. Skill content (SKILL.md
and 16 references covering 13 operations skills + 3 workflow skills,
including the standalone filter-view skill) is mirrored from the
sheet-skill-spec canonical-spec; do not hand-edit, change upstream and
rerun npm run sync:consumers.
Drop the 11 legacy shortcut sources (spreadsheet / sheet management,
cell ops, dropdown, filter-view, float image, etc.) and 10 associated
tests. Wire up the new sheet_ai/v2 One-OpenAPI single entry that
dispatches by tool_name with JSON-string input/output, and land the
first canonical shortcut +workbook-info as a template that exercises
the public token XOR pair, Risk tiering, and zero-side-effect DryRun.
sheet_ai_api.go provides callTool / invokeToolDryRun and bypasses
runtime.CallAPI's silent swallowing of non-envelope responses so
gateway and business errors from the new endpoint surface precisely.
The remaining 55 shortcuts will be designed and landed separately,
canonical skill by canonical skill.
* feat(sheets): implement lark_sheet_workbook shortcuts (B1)
Land the 8 modify_workbook_structure shortcuts that round out the
lark_sheet_workbook canonical skill alongside the existing +workbook-info:
+sheet-create / +sheet-delete / +sheet-rename / +sheet-move / +sheet-copy
/ +sheet-hide / +sheet-unhide / +sheet-set-tab-color. All eight call
modify_workbook_structure via the One-OpenAPI invoke_write endpoint,
dispatched by the `operation` enum.
Helpers in helpers.go grow publicSheetFlags() / resolveSheetSelector() /
sheetSelectorForToolInput() / sheetSelectorPlaceholder() so future
sheet-level shortcuts share the public --sheet-id / --sheet-name XOR
treatment. +sheet-create intentionally drops the sheet selector pair since
create has no existing-sheet anchor (matches the spec fix in
tool-shortcut-map.json).
+sheet-delete is the first high-risk-write shortcut in the canonical
package; the framework requires --yes (exit code 10 otherwise).
+sheet-move's tool requires source_index in addition to target_index. The
CLI accepts an optional --source-index override and falls back to a
single get_workbook_structure read to derive it (and to resolve sheet_id
from --sheet-name). DryRun stays network-free by rendering <resolve>
placeholders for any field that would need that read.
* feat(sheets): implement lark_sheet_sheet_structure shortcuts (B2)
Add 8 shortcuts under the lark_sheet_sheet_structure canonical skill:
+sheet-info (get_sheet_structure) plus +dim-insert / +dim-delete /
+dim-hide / +dim-unhide / +dim-freeze / +dim-group / +dim-ungroup
(modify_sheet_structure, dispatched by operation enum).
Two reusable conversion helpers cover the impedance mismatch between
the CLI surface and the tool input:
- dimRange / dimPosition translate the CLI's 0-based exclusive-end
range into the tool's 1-based A1 notation. row 5..8 becomes
position "6" + count 3 (insert) or range "6:8" (range ops); column
26..29 becomes "AA:AC".
- infoTypeFromInclude maps the fine-grained --include vocabulary
(row_heights / col_widths / merges / hidden_rows / hidden_cols /
groups / frozen) to the coarse info_type enum the tool accepts;
mixed categories collapse to "all".
+dim-delete is high-risk-write (irreversible row/column removal).
+dim-freeze --count 0 auto-dispatches to operation=unfreeze. +dim-group
accepts --depth for forward-compat with a future server-side nested
group endpoint but does not pass it through today.
* feat(sheets): implement read_data / search_replace / write_cells shortcuts (B3)
Land 11 shortcuts across three canonical skills:
- lark_sheet_read_data (3): +cells-get / +csv-get / +dropdown-get
- lark_sheet_search_replace (2): +cells-search / +cells-replace
- lark_sheet_write_cells (6): +cells-set / +cells-set-style / +csv-put
/ +dropdown-set / +dropdown-update / +dropdown-delete
+dropdown-get reads the data_validation field via get_cell_ranges with
the range carrying its own sheet prefix (no --sheet-id needed). The
fine-grained --include vocabulary (value / formula / style / comment /
data_validation) maps to the tool's coarse include_styles bool plus
value_render_option enum. +csv-get's --include-row-prefix=false strips
the [row=N] prefix client-side because the tool only emits the
annotated form.
+cells-search / +cells-replace flatten the tool's options sub-object
into four independent flags (--match-case / --match-entire-cell /
--regex / --include-formulas) per the flat-flag rule, then repack them on the way
in.
+cells-set takes a raw --data JSON body whose `cells` array must match
the --range dimensions. +cells-set-style fans a single --style block
out to every cell in the range via a new fillCellsMatrix helper; the
range parser (rangeDimensions / splitCellRef / letterToColumnIndex)
only accepts rectangular A1:B2 forms — whole-column / whole-row need
sheet totals and are deferred.
+dropdown-set fans the validation block out to one range; +dropdown-
update / +dropdown-delete iterate sheet-prefixed --ranges and call
set_cell_range sequentially (partial failure leaves earlier ranges
already mutated; the Tip calls this out). +dropdown-delete is
high-risk-write and requires --yes.
+cells-set-image stays deferred to the cli-only batch (needs the
shared local-file upload helper alongside +workbook-create / +dim-move
/ +workbook-export).
* refactor(sheets): move +dropdown-update / +dropdown-delete to lark_sheet_batch_update
Follow-up to B3 after the spec re-mapped these two shortcuts to the
batch_update tool (atomic multi-range CRUD) instead of fan-out via
set_cell_range. Drop their Go implementations + helper validateDropdownRanges
+ splitSheetPrefixedRange from lark_sheet_write_cells.go and remove the
registrations from Shortcuts(); the shortcuts will reappear under
lark_sheet_batch_update during B7.
Also pull in the re-rendered reference docs:
- skills/lark-sheets/references/lark-sheets-write-cells.md
- skills/lark-sheets/references/lark-sheets-batch-update.md
* feat(sheets): implement lark_sheet_range_operations shortcuts (B4)
Land 8 shortcuts across four canonical tools:
- clear_cell_range → +cells-clear (high-risk-write)
- merge_cells → +cells-merge / +cells-unmerge
- resize_range → +dim-resize
- transform_range → +range-move / +range-copy / +range-fill / +range-sort
Three CLI↔tool vocabulary bridges live in this file:
- +cells-clear: --scope content normalizes to the tool's clear_type
"contents" (singular/plural spec mismatch is absorbed in the CLI).
- +dim-resize: --size <px> wraps as resize_{height,width}:{value:N};
--reset wraps as {reset:true}. The two flags are mutually exclusive
and at least one is required.
- +range-fill: CLI's five-valued --series-type collapses to the tool's
binary fill_type — `copy` → "copyCells", anything else → "fillSeries"
(the actual series progression is inferred server-side from the
seed cells in --source-range).
- +range-copy: --paste-type {values, formulas, formats} maps to the
tool's {value_only, formula_only, format_only}; "all" omits the
field entirely so the server applies its default.
+cells-clear is the second high-risk-write shortcut in the package;
the framework enforces --yes with exit code 10 as usual.
* feat(sheets): implement object-list shortcuts (B5)
Land 7 read shortcuts, one per object skill — chart / pivot table /
conditional format / filter / filter view / sparkline / float image. All
share the same shape (public sheet selector + optional <obj>-id filter)
so they're declared via newObjectListShortcut + an objectListSpec.
Notes:
- +cond-format-list exposes --rule-id, which is renamed to
conditional_format_id on the wire (the tool's full field name).
- +sparkline-list exposes --group-id (the higher-level handle); the
tool also accepts sparkline_id, intentionally not surfaced.
- +filter-list takes no id filter — at most one sheet-level filter
per sheet, so the listing is already unique.
- +filter-view-list is `cli_status: cli-only` but get_filter_view_objects
is in mcp-tools.json and dispatches through the same One-OpenAPI
endpoint; no special path required.
* feat(sheets): implement object CRUD shortcuts (B6)
Land 21 shortcuts — three (create / update / delete) per object skill —
backed by the manage_<obj>_object tools dispatched on the operation
enum. Five standard objects (chart / cond-format / sparkline /
float-image / filter-view) share an objectCRUDSpec factory; pivot and
filter are special-cased.
Shared wire contract:
excel_id + sheet_id|sheet_name + operation + [<obj>_id] + [properties]
CLI --data is passed through as the tool's `properties` field as-is, so
callers shape it per each object's spec doc.
Special cases:
- pivot adds optional --target-sheet-id / --target-position on create
(siblings of properties, not inside it).
- cond-format exposes --rule-id (short CLI name) wired to the tool's
conditional_format_id on the wire.
- sparkline uses --group-id (higher-level object handle) instead of
sparkline_id.
- filter has no separate id flag — at most one filter per sheet, so
filter_id is implicit. +filter-create promotes --range to a first-
class flag (instead of burying it inside --data).
- filter-view CRUD are `cli_status: cli-only` but
manage_filter_view_object is in mcp-tools.json, so they go through
callTool / One-OpenAPI alongside everything else.
All delete shortcuts are high-risk-write and require --yes.
* feat(sheets): implement lark_sheet_batch_update shortcuts (B7)
Land 4 shortcuts that all funnel through the batch_update tool's atomic
operations array:
- +batch-update raw passthrough; --data carries the full
{ operations: [{tool, params}, ...] } payload
plus optional continue_on_error. high-risk-write
since the caller may stuff anything inside.
- +cells-batch-set-style --data is [{ranges, style}, ...]; CLI flattens
each (entry × range) pair into a set_cell_range
op with a fan-out cells matrix carrying
cell_styles + border_styles.
- +dropdown-update --ranges + --options (+ --colors / --multiple /
--highlight) — installs/replaces one dropdown
across many ranges, each becoming a separate
set_cell_range op with data_validation in cells.
- +dropdown-delete --ranges — clears data_validation across many
ranges (high-risk-write).
Default is strict transaction: if any sub-tool fails the whole batch rolls
back. +batch-update exposes --continue-on-error to flip the policy; the
three fan-out shortcuts leave it strict (they're meant to be all-or-nothing).
Reinstates validateDropdownRanges + splitSheetPrefixedRange that were
removed during B3 → B7 relocation.
* feat(sheets): implement cli-only shortcuts (B8) — 70/70 complete
Land the four cli-only shortcuts that can't route through the One-OpenAPI
dispatcher (their backing capabilities aren't in mcp-tools.json):
- +workbook-create POST /open-apis/sheets/v3/spreadsheets
+ optional set_cell_range follow-up that zips
--headers and --data into the first sheet starting
at A1.
- +workbook-export POST /open-apis/drive/v1/export_tasks (type=sheet)
→ poll /export_tasks/:ticket up to ~30s
→ optional GET /export_tasks/file/:file_token/download.
CSV mode requires --sheet-id (single sheet export).
- +dim-move POST /open-apis/sheets/v2/spreadsheets/:token
/dimension_range
CLI is 0-indexed inclusive (--start / --end); the v2
endpoint expects half-open [startIndex, endIndex)
so the body uses endIndex = --end + 1. --sheet-name
is resolved client-side to sheet_id via
lookupSheetIndex when needed.
- +cells-set-image common.UploadDriveMediaAll
(parent_type=sheet_image, parent_node=token)
then callTool set_cell_range with cells carrying
rich_text: [{type:"embed-image", attachment_token, attachment_name}].
--range must be exactly one cell.
All four use runtime.CallAPI / DoAPI directly; only +cells-set-image
combines a legacy upload with the new One-OpenAPI for the second step
(set_cell_range is in mcp-tools.json so callTool is the right path).
This closes the migration: 70 shortcuts × 17 canonical skills × matching
the sheet-skill-spec v0.5.0 tool-shortcut-map.
* test(sheets): cover all 70 shortcuts with dry-run + execute-path tests
Twelve _test.go files alongside the implementation, mirroring the legacy
package's coverage style:
- testhelpers_test.go shared rig: TestFactory + Mount + dry-run
capture + JSON-input decode + envelope helpers.
- lark_sheet_*_test.go one test file per implementation file (9
files), table-driven dry-run cases per shortcut
plus targeted validation guards.
- execute_paths_test.go end-to-end execute paths via httpmock stubs.
Covers callTool unwrap, JSON-string output
decoding, two-step lookup (+sheet-move),
batch_update fan-out, dropdown atomic writes,
and the legacy OAPI shortcuts (+workbook-create,
+dim-move) including CLI inclusive → API
half-open index conversion.
Test coverage on the sheets package is 60.5 % of statements with -race
clean, meeting the dev manual's ≥ 60 % patch-coverage gate.
* refactor(sheets): inline cli-only shortcuts into their canonical skill files
Two naming cleanups:
- lark_sheet_cli_only.go is gone. The four shortcuts it grouped
(+workbook-create / +workbook-export / +dim-move / +cells-set-image)
were bundled by their implementation pattern (legacy OAPI direct
calls) rather than by canonical skill. The whole sheets package IS
the CLI implementation, so "cli only" wasn't a meaningful grouping
at the Go layer. Each shortcut now lives next to its skill peers:
+workbook-create / +workbook-export → lark_sheet_workbook.go
+dim-move → lark_sheet_sheet_structure.go
+cells-set-image → lark_sheet_write_cells.go
Per-skill shortcut counts now match tool-shortcut-map.json exactly
(workbook: 11, sheet_structure: 9, write_cells: 5). Helpers
(buildInitialFillInput, pollExportTask, downloadExportFile,
dimMoveBody) move with their shortcuts; nothing else in the package
referenced them.
- testhelpers_test.go → helpers_test.go. The _test.go suffix already
conveys "test"; the leading "test" was redundant. Matches the
helpers.go naming convention.
Behavior unchanged. go test -race -cover stays at 60.5 %.
* refactor(sheets): sync shortcut flags with sheet-skill-spec v0.5.0
Upstream hoisted a batch of high-frequency scalar fields out of --data
into independent flags and renamed several composite-JSON flags to
match their semantic content. CLI catches up.
Renames (drop-in, same payload semantics):
- +cells-replace --replace → --replacement
- +cells-set --data → --cells
- +workbook-create --data → --values
- +batch-update --data → --operations (now a bare array;
still accepts the envelope form for
back-compat with continue_on_error)
Flat-flag hoists out of --style / --data:
- +cells-set-style / +cells-batch-set-style
--style JSON drops; replaced by 11 flat style flags
(--background-color / --font-color / --font-size / --font-style /
--font-weight / --font-line / --horizontal-alignment /
--vertical-alignment / --word-wrap / --number-format) plus
--border-styles for the one field that's still nested. Both
shortcuts share styleFlatFlags() + buildCellStyleFromFlags().
- +cells-batch-set-style also drops the [{ranges, style}] array shape
in favor of one --ranges + the same flat style flags applied to
all of them.
Object CRUD --data → --properties everywhere (chart / pivot / cond-format
/ filter / filter-view / sparkline / float-image). Per-skill scalar
hoists merged into properties via an enhanceCreate/UpdateInput callback:
- +pivot-create adds --source (required), --range
(and continues to expose --target-sheet-id /
--target-position at top level)
- +cond-format-{create,update}
adds --rule-type (enum) + --ranges (JSON array);
merged into properties.rule.type and
properties.ranges respectively
- +filter-view-{create,update}
adds --view-name and --range; both override
their properties.* counterparts
- +filter-update adds first-class --range (was buried in --data)
Float-image is fully hoisted — no --properties flag at all. Ten flat
flags (--image-name / --image-token | --image-uri / --position-row /
--position-col / --size-width / --size-height / --offset-row /
--offset-col / --z-index) compose the properties block. Implemented as
its own factory (newFloatImageWriteShortcut) since it diverges from the
shared CRUD spec.
Tests track every flag renamed and add explicit cases for the new flag
combos. go test -race -cover stays at 60.3 %.
* refactor(sheets): align batch_update + cells-set with synced reference docs
Sync to upstream reference doc updates for 9 skills:
- batch_update sub-ops: rewrite wire fields tool/params -> tool_name/input
in CellsBatchSetStyle and DropdownUpdate/Delete fan-out (the actual
server contract per Schemas section); update --operations flag desc
and tests.
- +cells-set --cells: accept bare 2D matrix [[{cell},...],...] instead
of envelope {"cells":[[...]]}; spec example shows bare-array form.
- sparkline createDataDesc enum: win_loss -> winLoss (camelCase).
All other doc changes (float-image flat flags, cond-format
--rule-type/--ranges, pivot create-only --source/--range, filter /
filter-view extra flags, chart --properties) were already aligned in
commit
|
||
|
|
fe72e41fb2 |
feat(errs): add structured CLI error contract (#984)
Introduce a typed error contract framework for lark-cli so in-process
Go callers can branch via errors.As(&errs.XxxError{}) and shell scripts,
AI agents, and protocol adapters can branch on stable JSON type/subtype
fields instead of regex-parsing free-form messages.
Adds:
- Canonical taxonomy under errs/ (9 categories + typed Error structs
embedding a shared Problem, RFC 7807-aligned)
- Centralized Lark code metadata + identity-aware BuildAPIError dispatch
- Typed JSON envelope writer alongside the legacy envelope writer
- MCP / OAuth (RFC 6750 Bearer) projection adapters
- Five CI lint guards preventing ad-hoc taxonomy drift
Backward compatibility: legacy *output.ExitError producers (ErrAPI,
ErrWithHint, Errorf, ErrBare) and business shortcuts that use them
continue to render the legacy envelope unchanged. SecurityPolicyError
wire format and exit code are preserved via a carve-out; taxonomy
migration is deferred to PR 2. Domain-specific business migration is
staged across PR 3+.
Framework-direct paths now return typed *errs.*Error: ErrAuth /
ErrValidation / ErrNetwork emit category literals on the wire
(authentication / validation / network), *core.ConfigError is promoted
at the cmd/root boundary with exit code aligned from 2 to 3, and Lark
API permission denials classified by BuildAPIError exit 3.
At the SDK boundary, WrapDoAPIError preserves any already-classified
error (legacy *output.ExitError or typed *errs.*) so output.ErrAuth
from missing credentials surfaces with the auth category and exit 3
intact instead of being downgraded to a network error. Policy responses
classified by BuildAPIError (codes 21000 / 21001) extract challenge_url
and the canonical hint from the response body, matching what the
auth transport already surfaces at the HTTP layer; non-https
challenge URLs are dropped.
First PR in the feat/error-contract-* series.
|
||
|
|
708cbc2b31 |
fix: use ErrValidation instead of fmt.Errorf in Validate paths (#1001)
Replace 8 bare fmt.Errorf calls with output.ErrValidation across 3 files so validation errors consistently return structured JSON (type: validation, exit 2) matching the rest of the codebase. Affected functions: validateExpectedFlag (sheets), validateSendTime, validateComposeInlineAndAttachments, validateEventFlags (mail), validateSignatureWithPlainText (mail) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
816927f8b8 |
fix: surface auto-grant failures via stderr and JSON hint (#1015)
When a resource is created with bot identity, the CLI attempts to auto-grant full_access to the current user. If the user open_id is missing or the grant API call fails, the result was only written to the JSON permission_grant field and easily overlooked. Changes: - Add stderr warnings when auto-grant is skipped or fails - Add 'hint' field to permission_grant JSON output with failure reason and actionable next step (e.g. auth login, check scope, retry) - Add end-to-end skipped/failed tests across all affected shortcuts (doc, drive, sheets, slides, wiki, markdown, base) Closes #963 |
||
|
|
56749e70cb | fix(sheets): use FileIO for write-image input (#996) | ||
|
|
ce485eb3f5 | fix(sheets): declare metadata scope for info shortcut (#994) | ||
|
|
4b721c0410 |
fix(sheets): explicitly document safe JSON unmarshal ignore in DryRun (#935)
Two DryRun functions in the sheets shortcuts called json.Unmarshal without checking the return value. This looks like a bug, but Validate already parses and validates the same --style / --data JSON before DryRun runs, so the error is structurally impossible at this point. Use _ = assignment + comment to silence the unchecked-error lint warning and make the safety invariant explicit to future readers. Co-authored-by: KhanCold <KhanCold@users.noreply.github.com> |