From 6bc0d30c69cc22306c392f24e732ddac9325c89b Mon Sep 17 00:00:00 2001 From: scosman Date: Sat, 14 Mar 2026 16:38:21 -0400 Subject: [PATCH 01/13] WIP Zellij workspace --- .config/wt.toml | 13 +++++++++++++ .config/wt/layout.kdl | 29 +++++++++++++++++++++++++++++ .config/wt/start.sh | 29 +++++++++++++++++++++++++++++ .config/wt/user_settings.sh.example | 9 +++++++++ .gitignore | 2 ++ app/desktop/dev_server.py | 4 +++- app/web_ui/src/lib/api_client.ts | 3 ++- 7 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 .config/wt.toml create mode 100644 .config/wt/layout.kdl create mode 100755 .config/wt/start.sh create mode 100644 .config/wt/user_settings.sh.example diff --git a/.config/wt.toml b/.config/wt.toml new file mode 100644 index 000000000..73a2b8dd3 --- /dev/null +++ b/.config/wt.toml @@ -0,0 +1,13 @@ +[post-create] +# deps = "uv sync && cd app/web_ui && npm install" + +[pre-remove] +session = "zellij delete-session {{ branch | sanitize }} 2>/dev/null || true" +ports = """ +PORT={{ branch | hash_port }} +lsof -ti :$PORT -sTCP:LISTEN | xargs kill 2>/dev/null || true +lsof -ti :$((PORT + 1)) -sTCP:LISTEN | xargs kill 2>/dev/null || true +""" + +[list] +url = "http://localhost:{{ branch | hash_port }}" diff --git a/.config/wt/layout.kdl b/.config/wt/layout.kdl new file mode 100644 index 000000000..40cb30e19 --- /dev/null +++ b/.config/wt/layout.kdl @@ -0,0 +1,29 @@ +layout { + default_tab_template { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + children + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + tab name="term" focus=true { + pane + } + tab name="code" { + pane command="bash" start_suspended=false { + args "-c" "$KILN_CODER_CMD" + } + } + tab name="backend" { + pane command="bash" start_suspended=false { + args "-c" "uv run python -m app.desktop.dev_server" + } + } + tab name="webui" cwd="app/web_ui" { + pane command="bash" start_suspended=false { + args "-c" "npm run dev -- --host --port $KILN_FRONTEND_PORT" + } + } +} diff --git a/.config/wt/start.sh b/.config/wt/start.sh new file mode 100755 index 000000000..9d5a0e9c0 --- /dev/null +++ b/.config/wt/start.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +PORT="${1:-8757}" +BRANCH="${2:-$(git -C "$REPO_ROOT" branch --show-current 2>/dev/null || echo "main")}" +SESSION_NAME="${BRANCH//\//-}" + +export KILN_PORT="$PORT" +export KILN_FRONTEND_PORT="$((PORT + 1))" +export VITE_API_PORT="$PORT" + +export KILN_CODER_CMD="claude" +if [ -f "$REPO_ROOT/.config/wt/user_settings.sh" ]; then + # shellcheck source=/dev/null + source "$REPO_ROOT/.config/wt/user_settings.sh" +fi + +printf '\e]0;FEAT: %s\a' "$BRANCH" + +cd "$REPO_ROOT" + +LAYOUT="$REPO_ROOT/.config/wt/layout.kdl" + +# Kill any cached/serialized session so we always get a fresh layout +zellij delete-session "$SESSION_NAME" 2>/dev/null || true + +exec zellij -s "$SESSION_NAME" -n "$LAYOUT" diff --git a/.config/wt/user_settings.sh.example b/.config/wt/user_settings.sh.example new file mode 100644 index 000000000..1ef2bfce3 --- /dev/null +++ b/.config/wt/user_settings.sh.example @@ -0,0 +1,9 @@ +# Per-user settings for the Kiln dev environment. +# Copy this file to user_settings.sh and customize: +# cp user_settings.sh.example user_settings.sh +# +# user_settings.sh is gitignored — your changes stay local. + +# Coding agent command (default: claude) +# Examples: "claude", "cursor .", "aider", "zed ." +KILN_CODER_CMD="claude" diff --git a/.gitignore b/.gitignore index a4b8f5f71..c504010f2 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ libs/server/build dist/ .mcp.json + +.config/wt/user_settings.sh diff --git a/app/desktop/dev_server.py b/app/desktop/dev_server.py index 88dc5047b..3ed7c7d4d 100644 --- a/app/desktop/dev_server.py +++ b/app/desktop/dev_server.py @@ -20,10 +20,12 @@ if __name__ == "__main__": setup_resource_limits() + port = int(os.environ.get("KILN_PORT", "8757")) + uvicorn.run( "app.desktop.dev_server:dev_app", host="127.0.0.1", - port=8757, + port=port, reload=True, # Debounce when changing many files (changing branch) reload_delay=0.1, diff --git a/app/web_ui/src/lib/api_client.ts b/app/web_ui/src/lib/api_client.ts index 8b4e9e0ed..a2cae4366 100644 --- a/app/web_ui/src/lib/api_client.ts +++ b/app/web_ui/src/lib/api_client.ts @@ -1,7 +1,8 @@ import createClient from "openapi-fetch" import type { paths } from "./api_schema" -export const base_url = "http://localhost:8757" +const api_port = import.meta.env.VITE_API_PORT || "8757" +export const base_url = `http://localhost:${api_port}` export const client = createClient({ baseUrl: base_url, From baeb9c2f6e5ec2e0baddae39a2e49465dfdcd794 Mon Sep 17 00:00:00 2001 From: scosman Date: Sat, 14 Mar 2026 16:42:51 -0400 Subject: [PATCH 02/13] open web with command web --- .config/wt.toml | 8 -------- .config/wt/bin/web | 2 ++ .config/wt/layout.kdl | 4 +++- .config/wt/start.sh | 12 ++++++++++-- 4 files changed, 15 insertions(+), 11 deletions(-) create mode 100755 .config/wt/bin/web diff --git a/.config/wt.toml b/.config/wt.toml index 73a2b8dd3..1aa54130d 100644 --- a/.config/wt.toml +++ b/.config/wt.toml @@ -3,11 +3,3 @@ [pre-remove] session = "zellij delete-session {{ branch | sanitize }} 2>/dev/null || true" -ports = """ -PORT={{ branch | hash_port }} -lsof -ti :$PORT -sTCP:LISTEN | xargs kill 2>/dev/null || true -lsof -ti :$((PORT + 1)) -sTCP:LISTEN | xargs kill 2>/dev/null || true -""" - -[list] -url = "http://localhost:{{ branch | hash_port }}" diff --git a/.config/wt/bin/web b/.config/wt/bin/web new file mode 100755 index 000000000..1aea64267 --- /dev/null +++ b/.config/wt/bin/web @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +open "${KILN_WEB_URL:-http://localhost:8758}" diff --git a/.config/wt/layout.kdl b/.config/wt/layout.kdl index 40cb30e19..a27c1ee6e 100644 --- a/.config/wt/layout.kdl +++ b/.config/wt/layout.kdl @@ -9,7 +9,9 @@ layout { } } tab name="term" focus=true { - pane + pane command="bash" start_suspended=false { + args "-c" "echo \"\nWeb UI: $KILN_WEB_URL\nBackend: http://localhost:$KILN_PORT\n\nType 'web' to open in browser.\n\"; exec $SHELL" + } } tab name="code" { pane command="bash" start_suspended=false { diff --git a/.config/wt/start.sh b/.config/wt/start.sh index 9d5a0e9c0..43d2e4f9f 100755 --- a/.config/wt/start.sh +++ b/.config/wt/start.sh @@ -3,20 +3,28 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -PORT="${1:-8757}" -BRANCH="${2:-$(git -C "$REPO_ROOT" branch --show-current 2>/dev/null || echo "main")}" +BRANCH="${1:-$(git -C "$REPO_ROOT" branch --show-current 2>/dev/null || echo "main")}" SESSION_NAME="${BRANCH//\//-}" +hash_port() { + printf '%s' "$1" | cksum | awk '{print ($1 % 10000) + 10000}' +} +PORT=$(hash_port "$BRANCH") + export KILN_PORT="$PORT" export KILN_FRONTEND_PORT="$((PORT + 1))" export VITE_API_PORT="$PORT" +export KILN_WEB_URL="http://localhost:$KILN_FRONTEND_PORT" + export KILN_CODER_CMD="claude" if [ -f "$REPO_ROOT/.config/wt/user_settings.sh" ]; then # shellcheck source=/dev/null source "$REPO_ROOT/.config/wt/user_settings.sh" fi +export PATH="$REPO_ROOT/.config/wt/bin:$PATH" + printf '\e]0;FEAT: %s\a' "$BRANCH" cd "$REPO_ROOT" From 1bcca4ad194f383026ad3a4a4c2529f04e97dc11 Mon Sep 17 00:00:00 2001 From: scosman Date: Sat, 14 Mar 2026 16:46:25 -0400 Subject: [PATCH 03/13] CORS fix for dev server --- libs/server/kiln_server/server.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libs/server/kiln_server/server.py b/libs/server/kiln_server/server.py index fbc1fb69b..a2c2a4bda 100644 --- a/libs/server/kiln_server/server.py +++ b/libs/server/kiln_server/server.py @@ -1,4 +1,5 @@ import argparse +import os from importlib.metadata import version from typing import Sequence @@ -45,11 +46,12 @@ def ping(): connect_document_api(app) connect_custom_errors(app) + frontend_port = os.environ.get("KILN_FRONTEND_PORT", "5173") allowed_origins = [ - "http://localhost:5173", - "http://127.0.0.1:5173", - "https://localhost:5173", - "https://127.0.0.1:5173", + f"http://localhost:{frontend_port}", + f"http://127.0.0.1:{frontend_port}", + f"https://localhost:{frontend_port}", + f"https://127.0.0.1:{frontend_port}", ] app.add_middleware( From 6ceb44df543bd568484ede046360dbca7ad59f8e Mon Sep 17 00:00:00 2001 From: scosman Date: Sat, 14 Mar 2026 16:58:58 -0400 Subject: [PATCH 04/13] setup env --- .config/wt.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/wt.toml b/.config/wt.toml index 1aa54130d..1eb3e8411 100644 --- a/.config/wt.toml +++ b/.config/wt.toml @@ -1,5 +1,5 @@ [post-create] -# deps = "uv sync && cd app/web_ui && npm install" +deps = "uv sync && cd app/web_ui && npm install" [pre-remove] session = "zellij delete-session {{ branch | sanitize }} 2>/dev/null || true" From d697984111da49e6a286eb3588ceb2e1f9f3e382 Mon Sep 17 00:00:00 2001 From: scosman Date: Sat, 14 Mar 2026 17:34:49 -0400 Subject: [PATCH 05/13] setup default WT path --- .config/wt/config.toml | 1 + .gitignore | 1 + utils/setup_env.sh | 10 ++++++++++ 3 files changed, 12 insertions(+) create mode 100644 .config/wt/config.toml diff --git a/.config/wt/config.toml b/.config/wt/config.toml new file mode 100644 index 000000000..3ce2ba2b2 --- /dev/null +++ b/.config/wt/config.toml @@ -0,0 +1 @@ +worktree-path = ".worktrees/{{ branch | sanitize }}" diff --git a/.gitignore b/.gitignore index c504010f2..cc44bed61 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ dist/ .mcp.json .config/wt/user_settings.sh +.worktrees/ diff --git a/utils/setup_env.sh b/utils/setup_env.sh index 7303c46e8..610020771 100755 --- a/utils/setup_env.sh +++ b/utils/setup_env.sh @@ -10,3 +10,13 @@ uv sync cd "$PROJECT_ROOT/app/web_ui" npm install +# Setup worktrunk config +WT_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/worktrunk" +WT_CONFIG="$WT_CONFIG_DIR/config.toml" +WT_PROJECT_CONFIG="$PROJECT_ROOT/.config/wt/config.toml" +if [ ! -e "$WT_CONFIG" ] && [ -f "$WT_PROJECT_CONFIG" ]; then + mkdir -p "$WT_CONFIG_DIR" + ln -sf "$WT_PROJECT_CONFIG" "$WT_CONFIG" + echo "Linked worktrunk config: $WT_CONFIG -> $WT_PROJECT_CONFIG" +fi + From 73a9e857078e5af6dd3cad80d6db58168bd68c22 Mon Sep 17 00:00:00 2001 From: scosman Date: Sat, 14 Mar 2026 20:19:44 -0400 Subject: [PATCH 06/13] worktree README --- .config/wt/README.md | 149 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 .config/wt/README.md diff --git a/.config/wt/README.md b/.config/wt/README.md new file mode 100644 index 000000000..9799f4f8f --- /dev/null +++ b/.config/wt/README.md @@ -0,0 +1,149 @@ +# Worktree Workflow + +We use [Worktrunk](https://worktrunk.dev/) to manage git worktrees for parallel development. Each worktree gets its own Zellij session with dedicated ports, a backend server, a frontend dev server, and a coding agent. + +## Setup + +1. Install worktrunk: `brew install worktrunk && wt config shell install` +2. Install Zellij: `brew install zellij` +3. Configure worktree path (one-time, applies to all repos): + +```bash +mkdir -p ~/.config/worktrunk +echo 'worktree-path = ".worktrees/{{ branch | sanitize }}"' > ~/.config/worktrunk/config.toml +``` + +Or run `utils/setup_env.sh`, which does this automatically. + +## Commands + +### Create a new worktree and launch it + +```bash +wt switch --create my-feature -x .config/wt/start.sh +``` + +This creates a branch, sets up the worktree, installs dependencies (via `post-create` hook), then launches a Zellij session with all dev tools. + +To branch from something other than `main`: + +```bash +wt switch --create my-feature --base other-branch -x .config/wt/start.sh +``` + +### Launch an existing worktree + +```bash +wt switch my-feature -x .config/wt/start.sh +``` + +If the worktree already exists, this switches to it and launches Zellij. If the worktree was removed but the branch exists, it re-creates the worktree first. + +### Switch to a worktree (cd only, no Zellij) + +```bash +wt switch my-feature +``` + +### List all worktrees + +```bash +wt list +``` + +With CI status and line diffs: + +```bash +wt list --full +``` + +Include branches that don't have worktrees: + +```bash +wt list --branches +``` + +### Interactive picker + +```bash +wt switch +``` + +Running `wt switch` with no arguments opens an interactive picker with live diff/log previews. + +### Remove a worktree + +From inside the worktree: + +```bash +wt remove +``` + +From anywhere: + +```bash +wt remove my-feature +``` + +Force-remove with unmerged commits: + +```bash +wt remove -D my-feature +``` + +### Merge a feature branch + +From inside the feature worktree — squashes, rebases, fast-forward merges to main, and cleans up: + +```bash +wt merge +``` + +Merge to a different target: + +```bash +wt merge develop +``` + +Keep the worktree after merging: + +```bash +wt merge --no-remove +``` + +### Quick navigation + +```bash +wt switch - # Previous worktree (like cd -) +wt switch ^ # Default branch (main) +wt switch pr:123 # GitHub PR #123 +``` + +### View full diff of a branch + +All changes since branching (committed + uncommitted): + +```bash +wt step diff +``` + +### Commit with LLM-generated message + +```bash +wt step commit +``` + +### Cleanup merged branches + +```bash +wt step prune +``` + +## Architecture + +- `.config/wt.toml` — project hooks: dependency install on create, Zellij session cleanup on remove +- `.config/wt/start.sh` — launches a Zellij session with per-branch ports (via `hash_port`) +- `.config/wt/layout.kdl` — Zellij layout: terminal, coding agent, backend server, frontend dev server +- `.config/wt/bin/web` — opens the worktree's web UI in a browser (type `web` in the terminal tab) +- `.config/wt/user_settings.sh` — per-user overrides (gitignored); copy from `user_settings.sh.example` +- `.config/wt/config.toml` — worktrunk user config (worktree path template) From 89deeb5bc431dd2fe71e4b0b85c61bba4d4f67e2 Mon Sep 17 00:00:00 2001 From: scosman Date: Sat, 14 Mar 2026 21:06:58 -0400 Subject: [PATCH 07/13] CR feedback --- .config/wt/bin/web | 7 ++++++- .config/wt/layout.kdl | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.config/wt/bin/web b/.config/wt/bin/web index 1aea64267..b90c74c12 100755 --- a/.config/wt/bin/web +++ b/.config/wt/bin/web @@ -1,2 +1,7 @@ #!/usr/bin/env bash -open "${KILN_WEB_URL:-http://localhost:8758}" +URL="${KILN_WEB_URL:-http://localhost:8758}" +if [[ "$(uname)" == "Darwin" ]]; then + open "$URL" +else + xdg-open "$URL" +fi diff --git a/.config/wt/layout.kdl b/.config/wt/layout.kdl index a27c1ee6e..61aebacfa 100644 --- a/.config/wt/layout.kdl +++ b/.config/wt/layout.kdl @@ -13,7 +13,7 @@ layout { args "-c" "echo \"\nWeb UI: $KILN_WEB_URL\nBackend: http://localhost:$KILN_PORT\n\nType 'web' to open in browser.\n\"; exec $SHELL" } } - tab name="code" { + tab name="agent" { pane command="bash" start_suspended=false { args "-c" "$KILN_CODER_CMD" } From 5cd3a327330e5220c44b4be59c9a61acc4499135 Mon Sep 17 00:00:00 2001 From: scosman Date: Sat, 14 Mar 2026 21:36:04 -0400 Subject: [PATCH 08/13] better session reattach --- .config/wt/start.sh | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/.config/wt/start.sh b/.config/wt/start.sh index 43d2e4f9f..39834e15a 100755 --- a/.config/wt/start.sh +++ b/.config/wt/start.sh @@ -1,8 +1,15 @@ #!/usr/bin/env bash +set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +RESTART=false +if [ "${1:-}" = "--restart" ] || [ "${1:-}" = "-r" ]; then + RESTART=true + shift +fi + BRANCH="${1:-$(git -C "$REPO_ROOT" branch --show-current 2>/dev/null || echo "main")}" SESSION_NAME="${BRANCH//\//-}" @@ -31,7 +38,17 @@ cd "$REPO_ROOT" LAYOUT="$REPO_ROOT/.config/wt/layout.kdl" -# Kill any cached/serialized session so we always get a fresh layout -zellij delete-session "$SESSION_NAME" 2>/dev/null || true +SESSION_STATUS=$(zellij list-sessions --no-formatting 2>/dev/null \ + | awk -v name="$SESSION_NAME" '{ n=$1; gsub(/[[:space:]]/, "", n); if (n == name) { print (/EXITED/ ? "exited" : "active"); exit } }') || true -exec zellij -s "$SESSION_NAME" -n "$LAYOUT" +if $RESTART; then + zellij kill-session "$SESSION_NAME" 2>/dev/null || true + zellij delete-session "$SESSION_NAME" 2>/dev/null || true + exec zellij -s "$SESSION_NAME" -n "$LAYOUT" +fi + +if [ -n "$SESSION_STATUS" ]; then + exec zellij attach "$SESSION_NAME" +else + exec zellij -s "$SESSION_NAME" -n "$LAYOUT" +fi From 53c5f8774c0492684b33de689e4139ec7c3df902 Mon Sep 17 00:00:00 2001 From: scosman Date: Sat, 14 Mar 2026 22:55:05 -0400 Subject: [PATCH 09/13] Better setup --- .config/wt/README.md | 19 +++++++++++++++++-- .config/wt/layout.kdl | 2 +- utils/setup_env.sh | 39 +++++++++++++++++++++++++++++++-------- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/.config/wt/README.md b/.config/wt/README.md index 9799f4f8f..df52707f9 100644 --- a/.config/wt/README.md +++ b/.config/wt/README.md @@ -4,6 +4,10 @@ We use [Worktrunk](https://worktrunk.dev/) to manage git worktrees for parallel ## Setup +Run `utils/setup_env.sh` — it installs project dependencies and optionally sets up workspaces (worktrunk, Zellij, and config). + +Or manually: + 1. Install worktrunk: `brew install worktrunk && wt config shell install` 2. Install Zellij: `brew install zellij` 3. Configure worktree path (one-time, applies to all repos): @@ -13,8 +17,6 @@ mkdir -p ~/.config/worktrunk echo 'worktree-path = ".worktrees/{{ branch | sanitize }}"' > ~/.config/worktrunk/config.toml ``` -Or run `utils/setup_env.sh`, which does this automatically. - ## Commands ### Create a new worktree and launch it @@ -139,6 +141,19 @@ wt step commit wt step prune ``` +## Keybinds for Option Left/right to switch tabs + +In `~/.config/zellij/config.kdl` add: + +``` +keybinds clear-defaults=true { + shared_except "locked" { + bind "Alt b" { GoToPreviousTab; } + bind "Alt f" { GoToNextTab; } + } +} +``` + ## Architecture - `.config/wt.toml` — project hooks: dependency install on create, Zellij session cleanup on remove diff --git a/.config/wt/layout.kdl b/.config/wt/layout.kdl index 61aebacfa..e3be3dde0 100644 --- a/.config/wt/layout.kdl +++ b/.config/wt/layout.kdl @@ -9,7 +9,7 @@ layout { } } tab name="term" focus=true { - pane command="bash" start_suspended=false { + pane name="terminal" command="bash" start_suspended=false { args "-c" "echo \"\nWeb UI: $KILN_WEB_URL\nBackend: http://localhost:$KILN_PORT\n\nType 'web' to open in browser.\n\"; exec $SHELL" } } diff --git a/utils/setup_env.sh b/utils/setup_env.sh index 610020771..c5e8216d9 100755 --- a/utils/setup_env.sh +++ b/utils/setup_env.sh @@ -10,13 +10,36 @@ uv sync cd "$PROJECT_ROOT/app/web_ui" npm install -# Setup worktrunk config -WT_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/worktrunk" -WT_CONFIG="$WT_CONFIG_DIR/config.toml" -WT_PROJECT_CONFIG="$PROJECT_ROOT/.config/wt/config.toml" -if [ ! -e "$WT_CONFIG" ] && [ -f "$WT_PROJECT_CONFIG" ]; then - mkdir -p "$WT_CONFIG_DIR" - ln -sf "$WT_PROJECT_CONFIG" "$WT_CONFIG" - echo "Linked worktrunk config: $WT_CONFIG -> $WT_PROJECT_CONFIG" +echo "" +read -rp "Install Kiln workspaces (worktree-based parallel dev with Zellij)? [y/N] " install_workspaces +if [[ "$install_workspaces" =~ ^[Yy]$ ]]; then + if ! command -v wt &>/dev/null; then + echo "Installing worktrunk..." + brew install worktrunk + wt config shell install + echo " Restart your shell (or open a new tab) for 'wt' completions to take effect." + else + echo " worktrunk already installed." + fi + + if ! command -v zellij &>/dev/null; then + echo "Installing zellij..." + brew install zellij + else + echo " zellij already installed." + fi + + WT_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/worktrunk" + WT_CONFIG="$WT_CONFIG_DIR/config.toml" + WT_PROJECT_CONFIG="$PROJECT_ROOT/.config/wt/config.toml" + if [ ! -e "$WT_CONFIG" ] && [ -f "$WT_PROJECT_CONFIG" ]; then + mkdir -p "$WT_CONFIG_DIR" + ln -sf "$WT_PROJECT_CONFIG" "$WT_CONFIG" + echo " Linked worktrunk config: $WT_CONFIG -> $WT_PROJECT_CONFIG" + else + echo " Worktrunk config already present." + fi + + echo "Workspaces ready! See .config/wt/README.md for usage." fi From 6079324eb2b1558946058db2778593985c8be9d0 Mon Sep 17 00:00:00 2001 From: scosman Date: Sun, 15 Mar 2026 13:39:34 -0400 Subject: [PATCH 10/13] Add wk command --- .config/wk.yml | 2 ++ .config/wt/README.md | 9 +++++++-- utils/setup_env.sh | 7 +++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 .config/wk.yml diff --git a/.config/wk.yml b/.config/wk.yml new file mode 100644 index 000000000..78d5461fa --- /dev/null +++ b/.config/wk.yml @@ -0,0 +1,2 @@ +open_workspace_cmd: "./.config/wt/start.sh" +restart_workspace_cmd: "./.config/wt/start.sh --restart" diff --git a/.config/wt/README.md b/.config/wt/README.md index df52707f9..6c9968b9a 100644 --- a/.config/wt/README.md +++ b/.config/wt/README.md @@ -10,14 +10,19 @@ Or manually: 1. Install worktrunk: `brew install worktrunk && wt config shell install` 2. Install Zellij: `brew install zellij` -3. Configure worktree path (one-time, applies to all repos): +3. Install worktree TUI: `uv tool install "git+https://github.com/scosman/worktree_tui"` +4. Configure worktree path (one-time, applies to all repos): ```bash mkdir -p ~/.config/worktrunk echo 'worktree-path = ".worktrees/{{ branch | sanitize }}"' > ~/.config/worktrunk/config.toml ``` -## Commands +## Command + +Just run `wk` and you can manage workspaces! + +## Advanced Commands ### Create a new worktree and launch it diff --git a/utils/setup_env.sh b/utils/setup_env.sh index c5e8216d9..ea3c65a71 100755 --- a/utils/setup_env.sh +++ b/utils/setup_env.sh @@ -29,6 +29,13 @@ if [[ "$install_workspaces" =~ ^[Yy]$ ]]; then echo " zellij already installed." fi + if ! command -v wk &>/dev/null; then + echo "Installing worktree TUI..." + uv tool install "git+https://github.com/scosman/worktree_tui" + else + echo " worktree TUI already installed." + fi + WT_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/worktrunk" WT_CONFIG="$WT_CONFIG_DIR/config.toml" WT_PROJECT_CONFIG="$PROJECT_ROOT/.config/wt/config.toml" From 06fd6f57adadf123769a29891e98eeb4b6a59a67 Mon Sep 17 00:00:00 2001 From: scosman Date: Mon, 16 Mar 2026 10:10:39 -0400 Subject: [PATCH 11/13] check brew is installed --- utils/setup_env.sh | 71 +++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/utils/setup_env.sh b/utils/setup_env.sh index ea3c65a71..2add07d16 100755 --- a/utils/setup_env.sh +++ b/utils/setup_env.sh @@ -13,40 +13,45 @@ npm install echo "" read -rp "Install Kiln workspaces (worktree-based parallel dev with Zellij)? [y/N] " install_workspaces if [[ "$install_workspaces" =~ ^[Yy]$ ]]; then - if ! command -v wt &>/dev/null; then - echo "Installing worktrunk..." - brew install worktrunk - wt config shell install - echo " Restart your shell (or open a new tab) for 'wt' completions to take effect." + if ! command -v brew &>/dev/null; then + echo "Warning: Homebrew (brew) is not installed. Skipping workspace setup." + echo " Install it from https://brew.sh and re-run this script." else - echo " worktrunk already installed." + if ! command -v wt &>/dev/null; then + echo "Installing worktrunk..." + brew install worktrunk + wt config shell install + echo " Restart your shell (or open a new tab) for 'wt' completions to take effect." + else + echo " worktrunk already installed." + fi + + if ! command -v zellij &>/dev/null; then + echo "Installing zellij..." + brew install zellij + else + echo " zellij already installed." + fi + + if ! command -v wk &>/dev/null; then + echo "Installing worktree TUI..." + uv tool install "git+https://github.com/scosman/worktree_tui" + else + echo " worktree TUI already installed." + fi + + WT_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/worktrunk" + WT_CONFIG="$WT_CONFIG_DIR/config.toml" + WT_PROJECT_CONFIG="$PROJECT_ROOT/.config/wt/config.toml" + if [ ! -e "$WT_CONFIG" ] && [ -f "$WT_PROJECT_CONFIG" ]; then + mkdir -p "$WT_CONFIG_DIR" + ln -sf "$WT_PROJECT_CONFIG" "$WT_CONFIG" + echo " Linked worktrunk config: $WT_CONFIG -> $WT_PROJECT_CONFIG" + else + echo " Worktrunk config already present." + fi + + echo "Workspaces ready! See .config/wt/README.md for usage." fi - - if ! command -v zellij &>/dev/null; then - echo "Installing zellij..." - brew install zellij - else - echo " zellij already installed." - fi - - if ! command -v wk &>/dev/null; then - echo "Installing worktree TUI..." - uv tool install "git+https://github.com/scosman/worktree_tui" - else - echo " worktree TUI already installed." - fi - - WT_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/worktrunk" - WT_CONFIG="$WT_CONFIG_DIR/config.toml" - WT_PROJECT_CONFIG="$PROJECT_ROOT/.config/wt/config.toml" - if [ ! -e "$WT_CONFIG" ] && [ -f "$WT_PROJECT_CONFIG" ]; then - mkdir -p "$WT_CONFIG_DIR" - ln -sf "$WT_PROJECT_CONFIG" "$WT_CONFIG" - echo " Linked worktrunk config: $WT_CONFIG -> $WT_PROJECT_CONFIG" - else - echo " Worktrunk config already present." - fi - - echo "Workspaces ready! See .config/wt/README.md for usage." fi From 60317cf2c142eda3d2af580525c2407d481736b6 Mon Sep 17 00:00:00 2001 From: scosman Date: Wed, 18 Mar 2026 10:55:07 -0400 Subject: [PATCH 12/13] ignore worktrees in checks --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2c9a1b632..9667aad52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,10 +40,10 @@ kiln-studio-desktop = { workspace = true } kiln-ai = { workspace = true } [tool.ruff] -exclude = [] +exclude = [".worktrees"] [tool.ty.src] -exclude = ["**/test_*.py", "app/desktop/build/**", "app/web_ui/**", "**/.venv", "**/kiln_ai_server_client/**"] +exclude = [".worktrees", "**/test_*.py", "app/desktop/build/**", "app/web_ui/**", "**/.venv", "**/kiln_ai_server_client/**"] [tool.ty.rules] invalid-key = "ignore" From cad853f685d6f5bc247c73aa5eb07cb121ecbcc2 Mon Sep 17 00:00:00 2001 From: scosman Date: Wed, 18 Mar 2026 11:04:31 -0400 Subject: [PATCH 13/13] move agents folder to now standard .agents --- {agents => .agents}/card_style.md | 0 {agents => .agents}/frontend_controls.md | 0 {agents => .agents}/frontend_design_guide.md | 0 {agents => .agents}/python_test_guide.md | 0 {agents => .agents}/tables_style.md | 0 hooks_mcp.yaml | 10 +++++----- 6 files changed, 5 insertions(+), 5 deletions(-) rename {agents => .agents}/card_style.md (100%) rename {agents => .agents}/frontend_controls.md (100%) rename {agents => .agents}/frontend_design_guide.md (100%) rename {agents => .agents}/python_test_guide.md (100%) rename {agents => .agents}/tables_style.md (100%) diff --git a/agents/card_style.md b/.agents/card_style.md similarity index 100% rename from agents/card_style.md rename to .agents/card_style.md diff --git a/agents/frontend_controls.md b/.agents/frontend_controls.md similarity index 100% rename from agents/frontend_controls.md rename to .agents/frontend_controls.md diff --git a/agents/frontend_design_guide.md b/.agents/frontend_design_guide.md similarity index 100% rename from agents/frontend_design_guide.md rename to .agents/frontend_design_guide.md diff --git a/agents/python_test_guide.md b/.agents/python_test_guide.md similarity index 100% rename from agents/python_test_guide.md rename to .agents/python_test_guide.md diff --git a/agents/tables_style.md b/.agents/tables_style.md similarity index 100% rename from agents/tables_style.md rename to .agents/tables_style.md diff --git a/hooks_mcp.yaml b/hooks_mcp.yaml index 8374e6997..093a3dc32 100644 --- a/hooks_mcp.yaml +++ b/hooks_mcp.yaml @@ -87,20 +87,20 @@ prompts: - name: "python_test_guide.md" description: "Guide for testing best practices for python code" - prompt-file: "agents/python_test_guide.md" + prompt-file: ".agents/python_test_guide.md" - name: "frontend_design_guide.md" description: "Guide for UI design including color, typography, and controls" - prompt-file: "agents/frontend_design_guide.md" + prompt-file: ".agents/frontend_design_guide.md" - name: "frontend_controls.md" description: "Guide for UI controls available in the web app (spinners, buttons, dialogs, etc)" - prompt-file: "agents/frontend_controls.md" + prompt-file: ".agents/frontend_controls.md" - name: "tables_style.md" description: "Guide for css table styling" - prompt-file: "agents/tables_style.md" + prompt-file: ".agents/tables_style.md" - name: "card_style.md" description: "Guide for css card styling" - prompt-file: "agents/card_style.md" + prompt-file: ".agents/card_style.md"