@agentskillkit/agent-skills 3.2.1 → 3.2.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.
Files changed (51) hide show
  1. package/.agent/skills/mobile-design/scripts/mobile_audit.js +333 -0
  2. package/.agent/skills/typescript-expert/scripts/ts_diagnostic.js +227 -0
  3. package/README.md +197 -720
  4. package/package.json +4 -4
  5. package/packages/cli/lib/audit.js +2 -2
  6. package/packages/cli/lib/auto-learn.js +8 -8
  7. package/packages/cli/lib/eslint-fix.js +1 -1
  8. package/packages/cli/lib/fix.js +5 -5
  9. package/packages/cli/lib/hooks/install-hooks.js +4 -4
  10. package/packages/cli/lib/hooks/lint-learn.js +4 -4
  11. package/packages/cli/lib/knowledge-index.js +4 -4
  12. package/packages/cli/lib/knowledge-metrics.js +2 -2
  13. package/packages/cli/lib/knowledge-retention.js +3 -3
  14. package/packages/cli/lib/knowledge-validator.js +3 -3
  15. package/packages/cli/lib/learn.js +10 -10
  16. package/packages/cli/lib/recall.js +1 -1
  17. package/packages/cli/lib/skill-learn.js +2 -2
  18. package/packages/cli/lib/stats.js +3 -3
  19. package/packages/cli/lib/ui/dashboard-ui.js +222 -0
  20. package/packages/cli/lib/ui/help-ui.js +41 -18
  21. package/packages/cli/lib/ui/index.js +57 -5
  22. package/packages/cli/lib/ui/settings-ui.js +292 -14
  23. package/packages/cli/lib/ui/stats-ui.js +93 -43
  24. package/packages/cli/lib/watcher.js +2 -2
  25. package/packages/kit/kit.js +89 -0
  26. package/packages/kit/lib/agents.js +208 -0
  27. package/packages/kit/lib/commands/analyze.js +70 -0
  28. package/packages/kit/lib/commands/cache.js +65 -0
  29. package/packages/kit/lib/commands/doctor.js +75 -0
  30. package/packages/kit/lib/commands/help.js +155 -0
  31. package/packages/kit/lib/commands/info.js +38 -0
  32. package/packages/kit/lib/commands/init.js +39 -0
  33. package/packages/kit/lib/commands/install.js +803 -0
  34. package/packages/kit/lib/commands/list.js +43 -0
  35. package/packages/kit/lib/commands/lock.js +57 -0
  36. package/packages/kit/lib/commands/uninstall.js +307 -0
  37. package/packages/kit/lib/commands/update.js +55 -0
  38. package/packages/kit/lib/commands/validate.js +69 -0
  39. package/packages/kit/lib/commands/verify.js +56 -0
  40. package/packages/kit/lib/config.js +81 -0
  41. package/packages/kit/lib/helpers.js +196 -0
  42. package/packages/kit/lib/helpers.test.js +60 -0
  43. package/packages/kit/lib/installer.js +164 -0
  44. package/packages/kit/lib/skills.js +119 -0
  45. package/packages/kit/lib/skills.test.js +109 -0
  46. package/packages/kit/lib/types.js +82 -0
  47. package/packages/kit/lib/ui.js +329 -0
  48. package/.agent/skills/mobile-design/scripts/mobile_audit.py +0 -670
  49. package/.agent/skills/requirements-python.txt +0 -25
  50. package/.agent/skills/requirements.txt +0 -96
  51. package/.agent/skills/typescript-expert/scripts/ts_diagnostic.py +0 -203
