@skillkit/cli 1.3.1 → 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
@@ -1771,15 +1771,19 @@ ${chalk12.bold("Agents:")}`);
1771
1771
 
1772
1772
  // src/commands/recommend.ts
1773
1773
  import { Command as Command14, Option as Option13 } from "clipanion";
1774
- import { existsSync as existsSync9, readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
1775
- import { resolve as resolve2, join as join6 } from "path";
1774
+ import { resolve as resolve2 } from "path";
1776
1775
  import chalk13 from "chalk";
1776
+ import ora3 from "ora";
1777
1777
  import {
1778
1778
  ContextManager as ContextManager2,
1779
- RecommendationEngine
1779
+ RecommendationEngine,
1780
+ buildSkillIndex,
1781
+ saveIndex,
1782
+ loadIndex as loadIndexFromCache,
1783
+ isIndexStale,
1784
+ INDEX_PATH,
1785
+ KNOWN_SKILL_REPOS
1780
1786
  } from "@skillkit/core";
1781
- var INDEX_PATH = join6(process.env.HOME || "~", ".skillkit", "index.json");
1782
- var INDEX_CACHE_HOURS = 24;
1783
1787
  var RecommendCommand = class extends Command14 {
1784
1788
  static paths = [["recommend"], ["rec"]];
1785
1789
  static usage = Command14.Usage({
@@ -1803,7 +1807,8 @@ var RecommendCommand = class extends Command14 {
1803
1807
  ["Filter by category", "$0 recommend --category security"],
1804
1808
  ["Show detailed reasons", "$0 recommend --verbose"],
1805
1809
  ["Update skill index", "$0 recommend --update"],
1806
- ["Search for skills by task", '$0 recommend --search "authentication"']
1810
+ ["Search for skills by task", '$0 recommend --task "authentication"'],
1811
+ ["Search for skills (alias)", '$0 recommend --search "testing"']
1807
1812
  ]
1808
1813
  });
1809
1814
  // Limit number of results
@@ -1826,10 +1831,14 @@ var RecommendCommand = class extends Command14 {
1826
1831
  update = Option13.Boolean("--update,-u", false, {
1827
1832
  description: "Update skill index from sources"
1828
1833
  });
1829
- // Search mode
1834
+ // Search mode (--search or --task)
1830
1835
  search = Option13.String("--search,-s", {
1831
1836
  description: "Search skills by task/query"
1832
1837
  });
1838
+ // Task alias for search (GSD-style)
1839
+ task = Option13.String("--task,-t", {
1840
+ description: "Search skills by task (alias for --search)"
1841
+ });
1833
1842
  // Include installed skills
1834
1843
  includeInstalled = Option13.Boolean("--include-installed", false, {
1835
1844
  description: "Include already installed skills"
@@ -1845,7 +1854,7 @@ var RecommendCommand = class extends Command14 {
1845
1854
  async execute() {
1846
1855
  const targetPath = resolve2(this.projectPath || process.cwd());
1847
1856
  if (this.update) {
1848
- return this.updateIndex();
1857
+ return await this.updateIndex();
1849
1858
  }
1850
1859
  const profile = await this.getProjectProfile(targetPath);
1851
1860
  if (!profile) {
@@ -1862,8 +1871,9 @@ var RecommendCommand = class extends Command14 {
1862
1871
  }
1863
1872
  const engine = new RecommendationEngine();
1864
1873
  engine.loadIndex(index);
1865
- if (this.search) {
1866
- return this.handleSearch(engine, this.search);
1874
+ const searchQuery = this.search || this.task;
1875
+ if (searchQuery) {
1876
+ return this.handleSearch(engine, searchQuery);
1867
1877
  }
1868
1878
  const result = engine.recommend(profile, {
1869
1879
  limit: this.limit ? parseInt(this.limit, 10) : 10,
@@ -2008,220 +2018,3225 @@ var RecommendCommand = class extends Command14 {
2008
2018
  * Load skill index from cache
2009
2019
  */
2010
2020
  loadIndex() {
2011
- if (!existsSync9(INDEX_PATH)) {
2021
+ const index = loadIndexFromCache();
2022
+ if (!index) {
2012
2023
  return null;
2013
2024
  }
2014
- try {
2015
- const content = readFileSync4(INDEX_PATH, "utf-8");
2016
- const index = JSON.parse(content);
2025
+ if (isIndexStale(index) && !this.json) {
2017
2026
  const lastUpdated = new Date(index.lastUpdated);
2018
2027
  const hoursSinceUpdate = (Date.now() - lastUpdated.getTime()) / (1e3 * 60 * 60);
2019
- if (hoursSinceUpdate > INDEX_CACHE_HOURS && !this.json) {
2020
- console.log(
2021
- chalk13.dim(`Index is ${Math.round(hoursSinceUpdate)} hours old. Run --update to refresh.
2028
+ console.log(
2029
+ chalk13.dim(`Index is ${Math.round(hoursSinceUpdate)} hours old. Run --update to refresh.
2022
2030
  `)
2023
- );
2024
- }
2025
- return index;
2026
- } catch {
2027
- return null;
2031
+ );
2028
2032
  }
2033
+ return index;
2029
2034
  }
2030
2035
  /**
2031
2036
  * Update skill index from sources
2032
2037
  */
