claude-spotter 1.5.2 → 1.5.3
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/CHANGELOG.md +8 -0
- package/README.md +3 -2
- package/package.json +1 -1
- package/src/dashboard/render.mjs +13 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.3 — 2026-08-04
|
|
4
|
+
|
|
5
|
+
- **dashboardの難解な集計略号を廃止。** 概要cardとproject/tool別内訳の
|
|
6
|
+
`S/P/I/C/A/M`を、対象ターン、ツール提案あり、提案ツール数、利用判定済み、
|
|
7
|
+
実際に使用、判定不能の日本語表示へ置き換える。集計schemaとCLIの内部キーは変更しない。
|
|
8
|
+
- **率の意味を式ではなく言葉で表示。** 提案率は「ツール提案あり ÷ 対象ターン」、
|
|
9
|
+
採用率は「実際に使用 ÷ 利用判定済み」と併記する。
|
|
10
|
+
|
|
3
11
|
## 1.5.2 — 2026-08-04
|
|
4
12
|
|
|
5
13
|
- **Mac LaunchAgentでNodeを解決。** `spotter`の絶対pathだけでなく、env shebangが使う
|
package/README.md
CHANGED
|
@@ -256,8 +256,9 @@ spotter uninstall # remove hooks from this project (leaves ~/.spotter int
|
|
|
256
256
|
|
|
257
257
|
The dashboard is local-first. Every terminal reads its own `~/.spotter/evaluation.db`; the hub
|
|
258
258
|
keeps only a static device-to-upstream map and does not copy evaluation data into a cloud database.
|
|
259
|
-
The device view shows
|
|
260
|
-
non-adopted cases, and the two
|
|
259
|
+
The device view shows Japanese labels for every evaluation metric, proposal and adoption rates
|
|
260
|
+
with their numerator and denominator, project/tool breakdowns, non-adopted cases, and the two
|
|
261
|
+
separately captured context sources. The hub checks health only
|
|
261
262
|
when the device list is requested, so an offline terminal is isolated without a background monitor
|
|
262
263
|
or retry queue.
|
|
263
264
|
|
package/package.json
CHANGED
package/src/dashboard/render.mjs
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
// opens EvaluationStore or asks Throughline for context.
|
|
4
4
|
|
|
5
5
|
const METRIC_KEYS = ['S', 'P', 'I', 'C', 'A', 'M'];
|
|
6
|
+
const METRIC_LABELS = {
|
|
7
|
+
S: '対象ターン',
|
|
8
|
+
P: 'ツール提案あり',
|
|
9
|
+
I: '提案ツール数',
|
|
10
|
+
C: '利用判定済み',
|
|
11
|
+
A: '実際に使用',
|
|
12
|
+
M: '判定不能',
|
|
13
|
+
};
|
|
6
14
|
|
|
7
15
|
/**
|
|
8
16
|
* Render a complete dashboard document.
|
|
@@ -96,7 +104,7 @@ function renderDeviceContent({ overview, cases, caseDetail, filters, action }) {
|
|
|
96
104
|
return `${renderFilters(filters, action)}
|
|
97
105
|
<section aria-labelledby="overview-heading">
|
|
98
106
|
<div class="section-heading"><h2 id="overview-heading">概要</h2>${renderRates(totals)}</div>
|
|
99
|
-
<div class="metrics" aria-label="評価メトリクス">${METRIC_KEYS.map((key) => `<article class="metric"><span>${key}</span><strong>${escapeHtml(metric(totals, key))}</strong></article>`).join('')}</div>
|
|
107
|
+
<div class="metrics" aria-label="評価メトリクス">${METRIC_KEYS.map((key) => `<article class="metric"><span>${METRIC_LABELS[key]}</span><strong>${escapeHtml(metric(totals, key))}</strong></article>`).join('')}</div>
|
|
100
108
|
</section>
|
|
101
109
|
${renderBreakdown('project-breakdown', 'project別内訳', projects)}
|
|
102
110
|
${renderBreakdown('tool-breakdown', 'tool別内訳', tools)}
|
|
@@ -119,13 +127,13 @@ function renderFilters(filters, action) {
|
|
|
119
127
|
function renderRates(summary) {
|
|
120
128
|
const proposal = rate(metricNumber(summary, 'P'), metricNumber(summary, 'S'));
|
|
121
129
|
const adoption = rate(metricNumber(summary, 'A'), metricNumber(summary, 'C'));
|
|
122
|
-
return `<dl class="rates"><div><dt
|
|
130
|
+
return `<dl class="rates"><div><dt>提案率<small>ツール提案あり ÷ 対象ターン</small></dt><dd>${escapeHtml(proposal)}</dd></div><div><dt>採用率<small>実際に使用 ÷ 利用判定済み</small></dt><dd>${escapeHtml(adoption)}</dd></div></dl>`;
|
|
123
131
|
}
|
|
124
132
|
|
|
125
133
|
function renderBreakdown(id, title, rows) {
|
|
126
134
|
return `<section aria-labelledby="${id}">
|
|
127
135
|
<h2 id="${id}">${title}</h2>
|
|
128
|
-
${rows.length === 0 ? '<p class="empty">該当するデータはありません。</p>' : `<div class="table-wrap"><table><thead><tr><th scope="col">項目</th>${METRIC_KEYS.map((key) => `<th scope="col">${key}</th>`).join('')}<th scope="col">提案率</th><th scope="col">採用率</th></tr></thead><tbody>${rows.map(([label, summary]) => `<tr><th scope="row">${escapeHtml(label)}</th>${METRIC_KEYS.map((key) => `<td>${escapeHtml(metric(summary, key))}</td>`).join('')}<td>${escapeHtml(rate(metricNumber(summary, 'P'), metricNumber(summary, 'S')))}</td><td>${escapeHtml(rate(metricNumber(summary, 'A'), metricNumber(summary, 'C')))}</td></tr>`).join('')}</tbody></table></div>`}
|
|
136
|
+
${rows.length === 0 ? '<p class="empty">該当するデータはありません。</p>' : `<div class="table-wrap"><table><thead><tr><th scope="col">項目</th>${METRIC_KEYS.map((key) => `<th scope="col">${METRIC_LABELS[key]}</th>`).join('')}<th scope="col">提案率</th><th scope="col">採用率</th></tr></thead><tbody>${rows.map(([label, summary]) => `<tr><th scope="row">${escapeHtml(label)}</th>${METRIC_KEYS.map((key) => `<td>${escapeHtml(metric(summary, key))}</td>`).join('')}<td>${escapeHtml(rate(metricNumber(summary, 'P'), metricNumber(summary, 'S')))}</td><td>${escapeHtml(rate(metricNumber(summary, 'A'), metricNumber(summary, 'C')))}</td></tr>`).join('')}</tbody></table></div>`}
|
|
129
137
|
</section>`;
|
|
130
138
|
}
|
|
131
139
|
|
|
@@ -217,8 +225,8 @@ h1 { margin: .25rem 0; font-size: clamp(1.75rem, 4vw, 2.5rem); } h2 { margin-top
|
|
|
217
225
|
.device { align-items: center; border: 1px solid #ccd4dd; border-radius: 999px; color: inherit; display: inline-flex; gap: .45rem; padding: .45rem .7rem; text-decoration: none; }
|
|
218
226
|
.device[aria-current="page"] { border-color: #1c63b8; box-shadow: 0 0 0 2px #b8d6fa; }.device small { color: #52606d; }.status-dot { background: #a03333; border-radius: 50%; height: .55rem; width: .55rem; }.online .status-dot { background: #16803c; }
|
|
219
227
|
.filters { align-items: end; display: flex; flex-wrap: wrap; gap: .75rem; margin: 1.5rem 0; }.filters label { display: grid; font-size: .85rem; gap: .25rem; }.filters input, button { border: 1px solid #aeb8c4; border-radius: .35rem; font: inherit; padding: .45rem; }button { background: #1c63b8; color: white; cursor: pointer; }
|
|
220
|
-
.section-heading { align-items: baseline; display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; }.rates { display: flex; gap: 1.25rem; margin: 0; }.rates div { display: flex; gap: .
|
|
221
|
-
.metrics { display: grid; gap: .75rem; grid-template-columns: repeat(6, minmax(
|
|
228
|
+
.section-heading { align-items: baseline; display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; }.rates { display: flex; gap: 1.25rem; margin: 0; }.rates div { display: flex; gap: .45rem; }.rates dt { color: #52606d; }.rates dt small { display: block; font-size: .72rem; }.rates dd { font-weight: 700; margin: 0; }
|
|
229
|
+
.metrics { display: grid; gap: .75rem; grid-template-columns: repeat(6, minmax(110px, 1fr)); }.metric { background: #fff; border: 1px solid #d7dce2; border-radius: .5rem; padding: .8rem; }.metric span { color: #52606d; display: block; }.metric strong { font-size: 1.5rem; }
|
|
222
230
|
.table-wrap { overflow-x: auto; }table { border-collapse: collapse; min-width: 680px; width: 100%; }th, td { border-bottom: 1px solid #d7dce2; padding: .6rem; text-align: left; vertical-align: top; }thead { background: #eaf0f6; }tbody tr:nth-child(even) { background: #fbfcfd; }
|
|
223
231
|
.case-detail { border-top: 2px solid #1c63b8; margin-top: 2.5rem; }.metadata { display: flex; flex-wrap: wrap; gap: 1rem; }.metadata div { min-width: 12rem; }.metadata dt { color: #52606d; }.metadata dd { margin: .2rem 0; overflow-wrap: anywhere; }pre { background: #1e2935; color: #e6edf3; margin: 0; overflow-x: auto; padding: 1rem; white-space: pre-wrap; word-break: break-word; }
|
|
224
232
|
@media (max-width: 700px) { .dashboard { padding-top: 1rem; }.metrics { grid-template-columns: repeat(3, 1fr); }.filters { align-items: stretch; flex-direction: column; }.filters input, button { width: 100%; }.rates { flex-direction: column; gap: .25rem; } }
|