@skillkit/cli 1.3.0 → 1.4.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,219 +2018,2028 @@ 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"]
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;
2117
+ }
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
+ } from "@skillkit/core";
2361
+ var WorkflowRunCommand = class extends Command18 {
2362
+ static paths = [["workflow", "run"], ["wf", "run"]];
2363
+ static usage = Command18.Usage({
2364
+ description: "Execute a skill workflow",
2365
+ details: `
2366
+ The workflow run command executes a workflow definition,
2367
+ running skills in waves with parallel or sequential execution.
2368
+
2369
+ Workflows are defined in YAML files in .skillkit/workflows/
2370
+ `,
2371
+ examples: [
2372
+ ["Run a workflow by name", "$0 workflow run setup-project"],
2373
+ ["Run a workflow from file", "$0 workflow run --file my-workflow.yaml"],
2374
+ ["Dry run (no execution)", "$0 workflow run setup-project --dry-run"]
2375
+ ]
2376
+ });
2377
+ // Workflow name
2378
+ workflowName = Option17.String({ required: false });
2379
+ // Workflow file path
2380
+ file = Option17.String("--file,-f", {
2381
+ description: "Path to workflow YAML file"
2382
+ });
2383
+ // Dry run
2384
+ dryRun = Option17.Boolean("--dry-run,-n", false, {
2385
+ description: "Show what would be executed without running"
2386
+ });
2387
+ // Verbose output
2388
+ verbose = Option17.Boolean("--verbose,-v", false, {
2389
+ description: "Show detailed execution progress"
2390
+ });
2391
+ // Continue on error
2392
+ continueOnError = Option17.Boolean("--continue-on-error", false, {
2393
+ description: "Continue execution even if a skill fails"
2394
+ });
2395
+ // JSON output
2396
+ json = Option17.Boolean("--json,-j", false, {
2397
+ description: "Output in JSON format"
2398
+ });
2399
+ // Project path
2400
+ projectPath = Option17.String("--path,-p", {
2401
+ description: "Project path (default: current directory)"
2402
+ });
2403
+ async execute() {
2404
+ const targetPath = resolve6(this.projectPath || process.cwd());
2405
+ let workflow;
2406
+ try {
2407
+ if (this.file) {
2408
+ workflow = loadWorkflow(resolve6(this.file));
2409
+ } else if (this.workflowName) {
2410
+ workflow = loadWorkflowByName(targetPath, this.workflowName);
2411
+ if (!workflow) {
2412
+ console.error(chalk17.red(`Workflow "${this.workflowName}" not found`));
2413
+ console.log(chalk17.dim("List available workflows: skillkit workflow list"));
2414
+ return 1;
2415
+ }
2416
+ } else {
2417
+ console.error(chalk17.red("Please specify a workflow name or --file"));
2418
+ return 1;
2419
+ }
2420
+ } catch (error) {
2421
+ console.error(chalk17.red(`Failed to load workflow: ${error}`));
2422
+ return 1;
2423
+ }
2424
+ const validation = validateWorkflow(workflow);
2425
+ if (!validation.valid) {
2426
+ console.error(chalk17.red("Invalid workflow:"));
2427
+ for (const error of validation.errors) {
2428
+ console.error(chalk17.red(` \u2022 ${error}`));
2429
+ }
2430
+ return 1;
2431
+ }
2432
+ if (this.dryRun) {
2433
+ this.showDryRun(workflow);
2434
+ return 0;
2435
+ }
2436
+ console.log(chalk17.cyan(`Executing workflow: ${chalk17.bold(workflow.name)}`));
2437
+ if (workflow.description) {
2438
+ console.log(chalk17.dim(workflow.description));
2439
+ }
2440
+ console.log();
2441
+ const spinner = ora4();
2442
+ let currentWave = -1;
2443
+ const orchestrator = createWorkflowOrchestrator(
2444
+ async (_skillName, _config) => {
2445
+ await new Promise((resolve11) => setTimeout(resolve11, 500));
2446
+ return { success: true };
2216
2447
  },
2217
- popularity: 650,
2218
- quality: 84,
2219
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2220
- verified: false
2448
+ (event) => {
2449
+ if (this.json) return;
2450
+ switch (event.type) {
2451
+ case "wave_start":
2452
+ currentWave = event.waveIndex || 0;
2453
+ spinner.start(`Wave ${currentWave + 1}: ${event.waveName || "Executing..."}`);
2454
+ break;
2455
+ case "skill_start":
2456
+ if (this.verbose) {
2457
+ spinner.text = `Wave ${currentWave + 1}: Running ${event.skillName}...`;
2458
+ }
2459
+ break;
2460
+ case "skill_complete":
2461
+ if (this.verbose) {
2462
+ const icon = event.status === "completed" ? chalk17.green("\u2713") : chalk17.red("\u2717");
2463
+ console.log(` ${icon} ${event.skillName}`);
2464
+ }
2465
+ break;
2466
+ case "wave_complete":
2467
+ const waveIcon = event.status === "completed" ? chalk17.green("\u2713") : chalk17.red("\u2717");
2468
+ spinner.stopAndPersist({
2469
+ symbol: waveIcon,
2470
+ text: `Wave ${(event.waveIndex || 0) + 1}: ${event.waveName || "Complete"}`
2471
+ });
2472
+ break;
2473
+ case "workflow_complete":
2474
+ console.log();
2475
+ if (event.status === "completed") {
2476
+ console.log(chalk17.green("\u2713 Workflow completed successfully"));
2477
+ } else {
2478
+ console.log(chalk17.red(`\u2717 Workflow ${event.status}`));
2479
+ if (event.error) {
2480
+ console.log(chalk17.red(` Error: ${event.error}`));
2481
+ }
2482
+ }
2483
+ break;
2484
+ }
2485
+ }
2486
+ );
2487
+ const execution = await orchestrator.execute(workflow);
2488
+ if (this.json) {
2489
+ console.log(JSON.stringify(execution, null, 2));
2221
2490
  }
2222
- ];
2223
- }
2491
+ return execution.status === "completed" ? 0 : 1;
2492
+ }
2493
+ showDryRun(workflow) {
2494
+ console.log(chalk17.cyan("Dry Run - Workflow Execution Plan"));
2495
+ console.log();
2496
+ console.log(`Workflow: ${chalk17.bold(workflow.name)}`);
2497
+ if (workflow.description) {
2498
+ console.log(`Description: ${chalk17.dim(workflow.description)}`);
2499
+ }
2500
+ console.log();
2501
+ for (let i = 0; i < workflow.waves.length; i++) {
2502
+ const wave = workflow.waves[i];
2503
+ const modeLabel = wave.parallel ? chalk17.blue("[parallel]") : chalk17.yellow("[sequential]");
2504
+ console.log(`${chalk17.cyan(`Wave ${i + 1}`)}: ${wave.name || "Unnamed"} ${modeLabel}`);
2505
+ for (const skill of wave.skills) {
2506
+ const skillName = typeof skill === "string" ? skill : skill.skill;
2507
+ console.log(` \u2022 ${skillName}`);
2508
+ }
2509
+ console.log();
2510
+ }
2511
+ console.log(chalk17.dim("This is a dry run. No skills were executed."));
2512
+ console.log(chalk17.dim("Remove --dry-run to execute the workflow."));
2513
+ }
2514
+ };
2515
+
2516
+ // src/commands/workflow/list.ts
2517
+ import { Command as Command19, Option as Option18 } from "clipanion";
2518
+ import { resolve as resolve7 } from "path";
2519
+ import chalk18 from "chalk";
2520
+ import { listWorkflows } from "@skillkit/core";
2521
+ var WorkflowListCommand = class extends Command19 {
2522
+ static paths = [["workflow", "list"], ["wf", "list"], ["workflow", "ls"], ["wf", "ls"]];
2523
+ static usage = Command19.Usage({
2524
+ description: "List available workflows",
2525
+ details: `
2526
+ The workflow list command shows all available workflows
2527
+ defined in .skillkit/workflows/
2528
+ `,
2529
+ examples: [
2530
+ ["List all workflows", "$0 workflow list"],
2531
+ ["List with details", "$0 workflow list --verbose"]
2532
+ ]
2533
+ });
2534
+ // Verbose output
2535
+ verbose = Option18.Boolean("--verbose,-v", false, {
2536
+ description: "Show detailed workflow information"
2537
+ });
2538
+ // JSON output
2539
+ json = Option18.Boolean("--json,-j", false, {
2540
+ description: "Output in JSON format"
2541
+ });
2542
+ // Project path
2543
+ projectPath = Option18.String("--path,-p", {
2544
+ description: "Project path (default: current directory)"
2545
+ });
2546
+ async execute() {
2547
+ const targetPath = resolve7(this.projectPath || process.cwd());
2548
+ let workflows;
2549
+ try {
2550
+ workflows = listWorkflows(targetPath);
2551
+ } catch (err) {
2552
+ console.log(chalk18.red("Failed to list workflows."));
2553
+ console.log(chalk18.dim(String(err)));
2554
+ return 1;
2555
+ }
2556
+ if (this.json) {
2557
+ console.log(JSON.stringify(workflows, null, 2));
2558
+ return 0;
2559
+ }
2560
+ if (workflows.length === 0) {
2561
+ console.log(chalk18.yellow("No workflows found."));
2562
+ console.log(chalk18.dim("Create a workflow with: skillkit workflow create"));
2563
+ console.log(chalk18.dim("Or add YAML files to .skillkit/workflows/"));
2564
+ return 0;
2565
+ }
2566
+ console.log(chalk18.cyan(`Available Workflows (${workflows.length}):
2567
+ `));
2568
+ for (const workflow of workflows) {
2569
+ console.log(` ${chalk18.bold(workflow.name)}`);
2570
+ if (workflow.description) {
2571
+ console.log(` ${chalk18.dim(workflow.description)}`);
2572
+ }
2573
+ if (this.verbose) {
2574
+ console.log(` Version: ${chalk18.dim(workflow.version || "N/A")}`);
2575
+ console.log(` Waves: ${chalk18.dim(workflow.waves.length.toString())}`);
2576
+ const totalSkills = workflow.waves.reduce(
2577
+ (sum, wave) => sum + wave.skills.length,
2578
+ 0
2579
+ );
2580
+ console.log(` Total Skills: ${chalk18.dim(totalSkills.toString())}`);
2581
+ if (workflow.tags && workflow.tags.length > 0) {
2582
+ console.log(` Tags: ${chalk18.dim(workflow.tags.join(", "))}`);
2583
+ }
2584
+ }
2585
+ console.log();
2586
+ }
2587
+ console.log(chalk18.dim("Run a workflow with: skillkit workflow run <name>"));
2588
+ return 0;
2589
+ }
2590
+ };
2591
+
2592
+ // src/commands/workflow/create.ts
2593
+ import { Command as Command20, Option as Option19 } from "clipanion";
2594
+ import { resolve as resolve8 } from "path";
2595
+ import chalk19 from "chalk";
2596
+ import { createWorkflowTemplate, saveWorkflow, serializeWorkflow } from "@skillkit/core";
2597
+ var WorkflowCreateCommand = class extends Command20 {
2598
+ static paths = [["workflow", "create"], ["wf", "create"], ["workflow", "new"], ["wf", "new"]];
2599
+ static usage = Command20.Usage({
2600
+ description: "Create a new workflow",
2601
+ details: `
2602
+ The workflow create command creates a new workflow template
2603
+ that you can customize with your skill composition.
2604
+ `,
2605
+ examples: [
2606
+ ["Create a new workflow", "$0 workflow create my-workflow"],
2607
+ ["Create with description", '$0 workflow create setup-project --description "Project setup workflow"'],
2608
+ ["Print to stdout", "$0 workflow create my-workflow --stdout"]
2609
+ ]
2610
+ });
2611
+ // Workflow name
2612
+ workflowName = Option19.String({ required: true });
2613
+ // Description
2614
+ description = Option19.String("--description,-d", {
2615
+ description: "Workflow description"
2616
+ });
2617
+ // Print to stdout instead of saving
2618
+ stdout = Option19.Boolean("--stdout", false, {
2619
+ description: "Print workflow YAML to stdout instead of saving"
2620
+ });
2621
+ // Project path
2622
+ projectPath = Option19.String("--path,-p", {
2623
+ description: "Project path (default: current directory)"
2624
+ });
2625
+ async execute() {
2626
+ const targetPath = resolve8(this.projectPath || process.cwd());
2627
+ const workflow = createWorkflowTemplate(this.workflowName, this.description);
2628
+ if (this.stdout) {
2629
+ console.log(serializeWorkflow(workflow));
2630
+ return 0;
2631
+ }
2632
+ const filePath = saveWorkflow(targetPath, workflow);
2633
+ console.log(chalk19.green(`\u2713 Created workflow: ${chalk19.bold(this.workflowName)}`));
2634
+ console.log(chalk19.dim(` File: ${filePath}`));
2635
+ console.log();
2636
+ console.log(chalk19.dim("Edit the workflow file to add skills and configure waves."));
2637
+ console.log(chalk19.dim("Run with: skillkit workflow run " + this.workflowName));
2638
+ return 0;
2639
+ }
2640
+ };
2641
+
2642
+ // src/commands/run.ts
2643
+ import { Command as Command21, Option as Option20 } from "clipanion";
2644
+ import { resolve as resolve9, join as join6 } from "path";
2645
+ import { existsSync as existsSync9, readFileSync as readFileSync4, statSync } from "fs";
2646
+ import chalk20 from "chalk";
2647
+ import ora5 from "ora";
2648
+ import {
2649
+ createExecutionEngine,
2650
+ discoverSkills,
2651
+ extractFrontmatter
2652
+ } from "@skillkit/core";
2653
+ var RunCommand = class extends Command21 {
2654
+ static paths = [["run"]];
2655
+ static usage = Command21.Usage({
2656
+ description: "Execute a skill with task-based orchestration",
2657
+ details: `
2658
+ The run command executes a skill, optionally breaking it down into
2659
+ tasks with verification checkpoints.
2660
+
2661
+ Skills can be:
2662
+ - Installed skills (by name)
2663
+ - Local skill files (by path)
2664
+ - Remote skills (owner/repo/path)
2665
+ `,
2666
+ examples: [
2667
+ ["Run an installed skill", "$0 run typescript-strict-mode"],
2668
+ ["Run a local skill file", "$0 run ./my-skill/SKILL.md"],
2669
+ ["Dry run (show what would happen)", "$0 run typescript-strict-mode --dry-run"],
2670
+ ["Run with verification", "$0 run setup-testing --verify"]
2671
+ ]
2672
+ });
2673
+ // Skill name or path
2674
+ skillRef = Option20.String({ required: true });
2675
+ // Target agent
2676
+ agent = Option20.String("--agent,-a", {
2677
+ description: "Target agent (claude-code, cursor, etc.)"
2678
+ });
2679
+ // Dry run
2680
+ dryRun = Option20.Boolean("--dry-run,-n", false, {
2681
+ description: "Show what would be executed without running"
2682
+ });
2683
+ // Enable verification
2684
+ verify = Option20.Boolean("--verify", false, {
2685
+ description: "Run verification checks after each task"
2686
+ });
2687
+ // Auto-commit
2688
+ autoCommit = Option20.Boolean("--auto-commit", false, {
2689
+ description: "Create git commits after each task"
2690
+ });
2691
+ // Continue on error
2692
+ continueOnError = Option20.Boolean("--continue-on-error", false, {
2693
+ description: "Continue execution even if a task fails"
2694
+ });
2695
+ // Verbose output
2696
+ verbose = Option20.Boolean("--verbose,-v", false, {
2697
+ description: "Show detailed execution progress"
2698
+ });
2699
+ // JSON output
2700
+ json = Option20.Boolean("--json,-j", false, {
2701
+ description: "Output in JSON format"
2702
+ });
2703
+ // Project path
2704
+ projectPath = Option20.String("--path,-p", {
2705
+ description: "Project path (default: current directory)"
2706
+ });
2707
+ async execute() {
2708
+ const targetPath = resolve9(this.projectPath || process.cwd());
2709
+ const skill = await this.loadSkill(targetPath);
2710
+ if (!skill) {
2711
+ return 1;
2712
+ }
2713
+ if (!this.json) {
2714
+ console.log(chalk20.cyan(`Executing skill: ${chalk20.bold(skill.name)}`));
2715
+ if (skill.description) {
2716
+ console.log(chalk20.dim(skill.description));
2717
+ }
2718
+ console.log();
2719
+ }
2720
+ if (this.dryRun) {
2721
+ this.showDryRun(skill);
2722
+ return 0;
2723
+ }
2724
+ const spinner = ora5();
2725
+ let currentTask = "";
2726
+ const engine = createExecutionEngine(targetPath, {
2727
+ onProgress: (event) => {
2728
+ if (this.json) return;
2729
+ switch (event.type) {
2730
+ case "task_start":
2731
+ currentTask = event.taskName || "";
2732
+ spinner.start(`Task ${(event.taskIndex || 0) + 1}/${event.totalTasks}: ${currentTask}`);
2733
+ break;
2734
+ case "task_complete":
2735
+ const icon = event.status === "completed" ? chalk20.green("\u2713") : chalk20.red("\u2717");
2736
+ spinner.stopAndPersist({
2737
+ symbol: icon,
2738
+ text: `Task ${(event.taskIndex || 0) + 1}/${event.totalTasks}: ${currentTask}`
2739
+ });
2740
+ if (event.error && this.verbose) {
2741
+ console.log(chalk20.red(` Error: ${event.error}`));
2742
+ }
2743
+ break;
2744
+ case "checkpoint":
2745
+ spinner.info(`Checkpoint: ${event.message}`);
2746
+ break;
2747
+ case "verification":
2748
+ if (this.verbose) {
2749
+ console.log(chalk20.dim(` ${event.message}`));
2750
+ }
2751
+ break;
2752
+ case "complete":
2753
+ console.log();
2754
+ if (event.status === "completed") {
2755
+ console.log(chalk20.green("\u2713 Skill execution completed"));
2756
+ } else {
2757
+ console.log(chalk20.red(`\u2717 Skill execution ${event.status}`));
2758
+ if (event.error) {
2759
+ console.log(chalk20.red(` Error: ${event.error}`));
2760
+ }
2761
+ }
2762
+ break;
2763
+ }
2764
+ },
2765
+ checkpointHandler: async (task, _context) => {
2766
+ if (task.type === "checkpoint:decision" && task.options) {
2767
+ return {
2768
+ continue: true,
2769
+ selectedOption: task.options[0]
2770
+ };
2771
+ }
2772
+ return { continue: true };
2773
+ }
2774
+ });
2775
+ const result = await engine.execute(skill, {
2776
+ agent: this.agent,
2777
+ autoCommit: this.autoCommit,
2778
+ verify: this.verify,
2779
+ continueOnError: this.continueOnError
2780
+ });
2781
+ if (this.json) {
2782
+ console.log(JSON.stringify(result, null, 2));
2783
+ } else {
2784
+ console.log();
2785
+ console.log(chalk20.cyan("Summary:"));
2786
+ console.log(` Duration: ${chalk20.dim(this.formatDuration(result.durationMs || 0))}`);
2787
+ console.log(` Tasks: ${chalk20.dim(`${result.tasks.filter((t) => t.status === "completed").length}/${result.tasks.length} completed`)}`);
2788
+ if (result.filesModified.length > 0) {
2789
+ console.log(` Files modified: ${chalk20.dim(result.filesModified.length.toString())}`);
2790
+ }
2791
+ if (result.commits.length > 0) {
2792
+ console.log(` Commits: ${chalk20.dim(result.commits.join(", "))}`);
2793
+ }
2794
+ }
2795
+ return result.status === "completed" ? 0 : 1;
2796
+ }
2797
+ async loadSkill(projectPath) {
2798
+ if (this.skillRef.endsWith(".md") || existsSync9(this.skillRef) && statSync(this.skillRef).isFile()) {
2799
+ return this.loadSkillFromFile(resolve9(this.skillRef));
2800
+ }
2801
+ const skill = this.findInstalledSkill(projectPath, this.skillRef);
2802
+ if (skill) {
2803
+ return skill;
2804
+ }
2805
+ console.error(chalk20.red(`Skill "${this.skillRef}" not found`));
2806
+ console.log(chalk20.dim("Install skills with: skillkit install <source>"));
2807
+ return null;
2808
+ }
2809
+ loadSkillFromFile(filePath) {
2810
+ if (!existsSync9(filePath)) {
2811
+ console.error(chalk20.red(`File not found: ${filePath}`));
2812
+ return null;
2813
+ }
2814
+ try {
2815
+ const content = readFileSync4(filePath, "utf-8");
2816
+ const frontmatter = extractFrontmatter(content);
2817
+ const tasks = this.parseTasksFromFrontmatter(frontmatter);
2818
+ return {
2819
+ name: frontmatter?.name || this.skillRef,
2820
+ description: frontmatter?.description,
2821
+ version: frontmatter?.version,
2822
+ source: filePath,
2823
+ content,
2824
+ tasks
2825
+ };
2826
+ } catch (error) {
2827
+ console.error(chalk20.red(`Failed to load skill: ${error}`));
2828
+ return null;
2829
+ }
2830
+ }
2831
+ findInstalledSkill(projectPath, skillName) {
2832
+ const skillDirs = [
2833
+ join6(projectPath, ".claude", "skills"),
2834
+ join6(projectPath, ".cursor", "skills"),
2835
+ join6(projectPath, "skills"),
2836
+ join6(projectPath, ".skillkit", "skills")
2837
+ ];
2838
+ for (const dir of skillDirs) {
2839
+ if (!existsSync9(dir)) continue;
2840
+ const skills = discoverSkills(dir);
2841
+ const skill = skills.find((s) => s.name === skillName);
2842
+ if (skill) {
2843
+ const skillMdPath = join6(skill.path, "SKILL.md");
2844
+ if (existsSync9(skillMdPath)) {
2845
+ return this.loadSkillFromFile(skillMdPath);
2846
+ }
2847
+ }
2848
+ }
2849
+ return null;
2850
+ }
2851
+ parseTasksFromFrontmatter(frontmatter) {
2852
+ if (!frontmatter?.tasks || !Array.isArray(frontmatter.tasks)) {
2853
+ return void 0;
2854
+ }
2855
+ return frontmatter.tasks.map((task, index) => ({
2856
+ id: task.id || `task-${index}`,
2857
+ name: task.name || `Task ${index + 1}`,
2858
+ type: task.type || "auto",
2859
+ action: task.action || "",
2860
+ files: task.files,
2861
+ options: task.options,
2862
+ verify: task.verify
2863
+ }));
2864
+ }
2865
+ showDryRun(skill) {
2866
+ console.log(chalk20.cyan("Dry Run - Execution Plan"));
2867
+ console.log();
2868
+ console.log(`Skill: ${chalk20.bold(skill.name)}`);
2869
+ if (skill.description) {
2870
+ console.log(`Description: ${chalk20.dim(skill.description)}`);
2871
+ }
2872
+ console.log(`Source: ${chalk20.dim(skill.source)}`);
2873
+ console.log();
2874
+ if (skill.tasks && skill.tasks.length > 0) {
2875
+ console.log(chalk20.cyan("Tasks:"));
2876
+ for (let i = 0; i < skill.tasks.length; i++) {
2877
+ const task = skill.tasks[i];
2878
+ const typeLabel = this.getTaskTypeLabel(task.type);
2879
+ console.log(` ${i + 1}. ${task.name} ${typeLabel}`);
2880
+ if (task.action) {
2881
+ console.log(` ${chalk20.dim(task.action)}`);
2882
+ }
2883
+ if (task.files && task.files.length > 0) {
2884
+ console.log(` Files: ${chalk20.dim(task.files.join(", "))}`);
2885
+ }
2886
+ }
2887
+ } else {
2888
+ console.log(chalk20.dim("No structured tasks defined. Skill will be executed as a single unit."));
2889
+ }
2890
+ console.log();
2891
+ console.log(chalk20.dim("This is a dry run. Remove --dry-run to execute."));
2892
+ }
2893
+ getTaskTypeLabel(type) {
2894
+ switch (type) {
2895
+ case "auto":
2896
+ return chalk20.green("[auto]");
2897
+ case "checkpoint:human-verify":
2898
+ return chalk20.yellow("[verify]");
2899
+ case "checkpoint:decision":
2900
+ return chalk20.blue("[decision]");
2901
+ case "checkpoint:human-action":
2902
+ return chalk20.magenta("[manual]");
2903
+ default:
2904
+ return "";
2905
+ }
2906
+ }
2907
+ formatDuration(ms) {
2908
+ if (ms < 1e3) return `${ms}ms`;
2909
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2910
+ const minutes = Math.floor(ms / 6e4);
2911
+ const seconds = Math.floor(ms % 6e4 / 1e3);
2912
+ return `${minutes}m ${seconds}s`;
2913
+ }
2914
+ };
2915
+
2916
+ // src/commands/test.ts
2917
+ import { Command as Command22, Option as Option21 } from "clipanion";
2918
+ import { resolve as resolve10, join as join7 } from "path";
2919
+ import { existsSync as existsSync10, readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
2920
+ import chalk21 from "chalk";
2921
+ import {
2922
+ runTestSuite,
2923
+ createTestSuiteFromFrontmatter
2924
+ } from "@skillkit/core";
2925
+ import { parse as parseYaml } from "yaml";
2926
+ var TestCommand = class extends Command22 {
2927
+ static paths = [["test"]];
2928
+ static usage = Command22.Usage({
2929
+ description: "Run skill tests",
2930
+ details: `
2931
+ The test command runs test cases defined in skill frontmatter.
2932
+
2933
+ Tests are defined in the skill's YAML frontmatter using the 'tests' key.
2934
+
2935
+ Example skill with tests:
2936
+ \`\`\`yaml
2937
+ ---
2938
+ name: my-skill
2939
+ tests:
2940
+ - name: "Creates config file"
2941
+ assertions:
2942
+ - type: file_exists
2943
+ target: config.json
2944
+ - name: "Types check"
2945
+ assertions:
2946
+ - type: type_check
2947
+ ---
2948
+ \`\`\`
2949
+ `,
2950
+ examples: [
2951
+ ["Run all skill tests", "$0 test"],
2952
+ ["Run tests for a specific skill", "$0 test my-skill"],
2953
+ ["Run with verbose output", "$0 test --verbose"],
2954
+ ["Stop on first failure", "$0 test --bail"],
2955
+ ["Run tests with specific tags", "$0 test --tags unit,integration"]
2956
+ ]
2957
+ });
2958
+ skill = Option21.String({ required: false });
2959
+ verbose = Option21.Boolean("--verbose,-v", false, {
2960
+ description: "Verbose output"
2961
+ });
2962
+ bail = Option21.Boolean("--bail,-b", false, {
2963
+ description: "Stop on first failure"
2964
+ });
2965
+ tags = Option21.String("--tags,-t", {
2966
+ description: "Only run tests with these tags (comma-separated)"
2967
+ });
2968
+ skipTags = Option21.String("--skip-tags", {
2969
+ description: "Skip tests with these tags (comma-separated)"
2970
+ });
2971
+ json = Option21.Boolean("--json,-j", false, {
2972
+ description: "Output results as JSON"
2973
+ });
2974
+ projectPath = Option21.String("--path,-p", {
2975
+ description: "Project path"
2976
+ });
2977
+ timeout = Option21.String("--timeout", {
2978
+ description: "Test timeout in milliseconds"
2979
+ });
2980
+ async execute() {
2981
+ const targetPath = resolve10(this.projectPath || process.cwd());
2982
+ const skillFiles = this.findSkillFiles(targetPath);
2983
+ if (skillFiles.length === 0) {
2984
+ if (!this.json) {
2985
+ console.log(chalk21.yellow("No skills found with tests."));
2986
+ console.log(chalk21.dim("Add tests to your skills using YAML frontmatter."));
2987
+ } else {
2988
+ console.log(JSON.stringify({ results: [], passed: true, total: 0 }));
2989
+ }
2990
+ return 0;
2991
+ }
2992
+ const filesToTest = this.skill ? skillFiles.filter((f) => f.name.includes(this.skill)) : skillFiles;
2993
+ if (filesToTest.length === 0) {
2994
+ if (!this.json) {
2995
+ console.log(chalk21.yellow(`No skills found matching "${this.skill}"`));
2996
+ }
2997
+ return 1;
2998
+ }
2999
+ if (!this.json) {
3000
+ console.log(chalk21.bold("Running skill tests...\n"));
3001
+ }
3002
+ const tags = this.tags?.split(",").map((t) => t.trim());
3003
+ const skipTags = this.skipTags?.split(",").map((t) => t.trim());
3004
+ const timeout = this.timeout ? parseInt(this.timeout, 10) : void 0;
3005
+ const results = [];
3006
+ let allPassed = true;
3007
+ for (const file of filesToTest) {
3008
+ const suite = this.parseSkillTests(file.path, file.name);
3009
+ if (!suite || suite.tests.length === 0) {
3010
+ continue;
3011
+ }
3012
+ if (!this.json) {
3013
+ console.log(chalk21.blue(`Testing: ${suite.skillName}`));
3014
+ }
3015
+ const result = await runTestSuite(suite, {
3016
+ cwd: targetPath,
3017
+ verbose: this.verbose,
3018
+ bail: this.bail,
3019
+ tags,
3020
+ skipTags,
3021
+ timeout,
3022
+ onProgress: (event) => {
3023
+ if (this.json || !this.verbose) return;
3024
+ switch (event.type) {
3025
+ case "test_start":
3026
+ console.log(chalk21.dim(` Running: ${event.testName}`));
3027
+ break;
3028
+ case "test_end":
3029
+ if (event.passed) {
3030
+ console.log(chalk21.green(` \u2713 ${event.testName}`));
3031
+ } else {
3032
+ console.log(chalk21.red(` \u2717 ${event.testName}`));
3033
+ if (event.error) {
3034
+ console.log(chalk21.red(` ${event.error}`));
3035
+ }
3036
+ }
3037
+ break;
3038
+ case "assertion_end":
3039
+ if (this.verbose && !event.passed) {
3040
+ console.log(chalk21.red(` - ${event.assertionType}: ${event.error}`));
3041
+ }
3042
+ break;
3043
+ }
3044
+ }
3045
+ });
3046
+ results.push(result);
3047
+ if (!result.passed) {
3048
+ allPassed = false;
3049
+ }
3050
+ if (!this.json) {
3051
+ const icon = result.passed ? chalk21.green("\u2713") : chalk21.red("\u2717");
3052
+ const status = result.passed ? "PASSED" : "FAILED";
3053
+ console.log(
3054
+ `${icon} ${suite.skillName}: ${result.passedCount}/${result.tests.length} tests ${status} (${result.duration}ms)
3055
+ `
3056
+ );
3057
+ }
3058
+ if (this.bail && !result.passed) {
3059
+ break;
3060
+ }
3061
+ }
3062
+ if (this.json) {
3063
+ console.log(
3064
+ JSON.stringify({
3065
+ results: results.map((r) => ({
3066
+ skill: r.skillName,
3067
+ passed: r.passed,
3068
+ tests: r.passedCount,
3069
+ failed: r.failedCount,
3070
+ skipped: r.skippedCount,
3071
+ duration: r.duration
3072
+ })),
3073
+ passed: allPassed,
3074
+ total: results.length
3075
+ })
3076
+ );
3077
+ } else {
3078
+ const totalTests = results.reduce((acc, r) => acc + r.tests.length, 0);
3079
+ const passedTests = results.reduce((acc, r) => acc + r.passedCount, 0);
3080
+ const failedTests = results.reduce((acc, r) => acc + r.failedCount, 0);
3081
+ const skippedTests = results.reduce((acc, r) => acc + r.skippedCount, 0);
3082
+ console.log(chalk21.bold("Summary:"));
3083
+ console.log(` Skills tested: ${results.length}`);
3084
+ console.log(` Total tests: ${totalTests}`);
3085
+ console.log(chalk21.green(` Passed: ${passedTests}`));
3086
+ if (failedTests > 0) {
3087
+ console.log(chalk21.red(` Failed: ${failedTests}`));
3088
+ }
3089
+ if (skippedTests > 0) {
3090
+ console.log(chalk21.yellow(` Skipped: ${skippedTests}`));
3091
+ }
3092
+ if (allPassed) {
3093
+ console.log(chalk21.green("\n\u2713 All tests passed!"));
3094
+ } else {
3095
+ console.log(chalk21.red("\n\u2717 Some tests failed."));
3096
+ }
3097
+ }
3098
+ return allPassed ? 0 : 1;
3099
+ }
3100
+ /**
3101
+ * Find skill files with tests
3102
+ */
3103
+ findSkillFiles(projectPath) {
3104
+ const files = [];
3105
+ const skillDirs = [
3106
+ ".claude/skills",
3107
+ ".cursor/skills",
3108
+ ".skillkit/skills",
3109
+ "skills"
3110
+ ];
3111
+ for (const dir of skillDirs) {
3112
+ const fullDir = join7(projectPath, dir);
3113
+ if (!existsSync10(fullDir)) continue;
3114
+ try {
3115
+ const entries = readdirSync2(fullDir, { withFileTypes: true });
3116
+ for (const entry of entries) {
3117
+ if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".mdc"))) {
3118
+ files.push({
3119
+ name: entry.name.replace(/\.(md|mdc)$/, ""),
3120
+ path: join7(fullDir, entry.name)
3121
+ });
3122
+ }
3123
+ }
3124
+ } catch {
3125
+ }
3126
+ }
3127
+ return files;
3128
+ }
3129
+ /**
3130
+ * Parse skill file for tests
3131
+ */
3132
+ parseSkillTests(filePath, skillName) {
3133
+ try {
3134
+ const content = readFileSync5(filePath, "utf-8");
3135
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
3136
+ if (!frontmatterMatch) {
3137
+ return null;
3138
+ }
3139
+ const frontmatter = parseYaml(frontmatterMatch[1]);
3140
+ if (!frontmatter.tests) {
3141
+ return null;
3142
+ }
3143
+ return createTestSuiteFromFrontmatter(skillName, frontmatter);
3144
+ } catch {
3145
+ return null;
3146
+ }
3147
+ }
3148
+ };
3149
+
3150
+ // src/commands/marketplace.ts
3151
+ import { Command as Command23, Option as Option22 } from "clipanion";
3152
+ import chalk22 from "chalk";
3153
+ import ora6 from "ora";
3154
+ import {
3155
+ createMarketplaceAggregator
3156
+ } from "@skillkit/core";
3157
+ var MarketplaceCommand = class extends Command23 {
3158
+ static paths = [["marketplace"], ["market"], ["mp"]];
3159
+ static usage = Command23.Usage({
3160
+ description: "Browse and install skills from the marketplace",
3161
+ details: `
3162
+ The marketplace command lets you discover and install skills
3163
+ from curated repositories.
3164
+
3165
+ Skills are aggregated from multiple sources including:
3166
+ - composioHQ/awesome-claude-code-skills
3167
+ - anthropics/courses
3168
+ - User-added custom sources
3169
+ `,
3170
+ examples: [
3171
+ ["Browse marketplace", "$0 marketplace"],
3172
+ ["Search for skills", "$0 marketplace search typescript"],
3173
+ ["Refresh marketplace index", "$0 marketplace refresh"],
3174
+ ["Show popular tags", "$0 marketplace tags"],
3175
+ ["List sources", "$0 marketplace sources"]
3176
+ ]
3177
+ });
3178
+ action = Option22.String({ required: false });
3179
+ query = Option22.String({ required: false });
3180
+ limit = Option22.String("--limit,-l", {
3181
+ description: "Limit results"
3182
+ });
3183
+ tags = Option22.String("--tags,-t", {
3184
+ description: "Filter by tags (comma-separated)"
3185
+ });
3186
+ source = Option22.String("--source,-s", {
3187
+ description: "Filter by source"
3188
+ });
3189
+ json = Option22.Boolean("--json,-j", false, {
3190
+ description: "Output as JSON"
3191
+ });
3192
+ async execute() {
3193
+ const marketplace = createMarketplaceAggregator();
3194
+ switch (this.action) {
3195
+ case "search":
3196
+ return this.searchSkills(marketplace);
3197
+ case "refresh":
3198
+ return this.refreshIndex(marketplace);
3199
+ case "tags":
3200
+ return this.showTags(marketplace);
3201
+ case "sources":
3202
+ return this.showSources(marketplace);
3203
+ default:
3204
+ if (this.query || this.action) {
3205
+ return this.searchSkills(marketplace);
3206
+ }
3207
+ return this.browseMarketplace(marketplace);
3208
+ }
3209
+ }
3210
+ async browseMarketplace(marketplace) {
3211
+ const spinner = ora6("Loading marketplace...").start();
3212
+ try {
3213
+ const index = await marketplace.getIndex();
3214
+ spinner.stop();
3215
+ if (this.json) {
3216
+ console.log(JSON.stringify({
3217
+ totalSkills: index.totalCount,
3218
+ sources: index.sources.length,
3219
+ updatedAt: index.updatedAt
3220
+ }));
3221
+ return 0;
3222
+ }
3223
+ console.log(chalk22.bold("Skill Marketplace\n"));
3224
+ console.log(`Total skills: ${chalk22.cyan(index.totalCount)}`);
3225
+ console.log(`Sources: ${chalk22.cyan(index.sources.length)}`);
3226
+ console.log(`Last updated: ${chalk22.dim(new Date(index.updatedAt).toLocaleString())}
3227
+ `);
3228
+ console.log(chalk22.bold("Featured Skills:\n"));
3229
+ const featured = index.skills.slice(0, 10);
3230
+ for (const skill of featured) {
3231
+ console.log(` ${chalk22.cyan(skill.name)}`);
3232
+ if (skill.description) {
3233
+ console.log(` ${chalk22.dim(skill.description.slice(0, 60))}${skill.description.length > 60 ? "..." : ""}`);
3234
+ }
3235
+ console.log(` ${chalk22.dim(`Source: ${skill.source.name}`)}`);
3236
+ console.log();
3237
+ }
3238
+ console.log(chalk22.dim('Use "skillkit marketplace search <query>" to search for skills.'));
3239
+ console.log(chalk22.dim('Use "skillkit marketplace tags" to see popular tags.'));
3240
+ return 0;
3241
+ } catch (error) {
3242
+ spinner.fail("Failed to load marketplace");
3243
+ console.error(chalk22.red(error instanceof Error ? error.message : String(error)));
3244
+ return 1;
3245
+ }
3246
+ }
3247
+ async searchSkills(marketplace) {
3248
+ const query = this.query || this.action;
3249
+ let limit = 20;
3250
+ if (this.limit) {
3251
+ const parsed = parseInt(this.limit, 10);
3252
+ if (isNaN(parsed) || parsed <= 0) {
3253
+ console.error(chalk22.red("Invalid --limit value. Must be a positive number."));
3254
+ return 1;
3255
+ }
3256
+ limit = parsed;
3257
+ }
3258
+ const tags = this.tags?.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
3259
+ const spinner = ora6(`Searching for "${query}"...`).start();
3260
+ try {
3261
+ const options = {
3262
+ query: query || void 0,
3263
+ limit,
3264
+ tags: tags && tags.length > 0 ? tags : void 0,
3265
+ source: this.source
3266
+ };
3267
+ const result = await marketplace.search(options);
3268
+ spinner.stop();
3269
+ if (this.json) {
3270
+ console.log(JSON.stringify(result));
3271
+ return 0;
3272
+ }
3273
+ if (result.skills.length === 0) {
3274
+ console.log(chalk22.yellow(`No skills found${query ? ` matching "${query}"` : ""}.`));
3275
+ return 0;
3276
+ }
3277
+ console.log(chalk22.bold(`Found ${result.total} skill(s):
3278
+ `));
3279
+ for (const skill of result.skills) {
3280
+ console.log(`${chalk22.cyan(skill.name)} ${chalk22.dim(`(${skill.source.name})`)}`);
3281
+ if (skill.description) {
3282
+ console.log(` ${skill.description}`);
3283
+ }
3284
+ if (skill.tags.length > 0) {
3285
+ console.log(` Tags: ${chalk22.dim(skill.tags.join(", "))}`);
3286
+ }
3287
+ console.log();
3288
+ }
3289
+ if (result.total > result.skills.length) {
3290
+ console.log(chalk22.dim(`Showing ${result.skills.length} of ${result.total} results. Use --limit to see more.`));
3291
+ }
3292
+ return 0;
3293
+ } catch (error) {
3294
+ spinner.fail("Search failed");
3295
+ console.error(chalk22.red(error instanceof Error ? error.message : String(error)));
3296
+ return 1;
3297
+ }
3298
+ }
3299
+ async refreshIndex(marketplace) {
3300
+ const spinner = ora6("Refreshing marketplace index...").start();
3301
+ try {
3302
+ const index = await marketplace.refresh();
3303
+ spinner.succeed(`Marketplace refreshed: ${index.totalCount} skills from ${index.sources.length} sources`);
3304
+ return 0;
3305
+ } catch (error) {
3306
+ spinner.fail("Refresh failed");
3307
+ console.error(chalk22.red(error instanceof Error ? error.message : String(error)));
3308
+ return 1;
3309
+ }
3310
+ }
3311
+ async showTags(marketplace) {
3312
+ const spinner = ora6("Loading tags...").start();
3313
+ try {
3314
+ const tags = await marketplace.getPopularTags(30);
3315
+ spinner.stop();
3316
+ if (this.json) {
3317
+ console.log(JSON.stringify(tags));
3318
+ return 0;
3319
+ }
3320
+ console.log(chalk22.bold("Popular Tags:\n"));
3321
+ for (const { tag, count } of tags) {
3322
+ const bar = "\u2588".repeat(Math.min(count, 20));
3323
+ console.log(` ${tag.padEnd(15)} ${chalk22.cyan(bar)} ${count}`);
3324
+ }
3325
+ return 0;
3326
+ } catch (error) {
3327
+ spinner.fail("Failed to load tags");
3328
+ console.error(chalk22.red(error instanceof Error ? error.message : String(error)));
3329
+ return 1;
3330
+ }
3331
+ }
3332
+ async showSources(marketplace) {
3333
+ const sources = marketplace.getSources();
3334
+ if (this.json) {
3335
+ console.log(JSON.stringify(sources));
3336
+ return 0;
3337
+ }
3338
+ console.log(chalk22.bold("Skill Sources:\n"));
3339
+ for (const source of sources) {
3340
+ console.log(`${chalk22.cyan(source.name)} ${source.official ? chalk22.green("(official)") : ""}`);
3341
+ console.log(` ${chalk22.dim(`${source.owner}/${source.repo}`)}`);
3342
+ if (source.description) {
3343
+ console.log(` ${source.description}`);
3344
+ }
3345
+ console.log();
3346
+ }
3347
+ return 0;
3348
+ }
3349
+ };
3350
+
3351
+ // src/commands/memory.ts
3352
+ import { Command as Command24, Option as Option23 } from "clipanion";
3353
+ import chalk23 from "chalk";
3354
+ import {
3355
+ ObservationStore,
3356
+ LearningStore,
3357
+ getMemoryPaths,
3358
+ initializeMemoryDirectory,
3359
+ getMemoryStatus,
3360
+ createMemoryCompressor,
3361
+ createMemoryInjector
3362
+ } from "@skillkit/core";
3363
+ var MemoryCommand = class extends Command24 {
3364
+ static paths = [["memory"], ["mem"]];
3365
+ static usage = Command24.Usage({
3366
+ description: "Manage session memory across AI coding agents",
3367
+ details: `
3368
+ The memory command helps you view, search, and manage learnings
3369
+ captured from coding sessions across all AI agents.
3370
+
3371
+ Subcommands:
3372
+ - status: Show current memory status
3373
+ - search: Search memories by query
3374
+ - list: List all learnings
3375
+ - show: Show a specific learning
3376
+ - compress: Compress observations into learnings
3377
+ - export: Export a learning as a skill
3378
+ - import: Import memories from another project
3379
+ - clear: Clear session observations
3380
+ - add: Manually add a learning
3381
+ - rate: Rate a learning's effectiveness
3382
+ - config: Configure memory settings
3383
+ `,
3384
+ examples: [
3385
+ ["Show memory status", "$0 memory status"],
3386
+ ["Search memories", '$0 memory search "authentication"'],
3387
+ ["Search global memories", '$0 memory search --global "react hooks"'],
3388
+ ["List project learnings", "$0 memory list"],
3389
+ ["List global learnings", "$0 memory list --global"],
3390
+ ["Show a learning", "$0 memory show <id>"],
3391
+ ["Compress observations", "$0 memory compress"],
3392
+ ["Export as skill", "$0 memory export <id> --name my-skill"],
3393
+ ["Clear session", "$0 memory clear"],
3394
+ ["Add manual learning", '$0 memory add --title "..." --content "..."'],
3395
+ ["Rate effectiveness", "$0 memory rate <id> 85"]
3396
+ ]
3397
+ });
3398
+ // Subcommand (status, search, list, show, compress, export, import, clear, add, rate, config)
3399
+ action = Option23.String({ required: false });
3400
+ // Second argument (query for search, id for show/export/rate)
3401
+ arg = Option23.String({ required: false });
3402
+ // Third argument (rating value for rate command)
3403
+ ratingArg = Option23.String({ required: false });
3404
+ // Global scope
3405
+ global = Option23.Boolean("--global,-g", false, {
3406
+ description: "Use global memories instead of project"
3407
+ });
3408
+ // Tags filter
3409
+ tags = Option23.String("--tags,-t", {
3410
+ description: "Filter by tags (comma-separated)"
3411
+ });
3412
+ // Limit results
3413
+ limit = Option23.String("--limit,-l", {
3414
+ description: "Maximum number of results"
3415
+ });
3416
+ // Title for add
3417
+ title = Option23.String("--title", {
3418
+ description: "Title for new learning"
3419
+ });
3420
+ // Content for add
3421
+ content = Option23.String("--content,-c", {
3422
+ description: "Content for new learning"
3423
+ });
3424
+ // Name for export
3425
+ name = Option23.String("--name,-n", {
3426
+ description: "Name for exported skill"
3427
+ });
3428
+ // Output file
3429
+ output = Option23.String("--output,-o", {
3430
+ description: "Output file path"
3431
+ });
3432
+ // Input file
3433
+ input = Option23.String("--input,-i", {
3434
+ description: "Input file path"
3435
+ });
3436
+ // Keep learnings when clearing
3437
+ keepLearnings = Option23.Boolean("--keep-learnings", false, {
3438
+ description: "Keep learnings when clearing"
3439
+ });
3440
+ // Dry run
3441
+ dryRun = Option23.Boolean("--dry-run", false, {
3442
+ description: "Preview without making changes"
3443
+ });
3444
+ // JSON output
3445
+ json = Option23.Boolean("--json,-j", false, {
3446
+ description: "Output in JSON format"
3447
+ });
3448
+ // Verbose output
3449
+ verbose = Option23.Boolean("--verbose,-v", false, {
3450
+ description: "Show detailed output"
3451
+ });
3452
+ async execute() {
3453
+ const action = this.action || "status";
3454
+ switch (action) {
3455
+ case "status":
3456
+ return this.showStatus();
3457
+ case "search":
3458
+ return this.searchMemories();
3459
+ case "list":
3460
+ return this.listLearnings();
3461
+ case "show":
3462
+ return this.showLearning();
3463
+ case "compress":
3464
+ return this.compressObservations();
3465
+ case "export":
3466
+ return this.exportLearning();
3467
+ case "import":
3468
+ return this.importMemories();
3469
+ case "clear":
3470
+ return this.clearMemory();
3471
+ case "add":
3472
+ return this.addLearning();
3473
+ case "rate":
3474
+ return this.rateLearning();
3475
+ case "config":
3476
+ return this.showConfig();
3477
+ default:
3478
+ console.error(chalk23.red(`Unknown action: ${action}`));
3479
+ console.log(chalk23.gray("Available actions: status, search, list, show, compress, export, import, clear, add, rate, config"));
3480
+ return 1;
3481
+ }
3482
+ }
3483
+ /**
3484
+ * Show memory status
3485
+ */
3486
+ async showStatus() {
3487
+ const projectPath = process.cwd();
3488
+ const status = getMemoryStatus(projectPath);
3489
+ const paths = getMemoryPaths(projectPath);
3490
+ const observationStore = new ObservationStore(projectPath);
3491
+ const projectLearningStore = new LearningStore("project", projectPath);
3492
+ const globalLearningStore = new LearningStore("global");
3493
+ const sessionObservations = status.hasObservations ? observationStore.count() : 0;
3494
+ const projectLearnings = status.hasLearnings ? projectLearningStore.count() : 0;
3495
+ const globalLearnings = status.hasGlobalLearnings ? globalLearningStore.count() : 0;
3496
+ const sessionId = status.hasObservations ? observationStore.getSessionId() : null;
3497
+ if (this.json) {
3498
+ console.log(JSON.stringify({
3499
+ ...status,
3500
+ sessionObservations,
3501
+ projectLearnings,
3502
+ globalLearnings,
3503
+ sessionId,
3504
+ paths
3505
+ }, null, 2));
3506
+ return 0;
3507
+ }
3508
+ console.log(chalk23.bold("\nMemory Status\n"));
3509
+ console.log(chalk23.cyan("Session:"));
3510
+ console.log(` Observations: ${sessionObservations}`);
3511
+ if (sessionId) {
3512
+ console.log(` Session ID: ${chalk23.gray(sessionId.slice(0, 8))}`);
3513
+ }
3514
+ console.log();
3515
+ console.log(chalk23.cyan("Project:"));
3516
+ console.log(` Learnings: ${projectLearnings}`);
3517
+ console.log(` Path: ${chalk23.gray(status.projectMemoryExists ? paths.projectMemoryDir : "Not initialized")}`);
3518
+ console.log();
3519
+ console.log(chalk23.cyan("Global:"));
3520
+ console.log(` Learnings: ${globalLearnings}`);
3521
+ console.log(` Path: ${chalk23.gray(status.globalMemoryExists ? paths.globalMemoryDir : "Not initialized")}`);
3522
+ console.log();
3523
+ if (sessionObservations >= 50) {
3524
+ console.log(chalk23.yellow("\u{1F4A1} You have many uncompressed observations. Consider running:"));
3525
+ console.log(chalk23.gray(" skillkit memory compress"));
3526
+ }
3527
+ return 0;
3528
+ }
3529
+ /**
3530
+ * Search memories
3531
+ */
3532
+ async searchMemories() {
3533
+ const query = this.arg;
3534
+ if (!query) {
3535
+ console.error(chalk23.red("Error: Search query is required"));
3536
+ console.log(chalk23.gray('Usage: skillkit memory search "your query"'));
3537
+ return 1;
3538
+ }
3539
+ const projectPath = process.cwd();
3540
+ const injector = createMemoryInjector(projectPath);
3541
+ let maxLearnings = 10;
3542
+ if (this.limit) {
3543
+ const parsed = parseInt(this.limit, 10);
3544
+ if (isNaN(parsed) || parsed <= 0) {
3545
+ console.log(chalk23.red("Invalid --limit value. Must be a positive number."));
3546
+ return 1;
3547
+ }
3548
+ maxLearnings = parsed;
3549
+ }
3550
+ const tags = this.tags?.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
3551
+ const results = injector.search(query, {
3552
+ includeGlobal: this.global,
3553
+ tags: tags && tags.length > 0 ? tags : void 0,
3554
+ maxLearnings,
3555
+ minRelevance: 0
3556
+ });
3557
+ if (this.json) {
3558
+ console.log(JSON.stringify(results, null, 2));
3559
+ return 0;
3560
+ }
3561
+ if (results.length === 0) {
3562
+ console.log(chalk23.yellow(`No memories found for: "${query}"`));
3563
+ return 0;
3564
+ }
3565
+ console.log(chalk23.bold(`
3566
+ Found ${results.length} memories:
3567
+ `));
3568
+ for (const { learning, relevanceScore, matchedBy } of results) {
3569
+ console.log(`${chalk23.cyan("\u25CF")} ${chalk23.bold(learning.title)}`);
3570
+ console.log(` ID: ${chalk23.gray(learning.id.slice(0, 8))}`);
3571
+ console.log(` Relevance: ${this.formatScore(relevanceScore)}%`);
3572
+ console.log(` Tags: ${learning.tags.join(", ")}`);
3573
+ console.log(` Scope: ${learning.scope}`);
3574
+ if (this.verbose) {
3575
+ console.log(` Matched: ${this.formatMatchedBy(matchedBy)}`);
3576
+ console.log(` Created: ${new Date(learning.createdAt).toLocaleDateString()}`);
3577
+ console.log(` Uses: ${learning.useCount}`);
3578
+ }
3579
+ const excerpt = learning.content.slice(0, 100);
3580
+ console.log(` ${chalk23.gray(excerpt)}${learning.content.length > 100 ? "..." : ""}`);
3581
+ console.log();
3582
+ }
3583
+ return 0;
3584
+ }
3585
+ /**
3586
+ * List all learnings
3587
+ */
3588
+ async listLearnings() {
3589
+ const projectPath = process.cwd();
3590
+ const store = new LearningStore(
3591
+ this.global ? "global" : "project",
3592
+ this.global ? void 0 : projectPath
3593
+ );
3594
+ let learnings = store.getAll();
3595
+ if (this.tags) {
3596
+ const tagList = this.tags.split(",").map((t) => t.trim().toLowerCase());
3597
+ learnings = learnings.filter(
3598
+ (l) => l.tags.some((t) => tagList.includes(t.toLowerCase()))
3599
+ );
3600
+ }
3601
+ const limit = this.limit ? parseInt(this.limit, 10) : learnings.length;
3602
+ learnings = learnings.slice(0, limit);
3603
+ if (this.json) {
3604
+ console.log(JSON.stringify(learnings, null, 2));
3605
+ return 0;
3606
+ }
3607
+ const scope = this.global ? "Global" : "Project";
3608
+ console.log(chalk23.bold(`
3609
+ ${scope} Learnings (${learnings.length}):
3610
+ `));
3611
+ if (learnings.length === 0) {
3612
+ console.log(chalk23.gray("No learnings found."));
3613
+ console.log(chalk23.gray("\nCapture learnings by running skills or add manually:"));
3614
+ console.log(chalk23.gray(' skillkit memory add --title "..." --content "..."'));
3615
+ return 0;
3616
+ }
3617
+ for (const learning of learnings) {
3618
+ const effectiveness = learning.effectiveness !== void 0 ? ` [${this.formatScore(learning.effectiveness)}%]` : "";
3619
+ console.log(`${chalk23.cyan("\u25CF")} ${learning.title}${chalk23.green(effectiveness)}`);
3620
+ console.log(` ${chalk23.gray(learning.id.slice(0, 8))} | ${learning.tags.join(", ")} | ${learning.useCount} uses`);
3621
+ if (this.verbose) {
3622
+ const excerpt = learning.content.slice(0, 80);
3623
+ console.log(` ${chalk23.gray(excerpt)}${learning.content.length > 80 ? "..." : ""}`);
3624
+ }
3625
+ }
3626
+ console.log();
3627
+ return 0;
3628
+ }
3629
+ /**
3630
+ * Show a specific learning
3631
+ */
3632
+ async showLearning() {
3633
+ const id = this.arg;
3634
+ if (!id) {
3635
+ console.error(chalk23.red("Error: Learning ID is required"));
3636
+ console.log(chalk23.gray("Usage: skillkit memory show <id>"));
3637
+ return 1;
3638
+ }
3639
+ const projectPath = process.cwd();
3640
+ let learning = new LearningStore("project", projectPath).getById(id);
3641
+ if (!learning) {
3642
+ learning = new LearningStore("global").getById(id);
3643
+ }
3644
+ if (!learning) {
3645
+ const projectLearnings = new LearningStore("project", projectPath).getAll();
3646
+ const globalLearnings = new LearningStore("global").getAll();
3647
+ const all = [...projectLearnings, ...globalLearnings];
3648
+ learning = all.find((l) => l.id.startsWith(id));
3649
+ }
3650
+ if (!learning) {
3651
+ console.error(chalk23.red(`Learning not found: ${id}`));
3652
+ return 1;
3653
+ }
3654
+ if (this.json) {
3655
+ console.log(JSON.stringify(learning, null, 2));
3656
+ return 0;
3657
+ }
3658
+ console.log(chalk23.bold(`
3659
+ ${learning.title}
3660
+ `));
3661
+ console.log(chalk23.gray(`ID: ${learning.id}`));
3662
+ console.log(chalk23.gray(`Scope: ${learning.scope}`));
3663
+ console.log(chalk23.gray(`Source: ${learning.source}`));
3664
+ console.log(chalk23.gray(`Tags: ${learning.tags.join(", ")}`));
3665
+ if (learning.frameworks?.length) {
3666
+ console.log(chalk23.gray(`Frameworks: ${learning.frameworks.join(", ")}`));
3667
+ }
3668
+ if (learning.patterns?.length) {
3669
+ console.log(chalk23.gray(`Patterns: ${learning.patterns.join(", ")}`));
3670
+ }
3671
+ console.log(chalk23.gray(`Created: ${new Date(learning.createdAt).toLocaleString()}`));
3672
+ console.log(chalk23.gray(`Updated: ${new Date(learning.updatedAt).toLocaleString()}`));
3673
+ console.log(chalk23.gray(`Uses: ${learning.useCount}`));
3674
+ if (learning.effectiveness !== void 0) {
3675
+ console.log(chalk23.gray(`Effectiveness: ${this.formatScore(learning.effectiveness)}%`));
3676
+ }
3677
+ console.log(chalk23.bold("\nContent:\n"));
3678
+ console.log(learning.content);
3679
+ console.log();
3680
+ return 0;
3681
+ }
3682
+ /**
3683
+ * Compress observations into learnings
3684
+ */
3685
+ async compressObservations() {
3686
+ const projectPath = process.cwd();
3687
+ initializeMemoryDirectory(projectPath);
3688
+ const observationStore = new ObservationStore(projectPath);
3689
+ const observations = observationStore.getAll();
3690
+ if (observations.length === 0) {
3691
+ console.log(chalk23.yellow("No observations to compress."));
3692
+ return 0;
3693
+ }
3694
+ console.log(chalk23.cyan(`Found ${observations.length} observations to compress...
3695
+ `));
3696
+ const compressor = createMemoryCompressor(projectPath);
3697
+ const compressionOptions = {
3698
+ minObservations: 2,
3699
+ additionalTags: this.tags?.split(",").map((t) => t.trim())
3700
+ };
3701
+ if (this.dryRun) {
3702
+ console.log(chalk23.gray("(Dry run - no changes will be made)\n"));
3703
+ const result2 = await compressor.compress(observations, compressionOptions);
3704
+ console.log(chalk23.green(`\u2713 Would compress ${result2.stats.inputCount} observations into ${result2.stats.outputCount} learnings
3705
+ `));
3706
+ if (result2.learnings.length > 0) {
3707
+ console.log(chalk23.bold("Learnings that would be created:"));
3708
+ for (const learning of result2.learnings) {
3709
+ console.log(` ${chalk23.cyan("\u25CF")} ${learning.title}`);
3710
+ console.log(` Tags: ${learning.tags.join(", ")}`);
3711
+ }
3712
+ console.log();
3713
+ }
3714
+ return 0;
3715
+ }
3716
+ const { learnings, result } = await compressor.compressAndStore(observations, compressionOptions);
3717
+ console.log(chalk23.green(`\u2713 Compressed ${result.stats.inputCount} observations into ${result.stats.outputCount} learnings
3718
+ `));
3719
+ if (learnings.length > 0) {
3720
+ console.log(chalk23.bold("New Learnings:"));
3721
+ for (const learning of learnings) {
3722
+ console.log(` ${chalk23.cyan("\u25CF")} ${learning.title}`);
3723
+ console.log(` Tags: ${learning.tags.join(", ")}`);
3724
+ }
3725
+ console.log();
3726
+ }
3727
+ if (result.processedObservationIds.length > 0) {
3728
+ const deleted = observationStore.deleteMany(result.processedObservationIds);
3729
+ console.log(chalk23.gray(`Cleared ${deleted} processed observations.`));
3730
+ }
3731
+ return 0;
3732
+ }
3733
+ /**
3734
+ * Export a learning as a skill
3735
+ */
3736
+ async exportLearning() {
3737
+ const id = this.arg;
3738
+ if (!id) {
3739
+ console.error(chalk23.red("Error: Learning ID is required"));
3740
+ console.log(chalk23.gray("Usage: skillkit memory export <id> --name my-skill"));
3741
+ return 1;
3742
+ }
3743
+ const projectPath = process.cwd();
3744
+ let learning = new LearningStore("project", projectPath).getById(id);
3745
+ if (!learning) {
3746
+ learning = new LearningStore("global").getById(id);
3747
+ }
3748
+ if (!learning) {
3749
+ const projectLearnings = new LearningStore("project", projectPath).getAll();
3750
+ const globalLearnings = new LearningStore("global").getAll();
3751
+ const all = [...projectLearnings, ...globalLearnings];
3752
+ learning = all.find((l) => l.id.startsWith(id));
3753
+ }
3754
+ if (!learning) {
3755
+ console.error(chalk23.red(`Learning not found: ${id}`));
3756
+ return 1;
3757
+ }
3758
+ const skillName = this.name || this.slugify(learning.title);
3759
+ const skillContent = this.generateSkillContent(learning, skillName);
3760
+ if (this.dryRun) {
3761
+ console.log(chalk23.gray("(Dry run preview)\n"));
3762
+ console.log(skillContent);
3763
+ return 0;
3764
+ }
3765
+ const outputPath = this.output || `.skillkit/exports/${skillName}/SKILL.md`;
3766
+ const { dirname: dirname3 } = await import("path");
3767
+ const { existsSync: existsSync11, mkdirSync: mkdirSync5, writeFileSync: writeFileSync5 } = await import("fs");
3768
+ const outputDir = dirname3(outputPath);
3769
+ if (!existsSync11(outputDir)) {
3770
+ mkdirSync5(outputDir, { recursive: true });
3771
+ }
3772
+ writeFileSync5(outputPath, skillContent, "utf-8");
3773
+ console.log(chalk23.green(`\u2713 Exported learning as skill: ${skillName}`));
3774
+ console.log(chalk23.gray(` Path: ${outputPath}`));
3775
+ return 0;
3776
+ }
3777
+ /**
3778
+ * Import memories from another project
3779
+ */
3780
+ async importMemories() {
3781
+ const inputPath = this.input || this.arg;
3782
+ if (!inputPath) {
3783
+ console.error(chalk23.red("Error: Input path is required"));
3784
+ console.log(chalk23.gray("Usage: skillkit memory import --input <path>"));
3785
+ return 1;
3786
+ }
3787
+ const { existsSync: existsSync11, readFileSync: readFileSync6 } = await import("fs");
3788
+ const { resolve: resolve11 } = await import("path");
3789
+ const fullPath = resolve11(inputPath);
3790
+ if (!existsSync11(fullPath)) {
3791
+ console.error(chalk23.red(`File not found: ${fullPath}`));
3792
+ return 1;
3793
+ }
3794
+ const projectPath = process.cwd();
3795
+ const store = new LearningStore(
3796
+ this.global ? "global" : "project",
3797
+ this.global ? void 0 : projectPath
3798
+ );
3799
+ try {
3800
+ const content = readFileSync6(fullPath, "utf-8");
3801
+ const { parse: parseYaml2 } = await import("yaml");
3802
+ const data = parseYaml2(content);
3803
+ if (!data.learnings || !Array.isArray(data.learnings)) {
3804
+ console.error(chalk23.red("Invalid memory file format"));
3805
+ return 1;
3806
+ }
3807
+ let imported = 0;
3808
+ for (const learning of data.learnings) {
3809
+ if (this.dryRun) {
3810
+ console.log(chalk23.gray(`Would import: ${learning.title}`));
3811
+ } else {
3812
+ store.add({
3813
+ source: "imported",
3814
+ title: learning.title,
3815
+ content: learning.content,
3816
+ // Default to empty arrays if not present in imported YAML
3817
+ tags: Array.isArray(learning.tags) ? learning.tags : [],
3818
+ frameworks: Array.isArray(learning.frameworks) ? learning.frameworks : [],
3819
+ patterns: Array.isArray(learning.patterns) ? learning.patterns : []
3820
+ });
3821
+ imported++;
3822
+ }
3823
+ }
3824
+ if (this.dryRun) {
3825
+ console.log(chalk23.gray(`
3826
+ (Dry run - ${data.learnings.length} learnings would be imported)`));
3827
+ } else {
3828
+ console.log(chalk23.green(`\u2713 Imported ${imported} learnings`));
3829
+ }
3830
+ return 0;
3831
+ } catch (error) {
3832
+ console.error(chalk23.red(`Import failed: ${error}`));
3833
+ return 1;
3834
+ }
3835
+ }
3836
+ /**
3837
+ * Clear session observations
3838
+ */
3839
+ async clearMemory() {
3840
+ const projectPath = process.cwd();
3841
+ if (this.dryRun) {
3842
+ const observationStore2 = new ObservationStore(projectPath);
3843
+ const learningStore = new LearningStore("project", projectPath);
3844
+ console.log(chalk23.gray("(Dry run preview)\n"));
3845
+ console.log(`Would clear ${observationStore2.count()} observations`);
3846
+ if (!this.keepLearnings) {
3847
+ console.log(`Would clear ${learningStore.count()} learnings`);
3848
+ }
3849
+ return 0;
3850
+ }
3851
+ const observationStore = new ObservationStore(projectPath);
3852
+ observationStore.clear();
3853
+ console.log(chalk23.green("\u2713 Cleared session observations"));
3854
+ if (!this.keepLearnings) {
3855
+ const learningStore = new LearningStore("project", projectPath);
3856
+ learningStore.clear();
3857
+ console.log(chalk23.green("\u2713 Cleared project learnings"));
3858
+ }
3859
+ return 0;
3860
+ }
3861
+ /**
3862
+ * Add a manual learning
3863
+ */
3864
+ async addLearning() {
3865
+ if (!this.title) {
3866
+ console.error(chalk23.red("Error: --title is required"));
3867
+ console.log(chalk23.gray('Usage: skillkit memory add --title "..." --content "..."'));
3868
+ return 1;
3869
+ }
3870
+ if (!this.content) {
3871
+ console.error(chalk23.red("Error: --content is required"));
3872
+ console.log(chalk23.gray('Usage: skillkit memory add --title "..." --content "..."'));
3873
+ return 1;
3874
+ }
3875
+ const projectPath = process.cwd();
3876
+ const store = new LearningStore(
3877
+ this.global ? "global" : "project",
3878
+ this.global ? void 0 : projectPath
3879
+ );
3880
+ const tags = this.tags?.split(",").map((t) => t.trim()) || [];
3881
+ const learning = store.add({
3882
+ source: "manual",
3883
+ title: this.title,
3884
+ content: this.content,
3885
+ tags
3886
+ });
3887
+ console.log(chalk23.green(`\u2713 Added learning: ${learning.title}`));
3888
+ console.log(chalk23.gray(` ID: ${learning.id}`));
3889
+ return 0;
3890
+ }
3891
+ /**
3892
+ * Rate a learning's effectiveness
3893
+ */
3894
+ async rateLearning() {
3895
+ const id = this.arg;
3896
+ if (!id) {
3897
+ console.error(chalk23.red("Error: Learning ID is required"));
3898
+ console.log(chalk23.gray("Usage: skillkit memory rate <id> <rating>"));
3899
+ return 1;
3900
+ }
3901
+ const rating = parseInt(this.ratingArg || "0", 10);
3902
+ if (isNaN(rating) || rating < 0 || rating > 100) {
3903
+ console.error(chalk23.red("Error: Rating must be 0-100"));
3904
+ console.log(chalk23.gray("Usage: skillkit memory rate <id> <rating>"));
3905
+ return 1;
3906
+ }
3907
+ const projectPath = process.cwd();
3908
+ const projectStore = new LearningStore("project", projectPath);
3909
+ let learning = projectStore.getById(id);
3910
+ let store = projectStore;
3911
+ if (!learning) {
3912
+ const globalStore = new LearningStore("global");
3913
+ learning = globalStore.getById(id);
3914
+ store = globalStore;
3915
+ }
3916
+ if (!learning) {
3917
+ const projectLearnings = projectStore.getAll();
3918
+ const globalStore = new LearningStore("global");
3919
+ const globalLearnings = globalStore.getAll();
3920
+ learning = projectLearnings.find((l) => l.id.startsWith(id));
3921
+ if (learning) {
3922
+ store = projectStore;
3923
+ } else {
3924
+ learning = globalLearnings.find((l) => l.id.startsWith(id));
3925
+ if (learning) {
3926
+ store = globalStore;
3927
+ }
3928
+ }
3929
+ }
3930
+ if (!learning) {
3931
+ console.error(chalk23.red(`Learning not found: ${id}`));
3932
+ return 1;
3933
+ }
3934
+ store.setEffectiveness(learning.id, rating);
3935
+ console.log(chalk23.green(`\u2713 Rated "${learning.title}" as ${rating}% effective`));
3936
+ return 0;
3937
+ }
3938
+ /**
3939
+ * Show memory configuration
3940
+ */
3941
+ async showConfig() {
3942
+ const projectPath = process.cwd();
3943
+ const paths = getMemoryPaths(projectPath);
3944
+ if (this.json) {
3945
+ console.log(JSON.stringify(paths, null, 2));
3946
+ return 0;
3947
+ }
3948
+ console.log(chalk23.bold("\nMemory Configuration\n"));
3949
+ console.log(chalk23.cyan("Paths:"));
3950
+ console.log(` Project observations: ${chalk23.gray(paths.observationsFile)}`);
3951
+ console.log(` Project learnings: ${chalk23.gray(paths.learningsFile)}`);
3952
+ console.log(` Project index: ${chalk23.gray(paths.indexFile)}`);
3953
+ console.log(` Global learnings: ${chalk23.gray(paths.globalLearningsFile)}`);
3954
+ console.log(` Global index: ${chalk23.gray(paths.globalIndexFile)}`);
3955
+ console.log();
3956
+ return 0;
3957
+ }
3958
+ /**
3959
+ * Format relevance/effectiveness score with color
3960
+ */
3961
+ formatScore(score) {
3962
+ if (score >= 80) return chalk23.green(score.toString());
3963
+ if (score >= 50) return chalk23.yellow(score.toString());
3964
+ return chalk23.red(score.toString());
3965
+ }
3966
+ /**
3967
+ * Format matched by info
3968
+ */
3969
+ formatMatchedBy(matchedBy) {
3970
+ const parts = [];
3971
+ if (matchedBy.frameworks.length > 0) {
3972
+ parts.push(`frameworks: ${matchedBy.frameworks.join(", ")}`);
3973
+ }
3974
+ if (matchedBy.tags.length > 0) {
3975
+ parts.push(`tags: ${matchedBy.tags.join(", ")}`);
3976
+ }
3977
+ if (matchedBy.keywords.length > 0) {
3978
+ parts.push(`keywords: ${matchedBy.keywords.slice(0, 3).join(", ")}`);
3979
+ }
3980
+ if (matchedBy.patterns.length > 0) {
3981
+ parts.push(`patterns: ${matchedBy.patterns.join(", ")}`);
3982
+ }
3983
+ return parts.join(" | ") || "general";
3984
+ }
3985
+ /**
3986
+ * Convert title to slug
3987
+ */
3988
+ slugify(text) {
3989
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 50);
3990
+ }
3991
+ /**
3992
+ * Escape a YAML scalar value if it contains special characters
3993
+ */
3994
+ escapeYamlValue(value) {
3995
+ if (/[:#\[\]{}|>&*!?,'"\\@`]/.test(value) || value.includes("\n")) {
3996
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
3997
+ }
3998
+ return value;
3999
+ }
4000
+ /**
4001
+ * Generate skill content from learning
4002
+ */
4003
+ generateSkillContent(learning, skillName) {
4004
+ const escapedName = this.escapeYamlValue(skillName);
4005
+ const escapedDesc = this.escapeYamlValue(learning.title);
4006
+ const escapedTags = learning.tags.map((t) => this.escapeYamlValue(t));
4007
+ const lines = [
4008
+ "---",
4009
+ `name: ${escapedName}`,
4010
+ `description: ${escapedDesc}`,
4011
+ `version: 1.0.0`,
4012
+ `tags: [${escapedTags.join(", ")}]`,
4013
+ `source: skillkit-memory`,
4014
+ `sourceType: local`
4015
+ ];
4016
+ if (learning.frameworks?.length) {
4017
+ const escapedFrameworks = learning.frameworks.map((f) => this.escapeYamlValue(f));
4018
+ lines.push(`compatibility:`);
4019
+ lines.push(` frameworks: [${escapedFrameworks.join(", ")}]`);
4020
+ }
4021
+ lines.push("---", "", `# ${learning.title}`, "");
4022
+ if (learning.patterns?.length) {
4023
+ lines.push(`## Patterns`, "");
4024
+ for (const pattern of learning.patterns) {
4025
+ lines.push(`- ${pattern}`);
4026
+ }
4027
+ lines.push("");
4028
+ }
4029
+ lines.push(`## Content`, "", learning.content, "");
4030
+ lines.push(
4031
+ "---",
4032
+ "",
4033
+ "*Exported from SkillKit session memory*",
4034
+ `*Created: ${new Date(learning.createdAt).toLocaleDateString()}*`,
4035
+ `*Uses: ${learning.useCount}*`
4036
+ );
4037
+ if (learning.effectiveness !== void 0) {
4038
+ lines.push(`*Effectiveness: ${learning.effectiveness}%*`);
4039
+ }
4040
+ return lines.join("\n");
4041
+ }
4042
+ };
2224
4043
  export {
2225
4044
  ContextCommand,
2226
4045
  CreateCommand,
@@ -2229,14 +4048,24 @@ export {
2229
4048
  InitCommand,
2230
4049
  InstallCommand,
2231
4050
  ListCommand,
4051
+ MarketplaceCommand,
4052
+ MemoryCommand,
4053
+ PauseCommand,
2232
4054
  ReadCommand,
2233
4055
  RecommendCommand,
2234
4056
  RemoveCommand,
4057
+ ResumeCommand,
4058
+ RunCommand,
4059
+ StatusCommand,
2235
4060
  SyncCommand,
4061
+ TestCommand,
2236
4062
  TranslateCommand,
2237
4063
  UICommand,
2238
4064
  UpdateCommand,
2239
4065
  ValidateCommand,
4066
+ WorkflowCreateCommand,
4067
+ WorkflowListCommand,
4068
+ WorkflowRunCommand,
2240
4069
  getAgentConfigPath,
2241
4070
  getInstallDir,
2242
4071
  getSearchDirs,