Skip to content

fix: support project-scoped skills sync#1058

Open
hehanlin1996 wants to merge 1 commit into
larksuite:mainfrom
hehanlin1996:ai-fix-20260523-skills-project-scope
Open

fix: support project-scoped skills sync#1058
hehanlin1996 wants to merge 1 commit into
larksuite:mainfrom
hehanlin1996:ai-fix-20260523-skills-project-scope

Conversation

@hehanlin1996
Copy link
Copy Markdown

@hehanlin1996 hehanlin1996 commented May 23, 2026

Summary

Adds a project-scoped skills sync option for the update/install flows so users can keep lark-* skills out of the global agent skill namespace when desired. Default behavior remains global for compatibility.

Changes

  • Add lark-cli update --project and plumb project scope through the self-update skills sync path.
  • Use npx skills add ... -p when project scope is requested, while preserving -g by default.
  • Add the same --project scope to the npm install wizard and retry command text.
  • Avoid treating a project-scoped sync as a global skills stamp update.

Test Plan

  • go test ./internal/selfupdate ./cmd/update
  • node --test scripts/install-wizard.test.js
  • node --check scripts/install-wizard.js

Related Issues

Summary by CodeRabbit

  • New Features
    • Added --project flag to the lark-cli update command, allowing AI skills to be installed at project scope instead of globally.
    • Skills syncing now respects the chosen scope with scope-aware command guidance in output messages.

Review Change Stack

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


hehanlin.lq seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

📝 Walkthrough

Walkthrough

This PR extends lark-cli update with a --project flag to synchronize AI skills at project scope (npx -y skills add -p) instead of globally (-g). The Updater service gains a RunSkillsUpdateWithScope(project bool) method with corresponding override hooks. All skills-sync call sites in the update command and install wizard thread the scope flag through, with scope-aware command formatting in output messages and hints.

Changes

Project-Scoped Skills Synchronization

Layer / File(s) Summary
UpdateOptions and CLI flag wiring
cmd/update/update.go
Add ProjectSkills boolean field to UpdateOptions struct and register the --project boolean flag bound to that field.
Updater struct overrides and scoped method
internal/selfupdate/updater.go
Extend Updater struct with SkillsUpdateWithScopeOverride override hook and RestoreAvailableOverride; implement RunSkillsUpdateWithScope(project bool) method that respects override precedence and wires scope into skills-install flow; add skillsAddArgs(source, project) helper to generate correct -g vs -p CLI arguments.
Update command scope-aware flows
cmd/update/update.go
Thread opts.ProjectSkills through all update paths: "already up to date" (non---check) path calls runSkillsAndStamp with scope; manual-install and npm-update success paths pass scope to skills runner; verification failure hint generates scope-aware skills reinstall guidance; runSkillsAndStamp selects between scoped override and unscoped fallback while preserving stamp dedup/force semantics; emitSkillsTextHints prints scope-specific "run manually" command via new skillsManualCommand helper.
Update command test updates and new tests
cmd/update/update_test.go
Update all runSkillsAndStamp test call sites to use new two-boolean signature (dedup/force, projectSkills) across dedup-hit, force-bypass, success, and failure scenarios; add TestRunSkillsAndStamp_ProjectScopeBypassesGlobalStamp to verify scoped skills use SkillsUpdateWithScopeOverride without mutating global stamp; add TestUpdateProjectFlagPassesProjectScope to verify --project flag triggers scoped update with project=true; update TestEmitSkillsTextHints_Success with new signature and fix rollback hint expectation string.
Updater helper tests
internal/selfupdate/updater_test.go
Add reflect import and introduce TestSkillsAddArgs_GlobalScope and TestSkillsAddArgs_ProjectScope to verify skillsAddArgs constructs correct argument slices with -g vs -p flags.
Install wizard project scope support
scripts/install-wizard.js
Refactor to accept projectScope parameter throughout; add test-mode conditional require and helper exports (parseProjectArg, skillsScopeFlag, skillsAddArgs, skillsAddCommand); update Chinese/English failure message templates with %s placeholders for scope-aware manual commands; refactor skillsAlreadyInstalled and stepInstallSkills to use skillsScopeFlag(projectScope) for -p vs -g selection; update main() to parse --project argument and pass projectScope into both interactive and non-interactive install flows.
Install wizard test suite
scripts/install-wizard.test.js
New test file using node:test to verify scope flag selection (skillsScopeFlag()), argument array generation (skillsAddArgs()), and retry command formatting (skillsAddCommand()) for both global and project scopes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The changes introduce a new feature flag (--project) that threads through multiple layers (CLI, updater service, command flows, output messaging) with consistent scope-aware parameter passing. The logic is straightforward (selecting -p vs -g flags based on boolean scope), but the feature spans heterogeneous files (Go commands, Go services, JavaScript install wizard) with test coverage across all layers. Moderate complexity due to multi-file integration and multiple independent test suites, but the pattern is repetitive and the changes do not introduce dense conditional logic.

