@judge-java/dashboard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +60 -0
- package/dist/cli.js.map +1 -0
- package/dist/generator.d.ts +2 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +436 -0
- package/dist/generator.js.map +1 -0
- package/package.json +25 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI for generating the code reviewe dashboard.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx judge-java-dashboard [--repo <path>] [--output <path>]
|
|
7
|
+
*
|
|
8
|
+
* By default, runs in the current directory and outputs to .codeagent/dashboard.html
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI for generating the code reviewe dashboard.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx judge-java-dashboard [--repo <path>] [--output <path>]
|
|
7
|
+
*
|
|
8
|
+
* By default, runs in the current directory and outputs to .codeagent/dashboard.html
|
|
9
|
+
*/
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
import { generateDashboard } from "@judge-java/dashboard";
|
|
12
|
+
async function main() {
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
let repoRoot = process.cwd();
|
|
15
|
+
let outputOverride;
|
|
16
|
+
for (let i = 0; i < args.length; i++) {
|
|
17
|
+
if (args[i] === "--repo" && args[i + 1]) {
|
|
18
|
+
repoRoot = path.resolve(args[++i]);
|
|
19
|
+
}
|
|
20
|
+
else if (args[i] === "--output" && args[i + 1]) {
|
|
21
|
+
outputOverride = path.resolve(args[++i]);
|
|
22
|
+
}
|
|
23
|
+
else if (args[i] === "--help") {
|
|
24
|
+
console.log(`
|
|
25
|
+
judge-java-dashboard ā generate the code health dashboard
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
judge-java-dashboard [options]
|
|
29
|
+
|
|
30
|
+
Options:
|
|
31
|
+
--repo <path> Path to the repository root (default: current directory)
|
|
32
|
+
--output <path> Output path for dashboard.html (default: .codeagent/dashboard.html)
|
|
33
|
+
--help Show this help
|
|
34
|
+
|
|
35
|
+
The dashboard is a self-contained HTML file that visualises your .codeagent/ state:
|
|
36
|
+
- Open and resolved tracked issues
|
|
37
|
+
- Team decisions
|
|
38
|
+
- Domain conventions
|
|
39
|
+
- Audit log
|
|
40
|
+
|
|
41
|
+
Host it on GitHub Pages by pointing your Pages source to the .codeagent/ folder,
|
|
42
|
+
or simply open it locally in a browser.
|
|
43
|
+
`);
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
console.log(`\nā judge-java dashboard\n`);
|
|
48
|
+
console.log(` Repository: ${repoRoot}`);
|
|
49
|
+
try {
|
|
50
|
+
const outputPath = await generateDashboard(repoRoot, outputOverride);
|
|
51
|
+
console.log(` Output: ${outputPath}`);
|
|
52
|
+
console.log(`\nā Dashboard generated.\n`);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
console.error(`\nā Failed to generate dashboard: ${String(err)}\n`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
main();
|
|
60
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAC,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAExD,KAAK,UAAU,IAAI;IACf,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,cAAkC,CAAC;IAEvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACtC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC/C,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;CAmBvB,CAAC,CAAC;YACS,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;IAEzC,IAAI,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,iBAAiB,UAAU,EAAE,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,qCAAqC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAKA,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW9F"}
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { loadMemory } from "@judge-java/core";
|
|
4
|
+
export async function generateDashboard(repoRoot, outputPath) {
|
|
5
|
+
const memory = await loadMemory(repoRoot);
|
|
6
|
+
const auditLog = await readAuditLog(repoRoot);
|
|
7
|
+
const repoName = path.basename(repoRoot);
|
|
8
|
+
const html = renderDashboard(repoName, memory, auditLog);
|
|
9
|
+
const resolvedOutputPath = outputPath ?? path.join(repoRoot, ".codeagent", "dashboard.html");
|
|
10
|
+
await fs.writeFile(resolvedOutputPath, html, "utf-8");
|
|
11
|
+
return resolvedOutputPath;
|
|
12
|
+
}
|
|
13
|
+
async function readAuditLog(repoRoot) {
|
|
14
|
+
try {
|
|
15
|
+
const content = await fs.readFile(path.join(repoRoot, ".codeagent", "audit.log"), "utf-8");
|
|
16
|
+
return content
|
|
17
|
+
.split("\n")
|
|
18
|
+
.filter(Boolean)
|
|
19
|
+
.reverse()
|
|
20
|
+
.slice(0, 50); // last 50 entries
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function renderDashboard(repoName, memory, auditLog) {
|
|
27
|
+
const openIssues = memory.trackedIssues.filter((i) => i.status === "open");
|
|
28
|
+
const closedIssues = memory.trackedIssues.filter((i) => i.status === "closed");
|
|
29
|
+
const issuesByLevel = groupBy(openIssues, (i) => i.reviewLevel);
|
|
30
|
+
const generatedAt = new Date().toLocaleString();
|
|
31
|
+
return `<!DOCTYPE html>
|
|
32
|
+
<html lang="en">
|
|
33
|
+
<head>
|
|
34
|
+
<meta charset="UTF-8" />
|
|
35
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
36
|
+
<title>judge-java ā ${escapeHtml(repoName)}</title>
|
|
37
|
+
<style>
|
|
38
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
39
|
+
|
|
40
|
+
:root {
|
|
41
|
+
--bg: #0d1117;
|
|
42
|
+
--surface: #161b22;
|
|
43
|
+
--surface2: #21262d;
|
|
44
|
+
--border: #30363d;
|
|
45
|
+
--text: #e6edf3;
|
|
46
|
+
--muted: #8b949e;
|
|
47
|
+
--accent: #58a6ff;
|
|
48
|
+
--green: #3fb950;
|
|
49
|
+
--yellow: #d29922;
|
|
50
|
+
--red: #f85149;
|
|
51
|
+
--purple: #bc8cff;
|
|
52
|
+
--orange: #ffa657;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
body {
|
|
56
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
57
|
+
background: var(--bg);
|
|
58
|
+
color: var(--text);
|
|
59
|
+
line-height: 1.6;
|
|
60
|
+
min-height: 100vh;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
header {
|
|
64
|
+
background: var(--surface);
|
|
65
|
+
border-bottom: 1px solid var(--border);
|
|
66
|
+
padding: 1.25rem 2rem;
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
gap: 1rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
header .logo {
|
|
73
|
+
font-size: 1.1rem;
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
color: var(--accent);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
header .repo {
|
|
79
|
+
color: var(--muted);
|
|
80
|
+
font-size: 0.9rem;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
header .generated {
|
|
84
|
+
margin-left: auto;
|
|
85
|
+
font-size: 0.8rem;
|
|
86
|
+
color: var(--muted);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
main {
|
|
90
|
+
max-width: 1200px;
|
|
91
|
+
margin: 0 auto;
|
|
92
|
+
padding: 2rem;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.stats-grid {
|
|
96
|
+
display: grid;
|
|
97
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
98
|
+
gap: 1rem;
|
|
99
|
+
margin-bottom: 2rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.stat-card {
|
|
103
|
+
background: var(--surface);
|
|
104
|
+
border: 1px solid var(--border);
|
|
105
|
+
border-radius: 8px;
|
|
106
|
+
padding: 1.25rem;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.stat-card .value {
|
|
110
|
+
font-size: 2rem;
|
|
111
|
+
font-weight: 700;
|
|
112
|
+
line-height: 1;
|
|
113
|
+
margin-bottom: 0.25rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.stat-card .label {
|
|
117
|
+
font-size: 0.8rem;
|
|
118
|
+
color: var(--muted);
|
|
119
|
+
text-transform: uppercase;
|
|
120
|
+
letter-spacing: 0.05em;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.value.green { color: var(--green); }
|
|
124
|
+
.value.yellow { color: var(--yellow); }
|
|
125
|
+
.value.red { color: var(--red); }
|
|
126
|
+
.value.purple { color: var(--purple); }
|
|
127
|
+
.value.blue { color: var(--accent); }
|
|
128
|
+
|
|
129
|
+
.section {
|
|
130
|
+
margin-bottom: 2.5rem;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.section-title {
|
|
134
|
+
font-size: 1rem;
|
|
135
|
+
font-weight: 600;
|
|
136
|
+
margin-bottom: 1rem;
|
|
137
|
+
padding-bottom: 0.5rem;
|
|
138
|
+
border-bottom: 1px solid var(--border);
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
gap: 0.5rem;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.badge {
|
|
145
|
+
display: inline-flex;
|
|
146
|
+
align-items: center;
|
|
147
|
+
padding: 0.15rem 0.5rem;
|
|
148
|
+
border-radius: 20px;
|
|
149
|
+
font-size: 0.72rem;
|
|
150
|
+
font-weight: 600;
|
|
151
|
+
background: var(--surface2);
|
|
152
|
+
color: var(--muted);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.issue-list {
|
|
156
|
+
display: flex;
|
|
157
|
+
flex-direction: column;
|
|
158
|
+
gap: 0.5rem;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.issue-item {
|
|
162
|
+
background: var(--surface);
|
|
163
|
+
border: 1px solid var(--border);
|
|
164
|
+
border-radius: 6px;
|
|
165
|
+
padding: 0.875rem 1rem;
|
|
166
|
+
display: flex;
|
|
167
|
+
align-items: flex-start;
|
|
168
|
+
gap: 0.75rem;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.issue-item:hover {
|
|
172
|
+
border-color: var(--accent);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.issue-number {
|
|
176
|
+
color: var(--muted);
|
|
177
|
+
font-size: 0.8rem;
|
|
178
|
+
font-family: monospace;
|
|
179
|
+
min-width: 3rem;
|
|
180
|
+
padding-top: 0.1rem;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.issue-content { flex: 1; }
|
|
184
|
+
|
|
185
|
+
.issue-title {
|
|
186
|
+
font-size: 0.9rem;
|
|
187
|
+
font-weight: 500;
|
|
188
|
+
margin-bottom: 0.25rem;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.issue-meta {
|
|
192
|
+
font-size: 0.78rem;
|
|
193
|
+
color: var(--muted);
|
|
194
|
+
display: flex;
|
|
195
|
+
gap: 0.75rem;
|
|
196
|
+
flex-wrap: wrap;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.level-tag {
|
|
200
|
+
display: inline-flex;
|
|
201
|
+
padding: 0.1rem 0.4rem;
|
|
202
|
+
border-radius: 4px;
|
|
203
|
+
font-size: 0.72rem;
|
|
204
|
+
font-weight: 500;
|
|
205
|
+
background: var(--surface2);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.level-tag.refactoring_opportunities { color: var(--yellow); }
|
|
209
|
+
.level-tag.security { color: var(--red); }
|
|
210
|
+
.level-tag.performance { color: var(--orange); }
|
|
211
|
+
.level-tag.architecture { color: var(--purple); }
|
|
212
|
+
.level-tag.domain_language { color: var(--accent); }
|
|
213
|
+
.level-tag.test_quality { color: var(--green); }
|
|
214
|
+
.level-tag.scope_check { color: var(--muted); }
|
|
215
|
+
.level-tag.obvious_bugs { color: var(--red); }
|
|
216
|
+
|
|
217
|
+
.decision-item {
|
|
218
|
+
background: var(--surface);
|
|
219
|
+
border: 1px solid var(--border);
|
|
220
|
+
border-radius: 6px;
|
|
221
|
+
padding: 0.875rem 1rem;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.decision-title {
|
|
225
|
+
font-size: 0.9rem;
|
|
226
|
+
font-weight: 500;
|
|
227
|
+
margin-bottom: 0.25rem;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.decision-reason {
|
|
231
|
+
font-size: 0.82rem;
|
|
232
|
+
color: var(--muted);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.decision-meta {
|
|
236
|
+
font-size: 0.75rem;
|
|
237
|
+
color: var(--muted);
|
|
238
|
+
margin-top: 0.25rem;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.conventions-block {
|
|
242
|
+
background: var(--surface);
|
|
243
|
+
border: 1px solid var(--border);
|
|
244
|
+
border-radius: 6px;
|
|
245
|
+
padding: 1.25rem;
|
|
246
|
+
font-size: 0.85rem;
|
|
247
|
+
white-space: pre-wrap;
|
|
248
|
+
color: var(--muted);
|
|
249
|
+
max-height: 300px;
|
|
250
|
+
overflow-y: auto;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.audit-list {
|
|
254
|
+
background: var(--surface);
|
|
255
|
+
border: 1px solid var(--border);
|
|
256
|
+
border-radius: 6px;
|
|
257
|
+
overflow: hidden;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.audit-entry {
|
|
261
|
+
font-family: monospace;
|
|
262
|
+
font-size: 0.78rem;
|
|
263
|
+
padding: 0.5rem 1rem;
|
|
264
|
+
border-bottom: 1px solid var(--border);
|
|
265
|
+
color: var(--muted);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.audit-entry:last-child { border-bottom: none; }
|
|
269
|
+
|
|
270
|
+
.empty-state {
|
|
271
|
+
color: var(--muted);
|
|
272
|
+
font-size: 0.85rem;
|
|
273
|
+
text-align: center;
|
|
274
|
+
padding: 2rem;
|
|
275
|
+
background: var(--surface);
|
|
276
|
+
border: 1px dashed var(--border);
|
|
277
|
+
border-radius: 6px;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.level-breakdown {
|
|
281
|
+
display: grid;
|
|
282
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
283
|
+
gap: 0.75rem;
|
|
284
|
+
margin-bottom: 1.5rem;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.level-card {
|
|
288
|
+
background: var(--surface);
|
|
289
|
+
border: 1px solid var(--border);
|
|
290
|
+
border-radius: 6px;
|
|
291
|
+
padding: 1rem;
|
|
292
|
+
display: flex;
|
|
293
|
+
justify-content: space-between;
|
|
294
|
+
align-items: center;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.level-card .count {
|
|
298
|
+
font-size: 1.4rem;
|
|
299
|
+
font-weight: 700;
|
|
300
|
+
color: var(--yellow);
|
|
301
|
+
}
|
|
302
|
+
</style>
|
|
303
|
+
</head>
|
|
304
|
+
<body>
|
|
305
|
+
<header>
|
|
306
|
+
<span class="logo">ā judge-java</span>
|
|
307
|
+
<span class="repo">${escapeHtml(repoName)}</span>
|
|
308
|
+
<span class="generated">Generated ${escapeHtml(generatedAt)}</span>
|
|
309
|
+
</header>
|
|
310
|
+
|
|
311
|
+
<main>
|
|
312
|
+
<!-- Stats -->
|
|
313
|
+
<div class="stats-grid">
|
|
314
|
+
<div class="stat-card">
|
|
315
|
+
<div class="value yellow">${openIssues.length}</div>
|
|
316
|
+
<div class="label">Open Issues</div>
|
|
317
|
+
</div>
|
|
318
|
+
<div class="stat-card">
|
|
319
|
+
<div class="value green">${closedIssues.length}</div>
|
|
320
|
+
<div class="label">Resolved Issues</div>
|
|
321
|
+
</div>
|
|
322
|
+
<div class="stat-card">
|
|
323
|
+
<div class="value purple">${memory.decisions.length}</div>
|
|
324
|
+
<div class="label">Team Decisions</div>
|
|
325
|
+
</div>
|
|
326
|
+
<div class="stat-card">
|
|
327
|
+
<div class="value blue">${auditLog.length}</div>
|
|
328
|
+
<div class="label">Audit Events</div>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<!-- Open Issues by Level -->
|
|
333
|
+
${openIssues.length > 0
|
|
334
|
+
? `
|
|
335
|
+
<div class="section">
|
|
336
|
+
<div class="section-title">
|
|
337
|
+
Open Issues
|
|
338
|
+
<span class="badge">${openIssues.length}</span>
|
|
339
|
+
</div>
|
|
340
|
+
<div class="level-breakdown">
|
|
341
|
+
${Object.entries(issuesByLevel)
|
|
342
|
+
.map(([level, issues]) => `
|
|
343
|
+
<div class="level-card">
|
|
344
|
+
<span class="level-tag ${escapeHtml(level)}">${formatLevel(level)}</span>
|
|
345
|
+
<span class="count">${issues.length}</span>
|
|
346
|
+
</div>`)
|
|
347
|
+
.join("")}
|
|
348
|
+
</div>
|
|
349
|
+
<div class="issue-list">
|
|
350
|
+
${openIssues.map(renderIssue).join("")}
|
|
351
|
+
</div>
|
|
352
|
+
</div>`
|
|
353
|
+
: `
|
|
354
|
+
<div class="section">
|
|
355
|
+
<div class="section-title">Open Issues</div>
|
|
356
|
+
<div class="empty-state">No open issues. š</div>
|
|
357
|
+
</div>`}
|
|
358
|
+
|
|
359
|
+
<!-- Team Decisions -->
|
|
360
|
+
<div class="section">
|
|
361
|
+
<div class="section-title">
|
|
362
|
+
Team Decisions
|
|
363
|
+
<span class="badge">${memory.decisions.length}</span>
|
|
364
|
+
</div>
|
|
365
|
+
${memory.decisions.length > 0
|
|
366
|
+
? `<div class="issue-list">${memory.decisions.map(renderDecision).join("")}</div>`
|
|
367
|
+
: `<div class="empty-state">No decisions recorded yet. Use <code>add_decision</code> to dismiss known patterns.</div>`}
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
<!-- Conventions -->
|
|
371
|
+
${memory.narrative.trim()
|
|
372
|
+
? `
|
|
373
|
+
<div class="section">
|
|
374
|
+
<div class="section-title">Domain Conventions & Context</div>
|
|
375
|
+
<div class="conventions-block">${escapeHtml(memory.narrative)}</div>
|
|
376
|
+
</div>`
|
|
377
|
+
: ""}
|
|
378
|
+
|
|
379
|
+
<!-- Audit Log -->
|
|
380
|
+
${auditLog.length > 0
|
|
381
|
+
? `
|
|
382
|
+
<div class="section">
|
|
383
|
+
<div class="section-title">
|
|
384
|
+
Audit Log
|
|
385
|
+
<span class="badge">last ${auditLog.length}</span>
|
|
386
|
+
</div>
|
|
387
|
+
<div class="audit-list">
|
|
388
|
+
${auditLog.map((l) => `<div class="audit-entry">${escapeHtml(l)}</div>`).join("")}
|
|
389
|
+
</div>
|
|
390
|
+
</div>`
|
|
391
|
+
: ""}
|
|
392
|
+
</main>
|
|
393
|
+
</body>
|
|
394
|
+
</html>`;
|
|
395
|
+
}
|
|
396
|
+
function renderIssue(issue) {
|
|
397
|
+
return `
|
|
398
|
+
<div class="issue-item">
|
|
399
|
+
<span class="issue-number">#${issue.githubIssueNumber}</span>
|
|
400
|
+
<div class="issue-content">
|
|
401
|
+
<div class="issue-title">${escapeHtml(issue.title)}</div>
|
|
402
|
+
<div class="issue-meta">
|
|
403
|
+
<span class="level-tag ${escapeHtml(issue.reviewLevel)}">${formatLevel(issue.reviewLevel)}</span>
|
|
404
|
+
<span>${escapeHtml(issue.createdAt.split("T")[0])}</span>
|
|
405
|
+
${issue.files.length > 0 ? `<span>${escapeHtml(issue.files[0])}</span>` : ""}
|
|
406
|
+
</div>
|
|
407
|
+
</div>
|
|
408
|
+
</div>`;
|
|
409
|
+
}
|
|
410
|
+
function renderDecision(decision) {
|
|
411
|
+
return `
|
|
412
|
+
<div class="decision-item">
|
|
413
|
+
<div class="decision-title">${escapeHtml(decision.title)}</div>
|
|
414
|
+
<div class="decision-reason">${escapeHtml(decision.reason)}</div>
|
|
415
|
+
<div class="decision-meta">${escapeHtml(decision.date)} Ā· ${escapeHtml(decision.author)}</div>
|
|
416
|
+
</div>`;
|
|
417
|
+
}
|
|
418
|
+
function formatLevel(level) {
|
|
419
|
+
return level.replace(/_/g, " ");
|
|
420
|
+
}
|
|
421
|
+
function groupBy(arr, key) {
|
|
422
|
+
return arr.reduce((acc, item) => {
|
|
423
|
+
const k = key(item);
|
|
424
|
+
(acc[k] ??= []).push(item);
|
|
425
|
+
return acc;
|
|
426
|
+
}, {});
|
|
427
|
+
}
|
|
428
|
+
function escapeHtml(str) {
|
|
429
|
+
return str
|
|
430
|
+
.replace(/&/g, "&")
|
|
431
|
+
.replace(/</g, "<")
|
|
432
|
+
.replace(/>/g, ">")
|
|
433
|
+
.replace(/"/g, """)
|
|
434
|
+
.replace(/'/g, "'");
|
|
435
|
+
}
|
|
436
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@judge-java/dashboard",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Static HTML dashboard generator for judge-java",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/generator.js",
|
|
7
|
+
"types": "./dist/generator.d.ts",
|
|
8
|
+
"files": ["dist"],
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/generator.js",
|
|
12
|
+
"types": "./dist/generator.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"judge-java-dashboard": "./dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"test": "vitest run"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@judge-java/core": "*"
|
|
24
|
+
}
|
|
25
|
+
}
|