#!/usr/bin/env nu # Private dev-internal overlay manager. # # Tracks the gitignored .coder/ and .claude/ of a project in a SEPARATE # "bare-overlay" repo: its git-dir lives apart (/.internal.git, itself # gitignored), its work-tree is the project root, and it tracks only the two # private dirs. The public repo and the overlay coexist on one working tree # without colliding (distinct git-dirs). The overlay pushes ONLY to a private # remote and is NEVER mirrored — same boundary as vault (ADR-062). # # Generic: works for any project using the same .coder/.claude layout. # # Usage: # nu internal-overlay.nu setup [--remote ] [--push] # nu internal-overlay.nu onboard --remote # nu internal-overlay.nu run -- # nu internal-overlay.nu status # Absolute path of the overlay's bare git-dir for a given work-tree. def overlay-gitdir [wt: path] { $wt | path join ".internal.git" } # The git CLI prefix (cwd into the work-tree, pin git-dir + work-tree). def git-prefix [wt: path] { ["-C" $wt "--git-dir" (overlay-gitdir $wt) "--work-tree" $wt] } # Resolve the overlay work-tree for daily commands (commit/run/status), in order: # 1. explicit --worktree flag # 2. $env.ONTOREF_INTERNAL_ROOT (for agents in a jj workspace — anchors the # overlay to the MAIN checkout, since ignored .coder/.claude are NOT # materialized in a fresh workspace) # 3. walk up from $env.PWD to the nearest .internal.git # The overlay must already exist (setup/onboard create it). def resolve-wt [given: string] { if ($given | is-not-empty) { return ($given | path expand) } let er = ($env.ONTOREF_INTERNAL_ROOT? | default "") if ($er | is-not-empty) { let p = ($er | path expand) if not (overlay-gitdir $p | path exists) { print --stderr $"ONTOREF_INTERNAL_ROOT=($p) but no overlay at (overlay-gitdir $p)" exit 1 } return $p } mut d = $env.PWD while $d != "/" { if (overlay-gitdir $d | path exists) { return $d } $d = ($d | path dirname) } print --stderr "no overlay: pass --worktree, set ONTOREF_INTERNAL_ROOT, or cd into an overlaid project" exit 1 } def main [] { print "internal-overlay — private dev-internal overlay (.coder/.claude), never mirrored" print "" print " setup [--remote ] [--push] create overlay, capture current .coder/.claude" print " onboard --remote clone the overlay into an existing checkout" print " commit [--worktree ] force-add .coder/.claude and commit" print " run \"\" [--worktree ] run any git command against the overlay" print " status [--worktree ] short status of the overlay" print "" print " commit/run/status resolve the work-tree as: --worktree -> \$ONTOREF_INTERNAL_ROOT -> walk up from cwd." print " In a jj agent workspace, set ONTOREF_INTERNAL_ROOT to the MAIN checkout (ignored .coder/.claude" print " are not materialized in a fresh workspace). The overlay never gets a jj/rad/github remote." } def "main setup" [ worktree: path # project/member root to overlay --remote: string = "" # forgejo PRIVATE url (optional) --push # push after commit (default: you push) ...paths: string # dirs to track (default: .coder .claude) ] { let paths = (if ($paths | is-empty) { [".coder" ".claude"] } else { $paths }) let wt = ($worktree | path expand) let gd = (overlay-gitdir $wt) if not ($wt | path exists) { print --stderr $"work-tree does not exist: ($wt)" exit 1 } if ($gd | path exists) { print --stderr $"overlay already exists at ($gd) — use 'run' or 'status'" exit 1 } # PRE-1 privacy: refuse if the dirs are already tracked in the PUBLIC repo — # they would otherwise reach the public mirror through that history. let pub = (^git -C $wt ls-files | complete) if $pub.exit_code == 0 { let leaked = ($pub.stdout | lines | where ($it =~ '^\.(coder|claude)/')) if ($leaked | length) > 0 { print --stderr $"PRE-1 FAILED: ($leaked | length) files under .coder/.claude are TRACKED in the public repo and would be mirrored." print --stderr $" fix: git -C ($wt) rm -r --cached .coder .claude then scrub history before creating the overlay." exit 1 } } # PRE-2 privacy: the public repo must ignore the private dirs AND the overlay git-dir. let gi = ($wt | path join ".gitignore") let want = [".coder/" ".claude/" ".internal.git/"] let have = (if ($gi | path exists) { open --raw $gi | lines } else { [] }) let missing = ($want | where ($it not-in $have)) if ($missing | length) > 0 { $"(char nl)($missing | str join (char nl))(char nl)" | save --append --raw $gi print $"PRE-2: added to .gitignore -> ($missing | str join ', ')" } let pre = (git-prefix $wt) ^git init --bare $gd ^git ...($pre ++ ["config" "status.showUntrackedFiles" "no"]) # -f is required: the work-tree's .gitignore ignores these dirs (PRE-2), and the # overlay's whole purpose is to track exactly those ignored paths. ^git ...($pre ++ ["add" "-f"] ++ $paths) ^git ...($pre ++ ["commit" "-m" "internal: dev-internal overlay (.coder + .claude)"]) if ($remote | is-not-empty) { ^git ...($pre ++ ["remote" "add" "forgejo-priv" $remote]) if $push { ^git ...($pre ++ ["push" "-u" "forgejo-priv" "HEAD"]) } else { print $"remote 'forgejo-priv' set. Push yourself: nu internal-overlay.nu run ($wt) -- push -u forgejo-priv HEAD" } } else { print $"overlay created at ($gd). Add a private remote later: nu internal-overlay.nu run ($wt) -- remote add forgejo-priv " } } def "main onboard" [ worktree: path # existing checkout of the PUBLIC repo --remote: string = "" # private overlay url (required) ] { let wt = ($worktree | path expand) let gd = (overlay-gitdir $wt) if ($remote | is-empty) { print --stderr "--remote is required for onboard" exit 1 } if ($gd | path exists) { print --stderr $"overlay git-dir already present at ($gd)" exit 1 } ^git clone --bare $remote $gd let pre = (git-prefix $wt) # a bare clone leaves core.bare=true and drops local config — restore both. ^git ...($pre ++ ["config" "core.bare" "false"]) ^git ...($pre ++ ["config" "status.showUntrackedFiles" "no"]) ^git ...($pre ++ ["checkout" "-f"]) print $"onboarded: .coder/.claude materialized into ($wt)" } def "main commit" [ message: string # commit message --worktree: string = "" # override (else env / walk-up) ...paths: string # tracked dirs (default: .coder .claude) ] { let paths = (if ($paths | is-empty) { [".coder" ".claude"] } else { $paths }) let wt = (resolve-wt $worktree) let pre = (git-prefix $wt) # -f bypasses the work-tree .gitignore that hides these dirs from the public repo. ^git ...($pre ++ ["add" "-f"] ++ $paths) ^git ...($pre ++ ["commit" "-m" $message]) } def "main run" [ git_command: string # quoted git args, e.g. "push -u forgejo-priv HEAD" --worktree: string = "" # override (else env / walk-up) ] { let wt = (resolve-wt $worktree) let parts = ($git_command | split row " " | where ($it | is-not-empty)) ^git ...((git-prefix $wt) ++ $parts) } def "main status" [ --worktree: string = "" # override (else env / walk-up) ] { let wt = (resolve-wt $worktree) ^git ...((git-prefix $wt) ++ ["status" "--short" "--branch"]) }