Skip to content

feat(shortcuts): introduce TypedShortcut framework#1110

Open
sang-neo03 wants to merge 3 commits into
mainfrom
feat/shortcut-protocol
Open

feat(shortcuts): introduce TypedShortcut framework#1110
sang-neo03 wants to merge 3 commits into
mainfrom
feat/shortcut-protocol

Conversation

@sang-neo03
Copy link
Copy Markdown
Collaborator

@sang-neo03 sang-neo03 commented May 26, 2026

Summary

Introduces a strongly-typed shortcut protocol that coexists with the legacy common.Shortcut. A generic TypedShortcut[T] outer wrapper, a reflect-driven binder, six typed primitives, and a sectioned --help renderer give shortcut authors compile-time argument types and structured error envelopes (shortcut_oneof_missing / shortcut_oneof_multiple / shortcut_group_incomplete) without forking the existing runShortcut pipeline.

Note

Update (ccf654d): the im +messages-send pilot was reverted. The pilot
was an exploratory end-to-end validation of the framework only. It has been
rolled back so this PR lands the framework aloneim +messages-send
stays on the legacy common.Shortcut path, identical to main. The
framework retains no production caller yet; the first real migration will
come in a follow-up PR. See the "Net result after revert" section below for
the current diff. The Changes / Test Plan sections below describe the
original pilot commits (a07239b / ad4368e) and are kept for history.

Net result after revert (current PR diff)

  • Kept: shortcuts/common/ framework (protocol / binder / typed_shortcut / typed_help / runner / types), shortcuts/common/argstype/ primitives, errs shortcut subtypes, cmd/auth + register.go ShortcutDescriptor / Mountable dual-track dispatch.
  • Reverted: shortcuts/im/ restored to its pre-pilot (main) state — im_messages_send.go back to legacy common.Shortcut; protocol.go / protocol_test.go / im_messages_send_test.go deleted; shortcuts.go no longer exports TypedShortcuts().
  • Removed wiring: addTyped(im.TypedShortcuts()) and the now-unused addTyped helper dropped from register.go; framework doc comments that referenced the pilot types (MessageTarget / MessageContent / VideoContent / RawContent) rewritten as neutral descriptions.
  • Verified: go build ./... and go test ./shortcuts/... ./errs/... ./cmd/auth/... ./cmd/ all pass; gofmt clean; no dangling references to the removed pilot types remain in the framework.

Changes (original pilot — see Update note above)

  • Add shortcuts/common/protocol.goMountable / ShortcutDescriptor / OneOfMarker / Validatable / Normalizable[T] / ArgsValidator / Maybe[T] / HelpExample
  • Add shortcuts/common/binder.go — reflect Args walker with intra-Args tag-uniqueness panic, cobra flag registration, value binding, bindBuckets for nested OneOf / group sub-structs, runNormalize (reflect-dispatched, generic-safe), runValidateValue (recurses into buckets / groups), runFrameworkRules (required / enum / OneOf / group, recursive)
  • Add shortcuts/common/typed_shortcut.goTypedShortcut[T] with 8 metadata methods + mountTyped adapter that synthesizes a legacy shell and reuses the existing identity / scopes / @file / stdin / jq / dry-run / high-risk gate
  • Add shortcuts/common/typed_help.go — sectioned --help (CHOOSE ONE <FIELD> / OPTIONAL / EXAMPLES / GLOBAL FLAGS) with Risk: / Tips: passthrough
  • Add shortcuts/common/argstype/ — 6 typed primitives: ChatID (oc_ prefix), UserOpenID (ou_ prefix), UserOpenIDList (CSV), SafePath (cwd-relative, rejects abs / ..), MediaInput (URL / key / path tri-state), SpreadsheetRef (URL → token Normalize)
  • Add errs/subtypes_shortcut.goSubtypeShortcutOneOfMissing / SubtypeShortcutOneOfMultiple / SubtypeShortcutGroupIncomplete
  • Modify shortcuts/common/types.go — 5 Get* accessors on *Shortcut (GetService / GetCommand / GetDescription / GetAuthTypes / GetRisk)
  • Modify shortcuts/common/runner.gotypedArgs any lifecycle slot + TypedArgs / SetTypedArgs accessors on RuntimeContext
  • Modify shortcuts/register.goAllShortcuts() returns []ShortcutDescriptor; RegisterShortcutsWithContext dispatches Mountable so legacy and typed shortcuts share one path; addLegacy helper boxes each domain's contributions (the addTyped helper and im pilot wiring were later reverted — see Update note)
  • Add shortcuts/im/protocol.goMessageTarget / MessageContent / VideoContent / RawContent (reverted)
  • Modify shortcuts/im/im_messages_send.go — migrated to TypedShortcut[*ImMessagesSendArgs] (reverted; back to legacy)
  • Modify shortcuts/im/shortcuts.go — new TypedShortcuts() exporter (reverted)
  • Modify cmd/auth/login.go, cmd/auth/login_interactive.go, cmd/auth/login_test.go, cmd/error_auth_hint.go, cmd/diagnose_scope_test.go, shortcuts/register_test.go — read shortcuts through ShortcutDescriptor interface methods (GetService / GetCommand / GetAuthTypes / DeclaredScopesForIdentity); shortcutSupportsIdentity accepts the interface

Test Plan

  • go build ./... and unit tests pass (go test ./shortcuts/... ./errs/... ./cmd/auth/... ./cmd/)
  • validate passed (build / vet / unit / convention)
  • gofmt clean; no dangling references to reverted pilot types
  • independent Codex review of the revert: confirmed complete & correct, no dead-code / unused-lint hazard from retaining the framework
  • Note: pilot-specific E2E / acceptance / skill-eval from the original pilot no longer apply, since im +messages-send reverts to its existing (already-tested) legacy behavior.

Related Issues

N/A

Summary by CodeRabbit

  • New Features

    • Typed CLI shortcut framework: structured arg binding, richer typed help, and typed shortcut mounting.
    • New argument validators: safe relative paths, media inputs (URL/key/path), chat IDs, user IDs/lists, and spreadsheet references.
    • IM message-send command migrated to typed args with clearer validation and behavior.
  • Bug Fixes

    • Improved auth-domain scoping and matching to reduce incorrect domain inclusion.
  • Tests

    • Expanded unit tests and new error subtypes covering validators, binder, typed shortcuts, and IM command.

