neo-skill 0.1.17 → 0.1.19

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/README.md CHANGED
@@ -22,14 +22,17 @@ pip install neo-skill
22
22
  ### 命令行工具(安装后)
23
23
 
24
24
  ```bash
25
- # 初始化技能
26
- omni-skill init --ai claude
25
+ # 初始化技能(从 npm 包安装所有 skills)
26
+ omni-skill init --ai windsurf
27
27
 
28
- # 生成技能输出
29
- skill-creator generate skills/skill-name/skillspec.json
28
+ # 手动安装本地创建的 skill
29
+ omni-skill install ./skills/my-new-skill
30
30
 
31
- # 验证技能
32
- skill-creator validate skills/skill-name/skillspec.json
31
+ # 更新 npm 包并重新初始化
32
+ omni-skill update
33
+
34
+ # 查看帮助
35
+ omni-skill --help
33
36
  ```
34
37
 
35
38
  ### 直接运行 Python 模块(开发模式)
@@ -72,29 +75,51 @@ python -m skill_creator.cli validate skills/skill-name/skillspec.json
72
75
 
73
76
  ### 使用场景
74
77
 
75
- **初始化技能文件**
78
+ **1. 初始化技能(首次使用)**
76
79
  ```bash
77
80
  # 初始化指定 AI 助手的技能文件
78
- omni-skill init --ai claude
79
81
  omni-skill init --ai windsurf
82
+ omni-skill init --ai claude
80
83
  omni-skill init --ai all # 初始化所有支持的 AI 助手
81
-
82
- # 更新(基于上次 init 的配置)
83
- omni-skill update
84
84
  ```
85
85
 
86
- **生成和验证技能**
86
+ 这会:
87
+ - 从 npm 包同步 skills/ 和 .shared/ 到当前目录
88
+ - 为所有 skills 生成**指定 AI 助手**的输出文件
89
+ - `--ai windsurf` → 只生成 `.windsurf/workflows/`
90
+ - `--ai claude` → 只生成 `.claude/skills/`
91
+ - `--ai all` → 生成所有目标
92
+ - 保存初始化状态到 .neo-skill.json
93
+
94
+ **2. 使用 skill-creator 创建新 skill**
95
+
96
+ 在 IDE 中输入 `/skill-creator`(Windsurf)或使用其他 AI 助手的触发方式,按照对话式流程创建新 skill。
97
+
98
+ **3. 安装刚创建的 skill**
87
99
  ```bash
88
- # 生成技能输出
89
- skill-creator generate skills/skill-name/skillspec.json
100
+ # 安装单个 skill
101
+ omni-skill install ./skills/my-new-skill
90
102
 
91
- # 验证技能
92
- skill-creator validate skills/skill-name/skillspec.json
103
+ # 或安装整个 skills 目录
104
+ omni-skill install ./skills
105
+ ```
93
106
 
94
- # 打包 Claude 技能
95
- skill-creator package --target claude --skill skill-name
107
+ 这会:
108
+ - 复制 skill 到当前目录的 skills/ 文件夹
109
+ - 为该 skill 生成**所有 AI 助手**的输出文件(.windsurf, .claude, .cursor, .github)
110
+
111
+ **注意**:与 `init` 不同,`install` 命令总是生成所有 AI 目标的输出,以确保最大兼容性。
112
+
113
+ **4. 更新 npm 包**
114
+ ```bash
115
+ # 更新到最新版本并重新初始化
116
+ omni-skill update
96
117
  ```
97
118
 
119
+ 这会:
120
+ - 运行 `npm install neo-skill@latest`
121
+ - 重新执行 `omni-skill init`(使用保存的 AI 目标)
122
+
98
123
  **在其他项目中使用**
99
124
  将 neo-skill 仓库克隆到你的项目中(例如 `vendor/neo-skill/`),然后:
100
125
  1. 设置 PYTHONPATH:`export PYTHONPATH=$PWD/vendor/neo-skill/src:$PYTHONPATH`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo-skill",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "A multi-assistant skill generator (Claude/Windsurf/Cursor/GitHub Skills) driven by a canonical SkillSpec.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -10,3 +10,56 @@
10
10
  ## 约定
11
11
 
12
12
  - 同步规则与生成行为应保持确定性(同输入得到同输出)。
