Skip to content

Commit 6a0efc1

Browse files
committed
fix: scan agent skills for Claude workspace
1 parent fccaec2 commit 6a0efc1

7 files changed

Lines changed: 71 additions & 4 deletions

File tree

.agents/skills/gettokens-domain-engineering/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ This skill unifies the technical rules for building, styling, and debugging GetT
129129
- **Codex Extensions**: For Codex Skills / MCP Servers, `[[skills.config]]`, `tk://github.com` / `tk://gitlab.com` skill sources, and `#frame=codex&workspace=skills|mcp-servers`, use the dedicated `gettokens-codex-extensions-management` skill. Keep source-accurate parsing, modal/list UI semantics, and cleanup split rules in that skill instead of expanding this general domain skill.
130130
- **Claude Code Workspace Parity**: When Claude Code adds a capability that corresponds to existing Codex workspace entries, keep the workspace granularity aligned with Codex unless Claude semantics clearly require a different information architecture.
131131
- If Codex exposes separate workspaces such as `#frame=codex&workspace=skills` and `#frame=codex&workspace=mcp-servers`, Claude should expose separate workspaces such as `#frame=claude&workspace=skills` and `#frame=claude&workspace=mcp-servers`, not a single merged page with internal tabs.
132+
- Claude skills read-only scanning must include both native Claude roots (`$CLAUDE_CONFIG_DIR/skills` or `~/.claude/skills`, plus project `.claude/skills`) and unified Agent Skills roots (`~/.agents/skills`, plus project `.agents/skills`). GetTokens installs project skills under `.agents/skills`, so scanning only `.claude/skills` produces a false empty state.
132133
- Implement this split as route-level feature components, for example `ClaudeCodeSkillsWorkspace` and `ClaudeCodeMcpServersWorkspace`, with the page wrapper only dispatching by `ClaudeWorkspace`.
133134
- Shared shells and visual primitives are still encouraged. Reuse `AssetWorkbenchShell`, preview data, DTO mappers, and list patterns where they fit, but do not let shared UI collapse distinct navigation surfaces into one tabbed page.
134135
- Keep legacy hash/storage compatibility explicit. A retired merged workspace such as `extensions` may migrate to the safest default (`skills`), but should not remain a first-class menu item once split pages exist.

docs-linhay/memory/2026-05-28.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,10 @@
9696
- preview usage 和 rate-limit 现在对新增预览账号有兜底生成逻辑,避免扩展 provider 后出现无运行态的空卡片。
9797
- 验收截图已归档:`docs-linhay/screenshots/20260528/accounts/20260528-accounts-full-mock-after-v01.png`
9898
- 验证通过:`node --test frontend/src/features/accounts/tests/previewData.test.mjs``npm --prefix frontend run typecheck`、Chrome DevTools DOM 验收 `http://localhost:5173/#frame=accounts` 显示 `22 UNITS`
99+
100+
## Claude Skills 扫描修复
101+
- `#frame=claude&workspace=skills` 空态根因是后端只扫 `$CLAUDE_CONFIG_DIR/skills`、项目 `.claude/skills``.claude/commands`,但 GetTokens 当前项目级 skills 安装在 `.agents/skills`
102+
- 已将 Claude Code skills snapshot 的只读扫描 roots 扩展为:native Claude roots + `~/.agents/skills` + 项目 `.agents/skills`;项目 `.agents/skills` 以 project scope 展示,用户 `~/.agents/skills` 以 user scope 展示。
103+
- 回归测试新增 `TestGetClaudeCodeExtensionsSnapshotScansAgentSkillRoots`,验证 `.agents/skills` 下的用户级和项目级 `SKILL.md` 都能被扫描到。
104+
- 已同步更新 `20260521-claude-code-codex-alignment` space 与 `.agents/skills/gettokens-domain-engineering/SKILL.md` 的 Claude Code workspace parity 规则。
105+
- 本机 Wails dev 页面验收通过:`http://127.0.0.1:34115/#frame=claude&workspace=skills` 显示 `41 SKILLS`,包含 `~/.agents/skills` 的 user skills 和项目 `.agents/skills` 的 project skills;截图归档到 `docs-linhay/spaces/20260521-claude-code-codex-alignment/screenshots/20260528/claude/20260528-claude-skills-scan-after-v01.png`

docs-linhay/spaces/20260521-claude-code-codex-alignment/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ GetTokens 已经把 Claude Code 账号列表从旧判断里的“单 key 本地
8080
### P0:Claude Code 扩展资产工作台
8181

8282
- 新增 Claude workspace:`skills``mcp-servers`;旧 `extensions` 作为兼容 hash 迁移到 `skills`
83-
- Skills 覆盖用户级 `~/.claude/skills/`、项目级 `.claude/skills/`、兼容 `.claude/commands/` 只读展示与迁移提示。
83+
- Skills 覆盖用户级 `~/.claude/skills/`统一用户级 `~/.agents/skills/`、项目级 `.agents/skills/`项目级 `.claude/skills/`、兼容 `.claude/commands/` 只读展示与迁移提示。
8484
- MCP 覆盖 user/local `~/.claude.json` 与 project `.mcp.json`,支持 stdio / http、env、headers、tool 限制和 scope 标记。
8585
- 新增通用 target adapter:`codex` 使用 TOML adapter,`claude` 使用 JSON / directory adapter;前端组件尽量复用现有 Codex Extensions。
8686

docs-linhay/spaces/20260521-claude-code-codex-alignment/plans/research-skills-commands.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,30 @@
55

66
## 结论
77

8-
Claude Code Skills 与 Codex Skills 在 UI 工作流上高度可复用,但数据语义必须按 Claude Code 单独实现。P0 可以做 `~/.claude/skills/`、项目 `.claude/skills/`、兼容 `.claude/commands/` 的扫描、预览、来源标记和 Git source 预研;不先做“启停开关”,因为 Claude Code 的可见性和触发控制主要来自 frontmatter,而不是 Codex 的 `[[skills.config]] enabled=false`
8+
Claude Code Skills 与 Codex Skills 在 UI 工作流上高度可复用,但数据语义必须按 Claude Code 单独实现。P0 可以做 `~/.claude/skills/``~/.agents/skills/`、项目 `.agents/skills/`项目 `.claude/skills/`、兼容 `.claude/commands/` 的扫描、预览、来源标记和 Git source 预研;不先做“启停开关”,因为 Claude Code 的可见性和触发控制主要来自 frontmatter,而不是 Codex 的 `[[skills.config]] enabled=false`
99

1010
## 已验证依据
1111

1212
- 官方文档确认:skill 是包含 `SKILL.md` 的目录;personal 路径为 `~/.claude/skills/<skill-name>/SKILL.md`,project 路径为 `.claude/skills/<skill-name>/SKILL.md`;legacy `.claude/commands/` 仍可用,并且 skill 与 command 同名时 skill 优先。
1313
- 官方 frontmatter 已确认包含 `name``description``when_to_use``argument-hint``arguments``disable-model-invocation``user-invocable``allowed-tools``model``effort``context``agent``hooks``paths``shell` 等字段。
1414
- 本地参考 `docs-linhay/references/cherry-studio/scripts/skills-sync.ts` 已证明 `.agents/skills/<skill>` 可以通过 symlink mirror 到 `.claude/skills/<skill>`
1515
- 本地参考 `docs-linhay/references/cherry-studio/scripts/skills-check.ts` 已证明 skill 白名单、symlink 目标和 git tracked 文件范围可以自动校验。
16+
- GetTokens 当前项目级 skills 实际安装在 `.agents/skills/`,用户级开放标准目录为 `~/.agents/skills/`;Claude skills 页面只读扫描必须直接覆盖这两类 roots,否则本机页面会误显示空态。
1617
- 可行性验证已跑过 Node frontmatter 解析原型,可解析 `SKILL.md` 的 YAML frontmatter 与 markdown body。
1718

1819
## 数据边界
1920

2021
- 读取 roots:
2122
- user:`$CLAUDE_CONFIG_DIR/skills` 或默认 `~/.claude/skills`
23+
- user agent:`~/.agents/skills`
24+
- project agent:`<repo>/.agents/skills`
2225
- project:`<repo>/.claude/skills`
2326
- legacy command:`<repo>/.claude/commands`
2427
- P0 写入:不写入用户现有 skill;只提供扫描和预览。
2528
- P1 写入:
2629
- 新增 skill:仅写目标 root 下的新目录,目录名 slug 校验。
2730
- 删除:需要二次确认,只允许删除已识别 root 内目录。
28-
- `.agents/skills` mirror:优先采用 symlink,Windows 或权限受限时降级为复制前必须另行验证。
31+
- `.agents/skills` mirror:只读扫描可直接读取;涉及同步到 `.claude/skills` 时优先采用 symlink,Windows 或权限受限时降级为复制前必须另行验证。
2932
- 不复用 Codex 的 `[[skills.config]] enabled=false`,Claude Code 没有同构禁用语义。
3033

3134
## 后端设计
@@ -55,6 +58,7 @@ Claude Code Skills 与 Codex Skills 在 UI 工作流上高度可复用,但数
5558

