@amaster.ai/pi-lark 0.1.2-beta.40 → 0.1.2-beta.42

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.
Files changed (122) hide show
  1. package/package.json +2 -2
  2. package/skills/lark-apps/SKILL.md +15 -3
  3. package/skills/lark-apps/references/lark-apps-automation.md +164 -0
  4. package/skills/lark-apps/references/lark-apps-db-execute.md +1 -1
  5. package/skills/lark-apps/references/lark-apps-db.md +2 -2
  6. package/skills/lark-apps/references/lark-apps-get.md +43 -0
  7. package/skills/lark-apps/references/lark-apps-html-publish.md +7 -2
  8. package/skills/lark-apps/references/lark-apps-init.md +1 -2
  9. package/skills/lark-apps/references/lark-apps-openapi-key.md +1 -1
  10. package/skills/lark-apps/references/lark-apps-release-create.md +3 -1
  11. package/skills/lark-base/SKILL.md +1 -1
  12. package/skills/lark-base/references/lark-base-dashboard-block-get-data.md +7 -7
  13. package/skills/lark-calendar/SKILL.md +89 -31
  14. package/skills/lark-calendar/references/lark-calendar-create.md +8 -39
  15. package/skills/lark-calendar/references/lark-calendar-room-find.md +5 -9
  16. package/skills/lark-calendar/references/lark-calendar-rsvp.md +1 -5
  17. package/skills/lark-calendar/references/lark-calendar-schedule-clear-time.md +59 -0
  18. package/skills/lark-calendar/references/lark-calendar-schedule-fuzzy-time.md +88 -0
  19. package/skills/lark-calendar/references/lark-calendar-schedule-meeting.md +67 -210
  20. package/skills/lark-calendar/references/lark-calendar-suggestion.md +1 -5
  21. package/skills/lark-calendar/references/lark-calendar-update.md +2 -7
  22. package/skills/lark-doc/SKILL.md +1 -1
  23. package/skills/lark-doc/references/lark-doc-fetch.md +4 -2
  24. package/skills/lark-doc/references/lark-doc-mindnote.md +17 -2
  25. package/skills/lark-doc/references/lark-doc-whiteboard.md +4 -0
  26. package/skills/lark-doc/references/lark-doc-xml-extended-blocks.md +35 -0
  27. package/skills/lark-doc/references/lark-doc-xml.md +3 -2
  28. package/skills/lark-drive/SKILL.md +17 -7
  29. package/skills/lark-drive/references/lark-drive-comment-location.md +16 -4
  30. package/skills/lark-drive/references/lark-drive-comments-guide.md +16 -8
  31. package/skills/lark-drive/references/lark-drive-delete.md +12 -0
  32. package/skills/lark-drive/references/lark-drive-export.md +39 -10
  33. package/skills/lark-drive/references/lark-drive-files-list.md +27 -2
  34. package/skills/lark-drive/references/lark-drive-inspect.md +2 -0
  35. package/skills/lark-drive/references/lark-drive-list-comments.md +125 -0
  36. package/skills/lark-drive/references/lark-drive-member-add.md +1 -1
  37. package/skills/lark-drive/references/lark-drive-permission-guide.md +12 -0
  38. package/skills/lark-drive/references/lark-drive-pull.md +3 -3
  39. package/skills/lark-drive/references/lark-drive-push.md +33 -6
  40. package/skills/lark-drive/references/lark-drive-status.md +12 -14
  41. package/skills/lark-drive/references/lark-drive-workflow-knowledge-organize.md +26 -20
  42. package/skills/lark-drive/references/lark-drive-workflow.md +2 -1
  43. package/skills/lark-im/SKILL.md +5 -4
  44. package/skills/lark-im/references/lark-im-messages-reply.md +1 -1
  45. package/skills/lark-im/references/lark-im-messages-send.md +1 -1
  46. package/skills/lark-mail/SKILL.md +12 -9
  47. package/skills/lark-mail/references/lark-mail-forward.md +1 -1
  48. package/skills/lark-mail/references/lark-mail-message-modify.md +48 -0
  49. package/skills/lark-mail/references/lark-mail-message-trash.md +41 -0
  50. package/skills/lark-mail/references/lark-mail-reply-all.md +1 -1
  51. package/skills/lark-mail/references/lark-mail-reply.md +1 -1
  52. package/skills/lark-mail/references/lark-mail-watch.md +1 -1
  53. package/skills/lark-markdown/SKILL.md +3 -2
  54. package/skills/lark-markdown/references/lark-markdown-create.md +22 -2
  55. package/skills/lark-minutes/SKILL.md +19 -4
  56. package/skills/lark-minutes/references/lark-minutes-download.md +0 -2
  57. package/skills/lark-minutes/references/lark-minutes-search.md +0 -2
  58. package/skills/lark-minutes/references/lark-minutes-speaker-replace.md +0 -2
  59. package/skills/lark-minutes/references/lark-minutes-summary.md +0 -2
  60. package/skills/lark-minutes/references/lark-minutes-todo.md +2 -4
  61. package/skills/lark-minutes/references/lark-minutes-update.md +0 -2
  62. package/skills/lark-minutes/references/lark-minutes-upload.md +10 -10
  63. package/skills/lark-shared/SKILL.md +26 -8
  64. package/skills/lark-sheets/SKILL.md +98 -29
  65. package/skills/lark-sheets/references/lark-sheets-batch-update.md +18 -9
  66. package/skills/lark-sheets/references/lark-sheets-changeset.md +105 -0
  67. package/skills/lark-sheets/references/lark-sheets-chart.md +4 -2
  68. package/skills/lark-sheets/references/lark-sheets-conditional-format.md +2 -0
  69. package/skills/lark-sheets/references/lark-sheets-filter-view.md +1 -1
  70. package/skills/lark-sheets/references/lark-sheets-float-image.md +6 -6
  71. package/skills/lark-sheets/references/lark-sheets-formula-translation.md +12 -3
  72. package/skills/lark-sheets/references/lark-sheets-formula-verify.md +77 -0
  73. package/skills/lark-sheets/references/lark-sheets-history.md +93 -0
  74. package/skills/lark-sheets/references/lark-sheets-pivot-table.md +7 -2
  75. package/skills/lark-sheets/references/lark-sheets-range-operations.md +44 -14
  76. package/skills/lark-sheets/references/lark-sheets-read-data.md +3 -3
  77. package/skills/lark-sheets/references/lark-sheets-sheet-structure.md +4 -4
  78. package/skills/lark-sheets/references/lark-sheets-visual-standards.md +4 -4
  79. package/skills/lark-sheets/references/lark-sheets-workbook.md +29 -4
  80. package/skills/lark-sheets/references/lark-sheets-write-cells.md +21 -11
  81. package/skills/lark-slides/SKILL.md +29 -18
  82. package/skills/lark-slides/references/asset-planning.md +16 -5
  83. package/skills/lark-slides/references/examples.md +57 -227
  84. package/skills/lark-slides/references/iconpark.md +2 -2
  85. package/skills/lark-slides/references/lark-slides-create.md +21 -2
  86. package/skills/lark-slides/references/lark-slides-media-upload.md +0 -1
  87. package/skills/lark-slides/references/lark-slides-pptx-template-workflows.md +89 -0
  88. package/skills/lark-slides/references/lark-slides-replace-pages.md +1 -1
  89. package/skills/lark-slides/references/lark-slides-replace-slide.md +1 -1
  90. package/skills/lark-slides/references/lark-slides-screenshot.md +11 -8
  91. package/skills/lark-slides/references/lark-slides-whiteboard.md +31 -30
  92. package/skills/lark-slides/references/lark-slides-xml-get.md +100 -0
  93. package/skills/lark-slides/references/lark-slides-xml-presentation-slide-delete.md +9 -7
  94. package/skills/lark-slides/references/lark-slides-xml-presentation-slide-get.md +4 -4
  95. package/skills/lark-slides/references/lark-slides-xml-presentation-slide-replace.md +12 -10
  96. package/skills/lark-slides/references/lark-slides-xml-presentations-get.md +14 -13
  97. package/skills/lark-slides/references/planning-layer.md +32 -2
  98. package/skills/lark-slides/references/slides_chart_demo.xml +1 -0
  99. package/skills/lark-slides/references/slides_xml_schema_definition.xml +1 -1
  100. package/skills/lark-slides/references/troubleshooting.md +7 -25
  101. package/skills/lark-slides/references/validation-checklist.md +18 -9
  102. package/skills/lark-slides/references/visual-planning.md +4 -3
  103. package/skills/lark-slides/references/xml-format-guide.md +50 -1
  104. package/skills/lark-slides/references/xml-schema-quick-ref.md +7 -3
  105. package/skills/lark-slides/scripts/xml_text_overlap_lint.py +647 -52
  106. package/skills/lark-slides/scripts/xml_text_overlap_lint_test.py +529 -0
  107. package/skills/lark-task/SKILL.md +1 -0
  108. package/skills/lark-task/references/lark-task-create.md +14 -1
  109. package/skills/lark-vc/references/lark-vc-recording.md +0 -2
  110. package/skills/lark-vc-agent/SKILL.md +24 -14
  111. package/skills/lark-vc-agent/references/lark-vc-agent-meeting-events.md +65 -37
  112. package/skills/lark-vc-agent/references/lark-vc-agent-meeting-leave.md +1 -1
  113. package/skills/lark-vc-agent/references/lark-vc-agent-meeting-list-active.md +8 -8
  114. package/skills/lark-whiteboard/references/lark-whiteboard-workflow.md +5 -2
  115. package/skills/lark-wiki/SKILL.md +4 -2
  116. package/skills/lark-wiki/references/lark-wiki-node-get.md +1 -1
  117. package/skills/lark-wiki/references/lark-wiki-node-list.md +9 -2
  118. package/skills/lark-calendar/references/lark-calendar-agenda.md +0 -78
  119. package/skills/lark-calendar/references/lark-calendar-freebusy.md +0 -124
  120. package/skills/lark-calendar/references/lark-calendar-search-event.md +0 -29
  121. package/skills/lark-sheets/references/lark-sheets-core-operations.md +0 -103
  122. package/skills/lark-slides/references/lark-slides-xml-presentation-slide-create.md +0 -220
