skill-organizer 0.0.1

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 ADDED
@@ -0,0 +1,16 @@
1
+ # skill-organizer npm package
2
+
3
+ This package installs the `skill-organizer` CLI by downloading the matching prebuilt binary from GitHub Releases.
4
+
5
+ Agent-first install and onboarding instructions are documented in [`../../AGENTS_README.md`](../../AGENTS_README.md).
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm i -g skill-organizer@alpha
11
+ ```
12
+
13
+ ## Notes
14
+
15
+ - The install script downloads a release artifact for your current OS and architecture.
16
+ - Installs with `--ignore-scripts` are not supported.
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("node:child_process");
4
+ const fs = require("node:fs");
5
+ const path = require("node:path");
6
+
7
+ const extension = process.platform === "win32" ? ".exe" : "";
8
+ const binaryPath = path.join(__dirname, "..", "vendor", `skill-organizer${extension}`);
9
+
10
+ if (!fs.existsSync(binaryPath)) {
11
+ console.error("skill-organizer binary is missing. Reinstall the package without --ignore-scripts.");
12
+ process.exit(1);
13
+ }
14
+
15
+ const child = spawn(binaryPath, process.argv.slice(2), { stdio: "inherit" });
16
+ child.on("exit", (code, signal) => {
17
+ if (signal) {
18
+ process.kill(process.pid, signal);
19
+ return;
20
+ }
21
+ process.exit(code ?? 1);
22
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "skill-organizer",
3
+ "version": "0.0.1",
4
+ "description": "Install the skill-organizer CLI from GitHub Releases",
5
+ "license": "UNLICENSED",
6
+ "homepage": "https://github.com/sergiocarracedo/skill-organizer",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/sergiocarracedo/skill-organizer.git",
10
+ "directory": "cli/packages/npm"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/sergiocarracedo/skill-organizer/issues"
14
+ },
15
+ "keywords": [
16
+ "cli",
17
+ "skills",
18
+ "agent",
19
+ "tooling"
20
+ ],
21
+ "preferGlobal": true,
22
+ "bin": {
23
+ "skill-organizer": "bin/skill-organizer.js"
24
+ },
25
+ "files": [
26
+ "bin",
27
+ "scripts",
28
+ "README.md"
29
+ ],
30
+ "scripts": {
31
+ "postinstall": "node scripts/postinstall.js"
32
+ },
33
+ "engines": {
34
+ "node": ">=18"
35
+ }
36
+ }
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("node:fs");
4
+ const fsp = require("node:fs/promises");
5
+ const crypto = require("node:crypto");
6
+ const https = require("node:https");
7
+ const path = require("node:path");
8
+ const { pipeline } = require("node:stream/promises");
9
+ const { spawn } = require("node:child_process");
10
+
11
+ const pkg = require("../package.json");
12
+
13
+ const owner = process.env.SKILL_ORGANIZER_GITHUB_OWNER || "sergiocarracedo";
14
+ const repo = process.env.SKILL_ORGANIZER_GITHUB_REPO || "skill-organizer";
15
+ const version = pkg.version;
16
+ const tag = `v${version}`;
17
+ const osMap = {
18
+ linux: "Linux",
19
+ darwin: "Darwin",
20
+ win32: "Windows"
21
+ };
22
+ const archMap = {
23
+ x64: "x86_64",
24
+ arm64: "arm64"
25
+ };
26
+
27
+ async function main() {
28
+ const osName = osMap[process.platform];
29
+ const archName = archMap[process.arch];
30
+ if (!osName || !archName) {
31
+ throw new Error(`unsupported platform: ${process.platform}/${process.arch}`);
32
+ }
33
+
34
+ const ext = process.platform === "win32" ? "zip" : "tar.gz";
35
+ const archiveName = `skill-organizer_${version}_${osName}_${archName}.${ext}`;
36
+ const url = `https://github.com/${owner}/${repo}/releases/download/${tag}/${archiveName}`;
37
+ const checksumsUrl = `https://github.com/${owner}/${repo}/releases/download/${tag}/checksums.txt`;
38
+ const tmpDir = path.join(__dirname, "..", ".tmp");
39
+ const vendorDir = path.join(__dirname, "..", "vendor");
40
+ const archivePath = path.join(tmpDir, archiveName);
41
+ const checksumsPath = path.join(tmpDir, "checksums.txt");
42
+
43
+ await fsp.rm(tmpDir, { recursive: true, force: true });
44
+ await fsp.mkdir(tmpDir, { recursive: true });
45
+ await fsp.mkdir(vendorDir, { recursive: true });
46
+
47
+ console.log(`Downloading ${url}`);
48
+ await download(url, archivePath);
49
+ console.log(`Downloading ${checksumsUrl}`);
50
+ await download(checksumsUrl, checksumsPath);
51
+ await verifyChecksum(archivePath, checksumsPath, archiveName);
52
+
53
+ if (process.platform === "win32") {
54
+ await unzip(archivePath, vendorDir);
55
+ } else {
56
+ await untar(archivePath, vendorDir);
57
+ }
58
+
59
+ const binaryName = process.platform === "win32" ? "skill-organizer.exe" : "skill-organizer";
60
+ const extracted = path.join(vendorDir, binaryName);
61
+ if (!fs.existsSync(extracted)) {
62
+ throw new Error(`binary not found after extraction: ${extracted}`);
63
+ }
64
+
65
+ if (process.platform !== "win32") {
66
+ await fsp.chmod(extracted, 0o755);
67
+ }
68
+
69
+ await fsp.rm(tmpDir, { recursive: true, force: true });
70
+ }
71
+
72
+ function download(url, destination) {
73
+ return new Promise((resolve, reject) => {
74
+ https.get(url, (response) => {
75
+ if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
76
+ response.resume();
77
+ download(response.headers.location, destination).then(resolve, reject);
78
+ return;
79
+ }
80
+
81
+ if (response.statusCode !== 200) {
82
+ reject(new Error(`download failed: ${response.statusCode} ${response.statusMessage}`));
83
+ response.resume();
84
+ return;
85
+ }
86
+
87
+ const file = fs.createWriteStream(destination);
88
+ pipeline(response, file).then(resolve, reject);
89
+ }).on("error", reject);
90
+ });
91
+ }
92
+
93
+ function untar(archivePath, destination) {
94
+ return run("tar", ["-xzf", archivePath, "-C", destination]);
95
+ }
96
+
97
+ function unzip(archivePath, destination) {
98
+ return run("powershell", [
99
+ "-NoProfile",
100
+ "-Command",
101
+ `Expand-Archive -LiteralPath '${archivePath.replace(/'/g, "''")}' -DestinationPath '${destination.replace(/'/g, "''")}' -Force`
102
+ ]);
103
+ }
104
+
105
+ function run(command, args) {
106
+ return new Promise((resolve, reject) => {
107
+ const child = spawn(command, args, { stdio: "inherit" });
108
+ child.on("exit", (code) => {
109
+ if (code === 0) {
110
+ resolve();
111
+ return;
112
+ }
113
+ reject(new Error(`${command} exited with code ${code}`));
114
+ });
115
+ child.on("error", reject);
116
+ });
117
+ }
118
+
119
+ async function verifyChecksum(archivePath, checksumsPath, archiveName) {
120
+ const content = await fsp.readFile(checksumsPath, "utf8");
121
+ const line = content
122
+ .split(/\r?\n/)
123
+ .find((entry) => entry.trim().endsWith(` ${archiveName}`) || entry.trim().endsWith(` ${archiveName}`));
124
+
125
+ if (!line) {
126
+ throw new Error(`checksum entry not found for ${archiveName}`);
127
+ }
128
+
129
+ const expected = line.trim().split(/\s+/)[0].toLowerCase();
130
+ const actual = await sha256File(archivePath);
131
+ if (expected !== actual) {
132
+ throw new Error(`checksum mismatch for ${archiveName}`);
133
+ }
134
+ }
135
+
136
+ function sha256File(filePath) {
137
+ return new Promise((resolve, reject) => {
138
+ const hash = crypto.createHash("sha256");
139
+ const stream = fs.createReadStream(filePath);
140
+ stream.on("data", (chunk) => hash.update(chunk));
141
+ stream.on("end", () => resolve(hash.digest("hex")));
142
+ stream.on("error", reject);
143
+ });
144
+ }
145
+
146
+ main().catch((error) => {
147
+ console.error(`Failed to install skill-organizer: ${error.message}`);
148
+ process.exit(1);
149
+ });