From b33a06c1e48c69d53a5a1288b4e52d030d32446d Mon Sep 17 00:00:00 2001 From: xiongyuanwen-byted Date: Fri, 15 May 2026 23:57:09 +0800 Subject: [PATCH] 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 placeholders for any field that would need that read. --- shortcuts/sheets/helpers.go | 53 ++ shortcuts/sheets/lark_sheet_workbook.go | 513 +++++++++++++++++- shortcuts/sheets/shortcuts.go | 8 + .../references/lark-sheets-workbook.md | 3 +- 4 files changed, 571 insertions(+), 6 deletions(-) diff --git a/shortcuts/sheets/helpers.go b/shortcuts/sheets/helpers.go index 2fb2e64ef..4fbcfb730 100644 --- a/shortcuts/sheets/helpers.go +++ b/shortcuts/sheets/helpers.go @@ -65,3 +65,56 @@ func publicTokenFlags() []common.Flag { {Name: "spreadsheet-token", Desc: "spreadsheet token (XOR --url)"}, } } + +// publicSheetFlags extends publicTokenFlags with the sheet selector pair. +// Use for any +sheet-* / +cells-* / +dim-* / object shortcut that operates +// on an existing single sub-sheet. +func publicSheetFlags() []common.Flag { + return append(publicTokenFlags(), + common.Flag{Name: "sheet-id", Desc: "sheet reference_id (XOR --sheet-name)"}, + common.Flag{Name: "sheet-name", Desc: "sheet title (XOR --sheet-id)"}, + ) +} + +// resolveSheetSelector validates the --sheet-id / --sheet-name XOR and +// returns whichever was supplied. Network-free. +// +// Returned tuple: (sheetID, sheetName). Exactly one is non-empty — callers +// pass both through to the tool input; the server picks whichever fits. +func resolveSheetSelector(runtime *common.RuntimeContext) (sheetID, sheetName string, err error) { + if err := common.ExactlyOne(runtime, "sheet-id", "sheet-name"); err != nil { + return "", "", err + } + if id := strings.TrimSpace(runtime.Str("sheet-id")); id != "" { + if err := validate.RejectControlChars(id, "sheet-id"); err != nil { + return "", "", common.FlagErrorf("%v", err) + } + return id, "", nil + } + name := strings.TrimSpace(runtime.Str("sheet-name")) + if err := validate.RejectControlChars(name, "sheet-name"); err != nil { + return "", "", common.FlagErrorf("%v", err) + } + return "", name, nil +} + +// sheetSelectorForToolInput packs --sheet-id / --sheet-name into the tool +// input map, omitting empty fields. Use after resolveSheetSelector returns. +func sheetSelectorForToolInput(input map[string]interface{}, sheetID, sheetName string) { + if sheetID != "" { + input["sheet_id"] = sheetID + } + if sheetName != "" { + input["sheet_name"] = sheetName + } +} + +// sheetSelectorPlaceholder returns a human-readable identifier for the +// selected sheet, suitable for DryRun output. Avoids leaking that --sheet-name +// would be resolved server-side at execute time. +func sheetSelectorPlaceholder(sheetID, sheetName string) string { + if sheetID != "" { + return sheetID + } + return "" +} diff --git a/shortcuts/sheets/lark_sheet_workbook.go b/shortcuts/sheets/lark_sheet_workbook.go index 8ba1f42a6..f7b87ac83 100644 --- a/shortcuts/sheets/lark_sheet_workbook.go +++ b/shortcuts/sheets/lark_sheet_workbook.go @@ -5,14 +5,31 @@ package sheets import ( "context" + "fmt" + "strings" + "github.com/larksuite/cli/internal/output" + "github.com/larksuite/cli/internal/util" "github.com/larksuite/cli/shortcuts/common" ) -// WorkbookInfo wraps the get_workbook_structure tool: list a workbook's -// sub-sheets with their metadata (sheet_id, title, dimensions, freeze rows -// and cols, index, hidden). This is the first step for every sheets task — -// downstream sheet-level operations all depend on the sheet_id returned here. +// ─── lark_sheet_workbook ────────────────────────────────────────────── +// +// Wraps two tools behind the One-OpenAPI: get_workbook_structure (read) and +// modify_workbook_structure (write, dispatched by `operation` enum). +// +// CLI Risk tiers diverge intentionally from the tool's single endpoint: +// - +sheet-delete is high-risk-write (irreversible) +// - everything else is plain write +// +// +sheet-create only carries --url / --spreadsheet-token (no sheet selector): +// the create tool path needs no existing-sheet anchor, so the public sheet +// selector pair is dropped here to avoid a misleading XOR requirement. + +// WorkbookInfo wraps get_workbook_structure: list a workbook's sub-sheets +// with their metadata (sheet_id, title, dimensions, freeze rows and cols, +// index, hidden). First step for every sheets task — downstream sheet-level +// operations all depend on the sheet_id returned here. var WorkbookInfo = common.Shortcut{ Service: "sheets", Command: "+workbook-info", @@ -50,3 +67,491 @@ var WorkbookInfo = common.Shortcut{ "First step for every sheets task — capture sheet_id from the result before doing any sheet-level operation.", }, } + +// SheetCreate creates a new sub-sheet. --title is the new sheet's name; +// --index inserts at a specific position (omitted → appended). Default +// dimensions match the canonical schema (rows=100, cols=26 when omitted — +// tool's defaults differ but CLI surface stays predictable). +var SheetCreate = common.Shortcut{ + Service: "sheets", + Command: "+sheet-create", + Description: "Create a new sub-sheet with an optional position and initial dimensions.", + Risk: "write", + Scopes: []string{"sheets:spreadsheet:write_only"}, + AuthTypes: []string{"user", "bot"}, + HasFormat: true, + Flags: append(publicTokenFlags(), + common.Flag{Name: "title", Desc: "new sheet title", Required: true}, + common.Flag{Name: "index", Type: "int", Default: "-1", Desc: "insertion position (0-based); omit to append"}, + common.Flag{Name: "row-count", Type: "int", Default: "0", Desc: "initial row count; omit for tool default (200)"}, + common.Flag{Name: "col-count", Type: "int", Default: "0", Desc: "initial column count; omit for tool default (20)"}, + ), + Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + if _, err := resolveSpreadsheetToken(runtime); err != nil { + return err + } + if strings.TrimSpace(runtime.Str("title")) == "" { + return common.FlagErrorf("--title is required") + } + if n := runtime.Int("row-count"); n < 0 || n > 50000 { + return common.FlagErrorf("--row-count must be between 0 and 50000") + } + if n := runtime.Int("col-count"); n < 0 || n > 200 { + return common.FlagErrorf("--col-count must be between 0 and 200") + } + return nil + }, + DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + token, _ := resolveSpreadsheetToken(runtime) + return invokeToolDryRun(token, ToolKindWrite, "modify_workbook_structure", sheetCreateInput(runtime, token)) + }, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + token, err := resolveSpreadsheetToken(runtime) + if err != nil { + return err + } + out, err := callTool(ctx, runtime, token, ToolKindWrite, "modify_workbook_structure", sheetCreateInput(runtime, token)) + if err != nil { + return err + } + runtime.Out(out, nil) + return nil + }, +} + +func sheetCreateInput(runtime *common.RuntimeContext, token string) map[string]interface{} { + input := map[string]interface{}{ + "excel_id": token, + "operation": "create", + "sheet_name": strings.TrimSpace(runtime.Str("title")), + } + if runtime.Changed("index") { + input["target_index"] = runtime.Int("index") + } + if n := runtime.Int("row-count"); n > 0 { + input["rows"] = n + } + if n := runtime.Int("col-count"); n > 0 { + input["columns"] = n + } + return input +} + +// SheetDelete deletes a sub-sheet. high-risk-write — framework rejects +// without --yes. Always preview with --dry-run first to confirm the target. +var SheetDelete = common.Shortcut{ + Service: "sheets", + Command: "+sheet-delete", + Description: "Delete a sub-sheet (irreversible).", + Risk: "high-risk-write", + Scopes: []string{"sheets:spreadsheet:write_only"}, + AuthTypes: []string{"user", "bot"}, + HasFormat: true, + Flags: publicSheetFlags(), + Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + if _, err := resolveSpreadsheetToken(runtime); err != nil { + return err + } + _, _, err := resolveSheetSelector(runtime) + return err + }, + DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + token, _ := resolveSpreadsheetToken(runtime) + sheetID, sheetName, _ := resolveSheetSelector(runtime) + input := map[string]interface{}{"excel_id": token, "operation": "delete"} + sheetSelectorForToolInput(input, sheetID, sheetName) + return invokeToolDryRun(token, ToolKindWrite, "modify_workbook_structure", input) + }, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + token, err := resolveSpreadsheetToken(runtime) + if err != nil { + return err + } + sheetID, sheetName, err := resolveSheetSelector(runtime) + if err != nil { + return err + } + input := map[string]interface{}{"excel_id": token, "operation": "delete"} + sheetSelectorForToolInput(input, sheetID, sheetName) + out, err := callTool(ctx, runtime, token, ToolKindWrite, "modify_workbook_structure", input) + if err != nil { + return err + } + runtime.Out(out, nil) + return nil + }, + Tips: []string{ + "Sheet deletion is irreversible. Always run with --dry-run first to verify the target sheet_id/sheet_name.", + }, +} + +// SheetRename renames a sub-sheet via --title (mapped to tool's new_name). +var SheetRename = common.Shortcut{ + Service: "sheets", + Command: "+sheet-rename", + Description: "Rename a sub-sheet.", + Risk: "write", + Scopes: []string{"sheets:spreadsheet:write_only"}, + AuthTypes: []string{"user", "bot"}, + HasFormat: true, + Flags: append(publicSheetFlags(), + common.Flag{Name: "title", Desc: "new sheet title", Required: true}, + ), + Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + if _, err := resolveSpreadsheetToken(runtime); err != nil { + return err + } + if _, _, err := resolveSheetSelector(runtime); err != nil { + return err + } + if strings.TrimSpace(runtime.Str("title")) == "" { + return common.FlagErrorf("--title is required") + } + return nil + }, + DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + token, _ := resolveSpreadsheetToken(runtime) + sheetID, sheetName, _ := resolveSheetSelector(runtime) + input := map[string]interface{}{ + "excel_id": token, + "operation": "rename", + "new_name": strings.TrimSpace(runtime.Str("title")), + } + sheetSelectorForToolInput(input, sheetID, sheetName) + return invokeToolDryRun(token, ToolKindWrite, "modify_workbook_structure", input) + }, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + token, err := resolveSpreadsheetToken(runtime) + if err != nil { + return err + } + sheetID, sheetName, err := resolveSheetSelector(runtime) + if err != nil { + return err + } + input := map[string]interface{}{ + "excel_id": token, + "operation": "rename", + "new_name": strings.TrimSpace(runtime.Str("title")), + } + sheetSelectorForToolInput(input, sheetID, sheetName) + out, err := callTool(ctx, runtime, token, ToolKindWrite, "modify_workbook_structure", input) + if err != nil { + return err + } + runtime.Out(out, nil) + return nil + }, +} + +// SheetMove moves a sub-sheet to a new index. The tool requires sheet_id +// and source_index in addition to target_index. The CLI accepts: +// - --sheet-id / --sheet-name to identify the sheet +// - --source-index (optional) for explicit source position +// +// When --source-index is omitted, or when --sheet-name is used instead of +// --sheet-id, Execute issues a single get_workbook_structure read to derive +// the missing pieces. DryRun stays network-free: it uses placeholders +// for any field that would need that read. +var SheetMove = common.Shortcut{ + Service: "sheets", + Command: "+sheet-move", + Description: "Move a sub-sheet to a new position.", + Risk: "write", + Scopes: []string{"sheets:spreadsheet:read", "sheets:spreadsheet:write_only"}, + AuthTypes: []string{"user", "bot"}, + HasFormat: true, + Flags: append(publicSheetFlags(), + common.Flag{Name: "index", Type: "int", Required: true, Desc: "target position (0-based)"}, + common.Flag{Name: "source-index", Type: "int", Default: "-1", Desc: "source position (0-based); omitted → auto-derived from --sheet-id/--sheet-name's current workbook position"}, + ), + Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + if _, err := resolveSpreadsheetToken(runtime); err != nil { + return err + } + if _, _, err := resolveSheetSelector(runtime); err != nil { + return err + } + if !runtime.Changed("index") { + return common.FlagErrorf("--index is required") + } + if runtime.Int("index") < 0 { + return common.FlagErrorf("--index must be >= 0") + } + if runtime.Changed("source-index") && runtime.Int("source-index") < 0 { + return common.FlagErrorf("--source-index must be >= 0") + } + return nil + }, + DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + token, _ := resolveSpreadsheetToken(runtime) + sheetID, sheetName, _ := resolveSheetSelector(runtime) + input := map[string]interface{}{ + "excel_id": token, + "operation": "move", + "sheet_id": sheetSelectorPlaceholder(sheetID, sheetName), + "target_index": runtime.Int("index"), + "source_index": sourceIndexOrPlaceholder(runtime), + } + return invokeToolDryRun(token, ToolKindWrite, "modify_workbook_structure", input) + }, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + token, err := resolveSpreadsheetToken(runtime) + if err != nil { + return err + } + sheetID, sheetName, err := resolveSheetSelector(runtime) + if err != nil { + return err + } + + resolvedID := sheetID + var sourceIndex int + needIDLookup := sheetID == "" + needIndexLookup := !runtime.Changed("source-index") + if needIDLookup || needIndexLookup { + lookedID, lookedIdx, err := lookupSheetIndex(ctx, runtime, token, sheetID, sheetName) + if err != nil { + return err + } + resolvedID = lookedID + sourceIndex = lookedIdx + } + if runtime.Changed("source-index") { + sourceIndex = runtime.Int("source-index") + } + + input := map[string]interface{}{ + "excel_id": token, + "operation": "move", + "sheet_id": resolvedID, + "source_index": sourceIndex, + "target_index": runtime.Int("index"), + } + out, err := callTool(ctx, runtime, token, ToolKindWrite, "modify_workbook_structure", input) + if err != nil { + return err + } + runtime.Out(out, nil) + return nil + }, + Tips: []string{ + "Pass --source-index when you already know it to avoid the extra read; otherwise CLI derives it from --sheet-id/--sheet-name.", + }, +} + +// sourceIndexOrPlaceholder returns the user-supplied source-index, or the +// string "" when DryRun should signal that Execute will derive it. +func sourceIndexOrPlaceholder(runtime *common.RuntimeContext) interface{} { + if runtime.Changed("source-index") { + return runtime.Int("source-index") + } + return "" +} + +// SheetCopy duplicates a sub-sheet. --title (optional) names the copy; +// --index (optional) places it. +var SheetCopy = common.Shortcut{ + Service: "sheets", + Command: "+sheet-copy", + Description: "Duplicate a sub-sheet, optionally renaming and repositioning the copy.", + Risk: "write", + Scopes: []string{"sheets:spreadsheet:write_only"}, + AuthTypes: []string{"user", "bot"}, + HasFormat: true, + Flags: append(publicSheetFlags(), + common.Flag{Name: "title", Desc: "title for the duplicated sheet (server-generated when omitted)"}, + common.Flag{Name: "index", Type: "int", Default: "-1", Desc: "insertion position for the copy (0-based); omit to append"}, + ), + Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + if _, err := resolveSpreadsheetToken(runtime); err != nil { + return err + } + _, _, err := resolveSheetSelector(runtime) + return err + }, + DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + token, _ := resolveSpreadsheetToken(runtime) + sheetID, sheetName, _ := resolveSheetSelector(runtime) + return invokeToolDryRun(token, ToolKindWrite, "modify_workbook_structure", sheetCopyInput(runtime, token, sheetID, sheetName)) + }, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + token, err := resolveSpreadsheetToken(runtime) + if err != nil { + return err + } + sheetID, sheetName, err := resolveSheetSelector(runtime) + if err != nil { + return err + } + out, err := callTool(ctx, runtime, token, ToolKindWrite, "modify_workbook_structure", sheetCopyInput(runtime, token, sheetID, sheetName)) + if err != nil { + return err + } + runtime.Out(out, nil) + return nil + }, +} + +func sheetCopyInput(runtime *common.RuntimeContext, token, sheetID, sheetName string) map[string]interface{} { + input := map[string]interface{}{"excel_id": token, "operation": "duplicate"} + sheetSelectorForToolInput(input, sheetID, sheetName) + if t := strings.TrimSpace(runtime.Str("title")); t != "" { + input["new_name"] = t + } + if runtime.Changed("index") { + input["target_index"] = runtime.Int("index") + } + return input +} + +// SheetHide / SheetUnhide toggle visibility. Visible bool semantics live in +// the operation enum so callers don't need a --visible flag. +var SheetHide = newSheetVisibilityShortcut( + "+sheet-hide", "Hide a sub-sheet from the tabs bar.", "hide", +) + +var SheetUnhide = newSheetVisibilityShortcut( + "+sheet-unhide", "Restore a hidden sub-sheet.", "unhide", +) + +func newSheetVisibilityShortcut(command, desc, op string) common.Shortcut { + return common.Shortcut{ + Service: "sheets", + Command: command, + Description: desc, + Risk: "write", + Scopes: []string{"sheets:spreadsheet:write_only"}, + AuthTypes: []string{"user", "bot"}, + HasFormat: true, + Flags: publicSheetFlags(), + Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + if _, err := resolveSpreadsheetToken(runtime); err != nil { + return err + } + _, _, err := resolveSheetSelector(runtime) + return err + }, + DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + token, _ := resolveSpreadsheetToken(runtime) + sheetID, sheetName, _ := resolveSheetSelector(runtime) + input := map[string]interface{}{"excel_id": token, "operation": op} + sheetSelectorForToolInput(input, sheetID, sheetName) + return invokeToolDryRun(token, ToolKindWrite, "modify_workbook_structure", input) + }, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + token, err := resolveSpreadsheetToken(runtime) + if err != nil { + return err + } + sheetID, sheetName, err := resolveSheetSelector(runtime) + if err != nil { + return err + } + input := map[string]interface{}{"excel_id": token, "operation": op} + sheetSelectorForToolInput(input, sheetID, sheetName) + out, err := callTool(ctx, runtime, token, ToolKindWrite, "modify_workbook_structure", input) + if err != nil { + return err + } + runtime.Out(out, nil) + return nil + }, + } +} + +// SheetSetTabColor sets the tab color of a sub-sheet. --color "" clears. +var SheetSetTabColor = common.Shortcut{ + Service: "sheets", + Command: "+sheet-set-tab-color", + Description: "Set or clear the tab color of a sub-sheet.", + Risk: "write", + Scopes: []string{"sheets:spreadsheet:write_only"}, + AuthTypes: []string{"user", "bot"}, + HasFormat: true, + Flags: append(publicSheetFlags(), + common.Flag{Name: "color", Desc: "hex color like #FF0000; pass empty string to clear"}, + ), + Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + if _, err := resolveSpreadsheetToken(runtime); err != nil { + return err + } + if _, _, err := resolveSheetSelector(runtime); err != nil { + return err + } + if !runtime.Changed("color") { + return common.FlagErrorf("--color is required (empty string clears)") + } + return nil + }, + DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + token, _ := resolveSpreadsheetToken(runtime) + sheetID, sheetName, _ := resolveSheetSelector(runtime) + input := map[string]interface{}{ + "excel_id": token, + "operation": "set_tab_color", + "tab_color": runtime.Str("color"), + } + sheetSelectorForToolInput(input, sheetID, sheetName) + return invokeToolDryRun(token, ToolKindWrite, "modify_workbook_structure", input) + }, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + token, err := resolveSpreadsheetToken(runtime) + if err != nil { + return err + } + sheetID, sheetName, err := resolveSheetSelector(runtime) + if err != nil { + return err + } + input := map[string]interface{}{ + "excel_id": token, + "operation": "set_tab_color", + "tab_color": runtime.Str("color"), + } + sheetSelectorForToolInput(input, sheetID, sheetName) + out, err := callTool(ctx, runtime, token, ToolKindWrite, "modify_workbook_structure", input) + if err != nil { + return err + } + runtime.Out(out, nil) + return nil + }, +} + +// lookupSheetIndex finds a sub-sheet by id or name and returns its canonical +// id + current 0-based index. Caller is responsible for ensuring at least one +// of sheetID/sheetName is non-empty. +func lookupSheetIndex(ctx context.Context, runtime *common.RuntimeContext, token, sheetID, sheetName string) (resolvedID string, index int, err error) { + out, err := callTool(ctx, runtime, token, ToolKindRead, "get_workbook_structure", map[string]interface{}{ + "excel_id": token, + }) + if err != nil { + return "", 0, err + } + m, ok := out.(map[string]interface{}) + if !ok { + return "", 0, output.Errorf(output.ExitAPI, "tool_output", "get_workbook_structure returned non-object output") + } + sheets, _ := m["sheets"].([]interface{}) + for _, raw := range sheets { + sm, ok := raw.(map[string]interface{}) + if !ok { + continue + } + id, _ := sm["sheet_id"].(string) + name, _ := sm["sheet_name"].(string) + if (sheetID != "" && id == sheetID) || (sheetName != "" && name == sheetName) { + idx, ok := util.ToFloat64(sm["index"]) + if !ok { + return "", 0, output.Errorf(output.ExitAPI, "tool_output", "sheet entry missing index field") + } + return id, int(idx), nil + } + } + target := sheetID + if target == "" { + target = sheetName + } + return "", 0, output.Errorf(output.ExitAPI, "not_found", fmt.Sprintf("sheet %q not found in workbook", target)) +} diff --git a/shortcuts/sheets/shortcuts.go b/shortcuts/sheets/shortcuts.go index 47e6b5106..f1a911783 100644 --- a/shortcuts/sheets/shortcuts.go +++ b/shortcuts/sheets/shortcuts.go @@ -12,5 +12,13 @@ func Shortcuts() []common.Shortcut { return []common.Shortcut{ // lark_sheet_workbook WorkbookInfo, + SheetCreate, + SheetDelete, + SheetRename, + SheetMove, + SheetCopy, + SheetHide, + SheetUnhide, + SheetSetTabColor, } } diff --git a/skills/lark-sheets/references/lark-sheets-workbook.md b/skills/lark-sheets/references/lark-sheets-workbook.md index d7d2a7eb6..78148673b 100644 --- a/skills/lark-sheets/references/lark-sheets-workbook.md +++ b/skills/lark-sheets/references/lark-sheets-workbook.md @@ -65,8 +65,6 @@ | --- | --- | --- | --- | --- | | `--url` | 公共 | string | XOR | spreadsheet URL(与 `--spreadsheet-token` 二选一) | | `--spreadsheet-token` | 公共 | string | XOR | spreadsheet token(与 `--url` 二选一) | -| `--sheet-id` | 公共 | string | XOR | 工作表 reference_id(与 `--sheet-name` 二选一) | -| `--sheet-name` | 公共 | string | XOR | 工作表名称(与 `--sheet-id` 二选一) | | `--title` | 专有 | string | 是 | 新工作表名称 | | `--index` | 专有 | int | 否 | 插入位置;省略时附加到末尾 | | `--row-count` | 专有 | int | 否 | 初始行数,默认 100 | @@ -104,6 +102,7 @@ | `--sheet-id` | 公共 | string | XOR | 工作表 reference_id(与 `--sheet-name` 二选一) | | `--sheet-name` | 公共 | string | XOR | 工作表名称(与 `--sheet-id` 二选一) | | `--index` | 专有 | int | 是 | 目标位置(0-based) | +| `--source-index` | 专有 | int | 否 | 源位置(0-based);可选,未传时由 CLI runtime 根据 --sheet-id / --sheet-name 当前在工作簿中的 index 自动派生 | | `--dry-run` | 系统 | bool | 否 | | ### `+sheet-copy`