Improve code quality, adaptive redraw, and CI coverage#1
Conversation
There was a problem hiding this comment.
Pull request overview
This PR improves reliability and UX around the Ghostty-backed Emacs terminal by deduplicating OSC parsing logic, handling native terminal option set failures more explicitly, introducing adaptive redraw timing, and expanding CI to build the native module.
Changes:
- Added a
terminalSethelper and updated Terminal setter APIs to return errors instead of discardingghostty_terminal_setresults. - Refactored OSC 7/52/133 parsing to share terminator scanning via
findOscTerminator+OscScanner. - Added native module auto-download/load support and adaptive redraw timing in Elisp, plus a new CI job to build the native module with Zig.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/terminal.zig | Adds terminalSet helper and converts several setters to !void so failures can be handled by callers. |
| src/module.zig | Updates callback/color setup to handle new error-returning APIs; refactors OSC parsing via shared scanner helpers. |
| ghostel.el | Adds module auto-download helpers, adaptive redraw delay logic, and updates module loading behavior. |
| build.zig | Makes Emacs include path configurable and fixes installed artifact suffix based on platform. |
| .github/workflows/ci.yml | Adds a matrix job to build and upload the native module on Linux/macOS using Zig 0.14.1. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| (url (format "%s/download/%s/%s" | ||
| ghostel-github-release-url | ||
| (if version (concat "v" version) "latest") | ||
| asset-name)) |
There was a problem hiding this comment.
The constructed download URL uses /releases/download/latest/... when ghostel--package-version returns nil. GitHub's 'latest release' download URLs use /releases/latest/download/<asset> (or a concrete tag), so this path will 404 and prevent auto-download for non-package installs (and any case where the version can't be resolved).
| (url (format "%s/download/%s/%s" | |
| ghostel-github-release-url | |
| (if version (concat "v" version) "latest") | |
| asset-name)) | |
| (url (if version | |
| (format "%s/download/v%s/%s" | |
| ghostel-github-release-url | |
| version | |
| asset-name) | |
| (format "%s/latest/download/%s" | |
| ghostel-github-release-url | |
| asset-name))) |
There was a problem hiding this comment.
Fixed in 3f7d349 — now uses /releases/latest/download/<asset> for the fallback URL.
Generated by Claude Code
| (defun ghostel--maybe-download-module (dir) | ||
| "Try to download a pre-built module into DIR if available. | ||
| Downloads from the latest GitHub release matching the current platform. | ||
| Does nothing if the platform is unsupported or the download fails." | ||
| (condition-case err | ||
| (let ((asset-name (ghostel--module-asset-name))) | ||
| (when asset-name | ||
| (let* ((version (ghostel--package-version)) | ||
| (url (format "%s/download/%s/%s" | ||
| ghostel-github-release-url | ||
| (if version (concat "v" version) "latest") | ||
| asset-name)) | ||
| (dest (expand-file-name | ||
| (concat "ghostel-module" module-file-suffix) dir))) | ||
| (message "ghostel: downloading native module from %s..." url) | ||
| (when (ghostel--download-file url dest) | ||
| (message "ghostel: native module downloaded to %s" dest))))) | ||
| (error |
There was a problem hiding this comment.
This change introduces automatic download + module-load of a prebuilt native module from the network without any integrity/authenticity verification (checksum/signature/pinned commit). That is a significant supply-chain risk; consider requiring explicit user confirmation before downloading/loading, and/or verifying an expected hash/signature published with the release before calling module-load.
There was a problem hiding this comment.
Addressed: auto-download is now opt-in via ghostel-auto-download-module (default nil). Users must explicitly enable it or use M-x ghostel-download-module. Also added HTTPS enforcement — non-HTTPS URLs are rejected before downloading.
Generated by Claude Code
| (defun ghostel-module-compile () | ||
| "Build the native module from source using build.sh." | ||
| (interactive) | ||
| (let ((default-directory (file-name-directory | ||
| (or load-file-name | ||
| (locate-library "ghostel") | ||
| buffer-file-name)))) | ||
| (compile "./build.sh"))) | ||
|
|
There was a problem hiding this comment.
ghostel-module-compile is now defined twice in this file (once here, and again near the end under "Module compilation"). The later definition will overwrite this one, which is confusing and can break expectations (different default-directory, different compile invocation). Please keep a single definition and remove/merge the duplicate.
| (defun ghostel-module-compile () | |
| "Build the native module from source using build.sh." | |
| (interactive) | |
| (let ((default-directory (file-name-directory | |
| (or load-file-name | |
| (locate-library "ghostel") | |
| buffer-file-name)))) | |
| (compile "./build.sh"))) |
There was a problem hiding this comment.
Fixed in 3f7d349 — removed the duplicate, keeping the pre-existing definition.
Generated by Claude Code
| (with-current-buffer (url-retrieve-synchronously url t t 30) | ||
| (set-buffer-multibyte nil) | ||
| (goto-char (point-min)) | ||
| (when (re-search-forward "^HTTP/[0-9.]+ 200" nil t) | ||
| (when (re-search-forward "\r?\n\r?\n" nil t) | ||
| (let ((coding-system-for-write 'binary) | ||
| (start (point))) | ||
| (when (< start (point-max)) | ||
| (write-region start (point-max) dest nil 'silent) | ||
| ;; Make executable on Unix | ||
| (set-file-modes dest #o755) | ||
| t)))))) |
There was a problem hiding this comment.
ghostel--download-file doesn't kill the buffer returned by url-retrieve-synchronously, which can leak temporary buffers over time (and during retries). Consider binding the result to a variable and using unwind-protect to ensure the buffer is killed after extracting/writing the payload.
| (with-current-buffer (url-retrieve-synchronously url t t 30) | |
| (set-buffer-multibyte nil) | |
| (goto-char (point-min)) | |
| (when (re-search-forward "^HTTP/[0-9.]+ 200" nil t) | |
| (when (re-search-forward "\r?\n\r?\n" nil t) | |
| (let ((coding-system-for-write 'binary) | |
| (start (point))) | |
| (when (< start (point-max)) | |
| (write-region start (point-max) dest nil 'silent) | |
| ;; Make executable on Unix | |
| (set-file-modes dest #o755) | |
| t)))))) | |
| (let ((buf (url-retrieve-synchronously url t t 30))) | |
| (when buf | |
| (unwind-protect | |
| (with-current-buffer buf | |
| (set-buffer-multibyte nil) | |
| (goto-char (point-min)) | |
| (when (re-search-forward "^HTTP/[0-9.]+ 200" nil t) | |
| (when (re-search-forward "\r?\n\r?\n" nil t) | |
| (let ((coding-system-for-write 'binary) | |
| (start (point))) | |
| (when (< start (point-max)) | |
| (write-region start (point-max) dest nil 'silent) | |
| ;; Make executable on Unix | |
| (set-file-modes dest #o755) | |
| t))))) | |
| (when (buffer-live-p buf) | |
| (kill-buffer buf)))))) |
There was a problem hiding this comment.
| (current-buffer))))) | ||
| (let ((delay (if (and ghostel-adaptive-fps ghostel--last-output-time) | ||
| (let ((idle-secs (float-time | ||
| (time-subtract nil |
There was a problem hiding this comment.
time-subtract expects two time values; passing nil here will raise an error on the second and subsequent redraw scheduling when ghostel-adaptive-fps is enabled (breaking redraw). Use (current-time) (or time-since) instead of nil when computing idle-secs.
| (time-subtract nil | |
| (time-subtract (current-time) |
There was a problem hiding this comment.
| (unless (file-exists-p mod) | ||
| (ghostel--maybe-download-module dir)) | ||
| (if (file-exists-p mod) | ||
| (module-load mod) | ||
| (display-warning 'ghostel | ||
| (concat "Native module not found: " mod | ||
| "\nRun ./build.sh or M-x ghostel-download-module"))))) |
There was a problem hiding this comment.
Since this code now may auto-download the native module, module-load failures (corrupt download, wrong architecture/suffix, partial write) will throw an error during (require 'ghostel) and can prevent the package from loading at all. Consider wrapping module-load in condition-case and falling back to a warning + instructions, rather than erroring out during load.
There was a problem hiding this comment.
Fixed in 3f7d349 — module-load is now wrapped in condition-case, falling back to a display-warning with instructions instead of erroring out.
Generated by Claude Code
| while (scanner.next()) |match| { | ||
| if (match.payload.len > 0) { | ||
| const gs = gt.GhosttyString{ .ptr = match.payload.ptr, .len = match.payload.len }; | ||
| _ = gt.c.ghostty_terminal_set(term.terminal, gt.OPT_PWD, &gs); |
There was a problem hiding this comment.
This still discards the return value from ghostty_terminal_set when setting OPT_PWD. Since the PR is introducing terminalSet to detect failures, this path should also check the result (and decide how to handle failure) so PWD updates don't silently fail.
| _ = gt.c.ghostty_terminal_set(term.terminal, gt.OPT_PWD, &gs); | |
| const rc = gt.c.ghostty_terminal_set(term.terminal, gt.OPT_PWD, &gs); | |
| if (rc != 0) { | |
| std.log.err("ghostty_terminal_set(OPT_PWD) failed with error code {d}", .{rc}); | |
| } |
There was a problem hiding this comment.
Fixed in 3f7d349 — added Terminal.setPwd method that checks the return code, used by extractAndSetPwd.
Generated by Claude Code
| uses: mlugg/setup-zig@v2 | ||
| with: | ||
| version: 0.14.1 | ||
|
|
There was a problem hiding this comment.
The build-native job runs ./build.sh but doesn't install Emacs development headers or set EMACS_INCLUDE_DIR. Since src/emacs.zig includes emacs-module.h, this build is likely to be environment-dependent and can fail on fresh runners. Consider installing an Emacs dev package (or using purcell/setup-emacs here and exporting the include dir) so the job is reproducible.
| - name: Install Emacs headers | |
| run: | | |
| if [ "${RUNNER_OS}" = "Linux" ]; then | |
| sudo apt-get update | |
| sudo apt-get install -y emacs-nox | |
| echo "EMACS_INCLUDE_DIR=/usr/include" >> "$GITHUB_ENV" | |
| elif [ "${RUNNER_OS}" = "macOS" ]; then | |
| brew install emacs | |
| EMACS_PREFIX="$(brew --prefix emacs)" | |
| echo "EMACS_INCLUDE_DIR=${EMACS_PREFIX}/include" >> "$GITHUB_ENV" | |
| else | |
| echo "Unsupported OS for installing Emacs headers: ${RUNNER_OS}" >&2 | |
| exit 1 | |
| fi |
There was a problem hiding this comment.
Fixed — CI now uses purcell/setup-emacs and a targeted header search instead of raw apt/brew installs.
Generated by Claude Code
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| term.setUserdata(term) catch { | ||
| env.signalError("ghostel: failed to set terminal userdata"); | ||
| return env.nil(); | ||
| }; | ||
| term.setWritePty(&writePtyCallback) catch { | ||
| env.signalError("ghostel: failed to set write_pty callback"); | ||
| return env.nil(); | ||
| }; | ||
| term.setBell(&bellCallback) catch { | ||
| env.signalError("ghostel: failed to set bell callback"); | ||
| return env.nil(); | ||
| }; | ||
| term.setTitleChanged(&titleChangedCallback) catch { |
There was a problem hiding this comment.
In these error paths, the allocated term (and its ghostty resources) is leaked: after Terminal.init succeeds, returning env.nil() from any of these catch blocks skips term.deinit() / destroy(term). Consider errdefer term.deinit(); errdefer std.heap.c_allocator.destroy(term); after init (or manually cleanup in each catch) before signaling the Emacs error.
| term.setUserdata(term) catch { | |
| env.signalError("ghostel: failed to set terminal userdata"); | |
| return env.nil(); | |
| }; | |
| term.setWritePty(&writePtyCallback) catch { | |
| env.signalError("ghostel: failed to set write_pty callback"); | |
| return env.nil(); | |
| }; | |
| term.setBell(&bellCallback) catch { | |
| env.signalError("ghostel: failed to set bell callback"); | |
| return env.nil(); | |
| }; | |
| term.setTitleChanged(&titleChangedCallback) catch { | |
| term.setUserdata(term) catch { | |
| term.deinit(); | |
| std.heap.c_allocator.destroy(term); | |
| env.signalError("ghostel: failed to set terminal userdata"); | |
| return env.nil(); | |
| }; | |
| term.setWritePty(&writePtyCallback) catch { | |
| term.deinit(); | |
| std.heap.c_allocator.destroy(term); | |
| env.signalError("ghostel: failed to set write_pty callback"); | |
| return env.nil(); | |
| }; | |
| term.setBell(&bellCallback) catch { | |
| term.deinit(); | |
| std.heap.c_allocator.destroy(term); | |
| env.signalError("ghostel: failed to set bell callback"); | |
| return env.nil(); | |
| }; | |
| term.setTitleChanged(&titleChangedCallback) catch { | |
| term.deinit(); | |
| std.heap.c_allocator.destroy(term); |
There was a problem hiding this comment.
Good catch. Fixed — the callback setup now uses a labeled block pattern. If any setter fails, term.deinit() and destroy(term) are called before signaling the error.
Generated by Claude Code
| "Return ghostel package version string, or nil." | ||
| (let ((pkg (and (fboundp 'package-desc-version) | ||
| (car (alist-get 'ghostel package-alist))))) | ||
| (when pkg | ||
| (package-version-join (package-desc-version pkg))))) |
There was a problem hiding this comment.
ghostel--package-version uses package-alist without checking it’s bound. When ghostel is loaded outside package.el (e.g., via -L . / straight clone), this can raise void-variable package-alist and make auto-download always fail noisily. Guard with (boundp 'package-alist) (and/or (require 'package)), returning nil when unavailable.
| "Return ghostel package version string, or nil." | |
| (let ((pkg (and (fboundp 'package-desc-version) | |
| (car (alist-get 'ghostel package-alist))))) | |
| (when pkg | |
| (package-version-join (package-desc-version pkg))))) | |
| "Return ghostel package version string, or nil. | |
| If `package.el' is not available or `package-alist' is unbound, | |
| return nil without signaling an error." | |
| (when (and (require 'package nil t) | |
| (boundp 'package-alist)) | |
| (let ((pkg (car (alist-get 'ghostel package-alist)))) | |
| (when pkg | |
| (package-version-join (package-desc-version pkg)))))) |
There was a problem hiding this comment.
Fixed — now guarded with (require 'package nil t) and (boundp 'package-alist), returning nil when package.el is unavailable.
Generated by Claude Code
| (defun ghostel--maybe-download-module (dir) | ||
| "Try to download a pre-built module into DIR if available. | ||
| Downloads from the latest GitHub release matching the current platform. | ||
| Does nothing if the platform is unsupported or the download fails." | ||
| (condition-case err | ||
| (let ((asset-name (ghostel--module-asset-name))) | ||
| (when asset-name | ||
| (let* ((version (ghostel--package-version)) | ||
| (url (if version | ||
| (format "%s/download/v%s/%s" | ||
| ghostel-github-release-url | ||
| version | ||
| asset-name) | ||
| (format "%s/latest/download/%s" | ||
| ghostel-github-release-url | ||
| asset-name))) | ||
| (dest (expand-file-name | ||
| (concat "ghostel-module" module-file-suffix) dir))) | ||
| (message "ghostel: downloading native module from %s..." url) | ||
| (when (ghostel--download-file url dest) | ||
| (message "ghostel: native module downloaded to %s" dest))))) |
There was a problem hiding this comment.
Auto-downloading and then module-loading a native binary lacks any integrity/authenticity checks. At minimum, consider enforcing an https:// URL (reject/ignore non-HTTPS if ghostel-github-release-url is customized) and/or verifying a checksum/signature from the release before writing and loading the file.
There was a problem hiding this comment.
Addressed with two mitigations:
- Auto-download is opt-in (
ghostel-auto-download-moduledefaults tonil) - HTTPS is now enforced — the download function rejects non-HTTPS URLs before fetching
Checksum/signature verification would be a good future addition but is out of scope for this PR since there are no releases with published checksums yet.
Generated by Claude Code
| HEADER=$(find / -name emacs-module.h -print -quit 2>/dev/null || true) | ||
| if [ -z "$HEADER" ]; then | ||
| echo "::error::emacs-module.h not found" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
This step does find / -name emacs-module.h, which can be very slow on GitHub-hosted runners and may hit permission-denied noise/timeouts. Consider limiting the search to likely prefixes (e.g. $(dirname $(which emacs))/../include, /usr/include, /usr/local/include, Homebrew prefix on macOS), or use an Emacs/brew query to derive the include dir directly.
| HEADER=$(find / -name emacs-module.h -print -quit 2>/dev/null || true) | |
| if [ -z "$HEADER" ]; then | |
| echo "::error::emacs-module.h not found" | |
| exit 1 | |
| fi | |
| set -e | |
| # Derive candidate include directories from the Emacs binary and common prefixes. | |
| EMACS_BIN="$(command -v emacs || true)" | |
| EMACS_BIN_DIR="" | |
| if [ -n "$EMACS_BIN" ]; then | |
| EMACS_BIN_DIR="$(dirname "$EMACS_BIN")" | |
| fi | |
| CANDIDATES="" | |
| if [ -n "$EMACS_BIN_DIR" ]; then | |
| CANDIDATES="$CANDIDATES \"$EMACS_BIN_DIR/../include\"" | |
| CANDIDATES="$CANDIDATES \"$EMACS_BIN_DIR/../share/emacs/include\"" | |
| fi | |
| # Common system include directories on Linux and macOS. | |
| CANDIDATES="$CANDIDATES /usr/include /usr/local/include /opt/homebrew/include /opt/local/include" | |
| HEADER="" | |
| for dir in $CANDIDATES; do | |
| # Skip non-existent directories. | |
| if [ ! -d "$dir" ]; then | |
| continue | |
| fi | |
| # Try direct path first. | |
| if [ -f "$dir/emacs-module.h" ]; then | |
| HEADER="$dir/emacs-module.h" | |
| break | |
| fi | |
| # Fall back to a depth-limited search within the candidate directory. | |
| FOUND="$(find "$dir" -maxdepth 5 -name emacs-module.h -print -quit 2>/dev/null || true)" | |
| if [ -n "$FOUND" ]; then | |
| HEADER="$FOUND" | |
| break | |
| fi | |
| done | |
| if [ -z "$HEADER" ]; then | |
| echo "::error::emacs-module.h not found" | |
| exit 1 | |
| fi |
There was a problem hiding this comment.
Fixed — replaced find / with a targeted search that checks directories relative to the Emacs binary (including Nix store symlinks), then falls back to common system prefixes with maxdepth 5.
Generated by Claude Code
| build-native: | ||
| # Full native build requires the ghostty submodule which needs | ||
| # Zig >= 0.15.2 (not yet released as a stable version). | ||
| # For now, build on macOS and Linux using Zig 0.15.1 and skip the | ||
| # ghostty submodule build — only verify our module compiles against | ||
| # a pre-built libghostty-vt if available. | ||
| # Once Zig 0.15.2+ is released, switch to: ./build.sh |
There was a problem hiding this comment.
CI pins Zig to 0.15.1, but the PR description (and ghostel.el commentary) mentions Zig 0.14+. Please align the version/documentation: either set the workflow to the intended 0.14.x baseline, or update the PR description/docs and remove/update the inline comments about 0.15.2 being "not yet released" to avoid stale guidance.
There was a problem hiding this comment.
Fixed — updated the CI comment to clarify: ghostel itself needs Zig 0.14+, the vendored ghostty submodule needs Zig >= 0.15.2, and CI uses 0.15.1 with zig build check (compile-only, no linking) as a compromise until 0.15.2+ is released.
Generated by Claude Code
fd90512 to
2f5cd94
Compare
- Extract shared OSC terminator helper (findOscTerminator + OscScanner) to deduplicate terminator detection across OSC 7, 52, and 133 parsers - Check ghostty_terminal_set return values via terminalSet helper instead of silently discarding them - Add adaptive frame rate: shorter initial delay after idle for responsive interactive feedback, configurable via ghostel-adaptive-fps - Add native module build job to CI using Zig 0.14
- Make build.zig platform-aware: detect macOS vs Linux for Emacs header paths and output file suffix (.dylib vs .so). Support EMACS_INCLUDE_DIR env var override for custom Emacs installations. - Add automatic module download: when the native module is missing, attempt to fetch a pre-built binary from the GitHub releases page matching the current platform (x86_64-linux, aarch64-macos, etc.) - Add M-x ghostel-download-module for interactive download and M-x ghostel-module-compile for building from source - Update CI to build native module on both Ubuntu and macOS, uploading build artifacts for potential use in GitHub releases
- Fix GitHub release URL format: use /releases/latest/download/ for the latest release fallback instead of /releases/download/latest/ - Add ghostel-auto-download-module defcustom (default nil) so module auto-download requires explicit opt-in, addressing supply-chain concern - Remove duplicate ghostel-module-compile definition (keep existing one) - Fix buffer leak in ghostel--download-file: use unwind-protect to always kill the url-retrieve-synchronously buffer - Fix time-subtract call: use (current-time) instead of nil which would error on Emacs versions that don't accept nil - Wrap module-load in condition-case so corrupt/incompatible downloads produce a warning instead of breaking (require 'ghostel) - Check OPT_PWD return value via new Terminal.setPwd method instead of discarding it with _ = - Add Emacs header installation step to CI build-native job so emacs-module.h is available on fresh runners
- Fix duplicate `const resolved` declaration in build.zig that caused Zig compilation error - Use purcell/setup-emacs in build-native CI job (same as lint/test jobs) to reliably provide emacs-module.h on both platforms - Dynamically find emacs-module.h location and export EMACS_INCLUDE_DIR Fix CI: rewrite SSH submodule URLs to HTTPS for CI checkout The ghostty submodule uses git@github.com: (SSH) which fails in CI without SSH keys. Add a step to rewrite SSH URLs to HTTPS before cloning submodules, so the public ghostty repo can be fetched with the default GITHUB_TOKEN. Fix CI: use Zig nightly for ghostty submodule compatibility The ghostty submodule requires minimum_zig_version = 0.15.2 which is not yet a stable release. Use Zig nightly (master) until a compatible stable version is available. Fix CI: use zig ast-check instead of full build The ghostty submodule requires Zig >= 0.15.2 which is not yet a stable release, so a full ./build.sh is not possible in CI currently. Instead, verify Zig source files parse correctly using zig ast-check. The full build job can be restored once Zig 0.15.2+ is released. Fix CI: compile native module with zig build check Add a "check" build step to build.zig that compiles all Zig source files against ghostty and Emacs headers without linking. This catches type errors, import issues, and API misuse without needing the pre-built libghostty-vt static libraries (which require Zig >= 0.15.2 to build). Also add vendor/ghostty/include as an include path so headers can be found from the source tree without running the ghostty build first.
- Fix memory leak in fnNew: clean up terminal on callback setup failure using a labeled block pattern instead of leaking - Guard package-alist access with (boundp) and (require 'package) to avoid void-variable errors when loaded outside package.el - Enforce HTTPS for module download URLs to mitigate supply-chain risk - Replace slow `find /` with targeted header search in CI using Emacs binary path, Nix store symlinks, and common system prefixes - Clarify CI Zig version comment: ghostel needs 0.14+, ghostty submodule needs 0.15.2+, CI uses 0.15.1 for compile-only check
Replace ghostel-auto-download-module with ghostel-module-auto-install which supports four modes: - 'ask (default): prompt with [d]ownload / [c]ompile / [s]kip - 'download: silently download pre-built binary from GitHub releases - 'compile: build from source via ghostel-module-compile - nil: do nothing, user installs manually The interactive prompt shows the download URL and mentions Zig as a requirement for compilation. Compile mode runs build.sh synchronously and reports results in *ghostel-build* buffer.
- Rename unused `dir` arg to `_dir` in ghostel--ask-install-action - Add declare-function for package-version-join and package-desc-version - Add declare-function for ghostel--scroll-top and ghostel--scroll-bottom - Move ghostel--copy-mode-active defvar-local before first use
The interactive read-char-choice prompt in ghostel--ensure-module causes an end-of-file error when Emacs runs in batch mode (e.g. CI tests). Skip the prompt when noninteractive is set.
5931f4f to
e7b1189
Compare
to deduplicate terminator detection across OSC 7, 52, and 133 parsers
of silently discarding them
interactive feedback, configurable via ghostel-adaptive-fps
https://claude.ai/code/session_01DvxJHyLFPaaZTNYcEFN2hQ