Skip to content

Commit 582c66e

Browse files
CopilotOhYee
andauthored
feat: refactor sync-skills to use --tool choice and add github-copilot/cursor/qoder
Signed-off-by: GitHub <noreply@github.com> Co-authored-by: OhYee <13498329+OhYee@users.noreply.github.com>
1 parent 9fc4a34 commit 582c66e

5 files changed

Lines changed: 207 additions & 74 deletions

File tree

docs/en/sync-skills.md

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,58 @@
22

33
# ar sync-skills
44

5-
Sync platform Skills to local AI tool skill directories (Claude Code or Codex).
5+
Sync platform Skills to a local AI tool skill directory.
66

77
```
8-
ar sync-skills [tool] [scope] [options]
8+
ar sync-skills --tool <tool> (--user | --project) [options]
99
```
1010

1111
## Options
1212

1313
| Flag | Type | Required | Default | Description |
1414
|------|------|----------|---------|-------------|
15-
| `--claude-code` | flag | yes* | | Sync to Claude Code skill directory. |
16-
| `--codex` | flag | yes* | | Sync to Codex skill directory. |
17-
| `--user` | flag | yes** | | Sync to user-level directory. |
18-
| `--project` | flag | yes** | | Sync to project-level directory. |
15+
| `--tool` | choice | yes | | Target AI tool. See choices below. |
16+
| `--user` | flag | yes* | | Sync to user-level directory. |
17+
| `--project` | flag | yes* | | Sync to project-level directory. |
1918
| `--workspace` | multi | no | all | Workspace filter, repeatable. |
2019
| `--delete-unmanaged` | flag | no | false | Delete local skills outside selected workspace scope (with confirmation). |
2120
| `-y`, `--yes` | flag | no | false | Skip confirmation prompts. |
2221

23-
\* Exactly one of `--claude-code` or `--codex` is required.
24-
\** Exactly one of `--user` or `--project` is required.
22+
\* Exactly one of `--user` or `--project` is required.
23+
24+
### `--tool` choices
25+
26+
| Choice | User-level path | Project-level path |
27+
|--------|----------------|--------------------|
28+
| `claude-code` | `~/.claude/skills` | `.claude/skills` |
29+
| `codex` | `~/.codex/skills` | `.codex/skills` |
30+
| `github-copilot` | `~/.github/copilot/skills` | `.github/copilot/skills` |
31+
| `cursor` | `~/.cursor/skills` | `.cursor/skills` |
32+
| `qoder` | `~/.qoder/skills` | `.qoder/skills` |
2533

2634
## Behavior
2735

2836
- By default, all platform skills are selected (unless `--workspace` is provided).
29-
- Before downloading/updating skills, the CLI asks for confirmation.
37+
- Before downloading/updating skills, the CLI asks for confirmation (skip with `-y`).
3038
- Sync checks local metadata and only downloads skills that are missing or outdated.
3139
- When `--delete-unmanaged` is enabled, local skill directories not in the selected
32-
managed scope can be removed after confirmation.
40+
managed scope can be removed after a separate confirmation.
3341

3442
## Examples
3543

3644
```bash
3745
# Sync skills from workspace abc + def into user-level Claude Code skills
38-
ar sync-skills --claude-code --user --workspace abc --workspace def
46+
ar sync-skills --tool claude-code --user --workspace abc --workspace def
3947

4048
# Sync skills from workspace abc into project-level Codex skills
41-
ar sync-skills --codex --project --workspace abc
49+
ar sync-skills --tool codex --project --workspace abc
50+
51+
# Sync all skills to project-level Cursor directory without prompts
52+
ar sync-skills --tool cursor --project -y
53+
54+
# Sync to GitHub Copilot user-level directory and remove unmanaged local skills
55+
ar sync-skills --tool github-copilot --user --delete-unmanaged
4256

43-
# Sync all skills and also remove unmanaged local skills
44-
ar sync-skills --claude-code --project --delete-unmanaged
57+
# Sync to Qoder project-level directory
58+
ar sync-skills --tool qoder --project --workspace my-workspace
4559
```

docs/zh/sync-skills.md

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,57 @@
22

33
# ar sync-skills
44

5-
把平台上的 Skill 同步到本地 AI 工具技能目录(Claude Code 或 Codex)
5+
把平台上的 Skill 同步到本地 AI 工具技能目录。
66

77
```
8-
ar sync-skills [tool] [scope] [options]
8+
ar sync-skills --tool <tool> (--user | --project) [options]
99
```
1010

