claude-cup 0.2.2 → 0.2.4
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.
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
// It supports both long-lived stdio MCP server mode and short-lived --hook mode.
|
|
5
5
|
// The source path is baked in at build time so the launcher works from any directory.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
const SRC = "C:\\Users\\itaib\\nice claude iedeas\\Claude-\\claude-jar\\mcp-server\\src";
|
|
9
|
+
const toURL = f => pathToFileURL(f).href;
|
|
8
10
|
const isHook = process.argv.includes('--hook');
|
|
9
11
|
|
|
10
12
|
if (isHook) {
|
|
11
|
-
const { runHookIngest } = await import(
|
|
13
|
+
const { runHookIngest } = await import(toURL(SRC + '/hook-ingest.js'));
|
|
12
14
|
await runHookIngest(process.argv);
|
|
13
15
|
} else {
|
|
14
|
-
const { startMcpServer } = await import(
|
|
16
|
+
const { startMcpServer } = await import(toURL(SRC + '/index.js'));
|
|
15
17
|
await startMcpServer();
|
|
16
18
|
}
|
|
@@ -75,13 +75,24 @@ export async function runHookIngest(argv = process.argv) {
|
|
|
75
75
|
const pjIdx = argv.indexOf('--payload-json');
|
|
76
76
|
if (pjIdx !== -1 && argv[pjIdx + 1]) {
|
|
77
77
|
try { payload = JSON.parse(argv[pjIdx + 1]); } catch { /* ignore */ }
|
|
78
|
-
} else {
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
} else if (!process.stdin.isTTY) {
|
|
79
|
+
// Read stdin with a timeout so the hook doesn't hang if Claude Code
|
|
80
|
+
// closes the pipe slowly or sends an empty payload.
|
|
81
|
+
try {
|
|
82
|
+
const chunks = [];
|
|
83
|
+
const timeout = new Promise(r => setTimeout(r, 2000));
|
|
84
|
+
const read = new Promise(resolve => {
|
|
85
|
+
process.stdin.on('data', d => chunks.push(d));
|
|
86
|
+
process.stdin.on('end', resolve);
|
|
87
|
+
process.stdin.on('error', resolve);
|
|
88
|
+
});
|
|
89
|
+
await Promise.race([read, timeout]);
|
|
90
|
+
process.stdin.removeAllListeners();
|
|
91
|
+
const raw = Buffer.concat(chunks).toString('utf8');
|
|
92
|
+
if (raw.trim()) {
|
|
93
|
+
try { payload = JSON.parse(raw); } catch { /* ignore */ }
|
|
94
|
+
}
|
|
95
|
+
} catch { /* ignore stdin errors */ }
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
const configDir = process.env.CLAUDE_CONFIG_DIR || undefined;
|
|
@@ -28,9 +28,9 @@ function deepMergeOnlyOurKeys(target, our) {
|
|
|
28
28
|
result.hooks = { ...(target.hooks || {}) };
|
|
29
29
|
for (const [k, arr] of Object.entries(our.hooks)) {
|
|
30
30
|
const existing = Array.isArray(result.hooks[k]) ? result.hooks[k] : [];
|
|
31
|
-
const seen = new Set(existing.map(e => JSON.stringify(
|
|
31
|
+
const seen = new Set(existing.map(e => JSON.stringify(e)));
|
|
32
32
|
for (const item of arr) {
|
|
33
|
-
const key = JSON.stringify(
|
|
33
|
+
const key = JSON.stringify(item);
|
|
34
34
|
if (!seen.has(key)) {
|
|
35
35
|
existing.push(item);
|
|
36
36
|
seen.add(key);
|
|
@@ -68,9 +68,9 @@ export function registerClaudeCode(configDir) {
|
|
|
68
68
|
},
|
|
69
69
|
},
|
|
70
70
|
hooks: {
|
|
71
|
-
SessionStart: [{
|
|
72
|
-
PreToolUse: [{
|
|
73
|
-
PostToolUse: [{
|
|
71
|
+
SessionStart: [{ matcher: '', hooks: [{ type: 'command', command: `node "${launcher}" --hook SessionStart` }] }],
|
|
72
|
+
PreToolUse: [{ matcher: '', hooks: [{ type: 'command', command: `node "${launcher}" --hook PreToolUse` }] }],
|
|
73
|
+
PostToolUse: [{ matcher: '', hooks: [{ type: 'command', command: `node "${launcher}" --hook PostToolUse` }] }],
|
|
74
74
|
},
|
|
75
75
|
};
|
|
76
76
|
|
|
@@ -78,7 +78,7 @@ export function registerClaudeCode(configDir) {
|
|
|
78
78
|
let backup = null;
|
|
79
79
|
try {
|
|
80
80
|
if (existsSync(settingsPath)) {
|
|
81
|
-
const raw = readFileSync(settingsPath, 'utf8');
|
|
81
|
+
const raw = readFileSync(settingsPath, 'utf8').replace(/^\uFEFF/, '');
|
|
82
82
|
if (raw.trim()) original = JSON.parse(raw);
|
|
83
83
|
}
|
|
84
84
|
} catch (e) {
|
|
@@ -117,7 +117,7 @@ export function disableClaudeCode(configDir) {
|
|
|
117
117
|
|
|
118
118
|
let settings = {};
|
|
119
119
|
if (existsSync(settingsPath)) {
|
|
120
|
-
try { settings = JSON.parse(readFileSync(settingsPath, 'utf8')); } catch { return { ok: false, error: 'corrupt settings.json', didWrite: false, backupPath: null, launcherPath: launcher }; }
|
|
120
|
+
try { settings = JSON.parse(readFileSync(settingsPath, 'utf8').replace(/^\uFEFF/, '')); } catch { return { ok: false, error: 'corrupt settings.json', didWrite: false, backupPath: null, launcherPath: launcher }; }
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
let changed = false;
|
|
@@ -129,10 +129,10 @@ export function disableClaudeCode(configDir) {
|
|
|
129
129
|
if (settings.hooks) {
|
|
130
130
|
const launcherBase = 'mcp-server.mjs';
|
|
131
131
|
for (const k of Object.keys(settings.hooks)) {
|
|
132
|
-
settings.hooks[k] = (settings.hooks[k] || []).filter(
|
|
133
|
-
if (!
|
|
134
|
-
const
|
|
135
|
-
return !
|
|
132
|
+
settings.hooks[k] = (settings.hooks[k] || []).filter(rule => {
|
|
133
|
+
if (!rule) return true;
|
|
134
|
+
const ruleStr = JSON.stringify(rule);
|
|
135
|
+
return !ruleStr.includes(launcherBase);
|
|
136
136
|
});
|
|
137
137
|
if (settings.hooks[k].length === 0) delete settings.hooks[k];
|
|
138
138
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-cup",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Claude Jar v2 — native desktop visual companion (Tauri + Svelte) with MCP/hook integration for live Claude activity. Beautiful accumulating jar + live intensity meter. The jar is the usage meter.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -24,14 +24,16 @@ const launcher = `#!/usr/bin/env node
|
|
|
24
24
|
// It supports both long-lived stdio MCP server mode and short-lived --hook mode.
|
|
25
25
|
// The source path is baked in at build time so the launcher works from any directory.
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
import { pathToFileURL } from 'node:url';
|
|
28
|
+
const SRC = ${JSON.stringify(mcpSrc)};
|
|
29
|
+
const toURL = f => pathToFileURL(f).href;
|
|
28
30
|
const isHook = process.argv.includes('--hook');
|
|
29
31
|
|
|
30
32
|
if (isHook) {
|
|
31
|
-
const { runHookIngest } = await import(
|
|
33
|
+
const { runHookIngest } = await import(toURL(SRC + '/hook-ingest.js'));
|
|
32
34
|
await runHookIngest(process.argv);
|
|
33
35
|
} else {
|
|
34
|
-
const { startMcpServer } = await import(
|
|
36
|
+
const { startMcpServer } = await import(toURL(SRC + '/index.js'));
|
|
35
37
|
await startMcpServer();
|
|
36
38
|
}
|
|
37
39
|
`;
|