coolhand-cli 0.7.0 → 0.8.0

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 (90) hide show
  1. package/README.md +21 -2
  2. package/dist/api/feedback-client.d.ts +17 -0
  3. package/dist/api/feedback-client.d.ts.map +1 -0
  4. package/dist/api/feedback-client.js +40 -0
  5. package/dist/api/feedback-client.js.map +1 -0
  6. package/dist/api/last-sync.d.ts +8 -6
  7. package/dist/api/last-sync.d.ts.map +1 -1
  8. package/dist/api/last-sync.js +27 -16
  9. package/dist/api/last-sync.js.map +1 -1
  10. package/dist/bin.js +0 -0
  11. package/dist/cli.d.ts +1 -1
  12. package/dist/cli.d.ts.map +1 -1
  13. package/dist/cli.js +170 -4
  14. package/dist/cli.js.map +1 -1
  15. package/dist/commands/analyze-claude-sessions.d.ts +6 -0
  16. package/dist/commands/analyze-claude-sessions.d.ts.map +1 -1
  17. package/dist/commands/analyze-claude-sessions.js +60 -9
  18. package/dist/commands/analyze-claude-sessions.js.map +1 -1
  19. package/dist/commands/clients.d.ts.map +1 -1
  20. package/dist/commands/clients.js +2 -1
  21. package/dist/commands/clients.js.map +1 -1
  22. package/dist/commands/close-optimization.js +1 -1
  23. package/dist/commands/close-optimization.js.map +1 -1
  24. package/dist/commands/get-feedback.d.ts +3 -0
  25. package/dist/commands/get-feedback.d.ts.map +1 -0
  26. package/dist/commands/get-feedback.js +70 -0
  27. package/dist/commands/get-feedback.js.map +1 -0
  28. package/dist/commands/get-optimization.d.ts.map +1 -1
  29. package/dist/commands/get-optimization.js +61 -10
  30. package/dist/commands/get-optimization.js.map +1 -1
  31. package/dist/commands/mcp-call.d.ts.map +1 -1
  32. package/dist/commands/mcp-call.js +14 -50
  33. package/dist/commands/mcp-call.js.map +1 -1
  34. package/dist/commands/search-feedback.d.ts +3 -0
  35. package/dist/commands/search-feedback.d.ts.map +1 -0
  36. package/dist/commands/search-feedback.js +71 -0
  37. package/dist/commands/search-feedback.js.map +1 -0
  38. package/dist/commands/status.d.ts.map +1 -1
  39. package/dist/commands/status.js +2 -1
  40. package/dist/commands/status.js.map +1 -1
  41. package/dist/commands/whoami.js +1 -1
  42. package/dist/commands/whoami.js.map +1 -1
  43. package/dist/config.d.ts.map +1 -1
  44. package/dist/config.js +5 -1
  45. package/dist/config.js.map +1 -1
  46. package/dist/errors.d.ts +1 -1
  47. package/dist/errors.d.ts.map +1 -1
  48. package/dist/errors.js.map +1 -1
  49. package/dist/parse-when.d.ts +17 -0
  50. package/dist/parse-when.d.ts.map +1 -0
  51. package/dist/parse-when.js +49 -0
  52. package/dist/parse-when.js.map +1 -0
  53. package/dist/sessions/claude-scanner.d.ts +14 -1
  54. package/dist/sessions/claude-scanner.d.ts.map +1 -1
  55. package/dist/sessions/claude-scanner.js +205 -41
  56. package/dist/sessions/claude-scanner.js.map +1 -1
  57. package/dist/sessions/cowork-scanner.d.ts +2 -0
  58. package/dist/sessions/cowork-scanner.d.ts.map +1 -1
  59. package/dist/sessions/cowork-scanner.js +22 -9
  60. package/dist/sessions/cowork-scanner.js.map +1 -1
  61. package/dist/sessions/redact-secrets.d.ts +14 -0
  62. package/dist/sessions/redact-secrets.d.ts.map +1 -0
  63. package/dist/sessions/redact-secrets.js +47 -0
  64. package/dist/sessions/redact-secrets.js.map +1 -0
  65. package/dist/sessions/session-filter.d.ts +28 -0
  66. package/dist/sessions/session-filter.d.ts.map +1 -0
  67. package/dist/sessions/session-filter.js +38 -0
  68. package/dist/sessions/session-filter.js.map +1 -0
  69. package/dist/types.d.ts +35 -0
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/types.js.map +1 -1
  72. package/dist/version.d.ts +2 -2
  73. package/dist/version.js +1 -1
  74. package/package.json +2 -2
  75. package/dist/commands/accounts.d.ts +0 -3
  76. package/dist/commands/accounts.d.ts.map +0 -1
  77. package/dist/commands/accounts.js +0 -59
  78. package/dist/commands/accounts.js.map +0 -1
  79. package/dist/commands/add-optimization-comment.d.ts +0 -3
  80. package/dist/commands/add-optimization-comment.d.ts.map +0 -1
  81. package/dist/commands/add-optimization-comment.js +0 -28
  82. package/dist/commands/add-optimization-comment.js.map +0 -1
  83. package/dist/commands/capture-sessions.d.ts +0 -3
  84. package/dist/commands/capture-sessions.d.ts.map +0 -1
  85. package/dist/commands/capture-sessions.js +0 -79
  86. package/dist/commands/capture-sessions.js.map +0 -1
  87. package/dist/commands/create-optimization.d.ts +0 -3
  88. package/dist/commands/create-optimization.d.ts.map +0 -1
  89. package/dist/commands/create-optimization.js +0 -38
  90. package/dist/commands/create-optimization.js.map +0 -1
