ccsniff 1.0.12 → 1.0.14

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/package.json +1 -1
  2. package/src/cli.js +16 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccsniff",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Watch Claude Code JSONL output files and emit structured events as a Node.js EventEmitter",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/cli.js CHANGED
@@ -81,33 +81,44 @@ if (opts.gmAudit) {
81
81
  r2.on('streaming_progress', ev => {
82
82
  const conv = ev.conversation;
83
83
  if (cwdRe && !cwdRe.test(conv.cwd || '')) return;
84
+ if (conv.isSubagent) return; // memorize/subagents don't need gm invocation
84
85
  if (ev.role !== 'user' && ev.role !== 'assistant') return;
85
86
  const sid = conv.id;
86
87
  if (!sessions.has(sid)) sessions.set(sid, { cwd: conv.cwd, turns: [] });
87
88
  const s = sessions.get(sid);
88
89
  if (ev.role === 'user' && ev.block?.type === 'text') {
89
- s.turns.push({ isMeta: ev.block.isMeta || false, firstTool: null, text: (ev.block.text || '').slice(0, 80) });
90
+ const t = ev.block.text || '';
91
+ const isContinuation = /^This session is being continued from a previous conversation/.test(t.trimStart());
92
+ const isSystem = ev.block.isMeta || isContinuation || /^<(task-notification|command-name|local-command|system-reminder)\b/.test(t.trimStart()) || t === '[Request interrupted by user]' || t === '[Request interrupted by user for tool use]';
93
+ s.turns.push({ isMeta: isSystem, firstTool: null, text: t.slice(0, 80) });
90
94
  } else if (ev.role === 'assistant' && ev.block?.type === 'tool_use' && s.turns.length) {
91
95
  const last = s.turns[s.turns.length - 1];
92
96
  if (last.firstTool === null) last.firstTool = ev.block.name || '';
93
97
  }
94
98
  });
95
99
  r2.replay({ since });
96
- let totalReal = 0, totalCompliant = 0;
100
+ // terse: single-word / slash-command / short follow-up — less critical to enforce gm
101
+ const isTerse = t => t.text.trim().length <= 5 || /^\/\w/.test(t.text.trim());
102
+ let totalReal = 0, totalCompliant = 0, totalTerse = 0;
97
103
  for (const [sid, s] of sessions) {
98
104
  const real = s.turns.filter(t => !t.isMeta);
99
105
  const compliant = real.filter(t => t.firstTool === 'Skill' || t.firstTool === 'mcp__gm__Skill');
106
+ const terse = real.filter(t => isTerse(t) && t.firstTool !== 'Skill' && t.firstTool !== 'mcp__gm__Skill');
100
107
  totalReal += real.length;
101
108
  totalCompliant += compliant.length;
109
+ totalTerse += terse.length;
102
110
  const pct = real.length ? Math.round(100 * compliant.length / real.length) : 0;
103
- const violations = real.filter(t => t.firstTool !== 'Skill' && t.firstTool !== 'mcp__gm__Skill');
104
- process.stdout.write(`[${pct}%] ${path.basename(s.cwd || sid)} (${compliant.length}/${real.length}) sid=${sid.slice(0, 8)}\n`);
111
+ const violations = real.filter(t => t.firstTool !== 'Skill' && t.firstTool !== 'mcp__gm__Skill' && !isTerse(t));
112
+ if (real.length === 0) continue;
113
+ process.stdout.write(`[${pct}%] ${path.basename(s.cwd || sid)} (${compliant.length}/${real.length}, terse-skip:${terse.length}) sid=${sid.slice(0, 8)}\n`);
105
114
  for (const v of violations.slice(0, 3)) {
106
115
  process.stdout.write(` MISS first=${v.firstTool || 'none'} msg="${v.text.replace(/\s+/g, ' ')}"\n`);
107
116
  }
108
117
  }
109
118
  const total = totalReal ? Math.round(100 * totalCompliant / totalReal) : 0;
110
- process.stderr.write(`# gm-audit: ${totalCompliant}/${totalReal} compliant (${total}%) across ${sessions.size} sessions\n`);
119
+ const adjCompliant = totalCompliant + totalTerse;
120
+ const adjTotal = totalReal ? Math.round(100 * adjCompliant / totalReal) : 0;
121
+ process.stderr.write(`# gm-audit: ${totalCompliant}/${totalReal} compliant (${total}%) | adj (terse-skip): ${adjCompliant}/${totalReal} (${adjTotal}%) | ${sessions.size} sessions\n`);
111
122
  process.exit(0);
112
123
  }
113
124