feat(skills): discover global ~/.claude/skills (#902)

The skill registry already walks workspace-local `.claude/skills` for
Claude Code interop, plus global `~/.agents/skills` and
`~/.deepseek/skills`. Picking up the global `~/.claude/skills` brings
DeepSeek TUI in line with the broader Claude-ecosystem convention so
users can inherit skills installed for other Claude-compatible tools
without re-authoring them in DeepSeek's native layout.

Adds `claude_global_skills_dir()` mirroring `agents_global_skills_dir()`
and inserts it into `skills_directories()` between the agentskills.io
global and the DeepSeek-native global. Workspace candidates still win
on name conflicts; first-match-wins is preserved.

Tests:
- claude_global_skills_dir_returns_home_relative_path
- existing_skill_dirs_orders_globals_agents_then_claude_then_deepseek
- All 55 pre-existing skills tests still pass

Docs synced (README publishing-skills section, CONFIGURATION).
docs/COMPETITIVE_ANALYSIS.md already advertised this lookup; this
brings the implementation in line with the documented contract.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Hunter Bown
2026-05-06 15:00:24 -05:00
parent bf609465c1
commit 58f52c7725
3 changed files with 49 additions and 3 deletions
+1 -1
View File
@@ -335,7 +335,7 @@ Legacy aliases `deepseek-chat` / `deepseek-reasoner` map to `deepseek-v4-flash`.
## Publishing Your Own Skill
DeepSeek TUI discovers skills from workspace directories (`.agents/skills``skills``.opencode/skills``.claude/skills``.cursor/skills`) and global directories (`~/.agents/skills``~/.deepseek/skills`). Each skill is a directory with a `SKILL.md` file:
DeepSeek TUI discovers skills from workspace directories (`.agents/skills``skills``.opencode/skills``.claude/skills``.cursor/skills`) and global directories (`~/.agents/skills` `~/.claude/skills` `~/.deepseek/skills`). Each skill is a directory with a `SKILL.md` file:
```text
~/.agents/skills/my-skill/
+47 -1
View File
@@ -42,6 +42,16 @@ pub fn agents_global_skills_dir() -> Option<PathBuf> {
dirs::home_dir().map(|p| p.join(".agents").join("skills"))
}
/// Global Claude-compatible skills directory (`~/.claude/skills`). The
/// SKILL.md frontmatter convention is shared across the broader Claude
/// ecosystem, so picking up the global path lets users inherit skills
/// they already installed for other Claude-compatible tools without
/// re-authoring them in DeepSeek's native layout (#902).
#[must_use]
pub fn claude_global_skills_dir() -> Option<PathBuf> {
dirs::home_dir().map(|p| p.join(".claude").join("skills"))
}
// === Types ===
/// Parsed representation of a SKILL.md definition.
@@ -334,7 +344,8 @@ pub fn resolve_skills_dir(workspace: &Path) -> PathBuf {
/// 4. `<workspace>/.claude/skills` — Claude Code interop.
/// 5. `<workspace>/.cursor/skills` — Cursor interop.
/// 6. [`agents_global_skills_dir`] — agentskills.io global.
/// 7. [`default_skills_dir`] — DeepSeek global, user-installed.
/// 7. [`claude_global_skills_dir`] — Claude-ecosystem global (#902).
/// 8. [`default_skills_dir`] — DeepSeek global, user-installed.
///
/// Only directories that exist on disk are returned — callers don't
/// need to filter further. Returns an empty vec when nothing is
@@ -351,6 +362,9 @@ pub fn skills_directories(workspace: &Path) -> Vec<PathBuf> {
if let Some(global_agents) = agents_global_skills_dir() {
candidates.push(global_agents);
}
if let Some(global_claude) = claude_global_skills_dir() {
candidates.push(global_claude);
}
candidates.push(default_skills_dir());
existing_skill_dirs(candidates)
}
@@ -742,6 +756,38 @@ mod tests {
);
}
#[test]
fn claude_global_skills_dir_returns_home_relative_path() {
// Smoke test for the #902 helper. We don't assert the exact path
// because dirs::home_dir() is host-dependent; we just pin the
// suffix shape so a future refactor can't silently rename it.
let path = super::claude_global_skills_dir().expect("home dir resolves on test host");
assert!(path.ends_with(".claude/skills") || path.ends_with(r".claude\skills"));
}
#[test]
fn existing_skill_dirs_orders_globals_agents_then_claude_then_deepseek() {
// Pins the precedence among the three global skill roots (#902).
// Workspace candidates are tested separately above; here we only
// exercise the global ordering at the existing_skill_dirs level
// so the assertion is host-independent.
let tmpdir = TempDir::new().unwrap();
let agents_global = tmpdir.path().join(".agents").join("skills");
let claude_global = tmpdir.path().join(".claude").join("skills");
let deepseek_global = tmpdir.path().join(".deepseek").join("skills");
std::fs::create_dir_all(&agents_global).unwrap();
std::fs::create_dir_all(&claude_global).unwrap();
std::fs::create_dir_all(&deepseek_global).unwrap();
let dirs = super::existing_skill_dirs(vec![
agents_global.clone(),
claude_global.clone(),
deepseek_global.clone(),
]);
assert_eq!(dirs, vec![agents_global, claude_global, deepseek_global]);
}
#[test]
fn existing_skill_dirs_keeps_agents_global_before_deepseek_global() {
let tmpdir = TempDir::new().unwrap();
+1 -1
View File
@@ -326,7 +326,7 @@ If you are upgrading from older releases:
keys such as `worker`, `explorer`, `general`, `explore`, `plan`, and
`review`. Values must normalize to a supported DeepSeek model id before an
agent is spawned.
- `skills_dir` (string, optional): defaults to `~/.deepseek/skills` (each skill is a directory containing `SKILL.md`). Workspace-local `.agents/skills` or `./skills` are preferred when present; the runtime also discovers global agentskills.io-compatible `~/.agents/skills`.
- `skills_dir` (string, optional): defaults to `~/.deepseek/skills` (each skill is a directory containing `SKILL.md`). Workspace-local `.agents/skills` or `./skills` are preferred when present; the runtime also discovers global agentskills.io-compatible `~/.agents/skills` and the broader Claude-ecosystem `~/.claude/skills`.
- `mcp_config_path` (string, optional): defaults to `~/.deepseek/mcp.json`.
It is visible in `/config` and can be changed from the TUI. The new path is
used immediately by `/mcp`, but rebuilding the model-visible MCP tool pool