@hasnatools/skills 0.1.38 → 0.1.39

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 +273 -60
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -13018,7 +13018,7 @@ var require_prompt = __commonJS((exports, module) => {
13018
13018
 
13019
13019
  // ../../node_modules/.bun/prompts@2.4.2/node_modules/prompts/dist/elements/text.js
13020
13020
  var require_text = __commonJS((exports, module) => {
13021
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
13021
+ function asyncGeneratorStep(gen, resolve2, reject, _next, _throw, key, arg) {
13022
13022
  try {
13023
13023
  var info = gen[key](arg);
13024
13024
  var value = info.value;
@@ -13027,7 +13027,7 @@ var require_text = __commonJS((exports, module) => {
13027
13027
  return;
13028
13028
  }
13029
13029
  if (info.done) {
13030
- resolve(value);
13030
+ resolve2(value);
13031
13031
  } else {
13032
13032
  Promise.resolve(value).then(_next, _throw);
13033
13033
  }
@@ -13035,13 +13035,13 @@ var require_text = __commonJS((exports, module) => {
13035
13035
  function _asyncToGenerator(fn) {
13036
13036
  return function() {
13037
13037
  var self = this, args = arguments;
13038
- return new Promise(function(resolve, reject) {
13038
+ return new Promise(function(resolve2, reject) {
13039
13039
  var gen = fn.apply(self, args);
13040
13040
  function _next(value) {
13041
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
13041
+ asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "next", value);
13042
13042
  }
13043
13043
  function _throw(err) {
13044
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
13044
+ asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "throw", err);
13045
13045
  }
13046
13046
  _next(undefined);
13047
13047
  });
@@ -13755,7 +13755,7 @@ var require_dateparts = __commonJS((exports, module) => {
13755
13755
 
13756
13756
  // ../../node_modules/.bun/prompts@2.4.2/node_modules/prompts/dist/elements/date.js
13757
13757
  var require_date = __commonJS((exports, module) => {
13758
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
13758
+ function asyncGeneratorStep(gen, resolve2, reject, _next, _throw, key, arg) {
13759
13759
  try {
13760
13760
  var info = gen[key](arg);
13761
13761
  var value = info.value;
@@ -13764,7 +13764,7 @@ var require_date = __commonJS((exports, module) => {
13764
13764
  return;
13765
13765
  }
13766
13766
  if (info.done) {
13767
- resolve(value);
13767
+ resolve2(value);
13768
13768
  } else {
13769
13769
  Promise.resolve(value).then(_next, _throw);
13770
13770
  }
@@ -13772,13 +13772,13 @@ var require_date = __commonJS((exports, module) => {
13772
13772
  function _asyncToGenerator(fn) {
13773
13773
  return function() {
13774
13774
  var self = this, args = arguments;
13775
- return new Promise(function(resolve, reject) {
13775
+ return new Promise(function(resolve2, reject) {
13776
13776
  var gen = fn.apply(self, args);
13777
13777
  function _next(value) {
13778
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
13778
+ asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "next", value);
13779
13779
  }
13780
13780
  function _throw(err) {
13781
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
13781
+ asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "throw", err);
13782
13782
  }
13783
13783
  _next(undefined);
13784
13784
  });
@@ -13982,7 +13982,7 @@ ${i ? ` ` : figures.pointerSmall} ${color.red().italic(l)}`, ``);
13982
13982
 
13983
13983
  // ../../node_modules/.bun/prompts@2.4.2/node_modules/prompts/dist/elements/number.js
13984
13984
  var require_number = __commonJS((exports, module) => {
13985
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
13985
+ function asyncGeneratorStep(gen, resolve2, reject, _next, _throw, key, arg) {
13986
13986
  try {
13987
13987
  var info = gen[key](arg);
13988
13988
  var value = info.value;
@@ -13991,7 +13991,7 @@ var require_number = __commonJS((exports, module) => {
13991
13991
  return;
13992
13992
  }
13993
13993
  if (info.done) {
13994
- resolve(value);
13994
+ resolve2(value);
13995
13995
  } else {
13996
13996
  Promise.resolve(value).then(_next, _throw);
13997
13997
  }
@@ -13999,13 +13999,13 @@ var require_number = __commonJS((exports, module) => {
13999
13999
  function _asyncToGenerator(fn) {
14000
14000
  return function() {
14001
14001
  var self = this, args = arguments;
14002
- return new Promise(function(resolve, reject) {
14002
+ return new Promise(function(resolve2, reject) {
14003
14003
  var gen = fn.apply(self, args);
14004
14004
  function _next(value) {
14005
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
14005
+ asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "next", value);
14006
14006
  }
14007
14007
  function _throw(err) {
14008
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
14008
+ asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "throw", err);
14009
14009
  }
14010
14010
  _next(undefined);
14011
14011
  });
@@ -14441,7 +14441,7 @@ Instructions:
14441
14441
 
14442
14442
  // ../../node_modules/.bun/prompts@2.4.2/node_modules/prompts/dist/elements/autocomplete.js
14443
14443
  var require_autocomplete = __commonJS((exports, module) => {
14444
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
14444
+ function asyncGeneratorStep(gen, resolve2, reject, _next, _throw, key, arg) {
14445
14445
  try {
14446
14446
  var info = gen[key](arg);
14447
14447
  var value = info.value;
@@ -14450,7 +14450,7 @@ var require_autocomplete = __commonJS((exports, module) => {
14450
14450
  return;
14451
14451
  }
14452
14452
  if (info.done) {
14453
- resolve(value);
14453
+ resolve2(value);
14454
14454
  } else {
14455
14455
  Promise.resolve(value).then(_next, _throw);
14456
14456
  }
@@ -14458,13 +14458,13 @@ var require_autocomplete = __commonJS((exports, module) => {
14458
14458
  function _asyncToGenerator(fn) {
14459
14459
  return function() {
14460
14460
  var self = this, args = arguments;
14461
- return new Promise(function(resolve, reject) {
14461
+ return new Promise(function(resolve2, reject) {
14462
14462
  var gen = fn.apply(self, args);
14463
14463
  function _next(value) {
14464
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
14464
+ asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "next", value);
14465
14465
  }
14466
14466
  function _throw(err) {
14467
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
14467
+ asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "throw", err);
14468
14468
  }
14469
14469
  _next(undefined);
14470
14470
  });
@@ -15122,7 +15122,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
15122
15122
  arr2[i] = arr[i];
15123
15123
  return arr2;
15124
15124
  }
15125
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
15125
+ function asyncGeneratorStep(gen, resolve2, reject, _next, _throw, key, arg) {
15126
15126
  try {
15127
15127
  var info = gen[key](arg);
15128
15128
  var value = info.value;
@@ -15131,7 +15131,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
15131
15131
  return;
15132
15132
  }
15133
15133
  if (info.done) {
15134
- resolve(value);
15134
+ resolve2(value);
15135
15135
  } else {
15136
15136
  Promise.resolve(value).then(_next, _throw);
15137
15137
  }
@@ -15139,13 +15139,13 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
15139
15139
  function _asyncToGenerator(fn) {
15140
15140
  return function() {
15141
15141
  var self = this, args = arguments;
15142
- return new Promise(function(resolve, reject) {
15142
+ return new Promise(function(resolve2, reject) {
15143
15143
  var gen = fn.apply(self, args);
15144
15144
  function _next(value) {
15145
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
15145
+ asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "next", value);
15146
15146
  }
15147
15147
  function _throw(err) {
15148
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
15148
+ asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "throw", err);
15149
15149
  }
15150
15150
  _next(undefined);
15151
15151
  });
@@ -19926,7 +19926,7 @@ class Conf {
19926
19926
 
19927
19927
  // src/lib/config.ts
19928
19928
  import { homedir as homedir2 } from "os";
19929
- import { join } from "path";
19929
+ import { join, dirname, resolve } from "path";
19930
19930
  import { existsSync, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
19931
19931
  var defaultConfig = {
19932
19932
  version: "0.1.0",
@@ -20014,13 +20014,16 @@ function ensureSkillsDir(dir) {
20014
20014
  }
20015
20015
  }
20016
20016
  function findProjectRoot(startDir = process.cwd()) {
20017
- let currentDir = startDir;
20018
- const root = "/";
20019
- while (currentDir !== root) {
20017
+ let currentDir = resolve(startDir);
20018
+ while (true) {
20020
20019
  if (existsSync(join(currentDir, LOCAL_CONFIG_DIR, LOCAL_CONFIG_FILE))) {
20021
20020
  return currentDir;
20022
20021
  }
20023
- currentDir = join(currentDir, "..");
20022
+ const parentDir = dirname(currentDir);
20023
+ if (parentDir === currentDir) {
20024
+ break;
20025
+ }
20026
+ currentDir = parentDir;
20024
20027
  }
20025
20028
  return null;
20026
20029
  }
@@ -20031,6 +20034,23 @@ function getProjectSkillsOutputDir(startDir = process.cwd()) {
20031
20034
  }
20032
20035
  return null;
20033
20036
  }
20037
+ function isValidSlug(slug) {
20038
+ if (!slug || typeof slug !== "string") {
20039
+ return false;
20040
+ }
20041
+ const validSlugPattern = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
20042
+ return validSlugPattern.test(slug) && slug.length <= 64;
20043
+ }
20044
+ function getSkillsDir(options = {}) {
20045
+ const target = options.target ?? getDefaultTarget();
20046
+ if (options.local) {
20047
+ const projectRoot = findProjectRoot();
20048
+ if (projectRoot) {
20049
+ return target === "claude" ? getProjectClaudeSkillsDir(projectRoot) : getProjectCodexSkillsDir(projectRoot);
20050
+ }
20051
+ }
20052
+ return target === "claude" ? getClaudeSkillsDir() : getCodexSkillsDir();
20053
+ }
20034
20054
 
20035
20055
  // src/commands/init.ts
20036
20056
  async function initCommand(options = {}) {
@@ -20557,19 +20577,19 @@ var baseOpen = async (options) => {
20557
20577
  }
20558
20578
  const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
20559
20579
  if (options.wait) {
20560
- return new Promise((resolve, reject) => {
20580
+ return new Promise((resolve2, reject) => {
20561
20581
  subprocess.once("error", reject);
20562
20582
  subprocess.once("close", (exitCode) => {
20563
20583
  if (!options.allowNonzeroExitCode && exitCode !== 0) {
20564
20584
  reject(new Error(`Exited with code ${exitCode}`));
20565
20585
  return;
20566
20586
  }
20567
- resolve(subprocess);
20587
+ resolve2(subprocess);
20568
20588
  });
20569
20589
  });
20570
20590
  }
20571
20591
  if (isFallbackAttempt) {
20572
- return new Promise((resolve, reject) => {
20592
+ return new Promise((resolve2, reject) => {
20573
20593
  subprocess.once("error", reject);
20574
20594
  subprocess.once("spawn", () => {
20575
20595
  subprocess.once("close", (exitCode) => {
@@ -20579,17 +20599,17 @@ var baseOpen = async (options) => {
20579
20599
  return;
20580
20600
  }
20581
20601
  subprocess.unref();
20582
- resolve(subprocess);
20602
+ resolve2(subprocess);
20583
20603
  });
20584
20604
  });
20585
20605
  });
20586
20606
  }
20587
20607
  subprocess.unref();
20588
- return new Promise((resolve, reject) => {
20608
+ return new Promise((resolve2, reject) => {
20589
20609
  subprocess.once("error", reject);
20590
20610
  subprocess.once("spawn", () => {
20591
20611
  subprocess.off("error", reject);
20592
- resolve(subprocess);
20612
+ resolve2(subprocess);
20593
20613
  });
20594
20614
  });
20595
20615
  };
@@ -20675,25 +20695,40 @@ async function apiRequest(path6, options = {}) {
20675
20695
  if (apiKey) {
20676
20696
  headers["Authorization"] = `Bearer ${apiKey}`;
20677
20697
  }
20698
+ let response;
20678
20699
  try {
20679
- const response = await fetch(`${endpoint}${path6}`, {
20700
+ response = await fetch(`${endpoint}${path6}`, {
20680
20701
  ...options,
20681
20702
  headers
20682
20703
  });
20683
- const data = await response.json();
20704
+ } catch (error) {
20705
+ return {
20706
+ error: error instanceof Error ? error.message : "Network error",
20707
+ status: 0
20708
+ };
20709
+ }
20710
+ let data;
20711
+ try {
20712
+ data = await response.json();
20713
+ } catch {
20684
20714
  if (!response.ok) {
20685
20715
  return {
20686
- error: data.error || "Request failed",
20716
+ error: `Server error: ${response.status} ${response.statusText}`,
20687
20717
  status: response.status
20688
20718
  };
20689
20719
  }
20690
- return { data, status: response.status };
20691
- } catch (error) {
20692
20720
  return {
20693
- error: error instanceof Error ? error.message : "Network error",
20694
- status: 0
20721
+ error: "Invalid response from server",
20722
+ status: response.status
20723
+ };
20724
+ }
20725
+ if (!response.ok) {
20726
+ return {
20727
+ error: data.error || `Request failed: ${response.status}`,
20728
+ status: response.status
20695
20729
  };
20696
20730
  }
20731
+ return { data, status: response.status };
20697
20732
  }
20698
20733
  async function searchSkills(query) {
20699
20734
  return apiRequest(`/skills?q=${encodeURIComponent(query)}`);
@@ -20706,9 +20741,27 @@ async function getMarketplaceSkills(options) {
20706
20741
  params.set("page", options.page.toString());
20707
20742
  if (options?.category)
20708
20743
  params.set("category", options.category);
20744
+ if (options?.tag)
20745
+ params.set("tag", options.tag);
20709
20746
  const query = params.toString();
20710
20747
  return apiRequest(`/skills${query ? `?${query}` : ""}`);
20711
20748
  }
20749
+ async function getSkillsByTag(tag) {
20750
+ const allSkills = [];
20751
+ let page = 1;
20752
+ const limit = 100;
20753
+ let totalPages = 1;
20754
+ while (page <= totalPages) {
20755
+ const result = await getMarketplaceSkills({ page, limit, tag });
20756
+ if (result.error || !result.data) {
20757
+ return { error: result.error || "Failed to fetch skills", status: result.status };
20758
+ }
20759
+ allSkills.push(...result.data.skills);
20760
+ totalPages = result.data.pagination.totalPages;
20761
+ page++;
20762
+ }
20763
+ return { data: { skills: allSkills, total: allSkills.length }, status: 200 };
20764
+ }
20712
20765
  async function getAllMarketplaceSkills() {
20713
20766
  const allSkills = [];
20714
20767
  let page = 1;
@@ -21031,7 +21084,7 @@ async function whoamiCommand() {
21031
21084
  console.log();
21032
21085
  }
21033
21086
  function sleep(ms) {
21034
- return new Promise((resolve) => setTimeout(resolve, ms));
21087
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
21035
21088
  }
21036
21089
 
21037
21090
  // src/commands/search.ts
@@ -21179,10 +21232,14 @@ async function installCommand(slug, options = {}) {
21179
21232
  if (options.all) {
21180
21233
  return installAllSkills(options);
21181
21234
  }
21235
+ if (options.tag) {
21236
+ return installSkillsByTag(options.tag, options);
21237
+ }
21182
21238
  if (!slug || slug.trim() === "") {
21183
21239
  console.log(colors.warning("Please provide a skill name"));
21184
21240
  console.log(colors.dim("Usage: skills install <name>"));
21185
21241
  console.log(colors.dim(" skills install --all"));
21242
+ console.log(colors.dim(" skills install --tag <tag-name>"));
21186
21243
  return;
21187
21244
  }
21188
21245
  const isLocal = options.local ?? false;
@@ -21345,6 +21402,141 @@ async function installAllSkills(options) {
21345
21402
  console.error(colors.error(error instanceof Error ? error.message : "Unknown error"));
21346
21403
  }
21347
21404
  }
21405
+ async function installSkillsByTag(tag, options) {
21406
+ const isLocal = options.local ?? false;
21407
+ const target = options.target ?? getDefaultTarget();
21408
+ const apiKey = getApiKey();
21409
+ let installDir;
21410
+ if (isLocal) {
21411
+ const projectRoot = findProjectRoot();
21412
+ if (!projectRoot) {
21413
+ console.log(colors.warning("Not in a skills.md project"));
21414
+ console.log(colors.dim("Run `skills init` first or install globally (default)"));
21415
+ return;
21416
+ }
21417
+ installDir = target === "claude" ? getProjectClaudeSkillsDir(projectRoot) : getProjectCodexSkillsDir(projectRoot);
21418
+ } else {
21419
+ installDir = target === "claude" ? getClaudeSkillsDir() : getCodexSkillsDir();
21420
+ }
21421
+ banner(`Installing Skills by Tag: ${tag}`);
21422
+ const fetchSpinner = ora({
21423
+ text: `Fetching skills with tag "${tag}"...`,
21424
+ color: "magenta"
21425
+ }).start();
21426
+ try {
21427
+ const result = await getSkillsByTag(tag);
21428
+ if (result.error || !result.data) {
21429
+ fetchSpinner.fail(colors.error("Failed to fetch skills"));
21430
+ console.error(colors.error(result.error || "Could not fetch skills by tag"));
21431
+ return;
21432
+ }
21433
+ const skills = result.data.skills;
21434
+ const total = skills.length;
21435
+ if (total === 0) {
21436
+ fetchSpinner.warn(colors.warning(`No skills found with tag "${tag}"`));
21437
+ console.log();
21438
+ console.log(colors.dim("Try searching for available tags in the marketplace:"));
21439
+ console.log(` ${command("skills marketplace")}`);
21440
+ return;
21441
+ }
21442
+ fetchSpinner.succeed(colors.success(`Found ${count(total)} skill${total !== 1 ? "s" : ""} with tag "${tag}"`));
21443
+ console.log();
21444
+ console.log(keyValue("Tag", colors.primaryBold(tag)));
21445
+ console.log(keyValue("Target", colors.primaryBold(target)));
21446
+ console.log(keyValue("Scope", colors.primaryBold(isLocal ? "project" : "global")));
21447
+ console.log(keyValue("Directory", path6(installDir)));
21448
+ console.log();
21449
+ console.log(colors.dim("Skills to install:"));
21450
+ const displayCount = Math.min(skills.length, 15);
21451
+ for (let i = 0;i < displayCount; i++) {
21452
+ console.log(` ${colors.primary(symbols.bullet)} ${skillName(skills[i].name)} ${colors.dim(`(${skills[i].slug})`)}`);
21453
+ }
21454
+ if (skills.length > displayCount) {
21455
+ console.log(colors.dim(` ... and ${skills.length - displayCount} more`));
21456
+ }
21457
+ console.log();
21458
+ if (!options.force) {
21459
+ const prompts2 = (await Promise.resolve().then(() => __toESM(require_prompts3(), 1))).default;
21460
+ const response = await prompts2({
21461
+ type: "confirm",
21462
+ name: "confirm",
21463
+ message: `Install ${total} skill${total !== 1 ? "s" : ""} with tag "${tag}"?`,
21464
+ initial: true
21465
+ });
21466
+ if (!response.confirm) {
21467
+ console.log(colors.dim("Cancelled"));
21468
+ return;
21469
+ }
21470
+ console.log();
21471
+ }
21472
+ const results = {
21473
+ success: [],
21474
+ failed: [],
21475
+ skipped: []
21476
+ };
21477
+ for (let i = 0;i < skills.length; i++) {
21478
+ const skill = skills[i];
21479
+ const current = i + 1;
21480
+ const percent = Math.round(current / total * 100);
21481
+ const padWidth = String(total).length;
21482
+ const progress = `[${String(current).padStart(padWidth, " ")}/${total}]`;
21483
+ process.stdout.write(`\r${colors.dim(progress)} ${progressBar(percent)} ${colors.dim(`${percent}%`)} Installing ${colors.primary(skill.slug)}...`);
21484
+ process.stdout.write("\x1B[K");
21485
+ try {
21486
+ const skillResult = await installSkill(skill.slug);
21487
+ if (skillResult.error || !skillResult.data) {
21488
+ results.failed.push({ slug: skill.slug, error: skillResult.error || "Not found" });
21489
+ continue;
21490
+ }
21491
+ const skillData = skillResult.data;
21492
+ const skillDir = join3(installDir, `skill-${skill.slug}`);
21493
+ const exportsDir = join3(skillDir, "exports");
21494
+ const logsDir = join3(skillDir, "logs");
21495
+ ensureSkillsDir(skillDir);
21496
+ ensureSkillsDir(exportsDir);
21497
+ ensureSkillsDir(logsDir);
21498
+ writeFileSync3(join3(skillDir, "SKILL.md"), addSkillsMdMarker(skillData.skillMdContent));
21499
+ if (apiKey) {
21500
+ try {
21501
+ await installSkillRemote(skill.slug);
21502
+ } catch {}
21503
+ }
21504
+ results.success.push(skill.slug);
21505
+ } catch (error) {
21506
+ results.failed.push({
21507
+ slug: skill.slug,
21508
+ error: error instanceof Error ? error.message : "Unknown error"
21509
+ });
21510
+ }
21511
+ }
21512
+ process.stdout.write("\r\x1B[K");
21513
+ completionBanner("Installation Complete");
21514
+ if (results.success.length > 0) {
21515
+ console.log(successItem(`Successfully installed: ${colors.successBold(String(results.success.length))} skills`));
21516
+ }
21517
+ if (results.failed.length > 0) {
21518
+ console.log(errorItem(`Failed to install: ${colors.errorBold(String(results.failed.length))} skills`));
21519
+ console.log();
21520
+ console.log(colors.dim("Failed skills:"));
21521
+ for (const fail of results.failed.slice(0, 10)) {
21522
+ console.log(colors.error(` ${symbols.bullet} ${fail.slug}: ${fail.error}`));
21523
+ }
21524
+ if (results.failed.length > 10) {
21525
+ console.log(colors.dim(` ... and ${results.failed.length - 10} more`));
21526
+ }
21527
+ }
21528
+ console.log();
21529
+ console.log(colors.dim("Installation directory:"));
21530
+ console.log(` ${path6(installDir)}`);
21531
+ console.log();
21532
+ console.log(colors.dim("Run any skill with:"));
21533
+ console.log(` ${command('skills run <skill-name> -- "your prompt"')}`);
21534
+ console.log();
21535
+ } catch (error) {
21536
+ fetchSpinner.fail(colors.error("Installation failed"));
21537
+ console.error(colors.error(error instanceof Error ? error.message : "Unknown error"));
21538
+ }
21539
+ }
21348
21540
  function getInstalledSkillsWithStatus(installDir) {
21349
21541
  const { readdirSync, statSync } = __require("fs");
21350
21542
  if (!existsSync3(installDir)) {
@@ -21654,7 +21846,7 @@ async function uninstallAllSkills(installDir, target, isLocal, force) {
21654
21846
 
21655
21847
  // src/commands/download.ts
21656
21848
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
21657
- import { join as join4, normalize, basename, dirname, isAbsolute } from "path";
21849
+ import { join as join4, normalize, basename, dirname as dirname2, isAbsolute } from "path";
21658
21850
  var indigo2 = source_default.hex("#6366f1");
21659
21851
  function validateFilename(filename) {
21660
21852
  if (!filename || filename.trim() === "") {
@@ -21793,7 +21985,7 @@ async function downloadCommand(slug, options = {}) {
21793
21985
  filesSkipped++;
21794
21986
  continue;
21795
21987
  }
21796
- const parentDir = dirname(safePath);
21988
+ const parentDir = dirname2(safePath);
21797
21989
  if (!existsSync4(parentDir)) {
21798
21990
  mkdirSync3(parentDir, { recursive: true });
21799
21991
  }
@@ -22352,21 +22544,25 @@ function findLocalSkill(skillName2) {
22352
22544
  }
22353
22545
  return null;
22354
22546
  }
22547
+ function escapeShellArg(arg) {
22548
+ return `'${arg.replace(/'/g, "'\\''")}'`;
22549
+ }
22355
22550
  async function runLocalSkill(skillDir, args) {
22356
- return new Promise((resolve) => {
22551
+ return new Promise((resolve2) => {
22357
22552
  const skillMdPath = join7(skillDir, "SKILL.md");
22358
22553
  if (!existsSync7(skillMdPath)) {
22359
- resolve(false);
22554
+ resolve2(false);
22360
22555
  return;
22361
22556
  }
22362
22557
  const content = __require("fs").readFileSync(skillMdPath, "utf-8");
22363
22558
  const match = content.match(/scripts:\s*\n\s*run:\s*(.+)/);
22364
22559
  if (!match) {
22365
- resolve(false);
22560
+ resolve2(false);
22366
22561
  return;
22367
22562
  }
22368
22563
  const runScript = match[1].trim();
22369
- const fullCommand = args.length > 0 ? `${runScript} ${args.join(" ")}` : runScript;
22564
+ const escapedArgs = args.map(escapeShellArg);
22565
+ const fullCommand = args.length > 0 ? `${runScript} ${escapedArgs.join(" ")}` : runScript;
22370
22566
  const projectRoot = findProjectRoot();
22371
22567
  const skillsOutputDir = projectRoot ? join7(projectRoot, ".skills") : null;
22372
22568
  const child = spawn(fullCommand, {
@@ -22381,8 +22577,11 @@ async function runLocalSkill(skillDir, args) {
22381
22577
  SKILLS_OUTPUT_DIR: skillsOutputDir || ""
22382
22578
  }
22383
22579
  });
22384
- child.on("error", () => resolve(false));
22385
- child.on("exit", (code) => resolve(code === 0));
22580
+ child.on("error", (err) => {
22581
+ console.error("Skill execution error:", err.message);
22582
+ resolve2(false);
22583
+ });
22584
+ child.on("exit", (code) => resolve2(code === 0));
22386
22585
  });
22387
22586
  }
22388
22587
  async function generateCommand(mediaType, prompt, options = {}) {
@@ -22729,8 +22928,12 @@ async function localHistoryCommand(options) {
22729
22928
  }
22730
22929
  }
22731
22930
  async function exportsCommand(slug, options = {}) {
22732
- const target = options.target || getDefaultTarget();
22733
- const skillsDir = target === "claude" ? getProjectClaudeSkillsDir() : getProjectCodexSkillsDir();
22931
+ if (!isValidSlug(slug)) {
22932
+ console.log(source_default.red(`Invalid skill name: "${slug}"`));
22933
+ console.log(source_default.dim("Skill names can only contain letters, numbers, hyphens, and underscores"));
22934
+ return;
22935
+ }
22936
+ const skillsDir = getSkillsDir({ target: options.target });
22734
22937
  let skillDir = join8(skillsDir, `skill-${slug}`);
22735
22938
  if (!existsSync8(skillDir)) {
22736
22939
  skillDir = join8(skillsDir, slug);
@@ -22769,8 +22972,12 @@ async function exportsCommand(slug, options = {}) {
22769
22972
  console.log(source_default.dim(`Total: ${files.length} file(s)`));
22770
22973
  }
22771
22974
  async function logsCommand(slug, options = {}) {
22772
- const target = options.target || getDefaultTarget();
22773
- const skillsDir = target === "claude" ? getProjectClaudeSkillsDir() : getProjectCodexSkillsDir();
22975
+ if (!isValidSlug(slug)) {
22976
+ console.log(source_default.red(`Invalid skill name: "${slug}"`));
22977
+ console.log(source_default.dim("Skill names can only contain letters, numbers, hyphens, and underscores"));
22978
+ return;
22979
+ }
22980
+ const skillsDir = getSkillsDir({ target: options.target });
22774
22981
  let skillDir = join8(skillsDir, `skill-${slug}`);
22775
22982
  if (!existsSync8(skillDir)) {
22776
22983
  skillDir = join8(skillsDir, slug);
@@ -23868,7 +24075,7 @@ async function scheduleListCommand(options = {}) {
23868
24075
  printScheduleSummary(schedule);
23869
24076
  }
23870
24077
  console.log();
23871
- console.log(source_default.dim("View all at: https://skills.md/dashboard/schedules"));
24078
+ console.log(source_default.dim("View all at: https://skills.md (go to your team dashboard)"));
23872
24079
  } catch (error) {
23873
24080
  spinner.fail("Request failed");
23874
24081
  console.log(source_default.red(error instanceof Error ? error.message : "Unknown error"));
@@ -24161,7 +24368,7 @@ function formatDuration3(ms) {
24161
24368
  // src/index.ts
24162
24369
  var indigo13 = colors.primary;
24163
24370
  var program2 = new Command;
24164
- program2.name("skills").description("CLI for skills.md - AI Agent Skills Marketplace").version("0.1.38");
24371
+ program2.name("skills").description("CLI for skills.md - AI Agent Skills Marketplace").version("0.1.39");
24165
24372
  program2.command("init").description("Initialize skills.md in current project").option("-f, --force", "Force re-initialization (removes existing .skills/)").action((options) => {
24166
24373
  initCommand({ force: options.force });
24167
24374
  });
@@ -24178,11 +24385,13 @@ program2.command("marketplace").alias("market").description("Browse skills from
24178
24385
  category: options.category
24179
24386
  });
24180
24387
  });
24181
- program2.command("install [name]").alias("i").description("Install a skill or all skills (global by default)").option("-l, --local", "Install to current project instead of global").option("-t, --target <target>", "Target platform (claude, codex)").option("-a, --all", "Install all skills from marketplace").action((name, options) => {
24388
+ program2.command("install [name]").alias("i").description("Install a skill, all skills, or skills by tag (global by default)").option("-l, --local", "Install to current project instead of global").option("-t, --target <target>", "Target platform (claude, codex)").option("-a, --all", "Install all skills from marketplace").option("--tag <tag>", "Install all skills with a specific tag").option("-f, --force", "Skip confirmation prompt").action((name, options) => {
24182
24389
  installCommand(name || "", {
24183
24390
  local: options.local,
24184
24391
  target: options.target,
24185
- all: options.all
24392
+ all: options.all,
24393
+ tag: options.tag,
24394
+ force: options.force
24186
24395
  });
24187
24396
  });
24188
24397
  program2.command("uninstall [name]").alias("remove").description("Uninstall skill(s) - single, comma-separated, or all").option("-l, --local", "Uninstall from current project instead of global").option("-t, --target <target>", "Target platform (claude, codex)").option("-a, --all", "Uninstall all installed skills").option("-f, --force", "Skip confirmation prompt").action((name, options) => {
@@ -24314,6 +24523,10 @@ ${indigo13.bold("Examples:")}
24314
24523
  ${source_default.dim("# Install globally for Claude Code")}
24315
24524
  $ skills install code-review -g -t claude
24316
24525
 
24526
+ ${source_default.dim("# Install all skills with a tag")}
24527
+ $ skills install --tag automation
24528
+ $ skills install --tag ai -f ${source_default.dim("# skip confirmation")}
24529
+
24317
24530
  ${source_default.dim("# AI-powered setup wizard")}
24318
24531
  $ skills setup
24319
24532
  $ skills setup "SaaS application with API"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasnatools/skills",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
4
4
  "description": "CLI for skills.md - AI Agent Skills Marketplace",
5
5
  "type": "module",
6
6
  "bin": {