@rcrsr/rill-ext-claude-code 0.8.6 → 0.11.0
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 +7 -105
- package/dist/index.d.ts +255 -8
- package/dist/index.js +647 -20
- package/package.json +14 -9
- package/dist/factory.d.ts +0 -26
- package/dist/factory.d.ts.map +0 -1
- package/dist/factory.js +0 -376
- package/dist/factory.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/process.d.ts +0 -44
- package/dist/process.d.ts.map +0 -1
- package/dist/process.js +0 -125
- package/dist/process.js.map +0 -1
- package/dist/result.d.ts +0 -14
- package/dist/result.d.ts.map +0 -1
- package/dist/result.js +0 -93
- package/dist/result.js.map +0 -1
- package/dist/stream-parser.d.ts +0 -36
- package/dist/stream-parser.d.ts.map +0 -1
- package/dist/stream-parser.js +0 -96
- package/dist/stream-parser.js.map +0 -1
- package/dist/types.d.ts +0 -153
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -6
- package/dist/types.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,20 +1,647 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
// src/stream-parser.ts
|
|
2
|
+
var ANSI_ESCAPE_PATTERN = new RegExp(
|
|
3
|
+
[
|
|
4
|
+
// CSI sequences: ESC [ ... letter
|
|
5
|
+
`${String.fromCharCode(27)}\\[[0-9;]*[A-Za-z]`,
|
|
6
|
+
// OSC sequences: ESC ] ... BEL
|
|
7
|
+
`${String.fromCharCode(27)}\\][^${String.fromCharCode(7)}]*${String.fromCharCode(7)}`,
|
|
8
|
+
// ESC = and ESC >
|
|
9
|
+
`${String.fromCharCode(27)}[=>]`,
|
|
10
|
+
// Character set selection: ESC ( or ESC ) followed by code
|
|
11
|
+
`${String.fromCharCode(27)}[()][AB012]`
|
|
12
|
+
].join("|"),
|
|
13
|
+
"g"
|
|
14
|
+
);
|
|
15
|
+
function stripAnsi(text) {
|
|
16
|
+
return text.replace(ANSI_ESCAPE_PATTERN, "");
|
|
17
|
+
}
|
|
18
|
+
function createStreamParser() {
|
|
19
|
+
let buffer = "";
|
|
20
|
+
function processLine(line, onMessage) {
|
|
21
|
+
if (line.trim().length === 0) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const cleaned = line.trim().replace(/\[<u/g, "");
|
|
25
|
+
if (cleaned.length === 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(cleaned);
|
|
30
|
+
if (typeof parsed !== "object" || parsed === null || !("type" in parsed)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
onMessage(parsed);
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
processChunk(chunk, onMessage) {
|
|
39
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
40
|
+
buffer += text;
|
|
41
|
+
let newlineIndex;
|
|
42
|
+
while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
|
|
43
|
+
const rawLine = buffer.slice(0, newlineIndex);
|
|
44
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
45
|
+
const cleanLine = stripAnsi(rawLine);
|
|
46
|
+
processLine(cleanLine, onMessage);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
flush(onMessage) {
|
|
50
|
+
if (buffer.trim().length > 0) {
|
|
51
|
+
const cleanLine = stripAnsi(buffer);
|
|
52
|
+
processLine(cleanLine, onMessage);
|
|
53
|
+
buffer = "";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/result.ts
|
|
60
|
+
function extractResult(messages) {
|
|
61
|
+
const tokens = {
|
|
62
|
+
prompt: 0,
|
|
63
|
+
cacheWrite5m: 0,
|
|
64
|
+
cacheWrite1h: 0,
|
|
65
|
+
cacheRead: 0,
|
|
66
|
+
output: 0
|
|
67
|
+
};
|
|
68
|
+
const textParts = [];
|
|
69
|
+
let cost = 0;
|
|
70
|
+
let duration = 0;
|
|
71
|
+
let exitCode = 0;
|
|
72
|
+
for (const msg of messages) {
|
|
73
|
+
if (msg.type === "assistant") {
|
|
74
|
+
if (msg.message.usage) {
|
|
75
|
+
accumulateTokens(tokens, msg.message.usage);
|
|
76
|
+
}
|
|
77
|
+
for (const block of msg.message.content) {
|
|
78
|
+
if (block.type === "text") {
|
|
79
|
+
textParts.push(block.text);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} else if (msg.type === "result") {
|
|
83
|
+
cost = msg.cost_usd;
|
|
84
|
+
duration = msg.duration_ms;
|
|
85
|
+
exitCode = msg.is_error ? 1 : 0;
|
|
86
|
+
accumulateTokens(tokens, msg.usage);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
result: textParts.join(""),
|
|
91
|
+
tokens,
|
|
92
|
+
cost,
|
|
93
|
+
exitCode,
|
|
94
|
+
duration
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function accumulateTokens(tokens, usage) {
|
|
98
|
+
if (usage.input_tokens !== void 0) {
|
|
99
|
+
tokens.prompt += usage.input_tokens;
|
|
100
|
+
}
|
|
101
|
+
if (usage.output_tokens !== void 0) {
|
|
102
|
+
tokens.output += usage.output_tokens;
|
|
103
|
+
}
|
|
104
|
+
if (usage.cache_creation?.ephemeral_5m_input_tokens !== void 0) {
|
|
105
|
+
tokens.cacheWrite5m += usage.cache_creation.ephemeral_5m_input_tokens;
|
|
106
|
+
}
|
|
107
|
+
if (usage.cache_creation?.ephemeral_1h_input_tokens !== void 0) {
|
|
108
|
+
tokens.cacheWrite1h += usage.cache_creation.ephemeral_1h_input_tokens;
|
|
109
|
+
}
|
|
110
|
+
if (usage.cache_read_input_tokens !== void 0) {
|
|
111
|
+
tokens.cacheRead += usage.cache_read_input_tokens;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/process.ts
|
|
116
|
+
import * as pty from "node-pty";
|
|
117
|
+
import { RuntimeError } from "@rcrsr/rill";
|
|
118
|
+
function spawnClaudeCli(prompt, options = {}) {
|
|
119
|
+
const {
|
|
120
|
+
binaryPath = "claude",
|
|
121
|
+
timeoutMs,
|
|
122
|
+
cwd = process.cwd(),
|
|
123
|
+
env = process.env,
|
|
124
|
+
dangerouslySkipPermissions = true,
|
|
125
|
+
settingSources = ""
|
|
126
|
+
} = options;
|
|
127
|
+
const args = [
|
|
128
|
+
"-p",
|
|
129
|
+
prompt,
|
|
130
|
+
"--output-format",
|
|
131
|
+
"stream-json",
|
|
132
|
+
"--verbose",
|
|
133
|
+
"--no-session-persistence",
|
|
134
|
+
"--setting-sources",
|
|
135
|
+
settingSources
|
|
136
|
+
];
|
|
137
|
+
if (dangerouslySkipPermissions) {
|
|
138
|
+
args.push("--dangerously-skip-permissions");
|
|
139
|
+
}
|
|
140
|
+
let timeoutId;
|
|
141
|
+
let disposed = false;
|
|
142
|
+
let resolveExit;
|
|
143
|
+
let rejectExit;
|
|
144
|
+
const exitCode = new Promise((resolve, reject) => {
|
|
145
|
+
resolveExit = resolve;
|
|
146
|
+
rejectExit = reject;
|
|
147
|
+
});
|
|
148
|
+
let ptyProcess;
|
|
149
|
+
try {
|
|
150
|
+
ptyProcess = pty.spawn(binaryPath, args, {
|
|
151
|
+
name: "xterm-256color",
|
|
152
|
+
cols: 80,
|
|
153
|
+
rows: 30,
|
|
154
|
+
cwd,
|
|
155
|
+
env
|
|
156
|
+
});
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (error instanceof Error) {
|
|
159
|
+
const code = error.code;
|
|
160
|
+
if (code === "ENOENT") {
|
|
161
|
+
throw new RuntimeError(
|
|
162
|
+
"RILL-R004",
|
|
163
|
+
"claude binary not found",
|
|
164
|
+
void 0,
|
|
165
|
+
{ binaryPath }
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
if (code === "EACCES") {
|
|
169
|
+
throw new RuntimeError(
|
|
170
|
+
"RILL-R004",
|
|
171
|
+
"Permission denied: claude",
|
|
172
|
+
void 0,
|
|
173
|
+
{ binaryPath }
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
throw new RuntimeError(
|
|
177
|
+
"RILL-R004",
|
|
178
|
+
`Failed to spawn claude binary: ${error.message}`,
|
|
179
|
+
void 0,
|
|
180
|
+
{ binaryPath, originalError: error.message }
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
throw new RuntimeError(
|
|
184
|
+
"RILL-R004",
|
|
185
|
+
`Failed to spawn claude binary: Unknown error`,
|
|
186
|
+
void 0,
|
|
187
|
+
{ binaryPath }
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
if (timeoutMs !== void 0 && timeoutMs > 0) {
|
|
191
|
+
timeoutId = setTimeout(() => {
|
|
192
|
+
if (!disposed) {
|
|
193
|
+
disposed = true;
|
|
194
|
+
ptyProcess.kill();
|
|
195
|
+
rejectExit(
|
|
196
|
+
new RuntimeError(
|
|
197
|
+
"RILL-R004",
|
|
198
|
+
`Claude CLI timeout after ${timeoutMs}ms`,
|
|
199
|
+
void 0,
|
|
200
|
+
{ timeoutMs }
|
|
201
|
+
)
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}, timeoutMs);
|
|
205
|
+
}
|
|
206
|
+
ptyProcess.onExit((event) => {
|
|
207
|
+
if (timeoutId) {
|
|
208
|
+
clearTimeout(timeoutId);
|
|
209
|
+
}
|
|
210
|
+
if (!disposed) {
|
|
211
|
+
disposed = true;
|
|
212
|
+
const { exitCode: code } = event;
|
|
213
|
+
if (code !== 0) {
|
|
214
|
+
rejectExit(
|
|
215
|
+
new RuntimeError(
|
|
216
|
+
"RILL-R004",
|
|
217
|
+
`Claude CLI exited with code ${code}`,
|
|
218
|
+
void 0,
|
|
219
|
+
{ exitCode: code }
|
|
220
|
+
)
|
|
221
|
+
);
|
|
222
|
+
} else {
|
|
223
|
+
resolveExit(code);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
const dispose = () => {
|
|
228
|
+
if (disposed) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
disposed = true;
|
|
232
|
+
if (timeoutId) {
|
|
233
|
+
clearTimeout(timeoutId);
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
ptyProcess.kill();
|
|
237
|
+
} catch (error) {
|
|
238
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
239
|
+
console.warn(`Failed to kill claude process during cleanup: ${message}`);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
return {
|
|
243
|
+
ptyProcess,
|
|
244
|
+
exitCode,
|
|
245
|
+
dispose
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// src/factory.ts
|
|
250
|
+
import which from "which";
|
|
251
|
+
import {
|
|
252
|
+
RuntimeError as RuntimeError3,
|
|
253
|
+
emitExtensionEvent
|
|
254
|
+
} from "@rcrsr/rill";
|
|
255
|
+
|
|
256
|
+
// ../../shared/ext-param/dist/param.js
|
|
257
|
+
import { RuntimeError as RuntimeError2 } from "@rcrsr/rill";
|
|
258
|
+
function validateParamName(name) {
|
|
259
|
+
if (name === "") {
|
|
260
|
+
throw new RuntimeError2("RILL-R001", "param name must not be empty");
|
|
261
|
+
}
|
|
262
|
+
if (/\s/.test(name)) {
|
|
263
|
+
throw new RuntimeError2("RILL-R001", "param name must be a valid identifier");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function buildAnnotations(desc) {
|
|
267
|
+
if (desc !== void 0) {
|
|
268
|
+
return { description: desc };
|
|
269
|
+
}
|
|
270
|
+
return {};
|
|
271
|
+
}
|
|
272
|
+
var p = {
|
|
273
|
+
/**
|
|
274
|
+
* IR-1: Creates a string parameter descriptor.
|
|
275
|
+
*
|
|
276
|
+
* @param name - Parameter name (must be a valid identifier)
|
|
277
|
+
* @param desc - Optional description
|
|
278
|
+
* @returns RillParam with type 'string'
|
|
279
|
+
*/
|
|
280
|
+
str(name, desc) {
|
|
281
|
+
validateParamName(name);
|
|
282
|
+
return {
|
|
283
|
+
name,
|
|
284
|
+
type: { type: "string" },
|
|
285
|
+
defaultValue: void 0,
|
|
286
|
+
annotations: buildAnnotations(desc)
|
|
287
|
+
};
|
|
288
|
+
},
|
|
289
|
+
/**
|
|
290
|
+
* IR-2: Creates a number parameter descriptor.
|
|
291
|
+
*
|
|
292
|
+
* @param name - Parameter name (must be a valid identifier)
|
|
293
|
+
* @param desc - Optional description
|
|
294
|
+
* @param def - Optional default value
|
|
295
|
+
* @returns RillParam with type 'number'
|
|
296
|
+
*/
|
|
297
|
+
num(name, desc, def) {
|
|
298
|
+
validateParamName(name);
|
|
299
|
+
return {
|
|
300
|
+
name,
|
|
301
|
+
type: { type: "number" },
|
|
302
|
+
defaultValue: def,
|
|
303
|
+
annotations: buildAnnotations(desc)
|
|
304
|
+
};
|
|
305
|
+
},
|
|
306
|
+
/**
|
|
307
|
+
* IR-3: Creates a boolean parameter descriptor.
|
|
308
|
+
*
|
|
309
|
+
* @param name - Parameter name (must be a valid identifier)
|
|
310
|
+
* @param desc - Optional description
|
|
311
|
+
* @param def - Optional default value
|
|
312
|
+
* @returns RillParam with type 'bool'
|
|
313
|
+
*/
|
|
314
|
+
bool(name, desc, def) {
|
|
315
|
+
validateParamName(name);
|
|
316
|
+
return {
|
|
317
|
+
name,
|
|
318
|
+
type: { type: "bool" },
|
|
319
|
+
defaultValue: def,
|
|
320
|
+
annotations: buildAnnotations(desc)
|
|
321
|
+
};
|
|
322
|
+
},
|
|
323
|
+
/**
|
|
324
|
+
* IR-4: Creates a dict parameter descriptor.
|
|
325
|
+
*
|
|
326
|
+
* @param name - Parameter name (must be a valid identifier)
|
|
327
|
+
* @param desc - Optional description
|
|
328
|
+
* @param def - Optional default value
|
|
329
|
+
* @returns RillParam with type 'dict'
|
|
330
|
+
*/
|
|
331
|
+
dict(name, desc, def) {
|
|
332
|
+
validateParamName(name);
|
|
333
|
+
return {
|
|
334
|
+
name,
|
|
335
|
+
type: { type: "dict" },
|
|
336
|
+
defaultValue: def,
|
|
337
|
+
annotations: buildAnnotations(desc)
|
|
338
|
+
};
|
|
339
|
+
},
|
|
340
|
+
/**
|
|
341
|
+
* IR-5: Creates a list parameter descriptor.
|
|
342
|
+
*
|
|
343
|
+
* @param name - Parameter name (must be a valid identifier)
|
|
344
|
+
* @param itemType - Optional element type; omitted when not provided
|
|
345
|
+
* @param desc - Optional description
|
|
346
|
+
* @returns RillParam with type 'list' (with element if itemType provided)
|
|
347
|
+
*/
|
|
348
|
+
list(name, itemType, desc) {
|
|
349
|
+
validateParamName(name);
|
|
350
|
+
const type = itemType !== void 0 ? { type: "list", element: itemType } : { type: "list" };
|
|
351
|
+
return {
|
|
352
|
+
name,
|
|
353
|
+
type,
|
|
354
|
+
defaultValue: void 0,
|
|
355
|
+
annotations: buildAnnotations(desc)
|
|
356
|
+
};
|
|
357
|
+
},
|
|
358
|
+
/**
|
|
359
|
+
* IR-6: Creates a callable parameter descriptor.
|
|
360
|
+
*
|
|
361
|
+
* @param name - Parameter name (must be a valid identifier)
|
|
362
|
+
* @param desc - Optional description
|
|
363
|
+
* @returns RillParam with type 'closure'
|
|
364
|
+
*/
|
|
365
|
+
callable(name, desc) {
|
|
366
|
+
validateParamName(name);
|
|
367
|
+
return {
|
|
368
|
+
name,
|
|
369
|
+
type: { type: "closure" },
|
|
370
|
+
defaultValue: void 0,
|
|
371
|
+
annotations: buildAnnotations(desc)
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// src/factory.ts
|
|
377
|
+
var DEFAULT_BINARY_PATH = "claude";
|
|
378
|
+
var DEFAULT_TIMEOUT = 18e5;
|
|
379
|
+
var MAX_TIMEOUT = 36e5;
|
|
380
|
+
function serializeArgsToFlags(args) {
|
|
381
|
+
const flags = [];
|
|
382
|
+
for (const [key, value] of Object.entries(args)) {
|
|
383
|
+
if (value === true) {
|
|
384
|
+
flags.push(`--${key}`);
|
|
385
|
+
} else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
386
|
+
const nested = value;
|
|
387
|
+
for (const [nestedKey, nestedValue] of Object.entries(nested)) {
|
|
388
|
+
flags.push(`--${key}.${nestedKey}`, String(nestedValue));
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
flags.push(`--${key}`, String(value));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return flags;
|
|
395
|
+
}
|
|
396
|
+
function truncateText(text, maxLength = 100) {
|
|
397
|
+
if (text.length <= maxLength) {
|
|
398
|
+
return text;
|
|
399
|
+
}
|
|
400
|
+
return text.slice(0, maxLength) + "...";
|
|
401
|
+
}
|
|
402
|
+
function validateTimeout(timeout) {
|
|
403
|
+
if (!Number.isInteger(timeout)) {
|
|
404
|
+
throw new Error("Invalid timeout: must be positive integer, max 3600000");
|
|
405
|
+
}
|
|
406
|
+
if (timeout <= 0) {
|
|
407
|
+
throw new Error("Invalid timeout: must be positive integer, max 3600000");
|
|
408
|
+
}
|
|
409
|
+
if (timeout > MAX_TIMEOUT) {
|
|
410
|
+
throw new Error("Invalid timeout: must be positive integer, max 3600000");
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function createClaudeCodeExtension(config = {}) {
|
|
414
|
+
const binaryPath = config.binaryPath ?? DEFAULT_BINARY_PATH;
|
|
415
|
+
const defaultTimeout = config.defaultTimeout ?? DEFAULT_TIMEOUT;
|
|
416
|
+
const dangerouslySkipPermissions = config.dangerouslySkipPermissions ?? true;
|
|
417
|
+
const settingSources = config.settingSources ?? "";
|
|
418
|
+
validateTimeout(defaultTimeout);
|
|
419
|
+
try {
|
|
420
|
+
which.sync(binaryPath);
|
|
421
|
+
} catch {
|
|
422
|
+
throw new Error(`Binary not found: ${binaryPath}`);
|
|
423
|
+
}
|
|
424
|
+
const tracker = {
|
|
425
|
+
disposers: /* @__PURE__ */ new Set()
|
|
426
|
+
};
|
|
427
|
+
const dispose = () => {
|
|
428
|
+
for (const disposer of tracker.disposers) {
|
|
429
|
+
try {
|
|
430
|
+
disposer();
|
|
431
|
+
} catch (error) {
|
|
432
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
433
|
+
console.warn(`Failed to cleanup process: ${message}`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
tracker.disposers.clear();
|
|
437
|
+
};
|
|
438
|
+
const result = {
|
|
439
|
+
// IR-2: claude-code::prompt
|
|
440
|
+
prompt: {
|
|
441
|
+
params: [
|
|
442
|
+
p.str("text"),
|
|
443
|
+
p.dict("options", void 0, {})
|
|
444
|
+
],
|
|
445
|
+
fn: async (args, ctx) => {
|
|
446
|
+
const startTime = Date.now();
|
|
447
|
+
try {
|
|
448
|
+
const text = args[0];
|
|
449
|
+
const options = args[1] ?? {};
|
|
450
|
+
if (text.trim().length === 0) {
|
|
451
|
+
throw new RuntimeError3("RILL-R004", "prompt text cannot be empty");
|
|
452
|
+
}
|
|
453
|
+
const timeout = typeof options["timeout"] === "number" ? options["timeout"] : defaultTimeout;
|
|
454
|
+
const spawn2 = spawnClaudeCli(text, {
|
|
455
|
+
binaryPath,
|
|
456
|
+
timeoutMs: timeout,
|
|
457
|
+
dangerouslySkipPermissions,
|
|
458
|
+
settingSources
|
|
459
|
+
});
|
|
460
|
+
tracker.disposers.add(spawn2.dispose);
|
|
461
|
+
const parser = createStreamParser();
|
|
462
|
+
const messages = [];
|
|
463
|
+
spawn2.ptyProcess.onData((chunk) => {
|
|
464
|
+
parser.processChunk(chunk, (msg) => messages.push(msg));
|
|
465
|
+
});
|
|
466
|
+
try {
|
|
467
|
+
await spawn2.exitCode;
|
|
468
|
+
parser.flush((msg) => messages.push(msg));
|
|
469
|
+
} finally {
|
|
470
|
+
tracker.disposers.delete(spawn2.dispose);
|
|
471
|
+
spawn2.dispose();
|
|
472
|
+
}
|
|
473
|
+
const result2 = extractResult(messages);
|
|
474
|
+
const duration = Date.now() - startTime;
|
|
475
|
+
emitExtensionEvent(ctx, {
|
|
476
|
+
event: "claude-code:prompt",
|
|
477
|
+
subsystem: "extension:claude-code",
|
|
478
|
+
prompt: truncateText(text),
|
|
479
|
+
duration
|
|
480
|
+
});
|
|
481
|
+
return {
|
|
482
|
+
...result2,
|
|
483
|
+
tokens: { ...result2.tokens }
|
|
484
|
+
};
|
|
485
|
+
} catch (error) {
|
|
486
|
+
const duration = Date.now() - startTime;
|
|
487
|
+
emitExtensionEvent(ctx, {
|
|
488
|
+
event: "claude-code:error",
|
|
489
|
+
subsystem: "extension:claude-code",
|
|
490
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
491
|
+
duration
|
|
492
|
+
});
|
|
493
|
+
throw error;
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
description: "Execute Claude Code prompt and return result text and token usage",
|
|
497
|
+
returnType: { type: "dict" }
|
|
498
|
+
},
|
|
499
|
+
// IR-3: claude-code::skill
|
|
500
|
+
skill: {
|
|
501
|
+
params: [
|
|
502
|
+
p.str("name"),
|
|
503
|
+
p.dict("args", void 0, {})
|
|
504
|
+
],
|
|
505
|
+
fn: async (fnArgs, ctx) => {
|
|
506
|
+
const startTime = Date.now();
|
|
507
|
+
try {
|
|
508
|
+
const name = fnArgs[0];
|
|
509
|
+
const args = fnArgs[1] ?? {};
|
|
510
|
+
if (name.trim().length === 0) {
|
|
511
|
+
throw new RuntimeError3("RILL-R004", "skill name cannot be empty");
|
|
512
|
+
}
|
|
513
|
+
const flags = serializeArgsToFlags(args);
|
|
514
|
+
const flagsText = flags.length > 0 ? " " + flags.join(" ") : "";
|
|
515
|
+
const prompt = `/${name}${flagsText}`;
|
|
516
|
+
const timeout = typeof args["timeout"] === "number" ? args["timeout"] : defaultTimeout;
|
|
517
|
+
const spawn2 = spawnClaudeCli(prompt, {
|
|
518
|
+
binaryPath,
|
|
519
|
+
timeoutMs: timeout,
|
|
520
|
+
dangerouslySkipPermissions,
|
|
521
|
+
settingSources
|
|
522
|
+
});
|
|
523
|
+
tracker.disposers.add(spawn2.dispose);
|
|
524
|
+
const parser = createStreamParser();
|
|
525
|
+
const messages = [];
|
|
526
|
+
spawn2.ptyProcess.onData((chunk) => {
|
|
527
|
+
parser.processChunk(chunk, (msg) => messages.push(msg));
|
|
528
|
+
});
|
|
529
|
+
try {
|
|
530
|
+
await spawn2.exitCode;
|
|
531
|
+
parser.flush((msg) => messages.push(msg));
|
|
532
|
+
} finally {
|
|
533
|
+
tracker.disposers.delete(spawn2.dispose);
|
|
534
|
+
spawn2.dispose();
|
|
535
|
+
}
|
|
536
|
+
const result2 = extractResult(messages);
|
|
537
|
+
const duration = Date.now() - startTime;
|
|
538
|
+
emitExtensionEvent(ctx, {
|
|
539
|
+
event: "claude-code:skill",
|
|
540
|
+
subsystem: "extension:claude-code",
|
|
541
|
+
name,
|
|
542
|
+
args,
|
|
543
|
+
duration
|
|
544
|
+
});
|
|
545
|
+
return {
|
|
546
|
+
...result2,
|
|
547
|
+
tokens: { ...result2.tokens }
|
|
548
|
+
};
|
|
549
|
+
} catch (error) {
|
|
550
|
+
const duration = Date.now() - startTime;
|
|
551
|
+
emitExtensionEvent(ctx, {
|
|
552
|
+
event: "claude-code:error",
|
|
553
|
+
subsystem: "extension:claude-code",
|
|
554
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
555
|
+
duration
|
|
556
|
+
});
|
|
557
|
+
throw error;
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
description: "Execute Claude Code skill with instruction and return structured result",
|
|
561
|
+
returnType: { type: "dict" }
|
|
562
|
+
},
|
|
563
|
+
// IR-4: claude-code::command
|
|
564
|
+
command: {
|
|
565
|
+
params: [
|
|
566
|
+
p.str("name"),
|
|
567
|
+
p.dict("args", void 0, {})
|
|
568
|
+
],
|
|
569
|
+
fn: async (fnArgs, ctx) => {
|
|
570
|
+
const startTime = Date.now();
|
|
571
|
+
try {
|
|
572
|
+
const name = fnArgs[0];
|
|
573
|
+
const args = fnArgs[1] ?? {};
|
|
574
|
+
if (name.trim().length === 0) {
|
|
575
|
+
throw new RuntimeError3("RILL-R004", "command name cannot be empty");
|
|
576
|
+
}
|
|
577
|
+
const flags = serializeArgsToFlags(args);
|
|
578
|
+
const flagsText = flags.length > 0 ? " " + flags.join(" ") : "";
|
|
579
|
+
const prompt = `/${name}${flagsText}`;
|
|
580
|
+
const timeout = typeof args["timeout"] === "number" ? args["timeout"] : defaultTimeout;
|
|
581
|
+
const spawn2 = spawnClaudeCli(prompt, {
|
|
582
|
+
binaryPath,
|
|
583
|
+
timeoutMs: timeout,
|
|
584
|
+
dangerouslySkipPermissions,
|
|
585
|
+
settingSources
|
|
586
|
+
});
|
|
587
|
+
tracker.disposers.add(spawn2.dispose);
|
|
588
|
+
const parser = createStreamParser();
|
|
589
|
+
const messages = [];
|
|
590
|
+
spawn2.ptyProcess.onData((chunk) => {
|
|
591
|
+
parser.processChunk(chunk, (msg) => messages.push(msg));
|
|
592
|
+
});
|
|
593
|
+
try {
|
|
594
|
+
await spawn2.exitCode;
|
|
595
|
+
parser.flush((msg) => messages.push(msg));
|
|
596
|
+
} finally {
|
|
597
|
+
tracker.disposers.delete(spawn2.dispose);
|
|
598
|
+
spawn2.dispose();
|
|
599
|
+
}
|
|
600
|
+
const result2 = extractResult(messages);
|
|
601
|
+
const duration = Date.now() - startTime;
|
|
602
|
+
emitExtensionEvent(ctx, {
|
|
603
|
+
event: "claude-code:command",
|
|
604
|
+
subsystem: "extension:claude-code",
|
|
605
|
+
name,
|
|
606
|
+
args,
|
|
607
|
+
duration
|
|
608
|
+
});
|
|
609
|
+
return {
|
|
610
|
+
...result2,
|
|
611
|
+
tokens: { ...result2.tokens }
|
|
612
|
+
};
|
|
613
|
+
} catch (error) {
|
|
614
|
+
const duration = Date.now() - startTime;
|
|
615
|
+
emitExtensionEvent(ctx, {
|
|
616
|
+
event: "claude-code:error",
|
|
617
|
+
subsystem: "extension:claude-code",
|
|
618
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
619
|
+
duration
|
|
620
|
+
});
|
|
621
|
+
throw error;
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
description: "Execute Claude Code command with task description and return execution summary",
|
|
625
|
+
returnType: { type: "dict" }
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
result.dispose = dispose;
|
|
629
|
+
return result;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/index.ts
|
|
633
|
+
var VERSION = "0.1.0";
|
|
634
|
+
var configSchema = {
|
|
635
|
+
binaryPath: { type: "string" },
|
|
636
|
+
defaultTimeout: { type: "number" },
|
|
637
|
+
dangerouslySkipPermissions: { type: "boolean" },
|
|
638
|
+
settingSources: { type: "string" }
|
|
639
|
+
};
|
|
640
|
+
export {
|
|
641
|
+
VERSION,
|
|
642
|
+
configSchema,
|
|
643
|
+
createClaudeCodeExtension,
|
|
644
|
+
createStreamParser,
|
|
645
|
+
extractResult,
|
|
646
|
+
spawnClaudeCli
|
|
647
|
+
};
|