add-coder 0.1.17 → 0.2.1
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.
- package/README.en.md +108 -10
- package/README.md +254 -17
- package/dist/index.js +72 -98
- package/package.json +2 -2
- package/templates/adapters/claude/hooks/doc-format-guard.sh +164 -9
- package/templates/adapters/claude/hooks/notification.sh +29 -0
- package/templates/adapters/claude/hooks/permission-denied.sh +42 -0
- package/templates/adapters/claude/hooks/post-tool-use.sh +25 -8
- package/templates/adapters/claude/hooks/pre-compact.sh +31 -8
- package/templates/adapters/claude/hooks/pre-tool-use.sh +63 -13
- package/templates/adapters/claude/hooks/prompt-submit.sh +70 -8
- package/templates/adapters/claude/hooks/session-end.sh +21 -0
- package/templates/adapters/claude/hooks/session-start.sh +26 -13
- package/templates/adapters/claude/hooks/stop-check.sh +40 -5
- package/templates/adapters/claude/hooks/stop-failure.sh +44 -0
- package/templates/adapters/claude/hooks/subagent-guard.sh +29 -8
- package/templates/adapters/claude/hooks/subagent-stop.sh +20 -0
- package/templates/adapters/codex/hooks/notification.sh +38 -0
- package/templates/adapters/codex/hooks/post-tool-use.sh +35 -0
- package/templates/adapters/codex/hooks/pre-tool-use.sh +78 -0
- package/templates/adapters/codex/hooks/prompt-submit.sh +78 -0
- package/templates/adapters/codex/hooks/session-start.sh +36 -0
- package/templates/adapters/codex/hooks/stop-check.sh +45 -0
- package/templates/adapters/codex/hooks.json +1 -0
- package/templates/adapters/codex/settings.json +1 -0
- package/templates/adapters/qoder/hooks/doc-format-guard.sh +9 -1
- package/templates/adapters/qoder/hooks/lib/vocabulary.sh +1 -1
- package/templates/adapters/qoder/hooks/notification.sh +8 -5
- package/templates/adapters/qoder/hooks/post-tool-use.sh +21 -11
- package/templates/adapters/qoder/hooks/pre-compact.sh +31 -6
- package/templates/adapters/qoder/hooks/pre-tool-use.sh +66 -64
- package/templates/adapters/qoder/hooks/prompt-submit.sh +12 -2
- package/templates/adapters/qoder/hooks/session-end.sh +24 -0
- package/templates/adapters/qoder/hooks/session-start.sh +22 -7
- package/templates/adapters/qoder/hooks/stop-check.sh +18 -41
- package/templates/adapters/qoder/hooks/subagent-guard.sh +22 -6
- package/templates/adapters/qoder/hooks/subagent-stop.sh +26 -0
- package/templates/adapters/qoder/settings.json +13 -13
- package/templates/adapters/trae/hooks/notification.sh +38 -0
- package/templates/adapters/trae/hooks/post-tool-failure.sh +10 -0
- package/templates/adapters/trae/hooks/post-tool-use.sh +35 -0
- package/templates/adapters/trae/hooks/pre-compact.sh +37 -0
- package/templates/adapters/trae/hooks/pre-tool-use.sh +78 -0
- package/templates/adapters/trae/hooks/prompt-submit.sh +78 -0
- package/templates/adapters/trae/hooks/session-end.sh +21 -0
- package/templates/adapters/trae/hooks/session-start.sh +36 -0
- package/templates/adapters/trae/hooks/stop-check.sh +45 -0
- package/templates/adapters/trae/hooks/subagent-guard.sh +36 -0
- package/templates/adapters/trae/hooks/subagent-stop.sh +20 -0
- package/templates/adapters/trae/hooks.json +1 -0
- package/templates/adapters/trae/settings.json +1 -0
- package/templates/adapters/vscode/.github/hooks/error-occurred.json +1 -0
- package/templates/adapters/vscode/.github/hooks/post-tool-use.json +1 -0
- package/templates/adapters/vscode/.github/hooks/pre-compact.json +1 -0
- package/templates/adapters/vscode/.github/hooks/pre-tool-use.json +14 -0
- package/templates/adapters/vscode/.github/hooks/session-end.json +1 -0
- package/templates/adapters/vscode/.github/hooks/session-start.json +14 -0
- package/templates/adapters/vscode/.github/hooks/stop-check.json +1 -0
- package/templates/adapters/vscode/.github/hooks/subagent-start.json +1 -0
- package/templates/adapters/vscode/.github/hooks/subagent-stop.json +1 -0
- package/templates/adapters/vscode/.github/hooks/user-prompt-submit.json +14 -0
- package/templates/adapters/vscode/hooks/notification.sh +37 -0
- package/templates/adapters/vscode/hooks/post-tool-failure.sh +28 -0
- package/templates/adapters/vscode/hooks/post-tool-use.sh +35 -0
- package/templates/adapters/vscode/hooks/pre-compact.sh +37 -0
- package/templates/adapters/vscode/hooks/pre-tool-use.sh +78 -0
- package/templates/adapters/vscode/hooks/prompt-submit.sh +78 -0
- package/templates/adapters/vscode/hooks/session-end.sh +13 -0
- package/templates/adapters/vscode/hooks/session-start.sh +35 -0
- package/templates/adapters/vscode/hooks/stop-check.sh +53 -0
- package/templates/adapters/vscode/hooks/subagent-guard.sh +37 -0
- package/templates/adapters/vscode/hooks/subagent-stop.sh +12 -0
- package/templates/adapters/vscode/launch.json +1 -1
- package/templates/adapters/vscode/settings.json +2 -3
- package/templates/adapters/vscode/tasks.json +3 -3
- package/templates/core/docs/ADD-governance-claude-code.md +121 -0
- package/templates/core/docs/ADD-governance-codex.md +75 -0
- package/templates/core/docs/ADD-governance-qoder-cn.md +99 -0
- package/templates/core/docs/ADD-governance-trae.md +63 -0
- package/templates/core/docs/ADD-governance-vscode-copilot.md +132 -0
- package/templates/core/hooks/doc-format-guard.sh +164 -9
- package/templates/core/hooks/lib/common.sh +261 -0
- package/templates/core/hooks/lib/preload-templates.sh +187 -0
- package/templates/core/hooks/lib/session-end.sh +76 -0
- package/templates/core/hooks/lib/subagent-stop.sh +90 -0
- package/templates/core/hooks/notification.sh +29 -0
- package/templates/core/hooks/post-tool-use.sh +25 -8
- package/templates/core/hooks/pre-compact.sh +31 -8
- package/templates/core/hooks/pre-tool-use.sh +63 -13
- package/templates/core/hooks/prompt-submit.sh +70 -8
- package/templates/core/hooks/session-end.sh +38 -0
- package/templates/core/hooks/session-start.sh +26 -13
- package/templates/core/hooks/stop-check.sh +40 -5
- package/templates/core/hooks/subagent-stop.sh +47 -0
- package/templates/core/rules/project_rules.md +1 -1
- package/templates/core/scripts/mcp-server.ts +38 -120
- package/templates/core/skills/add-paradigm/SKILL.md +24 -0
- package/templates/core/hooks/lib/context-inject.sh +0 -96
- package/templates/core/hooks/lib/state-detect.sh +0 -104
- package/templates/core/hooks/lib/vocabulary.sh +0 -49
|
@@ -425,7 +425,7 @@ reason: 中文/英文说明本次改动的目的
|
|
|
425
425
|
正确示例:
|
|
426
426
|
- ✅ `src/middleware.ts`({{projectName}} workspace 内)
|
|
427
427
|
- ✅ `agrisynapse/src/api/agent/types.ts`(跨项目时带项目名前缀)
|
|
428
|
-
- ✅ `{{magicDir}}/plans/{{projectName}}-agrisynapse-integration-plan-v1.md
|
|
428
|
+
- ✅ `{{magicDir}}/plans/{{projectName}}-agrisynapse-integration-plan-v1.md`({{magicDir}} 内文件)
|
|
429
429
|
|
|
430
430
|
原因:`query_audit_logs({ targetId })` 做精确匹配,绝对路径和相对路径是两条不同的记录,导致漏查。
|
|
431
431
|
|
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from "url"
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url)
|
|
6
6
|
const __dirname = dirname(__filename)
|
|
7
7
|
const PROJECT_ROOT = resolve(__dirname, "..", "..")
|
|
8
|
-
const MAGIC_DIR = basename(dirname(__dirname)) // ".qoder"
|
|
8
|
+
const MAGIC_DIR = basename(dirname(__dirname)) // ".qoder" / ".claude" / ".vscode" / ".trae" / ".codex" / ".add"
|
|
9
9
|
|
|
10
10
|
// 环境变量加载优先级: .env.development.local > .env.development > .env.local > .env
|
|
11
11
|
const ENV_CANDIDATES = [".env.development.local", ".env.development", ".env.local", ".env"];
|
|
@@ -122,7 +122,7 @@ function extractBackflowKeywords(fixText: string): string[] {
|
|
|
122
122
|
const keywords: string[] = []
|
|
123
123
|
|
|
124
124
|
// 英文标识符(如 perExpertTopK, agri_tech, SearchOptions)
|
|
125
|
-
const enMatches = fixText.match(/\b(perExpertTopK|RetrieveBudget|GroundingStatus|SearchOptions|add-route|handoff|spec\.md|tasks\.md|checklist\.md|collectionName|Phase\s*\d+|ChromaCollectionManager|agri_tech|crop_compare|roi_analysis|pest_risk|daily_report|weekly_report)\b/gi)
|
|
125
|
+
const enMatches = fixText.match(/\b(perExpertTopK|RetrieveBudget|GroundingStatus|SearchOptions|add-route|handoff|spec\.md|tasks\.md|checklist\.md|collectionName|Phase\s*\d+|ChromaCollectionManager|agri_tech|crop_compare|roi_analysis|pest_risk|daily_report|weekly_report|session-start|prompt-submit|pre-tool-use|post-tool-use|stop-check|pre-compact|subagent-stop|renderer\.ts|SessionStart|UserPromptSubmit|hooks\/lib|github\/hooks)\b/gi)
|
|
126
126
|
if (enMatches) keywords.push(...enMatches.map(m => m.toLowerCase()))
|
|
127
127
|
|
|
128
128
|
// 中文专用名词
|
|
@@ -130,14 +130,25 @@ function extractBackflowKeywords(fixText: string): string[] {
|
|
|
130
130
|
"迁移路线图", "迁移roadmap", "多collection", "多 collection",
|
|
131
131
|
"冗余策略", "复制", "引用方案", "新签名",
|
|
132
132
|
"汇总专家", "per-collection", "per collection",
|
|
133
|
-
"不阻塞", "降级路径", "回滚",
|
|
133
|
+
"不阻塞", "降级路径", "回滚", "回流",
|
|
134
|
+
// add-coder 领域关键词
|
|
135
|
+
"轮次", "实施顺序", "增量插入", "模板", "预读", "renderer", "共享脚本", "Layer",
|
|
136
|
+
"stderr", "stdout", "注入", "标记文件", "tpl-injected", "验收阻断",
|
|
134
137
|
]
|
|
135
138
|
for (const pat of cnPatterns) {
|
|
136
139
|
if (fixText.includes(pat)) keywords.push(pat)
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
// 去重并过滤停用词
|
|
140
|
-
|
|
143
|
+
const result = [...new Set(keywords.filter(k => k.length > 1 && !stopWords.has(k)))]
|
|
144
|
+
|
|
145
|
+
// 兜底:若关键词提取为空,用 fix 原文作为搜索串(防止新领域术语永不被识别)
|
|
146
|
+
if (result.length === 0 && fixText.length > 0) {
|
|
147
|
+
const fallback = fixText.replace(/[✅⬜\[\]\*]/g, "").trim().slice(0, 60)
|
|
148
|
+
if (fallback.length > 3) result.push(fallback)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return result
|
|
141
152
|
}
|
|
142
153
|
|
|
143
154
|
const server = new McpServer(
|
|
@@ -2810,7 +2821,7 @@ server.registerTool(
|
|
|
2810
2821
|
const reviewFiles = await readdir(reviewsDir)
|
|
2811
2822
|
// 从 Plan 全文提取所有 Review 引用,优先匹配含 planKeyword 的
|
|
2812
2823
|
const allReviewRefs = Array.from(planContent.matchAll(/\.(qoder|claude|add|vscode)\/reviews\/([^\s)]+\.md)/g))
|
|
2813
|
-
.map(m => m[
|
|
2824
|
+
.map(m => m[2].split("/").pop() || "")
|
|
2814
2825
|
.filter(Boolean)
|
|
2815
2826
|
// 策略1: 在所有引用中优先选含 planKeyword 的
|
|
2816
2827
|
reviewName = allReviewRefs.find(f =>
|
|
@@ -2868,8 +2879,10 @@ server.registerTool(
|
|
|
2868
2879
|
planPenalties.push(`Plan 缺少 §6 验收标准(${phaseTotal} 个 Phase 无独立验收)`)
|
|
2869
2880
|
}
|
|
2870
2881
|
|
|
2871
|
-
// 1b. 统计 Task
|
|
2872
|
-
|
|
2882
|
+
// 1b. 统计 Task 是否指定了具体文件(含表格内路径引用)
|
|
2883
|
+
// 匹配常见路径前缀:templates/ src/ .qoder/ .claude/ .vscode/ docs/ scripts/ tests/
|
|
2884
|
+
const pathPrefixes = '(?:templates\/|src\/|\\.qoder\/|\\.claude\/|\\.vscode\/|\\.add\/|docs\/|scripts\/|tests\/|prisma\/|queries\/)'
|
|
2885
|
+
const taskFileRefs = planContent.match(new RegExp(pathPrefixes + '[^\\s|)\\n]+', 'g')) || []
|
|
2873
2886
|
const taskCount = (planContent.match(/Task\s+\d+/g) || []).length
|
|
2874
2887
|
// 统计 Task 标题数(用于 Specs 精确度维度三),不含正文中 Task 引用
|
|
2875
2888
|
const taskHeadingCount = (planContent.match(/###\s+Task\s+\d+/g) || []).length
|
|
@@ -3017,130 +3030,35 @@ server.registerTool(
|
|
|
3017
3030
|
parts.push("")
|
|
3018
3031
|
|
|
3019
3032
|
// ====== 维度四:Review 回流完整度(权重 25%) ======
|
|
3020
|
-
//
|
|
3033
|
+
// 策略:数 Review 中 P0/P1 条目数 vs Plan 中 [回流: 标记数,简单计数比对
|
|
3021
3034
|
let backflowScore = 0
|
|
3022
|
-
const backflowDetails: string[] = []
|
|
3023
3035
|
|
|
3024
3036
|
if (reviewContent) {
|
|
3025
|
-
// 解析 Review 中的 P0/P1 问题表格
|
|
3026
|
-
// 典型格式: | # | 缺陷 | 证据 | 修复 |
|
|
3027
|
-
// 其中 P0/P1 标记在行内(如 "(P0)" 或 "3.1" 标题下)
|
|
3028
3037
|
const reviewLines = reviewContent.split("\n")
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
let inProblemTable = false
|
|
3033
|
-
let tableHeaderPassed = false
|
|
3034
|
-
|
|
3035
|
-
for (let i = 0; i < reviewLines.length; i++) {
|
|
3036
|
-
const line = reviewLines[i]
|
|
3037
|
-
|
|
3038
|
-
// 检测 P0/P1 章节
|
|
3039
|
-
if (line.match(/P0|ADD.*合规|阻断/)) {
|
|
3040
|
-
inP0Section = true
|
|
3041
|
-
inP1Section = false
|
|
3042
|
-
inProblemTable = true
|
|
3043
|
-
tableHeaderPassed = false
|
|
3044
|
-
continue
|
|
3045
|
-
}
|
|
3046
|
-
if (line.match(/P1|架构设计.*缺口/)) {
|
|
3047
|
-
inP0Section = false
|
|
3048
|
-
inP1Section = true
|
|
3049
|
-
inProblemTable = true
|
|
3050
|
-
tableHeaderPassed = false
|
|
3051
|
-
continue
|
|
3052
|
-
}
|
|
3053
|
-
if (line.match(/P2|中等|影响评估|决策结论|方案对比/)) {
|
|
3054
|
-
inP0Section = false
|
|
3055
|
-
inP1Section = false
|
|
3056
|
-
inProblemTable = false
|
|
3057
|
-
continue
|
|
3058
|
-
}
|
|
3059
|
-
|
|
3060
|
-
// 表格数据行: | N | 描述 | 证据 | 修复 |
|
|
3061
|
-
if (inProblemTable && line.trim().startsWith("|") && !line.includes("---")) {
|
|
3062
|
-
const cols = line.split("|").map(c => c.trim()).filter(Boolean)
|
|
3063
|
-
if (cols.length >= 4 && cols[0].match(/^\d+/)) {
|
|
3064
|
-
const priority = inP0Section ? "P0" : inP1Section ? "P1" : ""
|
|
3065
|
-
if (priority) {
|
|
3066
|
-
// cols[1] = 缺陷描述, cols[2] = 证据, cols[3] = 修复建议
|
|
3067
|
-
problemItems.push({
|
|
3068
|
-
id: cols[0],
|
|
3069
|
-
description: cols[1] || "",
|
|
3070
|
-
fix: cols[3] || "",
|
|
3071
|
-
priority,
|
|
3072
|
-
})
|
|
3073
|
-
}
|
|
3074
|
-
}
|
|
3075
|
-
}
|
|
3038
|
+
let reviewP0P1Count = 0
|
|
3039
|
+
for (const line of reviewLines) {
|
|
3040
|
+
if (/^\|\s*\d+\s*\|\s*(P0|P1)\s*\|/.test(line)) reviewP0P1Count++
|
|
3076
3041
|
}
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
const mirroredItems: string[] = []
|
|
3088
|
-
|
|
3089
|
-
for (const item of problemItems) {
|
|
3090
|
-
// 从修复建议文本中提取代表关键词
|
|
3091
|
-
const fixKeywords = extractBackflowKeywords(item.fix)
|
|
3092
|
-
const searchTargets = [planContent]
|
|
3093
|
-
if (specContent) searchTargets.push(specContent)
|
|
3094
|
-
|
|
3095
|
-
let found = false
|
|
3096
|
-
for (const kw of fixKeywords) {
|
|
3097
|
-
for (const target of searchTargets) {
|
|
3098
|
-
if (target.toLowerCase().includes(kw.toLowerCase())) {
|
|
3099
|
-
found = true
|
|
3100
|
-
break
|
|
3101
|
-
}
|
|
3102
|
-
}
|
|
3103
|
-
if (found) break
|
|
3104
|
-
}
|
|
3105
|
-
|
|
3106
|
-
if (found) {
|
|
3107
|
-
reflectedCount++
|
|
3108
|
-
mirroredItems.push(` ✅ [${item.priority}] #${item.id}: \"${item.description.slice(0, 50)}\" → Plan/Specs 已修正`)
|
|
3109
|
-
} else {
|
|
3110
|
-
unreflectedItems.push(` ❌ [${item.priority}] #${item.id}: \"${item.description.slice(0, 50)}\" → 修复建议「${item.fix.slice(0, 40)}」未在 Plan/Specs 中找到`)
|
|
3111
|
-
}
|
|
3112
|
-
}
|
|
3113
|
-
|
|
3114
|
-
backflowScore = Math.round((reflectedCount / problemItems.length) * 100)
|
|
3115
|
-
parts.push("=== 维度四:Review 回流完整度(25%)===")
|
|
3116
|
-
parts.push(` P0/P1 问题总数: ${problemItems.length}`)
|
|
3117
|
-
parts.push(` 已回流: ${reflectedCount} / 未回流: ${unreflectedItems.length}`)
|
|
3118
|
-
parts.push(` 回流率: ${backflowScore}%`)
|
|
3119
|
-
parts.push(` 分数: ${backflowScore}/100`)
|
|
3120
|
-
if (mirroredItems.length > 0) {
|
|
3121
|
-
for (const m of mirroredItems.slice(0, 5)) parts.push(m)
|
|
3122
|
-
if (mirroredItems.length > 5) parts.push(` ... 还有 ${mirroredItems.length - 5} 项已回流`)
|
|
3123
|
-
}
|
|
3124
|
-
if (unreflectedItems.length > 0) {
|
|
3125
|
-
for (const u of unreflectedItems) parts.push(u)
|
|
3126
|
-
parts.push(` ⚠️ 修复建议: 执行 0.6.5 卡位,将 Review 未回流项逐条写入 Plan 对应章节`)
|
|
3127
|
-
}
|
|
3128
|
-
for (const d of backflowDetails) parts.push(d)
|
|
3042
|
+
const planBackflowCount = (planContent.match(/\[回流\s*[::]/g) || []).length
|
|
3043
|
+
parts.push("=== 维度四:Review 回流完整度(25%)===")
|
|
3044
|
+
parts.push(" Review P0/P1 条目总数: " + reviewP0P1Count)
|
|
3045
|
+
parts.push(" Plan [回流:] 标记数: " + planBackflowCount)
|
|
3046
|
+
if (reviewP0P1Count === 0) {
|
|
3047
|
+
backflowScore = 100
|
|
3048
|
+
parts.push(" ✅ Review 无 P0/P1 问题,跳过回流检查")
|
|
3049
|
+
} else if (planBackflowCount >= reviewP0P1Count) {
|
|
3050
|
+
backflowScore = 100
|
|
3051
|
+
parts.push(" ✅ 回流完整:" + planBackflowCount + "/" + reviewP0P1Count + " 条全部对应")
|
|
3129
3052
|
} else {
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
parts.push("
|
|
3133
|
-
parts.push(" ⚠️ Review 文件中未检测到 P0/P1 问题表格,无法自动校验回流")
|
|
3134
|
-
parts.push(" ℹ️ 请手动确认 Review 结论是否已写回 Plan")
|
|
3135
|
-
parts.push(` 分数: ${backflowScore}/100(默认值,待人工确认)`)
|
|
3053
|
+
backflowScore = Math.round((planBackflowCount / reviewP0P1Count) * 100)
|
|
3054
|
+
parts.push(" ⚠️ 回流不完整:" + planBackflowCount + "/" + reviewP0P1Count + ",缺 " + (reviewP0P1Count-planBackflowCount) + " 条")
|
|
3055
|
+
parts.push(" ⚠️ 修复建议: 执行 0.6.5 卡位,在 Plan 对应章节添加 [回流: Review PN #N 简述] 标记")
|
|
3136
3056
|
}
|
|
3137
3057
|
} else {
|
|
3138
|
-
// 无 Review 文件
|
|
3139
|
-
backflowScore = 0
|
|
3140
3058
|
parts.push("=== 维度四:Review 回流完整度(25%)===")
|
|
3141
3059
|
parts.push(" ⚠️ Review 文件未找到,回流检查不可用")
|
|
3142
|
-
parts.push(` 分数: ${backflowScore}/100`)
|
|
3143
3060
|
}
|
|
3061
|
+
parts.push(" 分数: " + backflowScore + "/100")
|
|
3144
3062
|
parts.push("")
|
|
3145
3063
|
|
|
3146
3064
|
// ====== DPS 复合计算 ======
|
|
@@ -197,7 +197,14 @@ find_related_docs({ query: "功能关键词" })
|
|
|
197
197
|
- P1 问题:必须在 Plan 中显式响应(接受并修改,或接受并推迟并给出理由)
|
|
198
198
|
- P2 问题:建议响应,非强制但不响应需在 Plan 中标记为 "已知不处理"
|
|
199
199
|
- **不要**简单复制 Review 文字——要把修复建议转成 Plan 的具体文字(新增章节、修改表格、补充步骤)
|
|
200
|
+
- **增量修订(强制)**:回流涉及修改已有内容时(非新增),必须遵循 ADD 文档增量修订规则——旧内容用 `~~删除线~~` 包裹保留,新内容紧跟后以 `→` 引导,末尾标注 `[修订日期: 修订原因]`。**禁止直接删除或覆盖原文**。格式示例:`~~VS Code 端放在轮次 4~~ → VS Code 基础 hooks 并入轮次 1 [2026-07-17 修订: Review P1 #6 实施顺序调整]`。仅新增内容(如新增 Task、新增约束块)无需删除线,但仍需标注修订原因
|
|
200
201
|
- 回流完毕后,Plan 中的内容应与 Review 修复建议一致,但表达方式适应 Plan 的叙述风格
|
|
202
|
+
- **回流标记格式(强制)**:每个回流点必须在 Plan 对应用 `[回流: Review {P0/P1} #{编号} {简述}]` 显式标注,供 DPS 扫描器计数回流完整度。回流标记与增量修订可合并——增量修订的 `修订原因` 段包含 `Review` 关键字即视为有效标记。示例:`[回流: Review P1 #6 实施顺序调整]`。格式说明:
|
|
203
|
+
- `Review` 关键字固定
|
|
204
|
+
- `{P0/P1}` 为 Review 中对应问题的严重度
|
|
205
|
+
- `#{编号}` 为 Review §4.1 P0/P1 问题清单中的编号
|
|
206
|
+
- `{简述}` 为 5-10 字中文简述
|
|
207
|
+
- 回流标记可放置在 Plan 正文任意位置(章节标题、表项说明、约束块等),DPS 通过正则 `/\[回流\s*[::]/g` 计数
|
|
201
208
|
|
|
202
209
|
4. **逐问题写入 Specs**:
|
|
203
210
|
- 如果 Review 发现 Implementation Requirements 层面的缺口(如 `RetrieveBudget` 缺 `perExpertTopK`),在 Spec §Requirements 中新增对应的 WHEN-THEN Scenario
|
|
@@ -298,6 +305,23 @@ Plan 级闭包: {业务功能描述}
|
|
|
298
305
|
- 若某轮次"独立验证"不通过(tsc 这轮过不了)→ 检查是否漏了该轮的前置文件改动
|
|
299
306
|
- 若判定为多轮但 plan 未提供轮次拆分 → 回退 Plan 阶段补充轮次拆分
|
|
300
307
|
|
|
308
|
+
#### 0.7.1 Plan 活跃判定标准(裁决逻辑)
|
|
309
|
+
|
|
310
|
+
> **裁决定位**:"某个 Plan 是否活跃"不是文档约定问题,而是需要从 add-route 的 Step 勾选状态中做确定性判定的裁决逻辑。在 add-coder 项目的 hook 脚本中以 shell 函数实现(`detect_active_add`),暂不依赖 caijuehub 裁决层分发(`npx add-coder init` 不产出 caijuehub)。
|
|
311
|
+
|
|
312
|
+
**活跃判定规则**:
|
|
313
|
+
|
|
314
|
+
1. **判定输入**:指定 magicDir 下的 `plans/` 目录(按当前端优先回退——以 Claude IDE 宿主环境为例:`.claude/plans/` → `.qoder/plans/` → `.add/plans/`,其他 IDE 同理取各自的 magicDir)
|
|
315
|
+
2. **索引优先**:先读 `plans/index.md` 匹配 planKeyword → 无匹配才 glob `*-plan-v*.md`
|
|
316
|
+
3. **活跃定义**:读 add-route 文件 → 存在 `[ ]` 未勾选的 Step 产出项 = 活跃(进行中);全部 `[x]` = 已收敛(不活跃)
|
|
317
|
+
4. **多 Plan 冲突**:多个 Plan 均有 `[ ]` → 取 add-route 文件最近修改时间(`mtime`)最大的 Plan
|
|
318
|
+
5. **无 add-route**:Plan 文件存在但无 add-route → 判定为"Plan 初始态",视为活跃但需要先生成 add-route
|
|
319
|
+
|
|
320
|
+
**应用场景**:
|
|
321
|
+
- SessionStart hook:恢复上下文时定位当前活跃 Plan
|
|
322
|
+
- UserPromptSubmit hook:决定注入"进行中"还是"新启动"的 ADD 状态
|
|
323
|
+
- PreToolUse hook:文件写入前置守卫——写入 plans/specs 时检查是否有活跃 Plan
|
|
324
|
+
|
|
301
325
|
### 第一阶段产出检查
|
|
302
326
|
|
|
303
327
|
- [ ] 已调用 `find_related_docs` 搜索相关文档
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# context-inject.sh — 上下文注入模板
|
|
3
|
-
# 共享库
|
|
4
|
-
|
|
5
|
-
# 结构化 SessionStart JSON 注入
|
|
6
|
-
build_session_start_json() {
|
|
7
|
-
local plan="$1" step="$2" round="$3" handoff="$4"
|
|
8
|
-
cat <<EOF
|
|
9
|
-
{
|
|
10
|
-
"continue": true,
|
|
11
|
-
"hookSpecificOutput": {
|
|
12
|
-
"hookEventName": "SessionStart",
|
|
13
|
-
"additionalContext": "上次 ADD 流程未完成:\n Plan: ${plan}\n 轮次: ${round}\n 当前 Step: ${step} (add-route)\n 恢复命令: query_audit_logs({ planKeyword: '${plan}' })\n handoff: ${handoff}"
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
EOF
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
# Stop 四象限分流 few-shot 上下文
|
|
20
|
-
build_stop_context() {
|
|
21
|
-
local quadrant="$1" info="$2"
|
|
22
|
-
case "$quadrant" in
|
|
23
|
-
"no_add_no_dev")
|
|
24
|
-
echo "[ADD Stop] 无活跃 Plan,无代码改动。正常结束。"
|
|
25
|
-
;;
|
|
26
|
-
"no_add_has_dev")
|
|
27
|
-
cat <<'EOF'
|
|
28
|
-
[ADD Stop] ⚠️ 检测到代码修改但无活跃 ADD Plan。
|
|
29
|
-
|
|
30
|
-
Plan 不是"文档开销"——它是代码治理的基础设斻。跳过 Plan 的后果:
|
|
31
|
-
· 没有 add-route → 每次改动无法追溯到具体 Task
|
|
32
|
-
· 没有 tasks.md → 后续 AI Session 不知道改了哪些文件
|
|
33
|
-
· 没有 handoff → 交接时上下文全丢,只能靠 git log 猜
|
|
34
|
-
|
|
35
|
-
你必须立即补救,二选一:
|
|
36
|
-
|
|
37
|
-
方案 A — 补 ADD 流程(招安):
|
|
38
|
-
Step 0: 读 .qoder/templates/plan-template.md → 生成 Plan → .qoder/plans/{today}/{keyword}-plan-v1.md
|
|
39
|
-
生成 add-route → check_dps ≥ 85
|
|
40
|
-
Step 1: 扩展 AgentAuditPhase(如需要)
|
|
41
|
-
Step 2: 确认 agentAudit() 通道
|
|
42
|
-
Step 3: 将已写代码关联到 tasks.md
|
|
43
|
-
完成后可正常停止。
|
|
44
|
-
|
|
45
|
-
方案 B — 补不上则回滚:
|
|
46
|
-
如果改动太复杂无法追溯生成 Plan,则:
|
|
47
|
-
① git diff 确认改动范围
|
|
48
|
-
② git checkout -- <改动的文件> 撤回
|
|
49
|
-
③ 告知用户 "以下改动已回滚,请先走 ADD 流程再重新实施: <文件列表>"
|
|
50
|
-
|
|
51
|
-
无论选 A 还是 B,完成后告诉用户下次执行 session-init 恢复上下文。
|
|
52
|
-
EOF
|
|
53
|
-
;;
|
|
54
|
-
"has_add_no_dev")
|
|
55
|
-
echo "[ADD Stop] ADD 流程进行中 (${info}),本次无代码改动。下次继续时执行 session-init 恢复上下文。"
|
|
56
|
-
;;
|
|
57
|
-
"has_add_dev_step02")
|
|
58
|
-
echo "[ADD Stop] ADD Step 0-2: 文档先行/审计准备阶段。无需验收闭环。下一步: 进入 Step 3 代码实现。"
|
|
59
|
-
;;
|
|
60
|
-
"has_add_dev_step3")
|
|
61
|
-
echo "[ADD Stop] ADD Step 3: 代码实现进行中 (${info})。完成后进入 Step 3.5 实现审查。"
|
|
62
|
-
;;
|
|
63
|
-
"has_add_dev_unclosed")
|
|
64
|
-
cat <<'EOF'
|
|
65
|
-
[ADD Stop] ⚠️ 代码已完成但验收未闭环:
|
|
66
|
-
${info}
|
|
67
|
-
|
|
68
|
-
请依次执行(不要等下次会话):
|
|
69
|
-
① Write devlog → handoff 同目录 devlog-{plan}-v{n}.md
|
|
70
|
-
格式: # Devlog: {plan}\n 日期 / Plan / 轮次 / 本轮改了什么 / 验收结果 / 遗留项 / 架构回看
|
|
71
|
-
② Edit handoff → 更新 §验证标准 全部 [x] + 补充审计 ID
|
|
72
|
-
★ 同步: checklist 有新 cuid → handoff ADD-7 表必须对应新增行
|
|
73
|
-
★ Step 0 准入: handoff + add-route + Specs 三元组缺一不可,缺则回退 Step 0.5
|
|
74
|
-
③ Read docs/ → 回看架构文档确认一致性
|
|
75
|
-
④ Edit add-route → 勾选对应 Step [x]
|
|
76
|
-
|
|
77
|
-
以上全部完成后 Agent 才能停止。
|
|
78
|
-
|
|
79
|
-
下次恢复: 读 handoff → 查同目录 devlog-*.md → query_audit_logs
|
|
80
|
-
EOF
|
|
81
|
-
;;
|
|
82
|
-
"has_add_dev_closed")
|
|
83
|
-
echo "[ADD Stop] ✅ 验收闭环: add-route全部[x], devlog已记录, handoff已更新。验收幂等——重复触发不覆盖已有结论。"
|
|
84
|
-
;;
|
|
85
|
-
esac
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
# 写操作前置守卫上下文
|
|
89
|
-
build_pretool_context() {
|
|
90
|
-
local plan="$1" round="$2"
|
|
91
|
-
cat <<EOF
|
|
92
|
-
[ADD PreToolUse] 当前 Plan: ${plan},轮次: ${round}。
|
|
93
|
-
本次写入应属于 ADD Step 3 代码实现阶段。
|
|
94
|
-
完成后执行 record_dev_operation 记录审计。
|
|
95
|
-
EOF
|
|
96
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# state-detect.sh — ADD 活跃流程检测 + dev action 追踪
|
|
3
|
-
# 共享库
|
|
4
|
-
|
|
5
|
-
PROJECT_DIR="${QODER_PROJECT_DIR:-${QODERCN_PROJECT_DIR:-$PWD}}"
|
|
6
|
-
QODER_DIR="$PROJECT_DIR/.qoder"
|
|
7
|
-
PLANS_DIR="$QODER_DIR/plans"
|
|
8
|
-
|
|
9
|
-
# dev action 标记文件(项目级,PreToolUse 写入,Stop 读取)
|
|
10
|
-
DEV_FLAG="/tmp/qoder_dev_$(echo "$PROJECT_DIR" | md5sum 2>/dev/null | cut -c1-8 || echo "default")"
|
|
11
|
-
|
|
12
|
-
# 检测活跃 ADD 流程
|
|
13
|
-
# 返回: "plan_keyword::step_x/total::round_n/total_r::handoff_path::add_route_path" 或 ""
|
|
14
|
-
detect_active_add() {
|
|
15
|
-
local handoff=""
|
|
16
|
-
local today_dir="$PLANS_DIR/$(date +%Y-%m)"
|
|
17
|
-
[ -d "$today_dir" ] && handoff=$(find "$today_dir" -name "*handoff*.md" -mtime -7 -type f 2>/dev/null | head -1)
|
|
18
|
-
[ -z "$handoff" ] && handoff=$(find "$PLANS_DIR" -name "*handoff*.md" -mtime -7 -type f 2>/dev/null | head -1)
|
|
19
|
-
[ -z "$handoff" ] && return 1
|
|
20
|
-
|
|
21
|
-
local plan_kw=$(basename "$handoff" | sed 's/-handoff.*//')
|
|
22
|
-
|
|
23
|
-
# add-route
|
|
24
|
-
local add_route=$(find "$PLANS_DIR" -name "*add-route*.md" -type f 2>/dev/null | head -1)
|
|
25
|
-
|
|
26
|
-
# Step 统计(从 add-route)
|
|
27
|
-
local step_info="?"
|
|
28
|
-
if [ -f "$add_route" ]; then
|
|
29
|
-
local ck=$(grep -c '\[x\]' "$add_route" 2>/dev/null || true)
|
|
30
|
-
local uc=$(grep -c '\[ \]' "$add_route" 2>/dev/null || true)
|
|
31
|
-
ck=${ck:-0}; uc=${uc:-0}
|
|
32
|
-
local total=$((ck + uc))
|
|
33
|
-
[ "$total" -gt 0 ] && step_info="${ck}/${total}"
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
# 轮次统计(从 handoff)
|
|
37
|
-
local round_info="?"
|
|
38
|
-
if grep -q '<第[0-9]轮>' "$handoff" 2>/dev/null; then
|
|
39
|
-
local ck=$(grep -c '\[x\]' "$handoff" 2>/dev/null || true); ck=${ck:-0}
|
|
40
|
-
local total=$(grep -c '<第[0-9]轮>' "$handoff" 2>/dev/null || true); total=${total:-0}
|
|
41
|
-
[ "$total" -gt 0 ] && round_info="${ck}/${total}"
|
|
42
|
-
else
|
|
43
|
-
round_info="1/1"
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
echo "${plan_kw}::${step_info}::${round_info}::${handoff}::${add_route:-none}"
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
# 标记 dev action
|
|
50
|
-
mark_dev_action() {
|
|
51
|
-
touch "$DEV_FLAG" 2>/dev/null || true
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
# 检测 dev action
|
|
55
|
-
has_dev_action() {
|
|
56
|
-
[ -f "$DEV_FLAG" ]
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
# 清理 dev action 标记(验收闭环后调用)
|
|
60
|
-
clear_dev_action() {
|
|
61
|
-
rm -f "$DEV_FLAG" 2>/dev/null || true
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
# 检查验收完整度,返回 issues 文本
|
|
65
|
-
check_add_completeness() {
|
|
66
|
-
local handoff="$1" add_route="$2"
|
|
67
|
-
local issues=""
|
|
68
|
-
|
|
69
|
-
# devlog(内容已回流至 handoff,检查 handoff 是否含验收结果)
|
|
70
|
-
if [ -f "$handoff" ] && ! grep -qE '验收|收敛|闭环|本轮改了什么|devlog' "$handoff" 2>/dev/null; then
|
|
71
|
-
issues="${issues} [ ] devlog 缺失(handoff 无验收记录)\n"
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
# handoff 验证标准
|
|
75
|
-
if [ -f "$handoff" ]; then
|
|
76
|
-
local uc=$(grep -c '\[ \]' "$handoff" 2>/dev/null || echo "0")
|
|
77
|
-
[ "$uc" -gt 0 ] && issues="${issues} [ ] handoff ${uc} 项未勾选\n"
|
|
78
|
-
fi
|
|
79
|
-
|
|
80
|
-
# add-route Step
|
|
81
|
-
if [ -f "$add_route" ]; then
|
|
82
|
-
local uc=$(grep -c '\[ \]' "$add_route" 2>/dev/null || echo "0")
|
|
83
|
-
[ "$uc" -gt 0 ] && issues="${issues} [ ] add-route ${uc} Step 未闭环\n"
|
|
84
|
-
fi
|
|
85
|
-
|
|
86
|
-
echo -e "$issues"
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
# 检查是否已验收(幂等保护)
|
|
90
|
-
# 返回 0 = 未验收,1 = 已验收
|
|
91
|
-
is_already_accepted() {
|
|
92
|
-
local add_route="$1" handoff="$2"
|
|
93
|
-
# add-route Step 8 是否已 [x]
|
|
94
|
-
if [ -f "$add_route" ]; then
|
|
95
|
-
# 检查 Step 8 产出项是否勾选
|
|
96
|
-
if grep -A 10 'Step 8' "$add_route" 2>/dev/null | grep -q '\[x\].*验证并更新项目状态'; then
|
|
97
|
-
# handoff 是否有验收记录
|
|
98
|
-
if [ -f "$handoff" ] && grep -qE '✅.*验收|收敛|全部闭环|全部.*完成' "$handoff" 2>/dev/null; then
|
|
99
|
-
return 0
|
|
100
|
-
fi
|
|
101
|
-
fi
|
|
102
|
-
fi
|
|
103
|
-
return 1
|
|
104
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# vocabulary.sh — 从 vocabulary markdown 表格加载触发词
|
|
3
|
-
# 单一数据源: .qoder/vocabulary/add-governance-vocabulary.md §类别 A-F 表格
|
|
4
|
-
|
|
5
|
-
VOCABULARY_FILE="$PWD/.qoder/vocabulary/add-governance-vocabulary.md"
|
|
6
|
-
|
|
7
|
-
# 输出格式: 优先级::触发词正则::响应文本(:: 避免与触发词内的 | 冲突)
|
|
8
|
-
load_triggers() {
|
|
9
|
-
[ ! -f "$VOCABULARY_FILE" ] && return 1
|
|
10
|
-
sed -n '/^## 类别 A: 文档类型/,/^## 类别 [G-Z]/p' "$VOCABULARY_FILE" \
|
|
11
|
-
| grep -E '^\| (P0|P1|P2) ' \
|
|
12
|
-
| while IFS='|' read -r _ prio raw_trigger action; do
|
|
13
|
-
prio=$(echo "$prio" | xargs)
|
|
14
|
-
trigger=$(echo "$raw_trigger" | sed 's/`//g;s/ *\/ */|/g;s/^ *//;s/ *$//')
|
|
15
|
-
action=$(echo "$action" | xargs)
|
|
16
|
-
[ -z "$trigger" ] && continue
|
|
17
|
-
echo "${prio}::${trigger}::${action}"
|
|
18
|
-
done || true
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
match_trigger() {
|
|
22
|
-
local prompt="$1"
|
|
23
|
-
while IFS= read -r line; do
|
|
24
|
-
[ -z "$line" ] && continue
|
|
25
|
-
local prio="${line%%::*}"
|
|
26
|
-
local rest="${line#*::}"
|
|
27
|
-
local regex="${rest%%::*}"
|
|
28
|
-
local action="${rest#*::}"
|
|
29
|
-
# 跳过开发关键词检测行(由 Layer 2/3 分流处理,特征:超长regex含"修bug")
|
|
30
|
-
if echo "$regex" | grep -qE '修\.\?bug|fix\.\?bug' 2>/dev/null; then
|
|
31
|
-
continue
|
|
32
|
-
fi
|
|
33
|
-
if echo "$prompt" | grep -qiE "$regex" 2>/dev/null; then
|
|
34
|
-
echo "[ADD 触发] ${regex} → ${action}"
|
|
35
|
-
fi
|
|
36
|
-
done < <(load_triggers 2>/dev/null)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
load_dev_keywords() {
|
|
40
|
-
load_triggers 2>/dev/null | while IFS= read -r line; do
|
|
41
|
-
[ -z "$line" ] && continue
|
|
42
|
-
# 只取包含"开发|改功能"的开发关键词检测行
|
|
43
|
-
if echo "$line" | grep -q '开发|改功能' 2>/dev/null; then
|
|
44
|
-
local rest="${line#*::}"
|
|
45
|
-
local regex="${rest%%::*}"
|
|
46
|
-
echo "$regex"
|
|
47
|
-
fi
|
|
48
|
-
done || true
|
|
49
|
-
}
|