@kunlv/atdc 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.
Files changed (222) hide show
  1. package/AGENTS.md +58 -0
  2. package/GO.md +245 -0
  3. package/bin/add-command.py +144 -0
  4. package/bin/atdc.js +460 -0
  5. package/bin/cls.js +98 -0
  6. package/central/link.sh +60 -0
  7. package/central/ticket_center_server/.handoffs/TASK-238389_QYinVipLimit.md +128 -0
  8. package/central/ticket_center_server/.test_plans/TASK-227254-SupportMembership-self-test-plan.md +566 -0
  9. package/central/ticket_center_server/.test_plans/task-238389-qyin-vip-limit-self-test-plan.md +210 -0
  10. package/commands/claude/atd.md +3 -0
  11. package/commands/claude/atdc/1-spec.md +5 -0
  12. package/commands/claude/atdc/1-update.md +5 -0
  13. package/commands/claude/atdc/2-qa.md +3 -0
  14. package/commands/claude/atdc/3-impl-skip-spec.md +5 -0
  15. package/commands/claude/atdc/3-impl.md +3 -0
  16. package/commands/claude/atdc/4-verify.md +3 -0
  17. package/commands/claude/atdc/5-done.md +3 -0
  18. package/commands/claude/atdc/add-command.md +4 -0
  19. package/commands/claude/atdc/add-to-hc.md +3 -0
  20. package/commands/claude/atdc/analysis-cls-log.md +4 -0
  21. package/commands/claude/atdc/article.md +6 -0
  22. package/commands/claude/atdc/bugfix.md +4 -0
  23. package/commands/claude/atdc/code-simplifier.md +8 -0
  24. package/commands/claude/atdc/commit.md +4 -0
  25. package/commands/claude/atdc/compound.md +5 -0
  26. package/commands/claude/atdc/context.md +3 -0
  27. package/commands/claude/atdc/diagnose.md +8 -0
  28. package/commands/claude/atdc/git-guardrails.md +18 -0
  29. package/commands/claude/atdc/grill-with-docs.md +8 -0
  30. package/commands/claude/atdc/handoff.md +8 -0
  31. package/commands/claude/atdc/health.md +3 -0
  32. package/commands/claude/atdc/learn.md +4 -0
  33. package/commands/claude/atdc/review.md +10 -0
  34. package/commands/claude/atdc/search.md +4 -0
  35. package/commands/claude/atdc/strategy.md +3 -0
  36. package/commands/claude/atdc/table2xlsx.md +4 -0
  37. package/commands/claude/atdc/test-plan.md +5 -0
  38. package/commands/claude/atdc/travel-planner.md +4 -0
  39. package/commands/claude/atdc/xlsx2sql-insert.md +4 -0
  40. package/commands/claude/atdc/xlsx2sql-update.md +4 -0
  41. package/commands/claude/atdc/zoom-out.md +8 -0
  42. package/commands/codex/atdc/SKILL.md +8 -0
  43. package/commands/codex/atdc-1-spec/SKILL.md +8 -0
  44. package/commands/codex/atdc-1-update/SKILL.md +8 -0
  45. package/commands/codex/atdc-2-qa/SKILL.md +8 -0
  46. package/commands/codex/atdc-3-impl/SKILL.md +8 -0
  47. package/commands/codex/atdc-3-impl-skip-spec/SKILL.md +8 -0
  48. package/commands/codex/atdc-4-verify/SKILL.md +8 -0
  49. package/commands/codex/atdc-5-done/SKILL.md +8 -0
  50. package/commands/codex/atdc-add-command/SKILL.md +7 -0
  51. package/commands/codex/atdc-add-to-hc/SKILL.md +8 -0
  52. package/commands/codex/atdc-analysis-cls-log/SKILL.md +5 -0
  53. package/commands/codex/atdc-article/SKILL.md +8 -0
  54. package/commands/codex/atdc-bugfix/SKILL.md +5 -0
  55. package/commands/codex/atdc-code-simplifier/SKILL.md +7 -0
  56. package/commands/codex/atdc-commit/SKILL.md +7 -0
  57. package/commands/codex/atdc-compound/SKILL.md +8 -0
  58. package/commands/codex/atdc-context/SKILL.md +8 -0
  59. package/commands/codex/atdc-diagnose/SKILL.md +9 -0
  60. package/commands/codex/atdc-git-guardrails/SKILL.md +19 -0
  61. package/commands/codex/atdc-grill-with-docs/SKILL.md +9 -0
  62. package/commands/codex/atdc-handoff/SKILL.md +9 -0
  63. package/commands/codex/atdc-health/SKILL.md +8 -0
  64. package/commands/codex/atdc-learn/SKILL.md +7 -0
  65. package/commands/codex/atdc-review/SKILL.md +49 -0
  66. package/commands/codex/atdc-search/SKILL.md +5 -0
  67. package/commands/codex/atdc-strategy/SKILL.md +8 -0
  68. package/commands/codex/atdc-table2xlsx/SKILL.md +8 -0
  69. package/commands/codex/atdc-test-plan/SKILL.md +13 -0
  70. package/commands/codex/atdc-travel-planner/SKILL.md +7 -0
  71. package/commands/codex/atdc-xlsx2sql-insert/SKILL.md +8 -0
  72. package/commands/codex/atdc-xlsx2sql-update/SKILL.md +8 -0
  73. package/commands/codex/atdc-zoom-out/SKILL.md +9 -0
  74. package/commands/cursor/atdc/SKILL.md +8 -0
  75. package/commands/cursor/atdc-1-spec/SKILL.md +8 -0
  76. package/commands/cursor/atdc-1-update/SKILL.md +8 -0
  77. package/commands/cursor/atdc-2-qa/SKILL.md +8 -0
  78. package/commands/cursor/atdc-3-impl/SKILL.md +8 -0
  79. package/commands/cursor/atdc-3-impl-skip-spec/SKILL.md +8 -0
  80. package/commands/cursor/atdc-4-verify/SKILL.md +8 -0
  81. package/commands/cursor/atdc-5-done/SKILL.md +8 -0
  82. package/commands/cursor/atdc-add-command/SKILL.md +7 -0
  83. package/commands/cursor/atdc-add-to-hc/SKILL.md +8 -0
  84. package/commands/cursor/atdc-analysis-cls-log/SKILL.md +5 -0
  85. package/commands/cursor/atdc-article/SKILL.md +8 -0
  86. package/commands/cursor/atdc-bugfix/SKILL.md +5 -0
  87. package/commands/cursor/atdc-code-simplifier/SKILL.md +7 -0
  88. package/commands/cursor/atdc-commit/SKILL.md +7 -0
  89. package/commands/cursor/atdc-compound/SKILL.md +8 -0
  90. package/commands/cursor/atdc-context/SKILL.md +8 -0
  91. package/commands/cursor/atdc-diagnose/SKILL.md +8 -0
  92. package/commands/cursor/atdc-git-guardrails/SKILL.md +18 -0
  93. package/commands/cursor/atdc-grill-with-docs/SKILL.md +8 -0
  94. package/commands/cursor/atdc-handoff/SKILL.md +8 -0
  95. package/commands/cursor/atdc-health/SKILL.md +8 -0
  96. package/commands/cursor/atdc-learn/SKILL.md +7 -0
  97. package/commands/cursor/atdc-review/SKILL.md +49 -0
  98. package/commands/cursor/atdc-search/SKILL.md +5 -0
  99. package/commands/cursor/atdc-strategy/SKILL.md +8 -0
  100. package/commands/cursor/atdc-table2xlsx/SKILL.md +8 -0
  101. package/commands/cursor/atdc-test-plan/SKILL.md +7 -0
  102. package/commands/cursor/atdc-travel-planner/SKILL.md +7 -0
  103. package/commands/cursor/atdc-xlsx2sql-insert/SKILL.md +8 -0
  104. package/commands/cursor/atdc-xlsx2sql-update/SKILL.md +8 -0
  105. package/commands/cursor/atdc-zoom-out/SKILL.md +8 -0
  106. package/commands/opencode/atd.md +6 -0
  107. package/commands/opencode/atdc/1-spec.md +8 -0
  108. package/commands/opencode/atdc/1-update.md +8 -0
  109. package/commands/opencode/atdc/2-qa.md +6 -0
  110. package/commands/opencode/atdc/3-impl-skip-spec.md +8 -0
  111. package/commands/opencode/atdc/3-impl.md +6 -0
  112. package/commands/opencode/atdc/4-verify.md +6 -0
  113. package/commands/opencode/atdc/5-done.md +6 -0
  114. package/commands/opencode/atdc/add-command.md +6 -0
  115. package/commands/opencode/atdc/add-to-hc.md +6 -0
  116. package/commands/opencode/atdc/analysis-cls-log.md +6 -0
  117. package/commands/opencode/atdc/article.md +6 -0
  118. package/commands/opencode/atdc/bugfix.md +6 -0
  119. package/commands/opencode/atdc/code-simplifier.md +8 -0
  120. package/commands/opencode/atdc/commit.md +6 -0
  121. package/commands/opencode/atdc/compound.md +8 -0
  122. package/commands/opencode/atdc/context.md +6 -0
  123. package/commands/opencode/atdc/diagnose.md +8 -0
  124. package/commands/opencode/atdc/git-guardrails.md +18 -0
  125. package/commands/opencode/atdc/grill-with-docs.md +8 -0
  126. package/commands/opencode/atdc/handoff.md +8 -0
  127. package/commands/opencode/atdc/health.md +6 -0
  128. package/commands/opencode/atdc/learn.md +6 -0
  129. package/commands/opencode/atdc/review.md +7 -0
  130. package/commands/opencode/atdc/search.md +6 -0
  131. package/commands/opencode/atdc/strategy.md +6 -0
  132. package/commands/opencode/atdc/table2xlsx.md +6 -0
  133. package/commands/opencode/atdc/test-plan.md +6 -0
  134. package/commands/opencode/atdc/travel-planner.md +6 -0
  135. package/commands/opencode/atdc/xlsx2sql-insert.md +6 -0
  136. package/commands/opencode/atdc/xlsx2sql-update.md +6 -0
  137. package/commands/opencode/atdc/zoom-out.md +8 -0
  138. package/config/cls-defaults.json +19 -0
  139. package/hooks/atd-archive.sh +86 -0
  140. package/hooks/atd-state.sh +165 -0
  141. package/hooks/evidence-hook.sh +88 -0
  142. package/hooks/evidence-log.sh +54 -0
  143. package/hooks/guardrails.sh +65 -0
  144. package/hooks/handoff-compress.sh +104 -0
  145. package/hooks/phase-guard.sh +134 -0
  146. package/hooks/phase-write-guard.sh +96 -0
  147. package/hooks/ponytail-fix.sh +179 -0
  148. package/hooks/ponytail-guard.sh +66 -0
  149. package/hooks/ponytail-scan.sh +62 -0
  150. package/install.sh +276 -0
  151. package/package.json +20 -0
  152. package/packages/cls/bin/cls.js +146 -0
  153. package/packages/cls/config/defaults.json +17 -0
  154. package/packages/cls/mcp/server.js +142 -0
  155. package/packages/cls/package.json +29 -0
  156. package/packages/cls/scripts/postinstall.js +19 -0
  157. package/packages/cls/src/client.js +262 -0
  158. package/prompts/1-spec.md +271 -0
  159. package/prompts/1-update.md +198 -0
  160. package/prompts/2-qa.md +164 -0
  161. package/prompts/3-impl-bugfix.md +271 -0
  162. package/prompts/3-impl.md +419 -0
  163. package/prompts/4-verify.md +160 -0
  164. package/prompts/5-done.md +227 -0
  165. package/prompts/6-article.md +571 -0
  166. package/prompts/X-add-command.md +60 -0
  167. package/prompts/X-add-to-hc.md +178 -0
  168. package/prompts/X-analysis-cls-log.md +68 -0
  169. package/prompts/X-bugfix.md +10 -0
  170. package/prompts/X-code-simplifier.md +38 -0
  171. package/prompts/X-commit.md +116 -0
  172. package/prompts/X-compound.md +188 -0
  173. package/prompts/X-context-scan.md +101 -0
  174. package/prompts/X-diagnose.md +116 -0
  175. package/prompts/X-grill-with-docs.md +120 -0
  176. package/prompts/X-handoff.md +80 -0
  177. package/prompts/X-health.md +78 -0
  178. package/prompts/X-learn.md +151 -0
  179. package/prompts/X-review.md +417 -0
  180. package/prompts/X-search.md +74 -0
  181. package/prompts/X-strategy.md +95 -0
  182. package/prompts/X-table2xlsx.md +46 -0
  183. package/prompts/X-test-plan.md +436 -0
  184. package/prompts/X-travel-planner.md +22 -0
  185. package/prompts/X-xlsx2sql-insert.md +34 -0
  186. package/prompts/X-xlsx2sql-update.md +40 -0
  187. package/prompts/X-zoom-out.md +119 -0
  188. package/reference/diagnose-loop.md +66 -0
  189. package/reference/gates.md +90 -0
  190. package/reference/git-guardrails.md +71 -0
  191. package/reference/guards.md +275 -0
  192. package/reference/learn.md +100 -0
  193. package/reference/memory.md +190 -0
  194. package/reference/risk.md +74 -0
  195. package/reference/tdd.md +40 -0
  196. package/scripts/svg2png.js +46 -0
  197. package/src/cls/client.js +244 -0
  198. package/src/constants.js +49 -0
  199. package/src/evidence/ai-draft-metrics.js +157 -0
  200. package/src/evidence/canvas.js +184 -0
  201. package/src/evidence/index.js +98 -0
  202. package/src/evidence/sink-events.js +165 -0
  203. package/src/evidence/stage-timing.js +112 -0
  204. package/src/installers/claude.js +79 -0
  205. package/src/installers/codex.js +62 -0
  206. package/src/installers/opencode.js +72 -0
  207. package/src/manifest/ponytail-patterns.json +185 -0
  208. package/src/mcp/cls-mcp-server.js +142 -0
  209. package/src/utils.js +79 -0
  210. package/src/validators/state-validate.js +123 -0
  211. package/templates/.phase-state.json +13 -0
  212. package/templates/adr.md +62 -0
  213. package/templates/changelog.md +21 -0
  214. package/templates/concepts.md +35 -0
  215. package/templates/design.md +47 -0
  216. package/templates/handoff.md +64 -0
  217. package/templates/learn-skill.md +43 -0
  218. package/templates/progress.json +17 -0
  219. package/templates/proposal.md +29 -0
  220. package/templates/solution.md +36 -0
  221. package/templates/strategy.md +39 -0
  222. package/templates/tasks.md +41 -0
