@nathan33/nao-skill 0.1.0 → 0.3.0

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
@@ -139,20 +139,23 @@ PM 是任务状态权威,以状态机驱动执行:
139
139
  .agents/scripts/ui-tokens-check.sh <repo> # 扫 UI 硬编码色值(绕过 Design Tokens)
140
140
  ```
141
141
 
142
- ## 安装(npm 发布)
142
+ ## 安装与升级(npm 发布)
143
143
 
144
- 发布为 **`@nathan33/nao-skill`**,提供 `nao-skill install` CLI 一键接入项目:
144
+ 发布为 **`@nathan33/nao-skill`**,CLI `nao-skill` 一键接入项目:
145
145
 
146
146
  ```bash
147
147
  npm install -g @nathan33/nao-skill # 全局安装 CLI
148
148
  # 或在任意项目里直接:npx @nathan33/nao-skill install
149
149
 
150
- nao-skill install # 安装到当前目录
151
- nao-skill install /path/to/proj --force # 指定目录;--force 覆盖同名文件
150
+ nao-skill install # 安装/合并到当前目录(保守,不覆盖已有)
151
+ nao-skill install /path/to/proj --force # 指定目录;--force 备份后覆盖同名
152
+ nao-skill update # 升级已有安装(源优先 + 清理废弃 + 留自定义)
152
153
  ```
153
154
 
155
+ - **install(保守合并)**:目标已有同名文件默认保留项目既有;`--force` 覆盖;写入 `.agents/.nao-version` 版本标记。
156
+ - **update(升级)**:机制文件源优先覆盖(项目定制应放 `AGENTS.md`)、备份清理已知废弃路径(如旧版 `skills/checklists/`,防 skill 冲突)到 `.agents/.nao-obsolete/`、保留项目自定义文件。
157
+ - **旧版检测**:install 发现版本落后或废弃路径 → 提示运行 `update`。
154
158
  - **安装内容**:`.agents/` 全套(角色卡 / 技能 / 交付清单 / 协议 / roles.yaml / 工具链 / 模板)+ 生成 `AGENTS.md`(已存在则提示按 §七 合并,不覆盖)。
155
- - **合并策略**:目标已有同名文件默认保留项目既有(避免覆盖自定义);`--force` 备份后覆盖。
156
159
  - **发布**:`npm publish --access public`(scope 包需 `--access public`)。
157
160
 
158
161
  ## 目录结构
package/bin/nao-skill.js CHANGED
@@ -17,6 +17,7 @@
17
17
  * bash .agents/scripts/nao-fleet.sh check / ensure arch rd-fe ...
18
18
  * 角色卡经 fleet 拉起时 --append-system-prompt 注入;checklists 按需读取
19
19
  */
20
+ import { execFileSync } from 'node:child_process';
20
21
  import {
21
22
  cpSync,
22
23
  existsSync,
@@ -34,6 +35,11 @@ const PKG = JSON.parse(readFileSync(new URL('../package.json', import.meta.url),
34
35
  const SRC = fileURLToPath(new URL('../.agents', import.meta.url));
35
36
  const TEMPLATE_AGENTS = fileURLToPath(new URL('../.agents/templates/AGENTS.md.example', import.meta.url));
36
37
 
38
+ // 已知废弃路径(相对项目根):旧版遗留,update 时备份后移除
39
+ const OBSOLETE = ['.agents/skills/checklists'];
40
+ // 安装版本标记(install/update 写入,供下次检测升级)
41
+ const VERSION_FILE = '.agents/.nao-version';
42
+
37
43
  const log = (...a) => console.log('[nao-skill]', ...a);
38
44
  const warn = (...a) => console.error('[nao-skill]', ...a);
39
45
 
@@ -59,6 +65,67 @@ function copyDir(src, dst, force) {
59
65
  return { copied, skipped, overwritten };
60
66
  }
61
67
 
68
+ // 已知 pi 插件(nao 舰队生态),name → npm 包
69
+ const PI_PLUGINS = {
70
+ intercom: { pkg: 'pi-intercom', desc: '多会话协作(舰队机制核心依赖)' },
71
+ 'ask-me': { pkg: 'pi-ask-me', desc: '提问/访谈工具' },
72
+ subagents: { pkg: 'pi-subagents', desc: '子代理委派' },
73
+ 'web-access': { pkg: 'pi-web-access', desc: '网页访问' },
74
+ codegraph: { pkg: '@sean_pedersen/pi-codegraph', desc: 'CodeGraph 代码定位(pi 集成)' },
75
+ };
76
+
77
+ function piSettingsPath() {
78
+ return join(process.env.HOME || process.env.USERPROFILE || '', '.pi', 'agent', 'settings.json');
79
+ }
80
+
81
+ function readPackages() {
82
+ try {
83
+ const d = JSON.parse(readFileSync(piSettingsPath(), 'utf8'));
84
+ return Array.isArray(d.packages) ? d.packages : [];
85
+ } catch {
86
+ return [];
87
+ }
88
+ }
89
+
90
+ function installedPlugins() {
91
+ const pkgs = readPackages();
92
+ const out = {};
93
+ for (const [name, { pkg }] of Object.entries(PI_PLUGINS)) {
94
+ out[name] = pkgs.includes(`npm:${pkg}`) || pkgs.includes(pkg);
95
+ }
96
+ return out;
97
+ }
98
+
99
+ function pluginsList() {
100
+ const installed = installedPlugins();
101
+ log(`pi 插件状态(settings: ${piSettingsPath()})`);
102
+ for (const [name, { pkg, desc }] of Object.entries(PI_PLUGINS)) {
103
+ console.log(` ${installed[name] ? '✓' : '·'} ${name.padEnd(11)} ${pkg.padEnd(26)} ${desc}${installed[name] ? '' : '(缺失)'}`);
104
+ }
105
+ console.log('\n安装: nao-skill plugins install <name...> | plugins install-all');
106
+ }
107
+
108
+ function pluginsInstall(names, all = false) {
109
+ const targets = all ? Object.keys(PI_PLUGINS) : names;
110
+ const todo = [];
111
+ for (const n of targets) {
112
+ const p = PI_PLUGINS[n];
113
+ if (!p) { warn(`未知插件: ${n}(可用: ${Object.keys(PI_PLUGINS).join(', ')})`); continue; }
114
+ if (installedPlugins()[n]) { log(`已安装: ${n}`); continue; }
115
+ todo.push([n, p]);
116
+ }
117
+ if (!todo.length) { log('全部插件已就绪。'); return; }
118
+ for (const [n, p] of todo) {
119
+ log(`安装 ${n} → pi install npm:${p.pkg} ...`);
120
+ try {
121
+ execFileSync('pi', ['install', `npm:${p.pkg}`], { stdio: 'inherit' });
122
+ log(`✓ ${n} 安装完成(重启 pi 会话后生效)`);
123
+ } catch (e) {
124
+ warn(`✗ ${n} 安装失败: ${e.message}(确认 pi 已安装且在 PATH)`);
125
+ }
126
+ }
127
+ }
128
+
62
129
  function install(target, force) {
63
130
  if (!existsSync(SRC)) {
64
131
  warn(`包内 .agents 缺失:${SRC}(发布物损坏?)`);
@@ -77,6 +144,16 @@ function install(target, force) {
77
144
  }
78
145
  }
79
146
 
147
+ // 版本标记 + 升级/废弃检测
148
+ const prev = readInstalledVersion(target);
149
+ writeFileSync(join(target, VERSION_FILE), PKG.version);
150
+ if (prev && prev !== PKG.version) {
151
+ warn(`检测到旧版安装(v${prev} → v${PKG.version}):建议运行 nao-skill update 升级机制文件。`);
152
+ }
153
+ for (const p of detectObsolete(target)) {
154
+ warn(`发现废弃路径 ${p}(旧版遗留,可能导致 skill 冲突):建议运行 nao-skill update 清理。`);
155
+ }
156
+
80
157
  const agentsMd = join(target, 'AGENTS.md');
81
158
  if (!existsSync(agentsMd)) {
82
159
  let tpl = readFileSync(TEMPLATE_AGENTS, 'utf8');
@@ -95,11 +172,55 @@ function install(target, force) {
95
172
  log(' 角色卡:.agents/prompts/ · 技能:.agents/skills/ · 交付清单:.agents/checklists/');
96
173
  }
97
174
 
175
+ function update(target) {
176
+ // 源优先升级:机制文件以包为权威(项目定制应放 AGENTS.md,不在机制文件里改)
177
+ const dst = join(target, '.agents');
178
+ if (!existsSync(dst)) {
179
+ log('目标无 .agents/,直接完整安装。');
180
+ return install(target, false);
181
+ }
182
+ let overwritten = 0;
183
+ for (const e of readdirSync(SRC)) {
184
+ const s = join(SRC, e);
185
+ const d = join(dst, e);
186
+ rmSync(d, { recursive: true, force: true });
187
+ cpSync(s, d, { recursive: true });
188
+ overwritten++;
189
+ }
190
+ // 清理已知废弃路径(先备份到 .agents/.nao-obsolete/)
191
+ for (const p of OBSOLETE) {
192
+ const full = join(target, p);
193
+ if (existsSync(full)) {
194
+ const bakDir = join(dst, '.nao-obsolete');
195
+ mkdirSync(bakDir, { recursive: true });
196
+ cpSync(full, join(bakDir, `${basename(p)}-${Date.now()}`), { recursive: true });
197
+ rmSync(full, { recursive: true, force: true });
198
+ log(`废弃路径已备份并移除: ${p}(备份于 .agents/.nao-obsolete/)`);
199
+ }
200
+ }
201
+ // 项目自定义文件保留(源没有的不动)
202
+ writeFileSync(join(target, VERSION_FILE), PKG.version);
203
+ log(`✔ 升级完成:机制文件以 v${PKG.version} 为准,项目自定义已保留。`);
204
+ }
205
+
206
+ function readInstalledVersion(target) {
207
+ try { return readFileSync(join(target, VERSION_FILE), 'utf8').trim(); } catch { return null; }
208
+ }
209
+
210
+ function detectObsolete(target) {
211
+ return OBSOLETE.filter((p) => existsSync(join(target, p)));
212
+ }
213
+
98
214
  function help() {
99
215
  console.log(`nao-skill v${PKG.version} — nao 多角色 AI 开发舰队安装器
