@statforge/claudestat 1.2.2 → 1.2.3

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
@@ -468,7 +468,7 @@ claudestat includes an MCP (Model Context Protocol) server that lets Claude Code
468
468
  ### Register with Claude Code
469
469
 
470
470
  ```bash
471
- claude mcp add --transport stdio claudestat -- claudestat-mcp
471
+ claude mcp add claudestat -s user -- claudestat-mcp
472
472
  ```
473
473
 
474
474
  Once registered, ask Claude things like:
package/dist/doctor.js CHANGED
@@ -17,11 +17,9 @@ async function runDoctor() {
17
17
  const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
18
18
  checks.push({
19
19
  label: `Node.js version (${process.versions.node})`,
20
- ok: nodeMajor >= 18,
21
- note: nodeMajor >= 22 ? 'node:sqlite supported ✓'
22
- : nodeMajor >= 18 ? 'Works Node 22+ recommended for native node:sqlite'
23
- : undefined,
24
- fix: nodeMajor < 18 ? 'Install Node.js 18 or later: https://nodejs.org' : undefined,
20
+ ok: nodeMajor >= 22,
21
+ note: nodeMajor >= 22 ? 'node:sqlite supported ✓' : undefined,
22
+ fix: nodeMajor < 22 ? 'Install Node.js 22 or later: https://nodejs.org' : undefined,
25
23
  });
26
24
  // 2. Claude Code installed
27
25
  const claudeOk = (() => { try {
@@ -98,7 +96,7 @@ async function runDoctor() {
98
96
  label: 'Global CLI symlink valid',
99
97
  ok: symlinkOk,
100
98
  note: symlinkNote,
101
- fix: symlinkOk ? undefined : 'npm install -g @deibygs/claudestat',
99
+ fix: symlinkOk ? undefined : 'npm install -g @statforge/claudestat',
102
100
  });
103
101
  // 8. No duplicate claudestat binaries in PATH
104
102
  let duplicatesOk = true;
@@ -117,7 +115,7 @@ async function runDoctor() {
117
115
  ok: duplicatesOk,
118
116
  note: duplicatesNote,
119
117
  fix: duplicatesOk ? undefined :
120
- `npm uninstall -g @deibygs/claudestat && npm install -g @deibygs/claudestat\n Then restart your terminal or run: ${paths_1.isWindows ? 'refreshenv' : 'hash -r claudestat'}`,
118
+ `npm uninstall -g @statforge/claudestat && npm install -g @statforge/claudestat\n Then restart your terminal or run: ${paths_1.isWindows ? 'refreshenv' : 'hash -r claudestat'}`,
121
119
  });
122
120
  // 9. Active binary version matches installed package
123
121
  let versionOk = true;
@@ -146,7 +144,7 @@ async function runDoctor() {
146
144
  ok: versionOk,
147
145
  note: versionNote,
148
146
  fix: versionOk ? undefined :
149
- `${paths_1.isWindows ? 'refreshenv' : 'hash -r claudestat'} (or restart terminal)\n If persists: npm uninstall -g @deibygs/claudestat && npm install -g @deibygs/claudestat`,
147
+ `${paths_1.isWindows ? 'refreshenv' : 'hash -r claudestat'} (or restart terminal)\n If persists: npm uninstall -g @statforge/claudestat && npm install -g @statforge/claudestat`,
150
148
  });
151
149
  // 10. NVM prefix sanity (only when NVM is active)
152
150
  if ((process.env.NVM_DIR || process.env.NVM_HOME) && activeBinary) {
@@ -165,9 +163,29 @@ async function runDoctor() {
165
163
  ok: nvmOk,
166
164
  note: nvmNote,
167
165
  fix: nvmOk ? undefined :
168
- `nvm use default && npm install -g @deibygs/claudestat\n Then restart terminal`,
166
+ `nvm use default && npm install -g @statforge/claudestat\n Then restart terminal`,
169
167
  });
170
168
  }
169
+ // 11. MCP server registered in Claude Code
170
+ let mcpOk = false;
171
+ let mcpNote;
172
+ const mcpResult = (0, child_process_1.spawnSync)('claude', ['mcp', 'list'], { encoding: 'utf8', timeout: 15000 });
173
+ if (mcpResult.error) {
174
+ mcpNote = '"claude" CLI not found — install Claude Code first';
175
+ }
176
+ else {
177
+ const mcpList = (mcpResult.stdout ?? '') + (mcpResult.stderr ?? '');
178
+ const mcpLine = mcpList.split('\n').find(l => l.includes('claudestat'));
179
+ mcpOk = !!mcpLine && !mcpLine.includes('Failed') && !mcpLine.includes('✗');
180
+ if (!mcpOk)
181
+ mcpNote = 'Run "claudestat install" to register it automatically';
182
+ }
183
+ checks.push({
184
+ label: 'MCP server registered in Claude Code',
185
+ ok: mcpOk,
186
+ note: mcpNote,
187
+ fix: mcpOk ? undefined : 'claudestat install',
188
+ });
171
189
  // ── Print results ───────────────────────────────────────────
172
190
  console.log('\n🩺 claudestat doctor\n' + '─'.repeat(46));
173
191
  for (const c of checks) {
package/dist/index.js CHANGED
@@ -34,6 +34,41 @@ const quota_tracker_1 = require("./quota-tracker");
34
34
  const program = new commander_1.Command();
35
35
  const PKG_VERSION = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '..', 'package.json'), 'utf8')).version;
