claude-remote-cli 3.4.2 → 3.5.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.
@@ -1,4 +1,4 @@
1
- .pin-gate.svelte-1qp96yb{display:flex;align-items:center;justify-content:center;height:100vh;background:var(--bg);padding:1rem}.pin-container.svelte-1qp96yb{display:flex;flex-direction:column;align-items:center;gap:1rem;width:100%;max-width:320px;text-align:center}.pin-container.svelte-1qp96yb h1:where(.svelte-1qp96yb){font-size:1.5rem;color:var(--text)}.pin-container.svelte-1qp96yb p:where(.svelte-1qp96yb){color:var(--text-muted);font-size:.95rem}input.svelte-1qp96yb{width:100%;padding:14px 16px;background:var(--surface);border:1px solid var(--border);border-radius:8px;color:var(--text);font-size:1.2rem;text-align:center;outline:none;-webkit-appearance:none}input.svelte-1qp96yb:focus{border-color:var(--accent)}button.svelte-1qp96yb{width:100%;padding:14px;background:var(--accent);color:#fff;border:none;border-radius:8px;font-size:1rem;font-weight:600;cursor:pointer;touch-action:manipulation}button.svelte-1qp96yb:active{opacity:.8}.error.svelte-1qp96yb{color:var(--accent);font-size:.9rem}.context-menu-trigger.svelte-1nmhce7{background:none;border:none;color:var(--text-muted);font-size:1rem;font-weight:700;cursor:pointer;padding:0 6px;border-radius:4px;touch-action:manipulation;flex-shrink:0;line-height:1;letter-spacing:1px;min-height:24px;display:inline-flex;align-items:center;transition:color .15s,background .15s}.context-menu-trigger.svelte-1nmhce7:hover{color:var(--text);background:var(--border)}.context-menu-backdrop.svelte-1nmhce7{position:fixed;top:0;right:0;bottom:0;left:0;z-index:999}.context-menu.svelte-1nmhce7{position:fixed;list-style:none;margin:0;padding:4px 0;background:var(--surface);border:1px solid var(--border);border-radius:8px;box-shadow:0 4px 16px #0006;z-index:1000;min-width:175px}.context-menu-item.svelte-1nmhce7{padding:9px 14px;font-size:.85rem;cursor:pointer;color:var(--text);white-space:nowrap}.context-menu-item.svelte-1nmhce7:hover{background:var(--border)}.context-menu-item.svelte-1nmhce7:focus{outline:2px solid var(--accent);outline-offset:-2px}.context-menu-item--danger.svelte-1nmhce7{color:#e74c3c}.context-menu-item--danger.svelte-1nmhce7:hover{background:#e74c3c1f}.context-menu-item--disabled.svelte-1nmhce7{opacity:.4;cursor:default}.context-menu-item--disabled.svelte-1nmhce7:hover{background:none}.workspace-item.svelte-168i8d5{display:flex;flex-direction:column}.workspace-header.svelte-168i8d5{display:flex;align-items:center;justify-content:space-between;padding:8px 10px;cursor:pointer;min-height:44px;transition:background .12s}.workspace-header.svelte-168i8d5:hover{background:var(--surface-hover)}.workspace-item.active.svelte-168i8d5 .workspace-header:where(.svelte-168i8d5){background:var(--surface-hover)}.workspace-left.svelte-168i8d5{display:flex;align-items:center;gap:8px;min-width:0;flex:1}.grip-handle.svelte-168i8d5{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:.8rem;color:var(--text-muted);cursor:grab;flex-shrink:0;opacity:0;transition:opacity .12s;-webkit-user-select:none;user-select:none}.workspace-header.svelte-168i8d5:hover .grip-handle:where(.svelte-168i8d5){opacity:1}.grip-handle.grip-visible.svelte-168i8d5{opacity:1}.workspace-header.reorder-mode.svelte-168i8d5{cursor:grab}.workspace-header.reorder-mode.svelte-168i8d5:active{cursor:grabbing}.collapse-chevron.svelte-168i8d5{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:.7rem;color:var(--text-muted);cursor:pointer;flex-shrink:0;transition:color .12s}.collapse-chevron.svelte-168i8d5:hover{color:var(--text)}.collapse-count.svelte-168i8d5{font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-muted);background:var(--border);border-radius:8px;padding:1px 6px;flex-shrink:0}.initial-block.svelte-168i8d5{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:4px;font-size:var(--font-size-xs);font-weight:700;color:#000;font-family:var(--font-mono);flex-shrink:0;line-height:1}.workspace-name.svelte-168i8d5{font-size:var(--font-size-sm);font-weight:700;color:var(--text);font-family:var(--font-mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}.workspace-actions.svelte-168i8d5{display:flex;align-items:center;gap:2px;opacity:0;transition:opacity .12s;flex-shrink:0}.workspace-header.svelte-168i8d5:hover .workspace-actions:where(.svelte-168i8d5){opacity:1}.action-btn.svelte-168i8d5{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:3px;font-size:var(--font-size-xs);color:var(--text-muted);cursor:pointer;font-family:var(--font-mono);transition:background .1s,color .1s}.action-btn.svelte-168i8d5:hover{background:var(--border);color:var(--text)}.session-list.svelte-168i8d5{list-style:none}.session-row.svelte-168i8d5{display:flex;flex-direction:column;gap:2px;padding:6px 10px 6px 36px;cursor:pointer;min-height:44px;font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-muted);transition:background .1s;border-left:3px solid transparent;justify-content:center}.session-row.svelte-168i8d5:hover{background:var(--surface-hover);color:var(--text)}.session-row.selected.svelte-168i8d5{border-left-color:var(--accent);background:var(--surface-hover);color:var(--text)}.session-row.attention.svelte-168i8d5 .session-name:where(.svelte-168i8d5){font-weight:700;color:var(--text)}.session-row-primary.svelte-168i8d5{display:flex;align-items:center;gap:8px;min-width:0}.session-row-secondary.svelte-168i8d5{display:flex;align-items:center;gap:6px;padding-left:15px;font-size:.65rem;color:var(--text-muted);opacity:.7;min-width:0}.secondary-branch.svelte-168i8d5{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}.context-menu-spacer.svelte-168i8d5{flex:1}.secondary-pr.svelte-168i8d5{white-space:nowrap;flex-shrink:0;color:var(--accent)}.secondary-time.svelte-168i8d5{white-space:nowrap;flex-shrink:0;opacity:.7}.session-count-badge.svelte-168i8d5{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;border-radius:8px;background:var(--border);color:var(--text-muted);font-size:.55rem;font-family:var(--font-mono);font-weight:600;padding:0 4px;flex-shrink:0}.diff-badge.svelte-168i8d5{display:inline-flex;align-items:center;gap:4px;font-size:.6rem;flex-shrink:0;margin-left:auto}.diff-add.svelte-168i8d5{color:var(--status-success)}.diff-del.svelte-168i8d5{color:var(--status-error)}.status-dot.svelte-168i8d5{display:inline-block;width:7px;height:7px;border-radius:50%;flex-shrink:0}.status-dot--running.svelte-168i8d5{background:var(--status-success)}.status-dot--idle.svelte-168i8d5{background:var(--status-info)}.dot-inactive.svelte-168i8d5{width:7px;height:7px;border-radius:50%;background:#555;flex-shrink:0}.session-row.inactive.svelte-168i8d5 .session-name:where(.svelte-168i8d5){color:var(--text-muted)}.session-row.inactive.svelte-168i8d5:hover .session-name:where(.svelte-168i8d5){color:var(--text)}.session-row.loading.svelte-168i8d5{pointer-events:none;opacity:.7}.session-row.loading.svelte-168i8d5 .session-name:where(.svelte-168i8d5){color:var(--accent)}.status-dot--attention.svelte-168i8d5{background:var(--status-warning);box-shadow:0 0 5px 1px #fbbf2473;animation:svelte-168i8d5-attention-glow 2s ease-in-out infinite}@keyframes svelte-168i8d5-attention-glow{0%,to{box-shadow:0 0 3px 1px #fbbf244d}50%{box-shadow:0 0 7px 2px #fbbf2499}}.terminal-icon.svelte-168i8d5{font-size:.6rem;font-weight:700;color:var(--text-muted);flex-shrink:0;font-family:var(--font-mono);line-height:1}.session-name.svelte-168i8d5{flex:1;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;min-width:0}.session-name.bold.svelte-168i8d5{font-weight:700}.add-worktree-row.svelte-168i8d5{padding:4px 10px 6px 36px}.add-worktree-btn.svelte-168i8d5{font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-muted);opacity:.5;cursor:pointer;transition:opacity .1s,color .1s}.add-worktree-btn.svelte-168i8d5:hover{opacity:1;color:var(--text)}.add-worktree-row.disabled.svelte-168i8d5{pointer-events:none}.add-worktree-row.disabled.svelte-168i8d5 .add-worktree-btn:where(.svelte-168i8d5){opacity:.7;color:var(--accent)}.workspace-divider.svelte-168i8d5{height:1px;background:var(--border);margin:0}@media(max-width:600px){.workspace-header.svelte-168i8d5,.session-row.svelte-168i8d5{min-height:48px}.workspace-actions.svelte-168i8d5,.grip-handle.grip-visible.svelte-168i8d5{opacity:1}}.smart-search.svelte-itm1qs{position:relative;flex-shrink:0}.input-row.svelte-itm1qs{display:flex;align-items:center;padding:6px 10px;border-bottom:1px solid var(--border);gap:6px}.prompt.svelte-itm1qs{font-family:var(--font-mono);font-size:var(--font-size-sm);color:var(--accent);flex-shrink:0;line-height:1;-webkit-user-select:none;user-select:none}.search-input.svelte-itm1qs{flex:1;background:transparent;border:none;outline:none;color:var(--text);font-family:var(--font-mono);font-size:var(--font-size-sm);caret-color:var(--accent)}.search-input.svelte-itm1qs::placeholder{color:var(--text-muted);opacity:.5}.dropdown.svelte-itm1qs{position:absolute;top:100%;left:0;right:0;background:var(--surface);border:1px solid var(--border);border-top:none;list-style:none;z-index:200;max-height:240px;overflow-y:auto}.dropdown-item.svelte-itm1qs{display:flex;align-items:baseline;gap:6px;padding:8px 10px;cursor:pointer;font-family:var(--font-mono);font-size:var(--font-size-sm);color:var(--text-muted);transition:background .1s;white-space:nowrap;overflow:hidden}.dropdown-item.svelte-itm1qs:hover,.dropdown-item.focused.svelte-itm1qs{background:var(--surface-hover);color:var(--text)}.dropdown-item.svelte-itm1qs strong:where(.svelte-itm1qs){color:var(--text);font-weight:700}.dropdown-path.svelte-itm1qs{font-size:var(--font-size-xs);color:var(--text-muted);opacity:.5;overflow:hidden;text-overflow:ellipsis;min-width:0;flex-shrink:1}@media(max-width:600px){.smart-search.svelte-itm1qs{width:100%}.input-row.svelte-itm1qs{width:100%;box-sizing:border-box}.search-input.svelte-itm1qs{width:100%;min-width:0}}.sidebar.svelte-owj5vn{position:relative;display:flex;flex-direction:column;background:var(--bg);border-right:1px solid var(--border);overflow:hidden;transition:transform .25s ease,width .2s ease,min-width .2s ease;z-index:100}.resize-handle.svelte-owj5vn{position:absolute;top:0;right:0;width:4px;height:100%;cursor:col-resize;z-index:10;transition:background .15s}.resize-handle.svelte-owj5vn:hover{background:var(--accent)}.sidebar-header.svelte-owj5vn{display:flex;align-items:center;justify-content:space-between;padding:12px 10px;border-bottom:1px solid var(--border);flex-shrink:0}.sidebar-label.svelte-owj5vn{flex:1;font-size:var(--font-size-xs);font-weight:600;color:var(--text-muted);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em}.collapse-btn.svelte-owj5vn{background:none;border:none;color:var(--text-muted);font-size:1.1rem;cursor:pointer;padding:8px 10px;border-radius:4px;flex-shrink:0;line-height:1;font-family:var(--font-mono);min-width:36px;min-height:36px;display:flex;align-items:center;justify-content:center}.collapse-btn.svelte-owj5vn:hover{color:var(--text);background:var(--border)}.sidebar.collapsed.svelte-owj5vn .sidebar-header:where(.svelte-owj5vn){justify-content:center;padding:12px 4px}.icon-btn.svelte-owj5vn{background:none;border:none;color:var(--text);font-size:1.2rem;cursor:pointer;padding:4px 6px;border-radius:4px;touch-action:manipulation;display:none}.icon-btn.svelte-owj5vn:active{background:var(--border)}.workspace-list.svelte-owj5vn{flex:1;overflow-y:auto;overflow-x:hidden}.empty-state.svelte-owj5vn{padding:16px 10px;font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-muted);opacity:.5;text-align:center}.add-workspace-btn.svelte-owj5vn{margin:8px;padding:10px 12px;min-height:40px;background:none;border:1px solid var(--border);border-radius:0;color:var(--text);font-size:var(--font-size-xs);font-family:var(--font-mono);cursor:pointer;touch-action:manipulation;text-align:center;flex-shrink:0;transition:background .1s,border-color .1s}.add-workspace-btn.svelte-owj5vn{border-color:var(--accent);color:var(--accent)}.add-workspace-btn.svelte-owj5vn:hover{background:color-mix(in srgb,var(--accent) 12%,transparent)}.add-workspace-btn.svelte-owj5vn:active{background:var(--border)}.done-reorder-btn.svelte-owj5vn{margin:8px;padding:10px 12px;min-height:40px;background:none;border:1px solid var(--accent);border-radius:0;color:var(--accent);font-size:var(--font-size-xs);font-family:var(--font-mono);cursor:pointer;touch-action:manipulation;text-align:center;flex-shrink:0;transition:background .1s}.done-reorder-btn.svelte-owj5vn:hover{background:color-mix(in srgb,var(--accent) 12%,transparent)}.done-reorder-btn.svelte-owj5vn:active{background:var(--border)}.settings-btn.svelte-owj5vn{margin:0 8px 8px;padding:10px 12px;min-height:40px;background:none;border:1px solid var(--border);border-radius:0;color:var(--text-muted);font-size:var(--font-size-xs);font-family:var(--font-mono);cursor:pointer;touch-action:manipulation;text-align:center;flex-shrink:0;transition:background .1s}.settings-btn.svelte-owj5vn:hover{background:var(--surface-hover);color:var(--text)}.settings-btn.svelte-owj5vn:active{background:var(--border)}@media(max-width:600px){.sidebar.svelte-owj5vn{position:fixed;top:0;left:0;height:100%;transform:translate(-100%);box-shadow:2px 0 12px #00000080;transition:transform .25s ease}.sidebar.open.svelte-owj5vn{transform:translate(0)}.collapse-btn.svelte-owj5vn{display:none}.icon-btn.svelte-owj5vn{display:block;font-size:1.4rem;padding:4px 8px}.resize-handle.svelte-owj5vn{display:none}}/**
1
+ .pin-gate.svelte-1qp96yb{display:flex;align-items:center;justify-content:center;height:100vh;background:var(--bg);padding:1rem}.pin-container.svelte-1qp96yb{display:flex;flex-direction:column;align-items:center;gap:1rem;width:100%;max-width:320px;text-align:center}.pin-container.svelte-1qp96yb h1:where(.svelte-1qp96yb){font-size:1.5rem;color:var(--text)}.pin-container.svelte-1qp96yb p:where(.svelte-1qp96yb){color:var(--text-muted);font-size:.95rem}input.svelte-1qp96yb{width:100%;padding:14px 16px;background:var(--surface);border:1px solid var(--border);border-radius:8px;color:var(--text);font-size:1.2rem;text-align:center;outline:none;-webkit-appearance:none}input.svelte-1qp96yb:focus{border-color:var(--accent)}button.svelte-1qp96yb{width:100%;padding:14px;background:var(--accent);color:#fff;border:none;border-radius:8px;font-size:1rem;font-weight:600;cursor:pointer;touch-action:manipulation}button.svelte-1qp96yb:active{opacity:.8}.error.svelte-1qp96yb{color:var(--accent);font-size:.9rem}.context-menu-trigger.svelte-1nmhce7{background:none;border:none;color:var(--text-muted);font-size:1rem;font-weight:700;cursor:pointer;padding:0 6px;border-radius:4px;touch-action:manipulation;flex-shrink:0;line-height:1;letter-spacing:1px;min-height:24px;display:inline-flex;align-items:center;transition:color .15s,background .15s}.context-menu-trigger.svelte-1nmhce7:hover{color:var(--text);background:var(--border)}.context-menu-backdrop.svelte-1nmhce7{position:fixed;top:0;right:0;bottom:0;left:0;z-index:999}.context-menu.svelte-1nmhce7{position:fixed;list-style:none;margin:0;padding:4px 0;background:var(--surface);border:1px solid var(--border);border-radius:8px;box-shadow:0 4px 16px #0006;z-index:1000;min-width:175px}.context-menu-item.svelte-1nmhce7{padding:9px 14px;font-size:.85rem;cursor:pointer;color:var(--text);white-space:nowrap}.context-menu-item.svelte-1nmhce7:hover{background:var(--border)}.context-menu-item.svelte-1nmhce7:focus{outline:2px solid var(--accent);outline-offset:-2px}.context-menu-item--danger.svelte-1nmhce7{color:#e74c3c}.context-menu-item--danger.svelte-1nmhce7:hover{background:#e74c3c1f}.context-menu-item--disabled.svelte-1nmhce7{opacity:.4;cursor:default}.context-menu-item--disabled.svelte-1nmhce7:hover{background:none}.workspace-item.svelte-168i8d5{display:flex;flex-direction:column}.workspace-header.svelte-168i8d5{display:flex;align-items:center;justify-content:space-between;padding:8px 10px;cursor:pointer;min-height:44px;transition:background .12s}.workspace-header.svelte-168i8d5:hover{background:var(--surface-hover)}.workspace-item.active.svelte-168i8d5 .workspace-header:where(.svelte-168i8d5){background:var(--surface-hover)}.workspace-left.svelte-168i8d5{display:flex;align-items:center;gap:8px;min-width:0;flex:1}.grip-handle.svelte-168i8d5{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:.8rem;color:var(--text-muted);cursor:grab;flex-shrink:0;opacity:0;transition:opacity .12s;-webkit-user-select:none;user-select:none}.workspace-header.svelte-168i8d5:hover .grip-handle:where(.svelte-168i8d5){opacity:1}.grip-handle.grip-visible.svelte-168i8d5{opacity:1}.workspace-header.reorder-mode.svelte-168i8d5{cursor:grab}.workspace-header.reorder-mode.svelte-168i8d5:active{cursor:grabbing}.collapse-chevron.svelte-168i8d5{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:.7rem;color:var(--text-muted);cursor:pointer;flex-shrink:0;transition:color .12s}.collapse-chevron.svelte-168i8d5:hover{color:var(--text)}.collapse-count.svelte-168i8d5{font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-muted);background:var(--border);border-radius:8px;padding:1px 6px;flex-shrink:0}.initial-block.svelte-168i8d5{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:4px;font-size:var(--font-size-xs);font-weight:700;color:#000;font-family:var(--font-mono);flex-shrink:0;line-height:1}.workspace-name.svelte-168i8d5{font-size:var(--font-size-sm);font-weight:700;color:var(--text);font-family:var(--font-mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}.workspace-actions.svelte-168i8d5{display:flex;align-items:center;gap:2px;opacity:0;transition:opacity .12s;flex-shrink:0}.workspace-header.svelte-168i8d5:hover .workspace-actions:where(.svelte-168i8d5){opacity:1}.action-btn.svelte-168i8d5{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:3px;font-size:var(--font-size-xs);color:var(--text-muted);cursor:pointer;font-family:var(--font-mono);transition:background .1s,color .1s}.action-btn.svelte-168i8d5:hover{background:var(--border);color:var(--text)}.session-list.svelte-168i8d5{list-style:none}.session-row.svelte-168i8d5{display:flex;flex-direction:column;gap:2px;padding:6px 10px 6px 36px;cursor:pointer;min-height:44px;font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-muted);transition:background .1s;border-left:3px solid transparent;justify-content:center}.session-row.svelte-168i8d5:hover{background:var(--surface-hover);color:var(--text)}.session-row.selected.svelte-168i8d5{border-left-color:var(--accent);background:var(--surface-hover);color:var(--text)}.session-row.attention.svelte-168i8d5 .session-name:where(.svelte-168i8d5){font-weight:700;color:var(--text)}.session-row-primary.svelte-168i8d5{display:flex;align-items:center;gap:8px;min-width:0}.session-row-secondary.svelte-168i8d5{display:flex;align-items:center;gap:6px;padding-left:15px;font-size:.65rem;color:var(--text-muted);opacity:.7;min-width:0}.secondary-branch.svelte-168i8d5{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}.context-menu-spacer.svelte-168i8d5{flex:1}.secondary-pr.svelte-168i8d5{white-space:nowrap;flex-shrink:0;color:var(--accent)}.secondary-time.svelte-168i8d5{white-space:nowrap;flex-shrink:0;opacity:.7}.session-count-badge.svelte-168i8d5{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;border-radius:8px;background:var(--border);color:var(--text-muted);font-size:.55rem;font-family:var(--font-mono);font-weight:600;padding:0 4px;flex-shrink:0}.diff-badge.svelte-168i8d5{display:inline-flex;align-items:center;gap:4px;font-size:.6rem;flex-shrink:0;margin-left:auto}.diff-add.svelte-168i8d5{color:var(--status-success)}.diff-del.svelte-168i8d5{color:var(--status-error)}.status-dot.svelte-168i8d5{display:inline-block;width:7px;height:7px;border-radius:50%;flex-shrink:0}.status-dot--running.svelte-168i8d5{background:var(--status-success)}.status-dot--idle.svelte-168i8d5{background:var(--status-info)}.dot-inactive.svelte-168i8d5{width:7px;height:7px;border-radius:50%;background:#555;flex-shrink:0}.session-row.inactive.svelte-168i8d5 .session-name:where(.svelte-168i8d5){color:var(--text-muted)}.session-row.inactive.svelte-168i8d5:hover .session-name:where(.svelte-168i8d5){color:var(--text)}.session-row.loading.svelte-168i8d5{pointer-events:none;opacity:.7}.session-row.loading.svelte-168i8d5 .session-name:where(.svelte-168i8d5){color:var(--accent)}.status-dot--attention.svelte-168i8d5{background:var(--status-warning);box-shadow:0 0 5px 1px #fbbf2473;animation:svelte-168i8d5-attention-glow 2s ease-in-out infinite}.status-dot--permission-prompt.svelte-168i8d5{background:#eab308;box-shadow:0 0 5px 1px #eab30873;animation:svelte-168i8d5-attention-glow 1.5s ease-in-out infinite}@keyframes svelte-168i8d5-attention-glow{0%,to{box-shadow:0 0 3px 1px #fbbf244d}50%{box-shadow:0 0 7px 2px #fbbf2499}}.terminal-icon.svelte-168i8d5{font-size:.6rem;font-weight:700;color:var(--text-muted);flex-shrink:0;font-family:var(--font-mono);line-height:1}.session-name.svelte-168i8d5{flex:1;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;min-width:0}.session-name.bold.svelte-168i8d5{font-weight:700}.add-worktree-row.svelte-168i8d5{padding:4px 10px 6px 36px}.add-worktree-btn.svelte-168i8d5{font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-muted);opacity:.5;cursor:pointer;transition:opacity .1s,color .1s}.add-worktree-btn.svelte-168i8d5:hover{opacity:1;color:var(--text)}.add-worktree-row.disabled.svelte-168i8d5{pointer-events:none}.add-worktree-row.disabled.svelte-168i8d5 .add-worktree-btn:where(.svelte-168i8d5){opacity:.7;color:var(--accent)}.workspace-divider.svelte-168i8d5{height:1px;background:var(--border);margin:0}@media(max-width:600px){.workspace-header.svelte-168i8d5,.session-row.svelte-168i8d5{min-height:48px}.workspace-actions.svelte-168i8d5,.grip-handle.grip-visible.svelte-168i8d5{opacity:1}}.smart-search.svelte-itm1qs{position:relative;flex-shrink:0}.input-row.svelte-itm1qs{display:flex;align-items:center;padding:6px 10px;border-bottom:1px solid var(--border);gap:6px}.prompt.svelte-itm1qs{font-family:var(--font-mono);font-size:var(--font-size-sm);color:var(--accent);flex-shrink:0;line-height:1;-webkit-user-select:none;user-select:none}.search-input.svelte-itm1qs{flex:1;background:transparent;border:none;outline:none;color:var(--text);font-family:var(--font-mono);font-size:var(--font-size-sm);caret-color:var(--accent)}.search-input.svelte-itm1qs::placeholder{color:var(--text-muted);opacity:.5}.dropdown.svelte-itm1qs{position:absolute;top:100%;left:0;right:0;background:var(--surface);border:1px solid var(--border);border-top:none;list-style:none;z-index:200;max-height:240px;overflow-y:auto}.dropdown-item.svelte-itm1qs{display:flex;align-items:baseline;gap:6px;padding:8px 10px;cursor:pointer;font-family:var(--font-mono);font-size:var(--font-size-sm);color:var(--text-muted);transition:background .1s;white-space:nowrap;overflow:hidden}.dropdown-item.svelte-itm1qs:hover,.dropdown-item.focused.svelte-itm1qs{background:var(--surface-hover);color:var(--text)}.dropdown-item.svelte-itm1qs strong:where(.svelte-itm1qs){color:var(--text);font-weight:700}.dropdown-path.svelte-itm1qs{font-size:var(--font-size-xs);color:var(--text-muted);opacity:.5;overflow:hidden;text-overflow:ellipsis;min-width:0;flex-shrink:1}@media(max-width:600px){.smart-search.svelte-itm1qs{width:100%}.input-row.svelte-itm1qs{width:100%;box-sizing:border-box}.search-input.svelte-itm1qs{width:100%;min-width:0}}.sidebar.svelte-owj5vn{position:relative;display:flex;flex-direction:column;background:var(--bg);border-right:1px solid var(--border);overflow:hidden;transition:transform .25s ease,width .2s ease,min-width .2s ease;z-index:100}.resize-handle.svelte-owj5vn{position:absolute;top:0;right:0;width:4px;height:100%;cursor:col-resize;z-index:10;transition:background .15s}.resize-handle.svelte-owj5vn:hover{background:var(--accent)}.sidebar-header.svelte-owj5vn{display:flex;align-items:center;justify-content:space-between;padding:12px 10px;border-bottom:1px solid var(--border);flex-shrink:0}.sidebar-label.svelte-owj5vn{flex:1;font-size:var(--font-size-xs);font-weight:600;color:var(--text-muted);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em}.collapse-btn.svelte-owj5vn{background:none;border:none;color:var(--text-muted);font-size:1.1rem;cursor:pointer;padding:8px 10px;border-radius:4px;flex-shrink:0;line-height:1;font-family:var(--font-mono);min-width:36px;min-height:36px;display:flex;align-items:center;justify-content:center}.collapse-btn.svelte-owj5vn:hover{color:var(--text);background:var(--border)}.sidebar.collapsed.svelte-owj5vn .sidebar-header:where(.svelte-owj5vn){justify-content:center;padding:12px 4px}.icon-btn.svelte-owj5vn{background:none;border:none;color:var(--text);font-size:1.2rem;cursor:pointer;padding:4px 6px;border-radius:4px;touch-action:manipulation;display:none}.icon-btn.svelte-owj5vn:active{background:var(--border)}.workspace-list.svelte-owj5vn{flex:1;overflow-y:auto;overflow-x:hidden}.empty-state.svelte-owj5vn{padding:16px 10px;font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-muted);opacity:.5;text-align:center}.add-workspace-btn.svelte-owj5vn{margin:8px;padding:10px 12px;min-height:40px;background:none;border:1px solid var(--border);border-radius:0;color:var(--text);font-size:var(--font-size-xs);font-family:var(--font-mono);cursor:pointer;touch-action:manipulation;text-align:center;flex-shrink:0;transition:background .1s,border-color .1s}.add-workspace-btn.svelte-owj5vn{border-color:var(--accent);color:var(--accent)}.add-workspace-btn.svelte-owj5vn:hover{background:color-mix(in srgb,var(--accent) 12%,transparent)}.add-workspace-btn.svelte-owj5vn:active{background:var(--border)}.done-reorder-btn.svelte-owj5vn{margin:8px;padding:10px 12px;min-height:40px;background:none;border:1px solid var(--accent);border-radius:0;color:var(--accent);font-size:var(--font-size-xs);font-family:var(--font-mono);cursor:pointer;touch-action:manipulation;text-align:center;flex-shrink:0;transition:background .1s}.done-reorder-btn.svelte-owj5vn:hover{background:color-mix(in srgb,var(--accent) 12%,transparent)}.done-reorder-btn.svelte-owj5vn:active{background:var(--border)}.settings-btn.svelte-owj5vn{margin:0 8px 8px;padding:10px 12px;min-height:40px;background:none;border:1px solid var(--border);border-radius:0;color:var(--text-muted);font-size:var(--font-size-xs);font-family:var(--font-mono);cursor:pointer;touch-action:manipulation;text-align:center;flex-shrink:0;transition:background .1s}.settings-btn.svelte-owj5vn:hover{background:var(--surface-hover);color:var(--text)}.settings-btn.svelte-owj5vn:active{background:var(--border)}@media(max-width:600px){.sidebar.svelte-owj5vn{position:fixed;top:0;left:0;height:100%;transform:translate(-100%);box-shadow:2px 0 12px #00000080;transition:transform .25s ease}.sidebar.open.svelte-owj5vn{transform:translate(0)}.collapse-btn.svelte-owj5vn{display:none}.icon-btn.svelte-owj5vn{display:block;font-size:1.4rem;padding:4px 8px}.resize-handle.svelte-owj5vn{display:none}}/**
2
2
  * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
3
  * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
4
  * https://github.com/chjj/term.js
@@ -11,8 +11,8 @@
11
11
  <meta name="apple-mobile-web-app-capable" content="yes" />
12
12
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
13
13
  <meta name="theme-color" content="#1a1a1a" />
14
- <script type="module" crossorigin src="/assets/index-Dn_qB0Yk.js"></script>
15
- <link rel="stylesheet" crossorigin href="/assets/index-m2Bcao6e.css">
14
+ <script type="module" crossorigin src="/assets/index-BSLqoOlv.js"></script>
15
+ <link rel="stylesheet" crossorigin href="/assets/index-BtybMy6I.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="app"></div>
@@ -0,0 +1,54 @@
1
+ // Strip ANSI escape sequences (CSI, OSC, charset, mode sequences)
2
+ const ANSI_RE = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\x1b[()][AB012]|\x1b\[\?[0-9;]*[hlm]|\x1b\[[0-9]*[ABCDJKH]/g;
3
+ /**
4
+ * Claude Code output parser.
5
+ *
6
+ * Claude Code uses a React/Ink TUI. This parser detects semantic state
7
+ * transitions by pattern-matching on cleaned terminal output.
8
+ */
9
+ export class ClaudeOutputParser {
10
+ currentState = 'initializing';
11
+ hasSeenFirstPrompt = false;
12
+ onData(chunk, _recentScrollback) {
13
+ const clean = chunk.replace(ANSI_RE, '');
14
+ if (!clean.trim())
15
+ return null;
16
+ const newState = this.classify(clean);
17
+ if (newState && newState !== this.currentState) {
18
+ this.currentState = newState;
19
+ return { state: newState };
20
+ }
21
+ return null;
22
+ }
23
+ reset() {
24
+ this.currentState = 'initializing';
25
+ this.hasSeenFirstPrompt = false;
26
+ }
27
+ get state() {
28
+ return this.currentState;
29
+ }
30
+ classify(clean) {
31
+ // Permission prompt detection (highest priority)
32
+ if (/\bAllow\b/.test(clean) && /\bDeny\b/.test(clean)) {
33
+ return 'permission-prompt';
34
+ }
35
+ // Error detection
36
+ if (/^Error:|^ERROR:|✗\s|error:/m.test(clean)) {
37
+ return 'error';
38
+ }
39
+ // Waiting for input: the ">" prompt on its own line, or initial greeting
40
+ if (/^>\s*$/m.test(clean) || /How can I help/i.test(clean) || /What would you like/i.test(clean)) {
41
+ this.hasSeenFirstPrompt = true;
42
+ return 'waiting-for-input';
43
+ }
44
+ // Processing: substantive output after the first prompt
45
+ if (this.hasSeenFirstPrompt && clean.trim().length > 0) {
46
+ return 'processing';
47
+ }
48
+ // Still initializing if we haven't seen the first prompt
49
+ if (!this.hasSeenFirstPrompt) {
50
+ return 'initializing';
51
+ }
52
+ return null;
53
+ }
54
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Codex output parser — stub.
3
+ *
4
+ * Always returns null, which tells the system to fall back to
5
+ * timer-based idle detection. Replace with real pattern matching
6
+ * when Codex TUI patterns are mapped.
7
+ */
8
+ export class CodexOutputParser {
9
+ onData(_chunk, _recentScrollback) {
10
+ return null;
11
+ }
12
+ reset() {
13
+ // No state to reset
14
+ }
15
+ }
@@ -0,0 +1,7 @@
1
+ import { ClaudeOutputParser } from './claude-parser.js';
2
+ import { CodexOutputParser } from './codex-parser.js';
3
+ /** Registry: factory per agent type */
4
+ export const outputParsers = {
5
+ claude: () => new ClaudeOutputParser(),
6
+ codex: () => new CodexOutputParser(),
7
+ };
@@ -5,6 +5,7 @@ import path from 'node:path';
5
5
  import { AGENT_COMMANDS, AGENT_CONTINUE_ARGS } from './types.js';
6
6
  import { readMeta, writeMeta } from './config.js';
7
7
  import { fireSessionEnd } from './sessions.js';
8
+ import { outputParsers } from './output-parsers/index.js';
8
9
  const IDLE_TIMEOUT_MS = 5000;
9
10
  const MAX_SCROLLBACK = 256 * 1024; // 256KB max
10
11
  export function generateTmuxSessionName(displayName, id) {
@@ -22,7 +23,7 @@ export function resolveTmuxSpawn(command, args, tmuxSessionName) {
22
23
  ],
23
24
  };
24
25
  }
25
- export function createPtySession(params, sessionsMap, idleChangeCallbacks) {
26
+ export function createPtySession(params, sessionsMap, idleChangeCallbacks, stateChangeCallbacks = []) {
26
27
  const { id, type, agent = 'claude', repoName, repoPath, cwd, root, worktreeName, branchName, displayName, command, args = [], cols = 80, rows = 24, configPath, useTmux: paramUseTmux, tmuxSessionName: paramTmuxSessionName, initialScrollback, restored: paramRestored, } = params;
27
28
  const createdAt = new Date().toISOString();
28
29
  const resolvedCommand = command || AGENT_COMMANDS[agent];
@@ -49,6 +50,8 @@ export function createPtySession(params, sessionsMap, idleChangeCallbacks) {
49
50
  const scrollback = initialScrollback ? [...initialScrollback] : [];
50
51
  let scrollbackBytes = initialScrollback ? initialScrollback.reduce((sum, s) => sum + s.length, 0) : 0;
51
52
  const resolvedCwd = cwd || repoPath;
53
+ // Instantiate vendor-specific output parser (terminal/custom-command sessions get no parser)
54
+ const parser = command ? outputParsers['claude']() : outputParsers[agent]();
52
55
  const session = {
53
56
  id,
54
57
  type: type || 'worktree',
@@ -73,6 +76,8 @@ export function createPtySession(params, sessionsMap, idleChangeCallbacks) {
73
76
  status: 'active',
74
77
  restored: paramRestored || false,
75
78
  needsBranchRename: false,
79
+ agentState: 'initializing',
80
+ outputParser: parser,
76
81
  };
77
82
  sessionsMap.set(id, session);
78
83
  // Load existing metadata to preserve a previously-set displayName
@@ -121,6 +126,13 @@ export function createPtySession(params, sessionsMap, idleChangeCallbacks) {
121
126
  writeMeta(configPath, { worktreePath: repoPath, displayName: session.displayName, lastActivity: session.lastActivity });
122
127
  }, 5000);
123
128
  }
129
+ // Vendor-specific output parsing for semantic state detection
130
+ const parseResult = session.outputParser.onData(data, scrollback.slice(-20));
131
+ if (parseResult && parseResult.state !== session.agentState) {
132
+ session.agentState = parseResult.state;
133
+ for (const cb of stateChangeCallbacks)
134
+ cb(session.id, parseResult.state);
135
+ }
124
136
  });
125
137
  proc.onExit(() => {
126
138
  if (canRetry && (Date.now() - spawnTime) < 3000) {
@@ -213,6 +225,7 @@ export function createPtySession(params, sessionsMap, idleChangeCallbacks) {
213
225
  tmuxSessionName,
214
226
  status: 'active',
215
227
  needsBranchRename: false,
228
+ agentState: 'initializing',
216
229
  };
217
230
  return { session, result };
218
231
  }
@@ -17,6 +17,10 @@ const idleChangeCallbacks = [];
17
17
  function onIdleChange(cb) {
18
18
  idleChangeCallbacks.push(cb);
19
19
  }
20
+ const stateChangeCallbacks = [];
21
+ function onStateChange(cb) {
22
+ stateChangeCallbacks.push(cb);
23
+ }
20
24
  const sessionEndCallbacks = [];
21
25
  function onSessionEnd(cb) {
22
26
  sessionEndCallbacks.push(cb);
@@ -49,7 +53,7 @@ function create({ id: providedId, type, agent = 'claude', repoName, repoPath, cw
49
53
  initialScrollback,
50
54
  restored: paramRestored,
51
55
  };
52
- const { session: ptySession, result } = createPtySession(ptyParams, sessions, idleChangeCallbacks);
56
+ const { session: ptySession, result } = createPtySession(ptyParams, sessions, idleChangeCallbacks, stateChangeCallbacks);
53
57
  if (paramNeedsBranchRename) {
54
58
  ptySession.needsBranchRename = true;
55
59
  }
@@ -83,6 +87,7 @@ function list() {
83
87
  tmuxSessionName: s.tmuxSessionName,
84
88
  status: s.status,
85
89
  needsBranchRename: !!s.needsBranchRename,
90
+ agentState: s.agentState,
86
91
  }))
87
92
  .sort((a, b) => b.lastActivity.localeCompare(a.lastActivity));
88
93
  }
@@ -343,4 +348,4 @@ async function populateMetaCache() {
343
348
  }
344
349
  // Re-export pty-handler utilities for backward compatibility
345
350
  export { generateTmuxSessionName, resolveTmuxSpawn } from './pty-handler.js';
346
- export { create, get, list, kill, killAllTmuxSessions, resize, updateDisplayName, write, onIdleChange, onSessionEnd, fireSessionEnd, findRepoSession, nextTerminalName, serializeAll, restoreFromDisk, activeTmuxSessionNames, getSessionMeta, getAllSessionMeta, populateMetaCache, AGENT_COMMANDS, AGENT_CONTINUE_ARGS, AGENT_YOLO_ARGS };
351
+ export { create, get, list, kill, killAllTmuxSessions, resize, updateDisplayName, write, onIdleChange, onStateChange, onSessionEnd, fireSessionEnd, findRepoSession, nextTerminalName, serializeAll, restoreFromDisk, activeTmuxSessionNames, getSessionMeta, getAllSessionMeta, populateMetaCache, AGENT_COMMANDS, AGENT_CONTINUE_ARGS, AGENT_YOLO_ARGS };
package/dist/server/ws.js CHANGED
@@ -38,6 +38,42 @@ function startBranchWatcher(session, broadcastEvent, cfgPath) {
38
38
  }
39
39
  }, BRANCH_POLL_INTERVAL_MS);
40
40
  }
41
+ /** Sideband branch rename: uses headless claude to generate a branch name from the first message */
42
+ async function spawnBranchRename(session, firstMessage, cfgPath, broadcastEvent) {
43
+ try {
44
+ const prompt = `Output ONLY a short kebab-case git branch name (no explanation, no backticks, no prefix, just the name) that describes this task:\n\n${firstMessage.slice(0, 500)}`;
45
+ const { stdout } = await execFileAsync('claude', ['-p', '--model', 'haiku', prompt], {
46
+ cwd: session.cwd,
47
+ timeout: 30000,
48
+ });
49
+ const branchName = stdout.trim().replace(/`/g, '').replace(/[^a-z0-9-]/gi, '-').replace(/-+/g, '-').replace(/^-|-$/g, '').toLowerCase().slice(0, 60);
50
+ if (!branchName)
51
+ return;
52
+ await execFileAsync('git', ['branch', '-m', branchName], { cwd: session.cwd });
53
+ // Update session state
54
+ const displayName = branchToDisplayName(branchName);
55
+ session.branchName = branchName;
56
+ session.displayName = displayName;
57
+ broadcastEvent('session-renamed', {
58
+ sessionId: session.id,
59
+ branchName,
60
+ displayName,
61
+ });
62
+ if (cfgPath) {
63
+ writeMeta(cfgPath, {
64
+ worktreePath: session.repoPath,
65
+ displayName,
66
+ lastActivity: new Date().toISOString(),
67
+ branchName,
68
+ });
69
+ }
70
+ }
71
+ catch {
72
+ // Sideband rename is best-effort — fall back to branch watcher if claude CLI isn't available
73
+ if (cfgPath)
74
+ startBranchWatcher(session, broadcastEvent, cfgPath);
75
+ }
76
+ }
41
77
  function parseCookies(cookieHeader) {
42
78
  const cookies = {};
43
79
  if (!cookieHeader)
@@ -143,29 +179,24 @@ function setupWebSocket(server, authenticatedTokens, watcher, configPath) {
143
179
  }
144
180
  }
145
181
  catch (_) { }
146
- // Branch rename interception: prepend rename prompt before the user's first message
182
+ // Sideband branch rename: capture first message, pass through unmodified, rename out-of-band
147
183
  if (ptySession.needsBranchRename) {
148
184
  if (!ptySession._renameBuffer)
149
185
  ptySession._renameBuffer = '';
150
186
  const enterIndex = str.indexOf('\r');
151
187
  if (enterIndex === -1) {
152
188
  ptySession._renameBuffer += str;
153
- ptySession.pty.write(str); // Echo to terminal so user sees what they type
189
+ ptySession.pty.write(str); // pass through to PTY normally user sees their typing
154
190
  return;
155
191
  }
156
- // Enter detected — send rename prompt + full message to PTY in one shot
192
+ // Enter detected — pass everything through unmodified
157
193
  const buffered = ptySession._renameBuffer;
158
- const beforeEnter = buffered + str.slice(0, enterIndex);
159
- const afterEnter = str.slice(enterIndex); // includes the \r
160
- // Clear the echoed input line before writing the full prompt+message
161
- const clearLine = '\r' + ' '.repeat(Math.min(beforeEnter.length + 2, 200)) + '\r';
162
- ptySession.pty.write(clearLine);
163
- const renamePrompt = `Before doing anything else, rename the current git branch using \`git branch -m <new-name>\`. Choose a short, descriptive kebab-case branch name based on the task below.${ptySession.branchRenamePrompt ? ' User preferences: ' + ptySession.branchRenamePrompt : ''} Do not ask for confirmation — just rename and proceed.\n\n`;
164
- ptySession.pty.write(renamePrompt + beforeEnter + afterEnter);
194
+ const firstMessage = buffered + str.slice(0, enterIndex);
195
+ ptySession.pty.write(str); // pass through the Enter key
165
196
  ptySession.needsBranchRename = false;
166
197
  delete ptySession._renameBuffer;
167
- if (configPath)
168
- startBranchWatcher(ptySession, broadcastEvent, configPath);
198
+ // Sideband: spawn headless claude to generate branch name (async, non-blocking)
199
+ spawnBranchRename(ptySession, firstMessage, configPath, broadcastEvent);
169
200
  return;
170
201
  }
171
202
  // Use ptySession.pty dynamically so writes go to current PTY
@@ -182,6 +213,9 @@ function setupWebSocket(server, authenticatedTokens, watcher, configPath) {
182
213
  sessions.onIdleChange((sessionId, idle) => {
183
214
  broadcastEvent('session-idle-changed', { sessionId, idle });
184
215
  });
216
+ sessions.onStateChange((sessionId, state) => {
217
+ broadcastEvent('session-state-changed', { sessionId, state });
218
+ });
185
219
  sessions.onSessionEnd((sessionId, repoPath, branchName) => {
186
220
  broadcastEvent('session-ended', { sessionId, repoPath, branchName });
187
221
  });
@@ -0,0 +1,95 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { ClaudeOutputParser } from '../server/output-parsers/claude-parser.js';
4
+ import { CodexOutputParser } from '../server/output-parsers/codex-parser.js';
5
+ import { outputParsers } from '../server/output-parsers/index.js';
6
+ describe('ClaudeOutputParser', () => {
7
+ it('starts in initializing state', () => {
8
+ const parser = new ClaudeOutputParser();
9
+ assert.equal(parser.state, 'initializing');
10
+ });
11
+ it('transitions to waiting-for-input on > prompt', () => {
12
+ const parser = new ClaudeOutputParser();
13
+ const result = parser.onData('>\n', []);
14
+ assert.deepEqual(result, { state: 'waiting-for-input' });
15
+ });
16
+ it('transitions to waiting-for-input on greeting', () => {
17
+ const parser = new ClaudeOutputParser();
18
+ const result = parser.onData('How can I help you today?', []);
19
+ assert.deepEqual(result, { state: 'waiting-for-input' });
20
+ });
21
+ it('transitions to processing after first prompt when output arrives', () => {
22
+ const parser = new ClaudeOutputParser();
23
+ // First: see the prompt
24
+ parser.onData('>\n', []);
25
+ // Then: output starts
26
+ const result = parser.onData('I will help you with that task...', []);
27
+ assert.deepEqual(result, { state: 'processing' });
28
+ });
29
+ it('transitions back to waiting-for-input after processing', () => {
30
+ const parser = new ClaudeOutputParser();
31
+ parser.onData('>\n', []);
32
+ parser.onData('Working on it...', []);
33
+ const result = parser.onData('>\n', []);
34
+ assert.deepEqual(result, { state: 'waiting-for-input' });
35
+ });
36
+ it('detects permission prompt', () => {
37
+ const parser = new ClaudeOutputParser();
38
+ parser.onData('>\n', []);
39
+ const result = parser.onData('Allow tool access to /usr/bin? Allow / Deny', []);
40
+ assert.deepEqual(result, { state: 'permission-prompt' });
41
+ });
42
+ it('detects error state', () => {
43
+ const parser = new ClaudeOutputParser();
44
+ parser.onData('>\n', []);
45
+ const result = parser.onData('Error: something went wrong', []);
46
+ assert.deepEqual(result, { state: 'error' });
47
+ });
48
+ it('ignores pure ANSI escape sequences', () => {
49
+ const parser = new ClaudeOutputParser();
50
+ const result = parser.onData('\x1b[32m\x1b[0m', []);
51
+ assert.equal(result, null);
52
+ });
53
+ it('returns null when state does not change', () => {
54
+ const parser = new ClaudeOutputParser();
55
+ parser.onData('>\n', []);
56
+ // Already in waiting-for-input, send another prompt
57
+ const result = parser.onData('>\n', []);
58
+ assert.equal(result, null);
59
+ });
60
+ it('reset returns to initializing', () => {
61
+ const parser = new ClaudeOutputParser();
62
+ parser.onData('>\n', []);
63
+ assert.equal(parser.state, 'waiting-for-input');
64
+ parser.reset();
65
+ assert.equal(parser.state, 'initializing');
66
+ });
67
+ it('stays initializing before first prompt', () => {
68
+ const parser = new ClaudeOutputParser();
69
+ const result = parser.onData('Loading configuration...', []);
70
+ // Still initializing since no prompt seen
71
+ assert.equal(result, null); // already in initializing, no change
72
+ });
73
+ });
74
+ describe('CodexOutputParser', () => {
75
+ it('always returns null', () => {
76
+ const parser = new CodexOutputParser();
77
+ assert.equal(parser.onData('any output', []), null);
78
+ assert.equal(parser.onData('>\n', []), null);
79
+ assert.equal(parser.onData('Error: something', []), null);
80
+ });
81
+ it('reset is a no-op', () => {
82
+ const parser = new CodexOutputParser();
83
+ parser.reset(); // should not throw
84
+ });
85
+ });
86
+ describe('outputParsers registry', () => {
87
+ it('creates ClaudeOutputParser for claude', () => {
88
+ const parser = outputParsers['claude']();
89
+ assert.ok(parser instanceof ClaudeOutputParser);
90
+ });
91
+ it('creates CodexOutputParser for codex', () => {
92
+ const parser = outputParsers['codex']();
93
+ assert.ok(parser instanceof CodexOutputParser);
94
+ });
95
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-remote-cli",
3
- "version": "3.4.2",
3
+ "version": "3.5.0",
4
4
  "description": "Remote web interface for Claude Code CLI sessions",
5
5
  "type": "module",
6
6
  "main": "dist/server/index.js",