@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 +1 -1
- package/dist/doctor.js +27 -9
- package/dist/index.js +35 -0
- package/dist/install.js +39 -0
- package/package.json +1 -1
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
|
|
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 >=
|
|
21
|
-
note: nodeMajor >= 22 ? 'node:sqlite supported ✓'
|
|
22
|
-
|
|
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 @
|
|
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 @
|
|
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 @
|
|
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 @
|
|
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.
|
|
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",
|