@@ -1,40 +1,169 @@
1
1
  import { promises as fs } from 'fs';
2
2
  import * as os from 'os';
3
3
  import * as path from 'path';
4
+ import { redactSecrets } from './redact-secrets.js';
5
+ /** Cap on a single serialised tool input/output. Edit/Write inputs carry whole file bodies. */
6
+ const MAX_BLOCK_CHARS = 2000;
4
7
  export function defaultProjectsDir() {
5
8
  return path.join(os.homedir(), '.claude', 'projects');
6
9
  }
7
- /** Pull plain text out of a Claude message `content`, which is either a string or block array. */
8
- function extractText(content) {
10
+ function toCount(value) {
11
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
12
+ }
13
+ /** JSON.stringify that yields a placeholder instead of throwing on circular structures. */
14
+ function safeStringify(value) {
15
+ let json;
16
+ try {
17
+ json = JSON.stringify(value);
18
+ }
19
+ catch {
20
+ return '[unserializable]';
21
+ }
22
+ if (typeof json !== 'string') {
23
+ return '';
24
+ }
25
+ return json;
26
+ }
27
+ function truncate(text) {
28
+ return text.length > MAX_BLOCK_CHARS ? `${text.slice(0, MAX_BLOCK_CHARS)}…[truncated]` : text;
29
+ }
30
+ /**
31
+ * Render one content block as plain text. `thinking`/`redacted_thinking` are dropped for now
32
+ * (highest-sensitivity; restored once redaction is proven) and unknown types render empty. Never throws.
33
+ */
34
+ function blockToText(block) {
35
+ if (!block || typeof block !== 'object') {
36
+ return '';
37
+ }
38
+ const type = block.type;
39
+ if (type === 'text') {
40
+ const text = block.text;
41
+ return typeof text === 'string' ? text : '';
42
+ }
43
+ if (type === 'tool_use') {
44
+ const name = block.name;
45
+ const toolName = typeof name === 'string' ? name : 'unknown';
46
+ const input = block.input;
47
+ const rendered = input === undefined ? '' : ` ${truncate(redactSecrets(safeStringify(input)))}`;
48
+ return `[tool_use: ${toolName}]${rendered}`;
49
+ }
50
+ if (type === 'tool_result') {
51
+ const isError = block.is_error === true;
52
+ const label = isError ? '[tool_result:error]' : '[tool_result]';
53
+ const body = toolResultToText(block.content);
54
+ return body ? `${label} ${body}` : label;
55
+ }
56
+ if (type === 'image') {
57
+ return '[image]';
58
+ }
59
+ return '';
60
+ }
61
+ /** A tool_result's `content` is a string OR an array of blocks; render either to text. */
62
+ function toolResultToText(content) {
63
+ if (typeof content === 'string') {
64
+ return truncate(redactSecrets(content));
65
+ }
66
+ if (Array.isArray(content)) {
67
+ return truncate(redactSecrets(content.map(blockToText).filter((part) => part.length > 0).join('\n')));
68
+ }
69
+ return '';
70
+ }
71
+ /** A user message's `content` is a plain string OR an array of blocks (e.g. tool_result). */
72
+ function userContentToText(content) {
9
73
  if (typeof content === 'string') {
10
74
  return content;
11
75
  }
12
76
  if (Array.isArray(content)) {
13
- return content
14
- .filter((block) => Boolean(block) &&
15
- typeof block === 'object' &&
16
- block.type === 'text' &&
17
- typeof block.text === 'string')
18
- .map((block) => block.text)
19
- .join('\n');
77
+ return content.map(blockToText).filter((part) => part.length > 0).join('\n');
20
78
  }
21
79
  return '';
22
80
  }
23
- function toCount(value) {
24
- return typeof value === 'number' && Number.isFinite(value) ? value : 0;
81
+ /** Deep-redact secret-looking strings while preserving object/array structure. */
82
+ function redactDeep(value) {
83
+ if (typeof value === 'string') {
84
+ return redactSecrets(value);
85
+ }
86
+ if (Array.isArray(value)) {
87
+ return value.map(redactDeep);
88
+ }
89
+ if (value && typeof value === 'object') {
90
+ const out = {};
91
+ for (const [key, inner] of Object.entries(value)) {
92
+ if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
93
+ continue;
94
+ }
95
+ out[key] = redactDeep(inner);
96
+ }
97
+ return out;
98
+ }
99
+ return value;
100
+ }
101
+ /**
102
+ * The final assistant turn's raw blocks, prepared for `response_body.content`: thinking is dropped
103
+ * (as in the message text) and every string is run through the secret scrubber so nothing sensitive
104
+ * rides along in the structured half of the envelope.
105
+ */
106
+ function sanitizeResponseBlocks(blocks) {
107
+ const out = [];
108
+ for (const block of blocks) {
109
+ if (!block || typeof block !== 'object') {
110
+ continue;
111
+ }
112
+ const type = block.type;
113
+ if (type === 'thinking' || type === 'redacted_thinking') {
114
+ continue;
115
+ }
116
+ out.push(redactDeep(block));
117
+ }
118
+ return out;
119
+ }
120
+ /** The stable identity of an assistant turn. One turn is split across multiple JSONL lines. */
121
+ function turnKey(event, message, sessionId) {
122
+ const requestId = event.requestId;
123
+ if (typeof requestId === 'string' && requestId) {
124
+ return requestId;
125
+ }
126
+ const messageId = message.id;
127
+ if (typeof messageId === 'string' && messageId) {
128
+ return messageId;
129
+ }
130
+ return sessionId;
25
131
  }
26
132
  /**
27
133
  * Parse one transcript's raw text (newline-delimited JSON) into a SINGLE session envelope holding
28
134
  * the whole conversation — one log per session, not one per turn. Returns `null` when the transcript
29
135
  * contains no assistant turns. Pure function (no filesystem) so it is easy to unit test.
136
+ *
137
+ * Real transcripts split a single assistant API call across MULTIPLE lines — one per content block
138
+ * (thinking / text / tool_use) — and repeat the identical `usage` on every line. We therefore merge
139
+ * lines that share a `requestId` into one assistant message and count each turn (and its tokens)
140
+ * exactly once, matching benchmark/lib/transcript.mjs.
30
141
  */
31
142
  export function parseTranscript(content, sessionId) {
32
143
  const messages = [];
33
- const usage = { input_tokens: 0, output_tokens: 0, cache_read_input_tokens: 0 };
144
+ const usage = {
145
+ input_tokens: 0,
146
+ output_tokens: 0,
147
+ cache_read_input_tokens: 0,
148
+ cache_creation_input_tokens: 0,
149
+ };
34
150
  let sawUsage = false;
35
151
  let sessionModel;
36
152
  let lastAssistant = null;
37
153
  let turnCount = 0;
154
+ const countedTurns = new Set();
155
+ let pending = null;
156
+ const flushPending = () => {
157
+ if (!pending) {
158
+ return;
159
+ }
160
+ const text = redactSecrets(pending.blocks.map(blockToText).filter((part) => part.length > 0).join('\n')).trim();
161
+ if (text) {
162
+ messages.push({ role: 'assistant', content: text });
163
+ }
164
+ lastAssistant = { id: pending.key, content: pending.blocks, model: pending.model };
165
+ pending = null;
166
+ };
38
167
  for (const line of content.split('\n')) {
39
168
  const trimmed = line.trim();
40
169
  if (!trimmed) {
@@ -51,47 +180,54 @@ export function parseTranscript(content, sessionId) {
51
180
  continue;
52
181
  }
53
182
  const message = event.message;
54
- if (!message) {
183
+ if (!message || typeof message !== 'object') {
55
184
  continue;
56
185
  }
57
186
  if (event.type === 'user') {
58
- // `user` events include tool results (no text) — only keep actual prompt text.
59
- const text = extractText(message.content);
187
+ // A user turn ends any in-progress assistant turn (e.g. a tool_use awaiting its result).
188
+ flushPending();
189
+ const text = redactSecrets(userContentToText(message.content)).trim();
60
190
  if (text) {
61
191
  messages.push({ role: 'user', content: text });
62
192
  }
63
193
  continue;
64
194
  }
65
195
  if (event.type === 'assistant' && message.role === 'assistant') {
66
- const requestId = typeof event.requestId === 'string'
67
- ? event.requestId
68
- : typeof message.id === 'string'
69
- ? message.id
70
- : undefined;
71
- messages.push({ role: 'assistant', content: extractText(message.content) });
72
- turnCount += 1;
196
+ const key = turnKey(event, message, sessionId);
197
+ if (!pending || pending.key !== key) {
198
+ flushPending();
199
+ pending = { key, blocks: [], model: undefined };
200
+ }
201
+ const blocks = Array.isArray(message.content) ? message.content : [];
202
+ for (const block of blocks) {
203
+ pending.blocks.push(block);
204
+ }
73
205
  const model = typeof message.model === 'string' ? message.model : undefined;
74
206
  if (model) {
75
207
  sessionModel = model;
208
+ pending.model = model;
76
209
  }
77
- const turnUsage = message.usage;
78
- if (turnUsage && typeof turnUsage === 'object') {
79
- sawUsage = true;
80
- usage.input_tokens += toCount(turnUsage.input_tokens);
81
- usage.output_tokens += toCount(turnUsage.output_tokens);
82
- usage.cache_read_input_tokens += toCount(turnUsage.cache_read_input_tokens);
210
+ // Count each turn — and sum its usage — exactly once, no matter how many lines it spans.
211
+ if (!countedTurns.has(key)) {
212
+ countedTurns.add(key);
213
+ turnCount += 1;
214
+ const turnUsage = message.usage;
215
+ if (turnUsage && typeof turnUsage === 'object') {
216
+ sawUsage = true;
217
+ usage.input_tokens += toCount(turnUsage.input_tokens);
218
+ usage.output_tokens += toCount(turnUsage.output_tokens);
219
+ usage.cache_read_input_tokens += toCount(turnUsage.cache_read_input_tokens);
220
+ usage.cache_creation_input_tokens += toCount(turnUsage.cache_creation_input_tokens);
221
+ }
83
222
  }
84
- lastAssistant = {
85
- id: requestId ?? sessionId,
86
- content: Array.isArray(message.content) ? message.content : [],
87
- model,
88
- };
89
223
  }
90
224
  }
225
+ flushPending();
91
226
  // A session with no assistant turns has nothing to log.
92
227
  if (!lastAssistant) {
93
228
  return null;
94
229
  }
230
+ const finalTurn = lastAssistant;
95
231
  return {
96
232
  url: `claudecode://session/${sessionId}`,
97
233
  method: 'POST',
@@ -101,11 +237,11 @@ export function parseTranscript(content, sessionId) {
101
237
  messages,
102
238
  },
103
239
  response_body: {
104
- id: lastAssistant.id,
240
+ id: finalTurn.id,
105
241
  type: 'message',
106
242
  role: 'assistant',
107
- model: lastAssistant.model ?? sessionModel,
108
- content: lastAssistant.content,
243
+ model: finalTurn.model ?? sessionModel,
244
+ content: sanitizeResponseBlocks(finalTurn.content),
109
245
  usage: sawUsage ? usage : undefined,
110
246
  },
111
247
  turnCount,
@@ -118,26 +254,32 @@ export function parseTranscript(content, sessionId) {
118
254
  * When `sinceTime` is given, files whose last-modified time is older than it are skipped entirely
119
255
  * (not even read) — they cannot have grown since the last sync. `sessionCount` therefore counts only
120
256
  * the files actually examined this run, so it reflects the work done rather than the whole corpus.
257
+ *
258
+ * When `preFilter` is given it runs on each file's metadata (id, project folder, mtime) BEFORE the
259
+ * file is read; a rejected file is counted in `filteredOut` and its content never leaves disk.
121
260
  */
122
261
  export async function scanSessions(options = {}) {
123
262
  const dir = options.projectsDir ?? defaultProjectsDir();
124
263
  const sinceMs = options.sinceTime?.getTime();
264
+ const preFilter = options.preFilter;
125
265
  let names;
126
266
  try {
127
267
  names = await fs.readdir(dir, { recursive: true });
128
268
  }
129
269
  catch (err) {
130
270
  if (err.code === 'ENOENT') {
131
- return { envelopes: [], sessionCount: 0, ok: true };
271
+ return { envelopes: [], sessionCount: 0, filteredOut: 0, ok: true };
132
272
  }
133
273
  throw err;
134
274
  }
135
275
  const files = names.filter((name) => name.endsWith('.jsonl'));
136
276
  const envelopes = [];
137
277
  let sessionCount = 0;
278
+ let filteredOut = 0;
138
279
  for (const relativePath of files) {
139
280
  const fullPath = path.join(dir, relativePath);
140
- if (sinceMs !== undefined) {
281
+ let mtimeMs = 0;
282
+ if (sinceMs !== undefined || preFilter !== undefined) {
141
283
  let stat;
142
284
  try {
143
285
  stat = await fs.stat(fullPath);
@@ -146,9 +288,24 @@ export async function scanSessions(options = {}) {
146
288
  // Unreadable transcript — skip it rather than failing the whole scan.
147
289
  continue;
148
290
  }
291
+ mtimeMs = stat.mtimeMs;
149
292
  // Skip files unchanged since the cutoff. Use `>=` so a file touched exactly at the cutoff
150
293
  // is still examined.
151
- if (stat.mtimeMs < sinceMs) {
294
+ if (sinceMs !== undefined && mtimeMs < sinceMs) {
295
+ continue;
296
+ }
297
+ }
298
+ if (preFilter !== undefined) {
299
+ // readdir({recursive}) yields platform separators; split on both to find the project folder.
300
+ const parts = relativePath.split(/[\\/]/);
301
+ const meta = {
302
+ sessionId: path.basename(relativePath, '.jsonl'),
303
+ project: parts.length > 1 ? parts[0] : null,
304
+ mtimeMs,
305
+ source: 'claude-code',
306
+ };
307
+ if (!preFilter(meta)) {
308
+ filteredOut += 1;
152
309
  continue;
153
310
  }
154
311
  }
@@ -162,12 +319,19 @@ export async function scanSessions(options = {}) {
162
319
  }
163
320
  sessionCount += 1;
164
321
  const sessionId = path.basename(relativePath, '.jsonl');
165
- const envelope = parseTranscript(content, sessionId);
322
+ let envelope;
323
+ try {
324
+ envelope = parseTranscript(content, sessionId);
325
+ }
326
+ catch {
327
+ // A single malformed transcript must never abort the whole scan.
328
+ continue;
329
+ }
166
330
  if (envelope) {
167
331
  envelopes.push(envelope);
168
332
  }
169
333
  }
170
- return { envelopes, sessionCount, ok: true };
334
+ return { envelopes, sessionCount, filteredOut, ok: true };
171
335
  }
172
336
  /** The session id this envelope represents — used as the local dedup key. */
173
337
  export function sessionIdOf(envelope) {
@@ -1 +1 @@
1
- {"version":3,"file":"claude-scanner.js","sourceRoot":"","sources":["../../src/sessions/claude-scanner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claude-scanner.js","sourceRoot":"","sources":["../../src/sessions/claude-scanner.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { type ScanResult } from './claude-scanner.js';
2
+ import type { SessionFileMeta } from './session-filter.js';
2
3
  export declare function defaultCoworkSessionsDir(): string;
3
4
  /**
4
5
  * Scan Claude Cowork sessions stored under `~/Library/Application Support/Claude/local-agent-mode-sessions/`.
@@ -16,5 +17,6 @@ export declare function defaultCoworkSessionsDir(): string;
16
17
  export declare function scanCoworkSessions(options?: {
17
18
  sessionsDir?: string;
18
19
  sinceTime?: Date;
20
+ preFilter?: (meta: SessionFileMeta) => boolean;
19
21
  }): Promise<ScanResult>;
20
22
  //# sourceMappingURL=cowork-scanner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cowork-scanner.d.ts","sourceRoot":"","sources":["../../src/sessions/cowork-scanner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyC,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE7F,wBAAgB,wBAAwB,IAAI,MAAM,CAQjD;AAQD;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,IAAI,CAAA;CAAO,GACvD,OAAO,CAAC,UAAU,CAAC,CAoDrB"}
1
+ {"version":3,"file":"cowork-scanner.d.ts","sourceRoot":"","sources":["../../src/sessions/cowork-scanner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyC,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC7F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,wBAAgB,wBAAwB,IAAI,MAAM,CAQjD;AAQD;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,OAAO,CAAC;CAC3C,GACL,OAAO,CAAC,UAAU,CAAC,CAkErB"}
@@ -26,6 +26,7 @@ const LOCAL_AUDIT_RE = /[/\\]local_([^/\\]+)[/\\]audit\.jsonl$/;
26
26
  export async function scanCoworkSessions(options = {}) {
27
27
  const dir = options.sessionsDir ?? defaultCoworkSessionsDir();
28
28
  const sinceMs = options.sinceTime?.getTime();
29
+ const preFilter = options.preFilter;
29
30
  let names;
30
31
  try {
31
32
  names = await fs.readdir(dir, { recursive: true });
@@ -34,14 +35,23 @@ export async function scanCoworkSessions(options = {}) {
34
35
  // Treat any readdir failure (ENOENT, EACCES, ENOTDIR, …) as an empty result. Cowork scanning
35
36
  // is best-effort and macOS-only; an error here must not abort the Claude Code capture running
36
37
  // in parallel inside Promise.all.
37
- return { envelopes: [], sessionCount: 0, ok: false };
38
+ return { envelopes: [], sessionCount: 0, filteredOut: 0, ok: false };
38
39
  }
39
40
  const files = names.filter((name) => LOCAL_AUDIT_RE.test(name));
40
41
  const envelopes = [];
41
42
  let sessionCount = 0;
43
+ let filteredOut = 0;
42
44
  for (const relativePath of files) {
43
45
  const fullPath = path.join(dir, relativePath);
44
- if (sinceMs !== undefined) {
46
+ // The id lives in the path (already matched by LOCAL_AUDIT_RE above), so it is known
47
+ // before the file is read — which is what lets preFilter reject without reading.
48
+ const m = relativePath.match(LOCAL_AUDIT_RE);
49
+ if (!m) {
50
+ continue;
51
+ }
52
+ const sessionUuid = m[1];
53
+ let mtimeMs = 0;
54
+ if (sinceMs !== undefined || preFilter !== undefined) {
45
55
  let stat;
46
56
  try {
47
57
  stat = await fs.stat(fullPath);
@@ -49,7 +59,15 @@ export async function scanCoworkSessions(options = {}) {
49
59
  catch {
50
60
  continue;
51
61
  }
52
- if (stat.mtimeMs < sinceMs) {
62
+ mtimeMs = stat.mtimeMs;
63
+ if (sinceMs !== undefined && mtimeMs < sinceMs) {
64
+ continue;
65
+ }
66
+ }
67
+ if (preFilter !== undefined) {
68
+ const meta = { sessionId: sessionUuid, project: null, mtimeMs, source: 'cowork' };
69
+ if (!preFilter(meta)) {
70
+ filteredOut += 1;
53
71
  continue;
54
72
  }
55
73
  }
@@ -60,17 +78,12 @@ export async function scanCoworkSessions(options = {}) {
60
78
  catch {
61
79
  continue;
62
80
  }
63
- const m = relativePath.match(LOCAL_AUDIT_RE);
64
- if (!m) {
65
- continue;
66
- }
67
- const sessionUuid = m[1];
68
81
  sessionCount += 1;
69
82
  const envelope = parseTranscript(content, sessionUuid);
70
83
  if (envelope) {
71
84
  envelopes.push({ ...envelope, url: `cowork://session/${sessionUuid}` });
72
85
  }
73
86
  }
74
- return { envelopes, sessionCount, ok: true };
87
+ return { envelopes, sessionCount, filteredOut, ok: true };
75
88
  }
76
89
  //# sourceMappingURL=cowork-scanner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cowork-scanner.js","sourceRoot":"","sources":["../../src/sessions/cowork-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAyC,MAAM,qBAAqB,CAAC;AAE7F,MAAM,UAAU,wBAAwB;IACtC,OAAO,IAAI,CAAC,IAAI,CACd,EAAE,CAAC,OAAO,EAAE,EACZ,SAAS,EACT,qBAAqB,EACrB,QAAQ,EACR,2BAA2B,CAC5B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,cAAc,GAAG,wCAAwC,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,UAAsD,EAAE;IAExD,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,IAAI,wBAAwB,EAAE,CAAC;IAC9D,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC;IAE7C,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,6FAA6F;QAC7F,8FAA8F;QAC9F,kCAAkC;QAClC,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IACvD,CAAC;IAED,MAAM,KAAK,GAAI,KAAkB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9E,MAAM,SAAS,GAAsB,EAAE,CAAC;IACxC,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,KAAK,MAAM,YAAY,IAAI,KAAK,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAE9C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,IAAI,IAAI,CAAC;YACT,IAAI,CAAC;gBACH,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC;gBAC3B,SAAS;YACX,CAAC;QACH,CAAC;QAED,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,MAAM,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC7C,IAAI,CAAC,CAAC,EAAE,CAAC;YAAC,SAAS;QAAC,CAAC;QACrB,MAAM,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACzB,YAAY,IAAI,CAAC,CAAC;QAElB,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QACvD,IAAI,QAAQ,EAAE,CAAC;YACb,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,EAAE,oBAAoB,WAAW,EAAE,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"cowork-scanner.js","sourceRoot":"","sources":["../../src/sessions/cowork-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Scrub likely secrets out of captured session text before it leaves the machine.
3
+ *
4
+ * Session transcripts now carry tool inputs (file contents from Edit/Write) and tool outputs
5
+ * (command output, `cat .env`, printed environment), so the upload body can contain credentials.
6
+ * `logger.redact` only covers stderr and only Coolhand's own token shape, so this is a separate,
7
+ * broader scrubber applied to every message's content string in `parseTranscript`.
8
+ *
9
+ * This is best-effort defence in depth, not a guarantee: it targets common, well-shaped secret
10
+ * formats. It deliberately errs toward over-redaction (e.g. long hex strings) rather than leaking.
11
+ */
12
+ /** Return `text` with recognised secrets replaced by `[REDACTED]`. Safe on empty input. */
13
+ export declare function redactSecrets(text: string): string;
14
+ //# sourceMappingURL=redact-secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact-secrets.d.ts","sourceRoot":"","sources":["../../src/sessions/redact-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA6BH,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWlD"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Scrub likely secrets out of captured session text before it leaves the machine.
3
+ *
4
+ * Session transcripts now carry tool inputs (file contents from Edit/Write) and tool outputs
5
+ * (command output, `cat .env`, printed environment), so the upload body can contain credentials.
6
+ * `logger.redact` only covers stderr and only Coolhand's own token shape, so this is a separate,
7
+ * broader scrubber applied to every message's content string in `parseTranscript`.
8
+ *
9
+ * This is best-effort defence in depth, not a guarantee: it targets common, well-shaped secret
10
+ * formats. It deliberately errs toward over-redaction (e.g. long hex strings) rather than leaking.
11
+ */
12
+ /** Well-known token shapes replaced wholesale wherever they appear. */
13
+ const TOKEN_PATTERNS = [
14
+ /ch_pub_[A-Za-z0-9_-]{8,}/g, // Coolhand public token
15
+ /ch_priv_[A-Za-z0-9_-]{8,}/g, // Coolhand private token
16
+ /sk-[A-Za-z0-9_-]{16,}/g, // OpenAI / Anthropic style secret key
17
+ /ghp_[A-Za-z0-9]{20,}/g, // GitHub personal access token
18
+ /gho_[A-Za-z0-9]{20,}/g, // GitHub OAuth token
19
+ /github_pat_[A-Za-z0-9_]{20,}/g, // GitHub fine-grained PAT
20
+ /AKIA[0-9A-Z]{16}/g, // AWS access key id
21
+ /xox[baprs]-[A-Za-z0-9-]{10,}/g, // Slack token (bot/user/app/refresh/config)
22
+ /AIza[0-9A-Za-z_-]{35}/g, // Google API key
23
+ /sk_live_[0-9A-Za-z]{16,}/g, // Stripe live secret key
24
+ /sk_test_[0-9A-Za-z]{16,}/g, // Stripe test secret key
25
+ /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, // JWT (three base64url segments)
26
+ /\bBearer\s+[A-Za-z0-9._~+/-]{12,}=*/gi, // Authorization: Bearer <token>
27
+ /\b[A-Fa-f0-9]{40,}\b/g, // long hex blobs (generic tokens / hashes)
28
+ ];
29
+ // Two variants: a QUOTED value may contain spaces ("my secret value"); a BARE value stops at
30
+ // whitespace. Splitting them closes the gap where a spaced quoted secret was only partly redacted.
31
+ const ASSIGNMENT_QUOTED = /((?:api[_-]?key|secret|token|password|passwd|access[_-]?key|private[_-]?key)\b\s*[:=]\s*)(['"])[^'"]*\2/gi;
32
+ const ASSIGNMENT_BARE = /((?:api[_-]?key|secret|token|password|passwd|access[_-]?key|private[_-]?key)\b\s*[:=]\s*)[^\s'"]+/gi;
33
+ const REDACTED = '[REDACTED]';
34
+ /** Return `text` with recognised secrets replaced by `[REDACTED]`. Safe on empty input. */
35
+ export function redactSecrets(text) {
36
+ if (!text) {
37
+ return text;
38
+ }
39
+ let out = text
40
+ .replace(ASSIGNMENT_QUOTED, `$1$2${REDACTED}$2`)
41
+ .replace(ASSIGNMENT_BARE, `$1${REDACTED}`);
42
+ for (const pattern of TOKEN_PATTERNS) {
43
+ out = out.replace(pattern, REDACTED);
44
+ }
45
+ return out;
46
+ }
47
+ //# sourceMappingURL=redact-secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact-secrets.js","sourceRoot":"","sources":["../../src/sessions/redact-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,uEAAuE;AACvE,MAAM,cAAc,GAAsB;IACxC,2BAA2B,EAAE,wBAAwB;IACrD,4BAA4B,EAAE,yBAAyB;IACvD,wBAAwB,EAAE,sCAAsC;IAChE,uBAAuB,EAAE,+BAA+B;IACxD,uBAAuB,EAAE,qBAAqB;IAC9C,+BAA+B,EAAE,0BAA0B;IAC3D,mBAAmB,EAAE,oBAAoB;IACzC,+BAA+B,EAAE,4CAA4C;IAC7E,wBAAwB,EAAE,iBAAiB;IAC3C,2BAA2B,EAAE,yBAAyB;IACtD,2BAA2B,EAAE,yBAAyB;IACtD,sDAAsD,EAAE,iCAAiC;IACzF,uCAAuC,EAAE,gCAAgC;IACzE,uBAAuB,EAAE,2CAA2C;CACrE,CAAC;AAEF,6FAA6F;AAC7F,mGAAmG;AACnG,MAAM,iBAAiB,GACrB,2GAA2G,CAAC;AAC9G,MAAM,eAAe,GACnB,qGAAqG,CAAC;AAExG,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B,2FAA2F;AAC3F,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,GAAG,GAAG,IAAI;SACX,OAAO,CAAC,iBAAiB,EAAE,OAAO,QAAQ,IAAI,CAAC;SAC/C,OAAO,CAAC,eAAe,EAAE,KAAK,QAAQ,EAAE,CAAC,CAAC;IAC7C,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Pure, pre-read session filtering. The predicate built here runs on metadata knowable
3
+ * BEFORE a transcript is read (filename, project folder, file mtime) so excluded sessions
4
+ * are never loaded from disk — the compliance guarantee behind --project/--until.
5
+ */
6
+ /** What a scanner knows about a session file before reading its content. */
7
+ export interface SessionFileMeta {
8
+ sessionId: string;
9
+ /** Encoded project folder under ~/.claude/projects; null for Cowork sessions. */
10
+ project: string | null;
11
+ mtimeMs: number;
12
+ source: 'claude-code' | 'cowork';
13
+ }
14
+ export interface FilterCriteria {
15
+ sinceMs?: number;
16
+ untilMs?: number;
17
+ includeProjects?: string[];
18
+ excludeProjects?: string[];
19
+ }
20
+ /**
21
+ * Normalise a project name for matching: lowercase with every non-alphanumeric run
22
+ * collapsed to a single dash, so user input ("Coolhand CLI") and the encoded folder
23
+ * name ("C--Users-...-coolhand-cli") compare on the same footing.
24
+ */
25
+ export declare function sanitizeProjectKey(value: string): string;
26
+ /** Build a predicate deciding whether a session file may be read and considered for upload. */
27
+ export declare function buildSessionFilter(criteria: FilterCriteria): (meta: SessionFileMeta) => boolean;
28
+ //# sourceMappingURL=session-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-filter.d.ts","sourceRoot":"","sources":["../../src/sessions/session-filter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,aAAa,GAAG,QAAQ,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,GAAG,CAAC,IAAI,EAAE,eAAe,KAAK,OAAO,CA4B/F"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Pure, pre-read session filtering. The predicate built here runs on metadata knowable
3
+ * BEFORE a transcript is read (filename, project folder, file mtime) so excluded sessions
4
+ * are never loaded from disk — the compliance guarantee behind --project/--until.
5
+ */
6
+ /**
7
+ * Normalise a project name for matching: lowercase with every non-alphanumeric run
8
+ * collapsed to a single dash, so user input ("Coolhand CLI") and the encoded folder
9
+ * name ("C--Users-...-coolhand-cli") compare on the same footing.
10
+ */
11
+ export function sanitizeProjectKey(value) {
12
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, '-');
13
+ }
14
+ /** Build a predicate deciding whether a session file may be read and considered for upload. */
15
+ export function buildSessionFilter(criteria) {
16
+ const includes = (criteria.includeProjects ?? []).map(sanitizeProjectKey);
17
+ const excludes = (criteria.excludeProjects ?? []).map(sanitizeProjectKey);
18
+ return (meta) => {
19
+ const projectKey = meta.project === null ? null : sanitizeProjectKey(meta.project);
20
+ if (includes.length > 0) {
21
+ // A session with no project (Cowork) cannot match an include list.
22
+ if (projectKey === null || !includes.some((needle) => projectKey.includes(needle))) {
23
+ return false;
24
+ }
25
+ }
26
+ if (projectKey !== null && excludes.some((needle) => projectKey.includes(needle))) {
27
+ return false;
28
+ }
29
+ if (criteria.sinceMs !== undefined && meta.mtimeMs < criteria.sinceMs) {
30
+ return false;
31
+ }
32
+ if (criteria.untilMs !== undefined && meta.mtimeMs > criteria.untilMs) {
33
+ return false;
34
+ }
35
+ return true;
36
+ };
37
+ }
38
+ //# sourceMappingURL=session-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-filter.js","sourceRoot":"","sources":["../../src/sessions/session-filter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkBH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;AACzD,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,kBAAkB,CAAC,QAAwB;IACzD,MAAM,QAAQ,GAAG,CAAC,QAAQ,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC1E,MAAM,QAAQ,GAAG,CAAC,QAAQ,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAE1E,OAAO,CAAC,IAAqB,EAAW,EAAE;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,mEAAmE;YACnE,IAAI,UAAU,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBACnF,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,IAAI,UAAU,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YAClF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}