@hasnatools/skills 0.1.8 → 0.1.9

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 +660 -75
  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,
@@ -21730,8 +21913,8 @@ 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";
21735
21918
  async function historyCommand(options = {}) {
21736
21919
  const projectOutputDir = getProjectSkillsOutputDir();
21737
21920
  if (!projectOutputDir) {
@@ -21739,8 +21922,8 @@ async function historyCommand(options = {}) {
21739
21922
  console.log(source_default.dim("Run `skills init` first"));
21740
21923
  return;
21741
21924
  }
21742
- const historyFile = join7(projectOutputDir, "execution-history.jsonl");
21743
- if (!existsSync7(historyFile)) {
21925
+ const historyFile = join8(projectOutputDir, "execution-history.jsonl");
21926
+ if (!existsSync8(historyFile)) {
21744
21927
  console.log(source_default.dim("No execution history found"));
21745
21928
  console.log(source_default.dim("Run a skill with `skills run <name>` to start tracking"));
21746
21929
  return;
@@ -21788,7 +21971,7 @@ async function historyCommand(options = {}) {
21788
21971
  }
21789
21972
  }
21790
21973
  }
21791
- if (options.verbose && existsSync7(entry.logFile)) {
21974
+ if (options.verbose && existsSync8(entry.logFile)) {
21792
21975
  console.log(` ${source_default.dim("Log:")} ${entry.logFile}`);
21793
21976
  }
21794
21977
  console.log();
@@ -21797,16 +21980,16 @@ async function historyCommand(options = {}) {
21797
21980
  async function exportsCommand(slug, options = {}) {
21798
21981
  const target = options.target || getDefaultTarget();
21799
21982
  const skillsDir = target === "claude" ? getProjectClaudeSkillsDir() : getProjectCodexSkillsDir();
21800
- let skillDir = join7(skillsDir, `skill-${slug}`);
21801
- if (!existsSync7(skillDir)) {
21802
- skillDir = join7(skillsDir, slug);
21983
+ let skillDir = join8(skillsDir, `skill-${slug}`);
21984
+ if (!existsSync8(skillDir)) {
21985
+ skillDir = join8(skillsDir, slug);
21803
21986
  }
21804
- if (!existsSync7(skillDir)) {
21987
+ if (!existsSync8(skillDir)) {
21805
21988
  console.log(source_default.red(`Skill "${slug}" not found`));
21806
21989
  return;
21807
21990
  }
21808
- const exportsDir = join7(skillDir, "exports");
21809
- if (!existsSync7(exportsDir)) {
21991
+ const exportsDir = join8(skillDir, "exports");
21992
+ if (!existsSync8(exportsDir)) {
21810
21993
  console.log(source_default.dim(`No exports found for "${slug}"`));
21811
21994
  return;
21812
21995
  }
@@ -21820,7 +22003,7 @@ async function exportsCommand(slug, options = {}) {
21820
22003
  console.log(source_default.dim(`Directory: ${exportsDir}`));
21821
22004
  console.log();
21822
22005
  const fileInfos = files.map((file) => {
21823
- const filePath = join7(exportsDir, file);
22006
+ const filePath = join8(exportsDir, file);
21824
22007
  const stats = statSync(filePath);
21825
22008
  return { file, filePath, mtime: stats.mtime, size: stats.size };
21826
22009
  }).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
@@ -21837,16 +22020,16 @@ async function exportsCommand(slug, options = {}) {
21837
22020
  async function logsCommand(slug, options = {}) {
21838
22021
  const target = options.target || getDefaultTarget();
21839
22022
  const skillsDir = target === "claude" ? getProjectClaudeSkillsDir() : getProjectCodexSkillsDir();
21840
- let skillDir = join7(skillsDir, `skill-${slug}`);
21841
- if (!existsSync7(skillDir)) {
21842
- skillDir = join7(skillsDir, slug);
22023
+ let skillDir = join8(skillsDir, `skill-${slug}`);
22024
+ if (!existsSync8(skillDir)) {
22025
+ skillDir = join8(skillsDir, slug);
21843
22026
  }
21844
- if (!existsSync7(skillDir)) {
22027
+ if (!existsSync8(skillDir)) {
21845
22028
  console.log(source_default.red(`Skill "${slug}" not found`));
21846
22029
  return;
21847
22030
  }
21848
- const logsDir = join7(skillDir, "logs");
21849
- if (!existsSync7(logsDir)) {
22031
+ const logsDir = join8(skillDir, "logs");
22032
+ if (!existsSync8(logsDir)) {
21850
22033
  console.log(source_default.dim(`No logs found for "${slug}"`));
21851
22034
  return;
21852
22035
  }
@@ -21857,7 +22040,7 @@ async function logsCommand(slug, options = {}) {
21857
22040
  }
21858
22041
  files.sort().reverse();
21859
22042
  const latestLog = files[0];
21860
- const logPath = join7(logsDir, latestLog);
22043
+ const logPath = join8(logsDir, latestLog);
21861
22044
  console.log();
21862
22045
  console.log(source_default.bold(`Latest Log for ${slug}`));
21863
22046
  console.log(source_default.dim(`File: ${logPath}`));
@@ -21899,7 +22082,7 @@ function formatBytes(bytes) {
21899
22082
  }
21900
22083
 
21901
22084
  // src/commands/marketplace.ts
21902
- var indigo = source_default.hex("#6366f1");
22085
+ var indigo3 = source_default.hex("#6366f1");
21903
22086
  async function marketplaceCommand(options = {}) {
21904
22087
  const spinner = ora("Fetching skills from marketplace...").start();
21905
22088
  const limit = options.limit || 20;
@@ -21922,36 +22105,36 @@ async function marketplaceCommand(options = {}) {
21922
22105
  return;
21923
22106
  }
21924
22107
  console.log();
21925
- console.log(indigo.bold(`Skills Marketplace`) + source_default.dim(` (page ${page}/${totalPages}, ${total} total skills)`));
22108
+ console.log(indigo3.bold(`Skills Marketplace`) + source_default.dim(` (page ${page}/${totalPages}, ${total} total skills)`));
21926
22109
  console.log();
21927
22110
  for (const skill of skills) {
21928
- const verified = skill.isVerified ? indigo(" ✓") : "";
22111
+ const verified = skill.isVerified ? indigo3(" ✓") : "";
21929
22112
  const downloads = skill.downloadCount > 0 ? source_default.dim(` (${skill.downloadCount} installs)`) : "";
21930
22113
  console.log(" " + source_default.bold(skill.name) + verified + source_default.dim(` v${skill.version}`) + downloads);
21931
22114
  console.log(" " + source_default.dim(skill.description || "No description"));
21932
- console.log(" " + indigo(`skills install ${skill.slug}`));
22115
+ console.log(" " + indigo3(`skills install ${skill.slug}`));
21933
22116
  console.log();
21934
22117
  }
21935
22118
  console.log(source_default.dim("─".repeat(50)));
21936
22119
  if (totalPages > 1) {
21937
22120
  const navHints = [];
21938
22121
  if (page > 1) {
21939
- navHints.push(`${indigo(`skills marketplace -p ${page - 1}`)} for previous`);
22122
+ navHints.push(`${indigo3(`skills marketplace -p ${page - 1}`)} for previous`);
21940
22123
  }
21941
22124
  if (page < totalPages) {
21942
- navHints.push(`${indigo(`skills marketplace -p ${page + 1}`)} for next`);
22125
+ navHints.push(`${indigo3(`skills marketplace -p ${page + 1}`)} for next`);
21943
22126
  }
21944
22127
  if (navHints.length > 0) {
21945
22128
  console.log(source_default.dim(navHints.join(" | ")));
21946
22129
  }
21947
22130
  }
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`));
22131
+ console.log(source_default.dim(`Run ${indigo3("skills install <name>")} to install a skill`));
22132
+ console.log(source_default.dim(`Run ${indigo3("skills search <query>")} to search for specific skills`));
21950
22133
  }
21951
22134
 
21952
22135
  // src/commands/feedback.ts
21953
22136
  var import_prompts2 = __toESM(require_prompts3(), 1);
21954
- var indigo2 = source_default.hex("#6366f1");
22137
+ var indigo4 = source_default.hex("#6366f1");
21955
22138
  var indigoBold = source_default.hex("#6366f1").bold;
21956
22139
  async function submitFeedback(type, title, description) {
21957
22140
  const spinner = ora("Submitting feedback...").start();
@@ -21973,7 +22156,7 @@ async function submitFeedback(type, title, description) {
21973
22156
  }
21974
22157
  spinner.succeed("Feedback submitted!");
21975
22158
  console.log();
21976
- console.log(indigo2(" Thank you for your feedback! \uD83D\uDE4F"));
22159
+ console.log(indigo4(" Thank you for your feedback! \uD83D\uDE4F"));
21977
22160
  console.log(source_default.dim(" We'll review it and get back to you if needed."));
21978
22161
  return true;
21979
22162
  } catch (error) {
@@ -21989,7 +22172,7 @@ async function feedbackCommand(options = {}) {
21989
22172
  const apiKey = getApiKey();
21990
22173
  if (!apiKey) {
21991
22174
  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."));
22175
+ console.log(source_default.dim(" Run ") + indigo4("skills login") + source_default.dim(" first."));
21993
22176
  console.log();
21994
22177
  return;
21995
22178
  }
@@ -22041,10 +22224,384 @@ Feedback cancelled.`));
22041
22224
  console.log();
22042
22225
  }
22043
22226
 
22227
+ // src/commands/info.ts
22228
+ var indigo5 = source_default.hex("#6366f1");
22229
+ async function infoCommand(name) {
22230
+ const spinner = ora("Fetching skill info...").start();
22231
+ const result = await getSkill(name);
22232
+ if (result.error || !result.data) {
22233
+ spinner.fail("Skill not found");
22234
+ console.error(source_default.red(result.error || "Unknown error"));
22235
+ return;
22236
+ }
22237
+ spinner.stop();
22238
+ const skill = result.data;
22239
+ console.log();
22240
+ console.log(indigo5.bold(skill.name) + (skill.isVerified ? indigo5(" ✓") : ""));
22241
+ console.log(source_default.dim(`v${skill.version}`));
22242
+ console.log();
22243
+ if (skill.description) {
22244
+ console.log(skill.description);
22245
+ console.log();
22246
+ }
22247
+ console.log(source_default.dim("─".repeat(50)));
22248
+ console.log();
22249
+ const details = [
22250
+ ["Slug", skill.slug],
22251
+ ["Version", skill.version],
22252
+ ["Category", skill.category?.name || "Uncategorized"],
22253
+ ["License", skill.license || "Not specified"],
22254
+ ["Downloads", skill.downloadCount.toLocaleString()],
22255
+ ["Verified", skill.isVerified ? source_default.green("Yes") : "No"],
22256
+ ["Remote Execution", skill.isRemoteExecution ? source_default.green("Yes") : "No"],
22257
+ ["Credits/Run", (skill.creditsPerExecution ?? 0) > 0 ? indigo5(String(skill.creditsPerExecution)) : source_default.green("Free")]
22258
+ ];
22259
+ for (const [label, value] of details) {
22260
+ console.log(` ${source_default.dim(label.padEnd(18))} ${value}`);
22261
+ }
22262
+ if (skill.tags && skill.tags.length > 0) {
22263
+ console.log();
22264
+ console.log(` ${source_default.dim("Tags".padEnd(18))} ${skill.tags.map((t) => source_default.dim(`#${t.name}`)).join(" ")}`);
22265
+ }
22266
+ console.log();
22267
+ console.log(source_default.dim("─".repeat(50)));
22268
+ console.log();
22269
+ console.log(` ${source_default.dim("Install:")} ${indigo5(`skills install ${skill.slug}`)}`);
22270
+ if (skill.isRemoteExecution) {
22271
+ console.log(` ${source_default.dim("Run:")} ${indigo5(`skills run ${skill.slug}`)}`);
22272
+ }
22273
+ console.log();
22274
+ }
22275
+
22276
+ // src/commands/credits.ts
22277
+ var indigo6 = source_default.hex("#6366f1");
22278
+ async function creditsCommand() {
22279
+ const apiKey = getApiKey();
22280
+ if (!apiKey) {
22281
+ console.log(source_default.yellow("Not logged in"));
22282
+ console.log(source_default.dim("Run `skills login` to authenticate"));
22283
+ return;
22284
+ }
22285
+ const spinner = ora("Fetching credits...").start();
22286
+ const result = await getCurrentUser();
22287
+ if (result.error || !result.data) {
22288
+ spinner.fail("Failed to fetch credits");
22289
+ console.error(source_default.red(result.error || "Unknown error"));
22290
+ return;
22291
+ }
22292
+ spinner.stop();
22293
+ const { creditsBalance, plan, tenantName } = result.data;
22294
+ console.log();
22295
+ console.log(indigo6.bold("Credits Balance"));
22296
+ console.log();
22297
+ console.log(" " + indigo6.bold(creditsBalance.toLocaleString()) + " credits");
22298
+ console.log();
22299
+ console.log(source_default.dim("─".repeat(30)));
22300
+ console.log();
22301
+ console.log(" Plan: " + source_default.bold(plan));
22302
+ console.log(" Org: " + (tenantName || source_default.dim("Personal")));
22303
+ console.log();
22304
+ console.log(source_default.dim("Purchase more at https://skills.md/billing"));
22305
+ console.log();
22306
+ }
22307
+
22308
+ // src/commands/update.ts
22309
+ import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
22310
+ import { join as join9 } from "path";
22311
+ var indigo7 = source_default.hex("#6366f1");
22312
+ function parseFrontmatter2(content) {
22313
+ const result = {};
22314
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
22315
+ if (!match)
22316
+ return result;
22317
+ const frontmatter = match[1];
22318
+ const lines = frontmatter.split(`
22319
+ `);
22320
+ for (const line of lines) {
22321
+ const colonIndex = line.indexOf(":");
22322
+ if (colonIndex === -1)
22323
+ continue;
22324
+ const key = line.slice(0, colonIndex).trim();
22325
+ let value = line.slice(colonIndex + 1).trim();
22326
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
22327
+ value = value.slice(1, -1);
22328
+ }
22329
+ result[key] = value;
22330
+ }
22331
+ return result;
22332
+ }
22333
+ async function updateCommand(skillName, options = {}) {
22334
+ const target = options.target ?? getDefaultTarget();
22335
+ const skillsDir = target === "claude" ? getClaudeSkillsDir() : getCodexSkillsDir();
22336
+ if (!existsSync9(skillsDir)) {
22337
+ console.log(source_default.dim("No skills installed"));
22338
+ return;
22339
+ }
22340
+ const entries = readdirSync3(skillsDir, { withFileTypes: true });
22341
+ const installedSkills = [];
22342
+ for (const entry of entries) {
22343
+ if (!entry.isDirectory() || !entry.name.startsWith("skill-"))
22344
+ continue;
22345
+ const skillDir = join9(skillsDir, entry.name);
22346
+ const skillMdPath = join9(skillDir, "SKILL.md");
22347
+ if (!existsSync9(skillMdPath))
22348
+ continue;
22349
+ const content = readFileSync5(skillMdPath, "utf-8");
22350
+ const frontmatter = parseFrontmatter2(content);
22351
+ const slug = entry.name.replace("skill-", "");
22352
+ if (skillName && slug !== skillName && frontmatter.name !== skillName) {
22353
+ continue;
22354
+ }
22355
+ installedSkills.push({
22356
+ slug,
22357
+ name: frontmatter.name || slug,
22358
+ version: frontmatter.version || "unknown",
22359
+ dir: skillDir
22360
+ });
22361
+ }
22362
+ if (installedSkills.length === 0) {
22363
+ if (skillName) {
22364
+ console.log(source_default.yellow(`Skill "${skillName}" not found`));
22365
+ } else {
22366
+ console.log(source_default.dim("No skills installed"));
22367
+ }
22368
+ return;
22369
+ }
22370
+ console.log();
22371
+ console.log(indigo7.bold("Updating Skills"));
22372
+ console.log();
22373
+ let updated = 0;
22374
+ let upToDate = 0;
22375
+ let failed = 0;
22376
+ for (const skill of installedSkills) {
22377
+ const spinner = ora(`Checking ${skill.name}...`).start();
22378
+ try {
22379
+ const result = await installSkill(skill.slug);
22380
+ if (result.error || !result.data) {
22381
+ spinner.fail(`${skill.name}: Failed to fetch`);
22382
+ failed++;
22383
+ continue;
22384
+ }
22385
+ const newVersion = result.data.version;
22386
+ const currentVersion = skill.version;
22387
+ if (newVersion !== currentVersion) {
22388
+ writeFileSync6(join9(skill.dir, "SKILL.md"), result.data.skillMdContent);
22389
+ spinner.succeed(`${skill.name}: ${source_default.dim(currentVersion)} ${indigo7("→")} ${indigo7(newVersion)}`);
22390
+ updated++;
22391
+ } else {
22392
+ spinner.succeed(`${skill.name}: ${source_default.dim(`v${currentVersion}`)} ${source_default.green("(up to date)")}`);
22393
+ upToDate++;
22394
+ }
22395
+ } catch (error) {
22396
+ spinner.fail(`${skill.name}: Error`);
22397
+ failed++;
22398
+ }
22399
+ }
22400
+ console.log();
22401
+ console.log(source_default.dim("─".repeat(40)));
22402
+ console.log();
22403
+ const summary = [];
22404
+ if (updated > 0)
22405
+ summary.push(indigo7(`${updated} updated`));
22406
+ if (upToDate > 0)
22407
+ summary.push(source_default.green(`${upToDate} up to date`));
22408
+ if (failed > 0)
22409
+ summary.push(source_default.red(`${failed} failed`));
22410
+ console.log(" " + summary.join(", "));
22411
+ console.log();
22412
+ }
22413
+
22414
+ // src/commands/upgrade.ts
22415
+ import { execSync } from "child_process";
22416
+ var indigo8 = source_default.hex("#6366f1");
22417
+ var PACKAGE_NAME = "@hasnatools/skills";
22418
+ async function upgradeCommand() {
22419
+ console.log();
22420
+ console.log(indigo8.bold("Upgrade Skills CLI"));
22421
+ console.log();
22422
+ const currentVersion = process.env.npm_package_version || getInstalledVersion();
22423
+ console.log(` Current: ${source_default.dim(`v${currentVersion}`)}`);
22424
+ const spinner = ora("Checking for updates...").start();
22425
+ try {
22426
+ const latestVersion = execSync(`npm view ${PACKAGE_NAME} version`, {
22427
+ encoding: "utf-8",
22428
+ stdio: ["pipe", "pipe", "pipe"]
22429
+ }).trim();
22430
+ if (latestVersion === currentVersion) {
22431
+ spinner.succeed("Already on latest version");
22432
+ console.log();
22433
+ return;
22434
+ }
22435
+ spinner.text = `Upgrading to v${latestVersion}...`;
22436
+ const pm = detectPackageManager();
22437
+ 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`;
22438
+ execSync(upgradeCmd, {
22439
+ stdio: ["pipe", "pipe", "pipe"]
22440
+ });
22441
+ spinner.succeed(`Upgraded to ${indigo8(`v${latestVersion}`)}`);
22442
+ console.log();
22443
+ console.log(source_default.dim(" Restart your terminal to use the new version"));
22444
+ console.log();
22445
+ } catch (error) {
22446
+ spinner.fail("Upgrade failed");
22447
+ console.log();
22448
+ console.log(source_default.red(" " + (error instanceof Error ? error.message : "Unknown error")));
22449
+ console.log();
22450
+ console.log(source_default.dim(" Try manually: npm install -g " + PACKAGE_NAME + "@latest"));
22451
+ console.log();
22452
+ }
22453
+ }
22454
+ function getInstalledVersion() {
22455
+ try {
22456
+ const output = execSync(`npm list -g ${PACKAGE_NAME} --depth=0 --json`, {
22457
+ encoding: "utf-8",
22458
+ stdio: ["pipe", "pipe", "pipe"]
22459
+ });
22460
+ const data = JSON.parse(output);
22461
+ return data.dependencies?.[PACKAGE_NAME]?.version || "unknown";
22462
+ } catch {
22463
+ return "unknown";
22464
+ }
22465
+ }
22466
+ function detectPackageManager() {
22467
+ try {
22468
+ execSync("bun --version", { stdio: ["pipe", "pipe", "pipe"] });
22469
+ return "bun";
22470
+ } catch {}
22471
+ try {
22472
+ execSync("pnpm --version", { stdio: ["pipe", "pipe", "pipe"] });
22473
+ return "pnpm";
22474
+ } catch {}
22475
+ try {
22476
+ execSync("yarn --version", { stdio: ["pipe", "pipe", "pipe"] });
22477
+ return "yarn";
22478
+ } catch {}
22479
+ return "npm";
22480
+ }
22481
+
22482
+ // src/commands/doctor.ts
22483
+ import { existsSync as existsSync10 } from "fs";
22484
+ import { execSync as execSync2 } from "child_process";
22485
+ var indigo9 = source_default.hex("#6366f1");
22486
+ async function doctorCommand() {
22487
+ console.log();
22488
+ console.log(indigo9.bold("Skills Doctor"));
22489
+ console.log(source_default.dim("Checking your environment..."));
22490
+ console.log();
22491
+ const results = [];
22492
+ const spinner1 = ora("Checking Node.js...").start();
22493
+ try {
22494
+ const nodeVersion = process.version;
22495
+ const major = parseInt(nodeVersion.slice(1).split(".")[0], 10);
22496
+ if (major >= 18) {
22497
+ spinner1.succeed(`Node.js ${nodeVersion}`);
22498
+ results.push({ name: "Node.js", status: "ok", message: nodeVersion });
22499
+ } else {
22500
+ spinner1.warn(`Node.js ${nodeVersion} (v18+ recommended)`);
22501
+ results.push({ name: "Node.js", status: "warn", message: `${nodeVersion} (v18+ recommended)` });
22502
+ }
22503
+ } catch {
22504
+ spinner1.fail("Node.js not found");
22505
+ results.push({ name: "Node.js", status: "error", message: "Not found" });
22506
+ }
22507
+ const spinner2 = ora("Checking Bun...").start();
22508
+ try {
22509
+ const bunVersion = execSync2("bun --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
22510
+ spinner2.succeed(`Bun v${bunVersion}`);
22511
+ results.push({ name: "Bun", status: "ok", message: `v${bunVersion}` });
22512
+ } catch {
22513
+ spinner2.info("Bun not installed (optional)");
22514
+ results.push({ name: "Bun", status: "warn", message: "Not installed (optional)" });
22515
+ }
22516
+ const spinner3 = ora("Checking API connectivity...").start();
22517
+ const endpoint = getApiEndpoint();
22518
+ try {
22519
+ const response = await fetch(`${endpoint}/status`, {
22520
+ method: "GET",
22521
+ signal: AbortSignal.timeout(1e4)
22522
+ });
22523
+ if (response.ok) {
22524
+ spinner3.succeed(`API reachable (${endpoint})`);
22525
+ results.push({ name: "API", status: "ok", message: endpoint });
22526
+ } else {
22527
+ spinner3.warn(`API returned ${response.status}`);
22528
+ results.push({ name: "API", status: "warn", message: `Status ${response.status}` });
22529
+ }
22530
+ } catch (error) {
22531
+ spinner3.fail("API unreachable");
22532
+ results.push({ name: "API", status: "error", message: error instanceof Error ? error.message : "Connection failed" });
22533
+ }
22534
+ const spinner4 = ora("Checking authentication...").start();
22535
+ const apiKey = getApiKey();
22536
+ if (apiKey) {
22537
+ try {
22538
+ const response = await fetch(`${endpoint}/me`, {
22539
+ headers: { Authorization: `Bearer ${apiKey}` },
22540
+ signal: AbortSignal.timeout(1e4)
22541
+ });
22542
+ if (response.ok) {
22543
+ const data = await response.json();
22544
+ spinner4.succeed(`Authenticated as ${data.email}`);
22545
+ results.push({ name: "Auth", status: "ok", message: data.email });
22546
+ } else if (response.status === 401) {
22547
+ spinner4.warn("API key invalid or expired");
22548
+ results.push({ name: "Auth", status: "warn", message: "Invalid/expired key" });
22549
+ } else {
22550
+ spinner4.warn(`Auth check returned ${response.status}`);
22551
+ results.push({ name: "Auth", status: "warn", message: `Status ${response.status}` });
22552
+ }
22553
+ } catch {
22554
+ spinner4.warn("Could not verify authentication");
22555
+ results.push({ name: "Auth", status: "warn", message: "Verification failed" });
22556
+ }
22557
+ } else {
22558
+ spinner4.info("Not logged in");
22559
+ results.push({ name: "Auth", status: "warn", message: "Not logged in" });
22560
+ }
22561
+ const spinner5 = ora("Checking Claude skills directory...").start();
22562
+ const claudeDir = getClaudeSkillsDir();
22563
+ if (existsSync10(claudeDir)) {
22564
+ spinner5.succeed(`Claude skills: ${claudeDir}`);
22565
+ results.push({ name: "Claude Dir", status: "ok", message: claudeDir });
22566
+ } else {
22567
+ spinner5.info("Claude skills directory not found");
22568
+ results.push({ name: "Claude Dir", status: "warn", message: "Not found (will be created on install)" });
22569
+ }
22570
+ const spinner6 = ora("Checking Codex skills directory...").start();
22571
+ const codexDir = getCodexSkillsDir();
22572
+ if (existsSync10(codexDir)) {
22573
+ spinner6.succeed(`Codex skills: ${codexDir}`);
22574
+ results.push({ name: "Codex Dir", status: "ok", message: codexDir });
22575
+ } else {
22576
+ spinner6.info("Codex skills directory not found");
22577
+ results.push({ name: "Codex Dir", status: "warn", message: "Not found (will be created on install)" });
22578
+ }
22579
+ console.log();
22580
+ console.log(source_default.dim("─".repeat(50)));
22581
+ console.log();
22582
+ const okCount = results.filter((r) => r.status === "ok").length;
22583
+ const warnCount = results.filter((r) => r.status === "warn").length;
22584
+ const errorCount = results.filter((r) => r.status === "error").length;
22585
+ if (errorCount > 0) {
22586
+ console.log(source_default.red.bold(` ${errorCount} error(s) found`));
22587
+ }
22588
+ if (warnCount > 0) {
22589
+ console.log(source_default.yellow(` ${warnCount} warning(s)`));
22590
+ }
22591
+ if (okCount === results.length) {
22592
+ console.log(indigo9.bold(" All checks passed!"));
22593
+ }
22594
+ console.log();
22595
+ if (!apiKey) {
22596
+ console.log(source_default.dim(" Run `skills login` to authenticate"));
22597
+ }
22598
+ console.log();
22599
+ }
22600
+
22044
22601
  // src/index.ts
22045
- var indigo3 = source_default.hex("#6366f1");
22602
+ var indigo10 = source_default.hex("#6366f1");
22046
22603
  var program2 = new Command;
22047
- program2.name("skills").description("CLI for skills.md - AI Agent Skills Marketplace").version("0.1.0");
22604
+ program2.name("skills").description("CLI for skills.md - AI Agent Skills Marketplace").version("0.1.9");
22048
22605
  program2.command("init").description("Initialize skills.md in current project").option("-f, --force", "Force re-initialization (removes existing .skills/)").action((options) => {
22049
22606
  initCommand({ force: options.force });
22050
22607
  });
@@ -22073,6 +22630,12 @@ program2.command("uninstall <name>").alias("remove").description("Uninstall a sk
22073
22630
  target: options.target
22074
22631
  });
22075
22632
  });
22633
+ 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) => {
22634
+ downloadCommand(name, {
22635
+ local: options.local,
22636
+ target: options.target
22637
+ });
22638
+ });
22076
22639
  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
22640
  listCommand({
22078
22641
  global: options.global,
@@ -22124,8 +22687,15 @@ program2.command("feedback").description("Submit feedback, bug reports, or featu
22124
22687
  message: options.message
22125
22688
  });
22126
22689
  });
22690
+ program2.command("info <name>").description("Show detailed information about a skill").action(infoCommand);
22691
+ program2.command("credits").description("Check your credits balance").action(creditsCommand);
22692
+ program2.command("update [skill]").description("Update installed skills to latest versions").option("-t, --target <target>", "Target platform (claude, codex)").action((skill, options) => {
22693
+ updateCommand(skill, { target: options.target });
22694
+ });
22695
+ program2.command("upgrade").description("Upgrade the Skills CLI to the latest version").action(upgradeCommand);
22696
+ program2.command("doctor").description("Check your environment and diagnose issues").action(doctorCommand);
22127
22697
  program2.addHelpText("after", `
22128
- ${indigo3.bold("Examples:")}
22698
+ ${indigo10.bold("Examples:")}
22129
22699
  ${source_default.dim("# Initialize in current project")}
22130
22700
  $ skills init
22131
22701
 
@@ -22163,7 +22733,22 @@ ${indigo3.bold("Examples:")}
22163
22733
  ${source_default.dim("# Submit feedback")}
22164
22734
  $ skills feedback
22165
22735
 
22166
- ${indigo3.bold("Documentation:")}
22167
- ${indigo3("https://skills.md/docs")}
22736
+ ${source_default.dim("# Get skill info")}
22737
+ $ skills info code-review
22738
+
22739
+ ${source_default.dim("# Check credits balance")}
22740
+ $ skills credits
22741
+
22742
+ ${source_default.dim("# Update installed skills")}
22743
+ $ skills update
22744
+
22745
+ ${source_default.dim("# Upgrade CLI")}
22746
+ $ skills upgrade
22747
+
22748
+ ${source_default.dim("# Check environment")}
22749
+ $ skills doctor
22750
+
22751
+ ${indigo10.bold("Documentation:")}
22752
+ ${indigo10("https://skills.md/docs")}
22168
22753
  `);
22169
22754
  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.9",
4
4
  "description": "CLI for skills.md - AI Agent Skills Marketplace",
5
5
  "type": "module",
6
6
  "bin": {