100
216
 
101
217
  用法:
102
218
  nao-skill install [dir] [--force] 安装/合并 .agents/ 到目标项目(默认当前目录)
219
+ nao-skill install --plugins 项目接入时顺带安装 pi 舰队插件
220
+ nao-skill update [dir] 升级已有安装:源优先覆盖 + 清理废弃路径 + 保留项目自定义
221
+ nao-skill plugins list 列出已知 pi 插件与安装状态
222
+ nao-skill plugins install <名...> 安装指定插件(intercom/ask-me/subagents/web-access/codegraph)
223
+ nao-skill plugins install-all 安装全部舰队插件
103
224
  nao-skill --version 显示版本
104
225
  nao-skill --help 显示本帮助
105
226
 
@@ -124,18 +245,47 @@ if (cmd === '--version' || cmd === '-v' || cmd === 'version') {
124
245
  } else if (cmd === 'install' || cmd === 'i') {
125
246
  let target = process.cwd();
126
247
  let force = false;
248
+ let withPlugins = false;
127
249
  const rest = args.slice(1);
128
250
  for (let i = 0; i < rest.length; i++) {
129
251
  if (rest[i] === '--force' || rest[i] === '-f') force = true;
252
+ else if (rest[i] === '--plugins') withPlugins = true;
130
253
  else if (rest[i] === '--target') target = resolve(rest[++i]);
131
254
  else if (!rest[i].startsWith('-')) target = resolve(rest[i]);
132
255
  else { warn(`未知参数: ${rest[i]}`); process.exit(2); }
133
256
  }
134
- if (!existsSync(target) || !statIsDir(target)) {
257
+ if (!statIsDir(target)) {
135
258
  warn(`目标目录无效: ${target}`);
136
259
  process.exit(2);
137
260
  }
138
261
  install(target, force);
262
+ if (withPlugins) pluginsInstall([], true);
263
+ } else if (cmd === 'plugins' || cmd === 'plugin') {
264
+ const sub = args[1];
265
+ const rest = args.slice(2);
266
+ if (sub === 'list' || sub === 'ls' || !sub) {
267
+ pluginsList();
268
+ } else if (sub === 'install' || sub === 'add') {
269
+ pluginsInstall(rest);
270
+ } else if (sub === 'install-all' || sub === 'installAll') {
271
+ pluginsInstall([], true);
272
+ } else {
273
+ warn(`未知插件命令: ${sub}(可用: list / install / install-all)`);
274
+ process.exit(2);
275
+ }
276
+ } else if (cmd === 'update' || cmd === 'u') {
277
+ let target = process.cwd();
278
+ const rest = args.slice(1);
279
+ for (let i = 0; i < rest.length; i++) {
280
+ if (rest[i] === '--target') target = resolve(rest[++i]);
281
+ else if (!rest[i].startsWith('-')) target = resolve(rest[i]);
282
+ else { warn(`未知参数: ${rest[i]}`); process.exit(2); }
283
+ }
284
+ if (!statIsDir(target)) {
285
+ warn(`目标目录无效: ${target}`);
286
+ process.exit(2);
287
+ }
288
+ update(target);
139
289
  } else {
140
290
  warn(`未知命令: ${cmd}(可用: install)`);
141
291
  process.exit(2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathan33/nao-skill",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "nao 多角色 AI 开发舰队:角色卡 / 技能 / 协作协议 / 工具链,一键接入项目(pi / Claude / 任意 Agent)",
5
5
  "type": "module",
6
6
  "bin": {