Skip to content

feat(site/src/pages/AgentsPage): add download and export for personal skills - #28032

Open
Shelnutt2 wants to merge 2 commits into
mainfrom
codagt-918-download-export-skills
Open

feat(site/src/pages/AgentsPage): add download and export for personal skills#28032
Shelnutt2 wants to merge 2 commits into
mainfrom
codagt-918-download-export-skills

Conversation

@Shelnutt2

Copy link
Copy Markdown
Contributor

Summary

Adds a way to get personal skills back out of Coder Agents as files, so
sharing a skill no longer means pasting SKILL.md by hand.

  • Per-skill Download: a Download action on each row of the Personal
    skills
    settings page saves that skill's SKILL.md as <name>.md.
  • Export all: a header button zips every personal skill (each as
    <name>/SKILL.md) and downloads personal-skills.zip.

Scope is personal skills only (workspace/filesystem skills are read-only
in chat and out of scope). No backend changes: the single-skill content
endpoint (GET /api/experimental/users/{user}/skills/{skillName}) already
returns full content, so the view fetches on demand and downloads with
file-saver, zipping with jszip (both existing deps, matching
DownloadLogsDialog).

Changes

  • AgentSettingsPersonalSkillsPageView.tsx: Download per-row button (with
    per-row spinner) and an Export all header button (disabled when empty or
    loading).
  • AgentSettingsPersonalSkillsPage.tsx: container handlers that fetch
    content via queryClient.fetchQuery(userSkill(name)) and trigger the
    download/zip, with toast error handling. Download logic is extracted to
    module-level helpers to stay React Compiler friendly.
  • AgentSettingsPersonalSkillsPageView.stories.tsx: interaction stories
    asserting onDownload/onExportAll fire, plus loading-state stories.

Testing

  • pnpm check (biome), pnpm lint:types (tsc), React Compiler check, and
    Storybook interaction tests (22 passed) all pass locally.
  • make pre-commit passed.

Closes CODAGT-918.

Implementation plan

Problem

Users can create, edit, and delete personal skills in Agent settings, but
there is no way to get a skill back out as a file. The only workaround was to
paste the SKILL.md content by hand.

Decisions (confirmed with requester)

  1. Surface: download in the settings UI (Personal skills page).
  2. Build both single-skill download and export-all (zip).
  3. Personal skills only (workspace/filesystem skills out of scope).

Approach (frontend-only, additive)

No backend changes: the single-skill content endpoint already exists. The
view stays presentational and exposes new callbacks; the container fetches
content and performs the download, mirroring how Edit/Delete already split
between view and container.

  • Per-row Download button, placed before Edit in the actions cell; shows a
    spinner while its own row is downloading.
  • Export all button in the section header, disabled when there are no skills
    or while loading.
  • Container: single download fetches content and saveAs(<name>.md); export
    all fetches every skill, adds each as <name>/SKILL.md to a JSZip,
    generates a blob, and saveAs(personal-skills.zip). Failures surface via
    toast.error.
  • Stories cover the interactions and loading states (stories are the FE test
    surface).

Out of scope

  • Workspace skills download (filesystem source): different data path,
    read-only in chat, not user-owned data.
  • In-chat download button on the read_skill tool output: possible
    follow-up, different surface and interaction model.

Opened by Coder Agents on behalf of @Shelnutt2.

@linear-code

linear-code Bot commented Aug 11, 2026

Copy link
Copy Markdown

CODAGT-918

@coder-tasks

coder-tasks Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Documentation Check

Updates Needed

  • docs/ai-coder/agents/extending-agents.md - The Personal skills section documents managing personal skills (create/edit/delete) from Agents > Settings > Personal Skills, but not the new export capabilities this PR adds. Add a short note that users can Download an individual skill as <name>.md and use Export all to download every personal skill as personal-skills.zip (each stored as <name>/SKILL.md). This reinforces the existing portability story between personal and workspace skills.

Automated review via Coder Agents

Comment on lines +189 to +220
const handleDownload = async (skill: UserSkillMetadata) => {
if (downloadingSkillName) {
return;
}
setDownloadingSkillName(skill.name);
try {
await downloadPersonalSkillFile(skill.name, fetchSkillContent);
} catch (error) {
toast.error(
getErrorMessage(error, "Failed to download personal skill."),
{
description: getErrorDetail(error),
},
);
}
setDownloadingSkillName(undefined);
};

const handleExportAll = async () => {
if (isExportingAll || skills.length === 0) {
return;
}
setIsExportingAll(true);
try {
await exportPersonalSkillsArchive(skills, fetchSkillContent);
} catch (error) {
toast.error(getErrorMessage(error, "Failed to export personal skills."), {
description: getErrorDetail(error),
});
}
setIsExportingAll(false);
};

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.

Can we not make use of TanStack Query's useMutation to avoid having to handle the setIsExportingAll(true/false) stuff we're doing here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call. Replaced the manual setIsExportingAll/setDownloadingSkillName state with two useMutations (downloadMutation, exportAllMutation). Loading state now comes from the mutations: isExportingAll is exportAllMutation.isPending, and the per-row spinner uses downloadMutation.isPending ? downloadMutation.variables : undefined. Errors are handled in each mutation's onError. Done in 66df791.

Posted by Coder Agents.

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