@skillkit/cli 1.4.0 → 1.5.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
@@ -2356,7 +2356,9 @@ import {
2356
2356
  loadWorkflowByName,
2357
2357
  loadWorkflow,
2358
2358
  validateWorkflow,
2359
- createWorkflowOrchestrator
2359
+ createWorkflowOrchestrator,
2360
+ createSkillExecutor,
2361
+ createSimulatedSkillExecutor
2360
2362
  } from "@skillkit/core";
2361
2363
  var WorkflowRunCommand = class extends Command18 {
2362
2364
  static paths = [["workflow", "run"], ["wf", "run"]];
@@ -2400,6 +2402,14 @@ var WorkflowRunCommand = class extends Command18 {
2400
2402
  projectPath = Option17.String("--path,-p", {
2401
2403
  description: "Project path (default: current directory)"
2402
2404
  });
2405
+ // Agent to use for execution
2406
+ agent = Option17.String("--agent,-a", {
2407
+ description: "Agent to use for skill execution (e.g., claude-code, codex)"
2408
+ });
2409
+ // Simulate execution (for testing)
2410
+ simulate = Option17.Boolean("--simulate", false, {
2411
+ description: "Simulate execution without running skills (for testing)"
2412
+ });
2403
2413
  async execute() {
2404
2414
  const targetPath = resolve6(this.projectPath || process.cwd());
2405
2415
  let workflow;
@@ -2440,11 +2450,40 @@ var WorkflowRunCommand = class extends Command18 {
2440
2450
  console.log();
2441
2451
  const spinner = ora4();
2442
2452
  let currentWave = -1;
2453
+ const skillExecutor = this.simulate ? createSimulatedSkillExecutor({
2454
+ delay: 500,
2455
+ onExecute: (skillName) => {
2456
+ if (this.verbose && !this.json) {
2457
+ console.log(chalk17.dim(` [Simulated] ${skillName}`));
2458
+ }
2459
+ }
2460
+ }) : createSkillExecutor({
2461
+ projectPath: targetPath,
2462
+ preferredAgent: this.agent,
2463
+ fallbackToAvailable: true,
2464
+ onExecutionEvent: (event) => {
2465
+ if (this.verbose && !this.json) {
2466
+ switch (event.type) {
2467
+ case "skill_found":
2468
+ console.log(chalk17.dim(` Found: ${event.message}`));
2469
+ break;
2470
+ case "skill_not_found":
2471
+ console.log(chalk17.red(` Not found: ${event.skillName}`));
2472
+ break;
2473
+ case "agent_selected":
2474
+ console.log(chalk17.dim(` Agent: ${event.agent}`));
2475
+ break;
2476
+ case "execution_complete":
2477
+ if (!event.success) {
2478
+ console.log(chalk17.red(` Error: ${event.error}`));
2479
+ }
2480
+ break;
2481
+ }
2482
+ }
2483
+ }
2484
+ });
2443
2485
  const orchestrator = createWorkflowOrchestrator(
2444
- async (_skillName, _config) => {
2445
- await new Promise((resolve11) => setTimeout(resolve11, 500));
2446
- return { success: true };
2447
- },
2486
+ skillExecutor,
2448
2487
  (event) => {
2449
2488
  if (this.json) return;
2450
2489
  switch (event.type) {
@@ -3764,12 +3803,12 @@ ${learning.title}
3764
3803
  }
3765
3804
  const outputPath = this.output || `.skillkit/exports/${skillName}/SKILL.md`;
3766
3805
  const { dirname: dirname3 } = await import("path");
3767
- const { existsSync: existsSync11, mkdirSync: mkdirSync5, writeFileSync: writeFileSync5 } = await import("fs");
3806
+ const { existsSync: existsSync13, mkdirSync: mkdirSync7, writeFileSync: writeFileSync6 } = await import("fs");
3768
3807
  const outputDir = dirname3(outputPath);
3769
- if (!existsSync11(outputDir)) {
3770
- mkdirSync5(outputDir, { recursive: true });
3808
+ if (!existsSync13(outputDir)) {
3809
+ mkdirSync7(outputDir, { recursive: true });
3771
3810
  }
3772
- writeFileSync5(outputPath, skillContent, "utf-8");
3811
+ writeFileSync6(outputPath, skillContent, "utf-8");
3773
3812
  console.log(chalk23.green(`\u2713 Exported learning as skill: ${skillName}`));
3774
3813
  console.log(chalk23.gray(` Path: ${outputPath}`));
3775
3814
  return 0;
@@ -3784,10 +3823,10 @@ ${learning.title}
3784
3823
  console.log(chalk23.gray("Usage: skillkit memory import --input <path>"));
3785
3824
  return 1;
3786
3825
  }
3787
- const { existsSync: existsSync11, readFileSync: readFileSync6 } = await import("fs");
3788
- const { resolve: resolve11 } = await import("path");
3789
- const fullPath = resolve11(inputPath);
3790
- if (!existsSync11(fullPath)) {
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)) {
3791
3830
  console.error(chalk23.red(`File not found: ${fullPath}`));
3792
3831
  return 1;
3793
3832
  }
@@ -3797,7 +3836,7 @@ ${learning.title}
3797
3836
  this.global ? void 0 : projectPath
3798
3837
  );
3799
3838
  try {
3800
- const content = readFileSync6(fullPath, "utf-8");
3839
+ const content = readFileSync7(fullPath, "utf-8");
3801
3840
  const { parse: parseYaml2 } = await import("yaml");
3802
3841
  const data = parseYaml2(content);
3803
3842
  if (!data.learnings || !Array.isArray(data.learnings)) {
@@ -4040,7 +4079,1164 @@ ${learning.title}
4040
4079
  return lines.join("\n");
4041
4080
  }
4042
4081
  };
4082
+
4083
+ // src/commands/settings.ts
4084
+ import { Command as Command25, Option as Option24 } from "clipanion";
4085
+ import chalk24 from "chalk";
4086
+ import { loadConfig as loadConfig3, saveConfig } from "@skillkit/core";
4087
+ var VALID_AGENTS = [
4088
+ "claude-code",
4089
+ "cursor",
4090
+ "codex",
4091
+ "gemini-cli",
4092
+ "opencode",
4093
+ "antigravity",
4094
+ "amp",
4095
+ "clawdbot",
4096
+ "droid",
4097
+ "github-copilot",
4098
+ "goose",
4099
+ "kilo",
4100
+ "kiro-cli",
4101
+ "roo",
4102
+ "trae",
4103
+ "windsurf",
4104
+ "universal"
4105
+ ];
4106
+ var SettingsCommand = class extends Command25 {
4107
+ static paths = [["settings"], ["config"]];
4108
+ static usage = Command25.Usage({
4109
+ description: "View and modify SkillKit settings",
4110
+ details: `
4111
+ View or modify SkillKit configuration. Settings are stored in skillkit.yaml.
4112
+
4113
+ Without arguments, shows all current settings.
4114
+ With --set, modifies a specific setting.
4115
+ `,
4116
+ examples: [
4117
+ ["Show all settings", "$0 settings"],
4118
+ ["Set default agent", "$0 settings --set agent=claude-code"],
4119
+ ["Enable auto-sync", "$0 settings --set autoSync=true"],
4120
+ ["Set cache directory", "$0 settings --set cacheDir=~/.cache/skillkit"],
4121
+ ["Show settings as JSON", "$0 settings --json"],
4122
+ ["Save to global config", "$0 settings --set agent=cursor --global"]
4123
+ ]
4124
+ });
4125
+ // Set a setting
4126
+ set = Option24.String("--set,-s", {
4127
+ description: "Set a config value (key=value)"
4128
+ });
4129
+ // Get a specific setting
4130
+ get = Option24.String("--get,-g", {
4131
+ description: "Get a specific setting value"
4132
+ });
4133
+ // JSON output
4134
+ json = Option24.Boolean("--json,-j", false, {
4135
+ description: "Output as JSON"
4136
+ });
4137
+ // Global config
4138
+ global = Option24.Boolean("--global", false, {
4139
+ description: "Use global config (~/.config/skillkit/)"
4140
+ });
4141
+ // Reset to defaults
4142
+ reset = Option24.Boolean("--reset", false, {
4143
+ description: "Reset all settings to defaults"
4144
+ });
4145
+ async execute() {
4146
+ const config = loadConfig3(this.global);
4147
+ if (this.reset) {
4148
+ const defaultConfig = {
4149
+ version: 1,
4150
+ agent: "universal",
4151
+ autoSync: true
4152
+ };
4153
+ saveConfig(defaultConfig, this.global);
4154
+ console.log(chalk24.green("Settings reset to defaults"));
4155
+ return 0;
4156
+ }
4157
+ if (this.get) {
4158
+ const knownKeys = [
4159
+ "agent",
4160
+ "autoSync",
4161
+ "cacheDir",
4162
+ "skillsDir",
4163
+ "enabledSkills",
4164
+ "disabledSkills",
4165
+ "marketplaceSources",
4166
+ "defaultTimeout"
4167
+ ];
4168
+ if (!knownKeys.includes(this.get)) {
4169
+ console.error(chalk24.red(`Unknown setting: ${this.get}`));
4170
+ return 1;
4171
+ }
4172
+ const value = this.getConfigValue(config, this.get);
4173
+ if (this.json) {
4174
+ console.log(JSON.stringify({ [this.get]: value ?? null }));
4175
+ } else {
4176
+ console.log(value ?? "(not set)");
4177
+ }
4178
+ return 0;
4179
+ }
4180
+ if (this.set) {
4181
+ const [key, ...valueParts] = this.set.split("=");
4182
+ const value = valueParts.join("=");
4183
+ if (!key || !this.set.includes("=")) {
4184
+ console.error(chalk24.red("Invalid format. Use: --set key=value"));
4185
+ return 1;
4186
+ }
4187
+ const result = this.setConfigValue(config, key, value);
4188
+ if (!result.success) {
4189
+ console.error(chalk24.red(result.error));
4190
+ return 1;
4191
+ }
4192
+ saveConfig(config, this.global);
4193
+ console.log(chalk24.green(`${key} = ${value}`));
4194
+ return 0;
4195
+ }
4196
+ if (this.json) {
4197
+ console.log(JSON.stringify(config, null, 2));
4198
+ } else {
4199
+ console.log(chalk24.cyan("SkillKit Settings"));
4200
+ console.log(chalk24.dim("\u2500".repeat(40)));
4201
+ console.log();
4202
+ const settings = [
4203
+ { key: "agent", label: "Default Agent", value: config.agent },
4204
+ { key: "autoSync", label: "Auto Sync", value: config.autoSync ? "enabled" : "disabled" },
4205
+ { key: "cacheDir", label: "Cache Dir", value: config.cacheDir || "~/.skillkit/cache" },
4206
+ { key: "skillsDir", label: "Skills Dir", value: config.skillsDir || "(default)" },
4207
+ { key: "defaultTimeout", label: "Timeout", value: config.defaultTimeout ? `${config.defaultTimeout}ms` : "(default)" }
4208
+ ];
4209
+ for (const setting of settings) {
4210
+ console.log(` ${chalk24.white(setting.label.padEnd(14))} ${chalk24.dim(setting.value)}`);
4211
+ }
4212
+ if (config.enabledSkills?.length) {
4213
+ console.log();
4214
+ console.log(` ${chalk24.white("Enabled Skills".padEnd(14))} ${chalk24.dim(config.enabledSkills.join(", "))}`);
4215
+ }
4216
+ if (config.disabledSkills?.length) {
4217
+ console.log(` ${chalk24.white("Disabled Skills".padEnd(14))} ${chalk24.dim(config.disabledSkills.join(", "))}`);
4218
+ }
4219
+ if (config.marketplaceSources?.length) {
4220
+ console.log(` ${chalk24.white("Marketplaces".padEnd(14))} ${chalk24.dim(config.marketplaceSources.join(", "))}`);
4221
+ }
4222
+ console.log();
4223
+ console.log(chalk24.dim("Use --set key=value to modify settings"));
4224
+ console.log(chalk24.dim("Available keys: agent, autoSync, cacheDir, skillsDir, defaultTimeout"));
4225
+ }
4226
+ return 0;
4227
+ }
4228
+ getConfigValue(config, key) {
4229
+ switch (key) {
4230
+ case "agent":
4231
+ return config.agent;
4232
+ case "autoSync":
4233
+ return config.autoSync;
4234
+ case "cacheDir":
4235
+ return config.cacheDir;
4236
+ case "skillsDir":
4237
+ return config.skillsDir;
4238
+ case "enabledSkills":
4239
+ return config.enabledSkills;
4240
+ case "disabledSkills":
4241
+ return config.disabledSkills;
4242
+ case "marketplaceSources":
4243
+ return config.marketplaceSources;
4244
+ case "defaultTimeout":
4245
+ return config.defaultTimeout;
4246
+ default:
4247
+ return void 0;
4248
+ }
4249
+ }
4250
+ setConfigValue(config, key, value) {
4251
+ switch (key) {
4252
+ case "agent":
4253
+ if (!VALID_AGENTS.includes(value)) {
4254
+ return {
4255
+ success: false,
4256
+ error: `Invalid agent. Valid options: ${VALID_AGENTS.join(", ")}`
4257
+ };
4258
+ }
4259
+ config.agent = value;
4260
+ break;
4261
+ case "autoSync":
4262
+ if (!["true", "false", "enabled", "disabled"].includes(value.toLowerCase())) {
4263
+ return { success: false, error: "autoSync must be true/false or enabled/disabled" };
4264
+ }
4265
+ config.autoSync = value.toLowerCase() === "true" || value.toLowerCase() === "enabled";
4266
+ break;
4267
+ case "cacheDir":
4268
+ config.cacheDir = value || void 0;
4269
+ break;
4270
+ case "skillsDir":
4271
+ config.skillsDir = value || void 0;
4272
+ break;
4273
+ case "defaultTimeout": {
4274
+ const timeout = parseInt(value, 10);
4275
+ if (isNaN(timeout) || timeout <= 0) {
4276
+ return { success: false, error: "defaultTimeout must be a positive number (milliseconds)" };
4277
+ }
4278
+ config.defaultTimeout = timeout;
4279
+ break;
4280
+ }
4281
+ default:
4282
+ return { success: false, error: `Unknown setting: ${key}` };
4283
+ }
4284
+ return { success: true };
4285
+ }
4286
+ };
4287
+
4288
+ // src/commands/cicd.ts
4289
+ import { Command as Command26, Option as Option25 } from "clipanion";
4290
+ import chalk25 from "chalk";
4291
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5, readFileSync as readFileSync6 } from "fs";
4292
+ import { join as join8 } from "path";
4293
+ var GITHUB_ACTIONS_WORKFLOW = `name: SkillKit CI
4294
+
4295
+ on:
4296
+ push:
4297
+ branches: [main, master]
4298
+ paths:
4299
+ - '.skillkit/**'
4300
+ - 'skills/**'
4301
+ - '.claude/skills/**'
4302
+ - '.cursor/skills/**'
4303
+ pull_request:
4304
+ branches: [main, master]
4305
+ paths:
4306
+ - '.skillkit/**'
4307
+ - 'skills/**'
4308
+ - '.claude/skills/**'
4309
+ - '.cursor/skills/**'
4310
+
4311
+ jobs:
4312
+ validate:
4313
+ name: Validate Skills
4314
+ runs-on: ubuntu-latest
4315
+ steps:
4316
+ - uses: actions/checkout@v4
4317
+
4318
+ - uses: actions/setup-node@v4
4319
+ with:
4320
+ node-version: '20'
4321
+
4322
+ - name: Install SkillKit
4323
+ run: npm install -g skillkit
4324
+
4325
+ - name: Validate Skills
4326
+ run: skillkit validate --all
4327
+
4328
+ - name: List Skills
4329
+ run: skillkit list
4330
+
4331
+ test:
4332
+ name: Test Skills
4333
+ runs-on: ubuntu-latest
4334
+ needs: validate
4335
+ steps:
4336
+ - uses: actions/checkout@v4
4337
+
4338
+ - uses: actions/setup-node@v4
4339
+ with:
4340
+ node-version: '20'
4341
+
4342
+ - name: Install SkillKit
4343
+ run: npm install -g skillkit
4344
+
4345
+ - name: Run Skill Tests
4346
+ run: skillkit test --all
4347
+ continue-on-error: true
4348
+
4349
+ sync:
4350
+ name: Sync Skills
4351
+ runs-on: ubuntu-latest
4352
+ needs: validate
4353
+ if: github.event_name == 'push'
4354
+ steps:
4355
+ - uses: actions/checkout@v4
4356
+
4357
+ - uses: actions/setup-node@v4
4358
+ with:
4359
+ node-version: '20'
4360
+
4361
+ - name: Install SkillKit
4362
+ run: npm install -g skillkit
4363
+
4364
+ - name: Sync Skills to All Agents
4365
+ run: skillkit sync --yes
4366
+ `;
4367
+ var GITLAB_CI = `stages:
4368
+ - validate
4369
+ - test
4370
+ - sync
4371
+
4372
+ variables:
4373
+ NODE_VERSION: "20"
4374
+
4375
+ .skillkit-base:
4376
+ image: node:\${NODE_VERSION}
4377
+ before_script:
4378
+ - npm install -g skillkit
4379
+ rules:
4380
+ - changes:
4381
+ - .skillkit/**/*
4382
+ - skills/**/*
4383
+ - .claude/skills/**/*
4384
+ - .cursor/skills/**/*
4385
+
4386
+ validate:
4387
+ extends: .skillkit-base
4388
+ stage: validate
4389
+ script:
4390
+ - skillkit validate --all
4391
+ - skillkit list
4392
+
4393
+ test:
4394
+ extends: .skillkit-base
4395
+ stage: test
4396
+ script:
4397
+ - skillkit test --all
4398
+ allow_failure: true
4399
+ needs:
4400
+ - validate
4401
+
4402
+ sync:
4403
+ extends: .skillkit-base
4404
+ stage: sync
4405
+ script:
4406
+ - skillkit sync --yes
4407
+ needs:
4408
+ - validate
4409
+ rules:
4410
+ - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "master"
4411
+ changes:
4412
+ - .skillkit/**/*
4413
+ - skills/**/*
4414
+ - .claude/skills/**/*
4415
+ - .cursor/skills/**/*
4416
+ `;
4417
+ var CIRCLECI_CONFIG = `version: 2.1
4418
+
4419
+ executors:
4420
+ node:
4421
+ docker:
4422
+ - image: cimg/node:20.0
4423
+
4424
+ jobs:
4425
+ validate:
4426
+ executor: node
4427
+ steps:
4428
+ - checkout
4429
+ - run:
4430
+ name: Install SkillKit
4431
+ command: npm install -g skillkit
4432
+ - run:
4433
+ name: Validate Skills
4434
+ command: skillkit validate --all
4435
+ - run:
4436
+ name: List Skills
4437
+ command: skillkit list
4438
+
4439
+ test:
4440
+ executor: node
4441
+ steps:
4442
+ - checkout
4443
+ - run:
4444
+ name: Install SkillKit
4445
+ command: npm install -g skillkit
4446
+ - run:
4447
+ name: Run Skill Tests
4448
+ command: skillkit test --all || true
4449
+
4450
+ sync:
4451
+ executor: node
4452
+ steps:
4453
+ - checkout
4454
+ - run:
4455
+ name: Install SkillKit
4456
+ command: npm install -g skillkit
4457
+ - run:
4458
+ name: Sync Skills
4459
+ command: skillkit sync --yes
4460
+
4461
+ workflows:
4462
+ skillkit:
4463
+ jobs:
4464
+ - validate:
4465
+ filters:
4466
+ branches:
4467
+ only:
4468
+ - main
4469
+ - master
4470
+ - test:
4471
+ requires:
4472
+ - validate
4473
+ - sync:
4474
+ requires:
4475
+ - validate
4476
+ filters:
4477
+ branches:
4478
+ only:
4479
+ - main
4480
+ - master
4481
+ `;
4482
+ var CICDCommand = class extends Command26 {
4483
+ static paths = [["cicd", "init"]];
4484
+ static usage = Command26.Usage({
4485
+ description: "Initialize CI/CD workflows for skill validation",
4486
+ details: `
4487
+ The cicd command sets up continuous integration workflows to automatically
4488
+ validate and test skills on every push or pull request.
4489
+
4490
+ Supported providers: github, gitlab, circleci
4491
+ `,
4492
+ examples: [
4493
+ ["Initialize GitHub Actions workflow", "$0 cicd init"],
4494
+ ["Initialize GitLab CI", "$0 cicd init --provider gitlab"],
4495
+ ["Initialize CircleCI", "$0 cicd init --provider circleci"],
4496
+ ["Initialize all providers", "$0 cicd init --all"],
4497
+ ["Force overwrite existing files", "$0 cicd init --force"]
4498
+ ]
4499
+ });
4500
+ // Provider selection
4501
+ provider = Option25.String("--provider,-p", "github", {
4502
+ description: "CI/CD provider (github, gitlab, circleci)"
4503
+ });
4504
+ // Generate for all providers
4505
+ all = Option25.Boolean("--all,-a", false, {
4506
+ description: "Generate workflows for all supported providers"
4507
+ });
4508
+ // Force overwrite
4509
+ force = Option25.Boolean("--force,-f", false, {
4510
+ description: "Overwrite existing workflow files"
4511
+ });
4512
+ // Project path
4513
+ targetPath = Option25.String("--path", {
4514
+ description: "Project path (default: current directory)"
4515
+ });
4516
+ async execute() {
4517
+ const projectPath = this.targetPath || process.cwd();
4518
+ const providers = this.all ? ["github", "gitlab", "circleci"] : [this.provider];
4519
+ console.log(chalk25.cyan("Initializing CI/CD workflows..."));
4520
+ console.log();
4521
+ let success = true;
4522
+ let created = false;
4523
+ for (const provider of providers) {
4524
+ const result = this.createWorkflow(projectPath, provider);
4525
+ if (result.skipped) {
4526
+ console.log(chalk25.yellow(` ${result.message}`));
4527
+ continue;
4528
+ }
4529
+ if (!result.success) {
4530
+ success = false;
4531
+ console.error(chalk25.red(` ${result.message}`));
4532
+ continue;
4533
+ }
4534
+ created = true;
4535
+ console.log(chalk25.green(` \u2713 ${result.message}`));
4536
+ }
4537
+ if (success) {
4538
+ console.log();
4539
+ console.log(
4540
+ created ? chalk25.green("CI/CD workflows initialized successfully!") : chalk25.yellow("CI/CD workflows already exist; nothing to do.")
4541
+ );
4542
+ console.log();
4543
+ if (created) {
4544
+ console.log(chalk25.dim("The workflows will run on push/PR to validate your skills."));
4545
+ console.log(chalk25.dim("Commit the generated files to enable CI/CD."));
4546
+ }
4547
+ }
4548
+ return success ? 0 : 1;
4549
+ }
4550
+ createWorkflow(projectPath, provider) {
4551
+ switch (provider) {
4552
+ case "github":
4553
+ return this.createGitHubActions(projectPath);
4554
+ case "gitlab":
4555
+ return this.createGitLabCI(projectPath);
4556
+ case "circleci":
4557
+ return this.createCircleCI(projectPath);
4558
+ default:
4559
+ return { success: false, message: `Unknown provider: ${provider}` };
4560
+ }
4561
+ }
4562
+ createGitHubActions(projectPath) {
4563
+ const workflowDir = join8(projectPath, ".github", "workflows");
4564
+ const workflowFile = join8(workflowDir, "skillkit.yml");
4565
+ if (existsSync11(workflowFile) && !this.force) {
4566
+ return {
4567
+ success: true,
4568
+ message: `GitHub Actions workflow already exists (use --force to overwrite)`,
4569
+ skipped: true
4570
+ };
4571
+ }
4572
+ try {
4573
+ mkdirSync5(workflowDir, { recursive: true });
4574
+ writeFileSync5(workflowFile, GITHUB_ACTIONS_WORKFLOW);
4575
+ return { success: true, message: `Created ${workflowFile}` };
4576
+ } catch (error) {
4577
+ return { success: false, message: `Failed to create GitHub workflow: ${error}` };
4578
+ }
4579
+ }
4580
+ createGitLabCI(projectPath) {
4581
+ const ciFile = join8(projectPath, ".gitlab-ci.yml");
4582
+ if (existsSync11(ciFile) && !this.force) {
4583
+ try {
4584
+ const content = readFileSync6(ciFile, "utf-8");
4585
+ if (content.includes("skillkit")) {
4586
+ return {
4587
+ success: true,
4588
+ message: `.gitlab-ci.yml already contains SkillKit config (use --force to overwrite)`,
4589
+ skipped: true
4590
+ };
4591
+ }
4592
+ return {
4593
+ success: true,
4594
+ message: `.gitlab-ci.yml already exists (use --force to overwrite)`,
4595
+ skipped: true
4596
+ };
4597
+ } catch (error) {
4598
+ return {
4599
+ success: false,
4600
+ message: `Failed to read .gitlab-ci.yml: ${error}`
4601
+ };
4602
+ }
4603
+ }
4604
+ try {
4605
+ writeFileSync5(ciFile, GITLAB_CI);
4606
+ return { success: true, message: `Created ${ciFile}` };
4607
+ } catch (error) {
4608
+ return { success: false, message: `Failed to create GitLab CI config: ${error}` };
4609
+ }
4610
+ }
4611
+ createCircleCI(projectPath) {
4612
+ const circleDir = join8(projectPath, ".circleci");
4613
+ const configFile = join8(circleDir, "config.yml");
4614
+ if (existsSync11(configFile) && !this.force) {
4615
+ return {
4616
+ success: true,
4617
+ message: `CircleCI config already exists (use --force to overwrite)`,
4618
+ skipped: true
4619
+ };
4620
+ }
4621
+ try {
4622
+ mkdirSync5(circleDir, { recursive: true });
4623
+ writeFileSync5(configFile, CIRCLECI_CONFIG);
4624
+ return { success: true, message: `Created ${configFile}` };
4625
+ } catch (error) {
4626
+ return { success: false, message: `Failed to create CircleCI config: ${error}` };
4627
+ }
4628
+ }
4629
+ };
4630
+
4631
+ // src/commands/team.ts
4632
+ import { Command as Command27, Option as Option26 } from "clipanion";
4633
+ import chalk26 from "chalk";
4634
+ import { createTeamManager, createSkillBundle, exportBundle, importBundle } from "@skillkit/core";
4635
+ import { join as join9 } from "path";
4636
+ var TeamCommand = class extends Command27 {
4637
+ static paths = [["team"]];
4638
+ static usage = Command27.Usage({
4639
+ description: "Manage team skill sharing and collaboration",
4640
+ examples: [
4641
+ ["Initialize team", '$0 team init --name "My Team" --registry https://github.com/myteam/skills'],
4642
+ ["Share a skill", "$0 team share --name my-skill"],
4643
+ ["Import a skill", "$0 team import --name shared-skill"],
4644
+ ["List shared skills", "$0 team list"],
4645
+ ["Sync with remote", "$0 team sync"],
4646
+ ["Create a bundle", "$0 team bundle-create --name my-bundle --skills skill1,skill2"],
4647
+ ["Export a bundle", "$0 team bundle-export --name my-bundle --output ./bundle.json"],
4648
+ ["Import a bundle", "$0 team bundle-import --source ./bundle.json"]
4649
+ ]
4650
+ });
4651
+ action = Option26.String({ required: true });
4652
+ name = Option26.String("--name", { description: "Team name (for init), skill name, or bundle name" });
4653
+ registry = Option26.String("--registry", { description: "Registry URL (for init)" });
4654
+ description = Option26.String("--description,-d", { description: "Description (for share/bundle)" });
4655
+ tags = Option26.String("--tags,-t", { description: "Comma-separated tags (for share)" });
4656
+ skills = Option26.String("--skills", { description: "Comma-separated skill names (for bundle)" });
4657
+ output = Option26.String("--output,-o", { description: "Output path (for bundle-export)" });
4658
+ source = Option26.String("--source,-s", { description: "Source path (for bundle-import)" });
4659
+ overwrite = Option26.Boolean("--overwrite", { description: "Overwrite existing (for import)" });
4660
+ dryRun = Option26.Boolean("--dry-run", { description: "Preview without changes" });
4661
+ async execute() {
4662
+ const projectPath = process.cwd();
4663
+ const teamManager = createTeamManager(projectPath);
4664
+ try {
4665
+ switch (this.action) {
4666
+ case "init":
4667
+ return await this.initTeam(teamManager);
4668
+ case "share":
4669
+ return await this.shareSkill(teamManager);
4670
+ case "import":
4671
+ return await this.importSkill(teamManager);
4672
+ case "list":
4673
+ return await this.listSkills(teamManager);
4674
+ case "sync":
4675
+ return await this.syncTeam(teamManager);
4676
+ case "remove":
4677
+ return await this.removeSkill(teamManager);
4678
+ case "bundle-create":
4679
+ return await this.createBundle(teamManager);
4680
+ case "bundle-export":
4681
+ return await this.exportSkillBundle(teamManager);
4682
+ case "bundle-import":
4683
+ return await this.importSkillBundle();
4684
+ default:
4685
+ this.context.stderr.write(chalk26.red(`Unknown action: ${this.action}
4686
+ `));
4687
+ this.context.stderr.write("Available actions: init, share, import, list, sync, remove, bundle-create, bundle-export, bundle-import\n");
4688
+ return 1;
4689
+ }
4690
+ } catch (err) {
4691
+ this.context.stderr.write(chalk26.red(`\u2717 ${err instanceof Error ? err.message : "Unknown error"}
4692
+ `));
4693
+ return 1;
4694
+ }
4695
+ }
4696
+ async initTeam(teamManager) {
4697
+ if (!this.name) {
4698
+ this.context.stderr.write(chalk26.red("--name is required for init\n"));
4699
+ return 1;
4700
+ }
4701
+ if (!this.registry) {
4702
+ this.context.stderr.write(chalk26.red("--registry is required for init\n"));
4703
+ return 1;
4704
+ }
4705
+ const config = await teamManager.init({
4706
+ teamName: this.name,
4707
+ registryUrl: this.registry
4708
+ });
4709
+ this.context.stdout.write(chalk26.green("\u2713 Team initialized!\n"));
4710
+ this.context.stdout.write(` Team ID: ${config.teamId}
4711
+ `);
4712
+ this.context.stdout.write(` Registry: ${config.registryUrl}
4713
+ `);
4714
+ return 0;
4715
+ }
4716
+ async shareSkill(teamManager) {
4717
+ const config = teamManager.load();
4718
+ if (!config) {
4719
+ this.context.stderr.write(chalk26.red("Team not initialized. Run `skillkit team init` first.\n"));
4720
+ return 1;
4721
+ }
4722
+ if (!this.name) {
4723
+ this.context.stderr.write(chalk26.red("--name <skill-name> is required for share\n"));
4724
+ return 1;
4725
+ }
4726
+ const shared = await teamManager.shareSkill({
4727
+ skillName: this.name,
4728
+ description: this.description,
4729
+ tags: this.tags?.split(",").map((t) => t.trim())
4730
+ });
4731
+ this.context.stdout.write(chalk26.green("\u2713 Skill shared!\n"));
4732
+ this.context.stdout.write(` Name: ${shared.name}
4733
+ `);
4734
+ this.context.stdout.write(` Version: ${shared.version}
4735
+ `);
4736
+ this.context.stdout.write(` Source: ${shared.source}
4737
+ `);
4738
+ return 0;
4739
+ }
4740
+ async importSkill(teamManager) {
4741
+ const config = teamManager.load();
4742
+ if (!config) {
4743
+ this.context.stderr.write(chalk26.red("Team not initialized. Run `skillkit team init` first.\n"));
4744
+ return 1;
4745
+ }
4746
+ if (!this.name) {
4747
+ this.context.stderr.write(chalk26.red("--name <skill-name> is required for import\n"));
4748
+ return 1;
4749
+ }
4750
+ const result = await teamManager.importSkill(this.name, {
4751
+ overwrite: this.overwrite,
4752
+ dryRun: this.dryRun
4753
+ });
4754
+ if (!result.success) {
4755
+ this.context.stderr.write(chalk26.red(`\u2717 ${result.error}
4756
+ `));
4757
+ return 1;
4758
+ }
4759
+ if (this.dryRun) {
4760
+ this.context.stdout.write(chalk26.cyan(`[dry-run] Would import to: ${result.path}
4761
+ `));
4762
+ } else {
4763
+ this.context.stdout.write(chalk26.green(`\u2713 Skill imported to: ${result.path}
4764
+ `));
4765
+ }
4766
+ return 0;
4767
+ }
4768
+ async listSkills(teamManager) {
4769
+ const config = teamManager.load();
4770
+ if (!config) {
4771
+ this.context.stderr.write(chalk26.red("Team not initialized. Run `skillkit team init` first.\n"));
4772
+ return 1;
4773
+ }
4774
+ const skills = teamManager.listSharedSkills();
4775
+ this.context.stdout.write(chalk26.cyan(`Team: ${config.teamName}
4776
+ `));
4777
+ this.context.stdout.write(chalk26.gray(`Registry: ${config.registryUrl}
4778
+
4779
+ `));
4780
+ if (skills.length === 0) {
4781
+ this.context.stdout.write("No shared skills yet. Use `skillkit team share` to share a skill.\n");
4782
+ return 0;
4783
+ }
4784
+ this.context.stdout.write(`Shared Skills (${skills.length}):
4785
+ `);
4786
+ for (const skill of skills) {
4787
+ this.context.stdout.write(chalk26.cyan(` ${skill.name}`) + ` v${skill.version}
4788
+ `);
4789
+ if (skill.description) {
4790
+ this.context.stdout.write(chalk26.gray(` ${skill.description}
4791
+ `));
4792
+ }
4793
+ this.context.stdout.write(` by ${skill.author} | ${skill.downloads || 0} downloads
4794
+ `);
4795
+ }
4796
+ return 0;
4797
+ }
4798
+ async syncTeam(teamManager) {
4799
+ const config = teamManager.load();
4800
+ if (!config) {
4801
+ this.context.stderr.write(chalk26.red("Team not initialized. Run `skillkit team init` first.\n"));
4802
+ return 1;
4803
+ }
4804
+ this.context.stdout.write(`Syncing with ${config.registryUrl}...
4805
+ `);
4806
+ const result = await teamManager.sync();
4807
+ this.context.stdout.write(chalk26.green("\u2713 Sync complete!\n"));
4808
+ if (result.added.length > 0) {
4809
+ this.context.stdout.write(` Added: ${result.added.join(", ")}
4810
+ `);
4811
+ }
4812
+ if (result.updated.length > 0) {
4813
+ this.context.stdout.write(` Updated: ${result.updated.join(", ")}
4814
+ `);
4815
+ }
4816
+ if (result.added.length === 0 && result.updated.length === 0) {
4817
+ this.context.stdout.write(" Already up to date.\n");
4818
+ }
4819
+ return 0;
4820
+ }
4821
+ async removeSkill(teamManager) {
4822
+ const config = teamManager.load();
4823
+ if (!config) {
4824
+ this.context.stderr.write(chalk26.red("Team not initialized. Run `skillkit team init` first.\n"));
4825
+ return 1;
4826
+ }
4827
+ if (!this.name) {
4828
+ this.context.stderr.write(chalk26.red("--name <skill-name> is required for remove\n"));
4829
+ return 1;
4830
+ }
4831
+ const removed = teamManager.removeSkill(this.name);
4832
+ if (!removed) {
4833
+ this.context.stderr.write(chalk26.red(`Skill "${this.name}" not found in team registry.
4834
+ `));
4835
+ return 1;
4836
+ }
4837
+ this.context.stdout.write(chalk26.green(`\u2713 Skill "${this.name}" removed from team registry.
4838
+ `));
4839
+ return 0;
4840
+ }
4841
+ async createBundle(teamManager) {
4842
+ const config = teamManager.load();
4843
+ if (!config) {
4844
+ this.context.stderr.write(chalk26.red("Team not initialized. Run `skillkit team init` first.\n"));
4845
+ return 1;
4846
+ }
4847
+ if (!this.name) {
4848
+ this.context.stderr.write(chalk26.red("--name <bundle-name> is required for bundle-create\n"));
4849
+ return 1;
4850
+ }
4851
+ if (!this.skills) {
4852
+ this.context.stderr.write(chalk26.red("--skills <skill1,skill2,...> is required for bundle-create\n"));
4853
+ return 1;
4854
+ }
4855
+ const skillNames = this.skills.split(",").map((s) => s.trim());
4856
+ const projectPath = process.cwd();
4857
+ const skillsDir = join9(projectPath, "skills");
4858
+ const bundle = createSkillBundle(this.name, config.teamName, this.description);
4859
+ let addedCount = 0;
4860
+ for (const skillName of skillNames) {
4861
+ const skillPath = join9(skillsDir, skillName);
4862
+ try {
4863
+ bundle.addSkill(skillPath);
4864
+ addedCount++;
4865
+ this.context.stdout.write(chalk26.gray(` + ${skillName}
4866
+ `));
4867
+ } catch (err) {
4868
+ this.context.stderr.write(chalk26.yellow(` \u26A0 Skipping ${skillName}: ${err instanceof Error ? err.message : "Unknown error"}
4869
+ `));
4870
+ }
4871
+ }
4872
+ if (addedCount === 0) {
4873
+ this.context.stderr.write(chalk26.red("No skills were added to the bundle.\n"));
4874
+ return 1;
4875
+ }
4876
+ const outputPath = this.output || join9(projectPath, ".skillkit", "bundles", `${this.name}.json`);
4877
+ const result = exportBundle(bundle, outputPath);
4878
+ if (!result.success) {
4879
+ this.context.stderr.write(chalk26.red(`\u2717 ${result.error}
4880
+ `));
4881
+ return 1;
4882
+ }
4883
+ this.context.stdout.write(chalk26.green(`
4884
+ \u2713 Bundle "${this.name}" created with ${addedCount} skills!
4885
+ `));
4886
+ this.context.stdout.write(` Checksum: ${bundle.getChecksum()}
4887
+ `);
4888
+ this.context.stdout.write(` Output: ${result.path}
4889
+ `);
4890
+ return 0;
4891
+ }
4892
+ async exportSkillBundle(teamManager) {
4893
+ const config = teamManager.load();
4894
+ if (!config) {
4895
+ this.context.stderr.write(chalk26.red("Team not initialized. Run `skillkit team init` first.\n"));
4896
+ return 1;
4897
+ }
4898
+ if (!this.name) {
4899
+ this.context.stderr.write(chalk26.red("--name <bundle-name> is required for bundle-export\n"));
4900
+ return 1;
4901
+ }
4902
+ if (!this.output) {
4903
+ this.context.stderr.write(chalk26.red("--output <path> is required for bundle-export\n"));
4904
+ return 1;
4905
+ }
4906
+ const projectPath = process.cwd();
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)) {
4910
+ this.context.stderr.write(chalk26.red(`Bundle "${this.name}" not found. Create it first with bundle-create.
4911
+ `));
4912
+ return 1;
4913
+ }
4914
+ const content = readFileSync7(bundlePath, "utf-8");
4915
+ writeFileSync6(this.output, content, "utf-8");
4916
+ this.context.stdout.write(chalk26.green(`\u2713 Bundle exported to: ${this.output}
4917
+ `));
4918
+ return 0;
4919
+ }
4920
+ async importSkillBundle() {
4921
+ if (!this.source) {
4922
+ this.context.stderr.write(chalk26.red("--source <path> is required for bundle-import\n"));
4923
+ return 1;
4924
+ }
4925
+ const { existsSync: existsSync13 } = await import("fs");
4926
+ if (!existsSync13(this.source)) {
4927
+ this.context.stderr.write(chalk26.red(`Bundle file not found: ${this.source}
4928
+ `));
4929
+ return 1;
4930
+ }
4931
+ const projectPath = process.cwd();
4932
+ const skillsDir = join9(projectPath, "skills");
4933
+ if (this.dryRun) {
4934
+ this.context.stdout.write(chalk26.cyan("[dry-run] Would import bundle to: " + skillsDir + "\n"));
4935
+ return 0;
4936
+ }
4937
+ const result = importBundle(this.source, skillsDir, { overwrite: this.overwrite });
4938
+ if (!result.success && result.imported.length === 0) {
4939
+ this.context.stderr.write(chalk26.red("\u2717 Failed to import bundle:\n"));
4940
+ for (const error of result.errors) {
4941
+ this.context.stderr.write(chalk26.red(` - ${error}
4942
+ `));
4943
+ }
4944
+ return 1;
4945
+ }
4946
+ this.context.stdout.write(chalk26.green(`\u2713 Imported ${result.imported.length} skills from bundle!
4947
+ `));
4948
+ for (const name of result.imported) {
4949
+ this.context.stdout.write(chalk26.gray(` + ${name}
4950
+ `));
4951
+ }
4952
+ if (result.errors.length > 0) {
4953
+ this.context.stdout.write(chalk26.yellow("\nWarnings:\n"));
4954
+ for (const error of result.errors) {
4955
+ this.context.stdout.write(chalk26.yellow(` - ${error}
4956
+ `));
4957
+ }
4958
+ }
4959
+ return 0;
4960
+ }
4961
+ };
4962
+
4963
+ // src/commands/plugin.ts
4964
+ import { Command as Command28, Option as Option27 } from "clipanion";
4965
+ import { join as join10, isAbsolute, resolve as resolve11, sep } from "path";
4966
+ import { homedir } from "os";
4967
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, copyFileSync, cpSync as cpSync3, rmSync as rmSync4 } from "fs";
4968
+ import chalk27 from "chalk";
4969
+ import { createPluginManager, loadPlugin, loadPluginsFromDirectory } from "@skillkit/core";
4970
+ var PluginCommand = class extends Command28 {
4971
+ static paths = [["plugin"]];
4972
+ static usage = Command28.Usage({
4973
+ description: "Manage SkillKit plugins",
4974
+ examples: [
4975
+ ["List installed plugins", "$0 plugin list"],
4976
+ ["Install a plugin", "$0 plugin install --source ./my-plugin"],
4977
+ ["Install from npm", "$0 plugin install --source skillkit-plugin-gitlab"],
4978
+ ["Uninstall a plugin", "$0 plugin uninstall --name my-plugin"],
4979
+ ["Enable a plugin", "$0 plugin enable --name my-plugin"],
4980
+ ["Disable a plugin", "$0 plugin disable --name my-plugin"]
4981
+ ]
4982
+ });
4983
+ action = Option27.String({ required: true });
4984
+ source = Option27.String("--source,-s", { description: "Plugin source (file path or npm package)" });
4985
+ name = Option27.String("--name,-n", { description: "Plugin name" });
4986
+ global = Option27.Boolean("--global,-g", { description: "Use global plugin directory" });
4987
+ async execute() {
4988
+ const projectPath = this.global ? join10(homedir(), ".skillkit") : process.cwd();
4989
+ const pluginManager = createPluginManager(projectPath);
4990
+ const pluginsDir = this.global ? join10(projectPath, "plugins") : join10(projectPath, ".skillkit", "plugins");
4991
+ try {
4992
+ const plugins = await loadPluginsFromDirectory(pluginsDir);
4993
+ for (const plugin of plugins) {
4994
+ if (pluginManager.isPluginEnabled(plugin.metadata.name)) {
4995
+ await pluginManager.register(plugin);
4996
+ }
4997
+ }
4998
+ } catch {
4999
+ }
5000
+ try {
5001
+ switch (this.action) {
5002
+ case "list":
5003
+ return this.listPlugins(pluginManager);
5004
+ case "install":
5005
+ return await this.installPlugin(pluginManager);
5006
+ case "uninstall":
5007
+ return await this.uninstallPlugin(pluginManager);
5008
+ case "enable":
5009
+ return this.enablePlugin(pluginManager);
5010
+ case "disable":
5011
+ return this.disablePlugin(pluginManager);
5012
+ case "info":
5013
+ return this.pluginInfo(pluginManager);
5014
+ default:
5015
+ this.context.stderr.write(chalk27.red(`Unknown action: ${this.action}
5016
+ `));
5017
+ this.context.stderr.write("Available actions: list, install, uninstall, enable, disable, info\n");
5018
+ return 1;
5019
+ }
5020
+ } catch (err) {
5021
+ this.context.stderr.write(chalk27.red(`\u2717 ${err instanceof Error ? err.message : "Unknown error"}
5022
+ `));
5023
+ return 1;
5024
+ }
5025
+ }
5026
+ listPlugins(pluginManager) {
5027
+ const plugins = pluginManager.listPlugins();
5028
+ if (plugins.length === 0) {
5029
+ this.context.stdout.write("No plugins installed.\n");
5030
+ this.context.stdout.write("Use `skillkit plugin install --source <source>` to install a plugin.\n");
5031
+ return 0;
5032
+ }
5033
+ this.context.stdout.write(chalk27.cyan(`Installed Plugins (${plugins.length}):
5034
+
5035
+ `));
5036
+ for (const plugin of plugins) {
5037
+ const enabled = pluginManager.isPluginEnabled(plugin.name);
5038
+ const status = enabled ? chalk27.green("enabled") : chalk27.gray("disabled");
5039
+ this.context.stdout.write(chalk27.cyan(` ${plugin.name}`) + ` v${plugin.version} [${status}]
5040
+ `);
5041
+ if (plugin.description) {
5042
+ this.context.stdout.write(chalk27.gray(` ${plugin.description}
5043
+ `));
5044
+ }
5045
+ }
5046
+ const translators = pluginManager.getAllTranslators();
5047
+ const providers = pluginManager.getAllProviders();
5048
+ const commands = pluginManager.getAllCommands();
5049
+ if (translators.size > 0 || providers.size > 0 || commands.length > 0) {
5050
+ this.context.stdout.write(chalk27.cyan("\nRegistered Extensions:\n"));
5051
+ if (translators.size > 0) {
5052
+ this.context.stdout.write(` Translators: ${Array.from(translators.keys()).join(", ")}
5053
+ `);
5054
+ }
5055
+ if (providers.size > 0) {
5056
+ this.context.stdout.write(` Providers: ${Array.from(providers.keys()).join(", ")}
5057
+ `);
5058
+ }
5059
+ if (commands.length > 0) {
5060
+ this.context.stdout.write(` Commands: ${commands.map((c) => c.name).join(", ")}
5061
+ `);
5062
+ }
5063
+ }
5064
+ return 0;
5065
+ }
5066
+ /**
5067
+ * Validate plugin name to prevent path traversal attacks
5068
+ * Allows scoped npm names like @scope/name (mirrors loader.ts validation)
5069
+ */
5070
+ isValidPluginName(name) {
5071
+ if (!name) return false;
5072
+ if (name.includes("\\") || name.includes("..") || name === "." || name.startsWith(".")) {
5073
+ return false;
5074
+ }
5075
+ return /^(?:@[a-z0-9-]+\/)?[a-z0-9-]+$/.test(name);
5076
+ }
5077
+ async installPlugin(pluginManager) {
5078
+ if (!this.source) {
5079
+ this.context.stderr.write(chalk27.red("--source is required for install\n"));
5080
+ return 1;
5081
+ }
5082
+ const resolvedSource = this.source.startsWith("~") ? join10(homedir(), this.source.slice(1)) : this.source;
5083
+ this.context.stdout.write(`Installing plugin from ${this.source}...
5084
+ `);
5085
+ const plugin = await loadPlugin(resolvedSource);
5086
+ const pluginName = plugin.metadata.name;
5087
+ if (!this.isValidPluginName(pluginName)) {
5088
+ this.context.stderr.write(chalk27.red(`Invalid plugin name: ${pluginName}
5089
+ `));
5090
+ return 1;
5091
+ }
5092
+ const projectPath = this.global ? join10(homedir(), ".skillkit") : process.cwd();
5093
+ const pluginsDir = this.global ? join10(projectPath, "plugins") : join10(projectPath, ".skillkit", "plugins");
5094
+ const isLocalPath3 = this.source.startsWith("./") || this.source.startsWith("../") || this.source.startsWith("/") || this.source.startsWith("~") || this.source.includes("\\") || isAbsolute(this.source);
5095
+ if (isLocalPath3 && existsSync12(resolvedSource)) {
5096
+ const targetDir = join10(pluginsDir, pluginName);
5097
+ const resolvedTarget = resolve11(targetDir);
5098
+ const resolvedPluginsDir = resolve11(pluginsDir);
5099
+ if (!resolvedTarget.startsWith(resolvedPluginsDir + sep)) {
5100
+ this.context.stderr.write(chalk27.red("Invalid plugin name\n"));
5101
+ return 1;
5102
+ }
5103
+ if (!existsSync12(pluginsDir)) {
5104
+ mkdirSync6(pluginsDir, { recursive: true });
5105
+ }
5106
+ const { statSync: statSync2 } = await import("fs");
5107
+ const sourceStat = statSync2(resolvedSource);
5108
+ if (sourceStat.isDirectory()) {
5109
+ cpSync3(resolvedSource, targetDir, { recursive: true });
5110
+ } else {
5111
+ if (!existsSync12(targetDir)) {
5112
+ mkdirSync6(targetDir, { recursive: true });
5113
+ }
5114
+ let destFileName;
5115
+ if (resolvedSource.endsWith(".json")) {
5116
+ destFileName = "plugin.json";
5117
+ } else if (resolvedSource.endsWith(".mjs")) {
5118
+ destFileName = "index.mjs";
5119
+ } else {
5120
+ destFileName = "index.js";
5121
+ }
5122
+ copyFileSync(resolvedSource, join10(targetDir, destFileName));
5123
+ }
5124
+ this.context.stdout.write(chalk27.dim(` Copied to ${targetDir}
5125
+ `));
5126
+ }
5127
+ await pluginManager.register(plugin);
5128
+ this.context.stdout.write(chalk27.green(`\u2713 Plugin "${plugin.metadata.name}" installed!
5129
+ `));
5130
+ this.context.stdout.write(` Version: ${plugin.metadata.version}
5131
+ `);
5132
+ if (plugin.metadata.description) {
5133
+ this.context.stdout.write(` ${plugin.metadata.description}
5134
+ `);
5135
+ }
5136
+ if (plugin.translators?.length) {
5137
+ this.context.stdout.write(` Translators: ${plugin.translators.map((t) => t.agentType).join(", ")}
5138
+ `);
5139
+ }
5140
+ if (plugin.providers?.length) {
5141
+ this.context.stdout.write(` Providers: ${plugin.providers.map((p) => p.providerName).join(", ")}
5142
+ `);
5143
+ }
5144
+ if (plugin.commands?.length) {
5145
+ this.context.stdout.write(` Commands: ${plugin.commands.map((c) => c.name).join(", ")}
5146
+ `);
5147
+ }
5148
+ return 0;
5149
+ }
5150
+ async uninstallPlugin(pluginManager) {
5151
+ if (!this.name) {
5152
+ this.context.stderr.write(chalk27.red("--name is required for uninstall\n"));
5153
+ return 1;
5154
+ }
5155
+ if (!this.isValidPluginName(this.name)) {
5156
+ this.context.stderr.write(chalk27.red("Invalid plugin name\n"));
5157
+ return 1;
5158
+ }
5159
+ await pluginManager.unregister(this.name);
5160
+ const projectPath = this.global ? join10(homedir(), ".skillkit") : process.cwd();
5161
+ const pluginsDir = this.global ? join10(projectPath, "plugins") : join10(projectPath, ".skillkit", "plugins");
5162
+ const pluginDir = join10(pluginsDir, this.name);
5163
+ const resolvedPluginDir = resolve11(pluginDir);
5164
+ const resolvedPluginsDir = resolve11(pluginsDir);
5165
+ if (!resolvedPluginDir.startsWith(resolvedPluginsDir + sep)) {
5166
+ this.context.stderr.write(chalk27.red("Invalid plugin name\n"));
5167
+ return 1;
5168
+ }
5169
+ if (existsSync12(pluginDir)) {
5170
+ rmSync4(pluginDir, { recursive: true, force: true });
5171
+ this.context.stdout.write(chalk27.dim(` Removed ${pluginDir}
5172
+ `));
5173
+ }
5174
+ this.context.stdout.write(chalk27.green(`\u2713 Plugin "${this.name}" uninstalled.
5175
+ `));
5176
+ return 0;
5177
+ }
5178
+ enablePlugin(pluginManager) {
5179
+ if (!this.name) {
5180
+ this.context.stderr.write(chalk27.red("--name is required for enable\n"));
5181
+ return 1;
5182
+ }
5183
+ pluginManager.enablePlugin(this.name);
5184
+ this.context.stdout.write(chalk27.green(`\u2713 Plugin "${this.name}" enabled.
5185
+ `));
5186
+ return 0;
5187
+ }
5188
+ disablePlugin(pluginManager) {
5189
+ if (!this.name) {
5190
+ this.context.stderr.write(chalk27.red("--name is required for disable\n"));
5191
+ return 1;
5192
+ }
5193
+ pluginManager.disablePlugin(this.name);
5194
+ this.context.stdout.write(chalk27.green(`\u2713 Plugin "${this.name}" disabled.
5195
+ `));
5196
+ return 0;
5197
+ }
5198
+ pluginInfo(pluginManager) {
5199
+ if (!this.name) {
5200
+ this.context.stderr.write(chalk27.red("--name is required for info\n"));
5201
+ return 1;
5202
+ }
5203
+ const plugin = pluginManager.getPlugin(this.name);
5204
+ if (!plugin) {
5205
+ this.context.stderr.write(chalk27.red(`Plugin "${this.name}" not found.
5206
+ `));
5207
+ return 1;
5208
+ }
5209
+ const { metadata } = plugin;
5210
+ const enabled = pluginManager.isPluginEnabled(this.name);
5211
+ this.context.stdout.write(chalk27.cyan(`${metadata.name}`) + ` v${metadata.version}
5212
+ `);
5213
+ this.context.stdout.write(`Status: ${enabled ? "enabled" : "disabled"}
5214
+ `);
5215
+ if (metadata.description) {
5216
+ this.context.stdout.write(`Description: ${metadata.description}
5217
+ `);
5218
+ }
5219
+ if (metadata.author) {
5220
+ this.context.stdout.write(`Author: ${metadata.author}
5221
+ `);
5222
+ }
5223
+ if (metadata.homepage) {
5224
+ this.context.stdout.write(`Homepage: ${metadata.homepage}
5225
+ `);
5226
+ }
5227
+ if (metadata.keywords?.length) {
5228
+ this.context.stdout.write(`Keywords: ${metadata.keywords.join(", ")}
5229
+ `);
5230
+ }
5231
+ if (metadata.dependencies?.length) {
5232
+ this.context.stdout.write(`Dependencies: ${metadata.dependencies.join(", ")}
5233
+ `);
5234
+ }
5235
+ return 0;
5236
+ }
5237
+ };
4043
5238
  export {
5239
+ CICDCommand,
4044
5240
  ContextCommand,
4045
5241
  CreateCommand,
4046
5242
  DisableCommand,
@@ -4051,13 +5247,16 @@ export {
4051
5247
  MarketplaceCommand,
4052
5248
  MemoryCommand,
4053
5249
  PauseCommand,
5250
+ PluginCommand,
4054
5251
  ReadCommand,
4055
5252
  RecommendCommand,
4056
5253
  RemoveCommand,
4057
5254
  ResumeCommand,
4058
5255
  RunCommand,
5256
+ SettingsCommand,
4059
5257
  StatusCommand,
4060
5258
  SyncCommand,
5259
+ TeamCommand,
4061
5260
  TestCommand,
4062
5261
  TranslateCommand,
4063
5262
  UICommand,