Skip to content

fix: support nested Claude skill directories#1697

Closed
Alexlangl wants to merge 6 commits intofarion1231:mainfrom
Alexlangl:fix/claude-skill-subdir-mapping-1686
Closed

fix: support nested Claude skill directories#1697
Alexlangl wants to merge 6 commits intofarion1231:mainfrom
Alexlangl:fix/claude-skill-subdir-mapping-1686

Conversation

@Alexlangl
Copy link
Copy Markdown
Contributor

概要

主要解决Claude skill子目录问题
有些Claude skill拥有子目录,典型的就是superpowers
CC Switch 之前只能识别顶层目录,导致这类Claude skills 无法被正常发现、导入和统一管理。

变更

后端:

  • Claude 的 skill 扫描改为支持递归识别子目录中的叶子 skill
  • 导入到 SSOT 时保留相对路径,比如 superpowers/brainstorming
  • 同步回 Claude 目录时,按 Claude 的使用方式映射为叶子目录名

也就是说,现在这类结构:

~/.claude/skills/superpowers/brainstorming/SKILL.md

可以被识别并导入,在同步到Cluade时,会按叶子skill处理,例如 superpowers/brainstorming 会同步为 ~/.claude/skills/brainstorming

前端:

  • 已安装的skill列表里,如果 skill 的 directory 是嵌套路径,会额外显示完整目录
  • 这样像 superpowers/using-superpowers 这类 skill,在列表里不会只看到叶子名,可以更好地区分来源

额外说明

此处改动只对 Claude 做了特判,没有扩大到其他 app。
主要是原因是这个 issue 对应的是 Claude skills 的目录组织方式和其他 app 有点不同:

  • 导入时需要递归识别分组目录下的叶子 skill
  • 同步时又需要按 Claude 的 live skills 目录结构来映射

验证

已通过

cargo test skill --manifest-path src-tauri/Cargo.toml -- --nocapture
cargo test skill_sync --manifest-path src-tauri/Cargo.toml -- --nocapture
source ~/.nvm/nvm.sh && nvm use 22 >/dev/null && pnpm vitest run tests/components/UnifiedSkillsPanel.test.tsx
source ~/.nvm/nvm.sh && nvm use 22 >/dev/null && pnpm typecheck

Related

Fix #1686

其它

是否需要不只是针对claude做单独处理?此处处理暂时没有将问题放大 @farion1231

Copy link
Copy Markdown
Owner

@farion1231 farion1231 left a comment

Choose a reason for hiding this comment

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

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown
Owner

@farion1231 farion1231 left a comment

Choose a reason for hiding this comment

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

感谢您的贡献,请查看一下有没有以下问题。:

  1. [P1] Claude 同步目标路径只取叶子目录名,存在重名冲突。
    app_relative_skill_path() 对 Claude 只保留 file_name(),这会让 superpowers/brainstormingtools/brainstorming 都映射到同一个 ~/.claude/skills/brainstorming。这样在同步、删除和 cleanup 时都会互相覆盖/误删,indexed_skills 里也只会保留其中一个。建议至少在导入或启用 Claude 时显式检测 leaf name 冲突,并阻止同名 skill 同时启用。

  2. [P1] 扁平化映射后,旧的嵌套 live 目录不会被清理。
    例如用户原本有 ~/.claude/skills/superpowers/brainstorming/SKILL.md,导入后同步回 Claude 会创建新的 ~/.claude/skills/brainstorming,但旧的 superpowers/brainstorming 不会被 sync_to_app() 清掉,因为当前 cleanup 只扫顶层目录。这样问题不只是“残留目录不好看”,而是旧 skill 仍可能继续被 Claude 读取,后续禁用/卸载时 CC Switch 也删不到它。

  3. [P2] 已管理的嵌套 Claude skill 可能再次被扫成“未管理”。
    数据库/SSOT 里保存的是完整相对路径,比如 superpowers/brainstorming,但同步到 Claude live dir 后变成了叶子名 brainstorming。这样下次 scan_unmanaged() 时,可能又把同一个 skill 当成新的 unmanaged skill 报出来,导致重复导入。

  4. [P3] 小问题:注释里有个 typo。
    list_app_skill_entries() 的注释里写成了 skillsg,应为 skills

建议补两类测试后再合:

  1. 同 leaf name、不同嵌套路径的 Claude skill 冲突场景。
  2. 嵌套 Claude skill 导入并同步后,再次 scan_unmanaged() 不应重复出现。

@Alexlangl
Copy link
Copy Markdown
Contributor Author

感谢 review
主要处理了:

  1. Claude leaf name 冲突
  • 现在会显式检查 Claude 扁平化后的目标路径冲突
  • superpowers/brainstormingtools/brainstorming 这种最终都会映射到 ~/.claude/skills/brainstorming 的情况,现在会直接报错并阻止继续导入/启用,不再出现互相覆盖或 cleanup 误删的问题
  1. 旧的嵌套 live 目录清理
  • Claude cleanup 现在会递归处理实际存在的 skill entry
  • 这样像旧的 ~/.claude/skills/superpowers/brainstorming 在同步后会被清掉,空的父目录也会顺手清理掉,不会继续残留被 Claude 读取
  1. 避免再次扫成 unmanaged
  • scan_unmanaged() 现在除了比对完整 directory,也会比对 app 实际暴露出来的路径
  • 对 Claude 这种扁平映射场景,已经管理的 superpowers/brainstorming 不会再因为 live dir 里显示成 brainstorming 被重复扫出来
  1. typo
  • skillsg 已经改成 skills

另外按建议已经补充了两类测试

  • 同 leaf name、不同嵌套路径的 Claude skill 冲突场景
  • 嵌套 Claude skill 导入并同步后,再次 scan_unmanaged() 不会重复出现

