@skillkit/cli 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3802,10 +3802,10 @@ ${learning.title}
3802
3802
  return 0;
3803
3803
  }
3804
3804
  const outputPath = this.output || `.skillkit/exports/${skillName}/SKILL.md`;
3805
- const { dirname: dirname3 } = await import("path");
3806
- const { existsSync: existsSync13, mkdirSync: mkdirSync7, writeFileSync: writeFileSync6 } = await import("fs");
3807
- const outputDir = dirname3(outputPath);
3808
- if (!existsSync13(outputDir)) {
3805
+ const { dirname: dirname5 } = await import("path");
3806
+ const { existsSync: existsSync14, mkdirSync: mkdirSync7, writeFileSync: writeFileSync6 } = await import("fs");
3807
+ const outputDir = dirname5(outputPath);
3808
+ if (!existsSync14(outputDir)) {
3809
3809
  mkdirSync7(outputDir, { recursive: true });
3810
3810
  }
3811
3811
  writeFileSync6(outputPath, skillContent, "utf-8");
@@ -3823,10 +3823,10 @@ ${learning.title}
3823
3823
  console.log(chalk23.gray("Usage: skillkit memory import --input <path>"));
3824
3824
  return 1;
3825
3825
  }
3826
- const { existsSync: existsSync13, readFileSync: readFileSync7 } = await import("fs");
3827
- const { resolve: resolve12 } = await import("path");
3828
- const fullPath = resolve12(inputPath);
3829
- if (!existsSync13(fullPath)) {
3826
+ const { existsSync: existsSync14, readFileSync: readFileSync7 } = await import("fs");
3827
+ const { resolve: resolve16 } = await import("path");
3828
+ const fullPath = resolve16(inputPath);
3829
+ if (!existsSync14(fullPath)) {
3830
3830
  console.error(chalk23.red(`File not found: ${fullPath}`));
3831
3831
  return 1;
3832
3832
  }
@@ -4905,8 +4905,8 @@ var TeamCommand = class extends Command27 {
4905
4905
  }
4906
4906
  const projectPath = process.cwd();
4907
4907
  const bundlePath = join9(projectPath, ".skillkit", "bundles", `${this.name}.json`);
4908
- const { existsSync: existsSync13, readFileSync: readFileSync7, writeFileSync: writeFileSync6 } = await import("fs");
4909
- if (!existsSync13(bundlePath)) {
4908
+ const { existsSync: existsSync14, readFileSync: readFileSync7, writeFileSync: writeFileSync6 } = await import("fs");
4909
+ if (!existsSync14(bundlePath)) {
4910
4910
  this.context.stderr.write(chalk26.red(`Bundle "${this.name}" not found. Create it first with bundle-create.
4911
4911
  `));
4912
4912
  return 1;
@@ -4922,8 +4922,8 @@ var TeamCommand = class extends Command27 {
4922
4922
  this.context.stderr.write(chalk26.red("--source <path> is required for bundle-import\n"));
4923
4923
  return 1;
4924
4924
  }
4925
- const { existsSync: existsSync13 } = await import("fs");
4926
- if (!existsSync13(this.source)) {
4925
+ const { existsSync: existsSync14 } = await import("fs");
4926
+ if (!existsSync14(this.source)) {
4927
4927
  this.context.stderr.write(chalk26.red(`Bundle file not found: ${this.source}
4928
4928
  `));
4929
4929
  return 1;
@@ -5235,18 +5235,1985 @@ var PluginCommand = class extends Command28 {
5235
5235
  return 0;
5236
5236
  }
5237
5237
  };
5238
+
5239
+ // src/commands/methodology.ts
5240
+ import { Command as Command29, Option as Option28 } from "clipanion";
5241
+ import chalk28 from "chalk";
5242
+ import { createMethodologyManager, createMethodologyLoader } from "@skillkit/core";
5243
+ var MethodologyCommand = class extends Command29 {
5244
+ static paths = [["methodology"]];
5245
+ static usage = Command29.Usage({
5246
+ description: "Manage methodology packs for AI coding agents",
5247
+ examples: [
5248
+ ["List available packs", "$0 methodology list"],
5249
+ ["Install all methodology packs", "$0 methodology install"],
5250
+ ["Install specific pack", "$0 methodology install testing"],
5251
+ ["Install specific skill", "$0 methodology install testing/red-green-refactor"],
5252
+ ["Sync methodology skills to agents", "$0 methodology sync"],
5253
+ ["Search methodology skills", "$0 methodology search tdd"],
5254
+ ["Show pack details", "$0 methodology info testing"]
5255
+ ]
5256
+ });
5257
+ action = Option28.String({ required: true });
5258
+ target = Option28.String({ required: false });
5259
+ agent = Option28.String("--agent,-a", { description: "Target agent for sync" });
5260
+ dryRun = Option28.Boolean("--dry-run", { description: "Preview without making changes" });
5261
+ verbose = Option28.Boolean("--verbose,-v", { description: "Show detailed output" });
5262
+ async execute() {
5263
+ const projectPath = process.cwd();
5264
+ try {
5265
+ switch (this.action) {
5266
+ case "list":
5267
+ return await this.listPacks();
5268
+ case "install":
5269
+ return await this.installPacks(projectPath);
5270
+ case "uninstall":
5271
+ return await this.uninstallPack(projectPath);
5272
+ case "sync":
5273
+ return await this.syncSkills(projectPath);
5274
+ case "search":
5275
+ return await this.searchSkills();
5276
+ case "info":
5277
+ return await this.showPackInfo();
5278
+ case "installed":
5279
+ return await this.listInstalled(projectPath);
5280
+ default:
5281
+ this.context.stderr.write(chalk28.red(`Unknown action: ${this.action}
5282
+ `));
5283
+ this.context.stderr.write("Available actions: list, install, uninstall, sync, search, info, installed\n");
5284
+ return 1;
5285
+ }
5286
+ } catch (err) {
5287
+ this.context.stderr.write(chalk28.red(`\u2717 ${err instanceof Error ? err.message : "Unknown error"}
5288
+ `));
5289
+ return 1;
5290
+ }
5291
+ }
5292
+ async listPacks() {
5293
+ const loader = createMethodologyLoader();
5294
+ const packs = await loader.loadAllPacks();
5295
+ if (packs.length === 0) {
5296
+ this.context.stdout.write("No methodology packs available.\n");
5297
+ return 0;
5298
+ }
5299
+ this.context.stdout.write(chalk28.cyan("Available Methodology Packs:\n\n"));
5300
+ for (const pack of packs) {
5301
+ this.context.stdout.write(chalk28.green(` ${pack.name}`) + ` v${pack.version}
5302
+ `);
5303
+ this.context.stdout.write(chalk28.gray(` ${pack.description}
5304
+ `));
5305
+ this.context.stdout.write(` Skills: ${pack.skills.join(", ")}
5306
+ `);
5307
+ this.context.stdout.write(` Tags: ${pack.tags.join(", ")}
5308
+ `);
5309
+ this.context.stdout.write("\n");
5310
+ }
5311
+ this.context.stdout.write(`Total: ${packs.length} packs
5312
+ `);
5313
+ this.context.stdout.write(chalk28.gray("\nRun `skillkit methodology install` to install all packs.\n"));
5314
+ return 0;
5315
+ }
5316
+ async installPacks(projectPath) {
5317
+ const manager = createMethodologyManager({ projectPath });
5318
+ const loader = manager.getLoader();
5319
+ if (this.target) {
5320
+ if (this.target.includes("/")) {
5321
+ this.context.stdout.write(`Installing skill: ${chalk28.cyan(this.target)}...
5322
+ `);
5323
+ if (this.dryRun) {
5324
+ this.context.stdout.write(chalk28.yellow("[dry-run] Would install skill.\n"));
5325
+ return 0;
5326
+ }
5327
+ const result = await manager.installSkill(this.target);
5328
+ if (result.success) {
5329
+ this.context.stdout.write(chalk28.green(`\u2713 Skill installed: ${this.target}
5330
+ `));
5331
+ } else {
5332
+ for (const failed of result.failed) {
5333
+ this.context.stderr.write(chalk28.red(`\u2717 ${failed.name}: ${failed.error}
5334
+ `));
5335
+ }
5336
+ return 1;
5337
+ }
5338
+ } else {
5339
+ this.context.stdout.write(`Installing pack: ${chalk28.cyan(this.target)}...
5340
+ `);
5341
+ if (this.dryRun) {
5342
+ const pack = await loader.loadPack(this.target);
5343
+ if (pack) {
5344
+ this.context.stdout.write(chalk28.yellow(`[dry-run] Would install ${pack.skills.length} skills.
5345
+ `));
5346
+ }
5347
+ return 0;
5348
+ }
5349
+ const result = await manager.installPack(this.target);
5350
+ if (result.success) {
5351
+ this.context.stdout.write(chalk28.green(`\u2713 Pack "${this.target}" installed!
5352
+ `));
5353
+ this.context.stdout.write(` Installed: ${result.installed.length} skills
5354
+ `);
5355
+ if (result.skipped.length > 0) {
5356
+ this.context.stdout.write(` Skipped (already installed): ${result.skipped.length}
5357
+ `);
5358
+ }
5359
+ } else {
5360
+ this.context.stderr.write(chalk28.red(`\u2717 Failed to install pack
5361
+ `));
5362
+ for (const failed of result.failed) {
5363
+ this.context.stderr.write(chalk28.red(` - ${failed.name}: ${failed.error}
5364
+ `));
5365
+ }
5366
+ return 1;
5367
+ }
5368
+ }
5369
+ } else {
5370
+ this.context.stdout.write("Installing all methodology packs...\n\n");
5371
+ if (this.dryRun) {
5372
+ const packs = await loader.loadAllPacks();
5373
+ let totalSkills = 0;
5374
+ for (const pack of packs) {
5375
+ this.context.stdout.write(chalk28.yellow(`[dry-run] Would install ${pack.name} (${pack.skills.length} skills)
5376
+ `));
5377
+ totalSkills += pack.skills.length;
5378
+ }
5379
+ this.context.stdout.write(chalk28.yellow(`
5380
+ [dry-run] Would install ${totalSkills} skills total.
5381
+ `));
5382
+ return 0;
5383
+ }
5384
+ const result = await manager.installAllPacks();
5385
+ if (result.success) {
5386
+ this.context.stdout.write(chalk28.green("\n\u2713 All methodology packs installed!\n"));
5387
+ this.context.stdout.write(` Installed: ${result.installed.length} skills
5388
+ `);
5389
+ if (result.skipped.length > 0) {
5390
+ this.context.stdout.write(` Skipped (already installed): ${result.skipped.length}
5391
+ `);
5392
+ }
5393
+ } else {
5394
+ this.context.stdout.write(chalk28.yellow("\n\u26A0 Some skills failed to install:\n"));
5395
+ for (const failed of result.failed) {
5396
+ this.context.stderr.write(chalk28.red(` - ${failed.name}: ${failed.error}
5397
+ `));
5398
+ }
5399
+ this.context.stdout.write(`
5400
+ Installed: ${result.installed.length} skills
5401
+ `);
5402
+ }
5403
+ }
5404
+ this.context.stdout.write(chalk28.gray("\nRun `skillkit methodology sync` to sync to detected agents.\n"));
5405
+ return 0;
5406
+ }
5407
+ async uninstallPack(projectPath) {
5408
+ const manager = createMethodologyManager({ projectPath });
5409
+ if (!this.target) {
5410
+ this.context.stderr.write(chalk28.red("Pack name required for uninstall.\n"));
5411
+ this.context.stderr.write("Usage: skillkit methodology uninstall <pack-name>\n");
5412
+ return 1;
5413
+ }
5414
+ if (this.dryRun) {
5415
+ this.context.stdout.write(chalk28.yellow(`[dry-run] Would uninstall pack: ${this.target}
5416
+ `));
5417
+ return 0;
5418
+ }
5419
+ await manager.uninstallPack(this.target);
5420
+ this.context.stdout.write(chalk28.green(`\u2713 Pack "${this.target}" uninstalled.
5421
+ `));
5422
+ return 0;
5423
+ }
5424
+ async syncSkills(projectPath) {
5425
+ const manager = createMethodologyManager({ projectPath, autoSync: false });
5426
+ this.context.stdout.write("Syncing methodology skills to detected agents...\n\n");
5427
+ if (this.dryRun) {
5428
+ const installed = manager.listInstalledSkills();
5429
+ this.context.stdout.write(chalk28.yellow(`[dry-run] Would sync ${installed.length} skills.
5430
+ `));
5431
+ return 0;
5432
+ }
5433
+ const result = await manager.syncAll();
5434
+ if (result.synced.length > 0) {
5435
+ this.context.stdout.write(chalk28.green("\u2713 Sync complete!\n"));
5436
+ for (const sync of result.synced) {
5437
+ this.context.stdout.write(` ${sync.skill} \u2192 ${sync.agents.join(", ")}
5438
+ `);
5439
+ }
5440
+ }
5441
+ if (result.failed.length > 0) {
5442
+ this.context.stdout.write(chalk28.yellow("\n\u26A0 Some syncs failed:\n"));
5443
+ for (const fail of result.failed) {
5444
+ this.context.stderr.write(chalk28.red(` ${fail.skill} (${fail.agent}): ${fail.error}
5445
+ `));
5446
+ }
5447
+ }
5448
+ if (result.synced.length === 0 && result.failed.length === 0) {
5449
+ this.context.stdout.write("No installed skills to sync. Run `skillkit methodology install` first.\n");
5450
+ }
5451
+ return result.success ? 0 : 1;
5452
+ }
5453
+ async searchSkills() {
5454
+ const loader = createMethodologyLoader();
5455
+ if (!this.target) {
5456
+ this.context.stderr.write(chalk28.red("Search query required.\n"));
5457
+ this.context.stderr.write("Usage: skillkit methodology search <query>\n");
5458
+ return 1;
5459
+ }
5460
+ const skills = await loader.searchSkills(this.target);
5461
+ if (skills.length === 0) {
5462
+ this.context.stdout.write(`No skills found matching "${this.target}".
5463
+ `);
5464
+ return 0;
5465
+ }
5466
+ this.context.stdout.write(chalk28.cyan(`Found ${skills.length} skills matching "${this.target}":
5467
+
5468
+ `));
5469
+ for (const skill of skills) {
5470
+ this.context.stdout.write(chalk28.green(` ${skill.id}`) + ` v${skill.version}
5471
+ `);
5472
+ this.context.stdout.write(chalk28.gray(` ${skill.description || "No description"}
5473
+ `));
5474
+ if (skill.tags.length > 0) {
5475
+ this.context.stdout.write(` Tags: ${skill.tags.join(", ")}
5476
+ `);
5477
+ }
5478
+ if (this.verbose && skill.metadata.triggers) {
5479
+ this.context.stdout.write(` Triggers: ${skill.metadata.triggers.join(", ")}
5480
+ `);
5481
+ }
5482
+ this.context.stdout.write("\n");
5483
+ }
5484
+ return 0;
5485
+ }
5486
+ async showPackInfo() {
5487
+ const loader = createMethodologyLoader();
5488
+ if (!this.target) {
5489
+ this.context.stderr.write(chalk28.red("Pack name required.\n"));
5490
+ this.context.stderr.write("Usage: skillkit methodology info <pack-name>\n");
5491
+ return 1;
5492
+ }
5493
+ const pack = await loader.loadPack(this.target);
5494
+ if (!pack) {
5495
+ this.context.stderr.write(chalk28.red(`Pack not found: ${this.target}
5496
+ `));
5497
+ return 1;
5498
+ }
5499
+ this.context.stdout.write(chalk28.cyan(`
5500
+ Pack: ${pack.name}
5501
+ `));
5502
+ this.context.stdout.write(`Version: ${pack.version}
5503
+ `);
5504
+ this.context.stdout.write(`Description: ${pack.description}
5505
+ `);
5506
+ this.context.stdout.write(`Tags: ${pack.tags.join(", ")}
5507
+ `);
5508
+ this.context.stdout.write(`Author: ${pack.author || "Unknown"}
5509
+ `);
5510
+ this.context.stdout.write(`License: ${pack.license || "Unknown"}
5511
+ `);
5512
+ this.context.stdout.write(`Compatibility: ${pack.compatibility.join(", ")}
5513
+ `);
5514
+ this.context.stdout.write(chalk28.cyan(`
5515
+ Skills (${pack.skills.length}):
5516
+ `));
5517
+ const skills = await loader.loadPackSkills(this.target);
5518
+ for (const skill of skills) {
5519
+ this.context.stdout.write(chalk28.green(` ${skill.id.split("/")[1]}
5520
+ `));
5521
+ this.context.stdout.write(chalk28.gray(` ${skill.description || "No description"}
5522
+ `));
5523
+ if (this.verbose) {
5524
+ if (skill.metadata.triggers) {
5525
+ this.context.stdout.write(` Triggers: ${skill.metadata.triggers.join(", ")}
5526
+ `);
5527
+ }
5528
+ if (skill.metadata.difficulty) {
5529
+ this.context.stdout.write(` Difficulty: ${skill.metadata.difficulty}
5530
+ `);
5531
+ }
5532
+ if (skill.metadata.estimatedTime) {
5533
+ this.context.stdout.write(` Est. Time: ${skill.metadata.estimatedTime} min
5534
+ `);
5535
+ }
5536
+ }
5537
+ }
5538
+ this.context.stdout.write(chalk28.gray(`
5539
+ Run \`skillkit methodology install ${this.target}\` to install this pack.
5540
+ `));
5541
+ return 0;
5542
+ }
5543
+ async listInstalled(projectPath) {
5544
+ const manager = createMethodologyManager({ projectPath });
5545
+ const installedPacks = manager.listInstalledPacks();
5546
+ const installedSkills = manager.listInstalledSkills();
5547
+ if (installedPacks.length === 0 && installedSkills.length === 0) {
5548
+ this.context.stdout.write("No methodology skills installed.\n");
5549
+ this.context.stdout.write(chalk28.gray("Run `skillkit methodology install` to install methodology packs.\n"));
5550
+ return 0;
5551
+ }
5552
+ if (installedPacks.length > 0) {
5553
+ this.context.stdout.write(chalk28.cyan("Installed Packs:\n"));
5554
+ for (const pack of installedPacks) {
5555
+ this.context.stdout.write(chalk28.green(` ${pack.name}`) + ` v${pack.version}
5556
+ `);
5557
+ this.context.stdout.write(` Skills: ${pack.skills.length}
5558
+ `);
5559
+ }
5560
+ this.context.stdout.write("\n");
5561
+ }
5562
+ if (installedSkills.length > 0) {
5563
+ this.context.stdout.write(chalk28.cyan("Installed Skills:\n"));
5564
+ for (const skill of installedSkills) {
5565
+ const syncStatus = skill.syncedAgents.length > 0 ? chalk28.green(`synced to ${skill.syncedAgents.length} agents`) : chalk28.yellow("not synced");
5566
+ this.context.stdout.write(` ${chalk28.green(skill.id)} v${skill.version} [${syncStatus}]
5567
+ `);
5568
+ }
5569
+ }
5570
+ this.context.stdout.write(chalk28.gray(`
5571
+ Total: ${installedSkills.length} skills installed.
5572
+ `));
5573
+ return 0;
5574
+ }
5575
+ };
5576
+
5577
+ // src/commands/hook.ts
5578
+ import { Command as Command30, Option as Option29 } from "clipanion";
5579
+ import chalk29 from "chalk";
5580
+ import { createHookManager } from "@skillkit/core";
5581
+ var HookCommand = class extends Command30 {
5582
+ static paths = [["hook"]];
5583
+ static usage = Command30.Usage({
5584
+ description: "Manage skill hooks for automatic triggering",
5585
+ examples: [
5586
+ ["List all hooks", "$0 hook list"],
5587
+ ["Add a session start hook", "$0 hook add session:start tdd-workflow"],
5588
+ ["Add a file save hook with pattern", '$0 hook add file:save code-review --pattern "*.ts"'],
5589
+ ["Remove a hook", "$0 hook remove <hook-id>"],
5590
+ ["Enable a hook", "$0 hook enable <hook-id>"],
5591
+ ["Disable a hook", "$0 hook disable <hook-id>"],
5592
+ ["Generate agent hooks", "$0 hook generate --agent claude-code"]
5593
+ ]
5594
+ });
5595
+ action = Option29.String({ required: true });
5596
+ target = Option29.String({ required: false });
5597
+ skill = Option29.String({ required: false });
5598
+ pattern = Option29.String("--pattern,-p", { description: "File pattern matcher" });
5599
+ agent = Option29.String("--agent,-a", { description: "Target agent for generation" });
5600
+ inject = Option29.String("--inject,-i", { description: "Injection mode: content, reference, prompt" });
5601
+ priority = Option29.String("--priority", { description: "Hook priority (higher = earlier)" });
5602
+ verbose = Option29.Boolean("--verbose,-v", { description: "Show detailed output" });
5603
+ async execute() {
5604
+ const projectPath = process.cwd();
5605
+ try {
5606
+ switch (this.action) {
5607
+ case "list":
5608
+ return await this.listHooks(projectPath);
5609
+ case "add":
5610
+ return await this.addHook(projectPath);
5611
+ case "remove":
5612
+ return await this.removeHook(projectPath);
5613
+ case "enable":
5614
+ return await this.enableHook(projectPath);
5615
+ case "disable":
5616
+ return await this.disableHook(projectPath);
5617
+ case "generate":
5618
+ return await this.generateHooks(projectPath);
5619
+ case "info":
5620
+ return await this.showHookInfo(projectPath);
5621
+ default:
5622
+ this.context.stderr.write(chalk29.red(`Unknown action: ${this.action}
5623
+ `));
5624
+ this.context.stderr.write("Available actions: list, add, remove, enable, disable, generate, info\n");
5625
+ return 1;
5626
+ }
5627
+ } catch (err) {
5628
+ this.context.stderr.write(chalk29.red(`\u2717 ${err instanceof Error ? err.message : "Unknown error"}
5629
+ `));
5630
+ return 1;
5631
+ }
5632
+ }
5633
+ async listHooks(projectPath) {
5634
+ const manager = createHookManager({ projectPath });
5635
+ const hooks = manager.getAllHooks();
5636
+ if (hooks.length === 0) {
5637
+ this.context.stdout.write("No hooks configured.\n");
5638
+ this.context.stdout.write(chalk29.gray("Run `skillkit hook add <event> <skill>` to add a hook.\n"));
5639
+ return 0;
5640
+ }
5641
+ this.context.stdout.write(chalk29.cyan("Configured Hooks:\n\n"));
5642
+ const eventGroups = /* @__PURE__ */ new Map();
5643
+ for (const hook of hooks) {
5644
+ const group = eventGroups.get(hook.event) || [];
5645
+ group.push(hook);
5646
+ eventGroups.set(hook.event, group);
5647
+ }
5648
+ for (const [event, eventHooks] of eventGroups) {
5649
+ this.context.stdout.write(chalk29.yellow(`${event}:
5650
+ `));
5651
+ for (const hook of eventHooks) {
5652
+ const status = hook.enabled ? chalk29.green("\u25CF") : chalk29.gray("\u25CB");
5653
+ this.context.stdout.write(` ${status} ${chalk29.white(hook.id.slice(0, 8))} \u2192 ${hook.skills.join(", ")}
5654
+ `);
5655
+ if (hook.matcher) {
5656
+ this.context.stdout.write(chalk29.gray(` Pattern: ${hook.matcher}
5657
+ `));
5658
+ }
5659
+ if (this.verbose) {
5660
+ this.context.stdout.write(chalk29.gray(` Inject: ${hook.inject}, Priority: ${hook.priority || 0}
5661
+ `));
5662
+ }
5663
+ }
5664
+ }
5665
+ this.context.stdout.write(`
5666
+ Total: ${hooks.length} hooks
5667
+ `);
5668
+ return 0;
5669
+ }
5670
+ async addHook(projectPath) {
5671
+ if (!this.target) {
5672
+ this.context.stderr.write(chalk29.red("Event type required.\n"));
5673
+ this.context.stderr.write("Usage: skillkit hook add <event> <skill>\n");
5674
+ this.context.stderr.write("Events: session:start, session:end, file:save, file:open, task:start, commit:pre, commit:post, error:occur, test:fail, build:fail\n");
5675
+ return 1;
5676
+ }
5677
+ if (!this.skill) {
5678
+ this.context.stderr.write(chalk29.red("Skill name required.\n"));
5679
+ this.context.stderr.write("Usage: skillkit hook add <event> <skill>\n");
5680
+ return 1;
5681
+ }
5682
+ const event = this.target;
5683
+ const validEvents = [
5684
+ "session:start",
5685
+ "session:resume",
5686
+ "session:end",
5687
+ "file:open",
5688
+ "file:save",
5689
+ "file:create",
5690
+ "file:delete",
5691
+ "task:start",
5692
+ "task:complete",
5693
+ "commit:pre",
5694
+ "commit:post",
5695
+ "error:occur",
5696
+ "test:fail",
5697
+ "test:pass",
5698
+ "build:start",
5699
+ "build:fail",
5700
+ "build:success"
5701
+ ];
5702
+ if (!validEvents.includes(event)) {
5703
+ this.context.stderr.write(chalk29.red(`Invalid event: ${this.target}
5704
+ `));
5705
+ this.context.stderr.write(`Valid events: ${validEvents.join(", ")}
5706
+ `);
5707
+ return 1;
5708
+ }
5709
+ const manager = createHookManager({ projectPath });
5710
+ const hook = manager.registerHook({
5711
+ event,
5712
+ skills: [this.skill],
5713
+ inject: this.inject || "reference",
5714
+ matcher: this.pattern,
5715
+ priority: this.priority ? parseInt(this.priority, 10) : 0,
5716
+ enabled: true
5717
+ });
5718
+ manager.save();
5719
+ this.context.stdout.write(chalk29.green(`\u2713 Hook added: ${hook.id.slice(0, 8)}
5720
+ `));
5721
+ this.context.stdout.write(` Event: ${event}
5722
+ `);
5723
+ this.context.stdout.write(` Skill: ${this.skill}
5724
+ `);
5725
+ if (this.pattern) {
5726
+ this.context.stdout.write(` Pattern: ${this.pattern}
5727
+ `);
5728
+ }
5729
+ return 0;
5730
+ }
5731
+ async removeHook(projectPath) {
5732
+ if (!this.target) {
5733
+ this.context.stderr.write(chalk29.red("Hook ID required.\n"));
5734
+ this.context.stderr.write("Usage: skillkit hook remove <hook-id>\n");
5735
+ return 1;
5736
+ }
5737
+ const manager = createHookManager({ projectPath });
5738
+ const hooks = manager.getAllHooks();
5739
+ const hook = hooks.find((h) => h.id.startsWith(this.target));
5740
+ if (!hook) {
5741
+ this.context.stderr.write(chalk29.red(`Hook not found: ${this.target}
5742
+ `));
5743
+ return 1;
5744
+ }
5745
+ manager.unregisterHook(hook.id);
5746
+ manager.save();
5747
+ this.context.stdout.write(chalk29.green(`\u2713 Hook removed: ${hook.id.slice(0, 8)}
5748
+ `));
5749
+ return 0;
5750
+ }
5751
+ async enableHook(projectPath) {
5752
+ if (!this.target) {
5753
+ this.context.stderr.write(chalk29.red("Hook ID required.\n"));
5754
+ return 1;
5755
+ }
5756
+ const manager = createHookManager({ projectPath });
5757
+ const hooks = manager.getAllHooks();
5758
+ const hook = hooks.find((h) => h.id.startsWith(this.target));
5759
+ if (!hook) {
5760
+ this.context.stderr.write(chalk29.red(`Hook not found: ${this.target}
5761
+ `));
5762
+ return 1;
5763
+ }
5764
+ manager.enableHook(hook.id);
5765
+ manager.save();
5766
+ this.context.stdout.write(chalk29.green(`\u2713 Hook enabled: ${hook.id.slice(0, 8)}
5767
+ `));
5768
+ return 0;
5769
+ }
5770
+ async disableHook(projectPath) {
5771
+ if (!this.target) {
5772
+ this.context.stderr.write(chalk29.red("Hook ID required.\n"));
5773
+ return 1;
5774
+ }
5775
+ const manager = createHookManager({ projectPath });
5776
+ const hooks = manager.getAllHooks();
5777
+ const hook = hooks.find((h) => h.id.startsWith(this.target));
5778
+ if (!hook) {
5779
+ this.context.stderr.write(chalk29.red(`Hook not found: ${this.target}
5780
+ `));
5781
+ return 1;
5782
+ }
5783
+ manager.disableHook(hook.id);
5784
+ manager.save();
5785
+ this.context.stdout.write(chalk29.yellow(`\u25CB Hook disabled: ${hook.id.slice(0, 8)}
5786
+ `));
5787
+ return 0;
5788
+ }
5789
+ async generateHooks(projectPath) {
5790
+ const manager = createHookManager({ projectPath });
5791
+ const hooks = manager.getAllHooks();
5792
+ if (hooks.length === 0) {
5793
+ this.context.stdout.write("No hooks configured to generate.\n");
5794
+ return 0;
5795
+ }
5796
+ const agent = this.agent || "claude-code";
5797
+ const generated = manager.generateAgentHooks(agent);
5798
+ this.context.stdout.write(chalk29.cyan(`Generated hooks for ${agent}:
5799
+
5800
+ `));
5801
+ if (typeof generated === "string") {
5802
+ this.context.stdout.write(generated);
5803
+ } else {
5804
+ this.context.stdout.write(JSON.stringify(generated, null, 2));
5805
+ }
5806
+ this.context.stdout.write("\n");
5807
+ return 0;
5808
+ }
5809
+ async showHookInfo(projectPath) {
5810
+ if (!this.target) {
5811
+ this.context.stderr.write(chalk29.red("Hook ID required.\n"));
5812
+ return 1;
5813
+ }
5814
+ const manager = createHookManager({ projectPath });
5815
+ const hooks = manager.getAllHooks();
5816
+ const hook = hooks.find((h) => h.id.startsWith(this.target));
5817
+ if (!hook) {
5818
+ this.context.stderr.write(chalk29.red(`Hook not found: ${this.target}
5819
+ `));
5820
+ return 1;
5821
+ }
5822
+ this.context.stdout.write(chalk29.cyan(`
5823
+ Hook: ${hook.id}
5824
+ `));
5825
+ this.context.stdout.write(`Event: ${hook.event}
5826
+ `);
5827
+ this.context.stdout.write(`Skills: ${hook.skills.join(", ")}
5828
+ `);
5829
+ this.context.stdout.write(`Enabled: ${hook.enabled ? "Yes" : "No"}
5830
+ `);
5831
+ this.context.stdout.write(`Inject: ${hook.inject}
5832
+ `);
5833
+ this.context.stdout.write(`Priority: ${hook.priority || 0}
5834
+ `);
5835
+ if (hook.matcher) {
5836
+ this.context.stdout.write(`Pattern: ${hook.matcher}
5837
+ `);
5838
+ }
5839
+ if (hook.condition) {
5840
+ this.context.stdout.write(`Condition: ${hook.condition}
5841
+ `);
5842
+ }
5843
+ if (hook.agentOverrides) {
5844
+ this.context.stdout.write(`Agent Overrides: ${Object.keys(hook.agentOverrides).join(", ")}
5845
+ `);
5846
+ }
5847
+ return 0;
5848
+ }
5849
+ };
5850
+
5851
+ // src/commands/plan.ts
5852
+ import { Command as Command31, Option as Option30 } from "clipanion";
5853
+ import { readFile, writeFile } from "fs/promises";
5854
+ import { resolve as resolve12 } from "path";
5855
+ import {
5856
+ createPlanParser,
5857
+ createPlanValidator,
5858
+ createPlanGenerator,
5859
+ createPlanExecutor,
5860
+ dryRunExecutor,
5861
+ shellExecutor,
5862
+ TASK_TEMPLATES
5863
+ } from "@skillkit/core";
5864
+ var PlanCommand = class extends Command31 {
5865
+ static paths = [["plan"]];
5866
+ static usage = Command31.Usage({
5867
+ category: "Development",
5868
+ description: "Manage structured development plans",
5869
+ details: `
5870
+ This command provides tools for working with structured development plans.
5871
+
5872
+ Actions:
5873
+ - parse: Parse a markdown plan to structured format
5874
+ - validate: Validate a plan for completeness
5875
+ - execute: Execute a plan (with optional dry-run)
5876
+ - generate: Generate a plan from task list
5877
+ - templates: List available task templates
5878
+ - create: Create a new empty plan
5879
+
5880
+ Examples:
5881
+ $ skillkit plan parse ./plan.md
5882
+ $ skillkit plan validate ./plan.md
5883
+ $ skillkit plan execute ./plan.md --dry-run
5884
+ $ skillkit plan generate "Add auth" --tasks "Setup,Login,Logout"
5885
+ $ skillkit plan templates
5886
+ `,
5887
+ examples: [
5888
+ ["Parse a markdown plan", "$0 plan parse ./development-plan.md"],
5889
+ ["Validate a plan", "$0 plan validate ./plan.md --strict"],
5890
+ ["Dry-run execute a plan", "$0 plan execute ./plan.md --dry-run"],
5891
+ ["List task templates", "$0 plan templates"]
5892
+ ]
5893
+ });
5894
+ action = Option30.String({ required: true });
5895
+ file = Option30.String("--file,-f", {
5896
+ description: "Plan file path"
5897
+ });
5898
+ output = Option30.String("--output,-o", {
5899
+ description: "Output file path"
5900
+ });
5901
+ name = Option30.String("--name,-n", {
5902
+ description: "Plan name"
5903
+ });
5904
+ goal = Option30.String("--goal,-g", {
5905
+ description: "Plan goal"
5906
+ });
5907
+ tasks = Option30.String("--tasks,-t", {
5908
+ description: "Comma-separated list of task names"
5909
+ });
5910
+ template = Option30.String("--template", {
5911
+ description: "Task template to use"
5912
+ });
5913
+ techStack = Option30.String("--tech-stack", {
5914
+ description: "Comma-separated tech stack"
5915
+ });
5916
+ dryRun = Option30.Boolean("--dry-run", false, {
5917
+ description: "Dry run execution (no actual changes)"
5918
+ });
5919
+ strict = Option30.Boolean("--strict", false, {
5920
+ description: "Strict validation (treat warnings as errors)"
5921
+ });
5922
+ stopOnError = Option30.Boolean("--stop-on-error", true, {
5923
+ description: "Stop execution on first error"
5924
+ });
5925
+ verbose = Option30.Boolean("--verbose,-v", false, {
5926
+ description: "Verbose output"
5927
+ });
5928
+ json = Option30.Boolean("--json", false, {
5929
+ description: "Output as JSON"
5930
+ });
5931
+ async execute() {
5932
+ try {
5933
+ switch (this.action) {
5934
+ case "parse":
5935
+ return await this.parsePlan();
5936
+ case "validate":
5937
+ return await this.validatePlan();
5938
+ case "execute":
5939
+ return await this.executePlan();
5940
+ case "generate":
5941
+ return await this.generatePlan();
5942
+ case "templates":
5943
+ return await this.listTemplates();
5944
+ case "create":
5945
+ return await this.createPlan();
5946
+ default:
5947
+ this.context.stderr.write(
5948
+ `Unknown action: ${this.action}
5949
+ Available actions: parse, validate, execute, generate, templates, create
5950
+ `
5951
+ );
5952
+ return 1;
5953
+ }
5954
+ } catch (error) {
5955
+ this.context.stderr.write(`Error: ${error.message}
5956
+ `);
5957
+ return 1;
5958
+ }
5959
+ }
5960
+ async parsePlan() {
5961
+ if (!this.file) {
5962
+ this.context.stderr.write("Error: --file is required for parse action\n");
5963
+ return 1;
5964
+ }
5965
+ const filePath = resolve12(this.file);
5966
+ const content = await readFile(filePath, "utf-8");
5967
+ const parser = createPlanParser();
5968
+ const plan = parser.parse(content);
5969
+ if (this.json) {
5970
+ this.context.stdout.write(JSON.stringify(plan, null, 2) + "\n");
5971
+ } else {
5972
+ this.context.stdout.write(`Plan: ${plan.name}
5973
+ `);
5974
+ this.context.stdout.write(`Goal: ${plan.goal}
5975
+ `);
5976
+ this.context.stdout.write(`Status: ${plan.status}
5977
+ `);
5978
+ this.context.stdout.write(`Tasks: ${plan.tasks.length}
5979
+
5980
+ `);
5981
+ for (const task of plan.tasks) {
5982
+ this.context.stdout.write(` Task ${task.id}: ${task.name}
5983
+ `);
5984
+ this.context.stdout.write(` Steps: ${task.steps.length}
5985
+ `);
5986
+ if (task.estimatedMinutes) {
5987
+ this.context.stdout.write(` Estimated: ${task.estimatedMinutes} min
5988
+ `);
5989
+ }
5990
+ }
5991
+ }
5992
+ if (this.output) {
5993
+ const outputPath = resolve12(this.output);
5994
+ await writeFile(outputPath, JSON.stringify(plan, null, 2));
5995
+ this.context.stdout.write(`
5996
+ Saved to: ${outputPath}
5997
+ `);
5998
+ }
5999
+ return 0;
6000
+ }
6001
+ async validatePlan() {
6002
+ if (!this.file) {
6003
+ this.context.stderr.write("Error: --file is required for validate action\n");
6004
+ return 1;
6005
+ }
6006
+ const filePath = resolve12(this.file);
6007
+ const content = await readFile(filePath, "utf-8");
6008
+ const parser = createPlanParser();
6009
+ const plan = parser.parse(content);
6010
+ const validator = createPlanValidator({ strict: this.strict });
6011
+ const result = validator.validate(plan);
6012
+ if (this.json) {
6013
+ this.context.stdout.write(JSON.stringify(result, null, 2) + "\n");
6014
+ } else {
6015
+ this.context.stdout.write(`Validation: ${result.valid ? "PASSED" : "FAILED"}
6016
+
6017
+ `);
6018
+ this.context.stdout.write(`Statistics:
6019
+ `);
6020
+ this.context.stdout.write(` Total tasks: ${result.stats.totalTasks}
6021
+ `);
6022
+ this.context.stdout.write(` Total steps: ${result.stats.totalSteps}
6023
+ `);
6024
+ this.context.stdout.write(` Estimated time: ${result.stats.estimatedMinutes} minutes
6025
+ `);
6026
+ this.context.stdout.write(` Tasks with tests: ${result.stats.tasksWithTests}
6027
+ `);
6028
+ this.context.stdout.write(` Avg steps/task: ${result.stats.avgStepsPerTask}
6029
+
6030
+ `);
6031
+ if (result.issues.length > 0) {
6032
+ this.context.stdout.write(`Issues:
6033
+ `);
6034
+ for (const issue of result.issues) {
6035
+ const icon = issue.type === "error" ? "\u274C" : issue.type === "warning" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
6036
+ const location = issue.taskId ? `Task ${issue.taskId}` : "Plan";
6037
+ this.context.stdout.write(` ${icon} [${location}] ${issue.message}
6038
+ `);
6039
+ if (issue.suggestion && this.verbose) {
6040
+ this.context.stdout.write(` Suggestion: ${issue.suggestion}
6041
+ `);
6042
+ }
6043
+ }
6044
+ } else {
6045
+ this.context.stdout.write("No issues found.\n");
6046
+ }
6047
+ }
6048
+ return result.valid ? 0 : 1;
6049
+ }
6050
+ async executePlan() {
6051
+ if (!this.file) {
6052
+ this.context.stderr.write("Error: --file is required for execute action\n");
6053
+ return 1;
6054
+ }
6055
+ const filePath = resolve12(this.file);
6056
+ const content = await readFile(filePath, "utf-8");
6057
+ const parser = createPlanParser();
6058
+ const plan = parser.parse(content);
6059
+ const validator = createPlanValidator({ strict: this.strict });
6060
+ const validation = validator.validate(plan);
6061
+ if (!validation.valid) {
6062
+ this.context.stderr.write("Plan validation failed. Fix errors before executing.\n");
6063
+ for (const issue of validation.issues.filter((i) => i.type === "error")) {
6064
+ this.context.stderr.write(` \u274C ${issue.message}
6065
+ `);
6066
+ }
6067
+ return 1;
6068
+ }
6069
+ const executor = createPlanExecutor({
6070
+ stepExecutor: this.dryRun ? dryRunExecutor : shellExecutor
6071
+ });
6072
+ executor.addListener((event, _plan, task) => {
6073
+ if (this.verbose) {
6074
+ if (event === "plan:task_started" && task) {
6075
+ this.context.stdout.write(`Starting task ${task.id}: ${task.name}
6076
+ `);
6077
+ } else if (event === "plan:task_completed" && task) {
6078
+ this.context.stdout.write(`Completed task ${task.id}: ${task.name}
6079
+ `);
6080
+ } else if (event === "plan:task_failed" && task) {
6081
+ this.context.stderr.write(`Failed task ${task.id}: ${task.name}
6082
+ `);
6083
+ }
6084
+ }
6085
+ });
6086
+ this.context.stdout.write(`Executing plan: ${plan.name}
6087
+ `);
6088
+ if (this.dryRun) {
6089
+ this.context.stdout.write("(Dry run mode - no actual changes)\n");
6090
+ }
6091
+ this.context.stdout.write("\n");
6092
+ const result = await executor.execute(plan, {
6093
+ dryRun: this.dryRun,
6094
+ stopOnError: this.stopOnError,
6095
+ onProgress: (taskId, step, status) => {
6096
+ if (this.verbose) {
6097
+ this.context.stdout.write(` Task ${taskId}, Step ${step}: ${status}
6098
+ `);
6099
+ }
6100
+ }
6101
+ });
6102
+ this.context.stdout.write("\n");
6103
+ if (this.json) {
6104
+ this.context.stdout.write(
6105
+ JSON.stringify(
6106
+ {
6107
+ ...result,
6108
+ taskResults: Object.fromEntries(result.taskResults)
6109
+ },
6110
+ null,
6111
+ 2
6112
+ ) + "\n"
6113
+ );
6114
+ } else {
6115
+ this.context.stdout.write(`Execution: ${result.success ? "SUCCESS" : "FAILED"}
6116
+ `);
6117
+ this.context.stdout.write(`Duration: ${result.durationMs}ms
6118
+ `);
6119
+ this.context.stdout.write(`Completed: ${result.completedTasks.length} tasks
6120
+ `);
6121
+ this.context.stdout.write(`Failed: ${result.failedTasks.length} tasks
6122
+ `);
6123
+ this.context.stdout.write(`Skipped: ${result.skippedTasks.length} tasks
6124
+ `);
6125
+ if (result.errors && result.errors.length > 0) {
6126
+ this.context.stdout.write("\nErrors:\n");
6127
+ for (const error of result.errors) {
6128
+ this.context.stderr.write(` - ${error}
6129
+ `);
6130
+ }
6131
+ }
6132
+ }
6133
+ return result.success ? 0 : 1;
6134
+ }
6135
+ async generatePlan() {
6136
+ if (!this.name) {
6137
+ this.context.stderr.write("Error: --name is required for generate action\n");
6138
+ return 1;
6139
+ }
6140
+ const generator = createPlanGenerator({
6141
+ includeTests: true,
6142
+ includeCommits: true,
6143
+ techStack: this.techStack?.split(",").map((s) => s.trim()).filter((s) => s !== "")
6144
+ });
6145
+ let plan;
6146
+ if (this.tasks) {
6147
+ const taskNames = this.tasks.split(",").map((s) => s.trim()).filter((s) => s !== "");
6148
+ plan = generator.fromTaskList(this.name, this.goal || "Complete the tasks", taskNames, {
6149
+ template: this.template,
6150
+ techStack: this.techStack?.split(",").map((s) => s.trim()).filter((s) => s !== "")
6151
+ });
6152
+ } else {
6153
+ plan = generator.createPlan(this.name, this.goal || "Complete the project");
6154
+ if (this.techStack) {
6155
+ plan.techStack = this.techStack.split(",").map((s) => s.trim()).filter((s) => s !== "");
6156
+ }
6157
+ }
6158
+ const markdown = generator.toMarkdown(plan);
6159
+ if (this.output) {
6160
+ const outputPath = resolve12(this.output);
6161
+ await writeFile(outputPath, markdown);
6162
+ this.context.stdout.write(`Plan saved to: ${outputPath}
6163
+ `);
6164
+ } else if (this.json) {
6165
+ this.context.stdout.write(JSON.stringify(plan, null, 2) + "\n");
6166
+ } else {
6167
+ this.context.stdout.write(markdown);
6168
+ }
6169
+ return 0;
6170
+ }
6171
+ async listTemplates() {
6172
+ if (this.json) {
6173
+ this.context.stdout.write(JSON.stringify(TASK_TEMPLATES, null, 2) + "\n");
6174
+ } else {
6175
+ this.context.stdout.write("Available Task Templates:\n\n");
6176
+ for (const [name, template] of Object.entries(TASK_TEMPLATES)) {
6177
+ this.context.stdout.write(` ${name}
6178
+ `);
6179
+ this.context.stdout.write(` ${template.description}
6180
+ `);
6181
+ this.context.stdout.write(` Steps: ${template.steps.length}
6182
+ `);
6183
+ this.context.stdout.write(` Estimated: ${template.estimatedMinutes} min
6184
+
6185
+ `);
6186
+ }
6187
+ }
6188
+ return 0;
6189
+ }
6190
+ async createPlan() {
6191
+ if (!this.name) {
6192
+ this.context.stderr.write("Error: --name is required for create action\n");
6193
+ return 1;
6194
+ }
6195
+ const generator = createPlanGenerator({
6196
+ includeTests: true,
6197
+ includeCommits: true
6198
+ });
6199
+ const plan = generator.createPlan(this.name, this.goal || "Complete the project");
6200
+ if (this.techStack) {
6201
+ plan.techStack = this.techStack.split(",").map((s) => s.trim()).filter((s) => s !== "");
6202
+ }
6203
+ const markdown = generator.toMarkdown(plan);
6204
+ const outputPath = this.output || resolve12(`${this.name.toLowerCase().replace(/\s+/g, "-")}-plan.md`);
6205
+ await writeFile(outputPath, markdown);
6206
+ this.context.stdout.write(`Plan created: ${outputPath}
6207
+ `);
6208
+ return 0;
6209
+ }
6210
+ };
6211
+
6212
+ // src/commands/command.ts
6213
+ import { Command as Command32, Option as Option31 } from "clipanion";
6214
+ import { readFile as readFile2, writeFile as writeFile2, mkdir } from "fs/promises";
6215
+ import { resolve as resolve13, join as join11, dirname as dirname3, extname } from "path";
6216
+ import * as path from "path";
6217
+ import * as fs from "fs/promises";
6218
+ import { existsSync as existsSync13 } from "fs";
6219
+ import {
6220
+ createCommandRegistry,
6221
+ createCommandGenerator,
6222
+ getAgentFormat,
6223
+ supportsSlashCommands
6224
+ } from "@skillkit/core";
6225
+ var CommandCmd = class extends Command32 {
6226
+ static paths = [["command"]];
6227
+ static usage = Command32.Usage({
6228
+ category: "Commands",
6229
+ description: "Manage slash commands and agent integration",
6230
+ details: `
6231
+ This command provides tools for working with slash commands.
6232
+
6233
+ Actions:
6234
+ - list: List registered commands
6235
+ - create: Create a new command from a skill
6236
+ - generate: Generate agent-specific command files
6237
+ - validate: Validate command definitions
6238
+ - export: Export commands to a bundle
6239
+ - import: Import commands from a bundle
6240
+
6241
+ Examples:
6242
+ $ skillkit command list
6243
+ $ skillkit command create my-skill --name my-command
6244
+ $ skillkit command generate --agent claude-code
6245
+ $ skillkit command validate ./commands.json
6246
+ `,
6247
+ examples: [
6248
+ ["List all commands", "$0 command list"],
6249
+ ["Create command from skill", "$0 command create tdd --name run-tdd"],
6250
+ ["Generate for Claude Code", "$0 command generate --agent claude-code"],
6251
+ ["Export commands", "$0 command export --output commands.json"]
6252
+ ]
6253
+ });
6254
+ action = Option31.String({ required: true });
6255
+ skill = Option31.String("--skill,-s", {
6256
+ description: "Skill to create command from"
6257
+ });
6258
+ name = Option31.String("--name,-n", {
6259
+ description: "Command name"
6260
+ });
6261
+ description = Option31.String("--description,-d", {
6262
+ description: "Command description"
6263
+ });
6264
+ agent = Option31.String("--agent,-a", {
6265
+ description: "Target agent for generation"
6266
+ });
6267
+ output = Option31.String("--output,-o", {
6268
+ description: "Output file or directory"
6269
+ });
6270
+ input = Option31.String("--input,-i", {
6271
+ description: "Input file"
6272
+ });
6273
+ category = Option31.String("--category,-c", {
6274
+ description: "Command category"
6275
+ });
6276
+ all = Option31.Boolean("--all", false, {
6277
+ description: "Include all commands (hidden and disabled)"
6278
+ });
6279
+ json = Option31.Boolean("--json", false, {
6280
+ description: "Output as JSON"
6281
+ });
6282
+ dryRun = Option31.Boolean("--dry-run", false, {
6283
+ description: "Show what would be generated without writing files"
6284
+ });
6285
+ async execute() {
6286
+ try {
6287
+ switch (this.action) {
6288
+ case "list":
6289
+ return await this.listCommands();
6290
+ case "create":
6291
+ return await this.createCommand();
6292
+ case "generate":
6293
+ return await this.generateCommands();
6294
+ case "validate":
6295
+ return await this.validateCommands();
6296
+ case "export":
6297
+ return await this.exportCommands();
6298
+ case "import":
6299
+ return await this.importCommands();
6300
+ case "info":
6301
+ return await this.showInfo();
6302
+ default:
6303
+ this.context.stderr.write(
6304
+ `Unknown action: ${this.action}
6305
+ Available actions: list, create, generate, validate, export, import, info
6306
+ `
6307
+ );
6308
+ return 1;
6309
+ }
6310
+ } catch (error) {
6311
+ this.context.stderr.write(`Error: ${error.message}
6312
+ `);
6313
+ return 1;
6314
+ }
6315
+ }
6316
+ async listCommands() {
6317
+ const registry = createCommandRegistry();
6318
+ await this.loadCommandsFromConfig(registry);
6319
+ const commands = registry.search({
6320
+ category: this.category,
6321
+ includeHidden: this.all,
6322
+ includeDisabled: this.all
6323
+ });
6324
+ if (this.json) {
6325
+ this.context.stdout.write(JSON.stringify(commands, null, 2) + "\n");
6326
+ } else {
6327
+ if (commands.length === 0) {
6328
+ this.context.stdout.write("No commands registered.\n");
6329
+ this.context.stdout.write('Use "skillkit command create" to create commands from skills.\n');
6330
+ return 0;
6331
+ }
6332
+ this.context.stdout.write(`Registered Commands (${commands.length}):
6333
+
6334
+ `);
6335
+ const categories = /* @__PURE__ */ new Map();
6336
+ for (const cmd of commands) {
6337
+ const cat = cmd.category || "uncategorized";
6338
+ if (!categories.has(cat)) {
6339
+ categories.set(cat, []);
6340
+ }
6341
+ categories.get(cat).push(cmd);
6342
+ }
6343
+ for (const [category, cmds] of categories) {
6344
+ this.context.stdout.write(` ${category.toUpperCase()}
6345
+ `);
6346
+ for (const cmd of cmds) {
6347
+ const status = cmd.enabled ? "" : " (disabled)";
6348
+ const hidden = cmd.hidden ? " (hidden)" : "";
6349
+ this.context.stdout.write(` /${cmd.name}${status}${hidden}
6350
+ `);
6351
+ this.context.stdout.write(` ${cmd.description}
6352
+ `);
6353
+ if (cmd.aliases && cmd.aliases.length > 0) {
6354
+ this.context.stdout.write(` Aliases: ${cmd.aliases.join(", ")}
6355
+ `);
6356
+ }
6357
+ }
6358
+ this.context.stdout.write("\n");
6359
+ }
6360
+ }
6361
+ return 0;
6362
+ }
6363
+ async createCommand() {
6364
+ if (!this.skill) {
6365
+ this.context.stderr.write("Error: --skill is required for create action\n");
6366
+ return 1;
6367
+ }
6368
+ const commandName = this.name || this.skill.toLowerCase().replace(/[^a-z0-9]+/g, "-");
6369
+ const command = {
6370
+ name: commandName,
6371
+ description: this.description || `Execute ${this.skill} skill`,
6372
+ skill: this.skill,
6373
+ category: this.category,
6374
+ examples: [`/${commandName}`]
6375
+ };
6376
+ const configPath = resolve13(process.cwd(), ".skillkit", "commands.json");
6377
+ let commands = [];
6378
+ if (existsSync13(configPath)) {
6379
+ const content = await readFile2(configPath, "utf-8");
6380
+ const config = JSON.parse(content);
6381
+ commands = config.commands || [];
6382
+ }
6383
+ const existing = commands.findIndex((c) => c.name === commandName);
6384
+ if (existing >= 0) {
6385
+ const previous = commands[existing];
6386
+ commands[existing] = {
6387
+ ...previous,
6388
+ name: commandName,
6389
+ skill: this.skill,
6390
+ description: this.description ?? previous.description ?? command.description,
6391
+ category: this.category ?? previous.category,
6392
+ examples: command.examples?.length ? command.examples : previous.examples,
6393
+ // Preserve existing metadata if not provided
6394
+ aliases: previous.aliases,
6395
+ args: previous.args,
6396
+ tags: previous.tags,
6397
+ hidden: previous.hidden,
6398
+ metadata: previous.metadata
6399
+ };
6400
+ this.context.stdout.write(`Updated command: /${commandName}
6401
+ `);
6402
+ } else {
6403
+ commands.push(command);
6404
+ this.context.stdout.write(`Created command: /${commandName}
6405
+ `);
6406
+ }
6407
+ await mkdir(dirname3(configPath), { recursive: true });
6408
+ await writeFile2(
6409
+ configPath,
6410
+ JSON.stringify({ version: "1.0.0", commands }, null, 2)
6411
+ );
6412
+ this.context.stdout.write(`Saved to: ${configPath}
6413
+ `);
6414
+ return 0;
6415
+ }
6416
+ async generateCommands() {
6417
+ const agent = this.agent || "claude-code";
6418
+ if (!supportsSlashCommands(agent)) {
6419
+ this.context.stdout.write(`Note: ${agent} has limited slash command support.
6420
+ `);
6421
+ }
6422
+ const registry = createCommandRegistry();
6423
+ await this.loadCommandsFromConfig(registry);
6424
+ const commands = registry.getAll(this.all);
6425
+ if (commands.length === 0) {
6426
+ this.context.stderr.write("No commands to generate. Create commands first.\n");
6427
+ return 1;
6428
+ }
6429
+ const generator = createCommandGenerator({
6430
+ includeHidden: this.all,
6431
+ includeDisabled: this.all
6432
+ });
6433
+ const format = getAgentFormat(agent);
6434
+ const outputTarget = this.output ? resolve13(this.output) : resolve13(process.cwd(), format.directory);
6435
+ const outputDir = extname(outputTarget) ? dirname3(outputTarget) : outputTarget;
6436
+ const result = generator.generate(commands, agent);
6437
+ if (typeof result === "string") {
6438
+ const extension = format.extension ? format.extension.startsWith(".") ? format.extension : `.${format.extension}` : ".mdc";
6439
+ const filePath = extname(outputTarget) ? outputTarget : join11(outputTarget, `commands${extension}`);
6440
+ if (this.dryRun) {
6441
+ this.context.stdout.write("Would write to: " + filePath + "\n\n");
6442
+ this.context.stdout.write(result);
6443
+ } else {
6444
+ await mkdir(dirname3(filePath), { recursive: true });
6445
+ await writeFile2(filePath, result);
6446
+ this.context.stdout.write(`Generated: ${filePath}
6447
+ `);
6448
+ }
6449
+ } else {
6450
+ if (extname(outputTarget)) {
6451
+ this.context.stderr.write(
6452
+ "Error: --output must be a directory for multi-file formats\n"
6453
+ );
6454
+ return 1;
6455
+ }
6456
+ if (this.dryRun) {
6457
+ this.context.stdout.write("Would write to: " + outputDir + "\n\n");
6458
+ for (const [filename, content] of result) {
6459
+ this.context.stdout.write(`--- ${filename} ---
6460
+ `);
6461
+ this.context.stdout.write(content.substring(0, 500));
6462
+ if (content.length > 500) {
6463
+ this.context.stdout.write("\n... (truncated)\n");
6464
+ }
6465
+ this.context.stdout.write("\n");
6466
+ }
6467
+ } else {
6468
+ await mkdir(outputDir, { recursive: true });
6469
+ for (const [filename, content] of result) {
6470
+ const filePath = join11(outputDir, filename);
6471
+ await writeFile2(filePath, content);
6472
+ this.context.stdout.write(`Generated: ${filePath}
6473
+ `);
6474
+ }
6475
+ }
6476
+ }
6477
+ const manifestPath = join11(outputDir, "manifest.json");
6478
+ const manifest = generator.generateManifest(commands);
6479
+ if (!this.dryRun) {
6480
+ await writeFile2(manifestPath, manifest);
6481
+ this.context.stdout.write(`Generated: ${manifestPath}
6482
+ `);
6483
+ }
6484
+ this.context.stdout.write(`
6485
+ Generated ${commands.length} commands for ${agent}
6486
+ `);
6487
+ return 0;
6488
+ }
6489
+ async validateCommands() {
6490
+ const inputPath = this.input || resolve13(process.cwd(), ".skillkit", "commands.json");
6491
+ if (!existsSync13(inputPath)) {
6492
+ this.context.stderr.write(`File not found: ${inputPath}
6493
+ `);
6494
+ return 1;
6495
+ }
6496
+ const content = await readFile2(inputPath, "utf-8");
6497
+ const config = JSON.parse(content);
6498
+ const commands = config.commands || [];
6499
+ const registry = createCommandRegistry({ validateOnRegister: false });
6500
+ let valid = 0;
6501
+ let invalid = 0;
6502
+ this.context.stdout.write(`Validating ${commands.length} commands...
6503
+
6504
+ `);
6505
+ for (const command of commands) {
6506
+ const result = registry.validate(command);
6507
+ if (result.valid) {
6508
+ valid++;
6509
+ if (!this.json) {
6510
+ this.context.stdout.write(`\u2713 /${command.name}
6511
+ `);
6512
+ }
6513
+ } else {
6514
+ invalid++;
6515
+ if (!this.json) {
6516
+ this.context.stdout.write(`\u2717 /${command.name}
6517
+ `);
6518
+ for (const error of result.errors) {
6519
+ this.context.stdout.write(` Error: ${error}
6520
+ `);
6521
+ }
6522
+ }
6523
+ }
6524
+ if (!this.json && result.warnings.length > 0) {
6525
+ for (const warning of result.warnings) {
6526
+ this.context.stdout.write(` Warning: ${warning}
6527
+ `);
6528
+ }
6529
+ }
6530
+ }
6531
+ if (this.json) {
6532
+ this.context.stdout.write(
6533
+ JSON.stringify({
6534
+ total: commands.length,
6535
+ valid,
6536
+ invalid
6537
+ }) + "\n"
6538
+ );
6539
+ } else {
6540
+ this.context.stdout.write(`
6541
+ Validation complete: ${valid} valid, ${invalid} invalid
6542
+ `);
6543
+ }
6544
+ return invalid > 0 ? 1 : 0;
6545
+ }
6546
+ async exportCommands() {
6547
+ const registry = createCommandRegistry();
6548
+ await this.loadCommandsFromConfig(registry);
6549
+ const bundle = registry.export(this.all);
6550
+ if (this.output) {
6551
+ let outputPath = resolve13(this.output);
6552
+ const stat2 = await fs.stat(outputPath).catch(() => null);
6553
+ if (stat2 && stat2.isDirectory() || this.output.endsWith(path.sep)) {
6554
+ outputPath = path.join(outputPath, "commands.json");
6555
+ }
6556
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
6557
+ await writeFile2(outputPath, JSON.stringify(bundle, null, 2));
6558
+ this.context.stdout.write(`Exported ${bundle.commands.length} commands to: ${outputPath}
6559
+ `);
6560
+ } else {
6561
+ this.context.stdout.write(JSON.stringify(bundle, null, 2) + "\n");
6562
+ }
6563
+ return 0;
6564
+ }
6565
+ async importCommands() {
6566
+ if (!this.input) {
6567
+ this.context.stderr.write("Error: --input is required for import action\n");
6568
+ return 1;
6569
+ }
6570
+ const inputPath = resolve13(this.input);
6571
+ if (!existsSync13(inputPath)) {
6572
+ this.context.stderr.write(`File not found: ${inputPath}
6573
+ `);
6574
+ return 1;
6575
+ }
6576
+ const content = await readFile2(inputPath, "utf-8");
6577
+ const bundle = JSON.parse(content);
6578
+ const configPath = resolve13(process.cwd(), ".skillkit", "commands.json");
6579
+ let existingCommands = [];
6580
+ if (existsSync13(configPath)) {
6581
+ const existingContent = await readFile2(configPath, "utf-8");
6582
+ const config = JSON.parse(existingContent);
6583
+ existingCommands = config.commands || [];
6584
+ }
6585
+ const commandMap = /* @__PURE__ */ new Map();
6586
+ for (const cmd of existingCommands) {
6587
+ commandMap.set(cmd.name, cmd);
6588
+ }
6589
+ for (const cmd of bundle.commands) {
6590
+ commandMap.set(cmd.name, cmd);
6591
+ }
6592
+ const mergedCommands = Array.from(commandMap.values());
6593
+ await mkdir(dirname3(configPath), { recursive: true });
6594
+ await writeFile2(
6595
+ configPath,
6596
+ JSON.stringify({ version: "1.0.0", commands: mergedCommands }, null, 2)
6597
+ );
6598
+ const imported = bundle.commands.length;
6599
+ this.context.stdout.write(`Imported ${imported} commands
6600
+ `);
6601
+ this.context.stdout.write(`Total commands: ${mergedCommands.length}
6602
+ `);
6603
+ this.context.stdout.write(`Saved to: ${configPath}
6604
+ `);
6605
+ return 0;
6606
+ }
6607
+ async showInfo() {
6608
+ const agent = this.agent || "claude-code";
6609
+ const format = getAgentFormat(agent);
6610
+ if (this.json) {
6611
+ this.context.stdout.write(JSON.stringify(format, null, 2) + "\n");
6612
+ } else {
6613
+ this.context.stdout.write(`Agent: ${agent}
6614
+ `);
6615
+ this.context.stdout.write(`Directory: ${format.directory}
6616
+ `);
6617
+ this.context.stdout.write(`Extension: ${format.extension}
6618
+ `);
6619
+ this.context.stdout.write(`Supports Slash Commands: ${format.supportsSlashCommands}
6620
+ `);
6621
+ this.context.stdout.write(`Supports Command Files: ${format.supportsCommandFiles}
6622
+ `);
6623
+ }
6624
+ return 0;
6625
+ }
6626
+ async loadCommandsFromConfig(registry) {
6627
+ const configPath = resolve13(process.cwd(), ".skillkit", "commands.json");
6628
+ if (existsSync13(configPath)) {
6629
+ const content = await readFile2(configPath, "utf-8");
6630
+ const config = JSON.parse(content);
6631
+ const commands = config.commands || [];
6632
+ for (const command of commands) {
6633
+ try {
6634
+ registry.register(command);
6635
+ } catch {
6636
+ }
6637
+ }
6638
+ }
6639
+ }
6640
+ };
6641
+
6642
+ // src/commands/ai.ts
6643
+ import { Command as Command33, Option as Option32 } from "clipanion";
6644
+ import { resolve as resolve14 } from "path";
6645
+ import { promises as fs2 } from "fs";
6646
+ import chalk30 from "chalk";
6647
+ import ora7 from "ora";
6648
+ import {
6649
+ AIManager,
6650
+ loadIndex as loadIndexFromCache2
6651
+ } from "@skillkit/core";
6652
+ var AICommand = class extends Command33 {
6653
+ static paths = [["ai"]];
6654
+ static usage = Command33.Usage({
6655
+ description: "AI-powered skill search and generation",
6656
+ details: `
6657
+ The ai command provides AI-powered features for skills:
6658
+
6659
+ - Natural language search: Find skills using conversational queries
6660
+ - Skill generation: Automatically generate skills from examples
6661
+ - Similar skills: Find skills similar to a given skill
6662
+
6663
+ Note: Without an API key, the command uses a basic mock AI provider.
6664
+ Set ANTHROPIC_API_KEY or OPENAI_API_KEY to use real AI features.
6665
+ `,
6666
+ examples: [
6667
+ ["Search skills naturally", '$0 ai search "help me write better tests"'],
6668
+ ["Generate skill from description", '$0 ai generate --description "skill for code review"'],
6669
+ ["Generate from code example", '$0 ai generate --from-code example.ts --description "..."'],
6670
+ ["Find similar skills", "$0 ai similar my-skill-name"]
6671
+ ]
6672
+ });
6673
+ // Subcommand
6674
+ subcommand = Option32.String({ required: true });
6675
+ // Search options
6676
+ query = Option32.String("--query,-q", {
6677
+ description: "Search query for natural language search"
6678
+ });
6679
+ // Generation options
6680
+ description = Option32.String("--description,-d", {
6681
+ description: "Description of the skill to generate"
6682
+ });
6683
+ fromCode = Option32.String("--from-code", {
6684
+ description: "Generate skill from code example file"
6685
+ });
6686
+ additionalContext = Option32.String("--context", {
6687
+ description: "Additional context for generation"
6688
+ });
6689
+ targetAgent = Option32.String("--agent,-a", {
6690
+ description: "Target agent for generated skill"
6691
+ });
6692
+ // Similar skills option
6693
+ skillName = Option32.String({ required: false });
6694
+ // Common options
6695
+ limit = Option32.String("--limit,-l", {
6696
+ description: "Maximum number of results"
6697
+ });
6698
+ minRelevance = Option32.String("--min-relevance", {
6699
+ description: "Minimum relevance score (0-1)"
6700
+ });
6701
+ json = Option32.Boolean("--json,-j", false, {
6702
+ description: "Output in JSON format"
6703
+ });
6704
+ output = Option32.String("--output,-o", {
6705
+ description: "Output file for generated skill"
6706
+ });
6707
+ async execute() {
6708
+ const aiConfig = this.getAIConfig();
6709
+ const manager = new AIManager(aiConfig);
6710
+ if (!this.json) {
6711
+ const providerName = manager.getProviderName();
6712
+ if (providerName === "mock") {
6713
+ console.log(
6714
+ chalk30.yellow(
6715
+ "\u26A0 Using mock AI provider (limited functionality)\n Set ANTHROPIC_API_KEY or OPENAI_API_KEY for real AI features\n"
6716
+ )
6717
+ );
6718
+ }
6719
+ }
6720
+ switch (this.subcommand) {
6721
+ case "search":
6722
+ return await this.handleSearch(manager);
6723
+ case "generate":
6724
+ case "gen":
6725
+ return await this.handleGenerate(manager);
6726
+ case "similar":
6727
+ return await this.handleSimilar(manager);
6728
+ default:
6729
+ console.error(
6730
+ chalk30.red(`Unknown subcommand: ${this.subcommand}
6731
+ `)
6732
+ );
6733
+ console.log("Valid subcommands: search, generate, similar");
6734
+ return 1;
6735
+ }
6736
+ }
6737
+ async handleSearch(manager) {
6738
+ const query = this.query || this.skillName;
6739
+ if (!query) {
6740
+ console.error(chalk30.red("Search query required (--query or positional argument)"));
6741
+ return 1;
6742
+ }
6743
+ const skills = await this.loadSkills();
6744
+ if (skills.length === 0) {
6745
+ console.log(
6746
+ chalk30.yellow('No skills found. Run "skillkit recommend --update" first.')
6747
+ );
6748
+ return 0;
6749
+ }
6750
+ const spinner = ora7("Searching with AI...").start();
6751
+ try {
6752
+ const results = await manager.searchSkills(query, skills, {
6753
+ limit: this.limit ? parseInt(this.limit, 10) : 10,
6754
+ minRelevance: this.minRelevance ? parseFloat(this.minRelevance) : 0.5,
6755
+ includeReasoning: !this.json
6756
+ });
6757
+ spinner.stop();
6758
+ if (this.json) {
6759
+ console.log(JSON.stringify(results, null, 2));
6760
+ return 0;
6761
+ }
6762
+ if (results.length === 0) {
6763
+ console.log(chalk30.yellow(`No skills found matching "${query}"`));
6764
+ return 0;
6765
+ }
6766
+ console.log(chalk30.cyan(`
6767
+ AI Search Results (${results.length} found):
6768
+ `));
6769
+ for (const result of results) {
6770
+ const score = Math.round(result.relevance * 100);
6771
+ const scoreColor = score >= 70 ? chalk30.green : score >= 50 ? chalk30.yellow : chalk30.dim;
6772
+ console.log(
6773
+ ` ${scoreColor(`${score}%`)} ${chalk30.bold(result.skill.name)}`
6774
+ );
6775
+ if (result.skill.description) {
6776
+ console.log(` ${chalk30.dim(result.skill.description)}`);
6777
+ }
6778
+ if (result.reasoning) {
6779
+ console.log(` ${chalk30.dim("\u2192 " + result.reasoning)}`);
6780
+ }
6781
+ console.log();
6782
+ }
6783
+ return 0;
6784
+ } catch (error) {
6785
+ spinner.fail("Search failed");
6786
+ console.error(
6787
+ chalk30.red(error instanceof Error ? error.message : String(error))
6788
+ );
6789
+ return 1;
6790
+ }
6791
+ }
6792
+ async handleGenerate(manager) {
6793
+ const description = this.description;
6794
+ if (!description) {
6795
+ console.error(chalk30.red("Description required (--description)"));
6796
+ return 1;
6797
+ }
6798
+ let codeExamples = [];
6799
+ if (this.fromCode) {
6800
+ const codePath = resolve14(this.fromCode);
6801
+ try {
6802
+ const code = await fs2.readFile(codePath, "utf-8");
6803
+ codeExamples = [code];
6804
+ } catch {
6805
+ console.error(chalk30.red(`Failed to read code file: ${codePath}`));
6806
+ return 1;
6807
+ }
6808
+ }
6809
+ const example = {
6810
+ description,
6811
+ context: this.additionalContext,
6812
+ codeExamples,
6813
+ targetAgent: this.targetAgent
6814
+ };
6815
+ const spinner = ora7("Generating skill with AI...").start();
6816
+ try {
6817
+ const generated = await manager.generateSkill(example, {
6818
+ targetAgent: this.targetAgent,
6819
+ includeTests: true,
6820
+ includeDocumentation: true
6821
+ });
6822
+ spinner.stop();
6823
+ const validation = manager.validateGenerated(generated);
6824
+ if (!validation.valid) {
6825
+ console.log(chalk30.yellow("\n\u26A0 Generated skill has validation warnings:"));
6826
+ for (const error of validation.errors) {
6827
+ console.log(chalk30.dim(` \u2022 ${error}`));
6828
+ }
6829
+ console.log();
6830
+ }
6831
+ if (this.json) {
6832
+ console.log(JSON.stringify(generated, null, 2));
6833
+ return 0;
6834
+ }
6835
+ console.log(chalk30.green("\n\u2713 Generated skill successfully\n"));
6836
+ console.log(chalk30.cyan("Name:") + ` ${generated.name}`);
6837
+ console.log(chalk30.cyan("Description:") + ` ${generated.description}`);
6838
+ console.log(
6839
+ chalk30.cyan("Tags:") + ` ${generated.tags.join(", ")}`
6840
+ );
6841
+ console.log(
6842
+ chalk30.cyan("Confidence:") + ` ${Math.round(generated.confidence * 100)}%`
6843
+ );
6844
+ if (generated.reasoning) {
6845
+ console.log(chalk30.cyan("Reasoning:") + ` ${generated.reasoning}`);
6846
+ }
6847
+ console.log("\n" + chalk30.dim("Content:"));
6848
+ console.log(chalk30.dim("\u2500".repeat(60)));
6849
+ console.log(generated.content);
6850
+ console.log(chalk30.dim("\u2500".repeat(60)));
6851
+ if (this.output) {
6852
+ const outputPath = resolve14(this.output);
6853
+ await fs2.writeFile(outputPath, generated.content, "utf-8");
6854
+ console.log(chalk30.green(`
6855
+ \u2713 Saved to: ${outputPath}`));
6856
+ } else {
6857
+ console.log(
6858
+ chalk30.dim("\nSave with: --output <filename>")
6859
+ );
6860
+ }
6861
+ return 0;
6862
+ } catch (error) {
6863
+ spinner.fail("Generation failed");
6864
+ console.error(
6865
+ chalk30.red(error instanceof Error ? error.message : String(error))
6866
+ );
6867
+ return 1;
6868
+ }
6869
+ }
6870
+ async handleSimilar(manager) {
6871
+ const skillName = this.skillName;
6872
+ if (!skillName) {
6873
+ console.error(chalk30.red("Skill name required"));
6874
+ return 1;
6875
+ }
6876
+ const skills = await this.loadSkills();
6877
+ if (skills.length === 0) {
6878
+ console.log(
6879
+ chalk30.yellow('No skills found. Run "skillkit recommend --update" first.')
6880
+ );
6881
+ return 0;
6882
+ }
6883
+ const targetSkill = skills.find(
6884
+ (s) => s.name.toLowerCase() === skillName.toLowerCase()
6885
+ );
6886
+ if (!targetSkill) {
6887
+ console.error(chalk30.red(`Skill not found: ${skillName}`));
6888
+ return 1;
6889
+ }
6890
+ const spinner = ora7("Finding similar skills...").start();
6891
+ try {
6892
+ const results = await manager.findSimilar(targetSkill, skills, {
6893
+ limit: this.limit ? parseInt(this.limit, 10) : 10,
6894
+ minRelevance: this.minRelevance ? parseFloat(this.minRelevance) : 0.5,
6895
+ includeReasoning: !this.json
6896
+ });
6897
+ spinner.stop();
6898
+ if (this.json) {
6899
+ console.log(JSON.stringify(results, null, 2));
6900
+ return 0;
6901
+ }
6902
+ if (results.length === 0) {
6903
+ console.log(
6904
+ chalk30.yellow(`No similar skills found for "${skillName}"`)
6905
+ );
6906
+ return 0;
6907
+ }
6908
+ console.log(
6909
+ chalk30.cyan(`
6910
+ Skills similar to "${skillName}" (${results.length} found):
6911
+ `)
6912
+ );
6913
+ for (const result of results) {
6914
+ const score = Math.round(result.relevance * 100);
6915
+ const scoreColor = score >= 70 ? chalk30.green : score >= 50 ? chalk30.yellow : chalk30.dim;
6916
+ console.log(
6917
+ ` ${scoreColor(`${score}%`)} ${chalk30.bold(result.skill.name)}`
6918
+ );
6919
+ if (result.skill.description) {
6920
+ console.log(` ${chalk30.dim(result.skill.description)}`);
6921
+ }
6922
+ if (result.reasoning) {
6923
+ console.log(` ${chalk30.dim("\u2192 " + result.reasoning)}`);
6924
+ }
6925
+ console.log();
6926
+ }
6927
+ return 0;
6928
+ } catch (error) {
6929
+ spinner.fail("Search failed");
6930
+ console.error(
6931
+ chalk30.red(error instanceof Error ? error.message : String(error))
6932
+ );
6933
+ return 1;
6934
+ }
6935
+ }
6936
+ async loadSkills() {
6937
+ const index = loadIndexFromCache2();
6938
+ if (!index) {
6939
+ return [];
6940
+ }
6941
+ return index.skills.map((s) => ({
6942
+ name: s.name,
6943
+ description: s.description,
6944
+ content: "",
6945
+ tags: s.tags,
6946
+ source: s.source
6947
+ }));
6948
+ }
6949
+ getAIConfig() {
6950
+ const apiKey = process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
6951
+ return {
6952
+ provider: process.env.ANTHROPIC_API_KEY ? "anthropic" : process.env.OPENAI_API_KEY ? "openai" : "none",
6953
+ apiKey,
6954
+ model: process.env.ANTHROPIC_API_KEY ? "claude-3-sonnet-20240229" : void 0,
6955
+ maxTokens: 4096,
6956
+ temperature: 0.7
6957
+ };
6958
+ }
6959
+ };
6960
+
6961
+ // src/commands/audit.ts
6962
+ import { Command as Command34, Option as Option33 } from "clipanion";
6963
+ import { resolve as resolve15 } from "path";
6964
+ import { promises as fs3 } from "fs";
6965
+ import path2 from "path";
6966
+ import chalk31 from "chalk";
6967
+ import { AuditLogger } from "@skillkit/core";
6968
+ var AuditCommand = class extends Command34 {
6969
+ static paths = [["audit"]];
6970
+ static usage = Command34.Usage({
6971
+ description: "View and manage audit logs",
6972
+ details: `
6973
+ The audit command provides access to the audit log system.
6974
+ All skill operations, team activities, and plugin actions are logged.
6975
+
6976
+ Subcommands:
6977
+ log View recent audit log entries
6978
+ export Export audit logs to file
6979
+ stats Show audit statistics
6980
+ clear Clear old audit entries
6981
+ `,
6982
+ examples: [
6983
+ ["View recent logs", "$0 audit log"],
6984
+ ["View logs with filters", "$0 audit log --type skill.install --limit 20"],
6985
+ ["Export logs to JSON", "$0 audit export --format json --output audit.json"],
6986
+ ["Show statistics", "$0 audit stats"],
6987
+ ["Clear logs older than 30 days", "$0 audit clear --days 30"]
6988
+ ]
6989
+ });
6990
+ subcommand = Option33.String({ required: true });
6991
+ // Query options
6992
+ type = Option33.Array("--type,-t", {
6993
+ description: "Filter by event type"
6994
+ });
6995
+ user = Option33.String("--user,-u", {
6996
+ description: "Filter by user"
6997
+ });
6998
+ resource = Option33.String("--resource,-r", {
6999
+ description: "Filter by resource"
7000
+ });
7001
+ success = Option33.Boolean("--success", {
7002
+ description: "Show only successful operations"
7003
+ });
7004
+ failed = Option33.Boolean("--failed", {
7005
+ description: "Show only failed operations"
7006
+ });
7007
+ since = Option33.String("--since", {
7008
+ description: "Show events since date (ISO 8601)"
7009
+ });
7010
+ until = Option33.String("--until", {
7011
+ description: "Show events until date (ISO 8601)"
7012
+ });
7013
+ limit = Option33.String("--limit,-l", {
7014
+ description: "Maximum number of entries"
7015
+ });
7016
+ // Export options
7017
+ format = Option33.String("--format,-f", {
7018
+ description: "Export format (json, csv, text)"
7019
+ });
7020
+ output = Option33.String("--output,-o", {
7021
+ description: "Output file path"
7022
+ });
7023
+ // Clear options
7024
+ days = Option33.String("--days", {
7025
+ description: "Clear entries older than N days"
7026
+ });
7027
+ // Common options
7028
+ json = Option33.Boolean("--json,-j", false, {
7029
+ description: "Output in JSON format"
7030
+ });
7031
+ projectPath = Option33.String("--path,-p", {
7032
+ description: "Project path (default: current directory)"
7033
+ });
7034
+ async execute() {
7035
+ const targetPath = resolve15(this.projectPath || process.cwd());
7036
+ const logDir = path2.join(targetPath, ".skillkit");
7037
+ const logger = new AuditLogger(logDir);
7038
+ try {
7039
+ switch (this.subcommand) {
7040
+ case "log":
7041
+ case "list":
7042
+ return await this.handleLog(logger);
7043
+ case "export":
7044
+ return await this.handleExport(logger);
7045
+ case "stats":
7046
+ return await this.handleStats(logger);
7047
+ case "clear":
7048
+ return await this.handleClear(logger);
7049
+ default:
7050
+ console.error(chalk31.red(`Unknown subcommand: ${this.subcommand}
7051
+ `));
7052
+ console.log("Valid subcommands: log, export, stats, clear");
7053
+ return 1;
7054
+ }
7055
+ } finally {
7056
+ await logger.destroy();
7057
+ }
7058
+ }
7059
+ async handleLog(logger) {
7060
+ const query = this.buildQuery();
7061
+ const events = await logger.query(query);
7062
+ if (this.json) {
7063
+ console.log(JSON.stringify(events, null, 2));
7064
+ return 0;
7065
+ }
7066
+ if (events.length === 0) {
7067
+ console.log(chalk31.yellow("No audit events found"));
7068
+ return 0;
7069
+ }
7070
+ console.log(chalk31.cyan(`
7071
+ Audit Log (${events.length} entries):
7072
+ `));
7073
+ for (const event of events) {
7074
+ const statusIcon = event.success ? chalk31.green("\u2713") : chalk31.red("\u2717");
7075
+ const timestamp = event.timestamp.toLocaleString();
7076
+ console.log(
7077
+ `${statusIcon} ${chalk31.dim(timestamp)} ${chalk31.bold(event.type)}`
7078
+ );
7079
+ console.log(
7080
+ ` ${event.action} on ${chalk31.cyan(event.resource)}`
7081
+ );
7082
+ if (event.user) {
7083
+ console.log(` ${chalk31.dim("User:")} ${event.user}`);
7084
+ }
7085
+ if (event.duration) {
7086
+ console.log(` ${chalk31.dim("Duration:")} ${event.duration}ms`);
7087
+ }
7088
+ if (event.error) {
7089
+ console.log(` ${chalk31.red("Error:")} ${event.error}`);
7090
+ }
7091
+ if (event.details && Object.keys(event.details).length > 0) {
7092
+ console.log(` ${chalk31.dim("Details:")} ${JSON.stringify(event.details)}`);
7093
+ }
7094
+ console.log();
7095
+ }
7096
+ return 0;
7097
+ }
7098
+ async handleExport(logger) {
7099
+ const format = this.format || "json";
7100
+ const query = this.buildQuery();
7101
+ const content = await logger.export({ format, query });
7102
+ if (this.output) {
7103
+ const outputPath = resolve15(this.output);
7104
+ await fs3.writeFile(outputPath, content, "utf-8");
7105
+ console.log(chalk31.green(`\u2713 Exported audit log to: ${outputPath}`));
7106
+ } else {
7107
+ console.log(content);
7108
+ }
7109
+ return 0;
7110
+ }
7111
+ async handleStats(logger) {
7112
+ const stats = await logger.stats();
7113
+ if (this.json) {
7114
+ console.log(JSON.stringify(stats, null, 2));
7115
+ return 0;
7116
+ }
7117
+ console.log(chalk31.cyan("\nAudit Statistics:\n"));
7118
+ console.log(`Total Events: ${chalk31.bold(stats.totalEvents.toString())}`);
7119
+ console.log(
7120
+ `Success Rate: ${chalk31.bold(`${(stats.successRate * 100).toFixed(1)}%`)}`
7121
+ );
7122
+ if (Object.keys(stats.eventsByType).length > 0) {
7123
+ console.log(chalk31.cyan("\nEvents by Type:"));
7124
+ const sorted = Object.entries(stats.eventsByType).sort(
7125
+ ([, a], [, b]) => b - a
7126
+ );
7127
+ for (const [type, count] of sorted) {
7128
+ console.log(` ${type.padEnd(30)} ${count}`);
7129
+ }
7130
+ }
7131
+ if (stats.topResources.length > 0) {
7132
+ console.log(chalk31.cyan("\nTop Resources:"));
7133
+ for (const { resource, count } of stats.topResources) {
7134
+ console.log(` ${resource.padEnd(40)} ${count}`);
7135
+ }
7136
+ }
7137
+ if (stats.recentErrors.length > 0) {
7138
+ console.log(chalk31.cyan(`
7139
+ Recent Errors (${stats.recentErrors.length}):`));
7140
+ for (const error of stats.recentErrors.slice(0, 5)) {
7141
+ const timestamp = error.timestamp.toLocaleString();
7142
+ console.log(
7143
+ ` ${chalk31.red("\u2717")} ${chalk31.dim(timestamp)} ${error.type}`
7144
+ );
7145
+ if (error.error) {
7146
+ console.log(` ${chalk31.dim(error.error)}`);
7147
+ }
7148
+ }
7149
+ }
7150
+ console.log();
7151
+ return 0;
7152
+ }
7153
+ async handleClear(logger) {
7154
+ if (!this.days) {
7155
+ console.error(chalk31.red("--days option required"));
7156
+ return 1;
7157
+ }
7158
+ const daysAgo = parseInt(this.days, 10);
7159
+ const cutoffDate = /* @__PURE__ */ new Date();
7160
+ cutoffDate.setDate(cutoffDate.getDate() - daysAgo);
7161
+ const cleared = await logger.clear(cutoffDate);
7162
+ console.log(
7163
+ chalk31.green(
7164
+ `\u2713 Cleared ${cleared} audit entries older than ${daysAgo} days`
7165
+ )
7166
+ );
7167
+ return 0;
7168
+ }
7169
+ buildQuery() {
7170
+ const query = {};
7171
+ if (this.type && this.type.length > 0) {
7172
+ query.types = this.type;
7173
+ }
7174
+ if (this.user) {
7175
+ query.user = this.user;
7176
+ }
7177
+ if (this.resource) {
7178
+ query.resource = this.resource;
7179
+ }
7180
+ if (this.success) {
7181
+ query.success = true;
7182
+ } else if (this.failed) {
7183
+ query.success = false;
7184
+ }
7185
+ if (this.since) {
7186
+ query.startDate = new Date(this.since);
7187
+ }
7188
+ if (this.until) {
7189
+ query.endDate = new Date(this.until);
7190
+ }
7191
+ if (this.limit) {
7192
+ query.limit = parseInt(this.limit, 10);
7193
+ } else {
7194
+ query.limit = 50;
7195
+ }
7196
+ return query;
7197
+ }
7198
+ };
5238
7199
  export {
7200
+ AICommand,
7201
+ AuditCommand,
5239
7202
  CICDCommand,
7203
+ CommandCmd,
5240
7204
  ContextCommand,
5241
7205
  CreateCommand,
5242
7206
  DisableCommand,
5243
7207
  EnableCommand,
7208
+ HookCommand,
5244
7209
  InitCommand,
5245
7210
  InstallCommand,
5246
7211
  ListCommand,
5247
7212
  MarketplaceCommand,
5248
7213
  MemoryCommand,
7214
+ MethodologyCommand,
5249
7215
  PauseCommand,
7216
+ PlanCommand,
5250
7217
  PluginCommand,
5251
7218
  ReadCommand,
5252
7219
  RecommendCommand,