@gifflet/ccmd 1.0.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/dist/ccmd-darwin-amd64_darwin_amd64/ccmd +0 -0
- package/dist/ccmd-darwin-arm64_darwin_arm64/ccmd +0 -0
- package/dist/ccmd-linux-amd64_linux_amd64/ccmd +0 -0
- package/dist/ccmd-windows-amd64_windows_amd64/ccmd.exe +0 -0
- package/index.js +71 -0
- package/package.json +48 -0
- package/postinstall.js +221 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const childProcess = require('node:child_process');
|
|
5
|
+
const fsPromises = require('node:fs/promises');
|
|
6
|
+
|
|
7
|
+
// Mapping from Node's `process.arch` to Golang's `$GOARCH`
|
|
8
|
+
const ARCH_MAPPING = {
|
|
9
|
+
x64: 'amd64',
|
|
10
|
+
arm64: 'arm64',
|
|
11
|
+
};
|
|
12
|
+
const PLATFORM_MACOS = 'darwin';
|
|
13
|
+
// Mapping between Node's `process.platform` to Golang's
|
|
14
|
+
const PLATFORM_MAPPING = {
|
|
15
|
+
[PLATFORM_MACOS]: PLATFORM_MACOS,
|
|
16
|
+
linux: 'linux',
|
|
17
|
+
win32: 'windows',
|
|
18
|
+
};
|
|
19
|
+
/** @type {string?} */
|
|
20
|
+
let fullPath = null;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @return {Promise<string>}
|
|
24
|
+
*/
|
|
25
|
+
export async function findCcmdBinary() {
|
|
26
|
+
if (fullPath) {
|
|
27
|
+
// return the previously cached value
|
|
28
|
+
return fullPath;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const binaryName = `ccmd${process.platform === 'win32' ? '.exe' : ''}`;
|
|
32
|
+
const binaryRoot = path.join(__dirname, 'dist');
|
|
33
|
+
const goPlatform = PLATFORM_MAPPING[process.platform];
|
|
34
|
+
const goArch = ARCH_MAPPING[process.arch];
|
|
35
|
+
if (goPlatform && goArch) {
|
|
36
|
+
const sysFolderName = `ccmd-${goPlatform}-${goArch}_${goPlatform}_${goArch}`;
|
|
37
|
+
fullPath = path.join(binaryRoot, sysFolderName, binaryName);
|
|
38
|
+
try {
|
|
39
|
+
await fsPromises.access(fullPath, fsPromises.constants.R_OK);
|
|
40
|
+
} catch (ign) {
|
|
41
|
+
fullPath = null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!fullPath) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`There is no precompiled ccmd binary for ${process.platform}@${process.arch} at '${binaryRoot}'`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return fullPath;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @returns {Promise<void>}
|
|
54
|
+
*/
|
|
55
|
+
async function main() {
|
|
56
|
+
const binaryPath = await findCcmdBinary();
|
|
57
|
+
const child = childProcess.spawn(binaryPath, process.argv.slice(2), {
|
|
58
|
+
cwd: process.cwd(),
|
|
59
|
+
env: process.env,
|
|
60
|
+
stdio: [process.stdin, process.stdout, process.stderr],
|
|
61
|
+
});
|
|
62
|
+
await new Promise((resolve, reject) => {
|
|
63
|
+
child.once('error', reject);
|
|
64
|
+
child.once('exit', (code) => process.exit(code));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if (require.main === module) {
|
|
70
|
+
(async () => await main())();
|
|
71
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gifflet/ccmd",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Simple command-line tool for managing custom commands in Claude Code. Install and share commands from Git repositories with the ease of a package manager.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"postinstall": "node postinstall.js install",
|
|
8
|
+
"preuninstall": "node postinstall.js uninstall"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/gifflet/ccmd.git"
|
|
13
|
+
},
|
|
14
|
+
"author": "gifflet",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/gifflet/ccmd/issues"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/gifflet/ccmd#readme",
|
|
20
|
+
"keywords": [
|
|
21
|
+
"claude",
|
|
22
|
+
"claude-code",
|
|
23
|
+
"command-manager",
|
|
24
|
+
"cli",
|
|
25
|
+
"package-manager"
|
|
26
|
+
],
|
|
27
|
+
"bin": {
|
|
28
|
+
"ccmd": "./index.js"
|
|
29
|
+
},
|
|
30
|
+
"goBinary": {
|
|
31
|
+
"name": "ccmd",
|
|
32
|
+
"path": "./bin"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"postinstall.js",
|
|
37
|
+
"index.js"
|
|
38
|
+
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"mkdirp": "^1.0.4"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=14.0.0"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
}
|
|
48
|
+
}
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
// Thanks to author of https://github.com/sanathkr/go-npm, we were able to modify his code to work with private packages
|
|
5
|
+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
6
|
+
|
|
7
|
+
var path = require('path'),
|
|
8
|
+
mkdirp = require('mkdirp'),
|
|
9
|
+
fs = require('fs');
|
|
10
|
+
|
|
11
|
+
// Mapping from Node's `process.arch` to Golang's `$GOARCH`
|
|
12
|
+
var ARCH_MAPPING = {
|
|
13
|
+
"ia32": "386",
|
|
14
|
+
"x64": "amd64",
|
|
15
|
+
"arm": "arm",
|
|
16
|
+
"arm64": "arm64"
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Mapping between Node's `process.platform` to Golang's
|
|
20
|
+
var PLATFORM_MAPPING = {
|
|
21
|
+
"darwin": "darwin",
|
|
22
|
+
"linux": "linux",
|
|
23
|
+
"win32": "windows",
|
|
24
|
+
"freebsd": "freebsd"
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
async function getInstallationPath() {
|
|
28
|
+
|
|
29
|
+
// `npm bin` will output the path where binary files should be installed
|
|
30
|
+
|
|
31
|
+
const value = null //await execShellCommand("npm bin -g");
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
var dir = null;
|
|
35
|
+
if (!value || value.length === 0) {
|
|
36
|
+
|
|
37
|
+
// We couldn't infer path from `npm bin`. Let's try to get it from
|
|
38
|
+
// Environment variables set by NPM when it runs.
|
|
39
|
+
// npm_config_prefix points to NPM's installation directory where `bin` folder is available
|
|
40
|
+
// Ex: /Users/foo/.nvm/versions/node/v4.3.0
|
|
41
|
+
var env = process.env;
|
|
42
|
+
if (env && env.npm_config_prefix) {
|
|
43
|
+
dir = path.join(env.npm_config_prefix, "bin");
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
dir = value.trim();
|
|
47
|
+
}
|
|
48
|
+
//throw (dir)
|
|
49
|
+
///Users/danielpaulus/.nvm/versions/node/v19.7.0/lib/node_modules/go-ios/node_modules/.bin
|
|
50
|
+
await mkdirp(dir);
|
|
51
|
+
return dir;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function verifyAndPlaceBinary(binName, binPath, callback) {
|
|
55
|
+
if (!fs.existsSync(path.join(binPath, binName))) return callback('Downloaded binary does not contain the binary specified in configuration - ' + binName);
|
|
56
|
+
|
|
57
|
+
// Get installation path for executables under node
|
|
58
|
+
const installationPath = await getInstallationPath();
|
|
59
|
+
// Copy the executable to the path
|
|
60
|
+
fs.rename(path.join(binPath, binName), path.join(installationPath, binName), (err) => {
|
|
61
|
+
if (!err) {
|
|
62
|
+
console.info("Installed cli successfully");
|
|
63
|
+
callback(null);
|
|
64
|
+
} else {
|
|
65
|
+
callback(err);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function validateConfiguration(packageJson) {
|
|
71
|
+
|
|
72
|
+
if (!packageJson.version) {
|
|
73
|
+
return "'version' property must be specified";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!packageJson.goBinary || _typeof(packageJson.goBinary) !== "object") {
|
|
77
|
+
return "'goBinary' property must be defined and be an object";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!packageJson.goBinary.name) {
|
|
81
|
+
return "'name' property is necessary";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!packageJson.goBinary.path) {
|
|
85
|
+
return "'path' property is necessary";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function parsePackageJson() {
|
|
90
|
+
if (process.arch !== "arm64" && process.platform !== "darwin") {
|
|
91
|
+
if (!(process.arch in ARCH_MAPPING)) {
|
|
92
|
+
console.error("Installation is not supported for this architecture: " + process.arch);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!(process.platform in PLATFORM_MAPPING)) {
|
|
98
|
+
console.error("Installation is not supported for this platform: " + process.platform);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
var packageJsonPath = path.join(".", "package.json");
|
|
103
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
104
|
+
console.error("Unable to find package.json. " + "Please run this script at root of the package you want to be installed");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
var packageJson = JSON.parse(fs.readFileSync(packageJsonPath));
|
|
109
|
+
var error = validateConfiguration(packageJson);
|
|
110
|
+
if (error && error.length > 0) {
|
|
111
|
+
console.error("Invalid package.json: " + error);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// We have validated the config. It exists in all its glory
|
|
116
|
+
var binName = packageJson.goBinary.name;
|
|
117
|
+
var binPath = packageJson.goBinary.path;
|
|
118
|
+
var version = packageJson.version;
|
|
119
|
+
if (version[0] === 'v') version = version.substr(1); // strip the 'v' if necessary v0.0.1 => 0.0.1
|
|
120
|
+
|
|
121
|
+
// Binary name on Windows has .exe suffix
|
|
122
|
+
if (process.platform === "win32") {
|
|
123
|
+
binName += ".exe";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
binName: binName,
|
|
129
|
+
binPath: binPath,
|
|
130
|
+
version: version
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Reads the configuration from application's package.json,
|
|
136
|
+
* validates properties, copied the binary from the package and stores at
|
|
137
|
+
* ./bin in the package's root. NPM already has support to install binary files
|
|
138
|
+
* specific locations when invoked with "npm install -g"
|
|
139
|
+
*
|
|
140
|
+
* See: https://docs.npmjs.com/files/package.json#bin
|
|
141
|
+
*/
|
|
142
|
+
var INVALID_INPUT = "Invalid inputs";
|
|
143
|
+
async function install(callback) {
|
|
144
|
+
|
|
145
|
+
var opts = parsePackageJson();
|
|
146
|
+
if (!opts) return callback(INVALID_INPUT);
|
|
147
|
+
mkdirp.sync(opts.binPath);
|
|
148
|
+
console.info(`Copying the relevant binary for your platform ${process.platform}`);
|
|
149
|
+
let src = `./dist/ccmd-${PLATFORM_MAPPING[process.platform]}-${ARCH_MAPPING[process.arch]}_${PLATFORM_MAPPING[process.platform]}_${ARCH_MAPPING[process.arch]}/${opts.binName}`;
|
|
150
|
+
|
|
151
|
+
if (process.arch === "ia32" && process.platform === "win32") {
|
|
152
|
+
src = `./dist/ccmd-${PLATFORM_MAPPING[process.platform]}-amd64_${PLATFORM_MAPPING[process.platform]}_amd64/${opts.binName}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (PLATFORM_MAPPING[process.platform] === "windows") {
|
|
156
|
+
let cmd = `copy ${src} ${opts.binPath}/${opts.binName}`
|
|
157
|
+
cmd = cmd.replace(/\//g, "\\")
|
|
158
|
+
await execShellCommand(cmd);
|
|
159
|
+
} else {
|
|
160
|
+
await execShellCommand(`cp ${src} ${opts.binPath}/${opts.binName}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
await verifyAndPlaceBinary(opts.binName, opts.binPath, callback);
|
|
164
|
+
console.log("\x1b[32m", "ccmd installed, run 'ccmd --help' for details\n\n")
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function uninstall(callback) {
|
|
168
|
+
var opts = parsePackageJson();
|
|
169
|
+
try {
|
|
170
|
+
const installationPath = await getInstallationPath();
|
|
171
|
+
fs.unlink(path.join(installationPath, opts.binName), (err) => {
|
|
172
|
+
if (err) {
|
|
173
|
+
return callback(err);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
} catch (ex) {
|
|
177
|
+
// Ignore errors when deleting the file.
|
|
178
|
+
}
|
|
179
|
+
console.info("Uninstalled cli successfully");
|
|
180
|
+
return callback(null);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Parse command line arguments and call the right method
|
|
184
|
+
var actions = {
|
|
185
|
+
"install": install,
|
|
186
|
+
"uninstall": uninstall
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Executes a shell command and return it as a Promise.
|
|
190
|
+
* @param cmd {string}
|
|
191
|
+
* @return {Promise<string>}
|
|
192
|
+
*/
|
|
193
|
+
function execShellCommand(cmd) {
|
|
194
|
+
const exec = require('child_process').exec;
|
|
195
|
+
return new Promise((resolve, reject) => {
|
|
196
|
+
exec(cmd, (error, stdout, stderr) => {
|
|
197
|
+
if (error) {
|
|
198
|
+
console.warn(error);
|
|
199
|
+
}
|
|
200
|
+
resolve(stdout ? stdout : stderr);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
var argv = process.argv;
|
|
206
|
+
if (argv && argv.length > 2) {
|
|
207
|
+
var cmd = process.argv[2];
|
|
208
|
+
if (!actions[cmd]) {
|
|
209
|
+
console.log("Invalid command. `install` and `uninstall` are the only supported commands");
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
actions[cmd](function (err) {
|
|
214
|
+
if (err) {
|
|
215
|
+
console.error(err);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
} else {
|
|
218
|
+
process.exit(0);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|