claude-remote-cli 2.10.2 → 2.12.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.
@@ -29,4 +29,4 @@
29
29
  * The original design remains. The terminal itself
30
30
  * has been extended to include xterm CSI codes, among
31
31
  * other features.
32
- */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{font-family:monospace;-webkit-user-select:text;user-select:text;white-space:pre}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;background:#0000;transition:opacity .1s linear;z-index:11}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.terminal-wrapper.svelte-5qgfij{display:flex;flex:1;min-height:0;position:relative;overflow:hidden}.terminal-container.svelte-5qgfij{flex:1;min-width:0;min-height:0;overflow:hidden;padding:4px}.terminal-wrapper.drag-over.svelte-5qgfij{outline:2px dashed var(--accent);outline-offset:-2px}.terminal-scrollbar.svelte-5qgfij{width:8px;background:transparent;position:relative;flex-shrink:0}.terminal-scrollbar-thumb.svelte-5qgfij{position:absolute;right:0;width:6px;background:var(--border);border-radius:3px;cursor:pointer}@media(hover:none){.terminal-wrapper.selection-mode.svelte-5qgfij .terminal-container:where(.svelte-5qgfij){outline:2px solid var(--accent);outline-offset:-2px}.terminal-container.svelte-5qgfij{touch-action:none}.terminal-scrollbar.svelte-5qgfij{width:12px}.terminal-scrollbar-thumb.svelte-5qgfij{width:8px;min-height:44px}.scroll-fabs.svelte-5qgfij{position:absolute;right:16px;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;gap:12px;z-index:1;opacity:.6;pointer-events:auto}.scroll-fab.svelte-5qgfij{width:44px;height:44px;border-radius:50%;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:18px;display:flex;align-items:center;justify-content:center;cursor:pointer;touch-action:manipulation;-webkit-user-select:none;user-select:none}.scroll-fab.svelte-5qgfij:active{opacity:1;background:var(--border)}.scroll-fab-bottom.svelte-5qgfij{margin-top:4px}}.toolbar.svelte-k7we1m{background:var(--surface);border-top:1px solid var(--border);padding:4px;padding-bottom:calc(4px + env(safe-area-inset-bottom,0px));flex-shrink:0}.toolbar-grid.svelte-k7we1m{display:grid;grid-template-columns:repeat(6,1fr);gap:4px}.tb-btn.svelte-k7we1m{background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.85rem;padding:8px 4px;cursor:pointer;touch-action:manipulation;min-height:40px;display:flex;align-items:center;justify-content:center;user-select:none;-webkit-user-select:none}.tb-btn.svelte-k7we1m:active{background:var(--border)}.tb-enter.svelte-k7we1m{background:var(--accent);border-color:var(--accent);color:#fff}.tb-enter.svelte-k7we1m:active{opacity:.8}.mobile-header.svelte-wp0i5g{display:none;align-items:center;gap:8px;padding:8px 12px;background:var(--surface);border-bottom:1px solid var(--border);min-height:44px;flex-shrink:0}@media(max-width:768px){.mobile-header.svelte-wp0i5g{display:flex}.mobile-header.hidden.svelte-wp0i5g{display:none}}.mobile-title.svelte-wp0i5g{font-size:.95rem;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.icon-btn.svelte-wp0i5g{background:none;border:none;color:var(--text);font-size:1.2rem;cursor:pointer;padding:6px;min-width:36px;min-height:36px;display:flex;align-items:center;justify-content:center;border-radius:6px;touch-action:manipulation}.icon-btn.svelte-wp0i5g:hover{background:var(--border)}.mobile-input-form.svelte-us0qpd{position:fixed;top:0;left:0;width:1px;height:1px;padding:0;margin:0;overflow:hidden;clip-path:inset(50%);opacity:0;pointer-events:none}.mobile-input.svelte-us0qpd{width:100%;height:100%;background:transparent;border:none;outline:none;color:transparent;caret-color:transparent;font-size:16px}.mobile-input.svelte-us0qpd::-webkit-search-cancel-button,.mobile-input.svelte-us0qpd::-webkit-search-decoration{-webkit-appearance:none;-moz-appearance:none;appearance:none}.debug-toggle.svelte-us0qpd{position:fixed;bottom:60px;right:8px;z-index:10000;background:#333;color:#0f0;border:1px solid #0f0;border-radius:6px;font:12px monospace;padding:6px 10px;min-width:44px;min-height:44px;touch-action:manipulation}.debug-panel.svelte-us0qpd{position:fixed;top:0;left:0;right:0;bottom:0;overflow-y:scroll;-webkit-overflow-scrolling:touch;background:#000000eb;color:#0f0;font:11px/1.4 monospace;padding:6px 6px 6px 40px;z-index:9999;white-space:pre-wrap;word-break:break-all;overscroll-behavior:contain}.update-toast.svelte-1trivgv{position:fixed;bottom:0;left:0;right:0;z-index:150;display:flex;justify-content:center;padding:12px 12px calc(12px + env(safe-area-inset-bottom));pointer-events:none;animation:svelte-1trivgv-toast-slide-up .25s ease-out}@keyframes svelte-1trivgv-toast-slide-up{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}.update-toast-content.svelte-1trivgv{display:flex;flex-direction:row;align-items:center;gap:12px;background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:12px 16px;max-width:500px;box-shadow:0 4px 16px #0000004d;pointer-events:auto}.update-toast-text.svelte-1trivgv{flex:1;font-size:.85rem;color:var(--text)}.update-toast-actions.svelte-1trivgv{display:flex;gap:8px;flex-shrink:0}.update-toast-btn.svelte-1trivgv{padding:8px 14px;border-radius:6px;font-size:.8rem;border:none;background:var(--accent);color:#fff;cursor:pointer;white-space:nowrap}.update-toast-btn.svelte-1trivgv:disabled{opacity:.6;cursor:not-allowed}.update-toast-dismiss.svelte-1trivgv{background:none;border:none;color:var(--text-muted);font-size:1.2rem;padding:4px 6px;cursor:pointer}.update-toast-dismiss.svelte-1trivgv:hover{color:var(--text)}.image-toast.svelte-1y8sviv{position:fixed;bottom:60px;left:50%;transform:translate(-50%);z-index:1000;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:8px 14px;color:var(--text);font-size:13px;max-width:90vw;box-shadow:0 4px 12px #0006}.image-toast-content.svelte-1y8sviv{display:flex;align-items:center;gap:10px}.image-toast-text.svelte-1y8sviv{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:60vw}.image-toast-actions.svelte-1y8sviv{display:flex;gap:6px;align-items:center;flex-shrink:0}.image-toast-insert.svelte-1y8sviv{background:var(--accent);color:#fff;border:none;border-radius:4px;padding:4px 10px;font-size:12px;cursor:pointer}.image-toast-insert.svelte-1y8sviv:active{opacity:.8}.image-toast-dismiss.svelte-1y8sviv{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:16px;padding:2px 6px}.image-toast-dismiss.svelte-1y8sviv:hover{color:var(--text)}.dialog.svelte-thtd9b{background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:10px;padding:0;width:min(480px,95vw);max-height:90vh;overflow:hidden}.dialog.svelte-thtd9b::backdrop{background:#0009}.dialog-content.svelte-thtd9b{display:flex;flex-direction:column;max-height:90vh;overflow:hidden}.dialog-title.svelte-thtd9b{font-size:1.1rem;font-weight:600;padding:16px 20px 12px;margin:0;border-bottom:1px solid var(--border);flex-shrink:0}.dialog-tabs.svelte-thtd9b{display:flex;border-bottom:1px solid var(--border);flex-shrink:0}.dialog-tab.svelte-thtd9b{flex:1;padding:10px;background:none;border:none;color:var(--text-muted);font-size:.9rem;cursor:pointer;border-bottom:2px solid transparent;transition:color .15s,border-color .15s}.dialog-tab.active.svelte-thtd9b{color:var(--accent);border-bottom-color:var(--accent)}.dialog-tab.svelte-thtd9b:hover:not(.active){color:var(--text)}.dialog-body.svelte-thtd9b{padding:16px 20px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:14px}.dialog-field.svelte-thtd9b{display:flex;flex-direction:column;gap:5px}.dialog-field--inline.svelte-thtd9b{flex-direction:row;align-items:center;gap:8px}.dialog-label.svelte-thtd9b{font-size:.85rem;color:var(--text-muted)}.dialog-label-inline.svelte-thtd9b{font-size:.9rem;cursor:pointer}.dialog-select.svelte-thtd9b,.dialog-input.svelte-thtd9b{background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.9rem;padding:7px 10px;width:100%;box-sizing:border-box}.dialog-select.svelte-thtd9b:disabled{opacity:.5;cursor:not-allowed}.dialog-checkbox.svelte-thtd9b{width:16px;height:16px;accent-color:var(--accent);cursor:pointer;flex-shrink:0}.branch-input-wrap.svelte-thtd9b{position:relative}.branch-dropdown.svelte-thtd9b{position:absolute;top:calc(100% + 2px);left:0;right:0;background:var(--surface);border:1px solid var(--border);border-radius:6px;list-style:none;margin:0;padding:4px 0;z-index:100;max-height:180px;overflow-y:auto}.branch-dropdown.svelte-thtd9b li:where(.svelte-thtd9b){padding:7px 12px;font-size:.9rem;cursor:pointer}.branch-dropdown.svelte-thtd9b li:where(.svelte-thtd9b):hover{background:var(--border)}.branch-create-new.svelte-thtd9b{color:var(--accent);border-bottom:1px solid var(--border)}.dialog-footer.svelte-thtd9b{display:flex;justify-content:flex-end;gap:10px;padding:12px 20px 16px;border-top:1px solid var(--border);flex-shrink:0}.btn.svelte-thtd9b{padding:8px 18px;border-radius:6px;font-size:.9rem;cursor:pointer;border:1px solid transparent;font-weight:500;transition:opacity .15s}.btn.svelte-thtd9b:disabled{opacity:.4;cursor:not-allowed}.btn-primary.svelte-thtd9b{background:var(--accent);color:#fff}.btn-primary.svelte-thtd9b:hover:not(:disabled){opacity:.9}.btn-ghost.svelte-thtd9b{background:transparent;color:var(--text-muted);border-color:var(--border)}.btn-ghost.svelte-thtd9b:hover{background:var(--border);color:var(--text)}.dialog.svelte-fdtoa4{background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:10px;padding:0;width:min(460px,95vw);max-height:90vh;overflow:hidden}.dialog.svelte-fdtoa4::backdrop{background:#0009}.dialog-content.svelte-fdtoa4{display:flex;flex-direction:column;max-height:90vh;overflow:hidden}.dialog-header.svelte-fdtoa4{display:flex;align-items:center;justify-content:space-between;padding:16px 20px 12px;border-bottom:1px solid var(--border);flex-shrink:0}.dialog-title.svelte-fdtoa4{font-size:1.1rem;font-weight:600;margin:0}.close-btn.svelte-fdtoa4{background:none;border:none;color:var(--text-muted);font-size:1rem;cursor:pointer;padding:4px 6px;border-radius:4px}.close-btn.svelte-fdtoa4:hover{background:var(--border);color:var(--text)}.dialog-body.svelte-fdtoa4{padding:16px 20px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:20px}.settings-section.svelte-fdtoa4{display:flex;flex-direction:column;gap:10px}.section-title.svelte-fdtoa4{font-size:.9rem;font-weight:600;color:var(--text);margin:0}.section-desc.svelte-fdtoa4{font-size:.82rem;color:var(--text-muted);margin:0}.empty-msg.svelte-fdtoa4{font-size:.85rem;color:var(--text-muted);font-style:italic;margin:0}.roots-list.svelte-fdtoa4{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.root-item.svelte-fdtoa4{display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:7px 10px}.root-path.svelte-fdtoa4{font-size:.85rem;font-family:monospace;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.remove-btn.svelte-fdtoa4{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:.9rem;padding:2px 5px;border-radius:4px;flex-shrink:0;line-height:1}.remove-btn.svelte-fdtoa4:hover{background:var(--border);color:var(--text)}.add-root-row.svelte-fdtoa4{display:flex;gap:8px}.add-root-input.svelte-fdtoa4{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.9rem;padding:7px 10px}.error-msg.svelte-fdtoa4{font-size:.82rem;color:#e74c3c;margin:0}.agent-select.svelte-fdtoa4{background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.9rem;padding:7px 10px;width:100%;box-sizing:border-box}.devtools-row.svelte-fdtoa4{display:flex;align-items:center;gap:8px}.dialog-checkbox.svelte-fdtoa4{width:16px;height:16px;accent-color:var(--accent);cursor:pointer;flex-shrink:0}.devtools-label.svelte-fdtoa4{font-size:.9rem;cursor:pointer}.dialog-footer.svelte-fdtoa4{display:flex;justify-content:flex-end;gap:10px;padding:12px 20px 16px;border-top:1px solid var(--border);flex-shrink:0}.btn.svelte-fdtoa4{padding:8px 18px;border-radius:6px;font-size:.9rem;cursor:pointer;border:1px solid transparent;font-weight:500}.btn-primary.svelte-fdtoa4{background:var(--accent);color:#fff;flex-shrink:0}.btn-primary.svelte-fdtoa4:hover{opacity:.9}.btn-ghost.svelte-fdtoa4{background:transparent;color:var(--text-muted);border-color:var(--border)}.btn-ghost.svelte-fdtoa4:hover{background:var(--border);color:var(--text)}.dialog.svelte-15sco2n{background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:10px;padding:0;width:min(400px,95vw);overflow:hidden}.dialog.svelte-15sco2n::backdrop{background:#0009}.dialog-content.svelte-15sco2n{display:flex;flex-direction:column}.dialog-title.svelte-15sco2n{font-size:1.1rem;font-weight:600;padding:16px 20px 12px;margin:0;border-bottom:1px solid var(--border)}.dialog-body.svelte-15sco2n{padding:16px 20px;display:flex;flex-direction:column;gap:8px}.confirm-msg.svelte-15sco2n{font-size:.95rem;margin:0;line-height:1.5}.wt-name.svelte-15sco2n{color:var(--text)}.wt-path.svelte-15sco2n{font-size:.82rem;color:var(--text-muted);font-family:monospace;margin:0;word-break:break-all}.warning-msg.svelte-15sco2n{font-size:.82rem;color:#e74c3c;margin:0}.error-msg.svelte-15sco2n{font-size:.85rem;color:#e74c3c;margin:0;padding:8px 10px;background:#e74c3c1a;border-radius:6px;border:1px solid rgba(231,76,60,.3)}.dialog-footer.svelte-15sco2n{display:flex;justify-content:flex-end;gap:10px;padding:12px 20px 16px;border-top:1px solid var(--border)}.btn.svelte-15sco2n{padding:8px 18px;border-radius:6px;font-size:.9rem;cursor:pointer;border:1px solid transparent;font-weight:500}.btn.svelte-15sco2n:disabled{opacity:.4;cursor:not-allowed}.btn-danger.svelte-15sco2n{background:#e74c3c;color:#fff}.btn-danger.svelte-15sco2n:hover:not(:disabled){opacity:.9}.btn-ghost.svelte-15sco2n{background:transparent;color:var(--text-muted);border-color:var(--border)}.btn-ghost.svelte-15sco2n:hover:not(:disabled){background:var(--border);color:var(--text)}.main-app.svelte-13zv0lp{display:flex;flex-direction:row;width:100%;height:100vh;height:100dvh;overflow:hidden}.sidebar-overlay.svelte-13zv0lp{display:none}.terminal-area.svelte-13zv0lp{flex:1;display:flex;flex-direction:column;min-width:0;overflow:hidden;position:relative}.no-session-msg.svelte-13zv0lp{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--text-muted);font-size:.95rem;text-align:center;pointer-events:none}@media(max-width:600px){.main-app.svelte-13zv0lp{position:fixed;top:0;right:0;bottom:0;left:0;width:100%}.sidebar-overlay.svelte-13zv0lp{display:block;position:fixed;top:0;right:0;bottom:0;left:0;background:#00000080;z-index:99}.terminal-area.svelte-13zv0lp{width:100%}}:root{--bg: #1a1a1a;--surface: #2b2b2b;--accent: #d97757;--text: #ececec;--text-muted: #9b9b9b;--border: #3d3d3d;--sidebar-width: 240px;--toolbar-height: auto}[hidden]{display:none!important}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}dialog{margin:auto}html,body{height:100%;overflow:hidden;overscroll-behavior:none;background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:15px}::-webkit-scrollbar{width:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}
32
+ */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{font-family:monospace;-webkit-user-select:text;user-select:text;white-space:pre}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;background:#0000;transition:opacity .1s linear;z-index:11}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.terminal-wrapper.svelte-5qgfij{display:flex;flex:1;min-height:0;position:relative;overflow:hidden}.terminal-container.svelte-5qgfij{flex:1;min-width:0;min-height:0;overflow:hidden;padding:4px}.terminal-wrapper.drag-over.svelte-5qgfij{outline:2px dashed var(--accent);outline-offset:-2px}.terminal-scrollbar.svelte-5qgfij{width:8px;background:transparent;position:relative;flex-shrink:0}.terminal-scrollbar-thumb.svelte-5qgfij{position:absolute;right:0;width:6px;background:var(--border);border-radius:3px;cursor:pointer}@media(hover:none){.terminal-wrapper.selection-mode.svelte-5qgfij .terminal-container:where(.svelte-5qgfij){outline:2px solid var(--accent);outline-offset:-2px}.terminal-container.svelte-5qgfij{touch-action:none}.terminal-scrollbar.svelte-5qgfij{width:12px}.terminal-scrollbar-thumb.svelte-5qgfij{width:8px;min-height:44px}.scroll-fabs.svelte-5qgfij{position:absolute;right:16px;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;gap:12px;z-index:1;opacity:.6;pointer-events:auto}.scroll-fab.svelte-5qgfij{width:44px;height:44px;border-radius:50%;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:18px;display:flex;align-items:center;justify-content:center;cursor:pointer;touch-action:manipulation;-webkit-user-select:none;user-select:none}.scroll-fab.svelte-5qgfij:active{opacity:1;background:var(--border)}.scroll-fab-bottom.svelte-5qgfij{margin-top:4px}}.toolbar.svelte-k7we1m{background:var(--surface);border-top:1px solid var(--border);padding:4px;padding-bottom:calc(4px + env(safe-area-inset-bottom,0px));flex-shrink:0}.toolbar-grid.svelte-k7we1m{display:grid;grid-template-columns:repeat(6,1fr);gap:4px}.tb-btn.svelte-k7we1m{background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.85rem;padding:8px 4px;cursor:pointer;touch-action:manipulation;min-height:40px;display:flex;align-items:center;justify-content:center;user-select:none;-webkit-user-select:none}.tb-btn.svelte-k7we1m:active{background:var(--border)}.tb-enter.svelte-k7we1m{background:var(--accent);border-color:var(--accent);color:#fff}.tb-enter.svelte-k7we1m:active{opacity:.8}.mobile-header.svelte-wp0i5g{display:none;align-items:center;gap:8px;padding:8px 12px;background:var(--surface);border-bottom:1px solid var(--border);min-height:44px;flex-shrink:0}@media(max-width:768px){.mobile-header.svelte-wp0i5g{display:flex}.mobile-header.hidden.svelte-wp0i5g{display:none}}.mobile-title.svelte-wp0i5g{font-size:.95rem;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.icon-btn.svelte-wp0i5g{background:none;border:none;color:var(--text);font-size:1.2rem;cursor:pointer;padding:6px;min-width:36px;min-height:36px;display:flex;align-items:center;justify-content:center;border-radius:6px;touch-action:manipulation}.icon-btn.svelte-wp0i5g:hover{background:var(--border)}.mobile-input-form.svelte-us0qpd{position:fixed;top:0;left:0;width:1px;height:1px;padding:0;margin:0;overflow:hidden;clip-path:inset(50%);opacity:0;pointer-events:none}.mobile-input.svelte-us0qpd{width:100%;height:100%;background:transparent;border:none;outline:none;color:transparent;caret-color:transparent;font-size:16px}.mobile-input.svelte-us0qpd::-webkit-search-cancel-button,.mobile-input.svelte-us0qpd::-webkit-search-decoration{-webkit-appearance:none;-moz-appearance:none;appearance:none}.debug-toggle.svelte-us0qpd{position:fixed;bottom:60px;right:8px;z-index:10000;background:#333;color:#0f0;border:1px solid #0f0;border-radius:6px;font:12px monospace;padding:6px 10px;min-width:44px;min-height:44px;touch-action:manipulation}.debug-panel.svelte-us0qpd{position:fixed;top:0;left:0;right:0;bottom:0;overflow-y:scroll;-webkit-overflow-scrolling:touch;background:#000000eb;color:#0f0;font:11px/1.4 monospace;padding:6px 6px 6px 40px;z-index:9999;white-space:pre-wrap;word-break:break-all;overscroll-behavior:contain}.update-toast.svelte-1trivgv{position:fixed;bottom:0;left:0;right:0;z-index:150;display:flex;justify-content:center;padding:12px 12px calc(12px + env(safe-area-inset-bottom));pointer-events:none;animation:svelte-1trivgv-toast-slide-up .25s ease-out}@keyframes svelte-1trivgv-toast-slide-up{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}.update-toast-content.svelte-1trivgv{display:flex;flex-direction:row;align-items:center;gap:12px;background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:12px 16px;max-width:500px;box-shadow:0 4px 16px #0000004d;pointer-events:auto}.update-toast-text.svelte-1trivgv{flex:1;font-size:.85rem;color:var(--text)}.update-toast-actions.svelte-1trivgv{display:flex;gap:8px;flex-shrink:0}.update-toast-btn.svelte-1trivgv{padding:8px 14px;border-radius:6px;font-size:.8rem;border:none;background:var(--accent);color:#fff;cursor:pointer;white-space:nowrap}.update-toast-btn.svelte-1trivgv:disabled{opacity:.6;cursor:not-allowed}.update-toast-dismiss.svelte-1trivgv{background:none;border:none;color:var(--text-muted);font-size:1.2rem;padding:4px 6px;cursor:pointer}.update-toast-dismiss.svelte-1trivgv:hover{color:var(--text)}.image-toast.svelte-1y8sviv{position:fixed;bottom:60px;left:50%;transform:translate(-50%);z-index:1000;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:8px 14px;color:var(--text);font-size:13px;max-width:90vw;box-shadow:0 4px 12px #0006}.image-toast-content.svelte-1y8sviv{display:flex;align-items:center;gap:10px}.image-toast-text.svelte-1y8sviv{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:60vw}.image-toast-actions.svelte-1y8sviv{display:flex;gap:6px;align-items:center;flex-shrink:0}.image-toast-insert.svelte-1y8sviv{background:var(--accent);color:#fff;border:none;border-radius:4px;padding:4px 10px;font-size:12px;cursor:pointer}.image-toast-insert.svelte-1y8sviv:active{opacity:.8}.image-toast-dismiss.svelte-1y8sviv{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:16px;padding:2px 6px}.image-toast-dismiss.svelte-1y8sviv:hover{color:var(--text)}.dialog.svelte-thtd9b{background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:10px;padding:0;width:min(480px,95vw);max-height:90vh;overflow:hidden}.dialog.svelte-thtd9b::backdrop{background:#0009}.dialog-content.svelte-thtd9b{display:flex;flex-direction:column;max-height:90vh;overflow:hidden}.dialog-title.svelte-thtd9b{font-size:1.1rem;font-weight:600;padding:16px 20px 12px;margin:0;border-bottom:1px solid var(--border);flex-shrink:0}.dialog-tabs.svelte-thtd9b{display:flex;border-bottom:1px solid var(--border);flex-shrink:0}.dialog-tab.svelte-thtd9b{flex:1;padding:10px;background:none;border:none;color:var(--text-muted);font-size:.9rem;cursor:pointer;border-bottom:2px solid transparent;transition:color .15s,border-color .15s}.dialog-tab.active.svelte-thtd9b{color:var(--accent);border-bottom-color:var(--accent)}.dialog-tab.svelte-thtd9b:hover:not(.active){color:var(--text)}.dialog-body.svelte-thtd9b{padding:16px 20px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:14px}.dialog-field.svelte-thtd9b{display:flex;flex-direction:column;gap:5px}.dialog-field--inline.svelte-thtd9b{flex-direction:row;align-items:center;gap:8px}.dialog-label.svelte-thtd9b{font-size:.85rem;color:var(--text-muted)}.dialog-label-row.svelte-thtd9b{display:flex;align-items:center;justify-content:space-between;gap:10px}.dialog-label-inline.svelte-thtd9b{font-size:.9rem;cursor:pointer}.dialog-select.svelte-thtd9b,.dialog-input.svelte-thtd9b{background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.9rem;padding:7px 10px;width:100%;box-sizing:border-box}.dialog-select.svelte-thtd9b:disabled{opacity:.5;cursor:not-allowed}.dialog-checkbox.svelte-thtd9b{width:16px;height:16px;accent-color:var(--accent);cursor:pointer;flex-shrink:0}.branch-input-wrap.svelte-thtd9b{position:relative}.branch-refresh.svelte-thtd9b{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--text-muted);cursor:pointer;transition:color .15s,border-color .15s,background .15s}.branch-refresh.svelte-thtd9b:hover:not(:disabled){color:var(--accent);border-color:var(--accent)}.branch-refresh.svelte-thtd9b:disabled{opacity:.5;cursor:not-allowed}.branch-refresh.svelte-thtd9b svg:where(.svelte-thtd9b){width:15px;height:15px}.branch-dropdown.svelte-thtd9b{position:absolute;top:calc(100% + 2px);left:0;right:0;background:var(--surface);border:1px solid var(--border);border-radius:6px;list-style:none;margin:0;padding:4px 0;z-index:100;max-height:180px;overflow-y:auto}.branch-dropdown.svelte-thtd9b li:where(.svelte-thtd9b){padding:7px 12px;font-size:.9rem;cursor:pointer}.branch-dropdown.svelte-thtd9b li:where(.svelte-thtd9b):hover{background:var(--border)}.branch-create-new.svelte-thtd9b{color:var(--accent);border-bottom:1px solid var(--border)}.dialog-footer.svelte-thtd9b{display:flex;justify-content:flex-end;gap:10px;padding:12px 20px 16px;border-top:1px solid var(--border);flex-shrink:0}.btn.svelte-thtd9b{padding:8px 18px;border-radius:6px;font-size:.9rem;cursor:pointer;border:1px solid transparent;font-weight:500;transition:opacity .15s}.btn.svelte-thtd9b:disabled{opacity:.4;cursor:not-allowed}.btn-primary.svelte-thtd9b{background:var(--accent);color:#fff}.btn-primary.svelte-thtd9b:hover:not(:disabled){opacity:.9}.btn-ghost.svelte-thtd9b{background:transparent;color:var(--text-muted);border-color:var(--border)}.btn-ghost.svelte-thtd9b:hover{background:var(--border);color:var(--text)}.spinning.svelte-thtd9b{animation:svelte-thtd9b-spin 1s linear infinite;transform-origin:center}@keyframes svelte-thtd9b-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.dialog.svelte-fdtoa4{background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:10px;padding:0;width:min(460px,95vw);max-height:90vh;overflow:hidden}.dialog.svelte-fdtoa4::backdrop{background:#0009}.dialog-content.svelte-fdtoa4{display:flex;flex-direction:column;max-height:90vh;overflow:hidden}.dialog-header.svelte-fdtoa4{display:flex;align-items:center;justify-content:space-between;padding:16px 20px 12px;border-bottom:1px solid var(--border);flex-shrink:0}.dialog-title.svelte-fdtoa4{font-size:1.1rem;font-weight:600;margin:0}.close-btn.svelte-fdtoa4{background:none;border:none;color:var(--text-muted);font-size:1rem;cursor:pointer;padding:4px 6px;border-radius:4px}.close-btn.svelte-fdtoa4:hover{background:var(--border);color:var(--text)}.dialog-body.svelte-fdtoa4{padding:16px 20px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:20px}.settings-section.svelte-fdtoa4{display:flex;flex-direction:column;gap:10px}.section-title.svelte-fdtoa4{font-size:.9rem;font-weight:600;color:var(--text);margin:0}.section-desc.svelte-fdtoa4{font-size:.82rem;color:var(--text-muted);margin:0}.empty-msg.svelte-fdtoa4{font-size:.85rem;color:var(--text-muted);font-style:italic;margin:0}.roots-list.svelte-fdtoa4{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.root-item.svelte-fdtoa4{display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:7px 10px}.root-path.svelte-fdtoa4{font-size:.85rem;font-family:monospace;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.remove-btn.svelte-fdtoa4{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:.9rem;padding:2px 5px;border-radius:4px;flex-shrink:0;line-height:1}.remove-btn.svelte-fdtoa4:hover{background:var(--border);color:var(--text)}.add-root-row.svelte-fdtoa4{display:flex;gap:8px}.add-root-input.svelte-fdtoa4{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.9rem;padding:7px 10px}.error-msg.svelte-fdtoa4{font-size:.82rem;color:#e74c3c;margin:0}.agent-select.svelte-fdtoa4{background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.9rem;padding:7px 10px;width:100%;box-sizing:border-box}.devtools-row.svelte-fdtoa4{display:flex;align-items:center;gap:8px}.dialog-checkbox.svelte-fdtoa4{width:16px;height:16px;accent-color:var(--accent);cursor:pointer;flex-shrink:0}.devtools-label.svelte-fdtoa4{font-size:.9rem;cursor:pointer}.dialog-footer.svelte-fdtoa4{display:flex;justify-content:flex-end;gap:10px;padding:12px 20px 16px;border-top:1px solid var(--border);flex-shrink:0}.btn.svelte-fdtoa4{padding:8px 18px;border-radius:6px;font-size:.9rem;cursor:pointer;border:1px solid transparent;font-weight:500}.btn-primary.svelte-fdtoa4{background:var(--accent);color:#fff;flex-shrink:0}.btn-primary.svelte-fdtoa4:hover{opacity:.9}.btn-ghost.svelte-fdtoa4{background:transparent;color:var(--text-muted);border-color:var(--border)}.btn-ghost.svelte-fdtoa4:hover{background:var(--border);color:var(--text)}.dialog.svelte-15sco2n{background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:10px;padding:0;width:min(400px,95vw);overflow:hidden}.dialog.svelte-15sco2n::backdrop{background:#0009}.dialog-content.svelte-15sco2n{display:flex;flex-direction:column}.dialog-title.svelte-15sco2n{font-size:1.1rem;font-weight:600;padding:16px 20px 12px;margin:0;border-bottom:1px solid var(--border)}.dialog-body.svelte-15sco2n{padding:16px 20px;display:flex;flex-direction:column;gap:8px}.confirm-msg.svelte-15sco2n{font-size:.95rem;margin:0;line-height:1.5}.wt-name.svelte-15sco2n{color:var(--text)}.wt-path.svelte-15sco2n{font-size:.82rem;color:var(--text-muted);font-family:monospace;margin:0;word-break:break-all}.warning-msg.svelte-15sco2n{font-size:.82rem;color:#e74c3c;margin:0}.error-msg.svelte-15sco2n{font-size:.85rem;color:#e74c3c;margin:0;padding:8px 10px;background:#e74c3c1a;border-radius:6px;border:1px solid rgba(231,76,60,.3)}.dialog-footer.svelte-15sco2n{display:flex;justify-content:flex-end;gap:10px;padding:12px 20px 16px;border-top:1px solid var(--border)}.btn.svelte-15sco2n{padding:8px 18px;border-radius:6px;font-size:.9rem;cursor:pointer;border:1px solid transparent;font-weight:500}.btn.svelte-15sco2n:disabled{opacity:.4;cursor:not-allowed}.btn-danger.svelte-15sco2n{background:#e74c3c;color:#fff}.btn-danger.svelte-15sco2n:hover:not(:disabled){opacity:.9}.btn-ghost.svelte-15sco2n{background:transparent;color:var(--text-muted);border-color:var(--border)}.btn-ghost.svelte-15sco2n:hover:not(:disabled){background:var(--border);color:var(--text)}.main-app.svelte-13zv0lp{display:flex;flex-direction:row;width:100%;height:100vh;height:100dvh;overflow:hidden}.sidebar-overlay.svelte-13zv0lp{display:none}.terminal-area.svelte-13zv0lp{flex:1;display:flex;flex-direction:column;min-width:0;overflow:hidden;position:relative}.no-session-msg.svelte-13zv0lp{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--text-muted);font-size:.95rem;text-align:center;pointer-events:none}@media(max-width:600px){.main-app.svelte-13zv0lp{position:fixed;top:0;right:0;bottom:0;left:0;width:100%}.sidebar-overlay.svelte-13zv0lp{display:block;position:fixed;top:0;right:0;bottom:0;left:0;background:#00000080;z-index:99}.terminal-area.svelte-13zv0lp{width:100%}}:root{--bg: #1a1a1a;--surface: #2b2b2b;--accent: #d97757;--text: #ececec;--text-muted: #9b9b9b;--border: #3d3d3d;--sidebar-width: 240px;--toolbar-height: auto}[hidden]{display:none!important}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}dialog{margin:auto}html,body{height:100%;overflow:hidden;overscroll-behavior:none;background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:15px}::-webkit-scrollbar{width:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}
@@ -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-CyCuDCwK.js"></script>
15
- <link rel="stylesheet" crossorigin href="/assets/index-BVQ78t7Z.css">
14
+ <script type="module" crossorigin src="/assets/index-CEJznk5F.js"></script>
15
+ <link rel="stylesheet" crossorigin href="/assets/index-nIPDa7NP.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="app"></div>
@@ -9,6 +9,9 @@ export const DEFAULTS = {
9
9
  claudeCommand: 'claude',
10
10
  claudeArgs: [],
11
11
  defaultAgent: 'claude',
12
+ defaultContinue: true,
13
+ defaultYolo: false,
14
+ launchInTmux: false,
12
15
  };
