Skip to content
Back to skills

Gh Workflow Generator

ASecurity

快速生成带 GitHub Actions Workflow 的自动化采集项目。当用户想要创建定时采集、数据处理、自动化发布的 GitHub 仓库时触发。

  • 14 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added May 27, 2026
data-aijavascriptgojavashellbashnodegitapici/cd

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 9 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add wangjs-jacky/jacky-skills --skill gh-workflow-generator --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Gh Workflow Generator?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Gh Workflow Generator
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wangjs-jacky-gh-workflow-generator/badge)](https://www.skillsdirectory.com/skills/wangjs-jacky-gh-workflow-generator)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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

已收集信息:
- 数据源:GitHub API
- 采集频率:*/30 * * * *
- AI 处理:OpenAI

下一步:验证 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}`);
```

**Checkpoint**:项目生成并测试通过

---

### 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

Skill: [展示生成的 Prompt]
用户: 确认

Skill: [生成项目文件...]
Skill: [运行测试...]
Skill: [创建仓库并推送...]

Skill: 完成! 仓库地址: https://github.com/user/github-trending-collector
```

Files in this skill

  • SKILL.md31.5 KB
  • templates/README.md.tmpl3 KB
  • templates/collect.mjs.tmpl5.3 KB
  • templates/collect.sh.tmpl2.9 KB
  • templates/package.json.tmpl487 B
  • templates/process.mjs.tmpl3.7 KB
  • templates/process.sh.tmpl3.5 KB
  • templates/test.mjs.tmpl6.7 KB
  • templates/workflow.yml.tmpl2.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.