ccsniff 1.1.3 → 1.1.5
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/package.json +1 -1
- package/src/cli.js +12 -2
- package/src/filters.js +6 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -26,8 +26,8 @@ if (process.argv[2] === 'gui') {
|
|
|
26
26
|
} else {
|
|
27
27
|
|
|
28
28
|
const FLAGS = {
|
|
29
|
-
string: ['since', 'until', 'before', 'after', 'grep', 'igrep', 'cwd', 'project', 'role', 'type', 'tool', 'session', 'sid', 'parent', 'rollup', 'format', 'sort', 'unsloth', 'unsloth-format'],
|
|
30
|
-
multi: ['grep', 'igrep', 'role', 'type', 'tool', 'session', 'sid', 'project', 'cwd'],
|
|
29
|
+
string: ['since', 'until', 'before', 'after', 'grep', 'igrep', 'cwd', 'project', 'role', 'type', 'tool', 'session', 'sid', 'parent', 'rollup', 'format', 'sort', 'unsloth', 'unsloth-format', 'exclude-sess', 'exclude-sid', 'exclude-cwd', 'exclude-project'],
|
|
30
|
+
multi: ['grep', 'igrep', 'role', 'type', 'tool', 'session', 'sid', 'project', 'cwd', 'exclude-sess', 'exclude-sid', 'exclude-cwd', 'exclude-project'],
|
|
31
31
|
number: ['limit', 'head', 'tail-n', 'ctx', 'truncate'],
|
|
32
32
|
bool: ['json', 'ndjson', 'tail', 'f', 'full', 'reverse', 'invert', 'no-subagents', 'only-subagents', 'no-meta', 'only-meta', 'list-sessions', 'list-projects', 'list-tools', 'bash-discipline', 'include-subagents', 'stats', 'count', 'help', 'h'],
|
|
33
33
|
};
|
|
@@ -81,6 +81,9 @@ FILTERS (repeatable flags combine as OR within a flag, AND across flags)
|
|
|
81
81
|
--type <t> text|tool_use|tool_result|thinking|system|result; repeat = OR
|
|
82
82
|
--tool <name> tool name (Read, Bash, ...); repeat = OR
|
|
83
83
|
--session <sid> session id prefix; repeat = OR (alias: --sid)
|
|
84
|
+
--exclude-sess <sid> exclude session id prefix; repeat = exclude any (alias: --exclude-sid)
|
|
85
|
+
--exclude-cwd <re> exclude working-dir regex; repeat = exclude any
|
|
86
|
+
--exclude-project <n> exclude basename(cwd) exact match; repeat = exclude any
|
|
84
87
|
--parent <sid> subagent parent session id
|
|
85
88
|
--no-subagents exclude subagent sessions
|
|
86
89
|
--only-subagents only subagent sessions
|
|
@@ -248,6 +251,11 @@ if (opts['bash-discipline']) {
|
|
|
248
251
|
const BAD_LEADING = /^\s*(cat|head|tail|ls|grep|find|sed|awk)\b/;
|
|
249
252
|
const SLEEP_POLL = /\bsleep\s+\d+\s*;.*(cat|ls|grep|find|head|tail)/;
|
|
250
253
|
const SPOOL_WRITE = /\.gm\/exec-spool\/in\//;
|
|
254
|
+
// The host harness explicitly endorses `until <check>; do sleep N; done` as
|
|
255
|
+
// the canonical pattern for polling external state (see Bash tool description
|
|
256
|
+
// and Monitor docs). Same for `while !curl ...; do sleep N; done`. These are
|
|
257
|
+
// NOT sleep-poll violations even though they contain `sleep N`.
|
|
258
|
+
const ENDORSED_POLL = /^\s*(until|while)\s+/;
|
|
251
259
|
const violations = [];
|
|
252
260
|
for (const ev of all) {
|
|
253
261
|
if (!filter(ev)) continue;
|
|
@@ -256,6 +264,8 @@ if (opts['bash-discipline']) {
|
|
|
256
264
|
const cmd = ev.block?.input?.command || '';
|
|
257
265
|
// `echo > .gm/exec-spool/in/<verb>/N.txt` is the canonical spool-write pattern, not a deviation.
|
|
258
266
|
if (SPOOL_WRITE.test(cmd) && /^\s*echo\b/.test(cmd)) continue;
|
|
267
|
+
// `until ...; do sleep N; done` is the harness-endorsed poll pattern.
|
|
268
|
+
if (ENDORSED_POLL.test(cmd)) continue;
|
|
259
269
|
const kind = SLEEP_POLL.test(cmd) ? 'sleep-poll' : (BAD_LEADING.test(cmd) ? 'bad-leading-cmd' : null);
|
|
260
270
|
if (!kind) continue;
|
|
261
271
|
violations.push({ ts: ev.timestamp, sid: ev.conversation.id, project: path.basename(ev.conversation.cwd || ''), kind, cmd: cmd.slice(0, 200) });
|
package/src/filters.js
CHANGED
|
@@ -46,6 +46,9 @@ export function buildFilter(opts) {
|
|
|
46
46
|
const types = new Set(m.type || []);
|
|
47
47
|
const tools = new Set(m.tool || []);
|
|
48
48
|
const sids = (m.session || []).concat(m.sid || []);
|
|
49
|
+
const excludeSids = (m['exclude-sess'] || []).concat(m['exclude-sid'] || []);
|
|
50
|
+
const excludeCwdRes = compileRegexes(m['exclude-cwd']);
|
|
51
|
+
const excludeProjects = new Set(m['exclude-project'] || []);
|
|
49
52
|
const parent = opts.parent || null;
|
|
50
53
|
|
|
51
54
|
return ev => {
|
|
@@ -61,6 +64,9 @@ export function buildFilter(opts) {
|
|
|
61
64
|
else if (types.size && !types.has(block.type)) pass = false;
|
|
62
65
|
else if (tools.size && !tools.has(block.name)) pass = false;
|
|
63
66
|
else if (sids.length && !sids.some(s => conv.id?.startsWith(s))) pass = false;
|
|
67
|
+
else if (excludeSids.length && excludeSids.some(s => conv.id?.startsWith(s))) pass = false;
|
|
68
|
+
else if (excludeCwdRes.length && excludeCwdRes.some(r => r.test(conv.cwd || ''))) pass = false;
|
|
69
|
+
else if (excludeProjects.size && excludeProjects.has(path.basename(conv.cwd || ''))) pass = false;
|
|
64
70
|
else if (parent && conv.parentSid !== parent) pass = false;
|
|
65
71
|
else if (opts['no-subagents'] && conv.isSubagent) pass = false;
|
|
66
72
|
else if (opts['only-subagents'] && !conv.isSubagent) pass = false;
|