chore: 移除 SpecKit 相关文件和配置
- 删除所有 SpecKit 命令文件(.claude/commands/speckit.*) - 删除 .specify 配置目录(templates, scripts, memory) - 删除 specs 文档目录 - 更新 CLAUDE.md:移除 SpecKit Skills 说明
This commit is contained in:
@@ -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
|
||||
@@ -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 A–E 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 follow‑ups (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 是否明确指定?"
|
||||
@@ -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
|
||||
@@ -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` 文件.
|
||||
@@ -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. **建议的后续步骤**:例如代码审查、部署计划等
|
||||
@@ -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` 重新生成任务列表.
|
||||
@@ -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、代理特定文件
|
||||
|
||||
## 关键规则
|
||||
|
||||
- 使用绝对路径
|
||||
- 关卡失败或未解决的澄清事项时报错
|
||||
@@ -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%"(技术特定)
|
||||
@@ -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...)
|
||||
- 每个故事内: 测试(如果请求)→ 模型 → 服务 → 端点 → 集成
|
||||
- 最终阶段: 完善与横切关注点
|
||||
- 每个用户故事阶段应该是一个完整的、可独立测试的增量
|
||||
@@ -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 不匹配的仓库中创建议题**
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
<!-- 手动添加内容开始 -->
|
||||
<!-- 手动添加内容结束 -->
|
||||
@@ -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]`
|
||||
- 内联添加评论或发现
|
||||
- 链接到相关资源或文档
|
||||
- 项目按顺序编号, 便于参考
|
||||
@@ -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 个项目不够] |
|
||||
| [例如: 仓储模式] | [特定问题] | [为什么直接数据库访问不够] |
|
||||
@@ -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%"]
|
||||
@@ -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] 标签将任务映射到特定用户故事以实现可追溯性
|
||||
- 每个用户故事应该独立可完成和可测试
|
||||
- 在实施前验证测试失败
|
||||
- 在每个任务或逻辑组后提交
|
||||
- 在任何检查点停止以独立验证故事
|
||||
- 避免: 模糊任务、相同文件冲突、破坏独立性的跨故事依赖
|
||||
|
||||
@@ -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 领域快速变化
|
||||
- **可扩展性**: 清晰的实体关系,便于后续功能扩展
|
||||
- **性能优先**: 合理的索引设计,优化查询性能
|
||||
@@ -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) - 技术选型研究
|
||||
@@ -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 作为 ORM,PostgreSQL 作为数据库
|
||||
|
||||
**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 支持中英双语
|
||||
|
||||
这些技术栈完全符合项目章程要求,为后续开发奠定了坚实基础。
|
||||
@@ -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导入)
|
||||
- 多语言支持(专注于中文用户体验)
|
||||
- 实时通知或更新提醒功能
|
||||
- 付费内容或高级会员功能
|
||||
@@ -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 个任务
|
||||
Reference in New Issue
Block a user