1111
## 参数
1212

1313
| Flag | 类型 | 必填 | 默认 | 说明 |
1414
|------|------|------|------|------|
15-
| `--claude-code` | flag |* | | 同步到 Claude Code 技能目录。 |
16-
| `--codex` | flag |* | | 同步到 Codex 技能目录。 |
17-
| `--user` | flag |** | | 同步到用户级目录。 |
18-
| `--project` | flag |** | | 同步到项目级目录。 |
15+
| `--tool` | choice || | 目标 AI 工具,见下表。 |
16+
| `--user` | flag |* | | 同步到用户级目录。 |
17+
| `--project` | flag |* | | 同步到项目级目录。 |
1918
| `--workspace` | multi || 全部 | 工作空间过滤,可重复。 |
2019
| `--delete-unmanaged` | flag || false | 删除不在所选管控范围内的本地技能目录(需确认)。 |
2120
| `-y``--yes` | flag || false | 跳过确认提示。 |
2221

23-
\* `--claude-code``--codex` 二选一。
24-
\** `--user``--project` 二选一。
22+
\* `--user``--project` 二选一。
23+
24+
### `--tool` 可选值
25+
26+
| 选项 | 用户级路径 | 项目级路径 |
27+
|------|-----------|-----------|
28+
| `claude-code` | `~/.claude/skills` | `.claude/skills` |
29+
| `codex` | `~/.codex/skills` | `.codex/skills` |
30+
| `github-copilot` | `~/.github/copilot/skills` | `.github/copilot/skills` |
31+
| `cursor` | `~/.cursor/skills` | `.cursor/skills` |
32+
| `qoder` | `~/.qoder/skills` | `.qoder/skills` |
2533

2634
## 行为说明
2735

2836
- 默认同步全部平台 Skill(除非传入 `--workspace`)。
29-
- 在下载/更新 Skill 前会先进行用户确认。
37+
- 在下载/更新 Skill 前会先进行用户确认(传 `-y` 可跳过)
3038
- 同步会检查本地元数据,仅下载缺失或有更新的 Skill。
31-
- 开启 `--delete-unmanaged` 时,会在确认后删除不在当前管控范围内的本地 Skill 目录。
39+
- 开启 `--delete-unmanaged` 时,会在再次确认后删除不在当前管控范围内的本地 Skill 目录。
3240

3341
## 示例
3442

3543
```bash
3644
# 将 workspace abc + def 的 skills 同步到用户级 Claude Code 目录
37-
ar sync-skills --claude-code --user --workspace abc --workspace def
45+
ar sync-skills --tool claude-code --user --workspace abc --workspace def
3846

3947
# 将 workspace abc 的 skills 同步到项目级 Codex 目录
40-
ar sync-skills --codex --project --workspace abc
48+
ar sync-skills --tool codex --project --workspace abc
49+
50+
# 跳过确认,将全部 skills 同步到项目级 Cursor 目录
51+
ar sync-skills --tool cursor --project -y
52+
53+
# 同步到 GitHub Copilot 用户级目录,并删除不在管控范围的本地 skills
54+
ar sync-skills --tool github-copilot --user --delete-unmanaged
4155

42-
# 同步全部 skills,并删除不在管控范围内的本地 skills
43-
ar sync-skills --claude-code --project --delete-unmanaged
56+
# 同步到 Qoder 项目级目录
57+
ar sync-skills --tool qoder --project --workspace my-workspace
4458
```

src/agentrun_cli/commands/sync_skills_cmd.py

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@
1313

1414
_META_FILE_NAME = ".agentrun-sync-skills.json"
1515

16+
_TOOL_CHOICES = (
17+
"claude-code",
18+
"codex",
19+
"github-copilot",
20+
"cursor",
21+
"qoder",
22+
)
23+
1624

1725
def _ctx_cfg(ctx):
1826
return (ctx.obj or {}).get("profile"), (ctx.obj or {}).get("region")
@@ -72,19 +80,23 @@ def _extract_workspace_values(skill) -> set[str]:
7280
return values
7381

7482

