add-coder 0.3.25 → 0.3.27

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 (132) hide show
  1. package/README.md +36 -10
  2. package/dist/index.js +481 -209
  3. package/package.json +14 -4
  4. package/templates/.add-coder-src-hash.json +129 -106
  5. package/templates/adapters/claude/hooks/doc-format-guard.sh +114 -33
  6. package/templates/adapters/claude/hooks/lib/common.sh +46 -87
  7. package/templates/adapters/claude/hooks/lib/context-inject.sh +2 -2
  8. package/templates/adapters/claude/hooks/lib/notify.sh +6 -4
  9. package/templates/adapters/claude/hooks/lib/preload-templates.sh +27 -2
  10. package/templates/adapters/claude/hooks/lib/state-detect.sh +8 -112
  11. package/templates/adapters/claude/hooks/lib/vocabulary.sh +13 -7
  12. package/templates/adapters/claude/hooks/prompt-submit.sh +1 -1
  13. package/templates/adapters/claude/hooks/stop-check.sh +9 -0
  14. package/templates/adapters/codex/config.toml.example +12 -0
  15. package/templates/adapters/codex/hooks/doc-format-guard.sh +123 -41
  16. package/templates/adapters/codex/hooks/lib/common.sh +47 -99
  17. package/templates/adapters/codex/hooks/lib/context-inject.sh +4 -4
  18. package/templates/adapters/codex/hooks/lib/notify.sh +6 -4
  19. package/templates/adapters/codex/hooks/lib/preload-templates.sh +27 -6
  20. package/templates/adapters/codex/hooks/lib/state-detect.sh +5 -114
  21. package/templates/adapters/codex/hooks/lib/vocabulary.sh +3 -8
  22. package/templates/adapters/codex/hooks/notification.sh +3 -18
  23. package/templates/adapters/codex/hooks/post-tool-use.sh +24 -119
  24. package/templates/adapters/codex/hooks/pre-tool-use.sh +97 -184
  25. package/templates/adapters/codex/hooks/prompt-submit.sh +8 -3
  26. package/templates/adapters/codex/hooks/session-start.sh +19 -9
  27. package/templates/adapters/codex/hooks/stop-check.sh +44 -18
  28. package/templates/adapters/codex/hooks.json +67 -1
  29. package/templates/adapters/qoder/hooks/doc-format-guard.sh +121 -40
  30. package/templates/adapters/qoder/hooks/lib/common.sh +49 -87
  31. package/templates/adapters/qoder/hooks/lib/context-inject.sh +2 -2
  32. package/templates/adapters/qoder/hooks/lib/notify.sh +6 -4
  33. package/templates/adapters/qoder/hooks/lib/preload-templates.sh +27 -2
  34. package/templates/adapters/qoder/hooks/lib/state-detect.sh +8 -112
  35. package/templates/adapters/qoder/hooks/lib/vocabulary.sh +13 -7
  36. package/templates/adapters/qoder/hooks/prompt-submit.sh +1 -1
  37. package/templates/adapters/qoder/hooks/session-start.sh +1 -1
  38. package/templates/adapters/qoder/hooks/stop-check.sh +11 -0
  39. package/templates/adapters/trae/hooks/doc-format-guard.sh +122 -40
  40. package/templates/adapters/trae/hooks/lib/common.sh +46 -87
  41. package/templates/adapters/trae/hooks/lib/context-inject.sh +2 -2
  42. package/templates/adapters/trae/hooks/lib/notify.sh +5 -3
  43. package/templates/adapters/trae/hooks/lib/preload-templates.sh +27 -2
  44. package/templates/adapters/trae/hooks/lib/state-detect.sh +8 -115
  45. package/templates/adapters/trae/hooks/lib/vocabulary.sh +13 -7
  46. package/templates/adapters/trae/hooks/pre-tool-use.sh +14 -8
  47. package/templates/adapters/trae/hooks/stop-check.sh +9 -0
  48. package/templates/adapters/vscode/hooks/doc-format-guard.sh +114 -33
  49. package/templates/adapters/vscode/hooks/lib/common.sh +46 -87
  50. package/templates/adapters/vscode/hooks/lib/context-inject.sh +2 -2
  51. package/templates/adapters/vscode/hooks/lib/notify.sh +6 -4
  52. package/templates/adapters/vscode/hooks/lib/preload-templates.sh +27 -2
  53. package/templates/adapters/vscode/hooks/lib/state-detect.sh +8 -112
  54. package/templates/adapters/vscode/hooks/lib/vocabulary.sh +13 -7
  55. package/templates/adapters/vscode/hooks/notification.sh +3 -5
  56. package/templates/adapters/vscode/hooks/prompt-submit.sh +1 -1
  57. package/templates/adapters/vscode/hooks/stop-check.sh +9 -0
  58. package/templates/core/docs/ADD-governance-claude-code.md +1 -1
  59. package/templates/core/docs/ADD-governance-codex.md +1 -1
  60. package/templates/core/docs/ADD-governance-qoder-cn.md +1 -1
  61. package/templates/core/docs/ADD-governance-trae.md +1 -1
  62. package/templates/core/docs/ADD-governance-vscode-copilot.md +1 -1
  63. package/templates/core/hooks/doc-format-guard.sh +122 -40
  64. package/templates/core/hooks/lib/common.sh +46 -87
  65. package/templates/core/hooks/lib/context-inject.sh +2 -2
  66. package/templates/core/hooks/lib/notify.sh +5 -3
  67. package/templates/core/hooks/lib/preload-templates.sh +27 -2
  68. package/templates/core/hooks/lib/state-detect.sh +8 -115
  69. package/templates/core/hooks/lib/vocabulary.sh +13 -7
  70. package/templates/core/hooks/pre-tool-use.sh +14 -8
  71. package/templates/core/hooks/stop-check.sh +9 -0
  72. package/templates/core/prisma/add.prisma +140 -82
  73. package/templates/core/rules/project_rules.md +27 -24
  74. package/templates/core/scripts/db-ensure.sh +8 -0
  75. package/templates/core/scripts/gen-plan-index.sh +4 -15
  76. package/templates/core/scripts/mcp-server/notifications/hitl.ts +9 -1
  77. package/templates/core/scripts/mcp-server/notifications/hook.ts +25 -9
  78. package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +34 -0
  79. package/templates/core/scripts/mcp-server/resources/hook-events-report.ts +14 -2
  80. package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
  81. package/templates/core/scripts/mcp-server/sampling/review.ts +25 -10
  82. package/templates/core/scripts/mcp-server/shared/db-types.ts +33 -8
  83. package/templates/core/scripts/mcp-server/shared/dps-scoring.strategy.ts +1 -1
  84. package/templates/core/scripts/mcp-server/shared/env.ts +28 -8
  85. package/templates/core/scripts/mcp-server/shared/fs.ts +3 -0
  86. package/templates/core/scripts/mcp-server/shared/hitl-interaction.strategy.ts +2 -3
  87. package/templates/core/scripts/mcp-server/shared/hitl-lifecycle-mutation.ts +129 -0
  88. package/templates/core/scripts/mcp-server/shared/hitl-proposal-mutation.ts +98 -0
  89. package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +3 -0
  90. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-events.ts +62 -0
  91. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-mutation.ts +78 -0
  92. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-subscriber.ts +145 -0
  93. package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +153 -0
  94. package/templates/core/scripts/mcp-server/shared/plan-round-events.ts +56 -0
  95. package/templates/core/scripts/mcp-server/shared/plan-round-mutation.ts +143 -0
  96. package/templates/core/scripts/mcp-server/shared/plan-round-store.ts +47 -0
  97. package/templates/core/scripts/mcp-server/shared/plan-round-subscriber.ts +99 -0
  98. package/templates/core/scripts/mcp-server/shared/plan-status-store.ts +37 -0
  99. package/templates/core/scripts/mcp-server/shared/plan-tracking-mutation.ts +71 -0
  100. package/templates/core/scripts/mcp-server/shared/prisma.ts +12 -6
  101. package/templates/core/scripts/mcp-server/shared/project-root-strategy.ts +1 -2
  102. package/templates/core/scripts/mcp-server/shared/quant-recheck.strategy.ts +16 -0
  103. package/templates/core/scripts/mcp-server/shared/redact.ts +28 -0
  104. package/templates/core/scripts/mcp-server/shared/response.ts +3 -2
  105. package/templates/core/scripts/mcp-server/shared/runtime-context.ts +77 -0
  106. package/templates/core/scripts/mcp-server/shared/schema-topology.ts +159 -0
  107. package/templates/core/scripts/mcp-server/tools/audit.ts +49 -7
  108. package/templates/core/scripts/mcp-server/tools/context.ts +117 -121
  109. package/templates/core/scripts/mcp-server/tools/contract.ts +34 -9
  110. package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +50 -3
  111. package/templates/core/scripts/mcp-server/tools/gateway/check_doc_similarity.ts +218 -0
  112. package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +22 -7
  113. package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +75 -4
  114. package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +19 -6
  115. package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -0
  116. package/templates/core/scripts/mcp-server/tools/hitl.ts +160 -75
  117. package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +9 -2
  118. package/templates/core/scripts/mcp-server/tools/index.ts +69 -3
  119. package/templates/core/scripts/mcp-server/tools/plan.ts +140 -15
  120. package/templates/core/scripts/mcp-server/tools/review.ts +44 -15
  121. package/templates/core/scripts/mcp-server.ts +60 -1
  122. package/templates/core/scripts/plan-status-bridge.ts +26 -0
  123. package/templates/core/skills/add-paradigm/SKILL.md +2 -2
  124. package/templates/core/templates/add-route-template.schema.json +24 -56
  125. package/templates/core/templates/collab-contract-template.md +1 -1
  126. package/templates/core/templates/handoff-multi-round-template.schema.json +48 -0
  127. package/templates/core/templates/handoff-single-round-template.schema.json +92 -0
  128. package/templates/core/templates/handoff-single-round.schema.json +1 -1
  129. package/templates/core/templates/hitl-approval-widget.html +222 -37
  130. package/templates/core/templates/spec-template.schema.json +9 -46
  131. package/templates/core/templates/standard-plan-template.schema.json +1 -1
  132. package/templates/core/templates/tasks-template.schema.json +13 -54
