openyida 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cache/.gitkeep +0 -0
- package/.eslintrc.json +25 -0
- package/.github/workflows/ci.yml +123 -0
- package/.github/workflows/publish.yml +105 -0
- package/.github/workflows/update-contributors.yml +151 -0
- package/.openclaw/skills/yida-issue/SKILL.md +27 -0
- package/.openclaw/skills/yida-issue/scripts/create-issue.js +317 -0
- package/CLAUDE.md +168 -0
- package/CONTRIBUTING.md +59 -0
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/bin/yida.js +811 -0
- package/config.json +4 -0
- package/install-skills.ps1 +162 -0
- package/install-skills.sh +175 -0
- package/package.json +34 -0
- package/pages/dist/.gitkeep +0 -0
- package/pages/src/.gitkeep +0 -0
- package/prd/salary-calculator.md +15 -0
- package/tests/cli.test.js +930 -0
- package/tests/install.test.js +277 -0
- package/tests/yida-issue.test.js +314 -0
package/.cache/.gitkeep
ADDED
|
File without changes
|
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"browser": true,
|
|
4
|
+
"es2021": true,
|
|
5
|
+
"node": true
|
|
6
|
+
},
|
|
7
|
+
"extends": "eslint:recommended",
|
|
8
|
+
"parserOptions": {
|
|
9
|
+
"ecmaVersion": "latest",
|
|
10
|
+
"sourceType": "module"
|
|
11
|
+
},
|
|
12
|
+
"rules": {
|
|
13
|
+
"indent": ["error", 2],
|
|
14
|
+
"linebreak-style": ["error", "unix"],
|
|
15
|
+
"quotes": ["error", "single"],
|
|
16
|
+
"semi": ["error", "always"],
|
|
17
|
+
"no-unused-vars": "warn",
|
|
18
|
+
"no-console": "off"
|
|
19
|
+
},
|
|
20
|
+
"ignorePatterns": [
|
|
21
|
+
"*.compile.js",
|
|
22
|
+
"node_modules/",
|
|
23
|
+
"build/"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
# ── 安装脚本集成测试(三平台)────────────────────────────────────────
|
|
11
|
+
install-test:
|
|
12
|
+
name: Install Script (${{ matrix.os }})
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout code
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
# ── Mac / Linux ──────────────────────────────────────────────────
|
|
24
|
+
- name: Install Skills (Mac/Linux)
|
|
25
|
+
if: runner.os != 'Windows'
|
|
26
|
+
run: bash install-skills.sh --global
|
|
27
|
+
|
|
28
|
+
- name: Verify Skills installed (Mac/Linux)
|
|
29
|
+
if: runner.os != 'Windows'
|
|
30
|
+
run: |
|
|
31
|
+
if [ ! -d ".claude/skills/skills" ]; then
|
|
32
|
+
echo "❌ Skills 目录不存在:.claude/skills/skills"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
skill_count=$(ls -d .claude/skills/skills/*/ 2>/dev/null | wc -l | tr -d ' ')
|
|
36
|
+
if [ "$skill_count" -eq 0 ]; then
|
|
37
|
+
echo "❌ 未找到任何已安装的 Skill"
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
echo "✅ Skills 安装验证通过,共 ${skill_count} 个"
|
|
41
|
+
|
|
42
|
+
- name: Verify shell script syntax (Mac/Linux)
|
|
43
|
+
if: runner.os != 'Windows'
|
|
44
|
+
run: sh -n install-skills.sh && echo "✅ install-skills.sh 语法检查通过"
|
|
45
|
+
|
|
46
|
+
# ── Windows ──────────────────────────────────────────────────────
|
|
47
|
+
- name: Install Skills (Windows)
|
|
48
|
+
if: runner.os == 'Windows'
|
|
49
|
+
shell: pwsh
|
|
50
|
+
run: |
|
|
51
|
+
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
|
|
52
|
+
.\install-skills.ps1 --global
|
|
53
|
+
|
|
54
|
+
- name: Verify Skills installed (Windows)
|
|
55
|
+
if: runner.os == 'Windows'
|
|
56
|
+
shell: pwsh
|
|
57
|
+
run: |
|
|
58
|
+
$skillsPath = ".claude\skills\skills"
|
|
59
|
+
if (-not (Test-Path $skillsPath)) {
|
|
60
|
+
Write-Host "❌ Skills 目录不存在:$skillsPath"
|
|
61
|
+
exit 1
|
|
62
|
+
}
|
|
63
|
+
$skillCount = (Get-ChildItem -Path $skillsPath -Directory).Count
|
|
64
|
+
if ($skillCount -eq 0) {
|
|
65
|
+
Write-Host "❌ 未找到任何已安装的 Skill"
|
|
66
|
+
exit 1
|
|
67
|
+
}
|
|
68
|
+
Write-Host "✅ Skills 安装验证通过,共 $skillCount 个"
|
|
69
|
+
|
|
70
|
+
- name: Verify PowerShell script syntax (Windows)
|
|
71
|
+
if: runner.os == 'Windows'
|
|
72
|
+
shell: pwsh
|
|
73
|
+
run: |
|
|
74
|
+
$errors = $null
|
|
75
|
+
$null = [System.Management.Automation.Language.Parser]::ParseFile(
|
|
76
|
+
(Resolve-Path "install-skills.ps1").Path, [ref]$null, [ref]$errors
|
|
77
|
+
)
|
|
78
|
+
if ($errors.Count -gt 0) {
|
|
79
|
+
$errors | ForEach-Object { Write-Host "❌ $_" }
|
|
80
|
+
exit 1
|
|
81
|
+
}
|
|
82
|
+
Write-Host "✅ install-skills.ps1 语法检查通过"
|
|
83
|
+
|
|
84
|
+
# ── 单元测试与代码质量(ubuntu)──────────────────────────────────────
|
|
85
|
+
test:
|
|
86
|
+
name: Unit Tests
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
needs: install-test
|
|
89
|
+
|
|
90
|
+
steps:
|
|
91
|
+
- name: Checkout code
|
|
92
|
+
uses: actions/checkout@v4
|
|
93
|
+
|
|
94
|
+
- name: Install Skills
|
|
95
|
+
run: bash install-skills.sh --global
|
|
96
|
+
|
|
97
|
+
- name: Setup Node.js
|
|
98
|
+
uses: actions/setup-node@v4
|
|
99
|
+
with:
|
|
100
|
+
node-version: '20'
|
|
101
|
+
|
|
102
|
+
- name: Validate JavaScript syntax
|
|
103
|
+
run: |
|
|
104
|
+
for file in .claude/skills/skills/*/scripts/*.js; do
|
|
105
|
+
[ -f "$file" ] || continue
|
|
106
|
+
node --check "$file" || exit 1
|
|
107
|
+
done
|
|
108
|
+
|
|
109
|
+
- name: Validate JSON files
|
|
110
|
+
run: |
|
|
111
|
+
find . -name "*.json" -path "./.claude/skills/skills/*" | while read -r file; do
|
|
112
|
+
node -e "JSON.parse(require('fs').readFileSync(process.argv[1]))" -- "$file" || exit 1
|
|
113
|
+
echo " ✅ $file"
|
|
114
|
+
done
|
|
115
|
+
|
|
116
|
+
- name: Install dependencies
|
|
117
|
+
run: npm install
|
|
118
|
+
|
|
119
|
+
- name: Run tests
|
|
120
|
+
run: npm test
|
|
121
|
+
|
|
122
|
+
- name: Run tests with coverage
|
|
123
|
+
run: npm run test:coverage
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
# ── 发布前:三平台安装脚本验证 ────────────────────────────────────────
|
|
10
|
+
pre-publish-check:
|
|
11
|
+
name: Pre-publish Check (${{ matrix.os }})
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: true
|
|
15
|
+
matrix:
|
|
16
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout code
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Install Skills (Mac/Linux)
|
|
23
|
+
if: runner.os != 'Windows'
|
|
24
|
+
run: bash install-skills.sh --global
|
|
25
|
+
|
|
26
|
+
- name: Verify Skills installed (Mac/Linux)
|
|
27
|
+
if: runner.os != 'Windows'
|
|
28
|
+
run: |
|
|
29
|
+
skill_count=$(ls -d .claude/skills/skills/*/ 2>/dev/null | wc -l | tr -d ' ')
|
|
30
|
+
[ "$skill_count" -gt 0 ] && echo "✅ Skills 安装验证通过,共 ${skill_count} 个" || (echo "❌ 未找到任何已安装的 Skill" && exit 1)
|
|
31
|
+
|
|
32
|
+
- name: Install Skills (Windows)
|
|
33
|
+
if: runner.os == 'Windows'
|
|
34
|
+
shell: pwsh
|
|
35
|
+
run: |
|
|
36
|
+
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
|
|
37
|
+
.\install-skills.ps1 --global
|
|
38
|
+
|
|
39
|
+
- name: Verify Skills installed (Windows)
|
|
40
|
+
if: runner.os == 'Windows'
|
|
41
|
+
shell: pwsh
|
|
42
|
+
run: |
|
|
43
|
+
$skillCount = (Get-ChildItem -Path ".claude\skills\skills" -Directory -ErrorAction SilentlyContinue).Count
|
|
44
|
+
if ($skillCount -gt 0) { Write-Host "✅ Skills 安装验证通过,共 $skillCount 个" } else { Write-Host "❌ 未找到任何已安装的 Skill"; exit 1 }
|
|
45
|
+
|
|
46
|
+
# ── 发布到 npm ────────────────────────────────────────────────────────
|
|
47
|
+
publish:
|
|
48
|
+
name: Publish to npm
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
needs: pre-publish-check
|
|
51
|
+
permissions:
|
|
52
|
+
contents: write # 用于创建 GitHub Release
|
|
53
|
+
|
|
54
|
+
steps:
|
|
55
|
+
- name: Checkout code
|
|
56
|
+
uses: actions/checkout@v4
|
|
57
|
+
|
|
58
|
+
- name: Setup Node.js
|
|
59
|
+
uses: actions/setup-node@v4
|
|
60
|
+
with:
|
|
61
|
+
node-version: '20'
|
|
62
|
+
registry-url: 'https://registry.npmjs.org'
|
|
63
|
+
|
|
64
|
+
- name: Install Skills
|
|
65
|
+
run: bash install-skills.sh --global
|
|
66
|
+
|
|
67
|
+
- name: Install dependencies
|
|
68
|
+
run: npm install
|
|
69
|
+
|
|
70
|
+
- name: Run tests
|
|
71
|
+
run: npm test
|
|
72
|
+
|
|
73
|
+
- name: Verify package version matches tag
|
|
74
|
+
run: |
|
|
75
|
+
PKG_VERSION="v$(node -p "require('./package.json').version")"
|
|
76
|
+
TAG_VERSION="${GITHUB_REF_NAME}"
|
|
77
|
+
echo "package.json 版本:$PKG_VERSION"
|
|
78
|
+
echo "Tag 版本:$TAG_VERSION"
|
|
79
|
+
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
|
|
80
|
+
echo "❌ package.json 版本($PKG_VERSION)与 tag($TAG_VERSION)不一致,请先更新 package.json"
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
echo "✅ 版本一致,准备发布"
|
|
84
|
+
|
|
85
|
+
- name: Publish to npm
|
|
86
|
+
run: npm publish --access public
|
|
87
|
+
env:
|
|
88
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
89
|
+
|
|
90
|
+
- name: Create GitHub Release
|
|
91
|
+
uses: softprops/action-gh-release@v2
|
|
92
|
+
with:
|
|
93
|
+
tag_name: ${{ github.ref_name }}
|
|
94
|
+
name: ${{ github.ref_name }}
|
|
95
|
+
body: |
|
|
96
|
+
## 安装
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm install -g openyida
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 更新日志
|
|
103
|
+
|
|
104
|
+
请查看 [CHANGELOG](https://github.com/openyida/openyida/commits/main) 了解本版本变更详情。
|
|
105
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
name: Update Contributors
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [closed]
|
|
6
|
+
branches: [main]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
update-contributors:
|
|
11
|
+
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout code
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- name: Update README contributors
|
|
24
|
+
env:
|
|
25
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
26
|
+
REPO: ${{ github.repository }}
|
|
27
|
+
run: |
|
|
28
|
+
python3 - <<'PYEOF'
|
|
29
|
+
import subprocess
|
|
30
|
+
import json
|
|
31
|
+
import re
|
|
32
|
+
import sys
|
|
33
|
+
import os
|
|
34
|
+
|
|
35
|
+
repo = os.environ["REPO"]
|
|
36
|
+
print("📋 获取仓库所有贡献者...")
|
|
37
|
+
|
|
38
|
+
# 从 GitHub API 获取所有贡献者(gh cli 支持自动分页)
|
|
39
|
+
result = subprocess.run(
|
|
40
|
+
["gh", "api", f"repos/{repo}/contributors?per_page=100&anon=false", "--paginate"],
|
|
41
|
+
capture_output=True, text=True
|
|
42
|
+
)
|
|
43
|
+
if result.returncode != 0:
|
|
44
|
+
print(f"⚠️ 获取贡献者失败: {result.stderr}")
|
|
45
|
+
sys.exit(0)
|
|
46
|
+
|
|
47
|
+
# gh --paginate 返回多个 JSON 数组拼接,需要合并
|
|
48
|
+
raw = result.stdout.strip()
|
|
49
|
+
all_contributors = []
|
|
50
|
+
for chunk in re.findall(r'\[.*?\]', raw, re.DOTALL):
|
|
51
|
+
try:
|
|
52
|
+
all_contributors.extend(json.loads(chunk))
|
|
53
|
+
except json.JSONDecodeError:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
if not all_contributors:
|
|
57
|
+
print("⚠️ 未获取到贡献者数据,跳过更新")
|
|
58
|
+
sys.exit(0)
|
|
59
|
+
|
|
60
|
+
print(f"✅ 共获取到 {len(all_contributors)} 位贡献者")
|
|
61
|
+
|
|
62
|
+
# 读取当前 README
|
|
63
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
64
|
+
readme_content = f.read()
|
|
65
|
+
|
|
66
|
+
# 提取 README 中已有的贡献者用户名(小写,去重)
|
|
67
|
+
existing_users = set(
|
|
68
|
+
u.lower() for u in re.findall(r'href="https://github\.com/([^"]+)"', readme_content)
|
|
69
|
+
)
|
|
70
|
+
print(f"=== 当前 README 中已有贡献者 ===")
|
|
71
|
+
for u in sorted(existing_users):
|
|
72
|
+
print(f" - {u}")
|
|
73
|
+
|
|
74
|
+
# 机器人账号关键词(跳过)
|
|
75
|
+
bot_patterns = re.compile(r'\[bot\]|dependabot|github-actions|renovate|actions-user', re.IGNORECASE)
|
|
76
|
+
|
|
77
|
+
# 找出新贡献者,按贡献数量排序(API 返回已按贡献数降序)
|
|
78
|
+
new_html_parts = []
|
|
79
|
+
for contributor in all_contributors:
|
|
80
|
+
login = contributor.get("login", "")
|
|
81
|
+
avatar_url = contributor.get("avatar_url", "")
|
|
82
|
+
html_url = contributor.get("html_url", f"https://github.com/{login}")
|
|
83
|
+
|
|
84
|
+
if not login:
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
# 跳过机器人
|
|
88
|
+
if bot_patterns.search(login):
|
|
89
|
+
print(f" 🤖 跳过机器人: {login}")
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
# 跳过已在 README 中的贡献者
|
|
93
|
+
if login.lower() in existing_users:
|
|
94
|
+
print(f" ⏭️ 已存在: {login}")
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
print(f" ✨ 新贡献者: {login}")
|
|
98
|
+
# 构建头像 HTML(与现有格式完全一致)
|
|
99
|
+
# 强制将版本参数替换为 v=4,修复旧账号可能返回 v=3 的问题(issue #31)
|
|
100
|
+
avatar_url_v4 = re.sub(r'\?v=\d+', '?v=4', avatar_url)
|
|
101
|
+
avatar_48 = f"{avatar_url_v4}&s=48"
|
|
102
|
+
new_html_parts.append(
|
|
103
|
+
f'<a href="{html_url}"><img src="{avatar_48}" width="48" height="48" alt="{login}" title="{login}"/></a>'
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
if not new_html_parts:
|
|
107
|
+
print("\n✅ 没有新贡献者需要添加,README 无需更新")
|
|
108
|
+
sys.exit(0)
|
|
109
|
+
|
|
110
|
+
print(f"\n📝 添加 {len(new_html_parts)} 位新贡献者到 README...")
|
|
111
|
+
|
|
112
|
+
# 在 contributors 区域的 </p> 前插入新贡献者头像
|
|
113
|
+
# README 中 contributors 区域格式:
|
|
114
|
+
# ### 贡献者
|
|
115
|
+
# <p align="left">
|
|
116
|
+
# <a href="..."><img .../></a> ...
|
|
117
|
+
# </p>
|
|
118
|
+
new_html_str = " " + " ".join(new_html_parts)
|
|
119
|
+
|
|
120
|
+
pattern = r'(### 贡献者.*?<p align="left">)(.*?)(</p>)'
|
|
121
|
+
|
|
122
|
+
def replacer(m):
|
|
123
|
+
existing_block = m.group(2).rstrip()
|
|
124
|
+
return m.group(1) + existing_block + new_html_str + "\n" + m.group(3)
|
|
125
|
+
|
|
126
|
+
updated_content = re.sub(pattern, replacer, readme_content, flags=re.DOTALL)
|
|
127
|
+
|
|
128
|
+
if updated_content == readme_content:
|
|
129
|
+
print("⚠️ 未找到 contributors 区域(### 贡献者 + <p align=\"left\">),跳过更新")
|
|
130
|
+
sys.exit(0)
|
|
131
|
+
|
|
132
|
+
with open("README.md", "w", encoding="utf-8") as f:
|
|
133
|
+
f.write(updated_content)
|
|
134
|
+
|
|
135
|
+
print("✅ README.md 已更新")
|
|
136
|
+
PYEOF
|
|
137
|
+
|
|
138
|
+
- name: Commit and push if changed
|
|
139
|
+
run: |
|
|
140
|
+
git config user.name "github-actions[bot]"
|
|
141
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
142
|
+
|
|
143
|
+
if git diff --quiet README.md; then
|
|
144
|
+
echo "✅ README.md 无变化,无需提交"
|
|
145
|
+
exit 0
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
git add README.md
|
|
149
|
+
git commit -m "chore: 自动更新 README contributors [skip ci]"
|
|
150
|
+
git push origin main
|
|
151
|
+
echo "🚀 已推送更新到 main 分支"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yida-issue
|
|
3
|
+
description: 一句话给 OpenYida 提需求,自动判断路由到 openyida/openyida 还是 openyida/yida-skills,并创建格式规范的 GitHub Issue。触发词:给 OpenYida 提需求、提个 issue、报个 bug、希望支持 xxx、建议 xxx。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 一句话提需求
|
|
7
|
+
|
|
8
|
+
用自然语言描述需求,自动路由到正确仓库并创建 GitHub Issue。
|
|
9
|
+
|
|
10
|
+
## 路由规则
|
|
11
|
+
|
|
12
|
+
| 关键词/场景 | 目标仓库 |
|
|
13
|
+
|---|---|
|
|
14
|
+
| CLI、命令行、安装、CI/CD、贡献者、npm | `openyida/openyida` |
|
|
15
|
+
| 登录、创建应用/页面/表单、发布、Schema、宜搭 API | `openyida/yida-skills` |
|
|
16
|
+
| 无法判断 | 提示用户手动指定 `--repo` |
|
|
17
|
+
|
|
18
|
+
## 使用方式
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node scripts/create-issue.js "<需求描述>" [--repo openyida|yida-skills] [--type feature|bug] [--dry-run]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 前置依赖
|
|
25
|
+
|
|
26
|
+
- Node.js ≥ 16
|
|
27
|
+
- GitHub CLI(`gh`)已安装并已登录(`gh auth login`)
|