cc-agents 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/LICENSE +21 -0
- package/README.md +230 -0
- package/bin/cc-agents.mjs +17 -0
- package/dist/index.js +400 -0
- package/dist/token-tracker/public/app.js +230 -0
- package/dist/token-tracker/public/index.html +54 -0
- package/dist/token-tracker/public/style.css +233 -0
- package/package.json +37 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const POLL_MS = 3000;
|
|
4
|
+
|
|
5
|
+
const ROLE_COLORS = {
|
|
6
|
+
engineer: "#79c0ff",
|
|
7
|
+
genius: "#d2a8ff",
|
|
8
|
+
judge: "#ffa657",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const state = {
|
|
12
|
+
currentSessionId: null,
|
|
13
|
+
chart: null,
|
|
14
|
+
pollTimer: null,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function fmtTs(ms) {
|
|
18
|
+
const d = new Date(ms);
|
|
19
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
20
|
+
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function fmtDate(ms) {
|
|
24
|
+
const d = new Date(ms);
|
|
25
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
26
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function fmtTokens(n) {
|
|
30
|
+
if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;
|
|
31
|
+
return `${n}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function fmtDuration(ms) {
|
|
35
|
+
if (ms < 60_000) return `${Math.round(ms / 1000)}s`;
|
|
36
|
+
const min = Math.floor(ms / 60_000);
|
|
37
|
+
const sec = Math.round((ms % 60_000) / 1000);
|
|
38
|
+
return `${min}m${sec}s`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function fetchJson(url, opts) {
|
|
42
|
+
const res = await fetch(url, opts);
|
|
43
|
+
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
|
|
44
|
+
return res.json();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function loadSessions() {
|
|
48
|
+
const sessions = await fetchJson("/api/sessions");
|
|
49
|
+
const list = document.getElementById("sessions-list");
|
|
50
|
+
list.innerHTML = "";
|
|
51
|
+
for (const s of sessions) {
|
|
52
|
+
const li = document.createElement("li");
|
|
53
|
+
li.className = "session-item";
|
|
54
|
+
if (s.id === state.currentSessionId) li.classList.add("active");
|
|
55
|
+
const total = s.totalInput + s.totalOutput;
|
|
56
|
+
const dur = s.endedAt ? s.endedAt - s.startedAt : Date.now() - s.startedAt;
|
|
57
|
+
li.innerHTML = `
|
|
58
|
+
<div class="session-row">
|
|
59
|
+
<span class="session-when">${fmtDate(s.startedAt)}</span>
|
|
60
|
+
<span class="session-tokens">${fmtTokens(total)} tok</span>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="session-meta">
|
|
63
|
+
<span>${s.id.slice(0, 8)}…</span>
|
|
64
|
+
<span>${fmtDuration(dur)}</span>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="session-actions">
|
|
67
|
+
<button class="btn danger" data-action="delete">delete</button>
|
|
68
|
+
</div>
|
|
69
|
+
`;
|
|
70
|
+
li.addEventListener("click", (e) => {
|
|
71
|
+
if (e.target instanceof HTMLButtonElement) return;
|
|
72
|
+
selectSession(s.id);
|
|
73
|
+
});
|
|
74
|
+
li.querySelector('[data-action="delete"]').addEventListener("click", async (e) => {
|
|
75
|
+
e.stopPropagation();
|
|
76
|
+
if (!confirm(`Delete session ${s.id.slice(0, 8)}…?`)) return;
|
|
77
|
+
await fetchJson(`/api/sessions/${s.id}`, { method: "DELETE" });
|
|
78
|
+
if (state.currentSessionId === s.id) {
|
|
79
|
+
state.currentSessionId = null;
|
|
80
|
+
document.getElementById("current-session").textContent = "no session";
|
|
81
|
+
clearEvents();
|
|
82
|
+
clearChart();
|
|
83
|
+
}
|
|
84
|
+
await loadSessions();
|
|
85
|
+
});
|
|
86
|
+
list.appendChild(li);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!state.currentSessionId && sessions.length > 0) {
|
|
90
|
+
await selectSession(sessions[0].id);
|
|
91
|
+
} else if (state.currentSessionId) {
|
|
92
|
+
const cur = sessions.find((s) => s.id === state.currentSessionId);
|
|
93
|
+
if (!cur) {
|
|
94
|
+
state.currentSessionId = null;
|
|
95
|
+
clearEvents();
|
|
96
|
+
clearChart();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function clearEvents() {
|
|
102
|
+
document.getElementById("events-body").innerHTML = "";
|
|
103
|
+
document.getElementById("events-empty").style.display = "block";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function clearChart() {
|
|
107
|
+
if (state.chart) {
|
|
108
|
+
state.chart.destroy();
|
|
109
|
+
state.chart = null;
|
|
110
|
+
}
|
|
111
|
+
document.getElementById("byrole-table").innerHTML = "";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function selectSession(id) {
|
|
115
|
+
state.currentSessionId = id;
|
|
116
|
+
document.getElementById("current-session").textContent = `session: ${id.slice(0, 8)}…`;
|
|
117
|
+
await Promise.all([loadByRole(id), loadEvents(id)]);
|
|
118
|
+
document.querySelectorAll(".session-item").forEach((el) => el.classList.remove("active"));
|
|
119
|
+
for (const el of document.querySelectorAll(".session-item")) {
|
|
120
|
+
if (el.querySelector(".session-meta span")?.textContent?.startsWith(id.slice(0, 8))) {
|
|
121
|
+
el.classList.add("active");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function loadByRole(id) {
|
|
127
|
+
const rows = await fetchJson(`/api/sessions/${id}/by-role`);
|
|
128
|
+
const byRole = { engineer: { input: 0, output: 0, calls: 0 }, genius: { input: 0, output: 0, calls: 0 }, judge: { input: 0, output: 0, calls: 0 } };
|
|
129
|
+
for (const r of rows) byRole[r.role] = r;
|
|
130
|
+
|
|
131
|
+
renderChart(byRole);
|
|
132
|
+
renderByRoleTable(byRole);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function renderChart(byRole) {
|
|
136
|
+
const canvas = document.getElementById("chart-byrole");
|
|
137
|
+
const ctx = canvas.getContext("2d");
|
|
138
|
+
if (state.chart) state.chart.destroy();
|
|
139
|
+
|
|
140
|
+
const labels = ["engineer", "genius", "judge"];
|
|
141
|
+
const inputs = labels.map((r) => byRole[r].input);
|
|
142
|
+
const outputs = labels.map((r) => byRole[r].output);
|
|
143
|
+
|
|
144
|
+
state.chart = new Chart(ctx, {
|
|
145
|
+
type: "bar",
|
|
146
|
+
data: {
|
|
147
|
+
labels,
|
|
148
|
+
datasets: [
|
|
149
|
+
{ label: "input", data: inputs, backgroundColor: "#58a6ff" },
|
|
150
|
+
{ label: "output", data: outputs, backgroundColor: "#3fb950" },
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
options: {
|
|
154
|
+
indexAxis: "y",
|
|
155
|
+
responsive: true,
|
|
156
|
+
maintainAspectRatio: false,
|
|
157
|
+
plugins: { legend: { labels: { color: "#8b949e" } } },
|
|
158
|
+
scales: {
|
|
159
|
+
x: { stacked: true, ticks: { color: "#8b949e" }, grid: { color: "#30363d" } },
|
|
160
|
+
y: { stacked: true, ticks: { color: "#e6edf3" }, grid: { display: false } },
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function renderByRoleTable(byRole) {
|
|
167
|
+
const tbody = document.getElementById("byrole-table");
|
|
168
|
+
tbody.innerHTML = "";
|
|
169
|
+
for (const role of ["engineer", "genius", "judge"]) {
|
|
170
|
+
const r = byRole[role];
|
|
171
|
+
const tr = document.createElement("tr");
|
|
172
|
+
tr.innerHTML = `
|
|
173
|
+
<td><span class="role-pill ${role}">${role}</span></td>
|
|
174
|
+
<td>${fmtTokens(r.input)} in / ${fmtTokens(r.output)} out · ${r.calls} calls</td>
|
|
175
|
+
`;
|
|
176
|
+
tbody.appendChild(tr);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function loadEvents(id) {
|
|
181
|
+
const events = await fetchJson(`/api/sessions/${id}/events`);
|
|
182
|
+
const body = document.getElementById("events-body");
|
|
183
|
+
const empty = document.getElementById("events-empty");
|
|
184
|
+
body.innerHTML = "";
|
|
185
|
+
if (events.length === 0) {
|
|
186
|
+
empty.style.display = "block";
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
empty.style.display = "none";
|
|
190
|
+
for (const e of events) {
|
|
191
|
+
const tr = document.createElement("tr");
|
|
192
|
+
tr.innerHTML = `
|
|
193
|
+
<td>${fmtTs(e.ts)}</td>
|
|
194
|
+
<td><span class="role-pill ${e.role}">${e.role}</span></td>
|
|
195
|
+
<td>${e.command}</td>
|
|
196
|
+
<td>${e.messagesCount}</td>
|
|
197
|
+
<td>${e.hasTools ? "✓" : "—"}</td>
|
|
198
|
+
<td class="num">${fmtTokens(e.inputTokens)}</td>
|
|
199
|
+
<td class="num">${fmtTokens(e.outputTokens)}</td>
|
|
200
|
+
<td class="num">${fmtTokens(e.lastInputTokens)}</td>
|
|
201
|
+
<td class="num">${e.durationMs}</td>
|
|
202
|
+
`;
|
|
203
|
+
body.appendChild(tr);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function poll() {
|
|
208
|
+
try {
|
|
209
|
+
await loadSessions();
|
|
210
|
+
if (state.currentSessionId) {
|
|
211
|
+
await Promise.all([loadByRole(state.currentSessionId), loadEvents(state.currentSessionId)]);
|
|
212
|
+
}
|
|
213
|
+
} catch (err) {
|
|
214
|
+
console.warn("poll failed:", err);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function startPolling() {
|
|
219
|
+
if (state.pollTimer) return;
|
|
220
|
+
state.pollTimer = setInterval(poll, POLL_MS);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
(async function init() {
|
|
224
|
+
try {
|
|
225
|
+
await poll();
|
|
226
|
+
startPolling();
|
|
227
|
+
} catch (err) {
|
|
228
|
+
console.error("init failed:", err);
|
|
229
|
+
}
|
|
230
|
+
})();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>token-tracker</title>
|
|
6
|
+
<link rel="stylesheet" href="/style.css">
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.js"></script>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<header class="top">
|
|
11
|
+
<h1>token-tracker</h1>
|
|
12
|
+
<div class="session-info">
|
|
13
|
+
<span id="current-session" class="dim">no session</span>
|
|
14
|
+
<span id="live-dot" class="dot live"></span>
|
|
15
|
+
</div>
|
|
16
|
+
</header>
|
|
17
|
+
|
|
18
|
+
<main class="grid">
|
|
19
|
+
<section class="panel" id="panel-sessions">
|
|
20
|
+
<h2>sessions</h2>
|
|
21
|
+
<ol id="sessions-list" class="sessions-list"></ol>
|
|
22
|
+
</section>
|
|
23
|
+
|
|
24
|
+
<section class="panel" id="panel-byrole">
|
|
25
|
+
<h2>by role</h2>
|
|
26
|
+
<div class="chart-wrap"><canvas id="chart-byrole"></canvas></div>
|
|
27
|
+
<table id="byrole-table" class="kv-table"></table>
|
|
28
|
+
</section>
|
|
29
|
+
|
|
30
|
+
<section class="panel" id="panel-events">
|
|
31
|
+
<h2>events</h2>
|
|
32
|
+
<table id="events-table" class="data-table">
|
|
33
|
+
<thead>
|
|
34
|
+
<tr>
|
|
35
|
+
<th>ts</th>
|
|
36
|
+
<th>role</th>
|
|
37
|
+
<th>cmd</th>
|
|
38
|
+
<th>msg</th>
|
|
39
|
+
<th>tools</th>
|
|
40
|
+
<th class="num">in</th>
|
|
41
|
+
<th class="num">out</th>
|
|
42
|
+
<th class="num">ctx</th>
|
|
43
|
+
<th class="num">dur ms</th>
|
|
44
|
+
</tr>
|
|
45
|
+
</thead>
|
|
46
|
+
<tbody id="events-body"></tbody>
|
|
47
|
+
</table>
|
|
48
|
+
<div id="events-empty" class="dim empty">no events</div>
|
|
49
|
+
</section>
|
|
50
|
+
</main>
|
|
51
|
+
|
|
52
|
+
<script src="/app.js"></script>
|
|
53
|
+
</body>
|
|
54
|
+
</html>
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: dark;
|
|
3
|
+
--bg: #0d1117;
|
|
4
|
+
--panel: #161b22;
|
|
5
|
+
--border: #30363d;
|
|
6
|
+
--fg: #e6edf3;
|
|
7
|
+
--dim: #8b949e;
|
|
8
|
+
--accent: #58a6ff;
|
|
9
|
+
--engineer: #79c0ff;
|
|
10
|
+
--genius: #d2a8ff;
|
|
11
|
+
--judge: #ffa657;
|
|
12
|
+
--danger: #f85149;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
* {
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
html, body {
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
height: 100%;
|
|
23
|
+
background: var(--bg);
|
|
24
|
+
color: var(--fg);
|
|
25
|
+
font: 13px/1.4 -apple-system, "SF Pro Text", "Segoe UI", Helvetica, Arial, sans-serif;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.top {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: space-between;
|
|
32
|
+
padding: 10px 16px;
|
|
33
|
+
border-bottom: 1px solid var(--border);
|
|
34
|
+
background: var(--panel);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.top h1 {
|
|
38
|
+
margin: 0;
|
|
39
|
+
font-size: 14px;
|
|
40
|
+
font-weight: 600;
|
|
41
|
+
letter-spacing: 0.02em;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.session-info {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
gap: 8px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.dim {
|
|
51
|
+
color: var(--dim);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.dot {
|
|
55
|
+
width: 8px;
|
|
56
|
+
height: 8px;
|
|
57
|
+
border-radius: 50%;
|
|
58
|
+
background: var(--dim);
|
|
59
|
+
display: inline-block;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.dot.live {
|
|
63
|
+
background: #3fb950;
|
|
64
|
+
box-shadow: 0 0 6px #3fb950aa;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.grid {
|
|
68
|
+
display: grid;
|
|
69
|
+
grid-template-columns: 280px 320px 1fr;
|
|
70
|
+
gap: 1px;
|
|
71
|
+
background: var(--border);
|
|
72
|
+
height: calc(100vh - 41px);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.panel {
|
|
76
|
+
background: var(--panel);
|
|
77
|
+
padding: 12px 14px;
|
|
78
|
+
overflow: auto;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.panel h2 {
|
|
82
|
+
margin: 0 0 10px;
|
|
83
|
+
font-size: 11px;
|
|
84
|
+
text-transform: uppercase;
|
|
85
|
+
letter-spacing: 0.08em;
|
|
86
|
+
color: var(--dim);
|
|
87
|
+
font-weight: 600;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.sessions-list {
|
|
91
|
+
list-style: none;
|
|
92
|
+
margin: 0;
|
|
93
|
+
padding: 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.session-item {
|
|
97
|
+
padding: 8px 10px;
|
|
98
|
+
border: 1px solid var(--border);
|
|
99
|
+
border-radius: 6px;
|
|
100
|
+
margin-bottom: 8px;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
transition: background 0.1s;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.session-item:hover {
|
|
106
|
+
background: #1f242c;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.session-item.active {
|
|
110
|
+
border-color: var(--accent);
|
|
111
|
+
background: #1f2733;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.session-item.current {
|
|
115
|
+
border-color: #3fb950;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.session-row {
|
|
119
|
+
display: flex;
|
|
120
|
+
justify-content: space-between;
|
|
121
|
+
align-items: baseline;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.session-when {
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.session-tokens {
|
|
129
|
+
color: var(--dim);
|
|
130
|
+
font-variant-numeric: tabular-nums;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.session-meta {
|
|
134
|
+
display: flex;
|
|
135
|
+
justify-content: space-between;
|
|
136
|
+
font-size: 11px;
|
|
137
|
+
color: var(--dim);
|
|
138
|
+
margin-top: 2px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.session-actions {
|
|
142
|
+
display: flex;
|
|
143
|
+
gap: 6px;
|
|
144
|
+
margin-top: 6px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.btn {
|
|
148
|
+
background: transparent;
|
|
149
|
+
border: 1px solid var(--border);
|
|
150
|
+
color: var(--fg);
|
|
151
|
+
padding: 2px 8px;
|
|
152
|
+
border-radius: 4px;
|
|
153
|
+
font-size: 11px;
|
|
154
|
+
cursor: pointer;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.btn:hover {
|
|
158
|
+
border-color: var(--accent);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.btn.danger:hover {
|
|
162
|
+
border-color: var(--danger);
|
|
163
|
+
color: var(--danger);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.chart-wrap {
|
|
167
|
+
height: 180px;
|
|
168
|
+
margin-bottom: 12px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.kv-table {
|
|
172
|
+
width: 100%;
|
|
173
|
+
border-collapse: collapse;
|
|
174
|
+
font-variant-numeric: tabular-nums;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.kv-table td {
|
|
178
|
+
padding: 4px 0;
|
|
179
|
+
border-bottom: 1px dashed var(--border);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.kv-table td:last-child {
|
|
183
|
+
text-align: right;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.role-pill {
|
|
187
|
+
display: inline-block;
|
|
188
|
+
padding: 1px 6px;
|
|
189
|
+
border-radius: 3px;
|
|
190
|
+
font-size: 11px;
|
|
191
|
+
font-weight: 600;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.role-pill.engineer { background: #1f3a5a; color: var(--engineer); }
|
|
195
|
+
.role-pill.genius { background: #3a2a4a; color: var(--genius); }
|
|
196
|
+
.role-pill.judge { background: #4a3a1f; color: var(--judge); }
|
|
197
|
+
|
|
198
|
+
.data-table {
|
|
199
|
+
width: 100%;
|
|
200
|
+
border-collapse: collapse;
|
|
201
|
+
font-variant-numeric: tabular-nums;
|
|
202
|
+
font-size: 12px;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.data-table th {
|
|
206
|
+
text-align: left;
|
|
207
|
+
font-weight: 600;
|
|
208
|
+
color: var(--dim);
|
|
209
|
+
font-size: 11px;
|
|
210
|
+
padding: 4px 6px;
|
|
211
|
+
border-bottom: 1px solid var(--border);
|
|
212
|
+
position: sticky;
|
|
213
|
+
top: 0;
|
|
214
|
+
background: var(--panel);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.data-table th.num { text-align: right; }
|
|
218
|
+
|
|
219
|
+
.data-table td {
|
|
220
|
+
padding: 4px 6px;
|
|
221
|
+
border-bottom: 1px solid #1f242c22;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.data-table td.num { text-align: right; }
|
|
225
|
+
|
|
226
|
+
.data-table tr:hover td {
|
|
227
|
+
background: #1f242c44;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.empty {
|
|
231
|
+
padding: 12px;
|
|
232
|
+
text-align: center;
|
|
233
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cc-agents",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Three-role AI deliberation CLI: engineer proposes, genius challenges, judge points the next question.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"cc-agents": "./bin/cc-agents.mjs"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"bin",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"start": "tsx src/index.ts",
|
|
18
|
+
"build": "tsc --noEmit && node scripts/build.mjs",
|
|
19
|
+
"check": "biome check --write .",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"get-east-asian-width": "1.6.0",
|
|
25
|
+
"marked": "15.0.12"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@biomejs/biome": "2.3.5",
|
|
29
|
+
"@types/node": "22.19.19",
|
|
30
|
+
"esbuild": "^0.28.1",
|
|
31
|
+
"tsx": "4.22.1",
|
|
32
|
+
"typescript": "5.9.3"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22.19.0"
|
|
36
|
+
}
|
|
37
|
+
}
|