@@ -0,0 +1,803 @@
1
+ /**
2
+ * @fileoverview Install command - Interactive skill installation
3
+ */
4
+
5
+ import fs from "fs";
6
+ import path from "path";
7
+ import os from "os";
8
+ import { exec } from "child_process";
9
+ import util from "util";
10
+ const execAsync = util.promisify(exec);
11
+ import boxen from "boxen";
12
+ import { parseSkillSpec, merkleHash } from "../helpers.js";
13
+ import { parseSkillMdFrontmatter } from "../skills.js";
14
+ import { step, activeStep, stepLine, S, c, fatal, spinner, multiselect, select, confirm, isCancel, cancel } from "../ui.js";
15
+ import { WORKSPACE, GLOBAL_DIR, OFFLINE, GLOBAL } from "../config.js";
16
+ import { installSkill } from "../installer.js";
17
+
18
+ /**
19
+ * Install skills from repository
20
+ * @param {string} spec - Skill spec (org/repo or org/repo#skill)
21
+ */
22
+ export async function run(spec) {
23
+ if (!spec) {
24
+ fatal("Missing skill spec. Usage: add-skill <org/repo>");
25
+ return;
26
+ }
27
+
28
+ const { org, repo, skill: singleSkill, ref } = parseSkillSpec(spec);
29
+
30
+ if (!org || !repo) {
31
+ fatal("Invalid spec. Format: org/repo or org/repo#skill");
32
+ return;
33
+ }
34
+
35
+ // Check offline mode
36
+ if (OFFLINE) {
37
+ stepLine();
38
+ step(c.yellow("Offline mode enabled"), S.diamond, "yellow");
39
+ step(c.dim("Cannot install from remote repository in offline mode"), S.branch, "gray");
40
+ step(c.dim("Use --locked to install from lockfile instead"), S.branch, "gray");
41
+ return;
42
+ }
43
+
44
+ const url = `https://github.com/${org}/${repo}.git`;
45
+
46
+ stepLine();
47
+ step("Source: " + c.cyan(url));
48
+
49
+ const s = spinner();
50
+ s.start("Cloning repository");
51
+
52
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "add-skill-"));
53
+
54
+ // Retry logic with exponential backoff
55
+ const MAX_RETRIES = 3;
56
+ let lastError = null;
57
+
58
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
59
+ try {
60
+ await execAsync(`git clone --depth=1 ${url} "${tmp}"`, { timeout: 60000 });
61
+ if (ref) await execAsync(`git -C "${tmp}" checkout ${ref}`, { timeout: 30000 });
62
+ lastError = null;
63
+ break;
64
+ } catch (err) {
65
+ lastError = err;
66
+
67
+ if (attempt < MAX_RETRIES) {
68
+ const delay = Math.pow(2, attempt) * 1000; // 2s, 4s
69
+ s.message(`Retry ${attempt}/${MAX_RETRIES} in ${delay / 1000}s...`);
70
+ await new Promise(r => setTimeout(r, delay));
71
+
72
+ // Clean up failed attempt
73
+ try { fs.rmSync(tmp, { recursive: true, force: true }); } catch { }
74
+ fs.mkdirSync(tmp, { recursive: true });
75
+ }
76
+ }
77
+ }
78
+
79
+ if (lastError) {
80
+ s.fail("Failed to clone repository");
81
+ stepLine();
82
+
83
+ // Provide helpful error messages
84
+ const errMsg = lastError.message || "";
85
+ if (errMsg.includes("not found") || errMsg.includes("404")) {
86
+ step(c.red(`Repository not found: ${org}/${repo}`), S.cross, "red");
87
+ step(c.dim("Check if the repository exists and is public"), S.branch, "gray");
88
+ } else if (errMsg.includes("timeout")) {
89
+ step(c.red("Connection timeout"), S.cross, "red");
90
+ step(c.dim("Check your internet connection and try again"), S.branch, "gray");
91
+ } else if (errMsg.includes("Could not resolve")) {
92
+ step(c.red("Network error: Unable to reach GitHub"), S.cross, "red");
93
+ step(c.dim("Check your internet connection"), S.branch, "gray");
94
+ } else {
95
+ step(c.red("Clone failed: " + errMsg.split("\n")[0]), S.cross, "red");
96
+ }
97
+
98
+ fs.rmSync(tmp, { recursive: true, force: true });
99
+ return;
100
+ }
101
+
102
+ s.stop("Repository cloned");
103
+
104
+ // Find skills in repo - check multiple possible locations
105
+ const skillsInRepo = [];
106
+
107
+ // Possible skill locations (in order of priority)
108
+ const possibleSkillDirs = [
109
+ path.join(tmp, ".agent", "skills"), // Standard location
110
+ path.join(tmp, "skills"), // Alternative location
111
+ tmp // Root level (legacy)
112
+ ];
113
+
114
+ let skillsDir = null;
115
+ for (const dir of possibleSkillDirs) {
116
+ if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
117
+ // Check if this directory contains skill folders
118
+ const entries = fs.readdirSync(dir);
119
+ for (const e of entries) {
120
+ const sp = path.join(dir, e);
121
+ if (fs.statSync(sp).isDirectory() && fs.existsSync(path.join(sp, "SKILL.md"))) {
122
+ skillsDir = dir;
123
+ break;
124
+ }
125
+ }
126
+ if (skillsDir) break;
127
+ }
128
+ }
129
+
130
+ if (skillsDir) {
131
+ for (const e of fs.readdirSync(skillsDir)) {
132
+ const sp = path.join(skillsDir, e);
133
+ if (fs.statSync(sp).isDirectory()) {
134
+ // Check if this directory has SKILL.md (top-level skill only)
135
+ if (fs.existsSync(path.join(sp, "SKILL.md"))) {
136
+ const m = parseSkillMdFrontmatter(path.join(sp, "SKILL.md"));
137
+ skillsInRepo.push({
138
+ title: e, // Only show folder name
139
+ value: e,
140
+ description: m.description || "",
141
+ selected: singleSkill ? e === singleSkill : true,
142
+ _path: sp
143
+ });
144
+ }
145
+ // NOTE: Sub-folders are NOT counted as separate skills
146
+ // They are part of the parent skill (e.g. game-development/2d-games)
147
+ }
148
+ }
149
+ }
150
+
151
+ if (skillsInRepo.length === 0) {
152
+ step(c.yellow("No valid skills found"), S.diamond, "yellow");
153
+ step(c.dim("Expected skills in .agent/skills/ or skills/ directory"), S.branch, "gray");
154
+ fs.rmSync(tmp, { recursive: true, force: true });
155
+ return;
156
+ }
157
+
158
+ stepLine();
159
+ step(`Found ${skillsInRepo.length} skill${skillsInRepo.length > 1 ? "s" : ""}`);
160
+
161
+ let selectedSkills = [];
162
+
163
+ // If single skill specified via #skill_name, auto-select it
164
+ if (singleSkill) {
165
+ const found = skillsInRepo.find(s => s.value === singleSkill);
166
+ if (!found) {
167
+ stepLine();
168
+ step(c.red(`Skill '${singleSkill}' not found in repository`), S.cross, "red");
169
+ fs.rmSync(tmp, { recursive: true, force: true });
170
+ return;
171
+ }
172
+ selectedSkills = [singleSkill];
173
+ stepLine();
174
+ step(`Auto-selected: ${c.cyan(singleSkill)}`);
175
+ } else {
176
+ // FAANG-Grade Categories (8 balanced categories)
177
+ // NOTE: Order matters! Specialized categories FIRST, Core is fallback
178
+ const CATEGORY_KEYWORDS = {
179
+ // Specialized domains
180
+ "🎨 Frontend & UI": [
181
+ "frontend", "nextjs", "tailwind", "css", "ui", "ux", "visual",
182
+ "studio", "web-core", "design-system", "react-architect", "react"
183
+ ],
184
+ "🎮 Game Development": [
185
+ "game", "development", "engine", "unity", "unreal", "godot", "phaser"
186
+ ],
187
+ "📱 Mobile": [
188
+ "mobile", "first", "developer", "react-native", "flutter",
189
+ "ios", "android", "swift", "kotlin"
190
+ ],
191
+ "🔒 Security & DevOps": [
192
+ "security", "vulnerability", "offensive", "scanner", "red-team", "governance",
193
+ "cicd", "pipeline", "gitops", "docker", "deploy", "server-ops"
194
+ ],
195
+ // Technical domains
196
+ "🧪 Testing & Quality": [
197
+ "test", "testing", "tdd", "e2e", "debug", "quality", "review",
198
+ "lint", "validate", "automation", "problem", "checker"
199
+ ],
200
+ "🤖 AI & Agents": [
201
+ "agent", "pattern", "auto-learn", "execution", "self-evolution",
202
+ "lifecycle", "skill-forge", "intelligent", "routing"
203
+ ],
204
+ "📚 Docs & Planning": [
205
+ "doc", "template", "plan", "project", "idea", "brainstorm",
206
+ "geo", "seo", "i18n", "writing"
207
+ ],
208
+ // Fallback (core backend/infra)
209
+ "⚙️ Backend & Core": [
210
+ "backend", "api", "nodejs", "python", "server", "database",
211
+ "prisma", "mcp", "data", "architect", "scaffold", "system",
212
+ "typescript", "shell", "bash", "powershell", "git", "code-craft",
213
+ "code-constitution", "observability", "perf", "state", "rollback"
214
+ ]
215
+ };
216
+
217
+ function categorizeSkill(skillName) {
218
+ const lower = skillName.toLowerCase();
219
+ for (const [category, keywords] of Object.entries(CATEGORY_KEYWORDS)) {
220
+ if (keywords.some(kw => lower.includes(kw))) {
221
+ return category;
222
+ }
223
+ }
224
+ return "⚙️ Backend & Core"; // Default fallback (no "Other" category)
225
+ }
226
+
227
+ // REQUIRED SKILLS - Always installed, not shown in selection
228
+ const REQUIRED_SKILLS = ["auto-learner"];
229
+
230
+ // Filter out required skills from selection list
231
+ const selectableSkills = skillsInRepo.filter(s => !REQUIRED_SKILLS.includes(s.value));
232
+
233
+ // Group skills by category
234
+ const grouped = {};
235
+ for (const skill of selectableSkills) {
236
+ const cat = categorizeSkill(skill.value);
237
+ if (!grouped[cat]) grouped[cat] = [];
238
+ grouped[cat].push(skill);
239
+ }
240
+
241
+ // Custom sort: alphabetical but "Other" always last
242
+ const sortedCategories = Object.keys(grouped).sort((a, b) => {
243
+ if (a.includes("Other")) return 1;
244
+ if (b.includes("Other")) return -1;
245
+ return a.localeCompare(b);
246
+ });
247
+
248
+
249
+ stepLine();
250
+ activeStep("Select skill categories to install");
251
+
252
+ // Show only categories, not individual skills
253
+ const selectedCategories = await multiselect({
254
+ message: `${c.cyan("space")} select · ${c.cyan("enter")} confirm`,
255
+ options: sortedCategories.map(cat => ({
256
+ label: `${cat} (${grouped[cat].length} skills)`,
257
+ value: cat,
258
+ hint: grouped[cat].slice(0, 3).map(s => s.value).join(", ") + (grouped[cat].length > 3 ? "..." : "")
259
+ })),
260
+ initialValues: sortedCategories, // Pre-select all
261
+ required: true
262
+ });
263
+
264
+ if (isCancel(selectedCategories)) {
265
+ cancel("Cancelled.");
266
+ fs.rmSync(tmp, { recursive: true, force: true });
267
+ return;
268
+ }
269
+
270
+ // Get all skills from selected categories
271
+ selectedSkills = selectedCategories.flatMap(cat => grouped[cat].map(s => s.value));
272
+
273
+ // Add required system skills
274
+ const requiredInRepo = skillsInRepo.filter(s => REQUIRED_SKILLS.includes(s.value)).map(s => s.value);
275
+ selectedSkills = [...new Set([...selectedSkills, ...requiredInRepo])];
276
+ }
277
+
278
+ // Check for required skills and show info
279
+ const CORE_REQUIRED = ["auto-learner"];
280
+ const installedRequired = selectedSkills.filter(s => CORE_REQUIRED.includes(s));
281
+
282
+ stepLine();
283
+ step("Select skills to install");
284
+ console.log(`${c.gray(S.branch)} ${c.dim(selectedSkills.filter(s => !CORE_REQUIRED.includes(s)).join(", "))}`);
285
+ if (installedRequired.length > 0) {
286
+ console.log(`${c.gray(S.branch)} ${c.cyan("+ System required:")} ${c.green(installedRequired.join(", "))}`);
287
+ }
288
+
289
+ // --- Detect installed agents ---
290
+ stepLine();
291
+ const { detectInstalledAgents } = await import("../agents.js");
292
+ const detectedAgents = detectInstalledAgents();
293
+
294
+ if (detectedAgents.length === 0) {
295
+ step(c.yellow("No agents detected"), S.diamond, "yellow");
296
+ step(c.dim("Please install at least one AI agent (Antigravity, Claude Code, etc.)"), S.branch, "gray");
297
+ fs.rmSync(tmp, { recursive: true, force: true });
298
+ return;
299
+ }
300
+
301
+ step(`Detected ${detectedAgents.length} agents`);
302
+
303
+ // --- Select agents (Vercel-style) ---
304
+ const { selectAgentsPrompt, selectScopePrompt, selectMethodPrompt } = await import("../ui.js");
305
+
306
+ stepLine();
307
+ activeStep("Install to");
308
+ const selectedAgents = await selectAgentsPrompt(detectedAgents);
309
+
310
+ if (!selectedAgents || selectedAgents.length === 0) {
311
+ fs.rmSync(tmp, { recursive: true, force: true });
312
+ return;
313
+ }
314
+
315
+ stepLine();
316
+ step("Install to");
317
+ console.log(`${c.gray(S.branch)} ${c.dim(selectedAgents.map(a => a.displayName).join(", "))}`);
318
+
319
+ // --- Select installation scope ---
320
+ let isGlobal = GLOBAL;
321
+
322
+ if (!GLOBAL) {
323
+ stepLine();
324
+ activeStep("Installation scope");
325
+ const scope = await selectScopePrompt();
326
+
327
+ if (!scope) {
328
+ fs.rmSync(tmp, { recursive: true, force: true });
329
+ return;
330
+ }
331
+
332
+ isGlobal = scope === "global";
333
+ }
334
+
335
+ stepLine();
336
+ step("Installation scope");
337
+ console.log(`${c.gray(S.branch)} ${c.dim(isGlobal ? "Global" : "Project")}`);
338
+
339
+ // --- Select installation method ---
340
+ stepLine();
341
+ activeStep("Installation method");
342
+ const installMethod = await selectMethodPrompt();
343
+
344
+ if (!installMethod) {
345
+ fs.rmSync(tmp, { recursive: true, force: true });
346
+ return;
347
+ }
348
+
349
+
350
+ // Installation Summary Box
351
+ stepLine();
352
+ step("Installation method");
353
+ console.log(`${c.gray(S.branch)} ${c.dim(installMethod === "symlink" ? "Symlink" : "Copy")}`);
354
+
355
+ stepLine();
356
+ step("Installation Summary");
357
+ stepLine();
358
+
359
+ const agentsString = selectedAgents.map(a => a.displayName).join(", ");
360
+
361
+ let summaryContent = "";
362
+ const methodVerb = installMethod === "symlink" ? "symlink" : "copy";
363
+
364
+ for (const sn of selectedSkills) {
365
+ summaryContent += `${c.cyan(sn)}\n`;
366
+ summaryContent += ` ${c.dim(methodVerb)} ${c.gray("→")} ${c.dim(agentsString)}\n\n`;
367
+ }
368
+
369
+ // Remove trailing newlines
370
+ summaryContent = summaryContent.trim();
371
+
372
+ console.log(boxen(summaryContent, {
373
+ padding: 1,
374
+ borderColor: "gray",
375
+ borderStyle: "round",
376
+ dimBorder: true,
377
+ title: "Installation Summary",
378
+ titleAlignment: "left"
379
+ }).split("\n").map(l => `${c.gray(S.branch)} ${l}`).join("\n"));
380
+
381
+ stepLine();
382
+
383
+ // Confirmation
384
+ activeStep("Proceed with installation?");
385
+ const shouldProceed = await confirm({ message: " ", initialValue: true });
386
+
387
+ if (isCancel(shouldProceed) || !shouldProceed) {
388
+ cancel("Cancelled.");
389
+ fs.rmSync(tmp, { recursive: true, force: true });
390
+ return;
391
+ }
392
+
393
+ // Install skills to multiple agents
394
+ stepLine();
395
+ const { installSkillForAgents } = await import("../installer.js");
396
+
397
+ // Create a map for skill paths
398
+ const skillPathMap = Object.fromEntries(skillsInRepo.map(s => [s.value, s._path]));
399
+
400
+ const installResults = { success: [], failed: [] };
401
+
402
+ for (const sn of selectedSkills) {
403
+ const src = skillPathMap[sn] || path.join(skillsDir || tmp, sn);
404
+
405
+ const is = spinner();
406
+ is.start(`Installing ${sn} to ${selectedAgents.length} agents`);
407
+
408
+ const result = await installSkillForAgents(src, sn, selectedAgents, {
409
+ method: installMethod,
410
+ scope: isGlobal ? "global" : "project",
411
+ metadata: {
412
+ repo: `${org}/${repo}`,
413
+ ref: ref || null
414
+ }
415
+ });
416
+
417
+ installResults.success.push(...result.success);
418
+ installResults.failed.push(...result.failed);
419
+
420
+ if (result.failed.length === 0) {
421
+ is.stop(`Installed ${sn} (${result.success.length} agents)`);
422
+ } else {
423
+ is.stop(`${sn}: ${result.success.length} success, ${result.failed.length} failed`);
424
+ }
425
+ }
426
+
427
+
428
+ // Derive base .agent directory from skillsDir
429
+ // If skillsDir is .../skills, then baseAgentDir is parent (.agent)
430
+ const baseAgentDir = skillsDir ? path.dirname(skillsDir) : path.join(tmp, ".agent");
431
+
432
+ // Install workflows if they exist
433
+ const workflowsDir = path.join(baseAgentDir, "workflows");
434
+ const targetWorkflowsDir = path.join(WORKSPACE, "..", "workflows");
435
+ let workflowsInstalled = 0;
436
+
437
+
438
+ if (fs.existsSync(workflowsDir)) {
439
+ stepLine();
440
+ const ws = spinner();
441
+ ws.start("Installing workflows");
442
+
443
+ fs.mkdirSync(targetWorkflowsDir, { recursive: true });
444
+ const workflows = fs.readdirSync(workflowsDir).filter(f => f.endsWith(".md"));
445
+
446
+ for (const wf of workflows) {
447
+ const src = path.join(workflowsDir, wf);
448
+ const dest = path.join(targetWorkflowsDir, wf);
449
+
450
+ if (!fs.existsSync(dest)) {
451
+ fs.copyFileSync(src, dest);
452
+ workflowsInstalled++;
453
+ }
454
+ }
455
+
456
+ ws.stop(`Installed ${workflowsInstalled} workflows`);
457
+ }
458
+
459
+ // Install GEMINI.md if it exists
460
+ const geminiSrc = path.join(baseAgentDir, "GEMINI.md");
461
+ const geminiDest = path.join(WORKSPACE, "..", "GEMINI.md");
462
+ let geminiInstalled = false;
463
+
464
+ if (fs.existsSync(geminiSrc) && !fs.existsSync(geminiDest)) {
465
+ stepLine();
466
+ fs.copyFileSync(geminiSrc, geminiDest);
467
+ step("Installed GEMINI.md (Agent Rules)");
468
+ geminiInstalled = true;
469
+ }
470
+
471
+ // Install agents if they exist
472
+ const agentsDir = path.join(baseAgentDir, "agents");
473
+ const targetAgentsDir = path.join(WORKSPACE, "..", "agents");
474
+ let agentsInstalled = 0;
475
+
476
+ if (fs.existsSync(agentsDir)) {
477
+ stepLine();
478
+ const as = spinner();
479
+ as.start("Installing agents");
480
+
481
+ fs.mkdirSync(targetAgentsDir, { recursive: true });
482
+ const agents = fs.readdirSync(agentsDir).filter(f => f.endsWith(".md"));
483
+
484
+ for (const agent of agents) {
485
+ const src = path.join(agentsDir, agent);
486
+ const dest = path.join(targetAgentsDir, agent);
487
+
488
+ if (!fs.existsSync(dest)) {
489
+ fs.copyFileSync(src, dest);
490
+ agentsInstalled++;
491
+ }
492
+ }
493
+
494
+ as.stop(`Installed ${agentsInstalled} agents`);
495
+ }
496
+
497
+ // Install ARCHITECTURE.md if it exists
498
+ const archSrc = path.join(baseAgentDir, "ARCHITECTURE.md");
499
+ const archDest = path.join(WORKSPACE, "..", "ARCHITECTURE.md");
500
+ let archInstalled = false;
501
+
502
+ if (fs.existsSync(archSrc) && !fs.existsSync(archDest)) {
503
+ fs.copyFileSync(archSrc, archDest);
504
+ step("Installed ARCHITECTURE.md");
505
+ archInstalled = true;
506
+ }
507
+
508
+ // Install knowledge if it exists (required for Agent CLI)
509
+ const knowledgeDir = path.join(baseAgentDir, "knowledge");
510
+ const targetKnowledgeDir = path.join(WORKSPACE, "..", "knowledge");
511
+ let knowledgeInstalled = false;
512
+
513
+ if (fs.existsSync(knowledgeDir) && !fs.existsSync(targetKnowledgeDir)) {
514
+ fs.cpSync(knowledgeDir, targetKnowledgeDir, { recursive: true });
515
+ step("Installed knowledge/");
516
+ knowledgeInstalled = true;
517
+ } else if (!fs.existsSync(targetKnowledgeDir)) {
518
+ // Create empty knowledge folder for Agent CLI
519
+ fs.mkdirSync(targetKnowledgeDir, { recursive: true });
520
+ // Create minimal structure for Agent CLI
521
+ fs.writeFileSync(path.join(targetKnowledgeDir, "lessons-learned.yaml"), "# Lessons learned by AI Agent\nlessons: []\n");
522
+ step("Created knowledge/ (Agent CLI ready)");
523
+ knowledgeInstalled = true;
524
+ }
525
+
526
+ // Install config/ if it exists (required for skill configuration)
527
+ const configDir = path.join(baseAgentDir, "config");
528
+ const targetConfigDir = path.join(WORKSPACE, "..", "config");
529
+ let configInstalled = false;
530
+
531
+ if (fs.existsSync(configDir) && !fs.existsSync(targetConfigDir)) {
532
+ fs.cpSync(configDir, targetConfigDir, { recursive: true });
533
+ step("Installed config/");
534
+ configInstalled = true;
535
+ }
536
+
537
+ // Install scripts-js/ if it exists (required for skill operations)
538
+ const scriptsJsDir = path.join(baseAgentDir, "scripts-js");
539
+ const targetScriptsJsDir = path.join(WORKSPACE, "..", "scripts-js");
540
+ let scriptsJsInstalled = false;
541
+
542
+ if (fs.existsSync(scriptsJsDir) && !fs.existsSync(targetScriptsJsDir)) {
543
+ fs.cpSync(scriptsJsDir, targetScriptsJsDir, { recursive: true });
544
+ step("Installed scripts-js/");
545
+ scriptsJsInstalled = true;
546
+ }
547
+
548
+ // Install metrics/ if it exists (for agent performance tracking)
549
+ const metricsDir = path.join(baseAgentDir, "metrics");
550
+ const targetMetricsDir = path.join(WORKSPACE, "..", "metrics");
551
+ let metricsInstalled = false;
552
+
553
+ if (fs.existsSync(metricsDir) && !fs.existsSync(targetMetricsDir)) {
554
+ fs.cpSync(metricsDir, targetMetricsDir, { recursive: true });
555
+ step("Installed metrics/");
556
+ metricsInstalled = true;
557
+ }
558
+
559
+ // Install additional policy documents
560
+ const policyDocs = [
561
+ "CONTINUOUS_EXECUTION_POLICY.md",
562
+ "WORKFLOW_CHAINS.md"
563
+ ];
564
+ let policyDocsInstalled = 0;
565
+
566
+ for (const doc of policyDocs) {
567
+ const docSrc = path.join(baseAgentDir, doc);
568
+ const docDest = path.join(WORKSPACE, "..", doc);
569
+ if (fs.existsSync(docSrc) && !fs.existsSync(docDest)) {
570
+ fs.copyFileSync(docSrc, docDest);
571
+ policyDocsInstalled++;
572
+ }
573
+ }
574
+ if (policyDocsInstalled > 0) {
575
+ step(`Installed ${policyDocsInstalled} policy docs`);
576
+ }
577
+
578
+ // Install rules if they exist
579
+ const rulesDir = path.join(baseAgentDir, "rules");
580
+ const targetRulesDir = path.join(WORKSPACE, "..", "rules");
581
+ let rulesInstalled = 0;
582
+
583
+ if (fs.existsSync(rulesDir)) {
584
+ fs.mkdirSync(targetRulesDir, { recursive: true });
585
+ const rules = fs.readdirSync(rulesDir).filter(f => f.endsWith(".md"));
586
+
587
+ for (const rule of rules) {
588
+ const src = path.join(rulesDir, rule);
589
+ const dest = path.join(targetRulesDir, rule);
590
+
591
+ if (!fs.existsSync(dest)) {
592
+ fs.copyFileSync(src, dest);
593
+ rulesInstalled++;
594
+ }
595
+ }
596
+
597
+ if (rulesInstalled > 0) {
598
+ step(`Installed ${rulesInstalled} rules`);
599
+ }
600
+ }
601
+
602
+ // Install .shared if it exists (contains shared resources like ui-ux-pro-max data)
603
+ const sharedDir = path.join(tmp, ".agent", ".shared");
604
+ const targetSharedDir = path.join(WORKSPACE, "..", ".shared");
605
+ let sharedInstalled = false;
606
+
607
+ if (fs.existsSync(sharedDir) && !fs.existsSync(targetSharedDir)) {
608
+ stepLine();
609
+ const ss = spinner();
610
+ ss.start("Installing shared resources");
611
+
612
+ fs.cpSync(sharedDir, targetSharedDir, { recursive: true });
613
+ sharedInstalled = true;
614
+
615
+ ss.stop("Installed .shared/ (ui-ux-pro-max data)");
616
+ }
617
+
618
+ // Installation complete step
619
+ stepLine();
620
+ step("Installation complete");
621
+
622
+ // Final Success Box
623
+ stepLine();
624
+ console.log(`${c.gray(S.branch)}`); // Extra spacing line
625
+
626
+ let successContent = "";
627
+
628
+ // Skills summary
629
+ for (const sn of selectedSkills) {
630
+ const mockPath = `.agent/skills/${sn}`;
631
+ successContent += `${c.cyan("✓")} ${c.dim(mockPath)}\n`;
632
+ }
633
+
634
+ // Workflows summary
635
+ if (workflowsInstalled > 0) {
636
+ successContent += `${c.cyan("✓")} ${c.dim(`.agent/workflows/ (${workflowsInstalled} files)`)}\n`;
637
+ }
638
+
639
+ // Agents summary
640
+ if (agentsInstalled > 0) {
641
+ successContent += `${c.cyan("✓")} ${c.dim(`.agent/agents/ (${agentsInstalled} files)`)}\n`;
642
+ }
643
+
644
+ // GEMINI.md summary
645
+ if (geminiInstalled) {
646
+ successContent += `${c.cyan("✓")} ${c.dim(".agent/GEMINI.md (Agent Rules)")}\n`;
647
+ }
648
+
649
+ // ARCHITECTURE.md summary
650
+ if (archInstalled) {
651
+ successContent += `${c.cyan("✓")} ${c.dim(".agent/ARCHITECTURE.md")}\n`;
652
+ }
653
+
654
+ // Knowledge summary
655
+ if (knowledgeInstalled) {
656
+ successContent += `${c.cyan("✓")} ${c.dim(".agent/knowledge/")}\n`;
657
+ }
658
+
659
+ // Rules summary
660
+ if (rulesInstalled > 0) {
661
+ successContent += `${c.cyan("✓")} ${c.dim(`.agent/rules/ (${rulesInstalled} files)`)}\n`;
662
+ }
663
+
664
+ // Shared resources summary
665
+ if (sharedInstalled) {
666
+ successContent += `${c.cyan("✓")} ${c.dim(".agent/.shared/ (ui-ux-pro-max data)")}\n`;
667
+ }
668
+
669
+ // Build title
670
+ const parts = [`${selectedSkills.length} skills`];
671
+ if (workflowsInstalled > 0) parts.push(`${workflowsInstalled} workflows`);
672
+ if (agentsInstalled > 0) parts.push(`${agentsInstalled} agents`);
673
+ if (geminiInstalled) parts.push("GEMINI.md");
674
+
675
+ console.log(boxen(successContent.trim(), {
676
+ padding: 1,
677
+ borderColor: "cyan",
678
+ borderStyle: "round",
679
+ title: c.cyan(`Installed ${parts.join(", ")}`),
680
+ titleAlignment: "left"
681
+ }).split("\n").map(l => `${c.gray(S.branch)} ${l}`).join("\n"));
682
+
683
+ fs.rmSync(tmp, { recursive: true, force: true });
684
+
685
+ // Ask user about AutoLearn installation
686
+ stepLine();
687
+ const installAutoLearn = await confirm({
688
+ message: "Install AutoLearn (enables 'agent' command for learning & self-improvement)?",
689
+ initialValue: true
690
+ });
691
+
692
+ if (isCancel(installAutoLearn)) {
693
+ cancel("Installation cancelled");
694
+ process.exit(0);
695
+ }
696
+
697
+ // Install CLI package
698
+ stepLine();
699
+ const cliSpinner = spinner();
700
+ const cliPackage = "add-skill-kit";
701
+
702
+ if (isGlobal) {
703
+ if (installAutoLearn) {
704
+ cliSpinner.start(`Installing CLI globally (${cliPackage})`);
705
+ } else {
706
+ cliSpinner.start(`Installing kit CLI globally (${cliPackage})`);
707
+ }
708
+ try {
709
+ await execAsync(`npm install -g ${cliPackage}`, { timeout: 120000 });
710
+ cliSpinner.stop("CLI installed globally");
711
+ if (installAutoLearn) {
712
+ step(c.dim("Commands: agent, kit"));
713
+ } else {
714
+ step(c.dim("Command: kit"));
715
+ }
716
+ } catch (e) {
717
+ cliSpinner.stop(c.yellow("Global CLI installation failed"));
718
+ step(c.dim(`Try running manually: npm i -g ${cliPackage}`));
719
+ }
720
+ } else {
721
+ cliSpinner.start(`Installing Agent CLI locally (${cliPackage})`);
722
+ try {
723
+ await execAsync(`npm install -D ${cliPackage}`, { timeout: 120000 });
724
+ cliSpinner.stop("CLI installed locally");
725
+
726
+ // Add npm scripts to package.json
727
+ try {
728
+ const pkgPath = path.join(process.cwd(), "package.json");
729
+ if (fs.existsSync(pkgPath)) {
730
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
731
+ pkg.scripts = pkg.scripts || {};
732
+
733
+ // Always add kit script
734
+ if (!pkg.scripts.kit) {
735
+ pkg.scripts.kit = "kit";
736
+ }
737
+
738
+ // Add agent script only if AutoLearn enabled
739
+ if (installAutoLearn && !pkg.scripts.agent) {
740
+ pkg.scripts.agent = "agent";
741
+ }
742
+
743
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
744
+ if (installAutoLearn) {
745
+ step(c.green("✓ Added npm scripts: 'agent', 'kit'"));
746
+ } else {
747
+ step(c.green("✓ Added npm script: 'kit'"));
748
+ }
749
+ }
750
+ } catch (scriptErr) {
751
+ step(c.yellow("⚠ Could not add npm scripts automatically"));
752
+ }
753
+
754
+ // Create wrapper scripts for direct command access (Windows + Unix)
755
+ try {
756
+ const projectRoot = process.cwd();
757
+
758
+ // Always create kit wrappers
759
+ const kitCmd = `@echo off\nnode "%~dp0node_modules\\add-skill-kit\\bin\\kit.js" %*`;
760
+ const kitSh = `#!/bin/sh\nnode "$(dirname "$0")/node_modules/add-skill-kit/bin/kit.js" "$@"`;
761
+ fs.writeFileSync(path.join(projectRoot, "kit.cmd"), kitCmd);
762
+ fs.writeFileSync(path.join(projectRoot, "kit"), kitSh, { mode: 0o755 });
763
+
764
+ if (installAutoLearn) {
765
+ // Create agent wrappers only if AutoLearn enabled
766
+ const agentCmd = `@echo off\nnode "%~dp0node_modules\\add-skill-kit\\lib\\agent-cli\\bin\\agent.js" %*`;
767
+ const agentSh = `#!/bin/sh\nnode "$(dirname "$0")/node_modules/add-skill-kit/lib/agent-cli/bin/agent.js" "$@"`;
768
+ fs.writeFileSync(path.join(projectRoot, "agent.cmd"), agentCmd);
769
+ fs.writeFileSync(path.join(projectRoot, "agent"), agentSh, { mode: 0o755 });
770
+
771
+ step(c.green("✓ Created wrapper scripts: agent, kit"));
772
+ step(c.dim("Run directly: ./agent or ./kit (Unix) | agent or kit (Windows)"));
773
+ } else {
774
+ step(c.green("✓ Created wrapper script: kit"));
775
+ step(c.dim("Run directly: ./kit (Unix) | kit (Windows)"));
776
+ }
777
+ } catch (wrapperErr) {
778
+ step(c.dim("Run: npx kit"));
779
+ }
780
+ } catch (e) {
781
+ cliSpinner.stop(c.yellow("Local CLI installation skipped"));
782
+ step(c.dim(`Run manually: npm i -D ${cliPackage}`));
783
+ }
784
+ }
785
+
786
+ // Run npm install to ensure all skill dependencies are available
787
+ stepLine();
788
+ const depsSpinner = spinner();
789
+ depsSpinner.start("Installing skill dependencies (csv-parse, etc.)");
790
+ try {
791
+ await execAsync("npm install", { timeout: 120000 });
792
+ depsSpinner.stop("Skill dependencies installed");
793
+ } catch (e) {
794
+ depsSpinner.stop(c.yellow("Dependencies installation skipped"));
795
+ step(c.dim("Run manually: npm install"));
796
+ }
797
+
798
+ // Python dependencies no longer needed - all scripts migrated to JS
799
+
800
+ stepLine();
801
+ console.log(` ${c.cyan("Done!")}`);
802
+ console.log();
803
+ }