@axiomatic-labs/claudeflow 2.13.32 → 2.13.34

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.
Files changed (2) hide show
  1. package/lib/panel.js +70 -3
  2. package/package.json +1 -1
package/lib/panel.js CHANGED
@@ -422,6 +422,29 @@ function readLogEvent(cwd, id) {
422
422
  }
423
423
  }
424
424
 
425
+ // Destructive: removes every sidecar in .claude/tmp/log-events/ and truncates
426
+ // .claude/tmp/hooks.log. Returns counts so the UI can confirm the wipe.
427
+ function clearLogs(cwd) {
428
+ const dir = logEventsDir(cwd);
429
+ let removed = 0;
430
+ try {
431
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));
432
+ for (const f of files) {
433
+ try { fs.unlinkSync(path.join(dir, f)); removed++; } catch {}
434
+ }
435
+ } catch {}
436
+ // Truncate the human-readable log too so the user sees a fresh tail -f.
437
+ let logTruncated = false;
438
+ const logPath = path.join(cwd, '.claude', 'tmp', 'hooks.log');
439
+ try {
440
+ if (fs.existsSync(logPath)) {
441
+ fs.writeFileSync(logPath, '');
442
+ logTruncated = true;
443
+ }
444
+ } catch {}
445
+ return { removed, logTruncated };
446
+ }
447
+
425
448
  function getLogsInfo(cwd) {
426
449
  const dir = logEventsDir(cwd);
427
450
  const thresholds = tokenThresholds();
@@ -593,6 +616,10 @@ details[open] summary { padding-bottom: 8px; border-bottom: 1px solid var(--bord
593
616
  .logs-controls { display: flex; gap: 12px; align-items: center; margin-bottom: 14px; }
594
617
  .logs-controls select, .logs-controls button { background: var(--panel); border: 1px solid var(--border); color: var(--fg); padding: 6px 10px; border-radius: 6px; font: inherit; }
595
618
  .logs-controls button:hover { border-color: var(--accent); cursor: pointer; }
619
+ .logs-clear-btn { margin-left: auto; color: var(--err); border-color: rgba(248,81,73,0.4); }
620
+ .logs-clear-btn:hover { border-color: var(--err); background: rgba(248,81,73,0.08); }
621
+ .logs-clear-btn:disabled { opacity: 0.4; cursor: not-allowed; color: var(--muted); border-color: var(--border); }
622
+ .logs-clear-btn:disabled:hover { border-color: var(--border); background: var(--panel); }
596
623
  .logs-table { width: 100%; border-collapse: collapse; font-size: 13px; }
597
624
  .logs-table th, .logs-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
598
625
  .logs-table th { font-weight: 500; color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
@@ -606,6 +633,11 @@ details[open] summary { padding-bottom: 8px; border-bottom: 1px solid var(--bord
606
633
  .logs-table tr.token-normal td:first-child { border-left: 3px solid var(--border); }
607
634
  .logs-table tr.token-heavy td:first-child { border-left: 3px solid var(--warn); }
608
635
  .logs-table tr.token-high td:first-child { border-left: 3px solid var(--err); }
636
+ .logs-table tr.silenced { opacity: 0.78; }
637
+ .logs-table tr.silenced .logs-handler { text-decoration: line-through; text-decoration-color: rgba(248,81,73,0.5); }
638
+ .logs-status { font-family: var(--mono); font-size: 12px; white-space: nowrap; }
639
+ .logs-status.injected { color: #58a6ff; }
640
+ .logs-status.silenced { color: var(--err); font-weight: 600; }
609
641
  .token-badge { display: inline-block; padding: 1px 7px; border-radius: 999px; font-size: 10px; font-family: var(--mono); margin-left: 6px; vertical-align: middle; }
610
642
  .token-badge.heavy { background: rgba(210,153,34,0.18); color: var(--warn); }
611
643
  .token-badge.high { background: rgba(248,81,73,0.20); color: var(--err); }
@@ -1059,9 +1091,13 @@ function renderLogs() {
1059
1091
  const thresholds = (state.logs && state.logs.thresholds) || { heavy: 8000, high: 20000 };
1060
1092
  const rows = logsState.entries.map((e) => {
1061
1093
  const isReminder = e.type.startsWith('reminder');
1094
+ const isSilenced = e.type === 'enforcement-silenced';
1062
1095
  const badge = isReminder
1063
1096
  ? '<span class="badge info" style="background:rgba(88,166,255,0.18); color:#58a6ff">REMINDER</span>'
1064
1097
  : '<span class="badge err">ENFORCEMENT</span>';
1098
+ const statusCell = isSilenced
1099
+ ? '<span class="logs-status silenced" title="Master Enforcement OFF silenced this hook — it did NOT run. Claude Code received no output for this event.">✗ SILENCED</span>'
1100
+ : '<span class="logs-status injected" title="Hook ran and emitted additionalContext to Claude Code">✓ injected</span>';
1065
1101
  const tokenStatus = e.tokenStatus || 'none';
1066
1102
  const tokenBadge = tokenStatus === 'high'
1067
1103
  ? '<span class="token-badge high" title="High — exceeds the high-water threshold (' + thresholds.high.toLocaleString() + ' tokens). Consider trimming.">⚠ HIGH</span>'
@@ -1074,10 +1110,12 @@ function renderLogs() {
1074
1110
  const bytesStr = e.contentBytes ? fmtBytes(e.contentBytes) : '';
1075
1111
  const tokensTitle = e.contentBytes
1076
1112
  ? \`≈\${e.contentTokens} tokens (chars/4 heuristic) · \${fmtBytes(e.contentBytes)} raw · status: \${tokenStatus}\`
1077
- : 'No content (enforcement-silenced)';
1078
- return \`<tr class="token-\${tokenStatus}">
1113
+ : 'No content (hook was silenced — never ran)';
1114
+ const rowCls = \`token-\${tokenStatus}\${isSilenced ? ' silenced' : ''}\`;
1115
+ return \`<tr class="\${rowCls}">
1079
1116
  <td class="logs-time"><span title="\${escapeHtml(e.timestamp)}">\${escapeHtml(fmtLocalTime(e.timestamp))}</span><br><span class="muted" style="font-size:11px">\${fmtRelativeTime(e.timestamp)}</span></td>
1080
1117
  <td>\${badge}</td>
1118
+ <td>\${statusCell}</td>
1081
1119
  <td class="logs-handler"><span class="muted">\${escapeHtml(e.event)}</span><br>\${escapeHtml(e.handler)}\${e.matcher ? \` <span class="muted">(\${escapeHtml(e.matcher)})</span>\` : ''}</td>
1082
1120
  <td class="logs-size" title="\${escapeHtml(tokensTitle)}">\${tokensStr}\${tokenBadge}\${bytesStr ? \`<br><span class="muted" style="font-size:11px">\${bytesStr}</span>\` : ''}</td>
1083
1121
  <td><button class="logs-view" data-log-id="\${escapeHtml(e.id)}">View</button></td>
@@ -1102,9 +1140,10 @@ function renderLogs() {
1102
1140
  </select>
1103
1141
  </label>
1104
1142
  <button id="logs-reload">Reload</button>
1143
+ <button id="logs-clear" class="logs-clear-btn" \${state.logs && state.logs.total ? '' : 'disabled'} title="Delete every sidecar in .claude/tmp/log-events/ and truncate .claude/tmp/hooks.log. Cannot be undone.">Clear all</button>
1105
1144
  </div>
1106
1145
  \${empty || \`<table class="logs-table">
1107
- <thead><tr><th>Time</th><th>Type</th><th>Handler</th><th title="Approximate token count (chars/4 heuristic, ±10–15% vs Claude's real tokenizer). Heavy ≥\${thresholds.heavy.toLocaleString()} → yellow border. High ≥\${thresholds.high.toLocaleString()} → red border. Anthropic does not publish official thresholds for per-turn additionalContext.">≈ Tokens</th><th></th></tr></thead>
1146
+ <thead><tr><th>Time</th><th>Type</th><th title="Did the hook actually run? ✓ injected = hook ran and emitted to Claude Code · ✗ SILENCED = master Enforcement OFF blocked the hook from running.">Status</th><th>Handler</th><th title="Approximate token count (chars/4 heuristic, ±10–15% vs Claude's real tokenizer). Heavy ≥\${thresholds.heavy.toLocaleString()} → yellow border. High ≥\${thresholds.high.toLocaleString()} → red border. Anthropic does not publish official thresholds for per-turn additionalContext.">≈ Tokens</th><th></th></tr></thead>
1108
1147
  <tbody>\${rows}</tbody>
1109
1148
  </table>\`}
1110
1149
  \${logsState.allLoaded || logsState.entries.length === 0 ? '' : '<div style="margin-top:12px"><button id="logs-load-more">Load older</button></div>'}\`;
@@ -1226,8 +1265,31 @@ document.addEventListener('click', (e) => {
1226
1265
  if (t.id === 'logs-load-more') {
1227
1266
  return loadLogsPage({ append: true });
1228
1267
  }
1268
+ if (t.id === 'logs-clear') {
1269
+ const total = (state.logs && state.logs.total) || 0;
1270
+ if (!total) return;
1271
+ if (!confirm('Delete ' + total + ' captured log event(s) and truncate .claude/tmp/hooks.log?\\n\\nThis cannot be undone. (Future events will be captured normally.)')) return;
1272
+ return clearLogsAction();
1273
+ }
1229
1274
  });
1230
1275
 
1276
+ async function clearLogsAction() {
1277
+ try {
1278
+ const r = await fetch('/api/logs', { method: 'DELETE' });
1279
+ const result = await r.json();
1280
+ if (!r.ok) {
1281
+ showToast('Clear failed: ' + (result.error || r.status), 'err');
1282
+ return;
1283
+ }
1284
+ showToast('Cleared ' + result.removed + ' event(s)' + (result.logTruncated ? ' + truncated hooks.log' : ''), 'ok');
1285
+ logsState.entries = [];
1286
+ logsState.allLoaded = false;
1287
+ await refresh();
1288
+ } catch (e) {
1289
+ showToast('Network error: ' + e.message, 'err');
1290
+ }
1291
+ }
1292
+
1231
1293
  document.addEventListener('change', (e) => {
1232
1294
  const t = e.target;
1233
1295
  if (!t || !t.classList) return;
@@ -1369,6 +1431,11 @@ function handler(cwd) {
1369
1431
  return send(status, JSON.stringify(result), 'application/json');
1370
1432
  }
1371
1433
 
1434
+ if (req.method === 'DELETE' && route === '/api/logs') {
1435
+ const result = clearLogs(cwd);
1436
+ return send(200, JSON.stringify(result), 'application/json');
1437
+ }
1438
+
1372
1439
  if (req.method === 'POST' && route === '/api/enforcement/toggle') {
1373
1440
  const raw = await readRequestBody(req);
1374
1441
  let payload;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiomatic-labs/claudeflow",
3
- "version": "2.13.32",
3
+ "version": "2.13.34",
4
4
  "description": "Claudeflow — AI-powered development toolkit for Claude Code. Skills, agents, hooks, and quality gates that ship production apps.",
5
5
  "bin": {
6
6
  "claudeflow": "./bin/cli.js"