gitea-cli-skill 0.1.6 → 0.1.8

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.
Files changed (3) hide show
  1. package/assets/SKILL.md +146 -49
  2. package/package.json +1 -1
  3. package/src/index.js +135 -68
package/assets/SKILL.md CHANGED
@@ -74,6 +74,152 @@ When inside a git repo, `--branch` and `--ref` default to the current branch. `-
74
74
 
75
75
  **Env vars:** `GITEA_HOST`, `GITEA_TOKEN`, `GITEA_OWNER`, `GITEA_REPO`, `GITEA_OUTPUT`
76
76
 
77
+ ## Common Workflows
78
+
79
+ ### Workflow 1: Issue 处理
80
+
81
+ 处理用户反馈或 bug 报告的标准流程。
82
+
83
+ ```bash
84
+ # 1. 查看当前 open 的 issues
85
+ gitea-cli issue list --state open --output json
86
+
87
+ # 2. 查看具体 issue 详情
88
+ gitea-cli issue get <number> --output json
89
+
90
+ # 3. 如果需要 label/milestone,先查找 ID
91
+ gitea-cli label list --output json
92
+ gitea-cli milestone list --output json
93
+
94
+ # 4. 更新 issue(添加 label、milestone 等)
95
+ gitea-cli issue update <number> --label <id> --milestone <id>
96
+
97
+ # 5. 处理过程中添加评论记录进展
98
+ gitea-cli issue comment create <number> -b "分析原因:..."
99
+
100
+ # 6. 如果需要上传截图或日志
101
+ gitea-cli issue attachment upload <number> ./screenshot.png
102
+
103
+ # 7. 处理完成后关闭 issue
104
+ gitea-cli issue close <number>
105
+ ```
106
+
107
+ ### Workflow 2: PR 工作流
108
+
109
+ 从创建分支到合并 PR 的完整流程。
110
+
111
+ ```bash
112
+ # 1. 创建 feature 分支
113
+ gitea-cli branch create feature/xyz --from main
114
+
115
+ # 2. (本地开发完成后)查看当前分支列表确认
116
+ gitea-cli branch list --output json
117
+
118
+ # 3. 创建 PR
119
+ gitea-cli pr create --title "feat: add new feature" --head feature/xyz --base main --body "## Summary\n- Changes made\n\n## Test plan\n- [ ] Unit tests pass"
120
+
121
+ # 4. 查看 PR 状态
122
+ gitea-cli pr get <index> --output json
123
+
124
+ # 5. 检查 CI 状态(如果有 Actions 配置)
125
+ gitea-cli actions run list --output json
126
+ gitea-cli actions run jobs <run-id> --output json
127
+
128
+ # 6. CI 通过后合并 PR
129
+ gitea-cli pr merge <index> --strategy squash --delete-branch
130
+ ```
131
+
132
+ ### Workflow 3: Release 发布
133
+
134
+ 创建版本发布的完整流程。
135
+
136
+ ```bash
137
+ # 1. 查看最近的提交,确定版本范围
138
+ gitea-cli commit list --branch main --limit 20 --output json
139
+
140
+ # 2. 查看现有 tags 和 releases
141
+ gitea-cli tag list --output json
142
+ gitea-cli release list --output json
143
+
144
+ # 3. 创建 tag
145
+ gitea-cli tag create v1.0.0 --target main -m "Release v1.0.0"
146
+
147
+ # 4. 创建 release
148
+ gitea-cli release create --tag v1.0.0 --name "v1.0.0" --body "## New Features\n- Feature A\n\n## Bug Fixes\n- Fix B" --target main
149
+
150
+ # 5. 上传构建产物
151
+ gitea-cli release asset upload v1.0.0 ./dist/binary-linux-amd64 --name binary-linux-amd64
152
+ gitea-cli release asset upload v1.0.0 ./dist/binary-darwin-arm64 --name binary-darwin-arm64
153
+
154
+ # 6. 确认 release 内容
155
+ gitea-cli release get v1.0.0 --output json
156
+ gitea-cli release asset list v1.0.0 --output json
157
+ ```
158
+
159
+ ### Workflow 4: CI/CD 监控与排障
160
+
161
+ 监控 pipeline 状态和排查失败的任务。
162
+
163
+ ```bash
164
+ # 1. 查看最近的 runs
165
+ gitea-cli actions run list --limit 10 --output json
166
+
167
+ # 2. 查看失败 run 的 jobs
168
+ gitea-cli actions run jobs <run-id> --output json
169
+
170
+ # 3. 获取失败 job 的日志
171
+ gitea-cli actions job logs <job-id> -o ./logs/
172
+
173
+ # 4. 查看 job 详情(含步骤状态)
174
+ gitea-cli actions job get <job-id> --output json
175
+
176
+ # 5. 重新触发 workflow
177
+ gitea-cli actions workflow dispatch <workflow-id> --ref main
178
+
179
+ # 6. 查看构建产物
180
+ gitea-cli actions artifact list --output json
181
+ gitea-cli actions artifact download <artifact-id> -o ./dist/
182
+ ```
183
+
184
+ ### Workflow 5: 仓库文件修改
185
+
186
+ 通过 API 读取、修改、写回仓库文件。
187
+
188
+ ```bash
189
+ # 1. 读取文件内容(获取当前版本)
190
+ gitea-cli content get src/config.yaml --output json
191
+
192
+ # 2. 在 feature 分支上创建新版本(--branch 省略时用当前分支)
193
+ gitea-cli content create src/config.yaml --content "new: value" -m "add config"
194
+
195
+ # 3. 更新已有文件(SHA 自动获取)
196
+ gitea-cli content update src/config.yaml --content "updated: value" -m "update config"
197
+
198
+ # 4. 或从本地文件上传
199
+ gitea-cli content update src/config.yaml --file ./local-config.yaml -m "update config from local"
200
+
201
+ # 5. 确认修改
202
+ gitea-cli content get src/config.yaml --output json
203
+ ```
204
+
205
+ ### Workflow 6: 配置与多环境管理
206
+
207
+ 管理多个 Gitea 实例的 context 配置。
208
+
209
+ ```bash
210
+ # 1. 查看当前配置
211
+ gitea-cli config list --output json
212
+
213
+ # 2. 添加新的 context
214
+ gitea-cli config set --name work --host https://git.company.com --token <token> --default-owner myorg
215
+
216
+ # 3. 临时使用另一个 context(不保存)
217
+ gitea-cli issue list --context personal --output json
218
+
219
+ # 4. 查看特定 context 详情
220
+ gitea-cli config get work
221
+ ```
222
+
77
223
  ## Command Quick Reference
78
224
 
79
225
  ### Config
@@ -236,55 +382,6 @@ gitea-cli release asset download <tag> <asset-id> -o ./downloads/
236
382
  gitea-cli release asset delete <tag> <asset-id> --force
237
383
  ```