36
36
  const PID_FILE = (0, paths_1.getPidFile)();
37
+ // ── Update notifier ────────────────────────────────────────────
38
+ const SKIP_UPDATE_NOTICE = new Set(['start', 'stop', 'restart', 'watch']);
39
+ const subcommand = process.argv[2];
40
+ if (!SKIP_UPDATE_NOTICE.has(subcommand)) {
41
+ const UPDATE_CACHE = path_1.default.join((0, paths_1.getClaudestatDir)(), 'update-cache.json');
42
+ let cachedLatest = null;
43
+ const fetchLatestVersion = () => {
44
+ fetch('https://registry.npmjs.org/@statforge/claudestat/latest', { signal: AbortSignal.timeout(3000) })
45
+ .then(r => r.json())
46
+ .then(j => {
47
+ if (j?.version) {
48
+ cachedLatest = j.version;
49
+ fs_1.default.writeFileSync(UPDATE_CACHE, JSON.stringify({ version: j.version, ts: Date.now() }));
50
+ }
51
+ })
52
+ .catch(() => { });
53
+ };
54
+ try {
55
+ const cache = JSON.parse(fs_1.default.readFileSync(UPDATE_CACHE, 'utf8'));
56
+ cachedLatest = cache.version;
57
+ if (Date.now() - cache.ts >= 24 * 60 * 60 * 1000)
58
+ fetchLatestVersion();
59
+ }
60
+ catch {
61
+ fetchLatestVersion();
62
+ }
63
+ const _exit = process.exit.bind(process);
64
+ process.exit = ((code) => {
65
+ if ((code ?? 0) === 0 && cachedLatest && cachedLatest !== PKG_VERSION) {
66
+ console.log(`\n ✦ Update available: ${PKG_VERSION} → ${cachedLatest}`);
67
+ console.log(` Run: npm install -g @statforge/claudestat\n`);
68
+ }
69
+ _exit(code);
70
+ });
71
+ }
37
72
  function spawnDaemon() {
38
73
  const child = (0, child_process_1.spawn)(process.execPath, [process.argv[1], 'start'], {
39
74
  detached: true,
package/dist/install.js CHANGED
@@ -20,6 +20,7 @@ exports.uninstallHooks = uninstallHooks;
20
20
  const fs_1 = __importDefault(require("fs"));
21
21
  const path_1 = __importDefault(require("path"));
22
22
  const readline_1 = __importDefault(require("readline"));
23
+ const child_process_1 = require("child_process");
23
24
  const paths_1 = require("./paths");
24
25
  const config_1 = require("./config");
25
26
  const CLAUDESTAT_DIR = (0, paths_1.getClaudestatDir)();
@@ -98,7 +99,9 @@ async function runInstall() {
98
99
  }
99
100
  else {
100
101
  showInstallStatus();
102
+ installMcp();
101
103
  }
104
+ process.exit(0);
102
105
  }
103
106
  async function runWizard() {
104
107
  const nonInteractive = !process.stdin.isTTY;
@@ -150,6 +153,42 @@ async function runWizard() {
150
153
  }
151
154
  // Paso 5: instalar hooks
152
155
  installHooks();
156
+ // Paso 6: registrar MCP server en Claude Code
157
+ installMcp();
158
+ }
159
+ function installMcp() {
160
+ const nodeExec = process.execPath;
161
+ const mcpScript = path_1.default.join(__dirname, 'mcp-server.js');
162
+ const manualCmd = `claude mcp add claudestat -s user -- "${nodeExec}" --disable-warning=ExperimentalWarning "${mcpScript}"`;
163
+ try {
164
+ const result = (0, child_process_1.spawnSync)('claude', ['mcp', 'list'], { encoding: 'utf8', timeout: 15000 });
165
+ const list = (result.stdout ?? '') + (result.stderr ?? '');
166
+ const mcpLine = list.split('\n').find((l) => l.includes('claudestat'));
167
+ if (mcpLine && !mcpLine.includes('Failed') && !mcpLine.includes('✗')) {
168
+ console.log(' (already registered): MCP server');
169
+ return;
170
+ }
171
+ if (mcpLine) {
172
+ // Registered but failing — remove from both scopes and re-register
173
+ (0, child_process_1.spawnSync)('claude', ['mcp', 'remove', 'claudestat', '-s', 'user'], { encoding: 'utf8' });
174
+ (0, child_process_1.spawnSync)('claude', ['mcp', 'remove', 'claudestat', '-s', 'local'], { encoding: 'utf8' });
175
+ }
176
+ }
177
+ catch {
178
+ console.log('\n⚠ Could not reach "claude" CLI — skipping MCP setup.');
179
+ console.log(' To register manually:');
180
+ console.log(` ${manualCmd}\n`);
181
+ return;
182
+ }
183
+ try {
184
+ (0, child_process_1.execSync)(manualCmd, { stdio: 'pipe' });
185
+ console.log('✓ MCP server registered (user scope)\n');
186
+ }
187
+ catch (err) {
188
+ console.log('\n⚠ MCP registration failed.');
189
+ console.log(' To register manually:');
190
+ console.log(` ${manualCmd}\n`);
191
+ }
153
192
  }
154
193
  function showInstallStatus() {
155
194
  const cfg = (0, config_1.readConfig)();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statforge/claudestat",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "Observability layer for Claude Code — live token tracking, cost analytics, quota guard, loop detection, and usage dashboard. The htop for Claude Code.",
5
5
  "keywords": [
6
6
  "claude-code",