ancoder-skill-cli 0.13.27 → 0.13.28

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 CHANGED
@@ -22,6 +22,115 @@ The npm package is self-contained and includes prebuilt binaries for:
22
22
 
23
23
  After install, the wrapper selects the correct bundled binary for the current platform automatically.
24
24
 
25
+ ## One-line install (Gitee or intranet)
26
+
27
+ For users who cannot access GitHub, mirror this repository to Gitee or copy these files to an intranet static file server:
28
+
29
+ - `scripts/install.ps1`
30
+ - `scripts/install.sh`
31
+ - `bin/targets/skill-cli-win32-x64.exe`
32
+ - `bin/targets/skill-cli-linux-x64`
33
+ - `bin/targets/skill-cli-linux-arm64`
34
+ - `bin/targets/skill-cli-darwin-arm64`
35
+ - `bin/targets/skill-cli-darwin-x64`
36
+
37
+ Windows users can install from Gitee with PowerShell. The most stable command pins both the script and binary to the same release tag:
38
+
39
+ ```powershell
40
+ irm https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28/scripts/install.ps1 | iex
41
+ ```
42
+
43
+ macOS/Linux users can install from Gitee with curl:
44
+
45
+ ```bash
46
+ curl -fsSL https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28/scripts/install.sh | bash
47
+ ```
48
+
49
+ The installer downloads the matching prebuilt binary, installs it as `skill-cli`, runs `skill-cli install`, then runs a lite `doctor` check. No Go toolchain is required.
50
+
51
+ Before sharing the Gitee command externally, make sure the Gitee mirror contains the `v0.13.28` tag. The installer downloads the version-pinned `v0.13.28` binary by default.
52
+
53
+ Gitee release checklist:
54
+
55
+ 1. Run `go test ./...`, `bash scripts/build-all.sh`, `node scripts/check-bin.js`, and `npm pack --dry-run`.
56
+ 2. Commit `bin/targets/`, `scripts/install.ps1`, `scripts/install.sh`, `scripts/make-install-bundle.js`, `scripts/check-install-host.js`, `scripts/check-bin.js`, `scripts/build-all.sh`, `package.json`, `package-lock.json`, `.gitattributes`, and `README.md`.
57
+ 3. Push `main` and the release tag to Gitee, or mirror them from GitHub.
58
+ 4. Verify these URLs return `200` before sending the install command:
59
+ - `https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28/scripts/install.ps1`
60
+ - `https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28/scripts/install.sh`
61
+ - `https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28/bin/targets/skill-cli-win32-x64.exe`
62
+ Or run:
63
+
64
+ ```bash
65
+ node scripts/check-install-host.js \
66
+ --script-root https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28 \
67
+ --base-url https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28/bin/targets
68
+ ```
69
+
70
+ 5. Share the tag-pinned install command only after the check above passes.
71
+
72
+ For private intranet hosting, generate a static install bundle and upload the generated `dist/install/` directory as-is:
73
+
74
+ ```bash
75
+ npm run install-bundle -- --root https://intranet.example.com/skill-cli
76
+ ```
77
+
78
+ That directory contains:
79
+
80
+ - `scripts/install.ps1`
81
+ - `scripts/install.sh`
82
+ - `bin/targets/skill-cli-*`
83
+ - `manifest.json`
84
+ - `README.txt`
85
+
86
+ After uploading `dist/install/` to `https://intranet.example.com/skill-cli/`, users can run:
87
+
88
+ ```bash
89
+ node scripts/check-install-host.js --root https://intranet.example.com/skill-cli --all-binaries
90
+ ```
91
+
92
+ ```powershell
93
+ irm https://intranet.example.com/skill-cli/scripts/install.ps1 | iex
94
+ ```
95
+
96
+ ```bash
97
+ curl -fsSL https://intranet.example.com/skill-cli/scripts/install.sh | bash
98
+ ```
99
+
100
+ If binaries are hosted somewhere else, point the installer at the directory containing the `skill-cli-*` files:
101
+
102
+ ```powershell
103
+ $env:SKILL_CLI_BASE_URL = "https://intranet.example.com/skill-cli/bin/targets"
104
+ irm https://intranet.example.com/skill-cli/scripts/install.ps1 | iex
105
+ ```
106
+
107
+ ```bash
108
+ curl -fsSL https://intranet.example.com/skill-cli/scripts/install.sh | \
109
+ bash -s -- --base-url https://intranet.example.com/skill-cli/bin/targets
110
+ ```
111
+
112
+ Useful options:
113
+
114
+ ```powershell
115
+ # Install the binary only, without writing ~/.claude components
116
+ $env:SKILL_CLI_NO_INSTALL = "1"
117
+ irm https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28/scripts/install.ps1 | iex
118
+
119
+ # For a clean smoke test against a temporary Claude directory
120
+ $env:SKILL_CLI_NO_INSTALL = $null
121
+ $env:SKILL_CLI_CLAUDE_DIR = "$PWD\.skill-cli-test-claude"
122
+ irm https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28/scripts/install.ps1 | iex
123
+ ```
124
+
125
+ ```bash
126
+ # Install the binary only, without writing ~/.claude components
127
+ curl -fsSL https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28/scripts/install.sh | bash -s -- --no-install
128
+
129
+ # For a clean smoke test against a temporary Claude directory
130
+ curl -fsSL https://gitee.com/marvin-dev/skill-cli-creator/raw/v0.13.28/scripts/install.sh | \
131
+ bash -s -- --claude-dir "$PWD/.skill-cli-test-claude"
132
+ ```
133
+
25
134
  ## Build from source (Go)
