clawborrator-mcp 0.0.1 → 0.0.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ // Standalone bundled hook entry. esbuild rolls this + everything it
2
+ // transitively imports (sidecar, transcript, log, hook) into a single
3
+ // dist-hook/clawborrator-tail.mjs that gets copied into a project's
4
+ // .claude/hooks/ at `claw session init` time.
5
+ //
6
+ // Hooks then run as plain `node .claude/hooks/clawborrator-tail.mjs
7
+ // <HookName>` — no npx, no registry lookup, no install. Same shape
8
+ // as the original clawborrator-channel package.
9
+ //
10
+ // Usage from .claude/settings.json:
11
+ // { "type": "command", "command": "node \".claude/hooks/clawborrator-tail.mjs\" PreToolUse" }
12
+ import { runHook } from './hook.js';
13
+ import { log } from './log.js';
14
+ const hookName = process.argv[2];
15
+ if (!hookName) {
16
+ log.error('hook entry invoked without a hook name argument');
17
+ process.exit(0); // never fail Claude's hook chain
18
+ }
19
+ runHook(hookName).catch((err) => {
20
+ log.error('hook fatal', { error: String(err) });
21
+ process.exit(0);
22
+ });
23
+ //# sourceMappingURL=hook-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-entry.js","sourceRoot":"","sources":["../src/hook-entry.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,sEAAsE;AACtE,oEAAoE;AACpE,8CAA8C;AAC9C,EAAE;AACF,oEAAoE;AACpE,mEAAmE;AACnE,gDAAgD;AAChD,EAAE;AACF,oCAAoC;AACpC,gGAAgG;AAEhG,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjC,IAAI,CAAC,QAAQ,EAAE,CAAC;IACd,GAAG,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;IAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAE,iCAAiC;AACrD,CAAC;AAED,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACvC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/index.js CHANGED
@@ -11,6 +11,9 @@
11
11
  // gets an empty tools/list response and the WS work proceeds in the
12
12
  // background.
13
13
  import { hostname } from 'node:os';
14
+ import { readFileSync, existsSync } from 'node:fs';
15
+ import { resolve, dirname } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
14
17
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
15
18
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
16
19
  import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
@@ -21,9 +24,11 @@ import { runHook } from './hook.js';
21
24
  import { writeSidecar, deleteSidecar } from './sidecar.js';
22
25
  import { TOOL_DEFINITIONS, callTool } from './tools/index.js';
23
26
  import { enqueueRoutedPrompt } from './inbox.js';
24
- // Dispatch on --hook=<HookName> first; that's the short-lived spawn
25
- // path Claude Code's hook system uses. Without it, fall through to
26
- // the long-lived MCP stdio server.
27
+ const __dirname = dirname(fileURLToPath(import.meta.url));
28
+ // Dispatch on top-level flags BEFORE booting the long-lived MCP.
29
+ // --hook=<HookName> short-lived hook spawn (fast path)
30
+ // --print-hook-file stream the bundled dist-hook/.mjs to stdout
31
+ // (none) long-lived stdio MCP server
27
32
  const hookFlag = process.argv.find((a) => a.startsWith('--hook='));
28
33
  if (hookFlag) {
29
34
  const name = hookFlag.slice('--hook='.length);
@@ -32,6 +37,20 @@ if (hookFlag) {
32
37
  process.exit(0); // never fail the operator's actual Claude flow
33
38
  });
34
39
  }
40
+ else if (process.argv.includes('--print-hook-file')) {
41
+ // Used by `claw session init` to materialize a local copy of the
42
+ // bundled hook entry into the project's .claude/hooks/ folder.
43
+ // dist-hook/clawborrator-tail.mjs is built by esbuild at publish
44
+ // time and shipped in the package's `files` allow-list.
45
+ // Compiled __dirname is dist/, so the bundle is at ../dist-hook.
46
+ const candidate = resolve(__dirname, '..', 'dist-hook', 'clawborrator-tail.mjs');
47
+ if (!existsSync(candidate)) {
48
+ process.stderr.write(`[clawborrator-mcp] bundled hook file missing at ${candidate}\n`);
49
+ process.exit(2);
50
+ }
51
+ process.stdout.write(readFileSync(candidate, 'utf8'));
52
+ process.exit(0);
53
+ }
35
54
  else {
36
55
  main().catch((err) => {
37
56
  log.error('fatal', { error: String(err), stack: err?.stack });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,369 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/sidecar.ts
4
+ import { resolve } from "node:path";
5
+ import { mkdirSync, writeFileSync, readFileSync, unlinkSync, chmodSync, existsSync } from "node:fs";
6
+
7
+ // src/log.ts
8
+ var LEVELS = { debug: 10, info: 20, warn: 30, error: 40 };
9
+ var MIN = LEVELS[process.env.CLAWBORRATOR_LOG_LEVEL || "info"] ?? LEVELS.info;
10
+ function emit(level, msg, fields) {
11
+ if (LEVELS[level] < MIN) return;
12
+ const line = JSON.stringify({
13
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
14
+ level,
15
+ msg,
16
+ ...fields
17
+ });
18
+ process.stderr.write(line + "\n");
19
+ }
20
+ var log = {
21
+ debug: (msg, fields) => emit("debug", msg, fields),
22
+ info: (msg, fields) => emit("info", msg, fields),
23
+ warn: (msg, fields) => emit("warn", msg, fields),
24
+ error: (msg, fields) => emit("error", msg, fields)
25
+ };
26
+
27
+ // src/sidecar.ts
28
+ function sidecarPath(cwd) {
29
+ return resolve(cwd, ".claude", "clawborrator.session.json");
30
+ }
31
+ function readSidecar(cwd) {
32
+ try {
33
+ const raw = readFileSync(sidecarPath(cwd), "utf8");
34
+ return JSON.parse(raw);
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+ function findSidecar(start) {
40
+ let dir = resolve(start);
41
+ for (let i = 0; i < 32; i++) {
42
+ const found = readSidecar(dir);
43
+ if (found) return found;
44
+ const parent = resolve(dir, "..");
45
+ if (parent === dir) break;
46
+ dir = parent;
47
+ }
48
+ return null;
49
+ }
50
+
51
+ // src/transcript.ts
52
+ import { openSync, readSync, closeSync, statSync } from "node:fs";
53
+ var DEFAULT_TAIL_BYTES = 256 * 1024;
54
+ function readTranscriptMessages(path, tailBytes = DEFAULT_TAIL_BYTES) {
55
+ try {
56
+ const stat = statSync(path);
57
+ const start = Math.max(0, stat.size - tailBytes);
58
+ const fd = openSync(path, "r");
59
+ let raw;
60
+ try {
61
+ const buf = Buffer.alloc(stat.size - start);
62
+ readSync(fd, buf, 0, buf.length, start);
63
+ raw = buf.toString("utf8");
64
+ } finally {
65
+ closeSync(fd);
66
+ }
67
+ if (!raw) return [];
68
+ const lines = raw.split("\n").filter(Boolean);
69
+ if (start > 0 && lines.length > 0) lines.shift();
70
+ return lines.map((l) => {
71
+ try {
72
+ return JSON.parse(l);
73
+ } catch {
74
+ return null;
75
+ }
76
+ }).filter((m) => m !== null);
77
+ } catch {
78
+ return [];
79
+ }
80
+ }
81
+ function extractTextBlocksBeforeToolUse(messages, toolUseId) {
82
+ if (!toolUseId || !Array.isArray(messages)) return [];
83
+ let targetIdx = -1;
84
+ for (let i = messages.length - 1; i >= 0; i--) {
85
+ const m = messages[i];
86
+ if (m?.type !== "assistant") continue;
87
+ const content = m.message?.content;
88
+ if (!Array.isArray(content)) continue;
89
+ if (content.some((c) => c?.type === "tool_use" && c.id === toolUseId)) {
90
+ targetIdx = i;
91
+ break;
92
+ }
93
+ }
94
+ if (targetIdx < 0) return [];
95
+ const out = [];
96
+ for (let i = targetIdx - 1; i >= 0; i--) {
97
+ const m = messages[i];
98
+ if (m?.type === "user") break;
99
+ if (m?.type !== "assistant") continue;
100
+ const content = m.message?.content;
101
+ if (!Array.isArray(content)) continue;
102
+ for (let j = content.length - 1; j >= 0; j--) {
103
+ const c = content[j];
104
+ if (c?.type === "text" && typeof c.text === "string" && c.text.trim()) {
105
+ out.unshift(c.text);
106
+ }
107
+ }
108
+ }
109
+ return out;
110
+ }
111
+ function messageContainsToolUse(messages, toolUseId) {
112
+ if (!toolUseId || !Array.isArray(messages)) return false;
113
+ for (let i = messages.length - 1; i >= 0; i--) {
114
+ const m = messages[i];
115
+ if (m?.type !== "assistant") continue;
116
+ const content = m.message?.content;
117
+ if (!Array.isArray(content)) continue;
118
+ if (content.some((c) => c?.type === "tool_use" && c.id === toolUseId)) return true;
119
+ }
120
+ return false;
121
+ }
122
+ function hasThinkingBlocksBeforeToolUse(messages, toolUseId) {
123
+ if (!toolUseId || !Array.isArray(messages)) return false;
124
+ let targetIdx = -1;
125
+ for (let i = messages.length - 1; i >= 0; i--) {
126
+ const m = messages[i];
127
+ if (m?.type !== "assistant") continue;
128
+ const content = m.message?.content;
129
+ if (!Array.isArray(content)) continue;
130
+ if (content.some((c) => c?.type === "tool_use" && c.id === toolUseId)) {
131
+ targetIdx = i;
132
+ break;
133
+ }
134
+ }
135
+ if (targetIdx < 0) return false;
136
+ for (let i = targetIdx - 1; i >= 0; i--) {
137
+ const m = messages[i];
138
+ if (m?.type === "user") break;
139
+ if (m?.type !== "assistant") continue;
140
+ const content = m.message?.content;
141
+ if (!Array.isArray(content)) continue;
142
+ if (content.some((c) => c?.type === "thinking")) return true;
143
+ }
144
+ return false;
145
+ }
146
+ function joinTextAfterLastToolUse(content) {
147
+ if (!Array.isArray(content)) return "";
148
+ let lastToolIdx = -1;
149
+ for (let j = content.length - 1; j >= 0; j--) {
150
+ if (content[j]?.type === "tool_use") {
151
+ lastToolIdx = j;
152
+ break;
153
+ }
154
+ }
155
+ const parts = content.slice(lastToolIdx + 1).filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => c.text);
156
+ return parts.join("\n").trim();
157
+ }
158
+ function extractFromLastAssistantMessage(v) {
159
+ if (!v) return "";
160
+ if (typeof v === "string") return v.trim();
161
+ const obj = v;
162
+ const content = Array.isArray(obj.content) ? obj.content : obj.message && Array.isArray(obj.message.content) ? obj.message.content : null;
163
+ if (content) return joinTextAfterLastToolUse(content);
164
+ if (typeof obj.text === "string") return obj.text.trim();
165
+ return "";
166
+ }
167
+ function extractFinalAnswerFromTranscript(path, tailBytes = DEFAULT_TAIL_BYTES) {
168
+ const messages = readTranscriptMessages(path, tailBytes);
169
+ for (let i = messages.length - 1; i >= 0; i--) {
170
+ const m = messages[i];
171
+ if (m?.type !== "assistant") continue;
172
+ const content = m.message?.content;
173
+ if (!Array.isArray(content)) continue;
174
+ const joined = joinTextAfterLastToolUse(content);
175
+ if (joined) return joined;
176
+ }
177
+ return "";
178
+ }
179
+
180
+ // src/hook.ts
181
+ var HOOK_TO_EVENT = {
182
+ UserPromptSubmit: { kind: "chat", type: "prompt" },
183
+ PreToolUse: { kind: "tail", type: "PreToolUse" },
184
+ PostToolUse: { kind: "tail", type: "PostToolUse" },
185
+ PostToolUseFailure: { kind: "tail", type: "PostToolUseFailure" },
186
+ Stop: { kind: "tail", type: "Stop" },
187
+ Notification: { kind: "tail", type: "Notification" },
188
+ SessionStart: { kind: "tail", type: "SessionStart" },
189
+ SessionEnd: { kind: "tail", type: "SessionEnd" },
190
+ TaskCreated: { kind: "tail", type: "TaskCreated" },
191
+ SubagentStart: { kind: "tail", type: "SubagentStart" },
192
+ SubagentStop: { kind: "tail", type: "SubagentStop" },
193
+ TaskCompleted: { kind: "tail", type: "TaskCompleted" }
194
+ };
195
+ var TOOLS_SKIPPED_FOR_PRE_TEXT = /* @__PURE__ */ new Set([
196
+ "mcp__clawborrator__reply"
197
+ ]);
198
+ async function readStdin() {
199
+ return new Promise((res) => {
200
+ let buf = "";
201
+ process.stdin.setEncoding("utf8");
202
+ process.stdin.on("data", (chunk) => {
203
+ buf += chunk;
204
+ });
205
+ process.stdin.on("end", () => res(buf));
206
+ process.stdin.on("close", () => res(buf));
207
+ });
208
+ }
209
+ async function postEvent(sidecar, body, timeoutMs) {
210
+ const ctl = new AbortController();
211
+ const timer = setTimeout(() => ctl.abort(), timeoutMs);
212
+ try {
213
+ const res = await fetch(`${sidecar.hubUrl}/api/channel/event`, {
214
+ method: "POST",
215
+ headers: {
216
+ "Authorization": `Bearer ${sidecar.channelToken}`,
217
+ "Content-Type": "application/json"
218
+ },
219
+ body: JSON.stringify(body),
220
+ signal: ctl.signal
221
+ });
222
+ if (!res.ok) {
223
+ const text = await res.text().catch(() => "");
224
+ log.warn("hub rejected event", { kind: body.kind, type: body.type, status: res.status, body: text.slice(0, 240) });
225
+ return false;
226
+ }
227
+ return true;
228
+ } catch (e) {
229
+ log.warn("event POST failed", { kind: body.kind, type: body.type, error: e?.message ?? String(e) });
230
+ return false;
231
+ } finally {
232
+ clearTimeout(timer);
233
+ }
234
+ }
235
+ async function enrichStopAssistantText(payload, hookName2) {
236
+ if (typeof payload.assistant_text === "string" && payload.assistant_text.trim()) return;
237
+ const fromLAM = extractFromLastAssistantMessage(payload.last_assistant_message);
238
+ if (fromLAM) {
239
+ payload.assistant_text = fromLAM;
240
+ log.debug("stop: assistant_text from last_assistant_message", { chars: fromLAM.length });
241
+ return;
242
+ }
243
+ if (typeof payload.text === "string" && payload.text.trim() || typeof payload.response === "string" && payload.response.trim()) {
244
+ return;
245
+ }
246
+ const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : "";
247
+ if (!transcriptPath) {
248
+ log.debug("stop: no transcript_path on payload, no assistant_text recoverable", { hookName: hookName2 });
249
+ return;
250
+ }
251
+ await new Promise((r) => setTimeout(r, 500));
252
+ const text = extractFinalAnswerFromTranscript(transcriptPath, DEFAULT_TAIL_BYTES);
253
+ if (text) {
254
+ payload.assistant_text = text;
255
+ log.info("stop: assistant_text extracted from transcript", { chars: text.length });
256
+ } else {
257
+ log.debug("stop: transcript had no assistant-text block in tail window", { hookName: hookName2, path: transcriptPath });
258
+ }
259
+ }
260
+ async function shipPreToolAssistantText(sidecar, payload) {
261
+ const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : "";
262
+ const toolUseId = String(payload.tool_use_id ?? payload.toolUseId ?? "");
263
+ const toolName = String(payload.tool_name ?? payload.toolName ?? "");
264
+ if (!transcriptPath || !toolUseId) return;
265
+ if (TOOLS_SKIPPED_FOR_PRE_TEXT.has(toolName)) return;
266
+ let messages = readTranscriptMessages(transcriptPath, DEFAULT_TAIL_BYTES);
267
+ let foundTarget = messageContainsToolUse(messages, toolUseId);
268
+ let retries = 0;
269
+ while (!foundTarget && retries < 4) {
270
+ await new Promise((r) => setTimeout(r, 80));
271
+ messages = readTranscriptMessages(transcriptPath, DEFAULT_TAIL_BYTES);
272
+ foundTarget = messageContainsToolUse(messages, toolUseId);
273
+ retries++;
274
+ }
275
+ const blocks = extractTextBlocksBeforeToolUse(messages, toolUseId);
276
+ const hasThinking = blocks.length === 0 ? hasThinkingBlocksBeforeToolUse(messages, toolUseId) : false;
277
+ log.debug("PreToolUse extract", {
278
+ toolUseId: toolUseId.slice(0, 24),
279
+ messages: messages.length,
280
+ targetFound: foundTarget,
281
+ retries,
282
+ blocks: blocks.length,
283
+ placeholder: hasThinking
284
+ });
285
+ const baseMs = Date.now();
286
+ const tsAt = (i) => new Date(baseMs + i).toISOString();
287
+ if (blocks.length > 0) {
288
+ await Promise.all(blocks.map(
289
+ (text, i) => postEvent(sidecar, {
290
+ sessionId: sidecar.sessionId,
291
+ kind: "chat",
292
+ type: "assistant_text",
293
+ payload: { text, toolUseId },
294
+ ts: tsAt(i)
295
+ }, 800)
296
+ ));
297
+ } else if (hasThinking) {
298
+ await postEvent(sidecar, {
299
+ sessionId: sidecar.sessionId,
300
+ kind: "chat",
301
+ type: "assistant_text",
302
+ payload: {
303
+ text: "(extended thinking \u2014 content not in transcript)",
304
+ toolUseId,
305
+ placeholder: true
306
+ },
307
+ ts: tsAt(0)
308
+ }, 800);
309
+ }
310
+ }
311
+ async function runHook(hookName2) {
312
+ const map = HOOK_TO_EVENT[hookName2];
313
+ if (!map) {
314
+ log.warn("unknown hook name; skipping", { hookName: hookName2 });
315
+ process.exit(0);
316
+ }
317
+ const stdinRaw = await readStdin();
318
+ if (stdinRaw) process.stdout.write(stdinRaw);
319
+ let payload = {};
320
+ try {
321
+ if (stdinRaw.trim()) payload = JSON.parse(stdinRaw);
322
+ } catch {
323
+ payload = { rawStdin: stdinRaw.slice(0, 2e3) };
324
+ }
325
+ const sidecar = findSidecar(process.cwd());
326
+ if (!sidecar) {
327
+ log.warn("hook fired but no sidecar found \u2014 channel must not be running", { cwd: process.cwd() });
328
+ process.exit(0);
329
+ }
330
+ try {
331
+ if (hookName2 === "Stop") {
332
+ await enrichStopAssistantText(payload, hookName2);
333
+ const reply = typeof payload.assistant_text === "string" ? payload.assistant_text.trim() : "";
334
+ if (reply) {
335
+ await postEvent(sidecar, {
336
+ sessionId: sidecar.sessionId,
337
+ kind: "chat",
338
+ type: "reply",
339
+ payload: { text: reply },
340
+ ts: (/* @__PURE__ */ new Date()).toISOString()
341
+ }, 2e3);
342
+ }
343
+ } else if (hookName2 === "SubagentStop") {
344
+ } else if (hookName2 === "PreToolUse") {
345
+ await shipPreToolAssistantText(sidecar, payload);
346
+ }
347
+ } catch (e) {
348
+ log.warn("pre-event enrichment threw", { error: e?.message ?? String(e), hookName: hookName2 });
349
+ }
350
+ await postEvent(sidecar, {
351
+ sessionId: sidecar.sessionId,
352
+ kind: map.kind,
353
+ type: map.type,
354
+ payload,
355
+ ts: (/* @__PURE__ */ new Date()).toISOString()
356
+ }, 5e3);
357
+ process.exit(0);
358
+ }
359
+
360
+ // src/hook-entry.ts
361
+ var hookName = process.argv[2];
362
+ if (!hookName) {
363
+ log.error("hook entry invoked without a hook name argument");
364
+ process.exit(0);
365
+ }
366
+ runHook(hookName).catch((err) => {
367
+ log.error("hook fatal", { error: String(err) });
368
+ process.exit(0);
369
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawborrator-mcp",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
5
  "description": "clawborrator channel for hub_v1 — MCP server that connects Claude Code to a hub over WebSocket, with hooks for activity capture and tools for cross-session routing.",
6
6
  "license": "MIT",
@@ -28,6 +28,7 @@
28
28
  },
29
29
  "files": [
30
30
  "dist",
31
+ "dist-hook",
31
32
  "bin",
32
33
  "README.md"
33
34
  ],
@@ -35,10 +36,12 @@
35
36
  "node": ">=20"
36
37
  },
37
38
  "scripts": {
38
- "build": "tsc -p tsconfig.json",
39
- "dev": "tsx watch src/index.ts",
40
- "typecheck": "tsc -p tsconfig.json --noEmit",
41
- "start": "node dist/index.js",
39
+ "build": "npm run build:tsc && npm run build:hook",
40
+ "build:tsc": "tsc -p tsconfig.json",
41
+ "build:hook": "esbuild src/hook-entry.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist-hook/clawborrator-tail.mjs --banner:js=\"#!/usr/bin/env node\"",
42
+ "dev": "tsx watch src/index.ts",
43
+ "typecheck": "tsc -p tsconfig.json --noEmit",
44
+ "start": "node dist/index.js",
42
45
  "prepublishOnly": "npm run build"
43
46
  },
44
47
  "dependencies": {
@@ -48,6 +51,7 @@
48
51
  "devDependencies": {
49
52
  "@types/node": "^22.7.5",
50
53
  "@types/ws": "^8.5.13",
54
+ "esbuild": "^0.24.2",
51
55
  "tsx": "^4.19.1",
52
56
  "typescript": "^5.6.2"
53
57
  }