13
16
  export function loadConfig(configPath) {
14
17
  if (!fs.existsSync(configPath)) {
@@ -0,0 +1,30 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ const execFileAsync = promisify(execFile);
4
+ function normalizeBranchNames(stdout) {
5
+ const branches = stdout
6
+ .split('\n')
7
+ .map((branch) => branch.trim())
8
+ .filter((branch) => branch && !branch.includes('HEAD'))
9
+ .map((branch) => branch.replace(/^origin\//, ''));
10
+ return [...new Set(branches)].sort();
11
+ }
12
+ async function listBranches(repoPath, options = {}) {
13
+ const run = options.exec || execFileAsync;
14
+ if (options.refresh) {
15
+ try {
16
+ await run('git', ['fetch', '--all', '--prune'], { cwd: repoPath });
17
+ }
18
+ catch {
19
+ // Best effort — still return the locally-known refs below.
20
+ }
21
+ }
22
+ try {
23
+ const { stdout } = await run('git', ['branch', '-a', '--format=%(refname:short)'], { cwd: repoPath });
24
+ return normalizeBranchNames(stdout);
25
+ }
26
+ catch {
27
+ return [];
28
+ }
29
+ }
30
+ export { listBranches, normalizeBranchNames };
@@ -11,11 +11,12 @@ import cookieParser from 'cookie-parser';
11
11
  import { loadConfig, saveConfig, DEFAULTS, readMeta, writeMeta, deleteMeta, ensureMetaDir } from './config.js';
12
12
  import * as auth from './auth.js';
13
13
  import * as sessions from './sessions.js';
14
- import { AGENT_CONTINUE_ARGS } from './sessions.js';
14
+ import { AGENT_CONTINUE_ARGS, AGENT_YOLO_ARGS } from './sessions.js';
15
15
  import { setupWebSocket } from './ws.js';
16
16
  import { WorktreeWatcher, WORKTREE_DIRS, isValidWorktreePath, parseWorktreeListPorcelain, parseAllWorktrees } from './watcher.js';
17
17
  import { isInstalled as serviceIsInstalled } from './service.js';
18
18
  import { extensionForMime, setClipboardImage } from './clipboard.js';
19
+ import { listBranches } from './git.js';
19
20
  const __filename = fileURLToPath(import.meta.url);
20
21
  const __dirname = path.dirname(__filename);
21
22
  const execFileAsync = promisify(execFile);
@@ -192,6 +193,30 @@ async function main() {
192
193
  }
193
194
  next();
194
195
  };
196
+ function boolConfigEndpoints(name, defaultValue, onEnable) {
197
+ app.get(`/config/${name}`, requireAuth, (_req, res) => {
198
+ res.json({ [name]: config[name] ?? defaultValue });
199
+ });
200
+ app.patch(`/config/${name}`, requireAuth, async (req, res) => {
201
+ const value = req.body[name];
202
+ if (typeof value !== 'boolean') {
203
+ res.status(400).json({ error: `${name} must be a boolean` });
204
+ return;
205
+ }
206
+ if (value && onEnable) {
207
+ try {
208
+ await onEnable();
209
+ }
210
+ catch {
211
+ res.status(400).json({ error: `Validation failed for ${name}` });
212
+ return;
213
+ }
214
+ }
215
+ config[name] = value;
216
+ saveConfig(CONFIG_PATH, config);
217
+ res.json({ [name]: value });
218
+ });
219
+ }
195
220
  const watcher = new WorktreeWatcher();
196
221
  watcher.rebuild(config.rootDirs || []);
197
222
  const server = http.createServer(app);
@@ -246,23 +271,12 @@ async function main() {
246
271
  // GET /branches?repo=<path> — list local and remote branches for a repo
247
272
  app.get('/branches', requireAuth, async (req, res) => {
248
273
  const repoPath = typeof req.query.repo === 'string' ? req.query.repo : undefined;
274
+ const refresh = req.query.refresh === '1';
249
275
  if (!repoPath) {
250
276
  res.status(400).json({ error: 'repo query parameter is required' });
251
277
  return;
252
278
  }
253
- try {
254
- const { stdout } = await execFileAsync('git', ['branch', '-a', '--format=%(refname:short)'], { cwd: repoPath });
255
- const branches = stdout
256
- .split('\n')
257
- .map((b) => b.trim())
258
- .filter((b) => b && !b.includes('HEAD'))
259
- .map((b) => b.replace(/^origin\//, ''));
260
- const unique = [...new Set(branches)];
261
- res.json(unique.sort());
262
- }
263
- catch (_) {
264
- res.json([]);
265
- }
279
+ res.json(await listBranches(repoPath, { refresh }));
266
280
  });
267
281
  // GET /git-status?repo=<path>&branch=<name>
268
282
  app.get('/git-status', requireAuth, async (req, res) => {
@@ -516,6 +530,11 @@ async function main() {
516
530
  saveConfig(CONFIG_PATH, config);
517
531
  res.json({ defaultAgent: config.defaultAgent });
518
532
  });
533
+ boolConfigEndpoints('defaultContinue', true);
534
+ boolConfigEndpoints('defaultYolo', false);
535
+ boolConfigEndpoints('launchInTmux', false, async () => {
536
+ await execFileAsync('tmux', ['-V']);
537
+ });
519
538
  // DELETE /worktrees — remove a worktree, prune, and delete its branch
520
539
  app.delete('/worktrees', requireAuth, async (req, res) => {
521
540
  const { worktreePath, repoPath } = req.body;
@@ -590,14 +609,18 @@ async function main() {
590
609
  });
591
610
  // POST /sessions
592
611
  app.post('/sessions', requireAuth, async (req, res) => {
593
- const { repoPath, repoName, worktreePath, branchName, claudeArgs, agent } = req.body;
612
+ const { repoPath, repoName, worktreePath, branchName, claudeArgs, yolo, agent, useTmux } = req.body;
594
613
  if (!repoPath) {
595
614
  res.status(400).json({ error: 'repoPath is required' });
596
615
  return;
597
616
  }
598
617
  const resolvedAgent = agent || config.defaultAgent || 'claude';
599
618
  const name = repoName || repoPath.split('/').filter(Boolean).pop() || 'session';
600
- const baseArgs = [...(config.claudeArgs || []), ...(claudeArgs || [])];
619
+ const baseArgs = [
620
+ ...(config.claudeArgs || []),
621
+ ...(yolo ? AGENT_YOLO_ARGS[resolvedAgent] : []),
622
+ ...(claudeArgs || []),
623
+ ];
601
624
  // Compute root by matching repoPath against configured rootDirs
602
625
  const roots = config.rootDirs || [];
603
626
  const root = roots.find(function (r) { return repoPath.startsWith(r); }) || '';
@@ -672,6 +695,7 @@ async function main() {
672
695
  root,
673
696
  displayName: name,
674
697
  args: baseArgs,
698
+ useTmux: useTmux ?? config.launchInTmux,
675
699
  });
676
700
  res.status(201).json(repoSession);
677
701
  return;
@@ -695,6 +719,7 @@ async function main() {
695
719
  displayName: displayNameVal,
696
720
  args,
697
721
  configPath: CONFIG_PATH,
722
+ useTmux: useTmux ?? config.launchInTmux,
698
723
  });
699
724
  writeMeta(CONFIG_PATH, {
700
725
  worktreePath: sessionRepoPath,
@@ -737,6 +762,7 @@ async function main() {
737
762
  displayName,
738
763
  args,
739
764
  configPath: CONFIG_PATH,
765
+ useTmux: useTmux ?? config.launchInTmux,
740
766
  });
741
767
  if (!worktreePath) {
742
768
  writeMeta(CONFIG_PATH, {
@@ -750,7 +776,7 @@ async function main() {
750
776
  });
751
777
  // POST /sessions/repo — start a session in the repo root (no worktree)
752
778
  app.post('/sessions/repo', requireAuth, (req, res) => {
753
- const { repoPath, repoName, continue: continueSession, claudeArgs, agent } = req.body;
779
+ const { repoPath, repoName, continue: continueSession, claudeArgs, yolo, agent, useTmux } = req.body;
754
780
  if (!repoPath) {
755
781
  res.status(400).json({ error: 'repoPath is required' });
756
782
  return;
@@ -763,7 +789,11 @@ async function main() {
763
789
  return;
764
790
  }
765
791
  const name = repoName || repoPath.split('/').filter(Boolean).pop() || 'session';
766
- const baseArgs = [...(config.claudeArgs || []), ...(claudeArgs || [])];
792
+ const baseArgs = [
793
+ ...(config.claudeArgs || []),
794
+ ...(yolo ? AGENT_YOLO_ARGS[resolvedAgent] : []),
795
+ ...(claudeArgs || []),
796
+ ];
767
797
  const args = continueSession ? [...AGENT_CONTINUE_ARGS[resolvedAgent], ...baseArgs] : [...baseArgs];
768
798
  const roots = config.rootDirs || [];
769
799
  const root = roots.find(function (r) { return repoPath.startsWith(r); }) || '';
@@ -776,6 +806,7 @@ async function main() {
776
806
  root,
777
807
  displayName: name,
778
808
  args,
809
+ useTmux: useTmux ?? config.launchInTmux,
779
810
  });
780
811
  res.status(201).json(session);
781
812
  });
@@ -891,6 +922,34 @@ async function main() {
891
922
  res.status(500).json({ ok: false, error: message });
892
923
  }
893
924
  });
925
+ // Clean up orphaned tmux sessions from previous runs
926
+ try {
927
+ const { stdout } = await execFileAsync('tmux', ['list-sessions', '-F', '#{session_name}']);
928
+ const crcSessions = stdout.trim().split('\n').filter(name => name.startsWith('crc-'));
929
+ for (const name of crcSessions) {
930
+ execFileAsync('tmux', ['kill-session', '-t', name]).catch(() => { });
931
+ }
932
+ if (crcSessions.length > 0) {
933
+ console.log(`Cleaned up ${crcSessions.length} orphaned tmux session(s).`);
934
+ }
935
+ }
936
+ catch {
937
+ // tmux not installed or no sessions — ignore
938
+ }
939
+ function gracefulShutdown() {
940
+ server.close();
941
+ // Kill all active sessions (PTY + tmux)
942
+ for (const s of sessions.list()) {
943
+ try {
944
+ sessions.kill(s.id);
945
+ }
946
+ catch { /* already exiting */ }
947
+ }
948
+ // Brief delay to let async tmux kill-session calls fire
949
+ setTimeout(() => process.exit(0), 200);
950
+ }
951
+ process.on('SIGTERM', gracefulShutdown);
952
+ process.on('SIGINT', gracefulShutdown);
894
953
  server.listen(config.port, config.host, () => {
895
954
  console.log(`claude-remote-cli listening on ${config.host}:${config.port}`);
896
955
  });
@@ -3,6 +3,7 @@ import crypto from 'node:crypto';
3
3
  import fs from 'node:fs';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
+ import { execFile } from 'node:child_process';
6
7
  import { readMeta, writeMeta } from './config.js';
7
8
  const AGENT_COMMANDS = {
8
9
  claude: 'claude',
@@ -12,6 +13,20 @@ const AGENT_CONTINUE_ARGS = {
12
13
  claude: ['--continue'],
13
14
  codex: ['resume', '--last'],
14
15
  };
16
+ const AGENT_YOLO_ARGS = {
17
+ claude: ['--dangerously-skip-permissions'],
18
+ codex: ['--full-auto'],
19
+ };
20
+ function generateTmuxSessionName(displayName, id) {
21
+ const sanitized = displayName.replace(/[^a-zA-Z0-9-]/g, '-').replace(/-+/g, '-').slice(0, 30);
22
+ return `crc-${sanitized}-${id.slice(0, 8)}`;
23
+ }
24
+ function resolveTmuxSpawn(command, args, tmuxSessionName) {
25
+ return {
26
+ command: 'tmux',
27
+ args: ['new-session', '-s', tmuxSessionName, '--', command, ...args],
28
+ };
29
+ }
15
30
  // In-memory registry: id -> Session
16
31
  const sessions = new Map();
17
32
  const IDLE_TIMEOUT_MS = 5000;
@@ -20,14 +35,23 @@ let idleChangeCallback = null;
20
35
  function onIdleChange(cb) {
21
36
  idleChangeCallback = cb;
22
37
  }
23
- function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktreeName, branchName, displayName, command, args = [], cols = 80, rows = 24, configPath }) {
38
+ function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktreeName, branchName, displayName, command, args = [], cols = 80, rows = 24, configPath, useTmux: paramUseTmux }) {
24
39
  const id = crypto.randomBytes(8).toString('hex');
25
40
  const createdAt = new Date().toISOString();
26
41
  const resolvedCommand = command || AGENT_COMMANDS[agent];
27
42
  // Strip CLAUDECODE env var to allow spawning claude inside a claude-managed server
28
43
  const env = Object.assign({}, process.env);
29
44
  delete env.CLAUDECODE;
30
- const ptyProcess = pty.spawn(resolvedCommand, args, {
45
+ const useTmux = !command && !!paramUseTmux;
46
+ let spawnCommand = resolvedCommand;
47
+ let spawnArgs = args;
48
+ const tmuxSessionName = useTmux ? generateTmuxSessionName(displayName || repoName || 'session', id) : '';
49
+ if (useTmux) {
50
+ const tmux = resolveTmuxSpawn(resolvedCommand, args, tmuxSessionName);
51
+ spawnCommand = tmux.command;
52
+ spawnArgs = tmux.args;
53
+ }
54
+ const ptyProcess = pty.spawn(spawnCommand, spawnArgs, {
31
55
  name: 'xterm-256color',
32
56
  cols,
33
57
  rows,
@@ -53,6 +77,8 @@ function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktre
53
77
  lastActivity: createdAt,
54
78
  scrollback,
55
79
  idle: false,
80
+ useTmux,
81
+ tmuxSessionName,
56
82
  };
57
83
  sessions.set(id, session);
58
84
  // Load existing metadata to preserve a previously-set displayName
@@ -106,7 +132,14 @@ function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktre
106
132
  const retryArgs = args.filter(a => !continueArgs.includes(a));
107
133
  scrollback.length = 0;
108
134
  scrollbackBytes = 0;
109
- const retryPty = pty.spawn(resolvedCommand, retryArgs, {
135
+ let retryCommand = resolvedCommand;
136
+ let retrySpawnArgs = retryArgs;
137
+ if (useTmux && tmuxSessionName) {
138
+ const tmux = resolveTmuxSpawn(resolvedCommand, retryArgs, tmuxSessionName);
139
+ retryCommand = tmux.command;
140
+ retrySpawnArgs = tmux.args;
141
+ }
142
+ const retryPty = pty.spawn(retryCommand, retrySpawnArgs, {
110
143
  name: 'xterm-256color',
111
144
  cols,
112
145
  rows,
@@ -130,14 +163,14 @@ function create({ type, agent = 'claude', repoName, repoPath, cwd, root, worktre
130
163
  });
131
164
  }
132
165
  attachHandlers(ptyProcess, continueArgs.some(a => args.includes(a)));
133
- return { id, type: session.type, agent: session.agent, root: session.root, repoName: session.repoName, repoPath, worktreeName: session.worktreeName, branchName: session.branchName, displayName: session.displayName, pid: ptyProcess.pid, createdAt, lastActivity: createdAt, idle: false };
166
+ return { id, type: session.type, agent: session.agent, root: session.root, repoName: session.repoName, repoPath, worktreeName: session.worktreeName, branchName: session.branchName, displayName: session.displayName, pid: ptyProcess.pid, createdAt, lastActivity: createdAt, idle: false, useTmux, tmuxSessionName };
134
167
  }
135
168
  function get(id) {
136
169
  return sessions.get(id);
137
170
  }
138
171
  function list() {
139
172
  return Array.from(sessions.values())
140
- .map(({ id, type, agent, root, repoName, repoPath, worktreeName, branchName, displayName, createdAt, lastActivity, idle }) => ({
173
+ .map(({ id, type, agent, root, repoName, repoPath, worktreeName, branchName, displayName, createdAt, lastActivity, idle, useTmux, tmuxSessionName }) => ({
141
174
  id,
142
175
  type,
143
176
  agent,
@@ -150,6 +183,8 @@ function list() {
150
183
  createdAt,
151
184
  lastActivity,
152
185
  idle,
186
+ useTmux,
187
+ tmuxSessionName,
153
188
  }))
154
189
  .sort((a, b) => b.lastActivity.localeCompare(a.lastActivity));
155
190
  }
@@ -166,8 +201,18 @@ function kill(id) {
166
201
  throw new Error(`Session not found: ${id}`);
167
202
  }
168
203
  session.pty.kill('SIGTERM');
204
+ if (session.tmuxSessionName) {
205
+ execFile('tmux', ['kill-session', '-t', session.tmuxSessionName], () => { });
206
+ }
169
207
  sessions.delete(id);
170
208
  }
209
+ function killAllTmuxSessions() {
210
+ for (const session of sessions.values()) {
211
+ if (session.tmuxSessionName) {
212
+ execFile('tmux', ['kill-session', '-t', session.tmuxSessionName], () => { });
213
+ }
214
+ }
215
+ }
171
216
  function resize(id, cols, rows) {
172
217
  const session = sessions.get(id);
173
218
  if (!session) {
@@ -188,4 +233,4 @@ function findRepoSession(repoPath) {
188
233
  function nextTerminalName() {
189
234
  return `Terminal ${++terminalCounter}`;
190
235
  }
191
- export { create, get, list, kill, resize, updateDisplayName, write, onIdleChange, findRepoSession, nextTerminalName, AGENT_COMMANDS, AGENT_CONTINUE_ARGS };
236
+ export { create, get, list, kill, killAllTmuxSessions, resize, updateDisplayName, write, onIdleChange, findRepoSession, nextTerminalName, AGENT_COMMANDS, AGENT_CONTINUE_ARGS, AGENT_YOLO_ARGS, resolveTmuxSpawn, generateTmuxSessionName };
@@ -61,6 +61,17 @@ test('DEFAULTS has expected keys and values', () => {
61
61
  assert.equal(DEFAULTS.claudeCommand, 'claude');
62
62
  assert.deepEqual(DEFAULTS.claudeArgs, []);
63
63
  assert.equal(DEFAULTS.defaultAgent, 'claude');
64
+ assert.equal(DEFAULTS.defaultContinue, true);
65
+ assert.equal(DEFAULTS.defaultYolo, false);
66
+ assert.equal(DEFAULTS.launchInTmux, false);
67
+ });
68
+ test('loadConfig returns correct defaults for defaultContinue, defaultYolo, and launchInTmux', () => {
69
+ const configPath = path.join(tmpDir, 'config.json');
70
+ fs.writeFileSync(configPath, JSON.stringify({ port: 3456 }), 'utf8');
71
+ const config = loadConfig(configPath);
72
+ assert.equal(config.defaultContinue, true);
73
+ assert.equal(config.defaultYolo, false);
74
+ assert.equal(config.launchInTmux, false);
64
75
  });
65
76
  test('ensureMetaDir creates worktree-meta directory', () => {
66
77
  const configPath = path.join(tmpDir, 'config.json');
@@ -0,0 +1,67 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { listBranches, normalizeBranchNames } from '../server/git.js';
4
+ describe('normalizeBranchNames', () => {
5
+ it('deduplicates refs, strips origin prefixes, and skips HEAD entries', () => {
6
+ const stdout = [
7
+ 'main',
8
+ 'origin/main',
9
+ 'origin/feat/remote-only',
10
+ 'feat/local',
11
+ 'remotes/origin/HEAD -> origin/main',
12
+ 'origin/feat/remote-only',
13
+ '',
14
+ ].join('\n');
15
+ assert.deepEqual(normalizeBranchNames(stdout), [
16
+ 'feat/local',
17
+ 'feat/remote-only',
18
+ 'main',
19
+ ]);
20
+ });
21
+ });
22
+ describe('listBranches', () => {
23
+ it('refreshes remotes before listing when requested', async () => {
24
+ const calls = [];
25
+ const branches = await listBranches('/tmp/repo', {
26
+ refresh: true,
27
+ exec: async (file, args, options) => {
28
+ calls.push({ file, args, cwd: options.cwd });
29
+ if (args[0] === 'fetch') {
30
+ return { stdout: '', stderr: '' };
31
+ }
32
+ return {
33
+ stdout: ['main', 'origin/main', 'origin/feature/remote'].join('\n'),
34
+ stderr: '',
35
+ };
36
+ },
37
+ });
38
+ assert.deepEqual(calls, [
39
+ { file: 'git', args: ['fetch', '--all', '--prune'], cwd: '/tmp/repo' },
40
+ { file: 'git', args: ['branch', '-a', '--format=%(refname:short)'], cwd: '/tmp/repo' },
41
+ ]);
42
+ assert.deepEqual(branches, ['feature/remote', 'main']);
43
+ });
44
+ it('falls back to locally-known refs if fetch fails', async () => {
45
+ const branches = await listBranches('/tmp/repo', {
46
+ refresh: true,
47
+ exec: async (_file, args) => {
48
+ if (args[0] === 'fetch') {
49
+ throw new Error('network down');
50
+ }
51
+ return {
52
+ stdout: ['main', 'origin/feature/stale'].join('\n'),
53
+ stderr: '',
54
+ };
55
+ },
56
+ });
57
+ assert.deepEqual(branches, ['feature/stale', 'main']);
58
+ });
59
+ it('returns an empty list when refs cannot be listed', async () => {
60
+ const branches = await listBranches('/tmp/repo', {
61
+ exec: async () => {
62
+ throw new Error('git failed');
63
+ },
64
+ });
65
+ assert.deepEqual(branches, []);
66
+ });
67
+ });
@@ -1,6 +1,7 @@
1
1
  import { describe, it, afterEach } from 'node:test';
2
2
  import assert from 'node:assert';
3
3
  import * as sessions from '../server/sessions.js';
4
+ import { resolveTmuxSpawn, generateTmuxSessionName } from '../server/sessions.js';
4
5
  // Track created session IDs so we can clean up after each test
5
6
  const createdIds = [];
6
7
  afterEach(() => {
@@ -264,6 +265,38 @@ describe('sessions', () => {
264
265
  createdIds.push(result.id);
265
266
  assert.strictEqual(result.branchName, '');
266
267
  });
268
+ it('resolveTmuxSpawn returns correct tmux command and args', () => {
269
+ const result = resolveTmuxSpawn('claude', ['--continue'], 'test-session');
270
+ assert.deepStrictEqual(result, {
271
+ command: 'tmux',
272
+ args: ['new-session', '-s', 'test-session', '--', 'claude', '--continue'],
273
+ });
274
+ });
275
+ it('generateTmuxSessionName has crc- prefix', () => {
276
+ const name = generateTmuxSessionName('my-session', 'abcdef1234567890');
277
+ assert.ok(name.startsWith('crc-'), `expected crc- prefix, got: ${name}`);
278
+ });
279
+ it('generateTmuxSessionName sanitizes special characters', () => {
280
+ const name = generateTmuxSessionName('feat/auth-flow', 'abcdef1234567890');
281
+ assert.ok(name.startsWith('crc-feat-auth-flow-'), `expected sanitized name, got: ${name}`);
282
+ });
283
+ it('generateTmuxSessionName limits display name to 30 chars', () => {
284
+ const longName = 'a-very-long-display-name-that-exceeds-thirty-characters';
285
+ const id = 'abcdef1234567890';
286
+ const name = generateTmuxSessionName(longName, id);
287
+ // Format is crc-<sanitized up to 30>-<8 char id>
288
+ // The sanitized portion should be at most 30 chars
289
+ const withoutPrefix = name.slice('crc-'.length);
290
+ const parts = withoutPrefix.split('-');
291
+ const idPart = parts[parts.length - 1];
292
+ const displayPart = withoutPrefix.slice(0, withoutPrefix.length - idPart.length - 1);
293
+ assert.ok(displayPart.length <= 30, `display portion should be <= 30 chars, got: ${displayPart.length}`);
294
+ });
295
+ it('generateTmuxSessionName uses 8 chars from the provided id', () => {
296
+ const id = 'abcdef1234567890';
297
+ const name = generateTmuxSessionName('my-session', id);
298
+ assert.ok(name.endsWith(id.slice(0, 8)), `expected name to end with ${id.slice(0, 8)}, got: ${name}`);
299
+ });
267
300
  it('agent defaults to claude when not specified', () => {
268
301
  const result = sessions.create({
269
302
  repoName: 'test-repo',
@@ -299,4 +332,42 @@ describe('sessions', () => {
299
332
  assert.ok(session);
300
333
  assert.strictEqual(session.agent, 'codex');
301
334
  });
335
+ it('useTmux defaults to false when not specified', () => {
336
+ const result = sessions.create({
337
+ repoName: 'test-repo',
338
+ repoPath: '/tmp',
339
+ command: '/bin/echo',
340
+ args: ['hello'],
341
+ });
342
+ createdIds.push(result.id);
343
+ assert.strictEqual(result.useTmux, false);
344
+ assert.strictEqual(result.tmuxSessionName, '');
345
+ });
346
+ it('useTmux is disabled when custom command is provided even if useTmux is true', () => {
347
+ const result = sessions.create({
348
+ repoName: 'test-repo',
349
+ repoPath: '/tmp',
350
+ command: '/bin/echo',
351
+ args: ['hello'],
352
+ useTmux: true,
353
+ });
354
+ createdIds.push(result.id);
355
+ // Custom command sessions should never use tmux
356
+ assert.strictEqual(result.useTmux, false);
357
+ assert.strictEqual(result.tmuxSessionName, '');
358
+ });
359
+ it('list includes useTmux and tmuxSessionName fields', () => {
360
+ const result = sessions.create({
361
+ repoName: 'test-repo',
362
+ repoPath: '/tmp',
363
+ command: '/bin/echo',
364
+ args: ['hello'],
365
+ });
366
+ createdIds.push(result.id);
367
+ const list = sessions.list();
368
+ const session = list.find(s => s.id === result.id);
369
+ assert.ok(session);
370
+ assert.strictEqual(session.useTmux, false);
371
+ assert.strictEqual(session.tmuxSessionName, '');
372
+ });
302
373
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-remote-cli",
3
- "version": "2.10.2",
3
+ "version": "2.12.0",
4
4
  "description": "Remote web interface for Claude Code CLI sessions",
5
5
  "type": "module",
6
6
  "main": "dist/server/index.js",