5659
- `internal/wailsapp/claude_code_extensions_test.go`
5760
- 扫描 `CLAUDE_CONFIG_DIR/skills/foo/SKILL.md`
61+
- 扫描 `~/.agents/skills/foo/SKILL.md` 和项目 `.agents/skills/bar/SKILL.md`
5862
- 扫描项目 `.claude/skills/bar/SKILL.md`
5963
- 扫描 `.claude/commands/deploy.md``legacy-command`
6064
- frontmatter 缺失时从正文首段生成摘要。
@@ -75,4 +79,3 @@ Claude Code Skills 与 Codex Skills 在 UI 工作流上高度可复用,但数
7579

7680
- skill live reload 与新目录 watch 有差异,GetTokens 只能提示“Claude Code 可能需要重启会话”,不应承诺立即生效。
7781
- `allowed-tools``hooks``paths` 等字段未来可能变化,P0 解析时必须保留未知 frontmatter。
78-
462 KB
Loading

internal/wailsapp/claude_code_extensions.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,11 +241,16 @@ type claudeCodeSkillRoot struct {
241241
}
242242

243243
func scanClaudeCodeSkills(claudeConfigDir string, projectPath string) ([]ClaudeCodeSkillAsset, []string) {
244+
home, homeErr := os.UserHomeDir()
244245
roots := []claudeCodeSkillRoot{
245246
{scope: "user", path: filepath.Join(claudeConfigDir, "skills"), removable: true},
246247
}
248+
if homeErr == nil && home != "" {
249+
roots = append(roots, claudeCodeSkillRoot{scope: "user", path: filepath.Join(home, ".agents", "skills"), removable: true})
250+
}
247251
if projectPath != "" {
248252
roots = append(roots,
253+
claudeCodeSkillRoot{scope: "project", path: filepath.Join(projectPath, ".agents", "skills")},
249254
claudeCodeSkillRoot{scope: "project", path: filepath.Join(projectPath, ".claude", "skills")},
250255
claudeCodeSkillRoot{scope: "legacy-command", path: filepath.Join(projectPath, ".claude", "commands"), commands: true},
251256
)

internal/wailsapp/claude_code_extensions_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,57 @@ func TestGetClaudeCodeExtensionsSnapshotHandlesMissingAssets(t *testing.T) {
140140
}
141141
}
142142

143+
func TestGetClaudeCodeExtensionsSnapshotScansAgentSkillRoots(t *testing.T) {
144+
base := t.TempDir()
145+
home := filepath.Join(base, "home")
146+
project := filepath.Join(base, "project")
147+
claudeConfigDir := filepath.Join(home, ".claude")
148+
t.Setenv("HOME", home)
149+
t.Setenv("CLAUDE_CONFIG_DIR", claudeConfigDir)
150+
mustMkdirAll(t, project)
151+
152+
previousCwd, err := os.Getwd()
153+
if err != nil {
154+
t.Fatalf("Getwd: %v", err)
155+
}
156+
if err := os.Chdir(project); err != nil {
157+
t.Fatalf("Chdir project: %v", err)
158+
}
159+
project, err = os.Getwd()
160+
if err != nil {
161+
t.Fatalf("Getwd project: %v", err)
162+
}
163+
t.Cleanup(func() {
164+
_ = os.Chdir(previousCwd)
165+
})
166+
167+
writeTextFile(t, filepath.Join(home, ".agents", "skills", "user-agent-skill", "SKILL.md"), strings.Join([]string{
168+
"---",
169+
"name: user-agent-skill",
170+
"description: User agent skill.",
171+
"---",
172+
"",
173+
"# User Agent Skill",
174+
}, "\n"))
175+
writeTextFile(t, filepath.Join(project, ".agents", "skills", "project-agent-skill", "SKILL.md"), strings.Join([]string{
176+
"---",
177+
"name: project-agent-skill",
178+
"description: Project agent skill.",
179+
"---",
180+
"",
181+
"# Project Agent Skill",
182+
}, "\n"))
183+
184+
app := &App{}
185+
snapshot, err := app.GetClaudeCodeExtensionsSnapshot()
186+
if err != nil {
187+
t.Fatalf("GetClaudeCodeExtensionsSnapshot returned error: %v", err)
188+
}
189+
190+
assertClaudeSkill(t, snapshot.Skills, "user-agent-skill", "user", "auto", "enabled", "valid")
191+
assertClaudeSkill(t, snapshot.Skills, "project-agent-skill", "project", "auto", "enabled", "valid")
192+
}
193+
143194
func TestSaveClaudeCodeMcpServerPatchesProjectServerPreservingUnknownFields(t *testing.T) {
144195
base := t.TempDir()
145196
home := filepath.Join(base, "home")

0 commit comments

Comments
 (0)