Skip to content

feat(skills): allow replacing non-builtin skill auth without re-uploa… - #2223

Merged
jsers merged 1 commit into
mainfrom
feat-skill-0729
Aug 3, 2026
Merged

feat(skills): allow replacing non-builtin skill auth without re-uploa…#2223
jsers merged 1 commit into
mainfrom
feat-skill-0729

Conversation

@jsers

@jsers jsers commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

…ding

The replace dialog for non-builtin skills now defers submission until the user clicks OK, so they can update only the managing team and visibility without re-selecting an archive. Builtin skills keep the existing pick-to-upload flow. The detail panel also surfaces the managing team and visibility in its meta section.

Summary by CodeRabbit

  • New Features

    • Added support for replacing eligible skills without uploading a new file.
    • Improved upload confirmation, validation, retry, and loading behaviors.
    • Skill details now display management teams and visibility information when available.
    • Updated edit/replace actions and metadata layouts for clearer presentation.
  • Bug Fixes

    • Prevented imports from running when no file is selected.
    • Added localized messages for save failures, file selection, upload requirements, and unsupported formats across supported languages.

…ding

The replace dialog for non-builtin skills now defers submission until
the user clicks OK, so they can update only the managing team and
visibility without re-selecting an archive. Builtin skills keep the
existing pick-to-upload flow. The detail panel also surfaces the
managing team and visibility in its meta section.
Copilot AI review requested due to automatic review settings July 29, 2026 07:04
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Skill management flow now supports optional-file replacement, authorization-aware updates, team and visibility metadata in the detail panel, and expanded upload-modal translations across five locales.

Changes

Skill management flow