@@ -0,0 +1,48 @@
1
+ {
2
+ "template": "handoff-multi-round-template.md",
3
+ "sections": [
4
+ {
5
+ "id": "meta",
6
+ "heading": "## 全局元信息",
7
+ "required": true
8
+ },
9
+ {
10
+ "id": "boundary",
11
+ "heading": "## 原子事务边界说明",
12
+ "required": true
13
+ },
14
+ {
15
+ "id": "round",
16
+ "heading": "## <第N轮>",
17
+ "required": true
18
+ },
19
+ {
20
+ "id": "convergence",
21
+ "heading": "## 每轮收敛判定补充规则",
22
+ "required": true
23
+ },
24
+ {
25
+ "id": "appendix",
26
+ "heading": "## 附录:每轮启动模板",
27
+ "required": true
28
+ },
29
+ {
30
+ "id": "desensitize",
31
+ "heading": "### 脱敏要求",
32
+ "required": true
33
+ }
34
+ ],
35
+ "placeholders": [
36
+ "{项目名}",
37
+ "{轮次数}",
38
+ "{plan文件名}",
39
+ "{execution文件名}",
40
+ "{仓库绝对路径}",
41
+ "{N}"
42
+ ],
43
+ "forbidden_terms": [
44
+ "Phase",
45
+ "阶段",
46
+ "步骤"
47
+ ]
48
+ }
@@ -0,0 +1,92 @@
1
+ {
2
+ "template": "handoff-single-round-template.md",
3
+ "sections": [
4
+ {
5
+ "id": "1.prestate",
6
+ "heading": "## 1. 交接前状态",
7
+ "required": true
8
+ },
9
+ {
10
+ "id": "2.poststate",
11
+ "heading": "## 2. 交接后状态(目标)",
12
+ "required": true
13
+ },
14
+ {
15
+ "id": "3.changes",
16
+ "heading": "## 3. 改动清单",
17
+ "required": true
18
+ },
19
+ {
20
+ "id": "4.rollback",
21
+ "heading": "## 4. 回滚方案",
22
+ "required": true
23
+ },
24
+ {
25
+ "id": "5.precheck",
26
+ "heading": "## 5. 执行前置检查",
27
+ "required": true
28
+ },
29
+ {
30
+ "id": "6.steps",
31
+ "heading": "## 6. 执行 Task 摘要",
32
+ "required": true
33
+ },
34
+ {
35
+ "id": "7.risks",
36
+ "heading": "## 7. 关键风险点",
37
+ "required": true
38
+ },
39
+ {
40
+ "id": "8.audit",
41
+ "heading": "## 8. 恢复上下文审计查询",
42
+ "required": true,
43
+ "subsections": [
44
+ {
45
+ "id": "8.1",
46
+ "heading": "### 总体一键恢复"
47
+ },
48
+ {
49
+ "id": "8.2",
50
+ "heading": "### 逐任务/逐文件审计查询"
51
+ },
52
+ {
53
+ "id": "8.3",
54
+ "heading": "### SQL 管理员验证"
55
+ },
56
+ {
57
+ "id": "8.4",
58
+ "heading": "### 恢复判定标准"
59
+ }
60
+ ]
61
+ },
62
+ {
63
+ "id": "9.confirm",
64
+ "heading": "## 9. 后置确认",
65
+ "required": true
66
+ },
67
+ {
68
+ "id": "desensitize",
69
+ "heading": "### 脱敏要求",
70
+ "required": true
71
+ }
72
+ ],
73
+ "placeholders": [
74
+ "{项目名}",
75
+ "{变更描述}",
76
+ "{文件路径}",
77
+ "{新建/修改/删除}",
78
+ "{检查项1}",
79
+ "{检查项2}",
80
+ "{N}",
81
+ "{汇总关键词}",
82
+ "{关键字}",
83
+ "{ACTION_1}",
84
+ "{ACTION_2}",
85
+ "{ACTION_3}"
86
+ ],
87
+ "forbidden_terms": [
88
+ "Phase",
89
+ "Round",
90
+ "轮次"
91
+ ]
92
+ }
@@ -28,7 +28,7 @@
28
28
  },
