371 Commits

Author SHA1 Message Date
liangshuo-1
a18504b1f9 chore(release): v1.0.30 (#871)
Change-Id: Iaa769f2ddc98ece7bf36efe821d4eb192f7fc727
v1.0.30
2026-05-13 20:11:06 +08:00
shifengjuan-dev
5e0ac02f08 feat(im): add --chat-mode topic to +chat-create (#790)
Adds --chat-mode group|topic to lark-cli im +chat-create so users and AI agents can create 话题群 (topic chats) directly via the CLI. Without this, requests to create a topic chat silently fall back to a normal conversation group. Default remains group; chat_mode is now always emitted in the POST /open-apis/im/v1/chats request body.

Change-Id: I79385e2e8606f84e3f27de240d1b41037bf51261
2026-05-13 18:03:58 +08:00
aj
b0c9a4d74e fix(auth): support comma-separated --scope in auth login (#764)
`lark-cli auth login --scope "a,b"` previously sent the raw comma-joined
string to the device authorization endpoint, which treats it as a single
malformed scope and fails with:

  device authorization failed: The provided scope list contains invalid
  or malformed scopes.

OAuth 2.0 (RFC 6749 §3.3) requires space-delimited scopes on the wire,
but commas are the more natural separator for users typing on a shell
(quoting whitespace is awkward, especially for AI-agent generated
commands). Accept both: split on commas/whitespace, trim, dedupe, then
re-join with single spaces.

Also adds unit tests covering single, comma, space, mixed, dedupe, and
trailing-separator inputs.

Co-authored-by: aj <2072584+meijing0114@users.noreply.github.com>
2026-05-13 14:27:55 +08:00
JackZhao10086
ddc24fec90 fix(auth): clarify URL handling in auth messages and docs (#856) 2026-05-13 14:09:53 +08:00
liangshuo-1
25454f498b test(update): isolate stamp writes from real ~/.lark-cli/skills.stamp (#858)
Five tests in cmd/update mocked SkillsUpdateOverride to return success
and let runSkillsAndStamp call WriteStamp, but did not isolate
LARKSUITE_CLI_CONFIG_DIR. Each run clobbered the real
~/.lark-cli/skills.stamp with the mock version ("2.0.0" or "1.0.0"),
causing skillscheck to fire a misleading drift notice on every
subsequent lark-cli invocation.

Add t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) at the top of:
  - TestUpdateNpm_JSON
  - TestUpdateNpm_Human
  - TestUpdateForce_JSON
  - TestUpdateDevVersion_JSON
  - TestUpdateWindows_NpmSuccess_JSON

Scope is limited to tests that mock SkillsUpdateOverride to success;
tests that invoke real npx are pre-existing and out of scope here.

Change-Id: I7a78a6c70f276b51333253acc115e0109c01a851
2026-05-13 13:52:22 +08:00
evandance
62ff3d66a6 fix(bind): accept ~/ paths in OpenClaw secret references (#839)
OpenClaw stores secret file paths in user-authored ~/-relative form so
the configuration stays portable across machines. lark-cli config bind
previously rejected these as non-absolute, blocking users until they
rewrote the OpenClaw config with literal absolute paths.

bind now resolves ~ to the OpenClaw home directory (OPENCLAW_HOME if
set, otherwise the OS home) before the path audit runs, mirroring how
OpenClaw itself reads the same field. Cwd-relative paths and other
unsafe locations are still rejected as before.
2026-05-13 12:34:43 +08:00
liangshuo-1
ce0b68dc0e chore(release): v1.0.29 (#852) v1.0.29 2026-05-12 20:44:16 +08:00
zkh-bytedance
cc16c4d2d7 feat(whiteboard): pin whiteboard-cli to v0.2.11 in lark-whiteboard skill (#850) 2026-05-12 19:43:02 +08:00
zgz2048
1ee7f22ee5 docs: refine base analysis SOP wording (#849) 2026-05-12 17:18:05 +08:00
calendar-assistant
b612dde19e docs: update README capability descriptions (#793)
Change-Id: Ife2670e790da48b676e8f1d81db47f4b4a9e7430
2026-05-12 16:19:26 +08:00
zgz2048
4181174352 docs: refine lark-base data analysis SOP (#784)
* docs: refine lark-base data analysis SOP

* docs: clarify data-query record lookup paths

* docs: generalize data-query lookup example

* docs: clarify cloud-side query execution
2026-05-12 15:03:03 +08:00
xzcong0820
1180baac61 feat(mail): add unknown-flag fuzzy-match for lark-cli mail domain (#806)
Adds shortcuts/mail/flag_suggest.go (~120 LOC) implementing a cobra
FlagErrorFunc hook for the mail subcommand tree. On 'unknown flag: --X'
or 'unknown shorthand flag: "X" in -X', it collects flags from the
current command via cmd.Flags().VisitAll, runs bidirectional prefix
match + Levenshtein DP (threshold=max(1,len/3+1), cap 4), and returns
top-5 candidates inside the existing ErrorEnvelope JSON:

  error.type = "unknown_flag"
  error.detail.{unknown, command_path, candidates}
  error.detail.candidates[*] = {flag, shorthand, distance, reason}

Exit code stays 1 (ExitAPI), not ExitValidation - no breaking change for
CI/agent scripts that check non-zero exit. stderr switches from plain
'Error: unknown flag: --X' to JSON envelope, aligning with the existing
'errors = JSON envelope on stderr' convention; mail unknown-flag was the
last gap.

Scope is strictly the mail subcommand tree: shortcuts/register.go gains
a single 'if service == "mail" { mail.InstallOnMail(svc) }' branch
after the existing Mount loop. Other domains (calendar / im / api /
auth / ...) keep cobra's default FlagErrorFunc and unchanged plain-text
stderr behavior.

Covers:
- shortcuts/mail/flag_suggest.go      (new, ~120 LOC)
- shortcuts/mail/flag_suggest_test.go (new, 12 table-driven tests)
- shortcuts/register.go               (+3 lines after mail Mount loop)

No changes to cmd/root.go or internal/output/* - ErrDetail.Detail is
already interface{}, handleRootError already routes *ExitError via
WriteErrorEnvelope.
2026-05-12 14:28:09 +08:00
zhicong666-bytedance
db1a3fc0a6 feat(vc): add agent meeting join, leave, and events shortcuts (#824)
* feat(vc): agent join meeting basic shortcuts structure

Change-Id: Ic5d64067eb48670fa6636841cd00cbfa9b0bf3e7

* docs: add skill references for vc +meeting-join and +meeting-leave

* feat(vc): add meeting events shortcut

Add vc +meeting-events for bot meeting activity queries with page-all pagination support and tested pretty/json output.

* feat(vc): refine meeting events pagination and output

* test: add unit tests for vc +meeting-join and +meeting-leave shortcuts

* feat(vc): improve meeting events pretty timeline

* feat(vc): refine meeting events pretty output

* docs(skill): add vc meeting events shortcut guide

* docs(skill): clarify vc meeting events output guidance

* docs: clarify participant-snapshot vs meeting-events routing

* refactor: split lark-vc-agent from lark-vc

* docs: drop nonexistent workflow skill reference and fix identity

* docs: fix cross-links in lark-vc-agent references after split

* fix(vc): send meeting join password at top level

* docs: rewrite lark-vc-agent description in user-facing language

* docs: tighten lark-vc-agent description to descriptive neutral tone

* fix: use Chinese quotes in vc/vc-agent description YAML frontmatter

* docs: downgrade dry-run from mandatory to optional for vc-agent writes

* docs: clarify pretty vs json format choice by processing depth

* docs: systematic review of lark-vc-agent SKILL for clarity and precision

* feat(vc): print meeting event page token in pretty output

* docs(skill): refine vc agent meeting guidance

* revert: restore CRITICAL banner in lark-vc-agent to match repo convention

* docs: replace inaccurate no-replay warning with real social-cost risk

* docs: tighten meeting-join risk warning to single sentence

* docs: tighten vc-agent references - remove redundancy and fix vague wording

* Revert "docs: tighten vc-agent references - remove redundancy and fix vague wording"

This reverts commit 9845fc40622c65b0811da1c9ae4902434377f33e.

* docs(skill): refine vc meeting events paging guidance

* fix(vc): keep meeting event count aligned with events list

* docs(skill): tighten vc agent meeting events workflow

* refactor(vc): simplify meeting events pagination

* docs(skill): tighten vc agent meeting guidance

* docs(skill): require reading shared docs for meeting summaries

* chore(env): switch default feishu endpoints to pre

* fix(env): use feishu accounts host

* docs(vc): use explicit date in recording example

* revert(env): remove default ppe request header

* chore(env): switch default feishu endpoints to pre

* docs(skill): guide users to early-bird group on agent meeting gray miss

Teach the lark-vc-agent skill to recognize OAPI's new gray-miss signal for
the three agent meeting commands (`+meeting-join`, `+meeting-leave`,
`+meeting-events`) and route the user to the early-bird group instead of
treating it as a permission error.

When CLI stderr JSON returns `error.code=20017 / ErrNotInGray`, the agent
renders the fixed early-bird invite link
`https://go.larkoffice.com/join-chat/2f4nb0e1-fe00-4f67-bed7-25beaf533fbd`.
The user manual is intentionally not surfaced yet.

Scope-related errors still follow the existing `auth login --scope` flow
with no early-bird copy mixed in. lark-shared and other skills are not
touched, so the guidance stays scoped to the agent meeting commands only.

* chore(env): switch endpoints to boe for agent meeting gray testing

* chore(vc-agent): update gray guide and boe endpoints

* docs(vc-agent): refine gray guidance flow

* docs(vc-agent): centralize gray guidance

* fix(ci): stabilize vc output and skill frontmatter

* fix(vc): address review feedback

---------

Co-authored-by: zhaolei.vc <zhaolei.vc@bytedance.com>
Co-authored-by: renaocheng <renaocheng@bytedance.com>
2026-05-11 21:32:06 +08:00
niuchong
7c6abb3834 fix: silence misleading "skills not installed" startup notice (#801)
Remove the cold-start _notice.skills that fires whenever
~/.lark-cli/skills.stamp is missing. The stamp is written
exclusively by `lark-cli update`, so users who installed skills via
`npx skills add larksuite/cli -g` (the documented path) saw the
notice on every run despite a fully populated ~/.agents/skills/.

The version-drift notice (stamp != binary) is preserved unchanged
for users who opted into tracking by running `lark-cli update`.

- internal/skillscheck/check.go: Init returns silently on empty stamp
- internal/skillscheck/notice.go: drop dead cold-start branch in Message;
  Current field is now guaranteed non-empty
- tests updated in skillscheck package + cmd/root_integration_test.go
  to assert the new contract

No new files, no env vars, no JSON schema change. The _notice.skills
shape stays {current, target, message} — only the cold-start message
string is no longer possible.
2026-05-11 21:02:55 +08:00
liangshuo-1
4c63198237 chore(release): v1.0.28 (#830)
Change-Id: If8e5170a3abb8ef846fcb7473977e6bf8bc91767
v1.0.28
2026-05-11 20:40:32 +08:00
chenxingtong-bytedance
c0fbe54ef6 feat(lark-im): support UAT for forward and add threads.forward (#689)
- Update messages.forward identity to support `user` and `bot`
  - Add threads.forward entry under threads API resources
  - Add forward APIs -> `im:message`, `im:message.send_as_user` scope mapping

Change-Id: I2e33b0d78d72fd067ba3916095479f9b336e7eb9
2026-05-11 19:35:38 +08:00
fangshuyu-768
4ba39ef392 fix(drive): handle duplicate remote sync paths (#803) 2026-05-11 17:51:23 +08:00
shifengjuan-dev
25c72ced6f docs(im): name --query/--member-ids in +chat-search shortcut row (#812)
The +chat-search row in lark-im SKILL.md described the search as
"by keyword and/or member open_ids", which doesn't match the real
flag names (--query, --member-ids). Naming them inline avoids
agents guessing --keyword from the prose, matching the style
already used by +chat-messages-list.

Change-Id: Ife8668d9b13ee66711bc4e81a7b2bcc7f05d9586
2026-05-11 16:22:12 +08:00
SunPeiYang996
0ed63b02e4 chore(doc): inject docs scene into v2 requests (#808)
Change-Id: I4f23880e24164c8b229a5403942bfa1b7ddb0ce6
2026-05-11 14:35:00 +08:00
Yuxuan Zhao
5352e6a90a test: drop stale yes flags from e2e (#815) 2026-05-11 13:49:43 +08:00
seemslike
16f1a0f320 feat: add flag shortcuts for im (#770)
Add IM flag shortcut commands to lark-cli, enabling users to create, list, and cancel bookmarks on messages and threads via +flag-create, +flag-list, and +flag-cancel.

Change-Id: I8f87f0eadf83fb59b024a3b9fe67b23d363abe0a
2026-05-11 11:32:06 +08:00
Yuxuan Zhao
4d625420b0 test: drop stale e2e yes flags (#794) 2026-05-11 10:48:46 +08:00
liangshuo-1
4aceae9bff chore(release): v1.0.27 (#796)
Change-Id: I4004437e7dbeb195ab1133a8f7c657f9b6f835fd
v1.0.27
2026-05-09 20:35:55 +08:00
Agent Fitz ;-)
44ffa98b89 fix: Fix installation errors when PowerShell is disabled by Group Policy. (#789) 2026-05-09 16:54:51 +08:00
terry
f9792f056e docs: clarify task member id types in references (#777)
Change-Id: Icaf012238cd93eeb784014d807c12168faf0a202

Co-authored-by: tengchengwei <tengchengwei@bytedance.com>
2026-05-09 14:16:11 +08:00
mazhe-nerd
6e22a7e518 feat(config): add lark-channel as a bind source (#786) 2026-05-08 22:39:23 +08:00
liangshuo-1
29a98966a0 chore(release): v1.0.26 (#785)
Change-Id: I27dd5e9ad7dc083ab41821cfcfb12c69354fa2b0
v1.0.26
2026-05-08 19:39:26 +08:00
zgz2048
a81d07ca4f fix: clean base error detail output (#783) 2026-05-08 18:13:44 +08:00
sammi-bytedance
e754b3bc1b feat(im): add message_app_link to IM message outputs (#668)
- Assemble applinks via net/url to ensure proper encoding
- Normalize message position values across more numeric types
- Avoid leaking null message_app_link; assemble when missing
- Update unit tests to assert URL semantics and cover edge cases

Change-Id: Ic473cb563c8a648c4f6677c32b25b9f371a0f84e
2026-05-08 16:06:48 +08:00
JackZhao10086
a6de8360f0 feat(auth): add scope hint for missing authorization errors (#776)
* feat(auth): add scope hint for missing authorization errors

* fix(auth): handle existing hints in missing scope error

* refactor(auth): centralize user authorization error detection

* fix(auth): handle nil error case in IsNeedUserAuthorizationError
2026-05-08 15:23:29 +08:00
xzcong0820
88d7ec8ee7 feat(lark-mail): add data integrity and write-confirmation rules (#749)
Adds a new top-level safety section "数据真实性与操作合规" to the
lark-mail skill via the canonical generation pipeline:

  - skill-template/domains/mail.md (source) — adds the section to the
    domain introduction file that gen-skills.py renders into SKILL.md.
  - skills/lark-mail/SKILL.md (regenerated product) — produced by
    `make gen-skills project=mail` from larksuite-cli-registry against
    the modified mail.md source.

Why both files: skills/lark-mail/SKILL.md is auto-generated from
skill-template/domains/mail.md + registry-conf/skill-meta.yaml +
output/from_meta/mail.json. Editing only SKILL.md would be reverted on
the next `make gen-skills` run because SKILL.md has no AUTO-GENERATED
markers and falls into the "no markers -> overwrite whole file" branch
in scripts/gen-skills.py.

The section adds 3 hard constraints on agent behavior:
  - empty result is a valid answer; do not fabricate IDs or placeholders
  - explicit action preview before destructive write operations
    (delete / trash / batch_trash / cancel_scheduled_send / rules.*)
  - reversible modifications (label / read state / folder move) are
    exempt from the preview requirement

Addresses recurring evaluation failures (c03/c04/c06/c09/c14/c19~c24/c40)
where the agent fabricated IDs or auto-executed destructive operations.
2026-05-08 12:13:40 +08:00
syh-cpdsss
90757887b2 whiteboard-update as "write" risk (#775)
Change-Id: Iacc4d349b44337813392d75f4f0ec67718074efc
2026-05-07 22:53:37 +08:00
liangshuo-1
88d4e3bd90 chore(release): v1.0.25 (#774)
Change-Id: I9713902d6d7fdfb399e59d8ae23009789a71be3d
v1.0.25
2026-05-07 21:19:01 +08:00
MaxHuang22
7c68639b31 fix: remove misleading default value from --as flag help text (#769)
The --as flag displayed (default "bot"), (default "user"), or
(default "auto") in help text, but ResolveAs() never uses the cobra
default — it resolves identity via credential config and auto-detect.
The displayed default misled users into thinking a fixed identity was
used when --as was omitted.

Set cobra default to empty string so no (default ...) suffix appears.
Also remove "auto" from visible options since --as auto is equivalent
to omitting --as entirely.

Change-Id: I51ba550a6697eb3675a29f5cee4d0010e0a1cc16
2026-05-07 16:58:38 +08:00
zgz2048
8b80810fa0 docs: clarify base user open_id guidance (#763)
* docs: clarify base user open_id guidance

* docs: clarify base group chat id guidance
2026-05-07 12:14:03 +08:00
陈家名
eed802c814 fix: handle negative truncate lengths (#744) 2026-05-07 11:40:04 +08:00
niuchong
8f410ab140 feat: add skills version drift notice and unify update flow (#723)
Users who install or upgrade lark-cli via make install, go install, or
direct binary download end up with a binary but no AI agent skills,
degrading agent UX. This PR adds a startup-time skills version drift
notice (injected into JSON envelope _notice.skills, mirroring the
existing _notice.update pattern) and unifies lark-cli update's skills
sync across all three branches (npm / manual / already-latest) with
stamp-based dedup, so any explicit update invocation keeps skills in
sync regardless of how the binary was installed.

Changes:
- new internal/skillscheck package: notice (StaleNotice + atomic
  pending), stamp (~/.lark-cli/skills.stamp), skip (CI / DEV /
  non-release / LARKSUITE_CLI_NO_SKILLS_NOTIFIER opt-out), check
  (synchronous Init)
- cmd/root.go: rename setupUpdateNotice -> setupNotices, compose
  output.PendingNotice returning {update?, skills?}; capture
  build.Version locally before spawning the async update goroutine
- cmd/update/update.go: add runSkillsAndStamp helper with stamp-based
  dedup; rewire the three branches through shared applySkillsResult /
  emitSkillsTextHints helpers; add skills_status block to --check JSON
  output as a pure report (no side effects)
- internal/update: export IsRelease(version) bool / IsCIEnv() bool
  for cross-package reuse; refresh UpdateInfo.Message to append
  ', run: lark-cli update' so both notices recommend the same fix
- AGENTS.md: add Notification Opt-Outs section documenting
  LARKSUITE_CLI_NO_UPDATE_NOTIFIER and LARKSUITE_CLI_NO_SKILLS_NOTIFIER
- internal/binding/types.go: bump default exec-provider timeout from
  5s to 10s (out-of-scope flake fix for TestResolveExecRef_JSONResponse
  under heavy parallel test load)
2026-05-07 10:52:35 +08:00
陈家名
d9b9f094cf fix: reject invalid json pointer escapes (#741) 2026-05-06 21:54:17 +08:00
Zhang-986
b65147f208 fix: migrate task shortcut errors from bare fmt.Errorf to structured output.Errorf/ErrValidation (#740) 2026-05-06 21:45:37 +08:00
liangshuo-1
c3756f3642 chore(release): v1.0.24 (#761)
Change-Id: I248e14e1d546aa1c49bdb9f443103952488f16d7
v1.0.24
2026-05-06 20:35:36 +08:00
liangshuo-1
27a2f2758b fix(config): make agent-binding hints workspace-aware and surface user-identity risks (#728)
AI agents running inside OpenClaw / Hermes were routinely creating a parallel
app via `config init --new` instead of binding to the agent's existing app,
because every "not configured" hint and several deny errors hard-coded
`config init` regardless of workspace. Once bound, the same agents could
silently grant themselves user identity (impersonation) without the user
ever seeing a risk message in chat.

Changes:

- Introduce `core.NotConfiguredError` / `NoActiveProfileError` /
  `reconfigureHint` helpers that branch on `CurrentWorkspace()`. In agent
  workspaces they point at `lark-cli config bind --help` (a help page, not
  a ready-to-run command) so AI must read the binding workflow and confirm
  identity preset with the user before acting. In local terminals they
  preserve the previous `config init --new` guidance.

- Migrate every `config init` hint that should be workspace-aware:
  RequireConfigForProfile, default credential provider, credential provider
  fallback, secret-resolve mismatch, config show, strict-mode entry-point
  errors, default-as, profile use/rename/remove, auth list, doctor's
  config_file check (which now also wraps the OS-level "no such file"
  noise into the user-shaped "not configured" message).

- Refuse `config init` when run inside an OpenClaw / Hermes workspace by
  default; add `--force-init` for the rare case the user genuinely wants
  a parallel app. Without this guard, hint fixes were undone the moment
  AI ignored them.

- Rewrite the strict-mode deny errors in cmd/auth/login.go, cmd/prune.go,
  and internal/cmdutil/factory.go. The previous "AI agents are strictly
  prohibited from modifying this setting" terminated AI reasoning while
  providing no real gate. New errors point at `config strict-mode --help`
  with the legitimate confirmation flow and explicitly note that switching
  does NOT require re-bind. Integration test envelopes updated.

- Tighten `config bind --help` and `config strict-mode --help` to encode
  the user-confirmation discipline directly: identity preset semantics
  (bot-only vs user-default), "DO NOT switch without explicit user
  confirmation", and a cross-reference clarifying that `config bind` is
  for changing the underlying app while `config strict-mode` is the
  policy-only switch (resolves an ambiguity an audit run found).

- Surface user-identity (impersonation) risk at every config write that
  newly grants it, by reusing the canonical IdentityEscalationMessage
  string from bind_messages.go:
  - `noticeUserDefaultRisk` fires on flag-mode bind landing on
    user-default, including the first-time case `warnIdentityEscalation`
    misses (it requires a previous bot lock).
  - `setStrictMode` warns when transitioning bot → user or bot → off
    (newly permits user identity); stays quiet on narrowing changes
    and on off → user (off already permitted user).

- Add tests: notconfigured_test.go (workspace branches),
  init_guard_test.go (refuse + --force-init bypass), bind_warning_test.go
  (user-default warning fires; bot-only does not), strict_mode_warning_test.go
  (5 transitions covering both warn and no-warn paths).

Two follow-ups intentionally deferred: the keychain master-key hint at
internal/keychain/keychain.go:42 still suggests `config init` because the
keychain package can't import core (would be circular); fixing requires
either parameterizing the hint via callback or extracting workspace into
its own package. The lark-shared skill doc still tells AI to run
`config init` for first-time setup; updating the skill is in scope for
a follow-up PR.

Change-Id: I02273e044d9e061d211ceaa4f3ed5a3fb28325b3
2026-05-06 19:27:24 +08:00
JackZhao10086
15ae1fabec fix(auth): handle missing scopes and device flow improvements (#752)
* fix(auth): handle missing scopes and device flow improvements

* fix: remove redundant error return in login scope handler

* test(auth): rename test for zero interval default case

* fix: increase device code polling timeout from 180 to 600 seconds
2026-05-06 17:10:27 +08:00
wittam-01
d317493e49 fix: add url to markdown +create output (#753)
Change-Id: I4fa870415bbad76f721f8aa170180e83fd20281b
2026-05-06 16:03:33 +08:00
zgz2048
a8f078478e docs: refine field update conversion guidance (#748)
* docs: refine field update conversion guidance

* docs: refine field update conversion rules

* docs: adjust field update conversion allowlist
2026-05-06 15:32:38 +08:00
bytedance-zxy
06275415b1 feat(task): add upload task attachment shortcut (#736)
* feat(task): add upload task attachment shortcut

Change-Id: I668bf3d856baa6e35ed982a33c4bf4d03b924f4b

* feat(task): update SKILL.md adding resource_type description

Change-Id: I3ef1aba33ee22e8b03e6f59bc2fb64f55a742270
2026-05-06 14:36:41 +08:00
zgz2048
b4c9c09de0 feat(base): support batch record get and delete (#630)
* feat(base): support batch record get and delete

* fix(base): address batch record PR feedback

* docs(base): refine record skill routing

* refactor(base): use batch record get and delete only

* refactor(base): share record selection normalization

* docs(base): clarify record get field projection help
2026-05-06 14:13:22 +08:00
caojie0621
7fb71c6947 feat(sheets): add sheet management shortcuts (#722)
* feat(sheets): add sheet management shortcuts

- add +create-sheet, +copy-sheet, +delete-sheet, and +update-sheet
- cover request-shape dry-run and sheet workflow tests
- document new sheet management shortcuts in lark-sheets skill

* docs(sheets): consolidate lark-sheets reference docs
2026-05-01 15:49:24 +08:00
河伯
020aeb87ad feat(drive): pre-flight 10000-rune total cap for +add-comment reply_elements (#605)
* feat(drive): pre-flight per-text-element byte limit for +add-comment

The open-platform comment API returns an opaque [1069302] Invalid or
missing parameters whenever a single reply_elements[i] text exceeds
its implicit byte budget. The error does not name which element failed
or that length is the cause, so callers resort to binary-search
debugging.

Empirically: Chinese text up to ~80 chars (~240 bytes) lands; ~130
chars (~390 bytes) fails. Set the pre-flight limit to 300 bytes which
sits safely inside the known-good zone.

- parseCommentReplyElements now rejects any text element whose UTF-8
  byte length exceeds 300, with an ExitError naming the element index
  (#N, 1-based) and both the rune and byte counts, plus an ErrWithHint
  recommending the correct remediation (split into multiple text
  elements — the comment UI renders them as one contiguous comment).
- The previous 1000-rune check is removed: it was too lenient (a
  Chinese text under that cap would still fail server-side).
- skills/lark-drive/references/lark-drive-add-comment.md documents
  the per-element limit and the correct split pattern so agents
  avoid constructing oversized single elements upstream.

Addresses Case 12 in the 踩坑列表 doc.

* fix(drive): correct +add-comment hint to match actual escape coverage

`escapeCommentText` only expands `<` and `>` (each → 4 bytes via
`&lt;` / `&gt;`); `&` is intentionally left as-is. Both the over-limit
hint and the inline comment in `parseCommentReplyElements` previously
claimed `&` was also escaped, with a "4-5 bytes each" range that
implicitly assumed `&amp;` (5 bytes) — a string of 300 `&` chars
would actually fit in the budget, but a user reading the hint would
think otherwise and pre-emptively split it.

Code:
- Hint string ends with `Note: '<' and '>' are HTML-escaped and
  counted in their escaped form (4 bytes each).` (was: included `&`
  and "4-5 bytes")
- Inline comment above the budget check now matches:
  `escapeCommentText only expands '<' and '>' (each becomes 4 bytes:
  &lt; / &gt;); '&' is intentionally left as-is.`

Tests (regression):
- New `300 ampersands accepted (escapeCommentText leaves '&' as-is)`
  subtest pins that 300 `&` chars stay within budget. Without the fix
  this also passed (function was always correct), but the hint was
  lying — the test pins the budget contract loud and clear.
- New `TestParseCommentReplyElementsHintMatchesEscape` asserts the
  hint string itself: must mention `'<' and '>'` / `4 bytes`, must NOT
  mention `'&'` / `&amp;` / `4-5 bytes`. Catches a future drift if
  `escapeCommentText` is changed without updating the hint, or
  vice-versa.

The skill md (`skills/lark-drive/references/lark-drive-add-comment.md`)
already had the right wording (`每个 < 或 > 占 4 字节`), so it was the
in-Go strings that drifted; this commit aligns code with doc.

* fix(drive): rewrite +add-comment length cap to match real server behavior

The original PR set a 300-byte per-element pre-flight check, justified
by the empirical pattern "~80 Chinese chars succeeds, ~130 fails". A
fresh round of probing the live `/open-apis/drive/v1/files/{token}/
new_comments` endpoint with a real docx shows that pattern does not
reproduce, and the actual contract is very different:

  - 10000 ASCII / 10000 Chinese / 10000 '<' (escaped to 40000 bytes)
    in a single text element: all OK
  - 10001 of any of the above in a single text element: [1069302]
  - 5000 + 5000 across two text elements (total 10000): OK
  - 5000 + 5001 across two text elements (total 10001): [1069302]
  - 4000 + 4000 + 4000 across three (total 12000): [1069302]

Two consequences:

1. The cap is *10000 runes total across all reply_elements text*, not
   300 bytes per element. The old check rejected legitimate input
   anywhere from ~100 to 10000 Chinese chars (≈100x too aggressive).

2. The hint that recommended "split the content across multiple
   {\"type\":\"text\",\"text\":\"...\"} elements" was actively wrong —
   splitting doesn't bypass a total cap. A user told to split a
   10001-char message into 5000+5001 hits the same opaque [1069302].

This commit:

- Replaces `maxCommentTextElementBytes = 300` with
  `maxCommentTotalRunes = 10000`. The constant's doc comment records
  the probe matrix above so future maintainers know how it was
  derived.
- Switches the measurement from `len(escapeCommentText(input.Text))`
  to `utf8.RuneCountInString(input.Text)`. Server counts raw runes;
  byte width and post-escape form are irrelevant. The escape itself
  still happens — `<` and `>` still get rendered literally — but it
  no longer participates in the length check.
- Tracks a running `totalRunes` across the whole reply_elements array
  and bails at the first element that pushes the cumulative total
  over the 10000-rune budget, with index reporting that points at the
  offending element.
- Rewrites the over-cap hint to (a) name the actual 10000-rune budget,
  (b) explicitly say splitting does NOT help, (c) drop the wrong
  "comment UI still renders them as one contiguous comment" framing
  that implied splitting was a workaround.
- Adds a `TestParseCommentReplyElementsHintForbidsSplitAdvice`
  watchdog that fails if any future drift puts the discredited split
  advice back into the hint.

Tests: 11 cases on TestParseCommentReplyElementsTextLength covering
single-element boundary (ASCII / Chinese / angle brackets at exactly
10000 and at 10001), multi-element total cap (5000+5000 OK, 5000+5001
rejected with index pointing at element #2), early-element-overshoot
indexing (first element at 10001 reports index #1, not the trailing
element), and mention_user not double-counting toward the cap.

Skill md updated: removes the 300-byte / "split into multiple
elements" advice; documents the 10000-rune total cap with a note that
the schema currently advertises 1-1000 chars and is out of date,
plus a procedure for re-probing if the server-side limit ever moves.

Manual API verification: rebuilt binary and posted comments at
boundary lengths — all OK cases (100 / 5000 / 10000 chars, 5000+5000
split) accepted by server; over-cap cases (10001 / 10100 single, and
5000+5001 split) rejected by the new pre-flight before reaching the
network.

---------

Co-authored-by: fangshuyu <fangshuyu@bytedance.com>
2026-04-30 18:52:44 +08:00
liangshuo-1
686c91dc71 chore(release): v1.0.23 (#737)
Change-Id: I48f780acac9731585aeec0a51f5b403a00804dbc
v1.0.23
2026-04-30 18:04:10 +08:00
河伯
cfd89e0e28 feat(doc): warn when callout uses type= without background-color (#467)
* feat(doc): expand callout type= shorthand into background-color and border-color

When users write <callout type="warning" emoji="📝"> without an explicit
background-color, the Feishu doc renders the block with no color. This
commit adds fixCalloutType() which maps the semantic type= attribute to
the corresponding background-color/border-color pair accepted by create-doc.

- warning → light-yellow/yellow
- info/note → light-blue/blue
- tip/success/check → light-green/green
- error/danger → light-red/red
- caution → light-orange/orange
- important → light-purple/purple

Explicit background-color or border-color attributes are always preserved.
The fix is applied via prepareMarkdownForCreate() in both +create and
+update paths, and also inside fixExportedMarkdown() for round-trip fidelity.

* refactor(doc): replace silent callout type→color injection with hint output

Per reviewer feedback (SunPeiYang996), silently rewriting user Markdown is
the wrong layer for this adaptation. The type→color mapping is not part of
the Feishu spec, and covert transforms make debugging harder.

Replace fixCalloutType() (which rewrote the Markdown) with WarnCalloutType()
which leaves the Markdown unchanged and instead writes a hint line to stderr
for each callout tag that has type= but no background-color, telling the user
the recommended explicit attributes to add:

  hint: callout type="warning" has no background-color; consider: background-color="light-yellow" border-color="yellow"

Also fixes CodeRabbit feedback: the type= regex now accepts both single-quoted
and double-quoted attribute values (type='warning' and type="warning").

* fix(doc): harden background-color detection in WarnCalloutType

CodeRabbit flagged that the previous strings.Contains(attrs,
"background-color=") check missed forms like 'background-color =
"light-red"' with whitespace around the equals sign. Replace with a
regex that tolerates optional whitespace, and add a regression test.

* fix(doc): close real review gaps left over after rebase

PR #467's review thread had three substantive comments
(`fangshuyu-768`, 2026-04-21) that the prior reply messages claimed
were fixed in commit 7d4b556 — but that commit no longer exists on the
branch (lost in a rebase / squash), and the head still ships the
original buggy code. This commit makes the fixes real.

Three behavior fixes in shortcuts/doc/markdown_fix.go:

1. (#5) Tighten the type= and background-color= regex anchors. \b sits
   at any word/non-word boundary, and `-` is a non-word char, so
   `\btype=` also matched the suffix of `data-type=` — a tag like
   `<callout data-type="warning">` would emit a bogus light-yellow
   hint. Switched both regexes to `(?:^|\s)…` so a real attribute
   separator is required. The same anchor on background-color closes
   the symmetric case where a `data-background-color=` attribute
   would silently suppress the real hint.

2. (#4) WarnCalloutType is now a fence-aware line walker. Previously
   the regex ran over the entire markdown body, so a callout sample
   inside a documentation code fence (```markdown … ```) would
   generate a phantom stderr hint every time the docs mentioned the
   feature. The walker tracks fence state via the existing
   codeFenceOpenMarker / isCodeFenceClose helpers from
   docs_update_check.go, which handle both backtick and tilde fences
   per CommonMark §4.5.

3. (#3) Drop the ReplaceAllStringFunc-as-iterator pattern. The
   previous code routed callout iteration through a rewrite primitive
   whose rebuilt-string return value was discarded, then ran the same
   regex a second time inside the callback to recover the capture
   groups. New scanCalloutTagsForWarning helper uses
   FindAllStringSubmatch — one pass, no thrown-away allocation,
   intent matches the surface (read-only scan, not a mutator).

Tests: 5 new TestWarnCalloutType subtests pin each contract:

- data-type attribute does not trigger hint (#5)
- data-background-color does not suppress hint (#5, symmetric)
- callout inside backtick fence emits no hint (#4)
- callout inside tilde fence emits no hint (#4)
- callout after fence close still emits hint (#4, fence-state reset)

All 14 TestWarnCalloutType cases pass; go vet / golangci-lint
--new-from-rev=origin/main both clean.
2026-04-30 17:51:08 +08:00