Wrap the Drive file-copy endpoint as drive +copy. Accept a document URL
(recommended) or bare token + --type for the source; the target takes a
folder token, a folder URL, or the my_space constant, which resolves the
caller's My Space root folder via the root-folder-meta endpoint (absent
from platform metadata, works for both user and bot). Repeatable --extra
key=value pairs are forwarded verbatim for special copy semantics (e.g.
target_type=docx to convert a legacy doc during copy). Source and folder
tokens are validated with validate.ResourceName before path
interpolation. Reject wiki URLs/tokens with a typed validation error
whose hint carries a wiki +node-copy command template using a fixed
<node-token> placeholder, because a Drive copy of a wiki-backed document
would land in Drive space instead of the wiki tree. In bot mode the CLI
auto-grants the current CLI user full_access on the new copy (same
behavior as +upload/+import), reporting the outcome in the
permission_grant output field without failing the copy.
Declare docs:document:copy (the narrowest scope in the endpoint's any-of
set) plus a conditional drive:drive.metadata:readonly for my_space
resolution. Cover the shortcut with unit tests, dry-run e2e and a
self-contained live workflow (upload -> copy -> download-verify ->
my_space copy -> cleanup), and register it in
tests/cli_e2e/drive/coverage.md. Route copy intents in the lark-drive
skill to the shortcut instead of the raw files copy service command.
Adds AI-friendly output path handling to `slides +screenshot`.
- Supports `--output` for a single screenshot in both existing-slide and XML render modes.
- Validates selector count, conflicting output flags, unsafe paths, directories, whitespace, and unsupported extensions with structured errors.
- Reconciles the requested filename with the server’s actual PNG/JPEG format and reports the final path through `output`, `requested_output`, and `output_adjusted`.
- Avoids replacing existing screenshots by appending `_2`, `_3`, and subsequent suffixes.
- Keeps `--output-dir` for multi-page screenshots and preserves `--output-name` for render mode.
- Updates the Slides Skill with explicit `--slide-number` / `--slide-id` guidance and task-scoped screenshot directories.
- Adds unit, dry-run E2E, and live workflow coverage for validation, path handling, format adjustment, and collision behavior.
XML written into a field name this shortcut does not accept — most often
"content", because <shape> nests a <content> child — was silently dropped,
so the part failed the required-field check and reported "requires
non-empty replacement". That reads as "the value is empty", which sends
callers rewriting the value instead of the key.
Reject fields outside the action's own set and name the field the caller
most likely meant, with a correct one-liner attached as a hint. Matching
folds case and separators so "Content", "newXml" and "block-id" resolve
too, while the whitelist itself stays exact: the API accepts only
snake_case, so "Replacement" must be rejected rather than slip through.
Only block_replace and block_insert parts are checked, so missing /
str_replace / unknown actions keep their existing errors, and an
actually-empty payload still reports the non-empty wording.
The alias list covers only names that plausibly carry a fragment. A shape
attribute like "fill" is deliberately absent: whoever writes it means
"recolor this block", not "here is my XML", so answering did-you-mean
"replacement" would be guessing. The unknown-field error already names the
valid set, which is true under either reading.
Docs carry the same constraint at the three points a caller can hit first:
SKILL.md, the +replace-slide reference (warning + counter-examples + error
table), and the read-modify-write workflow. The --parts flag description
now spells the field names out instead of eliding them behind "...".
Note: this tightens parsing. Extra keys inside a part used to be ignored;
they are now rejected.
- require a slide ID or slide number for screenshot requests
- reject explicitly empty slide IDs
- remove unreachable dry-run validation
- document full-deck screenshot batching
- add unit and dry-run E2E coverage
* fix(slides): preserve requested lint input path
* fix(slides): migrate SML namespace from HTTP to HTTPS
- Change canonical namespace to https://www.larkoffice.com/sml/2.0
in protocol schema, production code, docs, and tests
- Keep HTTP and /sml/2.0 as legacy readback compat in validator
- Fix sml_prefixed_tag check to cover all accepted SML namespaces
- Add regression test for legacy HTTP namespace acceptance
Add agent-friendly aliases for slides +screenshot while preserving the canonical flag behavior.
- Support presentation and slide selector aliases, including --presentation-id, --slides, --slide-ids, --slide-numbers, and --slide.
- Route digits-only --slide values to page numbers and other values to slide IDs.
- Merge and deduplicate same-type selectors, reject mixed ID/number requests, and report the caller’s actual flag names in structured validation errors.
- Clarify selector exclusivity in the screenshot reference.
- Add unit, dry-run E2E, and self-contained live E2E coverage for aliases, validation, screenshot output, and cleanup.
Add an in-place whole-page slide update shortcut with validation, aliases, docs, unit tests, and dry-run E2E coverage.
Deprecate the superseded +replace-pages: the binary keeps the command working for a deprecation window, with the replacement named in its --help description and in a `deprecated` field on every output (dry-run, validate-only and real runs), while the skill no longer routes to it. Multi-page updates now call +update-slide once per page. The XML/revision helpers it shared with +add-slide / +delete-slide move to slides_shared.go so its eventual removal cannot break them.
+add-slide and +delete-slide now declare --presentation through the shared presentation-ref flag, so they accept the same alias spellings (--token, --url, ...) as every other slides shortcut.
Add two single-page slide shortcuts on top of the raw
xml_presentation.slide create/delete APIs.
slides +add-slide appends or inserts one page into an existing
presentation. It accepts --presentation as a token, a /slides/ URL or a
/wiki/ URL (resolved via wiki.spaces.get_node and checked for
obj_type=slides), takes the page XML through --slide as a literal, @file
or stdin so the document never has to be escaped into JSON and then into
the shell, and auto-uploads <img src="@./local.png"> placeholders,
replacing them with the returned file_token. Omitting --before-slide-id
appends to the end; the field is dropped from the body rather than sent
empty, which the backend rejects as an unknown slide.
slides +delete-slide removes one page by slide_id with the same
--presentation resolution. It is deliberately Risk "write" rather than
the raw command's high-risk-write, so it does not require --yes: it
targets a single explicit page and the deck keeps its version history.
Both take one page at a time so that batching stays an explicit loop and
every call has an unambiguous outcome.
The image placeholder validation used by +create is extracted into a
shared helper so both commands fail before any API call when a referenced
file is missing, is not a regular file or exceeds the 20 MB upload limit.
Covered by unit tests and by dry-run e2e tests through the built binary,
which is the only layer that proves a full <slide> document survives flag
parsing intact. Reference docs are added for both commands and the
existing slides skill docs now route to them.
* docs(base): clarify form and file operation routing
* docs: clarify complete base role table rules
* docs: clarify base advanced permission status
* docs: clarify base form field lifecycle
* docs: guide base form question creation
* fix(base): address form dry-run review findings
* docs(base): add complete editable role example
* fix(base): validate form question create inputs
* feat(drive): support Miaoda apps in permission shortcuts
Extend Drive permission shortcuts to accept Miaoda page URLs and the apps resource type while keeping each endpoint's accepted resource contract explicit.
Key features:
- Infer apps from /page/ URLs and accept explicit --type=apps in +apply-permission, +member-add, +member-list, and +permission-get-setting
- Decouple secure-label target parsing so expanding apply-permission does not widen secure-label support
- Align skill guidance and unit/dry-run coverage with the new resource type
* test(drive): cover apps permission target validation
Add focused coverage for Miaoda apps target handling across apply-permission and secure-label boundaries.
Exercise malformed page URLs, explicit apps bare tokens, typed validation errors, and command-level rejection so future resource-type changes cannot silently widen unsupported secure-label behavior.
* fix(drive): parse permission markers from URL paths
Keep drive +apply-permission resource inference aligned with URL component boundaries. Parse and validate URL inputs before extracting tokens so query strings and fragments cannot redirect permission requests to a different resource.
Key fixes:
- Match document and apps markers only against the parsed URL path
- Reject malformed URLs with a typed --token validation error
- Cover /page/ markers found only in query strings or fragments
* docs(skills): redact Miaoda page token example
Replace the concrete Miaoda page token with a representative pagcn placeholder. This keeps the token shape recognizable while avoiding exposure of a real resource identifier in the skill documentation.
* fix(drive): harden permission target resolution
Make Drive shortcut targets unambiguous before they reach read or write API paths. URL inputs now bind to a recognized root path and a single validated token segment, preventing encoded separators, dot segments, and type conflicts from silently changing the addressed resource.
Key fixes:
- Reject non-root URLs, dot/traversal tokens, and URL/type conflicts for secure-label and permission-apply writes
- Keep permission-setting URL parsing and pretty output reversible for every supported command-local resource kind
- Add unit and dry-run E2E regressions plus aligned permission-apply guidance
Add comment-domain shortcuts: +batch-query-comments, +resolve-comment,
+restore-comment, +add-reply, +list-replies, +update-reply, +delete-reply
and +react-reply, sharing one target resolver with per-endpoint file_type
sets.
Flatten the comment reference docs by dropping the comments-guide routing
layer and folding its cross-command knowledge into the command refs:
comment-card model, comment/reply/interaction counting and sorting rules
into lark-drive-list-comments.md; the --solved-status prerequisite into
lark-drive-restore-comment.md; the apps exception into
lark-drive-add-comment.md. Comment intents now route straight from the
drive SKILL.md Shortcuts table to each command ref.
Cover the new shortcuts with unit tests, dry-run e2e and live workflow
e2e behind LARK_DRIVE_MD_COMMENT_E2E=1, and register them in
tests/cli_e2e/drive/coverage.md.
Form questions can now carry a visible_rule (display condition) so a question shows only when earlier questions match the rule. The rule shares the exact same structure as the view filter, so extract that structure into a single shared reference (lark-base-filter-condition.md) that both view-set-filter and visible_rule point to.
- create/update shortcuts: document visible_rule in --questions help and transcribe the questions body (including visible_rule) into dry-run output
- document that form question updates use full overwrite semantics and must preserve existing fields via read-modify-write
- skill refs: add visible_rule sections to form-questions create/update, note it is only needed when the user asks for a display condition, and clarify that the shared tuple filter protocol does not apply to data-query filters
- tests: pin flag help, verbatim visible_rule passthrough on create/update/list, and add dry-run E2E coverage
Co-authored-by: yballul-bytedance <273011618+yballul-bytedance@users.noreply.github.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com>
* feat(drive): add +permission-get-setting shortcut
Add a Drive shortcut for reading public permission settings across supported documents, files, folders, and wiki nodes. Resolve URLs into typed resources, preserve permission_public output for machine consumers, and document the shortcut in the permission-governance workflow.
Key features:
- Infer resource type and token from supported Drive URLs while requiring --type for bare tokens
- Query the Drive v2 public permission endpoint with typed validation and user or bot identity
- Support folder permission inspection without recursing into child resources
- Add unit, dry-run E2E, live workflow, output, and skill guidance coverage
* fix(drive): harden permission get setting contract
Harden +permission-get-setting after review findings so callers receive only the documented permission payload and folder support is verified against the live workflow. This prevents malformed responses from being presented as permission settings and keeps the command guidance aligned with the shortcut contract.
Key fixes:
- Reject responses without data.permission_public instead of projecting arbitrary payload fields
- Render complete permission settings in pretty output and mark --token required
- Exercise a created Drive folder in the live workflow and add the command reference
- Correct folder resolution guidance while retaining the shortcut's documented URL forms
* feat/drive-folder-permission-get
* feat(drive): add +member-list shortcut
Add a Drive shortcut for listing collaborators on documents, files, folders, and wiki nodes. Resolve supported resource URLs into typed permission requests, preserve raw API data for machine consumers, and keep invalid flag combinations on typed validation paths.
Key features:
- Infer resource type and token from supported Drive URLs while requiring --type for bare tokens
- Validate optional member fields and wiki-only permission type filters
- Provide pretty output, skill guidance, unit coverage, and dry-run/live E2E workflows
- Read dry-run assertions from the standard data.api success envelope
* feat/drive-member-list
--slide-id used the cobra StringArray flag type, which only accepts
repeated flags and does not split comma-separated values, unlike
--slide-number (int_array -> cobra IntSlice) which already supported
CSV input. This made the two selector flags inconsistent.
Switch --slide-id to the string_slice flag type (cobra StringSlice),
which natively supports both comma-separated and repeated values, and
update the flag readers from StrArray to StrSlice. normalizeSlideIDs
already trims/dedupes/filters blanks, and
validateSlidesScreenshotSelectorLimit already caps the combined
selector count, so both continue to apply unchanged to CSV input.
Add tests covering --slide-id CSV parsing, whitespace/duplicate
normalization, and the >10 selector limit via CSV, mirroring the
existing --slide-number coverage.
Address review feedback:
- Fix "comma-separate" -> "comma-separated" wording in the --slide-id
flag description (CodeRabbit).
- Set LARKSUITE_CLI_CONFIG_DIR to t.TempDir() in the new screenshot
tests, per the AGENTS.md testing convention, so local configuration
state cannot leak into or be modified by the suite.
- Add a dry-run E2E test (tests/cli_e2e/slides) that pins --slide-id
CSV parsing through the built CLI binary and asserts the emitted
slide_ids request body, per the AGENTS.md dry-run E2E requirement
for shortcut flag/param changes.
- Update the lark-slides skill reference to document that --slide-id
and --slide-number both accept comma-separated values, not just
repeated flags, so agents can discover the new syntax.
Form submission writes and submits data through a public share link, an
irreversible action that should require explicit confirmation. Reclassify
the shortcut from write to high-risk-write so the runner's --yes gate fires
before execution, matching +form-delete and other high-risk base commands.
Update the lark-base skill docs (--yes on all examples, param table, tips)
and add tests pinning the confirmation gate (unit) and dry-run structure (e2e).
Co-authored-by: yballul-bytedance <273011618+yballul-bytedance@users.noreply.github.com>
* feat(apps): add design_html app type support and credential author identity
- Add design_html to appTypePolicies (same as modern_html: skip install/env-pull/skills-sync)
- Route +html-publish via policy (useTOSPublish) instead of hardcoded type check
- Parse commit_author_name/commit_author_email from +git-credential-init response
- Use server-provided author identity for repo-local git config, fallback to defaults
- Support meta_token as identifier in +get command
- Use envvars.AgentName() for source_agent in +create (reads LARKSUITE_CLI_AGENT_NAME)
- Add creative HTML guide reference skeleton and SKILL.md routing entry
- Update git-credential skill docs with new output fields
* fix(apps): unify html-publish to TOS path, add html to init skip policy
- Remove useTOSPublish policy field, html-publish always uses TOS upload
- Add html type to appTypePolicies (skip install/env-pull/skills-sync)
- Remove design_html from policies (not yet in use)
- Fix git credential dry-run test for new local_effects entry
* feat(apps): validate --app-id format to reject meta_token with resolution hint
* feat(apps): integrate creative-design skill and update skill docs
- Add creative-design skill under lark-apps/ (same level as references/)
- Update SKILL.md description with creative design trigger keywords
- Add creative design routing in development path selection table
- Add --path relative path guidance in html-publish reference
- Remove old creative-html-guide skeleton (replaced by creative-design)
* feat(apps): skip app sync for html/modern_html in +init
Add skipAppSync policy field; html and modern_html skip npx app sync
on non-empty repo path since static HTML sites don't need it.
* fix(apps): merge creative-design into html routing and add intent entry
- Merge static HTML and creative-design into one path selection row
- Add creative-design intent routing entry before html-publish
* docs(apps): add html local dev flow, unify publish link source
- Add html端到端 flow in local-dev.md (create → init → dev → release-create)
- Unify publish link source: html and full_stack both use +release-get
- Update SKILL.md routing and publish护栏 accordingly
* fix(apps): update html-publish dry-run and skill docs for TOS flow
- DryRun shows actual 3-step TOS flow (pre_release → TOS PUT → release-create)
- Skill docs: output is release_id, use +release-get to poll for online_url
- Remove references to legacy multipart upload and data.url
* TEMP: pin miaoda-cli alpha and add BOE header for testing
- Pin miaoda-cli to 0.1.24-alpha.fb2cf0a (revert to @latest before merge)
- Add x-tt-env=boe_aily_lark_cli header globally (remove before merge)
- html app-type uses --template design-html instead of --app-type (remove before merge)
* docs(apps): add creative mode link format and meta_token recognition
- Add creative mode (html) link format `https://{tenant}/page/{meta_token}` in publish护栏
- Note dev and publish URLs are the same for creative mode, unlike full_stack
- Add meta_token to app_id resolution with full link format in app_id获取
* docs(apps): route html apps through local-dev git pipeline by default
- Select dev path: html apps now default to local-dev pipeline instead of skipping local/cloud axis
- Intent routing: creative-design publishes via local-dev flow instead of +html-publish
- Remove +html-publish fallback from local-dev "when not to use" section
* docs(apps): generalize skill references to cover both html and full_stack
Remove full_stack-only wording from init, create, list, env-pull, and
release-create references since html apps now share the same local dev
and release flow.
* feat(apps): add meta_token to +get pretty output and dry-run description
* docs(apps): unify html as creative mode, fix routing and local-dev flow
- Remove "HTML" as separate dev path; html and full_stack both go through local-dev
- Intent routing: read local-dev before creative-design to establish git pipeline first
- Mark +html-publish as legacy, redirect to local-dev for creative mode
- Split html local-dev into 3 scenarios: first-time, iteration, pre-generated files
- git add . instead of selective add to capture all creative-design output files
* docs(apps): remove dev link from html-publish output, only return release-get online_url
* fix: add license header to deck-stage.js
* docs(apps): clarify dev link only for full_stack, creative mode shares dev/pub URL
* docs(apps): remove +html-publish from intent routing, description, and guardrails
All HTML apps now go through local-dev pipeline. +html-publish is deprecated.
* docs(apps): remove html-publish references from create/release-create/cloud-dev pages
html-publish is no longer the recommended path for HTML apps; all html
and full_stack apps now follow the same local-dev + release-create flow.
* fix(apps): address PR review feedback
- html-publish dry-run: register all 3 API calls (GET pre_release, PUT TOS, POST release-create) instead of hiding steps in metadata
- validateRealAppID: remove cli_ prefix check (not a valid app_id prefix)
- E2E: update git-credential dry-run to expect 4 local_effects
- E2E: update html-publish dry-run to expect GET pre_release
* fix(apps): address PR review — remove legacy multipart dead code, fix docs
- Delete html_publish_client.go and html_publish_client_test.go (legacy multipart)
- Remove runHTMLPublish, enrichHTMLPublishAPIError, buildHTMLPublishFailureHint
- Migrate tests from runHTMLPublish to prepareHTMLPublishTarball (same coverage)
- Remove cli_ prefix from validateRealAppID (not a valid app_id prefix)
- Fix html-publish.md error wording to match actual message
- Register all 3 TOS API calls in html-publish dry-run
- Update E2E tests for new dry-run contract
* fix(apps): correctly merge SKILL.md with main (role mgmt, auth wording, source boundary)
Rebuild SKILL.md from our branch version, then merge in main's additions:
- description: add HTML静态站点发布, 应用角色与成员管理, 应用角色/角色成员
- 身份与授权: use main's updated wording (no proactive re-login)
- intent routing: add +role-* row, +init refs 平台资源与应用源码边界
- 能力边界 → 平台资源与应用源码边界 (7 rules from main)
- 禁止预授权底线: add role ② and html-publish ③ clauses
* docs(apps): route legacy html-publish only for non-git html apps
* docs(apps): strengthen local-dev routing and git recovery guidance
fix:cherry-pick and resolve conflicts
* fix: gofmt apps_errors.go and apps_errors_test.go
* docs(apps): strengthen git credential recovery and add file-upload guidance
- Generalize git error recovery: any git operation failure triggers
+git-credential-init refresh, with environment analysis on failure
- Add resource file upload rule: use +file-upload instead of local
paths, base64 inlining, or git commits; files are app-scoped
* test(apps): strengthen html-publish dry-run assertions for TOS 3-step contract
* fix: 文件资源上传
* docs(apps): update creative-design skill content
* fix: re-add license header to deck-stage.js
* refactor(apps): merge system-prompt.md into SKILL.md for creative-design skill
Consolidate the thin SKILL.md wrapper and the full system-prompt.md
methodology into a single file, eliminating an unnecessary indirection.
Update references in claude.md and codex.md accordingly.
* chore: revert TEMP changes — miaoda-cli back to @latest, remove BOE header
* docs(apps): remove 可见范围 from 发布态护栏
创意模式的可见范围权限走 lark-drive 文档权限体系,而非妙搭应用
权限体系,当前的 +access-scope-set/get 无法正确管理创意模式应用
的可见范围。待文档协作支持妙搭能力后,再通过 lark-drive 域能力
引导修改。
TODO: 等文档协作支持妙搭能力后,在 skill 中加入使用文档域权限
能力修改创意模式可见范围的引导。
* docs(lark-apps): 在平台资源与应用源码边界添加路径规则,引导 agent 使用相对路径
`apps` 命令的 `--path`、`--file`、`--output` 只接受 cwd 下的相对路径,传绝对路径会报错。
* docs(lark-apps): 新增创意模式评论路由和裸 meta_token 识别引导
- 意图路由表新增创意模式应用评论,引导走 lark-drive 文档评论体系
- app_id 获取章节补充裸 meta_token 识别:非链接非 app_ 开头时尝试用 +get 解析
* refactor(apps): flatten creative-design built-in-skills into references
- Delete built-in-skills/ directory (9 nested sub-skill folders)
- Move media skill content to references/ as flat .md files
- Add assets/index.html React+Babel starter template
- Integrate publishing flow into creative-design SKILL.md
- Update harness reference docs (aily/claude/codex.md)
- Simplify lark-apps SKILL.md routing to point directly to creative-design
- Remove creative-design standalone .git directory
* refactor(apps): rename creative-design/SKILL.md to creative-design.md
Avoid being mistaken as an independent skill entry point.
Update all internal references (lark-apps routing table + 10 reference files).
* fix(apps): fail closed when queryAppType fails instead of falling back to full_stack
queryAppType now returns an error instead of silently returning "".
+init aborts if the app type cannot be determined, preventing wrong
scaffold type from being committed and pushed to the repository.
---------
Co-authored-by: zhangli <zhangli.268@bytedance.com>
* fix: converge drive delete workflow test on terminal state
* fix: narrow drive delete tolerance to the verified transient
* test: lock the delete failure guard with a subprocess contract test
* test: lock task-result and retry-exhaustion failure boundaries
A get_node success response may omit data.node/node_token (the field is
optional), so a missing token must not be read as proof of deletion.
Classify the response as same / different / unknown: only a different
non-empty node_token proves the original node is gone (move-to-drive),
while an unknown identity keeps polling in isWikiNodeDeleted and still
attempts deletion in deleteWikiNodeAndVerify instead of leaking nodes.