gitea-cli-skill 0.3.0 → 0.4.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/assets/SKILL.md CHANGED
@@ -15,7 +15,8 @@ Resolve `gitea-cli` to the platform binary in skill directory `~/.claude/skills/
15
15
  # Linux / macOS
16
16
  SKILL_DIR="$HOME/.claude/skills/gitea-cli"
17
17
  OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
18
- ARCH="$(uname -m)" && [[ "$ARCH" == "x86_64" ]] && ARCH="amd64" && [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]] && ARCH="arm64"
18
+ ARCH="$(uname -m)"
19
+ case "$ARCH" in x86_64) ARCH="amd64" ;; aarch64|arm64) ARCH="arm64" ;; esac
19
20
  GITEA_CLI="$SKILL_DIR/scripts/${OS}-${ARCH}/gitea-cli"
20
21
  # Windows: %USERPROFILE%\.claude\skills\gitea-cli\scripts\windows-amd64\gitea-cli.exe
21
22
  ```
@@ -61,7 +62,7 @@ webhook list/get/create/update/delete/test — Webhooks
61
62
 
62
63
  ## Common Workflows
63
64
 
64
- When performing a task, follow the matching workflow. Read `REFERENCE.md` in the same skill directory for full command details and flags.
65
+ When performing a task, follow the matching workflow. Read `references/command-reference.md` in the same skill directory for full command details and flags.
65
66
 
66
67
  **Issue** — `issue list` → `issue get <n>` → `comment create <n>` → `issue close <n>`
67
68
  **PR** — `branch create <name> --from <base>` → `pr create` → `actions run list` → `pr merge <idx>`
@@ -83,8 +84,10 @@ Destructive ops: always `list`/`get` first, then `--force`. Deleted resources ca
83
84
 
84
85
  ## On-Demand Reference
85
86
 
86
- For full command flags and detailed workflow steps, read the reference file:
87
+ For full command flags and detailed workflow steps, read:
87
88
 
88
89
  ```
89
- ~/.claude/skills/gitea-cli/REFERENCE.md
90
+ references/command-reference.md
90
91
  ```
