feat(lingo): add Feishu dictionary shortcuts and skill#854
Conversation
|
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (26)
📝 WalkthroughWalkthroughThis PR adds a Lingo (Feishu Baike) domain: six CLI shortcuts (+search, +match, +get, +create, +update, +delete) with validations and API wiring, unit and e2e dry-run tests, skill documentation, a registry entry, and registration wiring. ChangesLingo Enterprise Dictionary Service
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Introduces a new `lingo` service covering the six Feishu Lingo /
百科 dictionary entry endpoints as shortcuts, plus the accompanying
lark-lingo skill with per-verb reference docs.
Shortcuts (shortcuts/lingo/):
+search POST /lingo/v1/entities/search read
+match POST /lingo/v1/entities/match read
+get GET /lingo/v1/entities/:id read
+create POST /lingo/v1/entities write (bot-only)
+update PUT /lingo/v1/entities/:id write (bot-only, full overwrite)
+delete DELETE /lingo/v1/entities/:id high-risk-write (bot-only)
Each shortcut has validate / dry-run / execute hooks and unit tests
covering flag validation, dry-run payload, successful execute, and
API-error paths. Dry-run e2e tests added under tests/cli_e2e/lingo/.
The skill (skills/lark-lingo/) follows the lark-markdown pattern:
concise SKILL.md index + one references/lark-lingo-<verb>.md per
shortcut. References capture the real-world gotchas we hit during
integration:
* write endpoints reject user_access_token (99991668); bot-only
* bot scope is configured in the developer console, not via
`auth login --scope` (which only affects user scope)
* +update is PUT full-body overwrite — read current via +get
and merge before writing
* +create entries enter the review queue unless the app has
baike:entity:exempt_review (self-built apps only)
Also:
* internal/registry/service_descriptions.json: add lingo entry
* shortcuts/register.go: register lingo.Shortcuts()
* README.md / README.zh.md: new Lingo row in both feature and
skill tables
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@shortcuts/lingo/helpers_test.go`:
- Around line 21-29: The shared test helper lingoTestConfig does not isolate
config state; update the lingoTestConfig(t *testing.T) helper to call
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) (before returning the
*core.CliConfig) so all lingo shortcut tests use a unique temporary config
directory; keep the rest of the function (suffix computation and returned
core.CliConfig) unchanged.
In `@shortcuts/lingo/lingo_entity_delete.go`:
- Line 25: The command registration currently allows both user and bot auth via
AuthTypes: []string{"user", "bot"} in lingo_entity_delete.go; change AuthTypes
to only permit bot (e.g., AuthTypes: []string{"bot"}) so the +delete shortcut is
bot-only; update any related validation or tests that assume user-level access
to this shortcut to reflect the new bot-only restriction.
In `@shortcuts/lingo/lingo_entity_get.go`:
- Around line 30-45: In the Validate function for lingo_entity_get (the Validate
closure that checks runtime.Str("entity-id")), after the existing control-char
checks for runtime.Str("provider") and runtime.Str("outer-id"), add a
paired-presence check so that provider and outer-id must both be set or both be
empty; if one is set and the other is empty return a validation error (use the
same error-return pattern as other checks) referencing the "provider" and
"outer-id" fields so the user gets an immediate, clear message. Ensure you still
call validate.RejectControlChars for each non-empty value before this paired
check.
In `@skills/lark-lingo/references/lark-lingo-get.md`:
- Around line 13-15: The example for the +get command incorrectly passes an
empty required flag (--entity-id ""), so update the snippet in lark-lingo-get.md
to show a valid entity id or remove the flag entirely; specifically change the
example invocation of the +get command to include a realistic placeholder for
--entity-id (e.g., --entity-id "ENTITY-123" or similar) while keeping the rest
(--provider "myhr" --outer-id "EMP-001") so the +get usage and the --entity-id
requirement are clear.
In `@tests/cli_e2e/lingo/lingo_dryrun_test.go`:
- Around line 88-111: Update the dry-run tests to include the required bot
identity flag by adding "--as", "bot" to the Args slice in
TestLingo_CreateDryRun, TestLingo_UpdateDryRun, and TestLingo_DeleteDryRun; then
enforce the documented requirement in the CLI shortcuts by adding validation
inside the Validate callback of the shortcut implementations (functions in
lingo_entity_create.go, lingo_entity_update.go, lingo_entity_delete.go) to
reject non-bot auth types—check the parsed auth type (the same field used now
that allows "user" or "bot") and return a validation error unless it equals
"bot".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5735c02a-ad28-49ed-b19f-4407d609ecab
📒 Files selected for processing (26)
README.mdREADME.zh.mdinternal/registry/service_descriptions.jsonshortcuts/lingo/helpers_test.goshortcuts/lingo/lingo_entity_create.goshortcuts/lingo/lingo_entity_create_test.goshortcuts/lingo/lingo_entity_delete.goshortcuts/lingo/lingo_entity_delete_test.goshortcuts/lingo/lingo_entity_get.goshortcuts/lingo/lingo_entity_get_test.goshortcuts/lingo/lingo_entity_match.goshortcuts/lingo/lingo_entity_match_test.goshortcuts/lingo/lingo_entity_search.goshortcuts/lingo/lingo_entity_search_test.goshortcuts/lingo/lingo_entity_update.goshortcuts/lingo/lingo_entity_update_test.goshortcuts/lingo/shortcuts.goshortcuts/register.goskills/lark-lingo/SKILL.mdskills/lark-lingo/references/lark-lingo-create.mdskills/lark-lingo/references/lark-lingo-delete.mdskills/lark-lingo/references/lark-lingo-get.mdskills/lark-lingo/references/lark-lingo-match.mdskills/lark-lingo/references/lark-lingo-search.mdskills/lark-lingo/references/lark-lingo-update.mdtests/cli_e2e/lingo/lingo_dryrun_test.go
| func lingoTestConfig(t *testing.T) *core.CliConfig { | ||
| t.Helper() | ||
| suffix := strings.NewReplacer("/", "-", " ", "-").Replace(strings.ToLower(t.Name())) | ||
| return &core.CliConfig{ | ||
| AppID: "test-lingo-" + suffix, | ||
| AppSecret: "secret-lingo-" + suffix, | ||
| Brand: core.BrandFeishu, | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Set LARKSUITE_CLI_CONFIG_DIR in the shared test config helper.
Adding it here keeps all lingo shortcut tests consistently isolated.
Suggested fix
func lingoTestConfig(t *testing.T) *core.CliConfig {
t.Helper()
+ t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
suffix := strings.NewReplacer("/", "-", " ", "-").Replace(strings.ToLower(t.Name()))
return &core.CliConfig{As per coding guidelines: Use t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) to isolate config state in tests.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func lingoTestConfig(t *testing.T) *core.CliConfig { | |
| t.Helper() | |
| suffix := strings.NewReplacer("/", "-", " ", "-").Replace(strings.ToLower(t.Name())) | |
| return &core.CliConfig{ | |
| AppID: "test-lingo-" + suffix, | |
| AppSecret: "secret-lingo-" + suffix, | |
| Brand: core.BrandFeishu, | |
| } | |
| } | |
| func lingoTestConfig(t *testing.T) *core.CliConfig { | |
| t.Helper() | |
| t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) | |
| suffix := strings.NewReplacer("/", "-", " ", "-").Replace(strings.ToLower(t.Name())) | |
| return &core.CliConfig{ | |
| AppID: "test-lingo-" + suffix, | |
| AppSecret: "secret-lingo-" + suffix, | |
| Brand: core.BrandFeishu, | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@shortcuts/lingo/helpers_test.go` around lines 21 - 29, The shared test helper
lingoTestConfig does not isolate config state; update the lingoTestConfig(t
*testing.T) helper to call t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
(before returning the *core.CliConfig) so all lingo shortcut tests use a unique
temporary config directory; keep the rest of the function (suffix computation
and returned core.CliConfig) unchanged.
| Description: "Delete a dictionary entry by entity_id (irreversible; requires --yes)", | ||
| Risk: "high-risk-write", | ||
| Scopes: []string{"baike:entity"}, | ||
| AuthTypes: []string{"user", "bot"}, |
There was a problem hiding this comment.
Restrict +delete to bot auth.
This command is a write/high-risk operation but currently allows user auth. That contradicts the bot-only write contract described for Lingo write endpoints.
Suggested fix
- AuthTypes: []string{"user", "bot"},
+ AuthTypes: []string{"bot"},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| AuthTypes: []string{"user", "bot"}, | |
| AuthTypes: []string{"bot"}, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@shortcuts/lingo/lingo_entity_delete.go` at line 25, The command registration
currently allows both user and bot auth via AuthTypes: []string{"user", "bot"}
in lingo_entity_delete.go; change AuthTypes to only permit bot (e.g., AuthTypes:
[]string{"bot"}) so the +delete shortcut is bot-only; update any related
validation or tests that assume user-level access to this shortcut to reflect
the new bot-only restriction.
| Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { | ||
| if err := validate.RejectControlChars(runtime.Str("entity-id"), "entity-id"); err != nil { | ||
| return err | ||
| } | ||
| if v := runtime.Str("provider"); v != "" { | ||
| if err := validate.RejectControlChars(v, "provider"); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| if v := runtime.Str("outer-id"); v != "" { | ||
| if err := validate.RejectControlChars(v, "outer-id"); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| return nil | ||
| }, |
There was a problem hiding this comment.
Enforce --provider and --outer-id as a pair in Validate.
Right now one-sided input passes local validation and fails later at API time. Rejecting it early gives clearer UX and matches the documented contract.
Suggested fix
Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
+ provider := runtime.Str("provider")
+ outerID := runtime.Str("outer-id")
+ if (provider == "") != (outerID == "") {
+ return fmt.Errorf("--provider and --outer-id must be used together")
+ }
if err := validate.RejectControlChars(runtime.Str("entity-id"), "entity-id"); err != nil {
return err
}
- if v := runtime.Str("provider"); v != "" {
+ if v := provider; v != "" {
if err := validate.RejectControlChars(v, "provider"); err != nil {
return err
}
}
- if v := runtime.Str("outer-id"); v != "" {
+ if v := outerID; v != "" {
if err := validate.RejectControlChars(v, "outer-id"); err != nil {
return err
}
}
return nil
},🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@shortcuts/lingo/lingo_entity_get.go` around lines 30 - 45, In the Validate
function for lingo_entity_get (the Validate closure that checks
runtime.Str("entity-id")), after the existing control-char checks for
runtime.Str("provider") and runtime.Str("outer-id"), add a paired-presence check
so that provider and outer-id must both be set or both be empty; if one is set
and the other is empty return a validation error (use the same error-return
pattern as other checks) referencing the "provider" and "outer-id" fields so the
user gets an immediate, clear message. Ensure you still call
validate.RejectControlChars for each non-empty value before this paired check.
| # 按外部系统(provider + outer_id)查找对应词条 | ||
| lark-cli lingo +get --entity-id "" --provider "myhr" --outer-id "EMP-001" | ||
|
|
There was a problem hiding this comment.
Fix the example that passes an empty required --entity-id.
+get is documented as requiring --entity-id, so --entity-id "" is misleading and likely invalid in practice.
Suggested doc fix
-# 按外部系统(provider + outer_id)查找对应词条
-lark-cli lingo +get --entity-id "" --provider "myhr" --outer-id "EMP-001"
+# 按外部系统信息补充查询(仍需 entity_id)
+lark-cli lingo +get --entity-id "enterprise_xxxx" --provider "myhr" --outer-id "EMP-001"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # 按外部系统(provider + outer_id)查找对应词条 | |
| lark-cli lingo +get --entity-id "" --provider "myhr" --outer-id "EMP-001" | |
| # 按外部系统信息补充查询(仍需 entity_id) | |
| lark-cli lingo +get --entity-id "enterprise_xxxx" --provider "myhr" --outer-id "EMP-001" | |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/lark-lingo/references/lark-lingo-get.md` around lines 13 - 15, The
example for the +get command incorrectly passes an empty required flag
(--entity-id ""), so update the snippet in lark-lingo-get.md to show a valid
entity id or remove the flag entirely; specifically change the example
invocation of the +get command to include a realistic placeholder for
--entity-id (e.g., --entity-id "ENTITY-123" or similar) while keeping the rest
(--provider "myhr" --outer-id "EMP-001") so the +get usage and the --entity-id
requirement are clear.
| func TestLingo_CreateDryRun(t *testing.T) { | ||
| setDryRunConfigEnv(t) | ||
| ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | ||
| t.Cleanup(cancel) | ||
|
|
||
| result, err := clie2e.RunCmd(ctx, clie2e.Request{ | ||
| Args: []string{ | ||
| "lingo", "+create", | ||
| "--main-key", "KYC", | ||
| "--aliases", "Know Your Customer", | ||
| "--description", "AML monitoring concept", | ||
| "--dry-run", | ||
| }, | ||
| }) | ||
| require.NoError(t, err) | ||
| result.AssertExitCode(t, 0) | ||
|
|
||
| out := result.Stdout | ||
| assert.True(t, strings.Contains(out, "/open-apis/lingo/v1/entities"), "dry-run should contain API path, got: %s", out) | ||
| assert.True(t, strings.Contains(out, "POST"), "dry-run should be POST, got: %s", out) | ||
| assert.True(t, strings.Contains(out, "main_keys"), "dry-run body should contain main_keys, got: %s", out) | ||
| assert.True(t, strings.Contains(out, "Know Your Customer"), "dry-run should contain alias, got: %s", out) | ||
| assert.True(t, strings.Contains(out, "AML monitoring concept"), "dry-run should contain description, got: %s", out) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check if lingo +create validates --as bot requirement
# Search for validation logic in the create shortcut implementation
rg -n -A10 -B2 'func.*Validate' shortcuts/lingo/lingo_entity_create.go
# Look for bot identity checks
rg -n 'as.*bot|bot.*identity|RequireBot' shortcuts/lingo/lingo_entity_create.goRepository: larksuite/cli
Length of output: 39
🏁 Script executed:
# First, let's find where lingo shortcuts are located
find . -type f -name "*lingo*" | grep -E '\.(go|md)$' | head -20Repository: larksuite/cli
Length of output: 922
🏁 Script executed:
# Search for lingo shortcut definition or implementation
rg -l "lingo" --type go | grep -i shortcutRepository: larksuite/cli
Length of output: 630
🏁 Script executed:
# Look for the lingo create operation definition
rg -n "create.*lingo|lingo.*create" --type go | head -10Repository: larksuite/cli
Length of output: 124
🏁 Script executed:
# Check the lingo_entity_create.go file for validation logic
cat -n shortcuts/lingo/lingo_entity_create.go | head -100Repository: larksuite/cli
Length of output: 4159
🏁 Script executed:
# Search for Validate function and bot identity checks in the create file
rg -n "Validate|RequireBot|as.*bot" shortcuts/lingo/lingo_entity_create.go -A5Repository: larksuite/cli
Length of output: 366
🏁 Script executed:
# Check the documentation file to see what it says about --as bot requirement
cat -n skills/lark-lingo/references/lark-lingo-create.md | head -30Repository: larksuite/cli
Length of output: 843
🏁 Script executed:
# Check if there are other lingo shortcut implementations that DO enforce bot requirement
rg -n "RequireBot|AuthTypes.*bot" shortcuts/lingo/ --type goRepository: larksuite/cli
Length of output: 539
🏁 Script executed:
# Look at other write operations (update, delete) to see if they validate bot requirement
rg -n -A5 "Validate: func" shortcuts/lingo/lingo_entity_update.go shortcuts/lingo/lingo_entity_delete.goRepository: larksuite/cli
Length of output: 1159
🏁 Script executed:
# Check the delete and update tests to see if they also omit --as bot
grep -n -A3 "CreateDryRun\|UpdateDryRun\|DeleteDryRun" tests/cli_e2e/lingo/lingo_dryrun_test.go | head -40Repository: larksuite/cli
Length of output: 587
🏁 Script executed:
# Look for any other bot-only shortcuts to see how they handle validation
rg -n "bot-only\|must.*bot\|RequireBot" shortcuts/ --type goRepository: larksuite/cli
Length of output: 39
🏁 Script executed:
# Get the full args for Update and Delete tests to see if they include --as bot
sed -n '113,135p' tests/cli_e2e/lingo/lingo_dryrun_test.goRepository: larksuite/cli
Length of output: 815
🏁 Script executed:
# Get the full args for Delete test
sed -n '136,155p' tests/cli_e2e/lingo/lingo_dryrun_test.goRepository: larksuite/cli
Length of output: 663
Add --as bot flag to all write-operation tests and fix shortcut validation.
The lingo documentation (lark-lingo-create.md:5) explicitly mandates --as bot for the +create operation and examples show it for +update and +delete as well. However, the shortcut implementations for all three operations allow both "user" and "bot" auth types (lingo_entity_*.go:26) but do not validate the bot-identity requirement in their Validate callbacks.
This test suite tests incorrect behavior by omitting the --as bot flag across all three write operations and expecting exit code 0:
- TestLingo_CreateDryRun (line 95): missing
--as bot - TestLingo_UpdateDryRun (line 120): missing
--as bot - TestLingo_DeleteDryRun (line 141): missing
--as bot
Fix required in two places:
- Add
"--as", "bot"to the Args array in all three test functions - Add bot-identity validation to the Validate callback in each shortcut (lingo_entity_create.go, lingo_entity_update.go, lingo_entity_delete.go) to enforce the documented requirement
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/cli_e2e/lingo/lingo_dryrun_test.go` around lines 88 - 111, Update the
dry-run tests to include the required bot identity flag by adding "--as", "bot"
to the Args slice in TestLingo_CreateDryRun, TestLingo_UpdateDryRun, and
TestLingo_DeleteDryRun; then enforce the documented requirement in the CLI
shortcuts by adding validation inside the Validate callback of the shortcut
implementations (functions in lingo_entity_create.go, lingo_entity_update.go,
lingo_entity_delete.go) to reject non-bot auth types—check the parsed auth type
(the same field used now that allows "user" or "bot") and return a validation
error unless it equals "bot".
Introduces a new
lingoservice covering the six Feishu Lingo / 百科 dictionary entry endpoints as shortcuts, plus the accompanying lark-lingo skill with per-verb reference docs.Shortcuts (shortcuts/lingo/):
+search POST /lingo/v1/entities/search read
+match POST /lingo/v1/entities/match read
+get GET /lingo/v1/entities/:id read
+create POST /lingo/v1/entities write (bot-only)
+update PUT /lingo/v1/entities/:id write (bot-only, full overwrite)
+delete DELETE /lingo/v1/entities/:id high-risk-write (bot-only)
Each shortcut has validate / dry-run / execute hooks and unit tests covering flag validation, dry-run payload, successful execute, and API-error paths. Dry-run e2e tests added under tests/cli_e2e/lingo/.
The skill (skills/lark-lingo/) follows the lark-markdown pattern: concise SKILL.md index + one references/lark-lingo-.md per shortcut. References capture the real-world gotchas we hit during integration:
auth login --scope(which only affects user scope)Also:
Summary
Changes
Test Plan
lark xxxcommand works as expectedRelated Issues
Summary by CodeRabbit
New Features
Documentation
Tests
Chores