gh-workflow-generator · diff
git:20260405.06b2dd2 to git:20260711.dae29b8
19 added, 820 removed. Audit B to B.
---
name: gh-workflow-generator
description: "快速生成带 GitHub Actions Workflow 的自动化采集项目。当用户想要创建定时采集、数据处理、自动化发布的 GitHub 仓库时触发。"
---
<role>
你是一个 GitHub 自动化项目架构师。帮助用户快速搭建带有完整 CI/CD 流水线的数据采集项目。
</role>
<purpose>
通过引导式问答收集用户需求,生成完整的项目模板,包括 GitHub Actions Workflow、采集脚本、处理逻辑和发布配置。
</purpose>
<trigger>
```text
触发词/示例:
- 创建采集项目
- 生成 GitHub Workflow
- 搭建自动化流水线
- 定时采集数据
- gh-workflow-generator
- 自动化采集项目
- 创建带 workflow 的仓库
- 生成数据采集脚手架
```
</trigger>
- <gsd:workflow>
- <gsd:meta>
- <name>gh-workflow-generator</name>
- <trigger>采集项目、Workflow、自动化流水线、定时采集、数据采集脚手架</trigger>
- <requires>Read, Write, Edit, Glob, Bash, AskUserQuestion, Skill</requires>
- <stateFile>.gh-workflow-state.json</stateFile>
- <checkpoints>
- <checkpoint order="1">已检查 github-repo-publish skill</checkpoint>
- <checkpoint order="2">已收集数据源需求</checkpoint>
- <checkpoint order="3">API Key 验证通过</checkpoint>
- <checkpoint order="4">用户确认生成的 AI Prompt</checkpoint>
- <checkpoint order="5">项目文件生成完成</checkpoint>
- <checkpoint order="6">测试用例通过</checkpoint>
- <checkpoint order="7">Git 仓库创建并推送成功</checkpoint>
- <checkpoint order="8">Workflow 运行验证成功</checkpoint>
- </checkpoints>
- <constraints>
- <constraint>脚本使用 Node.js (ESM),不是 Shell</constraint>
- <constraint>先生成函数 + 测试用例,测试通过后组装</constraint>
- <constraint>API Key 必须通过测试脚本验证后才能继续</constraint>
- <constraint>必须创建真实的 .env 文件,不只是 .env.example</constraint>
- <constraint>Workflow 推送后必须自动验证运行状态</constraint>
- <constraint>自动修复失败最多 3 次后让用户协助</constraint>
- <constraint>所有 GitHub 操作自动化,不需要用户确认</constraint>
- <constraint>每个阶段完成后必须更新状态文件</constraint>
- <constraint>启动时检查状态文件,支持中断恢复</constraint>
- </constraints>
- </gsd:meta>
-
- <gsd:recovery>
- <detection>
- 启动时检查项目目录下是否存在 .gh-workflow-state.json 文件
- </detection>
- <stateFileSchema>
- {
- "phase": "collect", // 当前阶段名称
- "phaseOrder": 1, // 阶段序号
- "checkpoint": "需求收集完成", // 当前 checkpoint 描述
- "collected": { // 已收集的用户输入
- "dataSource": "GitHub API",
- "frequency": "*/30 * * * *",
- "aiProvider": "openai",
- "projectName": "my-collector"
- },
- "nextStep": "验证 API Key", // 下一步要执行的操作
- "projectDir": "/path/to/project", // 项目目录
- "updatedAt": "2026-03-25T10:30:00Z"
- }
- </stateFileSchema>
- <action>
- 1. 读取状态文件内容
- 2. 向用户展示当前进度:
- 📊 检测到未完成的流程
- 当前进度:Phase {phaseOrder}/6 - {checkpoint}
- 上次更新:{updatedAt}
- 3. 使用 AskUserQuestion 询问用户:
- - 继续执行(从 nextStep 开始)
- - 重新开始(删除状态文件,从头开始)
- 4. 如果选择继续,恢复 collected 数据,跳转到对应 phase
- 5. 如果选择重新开始,删除状态文件,执行 Phase 0
- </action>
- <stateUpdate>
- 每个阶段完成后必须执行:
- 1. 更新状态文件的 phase、phaseOrder、checkpoint
- 2. 更新 nextStep 为下一阶段的第一步
- 3. 更新 updatedAt 为当前时间
- 4. 将新收集的数据合并到 collected 对象
- </stateUpdate>
- <cleanup>
- 流程全部完成后:
- 1. 删除 .gh-workflow-state.json 文件
- 2. 或重命名为 .gh-workflow-state.completed.json 归档
- </cleanup>
- </gsd:recovery>
-
- <gsd:goal>通过引导式问答,帮助用户创建一个完整的 GitHub 自动化采集项目</gsd:goal>
-
- <gsd:phase name="preflight" order="0">
- <gsd:step>检查状态文件是否存在,存在则执行恢复流程</gsd:step>
- <gsd:step>检查 github-repo-publish skill 是否安装</gsd:step>
- <gsd:step>未安装则自动安装</gsd:step>
- <gsd:step>创建初始状态文件(如果不存在)</gsd:step>
- <gsd:checkpoint>环境预检完成</gsd:checkpoint>
- <gsd:stateUpdate>
- phase: "preflight"
- phaseOrder: 0
- checkpoint: "环境预检完成"
- nextStep: "收集用户需求"
- updatedAt: "{{current_time}}"
- </gsd:stateUpdate>
- </gsd:phase>
-
- <gsd:phase name="collect" order="1">
- <gsd:step>询问用户想监控什么数据源</gsd:step>
- <gsd:step>询问采集频率和执行模式</gsd:step>
- <gsd:step>询问是否需要 AI 处理</gsd:step>
- <gsd:step>收集对应的 API Key(根据 AI 选择)</gsd:step>
- <gsd:step>询问项目名称</gsd:step>
- <gsd:checkpoint>需求收集完成</gsd:checkpoint>
- <gsd:stateUpdate>
- phase: "collect"
- phaseOrder: 1
- checkpoint: "需求收集完成"
- collected: { dataSource, frequency, aiProvider, apiKey, projectName }
- nextStep: "创建 .env 文件并验证 API Key"
- updatedAt: "{{current_time}}"
- </gsd:stateUpdate>
- </gsd:phase>
-
- <gsd:phase name="validate" order="2">
- <gsd:step>创建项目目录</gsd:step>
- <gsd:step>创建 .env 文件(真实文件,非 .env.example)</gsd:step>
- <gsd:step>生成 API 测试脚本(scripts/test-api.mjs)</gsd:step>
- <gsd:step>运行测试脚本验证 API 连通性</gsd:step>
- <gsd:step>如果验证失败,提示用户重新输入 API Key</gsd:step>
- <gsd:checkpoint>API 验证通过</gsd:checkpoint>
- <gsd:stateUpdate>
- phase: "validate"
- phaseOrder: 2
- checkpoint: "API 验证通过"
- collected: { projectDir }
- nextStep: "生成 AI Prompt"
- updatedAt: "{{current_time}}"
- </gsd:stateUpdate>
- </gsd:phase>
-
- <gsd:phase name="prompt" order="3">
- <gsd:step>根据数据源生成 AI Prompt</gsd:step>
- <gsd:step>展示 Prompt 让用户确认或修改</gsd:step>
- <gsd:checkpoint>用户确认 Prompt</gsd:checkpoint>
- <gsd:stateUpdate>
- phase: "prompt"
- phaseOrder: 3
- checkpoint: "用户确认 Prompt"
- collected: { aiPrompt }
- nextStep: "生成项目文件结构"
- updatedAt: "{{current_time}}"
- </gsd:stateUpdate>
- </gsd:phase>
-
- <gsd:phase name="generate" order="4">
- <gsd:step>生成项目文件结构</gsd:step>
- <gsd:step>生成采集函数 + 测试用例</gsd:step>
- <gsd:step>运行测试验证</gsd:step>
- <gsd:checkpoint>项目生成并验证通过</gsd:checkpoint>
- <gsd:stateUpdate>
- phase: "generate"
- phaseOrder: 4
- checkpoint: "项目生成并验证通过"
- nextStep: "Git 初始化并创建仓库"
- updatedAt: "{{current_time}}"
- </gsd:stateUpdate>
- </gsd:phase>
-
- <gsd:phase name="publish" order="5">
- <gsd:step>Git init + commit</gsd:step>
- <gsd:step>调用 github-repo-publish 创建仓库</gsd:step>
- <gsd:step>配置 GitHub Secrets</gsd:step>
- <gsd:checkpoint>仓库创建并推送成功</gsd:checkpoint>
- <gsd:stateUpdate>
- phase: "publish"
- phaseOrder: 5
- checkpoint: "仓库创建并推送成功"
- collected: { repoUrl }
- nextStep: "触发并验证 Workflow 运行"
- updatedAt: "{{current_time}}"
- </gsd:stateUpdate>
- </gsd:phase>
-
- <gsd:phase name="verify" order="6">
- <gsd:step>触发 GitHub Workflow 运行</gsd:step>
- <gsd:step>轮询检查 Workflow 运行状态</gsd:step>
- <gsd:step>如果失败,分析错误日志</gsd:step>
- <gsd:step>尝试自动修复(最多 3 次)</gsd:step>
- <gsd:step>多次失败后让用户协助排查</gsd:step>
- <gsd:checkpoint>Workflow 运行成功</gsd:checkpoint>
- <gsd:stateUpdate>
- phase: "verify"
- phaseOrder: 6
- checkpoint: "Workflow 运行成功"
- status: "completed"
- nextStep: "清理状态文件,流程完成"
- updatedAt: "{{current_time}}"
- </gsd:stateUpdate>
- </gsd:phase>
- </gsd:workflow>
-
- ## 进度展示模板
-
- 在执行过程中,使用以下格式向用户展示当前进度:
-
- ```
- 📊 gh-workflow-generator 进度
-
- ✅ Phase 0: 环境预检
- ✅ Phase 1: 需求收集
- 🔄 Phase 2: API 验证 ← 当前
- ⬜ Phase 3: Prompt 生成
- ⬜ Phase 4: 项目生成
- ⬜ Phase 5: 仓库创建
- ⬜ Phase 6: Workflow 验证
- ```
-
- **恢复时的展示格式**:
-
- ```
- 📊 检测到未完成的流程
+ ## 工作流契约与恢复
- 当前进度:Phase 2/6 - API 验证
- 上次更新:2026-03-25 10:30:00
- 项目目录:/Users/xxx/my-collector
+ 任务启动时必须读取 [references/workflow-contract.md](references/workflow-contract.md),并据此:
- 已收集信息:
- - 数据源:GitHub API
- - 采集频率:*/30 * * * *
- - AI 处理:OpenAI
+ 1. 检查 `.gh-workflow-state.json`,支持继续或重新开始。
+ 2. 在每个 Phase 完成后更新 checkpoint、`nextStep` 与时间。
+ 3. 按 Phase 0–6 顺序执行预检、收集、验证、Prompt、生成、发布与 Workflow 验证。
+ 4. 使用参考文件中的进度模板展示当前阶段。
+ 5. 全部完成后删除状态文件或归档为 completed 文件。
- 下一步:验证 API Key
- ```
# gh-workflow-generator
一个泛化的 GitHub 自动化采集 Skill,让任何开发者可以快速创建一个带 GitHub Actions Workflow 的自动化采集项目。
## 参考案例
- [trending-skills](https://github.com/Aradotso/trending-skills) - GitHub Trending 自动生成 Skills
## 执行流程
### Phase 0: 环境预检
**目标**:确保依赖 skill 已安装,检查是否有未完成的流程
**步骤**:
1. **检查状态文件是否存在**
- 如果存在 `.gh-workflow-state.json`,执行恢复流程
- 向用户展示当前进度,询问是否继续
2. 检查 `github-repo-publish` skill 是否安装
3. 未安装则自动安装(不询问用户)
4. 创建初始状态文件(如果不存在)
```bash
# 检查状态文件
if [ -f ".gh-workflow-state.json" ]; then
echo "检测到未完成的流程"
cat .gh-workflow-state.json
fi
# 检查 skill 是否存在
j-skills list -g | grep github-repo-publish || j-skills install github-repo-publish -g
```
**状态文件初始化**:
```json
{
"phase": "preflight",
"phaseOrder": 0,
"checkpoint": "环境预检完成",
"collected": {},
"nextStep": "收集用户需求",
"projectDir": "{{current_project_dir}}",
"updatedAt": "{{current_time}}"
}
```
**Checkpoint**:环境预检完成
---
### Phase 1: 需求收集与 API 验证
**目标**:收集用户需求并验证 API Key
**步骤**:
1. 询问用户想监控什么数据源
2. 询问采集频率
3. 询问是否需要 AI 处理
4. **如果需要 AI 处理,立即收集对应的 API Key**
5. **创建 `.env` 文件(不只是 `.env.example`)**
6. **运行测试脚本验证 API 连通性**
7. **验证失败则重新询问 API Key**
**必要交互 1** - 需求收集(使用 AskUserQuestion):
```javascript
{
questions: [
{
header: "数据源",
question: "你想监控什么数据源?",
options: [
{ label: "GitHub API", description: "GitHub 仓库、Issue、PR 等" },
{ label: "REST API", description: "任意 REST API 端点" },
{ label: "RSS/Atom", description: "RSS 或 Atom 订阅源" },
{ label: "网页抓取", description: "需要解析 HTML 的网页" },
{ label: "自定义", description: "其他数据源" }
]
},
{
header: "采集频率",
question: "采集频率是多少?",
options: [
{ label: "每 15 分钟", description: "cron: '*/15 * * * *'" },
{ label: "每 30 分钟", description: "cron: '*/30 * * * *'" },
{ label: "每小时", description: "cron: '0 * * * *'" },
{ label: "每天", description: "cron: '0 0 * * *'" }
]
},
{
header: "AI 处理",
question: "是否需要 AI 处理采集的数据?",
options: [
{ label: "是,使用 OpenAI", description: "使用 OpenAI API 处理" },
{ label: "是,使用 Claude", description: "使用 Anthropic Claude API" },
{ label: "否", description: "仅存储原始数据" }
]
}
]
}
```
**必要交互 2** - API Key 收集(如果选择了 AI 处理):
```javascript
// 如果选择 OpenAI
{
header: "OpenAI API Key",
question: "请输入你的 OpenAI API Key(将以 sk- 开头):",
inputType: "password" // 密码输入
}
// 如果选择 Claude
{
header: "Claude API Key",
question: "请输入你的 Anthropic API Key:",
inputType: "password"
}
```
**API 验证步骤**:
1. **创建 `.env` 文件**(真实文件,不只是示例):
```bash
# 在项目目录下创建 .env
cat > .env << EOF
# AI API Key(由 gh-workflow-generator 自动配置)
OPENAI_API_KEY=${用户输入的Key}
# GitHub Token(用于 gh CLI)
GH_TOKEN=${从环境获取或用户输入}
EOF
```
2. **创建测试脚本** `scripts/test-api.mjs`:
```javascript
#!/usr/bin/env node
/**
* API 连通性测试脚本
* 验证 API Key 是否有效
*/
import 'dotenv/config';
const AI_PROVIDER = process.env.AI_PROVIDER || 'openai';
async function testOpenAI() {
const apiKey = process.env.OPENAI_API_KEY;
if (!apiKey) {
throw new Error('OPENAI_API_KEY 未配置');
}
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: 'hello world' }],
max_tokens: 10
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(`OpenAI API 错误: ${error.error?.message || response.statusText}`);
}
console.log('✅ OpenAI API 连接成功');
return true;
}
async function testClaude() {
const apiKey = process.env.ANTHROPIC_API_KEY;
if (!apiKey) {
throw new Error('ANTHROPIC_API_KEY 未配置');
}
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 10,
messages: [{ role: 'user', content: 'hello world' }]
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Claude API 错误: ${error.error?.message || response.statusText}`);
}
console.log('✅ Claude API 连接成功');
return true;
}
// 执行测试
try {
if (AI_PROVIDER === 'openai') {
await testOpenAI();
} else if (AI_PROVIDER === 'claude') {
await testClaude();
} else {
console.log('⏭️ 跳过 AI API 测试(未启用 AI 处理)');
}
process.exit(0);
} catch (error) {
console.error('❌ API 验证失败:', error.message);
process.exit(1);
}
```
3. **运行测试**:
```bash
# 安装依赖
npm install dotenv
# 运行 API 测试
node scripts/test-api.mjs
# 如果失败,提示用户重新输入 API Key
```
**验证失败处理**:
- 如果测试失败,输出错误信息
- 询问用户是否重新输入 API Key
- 最多重试 3 次
- 3 次失败后让用户手动检查
**Checkpoint**:API 验证通过
---
### Phase 2: Prompt 生成
**目标**:生成 AI Prompt 并让用户确认
**步骤**:
1. 根据数据源类型生成 AI Prompt 模板
2. 展示 Prompt 让用户确认
3. 用户可修改 Prompt
4. **将确认的 Prompt 保存到 `.ai-prompt.txt` 文件**
**Prompt 模板示例**(根据数据源类型):
```markdown
# GitHub API 数据源
你是一个数据采集助手。请处理以下 GitHub 数据:
1. 提取关键信息(名称、描述、星标数、语言等)
2. 生成结构化的 Markdown 文档
3. 输出格式:YAML frontmatter + Markdown 正文
数据源:{DATA_SOURCE}
```
**必要交互**:
- 展示生成的 Prompt
- 用户确认或修改
**Prompt 确认后**:
```bash
# 保存用户确认的 Prompt 到文件(供 process.mjs 读取)
echo "${用户确认的Prompt}" > .ai-prompt.txt
```
**Checkpoint**:用户确认 Prompt
---
### Phase 3: 项目生成
- **目标**:生成完整的项目文件
-
- **步骤**:
- 1. 生成项目目录结构
- 2. 生成采集函数(`scripts/collect.mjs`)
- 3. 生成测试用例(`scripts/__tests__/collect.test.mjs`)
- 4. 运行测试验证
- 5. 测试通过后生成 Workflow
-
- **生成的项目结构**:
-
- ```
- <project-name>/
- ├── .github/
- │ └── workflows/
- │ └── collect.yml # GitHub Actions Workflow
- ├── scripts/
- │ ├── collect.mjs # 数据采集(Node.js ESM)
- │ ├── process.mjs # 数据处理(AI 分析)
- │ ├── test-api.mjs # API 连通性测试脚本
- │ ├── verify-workflow.mjs # Workflow 验证脚本
- │ └── __tests__/
- │ ├── collect.test.mjs # 采集测试
- │ └── process.test.mjs # 处理测试
- ├── trending/ # AI 分析报告(不是 skills/)
- │ └── <owner>-<repo>/
- │ └── README.md # 不是 SKILL.md
- ├── output/ # 原始数据(JSON)
- │ └── trending.json
- ├── .ai-prompt.txt # 用户确认的 Prompt
- ├── README.md
- ├── package.json
- ├── .env # 真实的环境变量
- └── .gitignore
- ```
-
- ### 输出格式规范
-
- **文件夹结构**:
- ```
- trending/
- ├── <owner>-<repo>/
- │ └── README.md
- ```
-
- **命名规则**:
- - 文件夹名:`<owner>-<repo>`(将 `/` 替换为 `-`)
- - 文件名:`README.md`(不是 SKILL.md)
-
- **YAML Frontmatter 格式**:
- ```yaml
- ---
- name: {repo.name}
- full_name: {repo.full_name}
- owner: {repo.owner}
- url: {repo.url}
- language: {repo.language}
- stars: {repo.stars}
- forks: {repo.forks}
- today_stars: {repo.today_stars}
- rank: {repo.rank}
- date: {YYYY-MM-DD}
- text: |
- {AI 生成的分析内容}
- - 每行缩进 2 个空格
- - 不要包含 ```markdown 代码块标记
- - 直接是纯文本内容
- ---
- ```
-
- **正文格式**:
- ```markdown
- # {repo.full_name}
-
- {repo.description}
-
- {AI 分析内容(与 text 字段相同)}
-
- ---
-
- > 采集时间: {date} | 排名: #{rank} | 今日新增: {today_stars} ⭐
- ```
-
- **重要约束**:
- 1. `text` 字段不要包含代码块标记(` ```markdown ` 或 ` ``` `)
- 2. AI 分析内容直接输出,不要有"AI 分析"之类的标题
- 3. 元数据全部放在 YAML frontmatter 中
-
- **TDD 流程**:
- 1. 先生成 `collect.mjs` 函数
- 2. 生成 `collect.test.mjs` 测试用例
- 3. 运行 `node --test` 验证
- 4. 测试通过后继续
-
- ### process.mjs 模板
-
- **数据处理脚本**(调用 AI 生成仓库分析报告):
-
- ```javascript
- #!/usr/bin/env node
-
- /**
- * 数据处理脚本:调用 AI 生成仓库分析报告
- */
-
- import 'dotenv/config';
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
- import { join } from 'path';
-
- const AI_PROVIDER = process.env.AI_PROVIDER || 'qwen';
- const QWEN_API_KEY = process.env.QWEN_API_KEY;
- const QWEN_API_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions';
-
- if (!QWEN_API_KEY) {
- console.error('❌ 错误:未找到 QWEN_API_KEY 环境变量');
- process.exit(1);
- }
-
- // 确保 trending 目录存在
- if (!existsSync('trending')) {
- mkdirSync('trending', { recursive: true });
- console.log('📁 Created trending directory');
- }
-
- // 读取采集的数据
- const trendingData = JSON.parse(readFileSync('output/trending.json', 'utf-8'));
- console.log(`📊 Processing ${trendingData.repos.length} repos with ${AI_PROVIDER} AI...`);
-
- /**
- * 调用 AI API 生成仓库分析
- */
- async function analyzeRepo(repo, aiPrompt) {
- // 使用用户确认的 Prompt,或使用默认 Prompt
- const prompt = aiPrompt || `你是一个 GitHub 项目分析专家。请分析以下 GitHub 仓库并生成简洁的分析报告。
-
- ## 仓库信息
-
- - **名称**: ${repo.full_name}
- - **描述**: ${repo.description || '暂无描述'}
- - **语言**: ${repo.language}
- - **星标数**: ${repo.stars.toLocaleString()}
- - **今日新增**: ${(repo.today_stars || 0).toLocaleString()} stars
-
- ## 分析要求
-
- 请从以下维度进行分析(直接输出内容,不要代码块标记):
-
- 1. **项目定位**(50-100字)- 这个项目是做什么的?解决什么问题?
-
- 2. **核心功能**(3-5 个要点)
-
- 3. **技术特点**(50-100字)
-
- 4. **适用场景**(3-5 个场景)
-
- 5. **推荐理由**(50-100字)
-
- 请用 Markdown 格式输出,简洁明了,直接开始内容,不要使用代码块标记。`;
-
- const response = await fetch(QWEN_API_URL, {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${QWEN_API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- model: 'qwen-turbo',
- messages: [{ role: 'user', content: prompt }],
- max_tokens: 1000,
- temperature: 0.7
- })
- });
-
- if (!response.ok) {
- const errorText = await response.text();
- throw new Error(`AI API error: ${response.status} - ${errorText}`);
- }
-
- const data = await response.json();
- return data.choices[0].message.content;
- }
-
- /**
- * 生成 Markdown 文件
- */
- function generateMarkdown(repo, analysis) {
- const date = new Date().toISOString().split('T')[0];
-
- // 清理分析文本(移除代码块标记)
- const cleanAnalysis = analysis
- .replace(/```markdown\n?/gi, '')
- .replace(/```\n?/g, '')
- .trim();
-
- return `---
- name: ${repo.name}
- full_name: ${repo.full_name}
- owner: ${repo.owner}
- url: ${repo.url}
- language: ${repo.language}
- stars: ${repo.stars}
- forks: ${repo.forks}
- today_stars: ${repo.today_stars || 0}
- rank: ${repo.rank}
- date: ${date}
- text: |
- ${cleanAnalysis.split('\n').map(line => ' ' + line).join('\n')}
- ---
-
- # ${repo.full_name}
-
- ${repo.description || '暂无描述'}
-
- ${cleanAnalysis}
-
- ---
-
- > 采集时间: ${date} | 排名: #${repo.rank} | 今日新增: ${(repo.today_stars || 0).toLocaleString()} ⭐
- `;
- }
-
- // 读取用户确认的 Prompt(如果存在)
- let aiPrompt = null;
- try {
- const promptFile = readFileSync('.ai-prompt.txt', 'utf-8');
- aiPrompt = promptFile.trim();
- console.log('📝 使用用户确认的 Prompt');
- } catch {
- console.log('📝 使用默认 Prompt');
- }
-
- // 处理每个仓库
- const results = [];
- for (let i = 0; i < trendingData.repos.length; i++) {
- const repo = trendingData.repos[i];
- const dirName = repo.full_name.replace('/', '-');
- const trendingDir = join('trending', dirName);
-
- console.log(`\n[${i + 1}/${trendingData.repos.length}] 处理 ${repo.full_name}...`);
-
- try {
- if (!existsSync(trendingDir)) {
- mkdirSync(trendingDir, { recursive: true });
- }
-
- console.log(` 🤖 调用 AI 分析...`);
- const analysis = await analyzeRepo(repo, aiPrompt);
- const markdown = generateMarkdown(repo, analysis);
- writeFileSync(join(trendingDir, 'README.md'), markdown);
-
- console.log(` ✅ 完成`);
- results.push({ repo: repo.full_name, status: 'success' });
-
- // 避免 API 限流
- if (i < trendingData.repos.length - 1) {
- await new Promise(resolve => setTimeout(resolve, 1000));
- }
- } catch (error) {
- console.error(` ❌ 失败: ${error.message}`);
- results.push({ repo: repo.full_name, status: 'failed', error: error.message });
- }
- }
-
- console.log(`\n📊 处理完成!成功: ${results.filter(r => r.status === 'success').length}, 失败: ${results.filter(r => r.status === 'failed').length}`);
- ```
+ 执行生成阶段前,必须读取 [references/project-generation.md](references/project-generation.md),按其中的项目结构、测试优先顺序、输出格式和 `process.mjs` 模板生成文件。
- **Checkpoint**:项目生成并测试通过
+ 硬约束:
- ---
+ 1. 使用 Node.js ESM。
+ 2. 先生成采集函数与测试,测试通过后再组装。
+ 3. 不在脚本或仓库中提交 API Key。
+ 4. 阶段完成后更新 `.gh-workflow-state.json`。
### Phase 4: 仓库创建
**目标**:创建 Git 仓库并推送到 GitHub
**步骤**:
1. Git init + commit(自动化,不询问)
2. 调用 `github-repo-publish` skill 创建仓库
3. 配置 GitHub Secrets(自动化,不询问)
4. 推送到 GitHub(自动化,不询问)
**自动化操作**(不询问用户):
```bash
# Git 初始化
git init
git add .
git commit -m "Initial commit: setup automated collection project"
# 调用 github-repo-publish
# (自动创建仓库并推送)
# 配置 Secrets
gh secret set OPENAI_API_KEY --body "${OPENAI_API_KEY}"
gh secret set GITHUB_TOKEN --body "${GITHUB_TOKEN}"
```
**Checkpoint**:仓库创建并推送成功
---
### Phase 5: Workflow 验证
- **目标**:自动触发并验证 Workflow 运行状态
-
- **步骤**:
- 1. 触发 GitHub Workflow
- 2. 轮询检查运行状态
- 3. 如果失败,分析错误日志
- 4. 尝试自动修复(最多 3 次)
- 5. 多次失败后让用户协助排查
-
- **自动化验证流程**:
-
- ```bash
- # 1. 触发 Workflow
- gh workflow run collect.yml
-
- # 2. 获取最新的 run ID
- RUN_ID=$(gh run list --workflow=collect.yml --limit 1 --json | jq -r '.[0].id')
-
- # 3. 轮询检查状态(最多等待 5 分钟)
- for i in {1..30}; do
- STATUS=$(gh run view $RUN_ID --json | jq -r '.status')
- if [ "$STATUS" = "completed" ]; then
- echo "✅ Workflow 运行成功"
- exit 0
- elif [ "$STATUS" = "failed" ]; then
- echo "❌ Workflow 运行失败"
- # 获取错误日志
- gh run view $RUN_ID --log-failed
- exit 1
- fi
- echo "⏳ 等待中... ($i/30)"
- sleep 10
- done
- ```
-
- **错误分析逻辑**:
-
- ```javascript
- // 分析 Workflow 失败原因
- function analyzeFailure(logs) {
- const errorPatterns = [
- {
- pattern: /API key.*invalid/i,
- fix: '检查 GitHub Secrets 中的 API Key 是否正确配置'
- },
- {
- pattern: /permission denied/i,
- fix: '检查 workflow 的 permissions 配置是否正确'
- },
- {
- pattern: /module not found/i,
- fix: '运行 npm install 检查依赖是否完整'
- },
- {
- pattern: /ENOENT.*no such file/i,
- fix: '检查文件路径是否正确,确保所有脚本文件已提交'
- }
- ];
-
- for (const { pattern, fix } of errorPatterns) {
- if (pattern.test(logs)) {
- return { detected: true, fix };
- }
- }
-
- return { detected: false, fix: '未知错误,请查看完整日志' };
- }
- ```
-
- **自动修复流程**:
-
- ```
- 修复尝试次数: 0/3
-
- 循环:
- 1. 分析错误日志
- 2. 如果是已知错误:
- - 自动应用修复
- - 提交修复代码
- - 重新触发 Workflow
- - 等待结果
- 3. 如果是未知错误或修复失败:
- - 增加尝试次数
- 4. 如果达到 3 次:
- - 输出完整错误日志
- - 提供手动排查建议
- - 让用户协助处理
- ```
-
- **轮询状态脚本** `scripts/verify-workflow.mjs`:
-
- ```javascript
- #!/usr/bin/env node
-
- import { execSync } from 'child_process';
-
- const WORKFLOW_NAME = process.env.WORKFLOW_NAME || 'collect.yml';
- const MAX_ATTEMPTS = 30; // 5 分钟
- const RETRY_DELAY = 10000; // 10 秒
-
- async function sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
-
- async function triggerWorkflow() {
- try {
- console.log(`🚀 触发 Workflow: ${WORKFLOW_NAME}`);
- execSync(`gh workflow run ${WORKFLOW_NAME}`, { stdio: 'inherit' });
- return true;
- } catch (error) {
- console.error('❌ 触发失败:', error.message);
- return false;
- }
- }
-
- async function getLatestRunId() {
- const result = execSync(
- `gh run list --workflow=${WORKFLOW_NAME} --limit 1 --json`,
- { encoding: 'utf-8' }
- );
- const runs = JSON.parse(result);
- return runs[0]?.id;
- }
-
- async function getRunStatus(runId) {
- const result = execSync(
- `gh run view ${runId} --json`,
- { encoding: 'utf-8' }
- );
- const run = JSON.parse(result);
- return run.status;
- }
-
- async function getFailedLogs(runId) {
- try {
- const result = execSync(
- `gh run view ${runId} --log-failed`,
- { encoding: 'utf-8' }
- );
- return result;
- } catch (error) {
- return error.stdout || error.message;
- }
- }
-
- async function main() {
- // 1. 触发 Workflow
- const triggered = await triggerWorkflow();
- if (!triggered) {
- process.exit(1);
- }
-
- // 2. 等待 run 创建
- await sleep(5000);
-
- // 3. 获取 run ID
- const runId = await getLatestRunId();
- if (!runId) {
- console.error('❌ 无法获取 run ID');
- process.exit(1);
- }
-
- console.log(`📋 Run ID: ${runId}`);
-
- // 4. 轮询状态
- for (let i = 1; i <= MAX_ATTEMPTS; i++) {
- const status = await getRunStatus(runId);
- console.log(`⏳ 状态检查 ${i}/${MAX_ATTEMPTS}: ${status}`);
-
- if (status === 'completed') {
- console.log('✅ Workflow 运行成功!');
- process.exit(0);
- }
-
- if (status === 'failed') {
- console.log('❌ Workflow 运行失败');
- const logs = await getFailedLogs(runId);
- console.log('\n📜 错误日志:\n');
- console.log(logs);
- process.exit(1);
- }
-
- await sleep(RETRY_DELAY);
- }
-
- console.log('⏰ 超时:等待时间过长');
- process.exit(1);
- }
-
- main();
- ```
-
- **Checkpoint**:Workflow 运行成功
-
- ---
-
- ## 复用的配置(来自 trending-skills)
-
- ### Workflow 模板
-
- ```yaml
- name: Collect Data
-
- on:
- workflow_dispatch:
- inputs: {}
- schedule:
- - cron: '{{CRON_SCHEDULE}}' # 用户配置
-
- permissions:
- contents: write
-
- jobs:
- collect:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Setup Node
- uses: actions/setup-node@v4
- with:
- node-version: '22'
-
- - name: Install dependencies
- run: npm install
-
- - name: Collect data
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- {{#AI_ENABLED}}
- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- {{/AI_ENABLED}}
- run: node scripts/collect.mjs
-
- - name: Process data
- if: success()
- run: node scripts/process.mjs
-
- - name: Commit and push
- run: |
- git config user.name "bot"
- git config user.email "bot@users.noreply.github.com"
- git add -A
- git diff --staged --quiet && exit 0
- git commit -m "chore: update data $(date +%Y-%m-%d)"
- git push
- ```
-
- ---
-
- ## 模板文件
-
- 详细的模板文件存放在 `templates/` 目录:
-
- | 文件 | 用途 |
- |------|------|
- | `workflow.yml.tmpl` | GitHub Actions Workflow 模板 |
- | `collect.mjs.tmpl` | 采集脚本模板 |
- | `process.mjs.tmpl` | 处理脚本模板 |
- | `test.mjs.tmpl` | 测试用例模板 |
- | `README.md.tmpl` | README 模板 |
- | `package.json.tmpl` | package.json 模板 |
-
- ---
-
- ## 验证
-
- 项目生成完成后,验证以下内容:
-
- - [ ] Workflow 语法正确(`actionlint` 或手动检查)
- - [ ] 测试用例通过(`node --test`)
- - [ ] README 包含完整使用说明
- - [ ] .env 文件已创建并包含有效的 API Key
- - [ ] .env.example 包含所有必需变量(不含真实值)
- - [ ] API 连通性测试通过(`node scripts/test-api.mjs`)
- - [ ] Git 仓库已创建
- - [ ] GitHub Secrets 已配置
- - [ ] 代码已推送到 GitHub
- - [ ] Workflow 自动运行成功(通过 `scripts/verify-workflow.mjs`)
-
- ---
-
- ## Next Up
-
- - [ ] 自定义采集逻辑: 编辑 `scripts/collect.mjs`
- - [ ] 添加更多数据源: 扩展 `collect.mjs`
- - [ ] 修改采集频率: 编辑 `.github/workflows/collect.yml` 中的 cron 表达式
-
- ## 故障排查
-
- 如果 Workflow 运行失败,检查以下内容:
-
- ### 常见错误
-
- | 错误类型 | 可能原因 | 解决方案 |
- |----------|----------|----------|
- | `API key invalid` | API Key 配置错误 | 重新运行 `node scripts/test-api.mjs` 验证 Key |
- | `permission denied` | GitHub Token 权限不足 | 检查 workflow 的 `permissions` 配置 |
- | `module not found` | 依赖未安装 | 在 workflow 中添加 `npm ci` 步骤 |
- | `ENOENT no such file` | 文件未提交到 Git | 确保 `.gitignore` 没有排除必要文件 |
-
- ### 手动触发 Workflow
-
- ```bash
- # 手动触发
- gh workflow run collect.yml
-
- # 查看运行日志
- gh run watch
-
- # 查看失败的日志
- gh run view <run-id> --log-failed
- ```
-
- ---
-
- ## 示例:使用本 Skill 创建 GitHub Trending 采集器
-
- ```
- 用户: /gh-workflow-generator
-
- Skill: 你想监控什么数据源?
- 用户: GitHub Trending 仓库
-
- Skill: 采集频率?
- 用户: 每 30 分钟
-
- Skill: 是否需要 AI 处理?
- 用户: 是,使用 OpenAI
+ 推送后必须读取 [references/workflow-validation.md](references/workflow-validation.md),完成 Workflow 触发、状态轮询、失败日志分析和自动修复。自动修复最多 3 次;仍失败时停止并请用户协助。成功后清理或归档状态文件。
- Skill: [展示生成的 Prompt]
- 用户: 确认
+ ## 可复用配置与示例
- Skill: [生成项目文件...]
- Skill: [运行测试...]
- Skill: [创建仓库并推送...]
+ 按需读取 [references/reusable-config-and-example.md](references/reusable-config-and-example.md):
- Skill: 完成! 仓库地址: https://github.com/user/github-trending-collector
- ```
+ - 生成通用 Workflow、README 与模板文件时,读取“复用的配置”和“模板文件”
+ - 手动验证或排查常见错误时,读取“验证”和“故障排查”
+ - 需要端到端参考时,读取完整 Trending 采集器示例