* feat(mail): add +share-to-chat shortcut to share emails as IM cards
Two-step API (create share token → send card) wrapped in a single
shortcut. Supports message-id/thread-id, five receive-id-type variants
(chat_id, open_id, user_id, union_id, email), and dry-run mode.
Change-Id: Ic7b8c01c0d25fef262f35be92555f1fd019bd679
Co-Authored-By: AI
* fix(mail): regenerate SKILL.md from skill-template instead of manual edit
Add missing safety rule 8 (draft link rule) to skill-template/domains/mail.md
so it survives regeneration. SKILL.md is now produced by `make gen-skills`
in the registry repo rather than hand-edited.
Change-Id: I9cf3605deae8b6de2042e40819fedc304967e78e
Co-Authored-By: AI
* fix(mail): add docstrings and use real validation path in tests
- Add Go doc comments to exported symbols for docstring coverage
- Rewrite tests to exercise MailShareToChat.Validate via RuntimeContext
instead of duplicating validation logic
- Replace hand-rolled containsStr with strings.Contains
- Add httpmock stubs for execute and error path tests
Change-Id: Ic781494f61e9e844224185844bce7b0c48e8e200
Co-Authored-By: AI
* test(mail): add dry-run E2E test for +share-to-chat
Validate request shape (method, URL, mailbox path) under --dry-run
with fake credentials. Covers message-id, thread-id, and custom
mailbox variants.
Change-Id: Iae87bf141cbe4f312d3e9b1fca4ba175052c5c35
Co-Authored-By: AI
* fix(mail): include request body and params in dry-run output
DryRun now mirrors Execute: the share-token POST shows message_id or
thread_id, and the send POST shows receive_id_type and receive_id.
E2E test updated to assert these fields. Also fix strconv.Itoa usage.
Change-Id: I00f8770fd5a12b7354986c5e5077f97cfe5d6653
* style(mail): gofmt dry-run test file
Change-Id: I47dc6a9a47252dcfb7853737f88dfdaef65a0ae7
* test(mail): assert exact API call count in dry-run test
Change-Id: I9f4a1a183b55d03f5248eb4adddfddb08037ca95
Give each AI Agent (OpenClaw, Hermes) its own lark-cli workspace so
its Feishu calls don't overwrite the developer's local config or
collide with other Agents.
lark-cli config bind [--source openclaw|hermes] [--app-id <id>]
[--identity bot-only|user-default] [--force]
Key capabilities:
- Source auto-detected from OPENCLAW_* / HERMES_* env signals; config
written to ~/.lark-cli/<agent>/, isolated per Agent.
- Two identity presets: 'bot-only' (flag-mode default) and
'user-default'. Flag mode rejects silent bot→user escalation
without --force; TUI prompts are exempt.
- Agent-friendly stdout JSON with 'identity' + 'message' for
next-step branching.
- 'config show' and 'doctor' expose the bound 'workspace'.
- OpenClaw SecretRef resolution: plain / ${VAR} / file:+JSON Pointer
/ exec:.
Wrap the POST /drive/v1/permissions/:token/members/apply endpoint as a
user-only shortcut. --token accepts either a bare token or a document
URL, with type auto-inferred from the URL path (/docx/, /sheets/,
/base/, /bitable/, /file/, /wiki/, /doc/, /mindnote/, /minutes/,
/slides/); an explicit --type always wins. --perm is limited to view or
edit; full_access is rejected client-side to match the spec.
Classifier gains two domain-specific hints for the endpoint's newly
documented error codes: 1063006 (per-user-per-document quota of 5/day
reached) and 1063007 (document does not accept apply requests — covers
disallow-external-apply, already-has-access, and unsupported-type).
test(drive): add dry-run E2E for +apply-permission
Invoke the real CLI binary via clie2e.RunCmd under --dry-run and
parse the rendered request JSON with gjson to lock in method, URL
path (including the token segment), type query parameter (auto-inferred
for docx / sheet / slides URLs, taken from explicit --type for bare
tokens), perm body field, and remark presence/omission. A separate
test asserts --perm full_access is rejected by the enum validator
before reaching the server. Fake LARKSUITE_CLI_APP_ID / APP_SECRET /
BRAND are enough because dry-run short-circuits before any API call.
Update drive coverage.md to add a row and refresh metrics.
test(drive): isolate E2E dry-run subprocess from local CLI config
Set LARKSUITE_CLI_CONFIG_DIR to t.TempDir() in both +apply-permission
dry-run tests so the subprocess can't read a developer's real
credentials/profile instead of the fake env vars the tests inject.
test(drive): add E2E case that exercises URL inference override
Previous "bare token with explicit type wins over inference" row used a
bare token, which has no URL-derived type to override. Replace it with
a /docx/ URL + --type wiki combo that actually forces the explicit flag
to win over URL inference, and add a separate bare-token row to keep
the simpler path covered. Refresh coverage.md wording to match.
* feat(doc): add pre-write semantic warnings to docs +update
Two static checks run before the MCP update-doc call:
1. replace_* + blank-line markdown: replace_range / replace_all only
swap text inside an existing block — a \n\n in the payload will
render as literal text, not a paragraph break. Hint to use
delete_range + insert_before instead.
2. Combined bold+italic emphases (***text***, **_text_**, _**text**_)
cannot round-trip through Lark and are silently downgraded to a
single emphasis. Hint to split into two separate emphases.
Both warnings go to stderr and never block the update — they inform,
not gate. Adds table-driven tests for each check plus an aggregation
test, and wires the checks into Execute right before CallMCPTool.
Closes the first batch of items from the docs +update pitfalls
review (Cases 1 and 5).
* fix(doc): exclude code regions and escaped markers from docs +update checks (#578)
* fix(doc): exclude code regions and escaped markers from docs +update checks
Addresses the three review comments on #569: the blank-line paragraph
check and the bold+italic emphasis check both operate on the raw
markdown string, so fenced code blocks / inline code spans / literal
escaped markers produce false-positive warnings on content users
expect to pass through verbatim.
Changes:
- Add proseHasBlankLine(): fence-aware detector that returns true only
when a blank line sits outside of ```...``` or ~~~...~~~ regions.
Replaces the raw strings.Contains("\n\n") check in
checkDocsUpdateReplaceMultilineMarkdown.
- Add stripMarkdownCodeRegions(): blanks out fenced code lines and
masks inline code spans (via scanInlineCodeSpans from markdown_fix.go)
with equal-length whitespace so byte offsets outside the stripped
regions are preserved.
- Add stripEscapedEmphasisMarkers(): removes "\*" and "\_" so literal
sequences like "\***text***" — which CommonMark renders as a literal
asterisk plus bold — don't match the combined bold+italic regex.
- Wire both helpers into checkDocsUpdateBoldItalic(): the regex now runs
on stripEscapedEmphasisMarkers(stripMarkdownCodeRegions(markdown)),
so code samples and escaped markers are sanitized away before
detection.
Shared fence-parsing helpers (codeFenceOpenMarker, isCodeFenceClose,
leadingRun) are kept local to this file to avoid touching files outside
the scope of the reviewed PR. If a future change wants to reuse them
across the doc package, they can be promoted then.
Tests:
- TestCheckDocsUpdateReplaceMultilineMarkdown: add 4 negative/positive
cases — blank line inside backtick and tilde fences (no flag), blank
line in prose while fence also has blanks (flag wins), fenced code
with no blank lines (no flag).
- TestCheckDocsUpdateBoldItalic: add 9 cases — ***text*** / **_text_** /
_**text**_ inside fenced code (backtick and tilde), inside inline
code spans, and escaped \***text*** / \*\*_text_\*\* (none flagged);
plus two positive cases to verify the strip doesn't over-sanitize
(real emphasis in prose still fires when inline/fenced code is nearby).
* fix(doc): close CommonMark gaps and add three more combined-emphasis shapes
Self-review of the first commit turned up three issues:
- isCodeFenceClose was strict on exact marker length. Per CommonMark
§4.5, a closing fence must be at least as long as the opener, not
exactly the same length. A 3-backtick open legitimately closed by a
4-backtick closer (used to embed triple-backticks inside the code
sample) was left open-ended, causing the rest of the document to be
treated as code and both checks to silently skip it.
- Both fence helpers accepted any amount of leading whitespace because
they ran on strings.TrimSpace(line). CommonMark allows 0..3 leading
spaces before a fence marker; 4+ spaces (or any tab in leading
position, which expands to 4 columns) makes the line indented code
block content, not a fence open/close. Indented fence-like lines now
correctly remain prose and blank lines around them are detected.
- The bold/italic check only covered three of the six documented
combined-emphasis shapes. Added ___text___, __*text*__, and
*__text__* so parity with the asterisk variants is complete. The
regex set is now table-driven (combinedEmphasisPatterns) to make
adding future shapes a one-line change.
Implementation changes:
- New fenceIndentOK(line) helper: returns (body, true) for 0..3 leading
spaces with no tabs, else (_, false). Used by both codeFenceOpenMarker
and isCodeFenceClose.
- isCodeFenceClose now counts the fence-char run and accepts any run
length >= len(marker), with trailing whitespace only.
- checkDocsUpdateBoldItalic replaced three named var regexes with a
table of six {shape, re} entries and a single early-exit loop.
- Updated docsUpdateWarnings top docstring to list all six shapes.
- Noted the known limitation of stripEscapedEmphasisMarkers around
doubled backslash escapes ("\\***text***"), which is a false negative
we accept in exchange for keeping this a simple string replace.
Test additions (docs_update_check_test.go):
- Fence close: longer-marker close correctly ends fence; real prose
blank after a longer-close fence is still detected.
- Indentation: 4-space indented fence-like line is not a fence open,
so a surrounding blank line still flags; tab-indented variant same;
3-space indented fence is still a real fence.
- New shapes: ___text___ positive + all three negative-guards (fenced
code, inline code, escaped); __*text*__ and *__text__* positive +
fenced/inline negative-guards; plus two composition tests to ensure
the strip does not over-sanitize across the six-regex alternative set.
All 53 sub-tests in this file pass; go vet and gofmt are clean.
---------
Co-authored-by: fangshuyu-768 <shuyufang768@outlook.com>
* fix(doc): address CodeRabbit review on docs +update warnings (#581)
Two CodeRabbit nits from #569:
1. Unit test hint assertion only checked for `delete_range` in the
remediation message; the companion `insert_before` half of the
guidance could regress undetected. Broaden the assertion to require
both tokens so a future edit that drops half the remediation
produces an immediate test failure.
2. No E2E coverage proved the dry-run contract in the PR description
("Not emitted in dry-run mode — kept quiet during planning"). The
helper itself is unit-tested, but nothing caught a regression where
a later refactor wired docsUpdateWarnings into the DryRun path.
Add tests/cli_e2e/docs/docs_update_dryrun_test.go:
TestDocs_UpdateDryRunSuppressesSemanticWarnings invokes
`docs +update --dry-run --mode=replace_range --markdown "***x***\n\nb"`
— an input crafted to trip BOTH pre-write warnings — and asserts
neither the "warning:" prefix, the blank-line message, nor the
combined-emphasis message appears on stdout or stderr.
Note: the file needs -f to add because .gitignore has a bare
`docs/` rule that accidentally matches tests/cli_e2e/docs/. The
existing tracked files under that directory predate the rule; new
additions have to be force-added until the ignore pattern is
narrowed. Not worth rewriting .gitignore for one file.
Verified manually that the new E2E fails cleanly when warnings are
injected into DryRun and passes again after reverting — the test has
real regression-detection power, not just a sticker.
Co-authored-by: fangshuyu-768 <shuyufang768@outlook.com>
* fix(api): add stdin and single-quote support for --params/--data on Windows (#64)
Windows PowerShell 5.x mangles JSON double-quotes when passing arguments
to native executables, causing --params and --data to fail with
"invalid JSON format". This commit adds two mitigations at the framework
level:
- stdin piping: `echo '{"k":"v"}' | lark-cli --params -` bypasses
shell argument parsing entirely and works on all platforms/shells.
- single-quote stripping: cmd.exe passes literal single quotes which
are now transparently removed before JSON parsing.
Implementation:
- New `cmdutil.ResolveInput(raw, stdin)` handles `-` (stdin), strip
surrounding `'...'`, and plain passthrough.
- `ParseJSONMap` and `ParseOptionalBody` now accept an `io.Reader` and
delegate to `ResolveInput` before JSON unmarshalling.
- `cmd/api` and `cmd/service` pass `IOStreams.In` and guard against
simultaneous stdin usage by --params and --data.
- Empty stdin is rejected with a clear error message.
Closes#64
Change-Id: If21e735d0aed5c6a2d6674c1e6c898186fca3aba
* test: add stdin e2e regression coverage
Change-Id: I4e00bf1c6b6f3259f503e3414cae10fa4b34ba75
* feat: cli e2e test framework and demo
* feat: add cli-e2e-testcase-writer skill and task case
* feat: add cli e2e config and fix test resource prefix