crg-dev-kit 2.0.3 → 2.0.5
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/assets/setup-crg.sh +25 -4
- package/lib/actions.js +16 -1
- package/package.json +1 -1
- package/server.js +145 -72
- /package/assets/{README.md → CRG-README.md} +0 -0
package/assets/setup-crg.sh
CHANGED
|
@@ -178,11 +178,32 @@ if $INSTALL_EMBEDDINGS; then
|
|
|
178
178
|
fi
|
|
179
179
|
fi
|
|
180
180
|
|
|
181
|
-
# ── Step 6: Add .gitignore
|
|
181
|
+
# ── Step 6: Add .gitignore entries ──
|
|
182
182
|
if [ -f ".gitignore" ]; then
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
IGNORE_ENTRIES=(
|
|
184
|
+
".code-review-graph/"
|
|
185
|
+
"setup-crg.sh"
|
|
186
|
+
"setup-crg.ps1"
|
|
187
|
+
"check-crg.sh"
|
|
188
|
+
"crg-cheatsheet.pdf"
|
|
189
|
+
"CRG-README.md"
|
|
190
|
+
".cursorrules"
|
|
191
|
+
".opencode.json"
|
|
192
|
+
".windsurfrules"
|
|
193
|
+
"GEMINI.md"
|
|
194
|
+
)
|
|
195
|
+
ADDED=0
|
|
196
|
+
for entry in "${IGNORE_ENTRIES[@]}"; do
|
|
197
|
+
if ! grep -qxF "$entry" .gitignore 2>/dev/null; then
|
|
198
|
+
if [ $ADDED -eq 0 ]; then
|
|
199
|
+
echo -e "\n# code-review-graph" >> .gitignore
|
|
200
|
+
fi
|
|
201
|
+
echo "$entry" >> .gitignore
|
|
202
|
+
ADDED=$((ADDED + 1))
|
|
203
|
+
fi
|
|
204
|
+
done
|
|
205
|
+
if [ $ADDED -gt 0 ]; then
|
|
206
|
+
ok "Added $ADDED entries to .gitignore"
|
|
186
207
|
fi
|
|
187
208
|
fi
|
|
188
209
|
|
package/lib/actions.js
CHANGED
|
@@ -12,7 +12,7 @@ const INSTALL_FILES = [
|
|
|
12
12
|
{ name: 'check-crg.sh', executable: true, label: 'Health check script' },
|
|
13
13
|
{ name: 'CLAUDE.md', executable: false, label: 'AI configuration' },
|
|
14
14
|
{ name: 'crg-cheatsheet.pdf', executable: false, label: 'Cheatsheet PDF' },
|
|
15
|
-
{ name: 'README.md', executable: false, label: 'Documentation' },
|
|
15
|
+
{ name: 'CRG-README.md', executable: false, label: 'Documentation' },
|
|
16
16
|
];
|
|
17
17
|
|
|
18
18
|
function install(targetDir, trackROI = true) {
|
|
@@ -38,6 +38,21 @@ function install(targetDir, trackROI = true) {
|
|
|
38
38
|
copied++;
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
+
// Add copied files to .gitignore
|
|
42
|
+
if (copied > 0) {
|
|
43
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
44
|
+
const filesToIgnore = [...INSTALL_FILES.map(f => f.name), '.cursorrules', '.opencode.json', '.windsurfrules', 'GEMINI.md'];
|
|
45
|
+
if (fs.existsSync(gitignorePath)) {
|
|
46
|
+
let content = fs.readFileSync(gitignorePath, 'utf8');
|
|
47
|
+
const newFiles = filesToIgnore
|
|
48
|
+
.filter(name => !content.split('\n').some(line => line.trim() === name));
|
|
49
|
+
if (newFiles.length > 0) {
|
|
50
|
+
const block = '\n# code-review-graph\n' + newFiles.join('\n') + '\n';
|
|
51
|
+
fs.appendFileSync(gitignorePath, block);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
41
56
|
if (trackROI) {
|
|
42
57
|
roi.setInstallDate(targetDir);
|
|
43
58
|
}
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -113,7 +113,7 @@ function buildPage() {
|
|
|
113
113
|
];
|
|
114
114
|
|
|
115
115
|
const navLinks = tabs.map(t =>
|
|
116
|
-
`<a href="#" hx-get="/tab/${t.id}" hx-target="#
|
|
116
|
+
`<a href="#content" hx-get="/tab/${t.id}" hx-target="#main" hx-swap="innerHTML" class="${t.id === 'dashboard' ? 'active' : ''}" aria-current="${t.id === 'dashboard' ? 'page' : 'false'}" onclick="document.querySelectorAll('.nk a').forEach(a=>a.removeAttribute('aria-current'));this.setAttribute('aria-current','page');return true">${t.label}</a>`
|
|
117
117
|
).join('');
|
|
118
118
|
|
|
119
119
|
return `<!DOCTYPE html>
|
|
@@ -121,31 +121,40 @@ function buildPage() {
|
|
|
121
121
|
<head>
|
|
122
122
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
123
123
|
<title>code-review-graph Dev Kit</title>
|
|
124
|
+
<meta name="description" content="One-click setup for AI-powered code review with knowledge graph">
|
|
124
125
|
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
|
125
126
|
<style>
|
|
126
|
-
@import url('https://fonts.googleapis.com/css2?family=
|
|
127
|
+
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,400;9..40,500;9..40,600;9..40,700&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
|
127
128
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
128
|
-
:root{--bg:#fafaf9;--s:#fff;--b:#e7e5e4;--t:#1c1917;--t2:#
|
|
129
|
-
body{font-family:
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
|
|
133
|
-
.
|
|
134
|
-
.
|
|
129
|
+
:root{--bg:#fafaf9;--s:#fff;--b:#e7e5e4;--t:#1c1917;--t2:#57534e;--t3:#78716c;--t4:#a8a29e;--a:#ea580c;--al:#fff7ed;--ab:#fed7aa;--m:'JetBrains Mono',monospace;--f:'DM Sans',sans-serif}
|
|
130
|
+
body{font-family:var(--f);background:var(--bg);color:var(--t);line-height:1.5;-webkit-font-smoothing:antialiased}
|
|
131
|
+
.skip-link{position:absolute;left:-9999px;top:0;background:var(--a);color:#fff;padding:8px 16px;z-index:999}
|
|
132
|
+
.skip-link:focus{left:0}
|
|
133
|
+
nav{background:var(--s);border-bottom:1px solid var(--b);padding:0 clamp(16px,4vw,40px);display:flex;align-items:center;height:56px;position:sticky;top:0;z-index:100;gap:8px}
|
|
134
|
+
.nb{display:flex;align-items:center;gap:10px;font-weight:700;font-size:15px;letter-spacing:-0.02em}
|
|
135
|
+
.nl{width:28px;height:28px;background:var(--a);border-radius:6px;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px}
|
|
136
|
+
.nk{margin-left:auto;display:flex;gap:clamp(8px,2vw,24px);align-items:center}
|
|
137
|
+
.nk a{color:var(--t3);text-decoration:none;font-size:13px;font-weight:500;transition:color .15s;padding:16px 0;border-bottom:2px solid transparent}
|
|
135
138
|
.nk a:hover{color:var(--a)}
|
|
136
|
-
.nk a.active{color:var(--a);border-bottom:
|
|
139
|
+
.nk a.active,.nk a[aria-current="page"]{color:var(--a);border-bottom-color:var(--a)}
|
|
137
140
|
.badge{background:var(--al);color:var(--a);padding:2px 8px;border-radius:4px;font-size:11px;font-weight:700;border:1px solid var(--ab)}
|
|
138
|
-
|
|
139
|
-
.hero
|
|
141
|
+
main{max-width:100%;overflow-x:hidden}
|
|
142
|
+
.hero{text-align:center;padding:clamp(32px,6vw,64px) clamp(16px,4vw,40px) clamp(24px,4vw,48px);max-width:720px;margin:0 auto}
|
|
143
|
+
.hero h1{font-size:clamp(28px,5vw,42px);font-weight:700;letter-spacing:-0.03em;line-height:1.15;margin-bottom:12px}
|
|
140
144
|
.hero h1 span{color:var(--a)}
|
|
141
|
-
.hero
|
|
142
|
-
.
|
|
143
|
-
.
|
|
144
|
-
.
|
|
145
|
-
|
|
145
|
+
.hero-sub{font-size:clamp(14px,1.5vw,16px);color:var(--t3);max-width:520px;margin:0 auto}
|
|
146
|
+
.hero-metrics{display:flex;justify-content:center;gap:clamp(16px,3vw,32px);margin-top:28px;flex-wrap:wrap}
|
|
147
|
+
.metric-card{text-align:center;min-width:100px}
|
|
148
|
+
.metric-value{font-size:clamp(24px,3vw,32px);font-weight:700;letter-spacing:-0.02em}
|
|
149
|
+
.metric-label{font-size:11px;color:var(--t4);text-transform:uppercase;letter-spacing:.06em;font-weight:600;margin-top:4px}
|
|
150
|
+
section{max-width:clamp(320px,90vw,780px);margin:0 auto;padding:0 clamp(16px,4vw,40px) 48px}
|
|
146
151
|
.st{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--a);margin-bottom:16px;padding-bottom:8px;border-bottom:2px solid var(--a);display:inline-block}
|
|
152
|
+
.status-banner{display:flex;align-items:center;gap:10px;padding:12px 16px;background:var(--s);border:1px solid var(--b);border-radius:8px;margin-bottom:16px}
|
|
153
|
+
.status-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
|
154
|
+
.status-label{font-weight:600;font-size:14px}
|
|
155
|
+
.status-count{font-size:13px;color:var(--t3);margin-left:auto}
|
|
147
156
|
.step{display:flex;gap:16px;padding:20px 0;border-bottom:1px solid var(--b)}.step:last-child{border-bottom:none}
|
|
148
|
-
.step-num{width:32px;height:32px;background:var(--a);color:#fff;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:
|
|
157
|
+
.step-num{width:32px;height:32px;background:var(--a);color:#fff;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:14px;flex-shrink:0}
|
|
149
158
|
.step-body h3{font-size:15px;font-weight:700;margin-bottom:4px}
|
|
150
159
|
.step-desc{font-size:13px;color:var(--t3)}
|
|
151
160
|
.step-cmds{display:flex;gap:12px;margin-top:8px;flex-wrap:wrap}
|
|
@@ -155,14 +164,15 @@ section{max-width:780px;margin:0 auto;padding:0 40px 48px}
|
|
|
155
164
|
.dl-grid{display:flex;flex-direction:column;gap:8px}
|
|
156
165
|
.dl-card{display:flex;align-items:center;gap:12px;padding:14px 16px;background:var(--s);border:1px solid var(--b);border-radius:8px;text-decoration:none;color:var(--t);transition:border-color .15s,box-shadow .15s}
|
|
157
166
|
.dl-card:hover{border-color:var(--a);box-shadow:0 2px 8px rgba(234,88,12,.08)}
|
|
158
|
-
.dl-
|
|
167
|
+
.dl-card:focus-visible{outline:2px solid var(--a);outline-offset:2px}
|
|
168
|
+
.dl-icon{font-size:22px;flex-shrink:0}.dl-info{flex:1;min-width:0}
|
|
159
169
|
.dl-name{font-weight:700;font-size:14px;display:block}
|
|
160
170
|
.dl-desc{font-size:12px;color:var(--t3);display:block;margin-top:1px}
|
|
161
171
|
.dl-size{font-family:var(--m);font-size:11px;color:var(--t4);flex-shrink:0}
|
|
162
172
|
.dl-btn{background:var(--a);color:#fff;padding:6px 14px;border-radius:5px;font-size:12px;font-weight:700;flex-shrink:0;transition:background .15s}
|
|
163
173
|
.dl-card:hover .dl-btn{background:#c2410c}
|
|
164
|
-
.rc{background:var(--s);border:1px solid var(--b);border-radius:8px;padding:32px}
|
|
165
|
-
.rc h1{font-size:22px;font-weight:
|
|
174
|
+
.rc{background:var(--s);border:1px solid var(--b);border-radius:8px;padding:clamp(16px,3vw,32px)}
|
|
175
|
+
.rc h1{font-size:22px;font-weight:700;margin-bottom:16px;letter-spacing:-0.02em}
|
|
166
176
|
.rc h2{font-size:17px;font-weight:700;margin:24px 0 8px}
|
|
167
177
|
.rc h3{font-size:14px;font-weight:700;margin:16px 0 6px}
|
|
168
178
|
.rc p{font-size:14px;color:var(--t2);margin-bottom:8px}
|
|
@@ -174,7 +184,7 @@ section{max-width:780px;margin:0 auto;padding:0 40px 48px}
|
|
|
174
184
|
.rc td{padding:8px 12px;border-bottom:1px solid var(--b);color:var(--t2)}
|
|
175
185
|
.rc li{font-size:14px;color:var(--t2);margin-bottom:4px;margin-left:20px}
|
|
176
186
|
.rc a{color:var(--a)}
|
|
177
|
-
footer{text-align:center;padding:32px 40px;font-size:12px;color:var(--t4);border-top:1px solid var(--b);margin-top:24px}
|
|
187
|
+
footer{text-align:center;padding:32px clamp(16px,4vw,40px);font-size:12px;color:var(--t4);border-top:1px solid var(--b);margin-top:24px}
|
|
178
188
|
footer a{color:var(--a);text-decoration:none;font-weight:600}
|
|
179
189
|
.npx-banner{background:#1c1917;border-radius:8px;padding:16px 20px;margin-bottom:24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap}
|
|
180
190
|
.npx-banner code{font-family:var(--m);font-size:15px;color:#a6e3a1;font-weight:600}
|
|
@@ -188,10 +198,10 @@ footer a{color:var(--a);text-decoration:none;font-weight:600}
|
|
|
188
198
|
.roi-installed{font-size:13px;color:var(--t3)}
|
|
189
199
|
.roi-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:24px}
|
|
190
200
|
.roi-card{background:var(--s);border:1px solid var(--b);border-radius:8px;padding:20px;text-align:center}
|
|
191
|
-
.roi-value{font-size:28px;font-weight:
|
|
201
|
+
.roi-value{font-size:28px;font-weight:700;letter-spacing:-0.02em}
|
|
192
202
|
.roi-label{font-size:11px;color:var(--t3);margin-top:4px;font-weight:600;text-transform:uppercase;letter-spacing:.05em}
|
|
193
|
-
.roi-compare{display:flex;align-items:center;gap:24px;background:var(--s);border:1px solid var(--b);border-radius:8px;padding:24px;margin-bottom:16px}
|
|
194
|
-
.roi-compare-col{flex:1}
|
|
203
|
+
.roi-compare{display:flex;align-items:center;gap:24px;background:var(--s);border:1px solid var(--b);border-radius:8px;padding:24px;margin-bottom:16px;flex-wrap:wrap}
|
|
204
|
+
.roi-compare-col{flex:1;min-width:180px}
|
|
195
205
|
.roi-compare-col h4{font-size:12px;color:var(--t4);text-transform:uppercase;letter-spacing:.06em;margin-bottom:12px}
|
|
196
206
|
.roi-compare-stat{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--b)}
|
|
197
207
|
.roi-compare-stat:last-child{border-bottom:none}
|
|
@@ -202,10 +212,10 @@ footer a{color:var(--a);text-decoration:none;font-weight:600}
|
|
|
202
212
|
.roi-cta code{background:var(--bg);padding:4px 8px;border-radius:4px;font-size:12px}
|
|
203
213
|
.stat-card{background:var(--s);border:1px solid var(--b);border-radius:8px;padding:24px;text-align:center}
|
|
204
214
|
.stat-card.primary{border-color:var(--a);background:var(--al)}
|
|
205
|
-
.stat-value{font-size:32px;font-weight:
|
|
215
|
+
.stat-value{font-size:32px;font-weight:700;letter-spacing:-0.03em}
|
|
206
216
|
.stat-label{font-size:12px;color:var(--t3);margin-top:4px;font-weight:600}
|
|
207
217
|
.stat-sub{font-size:11px;color:var(--t4);margin-top:2px}
|
|
208
|
-
.project-table{margin-bottom:24px}
|
|
218
|
+
.project-table{margin-bottom:24px;overflow-x:auto}
|
|
209
219
|
.project-table table{width:100%;border-collapse:collapse;background:var(--s);border:1px solid var(--b);border-radius:8px;overflow:hidden}
|
|
210
220
|
.project-table th{text-align:left;padding:12px 16px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--t4);background:var(--bg);border-bottom:1px solid var(--b)}
|
|
211
221
|
.project-table td{padding:12px 16px;font-size:13px;color:var(--t2);border-bottom:1px solid var(--b)}
|
|
@@ -217,76 +227,115 @@ footer a{color:var(--a);text-decoration:none;font-weight:600}
|
|
|
217
227
|
.tool-bars{display:flex;flex-direction:column;gap:12px}
|
|
218
228
|
.tool-bar{display:flex;align-items:center;gap:12px}
|
|
219
229
|
.tool-bar-name{font-family:var(--m);font-size:11px;color:var(--t3);width:180px;flex-shrink:0;text-align:right}
|
|
220
|
-
.tool-bar-track{flex:1;height:8px;background:var(--bg);border-radius:4px;overflow:hidden}
|
|
230
|
+
.tool-bar-track{flex:1;height:8px;background:var(--bg);border-radius:4px;overflow:hidden;min-width:0}
|
|
221
231
|
.tool-bar-fill{height:100%;background:var(--a);border-radius:4px;transition:width .3s}
|
|
222
232
|
.tool-bar-count{font-family:var(--m);font-size:11px;color:var(--t4);width:30px;flex-shrink:0}
|
|
223
|
-
.tools-grid{display:grid;grid-template-columns:repeat(
|
|
224
|
-
.tool-card{background:var(--s);border:1px solid var(--b);border-radius:6px;padding:
|
|
233
|
+
.tools-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}
|
|
234
|
+
.tool-card{background:var(--s);border:1px solid var(--b);border-radius:6px;padding:16px;display:flex;flex-direction:column;gap:4px}
|
|
235
|
+
.tool-card:focus-visible{outline:2px solid var(--a);outline-offset:2px}
|
|
225
236
|
.tool-name{font-family:var(--m);font-size:11px;color:var(--a);font-weight:600}
|
|
226
|
-
.tool-label{font-size:
|
|
227
|
-
.tool-desc{font-size:
|
|
228
|
-
|
|
229
|
-
.nk a.active{color:var(--a);border-bottom:2px solid var(--a)}
|
|
230
|
-
/* Action buttons */
|
|
231
|
-
.btn{background:var(--a);color:#fff;padding:10px 20px;border:none;border-radius:6px;font-weight:700;font-size:13px;cursor:pointer;font-family:inherit}
|
|
237
|
+
.tool-label{font-size:14px;font-weight:600;color:var(--t)}
|
|
238
|
+
.tool-desc{font-size:12px;color:var(--t3);line-height:1.4}
|
|
239
|
+
.btn{background:var(--a);color:#fff;padding:10px 20px;border:none;border-radius:6px;font-weight:700;font-size:13px;cursor:pointer;font-family:inherit;transition:background .15s}
|
|
232
240
|
.btn:hover{background:#c2410c}
|
|
241
|
+
.btn:focus-visible{outline:2px solid var(--a);outline-offset:2px}
|
|
242
|
+
.btn:disabled{opacity:.6;cursor:not-allowed}
|
|
233
243
|
.btn-danger{background:#dc2626}
|
|
234
244
|
.btn-danger:hover{background:#b91c1c}
|
|
235
245
|
.btn-secondary{background:var(--bg);color:var(--t2);border:1px solid var(--b)}
|
|
236
|
-
/* Status checks */
|
|
237
246
|
.check-pass{color:#16a34a}
|
|
238
247
|
.check-fail{color:#dc2626}
|
|
239
248
|
.check-item{display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid var(--b)}
|
|
240
249
|
.check-item:last-child{border-bottom:none}
|
|
241
|
-
/* Result messages */
|
|
242
250
|
.result-box{background:var(--s);border:1px solid var(--b);border-radius:8px;padding:20px;margin-top:16px}
|
|
243
251
|
.result-success{border-color:#16a34a;background:#f0fdf4}
|
|
244
252
|
.result-error{border-color:#dc2626;background:#fef2f2}
|
|
245
|
-
|
|
246
|
-
.input{font-family:var(--m);font-size:13px;padding:8px 12px;border:1px solid var(--b);border-radius:6px;width:100%;background:var(--s)}
|
|
253
|
+
.input{font-family:var(--m);font-size:13px;padding:8px 12px;border:1px solid var(--b);border-radius:6px;width:100%;background:var(--s);color:var(--t)}
|
|
247
254
|
.input:focus{outline:none;border-color:var(--a)}
|
|
248
|
-
|
|
255
|
+
.input::placeholder{color:var(--t4)}
|
|
249
256
|
.htmx-indicator{display:none}
|
|
250
257
|
.htmx-request .htmx-indicator{display:inline-block}
|
|
251
258
|
.spinner{width:16px;height:16px;border:2px solid var(--b);border-top-color:var(--a);border-radius:50%;animation:spin .6s linear infinite;display:inline-block}
|
|
252
259
|
@keyframes spin{to{transform:rotate(360deg)}}
|
|
260
|
+
:focus-visible{outline:2px solid var(--a);outline-offset:2px}
|
|
261
|
+
@media(prefers-color-scheme:dark){
|
|
262
|
+
:root{--bg:#1c1917;--s:#292524;--b:#44403c;--t:#fafaf9;--t2:#d6d3d1;--t3:#a8a29e;--t4:#78716c;--al:#292524;--ab:#44403c}
|
|
263
|
+
.dl-card:hover,.roi-card,.stat-card,.tool-card,.project-table table,.roi-compare{background:var(--s)}
|
|
264
|
+
}
|
|
253
265
|
@media(max-width:768px){
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
266
|
+
.analytics-grid,.roi-grid{grid-template-columns:repeat(2,1fr)}
|
|
267
|
+
.tools-grid{grid-template-columns:1fr}
|
|
268
|
+
.hero-metrics{gap:16px}
|
|
269
|
+
.nk{gap:8px;overflow-x:auto;padding-bottom:4px}
|
|
270
|
+
.nk a{font-size:12px;padding:12px 8px;white-space:nowrap}
|
|
271
|
+
section{padding:0 16px 32px}
|
|
272
|
+
.roi-compare{flex-direction:column}
|
|
273
|
+
.roi-arrow{transform:rotate(90deg)}
|
|
261
274
|
}
|
|
262
275
|
</style>
|
|
263
276
|
</head>
|
|
264
277
|
<body>
|
|
265
|
-
<
|
|
278
|
+
<a href="#content" class="skip-link">Skip to main content</a>
|
|
279
|
+
<nav role="navigation" aria-label="Main navigation">
|
|
266
280
|
<div class="nb"><div class="nl">G</div>code-review-graph</div>
|
|
267
281
|
<div class="nk">${navLinks}<span class="badge">v2.1.0</span></div>
|
|
268
282
|
</nav>
|
|
269
|
-
<main id="
|
|
270
|
-
<footer><a href="https://github.com/tirth8205/code-review-graph">github.com/tirth8205/code-review-graph</a>
|
|
283
|
+
<main id="main" hx-get="/tab/dashboard" hx-trigger="load" hx-swap="innerHTML"></main>
|
|
284
|
+
<footer><a href="https://github.com/tirth8205/code-review-graph">github.com/tirth8205/code-review-graph</a> · MIT License · All data stays local</footer>
|
|
271
285
|
</body></html>`;
|
|
272
286
|
}
|
|
273
287
|
|
|
274
288
|
/* ── Tab fragments ────────────────────────────────────────────────────── */
|
|
275
289
|
|
|
276
|
-
function tabDashboard() {
|
|
290
|
+
function tabDashboard(cwd) {
|
|
291
|
+
let statusHtml = '';
|
|
292
|
+
try {
|
|
293
|
+
const statusData = actions.getStatusSync(cwd);
|
|
294
|
+
const verdictColors = { ready: '#16a34a', partial: '#ea580c', not_setup: '#dc2626' };
|
|
295
|
+
const verdictLabels = { ready: 'Ready', partial: 'Partial Setup', not_setup: 'Not Set Up' };
|
|
296
|
+
const color = verdictColors[statusData.verdict] || '#dc2626';
|
|
297
|
+
const label = verdictLabels[statusData.verdict] || statusData.verdict;
|
|
298
|
+
statusHtml = `
|
|
299
|
+
<div class="status-banner" style="border-color:${color}">
|
|
300
|
+
<span class="status-dot" style="background:${color}"></span>
|
|
301
|
+
<span class="status-label" style="color:${color}">${label}</span>
|
|
302
|
+
<span class="status-count">${statusData.found}/${statusData.total} files</span>
|
|
303
|
+
</div>`;
|
|
304
|
+
} catch (e) {
|
|
305
|
+
statusHtml = '';
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
let analyticsHtml = '';
|
|
309
|
+
try {
|
|
310
|
+
const analyticsData = analytics.getAllStats(cwd);
|
|
311
|
+
if (analyticsData) {
|
|
312
|
+
analyticsHtml = `
|
|
313
|
+
<div class="hero-metrics">
|
|
314
|
+
<div class="metric-card">
|
|
315
|
+
<div class="metric-value">${analyticsData.totalSessions}</div>
|
|
316
|
+
<div class="metric-label">Sessions</div>
|
|
317
|
+
</div>
|
|
318
|
+
<div class="metric-card">
|
|
319
|
+
<div class="metric-value">${analyticsData.avgSavingsPercent}%</div>
|
|
320
|
+
<div class="metric-label">Avg Savings</div>
|
|
321
|
+
</div>
|
|
322
|
+
<div class="metric-card">
|
|
323
|
+
<div class="metric-value">${formatTokens(analyticsData.totalTokensSaved)}</div>
|
|
324
|
+
<div class="metric-label">Tokens Saved</div>
|
|
325
|
+
</div>
|
|
326
|
+
</div>`;
|
|
327
|
+
}
|
|
328
|
+
} catch (e) {}
|
|
329
|
+
|
|
277
330
|
return `
|
|
278
331
|
<div class="hero">
|
|
279
332
|
<h1>Ship code with a <span>knowledge graph</span></h1>
|
|
280
|
-
<p>One command to set up AI-powered code review, impact analysis, and codebase navigation
|
|
281
|
-
|
|
282
|
-
<div><div class="sv">22</div><div class="sl">MCP Tools</div></div>
|
|
283
|
-
<div><div class="sv">19</div><div class="sl">Languages</div></div>
|
|
284
|
-
<div><div class="sv">8.2x</div><div class="sl">Token Reduction</div></div>
|
|
285
|
-
<div><div class="sv">0</div><div class="sl">Telemetry</div></div>
|
|
286
|
-
</div>
|
|
333
|
+
<p class="hero-sub">One command to set up AI-powered code review, impact analysis, and codebase navigation.</p>
|
|
334
|
+
${analyticsHtml}
|
|
287
335
|
</div>
|
|
288
336
|
<section>
|
|
289
337
|
<div class="st">Quick Status</div>
|
|
338
|
+
${statusHtml}
|
|
290
339
|
<div id="dashboard-status" hx-get="/api/health?fragment=1" hx-trigger="load" hx-swap="innerHTML">
|
|
291
340
|
<span class="spinner"></span> Loading status...
|
|
292
341
|
</div>
|
|
@@ -299,10 +348,10 @@ function tabInstall(cwd) {
|
|
|
299
348
|
<div class="st">Install CRG Dev Kit</div>
|
|
300
349
|
<p style="color:var(--t2);font-size:14px;margin-bottom:16px">Copy setup scripts, CLAUDE.md, and health check to your project directory.</p>
|
|
301
350
|
<div style="margin-bottom:16px">
|
|
302
|
-
<label style="font-size:12px;font-weight:600;color:var(--t3);display:block;margin-bottom:6px">Target Directory</label>
|
|
351
|
+
<label for="install-dir" style="font-size:12px;font-weight:600;color:var(--t3);display:block;margin-bottom:6px">Target Directory</label>
|
|
303
352
|
<input class="input" id="install-dir" name="targetDir" value="${esc(cwd)}" />
|
|
304
353
|
</div>
|
|
305
|
-
<div style="display:flex;gap:12px">
|
|
354
|
+
<div style="display:flex;gap:12px;flex-wrap:wrap">
|
|
306
355
|
<button class="btn"
|
|
307
356
|
hx-post="/api/install"
|
|
308
357
|
hx-target="#install-result"
|
|
@@ -365,11 +414,11 @@ function tabAnalytics(cwd) {
|
|
|
365
414
|
const roiData = roi.getAllProjectsROI(cwd);
|
|
366
415
|
if (roiData.installed) {
|
|
367
416
|
const verdictColor = roiData.roi.verdict === 'positive' ? '#16a34a' : roiData.roi.verdict === 'neutral' ? '#ea580c' : '#dc2626';
|
|
368
|
-
const verdictIcon = roiData.roi.verdict === 'positive' ? '
|
|
417
|
+
const verdictIcon = roiData.roi.verdict === 'positive' ? '✓' : roiData.roi.verdict === 'neutral' ? '−' : '⚠';
|
|
369
418
|
roiHtml = `
|
|
370
419
|
<div class="st">ROI Calculator</div>
|
|
371
420
|
<div class="roi-banner">
|
|
372
|
-
<div class="roi-verdict" style="background:${verdictColor}20;border-color:${verdictColor}">
|
|
421
|
+
<div class="roi-verdict" style="background:${verdictColor}20;border-color:${verdictColor};color:${verdictColor}">
|
|
373
422
|
<span class="roi-verdict-icon">${verdictIcon}</span>
|
|
374
423
|
<span class="roi-verdict-text">${roiData.roi.verdict === 'positive' ? 'CRG is helping' : roiData.roi.verdict === 'neutral' ? 'No significant change' : 'CRG may not be helping'}</span>
|
|
375
424
|
</div>
|
|
@@ -405,7 +454,7 @@ function tabAnalytics(cwd) {
|
|
|
405
454
|
<span class="roi-compare-value">${roiData.baseline.totalPrompts}</span>
|
|
406
455
|
</div>
|
|
407
456
|
</div>
|
|
408
|
-
<div class="roi-arrow"
|
|
457
|
+
<div class="roi-arrow" aria-hidden="true">→</div>
|
|
409
458
|
<div class="roi-compare-col">
|
|
410
459
|
<h4>After CRG</h4>
|
|
411
460
|
<div class="roi-compare-stat">
|
|
@@ -419,8 +468,10 @@ function tabAnalytics(cwd) {
|
|
|
419
468
|
</div>
|
|
420
469
|
</div>
|
|
421
470
|
<div class="roi-cta">
|
|
422
|
-
<
|
|
471
|
+
<button class="btn btn-secondary" hx-get="/api/roi-report" hx-target="#report-modal" hx-swap="innerHTML" style="margin-right:8px">View ROI Report</button>
|
|
472
|
+
<button class="btn btn-secondary" hx-get="/api/daily-breakdown" hx-target="#report-modal" hx-swap="innerHTML">Daily Breakdown</button>
|
|
423
473
|
</div>
|
|
474
|
+
<div id="report-modal" style="margin-top:16px"></div>
|
|
424
475
|
<div style="margin-bottom:32px"></div>`;
|
|
425
476
|
}
|
|
426
477
|
} catch (e) {
|
|
@@ -483,7 +534,11 @@ function tabAnalytics(cwd) {
|
|
|
483
534
|
</div>`;
|
|
484
535
|
}).join('')}
|
|
485
536
|
</div>
|
|
486
|
-
</div
|
|
537
|
+
</div>
|
|
538
|
+
<div class="roi-cta" style="margin-top:16px">
|
|
539
|
+
<button class="btn btn-secondary" hx-get="/api/report" hx-target="#analytics-report" hx-swap="innerHTML">View Full Report</button>
|
|
540
|
+
</div>
|
|
541
|
+
<div id="analytics-report"></div>` : ''}`;
|
|
487
542
|
} else {
|
|
488
543
|
tokenHtml = `<div class="st">Token Analytics</div><p style="color:var(--t3);font-size:14px">No analytics data yet. Use CRG tools in a project to start tracking.</p>`;
|
|
489
544
|
}
|
|
@@ -493,27 +548,35 @@ function tabAnalytics(cwd) {
|
|
|
493
548
|
|
|
494
549
|
function tabTools() {
|
|
495
550
|
const toolCards = CRG_TOOLS.map(t => `
|
|
496
|
-
<div class="tool-card">
|
|
551
|
+
<div class="tool-card" tabindex="0">
|
|
497
552
|
<span class="tool-name">${t.name}</span>
|
|
498
553
|
<span class="tool-label">${t.label}</span>
|
|
499
554
|
<span class="tool-desc">${t.desc}</span>
|
|
500
555
|
</div>`).join('');
|
|
501
|
-
return `<section
|
|
556
|
+
return `<section>
|
|
557
|
+
<div class="st">Available Tools</div>
|
|
558
|
+
<p style="font-size:14px;color:var(--t3);margin-bottom:16px">These MCP tools are available in your projects after setup. They provide token-efficient code review and analysis.</p>
|
|
559
|
+
<div class="tools-grid">${toolCards}</div>
|
|
560
|
+
</section>`;
|
|
502
561
|
}
|
|
503
562
|
|
|
504
563
|
function tabDownloads() {
|
|
505
564
|
const dlCards = DOWNLOADS.map(d => `
|
|
506
|
-
<a href="/download/${d.file}" class="dl-card" download>
|
|
507
|
-
<span class="dl-icon">${d.icon}</span>
|
|
565
|
+
<a href="/download/${d.file}" class="dl-card" download aria-label="Download ${d.label}">
|
|
566
|
+
<span class="dl-icon" aria-hidden="true">${d.icon}</span>
|
|
508
567
|
<div class="dl-info"><span class="dl-name">${d.label}</span><span class="dl-desc">${d.desc}</span></div>
|
|
509
568
|
<span class="dl-size">${fileSize(d.file)}</span>
|
|
510
569
|
<span class="dl-btn">Download</span>
|
|
511
570
|
</a>`).join('');
|
|
512
|
-
return `<section
|
|
571
|
+
return `<section>
|
|
572
|
+
<div class="st">Downloads</div>
|
|
573
|
+
<p style="font-size:14px;color:var(--t3);margin-bottom:16px">Download setup scripts for different platforms. All files are open source and stay local.</p>
|
|
574
|
+
<div class="dl-grid">${dlCards}</div>
|
|
575
|
+
</section>`;
|
|
513
576
|
}
|
|
514
577
|
|
|
515
578
|
function tabDocs() {
|
|
516
|
-
const readmePath = path.join(ASSETS, 'README.md');
|
|
579
|
+
const readmePath = path.join(ASSETS, 'CRG-README.md');
|
|
517
580
|
const readmeHtml = fs.existsSync(readmePath) ? mdToHtml(fs.readFileSync(readmePath, 'utf8')) : '<p>No README found.</p>';
|
|
518
581
|
return `<section><div class="st">Documentation</div><div class="rc">${readmeHtml}</div></section>`;
|
|
519
582
|
}
|
|
@@ -681,6 +744,16 @@ function start(port, noOpen) {
|
|
|
681
744
|
res.writeHead(200, { 'Content-Type': 'text/markdown' });
|
|
682
745
|
res.end(report);
|
|
683
746
|
|
|
747
|
+
} else if (url.pathname === '/api/roi-report') {
|
|
748
|
+
const report = roi.generateROIReport(cwd);
|
|
749
|
+
res.writeHead(200, { 'Content-Type': 'text/markdown' });
|
|
750
|
+
res.end(report);
|
|
751
|
+
|
|
752
|
+
} else if (url.pathname === '/api/daily-breakdown') {
|
|
753
|
+
const breakdown = analytics.generateDailyBreakdown(cwd);
|
|
754
|
+
res.writeHead(200, { 'Content-Type': 'text/markdown' });
|
|
755
|
+
res.end(breakdown);
|
|
756
|
+
|
|
684
757
|
} else if (url.pathname === '/api/session' && req.method === 'POST') {
|
|
685
758
|
readBody(req, res, (data) => {
|
|
686
759
|
const session = analytics.createSession(data.project, data.operation);
|
|
File without changes
|