Possibly related issues

  • larksuite/cli#1055: The PR directly implements the --project flag feature to switch skills installs between global (-g) and project (-p) scope across the updater, update command, and install-wizard scripts—addressing the same code paths and use case described in this issue.

Possibly related PRs

  • larksuite/cli#464: Both PRs touch scripts/install-wizard.js, with this PR extending the wizard's skills-install flow to support --project scope flag (-p vs -g) while the retrieved PR introduces the initial wizard implementation.
  • larksuite/cli#723: Both PRs modify cmd/update/update.go's shared skills synchronization logic (the runSkillsAndStamp flow) and related skills-update behavior, so the scope-aware changes here are directly connected to that refactor.
  • larksuite/cli#1008: Both PRs modify the cmd/update/update.go skills synchronization path by extending the runSkillsAndStamp stamp-based mechanism—this PR adds --project scoping to the same helper.

Suggested labels

size/L, feature

Suggested reviewers

  • liangshuo-1

🐰 A flag to choose your skills' scope—
Global reach, or nested, cozy home—
The update command now respects your way,
Whether -g spreads far or -p stays—
Hop along, dear friend, and sync with grace! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding support for project-scoped skills sync, which is the primary objective of this PR.
Description check ✅ Passed The description includes all required sections: Summary explains motivation, Changes lists key modifications, Test Plan documents verification steps, and Related Issues references the relevant issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label May 23, 2026
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

🧹 Nitpick comments (1)
scripts/install-wizard.test.js (1)

14-37: ⚡ Quick win

Add direct tests for parseProjectArg to lock CLI flag parsing.

The suite covers scope formatting well, but it doesn’t test the argv parser that actually drives main() behavior. Add present/absent --project cases to avoid regressions.

