skai 0.0.1 → 0.0.5
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 +151 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2258 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -4
- package/index.js +0 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,2258 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
|
|
28
|
+
// node_modules/sisteransi/src/index.js
|
|
29
|
+
var require_src = __commonJS({
|
|
30
|
+
"node_modules/sisteransi/src/index.js"(exports, module) {
|
|
31
|
+
"use strict";
|
|
32
|
+
var ESC = "\x1B";
|
|
33
|
+
var CSI = `${ESC}[`;
|
|
34
|
+
var beep = "\x07";
|
|
35
|
+
var cursor = {
|
|
36
|
+
to(x2, y2) {
|
|
37
|
+
if (!y2) return `${CSI}${x2 + 1}G`;
|
|
38
|
+
return `${CSI}${y2 + 1};${x2 + 1}H`;
|
|
39
|
+
},
|
|
40
|
+
move(x2, y2) {
|
|
41
|
+
let ret = "";
|
|
42
|
+
if (x2 < 0) ret += `${CSI}${-x2}D`;
|
|
43
|
+
else if (x2 > 0) ret += `${CSI}${x2}C`;
|
|
44
|
+
if (y2 < 0) ret += `${CSI}${-y2}A`;
|
|
45
|
+
else if (y2 > 0) ret += `${CSI}${y2}B`;
|
|
46
|
+
return ret;
|
|
47
|
+
},
|
|
48
|
+
up: (count = 1) => `${CSI}${count}A`,
|
|
49
|
+
down: (count = 1) => `${CSI}${count}B`,
|
|
50
|
+
forward: (count = 1) => `${CSI}${count}C`,
|
|
51
|
+
backward: (count = 1) => `${CSI}${count}D`,
|
|
52
|
+
nextLine: (count = 1) => `${CSI}E`.repeat(count),
|
|
53
|
+
prevLine: (count = 1) => `${CSI}F`.repeat(count),
|
|
54
|
+
left: `${CSI}G`,
|
|
55
|
+
hide: `${CSI}?25l`,
|
|
56
|
+
show: `${CSI}?25h`,
|
|
57
|
+
save: `${ESC}7`,
|
|
58
|
+
restore: `${ESC}8`
|
|
59
|
+
};
|
|
60
|
+
var scroll = {
|
|
61
|
+
up: (count = 1) => `${CSI}S`.repeat(count),
|
|
62
|
+
down: (count = 1) => `${CSI}T`.repeat(count)
|
|
63
|
+
};
|
|
64
|
+
var erase = {
|
|
65
|
+
screen: `${CSI}2J`,
|
|
66
|
+
up: (count = 1) => `${CSI}1J`.repeat(count),
|
|
67
|
+
down: (count = 1) => `${CSI}J`.repeat(count),
|
|
68
|
+
line: `${CSI}2K`,
|
|
69
|
+
lineEnd: `${CSI}K`,
|
|
70
|
+
lineStart: `${CSI}1K`,
|
|
71
|
+
lines(count) {
|
|
72
|
+
let clear = "";
|
|
73
|
+
for (let i = 0; i < count; i++)
|
|
74
|
+
clear += this.line + (i < count - 1 ? cursor.up() : "");
|
|
75
|
+
if (count)
|
|
76
|
+
clear += cursor.left;
|
|
77
|
+
return clear;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
module.exports = { cursor, scroll, erase, beep };
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// node_modules/picocolors/picocolors.js
|
|
85
|
+
var require_picocolors = __commonJS({
|
|
86
|
+
"node_modules/picocolors/picocolors.js"(exports, module) {
|
|
87
|
+
"use strict";
|
|
88
|
+
var p2 = process || {};
|
|
89
|
+
var argv = p2.argv || [];
|
|
90
|
+
var env = p2.env || {};
|
|
91
|
+
var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p2.platform === "win32" || (p2.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
92
|
+
var formatter = (open, close, replace = open) => (input) => {
|
|
93
|
+
let string = "" + input, index = string.indexOf(close, open.length);
|
|
94
|
+
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
95
|
+
};
|
|
96
|
+
var replaceClose = (string, close, replace, index) => {
|
|
97
|
+
let result = "", cursor = 0;
|
|
98
|
+
do {
|
|
99
|
+
result += string.substring(cursor, index) + replace;
|
|
100
|
+
cursor = index + close.length;
|
|
101
|
+
index = string.indexOf(close, cursor);
|
|
102
|
+
} while (~index);
|
|
103
|
+
return result + string.substring(cursor);
|
|
104
|
+
};
|
|
105
|
+
var createColors = (enabled = isColorSupported) => {
|
|
106
|
+
let f2 = enabled ? formatter : () => String;
|
|
107
|
+
return {
|
|
108
|
+
isColorSupported: enabled,
|
|
109
|
+
reset: f2("\x1B[0m", "\x1B[0m"),
|
|
110
|
+
bold: f2("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
111
|
+
dim: f2("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
112
|
+
italic: f2("\x1B[3m", "\x1B[23m"),
|
|
113
|
+
underline: f2("\x1B[4m", "\x1B[24m"),
|
|
114
|
+
inverse: f2("\x1B[7m", "\x1B[27m"),
|
|
115
|
+
hidden: f2("\x1B[8m", "\x1B[28m"),
|
|
116
|
+
strikethrough: f2("\x1B[9m", "\x1B[29m"),
|
|
117
|
+
black: f2("\x1B[30m", "\x1B[39m"),
|
|
118
|
+
red: f2("\x1B[31m", "\x1B[39m"),
|
|
119
|
+
green: f2("\x1B[32m", "\x1B[39m"),
|
|
120
|
+
yellow: f2("\x1B[33m", "\x1B[39m"),
|
|
121
|
+
blue: f2("\x1B[34m", "\x1B[39m"),
|
|
122
|
+
magenta: f2("\x1B[35m", "\x1B[39m"),
|
|
123
|
+
cyan: f2("\x1B[36m", "\x1B[39m"),
|
|
124
|
+
white: f2("\x1B[37m", "\x1B[39m"),
|
|
125
|
+
gray: f2("\x1B[90m", "\x1B[39m"),
|
|
126
|
+
bgBlack: f2("\x1B[40m", "\x1B[49m"),
|
|
127
|
+
bgRed: f2("\x1B[41m", "\x1B[49m"),
|
|
128
|
+
bgGreen: f2("\x1B[42m", "\x1B[49m"),
|
|
129
|
+
bgYellow: f2("\x1B[43m", "\x1B[49m"),
|
|
130
|
+
bgBlue: f2("\x1B[44m", "\x1B[49m"),
|
|
131
|
+
bgMagenta: f2("\x1B[45m", "\x1B[49m"),
|
|
132
|
+
bgCyan: f2("\x1B[46m", "\x1B[49m"),
|
|
133
|
+
bgWhite: f2("\x1B[47m", "\x1B[49m"),
|
|
134
|
+
blackBright: f2("\x1B[90m", "\x1B[39m"),
|
|
135
|
+
redBright: f2("\x1B[91m", "\x1B[39m"),
|
|
136
|
+
greenBright: f2("\x1B[92m", "\x1B[39m"),
|
|
137
|
+
yellowBright: f2("\x1B[93m", "\x1B[39m"),
|
|
138
|
+
blueBright: f2("\x1B[94m", "\x1B[39m"),
|
|
139
|
+
magentaBright: f2("\x1B[95m", "\x1B[39m"),
|
|
140
|
+
cyanBright: f2("\x1B[96m", "\x1B[39m"),
|
|
141
|
+
whiteBright: f2("\x1B[97m", "\x1B[39m"),
|
|
142
|
+
bgBlackBright: f2("\x1B[100m", "\x1B[49m"),
|
|
143
|
+
bgRedBright: f2("\x1B[101m", "\x1B[49m"),
|
|
144
|
+
bgGreenBright: f2("\x1B[102m", "\x1B[49m"),
|
|
145
|
+
bgYellowBright: f2("\x1B[103m", "\x1B[49m"),
|
|
146
|
+
bgBlueBright: f2("\x1B[104m", "\x1B[49m"),
|
|
147
|
+
bgMagentaBright: f2("\x1B[105m", "\x1B[49m"),
|
|
148
|
+
bgCyanBright: f2("\x1B[106m", "\x1B[49m"),
|
|
149
|
+
bgWhiteBright: f2("\x1B[107m", "\x1B[49m")
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
module.exports = createColors();
|
|
153
|
+
module.exports.createColors = createColors;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// src/index.ts
|
|
158
|
+
import { Command } from "commander";
|
|
159
|
+
import * as clack from "@clack/prompts";
|
|
160
|
+
import chalk from "chalk";
|
|
161
|
+
import * as fs7 from "fs";
|
|
162
|
+
import * as path7 from "path";
|
|
163
|
+
import { fileURLToPath } from "url";
|
|
164
|
+
|
|
165
|
+
// src/source-parser.ts
|
|
166
|
+
import * as fs from "fs";
|
|
167
|
+
import * as path from "path";
|
|
168
|
+
var GITHUB_SHORTHAND_REGEX = /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/;
|
|
169
|
+
var GITHUB_URL_REGEX = /^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/]+)(?:\/tree\/([^/]+)\/(.+))?/;
|
|
170
|
+
var GITLAB_URL_REGEX = /^https?:\/\/(?:www\.)?gitlab\.com\/([^/]+)\/([^/]+)(?:\/-\/tree\/([^/]+)\/(.+))?/;
|
|
171
|
+
function parseSource(source) {
|
|
172
|
+
if (source.startsWith("./") || source.startsWith("/") || source.startsWith("..")) {
|
|
173
|
+
const absolutePath = path.isAbsolute(source) ? source : path.resolve(process.cwd(), source);
|
|
174
|
+
if (fs.existsSync(absolutePath)) {
|
|
175
|
+
return {
|
|
176
|
+
type: "local",
|
|
177
|
+
localPath: absolutePath
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (GITHUB_SHORTHAND_REGEX.test(source)) {
|
|
182
|
+
const [owner, repo] = source.split("/");
|
|
183
|
+
return {
|
|
184
|
+
type: "github",
|
|
185
|
+
owner,
|
|
186
|
+
repo,
|
|
187
|
+
url: `https://github.com/${owner}/${repo}.git`
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const githubMatch = source.match(GITHUB_URL_REGEX);
|
|
191
|
+
if (githubMatch) {
|
|
192
|
+
const [, owner, repo, branch, subpath] = githubMatch;
|
|
193
|
+
const cleanRepo = repo.replace(/\.git$/, "");
|
|
194
|
+
return {
|
|
195
|
+
type: "github",
|
|
196
|
+
owner,
|
|
197
|
+
repo: cleanRepo,
|
|
198
|
+
branch: branch || void 0,
|
|
199
|
+
subpath: subpath || void 0,
|
|
200
|
+
url: `https://github.com/${owner}/${cleanRepo}.git`
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const gitlabMatch = source.match(GITLAB_URL_REGEX);
|
|
204
|
+
if (gitlabMatch) {
|
|
205
|
+
const [, owner, repo, branch, subpath] = gitlabMatch;
|
|
206
|
+
const cleanRepo = repo.replace(/\.git$/, "");
|
|
207
|
+
return {
|
|
208
|
+
type: "gitlab",
|
|
209
|
+
owner,
|
|
210
|
+
repo: cleanRepo,
|
|
211
|
+
branch: branch || void 0,
|
|
212
|
+
subpath: subpath || void 0,
|
|
213
|
+
url: `https://gitlab.com/${owner}/${cleanRepo}.git`
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (source.includes(".git") || source.startsWith("git@") || source.startsWith("git://")) {
|
|
217
|
+
return {
|
|
218
|
+
type: "git",
|
|
219
|
+
url: source
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const resolved = path.resolve(process.cwd(), source);
|
|
223
|
+
if (fs.existsSync(resolved)) {
|
|
224
|
+
return {
|
|
225
|
+
type: "local",
|
|
226
|
+
localPath: resolved
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
type: "git",
|
|
231
|
+
url: source.endsWith(".git") ? source : `${source}.git`
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/git.ts
|
|
236
|
+
import * as fs2 from "fs";
|
|
237
|
+
import * as path2 from "path";
|
|
238
|
+
import * as os from "os";
|
|
239
|
+
import { simpleGit } from "simple-git";
|
|
240
|
+
var git = simpleGit();
|
|
241
|
+
async function cloneRepo(url, branch) {
|
|
242
|
+
const tempDir = fs2.mkdtempSync(path2.join(os.tmpdir(), "skai-"));
|
|
243
|
+
const options = ["--depth", "1"];
|
|
244
|
+
if (branch) {
|
|
245
|
+
options.push("--branch", branch);
|
|
246
|
+
}
|
|
247
|
+
await git.clone(url, tempDir, options);
|
|
248
|
+
return tempDir;
|
|
249
|
+
}
|
|
250
|
+
function cleanupTempDir(tempDir) {
|
|
251
|
+
const systemTmp = os.tmpdir();
|
|
252
|
+
const resolved = path2.resolve(tempDir);
|
|
253
|
+
if (!resolved.startsWith(systemTmp)) {
|
|
254
|
+
throw new Error(`Refusing to cleanup directory outside temp: ${tempDir}`);
|
|
255
|
+
}
|
|
256
|
+
if (!resolved.includes("skai-")) {
|
|
257
|
+
throw new Error(`Refusing to cleanup non-skai temp directory: ${tempDir}`);
|
|
258
|
+
}
|
|
259
|
+
if (fs2.existsSync(resolved)) {
|
|
260
|
+
fs2.rmSync(resolved, { recursive: true, force: true });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/skills.ts
|
|
265
|
+
import * as fs3 from "fs";
|
|
266
|
+
import * as path3 from "path";
|
|
267
|
+
import matter from "gray-matter";
|
|
268
|
+
var SKILL_FILENAME = "SKILL.md";
|
|
269
|
+
var MAX_DEPTH = 10;
|
|
270
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
271
|
+
"node_modules",
|
|
272
|
+
".git",
|
|
273
|
+
"dist",
|
|
274
|
+
"build",
|
|
275
|
+
".next",
|
|
276
|
+
".nuxt",
|
|
277
|
+
"coverage",
|
|
278
|
+
"__pycache__"
|
|
279
|
+
]);
|
|
280
|
+
var TRANSPARENT_DIRS = /* @__PURE__ */ new Set([".curated", ".experimental", ".system"]);
|
|
281
|
+
var PRIORITY_DIRS = [
|
|
282
|
+
"",
|
|
283
|
+
// repo root
|
|
284
|
+
"skills",
|
|
285
|
+
"skills/.curated",
|
|
286
|
+
"skills/.experimental",
|
|
287
|
+
"skills/.system",
|
|
288
|
+
".claude/skills",
|
|
289
|
+
".cursor/skills",
|
|
290
|
+
".opencode/skill",
|
|
291
|
+
".codex/skills",
|
|
292
|
+
".agents/skills",
|
|
293
|
+
".kilocode/skills",
|
|
294
|
+
".roo/skills",
|
|
295
|
+
".goose/skills",
|
|
296
|
+
".gemini/skills",
|
|
297
|
+
".agent/skills",
|
|
298
|
+
".github/skills",
|
|
299
|
+
".factory/skills",
|
|
300
|
+
".windsurf/skills"
|
|
301
|
+
];
|
|
302
|
+
function extractCategory(skillDir, searchRoot) {
|
|
303
|
+
const relativePath = path3.relative(searchRoot, skillDir);
|
|
304
|
+
if (!relativePath || relativePath === ".") {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
const segments = relativePath.split(path3.sep).filter((seg) => !TRANSPARENT_DIRS.has(seg));
|
|
308
|
+
if (segments.length <= 1) {
|
|
309
|
+
return [];
|
|
310
|
+
}
|
|
311
|
+
return segments.slice(0, -1);
|
|
312
|
+
}
|
|
313
|
+
function parseSkillMd(filePath, searchRoot) {
|
|
314
|
+
try {
|
|
315
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
316
|
+
const { data, content: markdownContent } = matter(content);
|
|
317
|
+
const skillDir = path3.dirname(filePath);
|
|
318
|
+
if (!data.name) {
|
|
319
|
+
data.name = path3.basename(skillDir);
|
|
320
|
+
}
|
|
321
|
+
const category = searchRoot ? extractCategory(skillDir, searchRoot) : [];
|
|
322
|
+
return {
|
|
323
|
+
name: data.name,
|
|
324
|
+
description: data.description || "",
|
|
325
|
+
path: skillDir,
|
|
326
|
+
content: markdownContent.trim(),
|
|
327
|
+
category: category.length > 0 ? category : void 0
|
|
328
|
+
};
|
|
329
|
+
} catch {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function searchDirectory(dir, searchRoot, seenPaths, skills, depth) {
|
|
334
|
+
if (depth > MAX_DEPTH) return;
|
|
335
|
+
let entries;
|
|
336
|
+
try {
|
|
337
|
+
entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
338
|
+
} catch {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
for (const entry of entries) {
|
|
342
|
+
if (entry.isFile() && entry.name === SKILL_FILENAME) {
|
|
343
|
+
const skillPath = path3.join(dir, entry.name);
|
|
344
|
+
const skill = parseSkillMd(skillPath, searchRoot);
|
|
345
|
+
if (skill && !seenPaths.has(skill.path)) {
|
|
346
|
+
seenPaths.add(skill.path);
|
|
347
|
+
skills.push(skill);
|
|
348
|
+
}
|
|
349
|
+
} else if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
|
|
350
|
+
searchDirectory(path3.join(dir, entry.name), searchRoot, seenPaths, skills, depth + 1);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function discoverSkills(basePath, subpath) {
|
|
355
|
+
const skills = [];
|
|
356
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
357
|
+
if (subpath) {
|
|
358
|
+
const targetPath = path3.join(basePath, subpath);
|
|
359
|
+
if (fs3.existsSync(targetPath)) {
|
|
360
|
+
searchDirectory(targetPath, targetPath, seenPaths, skills, 0);
|
|
361
|
+
}
|
|
362
|
+
return skills;
|
|
363
|
+
}
|
|
364
|
+
for (const priorityDir of PRIORITY_DIRS) {
|
|
365
|
+
const targetPath = path3.join(basePath, priorityDir);
|
|
366
|
+
if (!fs3.existsSync(targetPath)) continue;
|
|
367
|
+
if (priorityDir === "") {
|
|
368
|
+
const rootSkillFile = path3.join(targetPath, SKILL_FILENAME);
|
|
369
|
+
if (fs3.existsSync(rootSkillFile)) {
|
|
370
|
+
const skill = parseSkillMd(rootSkillFile, targetPath);
|
|
371
|
+
if (skill && !seenPaths.has(skill.path)) {
|
|
372
|
+
seenPaths.add(skill.path);
|
|
373
|
+
skills.push(skill);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
let entries;
|
|
377
|
+
try {
|
|
378
|
+
entries = fs3.readdirSync(targetPath, { withFileTypes: true });
|
|
379
|
+
} catch {
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
for (const entry of entries) {
|
|
383
|
+
if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
|
|
384
|
+
const subSkillFile = path3.join(targetPath, entry.name, SKILL_FILENAME);
|
|
385
|
+
if (fs3.existsSync(subSkillFile)) {
|
|
386
|
+
const skill = parseSkillMd(subSkillFile, targetPath);
|
|
387
|
+
if (skill && !seenPaths.has(skill.path)) {
|
|
388
|
+
seenPaths.add(skill.path);
|
|
389
|
+
skills.push(skill);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
searchDirectory(targetPath, targetPath, seenPaths, skills, 0);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (skills.length === 0) {
|
|
399
|
+
searchDirectory(basePath, basePath, seenPaths, skills, 0);
|
|
400
|
+
}
|
|
401
|
+
return skills;
|
|
402
|
+
}
|
|
403
|
+
function buildSkillTree(skills) {
|
|
404
|
+
const root = { name: "root", children: /* @__PURE__ */ new Map() };
|
|
405
|
+
for (const skill of skills) {
|
|
406
|
+
let current = root;
|
|
407
|
+
for (const cat of skill.category || []) {
|
|
408
|
+
if (!current.children.has(cat)) {
|
|
409
|
+
current.children.set(cat, { name: cat, children: /* @__PURE__ */ new Map() });
|
|
410
|
+
}
|
|
411
|
+
current = current.children.get(cat);
|
|
412
|
+
}
|
|
413
|
+
current.children.set(skill.name, {
|
|
414
|
+
name: skill.name,
|
|
415
|
+
skill,
|
|
416
|
+
children: /* @__PURE__ */ new Map()
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
return root;
|
|
420
|
+
}
|
|
421
|
+
function skillTreeToTreeNodes(node, parentPath = "") {
|
|
422
|
+
const result = [];
|
|
423
|
+
const categories = [];
|
|
424
|
+
const skills = [];
|
|
425
|
+
for (const child of node.children.values()) {
|
|
426
|
+
if (child.skill) {
|
|
427
|
+
skills.push(child);
|
|
428
|
+
} else {
|
|
429
|
+
categories.push(child);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
categories.sort((a, b2) => a.name.localeCompare(b2.name));
|
|
433
|
+
skills.sort((a, b2) => a.name.localeCompare(b2.name));
|
|
434
|
+
for (const cat of categories) {
|
|
435
|
+
const id = parentPath ? `${parentPath}/${cat.name}` : cat.name;
|
|
436
|
+
const children = skillTreeToTreeNodes(cat, id);
|
|
437
|
+
result.push({
|
|
438
|
+
id,
|
|
439
|
+
label: cat.name,
|
|
440
|
+
children,
|
|
441
|
+
expanded: true
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
for (const s of skills) {
|
|
445
|
+
const id = parentPath ? `${parentPath}/${s.name}` : s.name;
|
|
446
|
+
result.push({
|
|
447
|
+
id,
|
|
448
|
+
label: s.name,
|
|
449
|
+
hint: s.skill.description,
|
|
450
|
+
skill: s.skill,
|
|
451
|
+
selected: false
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
return result;
|
|
455
|
+
}
|
|
456
|
+
function getQualifiedName(skill) {
|
|
457
|
+
return [...skill.category || [], skill.name].join("/");
|
|
458
|
+
}
|
|
459
|
+
function flattenSingleCategories(nodes) {
|
|
460
|
+
if (nodes.length === 1 && nodes[0].children && !nodes[0].skill) {
|
|
461
|
+
return flattenSingleCategories(nodes[0].children);
|
|
462
|
+
}
|
|
463
|
+
return nodes.map((node) => {
|
|
464
|
+
if (node.children && node.children.length > 0) {
|
|
465
|
+
return { ...node, children: flattenSingleCategories(node.children) };
|
|
466
|
+
}
|
|
467
|
+
return node;
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
function matchesSkillFilter(skill, filter) {
|
|
471
|
+
const filterLower = filter.toLowerCase();
|
|
472
|
+
if (filter.includes("/")) {
|
|
473
|
+
const qualifiedName = getQualifiedName(skill);
|
|
474
|
+
return qualifiedName.toLowerCase() === filterLower;
|
|
475
|
+
}
|
|
476
|
+
return skill.name.toLowerCase() === filterLower;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/agents.ts
|
|
480
|
+
import * as fs4 from "fs";
|
|
481
|
+
import * as path4 from "path";
|
|
482
|
+
import * as os2 from "os";
|
|
483
|
+
var homeDir = os2.homedir();
|
|
484
|
+
var AGENTS = {
|
|
485
|
+
opencode: {
|
|
486
|
+
name: "opencode",
|
|
487
|
+
displayName: "OpenCode",
|
|
488
|
+
projectPath: ".opencode/skill/",
|
|
489
|
+
globalPath: path4.join(homeDir, ".config/opencode/skill/")
|
|
490
|
+
},
|
|
491
|
+
"claude-code": {
|
|
492
|
+
name: "claude-code",
|
|
493
|
+
displayName: "Claude Code",
|
|
494
|
+
projectPath: ".claude/skills/",
|
|
495
|
+
globalPath: path4.join(homeDir, ".claude/skills/")
|
|
496
|
+
},
|
|
497
|
+
codex: {
|
|
498
|
+
name: "codex",
|
|
499
|
+
displayName: "Codex",
|
|
500
|
+
projectPath: ".codex/skills/",
|
|
501
|
+
globalPath: path4.join(homeDir, ".codex/skills/")
|
|
502
|
+
},
|
|
503
|
+
cursor: {
|
|
504
|
+
name: "cursor",
|
|
505
|
+
displayName: "Cursor",
|
|
506
|
+
projectPath: ".cursor/skills/",
|
|
507
|
+
globalPath: path4.join(homeDir, ".cursor/skills/")
|
|
508
|
+
},
|
|
509
|
+
amp: {
|
|
510
|
+
name: "amp",
|
|
511
|
+
displayName: "Amp",
|
|
512
|
+
projectPath: ".agents/skills/",
|
|
513
|
+
globalPath: path4.join(homeDir, ".config/agents/skills/")
|
|
514
|
+
},
|
|
515
|
+
"kilo-code": {
|
|
516
|
+
name: "kilo-code",
|
|
517
|
+
displayName: "Kilo Code",
|
|
518
|
+
projectPath: ".kilocode/skills/",
|
|
519
|
+
globalPath: path4.join(homeDir, ".kilocode/skills/")
|
|
520
|
+
},
|
|
521
|
+
"roo-code": {
|
|
522
|
+
name: "roo-code",
|
|
523
|
+
displayName: "Roo Code",
|
|
524
|
+
projectPath: ".roo/skills/",
|
|
525
|
+
globalPath: path4.join(homeDir, ".roo/skills/")
|
|
526
|
+
},
|
|
527
|
+
goose: {
|
|
528
|
+
name: "goose",
|
|
529
|
+
displayName: "Goose",
|
|
530
|
+
projectPath: ".goose/skills/",
|
|
531
|
+
globalPath: path4.join(homeDir, ".config/goose/skills/")
|
|
532
|
+
},
|
|
533
|
+
gemini: {
|
|
534
|
+
name: "gemini",
|
|
535
|
+
displayName: "Gemini CLI",
|
|
536
|
+
projectPath: ".gemini/skills/",
|
|
537
|
+
globalPath: path4.join(homeDir, ".gemini/skills/")
|
|
538
|
+
},
|
|
539
|
+
antigravity: {
|
|
540
|
+
name: "antigravity",
|
|
541
|
+
displayName: "Antigravity",
|
|
542
|
+
projectPath: ".agent/skills/",
|
|
543
|
+
globalPath: path4.join(homeDir, ".gemini/antigravity/skills/")
|
|
544
|
+
},
|
|
545
|
+
copilot: {
|
|
546
|
+
name: "copilot",
|
|
547
|
+
displayName: "GitHub Copilot",
|
|
548
|
+
projectPath: ".github/skills/",
|
|
549
|
+
globalPath: path4.join(homeDir, ".copilot/skills/")
|
|
550
|
+
},
|
|
551
|
+
clawdbot: {
|
|
552
|
+
name: "clawdbot",
|
|
553
|
+
displayName: "Clawdbot",
|
|
554
|
+
projectPath: "skills/",
|
|
555
|
+
globalPath: path4.join(homeDir, ".clawdbot/skills/")
|
|
556
|
+
},
|
|
557
|
+
droid: {
|
|
558
|
+
name: "droid",
|
|
559
|
+
displayName: "Droid",
|
|
560
|
+
projectPath: ".factory/skills/",
|
|
561
|
+
globalPath: path4.join(homeDir, ".factory/skills/")
|
|
562
|
+
},
|
|
563
|
+
windsurf: {
|
|
564
|
+
name: "windsurf",
|
|
565
|
+
displayName: "Windsurf",
|
|
566
|
+
projectPath: ".windsurf/skills/",
|
|
567
|
+
globalPath: path4.join(homeDir, ".codeium/windsurf/skills/")
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
function detectInstalledAgents() {
|
|
571
|
+
const detected = [];
|
|
572
|
+
for (const agent of Object.values(AGENTS)) {
|
|
573
|
+
const configDir = path4.dirname(agent.globalPath);
|
|
574
|
+
if (fs4.existsSync(configDir)) {
|
|
575
|
+
detected.push(agent);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return detected;
|
|
579
|
+
}
|
|
580
|
+
function getAgentByName(name) {
|
|
581
|
+
const normalized = name.toLowerCase().replace(/\s+/g, "-");
|
|
582
|
+
return AGENTS[normalized];
|
|
583
|
+
}
|
|
584
|
+
function getAllAgents() {
|
|
585
|
+
return Object.values(AGENTS);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/installer.ts
|
|
589
|
+
import * as fs5 from "fs";
|
|
590
|
+
import * as path5 from "path";
|
|
591
|
+
var EXCLUDE_FILES = /* @__PURE__ */ new Set(["README.md", "readme.md", "metadata.json"]);
|
|
592
|
+
function sanitizeName(name) {
|
|
593
|
+
return name.replace(/[/\\]/g, "").replace(/\0/g, "").replace(/^\.+|\.+$/g, "").trim();
|
|
594
|
+
}
|
|
595
|
+
function isPathSafe(targetPath, basePath) {
|
|
596
|
+
const resolvedTarget = path5.resolve(targetPath);
|
|
597
|
+
const resolvedBase = path5.resolve(basePath);
|
|
598
|
+
return resolvedTarget.startsWith(resolvedBase + path5.sep) || resolvedTarget === resolvedBase;
|
|
599
|
+
}
|
|
600
|
+
function copyDirectory(src, dest) {
|
|
601
|
+
fs5.mkdirSync(dest, { recursive: true });
|
|
602
|
+
const entries = fs5.readdirSync(src, { withFileTypes: true });
|
|
603
|
+
for (const entry of entries) {
|
|
604
|
+
if (EXCLUDE_FILES.has(entry.name) || entry.name.startsWith("_")) {
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
const srcPath = path5.join(src, entry.name);
|
|
608
|
+
const destPath = path5.join(dest, entry.name);
|
|
609
|
+
if (entry.isDirectory()) {
|
|
610
|
+
copyDirectory(srcPath, destPath);
|
|
611
|
+
} else {
|
|
612
|
+
fs5.copyFileSync(srcPath, destPath);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
function isSkillInstalled(skill, agent, options) {
|
|
617
|
+
const basePath = options.global ? agent.globalPath : path5.join(process.cwd(), agent.projectPath);
|
|
618
|
+
const sanitizedName = sanitizeName(skill.name);
|
|
619
|
+
const targetPath = path5.join(basePath, sanitizedName);
|
|
620
|
+
return fs5.existsSync(targetPath);
|
|
621
|
+
}
|
|
622
|
+
function installSkillForAgent(skill, agent, options) {
|
|
623
|
+
const basePath = options.global ? agent.globalPath : path5.join(process.cwd(), agent.projectPath);
|
|
624
|
+
const sanitizedName = sanitizeName(skill.name);
|
|
625
|
+
const targetPath = path5.join(basePath, sanitizedName);
|
|
626
|
+
if (!isPathSafe(targetPath, basePath)) {
|
|
627
|
+
return {
|
|
628
|
+
skill,
|
|
629
|
+
agent,
|
|
630
|
+
success: false,
|
|
631
|
+
targetPath,
|
|
632
|
+
error: `Invalid skill name: ${skill.name}`
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
try {
|
|
636
|
+
fs5.mkdirSync(basePath, { recursive: true });
|
|
637
|
+
copyDirectory(skill.path, targetPath);
|
|
638
|
+
return {
|
|
639
|
+
skill,
|
|
640
|
+
agent,
|
|
641
|
+
success: true,
|
|
642
|
+
targetPath
|
|
643
|
+
};
|
|
644
|
+
} catch (error) {
|
|
645
|
+
return {
|
|
646
|
+
skill,
|
|
647
|
+
agent,
|
|
648
|
+
success: false,
|
|
649
|
+
targetPath,
|
|
650
|
+
error: error instanceof Error ? error.message : String(error)
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/tree-select.ts
|
|
656
|
+
import * as p from "@clack/prompts";
|
|
657
|
+
|
|
658
|
+
// node_modules/@clack/core/dist/index.mjs
|
|
659
|
+
var import_sisteransi = __toESM(require_src(), 1);
|
|
660
|
+
import { stdin as $, stdout as j } from "process";
|
|
661
|
+
import * as f from "readline";
|
|
662
|
+
import M from "readline";
|
|
663
|
+
import { WriteStream as U } from "tty";
|
|
664
|
+
function J({ onlyFirst: t = false } = {}) {
|
|
665
|
+
const F = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
|
|
666
|
+
return new RegExp(F, t ? void 0 : "g");
|
|
667
|
+
}
|
|
668
|
+
var Q = J();
|
|
669
|
+
function T(t) {
|
|
670
|
+
if (typeof t != "string") throw new TypeError(`Expected a \`string\`, got \`${typeof t}\``);
|
|
671
|
+
return t.replace(Q, "");
|
|
672
|
+
}
|
|
673
|
+
function O(t) {
|
|
674
|
+
return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
|
|
675
|
+
}
|
|
676
|
+
var P = { exports: {} };
|
|
677
|
+
(function(t) {
|
|
678
|
+
var u = {};
|
|
679
|
+
t.exports = u, u.eastAsianWidth = function(e) {
|
|
680
|
+
var s = e.charCodeAt(0), i = e.length == 2 ? e.charCodeAt(1) : 0, D = s;
|
|
681
|
+
return 55296 <= s && s <= 56319 && 56320 <= i && i <= 57343 && (s &= 1023, i &= 1023, D = s << 10 | i, D += 65536), D == 12288 || 65281 <= D && D <= 65376 || 65504 <= D && D <= 65510 ? "F" : D == 8361 || 65377 <= D && D <= 65470 || 65474 <= D && D <= 65479 || 65482 <= D && D <= 65487 || 65490 <= D && D <= 65495 || 65498 <= D && D <= 65500 || 65512 <= D && D <= 65518 ? "H" : 4352 <= D && D <= 4447 || 4515 <= D && D <= 4519 || 4602 <= D && D <= 4607 || 9001 <= D && D <= 9002 || 11904 <= D && D <= 11929 || 11931 <= D && D <= 12019 || 12032 <= D && D <= 12245 || 12272 <= D && D <= 12283 || 12289 <= D && D <= 12350 || 12353 <= D && D <= 12438 || 12441 <= D && D <= 12543 || 12549 <= D && D <= 12589 || 12593 <= D && D <= 12686 || 12688 <= D && D <= 12730 || 12736 <= D && D <= 12771 || 12784 <= D && D <= 12830 || 12832 <= D && D <= 12871 || 12880 <= D && D <= 13054 || 13056 <= D && D <= 19903 || 19968 <= D && D <= 42124 || 42128 <= D && D <= 42182 || 43360 <= D && D <= 43388 || 44032 <= D && D <= 55203 || 55216 <= D && D <= 55238 || 55243 <= D && D <= 55291 || 63744 <= D && D <= 64255 || 65040 <= D && D <= 65049 || 65072 <= D && D <= 65106 || 65108 <= D && D <= 65126 || 65128 <= D && D <= 65131 || 110592 <= D && D <= 110593 || 127488 <= D && D <= 127490 || 127504 <= D && D <= 127546 || 127552 <= D && D <= 127560 || 127568 <= D && D <= 127569 || 131072 <= D && D <= 194367 || 177984 <= D && D <= 196605 || 196608 <= D && D <= 262141 ? "W" : 32 <= D && D <= 126 || 162 <= D && D <= 163 || 165 <= D && D <= 166 || D == 172 || D == 175 || 10214 <= D && D <= 10221 || 10629 <= D && D <= 10630 ? "Na" : D == 161 || D == 164 || 167 <= D && D <= 168 || D == 170 || 173 <= D && D <= 174 || 176 <= D && D <= 180 || 182 <= D && D <= 186 || 188 <= D && D <= 191 || D == 198 || D == 208 || 215 <= D && D <= 216 || 222 <= D && D <= 225 || D == 230 || 232 <= D && D <= 234 || 236 <= D && D <= 237 || D == 240 || 242 <= D && D <= 243 || 247 <= D && D <= 250 || D == 252 || D == 254 || D == 257 || D == 273 || D == 275 || D == 283 || 294 <= D && D <= 295 || D == 299 || 305 <= D && D <= 307 || D == 312 || 319 <= D && D <= 322 || D == 324 || 328 <= D && D <= 331 || D == 333 || 338 <= D && D <= 339 || 358 <= D && D <= 359 || D == 363 || D == 462 || D == 464 || D == 466 || D == 468 || D == 470 || D == 472 || D == 474 || D == 476 || D == 593 || D == 609 || D == 708 || D == 711 || 713 <= D && D <= 715 || D == 717 || D == 720 || 728 <= D && D <= 731 || D == 733 || D == 735 || 768 <= D && D <= 879 || 913 <= D && D <= 929 || 931 <= D && D <= 937 || 945 <= D && D <= 961 || 963 <= D && D <= 969 || D == 1025 || 1040 <= D && D <= 1103 || D == 1105 || D == 8208 || 8211 <= D && D <= 8214 || 8216 <= D && D <= 8217 || 8220 <= D && D <= 8221 || 8224 <= D && D <= 8226 || 8228 <= D && D <= 8231 || D == 8240 || 8242 <= D && D <= 8243 || D == 8245 || D == 8251 || D == 8254 || D == 8308 || D == 8319 || 8321 <= D && D <= 8324 || D == 8364 || D == 8451 || D == 8453 || D == 8457 || D == 8467 || D == 8470 || 8481 <= D && D <= 8482 || D == 8486 || D == 8491 || 8531 <= D && D <= 8532 || 8539 <= D && D <= 8542 || 8544 <= D && D <= 8555 || 8560 <= D && D <= 8569 || D == 8585 || 8592 <= D && D <= 8601 || 8632 <= D && D <= 8633 || D == 8658 || D == 8660 || D == 8679 || D == 8704 || 8706 <= D && D <= 8707 || 8711 <= D && D <= 8712 || D == 8715 || D == 8719 || D == 8721 || D == 8725 || D == 8730 || 8733 <= D && D <= 8736 || D == 8739 || D == 8741 || 8743 <= D && D <= 8748 || D == 8750 || 8756 <= D && D <= 8759 || 8764 <= D && D <= 8765 || D == 8776 || D == 8780 || D == 8786 || 8800 <= D && D <= 8801 || 8804 <= D && D <= 8807 || 8810 <= D && D <= 8811 || 8814 <= D && D <= 8815 || 8834 <= D && D <= 8835 || 8838 <= D && D <= 8839 || D == 8853 || D == 8857 || D == 8869 || D == 8895 || D == 8978 || 9312 <= D && D <= 9449 || 9451 <= D && D <= 9547 || 9552 <= D && D <= 9587 || 9600 <= D && D <= 9615 || 9618 <= D && D <= 9621 || 9632 <= D && D <= 9633 || 9635 <= D && D <= 9641 || 9650 <= D && D <= 9651 || 9654 <= D && D <= 9655 || 9660 <= D && D <= 9661 || 9664 <= D && D <= 9665 || 9670 <= D && D <= 9672 || D == 9675 || 9678 <= D && D <= 9681 || 9698 <= D && D <= 9701 || D == 9711 || 9733 <= D && D <= 9734 || D == 9737 || 9742 <= D && D <= 9743 || 9748 <= D && D <= 9749 || D == 9756 || D == 9758 || D == 9792 || D == 9794 || 9824 <= D && D <= 9825 || 9827 <= D && D <= 9829 || 9831 <= D && D <= 9834 || 9836 <= D && D <= 9837 || D == 9839 || 9886 <= D && D <= 9887 || 9918 <= D && D <= 9919 || 9924 <= D && D <= 9933 || 9935 <= D && D <= 9953 || D == 9955 || 9960 <= D && D <= 9983 || D == 10045 || D == 10071 || 10102 <= D && D <= 10111 || 11093 <= D && D <= 11097 || 12872 <= D && D <= 12879 || 57344 <= D && D <= 63743 || 65024 <= D && D <= 65039 || D == 65533 || 127232 <= D && D <= 127242 || 127248 <= D && D <= 127277 || 127280 <= D && D <= 127337 || 127344 <= D && D <= 127386 || 917760 <= D && D <= 917999 || 983040 <= D && D <= 1048573 || 1048576 <= D && D <= 1114109 ? "A" : "N";
|
|
682
|
+
}, u.characterLength = function(e) {
|
|
683
|
+
var s = this.eastAsianWidth(e);
|
|
684
|
+
return s == "F" || s == "W" || s == "A" ? 2 : 1;
|
|
685
|
+
};
|
|
686
|
+
function F(e) {
|
|
687
|
+
return e.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || [];
|
|
688
|
+
}
|
|
689
|
+
u.length = function(e) {
|
|
690
|
+
for (var s = F(e), i = 0, D = 0; D < s.length; D++) i = i + this.characterLength(s[D]);
|
|
691
|
+
return i;
|
|
692
|
+
}, u.slice = function(e, s, i) {
|
|
693
|
+
textLen = u.length(e), s = s || 0, i = i || 1, s < 0 && (s = textLen + s), i < 0 && (i = textLen + i);
|
|
694
|
+
for (var D = "", C = 0, o = F(e), E = 0; E < o.length; E++) {
|
|
695
|
+
var a = o[E], n = u.length(a);
|
|
696
|
+
if (C >= s - (n == 2 ? 1 : 0)) if (C + n <= i) D += a;
|
|
697
|
+
else break;
|
|
698
|
+
C += n;
|
|
699
|
+
}
|
|
700
|
+
return D;
|
|
701
|
+
};
|
|
702
|
+
})(P);
|
|
703
|
+
var X = P.exports;
|
|
704
|
+
var DD = O(X);
|
|
705
|
+
var uD = function() {
|
|
706
|
+
return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67)\uDB40\uDC7F|(?:\uD83E\uDDD1\uD83C\uDFFF\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFC-\uDFFF])|\uD83D\uDC68(?:\uD83C\uDFFB(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))?|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])\uFE0F|\u200D(?:(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D[\uDC66\uDC67])|\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC)?|(?:\uD83D\uDC69(?:\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69]))|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC69(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83E\uDDD1(?:\u200D(?:\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDE36\u200D\uD83C\uDF2B|\uD83C\uDFF3\uFE0F\u200D\u26A7|\uD83D\uDC3B\u200D\u2744|(?:(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\uD83C\uDFF4\u200D\u2620|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])\u200D[\u2640\u2642]|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u2600-\u2604\u260E\u2611\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26B0\u26B1\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0\u26F1\u26F4\u26F7\u26F8\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u3030\u303D\u3297\u3299]|\uD83C[\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]|\uD83D[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3])\uFE0F|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDE35\u200D\uD83D\uDCAB|\uD83D\uDE2E\u200D\uD83D\uDCA8|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83E\uDDD1(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83D\uDC69(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF6\uD83C\uDDE6|\uD83C\uDDF4\uD83C\uDDF2|\uD83D\uDC08\u200D\u2B1B|\u2764\uFE0F\u200D(?:\uD83D\uDD25|\uD83E\uDE79)|\uD83D\uDC41\uFE0F|\uD83C\uDFF3\uFE0F|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|[#\*0-9]\uFE0F\u20E3|\u2764\uFE0F|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|\uD83C\uDFF4|(?:[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270C\u270D]|\uD83D[\uDD74\uDD90])(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC08\uDC15\uDC3B\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE2E\uDE35\uDE36\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5]|\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD]|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0D\uDD0E\uDD10-\uDD17\uDD1D\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78\uDD7A-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCB\uDDD0\uDDE0-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6]|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5-\uDED7\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDD77\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g;
|
|
707
|
+
};
|
|
708
|
+
var FD = O(uD);
|
|
709
|
+
function A(t, u = {}) {
|
|
710
|
+
if (typeof t != "string" || t.length === 0 || (u = { ambiguousIsNarrow: true, ...u }, t = T(t), t.length === 0)) return 0;
|
|
711
|
+
t = t.replace(FD(), " ");
|
|
712
|
+
const F = u.ambiguousIsNarrow ? 1 : 2;
|
|
713
|
+
let e = 0;
|
|
714
|
+
for (const s of t) {
|
|
715
|
+
const i = s.codePointAt(0);
|
|
716
|
+
if (i <= 31 || i >= 127 && i <= 159 || i >= 768 && i <= 879) continue;
|
|
717
|
+
switch (DD.eastAsianWidth(s)) {
|
|
718
|
+
case "F":
|
|
719
|
+
case "W":
|
|
720
|
+
e += 2;
|
|
721
|
+
break;
|
|
722
|
+
case "A":
|
|
723
|
+
e += F;
|
|
724
|
+
break;
|
|
725
|
+
default:
|
|
726
|
+
e += 1;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return e;
|
|
730
|
+
}
|
|
731
|
+
var m = 10;
|
|
732
|
+
var L = (t = 0) => (u) => `\x1B[${u + t}m`;
|
|
733
|
+
var N = (t = 0) => (u) => `\x1B[${38 + t};5;${u}m`;
|
|
734
|
+
var I = (t = 0) => (u, F, e) => `\x1B[${38 + t};2;${u};${F};${e}m`;
|
|
735
|
+
var r = { modifier: { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], blackBright: [90, 39], gray: [90, 39], grey: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], bgBlackBright: [100, 49], bgGray: [100, 49], bgGrey: [100, 49], bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } };
|
|
736
|
+
Object.keys(r.modifier);
|
|
737
|
+
var tD = Object.keys(r.color);
|
|
738
|
+
var eD = Object.keys(r.bgColor);
|
|
739
|
+
[...tD, ...eD];
|
|
740
|
+
function sD() {
|
|
741
|
+
const t = /* @__PURE__ */ new Map();
|
|
742
|
+
for (const [u, F] of Object.entries(r)) {
|
|
743
|
+
for (const [e, s] of Object.entries(F)) r[e] = { open: `\x1B[${s[0]}m`, close: `\x1B[${s[1]}m` }, F[e] = r[e], t.set(s[0], s[1]);
|
|
744
|
+
Object.defineProperty(r, u, { value: F, enumerable: false });
|
|
745
|
+
}
|
|
746
|
+
return Object.defineProperty(r, "codes", { value: t, enumerable: false }), r.color.close = "\x1B[39m", r.bgColor.close = "\x1B[49m", r.color.ansi = L(), r.color.ansi256 = N(), r.color.ansi16m = I(), r.bgColor.ansi = L(m), r.bgColor.ansi256 = N(m), r.bgColor.ansi16m = I(m), Object.defineProperties(r, { rgbToAnsi256: { value: (u, F, e) => u === F && F === e ? u < 8 ? 16 : u > 248 ? 231 : Math.round((u - 8) / 247 * 24) + 232 : 16 + 36 * Math.round(u / 255 * 5) + 6 * Math.round(F / 255 * 5) + Math.round(e / 255 * 5), enumerable: false }, hexToRgb: { value: (u) => {
|
|
747
|
+
const F = /[a-f\d]{6}|[a-f\d]{3}/i.exec(u.toString(16));
|
|
748
|
+
if (!F) return [0, 0, 0];
|
|
749
|
+
let [e] = F;
|
|
750
|
+
e.length === 3 && (e = [...e].map((i) => i + i).join(""));
|
|
751
|
+
const s = Number.parseInt(e, 16);
|
|
752
|
+
return [s >> 16 & 255, s >> 8 & 255, s & 255];
|
|
753
|
+
}, enumerable: false }, hexToAnsi256: { value: (u) => r.rgbToAnsi256(...r.hexToRgb(u)), enumerable: false }, ansi256ToAnsi: { value: (u) => {
|
|
754
|
+
if (u < 8) return 30 + u;
|
|
755
|
+
if (u < 16) return 90 + (u - 8);
|
|
756
|
+
let F, e, s;
|
|
757
|
+
if (u >= 232) F = ((u - 232) * 10 + 8) / 255, e = F, s = F;
|
|
758
|
+
else {
|
|
759
|
+
u -= 16;
|
|
760
|
+
const C = u % 36;
|
|
761
|
+
F = Math.floor(u / 36) / 5, e = Math.floor(C / 6) / 5, s = C % 6 / 5;
|
|
762
|
+
}
|
|
763
|
+
const i = Math.max(F, e, s) * 2;
|
|
764
|
+
if (i === 0) return 30;
|
|
765
|
+
let D = 30 + (Math.round(s) << 2 | Math.round(e) << 1 | Math.round(F));
|
|
766
|
+
return i === 2 && (D += 60), D;
|
|
767
|
+
}, enumerable: false }, rgbToAnsi: { value: (u, F, e) => r.ansi256ToAnsi(r.rgbToAnsi256(u, F, e)), enumerable: false }, hexToAnsi: { value: (u) => r.ansi256ToAnsi(r.hexToAnsi256(u)), enumerable: false } }), r;
|
|
768
|
+
}
|
|
769
|
+
var iD = sD();
|
|
770
|
+
var v = /* @__PURE__ */ new Set(["\x1B", "\x9B"]);
|
|
771
|
+
var CD = 39;
|
|
772
|
+
var w = "\x07";
|
|
773
|
+
var W = "[";
|
|
774
|
+
var rD = "]";
|
|
775
|
+
var R = "m";
|
|
776
|
+
var y = `${rD}8;;`;
|
|
777
|
+
var V = (t) => `${v.values().next().value}${W}${t}${R}`;
|
|
778
|
+
var z = (t) => `${v.values().next().value}${y}${t}${w}`;
|
|
779
|
+
var ED = (t) => t.split(" ").map((u) => A(u));
|
|
780
|
+
var _ = (t, u, F) => {
|
|
781
|
+
const e = [...u];
|
|
782
|
+
let s = false, i = false, D = A(T(t[t.length - 1]));
|
|
783
|
+
for (const [C, o] of e.entries()) {
|
|
784
|
+
const E = A(o);
|
|
785
|
+
if (D + E <= F ? t[t.length - 1] += o : (t.push(o), D = 0), v.has(o) && (s = true, i = e.slice(C + 1).join("").startsWith(y)), s) {
|
|
786
|
+
i ? o === w && (s = false, i = false) : o === R && (s = false);
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
D += E, D === F && C < e.length - 1 && (t.push(""), D = 0);
|
|
790
|
+
}
|
|
791
|
+
!D && t[t.length - 1].length > 0 && t.length > 1 && (t[t.length - 2] += t.pop());
|
|
792
|
+
};
|
|
793
|
+
var nD = (t) => {
|
|
794
|
+
const u = t.split(" ");
|
|
795
|
+
let F = u.length;
|
|
796
|
+
for (; F > 0 && !(A(u[F - 1]) > 0); ) F--;
|
|
797
|
+
return F === u.length ? t : u.slice(0, F).join(" ") + u.slice(F).join("");
|
|
798
|
+
};
|
|
799
|
+
var oD = (t, u, F = {}) => {
|
|
800
|
+
if (F.trim !== false && t.trim() === "") return "";
|
|
801
|
+
let e = "", s, i;
|
|
802
|
+
const D = ED(t);
|
|
803
|
+
let C = [""];
|
|
804
|
+
for (const [E, a] of t.split(" ").entries()) {
|
|
805
|
+
F.trim !== false && (C[C.length - 1] = C[C.length - 1].trimStart());
|
|
806
|
+
let n = A(C[C.length - 1]);
|
|
807
|
+
if (E !== 0 && (n >= u && (F.wordWrap === false || F.trim === false) && (C.push(""), n = 0), (n > 0 || F.trim === false) && (C[C.length - 1] += " ", n++)), F.hard && D[E] > u) {
|
|
808
|
+
const B = u - n, p2 = 1 + Math.floor((D[E] - B - 1) / u);
|
|
809
|
+
Math.floor((D[E] - 1) / u) < p2 && C.push(""), _(C, a, u);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (n + D[E] > u && n > 0 && D[E] > 0) {
|
|
813
|
+
if (F.wordWrap === false && n < u) {
|
|
814
|
+
_(C, a, u);
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
C.push("");
|
|
818
|
+
}
|
|
819
|
+
if (n + D[E] > u && F.wordWrap === false) {
|
|
820
|
+
_(C, a, u);
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
C[C.length - 1] += a;
|
|
824
|
+
}
|
|
825
|
+
F.trim !== false && (C = C.map((E) => nD(E)));
|
|
826
|
+
const o = [...C.join(`
|
|
827
|
+
`)];
|
|
828
|
+
for (const [E, a] of o.entries()) {
|
|
829
|
+
if (e += a, v.has(a)) {
|
|
830
|
+
const { groups: B } = new RegExp(`(?:\\${W}(?<code>\\d+)m|\\${y}(?<uri>.*)${w})`).exec(o.slice(E).join("")) || { groups: {} };
|
|
831
|
+
if (B.code !== void 0) {
|
|
832
|
+
const p2 = Number.parseFloat(B.code);
|
|
833
|
+
s = p2 === CD ? void 0 : p2;
|
|
834
|
+
} else B.uri !== void 0 && (i = B.uri.length === 0 ? void 0 : B.uri);
|
|
835
|
+
}
|
|
836
|
+
const n = iD.codes.get(Number(s));
|
|
837
|
+
o[E + 1] === `
|
|
838
|
+
` ? (i && (e += z("")), s && n && (e += V(n))) : a === `
|
|
839
|
+
` && (s && n && (e += V(s)), i && (e += z(i)));
|
|
840
|
+
}
|
|
841
|
+
return e;
|
|
842
|
+
};
|
|
843
|
+
function G(t, u, F) {
|
|
844
|
+
return String(t).normalize().replace(/\r\n/g, `
|
|
845
|
+
`).split(`
|
|
846
|
+
`).map((e) => oD(e, u, F)).join(`
|
|
847
|
+
`);
|
|
848
|
+
}
|
|
849
|
+
var aD = ["up", "down", "left", "right", "space", "enter", "cancel"];
|
|
850
|
+
var c = { actions: new Set(aD), aliases: /* @__PURE__ */ new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["", "cancel"], ["escape", "cancel"]]) };
|
|
851
|
+
function k(t, u) {
|
|
852
|
+
if (typeof t == "string") return c.aliases.get(t) === u;
|
|
853
|
+
for (const F of t) if (F !== void 0 && k(F, u)) return true;
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
function lD(t, u) {
|
|
857
|
+
if (t === u) return;
|
|
858
|
+
const F = t.split(`
|
|
859
|
+
`), e = u.split(`
|
|
860
|
+
`), s = [];
|
|
861
|
+
for (let i = 0; i < Math.max(F.length, e.length); i++) F[i] !== e[i] && s.push(i);
|
|
862
|
+
return s;
|
|
863
|
+
}
|
|
864
|
+
var xD = globalThis.process.platform.startsWith("win");
|
|
865
|
+
var S = /* @__PURE__ */ Symbol("clack:cancel");
|
|
866
|
+
function BD(t) {
|
|
867
|
+
return t === S;
|
|
868
|
+
}
|
|
869
|
+
function d(t, u) {
|
|
870
|
+
const F = t;
|
|
871
|
+
F.isTTY && F.setRawMode(u);
|
|
872
|
+
}
|
|
873
|
+
var AD = Object.defineProperty;
|
|
874
|
+
var pD = (t, u, F) => u in t ? AD(t, u, { enumerable: true, configurable: true, writable: true, value: F }) : t[u] = F;
|
|
875
|
+
var h = (t, u, F) => (pD(t, typeof u != "symbol" ? u + "" : u, F), F);
|
|
876
|
+
var x = class {
|
|
877
|
+
constructor(u, F = true) {
|
|
878
|
+
h(this, "input"), h(this, "output"), h(this, "_abortSignal"), h(this, "rl"), h(this, "opts"), h(this, "_render"), h(this, "_track", false), h(this, "_prevFrame", ""), h(this, "_subscribers", /* @__PURE__ */ new Map()), h(this, "_cursor", 0), h(this, "state", "initial"), h(this, "error", ""), h(this, "value");
|
|
879
|
+
const { input: e = $, output: s = j, render: i, signal: D, ...C } = u;
|
|
880
|
+
this.opts = C, this.onKeypress = this.onKeypress.bind(this), this.close = this.close.bind(this), this.render = this.render.bind(this), this._render = i.bind(this), this._track = F, this._abortSignal = D, this.input = e, this.output = s;
|
|
881
|
+
}
|
|
882
|
+
unsubscribe() {
|
|
883
|
+
this._subscribers.clear();
|
|
884
|
+
}
|
|
885
|
+
setSubscriber(u, F) {
|
|
886
|
+
const e = this._subscribers.get(u) ?? [];
|
|
887
|
+
e.push(F), this._subscribers.set(u, e);
|
|
888
|
+
}
|
|
889
|
+
on(u, F) {
|
|
890
|
+
this.setSubscriber(u, { cb: F });
|
|
891
|
+
}
|
|
892
|
+
once(u, F) {
|
|
893
|
+
this.setSubscriber(u, { cb: F, once: true });
|
|
894
|
+
}
|
|
895
|
+
emit(u, ...F) {
|
|
896
|
+
const e = this._subscribers.get(u) ?? [], s = [];
|
|
897
|
+
for (const i of e) i.cb(...F), i.once && s.push(() => e.splice(e.indexOf(i), 1));
|
|
898
|
+
for (const i of s) i();
|
|
899
|
+
}
|
|
900
|
+
prompt() {
|
|
901
|
+
return new Promise((u, F) => {
|
|
902
|
+
if (this._abortSignal) {
|
|
903
|
+
if (this._abortSignal.aborted) return this.state = "cancel", this.close(), u(S);
|
|
904
|
+
this._abortSignal.addEventListener("abort", () => {
|
|
905
|
+
this.state = "cancel", this.close();
|
|
906
|
+
}, { once: true });
|
|
907
|
+
}
|
|
908
|
+
const e = new U(0);
|
|
909
|
+
e._write = (s, i, D) => {
|
|
910
|
+
this._track && (this.value = this.rl?.line.replace(/\t/g, ""), this._cursor = this.rl?.cursor ?? 0, this.emit("value", this.value)), D();
|
|
911
|
+
}, this.input.pipe(e), this.rl = M.createInterface({ input: this.input, output: e, tabSize: 2, prompt: "", escapeCodeTimeout: 50 }), M.emitKeypressEvents(this.input, this.rl), this.rl.prompt(), this.opts.initialValue !== void 0 && this._track && this.rl.write(this.opts.initialValue), this.input.on("keypress", this.onKeypress), d(this.input, true), this.output.on("resize", this.render), this.render(), this.once("submit", () => {
|
|
912
|
+
this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), d(this.input, false), u(this.value);
|
|
913
|
+
}), this.once("cancel", () => {
|
|
914
|
+
this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), d(this.input, false), u(S);
|
|
915
|
+
});
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
onKeypress(u, F) {
|
|
919
|
+
if (this.state === "error" && (this.state = "active"), F?.name && (!this._track && c.aliases.has(F.name) && this.emit("cursor", c.aliases.get(F.name)), c.actions.has(F.name) && this.emit("cursor", F.name)), u && (u.toLowerCase() === "y" || u.toLowerCase() === "n") && this.emit("confirm", u.toLowerCase() === "y"), u === " " && this.opts.placeholder && (this.value || (this.rl?.write(this.opts.placeholder), this.emit("value", this.opts.placeholder))), u && this.emit("key", u.toLowerCase()), F?.name === "return") {
|
|
920
|
+
if (this.opts.validate) {
|
|
921
|
+
const e = this.opts.validate(this.value);
|
|
922
|
+
e && (this.error = e instanceof Error ? e.message : e, this.state = "error", this.rl?.write(this.value));
|
|
923
|
+
}
|
|
924
|
+
this.state !== "error" && (this.state = "submit");
|
|
925
|
+
}
|
|
926
|
+
k([u, F?.name, F?.sequence], "cancel") && (this.state = "cancel"), (this.state === "submit" || this.state === "cancel") && this.emit("finalize"), this.render(), (this.state === "submit" || this.state === "cancel") && this.close();
|
|
927
|
+
}
|
|
928
|
+
close() {
|
|
929
|
+
this.input.unpipe(), this.input.removeListener("keypress", this.onKeypress), this.output.write(`
|
|
930
|
+
`), d(this.input, false), this.rl?.close(), this.rl = void 0, this.emit(`${this.state}`, this.value), this.unsubscribe();
|
|
931
|
+
}
|
|
932
|
+
restoreCursor() {
|
|
933
|
+
const u = G(this._prevFrame, process.stdout.columns, { hard: true }).split(`
|
|
934
|
+
`).length - 1;
|
|
935
|
+
this.output.write(import_sisteransi.cursor.move(-999, u * -1));
|
|
936
|
+
}
|
|
937
|
+
render() {
|
|
938
|
+
const u = G(this._render(this) ?? "", process.stdout.columns, { hard: true });
|
|
939
|
+
if (u !== this._prevFrame) {
|
|
940
|
+
if (this.state === "initial") this.output.write(import_sisteransi.cursor.hide);
|
|
941
|
+
else {
|
|
942
|
+
const F = lD(this._prevFrame, u);
|
|
943
|
+
if (this.restoreCursor(), F && F?.length === 1) {
|
|
944
|
+
const e = F[0];
|
|
945
|
+
this.output.write(import_sisteransi.cursor.move(0, e)), this.output.write(import_sisteransi.erase.lines(1));
|
|
946
|
+
const s = u.split(`
|
|
947
|
+
`);
|
|
948
|
+
this.output.write(s[e]), this._prevFrame = u, this.output.write(import_sisteransi.cursor.move(0, s.length - e - 1));
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
if (F && F?.length > 1) {
|
|
952
|
+
const e = F[0];
|
|
953
|
+
this.output.write(import_sisteransi.cursor.move(0, e)), this.output.write(import_sisteransi.erase.down());
|
|
954
|
+
const s = u.split(`
|
|
955
|
+
`).slice(e);
|
|
956
|
+
this.output.write(s.join(`
|
|
957
|
+
`)), this._prevFrame = u;
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
this.output.write(import_sisteransi.erase.down());
|
|
961
|
+
}
|
|
962
|
+
this.output.write(u), this.state === "initial" && (this.state = "active"), this._prevFrame = u;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
// src/tree-select.ts
|
|
968
|
+
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
969
|
+
var SEARCH_THRESHOLD = 5;
|
|
970
|
+
var MAX_VISIBLE_ITEMS = 10;
|
|
971
|
+
var S_STEP_ACTIVE = import_picocolors.default.green("\u25C6");
|
|
972
|
+
var S_STEP_CANCEL = import_picocolors.default.red("\u25A0");
|
|
973
|
+
var S_STEP_SUBMIT = import_picocolors.default.green("\u25C7");
|
|
974
|
+
var S_BAR = import_picocolors.default.gray("\u2502");
|
|
975
|
+
var S_BAR_END = import_picocolors.default.gray("\u2514");
|
|
976
|
+
var S_CHECKBOX_ACTIVE = import_picocolors.default.cyan("\u25FB");
|
|
977
|
+
var S_CHECKBOX_SELECTED = import_picocolors.default.green("\u25FC");
|
|
978
|
+
var S_CHECKBOX_INACTIVE = import_picocolors.default.dim("\u25FB");
|
|
979
|
+
var MAX_HINT_LENGTH = 60;
|
|
980
|
+
function truncateHint(hint, maxLen) {
|
|
981
|
+
if (!hint) return "";
|
|
982
|
+
if (hint.length <= maxLen) return hint;
|
|
983
|
+
return hint.slice(0, maxLen - 1) + "\u2026";
|
|
984
|
+
}
|
|
985
|
+
function wrapText(text, maxWidth) {
|
|
986
|
+
if (!text) return [];
|
|
987
|
+
const words = text.split(/\s+/);
|
|
988
|
+
const lines = [];
|
|
989
|
+
let currentLine = "";
|
|
990
|
+
for (const word of words) {
|
|
991
|
+
if (currentLine.length === 0) {
|
|
992
|
+
currentLine = word;
|
|
993
|
+
} else if (currentLine.length + 1 + word.length <= maxWidth) {
|
|
994
|
+
currentLine += " " + word;
|
|
995
|
+
} else {
|
|
996
|
+
lines.push(currentLine);
|
|
997
|
+
currentLine = word;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
if (currentLine) {
|
|
1001
|
+
lines.push(currentLine);
|
|
1002
|
+
}
|
|
1003
|
+
return lines;
|
|
1004
|
+
}
|
|
1005
|
+
var DESCRIPTION_INDENT = " ";
|
|
1006
|
+
var DESCRIPTION_MAX_WIDTH = 60;
|
|
1007
|
+
function buildSearchableOptions(options) {
|
|
1008
|
+
return options.map((opt) => ({
|
|
1009
|
+
option: opt,
|
|
1010
|
+
value: opt.value,
|
|
1011
|
+
searchableText: [opt.label, opt.hint || "", opt.value.description || ""].join("|").toLowerCase()
|
|
1012
|
+
}));
|
|
1013
|
+
}
|
|
1014
|
+
function filterOptions(options, searchTerm) {
|
|
1015
|
+
if (!searchTerm) return options;
|
|
1016
|
+
const term = searchTerm.toLowerCase();
|
|
1017
|
+
return options.filter((opt) => opt.searchableText.includes(term));
|
|
1018
|
+
}
|
|
1019
|
+
function highlightMatch(text, searchTerm) {
|
|
1020
|
+
if (!searchTerm) return text;
|
|
1021
|
+
const lowerText = text.toLowerCase();
|
|
1022
|
+
const lowerTerm = searchTerm.toLowerCase();
|
|
1023
|
+
const index = lowerText.indexOf(lowerTerm);
|
|
1024
|
+
if (index === -1) return text;
|
|
1025
|
+
const before = text.slice(0, index);
|
|
1026
|
+
const match = text.slice(index, index + searchTerm.length);
|
|
1027
|
+
const after = text.slice(index + searchTerm.length);
|
|
1028
|
+
return `${before}${import_picocolors.default.cyan(match)}${after}`;
|
|
1029
|
+
}
|
|
1030
|
+
function symbol(state) {
|
|
1031
|
+
switch (state) {
|
|
1032
|
+
case "active":
|
|
1033
|
+
return S_STEP_ACTIVE;
|
|
1034
|
+
case "cancel":
|
|
1035
|
+
return S_STEP_CANCEL;
|
|
1036
|
+
case "submit":
|
|
1037
|
+
return S_STEP_SUBMIT;
|
|
1038
|
+
default:
|
|
1039
|
+
return import_picocolors.default.cyan("\u25C6");
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
var SearchableMultiSelectPrompt = class extends x {
|
|
1043
|
+
searchTerm = "";
|
|
1044
|
+
listCursor = 0;
|
|
1045
|
+
selectedValues;
|
|
1046
|
+
allOptions;
|
|
1047
|
+
filteredOptions;
|
|
1048
|
+
scrollOffset = 0;
|
|
1049
|
+
maxItems;
|
|
1050
|
+
promptMessage;
|
|
1051
|
+
constructor(opts) {
|
|
1052
|
+
super(
|
|
1053
|
+
{
|
|
1054
|
+
render: () => this.renderPrompt()
|
|
1055
|
+
},
|
|
1056
|
+
false
|
|
1057
|
+
);
|
|
1058
|
+
this.allOptions = opts.options;
|
|
1059
|
+
this.filteredOptions = [...opts.options];
|
|
1060
|
+
this.selectedValues = new Set(opts.initialValues ?? []);
|
|
1061
|
+
this.maxItems = opts.maxItems ?? MAX_VISIBLE_ITEMS;
|
|
1062
|
+
this.promptMessage = opts.message;
|
|
1063
|
+
this.on("key", (key) => this.handleKey(key ?? ""));
|
|
1064
|
+
this.on("cursor", (action) => this.handleCursor(action ?? "up"));
|
|
1065
|
+
}
|
|
1066
|
+
handleKey(key) {
|
|
1067
|
+
if (key === "\x7F" || key === "\b") {
|
|
1068
|
+
if (this.searchTerm.length > 0) {
|
|
1069
|
+
this.searchTerm = this.searchTerm.slice(0, -1);
|
|
1070
|
+
this.updateFilter();
|
|
1071
|
+
}
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
if (key.length === 1 && /[a-z0-9\-_./\s]/i.test(key)) {
|
|
1075
|
+
this.searchTerm += key;
|
|
1076
|
+
this.updateFilter();
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
handleCursor(action) {
|
|
1080
|
+
switch (action) {
|
|
1081
|
+
case "up":
|
|
1082
|
+
this.listCursor = Math.max(0, this.listCursor - 1);
|
|
1083
|
+
this.adjustScroll();
|
|
1084
|
+
break;
|
|
1085
|
+
case "down":
|
|
1086
|
+
this.listCursor = Math.min(
|
|
1087
|
+
this.filteredOptions.length - 1,
|
|
1088
|
+
this.listCursor + 1
|
|
1089
|
+
);
|
|
1090
|
+
this.adjustScroll();
|
|
1091
|
+
break;
|
|
1092
|
+
case "space":
|
|
1093
|
+
this.toggleSelection();
|
|
1094
|
+
break;
|
|
1095
|
+
case "cancel":
|
|
1096
|
+
if (this.searchTerm) {
|
|
1097
|
+
this.searchTerm = "";
|
|
1098
|
+
this.updateFilter();
|
|
1099
|
+
}
|
|
1100
|
+
break;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
adjustScroll() {
|
|
1104
|
+
if (this.listCursor < this.scrollOffset) {
|
|
1105
|
+
this.scrollOffset = this.listCursor;
|
|
1106
|
+
} else if (this.listCursor >= this.scrollOffset + this.maxItems) {
|
|
1107
|
+
this.scrollOffset = this.listCursor - this.maxItems + 1;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
updateFilter() {
|
|
1111
|
+
this.filteredOptions = filterOptions(this.allOptions, this.searchTerm);
|
|
1112
|
+
this.listCursor = Math.min(
|
|
1113
|
+
this.listCursor,
|
|
1114
|
+
Math.max(0, this.filteredOptions.length - 1)
|
|
1115
|
+
);
|
|
1116
|
+
this.scrollOffset = 0;
|
|
1117
|
+
this.adjustScroll();
|
|
1118
|
+
}
|
|
1119
|
+
toggleSelection() {
|
|
1120
|
+
const current = this.filteredOptions[this.listCursor];
|
|
1121
|
+
if (!current) return;
|
|
1122
|
+
if (this.selectedValues.has(current.value)) {
|
|
1123
|
+
this.selectedValues.delete(current.value);
|
|
1124
|
+
} else {
|
|
1125
|
+
this.selectedValues.add(current.value);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
renderPrompt() {
|
|
1129
|
+
const lines = [];
|
|
1130
|
+
const total = this.allOptions.length;
|
|
1131
|
+
const filtered = this.filteredOptions.length;
|
|
1132
|
+
lines.push(`${import_picocolors.default.gray(S_BAR)}`);
|
|
1133
|
+
lines.push(`${symbol(this.state)} ${this.promptMessage}`);
|
|
1134
|
+
if (this.state === "submit") {
|
|
1135
|
+
const selectedLabels = this.allOptions.filter((opt) => this.selectedValues.has(opt.value)).map((opt) => opt.option.label);
|
|
1136
|
+
lines.push(
|
|
1137
|
+
`${import_picocolors.default.gray(S_BAR)} ${import_picocolors.default.dim(selectedLabels.join(", ") || "none")}`
|
|
1138
|
+
);
|
|
1139
|
+
return lines.join("\n");
|
|
1140
|
+
}
|
|
1141
|
+
if (this.state === "cancel") {
|
|
1142
|
+
const selectedLabels = this.allOptions.filter((opt) => this.selectedValues.has(opt.value)).map((opt) => import_picocolors.default.strikethrough(import_picocolors.default.dim(opt.option.label)));
|
|
1143
|
+
if (selectedLabels.length > 0) {
|
|
1144
|
+
lines.push(`${import_picocolors.default.gray(S_BAR)} ${selectedLabels.join(", ")}`);
|
|
1145
|
+
}
|
|
1146
|
+
lines.push(`${import_picocolors.default.gray(S_BAR)}`);
|
|
1147
|
+
return lines.join("\n");
|
|
1148
|
+
}
|
|
1149
|
+
const selectedCount = this.selectedValues.size;
|
|
1150
|
+
const countText = this.searchTerm || filtered !== total ? `(${filtered} of ${total} skills)` : `(${total} skills)`;
|
|
1151
|
+
const selectedText = selectedCount > 0 ? import_picocolors.default.green(` \u2022 ${selectedCount} selected`) : "";
|
|
1152
|
+
const cursor = this.state === "active" ? import_picocolors.default.inverse(" ") : "_";
|
|
1153
|
+
lines.push(
|
|
1154
|
+
`${import_picocolors.default.cyan(S_BAR)} Search: ${this.searchTerm}${cursor} ${import_picocolors.default.dim(countText)}${selectedText}`
|
|
1155
|
+
);
|
|
1156
|
+
lines.push(
|
|
1157
|
+
`${import_picocolors.default.cyan(S_BAR)} ${import_picocolors.default.dim("\u2191/\u2193 navigate \u2022 space select \u2022 enter confirm")}`
|
|
1158
|
+
);
|
|
1159
|
+
lines.push(`${import_picocolors.default.cyan(S_BAR)} ${import_picocolors.default.dim("\u2500".repeat(40))}`);
|
|
1160
|
+
if (this.filteredOptions.length === 0) {
|
|
1161
|
+
lines.push(
|
|
1162
|
+
`${import_picocolors.default.cyan(S_BAR)} ${import_picocolors.default.dim(`No skills match "${this.searchTerm}"`)}`
|
|
1163
|
+
);
|
|
1164
|
+
} else {
|
|
1165
|
+
const aboveCount = this.scrollOffset;
|
|
1166
|
+
const belowCount = Math.max(
|
|
1167
|
+
0,
|
|
1168
|
+
this.filteredOptions.length - this.scrollOffset - this.maxItems
|
|
1169
|
+
);
|
|
1170
|
+
if (aboveCount > 0) {
|
|
1171
|
+
lines.push(
|
|
1172
|
+
`${import_picocolors.default.cyan(S_BAR)} ${import_picocolors.default.dim(`\u2191 ${aboveCount} more above`)}`
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
const visibleOptions = this.filteredOptions.slice(
|
|
1176
|
+
this.scrollOffset,
|
|
1177
|
+
this.scrollOffset + this.maxItems
|
|
1178
|
+
);
|
|
1179
|
+
for (let i = 0; i < visibleOptions.length; i++) {
|
|
1180
|
+
const opt = visibleOptions[i];
|
|
1181
|
+
const globalIndex = this.scrollOffset + i;
|
|
1182
|
+
const isActive = globalIndex === this.listCursor;
|
|
1183
|
+
const isSelected = this.selectedValues.has(opt.value);
|
|
1184
|
+
let checkbox;
|
|
1185
|
+
if (isActive && isSelected) {
|
|
1186
|
+
checkbox = S_CHECKBOX_SELECTED;
|
|
1187
|
+
} else if (isSelected) {
|
|
1188
|
+
checkbox = S_CHECKBOX_SELECTED;
|
|
1189
|
+
} else if (isActive) {
|
|
1190
|
+
checkbox = S_CHECKBOX_ACTIVE;
|
|
1191
|
+
} else {
|
|
1192
|
+
checkbox = S_CHECKBOX_INACTIVE;
|
|
1193
|
+
}
|
|
1194
|
+
const label = this.searchTerm ? highlightMatch(opt.option.label, this.searchTerm) : opt.option.label;
|
|
1195
|
+
const fullHint = opt.option.hint || "";
|
|
1196
|
+
const needsTruncation = fullHint.length > MAX_HINT_LENGTH;
|
|
1197
|
+
const displayHint = needsTruncation ? truncateHint(fullHint, MAX_HINT_LENGTH) : fullHint;
|
|
1198
|
+
const hint = displayHint ? import_picocolors.default.dim(` (${displayHint})`) : "";
|
|
1199
|
+
const line = isActive ? `${checkbox} ${label}${hint}` : `${checkbox} ${import_picocolors.default.dim(opt.option.label)}${import_picocolors.default.dim(hint)}`;
|
|
1200
|
+
lines.push(`${import_picocolors.default.cyan(S_BAR)} ${line}`);
|
|
1201
|
+
if (isActive && needsTruncation) {
|
|
1202
|
+
const wrappedLines = wrapText(fullHint, DESCRIPTION_MAX_WIDTH);
|
|
1203
|
+
for (const descLine of wrappedLines) {
|
|
1204
|
+
lines.push(
|
|
1205
|
+
`${import_picocolors.default.cyan(S_BAR)} ${DESCRIPTION_INDENT}${import_picocolors.default.dim(descLine)}`
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
if (belowCount > 0) {
|
|
1211
|
+
lines.push(
|
|
1212
|
+
`${import_picocolors.default.cyan(S_BAR)} ${import_picocolors.default.dim(`\u2193 ${belowCount} more below`)}`
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
lines.push(`${import_picocolors.default.cyan(S_BAR_END)}`);
|
|
1217
|
+
return lines.join("\n");
|
|
1218
|
+
}
|
|
1219
|
+
async run() {
|
|
1220
|
+
const result = await this.prompt();
|
|
1221
|
+
if (BD(result)) {
|
|
1222
|
+
return result;
|
|
1223
|
+
}
|
|
1224
|
+
return Array.from(this.selectedValues);
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1227
|
+
function buildGroupedSearchableOptions(groups) {
|
|
1228
|
+
return Object.entries(groups).map(([groupName, options]) => ({
|
|
1229
|
+
groupName,
|
|
1230
|
+
searchableText: groupName.toLowerCase(),
|
|
1231
|
+
options: options.map((opt) => ({
|
|
1232
|
+
option: opt,
|
|
1233
|
+
value: opt.value,
|
|
1234
|
+
group: groupName,
|
|
1235
|
+
searchableText: [
|
|
1236
|
+
opt.label,
|
|
1237
|
+
groupName,
|
|
1238
|
+
opt.hint || "",
|
|
1239
|
+
opt.value.description || ""
|
|
1240
|
+
].join("|").toLowerCase()
|
|
1241
|
+
}))
|
|
1242
|
+
}));
|
|
1243
|
+
}
|
|
1244
|
+
var SearchableGroupMultiSelectPrompt = class extends x {
|
|
1245
|
+
searchTerm = "";
|
|
1246
|
+
listCursor = 0;
|
|
1247
|
+
selectedValues;
|
|
1248
|
+
groupedOptions;
|
|
1249
|
+
flatItems = [];
|
|
1250
|
+
scrollOffset = 0;
|
|
1251
|
+
maxItems;
|
|
1252
|
+
promptMessage;
|
|
1253
|
+
constructor(opts) {
|
|
1254
|
+
super(
|
|
1255
|
+
{
|
|
1256
|
+
render: () => this.renderPrompt()
|
|
1257
|
+
},
|
|
1258
|
+
false
|
|
1259
|
+
);
|
|
1260
|
+
this.groupedOptions = buildGroupedSearchableOptions(
|
|
1261
|
+
opts.groups
|
|
1262
|
+
);
|
|
1263
|
+
this.selectedValues = new Set(opts.initialValues ?? []);
|
|
1264
|
+
this.maxItems = opts.maxItems ?? MAX_VISIBLE_ITEMS;
|
|
1265
|
+
this.promptMessage = opts.message;
|
|
1266
|
+
this.rebuildFlatItems();
|
|
1267
|
+
this.on("key", (key) => this.handleKey(key ?? ""));
|
|
1268
|
+
this.on("cursor", (action) => this.handleCursor(action ?? "up"));
|
|
1269
|
+
}
|
|
1270
|
+
rebuildFlatItems() {
|
|
1271
|
+
this.flatItems = [];
|
|
1272
|
+
const term = this.searchTerm.toLowerCase();
|
|
1273
|
+
for (const group of this.groupedOptions) {
|
|
1274
|
+
const groupMatches = !term || group.searchableText.includes(term);
|
|
1275
|
+
const matchingOptions = groupMatches ? group.options : group.options.filter((opt) => opt.searchableText.includes(term));
|
|
1276
|
+
if (matchingOptions.length > 0) {
|
|
1277
|
+
this.flatItems.push({
|
|
1278
|
+
type: "group",
|
|
1279
|
+
name: group.groupName,
|
|
1280
|
+
searchable: group.searchableText
|
|
1281
|
+
});
|
|
1282
|
+
for (const opt of matchingOptions) {
|
|
1283
|
+
this.flatItems.push({
|
|
1284
|
+
type: "option",
|
|
1285
|
+
option: opt,
|
|
1286
|
+
groupName: group.groupName
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
handleKey(key) {
|
|
1293
|
+
if (key === "\x7F" || key === "\b") {
|
|
1294
|
+
if (this.searchTerm.length > 0) {
|
|
1295
|
+
this.searchTerm = this.searchTerm.slice(0, -1);
|
|
1296
|
+
this.updateFilter();
|
|
1297
|
+
}
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
if (key.length === 1 && /[a-z0-9\-_./\s]/i.test(key)) {
|
|
1301
|
+
this.searchTerm += key;
|
|
1302
|
+
this.updateFilter();
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
handleCursor(action) {
|
|
1306
|
+
switch (action) {
|
|
1307
|
+
case "up":
|
|
1308
|
+
this.listCursor = Math.max(0, this.listCursor - 1);
|
|
1309
|
+
this.adjustScroll();
|
|
1310
|
+
break;
|
|
1311
|
+
case "down":
|
|
1312
|
+
this.listCursor = Math.min(
|
|
1313
|
+
this.flatItems.length - 1,
|
|
1314
|
+
this.listCursor + 1
|
|
1315
|
+
);
|
|
1316
|
+
this.adjustScroll();
|
|
1317
|
+
break;
|
|
1318
|
+
case "space":
|
|
1319
|
+
this.toggleSelection();
|
|
1320
|
+
break;
|
|
1321
|
+
case "cancel":
|
|
1322
|
+
if (this.searchTerm) {
|
|
1323
|
+
this.searchTerm = "";
|
|
1324
|
+
this.updateFilter();
|
|
1325
|
+
}
|
|
1326
|
+
break;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
adjustScroll() {
|
|
1330
|
+
if (this.listCursor < this.scrollOffset) {
|
|
1331
|
+
this.scrollOffset = this.listCursor;
|
|
1332
|
+
} else if (this.listCursor >= this.scrollOffset + this.maxItems) {
|
|
1333
|
+
this.scrollOffset = this.listCursor - this.maxItems + 1;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
updateFilter() {
|
|
1337
|
+
this.rebuildFlatItems();
|
|
1338
|
+
this.listCursor = Math.min(
|
|
1339
|
+
this.listCursor,
|
|
1340
|
+
Math.max(0, this.flatItems.length - 1)
|
|
1341
|
+
);
|
|
1342
|
+
this.scrollOffset = 0;
|
|
1343
|
+
this.adjustScroll();
|
|
1344
|
+
}
|
|
1345
|
+
getGroupOptions(groupName) {
|
|
1346
|
+
const group = this.groupedOptions.find((g) => g.groupName === groupName);
|
|
1347
|
+
return group?.options ?? [];
|
|
1348
|
+
}
|
|
1349
|
+
isGroupSelected(groupName) {
|
|
1350
|
+
const options = this.getGroupOptions(groupName);
|
|
1351
|
+
return options.length > 0 && options.every((opt) => this.selectedValues.has(opt.value));
|
|
1352
|
+
}
|
|
1353
|
+
toggleSelection() {
|
|
1354
|
+
const current = this.flatItems[this.listCursor];
|
|
1355
|
+
if (!current) return;
|
|
1356
|
+
if (current.type === "group") {
|
|
1357
|
+
const options = this.getGroupOptions(current.name);
|
|
1358
|
+
const allSelected = this.isGroupSelected(current.name);
|
|
1359
|
+
for (const opt of options) {
|
|
1360
|
+
if (allSelected) {
|
|
1361
|
+
this.selectedValues.delete(opt.value);
|
|
1362
|
+
} else {
|
|
1363
|
+
this.selectedValues.add(opt.value);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
} else {
|
|
1367
|
+
const val = current.option.value;
|
|
1368
|
+
if (this.selectedValues.has(val)) {
|
|
1369
|
+
this.selectedValues.delete(val);
|
|
1370
|
+
} else {
|
|
1371
|
+
this.selectedValues.add(val);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
getTotalOptionCount() {
|
|
1376
|
+
return this.groupedOptions.reduce((sum, g) => sum + g.options.length, 0);
|
|
1377
|
+
}
|
|
1378
|
+
getFilteredOptionCount() {
|
|
1379
|
+
return this.flatItems.filter((item) => item.type === "option").length;
|
|
1380
|
+
}
|
|
1381
|
+
renderPrompt() {
|
|
1382
|
+
const lines = [];
|
|
1383
|
+
const total = this.getTotalOptionCount();
|
|
1384
|
+
const filtered = this.getFilteredOptionCount();
|
|
1385
|
+
lines.push(`${import_picocolors.default.gray(S_BAR)}`);
|
|
1386
|
+
lines.push(`${symbol(this.state)} ${this.promptMessage}`);
|
|
1387
|
+
if (this.state === "submit") {
|
|
1388
|
+
const selectedLabels = this.groupedOptions.flatMap((g) => g.options).filter((opt) => this.selectedValues.has(opt.value)).map((opt) => opt.option.label);
|
|
1389
|
+
lines.push(
|
|
1390
|
+
`${import_picocolors.default.gray(S_BAR)} ${import_picocolors.default.dim(selectedLabels.join(", ") || "none")}`
|
|
1391
|
+
);
|
|
1392
|
+
return lines.join("\n");
|
|
1393
|
+
}
|
|
1394
|
+
if (this.state === "cancel") {
|
|
1395
|
+
const selectedLabels = this.groupedOptions.flatMap((g) => g.options).filter((opt) => this.selectedValues.has(opt.value)).map((opt) => import_picocolors.default.strikethrough(import_picocolors.default.dim(opt.option.label)));
|
|
1396
|
+
if (selectedLabels.length > 0) {
|
|
1397
|
+
lines.push(`${import_picocolors.default.gray(S_BAR)} ${selectedLabels.join(", ")}`);
|
|
1398
|
+
}
|
|
1399
|
+
lines.push(`${import_picocolors.default.gray(S_BAR)}`);
|
|
1400
|
+
return lines.join("\n");
|
|
1401
|
+
}
|
|
1402
|
+
const selectedCount = this.selectedValues.size;
|
|
1403
|
+
const countText = this.searchTerm || filtered !== total ? `(${filtered} of ${total} skills)` : `(${total} skills)`;
|
|
1404
|
+
const selectedText = selectedCount > 0 ? import_picocolors.default.green(` \u2022 ${selectedCount} selected`) : "";
|
|
1405
|
+
const cursor = this.state === "active" ? import_picocolors.default.inverse(" ") : "_";
|
|
1406
|
+
lines.push(
|
|
1407
|
+
`${import_picocolors.default.cyan(S_BAR)} Search: ${this.searchTerm}${cursor} ${import_picocolors.default.dim(countText)}${selectedText}`
|
|
1408
|
+
);
|
|
1409
|
+
lines.push(
|
|
1410
|
+
`${import_picocolors.default.cyan(S_BAR)} ${import_picocolors.default.dim("\u2191/\u2193 navigate \u2022 space select \u2022 enter confirm")}`
|
|
1411
|
+
);
|
|
1412
|
+
lines.push(`${import_picocolors.default.cyan(S_BAR)} ${import_picocolors.default.dim("\u2500".repeat(40))}`);
|
|
1413
|
+
if (this.flatItems.length === 0) {
|
|
1414
|
+
lines.push(
|
|
1415
|
+
`${import_picocolors.default.cyan(S_BAR)} ${import_picocolors.default.dim(`No skills match "${this.searchTerm}"`)}`
|
|
1416
|
+
);
|
|
1417
|
+
} else {
|
|
1418
|
+
const aboveCount = this.scrollOffset;
|
|
1419
|
+
const belowCount = Math.max(
|
|
1420
|
+
0,
|
|
1421
|
+
this.flatItems.length - this.scrollOffset - this.maxItems
|
|
1422
|
+
);
|
|
1423
|
+
if (aboveCount > 0) {
|
|
1424
|
+
lines.push(
|
|
1425
|
+
`${import_picocolors.default.cyan(S_BAR)} ${import_picocolors.default.dim(`\u2191 ${aboveCount} more above`)}`
|
|
1426
|
+
);
|
|
1427
|
+
}
|
|
1428
|
+
const visibleItems = this.flatItems.slice(
|
|
1429
|
+
this.scrollOffset,
|
|
1430
|
+
this.scrollOffset + this.maxItems
|
|
1431
|
+
);
|
|
1432
|
+
for (let i = 0; i < visibleItems.length; i++) {
|
|
1433
|
+
const item = visibleItems[i];
|
|
1434
|
+
const globalIndex = this.scrollOffset + i;
|
|
1435
|
+
const isActive = globalIndex === this.listCursor;
|
|
1436
|
+
if (item.type === "group") {
|
|
1437
|
+
const groupOptions = this.getGroupOptions(item.name);
|
|
1438
|
+
const groupSelectedCount = groupOptions.filter(
|
|
1439
|
+
(opt) => this.selectedValues.has(opt.value)
|
|
1440
|
+
).length;
|
|
1441
|
+
const groupSelected = this.isGroupSelected(item.name);
|
|
1442
|
+
const checkbox = groupSelected ? S_CHECKBOX_SELECTED : isActive ? S_CHECKBOX_ACTIVE : S_CHECKBOX_INACTIVE;
|
|
1443
|
+
const label = this.searchTerm ? highlightMatch(item.name, this.searchTerm) : item.name;
|
|
1444
|
+
const countHint = groupSelectedCount > 0 ? import_picocolors.default.dim(` (${groupSelectedCount}/${groupOptions.length})`) : import_picocolors.default.dim(` (${groupOptions.length})`);
|
|
1445
|
+
const line = isActive ? `${checkbox} ${import_picocolors.default.bold(label)}${countHint}` : `${checkbox} ${import_picocolors.default.dim(item.name)}${countHint}`;
|
|
1446
|
+
lines.push(`${import_picocolors.default.cyan(S_BAR)} ${line}`);
|
|
1447
|
+
} else {
|
|
1448
|
+
const isSelected = this.selectedValues.has(item.option.value);
|
|
1449
|
+
let checkbox;
|
|
1450
|
+
if (isSelected) {
|
|
1451
|
+
checkbox = S_CHECKBOX_SELECTED;
|
|
1452
|
+
} else if (isActive) {
|
|
1453
|
+
checkbox = S_CHECKBOX_ACTIVE;
|
|
1454
|
+
} else {
|
|
1455
|
+
checkbox = S_CHECKBOX_INACTIVE;
|
|
1456
|
+
}
|
|
1457
|
+
const label = this.searchTerm ? highlightMatch(item.option.option.label, this.searchTerm) : item.option.option.label;
|
|
1458
|
+
const fullHint = item.option.option.hint || "";
|
|
1459
|
+
const needsTruncation = fullHint.length > MAX_HINT_LENGTH;
|
|
1460
|
+
const displayHint = needsTruncation ? truncateHint(fullHint, MAX_HINT_LENGTH) : fullHint;
|
|
1461
|
+
const hint = displayHint ? import_picocolors.default.dim(` (${displayHint})`) : "";
|
|
1462
|
+
const isLastInGroup = i + 1 >= visibleItems.length || visibleItems[i + 1].type === "group";
|
|
1463
|
+
const indent = isLastInGroup ? `${import_picocolors.default.gray("\u2514")} ` : `${import_picocolors.default.gray("\u2502")} `;
|
|
1464
|
+
const line = isActive ? `${indent}${checkbox} ${label}${hint}` : `${indent}${checkbox} ${import_picocolors.default.dim(item.option.option.label)}${import_picocolors.default.dim(hint)}`;
|
|
1465
|
+
lines.push(`${import_picocolors.default.cyan(S_BAR)} ${line}`);
|
|
1466
|
+
if (isActive && needsTruncation) {
|
|
1467
|
+
const descIndent = isLastInGroup ? " " : `${import_picocolors.default.gray("\u2502")} `;
|
|
1468
|
+
const wrappedLines = wrapText(fullHint, DESCRIPTION_MAX_WIDTH);
|
|
1469
|
+
for (const descLine of wrappedLines) {
|
|
1470
|
+
lines.push(
|
|
1471
|
+
`${import_picocolors.default.cyan(S_BAR)} ${descIndent}${DESCRIPTION_INDENT}${import_picocolors.default.dim(descLine)}`
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
if (belowCount > 0) {
|
|
1478
|
+
lines.push(
|
|
1479
|
+
`${import_picocolors.default.cyan(S_BAR)} ${import_picocolors.default.dim(`\u2193 ${belowCount} more below`)}`
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
lines.push(`${import_picocolors.default.cyan(S_BAR_END)}`);
|
|
1484
|
+
return lines.join("\n");
|
|
1485
|
+
}
|
|
1486
|
+
async run() {
|
|
1487
|
+
const result = await this.prompt();
|
|
1488
|
+
if (BD(result)) {
|
|
1489
|
+
return result;
|
|
1490
|
+
}
|
|
1491
|
+
return Array.from(this.selectedValues);
|
|
1492
|
+
}
|
|
1493
|
+
};
|
|
1494
|
+
async function searchableMultiselect(options) {
|
|
1495
|
+
const searchableOptions = buildSearchableOptions(options);
|
|
1496
|
+
const prompt = new SearchableMultiSelectPrompt({
|
|
1497
|
+
message: "Select skills to install:",
|
|
1498
|
+
options: searchableOptions
|
|
1499
|
+
});
|
|
1500
|
+
const result = await prompt.run();
|
|
1501
|
+
if (BD(result)) {
|
|
1502
|
+
throw new Error("Selection cancelled");
|
|
1503
|
+
}
|
|
1504
|
+
return result;
|
|
1505
|
+
}
|
|
1506
|
+
async function searchableGroupMultiselect(groups) {
|
|
1507
|
+
const prompt = new SearchableGroupMultiSelectPrompt({
|
|
1508
|
+
message: "Select skills to install:",
|
|
1509
|
+
groups
|
|
1510
|
+
});
|
|
1511
|
+
const result = await prompt.run();
|
|
1512
|
+
if (BD(result)) {
|
|
1513
|
+
throw new Error("Selection cancelled");
|
|
1514
|
+
}
|
|
1515
|
+
return result;
|
|
1516
|
+
}
|
|
1517
|
+
function countTotalOptions(uncategorized, groups) {
|
|
1518
|
+
const groupCount = Object.values(groups).reduce(
|
|
1519
|
+
(sum, opts) => sum + opts.length,
|
|
1520
|
+
0
|
|
1521
|
+
);
|
|
1522
|
+
return uncategorized.length + groupCount;
|
|
1523
|
+
}
|
|
1524
|
+
function categorizeNodes(nodes) {
|
|
1525
|
+
const uncategorized = [];
|
|
1526
|
+
const groups = {};
|
|
1527
|
+
for (const node of nodes) {
|
|
1528
|
+
if (node.skill) {
|
|
1529
|
+
uncategorized.push({
|
|
1530
|
+
value: node.skill,
|
|
1531
|
+
label: node.label,
|
|
1532
|
+
hint: node.hint
|
|
1533
|
+
});
|
|
1534
|
+
} else if (node.children) {
|
|
1535
|
+
const groupName = node.label;
|
|
1536
|
+
groups[groupName] = [];
|
|
1537
|
+
addChildrenToGroup(node.children, groups[groupName], groups);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
return { uncategorized, groups };
|
|
1541
|
+
}
|
|
1542
|
+
function addChildrenToGroup(children, currentGroup, allGroups) {
|
|
1543
|
+
for (const child of children) {
|
|
1544
|
+
if (child.skill) {
|
|
1545
|
+
currentGroup.push({
|
|
1546
|
+
value: child.skill,
|
|
1547
|
+
label: child.label,
|
|
1548
|
+
hint: child.hint
|
|
1549
|
+
});
|
|
1550
|
+
} else if (child.children) {
|
|
1551
|
+
const nestedGroupName = child.label;
|
|
1552
|
+
if (!allGroups[nestedGroupName]) {
|
|
1553
|
+
allGroups[nestedGroupName] = [];
|
|
1554
|
+
}
|
|
1555
|
+
addChildrenToGroup(child.children, allGroups[nestedGroupName], allGroups);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
async function treeSelect(nodes) {
|
|
1560
|
+
if (!process.stdin.isTTY) {
|
|
1561
|
+
throw new Error(
|
|
1562
|
+
"Interactive selection requires a TTY. Use -y flag for non-interactive mode."
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1565
|
+
const { uncategorized, groups } = categorizeNodes(nodes);
|
|
1566
|
+
const hasGroups = Object.keys(groups).length > 0;
|
|
1567
|
+
const hasUncategorized = uncategorized.length > 0;
|
|
1568
|
+
const totalOptions = countTotalOptions(uncategorized, groups);
|
|
1569
|
+
const useSearch = totalOptions > SEARCH_THRESHOLD;
|
|
1570
|
+
if (hasUncategorized && !hasGroups) {
|
|
1571
|
+
if (useSearch) {
|
|
1572
|
+
return searchableMultiselect(uncategorized);
|
|
1573
|
+
}
|
|
1574
|
+
const selected = await p.multiselect({
|
|
1575
|
+
message: "Select skills to install:",
|
|
1576
|
+
options: uncategorized,
|
|
1577
|
+
required: false
|
|
1578
|
+
});
|
|
1579
|
+
if (p.isCancel(selected)) {
|
|
1580
|
+
throw new Error("Selection cancelled");
|
|
1581
|
+
}
|
|
1582
|
+
return selected;
|
|
1583
|
+
}
|
|
1584
|
+
if (hasGroups && !hasUncategorized) {
|
|
1585
|
+
if (useSearch) {
|
|
1586
|
+
return searchableGroupMultiselect(groups);
|
|
1587
|
+
}
|
|
1588
|
+
const selected = await p.groupMultiselect({
|
|
1589
|
+
message: "Select skills to install:",
|
|
1590
|
+
options: groups,
|
|
1591
|
+
required: false
|
|
1592
|
+
});
|
|
1593
|
+
if (p.isCancel(selected)) {
|
|
1594
|
+
throw new Error("Selection cancelled");
|
|
1595
|
+
}
|
|
1596
|
+
return selected;
|
|
1597
|
+
}
|
|
1598
|
+
if (hasGroups && hasUncategorized) {
|
|
1599
|
+
const mixedGroups = { ...groups };
|
|
1600
|
+
mixedGroups["Other"] = uncategorized;
|
|
1601
|
+
if (useSearch) {
|
|
1602
|
+
return searchableGroupMultiselect(mixedGroups);
|
|
1603
|
+
}
|
|
1604
|
+
const selected = await p.groupMultiselect({
|
|
1605
|
+
message: "Select skills to install:",
|
|
1606
|
+
options: mixedGroups,
|
|
1607
|
+
required: false
|
|
1608
|
+
});
|
|
1609
|
+
if (p.isCancel(selected)) {
|
|
1610
|
+
throw new Error("Selection cancelled");
|
|
1611
|
+
}
|
|
1612
|
+
return selected;
|
|
1613
|
+
}
|
|
1614
|
+
p.log.warn("No skills available to select.");
|
|
1615
|
+
return [];
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
// src/dependencies.ts
|
|
1619
|
+
import * as fs6 from "fs";
|
|
1620
|
+
import * as path6 from "path";
|
|
1621
|
+
import { spawn } from "child_process";
|
|
1622
|
+
var SUPPORTED_PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
|
|
1623
|
+
var LOCKFILE_TO_PM = {
|
|
1624
|
+
"pnpm-lock.yaml": "pnpm",
|
|
1625
|
+
"package-lock.json": "npm",
|
|
1626
|
+
"yarn.lock": "yarn",
|
|
1627
|
+
"bun.lock": "bun",
|
|
1628
|
+
"bun.lockb": "bun"
|
|
1629
|
+
};
|
|
1630
|
+
var PM_INSTALL_COMMANDS = {
|
|
1631
|
+
npm: { command: "npm", args: ["install", "--save"] },
|
|
1632
|
+
pnpm: { command: "pnpm", args: ["add"] },
|
|
1633
|
+
yarn: { command: "yarn", args: ["add"] },
|
|
1634
|
+
bun: { command: "bun", args: ["add"] }
|
|
1635
|
+
};
|
|
1636
|
+
var PM_INSTALL_URLS = {
|
|
1637
|
+
npm: "https://docs.npmjs.com/downloading-and-installing-node-js-and-npm",
|
|
1638
|
+
pnpm: "https://pnpm.io/installation",
|
|
1639
|
+
yarn: "https://yarnpkg.com/getting-started/install",
|
|
1640
|
+
bun: "https://bun.sh/docs/installation"
|
|
1641
|
+
};
|
|
1642
|
+
function extractDependencies(skillPath) {
|
|
1643
|
+
const packageJsonPath = path6.join(skillPath, "package.json");
|
|
1644
|
+
if (!fs6.existsSync(packageJsonPath)) {
|
|
1645
|
+
return null;
|
|
1646
|
+
}
|
|
1647
|
+
try {
|
|
1648
|
+
const content = fs6.readFileSync(packageJsonPath, "utf-8");
|
|
1649
|
+
const packageJson = JSON.parse(content);
|
|
1650
|
+
const dependencies = packageJson.dependencies || {};
|
|
1651
|
+
if (Object.keys(dependencies).length === 0) {
|
|
1652
|
+
return null;
|
|
1653
|
+
}
|
|
1654
|
+
const skillName = path6.basename(skillPath);
|
|
1655
|
+
return {
|
|
1656
|
+
skillName,
|
|
1657
|
+
dependencies
|
|
1658
|
+
};
|
|
1659
|
+
} catch {
|
|
1660
|
+
return null;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
function detectPackageManager(cwd = process.cwd()) {
|
|
1664
|
+
const envPm = process.env.SKAI_PACKAGE_MANAGER?.toLowerCase();
|
|
1665
|
+
if (envPm && isValidPackageManager(envPm)) {
|
|
1666
|
+
return envPm;
|
|
1667
|
+
}
|
|
1668
|
+
for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {
|
|
1669
|
+
if (fs6.existsSync(path6.join(cwd, lockfile))) {
|
|
1670
|
+
return pm;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
return "pnpm";
|
|
1674
|
+
}
|
|
1675
|
+
function isValidPackageManager(pm) {
|
|
1676
|
+
return SUPPORTED_PACKAGE_MANAGERS.includes(pm);
|
|
1677
|
+
}
|
|
1678
|
+
function mergeDependencies(skillDeps) {
|
|
1679
|
+
const merged = {};
|
|
1680
|
+
for (const skill of skillDeps) {
|
|
1681
|
+
for (const [pkg, version] of Object.entries(skill.dependencies)) {
|
|
1682
|
+
merged[pkg] = version;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return merged;
|
|
1686
|
+
}
|
|
1687
|
+
function checkConflicts(skillDeps, projectDepsPath = path6.join(process.cwd(), "package.json")) {
|
|
1688
|
+
const conflicts = [];
|
|
1689
|
+
if (!fs6.existsSync(projectDepsPath)) {
|
|
1690
|
+
return conflicts;
|
|
1691
|
+
}
|
|
1692
|
+
try {
|
|
1693
|
+
const content = fs6.readFileSync(projectDepsPath, "utf-8");
|
|
1694
|
+
const packageJson = JSON.parse(content);
|
|
1695
|
+
const projectDeps = {
|
|
1696
|
+
...packageJson.dependencies,
|
|
1697
|
+
...packageJson.devDependencies
|
|
1698
|
+
};
|
|
1699
|
+
for (const skill of skillDeps) {
|
|
1700
|
+
for (const [pkg, version] of Object.entries(skill.dependencies)) {
|
|
1701
|
+
if (projectDeps[pkg] && projectDeps[pkg] !== version) {
|
|
1702
|
+
conflicts.push({
|
|
1703
|
+
packageName: pkg,
|
|
1704
|
+
skillVersion: version,
|
|
1705
|
+
projectVersion: projectDeps[pkg],
|
|
1706
|
+
skillName: skill.skillName
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
} catch {
|
|
1712
|
+
}
|
|
1713
|
+
return conflicts;
|
|
1714
|
+
}
|
|
1715
|
+
function formatManualInstallCommand(dependencies, pm) {
|
|
1716
|
+
const depStrings = Object.entries(dependencies).map(
|
|
1717
|
+
([pkg, version]) => `${pkg}@${version}`
|
|
1718
|
+
);
|
|
1719
|
+
const { command, args } = PM_INSTALL_COMMANDS[pm];
|
|
1720
|
+
return `${command} ${args.join(" ")} ${depStrings.join(" ")}`;
|
|
1721
|
+
}
|
|
1722
|
+
function formatDependencySummary(skillDeps) {
|
|
1723
|
+
return skillDeps.map((skill) => {
|
|
1724
|
+
const deps = Object.entries(skill.dependencies).map(([pkg, ver]) => `${pkg}@${ver}`).join(", ");
|
|
1725
|
+
return `\u2022 ${skill.skillName} (${deps})`;
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
function getPackageManagerInstallUrl(pm) {
|
|
1729
|
+
return PM_INSTALL_URLS[pm];
|
|
1730
|
+
}
|
|
1731
|
+
async function isPackageManagerAvailable(pm) {
|
|
1732
|
+
return new Promise((resolve4) => {
|
|
1733
|
+
const child = spawn(pm, ["--version"], {
|
|
1734
|
+
stdio: "ignore",
|
|
1735
|
+
shell: process.platform === "win32"
|
|
1736
|
+
});
|
|
1737
|
+
child.on("error", () => resolve4(false));
|
|
1738
|
+
child.on("close", (code) => resolve4(code === 0));
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
async function installDependencies(dependencies, pm, cwd = process.cwd(), signal) {
|
|
1742
|
+
const available = await isPackageManagerAvailable(pm);
|
|
1743
|
+
if (!available) {
|
|
1744
|
+
return {
|
|
1745
|
+
installed: false,
|
|
1746
|
+
packageManager: pm,
|
|
1747
|
+
error: `${pm} is not installed. Install it from: ${getPackageManagerInstallUrl(pm)}`
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
const projectPackageJson = path6.join(cwd, "package.json");
|
|
1751
|
+
if (!fs6.existsSync(projectPackageJson)) {
|
|
1752
|
+
return {
|
|
1753
|
+
installed: false,
|
|
1754
|
+
packageManager: pm,
|
|
1755
|
+
error: "No package.json found in current directory. Run `npm init -y` first."
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
const depStrings = Object.entries(dependencies).map(
|
|
1759
|
+
([pkg, version]) => `${pkg}@${version}`
|
|
1760
|
+
);
|
|
1761
|
+
const { command, args } = PM_INSTALL_COMMANDS[pm];
|
|
1762
|
+
return new Promise((resolve4) => {
|
|
1763
|
+
const child = spawn(command, [...args, ...depStrings], {
|
|
1764
|
+
cwd,
|
|
1765
|
+
stdio: "pipe",
|
|
1766
|
+
shell: process.platform === "win32"
|
|
1767
|
+
});
|
|
1768
|
+
let stderr = "";
|
|
1769
|
+
child.stderr?.on("data", (data) => {
|
|
1770
|
+
stderr += data.toString();
|
|
1771
|
+
});
|
|
1772
|
+
const abortHandler = () => {
|
|
1773
|
+
child.kill("SIGTERM");
|
|
1774
|
+
resolve4({
|
|
1775
|
+
installed: false,
|
|
1776
|
+
packageManager: pm,
|
|
1777
|
+
error: "Installation cancelled"
|
|
1778
|
+
});
|
|
1779
|
+
};
|
|
1780
|
+
signal?.addEventListener("abort", abortHandler, { once: true });
|
|
1781
|
+
child.on("error", (err) => {
|
|
1782
|
+
signal?.removeEventListener("abort", abortHandler);
|
|
1783
|
+
resolve4({
|
|
1784
|
+
installed: false,
|
|
1785
|
+
packageManager: pm,
|
|
1786
|
+
error: err.message
|
|
1787
|
+
});
|
|
1788
|
+
});
|
|
1789
|
+
child.on("close", (code) => {
|
|
1790
|
+
signal?.removeEventListener("abort", abortHandler);
|
|
1791
|
+
if (code === 0) {
|
|
1792
|
+
resolve4({
|
|
1793
|
+
installed: true,
|
|
1794
|
+
packageManager: pm
|
|
1795
|
+
});
|
|
1796
|
+
} else {
|
|
1797
|
+
resolve4({
|
|
1798
|
+
installed: false,
|
|
1799
|
+
packageManager: pm,
|
|
1800
|
+
error: stderr || `Process exited with code ${code}`
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
});
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
function hasProjectPackageJson(cwd = process.cwd()) {
|
|
1807
|
+
return fs6.existsSync(path6.join(cwd, "package.json"));
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// src/index.ts
|
|
1811
|
+
var __dirname = path7.dirname(fileURLToPath(import.meta.url));
|
|
1812
|
+
var EXIT_ERROR = 1;
|
|
1813
|
+
var EXIT_CANCELLED = 2;
|
|
1814
|
+
function showManualInstallHint(deps, pm) {
|
|
1815
|
+
const command = formatManualInstallCommand(deps, pm);
|
|
1816
|
+
clack.note(command, "Install manually");
|
|
1817
|
+
}
|
|
1818
|
+
function displaySingleSkill(skill) {
|
|
1819
|
+
const parts = [];
|
|
1820
|
+
if (skill.category && skill.category.length > 0) {
|
|
1821
|
+
parts.push(chalk.dim(skill.category.join("/")));
|
|
1822
|
+
}
|
|
1823
|
+
if (skill.description && skill.description.trim()) {
|
|
1824
|
+
parts.push(skill.description);
|
|
1825
|
+
} else {
|
|
1826
|
+
parts.push(chalk.dim("(No description provided)"));
|
|
1827
|
+
}
|
|
1828
|
+
clack.note(parts.join("\n"), skill.name);
|
|
1829
|
+
}
|
|
1830
|
+
function getVersion() {
|
|
1831
|
+
try {
|
|
1832
|
+
const pkgPath = path7.join(__dirname, "..", "package.json");
|
|
1833
|
+
const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
1834
|
+
return pkg.version ?? "0.0.1";
|
|
1835
|
+
} catch {
|
|
1836
|
+
return "0.0.1";
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
function printSkillTree(node, indent = 0) {
|
|
1840
|
+
const prefix = " ".repeat(indent);
|
|
1841
|
+
const categories = [];
|
|
1842
|
+
const skills = [];
|
|
1843
|
+
for (const child of node.children.values()) {
|
|
1844
|
+
if (child.skill) {
|
|
1845
|
+
skills.push(child);
|
|
1846
|
+
} else {
|
|
1847
|
+
categories.push(child);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
for (const cat of categories.sort((a, b2) => a.name.localeCompare(b2.name))) {
|
|
1851
|
+
console.log(chalk.blue(`\u2502 ${prefix}${cat.name}`));
|
|
1852
|
+
printSkillTree(cat, indent + 1);
|
|
1853
|
+
}
|
|
1854
|
+
for (const s of skills.sort((a, b2) => a.name.localeCompare(b2.name))) {
|
|
1855
|
+
const desc = s.skill?.description ? chalk.gray(` - ${s.skill.description}`) : "";
|
|
1856
|
+
console.log(chalk.green(`\u2502 ${prefix}\u2022 ${s.name}`) + desc);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
async function main() {
|
|
1860
|
+
const program = new Command();
|
|
1861
|
+
program.name("skai").description("The package manager for AI agent skills").version(getVersion(), "-V, --version", "Display version").argument("[source]", "GitHub repo, URL, or local path to install skills from").option("-g, --global", "Install to user directory instead of project", false).option("-a, --agent <agents...>", "Target specific agents").option("-s, --skill <skills...>", "Install specific skills by name").option("-l, --list", "List available skills without installing", false).option("-y, --yes", "Skip confirmation prompts", false).option("--json", "Output results in JSON format", false).action(async (source, options) => {
|
|
1862
|
+
await run(source, options);
|
|
1863
|
+
});
|
|
1864
|
+
await program.parseAsync(process.argv);
|
|
1865
|
+
}
|
|
1866
|
+
async function run(source, options) {
|
|
1867
|
+
let tempDirToClean = null;
|
|
1868
|
+
const handleSignal = () => {
|
|
1869
|
+
if (tempDirToClean) {
|
|
1870
|
+
try {
|
|
1871
|
+
cleanupTempDir(tempDirToClean);
|
|
1872
|
+
} catch {
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
clack.outro(chalk.yellow("Interrupted"));
|
|
1876
|
+
process.exit(EXIT_CANCELLED);
|
|
1877
|
+
};
|
|
1878
|
+
process.on("SIGINT", handleSignal);
|
|
1879
|
+
process.on("SIGTERM", handleSignal);
|
|
1880
|
+
clack.intro(chalk.cyan("skai - AI Agent Skills Package Manager"));
|
|
1881
|
+
if (!source) {
|
|
1882
|
+
clack.log.error("Please provide a source (GitHub repo, URL, or local path)");
|
|
1883
|
+
clack.log.info("Usage: skai <source> [options]");
|
|
1884
|
+
clack.log.info("Examples:");
|
|
1885
|
+
clack.log.info(" skai pproenca/dot-skills");
|
|
1886
|
+
clack.log.info(" skai https://github.com/org/repo");
|
|
1887
|
+
clack.log.info(" skai ./local/skills");
|
|
1888
|
+
clack.outro(chalk.red("No source provided"));
|
|
1889
|
+
process.exit(EXIT_ERROR);
|
|
1890
|
+
}
|
|
1891
|
+
let tempDir = null;
|
|
1892
|
+
let skillsBasePath;
|
|
1893
|
+
let subpath;
|
|
1894
|
+
try {
|
|
1895
|
+
const parsed = parseSource(source);
|
|
1896
|
+
clack.log.info(`Source type: ${parsed.type}`);
|
|
1897
|
+
if (parsed.type === "local") {
|
|
1898
|
+
if (!parsed.localPath) {
|
|
1899
|
+
throw new Error("Local path not found in parsed source");
|
|
1900
|
+
}
|
|
1901
|
+
skillsBasePath = parsed.localPath;
|
|
1902
|
+
clack.log.info(`Using local path: ${skillsBasePath}`);
|
|
1903
|
+
} else {
|
|
1904
|
+
const spinner2 = clack.spinner();
|
|
1905
|
+
spinner2.start("Cloning repository...");
|
|
1906
|
+
try {
|
|
1907
|
+
if (!parsed.url) {
|
|
1908
|
+
throw new Error("URL not found in parsed source");
|
|
1909
|
+
}
|
|
1910
|
+
tempDir = await cloneRepo(parsed.url, parsed.branch);
|
|
1911
|
+
tempDirToClean = tempDir;
|
|
1912
|
+
skillsBasePath = tempDir;
|
|
1913
|
+
subpath = parsed.subpath;
|
|
1914
|
+
spinner2.stop("Repository cloned");
|
|
1915
|
+
} catch (error) {
|
|
1916
|
+
spinner2.stop("Failed to clone repository");
|
|
1917
|
+
throw error;
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
const discoverSpinner = clack.spinner();
|
|
1921
|
+
discoverSpinner.start("Discovering skills...");
|
|
1922
|
+
const skills = discoverSkills(skillsBasePath, subpath);
|
|
1923
|
+
discoverSpinner.stop(`Found ${skills.length} skill(s)`);
|
|
1924
|
+
if (skills.length === 0) {
|
|
1925
|
+
clack.log.warn("No skills found in the repository");
|
|
1926
|
+
clack.outro(chalk.yellow("No skills to install"));
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
let filteredSkills = skills;
|
|
1930
|
+
if (options.skill && options.skill.length > 0) {
|
|
1931
|
+
filteredSkills = skills.filter((s) => {
|
|
1932
|
+
const skillFilters = options.skill;
|
|
1933
|
+
if (!skillFilters) {
|
|
1934
|
+
return false;
|
|
1935
|
+
}
|
|
1936
|
+
return skillFilters.some((filter) => matchesSkillFilter(s, filter));
|
|
1937
|
+
});
|
|
1938
|
+
if (filteredSkills.length === 0) {
|
|
1939
|
+
clack.log.error(`No matching skills found for: ${options.skill.join(", ")}`);
|
|
1940
|
+
clack.log.info(`Available skills: ${skills.map((s) => s.name).join(", ")}`);
|
|
1941
|
+
clack.outro(chalk.red("No matching skills"));
|
|
1942
|
+
return;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
if (filteredSkills.length === 1 && !options.list && !options.json) {
|
|
1946
|
+
displaySingleSkill(filteredSkills[0]);
|
|
1947
|
+
}
|
|
1948
|
+
if (options.list) {
|
|
1949
|
+
clack.log.info(chalk.bold("\nAvailable Skills:"));
|
|
1950
|
+
console.log("\u2502");
|
|
1951
|
+
const tree = buildSkillTree(filteredSkills);
|
|
1952
|
+
printSkillTree(tree, 0);
|
|
1953
|
+
console.log("\u2502");
|
|
1954
|
+
clack.outro(chalk.cyan(`${filteredSkills.length} skill(s) available`));
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
let targetAgents;
|
|
1958
|
+
if (options.agent && options.agent.length > 0) {
|
|
1959
|
+
targetAgents = options.agent.map((name) => getAgentByName(name)).filter((a) => a !== void 0);
|
|
1960
|
+
if (targetAgents.length === 0) {
|
|
1961
|
+
clack.log.error(`No valid agents found for: ${options.agent.join(", ")}`);
|
|
1962
|
+
clack.log.info(
|
|
1963
|
+
`Available agents: ${getAllAgents().map((a) => a.name).join(", ")}`
|
|
1964
|
+
);
|
|
1965
|
+
clack.outro(chalk.red("No valid agents"));
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
} else {
|
|
1969
|
+
const detectedAgents = detectInstalledAgents();
|
|
1970
|
+
if (detectedAgents.length === 0) {
|
|
1971
|
+
clack.log.warn("No AI agents detected on your system");
|
|
1972
|
+
if (!options.yes) {
|
|
1973
|
+
const useAll = await clack.confirm({
|
|
1974
|
+
message: "Would you like to see all available agents?"
|
|
1975
|
+
});
|
|
1976
|
+
if (clack.isCancel(useAll)) {
|
|
1977
|
+
clack.outro(chalk.yellow("Cancelled"));
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
if (useAll) {
|
|
1981
|
+
targetAgents = getAllAgents();
|
|
1982
|
+
} else {
|
|
1983
|
+
clack.outro(chalk.yellow("No agents selected"));
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
} else {
|
|
1987
|
+
clack.outro(chalk.yellow("No agents detected"));
|
|
1988
|
+
return;
|
|
1989
|
+
}
|
|
1990
|
+
} else {
|
|
1991
|
+
targetAgents = detectedAgents;
|
|
1992
|
+
clack.log.info(`Detected ${targetAgents.length} agent(s): ${targetAgents.map((a) => a.displayName).join(", ")}`);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
let selectedSkills;
|
|
1996
|
+
if (options.skill && options.skill.length > 0) {
|
|
1997
|
+
selectedSkills = filteredSkills;
|
|
1998
|
+
} else if (options.yes) {
|
|
1999
|
+
selectedSkills = filteredSkills;
|
|
2000
|
+
} else if (filteredSkills.length === 1) {
|
|
2001
|
+
selectedSkills = filteredSkills;
|
|
2002
|
+
} else {
|
|
2003
|
+
const tree = buildSkillTree(filteredSkills);
|
|
2004
|
+
let treeNodes = skillTreeToTreeNodes(tree);
|
|
2005
|
+
treeNodes = flattenSingleCategories(treeNodes);
|
|
2006
|
+
try {
|
|
2007
|
+
selectedSkills = await treeSelect(treeNodes);
|
|
2008
|
+
if (selectedSkills.length === 0) {
|
|
2009
|
+
clack.outro(chalk.yellow("No skills selected"));
|
|
2010
|
+
return;
|
|
2011
|
+
}
|
|
2012
|
+
} catch {
|
|
2013
|
+
clack.outro(chalk.yellow("Cancelled"));
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
let selectedAgents;
|
|
2018
|
+
if (options.agent && options.agent.length > 0) {
|
|
2019
|
+
selectedAgents = targetAgents;
|
|
2020
|
+
} else if (options.yes) {
|
|
2021
|
+
selectedAgents = targetAgents;
|
|
2022
|
+
} else if (targetAgents.length === 1) {
|
|
2023
|
+
selectedAgents = targetAgents;
|
|
2024
|
+
} else {
|
|
2025
|
+
const agentChoices = targetAgents.map((a) => ({
|
|
2026
|
+
value: a,
|
|
2027
|
+
label: a.displayName,
|
|
2028
|
+
hint: options.global ? a.globalPath : a.projectPath
|
|
2029
|
+
}));
|
|
2030
|
+
const selected = await clack.multiselect({
|
|
2031
|
+
message: "Select agents to install to:",
|
|
2032
|
+
options: agentChoices,
|
|
2033
|
+
required: true
|
|
2034
|
+
});
|
|
2035
|
+
if (clack.isCancel(selected)) {
|
|
2036
|
+
clack.outro(chalk.yellow("Cancelled"));
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
selectedAgents = selected;
|
|
2040
|
+
}
|
|
2041
|
+
let isGlobal = options.global;
|
|
2042
|
+
if (!options.global && !options.yes) {
|
|
2043
|
+
const scope = await clack.select({
|
|
2044
|
+
message: "Where would you like to install?",
|
|
2045
|
+
options: [
|
|
2046
|
+
{ value: "project", label: "Project", hint: "Install to current project only" },
|
|
2047
|
+
{ value: "global", label: "Global", hint: "Install to user home directory" }
|
|
2048
|
+
]
|
|
2049
|
+
});
|
|
2050
|
+
if (clack.isCancel(scope)) {
|
|
2051
|
+
clack.outro(chalk.yellow("Cancelled"));
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
isGlobal = scope === "global";
|
|
2055
|
+
}
|
|
2056
|
+
if (!options.yes) {
|
|
2057
|
+
const summary = [
|
|
2058
|
+
`Skills: ${selectedSkills.map((s) => s.name).join(", ")}`,
|
|
2059
|
+
`Agents: ${selectedAgents.map((a) => a.displayName).join(", ")}`,
|
|
2060
|
+
`Scope: ${isGlobal ? "Global" : "Project"}`
|
|
2061
|
+
].join("\n ");
|
|
2062
|
+
clack.log.info(`
|
|
2063
|
+
Installation Summary:
|
|
2064
|
+
${summary}`);
|
|
2065
|
+
const confirmed = await clack.confirm({
|
|
2066
|
+
message: "Proceed with installation?"
|
|
2067
|
+
});
|
|
2068
|
+
if (clack.isCancel(confirmed) || !confirmed) {
|
|
2069
|
+
clack.outro(chalk.yellow("Cancelled"));
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
const installSpinner = clack.spinner();
|
|
2074
|
+
installSpinner.start("Installing skills...");
|
|
2075
|
+
const results = {
|
|
2076
|
+
success: 0,
|
|
2077
|
+
failed: 0,
|
|
2078
|
+
skipped: 0
|
|
2079
|
+
};
|
|
2080
|
+
const installOptions = { global: isGlobal, yes: options.yes };
|
|
2081
|
+
const installedSkillNames = [];
|
|
2082
|
+
for (const skill of selectedSkills) {
|
|
2083
|
+
for (const agent of selectedAgents) {
|
|
2084
|
+
if (isSkillInstalled(skill, agent, installOptions)) {
|
|
2085
|
+
results.skipped++;
|
|
2086
|
+
continue;
|
|
2087
|
+
}
|
|
2088
|
+
const result = installSkillForAgent(skill, agent, installOptions);
|
|
2089
|
+
if (result.success) {
|
|
2090
|
+
results.success++;
|
|
2091
|
+
if (!installedSkillNames.includes(skill.name)) {
|
|
2092
|
+
installedSkillNames.push(skill.name);
|
|
2093
|
+
}
|
|
2094
|
+
} else {
|
|
2095
|
+
results.failed++;
|
|
2096
|
+
if (!options.json) {
|
|
2097
|
+
clack.log.warn(`Failed to install ${skill.name} to ${agent.displayName}: ${result.error}`);
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
installSpinner.stop(`Installed ${results.success} skill(s)`);
|
|
2103
|
+
const depSpinner = clack.spinner();
|
|
2104
|
+
depSpinner.start("Scanning for dependencies...");
|
|
2105
|
+
const skillDeps = [];
|
|
2106
|
+
for (const skill of selectedSkills) {
|
|
2107
|
+
const deps = extractDependencies(skill.path);
|
|
2108
|
+
if (deps) {
|
|
2109
|
+
skillDeps.push(deps);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
depSpinner.stop(
|
|
2113
|
+
skillDeps.length > 0 ? `Found dependencies in ${skillDeps.length} skill(s)` : "No dependencies found"
|
|
2114
|
+
);
|
|
2115
|
+
let depsInstalled = false;
|
|
2116
|
+
let usedPackageManager = null;
|
|
2117
|
+
if (skillDeps.length > 0) {
|
|
2118
|
+
const mergedDeps = mergeDependencies(skillDeps);
|
|
2119
|
+
const detectedPm = detectPackageManager();
|
|
2120
|
+
usedPackageManager = detectedPm;
|
|
2121
|
+
if (options.json) {
|
|
2122
|
+
if (hasProjectPackageJson()) {
|
|
2123
|
+
const installResult = await installDependencies(mergedDeps, detectedPm);
|
|
2124
|
+
depsInstalled = installResult.installed;
|
|
2125
|
+
}
|
|
2126
|
+
} else {
|
|
2127
|
+
clack.log.info(chalk.bold("\n\u{1F4E6} Skills with dependencies:"));
|
|
2128
|
+
for (const line of formatDependencySummary(skillDeps)) {
|
|
2129
|
+
clack.log.info(` ${line}`);
|
|
2130
|
+
}
|
|
2131
|
+
const conflicts = checkConflicts(skillDeps);
|
|
2132
|
+
if (conflicts.length > 0) {
|
|
2133
|
+
clack.log.warn(chalk.yellow("\n\u26A0 Dependency conflicts detected:"));
|
|
2134
|
+
for (const conflict of conflicts) {
|
|
2135
|
+
clack.log.warn(
|
|
2136
|
+
` \u2022 ${conflict.packageName}: skill requires ${conflict.skillVersion}, project has ${conflict.projectVersion}`
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
const isInteractive = process.stdout.isTTY && !options.yes;
|
|
2141
|
+
if (isInteractive) {
|
|
2142
|
+
const pmChoice = await clack.select({
|
|
2143
|
+
message: "Install dependencies now?",
|
|
2144
|
+
options: [
|
|
2145
|
+
{ value: detectedPm, label: `Yes, install with ${detectedPm} (detected)` },
|
|
2146
|
+
...["npm", "pnpm", "yarn", "bun"].filter((pm) => pm !== detectedPm).map((pm) => ({ value: pm, label: `Yes, install with ${pm}` })),
|
|
2147
|
+
{ value: "skip", label: "Skip (install manually later)" }
|
|
2148
|
+
]
|
|
2149
|
+
});
|
|
2150
|
+
if (clack.isCancel(pmChoice)) {
|
|
2151
|
+
showManualInstallHint(mergedDeps, detectedPm);
|
|
2152
|
+
clack.outro(chalk.yellow("Cancelled"));
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
if (pmChoice === "skip") {
|
|
2156
|
+
showManualInstallHint(mergedDeps, detectedPm);
|
|
2157
|
+
} else {
|
|
2158
|
+
const pm = pmChoice;
|
|
2159
|
+
usedPackageManager = pm;
|
|
2160
|
+
if (!hasProjectPackageJson()) {
|
|
2161
|
+
clack.log.warn("No package.json found in current directory.");
|
|
2162
|
+
const createPkg = await clack.confirm({
|
|
2163
|
+
message: "Create a package.json file?"
|
|
2164
|
+
});
|
|
2165
|
+
if (clack.isCancel(createPkg) || !createPkg) {
|
|
2166
|
+
showManualInstallHint(mergedDeps, pm);
|
|
2167
|
+
} else {
|
|
2168
|
+
fs7.writeFileSync(
|
|
2169
|
+
path7.join(process.cwd(), "package.json"),
|
|
2170
|
+
JSON.stringify({ name: path7.basename(process.cwd()), version: "1.0.0", private: true }, null, 2)
|
|
2171
|
+
);
|
|
2172
|
+
clack.log.info("Created package.json");
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
if (hasProjectPackageJson()) {
|
|
2176
|
+
const controller = new AbortController();
|
|
2177
|
+
const sigintHandler = () => {
|
|
2178
|
+
controller.abort();
|
|
2179
|
+
};
|
|
2180
|
+
process.on("SIGINT", sigintHandler);
|
|
2181
|
+
const depInstallSpinner = clack.spinner();
|
|
2182
|
+
depInstallSpinner.start(
|
|
2183
|
+
`Installing dependencies with ${pm} (${Object.keys(mergedDeps).length} packages)...`
|
|
2184
|
+
);
|
|
2185
|
+
const installResult = await installDependencies(mergedDeps, pm, process.cwd(), controller.signal);
|
|
2186
|
+
process.off("SIGINT", sigintHandler);
|
|
2187
|
+
if (installResult.installed) {
|
|
2188
|
+
depInstallSpinner.stop("Dependencies installed");
|
|
2189
|
+
depsInstalled = true;
|
|
2190
|
+
} else {
|
|
2191
|
+
depInstallSpinner.stop("Dependency installation failed");
|
|
2192
|
+
clack.log.warn(installResult.error ?? "Unknown error");
|
|
2193
|
+
showManualInstallHint(mergedDeps, pm);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
} else {
|
|
2198
|
+
if (!hasProjectPackageJson()) {
|
|
2199
|
+
clack.log.warn("No package.json found. Skipping dependency installation.");
|
|
2200
|
+
showManualInstallHint(mergedDeps, detectedPm);
|
|
2201
|
+
} else {
|
|
2202
|
+
const depInstallSpinner = clack.spinner();
|
|
2203
|
+
depInstallSpinner.start(
|
|
2204
|
+
`Installing dependencies with ${detectedPm} (${Object.keys(mergedDeps).length} packages)...`
|
|
2205
|
+
);
|
|
2206
|
+
const installResult = await installDependencies(mergedDeps, detectedPm);
|
|
2207
|
+
if (installResult.installed) {
|
|
2208
|
+
depInstallSpinner.stop("Dependencies installed");
|
|
2209
|
+
depsInstalled = true;
|
|
2210
|
+
} else {
|
|
2211
|
+
depInstallSpinner.stop("Dependency installation failed");
|
|
2212
|
+
clack.log.warn(installResult.error ?? "Unknown error");
|
|
2213
|
+
showManualInstallHint(mergedDeps, detectedPm);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
if (options.json) {
|
|
2220
|
+
const jsonOutput = {
|
|
2221
|
+
skills_installed: installedSkillNames,
|
|
2222
|
+
dependencies: Object.fromEntries(skillDeps.map((s) => [s.skillName, s.dependencies])),
|
|
2223
|
+
dependencies_installed: depsInstalled,
|
|
2224
|
+
package_manager: usedPackageManager
|
|
2225
|
+
};
|
|
2226
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
const resultParts = [];
|
|
2230
|
+
if (results.success > 0) resultParts.push(chalk.green(`${results.success} installed`));
|
|
2231
|
+
if (results.skipped > 0) resultParts.push(chalk.yellow(`${results.skipped} skipped (already installed)`));
|
|
2232
|
+
if (results.failed > 0) resultParts.push(chalk.red(`${results.failed} failed`));
|
|
2233
|
+
if (results.success > 0) {
|
|
2234
|
+
const nextSteps = ["Restart your AI agent to load the new skills."];
|
|
2235
|
+
if (depsInstalled) {
|
|
2236
|
+
nextSteps.push("Dependencies were installed to your project.");
|
|
2237
|
+
}
|
|
2238
|
+
clack.note(nextSteps.join("\n"), "Next steps");
|
|
2239
|
+
}
|
|
2240
|
+
clack.outro(resultParts.join(", ") || chalk.green("Done"));
|
|
2241
|
+
} catch (error) {
|
|
2242
|
+
clack.log.error(error instanceof Error ? error.message : String(error));
|
|
2243
|
+
clack.outro(chalk.red("Installation failed"));
|
|
2244
|
+
process.exit(EXIT_ERROR);
|
|
2245
|
+
} finally {
|
|
2246
|
+
if (tempDir) {
|
|
2247
|
+
try {
|
|
2248
|
+
cleanupTempDir(tempDir);
|
|
2249
|
+
} catch {
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
main().catch((error) => {
|
|
2255
|
+
console.error(chalk.red("Fatal error:"), error);
|
|
2256
|
+
process.exit(EXIT_ERROR);
|
|
2257
|
+
});
|
|
2258
|
+
//# sourceMappingURL=index.js.map
|