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 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 `S/P/I/C/A/M`, proposal and adoption rates, project/tool breakdowns,
260
- non-adopted cases, and the two separately captured context sources. The hub checks health only
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-spotter",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "Audit agent running alongside Claude Code that catches missed tool calls — 気づく役と実行する役の分離",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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>提案率 P/S</dt><dd>${escapeHtml(proposal)}</dd></div><div><dt>採用率 A/C</dt><dd>${escapeHtml(adoption)}</dd></div></dl>`;
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: .35rem; }.rates dt { color: #52606d; }.rates dd { font-weight: 700; margin: 0; }
221
- .metrics { display: grid; gap: .75rem; grid-template-columns: repeat(6, minmax(90px, 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; }
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; } }