feat(search): wiki +resolve-node shortcut + search methodology skills#346
feat(search): wiki +resolve-node shortcut + search methodology skills#346dingding0418 wants to merge 1 commit intolarksuite:mainfrom
Conversation
Validated by a 13-case search eval harness (cli-evals): v1 baseline
89/195 (45.6%) → v2 132/195 (67.7%), +48% relative, 8 wins / 0 regressions.
Source code:
- New shortcut `lark-cli wiki +resolve-node --token <url|token>` resolving
a wiki node to its underlying obj_token + obj_type + title. Replaces the
agent workaround `lark-cli api GET /open-apis/wiki/v2/spaces/get_node`
which had high LLM friction (knowing the path, nested JSON params,
nested response shape).
- Auto-extracts token from full wiki URLs.
- Returns flat output `{node_token, obj_token, obj_type, title, space_id}`.
Skill files (where most of the eval gain came from):
- skills/lark-doc/SKILL.md: new "AI Usage Guidance: 多轮关键词改写" section
with rewrite matrix, candidate-evaluation order, and best-effort fallback
rules for open-ended questions.
- skills/lark-doc/references/lark-doc-search.md: add multi-round retry
guidance, synonym list, "when to stop rewriting" rules.
- skills/lark-doc/references/lark-doc-fetch.md: add "大文档处理 ⚠️ "
section documenting docs +fetch 504 limits and the
search-summary → --limit → raw blocks API fallback ladder.
- skills/lark-doc/references/lark-doc-search-recipes.md (new): four-step
enterprise knowledge search methodology + synonym dictionary +
failure case library + decision tree.
- skills/lark-wiki/SKILL.md: new "wiki 节点是壳" section + Shortcuts table
pointing at the new +resolve-node.
- skills/lark-wiki/references/lark-wiki-resolve-node.md (new): full
reference for the new shortcut with examples and historical context.
Per-case verified wins (13-case eval harness):
case_001: 6 → 9 (+3, completeness 1→4, used wiki +resolve-node)
case_002: 5 → 12 (+7, recall 0→4, multi-keyword rewriting)
case_005: 5 → 13 (+8, best-effort fallback rule)
case_008: 13 → 14 (+1, multi-keyword found supplementary doc)
case_009: 13 → 15 (+2, fetched both expected sources)
case_010: 5 → 9 (+4, split-question rewriting matched 3/6 expected)
case_011: 6 → 13 (+7, "媒体沟通" rewrite hit expected token)
case_013: 0 → 11 (+11, search-summary hit on specific number query)
Unchanged cases reflect tool-capability gaps that skill changes cannot
fix and are documented for future work:
case_004: docs +fetch 504 timeout on large docs
case_007: cross-tenant search not supported
case_006: target doc title misaligned with query keywords
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR introduces a new Changes
Sequence DiagramsequenceDiagram
participant User
participant CLI as lark-cli
participant API as Lark API
User->>CLI: wiki +resolve-node --token <URL/token>
activate CLI
CLI->>CLI: Extract & normalize token<br/>(trim, extract from /wiki/*, strip query)
alt Dry-run mode
CLI->>CLI: Configure GET request
Note over CLI: /open-apis/wiki/v2/spaces/get_node<br/>token=<normalized>, obj_type="wiki"
CLI-->>User: Display dry-run request
else Execute mode
CLI->>API: GET /open-apis/wiki/v2/spaces/get_node
activate API
API-->>CLI: Return {node: {...}} or error
deactivate API
alt Node exists
CLI->>CLI: Flatten node fields<br/>(obj_token, obj_type, title, etc.)
CLI->>CLI: Format as table
CLI-->>User: Display resolved metadata
else Node missing
CLI-->>User: Return formatted API error<br/>(raw + normalized tokens)
end
end
deactivate CLI
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Greptile SummaryThis PR adds a Confidence Score: 5/5Safe to merge; all findings are P2 style/best-practice suggestions with no correctness impact. The core logic (token extraction, API call, response flattening) is correct and follows existing shortcut patterns. All three inline comments are P2: deprecated API usage, a semantically odd error code on a defensive nil-check, and missing unit tests. None of these block the feature from working correctly in production. shortcuts/wiki/wiki_resolve_node.go — minor style improvements recommended but not blocking.
|
| Filename | Overview |
|---|---|
| shortcuts/wiki/wiki_resolve_node.go | New shortcut implementing wiki node resolution; logic is correct but uses deprecated CallAPI, passes code 0 to ErrAPI on unexpected nil, and lacks unit tests for extractWikiToken. |
| shortcuts/wiki/shortcuts.go | Thin aggregator returning the single WikiResolveNode shortcut; straightforward and correct. |
| shortcuts/register.go | Adds wiki import and appends wiki.Shortcuts() to allShortcuts; follows existing registration pattern exactly. |
| skills/lark-wiki/references/lark-wiki-resolve-node.md | Comprehensive reference for the new shortcut including usage examples, output schema, downstream decision tree, and historical context; no issues found. |
| skills/lark-doc/references/lark-doc-search-recipes.md | New file with a four-step enterprise search methodology, synonym dictionary, failure case library, and decision tree; documentation-only addition with no code concerns. |
| skills/lark-wiki/SKILL.md | Adds 'wiki 节点是壳' section and a Shortcuts table pointing to +resolve-node; content is accurate and well-structured. |
| skills/lark-doc/SKILL.md | Adds multi-round keyword rewrite matrix and best-effort fallback rules for open-ended queries; documentation-only, no issues. |
| skills/lark-doc/references/lark-doc-fetch.md | Adds 504 timeout handling guidance and a search-summary → --limit → raw blocks API fallback ladder for large documents; documentation-only, no issues. |
| skills/lark-doc/references/lark-doc-search.md | Adds multi-round retry guidance, synonym list, and 'when to stop rewriting' rules; documentation-only, no issues. |
Sequence Diagram
sequenceDiagram
participant User
participant CLI as lark-cli wiki +resolve-node
participant Extract as extractWikiToken()
participant LarkAPI as Lark API /wiki/v2/spaces/get_node
participant NextStep as Next shortcut (docs/base/sheets)
User->>CLI: --token URL or bare node token
CLI->>Extract: raw input string
Extract-->>CLI: normalized node token
CLI->>LarkAPI: GET get_node with token and obj_type=wiki
LarkAPI-->>CLI: response with node object containing obj_token and obj_type
CLI-->>User: flat JSON with node_token, obj_token, obj_type, title, space_id
User->>NextStep: use obj_token and obj_type to call docs +fetch or base or sheets +read
Reviews (1): Last reviewed commit: "feat(search): wiki +resolve-node shortcu..." | Re-trigger Greptile
| node, _ := data["node"].(map[string]interface{}) | ||
| if node == nil { | ||
| return output.ErrAPI(0, "wiki node not found or not accessible (input="+rawInput+", normalized="+token+")", nil) |
There was a problem hiding this comment.
Semantically misleading error code 0 on unexpected-nil guard
output.ErrAPI(0, ...) passes Lark success code 0 to ClassifyLarkError, which doesn't match any known error case and falls through to a generic api_error. Because 0 is the standard Lark "OK" code, the intent (unexpected missing node field in an otherwise-successful response) would be clearer with output.Errorf:
| node, _ := data["node"].(map[string]interface{}) | |
| if node == nil { | |
| return output.ErrAPI(0, "wiki node not found or not accessible (input="+rawInput+", normalized="+token+")", nil) | |
| return output.Errorf(output.ExitAPI, "api_error", "wiki node not found or not accessible (input=%s, normalized=%s)", rawInput, token) |
| var wikiURLPattern = regexp.MustCompile(`/wiki/([A-Za-z0-9]+)`) | ||
|
|
||
| // extractWikiToken returns the bare wiki token from either a URL or a token string. | ||
| // If the input doesn't look like a URL, it's assumed to already be a token. | ||
| func extractWikiToken(input string) string { | ||
| input = strings.TrimSpace(input) | ||
| if input == "" { | ||
| return "" | ||
| } | ||
| if matches := wikiURLPattern.FindStringSubmatch(input); len(matches) > 1 { | ||
| return matches[1] | ||
| } | ||
| // Strip any trailing query string or fragment if present | ||
| if idx := strings.IndexAny(input, "?#"); idx >= 0 { | ||
| input = input[:idx] | ||
| } | ||
| return input |
There was a problem hiding this comment.
No unit tests for
extractWikiToken
extractWikiToken has three distinct branches (URL match, query-string strip, plain token passthrough) and is called from both Validate and Execute. Every other shortcut package that has non-trivial helpers includes a *_test.go; this package has none. Edge cases worth covering: scheme-less URL (bytedance.larkoffice.com/wiki/TOKEN), URL with query string, bare token with ? fragment, and empty input.
| data, err := runtime.CallAPI( | ||
| "GET", | ||
| "/open-apis/wiki/v2/spaces/get_node", | ||
| map[string]interface{}{ | ||
| "token": token, | ||
| "obj_type": "wiki", | ||
| }, | ||
| nil, | ||
| ) |
There was a problem hiding this comment.
Prefer
DoAPIJSON over the deprecated CallAPI for new shortcuts
runner.go documents: "Prefer DoAPI for new code — it calls the Lark SDK directly and supports file upload/download options." DoAPIJSON provides the same JSON-envelope unwrapping as CallAPI but goes through the SDK path. Since this is a net-new shortcut it's a good opportunity to follow the recommended pattern:
data, err := runtime.DoAPIJSON("GET", "/open-apis/wiki/v2/spaces/get_node",
larkcore.QueryParams{
"token": []string{token},
"obj_type": []string{"wiki"},
}, nil)
if err != nil {
return err
}Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
skills/lark-wiki/references/lark-wiki-resolve-node.md (1)
76-99: Consider noting the case statement is bash-specific.The downstream flow example uses bash
casesyntax. While line 101 clarifies LLM agents don't need scripts, users referencing this doc for shell automation might benefit from knowing this is bash-specific (won't work inshorzshwithout modification for some edge cases).📝 Suggested clarification
## 标准下游流程 +> 以下是 bash 脚本示例,供自动化参考。 + ```bash # 第一步:解析🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skills/lark-wiki/references/lark-wiki-resolve-node.md` around lines 76 - 99, The example uses bash-specific syntax (see RESULT=$(lark-cli wiki +resolve-node ...), OBJ_TYPE and the case "$OBJ_TYPE" in construct) but the doc doesn't state the shell; update the markdown to explicitly label the code block as bash and add one brief sentence above the block: "This example is written for bash; behavior may differ in sh/zsh—adjust quoting/command substitutions accordingly." Ensure the code fence is annotated as bash (```bash) and keep the existing example unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@shortcuts/wiki/wiki_resolve_node.go`:
- Around line 107-110: The current silent type assertion for data["node"] can
hide malformed responses; change the logic in the function handling data (the
code around the node variable in wiki_resolve_node.go) to first check whether
data contains "node" (v, ok := data["node"]) and return ErrAPI indicating the
node is missing when !ok or v == nil, and if present perform a type assertion to
map[string]interface{} (nodeMap, ok := v.(map[string]interface{})); when that
assertion fails return a different ErrAPI that reports an unexpected type
(include fmt.Sprintf("%T", v) or other diagnostic info) rather than claiming the
node was not found, so callers get a clear diagnostic for malformed API
responses.
---
Nitpick comments:
In `@skills/lark-wiki/references/lark-wiki-resolve-node.md`:
- Around line 76-99: The example uses bash-specific syntax (see
RESULT=$(lark-cli wiki +resolve-node ...), OBJ_TYPE and the case "$OBJ_TYPE" in
construct) but the doc doesn't state the shell; update the markdown to
explicitly label the code block as bash and add one brief sentence above the
block: "This example is written for bash; behavior may differ in sh/zsh—adjust
quoting/command substitutions accordingly." Ensure the code fence is annotated
as bash (```bash) and keep the existing example unchanged.
🪄 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: e98d0b54-3e2f-44e0-a7b2-5aba30cba6f1
📒 Files selected for processing (9)
shortcuts/register.goshortcuts/wiki/shortcuts.goshortcuts/wiki/wiki_resolve_node.goskills/lark-doc/SKILL.mdskills/lark-doc/references/lark-doc-fetch.mdskills/lark-doc/references/lark-doc-search-recipes.mdskills/lark-doc/references/lark-doc-search.mdskills/lark-wiki/SKILL.mdskills/lark-wiki/references/lark-wiki-resolve-node.md
| node, _ := data["node"].(map[string]interface{}) | ||
| if node == nil { | ||
| return output.ErrAPI(0, "wiki node not found or not accessible (input="+rawInput+", normalized="+token+")", nil) | ||
| } |
There was a problem hiding this comment.
Type assertion failure produces a misleading error message.
If data["node"] exists but is not a map[string]interface{} (e.g., malformed API response), the silent type assertion sets node to nil, and the error message claims "wiki node not found" when the actual issue is an unexpected response format. Consider distinguishing these cases.
🔧 Suggested improvement for clearer diagnostics
- node, _ := data["node"].(map[string]interface{})
- if node == nil {
- return output.ErrAPI(0, "wiki node not found or not accessible (input="+rawInput+", normalized="+token+")", nil)
+ nodeRaw, exists := data["node"]
+ if !exists || nodeRaw == nil {
+ return output.ErrAPI(0, "wiki node not found or not accessible (input="+rawInput+", normalized="+token+")", nil)
+ }
+ node, ok := nodeRaw.(map[string]interface{})
+ if !ok {
+ return output.ErrAPI(0, "unexpected API response format for wiki node", data)
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@shortcuts/wiki/wiki_resolve_node.go` around lines 107 - 110, The current
silent type assertion for data["node"] can hide malformed responses; change the
logic in the function handling data (the code around the node variable in
wiki_resolve_node.go) to first check whether data contains "node" (v, ok :=
data["node"]) and return ErrAPI indicating the node is missing when !ok or v ==
nil, and if present perform a type assertion to map[string]interface{} (nodeMap,
ok := v.(map[string]interface{})); when that assertion fails return a different
ErrAPI that reports an unexpected type (include fmt.Sprintf("%T", v) or other
diagnostic info) rather than claiming the node was not found, so callers get a
clear diagnostic for malformed API responses.
|
|
Validated by a 13-case search eval harness (cli-evals): v1 baseline 89/195 (45.6%) → v2 132/195 (67.7%), +48% relative, 8 wins / 0 regressions.
Source code:
lark-cli wiki +resolve-node --token <url|token>resolving a wiki node to its underlying obj_token + obj_type + title. Replaces the agent workaroundlark-cli api GET /open-apis/wiki/v2/spaces/get_nodewhich had high LLM friction (knowing the path, nested JSON params, nested response shape).{node_token, obj_token, obj_type, title, space_id}.Skill files (where most of the eval gain came from):
Per-case verified wins (13-case eval harness):
case_001: 6 → 9 (+3, completeness 1→4, used wiki +resolve-node)
case_002: 5 → 12 (+7, recall 0→4, multi-keyword rewriting)
case_005: 5 → 13 (+8, best-effort fallback rule)
case_008: 13 → 14 (+1, multi-keyword found supplementary doc)
case_009: 13 → 15 (+2, fetched both expected sources)
case_010: 5 → 9 (+4, split-question rewriting matched 3/6 expected)
case_011: 6 → 13 (+7, "媒体沟通" rewrite hit expected token)
case_013: 0 → 11 (+11, search-summary hit on specific number query)
Unchanged cases reflect tool-capability gaps that skill changes cannot fix and are documented for future work:
case_004: docs +fetch 504 timeout on large docs
case_007: cross-tenant search not supported
case_006: target doc title misaligned with query keywords
Summary
Changes
Test Plan
lark xxxcommand works as expectedRelated Issues
Summary by CodeRabbit
New Features
wiki +resolve-nodecommand to resolve wiki URLs and tokens into underlying object metadata.Documentation