@pushrec/skills 0.1.0 → 0.2.0

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 (3) hide show
  1. package/README.md +32 -8
  2. package/dist/index.js +282 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,23 +2,47 @@
2
2
 
3
3
  CLI for the pushREC Agent Skills Marketplace. Install, manage, and update Claude Code skills.
4
4
 
5
- ## Installation
5
+ **Version:** `0.1.0` | **Published:** 2026-02-19
6
+
7
+ ## Quick Start
6
8
 
7
9
  ```bash
10
+ # One-off (no install required)
11
+ npx @pushrec/skills auth login
12
+
13
+ # Or install globally
8
14
  npm install -g @pushrec/skills
15
+ pushrec-skills auth login
9
16
  ```
10
17
 
11
18
  ## Commands
12
19
 
20
+ ### Auth
21
+
22
+ | Command | Description |
23
+ |---------|-------------|
24
+ | `pushrec-skills auth login` | Activate your license key on this device (interactive prompt) |
25
+ | `pushrec-skills auth logout` | Remove license key and deactivate this device |
26
+ | `pushrec-skills auth status` | Show current license and device status |
27
+ | `pushrec-skills auth deactivate-device` | Remove this device from license (frees a slot) |
28
+
29
+ ### Skills
30
+
31
+ | Command | Description |
32
+ |---------|-------------|
33
+ | `pushrec-skills list` | Browse available skills |
34
+ | `pushrec-skills install <name>` | Install a skill |
35
+ | `pushrec-skills install --all` | Install all licensed skills |
36
+ | `pushrec-skills update [name]` | Update installed skills |
37
+ | `pushrec-skills search <query>` | Search the catalog |
38
+ | `pushrec-skills info <name>` | Get skill details |
39
+ | `pushrec-skills uninstall <name>` | Remove a skill |
40
+
41
+ ### Utility
42
+
13
43
  | Command | Description |
14
44
  |---------|-------------|
15
- | `skills auth <key>` | Activate your license key |
16
- | `skills list` | Browse available skills |
17
- | `skills install <name>` | Install a skill |
18
- | `skills update [name]` | Update installed skills |
19
- | `skills search <query>` | Search the catalog |
20
- | `skills info <name>` | Get skill details |
21
- | `skills uninstall <name>` | Remove a skill |
45
+ | `pushrec-skills health` | Check registry connectivity |
22
46
 
23
47
  ## Registry
24
48
 
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_commander9 = require("commander");
27
+ var import_commander10 = require("commander");
28
28
 
29
29
  // src/commands/auth.ts
30
30
  var import_commander = require("commander");
