neo-skill 0.1.18 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.shared/skill-creator/data_packs/tools/README.md +16 -11
- package/README.md +7 -2
- package/package.json +1 -1
- package/skills/skill-creator/references/architecture-rules.md +204 -0
- package/src/omni_skill/README.md +58 -2
- package/src/omni_skill/cli.py +121 -13
- package/src/omni_skill/doctor.py +323 -0
- package/src/omni_skill/install.py +352 -0
- package/src/skill_creator/core/builder.py +16 -0
- package/src/skill_creator/core/orchestrator.py +12 -0
- package/src/skill_creator/spec/model.py +2 -0
- package/src/skill_creator/targets/README.md +22 -1
- package/src/skill_creator/targets/claude.py +6 -1
- package/src/skill_creator/targets/common.py +83 -1
- package/src/skill_creator/targets/cursor.py +7 -0
- package/src/skill_creator/targets/github_skills.py +7 -1
- package/src/skill_creator/targets/windsurf.py +5 -10
|
@@ -43,17 +43,22 @@
|
|
|
43
43
|
}
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
##
|
|
47
|
-
|
|
48
|
-
1.
|
|
49
|
-
2.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
## 如何添加新工具
|
|
47
|
+
|
|
48
|
+
1. 创建工具详情文件 `{tool-name}.json`
|
|
49
|
+
2. 在 `index.json` 中添加索引条目
|
|
50
|
+
3. 确保 tags 与 capability_tags 设计一致
|
|
51
|
+
|
|
52
|
+
## 自动推荐机制
|
|
53
|
+
|
|
54
|
+
skill-creator 会**自动推荐**合适的第三方库,无需用户手动指定:
|
|
55
|
+
|
|
56
|
+
- **自动触发**:基于 `task_type` 和 `capability_tags` 自动匹配
|
|
57
|
+
- **智能排序**:返回 top-5 最匹配的工具库
|
|
58
|
+
- **多处展示**:推荐信息会在 Plan、Workflow Steps 和最终输出中展示
|
|
59
|
+
- **安全降级**:如果没有匹配的库,不影响 skill 生成
|
|
60
|
+
|
|
61
|
+
详见:`docs/skill-creator-refactoring/LIBRARY_RECOMMENDATION.md`
|
|
57
62
|
}
|
|
58
63
|
```
|
|
59
64
|
3. 无需修改代码,系统会自动检索
|
package/README.md
CHANGED
|
@@ -85,7 +85,10 @@ omni-skill init --ai all # 初始化所有支持的 AI 助手
|
|
|
85
85
|
|
|
86
86
|
这会:
|
|
87
87
|
- 从 npm 包同步 skills/ 和 .shared/ 到当前目录
|
|
88
|
-
- 为所有 skills
|
|
88
|
+
- 为所有 skills 生成**指定 AI 助手**的输出文件
|
|
89
|
+
- `--ai windsurf` → 只生成 `.windsurf/workflows/`
|
|
90
|
+
- `--ai claude` → 只生成 `.claude/skills/`
|
|
91
|
+
- `--ai all` → 生成所有目标
|
|
89
92
|
- 保存初始化状态到 .neo-skill.json
|
|
90
93
|
|
|
91
94
|
**2. 使用 skill-creator 创建新 skill**
|
|
@@ -103,7 +106,9 @@ omni-skill install ./skills
|
|
|
103
106
|
|
|
104
107
|
这会:
|
|
105
108
|
- 复制 skill 到当前目录的 skills/ 文件夹
|
|
106
|
-
- 为该 skill
|
|
109
|
+
- 为该 skill 生成**所有 AI 助手**的输出文件(.windsurf, .claude, .cursor, .github)
|
|
110
|
+
|
|
111
|
+
**注意**:与 `init` 不同,`install` 命令总是生成所有 AI 目标的输出,以确保最大兼容性。
|
|
107
112
|
|
|
108
113
|
**4. 更新 npm 包**
|
|
109
114
|
```bash
|
package/package.json
CHANGED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Skill Creator 架构规则(强制约束)
|
|
2
|
+
|
|
3
|
+
## 重要提示:AI 必须遵守以下规则
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. 禁止单文件脚本(Monolith)
|
|
8
|
+
|
|
9
|
+
### 规则
|
|
10
|
+
|
|
11
|
+
**严格禁止**生成任何"单文件可执行脚本"作为最终 skill 产物。
|
|
12
|
+
|
|
13
|
+
### 原因
|
|
14
|
+
|
|
15
|
+
- 违反分层索引原则
|
|
16
|
+
- 破坏可扩展性
|
|
17
|
+
- 难以维护和复用
|
|
18
|
+
- 无法利用数据包体系
|
|
19
|
+
|
|
20
|
+
### 允许的形态
|
|
21
|
+
|
|
22
|
+
✅ **Structured Skill**(唯一允许):
|
|
23
|
+
- 分层索引文件(index.json)
|
|
24
|
+
- 被索引的数据文件(items/*.json)
|
|
25
|
+
- Skill 主文档({{skill-id}}.md)
|
|
26
|
+
- 运行时依赖的最小闭包
|
|
27
|
+
|
|
28
|
+
❌ **Monolith Script**(严格禁止):
|
|
29
|
+
- 单个 Python/Shell 脚本包含所有逻辑
|
|
30
|
+
- 不使用索引系统
|
|
31
|
+
- 硬编码数据和逻辑
|
|
32
|
+
|
|
33
|
+
### AI 指令
|
|
34
|
+
|
|
35
|
+
**不得输出 Monolith**
|
|
36
|
+
**不得建议 Monolith 作为解决方案**
|
|
37
|
+
**即使更简单也必须使用分层索引结构**
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 2. Install Space 路径规范
|
|
42
|
+
|
|
43
|
+
### 路径空间定义
|
|
44
|
+
|
|
45
|
+
#### Source Space(源文件空间)
|
|
46
|
+
- 仅用于构建/安装阶段
|
|
47
|
+
- 位置:`skills/`, `.shared/`, `src/`
|
|
48
|
+
- **禁止**:运行时依赖
|
|
49
|
+
|
|
50
|
+
#### Install Space(安装后运行空间)
|
|
51
|
+
- 仅用于运行时
|
|
52
|
+
- 位置:`.windsurf/`, `.claude/`, `.cursor/`, `.github/`
|
|
53
|
+
- **要求**:所有索引路径必须指向此空间
|
|
54
|
+
|
|
55
|
+
### 路径写法规范
|
|
56
|
+
|
|
57
|
+
✅ **正确**:使用 Install Space 占位符
|
|
58
|
+
```markdown
|
|
59
|
+
- Data: `{{install_root}}/.windsurf/workflows/data/{{skill-id}}/`
|
|
60
|
+
- Index: `{{install_root}}/.windsurf/workflows/data/{{skill-id}}/index.json`
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
❌ **错误**:引用 Source Space 路径
|
|
64
|
+
```markdown
|
|
65
|
+
- Data: `skills/{{skill-id}}/data/`
|
|
66
|
+
- Index: `.shared/skill-creator/data_packs/`
|
|
67
|
+
- Relative: `../data/`, `../../skills/`
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 验证
|
|
71
|
+
|
|
72
|
+
使用 `omni-skill doctor --skill <skill-id>` 验证路径正确性。
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 3. 三方库规则
|
|
77
|
+
|
|
78
|
+
### 自动采用,无需确认
|
|
79
|
+
|
|
80
|
+
**规则**:
|
|
81
|
+
- ✅ 自动判断并采用三方库
|
|
82
|
+
- ❌ 不要求用户确认
|
|
83
|
+
- ❌ 不要求用户选择 A/B/C
|
|
84
|
+
|
|
85
|
+
### 结果汇报(强制)
|
|
86
|
+
|
|
87
|
+
**必须在最终输出中列出**:
|
|
88
|
+
|
|
89
|
+
```markdown
|
|
90
|
+
## 使用的第三方库
|
|
91
|
+
|
|
92
|
+
### 1. requests
|
|
93
|
+
- **版本策略**:>=2.28.0
|
|
94
|
+
- **用途**:发起 HTTP 请求
|
|
95
|
+
- **安装方式**:`pip install requests>=2.28.0`
|
|
96
|
+
- **文档链接**:
|
|
97
|
+
- PyPI: https://pypi.org/project/requests/
|
|
98
|
+
- Docs: https://requests.readthedocs.io/
|
|
99
|
+
|
|
100
|
+
### 2. feedparser
|
|
101
|
+
- **版本策略**:>=6.0.0
|
|
102
|
+
- **用途**:解析 RSS/Atom feed
|
|
103
|
+
- **安装方式**:`pip install feedparser>=6.0.0`
|
|
104
|
+
- **文档链接**:
|
|
105
|
+
- PyPI: https://pypi.org/project/feedparser/
|
|
106
|
+
- Docs: https://feedparser.readthedocs.io/
|
|
107
|
+
|
|
108
|
+
### Fallback 方案(可选)
|
|
109
|
+
- 如果不使用 `requests`,可使用标准库 `urllib`
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 4. 依赖闭包原则
|
|
115
|
+
|
|
116
|
+
### 最小闭包
|
|
117
|
+
|
|
118
|
+
**要求**:
|
|
119
|
+
- ✅ 只拷贝该 skill 需要的最小闭包
|
|
120
|
+
- ❌ 不原封不动复制整个源目录
|
|
121
|
+
- ❌ 不拷贝未被引用的文件
|
|
122
|
+
|
|
123
|
+
### 闭包内容
|
|
124
|
+
|
|
125
|
+
每个 skill 的依赖闭包包括:
|
|
126
|
+
1. Skill 主文档({{skill-id}}.md)
|
|
127
|
+
2. 索引文件(index.json 等)
|
|
128
|
+
3. 索引命中的 items
|
|
129
|
+
4. 必需不变量(universal schema, output packs, minimal checklists)
|
|
130
|
+
5. 三方库声明(requirements.txt)
|
|
131
|
+
6. References/Scripts/Assets
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 5. 生成流程
|
|
136
|
+
|
|
137
|
+
### 步骤
|
|
138
|
+
|
|
139
|
+
1. **分析需求**:提取任务类型、输出形态、约束条件
|
|
140
|
+
2. **收集信息**:对话式收集(≤10 问)
|
|
141
|
+
3. **设计系统**:分层索引 + 文件化数据包
|
|
142
|
+
4. **生成 SkillSpec**:写入 `skills/<name>/skillspec.json`
|
|
143
|
+
5. **生成输出**:为所有 AI 目标生成文档
|
|
144
|
+
6. **验证**:Schema 校验 + Dry-run
|
|
145
|
+
|
|
146
|
+
### 禁止事项
|
|
147
|
+
|
|
148
|
+
❌ 不生成单文件脚本
|
|
149
|
+
❌ 不在文档中引用 Source Space 路径
|
|
150
|
+
❌ 不要求用户确认三方库选型
|
|
151
|
+
❌ 不复制整个源目录
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 6. 验收标准
|
|
156
|
+
|
|
157
|
+
### 功能验收
|
|
158
|
+
|
|
159
|
+
✅ Skill 产物自包含
|
|
160
|
+
✅ 索引路径只指向 Install Space
|
|
161
|
+
✅ 使用分层索引结构
|
|
162
|
+
✅ 只拷贝最小闭包文件
|
|
163
|
+
|
|
164
|
+
### 质量验收
|
|
165
|
+
|
|
166
|
+
✅ 可追溯性:Install manifest 记录完整
|
|
167
|
+
✅ 可复现性:相同输入产生相同输出
|
|
168
|
+
✅ 可维护性:代码结构清晰,文档完整
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 7. 示例
|
|
173
|
+
|
|
174
|
+
### 正确的 Skill 结构
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
skills/review-gate/
|
|
178
|
+
├── skillspec.json # Skill 定义
|
|
179
|
+
├── references/ # 规则文档
|
|
180
|
+
│ └── checklist-rules.md
|
|
181
|
+
├── scripts/ # 确定性脚本
|
|
182
|
+
│ └── validate.py
|
|
183
|
+
└── assets/ # 数据文件
|
|
184
|
+
└── templates/
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 正确的索引引用
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"version": "1.0",
|
|
192
|
+
"items": {
|
|
193
|
+
"checklist-1": {
|
|
194
|
+
"file": "{{install_root}}/.windsurf/workflows/data/review-gate/checklists/checklist-1.json"
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
**本规则为强制约束,所有 AI 生成的 skill 必须遵守。**
|
|
203
|
+
|
|
204
|
+
详见:`docs/ARCHITECTURE_RULES.md`
|
package/src/omni_skill/README.md
CHANGED
|
@@ -25,10 +25,15 @@ omni-skill init --ai all
|
|
|
25
25
|
|
|
26
26
|
**What it does:**
|
|
27
27
|
1. Syncs skills/ and .shared/ from npm package to current directory
|
|
28
|
-
2. Installs all skills (generates outputs for
|
|
28
|
+
2. Installs all skills (generates outputs for **specified AI targets only**)
|
|
29
29
|
3. Writes VERSION files for each AI target
|
|
30
30
|
4. Saves init state to .neo-skill.json
|
|
31
31
|
|
|
32
|
+
**Example:**
|
|
33
|
+
- `omni-skill init --ai windsurf` → Only generates `.windsurf/workflows/`
|
|
34
|
+
- `omni-skill init --ai claude` → Only generates `.claude/skills/`
|
|
35
|
+
- `omni-skill init --ai all` → Generates all targets (.windsurf, .claude, .cursor, .github)
|
|
36
|
+
|
|
32
37
|
### `omni-skill install <path>`
|
|
33
38
|
Install skill(s) from a local directory.
|
|
34
39
|
|
|
@@ -43,7 +48,58 @@ omni-skill install ./skills
|
|
|
43
48
|
|
|
44
49
|
**What it does:**
|
|
45
50
|
1. Copies skill(s) to current directory's skills/ folder
|
|
46
|
-
2. Generates outputs for all AI targets (.windsurf, .claude, .cursor, .github)
|
|
51
|
+
2. Generates outputs for **all AI targets** (.windsurf, .claude, .cursor, .github)
|
|
52
|
+
|
|
53
|
+
**Note:** Unlike `init`, the `install` command always generates outputs for all AI targets to ensure maximum compatibility.
|
|
54
|
+
|
|
55
|
+
### `omni-skill install-skill <skill-id>`
|
|
56
|
+
Install skill with dependency closure (new architecture).
|
|
57
|
+
|
|
58
|
+
**Usage:**
|
|
59
|
+
```bash
|
|
60
|
+
omni-skill install-skill review-gate --target windsurf
|
|
61
|
+
omni-skill install-skill skill-creator --target claude
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**What it does:**
|
|
65
|
+
1. Resolves skill dependency closure (minimal files)
|
|
66
|
+
2. Materializes files to Install Space
|
|
67
|
+
3. Generates install manifest
|
|
68
|
+
4. Validates paths (no source path leakage)
|
|
69
|
+
|
|
70
|
+
**Architecture:**
|
|
71
|
+
- Uses Source Space (skills/) for build
|
|
72
|
+
- Copies to Install Space (.windsurf/, .claude/, etc.) for runtime
|
|
73
|
+
- Only copies minimal closure (not entire source directory)
|
|
74
|
+
|
|
75
|
+
### `omni-skill doctor --skill <skill-id>`
|
|
76
|
+
Diagnose skill installation and dependencies.
|
|
77
|
+
|
|
78
|
+
**Usage:**
|
|
79
|
+
```bash
|
|
80
|
+
omni-skill doctor --skill review-gate --target windsurf
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**What it checks:**
|
|
84
|
+
- Install manifest exists
|
|
85
|
+
- All referenced files exist
|
|
86
|
+
- No source path leakage
|
|
87
|
+
- Index files are valid
|
|
88
|
+
- Dependency closure is complete
|
|
89
|
+
|
|
90
|
+
**Output:**
|
|
91
|
+
```
|
|
92
|
+
=== Skill Diagnostic Report ===
|
|
93
|
+
Skill ID: review-gate
|
|
94
|
+
Install Root: .windsurf/workflows/data/review-gate
|
|
95
|
+
|
|
96
|
+
--- Path Validation ---
|
|
97
|
+
✓ No source path leakage detected
|
|
98
|
+
✓ All index paths point to Install Space
|
|
99
|
+
✓ All referenced files exist
|
|
100
|
+
|
|
101
|
+
=== Diagnostic Complete ===
|
|
102
|
+
```
|
|
47
103
|
|
|
48
104
|
### `omni-skill update`
|
|
49
105
|
Update npm package and re-initialize skills.
|
package/src/omni_skill/cli.py
CHANGED
|
@@ -4,9 +4,11 @@ import argparse
|
|
|
4
4
|
import json
|
|
5
5
|
import shutil
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Dict, List, Set
|
|
7
|
+
from typing import Dict, List, Optional, Set
|
|
8
8
|
|
|
9
9
|
from skill_creator.cli import cmd_generate
|
|
10
|
+
from .install import SkillInstaller
|
|
11
|
+
from .doctor import SkillDoctor
|
|
10
12
|
|
|
11
13
|
STATE_FILE = ".neo-skill.json"
|
|
12
14
|
|
|
@@ -192,10 +194,15 @@ def _generate_outputs_best_effort(pkg_root: Path, cwd: Path) -> None:
|
|
|
192
194
|
print(f" Skipping generator for {rel} (exit {e.code})")
|
|
193
195
|
|
|
194
196
|
|
|
195
|
-
def _install_skills_from_dir(skills_dir: Path, cwd: Path) -> int:
|
|
197
|
+
def _install_skills_from_dir(skills_dir: Path, cwd: Path, selected_ais: Optional[List[str]] = None) -> int:
|
|
196
198
|
"""
|
|
197
199
|
Install skills from a directory (either from npm package or local path).
|
|
198
|
-
Generates outputs for
|
|
200
|
+
Generates outputs for specified AI targets.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
skills_dir: Directory containing skills
|
|
204
|
+
cwd: Current working directory
|
|
205
|
+
selected_ais: List of AI targets to generate for. If None, generates for all targets.
|
|
199
206
|
"""
|
|
200
207
|
if not skills_dir.exists():
|
|
201
208
|
print(f"Skills directory not found: {skills_dir}")
|
|
@@ -206,6 +213,37 @@ def _install_skills_from_dir(skills_dir: Path, cwd: Path) -> int:
|
|
|
206
213
|
print(f"No skillspec.json found in: {skills_dir}")
|
|
207
214
|
return 1
|
|
208
215
|
|
|
216
|
+
# Determine target mapping: AI assistant -> skill-creator target
|
|
217
|
+
ai_to_target = {
|
|
218
|
+
"windsurf": "windsurf",
|
|
219
|
+
"claude": "claude",
|
|
220
|
+
"cursor": "cursor",
|
|
221
|
+
"antigravity": "github",
|
|
222
|
+
"copilot": "github",
|
|
223
|
+
"kiro": "github",
|
|
224
|
+
"codex": "github",
|
|
225
|
+
"qoder": "github",
|
|
226
|
+
"roocode": "github",
|
|
227
|
+
"gemini": "github",
|
|
228
|
+
"trae": "github",
|
|
229
|
+
"opencode": "github",
|
|
230
|
+
"continue": "github",
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
# Determine which targets to generate
|
|
234
|
+
if selected_ais is None:
|
|
235
|
+
# Generate all targets
|
|
236
|
+
generate_all_targets = True
|
|
237
|
+
targets_list = None
|
|
238
|
+
else:
|
|
239
|
+
# Map AI assistants to skill-creator targets
|
|
240
|
+
targets_set = set()
|
|
241
|
+
for ai in selected_ais:
|
|
242
|
+
target = ai_to_target.get(ai, "windsurf")
|
|
243
|
+
targets_set.add(target)
|
|
244
|
+
targets_list = sorted(targets_set)
|
|
245
|
+
generate_all_targets = False
|
|
246
|
+
|
|
209
247
|
print(f"\nInstalling {len(specs)} skill(s) from {skills_dir} ...")
|
|
210
248
|
for spec_path in specs:
|
|
211
249
|
skill_name = spec_path.parent.name
|
|
@@ -221,11 +259,18 @@ def _install_skills_from_dir(skills_dir: Path, cwd: Path) -> int:
|
|
|
221
259
|
shutil.copytree(spec_path.parent, dest_skill_dir)
|
|
222
260
|
print(f" Copied to: {dest_skill_dir}")
|
|
223
261
|
|
|
224
|
-
# Generate outputs for
|
|
262
|
+
# Generate outputs for specified targets
|
|
225
263
|
try:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
264
|
+
if generate_all_targets:
|
|
265
|
+
ns = argparse.Namespace(repo_root=str(cwd), spec=str(cwd / "skills" / skill_name / "skillspec.json"), all=True)
|
|
266
|
+
cmd_generate(ns)
|
|
267
|
+
print(f" Generated outputs for all targets")
|
|
268
|
+
else:
|
|
269
|
+
# Generate for each target separately
|
|
270
|
+
for target in targets_list:
|
|
271
|
+
ns = argparse.Namespace(repo_root=str(cwd), spec=str(cwd / "skills" / skill_name / "skillspec.json"), all=False, target=target)
|
|
272
|
+
cmd_generate(ns)
|
|
273
|
+
print(f" Generated outputs for: {', '.join(targets_list)}")
|
|
229
274
|
except SystemExit as e:
|
|
230
275
|
print(f" Warning: Generator failed (exit {e.code})")
|
|
231
276
|
|
|
@@ -242,10 +287,10 @@ def _handle_init(selected_ais: List[str], mode: str) -> int:
|
|
|
242
287
|
print("Initializing skills in:", cwd)
|
|
243
288
|
_perform_sync(pkg_root, cwd, sync_pairs)
|
|
244
289
|
|
|
245
|
-
# Install all skills from npm package
|
|
290
|
+
# Install all skills from npm package with specified AI targets
|
|
246
291
|
pkg_skills_dir = pkg_root / "skills"
|
|
247
292
|
if pkg_skills_dir.exists():
|
|
248
|
-
_install_skills_from_dir(pkg_skills_dir, cwd)
|
|
293
|
+
_install_skills_from_dir(pkg_skills_dir, cwd, selected_ais=selected_ais)
|
|
249
294
|
|
|
250
295
|
_write_version_files(cwd, effective_ais, version)
|
|
251
296
|
print("\nDone! neo-skill initialized.")
|
|
@@ -267,10 +312,39 @@ def _cmd_init(args: argparse.Namespace) -> int:
|
|
|
267
312
|
return _handle_init(resolved["selected"], "init")
|
|
268
313
|
|
|
269
314
|
|
|
315
|
+
def _cmd_install_new(args: argparse.Namespace) -> int:
|
|
316
|
+
"""
|
|
317
|
+
Install skill with dependency closure (new architecture).
|
|
318
|
+
Usage: omni-skill install <skill-id> [--target <agent>]
|
|
319
|
+
"""
|
|
320
|
+
cwd = Path.cwd().resolve()
|
|
321
|
+
pkg_root = _get_pkg_root()
|
|
322
|
+
|
|
323
|
+
skill_id = args.skill_id
|
|
324
|
+
target = getattr(args, 'target', 'windsurf')
|
|
325
|
+
|
|
326
|
+
# 使用新的 SkillInstaller
|
|
327
|
+
installer = SkillInstaller(source_root=pkg_root, install_root=cwd)
|
|
328
|
+
|
|
329
|
+
try:
|
|
330
|
+
manifest = installer.install(skill_id, target)
|
|
331
|
+
print(f"\n✓ Successfully installed {skill_id}")
|
|
332
|
+
print(f" Install root: {manifest.install_root}")
|
|
333
|
+
print(f" Files: {len(manifest.files)}")
|
|
334
|
+
if manifest.dependencies.get('libraries'):
|
|
335
|
+
print(f" Libraries: {', '.join(manifest.dependencies['libraries'])}")
|
|
336
|
+
return 0
|
|
337
|
+
except Exception as e:
|
|
338
|
+
print(f"\n✗ Installation failed: {e}")
|
|
339
|
+
return 1
|
|
340
|
+
|
|
341
|
+
|
|
270
342
|
def _cmd_install(args: argparse.Namespace) -> int:
|
|
271
343
|
"""
|
|
272
|
-
Install skill(s) from a local directory.
|
|
344
|
+
Install skill(s) from a local directory (legacy).
|
|
273
345
|
Usage: omni-skill install <path-to-skill-or-skills-dir>
|
|
346
|
+
|
|
347
|
+
Note: install command always generates outputs for all AI targets.
|
|
274
348
|
"""
|
|
275
349
|
cwd = Path.cwd().resolve()
|
|
276
350
|
skill_path = Path(args.path).resolve()
|
|
@@ -280,13 +354,13 @@ def _cmd_install(args: argparse.Namespace) -> int:
|
|
|
280
354
|
|
|
281
355
|
# Check if it's a single skill directory (contains skillspec.json)
|
|
282
356
|
if (skill_path / "skillspec.json").exists():
|
|
283
|
-
# Single skill
|
|
357
|
+
# Single skill - generate for all targets
|
|
284
358
|
temp_skills_dir = skill_path.parent
|
|
285
|
-
return _install_skills_from_dir(temp_skills_dir, cwd)
|
|
359
|
+
return _install_skills_from_dir(temp_skills_dir, cwd, selected_ais=None)
|
|
286
360
|
|
|
287
361
|
# Check if it's a skills directory (contains subdirs with skillspec.json)
|
|
288
362
|
elif skill_path.is_dir():
|
|
289
|
-
return _install_skills_from_dir(skill_path, cwd)
|
|
363
|
+
return _install_skills_from_dir(skill_path, cwd, selected_ais=None)
|
|
290
364
|
|
|
291
365
|
else:
|
|
292
366
|
raise SystemExit(f"Invalid path: {skill_path}. Must be a skill directory or skills directory.")
|
|
@@ -332,6 +406,28 @@ def _print_init_help() -> None:
|
|
|
332
406
|
print(" omni-skill init --ai all")
|
|
333
407
|
|
|
334
408
|
|
|
409
|
+
def _cmd_doctor(args: argparse.Namespace) -> int:
|
|
410
|
+
"""
|
|
411
|
+
Diagnose skill installation and dependencies.
|
|
412
|
+
Usage: omni-skill doctor --skill <skill-id> [--target <agent>]
|
|
413
|
+
"""
|
|
414
|
+
cwd = Path.cwd().resolve()
|
|
415
|
+
|
|
416
|
+
skill_id = args.skill_id
|
|
417
|
+
target = getattr(args, 'target', 'windsurf')
|
|
418
|
+
|
|
419
|
+
# 使用 SkillDoctor
|
|
420
|
+
doctor = SkillDoctor(install_root=cwd)
|
|
421
|
+
|
|
422
|
+
report = doctor.diagnose(skill_id, target)
|
|
423
|
+
print(report.format())
|
|
424
|
+
|
|
425
|
+
# 返回错误码
|
|
426
|
+
if report.has_errors():
|
|
427
|
+
return 1
|
|
428
|
+
return 0
|
|
429
|
+
|
|
430
|
+
|
|
335
431
|
def build_parser() -> argparse.ArgumentParser:
|
|
336
432
|
p = argparse.ArgumentParser(
|
|
337
433
|
prog="omni-skill",
|
|
@@ -347,6 +443,18 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
347
443
|
p_install = sub.add_parser("install", help="Install skill(s) from a local directory")
|
|
348
444
|
p_install.add_argument("path", help="Path to skill directory or skills directory")
|
|
349
445
|
p_install.set_defaults(func=_cmd_install)
|
|
446
|
+
|
|
447
|
+
# 新增:install-skill 命令(使用新架构)
|
|
448
|
+
p_install_new = sub.add_parser("install-skill", help="Install skill with dependency closure (new)")
|
|
449
|
+
p_install_new.add_argument("skill_id", help="Skill ID to install")
|
|
450
|
+
p_install_new.add_argument("--target", default="windsurf", help="Target AI (windsurf/claude/cursor/github)")
|
|
451
|
+
p_install_new.set_defaults(func=_cmd_install_new)
|
|
452
|
+
|
|
453
|
+
# 新增:doctor 命令
|
|
454
|
+
p_doctor = sub.add_parser("doctor", help="Diagnose skill installation")
|
|
455
|
+
p_doctor.add_argument("--skill", dest="skill_id", required=True, help="Skill ID to diagnose")
|
|
456
|
+
p_doctor.add_argument("--target", default="windsurf", help="Target AI (windsurf/claude/cursor/github)")
|
|
457
|
+
p_doctor.set_defaults(func=_cmd_doctor)
|
|
350
458
|
|
|
351
459
|
p_update = sub.add_parser("update", help="Update npm package and re-initialize skills")
|
|
352
460
|
p_update.set_defaults(func=_cmd_update)
|