docs: 更新命令文档和发布脚本

- 更新 release-android.md: 明确前置条件和通用项目支持
- 更新 auto-commit.md: 添加中英文 commit 信息平台选择规则
- 更新 commit-push.md: 添加中英文 commit 信息平台选择规则
- 更新 sync-oc-push.md: 添加中英文 commit 信息平台选择规则
- 更新 release-android.mjs: 支持从 tags 自动推断项目名,不再硬编码 android 前缀
- 更新 AGENTS.md: 补充中文交流规则的注意事项
This commit is contained in:
2026-01-09 16:54:56 +08:00
parent 0e161b3088
commit dce31f9729
6 changed files with 292 additions and 115 deletions

View File

@@ -9,4 +9,21 @@
- 用户明确要求使用其他语言
- 技术术语在中文中没有合适的翻译(可以中英文混用,如 "React Hooks"
这是用户的个人偏好设置,适用于所有项目。
### 重要注意事项
1. **不要因为文档、命令或代码是英文就切换语言**
- 即使项目文档是英文的,回复时仍然用中文解释
- 即使执行的命令是英文的,回复时仍然用中文说明
- 即使错误信息是英文的,回复时仍然用中文分析
2. **工具输出可以保持原语言**
- 代码本身使用英文(函数名、变量名等)
- 终端命令使用英文
- 但你对这些内容的解释和说明必须用中文
3. **用户交互始终用中文**
- 所有对话、提问、确认都用中文
- 所有建议、总结、分析都用中文
- 所有错误解释和问题诊断都用中文
这是用户的个人偏好设置,适用于所有项目和所有交互场景。

View File

@@ -17,9 +17,14 @@
* - GITEA_REPO (optional): Override repository name
*
* Usage:
* node release-android.mjs [android-dir]
* node release-android.mjs [--project <name>] [android-dir]
*
* If android-dir is not specified, will auto-detect from current directory.
* --project <name> Specify project name (e.g., admin-tool, android)
* android-dir Android project directory (optional, auto-detect if not specified)
*
* Examples:
* node release-android.mjs --project admin-tool
* node release-android.mjs --project android ./android
*/
import fs from 'node:fs';
@@ -179,11 +184,76 @@ function readVersion(androidDir) {
}
/**
* Get tag name based on project structure
* Infer project name from Android directory name and git tags
* Returns the prefix used in tags (e.g., "myapp", "android", or "")
*/
function getTagName(version, isMonorepoProject) {
// Monorepo uses prefixed tag, standalone uses v-prefix
return isMonorepoProject ? `android-${version}` : `v${version}`;
function inferProjectName(gitRoot, androidDir) {
try {
// First, try to infer from Android directory name
const dirName = path.basename(androidDir);
// Get all tags with version pattern
const tags = execSync('git tag -l --sort=-version:refname', {
cwd: gitRoot,
encoding: 'utf-8',
shell: true,
stdio: ['pipe', 'pipe', 'pipe']
}).trim().split('\n').filter(Boolean);
if (tags.length === 0) {
return null;
}
// If directory name matches any tag prefix, prefer that
// For example: if dir is "admin-tool", look for "admin-tool-*" tags
const dirPrefixTags = tags.filter(tag => tag.startsWith(`${dirName}-`));
if (dirPrefixTags.length > 0) {
return dirName;
}
// If directory is "android" or "app", try "android" prefix
if (dirName === 'android' || dirName === 'app') {
const androidTags = tags.filter(tag => tag.startsWith('android-'));
if (androidTags.length > 0) {
return 'android';
}
}
// Fallback: try to extract prefix from recent tags
// Patterns: "myapp-1.0.0", "android-1.0.0", "v1.0.0"
for (const tag of tags.slice(0, 10)) { // Check recent 10 tags
const match = tag.match(/^(.+?)-\d+\.\d+/);
if (match) {
return match[1]; // Return prefix like "myapp", "android"
}
if (tag.match(/^v\d+\.\d+/)) {
return ''; // v-prefix style (no project name)
}
}
return null;
} catch {
return null;
}
}
/**
* Get latest tag that matches the pattern
*/
function getLatestTag(gitRoot, projectPrefix) {
try {
const pattern = projectPrefix ? `${projectPrefix}-*` : 'v*';
const tag = execSync(`git tag -l '${pattern}' --sort=-version:refname | head -1`, {
cwd: gitRoot,
encoding: 'utf-8',
shell: true,
stdio: ['pipe', 'pipe', 'pipe']
}).trim();
return tag || null;
} catch {
return null;
}
}
/**
@@ -236,7 +306,7 @@ function getTagInfo(tagName, gitRoot) {
* Build Android APK
*/
function buildApk(androidDir, javaHome) {
console.log('Building APK...');
console.log('正在构建 APK...');
execSync('./gradlew assembleRelease --quiet', {
cwd: androidDir,
stdio: 'inherit',
@@ -290,12 +360,12 @@ function uploadToGitea(config) {
const existingRelease = releases.find((r) => r.tag_name === tagName);
if (existingRelease) {
releaseId = existingRelease.id;
console.log(`Found existing Release (ID: ${releaseId})`);
console.log(`找到已存在的 Release (ID: ${releaseId})`);
// Delete existing asset with same name
const existingAsset = (existingRelease.assets || []).find((a) => a.name === fileName);
if (existingAsset) {
console.log(`Deleting existing asset: ${fileName}`);
console.log(`删除已存在的文件: ${fileName}`);
execSync(
`curl -s -X DELETE -H "Authorization: token ${token}" "${repoApiBase}/releases/${releaseId}/assets/${existingAsset.id}"`,
{ shell: true, stdio: ['pipe', 'pipe', 'pipe'] }
@@ -305,7 +375,7 @@ function uploadToGitea(config) {
}
if (!releaseId) {
console.log('Creating new Release...');
console.log('创建新 Release...');
const releaseData = {
tag_name: tagName,
name: `Android APK ${tagName}`,
@@ -331,11 +401,11 @@ function uploadToGitea(config) {
}
releaseId = releaseInfo.id;
console.log(`Release created (ID: ${releaseId})`);
console.log(`Release 已创建 (ID: ${releaseId})`);
}
// Upload APK
console.log('Uploading APK...');
console.log('上传 APK...');
const uploadUrl = `${repoApiBase}/releases/${releaseId}/assets?name=${encodeURIComponent(fileName)}`;
const uploadResult = curlJson(
`curl -s -X POST "${uploadUrl}" \
@@ -357,12 +427,33 @@ function uploadToGitea(config) {
// Main
// ============================================================================
/**
* Parse command line arguments
*/
function parseArgs() {
const args = process.argv.slice(2);
let projectName = null;
let androidDir = null;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '--project' && i + 1 < args.length) {
projectName = args[i + 1];
i++; // skip next arg
} else if (!arg.startsWith('--')) {
androidDir = arg;
}
}
return { projectName, androidDir };
}
function main() {
const cwd = process.cwd();
const argDir = process.argv[2];
const { projectName, androidDir: argDir } = parseArgs();
console.log('='.repeat(60));
console.log('Android Release Script');
console.log('Android 发布脚本');
console.log('='.repeat(60));
// 1. Find Android project root
@@ -370,23 +461,23 @@ function main() {
const androidDir = findAndroidRoot(searchDir);
if (!androidDir) {
console.error('Error: Cannot find Android project');
console.error('Make sure you are in an Android project directory or specify the path');
console.error('Usage: release-android [android-dir]');
console.error('错误:找不到 Android 项目');
console.error('请确保当前目录是 Android 项目或指定正确的路径');
console.error('用法: release-android [android-dir]');
process.exit(1);
}
console.log(`Android project: ${androidDir}`);
console.log(`Android 项目: ${androidDir}`);
// 2. Find git root
const gitRoot = findGitRoot(androidDir);
if (!gitRoot) {
console.error('Error: Not a git repository');
console.error('错误:不是 git 仓库');
process.exit(1);
}
console.log(`Git root: ${gitRoot}`);
console.log(`Git 根目录: ${gitRoot}`);
const monorepo = isMonorepo(androidDir, gitRoot);
console.log(`Project type: ${monorepo ? 'Monorepo' : 'Standalone'}`);
console.log(`项目类型: ${monorepo ? 'Monorepo' : '独立项目'}`);
// 3. Detect Gitea configuration
const detected = detectGiteaConfig(gitRoot);
@@ -396,14 +487,14 @@ function main() {
const GITEA_REPO = process.env.GITEA_REPO || detected.repo || '';
if (!GITEA_TOKEN) {
console.error('Error: GITEA_TOKEN environment variable is not set');
console.error('Please set: export GITEA_TOKEN="your_token"');
console.error('错误:未设置 GITEA_TOKEN 环境变量');
console.error('请设置: export GITEA_TOKEN="your_token"');
process.exit(1);
}
if (!GITEA_API_URL || !GITEA_OWNER || !GITEA_REPO) {
console.error('Error: Cannot detect Gitea repository configuration');
console.error('Please set environment variables: GITEA_API_URL, GITEA_OWNER, GITEA_REPO');
console.error('错误:无法检测 Gitea 仓库配置');
console.error('请设置环境变量: GITEA_API_URL, GITEA_OWNER, GITEA_REPO');
process.exit(1);
}
@@ -412,20 +503,73 @@ function main() {
// 4. Read version
const version = readVersion(androidDir);
if (!version) {
console.error('Error: Cannot read versionName from build.gradle');
console.error('错误:无法从 build.gradle 读取 versionName');
process.exit(1);
}
console.log(`Version: ${version}`);
console.log(`版本: ${version}`);
// 5. Check tag
const tagName = getTagName(version, monorepo);
const tagInfo = getTagInfo(tagName, gitRoot);
// 5. Determine project prefix
let projectPrefix;
if (projectName) {
// Use provided project name
console.log(`项目前缀: ${projectName} (指定)`);
projectPrefix = projectName;
// Verify that tags with this prefix exist
const testTag = getLatestTag(gitRoot, projectPrefix);
if (!testTag) {
console.error(`错误:找不到匹配 "${projectPrefix}-*" 模式的 git tag`);
console.error('');
console.error('可用的 tags:');
try {
const allTags = execSync('git tag -l --sort=-version:refname | head -10', {
cwd: gitRoot,
encoding: 'utf-8',
shell: true,
stdio: ['pipe', 'pipe', 'pipe']
}).trim();
console.error(allTags || ' (未找到 tags)');
} catch {
console.error(' (无法列出 tags)');
}
console.error('');
console.error(`请创建正确前缀的 tag:`);
console.error(` git tag -a ${projectPrefix}-${version} -m "发布说明"`);
console.error(` git push origin ${projectPrefix}-${version}`);
process.exit(1);
}
} else {
// Infer project name from directory and tags
projectPrefix = inferProjectName(gitRoot, androidDir);
if (projectPrefix === null) {
console.error('错误:无法从 git tags 推断项目名称');
console.error('请确保至少存在一个以下格式的 tag:');
console.error(' myapp-1.0.0 或 v1.0.0');
console.error('');
console.error('或使用 --project 明确指定项目名称:');
console.error(' node release-android.mjs --project admin-tool');
process.exit(1);
}
console.log(`项目前缀: ${projectPrefix || '(v-style)'} (自动检测)`);
}
if (!tagInfo.exists) {
console.error(`Error: Git tag "${tagName}" not found`);
const tagName = getLatestTag(gitRoot, projectPrefix);
if (!tagName) {
const expectedTag = projectPrefix ? `${projectPrefix}-${version}` : `v${version}`;
console.error(`错误:找不到匹配的 git tag`);
console.error(`期望的 tag 格式: ${expectedTag}`);
console.error('');
console.error('Please create a tag before releasing:');
console.error(` git tag -a ${tagName} -m "Release notes"`);
console.error('请先创建 tag:');
console.error(` git tag -a ${expectedTag} -m "发布说明"`);
console.error(` git push origin ${expectedTag}`);
process.exit(1);
}
const tagInfo = getTagInfo(tagName, gitRoot);
if (!tagInfo.exists) {
console.error(`错误Git tag "${tagName}" 不存在`);
console.error('请推送 tag:');
console.error(` git push origin ${tagName}`);
process.exit(1);
}
@@ -434,37 +578,39 @@ function main() {
// 6. Detect Java
const javaHome = detectJavaHome();
if (!javaHome) {
console.error('Error: Cannot find Java Runtime');
console.error('Please install JDK or ensure Android Studio is properly installed');
console.error('错误:找不到 Java 运行环境');
console.error('请安装 JDK 或确保 Android Studio 已正确安装');
process.exit(1);
}
console.log(`Java: ${javaHome}`);
console.log('');
console.log('Building...');
console.log('开始构建...');
// 7. Build APK
try {
buildApk(androidDir, javaHome);
} catch (err) {
console.error('Build failed:', err.message);
console.error('构建失败:', err.message);
process.exit(1);
}
// 8. Find APK
const apk = findApk(androidDir);
if (!apk) {
console.error('Error: APK file not found');
console.error('Check build output at: app/build/outputs/apk/release/');
console.error('错误:找不到 APK 文件');
console.error('检查构建输出: app/build/outputs/apk/release/');
process.exit(1);
}
console.log(`APK: ${apk.path} (${apk.signed ? 'signed' : 'unsigned'})`);
console.log(`APK: ${apk.path} (${apk.signed ? '已签名' : '未签名'})`);
// 9. Upload to Gitea
const fileName = `${GITEA_REPO}-android-${version}.apk`;
const fileName = projectPrefix
? `${projectPrefix}-${version}.apk`
: `${GITEA_REPO}-${version}.apk`;
console.log('');
console.log('Uploading to Gitea...');
console.log('上传到 Gitea...');
try {
const result = uploadToGitea({
@@ -480,13 +626,13 @@ function main() {
console.log('');
console.log('='.repeat(60));
console.log('Release successful!');
console.log(`File: ${fileName}`);
console.log(`Size: ${(result.fileSize / 1024 / 1024).toFixed(2)} MB`);
console.log('发布成功!');
console.log(`文件: ${fileName}`);
console.log(`大小: ${(result.fileSize / 1024 / 1024).toFixed(2)} MB`);
console.log(`URL: ${result.releaseUrl}`);
console.log('='.repeat(60));
} catch (err) {
console.error('Upload failed:', err.message);
console.error('上传失败:', err.message);
process.exit(1);
}
}

View File

@@ -130,9 +130,13 @@ Based on changes in staging area, **analyze and generate meaningful commit messa
- 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: `<type>(<scope>): <description>`, where `<scope>` is subproject name
- Example: `feat(ios): support OGG Opus upload` or `fix(electron): fix clipboard injection failure`
- 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
@@ -149,11 +153,27 @@ Based on changes in staging area, **analyze and generate meaningful commit messa
Execute commit with generated commit message (staging area now includes user's changes and version number update).
**Windows encoding issue solution:**
**Commit message language by platform:**
Cursor's Shell tool has encoding issues when passing Chinese parameters on Windows, causing garbled Git commit messages.
**macOS/Linux** (use Chinese commit messages):
**Correct approach**: Use **English** commit message
```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
@@ -163,26 +183,12 @@ git commit -m "feat(android): add new feature"
git commit --message="fix(android): fix code review issues" --message="- Fix syntax error" --message="- Use JSONObject instead of manual JSON"
```
**Prohibited methods** (will cause garbled text):
**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
**For Chinese commit messages**: Please manually execute `git commit` in terminal, or use Git GUI tools other than Cursor.
**macOS/Linux alternative** (supports Chinese):
```bash
git commit -m "$(cat <<'EOF'
feat(android): new feature description
- Detail 1
- Detail 2
EOF
)"
```
### 8. Create Tag
> **Executed by default**. Only skip this step when user explicitly inputs "skip" or "skip tag".
@@ -228,7 +234,7 @@ Examples (multi-line commit, more recommended):
- **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.
- **Windows encoding issues**: Cursor Shell tool garbles Chinese parameters, must use **English** commit messages. For Chinese, please manually execute in terminal.
- **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 `<subproject>-<version>` 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.

View File

@@ -130,9 +130,13 @@ Based on changes in staging area, **analyze and generate meaningful commit messa
- 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: `<type>(<scope>): <description>`, where `<scope>` is subproject name
- Example: `feat(ios): support OGG Opus upload` or `fix(electron): fix clipboard injection failure`
- 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
@@ -149,11 +153,27 @@ Based on changes in staging area, **analyze and generate meaningful commit messa
Execute commit with generated commit message (staging area now includes user's changes and version number update).
**Windows encoding issue solution:**
**Commit message language by platform:**
Cursor's Shell tool has encoding issues when passing Chinese parameters on Windows, causing garbled Git commit messages.
**macOS/Linux** (use Chinese commit messages):
**Correct approach**: Use **English** commit message
```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
@@ -163,26 +183,12 @@ git commit -m "feat(android): add new feature"
git commit --message="fix(android): fix code review issues" --message="- Fix syntax error" --message="- Use JSONObject instead of manual JSON"
```
**Prohibited methods** (will cause garbled text):
**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
**For Chinese commit messages**: Please manually execute `git commit` in terminal, or use Git GUI tools other than Cursor.
**macOS/Linux alternative** (supports Chinese):
```bash
git commit -m "$(cat <<'EOF'
feat(android): new feature description
- Detail 1
- Detail 2
EOF
)"
```
### 8. Create Tag
> **Executed by default**. Only skip this step when user explicitly inputs "skip" or "skip tag".
@@ -247,7 +253,7 @@ git push origin <subproject>-<version>
- **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.
- **Windows encoding issues**: Cursor Shell tool garbles Chinese parameters, must use **English** commit messages. For Chinese, please manually execute in terminal.
- **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 `<subproject>-<version>` 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.

View File

@@ -1,14 +1,20 @@
---
description: Build and release Android APK to Gitea
description: Build and release Android APK to Gitea (generic for any Android project)
---
# Android Release Command
Build Android APK and upload to Gitea Release.
Build Android APK and upload to Gitea Release for the current directory's Android project.
## Prerequisites Check
## Prerequisites
First, verify the environment:
Before running this command:
1. **Create and push the git tag first** (e.g., `myapp-1.0.0`, `android-1.0.0`, or `v1.0.0`)
2. Ensure `GITEA_TOKEN` environment variable is set
3. Ensure Android project exists in current directory
Check the environment:
```bash
# Check GITEA_TOKEN
@@ -17,40 +23,25 @@ echo "GITEA_TOKEN: ${GITEA_TOKEN:+SET}"
# Check current directory for Android project
ls -la app/build.gradle.kts 2>/dev/null || ls -la android/app/build.gradle.kts 2>/dev/null || echo "No Android project found"
# Check current version
grep -h 'versionName' app/build.gradle.kts android/app/build.gradle.kts 2>/dev/null | head -1
# Check existing tags
git tag -l '*android*' -l 'v*' | tail -5
# Check recent tags (script will use the latest tag)
git tag -l | tail -10
```
## Release Process
**If there are uncommitted changes:**
1. Increment version: update `versionCode` (+1) and `versionName` in `app/build.gradle.kts`
2. Commit the changes with a descriptive message
3. Create annotated git tag:
- Monorepo: `git tag -a android-{version} -m "Release notes"`
- Standalone: `git tag -a v{version} -m "Release notes"`
4. Push commit and tag: `git push && git push origin {tag}`
**If no uncommitted changes but tag doesn't exist:**
1. Create tag and push it
## Build and Upload
After tag is created and pushed, run:
After creating and pushing the tag, run:
```bash
node ~/.config/opencode/bin/release-android.mjs
node ~/.opencode/bin/release-android.mjs
```
The script will:
- Auto-detect Android project root (standalone or monorepo)
- Auto-detect project name from recent git tags or directory name
- Auto-detect Gitea config from git remote URL (HTTPS/SSH)
- Auto-detect Java from Android Studio
- Build release APK
- Upload to Gitea Release
- Upload to Gitea Release matching the tag pattern
## Error Handling

View File

@@ -48,13 +48,24 @@ git add command/ skill/ opencode.json
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**:
**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"
@@ -72,4 +83,4 @@ git push origin main
- **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.
- **English commit**: To avoid encoding issues, suggest using English commit messages.
- **Commit message language**: Default use Chinese (macOS/Linux); Windows must use English due to Cursor Shell tool encoding issues.