Files
dingtalk-workspace-cli/internal/helpers/sheet_workbook.go
huwenjiao.hwjandClaude Opus 5 332b74e8ce refactor(sheet): split create-with-data and export-csv onto their own leaves
Both capabilities were added as flags on an existing leaf, and in both cases
the leaf's published interface stopped describing what the command did:

- `sheet create --values/--sheets/--styles` orchestrates create → probe →
  resolve default worksheet → write → read back → optional styles, yet the
  leaf still published `interface_mode: mcp` + `create_workspace_sheet`.
- `sheet export --export-format csv` reads `get_range_as_csv` and never
  invokes `submit_export_job`, yet the leaf published `submit_export_job`.

Each moves to its own command, declaring the interface it actually uses:
`sheet create-with-data` is `composite` with a reviewed reason and no
`interface_ref`; `sheet export-csv` is `mcp` + `get_range_as_csv`. Both are
pinned in the interface-disposition contract test.

`sheet create` and `sheet export` are restored byte-for-byte to main, so the
compatibility gates see two `command_added` additions instead of four
locked-field changes. The split also removes a user-visible trap: `--range`
without `--export-format csv` used to be silently discarded and the whole
workbook exported; the cross-format flags no longer exist, pinned by
TestSheetExportAndExportCsvFlagsDoNotLeak.

Drops the 8 now-stale mapping-ledger exclusions that covered the flags on
`sheet.create_workspace_sheet` / `sheet.submit_export_job`, shrinking this
branch's exclusion surface. Skill references and CHANGELOG follow the split.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 17:47:50 +08:00