238
384
 
239
- ## Common Patterns
240
-
241
- ### Read file from repo, modify, write back
242
-
243
- ```bash
244
- # 1. Get current content (captures SHA for update)
245
- gitea-cli content get src/config.yaml --output json --owner myorg --repo myrepo
246
-
247
- # 2. Update with new content (SHA auto-fetched if omitted)
248
- gitea-cli content update src/config.yaml --content "new: value" -m "update config" --owner myorg --repo myrepo
249
- ```
250
-
251
- ### Create issue with labels and milestone
252
-
253
- ```bash
254
- # 1. Find label IDs
255
- gitea-cli label list --output json --owner myorg --repo myrepo
256
-
257
- # 2. Find milestone ID
258
- gitea-cli milestone list --output json --owner myorg --repo myrepo
259
-
260
- # 3. Create issue
261
- gitea-cli issue create -t "Fix login bug" -b "Description" --label 3 --label 7 --milestone 2 --owner myorg --repo myrepo
262
- ```
263
-
264
- ### Create issue with attachments
265
-
266
- ```bash
267
- # Attach images (auto-embedded as markdown images) and files (embedded as links)
268
- gitea-cli issue create -t "Bug screenshot" -b "Steps to reproduce..." \
269
- --attach ./screenshot.png --attach ./error.log --owner myorg --repo myrepo
270
-
271
- # Supported image formats: png, jpg, jpeg, gif, webp, bmp, svg
272
- # Other file types are embedded as download links
273
- ```
274
-
275
- ### Monitor CI/CD pipeline
276
-
277
- ```bash
278
- # 1. Find latest run
279
- gitea-cli actions run list --limit 5 --output json
280
-
281
- # 2. Check job status
282
- gitea-cli actions run jobs <run-id> --output json
283
-
284
- # 3. Get logs for failed job
285
- gitea-cli actions job logs <job-id> -o ./logs/
286
- ```
287
-
288
385
  ## Error Handling