92
+
93
+ Located in the same skill directory as this file.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitea-cli-skill",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Install gitea-cli as a Claude Code skill",
5
5
  "bin": {
6
6
  "gitea-cli-skill": "src/index.js"
package/src/index.js CHANGED
@@ -4,7 +4,8 @@
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const os = require('os');
7
- const { execSync } = require('child_process');
7
+ const crypto = require('crypto');
8
+ const { execFileSync, execSync } = require('child_process');
8
9
 
9
10
  // ── Config ──────────────────────────────────────────────────────────────────
10
11
 
@@ -12,6 +13,7 @@ const GITEA_HOST = 'https://x.xgit.pro';
12
13
  const GITEA_OWNER = 'chenqi';
13
14
  const GITEA_REPO = 'git-cli';
14
15
  const SKILL_NAME = 'gitea-cli';
16
+ const PKG_VERSION = require(path.resolve(__dirname, '..', 'package.json')).version;
15
17
 
16
18
  // ── Platform definitions ────────────────────────────────────────────────────
17
19
 
@@ -61,7 +63,6 @@ function resolveSkillDir(platformName, targetPath) {
61
63
  if (platformName && PLATFORMS[platformName]) {
62
64
  return path.join(os.homedir(), PLATFORMS[platformName].dir, SKILL_NAME);
63
65
  }
64
- // Default: claude
65
66
  return path.join(os.homedir(), PLATFORMS.claude.dir, SKILL_NAME);
66
67
  }
67
68
 
@@ -74,38 +75,34 @@ function findGiteaCliBin(platformName, targetPath) {
74
75
  return path.join(skillDir, 'scripts', `${osArch.os}-${osArch.arch}`, binaryName);
75
76
  }
76
77
 
77
- // ── HTTP via curl (robust cross-platform) ───────────────────────────────────
78
+ // ── HTTP via curl (execFileSync — no shell injection) ────────────────────────
78
79
 
79
- function curlGet(url, token) {
80
- const args = ['-sS', '-f', '-k', '-H', 'User-Agent: gitea-cli-skill-installer'];
81
- if (token) {
82
- args.push('-H', `Authorization: token ${token}`);
83
- }
80
+ function curlGet(url, token, insecure) {
81
+ const args = ['-sS', '-f', '-H', 'User-Agent: gitea-cli-skill-installer'];
82
+ if (insecure) args.push('-k');
83
+ if (token) args.push('-H', `Authorization: token ${token}`);
84
84
  args.push(url);
85
- return execSync(`curl ${args.map((a) => `"${a}"`).join(' ')}`, { encoding: 'utf-8', timeout: 30000 });
85
+ return execFileSync('curl', args, { encoding: 'utf-8', timeout: 30000 });
86
86
  }
87
87
 
88
- function curlDownload(url, destPath, token) {
89
- const args = ['-sS', '-f', '-k', '-L', '-o', destPath, '-H', 'User-Agent: gitea-cli-skill-installer'];
90
- if (token) {
91
- args.push('-H', `Authorization: token ${token}`);
92
- }
88
+ function curlDownload(url, destPath, token, insecure) {
89
+ const args = ['-sS', '-f', '-L', '-o', destPath, '-H', 'User-Agent: gitea-cli-skill-installer'];
90
+ if (insecure) args.push('-k');
91
+ if (token) args.push('-H', `Authorization: token ${token}`);
93
92
  args.push(url);
94
- execSync(`curl ${args.map((a) => `"${a}"`).join(' ')}`, { encoding: 'utf-8', timeout: 120000 });
93
+ execFileSync('curl', args, { encoding: 'utf-8', timeout: 120000 });
95
94
  }
96
95
 
97
96
  // ── Gitea API ────────────────────────────────────────────────────────────────
98
97
 
99
- function getLatestRelease(host, owner, repo, token) {
100
- // Try /latest first
98
+ function getLatestRelease(host, owner, repo, token, insecure) {
101
99
  try {
102
- const data = curlGet(`${host}/api/v1/repos/${owner}/${repo}/releases/latest`, token);
100
+ const data = curlGet(`${host}/api/v1/repos/${owner}/${repo}/releases/latest`, token, insecure);
103
101
  const release = JSON.parse(data);
104
102
  if (release.assets && release.assets.length > 0) return release;
105
103
  } catch { /* fall through */ }
106
104
 
107
- // List all releases, find latest with assets
108
- const data = curlGet(`${host}/api/v1/repos/${owner}/${repo}/releases?limit=10`, token);
105
+ const data = curlGet(`${host}/api/v1/repos/${owner}/${repo}/releases?limit=10`, token, insecure);
109
106
  const releases = JSON.parse(data);
110
107
  for (const r of releases) {
111
108
  if (r.assets && r.assets.length > 0) return r;
@@ -119,14 +116,41 @@ function findAsset(release, osArch) {
119
116
  return release.assets.find((a) => a.name.endsWith(pattern) && !a.name.includes('checksum'));
120
117
  }
121
118
 
119
+ function findChecksumAsset(release) {
120
+ return release.assets.find((a) => a.name.includes('checksums'));
121
+ }
122
+
123
+ // ── Checksum verification ────────────────────────────────────────────────────
124
+
125
+ function verifyChecksum(archivePath, checksumContent, filename) {
126
+ for (const line of checksumContent.split('\n')) {
127
+ const trimmed = line.trim();
128
+ if (!trimmed) continue;
129
+ const parts = trimmed.split(/\s+/);
130
+ if (parts.length < 2) continue;
131
+ const [hash, name] = parts;
132
+ if (name === filename || path.basename(name) === filename) {
133
+ const actual = crypto.createHash('sha256').update(fs.readFileSync(archivePath)).digest('hex');
134
+ if (actual !== hash) {
135
+ throw new Error(`Checksum mismatch for ${filename}: expected ${hash}, got ${actual}`);
136
+ }
137
+ console.log(' Checksum verified (SHA256).');
138
+ return;
139
+ }
140
+ }
141
+ console.warn(' ⚠ Checksum entry not found for ${filename}, skipping verification.');
142
+ }
143
+
122
144
  // ── Archive extraction ──────────────────────────────────────────────────────
123
145
 
124
146
  function extractArchive(archivePath, destDir, osArch) {
125
147
  fs.mkdirSync(destDir, { recursive: true });
126
148
  if (osArch.os === 'windows') {
127
- execSync(`powershell -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${destDir}' -Force"`, { stdio: 'pipe' });
149
+ execFileSync('powershell', [
150
+ '-Command', 'Expand-Archive', '-Path', archivePath, '-DestinationPath', destDir, '-Force',
151
+ ], { stdio: 'pipe' });
128
152
  } else {
129
- execSync(`tar -xzf "${archivePath}" -C "${destDir}"`, { stdio: 'pipe' });
153
+ execFileSync('tar', ['-xzf', archivePath, '-C', destDir], { stdio: 'pipe' });
130
154
  }
131
155
 
132
156
  const binaryName = osArch.os === 'windows' ? 'gitea-cli.exe' : 'gitea-cli';
@@ -146,7 +170,6 @@ function extractArchive(archivePath, destDir, osArch) {
146
170
  function configureContext({ giteaCliBin, host, token, contextName, defaultOwner, defaultRepo }) {
147
171
  const ctxName = contextName || 'default';
148
172
  const cmdArgs = [
149
- `"${giteaCliBin}"`,
150
173
  'config', 'set',
151
174
  '--name', ctxName,
152
175
  '--host', host.replace(/\/$/, ''),
@@ -155,13 +178,33 @@ function configureContext({ giteaCliBin, host, token, contextName, defaultOwner,
155
178
  if (defaultOwner) cmdArgs.push('--default-owner', defaultOwner);
156
179
  if (defaultRepo) cmdArgs.push('--default-repo', defaultRepo);
157
180
 
158
- execSync(cmdArgs.join(' '), { stdio: 'inherit', timeout: 10000 });
181
+ execFileSync(giteaCliBin, cmdArgs, {
182
+ stdio: 'inherit',
183
+ timeout: 10000,
184
+ env: { ...process.env }, // token passed via CLI flag, not env
185
+ });
186
+ }
187
+
188
+ // ── Version consistency check ────────────────────────────────────────────────
189
+
190
+ function checkVersionConsistency(binaryPath) {
191
+ if (!fs.existsSync(binaryPath)) return;
192
+ try {
193
+ const raw = execFileSync(binaryPath, ['--version'], { encoding: 'utf-8', timeout: 5000 }).trim();
194
+ const match = raw.match(/(\d+\.\d+\.\d+)/);
195
+ if (!match) return;
196
+ const binVersion = match[1];
197
+ if (binVersion !== PKG_VERSION) {
198
+ console.warn(` ⚠ Version mismatch: npm=${PKG_VERSION}, binary=${binVersion}`);
199
+ console.warn(' Run `npx gitea-cli-skill@latest update` to sync.');
200
+ }
201
+ } catch { /* non-blocking */ }
159
202
  }
160
203
 
161
204
  // ── Install command ─────────────────────────────────────────────────────────
162
205
 
163
206
  async function install(args) {
164
- const { token: explicitToken, host: explicitHost, platform: platformName, target: targetPath, local: localDir, force, name: contextName, 'default-owner': defaultOwner, 'default-repo': defaultRepo } = args;
207
+ const { token: explicitToken, host: explicitHost, platform: platformName, target: targetPath, local: localDir, force, name: contextName, 'default-owner': defaultOwner, 'default-repo': defaultRepo, insecure } = args;
165
208
  const giteaHost = explicitHost || GITEA_HOST;
166
209
  const token = explicitToken || process.env.GITEA_TOKEN || null;
167
210
  const osArch = detectOSArch();
@@ -170,19 +213,8 @@ async function install(args) {
170
213
 
171
214
  console.log(`Installing gitea-cli skill (${osArch.os}-${osArch.arch}) to ${skillDir}...`);
172
215
 
173
- // 1. Copy SKILL.md and REFERENCE.md
174
- fs.mkdirSync(skillDir, { recursive: true });
175
- const srcSkill = path.resolve(__dirname, '..', 'assets', 'SKILL.md');
176
- const destSkill = path.join(skillDir, 'SKILL.md');
177
- fs.copyFileSync(srcSkill, destSkill);
178
- console.log(` SKILL.md -> ${destSkill}`);
179
-
180
- const srcRef = path.resolve(__dirname, '..', 'assets', 'REFERENCE.md');
181
- const destRef = path.join(skillDir, 'REFERENCE.md');
182
- if (fs.existsSync(srcRef)) {
183
- fs.copyFileSync(srcRef, destRef);
184
- console.log(` REFERENCE.md -> ${destRef}`);
185
- }
216
+ // 1. Copy SKILL.md and references/
217
+ copySkillFiles(skillDir);
186
218
 
187
219
  // 2. Check existing binary
188
220
  const binaryName = osArch.os === 'windows' ? 'gitea-cli.exe' : 'gitea-cli';
@@ -191,11 +223,9 @@ async function install(args) {
191
223
  if (fs.existsSync(destBinary) && !force) {
192
224
  console.log(` Binary already exists: ${destBinary} (use --force to overwrite)`);
193
225
  } else if (localDir) {
194
- // --local: copy from local directory
195
226
  installFromLocal(localDir, scriptsDir, destBinary, osArch);
196
227
  } else {
197
- // Download from Gitea releases
198
- await installFromRelease(giteaHost, token, scriptsDir, destBinary, osArch);
228
+ await installFromRelease(giteaHost, token, scriptsDir, destBinary, osArch, insecure);
199
229
  }
200
230
 
201
231
  // 3. Configure context if token provided
@@ -214,29 +244,54 @@ async function install(args) {
214
244
  }
215
245
  }
216
246
 
247
+ // 4. Version consistency check
248
+ checkVersionConsistency(destBinary);
249
+
217
250
  console.log(`\nDone!`);
218
251
  }
219
252
 
253
+ // ── Shared: copy SKILL.md + references + cleanup legacy ─────────────────────
254
+
255
+ function copySkillFiles(skillDir) {
256
+ fs.mkdirSync(skillDir, { recursive: true });
257
+
258
+ const srcSkill = path.resolve(__dirname, '..', 'assets', 'SKILL.md');
259
+ const destSkill = path.join(skillDir, 'SKILL.md');
260
+ fs.copyFileSync(srcSkill, destSkill);
261
+ console.log(` SKILL.md -> ${destSkill}`);
262
+
263
+ const srcRef = path.resolve(__dirname, '..', 'assets', 'references', 'command-reference.md');
264
+ const destRef = path.join(skillDir, 'references', 'command-reference.md');
265
+ if (fs.existsSync(srcRef)) {
266
+ fs.mkdirSync(path.dirname(destRef), { recursive: true });
267
+ fs.copyFileSync(srcRef, destRef);
268
+ console.log(` references/command-reference.md -> ${destRef}`);
269
+ }
270
+
271
+ // Cleanup legacy file
272
+ const legacyRef = path.join(skillDir, 'REFERENCE.md');
273
+ if (fs.existsSync(legacyRef)) {
274
+ fs.unlinkSync(legacyRef);
275
+ console.log(' Removed legacy REFERENCE.md');
276
+ }
277
+ }
278
+
220
279
  // ── Install from local goreleaser dist ───────────────────────────────────────
221
280
 
222
281
  function installFromLocal(localDir, scriptsDir, destBinary, osArch) {
223
- // Goreleaser dist structure: dist/gitea-cli_<version>_<os>_<arch>/gitea-cli[.exe]
224
282
  const ext = osArch.os === 'windows' ? 'zip' : 'tar.gz';
225
-
226
283
  const resolvedDir = localDir.replace(/^~/, os.homedir());
227
284
 
228
- // Try to find archive in dist/
229
285
  const files = fs.readdirSync(resolvedDir);
230
286
  const extRe = ext === 'tar.gz' ? 'tar\\.gz' : 'zip';
231
287
  const pattern = new RegExp('gitea-cli_\\d+\\.\\d+\\.\\d+(-\\w+)?_' + osArch.os + '_' + osArch.arch + '\\.' + extRe + '$');
232
288
  const match = files.find((f) => pattern.test(f));
233
289
  if (!match) {
234
- // Try finding extracted binary directly
235
290
  const binaryName = osArch.os === 'windows' ? 'gitea-cli.exe' : 'gitea-cli';
236
291
  const subDirs = files.filter((f) => f.includes(`${osArch.os}_${osArch.arch}`) && fs.statSync(path.join(resolvedDir, f)).isDirectory());
237
292
  for (const d of subDirs) {
238
293
  const candidate = path.join(resolvedDir, d, binaryName);
239
- if (fs.existsSync(candidate)) {
294
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
240
295
  fs.mkdirSync(scriptsDir, { recursive: true });
241
296
  fs.copyFileSync(candidate, destBinary);
242
297
  if (osArch.os !== 'windows') fs.chmodSync(destBinary, 0o755);
@@ -263,12 +318,12 @@ function installFromLocal(localDir, scriptsDir, destBinary, osArch) {
263
318
 
264
319
  // ── Install from Gitea release ───────────────────────────────────────────────
265
320
 
266
- async function installFromRelease(giteaHost, token, scriptsDir, destBinary, osArch) {
321
+ async function installFromRelease(giteaHost, token, scriptsDir, destBinary, osArch, insecure) {
267
322
  console.log(` Fetching latest release from ${giteaHost}...`);
268
323
 
269
324
  let release;
270
325
  try {
271
- release = getLatestRelease(giteaHost, GITEA_OWNER, GITEA_REPO, token);
326
+ release = getLatestRelease(giteaHost, GITEA_OWNER, GITEA_REPO, token, insecure);
272
327
  } catch (err) {
273
328
  if (!token) {
274
329
  console.error(` Failed to fetch release without token: ${err.message}`);
@@ -293,7 +348,21 @@ async function installFromRelease(giteaHost, token, scriptsDir, destBinary, osAr
293
348
  const tmpArchive = path.join(tmpDir, asset.name);
294
349
 
295
350
  try {
296
- curlDownload(asset.browser_download_url, tmpArchive, token);
351
+ curlDownload(asset.browser_download_url, tmpArchive, token, insecure);
352
+
353
+ // Verify checksum if available
354
+ const checksumAsset = findChecksumAsset(release);
355
+ if (checksumAsset) {
356
+ try {
357
+ const tmpChecksum = path.join(tmpDir, checksumAsset.name);
358
+ curlDownload(checksumAsset.browser_download_url, tmpChecksum, token, insecure);
359
+ const checksumContent = fs.readFileSync(tmpChecksum, 'utf-8');
360
+ verifyChecksum(tmpArchive, checksumContent, asset.name);
361
+ } catch (err) {
362
+ console.warn(` ⚠ Checksum verification skipped: ${err.message}`);
363
+ }
364
+ }
365
+
297
366
  console.log(' Extracting...');
298
367
  const binaryPath = extractArchive(tmpArchive, path.join(tmpDir, 'extracted'), osArch);
299
368
  fs.mkdirSync(scriptsDir, { recursive: true });
@@ -334,29 +403,17 @@ async function addContext(args) {
334
403
  // ── Update command ────────────────────────────────────────────────────────────
335
404
 
336
405
  async function update(args) {
337
- const { host: explicitHost, platform: platformName, target: targetPath, local: localDir } = args;
406
+ const { token: explicitToken, host: explicitHost, platform: platformName, target: targetPath, local: localDir, insecure } = args;
338
407
  const giteaHost = explicitHost || GITEA_HOST;
408
+ const token = explicitToken || process.env.GITEA_TOKEN || null;
339
409
  const osArch = detectOSArch();
340
410
  const skillDir = resolveSkillDir(platformName, targetPath);
341
411
  const scriptsDir = path.join(skillDir, 'scripts', `${osArch.os}-${osArch.arch}`);
342
412
 
343
413
  console.log(`Updating gitea-cli skill (${osArch.os}-${osArch.arch}) at ${skillDir}...`);
344
414
 
345
- // 1. Refresh SKILL.md and REFERENCE.md
346
- const srcSkill = path.resolve(__dirname, '..', 'assets', 'SKILL.md');
347
- const destSkill = path.join(skillDir, 'SKILL.md');
348
- if (fs.existsSync(srcSkill)) {
349
- fs.mkdirSync(skillDir, { recursive: true });
350
- fs.copyFileSync(srcSkill, destSkill);
351
- console.log(` SKILL.md -> ${destSkill}`);
352
- }
353
-
354
- const srcRef = path.resolve(__dirname, '..', 'assets', 'REFERENCE.md');
355
- const destRef = path.join(skillDir, 'REFERENCE.md');
356
- if (fs.existsSync(srcRef)) {
357
- fs.copyFileSync(srcRef, destRef);
358
- console.log(` REFERENCE.md -> ${destRef}`);
359
- }
415
+ // 1. Refresh SKILL.md and references/
416
+ copySkillFiles(skillDir);
360
417
 
361
418
  // 2. Re-download / re-copy binary (always overwrite)
362
419
  const binaryName = osArch.os === 'windows' ? 'gitea-cli.exe' : 'gitea-cli';
@@ -365,21 +422,24 @@ async function update(args) {
365
422
  if (localDir) {
366
423
  installFromLocal(localDir, scriptsDir, destBinary, osArch);
367
424
  } else {
368
- const token = process.env.GITEA_TOKEN || null;
369
- await installFromRelease(giteaHost, token, scriptsDir, destBinary, osArch);
425
+ await installFromRelease(giteaHost, token, scriptsDir, destBinary, osArch, insecure);
370
426
  }
371
427
 
428
+ // 3. Version consistency check
429
+ checkVersionConsistency(destBinary);
430
+
372
431
  console.log('\nDone! Configuration preserved.');
373
432
  }
374
433
 
375
434
  // ── CLI ──────────────────────────────────────────────────────────────────────
376
435
 
377
436
  function parseArgs(argv) {
378
- const args = { token: null, host: null, platform: null, target: null, local: null, force: false, command: null, subcommand: null, name: null, 'default-owner': null, 'default-repo': null };
437
+ const args = { token: null, host: null, platform: null, target: null, local: null, force: false, insecure: false, command: null, subcommand: null, name: null, 'default-owner': null, 'default-repo': null };
379
438
 
380
439
  for (let i = 2; i < argv.length; i++) {
381
440
  const arg = argv[i];
382
- if (arg === '--token' && i + 1 < argv.length) args.token = argv[++i];
441
+ if (arg === '--') break; // end of options
442
+ else if (arg === '--token' && i + 1 < argv.length) args.token = argv[++i];
383
443
  else if (arg === '--host' && i + 1 < argv.length) args.host = argv[++i];
384
444
  else if (arg === '--platform' && i + 1 < argv.length) args.platform = argv[++i];
385
445
  else if (arg === '--target' && i + 1 < argv.length) args.target = argv[++i];
@@ -388,6 +448,8 @@ function parseArgs(argv) {
388
448
  else if (arg === '--default-owner' && i + 1 < argv.length) args['default-owner'] = argv[++i];
389
449
  else if (arg === '--default-repo' && i + 1 < argv.length) args['default-repo'] = argv[++i];
390
450
  else if (arg === '--force' || arg === '-f') args.force = true;
451
+ else if (arg === '--insecure') args.insecure = true;
452
+ else if (arg === '--version' || arg === '-v') { console.log(`gitea-cli-skill v${PKG_VERSION}`); process.exit(0); }
391
453
  else if (arg === '--help' || arg === '-h') { printHelp(); process.exit(0); }
392
454
  else if (!arg.startsWith('-') && !args.command) args.command = arg;
393
455
  else if (!arg.startsWith('-') && args.command && !args.subcommand) args.subcommand = arg;
@@ -400,7 +462,7 @@ function printHelp() {
400
462
  .map(([k, v]) => ` ${k.padEnd(10)} ${v.desc}`)
401
463
  .join('\n');
402
464
 
403
- console.log(`gitea-cli-skill - Install gitea-cli as an AI agent skill
465
+ console.log(`gitea-cli-skill v${PKG_VERSION} - Install gitea-cli as an AI agent skill
404
466
 
405
467
  Usage:
406
468
  npx gitea-cli-skill init [flags] # Install + configure
@@ -409,7 +471,7 @@ Usage:
409
471
 
410
472
  Commands:
411
473
  init Install skill (binary + SKILL.md) and optionally configure context
412
- update Refresh binary and SKILL.md/REFERENCE.md (keeps config)
474
+ update Refresh binary and SKILL.md/references/ (keeps config)
413
475
  add context Add or update a Gitea API context (requires --token)
414
476
 
415
477
  Flags (init):
@@ -424,6 +486,7 @@ Flags (init):
424
486
  --force Overwrite existing binary
425
487
 
426
488
  Flags (update):
489
+ --token <t> Gitea API token (overrides GITEA_TOKEN env var)
427
490
  --host <url> Gitea host URL (default: ${GITEA_HOST})
428
491
  --local <dir> Update from local goreleaser dist directory (skip download)
429
492
  --platform <name> Target AI platform (default: claude)
@@ -439,6 +502,8 @@ Flags (add context):
439
502
 
440
503
  Global:
441
504
  -h, --help Show this help
505
+ -v, --version Show version
506
+ --insecure Skip SSL certificate verification (not recommended)
442
507
 
443
508
  Platforms:
444
509
  ${platformList}
@@ -476,7 +541,6 @@ async function main() {
476
541
  if (!args.command) { printHelp(); process.exit(0); }
477
542
 
478
543
  if (args.command === 'init') {
479
- // Validate platform name
480
544
  if (args.platform && !PLATFORMS[args.platform] && !args.target) {
481
545
  console.error(`Unknown platform: ${args.platform}`);
482
546
  console.error(`Available: ${Object.keys(PLATFORMS).join(', ')}, or use --target for custom path.`);