26
135
 
27
136
  ```bash
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ancoder-skill-cli",
3
- "version": "0.13.27",
3
+ "version": "0.13.28",
4
4
  "description": "CLI for managing everything-claude-code (ECC) components — agents, skills, commands, rules, hooks, MCP configs. Single binary, all assets embedded.",
5
5
  "bin": {
6
6
  "skill-cli": "bin/skill-cli.js"
@@ -8,7 +8,8 @@
8
8
  "scripts": {
9
9
  "postinstall": "node scripts/postinstall.js",
10
10
  "prepublishOnly": "node scripts/check-bin.js",
11
- "build": "bash scripts/build-all.sh"
11
+ "build": "bash scripts/build-all.sh",
12
+ "install-bundle": "node scripts/make-install-bundle.js"
12
13
  },
13
14
  "repository": {
14
15
  "type": "git",
@@ -38,6 +39,10 @@
38
39
  "files": [
39
40
  "bin/skill-cli.js",
40
41
  "bin/targets",
42
+ "scripts/install.sh",
43
+ "scripts/install.ps1",
44
+ "scripts/make-install-bundle.js",
45
+ "scripts/check-install-host.js",
41
46
  "scripts/postinstall.js",
42
47
  "scripts/check-bin.js",
43
48
  "README.md",
@@ -4,8 +4,10 @@
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
6
 
7
+ const rootDir = path.join(__dirname, '..');
7
8
  const binDir = path.join(__dirname, '..', 'bin');
8
9
  const targetsDir = path.join(binDir, 'targets');
10
+ const packageJSONPath = path.join(rootDir, 'package.json');
9
11
  const expected = [
10
12
  'skill-cli-darwin-arm64',
11
13
  'skill-cli-darwin-x64',
@@ -13,6 +15,12 @@ const expected = [
13
15
  'skill-cli-linux-x64',
14
16
  'skill-cli-win32-x64.exe',
15
17
  ];
18
+ const expectedInstallers = [
19
+ 'scripts/install.ps1',
20
+ 'scripts/install.sh',
21
+ 'scripts/make-install-bundle.js',
22
+ 'scripts/check-install-host.js',
23
+ ];
16
24
 
17
25
  const missing = expected.filter((name) => !fs.existsSync(path.join(targetsDir, name)));
18
26
 
@@ -24,3 +32,50 @@ if (missing.length > 0) {
24
32
  console.error('Run: bash scripts/build-all.sh');
25
33
  process.exit(1);
26
34
  }
35
+
36
+ const pkg = JSON.parse(fs.readFileSync(packageJSONPath, 'utf8'));
37
+ const files = new Set(pkg.files || []);
38
+ const missingInstallers = expectedInstallers.filter((name) => !fs.existsSync(path.join(rootDir, name)));
39
+ const missingInstallerFilesEntries = expectedInstallers.filter((name) => !files.has(name));
40
+
41
+ if (missingInstallers.length > 0) {
42
+ console.error('skill-cli: missing one-line installer scripts:');
43
+ for (const name of missingInstallers) {
44
+ console.error(' - ' + name);
45
+ }
46
+ process.exit(1);
47
+ }
48
+
49
+ if (missingInstallerFilesEntries.length > 0) {
50
+ console.error('skill-cli: package.json files is missing installer scripts:');
51
+ for (const name of missingInstallerFilesEntries) {
52
+ console.error(' - ' + name);
53
+ }
54
+ process.exit(1);
55
+ }
56
+
57
+ const expectedVersion = `v${pkg.version}`;
58
+ const installerVersionPatterns = [
59
+ {
60
+ file: 'scripts/install.sh',
61
+ pattern: /DEFAULT_VERSION="([^"]+)"/,
62
+ },
63
+ {
64
+ file: 'scripts/install.ps1',
65
+ pattern: /\$Version = "([^"]+)"/,
66
+ },
67
+ ];
68
+
69
+ for (const item of installerVersionPatterns) {
70
+ const installerPath = path.join(rootDir, item.file);
71
+ const content = fs.readFileSync(installerPath, 'utf8');
72
+ const match = content.match(item.pattern);
73
+ if (!match) {
74
+ console.error(`skill-cli: could not find default version in ${item.file}`);
75
+ process.exit(1);
76
+ }
77
+ if (match[1] !== expectedVersion) {
78
+ console.error(`skill-cli: ${item.file} default version is ${match[1]}, expected ${expectedVersion}`);
79
+ process.exit(1);
80
+ }
81
+ }
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const http = require('http');
5
+ const https = require('https');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const rootDir = path.join(__dirname, '..');
10
+ const pkg = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8'));
11
+ const version = `v${pkg.version}`;
12
+
13
+ const targets = [
14
+ 'skill-cli-darwin-arm64',
15
+ 'skill-cli-darwin-x64',
16
+ 'skill-cli-linux-arm64',
17
+ 'skill-cli-linux-x64',
18
+ 'skill-cli-win32-x64.exe',
19
+ ];
20
+
21
+ const args = process.argv.slice(2);
22
+ let rootURL = '';
23
+ let scriptRoot = '';
24
+ let binaryBaseURL = '';
25
+ let includeAllBinaries = false;
26
+
27
+ function usage() {
28
+ console.log(`Usage:
29
+ node scripts/check-install-host.js --root URL [--all-binaries]
30
+ node scripts/check-install-host.js --script-root URL --base-url URL [--all-binaries]
31
+
32
+ Examples:
33
+ node scripts/check-install-host.js --root https://intranet.example.com/skill-cli
34
+ node scripts/check-install-host.js \\
35
+ --script-root https://gitee.com/marvin-dev/skill-cli-creator/raw/${version} \\
36
+ --base-url https://gitee.com/marvin-dev/skill-cli-creator/raw/${version}/bin/targets
37
+
38
+ Checks installer scripts and the Windows binary by default. Pass --all-binaries
39
+ to verify every bundled platform binary.`);
40
+ }
41
+
42
+ for (let i = 0; i < args.length; i += 1) {
43
+ const arg = args[i];
44
+ if (arg === '--root') {
45
+ rootURL = args[i + 1] || '';
46
+ i += 1;
47
+ } else if (arg === '--script-root') {
48
+ scriptRoot = args[i + 1] || '';
49
+ i += 1;
50
+ } else if (arg === '--base-url') {
51
+ binaryBaseURL = args[i + 1] || '';
52
+ i += 1;
53
+ } else if (arg === '--all-binaries') {
54
+ includeAllBinaries = true;
55
+ } else if (arg === '-h' || arg === '--help') {
56
+ usage();
57
+ process.exit(0);
58
+ } else {
59
+ console.error(`unknown option: ${arg}`);
60
+ usage();
61
+ process.exit(2);
62
+ }
63
+ }
64
+
65
+ function trimSlash(value) {
66
+ return value.replace(/\/+$/, '');
67
+ }
68
+
69
+ if (rootURL) {
70
+ rootURL = trimSlash(rootURL);
71
+ scriptRoot = `${rootURL}`;
72
+ binaryBaseURL = `${rootURL}/bin/targets`;
73
+ }
74
+
75
+ if (!scriptRoot || !binaryBaseURL) {
76
+ console.error('missing --root, or both --script-root and --base-url');
77
+ usage();
78
+ process.exit(2);
79
+ }
80
+
81
+ scriptRoot = trimSlash(scriptRoot);
82
+ binaryBaseURL = trimSlash(binaryBaseURL);
83
+
84
+ const checks = [
85
+ `${scriptRoot}/scripts/install.ps1`,
86
+ `${scriptRoot}/scripts/install.sh`,
87
+ ];
88
+
89
+ const binaryNames = includeAllBinaries ? targets : ['skill-cli-win32-x64.exe'];
90
+ for (const target of binaryNames) {
91
+ checks.push(`${binaryBaseURL}/${target}`);
92
+ }
93
+
94
+ function request(url, method, redirectsRemaining = 5) {
95
+ return new Promise((resolve) => {
96
+ const lib = url.startsWith('https:') ? https : http;
97
+ const req = lib.request(url, { method, timeout: 15000 }, (res) => {
98
+ if (
99
+ res.statusCode >= 300 &&
100
+ res.statusCode < 400 &&
101
+ res.headers.location &&
102
+ redirectsRemaining > 0
103
+ ) {
104
+ res.resume();
105
+ const redirectedURL = new URL(res.headers.location, url).toString();
106
+ resolve(request(redirectedURL, method, redirectsRemaining - 1));
107
+ return;
108
+ }
109
+
110
+ res.resume();
111
+ res.on('end', () => {
112
+ resolve({
113
+ url,
114
+ statusCode: res.statusCode || 0,
115
+ contentLength: res.headers['content-length'] || '',
116
+ ok: res.statusCode >= 200 && res.statusCode < 300,
117
+ });
118
+ });
119
+ });
120
+ req.on('timeout', () => {
121
+ req.destroy(new Error('timeout'));
122
+ });
123
+ req.on('error', (err) => {
124
+ resolve({ url, statusCode: 0, contentLength: '', ok: false, error: err.message });
125
+ });
126
+ req.end();
127
+ });
128
+ }
129
+
130
+ async function checkURL(url) {
131
+ const head = await request(url, 'HEAD');
132
+ if (head.ok) {
133
+ return head;
134
+ }
135
+ const get = await request(url, 'GET');
136
+ return get.ok ? get : head;
137
+ }
138
+
139
+ (async () => {
140
+ let failed = false;
141
+ for (const url of checks) {
142
+ const result = await checkURL(url);
143
+ if (result.ok) {
144
+ const size = result.contentLength ? ` (${result.contentLength} bytes)` : '';
145
+ console.log(`OK ${url}${size}`);
146
+ } else {
147
+ failed = true;
148
+ const detail = result.error ? result.error : `HTTP ${result.statusCode}`;
149
+ console.error(`FAIL ${url} - ${detail}`);
150
+ }
151
+ }
152
+ process.exit(failed ? 1 : 0);
153
+ })();
@@ -0,0 +1,150 @@
1
+ param(
2
+ [string]$BaseUrl = $env:SKILL_CLI_BASE_URL,
3
+ [string]$Version = $env:SKILL_CLI_VERSION,
4
+ [string]$InstallDir = $env:SKILL_CLI_INSTALL_DIR,
5
+ [string]$ClaudeDir = $env:SKILL_CLI_CLAUDE_DIR,
6
+ [ValidateSet("lite", "full", "none")]
7
+ [string]$Doctor = $(if ($env:SKILL_CLI_DOCTOR) { $env:SKILL_CLI_DOCTOR } else { "lite" }),
8
+ [switch]$NoInstall,
9
+ [switch]$NoPathUpdate,
10
+ [switch]$DryRun
11
+ )
12
+
13
+ $ErrorActionPreference = "Stop"
14
+
15
+ function Test-TruthyEnv {
16
+ param([string]$Value)
17
+ if (-not $Value) {
18
+ return $false
19
+ }
20
+ return @("1", "true", "yes", "on") -contains $Value.ToLowerInvariant()
21
+ }
22
+
23
+ if (Test-TruthyEnv $env:SKILL_CLI_NO_INSTALL) {
24
+ $NoInstall = $true
25
+ }
26
+
27
+ if (Test-TruthyEnv $env:SKILL_CLI_NO_PATH_UPDATE) {
28
+ $NoPathUpdate = $true
29
+ }
30
+
31
+ if (Test-TruthyEnv $env:SKILL_CLI_DRY_RUN) {
32
+ $DryRun = $true
33
+ }
34
+
35
+ $baseUrlWasProvided = [bool]$BaseUrl
36
+
37
+ if (-not $Version) {
38
+ $Version = "v0.13.28"
39
+ }
40
+
41
+ if (-not $BaseUrl) {
42
+ $BaseUrl = "https://gitee.com/marvin-dev/skill-cli-creator/raw/$Version/bin/targets"
43
+ }
44
+
45
+ if (-not $InstallDir) {
46
+ $InstallDir = Join-Path $env:LOCALAPPDATA "Programs\skill-cli"
47
+ }
48
+
49
+ if (-not [Environment]::Is64BitOperatingSystem) {
50
+ throw "skill-cli currently provides a Windows x64 binary only."
51
+ }
52
+
53
+ $asset = "skill-cli-win32-x64.exe"
54
+ $url = ($BaseUrl.TrimEnd("/")) + "/" + $asset
55
+ $target = Join-Path $InstallDir "skill-cli.exe"
56
+
57
+ Write-Host "skill-cli installer"
58
+ Write-Host " asset: $asset"
59
+ Write-Host " source: $url"
60
+ Write-Host " target: $target"
61
+ if (-not $baseUrlWasProvided) {
62
+ Write-Host " version: $Version"
63
+ }
64
+ if ($ClaudeDir) {
65
+ Write-Host " claude dir: $ClaudeDir"
66
+ }
67
+
68
+ if ($DryRun) {
69
+ Write-Host "dry run: no files written"
70
+ return
71
+ }
72
+
73
+ $tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("skill-cli-install-" + [Guid]::NewGuid().ToString("N"))
74
+ New-Item -ItemType Directory -Path $tmp -Force | Out-Null
75
+
76
+ try {
77
+ $downloaded = Join-Path $tmp $asset
78
+ $downloadedOk = $false
79
+ for ($attempt = 1; $attempt -le 3; $attempt++) {
80
+ try {
81
+ Invoke-WebRequest -Uri $url -OutFile $downloaded -UseBasicParsing
82
+ $downloadedOk = $true
83
+ break
84
+ } catch {
85
+ if ($attempt -eq 3) {
86
+ throw
87
+ }
88
+ Start-Sleep -Seconds (2 * $attempt)
89
+ }
90
+ }
91
+ if (-not $downloadedOk) {
92
+ throw "download failed: $url"
93
+ }
94
+ New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
95
+ Copy-Item -LiteralPath $downloaded -Destination $target -Force
96
+
97
+ try {
98
+ Unblock-File -LiteralPath $target
99
+ } catch {
100
+ # Unblock-File can fail on filesystems without Zone.Identifier streams.
101
+ }
102
+
103
+ if (-not $NoPathUpdate) {
104
+ $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
105
+ $parts = @()
106
+ if ($userPath) {
107
+ $parts = $userPath -split ';' | Where-Object { $_ }
108
+ }
109
+ $alreadyInPath = $false
110
+ foreach ($part in $parts) {
111
+ if ([string]::Equals($part.TrimEnd('\'), $InstallDir.TrimEnd('\'), [StringComparison]::OrdinalIgnoreCase)) {
112
+ $alreadyInPath = $true
113
+ break
114
+ }
115
+ }
116
+ if (-not $alreadyInPath) {
117
+ $newPath = if ($userPath) { "$userPath;$InstallDir" } else { $InstallDir }
118
+ [Environment]::SetEnvironmentVariable("Path", $newPath, "User")
119
+ $env:Path = "$env:Path;$InstallDir"
120
+ Write-Host " PATH: added to current user PATH"
121
+ }
122
+ }
123
+
124
+ Write-Host ""
125
+ & $target version
126
+
127
+ $claudeArgs = @()
128
+ if ($ClaudeDir) {
129
+ $claudeArgs += @("--claude-dir", $ClaudeDir)
130
+ }
131
+
132
+ if (-not $NoInstall) {
133
+ Write-Host ""
134
+ & $target install @claudeArgs
135
+ }
136
+
137
+ if ($Doctor -eq "lite") {
138
+ Write-Host ""
139
+ & $target doctor @claudeArgs --check-adversarial=false
140
+ } elseif ($Doctor -eq "full") {
141
+ Write-Host ""
142
+ & $target doctor @claudeArgs
143
+ }
144
+
145
+ Write-Host ""
146
+ Write-Host "skill-cli install complete."
147
+ Write-Host "Open a new terminal and run: skill-cli doctor"
148
+ } finally {
149
+ Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue
150
+ }
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ DEFAULT_VERSION="v0.13.28"
5
+ VERSION="${SKILL_CLI_VERSION:-$DEFAULT_VERSION}"
6
+ DEFAULT_BASE_URL="https://gitee.com/marvin-dev/skill-cli-creator/raw/${VERSION}/bin/targets"
7
+ DEFAULT_BASE_URL_USES_VERSION=1
8
+ BASE_URL="${SKILL_CLI_BASE_URL:-$DEFAULT_BASE_URL}"
9
+ BASE_URL_EXPLICIT=0
10
+ if [ -n "${SKILL_CLI_BASE_URL:-}" ]; then
11
+ BASE_URL_EXPLICIT=1
12
+ fi
13
+ INSTALL_DIR="${SKILL_CLI_INSTALL_DIR:-$HOME/.local/bin}"
14
+ CLAUDE_DIR="${SKILL_CLI_CLAUDE_DIR:-}"
15
+ RUN_COMPONENT_INSTALL=1
16
+ DOCTOR_MODE="${SKILL_CLI_DOCTOR:-lite}"
17
+ DRY_RUN=0
18
+
19
+ usage() {
20
+ cat <<'EOF'
21
+ Install skill-cli from a prebuilt binary URL.
22
+
23
+ Usage:
24
+ curl -fsSL <install.sh-url> | bash
25
+ curl -fsSL <install.sh-url> | bash -s -- [options]
26
+
27
+ Options:
28
+ --base-url URL Base URL containing skill-cli-* binaries.
29
+ --version VERSION Gitee ref/tag used by the default Gitee base URL.
30
+ --install-dir DIR Directory to install skill-cli into.
31
+ --claude-dir DIR Pass --claude-dir to skill-cli install/doctor.
32
+ --no-install Only install the skill-cli binary.
33
+ --doctor MODE Doctor mode: lite, full, none. Default: lite.
34
+ --dry-run Print what would happen without downloading or writing.
35
+ -h, --help Show this help.
36
+
37
+ Environment:
38
+ SKILL_CLI_BASE_URL Same as --base-url.
39
+ SKILL_CLI_VERSION Same as --version. Default: v0.13.28.
40
+ SKILL_CLI_INSTALL_DIR Same as --install-dir.
41
+ SKILL_CLI_CLAUDE_DIR Same as --claude-dir.
42
+ SKILL_CLI_DOCTOR Same as --doctor.
43
+ EOF
44
+ }
45
+
46
+ while [ "$#" -gt 0 ]; do
47
+ case "$1" in
48
+ --base-url)
49
+ BASE_URL="${2:?missing value for --base-url}"
50
+ BASE_URL_EXPLICIT=1
51
+ shift 2
52
+ ;;
53
+ --version)
54
+ VERSION="${2:?missing value for --version}"
55
+ if [ "$BASE_URL_EXPLICIT" = "0" ] && [ "$DEFAULT_BASE_URL_USES_VERSION" = "1" ]; then
56
+ BASE_URL="https://gitee.com/marvin-dev/skill-cli-creator/raw/${VERSION}/bin/targets"
57
+ fi
58
+ shift 2
59
+ ;;
60
+ --install-dir)
61
+ INSTALL_DIR="${2:?missing value for --install-dir}"
62
+ shift 2
63
+ ;;
64
+ --claude-dir)
65
+ CLAUDE_DIR="${2:?missing value for --claude-dir}"
66
+ shift 2
67
+ ;;
68
+ --no-install)
69
+ RUN_COMPONENT_INSTALL=0
70
+ shift
71
+ ;;
72
+ --doctor)
73
+ DOCTOR_MODE="${2:?missing value for --doctor}"
74
+ shift 2
75
+ ;;
76
+ --dry-run)
77
+ DRY_RUN=1
78
+ shift
79
+ ;;
80
+ -h|--help)
81
+ usage
82
+ exit 0
83
+ ;;
84
+ *)
85
+ echo "skill-cli install: unknown option: $1" >&2
86
+ usage >&2
87
+ exit 2
88
+ ;;
89
+ esac
90
+ done
91
+
92
+ case "$DOCTOR_MODE" in
93
+ lite|full|none) ;;
94
+ *)
95
+ echo "skill-cli install: --doctor must be one of: lite, full, none" >&2
96
+ exit 2
97
+ ;;
98
+ esac
99
+
100
+ detect_asset() {
101
+ local os arch
102
+ os="$(uname -s | tr '[:upper:]' '[:lower:]')"
103
+ arch="$(uname -m | tr '[:upper:]' '[:lower:]')"
104
+
105
+ case "$os" in
106
+ linux*) os="linux" ;;
107
+ darwin*) os="darwin" ;;
108
+ mingw*|msys*|cygwin*) os="win32" ;;
109
+ *)
110
+ echo "unsupported OS: $os" >&2
111
+ return 1
112
+ ;;
113
+ esac
114
+
115
+ case "$arch" in
116
+ x86_64|amd64) arch="x64" ;;
117
+ aarch64|arm64) arch="arm64" ;;
118
+ *)
119
+ echo "unsupported architecture: $arch" >&2
120
+ return 1
121
+ ;;
122
+ esac
123
+
124
+ if [ "$os" = "win32" ]; then
125
+ if [ "$arch" != "x64" ]; then
126
+ echo "unsupported Windows architecture: $arch" >&2
127
+ return 1
128
+ fi
129
+ printf '%s\n' "skill-cli-win32-x64.exe"
130
+ return 0
131
+ fi
132
+
133
+ printf 'skill-cli-%s-%s\n' "$os" "$arch"
134
+ }
135
+
136
+ download_file() {
137
+ local url="$1" out="$2"
138
+ if command -v curl >/dev/null 2>&1; then
139
+ curl -fL --retry 3 --connect-timeout 15 -o "$out" "$url"
140
+ elif command -v wget >/dev/null 2>&1; then
141
+ wget -O "$out" "$url"
142
+ else
143
+ echo "skill-cli install: curl or wget is required" >&2
144
+ return 1
145
+ fi
146
+ }
147
+
148
+ ASSET="$(detect_asset)"
149
+ URL="${BASE_URL%/}/$ASSET"
150
+ TARGET_NAME="skill-cli"
151
+ case "$ASSET" in
152
+ *.exe) TARGET_NAME="skill-cli.exe" ;;
153
+ esac
154
+ TARGET_PATH="$INSTALL_DIR/$TARGET_NAME"
155
+
156
+ echo "skill-cli installer"
157
+ echo " asset: $ASSET"
158
+ echo " source: $URL"
159
+ echo " target: $TARGET_PATH"
160
+ if [ -z "${SKILL_CLI_BASE_URL:-}" ]; then
161
+ echo " version: $VERSION"
162
+ fi
163
+ if [ -n "$CLAUDE_DIR" ]; then
164
+ echo " claude dir: $CLAUDE_DIR"
165
+ fi
166
+
167
+ if [ "$DRY_RUN" = "1" ]; then
168
+ echo "dry run: no files written"
169
+ exit 0
170
+ fi
171
+
172
+ TMP_DIR="$(mktemp -d)"
173
+ trap 'rm -rf "$TMP_DIR"' EXIT
174
+
175
+ download_file "$URL" "$TMP_DIR/$ASSET"
176
+ chmod +x "$TMP_DIR/$ASSET" || true
177
+ mkdir -p "$INSTALL_DIR"
178
+ cp "$TMP_DIR/$ASSET" "$TARGET_PATH"
179
+ chmod +x "$TARGET_PATH" || true
180
+
181
+ echo ""
182
+ "$TARGET_PATH" version
183
+
184
+ claude_args=()
185
+ if [ -n "$CLAUDE_DIR" ]; then
186
+ claude_args=(--claude-dir "$CLAUDE_DIR")
187
+ fi
188
+
189
+ if [ "$RUN_COMPONENT_INSTALL" = "1" ]; then
190
+ echo ""
191
+ "$TARGET_PATH" install "${claude_args[@]}"
192
+ fi
193
+
194
+ case "$DOCTOR_MODE" in
195
+ lite)
196
+ echo ""
197
+ "$TARGET_PATH" doctor "${claude_args[@]}" --check-adversarial=false
198
+ ;;
199
+ full)
200
+ echo ""
201
+ "$TARGET_PATH" doctor "${claude_args[@]}"
202
+ ;;
203
+ none)
204
+ ;;
205
+ esac
206
+
207
+ case ":$PATH:" in
208
+ *":$INSTALL_DIR:"*) ;;
209
+ *)
210
+ echo ""
211
+ echo "Add this directory to PATH if 'skill-cli' is not found in new shells:"
212
+ echo " $INSTALL_DIR"
213
+ ;;
214
+ esac
215
+
216
+ echo ""
217
+ echo "skill-cli install complete."
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const rootDir = path.join(__dirname, '..');
8
+ const outDir = path.join(rootDir, 'dist', 'install');
9
+ const pkg = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8'));
10
+ const version = `v${pkg.version}`;
11
+ const targets = [
12
+ 'skill-cli-darwin-arm64',
13
+ 'skill-cli-darwin-x64',
14
+ 'skill-cli-linux-arm64',
15
+ 'skill-cli-linux-x64',
16
+ 'skill-cli-win32-x64.exe',
17
+ ];
18
+
19
+ const args = process.argv.slice(2);
20
+ let baseURL = '';
21
+ let rootURL = '';
22
+ for (let i = 0; i < args.length; i += 1) {
23
+ if (args[i] === '--base-url') {
24
+ baseURL = args[i + 1] || '';
25
+ i += 1;
26
+ } else if (args[i] === '--root') {
27
+ rootURL = args[i + 1] || '';
28
+ i += 1;
29
+ } else if (args[i] === '-h' || args[i] === '--help') {
30
+ console.log(`Usage:
31
+ node scripts/make-install-bundle.js [--root URL]
32
+ node scripts/make-install-bundle.js [--base-url URL]
33
+
34
+ Creates dist/install/ with one-line installer scripts and prebuilt binaries.
35
+ When --root is provided, generated installer copies default to ROOT/bin/targets.
36
+ When --base-url is provided, generated installer copies default to that URL
37
+ for binary downloads, which is useful for intranet static hosting.`);
38
+ process.exit(0);
39
+ } else {
40
+ console.error(`unknown option: ${args[i]}`);
41
+ process.exit(2);
42
+ }
43
+ }
44
+
45
+ function trimSlash(value) {
46
+ return value.replace(/\/+$/, '');
47
+ }
48
+
49
+ if (rootURL && baseURL) {
50
+ console.error('use either --root or --base-url, not both');
51
+ process.exit(2);
52
+ }
53
+
54
+ if (rootURL) {
55
+ rootURL = trimSlash(rootURL);
56
+ baseURL = `${rootURL}/bin/targets`;
57
+ }
58
+
59
+ function copyFile(src, dest) {
60
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
61
+ fs.copyFileSync(src, dest);
62
+ }
63
+
64
+ function writeFile(dest, content) {
65
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
66
+ fs.writeFileSync(dest, content);
67
+ }
68
+
69
+ fs.rmSync(outDir, { recursive: true, force: true });
70
+
71
+ for (const target of targets) {
72
+ const src = path.join(rootDir, 'bin', 'targets', target);
73
+ if (!fs.existsSync(src)) {
74
+ console.error(`missing binary: ${src}`);
75
+ process.exit(1);
76
+ }
77
+ copyFile(src, path.join(outDir, 'bin', 'targets', target));
78
+ }
79
+
80
+ let installSH = fs.readFileSync(path.join(rootDir, 'scripts', 'install.sh'), 'utf8');
81
+ let installPS1 = fs.readFileSync(path.join(rootDir, 'scripts', 'install.ps1'), 'utf8');
82
+
83
+ if (baseURL) {
84
+ const normalizedBaseURL = trimSlash(baseURL);
85
+ installSH = installSH.replace(
86
+ /DEFAULT_BASE_URL="https:\/\/gitee\.com\/marvin-dev\/skill-cli-creator\/raw\/\$\{VERSION\}\/bin\/targets"/,
87
+ `DEFAULT_BASE_URL="${normalizedBaseURL}"`
88
+ ).replace(
89
+ /DEFAULT_BASE_URL_USES_VERSION=1/,
90
+ 'DEFAULT_BASE_URL_USES_VERSION=0'
91
+ );
92
+ installPS1 = installPS1.replace(
93
+ /\$BaseUrl = "https:\/\/gitee\.com\/marvin-dev\/skill-cli-creator\/raw\/\$Version\/bin\/targets"/,
94
+ `$BaseUrl = "${normalizedBaseURL}"`
95
+ );
96
+ }
97
+
98
+ writeFile(path.join(outDir, 'scripts', 'install.sh'), installSH);
99
+ writeFile(path.join(outDir, 'scripts', 'install.ps1'), installPS1);
100
+
101
+ const manifest = {
102
+ package: pkg.name,
103
+ version: pkg.version,
104
+ tag: version,
105
+ generatedAt: new Date().toISOString(),
106
+ rootURL: rootURL || null,
107
+ baseURL: baseURL || null,
108
+ files: [
109
+ 'scripts/install.ps1',
110
+ 'scripts/install.sh',
111
+ ...targets.map((target) => `bin/targets/${target}`),
112
+ ],
113
+ };
114
+ writeFile(path.join(outDir, 'manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`);
115
+
116
+ function inferInstallRootFromBaseURL(value) {
117
+ const normalized = trimSlash(value);
118
+ if (normalized.endsWith('/bin/targets')) {
119
+ return normalized.slice(0, -'/bin/targets'.length);
120
+ }
121
+ return '<your-hosted-root>';
122
+ }
123
+
124
+ const installRoot = rootURL || (baseURL ? inferInstallRootFromBaseURL(baseURL) : `https://gitee.com/marvin-dev/skill-cli-creator/raw/${version}`);
125
+ const psURL = baseURL ? `${installRoot}/scripts/install.ps1` : `${installRoot}/scripts/install.ps1`;
126
+ const shURL = baseURL ? `${installRoot}/scripts/install.sh` : `${installRoot}/scripts/install.sh`;
127
+
128
+ writeFile(path.join(outDir, 'README.txt'), `skill-cli ${version} install bundle
129
+
130
+ Host this directory as a static tree. Required layout:
131
+ scripts/install.ps1
132
+ scripts/install.sh
133
+ bin/targets/skill-cli-*
134
+
135
+ Windows PowerShell:
136
+ irm ${psURL} | iex
137
+
138
+ macOS/Linux:
139
+ curl -fsSL ${shURL} | bash
140
+
141
+ If --base-url was not used to generate this bundle, set SKILL_CLI_BASE_URL
142
+ to the hosted bin/targets URL before running the installer.
143
+ `);
144
+
145
+ console.log(`Created ${outDir}`);
146
+ console.log(`Version: ${version}`);
147
+ if (baseURL) {
148
+ console.log(`Default binary base URL: ${baseURL}`);
149
+ }