Review Change Stack

@github-actions github-actions Bot added domain/im PR touches the im domain size/XL Architecture-level or global-impact change labels May 26, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a typed-shortcut protocol and reflection binder, many arg-type validators, RuntimeContext typed-args storage, typed help, migrates im +messages-send to TypedShortcut, refactors registration to descriptor/mountable, and updates auth/scope call sites to use descriptor accessors.

Changes

Typed Shortcut Framework & ImMessagesSend Migration

Layer / File(s) Summary
Protocol & interfaces
shortcuts/common/protocol.go, shortcuts/common/protocol_test.go
Introduces ShortcutDescriptor, Mountable, OneOfMarker, Validatable, Normalizable[T], ArgsValidator, Maybe[T], and HelpExample.
Argument type validators
shortcuts/common/argstype/*
Adds ChatID, UserOpenID, UserOpenIDList, SpreadsheetRef, SafePath, and MediaInput with Normalize/ValidateValue implementations and tests.
Reflection binder & rules
shortcuts/common/binder.go, shortcuts/common/binder_test.go, shortcuts/common/binder_coverage_test.go
Implements fieldSpec, walkArgs, flag registration/binding, runNormalize, runValidateValue, and framework rules (checkOneOf, checkGroup, checkEnumAndRequired) with tests asserting one-of/group validation subtypes and coverage tests.
TypedShortcut and typed help
shortcuts/common/typed_shortcut.go, shortcuts/common/typed_shortcut_test.go, shortcuts/common/typed_help.go, shortcuts/common/typed_help_test.go
Adds generic TypedShortcut[T] that mounts via a legacy shell delegating to typed hooks, and buildTypedHelp rendering CHOOSE ONE / OPTIONAL / EXAMPLES / GLOBAL FLAGS.
RuntimeContext typed args & legacy getters
shortcuts/common/runner.go, shortcuts/common/runner_args_test.go, shortcuts/common/types.go, shortcuts/common/types_test.go
Adds typedArgs storage with SetTypedArgs/TypedArgs and pointer-receiver getters on *Shortcut for descriptor compatibility.
IM protocol types
shortcuts/im/protocol.go, shortcuts/im/protocol_test.go
Defines MessageTarget and MessageContent one-of variants, VideoContent, and RawContent with JSON validation.
ImMessagesSend migration
shortcuts/im/im_messages_send.go, shortcuts/im/im_messages_send_test.go, shortcuts/im/builders_test.go
Migrates ImMessagesSend to TypedShortcut[*ImMessagesSendArgs], adds bindMessagesSendArgs, cross-field validators (validateVideoGroup, validateMsgTypeInterplay), updates DryRun/Execute to consume typed args, and adds binding/validation/mounting tests.
IM helpers & export refactor
shortcuts/im/helpers.go, shortcuts/im/helpers_test.go, shortcuts/im/shortcuts.go
Adds media-path validation helpers, removes ImMessagesSend from legacy Shortcuts(), adds TypedShortcuts() and updates tests to collect commands from both lists.
Shortcut registration refactor
shortcuts/register.go, shortcuts/register_test.go
Changes AllShortcuts() to return []common.ShortcutDescriptor, builds a combined descriptor list of legacy pointer shortcuts and typed mountables, groups by service, and mounts via MountWithContext; updates tests/JSON generation.
Auth and scope callers updated
cmd/auth/*, cmd/diagnose_scope_test.go, cmd/error_auth_hint.go, cmd/auth/login_test.go
Replaces direct Shortcut field access with descriptor getters (GetService, GetCommand, GetAuthTypes) and updates helper signatures to accept ShortcutDescriptor.
Shortcut validation subtypes
errs/subtypes_shortcut.go, errs/subtypes_shortcut_test.go
Adds SubtypeShortcutOneOfMissing, SubtypeShortcutOneOfMultiple, and SubtypeShortcutGroupIncomplete and tests asserting their string values.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • larksuite/cli#1002: Related changes to shortcut registration/descriptor handling and registry population.
  • larksuite/cli#984: Related auth-hinting and error hint rewrite touching similar auth/error paths.
  • larksuite/cli#322: Related IM media/file handling changes and FileIO abstraction updates.

Suggested reviewers

  • liangshuo-1
  • evandance
  • wittam-01

"🐇 I hop through flags with gentle paws,
Normalize trims and binder draws.
OneOfs pick, groups must be complete,
Typed args stored where runtimes meet.
Tests cheer loud — a carrot treat!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.06% 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
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.
Title check ✅ Passed The title 'feat(shortcuts): introduce TypedShortcut framework' accurately summarizes the main objective of this PR—introducing a new typed shortcut framework alongside the legacy system.
Description check ✅ Passed The PR description provides a comprehensive summary, detailed changes, test plan with checkmarks, and includes a notable update section clarifying the pilot revert. However, it lacks a 'Related Issues' section with issue links and doesn't strictly follow the provided template structure (particularly the 'Changes' list format).

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/shortcut-protocol

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 26, 2026

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/shortcut-protocol -y -g

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (3)
shortcuts/register.go (1)

126-135: ⚡ Quick win

Add a duplicate shortcut key guard before mounting.

Right now, duplicate service/command entries across legacy + typed lists are appended silently. A small guard here prevents accidental double-mount regressions.

Suggested patch
 	byService := make(map[string][]common.Mountable)
+	seen := make(map[string]struct{})
 	for _, d := range allShortcuts {
 		m, ok := d.(common.Mountable)
 		if !ok {
 			panic(fmt.Sprintf("shortcut %s/%s missing Mountable", d.GetService(), d.GetCommand()))
 		}
+		key := d.GetService() + "/" + d.GetCommand()
+		if _, exists := seen[key]; exists {
+			panic(fmt.Sprintf("duplicate shortcut registration: %s", key))
+		}
+		seen[key] = struct{}{}
 		byService[d.GetService()] = append(byService[d.GetService()], m)
 	}
🤖 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/register.go` around lines 126 - 135, There is no duplicate-key
guard when building byService from allShortcuts, so identical service/command
pairs can be silently appended; add a seen set (map[string]struct{}) keyed by
fmt.Sprintf("%s/%s", d.GetService(), d.GetCommand()) inside the loop that
iterates allShortcuts and before appending to byService check the seen map and
panic or return an error/log a fatal if the key already exists to prevent
double-mounts; update code around the byService creation, the loop over
allShortcuts, and use the common.Mountable assertion (m, ok :=
d.(common.Mountable)) together with the seen check to locate the duplicate and
stop execution.
shortcuts/im/im_messages_send_test.go (1)

85-86: ⚡ Quick win

Use cmdutil.TestFactory(t, config) in unit tests instead of &cmdutil.Factory{}.

Line 85 and Line 115 instantiate a bare factory directly. Please switch both mount tests to cmdutil.TestFactory(...) to align with the test harness contract.

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

Also applies to: 115-116

🤖 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/im/im_messages_send_test.go` around lines 85 - 86, Replace the
direct instantiation of cmdutil.Factory (used when mounting the command with
ImMessagesSend.MountWithContext(context.Background(), imParent,
&cmdutil.Factory{})) with the test harness helper cmdutil.TestFactory(t,
config); update both occurrences (the one at the ImMessagesSend.MountWithContext
call and the second mount test near lines 115–116) to call
cmdutil.TestFactory(t, config) so the tests use the proper test factory
contract, passing the current *testing.T and the appropriate test config.
shortcuts/im/builders_test.go (1)

363-418: ⚡ Quick win

Exercise typed-args behavior directly in these ImMessagesSend tests.

Most new calls pass &ImMessagesSendArgs{} and rely on runtime flags, so these cases can still pass even if typed field usage regresses. Prefer constructing meaningful ImMessagesSendArgs per case (or binding once through the framework path) so Validate/DryRun coverage tracks the typed contract.

Also applies to: 705-718

🤖 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/im/builders_test.go` around lines 363 - 418, Tests for
ImMessagesSend call Validate with an empty &ImMessagesSendArgs{} and only
runtime flags, so they don't exercise the typed-args contract; update each
failing subtest to construct and pass a meaningful ImMessagesSendArgs that
matches the scenario (e.g., set Text for the "valid text" case, set Video and
VideoCover for the "video with video-cover" case, set only Video for the "video
without video-cover" case, set only VideoCover for the "video-cover without
video" case, and set MsgType plus Image for the "conflicting explicit msg-type"
case) before calling ImMessagesSend.Validate(context.Background(), args,
runtime), and apply the same change to the similar tests at the other location
(lines ~705-718) so Validate/DryRun exercises the typed fields instead of
relying solely on runtime flags; alternatively bind args via the framework path
once so the typed struct is populated for each test.
🤖 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/common/argstype/safe_path.go`:
- Around line 35-36: The current check uses filepath.Clean(s) which can hide
parent-directory segments (e.g. "a/../b") — first scan the raw input string s
for any literal ".." path segments and reject if found before calling
filepath.Clean; implement this by splitting s on both '/' and '\' (or use
strings.FieldsFunc) and returning an error if any segment == "..", then continue
with the existing clean := filepath.Clean(s) and the remaining checks; reference
the existing variable clean and the use of filepath.Clean in safe_path.go when
making the change.

In `@shortcuts/common/argstype/spreadsheet_ref.go`:
- Around line 28-31: The URL token extraction currently checks segments from
strings.Split(trimmed, "/") and accepts segments that start with "shtcn" even
when they include query or fragment suffixes (e.g., "shtcn...?..."). Update the
extraction logic to strip any query ('?') or fragment ('#') portion from each
segment before checking HasPrefix and before returning SpreadsheetRef; do the
same fix in the other loop/block referenced around the SheetRef validation (the
code that returns SpreadsheetRef(seg) and the similar check at lines ~55-61).
Ensure you sanitize each seg by cutting at the first '?' or '#' (or parse the
URL and use the path segments) so only the raw token is validated and returned.

In `@shortcuts/common/argstype/user_open_id_list.go`:
- Around line 32-46: In UserOpenIDList.ValidateValue, add an explicit guard to
reject an explicitly empty parsed list (check len(l) == 0) and return an
errs.ValidationError referencing the flagName and a clear message like "empty
user open_id list" so inputs like ",," fail fast; keep the existing per-item
"ou_" prefix checks (function: ValidateValue on type UserOpenIDList, variables:
l and flagName, helper itoa) and ensure the new empty-list error uses the same
errs.Problem Category/Subtype pattern as the other errors.

In `@shortcuts/common/binder.go`:
- Around line 308-320: The code always calls cmd.Flags().GetString for both
pointer and non-pointer fields (s.FlagName) and then reflect.Converts that
string into the target type, which breaks for non-string leaves (bool/int) and
can panic; change the binding in binder.go (the s.IsPtr branch and the
non-pointer branch that use fv and elemType) to detect the target kind (use
elemType := fv.Type().Elem() for pointers and target := fv.Type() for
non-pointers) and switch on kind to call the appropriate flag getter (GetBool,
GetInt/GetInt64/GetUint, GetFloat64, GetStringSlice, etc.), handle errors from
those getters, and then set the reflect.Value using
reflect.ValueOf(parsedValue).Convert(target) (or set a new pointer when s.IsPtr)
so typed leaves are bound with their native types instead of always using
strings.

In `@shortcuts/common/typed_help_test.go`:
- Around line 29-33: In TestTypedHelp_RendersSections, the test must isolate
config state by setting the LARKSUITE_CLI_CONFIG_DIR environment variable; at
the start of the test (inside TestTypedHelp_RendersSections, before creating the
cobra.Command or calling walkArgs/reflect.TypeOf(&helpDemoArgs{})), call
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) so the test uses a temporary
config directory and doesn't depend on ambient state.
- Around line 33-34: The test currently ignores the error returned by
walkArgs(reflect.TypeOf(&helpDemoArgs{})); change this to capture the error
(e.g. specs, err := walkArgs(...)) and fail fast if err != nil by calling the
test helper (e.g. t.Fatalf or t.Fatal) with the error message before calling
buildTypedHelp; keep using the same symbols walkArgs, helpDemoArgs, specs, and
buildTypedHelp so the rest of the test remains unchanged.

In `@shortcuts/common/typed_help.go`:
- Around line 147-156: renderGlobalFlags currently uses fs.VisitAll and appends
every flag (via the loop containing fs.VisitAll and globals = append(...))
without checking for hidden flags, causing flags with f.Hidden==true to appear
in "GLOBAL FLAGS"; update the VisitAll callback to skip flags where f.Hidden is
true (i.e., add an early return when f.Hidden), preserving the existing dedup
logic (checks against rendered and seen) and still appending only non-hidden
flags to the globals slice (refer to fs.VisitAll, the closure's f variable, the
rendered/seen maps, and globalFlag{Name:..., Shorthand:..., Usage:...}).
- Around line 28-29: Change the typed help output to write to stderr and omit
hidden flags when rendering global flags: replace uses of cmd.OutOrStdout() with
cmd.ErrOrStderr() (so human-readable help goes to stderr), and update
renderGlobalFlags (the VisitAll iteration over flags) to skip flags where
f.Hidden is true (e.g., continue if f.Hidden) so hidden flags are not listed
under GLOBAL FLAGS. Ensure both changes reference the existing cmd object and
the renderGlobalFlags/VisitAll flag iteration so behavior and signatures remain
unchanged.

In `@shortcuts/common/typed_shortcut_test.go`:
- Around line 65-66: Replace direct construction of cmdutil.Factory (the
&cmdutil.Factory{} passed into ts.MountWithContext and other test calls) with
the test helper cmdutil.TestFactory(t, config); before creating the factory,
call t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) to isolate config state;
create or pass an appropriate minimal config value into cmdutil.TestFactory(t,
config) for these tests (references: ts.MountWithContext, cmdutil.Factory,
cmdutil.TestFactory, t.Setenv, "LARKSUITE_CLI_CONFIG_DIR").

In `@shortcuts/common/typed_shortcut.go`:
- Around line 189-192: The anonymous Execute wrapper currently calls s.Execute
unconditionally which can panic if s.Execute is nil; update the wrapper (the
Execute func that receives context.Context and *RuntimeContext and calls
rt.TypedArgs()) to first check if s.Execute == nil and return a clear error
(e.g., using fmt.Errorf or errors.New) when nil, otherwise invoke s.Execute(c,
args, rt) as before so missing handlers fail gracefully instead of panicking.

In `@shortcuts/im/helpers.go`:
- Around line 88-94: validateMediaFlagPath currently ignores os.IsNotExist
errors so missing local files pass validation; update the fio.Stat error
handling in validateMediaFlagPath to treat any non-nil err (including
os.IsNotExist) as a validation failure by returning output.ErrValidation("%s:
%v", flagName, err) when err != nil, leaving the early-returns for
empty/http/media-key values unchanged.

In `@shortcuts/register_test.go`:
- Around line 82-90: The test for AllShortcuts currently only compares lengths
after appending and can pass if slices alias; change it to capture the original
slice (orig := AllShortcuts()), then call got := AllShortcuts(), append to got,
and assert that orig's length and element values are unchanged by the append
(e.g., len(orig) == len(AllShortcuts()) and elements of orig equal elements of
AllShortcuts()); use the AllShortcuts symbol and the test function in
shortcuts/register_test.go to locate and replace the existing
length-after-append check so the test proves the returned slice is a true
defensive copy rather than an alias.

---

Nitpick comments:
In `@shortcuts/im/builders_test.go`:
- Around line 363-418: Tests for ImMessagesSend call Validate with an empty
&ImMessagesSendArgs{} and only runtime flags, so they don't exercise the
typed-args contract; update each failing subtest to construct and pass a
meaningful ImMessagesSendArgs that matches the scenario (e.g., set Text for the
"valid text" case, set Video and VideoCover for the "video with video-cover"
case, set only Video for the "video without video-cover" case, set only
VideoCover for the "video-cover without video" case, and set MsgType plus Image
for the "conflicting explicit msg-type" case) before calling
ImMessagesSend.Validate(context.Background(), args, runtime), and apply the same
change to the similar tests at the other location (lines ~705-718) so
Validate/DryRun exercises the typed fields instead of relying solely on runtime
flags; alternatively bind args via the framework path once so the typed struct
is populated for each test.

In `@shortcuts/im/im_messages_send_test.go`:
- Around line 85-86: Replace the direct instantiation of cmdutil.Factory (used
when mounting the command with
ImMessagesSend.MountWithContext(context.Background(), imParent,
&cmdutil.Factory{})) with the test harness helper cmdutil.TestFactory(t,
config); update both occurrences (the one at the ImMessagesSend.MountWithContext
call and the second mount test near lines 115–116) to call
cmdutil.TestFactory(t, config) so the tests use the proper test factory
contract, passing the current *testing.T and the appropriate test config.

In `@shortcuts/register.go`:
- Around line 126-135: There is no duplicate-key guard when building byService
from allShortcuts, so identical service/command pairs can be silently appended;
add a seen set (map[string]struct{}) keyed by fmt.Sprintf("%s/%s",
d.GetService(), d.GetCommand()) inside the loop that iterates allShortcuts and
before appending to byService check the seen map and panic or return an
error/log a fatal if the key already exists to prevent double-mounts; update
code around the byService creation, the loop over allShortcuts, and use the
common.Mountable assertion (m, ok := d.(common.Mountable)) together with the
seen check to locate the duplicate and stop execution.
🪄 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: 209aa335-569d-4ed7-8c1a-08a780d09bdd

📥 Commits

Reviewing files that changed from the base of the PR and between b783561 and 6d67e5b.

📒 Files selected for processing (42)
  • cmd/auth/login.go
  • cmd/auth/login_interactive.go
  • cmd/auth/login_test.go
  • cmd/diagnose_scope_test.go
  • cmd/error_auth_hint.go
  • errs/subtypes_shortcut.go
  • errs/subtypes_shortcut_test.go
  • shortcuts/common/argstype/chat_id.go
  • shortcuts/common/argstype/chat_id_test.go
  • shortcuts/common/argstype/media_input.go
  • shortcuts/common/argstype/media_input_test.go
  • shortcuts/common/argstype/safe_path.go
  • shortcuts/common/argstype/safe_path_test.go
  • shortcuts/common/argstype/spreadsheet_ref.go
  • shortcuts/common/argstype/spreadsheet_ref_test.go
  • shortcuts/common/argstype/user_open_id.go
  • shortcuts/common/argstype/user_open_id_list.go
  • shortcuts/common/argstype/user_open_id_list_test.go
  • shortcuts/common/argstype/user_open_id_test.go
  • shortcuts/common/binder.go
  • shortcuts/common/binder_test.go
  • shortcuts/common/protocol.go
  • shortcuts/common/protocol_test.go
  • shortcuts/common/runner.go
  • shortcuts/common/runner_args_test.go
  • shortcuts/common/typed_help.go
  • shortcuts/common/typed_help_test.go
  • shortcuts/common/typed_shortcut.go
  • shortcuts/common/typed_shortcut_test.go
  • shortcuts/common/types.go
  • shortcuts/common/types_test.go
  • shortcuts/im/builders_test.go
  • shortcuts/im/helpers.go
  • shortcuts/im/helpers_test.go
  • shortcuts/im/im_messages_send.go
  • shortcuts/im/im_messages_send_test.go
  • shortcuts/im/protocol.go
  • shortcuts/im/protocol_test.go
  • shortcuts/im/shortcuts.go
  • shortcuts/im/validate_media_test.go
  • shortcuts/register.go
  • shortcuts/register_test.go

Comment thread shortcuts/common/argstype/safe_path.go
Comment thread shortcuts/common/argstype/spreadsheet_ref.go
Comment thread shortcuts/common/argstype/user_open_id_list.go
Comment thread shortcuts/common/binder.go Outdated
Comment thread shortcuts/common/typed_help_test.go Outdated
Comment thread shortcuts/common/typed_help.go
Comment thread shortcuts/common/typed_shortcut_test.go
Comment thread shortcuts/common/typed_shortcut.go
Comment thread shortcuts/im/helpers.go Outdated
Comment thread shortcuts/register_test.go
@sang-neo03 sang-neo03 force-pushed the feat/shortcut-protocol branch from 6d67e5b to bd95794 Compare May 27, 2026 03:03
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
shortcuts/im/helpers.go (1)

92-93: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fail fast on missing local media paths.

Line 92 currently treats missing files as valid input. This defers a user-input error to a later stage and weakens preflight validation.

Suggested fix
 import (
 	"bytes"
 	"context"
 	"encoding/binary"
 	"encoding/json"
 	"fmt"
 	"io"
 	"math"
 	"net/http"
 	"net/url"
-	"os"
 	"path"
 	"path/filepath"
 	"regexp"
 	"strconv"
 	"strings"
@@
 func validateMediaFlagPath(fio fileio.FileIO, flagName, value string) error {
 	if value == "" || strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") || isMediaKey(value) {
 		return nil
 	}
-	if _, err := fio.Stat(value); err != nil && !os.IsNotExist(err) {
+	if _, err := fio.Stat(value); err != nil {
 		return output.ErrValidation("%s: %v", flagName, 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/im/helpers.go` around lines 92 - 93, The preflight validation in
shortcuts/im/helpers.go currently ignores os.IsNotExist errors when statting the
path; change the check in the block that calls fio.Stat(value) (the validation
that returns output.ErrValidation("%s: %v", flagName, err)) to treat any non-nil
err — including os.IsNotExist — as a validation failure so missing local media
paths fail fast; locate the fio.Stat(value) call and remove the special-case
os.IsNotExist(err) condition so the function that returns output.ErrValidation
using flagName and value runs whenever err != nil.
🤖 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.

Duplicate comments:
In `@shortcuts/im/helpers.go`:
- Around line 92-93: The preflight validation in shortcuts/im/helpers.go
currently ignores os.IsNotExist errors when statting the path; change the check
in the block that calls fio.Stat(value) (the validation that returns
output.ErrValidation("%s: %v", flagName, err)) to treat any non-nil err —
including os.IsNotExist — as a validation failure so missing local media paths
fail fast; locate the fio.Stat(value) call and remove the special-case
os.IsNotExist(err) condition so the function that returns output.ErrValidation
using flagName and value runs whenever err != nil.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a01b8809-103e-4b21-a758-f64431a04621

📥 Commits

Reviewing files that changed from the base of the PR and between 6d67e5b and bd95794.

📒 Files selected for processing (42)
  • cmd/auth/login.go
  • cmd/auth/login_interactive.go
  • cmd/auth/login_test.go
  • cmd/diagnose_scope_test.go
  • cmd/error_auth_hint.go
  • errs/subtypes_shortcut.go
  • errs/subtypes_shortcut_test.go
  • shortcuts/common/argstype/chat_id.go
  • shortcuts/common/argstype/chat_id_test.go
  • shortcuts/common/argstype/media_input.go
  • shortcuts/common/argstype/media_input_test.go
  • shortcuts/common/argstype/safe_path.go
  • shortcuts/common/argstype/safe_path_test.go
  • shortcuts/common/argstype/spreadsheet_ref.go
  • shortcuts/common/argstype/spreadsheet_ref_test.go
  • shortcuts/common/argstype/user_open_id.go
  • shortcuts/common/argstype/user_open_id_list.go
  • shortcuts/common/argstype/user_open_id_list_test.go
  • shortcuts/common/argstype/user_open_id_test.go
  • shortcuts/common/binder.go
  • shortcuts/common/binder_test.go
  • shortcuts/common/protocol.go
  • shortcuts/common/protocol_test.go
  • shortcuts/common/runner.go
  • shortcuts/common/runner_args_test.go
  • shortcuts/common/typed_help.go
  • shortcuts/common/typed_help_test.go
  • shortcuts/common/typed_shortcut.go
  • shortcuts/common/typed_shortcut_test.go
  • shortcuts/common/types.go
  • shortcuts/common/types_test.go
  • shortcuts/im/builders_test.go
  • shortcuts/im/helpers.go
  • shortcuts/im/helpers_test.go
  • shortcuts/im/im_messages_send.go
  • shortcuts/im/im_messages_send_test.go
  • shortcuts/im/protocol.go
  • shortcuts/im/protocol_test.go
  • shortcuts/im/shortcuts.go
  • shortcuts/im/validate_media_test.go
  • shortcuts/register.go
  • shortcuts/register_test.go
✅ Files skipped from review due to trivial changes (2)
  • shortcuts/common/argstype/user_open_id.go
  • shortcuts/im/validate_media_test.go
🚧 Files skipped from review as they are similar to previous changes (38)
  • shortcuts/common/types.go
  • errs/subtypes_shortcut.go
  • errs/subtypes_shortcut_test.go
  • shortcuts/common/argstype/safe_path_test.go
  • shortcuts/common/runner_args_test.go
  • shortcuts/common/argstype/spreadsheet_ref_test.go
  • shortcuts/common/argstype/user_open_id_list_test.go
  • shortcuts/common/typed_help_test.go
  • cmd/auth/login_interactive.go
  • cmd/error_auth_hint.go
  • shortcuts/common/argstype/chat_id_test.go
  • shortcuts/common/argstype/user_open_id_test.go
  • shortcuts/common/argstype/spreadsheet_ref.go
  • shortcuts/common/argstype/media_input.go
  • shortcuts/common/argstype/chat_id.go
  • shortcuts/im/shortcuts.go
  • shortcuts/common/types_test.go
  • cmd/auth/login.go
  • cmd/diagnose_scope_test.go
  • shortcuts/common/argstype/user_open_id_list.go
  • shortcuts/im/protocol_test.go
  • shortcuts/common/argstype/safe_path.go
  • shortcuts/im/protocol.go
  • shortcuts/register.go
  • shortcuts/common/protocol.go
  • cmd/auth/login_test.go
  • shortcuts/common/runner.go
  • shortcuts/common/argstype/media_input_test.go
  • shortcuts/im/im_messages_send_test.go
  • shortcuts/common/binder_test.go
  • shortcuts/im/helpers_test.go
  • shortcuts/im/builders_test.go
  • shortcuts/common/typed_shortcut.go
  • shortcuts/common/typed_shortcut_test.go
  • shortcuts/common/binder.go
  • shortcuts/im/im_messages_send.go
  • shortcuts/register_test.go
  • shortcuts/common/typed_help.go

@codecov
Copy link
Copy Markdown

codecov Bot commented May 27, 2026

Codecov Report

❌ Patch coverage is 63.38730% with 294 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.22%. Comparing base (ee9d090) to head (ccf654d).
⚠️ Report is 19 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/common/binder.go 63.82% 100 Missing and 40 partials ⚠️
shortcuts/common/typed_shortcut.go 26.16% 74 Missing and 5 partials ⚠️
shortcuts/common/typed_help.go 63.44% 27 Missing and 7 partials ⚠️
shortcuts/common/argstype/spreadsheet_ref.go 55.26% 15 Missing and 2 partials ⚠️
shortcuts/common/argstype/safe_path.go 68.75% 8 Missing and 2 partials ⚠️
shortcuts/common/argstype/user_open_id_list.go 80.48% 4 Missing and 4 partials ⚠️
cmd/auth/login_interactive.go 60.00% 1 Missing and 1 partial ⚠️
shortcuts/common/argstype/media_input.go 83.33% 1 Missing and 1 partial ⚠️
shortcuts/register.go 93.54% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1110      +/-   ##
==========================================
+ Coverage   67.92%   68.22%   +0.30%     
==========================================
  Files         601      628      +27     
  Lines       55766    58195    +2429     
==========================================
+ Hits        37880    39706    +1826     
- Misses      14751    15188     +437     
- Partials     3135     3301     +166     

☔ 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.

@sang-neo03 sang-neo03 force-pushed the feat/shortcut-protocol branch from bd95794 to c82ad7b Compare May 27, 2026 03:12
Add strongly-typed shortcut protocol that coexists with the legacy
common.Shortcut. The new common.TypedShortcut[T] is a generic outer
wrapper backed by a reflect-driven binder; both legacy and typed
shortcuts satisfy a new common.Mountable / common.ShortcutDescriptor
interface pair so register.go can dispatch either through the same
pipeline.

Framework (shortcuts/common):
- protocol.go — Mountable / ShortcutDescriptor / OneOfMarker /
  Validatable / Normalizable[T] / ArgsValidator / Maybe[T] /
  HelpExample
- binder.go — reflect Args walk, intra-Args flag-tag uniqueness
  panic, cobra flag registration, bindFlags + bindMaybe, runNormalize
  (via MethodByName dispatch — Normalizable[T] can't be type-asserted
  through a non-generic interface), runValidateValue, runFrameworkRules
  for required / enum / OneOf / group
- typed_shortcut.go — TypedShortcut[T] struct, 8 descriptor methods,
  mountTyped adapter that synthesizes a legacy Shortcut shell and
  reuses runShortcut verbatim (identity / scopes / @file / stdin / jq
  / dry-run / high-risk gate)
- typed_help.go — sectioned --help (CHOOSE ONE / OPTIONAL / EXAMPLES)
  with cmdutil.GetRisk/GetTips passthrough so typed shortcuts keep the
  Risk: and Tips: blocks
- runner.go — typedArgs lifecycle slot on RuntimeContext
- types.go — 5 GetX accessors on *Shortcut so legacy shortcuts
  satisfy ShortcutDescriptor alongside the existing pointer-receiver
  scope methods

Typed primitives (shortcuts/common/argstype):
- ChatID / UserOpenID / UserOpenIDList — prefix-validated identifiers
- SafePath — cwd-relative, rejects absolute paths and ".." segments
- MediaInput — tri-state (URL bypass / img_xxx-file_xxx key bypass /
  SafePath delegation)
- SpreadsheetRef — Normalize extracts shtcn token from feishu URLs

Error contract (errs):
- 3 new Subtype constants: shortcut_oneof_missing /
  shortcut_oneof_multiple / shortcut_group_incomplete. Per-field
  failures (required / enum / typed primitive format) reuse the
  existing SubtypeInvalidArgument so no new error type is introduced.

Registry refactor (shortcuts + cmd):
- AllShortcuts() now returns []common.ShortcutDescriptor; legacy
  shortcuts are boxed as *Shortcut (pointer required for the
  pointer-receiver scope methods), typed shortcuts boxed as Mountable
- Register dispatches via the Mountable interface
- cmd/auth/login, cmd/auth/login_interactive, cmd/error_auth_hint,
  cmd/diagnose_scope_test, shortcuts/register_test (shortcuts.json
  generator) updated to read through GetService / GetCommand /
  GetAuthTypes / GetDescription / DeclaredScopesForIdentity
- shortcutSupportsIdentity helpers accept ShortcutDescriptor

Pilot (shortcuts/im):
- protocol.go — MessageTarget / MessageContent (with seven content
  variants) / VideoContent (paired video + cover) / RawContent
  (--content with explicit msg-type, validates JSON in
  ValidateValue)
- im_messages_send.go — migrated to TypedShortcut[*ImMessagesSendArgs].
  Inline Validate closure is replaced by framework-derived checks
  (OneOf target, OneOf content, VideoContent group, typed-primitive
  formats, RawContent JSON). Helpers (resolveMediaContent,
  wrapMarkdownAsPostForDryRun, normalizeAtMentions, etc.) reused
  verbatim; only the field-access pattern changes from
  runtime.Str("x") to args.X.
- shortcuts.go — new TypedShortcuts() exporter; ImMessagesSend
  removed from the legacy Shortcuts() slice so it is not
  double-mounted
- register.go wires addTyped(im.TypedShortcuts()) into init

Known follow-ups:
- runFrameworkRules and bindFlags do not recurse into OneOf bucket /
  group sub-structs; im messages-send compensates with a local
  bindMessagesSendArgs + validateVideoGroup. Generalizing the binder
  to recurse will let future migrations drop the local shim.
- common.ValidateChatID / common.ValidateUserID become redundant
  once all legacy shortcuts that call them migrate; can be retired
  with the last legacy caller.

Refs: docs/superpowers/specs/2026-05-26-shortcut-protocol-design.md
@sang-neo03 sang-neo03 force-pushed the feat/shortcut-protocol branch from c82ad7b to a07239b Compare May 27, 2026 03:32
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/im/im_messages_send_test.go`:
- Around line 40-49: The test currently only compares lengths and iterates
values which allows duplicates like ["bot","bot"] to pass; change the assertion
to compare unique auth types by converting ImMessagesSend.GetAuthTypes() into a
map/set (e.g., seen := map[string]bool{}) then ensure every expected key in
wantAuth exists in seen and that seen has no unexpected keys and equal
cardinality to wantAuth; use ImMessagesSend.GetAuthTypes to locate the call and
update the loop/checks to detect duplicates and missing members.
🪄 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: 71545eb9-59c3-4571-8da0-3176d56f82db

📥 Commits

Reviewing files that changed from the base of the PR and between c82ad7b and a07239b.

📒 Files selected for processing (42)
  • cmd/auth/login.go
  • cmd/auth/login_interactive.go
  • cmd/auth/login_test.go
  • cmd/diagnose_scope_test.go
  • cmd/error_auth_hint.go
  • errs/subtypes_shortcut.go
  • errs/subtypes_shortcut_test.go
  • shortcuts/common/argstype/chat_id.go
  • shortcuts/common/argstype/chat_id_test.go
  • shortcuts/common/argstype/media_input.go
  • shortcuts/common/argstype/media_input_test.go
  • shortcuts/common/argstype/safe_path.go
  • shortcuts/common/argstype/safe_path_test.go
  • shortcuts/common/argstype/spreadsheet_ref.go
  • shortcuts/common/argstype/spreadsheet_ref_test.go
  • shortcuts/common/argstype/user_open_id.go
  • shortcuts/common/argstype/user_open_id_list.go
  • shortcuts/common/argstype/user_open_id_list_test.go
  • shortcuts/common/argstype/user_open_id_test.go
  • shortcuts/common/binder.go
  • shortcuts/common/binder_test.go
  • shortcuts/common/protocol.go
  • shortcuts/common/protocol_test.go
  • shortcuts/common/runner.go
  • shortcuts/common/runner_args_test.go
  • shortcuts/common/typed_help.go
  • shortcuts/common/typed_help_test.go
  • shortcuts/common/typed_shortcut.go
  • shortcuts/common/typed_shortcut_test.go
  • shortcuts/common/types.go
  • shortcuts/common/types_test.go
  • shortcuts/im/builders_test.go
  • shortcuts/im/helpers.go
  • shortcuts/im/helpers_test.go
  • shortcuts/im/im_messages_send.go
  • shortcuts/im/im_messages_send_test.go
  • shortcuts/im/protocol.go
  • shortcuts/im/protocol_test.go
  • shortcuts/im/shortcuts.go
  • shortcuts/im/validate_media_test.go
  • shortcuts/register.go
  • shortcuts/register_test.go
✅ Files skipped from review due to trivial changes (1)
  • shortcuts/im/validate_media_test.go
🚧 Files skipped from review as they are similar to previous changes (37)
  • shortcuts/common/typed_help_test.go
  • shortcuts/common/argstype/chat_id.go
  • shortcuts/common/runner_args_test.go
  • errs/subtypes_shortcut_test.go
  • cmd/auth/login.go
  • cmd/auth/login_interactive.go
  • shortcuts/common/types_test.go
  • shortcuts/common/argstype/spreadsheet_ref.go
  • shortcuts/im/helpers.go
  • shortcuts/common/runner.go
  • shortcuts/common/argstype/spreadsheet_ref_test.go
  • shortcuts/common/protocol.go
  • shortcuts/common/argstype/media_input_test.go
  • shortcuts/common/argstype/user_open_id_list.go
  • shortcuts/im/builders_test.go
  • shortcuts/common/argstype/user_open_id_test.go
  • cmd/diagnose_scope_test.go
  • errs/subtypes_shortcut.go
  • shortcuts/common/typed_help.go
  • shortcuts/im/protocol.go
  • shortcuts/common/typed_shortcut.go
  • shortcuts/common/argstype/user_open_id.go
  • shortcuts/common/argstype/safe_path.go
  • shortcuts/common/argstype/chat_id_test.go
  • shortcuts/common/protocol_test.go
  • shortcuts/im/protocol_test.go
  • shortcuts/common/typed_shortcut_test.go
  • cmd/error_auth_hint.go
  • shortcuts/common/types.go
  • shortcuts/register.go
  • cmd/auth/login_test.go
  • shortcuts/register_test.go
  • shortcuts/im/helpers_test.go
  • shortcuts/common/binder_test.go
  • shortcuts/common/binder.go
  • shortcuts/common/argstype/media_input.go
  • shortcuts/im/im_messages_send.go

Comment thread shortcuts/im/im_messages_send_test.go Outdated
- im_messages_send_test: assert auth-type set membership (reject
  duplicates / missing members) per CodeRabbit review
- binder_coverage_test: cover bindMaybe, bindBuckets / bindBucketInner,
  bucketLeafValue, runNormalize / asString, checkGroup,
  checkEnumAndRequired, and group recursion in runValidateValue — lifts
  patch coverage over the 60% gate
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
shortcuts/common/binder_coverage_test.go (2)

159-171: ⚡ Quick win

Assert pointer normalization explicitly.

TokenPtr is set up to exercise pointer normalization but never asserted. Add a direct check so this branch is validated, not just executed.

Proposed assertion
 if args.Token != "c:raw" {
   t.Errorf("Token = %q, want c:raw", args.Token)
 }
+if args.TokenPtr == nil || *args.TokenPtr != "c:xy" {
+  t.Errorf("TokenPtr = %v, want c:xy", args.TokenPtr)
+}
 if got := buf.String(); got == "" || !bytes.Contains([]byte(got), []byte("hint: raw")) {
   t.Errorf("stderr = %q, want it to contain the normalize hint", got)
 }
🤖 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/common/binder_coverage_test.go` around lines 159 - 171, After
calling runNormalize with bcNormArgs (ptr created by bcNormField("xy")), add an
explicit assertion that the pointer-normalized value was updated: check the
dereferenced TokenPtr on args (e.g., *args.TokenPtr) equals the expected
normalized string ("c:xy") and fail the test with t.Fatalf or t.Errorf if it
does not match; reference bcNormField, bcNormArgs, TokenPtr, runNormalize and
args when locating where to add this assertion.

28-29: ⚡ Quick win

Stop discarding setup errors in tests.

Several tests ignore errors from walkArgs, registerFlags, and ParseFlags. This can mask regressions and make failures harder to diagnose.

Suggested pattern to apply across this file
- specs, _ := walkArgs(reflect.TypeOf(&bcMaybeArgs{}))
- _ = registerFlags(cmd, specs)
- _ = cmd.ParseFlags([]string{"--m-on"})
+ specs, err := walkArgs(reflect.TypeOf(&bcMaybeArgs{}))
+ if err != nil {
+   t.Fatalf("walkArgs: %v", err)
+ }
+ if err := registerFlags(cmd, specs); err != nil {
+   t.Fatalf("registerFlags: %v", err)
+ }
+ if err := cmd.ParseFlags([]string{"--m-on"}); err != nil {
+   t.Fatalf("ParseFlags: %v", err)
+ }

Also applies to: 53-54, 119-120, 161-162, 180-180, 201-204, 217-217, 221-223, 243-243, 247-248, 259-260, 267-268, 286-286

🤖 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/common/binder_coverage_test.go` around lines 28 - 29, The tests are
discarding errors from setup calls (e.g., walkArgs, registerFlags, ParseFlags)
which can hide failures; update each call site (such as the
walkArgs(reflect.TypeOf(&bcMaybeArgs{})) call, registerFlags(cmd, specs), and
any ParseFlags uses) to check the returned error and fail the test immediately
(use t.Fatalf or t.Fatalf-like helper or testify/require.NoError) with a clear
message including the error; ensure you propagate and assert errors for all
occurrences listed so test setup failures cause visible test failures.
🤖 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.

Nitpick comments:
In `@shortcuts/common/binder_coverage_test.go`:
- Around line 159-171: After calling runNormalize with bcNormArgs (ptr created
by bcNormField("xy")), add an explicit assertion that the pointer-normalized
value was updated: check the dereferenced TokenPtr on args (e.g.,
*args.TokenPtr) equals the expected normalized string ("c:xy") and fail the test
with t.Fatalf or t.Errorf if it does not match; reference bcNormField,
bcNormArgs, TokenPtr, runNormalize and args when locating where to add this
assertion.
- Around line 28-29: The tests are discarding errors from setup calls (e.g.,
walkArgs, registerFlags, ParseFlags) which can hide failures; update each call
site (such as the walkArgs(reflect.TypeOf(&bcMaybeArgs{})) call,
registerFlags(cmd, specs), and any ParseFlags uses) to check the returned error
and fail the test immediately (use t.Fatalf or t.Fatalf-like helper or
testify/require.NoError) with a clear message including the error; ensure you
propagate and assert errors for all occurrences listed so test setup failures
cause visible test failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 77a27179-29fe-4765-9b55-186259dd15c4

📥 Commits

Reviewing files that changed from the base of the PR and between a07239b and ad4368e.

📒 Files selected for processing (2)
  • shortcuts/common/binder_coverage_test.go
  • shortcuts/im/im_messages_send_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • shortcuts/im/im_messages_send_test.go

The im +messages-send TypedShortcut pilot was exploratory only; revert it
while retaining the TypedShortcut framework for future migrations.

- shortcuts/im: restored to pre-pilot state (im_messages_send.go back to
  legacy common.Shortcut; deleted protocol.go, protocol_test.go,
  im_messages_send_test.go; shortcuts.go drops TypedShortcuts()).
- shortcuts/register.go: removed addTyped(im.TypedShortcuts()) wiring and
  the now-unused addTyped helper; legacy addLegacy + Mountable dispatch
  retained.
- shortcuts/common, argstype, errs subtypes, cmd/auth adapters: kept.
- framework doc comments: replaced examples referencing the removed pilot
  types (MessageTarget/MessageContent/VideoContent/RawContent) with neutral
  descriptions; noted no typed shortcut is registered today.

Framework now has no production caller. Verified: go build ./... and
go test ./shortcuts/... ./errs/... ./cmd/auth/... ./cmd/ all pass.
@github-actions github-actions Bot removed the domain/im PR touches the im domain label May 27, 2026
@sang-neo03 sang-neo03 changed the title feat: introduce typed shortcut framework + im send pilot feat(shortcuts): introduce TypedShortcut framework May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature size/XL Architecture-level or global-impact change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant