@hasnatools/skills 0.1.37 → 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 +739 -63
  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;
@@ -20793,6 +20846,52 @@ async function makeApiRequest(path6, options = {}) {
20793
20846
  headers
20794
20847
  });
20795
20848
  }
20849
+ async function listSchedules(options) {
20850
+ const params = new URLSearchParams;
20851
+ if (options?.status)
20852
+ params.set("status", options.status);
20853
+ if (options?.skill)
20854
+ params.set("skill", options.skill);
20855
+ if (options?.search)
20856
+ params.set("search", options.search);
20857
+ if (options?.limit)
20858
+ params.set("limit", options.limit.toString());
20859
+ if (options?.offset)
20860
+ params.set("offset", options.offset.toString());
20861
+ const query = params.toString();
20862
+ return apiRequest(`/schedules${query ? `?${query}` : ""}`);
20863
+ }
20864
+ async function getSchedule(id) {
20865
+ return apiRequest(`/schedules/${id}`);
20866
+ }
20867
+ async function createSchedule(input) {
20868
+ return apiRequest("/schedules", {
20869
+ method: "POST",
20870
+ body: JSON.stringify(input)
20871
+ });
20872
+ }
20873
+ async function updateSchedule(id, input) {
20874
+ return apiRequest(`/schedules/${id}`, {
20875
+ method: "PATCH",
20876
+ body: JSON.stringify(input)
20877
+ });
20878
+ }
20879
+ async function deleteSchedule(id) {
20880
+ return apiRequest(`/schedules/${id}`, {
20881
+ method: "DELETE"
20882
+ });
20883
+ }
20884
+ async function getScheduleRuns(id, options) {
20885
+ const params = new URLSearchParams;
20886
+ if (options?.status)
20887
+ params.set("status", options.status);
20888
+ if (options?.limit)
20889
+ params.set("limit", options.limit.toString());
20890
+ if (options?.offset)
20891
+ params.set("offset", options.offset.toString());
20892
+ const query = params.toString();
20893
+ return apiRequest(`/schedules/${id}/runs${query ? `?${query}` : ""}`);
20894
+ }
20796
20895
 
20797
20896
  // src/commands/login.ts
20798
20897
  async function loginCommand(options = {}) {
@@ -20985,7 +21084,7 @@ async function whoamiCommand() {
20985
21084
  console.log();
20986
21085
  }
20987
21086
  function sleep(ms) {
20988
- return new Promise((resolve) => setTimeout(resolve, ms));
21087
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
20989
21088
  }
20990
21089
 
20991
21090
  // src/commands/search.ts
@@ -21133,10 +21232,14 @@ async function installCommand(slug, options = {}) {
21133
21232
  if (options.all) {
21134
21233
  return installAllSkills(options);
21135
21234
  }
21235
+ if (options.tag) {
21236
+ return installSkillsByTag(options.tag, options);
21237
+ }
21136
21238
  if (!slug || slug.trim() === "") {
21137
21239
  console.log(colors.warning("Please provide a skill name"));
21138
21240
  console.log(colors.dim("Usage: skills install <name>"));
21139
21241
  console.log(colors.dim(" skills install --all"));
21242
+ console.log(colors.dim(" skills install --tag <tag-name>"));
21140
21243
  return;
21141
21244
  }
21142
21245
  const isLocal = options.local ?? false;
@@ -21299,6 +21402,141 @@ async function installAllSkills(options) {
21299
21402
  console.error(colors.error(error instanceof Error ? error.message : "Unknown error"));
21300
21403
  }
21301
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
+ }
21302
21540
  function getInstalledSkillsWithStatus(installDir) {
21303
21541
  const { readdirSync, statSync } = __require("fs");
21304
21542
  if (!existsSync3(installDir)) {
@@ -21608,7 +21846,7 @@ async function uninstallAllSkills(installDir, target, isLocal, force) {
21608
21846
 
21609
21847
  // src/commands/download.ts
21610
21848
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
21611
- import { join as join4, normalize, basename, dirname, isAbsolute } from "path";
21849
+ import { join as join4, normalize, basename, dirname as dirname2, isAbsolute } from "path";
21612
21850
  var indigo2 = source_default.hex("#6366f1");
21613
21851
  function validateFilename(filename) {
21614
21852
  if (!filename || filename.trim() === "") {
@@ -21747,7 +21985,7 @@ async function downloadCommand(slug, options = {}) {
21747
21985
  filesSkipped++;
21748
21986
  continue;
21749
21987
  }
21750
- const parentDir = dirname(safePath);
21988
+ const parentDir = dirname2(safePath);
21751
21989
  if (!existsSync4(parentDir)) {
21752
21990
  mkdirSync3(parentDir, { recursive: true });
21753
21991
  }
@@ -22306,21 +22544,25 @@ function findLocalSkill(skillName2) {
22306
22544
  }
22307
22545
  return null;
22308
22546
  }
22547
+ function escapeShellArg(arg) {
22548
+ return `'${arg.replace(/'/g, "'\\''")}'`;
22549
+ }
22309
22550
  async function runLocalSkill(skillDir, args) {
22310
- return new Promise((resolve) => {
22551
+ return new Promise((resolve2) => {
22311
22552
  const skillMdPath = join7(skillDir, "SKILL.md");
22312
22553
  if (!existsSync7(skillMdPath)) {
22313
- resolve(false);
22554
+ resolve2(false);
22314
22555
  return;
22315
22556
  }
22316
22557
  const content = __require("fs").readFileSync(skillMdPath, "utf-8");
22317
22558
  const match = content.match(/scripts:\s*\n\s*run:\s*(.+)/);
22318
22559
  if (!match) {
22319
- resolve(false);
22560
+ resolve2(false);
22320
22561
  return;
22321
22562
  }
22322
22563
  const runScript = match[1].trim();
22323
- 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;
22324
22566
  const projectRoot = findProjectRoot();
22325
22567
  const skillsOutputDir = projectRoot ? join7(projectRoot, ".skills") : null;
22326
22568
  const child = spawn(fullCommand, {
@@ -22335,8 +22577,11 @@ async function runLocalSkill(skillDir, args) {
22335
22577
  SKILLS_OUTPUT_DIR: skillsOutputDir || ""
22336
22578
  }
22337
22579
  });
22338
- child.on("error", () => resolve(false));
22339
- 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));
22340
22585
  });
22341
22586
  }
22342
22587
  async function generateCommand(mediaType, prompt, options = {}) {
@@ -22683,8 +22928,12 @@ async function localHistoryCommand(options) {
22683
22928
  }
22684
22929
  }
22685
22930
  async function exportsCommand(slug, options = {}) {
22686
- const target = options.target || getDefaultTarget();
22687
- 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 });
22688
22937
  let skillDir = join8(skillsDir, `skill-${slug}`);
22689
22938
  if (!existsSync8(skillDir)) {
22690
22939
  skillDir = join8(skillsDir, slug);
@@ -22723,8 +22972,12 @@ async function exportsCommand(slug, options = {}) {
22723
22972
  console.log(source_default.dim(`Total: ${files.length} file(s)`));
22724
22973
  }
22725
22974
  async function logsCommand(slug, options = {}) {
22726
- const target = options.target || getDefaultTarget();
22727
- 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 });
22728
22981
  let skillDir = join8(skillsDir, `skill-${slug}`);
22729
22982
  if (!existsSync8(skillDir)) {
22730
22983
  skillDir = join8(skillsDir, slug);
@@ -23739,10 +23992,383 @@ async function setupCommand(promptArg, options = {}) {
23739
23992
  console.log(` ${command("skills list")}`);
23740
23993
  }
23741
23994
 
23995
+ // src/commands/schedule.ts
23996
+ var indigo12 = source_default.hex("#6366f1");
23997
+ async function scheduleCreateCommand(skill, scheduleInput, options) {
23998
+ const apiKey = getApiKey();
23999
+ if (!apiKey) {
24000
+ console.log(source_default.red("Not logged in"));
24001
+ console.log(source_default.dim("Run `skills login` first"));
24002
+ return;
24003
+ }
24004
+ let scheduleConfig = {};
24005
+ if (options.cron) {
24006
+ scheduleConfig.cronExpression = options.cron;
24007
+ } else if (options.at) {
24008
+ scheduleConfig.scheduledAt = options.at;
24009
+ } else if (scheduleInput) {
24010
+ scheduleConfig.schedule = scheduleInput;
24011
+ } else {
24012
+ console.log(source_default.red("Please provide a schedule"));
24013
+ console.log(source_default.dim("Examples:"));
24014
+ console.log(source_default.dim(' skills schedule <skill> "every Monday at 9am"'));
24015
+ console.log(source_default.dim(' skills schedule <skill> --cron "0 9 * * 1"'));
24016
+ console.log(source_default.dim(' skills schedule <skill> --at "2025-01-15T09:00:00"'));
24017
+ return;
24018
+ }
24019
+ const spinner = ora("Creating schedule...").start();
24020
+ try {
24021
+ const result = await createSchedule({
24022
+ skillSlug: skill,
24023
+ name: options.name || `${skill} schedule`,
24024
+ ...scheduleConfig,
24025
+ command: options.command,
24026
+ timezone: options.timezone || "UTC",
24027
+ maxRuns: options.maxRuns
24028
+ });
24029
+ if (result.error) {
24030
+ spinner.fail("Failed to create schedule");
24031
+ console.log(source_default.red(result.error));
24032
+ return;
24033
+ }
24034
+ spinner.succeed("Schedule created successfully");
24035
+ console.log();
24036
+ const schedule = result.data?.schedule;
24037
+ if (schedule) {
24038
+ printScheduleDetails(schedule);
24039
+ }
24040
+ } catch (error) {
24041
+ spinner.fail("Request failed");
24042
+ console.log(source_default.red(error instanceof Error ? error.message : "Unknown error"));
24043
+ }
24044
+ }
24045
+ async function scheduleListCommand(options = {}) {
24046
+ const apiKey = getApiKey();
24047
+ if (!apiKey) {
24048
+ console.log(source_default.red("Not logged in"));
24049
+ console.log(source_default.dim("Run `skills login` first"));
24050
+ return;
24051
+ }
24052
+ const spinner = ora("Fetching schedules...").start();
24053
+ try {
24054
+ const result = await listSchedules({
24055
+ status: options.status,
24056
+ skill: options.skill,
24057
+ limit: options.limit || 20
24058
+ });
24059
+ spinner.stop();
24060
+ if (result.error) {
24061
+ console.log(source_default.red(result.error));
24062
+ return;
24063
+ }
24064
+ const { schedules, stats } = result.data || { schedules: [], stats: { total: 0, active: 0, paused: 0, completed: 0, failed: 0, cancelled: 0 } };
24065
+ console.log();
24066
+ console.log(indigo12.bold("Scheduled Jobs"));
24067
+ console.log(source_default.dim(`Total: ${stats.total} | Active: ${stats.active} | Paused: ${stats.paused} | Completed: ${stats.completed} | Failed: ${stats.failed}`));
24068
+ console.log();
24069
+ if (schedules.length === 0) {
24070
+ console.log(source_default.dim("No schedules found"));
24071
+ console.log(source_default.dim('Create one with: skills schedule <skill> "every day at 9am"'));
24072
+ return;
24073
+ }
24074
+ for (const schedule of schedules) {
24075
+ printScheduleSummary(schedule);
24076
+ }
24077
+ console.log();
24078
+ console.log(source_default.dim("View all at: https://skills.md (go to your team dashboard)"));
24079
+ } catch (error) {
24080
+ spinner.fail("Request failed");
24081
+ console.log(source_default.red(error instanceof Error ? error.message : "Unknown error"));
24082
+ }
24083
+ }
24084
+ async function scheduleGetCommand(id) {
24085
+ const apiKey = getApiKey();
24086
+ if (!apiKey) {
24087
+ console.log(source_default.red("Not logged in"));
24088
+ console.log(source_default.dim("Run `skills login` first"));
24089
+ return;
24090
+ }
24091
+ const spinner = ora("Fetching schedule...").start();
24092
+ try {
24093
+ const result = await getSchedule(id);
24094
+ spinner.stop();
24095
+ if (result.error) {
24096
+ console.log(source_default.red(result.error));
24097
+ return;
24098
+ }
24099
+ const { schedule, recentRuns, stats } = result.data || {};
24100
+ if (!schedule) {
24101
+ console.log(source_default.red("Schedule not found"));
24102
+ return;
24103
+ }
24104
+ console.log();
24105
+ printScheduleDetails(schedule);
24106
+ if (stats) {
24107
+ console.log();
24108
+ console.log(indigo12.bold("Run Statistics"));
24109
+ console.log(` ${source_default.dim("Total Runs:")} ${stats.totalRuns}`);
24110
+ console.log(` ${source_default.dim("Successful:")} ${source_default.green(String(stats.successfulRuns))}`);
24111
+ console.log(` ${source_default.dim("Failed:")} ${source_default.red(String(stats.failedRuns))}`);
24112
+ console.log(` ${source_default.dim("Credits Used:")} ${indigo12(String(stats.totalCredits))}`);
24113
+ if (stats.avgDurationMs) {
24114
+ console.log(` ${source_default.dim("Avg Duration:")} ${formatDuration3(stats.avgDurationMs)}`);
24115
+ }
24116
+ }
24117
+ if (recentRuns && recentRuns.length > 0) {
24118
+ console.log();
24119
+ console.log(indigo12.bold("Recent Runs"));
24120
+ for (const run of recentRuns.slice(0, 5)) {
24121
+ printRunSummary(run);
24122
+ }
24123
+ }
24124
+ } catch (error) {
24125
+ spinner.fail("Request failed");
24126
+ console.log(source_default.red(error instanceof Error ? error.message : "Unknown error"));
24127
+ }
24128
+ }
24129
+ async function schedulePauseCommand(id) {
24130
+ const apiKey = getApiKey();
24131
+ if (!apiKey) {
24132
+ console.log(source_default.red("Not logged in"));
24133
+ console.log(source_default.dim("Run `skills login` first"));
24134
+ return;
24135
+ }
24136
+ const spinner = ora("Pausing schedule...").start();
24137
+ try {
24138
+ const result = await updateSchedule(id, { status: "paused" });
24139
+ if (result.error) {
24140
+ spinner.fail("Failed to pause schedule");
24141
+ console.log(source_default.red(result.error));
24142
+ return;
24143
+ }
24144
+ spinner.succeed("Schedule paused successfully");
24145
+ const schedule = result.data?.schedule;
24146
+ if (schedule) {
24147
+ console.log();
24148
+ console.log(` ${source_default.dim("Name:")} ${schedule.name}`);
24149
+ console.log(` ${source_default.dim("Status:")} ${formatStatus3(schedule.status)}`);
24150
+ }
24151
+ } catch (error) {
24152
+ spinner.fail("Request failed");
24153
+ console.log(source_default.red(error instanceof Error ? error.message : "Unknown error"));
24154
+ }
24155
+ }
24156
+ async function scheduleResumeCommand(id) {
24157
+ const apiKey = getApiKey();
24158
+ if (!apiKey) {
24159
+ console.log(source_default.red("Not logged in"));
24160
+ console.log(source_default.dim("Run `skills login` first"));
24161
+ return;
24162
+ }
24163
+ const spinner = ora("Resuming schedule...").start();
24164
+ try {
24165
+ const result = await updateSchedule(id, { status: "active" });
24166
+ if (result.error) {
24167
+ spinner.fail("Failed to resume schedule");
24168
+ console.log(source_default.red(result.error));
24169
+ return;
24170
+ }
24171
+ spinner.succeed("Schedule resumed successfully");
24172
+ const schedule = result.data?.schedule;
24173
+ if (schedule) {
24174
+ console.log();
24175
+ console.log(` ${source_default.dim("Name:")} ${schedule.name}`);
24176
+ console.log(` ${source_default.dim("Status:")} ${formatStatus3(schedule.status)}`);
24177
+ if (schedule.nextRunAt) {
24178
+ console.log(` ${source_default.dim("Next Run:")} ${formatDate(schedule.nextRunAt)}`);
24179
+ }
24180
+ }
24181
+ } catch (error) {
24182
+ spinner.fail("Request failed");
24183
+ console.log(source_default.red(error instanceof Error ? error.message : "Unknown error"));
24184
+ }
24185
+ }
24186
+ async function scheduleCancelCommand(id) {
24187
+ const apiKey = getApiKey();
24188
+ if (!apiKey) {
24189
+ console.log(source_default.red("Not logged in"));
24190
+ console.log(source_default.dim("Run `skills login` first"));
24191
+ return;
24192
+ }
24193
+ const spinner = ora("Cancelling schedule...").start();
24194
+ try {
24195
+ const result = await deleteSchedule(id);
24196
+ if (result.error) {
24197
+ spinner.fail("Failed to cancel schedule");
24198
+ console.log(source_default.red(result.error));
24199
+ return;
24200
+ }
24201
+ spinner.succeed("Schedule cancelled successfully");
24202
+ const schedule = result.data?.schedule;
24203
+ if (schedule) {
24204
+ console.log();
24205
+ console.log(` ${source_default.dim("Name:")} ${schedule.name}`);
24206
+ console.log(` ${source_default.dim("Status:")} ${formatStatus3(schedule.status)}`);
24207
+ }
24208
+ } catch (error) {
24209
+ spinner.fail("Request failed");
24210
+ console.log(source_default.red(error instanceof Error ? error.message : "Unknown error"));
24211
+ }
24212
+ }
24213
+ async function scheduleHistoryCommand(id, options = {}) {
24214
+ const apiKey = getApiKey();
24215
+ if (!apiKey) {
24216
+ console.log(source_default.red("Not logged in"));
24217
+ console.log(source_default.dim("Run `skills login` first"));
24218
+ return;
24219
+ }
24220
+ const spinner = ora("Fetching run history...").start();
24221
+ try {
24222
+ const result = await getScheduleRuns(id, {
24223
+ status: options.status,
24224
+ limit: options.limit || 20
24225
+ });
24226
+ spinner.stop();
24227
+ if (result.error) {
24228
+ console.log(source_default.red(result.error));
24229
+ return;
24230
+ }
24231
+ const { schedule, runs, stats } = result.data || {};
24232
+ if (!schedule) {
24233
+ console.log(source_default.red("Schedule not found"));
24234
+ return;
24235
+ }
24236
+ console.log();
24237
+ console.log(indigo12.bold(`Run History: ${schedule.name}`));
24238
+ console.log(source_default.dim(`Skill: ${schedule.skillSlug}`));
24239
+ console.log(source_default.dim(`Total: ${stats?.totalRuns || 0} | Success: ${stats?.successfulRuns || 0} | Failed: ${stats?.failedRuns || 0} | Success Rate: ${stats?.successRate || 0}%`));
24240
+ console.log();
24241
+ if (!runs || runs.length === 0) {
24242
+ console.log(source_default.dim("No runs yet"));
24243
+ return;
24244
+ }
24245
+ for (const run of runs) {
24246
+ printRunSummary(run);
24247
+ }
24248
+ } catch (error) {
24249
+ spinner.fail("Request failed");
24250
+ console.log(source_default.red(error instanceof Error ? error.message : "Unknown error"));
24251
+ }
24252
+ }
24253
+ function printScheduleSummary(schedule) {
24254
+ const statusIcon = getStatusIcon2(schedule.status);
24255
+ const nextRun = schedule.nextRunAt ? formatRelativeTime(schedule.nextRunAt) : "-";
24256
+ console.log(`${statusIcon} ${source_default.bold(schedule.name)} ${source_default.dim(`(${schedule.skillSlug})`)}`);
24257
+ console.log(` ${source_default.dim("ID:")} ${schedule.id.slice(0, 8)}...`);
24258
+ console.log(` ${source_default.dim("Schedule:")} ${schedule.scheduleDescription || schedule.cronExpression || "One-time"}`);
24259
+ console.log(` ${source_default.dim("Status:")} ${formatStatus3(schedule.status)}`);
24260
+ console.log(` ${source_default.dim("Next Run:")} ${nextRun}`);
24261
+ console.log(` ${source_default.dim("Runs:")} ${schedule.runCount}${schedule.maxRuns ? `/${schedule.maxRuns}` : ""}`);
24262
+ console.log();
24263
+ }
24264
+ function printScheduleDetails(schedule) {
24265
+ console.log(indigo12.bold("Schedule Details"));
24266
+ console.log(` ${source_default.dim("ID:")} ${schedule.id}`);
24267
+ console.log(` ${source_default.dim("Name:")} ${schedule.name}`);
24268
+ console.log(` ${source_default.dim("Skill:")} ${schedule.skillSlug}`);
24269
+ console.log(` ${source_default.dim("Status:")} ${formatStatus3(schedule.status)}`);
24270
+ console.log(` ${source_default.dim("Type:")} ${schedule.scheduleType === "cron" ? "Recurring" : "One-time"}`);
24271
+ console.log(` ${source_default.dim("Schedule:")} ${schedule.scheduleDescription || schedule.cronExpression || formatDate(schedule.scheduledAt)}`);
24272
+ console.log(` ${source_default.dim("Timezone:")} ${schedule.timezone}`);
24273
+ if (schedule.command) {
24274
+ console.log(` ${source_default.dim("Command:")} ${schedule.command}`);
24275
+ }
24276
+ console.log(` ${source_default.dim("Runs:")} ${schedule.runCount}${schedule.maxRuns ? `/${schedule.maxRuns}` : ""}`);
24277
+ if (schedule.nextRunAt) {
24278
+ console.log(` ${source_default.dim("Next Run:")} ${formatDate(schedule.nextRunAt)} (${formatRelativeTime(schedule.nextRunAt)})`);
24279
+ }
24280
+ if (schedule.lastRunAt) {
24281
+ console.log(` ${source_default.dim("Last Run:")} ${formatDate(schedule.lastRunAt)} - ${formatStatus3(schedule.lastRunStatus || "unknown")}`);
24282
+ }
24283
+ console.log(` ${source_default.dim("Created:")} ${formatDate(schedule.createdAt)}`);
24284
+ }
24285
+ function printRunSummary(run) {
24286
+ const statusIcon = run.status === "completed" ? source_default.green("✓") : source_default.red("✗");
24287
+ const duration = run.durationMs ? formatDuration3(run.durationMs) : "-";
24288
+ console.log(` ${statusIcon} Run #${run.runNumber} - ${formatStatus3(run.status)}`);
24289
+ console.log(` ${source_default.dim("Time:")} ${formatDate(run.createdAt)}`);
24290
+ console.log(` ${source_default.dim("Duration:")} ${duration}`);
24291
+ if (run.creditsUsed) {
24292
+ console.log(` ${source_default.dim("Credits:")} ${run.creditsUsed}`);
24293
+ }
24294
+ if (run.errorMessage) {
24295
+ console.log(` ${source_default.dim("Error:")} ${source_default.red(run.errorMessage)}`);
24296
+ }
24297
+ }
24298
+ function getStatusIcon2(status) {
24299
+ switch (status) {
24300
+ case "active":
24301
+ return source_default.green("●");
24302
+ case "paused":
24303
+ return source_default.yellow("◐");
24304
+ case "completed":
24305
+ return source_default.blue("✓");
24306
+ case "failed":
24307
+ return source_default.red("✗");
24308
+ case "cancelled":
24309
+ return source_default.dim("○");
24310
+ default:
24311
+ return source_default.dim("○");
24312
+ }
24313
+ }
24314
+ function formatStatus3(status) {
24315
+ switch (status) {
24316
+ case "active":
24317
+ return source_default.green("active");
24318
+ case "paused":
24319
+ return source_default.yellow("paused");
24320
+ case "completed":
24321
+ return source_default.blue("completed");
24322
+ case "failed":
24323
+ return source_default.red("failed");
24324
+ case "cancelled":
24325
+ return source_default.dim("cancelled");
24326
+ default:
24327
+ return source_default.dim(status || "unknown");
24328
+ }
24329
+ }
24330
+ function formatDate(dateString) {
24331
+ if (!dateString)
24332
+ return "-";
24333
+ const date = new Date(dateString);
24334
+ return date.toLocaleString();
24335
+ }
24336
+ function formatRelativeTime(dateString) {
24337
+ const date = new Date(dateString);
24338
+ const now = new Date;
24339
+ const diffMs = date.getTime() - now.getTime();
24340
+ const diffMins = Math.round(diffMs / 60000);
24341
+ const diffHours = Math.round(diffMs / 3600000);
24342
+ const diffDays = Math.round(diffMs / 86400000);
24343
+ if (diffMins < 0) {
24344
+ if (diffMins > -60)
24345
+ return `${Math.abs(diffMins)}m ago`;
24346
+ if (diffHours > -24)
24347
+ return `${Math.abs(diffHours)}h ago`;
24348
+ return `${Math.abs(diffDays)}d ago`;
24349
+ }
24350
+ if (diffMins < 60)
24351
+ return `in ${diffMins}m`;
24352
+ if (diffHours < 24)
24353
+ return `in ${diffHours}h`;
24354
+ return `in ${diffDays}d`;
24355
+ }
24356
+ function formatDuration3(ms) {
24357
+ if (ms < 1000) {
24358
+ return `${ms}ms`;
24359
+ } else if (ms < 60000) {
24360
+ return `${(ms / 1000).toFixed(1)}s`;
24361
+ } else {
24362
+ const minutes = Math.floor(ms / 60000);
24363
+ const seconds = Math.floor(ms % 60000 / 1000);
24364
+ return `${minutes}m ${seconds}s`;
24365
+ }
24366
+ }
24367
+
23742
24368
  // src/index.ts
23743
- var indigo12 = colors.primary;
24369
+ var indigo13 = colors.primary;
23744
24370
  var program2 = new Command;
23745
- program2.name("skills").description("CLI for skills.md - AI Agent Skills Marketplace").version("0.1.37");
24371
+ program2.name("skills").description("CLI for skills.md - AI Agent Skills Marketplace").version("0.1.39");
23746
24372
  program2.command("init").description("Initialize skills.md in current project").option("-f, --force", "Force re-initialization (removes existing .skills/)").action((options) => {
23747
24373
  initCommand({ force: options.force });
23748
24374
  });
@@ -23759,11 +24385,13 @@ program2.command("marketplace").alias("market").description("Browse skills from
23759
24385
  category: options.category
23760
24386
  });
23761
24387
  });
23762
- 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) => {
23763
24389
  installCommand(name || "", {
23764
24390
  local: options.local,
23765
24391
  target: options.target,
23766
- all: options.all
24392
+ all: options.all,
24393
+ tag: options.tag,
24394
+ force: options.force
23767
24395
  });
23768
24396
  });