83+
_TOOL_ROOTS: dict[str, tuple[str, str]] = {
84+
# (user_root, project_subdir_name)
85+
"claude-code": ("~/.claude", ".claude"),
86+
"codex": ("~/.codex", ".codex"),
87+
"github-copilot": ("~/.github/copilot", ".github/copilot"),
88+
"cursor": ("~/.cursor", ".cursor"),
89+
"qoder": ("~/.qoder", ".qoder"),
90+
}
91+
92+
7593
def _resolve_target_dir(ai_tool: str, scope: str) -> str:
76-
if ai_tool == "claude-code":
77-
root = (
78-
os.path.expanduser("~/.claude")
79-
if scope == "user"
80-
else os.path.abspath(".claude")
81-
)
82-
else:
83-
root = (
84-
os.path.expanduser("~/.codex")
85-
if scope == "user"
86-
else os.path.abspath(".codex")
87-
)
94+
user_root, project_dir = _TOOL_ROOTS[ai_tool]
95+
root = (
96+
os.path.expanduser(user_root)
97+
if scope == "user"
98+
else os.path.abspath(project_dir)
99+
)
88100
return os.path.join(root, "skills")
89101

90102

@@ -148,21 +160,16 @@ def _list_platform_skills(profile, region):
148160

149161
@click.command(
150162
"sync-skills",
151-
help="Sync platform skills to Claude Code or Codex skill directories.",
152-
)
153-
@click.option(
154-
"--claude-code",
155-
"use_claude_code",
156-
is_flag=True,
157-
default=False,
158-
help="Sync to Claude Code skill directory.",
163+
help="Sync platform skills to a local AI tool skill directory.",
159164
)
160165
@click.option(
161-
"--codex",
162-
"use_codex",
163-
is_flag=True,
164-
default=False,
165-
help="Sync to Codex skill directory.",
166+
"--tool",
167+
"ai_tool",
168+
type=click.Choice(_TOOL_CHOICES),
169+
required=True,
170+
help=(
171+
"Target AI tool: claude-code, codex, github-copilot, cursor, qoder."
172+
),
166173
)
167174
@click.option(
168175
"--user",
@@ -205,23 +212,17 @@ def _list_platform_skills(profile, region):
205212
@handle_errors
206213
def sync_skills(
207214
ctx,
208-
use_claude_code,
209-
use_codex,
215+
ai_tool,
210216
user_scope,
211217
project_scope,
212218
workspaces,
213219
delete_unmanaged,
214220
auto_confirm,
215221
):
216222
"""Sync platform skills to local AI tool skill directories."""
217-
if use_claude_code == use_codex:
218-
raise click.UsageError(
219-
"You must specify exactly one of --claude-code or --codex."
220-
)
221223
if user_scope == project_scope:
222224
raise click.UsageError("You must specify exactly one of --user or --project.")
223225

224-
ai_tool = "claude-code" if use_claude_code else "codex"
225226
scope = "user" if user_scope else "project"
226227
target_dir = _resolve_target_dir(ai_tool, scope)
227228
os.makedirs(target_dir, exist_ok=True)

tests/integration/test_sync_skills_cmd.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,35 @@ def test_sync_success(self, mock_client_fn, mock_get_tool, _mock_cfg):
4646
runner = CliRunner()
4747
result = runner.invoke(
4848
cli,
49-
["sync-skills", "--claude-code", "--project", "-y"],
49+
["sync-skills", "--tool", "claude-code", "--project", "-y"],
5050
)
5151

5252
assert result.exit_code == 0, result.output
5353
out = json.loads(result.output)
5454
assert out["ai_tool"] == "claude-code"
5555
assert out["managed_skill_total"] == 1
5656

57-
def test_sync_usage_error(self):
57+
def test_sync_usage_error_missing_scope(self):
5858
runner = CliRunner()
5959
result = runner.invoke(
6060
cli,
61-
["sync-skills", "--claude-code", "--user", "--project"],
61+
["sync-skills", "--tool", "cursor"],
6262
)
6363
assert result.exit_code != 0
6464
assert "--user or --project" in result.output
65+
66+
def test_sync_usage_error_both_scopes(self):
67+
runner = CliRunner()
68+
result = runner.invoke(
69+
cli,
70+
["sync-skills", "--tool", "claude-code", "--user", "--project"],
71+
)
72+
assert result.exit_code != 0
73+
assert "--user or --project" in result.output
74+
75+
def test_sync_help_lists_all_tools(self):
76+
runner = CliRunner()
77+
result = runner.invoke(cli, ["sync-skills", "--help"])
78+
assert result.exit_code == 0
79+
for tool in ("claude-code", "codex", "github-copilot", "cursor", "qoder"):
80+
assert tool in result.output

0 commit comments

Comments
 (0)