598 lines
25 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package helpers
import (
"fmt"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
"github.com/spf13/cobra"
)
func newWorkbookCmds() []*cobra.Command {
createCmd := &cobra.Command{
Use: "create",
Short: "创建钉钉表格文档",
Long: `创建一篇新的钉钉在线电子表格,支持创建空表格。
创建位置优先级: --folder > --workspace > 默认 (我的文档根目录)`,
Example: ` dws sheet create --name "销售数据"
dws sheet create --name "Q1 数据" --folder FOLDER_ID
dws sheet create --name "知识库表格" --workspace WS_ID`,
RunE: func(cmd *cobra.Command, args []string) error {
toolArgs := map[string]any{
"name": mustGetFlag(cmd, "name"),
}
if v, _ := cmd.Flags().GetString("folder"); v != "" {
toolArgs["folderId"] = v
}
if v := flagOrFallback(cmd, "workspace", "workspace-id"); v != "" {
toolArgs["workspaceId"] = v
}
return callMCPTool("create_workspace_sheet", toolArgs)
},
}
DeclareLeafMetadata(createCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "create_workspace_sheet",
CanonicalPath: "sheet.create_workspace_sheet",
CLIPath: "sheet create",
PrimaryCLIPath: "sheet create",
},
Description: "创建钉钉在线电子表格文档(axls),返回 nodeId。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "create_workspace_sheet"},
},
Selection: contract.SelectionSpec{
AgentSummary: "创建钉钉在线电子表格文档(axls),返回 nodeId。",
UseWhen: []string{"需要新建一份钉钉在线电子表格文档(不是 AI 表格 Base,也不是本地 xlsx)时"},
AvoidWhen: []string{"要创建 AI 多维表 Base 用 aitable base create;只要在已有表格里加工作表用 sheet new;本地 xlsx 用 doc download"},
Examples: []string{
"dws sheet create --name \"销售数据\"",
"dws sheet create --name \"Q1 数据\" --folder <FOLDER_ID>",
},
},
// name is validated via mustGetFlag; publish required via ParamDecl
Parameters: []contract.ParamDecl{
{Name: "name", Required: boolPtr(true)},
{Name: "folder", Property: "folderId"},
{Name: "workspace", Property: "workspaceId"},
},
},
})
createCmd.Flags().String("name", "", "表格名称 (必填)")
createCmd.Flags().String("folder", "", "目标文件夹 ID 或 URL")
createCmd.Flags().String("workspace", "", "目标知识库 ID")
listCmd := &cobra.Command{
Use: "list",
Short: "获取全部工作表列表",
Long: `获取钉钉电子表格中所有工作表的 ID 和名称。
返回的 sheetId 可用于 info / range read (别名 get) / range update 等后续操作。
nodeId 支持传入文档链接 URL 或文档 ID(dentryUuid),系统自动识别。
文档链接格式:https://alidocs.dingtalk.com/i/nodes/{dentryUuid}`,
Example: ` dws sheet list --node NODE_ID
dws sheet list --node "https://alidocs.dingtalk.com/i/nodes/<DOC_UUID>"`,
RunE: func(cmd *cobra.Command, args []string) error {
return callMCPTool("get_all_sheets", map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
})
},
}
DeclareLeafMetadata(listCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "read", Risk: "low",
Confirmation: "not_required", Idempotency: "idempotent",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "get_all_sheets",
CanonicalPath: "sheet.get_all_sheets",
CLIPath: "sheet list",
PrimaryCLIPath: "sheet list",
},
Description: "列出电子表格文档内全部工作表的 ID 与名称。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "get_all_sheets"},
},
Selection: contract.SelectionSpec{
AgentSummary: "列出电子表格文档内全部工作表的 ID 与名称。",
UseWhen: []string{"需要拿到 sheetId/工作表名称,作为后续读写的前置步骤时"},
AvoidWhen: []string{"要看单个工作表行列边界与合并区用 sheet info;读单元格内容用 sheet range read"},
Examples: []string{"dws sheet list --node <NODE_ID>"},
},
// node is validated via mustGetFlag; publish required via ParamDecl
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId", Required: boolPtr(true)},
},
},
})
listCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
infoCmd := &cobra.Command{
Use: "info",
Short: "获取指定工作表详情",
Long: `获取指定工作表的详细信息,包括 ID、名称、可见性、行列数、最后非空行列位置、合并单元格范围(mergedRanges)等。
非空数据边界使用 A1/UI 语义字段:
nonEmptyRange.range 从 A1 到最后非空单元格的 A1 范围(空表为 null)
nonEmptyRange.lastCell 最后非空单元格地址(如 J5,空表为 null)
nonEmptyRange.lastRow 1-based 行号(空表为 null)
nonEmptyRange.lastColumn 列字母(空表为 null)
mergedRanges 已经是 A1 表示法(如 "A1:B2"),可原样用于 range/merge 相关命令。
nodeId 支持传入文档链接 URL 或文档 ID(dentryUuid);
sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获取。
不传 --sheet-id 时默认返回第一个工作表。`,
Example: ` dws sheet info --node NODE_ID
dws sheet info --node NODE_ID --sheet-id SHEET_ID
dws sheet info --node NODE_ID --sheet-id "Sheet1"
dws sheet info --node NODE_ID --sheet-id SHEET_ID --include row_heights,col_widths`,
RunE: func(cmd *cobra.Command, args []string) error {
toolArgs := map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
}
if v, _ := cmd.Flags().GetString("sheet-id"); v != "" {
toolArgs["sheetId"] = v
}
if v, _ := cmd.Flags().GetStringSlice("include"); len(v) > 0 {
toolArgs["include"] = v
}
return callMCPToolSheetInfo(toolArgs)
},
}
DeclareLeafMetadata(infoCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "read", Risk: "low",
Confirmation: "not_required", Idempotency: "idempotent",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "info",
CanonicalPath: "sheet.info",
CLIPath: "sheet info",
PrimaryCLIPath: "sheet info",
},
Description: "获取指定工作表详情(行列数、非空范围、合并区等)。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "get_sheet"},
},
Selection: contract.SelectionSpec{
AgentSummary: "获取指定工作表详情(行列数、非空范围、合并区等)。",
UseWhen: []string{"需要确认工作表尺寸、nonEmptyRange 或合并结构,以便规划读取范围时"},
AvoidWhen: []string{"只要工作表列表用 sheet list;读单元格值用 sheet range read"},
Examples: []string{"dws sheet info --node <NODE_ID> --sheet-id <SHEET_ID>"},
},
Parameters: []contract.ParamDecl{
{Name: "include", Property: "include"},
{Name: "node", Property: "nodeId"},
},
},
})
infoCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
infoCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (不传则返回第一个工作表)")
infoCmd.Flags().StringSlice("include", nil, "可选扩展信息,逗号分隔;支持 groups / row_heights / col_widths / hidden_rows / hidden_cols / frozen")
newCmd := &cobra.Command{
Use: "new",
Short: "新建工作表",
Long: `在指定钉钉表格中新建一个工作表。
当指定名称与已有工作表重复时,系统会自动重命名为合法值。`,
Example: ` dws sheet new --node NODE_ID --name "Sheet2"
dws sheet new --node NODE_ID --name "数据汇总"`,
RunE: func(cmd *cobra.Command, args []string) error {
return callMCPTool("create_sheet", map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"name": mustGetFlag(cmd, "name"),
})
},
}
DeclareLeafMetadata(newCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "create_sheet",
CanonicalPath: "sheet.create_sheet",
CLIPath: "sheet new",
PrimaryCLIPath: "sheet new",
},
Description: "在已有电子表格文档中新建工作表(Sheet 页签)。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "create_sheet"},
},
Selection: contract.SelectionSpec{
AgentSummary: "在已有电子表格文档中新建工作表(Sheet 页签)。",
UseWhen: []string{"已有 axls 文档,需要新增一个工作表页签时"},
AvoidWhen: []string{"要新建整份表格文档用 sheet create;复制已有工作表用 sheet copy;删除工作表用 sheet delete-sheet"},
Examples: []string{"dws sheet new --node <NODE_ID> --name \"Sheet2\""},
},
// node/name validated via mustGetFlag; publish required via ParamDecl
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId", Required: boolPtr(true)},
{Name: "name", Required: boolPtr(true)},
},
},
})
newCmd.Flags().String("node", "", "表格文档 ID (必填)")
newCmd.Flags().String("name", "", "工作表名称 (必填)")
updateSheetCmd := &cobra.Command{
Use: "update",
Short: "更新工作表属性",
Long: `更新工作表名称、位置、隐藏状态、冻结行列、标签颜色。
--name / --index / --hidden / --frozen-row-count / --frozen-column-count / --tab-color 至少提供一个;
多个属性可同时传入,将在同一次请求中更新。
注意:
- name 不能包含 / \ ? * [ ] : 等特殊字符,最长 100 字符
- 至少需要保留一个可见的工作表,不能将所有工作表都隐藏
- 冻结行数/列数不能超过工作表的总行数/总列数,设为 0 表示取消冻结
- tab-color 为 Hex 格式如 #FF0000,传空字符串清除颜色`,
Example: ` # 改名 + 调整冻结
dws sheet update --node NODE_ID --sheet-id SHEET_ID --name "汇总表" --frozen-row-count 2 --frozen-column-count 1
# 隐藏工作表
dws sheet update --node NODE_ID --sheet-id SHEET_ID --hidden=true
# 显示工作表
dws sheet update --node NODE_ID --sheet-id SHEET_ID --hidden=false
# 移动工作表到第一个位置
dws sheet update --node NODE_ID --sheet-id SHEET_ID --index 0
# 取消冻结
dws sheet update --node NODE_ID --sheet-id SHEET_ID --frozen-row-count 0 --frozen-column-count 0
# 设置工作表标签颜色
dws sheet update --node NODE_ID --sheet-id SHEET_ID --tab-color "#FF0000"
# 清除标签颜色
dws sheet update --node NODE_ID --sheet-id SHEET_ID --tab-color ""`,
RunE: func(cmd *cobra.Command, args []string) error {
nameChanged := cmd.Flags().Changed("name") || cmd.Flags().Changed("title")
indexChanged := cmd.Flags().Changed("index")
hiddenChanged := cmd.Flags().Changed("hidden")
frozenRowChanged := cmd.Flags().Changed("frozen-row-count")
frozenColChanged := cmd.Flags().Changed("frozen-column-count")
tabColorChanged := cmd.Flags().Changed("tab-color")
if !nameChanged && !indexChanged && !hiddenChanged && !frozenRowChanged && !frozenColChanged && !tabColorChanged {
return fmt.Errorf("--name、--index、--hidden、--frozen-row-count、--frozen-column-count、--tab-color 至少必须提供一个")
}
toolArgs := map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
}
if nameChanged {
toolArgs["title"] = resolveSheetName(cmd)
}
if indexChanged {
index, _ := cmd.Flags().GetInt("index")
if index < 0 {
return fmt.Errorf("--index 不能为负数,当前值: %d", index)
}
toolArgs["index"] = index
}
if hiddenChanged {
hidden, _ := cmd.Flags().GetBool("hidden")
toolArgs["hidden"] = hidden
}
if frozenRowChanged {
frozenRowCount, _ := cmd.Flags().GetInt("frozen-row-count")
if frozenRowCount < 0 {
return fmt.Errorf("--frozen-row-count 不能为负数,当前值: %d", frozenRowCount)
}
toolArgs["frozenRowCount"] = frozenRowCount
}
if frozenColChanged {
frozenColumnCount, _ := cmd.Flags().GetInt("frozen-column-count")
if frozenColumnCount < 0 {
return fmt.Errorf("--frozen-column-count 不能为负数,当前值: %d", frozenColumnCount)
}
toolArgs["frozenColumnCount"] = frozenColumnCount
}
if tabColorChanged {
tabColor, _ := cmd.Flags().GetString("tab-color")
toolArgs["tabColor"] = tabColor
}
return callMCPTool("update_sheet", toolArgs)
},
}
DeclareLeafMetadata(updateSheetCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "update_sheet",
CanonicalPath: "sheet.update_sheet",
CLIPath: "sheet update",
PrimaryCLIPath: "sheet update",
},
Description: "更新工作表属性:名称、位置、隐藏、标签色、冻结行列。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "update_sheet"},
},
Selection: contract.SelectionSpec{
AgentSummary: "更新工作表属性:名称、位置、隐藏、标签色、冻结行列。",
UseWhen: []string{"要重命名工作表、调整页签顺序、隐藏/显示、冻结行列或改标签颜色时"},
AvoidWhen: []string{"改单元格内容用 range update;删整张工作表用 delete-sheet;复制工作表用 copy"},
Examples: []string{"dws sheet update --node <NODE_ID> --sheet-id <SHEET_ID> --title \"汇总表\" --frozen-row-count 2"},
},
Parameters: []contract.ParamDecl{
{Name: "name", Property: "title"},
{Name: "node", Property: "nodeId"},
},
},
})
updateSheetCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
updateSheetCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
updateSheetCmd.Flags().String("name", "", "工作表新名称,最长 100 字符")
updateSheetCmd.Flags().String("title", "", "--name 的别名(兼容)")
updateSheetCmd.Flags().Int("index", 0, "工作表新位置索引,0-based")
updateSheetCmd.Flags().Bool("hidden", false, "是否隐藏工作表 (true=隐藏, false=显示)")
updateSheetCmd.Flags().Int("frozen-row-count", 0, "冻结行数,0 表示取消冻结")
updateSheetCmd.Flags().Int("frozen-column-count", 0, "冻结列数,0 表示取消冻结")
updateSheetCmd.Flags().String("tab-color", "", "工作表标签颜色,Hex 格式如 #FF0000;传空字符串清除颜色")
copySheetCmd := &cobra.Command{
Use: "copy",
Short: "复制工作表",
Long: `复制指定工作表,在同一表格中创建一个副本。
复制操作会将源工作表的所有内容(包括数据、格式、公式等)完整复制到新工作表中。
可选参数:
--name 指定副本名称;不传时系统自动生成(通常为"源名称 副本")。
名称与已有工作表重复时系统会自动重命名。
--index 指定副本位置(0-based);不传时放在源工作表之后。
传 --index 时,CLI 会先复制,再追加一次位置更新,把副本移动到目标索引。
name 不能包含 / \ ? * [ ] : 等特殊字符,最长 100 字符。`,
Example: ` # 按默认位置复制
dws sheet copy --node NODE_ID --sheet-id SHEET_ID
# 指定副本名称和位置
dws sheet copy --node NODE_ID --sheet-id SHEET_ID --name "销售副本" --index 2
# 只指定名称
dws sheet copy --node NODE_ID --sheet-id SHEET_ID --name "备份"`,
RunE: func(cmd *cobra.Command, args []string) error {
toolArgs := map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
}
if cmd.Flags().Changed("name") || cmd.Flags().Changed("title") {
toolArgs["title"] = resolveSheetName(cmd)
}
if cmd.Flags().Changed("index") {
index, _ := cmd.Flags().GetInt("index")
if index < 0 {
return fmt.Errorf("--index 不能为负数,当前值: %d", index)
}
toolArgs["index"] = index
}
return callMCPTool("copy_sheet", toolArgs)
},
}
DeclareLeafMetadata(copySheetCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "copy_sheet",
CanonicalPath: "sheet.copy_sheet",
CLIPath: "sheet copy",
PrimaryCLIPath: "sheet copy",
},
Description: "在同一文档内复制工作表(含数据与格式)。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "copy_sheet"},
},
Selection: contract.SelectionSpec{
AgentSummary: "在同一文档内复制工作表(含数据与格式)。",
UseWhen: []string{"需要基于现有工作表创建完整副本时可选用,可指定副本名与位置"},
AvoidWhen: []string{"只要新建空白工作表用 sheet new;跨文档复制请走文档/知识库复制能力,不要用本命令"},
Examples: []string{"dws sheet copy --node <NODE_ID> --sheet-id <SHEET_ID> --title \"销售副本\""},
},
Parameters: []contract.ParamDecl{
{Name: "name", Property: "title"},
{Name: "node", Property: "nodeId"},
},
},
})
copySheetCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
copySheetCmd.Flags().String("sheet-id", "", "源工作表 ID 或名称 (必填)")
copySheetCmd.Flags().String("name", "", "副本名称,最长 100 字符 (不传则系统自动生成)")
copySheetCmd.Flags().String("title", "", "--name 的别名(兼容)")
copySheetCmd.Flags().Int("index", 0, "副本位置索引,0-based (不传则放在源工作表之后)")
deleteSheetCmd := &cobra.Command{
Use: "delete-sheet",
Short: "删除工作表",
Long: `删除指定的工作表。此操作不可逆,删除后工作表及其所有数据将无法恢复。
约束:
- 不能删除隐藏的工作表(需先取消隐藏再删除)
- 不能删除最后一个可见工作表(至少保留一个可见工作表)`,
Example: ` dws sheet delete-sheet --node NODE_ID --sheet-id SHEET_ID --yes`,
RunE: func(cmd *cobra.Command, args []string) error {
return callMCPTool("delete_sheet", map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
})
},
}
DeclareLeafMetadata(deleteSheetCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "user_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "delete_sheet",
CanonicalPath: "sheet.delete_sheet",
CLIPath: "sheet delete-sheet",
PrimaryCLIPath: "sheet delete-sheet",
},
Description: "删除指定工作表(不可逆,需确认后加 --yes)。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "delete_sheet"},
},
Selection: contract.SelectionSpec{
AgentSummary: "删除指定工作表(不可逆,需确认后加 --yes)。",
UseWhen: []string{"用户明确要求永久删除某个工作表页签,且已确认目标 sheetId 时"},
AvoidWhen: []string{"只想清空单元格内容用 range clear;删行列用 delete-dimension;隐藏工作表用 update --hidden"},
Examples: []string{"dws sheet delete-sheet --node <NODE_ID> --sheet-id <SHEET_ID>"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
},
},
})
deleteSheetCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
deleteSheetCmd.Flags().String("sheet-id", "", "要删除的工作表 ID 或名称 (必填)")
showGridlineCmd := &cobra.Command{
Use: "show-gridline",
Short: "显示工作表网格线",
Example: ` dws sheet show-gridline --node NODE_ID --sheet-id SHEET_ID`,
RunE: func(cmd *cobra.Command, _ []string) error {
nodeID, err := mustFlagOrFallback(cmd, "node", "file-id", "node-id", "doc-id")
if err != nil {
return err
}
if err := validateRequiredFlags(cmd, "sheet-id"); err != nil {
return err
}
return callMCPTool("set_gridline_visibility", map[string]any{
"nodeId": nodeID,
"sheetId": mustGetFlag(cmd, "sheet-id"),
"visibility": "visible",
})
},
}
DeclareLeafMetadata(showGridlineCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "idempotent",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "show_gridline",
CanonicalPath: "sheet.show_gridline",
CLIPath: "sheet show-gridline",
PrimaryCLIPath: "sheet show-gridline",
},
Description: "显示工作表网格线。",
Interface: &contract.InterfaceSpec{
Mode: "composite",
Availability: "available",
Reason: "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
},
Selection: contract.SelectionSpec{
AgentSummary: "显示工作表网格线。",
UseWhen: []string{"需要打开指定工作表的网格线显示时"},
AvoidWhen: []string{"要隐藏网格线用 sheet hide-gridline;改冻结或隐藏工作表用 sheet update"},
Examples: []string{"dws sheet show-gridline --node <NODE_ID> --sheet-id <SHEET_ID>"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
},
},
})
showGridlineCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
showGridlineCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
hideGridlineCmd := &cobra.Command{
Use: "hide-gridline",
Short: "隐藏工作表网格线",
Example: ` dws sheet hide-gridline --node NODE_ID --sheet-id SHEET_ID`,
RunE: func(cmd *cobra.Command, _ []string) error {
nodeID, err := mustFlagOrFallback(cmd, "node", "file-id", "node-id", "doc-id")
if err != nil {
return err
}
if err := validateRequiredFlags(cmd, "sheet-id"); err != nil {
return err
}
return callMCPTool("set_gridline_visibility", map[string]any{
"nodeId": nodeID,
"sheetId": mustGetFlag(cmd, "sheet-id"),
"visibility": "hidden",
})
},
}
DeclareLeafMetadata(hideGridlineCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "idempotent",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "hide_gridline",
CanonicalPath: "sheet.hide_gridline",
CLIPath: "sheet hide-gridline",
PrimaryCLIPath: "sheet hide-gridline",
},
Description: "隐藏工作表网格线。",
Interface: &contract.InterfaceSpec{
Mode: "composite",
Availability: "available",
Reason: "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
},
Selection: contract.SelectionSpec{
AgentSummary: "隐藏工作表网格线。",
UseWhen: []string{"需要关闭指定工作表的网格线显示时"},
AvoidWhen: []string{"要显示网格线用 sheet show-gridline"},
Examples: []string{"dws sheet hide-gridline --node <NODE_ID> --sheet-id <SHEET_ID>"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
},
},
})
hideGridlineCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
hideGridlineCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
return []*cobra.Command{createCmd, listCmd, infoCmd, newCmd, updateSheetCmd, copySheetCmd, deleteSheetCmd, showGridlineCmd, hideGridlineCmd}
}