@nathan33/nao-skill 0.1.0 → 0.2.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 +8 -5
- package/bin/nao-skill.js +69 -1
- package/package.json +1 -1
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
|
-
##
|
|
142
|
+
## 安装与升级(npm 发布)
|
|
143
143
|
|
|
144
|
-
发布为 **`@nathan33/nao-skill
|
|
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
|
@@ -34,6 +34,11 @@ const PKG = JSON.parse(readFileSync(new URL('../package.json', import.meta.url),
|
|
|
34
34
|
const SRC = fileURLToPath(new URL('../.agents', import.meta.url));
|
|
35
35
|
const TEMPLATE_AGENTS = fileURLToPath(new URL('../.agents/templates/AGENTS.md.example', import.meta.url));
|
|
36
36
|
|
|
37
|
+
// 已知废弃路径(相对项目根):旧版遗留,update 时备份后移除
|
|
38
|
+
const OBSOLETE = ['.agents/skills/checklists'];
|
|
39
|
+
// 安装版本标记(install/update 写入,供下次检测升级)
|
|
40
|
+
const VERSION_FILE = '.agents/.nao-version';
|
|
41
|
+
|
|
37
42
|
const log = (...a) => console.log('[nao-skill]', ...a);
|
|
38
43
|
const warn = (...a) => console.error('[nao-skill]', ...a);
|
|
39
44
|
|
|
@@ -77,6 +82,16 @@ function install(target, force) {
|
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
|
|
85
|
+
// 版本标记 + 升级/废弃检测
|
|
86
|
+
const prev = readInstalledVersion(target);
|
|
87
|
+
writeFileSync(join(target, VERSION_FILE), PKG.version);
|
|
88
|
+
if (prev && prev !== PKG.version) {
|
|
89
|
+
warn(`检测到旧版安装(v${prev} → v${PKG.version}):建议运行 nao-skill update 升级机制文件。`);
|
|
90
|
+
}
|
|
91
|
+
for (const p of detectObsolete(target)) {
|
|
92
|
+
warn(`发现废弃路径 ${p}(旧版遗留,可能导致 skill 冲突):建议运行 nao-skill update 清理。`);
|
|
93
|
+
}
|
|
94
|
+
|
|
80
95
|
const agentsMd = join(target, 'AGENTS.md');
|
|
81
96
|
if (!existsSync(agentsMd)) {
|
|
82
97
|
let tpl = readFileSync(TEMPLATE_AGENTS, 'utf8');
|
|
@@ -95,11 +110,51 @@ function install(target, force) {
|
|
|
95
110
|
log(' 角色卡:.agents/prompts/ · 技能:.agents/skills/ · 交付清单:.agents/checklists/');
|
|
96
111
|
}
|
|
97
112
|
|
|
113
|
+
function update(target) {
|
|
114
|
+
// 源优先升级:机制文件以包为权威(项目定制应放 AGENTS.md,不在机制文件里改)
|
|
115
|
+
const dst = join(target, '.agents');
|
|
116
|
+
if (!existsSync(dst)) {
|
|
117
|
+
log('目标无 .agents/,直接完整安装。');
|
|
118
|
+
return install(target, false);
|
|
119
|
+
}
|
|
120
|
+
let overwritten = 0;
|
|
121
|
+
for (const e of readdirSync(SRC)) {
|
|
122
|
+
const s = join(SRC, e);
|
|
123
|
+
const d = join(dst, e);
|
|
124
|
+
rmSync(d, { recursive: true, force: true });
|
|
125
|
+
cpSync(s, d, { recursive: true });
|
|
126
|
+
overwritten++;
|
|
127
|
+
}
|
|
128
|
+
// 清理已知废弃路径(先备份到 .agents/.nao-obsolete/)
|
|
129
|
+
for (const p of OBSOLETE) {
|
|
130
|
+
const full = join(target, p);
|
|
131
|
+
if (existsSync(full)) {
|
|
132
|
+
const bakDir = join(dst, '.nao-obsolete');
|
|
133
|
+
mkdirSync(bakDir, { recursive: true });
|
|
134
|
+
cpSync(full, join(bakDir, `${basename(p)}-${Date.now()}`), { recursive: true });
|
|
135
|
+
rmSync(full, { recursive: true, force: true });
|
|
136
|
+
log(`废弃路径已备份并移除: ${p}(备份于 .agents/.nao-obsolete/)`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// 项目自定义文件保留(源没有的不动)
|
|
140
|
+
writeFileSync(join(target, VERSION_FILE), PKG.version);
|
|
141
|
+
log(`✔ 升级完成:机制文件以 v${PKG.version} 为准,项目自定义已保留。`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function readInstalledVersion(target) {
|
|
145
|
+
try { return readFileSync(join(target, VERSION_FILE), 'utf8').trim(); } catch { return null; }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function detectObsolete(target) {
|
|
149
|
+
return OBSOLETE.filter((p) => existsSync(join(target, p)));
|
|
150
|
+
}
|
|
151
|
+
|
|
98
152
|
function help() {
|
|
99
153
|
console.log(`nao-skill v${PKG.version} — nao 多角色 AI 开发舰队安装器
|
|
100
154
|
|
|
101
155
|
用法:
|
|
102
156
|
nao-skill install [dir] [--force] 安装/合并 .agents/ 到目标项目(默认当前目录)
|
|
157
|
+
nao-skill update [dir] 升级已有安装:源优先覆盖 + 清理废弃路径 + 保留项目自定义
|
|
103
158
|
nao-skill --version 显示版本
|
|
104
159
|
nao-skill --help 显示本帮助
|
|
105
160
|
|
|
@@ -131,11 +186,24 @@ if (cmd === '--version' || cmd === '-v' || cmd === 'version') {
|
|
|
131
186
|
else if (!rest[i].startsWith('-')) target = resolve(rest[i]);
|
|
132
187
|
else { warn(`未知参数: ${rest[i]}`); process.exit(2); }
|
|
133
188
|
}
|
|
134
|
-
if (!
|
|
189
|
+
if (!statIsDir(target)) {
|
|
135
190
|
warn(`目标目录无效: ${target}`);
|
|
136
191
|
process.exit(2);
|
|
137
192
|
}
|
|
138
193
|
install(target, force);
|
|
194
|
+
} else if (cmd === 'update' || cmd === 'u') {
|
|
195
|
+
let target = process.cwd();
|
|
196
|
+
const rest = args.slice(1);
|
|
197
|
+
for (let i = 0; i < rest.length; i++) {
|
|
198
|
+
if (rest[i] === '--target') target = resolve(rest[++i]);
|
|
199
|
+
else if (!rest[i].startsWith('-')) target = resolve(rest[i]);
|
|
200
|
+
else { warn(`未知参数: ${rest[i]}`); process.exit(2); }
|
|
201
|
+
}
|
|
202
|
+
if (!statIsDir(target)) {
|
|
203
|
+
warn(`目标目录无效: ${target}`);
|
|
204
|
+
process.exit(2);
|
|
205
|
+
}
|
|
206
|
+
update(target);
|
|
139
207
|
} else {
|
|
140
208
|
warn(`未知命令: ${cmd}(可用: install)`);
|
|
141
209
|
process.exit(2);
|