@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.d.ts +82 -1
- package/dist/index.js +1213 -14
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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
|
-
|
|
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:
|
|
3806
|
+
const { existsSync: existsSync13, mkdirSync: mkdirSync7, writeFileSync: writeFileSync6 } = await import("fs");
|
|
3768
3807
|
const outputDir = dirname3(outputPath);
|
|
3769
|
-
if (!
|
|
3770
|
-
|
|
3808
|
+
if (!existsSync13(outputDir)) {
|
|
3809
|
+
mkdirSync7(outputDir, { recursive: true });
|
|
3771
3810
|
}
|
|
3772
|
-
|
|
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:
|
|
3788
|
-
const { resolve:
|
|
3789
|
-
const fullPath =
|
|
3790
|
-
if (!
|
|
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 =
|
|
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,
|