@@ -1470,8 +1470,12 @@ function createUninstallCommand() {
1470
1470
 
1471
1471
  // src/commands/health.ts
1472
1472
  var import_commander8 = require("commander");
1473
+ var import_chalk3 = __toESM(require("chalk"));
1473
1474
  function healthCommand() {
1474
- const cmd = new import_commander8.Command("health").description("Check registry server health").option("--json", "Output as JSON").action(async (opts) => {
1475
+ const cmd = new import_commander8.Command("health").description("Check registry server health").option("--full", "Run full diagnostic (registry + license + device + skills)").option("--json", "Output as JSON").action(async (opts) => {
1476
+ if (opts.full) {
1477
+ return runFullDiagnostic(opts.json ?? false);
1478
+ }
1475
1479
  let response;
1476
1480
  try {
1477
1481
  response = await registryFetch("/api/health");
@@ -1514,12 +1518,285 @@ function healthCommand() {
1514
1518
  });
1515
1519
  return cmd;
1516
1520
  }
1521
+ async function runFullDiagnostic(json) {
1522
+ const checks = [];
1523
+ try {
1524
+ const response = await registryFetch("/api/health");
1525
+ if (response.ok) {
1526
+ checks.push({ name: "Registry", status: "pass", detail: `OK (${response.status})` });
1527
+ } else {
1528
+ checks.push({ name: "Registry", status: "fail", detail: `Unhealthy (${response.status})` });
1529
+ }
1530
+ } catch (err) {
1531
+ checks.push({ name: "Registry", status: "fail", detail: `Unreachable: ${err instanceof Error ? err.message : String(err)}` });
1532
+ }
1533
+ const key = await retrieveLicenseKey();
1534
+ if (!key) {
1535
+ checks.push({ name: "License", status: "fail", detail: "No license key found. Run: pushrec-skills setup" });
1536
+ checks.push({ name: "Device", status: "skip", detail: "Skipped (no license)" });
1537
+ checks.push({ name: "Skills", status: "skip", detail: "Skipped (no license)" });
1538
+ return printDiagnostic(checks, json);
1539
+ }
1540
+ const offlineResult = await verifyOffline(key);
1541
+ if (!offlineResult.valid) {
1542
+ checks.push({ name: "License", status: "fail", detail: `Invalid signature: ${offlineResult.error}` });
1543
+ checks.push({ name: "Device", status: "skip", detail: "Skipped (invalid license)" });
1544
+ checks.push({ name: "Skills", status: "skip", detail: "Skipped (invalid license)" });
1545
+ return printDiagnostic(checks, json);
1546
+ }
1547
+ const fingerprint = getMachineFingerprint();
1548
+ try {
1549
+ const onlineResult = await verifyLicenseOnline(key, fingerprint);
1550
+ if (onlineResult.valid) {
1551
+ const status = onlineResult.status ?? "active";
1552
+ const devices = `${onlineResult.deviceCount ?? "?"}/${onlineResult.maxDevices ?? "?"}`;
1553
+ checks.push({ name: "License", status: "pass", detail: `${status} | devices: ${devices}` });
1554
+ checks.push({ name: "Device", status: "pass", detail: `Registered (${devices} slots)` });
1555
+ } else {
1556
+ checks.push({ name: "License", status: "fail", detail: `Server rejected: ${onlineResult.error}` });
1557
+ checks.push({ name: "Device", status: "fail", detail: "Not verified (license rejected)" });
1558
+ }
1559
+ } catch {
1560
+ const cache = loadLicenseCache();
1561
+ if (cache) {
1562
+ const offlineStatus = checkOfflineStatus(cache);
1563
+ if (offlineStatus.status === "disabled") {
1564
+ checks.push({ name: "License", status: "fail", detail: `Offline too long: ${offlineStatus.message}` });
1565
+ checks.push({ name: "Device", status: "fail", detail: "Cannot verify offline" });
1566
+ } else {
1567
+ checks.push({ name: "License", status: "warn", detail: `Cached (${offlineStatus.status}): ${offlineStatus.message ?? "OK"}` });
1568
+ checks.push({ name: "Device", status: "warn", detail: "Using cached verification" });
1569
+ }
1570
+ } else {
1571
+ checks.push({ name: "License", status: "fail", detail: "Cannot reach server and no cached verification" });
1572
+ checks.push({ name: "Device", status: "fail", detail: "Cannot verify" });
1573
+ }
1574
+ }
1575
+ const config = loadConfig();
1576
+ const skillCount = Object.keys(config.installedSkills).length;
1577
+ if (skillCount > 0) {
1578
+ const names = Object.keys(config.installedSkills).join(", ");
1579
+ checks.push({ name: "Skills", status: "pass", detail: `${skillCount} installed: ${names}` });
1580
+ } else {
1581
+ checks.push({ name: "Skills", status: "warn", detail: "No skills installed. Run: pushrec-skills install --all" });
1582
+ }
1583
+ printDiagnostic(checks, json);
1584
+ }
1585
+ function printDiagnostic(checks, json) {
1586
+ if (json) {
1587
+ const allPass = checks.every((c) => c.status === "pass" || c.status === "skip");
1588
+ console.log(JSON.stringify({ ok: allPass, checks }, null, 2));
1589
+ if (!allPass) process.exit(1);
1590
+ return;
1591
+ }
1592
+ console.log("");
1593
+ console.log(import_chalk3.default.bold("Pushrec Skills Health Check"));
1594
+ console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1595
+ console.log("");
1596
+ let hasFailure = false;
1597
+ for (const check of checks) {
1598
+ const icon = check.status === "pass" ? import_chalk3.default.green("PASS") : check.status === "fail" ? import_chalk3.default.red("FAIL") : check.status === "warn" ? import_chalk3.default.yellow("WARN") : import_chalk3.default.dim("SKIP");
1599
+ console.log(` ${icon} ${check.name}: ${check.detail}`);
1600
+ if (check.status === "fail") hasFailure = true;
1601
+ }
1602
+ console.log("");
1603
+ if (hasFailure) {
1604
+ console.log(import_chalk3.default.red("Some checks failed. Fix the issues above and run again."));
1605
+ process.exit(1);
1606
+ } else {
1607
+ console.log(import_chalk3.default.green("All systems go!"));
1608
+ }
1609
+ }
1610
+
1611
+ // src/commands/setup.ts
1612
+ var import_commander9 = require("commander");
1613
+ var import_readline2 = require("readline");
1614
+ var import_chalk4 = __toESM(require("chalk"));
1615
+ var import_ora2 = __toESM(require("ora"));
1616
+ function readInput2(question) {
1617
+ const rl = (0, import_readline2.createInterface)({
1618
+ input: process.stdin,
1619
+ output: process.stdout
1620
+ });
1621
+ return new Promise((resolve2, reject) => {
1622
+ rl.on("error", (err) => {
1623
+ rl.close();
1624
+ reject(err);
1625
+ });
1626
+ rl.on("close", () => {
1627
+ resolve2("");
1628
+ });
1629
+ rl.question(question, (answer) => {
1630
+ rl.close();
1631
+ resolve2(answer.trim());
1632
+ });
1633
+ });
1634
+ }
1635
+ function setupCommand() {
1636
+ const cmd = new import_commander9.Command("setup").description("One-command onboarding: verify license, activate device, install all skills").option("--key <license-key>", "License key (or enter interactively)").option("--json", "Output JSON").action(async (opts) => {
1637
+ const existing = await retrieveLicenseKey();
1638
+ if (existing) {
1639
+ if (opts.json) {
1640
+ console.log(JSON.stringify({ ok: true, message: "Already set up" }));
1641
+ } else {
1642
+ console.log(import_chalk4.default.green("Already set up!") + " Run " + import_chalk4.default.cyan("pushrec-skills auth status") + " to check your license.");
1643
+ console.log("To re-setup, run " + import_chalk4.default.cyan("pushrec-skills auth logout") + " first.");
1644
+ }
1645
+ return;
1646
+ }
1647
+ if (!opts.json) {
1648
+ console.log("");
1649
+ console.log(import_chalk4.default.bold("Pushrec Skills Setup"));
1650
+ console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1651
+ console.log("");
1652
+ }
1653
+ const key = opts.key ?? await readInput2("Paste your license key: ");
1654
+ if (!key) {
1655
+ if (opts.json) {
1656
+ console.log(JSON.stringify({ ok: false, error: "No license key provided" }));
1657
+ } else {
1658
+ console.error("No license key provided.");
1659
+ }
1660
+ process.exit(1);
1661
+ }
1662
+ const verifySpinner = opts.json ? null : (0, import_ora2.default)("Verifying license signature...").start();
1663
+ const offlineResult = await verifyOffline(key);
1664
+ if (!offlineResult.valid) {
1665
+ verifySpinner?.fail(`Invalid license key: ${offlineResult.error}`);
1666
+ if (opts.json) {
1667
+ console.log(JSON.stringify({ ok: false, error: `Invalid license key: ${offlineResult.error}` }));
1668
+ }
1669
+ process.exit(1);
1670
+ }
1671
+ verifySpinner?.succeed("License signature valid");
1672
+ const onlineSpinner = opts.json ? null : (0, import_ora2.default)("Checking with server...").start();
1673
+ const fingerprint = getMachineFingerprint();
1674
+ let onlineResult;
1675
+ try {
1676
+ onlineResult = await verifyLicenseOnline(key, fingerprint);
1677
+ } catch (err) {
1678
+ const message = err instanceof Error ? err.message : String(err);
1679
+ onlineSpinner?.fail(`Could not reach server: ${message}`);
1680
+ if (opts.json) {
1681
+ console.log(JSON.stringify({ ok: false, error: `Could not reach server: ${message}` }));
1682
+ }
1683
+ process.exit(1);
1684
+ }
1685
+ if (!onlineResult.valid) {
1686
+ onlineSpinner?.fail(`License rejected: ${onlineResult.error}`);
1687
+ if (opts.json) {
1688
+ console.log(JSON.stringify({ ok: false, error: `License rejected: ${onlineResult.error}` }));
1689
+ }
1690
+ process.exit(1);
1691
+ }
1692
+ onlineSpinner?.succeed("Server confirmed license active");
1693
+ await storeLicenseKey(key);
1694
+ const deviceSpinner = opts.json ? null : (0, import_ora2.default)("Activating this device...").start();
1695
+ let activation;
1696
+ try {
1697
+ activation = await activateDevice(
1698
+ key,
1699
+ fingerprint,
1700
+ getPlatformName(),
1701
+ getHostname()
1702
+ );
1703
+ deviceSpinner?.succeed(`Device activated (${activation.deviceCount}/${activation.maxDevices} slots used)`);
1704
+ } catch (err) {
1705
+ const message = err instanceof Error ? err.message : String(err);
1706
+ deviceSpinner?.fail(`Device activation failed: ${message}`);
1707
+ if (opts.json) {
1708
+ console.log(JSON.stringify({ ok: false, error: `Device activation failed: ${message}` }));
1709
+ }
1710
+ process.exit(1);
1711
+ }
1712
+ const cache = {
1713
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
1714
+ expiresAt: onlineResult.updatesExpireAt ?? null,
1715
+ fingerprint,
1716
+ payload: {
1717
+ buyerId: onlineResult.buyerId ?? 0,
1718
+ email: onlineResult.email ?? "",
1719
+ maxDevices: onlineResult.maxDevices ?? 3,
1720
+ status: onlineResult.status ?? "active",
1721
+ hasUpdates: onlineResult.hasUpdates ?? true,
1722
+ deviceCount: onlineResult.deviceCount ?? 1
1723
+ }
1724
+ };
1725
+ saveLicenseCache(cache);
1726
+ if (!opts.json) {
1727
+ console.log("");
1728
+ console.log(import_chalk4.default.bold("Installing skills..."));
1729
+ }
1730
+ let catalog;
1731
+ try {
1732
+ catalog = await fetchSkillCatalog();
1733
+ } catch (err) {
1734
+ const message = err instanceof Error ? err.message : String(err);
1735
+ if (opts.json) {
1736
+ console.log(JSON.stringify({ ok: true, activated: true, installed: 0, error: `Could not fetch catalog: ${message}` }));
1737
+ } else {
1738
+ console.error(`Could not fetch catalog: ${message}`);
1739
+ console.log(import_chalk4.default.yellow("License activated but skill install failed. Run ") + import_chalk4.default.cyan("pushrec-skills install --all") + import_chalk4.default.yellow(" to retry."));
1740
+ }
1741
+ return;
1742
+ }
1743
+ const toInstall = catalog.items.filter((s) => !isSkillInstalled(s.name));
1744
+ let installed = 0;
1745
+ let failed = 0;
1746
+ for (let i = 0; i < toInstall.length; i++) {
1747
+ const skill = toInstall[i];
1748
+ const prefix = opts.json ? "" : `[${i + 1}/${toInstall.length}] `;
1749
+ const spinner = opts.json ? null : (0, import_ora2.default)(`${prefix}Installing ${skill.name}...`).start();
1750
+ try {
1751
+ const manifest = await fetchSkillManifest(skill.name);
1752
+ const archive = await downloadSkill(skill.name);
1753
+ const { path } = await installSkill(skill.name, archive);
1754
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1755
+ const config = loadConfig();
1756
+ config.installedSkills[skill.name] = {
1757
+ name: skill.name,
1758
+ version: manifest.currentVersion ?? "0.0.0",
1759
+ installedAt: config.installedSkills[skill.name]?.installedAt ?? now,
1760
+ updatedAt: now
1761
+ };
1762
+ updateConfig({ installedSkills: config.installedSkills });
1763
+ spinner?.succeed(`${prefix}${skill.name} v${manifest.currentVersion ?? "latest"} installed to ${path}`);
1764
+ installed++;
1765
+ } catch (err) {
1766
+ const message = err instanceof Error ? err.message : String(err);
1767
+ spinner?.fail(`${prefix}Failed to install ${skill.name}: ${message}`);
1768
+ failed++;
1769
+ }
1770
+ }
1771
+ const skipped = catalog.items.length - toInstall.length;
1772
+ if (opts.json) {
1773
+ console.log(JSON.stringify({
1774
+ ok: true,
1775
+ activated: true,
1776
+ installed,
1777
+ failed,
1778
+ skipped,
1779
+ total: catalog.items.length
1780
+ }));
1781
+ } else {
1782
+ console.log("");
1783
+ console.log(import_chalk4.default.green.bold("Setup complete!"));
1784
+ console.log(` ${import_chalk4.default.green("+")} ${installed} skills installed`);
1785
+ if (skipped > 0) console.log(` ${import_chalk4.default.dim("-")} ${skipped} already installed`);
1786
+ if (failed > 0) console.log(` ${import_chalk4.default.red("x")} ${failed} failed`);
1787
+ console.log("");
1788
+ console.log("Your skills are ready. Use " + import_chalk4.default.cyan("/<skill-name>") + " in your next Claude Code session.");
1789
+ console.log("Run " + import_chalk4.default.cyan("pushrec-skills health --full") + " to verify everything works.");
1790
+ }
1791
+ });
1792
+ return cmd;
1793
+ }
1517
1794
 
1518
1795
  // src/index.ts
1519
- var program = new import_commander9.Command();
1796
+ var program = new import_commander10.Command();
1520
1797
  program.name("pushrec-skills").description(
1521
1798
  "Install, update, and manage premium Claude Code skills from Pushrec"
1522
- ).version("0.1.0");
1799
+ ).version("0.2.0");
1523
1800
  program.addCommand(authCommand());
1524
1801
  program.addCommand(installCommand());
1525
1802
  program.addCommand(updateCommand());
@@ -1528,6 +1805,7 @@ program.addCommand(searchCommand());
1528
1805
  program.addCommand(infoCommand());
1529
1806
  program.addCommand(createUninstallCommand());
1530
1807
  program.addCommand(healthCommand());
1808
+ program.addCommand(setupCommand());
1531
1809
  program.parseAsync().catch((err) => {
1532
1810
  console.error(err instanceof Error ? err.message : String(err));
1533
1811
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushrec/skills",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Install, update, and manage premium Claude Code skills from Pushrec",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {