@co0ontty/wand 1.1.5 → 1.1.7

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.
@@ -435,12 +435,11 @@ export class ClaudePtyBridge extends EventEmitter {
435
435
  /\bgrant\b.*\bpermission\b/i.test(normalized) ||
436
436
  /\bhaven't granted\b/i.test(normalized) ||
437
437
  /\benter to confirm\b/i.test(normalized) ||
438
- /\bwould you like to proceed\b/i.test(normalized) ||
439
- /❯/.test(normalized));
438
+ /\bwould you like to proceed\b/i.test(normalized));
440
439
  }
441
440
  extractPromptText(normalized) {
442
441
  // Return a snippet around the permission prompt
443
- const match = normalized.match(/.{0,100}(?:do you want to|permission|grant|enter to confirm|would you like to proceed|❯).{0,100}/i);
442
+ const match = normalized.match(/.{0,100}(?:do you want to|permission|grant|enter to confirm|would you like to proceed).{0,100}/i);
444
443
  return match?.[0] ?? normalized.slice(-100);
445
444
  }
446
445
  extractPermissionTarget(normalized) {
@@ -40,8 +40,7 @@ const PROMPT_PATTERNS = [
40
40
  /\bwould you like to\b/i,
41
41
  /\bshall i\b/i,
42
42
  /\bcan i\b/i,
43
- /\bgrant\b.*\bpermission\b/i,
44
- /❯/
43
+ /\bgrant\b.*\bpermission\b/i
45
44
  ];
46
45
  const REAL_CONVERSATION_MIN_LINES = 2;
47
46
  const REAL_CONVERSATION_MIN_MESSAGES = 2;
@@ -1143,16 +1142,14 @@ export class ProcessManager extends EventEmitter {
1143
1142
  continue;
1144
1143
  const jsonlPath = path.join(getClaudeProjectDir(cwd), `${claudeSessionId}.jsonl`);
1145
1144
  try {
1146
- if (existsSync(jsonlPath)) {
1147
- unlinkSync(jsonlPath);
1148
- deleted++;
1149
- }
1145
+ unlinkSync(jsonlPath);
1146
+ deleted++;
1150
1147
  }
1151
1148
  catch {
1152
- // Best-effort — Claude cache cleanup is non-critical
1149
+ // Best-effort — file may already be gone
1153
1150
  }
1154
1151
  }
1155
- if (deleted > 0) {
1152
+ if (sessions.length > 0) {
1156
1153
  this.claudeHistoryCache = null;
1157
1154
  }
1158
1155
  return deleted;
@@ -165,6 +165,14 @@
165
165
  }
166
166
  }
167
167
 
168
+ function getConfigCwd() {
169
+ return (state.config && state.config.defaultCwd) || "/tmp";
170
+ }
171
+
172
+ function getEffectiveCwd() {
173
+ return state.workingDir || getConfigCwd();
174
+ }
175
+
168
176
  // PWA install prompt handling
169
177
  window.addEventListener('beforeinstallprompt', function(e) {
170
178
  e.preventDefault();
@@ -464,14 +472,14 @@
464
472
  '</div>' +
465
473
  '<div class="file-side-panel-body">' +
466
474
  '<div class="file-explorer-header">' +
467
- '<span class="file-explorer-path" id="file-explorer-cwd">' + escapeHtml(selectedSession && selectedSession.cwd ? selectedSession.cwd : (state.config && state.config.defaultCwd ? state.config.defaultCwd : "")) + '</span>' +
475
+ '<span class="file-explorer-path" id="file-explorer-cwd">' + escapeHtml(selectedSession && selectedSession.cwd ? selectedSession.cwd : getConfigCwd()) + '</span>' +
468
476
  '<button class="file-explorer-refresh" id="file-explorer-refresh" title="刷新" aria-label="刷新文件列表">↻</button>' +
469
477
  '</div>' +
470
478
  '<div class="file-search-box">' +
471
479
  '<input type="text" id="file-search-input" class="file-search-input" placeholder="搜索文件..." autocomplete="off" />' +
472
480
  '<button class="file-search-clear" id="file-search-clear" type="button" aria-label="清除搜索">×</button>' +
473
481
  '</div>' +
474
- '<div class="file-explorer" id="file-explorer">' + renderFileExplorer(selectedSession && selectedSession.cwd ? selectedSession.cwd : (state.config && state.config.defaultCwd ? state.config.defaultCwd : "")) + '</div>' +
482
+ '<div class="file-explorer" id="file-explorer">' + renderFileExplorer(selectedSession && selectedSession.cwd ? selectedSession.cwd : getConfigCwd()) + '</div>' +
475
483
  '</div>' +
476
484
  '</div>' +
477
485
  '<div id="output" class="terminal-container' + (state.selectedId ? "" : " hidden") + ' active">' +
@@ -498,7 +506,7 @@
498
506
  '<div class="blank-chat-cwd-wrap">' +
499
507
  '<div class="blank-chat-cwd" id="blank-chat-cwd" role="button" tabindex="0" title="点击切换工作目录">' +
500
508
  '<span class="blank-chat-cwd-icon">📁</span>' +
501
- '<span class="blank-chat-cwd-path" id="blank-chat-cwd-path">' + escapeHtml(state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "/tmp")) + '</span>' +
509
+ '<span class="blank-chat-cwd-path" id="blank-chat-cwd-path">' + escapeHtml(getEffectiveCwd()) + '</span>' +
502
510
  '<span class="blank-chat-cwd-arrow" id="blank-chat-cwd-arrow">▼</span>' +
503
511
  '</div>' +
504
512
  '<div class="blank-chat-cwd-dropdown hidden" id="blank-chat-cwd-dropdown"></div>' +
@@ -1103,7 +1111,7 @@
1103
1111
  function updateFilePanelCwd(session) {
1104
1112
  var cwdEl = document.getElementById("file-explorer-cwd");
1105
1113
  if (!cwdEl) return;
1106
- var cwd = session && session.cwd ? session.cwd : (state.config && state.config.defaultCwd ? state.config.defaultCwd : "");
1114
+ var cwd = session && session.cwd ? session.cwd : getConfigCwd();
1107
1115
  cwdEl.textContent = cwd;
1108
1116
  }
1109
1117
 
@@ -1142,7 +1150,7 @@
1142
1150
  }
1143
1151
 
1144
1152
  function renderFileExplorer(cwd) {
1145
- var root = cwd || (state.config && state.config.defaultCwd) || "";
1153
+ var root = cwd || getConfigCwd();
1146
1154
  if (!root) {
1147
1155
  return '<div class="file-explorer empty">No working directory configured.</div>';
1148
1156
  }
@@ -1161,8 +1169,8 @@
1161
1169
  var session = state.sessions.find(function(s) { return s.id === state.selectedId; });
1162
1170
  if (session) cwd = session.cwd || "";
1163
1171
  }
1164
- if (!cwd && state.config && state.config.defaultCwd) {
1165
- cwd = state.config.defaultCwd;
1172
+ if (!cwd) {
1173
+ cwd = getConfigCwd();
1166
1174
  }
1167
1175
  if (!cwd) {
1168
1176
  explorer.innerHTML = '<div class="file-explorer empty">No working directory.</div>';
@@ -1504,7 +1512,7 @@
1504
1512
  }
1505
1513
 
1506
1514
  function renderFolderPicker(state) {
1507
- var currentDir = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "/tmp");
1515
+ var currentDir = getEffectiveCwd();
1508
1516
 
1509
1517
  // 如果有选中的会话,不显示单独的工作目录标签(已嵌入输入框内部)
1510
1518
  if (state.selectedId) {
@@ -1530,7 +1538,7 @@
1530
1538
 
1531
1539
  // 渲染内嵌到输入框的工作目录指示器
1532
1540
  function renderWorkingDirIndicator(state) {
1533
- var currentDir = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "/tmp");
1541
+ var currentDir = getEffectiveCwd();
1534
1542
  var displayDir = currentDir;
1535
1543
 
1536
1544
  // 如果有选中的会话,使用会话的工作目录
@@ -1653,7 +1661,7 @@
1653
1661
  '<div class="field">' +
1654
1662
  '<label class="field-label" for="cwd">工作目录</label>' +
1655
1663
  '<div class="suggestions-wrap">' +
1656
- '<input id="cwd" type="text" class="field-input" autocomplete="off" placeholder="' + escapeHtml(state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "/tmp")) + '" />' +
1664
+ '<input id="cwd" type="text" class="field-input" autocomplete="off" placeholder="' + escapeHtml(getEffectiveCwd()) + '" />' +
1657
1665
  '<div id="cwd-suggestions" class="suggestions hidden"></div>' +
1658
1666
  '</div>' +
1659
1667
  '<p class="field-hint">留空则使用上方目录,支持路径自动补全。</p>' +
@@ -2221,7 +2229,7 @@
2221
2229
 
2222
2230
  if (folderPickerInput) {
2223
2231
  // Load initial folders from saved or default path
2224
- var initialPath = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "/tmp");
2232
+ var initialPath = getEffectiveCwd();
2225
2233
  loadFolderSuggestions(initialPath);
2226
2234
 
2227
2235
  folderPickerInput.addEventListener("focus", function() {
@@ -2397,16 +2405,14 @@
2397
2405
  folderPickerModal.classList.remove("hidden");
2398
2406
  // Set initial path in input
2399
2407
  if (folderPickerInput) {
2400
- folderPickerInput.value = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "/tmp");
2408
+ folderPickerInput.value = getEffectiveCwd();
2401
2409
  }
2402
2410
  // Load initial folders
2403
- var initialPath = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "/tmp");
2411
+ var initialPath = getEffectiveCwd();
2404
2412
  loadFolderSuggestions(initialPath);
2405
2413
  renderBreadcrumb(initialPath);
2406
2414
  }
2407
2415
 
2408
- // Welcome screen folder button (legacy, now handled by initBlankChatCwd)
2409
-
2410
2416
  if (closeFolderPicker && folderPickerModal) {
2411
2417
  closeFolderPicker.addEventListener("click", function() {
2412
2418
  folderPickerModal.classList.add("hidden");
@@ -2426,6 +2432,16 @@
2426
2432
  setupVisualViewportHandlers();
2427
2433
  }
2428
2434
 
2435
+ function activateSessionItem(sessionId) {
2436
+ var session = state.sessions.find(function(s) { return s.id === sessionId; });
2437
+ if (session && session.status !== "running") {
2438
+ resumeSessionFromList(sessionId);
2439
+ } else {
2440
+ selectSession(sessionId);
2441
+ }
2442
+ closeSessionsDrawer();
2443
+ }
2444
+
2429
2445
  function handleSessionItemClick(event) {
2430
2446
  var target = event.target;
2431
2447
  if (!target || !(target instanceof Element)) return;
@@ -2502,13 +2518,22 @@
2502
2518
  }
2503
2519
  if (_swipeState) return;
2504
2520
  if (item.dataset.sessionId) {
2505
- var clickedSession = state.sessions.find(function(s) { return s.id === item.dataset.sessionId; });
2506
- if (clickedSession && clickedSession.status !== "running") {
2507
- resumeSessionFromList(item.dataset.sessionId);
2508
- } else {
2509
- selectSession(item.dataset.sessionId);
2510
- }
2511
- closeSessionsDrawer();
2521
+ activateSessionItem(item.dataset.sessionId);
2522
+ } else if (item.dataset.claudeHistoryId) {
2523
+ var claudeSessionId = item.dataset.claudeHistoryId;
2524
+ var cwd = item.dataset.cwd;
2525
+ resumeClaudeHistorySession(claudeSessionId, cwd)
2526
+ .then(function(data) {
2527
+ if (data && data.id) {
2528
+ state.selectedId = data.id;
2529
+ persistSelectedId();
2530
+ state.drafts[data.id] = "";
2531
+ loadSessions().then(function() {
2532
+ selectSession(data.id);
2533
+ closeSessionsDrawer();
2534
+ });
2535
+ }
2536
+ });
2512
2537
  }
2513
2538
  }
2514
2539
  }
@@ -2527,13 +2552,22 @@
2527
2552
  return;
2528
2553
  }
2529
2554
  if (item.dataset.sessionId) {
2530
- var keySession = state.sessions.find(function(s) { return s.id === item.dataset.sessionId; });
2531
- if (keySession && keySession.status !== "running") {
2532
- resumeSessionFromList(item.dataset.sessionId);
2533
- } else {
2534
- selectSession(item.dataset.sessionId);
2535
- }
2536
- closeSessionsDrawer();
2555
+ activateSessionItem(item.dataset.sessionId);
2556
+ } else if (item.dataset.claudeHistoryId) {
2557
+ var claudeSessionId = item.dataset.claudeHistoryId;
2558
+ var cwd = item.dataset.cwd;
2559
+ resumeClaudeHistorySession(claudeSessionId, cwd)
2560
+ .then(function(data) {
2561
+ if (data && data.id) {
2562
+ state.selectedId = data.id;
2563
+ persistSelectedId();
2564
+ state.drafts[data.id] = "";
2565
+ loadSessions().then(function() {
2566
+ selectSession(data.id);
2567
+ closeSessionsDrawer();
2568
+ });
2569
+ }
2570
+ });
2537
2571
  }
2538
2572
  }
2539
2573
 
@@ -2968,15 +3002,9 @@
2968
3002
  state.terminalViewportSize = { width: 0, height: 0 };
2969
3003
  state.terminalAutoFollow = true;
2970
3004
  clearTerminalScrollIdleTimer();
2971
- // Double-rAF: wait for browser to complete layout before measuring and fitting
3005
+ // Retry-based fit: wait for browser to complete layout before measuring and fitting
2972
3006
  if (state.fitAddon) {
2973
- requestAnimationFrame(function() {
2974
- requestAnimationFrame(function() {
2975
- if (state.fitAddon && shouldResizeTerminalViewport()) {
2976
- state.fitAddon.fit();
2977
- }
2978
- });
2979
- });
3007
+ ensureTerminalFit();
2980
3008
  }
2981
3009
 
2982
3010
  var viewport = getTerminalViewport();
@@ -3403,11 +3431,7 @@
3403
3431
  updateShellChrome();
3404
3432
 
3405
3433
  var selectedSession = state.sessions.find(function(s) { return s.id === id; });
3406
- if (data.messages && data.messages.length > 0) {
3407
- state.currentMessages = data.messages;
3408
- } else {
3409
- state.currentMessages = [];
3410
- }
3434
+ state.currentMessages = data.messages || [];
3411
3435
 
3412
3436
  if (state.terminal) {
3413
3437
  syncTerminalBuffer(id, data.output || "", { mode: "replace" });
@@ -3891,7 +3915,7 @@
3891
3915
 
3892
3916
  function quickStartSession() {
3893
3917
  var command = getPreferredTool();
3894
- var defaultCwd = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "");
3918
+ var defaultCwd = getEffectiveCwd();
3895
3919
  var defaultMode = (state.config && state.config.defaultMode) ? state.config.defaultMode : "default";
3896
3920
  state.preferredCommand = command;
3897
3921
  state.chatMode = getSafeModeForTool(command, state.chatMode);
@@ -3928,7 +3952,7 @@
3928
3952
 
3929
3953
  hideError(errorEl);
3930
3954
 
3931
- var defaultCwd = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "");
3955
+ var defaultCwd = getEffectiveCwd();
3932
3956
  var selectedMode = getSafeModeForTool(command, state.modeValue);
3933
3957
  state.modeValue = selectedMode;
3934
3958
  state.chatMode = selectedMode;
@@ -4006,7 +4030,7 @@
4006
4030
  }
4007
4031
 
4008
4032
  function loadBlankChatCwdDropdown(dropdown) {
4009
- var defaultCwd = state.config && state.config.defaultCwd ? state.config.defaultCwd : "/tmp";
4033
+ var defaultCwd = getConfigCwd();
4010
4034
  dropdown.innerHTML = '<div class="blank-chat-cwd-loading">加载中...</div>';
4011
4035
  fetch("/api/recent-paths", { credentials: "same-origin" })
4012
4036
  .then(function(res) { return res.json(); })
@@ -4364,7 +4388,7 @@
4364
4388
  welcomeInput.placeholder = "正在启动会话...";
4365
4389
  welcomeInput.disabled = true;
4366
4390
  var mode = state.chatMode || "full-access";
4367
- var defaultCwd = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "");
4391
+ var defaultCwd = getEffectiveCwd();
4368
4392
  var preferredTool = getPreferredTool();
4369
4393
  fetch("/api/commands", {
4370
4394
  method: "POST",
@@ -4426,7 +4450,7 @@
4426
4450
 
4427
4451
  // No selected session, create a new one
4428
4452
  var mode = state.chatMode || "full-access";
4429
- var defaultCwd = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "");
4453
+ var defaultCwd = getEffectiveCwd();
4430
4454
  var preferredTool = getPreferredTool();
4431
4455
  fetch("/api/commands", {
4432
4456
  method: "POST",
@@ -4493,9 +4517,8 @@
4493
4517
  // Init terminal if not already done
4494
4518
  if (!state.terminal) initTerminal();
4495
4519
  applyCurrentView();
4496
- if (state.currentView === "terminal") {
4497
- state.terminalViewportSize = { width: 0, height: 0 };
4498
- scheduleTerminalResize(true);
4520
+ if (state.terminal && state.fitAddon) {
4521
+ ensureTerminalFit();
4499
4522
  }
4500
4523
  // Don't call renderChat() here — loadOutput() always calls renderChat() after it resolves.
4501
4524
  // Calling renderChat() prematurely would render with stale/empty messages.
@@ -5380,7 +5403,7 @@
5380
5403
  welcomeInput.placeholder = "Claude 正在思考,请稍候...";
5381
5404
  welcomeInput.disabled = true;
5382
5405
  var mode = state.chatMode || "full-access";
5383
- var defaultCwd = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "");
5406
+ var defaultCwd = getEffectiveCwd();
5384
5407
  var preferredTool = getPreferredTool();
5385
5408
  fetch("/api/commands", {
5386
5409
  method: "POST",
@@ -5416,7 +5439,7 @@
5416
5439
 
5417
5440
  function createSessionFromInput(value, inputBox, welcomeInput) {
5418
5441
  var mode = state.chatMode || "full-access";
5419
- var defaultCwd = state.workingDir || (state.config && state.config.defaultCwd ? state.config.defaultCwd : "");
5442
+ var defaultCwd = getEffectiveCwd();
5420
5443
  var preferredTool = getPreferredTool();
5421
5444
  fetch("/api/commands", {
5422
5445
  method: "POST",
@@ -6463,6 +6486,34 @@
6463
6486
  updateTerminalJumpToBottomButton();
6464
6487
  }
6465
6488
 
6489
+ function ensureTerminalFit() {
6490
+ var maxAttempts = 10;
6491
+ var attempt = 0;
6492
+ function tryFit() {
6493
+ attempt++;
6494
+ state.terminalViewportSize = { width: 0, height: 0 };
6495
+ if (shouldResizeTerminalViewport() && state.fitAddon) {
6496
+ state.fitAddon.fit();
6497
+ maybeScrollTerminalToBottom("resize");
6498
+ if (state.selectedId && state.terminal) {
6499
+ var nextSize = { cols: state.terminal.cols, rows: state.terminal.rows };
6500
+ if (state.lastResize.cols !== nextSize.cols || state.lastResize.rows !== nextSize.rows) {
6501
+ state.lastResize = nextSize;
6502
+ fetch("/api/sessions/" + state.selectedId + "/resize", {
6503
+ method: "POST",
6504
+ headers: { "Content-Type": "application/json" },
6505
+ credentials: "same-origin",
6506
+ body: JSON.stringify(nextSize)
6507
+ }).catch(function() {});
6508
+ }
6509
+ }
6510
+ } else if (attempt < maxAttempts) {
6511
+ requestAnimationFrame(tryFit);
6512
+ }
6513
+ }
6514
+ requestAnimationFrame(tryFit);
6515
+ }
6516
+
6466
6517
  function scheduleTerminalResize(immediate) {
6467
6518
  if (state.resizeTimer) {
6468
6519
  clearTimeout(state.resizeTimer);
@@ -6588,7 +6639,7 @@
6588
6639
  }
6589
6640
  updateSessionSnapshot(snapshot);
6590
6641
  if (msg.sessionId === state.selectedId) {
6591
- if (msg.data.messages && msg.data.messages.length > 0) {
6642
+ if (msg.data.messages) {
6592
6643
  state.currentMessages = msg.data.messages;
6593
6644
  }
6594
6645
  updateTaskDisplay();
@@ -6661,6 +6712,8 @@
6661
6712
  if (msg.sessionId === state.selectedId && msg.data) {
6662
6713
  if (chatRenderTimer) { clearTimeout(chatRenderTimer); chatRenderTimer = null; }
6663
6714
  updateTerminalOutput(msg.data.output || "", msg.sessionId, "replace");
6715
+ // Ensure terminal is properly fitted after receiving initial data
6716
+ scheduleTerminalResize(true);
6664
6717
  }
6665
6718
  break;
6666
6719
  case 'usage':
@@ -6719,10 +6772,10 @@
6719
6772
  }
6720
6773
  }
6721
6774
  if (permissionActionsEl) permissionActionsEl.classList.remove("hidden");
6722
- // Also show in task bar
6723
- taskEl.textContent = pendingEscalation ? (pendingEscalation.reason || "等待 Claude 权限授权") : "等待 Claude 权限授权";
6724
- taskEl.classList.remove("hidden");
6725
- taskEl.classList.add("permission-blocked");
6775
+ // Hide top task bar — permission info is already shown in the composer
6776
+ taskEl.textContent = "";
6777
+ taskEl.classList.add("hidden");
6778
+ taskEl.classList.remove("permission-blocked");
6726
6779
  return;
6727
6780
  }
6728
6781
 
@@ -1529,15 +1529,6 @@
1529
1529
  flex-shrink: 0;
1530
1530
  animation: task-pulse 1.2s ease-in-out infinite;
1531
1531
  }
1532
- .current-task.permission-blocked {
1533
- color: #d18b00;
1534
- background: rgba(240, 165, 0, 0.14);
1535
- border-color: rgba(240, 165, 0, 0.3);
1536
- }
1537
- .current-task.permission-blocked::before {
1538
- background: #f0a500;
1539
- animation: none;
1540
- }
1541
1532
  @keyframes task-pulse {
1542
1533
  0%, 100% { opacity: 1; transform: scale(1); }
1543
1534
  50% { opacity: 0.5; transform: scale(0.8); }
@@ -1559,8 +1550,8 @@
1559
1550
  padding: 10px;
1560
1551
  overflow: hidden;
1561
1552
  min-height: 0;
1562
- margin: 0 10px 10px;
1563
- border-radius: var(--radius-lg);
1553
+ margin: 0 6px 6px;
1554
+ border-radius: var(--radius-md);
1564
1555
  border: 1px solid rgba(140, 110, 85, 0.35);
1565
1556
  box-shadow:
1566
1557
  inset 0 1px 0 rgba(255, 255, 255, 0.06),
@@ -4861,7 +4852,7 @@
4861
4852
  width: min(300px, calc(100vw - 20px));
4862
4853
  top: 0;
4863
4854
  }
4864
- .terminal-container { margin: 0 12px 12px; min-height: 0; }
4855
+ .terminal-container { margin: 0 6px 6px; min-height: 0; }
4865
4856
  .btn { min-height: 40px; }
4866
4857
  .btn-sm { min-height: 36px; padding: 0 10px; font-size: 0.75rem; height: 36px; }
4867
4858
  .btn-icon { width: 36px; height: 36px; min-height: 36px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "A web terminal for local CLI tools like Claude.",
5
5
  "type": "module",
6
6
  "bin": {