289
386
 
290
387
  | Exit Code | Meaning |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitea-cli-skill",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
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
@@ -65,6 +65,15 @@ function resolveSkillDir(platformName, targetPath) {
65
65
  return path.join(os.homedir(), PLATFORMS.claude.dir, SKILL_NAME);
66
66
  }
67
67
 
68
+ // ── Find gitea-cli binary for the given platform ────────────────────────────
69
+
70
+ function findGiteaCliBin(platformName, targetPath) {
71
+ const osArch = detectOSArch();
72
+ const binaryName = osArch.os === 'windows' ? 'gitea-cli.exe' : 'gitea-cli';
73
+ const skillDir = resolveSkillDir(platformName, targetPath);
74
+ return path.join(skillDir, 'scripts', `${osArch.os}-${osArch.arch}`, binaryName);
75
+ }
76
+
68
77
  // ── HTTP via curl (robust cross-platform) ───────────────────────────────────
69
78
 
70
79
  function curlGet(url, token) {
@@ -132,39 +141,27 @@ function extractArchive(archivePath, destDir, osArch) {
132
141
  throw new Error(`Binary ${binaryName} not found in extracted archive`);
133
142
  }
134
143
 
135
- // ── Config generation ───────────────────────────────────────────────────────
136
-
137
- function writeGiteaCliConfig(host, token) {
138
- const configDir = path.join(os.homedir(), '.config', 'gitea-cli');
139
- const configPath = path.join(configDir, 'config.yaml');
140
- const contextName = 'default';
141
-
142
- // Read existing config if present
143
- let existing = '';
144
- if (fs.existsSync(configPath)) {
145
- existing = fs.readFileSync(configPath, 'utf-8');
146
- if (existing.includes(contextName)) {
147
- console.log(` Config already exists: ${configPath} (skipped)`);
148
- return;
149
- }
150
- }
151
-
152
- const newConfig = `${existing}contexts:
153
- - name: ${contextName}
154
- host: ${host.replace(/\/$/, '')}
155
- token: ${token}
156
- current-context: ${contextName}
157
- `;
158
-
159
- fs.mkdirSync(configDir, { recursive: true });
160
- fs.writeFileSync(configPath, newConfig, 'utf-8');
161
- console.log(` Config -> ${configPath}`);
144
+ // ── Config via gitea-cli ────────────────────────────────────────────────────
145
+
146
+ function configureContext({ giteaCliBin, host, token, contextName, defaultOwner, defaultRepo }) {
147
+ const ctxName = contextName || 'default';
148
+ const cmdArgs = [
149
+ `"${giteaCliBin}"`,
150
+ 'config', 'set',
151
+ '--name', ctxName,
152
+ '--host', host.replace(/\/$/, ''),
153
+ '--token', token,
154
+ ];
155
+ if (defaultOwner) cmdArgs.push('--default-owner', defaultOwner);
156
+ if (defaultRepo) cmdArgs.push('--default-repo', defaultRepo);
157
+
158
+ execSync(cmdArgs.join(' '), { stdio: 'inherit', timeout: 10000 });
162
159
  }
163
160
 
164
161
  // ── Install command ─────────────────────────────────────────────────────────
165
162
 
166
163
  async function install(args) {
167
- const { token: explicitToken, host: explicitHost, platform: platformName, target: targetPath, local: localDir, force } = args;
164
+ const { token: explicitToken, host: explicitHost, platform: platformName, target: targetPath, local: localDir, force, name: contextName, 'default-owner': defaultOwner, 'default-repo': defaultRepo } = args;
168
165
  const giteaHost = explicitHost || GITEA_HOST;
169
166
  const token = explicitToken || process.env.GITEA_TOKEN || null;
170
167
  const osArch = detectOSArch();
@@ -188,15 +185,26 @@ async function install(args) {
188
185
  console.log(` Binary already exists: ${destBinary} (use --force to overwrite)`);
189
186
  } else if (localDir) {
190
187
  // --local: copy from local directory
191
- await installFromLocal(localDir, scriptsDir, destBinary, osArch);
188
+ installFromLocal(localDir, scriptsDir, destBinary, osArch);
192
189
  } else {
193
190
  // Download from Gitea releases
194
191
  await installFromRelease(giteaHost, token, scriptsDir, destBinary, osArch);
195
192
  }
196
193
 
197
- // 4. Write gitea-cli config if token provided
194
+ // 3. Configure context if token provided
198
195
  if (token) {
199
- writeGiteaCliConfig(giteaHost, token);
196
+ const giteaCliBin = findGiteaCliBin(platformName, targetPath);
197
+ if (fs.existsSync(giteaCliBin)) {
198
+ console.log(' Configuring context...');
199
+ try {
200
+ configureContext({ giteaCliBin, host: giteaHost, token, contextName, defaultOwner, defaultRepo });
201
+ } catch (err) {
202
+ console.error(` Failed to configure context: ${err.message}`);
203
+ console.error(' You can manually run: gitea-cli config set --name default --host <url> --token <token>');
204
+ }
205
+ } else {
206
+ console.log(' Binary not found for context setup, skipping.');
207
+ }
200
208
  }
201
209
 
202
210
  console.log(`\nDone!`);
@@ -207,7 +215,6 @@ async function install(args) {
207
215
  function installFromLocal(localDir, scriptsDir, destBinary, osArch) {
208
216
  // Goreleaser dist structure: dist/gitea-cli_<version>_<os>_<arch>/gitea-cli[.exe]
209
217
  const ext = osArch.os === 'windows' ? 'zip' : 'tar.gz';
210
- const archiveName = `gitea-cli_*_${osArch.os}_${osArch.arch}.${ext}`;
211
218
 
212
219
  const resolvedDir = localDir.replace(/^~/, os.homedir());
213
220
 
@@ -251,19 +258,19 @@ function installFromLocal(localDir, scriptsDir, destBinary, osArch) {
251
258
 
252
259
  async function installFromRelease(giteaHost, token, scriptsDir, destBinary, osArch) {
253
260
  console.log(` Fetching latest release from ${giteaHost}...`);
254
- if (!token) {
255
- console.error(' Error: Gitea API token is required.');
256
- console.error(' Provide via --token <token> or GITEA_TOKEN env var.');
257
- console.error(' Or use --local <dir> to install from a local build.');
258
- process.exit(1);
259
- }
260
261
 
261
262
  let release;
262
263
  try {
263
264
  release = getLatestRelease(giteaHost, GITEA_OWNER, GITEA_REPO, token);
264
265
  } catch (err) {
265
- console.error(` Failed to fetch release: ${err.message}`);
266
- console.error(' Use --local <dir> to install from a local build instead.');
266
+ if (!token) {
267
+ console.error(` Failed to fetch release without token: ${err.message}`);
268
+ console.error(' Repository may be private. Provide --token or GITEA_TOKEN env var.');
269
+ console.error(' Or use --local <dir> to install from a local build.');
270
+ } else {
271
+ console.error(` Failed to fetch release: ${err.message}`);
272
+ console.error(' Use --local <dir> to install from a local build instead.');
273
+ }
267
274
  process.exit(1);
268
275
  }
269
276
 
@@ -291,10 +298,36 @@ async function installFromRelease(giteaHost, token, scriptsDir, destBinary, osAr
291
298
  }
292
299
  }
293
300
 
301
+ // ── Context command ─────────────────────────────────────────────────────────
302
+
303
+ async function addContext(args) {
304
+ const { token: explicitToken, host: explicitHost, platform: platformName, target: targetPath, 'default-owner': defaultOwner, 'default-repo': defaultRepo, name: contextName } = args;
305
+ const host = explicitHost || GITEA_HOST;
306
+ const token = explicitToken || process.env.GITEA_TOKEN || null;
307
+
308
+ if (!token) {
309
+ console.error('Error: --token or GITEA_TOKEN env var is required for context setup.');
310
+ process.exit(1);
311
+ }
312
+ if (!host) {
313
+ console.error('Error: --host is required for context setup.');
314
+ process.exit(1);
315
+ }
316
+
317
+ const giteaCliBin = findGiteaCliBin(platformName, targetPath);
318
+
319
+ if (!fs.existsSync(giteaCliBin)) {
320
+ console.error('Error: gitea-cli binary not found. Run `npx gitea-cli-skill init` first.');
321
+ process.exit(1);
322
+ }
323
+
324
+ configureContext({ giteaCliBin, host, token, contextName, defaultOwner, defaultRepo });
325
+ }
326
+
294
327
  // ── CLI ──────────────────────────────────────────────────────────────────────
295
328
 
296
329
  function parseArgs(argv) {
297
- const args = { token: null, host: null, platform: null, target: null, local: null, force: false, command: null };
330
+ 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 };
298
331
 
299
332
  for (let i = 2; i < argv.length; i++) {
300
333
  const arg = argv[i];
@@ -303,9 +336,13 @@ function parseArgs(argv) {
303
336
  else if (arg === '--platform' && i + 1 < argv.length) args.platform = argv[++i];
304
337
  else if (arg === '--target' && i + 1 < argv.length) args.target = argv[++i];
305
338
  else if (arg === '--local' && i + 1 < argv.length) args.local = argv[++i];
339
+ else if (arg === '--name' && i + 1 < argv.length) args.name = argv[++i];
340
+ else if (arg === '--default-owner' && i + 1 < argv.length) args['default-owner'] = argv[++i];
341
+ else if (arg === '--default-repo' && i + 1 < argv.length) args['default-repo'] = argv[++i];
306
342
  else if (arg === '--force' || arg === '-f') args.force = true;
307
343
  else if (arg === '--help' || arg === '-h') { printHelp(); process.exit(0); }
308
- else if (!arg.startsWith('-')) args.command = arg;
344
+ else if (!arg.startsWith('-') && !args.command) args.command = arg;
345
+ else if (!arg.startsWith('-') && args.command && !args.subcommand) args.subcommand = arg;
309
346
  }
310
347
  return args;
311
348
  }
@@ -318,28 +355,56 @@ function printHelp() {
318
355
  console.log(`gitea-cli-skill - Install gitea-cli as an AI agent skill
319
356
 
320
357
  Usage:
321
- npx gitea-cli-skill init [flags]
358
+ npx gitea-cli-skill init [flags] # Install + configure
359
+ gitea-cli-skill add context [flags] # Update context only
322
360
 
323
361
  Commands:
324
- init Install skill
325
-
326
- Flags:
327
- --token <t> Gitea API token (or set GITEA_TOKEN env var)
328
- --host <url> Gitea host URL (default: ${GITEA_HOST})
329
- --local <dir> Install from local goreleaser dist directory (skip download)
330
- --platform <name> Target AI platform (default: claude)
331
- --target <path> Custom install directory (overrides --platform)
332
- --force Overwrite existing binary
333
- -h, --help Show this help
362
+ init Install skill (binary + SKILL.md) and optionally configure context
363
+ add context Add or update a Gitea API context (requires --token)
364
+
365
+ Flags (init):
366
+ --token <t> Gitea API token (needed for private repos, enables auto-config)
367
+ --host <url> Gitea host URL (default: ${GITEA_HOST})
368
+ --local <dir> Install from local goreleaser dist directory (skip download)
369
+ --platform <name> Target AI platform (default: claude)
370
+ --target <path> Custom install directory (overrides --platform)
371
+ --name <name> Context name for auto-config (default: "default")
372
+ --default-owner <owner> Default owner for auto-config (optional)
373
+ --default-repo <repo> Default repo for auto-config (optional)
374
+ --force Overwrite existing binary
375
+
376
+ Flags (add context):
377
+ --name <name> Context name (default: "default")
378
+ --host <url> Gitea host URL (required)
379
+ --token <t> Gitea API token (required, or set GITEA_TOKEN env var)
380
+ --default-owner <owner> Default repository owner (optional)
381
+ --default-repo <repo> Default repository name (optional)
382
+ --platform <name> AI platform to locate binary (default: claude)
383
+
384
+ Global:
385
+ -h, --help Show this help
334
386
 
335
387
  Platforms:
336
388
  ${platformList}
337
389
 
338
390
  Examples:
339
- npx gitea-cli-skill init --local ../dist # from local build
340
- npx gitea-cli-skill init --token abc123 # from Gitea release
341
- npx gitea-cli-skill init --token abc123 --platform cursor # for Cursor
342
- npx gitea-cli-skill init --local ../dist --target ~/custom # custom dir
391
+ # One-step install + configure (recommended)
392
+ npx gitea-cli-skill init --host https://gitea.example.com --token abc123
393
+
394
+ # With default owner
395
+ npx gitea-cli-skill init --host https://gitea.example.com --token abc123 --default-owner myorg
396
+
397
+ # Install only (public repo, no token needed)
398
+ npx gitea-cli-skill init
399
+
400
+ # Update context later
401
+ gitea-cli-skill add context --host https://gitea.example.com --token new-token
402
+
403
+ # Install from local build
404
+ npx gitea-cli-skill init --local ../dist
405
+
406
+ # Install for another AI platform
407
+ npx gitea-cli-skill init --platform cursor --host https://gitea.example.com --token abc123
343
408
  `);
344
409
  }
345
410
 
@@ -347,20 +412,22 @@ async function main() {
347
412
  const args = parseArgs(process.argv);
348
413
 
349
414
  if (!args.command) { printHelp(); process.exit(0); }
350
- if (args.command !== 'init') {
351
- console.error(`Unknown command: ${args.command}`);
352
- console.error('Run with --help for usage.');
353
- process.exit(1);
354
- }
355
415
 
356
- // Validate platform name
357
- if (args.platform && !PLATFORMS[args.platform] && !args.target) {
358
- console.error(`Unknown platform: ${args.platform}`);
359
- console.error(`Available: ${Object.keys(PLATFORMS).join(', ')}, or use --target for custom path.`);
416
+ if (args.command === 'init') {
417
+ // Validate platform name
418
+ if (args.platform && !PLATFORMS[args.platform] && !args.target) {
419
+ console.error(`Unknown platform: ${args.platform}`);
420
+ console.error(`Available: ${Object.keys(PLATFORMS).join(', ')}, or use --target for custom path.`);
421
+ process.exit(1);
422
+ }
423
+ await install(args);
424
+ } else if (args.command === 'add' && args.subcommand === 'context') {
425
+ await addContext(args);
426
+ } else {
427
+ console.error(`Unknown command: ${args.command}${args.subcommand ? ' ' + args.subcommand : ''}`);
428
+ console.error('Run with --help for usage.');
360
429
  process.exit(1);
361
430
  }
362
-
363
- await install(args);
364
431
  }
365
432
 
366
433
  main().catch((err) => {