@axiomatic-labs/claudeflow 2.13.31 → 2.13.33

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 +28 -3
  2. package/package.json +1 -1
package/lib/panel.js CHANGED
@@ -405,6 +405,9 @@ function listLogEvents(cwd, { limit = 200, before = null, type = 'all' } = {}) {
405
405
  contentTokens: tokens,
406
406
  tokenStatus: classifyTokens(tokens, thresholds),
407
407
  activeReminderIds: r.activeReminderIds || null,
408
+ capturedFromStdout: r.capturedFromStdout === true,
409
+ stdoutTruncated: r.stdoutTruncated === true,
410
+ parseError: r.parseError || null,
408
411
  });
409
412
  }
410
413
  return { entries, total, thresholds };
@@ -603,6 +606,11 @@ details[open] summary { padding-bottom: 8px; border-bottom: 1px solid var(--bord
603
606
  .logs-table tr.token-normal td:first-child { border-left: 3px solid var(--border); }
604
607
  .logs-table tr.token-heavy td:first-child { border-left: 3px solid var(--warn); }
605
608
  .logs-table tr.token-high td:first-child { border-left: 3px solid var(--err); }
609
+ .logs-table tr.silenced { opacity: 0.78; }
610
+ .logs-table tr.silenced .logs-handler { text-decoration: line-through; text-decoration-color: rgba(248,81,73,0.5); }
611
+ .logs-status { font-family: var(--mono); font-size: 12px; white-space: nowrap; }
612
+ .logs-status.injected { color: #58a6ff; }
613
+ .logs-status.silenced { color: var(--err); font-weight: 600; }
606
614
  .token-badge { display: inline-block; padding: 1px 7px; border-radius: 999px; font-size: 10px; font-family: var(--mono); margin-left: 6px; vertical-align: middle; }
607
615
  .token-badge.heavy { background: rgba(210,153,34,0.18); color: var(--warn); }
608
616
  .token-badge.high { background: rgba(248,81,73,0.20); color: var(--err); }
@@ -982,6 +990,17 @@ async function viewLogDetail(id) {
982
990
  html += '<h4 style="margin-top:14px">Active reminders at fire time</h4>';
983
991
  html += '<p class="sub">' + ev.activeReminderIds.map(escapeHtml).join(', ') + '</p>';
984
992
  }
993
+ // Provenance: did we authentically capture the hook's stdout, or is the
994
+ // record empty / parsed-failed / truncated? Make this visible at the top
995
+ // of the drawer so the user can trust (or distrust) the content shown.
996
+ if (typeof ev.capturedFromStdout === 'boolean') {
997
+ const provBadge = ev.capturedFromStdout
998
+ ? '<span class="badge ok" title="Bytes captured directly from the hook&#39;s stdout — this is the actual additionalContext that Claude Code received.">✓ stdout-captured</span>'
999
+ : (ev.parseError
1000
+ ? '<span class="badge err" title="Hook stdout was captured but JSON.parse failed: ' + escapeHtml(ev.parseError) + '">✗ parse failed</span>'
1001
+ : '<span class="badge warn" title="No content was captured — the hook may have crashed before writing or returned null.">⚠ no stdout</span>');
1002
+ html += '<h4 style="margin-top:14px">Provenance ' + provBadge + (ev.stdoutTruncated ? ' <span class="badge warn">⚠ truncated</span>' : '') + '</h4>';
1003
+ }
985
1004
  if (typeof ev.contentBytes === 'number' && ev.contentBytes > 0) {
986
1005
  const tokens = typeof ev.contentTokens === 'number' ? ev.contentTokens : Math.round(ev.contentBytes / 4);
987
1006
  const th = (state.logs && state.logs.thresholds) || { heavy: 8000, high: 20000 };
@@ -1045,9 +1064,13 @@ function renderLogs() {
1045
1064
  const thresholds = (state.logs && state.logs.thresholds) || { heavy: 8000, high: 20000 };
1046
1065
  const rows = logsState.entries.map((e) => {
1047
1066
  const isReminder = e.type.startsWith('reminder');
1067
+ const isSilenced = e.type === 'enforcement-silenced';
1048
1068
  const badge = isReminder
1049
1069
  ? '<span class="badge info" style="background:rgba(88,166,255,0.18); color:#58a6ff">REMINDER</span>'
1050
1070
  : '<span class="badge err">ENFORCEMENT</span>';
1071
+ const statusCell = isSilenced
1072
+ ? '<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>'
1073
+ : '<span class="logs-status injected" title="Hook ran and emitted additionalContext to Claude Code">✓ injected</span>';
1051
1074
  const tokenStatus = e.tokenStatus || 'none';
1052
1075
  const tokenBadge = tokenStatus === 'high'
1053
1076
  ? '<span class="token-badge high" title="High — exceeds the high-water threshold (' + thresholds.high.toLocaleString() + ' tokens). Consider trimming.">⚠ HIGH</span>'
@@ -1060,10 +1083,12 @@ function renderLogs() {
1060
1083
  const bytesStr = e.contentBytes ? fmtBytes(e.contentBytes) : '';
1061
1084
  const tokensTitle = e.contentBytes
1062
1085
  ? \`≈\${e.contentTokens} tokens (chars/4 heuristic) · \${fmtBytes(e.contentBytes)} raw · status: \${tokenStatus}\`
1063
- : 'No content (enforcement-silenced)';
1064
- return \`<tr class="token-\${tokenStatus}">
1086
+ : 'No content (hook was silenced — never ran)';
1087
+ const rowCls = \`token-\${tokenStatus}\${isSilenced ? ' silenced' : ''}\`;
1088
+ return \`<tr class="\${rowCls}">
1065
1089
  <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>
1066
1090
  <td>\${badge}</td>
1091
+ <td>\${statusCell}</td>
1067
1092
  <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>
1068
1093
  <td class="logs-size" title="\${escapeHtml(tokensTitle)}">\${tokensStr}\${tokenBadge}\${bytesStr ? \`<br><span class="muted" style="font-size:11px">\${bytesStr}</span>\` : ''}</td>
1069
1094
  <td><button class="logs-view" data-log-id="\${escapeHtml(e.id)}">View</button></td>
@@ -1090,7 +1115,7 @@ function renderLogs() {
1090
1115
  <button id="logs-reload">Reload</button>
1091
1116
  </div>
1092
1117
  \${empty || \`<table class="logs-table">
1093
- <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>
1118
+ <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>
1094
1119
  <tbody>\${rows}</tbody>
1095
1120
  </table>\`}
1096
1121
  \${logsState.allLoaded || logsState.entries.length === 0 ? '' : '<div style="margin-top:12px"><button id="logs-load-more">Load older</button></div>'}\`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiomatic-labs/claudeflow",
3
- "version": "2.13.31",
3
+ "version": "2.13.33",
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"