@hasnatools/skills 0.1.8 → 0.1.10

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.
Files changed (2) hide show
  1. package/dist/index.js +779 -86
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20709,6 +20709,12 @@ async function getMarketplaceSkills(options) {
20709
20709
  const query = params.toString();
20710
20710
  return apiRequest(`/skills${query ? `?${query}` : ""}`);
20711
20711
  }
20712
+ async function getSkill(slug) {
20713
+ return apiRequest(`/skills/${slug}`);
20714
+ }
20715
+ async function downloadSkill(slug) {
20716
+ return apiRequest(`/skills/${slug}/download?format=json`);
20717
+ }
20712
20718
  async function installSkill(slug) {
20713
20719
  return apiRequest(`/skills/${slug}/install`);
20714
20720
  }
@@ -20916,6 +20922,7 @@ async function logoutCommand() {
20916
20922
  clearApiKey();
20917
20923
  console.log(source_default.green("✓") + " Logged out successfully");
20918
20924
  }
20925
+ var indigo = source_default.hex("#6366f1");
20919
20926
  async function whoamiCommand() {
20920
20927
  const apiKey = getApiKey();
20921
20928
  if (!apiKey) {
@@ -20932,12 +20939,18 @@ async function whoamiCommand() {
20932
20939
  }
20933
20940
  spinner.stop();
20934
20941
  console.log();
20935
- console.log(source_default.bold("Account Information"));
20942
+ console.log(indigo.bold("Account Information"));
20936
20943
  console.log();
20937
- console.log(" Email: " + source_default.cyan(result.data.email));
20938
- console.log(" Name: " + result.data.name);
20944
+ console.log(" Email: " + indigo(result.data.email));
20945
+ console.log(" Name: " + (result.data.name || source_default.dim("Not set")));
20939
20946
  console.log(" Plan: " + source_default.bold(result.data.plan));
20940
- console.log(" Credits: " + source_default.bold(result.data.creditsBalance));
20947
+ console.log();
20948
+ console.log(indigo.bold("Organization"));
20949
+ console.log();
20950
+ console.log(" Name: " + (result.data.tenantName || source_default.dim("Personal")));
20951
+ console.log(" Slug: " + indigo(result.data.tenantSlug || result.data.tenantId));
20952
+ console.log();
20953
+ console.log(source_default.dim("Run `skills credits` to check your balance"));
20941
20954
  console.log();
20942
20955
  }
20943
20956
  function sleep(ms) {
@@ -21080,9 +21093,179 @@ async function uninstallCommand(slug, options = {}) {
21080
21093
  }
21081
21094
  }
21082
21095
 
21096
+ // src/commands/download.ts
21097
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
21098
+ import { join as join4, normalize, basename, dirname, isAbsolute } from "path";
21099
+ var indigo2 = source_default.hex("#6366f1");
21100
+ function validateFilename(filename) {
21101
+ if (!filename || filename.trim() === "") {
21102
+ return null;
21103
+ }
21104
+ if (isAbsolute(filename)) {
21105
+ return null;
21106
+ }
21107
+ const normalized = normalize(filename);
21108
+ if (normalized.startsWith("..") || normalized.includes("/../") || normalized.includes("\\..\\")) {
21109
+ return null;
21110
+ }
21111
+ if (normalized.startsWith("/") || normalized.startsWith("\\")) {
21112
+ return null;
21113
+ }
21114
+ if (filename.includes("\x00")) {
21115
+ return null;
21116
+ }
21117
+ const rootFile = normalized.split(/[/\\]/)[0];
21118
+ if (rootFile.startsWith(".") && rootFile !== ".") {
21119
+ return null;
21120
+ }
21121
+ const allowedExtensions = [
21122
+ ".md",
21123
+ ".ts",
21124
+ ".js",
21125
+ ".mjs",
21126
+ ".cjs",
21127
+ ".json",
21128
+ ".yaml",
21129
+ ".yml",
21130
+ ".txt",
21131
+ ".sh",
21132
+ ".bash",
21133
+ ".zsh",
21134
+ ".py",
21135
+ ".rb",
21136
+ ".go",
21137
+ ".rs",
21138
+ ".css",
21139
+ ".html",
21140
+ ".svg",
21141
+ ".toml",
21142
+ ".env.example"
21143
+ ];
21144
+ const ext = filename.substring(filename.lastIndexOf(".")).toLowerCase();
21145
+ const hasValidExtension = allowedExtensions.some((allowed) => filename.toLowerCase().endsWith(allowed));
21146
+ const isKnownFile = ["LICENSE", "README", "CHANGELOG", "AUTHORS", "CONTRIBUTORS"].some((known) => basename(filename).toUpperCase() === known);
21147
+ if (!hasValidExtension && !isKnownFile && ext.length > 0) {
21148
+ console.log(source_default.yellow(` Skipping file with disallowed extension: ${filename}`));
21149
+ return null;
21150
+ }
21151
+ return normalized;
21152
+ }
21153
+ function validateSlug(slug) {
21154
+ const validSlugPattern = /^[a-z0-9][a-z0-9-_]*[a-z0-9]$|^[a-z0-9]$/i;
21155
+ return validSlugPattern.test(slug) && slug.length <= 64;
21156
+ }
21157
+ function safeJoin(basePath, relativePath) {
21158
+ const validated = validateFilename(relativePath);
21159
+ if (!validated) {
21160
+ return null;
21161
+ }
21162
+ const fullPath = join4(basePath, validated);
21163
+ const normalizedBase = normalize(basePath);
21164
+ const normalizedFull = normalize(fullPath);
21165
+ if (!normalizedFull.startsWith(normalizedBase)) {
21166
+ return null;
21167
+ }
21168
+ return fullPath;
21169
+ }
21170
+ async function downloadCommand(slug, options = {}) {
21171
+ if (!slug || slug.trim() === "") {
21172
+ console.log(source_default.yellow("Please provide a skill name"));
21173
+ console.log(source_default.dim("Usage: skills download <name>"));
21174
+ return;
21175
+ }
21176
+ if (!validateSlug(slug)) {
21177
+ console.log(source_default.red("Invalid skill name format"));
21178
+ console.log(source_default.dim("Skill names can only contain letters, numbers, hyphens, and underscores"));
21179
+ return;
21180
+ }
21181
+ const isLocal = options.local ?? false;
21182
+ const target = options.target ?? getDefaultTarget();
21183
+ let installDir;
21184
+ if (isLocal) {
21185
+ if (!hasLocalConfig()) {
21186
+ console.log(source_default.yellow("Not in a skills.md project"));
21187
+ console.log(source_default.dim("Run `skills init` first or download globally (default)"));
21188
+ return;
21189
+ }
21190
+ installDir = target === "claude" ? getProjectClaudeSkillsDir() : getProjectCodexSkillsDir();
21191
+ } else {
21192
+ installDir = target === "claude" ? getClaudeSkillsDir() : getCodexSkillsDir();
21193
+ }
21194
+ const spinner = ora(`Checking skill "${slug}"...`).start();
21195
+ try {
21196
+ const skillInfo = await getSkill(slug);
21197
+ if (skillInfo.error || !skillInfo.data) {
21198
+ spinner.fail("Skill not found");
21199
+ console.error(source_default.red(skillInfo.error || `Could not find skill: ${slug}`));
21200
+ return;
21201
+ }
21202
+ spinner.text = `Downloading ${skillInfo.data.name}...`;
21203
+ const result = await downloadSkill(slug);
21204
+ if (result.error || !result.data) {
21205
+ if (result.status === 404) {
21206
+ spinner.fail("Skill not available for download");
21207
+ console.log();
21208
+ console.log(source_default.yellow("This skill is not downloadable."));
21209
+ console.log(source_default.dim("Some skills are remote-execution only to protect source code."));
21210
+ console.log();
21211
+ console.log(source_default.dim("You can still use this skill via:"));
21212
+ console.log(` skills install ${slug}`);
21213
+ console.log(` skills run ${slug} -- "your prompt"`);
21214
+ } else {
21215
+ spinner.fail("Download failed");
21216
+ console.error(source_default.red(result.error || "Unknown error"));
21217
+ }
21218
+ return;
21219
+ }
21220
+ spinner.text = `Installing ${result.data.name}...`;
21221
+ const { name, version, files } = result.data;
21222
+ const skillDir = join4(installDir, `skill-${slug}`);
21223
+ const exportsDir = join4(skillDir, "exports");
21224
+ const logsDir = join4(skillDir, "logs");
21225
+ mkdirSync3(skillDir, { recursive: true });
21226
+ mkdirSync3(exportsDir, { recursive: true });
21227
+ mkdirSync3(logsDir, { recursive: true });
21228
+ let filesWritten = 0;
21229
+ let filesSkipped = 0;
21230
+ for (const [relativePath, content] of Object.entries(files)) {
21231
+ const safePath = safeJoin(skillDir, relativePath);
21232
+ if (!safePath) {
21233
+ console.log(source_default.yellow(` Skipping unsafe path: ${relativePath}`));
21234
+ filesSkipped++;
21235
+ continue;
21236
+ }
21237
+ const parentDir = dirname(safePath);
21238
+ if (!existsSync4(parentDir)) {
21239
+ mkdirSync3(parentDir, { recursive: true });
21240
+ }
21241
+ writeFileSync4(safePath, content, "utf-8");
21242
+ filesWritten++;
21243
+ }
21244
+ spinner.succeed(`Downloaded ${source_default.bold(name)} v${version}`);
21245
+ console.log();
21246
+ console.log(indigo2.bold("Download Summary"));
21247
+ console.log();
21248
+ console.log(source_default.green(" ✓") + ` Location: ${source_default.dim(skillDir)}`);
21249
+ console.log(source_default.green(" ✓") + ` Files written: ${source_default.bold(filesWritten)}`);
21250
+ if (filesSkipped > 0) {
21251
+ console.log(source_default.yellow(" ⚠") + ` Files skipped: ${source_default.bold(filesSkipped)}`);
21252
+ }
21253
+ console.log(source_default.green(" ✓") + ` Target: ${source_default.bold(target)}`);
21254
+ console.log(source_default.green(" ✓") + ` Scope: ${isLocal ? "project" : "global"}`);
21255
+ console.log();
21256
+ console.log(source_default.dim("This skill includes source code. You can:"));
21257
+ console.log(source_default.dim(" • Read and modify the code locally"));
21258
+ console.log(source_default.dim(" • Run it via remote API:") + ` skills run ${slug}`);
21259
+ console.log();
21260
+ } catch (error) {
21261
+ spinner.fail("Download failed");
21262
+ console.error(source_default.red(error instanceof Error ? error.message : "Unknown error"));
21263
+ }
21264
+ }
21265
+
21083
21266
  // src/commands/list.ts
21084
- import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync2 } from "fs";
21085
- import { join as join4 } from "path";
21267
+ import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync2 } from "fs";
21268
+ import { join as join5 } from "path";
21086
21269
  async function listCommand(options = {}) {
21087
21270
  const isGlobal = options.global ?? false;
21088
21271
  const target = options.target ?? getDefaultTarget();
@@ -21128,15 +21311,15 @@ async function listCommand(options = {}) {
21128
21311
  }
21129
21312
  const installedSkills = [];
21130
21313
  for (const dir of dirs) {
21131
- if (!existsSync4(dir))
21314
+ if (!existsSync5(dir))
21132
21315
  continue;
21133
21316
  const entries = readdirSync(dir, { withFileTypes: true });
21134
21317
  for (const entry of entries) {
21135
21318
  if (!entry.isDirectory())
21136
21319
  continue;
21137
- const skillDir = join4(dir, entry.name);
21138
- const skillMdPath = join4(skillDir, "SKILL.md");
21139
- if (!existsSync4(skillMdPath))
21320
+ const skillDir = join5(dir, entry.name);
21321
+ const skillMdPath = join5(skillDir, "SKILL.md");
21322
+ if (!existsSync5(skillMdPath))
21140
21323
  continue;
21141
21324
  const content = readFileSync2(skillMdPath, "utf-8");
21142
21325
  const frontmatter = parseFrontmatter(content);
@@ -21283,8 +21466,8 @@ function getNestedValue(obj, path6) {
21283
21466
  }
21284
21467
 
21285
21468
  // src/commands/run.ts
21286
- import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, appendFileSync } from "fs";
21287
- import { join as join5 } from "path";
21469
+ import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, appendFileSync } from "fs";
21470
+ import { join as join6 } from "path";
21288
21471
  async function runCommand(slug, options = {}) {
21289
21472
  if (!slug || slug.trim() === "") {
21290
21473
  console.log(source_default.yellow("Please provide a skill name"));
@@ -21298,8 +21481,8 @@ async function runCommand(slug, options = {}) {
21298
21481
  console.log(source_default.dim("Run `skills install " + slug + "` to install it"));
21299
21482
  return;
21300
21483
  }
21301
- const skillMdPath = join5(skillDir, "SKILL.md");
21302
- if (!existsSync5(skillMdPath)) {
21484
+ const skillMdPath = join6(skillDir, "SKILL.md");
21485
+ if (!existsSync6(skillMdPath)) {
21303
21486
  console.log(source_default.red(`Invalid skill: missing SKILL.md`));
21304
21487
  return;
21305
21488
  }
@@ -21334,16 +21517,16 @@ Stderr:`));
21334
21517
  }
21335
21518
  console.log();
21336
21519
  console.log(source_default.dim("─".repeat(60)));
21337
- const exportsDir = join5(skillDir, "exports");
21338
- const logsDir = join5(skillDir, "logs");
21339
- if (!existsSync5(exportsDir)) {
21340
- mkdirSync3(exportsDir, { recursive: true });
21520
+ const exportsDir = join6(skillDir, "exports");
21521
+ const logsDir = join6(skillDir, "logs");
21522
+ if (!existsSync6(exportsDir)) {
21523
+ mkdirSync4(exportsDir, { recursive: true });
21341
21524
  }
21342
- if (!existsSync5(logsDir)) {
21343
- mkdirSync3(logsDir, { recursive: true });
21525
+ if (!existsSync6(logsDir)) {
21526
+ mkdirSync4(logsDir, { recursive: true });
21344
21527
  }
21345
21528
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
21346
- const logFile = join5(logsDir, `log_${timestamp}.txt`);
21529
+ const logFile = join6(logsDir, `log_${timestamp}.txt`);
21347
21530
  const logContent = [
21348
21531
  `Execution ID: ${executionId}`,
21349
21532
  `Skill: ${slug}`,
@@ -21359,21 +21542,21 @@ Stderr:`));
21359
21542
  ${stderr}` : ""
21360
21543
  ].join(`
21361
21544
  `);
21362
- writeFileSync4(logFile, logContent);
21545
+ writeFileSync5(logFile, logContent);
21363
21546
  const savedExports = [];
21364
21547
  if (exportFiles && exportFiles.length > 0) {
21365
21548
  for (const file of exportFiles) {
21366
- const exportPath = join5(exportsDir, `export_${timestamp}_${file.filename}`);
21549
+ const exportPath = join6(exportsDir, `export_${timestamp}_${file.filename}`);
21367
21550
  const fileContent = Buffer.from(file.content, "base64");
21368
- writeFileSync4(exportPath, fileContent);
21551
+ writeFileSync5(exportPath, fileContent);
21369
21552
  savedExports.push(exportPath);
21370
21553
  }
21371
21554
  }
21372
21555
  if (logFiles && logFiles.length > 0) {
21373
21556
  for (const file of logFiles) {
21374
- const logPath = join5(logsDir, `server_${timestamp}_${file.filename}`);
21557
+ const logPath = join6(logsDir, `server_${timestamp}_${file.filename}`);
21375
21558
  const fileContent = Buffer.from(file.content, "base64");
21376
- writeFileSync4(logPath, fileContent);
21559
+ writeFileSync5(logPath, fileContent);
21377
21560
  }
21378
21561
  }
21379
21562
  saveExecutionHistory({
@@ -21406,7 +21589,7 @@ function saveExecutionHistory(entry) {
21406
21589
  if (!projectOutputDir) {
21407
21590
  return;
21408
21591
  }
21409
- const historyFile = join5(projectOutputDir, "execution-history.jsonl");
21592
+ const historyFile = join6(projectOutputDir, "execution-history.jsonl");
21410
21593
  try {
21411
21594
  appendFileSync(historyFile, JSON.stringify(entry) + `
21412
21595
  `);
@@ -21430,12 +21613,12 @@ function findSkillDir(slug, target) {
21430
21613
  }
21431
21614
  searchDirs.push(target === "claude" ? getClaudeSkillsDir() : getCodexSkillsDir());
21432
21615
  for (const dir of searchDirs) {
21433
- const newSkillDir = join5(dir, `skill-${slug}`);
21434
- if (existsSync5(newSkillDir)) {
21616
+ const newSkillDir = join6(dir, `skill-${slug}`);
21617
+ if (existsSync6(newSkillDir)) {
21435
21618
  return newSkillDir;
21436
21619
  }
21437
- const oldSkillDir = join5(dir, slug);
21438
- if (existsSync5(oldSkillDir)) {
21620
+ const oldSkillDir = join6(dir, slug);
21621
+ if (existsSync6(oldSkillDir)) {
21439
21622
  return oldSkillDir;
21440
21623
  }
21441
21624
  }
@@ -21499,8 +21682,8 @@ function parseSkillMd(content) {
21499
21682
 
21500
21683
  // src/commands/generate.ts
21501
21684
  import { spawn } from "child_process";
21502
- import { existsSync as existsSync6 } from "fs";
21503
- import { join as join6 } from "path";
21685
+ import { existsSync as existsSync7 } from "fs";
21686
+ import { join as join7 } from "path";
21504
21687
  import { homedir as homedir3 } from "os";
21505
21688
  var MEDIA_TYPES = ["image", "video", "audio", "text"];
21506
21689
  var LOCAL_SKILLS = {
@@ -21509,14 +21692,14 @@ var LOCAL_SKILLS = {
21509
21692
  audio: "generate-audio"
21510
21693
  };
21511
21694
  function findLocalSkill(skillName) {
21512
- const globalDir = join6(homedir3(), ".claude", "skills", skillName);
21513
- if (existsSync6(globalDir)) {
21695
+ const globalDir = join7(homedir3(), ".claude", "skills", skillName);
21696
+ if (existsSync7(globalDir)) {
21514
21697
  return globalDir;
21515
21698
  }
21516
21699
  const projectRoot = findProjectRoot();
21517
21700
  if (projectRoot) {
21518
- const projectDir = join6(projectRoot, ".claude", "skills", skillName);
21519
- if (existsSync6(projectDir)) {
21701
+ const projectDir = join7(projectRoot, ".claude", "skills", skillName);
21702
+ if (existsSync7(projectDir)) {
21520
21703
  return projectDir;
21521
21704
  }
21522
21705
  }
@@ -21524,8 +21707,8 @@ function findLocalSkill(skillName) {
21524
21707
  }
21525
21708
  async function runLocalSkill(skillDir, args) {
21526
21709
  return new Promise((resolve) => {
21527
- const skillMdPath = join6(skillDir, "SKILL.md");
21528
- if (!existsSync6(skillMdPath)) {
21710
+ const skillMdPath = join7(skillDir, "SKILL.md");
21711
+ if (!existsSync7(skillMdPath)) {
21529
21712
  resolve(false);
21530
21713
  return;
21531
21714
  }
@@ -21538,7 +21721,7 @@ async function runLocalSkill(skillDir, args) {
21538
21721
  const runScript = match[1].trim();
21539
21722
  const fullCommand = args.length > 0 ? `${runScript} ${args.join(" ")}` : runScript;
21540
21723
  const projectRoot = findProjectRoot();
21541
- const skillsOutputDir = projectRoot ? join6(projectRoot, ".skills") : null;
21724
+ const skillsOutputDir = projectRoot ? join7(projectRoot, ".skills") : null;
21542
21725
  const child = spawn(fullCommand, {
21543
21726
  cwd: skillDir,
21544
21727
  shell: true,
@@ -21602,7 +21785,7 @@ async function generateCommand(mediaType, prompt, options = {}) {
21602
21785
  console.log();
21603
21786
  const spinner = ora("Sending request...").start();
21604
21787
  try {
21605
- const response = await makeApiRequest("/api/generate", {
21788
+ const response = await makeApiRequest("/generate", {
21606
21789
  method: "POST",
21607
21790
  body: JSON.stringify({
21608
21791
  mediaType,
@@ -21656,7 +21839,7 @@ async function jobsCommand(jobId) {
21656
21839
  }
21657
21840
  const spinner = ora("Fetching jobs...").start();
21658
21841
  try {
21659
- const endpoint = jobId ? `/api/jobs/${jobId}` : "/api/jobs";
21842
+ const endpoint = jobId ? `/jobs/${jobId}` : "/jobs";
21660
21843
  const response = await makeApiRequest(endpoint);
21661
21844
  if (!response.ok) {
21662
21845
  const error = await response.json();
@@ -21730,18 +21913,122 @@ function formatStatus(status) {
21730
21913
  }
21731
21914
 
21732
21915
  // src/commands/history.ts
21733
- import { existsSync as existsSync7, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync } from "fs";
21734
- import { join as join7 } from "path";
21916
+ import { existsSync as existsSync8, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync } from "fs";
21917
+ import { join as join8 } from "path";
21918
+ var indigo3 = source_default.hex("#6366f1");
21735
21919
  async function historyCommand(options = {}) {
21920
+ if (options.local) {
21921
+ return localHistoryCommand(options);
21922
+ }
21923
+ const apiKey = getApiKey();
21924
+ if (!apiKey) {
21925
+ console.log(source_default.yellow("Not logged in - showing local history only"));
21926
+ console.log(source_default.dim("Run `skills login` to see remote execution history"));
21927
+ console.log();
21928
+ return localHistoryCommand(options);
21929
+ }
21930
+ const spinner = ora("Fetching execution history...").start();
21931
+ try {
21932
+ const params = new URLSearchParams;
21933
+ if (options.skill)
21934
+ params.set("skill", options.skill);
21935
+ if (options.limit)
21936
+ params.set("limit", String(options.limit));
21937
+ if (options.status)
21938
+ params.set("status", options.status);
21939
+ const query = params.toString();
21940
+ const response = await makeApiRequest(`/me/executions${query ? `?${query}` : ""}`);
21941
+ if (!response.ok) {
21942
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
21943
+ spinner.fail("Failed to fetch history");
21944
+ console.log(source_default.red(error.error));
21945
+ return;
21946
+ }
21947
+ const result = await response.json();
21948
+ spinner.stop();
21949
+ const { executions, pagination, counts } = result;
21950
+ console.log();
21951
+ console.log(indigo3.bold("Execution History"));
21952
+ console.log(source_default.dim(`Total: ${counts.all} | Completed: ${counts.completed} | Failed: ${counts.failed} | Pending: ${counts.pending}`));
21953
+ console.log();
21954
+ if (executions.length === 0) {
21955
+ console.log(source_default.dim("No executions found"));
21956
+ console.log(source_default.dim("Run a skill with `skills run <name>` to start tracking"));
21957
+ return;
21958
+ }
21959
+ for (const exec of executions) {
21960
+ const date = new Date(exec.createdAt);
21961
+ const dateStr = date.toLocaleDateString();
21962
+ const timeStr = date.toLocaleTimeString();
21963
+ const statusIcon = getStatusIcon(exec.status);
21964
+ const durationStr = exec.durationMs ? formatDuration2(exec.durationMs) : "-";
21965
+ console.log(`${statusIcon} ${source_default.bold(exec.skillSlug || exec.skillName || "Unknown")} ${source_default.dim(`(${dateStr} ${timeStr})`)}`);
21966
+ console.log(` ${source_default.dim("ID:")} ${exec.id.slice(0, 8)}...`);
21967
+ console.log(` ${source_default.dim("Status:")} ${formatStatus2(exec.status)}`);
21968
+ console.log(` ${source_default.dim("Duration:")} ${durationStr}`);
21969
+ if (exec.creditsUsed > 0) {
21970
+ console.log(` ${source_default.dim("Credits:")} ${indigo3(String(exec.creditsUsed))}`);
21971
+ }
21972
+ if (exec.outputType) {
21973
+ console.log(` ${source_default.dim("Output:")} ${exec.outputType}`);
21974
+ }
21975
+ if (exec.exports && exec.exports.length > 0) {
21976
+ console.log(` ${source_default.dim("Exports:")} ${exec.exports.length} file(s)`);
21977
+ if (options.verbose) {
21978
+ for (const exp of exec.exports) {
21979
+ console.log(` ${source_default.dim("→")} ${exp.filename}`);
21980
+ }
21981
+ }
21982
+ }
21983
+ if (exec.errorMessage && options.verbose) {
21984
+ console.log(` ${source_default.dim("Error:")} ${source_default.red(exec.errorMessage)}`);
21985
+ }
21986
+ console.log();
21987
+ }
21988
+ if (pagination.hasMore) {
21989
+ console.log(source_default.dim(`Showing ${executions.length} of ${pagination.total} executions`));
21990
+ console.log(source_default.dim(`Use --limit to see more`));
21991
+ }
21992
+ console.log(source_default.dim("View all at: https://skills.md/dashboard/executions"));
21993
+ } catch (error) {
21994
+ spinner.fail("Request failed");
21995
+ console.log(source_default.red(error instanceof Error ? error.message : "Unknown error"));
21996
+ }
21997
+ }
21998
+ function getStatusIcon(status) {
21999
+ switch (status) {
22000
+ case "completed":
22001
+ return source_default.green("✓");
22002
+ case "failed":
22003
+ return source_default.red("✗");
22004
+ case "pending":
22005
+ return source_default.yellow("◌");
22006
+ default:
22007
+ return source_default.dim("○");
22008
+ }
22009
+ }
22010
+ function formatStatus2(status) {
22011
+ switch (status) {
22012
+ case "completed":
22013
+ return source_default.green("completed");
22014
+ case "failed":
22015
+ return source_default.red("failed");
22016
+ case "pending":
22017
+ return source_default.yellow("pending");
22018
+ default:
22019
+ return source_default.dim(status || "unknown");
22020
+ }
22021
+ }
22022
+ async function localHistoryCommand(options) {
21736
22023
  const projectOutputDir = getProjectSkillsOutputDir();
21737
22024
  if (!projectOutputDir) {
21738
22025
  console.log(source_default.yellow("No project initialized"));
21739
22026
  console.log(source_default.dim("Run `skills init` first"));
21740
22027
  return;
21741
22028
  }
21742
- const historyFile = join7(projectOutputDir, "execution-history.jsonl");
21743
- if (!existsSync7(historyFile)) {
21744
- console.log(source_default.dim("No execution history found"));
22029
+ const historyFile = join8(projectOutputDir, "execution-history.jsonl");
22030
+ if (!existsSync8(historyFile)) {
22031
+ console.log(source_default.dim("No local execution history found"));
21745
22032
  console.log(source_default.dim("Run a skill with `skills run <name>` to start tracking"));
21746
22033
  return;
21747
22034
  }
@@ -21765,7 +22052,7 @@ async function historyCommand(options = {}) {
21765
22052
  return;
21766
22053
  }
21767
22054
  console.log();
21768
- console.log(source_default.bold("Execution History"));
22055
+ console.log(indigo3.bold("Local Execution History"));
21769
22056
  console.log(source_default.dim(`Showing ${entries.length} most recent execution(s)`));
21770
22057
  console.log();
21771
22058
  for (const entry of entries) {
@@ -21788,7 +22075,7 @@ async function historyCommand(options = {}) {
21788
22075
  }
21789
22076
  }
21790
22077
  }
21791
- if (options.verbose && existsSync7(entry.logFile)) {
22078
+ if (options.verbose && existsSync8(entry.logFile)) {
21792
22079
  console.log(` ${source_default.dim("Log:")} ${entry.logFile}`);
21793
22080
  }
21794
22081
  console.log();
@@ -21797,16 +22084,16 @@ async function historyCommand(options = {}) {
21797
22084
  async function exportsCommand(slug, options = {}) {
21798
22085
  const target = options.target || getDefaultTarget();
21799
22086
  const skillsDir = target === "claude" ? getProjectClaudeSkillsDir() : getProjectCodexSkillsDir();
21800
- let skillDir = join7(skillsDir, `skill-${slug}`);
21801
- if (!existsSync7(skillDir)) {
21802
- skillDir = join7(skillsDir, slug);
22087
+ let skillDir = join8(skillsDir, `skill-${slug}`);
22088
+ if (!existsSync8(skillDir)) {
22089
+ skillDir = join8(skillsDir, slug);
21803
22090
  }
21804
- if (!existsSync7(skillDir)) {
22091
+ if (!existsSync8(skillDir)) {
21805
22092
  console.log(source_default.red(`Skill "${slug}" not found`));
21806
22093
  return;
21807
22094
  }
21808
- const exportsDir = join7(skillDir, "exports");
21809
- if (!existsSync7(exportsDir)) {
22095
+ const exportsDir = join8(skillDir, "exports");
22096
+ if (!existsSync8(exportsDir)) {
21810
22097
  console.log(source_default.dim(`No exports found for "${slug}"`));
21811
22098
  return;
21812
22099
  }
@@ -21816,11 +22103,11 @@ async function exportsCommand(slug, options = {}) {
21816
22103
  return;
21817
22104
  }
21818
22105
  console.log();
21819
- console.log(source_default.bold(`Exports for ${slug}`));
22106
+ console.log(indigo3.bold(`Exports for ${slug}`));
21820
22107
  console.log(source_default.dim(`Directory: ${exportsDir}`));
21821
22108
  console.log();
21822
22109
  const fileInfos = files.map((file) => {
21823
- const filePath = join7(exportsDir, file);
22110
+ const filePath = join8(exportsDir, file);
21824
22111
  const stats = statSync(filePath);
21825
22112
  return { file, filePath, mtime: stats.mtime, size: stats.size };
21826
22113
  }).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
@@ -21837,16 +22124,16 @@ async function exportsCommand(slug, options = {}) {
21837
22124
  async function logsCommand(slug, options = {}) {
21838
22125
  const target = options.target || getDefaultTarget();
21839
22126
  const skillsDir = target === "claude" ? getProjectClaudeSkillsDir() : getProjectCodexSkillsDir();
21840
- let skillDir = join7(skillsDir, `skill-${slug}`);
21841
- if (!existsSync7(skillDir)) {
21842
- skillDir = join7(skillsDir, slug);
22127
+ let skillDir = join8(skillsDir, `skill-${slug}`);
22128
+ if (!existsSync8(skillDir)) {
22129
+ skillDir = join8(skillsDir, slug);
21843
22130
  }
21844
- if (!existsSync7(skillDir)) {
22131
+ if (!existsSync8(skillDir)) {
21845
22132
  console.log(source_default.red(`Skill "${slug}" not found`));
21846
22133
  return;
21847
22134
  }
21848
- const logsDir = join7(skillDir, "logs");
21849
- if (!existsSync7(logsDir)) {
22135
+ const logsDir = join8(skillDir, "logs");
22136
+ if (!existsSync8(logsDir)) {
21850
22137
  console.log(source_default.dim(`No logs found for "${slug}"`));
21851
22138
  return;
21852
22139
  }
@@ -21857,19 +22144,19 @@ async function logsCommand(slug, options = {}) {
21857
22144
  }
21858
22145
  files.sort().reverse();
21859
22146
  const latestLog = files[0];
21860
- const logPath = join7(logsDir, latestLog);
22147
+ const logPath = join8(logsDir, latestLog);
21861
22148
  console.log();
21862
- console.log(source_default.bold(`Latest Log for ${slug}`));
22149
+ console.log(indigo3.bold(`Latest Log for ${slug}`));
21863
22150
  console.log(source_default.dim(`File: ${logPath}`));
21864
22151
  console.log(source_default.dim("─".repeat(60)));
21865
22152
  console.log();
21866
22153
  const content = readFileSync4(logPath, "utf-8");
21867
- const lines = content.split(`
22154
+ const logLines = content.split(`
21868
22155
  `);
21869
22156
  const tailLines = options.tail || 50;
21870
- if (lines.length > tailLines) {
22157
+ if (logLines.length > tailLines) {
21871
22158
  console.log(source_default.dim(`... (showing last ${tailLines} lines)`));
21872
- console.log(lines.slice(-tailLines).join(`
22159
+ console.log(logLines.slice(-tailLines).join(`
21873
22160
  `));
21874
22161
  } else {
21875
22162
  console.log(content);
@@ -21899,7 +22186,7 @@ function formatBytes(bytes) {
21899
22186
  }
21900
22187
 
21901
22188
  // src/commands/marketplace.ts
21902
- var indigo = source_default.hex("#6366f1");
22189
+ var indigo4 = source_default.hex("#6366f1");
21903
22190
  async function marketplaceCommand(options = {}) {
21904
22191
  const spinner = ora("Fetching skills from marketplace...").start();
21905
22192
  const limit = options.limit || 20;
@@ -21922,36 +22209,36 @@ async function marketplaceCommand(options = {}) {
21922
22209
  return;
21923
22210
  }
21924
22211
  console.log();
21925
- console.log(indigo.bold(`Skills Marketplace`) + source_default.dim(` (page ${page}/${totalPages}, ${total} total skills)`));
22212
+ console.log(indigo4.bold(`Skills Marketplace`) + source_default.dim(` (page ${page}/${totalPages}, ${total} total skills)`));
21926
22213
  console.log();
21927
22214
  for (const skill of skills) {
21928
- const verified = skill.isVerified ? indigo(" ✓") : "";
22215
+ const verified = skill.isVerified ? indigo4(" ✓") : "";
21929
22216
  const downloads = skill.downloadCount > 0 ? source_default.dim(` (${skill.downloadCount} installs)`) : "";
21930
22217
  console.log(" " + source_default.bold(skill.name) + verified + source_default.dim(` v${skill.version}`) + downloads);
21931
22218
  console.log(" " + source_default.dim(skill.description || "No description"));
21932
- console.log(" " + indigo(`skills install ${skill.slug}`));
22219
+ console.log(" " + indigo4(`skills install ${skill.slug}`));
21933
22220
  console.log();
21934
22221
  }
21935
22222
  console.log(source_default.dim("─".repeat(50)));
21936
22223
  if (totalPages > 1) {
21937
22224
  const navHints = [];
21938
22225
  if (page > 1) {
21939
- navHints.push(`${indigo(`skills marketplace -p ${page - 1}`)} for previous`);
22226
+ navHints.push(`${indigo4(`skills marketplace -p ${page - 1}`)} for previous`);
21940
22227
  }
21941
22228
  if (page < totalPages) {
21942
- navHints.push(`${indigo(`skills marketplace -p ${page + 1}`)} for next`);
22229
+ navHints.push(`${indigo4(`skills marketplace -p ${page + 1}`)} for next`);
21943
22230
  }
21944
22231
  if (navHints.length > 0) {
21945
22232
  console.log(source_default.dim(navHints.join(" | ")));
21946
22233
  }
21947
22234
  }
21948
- console.log(source_default.dim(`Run ${indigo("skills install <name>")} to install a skill`));
21949
- console.log(source_default.dim(`Run ${indigo("skills search <query>")} to search for specific skills`));
22235
+ console.log(source_default.dim(`Run ${indigo4("skills install <name>")} to install a skill`));
22236
+ console.log(source_default.dim(`Run ${indigo4("skills search <query>")} to search for specific skills`));
21950
22237
  }
21951
22238
 
21952
22239
  // src/commands/feedback.ts
21953
22240
  var import_prompts2 = __toESM(require_prompts3(), 1);
21954
- var indigo2 = source_default.hex("#6366f1");
22241
+ var indigo5 = source_default.hex("#6366f1");
21955
22242
  var indigoBold = source_default.hex("#6366f1").bold;
21956
22243
  async function submitFeedback(type, title, description) {
21957
22244
  const spinner = ora("Submitting feedback...").start();
@@ -21973,7 +22260,7 @@ async function submitFeedback(type, title, description) {
21973
22260
  }
21974
22261
  spinner.succeed("Feedback submitted!");
21975
22262
  console.log();
21976
- console.log(indigo2(" Thank you for your feedback! \uD83D\uDE4F"));
22263
+ console.log(indigo5(" Thank you for your feedback! \uD83D\uDE4F"));
21977
22264
  console.log(source_default.dim(" We'll review it and get back to you if needed."));
21978
22265
  return true;
21979
22266
  } catch (error) {
@@ -21989,7 +22276,7 @@ async function feedbackCommand(options = {}) {
21989
22276
  const apiKey = getApiKey();
21990
22277
  if (!apiKey) {
21991
22278
  console.log(source_default.yellow("⚠") + " You need to be logged in to submit feedback.");
21992
- console.log(source_default.dim(" Run ") + indigo2("skills login") + source_default.dim(" first."));
22279
+ console.log(source_default.dim(" Run ") + indigo5("skills login") + source_default.dim(" first."));
21993
22280
  console.log();
21994
22281
  return;
21995
22282
  }
@@ -22041,10 +22328,384 @@ Feedback cancelled.`));
22041
22328
  console.log();
22042
22329
  }
22043
22330
 
22331
+ // src/commands/info.ts
22332
+ var indigo6 = source_default.hex("#6366f1");
22333
+ async function infoCommand(name) {
22334
+ const spinner = ora("Fetching skill info...").start();
22335
+ const result = await getSkill(name);
22336
+ if (result.error || !result.data) {
22337
+ spinner.fail("Skill not found");
22338
+ console.error(source_default.red(result.error || "Unknown error"));
22339
+ return;
22340
+ }
22341
+ spinner.stop();
22342
+ const skill = result.data;
22343
+ console.log();
22344
+ console.log(indigo6.bold(skill.name) + (skill.isVerified ? indigo6(" ✓") : ""));
22345
+ console.log(source_default.dim(`v${skill.version}`));
22346
+ console.log();
22347
+ if (skill.description) {
22348
+ console.log(skill.description);
22349
+ console.log();
22350
+ }
22351
+ console.log(source_default.dim("─".repeat(50)));
22352
+ console.log();
22353
+ const details = [
22354
+ ["Slug", skill.slug],
22355
+ ["Version", skill.version],
22356
+ ["Category", skill.category?.name || "Uncategorized"],
22357
+ ["License", skill.license || "Not specified"],
22358
+ ["Downloads", skill.downloadCount.toLocaleString()],
22359
+ ["Verified", skill.isVerified ? source_default.green("Yes") : "No"],
22360
+ ["Remote Execution", skill.isRemoteExecution ? source_default.green("Yes") : "No"],
22361
+ ["Credits/Run", (skill.creditsPerExecution ?? 0) > 0 ? indigo6(String(skill.creditsPerExecution)) : source_default.green("Free")]
22362
+ ];
22363
+ for (const [label, value] of details) {
22364
+ console.log(` ${source_default.dim(label.padEnd(18))} ${value}`);
22365
+ }
22366
+ if (skill.tags && skill.tags.length > 0) {
22367
+ console.log();
22368
+ console.log(` ${source_default.dim("Tags".padEnd(18))} ${skill.tags.map((t) => source_default.dim(`#${t.name}`)).join(" ")}`);
22369
+ }
22370
+ console.log();
22371
+ console.log(source_default.dim("─".repeat(50)));
22372
+ console.log();
22373
+ console.log(` ${source_default.dim("Install:")} ${indigo6(`skills install ${skill.slug}`)}`);
22374
+ if (skill.isRemoteExecution) {
22375
+ console.log(` ${source_default.dim("Run:")} ${indigo6(`skills run ${skill.slug}`)}`);
22376
+ }
22377
+ console.log();
22378
+ }
22379
+
22380
+ // src/commands/credits.ts
22381
+ var indigo7 = source_default.hex("#6366f1");
22382
+ async function creditsCommand() {
22383
+ const apiKey = getApiKey();
22384
+ if (!apiKey) {
22385
+ console.log(source_default.yellow("Not logged in"));
22386
+ console.log(source_default.dim("Run `skills login` to authenticate"));
22387
+ return;
22388
+ }
22389
+ const spinner = ora("Fetching credits...").start();
22390
+ const result = await getCurrentUser();
22391
+ if (result.error || !result.data) {
22392
+ spinner.fail("Failed to fetch credits");
22393
+ console.error(source_default.red(result.error || "Unknown error"));
22394
+ return;
22395
+ }
22396
+ spinner.stop();
22397
+ const { creditsBalance, plan, tenantName } = result.data;
22398
+ console.log();
22399
+ console.log(indigo7.bold("Credits Balance"));
22400
+ console.log();
22401
+ console.log(" " + indigo7.bold(creditsBalance.toLocaleString()) + " credits");
22402
+ console.log();
22403
+ console.log(source_default.dim("─".repeat(30)));
22404
+ console.log();
22405
+ console.log(" Plan: " + source_default.bold(plan));
22406
+ console.log(" Org: " + (tenantName || source_default.dim("Personal")));
22407
+ console.log();
22408
+ console.log(source_default.dim("Purchase more at https://skills.md/billing"));
22409
+ console.log();
22410
+ }
22411
+
22412
+ // src/commands/update.ts
22413
+ import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
22414
+ import { join as join9 } from "path";
22415
+ var indigo8 = source_default.hex("#6366f1");
22416
+ function parseFrontmatter2(content) {
22417
+ const result = {};
22418
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
22419
+ if (!match)
22420
+ return result;
22421
+ const frontmatter = match[1];
22422
+ const lines = frontmatter.split(`
22423
+ `);
22424
+ for (const line of lines) {
22425
+ const colonIndex = line.indexOf(":");
22426
+ if (colonIndex === -1)
22427
+ continue;
22428
+ const key = line.slice(0, colonIndex).trim();
22429
+ let value = line.slice(colonIndex + 1).trim();
22430
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
22431
+ value = value.slice(1, -1);
22432
+ }
22433
+ result[key] = value;
22434
+ }
22435
+ return result;
22436
+ }
22437
+ async function updateCommand(skillName, options = {}) {
22438
+ const target = options.target ?? getDefaultTarget();
22439
+ const skillsDir = target === "claude" ? getClaudeSkillsDir() : getCodexSkillsDir();
22440
+ if (!existsSync9(skillsDir)) {
22441
+ console.log(source_default.dim("No skills installed"));
22442
+ return;
22443
+ }
22444
+ const entries = readdirSync3(skillsDir, { withFileTypes: true });
22445
+ const installedSkills = [];
22446
+ for (const entry of entries) {
22447
+ if (!entry.isDirectory() || !entry.name.startsWith("skill-"))
22448
+ continue;
22449
+ const skillDir = join9(skillsDir, entry.name);
22450
+ const skillMdPath = join9(skillDir, "SKILL.md");
22451
+ if (!existsSync9(skillMdPath))
22452
+ continue;
22453
+ const content = readFileSync5(skillMdPath, "utf-8");
22454
+ const frontmatter = parseFrontmatter2(content);
22455
+ const slug = entry.name.replace("skill-", "");
22456
+ if (skillName && slug !== skillName && frontmatter.name !== skillName) {
22457
+ continue;
22458
+ }
22459
+ installedSkills.push({
22460
+ slug,
22461
+ name: frontmatter.name || slug,
22462
+ version: frontmatter.version || "unknown",
22463
+ dir: skillDir
22464
+ });
22465
+ }
22466
+ if (installedSkills.length === 0) {
22467
+ if (skillName) {
22468
+ console.log(source_default.yellow(`Skill "${skillName}" not found`));
22469
+ } else {
22470
+ console.log(source_default.dim("No skills installed"));
22471
+ }
22472
+ return;
22473
+ }
22474
+ console.log();
22475
+ console.log(indigo8.bold("Updating Skills"));
22476
+ console.log();
22477
+ let updated = 0;
22478
+ let upToDate = 0;
22479
+ let failed = 0;
22480
+ for (const skill of installedSkills) {
22481
+ const spinner = ora(`Checking ${skill.name}...`).start();
22482
+ try {
22483
+ const result = await installSkill(skill.slug);
22484
+ if (result.error || !result.data) {
22485
+ spinner.fail(`${skill.name}: Failed to fetch`);
22486
+ failed++;
22487
+ continue;
22488
+ }
22489
+ const newVersion = result.data.version;
22490
+ const currentVersion = skill.version;
22491
+ if (newVersion !== currentVersion) {
22492
+ writeFileSync6(join9(skill.dir, "SKILL.md"), result.data.skillMdContent);
22493
+ spinner.succeed(`${skill.name}: ${source_default.dim(currentVersion)} ${indigo8("→")} ${indigo8(newVersion)}`);
22494
+ updated++;
22495
+ } else {
22496
+ spinner.succeed(`${skill.name}: ${source_default.dim(`v${currentVersion}`)} ${source_default.green("(up to date)")}`);
22497
+ upToDate++;
22498
+ }
22499
+ } catch (error) {
22500
+ spinner.fail(`${skill.name}: Error`);
22501
+ failed++;
22502
+ }
22503
+ }
22504
+ console.log();
22505
+ console.log(source_default.dim("─".repeat(40)));
22506
+ console.log();
22507
+ const summary = [];
22508
+ if (updated > 0)
22509
+ summary.push(indigo8(`${updated} updated`));
22510
+ if (upToDate > 0)
22511
+ summary.push(source_default.green(`${upToDate} up to date`));
22512
+ if (failed > 0)
22513
+ summary.push(source_default.red(`${failed} failed`));
22514
+ console.log(" " + summary.join(", "));
22515
+ console.log();
22516
+ }
22517
+
22518
+ // src/commands/upgrade.ts
22519
+ import { execSync } from "child_process";
22520
+ var indigo9 = source_default.hex("#6366f1");
22521
+ var PACKAGE_NAME = "@hasnatools/skills";
22522
+ async function upgradeCommand() {
22523
+ console.log();
22524
+ console.log(indigo9.bold("Upgrade Skills CLI"));
22525
+ console.log();
22526
+ const currentVersion = process.env.npm_package_version || getInstalledVersion();
22527
+ console.log(` Current: ${source_default.dim(`v${currentVersion}`)}`);
22528
+ const spinner = ora("Checking for updates...").start();
22529
+ try {
22530
+ const latestVersion = execSync(`npm view ${PACKAGE_NAME} version`, {
22531
+ encoding: "utf-8",
22532
+ stdio: ["pipe", "pipe", "pipe"]
22533
+ }).trim();
22534
+ if (latestVersion === currentVersion) {
22535
+ spinner.succeed("Already on latest version");
22536
+ console.log();
22537
+ return;
22538
+ }
22539
+ spinner.text = `Upgrading to v${latestVersion}...`;
22540
+ const pm = detectPackageManager();
22541
+ const upgradeCmd = pm === "bun" ? `bun install -g ${PACKAGE_NAME}@latest` : pm === "yarn" ? `yarn global add ${PACKAGE_NAME}@latest` : pm === "pnpm" ? `pnpm add -g ${PACKAGE_NAME}@latest` : `npm install -g ${PACKAGE_NAME}@latest`;
22542
+ execSync(upgradeCmd, {
22543
+ stdio: ["pipe", "pipe", "pipe"]
22544
+ });
22545
+ spinner.succeed(`Upgraded to ${indigo9(`v${latestVersion}`)}`);
22546
+ console.log();
22547
+ console.log(source_default.dim(" Restart your terminal to use the new version"));
22548
+ console.log();
22549
+ } catch (error) {
22550
+ spinner.fail("Upgrade failed");
22551
+ console.log();
22552
+ console.log(source_default.red(" " + (error instanceof Error ? error.message : "Unknown error")));
22553
+ console.log();
22554
+ console.log(source_default.dim(" Try manually: npm install -g " + PACKAGE_NAME + "@latest"));
22555
+ console.log();
22556
+ }
22557
+ }
22558
+ function getInstalledVersion() {
22559
+ try {
22560
+ const output = execSync(`npm list -g ${PACKAGE_NAME} --depth=0 --json`, {
22561
+ encoding: "utf-8",
22562
+ stdio: ["pipe", "pipe", "pipe"]
22563
+ });
22564
+ const data = JSON.parse(output);
22565
+ return data.dependencies?.[PACKAGE_NAME]?.version || "unknown";
22566
+ } catch {
22567
+ return "unknown";
22568
+ }
22569
+ }
22570
+ function detectPackageManager() {
22571
+ try {
22572
+ execSync("bun --version", { stdio: ["pipe", "pipe", "pipe"] });
22573
+ return "bun";
22574
+ } catch {}
22575
+ try {
22576
+ execSync("pnpm --version", { stdio: ["pipe", "pipe", "pipe"] });
22577
+ return "pnpm";
22578
+ } catch {}
22579
+ try {
22580
+ execSync("yarn --version", { stdio: ["pipe", "pipe", "pipe"] });
22581
+ return "yarn";
22582
+ } catch {}
22583
+ return "npm";
22584
+ }
22585
+
22586
+ // src/commands/doctor.ts
22587
+ import { existsSync as existsSync10 } from "fs";
22588
+ import { execSync as execSync2 } from "child_process";
22589
+ var indigo10 = source_default.hex("#6366f1");
22590
+ async function doctorCommand() {
22591
+ console.log();
22592
+ console.log(indigo10.bold("Skills Doctor"));
22593
+ console.log(source_default.dim("Checking your environment..."));
22594
+ console.log();
22595
+ const results = [];
22596
+ const spinner1 = ora("Checking Node.js...").start();
22597
+ try {
22598
+ const nodeVersion = process.version;
22599
+ const major = parseInt(nodeVersion.slice(1).split(".")[0], 10);
22600
+ if (major >= 18) {
22601
+ spinner1.succeed(`Node.js ${nodeVersion}`);
22602
+ results.push({ name: "Node.js", status: "ok", message: nodeVersion });
22603
+ } else {
22604
+ spinner1.warn(`Node.js ${nodeVersion} (v18+ recommended)`);
22605
+ results.push({ name: "Node.js", status: "warn", message: `${nodeVersion} (v18+ recommended)` });
22606
+ }
22607
+ } catch {
22608
+ spinner1.fail("Node.js not found");
22609
+ results.push({ name: "Node.js", status: "error", message: "Not found" });
22610
+ }
22611
+ const spinner2 = ora("Checking Bun...").start();
22612
+ try {
22613
+ const bunVersion = execSync2("bun --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
22614
+ spinner2.succeed(`Bun v${bunVersion}`);
22615
+ results.push({ name: "Bun", status: "ok", message: `v${bunVersion}` });
22616
+ } catch {
22617
+ spinner2.info("Bun not installed (optional)");
22618
+ results.push({ name: "Bun", status: "warn", message: "Not installed (optional)" });
22619
+ }
22620
+ const spinner3 = ora("Checking API connectivity...").start();
22621
+ const endpoint = getApiEndpoint();
22622
+ try {
22623
+ const response = await fetch(`${endpoint}/status`, {
22624
+ method: "GET",
22625
+ signal: AbortSignal.timeout(1e4)
22626
+ });
22627
+ if (response.ok) {
22628
+ spinner3.succeed(`API reachable (${endpoint})`);
22629
+ results.push({ name: "API", status: "ok", message: endpoint });
22630
+ } else {
22631
+ spinner3.warn(`API returned ${response.status}`);
22632
+ results.push({ name: "API", status: "warn", message: `Status ${response.status}` });
22633
+ }
22634
+ } catch (error) {
22635
+ spinner3.fail("API unreachable");
22636
+ results.push({ name: "API", status: "error", message: error instanceof Error ? error.message : "Connection failed" });
22637
+ }
22638
+ const spinner4 = ora("Checking authentication...").start();
22639
+ const apiKey = getApiKey();
22640
+ if (apiKey) {
22641
+ try {
22642
+ const response = await fetch(`${endpoint}/me`, {
22643
+ headers: { Authorization: `Bearer ${apiKey}` },
22644
+ signal: AbortSignal.timeout(1e4)
22645
+ });
22646
+ if (response.ok) {
22647
+ const data = await response.json();
22648
+ spinner4.succeed(`Authenticated as ${data.email}`);
22649
+ results.push({ name: "Auth", status: "ok", message: data.email });
22650
+ } else if (response.status === 401) {
22651
+ spinner4.warn("API key invalid or expired");
22652
+ results.push({ name: "Auth", status: "warn", message: "Invalid/expired key" });
22653
+ } else {
22654
+ spinner4.warn(`Auth check returned ${response.status}`);
22655
+ results.push({ name: "Auth", status: "warn", message: `Status ${response.status}` });
22656
+ }
22657
+ } catch {
22658
+ spinner4.warn("Could not verify authentication");
22659
+ results.push({ name: "Auth", status: "warn", message: "Verification failed" });
22660
+ }
22661
+ } else {
22662
+ spinner4.info("Not logged in");
22663
+ results.push({ name: "Auth", status: "warn", message: "Not logged in" });
22664
+ }
22665
+ const spinner5 = ora("Checking Claude skills directory...").start();
22666
+ const claudeDir = getClaudeSkillsDir();
22667
+ if (existsSync10(claudeDir)) {
22668
+ spinner5.succeed(`Claude skills: ${claudeDir}`);
22669
+ results.push({ name: "Claude Dir", status: "ok", message: claudeDir });
22670
+ } else {
22671
+ spinner5.info("Claude skills directory not found");
22672
+ results.push({ name: "Claude Dir", status: "warn", message: "Not found (will be created on install)" });
22673
+ }
22674
+ const spinner6 = ora("Checking Codex skills directory...").start();
22675
+ const codexDir = getCodexSkillsDir();
22676
+ if (existsSync10(codexDir)) {
22677
+ spinner6.succeed(`Codex skills: ${codexDir}`);
22678
+ results.push({ name: "Codex Dir", status: "ok", message: codexDir });
22679
+ } else {
22680
+ spinner6.info("Codex skills directory not found");
22681
+ results.push({ name: "Codex Dir", status: "warn", message: "Not found (will be created on install)" });
22682
+ }
22683
+ console.log();
22684
+ console.log(source_default.dim("─".repeat(50)));
22685
+ console.log();
22686
+ const okCount = results.filter((r) => r.status === "ok").length;
22687
+ const warnCount = results.filter((r) => r.status === "warn").length;
22688
+ const errorCount = results.filter((r) => r.status === "error").length;
22689
+ if (errorCount > 0) {
22690
+ console.log(source_default.red.bold(` ${errorCount} error(s) found`));
22691
+ }
22692
+ if (warnCount > 0) {
22693
+ console.log(source_default.yellow(` ${warnCount} warning(s)`));
22694
+ }
22695
+ if (okCount === results.length) {
22696
+ console.log(indigo10.bold(" All checks passed!"));
22697
+ }
22698
+ console.log();
22699
+ if (!apiKey) {
22700
+ console.log(source_default.dim(" Run `skills login` to authenticate"));
22701
+ }
22702
+ console.log();
22703
+ }
22704
+
22044
22705
  // src/index.ts
22045
- var indigo3 = source_default.hex("#6366f1");
22706
+ var indigo11 = source_default.hex("#6366f1");
22046
22707
  var program2 = new Command;
22047
- program2.name("skills").description("CLI for skills.md - AI Agent Skills Marketplace").version("0.1.0");
22708
+ program2.name("skills").description("CLI for skills.md - AI Agent Skills Marketplace").version("0.1.10");
22048
22709
  program2.command("init").description("Initialize skills.md in current project").option("-f, --force", "Force re-initialization (removes existing .skills/)").action((options) => {
22049
22710
  initCommand({ force: options.force });
22050
22711
  });
@@ -22073,6 +22734,12 @@ program2.command("uninstall <name>").alias("remove").description("Uninstall a sk
22073
22734
  target: options.target
22074
22735
  });
22075
22736
  });
22737
+ program2.command("download <name>").alias("dl").description("Download a skill with full source code (if available)").option("-l, --local", "Download to current project instead of global").option("-t, --target <target>", "Target platform (claude, codex)").action((name, options) => {
22738
+ downloadCommand(name, {
22739
+ local: options.local,
22740
+ target: options.target
22741
+ });
22742
+ });
22076
22743
  program2.command("list").alias("ls").description("List installed skills").option("-g, --global", "List only global skills").option("-t, --target <target>", "Target platform (claude, codex)").option("-r, --remote", "List remote installations").action((options) => {
22077
22744
  listCommand({
22078
22745
  global: options.global,
@@ -22101,11 +22768,13 @@ program2.command("generate <mediaType> <prompt>").alias("gen").description("Gene
22101
22768
  });
22102
22769
  });
22103
22770
  program2.command("jobs [jobId]").description("List generation jobs or view job details").action(jobsCommand);
22104
- program2.command("history").description("View execution history").option("-s, --skill <name>", "Filter by skill name").option("-n, --limit <number>", "Limit number of entries", "20").option("-v, --verbose", "Show detailed output").action((options) => {
22771
+ program2.command("history").description("View execution history").option("-s, --skill <name>", "Filter by skill name").option("-n, --limit <number>", "Limit number of entries", "20").option("-v, --verbose", "Show detailed output").option("--status <status>", "Filter by status (completed, failed, pending)").option("-l, --local", "Show local history only (skip API)").action((options) => {
22105
22772
  historyCommand({
22106
22773
  skill: options.skill,
22107
22774
  limit: parseInt(options.limit, 10),
22108
- verbose: options.verbose
22775
+ verbose: options.verbose,
22776
+ status: options.status,
22777
+ local: options.local
22109
22778
  });
22110
22779
  });
22111
22780
  program2.command("exports <skill>").description("View exported files for a skill").option("-t, --target <target>", "Target platform (claude, codex)").action((skill, options) => {
@@ -22124,8 +22793,15 @@ program2.command("feedback").description("Submit feedback, bug reports, or featu
22124
22793
  message: options.message
22125
22794
  });
22126
22795
  });
22796
+ program2.command("info <name>").description("Show detailed information about a skill").action(infoCommand);
22797
+ program2.command("credits").description("Check your credits balance").action(creditsCommand);
22798
+ program2.command("update [skill]").description("Update installed skills to latest versions").option("-t, --target <target>", "Target platform (claude, codex)").action((skill, options) => {
22799
+ updateCommand(skill, { target: options.target });
22800
+ });
22801
+ program2.command("upgrade").description("Upgrade the Skills CLI to the latest version").action(upgradeCommand);
22802
+ program2.command("doctor").description("Check your environment and diagnose issues").action(doctorCommand);
22127
22803
  program2.addHelpText("after", `
22128
- ${indigo3.bold("Examples:")}
22804
+ ${indigo11.bold("Examples:")}
22129
22805
  ${source_default.dim("# Initialize in current project")}
22130
22806
  $ skills init
22131
22807
 
@@ -22153,6 +22829,8 @@ ${indigo3.bold("Examples:")}
22153
22829
  ${source_default.dim("# View execution history")}
22154
22830
  $ skills history
22155
22831
  $ skills history --skill image-generator
22832
+ $ skills history --status completed
22833
+ $ skills history --local
22156
22834
 
22157
22835
  ${source_default.dim("# View exports for a skill")}
22158
22836
  $ skills exports image-generator
@@ -22163,7 +22841,22 @@ ${indigo3.bold("Examples:")}
22163
22841
  ${source_default.dim("# Submit feedback")}
22164
22842
  $ skills feedback
22165
22843
 
22166
- ${indigo3.bold("Documentation:")}
22167
- ${indigo3("https://skills.md/docs")}
22844
+ ${source_default.dim("# Get skill info")}
22845
+ $ skills info code-review
22846
+
22847
+ ${source_default.dim("# Check credits balance")}
22848
+ $ skills credits
22849
+
22850
+ ${source_default.dim("# Update installed skills")}
22851
+ $ skills update
22852
+
22853
+ ${source_default.dim("# Upgrade CLI")}
22854
+ $ skills upgrade
22855
+
22856
+ ${source_default.dim("# Check environment")}
22857
+ $ skills doctor
22858
+
22859
+ ${indigo11.bold("Documentation:")}
22860
+ ${indigo11("https://skills.md/docs")}
22168
22861
  `);
22169
22862
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasnatools/skills",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "CLI for skills.md - AI Agent Skills Marketplace",
5
5
  "type": "module",
6
6
  "bin": {