@gabrihhh/jarvis 2.6.0 → 2.6.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.
package/README.md CHANGED
@@ -31,9 +31,13 @@ Reinicie o Claude Code após o setup.
31
31
  | `jarvis --trigger prompt` | Hook de memória roda a cada prompt |
32
32
  | `jarvis --trigger off` | Desativa o carregamento automático de memória |
33
33
  | `jarvis --theme` | Mostra o tema atual da status bar |
34
- | `jarvis --theme <name>:<#hex>` | Define a cor de um box (`context`, `trigger`, `memory`) |
34
+ | `jarvis --theme <name>:<#hex>` | Define a cor de um box (`context`, `trigger`, `memory`, `tokens`) |
35
35
  | `jarvis --theme <name>:reset` | Reseta a cor de um box para o padrão |
36
36
  | `jarvis --theme reset` | Reseta todas as cores para o padrão |
37
+ | `jarvis --token` | Mostra o modo de exibição de tokens atual |
38
+ | `jarvis --token on` | Ativa box com total de tokens do último turno (`◈`) |
39
+ | `jarvis --token complete` | Ativa box `◈` + linha de breakdown abaixo da status bar |
40
+ | `jarvis --token off` | Desativa exibição de tokens |
37
41
  | `jarvis --line` | Saída de uma linha usada internamente pela status bar |
38
42
  | `jarvis --help` | Lista todos os comandos |
39
43
 
@@ -79,3 +83,18 @@ Reinicie o Claude Code após o setup.
79
83
  │ CONTEXT ████░░░░ 52% │
80
84
  ╰──────────────────────╯