@@ -0,0 +1,190 @@
1
+ # reference/memory · 跨 session 记忆协议
2
+
3
+ ---
4
+
5
+ ## 渐进式披露(核心原则)
6
+
7
+ > 借鉴 claude-mem 的 Progressive Disclosure:索引先行,按需拉取详情。
8
+
9
+ **读策略**:
10
+ 1. **Session 启动** → 仅注入索引(~400 token),展示日期 + 类型图标 + change 名称
11
+ 2. **按需拉取** → agent 看到相关标题后用 Read 工具读取具体条目(~100-200 token/条)
12
+ 3. **优先拉取** → 🔴 bugfix / ⚠️ gotcha / 🟤 decision / ⚖️ tradeoff(高信号类型)
13
+ 4. **跳过无关** → 与当前任务无关的条目留在文件中,不进入 context
14
+
15
+ **vs 旧方案**:cat 全量 .memory.md(~2000+ token)→ grep 索引(~400 token),token 效率提升 5x+。
16
+
17
+ ---
18
+
19
+ ## 记忆文件位置
20
+
21
+ ```
22
+ ~/.atd-code/memory/<项目目录名>/
23
+ ├── .memory.md ← 决策日志(最近 20 条)
24
+ ├── .memory-archive.md ← 超出 20 条后的归档
25
+ └── .errors-log.md ← 失败知识库
26
+
27
+ <项目根目录>/
28
+ ├── STRATEGY.md ← 产品策略锚点(/atd strategy 维护)
29
+ ├── CONCEPTS.md ← 领域词汇表(/atd compound concepts 维护)
30
+ └── docs/solutions/ ← 结构化知识沉淀(YAML frontmatter 可搜索)
31
+ └── <topic>.md ← 每个已解决的复杂问题一个文件
32
+ ```
33
+
34
+ 项目目录名 = `basename` of 主 worktree 路径(worktree 场景下与 `basename $PWD` 不同)
35
+
36
+ ---
37
+
38
+ ## 类型图标对照
39
+
40
+ | 图标 | 类型 | 说明 | 拉取优先级 |
41
+ |--------|------|------|-------------|
42
+ | 🔴 | bugfix | 修 bug / 修复问题 | 🔥 高 |
43
+ | ⚠️ | gotcha | 发现坑 / 边界场景 | 🔥 高 |
44
+ | 🟤 | decision | 架构决策(默认类型) | 🔥 高 |
45
+ | ⚖️ | tradeoff | 明确取舍 / 方案对比 | 🔥 高 |
46
+ | 🟢 | feature | 新功能 | 中 |
47
+ | 🟡 | refactor | 重构 | 中 |
48
+ | 🟣 | discovery | 发现 / 学习 / 洞察 | 中 |
49
+ | 🧠 | learned-skill | auto-harvested golden path(self-learning 自动沉淀) | 🔥 高 |
50
+
51
+ ---
52
+
53
+ ## 何时写入
54
+
55
+ - **归档完成后立即写**(不等 session 结束)
56
+ - Session 结束前兜底检查,有未记录内容则补写
57
+
58
+ **格式(caveman 压缩,控制 5-8 行)**:
59
+
60
+ ```markdown
61
+ ## 🟤 [YYYY-MM-DD] <change-name> | ~150t
62
+ done: <一句话描述完成的内容>
63
+ decided: <核心技术决策,关键词即可>
64
+ rejected: <排除的方案及原因,无则删此行>
65
+ open: <未解决假设,无则删此行>
66
+
67
+ ### 🧠 learned-skill 格式
68
+
69
+ ```markdown
70
+ ## 🧠 [YYYY-MM-DD] <skill-name> | ~150t
71
+ done: <一句话描述学到的流程>
72
+ decided: <核心操作步骤,关键词>
73
+ failure: <这个 skill 避免的失败模式>
74
+ dead-end: <被排除的死路>
75
+ ```
76
+ ```
77
+
78
+ **规则**:
79
+ - Header 前缀 emoji 从类型对照表选择,默认 🟤 decision
80
+ - `~150t` 为 token 估算(~100-200,按内容行数估算)
81
+ - `rejected:` 和 `open:` 如无内容则删除整行,不留空行
82
+ - 总行数 5-8 行(含 header)
83
+ - `next:` 字段不再使用(由索引 + Read 按需拉取替代)
84
+
85
+ ---
86
+
87
+ ## 何时读取
88
+
89
+ - **GO.md Step 0a**:注入索引(grep headers),不读全量
90
+ - **1-spec.md**:提案前扫索引,相关条目用 Read 拉取详情
91
+ - **按需**:执行中遇到相关问题时,Read 拉取具体条目
92
+
93
+ ---
94
+
95
+ ## 索引生成命令
96
+
97
+ session 启动时执行:
98
+
99
+ ```bash
100
+ _PROJ=$(basename "$(git worktree list 2>/dev/null | head -1 | awk '{print $1}')" 2>/dev/null || basename "$PWD")
101
+ MEMFILE="$HOME/.atd-code/memory/$_PROJ/.memory.md"
102
+
103
+ if [ -f "$MEMFILE" ]; then
104
+ COUNT=$(grep -c "^## " "$MEMFILE" 2>/dev/null || echo 0)
105
+ echo "## 📋 项目记忆索引(共 $COUNT 条,显示最近 20)"
106
+ echo ""
107
+ grep "^## " "$MEMFILE" | tail -20 | awk '{
108
+ icon = substr($0, 4, 3)
109
+ rest = substr($0, 7)
110
+ printf "| %s | %s\n", icon, rest
111
+ }'
112
+ echo ""
113
+ echo "💡 渐进式披露:用 Read 工具读取具体条目(~100-200 token/条)。"
114
+ echo " 优先拉取:🔴 bugfix ⚠️ gotcha 🟤 decision ⚖️ tradeoff"
115
+ else
116
+ echo "[无历史记忆]"
117
+ fi
118
+ ```
119
+
120
+ 输出示例:
121
+ ```
122
+ ## 📋 项目记忆索引(共 9 条,显示最近 9)
123
+
124
+ | 🟤 | [2026-06-01] perf-list-singleflight | ~150t
125
+ | 🟢 | [2026-06-01] kugou-embedded-h5 | ~120t
126
+ | 🔴 | [2026-06-01] fixQYinMsgNotSend + subscribe-appid-aware-push-schema | ~160t
127
+ | 🟡 | [2026-06-09] simplify-ad-auto | ~130t
128
+
129
+ 💡 渐进式披露:用 Read 工具读取具体条目(~100-200 token/条)。
130
+ 优先拉取:🔴 bugfix ⚠️ gotcha 🟤 decision ⚖️ tradeoff
131
+ ```
132
+
133
+ ---
134
+
135
+ ## 自动裁剪
136
+
137
+ `.memory.md` 保留最近 **20 条**(以 `## ` 计数)。
138
+
139
+ 超出时将最旧的条目移到 `.memory-archive.md`。
140
+
141
+ ---
142
+
143
+ ## 失败知识库(.errors-log.md)
144
+
145
+ 超过 2 次尝试才解决的问题,必须记录:
146
+
147
+ ```markdown
148
+ ## [YYYY-MM-DD] <change-name> / Task N
149
+
150
+ - **失败路径**: <尝试了什么,为什么不 work>
151
+ - **成功路径**: <最终怎么解决>
152
+ - **教训**: <一句话总结>
153
+ ```
154
+
155
+ 新 change propose 时扫描相关历史,主动规避。
156
+
157
+ ---
158
+
159
+ ## 知识沉淀目录(docs/solutions/)
160
+
161
+ 结构化的问题→解决方案文档,由 `/atd compound` 生成和维护。
162
+
163
+ **何时写入**:
164
+ - 归档后用户运行 `/atd compound` 时
165
+ - Session 中遇到复杂问题时(超过 2 次尝试)
166
+
167
+ **文档结构**:参考 `@atd-code/templates/solution.md`
168
+ - YAML frontmatter(title, date, tags, difficulty, module)用于搜索
169
+ - 每个文档 20-40 行,不展开代码细节
170
+
171
+ **何时读取**:
172
+ - 1-spec 提案前:扫描 `docs/solutions/` 中与当前需求相关的历史解决方案
173
+ - 3-impl 执行时:遇到类似问题先查沉淀知识
174
+
175
+ **CONCEPTS.md**:
176
+ - 领域词汇表,记录项目的业务术语和命名约定
177
+ - 首次 `/atd context` 扫描时可生成,`/atd compound concepts` 可更新
178
+ - 不记录通用技术术语,只记录业务特有概念
179
+
180
+ ---
181
+
182
+ ## 3 层记忆体系(职责区分)
183
+
184
+ | 层 | 位置 | 作用 | 谁写 | 谁读 |
185
+ |----|------|------|------|------|
186
+ | **OpenCode session** | `.opencode/memory-context.md` | 当前项目 session 历史(OpenCode 原生格式) | OpenCode 自动 | GO.md Step 0a |
187
+ | **跨 Agent 记忆** | `~/.atd-code/memory/<项目>/` | 所有项目、所有 AI agent 共享的决策日志 | atd 工作流(5-done、compound) | GO.md Step 0a 索引 |
188
+ | **Self-learning** | `.memory.md`(🧠 条目)+ `.errors-log.md` | 自动检测 golden path 并沉淀为可复用 skill | self-learning detection(GO.md Step 0d) | X-learn.md、X-compound.md |
189
+
190
+ 3 层互补,不重叠:session 记忆管"上次讨论到哪",跨 Agent 记忆管"决定了什么/排除了什么",Self-learning 管"学到了什么可复用的流程"。
@@ -0,0 +1,74 @@
1
+ # reference/risk · 风险评分算法
2
+
3
+ > 供 4-verify、X-review、1-spec Phase 3 共同使用的风险评估标准。
4
+
5
+ ---
6
+
7
+ ## 评分维度
8
+
9
+ 每个变更(或 change)从 4 个维度打分,每维 1-3 分,总分 4-12 分。
10
+
11
+ ### 1. 影响广度(Blast Radius Breadth)
12
+
13
+ 用 `codegraph_impact` 的结果判断:
14
+
15
+ | 分数 | 条件 |
16
+ |------|------|
17
+ | 1 分(低) | blast radius 内 ≤3 个调用方 |
18
+ | 2 分(中) | 4-10 个调用方 |
19
+ | 3 分(高) | >10 个调用方,或影响核心公共接口 |
20
+
21
+ ### 2. 跨模块程度(Cross-Module Impact)
22
+
23
+ 检查受影响的目录/包是否跨越多个业务模块:
24
+
25
+ | 分数 | 条件 |
26
+ |------|------|
27
+ | 1 分(低) | 变更局限于单一模块/目录内 |
28
+ | 2 分(中) | 涉及 2 个模块(如 service + handler) |
29
+ | 3 分(高) | 涉及 ≥3 个模块,或跨越边界层(如 DB + API + Cache) |
30
+
31
+ ### 3. 测试覆盖(Test Coverage)
32
+
33
+ 用 `codegraph_callers` 查找变更函数的测试调用方:
34
+
35
+ | 分数 | 条件 |
36
+ |------|------|
37
+ | 1 分(低) | 所有变更函数都有对应的测试调用方 |
38
+ | 2 分(中) | 部分变更函数有测试,部分没有 |
39
+ | 3 分(高) | 多数变更函数无测试覆盖 |
40
+
41
+ ### 4. 变更类型(Change Type)
42
+
43
+ 基于 `git diff` 的变更性质判断:
44
+
45
+ | 分数 | 条件 |
46
+ |------|------|
47
+ | 1 分(低) | 新增代码、修改日志/配置/注释、纯工具函数 |
48
+ | 2 分(中) | 修改业务逻辑、重构内部实现 |
49
+ | 3 分(高) | 修改公共函数签名、删除代码/接口、修改数据结构 |
50
+
51
+ ---
52
+
53
+ ## 风险等级
54
+
55
+ | 总分 | 等级 | 标记 | 验证要求 |
56
+ |------|------|------|---------|
57
+ | 4-6 | 低风险 | 🟢 | 增量验证:只跑变更相关的测试 |
58
+ | 7-9 | 中风险 | 🟡 | 标准验证:跑变更模块的全量测试 |
59
+ | 10-12 | 高风险 | 🔴 | 全量验证:全量测试 + 人工确认 + 受影响流程 E2E 验证 |
60
+
61
+ ---
62
+
63
+ ## 快速评估(1-spec Phase 3 使用)
64
+
65
+ propose 阶段无法运行实际代码,使用简化的预估规则:
66
+
67
+ | 维度 | 预估方式 |
68
+ |------|---------|
69
+ | 影响广度 | 根据 design.md 中"涉及文件"列表的调用方数量估算 |
70
+ | 跨模块 | 根据"涉及模块"数量直接判断 |
71
+ | 测试覆盖 | 根据 tasks.md 是否有明确的测试任务判断 |
72
+ | 变更类型 | 根据 proposal.md 的"实施范围"判断 |
73
+
74
+ 预估分数用于复杂度评估,不影响最终的风险评分(4-verify 阶段用实际 codegraph 结果重新计算)。
@@ -0,0 +1,40 @@
1
+ # reference/tdd · TDD 约束
2
+
3
+ ---
4
+
5
+ ## 完整循环(缺任何一步 = 任务未完成)
6
+
7
+ ```
8
+ RED → 写测试,确认测试失败(编译错误或运行失败均可)
9
+ GREEN → 写最简实现,测试通过
10
+ REFACTOR → 清理代码,测试仍通过
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 测试文件要求
16
+
17
+ 1. 测试文件与实现文件同目录(Go: `xxx_test.go`,TS: `xxx.test.ts`)
18
+ 2. 每个测试函数必须有意图注释:
19
+ ```go
20
+ // Intent: 验证批量退款在部分票已退的情况下,不会重复扣减库存
21
+ func TestBatchRefund_PartialAlreadyRefunded(t *testing.T) { ... }
22
+ ```
23
+ 3. Intent 说明"为什么这个行为重要",不是"它做了什么"
24
+
25
+ ---
26
+
27
+ ## 覆盖维度
28
+
29
+ 每个业务功能至少覆盖:
30
+ - Happy path(正常流程)
31
+ - Sad path(错误/失败场景)
32
+ - 边界(空值、超限、并发)
33
+
34
+ ---
35
+
36
+ ## 禁止事项
37
+
38
+ - 不得在测试通过前提交实现
39
+ - 不得写"占位"测试(空函数体或 `t.Skip()`)
40
+ - 不得修改测试以适应错误实现(应修改实现)
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ const puppeteer = require('puppeteer');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ (async () => {
7
+ const dir = process.argv[2] || '.';
8
+ const svgFiles = fs.readdirSync(dir).filter(f => f.endsWith('.svg'));
9
+
10
+ const browser = await puppeteer.launch({
11
+ headless: 'new',
12
+ args: ['--no-sandbox', '--disable-setuid-sandbox']
13
+ });
14
+
15
+ for (const file of svgFiles) {
16
+ const svgPath = path.resolve(dir, file);
17
+ const pngPath = svgPath.replace(/\.svg$/, '.png');
18
+ const svgContent = fs.readFileSync(svgPath, 'utf-8');
19
+
20
+ const vbMatch = svgContent.match(/viewBox="[^"]*\s(\d+)\s(\d+)"/);
21
+ let width = vbMatch ? parseInt(vbMatch[1]) : 1200;
22
+ let height = vbMatch ? parseInt(vbMatch[2]) : 800;
23
+
24
+ const scale = 2;
25
+ const page = await browser.newPage();
26
+ await page.setViewport({ width, height, deviceScaleFactor: scale });
27
+
28
+ const html = `<!DOCTYPE html>
29
+ <html><head><meta charset="utf-8"><style>
30
+ body { margin: 0; padding: 0; background: transparent; }
31
+ img { display: block; }
32
+ </style></head>
33
+ <body>
34
+ <img src="data:image/svg+xml;base64,${Buffer.from(svgContent).toString('base64')}" width="${width}" height="${height}" />
35
+ </body></html>`;
36
+
37
+ await page.setContent(html, { waitUntil: 'networkidle0' });
38
+ await page.screenshot({ path: pngPath, type: 'png', omitBackground: true });
39
+ await page.close();
40
+
41
+ console.log(`Done: ${path.basename(file)} → ${path.basename(pngPath)} (${width}×${height} @${scale}x)`);
42
+ }
43
+
44
+ await browser.close();
45
+ console.log('All done.');
46
+ })();
@@ -0,0 +1,244 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const crypto = require('crypto');
4
+ const os = require('os');
5
+ const { execSync, exec } = require('child_process');
6
+
7
+ const CONFIG_DIR = path.join(os.homedir(), '.cls');
8
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
9
+ const DEFAULT_TEMPLATE = path.join(__dirname, '..', '..', 'config', 'cls-defaults.json');
10
+
11
+ function _defaults() {
12
+ return {
13
+ skill_dir: '',
14
+ python: 'python3',
15
+ accounts: {},
16
+ defaults: { limit: 200, out_dir: '.', topic_type: 'error', regions: 'gz,bj', time_range: '15m' },
17
+ output: { format: 'jsonl', filename: '{account}_{query}_{ts}-{suffix}.jsonl' },
18
+ };
19
+ }
20
+
21
+ function initConfig() {
22
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
23
+ if (fs.existsSync(CONFIG_FILE)) {
24
+ return { ok: false, msg: `配置文件已存在: ${CONFIG_FILE}`, path: CONFIG_FILE };
25
+ }
26
+ if (!fs.existsSync(DEFAULT_TEMPLATE)) {
27
+ return { ok: false, msg: `模板文件不存在: ${DEFAULT_TEMPLATE}` };
28
+ }
29
+ const template = fs.readFileSync(DEFAULT_TEMPLATE, 'utf8');
30
+ fs.writeFileSync(CONFIG_FILE, template);
31
+ return { ok: true, msg: `配置文件已生成: ${CONFIG_FILE},请编辑 skill_dir 和 accounts`, path: CONFIG_FILE };
32
+ }
33
+
34
+ function loadConfig() {
35
+ if (!fs.existsSync(CONFIG_FILE)) {
36
+ throw new Error(`配置文件 ${CONFIG_FILE} 不存在。请运行: cls --init`);
37
+ }
38
+ const raw = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
39
+ return { ..._defaults(), ...raw, defaults: { ..._defaults().defaults, ...(raw.defaults || {}) }, output: { ..._defaults().output, ...(raw.output || {}) } };
40
+ }
41
+
42
+ function resolveAccount(config, alias) {
43
+ const entry = config.accounts[alias];
44
+ if (!entry) {
45
+ const available = Object.keys(config.accounts).join(', ');
46
+ throw new Error(`账号 "${alias}" 未在配置中找到。可用: ${available}`);
47
+ }
48
+ if (typeof entry === 'string') return { account: entry, project_id: null };
49
+ return { account: entry.account || alias, project_id: entry.project_id || null };
50
+ }
51
+
52
+ function detectPython(cmd) {
53
+ try {
54
+ execSync(`which "${cmd}" 2>/dev/null || command -v "${cmd}"`, { encoding: 'utf8' });
55
+ return true;
56
+ } catch {
57
+ return false;
58
+ }
59
+ }
60
+
61
+ function _relativeTime(range) {
62
+ const m = range.match(/^(\d+)(m|h|d|s)$/);
63
+ if (!m) throw new Error(`无效的时间范围: ${range}(支持 15m/1h/2d)`);
64
+ const val = parseInt(m[1]);
65
+ const unit = m[2];
66
+ const ms = val * { s: 1000, m: 60000, h: 3600000, d: 86400000 }[unit];
67
+ return new Date(Date.now() - ms).toISOString().replace('T', ' ').slice(0, 19);
68
+ }
69
+
70
+ function _now() {
71
+ return new Date().toISOString().replace('T', ' ').slice(0, 19);
72
+ }
73
+
74
+ function _safeQuery(query) {
75
+ return (query || 'log')
76
+ .replace(/[^a-zA-Z0-9\u4e00-\u9fff]+/g, '_')
77
+ .replace(/_+/g, '_')
78
+ .replace(/^_|_$/g, '')
79
+ .slice(0, 30);
80
+ }
81
+
82
+ function buildOutputPath(account, query, outDir) {
83
+ const ts = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 15);
84
+ const suffix = crypto.randomBytes(2).toString('hex');
85
+ const safeQ = _safeQuery(query);
86
+ const ext = 'jsonl';
87
+ const name = `${account}_${safeQ}_${ts}-${suffix}.${ext}`;
88
+ const dir = (outDir || '.').replace(/^~/, os.homedir());
89
+ fs.mkdirSync(dir, { recursive: true });
90
+ return path.join(dir, name);
91
+ }
92
+
93
+ function buildSearchArgs(config, account, query, opts = {}) {
94
+ const resolved = resolveAccount(config, account);
95
+ const script = path.join(config.skill_dir, 'scripts', 'cls_query_python.py');
96
+ const outputPath = buildOutputPath(account, query, opts.out_dir || config.defaults.out_dir);
97
+ const limit = opts.limit || config.defaults.limit;
98
+ const from = opts.from || _relativeTime(config.defaults.time_range);
99
+ const to = opts.to || _now();
100
+ const topicType = opts.type || config.defaults.topic_type;
101
+ const regions = opts.regions || config.defaults.regions;
102
+
103
+ const args = [
104
+ config.python,
105
+ script,
106
+ '--account', resolved.account,
107
+ '--query', query,
108
+ '--limit', String(limit),
109
+ '--from', from,
110
+ '--to', to,
111
+ '--topic-type', topicType,
112
+ '--regions', regions,
113
+ '--output', outputPath,
114
+ '--pretty',
115
+ ];
116
+
117
+ if (resolved.project_id) {
118
+ args.splice(4, 0, '--project-id', resolved.project_id);
119
+ }
120
+
121
+ return { args, outputPath };
122
+ }
123
+
124
+ function buildFetchArgs(config, account, query, opts = {}) {
125
+ const resolved = resolveAccount(config, account);
126
+ const script = path.join(config.skill_dir, 'scripts', 'cls_fetch_bulk.py');
127
+ const outputPath = buildOutputPath(account, query, opts.out_dir || config.defaults.out_dir);
128
+ const from = opts.from || _relativeTime(config.defaults.time_range);
129
+ const to = opts.to || _now();
130
+ const max = opts.max || 5000;
131
+
132
+ const args = [
133
+ config.python,
134
+ script,
135
+ '--account', resolved.account,
136
+ '--query', query,
137
+ '--from', from,
138
+ '--to', to,
139
+ '--max', String(max),
140
+ '--out', outputPath,
141
+ '--content-only',
142
+ ];
143
+
144
+ if (resolved.project_id) {
145
+ args.splice(4, 0, '--project-id', resolved.project_id);
146
+ }
147
+
148
+ return { args, outputPath };
149
+ }
150
+
151
+ function listAccounts() {
152
+ const config = loadConfig();
153
+ const result = {};
154
+ for (const [alias, entry] of Object.entries(config.accounts)) {
155
+ if (typeof entry === 'string') {
156
+ result[alias] = entry;
157
+ } else {
158
+ result[alias] = `${entry.account} → project ${entry.project_id}`;
159
+ }
160
+ }
161
+ return result;
162
+ }
163
+
164
+ function searchSync(account, query, opts = {}) {
165
+ const config = loadConfig();
166
+ if (!detectPython(config.python)) throw new Error(`Python 解释器 "${config.python}" 未找到。请检查 ~/.cls/config.json 中 python 字段`);
167
+
168
+ const { args, outputPath } = buildSearchArgs(config, account, query, opts);
169
+ const cmd = args.map(a => a.includes(' ') ? `"${a}"` : a).join(' ');
170
+
171
+ if (!opts.quiet) console.error(`→ ${cmd}`);
172
+
173
+ execSync(cmd, { encoding: 'utf8', maxBuffer: 50 * 1024 * 1024, timeout: (opts.timeout || 60) * 1000 });
174
+ const raw = fs.readFileSync(outputPath, 'utf8');
175
+ const data = JSON.parse(raw);
176
+ const count = data?.Response?.Results?.length || data?.summary?.returned_count || 0;
177
+
178
+ return { output_file: outputPath, count, sample: (data?.Response?.Results || []).slice(0, 3) };
179
+ }
180
+
181
+ function searchAsync(account, query, opts = {}) {
182
+ return new Promise((resolve, reject) => {
183
+ const config = loadConfig();
184
+ if (!detectPython(config.python)) return reject(new Error(`Python "${config.python}" 未找到`));
185
+
186
+ const { args, outputPath } = buildSearchArgs(config, account, query, opts);
187
+ const cmd = args.map(a => a.includes(' ') ? `"${a}"` : a).join(' ');
188
+
189
+ exec(cmd, { encoding: 'utf8', maxBuffer: 50 * 1024 * 1024, timeout: (opts.timeout || 60) * 1000 }, (err, stdout, stderr) => {
190
+ if (err) return reject(new Error(stderr || err.message));
191
+ try {
192
+ const raw = fs.readFileSync(outputPath, 'utf8');
193
+ const data = JSON.parse(raw);
194
+ const count = data?.Response?.Results?.length || data?.summary?.returned_count || 0;
195
+ resolve({ output_file: outputPath, count, sample: (data?.Response?.Results || []).slice(0, 3) });
196
+ } catch (e) {
197
+ reject(e);
198
+ }
199
+ });
200
+ });
201
+ }
202
+
203
+ function fetchSync(account, query, opts = {}) {
204
+ const config = loadConfig();
205
+ if (!detectPython(config.python)) throw new Error(`Python "${config.python}" 未找到`);
206
+
207
+ const { args, outputPath } = buildFetchArgs(config, account, query, opts);
208
+ const cmd = args.map(a => a.includes(' ') ? `"${a}"` : a).join(' ');
209
+
210
+ if (!opts.quiet) console.error(`→ ${cmd}`);
211
+
212
+ execSync(cmd, { encoding: 'utf8', maxBuffer: 200 * 1024 * 1024, timeout: (opts.timeout || 120) * 1000 });
213
+ const lines = fs.readFileSync(outputPath, 'utf8').split('\n').filter(Boolean);
214
+ return { output_file: outputPath, total: lines.length };
215
+ }
216
+
217
+ function fetchAsync(account, query, opts = {}) {
218
+ return new Promise((resolve, reject) => {
219
+ const config = loadConfig();
220
+ if (!detectPython(config.python)) return reject(new Error(`Python "${config.python}" 未找到`));
221
+
222
+ const { args, outputPath } = buildFetchArgs(config, account, query, opts);
223
+ const cmd = args.map(a => a.includes(' ') ? `"${a}"` : a).join(' ');
224
+
225
+ exec(cmd, { encoding: 'utf8', maxBuffer: 200 * 1024 * 1024, timeout: (opts.timeout || 120) * 1000 }, (err, stdout, stderr) => {
226
+ if (err) return reject(new Error(stderr || err.message));
227
+ const lines = fs.readFileSync(outputPath, 'utf8').split('\n').filter(Boolean);
228
+ resolve({ output_file: outputPath, total: lines.length });
229
+ });
230
+ });
231
+ }
232
+
233
+ module.exports = {
234
+ initConfig,
235
+ loadConfig,
236
+ detectPython,
237
+ resolveAccount,
238
+ listAccounts,
239
+ searchSync,
240
+ searchAsync,
241
+ fetchSync,
242
+ fetchAsync,
243
+ buildOutputPath,
244
+ };
@@ -0,0 +1,49 @@
1
+ const os = require('os');
2
+ const path = require('path');
3
+
4
+ const HOME = os.homedir();
5
+
6
+ const PACKAGE_DIR = (() => {
7
+ if (process.env.ATDC_DIR) return process.env.ATDC_DIR;
8
+ const idx = __dirname.lastIndexOf('/node_modules/atdc/');
9
+ if (idx !== -1) return path.resolve(__dirname.slice(0, idx), 'node_modules/atdc');
10
+ const srcIdx = __dirname.lastIndexOf('/src');
11
+ if (srcIdx !== -1) return __dirname.slice(0, srcIdx);
12
+ return __dirname;
13
+ })();
14
+
15
+ const TERMINAL_CONFIGS = {
16
+ claude: {
17
+ name: 'Claude Code',
18
+ dir: path.join(HOME, '.claude', 'commands'),
19
+ fileLink: { src: 'commands/claude/atd.md', dest: 'atd.md' },
20
+ dirLink: { src: 'commands/claude/atd', dest: 'atd' },
21
+ installedCheck: () => [path.join(HOME, '.claude')],
22
+ },
23
+ opencode: {
24
+ name: 'OpenCode',
25
+ dir: path.join(HOME, '.config', 'opencode', 'commands'),
26
+ fileLink: { src: 'commands/opencode/atd.md', dest: 'atd.md' },
27
+ dirLink: { src: 'commands/opencode/atd', dest: 'atd' },
28
+ installedCheck: () => [path.join(HOME, '.config', 'opencode')],
29
+ },
30
+ codex: {
31
+ name: 'Codex',
32
+ dir: path.join(HOME, '.agents', 'skills'),
33
+ isDirLinks: true,
34
+ linkMap: [
35
+ 'atd', 'atd-1-spec', 'atd-1-update', 'atd-2-qa',
36
+ 'atd-3-impl', 'atd-3-impl-skip-spec', 'atd-4-verify',
37
+ 'atd-5-done', 'atd-add-to-hc', 'atd-article', 'atd-compound',
38
+ 'atd-context', 'atd-diagnose', 'atd-git-guardrails',
39
+ 'atd-grill-with-docs', 'atd-handoff', 'atd-health', 'atd-review',
40
+ 'atd-strategy', 'atd-zoom-out',
41
+ ],
42
+ installedCheck: () => [path.join(HOME, '.agents')],
43
+ },
44
+ };
45
+
46
+ const MEMORY_DIR = path.join(HOME, '.atdc', 'memory');
47
+ const PLACEHOLDER = '{{ATDC_DIR}}';
48
+
49
+ module.exports = { HOME, PACKAGE_DIR, TERMINAL_CONFIGS, MEMORY_DIR, PLACEHOLDER };