2033
- updateIndex() {
2034
- console.log(chalk13.cyan("Updating skill index...\n"));
2035
- const sampleIndex = {
2036
- version: 1,
2037
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2038
- skills: getSampleSkills(),
2039
- sources: [
2040
- {
2041
- name: "vercel-labs",
2042
- url: "https://github.com/vercel-labs/agent-skills",
2043
- lastFetched: (/* @__PURE__ */ new Date()).toISOString(),
2044
- skillCount: 5
2045
- },
2046
- {
2047
- name: "anthropics",
2048
- url: "https://github.com/anthropics/skills",
2049
- lastFetched: (/* @__PURE__ */ new Date()).toISOString(),
2050
- skillCount: 3
2038
+ async updateIndex() {
2039
+ console.log(chalk13.cyan("Updating skill index from GitHub repositories...\n"));
2040
+ console.log(chalk13.dim(`Sources: ${KNOWN_SKILL_REPOS.map((r) => `${r.owner}/${r.repo}`).join(", ")}
2041
+ `));
2042
+ const spinner = ora3("Fetching skills...").start();
2043
+ try {
2044
+ const { index, errors } = await buildSkillIndex(KNOWN_SKILL_REPOS, (message) => {
2045
+ spinner.text = message;
2046
+ });
2047
+ spinner.stop();
2048
+ if (errors.length > 0) {
2049
+ console.log(chalk13.yellow("\nWarnings:"));
2050
+ for (const error of errors) {
2051
+ console.log(chalk13.dim(` \u2022 ${error}`));
2051
2052
  }
2052
- ]
2053
- };
2054
- const indexDir = join6(process.env.HOME || "~", ".skillkit");
2055
- if (!existsSync9(indexDir)) {
2056
- mkdirSync5(indexDir, { recursive: true });
2057
- }
2058
- writeFileSync5(INDEX_PATH, JSON.stringify(sampleIndex, null, 2));
2059
- console.log(chalk13.green(`\u2713 Updated index with ${sampleIndex.skills.length} skills`));
2060
- console.log(chalk13.dim(` Sources: ${sampleIndex.sources.map((s) => s.name).join(", ")}`));
2061
- console.log(chalk13.dim(` Saved to: ${INDEX_PATH}
2053
+ console.log();
2054
+ }
2055
+ saveIndex(index);
2056
+ console.log(chalk13.green(`\u2713 Updated index with ${index.skills.length} skills`));
2057
+ if (index.sources.length > 0) {
2058
+ console.log(chalk13.dim(` Sources: ${index.sources.map((s) => s.name).join(", ")}`));
2059
+ }
2060
+ console.log(chalk13.dim(` Saved to: ${INDEX_PATH}
2062
2061
  `));
2063
- return 0;
2062
+ return 0;
2063
+ } catch (error) {
2064
+ spinner.fail("Failed to update index");
2065
+ console.error(chalk13.red(error instanceof Error ? error.message : String(error)));
2066
+ return 1;
2067
+ }
2064
2068
  }
2065
2069
  };
2066
2070
  function truncate2(str, maxLen) {
2067
2071
  if (str.length <= maxLen) return str;
2068
2072
  return str.slice(0, maxLen - 3) + "...";
2069
2073
  }
2070
- function getSampleSkills() {
2071
- return [
2072
- {
2073
- name: "vercel-react-best-practices",
2074
- description: "Modern React patterns including Server Components, hooks best practices, and performance optimization",
2075
- source: "vercel-labs/agent-skills",
2076
- tags: ["react", "frontend", "typescript", "nextjs", "performance"],
2077
- compatibility: {
2078
- frameworks: ["react", "nextjs"],
2079
- languages: ["typescript", "javascript"],
2080
- libraries: []
2081
- },
2082
- popularity: 1500,
2083
- quality: 95,
2084
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2085
- verified: true
2086
- },
2087
- {
2088
- name: "tailwind-v4-patterns",
2089
- description: "Tailwind CSS v4 utility patterns, responsive design, and component styling best practices",
2090
- source: "vercel-labs/agent-skills",
2091
- tags: ["tailwind", "css", "styling", "frontend", "responsive"],
2092
- compatibility: {
2093
- frameworks: [],
2094
- languages: ["typescript", "javascript"],
2095
- libraries: ["tailwindcss"]
2096
- },
2097
- popularity: 1200,
2098
- quality: 92,
2099
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2100
- verified: true
2101
- },
2102
- {
2103
- name: "nextjs-app-router",
2104
- description: "Next.js App Router patterns including layouts, server actions, and data fetching",
2105
- source: "vercel-labs/agent-skills",
2106
- tags: ["nextjs", "react", "routing", "server-actions", "frontend"],
2107
- compatibility: {
2108
- frameworks: ["nextjs"],
2109
- languages: ["typescript", "javascript"],
2110
- libraries: []
2111
- },
2112
- popularity: 1100,
2113
- quality: 94,
2114
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2115
- verified: true
2116
- },
2117
- {
2118
- name: "typescript-strict-patterns",
2119
- description: "TypeScript strict mode patterns, type safety, and advanced type utilities",
2120
- source: "anthropics/skills",
2121
- tags: ["typescript", "types", "safety", "patterns"],
2122
- compatibility: {
2123
- frameworks: [],
2124
- languages: ["typescript"],
2125
- libraries: []
2126
- },
2127
- popularity: 900,
2128
- quality: 90,
2129
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2130
- verified: true
2131
- },
2132
- {
2133
- name: "supabase-best-practices",
2134
- description: "Supabase integration patterns including auth, database queries, and real-time subscriptions",
2135
- source: "anthropics/skills",
2136
- tags: ["supabase", "database", "auth", "backend", "postgresql"],
2137
- compatibility: {
2138
- frameworks: [],
2139
- languages: ["typescript", "javascript"],
2140
- libraries: ["@supabase/supabase-js"]
2141
- },
2142
- popularity: 800,
2143
- quality: 88,
2144
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2145
- verified: true
2146
- },
2147
- {
2148
- name: "vitest-testing-patterns",
2149
- description: "Testing patterns with Vitest including mocking, assertions, and test organization",
2150
- source: "anthropics/skills",
2151
- tags: ["vitest", "testing", "typescript", "mocking", "tdd"],
2152
- compatibility: {
2153
- frameworks: [],
2154
- languages: ["typescript", "javascript"],
2155
- libraries: ["vitest"]
2156
- },
2157
- popularity: 700,
2158
- quality: 86,
2159
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2160
- verified: false
2161
- },
2162
- {
2163
- name: "prisma-database-patterns",
2164
- description: "Prisma ORM patterns for schema design, migrations, and efficient queries",
2165
- source: "vercel-labs/agent-skills",
2166
- tags: ["prisma", "database", "orm", "postgresql", "backend"],
2167
- compatibility: {
2168
- frameworks: [],
2169
- languages: ["typescript"],
2170
- libraries: ["@prisma/client"]
2171
- },
2172
- popularity: 850,
2173
- quality: 89,
2174
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2175
- verified: true
2176
- },
2177
- {
2178
- name: "security-best-practices",
2179
- description: "Security patterns for web applications including XSS prevention, CSRF, and secure headers",
2180
- source: "trailofbits/skills",
2181
- tags: ["security", "xss", "csrf", "headers", "owasp"],
2182
- compatibility: {
2183
- frameworks: [],
2184
- languages: ["typescript", "javascript", "python"],
2185
- libraries: []
2186
- },
2187
- popularity: 600,
2188
- quality: 95,
2189
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2190
- verified: true
2191
- },
2192
- {
2193
- name: "python-fastapi-patterns",
2194
- description: "FastAPI best practices for building high-performance Python APIs",
2195
- source: "python-skills/fastapi",
2196
- tags: ["python", "fastapi", "backend", "api", "async"],
2197
- compatibility: {
2198
- frameworks: ["fastapi"],
2199
- languages: ["python"],
2200
- libraries: []
2201
- },
2202
- popularity: 550,
2203
- quality: 85,
2204
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2205
- verified: false
2206
- },
2207
- {
2208
- name: "zustand-state-management",
2209
- description: "Zustand state management patterns for React applications",
2210
- source: "react-skills/state",
2211
- tags: ["zustand", "react", "state-management", "frontend"],
2212
- compatibility: {
2213
- frameworks: ["react"],
2214
- languages: ["typescript", "javascript"],
2215
- libraries: ["zustand"]
2216
- },
2217
- popularity: 650,
2218
- quality: 84,
2219
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2220
- verified: false
2074
+
2075
+ // src/commands/status.ts
2076
+ import { Command as Command15, Option as Option14 } from "clipanion";
2077
+ import { resolve as resolve3 } from "path";
2078
+ import chalk14 from "chalk";
2079
+ import { SessionManager } from "@skillkit/core";
2080
+ var StatusCommand = class extends Command15 {
2081
+ static paths = [["status"], ["st"]];
2082
+ static usage = Command15.Usage({
2083
+ description: "Show current session state and execution progress",
2084
+ details: `
2085
+ The status command shows the current state of skill execution sessions,
2086
+ including any paused executions and recent history.
2087
+ `,
2088
+ examples: [
2089
+ ["Show session status", "$0 status"],
2090
+ ["Show with history", "$0 status --history"],
2091
+ ["Show JSON output", "$0 status --json"]
2092
+ ]
2093
+ });
2094
+ // Show history
2095
+ history = Option14.Boolean("--history,-h", false, {
2096
+ description: "Show execution history"
2097
+ });
2098
+ // History limit
2099
+ limit = Option14.String("--limit,-l", {
2100
+ description: "Limit history entries (default: 10)"
2101
+ });
2102
+ // JSON output
2103
+ json = Option14.Boolean("--json,-j", false, {
2104
+ description: "Output in JSON format"
2105
+ });
2106
+ // Project path
2107
+ projectPath = Option14.String("--path,-p", {
2108
+ description: "Project path (default: current directory)"
2109
+ });
2110
+ async execute() {
2111
+ const targetPath = resolve3(this.projectPath || process.cwd());
2112
+ const manager = new SessionManager(targetPath);
2113
+ const state = manager.get();
2114
+ if (this.json) {
2115
+ console.log(JSON.stringify(state || { message: "No session found" }, null, 2));
2116
+ return 0;
2221
2117
  }
2222
- ];
2223
- }
2118
+ if (!state) {
2119
+ console.log(chalk14.dim("No active session found."));
2120
+ console.log(chalk14.dim('Run a skill with "skillkit run <skill>" to start a session.'));
2121
+ return 0;
2122
+ }
2123
+ if (state.currentExecution) {
2124
+ const exec = state.currentExecution;
2125
+ const statusColor = exec.status === "paused" ? chalk14.yellow : chalk14.green;
2126
+ console.log(chalk14.cyan("Current Execution:\n"));
2127
+ console.log(` Skill: ${chalk14.bold(exec.skillName)}`);
2128
+ console.log(` Source: ${chalk14.dim(exec.skillSource)}`);
2129
+ console.log(` Status: ${statusColor(exec.status)}`);
2130
+ console.log(` Progress: ${exec.currentStep}/${exec.totalSteps} tasks`);
2131
+ console.log(` Started: ${chalk14.dim(new Date(exec.startedAt).toLocaleString())}`);
2132
+ if (exec.pausedAt) {
2133
+ console.log(` Paused: ${chalk14.dim(new Date(exec.pausedAt).toLocaleString())}`);
2134
+ }
2135
+ console.log(chalk14.cyan("\n Tasks:"));
2136
+ for (const task of exec.tasks) {
2137
+ const statusIcon = this.getStatusIcon(task.status);
2138
+ const statusText = this.getStatusColor(task.status)(task.status);
2139
+ console.log(` ${statusIcon} ${task.name} - ${statusText}`);
2140
+ if (task.error) {
2141
+ console.log(` ${chalk14.red("Error:")} ${task.error}`);
2142
+ }
2143
+ }
2144
+ if (exec.status === "paused") {
2145
+ console.log(chalk14.yellow("\n Resume with: skillkit resume"));
2146
+ }
2147
+ } else {
2148
+ console.log(chalk14.dim("No active execution."));
2149
+ }
2150
+ if (this.history || !state.currentExecution && state.history.length > 0) {
2151
+ const limit = this.limit ? parseInt(this.limit, 10) : 10;
2152
+ const history = manager.getHistory(limit);
2153
+ if (history.length > 0) {
2154
+ console.log(chalk14.cyan("\nExecution History:\n"));
2155
+ for (const entry of history) {
2156
+ const statusColor = entry.status === "completed" ? chalk14.green : chalk14.red;
2157
+ const duration = this.formatDuration(entry.durationMs);
2158
+ console.log(` ${statusColor("\u25CF")} ${chalk14.bold(entry.skillName)}`);
2159
+ console.log(` ${chalk14.dim(entry.skillSource)} \u2022 ${duration}`);
2160
+ console.log(` ${chalk14.dim(new Date(entry.completedAt).toLocaleString())}`);
2161
+ if (entry.commits.length > 0) {
2162
+ console.log(` Commits: ${chalk14.dim(entry.commits.join(", "))}`);
2163
+ }
2164
+ if (entry.error) {
2165
+ console.log(` ${chalk14.red("Error:")} ${entry.error}`);
2166
+ }
2167
+ console.log();
2168
+ }
2169
+ }
2170
+ }
2171
+ if (state.decisions.length > 0) {
2172
+ console.log(chalk14.cyan("Saved Decisions:\n"));
2173
+ for (const decision of state.decisions.slice(0, 5)) {
2174
+ console.log(` ${chalk14.dim(decision.key)}: ${decision.value}`);
2175
+ }
2176
+ if (state.decisions.length > 5) {
2177
+ console.log(chalk14.dim(` ... and ${state.decisions.length - 5} more`));
2178
+ }
2179
+ }
2180
+ return 0;
2181
+ }
2182
+ getStatusIcon(status) {
2183
+ switch (status) {
2184
+ case "completed":
2185
+ return chalk14.green("\u2713");
2186
+ case "failed":
2187
+ return chalk14.red("\u2717");
2188
+ case "in_progress":
2189
+ return chalk14.blue("\u25CF");
2190
+ case "paused":
2191
+ return chalk14.yellow("\u23F8");
2192
+ default:
2193
+ return chalk14.dim("\u25CB");
2194
+ }
2195
+ }
2196
+ getStatusColor(status) {
2197
+ switch (status) {
2198
+ case "completed":
2199
+ return chalk14.green;
2200
+ case "failed":
2201
+ return chalk14.red;
2202
+ case "in_progress":
2203
+ return chalk14.blue;
2204
+ case "paused":
2205
+ return chalk14.yellow;
2206
+ default:
2207
+ return chalk14.dim;
2208
+ }
2209
+ }
2210
+ formatDuration(ms) {
2211
+ if (ms < 1e3) return `${ms}ms`;
2212
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2213
+ const minutes = Math.floor(ms / 6e4);
2214
+ const seconds = Math.floor(ms % 6e4 / 1e3);
2215
+ return `${minutes}m ${seconds}s`;
2216
+ }
2217
+ };
2218
+
2219
+ // src/commands/pause.ts
2220
+ import { Command as Command16, Option as Option15 } from "clipanion";
2221
+ import { resolve as resolve4 } from "path";
2222
+ import chalk15 from "chalk";
2223
+ import { SessionManager as SessionManager2 } from "@skillkit/core";
2224
+ var PauseCommand = class extends Command16 {
2225
+ static paths = [["pause"]];
2226
+ static usage = Command16.Usage({
2227
+ description: "Pause current skill execution for later resumption",
2228
+ details: `
2229
+ The pause command saves the current execution state so you can
2230
+ continue later with "skillkit resume".
2231
+
2232
+ This is useful when you need to:
2233
+ - Take a break from a long skill execution
2234
+ - Handle an interruption
2235
+ - Review progress before continuing
2236
+ `,
2237
+ examples: [
2238
+ ["Pause current execution", "$0 pause"]
2239
+ ]
2240
+ });
2241
+ // Project path
2242
+ projectPath = Option15.String("--path,-p", {
2243
+ description: "Project path (default: current directory)"
2244
+ });
2245
+ async execute() {
2246
+ const targetPath = resolve4(this.projectPath || process.cwd());
2247
+ const manager = new SessionManager2(targetPath);
2248
+ const state = manager.get();
2249
+ if (!state) {
2250
+ console.log(chalk15.yellow("No active session found."));
2251
+ return 1;
2252
+ }
2253
+ if (!state.currentExecution) {
2254
+ console.log(chalk15.yellow("No skill execution in progress."));
2255
+ return 1;
2256
+ }
2257
+ if (state.currentExecution.status === "paused") {
2258
+ console.log(chalk15.yellow("Execution is already paused."));
2259
+ console.log(chalk15.dim("Resume with: skillkit resume"));
2260
+ return 0;
2261
+ }
2262
+ const success = manager.pause();
2263
+ if (success) {
2264
+ const exec = state.currentExecution;
2265
+ console.log(chalk15.green("\u2713 Execution paused"));
2266
+ console.log();
2267
+ console.log(` Skill: ${chalk15.bold(exec.skillName)}`);
2268
+ console.log(` Progress: ${exec.currentStep}/${exec.totalSteps} tasks completed`);
2269
+ console.log();
2270
+ console.log(chalk15.dim("Resume with: skillkit resume"));
2271
+ console.log(chalk15.dim("View status: skillkit status"));
2272
+ return 0;
2273
+ } else {
2274
+ console.log(chalk15.red("Failed to pause execution."));
2275
+ return 1;
2276
+ }
2277
+ }
2278
+ };
2279
+
2280
+ // src/commands/resume.ts
2281
+ import { Command as Command17, Option as Option16 } from "clipanion";
2282
+ import { resolve as resolve5 } from "path";
2283
+ import chalk16 from "chalk";
2284
+ import { SessionManager as SessionManager3 } from "@skillkit/core";
2285
+ var ResumeCommand = class extends Command17 {
2286
+ static paths = [["resume"]];
2287
+ static usage = Command17.Usage({
2288
+ description: "Resume a paused skill execution",
2289
+ details: `
2290
+ The resume command continues a previously paused skill execution
2291
+ from where it left off.
2292
+
2293
+ The execution state is preserved, including:
2294
+ - Completed tasks
2295
+ - User decisions
2296
+ - Modified files
2297
+ `,
2298
+ examples: [
2299
+ ["Resume paused execution", "$0 resume"]
2300
+ ]
2301
+ });
2302
+ // Project path
2303
+ projectPath = Option16.String("--path,-p", {
2304
+ description: "Project path (default: current directory)"
2305
+ });
2306
+ async execute() {
2307
+ const targetPath = resolve5(this.projectPath || process.cwd());
2308
+ const manager = new SessionManager3(targetPath);
2309
+ const state = manager.get();
2310
+ if (!state) {
2311
+ console.log(chalk16.yellow("No active session found."));
2312
+ return 1;
2313
+ }
2314
+ if (!state.currentExecution) {
2315
+ console.log(chalk16.yellow("No skill execution to resume."));
2316
+ console.log(chalk16.dim("Start a new execution with: skillkit run <skill>"));
2317
+ return 1;
2318
+ }
2319
+ if (state.currentExecution.status !== "paused") {
2320
+ if (state.currentExecution.status === "running") {
2321
+ console.log(chalk16.yellow("Execution is already running."));
2322
+ } else {
2323
+ console.log(chalk16.yellow(`Execution is ${state.currentExecution.status}.`));
2324
+ }
2325
+ return 1;
2326
+ }
2327
+ const success = manager.resume();
2328
+ if (success) {
2329
+ const exec = state.currentExecution;
2330
+ console.log(chalk16.green("\u2713 Execution resumed"));
2331
+ console.log();
2332
+ console.log(` Skill: ${chalk16.bold(exec.skillName)}`);
2333
+ console.log(` Progress: ${exec.currentStep}/${exec.totalSteps} tasks`);
2334
+ console.log();
2335
+ const nextTask = exec.tasks.find((t) => t.status === "pending" || t.status === "in_progress");
2336
+ if (nextTask) {
2337
+ console.log(` Next task: ${chalk16.cyan(nextTask.name)}`);
2338
+ }
2339
+ console.log();
2340
+ console.log(chalk16.dim("The execution will continue from where it left off."));
2341
+ console.log(chalk16.dim("View status: skillkit status"));
2342
+ return 0;
2343
+ } else {
2344
+ console.log(chalk16.red("Failed to resume execution."));
2345
+ return 1;
2346
+ }
2347
+ }
2348
+ };
2349
+
2350
+ // src/commands/workflow/run.ts
2351
+ import { Command as Command18, Option as Option17 } from "clipanion";
2352
+ import { resolve as resolve6 } from "path";
2353
+ import chalk17 from "chalk";
2354
+ import ora4 from "ora";
2355
+ import {
2356
+ loadWorkflowByName,
2357
+ loadWorkflow,
2358
+ validateWorkflow,
2359
+ createWorkflowOrchestrator,
2360
+ createSkillExecutor,
2361
+ createSimulatedSkillExecutor
2362
+ } from "@skillkit/core";
2363
+ var WorkflowRunCommand = class extends Command18 {
2364
+ static paths = [["workflow", "run"], ["wf", "run"]];
2365
+ static usage = Command18.Usage({
2366
+ description: "Execute a skill workflow",
2367
+ details: `
2368
+ The workflow run command executes a workflow definition,
2369
+ running skills in waves with parallel or sequential execution.
2370
+
2371
+ Workflows are defined in YAML files in .skillkit/workflows/
2372
+ `,
2373
+ examples: [
2374
+ ["Run a workflow by name", "$0 workflow run setup-project"],
2375
+ ["Run a workflow from file", "$0 workflow run --file my-workflow.yaml"],
2376
+ ["Dry run (no execution)", "$0 workflow run setup-project --dry-run"]
2377
+ ]
2378
+ });
2379
+ // Workflow name
2380
+ workflowName = Option17.String({ required: false });
2381
+ // Workflow file path
2382
+ file = Option17.String("--file,-f", {
2383
+ description: "Path to workflow YAML file"
2384
+ });
2385
+ // Dry run
2386
+ dryRun = Option17.Boolean("--dry-run,-n", false, {
2387
+ description: "Show what would be executed without running"
2388
+ });
2389
+ // Verbose output
2390
+ verbose = Option17.Boolean("--verbose,-v", false, {
2391
+ description: "Show detailed execution progress"
2392
+ });
2393
+ // Continue on error
2394
+ continueOnError = Option17.Boolean("--continue-on-error", false, {
2395
+ description: "Continue execution even if a skill fails"
2396
+ });
2397
+ // JSON output
2398
+ json = Option17.Boolean("--json,-j", false, {
2399
+ description: "Output in JSON format"
2400
+ });
2401
+ // Project path
2402
+ projectPath = Option17.String("--path,-p", {
2403
+ description: "Project path (default: current directory)"
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
+ });
2413
+ async execute() {
2414
+ const targetPath = resolve6(this.projectPath || process.cwd());
2415
+ let workflow;
2416
+ try {
2417
+ if (this.file) {
2418
+ workflow = loadWorkflow(resolve6(this.file));
2419
+ } else if (this.workflowName) {
2420
+ workflow = loadWorkflowByName(targetPath, this.workflowName);
2421
+ if (!workflow) {
2422
+ console.error(chalk17.red(`Workflow "${this.workflowName}" not found`));
2423
+ console.log(chalk17.dim("List available workflows: skillkit workflow list"));
2424
+ return 1;
2425
+ }
2426
+ } else {
2427
+ console.error(chalk17.red("Please specify a workflow name or --file"));
2428
+ return 1;
2429
+ }
2430
+ } catch (error) {
2431
+ console.error(chalk17.red(`Failed to load workflow: ${error}`));
2432
+ return 1;
2433
+ }
2434
+ const validation = validateWorkflow(workflow);
2435
+ if (!validation.valid) {
2436
+ console.error(chalk17.red("Invalid workflow:"));
2437
+ for (const error of validation.errors) {
2438
+ console.error(chalk17.red(` \u2022 ${error}`));
2439
+ }
2440
+ return 1;
2441
+ }
2442
+ if (this.dryRun) {
2443
+ this.showDryRun(workflow);
2444
+ return 0;
2445
+ }
2446
+ console.log(chalk17.cyan(`Executing workflow: ${chalk17.bold(workflow.name)}`));
2447
+ if (workflow.description) {
2448
+ console.log(chalk17.dim(workflow.description));
2449
+ }
2450
+ console.log();
2451
+ const spinner = ora4();
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
+ });
2485
+ const orchestrator = createWorkflowOrchestrator(
2486
+ skillExecutor,
2487
+ (event) => {
2488
+ if (this.json) return;
2489
+ switch (event.type) {
2490
+ case "wave_start":
2491
+ currentWave = event.waveIndex || 0;
2492
+ spinner.start(`Wave ${currentWave + 1}: ${event.waveName || "Executing..."}`);
2493
+ break;
2494
+ case "skill_start":
2495
+ if (this.verbose) {
2496
+ spinner.text = `Wave ${currentWave + 1}: Running ${event.skillName}...`;
2497
+ }
2498
+ break;
2499
+ case "skill_complete":
2500
+ if (this.verbose) {
2501
+ const icon = event.status === "completed" ? chalk17.green("\u2713") : chalk17.red("\u2717");
2502
+ console.log(` ${icon} ${event.skillName}`);
2503
+ }
2504
+ break;
2505
+ case "wave_complete":
2506
+ const waveIcon = event.status === "completed" ? chalk17.green("\u2713") : chalk17.red("\u2717");
2507
+ spinner.stopAndPersist({
2508
+ symbol: waveIcon,
2509
+ text: `Wave ${(event.waveIndex || 0) + 1}: ${event.waveName || "Complete"}`
2510
+ });
2511
+ break;
2512
+ case "workflow_complete":
2513
+ console.log();
2514
+ if (event.status === "completed") {
2515
+ console.log(chalk17.green("\u2713 Workflow completed successfully"));
2516
+ } else {
2517
+ console.log(chalk17.red(`\u2717 Workflow ${event.status}`));
2518
+ if (event.error) {
2519
+ console.log(chalk17.red(` Error: ${event.error}`));
2520
+ }
2521
+ }
2522
+ break;
2523
+ }
2524
+ }
2525
+ );
2526
+ const execution = await orchestrator.execute(workflow);
2527
+ if (this.json) {
2528
+ console.log(JSON.stringify(execution, null, 2));
2529
+ }
2530
+ return execution.status === "completed" ? 0 : 1;
2531
+ }
2532
+ showDryRun(workflow) {
2533
+ console.log(chalk17.cyan("Dry Run - Workflow Execution Plan"));
2534
+ console.log();
2535
+ console.log(`Workflow: ${chalk17.bold(workflow.name)}`);
2536
+ if (workflow.description) {
2537
+ console.log(`Description: ${chalk17.dim(workflow.description)}`);
2538
+ }
2539
+ console.log();
2540
+ for (let i = 0; i < workflow.waves.length; i++) {
2541
+ const wave = workflow.waves[i];
2542
+ const modeLabel = wave.parallel ? chalk17.blue("[parallel]") : chalk17.yellow("[sequential]");
2543
+ console.log(`${chalk17.cyan(`Wave ${i + 1}`)}: ${wave.name || "Unnamed"} ${modeLabel}`);
2544
+ for (const skill of wave.skills) {
2545
+ const skillName = typeof skill === "string" ? skill : skill.skill;
2546
+ console.log(` \u2022 ${skillName}`);
2547
+ }
2548
+ console.log();
2549
+ }
2550
+ console.log(chalk17.dim("This is a dry run. No skills were executed."));
2551
+ console.log(chalk17.dim("Remove --dry-run to execute the workflow."));
2552
+ }
2553
+ };
2554
+
2555
+ // src/commands/workflow/list.ts
2556
+ import { Command as Command19, Option as Option18 } from "clipanion";
2557
+ import { resolve as resolve7 } from "path";
2558
+ import chalk18 from "chalk";
2559
+ import { listWorkflows } from "@skillkit/core";
2560
+ var WorkflowListCommand = class extends Command19 {
2561
+ static paths = [["workflow", "list"], ["wf", "list"], ["workflow", "ls"], ["wf", "ls"]];
2562
+ static usage = Command19.Usage({
2563
+ description: "List available workflows",
2564
+ details: `
2565
+ The workflow list command shows all available workflows
2566
+ defined in .skillkit/workflows/
2567
+ `,
2568
+ examples: [
2569
+ ["List all workflows", "$0 workflow list"],
2570
+ ["List with details", "$0 workflow list --verbose"]
2571
+ ]
2572
+ });
2573
+ // Verbose output
2574
+ verbose = Option18.Boolean("--verbose,-v", false, {
2575
+ description: "Show detailed workflow information"
2576
+ });
2577
+ // JSON output
2578
+ json = Option18.Boolean("--json,-j", false, {
2579
+ description: "Output in JSON format"
2580
+ });
2581
+ // Project path
2582
+ projectPath = Option18.String("--path,-p", {
2583
+ description: "Project path (default: current directory)"
2584
+ });
2585
+ async execute() {
2586
+ const targetPath = resolve7(this.projectPath || process.cwd());
2587
+ let workflows;
2588
+ try {
2589
+ workflows = listWorkflows(targetPath);
2590
+ } catch (err) {
2591
+ console.log(chalk18.red("Failed to list workflows."));
2592
+ console.log(chalk18.dim(String(err)));
2593
+ return 1;
2594
+ }
2595
+ if (this.json) {
2596
+ console.log(JSON.stringify(workflows, null, 2));
2597
+ return 0;
2598
+ }
2599
+ if (workflows.length === 0) {
2600
+ console.log(chalk18.yellow("No workflows found."));
2601
+ console.log(chalk18.dim("Create a workflow with: skillkit workflow create"));
2602
+ console.log(chalk18.dim("Or add YAML files to .skillkit/workflows/"));
2603
+ return 0;
2604
+ }
2605
+ console.log(chalk18.cyan(`Available Workflows (${workflows.length}):
2606
+ `));
2607
+ for (const workflow of workflows) {
2608
+ console.log(` ${chalk18.bold(workflow.name)}`);
2609
+ if (workflow.description) {
2610
+ console.log(` ${chalk18.dim(workflow.description)}`);
2611
+ }
2612
+ if (this.verbose) {
2613
+ console.log(` Version: ${chalk18.dim(workflow.version || "N/A")}`);
2614
+ console.log(` Waves: ${chalk18.dim(workflow.waves.length.toString())}`);
2615
+ const totalSkills = workflow.waves.reduce(
2616
+ (sum, wave) => sum + wave.skills.length,
2617
+ 0
2618
+ );
2619
+ console.log(` Total Skills: ${chalk18.dim(totalSkills.toString())}`);
2620
+ if (workflow.tags && workflow.tags.length > 0) {
2621
+ console.log(` Tags: ${chalk18.dim(workflow.tags.join(", "))}`);
2622
+ }
2623
+ }
2624
+ console.log();
2625
+ }
2626
+ console.log(chalk18.dim("Run a workflow with: skillkit workflow run <name>"));
2627
+ return 0;
2628
+ }
2629
+ };
2630
+
2631
+ // src/commands/workflow/create.ts
2632
+ import { Command as Command20, Option as Option19 } from "clipanion";
2633
+ import { resolve as resolve8 } from "path";
2634
+ import chalk19 from "chalk";
2635
+ import { createWorkflowTemplate, saveWorkflow, serializeWorkflow } from "@skillkit/core";
2636
+ var WorkflowCreateCommand = class extends Command20 {
2637
+ static paths = [["workflow", "create"], ["wf", "create"], ["workflow", "new"], ["wf", "new"]];
2638
+ static usage = Command20.Usage({
2639
+ description: "Create a new workflow",
2640
+ details: `
2641
+ The workflow create command creates a new workflow template
2642
+ that you can customize with your skill composition.
2643
+ `,
2644
+ examples: [
2645
+ ["Create a new workflow", "$0 workflow create my-workflow"],
2646
+ ["Create with description", '$0 workflow create setup-project --description "Project setup workflow"'],
2647
+ ["Print to stdout", "$0 workflow create my-workflow --stdout"]
2648
+ ]
2649
+ });
2650
+ // Workflow name
2651
+ workflowName = Option19.String({ required: true });
2652
+ // Description
2653
+ description = Option19.String("--description,-d", {
2654
+ description: "Workflow description"
2655
+ });
2656
+ // Print to stdout instead of saving
2657
+ stdout = Option19.Boolean("--stdout", false, {
2658
+ description: "Print workflow YAML to stdout instead of saving"
2659
+ });
2660
+ // Project path
2661
+ projectPath = Option19.String("--path,-p", {
2662
+ description: "Project path (default: current directory)"
2663
+ });
2664
+ async execute() {
2665
+ const targetPath = resolve8(this.projectPath || process.cwd());
2666
+ const workflow = createWorkflowTemplate(this.workflowName, this.description);
2667
+ if (this.stdout) {
2668
+ console.log(serializeWorkflow(workflow));
2669
+ return 0;
2670
+ }
2671
+ const filePath = saveWorkflow(targetPath, workflow);
2672
+ console.log(chalk19.green(`\u2713 Created workflow: ${chalk19.bold(this.workflowName)}`));
2673
+ console.log(chalk19.dim(` File: ${filePath}`));
2674
+ console.log();
2675
+ console.log(chalk19.dim("Edit the workflow file to add skills and configure waves."));
2676
+ console.log(chalk19.dim("Run with: skillkit workflow run " + this.workflowName));
2677
+ return 0;
2678
+ }
2679
+ };
2680
+
2681
+ // src/commands/run.ts
2682
+ import { Command as Command21, Option as Option20 } from "clipanion";
2683
+ import { resolve as resolve9, join as join6 } from "path";
2684
+ import { existsSync as existsSync9, readFileSync as readFileSync4, statSync } from "fs";
2685
+ import chalk20 from "chalk";
2686
+ import ora5 from "ora";
2687
+ import {
2688
+ createExecutionEngine,
2689
+ discoverSkills,
2690
+ extractFrontmatter
2691
+ } from "@skillkit/core";
2692
+ var RunCommand = class extends Command21 {
2693
+ static paths = [["run"]];
2694
+ static usage = Command21.Usage({
2695
+ description: "Execute a skill with task-based orchestration",
2696
+ details: `
2697
+ The run command executes a skill, optionally breaking it down into
2698
+ tasks with verification checkpoints.
2699
+
2700
+ Skills can be:
2701
+ - Installed skills (by name)
2702
+ - Local skill files (by path)
2703
+ - Remote skills (owner/repo/path)
2704
+ `,
2705
+ examples: [
2706
+ ["Run an installed skill", "$0 run typescript-strict-mode"],
2707
+ ["Run a local skill file", "$0 run ./my-skill/SKILL.md"],
2708
+ ["Dry run (show what would happen)", "$0 run typescript-strict-mode --dry-run"],
2709
+ ["Run with verification", "$0 run setup-testing --verify"]
2710
+ ]
2711
+ });
2712
+ // Skill name or path
2713
+ skillRef = Option20.String({ required: true });
2714
+ // Target agent
2715
+ agent = Option20.String("--agent,-a", {
2716
+ description: "Target agent (claude-code, cursor, etc.)"
2717
+ });
2718
+ // Dry run
2719
+ dryRun = Option20.Boolean("--dry-run,-n", false, {
2720
+ description: "Show what would be executed without running"
2721
+ });
2722
+ // Enable verification
2723
+ verify = Option20.Boolean("--verify", false, {
2724
+ description: "Run verification checks after each task"
2725
+ });
2726
+ // Auto-commit
2727
+ autoCommit = Option20.Boolean("--auto-commit", false, {
2728
+ description: "Create git commits after each task"
2729
+ });
2730
+ // Continue on error
2731
+ continueOnError = Option20.Boolean("--continue-on-error", false, {
2732
+ description: "Continue execution even if a task fails"
2733
+ });
2734
+ // Verbose output
2735
+ verbose = Option20.Boolean("--verbose,-v", false, {
2736
+ description: "Show detailed execution progress"
2737
+ });
2738
+ // JSON output
2739
+ json = Option20.Boolean("--json,-j", false, {
2740
+ description: "Output in JSON format"
2741
+ });
2742
+ // Project path
2743
+ projectPath = Option20.String("--path,-p", {
2744
+ description: "Project path (default: current directory)"
2745
+ });
2746
+ async execute() {
2747
+ const targetPath = resolve9(this.projectPath || process.cwd());
2748
+ const skill = await this.loadSkill(targetPath);
2749
+ if (!skill) {
2750
+ return 1;
2751
+ }
2752
+ if (!this.json) {
2753
+ console.log(chalk20.cyan(`Executing skill: ${chalk20.bold(skill.name)}`));
2754
+ if (skill.description) {
2755
+ console.log(chalk20.dim(skill.description));
2756
+ }
2757
+ console.log();
2758
+ }
2759
+ if (this.dryRun) {
2760
+ this.showDryRun(skill);
2761
+ return 0;
2762
+ }
2763
+ const spinner = ora5();
2764
+ let currentTask = "";
2765
+ const engine = createExecutionEngine(targetPath, {
2766
+ onProgress: (event) => {
2767
+ if (this.json) return;
2768
+ switch (event.type) {
2769
+ case "task_start":
2770
+ currentTask = event.taskName || "";
2771
+ spinner.start(`Task ${(event.taskIndex || 0) + 1}/${event.totalTasks}: ${currentTask}`);
2772
+ break;
2773
+ case "task_complete":
2774
+ const icon = event.status === "completed" ? chalk20.green("\u2713") : chalk20.red("\u2717");
2775
+ spinner.stopAndPersist({
2776
+ symbol: icon,
2777
+ text: `Task ${(event.taskIndex || 0) + 1}/${event.totalTasks}: ${currentTask}`
2778
+ });
2779
+ if (event.error && this.verbose) {
2780
+ console.log(chalk20.red(` Error: ${event.error}`));
2781
+ }
2782
+ break;
2783
+ case "checkpoint":
2784
+ spinner.info(`Checkpoint: ${event.message}`);
2785
+ break;
2786
+ case "verification":
2787
+ if (this.verbose) {
2788
+ console.log(chalk20.dim(` ${event.message}`));
2789
+ }
2790
+ break;
2791
+ case "complete":
2792
+ console.log();
2793
+ if (event.status === "completed") {
2794
+ console.log(chalk20.green("\u2713 Skill execution completed"));
2795
+ } else {
2796
+ console.log(chalk20.red(`\u2717 Skill execution ${event.status}`));
2797
+ if (event.error) {
2798
+ console.log(chalk20.red(` Error: ${event.error}`));
2799
+ }
2800
+ }
2801
+ break;
2802
+ }
2803
+ },
2804
+ checkpointHandler: async (task, _context) => {
2805
+ if (task.type === "checkpoint:decision" && task.options) {
2806
+ return {
2807
+ continue: true,
2808
+ selectedOption: task.options[0]
2809
+ };
2810
+ }
2811
+ return { continue: true };
2812
+ }
2813
+ });
2814
+ const result = await engine.execute(skill, {
2815
+ agent: this.agent,
2816
+ autoCommit: this.autoCommit,
2817
+ verify: this.verify,
2818
+ continueOnError: this.continueOnError
2819
+ });
2820
+ if (this.json) {
2821
+ console.log(JSON.stringify(result, null, 2));
2822
+ } else {
2823
+ console.log();
2824
+ console.log(chalk20.cyan("Summary:"));
2825
+ console.log(` Duration: ${chalk20.dim(this.formatDuration(result.durationMs || 0))}`);
2826
+ console.log(` Tasks: ${chalk20.dim(`${result.tasks.filter((t) => t.status === "completed").length}/${result.tasks.length} completed`)}`);
2827
+ if (result.filesModified.length > 0) {
2828
+ console.log(` Files modified: ${chalk20.dim(result.filesModified.length.toString())}`);
2829
+ }
2830
+ if (result.commits.length > 0) {
2831
+ console.log(` Commits: ${chalk20.dim(result.commits.join(", "))}`);
2832
+ }
2833
+ }
2834
+ return result.status === "completed" ? 0 : 1;
2835
+ }
2836
+ async loadSkill(projectPath) {
2837
+ if (this.skillRef.endsWith(".md") || existsSync9(this.skillRef) && statSync(this.skillRef).isFile()) {
2838
+ return this.loadSkillFromFile(resolve9(this.skillRef));
2839
+ }
2840
+ const skill = this.findInstalledSkill(projectPath, this.skillRef);
2841
+ if (skill) {
2842
+ return skill;
2843
+ }
2844
+ console.error(chalk20.red(`Skill "${this.skillRef}" not found`));
2845
+ console.log(chalk20.dim("Install skills with: skillkit install <source>"));
2846
+ return null;
2847
+ }
2848
+ loadSkillFromFile(filePath) {
2849
+ if (!existsSync9(filePath)) {
2850
+ console.error(chalk20.red(`File not found: ${filePath}`));
2851
+ return null;
2852
+ }
2853
+ try {
2854
+ const content = readFileSync4(filePath, "utf-8");
2855
+ const frontmatter = extractFrontmatter(content);
2856
+ const tasks = this.parseTasksFromFrontmatter(frontmatter);
2857
+ return {
2858
+ name: frontmatter?.name || this.skillRef,
2859
+ description: frontmatter?.description,
2860
+ version: frontmatter?.version,
2861
+ source: filePath,
2862
+ content,
2863
+ tasks
2864
+ };
2865
+ } catch (error) {
2866
+ console.error(chalk20.red(`Failed to load skill: ${error}`));
2867
+ return null;
2868
+ }
2869
+ }
2870
+ findInstalledSkill(projectPath, skillName) {
2871
+ const skillDirs = [
2872
+ join6(projectPath, ".claude", "skills"),
2873
+ join6(projectPath, ".cursor", "skills"),
2874
+ join6(projectPath, "skills"),
2875
+ join6(projectPath, ".skillkit", "skills")
2876
+ ];
2877
+ for (const dir of skillDirs) {
2878
+ if (!existsSync9(dir)) continue;
2879
+ const skills = discoverSkills(dir);
2880
+ const skill = skills.find((s) => s.name === skillName);
2881
+ if (skill) {
2882
+ const skillMdPath = join6(skill.path, "SKILL.md");
2883
+ if (existsSync9(skillMdPath)) {
2884
+ return this.loadSkillFromFile(skillMdPath);
2885
+ }
2886
+ }
2887
+ }
2888
+ return null;
2889
+ }
2890
+ parseTasksFromFrontmatter(frontmatter) {
2891
+ if (!frontmatter?.tasks || !Array.isArray(frontmatter.tasks)) {
2892
+ return void 0;
2893
+ }
2894
+ return frontmatter.tasks.map((task, index) => ({
2895
+ id: task.id || `task-${index}`,
2896
+ name: task.name || `Task ${index + 1}`,
2897
+ type: task.type || "auto",
2898
+ action: task.action || "",
2899
+ files: task.files,
2900
+ options: task.options,
2901
+ verify: task.verify
2902
+ }));
2903
+ }
2904
+ showDryRun(skill) {
2905
+ console.log(chalk20.cyan("Dry Run - Execution Plan"));
2906
+ console.log();
2907
+ console.log(`Skill: ${chalk20.bold(skill.name)}`);
2908
+ if (skill.description) {
2909
+ console.log(`Description: ${chalk20.dim(skill.description)}`);
2910
+ }
2911
+ console.log(`Source: ${chalk20.dim(skill.source)}`);
2912
+ console.log();
2913
+ if (skill.tasks && skill.tasks.length > 0) {
2914
+ console.log(chalk20.cyan("Tasks:"));
2915
+ for (let i = 0; i < skill.tasks.length; i++) {
2916
+ const task = skill.tasks[i];
2917
+ const typeLabel = this.getTaskTypeLabel(task.type);
2918
+ console.log(` ${i + 1}. ${task.name} ${typeLabel}`);
2919
+ if (task.action) {
2920
+ console.log(` ${chalk20.dim(task.action)}`);
2921
+ }
2922
+ if (task.files && task.files.length > 0) {
2923
+ console.log(` Files: ${chalk20.dim(task.files.join(", "))}`);
2924
+ }
2925
+ }
2926
+ } else {
2927
+ console.log(chalk20.dim("No structured tasks defined. Skill will be executed as a single unit."));
2928
+ }
2929
+ console.log();
2930
+ console.log(chalk20.dim("This is a dry run. Remove --dry-run to execute."));
2931
+ }
2932
+ getTaskTypeLabel(type) {
2933
+ switch (type) {
2934
+ case "auto":
2935
+ return chalk20.green("[auto]");
2936
+ case "checkpoint:human-verify":
2937
+ return chalk20.yellow("[verify]");
2938
+ case "checkpoint:decision":
2939
+ return chalk20.blue("[decision]");
2940
+ case "checkpoint:human-action":
2941
+ return chalk20.magenta("[manual]");
2942
+ default:
2943
+ return "";
2944
+ }
2945
+ }
2946
+ formatDuration(ms) {
2947
+ if (ms < 1e3) return `${ms}ms`;
2948
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2949
+ const minutes = Math.floor(ms / 6e4);
2950
+ const seconds = Math.floor(ms % 6e4 / 1e3);
2951
+ return `${minutes}m ${seconds}s`;
2952
+ }
2953
+ };
2954
+
2955
+ // src/commands/test.ts
2956
+ import { Command as Command22, Option as Option21 } from "clipanion";
2957
+ import { resolve as resolve10, join as join7 } from "path";
2958
+ import { existsSync as existsSync10, readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
2959
+ import chalk21 from "chalk";
2960
+ import {
2961
+ runTestSuite,
2962
+ createTestSuiteFromFrontmatter
2963
+ } from "@skillkit/core";
2964
+ import { parse as parseYaml } from "yaml";
2965
+ var TestCommand = class extends Command22 {
2966
+ static paths = [["test"]];
2967
+ static usage = Command22.Usage({
2968
+ description: "Run skill tests",
2969
+ details: `
2970
+ The test command runs test cases defined in skill frontmatter.
2971
+
2972
+ Tests are defined in the skill's YAML frontmatter using the 'tests' key.
2973
+
2974
+ Example skill with tests:
2975
+ \`\`\`yaml
2976
+ ---
2977
+ name: my-skill
2978
+ tests:
2979
+ - name: "Creates config file"
2980
+ assertions:
2981
+ - type: file_exists
2982
+ target: config.json
2983
+ - name: "Types check"
2984
+ assertions:
2985
+ - type: type_check
2986
+ ---
2987
+ \`\`\`
2988
+ `,
2989
+ examples: [
2990
+ ["Run all skill tests", "$0 test"],
2991
+ ["Run tests for a specific skill", "$0 test my-skill"],
2992
+ ["Run with verbose output", "$0 test --verbose"],
2993
+ ["Stop on first failure", "$0 test --bail"],
2994
+ ["Run tests with specific tags", "$0 test --tags unit,integration"]
2995
+ ]
2996
+ });
2997
+ skill = Option21.String({ required: false });
2998
+ verbose = Option21.Boolean("--verbose,-v", false, {
2999
+ description: "Verbose output"
3000
+ });
3001
+ bail = Option21.Boolean("--bail,-b", false, {
3002
+ description: "Stop on first failure"
3003
+ });
3004
+ tags = Option21.String("--tags,-t", {
3005
+ description: "Only run tests with these tags (comma-separated)"
3006
+ });
3007
+ skipTags = Option21.String("--skip-tags", {
3008
+ description: "Skip tests with these tags (comma-separated)"
3009
+ });
3010
+ json = Option21.Boolean("--json,-j", false, {
3011
+ description: "Output results as JSON"
3012
+ });
3013
+ projectPath = Option21.String("--path,-p", {
3014
+ description: "Project path"
3015
+ });
3016
+ timeout = Option21.String("--timeout", {
3017
+ description: "Test timeout in milliseconds"
3018
+ });
3019
+ async execute() {
3020
+ const targetPath = resolve10(this.projectPath || process.cwd());
3021
+ const skillFiles = this.findSkillFiles(targetPath);
3022
+ if (skillFiles.length === 0) {
3023
+ if (!this.json) {
3024
+ console.log(chalk21.yellow("No skills found with tests."));
3025
+ console.log(chalk21.dim("Add tests to your skills using YAML frontmatter."));
3026
+ } else {
3027
+ console.log(JSON.stringify({ results: [], passed: true, total: 0 }));
3028
+ }
3029
+ return 0;
3030
+ }
3031
+ const filesToTest = this.skill ? skillFiles.filter((f) => f.name.includes(this.skill)) : skillFiles;
3032
+ if (filesToTest.length === 0) {
3033
+ if (!this.json) {
3034
+ console.log(chalk21.yellow(`No skills found matching "${this.skill}"`));
3035
+ }
3036
+ return 1;
3037
+ }
3038
+ if (!this.json) {
3039
+ console.log(chalk21.bold("Running skill tests...\n"));
3040
+ }
3041
+ const tags = this.tags?.split(",").map((t) => t.trim());
3042
+ const skipTags = this.skipTags?.split(",").map((t) => t.trim());
3043
+ const timeout = this.timeout ? parseInt(this.timeout, 10) : void 0;
3044
+ const results = [];
3045
+ let allPassed = true;
3046
+ for (const file of filesToTest) {
3047
+ const suite = this.parseSkillTests(file.path, file.name);
3048
+ if (!suite || suite.tests.length === 0) {
3049
+ continue;
3050
+ }
3051
+ if (!this.json) {
3052
+ console.log(chalk21.blue(`Testing: ${suite.skillName}`));
3053
+ }
3054
+ const result = await runTestSuite(suite, {
3055
+ cwd: targetPath,
3056
+ verbose: this.verbose,
3057
+ bail: this.bail,
3058
+ tags,
3059
+ skipTags,
3060
+ timeout,
3061
+ onProgress: (event) => {
3062
+ if (this.json || !this.verbose) return;
3063
+ switch (event.type) {
3064
+ case "test_start":
3065
+ console.log(chalk21.dim(` Running: ${event.testName}`));
3066
+ break;
3067
+ case "test_end":
3068
+ if (event.passed) {
3069
+ console.log(chalk21.green(` \u2713 ${event.testName}`));
3070
+ } else {
3071
+ console.log(chalk21.red(` \u2717 ${event.testName}`));
3072
+ if (event.error) {
3073
+ console.log(chalk21.red(` ${event.error}`));
3074
+ }
3075
+ }
3076
+ break;
3077
+ case "assertion_end":
3078
+ if (this.verbose && !event.passed) {
3079
+ console.log(chalk21.red(` - ${event.assertionType}: ${event.error}`));
3080
+ }
3081
+ break;
3082
+ }
3083
+ }
3084
+ });
3085
+ results.push(result);
3086
+ if (!result.passed) {
3087
+ allPassed = false;
3088
+ }
3089
+ if (!this.json) {
3090
+ const icon = result.passed ? chalk21.green("\u2713") : chalk21.red("\u2717");
3091
+ const status = result.passed ? "PASSED" : "FAILED";
3092
+ console.log(
3093
+ `${icon} ${suite.skillName}: ${result.passedCount}/${result.tests.length} tests ${status} (${result.duration}ms)
3094
+ `
3095
+ );
3096
+ }
3097
+ if (this.bail && !result.passed) {
3098
+ break;
3099
+ }
3100
+ }
3101
+ if (this.json) {
3102
+ console.log(
3103
+ JSON.stringify({
3104
+ results: results.map((r) => ({
3105
+ skill: r.skillName,
3106
+ passed: r.passed,
3107
+ tests: r.passedCount,
3108
+ failed: r.failedCount,
3109
+ skipped: r.skippedCount,
3110
+ duration: r.duration
3111
+ })),
3112
+ passed: allPassed,
3113
+ total: results.length
3114
+ })
3115
+ );
3116
+ } else {
3117
+ const totalTests = results.reduce((acc, r) => acc + r.tests.length, 0);
3118
+ const passedTests = results.reduce((acc, r) => acc + r.passedCount, 0);
3119
+ const failedTests = results.reduce((acc, r) => acc + r.failedCount, 0);
3120
+ const skippedTests = results.reduce((acc, r) => acc + r.skippedCount, 0);
3121
+ console.log(chalk21.bold("Summary:"));
3122
+ console.log(` Skills tested: ${results.length}`);
3123
+ console.log(` Total tests: ${totalTests}`);
3124
+ console.log(chalk21.green(` Passed: ${passedTests}`));
3125
+ if (failedTests > 0) {
3126
+ console.log(chalk21.red(` Failed: ${failedTests}`));
3127
+ }
3128
+ if (skippedTests > 0) {
3129
+ console.log(chalk21.yellow(` Skipped: ${skippedTests}`));
3130
+ }
3131
+ if (allPassed) {
3132
+ console.log(chalk21.green("\n\u2713 All tests passed!"));
3133
+ } else {
3134
+ console.log(chalk21.red("\n\u2717 Some tests failed."));
3135
+ }
3136
+ }
3137
+ return allPassed ? 0 : 1;
3138
+ }
3139
+ /**
3140
+ * Find skill files with tests
3141
+ */
3142
+ findSkillFiles(projectPath) {
3143
+ const files = [];
3144
+ const skillDirs = [
3145
+ ".claude/skills",
3146
+ ".cursor/skills",
3147
+ ".skillkit/skills",
3148
+ "skills"
3149
+ ];
3150
+ for (const dir of skillDirs) {
3151
+ const fullDir = join7(projectPath, dir);
3152
+ if (!existsSync10(fullDir)) continue;
3153
+ try {
3154
+ const entries = readdirSync2(fullDir, { withFileTypes: true });
3155
+ for (const entry of entries) {
3156
+ if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".mdc"))) {
3157
+ files.push({
3158
+ name: entry.name.replace(/\.(md|mdc)$/, ""),
3159
+ path: join7(fullDir, entry.name)
3160
+ });
3161
+ }
3162
+ }
3163
+ } catch {
3164
+ }
3165
+ }
3166
+ return files;
3167
+ }
3168
+ /**
3169
+ * Parse skill file for tests
3170
+ */
3171
+ parseSkillTests(filePath, skillName) {
3172
+ try {
3173
+ const content = readFileSync5(filePath, "utf-8");
3174
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
3175
+ if (!frontmatterMatch) {
3176
+ return null;
3177
+ }
3178
+ const frontmatter = parseYaml(frontmatterMatch[1]);
3179
+ if (!frontmatter.tests) {
3180
+ return null;
3181
+ }
3182
+ return createTestSuiteFromFrontmatter(skillName, frontmatter);
3183
+ } catch {
3184
+ return null;
3185
+ }
3186
+ }
3187
+ };
3188
+
3189
+ // src/commands/marketplace.ts
3190
+ import { Command as Command23, Option as Option22 } from "clipanion";
3191
+ import chalk22 from "chalk";
3192
+ import ora6 from "ora";
3193
+ import {
3194
+ createMarketplaceAggregator
3195
+ } from "@skillkit/core";
3196
+ var MarketplaceCommand = class extends Command23 {
3197
+ static paths = [["marketplace"], ["market"], ["mp"]];
3198
+ static usage = Command23.Usage({
3199
+ description: "Browse and install skills from the marketplace",
3200
+ details: `
3201
+ The marketplace command lets you discover and install skills
3202
+ from curated repositories.
3203
+
3204
+ Skills are aggregated from multiple sources including:
3205
+ - composioHQ/awesome-claude-code-skills
3206
+ - anthropics/courses
3207
+ - User-added custom sources
3208
+ `,
3209
+ examples: [
3210
+ ["Browse marketplace", "$0 marketplace"],
3211
+ ["Search for skills", "$0 marketplace search typescript"],
3212
+ ["Refresh marketplace index", "$0 marketplace refresh"],
3213
+ ["Show popular tags", "$0 marketplace tags"],
3214
+ ["List sources", "$0 marketplace sources"]
3215
+ ]
3216
+ });
3217
+ action = Option22.String({ required: false });
3218
+ query = Option22.String({ required: false });
3219
+ limit = Option22.String("--limit,-l", {
3220
+ description: "Limit results"
3221
+ });
3222
+ tags = Option22.String("--tags,-t", {
3223
+ description: "Filter by tags (comma-separated)"
3224
+ });
3225
+ source = Option22.String("--source,-s", {
3226
+ description: "Filter by source"
3227
+ });
3228
+ json = Option22.Boolean("--json,-j", false, {
3229
+ description: "Output as JSON"
3230
+ });
3231
+ async execute() {
3232
+ const marketplace = createMarketplaceAggregator();
3233
+ switch (this.action) {
3234
+ case "search":
3235
+ return this.searchSkills(marketplace);
3236
+ case "refresh":
3237
+ return this.refreshIndex(marketplace);
3238
+ case "tags":
3239
+ return this.showTags(marketplace);
3240
+ case "sources":
3241
+ return this.showSources(marketplace);
3242
+ default:
3243
+ if (this.query || this.action) {
3244
+ return this.searchSkills(marketplace);
3245
+ }
3246
+ return this.browseMarketplace(marketplace);
3247
+ }
3248
+ }
3249
+ async browseMarketplace(marketplace) {
3250
+ const spinner = ora6("Loading marketplace...").start();
3251
+ try {
3252
+ const index = await marketplace.getIndex();
3253
+ spinner.stop();
3254
+ if (this.json) {
3255
+ console.log(JSON.stringify({
3256
+ totalSkills: index.totalCount,
3257
+ sources: index.sources.length,
3258
+ updatedAt: index.updatedAt
3259
+ }));
3260
+ return 0;
3261
+ }
3262
+ console.log(chalk22.bold("Skill Marketplace\n"));
3263
+ console.log(`Total skills: ${chalk22.cyan(index.totalCount)}`);
3264
+ console.log(`Sources: ${chalk22.cyan(index.sources.length)}`);
3265
+ console.log(`Last updated: ${chalk22.dim(new Date(index.updatedAt).toLocaleString())}
3266
+ `);
3267
+ console.log(chalk22.bold("Featured Skills:\n"));
3268
+ const featured = index.skills.slice(0, 10);
3269
+ for (const skill of featured) {
3270
+ console.log(` ${chalk22.cyan(skill.name)}`);
3271
+ if (skill.description) {
3272
+ console.log(` ${chalk22.dim(skill.description.slice(0, 60))}${skill.description.length > 60 ? "..." : ""}`);
3273
+ }
3274
+ console.log(` ${chalk22.dim(`Source: ${skill.source.name}`)}`);
3275
+ console.log();
3276
+ }
3277
+ console.log(chalk22.dim('Use "skillkit marketplace search <query>" to search for skills.'));
3278
+ console.log(chalk22.dim('Use "skillkit marketplace tags" to see popular tags.'));
3279
+ return 0;
3280
+ } catch (error) {
3281
+ spinner.fail("Failed to load marketplace");
3282
+ console.error(chalk22.red(error instanceof Error ? error.message : String(error)));
3283
+ return 1;
3284
+ }
3285
+ }
3286
+ async searchSkills(marketplace) {
3287
+ const query = this.query || this.action;
3288
+ let limit = 20;
3289
+ if (this.limit) {
3290
+ const parsed = parseInt(this.limit, 10);
3291
+ if (isNaN(parsed) || parsed <= 0) {
3292
+ console.error(chalk22.red("Invalid --limit value. Must be a positive number."));
3293
+ return 1;
3294
+ }
3295
+ limit = parsed;
3296
+ }
3297
+ const tags = this.tags?.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
3298
+ const spinner = ora6(`Searching for "${query}"...`).start();
3299
+ try {
3300
+ const options = {
3301
+ query: query || void 0,
3302
+ limit,
3303
+ tags: tags && tags.length > 0 ? tags : void 0,
3304
+ source: this.source
3305
+ };
3306
+ const result = await marketplace.search(options);
3307
+ spinner.stop();
3308
+ if (this.json) {
3309
+ console.log(JSON.stringify(result));
3310
+ return 0;
3311
+ }
3312
+ if (result.skills.length === 0) {
3313
+ console.log(chalk22.yellow(`No skills found${query ? ` matching "${query}"` : ""}.`));
3314
+ return 0;
3315
+ }
3316
+ console.log(chalk22.bold(`Found ${result.total} skill(s):
3317
+ `));
3318
+ for (const skill of result.skills) {
3319
+ console.log(`${chalk22.cyan(skill.name)} ${chalk22.dim(`(${skill.source.name})`)}`);
3320
+ if (skill.description) {
3321
+ console.log(` ${skill.description}`);
3322
+ }
3323
+ if (skill.tags.length > 0) {
3324
+ console.log(` Tags: ${chalk22.dim(skill.tags.join(", "))}`);
3325
+ }
3326
+ console.log();
3327
+ }
3328
+ if (result.total > result.skills.length) {
3329
+ console.log(chalk22.dim(`Showing ${result.skills.length} of ${result.total} results. Use --limit to see more.`));
3330
+ }
3331
+ return 0;
3332
+ } catch (error) {
3333
+ spinner.fail("Search failed");
3334
+ console.error(chalk22.red(error instanceof Error ? error.message : String(error)));
3335
+ return 1;
3336
+ }
3337
+ }
3338
+ async refreshIndex(marketplace) {
3339
+ const spinner = ora6("Refreshing marketplace index...").start();
3340
+ try {
3341
+ const index = await marketplace.refresh();
3342
+ spinner.succeed(`Marketplace refreshed: ${index.totalCount} skills from ${index.sources.length} sources`);
3343
+ return 0;
3344
+ } catch (error) {
3345
+ spinner.fail("Refresh failed");
3346
+ console.error(chalk22.red(error instanceof Error ? error.message : String(error)));
3347
+ return 1;
3348
+ }
3349
+ }
3350
+ async showTags(marketplace) {
3351
+ const spinner = ora6("Loading tags...").start();
3352
+ try {
3353
+ const tags = await marketplace.getPopularTags(30);
3354
+ spinner.stop();
3355
+ if (this.json) {
3356
+ console.log(JSON.stringify(tags));
3357
+ return 0;
3358
+ }
3359
+ console.log(chalk22.bold("Popular Tags:\n"));
3360
+ for (const { tag, count } of tags) {
3361
+ const bar = "\u2588".repeat(Math.min(count, 20));
3362
+ console.log(` ${tag.padEnd(15)} ${chalk22.cyan(bar)} ${count}`);
3363
+ }
3364
+ return 0;
3365
+ } catch (error) {
3366
+ spinner.fail("Failed to load tags");
3367
+ console.error(chalk22.red(error instanceof Error ? error.message : String(error)));
3368
+ return 1;
3369
+ }
3370
+ }
3371
+ async showSources(marketplace) {
3372
+ const sources = marketplace.getSources();
3373
+ if (this.json) {
3374
+ console.log(JSON.stringify(sources));
3375
+ return 0;
3376
+ }
3377
+ console.log(chalk22.bold("Skill Sources:\n"));
3378
+ for (const source of sources) {
3379
+ console.log(`${chalk22.cyan(source.name)} ${source.official ? chalk22.green("(official)") : ""}`);
3380
+ console.log(` ${chalk22.dim(`${source.owner}/${source.repo}`)}`);
3381
+ if (source.description) {
3382
+ console.log(` ${source.description}`);
3383
+ }
3384
+ console.log();
3385
+ }
3386
+ return 0;
3387
+ }
3388
+ };
3389
+
3390
+ // src/commands/memory.ts
3391
+ import { Command as Command24, Option as Option23 } from "clipanion";
3392
+ import chalk23 from "chalk";
3393
+ import {
3394
+ ObservationStore,
3395
+ LearningStore,
3396
+ getMemoryPaths,
3397
+ initializeMemoryDirectory,
3398
+ getMemoryStatus,
3399
+ createMemoryCompressor,
3400
+ createMemoryInjector
3401
+ } from "@skillkit/core";
3402
+ var MemoryCommand = class extends Command24 {
3403
+ static paths = [["memory"], ["mem"]];
3404
+ static usage = Command24.Usage({
3405
+ description: "Manage session memory across AI coding agents",
3406
+ details: `
3407
+ The memory command helps you view, search, and manage learnings
3408
+ captured from coding sessions across all AI agents.
3409
+
3410
+ Subcommands:
3411
+ - status: Show current memory status
3412
+ - search: Search memories by query
3413
+ - list: List all learnings
3414
+ - show: Show a specific learning
3415
+ - compress: Compress observations into learnings
3416
+ - export: Export a learning as a skill
3417
+ - import: Import memories from another project
3418
+ - clear: Clear session observations
3419
+ - add: Manually add a learning
3420
+ - rate: Rate a learning's effectiveness
3421
+ - config: Configure memory settings
3422
+ `,
3423
+ examples: [
3424
+ ["Show memory status", "$0 memory status"],
3425
+ ["Search memories", '$0 memory search "authentication"'],
3426
+ ["Search global memories", '$0 memory search --global "react hooks"'],
3427
+ ["List project learnings", "$0 memory list"],
3428
+ ["List global learnings", "$0 memory list --global"],
3429
+ ["Show a learning", "$0 memory show <id>"],
3430
+ ["Compress observations", "$0 memory compress"],
3431
+ ["Export as skill", "$0 memory export <id> --name my-skill"],
3432
+ ["Clear session", "$0 memory clear"],
3433
+ ["Add manual learning", '$0 memory add --title "..." --content "..."'],
3434
+ ["Rate effectiveness", "$0 memory rate <id> 85"]
3435
+ ]
3436
+ });
3437
+ // Subcommand (status, search, list, show, compress, export, import, clear, add, rate, config)
3438
+ action = Option23.String({ required: false });
3439
+ // Second argument (query for search, id for show/export/rate)
3440
+ arg = Option23.String({ required: false });
3441
+ // Third argument (rating value for rate command)
3442
+ ratingArg = Option23.String({ required: false });
3443
+ // Global scope
3444
+ global = Option23.Boolean("--global,-g", false, {
3445
+ description: "Use global memories instead of project"
3446
+ });
3447
+ // Tags filter
3448
+ tags = Option23.String("--tags,-t", {
3449
+ description: "Filter by tags (comma-separated)"
3450
+ });
3451
+ // Limit results
3452
+ limit = Option23.String("--limit,-l", {
3453
+ description: "Maximum number of results"
3454
+ });
3455
+ // Title for add
3456
+ title = Option23.String("--title", {
3457
+ description: "Title for new learning"
3458
+ });
3459
+ // Content for add
3460
+ content = Option23.String("--content,-c", {
3461
+ description: "Content for new learning"
3462
+ });
3463
+ // Name for export
3464
+ name = Option23.String("--name,-n", {
3465
+ description: "Name for exported skill"
3466
+ });
3467
+ // Output file
3468
+ output = Option23.String("--output,-o", {
3469
+ description: "Output file path"
3470
+ });
3471
+ // Input file
3472
+ input = Option23.String("--input,-i", {
3473
+ description: "Input file path"
3474
+ });
3475
+ // Keep learnings when clearing
3476
+ keepLearnings = Option23.Boolean("--keep-learnings", false, {
3477
+ description: "Keep learnings when clearing"
3478
+ });
3479
+ // Dry run
3480
+ dryRun = Option23.Boolean("--dry-run", false, {
3481
+ description: "Preview without making changes"
3482
+ });
3483
+ // JSON output
3484
+ json = Option23.Boolean("--json,-j", false, {
3485
+ description: "Output in JSON format"
3486
+ });
3487
+ // Verbose output
3488
+ verbose = Option23.Boolean("--verbose,-v", false, {
3489
+ description: "Show detailed output"
3490
+ });
3491
+ async execute() {
3492
+ const action = this.action || "status";
3493
+ switch (action) {
3494
+ case "status":
3495
+ return this.showStatus();
3496
+ case "search":
3497
+ return this.searchMemories();
3498
+ case "list":
3499
+ return this.listLearnings();
3500
+ case "show":
3501
+ return this.showLearning();
3502
+ case "compress":
3503
+ return this.compressObservations();
3504
+ case "export":
3505
+ return this.exportLearning();
3506
+ case "import":
3507
+ return this.importMemories();
3508
+ case "clear":
3509
+ return this.clearMemory();
3510
+ case "add":
3511
+ return this.addLearning();
3512
+ case "rate":
3513
+ return this.rateLearning();
3514
+ case "config":
3515
+ return this.showConfig();
3516
+ default:
3517
+ console.error(chalk23.red(`Unknown action: ${action}`));
3518
+ console.log(chalk23.gray("Available actions: status, search, list, show, compress, export, import, clear, add, rate, config"));
3519
+ return 1;
3520
+ }
3521
+ }
3522
+ /**
3523
+ * Show memory status
3524
+ */
3525
+ async showStatus() {
3526
+ const projectPath = process.cwd();
3527
+ const status = getMemoryStatus(projectPath);
3528
+ const paths = getMemoryPaths(projectPath);
3529
+ const observationStore = new ObservationStore(projectPath);
3530
+ const projectLearningStore = new LearningStore("project", projectPath);
3531
+ const globalLearningStore = new LearningStore("global");
3532
+ const sessionObservations = status.hasObservations ? observationStore.count() : 0;
3533
+ const projectLearnings = status.hasLearnings ? projectLearningStore.count() : 0;
3534
+ const globalLearnings = status.hasGlobalLearnings ? globalLearningStore.count() : 0;
3535
+ const sessionId = status.hasObservations ? observationStore.getSessionId() : null;
3536
+ if (this.json) {
3537
+ console.log(JSON.stringify({
3538
+ ...status,
3539
+ sessionObservations,
3540
+ projectLearnings,
3541
+ globalLearnings,
3542
+ sessionId,
3543
+ paths
3544
+ }, null, 2));
3545
+ return 0;
3546
+ }
3547
+ console.log(chalk23.bold("\nMemory Status\n"));
3548
+ console.log(chalk23.cyan("Session:"));
3549
+ console.log(` Observations: ${sessionObservations}`);
3550
+ if (sessionId) {
3551
+ console.log(` Session ID: ${chalk23.gray(sessionId.slice(0, 8))}`);
3552
+ }
3553
+ console.log();
3554
+ console.log(chalk23.cyan("Project:"));
3555
+ console.log(` Learnings: ${projectLearnings}`);
3556
+ console.log(` Path: ${chalk23.gray(status.projectMemoryExists ? paths.projectMemoryDir : "Not initialized")}`);
3557
+ console.log();
3558
+ console.log(chalk23.cyan("Global:"));
3559
+ console.log(` Learnings: ${globalLearnings}`);
3560
+ console.log(` Path: ${chalk23.gray(status.globalMemoryExists ? paths.globalMemoryDir : "Not initialized")}`);
3561
+ console.log();
3562
+ if (sessionObservations >= 50) {
3563
+ console.log(chalk23.yellow("\u{1F4A1} You have many uncompressed observations. Consider running:"));
3564
+ console.log(chalk23.gray(" skillkit memory compress"));
3565
+ }
3566
+ return 0;
3567
+ }
3568
+ /**
3569
+ * Search memories
3570
+ */
3571
+ async searchMemories() {
3572
+ const query = this.arg;
3573
+ if (!query) {
3574
+ console.error(chalk23.red("Error: Search query is required"));
3575
+ console.log(chalk23.gray('Usage: skillkit memory search "your query"'));
3576
+ return 1;
3577
+ }
3578
+ const projectPath = process.cwd();
3579
+ const injector = createMemoryInjector(projectPath);
3580
+ let maxLearnings = 10;
3581
+ if (this.limit) {
3582
+ const parsed = parseInt(this.limit, 10);
3583
+ if (isNaN(parsed) || parsed <= 0) {
3584
+ console.log(chalk23.red("Invalid --limit value. Must be a positive number."));
3585
+ return 1;
3586
+ }
3587
+ maxLearnings = parsed;
3588
+ }
3589
+ const tags = this.tags?.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
3590
+ const results = injector.search(query, {
3591
+ includeGlobal: this.global,
3592
+ tags: tags && tags.length > 0 ? tags : void 0,
3593
+ maxLearnings,
3594
+ minRelevance: 0
3595
+ });
3596
+ if (this.json) {
3597
+ console.log(JSON.stringify(results, null, 2));
3598
+ return 0;
3599
+ }
3600
+ if (results.length === 0) {
3601
+ console.log(chalk23.yellow(`No memories found for: "${query}"`));
3602
+ return 0;
3603
+ }
3604
+ console.log(chalk23.bold(`
3605
+ Found ${results.length} memories:
3606
+ `));
3607
+ for (const { learning, relevanceScore, matchedBy } of results) {
3608
+ console.log(`${chalk23.cyan("\u25CF")} ${chalk23.bold(learning.title)}`);
3609
+ console.log(` ID: ${chalk23.gray(learning.id.slice(0, 8))}`);
3610
+ console.log(` Relevance: ${this.formatScore(relevanceScore)}%`);
3611
+ console.log(` Tags: ${learning.tags.join(", ")}`);
3612
+ console.log(` Scope: ${learning.scope}`);
3613
+ if (this.verbose) {
3614
+ console.log(` Matched: ${this.formatMatchedBy(matchedBy)}`);
3615
+ console.log(` Created: ${new Date(learning.createdAt).toLocaleDateString()}`);
3616
+ console.log(` Uses: ${learning.useCount}`);
3617
+ }
3618
+ const excerpt = learning.content.slice(0, 100);
3619
+ console.log(` ${chalk23.gray(excerpt)}${learning.content.length > 100 ? "..." : ""}`);
3620
+ console.log();
3621
+ }
3622
+ return 0;
3623
+ }
3624
+ /**
3625
+ * List all learnings
3626
+ */
3627
+ async listLearnings() {
3628
+ const projectPath = process.cwd();
3629
+ const store = new LearningStore(
3630
+ this.global ? "global" : "project",
3631
+ this.global ? void 0 : projectPath
3632
+ );
3633
+ let learnings = store.getAll();
3634
+ if (this.tags) {
3635
+ const tagList = this.tags.split(",").map((t) => t.trim().toLowerCase());
3636
+ learnings = learnings.filter(
3637
+ (l) => l.tags.some((t) => tagList.includes(t.toLowerCase()))
3638
+ );
3639
+ }
3640
+ const limit = this.limit ? parseInt(this.limit, 10) : learnings.length;
3641
+ learnings = learnings.slice(0, limit);
3642
+ if (this.json) {
3643
+ console.log(JSON.stringify(learnings, null, 2));
3644
+ return 0;
3645
+ }
3646
+ const scope = this.global ? "Global" : "Project";
3647
+ console.log(chalk23.bold(`
3648
+ ${scope} Learnings (${learnings.length}):
3649
+ `));
3650
+ if (learnings.length === 0) {
3651
+ console.log(chalk23.gray("No learnings found."));
3652
+ console.log(chalk23.gray("\nCapture learnings by running skills or add manually:"));
3653
+ console.log(chalk23.gray(' skillkit memory add --title "..." --content "..."'));
3654
+ return 0;
3655
+ }
3656
+ for (const learning of learnings) {
3657
+ const effectiveness = learning.effectiveness !== void 0 ? ` [${this.formatScore(learning.effectiveness)}%]` : "";
3658
+ console.log(`${chalk23.cyan("\u25CF")} ${learning.title}${chalk23.green(effectiveness)}`);
3659
+ console.log(` ${chalk23.gray(learning.id.slice(0, 8))} | ${learning.tags.join(", ")} | ${learning.useCount} uses`);
3660
+ if (this.verbose) {
3661
+ const excerpt = learning.content.slice(0, 80);
3662
+ console.log(` ${chalk23.gray(excerpt)}${learning.content.length > 80 ? "..." : ""}`);
3663
+ }
3664
+ }
3665
+ console.log();
3666
+ return 0;
3667
+ }
3668
+ /**
3669
+ * Show a specific learning
3670
+ */
3671
+ async showLearning() {
3672
+ const id = this.arg;
3673
+ if (!id) {
3674
+ console.error(chalk23.red("Error: Learning ID is required"));
3675
+ console.log(chalk23.gray("Usage: skillkit memory show <id>"));
3676
+ return 1;
3677
+ }
3678
+ const projectPath = process.cwd();
3679
+ let learning = new LearningStore("project", projectPath).getById(id);
3680
+ if (!learning) {
3681
+ learning = new LearningStore("global").getById(id);
3682
+ }
3683
+ if (!learning) {
3684
+ const projectLearnings = new LearningStore("project", projectPath).getAll();
3685
+ const globalLearnings = new LearningStore("global").getAll();
3686
+ const all = [...projectLearnings, ...globalLearnings];
3687
+ learning = all.find((l) => l.id.startsWith(id));
3688
+ }
3689
+ if (!learning) {
3690
+ console.error(chalk23.red(`Learning not found: ${id}`));
3691
+ return 1;
3692
+ }
3693
+ if (this.json) {
3694
+ console.log(JSON.stringify(learning, null, 2));
3695
+ return 0;
3696
+ }
3697
+ console.log(chalk23.bold(`
3698
+ ${learning.title}
3699
+ `));
3700
+ console.log(chalk23.gray(`ID: ${learning.id}`));
3701
+ console.log(chalk23.gray(`Scope: ${learning.scope}`));
3702
+ console.log(chalk23.gray(`Source: ${learning.source}`));
3703
+ console.log(chalk23.gray(`Tags: ${learning.tags.join(", ")}`));
3704
+ if (learning.frameworks?.length) {
3705
+ console.log(chalk23.gray(`Frameworks: ${learning.frameworks.join(", ")}`));
3706
+ }
3707
+ if (learning.patterns?.length) {
3708
+ console.log(chalk23.gray(`Patterns: ${learning.patterns.join(", ")}`));
3709
+ }
3710
+ console.log(chalk23.gray(`Created: ${new Date(learning.createdAt).toLocaleString()}`));
3711
+ console.log(chalk23.gray(`Updated: ${new Date(learning.updatedAt).toLocaleString()}`));
3712
+ console.log(chalk23.gray(`Uses: ${learning.useCount}`));
3713
+ if (learning.effectiveness !== void 0) {
3714
+ console.log(chalk23.gray(`Effectiveness: ${this.formatScore(learning.effectiveness)}%`));
3715
+ }
3716
+ console.log(chalk23.bold("\nContent:\n"));
3717
+ console.log(learning.content);
3718
+ console.log();
3719
+ return 0;
3720
+ }
3721
+ /**
3722
+ * Compress observations into learnings
3723
+ */
3724
+ async compressObservations() {
3725
+ const projectPath = process.cwd();
3726
+ initializeMemoryDirectory(projectPath);
3727
+ const observationStore = new ObservationStore(projectPath);
3728
+ const observations = observationStore.getAll();
3729
+ if (observations.length === 0) {
3730
+ console.log(chalk23.yellow("No observations to compress."));
3731
+ return 0;
3732
+ }
3733
+ console.log(chalk23.cyan(`Found ${observations.length} observations to compress...
3734
+ `));
3735
+ const compressor = createMemoryCompressor(projectPath);
3736
+ const compressionOptions = {
3737
+ minObservations: 2,
3738
+ additionalTags: this.tags?.split(",").map((t) => t.trim())
3739
+ };
3740
+ if (this.dryRun) {
3741
+ console.log(chalk23.gray("(Dry run - no changes will be made)\n"));
3742
+ const result2 = await compressor.compress(observations, compressionOptions);
3743
+ console.log(chalk23.green(`\u2713 Would compress ${result2.stats.inputCount} observations into ${result2.stats.outputCount} learnings
3744
+ `));
3745
+ if (result2.learnings.length > 0) {
3746
+ console.log(chalk23.bold("Learnings that would be created:"));
3747
+ for (const learning of result2.learnings) {
3748
+ console.log(` ${chalk23.cyan("\u25CF")} ${learning.title}`);
3749
+ console.log(` Tags: ${learning.tags.join(", ")}`);
3750
+ }
3751
+ console.log();
3752
+ }
3753
+ return 0;
3754
+ }
3755
+ const { learnings, result } = await compressor.compressAndStore(observations, compressionOptions);
3756
+ console.log(chalk23.green(`\u2713 Compressed ${result.stats.inputCount} observations into ${result.stats.outputCount} learnings
3757
+ `));
3758
+ if (learnings.length > 0) {
3759
+ console.log(chalk23.bold("New Learnings:"));
3760
+ for (const learning of learnings) {
3761
+ console.log(` ${chalk23.cyan("\u25CF")} ${learning.title}`);
3762
+ console.log(` Tags: ${learning.tags.join(", ")}`);
3763
+ }
3764
+ console.log();
3765
+ }
3766
+ if (result.processedObservationIds.length > 0) {
3767
+ const deleted = observationStore.deleteMany(result.processedObservationIds);
3768
+ console.log(chalk23.gray(`Cleared ${deleted} processed observations.`));
3769
+ }
3770
+ return 0;
3771
+ }
3772
+ /**
3773
+ * Export a learning as a skill
3774
+ */
3775
+ async exportLearning() {
3776
+ const id = this.arg;
3777
+ if (!id) {
3778
+ console.error(chalk23.red("Error: Learning ID is required"));
3779
+ console.log(chalk23.gray("Usage: skillkit memory export <id> --name my-skill"));
3780
+ return 1;
3781
+ }
3782
+ const projectPath = process.cwd();
3783
+ let learning = new LearningStore("project", projectPath).getById(id);
3784
+ if (!learning) {
3785
+ learning = new LearningStore("global").getById(id);
3786
+ }
3787
+ if (!learning) {
3788
+ const projectLearnings = new LearningStore("project", projectPath).getAll();
3789
+ const globalLearnings = new LearningStore("global").getAll();
3790
+ const all = [...projectLearnings, ...globalLearnings];
3791
+ learning = all.find((l) => l.id.startsWith(id));
3792
+ }
3793
+ if (!learning) {
3794
+ console.error(chalk23.red(`Learning not found: ${id}`));
3795
+ return 1;
3796
+ }
3797
+ const skillName = this.name || this.slugify(learning.title);
3798
+ const skillContent = this.generateSkillContent(learning, skillName);
3799
+ if (this.dryRun) {
3800
+ console.log(chalk23.gray("(Dry run preview)\n"));
3801
+ console.log(skillContent);
3802
+ return 0;
3803
+ }
3804
+ const outputPath = this.output || `.skillkit/exports/${skillName}/SKILL.md`;
3805
+ const { dirname: dirname3 } = await import("path");
3806
+ const { existsSync: existsSync13, mkdirSync: mkdirSync7, writeFileSync: writeFileSync6 } = await import("fs");
3807
+ const outputDir = dirname3(outputPath);
3808
+ if (!existsSync13(outputDir)) {
3809
+ mkdirSync7(outputDir, { recursive: true });
3810
+ }
3811
+ writeFileSync6(outputPath, skillContent, "utf-8");
3812
+ console.log(chalk23.green(`\u2713 Exported learning as skill: ${skillName}`));
3813
+ console.log(chalk23.gray(` Path: ${outputPath}`));
3814
+ return 0;
3815
+ }
3816
+ /**
3817
+ * Import memories from another project
3818
+ */
3819
+ async importMemories() {
3820
+ const inputPath = this.input || this.arg;
3821
+ if (!inputPath) {
3822
+ console.error(chalk23.red("Error: Input path is required"));
3823
+ console.log(chalk23.gray("Usage: skillkit memory import --input <path>"));
3824
+ return 1;
3825
+ }
3826
+ const { existsSync: existsSync13, readFileSync: readFileSync7 } = await import("fs");
3827
+ const { resolve: resolve12 } = await import("path");
3828
+ const fullPath = resolve12(inputPath);
3829
+ if (!existsSync13(fullPath)) {
3830
+ console.error(chalk23.red(`File not found: ${fullPath}`));
3831
+ return 1;
3832
+ }
3833
+ const projectPath = process.cwd();
3834
+ const store = new LearningStore(
3835
+ this.global ? "global" : "project",
3836
+ this.global ? void 0 : projectPath
3837
+ );
3838
+ try {
3839
+ const content = readFileSync7(fullPath, "utf-8");
3840
+ const { parse: parseYaml2 } = await import("yaml");
3841
+ const data = parseYaml2(content);
3842
+ if (!data.learnings || !Array.isArray(data.learnings)) {
3843
+ console.error(chalk23.red("Invalid memory file format"));
3844
+ return 1;
3845
+ }
3846
+ let imported = 0;
3847
+ for (const learning of data.learnings) {
3848
+ if (this.dryRun) {
3849
+ console.log(chalk23.gray(`Would import: ${learning.title}`));
3850
+ } else {
3851
+ store.add({
3852
+ source: "imported",
3853
+ title: learning.title,
3854
+ content: learning.content,
3855
+ // Default to empty arrays if not present in imported YAML
3856
+ tags: Array.isArray(learning.tags) ? learning.tags : [],
3857
+ frameworks: Array.isArray(learning.frameworks) ? learning.frameworks : [],
3858
+ patterns: Array.isArray(learning.patterns) ? learning.patterns : []
3859
+ });
3860
+ imported++;
3861
+ }
3862
+ }
3863
+ if (this.dryRun) {
3864
+ console.log(chalk23.gray(`
3865
+ (Dry run - ${data.learnings.length} learnings would be imported)`));
3866
+ } else {
3867
+ console.log(chalk23.green(`\u2713 Imported ${imported} learnings`));
3868
+ }
3869
+ return 0;
3870
+ } catch (error) {
3871
+ console.error(chalk23.red(`Import failed: ${error}`));
3872
+ return 1;
3873
+ }
3874
+ }
3875
+ /**
3876
+ * Clear session observations
3877
+ */
3878
+ async clearMemory() {
3879
+ const projectPath = process.cwd();
3880
+ if (this.dryRun) {
3881
+ const observationStore2 = new ObservationStore(projectPath);
3882
+ const learningStore = new LearningStore("project", projectPath);
3883
+ console.log(chalk23.gray("(Dry run preview)\n"));
3884
+ console.log(`Would clear ${observationStore2.count()} observations`);
3885
+ if (!this.keepLearnings) {
3886
+ console.log(`Would clear ${learningStore.count()} learnings`);
3887
+ }
3888
+ return 0;
3889
+ }
3890
+ const observationStore = new ObservationStore(projectPath);
3891
+ observationStore.clear();
3892
+ console.log(chalk23.green("\u2713 Cleared session observations"));
3893
+ if (!this.keepLearnings) {
3894
+ const learningStore = new LearningStore("project", projectPath);
3895
+ learningStore.clear();
3896
+ console.log(chalk23.green("\u2713 Cleared project learnings"));
3897
+ }
3898
+ return 0;
3899
+ }
3900
+ /**
3901
+ * Add a manual learning
3902
+ */
3903
+ async addLearning() {
3904
+ if (!this.title) {
3905
+ console.error(chalk23.red("Error: --title is required"));
3906
+ console.log(chalk23.gray('Usage: skillkit memory add --title "..." --content "..."'));
3907
+ return 1;
3908
+ }
3909
+ if (!this.content) {
3910
+ console.error(chalk23.red("Error: --content is required"));
3911
+ console.log(chalk23.gray('Usage: skillkit memory add --title "..." --content "..."'));
3912
+ return 1;
3913
+ }
3914
+ const projectPath = process.cwd();
3915
+ const store = new LearningStore(
3916
+ this.global ? "global" : "project",
3917
+ this.global ? void 0 : projectPath
3918
+ );
3919
+ const tags = this.tags?.split(",").map((t) => t.trim()) || [];
3920
+ const learning = store.add({
3921
+ source: "manual",
3922
+ title: this.title,
3923
+ content: this.content,
3924
+ tags
3925
+ });
3926
+ console.log(chalk23.green(`\u2713 Added learning: ${learning.title}`));
3927
+ console.log(chalk23.gray(` ID: ${learning.id}`));
3928
+ return 0;
3929
+ }
3930
+ /**
3931
+ * Rate a learning's effectiveness
3932
+ */
3933
+ async rateLearning() {
3934
+ const id = this.arg;
3935
+ if (!id) {
3936
+ console.error(chalk23.red("Error: Learning ID is required"));
3937
+ console.log(chalk23.gray("Usage: skillkit memory rate <id> <rating>"));
3938
+ return 1;
3939
+ }
3940
+ const rating = parseInt(this.ratingArg || "0", 10);
3941
+ if (isNaN(rating) || rating < 0 || rating > 100) {
3942
+ console.error(chalk23.red("Error: Rating must be 0-100"));
3943
+ console.log(chalk23.gray("Usage: skillkit memory rate <id> <rating>"));
3944
+ return 1;
3945
+ }
3946
+ const projectPath = process.cwd();
3947
+ const projectStore = new LearningStore("project", projectPath);
3948
+ let learning = projectStore.getById(id);
3949
+ let store = projectStore;
3950
+ if (!learning) {
3951
+ const globalStore = new LearningStore("global");
3952
+ learning = globalStore.getById(id);
3953
+ store = globalStore;
3954
+ }
3955
+ if (!learning) {
3956
+ const projectLearnings = projectStore.getAll();
3957
+ const globalStore = new LearningStore("global");
3958
+ const globalLearnings = globalStore.getAll();
3959
+ learning = projectLearnings.find((l) => l.id.startsWith(id));
3960
+ if (learning) {
3961
+ store = projectStore;
3962
+ } else {
3963
+ learning = globalLearnings.find((l) => l.id.startsWith(id));
3964
+ if (learning) {
3965
+ store = globalStore;
3966
+ }
3967
+ }
3968
+ }
3969
+ if (!learning) {
3970
+ console.error(chalk23.red(`Learning not found: ${id}`));
3971
+ return 1;
3972
+ }
3973
+ store.setEffectiveness(learning.id, rating);
3974
+ console.log(chalk23.green(`\u2713 Rated "${learning.title}" as ${rating}% effective`));
3975
+ return 0;
3976
+ }
3977
+ /**
3978
+ * Show memory configuration
3979
+ */
3980
+ async showConfig() {
3981
+ const projectPath = process.cwd();
3982
+ const paths = getMemoryPaths(projectPath);
3983
+ if (this.json) {
3984
+ console.log(JSON.stringify(paths, null, 2));
3985
+ return 0;
3986
+ }
3987
+ console.log(chalk23.bold("\nMemory Configuration\n"));
3988
+ console.log(chalk23.cyan("Paths:"));
3989
+ console.log(` Project observations: ${chalk23.gray(paths.observationsFile)}`);
3990
+ console.log(` Project learnings: ${chalk23.gray(paths.learningsFile)}`);
3991
+ console.log(` Project index: ${chalk23.gray(paths.indexFile)}`);
3992
+ console.log(` Global learnings: ${chalk23.gray(paths.globalLearningsFile)}`);
3993
+ console.log(` Global index: ${chalk23.gray(paths.globalIndexFile)}`);
3994
+ console.log();
3995
+ return 0;
3996
+ }
3997
+ /**
3998
+ * Format relevance/effectiveness score with color
3999
+ */
4000
+ formatScore(score) {
4001
+ if (score >= 80) return chalk23.green(score.toString());
4002
+ if (score >= 50) return chalk23.yellow(score.toString());
4003
+ return chalk23.red(score.toString());
4004
+ }
4005
+ /**
4006
+ * Format matched by info
4007
+ */
4008
+ formatMatchedBy(matchedBy) {
4009
+ const parts = [];
4010
+ if (matchedBy.frameworks.length > 0) {
4011
+ parts.push(`frameworks: ${matchedBy.frameworks.join(", ")}`);
4012
+ }
4013
+ if (matchedBy.tags.length > 0) {
4014
+ parts.push(`tags: ${matchedBy.tags.join(", ")}`);
4015
+ }
4016
+ if (matchedBy.keywords.length > 0) {
4017
+ parts.push(`keywords: ${matchedBy.keywords.slice(0, 3).join(", ")}`);
4018
+ }
4019
+ if (matchedBy.patterns.length > 0) {
4020
+ parts.push(`patterns: ${matchedBy.patterns.join(", ")}`);
4021
+ }
4022
+ return parts.join(" | ") || "general";
4023
+ }
4024
+ /**
4025
+ * Convert title to slug
4026
+ */
4027
+ slugify(text) {
4028
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 50);
4029
+ }
4030
+ /**
4031
+ * Escape a YAML scalar value if it contains special characters
4032
+ */
4033
+ escapeYamlValue(value) {
4034
+ if (/[:#\[\]{}|>&*!?,'"\\@`]/.test(value) || value.includes("\n")) {
4035
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
4036
+ }
4037
+ return value;
4038
+ }
4039
+ /**
4040
+ * Generate skill content from learning
4041
+ */
4042
+ generateSkillContent(learning, skillName) {
4043
+ const escapedName = this.escapeYamlValue(skillName);
4044
+ const escapedDesc = this.escapeYamlValue(learning.title);
4045
+ const escapedTags = learning.tags.map((t) => this.escapeYamlValue(t));
4046
+ const lines = [
4047
+ "---",
4048
+ `name: ${escapedName}`,
4049
+ `description: ${escapedDesc}`,
4050
+ `version: 1.0.0`,
4051
+ `tags: [${escapedTags.join(", ")}]`,
4052
+ `source: skillkit-memory`,
4053
+ `sourceType: local`
4054
+ ];
4055
+ if (learning.frameworks?.length) {
4056
+ const escapedFrameworks = learning.frameworks.map((f) => this.escapeYamlValue(f));
4057
+ lines.push(`compatibility:`);
4058
+ lines.push(` frameworks: [${escapedFrameworks.join(", ")}]`);
4059
+ }
4060
+ lines.push("---", "", `# ${learning.title}`, "");
4061
+ if (learning.patterns?.length) {
4062
+ lines.push(`## Patterns`, "");
4063
+ for (const pattern of learning.patterns) {
4064
+ lines.push(`- ${pattern}`);
4065
+ }
4066
+ lines.push("");
4067
+ }
4068
+ lines.push(`## Content`, "", learning.content, "");
4069
+ lines.push(
4070
+ "---",
4071
+ "",
4072
+ "*Exported from SkillKit session memory*",
4073
+ `*Created: ${new Date(learning.createdAt).toLocaleDateString()}*`,
4074
+ `*Uses: ${learning.useCount}*`
4075
+ );
4076
+ if (learning.effectiveness !== void 0) {
4077
+ lines.push(`*Effectiveness: ${learning.effectiveness}%*`);
4078
+ }
4079
+ return lines.join("\n");
4080
+ }
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
+ };
2224
5238
  export {
5239
+ CICDCommand,
2225
5240
  ContextCommand,
2226
5241
  CreateCommand,
2227
5242
  DisableCommand,
@@ -2229,14 +5244,27 @@ export {
2229
5244
  InitCommand,
2230
5245
  InstallCommand,
2231
5246
  ListCommand,
5247
+ MarketplaceCommand,
5248
+ MemoryCommand,
5249
+ PauseCommand,
5250
+ PluginCommand,
2232
5251
  ReadCommand,
2233
5252
  RecommendCommand,
2234
5253
  RemoveCommand,
5254
+ ResumeCommand,
5255
+ RunCommand,
5256
+ SettingsCommand,
5257
+ StatusCommand,
2235
5258
  SyncCommand,
5259
+ TeamCommand,
5260
+ TestCommand,
2236
5261
  TranslateCommand,
2237
5262
  UICommand,
2238
5263
  UpdateCommand,
2239
5264
  ValidateCommand,
5265
+ WorkflowCreateCommand,
5266
+ WorkflowListCommand,
5267
+ WorkflowRunCommand,
2240
5268
  getAgentConfigPath,
2241
5269
  getInstallDir,
2242
5270
  getSearchDirs,