add-coder 0.3.16 → 0.3.19
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/dist/index.js +349 -62
- package/package.json +1 -1
- package/templates/.add-coder-src-hash.json +31 -24
- package/templates/adapters/claude/hooks/pre-tool-use.sh +6 -1
- package/templates/adapters/codex/hooks/pre-tool-use.sh +7 -1
- package/templates/adapters/qoder/hooks/pre-tool-use.sh +6 -1
- package/templates/adapters/trae/hooks/pre-tool-use.sh +7 -1
- package/templates/adapters/vscode/hooks/pre-tool-use.sh +6 -1
- package/templates/core/hooks/pre-tool-use.sh +7 -1
- package/templates/core/prisma/add.prisma +35 -0
- package/templates/core/rules/profiles/index.toml +12 -0
- package/templates/core/rules/profiles/machineserver-profile.md +22 -0
- package/templates/core/rules/profiles/webapp-profile.md +61 -0
- package/templates/core/rules/project_rules.md +6 -54
- package/templates/core/scripts/mcp-server/tools/audit.ts +23 -14
- package/templates/core/scripts/mcp-server/tools/context.ts +20 -2
- package/templates/core/scripts/mcp-server/tools/contract.ts +202 -0
- package/templates/core/scripts/mcp-server/tools/docs.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_completeness.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/hitl.ts +13 -5
- package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/index.ts +29 -10
- package/templates/core/scripts/mcp-server/tools/plan.ts +5 -2
- package/templates/core/scripts/mcp-server/tools/quality.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/registrar.ts +11 -0
- package/templates/core/scripts/mcp-server/tools/review.ts +2 -2
- package/templates/core/templates/collab-contract-template.md +203 -0
- package/templates/core/templates/collab-contract-template.schema.json +133 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "add-coder",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.19",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "[codein2027](https://github.com/xiaomingming92/codein2027) - A complete scaffolding for building the ADD programming paradigm, the implementation layer for AI code governance. Core principle: Audit as Infrastructure. Breaks the black-box programming process and cross-session amnesia, evolving programming paradigms into an auditable, traceable, and convergent new era. npx-ready.\n\n[codein2027](https://github.com/xiaomingming92/codein2027) 快速构建 ADD 编程范式的完整脚手架——AI 代码治理的落地方案。以「审计即基础设施」为核心,彻底打破编程过程黑盒与跨轮失忆,让编程范式进化为可审计、可追溯、可收敛的新时代。npx 即用,人人可体验。",
|
|
6
6
|
"repository": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_version": "0.3.
|
|
2
|
+
"_version": "0.3.19",
|
|
3
3
|
"adapters/claude/hooks/doc-format-guard.sh": "ec59ad3f",
|
|
4
4
|
"adapters/claude/hooks/lib/common.sh": "4f3e64ad",
|
|
5
5
|
"adapters/claude/hooks/lib/context-inject.sh": "cfc8c65e",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"adapters/claude/hooks/post-tool-failure.sh": "669b1b65",
|
|
16
16
|
"adapters/claude/hooks/post-tool-use.sh": "f54dd009",
|
|
17
17
|
"adapters/claude/hooks/pre-compact.sh": "4248f84f",
|
|
18
|
-
"adapters/claude/hooks/pre-tool-use.sh": "
|
|
18
|
+
"adapters/claude/hooks/pre-tool-use.sh": "080f8979",
|
|
19
19
|
"adapters/claude/hooks/prompt-submit.sh": "2fe9e42c",
|
|
20
20
|
"adapters/claude/hooks/review-checklist.sh": "b383a983",
|
|
21
21
|
"adapters/claude/hooks/session-end.sh": "777ae808",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"adapters/codex/hooks/post-tool-failure.sh": "669b1b65",
|
|
41
41
|
"adapters/codex/hooks/post-tool-use.sh": "27123aee",
|
|
42
42
|
"adapters/codex/hooks/pre-compact.sh": "6164c007",
|
|
43
|
-
"adapters/codex/hooks/pre-tool-use.sh": "
|
|
43
|
+
"adapters/codex/hooks/pre-tool-use.sh": "797664b8",
|
|
44
44
|
"adapters/codex/hooks/prompt-submit.sh": "a41371ea",
|
|
45
45
|
"adapters/codex/hooks/review-checklist.sh": "b383a983",
|
|
46
46
|
"adapters/codex/hooks/session-end.sh": "3cc8c346",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"adapters/qoder/hooks/post-tool-failure.sh": "8bc7a0e4",
|
|
65
65
|
"adapters/qoder/hooks/post-tool-use.sh": "051cb6ed",
|
|
66
66
|
"adapters/qoder/hooks/pre-compact.sh": "092fa6cf",
|
|
67
|
-
"adapters/qoder/hooks/pre-tool-use.sh": "
|
|
67
|
+
"adapters/qoder/hooks/pre-tool-use.sh": "565cff92",
|
|
68
68
|
"adapters/qoder/hooks/prompt-submit.sh": "336f262e",
|
|
69
69
|
"adapters/qoder/hooks/review-checklist.sh": "666831c8",
|
|
70
70
|
"adapters/qoder/hooks/session-end.sh": "c50339df",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"adapters/trae/hooks/post-tool-failure.sh": "669b1b65",
|
|
90
90
|
"adapters/trae/hooks/post-tool-use.sh": "27123aee",
|
|
91
91
|
"adapters/trae/hooks/pre-compact.sh": "6164c007",
|
|
92
|
-
"adapters/trae/hooks/pre-tool-use.sh": "
|
|
92
|
+
"adapters/trae/hooks/pre-tool-use.sh": "2d7fc7a7",
|
|
93
93
|
"adapters/trae/hooks/prompt-submit.sh": "a41371ea",
|
|
94
94
|
"adapters/trae/hooks/review-checklist.sh": "b383a983",
|
|
95
95
|
"adapters/trae/hooks/session-end.sh": "3cc8c346",
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
"adapters/vscode/hooks/post-tool-failure.sh": "355cfab1",
|
|
125
125
|
"adapters/vscode/hooks/post-tool-use.sh": "5f188718",
|
|
126
126
|
"adapters/vscode/hooks/pre-compact.sh": "a0908f0e",
|
|
127
|
-
"adapters/vscode/hooks/pre-tool-use.sh": "
|
|
127
|
+
"adapters/vscode/hooks/pre-tool-use.sh": "4bf878e7",
|
|
128
128
|
"adapters/vscode/hooks/prompt-submit.sh": "3e56d1a9",
|
|
129
129
|
"adapters/vscode/hooks/review-checklist.sh": "b383a983",
|
|
130
130
|
"adapters/vscode/hooks/session-end.sh": "7b407b9f",
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
"core/hooks/post-tool-failure.sh": "669b1b65",
|
|
156
156
|
"core/hooks/post-tool-use.sh": "27123aee",
|
|
157
157
|
"core/hooks/pre-compact.sh": "6164c007",
|
|
158
|
-
"core/hooks/pre-tool-use.sh": "
|
|
158
|
+
"core/hooks/pre-tool-use.sh": "f967ab24",
|
|
159
159
|
"core/hooks/prompt-submit.sh": "a41371ea",
|
|
160
160
|
"core/hooks/review-checklist.sh": "b383a983",
|
|
161
161
|
"core/hooks/session-end.sh": "3cc8c346",
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
"core/plans/2026-07/08/add-coder-npm-package-add-route-v1.md": "b05fce99",
|
|
167
167
|
"core/plans/2026-07/08/add-coder-npm-package-handoff-v1.md": "1bd40a4a",
|
|
168
168
|
"core/plans/2026-07/08/add-coder-npm-package-plan-v1.md": "a678d98e",
|
|
169
|
-
"core/prisma/add.prisma": "
|
|
169
|
+
"core/prisma/add.prisma": "037bd7e9",
|
|
170
170
|
"core/reports/REPORT-WORKFLOW.md": "03d39b21",
|
|
171
171
|
"core/reports/boundary-runtime-report.md": "6fd8053f",
|
|
172
172
|
"core/reports/code-review-combined-report.md": "195d52aa",
|
|
@@ -175,7 +175,10 @@
|
|
|
175
175
|
"core/reports/index.md": "6348c374",
|
|
176
176
|
"core/reports/runtime-report/gateway.md": "a5751665",
|
|
177
177
|
"core/reviews/.gitkeep": "e3b0c442",
|
|
178
|
-
"core/rules/
|
|
178
|
+
"core/rules/profiles/index.toml": "b83332d4",
|
|
179
|
+
"core/rules/profiles/machineserver-profile.md": "8a36cf39",
|
|
180
|
+
"core/rules/profiles/webapp-profile.md": "e493ae7b",
|
|
181
|
+
"core/rules/project_rules.md": "82b25e08",
|
|
179
182
|
"core/rules/theory-practice-map.toml": "b1db99fd",
|
|
180
183
|
"core/scripts/db-ensure.sh": "faeb6b92",
|
|
181
184
|
"core/scripts/gen-plan-index.sh": "bfc6ad3e",
|
|
@@ -203,23 +206,25 @@
|
|
|
203
206
|
"core/scripts/mcp-server/tasks/index.ts": "c040eb75",
|
|
204
207
|
"core/scripts/mcp-server/tasks/runner.ts": "d84bd35d",
|
|
205
208
|
"core/scripts/mcp-server/tasks/store.ts": "2fce1a92",
|
|
206
|
-
"core/scripts/mcp-server/tools/audit.ts": "
|
|
207
|
-
"core/scripts/mcp-server/tools/context.ts": "
|
|
208
|
-
"core/scripts/mcp-server/tools/
|
|
209
|
-
"core/scripts/mcp-server/tools/
|
|
210
|
-
"core/scripts/mcp-server/tools/gateway/
|
|
211
|
-
"core/scripts/mcp-server/tools/gateway/
|
|
212
|
-
"core/scripts/mcp-server/tools/gateway/
|
|
213
|
-
"core/scripts/mcp-server/tools/gateway/
|
|
209
|
+
"core/scripts/mcp-server/tools/audit.ts": "71a38969",
|
|
210
|
+
"core/scripts/mcp-server/tools/context.ts": "9ce21255",
|
|
211
|
+
"core/scripts/mcp-server/tools/contract.ts": "c894e64a",
|
|
212
|
+
"core/scripts/mcp-server/tools/docs.ts": "034e473c",
|
|
213
|
+
"core/scripts/mcp-server/tools/gateway/check_add_route_completeness.ts": "b58e76ee",
|
|
214
|
+
"core/scripts/mcp-server/tools/gateway/check_add_route_status.ts": "23fbbb6b",
|
|
215
|
+
"core/scripts/mcp-server/tools/gateway/check_dps.ts": "0f0bedd8",
|
|
216
|
+
"core/scripts/mcp-server/tools/gateway/check_rahs.ts": "fe210727",
|
|
217
|
+
"core/scripts/mcp-server/tools/gateway/check_spec_sync.ts": "89c2338b",
|
|
214
218
|
"core/scripts/mcp-server/tools/gateway/helpers.ts": "690d7891",
|
|
215
|
-
"core/scripts/mcp-server/tools/gateway/index.ts": "
|
|
219
|
+
"core/scripts/mcp-server/tools/gateway/index.ts": "559acb75",
|
|
216
220
|
"core/scripts/mcp-server/tools/gateway.backup": "65eae393",
|
|
217
|
-
"core/scripts/mcp-server/tools/hitl.ts": "
|
|
218
|
-
"core/scripts/mcp-server/tools/hook-event-report.ts": "
|
|
219
|
-
"core/scripts/mcp-server/tools/index.ts": "
|
|
220
|
-
"core/scripts/mcp-server/tools/plan.ts": "
|
|
221
|
-
"core/scripts/mcp-server/tools/quality.ts": "
|
|
222
|
-
"core/scripts/mcp-server/tools/
|
|
221
|
+
"core/scripts/mcp-server/tools/hitl.ts": "f447345f",
|
|
222
|
+
"core/scripts/mcp-server/tools/hook-event-report.ts": "3f21836e",
|
|
223
|
+
"core/scripts/mcp-server/tools/index.ts": "884172ba",
|
|
224
|
+
"core/scripts/mcp-server/tools/plan.ts": "477fd80d",
|
|
225
|
+
"core/scripts/mcp-server/tools/quality.ts": "c4634453",
|
|
226
|
+
"core/scripts/mcp-server/tools/registrar.ts": "efd81b3a",
|
|
227
|
+
"core/scripts/mcp-server/tools/review.ts": "4d72ca19",
|
|
223
228
|
"core/scripts/mcp-server/types.ts": "6c5e60bc",
|
|
224
229
|
"core/scripts/mcp-server.ts": "99cc6833",
|
|
225
230
|
"core/skills/add-paradigm/SKILL.md": "5d4fa389",
|
|
@@ -234,6 +239,8 @@
|
|
|
234
239
|
"core/templates/add-route-template.schema.json": "867a45c1",
|
|
235
240
|
"core/templates/checklist-template.md": "d6b73a5c",
|
|
236
241
|
"core/templates/checklist-template.schema.json": "fb3a73e8",
|
|
242
|
+
"core/templates/collab-contract-template.md": "bccba0fd",
|
|
243
|
+
"core/templates/collab-contract-template.schema.json": "ae0f482c",
|
|
237
244
|
"core/templates/fix-verification-template.md": "9c9ed175",
|
|
238
245
|
"core/templates/fix-verification-template.schema.json": "a18bc6a5",
|
|
239
246
|
"core/templates/handoff-multi-round-template.md": "d3a2375d",
|
|
@@ -103,8 +103,13 @@ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "
|
|
|
103
103
|
|
|
104
104
|
# §C: HITL tongyi 检查 — plans/ + PLAN_REVIEW reviews/ 写入前必须有 .hitl-tongyi-{planName} 哨兵
|
|
105
105
|
# implementation/runtime review 不需要 HITL,走 §B 活跃 Plan 检查
|
|
106
|
+
# handoff 是 Step 8 收敛产物(多轮交接手册),不经 HITL 审批,同样豁免
|
|
106
107
|
if echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(plans)/'; then
|
|
107
|
-
|
|
108
|
+
if echo "$file_path" | grep -qE '\-handoff'; then
|
|
109
|
+
_do_hitl=false # handoff 不被 HITL 拦截(Step 8 产物,无独立审批)
|
|
110
|
+
else
|
|
111
|
+
_do_hitl=true
|
|
112
|
+
fi
|
|
108
113
|
elif echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(reviews)/'; then
|
|
109
114
|
if echo "$file_path" | grep -qE '-(implementation|runtime)'; then
|
|
110
115
|
_do_hitl=false # implementation/runtime review 不被 HITL 拦截
|
|
@@ -175,8 +175,14 @@ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "
|
|
|
175
175
|
|
|
176
176
|
# §C: HITL tongyi 检查 — plans/ + PLAN_REVIEW reviews/ 写入前必须有 .hitl-tongyi-{planName} 哨兵
|
|
177
177
|
# implementation/runtime review 不需要 HITL,走 §B 活跃 Plan 检查
|
|
178
|
+
# handoff 是 Step 8 收敛产物(多轮交接手册),不经 HITL 审批,同样豁免
|
|
179
|
+
# TODO 应该caijuehub管理
|
|
178
180
|
if echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(plans)/'; then
|
|
179
|
-
|
|
181
|
+
if echo "$file_path" | grep -qE '\-handoff'; then
|
|
182
|
+
_do_hitl=false # handoff 不被 HITL 拦截(Step 8 产物,无独立审批)
|
|
183
|
+
else
|
|
184
|
+
_do_hitl=true
|
|
185
|
+
fi
|
|
180
186
|
elif echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(reviews)/'; then
|
|
181
187
|
if echo "$file_path" | grep -qE '-(implementation|runtime)'; then
|
|
182
188
|
_do_hitl=false # implementation/runtime review 不被 HITL 拦截
|
|
@@ -104,8 +104,13 @@ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "
|
|
|
104
104
|
|
|
105
105
|
# §C: HITL tongyi 检查 — plans/ + PLAN_REVIEW reviews/ 写入前必须有 .hitl-tongyi-{planName} 哨兵
|
|
106
106
|
# implementation/runtime review 不需要 HITL,走 §B 活跃 Plan 检查
|
|
107
|
+
# handoff 是 Step 8 收敛产物(多轮交接手册),不经 HITL 审批,同样豁免
|
|
107
108
|
if echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(plans)/'; then
|
|
108
|
-
|
|
109
|
+
if echo "$file_path" | grep -qE '\-handoff'; then
|
|
110
|
+
_do_hitl=false # handoff 不被 HITL 拦截(Step 8 产物,无独立审批)
|
|
111
|
+
else
|
|
112
|
+
_do_hitl=true
|
|
113
|
+
fi
|
|
109
114
|
elif echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(reviews)/'; then
|
|
110
115
|
if echo "$file_path" | grep -qE '-(implementation|runtime)'; then
|
|
111
116
|
_do_hitl=false # implementation/runtime review 不被 HITL 拦截
|
|
@@ -175,8 +175,14 @@ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "
|
|
|
175
175
|
|
|
176
176
|
# §C: HITL tongyi 检查 — plans/ + PLAN_REVIEW reviews/ 写入前必须有 .hitl-tongyi-{planName} 哨兵
|
|
177
177
|
# implementation/runtime review 不需要 HITL,走 §B 活跃 Plan 检查
|
|
178
|
+
# handoff 是 Step 8 收敛产物(多轮交接手册),不经 HITL 审批,同样豁免
|
|
179
|
+
# TODO 应该caijuehub管理
|
|
178
180
|
if echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(plans)/'; then
|
|
179
|
-
|
|
181
|
+
if echo "$file_path" | grep -qE '\-handoff'; then
|
|
182
|
+
_do_hitl=false # handoff 不被 HITL 拦截(Step 8 产物,无独立审批)
|
|
183
|
+
else
|
|
184
|
+
_do_hitl=true
|
|
185
|
+
fi
|
|
180
186
|
elif echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(reviews)/'; then
|
|
181
187
|
if echo "$file_path" | grep -qE '-(implementation|runtime)'; then
|
|
182
188
|
_do_hitl=false # implementation/runtime review 不被 HITL 拦截
|
|
@@ -103,8 +103,13 @@ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "
|
|
|
103
103
|
|
|
104
104
|
# §C: HITL tongyi 检查 — plans/ + PLAN_REVIEW reviews/ 写入前必须有 .hitl-tongyi-{planName} 哨兵
|
|
105
105
|
# implementation/runtime review 不需要 HITL,走 §B 活跃 Plan 检查
|
|
106
|
+
# handoff 是 Step 8 收敛产物(多轮交接手册),不经 HITL 审批,同样豁免
|
|
106
107
|
if echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(plans)/'; then
|
|
107
|
-
|
|
108
|
+
if echo "$file_path" | grep -qE '\-handoff'; then
|
|
109
|
+
_do_hitl=false # handoff 不被 HITL 拦截(Step 8 产物,无独立审批)
|
|
110
|
+
else
|
|
111
|
+
_do_hitl=true
|
|
112
|
+
fi
|
|
108
113
|
elif echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(reviews)/'; then
|
|
109
114
|
if echo "$file_path" | grep -qE '-(implementation|runtime)'; then
|
|
110
115
|
_do_hitl=false # implementation/runtime review 不被 HITL 拦截
|
|
@@ -175,8 +175,14 @@ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "
|
|
|
175
175
|
|
|
176
176
|
# §C: HITL tongyi 检查 — plans/ + PLAN_REVIEW reviews/ 写入前必须有 .hitl-tongyi-{planName} 哨兵
|
|
177
177
|
# implementation/runtime review 不需要 HITL,走 §B 活跃 Plan 检查
|
|
178
|
+
# handoff 是 Step 8 收敛产物(多轮交接手册),不经 HITL 审批,同样豁免
|
|
179
|
+
# TODO 应该caijuehub管理
|
|
178
180
|
if echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(plans)/'; then
|
|
179
|
-
|
|
181
|
+
if echo "$file_path" | grep -qE '\-handoff'; then
|
|
182
|
+
_do_hitl=false # handoff 不被 HITL 拦截(Step 8 产物,无独立审批)
|
|
183
|
+
else
|
|
184
|
+
_do_hitl=true
|
|
185
|
+
fi
|
|
180
186
|
elif echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(reviews)/'; then
|
|
181
187
|
if echo "$file_path" | grep -qE '-(implementation|runtime)'; then
|
|
182
188
|
_do_hitl=false # implementation/runtime review 不被 HITL 拦截
|
|
@@ -45,6 +45,7 @@ model AuditLog {
|
|
|
45
45
|
enum HitlType {
|
|
46
46
|
PLAN
|
|
47
47
|
PLAN_REVIEW
|
|
48
|
+
COLLAB_CONTRACT
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
enum HitlStatus {
|
|
@@ -94,6 +95,12 @@ model PlanRecord {
|
|
|
94
95
|
checklistTDone Int @default(0)
|
|
95
96
|
checklistR Int @default(0)
|
|
96
97
|
planKeyword String?
|
|
98
|
+
// 并发协作契约:该 Plan 在契约中的角色(MASTER=总控 / SUB=子 Plan)
|
|
99
|
+
contractRole ContractRole?
|
|
100
|
+
// 所属契约名(与 CollabContract.contractName 对应,反查用)
|
|
101
|
+
contractName String?
|
|
102
|
+
// 作为总控 Plan 持有的契约(masterPlanName 外键方向)
|
|
103
|
+
masterContract CollabContract?
|
|
97
104
|
// DPS 四维评分历史(check_dps 调用后回写,供 FFT 自适应权重消费)
|
|
98
105
|
dpsSemScore Int? // 语义相关性 0-100
|
|
99
106
|
dpsEntropyScore Int? // 信息熵匹配 0-100
|
|
@@ -106,6 +113,7 @@ model PlanRecord {
|
|
|
106
113
|
updatedAt DateTime @updatedAt
|
|
107
114
|
|
|
108
115
|
@@index([planKeyword])
|
|
116
|
+
@@index([contractName])
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
model ReviewRecord {
|
|
@@ -123,3 +131,30 @@ model ReviewRecord {
|
|
|
123
131
|
@@index([planName])
|
|
124
132
|
@@index([type])
|
|
125
133
|
}
|
|
134
|
+
|
|
135
|
+
enum ContractRole {
|
|
136
|
+
MASTER // 总控 Plan(契约持有者)
|
|
137
|
+
SUB // 子 Plan(契约实施单元)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 并发协作契约(多智能体并行实施规则集,对应 collab-contract 文档)
|
|
141
|
+
model CollabContract {
|
|
142
|
+
id String @id @default(cuid())
|
|
143
|
+
contractName String @unique
|
|
144
|
+
contractPath String
|
|
145
|
+
// 外键:总控 Plan(契约持有者,ContractRole=MASTER,一对一)
|
|
146
|
+
masterPlanName String @unique
|
|
147
|
+
masterPlan PlanRecord @relation(fields: [masterPlanName], references: [planName], onDelete: Restrict)
|
|
148
|
+
participants Json // [{role, platformEntity, boundPlan, planKeyword, description}]
|
|
149
|
+
abilityMatrix Json? // {能力: {专家: boolean}}
|
|
150
|
+
stages Json // [{stage, expert, trigger, parallelism}]
|
|
151
|
+
dependencyGraph String? // ASCII 图 + 机器可读声明
|
|
152
|
+
fileBoundaries Json // [{expert, exclusiveDomain, forbidden, isolationMode}] isolationMode: file/worktree
|
|
153
|
+
completionCriteria Json? // {专家: 完成标志}
|
|
154
|
+
status String @default("ACTIVE") // ACTIVE / SUPERSEDED
|
|
155
|
+
version Int @default(1)
|
|
156
|
+
createdAt DateTime @default(now())
|
|
157
|
+
updatedAt DateTime @updatedAt
|
|
158
|
+
|
|
159
|
+
@@index([masterPlanName])
|
|
160
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# add-coder 技术栈 profile 注册表(内置)
|
|
2
|
+
# 用户自定义: 在 {magicDir}/rules/profiles/ 下放置任意 *.md 即可被 `add-coder stack list` 识别
|
|
3
|
+
|
|
4
|
+
[[profile]]
|
|
5
|
+
name = "webapp"
|
|
6
|
+
description = "Web 应用(前端/全栈): LangGraph/Next.js/Prisma/TypeScript 案例栈"
|
|
7
|
+
file = "webapp-profile.md"
|
|
8
|
+
|
|
9
|
+
[[profile]]
|
|
10
|
+
name = "machineserver"
|
|
11
|
+
description = "后端服务(机器人→中间件→平台链路): Kestra/HTTP API/事件驱动"
|
|
12
|
+
file = "machineserver-profile.md"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# machineserver-profile — 后端服务技术栈约束
|
|
2
|
+
|
|
3
|
+
> 由 add-coder 分发,可通过 `add-coder stack set machineserver` 启用/切换。
|
|
4
|
+
> 本 profile 描述「机器人 → 中间件 → 平台」后端链路的通用约束,不绑定具体框架实现。
|
|
5
|
+
> 启用后,AI 必须遵守本文件中全部约束;未启用时本文件不构成约束。
|
|
6
|
+
|
|
7
|
+
## 技术栈事实(不是约束,仅供 AI 参考)
|
|
8
|
+
|
|
9
|
+
- 典型链路: 机器人/Agent → Kestra 工作流编排 → HTTP API → 平台
|
|
10
|
+
- 典型形态: 事件驱动 + 任务编排 + 服务间 HTTP 集成
|
|
11
|
+
|
|
12
|
+
## 代码质量
|
|
13
|
+
|
|
14
|
+
- 服务边界: 编排(工作流)与业务(API)分层,禁止在编排层写业务逻辑
|
|
15
|
+
- 幂等: 任务/接口消费必须幂等,失败可重放
|
|
16
|
+
- 契约: 跨服务 API 必须有显式契约(OpenAPI/JSON Schema),禁止隐式字段约定
|
|
17
|
+
- 可观测: 每个任务/请求链路必须有 traceId 贯穿
|
|
18
|
+
|
|
19
|
+
## 错误处理
|
|
20
|
+
|
|
21
|
+
- 失败路径与成功路径信息密度等价
|
|
22
|
+
- 编排失败必须有重试/补偿策略,禁止静默失败
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# webapp-profile — Web 应用技术栈约束
|
|
2
|
+
|
|
3
|
+
> 由 add-coder 分发,可通过 `add-coder stack set webapp` 启用/切换。
|
|
4
|
+
> 本 profile 为 ADD 案例研究(前端/全栈 Web 应用)沉淀的技术栈约束,从 project_rules.md 迁移而来。
|
|
5
|
+
> 启用后,AI 必须遵守本文件中全部约束;未启用时本文件不构成约束。
|
|
6
|
+
|
|
7
|
+
## 数据库 Schema
|
|
8
|
+
|
|
9
|
+
Prisma schema 修改时:
|
|
10
|
+
- 新增模型必须有审计数据字段(Json 类型)
|
|
11
|
+
- 关联关系必须定义 `onDelete: Cascade`
|
|
12
|
+
- 使用 `@id @default(cuid())` 生成 ID
|
|
13
|
+
- 使用 `@updatedAt` 自动更新时间戳
|
|
14
|
+
|
|
15
|
+
## Agent 节点
|
|
16
|
+
|
|
17
|
+
LangGraph 节点实现时:
|
|
18
|
+
- 必须通过 `wrapNodeWithAudit` 包装
|
|
19
|
+
- `inputSnapshot` 不能为空对象 `{}`
|
|
20
|
+
- 路由决策必须调用 `agentAuditRoute()`
|
|
21
|
+
- 检索节点必须记录证据链 diff
|
|
22
|
+
|
|
23
|
+
## 代码质量
|
|
24
|
+
|
|
25
|
+
- TypeScript 编译必须通过(`npx tsc --noEmit`)
|
|
26
|
+
- ESLint 零 error(`npx eslint src/` 不得出现 error 级别问题,warning 逐步降低)
|
|
27
|
+
- 禁止 `any` 类型(必须显式定义)
|
|
28
|
+
- 禁止简化代码实现,一切以代码高质量为衡量标准
|
|
29
|
+
- 新增文件必须在项目已有目录结构内,遵循现有命名规范
|
|
30
|
+
|
|
31
|
+
## 附录 A
|
|
32
|
+
|
|
33
|
+
### A. {{projectName}} ADD-0.3 实现(AuditCallback)
|
|
34
|
+
|
|
35
|
+
{{projectName}} 通过 **LangChain `BaseCallbackHandler` 继承模式** 实现自动审计:
|
|
36
|
+
|
|
37
|
+
- `AuditCallback extends BaseCallbackHandler` — 继承 LangChain 标准回调接口
|
|
38
|
+
- `handleChainStart()` / `handleChainEnd()` / `handleChainError()` — 节点进入/退出/异常自动记录
|
|
39
|
+
- `handleLLMEnd()` — LLM 调用完成时记录 token 用量
|
|
40
|
+
- `handleToolStart()` / `handleToolEnd()` — Tool 调用完成时记录输入/输出
|
|
41
|
+
|
|
42
|
+
注入方式(`src/agents/index.ts`):
|
|
43
|
+
```typescript
|
|
44
|
+
const callback = new AuditCallback(traceId, userId)
|
|
45
|
+
agent.invoke(input, { callbacks: [callback] })
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**与 Layer 1 dev-logger 的关系**:
|
|
49
|
+
- Layer 1(`wrapNodeWithAudit`):console + file,仅开发环境,AI 助手消费
|
|
50
|
+
- Layer 2(`AuditCallback`):AuditLog 表,所有环境始终开启,最终用户消费
|
|
51
|
+
- 两者共存互补,互不干扰
|
|
52
|
+
|
|
53
|
+
**设计目标达成情况**:
|
|
54
|
+
|
|
55
|
+
| 目标 | 状态 |
|
|
56
|
+
|------|:----:|
|
|
57
|
+
| 自动记录 | ✅ |
|
|
58
|
+
| 不阻塞响应 | ✅ |
|
|
59
|
+
| 成功/失败等价 | ✅ |
|
|
60
|
+
| 节点过滤 | ✅ |
|
|
61
|
+
| traceId 全链追踪 | ✅ |
|
|
@@ -846,6 +846,10 @@ ADD-17 定义 HITL 如何在 guard 约束下运作(temporary.md 绕过 guard
|
|
|
846
846
|
|
|
847
847
|
## 项目技术约束
|
|
848
848
|
|
|
849
|
+
### 技术栈约束(profile 引用)
|
|
850
|
+
|
|
851
|
+
{{stackReferenceLine}}
|
|
852
|
+
|
|
849
853
|
### 审计日志器模式
|
|
850
854
|
|
|
851
855
|
每个业务域的审计日志器必须遵循 `audit-logger.ts` 的完整模式:
|
|
@@ -856,30 +860,6 @@ ADD-17 定义 HITL 如何在 guard 约束下运作(temporary.md 绕过 guard
|
|
|
856
860
|
- `readRecentLogs()` / `clearLogs()` 读写函数
|
|
857
861
|
- `ENABLE_FILE_LOG` 环境变量控制,开发环境默认启用
|
|
858
862
|
|
|
859
|
-
### 数据库 Schema
|
|
860
|
-
|
|
861
|
-
Prisma schema 修改时:
|
|
862
|
-
- 新增模型必须有审计数据字段(Json 类型)
|
|
863
|
-
- 关联关系必须定义 `onDelete: Cascade`
|
|
864
|
-
- 使用 `@id @default(cuid())` 生成 ID
|
|
865
|
-
- 使用 `@updatedAt` 自动更新时间戳
|
|
866
|
-
|
|
867
|
-
### Agent 节点
|
|
868
|
-
|
|
869
|
-
LangGraph 节点实现时:
|
|
870
|
-
- 必须通过 `wrapNodeWithAudit` 包装
|
|
871
|
-
- `inputSnapshot` 不能为空对象 `{}`
|
|
872
|
-
- 路由决策必须调用 `agentAuditRoute()`
|
|
873
|
-
- 检索节点必须记录证据链 diff
|
|
874
|
-
|
|
875
|
-
### 代码质量
|
|
876
|
-
|
|
877
|
-
- TypeScript 编译必须通过(`npx tsc --noEmit`)
|
|
878
|
-
- ESLint 零 error(`npx eslint src/` 不得出现 error 级别问题,warning 逐步降低)
|
|
879
|
-
- 禁止 `any` 类型(必须显式定义)
|
|
880
|
-
- 禁止简化代码实现,一切以代码高质量为衡量标准
|
|
881
|
-
- 新增文件必须在项目已有目录结构内,遵循现有命名规范
|
|
882
|
-
|
|
883
863
|
---
|
|
884
864
|
|
|
885
865
|
## MCP 工具约束
|
|
@@ -890,7 +870,7 @@ LangGraph 节点实现时:
|
|
|
890
870
|
|
|
891
871
|
AI 助手在生成任何代码前,必须先调用以下工具获取真实信息,不得凭记忆假设:
|
|
892
872
|
- `get_project_context` — 获取项目结构、技术栈、可用脚本
|
|
893
|
-
- `get_db_schema` —
|
|
873
|
+
- `get_db_schema` — 获取项目数据模型定义(如 Prisma Schema / ORM 模型)
|
|
894
874
|
- `get_audit_logger_pattern` — 获取现有审计日志器模式
|
|
895
875
|
- `find_related_docs` — 查找与变更相关的项目文档(ADD-0.1 文档先行)
|
|
896
876
|
|
|
@@ -961,34 +941,6 @@ Trae IDE 加载项目时自动连接 MCP 服务器。
|
|
|
961
941
|
|
|
962
942
|
### 附录
|
|
963
943
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
{{projectName}} 通过 **LangChain `BaseCallbackHandler` 继承模式** 实现自动审计:
|
|
967
|
-
|
|
968
|
-
- `AuditCallback extends BaseCallbackHandler` — 继承 LangChain 标准回调接口
|
|
969
|
-
- `handleChainStart()` / `handleChainEnd()` / `handleChainError()` — 节点进入/退出/异常自动记录
|
|
970
|
-
- `handleLLMEnd()` — LLM 调用完成时记录 token 用量
|
|
971
|
-
- `handleToolStart()` / `handleToolEnd()` — Tool 调用完成时记录输入/输出
|
|
972
|
-
|
|
973
|
-
注入方式(`src/agents/index.ts`):
|
|
974
|
-
```typescript
|
|
975
|
-
const callback = new AuditCallback(traceId, userId)
|
|
976
|
-
agent.invoke(input, { callbacks: [callback] })
|
|
977
|
-
```
|
|
978
|
-
|
|
979
|
-
**与 Layer 1 dev-logger 的关系**:
|
|
980
|
-
- Layer 1(`wrapNodeWithAudit`):console + file,仅开发环境,AI 助手消费
|
|
981
|
-
- Layer 2(`AuditCallback`):AuditLog 表,所有环境始终开启,最终用户消费
|
|
982
|
-
- 两者共存互补,互不干扰
|
|
983
|
-
|
|
984
|
-
**设计目标达成情况**:
|
|
985
|
-
|
|
986
|
-
| 目标 | 状态 |
|
|
987
|
-
|------|:----:|
|
|
988
|
-
| 自动记录 | ✅ |
|
|
989
|
-
| 不阻塞响应 | ✅ |
|
|
990
|
-
| 成功/失败等价 | ✅ |
|
|
991
|
-
| 节点过滤 | ✅ |
|
|
992
|
-
| traceId 全链追踪 | ✅ |
|
|
944
|
+
> 附录 A({{projectName}} ADD-0.3 AuditCallback 实现,含 LangChain `BaseCallbackHandler` 模式)已移至技术栈 profile:`{{magicDir}}/rules/profiles/webapp-profile.md` §附录 A。未设置 webapp 技术栈时,该附录内容不构成约束。
|
|
993
945
|
|
|
994
946
|
#### B. 历史参考(milktea 项目)
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import * as z from "zod/v4"
|
|
2
|
-
import type {
|
|
2
|
+
import type { ToolRegistrar } from "./registrar.js"
|
|
3
3
|
import { textResponse, errorResponse } from "../shared/response.js"
|
|
4
4
|
import { prisma } from "../shared/prisma.js"
|
|
5
|
+
import { PROJECT_ID, PROJECT_ROOT } from "../shared/env.js"
|
|
5
6
|
|
|
6
|
-
export function registerAuditTools(server:
|
|
7
|
+
export function registerAuditTools(server: ToolRegistrar) {
|
|
8
|
+
|
|
9
|
+
// args 窄化辅助:MCP 工具参数可能是 string | number | undefined,统一转 string
|
|
10
|
+
const s = (v: string | number | undefined): string => (typeof v === "number" ? String(v) : (v ?? ""))
|
|
7
11
|
|
|
8
12
|
// ===== query_audit_logs (L992-1109) =====
|
|
9
13
|
server.registerTool("query_audit_logs", {
|
|
@@ -21,16 +25,18 @@ export function registerAuditTools(server: McpServer) {
|
|
|
21
25
|
try {
|
|
22
26
|
const { targetType, action, targetId, planKeyword, keyword, sinceMinutes, limit = 20 } = args
|
|
23
27
|
const where: Record<string, unknown> = {}
|
|
24
|
-
if (sinceMinutes !== undefined) where.createdAt = { gte: new Date(Date.now() - sinceMinutes * 60 * 1000) }
|
|
25
|
-
if (targetType) where.targetType = targetType; if (action) where.action = action; if (targetId) where.targetId = targetId; if (planKeyword) where.planKeyword = planKeyword
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
if (sinceMinutes !== undefined) where.createdAt = { gte: new Date(Date.now() - Number(sinceMinutes) * 60 * 1000) }
|
|
29
|
+
if (targetType) where.targetType = s(targetType); if (action) where.action = s(action); if (targetId) where.targetId = s(targetId); if (planKeyword) where.planKeyword = s(planKeyword)
|
|
30
|
+
const kw = s(keyword)
|
|
31
|
+
if (kw) where.OR = [{ action: { contains: kw, mode: "insensitive" } },{ targetType: { contains: kw, mode: "insensitive" } },{ targetId: { contains: kw, mode: "insensitive" } },{ reason: { contains: kw, mode: "insensitive" } },{ planKeyword: { contains: kw, mode: "insensitive" } }]
|
|
32
|
+
type AuditLogRow = { createdAt: Date; action: string; targetType: string; targetId: string | null; reason: string | null; planKeyword: string | null }
|
|
33
|
+
const logs = await (prisma.devOperation as Record<string, (...a: unknown[]) => unknown>).findMany({ where, orderBy: { createdAt: "desc" }, take: Math.min(Number(limit) || 20, 100), include: { user: { select: { username: true } } } }) as unknown as AuditLogRow[]
|
|
28
34
|
if (logs.length === 0) {
|
|
29
35
|
const f: string[] = []; if (targetType) f.push(`targetType=${targetType}`); if (action) f.push(`action=${action}`); if (targetId) f.push(`targetId=${targetId}`); if (planKeyword) f.push(`planKeyword=${planKeyword}`); if (keyword) f.push(`keyword="${keyword}"`); if (sinceMinutes !== undefined) f.push(`sinceMinutes=${sinceMinutes}`)
|
|
30
36
|
return textResponse(`=== 开发操作审计日志 ===\n\n未找到匹配的审计记录(条件: ${f.length > 0 ? f.join(", ") : "无条件"})。\n\n可能原因: 数据库未运行、尚无相关开发操作记录、或 filter 过于严格。`)
|
|
31
37
|
}
|
|
32
38
|
const fl: string[] = []; if (targetType) fl.push(`targetType=${targetType}`); if (action) fl.push(`action=${action}`); if (targetId) fl.push(`targetId=${targetId}`); if (planKeyword) fl.push(`planKeyword=${planKeyword}`); if (keyword) fl.push(`keyword="${keyword}"`); if (sinceMinutes !== undefined) fl.push(`最近${sinceMinutes}分钟`)
|
|
33
|
-
const lines = [`=== 开发操作审计日志 (条件: ${fl.length > 0 ? fl.join(", ") : "无过滤条件(最近全部)"}) ===`, `共 ${logs.length} 条记录`]
|
|
39
|
+
const lines = [`=== 开发操作审计日志 (条件: ${fl.length > 0 ? fl.join(", ") : "无过滤条件(最近全部)"}) ===`, `服务项目: ${PROJECT_ID} (${PROJECT_ROOT})`, `共 ${logs.length} 条记录`]
|
|
34
40
|
for (let i = 0; i < logs.length; i++) {
|
|
35
41
|
const l = logs[i]; lines.push(`[${i+1}] ${l.createdAt.toISOString()}`, ` action: ${l.action} | targetType: ${l.targetType} | targetId: ${l.targetId || "(无)"}`)
|
|
36
42
|
if (l.reason) lines.push(` reason: ${l.reason}`); if (l.planKeyword) lines.push(` planKeyword: ${l.planKeyword}`)
|
|
@@ -56,16 +62,19 @@ export function registerAuditTools(server: McpServer) {
|
|
|
56
62
|
}, async (args: Record<string, string | number | undefined>, _ctx: unknown) => {
|
|
57
63
|
try {
|
|
58
64
|
const { action, targetType, targetId, planKeyword, beforeState, afterState, reason } = args
|
|
65
|
+
const tId = s(targetId)
|
|
59
66
|
const pathWarnings: string[] = []
|
|
60
|
-
if (
|
|
67
|
+
if (tId && (tId.startsWith("/") || /^[A-Z]:\\/.test(tId))) pathWarnings.push(`⚠️ targetId 使用了绝对路径: "${tId}"。请使用相对路径。`)
|
|
61
68
|
let parsedBefore: unknown, parsedAfter: unknown
|
|
62
|
-
try { if (beforeState) parsedBefore = JSON.parse(beforeState); if (afterState) parsedAfter = JSON.parse(afterState) } catch { return errorResponse("beforeState/afterState 必须是有效的 JSON 字符串。") }
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
try { if (beforeState) parsedBefore = JSON.parse(s(beforeState)); if (afterState) parsedAfter = JSON.parse(s(afterState)) } catch { return errorResponse("beforeState/afterState 必须是有效的 JSON 字符串。") }
|
|
70
|
+
type AddUserRow = { id: string }
|
|
71
|
+
let systemUser = await (prisma.addUser as Record<string, (...a: unknown[]) => unknown>).findUnique({ where: { username: "ai-assistant" }, select: { id: true } }) as unknown as AddUserRow | null
|
|
72
|
+
if (!systemUser) systemUser = await (prisma.addUser as Record<string, (...a: unknown[]) => unknown>).create({ data: { id: "ai-assistant", username: "ai-assistant", email: "ai-assistant@internal" }, select: { id: true } }) as unknown as AddUserRow
|
|
73
|
+
type DevLogRow = { id: string; createdAt: Date }
|
|
74
|
+
const log = await (prisma.devOperation as Record<string, (...a: unknown[]) => unknown>).create({ data: { userId: systemUser.id, planKeyword: s(planKeyword) || "unknown", action: s(action), targetType: s(targetType), targetId: tId || "unknown", beforeState: parsedBefore ?? null, afterState: parsedAfter ?? null, reason: s(reason) || null } }) as unknown as DevLogRow
|
|
75
|
+
const lines = [`✅ 开发操作已记录`, ` 落库项目: ${PROJECT_ID} (${PROJECT_ROOT})`, ` ID: ${log.id}`, ` action: ${s(action)}`, ` targetType: ${s(targetType)}`, ` targetId: ${tId || "unknown"}`, ` planKeyword: ${s(planKeyword) || "unknown"}`, ` createdAt: ${log.createdAt.toISOString()}`]
|
|
67
76
|
if (pathWarnings.length > 0) { lines.push(""); lines.push(...pathWarnings) }
|
|
68
|
-
lines.push("", `📋 落库回查(必须执行):`,
|
|
77
|
+
lines.push("", `📋 落库回查(必须执行):`, tId ? ` query_audit_logs({ targetId: "${tId}" }) — 确认本条记录已写入` : "")
|
|
69
78
|
return textResponse(lines.join("\n"))
|
|
70
79
|
} catch (error) { return errorResponse(`记录开发操作失败: ${error instanceof Error ? error.message : String(error)}`) }
|
|
71
80
|
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as z from "zod/v4"
|
|
2
|
-
import type {
|
|
2
|
+
import type { ToolRegistrar } from "./registrar.js"
|
|
3
3
|
import { readFile } from "fs/promises"
|
|
4
4
|
import { readdir } from "fs/promises"
|
|
5
5
|
import { stat } from "fs/promises"
|
|
@@ -9,7 +9,7 @@ import { textResponse, errorResponse } from "../shared/response.js"
|
|
|
9
9
|
import { readFileSafe, readdirRecursive } from "../shared/fs.js"
|
|
10
10
|
import { PROJECT_ROOT, MAGIC_DIR } from "../shared/fs.js"
|
|
11
11
|
|
|
12
|
-
export function registerContextTools(server:
|
|
12
|
+
export function registerContextTools(server: ToolRegistrar) {
|
|
13
13
|
|
|
14
14
|
// ===== get_project_context (L166-430) =====
|
|
15
15
|
server.registerTool(
|
|
@@ -66,6 +66,24 @@ export function registerContextTools(server: McpServer) {
|
|
|
66
66
|
}
|
|
67
67
|
parts.push("")
|
|
68
68
|
}
|
|
69
|
+
// 技术栈 profile 追加(D8):stack.json → profiles/{stack}-profile.md 存在 → 追加全文
|
|
70
|
+
const stackRaw = await readFileSafe(join(PROJECT_ROOT, MAGIC_DIR, "stack.json"))
|
|
71
|
+
if (stackRaw) {
|
|
72
|
+
try {
|
|
73
|
+
const stackDoc = JSON.parse(stackRaw) as { stack?: unknown }
|
|
74
|
+
if (typeof stackDoc.stack === "string" && stackDoc.stack) {
|
|
75
|
+
const profileContent = await readFileSafe(join(PROJECT_ROOT, MAGIC_DIR, "rules", "profiles", `${stackDoc.stack}-profile.md`))
|
|
76
|
+
if (profileContent) {
|
|
77
|
+
parts.push(`=== 技术栈约束 (${stackDoc.stack}-profile) ===`)
|
|
78
|
+
parts.push(profileContent)
|
|
79
|
+
parts.push("")
|
|
80
|
+
} else {
|
|
81
|
+
parts.push(`⚠️ profile 文件缺失: ${MAGIC_DIR}/rules/profiles/${stackDoc.stack}-profile.md(技术栈约束未生效)`)
|
|
82
|
+
parts.push("")
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch { /* stack.json 损坏 → 中性处理,仅返回 project_rules.md */ }
|
|
86
|
+
}
|
|
69
87
|
}
|
|
70
88
|
|
|
71
89
|
if (scope === "all" || scope === "structure" || scope === "add-state") {
|