helixmind 0.7.0 → 0.7.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/chat.ts"],"names":[],"mappings":"AA6FA,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AA6ND,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAo/HrE"}
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/chat.ts"],"names":[],"mappings":"AA6FA,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AA+ND,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAioIrE"}
@@ -103,6 +103,7 @@ const HELP_CATEGORIES = [
103
103
  { cmd: '/context', label: '/context', description: 'Show context size & embeddings' },
104
104
  { cmd: '/compact', label: '/compact', description: 'Trigger spiral evolution' },
105
105
  { cmd: '/tokens', label: '/tokens', description: 'Show token usage & memory' },
106
+ { cmd: '/recap', label: '/recap', description: 'Toggle per-turn recap summary (on|off)' },
106
107
  ],
107
108
  },
108
109
  {
@@ -245,6 +246,7 @@ ${chalk.hex('#00ff88').bold(' Spiral Memory')}
245
246
  ${theme.primary('/context'.padEnd(22))} ${theme.dim('Show current context size & embeddings')}
246
247
  ${theme.primary('/compact'.padEnd(22))} ${theme.dim('Trigger spiral evolution (promote/demote nodes)')}
247
248
  ${theme.primary('/tokens'.padEnd(22))} ${theme.dim('Show token usage, checkpoints, memory')}
249
+ ${theme.primary('/recap [on|off]'.padEnd(22))} ${theme.dim('Toggle short summary line after each agent turn')}
248
250
 
249
251
  ${chalk.hex('#4169e1').bold(' Visualization & Brain')}
250
252
  ${theme.primary('/brain'.padEnd(22))} ${theme.dim('Show brain scope + open 3D visualization')}
@@ -508,6 +510,109 @@ export async function chatCommand(options) {
508
510
  let roundToolCalls = 0;
509
511
  let currentStepLabel = '';
510
512
  let currentStepFile = '';
513
+ // Recap tracking — per-round snapshot so we can summarise what just happened.
514
+ let roundStartTokensIn = 0;
515
+ let roundStartTokensOut = 0;
516
+ let roundStartTime = 0;
517
+ const roundToolCounts = new Map();
518
+ const roundFilesTouched = new Set();
519
+ let recapEnabled = true; // Can be toggled via /recap
520
+ function recordRoundTool(toolName, input) {
521
+ roundToolCounts.set(toolName, (roundToolCounts.get(toolName) ?? 0) + 1);
522
+ const fileTools = new Set(['read_file', 'write_file', 'edit_file']);
523
+ if (fileTools.has(toolName)) {
524
+ const p = typeof input?.path === 'string' ? input.path : null;
525
+ if (p)
526
+ roundFilesTouched.add(p);
527
+ }
528
+ }
529
+ function buildRecapLine() {
530
+ if (!recapEnabled)
531
+ return null;
532
+ const totalTools = [...roundToolCounts.values()].reduce((a, b) => a + b, 0);
533
+ const tokensIn = sessionTokensInput - roundStartTokensIn;
534
+ const tokensOut = sessionTokensOutput - roundStartTokensOut;
535
+ const elapsedMs = Date.now() - roundStartTime;
536
+ if (totalTools === 0 && tokensIn === 0 && tokensOut === 0)
537
+ return null;
538
+ const parts = [];
539
+ // Tool breakdown — compact verbs. Group reads (read_file/list_directory/
540
+ // search_files/find_files), writes (write_file/edit_file), shell, other.
541
+ const reads = (roundToolCounts.get('read_file') ?? 0)
542
+ + (roundToolCounts.get('list_directory') ?? 0)
543
+ + (roundToolCounts.get('search_files') ?? 0)
544
+ + (roundToolCounts.get('find_files') ?? 0);
545
+ const writes = (roundToolCounts.get('write_file') ?? 0)
546
+ + (roundToolCounts.get('edit_file') ?? 0);
547
+ const runs = roundToolCounts.get('run_command') ?? 0;
548
+ const gitOps = [...roundToolCounts.entries()]
549
+ .filter(([k]) => k.startsWith('git_'))
550
+ .reduce((sum, [, n]) => sum + n, 0);
551
+ const spiralOps = [...roundToolCounts.entries()]
552
+ .filter(([k]) => k.startsWith('spiral_'))
553
+ .reduce((sum, [, n]) => sum + n, 0);
554
+ const browserOps = [...roundToolCounts.entries()]
555
+ .filter(([k]) => k.startsWith('browser_'))
556
+ .reduce((sum, [, n]) => sum + n, 0);
557
+ if (reads > 0)
558
+ parts.push(`${reads} read${reads === 1 ? '' : 's'}`);
559
+ if (writes > 0) {
560
+ const fileNames = [...roundFilesTouched]
561
+ .map(p => p.split(/[\/\\]/).pop() ?? p)
562
+ .slice(0, 2);
563
+ if (writes === 1 && fileNames.length === 1) {
564
+ parts.push(`edited ${fileNames[0]}`);
565
+ }
566
+ else if (fileNames.length > 0) {
567
+ const suffix = writes > fileNames.length ? `+${writes - fileNames.length}` : '';
568
+ parts.push(`edited ${fileNames.join(', ')}${suffix}`);
569
+ }
570
+ else {
571
+ parts.push(`${writes} edit${writes === 1 ? '' : 's'}`);
572
+ }
573
+ }
574
+ if (runs > 0)
575
+ parts.push(`${runs} shell`);
576
+ if (gitOps > 0)
577
+ parts.push(`${gitOps} git`);
578
+ if (spiralOps > 0)
579
+ parts.push(`${spiralOps} spiral`);
580
+ if (browserOps > 0)
581
+ parts.push(`${browserOps} browser`);
582
+ // Other tools not caught above
583
+ const known = new Set([
584
+ 'read_file', 'list_directory', 'search_files', 'find_files',
585
+ 'write_file', 'edit_file', 'run_command',
586
+ ]);
587
+ const otherTotal = [...roundToolCounts.entries()]
588
+ .filter(([k]) => !known.has(k) && !k.startsWith('git_') && !k.startsWith('spiral_') && !k.startsWith('browser_'))
589
+ .reduce((sum, [, n]) => sum + n, 0);
590
+ if (otherTotal > 0)
591
+ parts.push(`${otherTotal} other`);
592
+ // Tokens
593
+ const totalTokens = tokensIn + tokensOut;
594
+ if (totalTokens >= 1000) {
595
+ parts.push(`${(totalTokens / 1000).toFixed(1)}k tokens`);
596
+ }
597
+ else if (totalTokens > 0) {
598
+ parts.push(`${totalTokens} tokens`);
599
+ }
600
+ // Time
601
+ if (elapsedMs >= 1000) {
602
+ const secs = Math.round(elapsedMs / 1000);
603
+ if (secs >= 60) {
604
+ const m = Math.floor(secs / 60);
605
+ const s = secs % 60;
606
+ parts.push(`${m}m${s > 0 ? ` ${s}s` : ''}`);
607
+ }
608
+ else {
609
+ parts.push(`${secs}s`);
610
+ }
611
+ }
612
+ if (parts.length === 0)
613
+ return null;
614
+ return chalk.dim(` \u258E Recap: ${parts.join(' \u00B7 ')}`);
615
+ }
511
616
  // Timer tracking
512
617
  const sessionStartTime = Date.now();
513
618
  let currentSection = null;
@@ -2956,6 +3061,24 @@ export async function chatCommand(options) {
2956
3061
  }
2957
3062
  // Persist to JSONL history (async, non-blocking)
2958
3063
  promptHistory.add(input, process.cwd()).catch(() => { });
3064
+ // Handle /recap here (toggles local recapEnabled state in the closure).
3065
+ const trimmedForRecap = input.trim();
3066
+ if (trimmedForRecap === '/recap' || trimmedForRecap.startsWith('/recap ')) {
3067
+ const arg = trimmedForRecap.split(/\s+/)[1]?.toLowerCase();
3068
+ if (arg === 'on') {
3069
+ recapEnabled = true;
3070
+ renderInfo('Recap enabled \u2014 summary line after every agent turn.');
3071
+ }
3072
+ else if (arg === 'off') {
3073
+ recapEnabled = false;
3074
+ renderInfo('Recap disabled.');
3075
+ }
3076
+ else {
3077
+ renderInfo(`Recap is ${recapEnabled ? 'ON' : 'OFF'}. Use /recap on | off to toggle.`);
3078
+ }
3079
+ showPrompt();
3080
+ return;
3081
+ }
2959
3082
  // Handle /feed directly here (needs access to inlineProgressActive flag)
2960
3083
  if (input.startsWith('/feed')) {
2961
3084
  const activeSpiral = spiralEngine ?? await ensureSpiralEngine(brainScope);
@@ -3547,6 +3670,12 @@ export async function chatCommand(options) {
3547
3670
  roundToolCalls = 0;
3548
3671
  currentStepLabel = '';
3549
3672
  currentStepFile = '';
3673
+ // Recap: snapshot round-start state.
3674
+ roundStartTokensIn = sessionTokensInput;
3675
+ roundStartTokensOut = sessionTokensOutput;
3676
+ roundStartTime = Date.now();
3677
+ roundToolCounts.clear();
3678
+ roundFilesTouched.clear();
3550
3679
  suggestionPanel.close(); // Close suggestions when agent starts
3551
3680
  agentRunning = true;
3552
3681
  chrome.promptActive = false; // Re-enable stdout hook redraws for agent output
@@ -3763,9 +3892,13 @@ export async function chatCommand(options) {
3763
3892
  await sendAgentMessage(input, agentHistory, provider, project, spiralEngine, config, permissions, undoStack, checkpointStore, agentController, activity, sessionBuffer, (inp, out) => {
3764
3893
  sessionTokensInput += inp;
3765
3894
  sessionTokensOutput += out;
3766
- }, () => {
3895
+ }, (toolName) => {
3767
3896
  sessionToolCalls++;
3768
3897
  roundToolCalls++;
3898
+ // Recap: record tool invocation by name.
3899
+ if (typeof toolName === 'string') {
3900
+ recordRoundTool(toolName, undefined);
3901
+ }
3769
3902
  }, () => {
3770
3903
  // Activity started — readline stays active for type-ahead buffering.
3771
3904
  // Muting is handled by activity.setMuteCallbacks (mute during LLM stream,
@@ -3774,11 +3907,26 @@ export async function chatCommand(options) {
3774
3907
  }, effectiveValidation, bugJournal, browserController, visionProcessor, pushScreenshotToBrainFn, getJarvisContextForPrompt(), (label, tool) => {
3775
3908
  currentStepLabel = label;
3776
3909
  const fileTools = new Set(['read_file', 'write_file', 'edit_file']);
3777
- currentStepFile = fileTools.has(tool) ? label.replace(/^(reading|writing|editing)\s+/, '') : '';
3910
+ if (fileTools.has(tool)) {
3911
+ const file = label.replace(/^(reading|writing|editing)\s+/, '');
3912
+ currentStepFile = file;
3913
+ // Recap: track files touched so the summary can name them.
3914
+ if (file && (tool === 'write_file' || tool === 'edit_file')) {
3915
+ roundFilesTouched.add(file);
3916
+ }
3917
+ }
3918
+ else {
3919
+ currentStepFile = '';
3920
+ }
3778
3921
  }, jarvisLearning);
3779
3922
  }
3780
3923
  }
3781
3924
  agentRunning = false;
3925
+ // Recap: emit a short summary of what just happened.
3926
+ const recapLine = buildRecapLine();
3927
+ if (recapLine) {
3928
+ process.stdout.write(recapLine + '\n');
3929
+ }
3782
3930
  // Keep simple message history for state persistence.
3783
3931
  // FIX: CHATFLOW-001 — also persist the assistant's reply, otherwise
3784
3932
  // saveState() writes a user-only transcript and checkpoint browser shows