ma-agents 3.13.2 → 3.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/agents.js +9 -2
- package/lib/bmad-cache/bmb/.claude-plugin/marketplace.json +3 -3
- package/lib/bmad-cache/bmb/_git_preserved/index +0 -0
- package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-b388526edf307cf56f8e65673466cff074105abf.idx +0 -0
- package/lib/bmad-cache/bmb/_git_preserved/objects/pack/{pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.pack → pack-b388526edf307cf56f8e65673466cff074105abf.pack} +0 -0
- package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-b388526edf307cf56f8e65673466cff074105abf.rev +0 -0
- package/lib/bmad-cache/bmb/_git_preserved/packed-refs +1 -1
- package/lib/bmad-cache/bmb/_git_preserved/refs/heads/main +1 -1
- package/lib/bmad-cache/bmb/_git_preserved/shallow +1 -1
- package/lib/bmad-cache/bmb/package-lock.json +2 -2
- package/lib/bmad-cache/bmb/package.json +1 -1
- package/lib/bmad-cache/bmb/samples/bmad-agent-code-coach/scripts/wake.py +78 -0
- package/lib/bmad-cache/bmb/samples/bmad-agent-creative-muse/scripts/wake.py +78 -0
- package/lib/bmad-cache/bmb/samples/bmad-agent-sentinel/scripts/wake.py +78 -0
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/customize-template.toml +3 -0
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/init-sanctum-template.py +11 -5
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/report-shell.html +1073 -0
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/wake-template.py +78 -0
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/customize.toml +48 -0
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/count_tokens.py +78 -0
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass.py +258 -0
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/process-template.py +20 -0
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/render_report.py +387 -0
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/assets/adapter-claude-code.json +9 -0
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/aggregate_benchmark.py +236 -0
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/memlog.py +197 -0
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/run_evals.py +513 -390
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/run_triggers.py +347 -251
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/tests/test_env_isolation.py +79 -0
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/tests/test_trigger_detection.py +123 -0
- package/lib/bmad-cache/bmb/skills/bmad-module-builder/scripts/tests/test-validate-module.py +82 -4
- package/lib/bmad-cache/bmb/skills/bmad-module-builder/scripts/validate-module.py +55 -11
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/assets/customize-template.toml +3 -5
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/assets/report-shell.html +860 -0
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/customize.toml +59 -0
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/count_tokens.py +77 -0
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/init_skill.py +132 -0
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/prepass-prompt-metrics.py +186 -221
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/quick_validate.py +117 -0
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/render_report.py +387 -0
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/tests/test_canon_sync.py +46 -0
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/tests/test_count_tokens.py +180 -0
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/tests/test_render_report.py +171 -0
- package/lib/bmad-cache/cache-manifest.json +4 -4
- package/lib/bmad-customize/bmm-qa.customize.yaml +3 -3
- package/lib/bmad-extension/.claude-plugin/marketplace.json.template +3 -2
- package/lib/bmad-extension/skills/bmad-dev-epic/SKILL.md +101 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/checklist.md +44 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/customize.toml +54 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/references/story-pipeline.md +112 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-01-intake.md +59 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-02-plan-waves.md +56 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-03-epic-branch.md +35 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-04-run-waves.md +49 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-05-epic-adversarial.md +31 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-06-release.md +57 -0
- package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-07-report.md +40 -0
- package/lib/bmad-extension/skills/ma-agent-sqa/SKILL.md +3 -3
- package/lib/bmad-extension/skills/ma-agent-sqa/bmad-skill-manifest.yaml +1 -1
- package/lib/bmad-extension/skills/module-help.csv +2 -1
- package/lib/bmad-extension/skills/module.yaml +1 -1
- package/lib/bmad-extension/skills/sqa-audit/SKILL.md +2 -2
- package/lib/bmad-extension/skills/sqa-ieee12207/SKILL.md +3 -3
- package/lib/bmad-extension/skills/sqa-requirements-quality/SKILL.md +2 -2
- package/lib/bmad-extension-plugin/.claude-plugin/marketplace.json +4 -3
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/SKILL.md +101 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/bmad-skill-manifest.yaml +3 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/checklist.md +44 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/customize.toml +54 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/references/story-pipeline.md +112 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-01-intake.md +59 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-02-plan-waves.md +56 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-03-epic-branch.md +35 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-04-run-waves.md +49 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-05-epic-adversarial.md +31 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-06-release.md +57 -0
- package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-07-report.md +40 -0
- package/lib/bmad-extension-plugin/skills/ma-agent-sqa/SKILL.md +3 -3
- package/lib/bmad-extension-plugin/skills/ma-agent-sqa/bmad-skill-manifest.yaml +1 -1
- package/lib/bmad-extension-plugin/skills/module-help.csv +2 -1
- package/lib/bmad-extension-plugin/skills/module.yaml +1 -1
- package/lib/bmad-extension-plugin/skills/sqa-audit/SKILL.md +2 -2
- package/lib/bmad-extension-plugin/skills/sqa-ieee12207/SKILL.md +3 -3
- package/lib/bmad-extension-plugin/skills/sqa-requirements-quality/SKILL.md +2 -2
- package/lib/bmad.js +6 -1
- package/package.json +3 -3
- package/lib/.bmad-extension-plugin.build-22612-1782454014750/.claude-plugin/marketplace.json +0 -109
- package/lib/.bmad-extension-plugin.build-22612-1782454014750/skills/module-help.csv +0 -62
- package/lib/.bmad-extension-plugin.build-22612-1782454014750/skills/module.yaml +0 -20
- package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.idx +0 -0
- package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.rev +0 -0
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/references/sample-init-sanctum.py +0 -274
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/generate-html-report.py +0 -534
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-execution-deps.py +0 -337
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-prompt-metrics.py +0 -425
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-sanctum-architecture.py +0 -385
- package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py +0 -482
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/assets/Dockerfile +0 -29
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/docker_setup.py +0 -115
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/generate_report.py +0 -184
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/pty_runner.py +0 -171
- package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/utils.py +0 -260
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/extract-report-json.py +0 -287
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/generate-html-report.py +0 -588
- package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py +0 -288
|
@@ -0,0 +1,1073 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Agent Analysis Report</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--bg: #0f1b2d;
|
|
10
|
+
--panel: #16263d;
|
|
11
|
+
--panel-2: #1d3250;
|
|
12
|
+
--ink: #e9eef6;
|
|
13
|
+
--ink-dim: #9fb0c7;
|
|
14
|
+
--line: #294366;
|
|
15
|
+
--accent: #b66d46;
|
|
16
|
+
--accent-ink: #f4d9c8;
|
|
17
|
+
--critical: #e05656;
|
|
18
|
+
--high: #e0904a;
|
|
19
|
+
--medium: #d8c24a;
|
|
20
|
+
--low: #5aa0d0;
|
|
21
|
+
--ok: #4caf72;
|
|
22
|
+
}
|
|
23
|
+
* { box-sizing: border-box; }
|
|
24
|
+
body {
|
|
25
|
+
margin: 0;
|
|
26
|
+
background: var(--bg);
|
|
27
|
+
color: var(--ink);
|
|
28
|
+
font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
29
|
+
}
|
|
30
|
+
.wrap { max-width: 980px; margin: 0 auto; padding: 28px 20px 80px; }
|
|
31
|
+
header h1 { font-size: 22px; margin: 0 0 4px; }
|
|
32
|
+
header .meta { color: var(--ink-dim); font-size: 13px; }
|
|
33
|
+
header .meta b { color: var(--ink); font-weight: 600; }
|
|
34
|
+
|
|
35
|
+
.banner {
|
|
36
|
+
display: none;
|
|
37
|
+
background: #3a1414;
|
|
38
|
+
border: 1px solid var(--critical);
|
|
39
|
+
color: #ffd9d9;
|
|
40
|
+
padding: 14px 16px;
|
|
41
|
+
border-radius: 8px;
|
|
42
|
+
margin: 16px 0;
|
|
43
|
+
white-space: pre-wrap;
|
|
44
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
45
|
+
font-size: 13px;
|
|
46
|
+
}
|
|
47
|
+
.banner.show { display: block; }
|
|
48
|
+
|
|
49
|
+
.overview {
|
|
50
|
+
background: var(--panel);
|
|
51
|
+
border: 1px solid var(--line);
|
|
52
|
+
border-radius: 10px;
|
|
53
|
+
padding: 18px;
|
|
54
|
+
margin: 18px 0;
|
|
55
|
+
}
|
|
56
|
+
.grade {
|
|
57
|
+
font-size: 34px;
|
|
58
|
+
font-weight: 800;
|
|
59
|
+
margin: 0 0 8px;
|
|
60
|
+
text-transform: capitalize;
|
|
61
|
+
}
|
|
62
|
+
.grade.g-excellent { color: var(--ok); }
|
|
63
|
+
.grade.g-good { color: var(--low); }
|
|
64
|
+
.grade.g-fair { color: var(--medium); }
|
|
65
|
+
.grade.g-poor { color: var(--critical); }
|
|
66
|
+
.verdict { font-size: 16px; font-weight: 600; margin: 0 0 14px; }
|
|
67
|
+
.summary { color: var(--ink-dim); margin: 0 0 14px; }
|
|
68
|
+
.counts { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
69
|
+
.pill {
|
|
70
|
+
display: inline-flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: 8px;
|
|
73
|
+
padding: 6px 12px;
|
|
74
|
+
border-radius: 999px;
|
|
75
|
+
background: var(--panel-2);
|
|
76
|
+
border: 1px solid var(--line);
|
|
77
|
+
font-size: 13px;
|
|
78
|
+
}
|
|
79
|
+
.pill .dot { width: 10px; height: 10px; border-radius: 50%; }
|
|
80
|
+
.pill .n { font-weight: 700; }
|
|
81
|
+
.dot.critical { background: var(--critical); }
|
|
82
|
+
.dot.high { background: var(--high); }
|
|
83
|
+
.dot.medium { background: var(--medium); }
|
|
84
|
+
.dot.low { background: var(--low); }
|
|
85
|
+
|
|
86
|
+
/* Agent portrait (agent_profile block) */
|
|
87
|
+
.portrait {
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
gap: 16px;
|
|
91
|
+
background: var(--panel);
|
|
92
|
+
border: 1px solid var(--line);
|
|
93
|
+
border-radius: 10px;
|
|
94
|
+
padding: 18px;
|
|
95
|
+
margin: 18px 0;
|
|
96
|
+
}
|
|
97
|
+
.portrait .icon {
|
|
98
|
+
flex: 0 0 auto;
|
|
99
|
+
width: 56px;
|
|
100
|
+
height: 56px;
|
|
101
|
+
border-radius: 12px;
|
|
102
|
+
background: var(--panel-2);
|
|
103
|
+
border: 1px solid var(--line);
|
|
104
|
+
display: flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
justify-content: center;
|
|
107
|
+
font-size: 30px;
|
|
108
|
+
}
|
|
109
|
+
.portrait .who { flex: 1 1 auto; min-width: 0; }
|
|
110
|
+
.portrait .who .name { font-size: 18px; font-weight: 700; }
|
|
111
|
+
.portrait .who .title { color: var(--ink-dim); font-size: 13px; margin-top: 1px; }
|
|
112
|
+
.portrait .who .mission { margin-top: 8px; }
|
|
113
|
+
.portrait .who .type {
|
|
114
|
+
display: inline-block;
|
|
115
|
+
margin-top: 8px;
|
|
116
|
+
font-size: 11px;
|
|
117
|
+
text-transform: uppercase;
|
|
118
|
+
letter-spacing: 0.04em;
|
|
119
|
+
padding: 3px 8px;
|
|
120
|
+
border-radius: 6px;
|
|
121
|
+
background: var(--panel-2);
|
|
122
|
+
border: 1px solid var(--accent);
|
|
123
|
+
color: var(--accent-ink);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Generic agent-block / synthesis panel */
|
|
127
|
+
.block {
|
|
128
|
+
background: var(--panel);
|
|
129
|
+
border: 1px solid var(--line);
|
|
130
|
+
border-radius: 10px;
|
|
131
|
+
padding: 18px;
|
|
132
|
+
margin: 18px 0;
|
|
133
|
+
}
|
|
134
|
+
.block > h2 {
|
|
135
|
+
font-size: 13px;
|
|
136
|
+
text-transform: uppercase;
|
|
137
|
+
letter-spacing: 0.06em;
|
|
138
|
+
color: var(--ink-dim);
|
|
139
|
+
margin: 0 0 12px;
|
|
140
|
+
}
|
|
141
|
+
.cap-list { list-style: none; margin: 0; padding: 0; }
|
|
142
|
+
.cap-list li {
|
|
143
|
+
display: flex;
|
|
144
|
+
align-items: baseline;
|
|
145
|
+
gap: 10px;
|
|
146
|
+
padding: 8px 0;
|
|
147
|
+
border-top: 1px solid var(--line);
|
|
148
|
+
}
|
|
149
|
+
.cap-list li:first-child { border-top: none; }
|
|
150
|
+
.cap-list .cap-name { font-weight: 600; flex: 0 0 auto; }
|
|
151
|
+
.cap-list .cap-kind {
|
|
152
|
+
flex: 0 0 auto;
|
|
153
|
+
font-size: 11px;
|
|
154
|
+
text-transform: uppercase;
|
|
155
|
+
letter-spacing: 0.04em;
|
|
156
|
+
padding: 2px 7px;
|
|
157
|
+
border-radius: 6px;
|
|
158
|
+
background: var(--panel-2);
|
|
159
|
+
border: 1px solid var(--line);
|
|
160
|
+
color: var(--ink-dim);
|
|
161
|
+
}
|
|
162
|
+
.cap-list .cap-note { color: var(--ink-dim); flex: 1 1 auto; min-width: 0; }
|
|
163
|
+
.kv { margin: 0; display: grid; grid-template-columns: 150px 1fr; gap: 6px 14px; }
|
|
164
|
+
.kv dt { color: var(--ink-dim); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
165
|
+
.kv dd { margin: 0; }
|
|
166
|
+
.block .journey { padding: 8px 0; border-top: 1px solid var(--line); }
|
|
167
|
+
.block .journey:first-of-type { border-top: none; padding-top: 0; }
|
|
168
|
+
.block .journey .j-name { font-weight: 600; }
|
|
169
|
+
.block .journey .j-steps { color: var(--ink-dim); margin-top: 2px; }
|
|
170
|
+
.block .mono, .block code {
|
|
171
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
172
|
+
font-size: 13px;
|
|
173
|
+
background: var(--panel-2);
|
|
174
|
+
padding: 1px 5px;
|
|
175
|
+
border-radius: 4px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Themes */
|
|
179
|
+
.theme { padding: 12px 0; border-top: 1px solid var(--line); }
|
|
180
|
+
.theme:first-of-type { border-top: none; padding-top: 0; }
|
|
181
|
+
.theme .t-head { display: flex; align-items: center; gap: 10px; }
|
|
182
|
+
.theme .t-title { font-weight: 600; flex: 1 1 auto; min-width: 0; }
|
|
183
|
+
.theme .t-cause { color: var(--ink-dim); margin-top: 4px; }
|
|
184
|
+
.theme .t-action { margin-top: 4px; }
|
|
185
|
+
.theme .t-findings { margin-top: 8px; padding-left: 12px; border-left: 2px solid var(--line); }
|
|
186
|
+
.theme .t-finding { font-size: 13px; color: var(--ink-dim); padding: 2px 0; }
|
|
187
|
+
|
|
188
|
+
/* Strengths */
|
|
189
|
+
.strength-list { margin: 0; padding-left: 20px; }
|
|
190
|
+
.strength-list li { padding: 2px 0; }
|
|
191
|
+
|
|
192
|
+
/* Recommendations */
|
|
193
|
+
.rec { padding: 8px 0; border-top: 1px solid var(--line); }
|
|
194
|
+
.rec:first-of-type { border-top: none; padding-top: 0; }
|
|
195
|
+
.rec .rank { font-weight: 700; color: var(--accent-ink); margin-right: 8px; }
|
|
196
|
+
.rec .resolves { color: var(--ink-dim); font-size: 12px; margin-left: 8px; }
|
|
197
|
+
|
|
198
|
+
.toolbar {
|
|
199
|
+
display: flex;
|
|
200
|
+
align-items: center;
|
|
201
|
+
gap: 12px;
|
|
202
|
+
margin: 18px 0 10px;
|
|
203
|
+
flex-wrap: wrap;
|
|
204
|
+
}
|
|
205
|
+
.toolbar .sel-count { color: var(--ink-dim); font-size: 13px; }
|
|
206
|
+
button {
|
|
207
|
+
font: inherit;
|
|
208
|
+
cursor: pointer;
|
|
209
|
+
border-radius: 8px;
|
|
210
|
+
border: 1px solid var(--line);
|
|
211
|
+
background: var(--panel-2);
|
|
212
|
+
color: var(--ink);
|
|
213
|
+
padding: 8px 14px;
|
|
214
|
+
}
|
|
215
|
+
button.primary {
|
|
216
|
+
background: var(--accent);
|
|
217
|
+
border-color: var(--accent);
|
|
218
|
+
color: #1a0e07;
|
|
219
|
+
font-weight: 600;
|
|
220
|
+
}
|
|
221
|
+
button:disabled { opacity: 0.5; cursor: default; }
|
|
222
|
+
button.link {
|
|
223
|
+
background: none;
|
|
224
|
+
border: none;
|
|
225
|
+
color: var(--accent-ink);
|
|
226
|
+
padding: 4px 6px;
|
|
227
|
+
font-size: 13px;
|
|
228
|
+
}
|
|
229
|
+
button.small { padding: 5px 10px; font-size: 13px; flex: 0 0 auto; }
|
|
230
|
+
|
|
231
|
+
.no-findings {
|
|
232
|
+
background: var(--panel);
|
|
233
|
+
border: 1px dashed var(--line);
|
|
234
|
+
border-radius: 10px;
|
|
235
|
+
padding: 28px;
|
|
236
|
+
text-align: center;
|
|
237
|
+
color: var(--ink-dim);
|
|
238
|
+
}
|
|
239
|
+
.no-findings .big { font-size: 18px; color: var(--ok); margin-bottom: 6px; }
|
|
240
|
+
|
|
241
|
+
.group { margin: 18px 0; }
|
|
242
|
+
.group > h2 {
|
|
243
|
+
font-size: 13px;
|
|
244
|
+
text-transform: uppercase;
|
|
245
|
+
letter-spacing: 0.06em;
|
|
246
|
+
color: var(--ink-dim);
|
|
247
|
+
margin: 0 0 8px;
|
|
248
|
+
display: flex;
|
|
249
|
+
align-items: center;
|
|
250
|
+
gap: 8px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.finding {
|
|
254
|
+
background: var(--panel);
|
|
255
|
+
border: 1px solid var(--line);
|
|
256
|
+
border-left: 4px solid var(--line);
|
|
257
|
+
border-radius: 8px;
|
|
258
|
+
margin: 8px 0;
|
|
259
|
+
overflow: hidden;
|
|
260
|
+
}
|
|
261
|
+
.finding.sev-critical { border-left-color: var(--critical); }
|
|
262
|
+
.finding.sev-high { border-left-color: var(--high); }
|
|
263
|
+
.finding.sev-medium { border-left-color: var(--medium); }
|
|
264
|
+
.finding.sev-low { border-left-color: var(--low); }
|
|
265
|
+
|
|
266
|
+
.finding .row {
|
|
267
|
+
display: flex;
|
|
268
|
+
align-items: center;
|
|
269
|
+
gap: 12px;
|
|
270
|
+
padding: 12px 14px;
|
|
271
|
+
}
|
|
272
|
+
.finding .row .chk { width: 16px; height: 16px; flex: 0 0 auto; cursor: pointer; }
|
|
273
|
+
.finding .row .head { flex: 1 1 auto; cursor: pointer; min-width: 0; }
|
|
274
|
+
.finding .row .title { font-weight: 600; }
|
|
275
|
+
.finding .row .sub { color: var(--ink-dim); font-size: 12px; margin-top: 2px; }
|
|
276
|
+
.finding .tag {
|
|
277
|
+
flex: 0 0 auto;
|
|
278
|
+
font-size: 11px;
|
|
279
|
+
text-transform: uppercase;
|
|
280
|
+
letter-spacing: 0.04em;
|
|
281
|
+
padding: 3px 8px;
|
|
282
|
+
border-radius: 6px;
|
|
283
|
+
background: var(--panel-2);
|
|
284
|
+
border: 1px solid var(--line);
|
|
285
|
+
color: var(--ink-dim);
|
|
286
|
+
}
|
|
287
|
+
.finding .caret { flex: 0 0 auto; color: var(--ink-dim); transition: transform 0.15s; cursor: pointer; }
|
|
288
|
+
.finding.open .caret { transform: rotate(90deg); }
|
|
289
|
+
|
|
290
|
+
.finding .body {
|
|
291
|
+
display: none;
|
|
292
|
+
padding: 0 14px 14px 42px;
|
|
293
|
+
border-top: 1px solid var(--line);
|
|
294
|
+
}
|
|
295
|
+
.finding.open .body { display: block; }
|
|
296
|
+
.finding .body dl { margin: 12px 0 0; display: grid; grid-template-columns: 130px 1fr; gap: 6px 14px; }
|
|
297
|
+
.finding .body dt { color: var(--ink-dim); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
298
|
+
.finding .body dd { margin: 0; }
|
|
299
|
+
.finding .body code, .finding .body .mono {
|
|
300
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
301
|
+
font-size: 13px;
|
|
302
|
+
background: var(--panel-2);
|
|
303
|
+
padding: 1px 5px;
|
|
304
|
+
border-radius: 4px;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.toast {
|
|
308
|
+
position: fixed;
|
|
309
|
+
left: 50%;
|
|
310
|
+
bottom: 28px;
|
|
311
|
+
transform: translateX(-50%);
|
|
312
|
+
background: var(--ok);
|
|
313
|
+
color: #06160c;
|
|
314
|
+
padding: 10px 18px;
|
|
315
|
+
border-radius: 8px;
|
|
316
|
+
font-weight: 600;
|
|
317
|
+
opacity: 0;
|
|
318
|
+
transition: opacity 0.2s;
|
|
319
|
+
pointer-events: none;
|
|
320
|
+
}
|
|
321
|
+
.toast.show { opacity: 1; }
|
|
322
|
+
|
|
323
|
+
.fallback-area { margin-top: 12px; display: none; }
|
|
324
|
+
.fallback-area.show { display: block; }
|
|
325
|
+
.fallback-area textarea {
|
|
326
|
+
width: 100%;
|
|
327
|
+
min-height: 160px;
|
|
328
|
+
background: var(--panel-2);
|
|
329
|
+
color: var(--ink);
|
|
330
|
+
border: 1px solid var(--line);
|
|
331
|
+
border-radius: 8px;
|
|
332
|
+
padding: 10px;
|
|
333
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
334
|
+
font-size: 13px;
|
|
335
|
+
}
|
|
336
|
+
</style>
|
|
337
|
+
</head>
|
|
338
|
+
<body>
|
|
339
|
+
<div class="wrap">
|
|
340
|
+
<header>
|
|
341
|
+
<h1>Agent Analysis Report</h1>
|
|
342
|
+
<div class="meta">
|
|
343
|
+
<span>Subject: <b id="m-subject">—</b></span> ·
|
|
344
|
+
<span>Generated: <b id="m-generated">—</b></span> ·
|
|
345
|
+
<span>Schema: <b id="m-schema">—</b></span>
|
|
346
|
+
</div>
|
|
347
|
+
</header>
|
|
348
|
+
|
|
349
|
+
<div id="parse-banner" class="banner"></div>
|
|
350
|
+
|
|
351
|
+
<!-- Agent portrait (agent_profile). Hidden when the block is absent. -->
|
|
352
|
+
<section id="portrait" class="portrait" hidden></section>
|
|
353
|
+
|
|
354
|
+
<section id="overview" class="overview" hidden>
|
|
355
|
+
<div id="grade" class="grade" hidden></div>
|
|
356
|
+
<p id="verdict" class="verdict"></p>
|
|
357
|
+
<p id="summary-text" class="summary" hidden></p>
|
|
358
|
+
<div id="counts" class="counts"></div>
|
|
359
|
+
</section>
|
|
360
|
+
|
|
361
|
+
<!-- Capability dashboard (capabilities). Hidden when absent. -->
|
|
362
|
+
<section id="capabilities" class="block" hidden></section>
|
|
363
|
+
|
|
364
|
+
<!-- Per-lens verdicts (detailed_analysis). Hidden when absent. -->
|
|
365
|
+
<section id="lens-verdicts" class="block" hidden></section>
|
|
366
|
+
|
|
367
|
+
<!-- Sanctum block (sanctum). Conditional; hidden for stateless agents. -->
|
|
368
|
+
<section id="sanctum" class="block" hidden></section>
|
|
369
|
+
|
|
370
|
+
<!-- Experience: journeys plus headless (experience). Hidden when absent. -->
|
|
371
|
+
<section id="experience" class="block" hidden></section>
|
|
372
|
+
|
|
373
|
+
<!-- Synthesis layer (themes, strengths, recommendations). Hidden when absent. -->
|
|
374
|
+
<section id="themes" class="block" hidden></section>
|
|
375
|
+
<section id="strengths" class="block" hidden></section>
|
|
376
|
+
<section id="recommendations" class="block" hidden></section>
|
|
377
|
+
|
|
378
|
+
<div id="toolbar" class="toolbar" hidden>
|
|
379
|
+
<button id="btn-copy" class="primary" disabled>Copy selected as paste-back prompt</button>
|
|
380
|
+
<span id="sel-count" class="sel-count">0 selected</span>
|
|
381
|
+
<button id="btn-select-all" class="link">Select all</button>
|
|
382
|
+
<button id="btn-clear" class="link">Clear</button>
|
|
383
|
+
<button id="btn-expand-all" class="link">Expand all</button>
|
|
384
|
+
<button id="btn-collapse-all" class="link">Collapse all</button>
|
|
385
|
+
</div>
|
|
386
|
+
|
|
387
|
+
<div id="fallback" class="fallback-area">
|
|
388
|
+
<p class="sel-count">Clipboard was unavailable. Copy the text below manually:</p>
|
|
389
|
+
<textarea id="fallback-text" readonly></textarea>
|
|
390
|
+
</div>
|
|
391
|
+
|
|
392
|
+
<div id="findings-root"></div>
|
|
393
|
+
</div>
|
|
394
|
+
|
|
395
|
+
<div id="toast" class="toast">Copied</div>
|
|
396
|
+
|
|
397
|
+
<!-- scripts/render_report.py replaces the contents of this island per run.
|
|
398
|
+
The placeholder below is intentionally unusable: the shell refuses to
|
|
399
|
+
render it, so a failed injection can never look like real findings. -->
|
|
400
|
+
<script type="application/json" id="report-data">
|
|
401
|
+
{
|
|
402
|
+
"schema_version": 2,
|
|
403
|
+
"subject": "__PLACEHOLDER__",
|
|
404
|
+
"generated": "",
|
|
405
|
+
"verdict": "",
|
|
406
|
+
"findings": []
|
|
407
|
+
}
|
|
408
|
+
</script>
|
|
409
|
+
|
|
410
|
+
<script>
|
|
411
|
+
(function () {
|
|
412
|
+
"use strict";
|
|
413
|
+
|
|
414
|
+
var SEVERITIES = ["critical", "high", "medium", "low"];
|
|
415
|
+
var SEV_LABEL = { critical: "Critical", high: "High", medium: "Medium", low: "Low" };
|
|
416
|
+
var GRADES = ["excellent", "good", "fair", "poor"];
|
|
417
|
+
var PLACEHOLDER_SUBJECT = "__PLACEHOLDER__";
|
|
418
|
+
|
|
419
|
+
var els = {
|
|
420
|
+
banner: document.getElementById("parse-banner"),
|
|
421
|
+
portrait: document.getElementById("portrait"),
|
|
422
|
+
overview: document.getElementById("overview"),
|
|
423
|
+
grade: document.getElementById("grade"),
|
|
424
|
+
verdict: document.getElementById("verdict"),
|
|
425
|
+
summaryText: document.getElementById("summary-text"),
|
|
426
|
+
counts: document.getElementById("counts"),
|
|
427
|
+
capabilities: document.getElementById("capabilities"),
|
|
428
|
+
lensVerdicts: document.getElementById("lens-verdicts"),
|
|
429
|
+
sanctum: document.getElementById("sanctum"),
|
|
430
|
+
experience: document.getElementById("experience"),
|
|
431
|
+
themes: document.getElementById("themes"),
|
|
432
|
+
strengths: document.getElementById("strengths"),
|
|
433
|
+
recommendations: document.getElementById("recommendations"),
|
|
434
|
+
toolbar: document.getElementById("toolbar"),
|
|
435
|
+
root: document.getElementById("findings-root"),
|
|
436
|
+
subject: document.getElementById("m-subject"),
|
|
437
|
+
generated: document.getElementById("m-generated"),
|
|
438
|
+
schema: document.getElementById("m-schema"),
|
|
439
|
+
selCount: document.getElementById("sel-count"),
|
|
440
|
+
btnCopy: document.getElementById("btn-copy"),
|
|
441
|
+
btnSelectAll: document.getElementById("btn-select-all"),
|
|
442
|
+
btnClear: document.getElementById("btn-clear"),
|
|
443
|
+
btnExpandAll: document.getElementById("btn-expand-all"),
|
|
444
|
+
btnCollapseAll: document.getElementById("btn-collapse-all"),
|
|
445
|
+
fallback: document.getElementById("fallback"),
|
|
446
|
+
fallbackText: document.getElementById("fallback-text"),
|
|
447
|
+
toast: document.getElementById("toast")
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
var selected = Object.create(null);
|
|
451
|
+
var findings = [];
|
|
452
|
+
var findingsById = Object.create(null);
|
|
453
|
+
var subjectPath = "";
|
|
454
|
+
var standards = null;
|
|
455
|
+
|
|
456
|
+
function showBanner(message) {
|
|
457
|
+
els.banner.textContent = message;
|
|
458
|
+
els.banner.classList.add("show");
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function esc(value) {
|
|
462
|
+
var s = value == null ? "" : String(value);
|
|
463
|
+
return s.replace(/[&<>"']/g, function (c) {
|
|
464
|
+
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c];
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Normalize an arbitrary parsed object against schema_version 2, supplying
|
|
469
|
+
// defaults so a partial or future island still renders. Unknown fields are
|
|
470
|
+
// ignored, not fatal. Severity counts are always derived from the findings
|
|
471
|
+
// array, never read from the island, so they cannot disagree with it. The
|
|
472
|
+
// agent blocks (agent_profile, capabilities, detailed_analysis, sanctum,
|
|
473
|
+
// experience) and the synthesis blocks (grade, summary, themes, strengths,
|
|
474
|
+
// recommendations) are OPTIONAL: each normalizes to an empty value that
|
|
475
|
+
// renders nothing rather than an empty panel or an error.
|
|
476
|
+
function normalize(raw) {
|
|
477
|
+
var obj = raw && typeof raw === "object" ? raw : {};
|
|
478
|
+
var rawFindings = Array.isArray(obj.findings) ? obj.findings : [];
|
|
479
|
+
|
|
480
|
+
var norm = {
|
|
481
|
+
schema_version: typeof obj.schema_version === "number" ? obj.schema_version : 2,
|
|
482
|
+
subject: obj.subject != null ? String(obj.subject) : "(unspecified)",
|
|
483
|
+
generated: obj.generated != null ? String(obj.generated) : "(unspecified)",
|
|
484
|
+
verdict: obj.verdict != null ? String(obj.verdict) : "(no verdict supplied)",
|
|
485
|
+
grade: GRADES.indexOf(String(obj.grade || "").toLowerCase()) >= 0
|
|
486
|
+
? String(obj.grade).toLowerCase() : "",
|
|
487
|
+
summary: typeof obj.summary === "string" ? obj.summary : "",
|
|
488
|
+
standards: (obj.standards && typeof obj.standards === "object") ? {
|
|
489
|
+
canon: obj.standards.canon != null ? String(obj.standards.canon) : "",
|
|
490
|
+
principles: obj.standards.principles != null ? String(obj.standards.principles) : "",
|
|
491
|
+
scripts: obj.standards.scripts != null ? String(obj.standards.scripts) : ""
|
|
492
|
+
} : null,
|
|
493
|
+
themes: normalizeThemes(obj.themes),
|
|
494
|
+
strengths: normalizeStrengths(obj.strengths),
|
|
495
|
+
recommendations: normalizeRecommendations(obj.recommendations),
|
|
496
|
+
counts: { critical: 0, high: 0, medium: 0, low: 0 },
|
|
497
|
+
findings: [],
|
|
498
|
+
agent_profile: normalizeProfile(obj.agent_profile),
|
|
499
|
+
capabilities: normalizeCapabilities(obj.capabilities),
|
|
500
|
+
detailed_analysis: normalizeDetailed(obj.detailed_analysis),
|
|
501
|
+
sanctum: normalizeSanctum(obj.sanctum),
|
|
502
|
+
experience: normalizeExperience(obj.experience)
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
rawFindings.forEach(function (f, i) {
|
|
506
|
+
if (!f || typeof f !== "object") { return; }
|
|
507
|
+
var sev = SEVERITIES.indexOf(f.severity) >= 0 ? f.severity : "low";
|
|
508
|
+
norm.findings.push({
|
|
509
|
+
id: f.id != null ? String(f.id) : "finding-" + (i + 1),
|
|
510
|
+
lens: f.lens != null ? String(f.lens) : "(unknown)",
|
|
511
|
+
severity: sev,
|
|
512
|
+
title: f.title != null ? String(f.title) : "(untitled finding)",
|
|
513
|
+
location: f.location != null ? String(f.location) : "",
|
|
514
|
+
evidence: f.evidence != null ? String(f.evidence) : "",
|
|
515
|
+
recommendation: f.recommendation != null ? String(f.recommendation) : "",
|
|
516
|
+
proposed_smallest: f.proposed_smallest != null ? String(f.proposed_smallest) : "",
|
|
517
|
+
predicted_delta: f.predicted_delta != null ? String(f.predicted_delta) : ""
|
|
518
|
+
});
|
|
519
|
+
norm.counts[sev] += 1;
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
return norm;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function normalizeThemes(raw) {
|
|
526
|
+
if (!Array.isArray(raw)) { return []; }
|
|
527
|
+
var list = [];
|
|
528
|
+
raw.forEach(function (t) {
|
|
529
|
+
if (!t || typeof t !== "object") { return; }
|
|
530
|
+
var ids = [];
|
|
531
|
+
if (Array.isArray(t.finding_ids)) {
|
|
532
|
+
t.finding_ids.forEach(function (id) { if (id != null) { ids.push(String(id)); } });
|
|
533
|
+
}
|
|
534
|
+
var title = t.title != null ? String(t.title) : "";
|
|
535
|
+
if (!title && !ids.length) { return; }
|
|
536
|
+
list.push({
|
|
537
|
+
title: title || "(untitled theme)",
|
|
538
|
+
root_cause: t.root_cause != null ? String(t.root_cause) : "",
|
|
539
|
+
action: t.action != null ? String(t.action) : "",
|
|
540
|
+
finding_ids: ids
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
return list;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function normalizeStrengths(raw) {
|
|
547
|
+
if (!Array.isArray(raw)) { return []; }
|
|
548
|
+
var list = [];
|
|
549
|
+
raw.forEach(function (s) {
|
|
550
|
+
if (typeof s === "string" && s) { list.push(s); }
|
|
551
|
+
else if (s && typeof s === "object" && s.title) {
|
|
552
|
+
list.push(String(s.title) + (s.detail ? " — " + String(s.detail) : ""));
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
return list;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function normalizeRecommendations(raw) {
|
|
559
|
+
if (!Array.isArray(raw)) { return []; }
|
|
560
|
+
var list = [];
|
|
561
|
+
raw.forEach(function (r, i) {
|
|
562
|
+
if (!r || typeof r !== "object") { return; }
|
|
563
|
+
var action = r.action != null ? String(r.action) : "";
|
|
564
|
+
if (!action) { return; }
|
|
565
|
+
var resolves = "";
|
|
566
|
+
if (Array.isArray(r.resolves)) { resolves = r.resolves.map(String).join(", "); }
|
|
567
|
+
else if (typeof r.resolves === "number") { resolves = r.resolves + " findings"; }
|
|
568
|
+
else if (r.resolves != null) { resolves = String(r.resolves); }
|
|
569
|
+
list.push({
|
|
570
|
+
rank: typeof r.rank === "number" ? r.rank : i + 1,
|
|
571
|
+
action: action,
|
|
572
|
+
resolves: resolves
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
list.sort(function (a, b) { return a.rank - b.rank; });
|
|
576
|
+
return list;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Optional agent_profile portrait: name/title/icon/agent_type/mission.
|
|
580
|
+
// Returns null when nothing usable is present, so the portrait stays hidden.
|
|
581
|
+
function normalizeProfile(raw) {
|
|
582
|
+
if (!raw || typeof raw !== "object") { return null; }
|
|
583
|
+
var p = {
|
|
584
|
+
name: raw.name != null ? String(raw.name) : "",
|
|
585
|
+
title: raw.title != null ? String(raw.title) : "",
|
|
586
|
+
icon: raw.icon != null ? String(raw.icon) : "",
|
|
587
|
+
agent_type: raw.agent_type != null ? String(raw.agent_type) : "",
|
|
588
|
+
mission: raw.mission != null ? String(raw.mission) : ""
|
|
589
|
+
};
|
|
590
|
+
if (!p.name && !p.title && !p.mission && !p.agent_type) { return null; }
|
|
591
|
+
return p;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Optional capability dashboard: a list of { name, kind, note }.
|
|
595
|
+
// Returns null when empty or absent.
|
|
596
|
+
function normalizeCapabilities(raw) {
|
|
597
|
+
if (!Array.isArray(raw)) { return null; }
|
|
598
|
+
var list = [];
|
|
599
|
+
raw.forEach(function (c) {
|
|
600
|
+
if (!c || typeof c !== "object") { return; }
|
|
601
|
+
var name = c.name != null ? String(c.name) : "";
|
|
602
|
+
if (!name) { return; }
|
|
603
|
+
list.push({
|
|
604
|
+
name: name,
|
|
605
|
+
kind: c.kind != null ? String(c.kind) : "",
|
|
606
|
+
note: c.note != null ? String(c.note) : ""
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
return list.length ? list : null;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Optional detailed_analysis: a map of lens name to one-line verdict.
|
|
613
|
+
// Returns null when empty or absent.
|
|
614
|
+
function normalizeDetailed(raw) {
|
|
615
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) { return null; }
|
|
616
|
+
var entries = [];
|
|
617
|
+
Object.keys(raw).forEach(function (key) {
|
|
618
|
+
var value = raw[key];
|
|
619
|
+
if (value == null || typeof value === "object") { return; }
|
|
620
|
+
entries.push({ lens: key, verdict: String(value) });
|
|
621
|
+
});
|
|
622
|
+
return entries.length ? entries : null;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Optional sanctum block, shown only for memory/autonomous agents.
|
|
626
|
+
// Returns null when absent or explicitly marked not present.
|
|
627
|
+
function normalizeSanctum(raw) {
|
|
628
|
+
if (!raw || typeof raw !== "object") { return null; }
|
|
629
|
+
if (raw.present === false) { return null; }
|
|
630
|
+
var files = [];
|
|
631
|
+
if (Array.isArray(raw.files)) {
|
|
632
|
+
raw.files.forEach(function (f) { if (f != null) { files.push(String(f)); } });
|
|
633
|
+
}
|
|
634
|
+
var s = {
|
|
635
|
+
location: raw.location != null ? String(raw.location) : "",
|
|
636
|
+
files: files,
|
|
637
|
+
note: raw.note != null ? String(raw.note) : ""
|
|
638
|
+
};
|
|
639
|
+
if (!s.location && !s.files.length && !s.note) { return null; }
|
|
640
|
+
return s;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Optional experience block: journeys plus a headless note. Returns null when
|
|
644
|
+
// neither is usable.
|
|
645
|
+
function normalizeExperience(raw) {
|
|
646
|
+
if (!raw || typeof raw !== "object") { return null; }
|
|
647
|
+
var journeys = [];
|
|
648
|
+
if (Array.isArray(raw.journeys)) {
|
|
649
|
+
raw.journeys.forEach(function (j) {
|
|
650
|
+
if (!j || typeof j !== "object") { return; }
|
|
651
|
+
var name = j.name != null ? String(j.name) : "";
|
|
652
|
+
var steps = j.steps != null ? String(j.steps) : "";
|
|
653
|
+
if (!name && !steps) { return; }
|
|
654
|
+
journeys.push({ name: name, steps: steps });
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
var headless = raw.headless != null ? String(raw.headless) : "";
|
|
658
|
+
if (!journeys.length && !headless) { return null; }
|
|
659
|
+
return { journeys: journeys, headless: headless };
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function renderOverview(data) {
|
|
663
|
+
els.subject.textContent = data.subject;
|
|
664
|
+
els.generated.textContent = data.generated;
|
|
665
|
+
els.schema.textContent = String(data.schema_version);
|
|
666
|
+
els.verdict.textContent = data.verdict;
|
|
667
|
+
|
|
668
|
+
if (data.grade) {
|
|
669
|
+
els.grade.textContent = data.grade;
|
|
670
|
+
els.grade.className = "grade g-" + data.grade;
|
|
671
|
+
els.grade.hidden = false;
|
|
672
|
+
}
|
|
673
|
+
if (data.summary) {
|
|
674
|
+
els.summaryText.textContent = data.summary;
|
|
675
|
+
els.summaryText.hidden = false;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
els.counts.innerHTML = "";
|
|
679
|
+
SEVERITIES.forEach(function (s) {
|
|
680
|
+
var pill = document.createElement("span");
|
|
681
|
+
pill.className = "pill";
|
|
682
|
+
pill.innerHTML =
|
|
683
|
+
'<span class="dot ' + s + '"></span>' +
|
|
684
|
+
'<span class="lbl">' + SEV_LABEL[s] + '</span>' +
|
|
685
|
+
'<span class="n">' + data.counts[s] + "</span>";
|
|
686
|
+
els.counts.appendChild(pill);
|
|
687
|
+
});
|
|
688
|
+
els.overview.hidden = false;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// Each agent-block renderer leaves its section hidden when its data is null,
|
|
692
|
+
// so a stateless agent (no sanctum) or a minimal island never shows a blank
|
|
693
|
+
// panel and never throws.
|
|
694
|
+
function renderProfile(profile) {
|
|
695
|
+
if (!profile) { els.portrait.hidden = true; return; }
|
|
696
|
+
var typeTag = profile.agent_type
|
|
697
|
+
? '<span class="type">' + esc(profile.agent_type) + "</span>"
|
|
698
|
+
: "";
|
|
699
|
+
var mission = profile.mission
|
|
700
|
+
? '<div class="mission">' + esc(profile.mission) + "</div>"
|
|
701
|
+
: "";
|
|
702
|
+
els.portrait.innerHTML =
|
|
703
|
+
'<div class="icon">' + (profile.icon ? esc(profile.icon) : "🤖") + "</div>" +
|
|
704
|
+
'<div class="who">' +
|
|
705
|
+
'<div class="name">' + esc(profile.name || "(unnamed agent)") + "</div>" +
|
|
706
|
+
(profile.title ? '<div class="title">' + esc(profile.title) + "</div>" : "") +
|
|
707
|
+
mission +
|
|
708
|
+
typeTag +
|
|
709
|
+
"</div>";
|
|
710
|
+
els.portrait.hidden = false;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function renderCapabilities(list) {
|
|
714
|
+
if (!list) { els.capabilities.hidden = true; return; }
|
|
715
|
+
var items = list.map(function (c) {
|
|
716
|
+
return "<li>" +
|
|
717
|
+
'<span class="cap-name">' + esc(c.name) + "</span>" +
|
|
718
|
+
(c.kind ? '<span class="cap-kind">' + esc(c.kind) + "</span>" : "") +
|
|
719
|
+
(c.note ? '<span class="cap-note">' + esc(c.note) + "</span>" : "") +
|
|
720
|
+
"</li>";
|
|
721
|
+
}).join("");
|
|
722
|
+
els.capabilities.innerHTML =
|
|
723
|
+
"<h2>Capabilities</h2><ul class=\"cap-list\">" + items + "</ul>";
|
|
724
|
+
els.capabilities.hidden = false;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function renderLensVerdicts(entries) {
|
|
728
|
+
if (!entries) { els.lensVerdicts.hidden = true; return; }
|
|
729
|
+
var rows = entries.map(function (e) {
|
|
730
|
+
return "<dt>" + esc(e.lens) + "</dt><dd>" + esc(e.verdict) + "</dd>";
|
|
731
|
+
}).join("");
|
|
732
|
+
els.lensVerdicts.innerHTML =
|
|
733
|
+
"<h2>Per-lens verdicts</h2><dl class=\"kv\">" + rows + "</dl>";
|
|
734
|
+
els.lensVerdicts.hidden = false;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function renderSanctum(s) {
|
|
738
|
+
if (!s) { els.sanctum.hidden = true; return; }
|
|
739
|
+
var rows = "";
|
|
740
|
+
if (s.location) {
|
|
741
|
+
rows += "<dt>Location</dt><dd><code>" + esc(s.location) + "</code></dd>";
|
|
742
|
+
}
|
|
743
|
+
if (s.files.length) {
|
|
744
|
+
rows += "<dt>Sanctum files</dt><dd>" +
|
|
745
|
+
s.files.map(function (f) { return '<span class="mono">' + esc(f) + "</span>"; }).join(" ") +
|
|
746
|
+
"</dd>";
|
|
747
|
+
}
|
|
748
|
+
if (s.note) {
|
|
749
|
+
rows += "<dt>Note</dt><dd>" + esc(s.note) + "</dd>";
|
|
750
|
+
}
|
|
751
|
+
els.sanctum.innerHTML = "<h2>Sanctum (runtime memory)</h2><dl class=\"kv\">" + rows + "</dl>";
|
|
752
|
+
els.sanctum.hidden = false;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function renderExperience(exp) {
|
|
756
|
+
if (!exp) { els.experience.hidden = true; return; }
|
|
757
|
+
var html = "<h2>Experience</h2>";
|
|
758
|
+
if (exp.journeys.length) {
|
|
759
|
+
html += exp.journeys.map(function (j) {
|
|
760
|
+
return '<div class="journey">' +
|
|
761
|
+
'<div class="j-name">' + esc(j.name || "(unnamed journey)") + "</div>" +
|
|
762
|
+
(j.steps ? '<div class="j-steps">' + esc(j.steps) + "</div>" : "") +
|
|
763
|
+
"</div>";
|
|
764
|
+
}).join("");
|
|
765
|
+
}
|
|
766
|
+
if (exp.headless) {
|
|
767
|
+
html += '<dl class="kv" style="margin-top:12px"><dt>Headless</dt><dd>' +
|
|
768
|
+
esc(exp.headless) + "</dd></dl>";
|
|
769
|
+
}
|
|
770
|
+
els.experience.innerHTML = html;
|
|
771
|
+
els.experience.hidden = false;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// Every copied fix prompt opens by anchoring the fixing session to the same
|
|
775
|
+
// standards that produced the findings, so the fix is held to the bar too.
|
|
776
|
+
function standardsPreamble() {
|
|
777
|
+
if (!standards || !standards.canon) { return []; }
|
|
778
|
+
var bar = standards.canon + (standards.principles ? " and " + standards.principles : "");
|
|
779
|
+
var lines = [
|
|
780
|
+
"Hold " + bar + " as the bar for every line you change — a fix that adds ceremony is a new finding, not a fix."
|
|
781
|
+
];
|
|
782
|
+
if (standards.scripts) {
|
|
783
|
+
lines.push("If the fix adds or changes scripts, follow " + standards.scripts + ".");
|
|
784
|
+
}
|
|
785
|
+
lines.push("");
|
|
786
|
+
return lines;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function composeThemePrompt(theme, resolved) {
|
|
790
|
+
var lines = standardsPreamble();
|
|
791
|
+
lines.push("Fix the following theme in " + subjectPath + ": " + theme.title);
|
|
792
|
+
lines.push("");
|
|
793
|
+
if (theme.root_cause) { lines.push("Root cause: " + theme.root_cause); }
|
|
794
|
+
if (theme.action) { lines.push("Fix: " + theme.action); }
|
|
795
|
+
if (resolved.length) {
|
|
796
|
+
lines.push("");
|
|
797
|
+
lines.push("Findings to address:");
|
|
798
|
+
resolved.forEach(function (f, i) {
|
|
799
|
+
lines.push((i + 1) + ". " + f.title);
|
|
800
|
+
if (f.location) { lines.push(" Location: " + f.location); }
|
|
801
|
+
if (f.evidence) { lines.push(" Evidence: " + f.evidence); }
|
|
802
|
+
if (f.recommendation) { lines.push(" Recommendation: " + f.recommendation); }
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
return lines.join("\n") + "\n";
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function renderThemes(themes) {
|
|
809
|
+
if (!themes.length) { els.themes.hidden = true; return; }
|
|
810
|
+
els.themes.innerHTML = "<h2>Themes</h2>";
|
|
811
|
+
themes.forEach(function (t) {
|
|
812
|
+
var resolved = t.finding_ids
|
|
813
|
+
.map(function (id) { return findingsById[id]; })
|
|
814
|
+
.filter(function (f) { return !!f; });
|
|
815
|
+
var items = resolved.map(function (f) {
|
|
816
|
+
return '<div class="t-finding"><span class="mono">' + esc(f.id) + "</span> " +
|
|
817
|
+
esc(f.title) +
|
|
818
|
+
(f.location ? ' · <span class="mono">' + esc(f.location) + "</span>" : "") +
|
|
819
|
+
"</div>";
|
|
820
|
+
}).join("");
|
|
821
|
+
|
|
822
|
+
var node = document.createElement("div");
|
|
823
|
+
node.className = "theme";
|
|
824
|
+
node.innerHTML =
|
|
825
|
+
'<div class="t-head"><span class="t-title">' + esc(t.title) + "</span>" +
|
|
826
|
+
'<button class="small t-fix">Fix This Theme</button></div>' +
|
|
827
|
+
(t.root_cause ? '<div class="t-cause">Root cause: ' + esc(t.root_cause) + "</div>" : "") +
|
|
828
|
+
(t.action ? '<div class="t-action"><b>Fix:</b> ' + esc(t.action) + "</div>" : "") +
|
|
829
|
+
(items ? '<div class="t-findings">' + items + "</div>" : "");
|
|
830
|
+
node.querySelector(".t-fix").addEventListener("click", function () {
|
|
831
|
+
copyText(composeThemePrompt(t, resolved));
|
|
832
|
+
});
|
|
833
|
+
els.themes.appendChild(node);
|
|
834
|
+
});
|
|
835
|
+
els.themes.hidden = false;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function renderStrengths(list) {
|
|
839
|
+
if (!list.length) { els.strengths.hidden = true; return; }
|
|
840
|
+
els.strengths.innerHTML =
|
|
841
|
+
"<h2>Strengths</h2><ul class=\"strength-list\">" +
|
|
842
|
+
list.map(function (s) { return "<li>" + esc(s) + "</li>"; }).join("") +
|
|
843
|
+
"</ul>";
|
|
844
|
+
els.strengths.hidden = false;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function renderRecommendations(recs) {
|
|
848
|
+
if (!recs.length) { els.recommendations.hidden = true; return; }
|
|
849
|
+
var html = "<h2>Recommendations</h2>";
|
|
850
|
+
recs.forEach(function (r) {
|
|
851
|
+
html += '<div class="rec"><span class="rank">#' + esc(String(r.rank)) + "</span>" +
|
|
852
|
+
esc(r.action) +
|
|
853
|
+
(r.resolves ? '<span class="resolves">resolves: ' + esc(r.resolves) + "</span>" : "") +
|
|
854
|
+
"</div>";
|
|
855
|
+
});
|
|
856
|
+
els.recommendations.innerHTML = html;
|
|
857
|
+
els.recommendations.hidden = false;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function renderNoFindings() {
|
|
861
|
+
els.root.innerHTML =
|
|
862
|
+
'<div class="no-findings">' +
|
|
863
|
+
'<div class="big">No findings</div>' +
|
|
864
|
+
"<div>The scanners returned a clean pass for this subject.</div>" +
|
|
865
|
+
"</div>";
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function findingNode(f) {
|
|
869
|
+
var node = document.createElement("div");
|
|
870
|
+
node.className = "finding sev-" + f.severity;
|
|
871
|
+
node.setAttribute("data-id", f.id);
|
|
872
|
+
|
|
873
|
+
var sub =
|
|
874
|
+
esc(f.lens) +
|
|
875
|
+
(f.location ? ' · <span class="mono">' + esc(f.location) + "</span>" : "");
|
|
876
|
+
|
|
877
|
+
var rows =
|
|
878
|
+
"<dt>Lens</dt><dd>" + esc(f.lens) + "</dd>" +
|
|
879
|
+
(f.location ? "<dt>Location</dt><dd><code>" + esc(f.location) + "</code></dd>" : "") +
|
|
880
|
+
(f.evidence ? "<dt>Evidence</dt><dd>" + esc(f.evidence) + "</dd>" : "") +
|
|
881
|
+
(f.recommendation ? "<dt>Recommendation</dt><dd>" + esc(f.recommendation) + "</dd>" : "") +
|
|
882
|
+
(f.proposed_smallest ? "<dt>Proposed smallest</dt><dd>" + esc(f.proposed_smallest) + "</dd>" : "") +
|
|
883
|
+
(f.predicted_delta ? "<dt>Predicted delta</dt><dd>" + esc(f.predicted_delta) + "</dd>" : "");
|
|
884
|
+
|
|
885
|
+
node.innerHTML =
|
|
886
|
+
'<div class="row">' +
|
|
887
|
+
'<input type="checkbox" class="chk" aria-label="Select finding">' +
|
|
888
|
+
'<div class="head">' +
|
|
889
|
+
'<div class="title">' + esc(f.title) + "</div>" +
|
|
890
|
+
'<div class="sub">' + sub + "</div>" +
|
|
891
|
+
"</div>" +
|
|
892
|
+
'<span class="tag">' + SEV_LABEL[f.severity] + "</span>" +
|
|
893
|
+
'<span class="caret">▸</span>' +
|
|
894
|
+
"</div>" +
|
|
895
|
+
'<div class="body"><dl>' + rows + "</dl></div>";
|
|
896
|
+
|
|
897
|
+
var chk = node.querySelector(".chk");
|
|
898
|
+
chk.checked = !!selected[f.id];
|
|
899
|
+
chk.addEventListener("change", function () {
|
|
900
|
+
if (chk.checked) { selected[f.id] = true; } else { delete selected[f.id]; }
|
|
901
|
+
updateSelection();
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
var head = node.querySelector(".head");
|
|
905
|
+
var caret = node.querySelector(".caret");
|
|
906
|
+
function toggle() { node.classList.toggle("open"); }
|
|
907
|
+
head.addEventListener("click", toggle);
|
|
908
|
+
caret.addEventListener("click", toggle);
|
|
909
|
+
|
|
910
|
+
return node;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function renderFindings(list) {
|
|
914
|
+
els.root.innerHTML = "";
|
|
915
|
+
if (list.length === 0) {
|
|
916
|
+
renderNoFindings();
|
|
917
|
+
els.toolbar.hidden = true;
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
els.toolbar.hidden = false;
|
|
921
|
+
|
|
922
|
+
SEVERITIES.forEach(function (sev) {
|
|
923
|
+
var group = list.filter(function (f) { return f.severity === sev; });
|
|
924
|
+
if (group.length === 0) { return; }
|
|
925
|
+
var wrap = document.createElement("div");
|
|
926
|
+
wrap.className = "group";
|
|
927
|
+
var h = document.createElement("h2");
|
|
928
|
+
h.innerHTML = '<span class="dot ' + sev + '"></span>' + SEV_LABEL[sev] + " (" + group.length + ")";
|
|
929
|
+
wrap.appendChild(h);
|
|
930
|
+
group.forEach(function (f) { wrap.appendChild(findingNode(f)); });
|
|
931
|
+
els.root.appendChild(wrap);
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function updateSelection() {
|
|
936
|
+
var n = Object.keys(selected).length;
|
|
937
|
+
els.selCount.textContent = n + " selected";
|
|
938
|
+
els.btnCopy.disabled = n === 0;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
function composePrompt() {
|
|
942
|
+
var picked = findings.filter(function (f) { return selected[f.id]; });
|
|
943
|
+
if (picked.length === 0) { return ""; }
|
|
944
|
+
var lines = standardsPreamble();
|
|
945
|
+
lines.push("Fix the following issues in " + subjectPath + ":");
|
|
946
|
+
lines.push("");
|
|
947
|
+
picked.forEach(function (f, i) {
|
|
948
|
+
lines.push((i + 1) + ". " + f.title);
|
|
949
|
+
if (f.location) { lines.push(" Location: " + f.location); }
|
|
950
|
+
if (f.evidence) { lines.push(" Evidence: " + f.evidence); }
|
|
951
|
+
if (f.recommendation) { lines.push(" Recommendation: " + f.recommendation); }
|
|
952
|
+
if (f.proposed_smallest) { lines.push(" Proposed smallest: " + f.proposed_smallest); }
|
|
953
|
+
lines.push("");
|
|
954
|
+
});
|
|
955
|
+
return lines.join("\n").replace(/\n+$/, "\n");
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function showToast(text) {
|
|
959
|
+
els.toast.textContent = text;
|
|
960
|
+
els.toast.classList.add("show");
|
|
961
|
+
setTimeout(function () { els.toast.classList.remove("show"); }, 1600);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function fallbackCopy(text) {
|
|
965
|
+
els.fallbackText.value = text;
|
|
966
|
+
els.fallback.classList.add("show");
|
|
967
|
+
els.fallbackText.focus();
|
|
968
|
+
els.fallbackText.select();
|
|
969
|
+
try {
|
|
970
|
+
var ok = document.execCommand && document.execCommand("copy");
|
|
971
|
+
if (ok) {
|
|
972
|
+
showToast("Copied");
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
} catch (e) { /* fall through to manual */ }
|
|
976
|
+
showToast("Copy the text shown below");
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
function copyText(text) {
|
|
980
|
+
if (!text) { return; }
|
|
981
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
982
|
+
navigator.clipboard.writeText(text).then(
|
|
983
|
+
function () { showToast("Copied"); },
|
|
984
|
+
function () { fallbackCopy(text); }
|
|
985
|
+
);
|
|
986
|
+
} else {
|
|
987
|
+
fallbackCopy(text);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function doCopy() {
|
|
992
|
+
copyText(composePrompt());
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
function wireToolbar() {
|
|
996
|
+
els.btnCopy.addEventListener("click", doCopy);
|
|
997
|
+
els.btnSelectAll.addEventListener("click", function () {
|
|
998
|
+
findings.forEach(function (f) { selected[f.id] = true; });
|
|
999
|
+
document.querySelectorAll(".finding .chk").forEach(function (c) { c.checked = true; });
|
|
1000
|
+
updateSelection();
|
|
1001
|
+
});
|
|
1002
|
+
els.btnClear.addEventListener("click", function () {
|
|
1003
|
+
selected = Object.create(null);
|
|
1004
|
+
document.querySelectorAll(".finding .chk").forEach(function (c) { c.checked = false; });
|
|
1005
|
+
els.fallback.classList.remove("show");
|
|
1006
|
+
updateSelection();
|
|
1007
|
+
});
|
|
1008
|
+
els.btnExpandAll.addEventListener("click", function () {
|
|
1009
|
+
document.querySelectorAll(".finding").forEach(function (n) { n.classList.add("open"); });
|
|
1010
|
+
});
|
|
1011
|
+
els.btnCollapseAll.addEventListener("click", function () {
|
|
1012
|
+
document.querySelectorAll(".finding").forEach(function (n) { n.classList.remove("open"); });
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
function init() {
|
|
1017
|
+
var island = document.getElementById("report-data");
|
|
1018
|
+
var parsed;
|
|
1019
|
+
try {
|
|
1020
|
+
if (!island) { throw new Error("report-data island element not found"); }
|
|
1021
|
+
parsed = JSON.parse(island.textContent);
|
|
1022
|
+
} catch (err) {
|
|
1023
|
+
showBanner(
|
|
1024
|
+
"Could not parse the report data island.\n\n" +
|
|
1025
|
+
"Error: " + (err && err.message ? err.message : String(err)) + "\n\n" +
|
|
1026
|
+
"The findings could not be rendered. The JSON inside the " +
|
|
1027
|
+
'report-data island (the application/json script tag) is malformed.'
|
|
1028
|
+
);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
var data = normalize(parsed);
|
|
1033
|
+
|
|
1034
|
+
if (data.subject === PLACEHOLDER_SUBJECT) {
|
|
1035
|
+
els.subject.textContent = data.subject;
|
|
1036
|
+
showBanner(
|
|
1037
|
+
"This is the unfilled report shell.\n\n" +
|
|
1038
|
+
"The report-data island still carries the placeholder subject, so " +
|
|
1039
|
+
"there are no findings here. Generate a real report with " +
|
|
1040
|
+
"scripts/render_report.py."
|
|
1041
|
+
);
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
findings = data.findings;
|
|
1046
|
+
subjectPath = data.subject;
|
|
1047
|
+
standards = data.standards;
|
|
1048
|
+
findingsById = Object.create(null);
|
|
1049
|
+
findings.forEach(function (f) { findingsById[f.id] = f; });
|
|
1050
|
+
|
|
1051
|
+
renderProfile(data.agent_profile);
|
|
1052
|
+
renderOverview(data);
|
|
1053
|
+
renderCapabilities(data.capabilities);
|
|
1054
|
+
renderLensVerdicts(data.detailed_analysis);
|
|
1055
|
+
renderSanctum(data.sanctum);
|
|
1056
|
+
renderExperience(data.experience);
|
|
1057
|
+
renderThemes(data.themes);
|
|
1058
|
+
renderStrengths(data.strengths);
|
|
1059
|
+
renderRecommendations(data.recommendations);
|
|
1060
|
+
renderFindings(findings);
|
|
1061
|
+
wireToolbar();
|
|
1062
|
+
updateSelection();
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
if (document.readyState === "loading") {
|
|
1066
|
+
document.addEventListener("DOMContentLoaded", init);
|
|
1067
|
+
} else {
|
|
1068
|
+
init();
|
|
1069
|
+
}
|
|
1070
|
+
})();
|
|
1071
|
+
</script>
|
|
1072
|
+
</body>
|
|
1073
|
+
</html>
|