13
+
14
+ ## Commands
15
+
16
+ ### `omni-skill init --ai <target>`
17
+ Initialize skills for target AI assistants.
18
+
19
+ **Usage:**
20
+ ```bash
21
+ omni-skill init --ai windsurf
22
+ omni-skill init --ai claude
23
+ omni-skill init --ai all
24
+ ```
25
+
26
+ **What it does:**
27
+ 1. Syncs skills/ and .shared/ from npm package to current directory
28
+ 2. Installs all skills (generates outputs for **specified AI targets only**)
29
+ 3. Writes VERSION files for each AI target
30
+ 4. Saves init state to .neo-skill.json
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
+
37
+ ### `omni-skill install <path>`
38
+ Install skill(s) from a local directory.
39
+
40
+ **Usage:**
41
+ ```bash
42
+ # Install a single skill
43
+ omni-skill install ./skills/my-new-skill
44
+
45
+ # Install all skills from a directory
46
+ omni-skill install ./skills
47
+ ```
48
+
49
+ **What it does:**
50
+ 1. Copies skill(s) to current directory's skills/ folder
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 update`
56
+ Update npm package and re-initialize skills.
57
+
58
+ **Usage:**
59
+ ```bash
60
+ omni-skill update
61
+ ```
62
+
63
+ **What it does:**
64
+ 1. Runs `npm install neo-skill@latest`
65
+ 2. Re-runs `omni-skill init` with saved AI targets
@@ -4,7 +4,7 @@ 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
10
 
@@ -185,13 +185,96 @@ def _generate_outputs_best_effort(pkg_root: Path, cwd: Path) -> None:
185
185
  print("\nGenerating skill outputs from skillspec.json ...")
186
186
  for spec_path in specs:
187
187
  try:
188
- ns = argparse.Namespace(repo_root=str(cwd), spec=str(spec_path))
188
+ ns = argparse.Namespace(repo_root=str(cwd), spec=str(spec_path), all=True)
189
189
  cmd_generate(ns)
190
190
  except SystemExit as e:
191
191
  rel = spec_path.relative_to(cwd) if spec_path.is_relative_to(cwd) else spec_path
192
192
  print(f" Skipping generator for {rel} (exit {e.code})")
193
193
 
194
194
 
195
+ def _install_skills_from_dir(skills_dir: Path, cwd: Path, selected_ais: Optional[List[str]] = None) -> int:
196
+ """
197
+ Install skills from a directory (either from npm package or local path).
198
+ Generates outputs for specified AI targets.
199
+
200
+ Args:
201
+ skills_dir: Directory containing skills
202
+ cwd: Current working directory
203
+ selected_ais: List of AI targets to generate for. If None, generates for all targets.
204
+ """
205
+ if not skills_dir.exists():
206
+ print(f"Skills directory not found: {skills_dir}")
207
+ return 1
208
+
209
+ specs = sorted(skills_dir.glob("*/skillspec.json"))
210
+ if not specs:
211
+ print(f"No skillspec.json found in: {skills_dir}")
212
+ return 1
213
+
214
+ # Determine target mapping: AI assistant -> skill-creator target
215
+ ai_to_target = {
216
+ "windsurf": "windsurf",
217
+ "claude": "claude",
218
+ "cursor": "cursor",
219
+ "antigravity": "github",
220
+ "copilot": "github",
221
+ "kiro": "github",
222
+ "codex": "github",
223
+ "qoder": "github",
224
+ "roocode": "github",
225
+ "gemini": "github",
226
+ "trae": "github",
227
+ "opencode": "github",
228
+ "continue": "github",
229
+ }
230
+
231
+ # Determine which targets to generate
232
+ if selected_ais is None:
233
+ # Generate all targets
234
+ generate_all_targets = True
235
+ targets_list = None
236
+ else:
237
+ # Map AI assistants to skill-creator targets
238
+ targets_set = set()
239
+ for ai in selected_ais:
240
+ target = ai_to_target.get(ai, "windsurf")
241
+ targets_set.add(target)
242
+ targets_list = sorted(targets_set)
243
+ generate_all_targets = False
244
+
245
+ print(f"\nInstalling {len(specs)} skill(s) from {skills_dir} ...")
246
+ for spec_path in specs:
247
+ skill_name = spec_path.parent.name
248
+ print(f" Installing skill: {skill_name}")
249
+
250
+ # Copy skill to cwd/skills if not already there
251
+ dest_skill_dir = cwd / "skills" / skill_name
252
+ if spec_path.parent.resolve() != dest_skill_dir.resolve():
253
+ dest_skill_dir.parent.mkdir(parents=True, exist_ok=True)
254
+ if dest_skill_dir.exists():
255
+ import shutil
256
+ shutil.rmtree(dest_skill_dir)
257
+ shutil.copytree(spec_path.parent, dest_skill_dir)
258
+ print(f" Copied to: {dest_skill_dir}")
259
+
260
+ # Generate outputs for specified targets
261
+ try:
262
+ if generate_all_targets:
263
+ ns = argparse.Namespace(repo_root=str(cwd), spec=str(cwd / "skills" / skill_name / "skillspec.json"), all=True)
264
+ cmd_generate(ns)
265
+ print(f" Generated outputs for all targets")
266
+ else:
267
+ # Generate for each target separately
268
+ for target in targets_list:
269
+ ns = argparse.Namespace(repo_root=str(cwd), spec=str(cwd / "skills" / skill_name / "skillspec.json"), all=False, target=target)
270
+ cmd_generate(ns)
271
+ print(f" Generated outputs for: {', '.join(targets_list)}")
272
+ except SystemExit as e:
273
+ print(f" Warning: Generator failed (exit {e.code})")
274
+
275
+ return 0
276
+
277
+
195
278
  def _handle_init(selected_ais: List[str], mode: str) -> int:
196
279
  pkg_root = _get_pkg_root()
197
280
  cwd = Path.cwd().resolve()
@@ -201,7 +284,12 @@ def _handle_init(selected_ais: List[str], mode: str) -> int:
201
284
  sync_pairs = _build_sync_pairs(effective_ais)
202
285
  print("Initializing skills in:", cwd)
203
286
  _perform_sync(pkg_root, cwd, sync_pairs)
204
- _generate_outputs_best_effort(pkg_root, cwd)
287
+
288
+ # Install all skills from npm package with specified AI targets
289
+ pkg_skills_dir = pkg_root / "skills"
290
+ if pkg_skills_dir.exists():
291
+ _install_skills_from_dir(pkg_skills_dir, cwd, selected_ais=selected_ais)
292
+
205
293
  _write_version_files(cwd, effective_ais, version)
206
294
  print("\nDone! neo-skill initialized.")
207
295
 
@@ -222,11 +310,59 @@ def _cmd_init(args: argparse.Namespace) -> int:
222
310
  return _handle_init(resolved["selected"], "init")
223
311
 
224
312
 
313
+ def _cmd_install(args: argparse.Namespace) -> int:
314
+ """
315
+ Install skill(s) from a local directory.
316
+ Usage: omni-skill install <path-to-skill-or-skills-dir>
317
+
318
+ Note: install command always generates outputs for all AI targets.
319
+ """
320
+ cwd = Path.cwd().resolve()
321
+ skill_path = Path(args.path).resolve()
322
+
323
+ if not skill_path.exists():
324
+ raise SystemExit(f"Path not found: {skill_path}")
325
+
326
+ # Check if it's a single skill directory (contains skillspec.json)
327
+ if (skill_path / "skillspec.json").exists():
328
+ # Single skill - generate for all targets
329
+ temp_skills_dir = skill_path.parent
330
+ return _install_skills_from_dir(temp_skills_dir, cwd, selected_ais=None)
331
+
332
+ # Check if it's a skills directory (contains subdirs with skillspec.json)
333
+ elif skill_path.is_dir():
334
+ return _install_skills_from_dir(skill_path, cwd, selected_ais=None)
335
+
336
+ else:
337
+ raise SystemExit(f"Invalid path: {skill_path}. Must be a skill directory or skills directory.")
338
+
339
+
225
340
  def _cmd_update(args: argparse.Namespace) -> int:
341
+ """
342
+ Update npm package and re-initialize skills.
343
+ """
344
+ import subprocess
345
+
226
346
  cwd = Path.cwd().resolve()
227
347
  state = _read_init_state(cwd)
228
348
  if not state["ok"]:
229
349
  raise SystemExit(state["error"])
350
+
351
+ print("Updating neo-skill npm package...")
352
+ result = subprocess.run(
353
+ ["npm", "install", "neo-skill@latest"],
354
+ cwd=cwd,
355
+ capture_output=True,
356
+ text=True
357
+ )
358
+
359
+ if result.returncode != 0:
360
+ print(f"Warning: npm install failed: {result.stderr}")
361
+ print("Continuing with re-initialization...")
362
+ else:
363
+ print("Package updated successfully.")
364
+
365
+ print("\nRe-initializing skills...")
230
366
  return _handle_init(state["selected"], "update")
231
367
 
232
368
 
@@ -253,7 +389,11 @@ def build_parser() -> argparse.ArgumentParser:
253
389
  p_init.add_argument("--ai", action="append", help="Target AI (can be repeated)")
254
390
  p_init.set_defaults(func=_cmd_init)
255
391
 
256
- p_update = sub.add_parser("update", help="Update skills based on previous init state")
392
+ p_install = sub.add_parser("install", help="Install skill(s) from a local directory")
393
+ p_install.add_argument("path", help="Path to skill directory or skills directory")
394
+ p_install.set_defaults(func=_cmd_install)
395
+
396
+ p_update = sub.add_parser("update", help="Update npm package and re-initialize skills")
257
397
  p_update.set_defaults(func=_cmd_update)
258
398
 
259
399
  return p