--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(slides): add layout density lint for sparse/empty containers
Extend the XML layout lint into a single release gate for Slides XML:
- Add blank_slide, sparse_container_content, and sparse_slide_content
detection, using visibility- and coverage-aware heuristics (alpha
filtering, image-overlay/layout-panel exemptions, similar-short-card
grouping) to avoid flagging intentional whitespace or background
panels
- Broaden out-of-canvas detection from table/chart/text-only to every
element kind, with rotation-aware bounding boxes and geometry
extraction for icon/line/polyline
- Restructure output to schema v2.0: every issue carries rule
(id/name/comparison/threshold), measurement, related_objects, and
hint; summary gains status/release_ready/screenshot_review_required
- Change CLI exit-code semantics so only errors block (exit 1);
warning-only output still exits 0 to let downstream screenshot review
proceed
- Harden XML attribute parsing (single/double-quoted and spaced
attributes, self-closing tags no longer bleeding content into the
next element) and fix edge cases surfaced during review
(image-overlay coverage ratio, invisible container/panel exemptions,
bbox_overlap measurement consistency, background-only slide bypass,
invisible short-card peers)
- Update SKILL.md, validation-checklist.md, and troubleshooting.md to
match the new gate; add regression tests for the new rules and fixes
* docs(base): clarify complete and partial updates
Consolidate the update rule introduced in #1879 and make the command-contract boundary explicit. Full-update commands must use trusted current configuration for the first actual request, while delta commands should send the smallest legal payload.
* docs(base): clarify full-update state preservation
Address review feedback by requiring unchanged writable configuration to remain intact, except when the requested update makes a setting inapplicable.
* docs(base): strengthen update contract guidance
From EVAL-07-22-02-53 (42 convos), agents fell back to the full XSD for:
- shape type enum + presetHandlers (rounded corners)
- polyline (bounding-box positioning, required border, connector type)
- table merged cells (colspan / rowspan)
Add compact coverage for each, sized to real usage (shape/polyline type
lists trimmed to what actually appears in generations). Chart gaps deferred.
paas_storage AppFileListForOpenAPI rejects page_size > 200 at the inner
checkMaxKeys guard with ErrInvalidRequest("maxKeys not in range (0, 200]").
Previously the CLI forwarded any --page-size straight to the API, so
--page-size 500 produced an opaque server error round-trip.
Add a client-side Validate check bounding --page-size to [1, 200] (aligned
with the existing validateAppsPageSize precedent in the observability
commands): out-of-range values now fail fast with a typed validation error
and never hit the network. The server tolerates page_size <= 0 by defaulting
to 20, but the CLI default is already 20 and an explicit < 1 is a user error,
so we reject it for a clearer message, consistent with other list commands.
Update the flag description and the lark-apps-file skill reference to
document the 1..200 range, and cover the boundaries in unit tests.
Card header icon documentation contained invalid tokens (e.g., mail_colorful, approve_colorful) that do not render, and icon guidance lacked precise token enumeration, causing LLM to guess or fabricate icon tokens. This PR replaces
examples with valid tokens and adds a definitive colorful icon reference table.
The API always returns presentation/slide XML as a single unindented
line, which is unreadable for decks with many shapes (e.g. PPTX-imported
presentations). slides +xml-get now formats it on the surfaces meant for
a human or a line tool to read:
- --raw and --output reindent the XML with etree so each structural
element (presentation/slide/shape/style/...) sits on its own line.
Reformatting never recurses into schema-mixed text-bearing elements
(p, span, strong, em, u, del, a, shadow, outline, chartTitle,
chartSubTitle), so rich-text content stays exactly as parsed. CDATA
sections and the schema's  /	/ / whitespace character
references (decimal, hex, and zero-padded) are preserved through the
parse/write pass instead of being silently normalized away. There is
no flag to disable this formatting.
- The default JSON envelope returns the server's XML verbatim: it is
never parsed, so it stays a byte-exact copy of the API response, at
no reformatting cost and with no failure mode on this path.
- If reformatting --raw/--output content fails (non-strict XML from the
service), the command falls back to the original content, prints a
warning to stderr, and reports pretty_printed: false in --output file
metadata.
Adds github.com/beevik/etree as a direct dependency.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
When creating an event as a bot, resolve the bot's own open_id via
/bot/v3/info and add it to the attendee list, mirroring how a user is
auto-joined to their own events; warn and proceed without it if the
lookup fails. Also note in the +create skill doc that the user-search
API is user-only, so resolving a name to open_id needs --as user.
* refactor: add output emitter contract and differential harness
Introduce a leaf Emitter in internal/output that composes the existing
output primitives (content-safety scan, envelope, jq, format rendering,
notice) behind a single command-scoped port. The emitter is unwired: no
production caller is migrated, so CLI output stays byte-for-byte unchanged.
A differential test harness drives the real legacy entry points
(RuntimeContext.Out/OutRaw/OutFormat/..., WriteSuccessEnvelope and the
pagination formatter) and asserts byte-identical stdout/stderr plus typed
errors, locking behavior before later slices migrate callers.
* refactor: tighten emitter API and cover pagination with real tests
- split Emitter.Success/PartialFailure and drop EmitOptions.OK so a
missing ok flag can no longer silently emit ok:false
- give StreamPage its own StreamOptions (format + pretty) instead of
reusing EmitOptions, making "jq needs aggregation" a compile-time fact
- pin the Emitter jq-error contract (returns error, writes no stderr);
the caller adapter re-emits the legacy stderr line on migration
- add in-package tests driving the real apiPaginate/servicePaginate over
a mock transport: multi-page aggregation, empty-result fallback,
MarkRaw handling, and the business-error raw-response red line
* test: use standard TestFactory harness for pagination tests
Replace the hand-rolled RoundTripper + APIClient construction in the
apiPaginate/servicePaginate tests with cmdutil.TestFactory and its
httpmock.Registry, and isolate LARKSUITE_CLI_CONFIG_DIR to t.TempDir(),
matching the repo's standard HTTP-mocked test convention. Assertions and
coverage (multi-page aggregation, empty-result fallback, MarkRaw, and the
business-error raw-response red line) are unchanged.
* refactor: route success output through the single Emitter port
Migrate the success-output surfaces onto internal/output's Emitter,
byte-for-byte identical (proven by frozen golden diffs and the real
paginate/HandleResponse tests):
- RuntimeContext.Out/OutRaw/OutFormat/OutFormatRaw/OutPartialFailure now
build an Emitter and call Success/PartialFailure; emit and outFormat are
removed. An adapter maps the returned error back to the legacy
outputErrOnce / jq-error stderr / exit-code behavior.
- WriteSuccessEnvelope degrades to a thin Emitter.Success delegate; its 8
callers are unchanged.
- apiPaginate/servicePaginate stream pages via Emitter.StreamPage; the
aggregate and business-error raw-response branches are untouched.
- HandleResponse routes its non-JSON structured-response branch through
Emitter.Success.
Frozen golden fixtures replace the runtime legacy oracles so the
differential harness cannot go self-referential after migration.
* fix: keep _notice on struct payloads in Emitter's unknown-format fallback
printLegacyDataJSON now normalizes via toGeneric first (matching FormatValue), so a struct / named-map payload retains its injected _notice on the unknown-format -> JSON fallback rather than dropping it silently. Add a regression test that fails against the pre-fix path.
* refactor: make the Emitter own write failures and stop mutating inputs
Route every Emitter stdout path through a render-to-buffer-then-copy helper so a marshal/render failure leaves stdout empty and surfaces a typed internal error (with cause), and a stdout write failure is propagated instead of silently swallowed. Leaf writers gain error-returning Write* cores; the legacy Print*/FormatValue wrappers keep their exact behavior for unmigrated callers.
- handleEmitterError now captures every error, not only the jq/safety branches; flip OutRaw's write-error test to assert propagation.
- Clone the map before injecting _notice so a caller's payload is never mutated and an existing _notice is never overwritten.
- Preserve jq's own typed error (validation/api) on a bad expression or runtime failure; only wrap genuine stdout write failures.
- Split tests: normative emitter_contract_test.go vs frozen emitter_legacy_compat_test.go (base SHA recorded, self-update env vars removed).
* fix: satisfy license-header and forbidigo lint on the emitter changes
- Move the base-SHA note below the copyright header in the renamed legacy-compat test so the license-header check sees a valid header at the top.
- Route the leaf wrappers' marshal/format stderr messages through a single legacyStderrf helper (one //nolint:forbidigo) instead of bare os.Stderr, preserving exact legacy behavior for unmigrated direct callers while passing forbidigo; drop the now-unused os imports.
* fix: stop legacy CSV wrappers reporting write failures to stderr
Align FormatAsCSV/FormatAsCSVPaginated and FormatValue/FormatPage's CSV branch with the other leaf wrappers: report only marshal failures, swallow write failures. Previously they emitted a 'csv write error' for the (empty) line and the JSON-fallback write failures that the pre-refactor code ignored, and mislabeled a JSON write failure as a CSV one. Failure-path only; success output is unchanged (golden double-diff still byte-for-byte).
* 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>