reload.sh: default to build-only, add --launch flag (#2097)
* reload.sh: default to build-only, add --launch flag to open app By default, reload.sh now builds and prints the app path without launching. Pass --launch to get the previous behavior (kill existing instance and open). This lets agents build without stealing focus, and the user can cmd-click the printed path to launch when ready. * CLAUDE.md: use reload.sh output for app path instead of hardcoded home dir The templates hardcoded /Users/lawrencechen/ which broke cmd-click on machines with a different home directory. Agents now read the actual path from reload.sh's "App path:" output. * CLAUDE.md: add concrete example for app path URL format Uses a fictional /Users/jane/ to make it clear the path comes from reload.sh output, not a hardcoded value. * CLAUDE.md: clearer step-by-step instructions for app path URL Explicit 3-step recipe (grab path, prepend file://, format as link) with example showing the reload.sh output and the expected result. --------- Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
This commit is contained in:
parent
65867b86ee
commit
960006e6d6
3 changed files with 115 additions and 76 deletions
35
CLAUDE.md
35
CLAUDE.md
|
|
@ -10,31 +10,47 @@ Run the setup script to initialize submodules and build GhosttyKit:
|
||||||
|
|
||||||
## Local dev
|
## Local dev
|
||||||
|
|
||||||
After making code changes, always run the reload script with a tag to launch the Debug app:
|
After making code changes, always run the reload script with a tag to build the Debug app:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./scripts/reload.sh --tag fix-zsh-autosuggestions
|
./scripts/reload.sh --tag fix-zsh-autosuggestions
|
||||||
```
|
```
|
||||||
|
|
||||||
When reporting a tagged reload result in chat, use the format for your agent type:
|
By default, `reload.sh` builds but does **not** launch the app. The script prints the `.app` path so the user can cmd-click to open it. Pass `--launch` to kill any existing instance and open the app automatically:
|
||||||
|
|
||||||
**Claude Code** (markdown link with correct derived-data path, cmd+clickable):
|
```bash
|
||||||
|
./scripts/reload.sh --tag fix-zsh-autosuggestions --launch
|
||||||
|
```
|
||||||
|
|
||||||
|
`reload.sh` prints an `App path:` line with the absolute path to the built `.app`. Use that path to build a cmd-clickable `file://` URL. Steps:
|
||||||
|
|
||||||
|
1. Grab the path from the `App path:` line in `reload.sh` output.
|
||||||
|
2. Prepend `file://` and URL-encode spaces as `%20`. Do not hardcode any part of the path.
|
||||||
|
3. Format it as a markdown link using the template for your agent type.
|
||||||
|
|
||||||
|
Example. If `reload.sh` output contains:
|
||||||
|
```
|
||||||
|
App path:
|
||||||
|
/Users/someone/Library/Developer/Xcode/DerivedData/cmux-my-tag/Build/Products/Debug/cmux DEV my-tag.app
|
||||||
|
```
|
||||||
|
|
||||||
|
**Claude Code** outputs:
|
||||||
```markdown
|
```markdown
|
||||||
=======================================================
|
=======================================================
|
||||||
[cmux DEV <tag-name>.app](file:///Users/lawrencechen/Library/Developer/Xcode/DerivedData/cmux-<tag-name>/Build/Products/Debug/cmux%20DEV%20<tag-name>.app)
|
[cmux DEV my-tag.app](file:///Users/someone/Library/Developer/Xcode/DerivedData/cmux-my-tag/Build/Products/Debug/cmux%20DEV%20my-tag.app)
|
||||||
=======================================================
|
=======================================================
|
||||||
```
|
```
|
||||||
|
|
||||||
**Codex** (plain text format):
|
**Codex** outputs:
|
||||||
```
|
```
|
||||||
=======================================================
|
=======================================================
|
||||||
[<tag-name>: file:///Users/lawrencechen/Library/Developer/Xcode/DerivedData/cmux-<tag-name>/Build/Products/Debug/cmux%20DEV%20<tag-name>.app](file:///Users/lawrencechen/Library/Developer/Xcode/DerivedData/cmux-<tag-name>/Build/Products/Debug/cmux%20DEV%20<tag-name>.app)
|
[my-tag: file:///Users/someone/Library/Developer/Xcode/DerivedData/cmux-my-tag/Build/Products/Debug/cmux%20DEV%20my-tag.app](file:///Users/someone/Library/Developer/Xcode/DerivedData/cmux-my-tag/Build/Products/Debug/cmux%20DEV%20my-tag.app)
|
||||||
=======================================================
|
=======================================================
|
||||||
```
|
```
|
||||||
|
|
||||||
Never use `/tmp/cmux-<tag>/...` app links in chat output. If the expected DerivedData path is missing, resolve the real `.app` path and report that `file://` URL.
|
Never use `/tmp/cmux-<tag>/...` app links in chat output.
|
||||||
|
|
||||||
After making code changes, always use `reload.sh --tag` to build and launch. **Never run bare `xcodebuild` or `open` an untagged `cmux DEV.app`.** Untagged builds share the default debug socket and bundle ID with other agents, causing conflicts and stealing focus.
|
After making code changes, always use `reload.sh --tag` to build. **Never run bare `xcodebuild` or `open` an untagged `cmux DEV.app`.** Untagged builds share the default debug socket and bundle ID with other agents, causing conflicts and stealing focus.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./scripts/reload.sh --tag <your-branch-slug>
|
./scripts/reload.sh --tag <your-branch-slug>
|
||||||
|
|
@ -58,10 +74,11 @@ When rebuilding cmuxd for release/bundling, always use ReleaseFast:
|
||||||
cd cmuxd && zig build -Doptimize=ReleaseFast
|
cd cmuxd && zig build -Doptimize=ReleaseFast
|
||||||
```
|
```
|
||||||
|
|
||||||
`reload` = kill and launch the Debug app only (tag required):
|
`reload` = build the Debug app (tag required). Pass `--launch` to also kill existing and open:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./scripts/reload.sh --tag <tag>
|
./scripts/reload.sh --tag <tag>
|
||||||
|
./scripts/reload.sh --tag <tag> --launch
|
||||||
```
|
```
|
||||||
|
|
||||||
`reloadp` = kill and launch the Release app:
|
`reloadp` = kill and launch the Release app:
|
||||||
|
|
|
||||||
|
|
@ -24,17 +24,18 @@
|
||||||
- Build the GhosttyKit.xcframework from source
|
- Build the GhosttyKit.xcframework from source
|
||||||
- Create the necessary symlinks
|
- Create the necessary symlinks
|
||||||
|
|
||||||
3. Build and run the debug app:
|
3. Build the debug app:
|
||||||
```bash
|
```bash
|
||||||
./scripts/reload.sh
|
./scripts/reload.sh --tag my-feature
|
||||||
```
|
```
|
||||||
|
The script prints the `.app` path. Cmd-click to open, or pass `--launch` to open automatically.
|
||||||
|
|
||||||
## Development Scripts
|
## Development Scripts
|
||||||
|
|
||||||
| Script | Description |
|
| Script | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `./scripts/setup.sh` | One-time setup (submodules + xcframework) |
|
| `./scripts/setup.sh` | One-time setup (submodules + xcframework) |
|
||||||
| `./scripts/reload.sh` | Build and launch Debug app |
|
| `./scripts/reload.sh` | Build Debug app (pass `--launch` to also open it) |
|
||||||
| `./scripts/reloadp.sh` | Build and launch Release app |
|
| `./scripts/reloadp.sh` | Build and launch Release app |
|
||||||
| `./scripts/reload2.sh` | Reload both Debug and Release |
|
| `./scripts/reload2.sh` | Reload both Debug and Release |
|
||||||
| `./scripts/rebuild.sh` | Clean rebuild |
|
| `./scripts/rebuild.sh` | Clean rebuild |
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ NAME_SET=0
|
||||||
BUNDLE_SET=0
|
BUNDLE_SET=0
|
||||||
DERIVED_SET=0
|
DERIVED_SET=0
|
||||||
TAG=""
|
TAG=""
|
||||||
|
LAUNCH=0
|
||||||
CMUX_DEBUG_LOG=""
|
CMUX_DEBUG_LOG=""
|
||||||
CLI_PATH=""
|
CLI_PATH=""
|
||||||
LAST_SOCKET_PATH_DIR="$HOME/Library/Application Support/cmux"
|
LAST_SOCKET_PATH_DIR="$HOME/Library/Application Support/cmux"
|
||||||
|
|
@ -106,6 +107,8 @@ Usage: ./scripts/reload.sh --tag <name> [options]
|
||||||
Options:
|
Options:
|
||||||
--tag <name> Required. Short tag for parallel builds (e.g., feature-xyz-lol).
|
--tag <name> Required. Short tag for parallel builds (e.g., feature-xyz-lol).
|
||||||
Sets app name, bundle id, and derived data path unless overridden.
|
Sets app name, bundle id, and derived data path unless overridden.
|
||||||
|
--launch Launch the app after building. Without this flag, the script
|
||||||
|
builds and prints the app path but does not open it.
|
||||||
--name <app name> Override app display/bundle name.
|
--name <app name> Override app display/bundle name.
|
||||||
--bundle-id <id> Override bundle identifier.
|
--bundle-id <id> Override bundle identifier.
|
||||||
--derived-data <path> Override derived data path.
|
--derived-data <path> Override derived data path.
|
||||||
|
|
@ -224,6 +227,10 @@ while [[ $# -gt 0 ]]; do
|
||||||
BUNDLE_SET=1
|
BUNDLE_SET=1
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
--launch)
|
||||||
|
LAUNCH=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--derived-data)
|
--derived-data)
|
||||||
DERIVED_DATA="${2:-}"
|
DERIVED_DATA="${2:-}"
|
||||||
if [[ -z "$DERIVED_DATA" ]]; then
|
if [[ -z "$DERIVED_DATA" ]]; then
|
||||||
|
|
@ -408,17 +415,7 @@ if [[ -x "$CLI_PATH" ]]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure any running instance is fully terminated, regardless of DerivedData path.
|
# Build cmuxd and ghostty helper binaries (needed for both launch and no-launch).
|
||||||
/usr/bin/osascript -e "tell application id \"${BUNDLE_ID}\" to quit" >/dev/null 2>&1 || true
|
|
||||||
sleep 0.3
|
|
||||||
if [[ -z "$TAG" ]]; then
|
|
||||||
# Non-tag mode: kill any running instance (across any DerivedData path) to avoid socket conflicts.
|
|
||||||
pkill -f "/${BASE_APP_NAME}.app/Contents/MacOS/${BASE_APP_NAME}" || true
|
|
||||||
else
|
|
||||||
# Tag mode: only kill the tagged instance; allow side-by-side with the main app.
|
|
||||||
pkill -f "${APP_NAME}.app/Contents/MacOS/${BASE_APP_NAME}" || true
|
|
||||||
fi
|
|
||||||
sleep 0.3
|
|
||||||
CMUXD_SRC="$PWD/cmuxd/zig-out/bin/cmuxd"
|
CMUXD_SRC="$PWD/cmuxd/zig-out/bin/cmuxd"
|
||||||
GHOSTTY_HELPER_SRC="$PWD/ghostty/zig-out/bin/ghostty"
|
GHOSTTY_HELPER_SRC="$PWD/ghostty/zig-out/bin/ghostty"
|
||||||
if [[ -d "$PWD/cmuxd" ]]; then
|
if [[ -d "$PWD/cmuxd" ]]; then
|
||||||
|
|
@ -443,62 +440,81 @@ CLI_PATH="$APP_PATH/Contents/Resources/bin/cmux"
|
||||||
if [[ -x "$CLI_PATH" ]]; then
|
if [[ -x "$CLI_PATH" ]]; then
|
||||||
echo "$CLI_PATH" > /tmp/cmux-last-cli-path || true
|
echo "$CLI_PATH" > /tmp/cmux-last-cli-path || true
|
||||||
fi
|
fi
|
||||||
# Avoid inheriting cmux/ghostty environment variables from the terminal that
|
|
||||||
# runs this script (often inside another cmux instance), which can cause
|
|
||||||
# socket and resource-path conflicts.
|
|
||||||
OPEN_CLEAN_ENV=(
|
|
||||||
env
|
|
||||||
-u CMUX_SOCKET_PATH
|
|
||||||
-u CMUX_WORKSPACE_ID
|
|
||||||
-u CMUX_SURFACE_ID
|
|
||||||
-u CMUX_TAB_ID
|
|
||||||
-u CMUX_PANEL_ID
|
|
||||||
-u CMUXD_UNIX_PATH
|
|
||||||
-u CMUX_TAG
|
|
||||||
-u CMUX_DEBUG_LOG
|
|
||||||
-u CMUX_BUNDLE_ID
|
|
||||||
-u CMUX_SHELL_INTEGRATION
|
|
||||||
-u GHOSTTY_BIN_DIR
|
|
||||||
-u GHOSTTY_RESOURCES_DIR
|
|
||||||
-u GHOSTTY_SHELL_FEATURES
|
|
||||||
# Dev shells (including CI/Codex) often force-disable paging by exporting these.
|
|
||||||
# Don't leak that into cmux, otherwise `git diff` won't page even with PAGER=less.
|
|
||||||
-u GIT_PAGER
|
|
||||||
-u GH_PAGER
|
|
||||||
-u TERMINFO
|
|
||||||
-u XDG_DATA_DIRS
|
|
||||||
)
|
|
||||||
|
|
||||||
if [[ -n "${TAG_SLUG:-}" && -n "${CMUX_SOCKET:-}" ]]; then
|
if [[ "$LAUNCH" -eq 1 ]]; then
|
||||||
# Ensure tag-specific socket paths win even if the caller has CMUX_* overrides.
|
# Ensure any running instance is fully terminated, regardless of DerivedData path.
|
||||||
"${OPEN_CLEAN_ENV[@]}" CMUX_TAG="$TAG_SLUG" CMUX_SOCKET_ENABLE=1 CMUX_SOCKET_MODE=automation CMUX_SOCKET_PATH="$CMUX_SOCKET" CMUXD_UNIX_PATH="$CMUXD_SOCKET" CMUX_DEBUG_LOG="$CMUX_DEBUG_LOG" CMUX_REMOTE_DAEMON_ALLOW_LOCAL_BUILD=1 CMUXTERM_REPO_ROOT="$PWD" open -g "$APP_PATH"
|
/usr/bin/osascript -e "tell application id \"${BUNDLE_ID}\" to quit" >/dev/null 2>&1 || true
|
||||||
elif [[ -n "${TAG_SLUG:-}" ]]; then
|
sleep 0.3
|
||||||
"${OPEN_CLEAN_ENV[@]}" CMUX_TAG="$TAG_SLUG" CMUX_SOCKET_ENABLE=1 CMUX_SOCKET_MODE=automation CMUX_DEBUG_LOG="$CMUX_DEBUG_LOG" CMUX_REMOTE_DAEMON_ALLOW_LOCAL_BUILD=1 CMUXTERM_REPO_ROOT="$PWD" open -g "$APP_PATH"
|
if [[ -z "$TAG" ]]; then
|
||||||
else
|
# Non-tag mode: kill any running instance (across any DerivedData path) to avoid socket conflicts.
|
||||||
echo "/tmp/cmux-debug.sock" > /tmp/cmux-last-socket-path || true
|
pkill -f "/${BASE_APP_NAME}.app/Contents/MacOS/${BASE_APP_NAME}" || true
|
||||||
echo "/tmp/cmux-debug.log" > /tmp/cmux-last-debug-log-path || true
|
else
|
||||||
"${OPEN_CLEAN_ENV[@]}" open -g "$APP_PATH"
|
# Tag mode: only kill the tagged instance; allow side-by-side with the main app.
|
||||||
|
pkill -f "${APP_NAME}.app/Contents/MacOS/${BASE_APP_NAME}" || true
|
||||||
|
fi
|
||||||
|
sleep 0.3
|
||||||
|
|
||||||
|
# Avoid inheriting cmux/ghostty environment variables from the terminal that
|
||||||
|
# runs this script (often inside another cmux instance), which can cause
|
||||||
|
# socket and resource-path conflicts.
|
||||||
|
OPEN_CLEAN_ENV=(
|
||||||
|
env
|
||||||
|
-u CMUX_SOCKET_PATH
|
||||||
|
-u CMUX_WORKSPACE_ID
|
||||||
|
-u CMUX_SURFACE_ID
|
||||||
|
-u CMUX_TAB_ID
|
||||||
|
-u CMUX_PANEL_ID
|
||||||
|
-u CMUXD_UNIX_PATH
|
||||||
|
-u CMUX_TAG
|
||||||
|
-u CMUX_DEBUG_LOG
|
||||||
|
-u CMUX_BUNDLE_ID
|
||||||
|
-u CMUX_SHELL_INTEGRATION
|
||||||
|
-u GHOSTTY_BIN_DIR
|
||||||
|
-u GHOSTTY_RESOURCES_DIR
|
||||||
|
-u GHOSTTY_SHELL_FEATURES
|
||||||
|
# Dev shells (including CI/Codex) often force-disable paging by exporting these.
|
||||||
|
# Don't leak that into cmux, otherwise `git diff` won't page even with PAGER=less.
|
||||||
|
-u GIT_PAGER
|
||||||
|
-u GH_PAGER
|
||||||
|
-u TERMINFO
|
||||||
|
-u XDG_DATA_DIRS
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ -n "${TAG_SLUG:-}" && -n "${CMUX_SOCKET:-}" ]]; then
|
||||||
|
# Ensure tag-specific socket paths win even if the caller has CMUX_* overrides.
|
||||||
|
"${OPEN_CLEAN_ENV[@]}" CMUX_TAG="$TAG_SLUG" CMUX_SOCKET_ENABLE=1 CMUX_SOCKET_MODE=automation CMUX_SOCKET_PATH="$CMUX_SOCKET" CMUXD_UNIX_PATH="$CMUXD_SOCKET" CMUX_DEBUG_LOG="$CMUX_DEBUG_LOG" CMUX_REMOTE_DAEMON_ALLOW_LOCAL_BUILD=1 CMUXTERM_REPO_ROOT="$PWD" open -g "$APP_PATH"
|
||||||
|
elif [[ -n "${TAG_SLUG:-}" ]]; then
|
||||||
|
"${OPEN_CLEAN_ENV[@]}" CMUX_TAG="$TAG_SLUG" CMUX_SOCKET_ENABLE=1 CMUX_SOCKET_MODE=automation CMUX_DEBUG_LOG="$CMUX_DEBUG_LOG" CMUX_REMOTE_DAEMON_ALLOW_LOCAL_BUILD=1 CMUXTERM_REPO_ROOT="$PWD" open -g "$APP_PATH"
|
||||||
|
else
|
||||||
|
echo "/tmp/cmux-debug.sock" > /tmp/cmux-last-socket-path || true
|
||||||
|
echo "/tmp/cmux-debug.log" > /tmp/cmux-last-debug-log-path || true
|
||||||
|
"${OPEN_CLEAN_ENV[@]}" open -g "$APP_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Safety: ensure only one instance is running.
|
||||||
|
sleep 0.2
|
||||||
|
PIDS=($(pgrep -f "${APP_PATH}/Contents/MacOS/" || true))
|
||||||
|
if [[ "${#PIDS[@]}" -gt 1 ]]; then
|
||||||
|
NEWEST_PID=""
|
||||||
|
NEWEST_AGE=999999
|
||||||
|
for PID in "${PIDS[@]}"; do
|
||||||
|
AGE="$(ps -o etimes= -p "$PID" | tr -d ' ')"
|
||||||
|
if [[ -n "$AGE" && "$AGE" -lt "$NEWEST_AGE" ]]; then
|
||||||
|
NEWEST_AGE="$AGE"
|
||||||
|
NEWEST_PID="$PID"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for PID in "${PIDS[@]}"; do
|
||||||
|
if [[ "$PID" != "$NEWEST_PID" ]]; then
|
||||||
|
kill "$PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Safety: ensure only one instance is running.
|
echo
|
||||||
sleep 0.2
|
echo "App path:"
|
||||||
PIDS=($(pgrep -f "${APP_PATH}/Contents/MacOS/" || true))
|
echo " $APP_PATH"
|
||||||
if [[ "${#PIDS[@]}" -gt 1 ]]; then
|
|
||||||
NEWEST_PID=""
|
|
||||||
NEWEST_AGE=999999
|
|
||||||
for PID in "${PIDS[@]}"; do
|
|
||||||
AGE="$(ps -o etimes= -p "$PID" | tr -d ' ')"
|
|
||||||
if [[ -n "$AGE" && "$AGE" -lt "$NEWEST_AGE" ]]; then
|
|
||||||
NEWEST_AGE="$AGE"
|
|
||||||
NEWEST_PID="$PID"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
for PID in "${PIDS[@]}"; do
|
|
||||||
if [[ "$PID" != "$NEWEST_PID" ]]; then
|
|
||||||
kill "$PID" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "${TAG_SLUG:-}" ]]; then
|
if [[ -n "${TAG_SLUG:-}" ]]; then
|
||||||
print_tag_cleanup_reminder "$TAG_SLUG"
|
print_tag_cleanup_reminder "$TAG_SLUG"
|
||||||
|
|
@ -516,3 +532,8 @@ if [[ -x "${CLI_PATH:-}" ]]; then
|
||||||
fi
|
fi
|
||||||
echo "If your shell still resolves the old cmux, run: rehash"
|
echo "If your shell still resolves the old cmux, run: rehash"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "$LAUNCH" -eq 0 ]]; then
|
||||||
|
echo
|
||||||
|
echo "Build complete. Pass --launch to open the app, or cmd-click the path above."
|
||||||
|
fi
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue