ccakashic 0.1.1 → 0.2.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/lib/html-generator.js +75 -10
- package/lib/parser.js +160 -12
- package/lib/template-assets.js +65 -0
- package/package.json +2 -2
package/lib/html-generator.js
CHANGED
|
@@ -132,12 +132,38 @@ function renderMessage(msg) {
|
|
|
132
132
|
const id = msgId(msg.timestamp);
|
|
133
133
|
const time = `<a class="timestamp" href="#${id}">${formatTime(msg.timestamp)}</a>`;
|
|
134
134
|
|
|
135
|
+
const turnBadge = msg.turnUsage
|
|
136
|
+
? (() => {
|
|
137
|
+
const c = calcCost(msg.turnUsage.input, msg.turnUsage.output, msg.turnUsage.cacheRead, msg.turnUsage.cacheCreate);
|
|
138
|
+
const dur = msg.turnUsage.durationMs ? ` | ${formatDuration(msg.turnUsage.durationMs)}` : '';
|
|
139
|
+
return `<span class="turn-usage">⚡ Turn: ${c.totalStr}${dur} <span class="usage-detail">(in:${c.inStr} out:${c.outStr} cache-r:${c.crStr} cache-w:${c.cwStr})</span></span>`;
|
|
140
|
+
})()
|
|
141
|
+
: '';
|
|
142
|
+
|
|
143
|
+
function makeItemBadge(m) {
|
|
144
|
+
const parts = [];
|
|
145
|
+
if (m.usage) {
|
|
146
|
+
const c = calcCost(m.usage.input_tokens || 0, m.usage.output_tokens || 0, m.usage.cache_read_input_tokens || 0, m.usage.cache_creation_input_tokens || 0);
|
|
147
|
+
parts.push(`${c.totalStr}`);
|
|
148
|
+
}
|
|
149
|
+
if (m.execMs) {
|
|
150
|
+
parts.push(`${formatDuration(m.execMs)}`);
|
|
151
|
+
} else if (m.elapsedMs && m.elapsedMs >= 1000) {
|
|
152
|
+
parts.push(`${formatDuration(m.elapsedMs)}`);
|
|
153
|
+
}
|
|
154
|
+
if (!parts.length) return '';
|
|
155
|
+
const detail = m.usage ? ` <span class="usage-detail">(in:${formatCost(tokenCostUsd(m.usage.input_tokens||0, COST_PER_M.input))} out:${formatCost(tokenCostUsd(m.usage.output_tokens||0, COST_PER_M.output))} cache-r:${formatCost(tokenCostUsd(m.usage.cache_read_input_tokens||0, COST_PER_M.cacheRead))} cache-w:${formatCost(tokenCostUsd(m.usage.cache_creation_input_tokens||0, COST_PER_M.cacheWrite))})</span>` : '';
|
|
156
|
+
return `<span class="item-usage">${parts.join(' | ')}${detail}</span>`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const itemBadge = makeItemBadge(msg);
|
|
160
|
+
|
|
135
161
|
switch (msg.type) {
|
|
136
162
|
case 'user':
|
|
137
|
-
return `<div class="msg msg-user" id="${id}">${time}<div class="msg-content" data-markdown>${escapeHtml(msg.text)}</div
|
|
163
|
+
return `<div class="msg msg-user" id="${id}">${time}<div class="msg-content" data-markdown>${escapeHtml(msg.text)}</div>${turnBadge}</div>`;
|
|
138
164
|
|
|
139
165
|
case 'assistant':
|
|
140
|
-
return `<div class="msg msg-assistant" id="${id}">${time}<div class="msg-content" data-markdown>${escapeHtml(msg.text)}</div
|
|
166
|
+
return `<div class="msg msg-assistant" id="${id}">${time}<div class="msg-content" data-markdown>${escapeHtml(msg.text)}</div>${itemBadge ? `<div>${itemBadge}</div>` : ''}</div>`;
|
|
141
167
|
|
|
142
168
|
case 'thinking':
|
|
143
169
|
return `<div class="msg msg-thinking" id="${id}">${time}<span class="thinking-indicator">Thinking...</span></div>`;
|
|
@@ -148,16 +174,23 @@ function renderMessage(msg) {
|
|
|
148
174
|
const subagentHtml = msg.subagentMessages
|
|
149
175
|
? `<div class="subagent-inline"><details><summary><span class="tool-summary">Subagent conversation</span></summary><div class="subagent-content">${msg.subagentMessages.map(renderMessage).join('\n')}</div></details></div>`
|
|
150
176
|
: '';
|
|
151
|
-
return `<div class="msg msg-tool" id="${id}"><details><summary>${time}<span class="tool-summary">${summary}</span></summary><div class="tool-details">${renderToolInput(msg)}${resultHtml}${subagentHtml}</div></details
|
|
177
|
+
return `<div class="msg msg-tool" id="${id}"><details><summary>${time}<span class="tool-summary">${summary}</span></summary><div class="tool-details">${renderToolInput(msg)}${resultHtml}${subagentHtml}</div></details>${itemBadge ? `<div class="tool-usage-row">${itemBadge}</div>` : ''}</div>`;
|
|
152
178
|
}
|
|
153
179
|
|
|
154
180
|
case 'tool_result':
|
|
155
181
|
// Unpaired tool result (shouldn't happen often)
|
|
156
182
|
return `<div class="msg msg-tool" id="${id}"><div class="tool-output"><pre><code>${escapeHtml((msg.content || '').slice(0, 2000))}</code></pre></div></div>`;
|
|
157
183
|
|
|
184
|
+
case 'local_command': {
|
|
185
|
+
const cmd = msg.command ? `<div class="local-cmd-input"><span class="local-cmd-prompt">$</span> ${escapeHtml(msg.command)}</div>` : '';
|
|
186
|
+
const stdout = msg.stdout && msg.stdout.trim() ? `<pre class="local-cmd-output"><code>${escapeHtml(msg.stdout)}</code></pre>` : '';
|
|
187
|
+
const stderr = msg.stderr && msg.stderr.trim() ? `<pre class="local-cmd-output local-cmd-stderr"><code>${escapeHtml(msg.stderr)}</code></pre>` : '';
|
|
188
|
+
return `<div class="msg msg-local-cmd" id="${id}">${time}${cmd}${stdout}${stderr}${turnBadge}</div>`;
|
|
189
|
+
}
|
|
190
|
+
|
|
158
191
|
case 'system':
|
|
159
192
|
if (msg.subtype === 'turn_duration') {
|
|
160
|
-
return
|
|
193
|
+
return ''; // Now shown in turn usage badge
|
|
161
194
|
}
|
|
162
195
|
return `<div class="msg msg-system" id="${id}">${escapeHtml(msg.content || '')}</div>`;
|
|
163
196
|
|
|
@@ -214,6 +247,40 @@ function groupMessagesByDate(messages) {
|
|
|
214
247
|
return groups;
|
|
215
248
|
}
|
|
216
249
|
|
|
250
|
+
// Cost per 1M tokens (USD) - Claude Opus 4 pricing
|
|
251
|
+
const COST_PER_M = {
|
|
252
|
+
input: 15,
|
|
253
|
+
output: 75,
|
|
254
|
+
cacheRead: 1.5,
|
|
255
|
+
cacheWrite: 18.75,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
function tokenCostUsd(tokens, rate) {
|
|
259
|
+
return (tokens / 1_000_000) * rate;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function formatCost(usd) {
|
|
263
|
+
if (usd < 0.001) return '<$0.01';
|
|
264
|
+
if (usd < 0.01) return `$${usd.toFixed(3)}`;
|
|
265
|
+
if (usd < 1) return `$${usd.toFixed(2)}`;
|
|
266
|
+
return `$${usd.toFixed(2)}`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function calcCost(input, output, cacheRead, cacheWrite) {
|
|
270
|
+
const inCost = tokenCostUsd(input, COST_PER_M.input);
|
|
271
|
+
const outCost = tokenCostUsd(output, COST_PER_M.output);
|
|
272
|
+
const crCost = tokenCostUsd(cacheRead, COST_PER_M.cacheRead);
|
|
273
|
+
const cwCost = tokenCostUsd(cacheWrite, COST_PER_M.cacheWrite);
|
|
274
|
+
const total = inCost + outCost + crCost + cwCost;
|
|
275
|
+
return {
|
|
276
|
+
totalStr: formatCost(total),
|
|
277
|
+
inStr: formatCost(inCost),
|
|
278
|
+
outStr: formatCost(outCost),
|
|
279
|
+
crStr: formatCost(crCost),
|
|
280
|
+
cwStr: formatCost(cwCost),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
217
284
|
function formatTokens(n) {
|
|
218
285
|
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
|
|
219
286
|
if (n >= 1000) return (n / 1000).toFixed(1) + 'K';
|
|
@@ -233,20 +300,18 @@ function formatDurationLong(ms) {
|
|
|
233
300
|
function renderStats(stats) {
|
|
234
301
|
if (!stats || !stats.turns) return '';
|
|
235
302
|
|
|
303
|
+
const totalCost = calcCost(stats.inputTokens, stats.outputTokens, stats.cacheRead, stats.cacheCreation);
|
|
304
|
+
|
|
236
305
|
const items = [];
|
|
306
|
+
items.push(`<span class="stat-item"><span class="stat-label">Est. Cost</span><span class="stat-value">${totalCost.totalStr}</span></span>`);
|
|
237
307
|
items.push(`<span class="stat-item"><span class="stat-label">Turns</span><span class="stat-value">${stats.turns}</span></span>`);
|
|
238
308
|
items.push(`<span class="stat-item"><span class="stat-label">Input</span><span class="stat-value">${formatTokens(stats.inputTokens)}</span></span>`);
|
|
239
309
|
items.push(`<span class="stat-item"><span class="stat-label">Output</span><span class="stat-value">${formatTokens(stats.outputTokens)}</span></span>`);
|
|
240
|
-
items.push(`<span class="stat-item"><span class="stat-label">Cache Create</span><span class="stat-value">${formatTokens(stats.cacheCreation)}</span></span>`);
|
|
241
310
|
items.push(`<span class="stat-item"><span class="stat-label">Cache Read</span><span class="stat-value">${formatTokens(stats.cacheRead)}</span></span>`);
|
|
242
|
-
items.push(`<span class="stat-item"><span class="stat-label">
|
|
311
|
+
items.push(`<span class="stat-item"><span class="stat-label">Cache Write</span><span class="stat-value">${formatTokens(stats.cacheCreation)}</span></span>`);
|
|
243
312
|
items.push(`<span class="stat-item"><span class="stat-label">Cache Hit</span><span class="stat-value">${(stats.cacheHitRate * 100).toFixed(0)}%</span></span>`);
|
|
244
313
|
if (stats.durationMs) {
|
|
245
314
|
items.push(`<span class="stat-item"><span class="stat-label">Duration</span><span class="stat-value">${formatDurationLong(stats.durationMs)}</span></span>`);
|
|
246
|
-
if (stats.outputTokens && stats.durationMs > 0) {
|
|
247
|
-
const tokPerMin = Math.round(stats.outputTokens / (stats.durationMs / 60000));
|
|
248
|
-
items.push(`<span class="stat-item"><span class="stat-label">Output/min</span><span class="stat-value">${formatTokens(tokPerMin)}</span></span>`);
|
|
249
|
-
}
|
|
250
315
|
}
|
|
251
316
|
|
|
252
317
|
return `<div class="stats-bar">${items.join('')}</div>`;
|
package/lib/parser.js
CHANGED
|
@@ -142,21 +142,97 @@ function buildConversation(lines) {
|
|
|
142
142
|
// Pair tool_use with tool_result
|
|
143
143
|
pairToolMessages(messages);
|
|
144
144
|
|
|
145
|
+
// Calculate per-turn token usage (tokens between each user input)
|
|
146
|
+
calculateTurnUsage(messages);
|
|
147
|
+
|
|
148
|
+
// Calculate elapsed time for each message (time since previous message)
|
|
149
|
+
calculateElapsed(messages);
|
|
150
|
+
|
|
145
151
|
return messages;
|
|
146
152
|
}
|
|
147
153
|
|
|
154
|
+
function parseLocalCommand(text) {
|
|
155
|
+
// Parse <bash-input>, <bash-stdout>, <bash-stderr> tags
|
|
156
|
+
const inputMatch = text.match(/<bash-input>([\s\S]*?)<\/bash-input>/);
|
|
157
|
+
const stdoutMatch = text.match(/<bash-stdout>([\s\S]*?)<\/bash-stdout>/);
|
|
158
|
+
const stderrMatch = text.match(/<bash-stderr>([\s\S]*?)<\/bash-stderr>/);
|
|
159
|
+
|
|
160
|
+
if (inputMatch) {
|
|
161
|
+
return { type: 'local_command', subtype: 'input', command: inputMatch[1] };
|
|
162
|
+
}
|
|
163
|
+
if (stdoutMatch || stderrMatch) {
|
|
164
|
+
return {
|
|
165
|
+
type: 'local_command',
|
|
166
|
+
subtype: 'output',
|
|
167
|
+
stdout: stdoutMatch ? stdoutMatch[1] : '',
|
|
168
|
+
stderr: stderrMatch ? stderrMatch[1] : '',
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function processUserText(text, line, messages) {
|
|
175
|
+
// Skip local-command-caveat (system instruction, not user input)
|
|
176
|
+
if (text.match(/^<local-command-caveat>/)) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Parse local command input/output
|
|
181
|
+
const localCmd = parseLocalCommand(text);
|
|
182
|
+
if (localCmd) {
|
|
183
|
+
if (localCmd.subtype === 'input') {
|
|
184
|
+
messages.push({
|
|
185
|
+
type: 'local_command',
|
|
186
|
+
command: localCmd.command,
|
|
187
|
+
timestamp: line.timestamp,
|
|
188
|
+
uuid: line.uuid,
|
|
189
|
+
});
|
|
190
|
+
} else if (localCmd.subtype === 'output') {
|
|
191
|
+
// Attach to previous local_command if exists
|
|
192
|
+
const prev = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
193
|
+
if (prev && prev.type === 'local_command' && !prev.stdout) {
|
|
194
|
+
prev.stdout = localCmd.stdout;
|
|
195
|
+
prev.stderr = localCmd.stderr;
|
|
196
|
+
} else {
|
|
197
|
+
messages.push({
|
|
198
|
+
type: 'local_command',
|
|
199
|
+
command: '',
|
|
200
|
+
stdout: localCmd.stdout,
|
|
201
|
+
stderr: localCmd.stderr,
|
|
202
|
+
timestamp: line.timestamp,
|
|
203
|
+
uuid: line.uuid,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Strip system-reminder tags but keep surrounding user text
|
|
211
|
+
const stripped = text
|
|
212
|
+
.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '')
|
|
213
|
+
.replace(/<command-name>[\s\S]*?<\/command-name>/g, '')
|
|
214
|
+
.replace(/<command-message>[\s\S]*?<\/command-message>/g, '')
|
|
215
|
+
.replace(/<command-args>[\s\S]*?<\/command-args>/g, '')
|
|
216
|
+
.replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g, '')
|
|
217
|
+
.trim();
|
|
218
|
+
|
|
219
|
+
if (stripped) {
|
|
220
|
+
messages.push({
|
|
221
|
+
type: 'user',
|
|
222
|
+
text: stripped,
|
|
223
|
+
timestamp: line.timestamp,
|
|
224
|
+
uuid: line.uuid,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
148
229
|
function processUserMessage(line, messages) {
|
|
149
230
|
const content = line.message?.content;
|
|
150
231
|
if (!content) return;
|
|
151
232
|
|
|
152
233
|
if (typeof content === 'string') {
|
|
153
234
|
if (content.trim()) {
|
|
154
|
-
messages
|
|
155
|
-
type: 'user',
|
|
156
|
-
text: content,
|
|
157
|
-
timestamp: line.timestamp,
|
|
158
|
-
uuid: line.uuid,
|
|
159
|
-
});
|
|
235
|
+
processUserText(content, line, messages);
|
|
160
236
|
}
|
|
161
237
|
return;
|
|
162
238
|
}
|
|
@@ -164,12 +240,7 @@ function processUserMessage(line, messages) {
|
|
|
164
240
|
if (Array.isArray(content)) {
|
|
165
241
|
for (const block of content) {
|
|
166
242
|
if (block.type === 'text' && block.text?.trim()) {
|
|
167
|
-
messages
|
|
168
|
-
type: 'user',
|
|
169
|
-
text: block.text,
|
|
170
|
-
timestamp: line.timestamp,
|
|
171
|
-
uuid: line.uuid,
|
|
172
|
-
});
|
|
243
|
+
processUserText(block.text, line, messages);
|
|
173
244
|
} else if (block.type === 'tool_result') {
|
|
174
245
|
const toolResult = {
|
|
175
246
|
type: 'tool_result',
|
|
@@ -218,6 +289,9 @@ function processAssistantMessage(line, messages) {
|
|
|
218
289
|
const content = line.message?.content;
|
|
219
290
|
if (!content || !Array.isArray(content)) return;
|
|
220
291
|
|
|
292
|
+
const usage = line.message?.usage || null;
|
|
293
|
+
let usageAssigned = false;
|
|
294
|
+
|
|
221
295
|
for (const block of content) {
|
|
222
296
|
if (block.type === 'thinking') {
|
|
223
297
|
messages.push({
|
|
@@ -232,7 +306,9 @@ function processAssistantMessage(line, messages) {
|
|
|
232
306
|
model: line.message.model,
|
|
233
307
|
timestamp: line.timestamp,
|
|
234
308
|
uuid: line.uuid,
|
|
309
|
+
usage: !usageAssigned ? usage : null,
|
|
235
310
|
});
|
|
311
|
+
usageAssigned = true;
|
|
236
312
|
} else if (block.type === 'tool_use') {
|
|
237
313
|
messages.push({
|
|
238
314
|
type: 'tool_use',
|
|
@@ -241,7 +317,79 @@ function processAssistantMessage(line, messages) {
|
|
|
241
317
|
input: block.input,
|
|
242
318
|
timestamp: line.timestamp,
|
|
243
319
|
uuid: line.uuid,
|
|
320
|
+
usage: !usageAssigned ? usage : null,
|
|
244
321
|
});
|
|
322
|
+
usageAssigned = true;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function calculateTurnUsage(messages) {
|
|
328
|
+
// Walk backwards from each user message, summing usage of preceding assistant messages
|
|
329
|
+
// A "turn" = user input → all assistant responses until next user input
|
|
330
|
+
for (let i = 0; i < messages.length; i++) {
|
|
331
|
+
if (messages[i].type !== 'user' && messages[i].type !== 'local_command') continue;
|
|
332
|
+
|
|
333
|
+
// Sum usage of all following assistant/tool messages until next user message
|
|
334
|
+
let totalInput = 0;
|
|
335
|
+
let totalOutput = 0;
|
|
336
|
+
let totalCacheRead = 0;
|
|
337
|
+
let totalCacheCreate = 0;
|
|
338
|
+
const seenUsageIds = new Set(); // Dedupe (multiple blocks from same API response)
|
|
339
|
+
|
|
340
|
+
for (let j = i + 1; j < messages.length; j++) {
|
|
341
|
+
const m = messages[j];
|
|
342
|
+
if (m.type === 'user' || m.type === 'local_command') break;
|
|
343
|
+
if (m.usage && !seenUsageIds.has(m.uuid + (m.usage.input_tokens || 0))) {
|
|
344
|
+
seenUsageIds.add(m.uuid + (m.usage.input_tokens || 0));
|
|
345
|
+
totalInput += m.usage.input_tokens || 0;
|
|
346
|
+
totalOutput += m.usage.output_tokens || 0;
|
|
347
|
+
totalCacheRead += m.usage.cache_read_input_tokens || 0;
|
|
348
|
+
totalCacheCreate += m.usage.cache_creation_input_tokens || 0;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Find turn_duration in the following messages
|
|
353
|
+
let turnDurationMs = 0;
|
|
354
|
+
for (let j = i + 1; j < messages.length; j++) {
|
|
355
|
+
const m = messages[j];
|
|
356
|
+
if (m.type === 'user' || m.type === 'local_command') break;
|
|
357
|
+
if (m.type === 'system' && m.subtype === 'turn_duration') {
|
|
358
|
+
turnDurationMs = m.durationMs || 0;
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const total = totalInput + totalOutput + totalCacheRead + totalCacheCreate;
|
|
364
|
+
if (total > 0 || turnDurationMs > 0) {
|
|
365
|
+
messages[i].turnUsage = {
|
|
366
|
+
input: totalInput,
|
|
367
|
+
output: totalOutput,
|
|
368
|
+
cacheRead: totalCacheRead,
|
|
369
|
+
cacheCreate: totalCacheCreate,
|
|
370
|
+
total,
|
|
371
|
+
durationMs: turnDurationMs,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function calculateElapsed(messages) {
|
|
378
|
+
for (let i = 1; i < messages.length; i++) {
|
|
379
|
+
const prev = messages[i - 1];
|
|
380
|
+
const curr = messages[i];
|
|
381
|
+
if (prev.timestamp && curr.timestamp) {
|
|
382
|
+
const elapsed = new Date(curr.timestamp) - new Date(prev.timestamp);
|
|
383
|
+
if (elapsed > 0) {
|
|
384
|
+
curr.elapsedMs = elapsed;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// For tool_use with paired result, calculate execution time
|
|
388
|
+
if (curr.type === 'tool_use' && curr.result?.timestamp && curr.timestamp) {
|
|
389
|
+
const execTime = new Date(curr.result.timestamp) - new Date(curr.timestamp);
|
|
390
|
+
if (execTime > 0) {
|
|
391
|
+
curr.execMs = execTime;
|
|
392
|
+
}
|
|
245
393
|
}
|
|
246
394
|
}
|
|
247
395
|
}
|
package/lib/template-assets.js
CHANGED
|
@@ -131,6 +131,71 @@ body {
|
|
|
131
131
|
position: relative;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/* Local command (user ! commands) */
|
|
135
|
+
.msg-local-cmd {
|
|
136
|
+
align-self: flex-end;
|
|
137
|
+
margin-left: 20%;
|
|
138
|
+
background: var(--bg-secondary);
|
|
139
|
+
border: 1px solid var(--border);
|
|
140
|
+
border-radius: 8px;
|
|
141
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
|
142
|
+
font-size: 0.82rem;
|
|
143
|
+
}
|
|
144
|
+
.local-cmd-input {
|
|
145
|
+
padding: 2px 0;
|
|
146
|
+
font-weight: 600;
|
|
147
|
+
}
|
|
148
|
+
.local-cmd-prompt {
|
|
149
|
+
color: var(--text-muted);
|
|
150
|
+
margin-right: 4px;
|
|
151
|
+
}
|
|
152
|
+
.local-cmd-output {
|
|
153
|
+
margin-top: 4px;
|
|
154
|
+
padding: 6px 8px;
|
|
155
|
+
background: var(--code-block-bg);
|
|
156
|
+
color: var(--code-block-text);
|
|
157
|
+
border-radius: 4px;
|
|
158
|
+
overflow-x: auto;
|
|
159
|
+
font-size: 0.78rem;
|
|
160
|
+
max-height: 200px;
|
|
161
|
+
overflow-y: auto;
|
|
162
|
+
}
|
|
163
|
+
.local-cmd-output code {
|
|
164
|
+
background: none;
|
|
165
|
+
padding: 0;
|
|
166
|
+
color: inherit;
|
|
167
|
+
}
|
|
168
|
+
.local-cmd-stderr {
|
|
169
|
+
border-left: 3px solid #ef4444;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* Token usage badges */
|
|
173
|
+
.turn-usage, .item-usage {
|
|
174
|
+
display: inline-block;
|
|
175
|
+
margin-top: 6px;
|
|
176
|
+
padding: 2px 8px;
|
|
177
|
+
font-size: 0.68rem;
|
|
178
|
+
font-variant-numeric: tabular-nums;
|
|
179
|
+
color: var(--text-muted);
|
|
180
|
+
background: var(--bg-secondary);
|
|
181
|
+
border: 1px solid var(--border);
|
|
182
|
+
border-radius: 4px;
|
|
183
|
+
}
|
|
184
|
+
.turn-usage {
|
|
185
|
+
font-weight: 600;
|
|
186
|
+
}
|
|
187
|
+
.item-usage {
|
|
188
|
+
font-weight: 400;
|
|
189
|
+
opacity: 0.8;
|
|
190
|
+
}
|
|
191
|
+
.tool-usage-row {
|
|
192
|
+
padding: 4px 14px 6px;
|
|
193
|
+
}
|
|
194
|
+
.usage-detail {
|
|
195
|
+
font-size: 0.6rem;
|
|
196
|
+
opacity: 0.7;
|
|
197
|
+
}
|
|
198
|
+
|
|
134
199
|
.msg .timestamp {
|
|
135
200
|
display: block;
|
|
136
201
|
font-size: 0.7rem;
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccakashic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Browse Claude Code session logs (~/.claude/projects/) as beautiful HTML in your browser — an Akashic Record of your Claude Code sessions",
|
|
5
5
|
"bin": {
|
|
6
|
-
"ccakashic": "
|
|
6
|
+
"ccakashic": "bin/ccakashic.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"bin",
|