diff --git a/.gitignore b/.gitignore index 1c3ba93a..650e61aa 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ result-* # no-mistakes local validation state - never commit in this public repo .no-mistakes/ +skills-lock.json + +# treehouse worktree pool - local runtime state, never commit +.treehouse/ diff --git a/README.md b/README.md index 160e73e7..5c13d68f 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ If you don't use it, just remove it from `brews` in your copy. **Heads-up:** -- `home/AGENTS.md` is my personal agent policy, and `home.nix` installs it for Claude, Codex, and opencode. +- `home/AGENTS.md` is my personal agent policy, and `home.nix` installs it as `~/AGENTS.md` and for Claude, Codex, and opencode. If you clone this repo, you'd silently inherit my agent instructions - edit or delete `home/AGENTS.md` if you don't want that. - The `cc` and `co` shell aliases in `home.nix` are high-agency shortcuts: `claude --dangerously-skip-permissions` and `codex --full-auto`. They're convenient for me, but know what they do before you use them. @@ -180,6 +180,16 @@ Both packages execute with your full user permissions and must be trusted like a Home Manager deliberately does not manage `~/.pi/agent` itself, or Pi authentication, sessions, trust decisions, caches, npm/git package trees, or any other runtime state. The model overrides contain no credentials or endpoint settings, do not choose a default model, and only take effect after you authenticate Pi yourself. This remains an additive post-video layer: it does not install Pi, a launcher, or package source code into this repository. +## Optional agentic toolkit + +A handful of [kunchenguid](https://github.com/kunchenguid) tools built for AI-assisted development - `treehouse`, `no-mistakes`, `gnhf`, `lavish-axi`, `gh-axi`, and `firstmate` - aren't Homebrew formulae/casks, so `rebuild.sh` can't install or restore them. They're personal, opt-in additions, not vendored by this config. Install (or reinstall on a new Mac) with: + +```sh +./tools.sh +``` + +Safe to re-run - it skips anything already installed. `firstmate` is cloned to `~/dev/firstmate` but never auto-started; `cd` into it and run your agent CLI there when you want to use it. + ## Notes The first time you launch `nvim`, it bootstraps [lazy.nvim](https://github.com/folke/lazy.nvim) by cloning plugins from GitHub. diff --git a/configuration.nix b/configuration.nix index 74baa399..3b60327b 100644 --- a/configuration.nix +++ b/configuration.nix @@ -28,18 +28,43 @@ nix-homebrew = { enable = true; inherit user; + autoMigrate = true; }; + # nix-homebrew only symlinks Library/Homebrew into the Nix store, not the + # top-level `completions` dir. That leaves Homebrew's own zsh completion + # symlinks dangling (e.g. share/zsh/site-functions/_brew -> + # ../../../completions/zsh/_brew), which makes every new zsh session print + # compinit errors. Recreate the missing symlink on every activation, since + # nix-homebrew rebuilds /opt/homebrew/Library from scratch each time. + system.activationScripts.postActivation.text = '' + brew_store_dir="$(dirname "$(dirname "$(readlink -f /opt/homebrew/Library/Homebrew)")")" + ln -sfn "$brew_store_dir/completions" /opt/homebrew/completions + ''; homebrew = { enable = true; onActivation.cleanup = "zap"; # remove anything not listed here onActivation.autoUpdate = true; onActivation.extraFlags = [ "--force" ]; + taps = [ + "supabase/tap" + ]; brews = [ "herdr" + "gh" + "neovim" + "pnpm" + "sox" + "supabase/tap/supabase" + "tmux" + "vercel" + "whisper-cpp" ]; casks = [ "wezterm" "claude-code" + "opensuperwhisper" + "visual-studio-code" + "battery" ]; }; } diff --git a/flake.nix b/flake.nix index 96b6b8dd..e1cc8f7e 100644 --- a/flake.nix +++ b/flake.nix @@ -18,7 +18,7 @@ let # The one username line to change if this isn't your machine. # bootstrap.sh offers to rewrite this for you if your macOS username differs. - user = "kunchen"; + user = "jakobsteenberg"; in { darwinConfigurations."mac" = nix-darwin.lib.darwinSystem { @@ -30,6 +30,7 @@ { home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; + home-manager.backupFileExtension = "backup"; home-manager.extraSpecialArgs = { inherit user; }; home-manager.users.${user} = import ./home.nix; } diff --git a/home.nix b/home.nix index 540dc972..e8ea9df4 100644 --- a/home.nix +++ b/home.nix @@ -40,6 +40,22 @@ in }; }; + programs.git = { + enable = true; + settings = { + user.name = "Jakob"; + user.email = "jakobsteen14@gmail.com"; + core.editor = "code --wait"; + diff.tool = "vscode"; + "difftool \"vscode\"".cmd = "code --wait --diff $LOCAL $REMOTE"; + merge.tool = "vscode"; + "mergetool \"vscode\"".keepBackup = false; + difftool.prompt = false; + "credential \"https://github.com\"".helper = [ "" "!/opt/homebrew/bin/gh auth git-credential" ]; + "credential \"https://gist.github.com\"".helper = [ "" "!/opt/homebrew/bin/gh auth git-credential" ]; + }; + }; + programs.starship = { enable = true; settings = { @@ -56,6 +72,8 @@ in # Edit-in-place: the real file stays in my repo, ~/.config just points at it. home.file.".config/wezterm".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/wezterm"; + home.file.".config/tmux".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/tmux"; home.file.".config/nvim".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/nvim"; home.file.".config/herdr".source = @@ -73,6 +91,8 @@ in home.file.".pi/agent/settings.json".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.pi/agent/settings.json"; + home.file."AGENTS.md".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md"; home.file.".claude/CLAUDE.md".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md"; home.file.".codex/AGENTS.md".source = diff --git a/home/.claude/settings.json b/home/.claude/settings.json index e3ab7750..3cff56cc 100644 --- a/home/.claude/settings.json +++ b/home/.claude/settings.json @@ -1,7 +1,22 @@ { - "theme": "dark-ansi", "statusLine": { "type": "command", "command": "input=$(cat); model=$(echo \"$input\" | jq -r '.model.display_name'); used=$(echo \"$input\" | jq -r '.context_window.used_percentage // empty'); if [ -n \"$used\" ]; then printf \"%s | ctx: %.0f%% used\" \"$model\" \"$used\"; else printf \"%s\" \"$model\"; fi" + }, + "theme": "dark-ansi", + "agentPushNotifEnabled": true, + "hooks": { + "SessionStart": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "lavish-axi", + "timeout": 10 + } + ] + } + ] } } diff --git a/home/.config/tmux/tmux.conf b/home/.config/tmux/tmux.conf new file mode 100644 index 00000000..df915aae --- /dev/null +++ b/home/.config/tmux/tmux.conf @@ -0,0 +1,11 @@ +set -g status-position top + +# Rose Pine Moon palette, matching WezTerm's color_scheme in home/.config/wezterm/wezterm.lua +set -g status-style "bg=#232136,fg=#e0def4" +set -g window-status-style "fg=#908caa" +set -g window-status-current-style "bg=#c4a7e7,fg=#232136,bold" + +# Pill-shaped "tabs": rounded Nerd Font caps around each window segment. +set -g window-status-format "#[fg=#44415a,bg=#232136]#[fg=#908caa,bg=#44415a] #I:#W #[fg=#44415a,bg=#232136]" +set -g window-status-current-format "#[fg=#c4a7e7,bg=#232136]#[fg=#232136,bg=#c4a7e7,bold] #I:#W #[fg=#c4a7e7,bg=#232136]" +set -g window-status-separator " " diff --git a/home/.config/wezterm/wezterm.lua b/home/.config/wezterm/wezterm.lua index 24041e72..420446ba 100644 --- a/home/.config/wezterm/wezterm.lua +++ b/home/.config/wezterm/wezterm.lua @@ -10,6 +10,93 @@ config.macos_window_background_blur = 50 config.hide_tab_bar_if_only_one_tab = true config.window_decorations = "RESIZE" +-- Simpler, layout-independent pane splits (iTerm2-style) alongside the stock defaults. +config.keys = { + { key = "d", mods = "CMD", action = wezterm.action.SplitHorizontal({ domain = "CurrentPaneDomain" }) }, + { key = "d", mods = "CMD|SHIFT", action = wezterm.action.SplitVertical({ domain = "CurrentPaneDomain" }) }, + { + key = "O", + mods = "CMD|SHIFT", + action = wezterm.action_callback(function(window, pane) + -- Explicit --pane-id because the background process doesn't inherit + -- the WEZTERM_PANE env var that the CLI would otherwise rely on. + wezterm.background_child_process({ + "wezterm", + "cli", + "move-pane-to-new-tab", + "--pane-id", + tostring(pane:pane_id()), + }) + end), + }, + -- Merge the tab to the left of the active one into the active pane as a split, + -- e.g. with 6 and 7 open and 7 active, pulls 6 in to make one tab with two panes. + { + key = "M", + mods = "CMD|SHIFT", + action = wezterm.action_callback(function(window, pane) + local mux_window = window:mux_window() + local tabs = mux_window:tabs_with_info() + + local active_index + for i, t in ipairs(tabs) do + if t.is_active then + active_index = i + break + end + end + + local prev_tab = tabs[active_index - 1] + if not prev_tab then + window:toast_notification("WezTerm", "No previous tab to merge with", nil, 2000) + return + end + + local other_pane_id + for _, info in ipairs(prev_tab.tab:panes_with_info()) do + if info.is_active then + other_pane_id = info.pane:pane_id() + end + end + + -- Same --pane-id workaround as the move-pane-to-new-tab binding above: + -- background_child_process doesn't inherit WEZTERM_PANE. + wezterm.background_child_process({ + "wezterm", + "cli", + "split-pane", + "--pane-id", + tostring(pane:pane_id()), + "--move-pane-id", + tostring(other_pane_id), + "--horizontal", + }) + end), + }, + -- Reorder the active tab one position left/right, e.g. to pull tab 7 to position 1. + { key = "LeftArrow", mods = "CMD|SHIFT", action = wezterm.action.MoveTabRelative(-1) }, + { key = "RightArrow", mods = "CMD|SHIFT", action = wezterm.action.MoveTabRelative(1) }, + -- Resize the active pane. ALT is the Option key on macOS. + { key = "LeftArrow", mods = "CTRL|SHIFT|ALT", action = wezterm.action.AdjustPaneSize({ "Left", 1 }) }, + { key = "RightArrow", mods = "CTRL|SHIFT|ALT", action = wezterm.action.AdjustPaneSize({ "Right", 1 }) }, + { key = "UpArrow", mods = "CTRL|SHIFT|ALT", action = wezterm.action.AdjustPaneSize({ "Up", 1 }) }, + { key = "DownArrow", mods = "CTRL|SHIFT|ALT", action = wezterm.action.AdjustPaneSize({ "Down", 1 }) }, + -- One-shot copy of the entire scrollback (not just the current viewport) to the clipboard, + -- so there's no need to manually drive Copy Mode's g / Shift+V / Shift+G / y sequence. + { + key = "C", + mods = "CMD|SHIFT", + action = wezterm.action.Multiple({ + wezterm.action.ActivateCopyMode, + wezterm.action.CopyMode("MoveToScrollbackTop"), + wezterm.action.CopyMode({ SetSelectionMode = "Line" }), + wezterm.action.CopyMode("MoveToScrollbackBottom"), + wezterm.action.CopyTo("Clipboard"), + wezterm.action.CopyMode("Close"), + }), + }, +} + -- Dim unfocused windows so the focused one is obvious at a glance. local UNFOCUSED_FOREGROUND_TEXT_HSB = { hue = 1.0, saturation = 0.25, brightness = 0.45 } local UNFOCUSED_WINDOW_BACKGROUND_OPACITY = 0.62 diff --git a/home/AGENTS.md b/home/AGENTS.md index ee6b7233..96054eed 100644 --- a/home/AGENTS.md +++ b/home/AGENTS.md @@ -13,6 +13,7 @@ - Apply that same high standard to engineering excellence: lint, test failures, and test flakiness. If you see one, even if it is not caused by what you are working on right now, still get it fixed. - Before using "dynamic workflows", "ultra code" or any harness feature that immediately spawns a large swarm of subagents, always explain the tradeoffs and ask the user for explicit approval. +- For any project that spans multiple sessions, set up a living `docs/HANDOFF.md` status file (template: `~/dotfiles/templates/HANDOFF.md.template`), referenced from the project's AGENTS.md/CLAUDE.md so a fresh session finds it automatically. Edit its SESSION STATUS section in place at the end of a work session - never leave it stale, and never just append a new dated entry instead of updating it. ## Maintaining this file diff --git a/home/CLAUDE.md b/home/CLAUDE.md deleted file mode 100644 index a9d4d269..00000000 --- a/home/CLAUDE.md +++ /dev/null @@ -1,2 +0,0 @@ - -@AGENTS.md diff --git a/notes/wezterm-keybindings-session.md b/notes/wezterm-keybindings-session.md new file mode 100644 index 00000000..b89f4a17 --- /dev/null +++ b/notes/wezterm-keybindings-session.md @@ -0,0 +1,95 @@ +# WezTerm-tangentbord: sessionsstatus (2026-08-28) + +Anteckning för att en ny (moln-)session ska kunna fortsätta där denna slutade. +Miljö: macOS, WezTerm 20240203-110809-5046fc22, tangentbordslayout "Swedish - Pro". +Config: `home/.config/wezterm/wezterm.lua`. + +## Vad som gjordes idag + +1. **Ny bindning `CMD+SHIFT+M`**: slår ihop den föregående (vänstra) fliken med + den aktiva som en pane-split i samma flik, via + `wezterm cli split-pane --pane-id --move-pane-id --horizontal` + (`--move-pane-id` finns i denna wezterm-version, verifierat med + `wezterm cli split-pane --help` och manuellt testat direkt via CLI - fungerar). + - Lua-callbacken använder `window:mux_window():tabs_with_info()` och + `tab:panes_with_info()` för att hitta föregående flikens aktiva pane-id. + - Ursprunglig version letade fel håll (nästa flik istället för föregående) och + misslyckades tyst på sista fliken - rättat. + - Visar nu en `toast_notification` om det inte finns någon flik att slå ihop med. + - **Status: bekräftat fungerande** ("Nu verkar det funka"). + +2. **Buggfix i en redan existerande bindning `CMD+SHIFT+C`** (helkopiering av + scrollback): `wezterm.action.CopyMode({ CopyTo = ... })` är fel syntax - + `CopyTo` är en egen toppnivå-action, inte en `CopyModeAssignment`-variant. + Orsakade ett fatalt config-laddningsfel (traceback i loggen). Rättat till + `wezterm.action.CopyTo("Clipboard")` (användaren ändrade sedan värdet själv + från `"ClipboardAndPrimarySelection"` till `"Clipboard"`). + +## Olöst: reload-tangenterna (CMD+R / CTRL+SHIFT+R / CMD+SHIFT+R) verkar inte fungera + +Användaren rapporterar att ingen av dessa reagerar: +- `CMD+SHIFT+R` - **är aldrig bunden till reload som standard** i WezTerm + (min ursprungliga instruktion om detta var fel). +- `CTRL+SHIFT+R` - default-bindning till `ReloadConfiguration`, borde fungera. +- `CMD+R` (`SUPER+r`) - default-bindning till `ReloadConfiguration`, borde fungera. + +### Redan uteslutet +- **Config-filen själv**: `wezterm show-keys --lua` visar att alla tre riktiga + bindningarna (`CTRL+R`, `SHIFT|CTRL+R`, `SUPER+r`) är korrekt registrerade mot + `ReloadConfiguration`, inga konflikterande overrides tidigare i listan. +- **Allmän interception av CTRL+SHIFT-kombon**: `CTRL+SHIFT+L` (debug overlay, + `ShowDebugOverlay`) fungerar och visas synligt - alltså tar WezTerm emot + CTRL+SHIFT-kombinationer generellt. Problemet är specifikt kopplat till + R-tangenten, inte till modifierartangenterna. +- **Kända remapping-verktyg**: inga processer för Karabiner-Elements, + Hammerspoon, BetterTouchTool, Rectangle, Alfred, Raycast, skhd hittades + körande (`ps aux`). +- **`hidutil` key remapping**: `hidutil property --get "UserKeyMapping"` -> null. +- **macOS systemgenvägar** (`defaults read com.apple.symbolichotkeys + AppleSymbolicHotKeys`): ingen post med `keyCode == 15` (R-tangentens + Carbon-keycode) hittades bland aktiva genvägar. +- **OpenSuperWhisper** (körande bakgrundsapp, diktering/Whisper-transkribering): + `~/Library/Preferences/ru.starmel.OpenSuperWhisper.plist` visar + `KeyboardShortcuts_toggleRecord` = Option + \` (grave-tangenten, + carbonKeyCode 50, carbonModifiers 2048=Option) och `modifierOnlyHotkey` = + `"rightOption"` (tryck/släpp höger Option ensam). Användaren bekräftade att + det är höger Option de faktiskt använder för att starta inspelning - inte R. + **Definitivt uteslutet.** +- **ChatGPT-skrivbordsappen** (körande, `com.openai.chat.plist`): + `toggleLauncher` = Option+Space, `toggleAttachedLauncher` = Option+Shift+1, + `openSettings` = Cmd+`,`. Ingen R-bindning. Sannolikt inte boven, men inte + testat genom att faktiskt stänga appen och pröva om. + +### Inte testat än / nästa steg +- **Stäng övriga körande appar en i taget och testa `CMD+R` mellan varje**: + NordVPN, Spotify, WhatsApp, Mail, Messages, Notes, Passwords, Safari, + Activity Monitor, TextEdit, Tips, System Settings. (Fullständig lista över + körande GUI-appar vid felsökningstillfället, hämtad via + `osascript -e 'tell application "System Events" to get name of every process + whose background only is false'`: Safari, ChatGPT, Terminal, Claude, Mail, + TextEdit, Activity Monitor, Notes, Messages, Passwords, Finder, NordVPN, + Spotify, WhatsApp, wezterm-gui, System Settings, OpenSuperWhisper, Tips.) +- **Starta om wezterm-gui med utökad loggning** (t.ex. `WEZTERM_LOG=trace`) + för att se om R-tangenttryckningen ens når WezTerm-processen. Kräver att + den körande instansen dödas och startas om, vilket stänger alla användarens + öppna flikar/paneler - måste godkännas av användaren innan det görs. +- Loggfilen för den aktiva processen (`~/.local/share/wezterm/wezterm-gui-log-.txt`, + hitta rätt pid via `ps aux | grep wezterm-gui`) visar bara ERROR-nivå som + standard - lyckade reloads syns inte där, bara Lua-fel. + +## Olöst: hur verifiera att en manuell reload faktiskt sker + +Föreslagen men ännu inte genomförd test: +1. Sätt temporärt `config.automatically_reload_config = false` i wezterm.lua + (så att sparning inte redan triggar auto-reload och maskerar resultatet). +2. Ändra något visuellt påtagligt, t.ex. `config.window_background_opacity` + till `0.3`. +3. Spara filen (inget ska hända ännu). +4. Tryck den tangentkombo som testas (t.ex. `CMD+R`). +5. Om bakgrunden blir mer genomskinlig fungerade den tangenten. Om inte - + den fastnar fortfarande. +6. Återställ `automatically_reload_config` (ta bort raden, standard är `true`) + och `window_background_opacity` till `0.8` efteråt. + +Användaren har inte bett mig genomföra detta än - fråga om de vill det, eller +om de redan testat manuellt. diff --git a/rebuild.sh b/rebuild.sh index b0b6bf18..40860790 100755 --- a/rebuild.sh +++ b/rebuild.sh @@ -2,4 +2,17 @@ set -euo pipefail DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" ln -sfn "$DIR" ~/.dotfiles -exec sudo darwin-rebuild switch --flake ~/.dotfiles#mac +# sudo resets PATH to a secure default that excludes /run/current-system/sw/bin, +# so a bare `darwin-rebuild` would not be found under sudo even when it resolves +# here. Resolve the absolute path first and invoke that instead. Fall back to +# the stable nix-darwin system-profile location when darwin-rebuild isn't on +# PATH at all (e.g. /run/current-system/sw/bin was never added to PATH). +if DARWIN_REBUILD="$(command -v darwin-rebuild 2>/dev/null)"; then + : +elif [ -x /run/current-system/sw/bin/darwin-rebuild ]; then + DARWIN_REBUILD=/run/current-system/sw/bin/darwin-rebuild +else + echo "error: darwin-rebuild not found on PATH or at /run/current-system/sw/bin/darwin-rebuild" >&2 + exit 1 +fi +exec sudo "$DARWIN_REBUILD" switch --flake ~/.dotfiles#mac diff --git a/templates/HANDOFF.md.template b/templates/HANDOFF.md.template new file mode 100644 index 00000000..f43d61ea --- /dev/null +++ b/templates/HANDOFF.md.template @@ -0,0 +1,86 @@ + + +# — Implementation Plan / Handoff + +## SESSION STATUS — read this first if you're a new session picking this up + +**Working directory / repo**: where the code actually lives, if it's not +obvious from where this file sits. + +**Live infra already set up** (don't recreate): databases, deployed +services, API keys/where they live, third-party project refs. Anything +a fresh session might otherwise try to re-provision from scratch. + +**Status by phase/milestone** (✅ done / 🔶 partial / ⬜ not started): +- ✅ Phase 0 — one line on what "done" means here and how it was verified. +- 🔶 Phase 1 — what's built, what's still missing, in one or two lines. + +**In-progress work not yet on the main branch** (if any): branch name, +what's on it, why it's deliberately unmerged (e.g. "needs a human to +spot-check real content before it ships"), and what needs to happen for +it to merge. Delete this subsection if everything is already on main. + +**Known blockers / lessons learned**: anything that failed once and +would fail again if repeated the same way. State the failure and the +fix as a rule for next time, not just a narrative - e.g. "don't run N +generation batches in parallel, they share a rate limit; run them +sequentially instead." This is the section that saves the next session +from re-discovering the same problem the hard way. + +**Next up**: the next concrete action, not a vague "continue development." + +## Original objective (verbatim, if there was one) + +Paste the user's original ask here unedited, if the project started from +a specific request. Keeps later sessions honest about what was actually +asked for vs. what's been added since. + +## Context / decisions log + +The "why" behind non-obvious choices - tech stack picks, things ruled +out and why, constraints that shaped the design. This is what stops a +future session from re-litigating a decision that was already made +deliberately. + +## Architecture / data model + +Whatever a new session needs to orient itself in the code: key +directories, data model, how the pieces fit together. Skip anything +that's already obvious from the code itself - this section is for +things that aren't. + +## Phased build order + +The plan, broken into verifiable phases. Each phase should have a +concrete "verify" step - not "it's done" but "here's how you'd confirm +it's done." + +## Risks to keep in mind + +Standing risks that don't go away when a phase is marked done - cost +implications, things that are "not a bug, expected behavior," fragile +external dependencies, anything a future session might mistake for a +bug and try to "fix." diff --git a/tools.sh b/tools.sh new file mode 100755 index 00000000..67c86693 --- /dev/null +++ b/tools.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Installs the optional kunchenguid agentic-engineering toolkit. +# None of these are Homebrew formulae/casks, so `rebuild.sh` can't bring them +# back on a new machine - this script exists so they survive a move. +# Safe to re-run: every step skips work that's already done. + +install_if_missing() { + local name="$1" check="$2" install_cmd="$3" + if eval "$check" >/dev/null 2>&1; then + echo "$name already installed, skipping" + else + echo "Installing $name..." + eval "$install_cmd" + fi +} + +install_if_missing "treehouse" "command -v treehouse" \ + "curl -fsSL https://kunchenguid.github.io/treehouse/install.sh | sh" + +install_if_missing "no-mistakes" "command -v no-mistakes" \ + "curl -fsSL https://raw.githubusercontent.com/kunchenguid/no-mistakes/main/docs/install.sh | sh" + +install_if_missing "gnhf" "command -v gnhf" \ + "npm install -g gnhf" + +install_if_missing "lavish-axi (lavish)" "command -v lavish-axi" \ + "npm install -g lavish-axi" + +install_if_missing "gh-axi" "command -v gh-axi" \ + "npm install -g gh-axi" + +FIRSTMATE_DIR="$HOME/dev/firstmate" +if [ -d "$FIRSTMATE_DIR" ]; then + echo "firstmate already cloned at $FIRSTMATE_DIR, skipping" +else + echo "Cloning firstmate into $FIRSTMATE_DIR..." + mkdir -p "$HOME/dev" + git clone https://github.com/kunchenguid/firstmate "$FIRSTMATE_DIR" +fi + +echo "" +echo "Done. firstmate is cloned but not started - cd into $FIRSTMATE_DIR and" +echo "run your agent CLI (e.g. \`claude\`) there when you're ready to use it." diff --git a/treehouse.toml b/treehouse.toml new file mode 100644 index 00000000..60cf6f16 --- /dev/null +++ b/treehouse.toml @@ -0,0 +1,8 @@ +max_trees = 16 +root = "." + +# Worktree root directory (relative to repo root or absolute path). +# Worktrees are placed under {root}/.treehouse/. Default: $HOME +# Override per-command with the --root flag or the TREEHOUSE_ROOT env var. +# Use "." to keep the pool in-project at /.treehouse/, next to the +# code and removed with the project. Example: root = "."