@punkcode/cli 0.1.5 → 0.1.6
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/cli.js +142 -60
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -92,6 +92,7 @@ function runClaude(options, callbacks) {
|
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
(async () => {
|
|
95
|
+
let sentCompactSummary = false;
|
|
95
96
|
try {
|
|
96
97
|
for await (const message of q) {
|
|
97
98
|
switch (message.type) {
|
|
@@ -118,7 +119,19 @@ function runClaude(options, callbacks) {
|
|
|
118
119
|
}
|
|
119
120
|
case "user": {
|
|
120
121
|
const userContent = message.message?.content;
|
|
121
|
-
if (
|
|
122
|
+
if (typeof userContent === "string") {
|
|
123
|
+
const match = userContent.match(/<local-command-stdout>([\s\S]*?)<\/local-command-stdout>/);
|
|
124
|
+
if (match) {
|
|
125
|
+
if (sentCompactSummary && match[1].trim() === "Compacted") {
|
|
126
|
+
sentCompactSummary = false;
|
|
127
|
+
} else {
|
|
128
|
+
callbacks.onSlashCommandOutput?.(match[1]);
|
|
129
|
+
}
|
|
130
|
+
} else if (userContent.startsWith("This session is being continued")) {
|
|
131
|
+
sentCompactSummary = true;
|
|
132
|
+
callbacks.onSlashCommandOutput?.(userContent);
|
|
133
|
+
}
|
|
134
|
+
} else if (Array.isArray(userContent)) {
|
|
122
135
|
for (const block of userContent) {
|
|
123
136
|
if (typeof block === "object" && block !== null && "type" in block && block.type === "tool_result") {
|
|
124
137
|
const tr = block;
|
|
@@ -132,10 +145,35 @@ function runClaude(options, callbacks) {
|
|
|
132
145
|
}
|
|
133
146
|
break;
|
|
134
147
|
}
|
|
135
|
-
case "
|
|
136
|
-
|
|
148
|
+
case "system": {
|
|
149
|
+
const sys = message;
|
|
150
|
+
if (sys.subtype === "init" && callbacks.onSessionCreated) {
|
|
151
|
+
callbacks.onSessionCreated({
|
|
152
|
+
sessionId: sys.session_id ?? "",
|
|
153
|
+
tools: sys.tools ?? [],
|
|
154
|
+
slashCommands: (sys.slash_commands ?? []).map((cmd) => ({ name: cmd, description: "" })),
|
|
155
|
+
skills: sys.skills ?? [],
|
|
156
|
+
mcpServers: sys.mcp_servers ?? [],
|
|
157
|
+
model: sys.model ?? "",
|
|
158
|
+
cwd: sys.cwd ?? "",
|
|
159
|
+
claudeCodeVersion: sys.claude_code_version ?? "",
|
|
160
|
+
permissionMode: sys.permissionMode ?? "default"
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
case "tool_use_summary": {
|
|
166
|
+
const summary = message.summary;
|
|
167
|
+
if (summary) {
|
|
168
|
+
callbacks.onSlashCommandOutput?.(summary);
|
|
169
|
+
}
|
|
137
170
|
break;
|
|
138
171
|
}
|
|
172
|
+
case "result": {
|
|
173
|
+
const resultText = message.subtype === "success" ? message.result : void 0;
|
|
174
|
+
callbacks.onResult(message.session_id, resultText);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
139
177
|
}
|
|
140
178
|
}
|
|
141
179
|
} catch (err) {
|
|
@@ -300,6 +338,9 @@ import { readdir, readFile, stat, open } from "fs/promises";
|
|
|
300
338
|
import { join } from "path";
|
|
301
339
|
import { homedir } from "os";
|
|
302
340
|
var CLAUDE_DIR = join(homedir(), ".claude", "projects");
|
|
341
|
+
function pathToProjectDir(dir) {
|
|
342
|
+
return dir.replace(/\//g, "-");
|
|
343
|
+
}
|
|
303
344
|
async function loadSession(sessionId) {
|
|
304
345
|
const sessionFile = `${sessionId}.jsonl`;
|
|
305
346
|
let projectDirs;
|
|
@@ -322,10 +363,14 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
322
363
|
function isValidSessionUUID(name) {
|
|
323
364
|
return UUID_RE.test(name);
|
|
324
365
|
}
|
|
325
|
-
async function listSessions() {
|
|
366
|
+
async function listSessions(workingDirectory) {
|
|
326
367
|
let projectDirs;
|
|
327
368
|
try {
|
|
328
|
-
|
|
369
|
+
if (workingDirectory) {
|
|
370
|
+
projectDirs = [pathToProjectDir(workingDirectory)];
|
|
371
|
+
} else {
|
|
372
|
+
projectDirs = await readdir(CLAUDE_DIR);
|
|
373
|
+
}
|
|
329
374
|
} catch {
|
|
330
375
|
return [];
|
|
331
376
|
}
|
|
@@ -353,16 +398,12 @@ async function listSessions() {
|
|
|
353
398
|
candidates.map(async (c) => {
|
|
354
399
|
try {
|
|
355
400
|
const fileStat = await stat(c.filePath);
|
|
356
|
-
const
|
|
357
|
-
extractTitle(c.filePath),
|
|
358
|
-
countTurns(c.filePath)
|
|
359
|
-
]);
|
|
401
|
+
const titleInfo = await extractTitle(c.filePath);
|
|
360
402
|
return {
|
|
361
403
|
sessionId: c.sessionId,
|
|
362
404
|
project: c.project,
|
|
363
405
|
title: titleInfo.title,
|
|
364
406
|
lastModified: fileStat.mtimeMs,
|
|
365
|
-
turnCount,
|
|
366
407
|
...titleInfo.summary && { summary: titleInfo.summary }
|
|
367
408
|
};
|
|
368
409
|
} catch {
|
|
@@ -429,7 +470,9 @@ async function extractFirstUserMessage(fh) {
|
|
|
429
470
|
try {
|
|
430
471
|
const entry = JSON.parse(line);
|
|
431
472
|
if (entry.isMeta || entry.parentUuid && metaUuids.has(entry.parentUuid)) {
|
|
432
|
-
if (entry.uuid
|
|
473
|
+
if (entry.uuid && entry.message?.role !== "assistant") {
|
|
474
|
+
metaUuids.add(entry.uuid);
|
|
475
|
+
}
|
|
433
476
|
continue;
|
|
434
477
|
}
|
|
435
478
|
if (entry.type === "user" && entry.message?.role === "user") {
|
|
@@ -439,9 +482,9 @@ async function extractFirstUserMessage(fh) {
|
|
|
439
482
|
}
|
|
440
483
|
if (Array.isArray(content)) {
|
|
441
484
|
const textBlock = content.find(
|
|
442
|
-
(b) => b.type === "text" && b.text && !b.text.startsWith("[Request interrupted")
|
|
485
|
+
(b) => b.type === "text" && "text" in b && b.text && !b.text.startsWith("[Request interrupted")
|
|
443
486
|
);
|
|
444
|
-
if (textBlock
|
|
487
|
+
if (textBlock && "text" in textBlock && typeof textBlock.text === "string") {
|
|
445
488
|
return textBlock.text.slice(0, 100);
|
|
446
489
|
}
|
|
447
490
|
}
|
|
@@ -451,41 +494,9 @@ async function extractFirstUserMessage(fh) {
|
|
|
451
494
|
}
|
|
452
495
|
return "Untitled session";
|
|
453
496
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
let count = 0;
|
|
458
|
-
const metaUuids = /* @__PURE__ */ new Set();
|
|
459
|
-
for (const line of lines) {
|
|
460
|
-
if (!line.trim()) continue;
|
|
461
|
-
try {
|
|
462
|
-
const entry = JSON.parse(line);
|
|
463
|
-
if (entry.isMeta || entry.parentUuid && metaUuids.has(entry.parentUuid)) {
|
|
464
|
-
if (entry.uuid) metaUuids.add(entry.uuid);
|
|
465
|
-
continue;
|
|
466
|
-
}
|
|
467
|
-
if (entry.type !== "user" || !entry.message || entry.message.role !== "user") continue;
|
|
468
|
-
const msgContent = entry.message.content;
|
|
469
|
-
if (typeof msgContent === "string") {
|
|
470
|
-
const trimmed = msgContent.trim();
|
|
471
|
-
if (trimmed && !trimmed.startsWith("<") && !trimmed.startsWith("[") && !trimmed.startsWith("/")) {
|
|
472
|
-
count++;
|
|
473
|
-
}
|
|
474
|
-
} else if (Array.isArray(msgContent)) {
|
|
475
|
-
const textBlock = msgContent.find(
|
|
476
|
-
(b) => b.type === "text" && b.text
|
|
477
|
-
);
|
|
478
|
-
if (textBlock?.text) {
|
|
479
|
-
const text = textBlock.text.trim();
|
|
480
|
-
if (text && !text.startsWith("<") && !text.startsWith("[") && !text.startsWith("/")) {
|
|
481
|
-
count++;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
} catch {
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
return count;
|
|
497
|
+
var ANSI_RE = /\u001b\[\d*m/g;
|
|
498
|
+
function stripAnsi(text) {
|
|
499
|
+
return text.replace(ANSI_RE, "");
|
|
489
500
|
}
|
|
490
501
|
function parseSessionFile(content) {
|
|
491
502
|
const messages = [];
|
|
@@ -495,7 +506,51 @@ function parseSessionFile(content) {
|
|
|
495
506
|
try {
|
|
496
507
|
const entry = JSON.parse(line);
|
|
497
508
|
if (entry.isMeta || entry.parentUuid && metaUuids.has(entry.parentUuid)) {
|
|
498
|
-
if (entry.uuid
|
|
509
|
+
if (entry.uuid && entry.message?.role !== "assistant") {
|
|
510
|
+
metaUuids.add(entry.uuid);
|
|
511
|
+
}
|
|
512
|
+
if (entry.message?.role === "user" && typeof entry.message.content === "string") {
|
|
513
|
+
const content2 = entry.message.content;
|
|
514
|
+
const cmdMatch = content2.match(/<command-name>\/(.+?)<\/command-name>/);
|
|
515
|
+
if (cmdMatch) {
|
|
516
|
+
messages.push({
|
|
517
|
+
role: "user",
|
|
518
|
+
content: [{ type: "text", text: `/${cmdMatch[1]}` }],
|
|
519
|
+
timestamp: entry.timestamp,
|
|
520
|
+
isMeta: entry.isMeta,
|
|
521
|
+
uuid: entry.uuid,
|
|
522
|
+
parentUuid: entry.parentUuid,
|
|
523
|
+
type: entry.type
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
const outMatch = content2.match(/<local-command-stdout>([\s\S]*?)<\/local-command-stdout>/);
|
|
527
|
+
if (outMatch && outMatch[1].trim()) {
|
|
528
|
+
const text = stripAnsi(outMatch[1].trim());
|
|
529
|
+
if (text) {
|
|
530
|
+
messages.push({
|
|
531
|
+
role: "assistant",
|
|
532
|
+
content: [{ type: "text", text }],
|
|
533
|
+
timestamp: entry.timestamp,
|
|
534
|
+
isMeta: entry.isMeta,
|
|
535
|
+
uuid: entry.uuid,
|
|
536
|
+
parentUuid: entry.parentUuid,
|
|
537
|
+
type: entry.type
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
if (entry.type === "system" && entry.subtype === "compact_boundary") {
|
|
545
|
+
messages.push({
|
|
546
|
+
role: "assistant",
|
|
547
|
+
content: [],
|
|
548
|
+
timestamp: entry.timestamp,
|
|
549
|
+
type: "system",
|
|
550
|
+
subtype: entry.subtype,
|
|
551
|
+
uuid: entry.uuid,
|
|
552
|
+
parentUuid: entry.parentUuid
|
|
553
|
+
});
|
|
499
554
|
continue;
|
|
500
555
|
}
|
|
501
556
|
if ((entry.type === "user" || entry.type === "assistant") && entry.message) {
|
|
@@ -503,7 +558,10 @@ function parseSessionFile(content) {
|
|
|
503
558
|
messages.push({
|
|
504
559
|
role: entry.message.role,
|
|
505
560
|
content: typeof msgContent === "string" ? [{ type: "text", text: msgContent }] : msgContent,
|
|
506
|
-
timestamp: entry.timestamp
|
|
561
|
+
timestamp: entry.timestamp,
|
|
562
|
+
uuid: entry.uuid,
|
|
563
|
+
parentUuid: entry.parentUuid,
|
|
564
|
+
type: entry.type
|
|
507
565
|
});
|
|
508
566
|
}
|
|
509
567
|
} catch {
|
|
@@ -515,12 +573,25 @@ function parseSessionFile(content) {
|
|
|
515
573
|
const blocks = msg.content;
|
|
516
574
|
if (!Array.isArray(blocks)) continue;
|
|
517
575
|
for (const block of blocks) {
|
|
518
|
-
if (block.type !== "tool_result"
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
)
|
|
522
|
-
|
|
523
|
-
|
|
576
|
+
if (block.type !== "tool_result") continue;
|
|
577
|
+
const content2 = block.content;
|
|
578
|
+
let dataUri;
|
|
579
|
+
if (Array.isArray(content2)) {
|
|
580
|
+
const imgBlock = content2.find(
|
|
581
|
+
(b) => b?.type === "image" && b?.source?.type === "base64" && b?.source?.data
|
|
582
|
+
);
|
|
583
|
+
if (imgBlock) {
|
|
584
|
+
dataUri = `data:${imgBlock.source.media_type};base64,${imgBlock.source.data}`;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
let resultText;
|
|
588
|
+
if (typeof content2 === "string") {
|
|
589
|
+
resultText = content2 || void 0;
|
|
590
|
+
} else if (Array.isArray(content2)) {
|
|
591
|
+
const texts = content2.filter((b) => b?.type === "text" && typeof b?.text === "string").map((b) => b.text);
|
|
592
|
+
resultText = texts.join("\n") || void 0;
|
|
593
|
+
}
|
|
594
|
+
if (!dataUri && !resultText) continue;
|
|
524
595
|
for (let j = i - 1; j >= 0; j--) {
|
|
525
596
|
if (messages[j].role !== "assistant") continue;
|
|
526
597
|
const aBlocks = messages[j].content;
|
|
@@ -528,7 +599,10 @@ function parseSessionFile(content) {
|
|
|
528
599
|
const toolUse = aBlocks.find(
|
|
529
600
|
(b) => b.type === "tool_use" && b.id === block.tool_use_id
|
|
530
601
|
);
|
|
531
|
-
if (toolUse)
|
|
602
|
+
if (toolUse) {
|
|
603
|
+
if (dataUri) toolUse.imageUri = dataUri;
|
|
604
|
+
if (resultText) toolUse.result = resultText;
|
|
605
|
+
}
|
|
532
606
|
break;
|
|
533
607
|
}
|
|
534
608
|
}
|
|
@@ -888,9 +962,16 @@ function handlePrompt(socket, msg, activeSessions) {
|
|
|
888
962
|
const handle = runClaude(
|
|
889
963
|
{ prompt: prompt2, sessionId, cwd, images, options },
|
|
890
964
|
{
|
|
965
|
+
onSessionCreated: (info) => {
|
|
966
|
+
send(socket, "response", { type: "session_created", data: info, requestId: id });
|
|
967
|
+
log2.info({ sessionId: info.sessionId }, "New session created");
|
|
968
|
+
},
|
|
891
969
|
onText: (text) => {
|
|
892
970
|
send(socket, "response", { type: "text", text, requestId: id });
|
|
893
971
|
},
|
|
972
|
+
onSlashCommandOutput: (output) => {
|
|
973
|
+
send(socket, "response", { type: "command_output", output, requestId: id });
|
|
974
|
+
},
|
|
894
975
|
onThinking: (thinking) => {
|
|
895
976
|
send(socket, "response", { type: "thinking", thinking, requestId: id });
|
|
896
977
|
},
|
|
@@ -900,8 +981,8 @@ function handlePrompt(socket, msg, activeSessions) {
|
|
|
900
981
|
onToolResult: (toolUseId, content, isError) => {
|
|
901
982
|
send(socket, "response", { type: "tool_result", tool_use_id: toolUseId, content, is_error: isError, requestId: id });
|
|
902
983
|
},
|
|
903
|
-
onResult: (sid) => {
|
|
904
|
-
send(socket, "response", { type: "result", session_id: sid, requestId: id });
|
|
984
|
+
onResult: (sid, result) => {
|
|
985
|
+
send(socket, "response", { type: "result", session_id: sid, ...result && { result }, requestId: id });
|
|
905
986
|
activeSessions.delete(id);
|
|
906
987
|
log2.info("Session done");
|
|
907
988
|
},
|
|
@@ -935,8 +1016,9 @@ function handleCancel(id, activeSessions) {
|
|
|
935
1016
|
}
|
|
936
1017
|
async function handleListSessions(socket, msg) {
|
|
937
1018
|
const { id } = msg;
|
|
1019
|
+
const workingDirectory = msg.workingDirectory ?? process.cwd();
|
|
938
1020
|
logger.info("Listing sessions...");
|
|
939
|
-
const sessions = await listSessions();
|
|
1021
|
+
const sessions = await listSessions(workingDirectory);
|
|
940
1022
|
send(socket, "response", { type: "sessions_list", sessions, requestId: id });
|
|
941
1023
|
logger.info({ count: sessions.length }, "Listed sessions");
|
|
942
1024
|
}
|