@gabrihhh/jarvis 2.4.0 → 2.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gabrihhh/jarvis",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Claude Code terminal dashboard + semantic memory graph via Neo4j",
5
5
  "bin": {
6
6
  "jarvis": "bin/jarvis.js",
package/src/display.js CHANGED
@@ -60,29 +60,13 @@ function tokenRow(label, stats, maxTokens) {
60
60
  return row(text);
61
61
  }
62
62
 
63
- // ─── Context window row ──────────────────────────────────────
64
- function contextRow(session) {
65
- if (!session) {
66
- return row(` ${chalk.hex(DIM)('No active session detected')}`);
67
- }
68
- const { contextUsed, contextWindow, percent, model, turns } = session;
69
- const b = bar(percent, 24);
70
- const pct = chalk.bold.hex(WHITE)(`${percent}%`);
71
- const info = chalk.hex(WHITE)(`${formatTokens(contextUsed)} / ${formatTokens(contextWindow)}`);
72
- const meta = chalk.hex(DIM)(`${turns} turn${turns !== 1 ? 's' : ''} · model: ${model.replace('claude-', '')}`);
73
- return [
74
- row(` ${b} ${pct} ${info}`),
75
- row(` ${meta}`),
76
- ].join('\n');
77
- }
78
-
79
63
  // ─── Section header ──────────────────────────────────────────
80
64
  function sectionHeader(icon, label) {
81
65
  return row(` ${chalk.hex(WHITE)(icon)} ${chalk.bold.hex(WHITE)(label)}`);
82
66
  }
83
67
 
84
68
  // ─── Full render ─────────────────────────────────────────────
85
- export function render(stats, session) {
69
+ export function render(stats) {
86
70
  const { monthly, weekly, daily } = stats;
87
71
  const maxTokens = monthly.total || 1;
88
72
 
@@ -108,12 +92,6 @@ export function render(stats, session) {
108
92
  row(''),
109
93
  DIV,
110
94
  row(''),
111
- sectionHeader('⬡', 'Context Window (current session)'),
112
- row(''),
113
- contextRow(session),
114
- row(''),
115
- DIV,
116
- row(''),
117
95
  breakdown(monthly),
118
96
  row(''),
119
97
  BOT,
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { readAllUsage, getCurrentSessionFile, readCurrentSessionUsage } from './reader.js';
2
- import { aggregateStats, aggregateSession } from './calculator.js';
1
+ import { readAllUsage } from './reader.js';
2
+ import { aggregateStats } from './calculator.js';
3
3
  import { render, renderError, renderLoading } from './display.js';
4
4
 
5
5
  export async function run() {
@@ -15,12 +15,6 @@ export async function run() {
15
15
 
16
16
  const stats = aggregateStats(allEntries);
17
17
 
18
- // current session
19
- const sessionMeta = getCurrentSessionFile();
20
- const sessionId = sessionMeta?.sessionId;
21
- const sessionEntries = sessionId ? readCurrentSessionUsage(sessionId) : [];
22
- const session = aggregateSession(sessionEntries);
23
-
24
18
  process.stdout.write('\r' + ' '.repeat(40) + '\r');
25
- console.log('\n' + render(stats, session) + '\n');
19
+ console.log('\n' + render(stats) + '\n');
26
20
  }
package/src/reader.js CHANGED
@@ -3,6 +3,34 @@ import { join } from 'path';
3
3
  import { homedir } from 'os';
4
4
  import { execSync } from 'child_process';
5
5
 
6
+ function getParentPid(pid) {
7
+ // Linux — leitura direta do /proc, sem subprocess
8
+ try {
9
+ const stat = readFileSync(`/proc/${pid}/stat`, 'utf8');
10
+ const match = stat.match(/^\d+ \(.*?\) \S+ (\d+)/);
11
+ if (match) return parseInt(match[1]);
12
+ } catch { /* não é Linux ou /proc indisponível */ }
13
+
14
+ // macOS / Linux fallback
15
+ if (process.platform !== 'win32') {
16
+ try {
17
+ const out = execSync(`ps -o ppid= -p ${pid}`, { stdio: 'pipe' }).toString().trim();
18
+ const n = parseInt(out);
19
+ return isNaN(n) ? null : n;
20
+ } catch { return null; }
21
+ }
22
+
23
+ // Windows — PowerShell com Get-CimInstance (não deprecated, Windows 7+)
24
+ try {
25
+ const out = execSync(
26
+ `powershell -NoProfile -Command "(Get-CimInstance Win32_Process -Filter 'ProcessId=${pid}').ParentProcessId"`,
27
+ { stdio: 'pipe' }
28
+ ).toString().trim();
29
+ const n = parseInt(out);
30
+ return isNaN(n) ? null : n;
31
+ } catch { return null; }
32
+ }
33
+
6
34
  const CLAUDE_DIR = join(homedir(), '.claude');
7
35
  const PROJECTS_DIR = join(CLAUDE_DIR, 'projects');
8
36
 
@@ -95,24 +123,16 @@ export function getCurrentSessionFile() {
95
123
  if (!existsSync(sessionsDir)) return null;
96
124
 
97
125
  try {
98
- // Try to match by parent PID chain: Claude spawns the status line command,
99
- // so process.ppid (or its parent) should match a session file named <pid>.json
100
- const pidsToCheck = new Set([process.ppid]);
101
-
102
- // also check grandparent in case the command runs through a shell
103
- try {
104
- const grandparent = parseInt(
105
- execSync(`ps -o ppid= -p ${process.ppid}`, { stdio: ['pipe','pipe','pipe'] }).toString().trim()
106
- );
107
- if (grandparent) pidsToCheck.add(grandparent);
108
- } catch { /* skip */ }
109
-
110
- for (const pid of pidsToCheck) {
126
+ // Sobe a árvore de processos até 5 níveis para encontrar o .json da sessão Claude
127
+ let pid = process.ppid;
128
+ for (let i = 0; i < 5; i++) {
129
+ if (!pid || pid <= 1) break;
111
130
  const candidate = join(sessionsDir, `${pid}.json`);
112
131
  if (existsSync(candidate)) {
113
132
  try { return JSON.parse(readFileSync(candidate, 'utf-8')); }
114
- catch { /* skip */ }
133
+ catch { break; }
115
134
  }
135
+ pid = getParentPid(pid);
116
136
  }
117
137
 
118
138
  // Fallback: find a session whose sessionId appears in JSONL files
package/src/statusline.js CHANGED
@@ -57,8 +57,6 @@ function isMemoryLoaded(sessionId) {
57
57
 
58
58
  export function renderLine() {
59
59
  const mode = readTriggerMode();
60
- const triggerInner = ` TRIGGER ${mode.toUpperCase()} `;
61
- const triggerBox = buildBox(triggerInner, PINK);
62
60
 
63
61
  const sessionMeta = getCurrentSessionFile();
64
62
  const sessionId = sessionMeta?.sessionId;
@@ -67,9 +65,12 @@ export function renderLine() {
67
65
 
68
66
  const allEntries = readAllUsage();
69
67
 
70
- const boxes = (contextBox) => loadedBox
71
- ? joinBoxes(contextBox, triggerBox, loadedBox)
72
- : joinBoxes(contextBox, triggerBox);
68
+ const boxes = (contextBox) => {
69
+ const toJoin = [contextBox];
70
+ if (mode !== 'off') toJoin.push(buildBox(` TRIGGER ${mode.toUpperCase()} `, PINK));
71
+ if (loadedBox) toJoin.push(loadedBox);
72
+ return joinBoxes(...toJoin);
73
+ };
73
74
 
74
75
  if (!allEntries.length) {
75
76
  const contextBox = buildBox(` CONTEXT ${'░'.repeat(8)} 0% `, CYAN);