@alenfitz/spec-copilot 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ # 编码规范(通用原则)
2
+
3
+ > 本文件定义跨技术栈的编码通用原则。栈相关的具体语法、工具链和约定请见 `spec_copilot/stack-adapters/<stack>.md`。
4
+
5
+ ## 1. 注释覆盖原则(强制,T1 起生效)
6
+
7
+ 不关心具体语法(Javadoc / docstring / JSDoc),关心**覆盖面**:
8
+
9
+ - **数据模型字段**:每个字段必须有说明,枚举值必须列出所有合法值
10
+ - **公共接口/方法**:必须说明用途、参数含义、返回值/副作用
11
+ - **对外接口层(Controller/API handler)**:必须注释接口用途和关键参数含义
12
+ - **UI 组件**:文件顶部说明组件职责,关键 props/events 必须注释
13
+ - **非显而易见的字段/变量**:必须加注释("显而易见"标准:读了命名 3 秒内能理解用途)
14
+
15
+ 具体语法规则 → 对应 stack adapter §1。
16
+
17
+ ## 2. 命名原则
18
+ - 见名知意,避免缩写(`cfg` / `mgr` / `tmp` 等除非是领域通用缩写)
19
+ - 方法/函数名以动词开头,描述行为
20
+ - 常量全大写下划线分隔
21
+ - **禁止拼音命名**(如 `fangshui` / `daima`)
22
+ - **禁止中英混拼**(如 `sendXiaoxi` / `getUserXinxi`)
23
+ - 模块/包/文件的命名风格保持全项目一致,不混用
24
+
25
+ 栈相关命名约定(大驼峰/小驼峰/蛇形)→ 对应 stack adapter §5。
26
+
27
+ ## 3. 魔法值(强制提取)
28
+
29
+ **禁止在业务逻辑中直接写字符串/数字字面量**,必须提取为常量或枚举。
30
+
31
+ 判定标准:
32
+ - 如果这个值在另一个文件里也出现过 → 一定要提取
33
+ - 如果这个值代表一个状态/类型/分类 → 一定要提取
34
+ - 单次出现的纯计算值(如 `* 100` 算百分比)可以不提取,但建议加注释说明含义
35
+
36
+ 违规示例(语言无关):
37
+ ```
38
+ // ❌ 裸字符串判断状态
39
+ if (status == "OPEN") { ... }
40
+
41
+ // ❌ 裸数字作为阈值
42
+ if (retryCount > 3) { ... }
43
+
44
+ // ✅ 提取为常量或枚举
45
+ if (status == DefectStatus.OPEN) { ... }
46
+ if (retryCount > MAX_RETRY_COUNT) { ... }
47
+ ```
48
+
49
+ ## 4. 异常处理原则
50
+
51
+ - **业务异常**:使用自定义业务异常基类,携带错误码(便于前端区分业务错误类型)
52
+ - **系统异常**:向上抛出,由统一异常处理器兜底
53
+ - **禁止空 catch**(吞掉异常不记录、不处理、不转换)
54
+ - **catch 中必须保留堆栈**:日志必须输出完整异常对象,禁止只打 `e.getMessage()` 丢失堆栈
55
+
56
+ 栈具体的异常基类名称和错误码约定 → 对应 stack adapter §2。
57
+
58
+ ## 5. 日志原则
59
+ - **Controller/API 入口层**:INFO 级别,含请求关键参数
60
+ - **异常**:ERROR 级别,含完整堆栈
61
+ - **敏感信息脱敏**:详见 `spec_copilot/rules/security.md`
62
+
63
+ 栈具体的日志框架和语法 → 对应 stack adapter §3。
64
+
65
+ ## 6. 数据初始化规范
66
+ - **测试/种子数据的日期字段必须使用相对时间**(如"当前时间减 N 天"),禁止写死静态日期
67
+ - 原因:静态日期几个月后会失效,导致"近 N 日"类查询结果异常
68
+
69
+ ## 7. 单一职责
70
+ - **禁止在同一个 task 里混入多个不相关的功能**(职责单一原则)
71
+ - 一个类/一个方法只做一件事
72
+
73
+ ## 8. 安全相关
74
+ → 详见 `spec_copilot/rules/security.md`,本文件不重复安全条目。
@@ -0,0 +1,35 @@
1
+ # 业务领域约束
2
+
3
+ > 涉及业务逻辑时加载本规则。
4
+ > 每个章节给出示例条目(`<!--` 包裹),替换为你的项目的实际规则后删掉示例。
5
+
6
+ ## 状态机
7
+
8
+ > 列出核心业务实体的状态流转规则。格式:实体名 → 允许的状态流转 → 触发条件。
9
+
10
+ <!-- 示例 -->
11
+ - **订单**:待支付 → 已支付(支付成功回调)/ 已取消(超时 30min 或用户取消)
12
+ - **订单**:已支付 → 已发货(仓库确认)/ 已退款(退款审核通过)
13
+ - **订单**:已完成 = 终态,不可流转
14
+ - **推送任务**:待发送 → 发送中 → 已送达 / 发送失败(可重试 3 次)
15
+ <!-- 示例结束 -->
16
+
17
+ ## 计算口径
18
+
19
+ > 列出关键业务指标的计算公式和口径定义。AI 写查询/统计逻辑时必须对齐。
20
+
21
+ <!-- 示例 -->
22
+ - **订单金额** = SUM(order_items.price * quantity) - 优惠券面额 + 运费,不含积分抵扣
23
+ - **退款金额** = 订单实付 × (退货商品原价 / 订单商品总原价),运费不退
24
+ - **活跃用户**:近 30 天有任意操作(登录/下单/评论),排除标记为"测试账号"的用户
25
+ <!-- 示例结束 -->
26
+
27
+ ## 业务流程
28
+
29
+ > 描述核心业务流程的步骤和约束。每个流程列出参与者、步骤顺序、异常分支。
30
+
31
+ <!-- 示例 -->
32
+ - **用户注册**:填写手机号 → 验证码校验(60s 内有效,同号 1min 限发 1 次)→ 设置密码(8-20 位,含数字+字母)→ 创建用户 → 发欢迎推送
33
+ - **定时推送**:Quartz 触发 → 查询待推送目标(分页 200 条/批)→ 逐批调用推送 SDK → 更新推送状态 → 批次间隔 100ms 防限流
34
+ - **数据删除**:所有删除操作均为软删除(set deleted_at),不做物理删除;查询默认带 deleted_at IS NULL
35
+ <!-- 示例结束 -->
@@ -0,0 +1,43 @@
1
+ # 工程上下文
2
+
3
+ > 本文件由 /spec:init 命令自动填充,描述当前项目的技术栈和结构。
4
+ > 切换项目时重新执行 /spec:init 覆盖本文件。
5
+
6
+ ## 1. 应用概况
7
+ - 应用名:
8
+ - 简介:
9
+ - 技术栈:
10
+ - 构建工具:
11
+
12
+ ## 2. 目录结构
13
+
14
+ ## 3. 分层架构(后端)
15
+
16
+ ## 4. 关键依赖与版本
17
+ | 中间件/框架 | 版本 | 用途 | 特别说明 |
18
+ |------------|------|------|---------|
19
+
20
+ ## 5. 主键命名约定
21
+
22
+ ## 6. 数据初始化规范
23
+
24
+ ## 7. API 响应格式
25
+
26
+ ## 8. 本地启动
27
+ ```bash
28
+ # 后端
29
+
30
+ # 前端
31
+ ```
32
+
33
+ ## 9. 镜像与依赖源
34
+ > 内网环境必填,公网项目可为空
35
+
36
+ | 包管理器 | Registry/Mirror URL | 认证方式 | 配置文件路径 |
37
+ |----------|-------------------|---------|-------------|
38
+ | npm | | 无 / token / 密码 | .npmrc |
39
+ | Maven | | 无 / 用户名密码 | settings.xml |
40
+ | pip | | 无 / token | pip.conf |
41
+ | Go | | 无 / 环境变量 | GOPROXY |
42
+
43
+ > 注意:认证凭据不可写入配置文件,通过环境变量注入。配置文件(.npmrc/pip.conf/settings.xml)需加入 .gitignore。
@@ -0,0 +1,38 @@
1
+ # 安全红线
2
+
3
+ > 本文件是安全规则的**唯一来源**,其他文件(coding-style.md 等)不再重复安全条目。
4
+ > 栈相关的具体实现方式(配置注入语法、依赖库选型)见 `spec_copilot/stack-adapters/<stack>.md` §4。
5
+
6
+ ## 1. 密钥与敏感信息(通用)
7
+ - ❌ 禁止在代码中硬编码 API 密钥、AK/SK、数据库密码、第三方服务 Token
8
+ - ❌ 禁止提交包含真实手机号、身份证、姓名等个人信息的测试数据
9
+ - ❌ 禁止在日志中打印手机号、API Key、密码、Token 等敏感信息
10
+ - ✅ 敏感字段必须脱敏或不打印:
11
+ - 手机号:`138****1234`(保留前 3 后 4)
12
+ - 邮箱:`a***@example.com`
13
+ - API Key / Token:`***`(不输出任何片段)
14
+ - 密码:完全不打印
15
+ - ✅ 敏感配置必须通过配置文件(gitignore)注入 → 具体语法见对应 stack adapter §4
16
+
17
+ ## 2. 接口安全(通用)
18
+ - ✅ 对外接口必须做参数非空/范围/格式校验
19
+ - ❌ 禁止信任前端传入的任何值,服务端必须独立校验
20
+ - ✅ 涉及权限的接口必须校验调用方身份,不能仅靠前端隐藏入口
21
+ - ✅ 分页接口必须限制单页最大条数(防止恶意构造大 size 拖垮数据库)
22
+
23
+ ## 3. 业务安全(通用)
24
+ - ⚠️ 涉及外部推送(短信/邮件/IM)的逻辑,必须在 spec 中明确推送范围,人工确认后方可编码
25
+ - ⚠️ 推送前必须校验接收人信息完整性
26
+ - ⚠️ 定时任务必须有幂等保护,防止重复执行
27
+ - ⚠️ 写接口必须考虑幂等(尤其是推送、支付、扣款等有副作用的操作)
28
+ - ⚠️ 批量操作接口必须有单次上限和频率限制
29
+
30
+ ## 4. 数据安全(通用)
31
+ - ✅ 删除操作默认软删除(加 `deleted_at` 字段),物理删除需要明确业务理由
32
+ - ✅ 涉及资金/关键业务数据的变更必须有操作审计日志(谁、何时、改了什么)
33
+ - ❌ 禁止在公开日志/错误响应中泄露数据库表结构、堆栈中的内网 IP、敏感配置路径
34
+
35
+ ## 5. 依赖安全(通用)
36
+ - ✅ 生产依赖必须锁定版本(不要用 `latest` / `^`)
37
+ - ✅ 定期运行依赖漏洞扫描(`npm audit` / `mvn dependency-check` / `pip-audit`)
38
+ - ⚠️ 引入新依赖前评估:官方/社区活跃度、最近维护时间、已知 CVE
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+ # install-hooks.sh — 把 spec-lint.sh 安装为 Git pre-commit hook
3
+ #
4
+ # 用法:在项目根目录运行
5
+ # bash spec_copilot/scripts/install-hooks.sh
6
+
7
+ set -eu
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
11
+
12
+ if [[ ! -d "$ROOT_DIR/.git" ]]; then
13
+ echo "✗ 未在 Git 仓库中(找不到 $ROOT_DIR/.git)"
14
+ exit 1
15
+ fi
16
+
17
+ HOOK_FILE="$ROOT_DIR/.git/hooks/pre-commit"
18
+
19
+ if [[ -f "$HOOK_FILE" ]]; then
20
+ echo "⚠ 已存在 pre-commit hook,备份为 pre-commit.bak.$(date +%s)"
21
+ mv "$HOOK_FILE" "$HOOK_FILE.bak.$(date +%s)"
22
+ fi
23
+
24
+ cat > "$HOOK_FILE" <<'EOF'
25
+ #!/usr/bin/env bash
26
+ # spec_copilot pre-commit hook
27
+
28
+ ROOT_DIR="$(git rev-parse --show-toplevel)"
29
+ LINT="$ROOT_DIR/spec_copilot/scripts/spec-lint.sh"
30
+
31
+ if [[ -x "$LINT" ]]; then
32
+ bash "$LINT" --hook
33
+ fi
34
+ EOF
35
+
36
+ chmod +x "$HOOK_FILE"
37
+ chmod +x "$SCRIPT_DIR/spec-lint.sh"
38
+
39
+ echo "✓ pre-commit hook 已安装到 $HOOK_FILE"
40
+ echo " 每次 commit 前会自动跑 spec-lint,检查 changes/ 下有改动的需求"
41
+ echo " 如需跳过:git commit --no-verify"
@@ -0,0 +1,122 @@
1
+ #!/bin/bash
2
+ # spec-gate.sh — 阶段进入条件检查
3
+ # Usage: spec-gate.sh <变更名> <phase>
4
+ # Exit 0 = gate passed, exit 1 = gate failed (blocked)
5
+
6
+ set -euo pipefail
7
+
8
+ CHANGE="$1"
9
+ PHASE="$2"
10
+ CHANGE_DIR="spec_copilot/changes/${CHANGE}"
11
+
12
+ die() { echo "GATE FAILED: $1"; exit 1; }
13
+ ok() { echo " ✓ $1"; }
14
+
15
+ [ -d "$CHANGE_DIR" ] || die "变更目录不存在: $CHANGE_DIR"
16
+
17
+ SPEC="$CHANGE_DIR/spec.md"
18
+ TASKS="$CHANGE_DIR/tasks.md"
19
+ LOG="$CHANGE_DIR/log.md"
20
+
21
+ case "$PHASE" in
22
+ apply)
23
+ echo "→ 门禁检查: apply 阶段"
24
+ [ -f "$SPEC" ] || die "spec.md 不存在 — 请先执行 /spec:propose"
25
+
26
+ # HARD-GATE: §9 待澄清必须为空
27
+ if grep -q '^[[:space:]]*- \[ \]' "$SPEC"; then
28
+ echo ""
29
+ echo " §9 待澄清仍有未解决项:"
30
+ grep '^[[:space:]]*- \[ \]' "$SPEC" || true
31
+ die "HARD-GATE 未通过 — 待澄清全部解决后才能进入 /spec:apply"
32
+ fi
33
+ ok "§9 待澄清已清空"
34
+
35
+ # 检查复杂度并验证相应制品
36
+ if grep -q 'complexity:.*🔴' "$SPEC"; then
37
+ [ -f "$TASKS" ] || die "🔴 复杂需求需要 tasks.md — 请重新执行 /spec:propose 生成 tasks"
38
+ ok "tasks.md 已就绪(复杂需求)"
39
+
40
+ # 检查 tasks.md 是否有未完成项(但允许进行中的 task)
41
+ incomplete=$(grep -c '^\s*- \[ \]' "$TASKS" 2>/dev/null || true)
42
+ if [ "${incomplete:-0}" -gt 0 ]; then
43
+ echo " ⚠ tasks.md 仍有 ${incomplete} 个未开始 task(AI 将逐 task 推进)"
44
+ fi
45
+ elif grep -q 'complexity:.*🟡' "$SPEC"; then
46
+ ok "中等需求,无需 tasks.md"
47
+ elif grep -q 'complexity:.*🟢' "$SPEC"; then
48
+ die "🟢 简单需求不应进入 apply 流程 — 直接在对话中编码"
49
+ fi
50
+
51
+ echo "GATE PASSED → 可以开始编码"
52
+ ;;
53
+
54
+ review)
55
+ echo "→ 门禁检查: review 阶段"
56
+ [ -f "$SPEC" ] || die "spec.md 不存在"
57
+
58
+ # 检查冒烟通过
59
+ if [ -f "$LOG" ]; then
60
+ if grep -qi '冒烟.*通过\|smoke.*pass\|编译.*0.*error' "$LOG"; then
61
+ ok "冒烟验证已通过"
62
+ else
63
+ die "log.md 中未找到冒烟通过记录 — 请先执行 /spec:smoke"
64
+ fi
65
+ else
66
+ die "log.md 不存在 — 请先执行 /spec:smoke"
67
+ fi
68
+
69
+ # 检查所有 task 已提交
70
+ if [ -f "$TASKS" ]; then
71
+ if grep -q '^\s*- \[ \]' "$TASKS" 2>/dev/null; then
72
+ echo " ⚠ tasks.md 仍有未完成 task(可能影响审查完整性)"
73
+ fi
74
+ fi
75
+
76
+ echo "GATE PASSED → 可以开始审查"
77
+ ;;
78
+
79
+ archive)
80
+ echo "→ 门禁检查: archive 阶段"
81
+ [ -f "$SPEC" ] || die "spec.md 不存在"
82
+
83
+ # 检查审查通过
84
+ if grep -qi '结论.*通过\|审查.*通过' "$SPEC"; then
85
+ ok "审查结论: 通过"
86
+ elif grep -qi '结论.*需修复\|审查.*未通过\|审查.*✗' "$SPEC"; then
87
+ die "审查未通过 — 请先执行 /spec:fix 修复 Critical 问题"
88
+ else
89
+ die "spec.md 中未找到审查结论 — 请先执行 /spec:review"
90
+ fi
91
+
92
+ # 检查是否有未沉淀的知识发现
93
+ if [ -f "$LOG" ]; then
94
+ if grep -qi '知识发现\|knowledge\|#tip' "$LOG"; then
95
+ echo " ℹ log.md 含知识发现,归档时将提示沉淀到 knowledge/"
96
+ fi
97
+ fi
98
+
99
+ echo "GATE PASSED → 可以归档"
100
+ ;;
101
+
102
+ test)
103
+ echo "→ 门禁检查: test 阶段"
104
+ [ -f "$SPEC" ] || die "spec.md 不存在"
105
+
106
+ # 🔴 复杂需求必须跑测试
107
+ if grep -q 'complexity:.*🔴' "$SPEC"; then
108
+ ok "🔴 复杂需求 — 强制执行自动化测试"
109
+ elif grep -q 'complexity:.*🟡' "$SPEC"; then
110
+ echo " ℹ 🟡 中等需求 — 测试可选但推荐"
111
+ else
112
+ die "此阶段仅适用于 🔴 复杂需求"
113
+ fi
114
+
115
+ echo "GATE PASSED → 可以生成测试"
116
+ ;;
117
+
118
+ *)
119
+ echo "Usage: spec-gate.sh <变更名> <apply|review|archive|test>"
120
+ exit 1
121
+ ;;
122
+ esac
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env bash
2
+ # spec-lint.sh — 检查 changes/<变更名>/ 下 spec/tasks/log 三件套的完整性
3
+ #
4
+ # 用法:
5
+ # ./scripts/spec-lint.sh <变更名> 检查指定需求
6
+ # ./scripts/spec-lint.sh --all 检查 changes/ 下所有进行中的需求
7
+ # ./scripts/spec-lint.sh --hook 作为 Git pre-commit hook 运行(仅检查有暂存改动的变更)
8
+ #
9
+ # 退出码:
10
+ # 0 全部通过
11
+ # 1 发现问题
12
+ # 2 用法错误
13
+
14
+ set -u
15
+
16
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17
+ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
18
+ CHANGES_DIR="$ROOT_DIR/changes"
19
+
20
+ RED=$'\033[31m'
21
+ YELLOW=$'\033[33m'
22
+ GREEN=$'\033[32m'
23
+ RESET=$'\033[0m'
24
+
25
+ ERRORS=0
26
+ WARNINGS=0
27
+
28
+ log_err() { echo "${RED}✗ ERROR${RESET} $1"; ERRORS=$((ERRORS+1)); }
29
+ log_warn() { echo "${YELLOW}⚠ WARN${RESET} $1"; WARNINGS=$((WARNINGS+1)); }
30
+ log_ok() { echo "${GREEN}✓ OK${RESET} $1"; }
31
+
32
+ check_spec() {
33
+ local change_dir="$1"
34
+ local spec="$change_dir/spec.md"
35
+
36
+ if [[ ! -f "$spec" ]]; then
37
+ log_err "$spec 不存在"
38
+ return
39
+ fi
40
+
41
+ local required_sections=(
42
+ "## 1. 背景与目标"
43
+ "## 2. 代码现状"
44
+ "## 3. 功能点"
45
+ "## 4. 业务规则"
46
+ "## 9. 待澄清"
47
+ "## 13. 确认记录"
48
+ )
49
+
50
+ for section in "${required_sections[@]}"; do
51
+ if ! grep -q "^${section}" "$spec"; then
52
+ log_err "$spec 缺少章节:$section"
53
+ fi
54
+ done
55
+
56
+ if ! grep -qE "^> status: (propose|apply|review|done)" "$spec"; then
57
+ log_err "$spec 缺少 status 字段或值非法"
58
+ fi
59
+
60
+ if ! grep -qE "^> complexity:" "$spec"; then
61
+ log_err "$spec 缺少 complexity 字段"
62
+ fi
63
+
64
+ local status
65
+ status=$(grep -oE "status: (propose|apply|review|done)" "$spec" | awk '{print $2}' | head -1)
66
+ if [[ "$status" == "apply" || "$status" == "review" || "$status" == "done" ]]; then
67
+ if awk '/^## 9\./,/^## 10\./' "$spec" | grep -qE "^- \[ \]"; then
68
+ log_err "$spec status=$status 但 §9 待澄清仍有未勾选项"
69
+ fi
70
+ fi
71
+ }
72
+
73
+ check_tasks() {
74
+ local change_dir="$1"
75
+ local tasks="$change_dir/tasks.md"
76
+ local spec="$change_dir/spec.md"
77
+
78
+ if [[ -f "$spec" ]] && grep -q "complexity:.*🔴" "$spec"; then
79
+ if [[ ! -f "$tasks" ]]; then
80
+ log_err "$change_dir 是 🔴 复杂需求但缺少 tasks.md"
81
+ return
82
+ fi
83
+ elif [[ ! -f "$tasks" ]]; then
84
+ log_ok "$change_dir 非 🔴 复杂需求,tasks.md 可选"
85
+ return
86
+ fi
87
+
88
+ if ! grep -q "^## 前置条件" "$tasks"; then
89
+ log_err "$tasks 缺少 ## 前置条件 章节"
90
+ fi
91
+
92
+ if ! grep -qE "^## Task [0-9]+" "$tasks"; then
93
+ log_err "$tasks 没有任何 Task"
94
+ fi
95
+
96
+ if grep -qE "^## Task [0-9]+" "$tasks"; then
97
+ local tc vc
98
+ tc=$(grep -cE "^## Task [0-9]+" "$tasks" 2>/dev/null); tc=${tc:-0}
99
+ vc=$(grep -c "验证命令" "$tasks" 2>/dev/null); vc=${vc:-0}
100
+ if [[ "${vc:-0}" -lt "${tc:-0}" ]]; then
101
+ log_warn "$tasks 部分 Task 可能缺少'验证命令'(${vc}/${tc})"
102
+ fi
103
+ fi
104
+
105
+ if [[ -f "$spec" ]] && grep -qE "status: (review|done)" "$spec"; then
106
+ if ! grep -q "^## 变更摘要" "$tasks"; then
107
+ log_err "$tasks status 已进入 review/done 但缺少'变更摘要'章节"
108
+ fi
109
+ fi
110
+ }
111
+
112
+ check_log() {
113
+ local change_dir="$1"
114
+ local log="$change_dir/log.md"
115
+
116
+ if [[ ! -f "$log" ]]; then
117
+ log_err "$log 不存在"
118
+ return
119
+ fi
120
+
121
+ local required=(
122
+ "## 时间线"
123
+ "## 知识发现"
124
+ "## Spec-Code 偏差记录"
125
+ )
126
+
127
+ for section in "${required[@]}"; do
128
+ if ! grep -q "^${section}" "$log"; then
129
+ log_err "$log 缺少章节:$section"
130
+ fi
131
+ done
132
+ }
133
+
134
+ check_change() {
135
+ local change_name="$1"
136
+ local change_dir="$CHANGES_DIR/$change_name"
137
+
138
+ echo ""
139
+ echo "▶ 检查 $change_name"
140
+
141
+ if [[ ! -d "$change_dir" ]]; then
142
+ log_err "目录不存在:$change_dir"
143
+ return
144
+ fi
145
+
146
+ check_spec "$change_dir"
147
+ check_tasks "$change_dir"
148
+ check_log "$change_dir"
149
+ }
150
+
151
+ case "${1:-}" in
152
+ --all)
153
+ for dir in "$CHANGES_DIR"/*/; do
154
+ name=$(basename "$dir")
155
+ [[ "$name" == "templates" ]] && continue
156
+ check_change "$name"
157
+ done
158
+ ;;
159
+ --hook)
160
+ if ! staged=$(git diff --cached --name-only 2>/dev/null); then
161
+ echo "不在 Git 仓库,跳过 hook 检查"
162
+ exit 0
163
+ fi
164
+ changes=$(echo "$staged" | grep -oE "spec_copilot/changes/[^/]+" | sort -u | sed 's|spec_copilot/changes/||')
165
+ if [[ -z "$changes" ]]; then
166
+ exit 0
167
+ fi
168
+ for name in $changes; do
169
+ [[ "$name" == "templates" ]] && continue
170
+ check_change "$name"
171
+ done
172
+ ;;
173
+ "")
174
+ echo "用法:$0 <变更名> | --all | --hook"
175
+ exit 2
176
+ ;;
177
+ -*)
178
+ echo "未知参数:$1"
179
+ exit 2
180
+ ;;
181
+ *)
182
+ check_change "$1"
183
+ ;;
184
+ esac
185
+
186
+ echo ""
187
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
188
+ if [[ $ERRORS -eq 0 && $WARNINGS -eq 0 ]]; then
189
+ echo "${GREEN}全部通过${RESET}"
190
+ exit 0
191
+ elif [[ $ERRORS -eq 0 ]]; then
192
+ echo "${YELLOW}$WARNINGS 个警告(不阻塞)${RESET}"
193
+ exit 0
194
+ else
195
+ echo "${RED}$ERRORS 个错误 / $WARNINGS 个警告${RESET}"
196
+ exit 1
197
+ fi
@@ -0,0 +1,70 @@
1
+ # {{技术栈名称}} 适配层
2
+
3
+ > 本文件是某个技术栈的具体规范补充。通用原则见 `spec_copilot/rules/coding-style.md`。
4
+
5
+ ## 1. 注释规范
6
+ > 对应 `rules/coding-style.md` §1。列出本栈的文档注释语法、工具链、覆盖要求。
7
+
8
+ - 类/函数/方法注释语法:
9
+ - 字段/属性注释语法:
10
+ - 工具链(生成器、lint 规则):
11
+
12
+ ## 2. 异常处理
13
+ > 对应 `rules/coding-style.md` §4。
14
+
15
+ - 自定义业务异常基类:
16
+ - 错误码约定:
17
+ - 统一异常处理器:
18
+
19
+ ## 3. 日志规范
20
+ > 对应 `rules/coding-style.md` §5。
21
+
22
+ - 日志框架:
23
+ - 日志级别使用约定:
24
+ - 脱敏方式:
25
+
26
+ ## 4. 配置注入
27
+ > 对应 `rules/security.md` §1。敏感配置如何通过配置文件注入。
28
+
29
+ - 配置文件命名和位置:
30
+ - 注入方式:
31
+ - gitignore 条目:
32
+
33
+ ## 5. 命名约定
34
+ - 类/类型名:
35
+ - 方法/函数名:
36
+ - 常量:
37
+ - 文件名:
38
+
39
+ ## 6. 分层架构
40
+ ```
41
+ {{分层调用关系,如 Controller → Service → Repository}}
42
+ ```
43
+
44
+ ## 7. 前端特有(如适用)
45
+ - 状态管理:
46
+ - 路由组织:
47
+ - 样式组织:
48
+ - API 调用封装位置:
49
+ - 组件命名和文件组织:
50
+
51
+ ## 8. 测试规范
52
+ - 单元测试框架:
53
+ - 集成测试方式:
54
+ - 测试文件命名和位置:
55
+ - 覆盖率工具:
56
+
57
+ ## 9. 常见坑与注意事项
58
+ > 这个栈特有的、容易踩的坑。随着知识积累持续更新。
59
+
60
+ ## 10. code-quality-reviewer 栈相关检查项
61
+ > 本栈专属的 review 规则,会被 /spec:review 阶段二读取。
62
+
63
+ ### Critical
64
+ - [ ]
65
+
66
+ ### Important
67
+ - [ ]
68
+
69
+ ### Minor
70
+ - [ ]