clawborrator-mcp 0.0.1

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/dist/log.js ADDED
@@ -0,0 +1,23 @@
1
+ // Structured logger. Writes to STDERR only — stdout is reserved for
2
+ // the MCP transport. Lines are JSON for easy grep + machine parsing,
3
+ // but include a human-readable level + msg up front.
4
+ const LEVELS = { debug: 10, info: 20, warn: 30, error: 40 };
5
+ const MIN = LEVELS[process.env.CLAWBORRATOR_LOG_LEVEL || 'info'] ?? LEVELS.info;
6
+ function emit(level, msg, fields) {
7
+ if (LEVELS[level] < MIN)
8
+ return;
9
+ const line = JSON.stringify({
10
+ ts: new Date().toISOString(),
11
+ level,
12
+ msg,
13
+ ...fields,
14
+ });
15
+ process.stderr.write(line + '\n');
16
+ }
17
+ export const log = {
18
+ debug: (msg, fields) => emit('debug', msg, fields),
19
+ info: (msg, fields) => emit('info', msg, fields),
20
+ warn: (msg, fields) => emit('warn', msg, fields),
21
+ error: (msg, fields) => emit('error', msg, fields),
22
+ };
23
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,qEAAqE;AACrE,qDAAqD;AAIrD,MAAM,MAAM,GAA0B,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AACnF,MAAM,GAAG,GAAG,MAAM,CAAE,OAAO,CAAC,GAAG,CAAC,sBAAgC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC;AAE3F,SAAS,IAAI,CAAC,KAAY,EAAE,GAAW,EAAE,MAAgC;IACvE,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG;QAAE,OAAO;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,KAAK;QACL,GAAG;QACH,GAAG,MAAM;KACV,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,KAAK,EAAE,CAAC,GAAW,EAAE,MAAgC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC;IACpF,IAAI,EAAG,CAAC,GAAW,EAAE,MAAgC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAG,GAAG,EAAE,MAAM,CAAC;IACpF,IAAI,EAAG,CAAC,GAAW,EAAE,MAAgC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAG,GAAG,EAAE,MAAM,CAAC;IACpF,KAAK,EAAE,CAAC,GAAW,EAAE,MAAgC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC;CACrF,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface SidecarPayload {
2
+ sessionId: string;
3
+ routingName: string | null;
4
+ hubUrl: string;
5
+ channelToken: string;
6
+ host: string;
7
+ cwd: string;
8
+ writtenAt: string;
9
+ }
10
+ export declare function writeSidecar(payload: SidecarPayload): void;
11
+ export declare function deleteSidecar(cwd: string): void;
12
+ export declare function readSidecar(cwd: string): SidecarPayload | null;
13
+ export declare function findSidecar(start: string): SidecarPayload | null;
@@ -0,0 +1,67 @@
1
+ // Sidecar file — written by the long-lived MCP on `welcome`, deleted
2
+ // on clean shutdown. Hook processes (short-lived spawns by Claude
3
+ // Code's hook system) read this file to know which session to
4
+ // attribute their event to and which hub to POST to.
5
+ //
6
+ // Path: <cwd>/.claude/clawborrator.session.json. Mode 0600 on POSIX.
7
+ //
8
+ // Storing the channel-token plaintext here matters: hooks need it to
9
+ // authenticate to /api/channel/event. The risk surface is the same
10
+ // as the .mcp.json that already lives in the project root with the
11
+ // same secret — readers of the dir can already grab the token.
12
+ import { resolve } from 'node:path';
13
+ import { mkdirSync, writeFileSync, readFileSync, unlinkSync, chmodSync, existsSync } from 'node:fs';
14
+ import { log } from './log.js';
15
+ function sidecarPath(cwd) {
16
+ return resolve(cwd, '.claude', 'clawborrator.session.json');
17
+ }
18
+ export function writeSidecar(payload) {
19
+ const dir = resolve(payload.cwd, '.claude');
20
+ try {
21
+ if (!existsSync(dir))
22
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
23
+ const file = sidecarPath(payload.cwd);
24
+ writeFileSync(file, JSON.stringify(payload, null, 2) + '\n', { mode: 0o600 });
25
+ try {
26
+ chmodSync(file, 0o600);
27
+ }
28
+ catch { /* Windows */ }
29
+ log.info('sidecar written', { file });
30
+ }
31
+ catch (e) {
32
+ log.warn('sidecar write failed', { error: String(e) });
33
+ }
34
+ }
35
+ export function deleteSidecar(cwd) {
36
+ try {
37
+ const file = sidecarPath(cwd);
38
+ if (existsSync(file))
39
+ unlinkSync(file);
40
+ }
41
+ catch { /* swallow */ }
42
+ }
43
+ export function readSidecar(cwd) {
44
+ try {
45
+ const raw = readFileSync(sidecarPath(cwd), 'utf8');
46
+ return JSON.parse(raw);
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ // Walk upward from `start` looking for a sidecar. Hooks may run from
53
+ // a deeper subdirectory than where the channel registered.
54
+ export function findSidecar(start) {
55
+ let dir = resolve(start);
56
+ for (let i = 0; i < 32; i++) {
57
+ const found = readSidecar(dir);
58
+ if (found)
59
+ return found;
60
+ const parent = resolve(dir, '..');
61
+ if (parent === dir)
62
+ break;
63
+ dir = parent;
64
+ }
65
+ return null;
66
+ }
67
+ //# sourceMappingURL=sidecar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,kEAAkE;AAClE,8DAA8D;AAC9D,qDAAqD;AACrD,EAAE;AACF,qEAAqE;AACrE,EAAE;AACF,qEAAqE;AACrE,mEAAmE;AACnE,mEAAmE;AACnE,+DAA+D;AAE/D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACpG,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAY/B,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,2BAA2B,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAuB;IAClD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACvE,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACtC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9E,IAAI,CAAC;YAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC;QACvD,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,GAAG,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,UAAU,CAAC,IAAI,CAAC;YAAE,UAAU,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,2DAA2D;AAC3D,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QACxB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAClC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,98 @@
1
+ import type { ChannelClient } from '../ws-client.js';
2
+ interface CallContext {
3
+ client: ChannelClient;
4
+ /** Hub's chosen sessionId for this channel (filled by `welcome`). */
5
+ sessionId: string | null;
6
+ }
7
+ export declare const TOOL_DEFINITIONS: readonly [{
8
+ readonly name: "await_routed_prompt";
9
+ readonly description: "Check the inbox for a routed prompt from a peer session. Call this at the START of every turn before replying to the user. If the result has a non-null `chatId`, treat `text` as the user prompt for this turn and respond by calling `reply({chat_id, text: <your answer>})`. If `chatId` is null, no peer routed anything — proceed with the actual user message normally.";
10
+ readonly inputSchema: {
11
+ readonly type: "object";
12
+ readonly additionalProperties: false;
13
+ readonly properties: {
14
+ readonly maxWaitMs: {
15
+ readonly type: "number";
16
+ readonly description: "Optional: how long to block waiting for a prompt (default 0 = return immediately).";
17
+ };
18
+ };
19
+ };
20
+ }, {
21
+ readonly name: "reply";
22
+ readonly description: "Post a tagged final reply to a chat. Use this whenever you finish replying to a routed prompt — pass back the chat_id you received from `await_routed_prompt`.";
23
+ readonly inputSchema: {
24
+ readonly type: "object";
25
+ readonly additionalProperties: false;
26
+ readonly required: readonly ["chat_id", "text"];
27
+ readonly properties: {
28
+ readonly chat_id: {
29
+ readonly type: "string";
30
+ readonly description: "The chat id of the prompt you are replying to";
31
+ };
32
+ readonly text: {
33
+ readonly type: "string";
34
+ readonly description: "Your reply text";
35
+ };
36
+ };
37
+ };
38
+ }, {
39
+ readonly name: "list_peers";
40
+ readonly description: "List your peer Claude Code sessions reachable for routing. Use this to discover what other projects you can route questions to.";
41
+ readonly inputSchema: {
42
+ readonly type: "object";
43
+ readonly additionalProperties: false;
44
+ readonly properties: {};
45
+ };
46
+ }, {
47
+ readonly name: "route_to_peer";
48
+ readonly description: "Send a prompt to a peer Claude Code session. Use this when a question genuinely belongs to another project. mode=ask blocks for the reply (up to 60s); mode=tell is fire-and-forget.";
49
+ readonly inputSchema: {
50
+ readonly type: "object";
51
+ readonly additionalProperties: false;
52
+ readonly required: readonly ["peer", "prompt", "mode"];
53
+ readonly properties: {
54
+ readonly peer: {
55
+ readonly type: "string";
56
+ readonly description: "Peer routing name (e.g. \"@reddit-scout\")";
57
+ };
58
+ readonly prompt: {
59
+ readonly type: "string";
60
+ readonly description: "What to ask";
61
+ };
62
+ readonly mode: {
63
+ readonly type: "string";
64
+ readonly enum: readonly ["ask", "tell"];
65
+ readonly description: "ask: wait for reply; tell: fire-and-forget";
66
+ };
67
+ };
68
+ };
69
+ }, {
70
+ readonly name: "probe_peers";
71
+ readonly description: "Fan-out the same short question to many peers in parallel. Use for discovery (e.g. \"do you have a User model?\"). Returns a list of (peer, answer) pairs collected within 30s.";
72
+ readonly inputSchema: {
73
+ readonly type: "object";
74
+ readonly additionalProperties: false;
75
+ readonly required: readonly ["prompt"];
76
+ readonly properties: {
77
+ readonly prompt: {
78
+ readonly type: "string";
79
+ readonly description: "The question to fan out";
80
+ };
81
+ readonly peers: {
82
+ readonly type: "array";
83
+ readonly items: {
84
+ readonly type: "string";
85
+ };
86
+ readonly description: "Peer routing names to ask; null = all online peers";
87
+ };
88
+ };
89
+ };
90
+ }];
91
+ export declare function callTool(ctx: CallContext, name: string, args: Record<string, unknown>): Promise<{
92
+ isError?: boolean;
93
+ content: {
94
+ type: 'text';
95
+ text: string;
96
+ }[];
97
+ }>;
98
+ export {};
@@ -0,0 +1,163 @@
1
+ // MCP tool registry. Each tool wraps a request-response round-trip
2
+ // against the hub via the channel WS.
3
+ //
4
+ // Tools exposed:
5
+ // reply — Claude posts a tagged final reply (resolves
6
+ // pending route_to_peer asks on the originator)
7
+ // list_peers — discover the operator's other online sessions
8
+ // route_to_peer — send a prompt to one peer; ask blocks for reply,
9
+ // tell is fire-and-forget
10
+ // probe_peers — fan-out a short question across many peers
11
+ import { randomUUID } from 'node:crypto';
12
+ import { awaitDequeue } from '../inbox.js';
13
+ const ASK_TIMEOUT_MS = 60_000;
14
+ const PROBE_TIMEOUT_MS = 30_000;
15
+ const LIST_TIMEOUT_MS = 5_000;
16
+ export const TOOL_DEFINITIONS = [
17
+ {
18
+ name: 'await_routed_prompt',
19
+ description: 'Check the inbox for a routed prompt from a peer session. Call this at the START of every turn before replying to the user. If the result has a non-null `chatId`, treat `text` as the user prompt for this turn and respond by calling `reply({chat_id, text: <your answer>})`. If `chatId` is null, no peer routed anything — proceed with the actual user message normally.',
20
+ inputSchema: {
21
+ type: 'object',
22
+ additionalProperties: false,
23
+ properties: {
24
+ maxWaitMs: { type: 'number', description: 'Optional: how long to block waiting for a prompt (default 0 = return immediately).' },
25
+ },
26
+ },
27
+ },
28
+ {
29
+ name: 'reply',
30
+ description: 'Post a tagged final reply to a chat. Use this whenever you finish replying to a routed prompt — pass back the chat_id you received from `await_routed_prompt`.',
31
+ inputSchema: {
32
+ type: 'object',
33
+ additionalProperties: false,
34
+ required: ['chat_id', 'text'],
35
+ properties: {
36
+ chat_id: { type: 'string', description: 'The chat id of the prompt you are replying to' },
37
+ text: { type: 'string', description: 'Your reply text' },
38
+ },
39
+ },
40
+ },
41
+ {
42
+ name: 'list_peers',
43
+ description: 'List your peer Claude Code sessions reachable for routing. Use this to discover what other projects you can route questions to.',
44
+ inputSchema: {
45
+ type: 'object', additionalProperties: false, properties: {},
46
+ },
47
+ },
48
+ {
49
+ name: 'route_to_peer',
50
+ description: 'Send a prompt to a peer Claude Code session. Use this when a question genuinely belongs to another project. mode=ask blocks for the reply (up to 60s); mode=tell is fire-and-forget.',
51
+ inputSchema: {
52
+ type: 'object',
53
+ additionalProperties: false,
54
+ required: ['peer', 'prompt', 'mode'],
55
+ properties: {
56
+ peer: { type: 'string', description: 'Peer routing name (e.g. "@reddit-scout")' },
57
+ prompt: { type: 'string', description: 'What to ask' },
58
+ mode: { type: 'string', enum: ['ask', 'tell'], description: 'ask: wait for reply; tell: fire-and-forget' },
59
+ },
60
+ },
61
+ },
62
+ {
63
+ name: 'probe_peers',
64
+ description: 'Fan-out the same short question to many peers in parallel. Use for discovery (e.g. "do you have a User model?"). Returns a list of (peer, answer) pairs collected within 30s.',
65
+ inputSchema: {
66
+ type: 'object',
67
+ additionalProperties: false,
68
+ required: ['prompt'],
69
+ properties: {
70
+ prompt: { type: 'string', description: 'The question to fan out' },
71
+ peers: { type: 'array', items: { type: 'string' }, description: 'Peer routing names to ask; null = all online peers' },
72
+ },
73
+ },
74
+ },
75
+ ];
76
+ export async function callTool(ctx, name, args) {
77
+ switch (name) {
78
+ case 'await_routed_prompt': {
79
+ const maxWaitMs = typeof args.maxWaitMs === 'number' && args.maxWaitMs > 0
80
+ ? Math.min(args.maxWaitMs, 60_000) // cap at 60s
81
+ : 0;
82
+ const entry = await awaitDequeue(maxWaitMs);
83
+ if (!entry)
84
+ return textContent(JSON.stringify({ chatId: null, text: null }));
85
+ return textContent(JSON.stringify({ chatId: entry.chatId, text: entry.text }));
86
+ }
87
+ case 'reply': {
88
+ const chatId = String(args.chat_id ?? '').trim();
89
+ const text = String(args.text ?? '');
90
+ if (!chatId)
91
+ return errorContent('chat_id is required');
92
+ if (!text)
93
+ return errorContent('text is required');
94
+ // Reply is a chat_event with chatId in the payload — hub uses
95
+ // it to correlate against any pending route_request waiting on
96
+ // this chatId.
97
+ ctx.client.send({
98
+ type: 'chat_event',
99
+ eventType: 'reply',
100
+ payload: { chatId, text },
101
+ ts: new Date().toISOString(),
102
+ });
103
+ return textContent('reply posted');
104
+ }
105
+ case 'list_peers': {
106
+ const correlationId = randomUUID();
107
+ try {
108
+ const peers = await ctx.client.requestSingle({ type: 'list_peers_request', correlationId }, LIST_TIMEOUT_MS);
109
+ if (peers.length === 0)
110
+ return textContent('no peers online');
111
+ const lines = peers.map((p) => `${p.online ? '●' : '○'} ${p.name} — @${p.login} (${p.online ? 'online' : 'offline'})`);
112
+ return textContent(lines.join('\n'));
113
+ }
114
+ catch (e) {
115
+ return errorContent(e?.message ?? 'list_peers failed');
116
+ }
117
+ }
118
+ case 'route_to_peer': {
119
+ const peer = String(args.peer ?? '').trim();
120
+ const prompt = String(args.prompt ?? '');
121
+ const mode = args.mode === 'tell' ? 'tell' : 'ask';
122
+ if (!peer)
123
+ return errorContent('peer is required');
124
+ if (!prompt)
125
+ return errorContent('prompt is required');
126
+ const correlationId = randomUUID();
127
+ if (mode === 'tell') {
128
+ ctx.client.send({ type: 'route_request', correlationId, peer, prompt, mode });
129
+ return textContent(`routed to ${peer} (tell mode — fire-and-forget)`);
130
+ }
131
+ try {
132
+ const result = await ctx.client.requestSingle({ type: 'route_request', correlationId, peer, prompt, mode }, ASK_TIMEOUT_MS);
133
+ return textContent(`@${result.peerLogin} replied:\n${result.reply}`);
134
+ }
135
+ catch (e) {
136
+ return errorContent(e?.message ?? 'route_to_peer failed');
137
+ }
138
+ }
139
+ case 'probe_peers': {
140
+ const prompt = String(args.prompt ?? '');
141
+ if (!prompt)
142
+ return errorContent('prompt is required');
143
+ const peersArg = Array.isArray(args.peers)
144
+ ? args.peers.filter((p) => typeof p === 'string')
145
+ : null;
146
+ const correlationId = randomUUID();
147
+ const results = await ctx.client.requestProbe({ type: 'probe_request', correlationId, peers: peersArg, prompt }, PROBE_TIMEOUT_MS);
148
+ if (results.length === 0)
149
+ return textContent('no peers responded within 30s');
150
+ const lines = results.map((r) => `@${r.peerLogin}: ${r.answer ?? '(no answer)'}`);
151
+ return textContent(lines.join('\n'));
152
+ }
153
+ default:
154
+ return errorContent(`unknown tool: ${name}`);
155
+ }
156
+ }
157
+ function textContent(text) {
158
+ return { content: [{ type: 'text', text }] };
159
+ }
160
+ function errorContent(text) {
161
+ return { isError: true, content: [{ type: 'text', text }] };
162
+ }
163
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ export interface TranscriptMessage {
2
+ type?: 'assistant' | 'user' | string;
3
+ message?: {
4
+ content?: ContentBlock[];
5
+ };
6
+ [k: string]: unknown;
7
+ }
8
+ export interface ContentBlock {
9
+ type?: 'text' | 'thinking' | 'tool_use' | 'tool_result' | string;
10
+ text?: string;
11
+ id?: string;
12
+ [k: string]: unknown;
13
+ }
14
+ export declare const DEFAULT_TAIL_BYTES: number;
15
+ export declare function readTranscriptMessages(path: string, tailBytes?: number): TranscriptMessage[];
16
+ export declare function extractTextBlocksBeforeToolUse(messages: TranscriptMessage[], toolUseId: string): string[];
17
+ export declare function messageContainsToolUse(messages: TranscriptMessage[], toolUseId: string): boolean;
18
+ export declare function hasThinkingBlocksBeforeToolUse(messages: TranscriptMessage[], toolUseId: string): boolean;
19
+ export declare function joinTextAfterLastToolUse(content: ContentBlock[] | unknown): string;
20
+ export declare function extractFromLastAssistantMessage(v: unknown): string;
21
+ export declare function extractFinalAnswerFromTranscript(path: string, tailBytes?: number): string;
@@ -0,0 +1,212 @@
1
+ // Tail-read and parse Claude Code's per-project JSONL transcript.
2
+ // Used by the hook subprocess to recover assistant text + extended-
3
+ // thinking presence at hook-fire time, since CC's hook payload alone
4
+ // doesn't carry that.
5
+ //
6
+ // Ported (with light edits for TS) from the original
7
+ // clawborrator-channel package's hook-template.mjs. Battle-tested
8
+ // against multi-MB transcripts; tail size is 256 KB by default which
9
+ // is enough to cover several recent turns even on tool-heavy sessions.
10
+ import { openSync, readSync, closeSync, statSync } from 'node:fs';
11
+ export const DEFAULT_TAIL_BYTES = 256 * 1024;
12
+ // Read the trailing portion of a Claude Code transcript JSONL and
13
+ // return parsed messages oldest-first. A partial first line (the tail
14
+ // boundary cuts mid-record) is dropped.
15
+ export function readTranscriptMessages(path, tailBytes = DEFAULT_TAIL_BYTES) {
16
+ try {
17
+ const stat = statSync(path);
18
+ const start = Math.max(0, stat.size - tailBytes);
19
+ const fd = openSync(path, 'r');
20
+ let raw;
21
+ try {
22
+ const buf = Buffer.alloc(stat.size - start);
23
+ readSync(fd, buf, 0, buf.length, start);
24
+ raw = buf.toString('utf8');
25
+ }
26
+ finally {
27
+ closeSync(fd);
28
+ }
29
+ if (!raw)
30
+ return [];
31
+ const lines = raw.split('\n').filter(Boolean);
32
+ if (start > 0 && lines.length > 0)
33
+ lines.shift(); // partial first line
34
+ return lines
35
+ .map((l) => { try {
36
+ return JSON.parse(l);
37
+ }
38
+ catch {
39
+ return null;
40
+ } })
41
+ .filter((m) => m !== null);
42
+ }
43
+ catch {
44
+ return [];
45
+ }
46
+ }
47
+ // Pull text blocks Claude wrote between the prior tool turn and the
48
+ // tool_use about to fire. CC stores each content-block kind in its
49
+ // own assistant message in the transcript JSONL — even though the
50
+ // API allows mixed content arrays, CC's recording splits them:
51
+ // assistant→text, assistant→thinking, assistant→tool_use,
52
+ // user→tool_result, assistant→text, ...
53
+ //
54
+ // Algorithm: find the assistant message that contains this tool_use,
55
+ // then walk BACKWARD through preceding assistant messages collecting
56
+ // text blocks. Stop at the first `user` message. Returns text blocks
57
+ // in chronological order (oldest first).
58
+ export function extractTextBlocksBeforeToolUse(messages, toolUseId) {
59
+ if (!toolUseId || !Array.isArray(messages))
60
+ return [];
61
+ let targetIdx = -1;
62
+ for (let i = messages.length - 1; i >= 0; i--) {
63
+ const m = messages[i];
64
+ if (m?.type !== 'assistant')
65
+ continue;
66
+ const content = m.message?.content;
67
+ if (!Array.isArray(content))
68
+ continue;
69
+ if (content.some((c) => c?.type === 'tool_use' && c.id === toolUseId)) {
70
+ targetIdx = i;
71
+ break;
72
+ }
73
+ }
74
+ if (targetIdx < 0)
75
+ return [];
76
+ const out = [];
77
+ for (let i = targetIdx - 1; i >= 0; i--) {
78
+ const m = messages[i];
79
+ if (m?.type === 'user')
80
+ break;
81
+ if (m?.type !== 'assistant')
82
+ continue;
83
+ const content = m.message?.content;
84
+ if (!Array.isArray(content))
85
+ continue;
86
+ // Walk THIS message's content array backward; unshift to keep the
87
+ // final list in chronological order.
88
+ for (let j = content.length - 1; j >= 0; j--) {
89
+ const c = content[j];
90
+ if (c?.type === 'text' && typeof c.text === 'string' && c.text.trim()) {
91
+ out.unshift(c.text);
92
+ }
93
+ }
94
+ }
95
+ return out;
96
+ }
97
+ // Quick presence check used by the PreToolUse race-retry loop.
98
+ // CC emits PreToolUse before fully flushing the assistant message
99
+ // that decided the tool_use to disk; we retry until we see it (or
100
+ // give up).
101
+ export function messageContainsToolUse(messages, toolUseId) {
102
+ if (!toolUseId || !Array.isArray(messages))
103
+ return false;
104
+ for (let i = messages.length - 1; i >= 0; i--) {
105
+ const m = messages[i];
106
+ if (m?.type !== 'assistant')
107
+ continue;
108
+ const content = m.message?.content;
109
+ if (!Array.isArray(content))
110
+ continue;
111
+ if (content.some((c) => c?.type === 'tool_use' && c.id === toolUseId))
112
+ return true;
113
+ }
114
+ return false;
115
+ }
116
+ // Same walk-back as extractTextBlocksBeforeToolUse, but returns true
117
+ // iff any thinking block exists between the prior user-message
118
+ // boundary and the target tool_use. Used to emit a placeholder
119
+ // "claude was thinking here" event when extended thinking happened
120
+ // but the plaintext wasn't persisted (CC strips it on disk; only the
121
+ // signature survives).
122
+ export function hasThinkingBlocksBeforeToolUse(messages, toolUseId) {
123
+ if (!toolUseId || !Array.isArray(messages))
124
+ return false;
125
+ let targetIdx = -1;
126
+ for (let i = messages.length - 1; i >= 0; i--) {
127
+ const m = messages[i];
128
+ if (m?.type !== 'assistant')
129
+ continue;
130
+ const content = m.message?.content;
131
+ if (!Array.isArray(content))
132
+ continue;
133
+ if (content.some((c) => c?.type === 'tool_use' && c.id === toolUseId)) {
134
+ targetIdx = i;
135
+ break;
136
+ }
137
+ }
138
+ if (targetIdx < 0)
139
+ return false;
140
+ for (let i = targetIdx - 1; i >= 0; i--) {
141
+ const m = messages[i];
142
+ if (m?.type === 'user')
143
+ break;
144
+ if (m?.type !== 'assistant')
145
+ continue;
146
+ const content = m.message?.content;
147
+ if (!Array.isArray(content))
148
+ continue;
149
+ if (content.some((c) => c?.type === 'thinking'))
150
+ return true;
151
+ }
152
+ return false;
153
+ }
154
+ // Walk a content array and return joined text from blocks AFTER the
155
+ // last tool_use. If no tool_use in the array, returns all text blocks
156
+ // joined. Used by Stop hook extraction to avoid double-shipping text
157
+ // that PreToolUse already shipped as intermediate AssistantText.
158
+ export function joinTextAfterLastToolUse(content) {
159
+ if (!Array.isArray(content))
160
+ return '';
161
+ let lastToolIdx = -1;
162
+ for (let j = content.length - 1; j >= 0; j--) {
163
+ if (content[j]?.type === 'tool_use') {
164
+ lastToolIdx = j;
165
+ break;
166
+ }
167
+ }
168
+ const parts = content.slice(lastToolIdx + 1)
169
+ .filter((c) => c?.type === 'text' && typeof c.text === 'string')
170
+ .map((c) => c.text);
171
+ return parts.join('\n').trim();
172
+ }
173
+ // Pull the FINAL-ANSWER text out of whatever shape CC hands us as
174
+ // `last_assistant_message` on Stop / SubagentStop. Documented as
175
+ // missing from the official input schema (CC issue #26710) so the
176
+ // shape drifts across versions.
177
+ export function extractFromLastAssistantMessage(v) {
178
+ if (!v)
179
+ return '';
180
+ if (typeof v === 'string')
181
+ return v.trim();
182
+ const obj = v;
183
+ const content = Array.isArray(obj.content) ? obj.content
184
+ : (obj.message && Array.isArray(obj.message.content)) ? obj.message.content
185
+ : null;
186
+ if (content)
187
+ return joinTextAfterLastToolUse(content);
188
+ if (typeof obj.text === 'string')
189
+ return obj.text.trim();
190
+ return '';
191
+ }
192
+ // Extract the final-answer assistant text from a transcript, given
193
+ // only the path. Walks backward to the LAST assistant message,
194
+ // returns trailing text blocks (after the last tool_use). Used by
195
+ // the Stop hook fallback when payload.last_assistant_message is
196
+ // absent or empty.
197
+ export function extractFinalAnswerFromTranscript(path, tailBytes = DEFAULT_TAIL_BYTES) {
198
+ const messages = readTranscriptMessages(path, tailBytes);
199
+ for (let i = messages.length - 1; i >= 0; i--) {
200
+ const m = messages[i];
201
+ if (m?.type !== 'assistant')
202
+ continue;
203
+ const content = m.message?.content;
204
+ if (!Array.isArray(content))
205
+ continue;
206
+ const joined = joinTextAfterLastToolUse(content);
207
+ if (joined)
208
+ return joined;
209
+ }
210
+ return '';
211
+ }
212
+ //# sourceMappingURL=transcript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript.js","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,oEAAoE;AACpE,qEAAqE;AACrE,sBAAsB;AACtB,EAAE;AACF,qDAAqD;AACrD,kEAAkE;AAClE,qEAAqE;AACrE,uEAAuE;AAEvE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAelE,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,GAAG,IAAI,CAAC;AAE7C,kEAAkE;AAClE,sEAAsE;AACtE,wCAAwC;AACxC,MAAM,UAAU,sBAAsB,CAAC,IAAY,EAAE,YAAoB,kBAAkB;IACzF,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;QACjD,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC/B,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;YAC5C,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACxC,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,EAAE,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,qBAAqB;QACvE,OAAO,KAAK;aACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC;YAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAsB,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC,CAAC,CAAC,CAAC;aACzF,MAAM,CAAC,CAAC,CAAC,EAA0B,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,mEAAmE;AACnE,kEAAkE;AAClE,+DAA+D;AAC/D,0DAA0D;AAC1D,wCAAwC;AACxC,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,qEAAqE;AACrE,yCAAyC;AACzC,MAAM,UAAU,8BAA8B,CAC5C,QAA6B,EAC7B,SAAiB;IAEjB,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IACtD,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,EAAE,IAAI,KAAK,WAAW;YAAE,SAAS;QACtC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC;QACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC;YACtE,SAAS,GAAG,CAAC,CAAC;YACd,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAE7B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,EAAE,IAAI,KAAK,MAAM;YAAE,MAAM;QAC9B,IAAI,CAAC,EAAE,IAAI,KAAK,WAAW;YAAE,SAAS;QACtC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC;QACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,kEAAkE;QAClE,qCAAqC;QACrC,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACrB,IAAI,CAAC,EAAE,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;gBACtE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,+DAA+D;AAC/D,kEAAkE;AAClE,kEAAkE;AAClE,YAAY;AACZ,MAAM,UAAU,sBAAsB,CAAC,QAA6B,EAAE,SAAiB;IACrF,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,EAAE,IAAI,KAAK,WAAW;YAAE,SAAS;QACtC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC;QACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;IACrF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,qEAAqE;AACrE,+DAA+D;AAC/D,+DAA+D;AAC/D,mEAAmE;AACnE,qEAAqE;AACrE,uBAAuB;AACvB,MAAM,UAAU,8BAA8B,CAC5C,QAA6B,EAC7B,SAAiB;IAEjB,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,EAAE,IAAI,KAAK,WAAW;YAAE,SAAS;QACtC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC;QACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC;YACtE,SAAS,GAAG,CAAC,CAAC;YACd,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,EAAE,IAAI,KAAK,MAAM;YAAE,MAAM;QAC9B,IAAI,CAAC,EAAE,IAAI,KAAK,WAAW;YAAE,SAAS;QACtC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC;QACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,UAAU,CAAC;YAAE,OAAO,IAAI,CAAC;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,oEAAoE;AACpE,sEAAsE;AACtE,qEAAqE;AACrE,iEAAiE;AACjE,MAAM,UAAU,wBAAwB,CAAC,OAAiC;IACxE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IACvC,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;YAAC,WAAW,GAAG,CAAC,CAAC;YAAC,MAAM;QAAC,CAAC;IAClE,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC;SACzC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;SAC/D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAc,CAAC,CAAC;IAChC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,kEAAkE;AAClE,iEAAiE;AACjE,kEAAkE;AAClE,gCAAgC;AAChC,MAAM,UAAU,+BAA+B,CAAC,CAAU;IACxD,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,MAAM,GAAG,GAAG,CAA4B,CAAC;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAwC,CAAC,CAAC,GAAG,CAAC,OAAO;QACjF,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAE,GAAG,CAAC,OAAiC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,OAAuC,CAAC,OAAO;YACvI,CAAC,CAAC,IAAI,CAAC;IACrB,IAAI,OAAO;QAAE,OAAO,wBAAwB,CAAC,OAAO,CAAC,CAAC;IACtD,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACzD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,mEAAmE;AACnE,+DAA+D;AAC/D,kEAAkE;AAClE,gEAAgE;AAChE,mBAAmB;AACnB,MAAM,UAAU,gCAAgC,CAAC,IAAY,EAAE,YAAoB,kBAAkB;IACnG,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACzD,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,EAAE,IAAI,KAAK,WAAW;YAAE,SAAS;QACtC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC;QACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,MAAM,MAAM,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;QACjD,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;IAC5B,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}