29
29
  {
30
30
  "id": "6.steps",
31
- "heading": "## 6. 执行步骤摘要",
31
+ "heading": "## 6. 执行 Task 摘要",
32
32
  "required": true
33
33
  },
34
34
  {
@@ -1,16 +1,41 @@
1
1
  <style>
2
2
  * { box-sizing:border-box; margin:0; padding:0 }
3
- body { font-family:var(--font-family); font-size:14px; color:var(--color-text); padding:16px; background:var(--color-bg) }
3
+ html, body { width:100%; max-width:100%; min-height:620px; overflow:hidden }
4
+ body {
5
+ font-family:var(--font-family, ui-sans-serif, system-ui, sans-serif);
6
+ font-size:14px;
7
+ color:var(--color-text, #e8e8e8);
8
+ padding:8px;
9
+ background:var(--color-bg, #1f1f1f)
10
+ }
11
+ .app-shell {
12
+ display:flex;
13
+ flex-direction:column;
14
+ width:100%;
15
+ height:clamp(600px, 78vh, 760px);
16
+ min-height:600px;
17
+ max-height:760px;
18
+ overflow:hidden;
19
+ }
4
20
 
5
- .header { margin-bottom:16px }
21
+ .header { flex:0 0 auto; margin-bottom:10px }
6
22
  .header h2 { font-size:16px; font-weight:500; margin-bottom:4px }
7
23
  .header .meta { font-size:12px; color:var(--color-text-muted) }
8
24
  .header .meta span { margin-right:12px }
9
25
 
10
- .table-wrap { border:1px solid var(--color-border); border-radius:8px; overflow:hidden }
26
+ .table-wrap {
27
+ flex:1 1 auto;
28
+ min-height:160px;
29
+ max-height:620px;
30
+ border:1px solid var(--color-border, #444);
31
+ border-radius:8px;
32
+ overflow:auto;
33
+ overscroll-behavior:contain;
34
+ scrollbar-gutter:stable;
35
+ }
11
36
 
12
- table { width:100%; border-collapse:collapse; font-size:13px }
13
- thead { background:var(--color-surface-elevated) }
37
+ table { width:100%; min-width:680px; border-collapse:separate; border-spacing:0; font-size:13px }
38
+ thead { position:sticky; top:0; z-index:3; background:var(--color-surface-elevated, #292929) }
14
39
  th { padding:8px 12px; text-align:left; font-weight:500; font-size:12px; color:var(--color-text-muted); border-bottom:1px solid var(--color-border) }
15
40
  th:first-child { width:32px; text-align:center }
16
41
  th:nth-child(4) { width:200px; text-align:center }
@@ -38,7 +63,17 @@ tr.adjusting .adj-input { display:block }
38
63
  .dirty-mark { font-size:11px; color:var(--color-warning); margin-left:4px }
39
64
  .saved-mark { font-size:11px; color:var(--color-success); margin-left:4px }
40
65
 
41
- .footer { display:flex; gap:8px; margin-top:16px; align-items:center }
66
+ .footer {
67
+ position:sticky;
68
+ bottom:0;
69
+ z-index:4;
70
+ flex:0 0 auto;
71
+ display:flex;
72
+ gap:8px;
73
+ padding-top:10px;
74
+ align-items:center;
75
+ background:var(--color-bg, #1f1f1f)
76
+ }
42
77
  .btn { padding:8px 16px; border:none; border-radius:6px; font-size:13px; font-weight:500; cursor:pointer }
43
78
  .btn-primary { background:var(--color-accent); color:var(--color-accent-text) }
44
79
  .btn-primary:disabled { background:var(--color-text-muted); cursor:not-allowed; opacity:0.5 }
@@ -51,6 +86,7 @@ tr.adjusting .adj-input { display:block }
51
86
  .status-msg.warn { background:var(--color-warning); color:#000 }
52
87
  </style>
53
88
 
89
+ <div class="app-shell">
54
90
  <div class="header">
55
91
  <h2 id="plan_title">— HITL 提案</h2>
56
92
  <div class="meta">
@@ -68,39 +104,111 @@ tr.adjusting .adj-input { display:block }
68
104
  </div>
69
105
 
70
106
  <div class="footer">
71
- <button class="btn btn-primary" id="btn_tongyi" onclick="submit('TONGYI')">同意全部</button>
72
- <button class="btn btn-danger" onclick="submit('BOHUI')">驳回全部</button>
73
- <button class="btn btn-outline" onclick="resetAll()">重置</button>
107
+ <button type="button" class="btn btn-primary" id="btn_tongyi" data-verdict="TONGYI">同意全部</button>
108
+ <button type="button" class="btn btn-danger" id="btn_bohui" data-verdict="BOHUI">驳回全部</button>
109
+ <button type="button" class="btn btn-outline" id="btn_reset">重置</button>
74
110
  <span class="status-msg" id="status_msg" style="display:none"></span>
75
111
  </div>
112
+ </div>
76
113
 
77
114
  <script>
78
- // 通过 window.__WIDGET_DATA__ 传入,无兜底默认值——缺失时直接报错
79
- // 格式: { planName, type, round, dimensions: [{ name, content }] }
80
- const data = window.__WIDGET_DATA__
81
- if (!data || !data.dimensions || !data.dimensions.length) {
82
- document.getElementById('plan_title').textContent = '错误:缺少 dimensions 数据'
83
- document.getElementById('plan_meta').textContent = '请通过 window.__WIDGET_DATA__.dimensions 传入决策维度'
84
- throw new Error('missing dimensions')
115
+ // core widget 数据契约:{ planName, type, round, status, dimensions: [{ name, content }] }
116
+ // Qoder 可继续注入 window.__WIDGET_DATA__;Codex/OpenAI 与标准 MCP Apps 通过宿主桥异步注入。
117
+ let data = null
118
+ let dims = []
119
+ let planName = ''
120
+ let hitlType = 'PLAN'
121
+ let round = 1
122
+ let decisions = []
123
+ let mcpAppsReady = false
124
+ let rpcId = 0
125
+ const pending = new Map()
126
+
127
+ function extractData(value) {
128
+ if (!value || typeof value !== 'object') return null
129
+ const candidate = value.structuredContent || value.toolOutput || value.result?.structuredContent || value.result || value
130
+ return candidate && Array.isArray(candidate.dimensions) ? candidate : null
85
131
  }
86
132
 
87
- const dims = data.dimensions
88
- const planName = data.planName || ''
89
- const hitlType = data.type || 'PLAN'
90
- const round = data.round || 1
133
+ function applyData(value) {
134
+ const next = extractData(value)
135
+ if (!next || !next.dimensions.length) return false
136
+ data = next
137
+ dims = next.dimensions
138
+ planName = next.planName || ''
139
+ hitlType = next.type || 'PLAN'
140
+ round = next.round || 1
141
+ decisions = dims.map(() => ({ action:'', adjusted:'', dirty:false, saved:false }))
142
+ document.getElementById('plan_title').textContent = planName + ' — HITL 提案 (round ' + round + ')'
143
+ document.getElementById('plan_meta').innerHTML =
144
+ '<span>类型: ' + esc(hitlType) + '</span><span>轮次: ' + round + '</span><span>状态: ' + esc(next.status || 'DRAFT') + '</span>'
145
+ render()
146
+ notifyHeight()
147
+ return true
148
+ }
149
+
150
+ function notifyHeight() {
151
+ if (typeof window.openai?.notifyIntrinsicHeight === 'function') {
152
+ window.openai.notifyIntrinsicHeight()
153
+ }
154
+ }
155
+
156
+ function postRpc(method, params) {
157
+ const id = ++rpcId
158
+ window.parent.postMessage({ jsonrpc:'2.0', id, method, params }, '*')
159
+ return new Promise((resolve, reject) => {
160
+ const timeout = setTimeout(() => {
161
+ pending.delete(id)
162
+ reject(new Error('MCP Apps request timeout'))
163
+ }, 5000)
164
+ pending.set(id, {
165
+ resolve: value => { clearTimeout(timeout); resolve(value) },
166
+ reject: error => { clearTimeout(timeout); reject(error) }
167
+ })
168
+ })
169
+ }
170
+
171
+ async function initializeMcpApps() {
172
+ if (window.parent === window) return
173
+ try {
174
+ await postRpc('ui/initialize', {
175
+ protocolVersion: '2025-06-18',
176
+ appCapabilities: {},
177
+ clientInfo: { name:'add-coder-hitl-widget', version:'1.0.0' }
178
+ })
179
+ mcpAppsReady = true
180
+ window.parent.postMessage({ jsonrpc:'2.0', method:'ui/notifications/initialized' }, '*')
181
+ } catch {
182
+ mcpAppsReady = false
183
+ }
184
+ }
91
185
 
92
- document.getElementById('plan_title').textContent = planName + ' — HITL 提案 (round ' + round + ')'
93
- document.getElementById('plan_meta').innerHTML =
94
- '<span>类型: ' + hitlType + '</span><span>轮次: ' + round + '</span><span>状态: DRAFT</span>'
186
+ window.addEventListener('message', (event) => {
187
+ const message = event.data
188
+ if (!message || message.jsonrpc !== '2.0') return
189
+ if (message.id && pending.has(message.id)) {
190
+ const request = pending.get(message.id)
191
+ pending.delete(message.id)
192
+ if (message.error) request.reject(new Error(message.error.message || 'MCP Apps request failed'))
193
+ else request.resolve(message.result)
194
+ return
195
+ }
196
+ if (message.method === 'ui/notifications/tool-result' || message.method === 'ui/notifications/tool-input') {
197
+ applyData(message.params)
198
+ }
199
+ })
95
200
 
96
- // decisions[i] = { action: ''|'agree'|'adjust', adjusted: '', dirty: false, saved: false }
97
- let decisions = dims.map(() => ({ action:'', adjusted:'', dirty:false, saved:false }))
201
+ window.addEventListener('openai:set_globals', (event) => {
202
+ const globals = event.detail?.globals || event.detail || {}
203
+ applyData(globals.toolOutput || globals.toolInput || globals)
204
+ })
98
205
 
99
206
  function hasUnsaved() {
100
207
  return decisions.some(d => d.action === 'adjust' && !d.saved)
101
208
  }
102
209
 
103
210
  function render() {
211
+ if (!dims.length) return
104
212
  let html = ''
105
213
  dims.forEach((d, i) => {
106
214
  const dec = decisions[i]
@@ -111,17 +219,17 @@ function render() {
111
219
  html += '<td>' + esc(d.content || '—')
112
220
  if (isAdjust) {
113
221
  html += '<input class="adj-input" id="adj_' + i + '" value="' + esc(dec.adjusted) +
114
- '" oninput="markDirty(' + i + ')" placeholder="输入调整后内容...">'
115
- html += '<button class="btn-save-row" id="save_' + i + '" disabled onclick="saveRow(' + i + ')">保存</button>'
222
+ '" data-row="' + i + '" placeholder="输入调整后内容...">'
223
+ html += '<button type="button" class="btn-save-row" id="save_' + i + '" data-row="' + i + '" data-action="save" disabled>保存</button>'
116
224
  html += '<span class="dirty-mark" id="dirty_' + i + '" style="display:none">未保存</span>'
117
225
  html += '<span class="saved-mark" id="saved_' + i + '" style="display:' + (dec.saved ? 'inline' : 'none') + '">已保存</span>'
118
226
  }
119
227
  html += '</td>'
120
228
  html += '<td><div class="btn-group">'
121
- html += '<button class="btn-row agree' + (dec.action === 'agree' ? ' active' : '') +
122
- '" onclick="toggle(' + i + ',' + String.fromCharCode(39) + 'agree' + String.fromCharCode(39) + ')">同意</button>'
123
- html += '<button class="btn-row adj' + (dec.action === 'adjust' ? ' active' : '') +
124
- '" onclick="toggle(' + i + ',' + String.fromCharCode(39) + 'adjust' + String.fromCharCode(39) + ')">调整</button>'
229
+ html += '<button type="button" class="btn-row agree' + (dec.action === 'agree' ? ' active' : '') +
230
+ '" data-row="' + i + '" data-action="agree" aria-pressed="' + (dec.action === 'agree') + '">同意</button>'
231
+ html += '<button type="button" class="btn-row adj' + (dec.action === 'adjust' ? ' active' : '') +
232
+ '" data-row="' + i + '" data-action="adjust" aria-pressed="' + (dec.action === 'adjust') + '">调整</button>'
125
233
  html += '</div></td></tr>'
126
234
  })
127
235
  document.getElementById('tbody').innerHTML = html
@@ -138,10 +246,11 @@ function render() {
138
246
 
139
247
  // "同意全部"在有未保存调整时 disable
140
248
  document.getElementById('btn_tongyi').disabled = hasUnsaved()
249
+ notifyHeight()
141
250
  }
142
251
 
143
252
  function esc(s) {
144
- return (s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;')
253
+ return String(s ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;')
145
254
  }
146
255
 
147
256
  function toggle(i, action) {
@@ -188,7 +297,69 @@ function resetAll() {
188
297
  render()
189
298
  }
190
299
 
191
- function submit(verdict) {
300
+ document.getElementById('tbody').addEventListener('click', (event) => {
301
+ const button = event.target.closest('button[data-row][data-action]')
302
+ if (!button) return
303
+ // 行级决定只更新 widget 草稿,不能被宿主当作聊天/工具提交事件。
304
+ event.preventDefault()
305
+ event.stopPropagation()
306
+ const index = Number(button.dataset.row)
307
+ if (!Number.isInteger(index) || !decisions[index]) return
308
+ if (button.dataset.action === 'save') saveRow(index)
309
+ else toggle(index, button.dataset.action)
310
+ })
311
+
312
+ document.getElementById('tbody').addEventListener('input', (event) => {
313
+ const input = event.target.closest('input[data-row]')
314
+ if (!input) return
315
+ event.stopPropagation()
316
+ const index = Number(input.dataset.row)
317
+ if (Number.isInteger(index) && decisions[index]) markDirty(index)
318
+ })
319
+
320
+ document.getElementById('btn_tongyi').addEventListener('click', () => submitDecision('TONGYI'))
321
+ document.getElementById('btn_bohui').addEventListener('click', () => submitDecision('BOHUI'))
322
+ document.getElementById('btn_reset').addEventListener('click', resetAll)
323
+
324
+ async function sendCodexFollowUp(payload, cause) {
325
+ if (typeof window.openai?.sendFollowUpMessage !== 'function') throw cause
326
+ const prompt = [
327
+ '[ADD HITL Widget Submission]',
328
+ '用户已在 HITL 弹框中完成选择。请立即调用 update_hitl,严格使用下面 JSON 参数,不要替用户改写决策:',
329
+ JSON.stringify({ ...payload, _use_widget:true })
330
+ ].join('\n')
331
+ await window.openai.sendFollowUpMessage({
332
+ prompt,
333
+ title:'提交 HITL 决策',
334
+ context:{ source:'add-coder-hitl-widget', payload }
335
+ })
336
+ return { queued:true, fallbackReason:cause?.message || String(cause) }
337
+ }
338
+
339
+ async function callUpdateHitl(payload) {
340
+ const args = { ...payload, _use_widget:true }
341
+ if (typeof window.sendToAgent === 'function') {
342
+ return window.sendToAgent({ ...payload, _use_genui:true }, { submit:true })
343
+ }
344
+ if (mcpAppsReady || window.parent !== window) {
345
+ try {
346
+ return await postRpc('tools/call', { name:'update_hitl', arguments:args })
347
+ } catch (error) {
348
+ return sendCodexFollowUp(payload, error)
349
+ }
350
+ }
351
+ if (typeof window.openai?.callTool === 'function') {
352
+ try {
353
+ return await window.openai.callTool('update_hitl', args)
354
+ } catch (error) {
355
+ return sendCodexFollowUp(payload, error)
356
+ }
357
+ }
358
+ return sendCodexFollowUp(payload, new Error('当前宿主未提供直接 MCP tool bridge'))
359
+ }
360
+
361
+ async function submitDecision(verdict) {
362
+ if (!data) return
192
363
  if (verdict === 'TONGYI') {
193
364
  // 有未保存调整时禁止提交(按钮已 disabled,双重防护)
194
365
  if (hasUnsaved()) return
@@ -197,8 +368,8 @@ function submit(verdict) {
197
368
  }
198
369
  const msg = document.getElementById('status_msg')
199
370
  msg.style.display = 'inline-block'
200
- msg.className = 'status-msg ' + (verdict === 'TONGYI' ? 'ok' : 'warn')
201
- msg.textContent = verdict === 'TONGYI' ? '✓ 已通过' : '✗ 已驳回'
371
+ msg.className = 'status-msg warn'
372
+ msg.textContent = '正在提交…'
202
373
 
203
374
  // 回传数据对齐 create_hitl / update_hitl 字段
204
375
  const results = dims.map((d, i) => ({
@@ -215,8 +386,22 @@ function submit(verdict) {
215
386
  dimensions: results
216
387
  }
217
388
  if (verdict === 'BOHUI') payload.reason = '用户驳回全部提案'
218
- sendToAgent(payload, { submit: true })
389
+ try {
390
+ const result = await callUpdateHitl(payload)
391
+ msg.className = 'status-msg ' + (verdict === 'TONGYI' ? 'ok' : 'warn')
392
+ msg.textContent = result?.queued
393
+ ? '✓ 已交给 Codex 执行 update_hitl'
394
+ : (verdict === 'TONGYI' ? '✓ 已通过' : '✗ 已驳回')
395
+ } catch (error) {
396
+ msg.className = 'status-msg warn'
397
+ msg.textContent = '提交失败:' + (error?.message || String(error))
398
+ }
399
+ notifyHeight()
219
400
  }
220
401
 
221
- render()
402
+ document.getElementById('plan_title').textContent = '正在加载 HITL 提案…'
403
+ document.getElementById('plan_meta').textContent = '等待宿主提供 scoped proposal data'
404
+ applyData(window.__WIDGET_DATA__)
405
+ applyData(window.openai?.toolOutput || window.openai?.toolInput)
406
+ initializeMcpApps()
222
407
  </script>
@@ -1,20 +1,23 @@
1
1
  {
2
+ "_generated": true,
3
+ "_generated_source": "guard-rules.toml",
2
4
  "template": "spec-template.md",
3
5
  "sections": [
4
6
  {
5
7
  "id": "plan_spec_mapping",
6
- "heading": "## Plan→Spec 映射",
7
- "required": true
8
+ "required": true,
9
+ "heading": "## Plan→Spec 映射"
8
10
  },
9
11
  {
10
12
  "id": "section_1",
11
- "heading": "## 1.",
12
- "required": true
13
+ "required": true,
14
+ "heading": "## 1."
13
15
  },
14
16
  {
15
17
  "id": "plan_track_note",
16
- "pattern": "plan_track\\(\\{ planName:",
17
- "required": true
18
+ "required": true,
19
+ "anchor": "plan_track",
20
+ "pattern": "plan_track\\(\\{ planName:"
18
21
  }
19
22
  ],
20
23
  "placeholders": [
@@ -29,43 +32,3 @@
29
32
  "步骤"
30
33
  ]
31
34
  }
32
- {
33
- "template": "spec-template.md",
34
- "sections": [
35
- {
36
- "id": "why",
37
- "heading": "## Why",
38
- "required": true
39
- },
40
- {
41
- "id": "what",
42
- "heading": "## What Changes",
43
- "required": true
44
- },
45
- {
46
- "id": "impact",
47
- "heading": "## Impact",
48
- "required": true
49
- },
50
- {
51
- "id": "boundaries",
52
- "heading": "## Boundaries",
53
- "required": false
54
- },
55
- {
56
- "id": "requirements",
57
- "heading": "## Requirements",
58
- "required": true
59
- }
60
- ],
61
- "placeholders": [
62
- "{功能名称}",
63
- "{描述}"
64
- ],
65
- "forbidden_terms": [
66
- "Phase",
67
- "Step",
68
- "阶段",
69
- "步骤"
70
- ]
71
- }
@@ -53,7 +53,7 @@
53
53
  },
54
54
  {
55
55
  "id": "steps",
56
- "heading": "## 四、实施 Task + 依赖图",
56
+ "heading": "## 四、实施 Task 概要",
57
57
  "required": true
58
58
  },
59
59
  {
@@ -1,30 +1,33 @@
1
1
  {
2
+ "_generated": true,
3
+ "_generated_source": "guard-rules.toml",
2
4
  "template": "tasks-template.md",
3
5
  "sections": [
4
6
  {
5
7
  "id": "round_deps",
6
- "heading": "## 轮次依赖",
7
- "required": true
8
+ "required": true,
9
+ "heading": "## 轮次依赖"
8
10
  },
9
11
  {
10
12
  "id": "plan_task_mapping",
11
- "heading": "## Plan→Task 映射",
12
- "required": true
13
+ "required": true,
14
+ "heading": "## Plan→Task 映射"
13
15
  },
14
16
  {
15
17
  "id": "round_1",
16
- "heading": "## 轮次 1:",
17
- "required": true
18
+ "required": true,
19
+ "heading": "## 轮次 1:"
18
20
  },
19
21
  {
20
22
  "id": "verification",
21
- "heading": "## Verification",
22
- "required": true
23
+ "required": true,
24
+ "heading": "## Verification"
23
25
  },
24
26
  {
25
27
  "id": "plan_track_note",
26
- "pattern": "plan_track\\(\\{ planName:",
27
- "required": true
28
+ "required": true,
29
+ "anchor": "plan_track",
30
+ "pattern": "plan_track\\(\\{ planName:"
28
31
  }
29
32
  ],
30
33
  "placeholders": [
@@ -38,50 +41,6 @@
38
41
  "forbidden_terms": [
39
42
  "Phase",
40
43
  "Round",
41
- "轮次",
42
44
  "阶段"
43
45
  ]
44
46
  }
45
- {
46
- "template": "tasks-template.md",
47
- "sections": [
48
- {
49
- "id": "preconditions",
50
- "heading": "## Preconditions",
51
- "required": true
52
- },
53
- {
54
- "id": "forbidden",
55
- "heading": "## Forbidden",
56
- "required": true
57
- },
58
- {
59
- "id": "tasks",
60
- "heading": "- [ ] Task",
61
- "required": true
62
- },
63
- {
64
- "id": "dependencies",
65
- "heading": "## Task Dependencies",
66
- "required": false
67
- },
68
- {
69
- "id": "verification",
70
- "heading": "## Verification",
71
- "required": true
72
- }
73
- ],
74
- "placeholders": [
75
- "{功能名称}",
76
- "{阶段名称}",
77
- "{任务描述}",
78
- "{子任务描述}"
79
- ],
80
- "forbidden_terms": [
81
- "Phase",
82
- "Step",
83
- "Round",
84
- "阶段",
85
- "步骤"
86
- ]
87
- }