Files
CherryHQ-cherry-studio/resources/database/drizzle
SuYao 0477bd0ad2 fix(skills): per-agent skill enablement with workspace symlinks (#14247)
### What this PR does

Before this PR:
Skills were enabled/disabled globally — toggling a skill affected all
agents. Symlinks were created in a global `.claude/skills/` directory.
Backup restore from another machine could cause EACCES errors when
workspace paths referenced non-existent directories.

After this PR:
Each agent has its own set of enabled skills tracked via the
`agent_skills` join table. Symlinks are created per-agent at
`{workspace}/.claude/skills/`. Workspace paths are validated before
symlink operations, preventing EACCES on restored backups.

### Why we need it and why it was done in this way

The following tradeoffs were made:
- Added a new `agent_skills` join table rather than modifying the
existing `skills` table, to cleanly separate per-agent state from the
global skill library
- A data migration seeds `agent_skills` from the legacy
`skills.is_enabled` column so existing users keep their enabled skills
after upgrading
- The legacy `is_enabled` column is kept for schema compatibility but no
longer read or written

The following alternatives were considered:
- Storing per-agent skill config in the `agents` table JSON blob —
rejected because a normalized join table is cleaner for queries and FK
cascade deletes

### Breaking changes

None. The data migration transparently upgrades existing skill
enablement state to the per-agent model.

### Special notes for your reviewer

- The `agent_skills` schema migration is `0006_famous_fallen_one.sql`
- The data migration (`migrateSkillsPerAgent.ts`) is idempotent and
handles fresh DBs gracefully
- Workspace path validation (`directoryExists` check) in
`getAgentWorkspace` and `enableForAllAgents` prevents EACCES when
`accessible_paths` contains paths from another machine (backup restore
scenario)
- Minor React perf optimizations in `SkillsSettings.tsx`: parallelized
batch uninstall, removed unnecessary `useMemo`, lazy `useState`
initializers

### Checklist

- [x] PR: The PR description is expressive enough and will help future
contributors
- [x] Code: [Write code that humans can
understand](https://en.wikiquote.org/wiki/Martin_Fowler#code-for-humans)
and [Keep it simple](https://en.wikipedia.org/wiki/KISS_principle)
- [x] Refactor: You have [left the code cleaner than you found it (Boy
Scout
Rule)](https://learning.oreilly.com/library/view/97-things-every/9780596809515/ch08.html)
- [ ] Upgrade: Impact of this change on upgrade flows was considered and
addressed if required
- [ ] Documentation: A [user-guide update](https://docs.cherry-ai.com)
was considered and is present (link) or not required. Check this only
when the PR introduces or changes a user-facing feature or behavior.
- [ ] Self-review: I have reviewed my own code (e.g., via
[`/gh-pr-review`](/.claude/skills/gh-pr-review/SKILL.md), `gh pr diff`,
or GitHub UI) before requesting review from others

### Release note

```release-note
NONE
```

---------

Signed-off-by: suyao <sy20010504@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: 亢奋猫 <kangfenmao@qq.com>
2026-04-16 12:20:02 +08:00
..