81
85
  ```
86
+
87
+ **Status bar com `--token on`** (box com total de tokens do último turno):
88
+ ```
89
+ ╭──────────────────────╮╭──────────╮
90
+ │ CONTEXT ████░░░░ 52% ││ ◈ 50.0K │
91
+ ╰──────────────────────╯╰──────────╯
92
+ ```
93
+
94
+ **Status bar com `--token complete`** (box + breakdown detalhado):
95
+ ```
96
+ ╭──────────────────────╮╭──────────╮
97
+ │ CONTEXT ████░░░░ 52% ││ ◈ 50.0K │
98
+ ╰──────────────────────╯╰──────────╯
99
+ INPUT 800 │ HISTORY 45.0K │ CACHE 1.2K │ RESPONSE 3.2K
100
+ ```
package/bin/jarvis.js CHANGED
@@ -78,9 +78,13 @@ if (args.includes('--help') || args.includes('-h')) {
78
78
  jarvis --trigger prompt Hook runs on every prompt
79
79
  jarvis --trigger off Disable automatic memory loading
80
80
  jarvis --theme Show current statusline theme
81
- jarvis --theme <name>:<hex> Set a box color (context, trigger, memory)
81
+ jarvis --theme <name>:<hex> Set a box color (context, trigger, memory, tokens)
82
82
  jarvis --theme <name>:reset Reset a single box to default color
83
83
  jarvis --theme reset Reset all colors to default
84
+ jarvis --token Show current token display mode
85
+ jarvis --token on Show total tokens box (◈)
86
+ jarvis --token complete Show token box + INPUT/HISTORY/CACHE/RESPONSE breakdown
87
+ jarvis --token off Disable token display
84
88
  jarvis --help Show this help
85
89
 
86
90
  Slash commands (inside Claude Code):
@@ -253,6 +257,25 @@ if (args.includes('--graph')) {
253
257
  writeTheme(theme);
254
258
  console.log(`\n ✓ ${name} set to ${chalk.hex(color).bold(color)}\n`);
255
259
  process.exit(0);
260
+ } else if (args.includes('--token')) {
261
+ const value = args[args.indexOf('--token') + 1];
262
+ const validValues = ['on', 'off', 'complete'];
263
+
264
+ if (!value || !validValues.includes(value)) {
265
+ const cfg = loadMemoryConfig();
266
+ const current = cfg.tokenDisplay || 'off';
267
+ console.log(`\n Token display: ${current}\n`);
268
+ console.log(` Usage: jarvis --token <on|complete|off>\n`);
269
+ process.exit(0);
270
+ }
271
+
272
+ const cfg = loadMemoryConfig();
273
+ cfg.tokenDisplay = value === 'on' ? 'simple' : value;
274
+ saveMemoryConfig(cfg);
275
+
276
+ const labels = { simple: 'on (◈ total tokens)', off: 'off', complete: 'complete (breakdown)' };
277
+ console.log(`\n ✓ Token display set to: ${labels[cfg.tokenDisplay]}\n`);
278
+ process.exit(0);
256
279
  } else if (args.includes('--query')) {
257
280
  try {
258
281
  const { queryByPath } = await import('../src/memory/query-by-path.js');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gabrihhh/jarvis",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "description": "Claude Code terminal dashboard + semantic memory graph via Neo4j",
5
5
  "bin": {
6
6
  "jarvis": "bin/jarvis.js",
package/src/calculator.js CHANGED
@@ -109,6 +109,18 @@ export function formatTokens(n) {
109
109
  return `${n}`;
110
110
  }
111
111
 
112
+ export function getLastTurnTokens(entries) {
113
+ if (!entries.length) return null;
114
+ const last = entries[entries.length - 1];
115
+ return {
116
+ input: last.inputTokens,
117
+ history: last.cacheReadTokens,
118
+ cache: last.cacheWriteTokens,
119
+ response: last.outputTokens,
120
+ total: last.inputTokens + last.outputTokens + last.cacheReadTokens + last.cacheWriteTokens,
121
+ };
122
+ }
123
+
112
124
  export function formatCost(n) {
113
125
  if (n >= 1) return `$${n.toFixed(2)}`;
114
126
  if (n >= 0.01) return `$${n.toFixed(3)}`;
package/src/reader.js CHANGED
@@ -135,29 +135,7 @@ export function getCurrentSessionFile() {
135
135
  pid = getParentPid(pid);
136
136
  }
137
137
 
138
- // Fallback: find a session whose sessionId appears in JSONL files
139
- const sessionFiles = readdirSync(sessionsDir)
140
- .map(f => {
141
- try { return JSON.parse(readFileSync(join(sessionsDir, f), 'utf-8')); }
142
- catch { return null; }
143
- })
144
- .filter(Boolean);
145
-
146
- if (!sessionFiles.length) return null;
147
-
148
- const allFiles = getAllSessionFiles();
149
- const sessionIdSet = new Set();
150
- for (const file of allFiles) {
151
- try {
152
- const lines = readFileSync(file, 'utf-8').split('\n').filter(l => l.trim());
153
- for (const line of lines.slice(-5)) {
154
- try { const obj = JSON.parse(line); if (obj.sessionId) sessionIdSet.add(obj.sessionId); }
155
- catch { /* skip */ }
156
- }
157
- } catch { /* skip */ }
158
- }
159
-
160
- return sessionFiles.find(s => sessionIdSet.has(s.sessionId)) || sessionFiles[0];
138
+ return null;
161
139
  } catch {
162
140
  return null;
163
141
  }
package/src/statusline.js CHANGED
@@ -3,7 +3,7 @@ import { readFileSync, existsSync, unlinkSync } from 'fs';
3
3
  import { join } from 'path';
4
4
  import { homedir, tmpdir } from 'os';
5
5
  import { readAllUsage, getCurrentSessionFile, readCurrentSessionUsage } from './reader.js';
6
- import { aggregateStats, aggregateSession } from './calculator.js';
6
+ import { aggregateStats, aggregateSession, getLastTurnTokens, formatTokens } from './calculator.js';
7
7
  import { readTheme } from './theme.js';
8
8
 
9
9
  const chalk = new Chalk({ level: 3 });
@@ -21,6 +21,13 @@ function readTriggerMode() {
21
21
  } catch { return 'off'; }
22
22
  }
23
23
 
24
+ function readTokenMode() {
25
+ try {
26
+ const cfg = JSON.parse(readFileSync(join(homedir(), '.claude-memory.json'), 'utf8'));
27
+ return cfg.tokenDisplay || 'off';
28
+ } catch { return 'off'; }
29
+ }
30
+
24
31
  function buildBox(inner, color, width = inner.length) {
25
32
  return [
26
33
  chalk.hex(color).bold(`╭${'─'.repeat(width)}╮`),
@@ -54,6 +61,7 @@ function isMemoryLoaded(sessionId) {
54
61
 
55
62
  export function renderLine() {
56
63
  const mode = readTriggerMode();
64
+ const tokenMode = readTokenMode();
57
65
  const theme = readTheme();
58
66
 
59
67
  const sessionMeta = getCurrentSessionFile();
@@ -63,28 +71,44 @@ export function renderLine() {
63
71
 
64
72
  const allEntries = readAllUsage();
65
73
 
66
- const boxes = (contextBox) => {
74
+ const buildOutput = (contextBox, turnTokens) => {
67
75
  const toJoin = [contextBox];
68
76
  if (mode !== 'off') toJoin.push(buildBox(` TRIGGER ${mode.toUpperCase()} `, theme.trigger));
69
77
  if (loadedBox) toJoin.push(loadedBox);
70
- return joinBoxes(...toJoin);
78
+ if (turnTokens) toJoin.push(buildBox(` ◈ ${formatTokens(turnTokens.total)} `, theme.tokens));
79
+
80
+ let out = joinBoxes(...toJoin);
81
+
82
+ if (tokenMode === 'complete' && turnTokens) {
83
+ const col = (s) => chalk.hex(theme.tokens).bold(s);
84
+ const parts = [
85
+ `INPUT ${formatTokens(turnTokens.input)}`,
86
+ `HISTORY ${formatTokens(turnTokens.history)}`,
87
+ `CACHE ${formatTokens(turnTokens.cache)}`,
88
+ `RESPONSE ${formatTokens(turnTokens.response)}`,
89
+ ];
90
+ out += '\n' + parts.map(col).join(col(' │ '));
91
+ }
92
+
93
+ return out;
71
94
  };
72
95
 
73
96
  if (!allEntries.length) {
74
97
  const contextBox = buildBox(` CONTEXT ${'░'.repeat(8)} 0% `, theme.context);
75
- process.stdout.write(boxes(contextBox));
98
+ process.stdout.write(buildOutput(contextBox, null));
76
99
  return;
77
100
  }
78
101
 
79
102
  const sessionEntries = sessionId ? readCurrentSessionUsage(sessionId) : [];
80
103
  const session = aggregateSession(sessionEntries);
104
+ const turnTokens = tokenMode !== 'off' ? getLastTurnTokens(sessionEntries) : null;
81
105
 
82
106
  if (!session) {
83
107
  const contextBox = buildBox(` CONTEXT ${'░'.repeat(8)} 0% `, theme.context);
84
- process.stdout.write(boxes(contextBox));
108
+ process.stdout.write(buildOutput(contextBox, turnTokens));
85
109
  return;
86
110
  }
87
111
 
88
112
  const contextBox = buildBox(` CONTEXT ${bar(session.percent)} ${session.percent}% `, theme.context);
89
- process.stdout.write(boxes(contextBox));
113
+ process.stdout.write(buildOutput(contextBox, turnTokens));
90
114
  }
package/src/theme.js CHANGED
@@ -8,6 +8,7 @@ export const DEFAULT_COLORS = {
8
8
  context: '#22d3ee',
9
9
  trigger: '#f472b6',
10
10
  memory: '#4ade80',
11
+ tokens: '#ef4444',
11
12
  };
12
13
 
13
14
  export const VALID_NAMES = Object.keys(DEFAULT_COLORS);