diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000..b904816a3 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,46 @@ +# LiveAgent 贡献指南 + +感谢你对 LiveAgent 的关注!为了让协作高效、降低审核成本,本项目对 issue 与 PR 有明确的流程要求。**不符合流程的 PR 会被自动转为 draft**,补齐后再点击 "Ready for review" 即可重新触发检查。 + +## 核心流程:先 Issue,后 PR + +1. **先提 issue**:无论缺陷还是新功能,先用对应的 issue 模板提交,写清需求说明 / 复现步骤。 +2. **等待确认**:功能类提案请等维护者确认方向后再动手,避免白做。 +3. **再提 PR**:PR 正文必须用 `Closes #123` / `Fixes #123` 关联对应 issue。 +4. 内部维护者的日常开发同样遵守"PR 关联 issue"——差别只是可以自己先补一个简要 issue 再提 PR。 + +不允许无 issue 直接提 PR。琐碎修复(错别字、注释、明显笔误)可在 PR 说明中注明豁免理由,由维护者判断。 + +## PR 硬性要求(自动检查) + +以下任一不满足,PR 会被自动转为 draft 并附检查报告: + +| 检查项 | 要求 | +| --- |----------------------------------------------------------------------------------------------------| +| 关联 issue | 正文包含 `Closes #N` / `Fixes #N` / `Resolves #N` | +| 截图 / 预览 | UI 改动必须附截图或录屏(强制);后端 / CLI 改动附请求响应示例或运行日志等文字材料即可,由人工审核把关 | +| 无合并冲突 | 与目标分支冲突时请先在自己的分支解决(merge 或 rebase 基线),维护者不代为解冲突 | + +被转为 draft 后:按报告补齐 → 点击 **Ready for review** → 重新检查。 + +## PR 内容要求 + +- **保持聚焦**:一个 PR 只做一件事,不混入无关重构或格式化。 +- **写清改动范围**:按模板列出涉及模块与关键路径(`agent-gui` / `agent-gateway` / `src-tauri`),降低审核复杂度,也便于 AI review。 +- **通过 CI**:提交前在本地跑对应模块的检查: + - Gateway(Go):`cd crates/agent-gateway && go test ./...`,proto 变更需 `make proto-check` + - GUI 前端:`cd crates/agent-gui && pnpm build && pnpm lint && pnpm test:frontend` + - Tauri(Rust):`cargo check --manifest-path crates/agent-gui/src-tauri/Cargo.toml --tests` + - Gateway WebUI:`cd crates/agent-gateway/web && pnpm build && pnpm lint && pnpm test` +- **不提交敏感信息**:API Key、token、个人数据、`.env`、本地配置一律不入库。 +- **同步文档与注释**:影响用户行为、部署或配置的变更,同步更新文档;修改代码后同步修改受影响的注释。 + +## Issue 要求 + +- Bug 报告:提供版本、环境、最小复现步骤、预期与实际行为。 +- 需求提案:写清要解决的问题与期望方案——**需求说明是必填项**,只有一句"希望支持 XXX"的 issue 会被要求补充。 +- 安全漏洞:**不要**发公开 issue,请通过 [Security Advisories](https://github.com/Stack-Cairn/LiveAgent/security/advisories/new) 私下报告。 + +## 许可 + +提交贡献即表示你同意你的代码以本仓库的 [MIT License](../LICENSE) 发布。 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..a7db26a6b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,88 @@ +name: Bug 报告 / Bug report +description: 报告 LiveAgent 的缺陷。请提供完整的复现信息,信息不全的 issue 可能被要求补充后才会处理。 +title: "[Bug] " +labels: + - bug +body: + - type: markdown + attributes: + value: | + 感谢反馈!请勿在此报告安全漏洞——请通过 [Security Advisories](https://github.com/Stack-Cairn/LiveAgent/security/advisories/new) 私下提交。 + 提交前请先搜索现有 issue,避免重复。 + - type: dropdown + id: area + attributes: + label: 影响范围 + description: 选择该问题主要影响的模块。 + options: + - 桌面端 UI(agent-gui / React) + - 桌面端内核(Tauri / Rust) + - 网关(agent-gateway / Go) + - Agent 会话 / 流式输出 + - 工具执行 / MCP / Skills + - 模型接入 / Provider + - 打包 / 发布 / 安装 + - 文档 + - 其他 + validations: + required: true + - type: input + id: version + attributes: + label: 版本或 commit + description: 发布版本号、Docker 镜像 tag、分支或 commit SHA。 + placeholder: v0.3.2 / main@f69f24dc + validations: + required: true + - type: textarea + id: environment + attributes: + label: 运行环境 + description: 操作系统与版本、部署方式(桌面端 / 网关自部署)、使用的模型 Provider 等。 + placeholder: | + OS: macOS 15.2 (Apple Silicon) + 部署方式: 桌面端 + 模型 Provider: Anthropic API + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: 复现步骤 + description: 能稳定复现问题的最小操作序列。 + placeholder: | + 1. 打开设置页…… + 2. 点击…… + 3. 观察到…… + validations: + required: true + - type: textarea + id: expected + attributes: + label: 预期行为 + validations: + required: true + - type: textarea + id: actual + attributes: + label: 实际行为 + validations: + required: true + - type: textarea + id: logs + attributes: + label: 日志 / 截图 + description: 相关日志、报错信息或截图,可直接拖拽上传。请先移除密钥、token 等敏感信息。 + validations: + required: false + - type: checkboxes + id: checklist + attributes: + label: 提交前确认 + options: + - label: 我已搜索现有 issue 和 PR,确认没有重复。 + required: true + - label: 内容中不包含 API Key、token、密码等敏感信息。 + required: true + - label: 这不是安全漏洞(安全问题请走 Security Advisories 私下报告)。 + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..1914c6a06 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: 安全漏洞报告 / Security vulnerability + url: https://github.com/Stack-Cairn/LiveAgent/security/advisories/new + about: 请勿在公开 issue 中报告安全漏洞,请通过 GitHub Security Advisories 私下提交。 + - name: 贡献指南 / Contributing guidelines + url: https://github.com/Stack-Cairn/LiveAgent/blob/main/.github/CONTRIBUTING.md + about: 提交 issue 或 PR 之前,请先阅读贡献指南。 + - name: 使用咨询 / Questions & discussions + url: https://github.com/Stack-Cairn/LiveAgent/discussions + about: 使用问题、想法讨论请使用 Discussions,issue 仅用于缺陷报告与需求提案。 diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 000000000..c3038b508 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,47 @@ +name: 文档问题 / Documentation +description: 报告文档的错误、过时、缺失或表述不清的问题。 +title: "[Docs] " +labels: + - documentation +body: + - type: dropdown + id: type + attributes: + label: 问题类型 + options: + - 内容有误(与实际行为不符) + - 内容过时(功能已变更但文档未更新) + - 内容缺失(该有说明但没有) + - 表述不清 / 易产生误解 + - 其他 + validations: + required: true + - type: input + id: location + attributes: + label: 文档位置 + description: 相关文档的链接或文件路径。 + placeholder: docs/xxx.md 或 README.md 的某一节 + validations: + required: true + - type: textarea + id: problem + attributes: + label: 问题描述 + description: 说明哪里有问题。是内容错误的话,请一并说明实际的正确行为。 + validations: + required: true + - type: textarea + id: suggestion + attributes: + label: 修改建议 + description: 如果你有建议的写法,写在这里;愿意直接提 PR 修复也请注明。 + validations: + required: false + - type: checkboxes + id: checklist + attributes: + label: 提交前确认 + options: + - label: 我已搜索现有 issue,确认没有重复。 + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..6a5ccdc08 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,71 @@ +name: 需求提案 / Feature request +description: 提出新功能或改进。所有功能类 PR 必须先有对应的需求 issue,请在这里写清需求说明。 +title: "[Feature] " +labels: + - enhancement +body: + - type: markdown + attributes: + value: | + 本项目要求 **先 issue 后 PR**:功能开发前请先提交本表单并等待维护者确认,再开始编码。 + 未关联需求 issue 的 PR 会被直接转为 draft。 + - type: dropdown + id: area + attributes: + label: 影响范围 + description: 选择该提案主要涉及的模块。 + options: + - 桌面端 UI(agent-gui / React) + - 桌面端内核(Tauri / Rust) + - 网关(agent-gateway / Go) + - Agent 会话 / 流式输出 + - 工具执行 / MCP / Skills + - 模型接入 / Provider + - 打包 / 发布 / 安装 + - 文档 + - 其他 + validations: + required: true + - type: textarea + id: problem + attributes: + label: 需求说明(要解决什么问题) + description: 描述当前的痛点或限制、受影响的用户场景。这是需求评审的核心依据,请写具体。 + placeholder: 当前……导致……,影响到……场景。 + validations: + required: true + - type: textarea + id: proposal + attributes: + label: 期望的方案或行为 + description: 描述期望的交互、API、配置或行为变化。UI 相关的提案建议附草图或参考截图。 + validations: + required: true + - type: textarea + id: scope + attributes: + label: 预计改动范围 + description: 如果你打算自己实现,请列出预计涉及的模块 / 目录 / 文件,便于评估复杂度。 + placeholder: | + crates/agent-gui/src/... + crates/agent-gateway/internal/... + validations: + required: false + - type: textarea + id: alternatives + attributes: + label: 备选方案 + description: 考虑过的其他方案或现有替代做法。 + validations: + required: false + - type: checkboxes + id: checklist + attributes: + label: 提交前确认 + options: + - label: 我已搜索现有 issue 和 PR,确认没有重复。 + required: true + - label: 本提案聚焦单一功能或改进,没有混杂多个不相关需求。 + required: true + - label: 我了解需先经维护者确认后再提交实现 PR,否则 PR 会被转为 draft。 + required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..db71fa115 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,52 @@ + + +## 关联 Issue + + + +Closes # + +## 变更说明 + + + +## 变更类型 + +- [ ] Bug 修复 +- [ ] 新功能 +- [ ] 重构 / 性能优化 +- [ ] 文档 +- [ ] 构建 / CI / 依赖 +- [ ] 其他 + +## 改动范围 + + + +- 模块: +- 关键路径: + +## 截图 / 效果预览 + + + +## 验证方式 + + + +## 提交前确认 + +- [ ] 已关联需求 issue(或该 PR 属于无需 issue 的琐碎修复,已在变更说明中说明)。 +- [ ] 已与目标分支同步,无合并冲突。 +- [ ] 改动聚焦,未混入无关变更。 +- [ ] 不包含密钥、token 或个人敏感数据。 +- [ ] 影响用户行为、部署或配置的变更已同步更新文档。 diff --git a/.github/workflows/pr-governance.yml b/.github/workflows/pr-governance.yml new file mode 100644 index 000000000..d9e4d2c3d --- /dev/null +++ b/.github/workflows/pr-governance.yml @@ -0,0 +1,157 @@ +name: PR Governance + +on: + pull_request_target: + types: [opened, edited, reopened, synchronize, ready_for_review] + +permissions: + pull-requests: write + contents: read + +concurrency: + group: pr-governance-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + governance: + name: Check PR compliance + runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.draft && github.event.pull_request.state == 'open' }} + steps: + - uses: actions/github-script@v8 + with: + script: | + const { owner, repo } = context.repo; + const pr = context.payload.pull_request; + const body = pr.body || ''; + const MARKER = ''; + + // PR(如 dependabot)与维护者显式豁免的 PR 跳过检查。 + const labels = pr.labels.map((l) => l.name); + if (pr.user.type === 'Bot' || labels.includes('governance-exempt')) { + core.info('Bot PR 或带 governance-exempt 标签,跳过治理检查。'); + return; + } + + const problems = []; + + // 1) 必须关联 issue:优先看 GitHub 解析出的关闭引用,正则兜底 + // (跨仓库引用等场景 closingIssuesReferences 可能未收录)。 + const gql = await github.graphql( + `query ($owner: String!, $repo: String!, $num: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $num) { + closingIssuesReferences(first: 1) { totalCount } + } + } + }`, + { owner, repo, num: pr.number }, + ); + const linkedByGithub = + gql.repository.pullRequest.closingIssuesReferences.totalCount > 0; + const linkedByText = + /(clos(?:e|es|ed)|fix(?:es|ed)?|resolv(?:e|es|ed))\s*:?\s+(?:[\w.-]+\/[\w.-]+)?#\d+/i.test(body); + if (!linkedByGithub && !linkedByText) { + problems.push( + '**未关联 issue**:正文需包含 `Closes #123` / `Fixes #123` / `Resolves #123`。' + + '本项目要求先 issue 后 PR,详见 [贡献指南](https://github.com/' + + `${owner}/${repo}/blob/main/.github/CONTRIBUTING.md)。`, + ); + } + + // 2) UI 改动必须附截图/预览:改动文件命中前端路径,而正文没有图片即视为缺失。 + const UI_PATHS = [ + 'crates/agent-gui/src/', + 'crates/agent-gateway/web/src/', + ]; + const files = await github.paginate(github.rest.pulls.listFiles, { + owner, + repo, + pull_number: pr.number, + per_page: 100, + }); + const uiTouched = files.some((f) => + UI_PATHS.some((p) => f.filename.startsWith(p)), + ); + const hasImage = + /!\[[^\]]*\]\([^)]+\)| setTimeout(r, 5000)); + const { data } = await github.rest.pulls.get({ + owner, + repo, + pull_number: pr.number, + }); + mergeable = data.mergeable; + } + if (mergeable === false) { + problems.push( + '**与目标分支存在冲突**:请在你的分支合并或 rebase 最新基线并解决冲突后再提审,维护者不代为解冲突。', + ); + } + + // 汇总:更新置顶报告评论;不合规则转为 draft。 + const passed = problems.length === 0; + const report = passed + ? `${MARKER}\n**PR 治理检查通过**,等待人工 review。` + : [ + MARKER, + '**PR 治理检查未通过,已自动转为 draft。**', + '', + ...problems.map((p) => `- ${p}`), + '', + '补齐后点击 **Ready for review** 会自动重新检查。', + ].join('\n'); + + const comments = await github.paginate(github.rest.issues.listComments, { + owner, + repo, + issue_number: pr.number, + per_page: 100, + }); + const existing = comments.find((c) => c.body?.includes(MARKER)); + if (existing) { + // 结果没变化就不重复编辑,避免 timeline 噪音。 + if (existing.body !== report) { + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: existing.id, + body: report, + }); + } + } else if (!passed) { + // 首次即通过的 PR 不发评论,保持安静。 + await github.rest.issues.createComment({ + owner, + repo, + issue_number: pr.number, + body: report, + }); + } + + if (!passed) { + // 即便转 draft 失败(token 权限不足),下面的 setFailed 也会把检查置红, + // 配合 main 的 required status check 仍能阻止合并,治理不会失效。 + try { + await github.graphql( + `mutation ($id: ID!) { + convertPullRequestToDraft(input: { pullRequestId: $id }) { + pullRequest { isDraft } + } + }`, + { id: pr.node_id }, + ); + } catch (e) { + core.warning(`转 draft 失败(${e.message}),但检查已置红,不影响合并拦截。`); + } + core.setFailed(`PR 治理检查未通过:\n- ${problems.join('\n- ')}`); + }