Skip to content

feat: add doctor preflight for shortcut readiness#837

Closed
lizixionglzx wants to merge 3 commits into
larksuite:mainfrom
lizixionglzx:feat/doctor-preflight
Closed

feat: add doctor preflight for shortcut readiness#837
lizixionglzx wants to merge 3 commits into
larksuite:mainfrom
lizixionglzx:feat/doctor-preflight

Conversation

@lizixionglzx

@lizixionglzx lizixionglzx commented May 12, 2026

Copy link
Copy Markdown

Summary

Add lark-cli doctor preflight to check whether a shortcut is ready before execution.

The CLI already performs strong runtime checks for config, identity, login, scopes, and strict mode, but callers currently discover those prerequisites only after attempting the real shortcut. This command surfaces the same readiness information earlier and returns structured next actions for both humans and AI agents.

Changes

  • add doctor preflight <service> <shortcut>
  • support --as auto|user|bot
  • support --format json|pretty
  • check config, identity, strict mode, login, user scopes, and shortcut risk
  • add focused tests and README updates

Scope

This PR is intentionally limited to shortcuts and does not change the real execution pipeline.

Testing

go test ./cmd/doctor
go test ./cmd/...
make unit-test

Risk

Low risk, additive only.

Summary by CodeRabbit

  • New Features

    • Added a doctor preflight command to verify whether a shortcut is ready to run (checks config, identity, login/token, scopes, and risk hints); supports JSON and human-readable output.
  • Documentation

    • Added "Command Preflight" docs with example invocations in English and Chinese.
  • Tests

    • Expanded test suite to cover preflight validation, identity resolution, token/scope branches, formatting, and execution-plan behaviors.

Review Change Stack

@CLAassistant

CLAassistant commented May 12, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label May 12, 2026
@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a new doctor preflight CLI subcommand that resolves a shortcut and identity, runs readiness checks (config, strict-mode, auth token, scopes, risk), and prints a JSON or human-readable report with next-action commands and dry-run guidance.

Changes

Doctor Preflight Feature

Layer / File(s) Summary
Command setup and result model
cmd/doctor/preflight.go (lines 1–100), cmd/doctor/doctor.go (line 46)
DoctorPreflightOptions, token-status hooks, JSON result types, and Cobra registration for preflight <service> <shortcut> with --as and --format validation.
Orchestration and target/identity resolution
cmd/doctor/preflight.go (lines 125–244)
Main doctorPreflightRun flow: resolve target shortcut, load effective CLI config via Factory/keychain/profile, normalize --as and compute identity source precedence (explicit, strict-mode, credential hint, config default).
Preflight checks (config, auth, scopes, risk)
cmd/doctor/preflight.go (lines 246–502)
Ordered checks: config readiness, strict-mode blocking, identity support, user token readiness (resolve/expiry/refresh handling), scope readiness (missing/unknown/granted), and risk hints/actions (including dry-run for write/high-risk-write).
Result rendering and output
cmd/doctor/preflight.go (lines 513–570)
Chooses JSON or pretty output; pretty mode prints READY/NOT READY, workspace/target/identity, execution and dry-run commands, required flags, per-check hints, and next-action commands.
Execution metadata & flag helpers
cmd/doctor/preflight.go (lines 616–729)
Builds execution command templates (including --as, required flags), dry-run command, --yes behavior for high-risk writes, and flag placeholder/normalization helpers.
Tests, documentation, and test helpers
README.md (lines 269–277), README.zh.md (lines 270–278), cmd/doctor/preflight_test.go
Expanded test suite covering input validation, identity resolution, token/scope branches, format handling, execution-plan templating, and helper functions; adds deterministic credential resolvers and factory wiring for tests; documents doctor preflight usage and examples in English and Chinese.

Sequence Diagram

sequenceDiagram
  participant CLI as lark-cli
  participant PreflightCmd as PreflightCommand
  participant Factory as Factory/Config
  participant CredProv as CredentialProvider
  participant TokenStore as TokenStore
  CLI->>PreflightCmd: invoke preflight <service> <shortcut> (--as, --format)
  PreflightCmd->>Factory: load effective CLI config / profile
  PreflightCmd->>PreflightCmd: resolve target shortcut & required scopes
  PreflightCmd->>CredProv: resolve identity/token (if user)
  CredProv->>TokenStore: fetch stored token / metadata
  PreflightCmd->>PreflightCmd: evaluate checks (config, strict-mode, token, scopes, risk)
  PreflightCmd->>CLI: render JSON or pretty result + next-action commands
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • larksuite/cli#245: Enforces rejection of auto bot-fallback in calendar shortcut validation; preflight adds identity and login readiness checks relevant to calendar scenarios.

Suggested labels

domain/ccm

Suggested reviewers

  • fangshuyu-768
  • sang-neo03

Poem

🐰 I poke the flags and scopes with care,

Ensuring tokens, identities, all fair.
Dry-run whispers, checks all in sight,
Preflight green — off hops the rabbit in delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add doctor preflight for shortcut readiness' accurately and clearly describes the main change—adding a new doctor preflight command to check shortcut readiness before execution.
Description check ✅ Passed The PR description includes a Summary, Changes with bullet points, and Testing section with test commands, though it lacks explicit Test Plan checkboxes and Related Issues link as defined in the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented May 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 55.26316% with 187 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.68%. Comparing base (db1a3fc) to head (685dd30).
⚠️ Report is 12 commits behind head on main.

Files with missing lines Patch % Lines
cmd/doctor/preflight.go 55.15% 165 Missing and 22 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #837      +/-   ##
==========================================
- Coverage   65.77%   65.68%   -0.09%     
==========================================
  Files         516      517       +1     
  Lines       48625    49043     +418     
==========================================
+ Hits        31985    32216     +231     
- Misses      13881    14046     +165     
- Partials     2759     2781      +22     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@685dd307631980d8772dff3b32e5221e2bb7618b

🧩 Skill update

npx skills add lizixionglzx/cli#feat/doctor-preflight -y -g

@evandance evandance added domain/core CLI framework and core libraries enhancement New feature or request labels May 13, 2026
@github-actions github-actions Bot removed the domain/core CLI framework and core libraries label May 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
cmd/doctor/preflight_test.go (1)

526-555: ⚡ Quick win

Consider whether cmdutil.TestFactory can replace cmdutil.NewDefault here.

Per coding guidelines, test factories should use cmdutil.TestFactory(t, config). However, Line 553 injects a custom credential.Provider which TestFactory may not support. If TestFactory can accommodate custom credential providers, refactor to use it; otherwise the current approach is justified for controlling token resolution in tests.

As per coding guidelines: "Use cmdutil.TestFactory(t, config) for test factories in unit tests"

🤖 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 `@cmd/doctor/preflight_test.go` around lines 526 - 555, The test helper
newPreflightFactory currently constructs a Factory via cmdutil.NewDefault so it
can set f.Credential to credential.NewCredentialProvider(... with
preflightAccountResolver and preflightTokenResolver); replace this with
cmdutil.TestFactory(t, config) if TestFactory supports injecting a custom
credential provider, otherwise add support: update cmdutil.TestFactory signature
(or add cmdutil.TestFactoryWithCredential) to accept an optional
credential.Provider parameter and use it to set f.Credential internally, then
switch newPreflightFactory to call the TestFactory variant and remove the manual
f.Credential assignment (keep using preflightAccountResolver and
preflightTokenResolver when constructing the provider).
cmd/doctor/preflight.go (1)

110-123: ⚡ Quick win

Validate --as upfront alongside --format.

--format is validated immediately in RunE, but --as validation is deferred to resolvePreflightIdentity, which only runs after resolveTargetShortcut and resolvePreflightConfig (config load + keychain resolution). A user / agent that passes --as=wrong pays the cost of config resolution before getting the validation error, and the two flag failures behave differently for callers. Moving the --as allowlist check next to the --format check makes the validation order predictable and keeps both flag-validation errors close to cobra.ExactArgs(2).

♻️ Proposed change
 		RunE: func(cmd *cobra.Command, args []string) error {
 			opts.Ctx = cmd.Context()
 			opts.Service = args[0]
 			opts.Shortcut = args[1]
 			if !slices.Contains([]string{"json", "pretty"}, opts.Format) {
 				return output.ErrValidation("invalid --format %q: must be json or pretty", opts.Format)
 			}
+			normalizedAs := strings.TrimSpace(opts.RequestedAs)
+			if normalizedAs == "" {
+				normalizedAs = string(core.AsAuto)
+			}
+			if !slices.Contains([]string{string(core.AsAuto), string(core.AsUser), string(core.AsBot)}, normalizedAs) {
+				return output.ErrValidation("invalid --as %q: must be auto, user, or bot", opts.RequestedAs)
+			}
+			opts.RequestedAs = normalizedAs
 			return doctorPreflightRun(opts)
 		},
🤖 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 `@cmd/doctor/preflight.go` around lines 110 - 123, The RunE should validate the
--as flag immediately (same place as --format) so callers get a fast, consistent
flag error; update the RunE block that currently sets opts.RequestedAs and
opts.Format to check that opts.RequestedAs is one of "auto","user","bot"
(returning output.ErrValidation(...) on invalid values) before calling
doctorPreflightRun, leaving resolvePreflightIdentity as-is for later runtime
resolution.
🤖 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 `@cmd/doctor/preflight.go`:
- Around line 290-303: The early-return on identity_supported currently returns
raw checks/actions and skips appending the risk hint; in runPreflightChecks
update the identity failure path (the block that handles
opts.Factory.CheckIdentity(...) error) to call appendRiskHints(checks, actions)
(or appendRiskHints(checks, actions, opts) if that signature is used) before
returning so the "risk" preflightCheck is included and the JSON/report shape
matches other failure paths; preserve the existing
preflightCheck/preflightAction values (Name "identity_supported", Blocking true,
etc.) and the return of checks, actions after appendRiskHints.

---

Nitpick comments:
In `@cmd/doctor/preflight_test.go`:
- Around line 526-555: The test helper newPreflightFactory currently constructs
a Factory via cmdutil.NewDefault so it can set f.Credential to
credential.NewCredentialProvider(... with preflightAccountResolver and
preflightTokenResolver); replace this with cmdutil.TestFactory(t, config) if
TestFactory supports injecting a custom credential provider, otherwise add
support: update cmdutil.TestFactory signature (or add
cmdutil.TestFactoryWithCredential) to accept an optional credential.Provider
parameter and use it to set f.Credential internally, then switch
newPreflightFactory to call the TestFactory variant and remove the manual
f.Credential assignment (keep using preflightAccountResolver and
preflightTokenResolver when constructing the provider).

In `@cmd/doctor/preflight.go`:
- Around line 110-123: The RunE should validate the --as flag immediately (same
place as --format) so callers get a fast, consistent flag error; update the RunE
block that currently sets opts.RequestedAs and opts.Format to check that
opts.RequestedAs is one of "auto","user","bot" (returning
output.ErrValidation(...) on invalid values) before calling doctorPreflightRun,
leaving resolvePreflightIdentity as-is for later runtime resolution.
🪄 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: 6c01efba-0854-4d97-9323-3f9fdfdce327

📥 Commits

Reviewing files that changed from the base of the PR and between 685dd30 and 2794114.

📒 Files selected for processing (4)
  • README.md
  • README.zh.md
  • cmd/doctor/preflight.go
  • cmd/doctor/preflight_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • README.zh.md

Comment thread cmd/doctor/preflight.go
Comment on lines +290 to +303
if err := opts.Factory.CheckIdentity(resolvedAs, shortcutAuthTypes(shortcut)); err != nil {
checks = append(checks, preflightCheck{
Name: "identity_supported",
Status: "fail",
Blocking: true,
Message: err.Error(),
})
actions = append(actions, preflightAction{
Type: "switch_identity",
Blocking: true,
Reason: "choose an identity supported by the target shortcut",
})
return checks, actions
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Inconsistent risk-hint emission when identity_supported fails.

Every other early-return path in runPreflightChecks finishes through appendRiskHints (strict-mode fail at line 272, user-token fail at line 318, and the normal end at line 351), but the identity_supported failure path returns the bare checks, actions instead. As a result, when the requested identity is unsupported by the shortcut, the risk check is silently dropped from the report while it appears in every other not-ready case, which makes the rendered output and JSON shape inconsistent for consumers.

🐛 Proposed fix
-		actions = append(actions, preflightAction{
-			Type:     "switch_identity",
-			Blocking: true,
-			Reason:   "choose an identity supported by the target shortcut",
-		})
-		return checks, actions
+		actions = append(actions, preflightAction{
+			Type:     "switch_identity",
+			Blocking: true,
+			Reason:   "choose an identity supported by the target shortcut",
+		})
+		return appendRiskHints(checks, actions, shortcut, opts.Service, opts.Shortcut)
📝 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.

Suggested change
if err := opts.Factory.CheckIdentity(resolvedAs, shortcutAuthTypes(shortcut)); err != nil {
checks = append(checks, preflightCheck{
Name: "identity_supported",
Status: "fail",
Blocking: true,
Message: err.Error(),
})
actions = append(actions, preflightAction{
Type: "switch_identity",
Blocking: true,
Reason: "choose an identity supported by the target shortcut",
})
return checks, actions
}
if err := opts.Factory.CheckIdentity(resolvedAs, shortcutAuthTypes(shortcut)); err != nil {
checks = append(checks, preflightCheck{
Name: "identity_supported",
Status: "fail",
Blocking: true,
Message: err.Error(),
})
actions = append(actions, preflightAction{
Type: "switch_identity",
Blocking: true,
Reason: "choose an identity supported by the target shortcut",
})
return appendRiskHints(checks, actions, shortcut, opts.Service, opts.Shortcut)
}
🤖 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 `@cmd/doctor/preflight.go` around lines 290 - 303, The early-return on
identity_supported currently returns raw checks/actions and skips appending the
risk hint; in runPreflightChecks update the identity failure path (the block
that handles opts.Factory.CheckIdentity(...) error) to call
appendRiskHints(checks, actions) (or appendRiskHints(checks, actions, opts) if
that signature is used) before returning so the "risk" preflightCheck is
included and the JSON/report shape matches other failure paths; preserve the
existing preflightCheck/preflightAction values (Name "identity_supported",
Blocking true, etc.) and the return of checks, actions after appendRiskHints.

@evandance evandance left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution — the implementation itself is clean. Before reviewing line-by-line, though, I'd like to step back: I couldn't find a motivating issue, and a lot of this overlaps with existing lark-cli doctor and the structured runtime error envelope (errType + hint + suggested command). Could we open an issue first to align on the concrete agent scenario this targets, and how the new subcommand fits next to those existing surfaces? Once the product direction is settled, the implementation here is in good shape to build on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants