Files
game-fe-agent/session-report-20260516-1041.html
2026-05-21 21:56:04 +09:00

576 lines
48 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>claude usage</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
:root {
/* anthropic / claude-code palette */
--ivory: #FAF9F5;
--term-bg: #1a1918;
--term-fg: #d1cfc5;
--titlebar: #252321;
--outline: rgba(255,255,255,0.08);
--hover: rgba(255,255,255,0.035);
--clay: #D97757;
--dim: rgb(136,136,136);
--subtle: rgb(80,80,80);
--green: rgb(78,186,101);
--red: rgb(255,107,128);
--blue: rgb(177,185,249);
--yellow: rgb(255,193,7);
--mono: 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, Monaco, monospace;
}
* { box-sizing: border-box; }
html { background: var(--ivory); }
body {
margin: 0; padding: 48px 24px 80px;
font: 13px/1.55 var(--mono);
font-variant-numeric: tabular-nums;
color: var(--term-fg);
}
/* ——— terminal window chrome ——— */
.term {
max-width: 1180px; margin: 0 auto;
background: var(--term-bg);
border-radius: 8px;
outline: 1px solid var(--outline);
box-shadow: 0 20px 60px rgba(20,20,19,0.22);
}
.titlebar {
background: var(--titlebar);
border-radius: 8px 8px 0 0;
border-bottom: 1px solid var(--outline);
padding: 11px 14px;
display: flex; align-items: center; gap: 7px;
}
.titlebar .dot { width: 11px; height: 11px; border-radius: 50%; background: #3a3836; }
.titlebar .path { margin-left: 14px; color: var(--dim); font-size: 11px; }
.term-body { padding: 22px 30px 30px; }
/* ——— command + hero ——— */
.cmd { color: var(--dim); margin-bottom: 6px; }
.cmd .prompt { color: var(--clay); }
.cmd .flag { color: var(--blue); }
#meta-line { color: var(--subtle); font-size: 11px; }
#hero { margin: 14px 0 6px; }
#hero-total { font-size: 56px; font-weight: 700; line-height: 1; }
#hero-total .unit { color: var(--clay); }
#hero-total .label { font-size: 18px; font-weight: 400; color: var(--dim); margin-left: 8px; }
#hero-split { color: var(--dim); margin-top: 8px; }
#hero-split b { color: var(--term-fg); font-weight: 500; }
#hero-split .ok { color: var(--green); }
#hero-split .warn { color: var(--yellow); }
/* ——— sections ——— */
section { margin-top: 26px; }
.hr { color: var(--subtle); overflow: hidden; white-space: nowrap;
user-select: none; margin-bottom: 8px; }
.hr::after { content: '────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'; }
h2 { all: unset; display: block; color: var(--clay); font-weight: 500; }
h2::before { content: '▸ '; }
h2 .hint { color: var(--subtle); font-size: 11px; font-weight: 400; margin-left: 10px; }
.section-body { margin-top: 10px; }
/* ——— overall stat grid ——— */
#overall-grid { display: grid; gap: 4px 28px;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.stat { padding: 4px 0; }
.stat .label { font-size: 11px; color: var(--dim); }
.stat .val { font-size: 20px; font-weight: 500; }
.stat .detail { font-size: 11px; color: var(--subtle); }
/* ——— takeaways ——— */
.take { display: grid; grid-template-columns: 9ch 1fr; gap: 18px;
padding: 6px 0; align-items: baseline; }
.take .fig { text-align: right; font-weight: 700; font-size: 15px; }
.take .txt { color: var(--dim); }
.take .txt b { color: var(--term-fg); font-weight: 500; }
.take.bad .fig { color: var(--red); }
.take.good .fig { color: var(--green); }
.take.info .fig { color: var(--blue); }
/* ——— callouts (recommendations) ——— */
.callout { padding: 6px 0 6px 14px; border-left: 2px solid var(--subtle);
color: var(--dim); margin: 6px 0; }
.callout b, .callout code { color: var(--term-fg); }
/* ——— day pills + session gantt ——— */
.days { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.dpill { flex: 1; min-width: 84px; max-width: 140px; background: none;
border: 1px solid var(--subtle); border-radius: 4px;
padding: 9px 6px; font: inherit; color: var(--dim);
cursor: pointer; text-align: center; }
.dpill:hover { border-color: var(--dim); background: var(--hover); }
.dpill .dow { font-size: 10px; color: var(--subtle); display: block; }
.dpill .date { font-size: 11px; color: var(--term-fg); font-weight: 500;
display: block; margin: 2px 0 4px; }
.dpill .pct { font-size: 16px; font-weight: 700; color: var(--term-fg); display: block; }
.dpill .ns { font-size: 10px; color: var(--subtle); display: block; margin-top: 2px; }
.dpill.heaviest .pct { color: var(--clay); }
.dpill.sel { border-color: var(--clay); background: rgba(217,119,87,0.10); }
.gantt-hd { display: flex; justify-content: space-between; align-items: baseline;
margin-bottom: 6px; }
.gantt-hd .day { color: var(--term-fg); font-weight: 500; }
.gantt-hd .stats { font-size: 11px; color: var(--dim); }
.gantt-hd .stats b { color: var(--clay); }
.gantt { position: relative; border-top: 1px solid var(--outline);
border-bottom: 1px solid var(--outline); min-height: 32px; }
.lane { position: relative; height: 16px;
border-bottom: 1px dashed rgba(255,255,255,0.04); }
.seg { position: absolute; top: 2px; height: 12px; border-radius: 2px;
opacity: .85; cursor: crosshair; }
.seg:hover { opacity: 1; outline: 1px solid var(--term-fg); z-index: 2; }
.gantt-rule { position: absolute; top: 0; bottom: 0; width: 0;
border-left: 1px dashed var(--subtle); opacity: .4;
pointer-events: none; }
.gantt-axis { display: flex; justify-content: space-between;
font-size: 10px; color: var(--subtle); padding: 4px 0; }
.gantt-leg { font-size: 10px; color: var(--subtle); margin-top: 8px;
display: flex; gap: 14px; flex-wrap: wrap; }
.gantt-leg .sw { display: inline-block; width: 14px; height: 10px;
border-radius: 2px; vertical-align: middle; margin-right: 4px; }
/* ——— block-char bars ——— */
.bar { display: grid; grid-template-columns: 26ch 1fr 8ch; gap: 14px;
padding: 2px 0; align-items: center; }
.bar .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar .blocks { color: var(--clay); white-space: pre; overflow: hidden; }
.bar .blocks .empty { color: var(--subtle); }
.bar .pct { text-align: right; color: var(--dim); }
.bar:hover .name { color: var(--clay); }
/* ——— drill-down lists (top prompts, cache breaks) ——— */
.drill details { border-top: 1px solid var(--outline); }
.drill details:last-of-type { border-bottom: 1px solid var(--outline); }
.drill summary { list-style: none; cursor: pointer;
display: grid; grid-template-columns: 8ch 1fr; gap: 16px;
padding: 9px 4px; align-items: baseline; }
.drill summary::-webkit-details-marker { display: none; }
.drill summary:hover { background: var(--hover); }
.drill .amt { font-weight: 700; text-align: right; color: var(--clay); }
.drill .desc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drill .meta { grid-column: 2; font-size: 11px; color: var(--subtle); }
.drill details[open] summary .desc { white-space: normal; }
.drill .body { padding: 4px 4px 14px calc(8ch + 20px); font-size: 12px; color: var(--dim); }
/* ——— transcript context (±2 user msgs) — light inset for legibility ——— */
.ctx { margin: 8px 0 12px; padding: 10px 14px;
background: #F0EEE6; color: #1a1918;
border-radius: 6px; font-size: 12px; }
.ctx-msg { padding: 4px 0; white-space: pre-wrap; }
.ctx-msg .who { color: #87867F; font-size: 11px; }
.ctx-msg .ts { color: #87867F; font-size: 10px; margin-left: 8px; }
.ctx-msg.here { margin: 2px -14px; padding: 6px 11px 6px 14px;
border-left: 3px solid var(--clay);
background: rgba(217,119,87,0.10); }
.ctx-msg.here .who { color: var(--clay); font-weight: 500; }
.ctx-gap { color: #87867F; font-size: 11px; padding: 3px 0 3px 7ch; }
.ctx-gap::before { content: '⟨ '; }
.ctx-gap::after { content: ' ⟩'; }
.ctx-break { margin: 2px -14px; padding: 8px 11px 8px 14px;
border-left: 3px solid #BD5E6D;
background: rgba(189,94,109,0.12); color: #A63244; }
.ctx-break b { color: #1a1918; margin-right: 6px; }
.more-btn { display: block; width: 100%; margin-top: 10px; padding: 9px;
background: none; border: 1px dashed var(--subtle); cursor: pointer;
font: 500 11px/1 var(--mono); letter-spacing: 0.06em;
color: var(--dim); }
.more-btn:hover { border-color: var(--dim); color: var(--term-fg); }
/* ——— tables ——— */
.scroll { max-height: 440px; overflow: auto;
border-top: 1px solid var(--outline); border-bottom: 1px solid var(--outline); }
table { width: 100%; border-collapse: collapse; font-size: 12px; }
th, td { text-align: left; padding: 6px 10px; }
td { border-top: 1px solid rgba(255,255,255,0.04); }
th { position: sticky; top: 0; background: var(--term-bg); z-index: 1;
font-weight: 500; font-size: 11px; color: var(--subtle);
cursor: pointer; user-select: none;
border-bottom: 1px solid var(--outline); }
th:hover { color: var(--dim); }
th.sorted { color: var(--clay); }
th.sorted::after { content: ' ↓'; }
th.sorted.asc::after { content: ' ↑'; }
td.num, th.num { text-align: right; }
tbody tr:hover td { background: var(--hover); }
footer { margin-top: 28px; color: var(--subtle); font-size: 11px;
display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
code { color: var(--blue); }
a { color: var(--clay); }
::selection { background: var(--clay); color: var(--term-bg); }
@media (max-width: 760px) {
body { padding: 20px 12px 48px; }
.term-body { padding: 16px 16px 24px; }
#hero-total { font-size: 40px; }
.bar { grid-template-columns: 14ch 1fr 7ch; gap: 8px; }
.drill summary { grid-template-columns: 6ch 1fr; }
.drill .body { padding-left: 12px; }
.take { grid-template-columns: 7ch 1fr; gap: 12px; }
}
</style>
</head>
<body>
<div class="term">
<div class="titlebar">
<span class="dot"></span><span class="dot"></span><span class="dot"></span>
<span class="path" id="title-path">~/.claude — session-report</span>
</div>
<div class="term-body">
<div class="cmd"><span class="prompt">&gt;</span> claude usage <span id="cmd-flags"></span></div>
<div id="meta-line">loading…</div>
<div id="hero">
<div id="hero-total"></div>
<div id="hero-split"></div>
</div>
<!-- ====================================================================
FINDINGS — agent fills this. 35 one-line takeaways. Use .bad for
waste/anomalies, .good for healthy signals, .info for neutral.
==================================================================== -->
<section>
<div class="hr"></div>
<h2>findings</h2>
<div class="section-body" id="takeaways">
<!-- AGENT: anomalies -->
<div class="take bad"><div class="fig">26.7%</div><div class="txt"><b>nuxt-wiki "위키 작성" 프롬프트</b>가 전체 토큰의 26.7%를 단일 턴에서 소비했습니다 (1.97M / 7.39M) — API 호출 19회, 서브에이전트 3회 포함</div></div>
<div class="take bad"><div class="fig">24.4%</div><div class="txt"><b>gameservice-fe-agent "단계별 스킬 만들기" 프롬프트</b>가 전체 토큰의 24.4%를 단일 턴에서 소비했습니다 (1.80M / 7.39M) — API 호출 19회, 서브에이전트 없음</div></div>
<div class="take bad"><div class="fig">60.4%</div><div class="txt"><b>Explore 서브에이전트</b> 캐시 히트율이 60.4%에 불과합니다 — 목표치 85% 대비 크게 낮아, 서브에이전트 호출마다 콜드 캐시로 비용이 증가했습니다</div></div>
<div class="take good"><div class="fig">89.2%</div><div class="txt"><b>전체 캐시 히트율</b>이 89.2%로 양호합니다 — 목표치 85%를 상회하며, 100k 초과 캐시 브레이크가 0건입니다</div></div>
<div class="take info"><div class="fig">6.6%</div><div class="txt"><b>session-report:session-report 스킬</b>이 2회 실행으로 전체 토큰의 6.6% (486k / 7.39M)를 소비했습니다 — 대부분 컨텍스트 재로딩 캐시 읽기</div></div>
<!-- /AGENT -->
</div>
</section>
<!-- ====================================================================
Everything below renders automatically from #report-data.
==================================================================== -->
<section>
<div class="hr"></div>
<h2>summary</h2>
<div class="section-body" id="overall-grid"></div>
</section>
<section>
<div class="hr"></div>
<h2>tokens by project<span class="hint">share of total</span></h2>
<div class="section-body" id="project-bars"></div>
</section>
<section id="timeline-section">
<div class="hr"></div>
<h2>session timeline by day<span class="hint">click a day · ←/→ to navigate</span></h2>
<div class="section-body">
<div class="days" id="day-pills"></div>
<div class="gantt-hd">
<span class="day" id="g-day"></span>
<span class="stats" id="g-stats"></span>
</div>
<div class="gantt-axis"><span>00:00</span><span>06:00</span><span>12:00</span><span>18:00</span><span>24:00</span></div>
<div class="gantt" id="gantt"></div>
<div class="gantt-leg" id="gantt-leg"></div>
</div>
</section>
<section>
<div class="hr"></div>
<h2>most expensive prompts<span class="hint">click to expand context</span></h2>
<div class="section-body drill" id="top-prompts"></div>
</section>
<section>
<div class="hr"></div>
<h2>cache breaks<span class="hint">&gt;100k uncached · click for context</span></h2>
<div class="section-body drill" id="cache-breaks"></div>
</section>
<section>
<div class="hr"></div>
<h2>projects</h2>
<div class="section-body scroll"><table id="tbl-projects"></table></div>
</section>
<section>
<div class="hr"></div>
<h2>subagent types</h2>
<div class="section-body scroll"><table id="tbl-subagents"></table></div>
</section>
<section>
<div class="hr"></div>
<h2>skills &amp; slash commands</h2>
<div class="section-body scroll"><table id="tbl-skills"></table></div>
</section>
<section>
<div class="hr"></div>
<h2>recommendations</h2>
<div class="section-body">
<!-- AGENT: optimizations -->
<div class="callout">"nuxt 위키 작성" (26.7%)과 "단계별 스킬 만들기" (24.4%) 두 프롬프트가 주간 전체 토큰의 절반 이상을 소비했습니다. 대규모 위키/스킬 생성 작업은 범위를 나눠 여러 프롬프트로 분할하면 턴당 컨텍스트 누적을 줄일 수 있습니다.</div>
<div class="callout"><b>Explore 서브에이전트</b>의 캐시 히트율이 60.4%로 목표치(85%)를 크게 밑돕니다. 서브에이전트는 부모 세션의 캐시를 상속받지 않으므로, 범위를 좁게 유지하거나 세션 초반에 호출하는 것이 유리합니다. 간단한 검색은 서브에이전트 대신 메인 스레드에서 직접 처리하는 방법도 고려하세요.</div>
<div class="callout">nuxt-wiki 프로젝트가 단일 세션으로 82.7시간(실제 활성 0.9시간)을 유지했습니다. 세션이 길어지면 오래된 컨텍스트가 계속 캐시에 쌓입니다. 수 시간에 걸친 작업 세션에서는 <code>/compact</code>를 주기적으로 실행해 불필요한 컨텍스트를 정리하면 캐시 효율을 높일 수 있습니다.</div>
<!-- /AGENT -->
</div>
</section>
<footer>
<span id="foot-gen"></span>
<span id="foot-stats"></span>
</footer>
</div>
</div>
<!-- ========================================================================
DATA — agent replaces the {} below with the full --json output.
======================================================================== -->
<script id="report-data" type="application/json">{"root":"/Users/gil/.claude/projects","generated_at":"2026-05-16T01:40:29.518Z","overall":{"sessions":8,"api_calls":124,"input_tokens":{"uncached":13270,"cache_create":762618,"cache_read":6405597,"total":7181485,"pct_cached":89.2},"output_tokens":208707,"human_messages":38,"hours":{"wall_clock":83.6,"active":1.5},"cache_breaks_over_100k":0,"subagent":{"calls":4,"total_tokens":269182,"avg_tokens_per_call":67296},"skill_invocations":{"compact":1,"login":1,"model":1,"clear":1,"plugin":5,"session-report:session-report":2,"exit":1},"span":{"from":"2026-05-12T14:14:40.027Z","to":"2026-05-16T01:40:24.095Z"}},"cache_breaks":[],"by_project":{"-Users-gil--openclaw-workspace-nuxt-wiki":{"sessions":1,"api_calls":63,"input_tokens":{"uncached":13184,"cache_create":330480,"cache_read":3550576,"total":3894240,"pct_cached":91.2},"output_tokens":110900,"human_messages":13,"hours":{"wall_clock":82.7,"active":0.9},"cache_breaks_over_100k":0,"subagent":{"calls":4,"total_tokens":269182,"avg_tokens_per_call":67296},"skill_invocations":{"compact":1,"login":1,"model":1},"span":{"from":"2026-05-12T15:04:06.239Z","to":"2026-05-16T01:39:00.703Z"}},"-Users-gil-Downloads-gameservice-fe-agent-2":{"sessions":7,"api_calls":61,"input_tokens":{"uncached":86,"cache_create":432138,"cache_read":2855021,"total":3287245,"pct_cached":86.9},"output_tokens":97807,"human_messages":25,"hours":{"wall_clock":0.9,"active":0.6},"cache_breaks_over_100k":0,"subagent":{"calls":0,"total_tokens":0,"avg_tokens_per_call":0},"skill_invocations":{"clear":1,"plugin":5,"session-report:session-report":2,"exit":1},"span":{"from":"2026-05-12T14:14:40.027Z","to":"2026-05-16T01:40:24.095Z"}}},"by_subagent_type":{"Explore":{"sessions":1,"api_calls":9,"input_tokens":{"uncached":38,"cache_create":90391,"cache_read":138094,"total":228523,"pct_cached":60.4},"output_tokens":40659,"human_messages":0,"hours":{"wall_clock":0.1,"active":0.1},"cache_breaks_over_100k":0,"subagent":{"calls":4,"total_tokens":269182,"avg_tokens_per_call":67296},"skill_invocations":{},"span":{"from":"2026-05-12T15:08:45.560Z","to":"2026-05-12T15:27:53.262Z"}}},"by_skill":{"session-report:session-report":{"sessions":0,"api_calls":15,"input_tokens":{"uncached":17,"cache_create":55795,"cache_read":425175,"total":480987,"pct_cached":88.4},"output_tokens":4940,"human_messages":0,"hours":{"wall_clock":0,"active":0},"cache_breaks_over_100k":0,"subagent":{"calls":0,"total_tokens":0,"avg_tokens_per_call":0},"skill_invocations":{},"span":null}},"top_prompts":[{"ts":"2026-05-12T15:24:51.660Z","project":"-Users-gil--openclaw-workspace-nuxt-wiki","session":"a98eda68-dae8-4ee7-8727-e4dbbfccc411","text":"'/Users/gil/.openclaw/workspace/nuxt-wiki/reference' nuxt정보 위키도 작성해줘","api_calls":19,"subagent_calls":3,"total_tokens":1971429,"input":{"uncached":9422,"cache_create":144138,"cache_read":1755812},"output":62057,"context":[{"text":"웹상에서 클로코드 업무 자동화 관련 자료 찾아서 raw폴더에 넣어줘","ts":"2026-05-12T15:13:54.781Z","calls":7,"here":false},{"text":"정리해줘","ts":"2026-05-12T15:18:28.590Z","calls":8,"here":false},{"text":"'/Users/gil/.openclaw/workspace/nuxt-wiki/reference' nuxt정보 위키도 작성해줘","ts":"2026-05-12T15:24:51.660Z","calls":19,"here":true},{"text":"/compact","ts":"2026-05-16T01:30:11.999Z","calls":0,"here":false},{"text":"nuxt 관련 위키를 토대로 개발 지침 생성해줘","ts":"2026-05-16T01:30:43.054Z","calls":1,"here":false}]},{"ts":"2026-05-12T14:46:32.501Z","project":"-Users-gil-Downloads-gameservice-fe-agent-2","session":"183bbac6-1874-4c04-a33a-3009e4e124aa","text":"단계별 스킬에대한 스킬만들어주고 단계별 Skill 테이블에 명령 프롬프트 예시도 작성해줘","api_calls":19,"subagent_calls":0,"total_tokens":1803022,"input":{"uncached":29,"cache_create":279242,"cache_read":1482416},"output":41335,"context":[{"text":"b로 바꿔줘","ts":"2026-05-12T14:43:44.144Z","calls":9,"here":false},{"text":"단계별 Skill 설명 추가해줘","ts":"2026-05-12T14:45:42.573Z","calls":0,"here":false},{"text":"단계별 스킬에대한 스킬만들어주고 단계별 Skill 테이블에 명령 프롬프트 예시도 작성해줘","ts":"2026-05-12T14:46:32.501Z","calls":19,"here":true}]},{"ts":"2026-05-12T15:18:28.590Z","project":"-Users-gil--openclaw-workspace-nuxt-wiki","session":"a98eda68-dae8-4ee7-8727-e4dbbfccc411","text":"정리해줘","api_calls":8,"subagent_calls":0,"total_tokens":737235,"input":{"uncached":1437,"cache_create":25183,"cache_read":698784},"output":11831,"context":[{"text":"현재 raw바깥에 폴더들은 raw로 넣으면돼?","ts":"2026-05-12T15:08:28.986Z","calls":14,"here":false},{"text":"웹상에서 클로코드 업무 자동화 관련 자료 찾아서 raw폴더에 넣어줘","ts":"2026-05-12T15:13:54.781Z","calls":7,"here":false},{"text":"정리해줘","ts":"2026-05-12T15:18:28.590Z","calls":8,"here":true},{"text":"'/Users/gil/.openclaw/workspace/nuxt-wiki/reference' nuxt정보 위키도 작성해줘","ts":"2026-05-12T15:24:51.660Z","calls":19,"here":false},{"text":"/compact","ts":"2026-05-16T01:30:11.999Z","calls":0,"here":false}]},{"ts":"2026-05-12T15:08:28.986Z","project":"-Users-gil--openclaw-workspace-nuxt-wiki","session":"a98eda68-dae8-4ee7-8727-e4dbbfccc411","text":"현재 raw바깥에 폴더들은 raw로 넣으면돼?","api_calls":14,"subagent_calls":1,"total_tokens":492766,"input":{"uncached":29,"cache_create":77960,"cache_read":404225},"output":10552,"context":[{"text":"나만의 Nuxt 개발 지식/노하우 위키를 만들어줘. 아래 Karpathy의 LLM Wiki 아이디어를 참고해서 구현해: https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f 이 프로젝트는 내가 Nuxt 개발을 하면서 쌓이는 지식을 축적하는 나만의 나무위키야. 자료를 넣으면 네가 읽고 정리해서 위키로 만들어주고, 나는 필요할 때 질문하면 돼. 나는 Nuxt(V…","ts":"2026-05-12T15:04:06.239Z","calls":9,"here":false},{"text":"현재 raw바깥에 폴더들은 raw로 넣으면돼?","ts":"2026-05-12T15:08:28.986Z","calls":14,"here":true},{"text":"웹상에서 클로코드 업무 자동화 관련 자료 찾아서 raw폴더에 넣어줘","ts":"2026-05-12T15:13:54.781Z","calls":7,"here":false},{"text":"정리해줘","ts":"2026-05-12T15:18:28.590Z","calls":8,"here":false}]},{"ts":"2026-05-12T14:43:44.144Z","project":"-Users-gil-Downloads-gameservice-fe-agent-2","session":"183bbac6-1874-4c04-a33a-3009e4e124aa","text":"b로 바꿔줘","api_calls":9,"subagent_calls":0,"total_tokens":470269,"input":{"uncached":11,"cache_create":7123,"cache_read":458633},"output":4502,"context":[{"text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/WDG00.04.02.06.07.01 AI 활용 - CBO-플랫폼서비스개발담당.md' 단계별 스킬을 더 추가해줘 아래 말한 단계 외에도 필요한 단계 추천 - 단위 테스트 - 코드 리뷰","ts":"2026-05-12T14:38:49.822Z","calls":11,"here":false},{"text":"개발 nuxt-component nuxt-docs nuxt-api-state unit-test-generator docs-generator 개발단계 prefix 추천해줘","ts":"2026-05-12T14:42:53.731Z","calls":1,"here":false},{"text":"b로 바꿔줘","ts":"2026-05-12T14:43:44.144Z","calls":9,"here":true},{"text":"단계별 Skill 설명 추가해줘","ts":"2026-05-12T14:45:42.573Z","calls":0,"here":false},{"text":"단계별 스킬에대한 스킬만들어주고 단계별 Skill 테이블에 명령 프롬프트 예시도 작성해줘","ts":"2026-05-12T14:46:32.501Z","calls":19,"here":false}]},{"ts":"2026-05-12T14:47:49.031Z","project":"-Users-gil-Downloads-gameservice-fe-agent-2","session":"b36d34e5-069a-45f0-8e61-0d1c4e768768","text":"/session-report:session-report","api_calls":14,"subagent_calls":0,"total_tokens":461307,"input":{"uncached":15,"cache_create":42235,"cache_read":414344},"output":4713,"context":[{"text":"/session-report:session-report","ts":"2026-05-12T14:47:49.031Z","calls":14,"here":true}]},{"ts":"2026-05-12T15:13:54.781Z","project":"-Users-gil--openclaw-workspace-nuxt-wiki","session":"a98eda68-dae8-4ee7-8727-e4dbbfccc411","text":"웹상에서 클로코드 업무 자동화 관련 자료 찾아서 raw폴더에 넣어줘","api_calls":7,"subagent_calls":0,"total_tokens":409813,"input":{"uncached":2125,"cache_create":30582,"cache_read":362830},"output":14276,"context":[{"text":"나만의 Nuxt 개발 지식/노하우 위키를 만들어줘. 아래 Karpathy의 LLM Wiki 아이디어를 참고해서 구현해: https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f 이 프로젝트는 내가 Nuxt 개발을 하면서 쌓이는 지식을 축적하는 나만의 나무위키야. 자료를 넣으면 네가 읽고 정리해서 위키로 만들어주고, 나는 필요할 때 질문하면 돼. 나는 Nuxt(V…","ts":"2026-05-12T15:04:06.239Z","calls":9,"here":false},{"text":"현재 raw바깥에 폴더들은 raw로 넣으면돼?","ts":"2026-05-12T15:08:28.986Z","calls":14,"here":false},{"text":"웹상에서 클로코드 업무 자동화 관련 자료 찾아서 raw폴더에 넣어줘","ts":"2026-05-12T15:13:54.781Z","calls":7,"here":true},{"text":"정리해줘","ts":"2026-05-12T15:18:28.590Z","calls":8,"here":false},{"text":"'/Users/gil/.openclaw/workspace/nuxt-wiki/reference' nuxt정보 위키도 작성해줘","ts":"2026-05-12T15:24:51.660Z","calls":19,"here":false}]},{"ts":"2026-05-12T14:38:49.822Z","project":"-Users-gil-Downloads-gameservice-fe-agent-2","session":"183bbac6-1874-4c04-a33a-3009e4e124aa","text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/WDG00.04.02.06.07.01 AI 활용 - CBO-플랫폼서비스개발담당.md' 단계별 스킬을 더 추가해줘 아래 말한 단계 외에도 필요한 단계 추천 - 단위 테스트 - 코드 리뷰","api_calls":11,"subagent_calls":0,"total_tokens":407317,"input":{"uncached":13,"cache_create":35109,"cache_read":359684},"output":12511,"context":[{"text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/WDG00.04.02.06.07.01 AI 활용 - CBO-플랫폼서비스개발담당.md' 단계별 스킬을 더 추가해줘 아래 말한 단계 외에도 필요한 단계 추천 - 단위 테스트 - 코드 리뷰","ts":"2026-05-12T14:38:49.822Z","calls":11,"here":true},{"text":"개발 nuxt-component nuxt-docs nuxt-api-state unit-test-generator docs-generator 개발단계 prefix 추천해줘","ts":"2026-05-12T14:42:53.731Z","calls":1,"here":false},{"text":"b로 바꿔줘","ts":"2026-05-12T14:43:44.144Z","calls":9,"here":false}]},{"ts":"2026-05-12T15:04:06.239Z","project":"-Users-gil--openclaw-workspace-nuxt-wiki","session":"a98eda68-dae8-4ee7-8727-e4dbbfccc411","text":"나만의 Nuxt 개발 지식/노하우 위키를 만들어줘. 아래 Karpathy의 LLM Wiki 아이디어를 참고해서 구현해: https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f 이 프로젝트는 내가 Nuxt 개발을 하면서 쌓이는 지식을 축적하는 나만의 나무위키야. 자료를 넣으면 네가 읽고 정리해서 위키로 만들어주고, 나는 필요할 때 질문하면 돼. 나는 Nuxt(V…","api_calls":9,"subagent_calls":0,"total_tokens":201301,"input":{"uncached":164,"cache_create":13556,"cache_read":182091},"output":5490,"context":[{"text":"나만의 Nuxt 개발 지식/노하우 위키를 만들어줘. 아래 Karpathy의 LLM Wiki 아이디어를 참고해서 구현해: https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f 이 프로젝트는 내가 Nuxt 개발을 하면서 쌓이는 지식을 축적하는 나만의 나무위키야. 자료를 넣으면 네가 읽고 정리해서 위키로 만들어주고, 나는 필요할 때 질문하면 돼. 나는 Nuxt(V…","ts":"2026-05-12T15:04:06.239Z","calls":9,"here":true},{"text":"현재 raw바깥에 폴더들은 raw로 넣으면돼?","ts":"2026-05-12T15:08:28.986Z","calls":14,"here":false},{"text":"웹상에서 클로코드 업무 자동화 관련 자료 찾아서 raw폴더에 넣어줘","ts":"2026-05-12T15:13:54.781Z","calls":7,"here":false}]},{"ts":"2026-05-16T01:31:28.037Z","project":"-Users-gil--openclaw-workspace-nuxt-wiki","session":"a98eda68-dae8-4ee7-8727-e4dbbfccc411","text":"nuxt 관련 위키를 토대로 개발 지침 생성해줘","api_calls":5,"subagent_calls":0,"total_tokens":192596,"input":{"uncached":7,"cache_create":39061,"cache_read":146834},"output":6694,"context":[{"text":"/model","ts":"2026-05-16T01:31:25.368Z","calls":0,"here":false},{"text":"Set model to u001b[1mSonnet 4.6 (default)u001b[22m","ts":"2026-05-16T01:31:25.368Z","calls":0,"here":false},{"text":"nuxt 관련 위키를 토대로 개발 지침 생성해줘","ts":"2026-05-16T01:31:28.037Z","calls":5,"here":true}]},{"ts":"2026-05-12T14:14:40.027Z","project":"-Users-gil-Downloads-gameservice-fe-agent-2","session":"f378c1ec-3e63-4b5b-bd6f-bd4b617e8ae5","text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/fe-ai-workflow.html' fe-ai-workflow.html fe-ai-reference-flow 2.html fe-ai-rules.html WDG00.04.02.06.07.01 AI 활용 - CBO-플랫폼서비스개발담당.md fe-ai-reference-flow.html 단계별 스킬을 …","api_calls":2,"subagent_calls":0,"total_tokens":88813,"input":{"uncached":4,"cache_create":20537,"cache_read":36133},"output":32139,"context":[{"text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/fe-ai-workflow.html' fe-ai-workflow.html fe-ai-reference-flow 2.html fe-ai-rules.html WDG00.04.02.06.07.01 AI 활용 - CBO-플랫폼서비스개발담당.md fe-ai-reference-flow.html 단계별 스킬을 …","ts":"2026-05-12T14:14:40.027Z","calls":2,"here":true}]},{"ts":"2026-05-12T14:42:53.731Z","project":"-Users-gil-Downloads-gameservice-fe-agent-2","session":"183bbac6-1874-4c04-a33a-3009e4e124aa","text":"개발 nuxt-component nuxt-docs nuxt-api-state unit-test-generator docs-generator 개발단계 prefix 추천해줘","api_calls":1,"subagent_calls":0,"total_tokens":48421,"input":{"uncached":3,"cache_create":520,"cache_read":45947},"output":1951,"context":[{"text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/WDG00.04.02.06.07.01 AI 활용 - CBO-플랫폼서비스개발담당.md' 단계별 스킬을 더 추가해줘 아래 말한 단계 외에도 필요한 단계 추천 - 단위 테스트 - 코드 리뷰","ts":"2026-05-12T14:38:49.822Z","calls":11,"here":false},{"text":"개발 nuxt-component nuxt-docs nuxt-api-state unit-test-generator docs-generator 개발단계 prefix 추천해줘","ts":"2026-05-12T14:42:53.731Z","calls":1,"here":true},{"text":"b로 바꿔줘","ts":"2026-05-12T14:43:44.144Z","calls":9,"here":false},{"text":"단계별 Skill 설명 추가해줘","ts":"2026-05-12T14:45:42.573Z","calls":0,"here":false}]},{"ts":"2026-05-12T14:28:26.067Z","project":"-Users-gil-Downloads-gameservice-fe-agent-2","session":"ccbfe545-12d5-4c3e-9feb-b3467feb8620","text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/WDG00.04.02.06.07.01 AI 활용 - CBO-플랫폼서비스개발담당.md' 단계별 스킬을 더 추가해줘 아래 말한 단계 외에도 필요한 단계 추천 - 단위 테스트 - 코드 리뷰","api_calls":1,"subagent_calls":0,"total_tokens":33131,"input":{"uncached":3,"cache_create":8926,"cache_read":23946},"output":256,"context":[{"text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/fe-ai-workflow.html' 단계별 스킬을 더 추가해줘 아래 말한 단계 외에도 필요한 단계 추천 - 단위 테스트 - 코드 리뷰 -","ts":"2026-05-12T14:28:04.868Z","calls":1,"here":false},{"text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/WDG00.04.02.06.07.01 AI 활용 - CBO-플랫폼서비스개발담당.md' 단계별 스킬을 더 추가해줘 아래 말한 단계 외에도 필요한 단계 추천 - 단위 테스트 - 코드 리뷰","ts":"2026-05-12T14:28:26.067Z","calls":1,"here":true},{"text":"--continue","ts":"2026-05-12T14:36:13.298Z","calls":0,"here":false}]},{"ts":"2026-05-16T01:40:17.904Z","project":"-Users-gil-Downloads-gameservice-fe-agent-2","session":"8c56053b-8d23-4314-926d-5895c6c27fd9","text":"/session-report:session-report","api_calls":1,"subagent_calls":0,"total_tokens":24620,"input":{"uncached":2,"cache_create":13560,"cache_read":10831},"output":227,"context":[{"text":"/plugin","ts":"2026-05-16T01:40:11.638Z","calls":0,"here":false},{"text":"Failed to reconnect to plugin:serena:serena.","ts":"2026-05-16T01:40:11.638Z","calls":0,"here":false},{"text":"/session-report:session-report","ts":"2026-05-16T01:40:17.904Z","calls":1,"here":true}]},{"ts":"2026-05-12T14:28:04.868Z","project":"-Users-gil-Downloads-gameservice-fe-agent-2","session":"ccbfe545-12d5-4c3e-9feb-b3467feb8620","text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/fe-ai-workflow.html' 단계별 스킬을 더 추가해줘 아래 말한 단계 외에도 필요한 단계 추천 - 단위 테스트 - 코드 리뷰 -","api_calls":1,"subagent_calls":0,"total_tokens":24122,"input":{"uncached":3,"cache_create":11782,"cache_read":12164},"output":173,"context":[{"text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/fe-ai-workflow.html' 단계별 스킬을 더 추가해줘 아래 말한 단계 외에도 필요한 단계 추천 - 단위 테스트 - 코드 리뷰 -","ts":"2026-05-12T14:28:04.868Z","calls":1,"here":true},{"text":"'/Users/gil/Downloads/gameservice-fe-agent 2/docs/WDG00.04.02.06.07.01 AI 활용 - CBO-플랫폼서비스개발담당.md' 단계별 스킬을 더 추가해줘 아래 말한 단계 외에도 필요한 단계 추천 - 단위 테스트 - 코드 리뷰","ts":"2026-05-12T14:28:26.067Z","calls":1,"here":false},{"text":"--continue","ts":"2026-05-12T14:36:13.298Z","calls":0,"here":false}]},{"ts":"2026-05-12T15:26:41.699Z","project":"-Users-gil-Downloads-gameservice-fe-agent-2","session":"724530e1-31af-40a0-b272-2535f9935ce4","text":"http://gilnas.synology.me:3000/gil/game-fe-agent.git 여기로 깃 연결","api_calls":2,"subagent_calls":0,"total_tokens":24030,"input":{"uncached":3,"cache_create":13104,"cache_read":10923},"output":0,"context":[{"text":"/clear","ts":"2026-05-12T15:26:32.796Z","calls":0,"here":false},{"text":"http://gilnas.synology.me:3000/gil/game-fe-agent.git 여기로 깃 연결","ts":"2026-05-12T15:26:41.699Z","calls":2,"here":true}]},{"ts":"2026-05-16T01:30:43.054Z","project":"-Users-gil--openclaw-workspace-nuxt-wiki","session":"a98eda68-dae8-4ee7-8727-e4dbbfccc411","text":"nuxt 관련 위키를 토대로 개발 지침 생성해줘","api_calls":1,"subagent_calls":0,"total_tokens":0,"input":{"uncached":0,"cache_create":0,"cache_read":0},"output":0,"context":[{"text":"'/Users/gil/.openclaw/workspace/nuxt-wiki/reference' nuxt정보 위키도 작성해줘","ts":"2026-05-12T15:24:51.660Z","calls":19,"here":false},{"text":"/compact","ts":"2026-05-16T01:30:11.999Z","calls":0,"here":false},{"text":"nuxt 관련 위키를 토대로 개발 지침 생성해줘","ts":"2026-05-16T01:30:43.054Z","calls":1,"here":true},{"text":"/login","ts":"2026-05-16T01:31:07.978Z","calls":0,"here":false},{"text":"Login successful","ts":"2026-05-16T01:31:07.978Z","calls":0,"here":false}]}],"by_day":[{"date":"2026-05-12","dow":"Tue","tokens":3336402,"sessions":[{"id":"f378c1ec-3e63-4b5b-bd6f-bd4b617e8ae5","project":"-Users-gil-Downloads-gameservice-fe-agent-2","tokens":88813,"start_min":1395,"end_min":1408},{"id":"ccbfe545-12d5-4c3e-9feb-b3467feb8620","project":"-Users-gil-Downloads-gameservice-fe-agent-2","tokens":57253,"start_min":1408,"end_min":1416},{"id":"183bbac6-1874-4c04-a33a-3009e4e124aa","project":"-Users-gil-Downloads-gameservice-fe-agent-2","tokens":2729029,"start_min":1419,"end_min":1440},{"id":"b36d34e5-069a-45f0-8e61-0d1c4e768768","project":"-Users-gil-Downloads-gameservice-fe-agent-2","tokens":461307,"start_min":1428,"end_min":1434}],"peak":2,"peak_at_min":1400},{"date":"2026-05-13","dow":"Wed","tokens":4029170,"sessions":[{"id":"a98eda68-dae8-4ee7-8727-e4dbbfccc411","project":"-Users-gil--openclaw-workspace-nuxt-wiki","tokens":4005140,"start_min":4,"end_min":4959},{"id":"724530e1-31af-40a0-b272-2535f9935ce4","project":"-Users-gil-Downloads-gameservice-fe-agent-2","tokens":24030,"start_min":27,"end_min":30}],"peak":2,"peak_at_min":20},{"date":"2026-05-16","dow":"Sat","tokens":24620,"sessions":[{"id":"8c56053b-8d23-4314-926d-5895c6c27fd9","project":"-Users-gil-Downloads-gameservice-fe-agent-2","tokens":24620,"start_min":640,"end_min":640}],"peak":0,"peak_at_min":0}]}</script>
<script>
(function() {
const DATA = JSON.parse(document.getElementById('report-data').textContent || '{}');
const $ = id => document.getElementById(id);
const fmt = n => n>=1e9 ? (n/1e9).toFixed(2)+'B' : n>=1e6 ? (n/1e6).toFixed(2)+'M'
: n>=1e3 ? (n/1e3).toFixed(1)+'k' : String(n);
const pct = (a,b) => b>0 ? ((100*a/b).toFixed(1)+'%') : '—';
const esc = s => String(s).replace(/[&<>"]/g, c =>
({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
const short = p => String(p||'').replace(/^-Users-[^-]+-/,'').replace(/^code-/,'');
const MON = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
const niceDate = iso => { const d = new Date(iso); return isNaN(d) ? '' :
`${MON[d.getMonth()]} ${d.getDate()} · ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`; };
if (!DATA.overall) { $('meta-line').textContent = 'no data — embed JSON in #report-data.'; return; }
// header + hero
const o = DATA.overall, it = o.input_tokens;
const GRAND = it.total + o.output_tokens;
const share = n => GRAND>0 ? (100*n/GRAND).toFixed(1)+'%' : '—';
const span = o.span;
$('cmd-flags').innerHTML = DATA.since
? `<span class="flag">--since</span> ${esc(DATA.since)}` : '';
$('meta-line').textContent =
(span ? `${span.from.slice(0,10)}${span.to.slice(0,10)}` : '') +
` · ${DATA.root || ''}`;
$('foot-gen').textContent = `generated ${DATA.generated_at?.slice(0,16).replace('T',' ') || ''}`;
$('foot-stats').textContent =
`${o.sessions} sessions · ${o.api_calls} api calls · ${o.human_messages} prompts`;
const num = fmt(GRAND), m = num.match(/^([\d.]+)([A-Za-z]*)$/);
$('hero-total').innerHTML =
`${m?m[1]:num}<span class="unit">${m?m[2]:''}</span><span class="label">tokens</span>`;
const cacheCls = it.pct_cached>=85 ? 'ok' : 'warn';
$('hero-split').innerHTML =
`<b>${fmt(it.total)}</b> input <span class="${cacheCls}">(${it.pct_cached}% cache-read)</span> · `+
`<b>${fmt(o.output_tokens)}</b> output · all figures below are % of this total`;
// overall stat grid
$('overall-grid').innerHTML = [
['sessions', o.sessions],
['api calls', o.api_calls],
['human msgs', o.human_messages],
['active hours', o.hours.active, `${o.hours.wall_clock} wall-clock`],
['cache breaks', o.cache_breaks_over_100k, '>100k uncached'],
['subagent calls', o.subagent.calls, `avg ${fmt(o.subagent.avg_tokens_per_call)}`],
].map(([l,v,d]) =>
`<div class="stat"><div class="label">${l}</div>`+
`<div class="val">${typeof v==='number'&&v>=1e4?fmt(v):v}</div>`+
(d?`<div class="detail">${d}</div>`:'')+`</div>`).join('');
// session timeline by day
(function() {
const days = (DATA.by_day||[]).slice(-14);
if (!days.length) { $('timeline-section').style.display='none'; return; }
const PCOL = ['rgb(177,185,249)','rgb(78,186,101)','#D97757','rgb(255,193,7)',
'rgb(255,107,128)','#9b8cff','#6ec1d6','#c792ea'];
const dayTotal = days.reduce((a,d)=>a+d.tokens,0) || 1;
const tokMax = Math.max(...days.map(d=>d.tokens));
const projects = [...new Set(days.flatMap(d=>d.sessions.map(s=>s.project)))];
const colorOf = p => PCOL[projects.indexOf(p)%PCOL.length];
const hhmm = m => (m>=1440?`+${Math.floor(m/1440)}d `:'') +
`${String(Math.floor(m/60)%24).padStart(2,'0')}:${String(m%60).padStart(2,'0')}`;
const md = iso => { const [,mo,da]=iso.split('-'); return `${MON[+mo-1]} ${+da}`; };
let sel = days.findIndex(d=>d.tokens===tokMax);
function pills() {
$('day-pills').innerHTML = days.map((d,i)=>
`<button class="dpill${d.tokens===tokMax?' heaviest':''}${i===sel?' sel':''}" data-i="${i}">`+
`<span class="dow">${esc(d.dow)}</span>`+
`<span class="date">${esc(md(d.date))}</span>`+
`<span class="pct">${(100*d.tokens/dayTotal).toFixed(1)}%</span>`+
`<span class="ns">${d.sessions.length} sess</span></button>`
).join('');
$('day-pills').querySelectorAll('.dpill').forEach(el=>
el.onclick=()=>{sel=+el.dataset.i;pills();gantt();});
}
function gantt() {
const d = days[sel], DAY = 1440;
$('g-day').textContent = `${d.dow} ${md(d.date)}`;
$('g-stats').innerHTML = `${d.sessions.length} sessions · ${fmt(d.tokens)} tokens`+
` · peak <b>${d.peak}</b> concurrent at <b>${hhmm(d.peak_at_min)}</b>`;
const lanes = [];
for (const s of d.sessions) {
let placed = false;
for (const L of lanes) if (L[L.length-1].end_min <= s.start_min) { L.push(s); placed=true; break; }
if (!placed) lanes.push([s]);
}
let h = '';
for (let t=0;t<=24;t+=6) h += `<div class="gantt-rule" style="left:${100*t/24}%"></div>`;
h += lanes.map(L=>`<div class="lane">${L.map(s=>{
const end = Math.min(s.end_min, DAY);
const w = Math.max(0.15, 100*(end-s.start_min)/DAY);
const tip = `folder: ${short(s.project)}\n`+
`${hhmm(s.start_min)}${hhmm(s.end_min)} · ${fmt(s.tokens)} tokens\n`+
`session ${s.id}`;
return `<span class="seg" style="left:${100*s.start_min/DAY}%;width:${w}%;`+
`background:${colorOf(s.project)}" title="${esc(tip)}"></span>`;
}).join('')}</div>`).join('');
$('gantt').innerHTML = h || '<div class="callout">no sessions</div>';
}
document.addEventListener('keydown',e=>{
if (e.key==='ArrowRight'&&sel<days.length-1){sel++;pills();gantt();e.preventDefault();}
if (e.key==='ArrowLeft'&&sel>0){sel--;pills();gantt();e.preventDefault();}
});
$('gantt-leg').innerHTML = projects.slice(0,12).map(p=>
`<span><span class="sw" style="background:${colorOf(p)}"></span>${esc(short(p))}</span>`).join('');
pills(); gantt();
})();
// block-char project bars
(function() {
const W = 48;
const rows = Object.entries(DATA.by_project||{})
.map(([k,v]) => [k, v.input_tokens.total + v.output_tokens])
.sort((a,b)=>b[1]-a[1]).slice(0,15);
const max = rows[0]?.[1] || 1;
$('project-bars').innerHTML = rows.map(([k,v]) => {
const n = Math.max(1, Math.round(W*v/max));
return `<div class="bar"><span class="name" title="${esc(k)}${fmt(v)}">${esc(short(k))}</span>`+
`<span class="blocks">${'█'.repeat(n)}<span class="empty">${'░'.repeat(W-n)}</span></span>`+
`<span class="pct">${share(v)}</span></div>`;
}).join('');
})();
// ±2 user-message transcript context
function renderContext(ctx, mark) {
if (!ctx || !ctx.length) return '<div class="ctx-gap">no transcript context available</div>';
let h = '<div class="ctx">';
ctx.forEach((m, i) => {
const who = m.here ? '&gt; user' : ' user';
h += `<div class="ctx-msg${m.here?' here':''}">`+
`<span class="who">${who}</span> ${esc(m.text||'(non-text)')}`+
`<span class="ts">${niceDate(m.ts)}</span></div>`;
if (m.here && mark) h += mark;
if (i < ctx.length-1 || m.here)
h += `<div class="ctx-gap">${m.calls} api call${m.calls===1?'':'s'}</div>`;
});
return h + '</div>';
}
// expandable drill-down list with "show N more" toggle
function drillList(hostId, items, rowFn, empty) {
const SHOW = 5;
const host = $(hostId);
if (!items.length) { host.innerHTML = `<div class="callout">${empty}</div>`; return; }
const head = items.slice(0,SHOW).map(rowFn).join('');
const rest = items.slice(SHOW).map(rowFn).join('');
host.innerHTML = head + (rest
? `<div hidden>${rest}</div><button class="more-btn">show ${items.length-SHOW} more</button>`
: '');
const btn = host.querySelector('.more-btn');
if (btn) btn.onclick = () => {
const r = btn.previousElementSibling; r.hidden = !r.hidden;
btn.textContent = r.hidden ? `show ${items.length-SHOW} more` : 'show less';
};
}
drillList('top-prompts', (DATA.top_prompts||[]).slice(0,100), p => {
const inTot = p.input.uncached+p.input.cache_create+p.input.cache_read;
return `<details><summary>`+
`<span class="amt">${share(p.total_tokens)}</span>`+
`<span class="desc">${esc(p.text)}</span>`+
`<span class="meta">${niceDate(p.ts)} · ${esc(short(p.project))} · ${p.api_calls} calls`+
(p.subagent_calls?` · ${p.subagent_calls} subagents`:'')+
` · ${pct(p.input.cache_read,inTot)} cached</span>`+
`</summary><div class="body">`+
renderContext(p.context)+
`<div>session <code>${esc(p.session)}</code></div>`+
`<div>in: uncached ${fmt(p.input.uncached)} · cache-create ${fmt(p.input.cache_create)} · `+
`cache-read ${fmt(p.input.cache_read)} · out ${fmt(p.output)}</div>`+
`</div></details>`;
}, 'No prompts in range.');
drillList('cache-breaks', (DATA.cache_breaks||[]).slice(0,100), b =>
`<details><summary>`+
`<span class="amt">${fmt(b.uncached)}</span>`+
`<span class="desc">${esc(short(b.project))} · `+
`${b.kind==='subagent'?esc(b.agentType||'subagent'):'main'}</span>`+
`<span class="meta">${niceDate(b.ts)} · ${pct(b.uncached,b.total)} of ${fmt(b.total)} uncached</span>`+
`</summary><div class="body">`+
renderContext(b.context,
`<div class="ctx-break"><b>${fmt(b.uncached)}</b> uncached `+
`(${pct(b.uncached,b.total)} of ${fmt(b.total)}) — cache break here</div>`)+
`<div>session <code>${esc(b.session)}</code></div>`+
`</div></details>`,
'No cache breaks over threshold.');
// sortable table
function table(el, cols, rows) {
let sortIdx = cols.findIndex(c=>c.sort), asc = false;
function render() {
const sorted = rows.slice().sort((a,b)=>{
const va=a[sortIdx], vb=b[sortIdx];
return (asc?1:-1)*(typeof va==='number' ? va-vb : String(va).localeCompare(String(vb)));
});
el.innerHTML = `<thead><tr>${cols.map((c,i)=>
`<th class="${c.num?'num':''} ${i===sortIdx?'sorted'+(asc?' asc':''):''}" data-i="${i}">${c.h}</th>`
).join('')}</tr></thead><tbody>${sorted.map(r=>
`<tr>${r.map((v,i)=>`<td class="${cols[i].num?'num':''}">${
cols[i].fmt?cols[i].fmt(v):esc(v)}</td>`).join('')}</tr>`
).join('')}</tbody>`;
el.querySelectorAll('th').forEach(th=>th.onclick=()=>{
const i=+th.dataset.i; if(i===sortIdx)asc=!asc; else{sortIdx=i;asc=false;} render();
});
}
render();
}
function statRows(obj) {
return Object.entries(obj||{}).map(([k,v])=>[
short(k), GRAND>0 ? 100*(v.input_tokens.total+v.output_tokens)/GRAND : 0,
v.sessions, v.api_calls, v.human_messages,
v.input_tokens.total, v.input_tokens.pct_cached, v.output_tokens,
v.hours.active, v.cache_breaks_over_100k,
v.subagent.calls, v.subagent.avg_tokens_per_call,
]);
}
const statCols = [
{h:'name'},{h:'% total',num:1,sort:1,fmt:v=>v.toFixed(1)+'%'},
{h:'sess',num:1},{h:'calls',num:1},{h:'msgs',num:1},
{h:'input',num:1,fmt:fmt},{h:'%cached',num:1,fmt:v=>v+'%'},
{h:'output',num:1,fmt:fmt},{h:'active h',num:1},{h:'breaks',num:1},
{h:'subagents',num:1},{h:'avg sub tok',num:1,fmt:fmt},
];
table($('tbl-projects'), statCols, statRows(DATA.by_project));
table($('tbl-subagents'), statCols, statRows(DATA.by_subagent_type));
table($('tbl-skills'), statCols, statRows(DATA.by_skill));
})();
</script>
</body>
</html>