@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.
- package/README.md +32 -8
- package/dist/index.js +282 -4
- 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
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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);
|