@punkcode/cli 0.1.12 → 0.1.14

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.
Files changed (2) hide show
  1. package/dist/cli.js +265 -148
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -493,13 +493,11 @@ function getModel() {
493
493
  }
494
494
 
495
495
  // src/lib/session.ts
496
- import { readdir as readdir2, readFile as readFile2, stat, open } from "fs/promises";
496
+ import { readdir as readdir2, readFile as readFile2 } from "fs/promises";
497
497
  import { join as join2 } from "path";
498
498
  import { homedir as homedir2 } from "os";
499
+ import { listSessions as sdkListSessions } from "@anthropic-ai/claude-agent-sdk";
499
500
  var CLAUDE_DIR = join2(homedir2(), ".claude", "projects");
500
- function pathToProjectDir(dir) {
501
- return dir.replace(/\//g, "-");
502
- }
503
501
  async function loadSession(sessionId) {
504
502
  const sessionFile = `${sessionId}.jsonl`;
505
503
  let projectDirs;
@@ -512,149 +510,61 @@ async function loadSession(sessionId) {
512
510
  const sessionPath = join2(CLAUDE_DIR, projectDir, sessionFile);
513
511
  try {
514
512
  const content = await readFile2(sessionPath, "utf-8");
515
- return parseSessionFile(content);
513
+ const messages = parseSessionFile(content);
514
+ const subagentsDir = join2(CLAUDE_DIR, projectDir, sessionId, "subagents");
515
+ await attachSubagentData(messages, subagentsDir);
516
+ return messages;
516
517
  } catch {
517
518
  }
518
519
  }
519
520
  return null;
520
521
  }
521
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
522
- function isValidSessionUUID(name) {
523
- return UUID_RE.test(name);
524
- }
525
- async function listSessions(workingDirectory) {
526
- let projectDirs;
527
- try {
528
- if (workingDirectory) {
529
- projectDirs = [pathToProjectDir(workingDirectory)];
530
- } else {
531
- projectDirs = await readdir2(CLAUDE_DIR);
522
+ var AGENT_ID_RE = /agentId: (\w+)/;
523
+ async function attachSubagentData(messages, subagentsDir) {
524
+ const taskBlocks = [];
525
+ for (const msg of messages) {
526
+ if (msg.role !== "assistant") continue;
527
+ const blocks = msg.content;
528
+ if (!Array.isArray(blocks)) continue;
529
+ for (const block of blocks) {
530
+ if (block.type !== "tool_use" || block.name !== "Task") continue;
531
+ const result = block.result;
532
+ if (typeof result !== "string") continue;
533
+ const match = result.match(AGENT_ID_RE);
534
+ if (match) {
535
+ taskBlocks.push({ block, agentId: match[1] });
536
+ }
532
537
  }
533
- } catch {
534
- return [];
535
538
  }
536
- const candidates = [];
537
- for (const projectDir of projectDirs) {
538
- const projectPath = join2(CLAUDE_DIR, projectDir);
539
- let files;
539
+ if (taskBlocks.length === 0) return;
540
+ await Promise.all(taskBlocks.map(async ({ block, agentId }) => {
540
541
  try {
541
- files = await readdir2(projectPath);
542
+ const content = await readFile2(join2(subagentsDir, `agent-${agentId}.jsonl`), "utf-8");
543
+ block.subagentMessages = parseSessionFile(content);
542
544
  } catch {
543
- continue;
544
545
  }
545
- for (const file of files) {
546
- if (!file.endsWith(".jsonl")) continue;
547
- const sessionId = file.replace(".jsonl", "");
548
- if (!isValidSessionUUID(sessionId)) continue;
549
- candidates.push({
550
- sessionId,
551
- // When a workingDirectory is provided, return the original path so it
552
- // matches the device's defaultWorkingDirectory on the mobile side.
553
- project: workingDirectory ?? projectDir,
554
- filePath: join2(projectPath, file)
555
- });
556
- }
557
- }
558
- const results = await Promise.all(
559
- candidates.map(async (c) => {
560
- try {
561
- const fileStat = await stat(c.filePath);
562
- const titleInfo = await extractTitle(c.filePath);
563
- return {
564
- sessionId: c.sessionId,
565
- project: c.project,
566
- title: titleInfo.title,
567
- lastModified: fileStat.mtimeMs,
568
- ...titleInfo.summary && { summary: titleInfo.summary }
569
- };
570
- } catch {
571
- return null;
572
- }
573
- })
574
- );
575
- const sessions = results.filter((s) => s !== null);
576
- sessions.sort((a, b) => b.lastModified - a.lastModified);
577
- return sessions.slice(0, 50);
546
+ }));
578
547
  }
579
- var HEAD_READ_BYTES = 8192;
580
- var TAIL_READ_BYTES = 16384;
581
- async function extractTitle(filePath) {
582
- const fh = await open(filePath, "r");
548
+ async function listSessions(workingDirectory) {
583
549
  try {
584
- const tailResult = await extractFromTail(fh, filePath);
585
- if (tailResult) return tailResult;
586
- const firstMsg = await extractFirstUserMessage(fh);
587
- return { title: firstMsg };
588
- } finally {
589
- await fh.close();
590
- }
591
- }
592
- async function extractFromTail(fh, filePath) {
593
- const fileStat = await stat(filePath);
594
- const fileSize = fileStat.size;
595
- if (fileSize === 0) return null;
596
- const readSize = Math.min(TAIL_READ_BYTES, fileSize);
597
- const offset = fileSize - readSize;
598
- const buf = Buffer.alloc(readSize);
599
- const { bytesRead } = await fh.read(buf, 0, readSize, offset);
600
- const chunk = buf.toString("utf-8", 0, bytesRead);
601
- const lines = chunk.split("\n");
602
- let customTitle = null;
603
- let summary = null;
604
- for (let i = lines.length - 1; i >= 0; i--) {
605
- const line = lines[i].trim();
606
- if (!line) continue;
607
- try {
608
- const entry = JSON.parse(line);
609
- if (entry.type === "custom-title" && entry.title && !customTitle) {
610
- customTitle = entry.title;
611
- }
612
- if (entry.type === "summary" && entry.summary && !summary) {
613
- summary = typeof entry.summary === "string" ? entry.summary : null;
614
- }
615
- if (customTitle && summary) break;
616
- } catch {
617
- }
618
- }
619
- if (customTitle) return { title: customTitle, summary: summary ?? void 0 };
620
- if (summary) return { title: summary, summary };
621
- return null;
622
- }
623
- async function extractFirstUserMessage(fh) {
624
- const buf = Buffer.alloc(HEAD_READ_BYTES);
625
- const { bytesRead } = await fh.read(buf, 0, HEAD_READ_BYTES, 0);
626
- const chunk = buf.toString("utf-8", 0, bytesRead);
627
- const lines = chunk.split("\n");
628
- const metaUuids = /* @__PURE__ */ new Set();
629
- for (const line of lines.slice(0, 20)) {
630
- if (!line.trim()) continue;
631
- try {
632
- const entry = JSON.parse(line);
633
- if (entry.isMeta || entry.parentUuid && metaUuids.has(entry.parentUuid)) {
634
- if (entry.uuid && entry.message?.role !== "assistant") {
635
- metaUuids.add(entry.uuid);
636
- }
637
- continue;
638
- }
639
- if (entry.type === "user" && entry.message?.role === "user") {
640
- const content = entry.message.content;
641
- if (typeof content === "string" && content.trim()) {
642
- return content.trim().slice(0, 100);
643
- }
644
- if (Array.isArray(content)) {
645
- const textBlock = content.find(
646
- (b) => b.type === "text" && "text" in b && b.text && !b.text.startsWith("[Request interrupted")
647
- );
648
- if (textBlock && "text" in textBlock && typeof textBlock.text === "string") {
649
- return textBlock.text.slice(0, 100);
650
- }
651
- }
652
- }
653
- } catch {
654
- }
550
+ const sdkSessions = await sdkListSessions({
551
+ ...workingDirectory && { dir: workingDirectory },
552
+ limit: 50
553
+ });
554
+ return sdkSessions.map((s) => ({
555
+ sessionId: s.sessionId,
556
+ project: workingDirectory ?? s.cwd ?? "",
557
+ title: s.summary,
558
+ lastModified: s.lastModified,
559
+ cwd: s.cwd,
560
+ gitBranch: s.gitBranch,
561
+ fileSize: s.fileSize
562
+ }));
563
+ } catch {
564
+ return [];
655
565
  }
656
- return "Untitled session";
657
566
  }
567
+ var TOOL_RESULT_PREVIEW_BYTES = 2048;
658
568
  var ANSI_RE = /\u001b\[\d*m/g;
659
569
  function stripAnsi(text) {
660
570
  return text.replace(ANSI_RE, "");
@@ -663,6 +573,7 @@ function parseSessionFile(content) {
663
573
  const messages = [];
664
574
  const lines = content.split("\n").filter((line) => line.trim());
665
575
  const metaUuids = /* @__PURE__ */ new Set();
576
+ const taskNotifications = /* @__PURE__ */ new Map();
666
577
  for (const line of lines) {
667
578
  try {
668
579
  const entry = JSON.parse(line);
@@ -732,6 +643,21 @@ function parseSessionFile(content) {
732
643
  }
733
644
  continue;
734
645
  }
646
+ if (entry.type === "user" && typeof entry.message?.content === "string") {
647
+ const raw = entry.message.content;
648
+ const notifMatch = raw.match(/<task-notification>([\s\S]*?)<\/task-notification>/);
649
+ if (notifMatch) {
650
+ const inner = notifMatch[1];
651
+ const toolUseId = inner.match(/<tool-use-id>(.*?)<\/tool-use-id>/)?.[1];
652
+ if (toolUseId) {
653
+ const taskId = inner.match(/<task-id>(.*?)<\/task-id>/)?.[1] ?? "";
654
+ const status = inner.match(/<status>(.*?)<\/status>/)?.[1] ?? "completed";
655
+ const summary = inner.match(/<summary>([\s\S]*?)<\/summary>/)?.[1]?.trim() ?? "";
656
+ taskNotifications.set(toolUseId, { taskId, status, summary });
657
+ }
658
+ continue;
659
+ }
660
+ }
735
661
  if ((entry.type === "user" || entry.type === "assistant") && entry.message) {
736
662
  const msgContent = entry.message.content;
737
663
  messages.push({
@@ -780,9 +706,24 @@ function parseSessionFile(content) {
780
706
  );
781
707
  if (toolUse) {
782
708
  if (dataUri) toolUse.imageUri = dataUri;
783
- if (resultText) toolUse.result = resultText;
709
+ if (resultText) toolUse.result = resultText.length > TOOL_RESULT_PREVIEW_BYTES ? resultText.slice(0, TOOL_RESULT_PREVIEW_BYTES) + "\u2026" : resultText;
710
+ break;
711
+ }
712
+ }
713
+ }
714
+ }
715
+ if (taskNotifications.size > 0) {
716
+ for (const msg of messages) {
717
+ if (msg.role !== "assistant") continue;
718
+ const blocks = msg.content;
719
+ if (!Array.isArray(blocks)) continue;
720
+ for (const block of blocks) {
721
+ if (block.type !== "tool_use" || block.name !== "Task") continue;
722
+ const notif = taskNotifications.get(block.id);
723
+ if (notif) {
724
+ block.taskStatus = notif.status;
725
+ block.taskSummary = notif.summary;
784
726
  }
785
- break;
786
727
  }
787
728
  }
788
729
  }
@@ -915,7 +856,7 @@ function escapeRegex(str) {
915
856
 
916
857
  // src/lib/directory-discovery.ts
917
858
  import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
918
- async function findProjectDirectory(description, searchRoot, rejections) {
859
+ async function findProjectDirectory(description, searchRoot, rejections, signal) {
919
860
  let prompt2 = `Find up to 3 project directories on this machine that best match: "${description}"
920
861
 
921
862
  Search starting from ${searchRoot}. Rank by relevance. Include a brief reason for each match.`;
@@ -961,6 +902,8 @@ Search starting from ${searchRoot}. Rank by relevance. Include a brief reason fo
961
902
  }
962
903
  }
963
904
  });
905
+ const onAbort = () => q.close();
906
+ signal?.addEventListener("abort", onAbort, { once: true });
964
907
  try {
965
908
  for await (const msg of q) {
966
909
  if (msg.type === "result") {
@@ -973,10 +916,10 @@ Search starting from ${searchRoot}. Rank by relevance. Include a brief reason fo
973
916
  }
974
917
  const result = resultMsg.result?.trim();
975
918
  if (result && result !== "null" && result.startsWith("/")) {
976
- const path3 = result.split("\n")[0].trim();
977
- const name = path3.split("/").pop() ?? path3;
978
- logger.info({ path: path3, name }, "Project directory found (text fallback)");
979
- return [{ path: path3, name, reason: "Best match" }];
919
+ const path4 = result.split("\n")[0].trim();
920
+ const name = path4.split("/").pop() ?? path4;
921
+ logger.info({ path: path4, name }, "Project directory found (text fallback)");
922
+ return [{ path: path4, name, reason: "Best match" }];
980
923
  }
981
924
  logger.info("No matching directories found");
982
925
  return [];
@@ -986,10 +929,81 @@ Search starting from ${searchRoot}. Rank by relevance. Include a brief reason fo
986
929
  }
987
930
  }
988
931
  } finally {
932
+ signal?.removeEventListener("abort", onAbort);
989
933
  q.close();
990
934
  }
991
935
  return [];
992
936
  }
937
+ async function suggestProjectLocation(description, searchRoot, name, signal) {
938
+ const prompt2 = `Suggest up to 3 suitable locations on this machine to create a new project.
939
+
940
+ User's description: "${description}"
941
+ ${name ? `Desired project name: "${name}"` : ""}
942
+
943
+ Search starting from ${searchRoot}. Look at common project directories (e.g. ~/github, ~/projects, ~/code, ~/Desktop).
944
+ For each suggestion, provide the full path WHERE the project folder would be created (including the project name as the last segment), a human-readable name, and a brief reason.`;
945
+ const q = query2({
946
+ prompt: prompt2,
947
+ options: {
948
+ systemPrompt: {
949
+ type: "preset",
950
+ preset: "claude_code",
951
+ append: "IMPORTANT: When searching for directories, always try listing likely parent directories with ls FIRST (e.g. ls ~/github, ls ~/projects). Only use find as a last resort, and always with -maxdepth 3. Never scan the entire home directory. The path in each suggestion must be the FULL path including the new project folder name."
952
+ },
953
+ permissionMode: "bypassPermissions",
954
+ persistSession: false,
955
+ cwd: searchRoot,
956
+ outputFormat: {
957
+ type: "json_schema",
958
+ schema: {
959
+ type: "object",
960
+ properties: {
961
+ suggestions: {
962
+ type: "array",
963
+ items: {
964
+ type: "object",
965
+ properties: {
966
+ path: { type: "string", description: "Absolute path for the new project directory (including project folder name)" },
967
+ name: { type: "string", description: "Human-readable project name" },
968
+ reason: { type: "string", description: "Brief reason why this location is suitable" }
969
+ },
970
+ required: ["path", "name", "reason"]
971
+ }
972
+ }
973
+ },
974
+ required: ["suggestions"]
975
+ }
976
+ }
977
+ }
978
+ });
979
+ const onAbort = () => q.close();
980
+ signal?.addEventListener("abort", onAbort, { once: true });
981
+ try {
982
+ for await (const msg of q) {
983
+ if (msg.type === "result") {
984
+ const resultMsg = msg;
985
+ if (resultMsg.subtype === "success") {
986
+ const structured = resultMsg.structured_output;
987
+ if (structured?.suggestions?.length) {
988
+ logger.info({ count: structured.suggestions.length }, "Location suggestions found (structured)");
989
+ return structured.suggestions;
990
+ }
991
+ logger.info("No location suggestions found");
992
+ return [];
993
+ }
994
+ logger.warn({ subtype: resultMsg.subtype }, "Location suggestion query failed");
995
+ return [];
996
+ }
997
+ }
998
+ } finally {
999
+ signal?.removeEventListener("abort", onAbort);
1000
+ q.close();
1001
+ }
1002
+ return [];
1003
+ }
1004
+
1005
+ // src/commands/connect.ts
1006
+ import path3 from "path";
993
1007
 
994
1008
  // src/lib/auth.ts
995
1009
  import fs2 from "fs";
@@ -1215,7 +1229,22 @@ async function connect(server, options) {
1215
1229
  });
1216
1230
  socket.on("find-project", (msg) => {
1217
1231
  if (msg.type === "find-project") {
1218
- handleFindProject(socket, msg, defaultCwd);
1232
+ handleFindProject(socket, msg, defaultCwd, activeSessions);
1233
+ }
1234
+ });
1235
+ socket.on("suggest-project-location", (msg) => {
1236
+ if (msg.type === "suggest-project-location") {
1237
+ handleSuggestProjectLocation(socket, msg, activeSessions);
1238
+ }
1239
+ });
1240
+ socket.on("create-project", (msg) => {
1241
+ if (msg.type === "create-project") {
1242
+ handleCreateProject(socket, msg);
1243
+ }
1244
+ });
1245
+ socket.on("check-path", (msg) => {
1246
+ if (msg.type === "check-path") {
1247
+ handleCheckPath(socket, msg);
1219
1248
  }
1220
1249
  });
1221
1250
  socket.on("cancel", (msg) => {
@@ -1244,6 +1273,9 @@ async function connect(server, options) {
1244
1273
  logger.error(detail, `Connection error: ${reason}`);
1245
1274
  logger.debug({ err }, "Connection error (raw)");
1246
1275
  });
1276
+ socket.on("error", (err) => {
1277
+ logger.error({ err }, "Socket error");
1278
+ });
1247
1279
  const refreshInterval = setInterval(async () => {
1248
1280
  try {
1249
1281
  const token = await refreshIdToken();
@@ -1322,6 +1354,7 @@ function formatConnectionError(err) {
1322
1354
  return { reason, ...result };
1323
1355
  }
1324
1356
  function send(socket, event, msg) {
1357
+ if (!socket.connected) return;
1325
1358
  socket.emit(event, msg);
1326
1359
  }
1327
1360
  function handlePrompt(socket, msg, activeSessions) {
@@ -1389,6 +1422,8 @@ function handleCancel(id, activeSessions) {
1389
1422
  session.abort();
1390
1423
  activeSessions.delete(id);
1391
1424
  logger.info({ sessionId: id }, "Session cancelled");
1425
+ } else {
1426
+ logger.warn({ sessionId: id }, "Cancel: session not found in activeSessions");
1392
1427
  }
1393
1428
  }
1394
1429
  async function handleListSessions(socket, msg, defaultCwd) {
@@ -1399,14 +1434,16 @@ async function handleListSessions(socket, msg, defaultCwd) {
1399
1434
  send(socket, "response", { type: "sessions_list", sessions, requestId: id });
1400
1435
  logger.info({ count: sessions.length }, "Listed sessions");
1401
1436
  }
1437
+ var DEFAULT_HISTORY_LIMIT = 30;
1402
1438
  async function handleLoadSession(socket, msg) {
1403
- const { id, sessionId } = msg;
1439
+ const { id, sessionId, limit = DEFAULT_HISTORY_LIMIT } = msg;
1404
1440
  const log2 = createChildLogger({ sessionId });
1405
1441
  log2.info("Loading session...");
1406
- const messages = await loadSession(sessionId);
1407
- if (messages) {
1408
- send(socket, "response", { type: "history", messages, requestId: id });
1409
- log2.info({ count: messages.length }, "Session loaded");
1442
+ const all = await loadSession(sessionId);
1443
+ if (all) {
1444
+ const messages = limit > 0 && all.length > limit ? all.slice(-limit) : all;
1445
+ send(socket, "response", { type: "history", messages, total: all.length, requestId: id });
1446
+ log2.info({ count: messages.length, total: all.length }, "Session loaded");
1410
1447
  } else {
1411
1448
  send(socket, "response", { type: "session_not_found", session_id: sessionId, requestId: id });
1412
1449
  log2.warn("Session not found");
@@ -1426,20 +1463,100 @@ async function handleGetCommands(socket, msg) {
1426
1463
  log2.error({ err }, "Commands error");
1427
1464
  }
1428
1465
  }
1429
- async function handleFindProject(socket, msg, _defaultCwd) {
1466
+ async function handleFindProject(socket, msg, _defaultCwd, activeSessions) {
1430
1467
  const { id, description, rootDirectory, rejections } = msg;
1431
1468
  const searchRoot = rootDirectory ?? os3.homedir();
1432
1469
  const log2 = createChildLogger({ requestId: id });
1433
1470
  log2.info({ description, searchRoot }, "Finding project directory...");
1471
+ const ac = new AbortController();
1472
+ const handle = {
1473
+ abort: () => ac.abort(),
1474
+ resolvePermission: () => {
1475
+ },
1476
+ setPermissionMode: async () => {
1477
+ }
1478
+ };
1479
+ activeSessions.set(id, handle);
1434
1480
  try {
1435
- const suggestions = await findProjectDirectory(description, searchRoot, rejections);
1481
+ const suggestions = await findProjectDirectory(description, searchRoot, rejections, ac.signal);
1436
1482
  send(socket, "response", { type: "project_suggestions", suggestions, requestId: id });
1437
1483
  log2.info({ count: suggestions.length }, "Project suggestions sent");
1438
1484
  } catch (err) {
1485
+ if (ac.signal.aborted) {
1486
+ log2.info("Find project cancelled");
1487
+ return;
1488
+ }
1439
1489
  const message = err instanceof Error ? err.message : String(err);
1440
1490
  send(socket, "response", { type: "error", message, requestId: id });
1441
1491
  log2.error({ err }, "Find project error");
1492
+ } finally {
1493
+ activeSessions.delete(id);
1494
+ }
1495
+ }
1496
+ async function handleSuggestProjectLocation(socket, msg, activeSessions) {
1497
+ const { id, description, name, rootDirectory } = msg;
1498
+ const searchRoot = rootDirectory ?? os3.homedir();
1499
+ const log2 = createChildLogger({ requestId: id });
1500
+ log2.info({ description, name, searchRoot }, "Suggesting project location...");
1501
+ const ac = new AbortController();
1502
+ const handle = {
1503
+ abort: () => ac.abort(),
1504
+ resolvePermission: () => {
1505
+ },
1506
+ setPermissionMode: async () => {
1507
+ }
1508
+ };
1509
+ activeSessions.set(id, handle);
1510
+ try {
1511
+ const suggestions = await suggestProjectLocation(description, searchRoot, name, ac.signal);
1512
+ send(socket, "response", { type: "project_suggestions", suggestions, requestId: id });
1513
+ log2.info({ count: suggestions.length }, "Location suggestions sent");
1514
+ } catch (err) {
1515
+ if (ac.signal.aborted) {
1516
+ log2.info("Suggest project location cancelled");
1517
+ return;
1518
+ }
1519
+ const message = err instanceof Error ? err.message : String(err);
1520
+ send(socket, "response", { type: "error", message, requestId: id });
1521
+ log2.error({ err }, "Suggest project location error");
1522
+ } finally {
1523
+ activeSessions.delete(id);
1524
+ }
1525
+ }
1526
+ function handleCreateProject(socket, msg) {
1527
+ const { id, path: projectPath } = msg;
1528
+ const log2 = createChildLogger({ requestId: id });
1529
+ log2.info({ path: projectPath }, "Creating project directory...");
1530
+ try {
1531
+ fs3.mkdirSync(projectPath, { recursive: true });
1532
+ send(socket, "response", { type: "project_created", path: projectPath, requestId: id });
1533
+ log2.info({ path: projectPath }, "Project directory created");
1534
+ } catch (err) {
1535
+ const message = err instanceof Error ? err.message : String(err);
1536
+ send(socket, "response", { type: "error", message, requestId: id });
1537
+ log2.error({ err }, "Create project error");
1538
+ }
1539
+ }
1540
+ function handleCheckPath(socket, msg) {
1541
+ const { id, path: checkTarget } = msg;
1542
+ const log2 = createChildLogger({ requestId: id });
1543
+ log2.info({ path: checkTarget }, "Checking path...");
1544
+ let exists = false;
1545
+ let isDirectory = false;
1546
+ let parentExists = false;
1547
+ try {
1548
+ const stat = fs3.statSync(checkTarget);
1549
+ exists = true;
1550
+ isDirectory = stat.isDirectory();
1551
+ } catch {
1552
+ }
1553
+ try {
1554
+ const parentStat = fs3.statSync(path3.dirname(checkTarget));
1555
+ parentExists = parentStat.isDirectory();
1556
+ } catch {
1442
1557
  }
1558
+ send(socket, "response", { type: "path_check", exists, isDirectory, parentExists, requestId: id });
1559
+ log2.info({ exists, isDirectory, parentExists }, "Path check complete");
1443
1560
  }
1444
1561
  async function handleGetContext(socket, msg, defaultCwd) {
1445
1562
  const { id, sessionId } = msg;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@punkcode/cli",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Control Claude Code from your phone",
5
5
  "type": "module",
6
6
  "bin": {
@@ -53,7 +53,7 @@
53
53
  "vitest": "^4.0.18"
54
54
  },
55
55
  "dependencies": {
56
- "@anthropic-ai/claude-agent-sdk": "^0.2.49",
56
+ "@anthropic-ai/claude-agent-sdk": "^0.2.62",
57
57
  "@anthropic-ai/sdk": "^0.78.0",
58
58
  "commander": "^14.0.3",
59
59
  "execa": "^9.6.1",