diff --git a/.gitignore b/.gitignore index 406b9fd..4b9bfcd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,15 @@ env.sh bin/opencode .DS_Store **/.DS_Store + +# 敏感配置文件 +config.env +.env +.env.* +*.pem +*.key +*.p12 +*.jks + +# Gitea 配置(如果不小心复制到这里) +gitea-config.env diff --git a/AGENTS.md b/AGENTS.md index 967b90e..ca16437 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,17 +8,25 @@ ## 语言偏好 +### 用户交互 **重要:请始终使用中文(简体中文)与用户交流。** 所有回复、解释、错误消息、建议和对话都应该使用中文,除非: -- 代码注释需要使用英文(根据项目规范) - 用户明确要求使用其他语言 - 技术术语在中文中没有合适的翻译(可以中英文混用,如 "React Hooks") +### 代码注释 +- **默认使用中文** +- 遵循项目规范(如果项目有特殊要求,以项目规范为准) + +### 独立文档文件 +- **默认使用英文**(.md、.txt、.rst 等文档文件) +- 只有用户明确要求使用中文时才使用中文 + ### 重要注意事项 1. **不要因为文档、命令或代码是英文就切换语言** - - 即使项目文档是英文的,回复时仍然用中文解释 + - 即使项目文档是英文的,对话回复时仍然用中文解释 - 即使执行的命令是英文的,回复时仍然用中文说明 - 即使错误信息是英文的,回复时仍然用中文分析 @@ -31,11 +39,202 @@ - 所有对话、提问、确认都用中文 - 所有建议、总结、分析都用中文 - 所有错误解释和问题诊断都用中文 + - 专有名词可以使用英文 这是用户的个人偏好设置,适用于所有项目和所有交互场景。 --- +## 回复展示规范 + +### 内容展示限制 +**重要:回复中不得直接展示超过 100 行的内容。** + +适用范围: +- 文档内容(.md、.txt、.rst 等) +- 代码文件(.js、.ts、.py、.go 等) +- 配置文件 +- 任何其他文本内容 + +### 长内容处理原则 + +1. **创建文件时(超过 100 行)** + - 首先考虑:是否可以拆分成多个模块? + - 优先使用模块化设计,拆分成多个小文件 + - 如果确实应该保持单一文件(如:配置文件、工具类等): + - 只告知用户文件已创建 + - 提供文件路径 + - 说明文件用途和关键特性 + - 不要在回复中粘贴完整内容 + +2. **展示修改时** + - 只展示核心、关键的改动部分 + - 使用代码片段展示(通常 10-30 行) + - 用省略号(...)表示被省略的部分 + - 说明改动的位置和目的 + +3. **提供概览时** + - 使用摘要和要点列表 + - 展示关键函数签名或接口定义 + - 提供文件路径供用户自行查看 + +### 回复简洁性要求 + +**必须隐藏的内容:** +- 完整的文件内容(超过 100 行) +- 详细的执行日志(除非有错误需要诊断) +- 重复性的代码模式 +- 自动生成的代码(如类型定义、配置模板等) + +**必须展示的内容:** +- 核心改动代码片段(< 50 行) +- 关键错误信息 +- 重要的配置修改 +- 用户需要注意的变更点 + +### 文件操作展示规范 + +**重要:CRUD 文件时,只需总结操作结果,无需展示具体变更内容。** + +适用于以下操作: +- **Create(创建)**:创建新文件 +- **Read(读取)**:读取文件内容(除非用户明确要求展示) +- **Update(更新)**:修改现有文件 +- **Delete(删除)**:删除文件 + +**展示原则:** +- ✅ 说明操作类型(创建、修改、删除) +- ✅ 提供文件路径 +- ✅ 简要说明改动的目的和影响 +- ❌ 不展示具体的代码变更内容 +- ❌ 不展示文件的完整内容 + +**示例:** + +✅ **正确做法:** +``` +已修改 src/config/settings.ts,更新了数据库连接配置。 +``` + +❌ **错误做法:** +``` +已修改 src/config/settings.ts: +[展示修改前后的代码对比] +``` + +### 避免重复命令输出 + +**重要:不要重复或总结命令输出中已有的内容。** + +当命令输出已经包含完整信息时(如:状态、操作步骤、使用说明等): +- ✅ 直接展示命令输出即可 +- ❌ 不要在命令输出后再次总结相同的内容 +- ❌ 不要重复命令中已提供的操作指令 + +**例外情况:** +- 命令出错需要分析和解释 +- 需要提取关键信息进行下一步操作 +- 用户明确要求总结 + +**示例:** + +❌ **错误做法:** +``` +[命令输出:包含完整的 runner 列表、状态、启动命令] + +✅ 已成功列出所有 Gitea Runners! + +当前状态: +- 总计:1 个 runner +- 名称:runner-mac-mini4 +- 状态:已停止 + +快速操作: +如需启动,执行:[重复命令输出中的启动命令] +``` + +✅ **正确做法:** +``` +[命令输出:包含完整的 runner 列表、状态、启动命令] + +(直接展示命令输出,无需额外总结) +``` + +### 任务完成后的回复规范 + +**重要:避免冗余的总结报告,保持简洁。** + +任务完成后,只需要提供一个简短的总结,包含: +1. ✅ 已完成的操作(使用要点列表,不超过 5 条) +2. 📝 核心修改内容(1-2 句话概括) +3. ⚠️ 需要注意的事项(如果有) + +**禁止的做法:** +- ❌ 生成详细的表格报告(如"检查报告"、"修复报告") +- ❌ 生成多个总结("总结报告" + "最终总结") +- ❌ 使用大量装饰性符号和分隔线 +- ❌ 重复描述已经执行过的步骤 +- ❌ 生成统计数据(除非用户明确要求) + +**推荐的做法:** +``` +✅ 已完成: +- 修改了 7 个文件,替换用户名为占位符 +- 更新 .gitignore,添加敏感文件保护规则 + +核心修改:用户名已脱敏,配置文件保护已加强。 +``` + +**例外情况(可以提供详细报告):** +- 用户明确要求生成报告 +- 复杂的检查任务需要呈现检查结果 +- 错误诊断需要详细分析 + +### 示例 + +❌ **错误做法:** +``` +我创建了 src/utils/helper.ts 文件,内容如下: +[粘贴 200 行完整代码] +``` + +✅ **正确做法:** +``` +已创建 src/utils/helper.ts(200 行),包含 formatDate、debounce、throttle、deepClone 等工具函数。 +``` + +❌ **错误做法(冗余总结):** +``` +[执行了一系列操作] + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +✅ 操作完成报告 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +【详细的表格和统计】 +【重复的操作描述】 +【多个章节的报告】 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +[然后又生成一个"最终总结"] + +## ✅ 最终总结 + +【再次总结相同的内容】 +``` + +✅ **正确做法(简洁总结):** +``` +✅ 已完成: +- 修改 git-add.md,添加未跟踪文件的一站式选项 +- 新增 all/u 选项可一次暂存所有文件 + +核心改进:不再需要运行两次命令。 +``` + +--- + ## 代码风格偏好 ### JavaScript/TypeScript @@ -57,10 +256,14 @@ ## 开发流程 ### Git 提交规范 +- 遵循 [Conventional Commits](https://www.conventionalcommits.org/) 规范 - **格式**:`(): ` -- **类型**:feat(功能)、fix(修复)、docs(文档)、style(格式)、refactor(重构)、test(测试)、chore(维护) +- **类型**:feat(功能)、fix(修复)、docs(文档)、style(格式)、refactor(重构)、test(测试)、chore(维护)、ci、build、perf - **主题**:使用中文,简洁明了,不超过 50 字 - **描述**:如有需要,空一行后补充详细说明 +- **主分支**:默认使用 `main`(不是 master) + +> 详细的 Git 工作流程、版本管理、Tag 规范等请参考 `skill/git/SKILL.md` ### 分支命名 - 功能分支:`feature/描述` @@ -74,9 +277,90 @@ - 修复所有 linter 和类型检查错误 ### 发布流程 -- 主分支(main/master)始终保持可部署状态 +- 主分支(main)始终保持可部署状态 - 不直接推送到主分支,必须通过 PR -- 标签格式:`v..` +- **标签格式**: + - 单仓库(polyrepo):`..`(如 `1.2.0`) + - 多仓库(monorepo):`-..`(如 `ios-1.2.0`) + +> 详细的版本管理、Tag 规范和 Git 工作流程请参考 `skill/git/SKILL.md` + +--- + +## 批量修改策略 + +### 先验证再批量修改原则 + +**重要:在进行批量修改时,必须遵循"先修改一个,验证通过后再批量修改"的原则。** + +适用范围: +- **Command(命令)**:修改或新增多个命令时 +- **Skill(技能)**:修改或新增多个技能时 +- **代码功能**:批量修改多个文件或多个函数时 +- **配置文件**:批量修改多个配置项时 + +### 执行步骤 + +1. **选择样本** + - 从批量修改的目标中选择 1 个作为样本 + - 优先选择最具代表性或最复杂的一个 + +2. **修改样本** + - 完成样本的修改 + - 确保代码质量和规范 + +3. **验证样本** + - 运行相关测试 + - 检查功能是否正常 + - 确认修改符合预期 + +4. **批量应用** + - 验证通过后,将相同的修改应用到其他目标 + - 保持一致性和规范性 + +5. **最终验证** + - 批量修改完成后,运行全局测试 + - 确保所有修改都正常工作 + +### 示例场景 + +**场景 1:批量修改多个命令** +``` +用户要求:给所有命令添加错误处理逻辑 + +正确做法: +1. 选择 1 个命令作为样本(如:最复杂的命令) +2. 添加错误处理逻辑 +3. 测试该命令是否正常工作 +4. 验证通过后,批量应用到其他命令 +5. 运行所有命令的测试 +``` + +**场景 2:批量重构代码** +``` +用户要求:重构 10 个相似的函数,改用新的设计模式 + +正确做法: +1. 选择 1 个函数进行重构 +2. 编写测试确保功能正确 +3. Code Review 确认设计合理 +4. 批量重构其他 9 个函数 +5. 运行完整的测试套件 +``` + +### 为什么要这样做? + +- **降低风险**:避免一次性批量修改导致大规模错误 +- **快速发现问题**:在样本阶段就能发现设计或实现上的问题 +- **提高质量**:样本验证通过后,批量应用的质量更有保障 +- **节省时间**:虽然看似多一步,但能避免批量回退和重做 + +### 例外情况 + +以下情况可以跳过样本验证: +- 非常简单的修改(如:批量重命名变量) +- 已有充分测试覆盖的修改 +- 用户明确要求直接批量修改 --- @@ -119,7 +403,7 @@ ### 测试要求 - **单元测试**:新功能必须包含测试 - **集成测试**:API 和跨模块交互需要测试 -- **目标覆盖率**:>= 80% +- **目标覆盖率**:>= 50% - 运行测试:提交前必须本地通过全部测试 ### 构建和类型检查 @@ -159,13 +443,6 @@ ## 常见问题和快速参考 -### 如何列出所有可用命令? -按下 `ctrl+p` 可查看 OpenCode 的所有可用操作。 - -### 如何获取帮助或反馈? -- 在 GitHub 提交 issue:https://github.com/anomalyco/opencode -- 查看官方文档:https://opencode.ai/docs - ### 何时创建新文件 vs 编辑现有文件? - **优先编辑**:修改现有代码和配置 - **仅在必要时创建**:添加全新的功能模块或配置文件 diff --git a/command/auto-commit.md b/command/auto-commit.md deleted file mode 100644 index 49ab9f8..0000000 --- a/command/auto-commit.md +++ /dev/null @@ -1,240 +0,0 @@ ---- -description: Auto-generate commit message, commit and create tag -agent: build ---- - -# auto-commit - -Auto-generate commit message for staged files, commit to local repository. - -## Features - -- **Create tag**: Automatically generate version number and create tag by default -- **Skip tag**: Skip creating tag when user inputs "skip" or "skip tag" - -> Version number update is **consistent** with git tag: AI will automatically update version number based on project type and changes, and create tag with the same version number. - -## Steps - -### 1. Check Staging Area - -Run `git diff --cached --name-only` to check if there are files in staging area. - -**If staging area is empty**: -- Output prompt: "No files in staging area, please use `git add` to add files first." -- **Terminate command execution**, do not continue - -### 2. Collect Information (Execute in parallel) - -- Run `git status` to view current status -- Run `git diff --cached` to view specific changes in staging area -- Run `git log --oneline -10` to view recent commit history, understand project's commit style -- Run `git tag --list | sort -V | tail -5` to view recent tags -- Read `AGENTS.md` file (if exists), understand project type, structure and **version update rules** - -### 3. Detect Repository Type - -**Detect repository type (polyrepo or monorepo)**: - -- Check if `AGENTS.md` file indicates **monorepo**, if `AGENTS.md` doesn't exist or doesn't clearly indicate, default to **polyrepo** - -**If monorepo, analyze change scope**: - -- Based on changed files from step 1, analyze if changes only affect a specific subproject -- If only single subproject is affected, record subproject name (e.g., extract `user-service` from path `packages/user-service/src/index.ts`) - -### 4. Detect Project Type and Determine Version Number - -**Prioritize reading version update rules from AGENTS.md**: - -If `AGENTS.md` defines version update rules (such as version file path, version field name, etc.), prioritize using that rule. - -**Auto-detect project type** (when AGENTS.md is not defined): - -AI needs to intelligently identify project type and determine version file, common project types include but not limited to: - -| Project Type | Version File | Version Field/Location | -| --- | --- | --- | -| iOS | `*.xcodeproj/project.pbxproj` | `MARKETING_VERSION` | -| npm/Node.js | `package.json` | `version` | -| Android (Groovy) | `app/build.gradle` | `versionName` | -| Android (Kotlin DSL) | `app/build.gradle.kts` | `versionName` | -| Python (pyproject) | `pyproject.toml` | `[project] version` or `[tool.poetry] version` | -| Python (setup) | `setup.py` | `version` parameter | -| Rust | `Cargo.toml` | `[package] version` | -| Go | Usually only uses git tag | - | -| Flutter | `pubspec.yaml` | `version` | -| .NET | `*.csproj` | `` or `` | - -> AI should determine project type based on files that actually exist in repository, can check multiple possible version files if necessary. - -**Read current version number**: - -1. Read current version number from identified version file -2. Parse as `major.minor.patch`: - - If parsing fails or doesn't exist: treat as `0.1.0` - - If current version only has `major.minor` format, auto-complete to `major.minor.0` - -**Calculate new version number** (based on change type): - -Version number uses **semantic versioning format**: `0.1.0`, `0.2.0`, ..., `0.9.0`, `1.0.0`, `1.1.0`, `1.1.1`... -- **Default starting version**: `0.1.0` -- **Increment rules**: - - **Major/breaking changes**: `1.2.3` -> `2.0.0` (major +1, minor and patch reset to zero) - - **New feature (feat)**: `1.2.3` -> `1.3.0` (minor +1, patch reset to zero) - - **Bug fix (fix)**: `1.2.3` -> `1.2.4` (patch +1) - -**Determine if version number update is needed**: - -> Core principle: **Version number reflects "user-perceivable changes"**. Ask yourself: Has the product that users download/use changed? - -**Need to update version number** (packaged into final product, user-perceivable): - -| commit type | version change | description | -| --- | --- | --- | -| `feat` | minor +1 | new feature | -| `fix` | patch +1 | user-perceivable bug fix | -| `perf` | patch +1 | performance optimization (user-perceivable improvement) | -| breaking change (`!`) | major +1 | API/protocol incompatible changes | - -**No need to update version number** (doesn't affect final product, user-imperceptible): - -| commit type | description | example | -| --- | --- | --- | -| `chore` | miscellaneous/toolchain | build scripts, CI config, dependency updates | -| `ci` | CI/CD config | `.github/workflows/*.yml`, `release.mjs` | -| `docs` | documentation | README, comments, API docs | -| `test` | test code | unit tests, E2E tests | -| `refactor` | refactoring | code reorganization without changing external behavior | -| `style` | code style | formatting, spaces, semicolons | -| `build` | build config | `tsconfig.json`, `biome.json`, `webpack.config.js` | - -**Judgment tips**: - -1. **Will the file be packaged into final product?** - - `scripts/`, `.github/`, `*.config.js` -> No -> Don't update version - - `src/`, `lib/`, `app/` -> Yes -> May need to update version - -2. **Does the change only affect developers?** - - Dev tool config, CI scripts, build process -> Only affects developers -> Don't update version - - Feature code, UI, API -> Affects users -> Update version - -3. **If project type cannot be identified or no version file** -> Only create git tag, don't update version file - -### 5. Generate Commit Message - -Based on changes in staging area, **analyze and generate meaningful commit message by AI**: - -- Analyze changed code content, understand the purpose and intent of this modification -- Reference project's commit history style -- Use [Conventional Commits](https://www.conventionalcommits.org/) specification -- Commit message should concisely but accurately describe "why" rather than just "what" -- Common types: `feat` (new feature), `fix` (fix), `docs` (documentation), `refactor` (refactoring), `chore` (miscellaneous), `test` (test), etc. -- **Language selection**: - - **Default (macOS/Linux)**: Use Chinese (中文) for commit messages - - **Windows**: Use English due to encoding issues with Cursor Shell tool -- **If monorepo and this commit only affects single subproject, include subproject name in commit message**: - - Format: `(): `, where `` is subproject name - - Example (Chinese): `feat(ios): 支持 OGG Opus 上传` or `fix(electron): 修复剪贴板注入失败` - - Example (English): `feat(ios): support OGG Opus upload` or `fix(electron): fix clipboard injection failure` - - If affecting multiple subprojects or entire repository, no need to add scope - -### 6. Update Project Version Number - -> Only execute when step 4 determines version number update is needed and version file is identified - -**Execution steps**: - -1. Based on version file and field identified in step 4, update version number to calculated new version -2. Add version file to staging area: `git add ` -3. **Verify staging area**: Run `git diff --cached --name-only` to confirm version file is in staging area - -### 7. Execute Commit - -Execute commit with generated commit message (staging area now includes user's changes and version number update). - -**Commit message language by platform:** - -**macOS/Linux** (use Chinese commit messages): - -```bash -# Single line commit -git commit -m "feat(android): 添加新功能" - -# Multi-line commit (recommended) -git commit -m "$(cat <<'EOF' -feat(android): 添加新功能 - -- 详细说明 1 -- 详细说明 2 -EOF -)" -``` - -**Windows** (use English commit messages): - -Due to Cursor's Shell tool encoding issues on Windows, **must use English** commit messages. - -```powershell -# Single line commit -git commit -m "feat(android): add new feature" - -# Multi-line commit (use --message multiple times) -git commit --message="fix(android): fix code review issues" --message="- Fix syntax error" --message="- Use JSONObject instead of manual JSON" -``` - -**Windows prohibited methods** (will cause garbled text): -- No Chinese commit messages - Cursor Shell tool encoding error when passing Chinese parameters -- No `Out-File -Encoding utf8` - writes UTF-8 with BOM -- No Write tool to write temp files - encoding uncontrollable -- No PowerShell here-string `@"..."@` - newline parsing issues - -### 8. Create Tag - -> **Executed by default**. Only skip this step when user explicitly inputs "skip" or "skip tag". -> Only create tag when step 4 determines version number update is needed (docs/chore types don't create tag). - -**Tag annotation content requirements**: - -- **Use the same content as commit message for tag annotation** -- Tag annotation will be used as Release notes by CI/CD scripts, should include detailed change content -- If commit message has multiple lines, tag annotation should also be multi-line - -**polyrepo**: -```bash -git tag -a "" -m "" -m "" -m "" -m "" ... -``` - -**monorepo**: -```bash -git tag -a "-" -m "" -m "" -m "" -m "" ... -``` - -Examples (single line commit): -- polyrepo: `git tag -a "1.2.0" -m "feat: add user authentication"` -- monorepo: `git tag -a "android-1.2.0" -m "feat(android): add user authentication"` - -Examples (multi-line commit, more recommended): -- polyrepo: - ```bash - git tag -a "1.2.1" -m "fix: resolve bluetooth connection timeout" -m "" -m "- Increase connection timeout to 30s" -m "- Add retry mechanism for failed connections" -m "- Improve error messages" - ``` -- monorepo: - ```bash - git tag -a "android-1.2.1" -m "fix(android): resolve bluetooth connection timeout" -m "" -m "- Increase connection timeout to 30s" -m "- Add retry mechanism for failed connections" -m "- Improve error messages" - ``` - -## Notes - -- **Staging area check first**: Must check if staging area has files first, if not, terminate immediately, don't execute any subsequent operations. -- **AGENTS.md priority**: If `AGENTS.md` defines version update rules, prioritize using that rule; otherwise AI auto-detects project type. -- **Smart detection**: AI should intelligently determine project type and version file location based on files that actually exist in repository. -- **Version number and tag consistency**: Project version number and git tag use the same version number, ensure consistency. -- **Version number format**: Use `major.minor.patch` semantic versioning format (e.g., `0.1.0`, `1.1.0`, `1.1.1`), default starting `0.1.0`. -- **Create tag by default**: Unless user inputs "skip" or "skip tag", create tag by default. -- **Update version before commit**: First determine version number and modify version file, then commit at once, avoid using `--amend`. -- **Version file must be verified**: After updating version number and `git add`, must run `git diff --cached --name-only` to confirm version file is in staging area before executing commit. -- **Commit message language**: Default use Chinese (macOS/Linux); Windows must use English due to Cursor Shell tool encoding issues. -- **monorepo scope**: If staging area only affects single subproject, commit message should have scope; if affecting multiple subprojects, no scope needed. -- **monorepo tag format**: Use `-` format (e.g., `ios-0.1.0`, `android-1.1.0`). -- **No version file case**: If project type cannot be identified or no version file (like pure Go project), only create git tag, don't update any files. diff --git a/command/commit-push.md b/command/commit-push.md deleted file mode 100644 index a4791e5..0000000 --- a/command/commit-push.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -description: Auto-generate commit, create tag, and push to remote -agent: build ---- - -# commit-push - -Auto-generate commit message for staged files, commit to local repository, then push to remote repository origin. - -## Features - -- **Create tag**: Automatically generate version number and create tag by default, push to remote -- **Skip tag**: Skip creating tag when user inputs "skip" or "skip tag" - -> Version number update is **consistent** with git tag: AI will automatically update version number based on project type and changes, and create tag with the same version number. - -## Steps - -### 1. Check Staging Area - -Run `git diff --cached --name-only` to check if there are files in staging area. - -**If staging area is empty**: -- Output prompt: "No files in staging area, please use `git add` to add files first." -- **Terminate command execution**, do not continue - -### 2. Collect Information (Execute in parallel) - -- Run `git status` to view current status -- Run `git diff --cached` to view specific changes in staging area -- Run `git log --oneline -10` to view recent commit history, understand project's commit style -- Run `git tag --list | sort -V | tail -5` to view recent tags -- Read `AGENTS.md` file (if exists), understand project type, structure and **version update rules** - -### 3. Detect Repository Type - -**Detect repository type (polyrepo or monorepo)**: - -- Check if `AGENTS.md` file indicates **monorepo**, if `AGENTS.md` doesn't exist or doesn't clearly indicate, default to **polyrepo** - -**If monorepo, analyze change scope**: - -- Based on changed files from step 1, analyze if changes only affect a specific subproject -- If only single subproject is affected, record subproject name (e.g., extract `user-service` from path `packages/user-service/src/index.ts`) - -### 4. Detect Project Type and Determine Version Number - -**Prioritize reading version update rules from AGENTS.md**: - -If `AGENTS.md` defines version update rules (such as version file path, version field name, etc.), prioritize using that rule. - -**Auto-detect project type** (when AGENTS.md is not defined): - -AI needs to intelligently identify project type and determine version file, common project types include but not limited to: - -| Project Type | Version File | Version Field/Location | -| --- | --- | --- | -| iOS | `*.xcodeproj/project.pbxproj` | `MARKETING_VERSION` | -| npm/Node.js | `package.json` | `version` | -| Android (Groovy) | `app/build.gradle` | `versionName` | -| Android (Kotlin DSL) | `app/build.gradle.kts` | `versionName` | -| Python (pyproject) | `pyproject.toml` | `[project] version` or `[tool.poetry] version` | -| Python (setup) | `setup.py` | `version` parameter | -| Rust | `Cargo.toml` | `[package] version` | -| Go | Usually only uses git tag | - | -| Flutter | `pubspec.yaml` | `version` | -| .NET | `*.csproj` | `` or `` | - -> AI should determine project type based on files that actually exist in repository, can check multiple possible version files if necessary. - -**Read current version number**: - -1. Read current version number from identified version file -2. Parse as `major.minor.patch`: - - If parsing fails or doesn't exist: treat as `0.1.0` - - If current version only has `major.minor` format, auto-complete to `major.minor.0` - -**Calculate new version number** (based on change type): - -Version number uses **semantic versioning format**: `0.1.0`, `0.2.0`, ..., `0.9.0`, `1.0.0`, `1.1.0`, `1.1.1`... -- **Default starting version**: `0.1.0` -- **Increment rules**: - - **Major/breaking changes**: `1.2.3` -> `2.0.0` (major +1, minor and patch reset to zero) - - **New feature (feat)**: `1.2.3` -> `1.3.0` (minor +1, patch reset to zero) - - **Bug fix (fix)**: `1.2.3` -> `1.2.4` (patch +1) - -**Determine if version number update is needed**: - -> Core principle: **Version number reflects "user-perceivable changes"**. Ask yourself: Has the product that users download/use changed? - -**Need to update version number** (packaged into final product, user-perceivable): - -| commit type | version change | description | -| --- | --- | --- | -| `feat` | minor +1 | new feature | -| `fix` | patch +1 | user-perceivable bug fix | -| `perf` | patch +1 | performance optimization (user-perceivable improvement) | -| breaking change (`!`) | major +1 | API/protocol incompatible changes | - -**No need to update version number** (doesn't affect final product, user-imperceptible): - -| commit type | description | example | -| --- | --- | --- | -| `chore` | miscellaneous/toolchain | build scripts, CI config, dependency updates | -| `ci` | CI/CD config | `.github/workflows/*.yml`, `release.mjs` | -| `docs` | documentation | README, comments, API docs | -| `test` | test code | unit tests, E2E tests | -| `refactor` | refactoring | code reorganization without changing external behavior | -| `style` | code style | formatting, spaces, semicolons | -| `build` | build config | `tsconfig.json`, `biome.json`, `webpack.config.js` | - -**Judgment tips**: - -1. **Will the file be packaged into final product?** - - `scripts/`, `.github/`, `*.config.js` -> No -> Don't update version - - `src/`, `lib/`, `app/` -> Yes -> May need to update version - -2. **Does the change only affect developers?** - - Dev tool config, CI scripts, build process -> Only affects developers -> Don't update version - - Feature code, UI, API -> Affects users -> Update version - -3. **If project type cannot be identified or no version file** -> Only create git tag, don't update version file - -### 5. Generate Commit Message - -Based on changes in staging area, **analyze and generate meaningful commit message by AI**: - -- Analyze changed code content, understand the purpose and intent of this modification -- Reference project's commit history style -- Use [Conventional Commits](https://www.conventionalcommits.org/) specification -- Commit message should concisely but accurately describe "why" rather than just "what" -- Common types: `feat` (new feature), `fix` (fix), `docs` (documentation), `refactor` (refactoring), `chore` (miscellaneous), `test` (test), etc. -- **Language selection**: - - **Default (macOS/Linux)**: Use Chinese (中文) for commit messages - - **Windows**: Use English due to encoding issues with Cursor Shell tool -- **If monorepo and this commit only affects single subproject, include subproject name in commit message**: - - Format: `(): `, where `` is subproject name - - Example (Chinese): `feat(ios): 支持 OGG Opus 上传` or `fix(electron): 修复剪贴板注入失败` - - Example (English): `feat(ios): support OGG Opus upload` or `fix(electron): fix clipboard injection failure` - - If affecting multiple subprojects or entire repository, no need to add scope - -### 6. Update Project Version Number - -> Only execute when step 4 determines version number update is needed and version file is identified - -**Execution steps**: - -1. Based on version file and field identified in step 4, update version number to calculated new version -2. Add version file to staging area: `git add ` -3. **Verify staging area**: Run `git diff --cached --name-only` to confirm version file is in staging area - -### 7. Execute Commit - -Execute commit with generated commit message (staging area now includes user's changes and version number update). - -**Commit message language by platform:** - -**macOS/Linux** (use Chinese commit messages): - -```bash -# Single line commit -git commit -m "feat(android): 添加新功能" - -# Multi-line commit (recommended) -git commit -m "$(cat <<'EOF' -feat(android): 添加新功能 - -- 详细说明 1 -- 详细说明 2 -EOF -)" -``` - -**Windows** (use English commit messages): - -Due to Cursor's Shell tool encoding issues on Windows, **must use English** commit messages. - -```powershell -# Single line commit -git commit -m "feat(android): add new feature" - -# Multi-line commit (use --message multiple times) -git commit --message="fix(android): fix code review issues" --message="- Fix syntax error" --message="- Use JSONObject instead of manual JSON" -``` - -**Windows prohibited methods** (will cause garbled text): -- No Chinese commit messages - Cursor Shell tool encoding error when passing Chinese parameters -- No `Out-File -Encoding utf8` - writes UTF-8 with BOM -- No Write tool to write temp files - encoding uncontrollable -- No PowerShell here-string `@"..."@` - newline parsing issues - -### 8. Create Tag - -> **Executed by default**. Only skip this step when user explicitly inputs "skip" or "skip tag". -> Only create tag when step 4 determines version number update is needed (docs/chore types don't create tag). - -**Tag annotation content requirements**: - -- **Use the same content as commit message for tag annotation** -- Tag annotation will be used as Release notes by CI/CD scripts, should include detailed change content -- If commit message has multiple lines, tag annotation should also be multi-line - -**polyrepo**: -```bash -git tag -a "" -m "" -m "" -m "" -m "" ... -``` - -**monorepo**: -```bash -git tag -a "-" -m "" -m "" -m "" -m "" ... -``` - -Examples (single line commit): -- polyrepo: `git tag -a "1.2.0" -m "feat: add user authentication"` -- monorepo: `git tag -a "android-1.2.0" -m "feat(android): add user authentication"` - -Examples (multi-line commit, more recommended): -- polyrepo: - ```bash - git tag -a "1.2.1" -m "fix: resolve bluetooth connection timeout" -m "" -m "- Increase connection timeout to 30s" -m "- Add retry mechanism for failed connections" -m "- Improve error messages" - ``` -- monorepo: - ```bash - git tag -a "android-1.2.1" -m "fix(android): resolve bluetooth connection timeout" -m "" -m "- Increase connection timeout to 30s" -m "- Add retry mechanism for failed connections" -m "- Improve error messages" - ``` - -### 9. Push to Remote Repository - -```bash -# Push current branch -git push origin $(git branch --show-current) -``` - -**If tag was created, also push tag**: - -**polyrepo**: -```bash -git push origin -``` - -**monorepo**: -```bash -git push origin - -``` - -## Notes - -- **Staging area check first**: Must check if staging area has files first, if not, terminate immediately, don't execute any subsequent operations. -- **AGENTS.md priority**: If `AGENTS.md` defines version update rules, prioritize using that rule; otherwise AI auto-detects project type. -- **Smart detection**: AI should intelligently determine project type and version file location based on files that actually exist in repository. -- **Version number and tag consistency**: Project version number and git tag use the same version number, ensure consistency. -- **Version number format**: Use `major.minor.patch` semantic versioning format (e.g., `0.1.0`, `1.1.0`, `1.1.1`), default starting `0.1.0`. -- **Create tag by default**: Unless user inputs "skip" or "skip tag", create and push tag by default. -- **Update version before commit**: First determine version number and modify version file, then commit at once, avoid using `--amend`. -- **Version file must be verified**: After updating version number and `git add`, must run `git diff --cached --name-only` to confirm version file is in staging area before executing commit. -- **Commit message language**: Default use Chinese (macOS/Linux); Windows must use English due to Cursor Shell tool encoding issues. -- **monorepo scope**: If staging area only affects single subproject, commit message should have scope; if affecting multiple subprojects, no scope needed. -- **monorepo tag format**: Use `-` format (e.g., `ios-0.1.0`, `android-1.1.0`). -- **No version file case**: If project type cannot be identified or no version file (like pure Go project), only create git tag, don't update any files. diff --git a/command/create-gitea-repo.md b/command/create-gitea-repo.md deleted file mode 100644 index 5b19927..0000000 --- a/command/create-gitea-repo.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -description: Create a new Git repository on Gitea -agent: build ---- - -# create-gitea-repo - -Create a new Git repository on Gitea. - -## Features - -- Create new repository under specified organization or user via Gitea API -- Support creating private or public repositories -- Automatically add remote repository address after successful creation - -## User Input Format - -User can specify parameters in the following format: - -``` -/ [private|public] -``` - -- `owner`: Organization name or username (required) -- `repo`: Repository name (required) -- `private|public`: Visibility (optional, default `private`) - -**Examples**: -- `ai/my-project` - Create private repository my-project under ai organization -- `ai/my-project public` - Create public repository my-project under ai organization -- `voson/test private` - Create private repository test under voson user - -## Configuration - -Use the following configuration when executing command: - -| Config Item | Value | -| --- | --- | -| Gitea Server URL | `https://git.digitevents.com/` | -| API Token | `{env:GITEA_API_TOKEN}` | - -## Steps - -### 1. Parse User Input - -Parse from user input: -- `owner`: Organization name or username -- `repo`: Repository name -- `visibility`: `private` (default) or `public` - -**Input validation**: -- If user didn't provide `owner/repo` format input, prompt user for correct format and terminate execution -- Repository name can only contain letters, numbers, underscores, hyphens and dots - -### 2. Call Gitea API to Create Repository - -Use curl to call Gitea API: - -```bash -curl -s -X POST "https://git.digitevents.com/api/v1/orgs//repos" \ - -H "Authorization: token $GITEA_API_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "", - "private": - }' -``` - -> Note: If owner is personal user instead of organization, API path should be `/api/v1/user/repos`, but usually try organization API first. - -### 3. Handle Response - -**Success** (HTTP 201): -- Extract repository info from response: - - `html_url`: Repository web URL - - `clone_url`: HTTPS clone URL - - `ssh_url`: SSH clone URL -- Output creation success message - -**Failure**: -- If 404 error, owner may not exist or no permission -- If 409 error, repository already exists -- Output error message and terminate - -### 4. Ask Whether to Add Remote Repository - -Ask user whether to add the newly created repository as current project's remote repository. - -**If user confirms**: - -1. Check if current directory is a Git repository: - ```bash - git rev-parse --is-inside-work-tree - ``` - -2. If not a Git repository, ask whether to initialize: - ```bash - git init - ``` - -3. Check if origin remote already exists: - ```bash - git remote get-url origin - ``` - -4. Add or update remote repository: - - If no origin: `git remote add origin ` - - If origin exists: Ask whether to overwrite, after confirmation execute `git remote set-url origin ` - -### 5. Output Result Summary - -Output creation result summary table: - -``` -Repository created successfully! - -| Property | Value | -|----------|-------| -| Repository Name | owner/repo | -| Web URL | https://git.digitevents.com/owner/repo | -| Clone URL (HTTPS) | https://git.digitevents.com/owner/repo.git | -| Clone URL (SSH) | git@git.digitevents.com:owner/repo.git | -| Private | Yes/No | -``` - -## Notes - -- **Permission check**: Ensure Token has permission to create repository -- **Organization vs User**: Creating organization repository and user repository use different API endpoints -- **Repository naming**: Follow Gitea naming rules, avoid special characters diff --git a/command/git-add.md b/command/git-add.md new file mode 100644 index 0000000..24da577 --- /dev/null +++ b/command/git-add.md @@ -0,0 +1,321 @@ +--- +description: Stage changes with automatic filtering of sensitive files +--- + +# git-add + +Intelligently stage changes while automatically filtering security-sensitive files. + +## What It Does + +- Detects all unstaged changes +- Automatically excludes common sensitive files (credentials, secrets, etc.) +- Previews files that will be staged +- Asks for confirmation before staging +- Shows excluded sensitive files for awareness + +## Quick Start + +Use this command when you want to safely stage changes without accidentally committing sensitive information. + +## Sensitive Files Filtered + +**Always excluded:** +- `.env*` - Environment files +- `*.key`, `*.pem`, `*.p8` - Private keys +- `.aws/*`, `.gcloud/*` - Cloud credentials +- `.ssh/*` - SSH keys +- `credentials.json`, `secrets.json` - Credential files +- `package-lock.json`, `yarn.lock` (optional, can override) +- `node_modules/`, `vendor/`, `.venv/` - Dependencies +- `dist/`, `build/`, `.next/` - Build artifacts +- `.DS_Store`, `Thumbs.db` - System files + +**Custom exclusions:** +- Can be specified in project `.gitignore` or `.secretsignore` + +## Steps + +### 1. Detect All Unstaged Changes + +```bash +git status --porcelain +git diff --name-only +``` + +Categories: +- Modified files (M) +- Deleted files (D) +- Untracked files (??) - **重要:第一次就显示并提供选项** + +### 2. Filter Sensitive Files + +Build exclusion list: +- Read `.gitignore` for patterns +- Read `.secretsignore` if exists (project-specific rules) +- Apply built-in security patterns +- Check for `.env*`, `*.key`, `credentials.json`, etc. + +Separate files into: +- **Modified/Deleted files (tracked)**: 已跟踪的修改和删除 +- **Untracked files (safe)**: 未跟踪但安全的新文件 +- **Sensitive files**: 敏感文件(需要 force 才能暂存) + +**关键逻辑:** +- 默认选项 (y): 只暂存已跟踪的修改/删除 +- all/u 选项: 暂存所有安全文件(包括未跟踪) +- force 选项: 暂存所有文件(包括敏感) +- 一次性显示所有选项,不需要二次运行命令 + +### 3. Display Preview + +Show clear summary in format: + +``` +=== 将要暂存的文件 (3) === +M src/index.js +M src/utils.js +D old-file.js + +=== 未跟踪的文件 (2) === +?? new-feature.js +?? tests/new-test.js + +=== 敏感文件已过滤 (2) === +⚠ .env.local (Environment file) +⚠ config/secrets.json (Credential file) + +=== 操作选项 === +- 输入 "y" 或 "yes" 暂存已修改/已删除的文件(不包括未跟踪文件) +- 输入 "all" 暂存所有文件(包括未跟踪文件,但排除敏感文件) +- 输入 "with-untracked" 或 "u" 暂存所有文件(包括未跟踪文件) +- 输入 "force" 强制暂存所有文件(包括敏感文件,谨慎使用) +- 输入 "no" 或 "cancel" 取消操作 +- 输入文件路径 暂存特定文件 +``` + +### 4. User Confirmation + +Prompt user: +``` +是否确认暂存上述文件? (y/all/u/no) +``` + +**Options:** +- `y` or `yes`: Stage modified/deleted files only (exclude untracked) +- `all` or `with-untracked` or `u`: Stage all including untracked files (exclude sensitive) +- `force`: Stage everything including sensitive files (show warning) +- `no` or `cancel`: Abort +- File path: Stage only specific file(s) + +**智能提示:** +- 如果没有未跟踪文件,只显示 y/no 选项 +- 如果有未跟踪文件,显示 y/all/u/no 选项,并高亮推荐使用 "all" 或 "u" +- 如果有敏感文件,额外显示 force 选项并警告风险 + +### 5. Execute Staging + +If user confirms: + +```bash +# Option 1: Stage modified/deleted only (y) +git add ... + +# Option 2: Stage all including untracked (all/u) +git add + +# Option 3: Stage everything including sensitive (force) +git add . +``` + +**Warning for "force" option:** +``` +⚠️ 警告:你选择暂存所有文件,包括敏感文件! +请确认这是有意的,避免意外提交凭证或密钥。 + +已暂存的敏感文件: +- .env.local +- config/secrets.json + +输入 "confirm" 继续,或 "cancel" 取消操作 +``` + +**Success message for "all/u" option:** +``` +✓ 已暂存所有文件(包括未跟踪文件) + +已暂存 5 个文件: + M src/index.js + M src/utils.js + D old-file.js + A new-feature.js + A tests/new-test.js + +敏感文件已自动过滤并排除。 +``` + +### 6. Display Result + +Show success message in Chinese: + +``` +✓ 暂存成功 + +已暂存 3 个文件: + M src/index.js + M src/utils.js + A command/git-add.md + +敏感文件已过滤并排除,保护了你的凭证信息。 + +下一步: +- 运行 /git-commit 生成提交信息并提交 +- 运行 /git-status 查看暂存区状态 +``` + +## Safety Features + +### Automatic Filters + +| Pattern | Why | Can Override | +|---------|-----|--------------| +| `.env*` | Environment variables | No | +| `*.key`, `*.pem` | Private keys | No | +| `credentials.json` | API credentials | No | +| `secrets.json` | Secrets | No | +| `.aws/*`, `.gcloud/*` | Cloud credentials | No | +| `.ssh/*` | SSH keys | No | +| `package-lock.json` | Lock files (optional) | Yes | + +### Warnings + +- Show count of filtered files +- List filtered file names for transparency +- Warn when using "all" option +- Explain why each sensitive file was excluded + +## Use Cases + +- Stage changes safely before commit +- Prevent accidental credential leaks +- Review what will be committed before staging +- Follow security best practices + +## Related Commands + +- `/git-status` - Check file changes before staging +- `/git-commit` - Commit staged files +- `/git-push` - Push commits to remote + +## Examples + +### Normal Usage (无未跟踪文件) +```bash +/git-add +# 显示:3 个已修改文件 +# 提示:是否确认暂存? (y/no) +# 输入 "y" 暂存所有已修改文件 +``` + +### 包含未跟踪文件的情况 +```bash +/git-add +# 显示: +# - 3 个已修改/已删除文件 +# - 2 个未跟踪文件 +# 提示:是否确认暂存? (y/all/u/no) +# 输入 "y" 仅暂存已修改文件 +# 输入 "all" 或 "u" 暂存所有文件(包括未跟踪) +``` + +### 强制暂存敏感文件 +```bash +/git-add +# 显示:包含 2 个敏感文件 +# 提示:是否确认暂存? (y/all/force/no) +# 输入 "force" 强制暂存所有文件 +# ⚠️ 显示二次确认警告 +``` + +### 暂存特定文件 +```bash +/git-add +# 显示预览 +# 输入文件路径:src/index.js +# 仅暂存指定的文件 +``` + +### 取消操作 +```bash +/git-add +# 显示预览 +# 输入 "no" 或 "cancel" +# 操作中止 +``` + +## Implementation Guide + +### 关键实现要点 + +**1. 文件分类逻辑** +```bash +# 获取所有状态 +git status --porcelain + +# 分类处理 +tracked_modified=() # M 状态 +tracked_deleted=() # D 状态 +untracked_safe=() # ?? 状态且非敏感 +untracked_sensitive=() # ?? 状态且敏感 +tracked_sensitive=() # M/D 状态但匹配敏感规则 +``` + +**2. 选项显示逻辑** +```python +if has_untracked_files: + if has_sensitive_files: + prompt = "是否确认暂存? (y/all/force/no)" + options = { + "y": "仅暂存已跟踪的修改/删除", + "all/u": "暂存所有安全文件(含未跟踪)", + "force": "暂存所有文件(含敏感)⚠️", + "no": "取消操作" + } + else: + prompt = "是否确认暂存? (y/all/no)" + options = { + "y": "仅暂存已跟踪的修改/删除", + "all/u": "暂存所有文件(含未跟踪)✓ 推荐", + "no": "取消操作" + } +else: + prompt = "是否确认暂存? (y/no)" +``` + +**3. 执行逻辑** +```bash +case $choice in + y|yes) + git add "${tracked_modified[@]}" "${tracked_deleted[@]}" + ;; + all|u|with-untracked) + git add "${tracked_modified[@]}" "${tracked_deleted[@]}" "${untracked_safe[@]}" + ;; + force) + # 二次确认 + read -p "⚠️ 确认暂存敏感文件? (输入 confirm): " confirm + if [[ "$confirm" == "confirm" ]]; then + git add . + fi + ;; +esac +``` + +## Notes + +- This command prioritizes security over convenience +- Sensitive files must be explicitly forced to stage +- **重要:第一次运行就显示所有选项,不需要用户多次运行命令** +- Untracked files are displayed upfront with clear options +- Use `.secretsignore` file for project-specific exclusion patterns +- Consider setting up git hooks for additional security diff --git a/command/git-commit.md b/command/git-commit.md new file mode 100644 index 0000000..5d7aca6 --- /dev/null +++ b/command/git-commit.md @@ -0,0 +1,189 @@ +--- +description: Commit staged files with auto-generated message and create version tag +--- + +# git-commit + +Auto-generate commit message for staged files, commit to local repository, and create version tag. + +## What It Does + +- Analyzes staged changes +- Auto-generates meaningful commit message following Conventional Commits +- Detects project type and updates version number if needed +- Creates appropriate git tag (polyrepo or monorepo format) +- Does NOT push to remote (local only) + +## Quick Start + +Use this command when you have files in staging area and want to commit with proper version tagging. + +## Workflow + +This command follows the git workflow defined in `skill/git/SKILL.md`: + +1. ✓ Check staging area (must have files) +2. ✓ Analyze changes and repository type +3. ✓ Detect project type and version file +4. ✓ Generate commit message (Conventional Commits) +5. ✓ Update version number if needed +6. ✓ Commit with generated message +7. ✓ Create version tag +8. ✗ Skip push (use `/git-push` to push) + +## Prerequisites + +**Staging area must not be empty:** +```bash +git add +``` + +## Options + +User can optionally input: +- **"skip tag"** or **"skip"**: Skip tag creation, only commit + +## Steps + +### 1. Check Staging Area + +```bash +git diff --cached --name-only +``` + +**If empty:** +- Display: "暂存区为空,请先使用 `git add` 添加文件。" +- **Terminate execution** + +### 2. Collect Information (parallel) + +```bash +git status +git diff --cached +git log --oneline -10 +git tag --list | sort -V | tail -5 +``` + +If `AGENTS.md` exists, read it for: +- Repository type (polyrepo/monorepo) +- Version update rules +- Project structure + +### 3. Detect Repository Type + +- **Polyrepo**: Single project, tag format `1.2.0` +- **Monorepo**: Multiple subprojects, tag format `-1.2.0` + +Detection: +- Check `AGENTS.md` for monorepo indicator +- Check directory structure (`packages/`, `apps/`, etc.) +- Analyze changed file paths for subproject scope + +### 4. Detect Project Type and Version + +Follow `skill/git/SKILL.md` guidelines: + +**Common project types:** +- iOS: `*.xcodeproj/project.pbxproj` → `MARKETING_VERSION` +- Node.js: `package.json` → `version` +- Android: `build.gradle(.kts)` → `versionName` +- Go: Git tag only (no version file) +- etc. + +**Version increment rules:** +- `feat`: minor +1 → `1.2.0` → `1.3.0` +- `fix`: patch +1 → `1.2.3` → `1.2.4` +- `perf`: patch +1 +- Breaking change: major +1 → `1.2.3` → `2.0.0` + +**Only update version for user-perceivable changes:** +- ✓ feat, fix, perf, breaking changes +- ✗ docs, test, refactor, style, chore, ci, build + +### 5. Generate Commit Message + +Follow Conventional Commits specification: + +**Format:** +``` +(): + + +``` + +**Commit language:** +- macOS/Linux: Chinese (中文) +- Windows: English (encoding issue) + +**Monorepo scope:** +- If changes affect single subproject: `feat(ios): 添加新功能` +- If multiple subprojects: `feat: 添加新功能` + +### 6. Update Version Number + +If version update is needed: + +1. Update version file with new version +2. Add version file to staging: `git add ` +3. Verify: `git diff --cached --name-only` + +### 7. Commit Changes + +```bash +# macOS/Linux (Chinese) +git commit -m "feat(android): 添加用户认证" + +# Windows (English) +git commit -m "feat(android): add user authentication" + +# Multi-line (macOS/Linux) +git commit -m "$(cat <<'EOF' +feat: add user authentication + +- Add OAuth2 support +- Implement JWT tokens +EOF +)" +``` + +### 8. Create Tag + +**Only if:** +- Version update occurred +- User didn't input "skip tag" + +**Tag format:** +- Polyrepo: `git tag -a "1.2.0" -m "feat: add feature"` +- Monorepo: `git tag -a "ios-1.2.0" -m "feat(ios): add feature"` + +**Tag annotation:** +- Use same content as commit message +- Multi-line commits → multi-line annotations + +### 9. Display Result + +Show summary in Chinese: +``` +✓ 提交成功 + +提交信息:feat(android): 添加用户认证 +版本标签:android-1.3.0 + +要推送到远程仓库,请运行:/git-push +``` + +## Use Cases + +- Commit staged changes with proper versioning +- Create local version tag for release preparation +- Follow semantic versioning automatically + +## Related Commands + +- `/git-status` - Check file changes before commit +- `/git-push` - Push commits and tags to remote +- `/git-push-tags` - Push tags only + +## Reference + +Complete workflow documentation: `skill/git/SKILL.md` diff --git a/command/git-pull.md b/command/git-pull.md new file mode 100644 index 0000000..8d263e5 --- /dev/null +++ b/command/git-pull.md @@ -0,0 +1,85 @@ +--- +description: Pull latest changes from remote repository +--- + +# git-pull + +Pull latest changes from remote repository for current branch. + +## What It Does + +- Fetches latest changes from remote +- Merges remote changes into local branch +- Updates working directory + +## Quick Start + +Use this command to synchronize your local branch with the latest remote changes. + +## Steps + +### 1. Execute Pull + +```bash +git pull +``` + +This command will: +- Fetch updates from remote repository +- Automatically merge changes into current branch +- Update your working directory + +### 2. Handle Conflicts (if any) + +If merge conflicts occur: +- Git will mark conflicting files +- Resolve conflicts manually +- Stage resolved files: `git add ` +- Complete merge: `git commit` + +Or use: `/git-status` to check conflict status + +### 3. Display Result + +Show result message: + +``` +✓ 拉取成功 + +分支已更新至最新状态。 +``` + +Or if conflicts: + +``` +⚠️ 发生合并冲突 + +请解决以下文件中的冲突: +- src/index.js +- README.md + +然后运行: +git add +git commit + +或使用 /git-status 查看详细状态 +``` + +## Use Cases + +- Sync local branch with remote updates +- Get latest changes from team +- Before starting new feature work +- Before pushing your changes + +## Related Commands + +- `/git-status` - Check file changes and merge status +- `/git-push` - Push local changes to remote +- `/git-commit` - Commit local changes + +## Notes + +- Pulls from tracking remote branch (usually origin/main) +- Uses default merge strategy (recursive merge) +- If conflicts occur, resolve them before committing diff --git a/command/git-push-tags.md b/command/git-push-tags.md new file mode 100644 index 0000000..750339a --- /dev/null +++ b/command/git-push-tags.md @@ -0,0 +1,91 @@ +--- +description: Push all local tags to remote repository +--- + +# git-push-tags + +Push all local tags to the remote repository. + +## What It Does + +1. Lists all local tags +2. Shows which tags are not yet on remote +3. Pushes all tags to remote (origin) +4. Confirms successful push + +## Steps + +### 1. List Local Tags + +```bash +echo "=== Local Tags ===" +git tag --list | sort -V +``` + +### 2. Check Remote Tags + +```bash +echo "=== Remote Tags ===" +git ls-remote --tags origin | awk '{print $2}' | sed 's|refs/tags/||' | sort -V +``` + +### 3. Identify Unpushed Tags + +Compare local and remote tags to identify which tags need to be pushed. + +### 4. Confirm Before Push + +Display summary in Chinese: +``` +本地标签总数:15 个 +远程标签总数:12 个 +待推送标签:3 个 +- 1.3.0 +- 1.3.1 +- ios-2.0.0 + +确认推送所有标签到 origin? +``` + +If user confirms or no confirmation needed, proceed to push. + +### 5. Push All Tags + +```bash +git push --tags +# or +git push origin --tags +``` + +### 6. Verify Push + +```bash +echo "=== Push Result ===" +git ls-remote --tags origin | tail -5 +``` + +Display confirmation in Chinese: +``` +✓ 已成功推送所有标签到 origin + +最近 5 个远程标签: +- 1.3.0 +- 1.3.1 +- ios-2.0.0 +- android-1.5.0 +- electron-3.0.0 +``` + +## Use Cases + +- After creating multiple local tags +- Sync tags with remote repository +- Before triggering CI/CD release workflows +- Share version tags with team + +## Notes + +- **Safe operation**: Pushing tags does not affect commits +- **No force push**: Uses standard push, won't overwrite existing tags +- **All tags**: Pushes ALL local tags, not just one +- If you only want to push a specific tag, use: `git push origin ` diff --git a/command/git-push.md b/command/git-push.md new file mode 100644 index 0000000..6952d0e --- /dev/null +++ b/command/git-push.md @@ -0,0 +1,190 @@ +--- +description: Commit staged files, create version tag, and push to remote repository +--- + +# git-push + +Complete workflow: auto-generate commit message, create version tag, and push everything to remote repository. + +## What It Does + +This is the **all-in-one command** that: +1. ✓ Analyzes and commits staged changes (like `/git-commit`) +2. ✓ Creates version tag if needed +3. ✓ Pushes commit to remote repository +4. ✓ Pushes tag to remote repository + +## Quick Start + +Use this command when you want to commit AND push in one step. + +## Prerequisites + +**Staging area must have files:** +```bash +git add +``` + +## Options + +User can optionally input: +- **"skip tag"** or **"skip"**: Skip tag creation, only commit and push + +## Workflow + +This command combines `/git-commit` + push operations: + +1. ✓ Check staging area +2. ✓ Analyze changes and repository type +3. ✓ Detect project type and version +4. ✓ Generate commit message +5. ✓ Update version number if needed +6. ✓ Commit with generated message +7. ✓ Create version tag +8. ✓ Push commit to remote +9. ✓ Push tag to remote + +## Steps + +### 1-8. Commit and Tag + +Execute the same steps as `/git-commit`: +- Check staging area (must not be empty) +- Collect information (status, diff, logs, tags, AGENTS.md) +- Detect repository type (polyrepo/monorepo) +- Detect project type and version file +- Generate commit message (Conventional Commits, Chinese/English) +- Update version number if needed +- Commit changes +- Create tag if needed + +Refer to `/git-commit` for detailed steps or see `skill/git/SKILL.md`. + +### 9. Push Commit to Remote + +```bash +# Push current branch to origin +git push origin $(git branch --show-current) +``` + +### 10. Push Tag to Remote + +**Only if tag was created:** + +**Polyrepo:** +```bash +git push origin +# Example: git push origin 1.2.0 +``` + +**Monorepo:** +```bash +git push origin - +# Example: git push origin ios-1.2.0 +``` + +### 11. Display Result + +Show summary in Chinese: +``` +✓ 提交并推送成功 + +分支:main +提交信息:feat(android): 添加用户认证 +版本标签:android-1.3.0 + +已推送到远程仓库:origin +- 提交:a1b2c3d +- 标签:android-1.3.0 +``` + +## Comparison with Other Commands + +| Command | Commit | Tag | Push Commit | Push Tag | +|---------|--------|-----|-------------|----------| +| `/git-commit` | ✓ | ✓ | ✗ | ✗ | +| `/git-push` | ✓ | ✓ | ✓ | ✓ | +| `/git-push-tags` | ✗ | ✗ | ✗ | ✓ (all tags) | + +## Use Cases + +- **Quick workflow**: Commit and push in one command +- **Release preparation**: Create version tag and push for CI/CD +- **Daily development**: Commit and sync with remote immediately + +## When to Use Which Command + +**Use `/git-commit`** (local only): +- You want to review before pushing +- Working on feature branch not ready to share +- Creating multiple commits before one push + +**Use `/git-push`** (commit + push): +- Ready to share changes immediately +- Main branch development with CI/CD +- Quick fixes that should be synced now + +**Use `/git-push-tags`** (tags only): +- Already committed but forgot to push tags +- Want to push multiple accumulated tags at once +- Tag-only sync without new commits + +## Error Handling + +### If Staging Area is Empty + +``` +暂存区为空,请先使用 `git add` 添加文件。 +``` +**Action:** Terminate, user must stage files first. + +### If Push Fails + +Common reasons: +- Remote branch protection +- Need to pull first (diverged history) +- No permission to push + +**Action:** Display error in Chinese, suggest solutions: +``` +✗ 推送失败 + +原因:远程分支有更新,需要先拉取 + +建议操作: +1. git pull --rebase origin main +2. 解决冲突(如有) +3. 重新运行 /git-push +``` + +### If Remote Rejects Tag + +If tag already exists on remote: +``` +✗ 标签推送失败 + +原因:远程仓库已存在标签 1.2.0 + +建议操作: +1. 删除本地标签:git tag -d 1.2.0 +2. 更新版本号后重新提交 +``` + +## Notes + +- **Main branch**: Default push target is `main` (not `master`) +- **Conventional Commits**: Auto-generated messages follow standard format +- **Semantic Versioning**: Automatic version bumping based on change type +- **Polyrepo/Monorepo**: Automatically detects and uses correct tag format +- **Platform-specific**: Commit messages in Chinese (macOS/Linux) or English (Windows) + +## Related Commands + +- `/git-status` - Check changes before committing +- `/git-commit` - Commit locally without pushing +- `/git-push-tags` - Push only tags (no commits) + +## Reference + +Complete git workflow guide: `skill/git/SKILL.md` +Quick command reference: `skill/git/quick-reference.md` diff --git a/command/git-status.md b/command/git-status.md new file mode 100644 index 0000000..681b518 --- /dev/null +++ b/command/git-status.md @@ -0,0 +1,54 @@ +--- +description: Check git working directory status and file changes +--- + +# git-status + +Check current git repository status and display file changes in a clear format. + +## What It Does + +- Shows current branch +- Lists changed files (unstaged and staged) + +## Steps + +Execute the following commands in parallel: + +```bash +# Current branch +git branch --show-current + +# Repository status +git status + +# List changed files only +echo "=== Unstaged Changes ===" +git diff --name-only + +echo "=== Staged Changes ===" +git diff --cached --name-only +``` + +## Output Format + +Present the information in Chinese with clear sections: + +``` +当前分支:main + +未暂存的文件 (3): +- src/index.js +- src/utils.js +- README.md + +已暂存的文件 (2): +- package.json +- src/config.js +``` + +## Use Cases + +- Quick status check before committing +- Review what files have changed +- Check if you're on the correct branch diff --git a/command/gitea-config.md b/command/gitea-config.md new file mode 100644 index 0000000..4081d31 --- /dev/null +++ b/command/gitea-config.md @@ -0,0 +1,168 @@ +--- +description: View current Gitea configuration and runner status +--- + +# gitea-config + +查看当前 Gitea 配置信息和 Runner 状态。 + +## Features + +- 显示配置的 Gitea URL +- 显示默认组织 +- 验证 Token 状态和关联用户 +- 显示已配置的 Runner 数量和列表 +- 显示配置文件路径 + +## Steps + +### 1. Check Configuration File + +```bash +config_dir="$HOME/.config/gitea" +config_file="$config_dir/config.env" + +if [ ! -f "$config_file" ]; then + echo "❌ Gitea 未配置" + echo "请运行 /gitea-reset 进行配置" + exit 1 +fi +``` + +### 2. Load and Display Configuration + +```bash +source "$config_file" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "当前 Gitea 配置" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " URL: $GITEA_URL" +echo " 默认组织: ${GITEA_DEFAULT_ORG:-<未设置>}" +echo " 配置文件: $config_file" +echo "" +``` + +### 3. Validate Token and Display User Info + +```bash +response=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $GITEA_TOKEN" \ + "${GITEA_URL}/api/v1/user") + +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | sed '$d') + +if [ "$http_code" = "200" ]; then + username=$(echo "$body" | jq -r '.login') + email=$(echo "$body" | jq -r '.email // "<未设置>"') + echo " Token 状态: ✓ 有效" + echo " 登录用户: $username" + echo " 邮箱: $email" +else + echo " Token 状态: ✗ 无效或已过期" +fi +``` + +### 4. Display Runner Information + +```bash +runners_dir="$config_dir/runners" + +if [ -d "$runners_dir" ]; then + runner_count=$(ls -1 "$runners_dir" 2>/dev/null | wc -l | tr -d ' ') + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Runner 信息" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo " 已配置 Runner 数量: $runner_count" + echo " Runner 目录: $runners_dir" + + if [ "$runner_count" -gt 0 ]; then + echo "" + echo " 已配置的 Runners:" + ls -1 "$runners_dir" | while read runner; do + # Check if running + config_file="$runners_dir/$runner/config.yaml" + if [ -f "$config_file" ]; then + if pgrep -f "act_runner daemon --config $config_file" > /dev/null; then + status="🟢" + else + status="🔴" + fi + echo " $status $runner" + else + echo " ⚠️ $runner (配置文件缺失)" + fi + done + fi +else + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Runner 信息" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo " 未配置任何 Runner" +fi +``` + +### 5. Display Management Commands + +```bash +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "管理命令" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " 重置配置: /gitea-reset" +echo " 切换组织: /gitea-switch-org " +echo " 列出 Runners: /gitea-list-runners" +echo " 创建仓库: /create-gitea-repo " +echo "" +``` + +## Output Example + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +当前 Gitea 配置 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + URL: https://git.digitevents.com + 默认组织: ai + 配置文件: /Users/voson/.config/gitea/config.env + + Token 状态: ✓ 有效 + 登录用户: your_username + 邮箱: your_username@example.com + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Runner 信息 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 已配置 Runner 数量: 2 + Runner 目录: /Users/voson/.config/gitea/runners + + 已配置的 Runners: + 🟢 runner-macbook-pro + 🔴 runner-mac-mini + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +管理命令 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 重置配置: /gitea-reset + 切换组织: /gitea-switch-org + 列出 Runners: /gitea-list-runners + 创建仓库: /create-gitea-repo +``` + +## Notes + +- 使用 `jq` 解析 JSON 响应 +- 检查 runner 进程使用 `pgrep` +- Token 验证通过调用 `/api/v1/user` endpoint +- Runner 状态:🟢 运行中、🔴 已停止、⚠️ 配置异常 diff --git a/command/gitea-create-repo.md b/command/gitea-create-repo.md new file mode 100644 index 0000000..d138909 --- /dev/null +++ b/command/gitea-create-repo.md @@ -0,0 +1,256 @@ +--- +description: Create a new Git repository on Gitea +--- + +# gitea-create-repo + +Create a new Git repository on Gitea. + +## Features + +- Create new repository under specified organization or user via Gitea API +- Support creating private or public repositories +- Automatically add remote repository address after successful creation + +## User Input Format + +User can specify parameters in the following format: + +``` +[/] [private|public] +``` + +- `owner`: Organization name or username (optional, uses `GITEA_DEFAULT_ORG` if set, otherwise uses current user) +- `repo`: Repository name (required) +- `private|public`: Visibility (optional, default `private`) + +**Examples**: +- `my-project` - Create private repository under default organization (or current user if not set) +- `ai/my-project` - Create private repository my-project under ai organization +- `ai/my-project public` - Create public repository my-project under ai organization +- `username/test private` - Create private repository test under username user + +## Configuration + +Load configuration from `~/.config/gitea/config.env`: + +| Config Item | Source | +| --- | --- | +| Gitea Server URL | `GITEA_URL` from config.env | +| API Token | `GITEA_TOKEN` from config.env | +| Default Organization | `GITEA_DEFAULT_ORG` from config.env (optional) | + +## Steps + +### 0. Load Configuration + +Before executing, load Gitea configuration: + +```bash +config_file="$HOME/.config/gitea/config.env" + +if [ ! -f "$config_file" ]; then + echo "❌ Gitea 未配置,请运行 /gitea-reset 进行初始化" + exit 1 +fi + +source "$config_file" + +# Validate required variables +if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then + echo "❌ 配置文件不完整,请运行 /gitea-reset 重新配置" + exit 1 +fi +``` + +### 1. Parse User Input + +Parse from user input: +- `owner`: Organization name or username +- `repo`: Repository name +- `visibility`: `private` (default) or `public` + +**Input parsing logic**: + +```bash +input="$1" +visibility="${2:-private}" + +# Parse owner/repo +if [[ "$input" =~ / ]]; then + owner=$(echo "$input" | cut -d'/' -f1) + repo=$(echo "$input" | cut -d'/' -f2) +else + # Use default organization if available, otherwise use current user + if [ -z "$GITEA_DEFAULT_ORG" ]; then + # Get current user from Gitea API + owner=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "${GITEA_URL}/api/v1/user" | jq -r '.login') + + if [ -z "$owner" ] || [ "$owner" = "null" ]; then + echo "❌ 无法获取当前用户信息,请使用 owner/repo 格式" + exit 1 + fi + + echo "使用当前用户: $owner" + else + owner="$GITEA_DEFAULT_ORG" + echo "使用默认组织: $owner" + fi + repo="$input" +fi + +# Convert visibility to boolean +private_bool=$([ "$visibility" = "private" ] && echo "true" || echo "false") +``` + +**Input validation**: +- Repository name can only contain letters, numbers, underscores, hyphens and dots + +```bash +if ! [[ "$repo" =~ ^[a-zA-Z0-9_.-]+$ ]]; then + echo "❌ 仓库名只能包含字母、数字、下划线、连字符和点" + exit 1 +fi +``` + +### 2. Call Gitea API to Create Repository + +Use curl to call Gitea API with configuration: + +```bash +echo "正在创建仓库: $owner/$repo ($visibility)" + +# Try organization repository first +response=$(curl -s -w "\n%{http_code}" -X POST \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"name\": \"${repo}\", + \"private\": ${private_bool}, + \"auto_init\": false, + \"default_branch\": \"main\" + }" \ + "${GITEA_URL}/api/v1/orgs/${owner}/repos") + +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | sed '$d') + +# If 404, try user repository +if [ "$http_code" = "404" ]; then + echo "⚠️ 组织不存在,尝试创建用户仓库..." + + response=$(curl -s -w "\n%{http_code}" -X POST \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"name\": \"${repo}\", + \"private\": ${private_bool}, + \"auto_init\": false, + \"default_branch\": \"main\" + }" \ + "${GITEA_URL}/api/v1/user/repos") + + http_code=$(echo "$response" | tail -n1) + body=$(echo "$response" | sed '$d') +fi +``` + +> Note: First try organization API, if 404 then try user API. + +### 3. Handle Response + +```bash +case "$http_code" in + 201) + echo "✓ 仓库创建成功" + ;; + 409) + echo "❌ 仓库已存在: $owner/$repo" + echo "查看仓库: ${GITEA_URL}/$owner/$repo" + exit 1 + ;; + 404) + echo "❌ Owner 不存在或无权限: $owner" + exit 1 + ;; + *) + echo "❌ 创建失败 (HTTP $http_code)" + echo "$body" | jq -r '.message // empty' + exit 1 + ;; +esac + +# Extract repository info +html_url=$(echo "$body" | jq -r '.html_url') +clone_url=$(echo "$body" | jq -r '.clone_url') +ssh_url=$(echo "$body" | jq -r '.ssh_url') +``` + +**Response Handling**: +- **HTTP 201**: Repository created successfully +- **HTTP 409**: Repository already exists +- **HTTP 404**: Owner does not exist or no permission +- **Other**: API error, display error message + +### 4. Ask Whether to Add Remote Repository + +Ask user whether to add the newly created repository as current project's remote repository. + +**If user confirms**: + +1. Check if current directory is a Git repository: + ```bash + git rev-parse --is-inside-work-tree + ``` + +2. If not a Git repository, ask whether to initialize: + ```bash + git init + ``` + +3. Check if origin remote already exists: + ```bash + git remote get-url origin + ``` + +4. Add or update remote repository: + - If no origin: `git remote add origin ` + - If origin exists: Ask whether to overwrite, after confirmation execute `git remote set-url origin ` + +### 5. Output Result Summary + +Output creation result summary: + +```bash +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "仓库创建成功" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " 名称: $owner/$repo" +echo " 可见性: $visibility" +echo " Web URL: $html_url" +echo " HTTPS URL: $clone_url" +echo " SSH URL: $ssh_url" +echo "" +``` + +**Example Output**: +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +仓库创建成功 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 名称: ai/my-project + 可见性: private + Web URL: https://git.digitevents.com/ai/my-project + HTTPS URL: https://git.digitevents.com/ai/my-project.git + SSH URL: git@git.digitevents.com:ai/my-project.git +``` + +## Notes + +- **Permission check**: Ensure Token has permission to create repository +- **Organization vs User**: Creating organization repository and user repository use different API endpoints +- **Repository naming**: Follow Gitea naming rules, avoid special characters diff --git a/command/gitea-create-runner.md b/command/gitea-create-runner.md new file mode 100644 index 0000000..ecb34e7 --- /dev/null +++ b/command/gitea-create-runner.md @@ -0,0 +1,662 @@ +--- +description: Create and start a Gitea Actions runner (default host mode) +--- + +# gitea-create-runner + +创建并启动 Gitea Actions Runner(默认 host 模式)。 + +## 命令说明 + +此命令用于快速创建和启动一个 Gitea Actions Runner。默认使用 host 模式,自动检测系统环境并配置。 + +**重要**:这是一个可执行命令,AI 应该按照以下步骤执行操作。 + +## Features + +- 自动检查并安装 act_runner(如果未安装) +- 自动加载 Gitea 配置(如果不存在则提示初始化) +- 默认使用 host 模式(支持 Android SDK、iOS 构建等原生工具) +- 智能检测系统环境(OS、架构)并生成合适的 labels +- 自动注册并后台启动 runner +- 优先尝试创建全局 runner(需要管理员权限) +- 权限不足时自动降级到组织 runner + +## User Input Format + +无需参数,运行后自动创建。可选提供 runner 名称。 + +```bash +/gitea-create-runner +# 或 +/gitea-create-runner my-runner-name +``` + +## AI 执行指导 + +当用户调用此命令时,AI 应该: + +1. **检查 act_runner 安装**:如果未安装,自动安装 +2. **加载 Gitea 配置**:如果不存在,提示用户先初始化 +3. **生成 runner 名称**:基于主机名或用户输入 +4. **检测系统环境**:自动生成 labels +5. **创建 runner 目录**:在 `~/.config/gitea/runners/` 下创建 +6. **生成配置文件**:默认 host 模式配置 +7. **获取注册 token**:优先全局,失败则降级到组织 +8. **注册 runner**:使用 act_runner register +9. **启动 runner**:后台启动并显示状态 + +## Implementation Steps + +### 1. Check act_runner Installation + +**AI 执行**:检查 act_runner 是否已安装,如果没有则自动安装。 + +```bash +echo "检查 act_runner 安装状态..." + +if command -v act_runner &> /dev/null; then + version=$(act_runner --version 2>&1 | head -n1) + echo "✓ act_runner 已安装: $version" +else + echo "⚠️ act_runner 未安装" + echo "正在使用 Homebrew 安装..." + + if ! command -v brew &> /dev/null; then + echo "❌ 需要先安装 Homebrew" + echo " 安装命令: /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"" + exit 1 + fi + + brew install act_runner + + if [ $? -eq 0 ]; then + version=$(act_runner --version 2>&1 | head -n1) + echo "✓ act_runner 安装成功: $version" + else + echo "❌ act_runner 安装失败" + exit 1 + fi +fi + +echo "" +``` + +### 2. Load Gitea Configuration + +**AI 执行**:加载 Gitea 配置,如果不存在则提示初始化。 + +```bash +config_file="$HOME/.config/gitea/config.env" + +if [ ! -f "$config_file" ]; then + echo "❌ Gitea 配置不存在" + echo "" + echo "请先初始化 Gitea 配置:" + echo " /gitea-reset" + echo "" + echo "或使用以下命令查看配置:" + echo " /gitea-config" + exit 1 +fi + +source "$config_file" + +if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then + echo "❌ Gitea 配置不完整" + echo " 需要 GITEA_URL 和 GITEA_TOKEN" + echo "" + echo "请重新初始化配置:" + echo " /gitea-reset" + exit 1 +fi + +echo "✓ 已加载 Gitea 配置" +echo " URL: $GITEA_URL" +echo "" +``` + +### 3. Generate Runner Name + +**AI 执行**:生成 runner 名称(基于主机名或用户输入)。 + +```bash +# Check if runner name provided as argument +# 如果用户提供了名称参数,使用该参数;否则基于主机名生成 + +if [ -n "$1" ]; then + runner_name="$1" + echo "使用指定的 Runner 名称: $runner_name" +else + hostname=$(hostname -s 2>/dev/null || echo "unknown") + runner_name="runner-$hostname" + echo "生成 Runner 名称: $runner_name" + echo " (基于主机名: $hostname)" +fi + +echo "" + +# Validate runner name +if [[ ! "$runner_name" =~ ^[a-zA-Z0-9_-]+$ ]]; then + echo "❌ Runner 名称只能包含字母、数字、下划线和连字符" + exit 1 +fi +``` + +### 4. Check Runner Existence + +**AI 执行**:检查 runner 是否已存在。 + +```bash +runners_dir="$HOME/.config/gitea/runners" +runner_dir="$runners_dir/$runner_name" + +if [ -d "$runner_dir" ]; then + echo "❌ Runner '$runner_name' 已存在" + echo " 路径: $runner_dir" + echo "" + echo "选项:" + echo " 1. 使用其他名称: /gitea-create-runner <新名称>" + echo " 2. 删除现有 runner: /gitea-delete-runner" + echo " 3. 查看所有 runners: /gitea-list-runners" + exit 1 +fi +``` + +### 5. Detect System Environment + +**AI 执行**:智能检测系统环境并生成 labels。 + +```bash +echo "检测系统环境..." + +# Detect OS +OS=$(uname -s) +case "$OS" in + Darwin) os_label="macOS" ;; + Linux) os_label="ubuntu" ;; + *) os_label="unknown" ;; +esac + +# Detect architecture +ARCH=$(uname -m) +case "$ARCH" in + arm64|aarch64) arch_label="ARM64" ;; + x86_64) arch_label="x64" ;; + *) arch_label="unknown" ;; +esac + +# Generate combined label +combined=$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]') + +# Generate labels for host mode +labels="self-hosted:host,${os_label}:host,${arch_label}:host,${combined}:host" + +echo "✓ 系统信息" +echo " 操作系统: $OS ($os_label)" +echo " 架构: $ARCH ($arch_label)" +echo " 组合标签: $combined" +echo "" +echo "✓ Runner Labels (Host Mode)" +echo " $labels" +echo "" +``` + +### 6. Create Runner Directory + +**AI 执行**:创建 runner 目录结构。 + +```bash +echo "创建 Runner 目录..." + +mkdir -p "$runner_dir"/{cache,workspace} + +if [ ! -d "$runner_dir" ]; then + echo "❌ 创建目录失败" + exit 1 +fi + +echo "✓ 目录创建成功" +echo " 路径: $runner_dir" +echo " - cache/" +echo " - workspace/" +echo "" +``` + +### 7. Generate Configuration File + +**AI 执行**:生成 host 模式配置文件。 + +```bash +echo "生成配置文件..." + +# Get environment-specific settings (with defaults) +runner_capacity="${GITEA_RUNNER_CAPACITY:-2}" +runner_timeout="${GITEA_RUNNER_TIMEOUT:-3h}" + +# Generate config.yaml for host mode +cat > "$runner_dir/config.yaml" << EOF +log: + level: info + +runner: + file: $runner_dir/.runner + capacity: $runner_capacity + timeout: $runner_timeout + shutdown_timeout: 30s + insecure: false + fetch_timeout: 5s + fetch_interval: 2s + labels: + - "self-hosted:host" + - "${os_label}:host" + - "${arch_label}:host" + - "${combined}:host" + +cache: + enabled: true + dir: "$runner_dir/cache" + host: "127.0.0.1" + port: 9000 + +host: + workdir_parent: "$runner_dir/workspace" +EOF + +if [ ! -f "$runner_dir/config.yaml" ]; then + echo "❌ 配置文件生成失败" + exit 1 +fi + +echo "✓ 配置文件已生成" +echo " 文件: $runner_dir/config.yaml" +echo " 模式: Host Mode (原生执行)" +echo " 容量: $runner_capacity 并发任务" +echo " 超时: $runner_timeout" +echo "" +``` + +### 8. Get Registration Token + +**AI 执行**:获取注册 token(优先全局,失败则降级到组织)。 + +```bash +echo "获取 Runner 注册 Token..." +echo "" + +# Try global runner first (requires admin token) +echo "尝试创建全局 Runner(可用于所有组织和仓库)..." + +response=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $GITEA_TOKEN" \ + "${GITEA_URL}/api/v1/admin/runners/registration-token") + +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | sed '$d') + +# Check for admin permission +if [ "$http_code" = "200" ]; then + echo "✓ 使用全局 Runner" + runner_level="global" + registration_token=$(echo "$body" | jq -r '.token') +else + echo "⚠️ 全局 Runner 权限不足 (HTTP $http_code)" + echo " 全局 Runner 需要管理员 Token" + echo "" + echo "降级到组织 Runner..." + + runner_level="organization" + + if [ -n "$GITEA_DEFAULT_ORG" ]; then + org_name="$GITEA_DEFAULT_ORG" + echo "使用默认组织: $org_name" + else + echo "❌ 未配置默认组织" + echo " 请在配置文件中设置 GITEA_DEFAULT_ORG" + echo " 或重新初始化配置: /gitea-reset" + exit 1 + fi + + response=$(curl -s -w "\n%{http_code}" -X POST \ + -H "Authorization: token $GITEA_TOKEN" \ + "${GITEA_URL}/api/v1/orgs/$org_name/actions/runners/registration-token") + + http_code=$(echo "$response" | tail -n1) + body=$(echo "$response" | sed '$d') + + if [ "$http_code" != "200" ]; then + echo "❌ 获取注册 Token 失败 (HTTP $http_code)" + echo " 响应: $body" + echo "" + echo "可能的原因:" + echo " 1. 组织不存在" + echo " 2. Token 没有组织管理权限" + echo " 3. 网络连接问题" + exit 1 + fi + + registration_token=$(echo "$body" | jq -r '.token') + echo "✓ 组织 Runner Token 已获取" +fi + +if [ -z "$registration_token" ] || [ "$registration_token" = "null" ]; then + echo "❌ 无法解析注册 Token" + exit 1 +fi + +echo "✓ 注册 Token 已准备" +echo "" +``` + +### 9. Register Runner + +**AI 执行**:注册 runner 到 Gitea 服务器。 + +```bash +echo "注册 Runner 到 Gitea 服务器..." + +act_runner register \ + --config "$runner_dir/config.yaml" \ + --instance "$GITEA_URL" \ + --token "$registration_token" \ + --name "$runner_name" \ + --labels "$labels" \ + --no-interactive + +if [ $? -eq 0 ]; then + echo "✓ Runner 注册成功" +else + echo "❌ Runner 注册失败" + echo "" + echo "请检查:" + echo " 1. Token 是否有效" + echo " 2. Gitea 服务器是否可访问" + echo " 3. 配置文件是否正确" + echo "" + echo "查看详细错误,可以手动运行:" + echo " act_runner register --config $runner_dir/config.yaml --instance $GITEA_URL --token --name $runner_name --labels \"$labels\"" + exit 1 +fi + +# Verify .runner file was created +if [ ! -f "$runner_dir/.runner" ]; then + echo "⚠️ 注册文件未创建,但注册可能成功" + echo " 请检查: $runner_dir/.runner" +fi + +echo "" +``` + +### 10. Start Runner + +**AI 执行**:后台启动 runner 并验证状态。 + +```bash +echo "启动 Runner..." + +# Start runner in background +nohup act_runner daemon --config "$runner_dir/config.yaml" \ + > "$runner_dir/runner.log" 2>&1 & + +runner_pid=$! + +echo " 进程已启动 (PID: $runner_pid)" + +# Wait for runner to initialize +echo " 等待初始化..." +sleep 3 + +# Check if process is still running +if ps -p $runner_pid > /dev/null 2>&1; then + echo "✓ Runner 运行中" +else + echo "❌ Runner 启动失败" + echo "" + echo "查看日志以诊断问题:" + echo " tail -n 50 $runner_dir/runner.log" + echo "" + tail -n 20 "$runner_dir/runner.log" + exit 1 +fi + +echo "" +``` + +### 11. Display Summary + +**AI 执行**:显示创建结果和管理命令。 + +```bash +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "✅ Runner 创建完成!" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Runner 信息:" +echo " 名称: $runner_name" +echo " 级别: $runner_level" +if [ "$runner_level" = "organization" ]; then + echo " 组织: $org_name" +fi +echo " 模式: Host Mode (原生执行)" +echo " 状态: 🟢 运行中" +echo " PID: $runner_pid" +echo "" +echo "配置:" +echo " 容量: $runner_capacity 并发任务" +echo " 超时: $runner_timeout" +echo " Labels: $labels" +echo "" +echo "目录:" +echo " 配置文件: $runner_dir/config.yaml" +echo " 注册信息: $runner_dir/.runner" +echo " 工作目录: $runner_dir/workspace" +echo " 缓存目录: $runner_dir/cache" +echo " 日志文件: $runner_dir/runner.log" +echo "" +echo "管理命令:" +echo " 查看日志: tail -f $runner_dir/runner.log" +echo " 停止 Runner: pkill -f 'act_runner daemon --config.*$runner_name'" +echo " 查看所有: /gitea-list-runners" +echo " 删除 Runner: /gitea-delete-runner" +echo "" +echo "使用 Runner:" +echo " 在 workflow 中使用以下任一 runs-on 值:" +echo " - runs-on: $combined" +echo " - runs-on: $os_label" +echo " - runs-on: [self-hosted, $os_label, $arch_label]" +echo "" +``` + +## Output Example + +### Example 1: Successful Creation (Global Runner) + +``` +检查 act_runner 安装状态... +✓ act_runner 已安装: act_runner version 0.2.13 + +✓ 已加载 Gitea 配置 + URL: https://git.digitevents.com + +生成 Runner 名称: runner-macbook-pro + (基于主机名: macbook-pro) + +检测系统环境... +✓ 系统信息 + 操作系统: Darwin (macOS) + 架构: arm64 (ARM64) + 组合标签: darwin-arm64 + +✓ Runner Labels (Host Mode) + self-hosted:host,macOS:host,ARM64:host,darwin-arm64:host + +创建 Runner 目录... +✓ 目录创建成功 + 路径: /Users/voson/.config/gitea/runners/runner-macbook-pro + - cache/ + - workspace/ + +生成配置文件... +✓ 配置文件已生成 + 文件: /Users/voson/.config/gitea/runners/runner-macbook-pro/config.yaml + 模式: Host Mode (原生执行) + 容量: 2 并发任务 + 超时: 3h + +获取 Runner 注册 Token... + +尝试创建全局 Runner(可用于所有组织和仓库)... +✓ 使用全局 Runner +✓ 注册 Token 已准备 + +注册 Runner 到 Gitea 服务器... +✓ Runner 注册成功 + +启动 Runner... + 进程已启动 (PID: 12345) + 等待初始化... +✓ Runner 运行中 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +✅ Runner 创建完成! +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Runner 信息: + 名称: runner-macbook-pro + 级别: global + 模式: Host Mode (原生执行) + 状态: 🟢 运行中 + PID: 12345 + +配置: + 容量: 2 并发任务 + 超时: 3h + Labels: self-hosted:host,macOS:host,ARM64:host,darwin-arm64:host + +目录: + 配置文件: /Users/voson/.config/gitea/runners/runner-macbook-pro/config.yaml + 注册信息: /Users/voson/.config/gitea/runners/runner-macbook-pro/.runner + 工作目录: /Users/voson/.config/gitea/runners/runner-macbook-pro/workspace + 缓存目录: /Users/voson/.config/gitea/runners/runner-macbook-pro/cache + 日志文件: /Users/voson/.config/gitea/runners/runner-macbook-pro/runner.log + +管理命令: + 查看日志: tail -f /Users/voson/.config/gitea/runners/runner-macbook-pro/runner.log + 停止 Runner: pkill -f 'act_runner daemon --config.*runner-macbook-pro' + 查看所有: /gitea-list-runners + 删除 Runner: /gitea-delete-runner + +使用 Runner: + 在 workflow 中使用以下任一 runs-on 值: + - runs-on: darwin-arm64 + - runs-on: macOS + - runs-on: [self-hosted, macOS, ARM64] +``` + +### Example 2: Auto-install act_runner + +``` +检查 act_runner 安装状态... +⚠️ act_runner 未安装 +正在使用 Homebrew 安装... +==> Downloading act_runner... +==> Installing act_runner... +✓ act_runner 安装成功: act_runner version 0.2.13 + +[继续后续步骤...] +``` + +### Example 3: Downgrade to Organization Runner + +``` +获取 Runner 注册 Token... + +尝试创建全局 Runner(可用于所有组织和仓库)... +⚠️ 全局 Runner 权限不足 (HTTP 403) + 全局 Runner 需要管理员 Token + +降级到组织 Runner... +使用默认组织: ai +✓ 组织 Runner Token 已获取 +✓ 注册 Token 已准备 + +[继续后续步骤...] + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +✅ Runner 创建完成! +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Runner 信息: + 名称: runner-macbook-pro + 级别: organization + 组织: ai + 模式: Host Mode (原生执行) + [...] +``` + +### Example 4: Configuration Not Found + +``` +检查 act_runner 安装状态... +✓ act_runner 已安装: act_runner version 0.2.13 + +❌ Gitea 配置不存在 + +请先初始化 Gitea 配置: + /gitea-reset + +或使用以下命令查看配置: + /gitea-config +``` + +### Example 5: Runner Already Exists + +``` +[前置检查...] + +生成 Runner 名称: runner-macbook-pro + (基于主机名: macbook-pro) + +❌ Runner 'runner-macbook-pro' 已存在 + 路径: /Users/voson/.config/gitea/runners/runner-macbook-pro + +选项: + 1. 使用其他名称: /gitea-create-runner <新名称> + 2. 删除现有 runner: /gitea-delete-runner + 3. 查看所有 runners: /gitea-list-runners +``` + +## Technical Notes + +- **默认模式**:Host Mode(支持 Android SDK、iOS 等原生工具) +- **自动安装**:使用 Homebrew 安装 act_runner +- **智能检测**:自动检测 OS 和架构,生成合适的 labels +- **优雅降级**:全局 runner 权限不足时自动降级到组织 runner +- **后台运行**:自动后台启动,日志记录到文件 +- **配置环境变量**: + - `GITEA_RUNNER_CAPACITY`: 并发任务数(默认 2) + - `GITEA_RUNNER_TIMEOUT`: 任务超时时间(默认 3h) +- **目录权限**:自动创建,权限继承用户默认权限 +- **进程管理**:使用 nohup 后台运行,支持系统重启后手动重启 + +## Related Commands + +- `/gitea-config`: 查看 Gitea 配置 +- `/gitea-reset`: 重置/初始化 Gitea 配置 +- `/gitea-list-runners`: 列出所有 runners +- `/gitea-delete-runner`: 删除指定 runner +- `/create-gitea-repo`: 创建 Gitea 仓库 + +## Safety Features + +- **配置验证**:检查必需的配置项 +- **目录冲突检测**:避免覆盖现有 runner +- **启动验证**:等待并验证进程启动成功 +- **错误提示**:清晰的错误信息和解决方案 +- **日志记录**:所有输出记录到日志文件 + +## Version + +- **Command Version**: 1.0 +- **Last Updated**: 2026-01-12 +- **Compatible with**: act_runner 0.2.13+, macOS/Linux diff --git a/command/gitea-delete-runner.md b/command/gitea-delete-runner.md new file mode 100644 index 0000000..d3d5772 --- /dev/null +++ b/command/gitea-delete-runner.md @@ -0,0 +1,595 @@ +--- +description: Delete a Gitea runner configuration (interactive) +--- + +# gitea-delete-runner + +删除 Gitea Runner 配置(交互式选择)。 + +## 命令说明 + +此命令用于从 Gitea 服务器删除 runner 配置。支持交互式选择单个或所有 runners。 + +**重要**:这是一个可执行命令,AI 应该按照以下步骤执行操作,并与用户进行交互。 + +## Features + +- 显示 Gitea 服务器上的全局 runners +- 交互式选择要删除的 runner +- 支持删除单个或所有 runners +- 删除 runner 配置目录(包括 cache 和 workspace) +- 停止运行中的 runner 进程 +- 自动从 Gitea 服务器注销 runner +- 安全确认机制(需要用户输入 'yes') + +## User Input Format + +无需参数,运行后交互式选择。 + +```bash +/gitea-delete-runner +``` + +## AI 执行指导 + +当用户调用此命令时,AI 应该: + +1. **获取并展示 runner 列表**:调用 Gitea API 获取服务器上的全局 runners,并以清晰的格式展示给用户 +2. **等待用户选择**:让用户选择要删除的 runner(序号、'all' 或 'q') +3. **确认操作**:在删除前要求用户输入 'yes' 确认 +4. **执行删除**:按照三步流程删除选中的 runners +5. **显示结果**:展示删除操作的详细结果 + +**注意**:此命令需要与用户进行多次交互,不要一次性执行所有步骤。 + +## Implementation Steps + +### 1. Load Configuration + +```bash +config_file_main="$HOME/.config/gitea/config.env" + +if [ ! -f "$config_file_main" ]; then + echo "❌ 未找到 Gitea 配置文件" + echo " 路径: $config_file_main" + exit 1 +fi + +source "$config_file_main" + +if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then + echo "❌ Gitea 配置不完整" + echo " 需要 GITEA_URL 和 GITEA_TOKEN" + exit 1 +fi +``` + +### 2. Fetch and Display Runners + +**AI 执行**:运行以下命令获取并显示 runners 列表,然后等待用户选择。 + +```bash +source "$HOME/.config/gitea/config.env" + +echo "正在从 Gitea 服务器获取全局 runners..." +echo "" + +# Fetch global runners from Gitea API +api_endpoint="${GITEA_URL}/api/v1/admin/actions/runners" +response=$(curl -s -H "Authorization: token $GITEA_TOKEN" "$api_endpoint") + +if [ $? -ne 0 ] || [ -z "$response" ]; then + echo "❌ 无法连接到 Gitea 服务器" + exit 1 +fi + +# Parse runners using jq +if ! command -v jq &> /dev/null; then + echo "❌ 需要安装 jq 工具" + echo " 安装: brew install jq" + exit 1 +fi + +# Check if response is valid JSON +if ! echo "$response" | jq empty 2>/dev/null; then + echo "❌ API 返回数据格式错误" + echo " 请检查 Token 权限(需要 admin 权限)" + exit 1 +fi + +# Extract runner information (注意:API 返回格式是 {"runners": [...], "total_count": N}) +runner_count=$(echo "$response" | jq '.runners | length') + +if [ "$runner_count" -eq 0 ]; then + echo "⚠️ 服务器上没有全局 runners" + exit 0 +fi + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "Gitea 全局 Runners (共 $runner_count 个)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Display runners with formatted output +echo "$response" | jq -r '.runners[] | [.id, .name, .status] | @tsv' | \ + awk 'BEGIN{i=1} { + printf "%2d. %-30s [ID: %-5s] %s\n", + i++, $2, $1, ($3=="online"?"🟢 在线":"🔴 离线") + }' + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "选择要删除的 Runner" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " 输入序号: 删除单个 runner" +echo " 输入 'all': 删除所有 runners" +echo " 输入 'q' 或 'quit': 取消" +echo "" + +# Save runner data to temp file for later use +echo "$response" | jq -r '.runners[] | "\(.id)|\(.name)"' > /tmp/gitea_runners.txt +``` + +**AI 指导**:执行完上述命令后,等待用户输入选择(序号、'all' 或 'q')。 + +### 3. Process User Selection + +**AI 指导**:根据用户的输入(从步骤 2 获取),执行相应的处理逻辑。 + +- 如果用户输入 'q' 或 'quit':取消操作,清理临时文件 +- 如果用户输入 'all':准备删除所有 runners,进入步骤 4 确认 +- 如果用户输入序号:验证并准备删除对应的 runner,进入步骤 4 确认 + +**选择处理脚本**(AI 根据用户输入执行相应部分): + +```bash +# 假设用户选择存储在变量 $USER_SELECTION 中 + +if [ "$USER_SELECTION" = "q" ] || [ "$USER_SELECTION" = "quit" ]; then + echo "已取消" + rm -f /tmp/gitea_runners.txt + exit 0 +fi + +# Read runner data from temp file +mapfile -t runner_data < /tmp/gitea_runners.txt +runner_count=${#runner_data[@]} + +if [ "$USER_SELECTION" = "all" ]; then + # Prepare to delete all runners + echo "" + echo "⚠️ 将删除所有 $runner_count 个 runners" + echo "" + # 继续到步骤 4 显示警告并确认 +else + # Validate selection is a number + if ! [[ "$USER_SELECTION" =~ ^[0-9]+$ ]]; then + echo "❌ 无效的选择" + rm -f /tmp/gitea_runners.txt + exit 1 + fi + + # Validate selection is in range + if [ "$USER_SELECTION" -lt 1 ] || [ "$USER_SELECTION" -gt "$runner_count" ]; then + echo "❌ 选择超出范围 (1-$runner_count)" + rm -f /tmp/gitea_runners.txt + exit 1 + fi + + # Get selected runner info + selected_data="${runner_data[$((USER_SELECTION-1))]}" + IFS='|' read -r id name <<< "$selected_data" + + echo "" + echo "已选择: $name (ID: $id)" + echo "" + # 继续到步骤 4 显示警告并确认 +fi +``` + +### 4. Display Warning and Get Confirmation + +**AI 执行**:显示删除警告,列出将要删除的 runners,然后等待用户输入 'yes' 确认。 + +```bash +echo "⚠️ 警告: 此操作将执行以下操作:" +echo " - 从 Gitea 服务器注销 runner" +echo " - 停止本地运行的 runner 进程" +echo " - 删除 runner 配置文件" +echo " - 删除 cache 和 workspace 目录" +echo " - 删除所有相关数据" +echo "" + +# List runners to be deleted +if [ "$USER_SELECTION" = "all" ]; then + echo "将删除以下 runners:" + cat /tmp/gitea_runners.txt | while IFS='|' read -r id name; do + echo " - $name (ID: $id)" + done +else + # Single runner already displayed in step 3 + : +fi + +echo "" +``` + +**AI 指导**:显示完警告后,等待用户输入确认('yes' 继续,其他取消)。 + +### 5. Execute Deletion + +**AI 执行**:如果用户确认(输入 'yes'),执行删除操作;否则取消。 + +```bash +# 假设用户确认输入存储在 $USER_CONFIRM 中 +if [ "$USER_CONFIRM" != "yes" ]; then + echo "已取消" + rm -f /tmp/gitea_runners.txt + exit 0 +fi + +source "$HOME/.config/gitea/config.env" + +echo "开始删除..." +echo "" + +success_count=0 +fail_count=0 +runners_dir="$HOME/.config/gitea/runners" + +# Process all selected runners +while IFS='|' read -r runner_id runner_name; do + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "删除: $runner_name (ID: $runner_id)" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + + # Find local runner directory by ID (处理 runners/* 不存在的情况) + runner_dir="" + if [ -d "$runners_dir" ]; then + shopt -s nullglob # 避免 * 无匹配时报错 + for dir in "$runners_dir"/*; do + if [ -f "$dir/.runner" ]; then + local_id=$(jq -r '.id // ""' "$dir/.runner" 2>/dev/null) + if [ "$local_id" = "$runner_id" ]; then + runner_dir="$dir" + break + fi + fi + done + shopt -u nullglob + fi + + # Step 1: Unregister from Gitea server + echo "[1/3] 从 Gitea 服务器注销..." + api_endpoint="${GITEA_URL}/api/v1/admin/actions/runners/${runner_id}" + http_code=$(curl -s -w "%{http_code}" -o /dev/null \ + -X DELETE \ + -H "Authorization: token $GITEA_TOKEN" \ + "$api_endpoint") + + if [ "$http_code" = "204" ]; then + echo " ✓ 已从服务器注销" + else + echo " ⚠️ 注销失败 (HTTP $http_code)" + fi + + # Step 2: Stop local process if running + echo "[2/3] 检查本地进程..." + if [ -n "$runner_dir" ] && [ -f "$runner_dir/config.yaml" ]; then + config_file="$runner_dir/config.yaml" + + if pgrep -f "act_runner daemon --config $config_file" > /dev/null 2>&1; then + pid=$(pgrep -f "act_runner daemon --config $config_file") + + # Check if runner is busy (executing jobs) + # 从服务器获取 runner 的 busy 状态 + runner_info=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "${GITEA_URL}/api/v1/admin/actions/runners/${runner_id}") + is_busy=$(echo "$runner_info" | jq -r '.busy // false') + + if [ "$is_busy" = "true" ]; then + echo " ⚠️ 警告: Runner 正在执行 job!" + echo " 强制停止可能导致 job 中断和数据不一致" + echo "" + echo " 选项:" + echo " 1. 等待 job 完成后再停止(推荐)" + echo " 2. 强制立即停止" + echo "" + # AI 应在此处等待用户选择 + # 假设用户选择存储在 $STOP_CHOICE 中 + + if [ "$STOP_CHOICE" = "1" ]; then + echo " 等待 job 完成..." + # 轮询检查 runner 是否仍然 busy + max_wait=300 # 最多等待 5 分钟 + waited=0 + while [ $waited -lt $max_wait ]; do + sleep 10 + waited=$((waited + 10)) + runner_info=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "${GITEA_URL}/api/v1/admin/actions/runners/${runner_id}") + is_busy=$(echo "$runner_info" | jq -r '.busy // false') + if [ "$is_busy" = "false" ]; then + echo " ✓ Job 已完成" + break + fi + echo " 仍在执行... (已等待 ${waited}s)" + done + + if [ $waited -ge $max_wait ]; then + echo " ⚠️ 等待超时,将强制停止" + fi + else + echo " ⚠️ 用户选择强制停止" + fi + fi + + # 优雅停止 runner + echo " 正在停止进程 (PID: $pid)..." + kill "$pid" 2>/dev/null + sleep 2 + + # 如果进程还在运行,强制杀死 + if pgrep -f "act_runner daemon --config $config_file" > /dev/null 2>&1; then + echo " 进程未响应,强制终止..." + kill -9 "$pid" 2>/dev/null + sleep 1 + fi + + # 验证进程已停止 + if pgrep -f "act_runner daemon --config $config_file" > /dev/null 2>&1; then + echo " ❌ 无法停止进程" + else + echo " ✓ 进程已停止" + fi + else + echo " ✓ 未运行" + fi + else + echo " ⓘ 未找到本地配置" + fi + + # Step 3: Delete local directory + echo "[3/3] 删除本地配置..." + if [ -n "$runner_dir" ] && [ -d "$runner_dir" ]; then + rm -rf "$runner_dir" + + if [ ! -d "$runner_dir" ]; then + echo " ✓ 本地配置已删除" + ((success_count++)) + else + echo " ❌ 删除失败" + ((fail_count++)) + fi + else + # Server runner deleted, but no local config + ((success_count++)) + echo " ⓘ 无本地配置需要删除" + fi + + echo "" +done < /tmp/gitea_runners.txt + +# Cleanup temp file +rm -f /tmp/gitea_runners.txt +``` + +### 6. Display Summary + +**AI 执行**:显示删除操作的最终结果。 + +```bash +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "删除完成" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "成功: $success_count 个" +if [ $fail_count -gt 0 ]; then + echo "失败: $fail_count 个" +fi +echo "" +echo "管理命令:" +echo " 查看剩余 runners: /gitea-list-runners" +echo " 创建新 runner: /gitea-create-runner" +echo "" +``` + +**AI 指导**:删除完成后,可以选择性地验证服务器上的 runner 数量,确认删除成功。 + +## Output Example + +### Example 1: Delete Single Runner + +``` +正在从 Gitea 服务器获取全局 runners... + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Gitea 全局 Runners (共 3 个) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 1. runner-mac-mini4 [ID: 42 ] 🟢 在线 + 系统: darwin 架构: arm64 最后在线: 2026-01-12 10:30 + + 2. runner-macbook-pro [ID: 43 ] 🔴 离线 + 系统: darwin 架构: arm64 最后在线: 2026-01-10 18:45 + + 3. runner-linux-server [ID: 44 ] 🟢 在线 + 系统: linux 架构: amd64 最后在线: 2026-01-12 10:25 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +选择要删除的 Runner +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 输入序号: 删除单个 runner + 输入 'all': 删除所有 runners + 输入 'q' 或 'quit': 取消 + +请选择: 2 + +已选择: runner-macbook-pro (ID: 43) + +⚠️ 警告: 此操作将执行以下操作: + - 从 Gitea 服务器注销 runner + - 停止本地运行的 runner 进程 + - 删除 runner 配置文件 + - 删除 cache 和 workspace 目录 + - 删除所有相关数据 + +确认删除? 输入 'yes' 继续: yes + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +删除: runner-macbook-pro (ID: 43) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +[1/3] 从 Gitea 服务器注销... + ✓ 已从服务器注销 +[2/3] 检查本地进程... + ✓ 未运行 +[3/3] 删除本地配置... + ✓ 本地配置已删除 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +删除完成 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +成功: 1 个 + +管理命令: + 查看剩余 runners: /gitea-list-runners + 创建新 runner: /gitea-create-runner +``` + +### Example 2: Delete All Runners + +``` +正在从 Gitea 服务器获取全局 runners... + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Gitea 全局 Runners (共 2 个) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 1. runner-mac-mini4 [ID: 42 ] 🟢 在线 + 系统: darwin 架构: arm64 最后在线: 2026-01-12 10:30 + + 2. runner-linux-server [ID: 44 ] 🟢 在线 + 系统: linux 架构: amd64 最后在线: 2026-01-12 10:25 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +选择要删除的 Runner +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 输入序号: 删除单个 runner + 输入 'all': 删除所有 runners + 输入 'q' 或 'quit': 取消 + +请选择: all + +⚠️ 将删除所有 2 个 runners + +⚠️ 警告: 此操作将执行以下操作: + - 从 Gitea 服务器注销 runner + - 停止本地运行的 runner 进程 + - 删除 runner 配置文件 + - 删除 cache 和 workspace 目录 + - 删除所有相关数据 + +将删除 2 个 runners: + - runner-mac-mini4 (ID: 42) + - runner-linux-server (ID: 44) + +确认删除? 输入 'yes' 继续: yes + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +删除: runner-mac-mini4 (ID: 42) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +[1/3] 从 Gitea 服务器注销... + ✓ 已从服务器注销 +[2/3] 检查本地进程... + 正在停止进程 (PID: 12345)... + ✓ 进程已停止 +[3/3] 删除本地配置... + ✓ 本地配置已删除 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +删除: runner-linux-server (ID: 44) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +[1/3] 从 Gitea 服务器注销... + ✓ 已从服务器注销 +[2/3] 检查本地进程... + ⓘ 未找到本地配置 +[3/3] 删除本地配置... + ⓘ 无本地配置需要删除 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +删除完成 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +成功: 2 个 + +管理命令: + 查看剩余 runners: /gitea-list-runners + 创建新 runner: /gitea-create-runner +``` + +## Safety Features + +- **服务器数据优先**:直接从 Gitea 服务器获取 runner 列表,确保准确性 +- **交互式选择**:支持选择单个或所有 runners,避免误删 +- **双重确认**:需要输入 'yes' 进行最终确认(防止误删) +- **Busy 状态检测**:检查 runner 是否正在执行 job + - 如果 runner 正在执行 job,给用户选择: + - 等待 job 完成后停止(推荐) + - 强制立即停止(有风险) +- **优雅停止**:先尝试 SIGTERM,2 秒后才使用 SIGKILL +- **服务器注销**:自动从 Gitea 服务器注销 runner +- **三步删除流程**: + 1. 从服务器注销 + 2. 停止本地进程(检测 busy 状态) + 3. 删除本地配置 +- **清晰提示**:显示每个步骤的执行状态和警告信息 +- **批量删除支持**:可一次性删除所有 runners +- **错误处理**:各步骤独立执行,部分失败不影响其他操作 +- **超时保护**:等待 job 完成最多 5 分钟,超时后强制停止 + +## Technical Notes + +- **必需工具**:需要安装 `jq` 工具(`brew install jq`) +- **权限要求**:需要 Gitea admin 权限(全局 runner 管理) +- **配置文件**:读取 `~/.config/gitea/config.env` 获取 API 配置 +- **本地匹配**:通过 runner ID 匹配本地配置目录 +- **服务器同步**:从服务器注销后再删除本地配置 +- **无本地配置**:如果只存在服务器记录,仅注销服务器端 +- **取消操作**:输入 'q' 或 'quit' 可随时取消 +- **临时文件**:使用 `/tmp/gitea_runners.txt` 存储临时数据,操作完成后自动清理 +- **API 格式**:Gitea API 返回格式为 `{"runners": [...], "total_count": N}`,不是直接的数组 +- **Busy 检测**:通过 API 的 `busy` 字段判断 runner 是否正在执行 job +- **停止信号**: + - `kill $pid` (SIGTERM): 优雅停止,给进程清理资源的机会 + - `kill -9 $pid` (SIGKILL): 强制终止,无法被捕获或忽略 +- **等待策略**:如果 runner busy,最多等待 5 分钟(300 秒),每 10 秒检查一次状态 + +## AI 执行流程总结 + +当用户调用 `/gitea-delete-runner` 时,AI 应该按以下流程执行: + +1. **步骤 1**:加载并验证 Gitea 配置 +2. **步骤 2**:获取并展示 runners 列表 → **等待用户选择** +3. **步骤 3**:处理用户选择,验证输入 +4. **步骤 4**:显示删除警告和列表 → **等待用户确认** +5. **步骤 5**:执行删除操作 + - 5.1: 从服务器注销 + - 5.2: 检查并停止本地进程 + - **如果 runner 正在执行 job(busy)** → **等待用户选择**(等待完成 or 强制停止) + - 5.3: 删除本地配置 +6. **步骤 6**:显示操作结果摘要 + +**重要**:这是一个交互式命令,可能需要在以下位置等待用户输入: +- 步骤 2: 选择要删除的 runner +- 步骤 4: 确认删除操作 +- 步骤 5.2: 如果 runner 正在执行 job,选择等待或强制停止 + +不要一次性执行所有步骤。 diff --git a/command/gitea-list-runners.md b/command/gitea-list-runners.md new file mode 100644 index 0000000..3ad56e7 --- /dev/null +++ b/command/gitea-list-runners.md @@ -0,0 +1,202 @@ +--- +description: List all configured Gitea runners and their status +--- + +# gitea-list-runners + +列出所有已配置的 Gitea Runners 及其运行状态。 + +## Features + +- 显示所有已配置的 runner +- 检查 runner 运行状态(运行中/已停止) +- 显示 runner 配置信息(labels、capacity 等) +- 显示 runner ID 和名称 +- 提供启动命令 + +## Steps + +### 1. Check Configuration + +```bash +config_dir="$HOME/.config/gitea" +runners_dir="$config_dir/runners" + +if [ ! -d "$runners_dir" ]; then + echo "❌ 未找到 runner 目录" + echo "请先创建 runner" + exit 1 +fi +``` + +### 2. List All Runners + +```bash +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "Gitea Runners" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +runners=$(ls -1 "$runners_dir" 2>/dev/null) + +if [ -z "$runners" ]; then + echo "未配置任何 runner" + echo "" + echo "创建 runner:" + echo " /gitea-create-runner" + exit 0 +fi + +runner_count=$(echo "$runners" | wc -l | tr -d ' ') +echo "总计: $runner_count 个 runner" +echo "" +``` + +### 3. Display Each Runner's Status + +```bash +for runner in $runners; do + runner_dir="$runners_dir/$runner" + config_file="$runner_dir/config.yaml" + + echo "[$runner]" + + # Check if config exists + if [ ! -f "$config_file" ]; then + echo " ⚠️ 配置文件缺失" + echo "" + continue + fi + + # Check if runner process is running + if pgrep -f "act_runner daemon --config $config_file" > /dev/null; then + status="🟢 运行中" + pid=$(pgrep -f "act_runner daemon --config $config_file") + else + status="🔴 已停止" + pid="-" + fi + + echo " 状态: $status" + echo " PID: $pid" + + # Display configuration info + if command -v yq &> /dev/null; then + # Use yq if available + capacity=$(yq eval '.runner.capacity' "$config_file" 2>/dev/null) + timeout=$(yq eval '.runner.timeout' "$config_file" 2>/dev/null) + else + # Fallback to grep + capacity=$(grep "capacity:" "$config_file" | awk '{print $2}') + timeout=$(grep "timeout:" "$config_file" | awk '{print $2}') + fi + + echo " 容量: ${capacity:-N/A}" + echo " 超时: ${timeout:-N/A}" + + # Display labels + labels=$(grep -A 10 "labels:" "$config_file" | grep "^ -" | sed 's/^ - "//' | sed 's/"$//' | tr '\n' ',' | sed 's/,$//') + if [ -n "$labels" ]; then + echo " Labels: $labels" + fi + + # Display runner info from .runner file + if [ -f "$runner_dir/.runner" ]; then + if command -v jq &> /dev/null; then + runner_id=$(jq -r '.id // "N/A"' "$runner_dir/.runner" 2>/dev/null) + runner_name=$(jq -r '.name // "N/A"' "$runner_dir/.runner" 2>/dev/null) + + echo " ID: $runner_id" + echo " 名称: $runner_name" + fi + fi + + echo " 路径: $runner_dir" + + # Display start command + echo "" + echo " 启动命令:" + echo " act_runner daemon --config $config_file" + + # Display background start command + if [ "$status" = "🔴 已停止" ]; then + echo "" + echo " 后台启动:" + echo " nohup act_runner daemon --config $config_file > $runner_dir/runner.log 2>&1 &" + fi + + echo "" +done +``` + +### 4. Display Summary Commands + +```bash +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "管理命令" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " 查看配置: /gitea-config" +echo " 创建 runner: /gitea-create-runner" +echo " 删除 runner: /gitea-delete-runner" +echo "" +``` + +## Output Example + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Gitea Runners +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +总计: 2 个 runner + +[runner-macbook-pro] + 状态: 🟢 运行中 + PID: 12345 + 容量: 2 + 超时: 3h + Labels: self-hosted:host,macOS:host,ARM64:host,darwin-arm64:host + ID: 42 + 名称: runner-macbook-pro + 路径: /Users/voson/.config/gitea/runners/runner-macbook-pro + + 启动命令: + act_runner daemon --config /Users/voson/.config/gitea/runners/runner-macbook-pro/config.yaml + +[runner-mac-mini] + 状态: 🔴 已停止 + PID: - + 容量: 2 + 超时: 3h + Labels: self-hosted:host,macOS:host,ARM64:host,darwin-arm64:host + ID: 43 + 名称: runner-mac-mini + 路径: /Users/voson/.config/gitea/runners/runner-mac-mini + + 启动命令: + act_runner daemon --config /Users/voson/.config/gitea/runners/runner-mac-mini/config.yaml + + 后台启动: + nohup act_runner daemon --config /Users/voson/.config/gitea/runners/runner-mac-mini/config.yaml > /Users/voson/.config/gitea/runners/runner-mac-mini/runner.log 2>&1 & + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +管理命令 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 查看配置: /gitea-config + 创建 runner: /gitea-create-runner + 删除 runner: /gitea-delete-runner +``` + +## Notes + +- 使用 `pgrep` 检查进程状态 +- 优先使用 `yq` 解析 YAML,fallback 到 `grep` +- 优先使用 `jq` 解析 JSON `.runner` 文件 +- Runner 状态图标: + - 🟢 运行中 + - 🔴 已停止 + - ⚠️ 配置异常 +- 显示启动命令方便用户复制执行 +- 对已停止的 runner,额外显示后台启动命令 diff --git a/command/gitea-reset.md b/command/gitea-reset.md new file mode 100644 index 0000000..825e8b6 --- /dev/null +++ b/command/gitea-reset.md @@ -0,0 +1,314 @@ +--- +description: Reset Gitea configuration with interactive setup wizard +--- + +# gitea-reset + +重置 Gitea 配置,启动交互式配置向导。 + +## Features + +- 交互式引导配置 +- 验证 URL 和 Token +- 检查 Token 权限 +- 自动保存配置到 `~/.config/gitea/config.env` +- 创建必要的目录结构 + +## Steps + +### 1. Create Configuration Directory + +```bash +config_dir="$HOME/.config/gitea" +config_file="$config_dir/config.env" + +# Create directories +mkdir -p "$config_dir/runners" + +echo "开始 Gitea 配置向导..." +echo "" +``` + +### 2. Input Gitea URL + +```bash +read -p "请输入 Gitea 实例地址 (例如: https://git.digitevents.com): " gitea_url + +# Validate URL format +if ! [[ "$gitea_url" =~ ^https?:// ]]; then + echo "❌ URL 必须以 http:// 或 https:// 开头" + exit 1 +fi + +# Remove trailing slash +gitea_url="${gitea_url%/}" + +echo "✓ URL: $gitea_url" +``` + +### 3. Input Personal Access Token + +```bash +echo "" +read -sp "请输入 Personal Access Token: " gitea_token +echo "" + +if [ -z "$gitea_token" ]; then + echo "❌ Token 不能为空" + exit 1 +fi + +echo "✓ Token 已输入" +``` + +**Token 获取提示**: + +在用户输入 Token 前,可以显示帮助信息: + +``` +提示:获取 Personal Access Token 的步骤: + 1. 登录 Gitea + 2. 右上角头像 → 设置 → 应用 → 访问令牌 + 3. 点击 "生成新令牌" + 4. 设置令牌名称(如 opencode-cli) + 5. 选择权限:repo, admin:org, write:runner(推荐) + 6. 点击 "生成令牌" + 7. 复制生成的 Token +``` + +### 4. Test Connection + +```bash +echo "" +echo "正在测试连接..." + +response=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $gitea_token" \ + "${gitea_url}/api/v1/user") + +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | sed '$d') + +if [ "$http_code" != "200" ]; then + echo "❌ 连接失败 (HTTP $http_code)" + echo "请检查:" + echo " - URL 是否正确" + echo " - Token 是否有效" + echo " - 网络连接是否正常" + exit 1 +fi + +username=$(echo "$body" | jq -r '.login') +echo "✓ 连接成功!" +echo "✓ 登录用户: $username" +``` + +### 5. Validate Token Permissions + +```bash +echo "" +echo "正在检查 Token 权限..." + +# Check repo permission +if curl -s -o /dev/null -w "%{http_code}" \ + -H "Authorization: token $gitea_token" \ + "${gitea_url}/api/v1/user/repos" | grep -q "200"; then + echo " ✓ repo (仓库管理)" + has_repo=true +else + echo " ✗ repo (仓库管理) - 缺少" + has_repo=false +fi + +# Check org permission +if curl -s -o /dev/null -w "%{http_code}" \ + -H "Authorization: token $gitea_token" \ + "${gitea_url}/api/v1/user/orgs" | grep -q "200"; then + echo " ✓ admin:org (组织管理)" + has_org=true +else + echo " ⚠ admin:org (组织管理) - 缺少(创建组织 Runner 时需要)" + has_org=false +fi + +# Check runner permission (try to get a registration token) +if curl -s -o /dev/null -w "%{http_code}" \ + -H "Authorization: token $gitea_token" \ + "${gitea_url}/api/v1/user/actions/runners/registration-token" 2>/dev/null | grep -q "200"; then + echo " ✓ write:runner (Runner 管理)" + has_runner=true +else + echo " ⚠ write:runner (Runner 管理) - 缺少(创建 Runner 时需要)" + has_runner=false +fi + +# Warning if missing critical permissions +if [ "$has_repo" = false ]; then + echo "" + echo "❌ 缺少必需的 repo 权限" + read -p "是否继续? [y/N] " continue_anyway + if [[ ! "$continue_anyway" =~ ^[Yy]$ ]]; then + echo "已取消,请重新创建具有足够权限的 Token" + exit 1 + fi +fi +``` + +### 6. Input Default Organization (Optional) + +```bash +echo "" +echo "设置默认组织(可选):" +echo " - 创建仓库时,如果不指定 owner,将使用默认组织" +echo " - 创建组织级 Runner 时使用" +echo "" +read -p "请输入默认组织名称 (回车跳过): " default_org + +if [ -n "$default_org" ]; then + # Validate organization exists + echo "正在验证组织..." + org_response=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $gitea_token" \ + "${gitea_url}/api/v1/orgs/${default_org}") + + org_http_code=$(echo "$org_response" | tail -n1) + + if [ "$org_http_code" = "200" ]; then + echo "✓ 组织验证成功: $default_org" + else + echo "⚠️ 组织 '$default_org' 不存在或无权限访问" + read -p "仍然设置为默认组织? [Y/n] " set_anyway + if [[ "$set_anyway" =~ ^[Nn]$ ]]; then + default_org="" + fi + fi +fi +``` + +### 7. Save Configuration + +```bash +echo "" +echo "正在保存配置..." + +cat > "$config_file" << EOF +# Gitea Configuration +# Generated at $(date '+%Y-%m-%d %H:%M:%S') + +GITEA_URL=$gitea_url +GITEA_TOKEN=$gitea_token +${default_org:+GITEA_DEFAULT_ORG=$default_org} + +# Runner Default Settings +GITEA_RUNNER_CAPACITY=2 +GITEA_RUNNER_TIMEOUT=3h + +# Optional: Override auto-detected labels +# GITEA_RUNNER_LABELS=custom-label-1:host,custom-label-2:host +EOF + +# Set restrictive permissions +chmod 600 "$config_file" + +# Create .gitignore +cat > "$config_dir/.gitignore" << EOF +config.env +runners/*/.runner +runners/*/.env +EOF + +echo "✓ 配置已保存" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "配置完成" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " 配置文件: $config_file" +echo " Runner 目录: $config_dir/runners" +echo "" +echo "下一步:" +echo " - 查看配置: /gitea-config" +echo " - 创建 Runner: 告诉 AI '创建一个 runner'" +echo " - 创建仓库: /create-gitea-repo " +echo "" +``` + +## Configuration File Format + +生成的 `~/.config/gitea/config.env` 文件格式: + +```bash +# Gitea Configuration +# Generated at 2026-01-12 22:00:00 + +GITEA_URL=https://git.digitevents.com +GITEA_TOKEN=git_xxxxxxxxxxxxxxxxxxxx +GITEA_DEFAULT_ORG=ai + +# Runner Default Settings +GITEA_RUNNER_CAPACITY=2 +GITEA_RUNNER_TIMEOUT=3h + +# Optional: Override auto-detected labels +# GITEA_RUNNER_LABELS=custom-label-1:host,custom-label-2:host +``` + +## Output Example + +``` +开始 Gitea 配置向导... + +请输入 Gitea 实例地址 (例如: https://git.digitevents.com): https://git.digitevents.com +✓ URL: https://git.digitevents.com + +请输入 Personal Access Token: **************** +✓ Token 已输入 + +正在测试连接... +✓ 连接成功! +✓ 登录用户: your_username + +正在检查 Token 权限... + ✓ repo (仓库管理) + ✓ admin:org (组织管理) + ✓ write:runner (Runner 管理) + +设置默认组织(可选): + - 创建仓库时,如果不指定 owner,将使用默认组织 + - 创建组织级 Runner 时使用 + +请输入默认组织名称 (回车跳过): ai +正在验证组织... +✓ 组织验证成功: ai + +正在保存配置... +✓ 配置已保存 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +配置完成 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 配置文件: /Users/voson/.config/gitea/config.env + Runner 目录: /Users/voson/.config/gitea/runners + +下一步: + - 查看配置: /gitea-config + - 创建 Runner: 告诉 AI '创建一个 runner' + - 创建仓库: /create-gitea-repo +``` + +## Security Notes + +- 配置文件权限设置为 `600`(仅所有者可读写) +- Token 不会在日志中显示 +- 创建 `.gitignore` 文件排除敏感信息 +- 建议定期轮换 Token(每 3-6 个月) + +## Notes + +- **必需权限**: `repo` - 创建和管理仓库 +- **推荐权限**: `admin:org` - 创建组织级 Runner +- **推荐权限**: `write:runner` - 管理 Runner +- **可选权限**: `admin:repo_hook` - 配置 Webhooks +- Token 只显示一次,请妥善保管 +- 重置配置不会影响已创建的 Runner,但 Runner 会继续使用旧的注册信息 diff --git a/command/gitea-switch-org.md b/command/gitea-switch-org.md new file mode 100644 index 0000000..e6d0283 --- /dev/null +++ b/command/gitea-switch-org.md @@ -0,0 +1,155 @@ +--- +description: Switch default Gitea organization +--- + +# gitea-switch-org + +切换默认 Gitea 组织。 + +## Features + +- 更新配置文件中的默认组织 +- 验证组织是否存在 +- 立即生效,无需重启 + +## User Input Format + +``` + +``` + +**Examples**: +- `ai` - Switch to 'ai' organization +- `my-team` - Switch to 'my-team' organization + +## Steps + +### 1. Check Configuration + +```bash +config_file="$HOME/.config/gitea/config.env" + +if [ ! -f "$config_file" ]; then + echo "❌ Gitea 未配置" + echo "请先运行 /gitea-reset 进行配置" + exit 1 +fi + +source "$config_file" +``` + +### 2. Parse User Input + +```bash +new_org="$1" + +if [ -z "$new_org" ]; then + echo "用法: /gitea-switch-org " + echo "" + echo "示例:" + echo " /gitea-switch-org ai" + echo " /gitea-switch-org my-team" + exit 1 +fi +``` + +### 3. Validate Organization Exists + +```bash +echo "正在验证组织 '$new_org'..." + +response=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $GITEA_TOKEN" \ + "${GITEA_URL}/api/v1/orgs/${new_org}") + +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | sed '$d') + +if [ "$http_code" != "200" ]; then + echo "❌ 组织 '$new_org' 不存在或无权限访问" + + # Suggest available organizations + echo "" + echo "你可以访问的组织:" + curl -s -H "Authorization: token $GITEA_TOKEN" \ + "${GITEA_URL}/api/v1/user/orgs" | jq -r '.[].username' | sed 's/^/ - /' + + exit 1 +fi + +org_name=$(echo "$body" | jq -r '.full_name // .username') +echo "✓ 组织验证成功: $org_name" +``` + +### 4. Update Configuration File + +```bash +echo "正在更新配置..." + +# Check if GITEA_DEFAULT_ORG already exists in config +if grep -q "^GITEA_DEFAULT_ORG=" "$config_file"; then + # Update existing line (macOS compatible) + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' "s/^GITEA_DEFAULT_ORG=.*/GITEA_DEFAULT_ORG=$new_org/" "$config_file" + else + sed -i "s/^GITEA_DEFAULT_ORG=.*/GITEA_DEFAULT_ORG=$new_org/" "$config_file" + fi +else + # Add new line after GITEA_TOKEN + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' "/^GITEA_TOKEN=/a\\ +GITEA_DEFAULT_ORG=$new_org +" "$config_file" + else + sed -i "/^GITEA_TOKEN=/a GITEA_DEFAULT_ORG=$new_org" "$config_file" + fi +fi + +echo "✓ 默认组织已切换到: $new_org" +``` + +### 5. Display Summary + +```bash +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "切换完成" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " 新的默认组织: $new_org" +echo "" +echo "现在可以:" +echo " - 创建仓库: /create-gitea-repo my-project" +echo " (将创建到 $new_org/my-project)" +echo " - 查看配置: /gitea-config" +echo "" +``` + +## Output Example + +``` +正在验证组织 'ai'... +✓ 组织验证成功: AI Team + +正在更新配置... +✓ 默认组织已切换到: ai + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +切换完成 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + 新的默认组织: ai + +现在可以: + - 创建仓库: /create-gitea-repo my-project + (将创建到 ai/my-project) + - 查看配置: /gitea-config +``` + +## Notes + +- 切换组织不会影响已创建的仓库和 Runner +- 仅影响后续创建仓库时的默认 owner +- 可以随时切换到其他组织 +- 使用 `jq` 解析 JSON 响应 +- macOS 和 Linux 的 `sed` 命令略有不同,需兼容处理 diff --git a/command/sync-oc-pull.md b/command/sync-oc-pull.md deleted file mode 100644 index acfd30c..0000000 --- a/command/sync-oc-pull.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -description: Pull latest OpenCode config changes from remote -agent: build ---- - -# sync-oc-pull - -Pull latest changes for OpenCode configuration from remote repository. - -## Use Cases - -When you updated OpenCode configuration on another device, or team members shared new commands/configurations, use this command to sync to local. - -## Steps - -### 1. Switch to OpenCode Config Directory - -```bash -cd ~/.config/opencode -``` - -### 2. Check Local Status - -Run `git status` to check if there are uncommitted local changes. - -**If there are uncommitted changes**: -- List changed files -- Ask user how to handle: - - **Stash**: `git stash` to save local changes, restore after pull - - **Discard**: `git checkout -- .` to discard local changes - - **Cancel**: Terminate operation, let user handle manually - -### 3. Pull Remote Changes - -```bash -git pull origin main -``` - -### 4. Handle Conflicts (if any) - -If conflicts occur during pull: - -1. List conflicting files -2. Open conflict files, analyze conflict content -3. Ask user to choose: - - **Keep local version**: Use `git checkout --ours ` - - **Use remote version**: Use `git checkout --theirs ` - - **Manual merge**: Prompt user to manually edit then execute `git add ` - -4. After resolving all conflicts, complete merge: - -```bash -git add . -git commit -m "chore: resolve merge conflicts" -``` - -### 5. Restore Stashed Changes (if any) - -If `git stash` was used in step 2: - -```bash -git stash pop -``` - -If conflicts occur during restore, handle according to step 4. - -### 6. Show Update Summary - -After pull completes, show update content: - -```bash -git log --oneline -5 -``` - -List newly added or modified command files to help user understand what new configurations are available. - -## Notes - -- **Backup important configs**: If there are local modifications before pull, suggest backing up or using `git stash` first. -- **Conflict handling**: Config file conflicts usually prioritize keeping local version, unless explicitly needing remote's new config. -- **Verify config**: After pull, suggest checking if `opencode.json` and other config files work correctly. diff --git a/command/sync-oc-push.md b/command/sync-oc-push.md deleted file mode 100644 index dc741fb..0000000 --- a/command/sync-oc-push.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -description: Commit and push OpenCode config changes to remote -agent: build ---- - -# sync-oc-push - -Commit and push OpenCode configuration repository changes to remote repository. - -## Use Cases - -When you modified config files in `~/.config/opencode` directory (such as `command/`, `skill/`, `opencode.json`, etc.), use this command to sync changes to remote repository. - -## Steps - -### 1. Switch to OpenCode Config Directory - -```bash -cd ~/.config/opencode -``` - -### 2. Check Change Status - -Run `git status` to view current changes. - -**If there are no changes**: -- Output prompt: "No changes to commit." -- **Terminate command execution** - -### 3. Collect Information (Execute in parallel) - -- Run `git diff` to view unstaged changes -- Run `git diff --cached` to view staged changes -- Run `git log --oneline -5` to view recent commit history - -### 4. Add Changes to Staging Area - -Add all relevant config files to staging area: - -```bash -git add command/ skill/ opencode.json -``` - -> Only add config files that need to be synced, ignore other local files. - -### 5. Generate Commit Message and Commit - -Generate concise commit message based on change content: - -- Use [Conventional Commits](https://www.conventionalcommits.org/) specification -- **Language selection**: - - **Default (macOS/Linux)**: Use Chinese (中文) for commit messages - - **Windows**: Use English due to encoding issues with Cursor Shell tool -- Common types: - - `feat`: New command or config - - `fix`: Fix command or config issues - - `docs`: Documentation update - - `chore`: Miscellaneous adjustments - -**Examples (macOS/Linux - Chinese)**: - -```bash -git commit -m "feat: 添加 Vue.js 开发命令" -git commit -m "fix: 修正 MCP 服务器配置" -git commit -m "docs: 更新 review 命令说明" -``` - -**Examples (Windows - English)**: - -```bash -git commit -m "feat: add new developer command for Vue.js" -git commit -m "fix: correct MCP server configuration" -git commit -m "docs: update review command instructions" -``` - -### 6. Push to Remote Repository - -```bash -git push origin main -``` - -## Notes - -- **Only sync config files**: Only add `command/`, `skill/` and `opencode.json`, don't commit other local data. -- **Sensitive info**: `opencode.json` may contain API keys, ensure remote repository access permissions are set correctly. -- **Commit message language**: Default use Chinese (macOS/Linux); Windows must use English due to Cursor Shell tool encoding issues. diff --git a/skill/git/SKILL.md b/skill/git/SKILL.md new file mode 100644 index 0000000..9045eeb --- /dev/null +++ b/skill/git/SKILL.md @@ -0,0 +1,565 @@ +--- +name: git +description: Git workflow best practices with commit conventions, tagging, and common operations for both polyrepo and monorepo +--- + +# Git Workflow Best Practices + +You are an expert in Git version control and repository management. + +## Core Principles + +1. **Default Main Branch**: Use `main` as the primary branch (not `master`) +2. **Conventional Commits**: Follow [Conventional Commits](https://www.conventionalcommits.org/) specification +3. **Semantic Versioning**: Use `major.minor.patch` format (e.g., `0.1.0`, `1.2.3`) +4. **Repository Types**: Support both polyrepo and monorepo workflows +5. **Clean History**: Maintain meaningful commit history and proper tagging + +## Repository Types + +### Polyrepo (Single Repository) + +- Each project has its own repository +- Tag format: `` (e.g., `1.2.0`) +- Version files at project root +- Simpler workflow for independent projects + +### Monorepo (Multiple Subprojects) + +- Multiple projects in a single repository +- Detect from `AGENTS.md` file or directory structure (e.g., `packages/`, `apps/`) +- Tag format: `-` (e.g., `ios-1.2.0`, `android-0.3.1`) +- Commit scope includes subproject name when changes affect single project +- Each subproject maintains independent versioning + +**Monorepo Detection Rules:** +- Check if `AGENTS.md` explicitly indicates monorepo +- Look for common monorepo directory structures: + - `packages/`, `apps/`, `services/`, `modules/`, `projects/` +- If uncertain, default to polyrepo + +## Commit Message Convention + +### Format + +``` +(): + + + +