另外我手动在隔离环境里手动进行了更接近真实链路的验证:

  • 冲突场景会被明确拦住
  • 导入 superpowers/brainstorming 后同步到 Claude 会生成扁平叶子目录
  • 旧的嵌套目录会被清掉
  • 再次扫描不会重复报成 unmanaged

Copy link
Copy Markdown
Owner

@farion1231 farion1231 left a comment

Choose a reason for hiding this comment

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

感谢您的更新,请查看一下是否存在以下问题:

[P2] Claude 的叶子目录冲突现在被“所有已安装 skill”占用,而不是“对 Claude 启用的 skill”占用。见 skill.rs (line 886) 和 skill.rs (line 1269)。collect_managed_app_paths(managed_skills.values()) 会把 superpowers/brainstorming 这样的 Codex-only skill 也映射成 Claude 的 brainstorming,结果是这个真实 Claude skill 会在未管理扫描里被跳过,并在 sync_to_app(&Claude) 时被 should_remove 删除。前面的 import_from_apps() 冲突检查只看 apps.claude,所以这里的行为明显更宽,容易误删用户已有的 Claude skill。

[P2] 嵌套 skill 的身份键还没全链路改成“完整 directory”。发现页在 SkillsPage.tsx (line 100) 仍用叶子名算 installed key,而 discoverable key 在 skill.rs (line 1681) 已经是完整路径;同样的叶子名 key 也还出现在 useSkills.ts (line 82) 和 UnifiedSkillsPanel.tsx (line 113)。这会让 superpowers/using-superpowers 这类 skill 在发现页继续显示未安装,安装/卸载后的 React Query 缓存更新也打不到正确条目,必须等全量刷新才恢复。

@Alexlangl Alexlangl force-pushed the fix/claude-skill-subdir-mapping-1686 branch from 9138389 to ff04f5d Compare March 29, 2026 08:15
@Alexlangl
Copy link
Copy Markdown
Contributor Author

Alexlangl commented Mar 30, 2026

感谢您的更新,请查看一下是否存在以下问题:

[P2] Claude 的叶子目录冲突现在被“所有已安装 skill”占用,而不是“对 Claude 启用的 skill”占用。见 skill.rs (line 886) 和 skill.rs (line 1269)。collect_managed_app_paths(managed_skills.values()) 会把 superpowers/brainstorming 这样的 Codex-only skill 也映射成 Claude 的 brainstorming,结果是这个真实 Claude skill 会在未管理扫描里被跳过,并在 sync_to_app(&Claude) 时被 should_remove 删除。前面的 import_from_apps() 冲突检查只看 apps.claude,所以这里的行为明显更宽,容易误删用户已有的 Claude skill。

[P2] 嵌套 skill 的身份键还没全链路改成“完整 directory”。发现页在 SkillsPage.tsx (line 100) 仍用叶子名算 installed key,而 discoverable key 在 skill.rs (line 1681) 已经是完整路径;同样的叶子名 key 也还出现在 useSkills.ts (line 82) 和 UnifiedSkillsPanel.tsx (line 113)。这会让 superpowers/using-superpowers 这类 skill 在发现页继续显示未安装,安装/卸载后的 React Query 缓存更新也打不到正确条目,必须等全量刷新才恢复。

  • 现在 Claude 的 managed path / cleanup 判断只看对 Claude 启用的 skills,不会再把其他 app-only 的同名 nested skill 也算进去。这样像 Codex-only 的 superpowers/brainstorming 也不会再影响真实 Claude leaf skill 的扫描、同步和清理
  • 前端发现页、安装/卸载后的缓存更新,现在都统一使用完整 directory + repoOwner + repoName 作为 identity key,不再混用 leaf name。这样 nested Claude skill 也不会再出现“明明已安装但发现页仍显示未安装”或者需要整页刷新才能恢复的问题
  • 外部 nested Claude skill 即使和已管理 skill 的 leaf name 冲突,也不会被静默吞掉,仍然会作为 unmanaged skill 保持可见。同时发现页安装时也不再只靠 directory 去匹配 skill,避免不同 repo 下同目录名 skill 选错对象

回归测试

  • Codex-only nested skill 不会阻塞真实 Claude leaf skill
  • 外部 nested Claude skill 即使 leaf 冲突也仍会显示为 unmanaged
  • 发现页会用完整目录 key 判断 nested skill 的安装状态
  • 安装/卸载 nested skill 时也会使用完整 identity key

@farion1231

@Alexlangl Alexlangl force-pushed the fix/claude-skill-subdir-mapping-1686 branch from 44d979b to 3d3c6c9 Compare April 3, 2026 00:53
@Alexlangl
Copy link
Copy Markdown
Contributor Author

Closing this for now. I’ve already put a fair amount of time into it, and I don’t want to keep investing more without knowing whether it’s something you still want to move forward.

If you’d like to revisit it later, feel free to let me know.

@Alexlangl Alexlangl closed this Apr 6, 2026
@farion1231
Copy link
Copy Markdown
Owner

Same message as in #1824 — these changes are welcome and I'd love to get them merged. Happy to help rebase if needed.

@Alexlangl Alexlangl reopened this Apr 7, 2026
@Alexlangl Alexlangl force-pushed the fix/claude-skill-subdir-mapping-1686 branch from efef847 to 9705c97 Compare April 13, 2026 05:56
@Alexlangl Alexlangl closed this Apr 13, 2026
@Alexlangl
Copy link
Copy Markdown
Contributor Author

Closing this stale PR in favor of a fresh PR rebased onto current main. Continued in #2045.

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.

希望skill的管理增加子目录映射

2 participants