23769
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) => {
@@ -23847,8 +24475,36 @@ program2.command("update [skill]").description("Update installed skills (refresh
23847
24475
  });
23848
24476
  program2.command("upgrade").description("Upgrade the Skills CLI to the latest version").action(upgradeCommand);
23849
24477
  program2.command("doctor").description("Check your environment and diagnose issues").action(doctorCommand);
24478
+ var scheduleCmd = program2.command("schedule").description("Schedule skills to run automatically");
24479
+ scheduleCmd.command("create <skill> [schedule]").description("Create a new schedule (use natural language or --cron/--at)").option("-c, --cron <expression>", "Cron expression (e.g., '0 9 * * 1' for every Monday at 9am)").option("-a, --at <datetime>", "One-time schedule (ISO 8601 format)").option("--command <command>", "Skill command to run").option("-t, --timezone <timezone>", "Timezone (default: UTC)").option("-m, --max-runs <number>", "Maximum number of runs").option("-n, --name <name>", "Schedule name").action((skill, schedule, options) => {
24480
+ scheduleCreateCommand(skill, schedule, {
24481
+ cron: options.cron,
24482
+ at: options.at,
24483
+ command: options.command,
24484
+ timezone: options.timezone,
24485
+ maxRuns: options.maxRuns ? parseInt(options.maxRuns, 10) : undefined,
24486
+ name: options.name
24487
+ });
24488
+ });
24489
+ scheduleCmd.command("list").alias("ls").description("List all schedules").option("-s, --status <status>", "Filter by status (active, paused, completed, failed, cancelled)").option("-k, --skill <skill>", "Filter by skill slug").option("-n, --limit <number>", "Limit number of results").action((options) => {
24490
+ scheduleListCommand({
24491
+ status: options.status,
24492
+ skill: options.skill,
24493
+ limit: options.limit ? parseInt(options.limit, 10) : undefined
24494
+ });
24495
+ });
24496
+ scheduleCmd.command("get <id>").description("Get schedule details").action(scheduleGetCommand);
24497
+ scheduleCmd.command("pause <id>").description("Pause a schedule").action(schedulePauseCommand);
24498
+ scheduleCmd.command("resume <id>").description("Resume a paused schedule").action(scheduleResumeCommand);
24499
+ scheduleCmd.command("cancel <id>").description("Cancel a schedule").action(scheduleCancelCommand);
24500
+ scheduleCmd.command("history <id>").description("View run history for a schedule").option("-s, --status <status>", "Filter by run status").option("-n, --limit <number>", "Limit number of results").action((id, options) => {
24501
+ scheduleHistoryCommand(id, {
24502
+ status: options.status,
24503
+ limit: options.limit ? parseInt(options.limit, 10) : undefined
24504
+ });
24505
+ });
23850
24506
  program2.addHelpText("after", `
23851
- ${indigo12.bold("Examples:")}
24507
+ ${indigo13.bold("Examples:")}
23852
24508
  ${source_default.dim("# Initialize in current project")}
23853
24509
  $ skills init
23854
24510
 
@@ -23867,6 +24523,10 @@ ${indigo12.bold("Examples:")}
23867
24523
  ${source_default.dim("# Install globally for Claude Code")}
23868
24524
  $ skills install code-review -g -t claude
23869
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
+
23870
24530
  ${source_default.dim("# AI-powered setup wizard")}
23871
24531
  $ skills setup
23872
24532
  $ skills setup "SaaS application with API"
@@ -23913,7 +24573,23 @@ ${indigo12.bold("Examples:")}
23913
24573
  ${source_default.dim("# Check environment")}
23914
24574
  $ skills doctor
23915
24575
 
23916
- ${indigo12.bold("Documentation:")}
23917
- ${indigo12("https://skills.md/docs")}
24576
+ ${source_default.dim("# Schedule a skill (natural language)")}
24577
+ $ skills schedule create calendar-events "every Monday at 9am" --command list
24578
+ $ skills schedule create linear-issues "daily at midnight" --command sync
24579
+
24580
+ ${source_default.dim("# Schedule a skill (cron expression)")}
24581
+ $ skills schedule create calendar-events --cron "0 9 * * 1-5" --command list
24582
+
24583
+ ${source_default.dim("# Schedule a one-time run")}
24584
+ $ skills schedule create compose-gmail --at "2025-01-15T09:00:00"
24585
+
24586
+ ${source_default.dim("# Manage schedules")}
24587
+ $ skills schedule list
24588
+ $ skills schedule pause <id>
24589
+ $ skills schedule resume <id>
24590
+ $ skills schedule history <id>
24591
+
24592
+ ${indigo13.bold("Documentation:")}
24593
+ ${indigo13("https://skills.md/docs")}
23918
24594
  `);
23919
24595
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasnatools/skills",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "description": "CLI for skills.md - AI Agent Skills Marketplace",
5
5
  "type": "module",
6
6
  "bin": {