Make Treemon own worktree forking with a post-fork setup hook#85
Conversation
Treemon now creates worktrees itself instead of delegating forking to repo fork scripts. Base resolution is upstream-aware: it fetches the base branch and prefers the remote-tracking ref (<upstream>/<base>) over a possibly-stale local branch, falling back to the local branch when no remote-tracking ref exists. This fixes "new worktree from main" forking from a stale local main in fork workflows, and removes the prior bugs where a fork script silently ignored the selected base or failed resolution it never used. After creation, an optional post-fork.ps1/.sh runs inside the new worktree for setup only (symlinks, dependency install); its failure is a non-fatal warning since the worktree already exists. Legacy fork.ps1/.sh are no longer executed. If one is present, creation still succeeds but returns a warning to migrate setup into post-fork.*. createWorktree now returns Result<string list, string>, with warnings surfaced in the create modal (UI) and on the console (CLI). Migrate Treemon's own fork.ps1 into post-fork.ps1 (setup half) and review-worktree.ps1 (the remote review-branch workflow, which reuses post-fork.ps1), and delete fork.ps1.
- post-fork.ps1: create directory junctions instead of symlinks with UAC elevation, so the hook works when spawned by the server (no interactive prompt) and without Developer Mode. - GitWorktree: reject leading-dash branch names (align regex with TreemonConfig) and pass "--" before the fetch refspec to prevent git argument injection. - ProcessRunner: add runResultWithTimeout; give the post-fork hook a 10-minute budget so npm install / bd init are not killed by the 60s git default, and make the timeout warning state that dependencies may be incomplete. - Tests: extract shared git helpers into GitTestHelpers.fs and dedupe CreateWorktreeServerTests and RemoveWorktreeTests. - CreateWorktreeModal: flatten update into a single (msg, modal) tuple match. - Make legacyForkScriptWarning pure by hoisting File.Exists to the CE boundary. - Name the createWorktree success channel via CreateWorktreeWarnings.
There was a problem hiding this comment.
Pull request overview
This PR makes Treemon own git worktree forking instead of delegating to a repo-local fork.ps1/fork.sh. Treemon now fetches the base branch from the upstream remote and forks the new worktree directly via git worktree add -b {name} {parentDir}/tm-{name} {baseRef}, preferring the remote-tracking ref ({remote}/{base}) over a possibly-stale local branch. After creation it runs an optional, non-fatal post-fork.ps1/post-fork.sh setup hook inside the new worktree. Legacy fork scripts are no longer executed; their presence yields a migration warning. Warnings now flow through createWorktree (Result<CreateWorktreeWarnings, string>) to both the create modal and the CLI.
Changes:
- Server-side, upstream-aware forking with
resolveBaseRefprecedence, an optionalpost-forkhook (10-min timeout), and non-fatal warnings; branch-name regex tightened to reject leading dashes and--added togit fetchrefspecs. - API contract change:
createWorktreereturnsResult<CreateWorktreeWarnings, string>; modal/CLI updated, modalupdateflattened to a(msg, modal)tuple match with a newCreateWarningstate and styling. fork.ps1removed and split intopost-fork.ps1(junction-based setup) andreview-worktree.ps1; shared git test helpers extracted toGitTestHelpers.fs; newCreateWorktreeServerTests; spec updated.
Show a summary per file
| File | Description |
|---|---|
| src/Server/GitWorktree.fs | Core change: base-ref resolution, fetch hardening, post-fork hook, warning assembly |
| src/Server/WorktreeApi.fs | Wires createWorktree to new signature; returns warnings; fixture/read-only modes updated |
| src/Server/ProcessRunner.fs | Parameterizes timeout; adds runResultWithTimeout and shared toResult |
| src/Shared/Types.fs | Adds CreateWorktreeWarnings alias; updates createWorktree return type |
| src/Client/CreateWorktreeModal.fs | Flattens update to tuple match; adds CreateWarning state and view |
| src/Client/index.html | Adds warning header/message CSS classes |
| src/Cli/Program.fs | new command prints warnings to stderr via tryCallServer |
| post-fork.ps1 | New junction-based setup hook (no elevation needed) |
| review-worktree.ps1 | New manual remote-review-branch workflow reusing post-fork |
| fork.ps1 | Deleted (legacy symlink/elevation flow) |
| src/Tests/GitTestHelpers.fs | Extracted shared git repo test helpers |
| src/Tests/CreateWorktreeServerTests.fs | New end-to-end + unit tests for base-ref precedence and creation |
| src/Tests/CreateWorktreeTests.fs | Updates for Ok [] and new warning case |
| src/Tests/RemoveWorktreeTests.fs | Dedupes git helpers into GitTestHelpers |
| src/Tests/Tests.fsproj | Registers new test/helper files |
| docs/spec/worktree-monitor.md | Documents the new create flow |
Copilot's findings
- Files reviewed: 16/16 changed files
- Comments generated: 2
| member _.``runs the post-fork script inside the new worktree``() = | ||
| if not (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) then | ||
| Assert.Ignore("post-fork execution test targets Windows/pwsh") |
There was a problem hiding this comment.
Good catch. In 82c8194 the test now writes post-fork.ps1 on Windows and post-fork.sh (invoked via bash) elsewhere, so it runs on ubuntu-latest instead of being Assert.Ignored. Also added a companion test for the post-fork failure-to-warning path.
- CreateWorktreeModal: render warning messages with List.map instead of a for-comprehension, matching the branch dropdown and the no-loops style rule. - CreateWorktreeServerTests: make the post-fork execution test portable so it runs on Linux CI via post-fork.sh/bash (was Assert.Ignore'd off Windows), and add a test for the post-fork failure-to-warning path.
Problem
Creating a worktree from the dashboard used to delegate forking to a repo-local
fork.ps1/fork.sh, passing only the branch name. That path had real bugs:main" could fork from a stale localmainin fork workflows where the upstream is ahead.Changes
Treemon owns forking (upstream-aware base)
git worktree add -b {name} {parentDir}/tm-{name} {baseRef}.{remote}/{base}over a possibly-stale local branch, falling back to the local{base}when no remote-tracking ref exists. No worktree needs the base checked out; fetch/remote failures fall back to whatever ref is available.post-forksetup hookpost-fork.ps1(Windows) /post-fork.sh(Unix) runs inside the new worktree, receiving{worktreePath} {sourceRepoRoot} {baseRef} {branchName}. It is for setup only (links, dependency install); a failure is a non-fatal warning since the worktree already exists.post-fork.ps1wires.claude/datavia directory junctions (no UAC elevation or Developer Mode required — important because the server spawns the hook) and runsbd init+npm install.fork.ps1/fork.share no longer executed. If one is present, creation still succeeds but returns a warning to migrate setup intopost-fork.*. Treemon'sfork.ps1is deleted and split intopost-fork.ps1(setup) andreview-worktree.ps1(the remote review-branch workflow, which reusespost-fork.ps1).Warnings surfaced to UI + CLI
createWorktreereturnsResult<CreateWorktreeWarnings, string>(a namedstring listalias). Warnings render in the create modal (new warning header/message styles) and on the CLI console.Hardening
TreemonConfig) and pass--before thegit fetchrefspec to prevent git argument injection.ProcessRunner.runResultWithTimeoutgives the post-fork hook a 10-minute budget sonpm install/bd initare not killed by the 60s default used for quick git probes; the timeout warning now states dependencies may be incomplete.Code quality
CreateWorktreeModal.updateflattened into a single(msg, modal)tuple match.legacyForkScriptWarningmade pure —File.Existshoisted to theasyncResultboundary.GitTestHelpers.fs;RemoveWorktreeTestsandCreateWorktreeServerTestsdeduped.docs/spec/worktree-monitor.md) updated to describe the new create flow.Tests
CreateWorktreeServerTestscover base-ref precedence and end-to-end create against real git repos: forks from the upstream tip over a stale local base, remote-only base, missing base (errors and creates nothing), legacy-script warning, and post-fork execution inside the new worktree.