@wowok/skills 3.0.4 → 3.1.1
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 +146 -122
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +223 -837
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/installer.d.ts +121 -0
- package/dist/installer.d.ts.map +1 -0
- package/dist/installer.js +802 -0
- package/dist/installer.js.map +1 -0
- package/dist/skills.d.ts +5 -2
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +86 -62
- package/dist/skills.js.map +1 -1
- package/dist/targets.d.ts +94 -0
- package/dist/targets.d.ts.map +1 -0
- package/dist/targets.js +421 -0
- package/dist/targets.js.map +1 -0
- package/dist/types.d.ts +5 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -32
- package/dist/types.js.map +1 -1
- package/package.json +5 -4
- package/scripts/install.js +21 -859
- package/wowok-arbitrator/SKILL.md +5 -12
- package/wowok-auditor/SKILL.md +5 -17
- package/wowok-collaborator/SKILL.md +5 -17
- package/wowok-governance/SKILL.md +10 -30
- package/wowok-machine/SKILL.md +5 -18
- package/wowok-market/SKILL.md +5 -21
- package/wowok-messenger/SKILL.md +32 -50
- package/wowok-onboard/SKILL.md +5 -22
- package/wowok-order/SKILL.md +6 -19
- package/wowok-output/SKILL.md +5 -10
- package/wowok-planner/SKILL.md +6 -20
- package/wowok-provider/SKILL.md +6 -18
- package/wowok-supplier/SKILL.md +5 -16
- package/examples/Insurance/Insurance.md +0 -1245
- package/examples/MyShop/MyShop.md +0 -2003
- package/examples/MyShop/myshop_machine_nodes.json +0 -93
- package/examples/MyShop_Advanced/MyShop_Advanced.md +0 -2880
- package/examples/ThreeBody_Signature/ThreeBody_Signature.md +0 -1831
- package/examples/Travel/Travel.md +0 -1849
- package/examples/Travel/calc-weather-timestamps.js +0 -12
package/scripts/install.js
CHANGED
|
@@ -1,871 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* npm lifecycle entry point.
|
|
3
3
|
*
|
|
4
|
-
* npm
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* preuninstall → remove SKILL.md folders from all installed client dirs
|
|
4
|
+
* Deliberately tiny: ALL logic lives in src/installer.ts so the npm hook and
|
|
5
|
+
* the `wowok-skills` CLI share one implementation (the two used to be
|
|
6
|
+
* duplicated and drifted apart).
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
* WOWOK_SKILLS_TARGETS Comma-separated client targets to override the default (all).
|
|
11
|
-
* Rarely needed — the default installs to every supported client.
|
|
12
|
-
* WOWOK_SKILLS_NO_MCP Set to "1" or "true" to skip MCP server management (default: auto-install MCP)
|
|
13
|
-
* WOWOK_REFERRER Airdrop referrer (address or name) to record during global install —
|
|
14
|
-
* saved once so the MCP auto-injects it on first on-chain interaction.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const fs = require('fs');
|
|
18
|
-
const path = require('path');
|
|
19
|
-
const os = require('os');
|
|
20
|
-
const { execSync } = require('child_process');
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Retained skills (post GLM5-31 sink refactor) — only these are installed.
|
|
24
|
-
* The 4 rule-reference skills (wowok-safety/tools/scenario/guard) were sunk
|
|
25
|
-
* into the MCP knowledge layer and are served by the MCP server directly:
|
|
26
|
-
* - wowok-safety → schema_query action='get_safety_rules'
|
|
27
|
-
* - wowok-tools → schema_query action='get_tool_reference'
|
|
28
|
-
* - wowok-scenario → industry_pack_operation recommend_industry / list_modes
|
|
29
|
-
* - wowok-guard → schema_query action='get_guard_design_patterns'
|
|
30
|
-
*/
|
|
31
|
-
const SKILL_DIRS = [
|
|
32
|
-
'wowok-provider',
|
|
33
|
-
'wowok-arbitrator',
|
|
34
|
-
'wowok-order',
|
|
35
|
-
'wowok-messenger',
|
|
36
|
-
'wowok-machine',
|
|
37
|
-
'wowok-output',
|
|
38
|
-
'wowok-onboard',
|
|
39
|
-
'wowok-planner',
|
|
40
|
-
'wowok-auditor',
|
|
41
|
-
'wowok-supplier',
|
|
42
|
-
'wowok-collaborator',
|
|
43
|
-
'wowok-market',
|
|
44
|
-
'wowok-governance',
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Deprecated skills removed in the GLM5-31 sink refactor. They are NEVER
|
|
49
|
-
* installed, but preuninstall/init still clean them up from legacy installs
|
|
50
|
-
* and warn the user where the content now lives.
|
|
51
|
-
*/
|
|
52
|
-
const LEGACY_SKILL_DIRS = [
|
|
53
|
-
'wowok-guard',
|
|
54
|
-
'wowok-tools',
|
|
55
|
-
'wowok-safety',
|
|
56
|
-
'wowok-scenario',
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
/** Where each deprecated skill's content now lives (migration message). */
|
|
60
|
-
const SKILL_MIGRATION_MAP = {
|
|
61
|
-
'wowok-safety': "MCP schema_query action='get_safety_rules'",
|
|
62
|
-
'wowok-tools': "MCP schema_query action='get_tool_reference'",
|
|
63
|
-
'wowok-scenario': "MCP industry_pack_operation action='recommend_industry' / 'list_modes'",
|
|
64
|
-
'wowok-guard': "MCP schema_query action='get_guard_design_patterns'",
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const CLIENT_DIRS = {
|
|
68
|
-
claude: path.join(os.homedir(), '.claude', 'skills'),
|
|
69
|
-
cursor: path.join(os.homedir(), '.cursor', 'rules'),
|
|
70
|
-
windsurf: path.join(os.homedir(), '.windsurf', 'skills'),
|
|
71
|
-
codebuddy: path.join(os.homedir(), '.codebuddy', 'skills'),
|
|
72
|
-
codex: path.join(os.homedir(), '.codex', 'skills'),
|
|
73
|
-
trae: path.join(os.homedir(), '.agents', 'skills'),
|
|
74
|
-
qoder: path.join(os.homedir(), '.qoder', 'skills'),
|
|
75
|
-
roo: path.join(os.homedir(), '.roo', 'skills'),
|
|
76
|
-
cline: path.join(os.homedir(), '.cline', 'skills'),
|
|
77
|
-
kilo: path.join(os.homedir(), '.kilo', 'skills'),
|
|
78
|
-
agents: path.join(os.homedir(), '.agents', 'skills'), // deprecated alias
|
|
79
|
-
copilot: path.join(os.homedir(), '.github', 'prompts'),
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const ALL_CLIENT_TARGETS = [
|
|
83
|
-
'claude', 'cursor', 'windsurf', 'codebuddy', 'codex', 'trae', 'qoder', 'roo', 'cline', 'kilo', 'copilot',
|
|
84
|
-
];
|
|
85
|
-
|
|
86
|
-
function getPackageRoot() {
|
|
87
|
-
return path.resolve(__dirname, '..');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Wow MCP data dir — mirrors @wowok/wowok getWowMcpDir(): dirname(wowDir)/mcp.
|
|
92
|
-
* Windows: %USERPROFILE%\.wow\mcp · macOS: ~/Library/Application Support/.wow/mcp
|
|
93
|
-
* Linux: ~/.config/.wow/mcp (or XDG fallbacks) · WOWOK_DATA_DIR override.
|
|
94
|
-
*/
|
|
95
|
-
function wowMcpDir() {
|
|
96
|
-
const home = os.homedir();
|
|
97
|
-
let wowDir;
|
|
98
|
-
if (process.env.WOWOK_DATA_DIR) {
|
|
99
|
-
wowDir = process.env.WOWOK_DATA_DIR;
|
|
100
|
-
} else if (process.platform === 'win32') {
|
|
101
|
-
wowDir = path.join(home, '.wow', 'V1');
|
|
102
|
-
} else if (process.platform === 'darwin') {
|
|
103
|
-
wowDir = path.join(home, 'Library', 'Application Support', '.wow', 'V1');
|
|
104
|
-
} else {
|
|
105
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME;
|
|
106
|
-
if (xdgConfig) {
|
|
107
|
-
wowDir = path.join(xdgConfig, '.wow', 'V1');
|
|
108
|
-
} else {
|
|
109
|
-
const xdgData = process.env.XDG_DATA_HOME;
|
|
110
|
-
wowDir = xdgData ? path.join(xdgData, '.wow', 'V1') : path.join(home, '.wow', 'V1');
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return path.join(path.dirname(wowDir), 'mcp');
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Persist the airdrop referrer so the MCP auto-injects it on every call.
|
|
118
|
-
* Also used by `wowok-skills referrer <addr>` and `init --referrer`.
|
|
119
|
-
*/
|
|
120
|
-
function saveReferrer(referrer) {
|
|
121
|
-
const dir = wowMcpDir();
|
|
122
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
123
|
-
fs.writeFileSync(path.join(dir, 'referrer'), String(referrer).trim() + '\n', 'utf-8');
|
|
124
|
-
console.log(`[wowok-skills] airdrop referrer saved: ${String(referrer).trim()}`);
|
|
125
|
-
console.log('[wowok-skills] it is auto-recorded on your first on-chain interaction.');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function copyDir(src, dest) {
|
|
129
|
-
if (!fs.existsSync(src)) return false;
|
|
130
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
131
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
132
|
-
for (const entry of entries) {
|
|
133
|
-
const srcPath = path.join(src, entry.name);
|
|
134
|
-
const destPath = path.join(dest, entry.name);
|
|
135
|
-
if (entry.isDirectory()) {
|
|
136
|
-
copyDir(srcPath, destPath);
|
|
137
|
-
} else {
|
|
138
|
-
fs.copyFileSync(srcPath, destPath);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function removeDir(dir) {
|
|
145
|
-
if (!fs.existsSync(dir)) return;
|
|
146
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
147
|
-
for (const entry of entries) {
|
|
148
|
-
const fullPath = path.join(dir, entry.name);
|
|
149
|
-
if (entry.isDirectory()) {
|
|
150
|
-
removeDir(fullPath);
|
|
151
|
-
} else {
|
|
152
|
-
fs.unlinkSync(fullPath);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
fs.rmdirSync(dir);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function getFileExt(target) {
|
|
159
|
-
const exts = { claude: '.md', codex: '.md', agents: '.md', codebuddy: '.md', cursor: '.mdc', copilot: '.prompt.md' };
|
|
160
|
-
return exts[target] || '.md';
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function parseFrontmatter(content) {
|
|
164
|
-
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
165
|
-
if (!match) return null;
|
|
166
|
-
const frontmatterStr = match[1];
|
|
167
|
-
const body = match[2];
|
|
168
|
-
const frontmatter = {};
|
|
169
|
-
let currentKey = null;
|
|
170
|
-
let currentValue = '';
|
|
171
|
-
for (const line of frontmatterStr.split('\n')) {
|
|
172
|
-
const kvMatch = line.match(/^(\w[\w-]*)\s*:\s*(.*)$/);
|
|
173
|
-
if (kvMatch) {
|
|
174
|
-
if (currentKey) {
|
|
175
|
-
frontmatter[currentKey] = currentValue.trim();
|
|
176
|
-
}
|
|
177
|
-
currentKey = kvMatch[1];
|
|
178
|
-
currentValue = kvMatch[2];
|
|
179
|
-
} else if (currentKey) {
|
|
180
|
-
currentValue += '\n' + line;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
if (currentKey) {
|
|
184
|
-
frontmatter[currentKey] = currentValue.trim();
|
|
185
|
-
}
|
|
186
|
-
return { frontmatter, body };
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function convertSkill(content, target, skillDir) {
|
|
190
|
-
if (target === 'cursor') {
|
|
191
|
-
const parsed = parseFrontmatter(content);
|
|
192
|
-
if (!parsed) return content;
|
|
193
|
-
const { frontmatter, body } = parsed;
|
|
194
|
-
let description = (frontmatter.description || frontmatter.name || skillDir);
|
|
195
|
-
if (typeof description === 'string') {
|
|
196
|
-
description = description.replace(/\n/g, ' ');
|
|
197
|
-
}
|
|
198
|
-
const isAlways = frontmatter.loading === 'always' || frontmatter.always === true;
|
|
199
|
-
const alwaysApply = isAlways ? 'true' : 'false';
|
|
200
|
-
const newFrontmatter = [
|
|
201
|
-
'---',
|
|
202
|
-
`description: "${description}"`,
|
|
203
|
-
`alwaysApply: ${alwaysApply}`,
|
|
204
|
-
'---',
|
|
205
|
-
].join('\n');
|
|
206
|
-
return newFrontmatter + '\n' + body;
|
|
207
|
-
}
|
|
208
|
-
if (target === 'copilot') {
|
|
209
|
-
const parsed = parseFrontmatter(content);
|
|
210
|
-
if (!parsed) return content;
|
|
211
|
-
return parsed.body;
|
|
212
|
-
}
|
|
213
|
-
return content;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function installSkills(targetDir, target) {
|
|
217
|
-
const pkgRoot = getPackageRoot();
|
|
218
|
-
const ext = getFileExt(target);
|
|
219
|
-
let count = 0;
|
|
220
|
-
|
|
221
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
222
|
-
|
|
223
|
-
for (const dir of SKILL_DIRS) {
|
|
224
|
-
const src = path.join(pkgRoot, dir, 'SKILL.md');
|
|
225
|
-
|
|
226
|
-
if (!fs.existsSync(src)) {
|
|
227
|
-
console.warn(`[wowok-skills] WARN: SKILL.md not found for ${dir}`);
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const content = fs.readFileSync(src, 'utf-8');
|
|
232
|
-
const converted = convertSkill(content, target, dir);
|
|
233
|
-
const basename = (target === 'cursor' || target === 'copilot')
|
|
234
|
-
? `wowok-${dir.replace('wowok-', '')}${ext}`
|
|
235
|
-
: 'SKILL.md';
|
|
236
|
-
const destDir = path.join(targetDir, dir);
|
|
237
|
-
const dest = path.join(destDir, basename);
|
|
238
|
-
|
|
239
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
240
|
-
fs.writeFileSync(dest, converted, 'utf-8');
|
|
241
|
-
count++;
|
|
242
|
-
console.log(`[wowok-skills] installed: ${dir} → ${dest}`);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return count;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function uninstallSkills(targetDir) {
|
|
249
|
-
let count = 0;
|
|
250
|
-
|
|
251
|
-
// Remove both retained and legacy (deprecated) skills — legacy dirs may
|
|
252
|
-
// still exist from pre-refactor installs and must be cleaned up.
|
|
253
|
-
for (const dir of [...SKILL_DIRS, ...LEGACY_SKILL_DIRS]) {
|
|
254
|
-
const dirPath = path.join(targetDir, dir);
|
|
255
|
-
if (fs.existsSync(dirPath)) {
|
|
256
|
-
removeDir(dirPath);
|
|
257
|
-
count++;
|
|
258
|
-
console.log(`[wowok-skills] removed: ${dirPath}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return count;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Detect deprecated skills still present in a target dir and print a
|
|
267
|
-
* migration hint (GLM5-31 §3.2). Returns the list of legacy dirs found.
|
|
268
|
-
*/
|
|
269
|
-
function warnLegacySkills(targetDir) {
|
|
270
|
-
const found = LEGACY_SKILL_DIRS.filter((dir) => fs.existsSync(path.join(targetDir, dir)));
|
|
271
|
-
if (found.length > 0) {
|
|
272
|
-
console.warn(`[wowok-skills] ⚠ MIGRATION: deprecated skills detected in ${targetDir}:`);
|
|
273
|
-
for (const dir of found) {
|
|
274
|
-
console.warn(`[wowok-skills] - ${dir} → content now served by ${SKILL_MIGRATION_MAP[dir]}`);
|
|
275
|
-
}
|
|
276
|
-
console.warn('[wowok-skills] These skills are stale (MCP knowledge layer is the source of truth).');
|
|
277
|
-
console.warn('[wowok-skills] Remove them with: wowok-skills uninit (or delete the folders above)');
|
|
278
|
-
}
|
|
279
|
-
return found;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function getTargets() {
|
|
283
|
-
const envTargets = process.env.WOWOK_SKILLS_TARGETS;
|
|
284
|
-
if (!envTargets) {
|
|
285
|
-
return [...ALL_CLIENT_TARGETS];
|
|
286
|
-
}
|
|
287
|
-
return envTargets.split(',').map(t => t.trim()).filter(t => CLIENT_DIRS[t]);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function main() {
|
|
291
|
-
const event = process.env.npm_lifecycle_event || '';
|
|
292
|
-
const skipMcp = process.env.WOWOK_SKILLS_NO_MCP === '1' || process.env.WOWOK_SKILLS_NO_MCP === 'true';
|
|
293
|
-
|
|
294
|
-
if (event === 'postinstall') {
|
|
295
|
-
const targets = getTargets();
|
|
296
|
-
console.log(`[wowok-skills] Installing skills to ${targets.length} client(s)...`);
|
|
297
|
-
|
|
298
|
-
let total = 0;
|
|
299
|
-
for (const target of targets) {
|
|
300
|
-
const dir = CLIENT_DIRS[target];
|
|
301
|
-
console.log(`[wowok-skills] → ${dir}`);
|
|
302
|
-
const count = installSkills(dir, target);
|
|
303
|
-
total += count;
|
|
304
|
-
// GLM5-31 §3.2: warn about stale pre-refactor skills still present.
|
|
305
|
-
warnLegacySkills(dir);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
console.log(`[wowok-skills] Done — ${total} skills installed across ${targets.length} client(s).`);
|
|
309
|
-
|
|
310
|
-
// ─── MCP Server Management ───────────────────────────────────────
|
|
311
|
-
console.log('');
|
|
312
|
-
|
|
313
|
-
if (skipMcp) {
|
|
314
|
-
console.log('[wowok-skills] WOWOK_SKILLS_NO_MCP=1 — skipping MCP server management.');
|
|
315
|
-
} else {
|
|
316
|
-
console.log('[wowok-skills] Checking MCP server...');
|
|
317
|
-
|
|
318
|
-
// Step 1: Install or upgrade the MCP server
|
|
319
|
-
const mcpChanged = ensureMcpServer();
|
|
320
|
-
|
|
321
|
-
// Step 2: Write MCP config for each target client
|
|
322
|
-
console.log('[wowok-skills] Registering MCP server in client config...');
|
|
323
|
-
for (const target of targets) {
|
|
324
|
-
writeMcpConfig(target);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Step 3: Restart MCP server so the new version takes effect
|
|
328
|
-
if (mcpChanged) {
|
|
329
|
-
console.log('[wowok-skills] Restarting MCP server (so IDE picks up the new version)...');
|
|
330
|
-
restartMcpServer();
|
|
331
|
-
} else {
|
|
332
|
-
console.log('[wowok-skills] MCP server unchanged — no restart needed.');
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
console.log('[wowok-skills] MCP server setup complete.');
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// ─── Airdrop referrer from WOWOK_REFERRER (single-command global install) ──
|
|
339
|
-
const referrer = process.env.WOWOK_REFERRER;
|
|
340
|
-
if (referrer && referrer.trim()) {
|
|
341
|
-
console.log('');
|
|
342
|
-
saveReferrer(referrer);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// ─── Project-install reminder ──────────────────────────────────────────
|
|
346
|
-
console.log('');
|
|
347
|
-
console.log('[wowok-skills] Skills installed GLOBALLY + MCP server registered (all clients).');
|
|
348
|
-
console.log('[wowok-skills] To ALSO install skills into a project, cd into it and run `wowok-skills init`.');
|
|
349
|
-
} else if (event === 'preuninstall') {
|
|
350
|
-
// Use the same target list as postinstall (ALL_CLIENT_TARGETS) so the
|
|
351
|
-
// set of dirs we remove from is symmetric with the set we installed to.
|
|
352
|
-
// Object.keys(CLIENT_DIRS) would additionally include the 'agents' alias
|
|
353
|
-
// which is identical to 'trae' → redundant.
|
|
354
|
-
const targets = [...ALL_CLIENT_TARGETS];
|
|
355
|
-
console.log('[wowok-skills] Removing skills from all client dirs...');
|
|
356
|
-
|
|
357
|
-
let total = 0;
|
|
358
|
-
for (const target of targets) {
|
|
359
|
-
const dir = CLIENT_DIRS[target];
|
|
360
|
-
if (countExisting(dir) > 0) {
|
|
361
|
-
console.log(`[wowok-skills] → ${dir}`);
|
|
362
|
-
total += uninstallSkills(dir);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
console.log(`[wowok-skills] Done — ${total} skills removed.`);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
function countExisting(targetDir) {
|
|
371
|
-
let count = 0;
|
|
372
|
-
for (const dir of [...SKILL_DIRS, ...LEGACY_SKILL_DIRS]) {
|
|
373
|
-
if (fs.existsSync(path.join(targetDir, dir))) {
|
|
374
|
-
count++;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return count;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// =========================================================================
|
|
381
|
-
// MCP Server Management — auto-install, upgrade, config, and restart
|
|
382
|
-
// =========================================================================
|
|
383
|
-
|
|
384
|
-
/** MCP server npm package name */
|
|
385
|
-
const MCP_PACKAGE = '@wowok/agent-mcp';
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Resolve CodeBuddy MCP config file according to its official priority rules.
|
|
389
|
-
* Reference: https://www.codebuddy.ai/docs/cli/mcp#configuration-file-locations
|
|
8
|
+
* postinstall → copy SKILL.md to every client + register the MCP server
|
|
390
9
|
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
* 2. ~/.codebuddy/mcp.json (deprecated)
|
|
394
|
-
* 3. ~/.codebuddy.json (legacy)
|
|
10
|
+
* There is NO uninstall hook: npm v7+ never runs `preuninstall`/`uninstall`
|
|
11
|
+
* scripts, so removal is an explicit command — `wowok-skills uninit`.
|
|
395
12
|
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
|
|
400
|
-
function resolveCodeBuddyMcpFile() {
|
|
401
|
-
const home = os.homedir();
|
|
402
|
-
const candidates = [
|
|
403
|
-
path.join(home, '.codebuddy', '.mcp.json'), // 1. recommended
|
|
404
|
-
path.join(home, '.codebuddy', 'mcp.json'), // 2. deprecated
|
|
405
|
-
path.join(home, '.codebuddy.json'), // 3. legacy
|
|
406
|
-
];
|
|
407
|
-
const existing = candidates.find((p) => fs.existsSync(p));
|
|
408
|
-
return existing || candidates[0];
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Resolve VS Code globalStorage MCP settings files for a given extension ID.
|
|
413
|
-
*
|
|
414
|
-
* VS Code extensions store their settings under:
|
|
415
|
-
* <user-data>/User/globalStorage/<extensionId>/settings/<fileName>
|
|
416
|
-
*
|
|
417
|
-
* Where <user-data> depends on the platform and the installed VS Code
|
|
418
|
-
* *variant* (Code, Code - Insiders, VSCodium, Cursor). Only variants whose
|
|
419
|
-
* User directory actually exists on disk are returned.
|
|
420
|
-
*
|
|
421
|
-
* macOS: ~/Library/Application Support/{Code,Code - Insiders,VSCodium,Cursor}
|
|
422
|
-
* Windows: %APPDATA%\{Code,Code - Insiders,VSCodium,Cursor}
|
|
423
|
-
* Linux: ~/.config/{Code,Code - Insiders,VSCodium,Cursor}
|
|
13
|
+
* Environment variables:
|
|
14
|
+
* WOWOK_SKILLS_TARGETS comma-separated target ids (default: all)
|
|
15
|
+
* WOWOK_SKILLS_NO_MCP "1" to skip MCP install/registration
|
|
16
|
+
* WOWOK_REFERRER airdrop referrer (address or name) to persist
|
|
424
17
|
*/
|
|
425
|
-
function resolveVscodeGlobalStorageFiles(extensionId, fileName) {
|
|
426
|
-
const home = os.homedir();
|
|
427
|
-
const vsBaseDirs = (() => {
|
|
428
|
-
switch (process.platform) {
|
|
429
|
-
case 'darwin':
|
|
430
|
-
return [path.join(home, 'Library', 'Application Support')];
|
|
431
|
-
case 'win32':
|
|
432
|
-
return [process.env.APPDATA || path.join(home, 'AppData', 'Roaming')];
|
|
433
|
-
default:
|
|
434
|
-
return [process.env.XDG_CONFIG_HOME || path.join(home, '.config')];
|
|
435
|
-
}
|
|
436
|
-
})();
|
|
437
18
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
for (const base of vsBaseDirs) {
|
|
441
|
-
for (const variant of vsVariants) {
|
|
442
|
-
const vsUserDir = path.join(base, variant, 'User');
|
|
443
|
-
if (fs.existsSync(vsUserDir)) {
|
|
444
|
-
results.push(path.join(
|
|
445
|
-
vsUserDir,
|
|
446
|
-
'globalStorage',
|
|
447
|
-
extensionId,
|
|
448
|
-
'settings',
|
|
449
|
-
fileName,
|
|
450
|
-
));
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
return results;
|
|
455
|
-
}
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const fs = require('fs');
|
|
456
21
|
|
|
457
|
-
|
|
458
|
-
* Resolve ALL Roo Code MCP config files that should receive the wowok entry.
|
|
459
|
-
* Roo Code ships in two forms and we should cover BOTH:
|
|
460
|
-
*
|
|
461
|
-
* (A) Standalone CLI config → ~/.roo/mcp_settings.json
|
|
462
|
-
* Reference: https://mcp.harishgarg.com/use/apple-script/mcp-server/with/43
|
|
463
|
-
*
|
|
464
|
-
* (B) VS Code extension (rooveterinaryinc.roo-cline) config. The extension
|
|
465
|
-
* stores MCP settings inside VS Code's globalStorage under its
|
|
466
|
-
* extension ID.
|
|
467
|
-
* References:
|
|
468
|
-
* - Path convention : https://www.skillmd.ai/skills/mcp-installer-1/
|
|
469
|
-
* - macOS/Windows : https://crosstrade.io/learn/connect-roo-code-to-ninjatrader-8-mcp
|
|
470
|
-
*/
|
|
471
|
-
function resolveRooMcpFiles() {
|
|
472
|
-
const results = [];
|
|
473
|
-
// (A) Standalone CLI config — always include this target
|
|
474
|
-
results.push(path.join(os.homedir(), '.roo', 'mcp_settings.json'));
|
|
475
|
-
// (B) VS Code extension globalStorage entries
|
|
476
|
-
results.push(...resolveVscodeGlobalStorageFiles(
|
|
477
|
-
'rooveterinaryinc.roo-cline',
|
|
478
|
-
'cline_mcp_settings.json',
|
|
479
|
-
));
|
|
480
|
-
return results;
|
|
481
|
-
}
|
|
22
|
+
const dist = path.join(__dirname, '..', 'dist', 'installer.js');
|
|
482
23
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
* Reference: https://www.skillmd.ai/skills/mcp-installer-1/
|
|
488
|
-
*/
|
|
489
|
-
function resolveClineMcpFiles() {
|
|
490
|
-
return resolveVscodeGlobalStorageFiles(
|
|
491
|
-
'saoudrizwan.claude-dev',
|
|
492
|
-
'cline_mcp_settings.json',
|
|
24
|
+
if (!fs.existsSync(dist)) {
|
|
25
|
+
console.warn(
|
|
26
|
+
'[wowok-skills] dist/installer.js is missing — skills were NOT installed.\n' +
|
|
27
|
+
'[wowok-skills] Build the package first (npm run build) or reinstall from the npm registry.',
|
|
493
28
|
);
|
|
29
|
+
process.exit(0);
|
|
494
30
|
}
|
|
495
31
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
* Kilo Code reads MCP settings from two separate places (documented by Kilo):
|
|
499
|
-
* (A) VS Code extension → globalStorage/kilocode.kilo-code/settings/mcp_settings.json
|
|
500
|
-
* (B) CLI → ~/.kilocode/cli/global/settings/mcp_settings.json
|
|
501
|
-
* References:
|
|
502
|
-
* - https://kilo.ai/docs/features/mcp/using-mcp-in-cli
|
|
503
|
-
* - https://github.com/Kilo-Org/kilocode/blob/main/docs/file-locations.md
|
|
504
|
-
*/
|
|
505
|
-
function resolveKiloMcpFiles() {
|
|
506
|
-
const results = [];
|
|
507
|
-
results.push(...resolveVscodeGlobalStorageFiles(
|
|
508
|
-
'kilocode.kilo-code',
|
|
509
|
-
'mcp_settings.json',
|
|
510
|
-
));
|
|
511
|
-
results.push(path.join(os.homedir(), '.kilocode', 'cli', 'global', 'settings', 'mcp_settings.json'));
|
|
512
|
-
return results;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* MCP config entries per client target.
|
|
517
|
-
* null means the client does not support MCP or manages it internally.
|
|
518
|
-
* Each entry may either provide a static `configPath` string, or a
|
|
519
|
-
* `resolveConfigPaths()` function that returns an array of concrete paths
|
|
520
|
-
* (for clients like CodeBuddy with priority-resolution rules, or Roo with
|
|
521
|
-
* multiple simultaneous install targets).
|
|
522
|
-
*/
|
|
523
|
-
const MCP_TARGET_CONFIGS = {
|
|
524
|
-
claude: {
|
|
525
|
-
configPath: path.join(os.homedir(), '.claude', 'settings.json'),
|
|
526
|
-
merge: (config) => {
|
|
527
|
-
config.mcpServers = config.mcpServers || {};
|
|
528
|
-
config.mcpServers.wowok = { command: 'npx', args: ['-y', '@wowok/agent-mcp'] };
|
|
529
|
-
return config;
|
|
530
|
-
}
|
|
531
|
-
},
|
|
532
|
-
codebuddy: {
|
|
533
|
-
resolveConfigPaths: () => [resolveCodeBuddyMcpFile()],
|
|
534
|
-
merge: (config) => {
|
|
535
|
-
config.mcpServers = config.mcpServers || {};
|
|
536
|
-
config.mcpServers.wowok = { command: 'npx', args: ['-y', '@wowok/agent-mcp'] };
|
|
537
|
-
return config;
|
|
538
|
-
}
|
|
539
|
-
},
|
|
540
|
-
windsurf: {
|
|
541
|
-
configPath: path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
|
|
542
|
-
merge: (config) => {
|
|
543
|
-
config.mcpServers = config.mcpServers || {};
|
|
544
|
-
config.mcpServers.wowok = { command: 'npx', args: ['-y', '@wowok/agent-mcp'] };
|
|
545
|
-
return config;
|
|
546
|
-
}
|
|
547
|
-
},
|
|
548
|
-
qoder: {
|
|
549
|
-
configPath: (() => {
|
|
550
|
-
// Qoder MCP config paths — confirmed by community guides:
|
|
551
|
-
// Windows : %APPDATA%\Qoder\mcp-settings.json
|
|
552
|
-
// macOS : ~/Library/Application Support/Qoder/mcp-settings.json
|
|
553
|
-
// Linux : ~/.config/qoder/mcp-settings.json
|
|
554
|
-
// Reference: github.com/oscar-wang-xin/mcp-session-saver/docs/MULTI_IDE_SETUP.md
|
|
555
|
-
if (process.platform === 'win32') {
|
|
556
|
-
return path.join(
|
|
557
|
-
process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'),
|
|
558
|
-
'Qoder', 'mcp-settings.json',
|
|
559
|
-
);
|
|
560
|
-
}
|
|
561
|
-
if (process.platform === 'darwin') {
|
|
562
|
-
return path.join(os.homedir(), 'Library', 'Application Support', 'Qoder', 'mcp-settings.json');
|
|
563
|
-
}
|
|
564
|
-
return path.join(
|
|
565
|
-
process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'),
|
|
566
|
-
'qoder', 'mcp-settings.json',
|
|
567
|
-
);
|
|
568
|
-
})(),
|
|
569
|
-
merge: (config) => {
|
|
570
|
-
config.mcpServers = config.mcpServers || {};
|
|
571
|
-
config.mcpServers.wowok = { command: 'npx', args: ['-y', '@wowok/agent-mcp'] };
|
|
572
|
-
return config;
|
|
573
|
-
}
|
|
574
|
-
},
|
|
575
|
-
roo: {
|
|
576
|
-
resolveConfigPaths: resolveRooMcpFiles,
|
|
577
|
-
merge: (config) => {
|
|
578
|
-
config.mcpServers = config.mcpServers || {};
|
|
579
|
-
config.mcpServers.wowok = { command: 'npx', args: ['-y', '@wowok/agent-mcp'] };
|
|
580
|
-
return config;
|
|
581
|
-
}
|
|
582
|
-
},
|
|
583
|
-
cline: {
|
|
584
|
-
resolveConfigPaths: resolveClineMcpFiles,
|
|
585
|
-
merge: (config) => {
|
|
586
|
-
config.mcpServers = config.mcpServers || {};
|
|
587
|
-
config.mcpServers.wowok = { command: 'npx', args: ['-y', '@wowok/agent-mcp'] };
|
|
588
|
-
return config;
|
|
589
|
-
}
|
|
590
|
-
},
|
|
591
|
-
kilo: {
|
|
592
|
-
resolveConfigPaths: resolveKiloMcpFiles,
|
|
593
|
-
merge: (config) => {
|
|
594
|
-
config.mcpServers = config.mcpServers || {};
|
|
595
|
-
config.mcpServers.wowok = { command: 'npx', args: ['-y', '@wowok/agent-mcp'] };
|
|
596
|
-
return config;
|
|
597
|
-
}
|
|
598
|
-
},
|
|
599
|
-
// trae / agents: IDE-managed MCP via ~/.trae-cn/mcps/ directory structure — skip global config
|
|
600
|
-
trae: null,
|
|
601
|
-
agents: null,
|
|
602
|
-
// cursor: MCP config is project-level, handled by `wowok-skills init` in project dir
|
|
603
|
-
cursor: null,
|
|
604
|
-
// copilot: user-level MCP config (Copilot CLI)
|
|
605
|
-
copilot: {
|
|
606
|
-
configPath: path.join(os.homedir(), '.copilot', 'mcp-config.json'),
|
|
607
|
-
merge: (config) => {
|
|
608
|
-
config.mcpServers = config.mcpServers || {};
|
|
609
|
-
config.mcpServers.wowok = { type: 'local', command: 'npx', args: ['-y', '@wowok/agent-mcp'] };
|
|
610
|
-
return config;
|
|
611
|
-
}
|
|
612
|
-
},
|
|
613
|
-
// codex: global TOML config (~/.codex/config.toml)
|
|
614
|
-
codex: {
|
|
615
|
-
configPath: path.join(os.homedir(), '.codex', 'config.toml'),
|
|
616
|
-
format: 'toml',
|
|
617
|
-
},
|
|
618
|
-
};
|
|
619
|
-
|
|
620
|
-
/**
|
|
621
|
-
* Parse a semver string to its major version number.
|
|
622
|
-
* Returns 0 for invalid/missing versions.
|
|
623
|
-
*/
|
|
624
|
-
function semverMajor(version) {
|
|
625
|
-
if (!version) return 0;
|
|
626
|
-
const parts = version.split('.');
|
|
627
|
-
return parseInt(parts[0], 10) || 0;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
/**
|
|
631
|
-
* Get the currently installed global version of the MCP server.
|
|
632
|
-
* Uses `npm ls -g <package>` (targeted, no tree scan) for speed.
|
|
633
|
-
* Returns null if not installed.
|
|
634
|
-
*/
|
|
635
|
-
function getInstalledMcpVersion() {
|
|
636
|
-
try {
|
|
637
|
-
// npm ls -g <pkg> is faster than npm list -g because it skips
|
|
638
|
-
// scanning unrelated packages in the global node_modules.
|
|
639
|
-
const output = execSync(`npm ls -g ${MCP_PACKAGE} --depth=0 --json`, {
|
|
640
|
-
encoding: 'utf-8',
|
|
641
|
-
stdio: 'pipe',
|
|
642
|
-
timeout: 15000,
|
|
643
|
-
});
|
|
644
|
-
const parsed = JSON.parse(output);
|
|
645
|
-
// npm ls output has the package as a top-level key, not under dependencies
|
|
646
|
-
const pkg = parsed.dependencies?.[MCP_PACKAGE] || parsed[MCP_PACKAGE];
|
|
647
|
-
return pkg?.version || null;
|
|
648
|
-
} catch {
|
|
649
|
-
return null;
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
/**
|
|
654
|
-
* Get the latest available version from the npm registry.
|
|
655
|
-
* Returns null on failure (offline, registry unreachable, etc.).
|
|
656
|
-
*/
|
|
657
|
-
function getLatestMcpVersion() {
|
|
658
|
-
try {
|
|
659
|
-
// --prefer-online forces npm to bypass local cache and hit the registry
|
|
660
|
-
// directly. Without this flag, stale cache can return outdated version
|
|
661
|
-
// numbers (see project memory: components.mjs fix).
|
|
662
|
-
const output = execSync(`npm view --prefer-online ${MCP_PACKAGE} version`, {
|
|
663
|
-
encoding: 'utf-8',
|
|
664
|
-
stdio: 'pipe',
|
|
665
|
-
timeout: 15000,
|
|
666
|
-
});
|
|
667
|
-
return output.trim();
|
|
668
|
-
} catch {
|
|
669
|
-
return null;
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
/**
|
|
674
|
-
* Install an npm package globally with retry logic.
|
|
675
|
-
* Retries up to 2 times on failure (covers transient network errors).
|
|
676
|
-
*/
|
|
677
|
-
function npmInstallGlobal(pkg, retries = 2) {
|
|
678
|
-
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
679
|
-
try {
|
|
680
|
-
execSync(`npm install -g ${pkg}`, { stdio: 'inherit', timeout: 120000 });
|
|
681
|
-
return true;
|
|
682
|
-
} catch (err) {
|
|
683
|
-
if (attempt < retries) {
|
|
684
|
-
console.log(`[wowok-skills] npm install attempt ${attempt} failed, retrying...`);
|
|
685
|
-
// Wait 2s before retry to let network recover
|
|
686
|
-
execSync('ping -n 3 127.0.0.1 >nul', { stdio: 'pipe', timeout: 5000 });
|
|
687
|
-
} else {
|
|
688
|
-
console.error(`[wowok-skills] npm install failed after ${retries} attempts: ${err.message}`);
|
|
689
|
-
return false;
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
return false;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
/**
|
|
697
|
-
* Ensure the MCP server is installed and up to date.
|
|
698
|
-
*
|
|
699
|
-
* - Not installed → install latest
|
|
700
|
-
* - Outdated (same major) → upgrade to latest
|
|
701
|
-
* - Major version bump → warn user, skip auto-upgrade
|
|
702
|
-
* - Up to date → skip
|
|
703
|
-
*
|
|
704
|
-
* Returns true if the package was installed or upgraded (changed).
|
|
705
|
-
*/
|
|
706
|
-
function ensureMcpServer() {
|
|
707
|
-
const currentVersion = getInstalledMcpVersion();
|
|
708
|
-
const latestVersion = getLatestMcpVersion();
|
|
709
|
-
|
|
710
|
-
if (currentVersion) {
|
|
711
|
-
console.log(`[wowok-skills] MCP server ${MCP_PACKAGE} v${currentVersion} found.`);
|
|
712
|
-
|
|
713
|
-
if (!latestVersion) {
|
|
714
|
-
console.log(`[wowok-skills] Cannot check latest version (offline or registry unreachable). Keeping v${currentVersion}.`);
|
|
715
|
-
return false;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
if (currentVersion === latestVersion) {
|
|
719
|
-
console.log(`[wowok-skills] MCP server is up to date (v${currentVersion}).`);
|
|
720
|
-
return false;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
// Major version guard: warn if the latest version has a different major
|
|
724
|
-
const currentMajor = semverMajor(currentVersion);
|
|
725
|
-
const latestMajor = semverMajor(latestVersion);
|
|
726
|
-
|
|
727
|
-
if (latestMajor > currentMajor) {
|
|
728
|
-
console.warn(`[wowok-skills] ⚠ WARNING: Major version bump detected v${currentVersion} → v${latestVersion}.`);
|
|
729
|
-
console.warn(`[wowok-skills] Skipping auto-upgrade to avoid breaking changes.`);
|
|
730
|
-
console.warn(`[wowok-skills] To upgrade manually: npm install -g ${MCP_PACKAGE}@latest`);
|
|
731
|
-
return false;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
console.log(`[wowok-skills] Upgrading MCP server v${currentVersion} → v${latestVersion}...`);
|
|
735
|
-
if (npmInstallGlobal(`${MCP_PACKAGE}@latest`)) {
|
|
736
|
-
console.log(`[wowok-skills] MCP server upgraded to v${latestVersion}.`);
|
|
737
|
-
return true;
|
|
738
|
-
}
|
|
739
|
-
console.error(`[wowok-skills] MCP server upgrade FAILED. Keeping v${currentVersion}.`);
|
|
740
|
-
return false;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
console.log(`[wowok-skills] Installing MCP server ${MCP_PACKAGE}...`);
|
|
744
|
-
if (npmInstallGlobal(MCP_PACKAGE)) {
|
|
745
|
-
console.log(`[wowok-skills] MCP server installed.`);
|
|
746
|
-
return true;
|
|
747
|
-
}
|
|
748
|
-
console.error(`[wowok-skills] MCP server installation FAILED.`);
|
|
749
|
-
return false;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
/**
|
|
753
|
-
* Write MCP server config entry for a given client target.
|
|
754
|
-
* Safely merges with any existing MCP configuration.
|
|
755
|
-
* Supports JSON format (most clients) and TOML format (Codex).
|
|
756
|
-
*
|
|
757
|
-
* When the target's MCP_TARGET_CONFIGS entry has `resolveConfigPaths()`
|
|
758
|
-
* (instead of a single `configPath` string) the function will iterate the
|
|
759
|
-
* returned array so multi-target clients (Roo, CodeBuddy with priority
|
|
760
|
-
* resolution) get every applicable location written to.
|
|
761
|
-
*/
|
|
762
|
-
function writeMcpConfig(target) {
|
|
763
|
-
const cfg = MCP_TARGET_CONFIGS[target];
|
|
764
|
-
if (!cfg) {
|
|
765
|
-
console.log(`[wowok-skills] MCP config: ${target} does not support external MCP registration (skipped).`);
|
|
766
|
-
return false;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// ── TOML format (Codex CLI uses ~/.codex/config.toml) ──────────────
|
|
770
|
-
if (cfg.format === 'toml') {
|
|
771
|
-
try {
|
|
772
|
-
const tomlEntry = '\n[mcp_servers.wowok]\ncommand = "npx"\nargs = ["-y", "@wowok/agent-mcp"]\n';
|
|
773
|
-
let content = '';
|
|
774
|
-
if (fs.existsSync(cfg.configPath)) {
|
|
775
|
-
content = fs.readFileSync(cfg.configPath, 'utf-8');
|
|
776
|
-
}
|
|
777
|
-
// Line-level regex detection. Avoids false positives when the header
|
|
778
|
-
// appears inside a comment (e.g. `; [mcp_servers.wowok]`) or a
|
|
779
|
-
// multi-line string literal.
|
|
780
|
-
if (/^\s*\[mcp_servers\.wowok\]\s*$/m.test(content)) {
|
|
781
|
-
console.log(`[wowok-skills] MCP config already up to date: ${cfg.configPath}`);
|
|
782
|
-
return false;
|
|
783
|
-
}
|
|
784
|
-
fs.mkdirSync(path.dirname(cfg.configPath), { recursive: true });
|
|
785
|
-
fs.writeFileSync(cfg.configPath, content + tomlEntry, 'utf-8');
|
|
786
|
-
console.log(`[wowok-skills] MCP config written: ${cfg.configPath}`);
|
|
787
|
-
return true;
|
|
788
|
-
} catch (err) {
|
|
789
|
-
console.error(`[wowok-skills] ERROR writing MCP config for ${target}: ${err.message}`);
|
|
790
|
-
return false;
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
// ── JSON format (all other clients) ────────────────────────────────
|
|
795
|
-
// Expand to an array of concrete paths so a single target can cover
|
|
796
|
-
// multiple physical files (priority resolution for CodeBuddy, CLI +
|
|
797
|
-
// VS Code variants for Roo/Cline/Kilo).
|
|
798
|
-
const configPaths = cfg.resolveConfigPaths ? cfg.resolveConfigPaths() : [cfg.configPath];
|
|
799
|
-
let anyWritten = false;
|
|
800
|
-
|
|
801
|
-
if (configPaths.length === 0) {
|
|
802
|
-
console.log(`[wowok-skills] MCP config: no config file detected for ${target} (client not installed) — skipped.`);
|
|
803
|
-
return false;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
for (const configPath of configPaths) {
|
|
807
|
-
try {
|
|
808
|
-
let config = {};
|
|
809
|
-
const dir = path.dirname(configPath);
|
|
810
|
-
if (fs.existsSync(configPath)) {
|
|
811
|
-
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
const before = JSON.stringify(config);
|
|
815
|
-
config = cfg.merge(config);
|
|
816
|
-
const after = JSON.stringify(config);
|
|
817
|
-
|
|
818
|
-
if (before !== after) {
|
|
819
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
820
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
821
|
-
console.log(`[wowok-skills] MCP config written: ${configPath}`);
|
|
822
|
-
anyWritten = true;
|
|
823
|
-
} else {
|
|
824
|
-
console.log(`[wowok-skills] MCP config already up to date: ${configPath}`);
|
|
825
|
-
}
|
|
826
|
-
} catch (err) {
|
|
827
|
-
console.error(`[wowok-skills] ERROR writing MCP config for ${target} at ${configPath}: ${err.message}`);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
return anyWritten;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
/**
|
|
835
|
-
* Attempt to restart the running MCP server process.
|
|
836
|
-
*
|
|
837
|
-
* The MCP server is a child process of the IDE (Claude Code, Trae, etc.).
|
|
838
|
-
* We cannot directly "start" it — only kill the existing process so the IDE
|
|
839
|
-
* detects the exit and auto-restarts it with the new version.
|
|
840
|
-
*
|
|
841
|
-
* Best-effort: if the kill fails (no process running), it's harmless.
|
|
842
|
-
*/
|
|
843
|
-
function restartMcpServer() {
|
|
844
|
-
try {
|
|
845
|
-
if (process.platform === 'win32') {
|
|
846
|
-
// Windows: use PowerShell (via -EncodedCommand to avoid quoting hell)
|
|
847
|
-
const psScript =
|
|
848
|
-
'Get-CimInstance Win32_Process -Filter "Name=\'node.exe\' AND CommandLine LIKE \'%wowok%agent-mcp%\'" | ' +
|
|
849
|
-
'ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }';
|
|
850
|
-
const encoded = Buffer.from(psScript, 'utf16le').toString('base64');
|
|
851
|
-
execSync(`powershell -NoProfile -EncodedCommand ${encoded}`, {
|
|
852
|
-
stdio: 'pipe',
|
|
853
|
-
timeout: 10000,
|
|
854
|
-
});
|
|
855
|
-
} else {
|
|
856
|
-
// Unix: pkill by command-line pattern
|
|
857
|
-
execSync('pkill -f "wowok.*agent-mcp" 2>/dev/null || true', {
|
|
858
|
-
stdio: 'pipe',
|
|
859
|
-
timeout: 5000,
|
|
860
|
-
shell: true,
|
|
861
|
-
});
|
|
862
|
-
}
|
|
863
|
-
console.log('[wowok-skills] MCP server process terminated (IDE will auto-restart).');
|
|
864
|
-
return true;
|
|
865
|
-
} catch (e) {
|
|
866
|
-
console.log('[wowok-skills] No running MCP server process found. Start your IDE to launch it.');
|
|
867
|
-
return false;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
main();
|
|
32
|
+
const { runLifecycle } = require(dist);
|
|
33
|
+
runLifecycle(process.env.npm_lifecycle_event || '');
|