✅ Suggested test addition
 const {
+  parseProjectArg,
   skillsAddArgs,
   skillsAddCommand,
   skillsScopeFlag,
 } = require("./install-wizard.js");

 describe("install wizard skills scope", () => {
+  it("parses --project from argv", () => {
+    const oldArgv = process.argv;
+    process.argv = ["node", "install-wizard.js", "--project"];
+    assert.equal(parseProjectArg(), true);
+    process.argv = ["node", "install-wizard.js"];
+    assert.equal(parseProjectArg(), false);
+    process.argv = oldArgv;
+  });
+
   it("keeps global skills install by default", () => {
🤖 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 `@scripts/install-wizard.test.js` around lines 14 - 37, Add direct unit tests
for the argv parser by exercising parseProjectArg with both present and absent
--project flag to lock CLI flag parsing; specifically, add tests that call
parseProjectArg(["--project"]) (or equivalent invocation used in your CLI
parsing functions) and assert it returns true, and call parseProjectArg([]) (no
flag) and assert it returns false, so regressions in main()/CLI parsing are
caught—look for and update the test suite near existing scope tests that
reference skillsScopeFlag, skillsAddArgs and skillsAddCommand to include these
parseProjectArg present/absent cases.
🤖 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/update/update_test.go`:
- Around line 964-989: The test currently seeds the same stamp value ("1.0.21")
that runSkillsAndStamp is invoked with, so it cannot detect accidental writes;
change the initial seed written by skillscheck.WriteStamp to a different version
(e.g., "1.0.20") while keeping runSkillsAndStamp invoked with "1.0.21", then
after calling runSkillsAndStamp verify via skillscheck.ReadStamp that the global
stamp still equals the original seeded value (the new "1.0.20"), referencing the
test helpers and overrides: skillscheck.WriteStamp, runSkillsAndStamp,
selfupdate.Updater.SkillsUpdateWithScopeOverride, and skillscheck.ReadStamp.

---

Nitpick comments:
In `@scripts/install-wizard.test.js`:
- Around line 14-37: Add direct unit tests for the argv parser by exercising
parseProjectArg with both present and absent --project flag to lock CLI flag
parsing; specifically, add tests that call parseProjectArg(["--project"]) (or
equivalent invocation used in your CLI parsing functions) and assert it returns
true, and call parseProjectArg([]) (no flag) and assert it returns false, so
regressions in main()/CLI parsing are caught—look for and update the test suite
near existing scope tests that reference skillsScopeFlag, skillsAddArgs and
skillsAddCommand to include these parseProjectArg present/absent cases.
🪄 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: e7abb9bc-464a-44b9-8173-9b236828066f

📥 Commits

Reviewing files that changed from the base of the PR and between 6e3e120 and 9d3b872.

📒 Files selected for processing (6)
  • cmd/update/update.go
  • cmd/update/update_test.go
  • internal/selfupdate/updater.go
  • internal/selfupdate/updater_test.go
  • scripts/install-wizard.js
  • scripts/install-wizard.test.js

Comment thread cmd/update/update_test.go
Comment on lines +964 to +989
if err := skillscheck.WriteStamp("1.0.21"); err != nil {
t.Fatal(err)
}
called := false
projectScope := false
updater := &selfupdate.Updater{
SkillsUpdateWithScopeOverride: func(project bool) *selfupdate.NpmResult {
called = true
projectScope = project
return &selfupdate.NpmResult{}
},
}
got := runSkillsAndStamp(updater, newTestIO(), "1.0.21", false, true)
if got == nil || got.Err != nil {
t.Fatalf("runSkillsAndStamp(project=true) = %+v, want non-nil success", got)
}
if !called {
t.Fatal("RunSkillsUpdateWithScope not called for project scope")
}
if !projectScope {
t.Fatal("RunSkillsUpdateWithScope called with project=false, want true")
}
stamp, _ := skillscheck.ReadStamp()
if stamp != "1.0.21" {
t.Errorf("project scope must not mutate global stamp, got %q", stamp)
}
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

Project-scope stamp assertion is currently non-detecting

This test seeds and verifies the same stamp value (1.0.21), so it still passes if project-scope accidentally writes the global stamp. Seed with a different version and assert it stays unchanged.

Suggested test tightening
-	if err := skillscheck.WriteStamp("1.0.21"); err != nil {
+	if err := skillscheck.WriteStamp("1.0.20"); err != nil {
 		t.Fatal(err)
 	}
@@
-	got := runSkillsAndStamp(updater, newTestIO(), "1.0.21", false, true)
+	got := runSkillsAndStamp(updater, newTestIO(), "1.0.21", false, true)
@@
-	if stamp != "1.0.21" {
+	if stamp != "1.0.20" {
 		t.Errorf("project scope must not mutate global stamp, got %q", stamp)
 	}
📝 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 := skillscheck.WriteStamp("1.0.21"); err != nil {
t.Fatal(err)
}
called := false
projectScope := false
updater := &selfupdate.Updater{
SkillsUpdateWithScopeOverride: func(project bool) *selfupdate.NpmResult {
called = true
projectScope = project
return &selfupdate.NpmResult{}
},
}
got := runSkillsAndStamp(updater, newTestIO(), "1.0.21", false, true)
if got == nil || got.Err != nil {
t.Fatalf("runSkillsAndStamp(project=true) = %+v, want non-nil success", got)
}
if !called {
t.Fatal("RunSkillsUpdateWithScope not called for project scope")
}
if !projectScope {
t.Fatal("RunSkillsUpdateWithScope called with project=false, want true")
}
stamp, _ := skillscheck.ReadStamp()
if stamp != "1.0.21" {
t.Errorf("project scope must not mutate global stamp, got %q", stamp)
}
if err := skillscheck.WriteStamp("1.0.20"); err != nil {
t.Fatal(err)
}
called := false
projectScope := false
updater := &selfupdate.Updater{
SkillsUpdateWithScopeOverride: func(project bool) *selfupdate.NpmResult {
called = true
projectScope = project
return &selfupdate.NpmResult{}
},
}
got := runSkillsAndStamp(updater, newTestIO(), "1.0.21", false, true)
if got == nil || got.Err != nil {
t.Fatalf("runSkillsAndStamp(project=true) = %+v, want non-nil success", got)
}
if !called {
t.Fatal("RunSkillsUpdateWithScope not called for project scope")
}
if !projectScope {
t.Fatal("RunSkillsUpdateWithScope called with project=false, want true")
}
stamp, _ := skillscheck.ReadStamp()
if stamp != "1.0.20" {
t.Errorf("project scope must not mutate global stamp, got %q", stamp)
}
🤖 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/update/update_test.go` around lines 964 - 989, The test currently seeds
the same stamp value ("1.0.21") that runSkillsAndStamp is invoked with, so it
cannot detect accidental writes; change the initial seed written by
skillscheck.WriteStamp to a different version (e.g., "1.0.20") while keeping
runSkillsAndStamp invoked with "1.0.21", then after calling runSkillsAndStamp
verify via skillscheck.ReadStamp that the global stamp still equals the original
seeded value (the new "1.0.20"), referencing the test helpers and overrides:
skillscheck.WriteStamp, runSkillsAndStamp,
selfupdate.Updater.SkillsUpdateWithScopeOverride, and skillscheck.ReadStamp.

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

Labels

size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants