@monoes/monomindcli 1.10.38 → 1.10.40

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.
@@ -372,6 +372,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
372
372
  const id = f.replace('.jsonl', '');
373
373
  let lastPrompt = '', summaries = [], totalDurationMs = 0, totalMessages = 0, firstTs = null, lastTs = null, totalCost = 0, toolCalls = 0, userMessages = 0, cacheReadTokens = 0, totalInputTokens = 0;
374
374
  const modelBreakdown = {};
375
+ const filesTouchedSet = new Set();
375
376
  try {
376
377
  const lines = fs.readFileSync(fp, 'utf8').split('\n').filter(Boolean);
377
378
  let pendingCompact = false;
@@ -394,7 +395,12 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
394
395
  if (e.type === 'assistant') {
395
396
  const msg = e.message || {};
396
397
  for (const block of (msg.content || [])) {
397
- if (block && block.type === 'tool_use') toolCalls++;
398
+ if (block && block.type === 'tool_use') {
399
+ toolCalls++;
400
+ if (['Write','Edit','Read','MultiEdit'].includes(block.name) && block.input?.file_path) {
401
+ filesTouchedSet.add(path.basename(block.input.file_path));
402
+ }
403
+ }
398
404
  }
399
405
  if (msg.usage && msg.model) {
400
406
  const c = _sjCalcCost(msg.model, msg.usage);
@@ -415,7 +421,8 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
415
421
  }
416
422
  }
417
423
  } catch {}
418
- sessions.push({ id, mtime, firstTs, lastTs, lastPrompt, summaries, totalDurationMs, totalMessages, totalCost, toolCalls, userMessages, cacheReadTokens, totalInputTokens, modelBreakdown, file: fp });
424
+ const filesTouched = [...filesTouchedSet].slice(0, 20);
425
+ sessions.push({ id, mtime, firstTs, lastTs, lastPrompt, summaries, totalDurationMs, totalMessages, totalCost, toolCalls, userMessages, cacheReadTokens, totalInputTokens, modelBreakdown, filesTouched, file: fp });
419
426
  }
420
427
  res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'no-cache' });
421
428
  res.end(JSON.stringify({ sessions }));
@@ -978,6 +985,98 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
978
985
  return;
979
986
  }
980
987
 
988
+ // ---------------------------------------------------------- POST /api/loops/create
989
+ if (req.method === 'POST' && url === '/api/loops/create') {
990
+ let body = '';
991
+ req.on('data', chunk => { body += chunk; });
992
+ req.on('end', () => {
993
+ try {
994
+ const { name, prompt, interval, maxReps } = JSON.parse(body);
995
+ if (!prompt) { res.writeHead(400); res.end(JSON.stringify({ error: 'prompt required' })); return; }
996
+ const loopsDir = path.join(projectDir || process.cwd(), '.monomind', 'loops');
997
+ fs.mkdirSync(loopsDir, { recursive: true });
998
+ const id = `loop-${Date.now()}-${Math.random().toString(36).slice(2,7)}`;
999
+ const loop = { id, name: name || prompt.slice(0, 40), prompt, interval: interval || '1h', maxReps: maxReps || null, status: 'active', currentRep: 0, startedAt: new Date().toISOString(), lastRunAt: null };
1000
+ fs.writeFileSync(path.join(loopsDir, `${id}.json`), JSON.stringify(loop, null, 2));
1001
+ res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
1002
+ res.end(JSON.stringify({ ok: true, id }));
1003
+ } catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: err.message })); }
1004
+ });
1005
+ return;
1006
+ }
1007
+
1008
+ // ---------------------------------------------------------- GET /api/session-errors
1009
+ if (req.method === 'GET' && url === '/api/session-errors') {
1010
+ const qs = new URL(req.url, 'http://localhost').searchParams;
1011
+ const d = path.resolve(qs.get('dir') || projectDir || process.cwd());
1012
+ const sessionId = qs.get('id') || '';
1013
+ const slug = d.replace(/\//g, '-');
1014
+ const projectClaudeDir = path.join(os.homedir(), '.claude', 'projects', slug);
1015
+ try {
1016
+ const files = fs.readdirSync(projectClaudeDir).filter(f => f.endsWith('.jsonl'));
1017
+ let fp = null;
1018
+ // Find the file matching sessionId
1019
+ for (const f of files) {
1020
+ if (f.includes(sessionId) || sessionId === f.replace('.jsonl', '')) { fp = path.join(projectClaudeDir, f); break; }
1021
+ }
1022
+ if (!fp) {
1023
+ // fallback: find by scanning
1024
+ for (const f of files) {
1025
+ const raw = fs.readFileSync(path.join(projectClaudeDir, f), 'utf8');
1026
+ const lines = raw.trim().split('\n').filter(Boolean);
1027
+ if (lines.length > 0) {
1028
+ try { const first = JSON.parse(lines[0]); if (first.sessionId === sessionId) { fp = path.join(projectClaudeDir, f); break; } } catch {}
1029
+ }
1030
+ }
1031
+ }
1032
+ if (!fp) { res.writeHead(404); res.end(JSON.stringify({ errors: [] })); return; }
1033
+ const raw = fs.readFileSync(fp, 'utf8');
1034
+ const lines = raw.trim().split('\n').filter(Boolean);
1035
+ const errors = [];
1036
+ for (const line of lines) {
1037
+ try {
1038
+ const obj = JSON.parse(line);
1039
+ const content = obj.message?.content;
1040
+ if (!Array.isArray(content)) continue;
1041
+ for (const block of content) {
1042
+ if (block.type === 'tool_result' && block.is_error) {
1043
+ const errText = Array.isArray(block.content) ? block.content.map(c => c.text || '').join('') : String(block.content || '');
1044
+ if (errText) errors.push({ toolUseId: block.tool_use_id || '', text: errText.slice(0, 500) });
1045
+ }
1046
+ }
1047
+ } catch {}
1048
+ }
1049
+ res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
1050
+ res.end(JSON.stringify({ errors: errors.slice(0, 50) }));
1051
+ } catch (err) { res.writeHead(500); res.end(JSON.stringify({ errors: [], error: err.message })); }
1052
+ return;
1053
+ }
1054
+
1055
+ // ---------------------------------------------------------- GET /api/events-stream (SSE)
1056
+ if (req.method === 'GET' && url.startsWith('/api/events-stream')) {
1057
+ const qs = new URL(req.url, 'http://localhost').searchParams;
1058
+ const d = path.resolve(qs.get('dir') || projectDir || process.cwd());
1059
+ const slug = d.replace(/\//g, '-');
1060
+ const projectClaudeDir = path.join(os.homedir(), '.claude', 'projects', slug);
1061
+ res.writeHead(200, {
1062
+ 'Content-Type': 'text/event-stream',
1063
+ 'Cache-Control': 'no-cache',
1064
+ 'Connection': 'keep-alive',
1065
+ 'Access-Control-Allow-Origin': '*',
1066
+ });
1067
+ const send = (ev, data) => { try { res.write(`event: ${ev}\ndata: ${JSON.stringify(data)}\n\n`); } catch {} };
1068
+ send('connected', { ts: Date.now() });
1069
+ let watcher = null;
1070
+ try {
1071
+ watcher = fs.watch(projectClaudeDir, { persistent: false }, (evtype) => {
1072
+ if (evtype === 'change' || evtype === 'rename') send('update', { ts: Date.now() });
1073
+ });
1074
+ } catch {}
1075
+ const pingInterval = setInterval(() => { try { res.write(': ping\n\n'); } catch {} }, 20000);
1076
+ req.on('close', () => { clearInterval(pingInterval); try { watcher?.close(); } catch {} });
1077
+ return;
1078
+ }
1079
+
981
1080
  // ------------------------------------------------------- DELETE /api/knowledge-chunk
982
1081
  if (req.method === 'DELETE' && url === '/api/knowledge-chunk') {
983
1082
  let body = '';