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.
- package/README.md +36 -10
- package/dist/index.js +481 -209
- package/package.json +14 -4
- package/templates/.add-coder-src-hash.json +129 -106
- package/templates/adapters/claude/hooks/doc-format-guard.sh +114 -33
- package/templates/adapters/claude/hooks/lib/common.sh +46 -87
- package/templates/adapters/claude/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/claude/hooks/lib/notify.sh +6 -4
- package/templates/adapters/claude/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/claude/hooks/lib/state-detect.sh +8 -112
- package/templates/adapters/claude/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/claude/hooks/prompt-submit.sh +1 -1
- package/templates/adapters/claude/hooks/stop-check.sh +9 -0
- package/templates/adapters/codex/config.toml.example +12 -0
- package/templates/adapters/codex/hooks/doc-format-guard.sh +123 -41
- package/templates/adapters/codex/hooks/lib/common.sh +47 -99
- package/templates/adapters/codex/hooks/lib/context-inject.sh +4 -4
- package/templates/adapters/codex/hooks/lib/notify.sh +6 -4
- package/templates/adapters/codex/hooks/lib/preload-templates.sh +27 -6
- package/templates/adapters/codex/hooks/lib/state-detect.sh +5 -114
- package/templates/adapters/codex/hooks/lib/vocabulary.sh +3 -8
- package/templates/adapters/codex/hooks/notification.sh +3 -18
- package/templates/adapters/codex/hooks/post-tool-use.sh +24 -119
- package/templates/adapters/codex/hooks/pre-tool-use.sh +97 -184
- package/templates/adapters/codex/hooks/prompt-submit.sh +8 -3
- package/templates/adapters/codex/hooks/session-start.sh +19 -9
- package/templates/adapters/codex/hooks/stop-check.sh +44 -18
- package/templates/adapters/codex/hooks.json +67 -1
- package/templates/adapters/qoder/hooks/doc-format-guard.sh +121 -40
- package/templates/adapters/qoder/hooks/lib/common.sh +49 -87
- package/templates/adapters/qoder/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/qoder/hooks/lib/notify.sh +6 -4
- package/templates/adapters/qoder/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/qoder/hooks/lib/state-detect.sh +8 -112
- package/templates/adapters/qoder/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/qoder/hooks/prompt-submit.sh +1 -1
- package/templates/adapters/qoder/hooks/session-start.sh +1 -1
- package/templates/adapters/qoder/hooks/stop-check.sh +11 -0
- package/templates/adapters/trae/hooks/doc-format-guard.sh +122 -40
- package/templates/adapters/trae/hooks/lib/common.sh +46 -87
- package/templates/adapters/trae/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/trae/hooks/lib/notify.sh +5 -3
- package/templates/adapters/trae/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/trae/hooks/lib/state-detect.sh +8 -115
- package/templates/adapters/trae/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/trae/hooks/pre-tool-use.sh +14 -8
- package/templates/adapters/trae/hooks/stop-check.sh +9 -0
- package/templates/adapters/vscode/hooks/doc-format-guard.sh +114 -33
- package/templates/adapters/vscode/hooks/lib/common.sh +46 -87
- package/templates/adapters/vscode/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/vscode/hooks/lib/notify.sh +6 -4
- package/templates/adapters/vscode/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/vscode/hooks/lib/state-detect.sh +8 -112
- package/templates/adapters/vscode/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/vscode/hooks/notification.sh +3 -5
- package/templates/adapters/vscode/hooks/prompt-submit.sh +1 -1
- package/templates/adapters/vscode/hooks/stop-check.sh +9 -0
- package/templates/core/docs/ADD-governance-claude-code.md +1 -1
- package/templates/core/docs/ADD-governance-codex.md +1 -1
- package/templates/core/docs/ADD-governance-qoder-cn.md +1 -1
- package/templates/core/docs/ADD-governance-trae.md +1 -1
- package/templates/core/docs/ADD-governance-vscode-copilot.md +1 -1
- package/templates/core/hooks/doc-format-guard.sh +122 -40
- package/templates/core/hooks/lib/common.sh +46 -87
- package/templates/core/hooks/lib/context-inject.sh +2 -2
- package/templates/core/hooks/lib/notify.sh +5 -3
- package/templates/core/hooks/lib/preload-templates.sh +27 -2
- package/templates/core/hooks/lib/state-detect.sh +8 -115
- package/templates/core/hooks/lib/vocabulary.sh +13 -7
- package/templates/core/hooks/pre-tool-use.sh +14 -8
- package/templates/core/hooks/stop-check.sh +9 -0
- package/templates/core/prisma/add.prisma +140 -82
- package/templates/core/rules/project_rules.md +27 -24
- package/templates/core/scripts/db-ensure.sh +8 -0
- package/templates/core/scripts/gen-plan-index.sh +4 -15
- package/templates/core/scripts/mcp-server/notifications/hitl.ts +9 -1
- package/templates/core/scripts/mcp-server/notifications/hook.ts +25 -9
- package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +34 -0
- package/templates/core/scripts/mcp-server/resources/hook-events-report.ts +14 -2
- package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
- package/templates/core/scripts/mcp-server/sampling/review.ts +25 -10
- package/templates/core/scripts/mcp-server/shared/db-types.ts +33 -8
- package/templates/core/scripts/mcp-server/shared/dps-scoring.strategy.ts +1 -1
- package/templates/core/scripts/mcp-server/shared/env.ts +28 -8
- package/templates/core/scripts/mcp-server/shared/fs.ts +3 -0
- package/templates/core/scripts/mcp-server/shared/hitl-interaction.strategy.ts +2 -3
- package/templates/core/scripts/mcp-server/shared/hitl-lifecycle-mutation.ts +129 -0
- package/templates/core/scripts/mcp-server/shared/hitl-proposal-mutation.ts +98 -0
- package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +3 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-events.ts +62 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-mutation.ts +78 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-subscriber.ts +145 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +153 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-events.ts +56 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-mutation.ts +143 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-store.ts +47 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-subscriber.ts +99 -0
- package/templates/core/scripts/mcp-server/shared/plan-status-store.ts +37 -0
- package/templates/core/scripts/mcp-server/shared/plan-tracking-mutation.ts +71 -0
- package/templates/core/scripts/mcp-server/shared/prisma.ts +12 -6
- package/templates/core/scripts/mcp-server/shared/project-root-strategy.ts +1 -2
- package/templates/core/scripts/mcp-server/shared/quant-recheck.strategy.ts +16 -0
- package/templates/core/scripts/mcp-server/shared/redact.ts +28 -0
- package/templates/core/scripts/mcp-server/shared/response.ts +3 -2
- package/templates/core/scripts/mcp-server/shared/runtime-context.ts +77 -0
- package/templates/core/scripts/mcp-server/shared/schema-topology.ts +159 -0
- package/templates/core/scripts/mcp-server/tools/audit.ts +49 -7
- package/templates/core/scripts/mcp-server/tools/context.ts +117 -121
- package/templates/core/scripts/mcp-server/tools/contract.ts +34 -9
- package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +50 -3
- package/templates/core/scripts/mcp-server/tools/gateway/check_doc_similarity.ts +218 -0
- package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +22 -7
- package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +75 -4
- package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +19 -6
- package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -0
- package/templates/core/scripts/mcp-server/tools/hitl.ts +160 -75
- package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +9 -2
- package/templates/core/scripts/mcp-server/tools/index.ts +69 -3
- package/templates/core/scripts/mcp-server/tools/plan.ts +140 -15
- package/templates/core/scripts/mcp-server/tools/review.ts +44 -15
- package/templates/core/scripts/mcp-server.ts +60 -1
- package/templates/core/scripts/plan-status-bridge.ts +26 -0
- package/templates/core/skills/add-paradigm/SKILL.md +2 -2
- package/templates/core/templates/add-route-template.schema.json +24 -56
- package/templates/core/templates/collab-contract-template.md +1 -1
- package/templates/core/templates/handoff-multi-round-template.schema.json +48 -0
- package/templates/core/templates/handoff-single-round-template.schema.json +92 -0
- package/templates/core/templates/handoff-single-round.schema.json +1 -1
- package/templates/core/templates/hitl-approval-widget.html +222 -37
- package/templates/core/templates/spec-template.schema.json +9 -46
- package/templates/core/templates/standard-plan-template.schema.json +1 -1
- 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
|
+
}
|
|
@@ -1,16 +1,41 @@
|
|
|
1
1
|
<style>
|
|
2
2
|
* { box-sizing:border-box; margin:0; padding:0 }
|
|
3
|
-
body {
|
|
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:
|
|
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 {
|
|
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:
|
|
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 {
|
|
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"
|
|
72
|
-
<button class="btn btn-danger"
|
|
73
|
-
<button class="btn btn-outline"
|
|
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
|
-
//
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
|
|
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
|
-
'"
|
|
115
|
-
html += '<button class="btn-save-row" id="save_' + i + '"
|
|
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
|
-
'"
|
|
123
|
-
html += '<button class="btn-row adj' + (dec.action === 'adjust' ? ' active' : '') +
|
|
124
|
-
'"
|
|
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
|
|
253
|
+
return String(s ?? '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"')
|
|
145
254
|
}
|
|
146
255
|
|
|
147
256
|
function toggle(i, action) {
|
|
@@ -188,7 +297,69 @@ function resetAll() {
|
|
|
188
297
|
render()
|
|
189
298
|
}
|
|
190
299
|
|
|
191
|
-
|
|
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
|
|
201
|
-
msg.textContent =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
7
|
-
"
|
|
8
|
+
"required": true,
|
|
9
|
+
"heading": "## Plan→Spec 映射"
|
|
8
10
|
},
|
|
9
11
|
{
|
|
10
12
|
"id": "section_1",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
+
"required": true,
|
|
14
|
+
"heading": "## 1."
|
|
13
15
|
},
|
|
14
16
|
{
|
|
15
17
|
"id": "plan_track_note",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
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
|
-
}
|
|
@@ -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
|
-
"
|
|
7
|
-
"
|
|
8
|
+
"required": true,
|
|
9
|
+
"heading": "## 轮次依赖"
|
|
8
10
|
},
|
|
9
11
|
{
|
|
10
12
|
"id": "plan_task_mapping",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
+
"required": true,
|
|
14
|
+
"heading": "## Plan→Task 映射"
|
|
13
15
|
},
|
|
14
16
|
{
|
|
15
17
|
"id": "round_1",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
+
"required": true,
|
|
19
|
+
"heading": "## 轮次 1:"
|
|
18
20
|
},
|
|
19
21
|
{
|
|
20
22
|
"id": "verification",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
+
"required": true,
|
|
24
|
+
"heading": "## Verification"
|
|
23
25
|
},
|
|
24
26
|
{
|
|
25
27
|
"id": "plan_track_note",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
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
|
-
}
|