replit-tools 1.2.36 → 1.2.37

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replit-tools",
3
- "version": "1.2.36",
3
+ "version": "1.2.37",
4
4
  "description": "DATA Tools - One command to set up Claude Code and Codex CLI on Replit with full persistence",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -42,39 +42,52 @@ get_recent_sessions() {
42
42
 
43
43
  const sessionData = new Map();
44
44
 
45
+ const isRealPrompt = (txt) => {
46
+ if (!txt) return false;
47
+ const t = txt.trim();
48
+ if (!t) return false;
49
+ if (/^[✅❌📦🔗⚠️🚀🎉🔧📝]/.test(t)) return false;
50
+ if (/Claude (history|binary|versions) symlink/.test(t)) return false;
51
+ if (t.startsWith('# AGENTS.md')) return false;
52
+ return true;
53
+ };
54
+
45
55
  // --- Claude sessions ---
46
56
  if (fs.existsSync(historyFile)) {
47
57
  const lines = fs.readFileSync(historyFile, 'utf8').trim().split('\n');
58
+ const entries = [];
48
59
  for (const line of lines) {
49
60
  try {
50
61
  const j = JSON.parse(line);
51
- if (!j.sessionId) continue;
52
- const key = 'claude:' + j.sessionId;
53
- if (!sessionData.has(key)) {
54
- sessionData.set(key, {
55
- tool: 'claude',
56
- id: j.sessionId,
57
- firstSeen: j.timestamp,
58
- lastSeen: j.timestamp,
59
- firstPrompt: j.display || '',
60
- lastPrompt: j.display || '',
61
- messageCount: 0
62
- });
63
- }
64
- const data = sessionData.get(key);
65
- if (j.timestamp < data.firstSeen) {
66
- data.firstSeen = j.timestamp;
67
- data.firstPrompt = j.display || data.firstPrompt;
68
- }
69
- if (j.timestamp > data.lastSeen) {
70
- data.lastSeen = j.timestamp;
71
- data.lastPrompt = j.display || data.lastPrompt;
72
- }
62
+ if (j.sessionId && j.timestamp) entries.push(j);
73
63
  } catch(e) {}
74
64
  }
65
+ entries.sort((a, b) => a.timestamp - b.timestamp);
66
+ for (const j of entries) {
67
+ const key = 'claude:' + j.sessionId;
68
+ if (!sessionData.has(key)) {
69
+ sessionData.set(key, {
70
+ tool: 'claude',
71
+ id: j.sessionId,
72
+ firstSeen: j.timestamp,
73
+ lastSeen: j.timestamp,
74
+ firstPrompt: '',
75
+ lastPrompt: '',
76
+ messageCount: 0
77
+ });
78
+ }
79
+ const data = sessionData.get(key);
80
+ if (j.timestamp < data.firstSeen) data.firstSeen = j.timestamp;
81
+ if (j.timestamp > data.lastSeen) data.lastSeen = j.timestamp;
82
+ if (isRealPrompt(j.display)) {
83
+ if (!data.firstPrompt) data.firstPrompt = j.display;
84
+ data.lastPrompt = j.display;
85
+ }
86
+ }
75
87
 
76
- for (const [key, data] of sessionData) {
88
+ for (const [key, data] of Array.from(sessionData)) {
77
89
  if (data.tool !== 'claude') continue;
90
+ if (!data.firstPrompt) { sessionData.delete(key); continue; }
78
91
  const jsonlPath = path.join(projectsDir, data.id + '.jsonl');
79
92
  const agentPath = path.join(projectsDir, 'agent-' + data.id.substring(0,7) + '.jsonl');
80
93
  let filePath = fs.existsSync(jsonlPath) ? jsonlPath : (fs.existsSync(agentPath) ? agentPath : null);
@@ -117,6 +130,22 @@ get_recent_sessions() {
117
130
  const id = meta.payload.id;
118
131
  const firstTs = Date.parse(meta.payload.timestamp || meta.timestamp);
119
132
 
133
+ const cleanUser = (raw) => {
134
+ if (!raw) return '';
135
+ let t = raw
136
+ .replace(/<environment_context>[\s\S]*?<\/environment_context>/g, '')
137
+ .replace(/<user_instructions>[\s\S]*?<\/user_instructions>/g, '')
138
+ .replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '')
139
+ .replace(/<command-name>[\s\S]*?<\/command-name>/g, '')
140
+ .replace(/<command-message>[\s\S]*?<\/command-message>/g, '')
141
+ .replace(/<command-args>[\s\S]*?<\/command-args>/g, '')
142
+ .replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g, '')
143
+ .replace(/<local-command-caveat>[\s\S]*?<\/local-command-caveat>/g, '')
144
+ .trim();
145
+ if (t.startsWith('# AGENTS.md')) return '';
146
+ return t;
147
+ };
148
+
120
149
  let lastTs = firstTs;
121
150
  let firstPrompt = '';
122
151
  let lastPrompt = '';
@@ -126,8 +155,9 @@ get_recent_sessions() {
126
155
  const j = JSON.parse(ln);
127
156
  if (j.timestamp) lastTs = Math.max(lastTs, Date.parse(j.timestamp));
128
157
  if (j.type === 'response_item' && j.payload && j.payload.role === 'user' && Array.isArray(j.payload.content)) {
129
- const text = (j.payload.content.find(c => c.type === 'input_text') || {}).text || '';
130
- if (text && !text.startsWith('<user_instructions>') && !text.startsWith('<environment_context>')) {
158
+ const raw = (j.payload.content.find(c => c.type === 'input_text') || {}).text || '';
159
+ const text = cleanUser(raw);
160
+ if (text) {
131
161
  if (!firstPrompt) firstPrompt = text;
132
162
  lastPrompt = text;
133
163
  msgCount++;
@@ -136,6 +166,9 @@ get_recent_sessions() {
136
166
  } catch(e) {}
137
167
  }
138
168
 
169
+ // Skip sessions with no real user input (sub-agents, programmatic runs)
170
+ if (msgCount === 0 || !firstPrompt) continue;
171
+
139
172
  const stat = fs.statSync(f);
140
173
  sessionData.set('codex:' + id, {
141
174
  tool: 'codex',
@@ -293,23 +326,46 @@ get_recent_24h_sessions() {
293
326
  const cwd = '/home/runner/workspace';
294
327
  const sessions = new Map();
295
328
 
329
+ // Heuristic: skip captured shell output / non-real prompts
330
+ const isRealPrompt = (txt) => {
331
+ if (!txt) return false;
332
+ const t = txt.trim();
333
+ if (!t) return false;
334
+ // Skip messages that are obviously captured shell output
335
+ if (/^[✅❌📦🔗⚠️🚀🎉🔧📝]/.test(t)) return false;
336
+ if (/Claude (history|binary|versions) symlink/.test(t)) return false;
337
+ if (t.startsWith('# AGENTS.md')) return false;
338
+ return true;
339
+ };
340
+
296
341
  // Claude
297
342
  const historyFile = '${history}';
298
343
  if (fs.existsSync(historyFile)) {
299
344
  const lines = fs.readFileSync(historyFile, 'utf8').trim().split('\n');
345
+ // Sort by timestamp ascending so we can pick first real prompt per session
346
+ const entries = [];
300
347
  for (const line of lines) {
301
348
  try {
302
349
  const j = JSON.parse(line);
303
- if (!j.sessionId || !j.timestamp) continue;
304
- const key = 'claude:' + j.sessionId;
305
- if (!sessions.has(key)) {
306
- sessions.set(key, { tool: 'claude', id: j.sessionId, firstSeen: j.timestamp, lastSeen: j.timestamp, firstPrompt: j.display || '' });
307
- }
308
- const s = sessions.get(key);
309
- if (j.timestamp < s.firstSeen) { s.firstSeen = j.timestamp; s.firstPrompt = j.display || s.firstPrompt; }
310
- if (j.timestamp > s.lastSeen) { s.lastSeen = j.timestamp; }
350
+ if (j.sessionId && j.timestamp) entries.push(j);
311
351
  } catch(e) {}
312
352
  }
353
+ entries.sort((a, b) => a.timestamp - b.timestamp);
354
+ for (const j of entries) {
355
+ const key = 'claude:' + j.sessionId;
356
+ if (!sessions.has(key)) {
357
+ sessions.set(key, { tool: 'claude', id: j.sessionId, firstSeen: j.timestamp, lastSeen: j.timestamp, firstPrompt: '' });
358
+ }
359
+ const s = sessions.get(key);
360
+ if (j.timestamp < s.firstSeen) s.firstSeen = j.timestamp;
361
+ if (j.timestamp > s.lastSeen) s.lastSeen = j.timestamp;
362
+ // Set firstPrompt only on first real user prompt encountered
363
+ if (!s.firstPrompt && isRealPrompt(j.display)) s.firstPrompt = j.display;
364
+ }
365
+ // Drop sessions with no real user prompt
366
+ for (const [key, s] of Array.from(sessions)) {
367
+ if (s.tool === 'claude' && !s.firstPrompt) sessions.delete(key);
368
+ }
313
369
  }
314
370
 
315
371
  // Codex
@@ -338,20 +394,43 @@ get_recent_24h_sessions() {
338
394
  if (meta.payload.cwd !== cwd) continue;
339
395
  const id = meta.payload.id;
340
396
  const firstTs = Date.parse(meta.payload.timestamp || meta.timestamp);
397
+
398
+ const cleanUser = (raw) => {
399
+ if (!raw) return '';
400
+ let t = raw
401
+ .replace(/<environment_context>[\s\S]*?<\/environment_context>/g, '')
402
+ .replace(/<user_instructions>[\s\S]*?<\/user_instructions>/g, '')
403
+ .replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '')
404
+ .replace(/<command-name>[\s\S]*?<\/command-name>/g, '')
405
+ .replace(/<command-message>[\s\S]*?<\/command-message>/g, '')
406
+ .replace(/<command-args>[\s\S]*?<\/command-args>/g, '')
407
+ .replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g, '')
408
+ .replace(/<local-command-caveat>[\s\S]*?<\/local-command-caveat>/g, '')
409
+ .trim();
410
+ if (t.startsWith('# AGENTS.md')) return '';
411
+ return t;
412
+ };
413
+
341
414
  let lastTs = firstTs;
342
415
  let firstPrompt = '';
416
+ let realMsgCount = 0;
343
417
  for (const ln of lns) {
344
418
  try {
345
419
  const j = JSON.parse(ln);
346
420
  if (j.timestamp) lastTs = Math.max(lastTs, Date.parse(j.timestamp));
347
- if (!firstPrompt && j.type === 'response_item' && j.payload && j.payload.role === 'user' && Array.isArray(j.payload.content)) {
348
- const text = (j.payload.content.find(c => c.type === 'input_text') || {}).text || '';
349
- if (text && !text.startsWith('<user_instructions>') && !text.startsWith('<environment_context>')) {
350
- firstPrompt = text;
421
+ if (j.type === 'response_item' && j.payload && j.payload.role === 'user' && Array.isArray(j.payload.content)) {
422
+ const text = cleanUser((j.payload.content.find(c => c.type === 'input_text') || {}).text || '');
423
+ if (text) {
424
+ if (!firstPrompt) firstPrompt = text;
425
+ realMsgCount++;
351
426
  }
352
427
  }
353
428
  } catch(e) {}
354
429
  }
430
+
431
+ // Skip sub-agent / programmatic sessions with no real user prompt
432
+ if (realMsgCount === 0 || !firstPrompt) continue;
433
+
355
434
  sessions.set('codex:' + id, { tool: 'codex', id, firstSeen: firstTs, lastSeen: lastTs, firstPrompt });
356
435
  } catch(e) {}
357
436
  }