Layer / File(s) Summary
Upload modal submission flow
src/pages/aiConfig/skills/pages/UploadSkillModal.tsx, src/pages/aiConfig/skills/locale/*
The modal stages files for authenticated confirmation, supports empty-file submission when enabled, validates authorization fields, and adds upload and error messages across locales.
Optional-file import and update integration
src/pages/aiConfig/skills/pages/SkillSidebar.tsx, src/pages/aiConfig/skills/pages/SkillDetailPanel.tsx, src/pages/aiConfig/skills/pages/List.tsx
Import callbacks accept optional files; empty-file updates fetch the current skill and persist authorization-derived fields, while file-based imports retain their existing path.
Skill detail metadata and actions
src/pages/aiConfig/skills/pages/SkillDetailPanel.tsx
Team information is fetched and displayed alongside visibility metadata, and edit/update actions and modal configuration are adjusted for builtin and non-builtin skills.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant UploadSkillModal
  participant SkillDetailPanel
  participant List
  User->>UploadSkillModal: select archive or confirm without file
  UploadSkillModal->>SkillDetailPanel: submit file and authorization
  SkillDetailPanel->>List: import or update skill
  List->>List: refresh skill and show result
Loading

Possibly related PRs

  • n9e/fe#2174: Introduces the authorization values and team/scope handling used by this Skill import and update flow.

Suggested reviewers: 710leo

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: enabling non-builtin skill replacement without re-uploading the archive.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-skill-0729

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the AI Skills UI to support replacing non-builtin skills’ auth settings (managing team + visibility) without requiring the user to re-upload an archive, and surfaces managing team + visibility in the skill detail meta section.

Changes:

  • Update UploadSkillModal to support submitting auth changes with an optional file (for non-builtin replace), while keeping builtin skills’ “pick-to-upload” flow.
  • Extend import/replace handlers to accept File | undefined, and add an auth-only update path when no file is provided.
  • Display managing team and visibility in SkillDetailPanel meta.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/pages/aiConfig/skills/pages/UploadSkillModal.tsx Adds optional-file submission + confirm-based flow for auth fields; keeps builtin immediate upload flow.
src/pages/aiConfig/skills/pages/SkillSidebar.tsx Updates onImport typing to accept `File
src/pages/aiConfig/skills/pages/SkillDetailPanel.tsx Fetches team list and renders managing team + visibility in meta; adjusts replace menu icon/text.
src/pages/aiConfig/skills/pages/List.tsx Supports auth-only replace (PUT) when no file is provided; updates success/error messaging.
src/pages/aiConfig/skills/locale/zh_HK.ts Adds new strings for auth-only save and file-required validation.
src/pages/aiConfig/skills/locale/zh_CN.ts Adds new strings for auth-only save and file-required validation.
src/pages/aiConfig/skills/locale/ru_RU.ts Adds new strings for auth-only save and file-required validation.
src/pages/aiConfig/skills/locale/ja_JP.ts Adds new strings for auth-only save and file-required validation.
src/pages/aiConfig/skills/locale/en_US.ts Adds new strings for auth-only save and file-required validation.

Comment on lines +116 to 121
onOk={handleConfirm}
okText={t('common:btn.ok')}
cancelText={t('common:btn.cancel')}
confirmLoading={submitting}
footer={showAuthFields ? undefined : null}
width={980}
Comment on lines +64 to +76
React.useEffect(() => {
if (!item.user_group_ids) {
setUserGroups([]);
return;
}
getTeamInfoList()
.then((res) => {
setUserGroups(res.dat ?? []);
})
.catch(() => {
setUserGroups([]);
});
}, [item.user_group_ids]);

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/pages/aiConfig/skills/pages/List.tsx (2)

171-204: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Refresh failure is misreported as a save failure, risking duplicate non-idempotent writes on retry.

Both handleImport and handleUpdateImport wrap the actual write (importItem/importItemToUpdate/putItem) together with the follow-up refresh (run()/refreshSkill()) in one try/catch. If the write succeeds but only the refresh call fails, the catch shows an error toast and rethrows, causing UploadSkillModal.submitFile to keep the modal open for a retry — even though the save already succeeded. Retrying handleImport in particular resubmits importItem, a presumably non-idempotent create call, risking a duplicate skill.

🛡️ Suggested fix
   async function handleImport(file: File | undefined, auth: SkillAuthValues) {
     if (!file) {
       return;
     }
     try {
       await importItem(file, auth);
-      await run();
-      message.success(t('upload_file_success'));
     } catch (_error) {
       message.error(t('upload_file_error'));
       throw _error;
     }
+    message.success(t('upload_file_success'));
+    try {
+      await run();
+    } catch (error) {
+      console.error(error);
+    }
   }

   async function handleUpdateImport(skillId: number, file: File | undefined, auth: SkillAuthValues) {
     try {
       if (file) {
         await importItemToUpdate(skillId, file, auth);
       } else {
         const currentSkill = await getItem(skillId);
         await putItem(skillId, {
           ..._.pick(currentSkill, ['name', 'description', 'instructions', 'license', 'compatibility', 'allowed_tools', 'metadata']),
           enabled: currentSkill.enabled,
           user_group_ids: auth.user_group_ids ?? currentSkill.user_group_ids,
           private: auth.private ?? currentSkill.private ?? 1,
         });
       }
-      await refreshSkill(skillId);
-      message.success(file ? t('upload_file_success') : t('common:success.modify'));
     } catch (_error) {
       message.error(file ? t('upload_file_error') : t('modify_error'));
       throw _error;
     }
+    message.success(file ? t('upload_file_success') : t('common:success.modify'));
+    try {
+      await refreshSkill(skillId);
+    } catch (error) {
+      console.error(error);
+    }
   }
🤖 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 `@src/pages/aiConfig/skills/pages/List.tsx` around lines 171 - 204, Separate
the write operations from the follow-up refresh calls in handleImport and
handleUpdateImport. Keep importItem, importItemToUpdate, and putItem inside the
save error handling so write failures still show the existing save errors, but
run run() or refreshSkill() after a successful save without rethrowing it as a
save failure or keeping the upload modal in retry state; preserve the existing
success messaging for completed writes.

451-453: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Missing return breaks the new "keep modal open on failure" retry safety.

This wrapper synchronously returns undefined instead of the promise from handleUpdateImport. Because UploadSkillModal.submitFile does await onSubmit(file, auth), the await resolves immediately and the modal closes (state cleared, onCancel() called) before the actual PUT/import call even completes — regardless of success or failure. This silently defeats the new retry-on-error behavior added in UploadSkillModal.tsx (the modal is designed to stay open on failure so the user can retry, but here it closes unconditionally). SkillSidebar.tsx's onSubmit={onImport} avoids this by passing the real async function reference directly.

🐛 Suggested fix
-                      onImport={(file, auth) => {
-                        handleUpdateImport(selectedSkillData.id, file, auth);
-                      }}
+                      onImport={(file, auth) => handleUpdateImport(selectedSkillData.id, file, auth)}
🤖 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 `@src/pages/aiConfig/skills/pages/List.tsx` around lines 451 - 453, Return the
promise from handleUpdateImport in the onImport wrapper in List.tsx by making
the callback expression propagate its result. Preserve the existing
selectedSkillData.id, file, and auth arguments so UploadSkillModal.submitFile
can await completion and keep the modal open when the import fails.
🧹 Nitpick comments (1)
src/pages/aiConfig/skills/pages/SkillDetailPanel.tsx (1)

64-77: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Redundant getTeamInfoList() calls due to array-reference dependency.

item.user_group_ids is a fresh array reference on every getItem/refreshSkill fetch, even when its contents are unchanged. Since this effect depends on that reference, it re-fetches the entire team list on every skill switch and every detail refresh (toggle, replace, update), not just when relevant data actually changes. SkillAuthFields.tsx fetches the same list once with an empty dependency array — consider following that pattern here instead of tying it to item.user_group_ids.

♻️ Suggested fix
   React.useEffect(() => {
-    if (!item.user_group_ids) {
-      setUserGroups([]);
-      return;
-    }
     getTeamInfoList()
       .then((res) => {
         setUserGroups(res.dat ?? []);
       })
       .catch(() => {
         setUserGroups([]);
       });
-  }, [item.user_group_ids]);
+  }, []);
🤖 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 `@src/pages/aiConfig/skills/pages/SkillDetailPanel.tsx` around lines 64 - 77,
Update the React.useEffect in SkillDetailPanel to avoid depending on the array
reference item.user_group_ids, which changes on every refresh. Follow the
existing SkillAuthFields pattern by fetching getTeamInfoList once with an empty
dependency array, while preserving the current success, error, and missing-data
handling.
🤖 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.

Outside diff comments:
In `@src/pages/aiConfig/skills/pages/List.tsx`:
- Around line 171-204: Separate the write operations from the follow-up refresh
calls in handleImport and handleUpdateImport. Keep importItem,
importItemToUpdate, and putItem inside the save error handling so write failures
still show the existing save errors, but run run() or refreshSkill() after a
successful save without rethrowing it as a save failure or keeping the upload
modal in retry state; preserve the existing success messaging for completed
writes.
- Around line 451-453: Return the promise from handleUpdateImport in the
onImport wrapper in List.tsx by making the callback expression propagate its
result. Preserve the existing selectedSkillData.id, file, and auth arguments so
UploadSkillModal.submitFile can await completion and keep the modal open when
the import fails.

---

Nitpick comments:
In `@src/pages/aiConfig/skills/pages/SkillDetailPanel.tsx`:
- Around line 64-77: Update the React.useEffect in SkillDetailPanel to avoid
depending on the array reference item.user_group_ids, which changes on every
refresh. Follow the existing SkillAuthFields pattern by fetching getTeamInfoList
once with an empty dependency array, while preserving the current success,
error, and missing-data handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5ed2c25d-7885-456e-bf77-ba8cb5f68a83

📥 Commits

Reviewing files that changed from the base of the PR and between 33bb3a3 and a061d1c.

📒 Files selected for processing (9)
  • src/pages/aiConfig/skills/locale/en_US.ts
  • src/pages/aiConfig/skills/locale/ja_JP.ts
  • src/pages/aiConfig/skills/locale/ru_RU.ts
  • src/pages/aiConfig/skills/locale/zh_CN.ts
  • src/pages/aiConfig/skills/locale/zh_HK.ts
  • src/pages/aiConfig/skills/pages/List.tsx
  • src/pages/aiConfig/skills/pages/SkillDetailPanel.tsx
  • src/pages/aiConfig/skills/pages/SkillSidebar.tsx
  • src/pages/aiConfig/skills/pages/UploadSkillModal.tsx

@jsers
jsers merged commit be09e20 into main Aug 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants