chore: 移除 SpecKit 相关文件和配置

- 删除所有 SpecKit 命令文件(.claude/commands/speckit.*)
- 删除 .specify 配置目录(templates, scripts, memory)
- 删除 specs 文档目录
- 更新 CLAUDE.md:移除 SpecKit Skills 说明
This commit is contained in:
2026-01-18 12:33:43 +08:00
parent bbcc023482
commit 44633baae5
30 changed files with 133 additions and 5903 deletions
-184
View File
@@ -1,184 +0,0 @@
---
description: 在任务生成后, 对 spec.md、plan.md 和 tasks.md 执行非破坏性的跨制品一致性和质量分析.
---
## 用户输入
```text
$ARGUMENTS
```
在继续之前, 你**必须**考虑用户输入(如果不为空).
## 目标
在实施之前, 识别三个核心制品(`spec.md``plan.md``tasks.md`)之间的不一致、重复、模糊和规范不足的项目. 此命令**必须**在 `/speckit.tasks` 成功生成完整的 `tasks.md` 后运行.
## 操作约束
**严格只读**: **不要**修改任何文件. 输出结构化分析报告. 提供可选的修复计划(用户必须明确批准后才能手动调用任何后续编辑命令).
**章程权威**: 项目章程(`.specify/memory/constitution.md`)在此分析范围内是**不可协商的**. 章程冲突自动为严重问题, 需要调整规范、计划或任务——而不是稀释、重新解释或默默忽略原则. 如果原则本身需要更改, 那必须在 `/speckit.analyze` 之外的单独、明确的章程更新中进行.
## 执行步骤
### 1. 初始化分析上下文
从仓库根目录运行一次 `.specify/scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks` 并解析 JSON 以获取 FEATURE_DIR 和 AVAILABLE_DOCS. 推导绝对路径:
- SPEC = FEATURE_DIR/spec.md
- PLAN = FEATURE_DIR/plan.md
- TASKS = FEATURE_DIR/tasks.md
如果任何必需文件缺失, 则以错误消息中止(指示用户运行缺失的先决条件命令).
对于参数中的单引号, 如 "I'm Groot", 使用转义语法: 例如 'I'\''m Groot'(或尽可能使用双引号: "I'm Groot").
### 2. 加载制品(渐进式展示)
仅从每个制品加载最小必需的上下文:
**从 spec.md: **
- 概述/上下文
- 功能需求
- 非功能需求
- 用户故事
- 边缘情况(如果存在)
**从 plan.md: **
- 架构/技术栈选择
- 数据模型引用
- 阶段
- 技术约束
**从 tasks.md: **
- 任务 ID
- 描述
- 阶段分组
- 并行标记 [P]
- 引用的文件路径
**从章程: **
- 加载 `.specify/memory/constitution.md` 进行原则验证
### 3. 构建语义模型
创建内部表示(输出中不包含原始制品):
- **需求清单**: 每个功能和非功能需求, 带有稳定键(基于祈使短语推导 slug; 例如, "User can upload file" → `user-can-upload-file`)
- **用户故事/操作清单**: 带有验收标准的离散用户操作
- **任务覆盖映射**: 将每个任务映射到一个或多个需求或故事(通过关键词/显式引用模式推断, 如 ID 或关键短语)
- **章程规则集**: 提取原则名称和 MUST/SHOULD 规范性声明
### 4. 检测过程(高效令牌分析)
专注于高信号发现. 限制总共 50 个发现; 在溢出摘要中聚合其余部分.
#### A. 重复检测
- 识别近似重复的需求
- 标记较低质量的表述以进行合并
#### B. 模糊性检测
- 标记缺乏可测量标准的模糊形容词(快速、可扩展、安全、直观、稳健)
- 标记未解决的占位符(TODO、TKTK、???、`<placeholder>` 等)
#### C. 规范不足
- 有动词但缺少对象或可测量结果的需求
- 缺少验收标准对齐的用户故事
- 引用规范/计划中未定义的文件或组件的任务
#### D. 章程对齐
- 与 MUST 原则冲突的任何需求或计划元素
- 章程中缺失的强制部分或质量门控
#### E. 覆盖缺口
- 没有关联任务的需求
- 没有映射需求/故事的任务
- 未在任务中反映的非功能需求(例如, 性能、安全性)
#### F. 不一致性
- 术语漂移(相同概念在不同文件中命名不同)
- 计划中引用但在规范中缺失的数据实体(反之亦然)
- 任务排序矛盾(例如, 集成任务在基础设置任务之前而没有依赖说明)
- 冲突需求(例如, 一个要求 Next.js 而另一个指定 Vue)
### 5. 严重性分配
使用此启发式方法对发现进行优先级排序:
- **严重**: 违反章程 MUST、缺失核心规范制品, 或零覆盖的需求阻止基线功能
- **高**: 重复或冲突需求、模糊的安全/性能属性、不可测试的验收标准
- **中**: 术语漂移、缺失非功能任务覆盖、规范不足的边缘情况
- **低**: 风格/措辞改进、不影响执行顺序的轻微冗余
### 6. 生成紧凑分析报告
输出 Markdown 报告(不写入文件), 结构如下:
## 规范分析报告
| ID | 类别 | 严重性 | 位置 | 摘要 | 建议 |
|----|------|--------|------|------|------|
| A1 | 重复 | 高 | spec.md:L120-134 | 两个相似需求... | 合并表述; 保留更清晰的版本 |
(每个发现添加一行; 生成以类别首字母为前缀的稳定 ID. )
**覆盖摘要表: **
| 需求键 | 有任务? | 任务 ID | 备注 |
|--------|----------|---------|------|
**章程对齐问题: **(如果有)
**未映射任务: **(如果有)
**指标: **
- 总需求数
- 总任务数
- 覆盖率%(有 >=1 个任务的需求)
- 模糊性计数
- 重复计数
- 严重问题计数
### 7. 提供下一步操作
在报告末尾, 输出简洁的下一步操作块:
- 如果存在严重问题: 建议在 `/speckit.implement` 之前解决
- 如果只有低/中问题: 用户可以继续, 但提供改进建议
- 提供明确的命令建议: 例如, "运行 /speckit.specify 进行细化"、"运行 /speckit.plan 调整架构"、"手动编辑 tasks.md 为 'performance-metrics' 添加覆盖"
### 8. 提供修复
询问用户: "你希望我为前 N 个问题建议具体的修复编辑吗?"(不要自动应用它们. )
## 操作原则
### 上下文效率
- **最小高信噪比令牌**: 专注于可操作的发现, 而不是详尽的文档
- **渐进式展示**: 增量加载制品; 不要将所有内容倾倒到分析中
- **高效令牌输出**: 限制发现表为 50 行; 总结溢出部分
- **确定性结果**: 无更改重新运行应产生一致的 ID 和计数
### 分析指南
- **绝不修改文件**(这是只读分析)
- **绝不虚构缺失部分**(如果缺失, 准确报告)
- **优先处理章程违规**(这些总是严重的)
- **使用示例而非详尽规则**(引用具体实例, 而不是通用模式)
- **优雅报告零问题**(发出带有覆盖统计的成功报告)
## 上下文
$ARGUMENTS
-287
View File
@@ -1,287 +0,0 @@
---
description: 基于用户需求为当前功能生成自定义检查清单.
---
## 清单目的: "需求编写的单元测试"
**核心概念**: 清单是**需求编写的单元测试** - 它们验证特定领域中需求的质量、清晰度和完整性.
**不用于验证/测试**:
- ❌ 不是"验证按钮点击正确"
- ❌ 不是"测试错误处理有效"
- ❌ 不是"确认 API 返回 200"
- ❌ 不是检查代码/实现是否符合规范
**用于需求质量验证**:
- ✅ "是否为所有卡片类型定义了视觉层次需求?"(完整性)
- ✅ "'突出显示'是否通过具体尺寸/位置进行了量化?"(清晰度)
- ✅ "所有交互元素的悬停状态需求是否一致?"(一致性)
- ✅ "是否为键盘导航定义了可访问性需求?"(覆盖度)
- ✅ "规范是否定义了 logo 图像加载失败时的处理?"(边缘情况)
**比喻**: 如果你的规范是用英文编写的代码, 那么清单就是它的单元测试套件. 你测试的是需求是否编写良好、完整、明确并准备好实施 - 而不是实现是否有效.
## 用户输入
```text
$ARGUMENTS
```
在继续之前, 你**必须**考虑用户输入(如果不为空).
## 执行步骤
1. **设置**: 从仓库根目录运行 `.specify/scripts/powershell/check-prerequisites.ps1 -Json` 并解析JSON以获取FEATURE_DIR和AVAILABLE_DOCS列表.
- 所有文件路径必须是绝对路径.
- 对于参数中的单引号如"I'm Groot", 使用转义语法: 例如 'I'\''m Groot'(或者尽可能使用双引号: "I'm Groot").
2. **澄清意图(动态)**: 推导最多三个初始上下文澄清问题(无预编目录). 它们必须:
- 从用户的表述 + 从规范/计划/任务中提取的信号生成
- 只询问实质上改变清单内容的信息
- 如果在`$ARGUMENTS`中已经明确, 则单独跳过
- 优先考虑精确性而非广度
Generation algorithm:
1. Extract signals: feature domain keywords (e.g., auth, latency, UX, API), risk indicators ("critical", "must", "compliance"), stakeholder hints ("QA", "review", "security team"), and explicit deliverables ("a11y", "rollback", "contracts").
2. Cluster signals into candidate focus areas (max 4) ranked by relevance.
3. Identify probable audience & timing (author, reviewer, QA, release) if not explicit.
4. Detect missing dimensions: scope breadth, depth/rigor, risk emphasis, exclusion boundaries, measurable acceptance criteria.
5. Formulate questions chosen from these archetypes:
- Scope refinement (e.g., "Should this include integration touchpoints with X and Y or stay limited to local module correctness?")
- Risk prioritization (e.g., "Which of these potential risk areas should receive mandatory gating checks?")
- Depth calibration (e.g., "Is this a lightweight pre-commit sanity list or a formal release gate?")
- Audience framing (e.g., "Will this be used by the author only or peers during PR review?")
- Boundary exclusion (e.g., "Should we explicitly exclude performance tuning items this round?")
- Scenario class gap (e.g., "No recovery flows detected—are rollback / partial failure paths in scope?")
Question formatting rules:
- If presenting options, generate a compact table with columns: Option | Candidate | Why It Matters
- Limit to AE options maximum; omit table if a free-form answer is clearer
- Never ask the user to restate what they already said
- Avoid speculative categories (no hallucination). If uncertain, ask explicitly: "Confirm whether X belongs in scope."
Defaults when interaction impossible:
- Depth: Standard
- Audience: Reviewer (PR) if code-related; Author otherwise
- Focus: Top 2 relevance clusters
Output the questions (label Q1/Q2/Q3). After answers: if ≥2 scenario classes (Alternate / Exception / Recovery / Non-Functional domain) remain unclear, you MAY ask up to TWO more targeted followups (Q4/Q5) with a one-line justification each (e.g., "Unresolved recovery path risk"). Do not exceed five total questions. Skip escalation if user explicitly declines more.
3. **理解用户请求**: 结合 `$ARGUMENTS` + 澄清答案:
- 推导清单主题(例如: security, review, deploy, ux)
- 整合用户明确提到的必需项目
- 将焦点选择映射到类别框架
- 从规范/计划/任务中推断任何缺失的上下文(不要虚构)
4. **加载功能上下文**: 从 FEATURE_DIR 读取:
- spec.md: 功能需求和范围
- plan.md(如果存在): 技术细节、依赖关系
- tasks.md(如果存在): 实施任务
**上下文加载策略**:
- 仅加载与活动焦点区域相关的必要部分(避免全文转储)
- 优先将长部分总结为简洁的场景/需求要点
- 使用渐进式披露: 仅在检测到差距时添加后续检索
- 如果源文档很大, 生成临时摘要项目而不是嵌入原始文本
5. **生成清单** - 创建"需求的单元测试":
- Create `FEATURE_DIR/checklists/` directory if it doesn't exist
- Generate unique checklist filename:
- Use short, descriptive name based on domain (e.g., `ux.md`, `api.md`, `security.md`)
- Format: `[domain].md`
- If file exists, append to existing file
- Number items sequentially starting from CHK001
- Each `/speckit.checklist` run creates a NEW file (never overwrites existing checklists)
**核心原则 - 测试需求, 而非实现**:
每个清单项目必须评估需求本身, 检查:
- **完整性**: 所有必要的需求是否存在?
- **清晰度**: 需求是否明确无歧义且具体?
- **一致性**: 需求之间是否相互一致?
- **可测量性**: 需求是否可以客观验证?
- **覆盖度**: 是否涵盖了所有场景/边缘情况?
**类别结构** - 按需求质量维度分组项目:
- **需求完整性**(所有必要的需求是否已记录?)
- **需求清晰度**(需求是否具体且无歧义?)
- **需求一致性**(需求是否一致且无冲突?)
- **验收标准质量**(成功标准是否可测量?)
- **场景覆盖度**(是否涵盖了所有流程/情况?)
- **边缘情况覆盖度**(是否定义了边界条件?)
- **非功能性需求**(性能、安全性、可访问性等 - 是否已指定?)
- **依赖关系和假设**(是否已记录和验证?)
- **歧义和冲突**(什么 NEEDS CLARIFICATION)
**如何编写清单项目 - "需求编写的单元测试"**:
**WRONG** (Testing implementation):
- "Verify landing page displays 3 episode cards"
- "Test hover states work on desktop"
- "Confirm logo click navigates home"
**CORRECT** (Testing requirements quality):
- "Are the exact number and layout of featured episodes specified?" [Completeness]
- "Is 'prominent display' quantified with specific sizing/positioning?" [Clarity]
- "Are hover state requirements consistent across all interactive elements?" [Consistency]
- "Are keyboard navigation requirements defined for all interactive UI?" [Coverage]
- "Is the fallback behavior specified when logo image fails to load?" [Edge Cases]
- "Are loading states defined for asynchronous episode data?" [Completeness]
- "Does the spec define visual hierarchy for competing UI elements?" [Clarity]
**项目结构**:
Each item should follow this pattern:
- Question format asking about requirement quality
- Focus on what's WRITTEN (or not written) in the spec/plan
- Include quality dimension in brackets [Completeness/Clarity/Consistency/etc.]
- Reference spec section `[Spec §X.Y]` when checking existing requirements
- Use `[Gap]` marker when checking for missing requirements
**按质量维度分类的示例**:
完整性:
- "Are error handling requirements defined for all API failure modes? [Gap]"
- "Are accessibility requirements specified for all interactive elements? [Completeness]"
- "Are mobile breakpoint requirements defined for responsive layouts? [Gap]"
清晰度:
- "Is 'fast loading' quantified with specific timing thresholds? [Clarity, Spec §NFR-2]"
- "Are 'related episodes' selection criteria explicitly defined? [Clarity, Spec §FR-5]"
- "Is 'prominent' defined with measurable visual properties? [Ambiguity, Spec §FR-4]"
一致性:
- "Do navigation requirements align across all pages? [Consistency, Spec §FR-10]"
- "Are card component requirements consistent between landing and detail pages? [Consistency]"
覆盖度:
- "Are requirements defined for zero-state scenarios (no episodes)? [Coverage, Edge Case]"
- "Are concurrent user interaction scenarios addressed? [Coverage, Gap]"
- "Are requirements specified for partial data loading failures? [Coverage, Exception Flow]"
可测量性:
- "Are visual hierarchy requirements measurable/testable? [Acceptance Criteria, Spec §FR-1]"
- "Can 'balanced visual weight' be objectively verified? [Measurability, Spec §FR-2]"
**场景分类与覆盖度**(需求质量焦点):
- Check if requirements exist for: Primary, Alternate, Exception/Error, Recovery, Non-Functional scenarios
- For each scenario class, ask: "Are [scenario type] requirements complete, clear, and consistent?"
- If scenario class missing: "Are [scenario type] requirements intentionally excluded or missing? [Gap]"
- Include resilience/rollback when state mutation occurs: "Are rollback requirements defined for migration failures? [Gap]"
**可追溯性要求**:
- MINIMUM: ≥80% of items MUST include at least one traceability reference
- Each item should reference: spec section `[Spec §X.Y]`, or use markers: `[Gap]`, `[Ambiguity]`, `[Conflict]`, `[Assumption]`
- If no ID system exists: "Is a requirement & acceptance criteria ID scheme established? [Traceability]"
**发现和解决问题**(需求质量问题):
Ask questions about the requirements themselves:
- Ambiguities: "Is the term 'fast' quantified with specific metrics? [Ambiguity, Spec §NFR-1]"
- Conflicts: "Do navigation requirements conflict between §FR-10 and §FR-10a? [Conflict]"
- Assumptions: "Is the assumption of 'always available podcast API' validated? [Assumption]"
- Dependencies: "Are external podcast API requirements documented? [Dependency, Gap]"
- Missing definitions: "Is 'visual hierarchy' defined with measurable criteria? [Gap]"
**内容整合**:
- Soft cap: If raw candidate items > 40, prioritize by risk/impact
- Merge near-duplicates checking the same requirement aspect
- If >5 low-impact edge cases, create one item: "Are edge cases X, Y, Z addressed in requirements? [Coverage]"
**🚫 ABSOLUTELY PROHIBITED** - These make it an implementation test, not a requirements test:
- ❌ Any item starting with "Verify", "Test", "Confirm", "Check" + implementation behavior
- ❌ References to code execution, user actions, system behavior
- ❌ "Displays correctly", "works properly", "functions as expected"
- ❌ "Click", "navigate", "render", "load", "execute"
- ❌ Test cases, test plans, QA procedures
- ❌ Implementation details (frameworks, APIs, algorithms)
**✅ REQUIRED PATTERNS** - These test requirements quality:
- ✅ "Are [requirement type] defined/specified/documented for [scenario]?"
- ✅ "Is [vague term] quantified/clarified with specific criteria?"
- ✅ "Are requirements consistent between [section A] and [section B]?"
- ✅ "Can [requirement] be objectively measured/verified?"
- ✅ "Are [edge cases/scenarios] addressed in requirements?"
- ✅ "Does the spec define [missing aspect]?"
6. **结构参考**: 按照 `.specify/templates/checklist-template.md` 中的规范模板生成清单, 包括标题、元部分、类别标题和 ID 格式. 如果模板不可用, 使用: H1 标题、purpose/created 元行、包含 `- [ ] CHK### <requirement item>` 行的 `##` 类别部分, ID 从 CHK001 开始全局递增.
7. **报告**: 输出创建清单的完整路径、项目数量, 并提醒用户每次运行都会创建新文件. 总结:
- 选择的焦点区域
- 深度级别
- 执行者/时间
- 任何整合的用户明确指定的必需项目
**重要说明**: 每次 `/speckit.checklist` 命令调用都会创建一个使用简短描述性名称的清单文件, 除非文件已存在. 这允许:
- 创建多种不同类型的清单(例如: `ux.md`, `test.md`, `security.md`)
- 使用简单、易记的文件名来表明清单用途
-`checklists/` 文件夹中轻松识别和导航
为避免混乱, 请使用描述性类型, 并在完成后清理过时的清单.
## 示例清单类型和示例项目
**UX 需求质量**: `ux.md`
示例项目(测试需求, 而非实现):
- "Are visual hierarchy requirements defined with measurable criteria? [Clarity, Spec §FR-1]"
- "Is the number and positioning of UI elements explicitly specified? [Completeness, Spec §FR-1]"
- "Are interaction state requirements (hover, focus, active) consistently defined? [Consistency]"
- "Are accessibility requirements specified for all interactive elements? [Coverage, Gap]"
- "Is fallback behavior defined when images fail to load? [Edge Case, Gap]"
- "Can 'prominent display' be objectively measured? [Measurability, Spec §FR-4]"
**API 需求质量**: `api.md`
示例项目:
- "Are error response formats specified for all failure scenarios? [Completeness]"
- "Are rate limiting requirements quantified with specific thresholds? [Clarity]"
- "Are authentication requirements consistent across all endpoints? [Consistency]"
- "Are retry/timeout requirements defined for external dependencies? [Coverage, Gap]"
- "Is versioning strategy documented in requirements? [Gap]"
**性能需求质量**: `performance.md`
示例项目:
- "Are performance requirements quantified with specific metrics? [Clarity]"
- "Are performance targets defined for all critical user journeys? [Coverage]"
- "Are performance requirements under different load conditions specified? [Completeness]"
- "Can performance requirements be objectively measured? [Measurability]"
- "Are degradation requirements defined for high-load scenarios? [Edge Case, Gap]"
**安全需求质量**: `security.md`
示例项目:
- "Are authentication requirements specified for all protected resources? [Coverage]"
- "Are data protection requirements defined for sensitive information? [Completeness]"
- "Is the threat model documented and requirements aligned to it? [Traceability]"
- "Are security requirements consistent with compliance obligations? [Consistency]"
- "Are security failure/breach response requirements defined? [Gap, Exception Flow]"
## 反例: 什么不要做
**❌ 错误 - 这些测试实现, 而非需求: **
```markdown
- [ ] CHK001 - Verify landing page displays 3 episode cards [Spec §FR-001]
- [ ] CHK002 - Test hover states work correctly on desktop [Spec §FR-003]
- [ ] CHK003 - Confirm logo click navigates to home page [Spec §FR-010]
- [ ] CHK004 - Check that related episodes section shows 3-5 items [Spec §FR-005]
```
**✅ 正确 - 这些测试需求质量: **
```markdown
- [ ] CHK001 - Are the number and layout of featured episodes explicitly specified? [Completeness, Spec §FR-001]
- [ ] CHK002 - Are hover state requirements consistently defined for all interactive elements? [Consistency, Spec §FR-003]
- [ ] CHK003 - Are navigation requirements clear for all clickable brand elements? [Clarity, Spec §FR-010]
- [ ] CHK004 - Is the selection criteria for related episodes documented? [Gap, Spec §FR-005]
- [ ] CHK005 - Are loading state requirements defined for asynchronous episode data? [Gap]
- [ ] CHK006 - Can "visual hierarchy" requirements be objectively measured? [Measurability, Spec §FR-001]
```
**关键区别: **
- 错误: 测试系统是否正常工作
- 正确: 测试需求是否编写正确
- 错误: 验证行为
- 正确: 验证需求质量
- 错误: "它是否做 X"
- 正确: "X 是否明确指定?"
-179
View File
@@ -1,179 +0,0 @@
---
description: 通过提出最多 5 个高度针对性的澄清问题, 识别当前功能规范中未充分说明的领域, 并将答案编码回规范中.
handoffs:
- label: 构建技术计划
agent: speckit.plan
prompt: 为规范创建计划。我正在构建...
---
## 用户输入
```text
$ARGUMENTS
```
在继续之前, 你**必须**考虑用户输入(如果不为空).
## 概述
目标: 检测并减少活跃功能规范中的模糊性或缺失的决策点, 并将澄清内容直接记录在规范文件中.
注意: 此澄清工作流应在调用 `/speckit.plan` 之前运行(并完成). 如果用户明确表示他们跳过澄清(例如, 探索性原型), 你可以继续, 但必须警告下游返工风险增加.
执行步骤:
1. 从仓库根目录运行 `.specify/scripts/powershell/check-prerequisites.ps1 -Json -PathsOnly` **一次**(组合 `--json --paths-only` 模式 / `-Json -PathsOnly`). 解析最小 JSON 负载字段:
- `FEATURE_DIR`
- `FEATURE_SPEC`
- (可选捕获 `IMPL_PLAN``TASKS` 用于未来的链式流程. )
- 如果 JSON 解析失败, 中止并指示用户重新运行 `/speckit.specify` 或验证功能分支环境.
- 对于参数中包含单引号的情况(如 "I'm Groot"), 使用转义语法: 例如 'I'\''m Groot'(或优先使用双引号: "I'm Groot").
2. 加载当前规范文件. 使用此分类法执行结构化模糊性和覆盖范围扫描. 对于每个类别, 标记状态: 清晰 / 部分 / 缺失. 生成用于优先级排序的内部覆盖范围图(除非不会提问, 否则不输出原始图).
功能范围与行为:
- 核心用户目标和成功标准
- 明确的超出范围声明
- 用户角色 / 角色区分
领域与数据模型:
- 实体、属性、关系
- 身份和唯一性规则
- 生命周期 / 状态转换
- 数据量 / 规模假设
交互与 UX 流程:
- 关键用户旅程 / 序列
- 错误 / 空白 / 加载状态
- 可访问性或本地化说明
非功能性质量属性:
- 性能(延迟、吞吐量目标)
- 可扩展性(水平 / 垂直、限制)
- 可靠性和可用性(正常运行时间、恢复期望)
- 可观察性(日志、指标、追踪信号)
- 安全性和隐私(身份验证 / 授权、数据保护、威胁假设)
- 合规性 / 监管约束(如有)
集成与外部依赖:
- 外部服务 / API 和故障模式
- 数据导入 / 导出格式
- 协议 / 版本控制假设
边缘情况与故障处理:
- 负面场景
- 速率限制 / 节流
- 冲突解决(例如, 并发编辑)
约束与权衡:
- 技术约束(语言、存储、托管)
- 明确的权衡或被拒绝的替代方案
术语与一致性:
- 规范术语表术语
- 避免的同义词 / 已弃用术语
完成信号:
- 验收标准可测试性
- 可衡量的完成定义风格指标
其他 / 占位符:
- TODO 标记 / 未解决的决策
- 缺少量化的模糊形容词("robust"、"intuitive")
对于每个处于部分或缺失状态的类别, 添加候选问题机会, 除非:
- 澄清不会实质性地改变实施或验证策略
- 信息更适合推迟到规划阶段(内部记录)
3. (内部)生成候选澄清问题的优先级队列(最多5个). 不要一次性输出所有问题. 应用这些约束:
- 整个会话最多10个问题.
- 每个问题必须可以用以下任一方式回答:
* 简短的多项选择(2-5个不同的、互斥的选项), 或
* 单词 / 短语答案(明确约束: "Answer in <=5 words").
- 仅包含答案实质上影响架构、数据建模、任务分解、测试设计、UX 行为、运营准备或合规性验证的问题.
- 确保类别覆盖平衡: 尝试首先覆盖最高影响的未解决类别; 避免在单个高影响领域(例如, 安全态势)未解决时询问两个低影响问题.
- 排除已回答的问题、琐碎的风格偏好或规划级别的执行细节(除非阻碍正确性).
- 偏好减少下游返工风险或防止不一致验收测试的澄清.
- 如果超过5个类别仍未解决, 通过(影响 * 不确定性)启发式选择前5个.
4. 顺序提问流程(交互式):
- 一次只提出**确切一个**问题.
- 对于多项选择题:
* **分析所有选项**并基于以下确定**最合适的选项**:
- 项目类型的最佳实践
- 类似实现中的常见模式
- 风险降低(安全性、性能、可维护性)
- 与规范中可见的任何明确项目目标或约束的一致性
* 在顶部突出显示你的**推荐选项**并附上清晰推理(1-2句话解释为什么这是最佳选择).
* 格式为: `**Recommended:** Option [X] - <reasoning>`
* 然后将所有选项渲染为 Markdown 表格:
| Option | Description |
|--------|-------------|
| A | <Option A description> |
| B | <Option B description> |
| C | <Option C description> | (根据需要添加 D/E, 最多5个)
| Short | Provide a different short answer (<=5 words) | (仅在自由形式替代方案合适时包含)
* 表格后添加: `You can reply with the option letter (e.g., "A"), accept the recommendation by saying "yes" or "recommended", or provide your own short answer.`
- 对于简答题风格(没有有意义的不同选项):
* 基于最佳实践和上下文提供你的**建议答案**.
* 格式为: `**Suggested:** <your proposed answer> - <brief reasoning>`
* 然后输出: `Format: Short answer (<=5 words). You can accept the suggestion by saying "yes" or "suggested", or provide your own answer.`
- 用户回答后:
* 如果用户回复 "yes"、"recommended" 或 "suggested", 使用你之前陈述的推荐 / 建议作为答案.
* 否则, 验证答案映射到一个选项或符合 <=5 个词的约束.
* 如果模糊, 要求快速消除歧义(计数仍属于同一问题; 不前进).
* 一旦满意, 将其记录在工作内存中(尚未写入磁盘)并移动到下一个排队的问题.
- 在以下情况时停止进一步提问:
* 所有关键模糊性早期解决(剩余排队项目变得不必要), 或
* 用户发出完成信号("done"、"good"、"no more"), 或
* 你达到5个已问问题.
- 永远不要提前揭示未来的排队问题.
- 如果开始时没有有效问题, 立即报告没有关键模糊性.
5. 每次接受答案后的集成(增量更新方法):
- 维护规范的内存表示(开始时加载一次)加上原始文件内容.
- 对于此会话中第一个集成的答案:
* 确保 `## Clarifications` 部分存在(如果缺失, 根据规范模板在最高级别的上下文 / 概述部分之后创建).
* 在其下, 创建(如果不存在)今天的 `### Session YYYY-MM-DD` 子标题.
- 接受后立即追加项目符号行: `- Q: <question> → A: <final answer>`.
- 然后立即将澄清应用到最合适的部分:
* 功能模糊性 → 更新或在功能需求中添加项目符号.
* 用户交互 / 角色区分 → 更新用户故事或角色子部分(如果存在), 包含澄清的角色、约束或场景.
* 数据形状 / 实体 → 更新数据模型(添加字段、类型、关系)保持顺序; 简洁地记录添加的约束.
* 非功能性约束 → 在非功能性 / 质量属性部分添加 / 修改可衡量标准(将模糊形容词转换为指标或明确目标).
* 边缘情况 / 负面流程 → 在边缘情况 / 错误处理下添加新项目符号(或如果模板提供占位符则创建此类子部分).
* 术语冲突 → 在整个规范中规范化术语; 仅在必要时通过添加一次 `(formerly referred to as "X")` 保留原始术语.
- 如果澄清使早期模糊声明无效, 替换该声明而不是重复; 不留用过时的矛盾文本.
- 每次集成后保存规范文件以最小化上下文丢失风险(原子覆盖).
- 保持格式: 不重新排序不相关的部分; 保持标题层次结构完整.
- 保持每个插入的澄清最小化和可测试(避免叙述性偏离).
6. 验证(每次写入后执行, 最终再进行一次完整检查):
- 澄清会话每个接受的答案只包含一个项目符号(无重复).
- 总共询问(接受)的问题 ≤ 5.
- 更新的部分不包含新答案旨在解决的持续模糊占位符.
- 没有矛盾的早期声明保留(扫描已删除的现在无效的替代选择).
- Markdown 结构有效; 仅允许新标题: `## Clarifications``### Session YYYY-MM-DD`.
- 术语一致性: 所有更新部分使用相同的规范术语.
7. 将更新的规范写回 `FEATURE_SPEC`.
8. 报告完成(提问循环结束或提前终止后):
- 询问和回答的问题数量.
- 更新规范的路径.
- 涉及的部分(列出名称).
- 覆盖范围摘要表, 列出每个分类类别及状态: 已解决(曾是部分 / 缺失并已处理)、已推迟(超过问题配额或更适合规划)、清晰(已足够)、未完成(仍是部分 / 缺失但影响低).
- 如果有任何未完成或已推迟的剩余, 建议是继续到 `/speckit.plan` 还是在规划后再次运行 `/speckit.clarify`.
- 建议的下一个命令.
行为规则:
- 如果没有发现有意义的模糊性(或所有潜在问题都是低影响的), 回应: "No critical ambiguities detected worth formal clarification." 并建议继续.
- 如果规范文件缺失, 指示用户首先运行 `/speckit.specify`(不要在此创建新规范).
- 永远不要超过总共5个询问的问题(单个问题的澄清重试不计为新问题).
- 避免推测性技术堆栈问题, 除非缺失阻碍功能清晰性.
- 尊重用户提前终止信号("stop"、"done"、"proceed").
- 如果由于完全覆盖而没有提问, 输出紧凑的覆盖范围摘要(所有类别清晰)然后建议前进.
- 如果达到配额但仍有未解决的高影响类别, 在已推迟下明确标记它们并附上理由.
优先级排序的上下文: $ARGUMENTS
-81
View File
@@ -1,81 +0,0 @@
---
description: 从交互式或提供的原则输入创建或更新项目章程, 确保所有依赖模板保持同步.
handoffs:
- label: 构建规范
agent: speckit.specify
prompt: 基于更新的章程实现功能规范。我想要构建...
---
## 用户输入
```text
$ARGUMENTS
```
在继续之前, 你**必须**考虑用户输入(如果不为空).
## 概述
你正在更新位于 `.specify/memory/constitution.md` 的项目章程. 该文件是一个包含方括号占位符标记的模板(例如 `[PROJECT_NAME]``[PRINCIPLE_1_NAME]`). 你的工作是(a)收集/推导具体值, (b)精确填充模板, 以及(c)将任何修改传播到相关依赖项中.
遵循此执行流程:
1. 加载位于 `.specify/memory/constitution.md` 的现有章程模板.
- 识别每个 `[ALL_CAPS_IDENTIFIER]` 形式的占位符标记.
**重要提示**: 用户可能需要比模板中使用的原则更少或更多. 如果指定了数字, 请遵循该数字 - 遵循通用模板. 你将相应地更新文档.
2. 为占位符收集/推导值:
- 如果用户输入(对话)提供了值, 则使用它.
- 否则从现有仓库上下文推断(README、文档、先前的章程版本(如果嵌入)).
- 对于治理日期: `RATIFICATION_DATE` 是原始采用日期(如果未知, 请询问或标记 TODO), `LAST_AMENDED_DATE` 是今天(如果进行了更改), 否则保留之前的日期.
- `CONSTITUTION_VERSION` 必须根据语义版本控制规则递增:
* MAJOR(主版本): 向后不兼容的治理/原则删除或重新定义.
* MINOR(次版本): 新原则/部分添加或实质性扩展指导.
* PATCH(补丁版本): 澄清、措辞、拼写错误修复、非语义优化.
- 如果版本递增类型不明确, 请在最终确定之前提出理由.
3. 起草更新后的章程内容:
- 用具体文本替换每个占位符(除了项目选择尚未定义的故意保留的模板槽位外, 不留下括号标记——明确说明任何保留的槽位).
- 保留标题层次结构, 一旦替换可以删除注释, 除非它们仍然提供澄清指导.
- 确保每个原则部分: 简洁的名称行、捕获不可协商规则的段落(或项目符号列表)、如果不明显则提供明确的理由.
- 确保治理部分列出修改程序、版本控制策略和合规审查期望.
4. 一致性传播检查清单(将先前的检查清单转换为主动验证):
- 读取 `.specify/templates/plan-template.md` 并确保任何"章程检查"或规则与更新的原则保持一致.
- 读取 `.specify/templates/spec-template.md` 进行范围/需求对齐——如果章程添加/删除强制部分或约束, 则更新.
- 读取 `.specify/templates/tasks-template.md` 并确保任务分类反映新的或删除的原则驱动的任务类型(例如, 可观测性、版本控制、测试纪律).
- 读取 `.specify/templates/commands/*.md` 中的每个命令文件(包括此文件)以验证在需要通用指导时没有过时的引用(如仅限 CLAUDE 的代理特定名称).
- 读取任何运行时指导文档(例如 `README.md``docs/quickstart.md` 或代理特定指导文件(如果存在)). 更新对已更改原则的引用.
5. 生成同步影响报告(在更新后作为 HTML 注释前置到章程文件顶部):
- 版本更改: 旧版本 → 新版本
- 修改的原则列表(如果重命名, 则为旧标题 → 新标题)
- 添加的部分
- 删除的部分
- 需要更新的模板(✅ 已更新 / ⚠ 待处理)及文件路径
- 如果有任何占位符故意延迟, 则提供后续 TODO.
6. 最终输出前的验证:
- 没有剩余的未解释的括号标记.
- 版本行与报告匹配.
- 日期采用 ISO 格式 YYYY-MM-DD.
- 原则是声明性的、可测试的, 并且没有模糊语言("should" → 在适当的地方用 MUST/SHOULD 理由替换).
7. 将完成的章程写回 `.specify/memory/constitution.md`(覆盖).
8. 向用户输出最终摘要, 包括:
- 新版本和递增理由.
- 任何标记为手动后续处理的文件.
- 建议的提交消息(例如 `docs: amend constitution to vX.Y.Z (principle additions + governance update)`).
格式和样式要求:
- 完全按照模板使用 Markdown 标题(不要降级/升级级别).
- 换行长理由行以保持可读性(理想情况下 <100 个字符), 但不要用尴尬的换行强制执行.
- 在部分之间保持单个空行.
- 避免尾随空白.
如果用户提供部分更新(例如, 仅一个原则修订), 仍然执行验证和版本决策步骤.
如果缺少关键信息(例如, 批准日期确实未知), 请插入 `TODO(<FIELD_NAME>): explanation` 并包含在同步影响报告中的延迟项下.
不要创建新模板; 始终操作现有的 `.specify/memory/constitution.md` 文件.
-74
View File
@@ -1,74 +0,0 @@
---
description: 针对项目中的bug进行精确修复,尽量减少对其他代码的影响
---
## 用户输入
```text
$ARGUMENTS
```
在继续之前,你**必须**考虑用户输入(如果不为空)。用户应该提供bug描述、相关文件路径和可能的修复方向。
## 目标
提供一个结构化的bug修复流程,通过精确的修改定位和最小化的代码变更,修复项目中的bug,同时避免引入新的问题或破坏现有功能。
## 操作约束
**最小化修改原则**:只修改与bug直接相关的代码,避免对其他功能造成影响。
**完整性保证**:确保bug修复是完整的,不仅解决表面症状,还要处理根本原因。
**测试优先**:在进行修复前,确保有适当的测试来验证bug的存在和修复的有效性。
**文档更新**:如果bug修复影响了API或行为,确保更新相应的文档。
## 执行步骤
### 1. 初始化bug修复上下文
从仓库根目录运行一次 `.specify/scripts/powershell/check-prerequisites.ps1 -Json` 并解析 JSON 以获取项目环境信息。这将帮助我们了解项目的基本结构和工具链。
### 2. 收集和分析bug信息
- **理解bug**:根据用户输入,明确理解bug的症状、复现步骤和预期行为
- **定位相关文件**:确定可能包含bug的文件和代码区域
- **分析错误日志**:如果有错误日志,分析异常堆栈和错误信息
- **版本历史检查**:查看最近的代码变更,识别可能引入bug的提交
### 3. 创建修复环境
- **创建bug修复分支**:基于当前主分支创建一个专门的bug修复分支
- **设置测试环境**:确保有适当的测试环境可以验证bug和修复
- **备份相关文件**:在修改前备份可能受影响的关键文件
### 4. 实施修复
- **精确修改**:针对已识别的问题进行最小化的代码修改
- **遵循代码风格**:确保修复代码遵循项目现有的代码风格和规范
- **添加必要注释**:为修复添加清晰的注释,说明修复的原因和方法
- **考虑边界情况**:确保修复不会在边界情况下导致新的问题
### 5. 验证修复
- **执行测试**:运行项目的测试套件,确保所有测试通过
- **复现验证**:使用原始的复现步骤,确认bug已被修复
- **回归测试**:执行可能受影响功能的回归测试
- **性能检查**:如果适用,验证修复不会对性能造成负面影响
### 6. 文档和提交
- **更新文档**:如果修复影响了API或功能行为,更新相应的文档
- **编写清晰的提交信息**:提交信息应包含bug描述、修复方法和相关问题引用
- **代码审查准备**:准备修复的代码审查材料,包括问题描述、修复策略和测试结果
## 输出格式
修复过程完成后,输出一个结构化的报告,包括:
1. **Bug摘要**:对bug的简要描述和影响范围
2. **修复概述**:所做的修改概述和修复原理
3. **验证结果**:测试结果和验证方法
4. **潜在风险**:任何可能的风险或需要后续关注的问题
5. **建议的后续步骤**:例如代码审查、部署计划等
-131
View File
@@ -1,131 +0,0 @@
---
description: 通过处理和执行 tasks.md 中定义的所有任务来执行实施计划
---
## 用户输入
```text
$ARGUMENTS
```
在继续之前, 你**必须**考虑用户输入(如果不为空).
## 概要
1. 从仓库根目录运行 `.specify/scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks` 并解析 FEATURE_DIR 和 AVAILABLE_DOCS 列表. 所有路径必须是绝对路径. 对于参数中的单引号如 "I'm Groot", 使用转义语法: 例如 'I'\''m Groot'(或尽可能使用双引号: "I'm Groot").
2. **检查清单状态**(如果 FEATURE_DIR/checklists/ 存在):
- 扫描 checklists/ 目录中的所有清单文件
- 对于每个清单, 统计:
* 总项目数: 所有匹配 `- [ ]``- [X]``- [x]` 的行
* 已完成项目数: 匹配 `- [X]``- [x]` 的行
* 未完成项目数: 匹配 `- [ ]` 的行
- 创建状态表:
```
| Checklist | Total | Completed | Incomplete | Status |
|-----------|-------|-----------|------------|--------|
| ux.md | 12 | 12 | 0 | ✓ PASS |
| test.md | 8 | 5 | 3 | ✗ FAIL |
| security.md | 6 | 6 | 0 | ✓ PASS |
```
- 计算总体状态:
* **PASS**: 所有清单都有 0 个未完成项目
* **FAIL**: 一个或多个清单有未完成项目
- **如果任何清单未完成**:
* 显示包含未完成项目数的表格
* **停止**并询问: "Some checklists are incomplete. Do you want to proceed with implementation anyway? (yes/no)"
* 在继续之前等待用户响应
* 如果用户说 "no" 或 "wait" 或 "stop", 停止执行
* 如果用户说 "yes" 或 "proceed" 或 "continue", 继续到步骤 3
- **如果所有清单都已完成**:
* 显示显示所有清单通过的表格
* 自动继续到步骤 3
3. 加载和分析实施上下文:
- **必需**: 读取 tasks.md 获取完整任务列表和执行计划
- **必需**: 读取 plan.md 获取技术栈、架构和文件结构
- **如果存在**: 读取 data-model.md 获取实体和关系
- **如果存在**: 读取 contracts/ 获取 API 规范和测试要求
- **如果存在**: 读取 research.md 获取技术决策和约束
- **如果存在**: 读取 quickstart.md 获取集成场景
4. **项目设置验证**:
- **必需**: 基于实际项目设置创建/验证忽略文件:
**检测和创建逻辑**:
- 检查以下命令是否成功以确定是否为 git 仓库(如果是, 创建/验证 .gitignore):
```sh
git rev-parse --git-dir 2>/dev/null
```
- 检查 Dockerfile* 是否存在或 plan.md 中是否提到 Docker → 创建/验证 .dockerignore
- 检查 .eslintrc* 或 eslint.config.* 是否存在 → 创建/验证 .eslintignore
- 检查 .prettierrc* 是否存在 → 创建/验证 .prettierignore
- 检查 .npmrc 或 package.json 是否存在 → 创建/验证 .npmignore(如果发布)
- 检查 terraform 文件(*.tf)是否存在 → 创建/验证 .terraformignore
- 检查是否需要 .helmignore(存在 helm charts)→ 创建/验证 .helmignore
**如果忽略文件存在**: 验证它包含基本模式, 仅追加缺失的关键模式
**如果忽略文件缺失**: 为检测到的技术创建完整模式集
**按技术的通用模式**(来自 plan.md 技术栈):
- **Node.js/JavaScript**: `node_modules/`, `dist/`, `build/`, `*.log`, `.env*`
- **Python**: `__pycache__/`, `*.pyc`, `.venv/`, `venv/`, `dist/`, `*.egg-info/`
- **Java**: `target/`, `*.class`, `*.jar`, `.gradle/`, `build/`
- **C#/.NET**: `bin/`, `obj/`, `*.user`, `*.suo`, `packages/`
- **Go**: `*.exe`, `*.test`, `vendor/`, `*.out`
- **Ruby**: `.bundle/`, `log/`, `tmp/`, `*.gem`, `vendor/bundle/`
- **PHP**: `vendor/`, `*.log`, `*.cache`, `*.env`
- **Rust**: `target/`, `debug/`, `release/`, `*.rs.bk`, `*.rlib`, `*.prof*`, `.idea/`, `*.log`, `.env*`
- **Kotlin**: `build/`, `out/`, `.gradle/`, `.idea/`, `*.class`, `*.jar`, `*.iml`, `*.log`, `.env*`
- **C++**: `build/`, `bin/`, `obj/`, `out/`, `*.o`, `*.so`, `*.a`, `*.exe`, `*.dll`, `.idea/`, `*.log`, `.env*`
- **C**: `build/`, `bin/`, `obj/`, `out/`, `*.o`, `*.a`, `*.so`, `*.exe`, `Makefile`, `config.log`, `.idea/`, `*.log`, `.env*`
- **Swift**: `.build/`, `DerivedData/`, `*.swiftpm/`, `Packages/`
- **R**: `.Rproj.user/`, `.Rhistory`, `.RData`, `.Ruserdata`, `*.Rproj`, `packrat/`, `renv/`
- **通用**: `.DS_Store`, `Thumbs.db`, `*.tmp`, `*.swp`, `.vscode/`, `.idea/`
**工具特定模式**:
- **Docker**: `node_modules/`, `.git/`, `Dockerfile*`, `.dockerignore`, `*.log*`, `.env*`, `coverage/`
- **ESLint**: `node_modules/`, `dist/`, `build/`, `coverage/`, `*.min.js`
- **Prettier**: `node_modules/`, `dist/`, `build/`, `coverage/`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`
- **Terraform**: `.terraform/`, `*.tfstate*`, `*.tfvars`, `.terraform.lock.hcl`
- **Kubernetes/k8s**: `*.secret.yaml`, `secrets/`, `.kube/`, `kubeconfig*`, `*.key`, `*.crt`
5. 解析 tasks.md 结构并提取:
- **任务阶段**: 设置、测试、核心、集成、完善
- **任务依赖**: 顺序执行与并行执行规则
- **任务详情**: ID、描述、文件路径、并行标记 [P]
- **执行流程**: 顺序和依赖要求
6. 按照任务计划执行实施:
- **分阶段执行**: 在进入下一阶段之前完成每个阶段
- **遵循依赖**: 按顺序运行顺序任务, 并行任务 [P] 可以一起运行
- **遵循 TDD 方法**: 在相应的实施任务之前执行测试任务
- **基于文件的协调**: 影响相同文件的任务必须顺序运行
- **验证检查点**: 在继续之前验证每个阶段的完成
7. 实施执行规则:
- **首先设置**: 初始化项目结构、依赖、配置
- **代码前测试**: 如果需要为合约、实体和集成场景编写测试
- **核心开发**: 实施模型、服务、CLI 命令、端点
- **集成工作**: 数据库连接、中间件、日志、外部服务
- **完善和验证**: 单元测试、性能优化、文档
8. 进度跟踪和错误处理:
- 在每个完成的任务后报告进度
- 如果任何非并行任务失败, 停止执行
- 对于并行任务 [P], 继续成功的任务, 报告失败的任务
- 提供带有调试上下文的清晰错误消息
- 如果实施无法继续, 建议下一步操作
- **重要** 对于已完成的任务, 确保在任务文件中将任务标记为 [X].
9. 完成验证:
- 验证所有必需任务已完成
- 检查实施的功能是否与原始规范匹配
- 验证测试通过且覆盖率满足要求
- 确认实施遵循技术计划
- 报告最终状态并附上已完成工作的摘要
注意: 此命令假设 tasks.md 中存在完整的任务分解. 如果任务不完整或缺失, 建议首先运行 `/tasks` 重新生成任务列表.
-89
View File
@@ -1,89 +0,0 @@
---
description: 执行实施规划工作流, 使用计划模板生成设计制品.
handoffs:
- label: 创建任务
agent: speckit.tasks
prompt: 将计划分解为任务
send: true
- label: 创建检查清单
agent: speckit.checklist
prompt: 为需求创建质量检查清单
send: true
---
## 用户输入
```text
$ARGUMENTS
```
在继续之前, 你**必须**考虑用户输入(如果不为空).
## 大纲
1. **设置**: 从仓库根目录运行 `.specify/scripts/powershell/setup-plan.ps1 -Json` 并解析 JSON 获取 FEATURE_SPEC、IMPL_PLAN、SPECS_DIR、BRANCH. 对于参数中的单引号如 "I'm Groot", 使用转义语法: 例如 'I'\''m Groot'(或尽可能使用双引号: "I'm Groot").
2. **加载上下文**: 读取 FEATURE_SPEC 和 `.specify/memory/constitution.md`. 加载 IMPL_PLAN 模板(已复制).
3. **执行计划工作流**: 按照 IMPL_PLAN 模板中的结构:
- 填充技术上下文(将未知项标记为 NEEDS CLARIFICATION)
- 从章程文档填充章程检查部分
- 评估关卡(如果违规无正当理由则报错)
- 阶段 0: 生成 research.md(解决所有 NEEDS CLARIFICATION)
- 阶段 1: 生成 data-model.md、contracts/、quickstart.md
- 阶段 1: 通过运行代理脚本更新代理上下文
- 设计后重新评估章程检查
4. **停止并报告**: 命令在阶段 2 规划后结束. 报告分支、IMPL_PLAN 路径和生成的制品.
## 阶段
### 阶段 0: 大纲与研究
1. **从上述技术上下文中提取未知项**:
- 每个 NEEDS CLARIFICATION → 研究任务
- 每个依赖项 → 最佳实践任务
- 每个集成 → 模式任务
2. **生成和分发研究代理**:
```
For each unknown in Technical Context:
Task: "Research {unknown} for {feature context}"
For each technology choice:
Task: "Find best practices for {tech} in {domain}"
```
3. **在 `research.md` 中整合发现**, 使用格式:
- Decision: [选择了什么]
- Rationale: [为什么选择]
- Alternatives considered: [还评估了什么]
**输出**: research.md, 所有 NEEDS CLARIFICATION 已解决
### 阶段 1: 设计与合同
**前提条件**: `research.md` 完成
1. **从功能规范中提取实体** → `data-model.md`:
- 实体名称、字段、关系
- 来自需求的验证规则
- 状态转换(如适用)
2. **从功能需求生成 API 合同**:
- 每个用户操作 → 端点
- 使用标准 REST/GraphQL 模式
- 将 OpenAPI/GraphQL 模式输出到 `/contracts/`
3. **代理上下文更新**:
- 运行 `.specify/scripts/powershell/update-agent-context.ps1 -AgentType claude`
- 这些脚本检测正在使用哪个 AI 代理
- 更新相应的代理特定上下文文件
- 仅添加当前计划中的新技术
- 保留标记之间的手动添加内容
**输出**: data-model.md、/contracts/*、quickstart.md、代理特定文件
## 关键规则
- 使用绝对路径
- 关卡失败或未解决的澄清事项时报错
-237
View File
@@ -1,237 +0,0 @@
---
description: 从自然语言功能描述创建或更新功能规范.
handoffs:
- label: 构建技术计划
agent: speckit.plan
prompt: 为规范创建计划。我正在构建...
- label: 澄清规范需求
agent: speckit.clarify
prompt: 分析规范的完整性和清晰度
send: true
---
## 用户输入
```text
$ARGUMENTS
```
在继续之前, 你**必须**考虑用户输入(如果不为空).
## 概述
用户在触发消息中 `/speckit.specify` 后输入的文本**就是**功能描述. 假设你始终可以在本次对话中访问它, 即使下面字面上显示 `$ARGUMENTS`. 除非用户提供了空命令, 否则不要要求用户重复.
基于该功能描述, 执行以下操作:
1. **为分支生成一个简短名称**(2-4个词):
- 分析功能描述并提取最有意义的关键词
- 创建一个2-4个词的简短名称, 捕捉功能的核心
- 尽可能使用动-名词格式(例如, "add-user-auth", "fix-payment-bug")
- 保留技术术语和缩写(OAuth2、API、JWT等)
- 保持简洁但足够描述性, 便于快速理解功能
- 示例:
- "I want to add user authentication" → "user-auth"
- "Implement OAuth2 integration for the API" → "oauth2-api-integration"
- "Create a dashboard for analytics" → "analytics-dashboard"
- "Fix payment processing timeout bug" → "fix-payment-timeout"
2. 从仓库根目录运行脚本 `.specify/scripts/powershell/create-new-feature.ps1 -Json "$ARGUMENTS"` **使用简短名称参数**并解析其 JSON 输出以获取 BRANCH_NAME 和 SPEC_FILE. 所有文件路径必须是绝对路径.
**重要说明**:
- 将第1步创建的2-4词简短名称作为参数附加到 `.specify/scripts/powershell/create-new-feature.ps1 -Json "$ARGUMENTS"` 命令, 功能描述作为最终参数.
- Bash 示例: `--short-name "your-generated-short-name" "功能描述内容"`
- PowerShell 示例: `-ShortName "your-generated-short-name" "功能描述内容"`
- 对于参数中包含单引号的情况(如 "I'm Groot"), 使用转义语法: 例如 'I'\''m Groot'(或优先使用双引号: "I'm Groot")
- 你必须且只能运行此脚本一次
- JSON 输出会显示在终端中 - 请始终参考该输出来获取你要查找的实际内容
3. 加载 `.specify/templates/spec-template.md` 以了解必需的章节.
4. 遵循此执行流程:
1. 从输入解析用户描述
如果为空: 错误 "未提供功能描述"
2. 从描述中提取关键概念
识别: 参与者、操作、数据、约束
3. 对于不明确的方面:
- 基于上下文和行业标准做出有根据的猜测
- 仅在以下情况下标记为 [NEEDS CLARIFICATION: 具体问题]:
- 选择显著影响功能范围或用户体验
- 存在多个合理的解释且有不同的含义
- 没有合理的默认值
- **限制: 最多 3 个 [NEEDS CLARIFICATION] 标记**
- 按影响优先级排序: 范围 > 安全/隐私 > 用户体验 > 技术细节
4. 填写用户场景和测试章节
如果没有明确的用户流程: 错误 "无法确定用户场景"
5. 生成功能需求
每个需求必须是可测试的
对未指定的细节使用合理的默认值(在假设章节中记录假设)
6. 定义成功标准
创建可衡量的、技术无关的结果
包括定量指标(时间、性能、数量)和定性措施(用户满意度、任务完成)
每个标准必须无需实现细节即可验证
7. 识别关键实体(如果涉及数据)
8. 返回: 成功(规范准备好进行规划)
5. 使用模板结构将规范写入 SPEC_FILE, 用从功能描述(参数)派生的具体细节替换占位符, 同时保持章节顺序和标题.
6. **规范质量验证**: 编写初始规范后, 根据质量标准进行验证:
a. **创建规范质量检查清单**: 使用检查清单模板结构在 `FEATURE_DIR/checklists/requirements.md` 生成检查清单文件, 包含这些验证项目:
```markdown
# 规范质量检查清单: [功能名称]
**目的**: 在继续规划之前验证规范的完整性和质量
**创建时间**: [日期]
**功能**: [指向 spec.md 的链接]
## 内容质量
- [ ] 无实现细节(语言、框架、API)
- [ ] 专注于用户价值和业务需求
- [ ] 为非技术利益相关者编写
- [ ] 所有必需章节已完成
## 需求完整性
- [ ] 没有 [NEEDS CLARIFICATION] 标记剩余
- [ ] 需求是可测试且明确的
- [ ] 成功标准是可衡量的
- [ ] 成功标准是技术无关的(无实现细节)
- [ ] 所有验收场景已定义
- [ ] 边缘情况已识别
- [ ] 范围明确界定
- [ ] 依赖关系和假设已识别
## 功能准备就绪
- [ ] 所有功能需求都有明确的验收标准
- [ ] 用户场景覆盖主要流程
- [ ] 功能满足成功标准中定义的可衡量结果
- [ ] 没有实现细节泄漏到规范中
## 备注
- 标记为不完整的项目需要在 `/speckit.clarify` 或 `/speckit.plan` 之前更新规范
```
b. **运行验证检查**: 根据每个检查清单项目审查规范:
- 对于每个项目, 确定是否通过或失败
- 记录发现的具体问题(引用相关规范章节)
c. **处理验证结果**:
- **如果所有项目都通过**: 标记检查清单完成并继续步骤 7
- **如果项目失败(不包括 [NEEDS CLARIFICATION])**:
1. 列出失败的项目和具体问题
2. 更新规范以解决每个问题
3. 重新运行验证直到所有项目都通过(最多 3 次迭代)
4. 如果 3 次迭代后仍然失败, 在检查清单备注中记录剩余问题并警告用户
- **如果 [NEEDS CLARIFICATION] 标记仍然存在**:
1. 从规范中提取所有 [NEEDS CLARIFICATION: ...] 标记
2. **限制检查**: 如果存在超过 3 个标记, 仅保留 3 个最关键的(按范围/安全/用户体验影响)并为其余部分做出有根据的猜测
3. 对于每个需要的澄清(最多 3 个), 以以下格式向用户呈现选项:
```markdown
## 问题 [N]: [主题]
**上下文**: [引用相关规范章节]
**我们需要了解**: [来自 NEEDS CLARIFICATION 标记的具体问题]
**建议答案**:
| 选项 | 答案 | 含义 |
|--------|--------|--------------|
| A | [第一个建议答案] | [这对功能意味着什么] |
| B | [第二个建议答案] | [这对功能意味着什么] |
| C | [第三个建议答案] | [这对功能意味着什么] |
| 自定义 | 提供你自己的答案 | [解释如何提供自定义输入] |
**你的选择**: _[等待用户响应]_
```
4. **关键 - 表格格式**: 确保 markdown 表格格式正确:
- 使用一致的间距, 管道符对齐
- 每个单元格内容周围应有空格: `| 内容 |` 而不是 `|内容|`
- 标题分隔符必须至少有 3 个破折号: `|--------|`
- 测试表格在 markdown 预览中正确渲染
5. 按顺序编号问题(Q1、Q2、Q3 - 最多 3 个)
6. 在等待响应之前一起呈现所有问题
7. 等待用户响应所有问题的选择(例如, "Q1: A, Q2: 自定义 - [详情], Q3: B")
8. 通过用用户选择或提供的答案替换每个 [NEEDS CLARIFICATION] 标记来更新规范
9. 在所有澄清解决后重新运行验证
d. **更新检查清单**: 每次验证迭代后, 使用当前的通过/失败状态更新检查清单文件
7. 报告完成情况, 包括分支名称、规范文件路径、检查清单结果以及下一阶段(`/speckit.clarify` 或 `/speckit.plan`)的准备就绪状态.
**注意**: 脚本在写入之前创建并检出新分支并初始化规范文件.
## 通用指南
## 快速指南
- 专注于用户需要**什么**和**为什么**.
- 避免如何实现(不涉及技术栈、API、代码结构).
- 为业务利益相关者编写, 而不是为开发者.
- 不要创建嵌入规范中的任何检查清单. 那将是一个单独的命令.
### 章节要求
- **必需章节**: 每个功能必须完成
- **可选章节**: 仅在与功能相关时包含
- 当章节不适用时, 完全删除它(不要保留为 "N/A")
### AI 生成
当从用户提示创建此规范时:
1. **做出有根据的猜测**: 使用上下文、行业标准和常见模式来填补空白
2. **记录假设**: 在假设章节中记录合理的默认值
3. **限制澄清**: 最多 3 个 [NEEDS CLARIFICATION] 标记 - 仅用于关键决策:
- 显著影响功能范围或用户体验
- 存在多个合理的解释且有不同的含义
- 缺乏任何合理的默认值
4. **优先澄清**: 范围 > 安全/隐私 > 用户体验 > 技术细节
5. **像测试人员一样思考**: 每个模糊的需求都应该在"可测试且明确"的检查清单项目上失败
6. **NEEDS CLARIFICATION 的常见领域**(仅在没有合理默认值时):
- 功能范围和边界(包含/排除特定用例)
- 用户类型和权限(如果可能存在多个冲突的解释)
- 安全/合规要求(当具有法律/财务重要性时)
**合理默认值的示例**(不要询问这些):
- 数据保留: 该行业的行业标准实践
- 性能目标: 标准 Web/移动应用期望, 除非另有说明
- 错误处理: 用户友好的消息和适当的回退
- 认证方法: Web 应用的标准基于会话或 OAuth2
- 集成模式: RESTful API, 除非另有说明
### 成功标准指南
成功标准必须是:
1. **可衡量的**: 包括具体指标(时间、百分比、计数、速率)
2. **技术无关的**: 不提及框架、语言、数据库或工具
3. **以用户为中心的**: 从用户/业务角度描述结果, 而不是系统内部
4. **可验证的**: 无需了解实现细节即可测试/验证
**好的示例**:
- "用户可以在 3 分钟内完成结账"
- "系统支持 10,000 个并发用户"
- "95% 的搜索在 1 秒内返回结果"
- "任务完成率提高 40%"
**坏的示例**(以实现为中心):
- "API 响应时间在 200ms 以下"(太技术化, 使用"用户立即看到结果")
- "数据库可以处理 1000 TPS"(实现细节, 使用面向用户的指标)
- "React 组件高效渲染"(框架特定)
- "Redis 缓存命中率超过 80%"(技术特定)
-120
View File
@@ -1,120 +0,0 @@
---
description: 基于可用的设计文档, 为功能特性生成可执行的、按依赖关系排序的 tasks.md 文件.
handoffs:
- label: 分析一致性
agent: speckit.analyze
prompt: 运行项目一致性分析
send: true
- label: 实施项目
agent: speckit.implement
prompt: 实施项目
send: true
---
## 用户输入
```text
$ARGUMENTS
```
在继续之前, 你**必须**考虑用户输入(如果不为空).
## 概述
1. **设置**: 从仓库根目录运行 `.specify/scripts/powershell/check-prerequisites.ps1 -Json` 并解析 FEATURE_DIR 和 AVAILABLE_DOCS 列表. 所有路径必须是绝对路径. 对于参数值中的单引号如 "I'm Groot", 使用转义语法: 例如 'I'\''m Groot'(或尽可能使用双引号: "I'm Groot").
2. **加载设计文档**: 从 FEATURE_DIR 读取:
- **必需**: plan.md(技术栈、库、结构), spec.md(带优先级的用户故事)
- **可选**: data-model.md(实体), contracts/(API 端点), research.md(决策), quickstart.md(测试场景)
- 注意: 并非所有项目都有所有文档. 基于可用内容生成任务.
3. **执行任务生成流程**(遵循模板结构):
- 加载 plan.md 并提取技术栈、库、项目结构
- **加载 spec.md 并提取带优先级的用户故事(P1、P2、P3 等)**
- 如果存在 data-model.md: 提取实体 → 映射到用户故事
- 如果存在 contracts/: 每个文件 → 映射端点到用户故事
- 如果存在 research.md: 提取决策 → 生成设置任务
- **按用户故事组织生成任务(重点)**:
- 设置任务(所有故事需要的共享基础设施)
- **基础任务(任何用户故事开始前必须完成的先决条件)**
- 对于每个用户故事(按优先级顺序 P1、P2、P3...):
- 组合完成该故事所需的所有任务
- 包含该故事特定的模型、服务、端点、UI 组件
- 标记哪些任务是 [P] 可并行的
- 如果请求测试: 包含该故事特定的测试
- 完善/集成任务(横切关注点)
- **测试是可选的**: 仅在功能规范中明确请求或用户要求 TDD 方法时生成测试任务
- 应用任务规则:
- 不同文件 = 标记 [P] 表示并行
- 相同文件 = 顺序执行(无 [P])
- 如果请求测试: 测试在实现之前(TDD 顺序)
- 按顺序编号任务(T001、T002...)
- 生成显示用户故事完成顺序的依赖关系图
- 为每个用户故事创建并行执行示例
- 验证任务完整性(每个用户故事都有所有必需任务, 可独立测试)
4. **生成 tasks.md**: 使用 `.specify/templates/tasks-template.md` 作为结构, 填充:
- 来自 plan.md 的正确功能名称
- 阶段 1: 设置任务(项目初始化)
- 阶段 2: 基础任务(所有用户故事的阻塞先决条件)
- 阶段 3+: 每个用户故事一个阶段(按 spec.md 中的优先级顺序)
- 每个阶段包括: 故事目标、独立测试标准、测试(如果请求)、实现任务
- 每个任务的清晰 [Story] 标签(US1、US2、US3...)
- 每个故事内可并行任务的 [P] 标记
- 每个故事阶段后的检查点标记
- 最终阶段: 完善与横切关注点
- 按执行顺序编号的任务(T001、T002...)
- 每个任务的清晰文件路径
- 显示故事完成顺序的依赖关系部分
- 每个故事的并行执行示例
- 实现策略部分(MVP 优先, 增量交付)
5. **报告**: 输出生成的 tasks.md 路径和摘要:
- 总任务数
- 每个用户故事的任务数
- 识别的并行机会
- 每个故事的独立测试标准
- 建议的 MVP 范围(通常只是用户故事 1)
任务生成上下文: $ARGUMENTS
tasks.md 应该立即可执行 - 每个任务必须足够具体, 以便 LLM 可以在没有额外上下文的情况下完成它.
## 任务生成规则
**重要**: 测试是可选的. 仅当用户在功能规范中明确请求测试或 TDD 方法时才生成测试任务.
**关键**: 任务必须按用户故事组织, 以实现独立的实现和测试.
1. **来自用户故事(spec.md)** - 主要组织方式:
- 每个用户故事(P1、P2、P3...)都有自己的阶段
- 将所有相关组件映射到它们的故事:
- 该故事所需的模型
- 该故事所需的服务
- 该故事所需的端点/UI
- 如果请求测试: 该故事特定的测试
- 标记故事依赖关系(大多数故事应该是独立的)
2. **来自合约**:
- 将每个合约/端点 → 映射到它服务的用户故事
- 如果请求测试: 每个合约 → 在该故事阶段的实现之前的合约测试任务 [P]
3. **来自数据模型**:
- 将每个实体 → 映射到需要它的用户故事
- 如果实体服务多个故事: 放在最早的故事或设置阶段
- 关系 → 适当故事阶段的服务层任务
4. **来自设置/基础设施**:
- 共享基础设施 → 设置阶段(阶段 1)
- 基础/阻塞任务 → 基础阶段(阶段 2)
- 示例: 数据库模式设置、认证框架、核心库、基础配置
- 这些必须在任何用户故事可以实现之前完成
- 故事特定的设置 → 在该故事的阶段内
5. **排序**:
- 阶段 1: 设置(项目初始化)
- 阶段 2: 基础(阻塞先决条件 - 必须在用户故事之前完成)
- 阶段 3+: 按优先级顺序的用户故事(P1、P2、P3...)
- 每个故事内: 测试(如果请求)→ 模型 → 服务 → 端点 → 集成
- 最终阶段: 完善与横切关注点
- 每个用户故事阶段应该是一个完整的、可独立测试的增量
-28
View File
@@ -1,28 +0,0 @@
---
description: 将现有任务转换为可操作的、按依赖关系排序的 GitHub 议题,基于可用的设计制品。
tools: ['github/github-mcp-server/issue_write']
---
## 用户输入
```text
$ARGUMENTS
```
你必须**在继续之前考虑用户输入**(如果非空)。
## 大纲
1. 从仓库根目录运行 `.specify/scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks` 并解析 FEATURE_DIR 和 AVAILABLE_DOCS 列表。所有路径必须是绝对路径。对于参数中的单引号如 "I'm Groot",使用转义语法:例如 'I'\''m Groot'(或尽可能使用双引号:"I'm Groot")。
1. 从执行的脚本中,提取 **任务** 的路径。
1. 通过运行以下命令获取 Git 远程仓库:
```bash
git config --get remote.origin.url
```
**仅当远程仓库是 GITHUB URL 时才继续下一步**
1. 对于列表中的每个任务,使用 GitHub MCP 服务器在与 Git 远程仓库对应的仓库中创建一个新议题。
**在任何情况下都不要在与远程 URL 不匹配的仓库中创建议题**
-200
View File
@@ -1,200 +0,0 @@
<!--
================================================================================
同步影响报告 | Constitution Update
================================================================================
版本变更: 未定义 → 1.0.0 (初始版本)
新增原则:
- I. TypeScript 严格模式与类型安全
- II. 组件优先架构
- III. 测试驱动开发(不可协商)
- IV. 性能优先
- V. 用户体验一致性
- VI. 代码质量与可维护性
新增章节:
- 技术标准(技术栈要求、框架版本)
- 开发工作流程(代码审查、质量门禁)
模板状态:
✅ plan-template.md - 已验证兼容
✅ spec-template.md - 已验证兼容
✅ tasks-template.md - 已验证兼容
✅ speckit.plan.md - 已更新章程检查引用
✅ speckit.specify.md - 已验证兼容
后续 TODO: 无
================================================================================
-->
# Agent Park v2 项目章程
## 核心原则
### I. TypeScript 严格模式与类型安全
TypeScript 严格模式是强制要求。所有代码必须:
- 在 tsconfig.json 中启用 `strict: true` 和所有严格选项
- 所有函数必须显式声明参数和返回值类型(禁止隐式 any)
- 禁止使用 `@ts-ignore``@ts-nocheck`(除非用于临时迁移, 需要工单跟踪)
- 外部数据必须经过运行时验证(如使用 Zod、Yup 等架构验证库)
- 所有 API 响应必须定义明确的类型接口
**理由**: TypeScript 严格模式在编译时捕获潜在错误, 减少运行时问题, 提高代码可维护性和开发体验。类型安全使重构更安全, 代码更自文档化。
### II. 组件优先架构
采用 Next.js App Router 架构模式, 遵循以下规则:
- 优先使用 Server Components 而非 Client Components
- 仅在需要交互性(状态、事件处理、浏览器 API)时使用 'use client' 指令
- 组件必须保持单一职责和可复用性
- 布局组件必须使用嵌套布局模式充分利用 Next.js 特性
- 路由处理程序(Route Handlers)用于 API 端点, 遵循 RESTful 原则
- 数据获取优先使用 Server Actions 或 Server Components 内的异步函数
**理由**: Server Components 提升性能、减少客户端 JavaScript 体积、改善 SEO。单一职责组件提高可维护性和可测试性。
### III. 测试驱动开发(不可协商)
TDD 是强制要求。测试必须在功能实现之前编写:
- 红-绿-重构循环必须严格遵循
- 单元测试覆盖率必须达到 80% 以上
- 关键业务逻辑必须有集成测试
- UI 组件必须有可视化测试(如 Playwright 或 Storybook)
- 所有测试必须在 PR 合并前通过
- 测试代码质量与生产代码同等重要
**理由**: TDD 确保代码可测试性、捕获早期错误、作为活文档、提供重构安全网。高测试覆盖率降低生产环境 Bug 风险。
### IV. 性能优先
性能是核心质量指标, 必须满足:
- 首次内容绘制(FCP) < 1.8s
- 最大内容绘制(LCP) < 2.5s
- 首次输入延迟(FID) < 100ms
- 累积布局偏移(CLS) < 0.1
- 首次字节时间(TTFB) < 800ms
- 图片必须使用 Next.js Image 组件(自动优化、懒加载)
- 启用增量静态再生(ISR)或静态生成(SSG)而非服务端渲染(SSR)(如适用)
- 客户端导航使用 Link 组件
- 禁止全量导入大型库(如 lodash), 使用按需导入
- 打包体积: 初始 JavaScript < 200KB gzipped
**理由**: 性能直接影响用户体验、转化率和 SEO。Google Core Web Vitals 是搜索排名的重要因素。快速加载减少跳出率。
### V. 用户体验一致性
跨整个应用必须保持一致的用户体验:
- 所有页面必须响应式设计(移动优先)
- 使用统一的设计系统(组件库、颜色、排版、间距)
- 加载状态必须提供视觉反馈(skeleton、spinner 或进度条)
- 错误处理必须提供用户友好的消息和恢复操作
- 表单必须有清晰的标签、验证提示和错误消息
- 所有交互元素必须有可访问性支持(ARIA 标签、键盘导航)
- 深色模式支持(如适用)
- 页面转换必须平滑(使用 View Transitions 或类似技术)
**理由**: 一致性降低用户学习曲线、提高信任度、改善可访问性。良好的 UX 提高参与度和用户满意度。
### VI. 代码质量与可维护性
代码必须遵循最佳实践和编码标准:
- 使用 ESLint 和 Prettier 进行代码检查和格式化
- 遵循 Airbnb 或 Standard TypeScript 风格指南
- 函数复杂度必须控制在合理范围(圈复杂度 < 10)
- 文件大小限制: < 300 行代码(可读性)
- 导入路径使用别名(@/components/...)而非相对路径
- 环境变量使用 .env 文件, 在 .env.example 中记录所有变量
- 关键功能必须包含 JSDoc 注释
- 禁止在生产代码中使用 console.log(使用结构化日志库如 Pino)
- 依赖项必须定期更新(每月审查安全和更新)
**理由**: 一致的代码风格改善团队协作、降低认知负荷。清晰的代码减少维护成本、加速新开发者上手。
## 技术标准
### 技术栈要求
- **框架**: Next.js 14+ (App Router)
- **语言**: TypeScript 5.0+ (严格模式)
- **样式**: Tailwind CSS 或 CSS Modules
- **状态管理**: React Context API 或 Zustand(如需要)
- **表单**: React Hook Form + Zod 验证
- **数据库**: PostgreSQL 或 MongoDB(基于项目需求)
- **ORM**: Prisma 或 Mongoose
- **认证**: NextAuth.js 或 Clerk
- **API**: RESTful (Route Handlers) 或 tRPC
- **测试**: Vitest/Jest + React Testing Library + Playwright
- **部署**: Vercel(推荐)或 Docker
### 框架版本控制
- Next.js 主版本升级需要评估和迁移计划
- 依赖项版本锁定(使用 package-lock.json 或 yarn.lock)
- 破坏性变更需要文档和迁移指南
## 开发工作流程
### 代码审查要求
所有代码必须经过同行评审:
- PR 必须包含清晰的描述和链接到相关议题/规范
- 至少一位团队成员批准才能合并
- 所有 CI 检查必须通过(测试、类型检查、Lint)
- PR 大小限制: < 400 行变更(大变更需要拆分)
- 审查者必须检查: 类型安全、测试覆盖、性能影响、可访问性
### 质量门禁
代码合并前必须满足:
- [ ] 所有测试通过(单元、集成、E2E)
- [ ] TypeScript 编译无错误
- [ ] ESLint 无警告
- [ ] 性能预算未超限
- [ ] 可访问性检查通过(如 axe-core)
- [ ] 安全扫描无高危漏洞(如 Dependabot)
### 分支策略
- `main`: 生产就绪代码
- `develop`: 开发集成分支
- `feature/*`: 功能分支(从 develop 分支)
- `fix/*`: Bug 修复分支(从 develop 分支)
- 使用语义化提交消息(Conventional Commits)
## 治理
本章程优先于所有其他实践和规范。
### 修正流程
1. 提出修正: 创建议题描述需要修改的原则及原因
2. 团队审查: 讨论影响、权衡和替代方案
3. 批准: 需要多数团队核心成员同意
4. 文档化: 更新章程版本号和修正日期
5. 迁移计划: 为破坏性变更提供迁移指南
### 合规审查
- 每个 PR 必须符合章程原则
- 每次功能规划必须检查章程兼容性
- 每季度审查章程并更新(如需要)
- 违反章程需要明确理由和记录(在复杂度跟踪中)
### 版本控制
- **MAJOR**: 向后不兼容的原则删除或重新定义
- **MINOR**: 新原则添加或实质性扩展
- **PATCH**: 澄清、措辞、拼写错误修复
**版本**: 1.0.0 | **批准日期**: 2025-12-25 | **最后修正**: 2025-12-25
@@ -1,148 +0,0 @@
#!/usr/bin/env pwsh
# Consolidated prerequisite checking script (PowerShell)
#
# This script provides unified prerequisite checking for Spec-Driven Development workflow.
# It replaces the functionality previously spread across multiple scripts.
#
# Usage: ./check-prerequisites.ps1 [OPTIONS]
#
# OPTIONS:
# -Json Output in JSON format
# -RequireTasks Require tasks.md to exist (for implementation phase)
# -IncludeTasks Include tasks.md in AVAILABLE_DOCS list
# -PathsOnly Only output path variables (no validation)
# -Help, -h Show help message
[CmdletBinding()]
param(
[switch]$Json,
[switch]$RequireTasks,
[switch]$IncludeTasks,
[switch]$PathsOnly,
[switch]$Help
)
$ErrorActionPreference = 'Stop'
# Show help if requested
if ($Help) {
Write-Output @"
Usage: check-prerequisites.ps1 [OPTIONS]
Consolidated prerequisite checking for Spec-Driven Development workflow.
OPTIONS:
-Json Output in JSON format
-RequireTasks Require tasks.md to exist (for implementation phase)
-IncludeTasks Include tasks.md in AVAILABLE_DOCS list
-PathsOnly Only output path variables (no prerequisite validation)
-Help, -h Show this help message
EXAMPLES:
# Check task prerequisites (plan.md required)
.\check-prerequisites.ps1 -Json
# Check implementation prerequisites (plan.md + tasks.md required)
.\check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks
# Get feature paths only (no validation)
.\check-prerequisites.ps1 -PathsOnly
"@
exit 0
}
# Source common functions
. "$PSScriptRoot/common.ps1"
# Get feature paths and validate branch
$paths = Get-FeaturePathsEnv
if (-not (Test-FeatureBranch -Branch $paths.CURRENT_BRANCH -HasGit:$paths.HAS_GIT)) {
exit 1
}
# If paths-only mode, output paths and exit (support combined -Json -PathsOnly)
if ($PathsOnly) {
if ($Json) {
[PSCustomObject]@{
REPO_ROOT = $paths.REPO_ROOT
BRANCH = $paths.CURRENT_BRANCH
FEATURE_DIR = $paths.FEATURE_DIR
FEATURE_SPEC = $paths.FEATURE_SPEC
IMPL_PLAN = $paths.IMPL_PLAN
TASKS = $paths.TASKS
} | ConvertTo-Json -Compress
} else {
Write-Output "REPO_ROOT: $($paths.REPO_ROOT)"
Write-Output "BRANCH: $($paths.CURRENT_BRANCH)"
Write-Output "FEATURE_DIR: $($paths.FEATURE_DIR)"
Write-Output "FEATURE_SPEC: $($paths.FEATURE_SPEC)"
Write-Output "IMPL_PLAN: $($paths.IMPL_PLAN)"
Write-Output "TASKS: $($paths.TASKS)"
}
exit 0
}
# Validate required directories and files
if (-not (Test-Path $paths.FEATURE_DIR -PathType Container)) {
Write-Output "ERROR: Feature directory not found: $($paths.FEATURE_DIR)"
Write-Output "Run /speckit.specify first to create the feature structure."
exit 1
}
if (-not (Test-Path $paths.IMPL_PLAN -PathType Leaf)) {
Write-Output "ERROR: plan.md not found in $($paths.FEATURE_DIR)"
Write-Output "Run /speckit.plan first to create the implementation plan."
exit 1
}
# Check for tasks.md if required
if ($RequireTasks -and -not (Test-Path $paths.TASKS -PathType Leaf)) {
Write-Output "ERROR: tasks.md not found in $($paths.FEATURE_DIR)"
Write-Output "Run /speckit.tasks first to create the task list."
exit 1
}
# Build list of available documents
$docs = @()
# Always check these optional docs
if (Test-Path $paths.RESEARCH) { $docs += 'research.md' }
if (Test-Path $paths.DATA_MODEL) { $docs += 'data-model.md' }
# Check contracts directory (only if it exists and has files)
if ((Test-Path $paths.CONTRACTS_DIR) -and (Get-ChildItem -Path $paths.CONTRACTS_DIR -ErrorAction SilentlyContinue | Select-Object -First 1)) {
$docs += 'contracts/'
}
if (Test-Path $paths.QUICKSTART) { $docs += 'quickstart.md' }
# Include tasks.md if requested and it exists
if ($IncludeTasks -and (Test-Path $paths.TASKS)) {
$docs += 'tasks.md'
}
# Output results
if ($Json) {
# JSON output
[PSCustomObject]@{
FEATURE_DIR = $paths.FEATURE_DIR
AVAILABLE_DOCS = $docs
} | ConvertTo-Json -Compress
} else {
# Text output
Write-Output "FEATURE_DIR:$($paths.FEATURE_DIR)"
Write-Output "AVAILABLE_DOCS:"
# Show status of each potential document
Test-FileExists -Path $paths.RESEARCH -Description 'research.md' | Out-Null
Test-FileExists -Path $paths.DATA_MODEL -Description 'data-model.md' | Out-Null
Test-DirHasFiles -Path $paths.CONTRACTS_DIR -Description 'contracts/' | Out-Null
Test-FileExists -Path $paths.QUICKSTART -Description 'quickstart.md' | Out-Null
if ($IncludeTasks) {
Test-FileExists -Path $paths.TASKS -Description 'tasks.md' | Out-Null
}
}
-137
View File
@@ -1,137 +0,0 @@
#!/usr/bin/env pwsh
# Common PowerShell functions analogous to common.sh
function Get-RepoRoot {
try {
$result = git rev-parse --show-toplevel 2>$null
if ($LASTEXITCODE -eq 0) {
return $result
}
} catch {
# Git command failed
}
# Fall back to script location for non-git repos
return (Resolve-Path (Join-Path $PSScriptRoot "../../..")).Path
}
function Get-CurrentBranch {
# First check if SPECIFY_FEATURE environment variable is set
if ($env:SPECIFY_FEATURE) {
return $env:SPECIFY_FEATURE
}
# Then check git if available
try {
$result = git rev-parse --abbrev-ref HEAD 2>$null
if ($LASTEXITCODE -eq 0) {
return $result
}
} catch {
# Git command failed
}
# For non-git repos, try to find the latest feature directory
$repoRoot = Get-RepoRoot
$specsDir = Join-Path $repoRoot "specs"
if (Test-Path $specsDir) {
$latestFeature = ""
$highest = 0
Get-ChildItem -Path $specsDir -Directory | ForEach-Object {
if ($_.Name -match '^(\d{3})-') {
$num = [int]$matches[1]
if ($num -gt $highest) {
$highest = $num
$latestFeature = $_.Name
}
}
}
if ($latestFeature) {
return $latestFeature
}
}
# Final fallback
return "main"
}
function Test-HasGit {
try {
git rev-parse --show-toplevel 2>$null | Out-Null
return ($LASTEXITCODE -eq 0)
} catch {
return $false
}
}
function Test-FeatureBranch {
param(
[string]$Branch,
[bool]$HasGit = $true
)
# For non-git repos, we can't enforce branch naming but still provide output
if (-not $HasGit) {
Write-Warning "[specify] Warning: Git repository not detected; skipped branch validation"
return $true
}
if ($Branch -notmatch '^[0-9]{3}-') {
Write-Output "ERROR: Not on a feature branch. Current branch: $Branch"
Write-Output "Feature branches should be named like: 001-feature-name"
return $false
}
return $true
}
function Get-FeatureDir {
param([string]$RepoRoot, [string]$Branch)
Join-Path $RepoRoot "specs/$Branch"
}
function Get-FeaturePathsEnv {
$repoRoot = Get-RepoRoot
$currentBranch = Get-CurrentBranch
$hasGit = Test-HasGit
$featureDir = Get-FeatureDir -RepoRoot $repoRoot -Branch $currentBranch
[PSCustomObject]@{
REPO_ROOT = $repoRoot
CURRENT_BRANCH = $currentBranch
HAS_GIT = $hasGit
FEATURE_DIR = $featureDir
FEATURE_SPEC = Join-Path $featureDir 'spec.md'
IMPL_PLAN = Join-Path $featureDir 'plan.md'
TASKS = Join-Path $featureDir 'tasks.md'
RESEARCH = Join-Path $featureDir 'research.md'
DATA_MODEL = Join-Path $featureDir 'data-model.md'
QUICKSTART = Join-Path $featureDir 'quickstart.md'
CONTRACTS_DIR = Join-Path $featureDir 'contracts'
}
}
function Test-FileExists {
param([string]$Path, [string]$Description)
if (Test-Path -Path $Path -PathType Leaf) {
Write-Output "$Description"
return $true
} else {
Write-Output "$Description"
return $false
}
}
function Test-DirHasFiles {
param([string]$Path, [string]$Description)
if ((Test-Path -Path $Path -PathType Container) -and (Get-ChildItem -Path $Path -ErrorAction SilentlyContinue | Where-Object { -not $_.PSIsContainer } | Select-Object -First 1)) {
Write-Output "$Description"
return $true
} else {
Write-Output "$Description"
return $false
}
}
@@ -1,327 +0,0 @@
#!/usr/bin/env pwsh
# Create a new feature
[CmdletBinding()]
param(
[switch]$Json,
[string]$ShortName,
[int]$Number = 0,
[switch]$Help,
[Parameter(ValueFromRemainingArguments = $true)]
[string[]]$FeatureDescription
)
$ErrorActionPreference = 'Stop'
# Show help if requested
if ($Help) {
Write-Host "Usage: ./create-new-feature.ps1 [-Json] [-ShortName <name>] [-Number N] <feature description>"
Write-Host ""
Write-Host "Options:"
Write-Host " -Json Output in JSON format"
Write-Host " -ShortName <name> Provide a custom short name (2-4 words) for the branch"
Write-Host " -Number N Specify branch number manually (overrides auto-detection)"
Write-Host " -Help Show this help message"
Write-Host ""
Write-Host "Examples:"
Write-Host " ./create-new-feature.ps1 'Add user authentication system' -ShortName 'user-auth'"
Write-Host " ./create-new-feature.ps1 'Implement OAuth2 integration for API'"
exit 0
}
# Check if feature description provided
if (-not $FeatureDescription -or $FeatureDescription.Count -eq 0) {
Write-Error "Usage: ./create-new-feature.ps1 [-Json] [-ShortName <name>] <feature description>"
exit 1
}
$featureDesc = ($FeatureDescription -join ' ').Trim()
# Resolve repository root. Prefer git information when available, but fall back
# to searching for repository markers so the workflow still functions in repositories that
# were initialized with --no-git.
function Find-RepositoryRoot {
param(
[string]$StartDir,
[string[]]$Markers = @('.git', '.specify')
)
$current = Resolve-Path $StartDir
while ($true) {
foreach ($marker in $Markers) {
if (Test-Path (Join-Path $current $marker)) {
return $current
}
}
$parent = Split-Path $current -Parent
if ($parent -eq $current) {
# Reached filesystem root without finding markers
return $null
}
$current = $parent
}
}
function Get-HighestNumberFromSpecs {
param([string]$SpecsDir)
$highest = 0
if (Test-Path $SpecsDir) {
Get-ChildItem -Path $SpecsDir -Directory | ForEach-Object {
if ($_.Name -match '^(\d+)') {
$num = [int]$matches[1]
if ($num -gt $highest) { $highest = $num }
}
}
}
return $highest
}
function Get-HighestNumberFromBranches {
param()
$highest = 0
try {
$branches = git branch -a 2>$null
if ($LASTEXITCODE -eq 0) {
foreach ($branch in $branches) {
# Clean branch name: remove leading markers and remote prefixes
$cleanBranch = $branch.Trim() -replace '^\*?\s+', '' -replace '^remotes/[^/]+/', ''
# Extract feature number if branch matches pattern ###-*
if ($cleanBranch -match '^(\d+)-') {
$num = [int]$matches[1]
if ($num -gt $highest) { $highest = $num }
}
}
}
} catch {
# If git command fails, return 0
Write-Verbose "Could not check Git branches: $_"
}
return $highest
}
function Get-NextBranchNumber {
param(
[string]$ShortName,
[string]$SpecsDir
)
# Fetch all remotes to get latest branch info (suppress errors if no remotes)
try {
git fetch --all --prune 2>$null | Out-Null
} catch {
# Ignore fetch errors
}
# Find remote branches matching the pattern using git ls-remote
$remoteBranches = @()
try {
$remoteRefs = git ls-remote --heads origin 2>$null
if ($remoteRefs) {
$remoteBranches = $remoteRefs | Where-Object { $_ -match "refs/heads/(\d+)-$([regex]::Escape($ShortName))$" } | ForEach-Object {
if ($_ -match "refs/heads/(\d+)-") {
[int]$matches[1]
}
}
}
} catch {
# Ignore errors
}
# Check local branches
$localBranches = @()
try {
$allBranches = git branch 2>$null
if ($allBranches) {
$localBranches = $allBranches | Where-Object { $_ -match "^\*?\s*(\d+)-$([regex]::Escape($ShortName))$" } | ForEach-Object {
if ($_ -match "(\d+)-") {
[int]$matches[1]
}
}
}
} catch {
# Ignore errors
}
# Check specs directory
$specDirs = @()
if (Test-Path $SpecsDir) {
try {
$specDirs = Get-ChildItem -Path $SpecsDir -Directory | Where-Object { $_.Name -match "^(\d+)-$([regex]::Escape($ShortName))$" } | ForEach-Object {
if ($_.Name -match "^(\d+)-") {
[int]$matches[1]
}
}
} catch {
# Ignore errors
}
}
# Combine all sources and get the highest number
$maxNum = 0
foreach ($num in ($remoteBranches + $localBranches + $specDirs)) {
if ($num -gt $maxNum) {
$maxNum = $num
}
}
# Return next number
return $maxNum + 1
}
function ConvertTo-CleanBranchName {
param([string]$Name)
return $Name.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
}
$fallbackRoot = (Find-RepositoryRoot -StartDir $PSScriptRoot)
if (-not $fallbackRoot) {
Write-Error "Error: Could not determine repository root. Please run this script from within the repository."
exit 1
}
try {
$repoRoot = git rev-parse --show-toplevel 2>$null
if ($LASTEXITCODE -eq 0) {
$hasGit = $true
} else {
throw "Git not available"
}
} catch {
$repoRoot = $fallbackRoot
$hasGit = $false
}
Set-Location $repoRoot
$specsDir = Join-Path $repoRoot 'specs'
New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
# Function to generate branch name with stop word filtering and length filtering
function Get-BranchName {
param([string]$Description)
# Common stop words to filter out
$stopWords = @(
'i', 'a', 'an', 'the', 'to', 'for', 'of', 'in', 'on', 'at', 'by', 'with', 'from',
'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had',
'do', 'does', 'did', 'will', 'would', 'should', 'could', 'can', 'may', 'might', 'must', 'shall',
'this', 'that', 'these', 'those', 'my', 'your', 'our', 'their',
'want', 'need', 'add', 'get', 'set'
)
# Convert to lowercase and extract words (alphanumeric only)
$cleanName = $Description.ToLower() -replace '[^a-z0-9\s]', ' '
$words = $cleanName -split '\s+' | Where-Object { $_ }
# Filter words: remove stop words and words shorter than 3 chars (unless they're uppercase acronyms in original)
$meaningfulWords = @()
foreach ($word in $words) {
# Skip stop words
if ($stopWords -contains $word) { continue }
# Keep words that are length >= 3 OR appear as uppercase in original (likely acronyms)
if ($word.Length -ge 3) {
$meaningfulWords += $word
} elseif ($Description -match "\b$($word.ToUpper())\b") {
# Keep short words if they appear as uppercase in original (likely acronyms)
$meaningfulWords += $word
}
}
# If we have meaningful words, use first 3-4 of them
if ($meaningfulWords.Count -gt 0) {
$maxWords = if ($meaningfulWords.Count -eq 4) { 4 } else { 3 }
$result = ($meaningfulWords | Select-Object -First $maxWords) -join '-'
return $result
} else {
# Fallback to original logic if no meaningful words found
$result = ConvertTo-CleanBranchName -Name $Description
$fallbackWords = ($result -split '-') | Where-Object { $_ } | Select-Object -First 3
return [string]::Join('-', $fallbackWords)
}
}
# Generate branch name
if ($ShortName) {
# Use provided short name, just clean it up
$branchSuffix = ConvertTo-CleanBranchName -Name $ShortName
} else {
# Generate from description with smart filtering
$branchSuffix = Get-BranchName -Description $featureDesc
}
# Determine branch number
if ($Number -eq 0) {
if ($hasGit) {
# Check existing branches on remotes
$Number = Get-NextBranchNumber -ShortName $branchSuffix -SpecsDir $specsDir
} else {
# Fall back to local directory check
$Number = (Get-HighestNumberFromSpecs -SpecsDir $specsDir) + 1
}
}
$featureNum = ('{0:000}' -f $Number)
$branchName = "$featureNum-$branchSuffix"
# GitHub enforces a 244-byte limit on branch names
# Validate and truncate if necessary
$maxBranchLength = 244
if ($branchName.Length -gt $maxBranchLength) {
# Calculate how much we need to trim from suffix
# Account for: feature number (3) + hyphen (1) = 4 chars
$maxSuffixLength = $maxBranchLength - 4
# Truncate suffix
$truncatedSuffix = $branchSuffix.Substring(0, [Math]::Min($branchSuffix.Length, $maxSuffixLength))
# Remove trailing hyphen if truncation created one
$truncatedSuffix = $truncatedSuffix -replace '-$', ''
$originalBranchName = $branchName
$branchName = "$featureNum-$truncatedSuffix"
Write-Warning "[specify] Branch name exceeded GitHub's 244-byte limit"
Write-Warning "[specify] Original: $originalBranchName ($($originalBranchName.Length) bytes)"
Write-Warning "[specify] Truncated to: $branchName ($($branchName.Length) bytes)"
}
if ($hasGit) {
try {
git checkout -b $branchName | Out-Null
} catch {
Write-Warning "Failed to create git branch: $branchName"
}
} else {
Write-Warning "[specify] Warning: Git repository not detected; skipped branch creation for $branchName"
}
$featureDir = Join-Path $specsDir $branchName
New-Item -ItemType Directory -Path $featureDir -Force | Out-Null
$template = Join-Path $repoRoot '.specify/templates/spec-template.md'
$specFile = Join-Path $featureDir 'spec.md'
if (Test-Path $template) {
Copy-Item $template $specFile -Force
} else {
New-Item -ItemType File -Path $specFile | Out-Null
}
# Set the SPECIFY_FEATURE environment variable for the current session
$env:SPECIFY_FEATURE = $branchName
if ($Json) {
$obj = [PSCustomObject]@{
BRANCH_NAME = $branchName
SPEC_FILE = $specFile
FEATURE_NUM = $featureNum
HAS_GIT = $hasGit
}
$obj | ConvertTo-Json -Compress
} else {
Write-Output "BRANCH_NAME: $branchName"
Write-Output "SPEC_FILE: $specFile"
Write-Output "FEATURE_NUM: $featureNum"
Write-Output "HAS_GIT: $hasGit"
Write-Output "SPECIFY_FEATURE environment variable set to: $branchName"
}
@@ -1,61 +0,0 @@
#!/usr/bin/env pwsh
# Setup implementation plan for a feature
[CmdletBinding()]
param(
[switch]$Json,
[switch]$Help
)
$ErrorActionPreference = 'Stop'
# Show help if requested
if ($Help) {
Write-Output "Usage: ./setup-plan.ps1 [-Json] [-Help]"
Write-Output " -Json Output results in JSON format"
Write-Output " -Help Show this help message"
exit 0
}
# Load common functions
. "$PSScriptRoot/common.ps1"
# Get all paths and variables from common functions
$paths = Get-FeaturePathsEnv
# Check if we're on a proper feature branch (only for git repos)
if (-not (Test-FeatureBranch -Branch $paths.CURRENT_BRANCH -HasGit $paths.HAS_GIT)) {
exit 1
}
# Ensure the feature directory exists
New-Item -ItemType Directory -Path $paths.FEATURE_DIR -Force | Out-Null
# Copy plan template if it exists, otherwise note it or create empty file
$template = Join-Path $paths.REPO_ROOT '.specify/templates/plan-template.md'
if (Test-Path $template) {
Copy-Item $template $paths.IMPL_PLAN -Force
Write-Output "Copied plan template to $($paths.IMPL_PLAN)"
} else {
Write-Warning "Plan template not found at $template"
# Create a basic plan file if template doesn't exist
New-Item -ItemType File -Path $paths.IMPL_PLAN -Force | Out-Null
}
# Output results
if ($Json) {
$result = [PSCustomObject]@{
FEATURE_SPEC = $paths.FEATURE_SPEC
IMPL_PLAN = $paths.IMPL_PLAN
SPECS_DIR = $paths.FEATURE_DIR
BRANCH = $paths.CURRENT_BRANCH
HAS_GIT = $paths.HAS_GIT
}
$result | ConvertTo-Json -Compress
} else {
Write-Output "FEATURE_SPEC: $($paths.FEATURE_SPEC)"
Write-Output "IMPL_PLAN: $($paths.IMPL_PLAN)"
Write-Output "SPECS_DIR: $($paths.FEATURE_DIR)"
Write-Output "BRANCH: $($paths.CURRENT_BRANCH)"
Write-Output "HAS_GIT: $($paths.HAS_GIT)"
}
@@ -1,442 +0,0 @@
#!/usr/bin/env pwsh
<#!
.SYNOPSIS
Update agent context files with information from plan.md (PowerShell version)
.DESCRIPTION
Mirrors the behavior of scripts/bash/update-agent-context.sh:
1. Environment Validation
2. Plan Data Extraction
3. Agent File Management (create from template or update existing)
4. Content Generation (technology stack, recent changes, timestamp)
5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, codebuddy, amp, shai, q)
.PARAMETER AgentType
Optional agent key to update a single agent. If omitted, updates all existing agent files (creating a default Claude file if none exist).
.EXAMPLE
./update-agent-context.ps1 -AgentType claude
.EXAMPLE
./update-agent-context.ps1 # Updates all existing agent files
.NOTES
Relies on common helper functions in common.ps1
#>
param(
[Parameter(Position=0)]
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','q')]
[string]$AgentType
)
$ErrorActionPreference = 'Stop'
# Import common helpers
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
. (Join-Path $ScriptDir 'common.ps1')
# Acquire environment paths
$envData = Get-FeaturePathsEnv
$REPO_ROOT = $envData.REPO_ROOT
$CURRENT_BRANCH = $envData.CURRENT_BRANCH
$HAS_GIT = $envData.HAS_GIT
$IMPL_PLAN = $envData.IMPL_PLAN
$NEW_PLAN = $IMPL_PLAN
# Agent file paths
$CLAUDE_FILE = Join-Path $REPO_ROOT 'CLAUDE.md'
$GEMINI_FILE = Join-Path $REPO_ROOT 'GEMINI.md'
$COPILOT_FILE = Join-Path $REPO_ROOT '.github/agents/copilot-instructions.md'
$CURSOR_FILE = Join-Path $REPO_ROOT '.cursor/rules/specify-rules.mdc'
$QWEN_FILE = Join-Path $REPO_ROOT 'QWEN.md'
$AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
$WINDSURF_FILE = Join-Path $REPO_ROOT '.windsurf/rules/specify-rules.md'
$KILOCODE_FILE = Join-Path $REPO_ROOT '.kilocode/rules/specify-rules.md'
$AUGGIE_FILE = Join-Path $REPO_ROOT '.augment/rules/specify-rules.md'
$ROO_FILE = Join-Path $REPO_ROOT '.roo/rules/specify-rules.md'
$CODEBUDDY_FILE = Join-Path $REPO_ROOT 'CODEBUDDY.md'
$AMP_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
$SHAI_FILE = Join-Path $REPO_ROOT 'SHAI.md'
$Q_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
$TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md'
# Parsed plan data placeholders
$script:NEW_LANG = ''
$script:NEW_FRAMEWORK = ''
$script:NEW_DB = ''
$script:NEW_PROJECT_TYPE = ''
function Write-Info {
param(
[Parameter(Mandatory=$true)]
[string]$Message
)
Write-Host "INFO: $Message"
}
function Write-Success {
param(
[Parameter(Mandatory=$true)]
[string]$Message
)
Write-Host "$([char]0x2713) $Message"
}
function Write-WarningMsg {
param(
[Parameter(Mandatory=$true)]
[string]$Message
)
Write-Warning $Message
}
function Write-Err {
param(
[Parameter(Mandatory=$true)]
[string]$Message
)
Write-Host "ERROR: $Message" -ForegroundColor Red
}
function Validate-Environment {
if (-not $CURRENT_BRANCH) {
Write-Err 'Unable to determine current feature'
if ($HAS_GIT) { Write-Info "Make sure you're on a feature branch" } else { Write-Info 'Set SPECIFY_FEATURE environment variable or create a feature first' }
exit 1
}
if (-not (Test-Path $NEW_PLAN)) {
Write-Err "No plan.md found at $NEW_PLAN"
Write-Info 'Ensure you are working on a feature with a corresponding spec directory'
if (-not $HAS_GIT) { Write-Info 'Use: $env:SPECIFY_FEATURE=your-feature-name or create a new feature first' }
exit 1
}
if (-not (Test-Path $TEMPLATE_FILE)) {
Write-Err "Template file not found at $TEMPLATE_FILE"
Write-Info 'Run specify init to scaffold .specify/templates, or add agent-file-template.md there.'
exit 1
}
}
function Extract-PlanField {
param(
[Parameter(Mandatory=$true)]
[string]$FieldPattern,
[Parameter(Mandatory=$true)]
[string]$PlanFile
)
if (-not (Test-Path $PlanFile)) { return '' }
# Lines like **Language/Version**: Python 3.12
$regex = "^\*\*$([Regex]::Escape($FieldPattern))\*\*: (.+)$"
Get-Content -LiteralPath $PlanFile -Encoding utf8 | ForEach-Object {
if ($_ -match $regex) {
$val = $Matches[1].Trim()
if ($val -notin @('NEEDS CLARIFICATION','N/A')) { return $val }
}
} | Select-Object -First 1
}
function Parse-PlanData {
param(
[Parameter(Mandatory=$true)]
[string]$PlanFile
)
if (-not (Test-Path $PlanFile)) { Write-Err "Plan file not found: $PlanFile"; return $false }
Write-Info "Parsing plan data from $PlanFile"
$script:NEW_LANG = Extract-PlanField -FieldPattern 'Language/Version' -PlanFile $PlanFile
$script:NEW_FRAMEWORK = Extract-PlanField -FieldPattern 'Primary Dependencies' -PlanFile $PlanFile
$script:NEW_DB = Extract-PlanField -FieldPattern 'Storage' -PlanFile $PlanFile
$script:NEW_PROJECT_TYPE = Extract-PlanField -FieldPattern 'Project Type' -PlanFile $PlanFile
if ($NEW_LANG) { Write-Info "Found language: $NEW_LANG" } else { Write-WarningMsg 'No language information found in plan' }
if ($NEW_FRAMEWORK) { Write-Info "Found framework: $NEW_FRAMEWORK" }
if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Info "Found database: $NEW_DB" }
if ($NEW_PROJECT_TYPE) { Write-Info "Found project type: $NEW_PROJECT_TYPE" }
return $true
}
function Format-TechnologyStack {
param(
[Parameter(Mandatory=$false)]
[string]$Lang,
[Parameter(Mandatory=$false)]
[string]$Framework
)
$parts = @()
if ($Lang -and $Lang -ne 'NEEDS CLARIFICATION') { $parts += $Lang }
if ($Framework -and $Framework -notin @('NEEDS CLARIFICATION','N/A')) { $parts += $Framework }
if (-not $parts) { return '' }
return ($parts -join ' + ')
}
function Get-ProjectStructure {
param(
[Parameter(Mandatory=$false)]
[string]$ProjectType
)
if ($ProjectType -match 'web') { return "backend/`nfrontend/`ntests/" } else { return "src/`ntests/" }
}
function Get-CommandsForLanguage {
param(
[Parameter(Mandatory=$false)]
[string]$Lang
)
switch -Regex ($Lang) {
'Python' { return "cd src; pytest; ruff check ." }
'Rust' { return "cargo test; cargo clippy" }
'JavaScript|TypeScript' { return "npm test; npm run lint" }
default { return "# Add commands for $Lang" }
}
}
function Get-LanguageConventions {
param(
[Parameter(Mandatory=$false)]
[string]$Lang
)
if ($Lang) { "${Lang}: Follow standard conventions" } else { 'General: Follow standard conventions' }
}
function New-AgentFile {
param(
[Parameter(Mandatory=$true)]
[string]$TargetFile,
[Parameter(Mandatory=$true)]
[string]$ProjectName,
[Parameter(Mandatory=$true)]
[datetime]$Date
)
if (-not (Test-Path $TEMPLATE_FILE)) { Write-Err "Template not found at $TEMPLATE_FILE"; return $false }
$temp = New-TemporaryFile
Copy-Item -LiteralPath $TEMPLATE_FILE -Destination $temp -Force
$projectStructure = Get-ProjectStructure -ProjectType $NEW_PROJECT_TYPE
$commands = Get-CommandsForLanguage -Lang $NEW_LANG
$languageConventions = Get-LanguageConventions -Lang $NEW_LANG
$escaped_lang = $NEW_LANG
$escaped_framework = $NEW_FRAMEWORK
$escaped_branch = $CURRENT_BRANCH
$content = Get-Content -LiteralPath $temp -Raw -Encoding utf8
$content = $content -replace '\[PROJECT NAME\]',$ProjectName
$content = $content -replace '\[DATE\]',$Date.ToString('yyyy-MM-dd')
# Build the technology stack string safely
$techStackForTemplate = ""
if ($escaped_lang -and $escaped_framework) {
$techStackForTemplate = "- $escaped_lang + $escaped_framework ($escaped_branch)"
} elseif ($escaped_lang) {
$techStackForTemplate = "- $escaped_lang ($escaped_branch)"
} elseif ($escaped_framework) {
$techStackForTemplate = "- $escaped_framework ($escaped_branch)"
}
$content = $content -replace '\[EXTRACTED FROM ALL PLAN.MD FILES\]',$techStackForTemplate
# For project structure we manually embed (keep newlines)
$escapedStructure = [Regex]::Escape($projectStructure)
$content = $content -replace '\[ACTUAL STRUCTURE FROM PLANS\]',$escapedStructure
# Replace escaped newlines placeholder after all replacements
$content = $content -replace '\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]',$commands
$content = $content -replace '\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]',$languageConventions
# Build the recent changes string safely
$recentChangesForTemplate = ""
if ($escaped_lang -and $escaped_framework) {
$recentChangesForTemplate = "- ${escaped_branch}: Added ${escaped_lang} + ${escaped_framework}"
} elseif ($escaped_lang) {
$recentChangesForTemplate = "- ${escaped_branch}: Added ${escaped_lang}"
} elseif ($escaped_framework) {
$recentChangesForTemplate = "- ${escaped_branch}: Added ${escaped_framework}"
}
$content = $content -replace '\[LAST 3 FEATURES AND WHAT THEY ADDED\]',$recentChangesForTemplate
# Convert literal \n sequences introduced by Escape to real newlines
$content = $content -replace '\\n',[Environment]::NewLine
$parent = Split-Path -Parent $TargetFile
if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent | Out-Null }
Set-Content -LiteralPath $TargetFile -Value $content -NoNewline -Encoding utf8
Remove-Item $temp -Force
return $true
}
function Update-ExistingAgentFile {
param(
[Parameter(Mandatory=$true)]
[string]$TargetFile,
[Parameter(Mandatory=$true)]
[datetime]$Date
)
if (-not (Test-Path $TargetFile)) { return (New-AgentFile -TargetFile $TargetFile -ProjectName (Split-Path $REPO_ROOT -Leaf) -Date $Date) }
$techStack = Format-TechnologyStack -Lang $NEW_LANG -Framework $NEW_FRAMEWORK
$newTechEntries = @()
if ($techStack) {
$escapedTechStack = [Regex]::Escape($techStack)
if (-not (Select-String -Pattern $escapedTechStack -Path $TargetFile -Quiet)) {
$newTechEntries += "- $techStack ($CURRENT_BRANCH)"
}
}
if ($NEW_DB -and $NEW_DB -notin @('N/A','NEEDS CLARIFICATION')) {
$escapedDB = [Regex]::Escape($NEW_DB)
if (-not (Select-String -Pattern $escapedDB -Path $TargetFile -Quiet)) {
$newTechEntries += "- $NEW_DB ($CURRENT_BRANCH)"
}
}
$newChangeEntry = ''
if ($techStack) { $newChangeEntry = "- ${CURRENT_BRANCH}: Added ${techStack}" }
elseif ($NEW_DB -and $NEW_DB -notin @('N/A','NEEDS CLARIFICATION')) { $newChangeEntry = "- ${CURRENT_BRANCH}: Added ${NEW_DB}" }
$lines = Get-Content -LiteralPath $TargetFile -Encoding utf8
$output = New-Object System.Collections.Generic.List[string]
$inTech = $false; $inChanges = $false; $techAdded = $false; $changeAdded = $false; $existingChanges = 0
for ($i=0; $i -lt $lines.Count; $i++) {
$line = $lines[$i]
if ($line -eq '## Active Technologies') {
$output.Add($line)
$inTech = $true
continue
}
if ($inTech -and $line -match '^##\s') {
if (-not $techAdded -and $newTechEntries.Count -gt 0) { $newTechEntries | ForEach-Object { $output.Add($_) }; $techAdded = $true }
$output.Add($line); $inTech = $false; continue
}
if ($inTech -and [string]::IsNullOrWhiteSpace($line)) {
if (-not $techAdded -and $newTechEntries.Count -gt 0) { $newTechEntries | ForEach-Object { $output.Add($_) }; $techAdded = $true }
$output.Add($line); continue
}
if ($line -eq '## Recent Changes') {
$output.Add($line)
if ($newChangeEntry) { $output.Add($newChangeEntry); $changeAdded = $true }
$inChanges = $true
continue
}
if ($inChanges -and $line -match '^##\s') { $output.Add($line); $inChanges = $false; continue }
if ($inChanges -and $line -match '^- ') {
if ($existingChanges -lt 2) { $output.Add($line); $existingChanges++ }
continue
}
if ($line -match '\*\*Last updated\*\*: .*\d{4}-\d{2}-\d{2}') {
$output.Add(($line -replace '\d{4}-\d{2}-\d{2}',$Date.ToString('yyyy-MM-dd')))
continue
}
$output.Add($line)
}
# Post-loop check: if we're still in the Active Technologies section and haven't added new entries
if ($inTech -and -not $techAdded -and $newTechEntries.Count -gt 0) {
$newTechEntries | ForEach-Object { $output.Add($_) }
}
Set-Content -LiteralPath $TargetFile -Value ($output -join [Environment]::NewLine) -Encoding utf8
return $true
}
function Update-AgentFile {
param(
[Parameter(Mandatory=$true)]
[string]$TargetFile,
[Parameter(Mandatory=$true)]
[string]$AgentName
)
if (-not $TargetFile -or -not $AgentName) { Write-Err 'Update-AgentFile requires TargetFile and AgentName'; return $false }
Write-Info "Updating $AgentName context file: $TargetFile"
$projectName = Split-Path $REPO_ROOT -Leaf
$date = Get-Date
$dir = Split-Path -Parent $TargetFile
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir | Out-Null }
if (-not (Test-Path $TargetFile)) {
if (New-AgentFile -TargetFile $TargetFile -ProjectName $projectName -Date $date) { Write-Success "Created new $AgentName context file" } else { Write-Err 'Failed to create new agent file'; return $false }
} else {
try {
if (Update-ExistingAgentFile -TargetFile $TargetFile -Date $date) { Write-Success "Updated existing $AgentName context file" } else { Write-Err 'Failed to update agent file'; return $false }
} catch {
Write-Err "Cannot access or update existing file: $TargetFile. $_"
return $false
}
}
return $true
}
function Update-SpecificAgent {
param(
[Parameter(Mandatory=$true)]
[string]$Type
)
switch ($Type) {
'claude' { Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code' }
'gemini' { Update-AgentFile -TargetFile $GEMINI_FILE -AgentName 'Gemini CLI' }
'copilot' { Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot' }
'cursor-agent' { Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE' }
'qwen' { Update-AgentFile -TargetFile $QWEN_FILE -AgentName 'Qwen Code' }
'opencode' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'opencode' }
'codex' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'Codex CLI' }
'windsurf' { Update-AgentFile -TargetFile $WINDSURF_FILE -AgentName 'Windsurf' }
'kilocode' { Update-AgentFile -TargetFile $KILOCODE_FILE -AgentName 'Kilo Code' }
'auggie' { Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI' }
'roo' { Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code' }
'codebuddy' { Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI' }
'amp' { Update-AgentFile -TargetFile $AMP_FILE -AgentName 'Amp' }
'shai' { Update-AgentFile -TargetFile $SHAI_FILE -AgentName 'SHAI' }
'q' { Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI' }
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q'; return $false }
}
}
function Update-AllExistingAgents {
$found = $false
$ok = $true
if (Test-Path $CLAUDE_FILE) { if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }; $found = $true }
if (Test-Path $GEMINI_FILE) { if (-not (Update-AgentFile -TargetFile $GEMINI_FILE -AgentName 'Gemini CLI')) { $ok = $false }; $found = $true }
if (Test-Path $COPILOT_FILE) { if (-not (Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot')) { $ok = $false }; $found = $true }
if (Test-Path $CURSOR_FILE) { if (-not (Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE')) { $ok = $false }; $found = $true }
if (Test-Path $QWEN_FILE) { if (-not (Update-AgentFile -TargetFile $QWEN_FILE -AgentName 'Qwen Code')) { $ok = $false }; $found = $true }
if (Test-Path $AGENTS_FILE) { if (-not (Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'Codex/opencode')) { $ok = $false }; $found = $true }
if (Test-Path $WINDSURF_FILE) { if (-not (Update-AgentFile -TargetFile $WINDSURF_FILE -AgentName 'Windsurf')) { $ok = $false }; $found = $true }
if (Test-Path $KILOCODE_FILE) { if (-not (Update-AgentFile -TargetFile $KILOCODE_FILE -AgentName 'Kilo Code')) { $ok = $false }; $found = $true }
if (Test-Path $AUGGIE_FILE) { if (-not (Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI')) { $ok = $false }; $found = $true }
if (Test-Path $ROO_FILE) { if (-not (Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code')) { $ok = $false }; $found = $true }
if (Test-Path $CODEBUDDY_FILE) { if (-not (Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI')) { $ok = $false }; $found = $true }
if (Test-Path $SHAI_FILE) { if (-not (Update-AgentFile -TargetFile $SHAI_FILE -AgentName 'SHAI')) { $ok = $false }; $found = $true }
if (Test-Path $Q_FILE) { if (-not (Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI')) { $ok = $false }; $found = $true }
if (-not $found) {
Write-Info 'No existing agent files found, creating default Claude file...'
if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }
}
return $ok
}
function Print-Summary {
Write-Host ''
Write-Info 'Summary of changes:'
if ($NEW_LANG) { Write-Host " - Added language: $NEW_LANG" }
if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" }
if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" }
Write-Host ''
Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q]'
}
function Main {
Validate-Environment
Write-Info "=== Updating agent context files for feature $CURRENT_BRANCH ==="
if (-not (Parse-PlanData -PlanFile $NEW_PLAN)) { Write-Err 'Failed to parse plan data'; exit 1 }
$success = $true
if ($AgentType) {
Write-Info "Updating specific agent: $AgentType"
if (-not (Update-SpecificAgent -Type $AgentType)) { $success = $false }
}
else {
Write-Info 'No agent specified, updating all existing agent files...'
if (-not (Update-AllExistingAgents)) { $success = $false }
}
Print-Summary
if ($success) { Write-Success 'Agent context update completed successfully'; exit 0 } else { Write-Err 'Agent context update completed with errors'; exit 1 }
}
Main
-23
View File
@@ -1,23 +0,0 @@
# [PROJECT NAME] 开发指南
基于所有功能计划自动生成. 最后更新时间: [DATE]
## 活跃技术
[EXTRACTED FROM ALL PLAN.MD FILES]
## 项目结构
```
[ACTUAL STRUCTURE FROM PLANS]
```
## 命令
[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES]
## 代码风格
[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE]
## 最近变更
[LAST 3 FEATURES AND WHAT THEY ADDED]
<!-- 手动添加内容开始 -->
<!-- 手动添加内容结束 -->
-40
View File
@@ -1,40 +0,0 @@
# [检查清单类型] 检查清单: [功能名称]
**目的**: [此检查清单涵盖内容的简要描述]
**创建时间**: [日期]
**功能**: [链接到 spec.md 或相关文档]
**注意**: 此检查清单由 `/speckit.checklist` 命令基于功能上下文和需求生成.
<!--
============================================================================
重要说明: 以下检查清单项目仅为示例项目, 仅供说明用途.
/speckit.checklist 命令必须根据以下内容替换为实际项目:
- 用户的具体检查清单请求
- 来自 spec.md 的功能需求
- 来自 plan.md 的技术上下文
- 来自 tasks.md 的实施细节
请勿在生成的检查清单文件中保留这些示例项目.
============================================================================
-->
## [类别 1]
- [ ] CHK001 第一个检查清单项目, 具有明确的行动
- [ ] CHK002 第二个检查清单项目
- [ ] CHK003 第三个检查清单项目
## [类别 2]
- [ ] CHK004 另一个类别项目
- [ ] CHK005 具有特定标准的项目
- [ ] CHK006 此类别中的最后一个项目
## 备注
- 完成项目时勾选: `[x]`
- 内联添加评论或发现
- 链接到相关资源或文档
- 项目按顺序编号, 便于参考
-101
View File
@@ -1,101 +0,0 @@
# 实施计划: [FEATURE]
**分支**: `[###-feature-name]` | **日期**: [DATE] | **规范**: [link]
**输入**: 来自 `/specs/[###-feature-name]/spec.md` 的功能规范
**注意**: 此模板由 `/speckit.plan` 命令填充. 执行工作流程请参见 `.specify/templates/commands/plan.md`.
## 摘要
[从功能规范中提取: 主要需求 + 研究得出的技术方法]
## 技术背景
<!--
需要操作: 将此部分内容替换为项目的技术细节.
此处的结构以咨询性质呈现, 用于指导迭代过程.
-->
**语言/版本**: [例如: Python 3.11、Swift 5.9、Rust 1.75 或 NEEDS CLARIFICATION]
**主要依赖**: [例如: FastAPI、UIKit、LLVM 或 NEEDS CLARIFICATION]
**存储**: [如适用, 例如: PostgreSQL、CoreData、文件 或 N/A]
**测试**: [例如: pytest、XCTest、cargo test 或 NEEDS CLARIFICATION]
**目标平台**: [例如: Linux 服务器、iOS 15+、WASM 或 NEEDS CLARIFICATION]
**项目类型**: [单一/网页/移动 - 决定源代码结构]
**性能目标**: [领域特定, 例如: 1000 请求/秒、10k 行/秒、60 fps 或 NEEDS CLARIFICATION]
**约束条件**: [领域特定, 例如: <200ms p95、<100MB 内存、离线可用 或 NEEDS CLARIFICATION]
**规模/范围**: [领域特定, 例如: 10k 用户、1M 行代码、50 个屏幕 或 NEEDS CLARIFICATION]
## 章程检查
*门控: 必须在阶段 0 研究前通过. 阶段 1 设计后重新检查. *
[基于章程文件确定的门控条件]
## 项目结构
### 文档(此功能)
```
specs/[###-feature]/
├── plan.md # 此文件 (/speckit.plan 命令输出)
├── research.md # 阶段 0 输出 (/speckit.plan 命令)
├── data-model.md # 阶段 1 输出 (/speckit.plan 命令)
├── quickstart.md # 阶段 1 输出 (/speckit.plan 命令)
├── contracts/ # 阶段 1 输出 (/speckit.plan 命令)
└── tasks.md # 阶段 2 输出 (/speckit.tasks 命令 - 非 /speckit.plan 创建)
```
### 源代码(仓库根目录)
<!--
需要操作: 将下面的占位符树结构替换为此功能的具体布局.
删除未使用的选项, 并使用真实路径(例如: apps/admin、packages/something)扩展所选结构.
交付的计划不得包含选项标签.
-->
```
# [如未使用请删除] 选项 1: 单一项目(默认)
src/
├── models/
├── services/
├── cli/
└── lib/
tests/
├── contract/
├── integration/
└── unit/
# [如未使用请删除] 选项 2: Web 应用程序(检测到"前端" + "后端"时)
backend/
├── src/
│ ├── models/
│ ├── services/
│ └── api/
└── tests/
frontend/
├── src/
│ ├── components/
│ ├── pages/
│ └── services/
└── tests/
# [如未使用请删除] 选项 3: 移动端 + API(检测到 "iOS/Android" 时)
api/
└── [同上后端结构]
ios/ 或 android/
└── [平台特定结构: 功能模块、UI 流程、平台测试]
```
**结构决策**: [记录所选结构并引用上面捕获的真实目录]
## 复杂度跟踪
*仅在章程检查有必须证明的违规时填写*
| 违规 | 为什么需要 | 拒绝更简单替代方案的原因 |
|-----------|------------|-------------------------------------|
| [例如: 第 4 个项目] | [当前需求] | [为什么 3 个项目不够] |
| [例如: 仓储模式] | [特定问题] | [为什么直接数据库访问不够] |
-115
View File
@@ -1,115 +0,0 @@
# 功能规范: [FEATURE NAME]
**功能分支**: `[###-feature-name]`
**创建时间**: [DATE]
**状态**: 草稿
**输入**: 用户描述: "$ARGUMENTS"
## 用户场景与测试 *(必填)*
<!--
重要说明: 用户故事应按重要性排序, 作为用户旅程进行优先级划分.
每个用户故事/旅程必须能够独立测试——这意味着即使只实现其中一个,
你仍然应该有一个可行的 MVP(最小可行产品)来交付价值.
为每个故事分配优先级(P1、P2、P3 等), 其中 P1 是最关键的.
将每个故事视为独立的功能切片, 可以:
- 独立开发
- 独立测试
- 独立部署
- 独立向用户演示
-->
### 用户故事 1 - [简要标题] (优先级: P1)
[用通俗语言描述这个用户旅程]
**优先级原因**: [解释价值以及为什么具有此优先级]
**独立测试**: [描述如何独立测试 - 例如: "可以通过 [具体操作] 完全测试并交付 [具体价值]"]
**验收场景**:
1. **给定** [初始状态], **当** [操作时], **那么** [预期结果]
2. **给定** [初始状态], **当** [操作时], **那么** [预期结果]
---
### 用户故事 2 - [简要标题] (优先级: P2)
[用通俗语言描述这个用户旅程]
**优先级原因**: [解释价值以及为什么具有此优先级]
**独立测试**: [描述如何独立测试]
**验收场景**:
1. **给定** [初始状态], **当** [操作时], **那么** [预期结果]
---
### 用户故事 3 - [简要标题] (优先级: P3)
[用通俗语言描述这个用户旅程]
**优先级原因**: [解释价值以及为什么具有此优先级]
**独立测试**: [描述如何独立测试]
**验收场景**:
1. **给定** [初始状态], **当** [操作时], **那么** [预期结果]
---
[根据需要添加更多用户故事, 每个都分配优先级]
### 边界情况
<!--
需要操作: 本节内容表示占位符.
请用正确的边界情况填写.
-->
- 当 [边界条件] 时会发生什么?
- 系统如何处理 [错误场景]
## 需求 *(必填)*
<!--
需要操作: 本节内容表示占位符.
请用正确的功能需求填写.
-->
### 功能需求
- **FR-001**: 系统必须 [具体能力, 例如: "允许用户创建账户"]
- **FR-002**: 系统必须 [具体能力, 例如: "验证电子邮件地址"]
- **FR-003**: 用户必须能够 [关键交互, 例如: "重置密码"]
- **FR-004**: 系统必须 [数据需求, 例如: "持久化用户偏好设置"]
- **FR-005**: 系统必须 [行为, 例如: "记录所有安全事件"]
*标记不明确需求的示例: *
- **FR-006**: 系统必须通过 [NEEDS CLARIFICATION: 未指定认证方法 - 电子邮件/密码、SSO、OAuth?] 认证用户
- **FR-007**: 系统必须保留用户数据 [NEEDS CLARIFICATION: 未指定保留期限]
### 关键实体 *(如果功能涉及数据则包含)*
- **[实体 1]**: [它代表什么, 关键属性(不含实现细节)]
- **[实体 2]**: [它代表什么, 与其他实体的关系]
## 成功标准 *(必填)*
<!--
需要操作: 定义可衡量的成功标准.
这些标准必须与技术无关且可衡量.
-->
### 可衡量的结果
- **SC-001**: [可衡量的指标, 例如: "用户可以在 2 分钟内完成账户创建"]
- **SC-002**: [可衡量的指标, 例如: "系统处理 1000 个并发用户而不降低性能"]
- **SC-003**: [用户满意度指标, 例如: "90% 的用户在首次尝试时成功完成主要任务"]
- **SC-004**: [业务指标, 例如: "将与 [X] 相关的支持工单减少 50%"]
-249
View File
@@ -1,249 +0,0 @@
---
description: "功能实现任务列表模板"
---
# 任务: [FEATURE NAME]
**输入**: 来自 `/specs/[###-feature-name]/` 的设计文档
**前置条件**: plan.md(必需)、spec.md(用户故事必需)、research.md、data-model.md、contracts/
**测试**: 以下示例包含测试任务. 测试是可选的 - 仅在功能规范中明确要求时才包含.
**组织结构**: 任务按用户故事分组, 以便每个故事能够独立实施和测试.
## 格式: `[ID] [P] [Story] 描述`
- **[P]**: 可以并行运行(不同文件, 无依赖关系)
- **[Story]**: 此任务属于哪个用户故事(例如: US1、US2、US3)
- 在描述中包含确切的文件路径
## 路径约定
- **单一项目**: 仓库根目录下的 `src/``tests/`
- **Web 应用**: `backend/src/``frontend/src/`
- **移动应用**: `api/src/``ios/src/``android/src/`
- 以下显示的路径假设为单一项目 - 根据 plan.md 结构进行调整
<!--
============================================================================
重要说明: 以下任务仅为说明目的的示例任务.
/speckit.tasks 命令必须根据以下内容替换为实际任务:
- 来自 spec.md 的用户故事(及其优先级 P1、P2、P3...)
- 来自 plan.md 的功能需求
- 来自 data-model.md 的实体
- 来自 contracts/ 的端点
任务必须按用户故事组织, 以便每个故事能够:
- 独立实施
- 独立测试
- 作为 MVP 增量交付
不要在生成的 tasks.md 文件中保留这些示例任务.
============================================================================
-->
## 阶段 1: 设置(共享基础设施)
**目的**: 项目初始化和基本结构
- [ ] T001 根据实施计划创建项目结构
- [ ] T002 使用 [framework] 依赖项初始化 [language] 项目
- [ ] T003 [P] 配置代码检查和格式化工具
---
## 阶段 2: 基础(阻塞前置条件)
**目的**: 在任何用户故事可以实施之前必须完成的核心基础设施
**⚠️ 关键**: 在此阶段完成之前, 无法开始任何用户故事工作
基础任务示例(根据你的项目调整):
- [ ] T004 设置数据库架构和迁移框架
- [ ] T005 [P] 实施身份验证/授权框架
- [ ] T006 [P] 设置 API 路由和中间件结构
- [ ] T007 创建所有故事依赖的基础模型/实体
- [ ] T008 配置错误处理和日志记录基础设施
- [ ] T009 设置环境配置管理
**检查点**: 基础就绪 - 现在可以开始并行实施用户故事
---
## 阶段 3: 用户故事 1 - [Title](优先级: P1)🎯 MVP
**目标**: [此故事交付内容的简要描述]
**独立测试**: [如何验证此故事独立运行]
### 用户故事 1 的测试(可选 - 仅在要求测试时)⚠️
**注意: 先编写这些测试, 确保在实施前它们失败**
- [ ] T010 [P] [US1] 在 tests/contract/test_[name].py 中为 [endpoint] 编写合约测试
- [ ] T011 [P] [US1] 在 tests/integration/test_[name].py 中为 [user journey] 编写集成测试
### 用户故事 1 的实施
- [ ] T012 [P] [US1] 在 src/models/[entity1].py 中创建 [Entity1] 模型
- [ ] T013 [P] [US1] 在 src/models/[entity2].py 中创建 [Entity2] 模型
- [ ] T014 [US1] 在 src/services/[service].py 中实施 [Service](依赖于 T012、T013)
- [ ] T015 [US1] 在 src/[location]/[file].py 中实施 [endpoint/feature]
- [ ] T016 [US1] 添加验证和错误处理
- [ ] T017 [US1] 为用户故事 1 操作添加日志记录
**检查点**: 此时, 用户故事 1 应该完全功能化且可独立测试
---
## 阶段 4: 用户故事 2 - [Title](优先级: P2)
**目标**: [此故事交付内容的简要描述]
**独立测试**: [如何验证此故事独立运行]
### 用户故事 2 的测试(可选 - 仅在要求测试时)⚠️
- [ ] T018 [P] [US2] 在 tests/contract/test_[name].py 中为 [endpoint] 编写合约测试
- [ ] T019 [P] [US2] 在 tests/integration/test_[name].py 中为 [user journey] 编写集成测试
### 用户故事 2 的实施
- [ ] T020 [P] [US2] 在 src/models/[entity].py 中创建 [Entity] 模型
- [ ] T021 [US2] 在 src/services/[service].py 中实施 [Service]
- [ ] T022 [US2] 在 src/[location]/[file].py 中实施 [endpoint/feature]
- [ ] T023 [US2] 与用户故事 1 组件集成(如需要)
**检查点**: 此时, 用户故事 1 和 2 都应该独立运行
---
## 阶段 5: 用户故事 3 - [Title](优先级: P3)
**目标**: [此故事交付内容的简要描述]
**独立测试**: [如何验证此故事独立运行]
### 用户故事 3 的测试(可选 - 仅在要求测试时)⚠️
- [ ] T024 [P] [US3] 在 tests/contract/test_[name].py 中为 [endpoint] 编写合约测试
- [ ] T025 [P] [US3] 在 tests/integration/test_[name].py 中为 [user journey] 编写集成测试
### 用户故事 3 的实施
- [ ] T026 [P] [US3] 在 src/models/[entity].py 中创建 [Entity] 模型
- [ ] T027 [US3] 在 src/services/[service].py 中实施 [Service]
- [ ] T028 [US3] 在 src/[location]/[file].py 中实施 [endpoint/feature]
**检查点**: 所有用户故事现在应该独立功能化
---
[根据需要添加更多用户故事阶段, 遵循相同模式]
---
## 阶段 N: 完善与横切关注点
**目的**: 影响多个用户故事的改进
- [ ] TXXX [P] 在 docs/ 中更新文档
- [ ] TXXX 代码清理和重构
- [ ] TXXX 跨所有故事的性能优化
- [ ] TXXX [P] 在 tests/unit/ 中添加额外的单元测试(如要求)
- [ ] TXXX 安全加固
- [ ] TXXX 运行 quickstart.md 验证
---
## 依赖关系与执行顺序
### 阶段依赖关系
- **设置(阶段 1)**: 无依赖关系 - 可立即开始
- **基础(阶段 2)**: 依赖于设置完成 - 阻塞所有用户故事
- **用户故事(阶段 3+)**: 都依赖于基础阶段完成
- 然后用户故事可以并行进行(如果有人员)
- 或按优先级顺序进行(P1 → P2 → P3)
- **完善(最终阶段)**: 依赖于所有期望的用户故事完成
### 用户故事依赖关系
- **用户故事 1(P1)**: 可在基础(阶段 2)后开始 - 无其他故事依赖
- **用户故事 2(P2)**: 可在基础(阶段 2)后开始 - 可与 US1 集成但应独立可测试
- **用户故事 3(P3)**: 可在基础(阶段 2)后开始 - 可与 US1/US2 集成但应独立可测试
### 每个用户故事内部
- 测试(如包含)必须在实施前编写并失败
- 模型在服务之前
- 服务在端点之前
- 核心实施在集成之前
- 故事完成后才移至下一个优先级
### 并行机会
- 所有标记为 [P] 的设置任务可以并行运行
- 所有标记为 [P] 的基础任务可以并行运行(在阶段 2 内)
- 基础阶段完成后, 所有用户故事可以并行开始(如果团队容量允许)
- 用户故事中所有标记为 [P] 的测试可以并行运行
- 故事中标记为 [P] 的模型可以并行运行
- 不同用户故事可以由不同团队成员并行处理
---
## 并行示例: 用户故事 1
```bash
# 一起启动用户故事 1 的所有测试(如要求测试):
任务: "在 tests/contract/test_[name].py 中为 [endpoint] 编写合约测试"
任务: "在 tests/integration/test_[name].py 中为 [user journey] 编写集成测试"
# 一起启动用户故事 1 的所有模型:
任务: "在 src/models/[entity1].py 中创建 [Entity1] 模型"
任务: "在 src/models/[entity2].py 中创建 [Entity2] 模型"
```
---
## 实施策略
### 仅 MVP(仅用户故事 1)
1. 完成阶段 1: 设置
2. 完成阶段 2: 基础(关键 - 阻塞所有故事)
3. 完成阶段 3: 用户故事 1
4. **停止并验证**: 独立测试用户故事 1
5. 如准备好则部署/演示
### 增量交付
1. 完成设置 + 基础 → 基础就绪
2. 添加用户故事 1 → 独立测试 → 部署/演示(MVP! )
3. 添加用户故事 2 → 独立测试 → 部署/演示
4. 添加用户故事 3 → 独立测试 → 部署/演示
5. 每个故事在不破坏先前故事的情况下增加价值
### 并行团队策略
有多个开发人员时:
1. 团队一起完成设置 + 基础
2. 基础完成后:
- 开发人员 A: 用户故事 1
- 开发人员 B: 用户故事 2
- 开发人员 C: 用户故事 3
3. 故事独立完成和集成
---
## 注意事项
- [P] 任务 = 不同文件, 无依赖关系
- [Story] 标签将任务映射到特定用户故事以实现可追溯性
- 每个用户故事应该独立可完成和可测试
- 在实施前验证测试失败
- 在每个任务或逻辑组后提交
- 在任何检查点停止以独立验证故事
- 避免: 模糊任务、相同文件冲突、破坏独立性的跨故事依赖
+133 -3
View File
@@ -130,11 +130,86 @@ When updating an existing project, the webhook:
### Next.js Configuration
- **next.config.js**:
- `next-intl` plugin wrapper for i18n
- Image domains: localhost, *.anthropic.com
- Image domains: localhost, *.anthropic.com, img.shields.io
- Lucide-react package import optimization
- **tsconfig.json**: ES2022 target, strict mode enabled
- **tsconfig.json**: ES2017 target, strict mode enabled, noUncheckedIndexedAccess enabled
- **Testing**: Vitest for unit tests, Playwright for E2E tests (configured but not extensively used yet)
### Project Discovery System
项目发现系统是自动化探索和收录 AI 项目的核心功能,采用**双 Agent 协作架构**实现上下文隔离:
#### 架构组件
1. **自定义 Agents** (`.claude/agents/`):
- `content-explorer-agent`: 项目内容探索专家,批量探索项目并生成结构化数据
- 使用 `agent-browser` 子任务并行探索 GitHub 项目
- 应用严格的内容质量标准(客观描述、避免营销术语、不写入动态数据)
- 生成符合 `ProjectInputSchema` 的 JSON 数据
- `api-submitter-agent`: API 提交专家,处理探索结果的提交和状态更新
- 批量标记任务为 IN_PROGRESS
- 提交探索数据到完成 API
- 自动重试失败的提交(指数退避,最多3次)
2. **Claude Commands** (`.claude/commands/`):
- `/discover-projects`: 主命令,协调探索和提交流程
- 参数解析(任务数量、批次大小)
- 分批处理(默认每批3个任务)
- Agent 调度和进度显示
- 结果汇总和错误报告
3. **API Endpoints** (`src/app/api/discovery/`):
- `POST /api/discovery/tasks`: 创建新的探索任务(支持批量)
- `GET /api/discovery/tasks`: 获取待处理任务列表(支持 status/limit/offset 过滤)
- `PATCH /api/discovery/tasks/{id}`: 更新任务状态
- `POST /api/discovery/tasks/{id}/complete`: 完成任务并提交项目数据
- `GET /api/webhook/check-duplicates`: 检查项目是否已存在(URL 去重)
4. **Database Model**:
- `ProjectDiscoveryTask`: 任务追踪表
- 状态: PENDING → IN_PROGRESS → COMPLETED/FAILED
- 原始数据: `sourceUrl`, `sourceType`
- 探索结果: `explorationData` (JSON), `explorationSummary`
- 错误处理: `errorMessage`, `retryCount`, `lastRetryAt`
- 索引: `idx_task_status_created`, `idx_task_source_url`, `idx_task_project_id`
#### 数据流转
```
用户输入 URL → 创建 PENDING 任务 → /discover-projects 命令
分批获取任务(每批3个)
Content Explorer Agent (并行探索) → 探索结果 JSON
API Submitter Agent (提交到生产环境 API)
更新任务状态 → COMPLETED/FAILED
```
#### 质量标准
- **数据模板**: `.claude/schemas/project-content-template.md`
- **描述要求**: 清晰说明功能、突出价值、避免营销术语、10-500字
- **内容要求**: 从 README 提取并重新组织、不机械翻译、符合中文表达习惯
- **链接要求**: 必须包含 GITHUB 链接、所有链接可访问
- **标签要求**: 1-10 个标签、技术/应用/状态分类
- **动态数据处理**: Star/Fork 数量等动态数据不写入内容,使用 GitHub Badge 显示
#### 环境变量
- `WEBHOOK_API_KEY`: 生产环境 API 密钥(必需,用于认证)
#### 使用示例
```bash
# 处理默认10个任务(每批3个)
/discover-projects
# 处理指定数量的任务
/discover-projects 5
# 自定义批次大小
/discover-projects 9 --batch=2
# 处理所有待处理任务
/discover-projects all --batch=5
```
## MCP Servers Usage (按需使用)
1. **context7**: 不确定 API 用法时查阅最新文档
@@ -144,7 +219,7 @@ When updating an existing project, the webhook:
## Git Commits
提交信息主要使用中文,使用描述性的提交格式
Git 提交信息遵循约定式提交格式(详见上方 Code Quality & Standards → Git Commit Conventions
## Development Workflow
@@ -153,3 +228,58 @@ When updating an existing project, the webhook:
- The middleware handles locale detection and routing automatically - no manual locale configuration needed
- When adding new translations, update both `src/messages/zh.json` and `src/messages/en.json`
- Database changes require running `pnpm prisma migrate dev` to update the schema
## Code Quality & Standards
### TypeScript Configuration
- **Strict mode enabled** with additional safety flags: `noUncheckedIndexedAccess`, `noImplicitReturns`, `noFallthroughCasesInSwitch`
- Path alias: `@/*` maps to `./src/*`
- Target: ES2017 for modern browser support
### Validation & Security
- **API Authentication**: Webhook uses timing-safe comparison (`crypto.timingSafeEqual`) to prevent timing attacks
- **Input Validation**: All API inputs use Zod schemas with detailed error messages
- **SQL Injection Prevention**: Prisma ORM with parameterized queries
- **Data Sanitization**: Markdown content sanitized with `rehype-sanitize` plugin
### Error Handling Patterns
- **Webhook**: Partial success mode - continues processing remaining projects even if individual projects fail
- **Database**: Unique constraints use try-catch with fallback logic (e.g., tag slug conflicts in webhook)
- **Console**: Use `console.warn()` for operational logs, `console.error()` for errors
### Git Commit Conventions
- **Format**: `<type>: <description>` (type in lowercase Chinese: feat/fix/refactor/chore)
- **Types**: `feat` (新功能), `fix` (修复), `refactor` (重构), `chore` (杂项)
- **Examples**:
- `feat: 新增项目发现任务系统`
- `fix: 修复 ESLint 警告`
- `refactor: 重构项目内容标准实现职责分离`
## Testing Strategy
### Unit Tests (Vitest)
- Location: Test files co-located with source code (e.g., `*.test.ts`)
- Run: `pnpm test` for all tests, `pnpm test <pattern>` for specific tests
- Configuration: Vitest with `@testing-library/jest-dom` matchers
### E2E Tests (Playwright)
- Location: `tests/e2e/` or co-located with features
- Run: `pnpm test:e2e` to execute all E2E tests
- Usage: Focus on critical user journeys (project browsing, search, locale switching)
## Performance Considerations
### Database Optimization
- **Index Strategy**: Composite indexes on frequently queried fields (status+createdAt, type+url)
- **N+1 Prevention**: Batch queries for tags (see webhook route:94-98)
- **Connection Pooling**: Prisma client singleton pattern (`src/lib/prisma.ts`)
### Frontend Performance
- **ISR**: Project detail pages revalidated every 5 minutes (`revalidate = 300`)
- **Package Optimization**: Lucide-react imports optimized via `experimental.optimizePackageImports`
- **Image Domains**: Pre-configured for localhost, *.anthropic.com, img.shields.io
### API Rate Limiting
- Webhook: Max 100 projects per request
- Discovery tasks: Max 50 tasks per batch creation
- All queries: Max 100 items per page (enforced via Zod schemas)
@@ -1,36 +0,0 @@
# 规范质量检查清单: Agent Park - AI项目导航网站
**目的**: 在继续规划之前验证规范的完整性和质量
**创建时间**: 2025-12-25
**功能**: [spec.md](../spec.md)
## 内容质量
- [x] 无实现细节(语言、框架、API)
- [x] 专注于用户价值和业务需求
- [x] 为非技术利益相关者编写
- [x] 所有必需章节已完成
## 需求完整性
- [x] 没有 [NEEDS CLARIFICATION] 标记剩余
- [x] 需求是可测试且明确的
- [x] 成功标准是可衡量的
- [x] 成功标准是技术无关的(无实现细节)
- [x] 所有验收场景已定义
- [x] 边缘情况已识别
- [x] 范围明确界定
- [x] 依赖关系和假设已识别
## 功能准备就绪
- [x] 所有功能需求都有明确的验收标准
- [x] 用户场景覆盖主要流程
- [x] 功能满足成功标准中定义的可衡量结果
- [x] 没有实现细节泄漏到规范中
## 备注
- 标记为不完整的项目需要在 `/speckit.clarify``/speckit.plan` 之前更新规范
- 所有检查项目均已通过验证,规范质量符合要求
- 规范已准备好进入下一阶段(规划或澄清)
@@ -1,456 +0,0 @@
openapi: 3.0.3
info:
title: Agent Park Webhook API
description: |
API 接口用于接收 n8n 工作流程推送的 AI 项目数据更新。
**认证方式**: 请求头中携带 `X-API-Key` 进行身份验证。
**数据处理模式**: 部分成功模式 - 部分项目验证失败时,有效项目正常入库,失败项目记录错误信息。
**响应格式**: JSON
version: 1.0.0
contact:
name: Agent Park Team
email: support@agent-park.example
servers:
- url: http://localhost:3000
description: 本地开发环境
- url: https://agent-park.example.com
description: 生产环境
tags:
- name: webhook
description: Webhook 接口
- name: projects
description: 项目数据管理
paths:
/api/webhook/projects:
post:
tags:
- webhook
- projects
summary: 接收 n8n 推送的项目数据
description: |
接收 n8n 工作流程推送的 AI 项目数据,验证后批量更新到数据库。
**认证**: 请求头中必须包含有效的 `X-API-Key`
**处理流程**:
1. 验证 API Key
2. 验证请求数据格式
3. 逐个验证项目数据
4. 有效项目入库(创建或更新)
5. 返回处理结果
**部分成功模式**: 即使部分项目验证失败,也会继续处理其他项目。
operationId: upsertProjects
security:
- ApiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookPayload'
examples:
success_single:
summary: 单个项目示例
value:
apiKey: "sk_live_1234567890abcdefghijklmnopqrstuvwxyz"
projects:
- name: "Claude"
nameEn: "Claude"
slug: "claude"
description: "Anthropic 开发的 AI 助手"
descriptionEn: "AI assistant by Anthropic"
content: "Claude 的详细介绍..."
contentEn: "Detailed description of Claude..."
status: "ACTIVE"
source: "n8n-daily-job"
tags:
- name: "代码助手"
nameEn: "Code Assistant"
links:
- type: "WEBSITE"
url: "https://www.anthropic.com/claude"
title: "Official Website"
- type: "GITHUB"
url: "https://github.com/anthropics"
success_batch:
summary: 批量项目示例
value:
apiKey: "sk_live_1234567890abcdefghijklmnopqrstuvwxyz"
projects:
- name: "GPT-4"
slug: "gpt-4"
description: "OpenAI 的大型语言模型"
status: "ACTIVE"
source: "n8n-daily-job"
tags:
- name: "语言模型"
- name: "对话AI"
links:
- type: "WEBSITE"
url: "https://openai.com/gpt-4"
- name: "Midjourney"
slug: "midjourney"
description: "AI 图像生成工具"
status: "ACTIVE"
source: "n8n-daily-job"
tags:
- name: "图像生成"
links:
- type: "WEBSITE"
url: "https://www.midjourney.com"
partial_failure:
summary: 部分失败示例
value:
apiKey: "sk_live_1234567890abcdefghijklmnopqrstuvwxyz"
projects:
- name: "Valid Project"
slug: "valid-project"
description: "这是一个有效的项目"
tags:
- name: "测试"
links:
- type: "WEBSITE"
url: "https://example.com"
- name: "" # 无效:名称为空
slug: "invalid-project"
description: "x"
tags: []
links: []
responses:
'200':
description: 处理完成(可能包含部分失败)
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponse'
examples:
success_all:
summary: 全部成功
value:
success: true
processed: 3
created: 2
updated: 1
failed: 0
errors: []
partial_success:
summary: 部分成功
value:
success: true
processed: 3
created: 1
updated: 1
failed: 1
errors:
- index: 1
field: "description"
message: "Description too short (minimum 10 characters)"
value: { "description": "x" }
validation_error:
summary: 验证错误(请求级别)
value:
success: false
error: "Validation error"
details:
- "At least one project is required"
'401':
$ref: '#/components/responses/Unauthorized'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: |
API Key 认证。n8n 在请求头中携带预共享的密钥。
格式: `X-API-Key: sk_live_32_characters_minimum`
schemas:
# ================================
# Enums
# ================================
ProjectStatus:
type: string
enum: [ACTIVE, ARCHIVED]
description: 项目状态
x-enum-descriptions:
ACTIVE: 活跃项目
ARCHIVED: 已归档项目
LinkType:
type: string
enum: [WEBSITE, GITHUB, HUGGINGFACE, PAPER]
description: 链接类型
x-enum-descriptions:
WEBSITE: 官方网站
GITHUB: GitHub 仓库
HUGGINGFACE: HuggingFace 模型/数据集
PAPER: 学术论文
# ================================
# Domain Models
# ================================
ExternalLink:
type: object
required:
- type
- url
properties:
type:
$ref: '#/components/schemas/LinkType'
url:
type: string
format: uri
description: 链接地址
example: "https://github.com/example/project"
title:
type: string
maxLength: 200
description: 链接标题(可选)
example: "Source Code"
Tag:
type: object
required:
- name
properties:
name:
type: string
minLength: 1
maxLength: 50
description: 标签名称(中文)
example: "图像生成"
nameEn:
type: string
maxLength: 50
description: 标签名称(英文,可选)
example: "Image Generation"
ProjectInput:
type: object
required:
- name
- slug
- description
- tags
- links
properties:
name:
type: string
minLength: 1
maxLength: 200
description: 项目名称(中文)
example: "Claude"
nameEn:
type: string
maxLength: 200
description: 项目名称(英文,可选)
example: "Claude"
slug:
type: string
pattern: '^[a-z0-9-]+$'
description: URL 友好标识符(唯一)
example: "claude"
description:
type: string
minLength: 10
maxLength: 500
description: 项目简介(中文)
example: "Anthropic 开发的 AI 助手,擅长分析、写作和编程任务"
descriptionEn:
type: string
maxLength: 500
description: 项目简介(英文,可选)
example: "AI assistant by Anthropic, excels at analysis, writing, and coding"
content:
type: string
maxLength: 10000
description: 详细介绍(中文,可选)
example: "Claude 是由 Anthropic 开发的下一代 AI 助手..."
contentEn:
type: string
maxLength: 10000
description: 详细介绍(英文,可选)
example: "Claude is a next-generation AI assistant..."
status:
$ref: '#/components/schemas/ProjectStatus'
description: 项目状态(默认 ACTIVE
source:
type: string
maxLength: 100
description: 数据来源标识(如 n8n 流程名称)
example: "n8n-daily-job"
tags:
type: array
minItems: 1
maxItems: 10
items:
$ref: '#/components/schemas/Tag'
description: 项目标签(至少一个)
links:
type: array
minItems: 1
maxItems: 10
items:
$ref: '#/components/schemas/ExternalLink'
description: 外部链接(至少一个)
# ================================
# Request/Response Models
# ================================
WebhookPayload:
type: object
required:
- apiKey
- projects
properties:
apiKey:
type: string
minLength: 32
description: API 密钥(用于认证)
pattern: '^[a-zA-Z0-9_]{32,}$'
example: "sk_live_1234567890abcdefghijklmnopqrstuvwxyz"
projects:
type: array
minItems: 1
maxItems: 100
items:
$ref: '#/components/schemas/ProjectInput'
description: 要创建/更新的项目列表
WebhookResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功(部分失败仍返回 true)
processed:
type: integer
description: 处理的项目总数
created:
type: integer
description: 新创建的项目数
updated:
type: integer
description: 更新的项目数
failed:
type: integer
description: 失败的项目数
errors:
type: array
description: 失败项目的错误详情
items:
type: object
properties:
index:
type: integer
description: 失败项目在请求中的索引(从 0 开始)
field:
type: string
description: 验证失败的字段名
message:
type: string
description: 错误消息
value:
type: object
description: 导致错误的值(调试用)
ErrorResponse:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
description: 错误类型
details:
type: array
items:
type: string
description: 详细错误信息
# ================================
# Common Responses
# ================================
responses:
Unauthorized:
description: 未授权 - API Key 无效或缺失
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error: "Unauthorized"
details: ["Invalid or missing API Key"]
BadRequest:
description: 请求格式错误或验证失败
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
validation_error:
summary: 验证错误
value:
success: false
error: "Validation error"
details:
- "At least one project is required"
invalid_json:
summary: JSON 格式错误
value:
success: false
error: "Invalid JSON"
details:
- "Unexpected end of JSON input"
InternalServerError:
description: 服务器内部错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error: "Internal server error"
details:
- "Database connection failed"
# ================================
# Examples
# ================================
x-webhook-examples:
n8n-configuration:
description: n8n HTTP Request 节点配置示例
method: POST
url: "https://agent-park.example.com/api/webhook/projects"
headers:
X-API-Key: "sk_live_32_characters_minimum"
Content-Type: "application/json"
body:
apiKey: "{{$env.WEBHOOK_API_KEY}}"
projects:
- name: "{{$json.projectName}}"
slug: "{{$json.projectSlug}}"
description: "{{$json.description}}"
tags:
- name: "{{$json.tag}}"
links:
- type: "WEBSITE"
url: "{{$json.website}}"
@@ -1,524 +0,0 @@
# 数据模型设计: Agent Park - AI项目导航网站
**功能分支**: `001-ai-project-navigator`
**创建时间**: 2025-12-25
**状态**: 完成
## 概述
本文档定义了 Agent Park 项目的数据模型,包括实体定义、关系、验证规则和状态转换。数据模型使用 Prisma Schema 定义,存储在 PostgreSQL 数据库中。
---
## 实体关系图 (ERD)
```
┌─────────────┐ ┌─────────────┐ ┌──────────────────┐
│ Project │<─────│ Tag │ │ ExternalLink │
│ │ N:M │ │ 1:N │ │
└─────────────┘ └─────────────┘ └──────────────────┘
│ 1 │ N
│ │
└────────────────────────────────────────────┘
1
```
---
## 实体定义
### 1. Project (AI项目)
代表一个AI工具、应用或研究项目。
#### 字段
| 字段名 | 类型 | 约束 | 默认值 | 描述 |
|--------|------|------|--------|------|
| `id` | String | Primary Key | cuid() | 主键 |
| `name` | String | NOT NULL | - | 项目名称(中文) |
| `nameEn` | String? | Nullable | - | 项目名称(英文) |
| `slug` | String | UNIQUE, NOT NULL | - | URL 友好标识符 |
| `description` | String | NOT NULL, Min(10) | - | 项目简介(中文) |
| `descriptionEn` | String? | Nullable | - | 项目简介(英文) |
| `content` | Text? | Nullable | - | 详细介绍(中文) |
| `contentEn` | Text? | Nullable | - | 详细介绍(英文) |
| `status` | Enum | NOT NULL | ACTIVE | 项目状态 |
| `source` | String? | Nullable | - | 数据来源标识(如 n8n) |
| `createdAt` | DateTime | NOT NULL | now() | 收录时间 |
| `updatedAt` | DateTime | NOT NULL | now() | 更新时间 |
#### 状态枚举
```prisma
enum ProjectStatus {
ACTIVE # 活跃项目
ARCHIVED # 已归档项目
}
```
#### 索引
- `idx_project_status_createdAt`: (status, createdAt) - 用于按状态和时间筛选
- `idx_project_slug`: (slug) UNIQUE - 用于详情页路由
#### 关系
- `tags`: 与 Tag 的多对多关系
- `links`: 与 ExternalLink 的一对多关系
---
### 2. Tag (标签)
代表AI项目的特征标记,如"图像生成"、"代码助手"等。
#### 字段
| 字段名 | 类型 | 约束 | 默认值 | 描述 |
|--------|------|------|--------|------|
| `id` | String | Primary Key | cuid() | 主键 |
| `name` | String | UNIQUE, NOT NULL | - | 标签名称(中文) |
| `nameEn` | String? | Nullable | - | 标签名称(英文) |
| `slug` | String | UNIQUE, NOT NULL | - | URL 友好标识符 |
| `createdAt` | DateTime | NOT NULL | now() | 创建时间 |
#### 索引
- `idx_tag_slug`: (slug) UNIQUE - 用于标签筛选页面
#### 关系
- `projects`: 与 Project 的多对多关系
---
### 3. ExternalLink (外部链接)
代表项目的外部来源链接,如官网、GitHub、HuggingFace、论文链接。
#### 字段
| 字段名 | 类型 | 约束 | 默认值 | 描述 |
|--------|------|------|--------|------|
| `id` | String | Primary Key | cuid() | 主键 |
| `type` | Enum | NOT NULL | - | 链接类型 |
| `url` | String | NOT NULL | - | 链接地址 |
| `title` | String? | Nullable | - | 链接标题(可选) |
| `projectId` | String | Foreign Key | - | 关联的项目ID |
#### 链接类型枚举
```prisma
enum LinkType {
WEBSITE # 官方网站
GITHUB # GitHub 仓库
HUGGINGFACE # HuggingFace 模型/数据集
PAPER # 学术论文
}
```
#### 索引
- `idx_link_projectId`: (projectId) - 用于查询项目的外部链接
- `idx_link_type`: (type) - 用于按类型筛选链接
#### 关系
- `project`: 与 Project 的多对一关系(级联删除)
---
## 关系定义
### Project ↔ Tag (多对多)
使用中间表 `_ProjectTags` 维护多对多关系。
```prisma
model Project {
// ... 其他字段
tags Tag[]
}
model Tag {
// ... 其他字段
projects Project[]
}
```
**级联规则**: 删除项目时不删除标签(标签可能被其他项目使用)
### Project ↔ ExternalLink (一对多)
一个项目可以有多个外部链接。
```prisma
model Project {
// ... 其他字段
links ExternalLink[]
}
model ExternalLink {
id String @id @default(cuid())
type LinkType
url String
title String?
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
}
```
**级联规则**: 删除项目时级联删除所有关联的外部链接
---
## Prisma Schema 完整定义
```prisma
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
previewFeatures = ["postgresqlExtensions"]
}
// ================================
// Enums
// ================================
enum ProjectStatus {
ACTIVE
ARCHIVED
}
enum LinkType {
WEBSITE
GITHUB
HUGGINGFACE
PAPER
}
// ================================
// Models
// ================================
model Project {
id String @id @default(cuid())
name String
nameEn String?
slug String @unique
description String
descriptionEn String?
content String? @db.Text
contentEn String? @db.Text
status ProjectStatus @default(ACTIVE)
source String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
tags Tag[]
links ExternalLink[]
// Indexes
@@index([status, createdAt], map: "idx_project_status_createdAt")
@@index([slug], map: "idx_project_slug")
@@map("projects")
}
model Tag {
id String @id @default(cuid())
name String @unique
nameEn String?
slug String @unique
createdAt DateTime @default(now())
// Relations
projects Project[]
// Indexes
@@index([slug], map: "idx_tag_slug")
@@map("tags")
}
model ExternalLink {
id String @id @default(cuid())
type LinkType
url String
title String?
projectId String
// Relations
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
// Indexes
@@index([projectId], map: "idx_link_projectId")
@@index([type], map: "idx_link_type")
@@map("external_links")
}
```
---
## Zod 验证 Schema
配合 Prisma 类型,使用 Zod 进行运行时数据验证。
```typescript
// src/lib/validations.ts
import { z } from 'zod'
// ================================
// Enums
// ================================
export const ProjectStatusEnum = z.enum(['ACTIVE', 'ARCHIVED'])
export const LinkTypeEnum = z.enum(['WEBSITE', 'GITHUB', 'HUGGINGFACE', 'PAPER'])
// ================================
// Base Schemas
// ================================
export const ExternalLinkSchema = z.object({
type: LinkTypeEnum,
url: z.string().url('Invalid URL format'),
title: z.string().max(200).optional()
})
export const TagSchema = z.object({
name: z.string().min(1).max(50),
nameEn: z.string().max(50).optional()
})
// ================================
// Project Schemas
// ================================
export const ProjectBaseSchema = z.object({
name: z.string().min(1).max(200),
nameEn: z.string().max(200).optional(),
description: z.string().min(10).max(500),
descriptionEn: z.string().max(500).optional(),
content: z.string().max(10000).optional(),
contentEn: z.string().max(10000).optional(),
status: ProjectStatusEnum.default('ACTIVE'),
source: z.string().max(100).optional()
})
export const ProjectInputSchema = ProjectBaseSchema.extend({
tags: z.array(TagSchema).min(1, 'At least one tag is required').max(10),
links: z.array(ExternalLinkSchema).min(1, 'At least one link is required').max(10)
})
// ================================
// Webhook Schemas
// ================================
export const WebhookAuthSchema = z.object({
apiKey: z.string().min(32, 'Invalid API key format')
})
export const WebhookPayloadSchema = WebhookAuthSchema.extend({
projects: z.array(ProjectInputSchema).min(1).max(100)
})
// ================================
// Query Schemas
// ================================
export const ProjectQuerySchema = z.object({
search: z.string().max(100).optional(),
tags: z.array(z.string()).optional(),
status: ProjectStatusEnum.optional(),
page: z.coerce.number().int().positive().default(1),
limit: z.coerce.number().int().positive().max(100).default(20)
})
// ================================
// Types
// ================================
export type ExternalLink = z.infer<typeof ExternalLinkSchema>
export type Tag = z.infer<typeof TagSchema>
export type ProjectInput = z.infer<typeof ProjectInputSchema>
export type WebhookPayload = z.infer<typeof WebhookPayloadSchema>
export type ProjectQuery = z.infer<typeof ProjectQuerySchema>
```
---
## 数据迁移策略
### 初始化迁移
```bash
# 创建初始迁移
npx prisma migrate dev --name init
# 生成 Prisma Client
npx prisma generate
# 推送 schema 到数据库(开发环境)
npx prisma db push
```
### 生产环境部署
```bash
# 应用待处理的迁移
npx prisma migrate deploy
# 重置数据库(仅开发环境,慎用!)
npx prisma migrate reset
```
### 迁移命名约定
- `init`: 初始化 schema
- `add_project_content`: 添加项目内容字段
- `add_external_link_title`: 添加链接标题字段
- `add_status_index`: 添加状态索引
---
## 种子数据
### 假数据示例 (20-30个项目)
```typescript
// prisma/seed.ts
import { PrismaClient } from '@prisma/client'
import { ProjectStatus, LinkType } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
// 创建标签
const tagImageGen = await prisma.tag.upsert({
where: { slug: 'image-generation' },
update: {},
create: {
name: '图像生成',
nameEn: 'Image Generation',
slug: 'image-generation'
}
})
const tagCodeAssistant = await prisma.tag.upsert({
where: { slug: 'code-assistant' },
update: {},
create: {
name: '代码助手',
nameEn: 'Code Assistant',
slug: 'code-assistant'
}
})
const tagDataAnalysis = await prisma.tag.upsert({
where: { slug: 'data-analysis' },
update: {},
create: {
name: '数据分析',
nameEn: 'Data Analysis',
slug: 'data-analysis'
}
})
// 创建项目
await prisma.project.upsert({
where: { slug: 'claude' },
update: {},
create: {
name: 'Claude',
nameEn: 'Claude',
slug: 'claude',
description: 'Anthropic 开发的 AI 助手,擅长分析、写作和编程任务。',
descriptionEn: 'AI assistant by Anthropic, excels at analysis, writing, and coding tasks.',
content: 'Claude 是由 Anthropic 开发的下一代 AI 助手。它基于 Constitutional AI 方法训练,强调安全性、诚实性和有用性。Claude 擅长长文本分析、创意写作、编程辅助等多种任务。',
contentEn: 'Claude is a next-generation AI assistant developed by Anthropic. Trained using Constitutional AI methods, it emphasizes safety, honesty, and helpfulness. Claude excels at long-text analysis, creative writing, coding assistance, and more.',
status: ProjectStatus.ACTIVE,
source: 'manual',
tags: {
connect: [{ id: tagCodeAssistant.id }]
},
links: {
create: [
{
type: LinkType.WEBSITE,
url: 'https://www.anthropic.com/claude',
title: 'Official Website'
},
{
type: LinkType.GITHUB,
url: 'https://github.com/anthropics/anthropic-sdk-python',
title: 'Python SDK'
}
]
}
}
})
// 更多项目...
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})
```
---
## 数据完整性约束
### 字段级约束
| 表 | 字段 | 约束 |
|---|------|------|
| projects | name | NOT NULL, Max(200) |
| projects | description | NOT NULL, Min(10), Max(500) |
| projects | slug | UNIQUE, URL-friendly |
| tags | name | UNIQUE, NOT NULL, Max(50) |
| external_links | url | NOT NULL, Valid URL |
### 业务规则
1. **项目必填字段**: name、description、至少一个 tag、至少一个 link
2. **标签唯一性**: 同名标签不能重复创建
3. **链接类型限制**: 每个项目每种类型的链接最多 5 个
4. **项目状态**: 新项目默认为 ACTIVE,仅可手动设置为 ARCHIVED
5. **删除保护**: 删除项目时级联删除链接,但保留标签
---
## 性能优化建议
1. **索引优化**: 为常用查询字段(status、slug、projectId)创建索引
2. **查询优化**: 使用 Prisma 的 `select``include` 精确获取数据
3. **分页查询**: 使用 `cursor``offset` 分页避免一次性加载大量数据
4. **全文搜索**: 如需高级搜索,可使用 PostgreSQL 的全文搜索功能
---
## 总结
数据模型设计遵循以下原则:
- **类型安全**: Prisma + Zod 提供端到端类型安全
- **国际化支持**: 核心字段提供中英双语版本
- **灵活性**: 标签系统而非固定分类,适应 AI 领域快速变化
- **可扩展性**: 清晰的实体关系,便于后续功能扩展
- **性能优先**: 合理的索引设计,优化查询性能
-157
View File
@@ -1,157 +0,0 @@
# 实施计划: Agent Park - AI项目导航网站
**分支**: `001-ai-project-navigator` | **日期**: 2025-12-25 | **规范**: [spec.md](./spec.md)
**输入**: 来自 `/specs/001-ai-project-navigator/spec.md` 的功能规范
## 摘要
构建一个名为 Agent Park 的全网AI项目导航网站,采用 Anthropic Claude 风格设计。网站支持中英双语,使用 Next.js 14+ App Router、shadcn/ui 组件库、Prisma ORM + PostgreSQL 数据库、Tailwind CSS。用户可以浏览、搜索AI项目,查看项目详情和外部链接。前期使用20-30个假数据构建,后期通过 webhook 接口接收 n8n 工作流程推送的更新数据。
## 技术背景
**语言/版本**: TypeScript 5.0+ (严格模式)
**主要依赖**:
- Next.js 14+ (App Router)
- shadcn/ui (组件库)
- Prisma (ORM)
- PostgreSQL (数据库)
- Tailwind CSS (样式)
- next-intl (国际化)
- Zod (数据验证)
**存储**: PostgreSQL (通过 Prisma ORM)
**测试**: Vitest + React Testing Library + Playwright
**目标平台**: Web (响应式设计 - 桌面/平板/手机)
**项目类型**: 全栈 Web 应用
**性能目标**:
- 首次内容绘制(FCP) < 1.8s
- 最大内容绘制(LCP) < 2.5s
- 首次字节时间(TTFB) < 800ms
- 初始 JavaScript < 200KB gzipped
**约束条件**:
- TypeScript 严格模式强制启用
- 单元测试覆盖率 80%+
- 所有代码必须通过 ESLint 检查
- 禁止使用 @ts-ignore
**规模/范围**:
- 前期: 20-30个项目数据
- 页面: 首页、项目列表、项目详情页
- API: 1个 webhook 接口 (POST /api/webhook/projects)
## 章程检查
*门控: 必须在阶段 0 研究前通过. 阶段 1 设计后重新检查. *
### 阶段 0 前检查
| 原则 | 状态 | 说明 |
|------|------|------|
| I. TypeScript 严格模式与类型安全 | ✅ 通过 | 项目使用 TypeScript 5.0+ 严格模式,所有 API 使用 Zod 验证 |
| II. 组件优先架构 | ✅ 通过 | 使用 Next.js App Router,优先 Server Components |
| III. 测试驱动开发(不可协商) | ✅ 通过 | 规划使用 Vitest + React Testing Library + Playwright,目标 80%+ 覆盖率 |
| IV. 性能优先 | ✅ 通过 | 定义了明确的 Core Web Vitals 目标,使用 ISR 和 SSG 优化 |
| V. 用户体验一致性 | ✅ 通过 | 使用 shadcn/ui 统一设计系统,支持响应式和深色模式 |
| VI. 代码质量与可维护性 | ✅ 通过 | 使用 ESLint + Prettier,遵循 TypeScript 最佳实践 |
### 阶段 1 后重新检查
| 原则 | 状态 | 说明 |
|------|------|------|
| 所有阶段 0 门控项 | ✅ 保持 | 设计确认符合所有章程原则 |
| 数据模型类型安全 | ✅ 通过 | Prisma 生成类型,配合 Zod 运行时验证 |
| API 合同完整性 | ✅ 通过 | webhook 接口使用 Zod schema 验证输入 |
**结论**: 项目设计完全符合章程要求,无违规项。
## 项目结构
### 文档(此功能)
```
specs/001-ai-project-navigator/
├── plan.md # 此文件 (/speckit.plan 命令输出)
├── research.md # 阶段 0 输出 - 技术选型研究
├── data-model.md # 阶段 1 输出 - 数据模型设计
├── quickstart.md # 阶段 1 输出 - 快速开始指南
├── contracts/ # 阶段 1 输出 - API 合同
│ └── webhook.yaml # OpenAPI 3.0 规范
└── tasks.md # 阶段 2 输出 (/speckit.tasks 命令)
```
### 源代码(仓库根目录)
```
agent-park-v2/
├── prisma/
│ ├── schema.prisma # Prisma 数据模型定义
│ └── seed.ts # 假数据种子脚本
├── public/
│ └── images/ # 静态图片资源
├── src/
│ ├── app/ # Next.js App Router 目录
│ │ ├── [locale]/ # next-intl 国际化路由
│ │ │ ├── layout.tsx # 根布局
│ │ │ ├── page.tsx # 首页
│ │ │ ├── projects/ # 项目列表页
│ │ │ │ ├── page.tsx
│ │ │ │ └── [id]/ # 项目详情页
│ │ │ │ └── page.tsx
│ │ │ └── api/ # API 路由
│ │ │ └── webhook/
│ │ │ └── projects/
│ │ │ └── route.ts # Webhook 端点
│ │ ├── globals.css # Tailwind 全局样式
│ │ └── layout.tsx # 根布局 (国际化)
│ ├── components/ # React 组件
│ │ ├── ui/ # shadcn/ui 组件 (自动生成)
│ │ ├── layout/ # 布局组件
│ │ │ ├── Header.tsx
│ │ │ ├── Footer.tsx
│ │ │ └── Navigation.tsx
│ │ ├── project/ # 项目相关组件
│ │ │ ├── ProjectCard.tsx
│ │ │ ├── ProjectList.tsx
│ │ │ ├── ProjectDetail.tsx
│ │ │ └── TagCloud.tsx
│ │ └── search/ # 搜索组件
│ │ ├── SearchBar.tsx
│ │ └── SearchResults.tsx
│ ├── lib/ # 工具库
│ │ ├── prisma.ts # Prisma 客户端单例
│ │ ├── utils.ts # 通用工具函数
│ │ └── validations.ts # Zod 验证 schemas
│ ├── hooks/ # 自定义 Hooks
│ │ ├── useSearch.ts
│ │ └── useProjects.ts
│ ├── types/ # TypeScript 类型定义
│ │ └── index.ts
│ ├── messages/ # next-intl 翻译文件
│ │ ├── en.json
│ │ └── zh.json
│ └── styles/ # 额外样式文件
├── tests/ # 测试文件
│ ├── unit/ # 单元测试
│ ├── integration/ # 集成测试
│ └── e2e/ # E2E 测试 (Playwright)
├── next.config.js # Next.js 配置
├── tailwind.config.js # Tailwind CSS 配置
├── tsconfig.json # TypeScript 配置
├── components.json # shadcn/ui 配置
├── .env.example # 环境变量示例
├── package.json
└── README.md
```
**结构决策**: 采用标准的 Next.js App Router 单体项目结构。所有源代码在 `src/` 目录下,使用 `app/` 目录进行路由。Prisma schema 在根目录 `prisma/` 文件夹。组件按功能分目录组织,shadcn/ui 组件放在 `components/ui/` 自动管理。
## 复杂度跟踪
*仅在章程检查有必须证明的违规时填写*
无违规项。项目设计完全符合章程要求。
| 违规 | 为什么需要 | 拒绝更简单替代方案的原因 |
|-----------|------------|-------------------------------------|
| - | - | - |
@@ -1,519 +0,0 @@
# 快速开始指南: Agent Park - AI项目导航网站
**功能分支**: `001-ai-project-navigator`
**创建时间**: 2025-12-25
**状态**: 完成
## 概述
本指南提供 Agent Park 项目的完整开发环境设置和快速启动步骤。
---
## 前置要求
### 必需软件
| 软件 | 版本要求 | 用途 |
|------|----------|------|
| Node.js | 18.17+ | 运行时环境 |
| pnpm | 8.0+ | 包管理器 |
| PostgreSQL | 14+ | 数据库 |
| Git | 最新版 | 版本控制 |
### 可选软件
| 软件 | 用途 |
|------|------|
| Docker | 容器化数据库(开发环境) |
| VS Code | 推荐的代码编辑器 |
### 检查安装
```bash
# 检查 Node.js 版本
node --version # 应该 >= v18.17.0
# 检查 pnpm 版本
pnpm --version # 应该 >= 8.0.0
# 检查 PostgreSQL
psql --version # 应该 >= 14.0
```
---
## 项目初始化
### 1. 创建 Next.js 项目
```bash
# 使用 pnpm 创建项目(推荐使用 shadcn/ui init 自动配置)
pnpm create next-app@latest agent-park-v2 --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
# 或使用 shadcn/ui 的 init 命令(更便捷)
npx shadcn@latest init
```
**交互式选项**:
- TypeScript: Yes
- ESLint: Yes
- Tailwind CSS: Yes
- `src/` directory: Yes
- App Router: Yes
- Import alias: `@/*`
### 2. 安装依赖
```bash
cd agent-park-v2
# 核心依赖
pnpm add next-intl zod clsx tailwind-merge
# Prisma
pnpm add @prisma/client
pnpm add -D prisma
# shadcn/ui(如未使用 init 命令)
npx shadcn@latest init
# 开发依赖
pnpm add -D @types/node @types/react @types/react-dom vitest @testing-library/react @testing-library/jest-dom @playwright/test
```
### 3. 安装 shadcn/ui 组件
```bash
# 安装常用组件
npx shadcn@latest add button card input textarea badge
npx shadcn@latest add skeleton separator dialog
npx shadcn@latest add navigation-menu dropdown-menu
```
---
## 数据库设置
### 方案 A: 使用 Docker(推荐)
```bash
# 启动 PostgreSQL 容器
docker run --name agent-park-db \
-e POSTGRES_USER=agentpark \
-e POSTGRES_PASSWORD=agentpark \
-e POSTGRES_DB=agent_park \
-p 5432:5432 \
-d postgres:16-alpine
# 等待数据库启动
docker logs -f agent-park-db
```
### 方案 B: 本地 PostgreSQL
```bash
# 创建数据库
createdb agent_park
# 或使用 psql
psql -U postgres -c "CREATE DATABASE agent_park;"
```
### 配置环境变量
```bash
# 创建 .env 文件
cp .env.example .env
# 编辑 .env
DATABASE_URL="postgresql://agentpark:agentpark@localhost:5432/agent_park"
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
WEBHOOK_API_KEY="sk_live_$(openssl rand -base64 32 | tr -d '=+/' | cut -c1-32)"
```
---
## Prisma 配置
### 1. 初始化 Prisma
```bash
npx prisma init
```
### 2. 编写 Schema
将以下内容写入 `prisma/schema.prisma`:
```prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
previewFeatures = ["postgresqlExtensions"]
}
enum ProjectStatus {
ACTIVE
ARCHIVED
}
enum LinkType {
WEBSITE
GITHUB
HUGGINGFACE
PAPER
}
model Project {
id String @id @default(cuid())
name String
nameEn String?
slug String @unique
description String
descriptionEn String?
content String? @db.Text
contentEn String? @db.Text
status ProjectStatus @default(ACTIVE)
source String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tags Tag[]
links ExternalLink[]
@@index([status, createdAt])
@@index([slug])
@@map("projects")
}
model Tag {
id String @id @default(cuid())
name String @unique
nameEn String?
slug String @unique
createdAt DateTime @default(now())
projects Project[]
@@index([slug])
@@map("tags")
}
model ExternalLink {
id String @id @default(cuid())
type LinkType
url String
title String?
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
@@index([projectId])
@@index([type])
@@map("external_links")
}
```
### 3. 执行迁移
```bash
# 创建初始迁移
npx prisma migrate dev --name init
# 生成 Prisma Client
npx prisma generate
```
### 4. 加载种子数据
创建 `prisma/seed.ts`:
```typescript
import { PrismaClient, ProjectStatus, LinkType } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
// 创建标签
const tags = await Promise.all([
prisma.tag.upsert({
where: { slug: 'code-assistant' },
update: {},
create: { name: '代码助手', nameEn: 'Code Assistant', slug: 'code-assistant' }
}),
prisma.tag.upsert({
where: { slug: 'image-generation' },
update: {},
create: { name: '图像生成', nameEn: 'Image Generation', slug: 'image-generation' }
})
])
// 创建项目
await prisma.project.upsert({
where: { slug: 'claude' },
update: {},
create: {
name: 'Claude',
nameEn: 'Claude',
slug: 'claude',
description: 'Anthropic 开发的 AI 助手,擅长分析、写作和编程任务。',
descriptionEn: 'AI assistant by Anthropic, excels at analysis, writing, and coding.',
status: ProjectStatus.ACTIVE,
tags: { connect: tags.map(t => ({ id: t.id })) },
links: {
create: [
{ type: LinkType.WEBSITE, url: 'https://www.anthropic.com/claude', title: 'Official Website' },
{ type: LinkType.GITHUB, url: 'https://github.com/anthropics', title: 'GitHub' }
]
}
}
})
console.log('Seed data loaded successfully!')
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})
```
```bash
# 运行种子脚本
npx ts-node prisma/seed.ts
```
---
## 国际化配置
### 1. 配置 next-intl
安装依赖:
```bash
pnpm add next-intl
```
### 2. 创建消息文件
```bash
mkdir -p src/messages
```
`src/messages/en.json`:
```json
{
"common": {
"search": "Search",
"loading": "Loading...",
"noResults": "No results found"
},
"home": {
"title": "AI Project Navigator",
"subtitle": "Discover and explore AI projects"
},
"project": {
"details": "Project Details",
"externalLinks": "External Links",
"viewProject": "View Project"
}
}
```
`src/messages/zh.json`:
```json
{
"common": {
"search": "搜索",
"loading": "加载中...",
"noResults": "未找到结果"
},
"home": {
"title": "AI 项目导航",
"subtitle": "发现和探索 AI 项目"
},
"project": {
"details": "项目详情",
"externalLinks": "外部链接",
"viewProject": "查看项目"
}
}
```
### 3. 配置 Next.js
更新 `next.config.js`:
```javascript
const createNextIntlPlugin = require('next-intl/plugin')
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts')
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{ hostname: 'localhost' },
{ hostname: '*.anthropic.com' }
]
}
}
module.exports = withNextIntl(nextConfig)
```
创建 `src/i18n/request.ts`:
```typescript
import { getRequestConfig } from 'next-intl/server'
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`../messages/${locale}.json`)).default
}))
```
---
## 目录结构创建
```bash
# 创建目录结构
mkdir -p src/components/layout
mkdir -p src/components/project
mkdir -p src/components/search
mkdir -p src/lib
mkdir -p src/hooks
mkdir -p src/types
mkdir -p tests/unit
mkdir -p tests/integration
mkdir -p tests/e2e
```
---
## 启动开发服务器
```bash
# 启动开发服务器
pnpm dev
```
访问:
- 英文: http://localhost:3000/en
- 中文: http://localhost:3000/zh
---
## 验证设置
### 1. 检查 Prisma 连接
```bash
npx prisma studio
```
访问 http://localhost:5555 查看数据库数据。
### 2. 测试 API 端点
```bash
# 测试 webhook(需要先创建 .env 中的 WEBHOOK_API_KEY
curl -X POST http://localhost:3000/api/webhook/projects \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"apiKey": "your-api-key",
"projects": [{
"name": "Test Project",
"slug": "test-project",
"description": "This is a test project for verification",
"tags": [{"name": "测试"}],
"links": [{"type": "WEBSITE", "url": "https://example.com"}]
}]
}'
```
### 3. 运行测试
```bash
# 单元测试
pnpm test
# E2E 测试
pnpm test:e2e
```
---
## 常见问题
### Q: Prisma 迁移失败?
```bash
# 重置数据库(开发环境)
npx prisma migrate reset
# 或手动删除迁移历史
rm -rf prisma/migrations
npx prisma migrate dev --name init
```
### Q: TypeScript 错误?
```bash
# 重新生成 Prisma Client
npx prisma generate
# 重启 TypeScript 服务器(VS Code
# Cmd+Shift+P -> "TypeScript: Restart TS Server"
```
### Q: 端口被占用?
```bash
# 查找占用 3000 端口的进程
lsof -i :3000 # macOS/Linux
netstat -ano | findstr :3000 # Windows
# 或使用不同端口
PORT=3001 pnpm dev
```
### Q: shadcn/ui 组件不工作?
```bash
# 重新安装组件
npx shadcn@latest add [component-name]
# 或手动检查 components.json 配置
```
---
## 下一步
设置完成后,继续以下步骤:
1. **创建基础布局**: Header、Footer、Navigation
2. **实现项目列表页**: 支持搜索和标签筛选
3. **实现项目详情页**: 显示完整信息和外部链接
4. **实现 Webhook API**: 接收 n8n 数据推送
5. **编写测试**: 单元测试和 E2E 测试
6. **样式定制**: 调整 Tailwind 主题实现 Claude 风格
参考文档:
- [data-model.md](./data-model.md) - 数据模型设计
- [contracts/webhook.yaml](./contracts/webhook.yaml) - API 接口规范
- [research.md](./research.md) - 技术选型研究
-509
View File
@@ -1,509 +0,0 @@
# 技术研究报告: Agent Park - AI项目导航网站
**功能分支**: `001-ai-project-navigator`
**创建时间**: 2025-12-25
**状态**: 完成
## 概述
本文档记录了 Agent Park 项目的技术选型决策和最佳实践研究。针对 Next.js 全栈应用、shadcn/ui 组件库、PostgreSQL + Prisma ORM、Tailwind CSS 和国际化支持等技术进行了深入研究。
---
## 1. Next.js 14+ App Router
### Decision: 选择 Next.js 14+ (App Router) 作为全栈框架
**Rationale**:
- **Server Components 优先**: App Router 默认使用 Server Components,减少客户端 JavaScript 体积,提升性能
- **内置 API Routes**: 通过 Route Handlers 可以轻松构建 webhook 接口,无需单独的后端服务器
- **文件系统路由**: 直观的路由结构,便于维护和扩展
- **优秀的 SEO 支持**: 服务端渲染确保搜索引擎可以正确索引内容
- **Vercel 部署优化**: 原生支持 Vercel 平台,零配置部署
**Alternatives considered**:
- **Remix**: 同样优秀的全栈框架,但社区和生态系统相对较小
- **Nuxt.js (Vue)**: 团队更熟悉 React 生态系统
- **SvelteKit**: 学习曲线较高,生态系统相对不成熟
### 最佳实践
1. **优先使用 Server Components**: 仅在需要交互性(状态、事件处理、浏览器 API)时添加 `'use client'` 指令
2. **使用动态导入**: 对于客户端组件,使用 `dynamic()` 进行代码分割
3. **利用并行路由**: 对于复杂布局,使用插槽和并行路由提升用户体验
4. **使用 Server Actions**: 表单提交和状态变更优先使用 Server Actions 而非 API Routes
5. **启用增量静态再生 (ISR)**: 对于项目列表页面,使用 ISR 减少 API 调用
### TypeScript 配置
```json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"paths": {
"@/*": ["./src/*"]
}
}
}
```
---
## 2. shadcn/ui 组件库
### Decision: 选择 shadcn/ui 作为 UI 组件库
**Rationale**:
- **代码而非 npm 包**: 组件代码直接复制到项目中,完全可控和可定制
- **基于 Radix UI**: 无障碍访问性良好,符合 WAI-ARIA 标准
- **Tailwind CSS 集成**: 完美配合 Tailwind CSS 进行样式定制
- **类型安全**: 完整的 TypeScript 类型定义
- **Anthropic Claude 风格**: 可以通过定制主题实现类似 Claude 的简洁优雅风格
**Alternatives considered**:
- **Mantine**: 功能丰富但体积较大,定制性较差
- **Chakra UI**: API 设计优秀,但性能不如 Radix UI
- **Material-UI**: 风格过于固定,难以实现 Claude 风格
### 最佳实践
1. **使用 CLI 安装**: `npx shadcn@latest add [component]` 自动添加依赖和配置
2. **自定义主题**: 通过 CSS 变量定制颜色、圆角、阴影等
3. **复用组件模式**: 参考现有组件创建新的组合组件
4. **保持组件更新**: 定期运行 `npx shadcn@latest diff` 检查组件更新
### Anthropic Claude 风格定制
```css
/* globals.css - Claude 风格配色 */
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 5.9% 10%;
--radius: 0.5rem;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
}
```
---
## 3. Prisma ORM + PostgreSQL
### Decision: 选择 Prisma 作为 ORMPostgreSQL 作为数据库
**Rationale**:
- **类型安全**: Prisma 自动生成 TypeScript 类型,与 strict mode 完美配合
- **声明式 Schema**: 直观的数据模型定义,支持关系和约束
- **迁移管理**: 内置版本控制的迁移系统,便于团队协作
- **开发体验**: Prisma Studio 提供可视化的数据库浏览和编辑
- **PostgreSQL 优势**: 成熟稳定、支持全文搜索、JSON 数据类型、性能优秀
**Alternatives considered**:
- **Drizzle ORM**: 性能更优但生态系统较新,迁移功能较弱
- **TypeORM**: 体积较大,类型安全性不如 Prisma
- **MongoDB + Mongoose**: 对于关系型数据不够自然
### 最佳实践
1. **使用 Prisma Accelerate**: 生产环境使用连接池提升性能
2. **启用查询日志**: 开发环境启用 `log: ['query', 'info', 'warn', 'error']`
3. **使用事务**: 对于多表操作使用 `$transaction` 确保数据一致性
4. **批量操作**: 使用 `createMany``updateMany` 而非循环单条操作
5. **选择特定字段**: 使用 `select` 减少数据传输量
### Schema 设计模式
```prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Project {
id String @id @default(cuid())
name String
nameEn String?
slug String @unique
description String
descriptionEn String?
content String? @db.Text
contentEn String? @db.Text
status ProjectStatus @default(ACTIVE)
source String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tags Tag[]
links ExternalLink[]
@@index([status, createdAt])
@@index([slug])
}
enum ProjectStatus {
ACTIVE
ARCHIVED
}
model Tag {
id String @id @default(cuid())
name String @unique
nameEn String?
slug String @unique
createdAt DateTime @default(now())
projects Project[]
@@index([slug])
}
model ExternalLink {
id String @id @default(cuid())
type LinkType
url String
title String?
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
@@index([projectId])
@@index([type])
}
enum LinkType {
WEBSITE
GITHUB
HUGGINGFACE
PAPER
}
```
---
## 4. Tailwind CSS
### Decision: 选择 Tailwind CSS 作为样式解决方案
**Rationale**:
- **实用优先**: 快速构建 UI,无需切换上下文编写 CSS
- **高度可定制**: 通过配置文件完全控制设计系统
- **生产优化**: 自动清除未使用的样式,保持 CSS 体积最小
- **响应式优先**: 移动优先的断点系统,适配各种设备
- **与 shadcn/ui 完美集成**: 组件库基于 Tailwind CSS 构建
**Alternatives considered**:
- **CSS Modules**: 组件隔离性好但维护成本高,不支持设计复用
- **Styled Components**: 运行时注入样式,性能不如 Tailwind
- **Emotion**: 类似 Styled Components,同样有性能问题
### 最佳实践
1. **使用组件变体**: 使用 `clsx``tailwind-merge` 处理条件类名
2. **提取公共模式**: 将重复的类组合提取为组件或工具函数
3. **使用 @apply 谨慎**: 仅在确实减少代码重复时使用
4. **启用 JIT 模式**: 确保使用最新版本的 JIT 编译器
5. **自定义断点**: 根据实际需求调整断点
### 工具函数
```typescript
// src/lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
```
---
## 5. 国际化 (i18n)
### Decision: 选择 next-intl 作为国际化解决方案
**Rationale**:
- **App Router 原生支持**: 完美集成 Next.js 14 App Router
- **类型安全**: TypeScript 自动生成翻译键的类型提示
- **复数支持**: 内置 ICU 消息语法处理复数和格式化
- **SEO 友好**: 自动生成不同语言的元数据
- **轻量级**: 与 next-i18next 相比体积更小
**Alternatives considered**:
- **next-i18next**: 专为 Pages Router 设计,App Router 支持不完善
- **react-i18next**: 需要额外配置路由和元数据
- **自定义方案**: 开发成本高,容易遗漏边界情况
### 最佳实践
1. **使用翻译键命名空间**: 按功能模块组织翻译文件
2. **提供上下文参数**: 翻译函数支持动态参数插值
3. **日期和数字格式化**: 使用 ICU 格式而非手动拼接
4. **默认语言回退**: 缺失翻译时回退到默认语言
5. **URL 前缀路由**: 使用 `/[locale]/...` 模式而非子域名
### 配置示例
```typescript
// src/i18n/request.ts
import { getRequestConfig } from 'next-intl/server'
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`../../messages/${locale}.json`)).default
}))
```
```json
// messages/en.json
{
"common": {
"search": "Search",
"loading": "Loading..."
},
"home": {
"title": "AI Project Navigator",
"subtitle": "Discover and explore AI projects"
},
"project": {
"details": "Project Details",
"links": "External Links",
"viewProject": "View Project"
}
}
```
```json
// messages/zh.json
{
"common": {
"search": "搜索",
"loading": "加载中..."
},
"home": {
"title": "AI 项目导航",
"subtitle": "发现和探索 AI 项目"
},
"project": {
"details": "项目详情",
"links": "外部链接",
"viewProject": "查看项目"
}
}
```
---
## 6. 数据验证 (Zod)
### Decision: 选择 Zod 作为运行时数据验证库
**Rationale**:
- **TypeScript 优先**: 自动从 schema 推断类型,与 Prisma 完美配合
- **链式 API**: 直观的验证规则定义
- **错误处理**: 详细的错误信息,便于调试和用户提示
- **零依赖**: 轻量级,不增加太多打包体积
**Alternatives considered**:
- **Yup**: API 较老,TypeScript 支持不如 Zod
- **Joi**: 体积较大,主要服务端使用
- **io-ts**: 函数式编程风格,学习曲线陡峭
### 最佳实践
1. **复用 Prisma 类型**: 使用 `z.prisma` 扩展从 Prisma schema 生成 Zod schema
2. **自定义错误消息**: 提供中英文双语错误提示
3. **严格验证**: webhook 接口使用 `strict()` 模式拒绝额外字段
4. **输入输出分离**: 区分输入验证 schema 和响应输出 schema
### Webhook 验证示例
```typescript
// src/lib/validations.ts
import { z } from 'zod'
export const LinkTypeEnum = z.enum(['WEBSITE', 'GITHUB', 'HUGGINGFACE', 'PAPER'])
export const ExternalLinkSchema = z.object({
type: LinkTypeEnum,
url: z.string().url('Invalid URL format'),
title: z.string().optional()
})
export const TagSchema = z.object({
name: z.string().min(1, 'Tag name is required'),
nameEn: z.string().optional()
})
export const ProjectInputSchema = z.object({
name: z.string().min(1, 'Project name is required'),
nameEn: z.string().optional(),
description: z.string().min(10, 'Description too short'),
descriptionEn: z.string().optional(),
content: z.string().optional(),
contentEn: z.string().optional(),
status: z.enum(['ACTIVE', 'ARCHIVED']).default('ACTIVE'),
source: z.string().optional(),
tags: z.array(TagSchema).min(1, 'At least one tag is required'),
links: z.array(ExternalLinkSchema).min(1, 'At least one link is required')
})
export const WebhookPayloadSchema = z.object({
apiKey: z.string().min(32, 'Invalid API key format'),
projects: z.array(ProjectInputSchema).min(1, 'At least one project is required')
})
export type ProjectInput = z.infer<typeof ProjectInputSchema>
export type WebhookPayload = z.infer<typeof WebhookPayloadSchema>
```
---
## 7. 测试策略
### Decision: 使用 Vitest + React Testing Library + Playwright
**Rationale**:
- **Vitest**: 与 Vite 生态深度集成,比 Jest 更快,原生支持 ESM
- **React Testing Library**: 专注用户行为测试,而非实现细节
- **Playwright**: 跨浏览器 E2E 测试,支持并行执行
**Alternatives considered**:
- **Jest**: 生态成熟但配置复杂,ESM 支持不佳
- **Cypress**: E2E 测试强大但较重,测试编写较慢
### 最佳实践
1. **测试金字塔**: 70% 单元测试 + 20% 集成测试 + 10% E2E 测试
2. **AAA 模式**: Arrange-Act-Assert 结构组织测试
3. **描述性测试名**: 使用 `should... when...` 格式
4. **Mock 外部依赖**: 使用 Vitest mock 功能隔离测试
5. **测试覆盖率**: 配置 `--coverage` 确保达标
---
## 8. 代码质量工具
### Decision: 使用 ESLint + Prettier + TypeScript
**Rationale**:
- **ESLint**: 代码静态分析,捕获潜在错误
- **Prettier**: 统一代码格式,减少团队协作摩擦
- **TypeScript**: 编译时类型检查,配合 strict mode
### 推荐配置
```json
{
"extends": [
"next/core-web-vitals",
"prettier"
],
"rules": {
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/explicit-function-return-type": ["warn", { "allowExpressions": true }],
"no-console": ["warn", { "allow": ["warn", "error"] }]
}
}
```
---
## 9. 部署策略
### Decision: 使用 Vercel 进行部署
**Rationale**:
- **Next.js 原生支持**: 零配置部署,自动优化
- **预览环境**: 每个 PR 自动生成预览 URL
- **边缘网络**: 全球 CDN 加速
- **环境变量**: 安全管理敏感信息
**Alternatives considered**:
- **Railway**: 支持数据库但部署配置较复杂
- **自托管**: 成本低但运维负担重
### 环境变量管理
```bash
# .env.example
DATABASE_URL="postgresql://user:password@localhost:5432/agent_park"
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
WEBHOOK_API_KEY="your-secure-api-key-min-32-chars"
NEXT_INTL_DEFAULT_LOCALE="zh"
NEXT_INTL_SUPPORTED_LOCALES="zh,en"
```
---
## 10. 性能优化
### Decision: 采用多项性能优化策略
**策略列表**:
1. **ISR (增量静态再生)**: 项目列表页面每 5 分钟重新验证
2. **动态导入**: 客户端组件使用 `next/dynamic` 按需加载
3. **图片优化**: 使用 `next/image` 组件自动优化
4. **字体优化**: 使用 `next/font` 优化字体加载
5. **代码分割**: 自动按路由分割代码
6. **Prisma 查询优化**: 使用 `select``include` 精确获取数据
7. **缓存策略**: 使用 Redis 缓存热门查询结果(可选)
---
## 总结
本研究报告涵盖了 Agent Park 项目的主要技术选型和最佳实践。所有选择都基于以下原则:
- **类型安全**: TypeScript + Zod + Prisma 提供端到端类型安全
- **性能优先**: Next.js App Router + ISR + 代码分割确保快速加载
- **开发体验**: shadcn/ui + Tailwind CSS 提供快速开发能力
- **可维护性**: ESLint + Prettier + 测试确保代码质量
- **国际化**: next-intl 支持中英双语
这些技术栈完全符合项目章程要求,为后续开发奠定了坚实基础。
-180
View File
@@ -1,180 +0,0 @@
# 功能规范: Agent Park - AI项目导航网站
**功能分支**: `001-ai-project-navigator`
**创建时间**: 2025-12-25
**状态**: 草稿
**输入**: 用户描述: "构建一个名为agent-park的全网ai项目导航网站,用户可以搜索和浏览各种领域 查看相关的ai项目 项目详情页需要包括项目的大致情况介绍 和相关网站来源的跳转链接(官网,github等)关于数据来源 需要定期从n8n的相关流程中获取(前期用假数据先把网站搭建起来)我希望风格是 anthropic claude的风格"
## Clarifications
### Session 2025-12-25
- Q: n8n数据更新频率如何配置? → A: 后台维护一个n8n流程列表,每个流程可以获取不同类型的数据,可以自定义每个定时任务的执行周期
- Q: 搜索功能范围和排序方式? → A: 全域搜索(名称+描述+标签)+ 按相关性和收录时间排序
- Q: 初始假数据规模? → A: 20-30个项目,最小化前期工作量,快速验证核心功能
- Q: 项目分类体系如何设计? → A: 采用标签系统而非固定分类,按标签和相关性进行灵活分组,适应AI领域快速变化的特点
- Q: 支持哪些外部链接类型? → A: 官网、GitHub、HuggingFace、论文链接四种类型,覆盖主流AI项目场景
- Q: n8n数据集成方式如何设计? → A: n8n执行完成后主动推送数据到本系统的webhook接口,由本系统定义数据字段结构,定时任务由n8n管理
- Q: 技术栈如何选择? → A: Next.js全栈方案,使用React组件开发,利用API Routes构建webhook接口
- Q: webhook接口如何进行身份认证? → A: 使用API Key认证,n8n在请求头中携带预共享密钥进行身份验证
- Q: 项目数据的必填字段有哪些? → A: name、description、tags、至少一个link为必填字段,确保项目信息的完整性和可用性
- Q: 日志和监控策略如何设计? → A: 使用简单控制台日志,区分不同日志级别,前期不实现告警机制
- Q: 当webhook接口接收大量项目数据更新时,前端如何展示数据刷新进度给用户? → A: 不展示更新状态,下次访问时自然生效
- Q: 当webhook接收到的项目数据中部分项目验证失败(如缺少必填字段),但其他项目有效时,系统应如何处理? → A: 部分成功模式,有效的入库,失败记录日志
## 用户场景与测试 *(必填)*
### 用户故事 1 - 浏览和搜索AI项目 (优先级: P1)
用户访问网站后,可以通过浏览不同分类或使用搜索功能,快速找到感兴趣的AI项目。
**优先级原因**: 这是网站的核心功能,用户需要能够发现和查找AI项目,否则整个网站失去价值。
**独立测试**: 可以通过浏览预设的分类列表,或使用搜索框输入关键词,验证项目列表能够正确显示和过滤。
**验收场景**:
1. **给定** 用户访问网站首页,**当** 查看页面时,**那么** 应看到热门标签云和精选项目展示
2. **给定** 用户在搜索框输入关键词,**当** 提交搜索时,**那么** 应显示包含该关键词的相关项目
3. **给定** 用户点击某个标签,**当** 选择标签时,**那么** 应只显示带该标签的项目
4. **给定** 用户组合多个标签筛选,**当** 应用时,**那么** 应显示同时满足所有标签条件的项目
5. **给定** 项目列表很长,**当** 用户滚动时,**那么** 应支持分页或无限滚动加载更多项目
---
### 用户故事 2 - 查看项目详细信息 (优先级: P1)
用户点击某个项目后,可以查看该项目的详细信息,包括项目介绍、官方网站链接、GitHub仓库等。
**优先级原因**: 用户需要了解项目的具体信息才能决定是否进一步探索,详情页是信息获取的关键环节。
**独立测试**: 点击任意项目卡片,验证详情页正确显示项目描述、外部链接等信息。
**验收场景**:
1. **给定** 用户点击某个项目卡片,**当** 进入详情页时,**那么** 应显示项目名称、描述、相关标签
2. **给定** 用户在项目详情页,**当** 查看时,**那么** 应看到官网、GitHub、HuggingFace、论文链接等外部来源的跳转链接
3. **给定** 用户点击外部链接,**当** 访问时,**那么** 应在新标签页中打开对应的官方网站或代码仓库
4. **给定** 某个项目缺少部分信息,**当** 显示时,**那么** 应合理隐藏或提示"暂无信息"
---
### 用户故事 3 - 响应式设计体验 (优先级: P2)
用户在不同设备(桌面、平板、手机)上访问网站,都能获得良好的浏览体验。
**优先级原因**: 现代用户使用多种设备访问网站,响应式设计确保所有用户都能正常使用。
**独立测试**: 在不同屏幕尺寸下访问网站,验证布局自动调整且所有功能可用。
**验收场景**:
1. **给定** 用户使用手机访问,**当** 查看首页时,**那么** 导航和内容应适配竖屏布局
2. **给定** 用户使用平板横屏访问,**当** 浏览时,**那么** 项目卡片应合理排列且易于点击
3. **给定** 用户使用桌面大屏访问,**当** 浏览时,**那么** 应充分利用屏幕空间展示更多内容
---
### 用户故事 4 - 数据更新和管理 (优先级: P3)
n8n工作流程在执行完成后主动推送最新的AI项目数据到本系统的webhook接口,更新网站内容。
**优先级原因**: 这是后台功能,对用户体验影响较小,可以后期实现。前期使用静态数据即可满足需求。
**独立测试**: 通过模拟n8n推送请求,验证新数据能够正确更新到网站。
**验收场景**:
1. **给定** n8n工作流程执行完成,**当** 主动推送数据到webhook接口时,**那么** 数据更新在后台静默执行,用户下次访问时自然看到最新内容
2. **给定** 某些项目不再维护,**当** n8n推送更新数据时,**那么** 这些项目应被标记或移除
3. **给定** webhook接收数据失败,**当** 发生错误时,**那么** 应返回明确错误信息给n8n并记录日志,现有数据不受影响
4. **给定** n8n推送的数据格式不符合要求,**当** 验证时,**那么** 应拒绝请求并返回具体错误信息
5. **给定** 请求缺少有效的API Key,**当** 验证时,**那么** 应返回401未授权错误并记录日志
6. **给定** 推送的数据中部分项目验证失败,**当** 处理时,**那么** 有效项目正常入库,失败项目记录详细错误信息
---
### 边界情况
- 当搜索无结果时,应显示友好提示并建议用户尝试其他关键词
- 当外部链接失效(404)时,应在页面上标注"链接可能失效"但仍保留入口
- 当数据加载缓慢时,应显示加载动画或骨架屏提升体验
- 当用户输入特殊字符搜索时,应正确处理并防止XSS攻击
- 当同时有大量用户访问时,网站应保持稳定响应
## 需求 *(必填)*
### 功能需求
- **FR-001**: 网站首页必须展示热门标签云和精选项目列表
- **FR-002**: 用户必须能够通过搜索框输入关键词查找相关项目,搜索范围覆盖项目名称、描述和标签,结果按相关性优先、收录时间次之排序
- **FR-003**: 用户必须能够通过点击标签筛选项目,支持多标签组合筛选
- **FR-004**: 每个项目必须显示至少包含:项目名称、简短描述、相关标签
- **FR-005**: 项目详情页必须显示项目的完整介绍和外部来源链接(官网、GitHub、HuggingFace、论文链接四种类型)
- **FR-006**: 外部链接必须在新标签页中打开,确保用户不会离开导航网站
- **FR-007**: 网站必须支持响应式设计,适配桌面、平板、手机等设备
- **FR-008**: 网站设计风格应参考Anthropic Claude官网,采用简洁、现代、优雅的视觉风格
- **FR-009**: 前期使用静态假数据构建网站,提供webhook接口接收n8n推送的数据
- **FR-010**: 系统必须提供webhook API接口,接收n8n工作流程推送的项目数据
- **FR-011**: webhook接口必须验证推送数据的格式和必填字段,拒绝不符合规范的数据请求
- **FR-012**: webhook接口必须支持部分成功模式,当批量数据中部分项目验证失败时,有效项目正常入库,失败项目记录详细错误信息
- **FR-013**: webhook接口必须使用API Key进行身份认证,n8n在请求头中携带预共享密钥
- **FR-014**: 系统必须实现控制台日志记录,区分info、warn、error级别,记录webhook请求和错误信息
### 关键实体 *(如果功能涉及数据则包含)*
- **AI项目 (Project)**: 代表一个AI工具或应用
- 必填字段: name(项目名称)、description(详细描述)、tags(标签集合)、links(至少一个外部链接)
- 可选字段: status(项目状态:活跃/停更,默认活跃)、createdAt(收录时间,默认当前时间)、source(数据来源标识)
- **标签 (Tag)**: 代表AI项目的特征标记,采用灵活的标签系统而非固定分类,如"图像生成"、"代码助手"、"数据分析"等,一个项目可以有多个标签
- **外部链接 (ExternalLink)**: 代表项目的来源网站,包含链接类型(官网/GitHub/HuggingFace/论文)和对应的URL
## 成功标准 *(必填)*
### 可衡量的结果
- **SC-001**: 用户可以在5秒内找到目标AI项目(通过搜索或分类浏览)
- **SC-002**: 95%的用户能够成功从项目详情页跳转到外部来源网站
- **SC-003**: 网站首屏加载时间在2秒以内,确保良好的用户体验
- **SC-004**: 网站在主流浏览器(Chrome、Firefox、Safari、Edge)的最新两个版本中正常显示和运行
- **SC-005**: 移动设备上的用户能够单手完成项目搜索和查看的完整流程
- **SC-006**: 网站能够支持至少100个并发用户访问而不出现性能明显下降
- **SC-007**: 用户对网站视觉设计的满意度评分达到4分以上(5分制)
## 假设 *(必填)*
### 技术假设
- 使用Next.js全栈框架,React组件开发,API Routes构建webhook接口
- 前期数据存储为JSON格式或静态文件,后期可迁移到数据库
- 本系统提供webhook API接口接收n8n推送的项目数据
- n8n按照本系统定义的数据字段结构推送JSON格式数据
- 网站部署在Vercel或支持Next.js的服务器上
### 内容假设
- 前期使用20-30个假项目数据进行开发和测试,覆盖主要标签类型
- AI项目数据由人工审核和整理,确保质量和准确性
- 项目标签参考主流AI社区的标记方式(如HuggingFace、Papers with Code),保持灵活性
- 外部链接的维护责任在于对应项目方,网站仅提供导航服务
### 用户假设
- 用户具备基本的网页浏览和搜索技能
- 用户主要使用中文进行搜索和浏览
- 用户希望在发现有趣项目后能够快速访问官方来源获取更多信息
### 约束
- 网站设计应避免过度使用动画效果,保持简洁快速
- 不需要用户注册登录功能,所有内容公开可访问
- 前期不包含用户评论、评分等社交功能,专注于导航展示
## 范围外 *(明确不包含的内容)*
- 用户账户系统和个性化推荐
- 项目评论、评分、收藏等社交功能
- 用户提交新项目的功能(全部由管理员从n8n导入)
- 多语言支持(专注于中文用户体验)
- 实时通知或更新提醒功能
- 付费内容或高级会员功能
-266
View File
@@ -1,266 +0,0 @@
# 任务: Agent Park - AI项目导航网站
**输入**: 来自 `/specs/001-ai-project-navigator/` 的设计文档
**前置条件**: plan.md, spec.md, data-model.md, contracts/webhook.yaml, research.md, quickstart.md
**测试**: 本任务清单不包含测试任务。根据章程要求测试驱动开发, 但实际实现可按需决定是否编写测试。
**组织结构**: 任务按用户故事分组, 以便每个故事能够独立实施和测试。
## 格式: `[ID] [P] [Story] 描述`
- **[P]**: 可以并行运行(不同文件, 无依赖关系)
- **[Story]**: 此任务属于哪个用户故事(例如: US1、US2、US3、US4)
- 在描述中包含确切的文件路径
## 路径约定
- **单一项目**: 仓库根目录下的 `src/``tests/``prisma/`
---
## 阶段 1: 设置(共享基础设施)
**目的**: 项目初始化和基本结构
- [x] T001 创建 Next.js 14+ 项目并配置 TypeScript 严格模式
- [x] T002 安装核心依赖包
- [x] T003 [P] 配置 ESLint 和 Prettier
- [x] T004 [P] 配置 Tailwind CSS
- [x] T005 [P] 配置 shadcn/ui 组件库
- [x] T006 [P] 配置 next-intl 国际化
---
## 阶段 2: 基础(阻塞前置条件)
**目的**: 在任何用户故事可以实施之前必须完成的核心基础设施
**⚠️ 关键**: 在此阶段完成之前, 无法开始任何用户故事工作
- [x] T007 设置 PostgreSQL 数据库
- [x] T008 创建 Prisma schema 定义
- [x] T009 执行 Prisma 初始迁移
- [x] T010 生成 Prisma Client
- [x] T011 创建 Zod 验证 schemas
- [x] T012 创建 Prisma 客户端单例
- [x] T013 创建种子数据脚本
- [x] T014 配置 Next.js 国际化路由结构
- [x] T015 创建环境变量配置文件
**检查点**: 基础就绪 - 现在可以开始并行实施用户故事
---
## 阶段 3: 用户故事 1 - 浏览和搜索AI项目(优先级: P1)🎯 MVP
**目标**: 用户可以通过浏览标签云、搜索关键词、筛选标签来发现AI项目
**独立测试**: 访问首页可以看到热门标签云和精选项目;输入搜索关键词可看到相关项目;点击标签可以筛选项目
### 用户故事 1 的实施
- [x] T016 [P] [US1] 创建首页布局组件 src/app/[locale]/layout.tsx
- [x] T017 [P] [US1] 创建 Header 导航组件 src/components/layout/Header.tsx
- [x] T018 [P] [US1] 创建 Footer 组件 src/components/layout/Footer.tsx
- [x] T019 [P] [US1] 创建 Navigation 组件 src/components/layout/Navigation.tsx
- [x] T020 [P] [US1] 创建 TagCloud 组件 src/components/project/TagCloud.tsx
- [x] T021 [P] [US1] 创建 ProjectCard 组件 src/components/project/ProjectCard.tsx
- [x] T022 [P] [US1] 创建 ProjectList 组件 src/components/project/ProjectList.tsx
- [x] T023 [P] [US1] 创建 SearchBar 组件 src/components/search/SearchBar.tsx
- [x] T024 [US1] 实现首页 src/app/[locale]/page.tsx(依赖于 T016-T023)
- [x] T025 [US1] 实现项目列表页路由 src/app/[locale]/projects/page.tsx
- [x] T026 [US1] 创建 useSearch hook src/hooks/useSearch.ts
- [x] T027 [US1] 创建 useProjects hook src/hooks/useProjects.ts
- [x] T028 [US1] 添加国际化翻译文件 src/messages/zh.json 和 src/messages/en.json
- [x] T029 [US1] 配置 ISR 缓存策略优化页面性能
**检查点**: 此时, 用户应该能够访问首页、浏览项目、使用搜索和标签筛选功能
---
## 阶段 4: 用户故事 2 - 查看项目详细信息(优先级: P1)
**目标**: 用户点击项目卡片后可以查看项目完整信息和外部链接
**独立测试**: 点击任意项目卡片进入详情页, 验证显示项目描述、标签、外部链接, 点击链接在新标签页打开
### 用户故事 2 的实施
- [x] T030 [P] [US2] 创建 ProjectDetail 组件 src/components/project/ProjectDetail.tsx
- [x] T031 [P] [US2] 创建 ExternalLinkCard 组件 src/components/project/ExternalLinkCard.tsx
- [x] T032 [US2] 实现项目详情页路由 src/app/[locale]/projects/[id]/page.tsx(依赖于 T030、T031)
- [x] T033 [US2] 添加项目详情页的国际化翻译
- [x] T034 [US2] 实现外部链接的 target="_blank" 安全属性
**检查点**: 此时, 用户故事 1 和用户故事 2 都应该完全功能化且可独立测试
---
## 阶段 5: 用户故事 3 - 响应式设计体验(优先级: P2)
**目标**: 网站在桌面、平板、手机等不同设备上都能正常显示和操作
**独立测试**: 在不同屏幕尺寸下访问网站, 验证布局自动调整且所有功能可用
### 用户故事 3 的实施
- [ ] T035 [P] [US3] 更新 Header 组件支持响应式布局
- [ ] T036 [P] [US3] 更新 Navigation 组件支持移动端菜单
- [ ] T037 [P] [US3] 更新 ProjectCard 组件支持不同屏幕尺寸
- [ ] T038 [P] [US3] 更新 ProjectList 组件支持响应式网格布局
- [ ] T039 [P] [US3] 更新 SearchBar 组件支持移动端输入
- [ ] T040 [US3] 更新 ProjectDetail 组件支持响应式布局
- [ ] T041 [US3] 配置 Tailwind 响应式断点
- [ ] T042 [US3] 测试并优化移动端触摸交互
**检查点**: 此时, 所有用户故事(US1、US2、US3)都应该独立功能化且支持响应式
---
## 阶段 6: 用户故事 4 - 数据更新和管理(优先级: P3)
**目标**: 提供 webhook API 接口, 接收 n8n 工作流程推送的 AI 项目数据更新
**独立测试**: 使用 curl 或 Postman 模拟 n8n 推送请求, 验证新数据能够正确更新到数据库
### 用户故事 4 的实施
- [x] T043 [P] [US4] 创建 WebhookAuthSchema 验证
- [x] T044 [P] [US4] 创建 WebhookPayloadSchema 验证
- [x] T045 [P] [US4] 创建 ProjectInputSchema 验证
- [x] T046 [US4] 实现 webhook API 端点 src/app/api/webhook/projects/route.ts(依赖于 T043-T045)
- [x] T047 [US4] 实现 API Key 身份验证中间件
- [x] T048 [US4] 实现部分成功模式的批量数据处理逻辑
- [x] T049 [US4] 添加 webhook 请求日志记录
- [x] T050 [US4] 添加 webhook 错误处理和响应格式
- [x] T051 [US4] 配置 webhook API 的环境变量
- [ ] T052 [US4] 编写 webhook API 使用文档
**检查点**: 此时, 所有用户故事现在应该完全功能化
---
## 阶段 7: 完善与横切关注点
**目的**: 影响多个用户故事的改进
- [ ] T053 [P] 全局样式优化实现 Anthropic Claude 风格
- [ ] T054 [P] 优化 Core Web Vitals 性能指标
- [ ] T055 [P] 添加 loading 和 skeleton 状态提升用户体验
- [ ] T056 [P] 实现 404 和错误页面
- [ ] T057 [P] 添加 SEO 元数据配置
- [ ] T058 配置 next.config.js 优化
- [ ] T059 创建 README.md 项目文档
- [ ] T060 运行 quickstart.md 验证完整流程
---
## 依赖关系与执行顺序
### 阶段依赖关系
- **设置(阶段 1)**: 无依赖关系 - 可立即开始
- **基础(阶段 2)**: 依赖于设置完成 - 阻塞所有用户故事
- **用户故事(阶段 3-6)**: 都依赖于基础阶段完成
- US1 和 US2 都是 P1 优先级, 可以并行开发
- US3 (P2) 可以在 US1/US2 基础上进行响应式优化
- US4 (P3) 是独立的后台功能, 可并行开发
- **完善(阶段 7)**: 依赖于所有期望的用户故事完成
### 用户故事依赖关系
- **用户故事 1 (P1)**: 可在基础(阶段 2)后开始 - 无其他故事依赖
- **用户故事 2 (P1)**: 可在基础(阶段 2)后开始 - 可与 US1 并行开发
- **用户故事 3 (P2)**: 依赖于 US1 和 US2 完成 - 在现有组件基础上添加响应式支持
- **用户故事 4 (P3)**: 可在基础(阶段 2)后开始 - 独立的 API 功能
### 每个用户故事内部
- US1: 布局和基础组件 [P] 并行 → 页面路由集成 → hooks 和国际化
- US2: 组件 [P] 并行 → 页面路由实现
- US3: 组件响应式更新 [P] 并行 → 全局配置
- US4: Schema 定义 [P] 并行 → API 端点实现 → 日志和错误处理
### 并行机会
- 所有标记为 [P] 的设置任务(T003-T006)可以并行运行
- US1 的所有布局和基础组件(T016-T023)可以并行开发
- US2 的组件(T030-T031)可以并行开发
- US3 的响应式更新(T035-T039)可以并行进行
- US4 的所有 Schema 定义(T043-T045)可以并行开发
- US1 和 US2 可以由不同团队成员并行处理
- US4 可以与 US1/US2/US3 并行开发(独立的后端功能)
---
## 并行示例: 用户故事 1 (US1)
```bash
# 一起启动用户故事 1 的所有布局和基础组件(可并行):
T016: "创建首页布局组件 src/app/[locale]/layout.tsx"
T017: "创建 Header 导航组件 src/components/layout/Header.tsx"
T018: "创建 Footer 组件 src/components/layout/Footer.tsx"
T019: "创建 Navigation 组件 src/components/layout/Navigation.tsx"
T020: "创建 TagCloud 组件 src/components/project/TagCloud.tsx"
T021: "创建 ProjectCard 组件 src/components/project/ProjectCard.tsx"
T022: "创建 ProjectList 组件 src/components/project/ProjectList.tsx"
T023: "创建 SearchBar 组件 src/components/search/SearchBar.tsx"
```
---
## 实施策略
### 仅 MVP(用户故事 1 + 2)
1. 完成阶段 1: 设置(T001-T006)
2. 完成阶段 2: 基础(T007-T015)
3. 完成阶段 3: 用户故事 1(T016-T029)
4. 完成阶段 4: 用户故事 2(T030-T034)
5. **停止并验证**: 独立测试核心浏览、搜索和详情功能
6. 如准备好则部署/演示
### 增量交付
1. 完成设置 + 基础(T001-T015) → 基础就绪
2. 添加用户故事 1(T016-T029) → 独立测试 → 部署/演示(MVP!)
3. 添加用户故事 2(T030-T034) → 独立测试 → 部署/演示
4. 添加用户故事 3(T035-T042) → 独立测试 → 部署/演示
5. 添加用户故事 4(T043-T052) → 独立测试 → 部署/演示
6. 添加完善(T053-T060) → 最终部署
### 并行团队策略
有多个开发人员时:
1. 团队一起完成设置 + 基础(T001-T015)
2. 基础完成后:
- 开发人员 A: 用户故事 1(T016-T029)
- 开发人员 B: 用户故事 2(T030-T034)
- 开发人员 C: 用户故事 4(T043-T052)
3. US1 和 US2 完成后, 开发人员 A/B 转向用户故事 3(T035-T042)
4. 所有故事完成后, 团队一起进行完善(T053-T060)
---
## 注意事项
- [P] 任务 = 不同文件, 无依赖关系
- [Story] 标签将任务映射到特定用户故事以实现可追溯性
- 每个用户故事应该独立可完成和可测试
- 在每个任务或逻辑组后提交
- 在任何检查点停止以独立验证故事
- 避免: 模糊任务、相同文件冲突、破坏独立性的跨故事依赖
---
## 摘要统计
- **总任务数**: 60 个任务
- **阶段数**: 7 个阶段
- **用户故事数**: 4 个用户故事
- US1 (P1): 14 个任务
- US2 (P1): 5 个任务
- US3 (P2): 8 个任务
- US4 (P3): 10 个任务
- **并行任务数**: 约 35 个任务标记为 [P] 可并行
- **MVP 范围**: 阶段 1-4 (T001-T034), 共 34 个任务