@hasnatools/skills 0.1.9 → 0.1.11

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 +385 -48
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20747,6 +20747,21 @@ async function installSkillRemote(slug) {
20747
20747
  body: JSON.stringify({ skillSlug: slug })
20748
20748
  });
20749
20749
  }
20750
+ async function getCLI2FASettings() {
20751
+ return apiRequest("/me/settings/cli-2fa");
20752
+ }
20753
+ async function setCLI2FASettings(enabled) {
20754
+ return apiRequest("/me/settings/cli-2fa", {
20755
+ method: "PUT",
20756
+ body: JSON.stringify({ enabled })
20757
+ });
20758
+ }
20759
+ async function purchaseCredits(amountCents, totpCode) {
20760
+ return apiRequest("/cli/credits/purchase", {
20761
+ method: "POST",
20762
+ body: JSON.stringify({ amountCents, totpCode })
20763
+ });
20764
+ }
20750
20765
  async function makeApiRequest(path6, options = {}) {
20751
20766
  const apiKey = getApiKey();
20752
20767
  const endpoint = getApiEndpoint();
@@ -21368,6 +21383,7 @@ function parseFrontmatter(content) {
21368
21383
  }
21369
21384
 
21370
21385
  // src/commands/config.ts
21386
+ var indigo3 = source_default.hex("#6366f1");
21371
21387
  async function configCommand(options) {
21372
21388
  if (!options.get && !options.set) {
21373
21389
  const config = getConfig();
@@ -21464,6 +21480,90 @@ function getNestedValue(obj, path6) {
21464
21480
  }
21465
21481
  return current;
21466
21482
  }
21483
+ async function cli2faCommand(action) {
21484
+ const apiKey = getApiKey();
21485
+ if (!apiKey) {
21486
+ console.log(source_default.yellow("Not logged in"));
21487
+ console.log(source_default.dim("Run `skills login` to authenticate"));
21488
+ return;
21489
+ }
21490
+ const normalizedAction = action?.toLowerCase();
21491
+ if (normalizedAction && normalizedAction !== "on" && normalizedAction !== "off") {
21492
+ console.log(source_default.red("Invalid action. Use 'on' or 'off'."));
21493
+ console.log();
21494
+ console.log("Usage:");
21495
+ console.log(" skills config cli-2fa " + source_default.dim("Show current setting"));
21496
+ console.log(" skills config cli-2fa on " + source_default.dim("Enable 2FA for CLI purchases"));
21497
+ console.log(" skills config cli-2fa off " + source_default.dim("Disable 2FA for CLI purchases"));
21498
+ return;
21499
+ }
21500
+ const spinner = ora("Fetching 2FA settings...").start();
21501
+ const result = await getCLI2FASettings();
21502
+ if (result.error || !result.data) {
21503
+ spinner.fail("Failed to fetch 2FA settings");
21504
+ console.error(source_default.red(result.error || "Unknown error"));
21505
+ return;
21506
+ }
21507
+ const { enabled, has2FASetup } = result.data;
21508
+ if (!normalizedAction) {
21509
+ spinner.stop();
21510
+ console.log();
21511
+ console.log(indigo3.bold("CLI 2FA Settings"));
21512
+ console.log();
21513
+ console.log(" 2FA for CLI purchases: " + (enabled ? source_default.green("enabled") : source_default.dim("disabled")));
21514
+ console.log(" Account 2FA setup: " + (has2FASetup ? source_default.green("yes") : source_default.dim("no")));
21515
+ console.log();
21516
+ if (!has2FASetup) {
21517
+ console.log(source_default.yellow("⚠") + " You must set up 2FA on your account before enabling this feature.");
21518
+ console.log(" Visit: " + indigo3("https://skills.md/dashboard/settings"));
21519
+ } else if (!enabled) {
21520
+ console.log(source_default.dim("Enable 2FA for CLI purchases: skills config cli-2fa on"));
21521
+ }
21522
+ console.log();
21523
+ return;
21524
+ }
21525
+ if (normalizedAction === "on") {
21526
+ if (!has2FASetup) {
21527
+ spinner.fail("Cannot enable - 2FA not set up on your account");
21528
+ console.log();
21529
+ console.log("You must set up 2FA on your account first.");
21530
+ console.log("Visit: " + indigo3("https://skills.md/dashboard/settings"));
21531
+ return;
21532
+ }
21533
+ if (enabled) {
21534
+ spinner.info("2FA for CLI purchases is already enabled");
21535
+ return;
21536
+ }
21537
+ spinner.text = "Enabling 2FA for CLI purchases...";
21538
+ const updateResult = await setCLI2FASettings(true);
21539
+ if (updateResult.error) {
21540
+ spinner.fail("Failed to enable 2FA");
21541
+ console.error(source_default.red(updateResult.error));
21542
+ return;
21543
+ }
21544
+ spinner.succeed("2FA enabled for CLI purchases");
21545
+ console.log();
21546
+ console.log(source_default.dim("You will now be prompted for a 2FA code when purchasing credits via CLI."));
21547
+ return;
21548
+ }
21549
+ if (normalizedAction === "off") {
21550
+ if (!enabled) {
21551
+ spinner.info("2FA for CLI purchases is already disabled");
21552
+ return;
21553
+ }
21554
+ spinner.text = "Disabling 2FA for CLI purchases...";
21555
+ const updateResult = await setCLI2FASettings(false);
21556
+ if (updateResult.error) {
21557
+ spinner.fail("Failed to disable 2FA");
21558
+ console.error(source_default.red(updateResult.error));
21559
+ return;
21560
+ }
21561
+ spinner.succeed("2FA disabled for CLI purchases");
21562
+ console.log();
21563
+ console.log(source_default.yellow("⚠") + " CLI credit purchases will no longer require 2FA verification.");
21564
+ return;
21565
+ }
21566
+ }
21467
21567
 
21468
21568
  // src/commands/run.ts
21469
21569
  import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, appendFileSync } from "fs";
@@ -21785,7 +21885,7 @@ async function generateCommand(mediaType, prompt, options = {}) {
21785
21885
  console.log();
21786
21886
  const spinner = ora("Sending request...").start();
21787
21887
  try {
21788
- const response = await makeApiRequest("/api/generate", {
21888
+ const response = await makeApiRequest("/generate", {
21789
21889
  method: "POST",
21790
21890
  body: JSON.stringify({
21791
21891
  mediaType,
@@ -21839,7 +21939,7 @@ async function jobsCommand(jobId) {
21839
21939
  }
21840
21940
  const spinner = ora("Fetching jobs...").start();
21841
21941
  try {
21842
- const endpoint = jobId ? `/api/jobs/${jobId}` : "/api/jobs";
21942
+ const endpoint = jobId ? `/jobs/${jobId}` : "/jobs";
21843
21943
  const response = await makeApiRequest(endpoint);
21844
21944
  if (!response.ok) {
21845
21945
  const error = await response.json();
@@ -21915,7 +22015,111 @@ function formatStatus(status) {
21915
22015
  // src/commands/history.ts
21916
22016
  import { existsSync as existsSync8, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync } from "fs";
21917
22017
  import { join as join8 } from "path";
22018
+ var indigo4 = source_default.hex("#6366f1");
21918
22019
  async function historyCommand(options = {}) {
22020
+ if (options.local) {
22021
+ return localHistoryCommand(options);
22022
+ }
22023
+ const apiKey = getApiKey();
22024
+ if (!apiKey) {
22025
+ console.log(source_default.yellow("Not logged in - showing local history only"));
22026
+ console.log(source_default.dim("Run `skills login` to see remote execution history"));
22027
+ console.log();
22028
+ return localHistoryCommand(options);
22029
+ }
22030
+ const spinner = ora("Fetching execution history...").start();
22031
+ try {
22032
+ const params = new URLSearchParams;
22033
+ if (options.skill)
22034
+ params.set("skill", options.skill);
22035
+ if (options.limit)
22036
+ params.set("limit", String(options.limit));
22037
+ if (options.status)
22038
+ params.set("status", options.status);
22039
+ const query = params.toString();
22040
+ const response = await makeApiRequest(`/me/executions${query ? `?${query}` : ""}`);
22041
+ if (!response.ok) {
22042
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
22043
+ spinner.fail("Failed to fetch history");
22044
+ console.log(source_default.red(error.error));
22045
+ return;
22046
+ }
22047
+ const result = await response.json();
22048
+ spinner.stop();
22049
+ const { executions, pagination, counts } = result;
22050
+ console.log();
22051
+ console.log(indigo4.bold("Execution History"));
22052
+ console.log(source_default.dim(`Total: ${counts.all} | Completed: ${counts.completed} | Failed: ${counts.failed} | Pending: ${counts.pending}`));
22053
+ console.log();
22054
+ if (executions.length === 0) {
22055
+ console.log(source_default.dim("No executions found"));
22056
+ console.log(source_default.dim("Run a skill with `skills run <name>` to start tracking"));
22057
+ return;
22058
+ }
22059
+ for (const exec of executions) {
22060
+ const date = new Date(exec.createdAt);
22061
+ const dateStr = date.toLocaleDateString();
22062
+ const timeStr = date.toLocaleTimeString();
22063
+ const statusIcon = getStatusIcon(exec.status);
22064
+ const durationStr = exec.durationMs ? formatDuration2(exec.durationMs) : "-";
22065
+ console.log(`${statusIcon} ${source_default.bold(exec.skillSlug || exec.skillName || "Unknown")} ${source_default.dim(`(${dateStr} ${timeStr})`)}`);
22066
+ console.log(` ${source_default.dim("ID:")} ${exec.id.slice(0, 8)}...`);
22067
+ console.log(` ${source_default.dim("Status:")} ${formatStatus2(exec.status)}`);
22068
+ console.log(` ${source_default.dim("Duration:")} ${durationStr}`);
22069
+ if (exec.creditsUsed > 0) {
22070
+ console.log(` ${source_default.dim("Credits:")} ${indigo4(String(exec.creditsUsed))}`);
22071
+ }
22072
+ if (exec.outputType) {
22073
+ console.log(` ${source_default.dim("Output:")} ${exec.outputType}`);
22074
+ }
22075
+ if (exec.exports && exec.exports.length > 0) {
22076
+ console.log(` ${source_default.dim("Exports:")} ${exec.exports.length} file(s)`);
22077
+ if (options.verbose) {
22078
+ for (const exp of exec.exports) {
22079
+ console.log(` ${source_default.dim("→")} ${exp.filename}`);
22080
+ }
22081
+ }
22082
+ }
22083
+ if (exec.errorMessage && options.verbose) {
22084
+ console.log(` ${source_default.dim("Error:")} ${source_default.red(exec.errorMessage)}`);
22085
+ }
22086
+ console.log();
22087
+ }
22088
+ if (pagination.hasMore) {
22089
+ console.log(source_default.dim(`Showing ${executions.length} of ${pagination.total} executions`));
22090
+ console.log(source_default.dim(`Use --limit to see more`));
22091
+ }
22092
+ console.log(source_default.dim("View all at: https://skills.md/dashboard/executions"));
22093
+ } catch (error) {
22094
+ spinner.fail("Request failed");
22095
+ console.log(source_default.red(error instanceof Error ? error.message : "Unknown error"));
22096
+ }
22097
+ }
22098
+ function getStatusIcon(status) {
22099
+ switch (status) {
22100
+ case "completed":
22101
+ return source_default.green("✓");
22102
+ case "failed":
22103
+ return source_default.red("✗");
22104
+ case "pending":
22105
+ return source_default.yellow("◌");
22106
+ default:
22107
+ return source_default.dim("○");
22108
+ }
22109
+ }
22110
+ function formatStatus2(status) {
22111
+ switch (status) {
22112
+ case "completed":
22113
+ return source_default.green("completed");
22114
+ case "failed":
22115
+ return source_default.red("failed");
22116
+ case "pending":
22117
+ return source_default.yellow("pending");
22118
+ default:
22119
+ return source_default.dim(status || "unknown");
22120
+ }
22121
+ }
22122
+ async function localHistoryCommand(options) {
21919
22123
  const projectOutputDir = getProjectSkillsOutputDir();
21920
22124
  if (!projectOutputDir) {
21921
22125
  console.log(source_default.yellow("No project initialized"));
@@ -21924,7 +22128,7 @@ async function historyCommand(options = {}) {
21924
22128
  }
21925
22129
  const historyFile = join8(projectOutputDir, "execution-history.jsonl");
21926
22130
  if (!existsSync8(historyFile)) {
21927
- console.log(source_default.dim("No execution history found"));
22131
+ console.log(source_default.dim("No local execution history found"));
21928
22132
  console.log(source_default.dim("Run a skill with `skills run <name>` to start tracking"));
21929
22133
  return;
21930
22134
  }
@@ -21948,7 +22152,7 @@ async function historyCommand(options = {}) {
21948
22152
  return;
21949
22153
  }
21950
22154
  console.log();
21951
- console.log(source_default.bold("Execution History"));
22155
+ console.log(indigo4.bold("Local Execution History"));
21952
22156
  console.log(source_default.dim(`Showing ${entries.length} most recent execution(s)`));
21953
22157
  console.log();
21954
22158
  for (const entry of entries) {
@@ -21999,7 +22203,7 @@ async function exportsCommand(slug, options = {}) {
21999
22203
  return;
22000
22204
  }
22001
22205
  console.log();
22002
- console.log(source_default.bold(`Exports for ${slug}`));
22206
+ console.log(indigo4.bold(`Exports for ${slug}`));
22003
22207
  console.log(source_default.dim(`Directory: ${exportsDir}`));
22004
22208
  console.log();
22005
22209
  const fileInfos = files.map((file) => {
@@ -22042,17 +22246,17 @@ async function logsCommand(slug, options = {}) {
22042
22246
  const latestLog = files[0];
22043
22247
  const logPath = join8(logsDir, latestLog);
22044
22248
  console.log();
22045
- console.log(source_default.bold(`Latest Log for ${slug}`));
22249
+ console.log(indigo4.bold(`Latest Log for ${slug}`));
22046
22250
  console.log(source_default.dim(`File: ${logPath}`));
22047
22251
  console.log(source_default.dim("─".repeat(60)));
22048
22252
  console.log();
22049
22253
  const content = readFileSync4(logPath, "utf-8");
22050
- const lines = content.split(`
22254
+ const logLines = content.split(`
22051
22255
  `);
22052
22256
  const tailLines = options.tail || 50;
22053
- if (lines.length > tailLines) {
22257
+ if (logLines.length > tailLines) {
22054
22258
  console.log(source_default.dim(`... (showing last ${tailLines} lines)`));
22055
- console.log(lines.slice(-tailLines).join(`
22259
+ console.log(logLines.slice(-tailLines).join(`
22056
22260
  `));
22057
22261
  } else {
22058
22262
  console.log(content);
@@ -22082,7 +22286,7 @@ function formatBytes(bytes) {
22082
22286
  }
22083
22287
 
22084
22288
  // src/commands/marketplace.ts
22085
- var indigo3 = source_default.hex("#6366f1");
22289
+ var indigo5 = source_default.hex("#6366f1");
22086
22290
  async function marketplaceCommand(options = {}) {
22087
22291
  const spinner = ora("Fetching skills from marketplace...").start();
22088
22292
  const limit = options.limit || 20;
@@ -22105,36 +22309,36 @@ async function marketplaceCommand(options = {}) {
22105
22309
  return;
22106
22310
  }
22107
22311
  console.log();
22108
- console.log(indigo3.bold(`Skills Marketplace`) + source_default.dim(` (page ${page}/${totalPages}, ${total} total skills)`));
22312
+ console.log(indigo5.bold(`Skills Marketplace`) + source_default.dim(` (page ${page}/${totalPages}, ${total} total skills)`));
22109
22313
  console.log();
22110
22314
  for (const skill of skills) {
22111
- const verified = skill.isVerified ? indigo3(" ✓") : "";
22315
+ const verified = skill.isVerified ? indigo5(" ✓") : "";
22112
22316
  const downloads = skill.downloadCount > 0 ? source_default.dim(` (${skill.downloadCount} installs)`) : "";
22113
22317
  console.log(" " + source_default.bold(skill.name) + verified + source_default.dim(` v${skill.version}`) + downloads);
22114
22318
  console.log(" " + source_default.dim(skill.description || "No description"));
22115
- console.log(" " + indigo3(`skills install ${skill.slug}`));
22319
+ console.log(" " + indigo5(`skills install ${skill.slug}`));
22116
22320
  console.log();
22117
22321
  }
22118
22322
  console.log(source_default.dim("─".repeat(50)));
22119
22323
  if (totalPages > 1) {
22120
22324
  const navHints = [];
22121
22325
  if (page > 1) {
22122
- navHints.push(`${indigo3(`skills marketplace -p ${page - 1}`)} for previous`);
22326
+ navHints.push(`${indigo5(`skills marketplace -p ${page - 1}`)} for previous`);
22123
22327
  }
22124
22328
  if (page < totalPages) {
22125
- navHints.push(`${indigo3(`skills marketplace -p ${page + 1}`)} for next`);
22329
+ navHints.push(`${indigo5(`skills marketplace -p ${page + 1}`)} for next`);
22126
22330
  }
22127
22331
  if (navHints.length > 0) {
22128
22332
  console.log(source_default.dim(navHints.join(" | ")));
22129
22333
  }
22130
22334
  }
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`));
22335
+ console.log(source_default.dim(`Run ${indigo5("skills install <name>")} to install a skill`));
22336
+ console.log(source_default.dim(`Run ${indigo5("skills search <query>")} to search for specific skills`));
22133
22337
  }
22134
22338
 
22135
22339
  // src/commands/feedback.ts
22136
22340
  var import_prompts2 = __toESM(require_prompts3(), 1);
22137
- var indigo4 = source_default.hex("#6366f1");
22341
+ var indigo6 = source_default.hex("#6366f1");
22138
22342
  var indigoBold = source_default.hex("#6366f1").bold;
22139
22343
  async function submitFeedback(type, title, description) {
22140
22344
  const spinner = ora("Submitting feedback...").start();
@@ -22156,7 +22360,7 @@ async function submitFeedback(type, title, description) {
22156
22360
  }
22157
22361
  spinner.succeed("Feedback submitted!");
22158
22362
  console.log();
22159
- console.log(indigo4(" Thank you for your feedback! \uD83D\uDE4F"));
22363
+ console.log(indigo6(" Thank you for your feedback! \uD83D\uDE4F"));
22160
22364
  console.log(source_default.dim(" We'll review it and get back to you if needed."));
22161
22365
  return true;
22162
22366
  } catch (error) {
@@ -22172,7 +22376,7 @@ async function feedbackCommand(options = {}) {
22172
22376
  const apiKey = getApiKey();
22173
22377
  if (!apiKey) {
22174
22378
  console.log(source_default.yellow("⚠") + " You need to be logged in to submit feedback.");
22175
- console.log(source_default.dim(" Run ") + indigo4("skills login") + source_default.dim(" first."));
22379
+ console.log(source_default.dim(" Run ") + indigo6("skills login") + source_default.dim(" first."));
22176
22380
  console.log();
22177
22381
  return;
22178
22382
  }
@@ -22225,7 +22429,7 @@ Feedback cancelled.`));
22225
22429
  }
22226
22430
 
22227
22431
  // src/commands/info.ts
22228
- var indigo5 = source_default.hex("#6366f1");
22432
+ var indigo7 = source_default.hex("#6366f1");
22229
22433
  async function infoCommand(name) {
22230
22434
  const spinner = ora("Fetching skill info...").start();
22231
22435
  const result = await getSkill(name);
@@ -22237,7 +22441,7 @@ async function infoCommand(name) {
22237
22441
  spinner.stop();
22238
22442
  const skill = result.data;
22239
22443
  console.log();
22240
- console.log(indigo5.bold(skill.name) + (skill.isVerified ? indigo5(" ✓") : ""));
22444
+ console.log(indigo7.bold(skill.name) + (skill.isVerified ? indigo7(" ✓") : ""));
22241
22445
  console.log(source_default.dim(`v${skill.version}`));
22242
22446
  console.log();
22243
22447
  if (skill.description) {
@@ -22254,7 +22458,7 @@ async function infoCommand(name) {
22254
22458
  ["Downloads", skill.downloadCount.toLocaleString()],
22255
22459
  ["Verified", skill.isVerified ? source_default.green("Yes") : "No"],
22256
22460
  ["Remote Execution", skill.isRemoteExecution ? source_default.green("Yes") : "No"],
22257
- ["Credits/Run", (skill.creditsPerExecution ?? 0) > 0 ? indigo5(String(skill.creditsPerExecution)) : source_default.green("Free")]
22461
+ ["Credits/Run", (skill.creditsPerExecution ?? 0) > 0 ? indigo7(String(skill.creditsPerExecution)) : source_default.green("Free")]
22258
22462
  ];
22259
22463
  for (const [label, value] of details) {
22260
22464
  console.log(` ${source_default.dim(label.padEnd(18))} ${value}`);
@@ -22266,15 +22470,19 @@ async function infoCommand(name) {
22266
22470
  console.log();
22267
22471
  console.log(source_default.dim("─".repeat(50)));
22268
22472
  console.log();
22269
- console.log(` ${source_default.dim("Install:")} ${indigo5(`skills install ${skill.slug}`)}`);
22473
+ console.log(` ${source_default.dim("Install:")} ${indigo7(`skills install ${skill.slug}`)}`);
22270
22474
  if (skill.isRemoteExecution) {
22271
- console.log(` ${source_default.dim("Run:")} ${indigo5(`skills run ${skill.slug}`)}`);
22475
+ console.log(` ${source_default.dim("Run:")} ${indigo7(`skills run ${skill.slug}`)}`);
22272
22476
  }
22273
22477
  console.log();
22274
22478
  }
22275
22479
 
22276
22480
  // src/commands/credits.ts
22277
- var indigo6 = source_default.hex("#6366f1");
22481
+ var import_prompts3 = __toESM(require_prompts3(), 1);
22482
+ var indigo8 = source_default.hex("#6366f1");
22483
+ var MIN_PURCHASE_DOLLARS = 5;
22484
+ var MAX_PURCHASE_DOLLARS = 500;
22485
+ var CENTS_PER_CREDIT = 10;
22278
22486
  async function creditsCommand() {
22279
22487
  const apiKey = getApiKey();
22280
22488
  if (!apiKey) {
@@ -22292,23 +22500,138 @@ async function creditsCommand() {
22292
22500
  spinner.stop();
22293
22501
  const { creditsBalance, plan, tenantName } = result.data;
22294
22502
  console.log();
22295
- console.log(indigo6.bold("Credits Balance"));
22503
+ console.log(indigo8.bold("Credits Balance"));
22296
22504
  console.log();
22297
- console.log(" " + indigo6.bold(creditsBalance.toLocaleString()) + " credits");
22505
+ console.log(" " + indigo8.bold(creditsBalance.toLocaleString()) + " credits");
22298
22506
  console.log();
22299
22507
  console.log(source_default.dim("─".repeat(30)));
22300
22508
  console.log();
22301
22509
  console.log(" Plan: " + source_default.bold(plan));
22302
22510
  console.log(" Org: " + (tenantName || source_default.dim("Personal")));
22303
22511
  console.log();
22304
- console.log(source_default.dim("Purchase more at https://skills.md/billing"));
22512
+ console.log(source_default.dim("Purchase more at https://skills.md/billing or run `skills credits buy <amount>`"));
22513
+ console.log();
22514
+ }
22515
+ async function buyCreditsCommand(amount) {
22516
+ const apiKey = getApiKey();
22517
+ if (!apiKey) {
22518
+ console.log(source_default.yellow("Not logged in"));
22519
+ console.log(source_default.dim("Run `skills login` to authenticate"));
22520
+ return;
22521
+ }
22522
+ const dollars = parseFloat(amount);
22523
+ if (isNaN(dollars)) {
22524
+ console.log(source_default.red("Invalid amount. Please enter a number (e.g., 10, 25.50)"));
22525
+ return;
22526
+ }
22527
+ if (dollars < MIN_PURCHASE_DOLLARS) {
22528
+ console.log(source_default.red(`Minimum purchase is $${MIN_PURCHASE_DOLLARS.toFixed(2)}`));
22529
+ return;
22530
+ }
22531
+ if (dollars > MAX_PURCHASE_DOLLARS) {
22532
+ console.log(source_default.red(`Maximum purchase is $${MAX_PURCHASE_DOLLARS.toFixed(2)}`));
22533
+ return;
22534
+ }
22535
+ const amountCents = Math.round(dollars * 100);
22536
+ const credits = Math.floor(amountCents / CENTS_PER_CREDIT);
22537
+ console.log();
22538
+ console.log(indigo8.bold("Purchase Credits"));
22539
+ console.log();
22540
+ console.log(" Amount: " + source_default.bold(`$${(amountCents / 100).toFixed(2)}`));
22541
+ console.log(" Credits: " + source_default.bold(credits.toLocaleString()));
22542
+ console.log();
22543
+ const spinner = ora("Checking purchase requirements...").start();
22544
+ const settingsResult = await getCLI2FASettings();
22545
+ if (settingsResult.error) {
22546
+ spinner.fail("Failed to check settings");
22547
+ console.error(source_default.red(settingsResult.error));
22548
+ return;
22549
+ }
22550
+ spinner.stop();
22551
+ let totpCode;
22552
+ if (settingsResult.data?.enabled) {
22553
+ console.log(source_default.yellow("2FA verification required for CLI purchases."));
22554
+ console.log();
22555
+ const response = await import_prompts3.default({
22556
+ type: "text",
22557
+ name: "code",
22558
+ message: "Enter 2FA code:",
22559
+ validate: (value) => {
22560
+ const normalized = value.replace(/[\s-]/g, "");
22561
+ if (/^\d{6}$/.test(normalized) || /^[A-Fa-f0-9]{8}$/.test(normalized)) {
22562
+ return true;
22563
+ }
22564
+ return "Enter a 6-digit authenticator code or 8-character backup code";
22565
+ }
22566
+ });
22567
+ if (!response.code) {
22568
+ console.log(source_default.yellow("Purchase cancelled."));
22569
+ return;
22570
+ }
22571
+ totpCode = response.code.replace(/[\s-]/g, "");
22572
+ }
22573
+ const confirmResponse = await import_prompts3.default({
22574
+ type: "confirm",
22575
+ name: "confirmed",
22576
+ message: `Purchase ${credits.toLocaleString()} credits for $${(amountCents / 100).toFixed(2)}?`,
22577
+ initial: true
22578
+ });
22579
+ if (!confirmResponse.confirmed) {
22580
+ console.log(source_default.yellow("Purchase cancelled."));
22581
+ return;
22582
+ }
22583
+ const purchaseSpinner = ora("Processing purchase...").start();
22584
+ const result = await purchaseCredits(amountCents, totpCode);
22585
+ if (result.error || !result.data) {
22586
+ purchaseSpinner.fail("Purchase failed");
22587
+ console.log();
22588
+ const errorData = result;
22589
+ const errorCode = errorData.data?.code;
22590
+ switch (errorCode) {
22591
+ case "2FA_REQUIRED":
22592
+ console.log(source_default.red("2FA verification is required for CLI purchases."));
22593
+ console.log(source_default.dim("Enable 2FA in your account settings first."));
22594
+ break;
22595
+ case "INVALID_2FA":
22596
+ console.log(source_default.red("Invalid 2FA code. Please try again."));
22597
+ break;
22598
+ case "NO_PAYMENT_METHOD":
22599
+ console.log(source_default.red("No payment method on file."));
22600
+ console.log(source_default.dim("Add a card at: " + indigo8("https://skills.md/dashboard/billing")));
22601
+ break;
22602
+ case "CARD_DECLINED":
22603
+ console.log(source_default.red("Card was declined. Please check your card or try a different payment method."));
22604
+ break;
22605
+ case "INSUFFICIENT_FUNDS":
22606
+ console.log(source_default.red("Insufficient funds. Please try a different payment method."));
22607
+ break;
22608
+ case "EXPIRED_CARD":
22609
+ console.log(source_default.red("Card has expired. Please update your payment method."));
22610
+ break;
22611
+ case "REQUIRES_ACTION":
22612
+ console.log(source_default.red("Payment requires additional verification."));
22613
+ console.log(source_default.dim("Please complete the purchase via the web dashboard."));
22614
+ break;
22615
+ case "SPENDING_LIMIT_EXCEEDED":
22616
+ console.log(source_default.red("Monthly spending limit would be exceeded."));
22617
+ break;
22618
+ default:
22619
+ console.log(source_default.red(result.error || "Unknown error"));
22620
+ }
22621
+ return;
22622
+ }
22623
+ purchaseSpinner.succeed("Purchase successful!");
22624
+ console.log();
22625
+ console.log(source_default.green("✓") + " Credits added: " + source_default.bold(result.data.creditsAdded.toLocaleString()));
22626
+ console.log(source_default.green("✓") + " New balance: " + source_default.bold(result.data.newBalance.toLocaleString()) + " credits");
22627
+ console.log(source_default.dim(" Transaction: " + result.data.transactionId));
22305
22628
  console.log();
22306
22629
  }
22307
22630
 
22308
22631
  // src/commands/update.ts
22309
22632
  import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
22310
22633
  import { join as join9 } from "path";
22311
- var indigo7 = source_default.hex("#6366f1");
22634
+ var indigo9 = source_default.hex("#6366f1");
22312
22635
  function parseFrontmatter2(content) {
22313
22636
  const result = {};
22314
22637
  const match = content.match(/^---\n([\s\S]*?)\n---/);
@@ -22368,7 +22691,7 @@ async function updateCommand(skillName, options = {}) {
22368
22691
  return;
22369
22692
  }
22370
22693
  console.log();
22371
- console.log(indigo7.bold("Updating Skills"));
22694
+ console.log(indigo9.bold("Updating Skills"));
22372
22695
  console.log();
22373
22696
  let updated = 0;
22374
22697
  let upToDate = 0;
@@ -22386,7 +22709,7 @@ async function updateCommand(skillName, options = {}) {
22386
22709
  const currentVersion = skill.version;
22387
22710
  if (newVersion !== currentVersion) {
22388
22711
  writeFileSync6(join9(skill.dir, "SKILL.md"), result.data.skillMdContent);
22389
- spinner.succeed(`${skill.name}: ${source_default.dim(currentVersion)} ${indigo7("→")} ${indigo7(newVersion)}`);
22712
+ spinner.succeed(`${skill.name}: ${source_default.dim(currentVersion)} ${indigo9("→")} ${indigo9(newVersion)}`);
22390
22713
  updated++;
22391
22714
  } else {
22392
22715
  spinner.succeed(`${skill.name}: ${source_default.dim(`v${currentVersion}`)} ${source_default.green("(up to date)")}`);
@@ -22402,7 +22725,7 @@ async function updateCommand(skillName, options = {}) {
22402
22725
  console.log();
22403
22726
  const summary = [];
22404
22727
  if (updated > 0)
22405
- summary.push(indigo7(`${updated} updated`));
22728
+ summary.push(indigo9(`${updated} updated`));
22406
22729
  if (upToDate > 0)
22407
22730
  summary.push(source_default.green(`${upToDate} up to date`));
22408
22731
  if (failed > 0)
@@ -22413,11 +22736,11 @@ async function updateCommand(skillName, options = {}) {
22413
22736
 
22414
22737
  // src/commands/upgrade.ts
22415
22738
  import { execSync } from "child_process";
22416
- var indigo8 = source_default.hex("#6366f1");
22739
+ var indigo10 = source_default.hex("#6366f1");
22417
22740
  var PACKAGE_NAME = "@hasnatools/skills";
22418
22741
  async function upgradeCommand() {
22419
22742
  console.log();
22420
- console.log(indigo8.bold("Upgrade Skills CLI"));
22743
+ console.log(indigo10.bold("Upgrade Skills CLI"));
22421
22744
  console.log();
22422
22745
  const currentVersion = process.env.npm_package_version || getInstalledVersion();
22423
22746
  console.log(` Current: ${source_default.dim(`v${currentVersion}`)}`);
@@ -22438,7 +22761,7 @@ async function upgradeCommand() {
22438
22761
  execSync(upgradeCmd, {
22439
22762
  stdio: ["pipe", "pipe", "pipe"]
22440
22763
  });
22441
- spinner.succeed(`Upgraded to ${indigo8(`v${latestVersion}`)}`);
22764
+ spinner.succeed(`Upgraded to ${indigo10(`v${latestVersion}`)}`);
22442
22765
  console.log();
22443
22766
  console.log(source_default.dim(" Restart your terminal to use the new version"));
22444
22767
  console.log();
@@ -22482,10 +22805,10 @@ function detectPackageManager() {
22482
22805
  // src/commands/doctor.ts
22483
22806
  import { existsSync as existsSync10 } from "fs";
22484
22807
  import { execSync as execSync2 } from "child_process";
22485
- var indigo9 = source_default.hex("#6366f1");
22808
+ var indigo11 = source_default.hex("#6366f1");
22486
22809
  async function doctorCommand() {
22487
22810
  console.log();
22488
- console.log(indigo9.bold("Skills Doctor"));
22811
+ console.log(indigo11.bold("Skills Doctor"));
22489
22812
  console.log(source_default.dim("Checking your environment..."));
22490
22813
  console.log();
22491
22814
  const results = [];
@@ -22589,7 +22912,7 @@ async function doctorCommand() {
22589
22912
  console.log(source_default.yellow(` ${warnCount} warning(s)`));
22590
22913
  }
22591
22914
  if (okCount === results.length) {
22592
- console.log(indigo9.bold(" All checks passed!"));
22915
+ console.log(indigo11.bold(" All checks passed!"));
22593
22916
  }
22594
22917
  console.log();
22595
22918
  if (!apiKey) {
@@ -22599,9 +22922,9 @@ async function doctorCommand() {
22599
22922
  }
22600
22923
 
22601
22924
  // src/index.ts
22602
- var indigo10 = source_default.hex("#6366f1");
22925
+ var indigo12 = source_default.hex("#6366f1");
22603
22926
  var program2 = new Command;
22604
- program2.name("skills").description("CLI for skills.md - AI Agent Skills Marketplace").version("0.1.9");
22927
+ program2.name("skills").description("CLI for skills.md - AI Agent Skills Marketplace").version("0.1.11");
22605
22928
  program2.command("init").description("Initialize skills.md in current project").option("-f, --force", "Force re-initialization (removes existing .skills/)").action((options) => {
22606
22929
  initCommand({ force: options.force });
22607
22930
  });
@@ -22649,12 +22972,13 @@ program2.command("run <name> [args...]").description("Run a skill").option("-t,
22649
22972
  args
22650
22973
  });
22651
22974
  });
22652
- program2.command("config").description("View or modify configuration").option("--get <key>", "Get a config value").option("--set <key=value>", "Set a config value").action((options) => {
22975
+ var configCmd = program2.command("config").description("View or modify configuration").option("--get <key>", "Get a config value").option("--set <key=value>", "Set a config value").action((options) => {
22653
22976
  configCommand({
22654
22977
  get: options.get,
22655
22978
  set: options.set
22656
22979
  });
22657
22980
  });
22981
+ configCmd.command("cli-2fa [action]").description("Enable/disable 2FA for CLI purchases (on/off)").action(cli2faCommand);
22658
22982
  program2.command("target [platform]").description("Get or set default target platform").action(targetCommand);
22659
22983
  program2.command("generate <mediaType> <prompt>").alias("gen").description("Generate content (image, video, audio, text)").option("-o, --output <path>", "Save output to file").option("-p, --provider <provider>", "Specify provider (e.g., fal, replicate)").option("-a, --async", "Run asynchronously and return job ID").action((mediaType, prompt, options) => {
22660
22984
  generateCommand(mediaType, prompt, {
@@ -22664,11 +22988,13 @@ program2.command("generate <mediaType> <prompt>").alias("gen").description("Gene
22664
22988
  });
22665
22989
  });
22666
22990
  program2.command("jobs [jobId]").description("List generation jobs or view job details").action(jobsCommand);
22667
- program2.command("history").description("View execution history").option("-s, --skill <name>", "Filter by skill name").option("-n, --limit <number>", "Limit number of entries", "20").option("-v, --verbose", "Show detailed output").action((options) => {
22991
+ program2.command("history").description("View execution history").option("-s, --skill <name>", "Filter by skill name").option("-n, --limit <number>", "Limit number of entries", "20").option("-v, --verbose", "Show detailed output").option("--status <status>", "Filter by status (completed, failed, pending)").option("-l, --local", "Show local history only (skip API)").action((options) => {
22668
22992
  historyCommand({
22669
22993
  skill: options.skill,
22670
22994
  limit: parseInt(options.limit, 10),
22671
- verbose: options.verbose
22995
+ verbose: options.verbose,
22996
+ status: options.status,
22997
+ local: options.local
22672
22998
  });
22673
22999
  });
22674
23000
  program2.command("exports <skill>").description("View exported files for a skill").option("-t, --target <target>", "Target platform (claude, codex)").action((skill, options) => {
@@ -22688,14 +23014,17 @@ program2.command("feedback").description("Submit feedback, bug reports, or featu
22688
23014
  });
22689
23015
  });
22690
23016
  program2.command("info <name>").description("Show detailed information about a skill").action(infoCommand);
22691
- program2.command("credits").description("Check your credits balance").action(creditsCommand);
23017
+ var creditsCmd = program2.command("credits").description("Manage credits");
23018
+ creditsCmd.command("balance").alias("bal").description("Check your credits balance").action(creditsCommand);
23019
+ creditsCmd.command("buy <amount>").description("Purchase credits ($5-$500)").action(buyCreditsCommand);
23020
+ creditsCmd.action(creditsCommand);
22692
23021
  program2.command("update [skill]").description("Update installed skills to latest versions").option("-t, --target <target>", "Target platform (claude, codex)").action((skill, options) => {
22693
23022
  updateCommand(skill, { target: options.target });
22694
23023
  });
22695
23024
  program2.command("upgrade").description("Upgrade the Skills CLI to the latest version").action(upgradeCommand);
22696
23025
  program2.command("doctor").description("Check your environment and diagnose issues").action(doctorCommand);
22697
23026
  program2.addHelpText("after", `
22698
- ${indigo10.bold("Examples:")}
23027
+ ${indigo12.bold("Examples:")}
22699
23028
  ${source_default.dim("# Initialize in current project")}
22700
23029
  $ skills init
22701
23030
 
@@ -22723,6 +23052,8 @@ ${indigo10.bold("Examples:")}
22723
23052
  ${source_default.dim("# View execution history")}
22724
23053
  $ skills history
22725
23054
  $ skills history --skill image-generator
23055
+ $ skills history --status completed
23056
+ $ skills history --local
22726
23057
 
22727
23058
  ${source_default.dim("# View exports for a skill")}
22728
23059
  $ skills exports image-generator
@@ -22739,6 +23070,12 @@ ${indigo10.bold("Examples:")}
22739
23070
  ${source_default.dim("# Check credits balance")}
22740
23071
  $ skills credits
22741
23072
 
23073
+ ${source_default.dim("# Purchase credits")}
23074
+ $ skills credits buy 25
23075
+
23076
+ ${source_default.dim("# Enable 2FA for CLI purchases")}
23077
+ $ skills config cli-2fa on
23078
+
22742
23079
  ${source_default.dim("# Update installed skills")}
22743
23080
  $ skills update
22744
23081
 
@@ -22748,7 +23085,7 @@ ${indigo10.bold("Examples:")}
22748
23085
  ${source_default.dim("# Check environment")}
22749
23086
  $ skills doctor
22750
23087
 
22751
- ${indigo10.bold("Documentation:")}
22752
- ${indigo10("https://skills.md/docs")}
23088
+ ${indigo12.bold("Documentation:")}
23089
+ ${indigo12("https://skills.md/docs")}
22753
23090
  `);
22754
23091
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasnatools/skills",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "CLI for skills.md - AI Agent Skills Marketplace",
5
5
  "type": "module",
6
6
  "bin": {