pikakit 1.0.0 → 1.0.2
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/bin/cli.mjs +1 -1
- package/bin/lib/commands/install.js +9 -9
- package/bin/lib/ui.js +329 -329
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -177,36 +177,36 @@ export async function run(spec) {
|
|
|
177
177
|
// NOTE: Order matters! Specialized categories FIRST, Core is fallback
|
|
178
178
|
const CATEGORY_KEYWORDS = {
|
|
179
179
|
// Specialized domains
|
|
180
|
-
"
|
|
180
|
+
"🎨 Frontend & UI": [
|
|
181
181
|
"frontend", "nextjs", "tailwind", "css", "ui", "ux", "visual",
|
|
182
182
|
"studio", "web-core", "design-system", "react-architect", "react"
|
|
183
183
|
],
|
|
184
|
-
"
|
|
184
|
+
"🎮 Game Development": [
|
|
185
185
|
"game", "development", "engine", "unity", "unreal", "godot", "phaser"
|
|
186
186
|
],
|
|
187
|
-
"
|
|
187
|
+
"📱 Mobile": [
|
|
188
188
|
"mobile", "first", "developer", "react-native", "flutter",
|
|
189
189
|
"ios", "android", "swift", "kotlin"
|
|
190
190
|
],
|
|
191
|
-
"
|
|
191
|
+
"🔒 Security & DevOps": [
|
|
192
192
|
"security", "vulnerability", "offensive", "scanner", "red-team", "governance",
|
|
193
193
|
"cicd", "pipeline", "gitops", "docker", "deploy", "server-ops"
|
|
194
194
|
],
|
|
195
195
|
// Technical domains
|
|
196
|
-
"
|
|
196
|
+
"🧪 Testing & Quality": [
|
|
197
197
|
"test", "testing", "tdd", "e2e", "debug", "quality", "review",
|
|
198
198
|
"lint", "validate", "automation", "problem", "checker"
|
|
199
199
|
],
|
|
200
|
-
"
|
|
200
|
+
"🤖 AI & Agents": [
|
|
201
201
|
"agent", "pattern", "auto-learn", "execution", "self-evolution",
|
|
202
202
|
"lifecycle", "skill-forge", "intelligent", "routing"
|
|
203
203
|
],
|
|
204
|
-
"
|
|
204
|
+
"📚 Docs & Planning": [
|
|
205
205
|
"doc", "template", "plan", "project", "idea", "brainstorm",
|
|
206
206
|
"geo", "seo", "i18n", "writing"
|
|
207
207
|
],
|
|
208
208
|
// Fallback (core backend/infra)
|
|
209
|
-
"
|
|
209
|
+
"⚙️ Backend & Core": [
|
|
210
210
|
"backend", "api", "nodejs", "python", "server", "database",
|
|
211
211
|
"prisma", "mcp", "data", "architect", "scaffold", "system",
|
|
212
212
|
"typescript", "shell", "bash", "powershell", "git", "code-craft",
|
|
@@ -221,7 +221,7 @@ export async function run(spec) {
|
|
|
221
221
|
return category;
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
-
return "
|
|
224
|
+
return "⚙️ Backend & Core"; // Default fallback (no "Other" category)
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
// REQUIRED SKILLS - Always installed, not shown in selection
|
package/bin/lib/ui.js
CHANGED
|
@@ -1,329 +1,329 @@
|
|
|
1
|
-
|
|
2
|
-
* @fileoverview UI components - Install Agent Skill theme
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import kleur from "kleur";
|
|
6
|
-
import boxen from "boxen";
|
|
7
|
-
import { intro, outro, multiselect, select, confirm, isCancel, cancel, text } from "@clack/prompts";
|
|
8
|
-
import ora from "ora";
|
|
9
|
-
import gradient from "gradient-string";
|
|
10
|
-
|
|
11
|
-
export { intro, outro, multiselect, select, confirm, isCancel, cancel, text };
|
|
12
|
-
|
|
13
|
-
// --- ASCII Art Banner ---
|
|
14
|
-
const PIKAKIT_BANNER = `
|
|
15
|
-
____ _ _ _ ___ _
|
|
16
|
-
| _ \\(_) | ____ _| |/ (_) |_
|
|
17
|
-
| |_) | | |/ / _\` | ' /| | __|
|
|
18
|
-
| __/| | < (_| | . \\| | |_
|
|
19
|
-
|_| |_|_|\\_\\__,_|_|\\_\\_|\\__|
|
|
20
|
-
`;
|
|
21
|
-
|
|
22
|
-
// Custom gradient: white
|
|
23
|
-
const pikaGradient = gradient(['#ffffff', '#bbbbbb', '#888888', '#555555']);
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Create a spinner
|
|
27
|
-
*/
|
|
28
|
-
export function spinner() {
|
|
29
|
-
return {
|
|
30
|
-
_s: null,
|
|
31
|
-
start(msg) {
|
|
32
|
-
this._s = ora({
|
|
33
|
-
text: " " + msg,
|
|
34
|
-
prefixText: "",
|
|
35
|
-
color: "blue",
|
|
36
|
-
spinner: {
|
|
37
|
-
interval: 80,
|
|
38
|
-
frames: ['
|
|
39
|
-
}
|
|
40
|
-
}).start();
|
|
41
|
-
},
|
|
42
|
-
stop(msg) {
|
|
43
|
-
if (this._s) {
|
|
44
|
-
this._s.stopAndPersist({
|
|
45
|
-
symbol: c.cyan(S.diamond),
|
|
46
|
-
text: " " + msg
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
fail(msg) {
|
|
51
|
-
if (this._s) {
|
|
52
|
-
this._s.stopAndPersist({
|
|
53
|
-
symbol: c.red(S.cross),
|
|
54
|
-
text: " " + msg
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
message(msg) {
|
|
59
|
-
if (this._s) this._s.text = " " + msg;
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// --- Symbols ---
|
|
65
|
-
|
|
66
|
-
/** UI symbols for tree structure */
|
|
67
|
-
export const S = {
|
|
68
|
-
branch: "
|
|
69
|
-
diamond: "
|
|
70
|
-
diamondFilled: "
|
|
71
|
-
check: "
|
|
72
|
-
cross: "x",
|
|
73
|
-
arrow: "
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// --- Colors ---
|
|
77
|
-
|
|
78
|
-
/** Color helper functions */
|
|
79
|
-
export const c = {
|
|
80
|
-
cyan: kleur.cyan,
|
|
81
|
-
gray: kleur.gray,
|
|
82
|
-
green: kleur.green,
|
|
83
|
-
red: kleur.red,
|
|
84
|
-
yellow: kleur.yellow,
|
|
85
|
-
magenta: kleur.magenta,
|
|
86
|
-
blue: kleur.blue,
|
|
87
|
-
white: kleur.white,
|
|
88
|
-
bgBlue: kleur.bgBlue,
|
|
89
|
-
bold: kleur.bold,
|
|
90
|
-
dim: kleur.dim,
|
|
91
|
-
inverse: kleur.inverse
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// --- UI Functions ---
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Print a step in the tree
|
|
98
|
-
* @param {string} text - Step text
|
|
99
|
-
* @param {string} [icon] - Icon to use
|
|
100
|
-
* @param {keyof typeof c} [color] - Color name
|
|
101
|
-
*/
|
|
102
|
-
export function step(text, icon = S.diamond, color = "cyan") {
|
|
103
|
-
const colorFn = c[color] || c.cyan;
|
|
104
|
-
console.log(`${colorFn(icon)} ${text}`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Print an active step (Blue Filled Diamond)
|
|
109
|
-
* @param {string} text - Step text
|
|
110
|
-
*/
|
|
111
|
-
export function activeStep(text) {
|
|
112
|
-
console.log(`${c.blue(S.diamondFilled)} ${text}`);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Print empty branch line
|
|
117
|
-
*/
|
|
118
|
-
export function stepLine() {
|
|
119
|
-
console.log(`${c.gray(S.branch)}`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Print fatal error and exit
|
|
124
|
-
* @param {string} msg - Error message
|
|
125
|
-
*/
|
|
126
|
-
export function fatal(msg) {
|
|
127
|
-
console.log(`${c.red(S.cross)} ${c.red(msg)}`);
|
|
128
|
-
process.exit(1);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Print success message
|
|
133
|
-
* @param {string} msg - Success message
|
|
134
|
-
*/
|
|
135
|
-
export function success(msg) {
|
|
136
|
-
console.log(`${c.cyan(S.diamond)} ${c.cyan(msg)}`);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Output JSON if JSON_OUTPUT mode
|
|
141
|
-
* @param {any} data - Data to output
|
|
142
|
-
* @param {boolean} jsonMode - Whether to output JSON
|
|
143
|
-
*/
|
|
144
|
-
export function outputJSON(data, jsonMode) {
|
|
145
|
-
if (jsonMode) console.log(JSON.stringify(data, null, 2));
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Create a nice box message
|
|
150
|
-
* @param {string} message - Message content
|
|
151
|
-
* @param {object} options - Box options
|
|
152
|
-
*/
|
|
153
|
-
export function box(message, options = {}) {
|
|
154
|
-
return "\n" + boxen(message, {
|
|
155
|
-
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
156
|
-
margin: { top: 0, bottom: 0, left: 0, right: 0 },
|
|
157
|
-
borderStyle: "round",
|
|
158
|
-
borderColor: "blue",
|
|
159
|
-
...options
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Show branded intro with version (matches agent CLI style)
|
|
165
|
-
* @param {string} version - Package version
|
|
166
|
-
* @param {string} [status] - Optional status text
|
|
167
|
-
*/
|
|
168
|
-
export function brandedIntro(version, status = "") {
|
|
169
|
-
// Split banner and filter to get content lines only
|
|
170
|
-
const bannerLines = PIKAKIT_BANNER.split('\n').filter(line => line.trim() !== '');
|
|
171
|
-
|
|
172
|
-
// Print all lines except the last with gradient
|
|
173
|
-
for (let i = 0; i < bannerLines.length - 1; i++) {
|
|
174
|
-
console.log(pikaGradient(bannerLines[i]));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Last line: gradient ASCII + dim version (aligned at bottom)
|
|
178
|
-
const lastLine = bannerLines[bannerLines.length - 1];
|
|
179
|
-
console.log(pikaGradient(lastLine) + ` ${c.dim(`v${version}`)}`);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (status) {
|
|
183
|
-
console.log(`${c.dim(status)}`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// --- Vercel-Style Installation Prompts ---
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Prompt user to select which agents to install to
|
|
191
|
-
* @param {Array<{name: string, displayName: string, skillsDir: string}>} detectedAgents
|
|
192
|
-
* @returns {Promise<Array<{name: string, displayName: string, skillsDir: string}> | null>}
|
|
193
|
-
*/
|
|
194
|
-
export async function selectAgentsPrompt(detectedAgents) {
|
|
195
|
-
if (detectedAgents.length === 0) {
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// First ask: All detected or select specific?
|
|
200
|
-
const installChoice = await select({
|
|
201
|
-
message: "Install to",
|
|
202
|
-
options: [
|
|
203
|
-
{
|
|
204
|
-
value: "all",
|
|
205
|
-
label: `All detected agents (Recommended)`,
|
|
206
|
-
hint: `Install to all ${detectedAgents.length} detected agents`
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
value: "select",
|
|
210
|
-
label: "Select specific agents",
|
|
211
|
-
hint: "Choose which agents to install to"
|
|
212
|
-
}
|
|
213
|
-
]
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
if (isCancel(installChoice)) {
|
|
217
|
-
cancel("Installation cancelled");
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (installChoice === "all") {
|
|
222
|
-
return detectedAgents;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Let user select specific agents
|
|
226
|
-
const selectedAgents = await multiselect({
|
|
227
|
-
message: "Select agents to install skills to",
|
|
228
|
-
options: detectedAgents.map(agent => ({
|
|
229
|
-
value: agent.name,
|
|
230
|
-
label: agent.displayName,
|
|
231
|
-
hint: agent.skillsDir
|
|
232
|
-
})),
|
|
233
|
-
required: true
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
if (isCancel(selectedAgents)) {
|
|
237
|
-
cancel("Installation cancelled");
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return detectedAgents.filter(a => selectedAgents.includes(a.name));
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Prompt user to select installation scope (Project or Global)
|
|
246
|
-
* @returns {Promise<"project" | "global" | null>}
|
|
247
|
-
*/
|
|
248
|
-
export async function selectScopePrompt() {
|
|
249
|
-
const scope = await select({
|
|
250
|
-
message: "Installation scope",
|
|
251
|
-
options: [
|
|
252
|
-
{
|
|
253
|
-
value: "project",
|
|
254
|
-
label: "Project",
|
|
255
|
-
hint: "Install in current directory (committed with your project)"
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
value: "global",
|
|
259
|
-
label: "Global",
|
|
260
|
-
hint: "Install globally (available across all projects)"
|
|
261
|
-
}
|
|
262
|
-
]
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
if (isCancel(scope)) {
|
|
266
|
-
cancel("Installation cancelled");
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return scope;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Prompt user to select installation method (Symlink or Copy)
|
|
275
|
-
* @returns {Promise<"symlink" | "copy" | null>}
|
|
276
|
-
*/
|
|
277
|
-
export async function selectMethodPrompt() {
|
|
278
|
-
const method = await select({
|
|
279
|
-
message: "Installation method",
|
|
280
|
-
options: [
|
|
281
|
-
{
|
|
282
|
-
value: "symlink",
|
|
283
|
-
label: "Symlink (Recommended)",
|
|
284
|
-
hint: "Single source of truth, easy updates"
|
|
285
|
-
},
|
|
286
|
-
{
|
|
287
|
-
value: "copy",
|
|
288
|
-
label: "Copy to all agents",
|
|
289
|
-
hint: "Independent copies for each agent"
|
|
290
|
-
}
|
|
291
|
-
]
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
if (isCancel(method)) {
|
|
295
|
-
cancel("Installation cancelled");
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return method;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Prompt user to select skills to install (multiselect with descriptions)
|
|
304
|
-
* @param {Array<{name: string, description: string, path: string}>} skills
|
|
305
|
-
* @returns {Promise<Array<{name: string, description: string, path: string}> | null>}
|
|
306
|
-
*/
|
|
307
|
-
export async function selectSkillsPrompt(skills) {
|
|
308
|
-
if (skills.length === 0) {
|
|
309
|
-
return null;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const selectedNames = await multiselect({
|
|
313
|
-
message: "Select skills to install",
|
|
314
|
-
options: skills.map(skill => ({
|
|
315
|
-
value: skill.name,
|
|
316
|
-
label: skill.name,
|
|
317
|
-
hint: skill.description ? skill.description.substring(0, 60) + "..." : ""
|
|
318
|
-
})),
|
|
319
|
-
required: true
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
if (isCancel(selectedNames)) {
|
|
323
|
-
cancel("Installation cancelled");
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return skills.filter(s => selectedNames.includes(s.name));
|
|
328
|
-
}
|
|
329
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview UI components - Install Agent Skill theme
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import kleur from "kleur";
|
|
6
|
+
import boxen from "boxen";
|
|
7
|
+
import { intro, outro, multiselect, select, confirm, isCancel, cancel, text } from "@clack/prompts";
|
|
8
|
+
import ora from "ora";
|
|
9
|
+
import gradient from "gradient-string";
|
|
10
|
+
|
|
11
|
+
export { intro, outro, multiselect, select, confirm, isCancel, cancel, text };
|
|
12
|
+
|
|
13
|
+
// --- ASCII Art Banner ---
|
|
14
|
+
const PIKAKIT_BANNER = `
|
|
15
|
+
____ _ _ _ ___ _
|
|
16
|
+
| _ \\(_) | ____ _| |/ (_) |_
|
|
17
|
+
| |_) | | |/ / _\` | ' /| | __|
|
|
18
|
+
| __/| | < (_| | . \\| | |_
|
|
19
|
+
|_| |_|_|\\_\\__,_|_|\\_\\_|\\__|
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
// Custom gradient: white → gray (like vercel style)
|
|
23
|
+
const pikaGradient = gradient(['#ffffff', '#bbbbbb', '#888888', '#555555']);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a spinner
|
|
27
|
+
*/
|
|
28
|
+
export function spinner() {
|
|
29
|
+
return {
|
|
30
|
+
_s: null,
|
|
31
|
+
start(msg) {
|
|
32
|
+
this._s = ora({
|
|
33
|
+
text: " " + msg,
|
|
34
|
+
prefixText: "",
|
|
35
|
+
color: "blue",
|
|
36
|
+
spinner: {
|
|
37
|
+
interval: 80,
|
|
38
|
+
frames: ['◒', '◐', '◓', '◑']
|
|
39
|
+
}
|
|
40
|
+
}).start();
|
|
41
|
+
},
|
|
42
|
+
stop(msg) {
|
|
43
|
+
if (this._s) {
|
|
44
|
+
this._s.stopAndPersist({
|
|
45
|
+
symbol: c.cyan(S.diamond),
|
|
46
|
+
text: " " + msg
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
fail(msg) {
|
|
51
|
+
if (this._s) {
|
|
52
|
+
this._s.stopAndPersist({
|
|
53
|
+
symbol: c.red(S.cross),
|
|
54
|
+
text: " " + msg
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
message(msg) {
|
|
59
|
+
if (this._s) this._s.text = " " + msg;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// --- Symbols ---
|
|
65
|
+
|
|
66
|
+
/** UI symbols for tree structure */
|
|
67
|
+
export const S = {
|
|
68
|
+
branch: "│",
|
|
69
|
+
diamond: "◇",
|
|
70
|
+
diamondFilled: "◆",
|
|
71
|
+
check: "✓",
|
|
72
|
+
cross: "x",
|
|
73
|
+
arrow: "→"
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// --- Colors ---
|
|
77
|
+
|
|
78
|
+
/** Color helper functions */
|
|
79
|
+
export const c = {
|
|
80
|
+
cyan: kleur.cyan,
|
|
81
|
+
gray: kleur.gray,
|
|
82
|
+
green: kleur.green,
|
|
83
|
+
red: kleur.red,
|
|
84
|
+
yellow: kleur.yellow,
|
|
85
|
+
magenta: kleur.magenta,
|
|
86
|
+
blue: kleur.blue,
|
|
87
|
+
white: kleur.white,
|
|
88
|
+
bgBlue: kleur.bgBlue,
|
|
89
|
+
bold: kleur.bold,
|
|
90
|
+
dim: kleur.dim,
|
|
91
|
+
inverse: kleur.inverse
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// --- UI Functions ---
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Print a step in the tree
|
|
98
|
+
* @param {string} text - Step text
|
|
99
|
+
* @param {string} [icon] - Icon to use
|
|
100
|
+
* @param {keyof typeof c} [color] - Color name
|
|
101
|
+
*/
|
|
102
|
+
export function step(text, icon = S.diamond, color = "cyan") {
|
|
103
|
+
const colorFn = c[color] || c.cyan;
|
|
104
|
+
console.log(`${colorFn(icon)} ${text}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Print an active step (Blue Filled Diamond)
|
|
109
|
+
* @param {string} text - Step text
|
|
110
|
+
*/
|
|
111
|
+
export function activeStep(text) {
|
|
112
|
+
console.log(`${c.blue(S.diamondFilled)} ${text}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Print empty branch line
|
|
117
|
+
*/
|
|
118
|
+
export function stepLine() {
|
|
119
|
+
console.log(`${c.gray(S.branch)}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Print fatal error and exit
|
|
124
|
+
* @param {string} msg - Error message
|
|
125
|
+
*/
|
|
126
|
+
export function fatal(msg) {
|
|
127
|
+
console.log(`${c.red(S.cross)} ${c.red(msg)}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Print success message
|
|
133
|
+
* @param {string} msg - Success message
|
|
134
|
+
*/
|
|
135
|
+
export function success(msg) {
|
|
136
|
+
console.log(`${c.cyan(S.diamond)} ${c.cyan(msg)}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Output JSON if JSON_OUTPUT mode
|
|
141
|
+
* @param {any} data - Data to output
|
|
142
|
+
* @param {boolean} jsonMode - Whether to output JSON
|
|
143
|
+
*/
|
|
144
|
+
export function outputJSON(data, jsonMode) {
|
|
145
|
+
if (jsonMode) console.log(JSON.stringify(data, null, 2));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Create a nice box message
|
|
150
|
+
* @param {string} message - Message content
|
|
151
|
+
* @param {object} options - Box options
|
|
152
|
+
*/
|
|
153
|
+
export function box(message, options = {}) {
|
|
154
|
+
return "\n" + boxen(message, {
|
|
155
|
+
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
156
|
+
margin: { top: 0, bottom: 0, left: 0, right: 0 },
|
|
157
|
+
borderStyle: "round",
|
|
158
|
+
borderColor: "blue",
|
|
159
|
+
...options
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Show branded intro with version (matches agent CLI style)
|
|
165
|
+
* @param {string} version - Package version
|
|
166
|
+
* @param {string} [status] - Optional status text
|
|
167
|
+
*/
|
|
168
|
+
export function brandedIntro(version, status = "") {
|
|
169
|
+
// Split banner and filter to get content lines only
|
|
170
|
+
const bannerLines = PIKAKIT_BANNER.split('\n').filter(line => line.trim() !== '');
|
|
171
|
+
|
|
172
|
+
// Print all lines except the last with gradient
|
|
173
|
+
for (let i = 0; i < bannerLines.length - 1; i++) {
|
|
174
|
+
console.log(pikaGradient(bannerLines[i]));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Last line: gradient ASCII + dim version (aligned at bottom)
|
|
178
|
+
const lastLine = bannerLines[bannerLines.length - 1];
|
|
179
|
+
console.log(pikaGradient(lastLine) + ` ${c.dim(`v${version}`)}`);
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if (status) {
|
|
183
|
+
console.log(`${c.dim(status)}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// --- Vercel-Style Installation Prompts ---
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Prompt user to select which agents to install to
|
|
191
|
+
* @param {Array<{name: string, displayName: string, skillsDir: string}>} detectedAgents
|
|
192
|
+
* @returns {Promise<Array<{name: string, displayName: string, skillsDir: string}> | null>}
|
|
193
|
+
*/
|
|
194
|
+
export async function selectAgentsPrompt(detectedAgents) {
|
|
195
|
+
if (detectedAgents.length === 0) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// First ask: All detected or select specific?
|
|
200
|
+
const installChoice = await select({
|
|
201
|
+
message: "Install to",
|
|
202
|
+
options: [
|
|
203
|
+
{
|
|
204
|
+
value: "all",
|
|
205
|
+
label: `All detected agents (Recommended)`,
|
|
206
|
+
hint: `Install to all ${detectedAgents.length} detected agents`
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
value: "select",
|
|
210
|
+
label: "Select specific agents",
|
|
211
|
+
hint: "Choose which agents to install to"
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (isCancel(installChoice)) {
|
|
217
|
+
cancel("Installation cancelled");
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (installChoice === "all") {
|
|
222
|
+
return detectedAgents;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Let user select specific agents
|
|
226
|
+
const selectedAgents = await multiselect({
|
|
227
|
+
message: "Select agents to install skills to",
|
|
228
|
+
options: detectedAgents.map(agent => ({
|
|
229
|
+
value: agent.name,
|
|
230
|
+
label: agent.displayName,
|
|
231
|
+
hint: agent.skillsDir
|
|
232
|
+
})),
|
|
233
|
+
required: true
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
if (isCancel(selectedAgents)) {
|
|
237
|
+
cancel("Installation cancelled");
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return detectedAgents.filter(a => selectedAgents.includes(a.name));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Prompt user to select installation scope (Project or Global)
|
|
246
|
+
* @returns {Promise<"project" | "global" | null>}
|
|
247
|
+
*/
|
|
248
|
+
export async function selectScopePrompt() {
|
|
249
|
+
const scope = await select({
|
|
250
|
+
message: "Installation scope",
|
|
251
|
+
options: [
|
|
252
|
+
{
|
|
253
|
+
value: "project",
|
|
254
|
+
label: "Project",
|
|
255
|
+
hint: "Install in current directory (committed with your project)"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
value: "global",
|
|
259
|
+
label: "Global",
|
|
260
|
+
hint: "Install globally (available across all projects)"
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (isCancel(scope)) {
|
|
266
|
+
cancel("Installation cancelled");
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return scope;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Prompt user to select installation method (Symlink or Copy)
|
|
275
|
+
* @returns {Promise<"symlink" | "copy" | null>}
|
|
276
|
+
*/
|
|
277
|
+
export async function selectMethodPrompt() {
|
|
278
|
+
const method = await select({
|
|
279
|
+
message: "Installation method",
|
|
280
|
+
options: [
|
|
281
|
+
{
|
|
282
|
+
value: "symlink",
|
|
283
|
+
label: "Symlink (Recommended)",
|
|
284
|
+
hint: "Single source of truth, easy updates"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
value: "copy",
|
|
288
|
+
label: "Copy to all agents",
|
|
289
|
+
hint: "Independent copies for each agent"
|
|
290
|
+
}
|
|
291
|
+
]
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
if (isCancel(method)) {
|
|
295
|
+
cancel("Installation cancelled");
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return method;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Prompt user to select skills to install (multiselect with descriptions)
|
|
304
|
+
* @param {Array<{name: string, description: string, path: string}>} skills
|
|
305
|
+
* @returns {Promise<Array<{name: string, description: string, path: string}> | null>}
|
|
306
|
+
*/
|
|
307
|
+
export async function selectSkillsPrompt(skills) {
|
|
308
|
+
if (skills.length === 0) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const selectedNames = await multiselect({
|
|
313
|
+
message: "Select skills to install",
|
|
314
|
+
options: skills.map(skill => ({
|
|
315
|
+
value: skill.name,
|
|
316
|
+
label: skill.name,
|
|
317
|
+
hint: skill.description ? skill.description.substring(0, 60) + "..." : ""
|
|
318
|
+
})),
|
|
319
|
+
required: true
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
if (isCancel(selectedNames)) {
|
|
323
|
+
cancel("Installation cancelled");
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return skills.filter(s => selectedNames.includes(s.name));
|
|
328
|
+
}
|
|
329
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pikakit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Enterprise-grade Agent Skill Manager with Antigravity Skills support, Progressive Disclosure detection, and semantic routing validation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "agentskillkit <agentskillkit@gmail.com>",
|