@@ -117,6 +117,58 @@ class XmlTextOverlapLintTest(unittest.TestCase):
117
117
  self.assertIn("attribute", issue["hint"])
118
118
  self.assertIn("a=1&b=2", issue["hint"])
119
119
 
120
+ def test_lint_xml_reports_mismatched_xml_tag(self) -> None:
121
+ result = xml_text_overlap_lint.lint_xml(
122
+ """
123
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
124
+ <data>
125
+ <shape type="text" topLeftX="80" topLeftY="80" width="300" height="60">
126
+ <content textType="body"><p>Broken XML</content>
127
+ </shape>
128
+ </data>
129
+ </slide>
130
+ """
131
+ )
132
+ issue = result["issues"][0]
133
+ self.assertEqual(result["summary"]["slide_count"], 0)
134
+ self.assertEqual(result["summary"]["error_count"], 1)
135
+ self.assertEqual(issue["code"], "xml_not_well_formed")
136
+ self.assertIsInstance(issue["line"], int)
137
+ self.assertIsInstance(issue["column"], int)
138
+ self.assertIn("Broken XML", issue["context"])
139
+
140
+ def test_lint_xml_rejects_prefixed_sml_tags(self) -> None:
141
+ result = xml_text_overlap_lint.lint_xml(
142
+ """
143
+ <ns0:slide xmlns:ns0="http://www.larkoffice.com/sml/2.0">
144
+ <ns0:data>
145
+ <sml:shape xmlns:sml="http://www.larkoffice.com/sml/2.0" type="text" topLeftX="80" topLeftY="80" width="300" height="60">
146
+ <sml:content textType="body"><sml:p>Prefixed SML</sml:p></sml:content>
147
+ </sml:shape>
148
+ </ns0:data>
149
+ </ns0:slide>
150
+ """
151
+ )
152
+ issues = result["issues"]
153
+ self.assertEqual(result["summary"]["error_count"], 5)
154
+ self.assertEqual([issue["code"] for issue in issues], ["sml_prefixed_tag"] * 5)
155
+ self.assertEqual(issues[0]["tag"], "ns0:slide")
156
+ self.assertIn("default namespace", issues[0]["hint"])
157
+
158
+ def test_lint_xml_allows_unprefixed_tags_without_namespace(self) -> None:
159
+ result = xml_text_overlap_lint.lint_xml(
160
+ """
161
+ <slide>
162
+ <data>
163
+ <shape type="text" topLeftX="80" topLeftY="80" width="300" height="60">
164
+ <content textType="body"><p>Unprefixed SML</p></content>
165
+ </shape>
166
+ </data>
167
+ </slide>
168
+ """
169
+ )
170
+ self.assertEqual(result["summary"]["error_count"], 0)
171
+
120
172
  def test_lint_xml_accepts_escaped_entities_without_suspicious_entity_warning(self) -> None:
121
173
  result = xml_text_overlap_lint.lint_xml(
122
174
  """
@@ -162,6 +214,291 @@ class XmlTextOverlapLintTest(unittest.TestCase):
162
214
  self.assertEqual(result["summary"]["slide_count"], 1)
163
215
  self.assertEqual(result["summary"]["error_count"], 0)
164
216
 
217
+ def test_lint_xml_preserves_presentation_canvas_and_slide_order(self) -> None:
218
+ result = xml_text_overlap_lint.lint_xml(
219
+ """
220
+ <presentation xmlns="http://www.larkoffice.com/sml/2.0" width="1280" height="720">
221
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
222
+ <data>
223
+ <shape id="first" type="text" topLeftX="80" topLeftY="80" width="300" height="60">
224
+ <content textType="body"><p>First slide</p></content>
225
+ </shape>
226
+ </data>
227
+ </slide>
228
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
229
+ <data>
230
+ <img id="second" src="tok" topLeftX="100" topLeftY="120" width="240" height="160"/>
231
+ <shape id="skipped" type="text" topLeftX="80" topLeftY="80" width="300">
232
+ <content textType="body"><p>Missing height</p></content>
233
+ </shape>
234
+ </data>
235
+ </slide>
236
+ </presentation>
237
+ """
238
+ )
239
+ self.assertEqual(result["slide_size"], {"width": 1280, "height": 720})
240
+ self.assertEqual(result["summary"]["slide_count"], 2)
241
+ self.assertEqual([slide["slide_number"] for slide in result["slides"]], [1, 2])
242
+ self.assertEqual([slide["element_count"] for slide in result["slides"]], [1, 1])
243
+ self.assertEqual(result["summary"]["error_count"], 0)
244
+ self.assertEqual(result["summary"]["warning_count"], 0)
245
+
246
+ def test_lint_xml_reports_sxsd_unsupported_tag_with_alias_hint(self) -> None:
247
+ cases = [
248
+ ("textbox", '<textbox topLeftX="80" topLeftY="80" width="300" height="60">Text</textbox>', '<shape type="text">'),
249
+ ("image", '<image src="tok" topLeftX="80" topLeftY="80" width="300" height="180"/>', "<img>"),
250
+ ]
251
+ for tag_name, element_xml, expected_hint in cases:
252
+ with self.subTest(tag=tag_name):
253
+ result = xml_text_overlap_lint.lint_xml(
254
+ f"""
255
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
256
+ <data>{element_xml}</data>
257
+ </slide>
258
+ """
259
+ )
260
+ issue = result["issues"][0]
261
+ self.assertEqual(result["summary"]["error_count"], 1)
262
+ self.assertEqual(issue["code"], "sxsd_unsupported_tag")
263
+ self.assertEqual(issue["tag"], tag_name)
264
+ self.assertIn(expected_hint, issue["hint"])
265
+
266
+ def test_lint_xml_reports_sxsd_unsupported_attr_with_alias_hint(self) -> None:
267
+ cases = [
268
+ ("shape", "x", "topLeftX", '<shape type="text" x="80" topLeftY="80" width="300" height="60"><content><p>Text</p></content></shape>'),
269
+ ("content", "fontColor", "color", '<shape type="text" topLeftX="80" topLeftY="80" width="300" height="60"><content fontColor="rgba(0, 0, 0, 1)"><p>Text</p></content></shape>'),
270
+ ]
271
+ for tag_name, attr_name, expected_attr, element_xml in cases:
272
+ with self.subTest(attr=attr_name):
273
+ result = xml_text_overlap_lint.lint_xml(
274
+ f"""
275
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
276
+ <data>{element_xml}</data>
277
+ </slide>
278
+ """
279
+ )
280
+ issue = result["issues"][0]
281
+ self.assertEqual(result["summary"]["error_count"], 1)
282
+ self.assertEqual(issue["code"], "sxsd_unsupported_attr")
283
+ self.assertEqual(issue["tag"], tag_name)
284
+ self.assertEqual(issue["attr"], attr_name)
285
+ self.assertIn(expected_attr, issue["hint"])
286
+
287
+ def test_lint_xml_ignores_server_filled_id_attrs(self) -> None:
288
+ result = xml_text_overlap_lint.lint_xml(
289
+ """
290
+ <slide id="server-slide-id" xmlns="http://www.larkoffice.com/sml/2.0">
291
+ <data>
292
+ <shape id="server-shape-id" type="rect" topLeftX="80" topLeftY="80" width="300" height="160">
293
+ <fill id="server-fill-id" unexpected="value">
294
+ <fillColor color="rgba(255, 255, 255, 1)"/>
295
+ </fill>
296
+ </shape>
297
+ </data>
298
+ </slide>
299
+ """
300
+ )
301
+
302
+ self.assertEqual(result["summary"]["error_count"], 1)
303
+ issue = result["issues"][0]
304
+ self.assertEqual(issue["code"], "sxsd_unsupported_attr")
305
+ self.assertEqual(issue["tag"], "fill")
306
+ self.assertEqual(issue["attr"], "unexpected")
307
+
308
+ def test_lint_xml_reports_gradient_shorthand_attrs_on_fill_color(self) -> None:
309
+ result = xml_text_overlap_lint.lint_xml(
310
+ """
311
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
312
+ <data>
313
+ <shape type="rect" topLeftX="80" topLeftY="80" width="300" height="160">
314
+ <fill>
315
+ <fillColor
316
+ type="gradient"
317
+ color1="rgba(255, 0, 0, 1)"
318
+ color2="rgba(0, 0, 255, 1)"
319
+ angle="45"
320
+ stop1="0%"
321
+ stop2="100%"/>
322
+ </fill>
323
+ </shape>
324
+ </data>
325
+ </slide>
326
+ """
327
+ )
328
+ unsupported_attrs = {issue["attr"] for issue in result["issues"]}
329
+ self.assertEqual(result["summary"]["error_count"], 6)
330
+ self.assertEqual(
331
+ unsupported_attrs,
332
+ {"type", "color1", "color2", "angle", "stop1", "stop2"},
333
+ )
334
+ self.assertTrue(all(issue["code"] == "sxsd_unsupported_attr" for issue in result["issues"]))
335
+ self.assertTrue(all(issue["tag"] == "fillColor" for issue in result["issues"]))
336
+
337
+ def test_lint_xml_accepts_chart_field_simple_content_attrs(self) -> None:
338
+ result = xml_text_overlap_lint.lint_xml(
339
+ """
340
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
341
+ <data>
342
+ <chart topLeftX="80" topLeftY="80" width="520" height="320">
343
+ <chartPlotArea>
344
+ <chartPlot type="line"/>
345
+ </chartPlotArea>
346
+ <chartData>
347
+ <dim1>
348
+ <chartField name="month" valueType="string">Jan, Feb</chartField>
349
+ </dim1>
350
+ <dim2>
351
+ <chartField name="value" valueType="number">1, 2</chartField>
352
+ </dim2>
353
+ </chartData>
354
+ </chart>
355
+ </data>
356
+ </slide>
357
+ """
358
+ )
359
+ self.assertEqual(result["summary"]["error_count"], 0)
360
+ self.assertNotIn("issues", result)
361
+
362
+ def test_lint_xml_does_not_load_iconpark_index_without_icons(self) -> None:
363
+ original_loader = xml_text_overlap_lint.load_iconpark_icon_types
364
+
365
+ def fail_if_loaded() -> set[str]:
366
+ raise AssertionError("iconpark index should not be loaded without <icon iconType>")
367
+
368
+ xml_text_overlap_lint.load_iconpark_icon_types = fail_if_loaded
369
+ try:
370
+ result = xml_text_overlap_lint.lint_xml(
371
+ """
372
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
373
+ <data>
374
+ <shape type="text" topLeftX="80" topLeftY="80" width="300" height="60">
375
+ <content><p>No icons here</p></content>
376
+ </shape>
377
+ </data>
378
+ </slide>
379
+ """
380
+ )
381
+ finally:
382
+ xml_text_overlap_lint.load_iconpark_icon_types = original_loader
383
+
384
+ self.assertEqual(result["summary"]["error_count"], 0)
385
+ self.assertNotIn("issues", result)
386
+
387
+ def test_lint_xml_accepts_iconpark_icon_type_from_index(self) -> None:
388
+ result = xml_text_overlap_lint.lint_xml(
389
+ """
390
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
391
+ <data>
392
+ <icon iconType="iconpark/Base/setting.svg" topLeftX="80" topLeftY="80" width="48" height="48">
393
+ <fill><fillColor color="rgba(37, 99, 235, 1)"/></fill>
394
+ </icon>
395
+ </data>
396
+ </slide>
397
+ """
398
+ )
399
+ self.assertEqual(result["summary"]["error_count"], 0)
400
+ self.assertNotIn("issues", result)
401
+
402
+ def test_lint_xml_reports_icon_missing_fill_color(self) -> None:
403
+ cases = [
404
+ '<icon iconType="iconpark/Base/setting.svg" topLeftX="80" topLeftY="80" width="48" height="48"/>',
405
+ '<icon iconType="iconpark/Base/setting.svg" topLeftX="80" topLeftY="80" width="48" height="48"><fill/></icon>',
406
+ (
407
+ '<icon iconType="iconpark/Base/setting.svg" topLeftX="80" topLeftY="80" width="48" height="48">'
408
+ "<fill><fillColor/></fill></icon>"
409
+ ),
410
+ ]
411
+ for icon_xml in cases:
412
+ with self.subTest(icon=icon_xml):
413
+ result = xml_text_overlap_lint.lint_xml(
414
+ f"""
415
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
416
+ <data>{icon_xml}</data>
417
+ </slide>
418
+ """
419
+ )
420
+
421
+ issue = result["issues"][0]
422
+ self.assertEqual(result["summary"]["error_count"], 1)
423
+ self.assertEqual(issue["code"], "icon_missing_fill_color")
424
+ self.assertEqual(issue["tag"], "icon")
425
+
426
+ def test_lint_xml_reports_icon_transparent_fill_color(self) -> None:
427
+ result = xml_text_overlap_lint.lint_xml(
428
+ """
429
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
430
+ <data>
431
+ <icon iconType="iconpark/Base/setting.svg" topLeftX="80" topLeftY="80" width="48" height="48">
432
+ <fill><fillColor color="rgba(37, 99, 235, 0)"/></fill>
433
+ </icon>
434
+ </data>
435
+ </slide>
436
+ """
437
+ )
438
+ issue = result["issues"][0]
439
+ self.assertEqual(result["summary"]["error_count"], 1)
440
+ self.assertEqual(issue["code"], "icon_transparent_fill_color")
441
+ self.assertEqual(issue["tag"], "icon")
442
+ self.assertEqual(issue["attr"], "fillColor")
443
+
444
+ def test_lint_xml_reports_iconpark_icon_type_outside_index(self) -> None:
445
+ result = xml_text_overlap_lint.lint_xml(
446
+ """
447
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
448
+ <data>
449
+ <icon iconType="iconpark/Base/settng.svg" topLeftX="80" topLeftY="80" width="48" height="48">
450
+ <fill><fillColor color="rgba(37, 99, 235, 1)"/></fill>
451
+ </icon>
452
+ </data>
453
+ </slide>
454
+ """
455
+ )
456
+ issue = result["issues"][0]
457
+ self.assertEqual(result["summary"]["error_count"], 1)
458
+ self.assertEqual(issue["code"], "iconpark_unsupported_icon_type")
459
+ self.assertEqual(issue["tag"], "icon")
460
+ self.assertEqual(issue["attr"], "iconType")
461
+ self.assertEqual(issue["iconType"], "iconpark/Base/settng.svg")
462
+ self.assertIn("iconpark-index.json", issue["hint"])
463
+ self.assertIn("iconpark/Base/setting.svg", issue["hint"])
464
+
465
+ def test_lint_xml_allows_namespaced_svg_inside_whiteboard(self) -> None:
466
+ result = xml_text_overlap_lint.lint_xml(
467
+ """
468
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
469
+ <data>
470
+ <whiteboard id="wb" topLeftX="80" topLeftY="80" width="300" height="180">
471
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 180">
472
+ <rect x="10" y="10" width="120" height="60"/>
473
+ </svg>
474
+ </whiteboard>
475
+ </data>
476
+ </slide>
477
+ """
478
+ )
479
+ self.assertEqual(result["summary"]["error_count"], 0)
480
+ self.assertNotIn("issues", result)
481
+
482
+ def test_lint_xml_reports_unqualified_svg_inside_whiteboard_with_namespace_hint(self) -> None:
483
+ result = xml_text_overlap_lint.lint_xml(
484
+ """
485
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
486
+ <data>
487
+ <whiteboard id="wb" topLeftX="80" topLeftY="80" width="300" height="180">
488
+ <svg viewBox="0 0 300 180">
489
+ <rect x="10" y="10" width="120" height="60"/>
490
+ </svg>
491
+ </whiteboard>
492
+ </data>
493
+ </slide>
494
+ """
495
+ )
496
+ issue = result["issues"][0]
497
+ self.assertEqual(result["summary"]["error_count"], 1)
498
+ self.assertEqual(issue["code"], "sxsd_unsupported_tag")
499
+ self.assertEqual(issue["tag"], "svg")
500
+ self.assertIn('xmlns="http://www.w3.org/2000/svg"', issue["hint"])
501
+
165
502
  def test_lint_xml_detects_overlapping_text_boxes(self) -> None:
166
503
  result = xml_text_overlap_lint.lint_xml(
167
504
  """
@@ -182,6 +519,83 @@ class XmlTextOverlapLintTest(unittest.TestCase):
182
519
  self.assertEqual(result["summary"]["error_count"], 1)
183
520
  self.assertEqual(result["slides"][0]["issues"][0]["code"], "bbox_overlap")
184
521
 
522
+ def test_lint_xml_detects_current_itinerary_cjk_caption_occlusion(self) -> None:
523
+ result = xml_text_overlap_lint.lint_xml(
524
+ """
525
+ <slide id="pQO" xmlns="http://www.larkoffice.com/sml/2.0">
526
+ <data>
527
+ <shape width="190" height="80" topLeftX="580" topLeftY="170" presetHandlers="0" type="rect" id="blI">
528
+ <fill><fillColor color="rgba(255, 255, 255, 0.9)"/></fill>
529
+ <border color="rgba(220, 205, 185, 1)" width="1"/>
530
+ <content fontSize="16" fontFamily="思源黑体" color="rgba(31, 35, 41, 1)"/>
531
+ </shape>
532
+ <shape width="160" height="25" topLeftX="595" topLeftY="180" type="text" id="blX">
533
+ <content fontSize="14" fontFamily="思源黑体" color="rgba(120, 80, 40, 1)" bold="true"><p>日照金山</p></content>
534
+ </shape>
535
+ <shape width="160" height="40" topLeftX="595" topLeftY="205" type="text" id="blY">
536
+ <content textType="caption" fontSize="11" fontFamily="思源黑体" color="rgba(130, 100, 70, 1)"><p>清晨躺在床上看玉龙雪山日照金山奇观</p></content>
537
+ </shape>
538
+ <shape width="180" height="80" topLeftX="730" topLeftY="170" presetHandlers="0" type="rect" id="blH">
539
+ <fill><fillColor color="rgba(255, 255, 255, 0.9)"/></fill>
540
+ <border color="rgba(220, 205, 185, 1)" width="1"/>
541
+ <content fontSize="16" fontFamily="思源黑体" color="rgba(31, 35, 41, 1)"/>
542
+ </shape>
543
+ <shape width="150" height="25" topLeftX="745" topLeftY="180" type="text" id="blp">
544
+ <content fontSize="14" fontFamily="思源黑体" color="rgba(120, 80, 40, 1)" bold="true"><p>午餐返程</p></content>
545
+ </shape>
546
+ <shape width="150" height="40" topLeftX="745" topLeftY="205" type="text" id="blV">
547
+ <content textType="caption" fontSize="11" fontFamily="思源黑体" color="rgba(130, 100, 70, 1)"><p>享用特色午餐,带着美好回忆返程</p></content>
548
+ </shape>
549
+ <shape width="190" height="80" topLeftX="580" topLeftY="310" presetHandlers="0" type="rect" id="blP">
550
+ <fill><fillColor color="rgba(255, 255, 255, 0.9)"/></fill>
551
+ <border color="rgba(220, 205, 185, 1)" width="1"/>
552
+ <content fontSize="16" fontFamily="思源黑体" color="rgba(31, 35, 41, 1)"/>
553
+ </shape>
554
+ <shape width="160" height="25" topLeftX="595" topLeftY="320" type="text" id="blG">
555
+ <content fontSize="14" fontFamily="思源黑体" color="rgba(120, 80, 40, 1)" bold="true"><p>高路徒步</p></content>
556
+ </shape>
557
+ <shape width="160" height="40" topLeftX="595" topLeftY="345" type="text" id="blQ">
558
+ <content textType="caption" fontSize="11" fontFamily="思源黑体" color="rgba(130, 100, 70, 1)"><p>经典高路徒步,28道拐,龙洞瀑布,中虎跳峡</p></content>
559
+ </shape>
560
+ <shape width="180" height="80" topLeftX="730" topLeftY="310" presetHandlers="0" type="rect" id="blw">
561
+ <fill><fillColor color="rgba(255, 255, 255, 0.9)"/></fill>
562
+ <border color="rgba(220, 205, 185, 1)" width="1"/>
563
+ <content fontSize="16" fontFamily="思源黑体" color="rgba(31, 35, 41, 1)"/>
564
+ </shape>
565
+ <shape width="150" height="25" topLeftX="745" topLeftY="320" type="text" id="blZ">
566
+ <content fontSize="14" fontFamily="思源黑体" color="rgba(120, 80, 40, 1)" bold="true"><p>伴手礼</p></content>
567
+ </shape>
568
+ <shape width="150" height="40" topLeftX="745" topLeftY="345" type="text" id="blS">
569
+ <content textType="caption" fontSize="11" fontFamily="思源黑体" color="rgba(130, 100, 70, 1)"><p>酒店精心准备的归途伴手礼,留下难忘纪念</p></content>
570
+ </shape>
571
+ </data>
572
+ </slide>
573
+ """
574
+ )
575
+ overlap_pairs = {tuple(issue["elements"]) for issue in result["slides"][0]["issues"]}
576
+ self.assertEqual(result["summary"]["error_count"], 2)
577
+ self.assertIn(("blY", "blV"), overlap_pairs)
578
+ self.assertIn(("blQ", "blS"), overlap_pairs)
579
+
580
+ def test_lint_xml_detects_horizontal_text_overflow_across_declared_box_gap(self) -> None:
581
+ result = xml_text_overlap_lint.lint_xml(
582
+ """
583
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
584
+ <data>
585
+ <shape id="source" type="text" topLeftX="80" topLeftY="100" width="160" height="40">
586
+ <content fontSize="18"><p>这是一个足够长的中文文本用于检测跨越间隙的横向溢出</p></content>
587
+ </shape>
588
+ <shape id="target" type="text" topLeftX="260" topLeftY="100" width="160" height="40">
589
+ <content fontSize="18"><p>目标</p></content>
590
+ </shape>
591
+ </data>
592
+ </slide>
593
+ """
594
+ )
595
+ self.assertEqual(result["summary"]["error_count"], 1)
596
+ self.assertEqual(result["slides"][0]["issues"][0]["code"], "bbox_overlap")
597
+ self.assertEqual(result["slides"][0]["issues"][0]["elements"], ["source", "target"])
598
+
185
599
  def test_lint_xml_does_not_check_bounds_or_text_height(self) -> None:
186
600
  result = xml_text_overlap_lint.lint_xml(
187
601
  """
@@ -223,6 +637,38 @@ class XmlTextOverlapLintTest(unittest.TestCase):
223
637
  self.assertEqual(result["summary"]["error_count"], 0)
224
638
  self.assertEqual(result["summary"]["warning_count"], 0)
225
639
 
640
+ def test_extract_elements_preserves_supported_element_geometry_order_and_text_metadata(self) -> None:
641
+ elements = xml_text_overlap_lint.extract_elements(
642
+ """
643
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
644
+ <data>
645
+ <img id="photo" src="tok" topLeftX="10" topLeftY="20" width="100" height="80"/>
646
+ <shape id="headline" type="text" topLeftX="40" topLeftY="60" width="320" height="90">
647
+ <content textType="headline" textAlign="center" autoFit="normal-auto-fit" fontSize="28">
648
+ <p><![CDATA[Growth & scale]]></p>
649
+ <p>Focused execution</p>
650
+ </content>
651
+ </shape>
652
+ <table id="table" topLeftX="400" topLeftY="60" width="220" height="120"></table>
653
+ <chart id="chart" topLeftX="640" topLeftY="60" width="220" height="120"/>
654
+ <whiteboard id="wb" topLeftX="80" topLeftY="220" width="760" height="240"/>
655
+ <shape id="missing-height" type="text" topLeftX="80" topLeftY="480" width="320">
656
+ <content><p>Skipped</p></content>
657
+ </shape>
658
+ </data>
659
+ </slide>
660
+ """
661
+ )
662
+ self.assertEqual([element["id"] for element in elements], ["photo", "headline", "table", "chart", "wb"])
663
+ self.assertEqual([element["kind"] for element in elements], ["img", "shape", "table", "chart", "whiteboard"])
664
+ self.assertEqual([element["order"] for element in elements], [0, 1, 2, 3, 4])
665
+ self.assertEqual(elements[1]["type"], "text")
666
+ self.assertEqual(elements[1]["textType"], "headline")
667
+ self.assertEqual(elements[1]["textAlign"], "center")
668
+ self.assertEqual(elements[1]["autoFit"], "normal-auto-fit")
669
+ self.assertEqual(elements[1]["fontSize"], 28)
670
+ self.assertEqual(elements[1]["text"], "Growth & scale\nFocused execution")
671
+
226
672
  def test_lint_xml_does_not_check_small_out_of_bounds_elements(self) -> None:
227
673
  result = xml_text_overlap_lint.lint_xml(
228
674
  """
@@ -266,6 +712,89 @@ class XmlTextOverlapLintTest(unittest.TestCase):
266
712
  )
267
713
  self.assertEqual(result["summary"]["error_count"], 0)
268
714
 
715
+ def test_lint_xml_warns_for_whiteboard_external_boundary_overlap(self) -> None:
716
+ result = xml_text_overlap_lint.lint_xml(
717
+ """
718
+ <presentation xmlns="http://www.larkoffice.com/sml/2.0" width="960" height="540">
719
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
720
+ <data>
721
+ <whiteboard id="wb" topLeftX="50" topLeftY="140" width="860" height="360"/>
722
+ <shape id="card" type="rect" topLeftX="50" topLeftY="450" width="200" height="60">
723
+ <fill><fillColor color="rgba(232, 186, 176, 0.2)"/></fill>
724
+ <content/>
725
+ </shape>
726
+ <shape id="label" type="text" topLeftX="65" topLeftY="460" width="170" height="20">
727
+ <content fontSize="12"><p>基础保障</p></content>
728
+ </shape>
729
+ </data>
730
+ </slide>
731
+ </presentation>
732
+ """
733
+ )
734
+ issues = result["slides"][0]["issues"]
735
+ self.assertEqual(result["summary"]["error_count"], 0)
736
+ self.assertEqual(result["summary"]["warning_count"], 1)
737
+ self.assertEqual(issues[0]["code"], "whiteboard_external_overlap")
738
+ self.assertIn("static whiteboard container-bbox risk", issues[0]["hint"])
739
+ self.assertIn("screenshot QA", issues[0]["hint"])
740
+ self.assertEqual(issues[0]["elements"], ["wb", "card"])
741
+ self.assertEqual(issues[0]["overlaps"][0]["overlap_height"], 50)
742
+
743
+ def test_lint_xml_allows_whiteboard_internal_labels(self) -> None:
744
+ result = xml_text_overlap_lint.lint_xml(
745
+ """
746
+ <presentation xmlns="http://www.larkoffice.com/sml/2.0" width="960" height="540">
747
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
748
+ <data>
749
+ <whiteboard id="wb" topLeftX="100" topLeftY="120" width="420" height="260"/>
750
+ <shape id="label" type="text" topLeftX="150" topLeftY="180" width="180" height="40">
751
+ <content fontSize="14"><p>内部标注</p></content>
752
+ </shape>
753
+ </data>
754
+ </slide>
755
+ </presentation>
756
+ """
757
+ )
758
+ self.assertEqual(result["summary"]["error_count"], 0)
759
+ self.assertEqual(result["summary"]["warning_count"], 0)
760
+
761
+ def test_lint_xml_allows_bottom_layer_full_slide_whiteboard_background(self) -> None:
762
+ result = xml_text_overlap_lint.lint_xml(
763
+ """
764
+ <presentation xmlns="http://www.larkoffice.com/sml/2.0" width="960" height="540">
765
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
766
+ <data>
767
+ <whiteboard id="background" topLeftX="0" topLeftY="0" width="960" height="540"/>
768
+ <shape id="title" type="text" topLeftX="80" topLeftY="80" width="420" height="80">
769
+ <content textType="title" fontSize="36"><p>Title</p></content>
770
+ </shape>
771
+ </data>
772
+ </slide>
773
+ </presentation>
774
+ """
775
+ )
776
+ self.assertEqual(result["summary"]["error_count"], 0)
777
+ self.assertEqual(result["summary"]["warning_count"], 0)
778
+
779
+ def test_lint_xml_allows_whiteboard_inside_background_panel(self) -> None:
780
+ result = xml_text_overlap_lint.lint_xml(
781
+ """
782
+ <presentation xmlns="http://www.larkoffice.com/sml/2.0" width="960" height="540">
783
+ <slide xmlns="http://www.larkoffice.com/sml/2.0">
784
+ <data>
785
+ <shape id="panel" type="rect" topLeftX="40" topLeftY="100" width="880" height="340">
786
+ <fill><fillColor color="rgba(232, 186, 176, 0.2)"/></fill>
787
+ <content/>
788
+ </shape>
789
+ <whiteboard id="wb" topLeftX="60" topLeftY="120" width="840" height="300"/>
790
+ </data>
791
+ </slide>
792
+ </presentation>
793
+ """
794
+ )
795
+ self.assertEqual(result["summary"]["error_count"], 0)
796
+ self.assertEqual(result["summary"]["warning_count"], 0)
797
+
269
798
  def test_lint_xml_detects_invalid_template_text_stack_overlap(self) -> None:
270
799
  cases = [
271
800
  (
@@ -13,6 +13,7 @@ metadata:
13
13
  **CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),其中包含认证、权限处理**
14
14
 
15
15
  > **任务搜索技巧**:先区分用户是否**特地指定使用搜索 skill**,以及是否真的提供了**查询关键字**(例如任务名称、关键词、片段描述)。如果用户特地指定使用搜索 skill,或明确给出了任务查询关键字,则目标是**任务**时优先使用 `+search`。如果用户没有特地指定使用搜索 skill,且意图里没有查询关键字,只有范围条件(例如“今年以来”“已完成”“由我创建”“我关注的”),并且使用 `+search` 与 `+get-related-tasks` / `+get-my-tasks` 都能达到目的时,应优先使用列表型能力,而不是搜索型能力。其中,“与我相关 / 我关注的 / 由我创建”等优先考虑 `+get-related-tasks`;“我负责的 / 分配给我”的列表优先考虑 `+get-my-tasks`。不要把时间范围词(例如“今年以来”)本身误当成 `query` 去走搜索。
16
+ > **任务搜索相关性提示**:`+search` 当前不会自动判断搜索结果与搜索发起人的相关性。如果用户明确要求搜索“与我相关”的任务,必须先识别具体关系,获取当前用户的 `open_id`,并显式传入对应的 `--assignee`(负责人)、`--creator`(创建人)或 `--follower`(关注人)过滤条件;不能只依赖 `query` 期待自动返回与当前用户相关的任务。
16
17
  > **任务清单搜索技巧**:任务清单也遵循同样的判断逻辑。先区分用户是否**特地指定使用搜索 skill**,以及是否真的提供了**清单查询关键字**(例如清单名称、关键词、片段描述)。如果用户特地指定使用搜索 skill,或明确给出了清单查询关键字,则优先使用 `+tasklist-search`。如果用户没有特地指定使用搜索 skill,且意图里没有查询关键字,只有范围条件(例如“由我创建的任务清单”“今年以来创建的清单”),并且使用搜索或原生列取清单都能达到目的时,应优先使用原生 `tasklists.list` 接口列取清单(先 `schema task.tasklists.list`,再 `lark-cli task tasklists list --as user ...`),再按 `creator`、`created_at` 等字段做本地筛选和分页控制。
17
18
  > **意图区分补充**:像“搜索飞书中今年以来我关注的任务”这类表达,虽然字面带有“搜索”,但如果没有真正的查询关键字,且本质是在限定“与我相关 + 时间范围”,则应优先走 `+get-related-tasks`;像“搜索飞书中由我创建的任务清单”这类表达,如果没有清单关键字,且本质是在限定“清单范围 + 创建者”,则应优先走原生 `tasklists.list` 后筛选,而不是直接走搜索型 shortcut。
18
19
  > **用户身份识别**:在用户身份(user identity)场景下,如果用户提到了“我”(例如“分配给我”、“由我创建”),请默认获取当前登录用户的 `open_id` 作为对应的参数值。
@@ -46,7 +46,20 @@ lark-cli task +create --summary "Test Task" --dry-run
46
46
  1. Confirm with the user: task summary, due date, assignee, and tasklist if necessary.
47
47
  - **Crucial Rule for Assignee**: If the user explicitly or implicitly says "create a task for me" (给我创建一个任务), or "help me create a task" (帮我新建/创建一个任务), you MUST assign the task to the current logged-in user. You can get the current user's `open_id` by executing `lark-cli auth status` (it already outputs JSON by default, so do not add `--json`) or `lark-cli contact +get-user` first, extracting `.identities.user.openId` (from `auth status`) or `.data.user.open_id` (from `contact +get-user`), and then passing it to the `--assignee` parameter.
48
48
  2. Execute `lark-cli task +create --summary "..." ...`
49
- 3. Report the result: task ID and summary.
49
+ 3. Judge success by `ok == true` in the stdout JSON (the success envelope has no `code` field — do not test `code == 0`), then report the result: task ID (`data.guid`) and summary.
50
+
51
+ Example success response:
52
+
53
+ ```json
54
+ {
55
+ "ok": true,
56
+ "identity": "user",
57
+ "data": {
58
+ "guid": "e297d3d0-4b60-4a5f-a4d4-xxxxxxxxxxxx",
59
+ "url": "https://applink.larkoffice.com/client/todo/detail?guid=e297d3d0-4b60-4a5f-a4d4-xxxxxxxxxxxx"
60
+ }
61
+ }
62
+ ```
50
63
 
51
64
  > [!CAUTION]
52
65
  > This is a **Write Operation** -- You must confirm the user's intent before executing.
@@ -1,7 +1,6 @@
1
1
 
2
2
  # vc +recording
3
3
 
4
- > **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
5
4
 
6
5
  通过 meeting_id 或 calendar_event_id 查询对应的 minute_token。这是 VC 域和 Minutes 域之间的桥梁命令。只读操作。
7
6
 
@@ -151,4 +150,3 @@ lark-cli minutes +download --minute-tokens <minute_token>
151
150
  - [lark-vc](../SKILL.md) — 视频会议全部命令
152
151
  - [lark-vc-search](lark-vc-search.md) — 搜索历史会议(获取 meeting_id)
153
152
  - [lark-minutes-detail](../../lark-minutes/references/lark-minutes-detail.md) — 获取会议纪要
154
- - [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数