@superdoc-dev/sdk 1.3.0-next.43 → 1.3.0-next.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@
5
5
  "tools": [
6
6
  {
7
7
  "toolName": "superdoc_get_content",
8
- "description": "Read document content. Use action \"info\" for structure and styles, \"blocks\" for all block IDs and types, \"text\" or \"markdown\" for content. Call info or blocks before editing.",
8
+ "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \"blocks\" returns structured block data with nodeId, nodeType, textPreview, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Action \"text\" and \"markdown\" return the full document as plain text or Markdown. Action \"html\" returns HTML. Action \"info\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \"blocks\" action supports pagination via \"offset\" and \"limit\", and filtering via \"nodeTypes\". Other actions ignore these parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.",
9
9
  "inputSchema": {
10
10
  "type": "object",
11
11
  "properties": {
@@ -81,7 +81,7 @@
81
81
  },
82
82
  {
83
83
  "toolName": "superdoc_edit",
84
- "description": "Insert, replace, delete text, or undo/redo",
84
+ "description": "Refs expire after any mutation; always re-search before the next edit. Modify document text: insert new content, replace existing text, delete a range, or undo/redo. Use this for single text modifications. For 2+ edits that must succeed or fail atomically, use superdoc_mutations instead. For replace and delete, pass a \"ref\" from superdoc_search or superdoc_get_content blocks. A search ref covers only the matched substring; a block ref covers the entire block text, so use block refs when rewriting or shortening whole paragraphs. Insert supports plain text (default), markdown, or html via the \"type\" parameter. Use \"placement\" (before, after, insideStart, insideEnd) to control position relative to the target. Supports \"dryRun\" to preview changes and \"changeMode: tracked\" to record edits as tracked changes. Do NOT build \"target\" objects manually when a ref is available; prefer \"ref\" for simpler, more reliable targeting.",
85
85
  "inputSchema": {
86
86
  "type": "object",
87
87
  "properties": {
@@ -303,7 +303,7 @@
303
303
  ]
304
304
  }
305
305
  ],
306
- "description": "Target address object. Use 'ref' instead if you have a search handle. Format: {kind:'text', blockId, range:{start,end}} or {kind:'block', nodeType, nodeId}."
306
+ "description": "Target address. For inline/set_style: prefer 'ref' from superdoc_search, or use {kind:'selection', start:{kind:'text', blockId, offset}, end:{kind:'text', blockId, offset}}. For paragraph actions (set_alignment, set_indentation, set_spacing, set_direction, set_flow_options): use {kind:'block', nodeType:'paragraph'|'heading'|'listItem', nodeId:'<nodeId from blocks list>'}."
307
307
  },
308
308
  "value": {
309
309
  "type": "string",
@@ -320,7 +320,7 @@
320
320
  },
321
321
  "ref": {
322
322
  "type": "string",
323
- "description": "Handle ref string returned by a prior search/query result."
323
+ "description": "Handle ref from superdoc_search result (pass handle.ref value directly). Preferred over building a target object."
324
324
  },
325
325
  "content": {
326
326
  "oneOf": [
@@ -360,22 +360,6 @@
360
360
  },
361
361
  "description": "Controls nesting behavior. tables: 'allow' permits inserting tables inside other tables. Only for actions 'insert', 'replace'. Omit for other actions."
362
362
  },
363
- "blockId": {
364
- "type": "string",
365
- "description": "Block ID of the target paragraph."
366
- },
367
- "start": {
368
- "type": "number",
369
- "description": "Start offset within the block (character index)."
370
- },
371
- "end": {
372
- "type": "number",
373
- "description": "End offset within the block (character index)."
374
- },
375
- "offset": {
376
- "type": "number",
377
- "description": "Character offset for insertion (alias for --start/--end with same value). Only for action 'insert'. Omit for other actions."
378
- },
379
363
  "text": {
380
364
  "type": "string",
381
365
  "description": "Replacement text content. Only for action 'replace'. Omit for other actions."
@@ -462,7 +446,7 @@
462
446
  },
463
447
  {
464
448
  "toolName": "superdoc_format",
465
- "description": "Change text and paragraph formatting. Use action \"inline\" with a search ref for bold/italic/etc. Use action \"set_style\" with a styleId from superdoc_get_content info to apply a named paragraph style.",
449
+ "description": "Change text and paragraph formatting. Use this after superdoc_create to style new content, or with a search ref to restyle existing text. Action \"inline\" applies character formatting (bold, italic, underline, color, fontSize, fontFamily, highlight, strike, vertAlign) to a text range via \"ref\". Action \"set_style\" applies a named paragraph style by styleId (get available styles from superdoc_get_content info). Actions \"set_alignment\", \"set_indentation\", \"set_spacing\", \"set_direction\", and \"set_flow_options\" change paragraph-level properties and require a block target: {kind:\"block\", nodeType:\"paragraph\", nodeId:\"<nodeId>\"}, NOT a ref. Use \"set_flow_options\" with pageBreakBefore:true to start a paragraph on a new page. Supports \"dryRun\" and \"changeMode: tracked\" for inline formatting. Paragraph-level actions do NOT support tracked changes. Do NOT use a search ref for paragraph-level actions; they require a block target with nodeId. Do NOT use {kind:\"block\", start:{kind:\"nodeEdge\",...}} or selection-like structures for paragraph actions. ONLY {kind:\"block\", nodeType, nodeId} is accepted. Do NOT issue multiple superdoc_format calls in parallel; each call invalidates refs for subsequent calls. Format one block at a time. Do NOT hardcode formatting values; always read them from superdoc_get_content blocks and replicate.",
466
450
  "inputSchema": {
467
451
  "type": "object",
468
452
  "properties": {
@@ -472,11 +456,12 @@
472
456
  "inline",
473
457
  "set_alignment",
474
458
  "set_direction",
459
+ "set_flow_options",
475
460
  "set_indentation",
476
461
  "set_spacing",
477
462
  "set_style"
478
463
  ],
479
- "description": "The action to perform. One of: inline, set_alignment, set_direction, set_indentation, set_spacing, set_style."
464
+ "description": "The action to perform. One of: inline, set_alignment, set_direction, set_flow_options, set_indentation, set_spacing, set_style."
480
465
  },
481
466
  "force": {
482
467
  "type": "boolean",
@@ -651,7 +636,7 @@
651
636
  "start",
652
637
  "end"
653
638
  ],
654
- "description": "Selection target: {kind:'selection', start:{kind:'text', blockId, offset}, end:{kind:'text', blockId, offset}}. Use 'ref' instead when you have a search result handle. Required for actions 'set_style', 'set_alignment', 'set_indentation', 'set_spacing', 'set_direction'."
639
+ "description": "Selection target: {kind:'selection', start:{kind:'text', blockId, offset}, end:{kind:'text', blockId, offset}}. Use 'ref' instead when you have a search result handle. Required for actions 'set_style', 'set_alignment', 'set_indentation', 'set_spacing', 'set_flow_options', 'set_direction'."
655
640
  },
656
641
  "inline": {
657
642
  "type": "object",
@@ -969,6 +954,18 @@
969
954
  "atLeast"
970
955
  ]
971
956
  },
957
+ "contextualSpacing": {
958
+ "type": "boolean",
959
+ "description": "Only for action 'set_flow_options'. Omit for other actions."
960
+ },
961
+ "pageBreakBefore": {
962
+ "type": "boolean",
963
+ "description": "Only for action 'set_flow_options'. Omit for other actions."
964
+ },
965
+ "suppressAutoHyphens": {
966
+ "type": "boolean",
967
+ "description": "Only for action 'set_flow_options'. Omit for other actions."
968
+ },
972
969
  "direction": {
973
970
  "type": "string",
974
971
  "enum": [
@@ -1037,6 +1034,24 @@
1037
1034
  "target"
1038
1035
  ]
1039
1036
  },
1037
+ {
1038
+ "operationId": "doc.format.paragraph.setFlowOptions",
1039
+ "intentAction": "set_flow_options",
1040
+ "requiredOneOf": [
1041
+ [
1042
+ "target",
1043
+ "contextualSpacing"
1044
+ ],
1045
+ [
1046
+ "target",
1047
+ "pageBreakBefore"
1048
+ ],
1049
+ [
1050
+ "target",
1051
+ "suppressAutoHyphens"
1052
+ ]
1053
+ ]
1054
+ },
1040
1055
  {
1041
1056
  "operationId": "doc.format.paragraph.setDirection",
1042
1057
  "intentAction": "set_direction",
@@ -1049,7 +1064,7 @@
1049
1064
  },
1050
1065
  {
1051
1066
  "toolName": "superdoc_create",
1052
- "description": "Create one paragraph or heading per call. After creating, search for it and apply formatting (fontFamily, fontSize) from neighboring blocks. For multiple paragraphs, use superdoc_mutations with text.insert steps instead of calling create repeatedly.",
1067
+ "description": "You MUST call superdoc_format after this tool to match document styling. Create a single paragraph, heading, or table in the document. Returns a nodeId for chaining subsequent creates and for use as a block target in superdoc_format. When the user asks for a \"heading\", use action \"heading\" with a level (default 1). Use action \"paragraph\" only when the user asks for regular body text. Before creating, call superdoc_get_content blocks to read formatting from regular body text paragraphs (non-empty, non-title blocks with alignment \"justify\" or \"left\"). After creating, re-fetch blocks with superdoc_get_content to get a fresh ref for the new block, then apply TWO format calls: (1) superdoc_format action \"inline\" for character styling, AND (2) superdoc_format action \"set_alignment\" with the block target for paragraph alignment. Both calls are REQUIRED. For body paragraphs: inline {bold:false, underline:false, fontFamily, fontSize, color from body blocks}, alignment \"justify\". Ignore underline:true from blocks data for body text; it is a style artifact. For headings: inline {bold:true, underline:true, fontSize scaled up, fontFamily, color}, alignment \"center\". Position with \"at\": {kind:\"documentEnd\"} (default), {kind:\"documentStart\"}, or {kind:\"after\"/\"before\", target:{kind:\"block\", nodeType, nodeId}} for relative placement. When creating multiple items in sequence, use the previous response nodeId as the next \"at\" target to maintain correct ordering. Do NOT use newlines in \"text\" to create multiple paragraphs; call this tool separately for each one.",
1053
1068
  "inputSchema": {
1054
1069
  "type": "object",
1055
1070
  "properties": {
@@ -1057,9 +1072,10 @@
1057
1072
  "type": "string",
1058
1073
  "enum": [
1059
1074
  "heading",
1060
- "paragraph"
1075
+ "paragraph",
1076
+ "table"
1061
1077
  ],
1062
- "description": "The action to perform. One of: heading, paragraph."
1078
+ "description": "The action to perform. One of: heading, paragraph, table."
1063
1079
  },
1064
1080
  "force": {
1065
1081
  "type": "boolean",
@@ -1203,6 +1219,14 @@
1203
1219
  "level": {
1204
1220
  "type": "number",
1205
1221
  "description": "Heading level (1-6). Required for action 'heading'."
1222
+ },
1223
+ "rows": {
1224
+ "type": "number",
1225
+ "description": "Required for action 'table'."
1226
+ },
1227
+ "columns": {
1228
+ "type": "number",
1229
+ "description": "Required for action 'table'."
1206
1230
  }
1207
1231
  },
1208
1232
  "required": [
@@ -1222,12 +1246,20 @@
1222
1246
  "required": [
1223
1247
  "level"
1224
1248
  ]
1249
+ },
1250
+ {
1251
+ "operationId": "doc.create.table",
1252
+ "intentAction": "table",
1253
+ "required": [
1254
+ "rows",
1255
+ "columns"
1256
+ ]
1225
1257
  }
1226
1258
  ]
1227
1259
  },
1228
1260
  {
1229
1261
  "toolName": "superdoc_list",
1230
- "description": "Create and manipulate lists",
1262
+ "description": "Create and manipulate bullet and numbered lists. To create a list: first create all paragraphs at the SAME location using superdoc_create (chain each using the previous nodeId as the \"at\" target). Then call action \"create\" with mode:\"fromParagraphs\", a preset (\"disc\" for bullet, \"decimal\" for numbered), and a range target: {from:{kind:\"block\", nodeType:\"paragraph\", nodeId:\"<first>\"}, to:{kind:\"block\", nodeType:\"paragraph\", nodeId:\"<last>\"}}. The range converts ALL paragraphs between from and to into list items. Make sure no other content exists between them. Action \"set_type\" converts between bullet and ordered (target any item in the list, kind:\"ordered\" or \"bullet\"). Action \"insert\" adds a new item before/after a target list item. Actions \"indent\" and \"outdent\" change nesting level; \"set_level\" jumps to a specific level (0-8). Action \"detach\" converts a list item back to a plain paragraph. Do NOT target paragraphs with indent/outdent/set_type; these actions require a listItem target.",
1231
1263
  "inputSchema": {
1232
1264
  "type": "object",
1233
1265
  "properties": {
@@ -1536,7 +1568,7 @@
1536
1568
  },
1537
1569
  {
1538
1570
  "toolName": "superdoc_comment",
1539
- "description": "Comment threads — create, edit, delete",
1571
+ "description": "Manage document comment threads: create, read, update, and delete. To create a comment, first use superdoc_search to find the target text, then pass action \"create\" with the comment text and a target: {kind:\"text\", blockId:\"<blockId>\", range:{start:<N>, end:<N>}} using the blockId and highlightRange from the search result. For threaded replies, pass \"parentId\" with the parent comment ID. Action \"list\" returns all comments with optional pagination (limit, offset) and filtering (includeResolved:true to include resolved). Action \"get\" retrieves a single comment by ID. Action \"update\" changes status to \"resolved\" or marks as internal. Action \"delete\" removes a comment or reply by ID. Do NOT pass \"ref\", \"id\", or \"parentId\" when creating a new top-level comment; only \"action\", \"text\", and \"target\" are needed.",
1540
1572
  "inputSchema": {
1541
1573
  "type": "object",
1542
1574
  "properties": {
@@ -1672,7 +1704,7 @@
1672
1704
  },
1673
1705
  {
1674
1706
  "toolName": "superdoc_track_changes",
1675
- "description": "Review and resolve tracked changes",
1707
+ "description": "Review and resolve tracked changes (insertions, deletions, format changes) in the document. Action \"list\" returns all tracked changes with optional filtering by type (insert, delete, format) and pagination (limit, offset). Each change includes an ID, type, author, timestamp, and content preview. Action \"decide\" accepts or rejects changes. Pass decision:\"accept\" to apply the change permanently, or decision:\"reject\" to discard it. Target a single change with {id:\"<changeId>\"} or all changes at once with {scope:\"all\"}. Do NOT use this tool unless the document has tracked changes. Use superdoc_get_content info to check the tracked change count first.",
1676
1708
  "inputSchema": {
1677
1709
  "type": "object",
1678
1710
  "properties": {
@@ -1774,7 +1806,7 @@
1774
1806
  },
1775
1807
  {
1776
1808
  "toolName": "superdoc_search",
1777
- "description": "Find text or nodes in the document",
1809
+ "description": "Refs expire after any mutation; always re-search before the next edit. Find text patterns or nodes in the document and get ref handles for targeting edits and formatting. Use this to locate content before calling superdoc_edit or superdoc_format. Text search returns handle.ref covering only the matched substring. Node search finds blocks by type (paragraph, heading, table, listItem, etc.). The \"require\" parameter controls match cardinality: \"first\" returns one match, \"all\" returns every match, \"exactlyOne\" fails if not exactly one match. Supports scoping via \"within\" to search inside a single block. Do NOT use regex or markdown formatting markers (#, **, etc.) in search patterns; patterns are plain text only. Do NOT use this tool when you already have a ref from superdoc_get_content blocks or superdoc_create; use that ref directly.",
1778
1810
  "inputSchema": {
1779
1811
  "type": "object",
1780
1812
  "properties": {
@@ -1939,7 +1971,7 @@
1939
1971
  },
1940
1972
  {
1941
1973
  "toolName": "superdoc_mutations",
1942
- "description": "Atomic multi-step batch edits (escape hatch)",
1974
+ "description": "All steps succeed or all fail; no partial application. Execute multiple text edits atomically in a single batch. Use this INSTEAD OF multiple sequential superdoc_edit calls when you need 2+ text changes that should succeed or fail together. Each step has an id (e.g. \"s1\"), an op (text.rewrite, text.insert, text.delete, format.apply, assert), a \"where\" clause for targeting ({by:\"select\", select:{...}, require:\"first\"|\"exactlyOne\"|\"all\"} or {by:\"ref\", ref:\"...\"}), and \"args\" with operation-specific parameters. Action \"preview\" dry-runs the plan without modifying the document. Action \"apply\" executes it. CRITICAL: split mutations by phase. Text mutations (text.rewrite, text.insert, text.delete) go in one call. Formatting (format.apply) goes in a separate call with fresh refs from a new superdoc_search. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step. Overlapping steps fail with PLAN_CONFLICT_OVERLAP. Do NOT use this for single edits; use superdoc_edit instead. Do NOT mix text mutations and formatting in the same call.",
1943
1975
  "inputSchema": {
1944
1976
  "type": "object",
1945
1977
  "properties": {
@@ -53,6 +53,8 @@ def dispatch_intent_tool(
53
53
  return execute('doc.format.paragraph.setIndentation', rest)
54
54
  elif action == 'set_spacing':
55
55
  return execute('doc.format.paragraph.setSpacing', rest)
56
+ elif action == 'set_flow_options':
57
+ return execute('doc.format.paragraph.setFlowOptions', rest)
56
58
  elif action == 'set_direction':
57
59
  return execute('doc.format.paragraph.setDirection', rest)
58
60
  else:
@@ -64,6 +66,8 @@ def dispatch_intent_tool(
64
66
  return execute('doc.create.paragraph', rest)
65
67
  elif action == 'heading':
66
68
  return execute('doc.create.heading', rest)
69
+ elif action == 'table':
70
+ return execute('doc.create.table', rest)
67
71
  else:
68
72
  raise SuperDocError(f'Unknown action for superdoc_create: {action}', code='TOOL_DISPATCH_NOT_FOUND', details={'toolName': 'superdoc_create', 'action': action})
69
73
  elif tool_name == 'superdoc_list':
@@ -4,146 +4,187 @@ You are a document editing assistant. You have a DOCX document open and a set of
4
4
 
5
5
  ## Tools overview
6
6
 
7
- | Tool | Purpose |
8
- |------|---------|
9
- | superdoc_search | Find text or nodes in the document |
10
- | superdoc_get_content | Read document content (text, markdown, html, info) |
11
- | superdoc_edit | Insert, replace, delete text, undo/redo |
12
- | superdoc_create | Create paragraphs or headings (with optional styleId) |
13
- | superdoc_format | Apply inline and paragraph formatting, set named styles |
14
- | superdoc_list | Create and manipulate bullet/numbered lists |
15
- | superdoc_comment | Create, update, delete, and list comments |
16
- | superdoc_track_changes | Review and resolve tracked changes |
17
- | superdoc_mutations | Execute multi-step atomic edits in a single batch |
7
+ | Tool | Purpose | Mutates |
8
+ |------|---------|---------|
9
+ | superdoc_get_content | Read document content (blocks, text, markdown, html, info) | No |
10
+ | superdoc_search | Find text or nodes, get ref handles for targeting | No |
11
+ | superdoc_edit | Insert, replace, delete text, undo/redo | Yes |
12
+ | superdoc_create | Create paragraphs, headings, or tables | Yes |
13
+ | superdoc_format | Apply inline and paragraph formatting, set named styles | Yes |
14
+ | superdoc_list | Create and manipulate bullet/numbered lists | Yes |
15
+ | superdoc_comment | Create, update, delete, and list comment threads | Yes |
16
+ | superdoc_track_changes | List, accept, or reject tracked changes | Yes |
17
+ | superdoc_mutations | Execute multi-step atomic edits in a single batch | Yes |
18
18
 
19
19
  ## How targeting works
20
20
 
21
- Every editing tool needs a **target** — an address telling the API *where* to apply the change.
21
+ Every editing tool needs a **target** telling the API *where* to apply the change. There are three ways to get one:
22
22
 
23
- ### Getting targets
23
+ - **From blocks data**: Each block has a `ref` (pass directly to superdoc_edit or superdoc_format) and a `nodeId` (for building `at` positions with superdoc_create).
24
+ - **From superdoc_search**: Returns `handle.ref` covering the matched text. Use search when you need to find text patterns, not when you already know which block to target.
25
+ - **From superdoc_create**: Returns `nodeId` for chaining creates and building block targets. Re-fetch blocks after create to get a fresh ref before formatting.
24
26
 
25
- Use `superdoc_search` to find content. Each match item returns:
27
+ **Refs expire after any mutation.** Always re-search or re-read blocks before the next operation.
26
28
 
27
- - **`handle.ref`** — a ref string for text-level operations. Pass the ref string as:
28
- - `ref` parameter on `superdoc_format` (for inline styles like bold, italic)
29
- - `ref` parameter on `superdoc_edit` (for text replacement, deletion)
30
- - Example: `superdoc_format({action: "inline", ref: "text:eyJ...", inline: {bold: true}})`
31
- - **`address`** — a block-level address like `{ "kind": "block", "nodeType": "paragraph", "nodeId": "abc123" }`. Pass it as `target` to `superdoc_format` (for paragraph-level properties like alignment, spacing), `superdoc_list`, and `superdoc_create`.
29
+ ## Common workflows
32
30
 
33
- ### Text search results
31
+ ### Replace a word everywhere
34
32
 
35
- When searching for text (`type: "text"`), each match includes:
36
- - `snippet` — the matched text with surrounding context
37
- - `highlightRange` — `{ start, end }` character offsets of the match
38
- - `blocks` — array of `{ blockId, range }` entries showing which blocks contain the match
33
+ ```
34
+ superdoc_search({select: {type: "text", pattern: "old word"}, require: "all"})
35
+ superdoc_edit({action: "replace", ref: "<handle.ref>", text: "new word"})
36
+ ```
39
37
 
40
- ### Node search results
38
+ Use `require: "all"` with a single edit, not multiple steps targeting the same pattern.
41
39
 
42
- When searching for nodes (`type: "node"`), each match includes:
43
- - `address` — the block address of the matched node
40
+ ### Rewrite a full paragraph
44
41
 
45
- ## Multi-action tools
42
+ ```
43
+ superdoc_get_content({action: "blocks"})
44
+ // Find the paragraph in the response, use its block ref (covers full text)
45
+ superdoc_edit({action: "replace", ref: "<block.ref>", text: "Entirely new paragraph text."})
46
+ ```
46
47
 
47
- Most tools support multiple actions via an `action` parameter. For example:
48
- - `superdoc_get_content` with `action: "text"` returns plain text; `action: "info"` returns document metadata and styles.
49
- - `superdoc_edit` with `action: "insert"` inserts content; `action: "delete"` deletes content.
50
- - `superdoc_format` with `action: "inline"` applies inline formatting; `action: "set_style"` applies a named paragraph style.
48
+ A block ref from superdoc_get_content covers the entire block text. A search ref covers only the matched substring. Use block refs when rewriting or shortening whole paragraphs.
51
49
 
52
- Single-action tools like `superdoc_search` do not require an `action` parameter.
50
+ ### Add a new paragraph after a heading
53
51
 
54
- ## Workflow
52
+ ```
53
+ superdoc_search({select: {type: "text", pattern: "Introduction"}, require: "first"})
54
+ // Get blockId from result.items[0].blocks[0].blockId
55
+ superdoc_create({action: "paragraph", text: "New content here.", at: {kind: "after", target: {kind: "block", nodeType: "heading", nodeId: "<blockId>"}}})
56
+ // Re-fetch blocks to get a fresh ref for the new paragraph
57
+ superdoc_get_content({action: "blocks", offset: 0, limit: 5})
58
+ // Find the new paragraph in the response, use its ref and nodeId
59
+ // Read formatting from BODY TEXT paragraphs (non-title, alignment "justify" or "left"), not from headings
60
+ superdoc_format({action: "inline", ref: "<new block ref>", inline: {fontFamily: "<from body blocks>", fontSize: <from body blocks>, color: "<from body blocks>", bold: false}})
61
+ superdoc_format({action: "set_alignment", target: {kind: "block", nodeType: "paragraph", nodeId: "<create.nodeId>"}, alignment: "<from body blocks>"})
62
+ ```
55
63
 
56
- **ALWAYS start by calling `superdoc_get_content({action: "blocks"})` before any other tool.** This returns every block in the document with its nodeId, type, text preview, styleId, fontFamily, fontSize, bold, and alignment. You need this to:
57
- - Know the document's structure and block IDs for targeting
58
- - See what fonts, sizes, and styles are used so new content matches
59
- - Find blocks by their text preview without a separate search
64
+ ### Create multiple paragraphs in sequence
60
65
 
61
- After getting blocks:
62
- 1. **Search before editing**: Use `superdoc_search` to get valid targets (handles/refs).
63
- 2. **Edit with targets**: Pass handles/addresses from search results to editing tools.
64
- 3. **Re-search after each mutation**: Refs expire after any edit. Always search again before the next operation.
65
- 4. **Batch when possible**: For multi-step edits, prefer `superdoc_mutations`.
66
+ Create all paragraphs first (chaining nodeIds), then re-fetch blocks once and format them all:
66
67
 
67
- ### Style-aware content creation
68
+ ```
69
+ // Step 1: Create all paragraphs, chaining with nodeId
70
+ superdoc_create({action: "paragraph", text: "First item.", at: {kind: "documentEnd"}})
71
+ // Use nodeId from response for next create
72
+ superdoc_create({action: "paragraph", text: "Second item.", at: {kind: "after", target: {kind: "block", nodeType: "paragraph", nodeId: "<nodeId1>"}}})
73
+ superdoc_create({action: "paragraph", text: "Third item.", at: {kind: "after", target: {kind: "block", nodeType: "paragraph", nodeId: "<nodeId2>"}}})
68
74
 
69
- After creating any content (paragraph, heading), you MUST match the document's formatting:
75
+ // Step 2: Re-fetch blocks to get fresh refs for all new paragraphs
76
+ superdoc_get_content({action: "blocks", offset: 0, limit: 10})
70
77
 
71
- 1. **Create** the content with `superdoc_create`
72
- 2. **Search** for the new text with `superdoc_search` to get a ref handle
73
- 3. **Apply formatting** with `superdoc_format({action: "inline", ref: "<handle>", inline: {fontFamily: "...", fontSize: ...}})` using the fontFamily and fontSize values from the neighboring blocks in the blocks data
78
+ // Step 3: Format each paragraph using fresh refs from blocks
79
+ // Read formatting from BODY TEXT paragraphs (alignment "justify" or "left", not titles)
80
+ superdoc_format({action: "inline", ref: "<fresh ref1>", inline: {fontFamily: "<body>", fontSize: <body>, color: "<body>", bold: false}})
81
+ superdoc_format({action: "set_alignment", target: {kind: "block", nodeType: "paragraph", nodeId: "<nodeId1>"}, alignment: "<body alignment>"})
82
+ // Repeat for each paragraph...
83
+ ```
74
84
 
75
- Example: if blocks show `fontFamily: "Times New Roman, serif"` and `fontSize: 9.5`, apply those same values to your new content.
85
+ ### Bold or format existing text
76
86
 
77
- ### Placing content near specific text
87
+ ```
88
+ superdoc_search({select: {type: "text", pattern: "important phrase"}, require: "first"})
89
+ superdoc_format({action: "inline", ref: "<handle.ref>", inline: {bold: true}})
90
+ ```
78
91
 
79
- To add content near a heading or specific text (e.g., "add a paragraph after the Introduction section"):
92
+ ### Set paragraph alignment, spacing, or page breaks
80
93
 
81
- 1. **Search for the text**: `superdoc_search({select: {type: "text", pattern: "Introduction"}, require: "first"})`
82
- 2. **Get the blockId** from `result.items[0].blocks[0].blockId`
83
- 3. **Create content after it**: `superdoc_create({action: "paragraph", text: "...", at: {kind: "after", target: {kind: "block", nodeType: "heading", nodeId: "<blockId>"}}})`
94
+ Paragraph-level actions require a **block target with nodeId**, not a ref:
84
95
 
85
- **Do NOT search by node type and then try to match by position** — this is unreliable in large documents. Always search for the actual text content to find the exact location.
96
+ ```
97
+ superdoc_format({action: "set_alignment", target: {kind: "block", nodeType: "paragraph", nodeId: "<nodeId>"}, alignment: "center"})
98
+ superdoc_format({action: "set_flow_options", target: {kind: "block", nodeType: "paragraph", nodeId: "<nodeId>"}, pageBreakBefore: true})
99
+ superdoc_format({action: "set_spacing", target: {kind: "block", nodeType: "paragraph", nodeId: "<nodeId>"}, lineSpacing: {rule: "auto", value: 1.5}})
100
+ ```
86
101
 
87
- ## Using superdoc_mutations
102
+ ### Create a bullet or numbered list
88
103
 
89
- The mutations tool executes a plan of steps atomically. Use `action: "apply"` to execute, or `action: "preview"` to dry-run.
104
+ 1. Create all paragraphs at the SAME location, chaining with previous nodeId:
105
+ ```
106
+ superdoc_create({action: "paragraph", text: "Item one", at: {kind: "documentEnd"}})
107
+ superdoc_create({action: "paragraph", text: "Item two", at: {kind: "after", target: {kind: "block", nodeType: "paragraph", nodeId: "<nodeId1>"}}})
108
+ superdoc_create({action: "paragraph", text: "Item three", at: {kind: "after", target: {kind: "block", nodeType: "paragraph", nodeId: "<nodeId2>"}}})
109
+ ```
90
110
 
91
- Each step has:
92
- - `id` — unique step identifier (e.g., `"s1"`, `"s2"`)
93
- - `op` — the operation: `text.rewrite`, `text.insert`, `text.delete`, `format.apply`, `assert`
94
- - `where` — targeting: either `{ by: "select", select: {...}, require: "first"|"exactlyOne"|"all" }` or `{ by: "ref", ref: "handle-ref-string" }`
95
- - `args` — operation-specific arguments
111
+ 2. Convert the consecutive paragraphs to a list in one call:
112
+ ```
113
+ superdoc_list({action: "create", mode: "fromParagraphs", preset: "disc", target: {from: {kind: "block", nodeType: "paragraph", nodeId: "<first>"}, to: {kind: "block", nodeType: "paragraph", nodeId: "<last>"}}})
114
+ ```
96
115
 
97
- ### Workflow: split mutations by logical phase
116
+ Use preset "disc" for bullets, "decimal" for numbered. WARNING: the range converts ALL paragraphs between from and to. Make sure no other content exists between them.
98
117
 
99
- **Always use `superdoc_search` first** to obtain stable refs, then reference those refs in your mutation steps.
118
+ 3. To change a bullet list to numbered: `superdoc_list({action: "set_type", target: {kind: "block", nodeType: "listItem", nodeId: "<anyItemId>"}, kind: "ordered"})`
100
119
 
101
- Split mutation calls into logical rounds:
102
- 1. **Text mutations first** — all `text.rewrite`, `text.insert`, `text.delete` operations in one `superdoc_mutations` call.
103
- 2. **Formatting second** — all `format.apply` operations in a separate `superdoc_mutations` call, using fresh refs from a new `superdoc_search`.
120
+ ### Batch multiple text edits atomically
104
121
 
105
- **Why**: Text edits change content and invalidate addresses. If you interleave text edits and formatting in the same batch, formatting steps may target stale positions. By splitting into rounds and re-searching between them, every ref points to the correct content.
122
+ Use superdoc_mutations when you need 2+ text changes that must succeed or fail together:
106
123
 
107
- ## Using superdoc_comment
124
+ ```
125
+ superdoc_mutations({
126
+ action: "apply", atomic: true, changeMode: "direct",
127
+ steps: [
128
+ {id: "s1", op: "text.rewrite", where: {by: "select", select: {type: "text", pattern: "old term"}, require: "all"}, args: {replacement: {text: "new term"}}},
129
+ {id: "s2", op: "text.delete", where: {by: "select", select: {type: "text", pattern: " (deprecated)"}, require: "all"}, args: {}},
130
+ {id: "s3", op: "text.insert", where: {by: "select", select: {type: "text", pattern: "Section Title"}, require: "first"}, args: {position: "after", content: {text: " (Updated)"}}}
131
+ ]
132
+ })
133
+ ```
108
134
 
109
- The comment tool manages comment threads in the document.
135
+ Split mutations by phase: text mutations (text.rewrite, text.insert, text.delete) in one call, then formatting (format.apply) in a separate call with fresh refs from a new superdoc_search.
110
136
 
111
- - **`create`** — Create a new comment thread anchored to a target range.
112
- - **`update`** — Patch fields on an existing comment: change text, move the anchor target, toggle `isInternal`, or update the `status` field.
113
- - **`delete`** — Remove a comment or reply by ID.
114
- - **`get`** — Retrieve a single comment thread by ID, including replies.
115
- - **`list`** — List all comment threads in the document.
137
+ Never create two steps targeting overlapping text in the same block. Combine them into a single text.rewrite instead.
116
138
 
117
- ### Creating comments
139
+ ### Add a comment on specific text
118
140
 
119
- To add a comment on specific text:
120
- 1. Search for the text: `superdoc_search({select: {type: "text", pattern: "target phrase"}, require: "first"})`
121
- 2. Use the `handle.ref` from the result and the `blocks[0]` info to build the target:
122
- ```
123
- superdoc_comment({
124
- action: "create",
125
- text: "My comment",
126
- target: {kind: "text", blockId: "<blocks[0].blockId>", range: {start: <highlightRange.start>, end: <highlightRange.end>}}
127
- })
128
- ```
141
+ ```
142
+ superdoc_search({select: {type: "text", pattern: "target phrase"}, require: "first"})
143
+ superdoc_comment({
144
+ action: "create",
145
+ text: "Please review this section.",
146
+ target: {kind: "text", blockId: "<blocks[0].blockId>", range: {start: <highlightRange.start>, end: <highlightRange.end>}}
147
+ })
148
+ ```
129
149
 
130
- **Only pass `action`, `text`, and `target` for creating a new comment.** Do not pass other params — they belong to different comment actions.
150
+ Only pass `action`, `text`, and `target` when creating a new top-level comment. For threaded replies, add `parentId`.
151
+
152
+ ### Accept or reject tracked changes
153
+
154
+ ```
155
+ superdoc_track_changes({action: "list"})
156
+ // Review changes, then accept or reject
157
+ superdoc_track_changes({action: "decide", decision: "accept", target: {id: "<changeId>"}})
158
+ // Or accept all at once
159
+ superdoc_track_changes({action: "decide", decision: "accept", target: {scope: "all"}})
160
+ ```
131
161
 
132
- ### Resolving comments
162
+ ### Match existing document formatting (CRITICAL)
133
163
 
134
- To resolve a comment, use `action: "update"` with `{ commentId: "<id>", status: "resolved" }`. There is no separate resolve action — it's a status field on the `update` action.
164
+ When creating content "like" or "similar to" existing content:
135
165
 
136
- ## Important rules
166
+ 1. Read blocks to get exact formatting properties of the reference content
167
+ 2. Use the same nodeType. Title blocks are often bold+underline paragraphs, not heading nodes. Check the blocks data.
168
+ 3. Copy ALL formatting exactly: bold, underline, fontSize, fontFamily, color, alignment
137
169
 
138
- - **Refs expire after any mutation.** Always re-search after each edit to get fresh refs. When applying the same change to multiple matches (e.g., bold every occurrence), use `superdoc_mutations` to batch them atomically instead of calling tools individually per match.
139
- - **Replace all occurrences** of the same text with a single mutation step using `require: "all"`, not multiple steps targeting the same pattern (which causes overlap conflicts).
140
- - **Search patterns are plain text**, not markdown. Don't include `#`, `**`, or formatting markers in search patterns.
141
- - **`within` scopes to a single block**, not a section. To find text in a section, search the full document for the text directly.
142
- - **Table cells are separate blocks.** Search for individual cell values (e.g., `"28"`), not patterns spanning multiple cells.
143
- - **superdoc_search `select.type`** must be `"text"` or `"node"`. To find headings, use `{type: "node", nodeType: "heading"}`, NOT `{type: "heading"}`.
144
- - **Do NOT combine `limit`/`offset` with `require: "first"` or `require: "exactlyOne"`** in superdoc_search. Use `require: "any"` with `limit` for paginated results.
145
- - For `superdoc_format` inline properties, use `null` inside the `inline` object to clear a property (e.g., `"inline": { "bold": null }` removes bold).
146
- - **Creating lists** requires two modes:
147
- - `mode: "fromParagraphs"` — converts existing paragraphs into list items. Requires `target` (a block address of the paragraph to convert) and `kind` (`"bullet"` or `"ordered"`).
148
- - `mode: "empty"` — creates a new empty list at a paragraph position. Requires `at` (a block address: `{kind:"block", nodeType:"paragraph", nodeId:"<id>"}`) and `kind`.
149
- - **Workflow**: Create paragraph(s) first with `superdoc_create`, then convert with `superdoc_list` action `"create"`, mode `"fromParagraphs"`, passing the paragraph's address as `target`.
170
+ ### Choosing formatting values (CRITICAL)
171
+
172
+ When formatting newly created content, use the right source:
173
+
174
+ - **Body text** (paragraphs, lorem ipsum, regular content): Read fontFamily, fontSize, color from non-empty, non-title paragraphs with alignment "justify" or "left". Always set `bold: false` and `underline: false` for body text. Many DOCX documents report `underline: true` on all blocks due to style inheritance; this is a style artifact, not intentional formatting. Body paragraphs should NOT be underlined unless the user explicitly asks for it.
175
+ - **Headings/titles**: Read from existing heading or title blocks (centered, bold, possibly underline). Scale fontSize up from body text.
176
+ - **Signature/form fields**: Use justify or left alignment
177
+ - When the user says "heading", use `action: "heading"` with a level, even if the document uses styled paragraphs as titles.
178
+
179
+ ## Constraints
180
+
181
+ - **Format calls must be sequential, one per turn.** Each format call bumps the document revision and invalidates all outstanding refs. Do NOT issue multiple superdoc_format calls in parallel within the same turn. Format one block, then re-fetch if needed for the next block.
182
+ - **set_alignment target must be `{kind: "block", nodeType, nodeId}`.** NEVER use `{kind: "block", start: {kind: "nodeEdge", ...}}` or any selection-like structure. Only the flat block target with nodeType and nodeId is accepted.
183
+ - **Always format ALL created items.** If formatting fails partway through a batch, re-fetch blocks and continue formatting the remaining items. Do not stop after a partial failure.
184
+ - **Search patterns are plain text.** Do not include `#`, `**`, or formatting markers.
185
+ - **`select.type` must be "text" or "node".** To find headings: `{type: "node", nodeType: "heading"}`, NOT `{type: "heading"}`.
186
+ - **`within` scopes to a single block**, not a section. To find text in a section, search the full document.
187
+ - **Table cells are separate blocks.** Search for individual cell values, not patterns spanning multiple cells.
188
+ - **Do NOT combine `limit`/`offset` with `require: "first"` or `require: "exactlyOne"`.** Use `require: "any"` with `limit` for paginated results.
189
+ - **Do NOT hardcode formatting values.** Always read from blocks data and replicate.
190
+ - **Do NOT copy heading/title formatting onto body paragraphs.** Read from body text blocks (alignment "justify" or "left"), not title blocks.
@@ -39,5 +39,5 @@
39
39
  "mutates": true
40
40
  }
41
41
  ],
42
- "contractHash": "bdbc36d9a7952ac8dadba20c75efd4a43b34fc125295f937032db78bc11cbd62"
42
+ "contractHash": "4a3601ee0f28a73c712fbe06e8b4913a9ae882a71152f9f6e892ea51137fc5e8"
43
43
  }