#!/usr/bin/env bash
#clear
#export PS4='\e[90m+${LINENO} in ${#BASH_SOURCE[@]}>${FUNCNAME[0]}:${BASH_SOURCE[@]##*/} \e[0m'
#set -x

#echo "starting: $0 <LOG_LEVEL=$1>"

# Cross-platform sed helper for first-match replacement
# BSD sed (macOS) doesn't support GNU's 0,/pattern/ syntax
# Uses perl which is available on both platforms
private.oo.sed.first() {
  local pattern="$1"
  local replacement="$2"
  local file="$3"
  # Escape special chars in replacement for perl
  local escapedReplacement=$(printf '%s' "$replacement" | sed 's/[&/\]/\\&/g')
  perl -i -pe "s/\Q${pattern}\E/${escapedReplacement}/ && (\$done = 1) unless \$done" "$file"
}

oo.deinstall() { # # deinstall oosh and clean leftover configurations
  problem.log "DEINSTALL OOSH: are you sure?  continue with by typing 'c'"
  cp $OOSH_DIR/init/oosh $HOME/install.oosh
  rm -Rf $HOME/oosh
  rm -Rf $HOME/config
  rm $HOME/.once
  rm $HOME/.bashrc
  mv $HOME/.bashrc.bak.without.completion $HOME/.bashrc
  clear
  echo "reinstall with: install.oosh"
}

oo.new() # <newClassName> # creates a new script that acts as a oo class with completion
{
  if [ -n "$1" ]; then
    info.log "oosh in $OOSH_DIR"
    cd $OOSH_DIR
    cat ./templates/code/newScript | sed 's/newScript/'"$1"'/' >./$1
    chmod +x ./$1
    success.log "created oo script $1"
    console.log "to enable completion on the new command, type

    reconfigure"

    private.oo.new.test "$1"

  else
    warn.log "usage: oo new filename
    
    please specify a filename"
    return 1
  fi
}

oo.test.new() # <newClassName> # creates a new test script for the class newClassName
{ private.oo.new.test "$@"; }

private.oo.new.test() # <newClassName> # creates a new test script for the class newClassName
{
  if [ -n "$1" ]; then
    info.log "oosh in $OOSH_DIR"

    cd $OOSH_DIR/test
    cat ./../templates/code/newScriptTest | sed 's/newScript/'"$1"'/' > test.$1
    chmod +x ./test.$1
    success.log "created test script test.$1"

  else
    warn.log "usage: oo test.new filename

    please specify a filename"
    return 1
  fi
}

oo.method.new() # <newScript.newMethod> # creates a new method in the script newScript
{ private.oo.new.method "$@"; }

private.oo.new.method()   # <newScript.newMethod> # creates a new method in the script newScript
# this is an higly interactive command
{
  local name="$1"
  if [ -z "$name" ]; then
    error.log "No parameter provided."
    return 1
  fi
  local newScript=$(echo $name | cut -d. -f1)
  local newMethod=$(echo $name | cut -d. -f2)
  
  if [ -z "$newMethod" ]; then
    warn.log "check if the parameter contains a ."
    error.lgo "no method name in: $name:"
    return 2
  fi
  console.log "
  command: $newScript
  method : $newMethod
  "

  if check file $OOSH_DIR/$newScript exists fix error.log "$OOSH_DIR/$newScript does not exist"
  then
    local tweakedMethod=$(cat $OOSH_DIR/templates/code/newMethod | sed 's/newScript/'"$newScript"'/' | sed 's/newMethod/'"$newMethod"'/')
    local replaceText="### new.method"

    replace within "$OOSH_DIR/$newScript" "$replaceText" by "$tweakedMethod"


    local replaceNameDesc="      ----      --------------------------"
    local newNameDesc="      ----      --------------------------
      ----      --------------------------"

    replace within "$OOSH_DIR/$newScript.new" "$replaceNameDesc" by "$newNameDesc"

    private.oo.sed.first "----" "$newMethod" "$OOSH_DIR/$newScript.new"

    local descriptParameter
    echo "A short description and Parameter(s) for method" $newMethod "of" $newScript": "
    read descriptParameter
    echo "$descriptParameter"

    private.oo.sed.first "--------------------------" "$descriptParameter" "$OOSH_DIR/$newScript.new"

    replace commit "$OOSH_DIR/$newScript"

    replace cleanup "$OOSH_DIR/$newScript"

    success.log "created oo new method in script $1"
    
  fi

  if check file $OOSH_DIR/test/test.$newScript exists fix error.log "$OOSH_DIR/test/test.$newScript does not exist"
  then
    local tweakedTestMethod=$(cat $OOSH_DIR/templates/code/newMethodTest | sed 's/newScript/'"$newScript"'/' | sed 's/newMethod/'"$newMethod"'/')
    local replaceTestText="### test.method"

    replace within "$OOSH_DIR/test/test.$newScript" "$replaceTestText" by "$tweakedTestMethod"

    local testDescription
    echo "Give a short description of the test" $newMethod "of script" $newScript": "
    read testDescription
    echo "$testDescription"

    private.oo.sed.first "testDescription" "$testDescription" "$OOSH_DIR/test/test.$newScript.new"

    local testParameter
    echo "Give the parameters for test" $newMethod "of script" $newScript": "
    read testParameter
    echo "$testParameter"

    private.oo.sed.first "newArguments" "$testParameter" "$OOSH_DIR/test/test.$newScript.new"

    local testExpect
    echo "What to expect of test" $newMethod "of script" $newScript": "
    read testExpect
    echo "$testExpect"

    private.oo.sed.first "new_expect" "$testExpect" "$OOSH_DIR/test/test.$newScript.new"
    
    replace commit "$OOSH_DIR/test/test.$newScript"

    replace cleanup "$OOSH_DIR/test/test.$newScript"

    success.log "created new test for method $1"
    
  fi

}



oo.commit() # <?mode:force> # commits latest changes to the oosh environment and publishes them.
{
  cd $OOSH_DIR

  local branch=$( git branch | line find "\*" )
  console.log "git branch is: $branch"
  if [ "$branch" = "* dev" ] || [ "$1" = "force" ]; then
    rm *\:
    git add *
    git commit
    git push
  else
    error.log "not on the dev branch"
    console.log "switch branch with
   
    oo mode.dev
    
    "
  fi
}

oo.update() # # updates oosh environment and pulls latest changes from github.
{
  cd "$OOSH_DIR" || return 1
  local pulled=0
  if git pull; then
    pulled=1
  else
    # SSH-based pull failed. The shared clone at oo:1332 uses the 2cuGitHub
    # SSH alias as its remote, which requires that alias to be present in the
    # current user's ~/.ssh/config. On users where user.oosh.install couldn't
    # seed the shared .ssh/ (e.g. fresh macOS install where developking's
    # template wasn't fully laid out yet) the alias is missing and git pull
    # can't resolve the host. Fall back to the same HTTPS URL init/oosh uses
    # for the initial clone — read-only, no auth needed for a public repo.
    # The configured remote is not changed, so future pushes keep using SSH.
    local fallbackUrl="${OOSH_REPO:-https://github.com/Cerulean-Circle-GmbH/once.sh.git}"
    local branch
    branch=$(this.git.branch.short "$OOSH_DIR" 2>/dev/null)
    [ -z "$branch" ] && branch=$(git -C "$OOSH_DIR" symbolic-ref --short HEAD 2>/dev/null)
    [ -z "$branch" ] && branch="dev"
    warn.log "git pull (SSH remote) failed; trying HTTPS fallback: $fallbackUrl $branch"
    if git pull "$fallbackUrl" "$branch"; then
      important.log "oo update succeeded via HTTPS fallback (configured remote unchanged)"
      pulled=1
    fi
  fi
  if [ "$pulled" -ne 1 ]; then
    error.log "oo update failed via both SSH remote and HTTPS fallback"
    return 1
  fi
  # Self-heal: rewire ~/config + ~/oosh symlinks via config.init.user.
  # Catches the case where init/oosh was re-run out of band and clobbered
  # them (the macstudio-root regression). Also runs during the normal
  # install pipeline: ossh.install.continue.local calls `oo update` at
  # ossh:666 before the state machine, so a re-install at SETUP_SERVER
  # state 99 still heals here even though the state machine no-ops.
  # Idempotent — config.init.user (config:212) no-ops when symlinks are
  # already canonical. Explicit user action (NOT shell startup), so it
  # stays on the safe side of the May-8 sudo-chain anti-pattern.
  private.oo.update.heal.symlinks
  return 0
}

private.oo.update.heal.symlinks() # # heal current user's ~/config + ~/oosh symlinks via config.init.user; idempotent
{
  [ "$(type -t config.init.user)" = "function" ] || source "$OOSH_DIR/config" >/dev/null 2>&1
  type config.init.user >/dev/null 2>&1 || return 0
  # Skip silently when developking doesn't exist yet — this is the
  # pre-install state (oo update fires very early in
  # ossh.install.continue.local at ossh:666, BEFORE state 31 creates
  # developking + the shared tree). The state machine will link
  # everything correctly when it gets there; healing post-install
  # drift is the heal's job, not pre-install bootstrap.
  [ "$(type -t user.get)" = "function" ] || source "$OOSH_DIR/user" >/dev/null 2>&1
  if type user.get >/dev/null 2>&1; then
    local _devHome
    _devHome=$(user.get home developking 2>/dev/null)
    [ -z "$_devHome" ] && return 0
  fi
  config.init.user "$USER" >/dev/null \
    || warn.log "oo update: \`config init.user $USER\` reported an issue — run \`oo user.fix\` for details"
}

oo.user.fix() # <username:$USER> # repair this user's ~/config + ~/oosh symlinks to the canonical shared tree; idempotent
{
  # Discoverable user-facing alias for config.init.user — the canonical
  # OOSH explicit-repair primitive (config:212). Handles real-dir →
  # symlink conversion (preserves originals as oosh.orig.<ts> /
  # config.orig.<ts>), ownership, branch detection, dev-group membership.
  # Naming follows the OOSH `noun.verb` convention and the per-scope
  # `ossh.rights.fix` / `ossh.folder.fix` pattern.
  [ "$(type -t config.init.user)" = "function" ] || source "$OOSH_DIR/config" >/dev/null 2>&1
  config.init.user "$@"
}
oo.user.fix.completion.username() {
  # Source `user` on demand so completion works in fresh-shell contexts
  # where the dispatcher hasn't yet auto-loaded the script. Mirrors the
  # guard pattern in private.oo.update.heal.symlinks and oo.user.fix.
  [ "$(type -t user.list)" = "function" ] || source "$OOSH_DIR/user" >/dev/null 2>&1
  user.list
}

oo.safeDirectory.prune() # # remove git safe.directory entries whose paths no longer exist on disk
{
  # Explicit, idempotent repair primitive. `git config --global
  # safe.directory` accumulates entries from install passes, `oo mode`
  # iterations on temp fixtures, and dev-group worktree adds. When the
  # list grows large (especially with stale /tmp/* paths) the Cursor /
  # VS Code Git extension can hang while validating each entry, leaving
  # the Source Control panel and branch picker empty. This primitive
  # walks the list, drops entries whose paths no longer exist, and
  # leaves real paths untouched. Honours $GIT_CONFIG_GLOBAL so tests
  # can sandbox without touching the user's real ~/.gitconfig.
  local entry pruned=0
  local -a kept=()

  while IFS= read -r entry; do
    [ -z "$entry" ] && continue
    if [ -e "$entry" ]; then
      kept+=("$entry")
    else
      pruned=$((pruned + 1))
    fi
  done < <(git config --global --get-all safe.directory 2>/dev/null)

  if [ "$pruned" -gt 0 ]; then
    git config --global --unset-all safe.directory 2>/dev/null
    local k
    for k in "${kept[@]}"; do
      git config --global --add safe.directory "$k"
    done
    console.log "Pruned $pruned stale safe.directory entries; ${#kept[@]} preserved"
  else
    info.log "No stale safe.directory entries to prune (${#kept[@]} present)"
  fi

  create.result 0 "pruned=$pruned kept=${#kept[@]}"
  return $(result)
}

private.oo.safeDirectory.add() # <path> # idempotently add path to git's global safe.directory list (no-op if already present)
{
  # Private helper used by install/setup paths that must declare a
  # shared worktree as trusted (`/home/shared/.../Once.sh/{main,dev,prod}`
  # is owned by developking:dev — other dev-group users hit dubious-
  # ownership without an explicit safe.directory entry). Calling
  # `git config --global --add safe.directory` blindly on every
  # (re)install accumulates duplicates; this helper short-circuits
  # when the exact path is already present.
  #
  # Uses `grep -qFx` (fixed string, full-line match) so that a path
  # which is a substring of another doesn't false-positive. Honours
  # $GIT_CONFIG_GLOBAL so tests can sandbox.
  local path="$1"
  if [ -z "$path" ]; then
    error.log "private.oo.safeDirectory.add: <path> required"
    create.result 1 "path required"
    return $(result)
  fi
  if git config --global --get-all safe.directory 2>/dev/null | grep -qFx "$path"; then
    create.result 0 "already present: $path"
  else
    git config --global --add safe.directory "$path"
    create.result 0 "added: $path"
  fi
  return $(result)
}

oo.remote.update() { # <sshConfigHost> # updates the oosh environment on <sshConfigHost>
  local sshConfigHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no sshConfigHost was specified"
    return 1
  fi
  ossh exec "$sshConfigHost" "oo update"
}
oo.remote.update.completion() {
  ossh config.get.completion
}

oo.stage() # <stage> # promote a stage forward (dev → testing, testing → prod)
{
  local stage="$1"
  if [ -z "$stage" ]; then
    error.log "Usage: oo stage <stage>"
    error.log "  dev      → promote dev to testing"
    error.log "  testing  → promote testing to prod"
    return 1
  fi
  shift

  case "$stage" in
    dev)     promote dev.to.testing "$@" ;;
    testing) promote testing.to.prod "$@" ;;
    *)       error.log "Unknown stage: $stage (expected: dev, testing)"; return 1 ;;
  esac
}

oo.mode.stage() # <stage> # promote a stage forward (dev → testing, testing → prod)
{ oo.stage "$@"; }

oo.release() # <?reset|yes|skip> # [deprecated: use oo stage dev] promote dev to testing
{ oo.stage dev "$@"; }

oo.testing.to.prod() # <?reset|yes|skip> # [deprecated: use oo stage testing] promote testing to prod
{ oo.stage testing "$@"; }

oo.dev.to.testing() # <?reset|yes|skip> # [deprecated: use oo stage dev] promote dev to testing
{ oo.stage dev "$@"; }

private.oo.dev.to.testing() # <?reset|yes|skip> # promote dev to testing, gated by core tests
{ oo.stage dev "$@"; }

oo.promote.status() # # show promotion pipeline state
{ private.oo.promote.status; }

private.oo.promote.status() # # show promotion pipeline state
{ promote status; }

oo.promote.report() # # show promotion history
{ private.oo.promote.report; }

private.oo.promote.report() # # show promotion history
{ promote report; }


oo.mode.base.get() # # returns the OOSH components base directory
{
  # Strategy 1: Explicit config (set by oo mode.base.set or oo mode.setup)
  if [ -n "$OOSH_COMPONENTS_DIR" ] && [ -d "$OOSH_COMPONENTS_DIR" ]; then
    echo "$OOSH_COMPONENTS_DIR"
    return 0
  fi

  # Strategy 2: Use git worktree list to find main worktree, derive base as its parent
  if [ -d "$OOSH_DIR/.git" ] || [ -f "$OOSH_DIR/.git" ]; then
    local mainWt
    mainWt=$(git -C "$OOSH_DIR" worktree list --porcelain 2>/dev/null | head -1 | sed 's/^worktree //')
    if [ -n "$mainWt" ]; then
      local mainBase
      mainBase=$(basename "$mainWt")
      if [ "$mainBase" = "main" ]; then
        echo "$(dirname "$mainWt")"
        return 0
      fi
    fi
  fi

  # Strategy 3: Check if OOSH_DIR is inside a worktree (.git file with gitdir: pointer)
  if [ -f "$OOSH_DIR/.git" ]; then
    local parent
    parent=$(dirname "$OOSH_DIR")
    if [ -d "$parent/main" ]; then
      echo "$parent"
      return 0
    fi
  fi

  # Strategy 4: Fall back to dirname if sibling "main" directory exists
  if [ -n "$OOSH_DIR" ]; then
    local parent
    parent=$(dirname "$OOSH_DIR")
    local selfName
    selfName=$(basename "$OOSH_DIR")
    if [ "$selfName" = "main" ] && [ -d "$OOSH_DIR/.git" ]; then
      echo "$parent"
      return 0
    fi
    if [ -d "$parent/main" ] && [ "$parent/main" != "$OOSH_DIR" ]; then
      echo "$parent"
      return 0
    fi
  fi

  # No worktree structure detected
  return 1
}

oo.mode.base.set() # <path> # set and persist the OOSH components base directory
{
  local path="$1"
  if [ -z "$path" ]; then
    error.log "Usage: oo mode.base.set <path>"
    return 1
  fi
  if [ ! -d "$path" ]; then
    error.log "Directory not found: $path"
    return 1
  fi
  export OOSH_COMPONENTS_DIR="$path"
  config save oosh OOSH
  console.log "Base set to: $path"
}

oo.mode.list() # # list available branches with git status
{
  local base
  base=$(oo.mode.base.get) || {
    error.log "No worktree structure detected. Run 'oo mode.setup' to convert your install."
    return 1
  }
  local dir
  for dir in "$base"/*/; do
    [ -d "$dir" ] || continue
    [ -L "${dir%/}" ] && continue
    local branchName
    branchName=$(basename "$dir")
    [[ "$branchName" == .* ]] && continue
    # Only show directories that are git repos or worktrees.
    # READ-ONLY: this method MUST NOT mutate global git config.
    # safe.directory setup belongs in explicit repair primitives
    # (oo.user.fix → config.init.user) per docs/repair-toolkit.md.
    if [ -d "$dir/.git" ] || [ -f "$dir/.git" ]; then
      local statusOut="" statusErr=""
      statusOut=$(cd "$dir" && git status --short --branch 2>/dev/null | head -1)
      if [ -z "$statusOut" ]; then
        statusErr=$(cd "$dir" && git status --short --branch 2>&1 >/dev/null)
        if echo "$statusErr" | grep -q "dubious ownership"; then
          statusOut="(unsafe ownership — run 'oo user.fix')"
        fi
      fi
      printf "  %-20s %s\n" "$branchName" "$statusOut"
    fi
  done
}

oo.branch.list() # <?source:all> # list available branches from worktrees, local git, and/or remote
{
  local source="${1:-all}"

  # Validate source parameter
  case "$source" in
    all|local|remote) ;;
    *)
      error.log "Invalid source: '$source' (use: all, local, remote)"
      create.result 1 "invalid source"
      return $(result)
      ;;
  esac

  local branches=""

  # Worktree directories (if worktree structure exists)
  if [ "$source" = "all" ] || [ "$source" = "local" ]; then
    local base
    base=$(oo.mode.base.get 2>/dev/null)
    if [ $? -eq 0 ] && [ -d "$base" ]; then
      local dir
      for dir in "$base"/*/; do
        [ -L "${dir%/}" ] && continue
        [ -d "$dir/.git" ] || [ -f "$dir/.git" ] || continue
        local dirName
        dirName=$(basename "$dir")
        [[ "$dirName" == .* ]] && continue
        branches="${branches}${dirName}"$'\n'
      done
    fi

    # Local git branches
    local localBranches
    localBranches=$(git -C "${OOSH_DIR:-$HOME/oosh}" branch --format='%(refname:short)' 2>/dev/null)
    if [ -n "$localBranches" ]; then
      branches="${branches}${localBranches}"$'\n'
    fi
  fi

  # Remote branches (with fetch)
  if [ "$source" = "all" ] || [ "$source" = "remote" ]; then
    git -C "${OOSH_DIR:-$HOME/oosh}" fetch origin 2>/dev/null
    local remoteBranches
    remoteBranches=$(git -C "${OOSH_DIR:-$HOME/oosh}" branch -r --format='%(refname:short)' 2>/dev/null \
      | sed 's|^origin/||' \
      | grep -v '^HEAD$')
    if [ -n "$remoteBranches" ]; then
      branches="${branches}${remoteBranches}"$'\n'
    fi
  fi

  # Deduplicate and sort
  local branchResult
  branchResult=$(echo "$branches" | grep -v '^$' | sort -u)

  if [ -n "$branchResult" ]; then
    echo "$branchResult"
    create.result 0 "$branchResult"
  else
    create.result 1 "no branches found"
  fi

  return $(result)
}
oo.branch.list.completion.source() {
  echo "all"
  echo "local"
  echo "remote"
}

oo.mode() # <?branch> # switch oosh to a branch worktree, or show current mode
{
  local WORKTREE_BASE
  WORKTREE_BASE=$(oo.mode.base.get)
  local hasWorktrees=$?
  local OOSH_LINK="$HOME/oosh"
  local branch="$1"

  local current_target
  current_target=$(readlink "$OOSH_LINK" 2>/dev/null)

  if [ -z "$branch" ]; then
    # No argument: show current worktree status
    # Resolve from symlink to show the actual target, not a stale OOSH_DIR.
    # `private.this.path.canonical` (this:144) is the portable resolver —
    # macOS BSD readlink predating 12.3 lacks `-f`, on those systems plain
    # `readlink -f` silently returns nothing and we'd report the symlink
    # path verbatim instead of its target.
    local actualPath
    actualPath=$(private.this.path.canonical "$OOSH_LINK" 2>/dev/null || echo "$OOSH_DIR")
    local currentName
    currentName=$(basename "$actualPath")
    echo "Mode: $currentName"
    echo "Path: $actualPath"
    if [ -n "$WORKTREE_BASE" ]; then
      echo "Worktree base: $WORKTREE_BASE"
    else
      echo "Worktree base: (none — run 'oo mode.setup' to enable branch switching)"
    fi
    if [ -d "$actualPath" ]; then
      (cd "$actualPath" && git status --short --branch 2>/dev/null)
    fi
    # Consistency check: git branch vs directory name (only in worktree environments)
    if [ -n "$WORKTREE_BASE" ]; then
      local gitBranch
      gitBranch=$(git -C "$actualPath" branch --show-current 2>/dev/null)
      if [ -n "$gitBranch" ] && [ "$gitBranch" != "$currentName" ]; then
        echo "Git branch is '$gitBranch' but worktree directory is '$currentName'"
        echo "  To fix: oo mode.align"
      fi
    fi
    return 0
  fi

  # Same-branch fast-path: if the requested branch matches the currently
  # resolved symlink target, just show the current state and return. Saves
  # the symlink-rm/shim-install/ln/config-save cascade (oo:566-600) and
  # side-steps any downstream `oo update` / git-hook chain that might
  # prompt for HTTPS credentials in unattended contexts (containers, CI
  # runners). Observed live in platform-test ubuntu container where
  # `oo mode dev` (already on dev) hung on `Username for 'https://github.com':`.
  local currentName=""
  if [ -n "$current_target" ]; then
    currentName=$(basename "$current_target")
  fi
  if [ -n "$currentName" ] && [ "$branch" = "$currentName" ]; then
    echo "Mode: $currentName (already current)"
    echo "Path: $(private.this.path.canonical "$OOSH_LINK" 2>/dev/null || echo "$OOSH_DIR")"
    return 0
  fi

  # Require worktree structure for switching
  if [ $hasWorktrees -ne 0 ] || [ -z "$WORKTREE_BASE" ]; then
    error.log "No worktree structure detected. Run 'oo mode.setup' to convert your install."
    return 1
  fi

  local target_dir="$WORKTREE_BASE/$branch"

  if [ ! -d "$target_dir" ]; then
    console.log "Directory '$branch' not found. Checking remote branches..."
    # Strict match: exact branch name (origin/<branch>), not regex/substring.
    # The previous `grep -i "${branch//\./.*}"` matched partial names —
    # `oo mode test` would silently select `origin/test/ish` (alphabetical
    # first) and try to wedge a worktree at $WORKTREE_BASE/test for a
    # branch actually named `test/ish`. Worse, when that worktree-add
    # silently failed but the rest of oo.mode ran anyway, OOSH_DIR was
    # exported to a non-existent path, breaking every subsequent c2
    # completion lookup (c2:296 `script="$OOSH_DIR/ng/c2"` → EPERM).
    # `grep -Fx` = fixed-string, full-line match; the user gets a clean
    # error and can retype the full name (e.g. `oo mode test/ish`).
    local remote_branch
    remote_branch=$(cd "$current_target" && git branch -r 2>/dev/null \
      | sed 's/^ *//' \
      | grep -v 'HEAD' \
      | grep -Fx "origin/$branch" | head -1)
    if [ -z "$remote_branch" ]; then
      error.log "No remote branch named '$branch' found (use full name, e.g. \`oo mode test/macos\`)"
      create.result 1 "branch not found"
      return $(result)
    fi
    console.log "Creating worktree for $remote_branch..."
    if ! (cd "$current_target" && git worktree add -B "${remote_branch#origin/}" "$target_dir" "$remote_branch"); then
      error.log "git worktree add failed for $remote_branch → $target_dir"
      create.result 1 "worktree add failed"
      return $(result)
    fi
    # Post-condition: target_dir MUST exist before we touch any symlink.
    # Even when `git worktree add` reports success, defensive check.
    if [ ! -d "$target_dir" ]; then
      error.log "Worktree add reported success but $target_dir is missing"
      create.result 1 "worktree add post-condition violated"
      return $(result)
    fi
  fi

  # Switch symlink
  if [ -L "$OOSH_LINK" ]; then
    rm "$OOSH_LINK"
  elif [ -d "$OOSH_LINK" ]; then
    error.log "$OOSH_LINK is a real directory, not a symlink. Run 'oo mode.setup' first."
    return 1
  fi
  # Check if oo() shim is loaded BEFORE installing (install sources it in subprocess)
  local wasShimFunction=false
  declare -F oo >/dev/null 2>&1 && wasShimFunction=true

  # Install shim BEFORE updating OOSH_DIR — the template lives in the current branch
  private.oo.install.shim 2>/dev/null

  ln -s "$target_dir" "$OOSH_LINK"

  # Replace old branch name in ALL PATH entries (handles /root/<branch>, /home/shared/.../<branch>, etc.)
  local oldOoshDir="$OOSH_DIR"
  local oldBranchName
  oldBranchName=$(basename "$oldOoshDir")
  if [ -n "$oldBranchName" ] && [ "$oldBranchName" != "$branch" ]; then
    PATH=$(echo "$PATH" | sed "s|/${oldBranchName}:|/${branch}:|g;s|/${oldBranchName}$|/${branch}|")
  fi
  hash -r  # Clear command hash table so bash finds commands in new path

  export OOSH_MODE="$branch"
  export OOSH_DIR="$target_dir"
  config save oosh OOSH 2>/dev/null

  # Write env pickup file so the shim can update the parent shell next time
  mkdir -p "$HOME/.config/oosh"
  cat > "$HOME/.config/oosh/mode-env.bash" <<MODEENV
PATH=\$(echo "\$PATH" | sed "s|/${oldBranchName}:|/${branch}:|g;s|/${oldBranchName}\$|/${branch}|")
hash -r
export OOSH_DIR="$target_dir"
export OOSH_MODE="$branch"
MODEENV

  echo "Switched to: $branch ($target_dir)"

  # If running as a script (not the oo() function), OOSH_DIR won't propagate
  if [ "$wasShimFunction" = "false" ]; then
    echo ""
    echo "Note: To update your current shell, run:"
    echo "  source ~/.config/oosh/oo-func.bash"
  fi
}

oo.mode.align() # # align git branch to match the current worktree directory name
{
  local OOSH_LINK="$HOME/oosh"
  local actualPath
  # private.this.path.canonical (this:144) — portable resolver; see comment
  # at oo.mode for the macOS BSD-readlink rationale.
  actualPath=$(private.this.path.canonical "$OOSH_LINK" 2>/dev/null || echo "$OOSH_DIR")
  local currentName
  currentName=$(basename "$actualPath")

  local gitBranch
  gitBranch=$(git -C "$actualPath" branch --show-current 2>/dev/null)

  if [ -z "$gitBranch" ]; then
    error.log "Cannot determine current git branch in $actualPath"
    create.result 1 "no git branch"
    return $(result)
  fi

  if [ "$gitBranch" = "$currentName" ]; then
    console.log "Already aligned: git branch '$gitBranch' matches directory '$currentName'"
    create.result 0 "already aligned"
    return $(result)
  fi

  warn.log "Git branch is '$gitBranch' but directory is '$currentName'"
  console.log "Checking out '$currentName'..."

  if git -C "$actualPath" checkout "$currentName" 2>/dev/null; then
    success.log "Aligned: git branch now matches directory '$currentName'"
    create.result 0 "$currentName"
  else
    error.log "Failed to checkout '$currentName' — branch may not exist locally"
    console.log "  Try: oo checkout $currentName"
    create.result 1 "checkout failed"
  fi

  return $(result)
}

private.oo.install.shim() {
  local sourceDir="${1:-$OOSH_DIR}"
  local shimSrc="$sourceDir/templates/user/ooShim"
  local shimDst="$HOME/.config/oosh/oo-func.bash"

  if [ ! -f "$shimSrc" ]; then
    return 0  # shim template not available, skip silently
  fi

  mkdir -p "$HOME/.config/oosh"
  cp "$shimSrc" "$shimDst"

  # Source it now so oo() function is available immediately
  source "$shimDst"

  # Persist in .bashrc if not already there
  if ! grep -q 'oo-func.bash' "$HOME/.bashrc" 2>/dev/null; then
    echo '[ -f "$HOME/.config/oosh/oo-func.bash" ] && source "$HOME/.config/oosh/oo-func.bash"' >> "$HOME/.bashrc"
  fi

  echo "  Shim:    $shimDst (oo function loaded — mode/use update current shell)"
}

oo.mode.setup() # <?worktree_base> # convert a plain clone to worktree structure for branch switching
{
  local OOSH_LINK="$HOME/oosh"
  local currentDir
  # private.this.path.canonical (this:144) — portable resolver.
  currentDir=$(private.this.path.canonical "$OOSH_DIR" 2>/dev/null || echo "$OOSH_DIR")

  # Check if already set up — base must exist AND contain at least one worktree subdir
  local existingBase
  existingBase=$(oo.mode.base.get 2>/dev/null)
  if [ -n "$existingBase" ] && [ -d "$existingBase" ]; then
    local hasWorktrees=false
    for d in "$existingBase"/*/; do
      if [ -d "$d/.git" ] || [ -f "$d/.git" ]; then
        hasWorktrees=true
        break
      fi
    done
    if [ "$hasWorktrees" = "true" ]; then
      echo "Worktree structure already exists at: $existingBase"
      return 0
    fi
  fi

  # Determine worktree base directory
  local base="${1:-$(dirname "$currentDir")}"
  local devDir="$base/dev"

  # Detect current branch
  local currentBranch
  currentBranch=$(this.git.branch.short "$currentDir")
  if [ -z "$currentBranch" ]; then
    error.log "Cannot detect current branch in $currentDir"
    return 1
  fi

  echo "Setting up worktree structure..."
  echo "  Source: $currentDir (branch: $currentBranch)"
  echo "  Base:   $base"

  if [ "$currentBranch" = "dev" ]; then
    # On dev: just move the directory
    if [ "$currentDir" = "$devDir" ]; then
      echo "Already at $devDir — nothing to move"
    else
      echo "Moving $currentDir → $devDir"
      mv "$currentDir" "$devDir"
    fi
  else
    # On a feature branch: move to dev dir, checkout dev, then worktree add for the branch
    echo "Moving $currentDir → $devDir"
    mv "$currentDir" "$devDir"

    echo "Switching $devDir to dev branch..."
    (cd "$devDir" && git checkout dev 2>/dev/null) || {
      # If dev doesn't exist locally, create it from origin/dev
      (cd "$devDir" && git checkout -b dev origin/dev 2>/dev/null) || {
        error.log "Failed to checkout dev branch. Restoring original location."
        mv "$devDir" "$currentDir"
        return 1
      }
    }

    echo "Creating worktree for $currentBranch..."
    (cd "$devDir" && git worktree add -B "$currentBranch" "../$currentBranch" "$currentBranch") || {
      error.log "Failed to create worktree for '$currentBranch'. Restoring original location."
      (cd "$devDir" && git checkout "$currentBranch" 2>/dev/null)
      mv "$devDir" "$currentDir"
      return 1
    }
  fi

  # Create symlink
  local targetDir
  if [ "$currentBranch" = "dev" ]; then
    targetDir="$devDir"
  else
    targetDir="$base/$currentBranch"
  fi

  if [ -L "$OOSH_LINK" ]; then
    rm "$OOSH_LINK"
  fi
  # At this point the original directory was moved, so OOSH_LINK path is free
  ln -s "$targetDir" "$OOSH_LINK"

  # Update environment
  export OOSH_COMPONENTS_DIR="$base"
  export OOSH_DIR="$targetDir"
  export OOSH_MODE="$currentBranch"
  config save oosh OOSH 2>/dev/null

  # Install ooShim so 'oo mode' always works regardless of active branch
  private.oo.install.shim "$targetDir"

  echo ""
  echo "Worktree structure created:"
  echo "  Base:    $base"
  echo "  Dev:     $devDir"
  if [ "$currentBranch" != "dev" ]; then
    echo "  Branch:  $targetDir"
  fi
  echo "  Symlink: $OOSH_LINK → $targetDir"
  echo ""
  echo "Use 'oo mode <branch>' to switch branches."
}

private.oo.completion.worktree.branches() # # echo branch names whose worktree dirs sit under oo.mode.base.get; safe inside c2 completion subprocesses (no create.result side-effects)
{
  # Single source of truth for the four `*.completion.branch` /
  # `*.completion.baseBranch` functions below.
  #
  # Why a private helper instead of calling oo.branch.list directly: that
  # function uses create.result (oo:420), which corrupts c2's completion
  # subprocess output on macOS — root never sees branches; admin sees them
  # only on the 3rd TAB via a c2 menu quirk. Same regression class as
  # 7e54c91 fixed for oo.checkout.completion.version, and 70009fa /
  # 8b14c78 fixed for the four call sites below. Memory:
  # [project-create-result-breaks-c2-completion].
  #
  # Why filesystem iteration instead of `git worktree list --porcelain`:
  # the T-MODE-5 fixture (and any future independent-`git init` sibling
  # layout) doesn't share a .git, so `git worktree list` would return
  # only the current dir — see T-MODE-5 commentary in test/test.oo.
  local base
  base=$(oo.mode.base.get 2>/dev/null)
  [ $? -eq 0 ] && [ -d "$base" ] || return 0
  local dir
  for dir in "$base"/*/; do
    [ -L "${dir%/}" ] && continue
    [ -d "$dir/.git" ] || [ -f "$dir/.git" ] || continue
    local dirName
    dirName=$(basename "$dir")
    [[ "$dirName" == .* ]] && continue
    echo "$dirName"
  done | sort -u
}

oo.mode.completion.branch() {
  # Delegates to private.oo.completion.worktree.branches (single source
  # of truth for worktree-branch enumeration across the four completion
  # functions; safe inside c2 completion subprocesses).
  private.oo.completion.worktree.branches
}

oo.checkout() # <version> # clone or add worktree for a remote branch
{
  local version="$1"

  if [ -z "$version" ]; then
    error.log "Usage: oo checkout <version>"
    create.result 1 "missing version"
    return $(result)
  fi

  # Derive directory name: strip prefixes, convert slashes to dots
  local dirName="$version"
  dirName="${dirName#test/}"
  dirName="${dirName#feature/}"
  dirName="${dirName#bugfix/}"
  dirName=$(echo "$dirName" | tr '/' '.')

  # Detect environment
  local base
  base=$(oo.mode.base.get 2>/dev/null)
  local hasWorktrees=$?

  if [ $hasWorktrees -eq 0 ] && [ -d "$base" ]; then
    # --- Worktree mode ---
    local targetDir="$base/$dirName"

    if [ -d "$targetDir" ]; then
      # Already exists — check if git branch matches directory name
      local currentGitBranch
      currentGitBranch=$(git -C "$targetDir" branch --show-current 2>/dev/null)

      if [ -n "$currentGitBranch" ] && [ "$currentGitBranch" != "$dirName" ]; then
        # Misaligned: git branch doesn't match directory name — fix it
        info.log "Aligning git branch to directory name: $dirName"
        if git -C "$targetDir" checkout "$dirName" 2>/dev/null; then
          success.log "Aligned branch to '$dirName' in $targetDir"
          create.result 0 "$targetDir"
          return $(result)
        else
          error.log "Failed to checkout branch '$dirName' in $targetDir"
          create.result 1 "checkout failed"
          return $(result)
        fi
      fi

      # Already aligned and present — nothing to do. Pulling latest is NOT
      # `oo.checkout`'s job; merging branches lives in `promote` /
      # `oo stage` (the canonical promotion pipeline) and pulling lives
      # in `oo update` (the explicit user-invoked refresh). Conflating
      # them here meant `oo checkout dev` (with dev/ already present)
      # silently fetched + merged origin/dev — surprising for a command
      # whose name says "checkout", not "pull".
      success.log "Worktree '$dirName' already present at $targetDir — run \`oo update\` to refresh"
      create.result 0 "$targetDir"
      return $(result)
    fi

    # New worktree
    git -C "${OOSH_DIR:-$HOME/oosh}" fetch origin 2>/dev/null
    info.log "Creating worktree for $version at $targetDir"
    if git -C "${OOSH_DIR:-$HOME/oosh}" worktree add -B "$dirName" "$targetDir" "origin/$version" 2>/dev/null; then
      success.log "Created worktree for $version at $targetDir"
      console.log "  Use 'oo mode $dirName' to switch to it"
      create.result 0 "$targetDir"
    else
      error.log "git worktree add failed for '$version'"
      create.result 1 "worktree add failed"
    fi

  else
    # --- Plain clone mode ---
    local repoUrl
    repoUrl=$(git -C "${OOSH_DIR:-$HOME/oosh}" remote get-url origin 2>/dev/null)
    if [ -z "$repoUrl" ]; then
      error.log "Cannot determine git remote URL from ${OOSH_DIR:-$HOME/oosh}"
      create.result 1 "no remote URL"
      return $(result)
    fi

    local targetDir
    targetDir="$(dirname "${OOSH_DIR:-$HOME/oosh}")/$dirName"

    if [ -d "$targetDir" ]; then
      if [ -d "$targetDir/.git" ] || [ -f "$targetDir/.git" ]; then
        # Same rationale as the worktree-mode branch above: oo.checkout
        # does NOT pull. Pulls live in `oo update`; merges in `promote`.
        success.log "Clone '$dirName' already present at $targetDir — run \`oo update\` to refresh"
        create.result 0 "$targetDir"
        return $(result)
      else
        error.log "Directory exists but is not a git repo: $targetDir"
        create.result 1 "not a git repo"
        return $(result)
      fi
    fi

    # Fetch before clone to ensure branch is available
    git -C "${OOSH_DIR:-$HOME/oosh}" fetch origin 2>/dev/null
    info.log "Cloning $version into $targetDir"
    if git clone "$repoUrl" -b "$version" "$targetDir" 2>/dev/null; then
      success.log "Cloned $version to $targetDir"
      console.log "  Use 'oo mode $dirName' to switch to it"
      create.result 0 "$targetDir"
    else
      error.log "git clone failed for branch '$version'"
      rm -rf "$targetDir" 2>/dev/null
      create.result 1 "clone failed"
    fi
  fi

  return $(result)
}
oo.checkout.completion.version() {
  # Try the canonical source first — `git ls-remote` reflects branches that
  # may have been pushed since the last fetch. When origin is unreachable
  # (offline laptop, VM without the 2cuGitHub SSH alias configured, expired
  # token, no network), ls-remote fails silently with `2>/dev/null` and the
  # caller would see no completions — c2's fallback then leaks the literal
  # `addDefaultValue` placeholder from PARAM_version. Fall back to the
  # locally-cached remote-tracking refs (`refs/remotes/origin/*`) so the user
  # at least sees the branches they last fetched. Linux with a reachable
  # origin gets the same output as before (out is non-empty → printed
  # directly). Pure-additive change for the unreachable case.
  local oosh="${OOSH_DIR:-$HOME/oosh}"
  local out
  out=$(git -C "$oosh" ls-remote --heads origin 2>/dev/null | sed 's|.*refs/heads/||')
  if [ -n "$out" ]; then
    printf '%s\n' "$out"
  else
    git -C "$oosh" for-each-ref refs/remotes/origin --format='%(refname:short)' 2>/dev/null \
      | sed 's|^origin/||'
  fi
}

oo.use() # <branch> <command> # run a command from a specific branch without switching
{
  local branch="$1"
  shift
  local command="$1"
  shift

  if [ -z "$branch" ] || [ -z "$command" ]; then
    error.log "Usage: oo use <branch> <command> [args...]"
    create.result 1 "missing branch or command"
    return $(result)
  fi

  # Try worktree base first, fall back to sibling directories
  local branchDir
  local base
  base=$(oo.mode.base.get 2>/dev/null)
  if [ $? -eq 0 ] && [ -d "$base/$branch" ]; then
    branchDir="$base/$branch"
  elif [ -d "$(dirname "${OOSH_DIR:-$HOME/oosh}")/$branch" ]; then
    branchDir="$(dirname "${OOSH_DIR:-$HOME/oosh}")/$branch"
  else
    error.log "Branch directory '$branch' not found"
    create.result 1 "branch not found"
    return $(result)
  fi

  if [ ! -f "$branchDir/$command" ]; then
    error.log "Command '$command' not found in branch '$branch'"
    create.result 1 "command not found"
    return $(result)
  fi

  # Export logging stubs so target branch bootstrap can call them.
  # Older branches call important.log/console.log during this.init before
  # log is fully loaded — without these exports, "command not found" errors occur.
  # IMPORTANT: Do NOT export info.log — it's used as bootstrap guard in log line 78.
  # If info.log exists, log skips sourcing this, which breaks the target branch.
  { export -f console.log important.log warn.log error.log \
             debug.log success.log silent.log stop.log seq.puml.log; } 2>/dev/null

  # Execute from target branch with overridden OOSH_DIR
  OOSH_DIR="$branchDir" "$branchDir/$command" "$@"
}
oo.use.completion.branch() {
  # Delegates to private.oo.completion.worktree.branches — see its
  # docstring for the create.result / worktree-list rationale.
  private.oo.completion.worktree.branches
}
oo.use.completion.command() {
  # In completion context, $1 is $cur (word being completed), not the branch.
  # The branch value was already parsed into PARAM_branch in current.method.env.
  source "$CONFIG_PATH/current.method.env" 2>/dev/null
  local branch="$PARAM_branch"
  local branchDir="$(oo.mode.base.get)/$branch"
  if [ -d "$branchDir" ]; then
    ls "$branchDir" 2>/dev/null | grep -v '^\.' | grep -v '\.md$'
  fi
}

oo.branches.check() # <featureBranchPrefix> <baseBranch> <startCommit> # show state of branches
{
  local featureBranchPrefix=$1
  shift
  local R=remotes/origin
  local baseBranch=$R/$1
  shift
  local startCommit=$1

  if [ -z $featureBranchPrefix ]; then
    error.log "No featureBranchPrefix given"
    exit 1
  fi
  if [ -z $baseBranch ]; then
    error.log "No baseBranch given"
    exit 1
  fi
  if [ -z $startCommit ]; then
    error.log "No startCommit given"
    exit 1
  fi

  # config
  padding1=".............................................................................."
  padding2="                                          "

  console.log
  console.log "==========================================================================="
  console.log "Update local repository (fetch --prune to remove all deleted branches)"
  git fetch --prune
  console.log "First commit for branches:"
  console.log `git show $startCommit`

  console.log
  console.log "==========================================================================="
  console.log "Check all relevant branches:"
  branches=`git branch -a --contains $startCommit | grep $R | grep -v $R/HEAD`
  for bl in $branches
  do
      b=`echo $bl | sed "s;$R/;;"`
      case "$b" in
      dev/neom|test/neom|master|dev|main|prod/WODA)
          ;;
      N1-104-embed-viewport-in-woda-metaverse-app|N1-356-create-persistent-structr-implementation-of-all-persistent-classes|feature/N1-424-cavrnus-component-styling|feature/neom/broken-startup-good-structrObjectLoad)
          console.log
          console.log ONHOLD: $b
          ;;
      *)
          NAME=`git log -1 --pretty=format:"%cn <%ce>" $bl`
          AGO=`git log -1 --pretty=format:"%cr" $bl`
          console.log
          console.log $b
          console.log "--> https://bitbucket.org/donges/eamd.ucp/branch/$b"
          console.log "    "`printf "%s%s - %s\n" "$NAME" "${padding2:${#NAME}}" "${AGO}"`

          # old, not deleted
          LONGAGO=`echo $AGO | grep -v day | grep -v hour | grep -v minute | grep -v second`
          if [[ -n "$LONGAGO" ]]; then
              error.log "Branch is old (Last commit ${LONGAGO})."
          fi

          # check name: ${featureBranchPrefix}
          BRANCH_START=${b::`echo -n ${featureBranchPrefix} | wc -c`}
          if [[ "${BRANCH_START}" != ${featureBranchPrefix} ]]; then
              error.log "Branch name must start with ${featureBranchPrefix} (not: ${BRANCH_START}...)."
          fi

          # check name: N1-<no>
          JIRA_NUMBER=`echo -n $b | grep -e "N1-[0-9][0-9][0-9]"`
          if [[ -z ${JIRA_NUMBER} ]]; then
              error.log "Branch name must contain a JIRA issue number."
          fi

          # merged and not deleted?
          MY_COMMIT=`git log -1 --pretty=format:"%H" $bl`
          COMMON_COMMIT=`git merge-base $bl $baseBranch`
          if [[ "$MY_COMMIT" == "$COMMON_COMMIT" ]]; then
              error.log "Branch is merged but not deleted."
          else
              console.log "Here are the changes compared to branch: $R/dev/neom"
              console.log "$(git diff --stat $R/dev/neom...$bl)"
          fi

          ;;
      esac
  done
}

oo.state() # # initializes the state machine or shows the current state if already initialized
{
  local machine=SETUP_SERVER

  if state machine.exists ${machine}; then
    state of ${machine} list all
    source $CONFIG_PATH/current.state.machine.env
    if [ $state -lt 4 ]; then
      state machine.start - oo
    fi
  else
    debug.log "initalize ${machine}"
    private.init.state.machine ${machine}
  fi

  # Advance state machine to completion (handles both first-install and re-runs)
  # Each state.next invokes the corresponding private.check.* guard function
  source $CONFIG_PATH/current.state.machine.env
  local maxIterations=30
  local i=0
  while [ $state -lt 99 ] && [ $i -lt $maxIterations ]; do
    local prevState=$state
    state next SETUP_SERVER
    source $CONFIG_PATH/current.state.machine.env
    if [ $state -le $prevState ]; then
      error.log "State machine stuck at [$state] — aborting advancement"
      # Propagate non-zero so init/oosh:518's `[ $rc -eq 0 ]` gate sees the
      # failure and the post-state-machine non-root invoker block at
      # init/oosh:536-561 correctly skips. Previously this `break` left
      # the function returning 0, so init/oosh ran post-machine work on top
      # of a broken install.
      return 1
    fi
    ((i++))
  done

  if [ $state -ge 99 ] && [ -n "$LOG_INSTALL" ]; then
    log.install.finish
  fi
}

private.init.state.machine() # <machine> # creates the state machine
{
  local machine=$1
  console.log "initialising state machine: ${machine}"
  
  source $OOSH_DIR/state

  state.machine.create ${machine} oo
  state.add remote.install.started               silent 
  state.add local.install.started                silent 
  state.add priviledges.checked                  silent 
  state.add 20                                   silent 
  state.add user.rights.only                     silent
  state.add user.installation.done               silent
  state.add user.mode.release                    silent
  state.add user.mode.dev                        silent
  state.add 30                                   silent  
  state.add root.rights                          silent 
  state.add root.shared.dev.folder.created       silent 
  state.add root.dev.keys.installed              silent 
  state.add root.installation.done               silent 
  state.add 40                                   silent 
  state.add user.shared.dev.folder.linked        silent 
  state.add user.state.machine.synced.with.root  silent 
  state.add 50                                   silent 
  state.add headless.setup.started               silent 
  state.add headless.setup.finished              silent 
  state.add 60                                   silent 
  state.add once.setup.started                   silent 
  state.add once.setup.finished                  silent 

  state.next
  state.machine.start oo
  state.next # remote.install.started
  state.next # root.rights
  state.list

  state next # root.shared.dev.folder.created
  # Full advancement to [99] is handled by oo.state()'s advancement loop
}

private.check.remote.install.started() {
  if [ -n "$SSH_CONFIG_FROM_REMOTE" ]; then
    success.log "
    
    Remote installation started from  : $SSH_CONFIG_FROM_REMOTE
    Remote name used for this machine±: $SSH_CONFIG_NAME_USED_FOR_LOCAL
    "
    create.result 0 12 $1
  else
    warn.log "SSH_CONFIG_FROM_REMOTE not set. No remote installation detected"
    success.log "remote.install.started: continue with local install"
    create.result 0 12 $1
  fi
  return $(result)
}

private.check.local.install.started() {
  if [ -f "$OOSH_DIR/this" ]; then
    create.result 0 "$OOSH_DIR/this exists"
  else
    create.result 1 "$OOSH_DIR/this does not exist"
  fi
}

private.check.priviledges.checked() # # RESULT will be the follow up state
{
  if [ "$USER" = "root" ]; then
    create.result 0 "30"
  else
    create.result 1 "user privilidges"
    if private.test.sudo.priviledges; then
      # RESULT will be the follow up state: [30]="root.rights""
      create.result 0 "30"
    else
      # RESULT will be the follow up state: [20]="user.rights.only"
      create.result 0 "20"
    fi
  fi

}

private.test.sudo.priviledges() {
  if [ -f ~/.sudo_as_admin_successful ]; then
    create.result 0 "sudo privilidges active"
    return $(result)
  else
    if [ -n "$SUDO" ]; then
      $SUDO touch ~/.sudo_as_admin_successful
      if [ -f ~/.sudo_as_admin_successful ]; then
          rm ~/.sudo_as_admin_successful
          create.result 0 "sudo privilidges by password"
          return $(result)
      fi
    fi
  fi

  if [ "$HOME" = "/root" ];then
          create.result 0 "sudo privilidges not required because USER is ROOT"
          return $(result)
  fi
  
  create.result 1 "no sudo privilidges"
  return $(result)
}

private.check.user.rights.only() {
  source state
  state.check SETUP_SERVER priviledges.checked oo
  if [ "$RESULT" = "20" ]; then
    create.result 0 "20"
    return $(result)
  fi
  if [ "$RESULT" = "30" ]; then
    create.result 0 "30"
    return $(result)
  fi
  create.result 1 "Something went wrong! \n$RESULT"
  return $(result)
}

private.check.user.installation.done() {
  if [ -d $HOME/oosh ]; then
    create.result 0 "$HOME/oosh exists"
  else
    create.result 1 "$HOME/oosh does not exist"
  fi
}

private.check.user.mode.release() {
  if [ "$OOSH_MODE" = "released" ]; then
    create.result 0 "OOSH_MODE=$OOSH_MODE"
  else
    create.result 1 "not in mode released!   OOSH_MODE=$OOSH_MODE"
  fi
  return $(result)
}

private.check.user.mode.dev() {
  if [ "$OOSH_MODE" = "dev" ]; then
    create.result 0 "OOSH_MODE=$OOSH_MODE"
  else
    create.result 1 "not in mode: dev!   OOSH_MODE=$OOSH_MODE"
    console.log "
    
    you can switch there manualy with
    
    oo mode.dev
    "
  fi
  return $(result)
}

private.check.root.rights() {
  source state
  state.check SETUP_SERVER priviledges.checked oo
  if [ "$RESULT" = "20" ]; then
    create.result 1 "21"
    return $(result)
  fi
  if [ "$RESULT" = "30" ]; then
    create.result 0 "30"
    return $(result)
  fi
  create.result 1 "Something went wrong! \n$RESULT"
  return $(result)
}


private.oo.shared.tree.from.local() # <sourceDir> <branchName> # produce canonical `main/ + <branchName>/` worktree layout in $(pwd) from a local clone. Idempotent — skips if main/ already exists. Used by state-31's fallback path when the GitHub SSH clone fails so the resulting layout matches what a successful network clone would have produced (oo.mode.base.get strategy 2/4 then succeeds and `oo mode <branch>` works).
{
  local src="$1"
  local branch="$2"

  if [ -z "$src" ] || [ -z "$branch" ]; then
    error.log "private.oo.shared.tree.from.local: usage <sourceDir> <branchName>"
    return 1
  fi
  if [ ! -d "$src" ]; then
    error.log "private.oo.shared.tree.from.local: source dir not found: $src"
    return 1
  fi

  # Idempotent fast-path: main/ already present → nothing to do.
  if [ -d "main" ]; then
    important.log "private.oo.shared.tree.from.local: main/ already present in $(pwd) — skipping"
    return 0
  fi

  # 1. Copy source → main/. The .git/ from the source comes along, so main/
  #    is a self-contained clone that can host worktrees.
  cp -a "$src" "main" || { error.log "private.oo.shared.tree.from.local: copy $src → main/ failed"; return 1; }

  # 2. Make sure main/'s active branch is 'main'. Local sources are typically
  #    cloned with `-b dev` so their HEAD points at dev. Create local 'main'
  #    from origin/main if available. If origin/main isn't fetched either,
  #    warn but proceed — oo.mode.base.get strategy 4 just checks dir
  #    existence so the layout is still usable.
  if ! git -C main rev-parse --verify main >/dev/null 2>&1; then
    if git -C main rev-parse --verify origin/main >/dev/null 2>&1; then
      git -C main checkout -B main origin/main >/dev/null 2>&1 \
        || warn.log "private.oo.shared.tree.from.local: could not create local main from origin/main"
    else
      warn.log "private.oo.shared.tree.from.local: no origin/main ref in source — main/ stays on '$branch' branch (oo.mode.base.get sibling-dir check still satisfied)"
    fi
  else
    git -C main checkout main >/dev/null 2>&1 \
      || warn.log "private.oo.shared.tree.from.local: could not checkout main in main/"
  fi

  # 3. Add the active branch as a worktree of main/. Skip when the active
  #    branch IS main — main/ already serves that role.
  if [ "$branch" != "main" ]; then
    git -C main worktree add -B "$branch" "../$branch" "$branch" >/dev/null 2>&1 \
      || git -C main worktree add -B "$branch" "../$branch" "origin/$branch" >/dev/null 2>&1 \
      || { error.log "private.oo.shared.tree.from.local: git worktree add ../$branch failed in main/"; return 1; }
  fi

  important.log "private.oo.shared.tree.from.local: canonical layout produced in $(pwd) (main/ + ${branch}/)"
  return 0
}


private.oo.user.shared.symlinks.ensure() # <userHome> <sharedConfigPath> <onceShBase> <ooshBranch> # ensure $userHome/config and $userHome/oosh are symlinks to the shared tree; idempotent; preserves originals as .orig.<ts>
{
  local userHome="$1"
  local sharedConfigPath="$2"
  local onceShBase="$3"
  local ooshBranch="$4"
  if [ -z "$userHome" ] || [ -z "$sharedConfigPath" ] \
     || [ -z "$onceShBase" ] || [ -z "$ooshBranch" ]; then
    create.result 1 "private.oo.user.shared.symlinks.ensure requires <userHome> <sharedConfigPath> <onceShBase> <ooshBranch>"
    error.log "$RESULT"
    return $(result)
  fi

  # Delegate the actual symlink-with-backup work to the shared primitive
  # private.this.symlink.with.backup (this:171). Same `.orig.<timestamp>`
  # collision-proof convention as config.init.user (config:295,304),
  # now driven by one canonical implementation. Each call fails loud on
  # any mv/ln failure — see the primitive's docstring.
  local _ts
  _ts=$(date +%Y%m%d-%H%M%S)
  private.this.symlink.with.backup "$userHome/config" "$sharedConfigPath"       "$_ts" || return $(result)
  private.this.symlink.with.backup "$userHome/oosh"   "$onceShBase/$ooshBranch" "$_ts" || return $(result)

  # Verify post-condition for fail-loud testing.
  if [ -L "$userHome/config" ] && [ -L "$userHome/oosh" ]; then
    create.result 0 "symlinks ensured for $userHome"
  else
    create.result 1 "failed to ensure symlinks at $userHome (config=$([ -L "$userHome/config" ] && echo L || echo D), oosh=$([ -L "$userHome/oosh" ] && echo L || echo D))"
    error.log "$RESULT"
  fi
  return $(result)
}

private.check.root.shared.dev.folder.created() {
  #check.debug.level 6
  #source user
  user get home
  if [ -z "$USER" ]; then
    warn.log "no user set"
    USER=$(user get name)
    console.log "fixed user: $USER"
  fi
  
  if [ ! "$USER" = "root" ]; then
    # Non-root user: check if shared infrastructure was already created by root
    local dir="$( user get basehome $USER )"
    console.log "dir=$dir"

    if [ -d "$dir/shared/EAMD.ucp" ]; then
      success.log "Shared infrastructure already created by root at $dir/shared"
      create.result 0 "root.shared.dev.folder.created" "$1"
      return $(result)
    else
      error.log "Shared infrastructure not found at $dir/shared — run root install first (ossh install <host>)"
      create.result 1 "shared folder not found at $dir/shared"
      return $(result)
    fi
  fi
  
  #set -x


  # ─── state 31 [1/5: developking.created] ────────────────────────────────
  # Ensure the `dev` group exists BEFORE any user.group.add invocation.
  # On minimal images (RHEL-family / busybox-based) the group is not
  # auto-created by `user.group.add` — without this explicit ensure, all
  # subsequent group memberships silently fail and private.ensure.sharedTree
  # (this:142) early-returns ("dev group not present — skipping").
  # private.this.group.create handles cross-platform: tries groupadd first
  # (Alma/Debian/etc), falls back to addgroup (Alpine/busybox), then to a
  # last-resort /etc/group append. Idempotent — no-op when group exists.
  private.this.group.create dev

  # Create developking and put them in the dev group. user.create
  # auto-invokes user.oosh.install, which (after f515903 + 4069475) gives
  # developking the same OOSH experience as any other user — bashrc with
  # completion, ~/oosh + ~/config symlinks, ed25519 keypair, WODA aliases.
  local devHome="$( user get home developking )"
  if [ -z "$devHome" ]; then
    user create developking
    user group.add dev developking
    devHome="$( user get home developking )"
  fi
  if [ -z "$devHome" ] || [ ! -d "$devHome" ]; then
    error.log "state 31 [1/5: developking.created]: 'user create developking' failed — $devHome (basehome) does not exist"
    create.result 1 "state 31 step 1/5 failed: developking not created"
    return $(result)
  fi
  # echo  "absolutePath of \"$devHome\..\"..."
  # this.absolutePath "$devHome/.."
  # debug.log "absolutePath of \"$devHome\..\": $RESULT"
  local dir="$( user get basehome developking )"
  console.log "dir=$dir"
  
  #return 2
  
  if [ -z "${OOSH_BRANCH}" ]; then
    # Detect branch from initial $HOME/oosh clone before it gets symlinked
    OOSH_BRANCH=$(this.git.branch.short "$HOME/oosh")
    if [ -z "$OOSH_BRANCH" ] || [ "$OOSH_BRANCH" = "HEAD" ]; then
      OOSH_BRANCH=main
    fi
  fi

  local onceShBase="$dir/shared/EAMD.ucp/Components/com/ceruleanCircle/EAM/1_infrastructure/Once.sh"

  # Idempotent fast-path: succeed silently only if the FULL state-31
  # invariant holds — shared dev tree exists AND developking has working
  # OOSH symlinks. Without the developking check, a previous install that
  # halted between the shared-tree mkdir and the user.oosh.install retry
  # below would short-circuit on the next pass and never finalize
  # developking — leaving the boss-as-developking experience broken.
  if [ -d "$onceShBase/${OOSH_BRANCH}" ] \
     && [ -L "$devHome/oosh" ] \
     && [ -L "$devHome/config" ] \
     && [ -L "$HOME/config" ] \
     && [ -L "$HOME/oosh" ]; then
    success.log "$onceShBase/${OOSH_BRANCH} exists; developking AND root have config+oosh symlinks"
    create.result 0 "root.shared.dev.folder.created" $1
  else
    console.log "creating shared infrastructure"

    # ─── state 31 [2/5: shared.tree.created] ──────────────────────────────
    mkdir -p "$dir/shared/dev/Workspaces"
    mkdir -p "$dir/shared/EAMD.ucp/Scenarios/localhost/EAM/1_infrastructure/Once.sh"
    mkdir -p "$dir/shared/EAMD.ucp/Components/com/ceruleanCircle/EAM/1_infrastructure/Once.sh"

    $SUDO user group.add dev $USER
    #cp -r $HOME/oosh "$dir/shared/dev/."

    $SUDO chown -R developking:dev "$dir/shared/dev/"
    #find "$dir/shared/dev/oosh" -type d -print0 | xargs -0 chmod 2775
    #find "$dir/shared/dev/oosh" -type f -print0 | xargs -0 chmod +grwx

    if [ ! -d "$dir/shared/EAMD.ucp" ] \
       || [ ! -d "$dir/shared/EAMD.ucp/Components/com/ceruleanCircle/EAM/1_infrastructure/Once.sh" ]; then
      error.log "state 31 [2/5: shared.tree.created]: $dir/shared/EAMD.ucp tree missing after mkdir -p"
      create.result 1 "state 31 step 2/5 failed: shared tree not created"
      return $(result)
    fi

    cd "$dir/developking/"
    oo.cmd wget
    oo.cmd tree
    #tree -a -L 2 /root/

    # Note: keypair + WODA Host entries + ~/.ssh structure are owned by
    # ossh.install.continue.local's user.init (user:230-278) which runs
    # before this state-31 check. No fallback needed here — modern pipeline
    # guarantees ~/.ssh exists by the time we get here.

    # ─── state 31 [3/5: dev.keys.deployed] ────────────────────────────────
    # Try web download first; fall back to runner-bundled template. Step
    # fails loud if neither produces ~developking/.ssh/id_rsa.
    export DEVELOPKING_SOURCE_SERVER=test.wo-da.de

    wget -r -np --cut-dirs=9 -R "index.html*" "https://${DEVELOPKING_SOURCE_SERVER}/EAMD.ucp/Components/com/ceruleanCircle/EAM/1_infrastructure/NewUserStuff/scripts/templates/developking.ssh"
    RETURN_VALUE=$?
    if ! [ "$RETURN_VALUE" = "0" ]; then
      warn.log "Could not download from ${DEVELOPKING_SOURCE_SERVER} — using local templates"
      if [ -d "$OOSH_DIR/templates/user/developking.ssh" ]; then
        cp -r "$OOSH_DIR/templates/user/developking.ssh" "$dir/developking/.ssh"
        chown -R developking:dev "$dir/developking/.ssh"
      else
        warn.log "developking.ssh templates not found — continuing without them"
      fi
    else
      chown -R developking:dev "$dir/developking/${DEVELOPKING_SOURCE_SERVER}/developking.ssh"
      mv "$dir/developking/${DEVELOPKING_SOURCE_SERVER}/developking.ssh" "$dir/developking/.ssh"
      rm -rf "$dir/developking/${DEVELOPKING_SOURCE_SERVER}"
    fi
    if [ ! -f "$dir/developking/.ssh/id_rsa" ]; then
      error.log "state 31 [3/5: dev.keys.deployed]: $dir/developking/.ssh/id_rsa missing after wget+template fallback"
      create.result 1 "state 31 step 3/5 failed: developking deploy keys not deployed"
      return $(result)
    fi

    # ─── state 31 [4/5: shared.config.seeded] ─────────────────────────────
    # Ensure root's ~/.ssh exists before any write. On Alpine 3.19's
    # naked_alpine_3_19 image the sshd config permits root login without
    # an authorized_keys file, so /root/.ssh is never auto-created on
    # first SSH session. private.install.dev.configs (and the heredoc
    # fallback below) then fail with "No such file or directory" on every
    # >> redirect to ~/.ssh/config. Also touched by ossh.config.save.last
    # (~/.ssh/known_hosts at oo:1777 in the same flow). Idempotent on
    # Alma/Ubuntu where /root/.ssh already exists from authorized_keys.
    mkdir -p "$HOME/.ssh"
    chmod 700 "$HOME/.ssh"

    # private.install.dev.configs writes Host github.com to root's
    # ~/.ssh/config via the canonical ossh.config.create + ossh.config.save.last
    # primitive (Phase A.8). On some platforms (verified on Alma 9 post-install)
    # that path historically silently failed to land the block — the function
    # reported success but the block didn't appear in the file. Below:
    # invoke the helper, then verify the post-condition; if missing, fall
    # through to a direct-write heredoc as a different-mechanism fail-safe
    # (ossh.config.create writes via $CONFIG_PATH/result.txt + cat — this
    # heredoc writes straight to ~/.ssh/config, bypassing any result.txt
    # permission/disk issue that might have caused the silent failure).
    # IdentityFile uses the portable `~` form (Phase A.5 onward).
    private.install.dev.configs
    touch "$HOME/.ssh/config"
    if ! ossh config.get github.com "$HOME/.ssh/config" >/dev/null 2>&1; then
      warn.log "state 31 [4/5]: private.install.dev.configs did not land Host github.com in $HOME/.ssh/config — falling through to direct-write heredoc"
      cat >>"$HOME/.ssh/config" <<'EOF'

Host github.com
 HostName github.com
 User git
 Port 22
 IdentityFile ~/.ssh/ids/ssh.developking/id_rsa
 IdentitiesOnly yes
EOF
    fi
    if ! ossh config.get github.com "$HOME/.ssh/config" >/dev/null 2>&1; then
      error.log "state 31 [4/5: shared.config.seeded]: Host github.com still missing from $HOME/.ssh/config after both private.install.dev.configs and heredoc fallback — write permissions on $HOME/.ssh/config?"
      ls -la "$HOME/.ssh/" >&2 2>/dev/null || true
      create.result 1 "state 31 step 4/5 failed: Host github.com alias not written"
      return $(result)
    fi


    cd "$dir/shared/EAMD.ucp/Components/com/ceruleanCircle/EAM/1_infrastructure/Once.sh"
    git clone git@github.com:Cerulean-Circle-GmbH/once.sh.git main
    RETURN_VALUE=$?
    if ! [ "$RETURN_VALUE" = "0" ]; then
      warn.log "SSH clone failed — falling back to local copy from \$HOME/oosh"
      if [ -d "$HOME/oosh" ]; then
        # Produce the canonical layout (main/ + ${OOSH_BRANCH}/ worktree) from
        # the local clone at \$HOME/oosh, mirroring what a successful network
        # clone above would have produced. Without this, the layout used to
        # be just `${OOSH_BRANCH}/` with no `main/` sibling — and
        # `oo.mode.base.get` strategy 2/3/4 then all failed, breaking
        # `oo mode <branch>` and tab-completion for every user on the
        # install. The helper is idempotent (skips if main/ already exists).
        private.oo.shared.tree.from.local "$(private.this.path.canonical "$HOME/oosh")" "${OOSH_BRANCH}"
        RETURN_VALUE=$?
        if ! [ "$RETURN_VALUE" = "0" ]; then return $RETURN_VALUE; fi
      else
        error.log "No oosh found to copy from"
        return $RETURN_VALUE
      fi
    else
      if [ ! -z ${OOSH_INSTALL_SOURCE} ] && [ -d ${OOSH_INSTALL_SOURCE} ]; then
        important.log "<oo> Install oosh from ${OOSH_INSTALL_SOURCE}"
        mkdir -p "${OOSH_BRANCH}"
        rsync -a ${OOSH_INSTALL_SOURCE}/ "${OOSH_BRANCH}/"
      elif [ "$OOSH_BRANCH" = "main" ]; then
        important.log "<oo> Branch is main — already cloned"
      else
        important.log "<oo> Creating worktree for branch ${OOSH_BRANCH}"
        cd main
        git fetch origin 2>&1
        RETURN_VALUE=$?
        if ! [ "$RETURN_VALUE" = "0" ]; then
          error.log "state 31: 'git fetch origin' failed (rc=$RETURN_VALUE) in $(pwd)"
          return $RETURN_VALUE
        fi
        # Defence in depth: if an older (unpatched) caller sent a polluted
        # $OOSH_BRANCH like "heads/origin/dev", strip it here before we
        # concatenate "origin/" below — otherwise git sees
        # "origin/heads/origin/dev" and fails with "invalid reference".
        OOSH_BRANCH="${OOSH_BRANCH#refs/heads/}"
        OOSH_BRANCH="${OOSH_BRANCH#refs/remotes/origin/}"
        OOSH_BRANCH="${OOSH_BRANCH#heads/origin/}"
        OOSH_BRANCH="${OOSH_BRANCH#origin/}"
        git worktree add -B "${OOSH_BRANCH}" "../${OOSH_BRANCH}" "origin/${OOSH_BRANCH}" 2>&1
        RETURN_VALUE=$?
        if ! [ "$RETURN_VALUE" = "0" ]; then
          error.log "state 31: 'git worktree add -B ${OOSH_BRANCH} ../${OOSH_BRANCH} origin/${OOSH_BRANCH}' failed (rc=$RETURN_VALUE) in $(pwd)"
          return $RETURN_VALUE
        fi
        cd ..
      fi
    fi



    # ln -s "$dir/shared/dev/oosh"
    # chown -h developking:dev "oosh"


    console.log "before config copy:
    SSH_CONFIG_NAME_USED_FOR_LOCAL
    $SSH_CONFIG_NAME_USED_FOR_LOCAL"

    local paramerUser="${SSH_CONFIG_NAME_USED_FOR_LOCAL#user.}"
    local currentHome
    if [ -n "$paramerUser" ] && [ "$paramerUser" != "root" ]; then
      currentHome="$dir/$paramerUser"
    else
      currentHome="$HOME"
    fi
    info.log "paramerUser=$paramerUser currentHome=$currentHome"

    local currentConfig="$currentHome/config"
    info.log "currentConfig=$currentConfig"

    mkdir -p "$currentHome/.ssh/ids/"
    cp -r "$devHome/.ssh" "$currentHome/.ssh/ids/ssh.developking"

  set -x
    # link root config to sharedConfig
    cp -r "$HOME/config" "$dir/shared/EAMD.ucp/Scenarios/localhost/EAM/1_infrastructure/Once.sh/sharedConfig"
    rm -rf "$dir/shared/EAMD.ucp/Scenarios/localhost/EAM/1_infrastructure/Once.sh/sharedConfig/stateMachines"
    cp -r "$currentConfig/stateMachines" "$dir/shared/EAMD.ucp/Scenarios/localhost/EAM/1_infrastructure/Once.sh/sharedConfig"
    
    # link user config to sharedConfig
    mv "$currentConfig" "$currentConfig.initial"
    ln -s "$dir/shared/EAMD.ucp/Scenarios/localhost/EAM/1_infrastructure/Once.sh/sharedConfig" "$currentConfig"
    chown -h $paramerUser:dev "$currentConfig"
    chown -R $paramerUser:dev "$dir/shared"
    chmod -R g+w "$dir/shared"

    # Always link ROOT's config AND oosh to the shared tree. State 31
    # runs as root regardless of which paramerUser is being set up —
    # without these symlinks, root's lazy
    # `${OOSH_DIR:="$(cd $HOME/oosh && pwd -P)"}` in
    # sharedConfig/user.env resolves to root's private clone, and root
    # can't see the shared worktrees (the macstudio-root regression).
    #
    # Previous guard `[ "$paramerUser" != "root" ]` was wrong: when
    # paramerUser is empty (SSH_CONFIG_NAME_USED_FOR_LOCAL isn't set
    # until ossh:674, which runs AFTER `oo state` at ossh:668 has
    # already advanced through state 31) OR explicitly "root", the
    # guard skipped the helper and left root's ~/oosh as a real dir.
    # Install log from the recurring macstudio bug confirmed the empty
    # case: `paramerUser= currentHome=/var/root`.
    #
    # The helper is idempotent — config already linked above will be
    # detected as a symlink and left alone. Delegates to the testable
    # helper private.oo.user.shared.symlinks.ensure (covered by
    # T-MODE-COMPLETION-LAZY-USERENV-ENSURE + T-SYMLINKS-ENSURE-COLLISION
    # in test/test.oo).
    private.oo.user.shared.symlinks.ensure \
      "$HOME" \
      "$dir/shared/EAMD.ucp/Scenarios/localhost/EAM/1_infrastructure/Once.sh/sharedConfig" \
      "$onceShBase" \
      "${OOSH_BRANCH}"

    # Enable git's shared-repository mode on both worktrees so subsequent
    # git operations (root's `git pull` on re-install at init/oosh:271,
    # any dev-group user running `oo update`) create files with group rw
    # and honour the setgid bit on .git directories. Without this, root's
    # git ops write FETCH_HEAD/ORIG_HEAD/objects as root:root 644 and
    # lock out dev-group users from further updates.
    for _wt in "$onceShBase/main" "$onceShBase/${OOSH_BRANCH}"; do
      if [ -e "$_wt/.git" ]; then
        git -C "$_wt" config core.sharedRepository group 2>/dev/null || true
      fi
    done
    # Propagate setgid to all .git dirs so new files inherit group=dev
    find "$onceShBase/main/.git" -type d -exec chmod g+s {} + 2>/dev/null
    chmod -R g+w "$onceShBase/main/.git" 2>/dev/null
    # Resolve canonical path — macOS case-insensitive FS needs exact case for safe.directory
    local resolvedOoshDir="$onceShBase/${OOSH_BRANCH}"
    if command -v osascript >/dev/null 2>&1; then
      resolvedOoshDir=$(osascript -e "POSIX path of (POSIX file \"$resolvedOoshDir\" as alias)" 2>/dev/null | sed 's:/$::') || resolvedOoshDir="$onceShBase/${OOSH_BRANCH}"
    fi
    private.oo.safeDirectory.add "$resolvedOoshDir" >/dev/null
    # Also add safe.directory for the `main` worktree (created in
    # step 2 above). Without this, root running `oo mode main` or any
    # git op against /Users/Shared/.../Once.sh/main hits dubious-
    # ownership rejection (shared tree is owned by developking:dev).
    # Future worktrees added via `oo checkout <branch>` would need
    # their own safe.directory entry — that's a separate concern at
    # the checkout site. Uses private.oo.safeDirectory.add for
    # idempotent dedup — earlier versions called `git config --add`
    # directly and accumulated duplicate entries on re-install.
    local resolvedMainDir="$onceShBase/main"
    if command -v osascript >/dev/null 2>&1; then
      resolvedMainDir=$(osascript -e "POSIX path of (POSIX file \"$resolvedMainDir\" as alias)" 2>/dev/null | sed 's:/$::') || resolvedMainDir="$onceShBase/main"
    fi
    private.oo.safeDirectory.add "$resolvedMainDir" >/dev/null

    # TODO: This should be removed to only use $HOME/oosh in the PATH
    export PATH="$( echo $PATH | line replace.sedquoted "$HOME/oosh" "$onceShBase/${OOSH_BRANCH}" )"

    #mv $HOME/config $HOME/config.initial
    if [ -d config.initial/stateMachines ]; then
      cp -r config.initial/stateMachines/ config
    fi
  set +x



    export CONFIG="$dir/shared/EAMD.ucp/Scenarios/localhost/EAM/1_infrastructure/Once.sh/sharedConfig/user.env"
    export CONFIG_FILE="user.env"
    export CONFIG_PATH="$dir/shared/EAMD.ucp/Scenarios/localhost/EAM/1_infrastructure/Once.sh/sharedConfig"
    export OOSH_DIR="$onceShBase/${OOSH_BRANCH}"
    export OOSH_MODE="${OOSH_BRANCH}"

    console.log "PATH=$PATH"
    console.log "CONFIG=$CONFIG"
    config save

    # Migrate install.log to /home/shared/sharedConfig (dev-group writable)
    # so non-root install-time processes (target-user user.oosh.install,
    # finish.local user-remote setup, runuser-spawned developking shells,
    # etc.) can append to the SAME file. Without this, child shells inherit
    # LOG_INSTALL=/root/config/install.log and their writes silently fail
    # (post-f2ceed3 [ -w ] guard) — leaving install.log incomplete. After
    # migration, install.log captures the full install lifecycle from all
    # actors. ossh:712-715 unsets LOG_INSTALL/INSTALL_LOG at end of install
    # so user.env stays clean and routine user shells don't append.
    local _bootstrapInstallLog="$HOME/config/install.log"
    local _sharedInstallLog="$CONFIG_PATH/install.log"
    if [ -f "$_bootstrapInstallLog" ] && [ ! -f "$_sharedInstallLog" ]; then
      cp "$_bootstrapInstallLog" "$_sharedInstallLog"
    fi
    touch "$_sharedInstallLog" 2>/dev/null
    chgrp dev "$_sharedInstallLog" 2>/dev/null
    chmod 664 "$_sharedInstallLog" 2>/dev/null
    export INSTALL_LOG="$_sharedInstallLog"
    export LOG_INSTALL="$INSTALL_LOG"

    # F3 absorption: template root-home paths in the just-saved shared
    # config so non-root users can source it without hitting /root paths
    # they can't read. Was at ossh:704-732 in ossh.install.continue.local
    # (post-state-machine fixup); moved here so the state machine owns it.
    if [ -f "$CONFIG" ]; then
      local _rootHome="$HOME"
      local _sedFlag="-i"
      if [ "$(uname -s)" = "Darwin" ]; then _sedFlag="-i ''"; fi
      eval sed $_sedFlag "'s|${_rootHome}/config|\$HOME/config|g'" "'$CONFIG'"
      eval sed $_sedFlag "'s|${_rootHome}/oosh|\$HOME/oosh|g'" "'$CONFIG'"
      if [ -n "$OOSH_BRANCH" ] && [ "$OOSH_BRANCH" != "dev" ]; then
        eval sed $_sedFlag "'s|${_rootHome}/${OOSH_BRANCH}|\$HOME/oosh|g'" "'$CONFIG'"
      fi
      local _configDir
      _configDir="$(dirname "$CONFIG")"
      for _f in "$_configDir"/oosh.env "$_configDir"/log.env; do
        if [ -f "$_f" ]; then
          eval sed $_sedFlag "'s|${_rootHome}/config|\$HOME/config|g'" "'$_f'"
          eval sed $_sedFlag "'s|${_rootHome}/oosh|\$HOME/oosh|g'" "'$_f'"
          if [ -n "$OOSH_BRANCH" ] && [ "$OOSH_BRANCH" != "dev" ]; then
            eval sed $_sedFlag "'s|${_rootHome}/${OOSH_BRANCH}|\$HOME/oosh|g'" "'$_f'"
          fi
          eval sed $_sedFlag "'s|OOSH_WORKTREE_BASE=\"${_rootHome}\"|OOSH_WORKTREE_BASE=\"\$HOME\"|g'" "'$_f'"
        fi
      done
    fi


    # sharedConfig ownership: re-chown the TOP-LEVEL dir to developking:dev
    # (the dev-group canonical layout wants sharedConfig owned by developking,
    # not the installing user). NOT recursive: existing files keep their
    # writer's ownership (all group=dev via the setgid bit below), and new
    # files written by any dev-group user inherit group=dev — that's the
    # canonical Unix shared-dir model.
    #
    # The previous `chown -R developking:dev` was too aggressive: when
    # `ossh install <host> <user>` re-runs state 31 on a host where some
    # OTHER user (e.g. GHA's `runner`) had already written to sharedConfig
    # via `~/config` symlink during a prior install, the -R stomped on
    # those files' ownership. Subsequent writes from the original writer
    # failed with `Permission denied` because they were no longer the
    # owner, and on existing shells they didn't have the dev group in
    # their effective group list either (group adds only take effect on
    # next login). Bisected on the 2026-05-19 macOS CI run 26095749022,
    # Phase A.4 where `/Users/runner/config/setup.color.env: Permission
    # denied` blocked bash-user's install — runner-written files from
    # bootstrap got re-chowned to developking:dev by state 31 during
    # Phase A.1b's `ossh install macos test`.
    chown developking:dev "$CONFIG_PATH"
    chmod g+s "$CONFIG_PATH"
    chmod -R g+w "$CONFIG_PATH"

    # Add the installing user to the dev group (so they can write to
    # sharedConfig). Skip when paramerUser is empty/root — `user
    # group.add` would error on an empty username, and root is already
    # member of every group on macOS / unnecessary on Linux.
    if [ -n "$paramerUser" ] && [ "$paramerUser" != "root" ]; then
      user group.add dev "$paramerUser"
    fi

    # `init` symlink — convenience pointer so `~/init/oosh` resolves
    # to the cloned init script. Absolute paths (the old relative
    # `cd /root` + `ln -s oosh/init init` block was Linux-only and
    # left `init` in whatever cwd happened to be when `cd /root`
    # failed on macOS). Idempotent.
    if [ ! -L "$HOME/init" ]; then
      [ -e "$HOME/init" ] && rm -rf "$HOME/init"
      ln -s "$HOME/oosh/init" "$HOME/init"
    fi

    # Diagnostic listing — portable (no Linux-specific `/home/shared`,
    # no `tree` dependency, no `/root` listing).
    ls -la "$onceShBase/${OOSH_BRANCH}"

    # `~/config` and `~/oosh` are already symlinks — set up above by
    # private.oo.user.shared.symlinks.ensure (oo:1740) which is the
    # single source of truth for that pair. The previous duplicate
    # `cd /root; ln -s … config; rm -rf oosh; ln -s … oosh` here was
    # leftover from before that helper was extracted; it tried to do
    # the same work via relative paths after `cd /root`, which on
    # macOS landed in the wrong directory (no /root on macOS — root's
    # home is /var/root). Removed.
    # ─── state 31 [5/5: dev.repo.cloned] ─────────────────────────────────
    # Post-body verification: without this, a silent failure in git clone /
    # worktree add leaves the state machine thinking state 31 succeeded while
    # $sharedOosh never materialized, causing downstream user.oosh.install to
    # time out polling for a dir that's never going to appear.
    if [ ! -d "$onceShBase/${OOSH_BRANCH}" ]; then
      error.log "state 31 [5/5: dev.repo.cloned]: $onceShBase/${OOSH_BRANCH} missing — check git clone/worktree-add logs above"
      create.result 1 "state 31 step 5/5 failed: dev folder not created at $onceShBase/${OOSH_BRANCH}"
      return $(result)
    fi

    # Seed /<basehome>/shared/.ssh/ via the same primitive the caller-side
    # install path uses (single source of truth). Idempotent. Was followed
    # by a defensive re-invocation at ossh:735-743 (F4) — that's gone now;
    # this call is fail-loud so any silent failure halts state 31.
    ossh config.shared.github.create 2>&1
    if ! ossh config.get github.com "$dir/shared/.ssh/config" >/dev/null 2>&1; then
      error.log "state 31 [5/5: dev.repo.cloned]: Host github.com missing from $dir/shared/.ssh/config after ossh config.shared.github.create"
      create.result 1 "state 31 step 5/5 failed: shared SSH not seeded"
      return $(result)
    fi

    # Retry user.oosh.install for developking now that $sharedOosh exists.
    # Step 1's `user create developking` auto-invoked user.oosh.install but
    # it early-returned (user:774) because $sharedOosh isn't created until
    # step 5's git clone. Without this retry, developking has no
    # ~/oosh + ~/config symlinks and no bashrcTemplate — boss-as-developking
    # logs in to a plain bash shell with no OOSH completion. With this
    # retry, developking gets the same OOSH login experience as any other
    # user. Boss-experience invariant verified below.
    #
    # Use the dispatched form `user oosh.install` (not the dotted
    # `user.oosh.install` function name) — state 31's check function runs
    # in a context where the user script's functions aren't directly in
    # scope, but the `user` dispatcher is. Same pattern as step 1's
    # `user create developking`.
    user oosh.install developking 2>&1
    if [ ! -L "$devHome/oosh" ] || [ ! -L "$devHome/config" ]; then
      error.log "state 31 [5/5: dev.repo.cloned]: developking missing OOSH symlinks after retry — boss-as-developking will get plain bash on login"
      create.result 1 "state 31 step 5/5 failed: developking OOSH home not finalized"
      return $(result)
    fi

    create.result 0 "root.shared.dev.folder.created" $1
  fi

  

  #check.debug.level 3
  return $(result)
}

private.check.root.dev.keys.installed() {
  return 0
}
private.check.root.installation.done() {
  # Install bashrcTemplate so root's interactive shell initializes oosh
  if [ -f "$OOSH_DIR/templates/user/bashrcTemplate" ]; then
    if [ -f "$HOME/.bashrc" ] && [ ! -f "$HOME/.bashrc.pre-oosh" ]; then
      cp "$HOME/.bashrc" "$HOME/.bashrc.pre-oosh"
    fi
    cp "$OOSH_DIR/templates/user/bashrcTemplate" "$HOME/.bashrc"
  fi

  # Ensure login shell is bash (Alpine defaults to ash)
  local currentShell bashPath
  if [ -z "$OOSH_OS" ]; then
    case "$OSTYPE" in
      darwin*) OOSH_OS="darwin" ;;
      linux*)  OOSH_OS="linux-gnu" ;;
    esac
  fi
  if [ "$OOSH_OS" = "darwin" ]; then
    currentShell=$(dscl . -read /Users/root UserShell 2>/dev/null | awk '{print $2}')
  else
    currentShell=$(getent passwd root 2>/dev/null | cut -d: -f7)
  fi
  bashPath=$(command -v bash 2>/dev/null)
  if [ -n "$bashPath" ] && [ "$currentShell" != "$bashPath" ]; then
    if command -v chsh >/dev/null 2>&1; then
      chsh -s "$bashPath" root
    elif [ -w /etc/passwd ]; then
      sed -i 's|^root\(.*\):[^:]*$|root\1:'"$bashPath"'|' /etc/passwd
    fi
  fi

  # Ensure shared log config has normal log level for interactive sessions
  # (install may have persisted a low level from the install process)
  export LOG_LEVEL=3
  export LOG_LEVEL_RESET=3
  config save log LOG

  return 0
}

private.check.user.shared.dev.folder.linked() {
  local devHome="$( user get home developking )"
  if [ -z "$devHome" ]; then
    error.log "user developking does not yet exist"
    return 2
  fi
  this.absolutePath "$devHome/.."
  info.log "absolutePath of \"$devHome\..\": $RESULT"
  local dir="$RESULT"

    export CONFIG="$dir/shared/EAMD.ucp/Scenarios/localhost/EAM/1_infrastructure/Once.sh/sharedConfig/user.env"
    export CONFIG_FILE="user.env"
    export CONFIG_PATH="$dir/shared/EAMD.ucp/Scenarios/localhost/EAM/1_infrastructure/Once.sh/sharedConfig"
    # Resolve OOSH_DIR from root's symlink (set during root install to the
    # correct branch folder). private.this.path.canonical (this:144) is the
    # portable resolver — see oo.mode for the macOS BSD-readlink rationale.
    export OOSH_DIR=$(private.this.path.canonical "$HOME/oosh")
    export OOSH_MODE=$(basename "$OOSH_DIR")

    cd $HOME
    if [ -L config ]; then
      success.log "config is alredy a link"
      ls -al config
      return 0
    else
      mv config "config.orig"
      ln -s "$CONFIG_PATH" config
      ln -s "$OOSH_DIR" oosh
    fi

  return 0
}

private.check.user.state.machine.synced.with.root() {
  return 0
}

private.check.headless.setup.started() {
  return 0
}

private.check.headless.setup.finished() {
  return 0
}

private.check.once.setup.started() {
  if command -v once >/dev/null 2>&1; then
    once init
  fi
  #once set.domain localhost
  #once stage.next
  return 0
}

private.check.once.setup.finished() {
  return 0
}

private.check.finished() {
  return 0
}

oo.install.dev.keys() {
    return 0
}

private.install.dev.configs() {
  # IdentityFile uses the literal `~` form (per-user expansion at SSH read
  # time) and points at the canonical developking key location placed by
  # osshLayout.role.developking. Pre-Phase-A.5 this used
  # "$dir/developking/.ssh/id_rsa" which expands to a hardcoded
  # /home/developking/.ssh/id_rsa — only valid on machines where that
  # specific path exists, broken on every other user. The literal `~` keeps
  # the block portable across all four users (root, developking, oosh-user,
  # bash-user) without rewriting per-user. Phase A.8: emit via the canonical
  # ossh.config.create primitive (identitiesOnly keyword added Phase A.8).
  if ! ossh config.get github.com >/dev/null 2>&1; then
    success.log "creating config github.com"
    ossh config.create github.com git@github.com:22 "~/.ssh/ids/ssh.developking/id_rsa" identitiesOnly
    ossh config.save.last
    ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null
  fi

  return 0
}

oo.tmp.cleanup.testing() { # # clean test BE CAREFULL removes oosh and SSH KEYS completly from the system
    sudo $HOME/oosh/init/deinstall.oosh
    sudo rm -rf $HOME/config.initial
    sudo rm -rf $HOME/oosh.http
    sudo rm -rf $HOME/ssh.root.u*
    sudo userdel -r developking
    #sudo rm -rf /home/shared
    rm config
    mv config.initial config
    source ~/config/user.env
    #oo update
    rm -rf oosh.http
    sudo rm -rf .ssh
    init/deinstall.oosh
}











oo.install.dev() # # DEPRECATED install the official dev oosh. GitHub keys required
{
  error.log "oo.install.dev() is deprecated"

  cd $OOSH_DIR
  cd ..

  check dir /var/dev not exists \
    call once su.mkdir.dev $USER
  
  private.install.dev.configs
  # ossh config.create 2cuGitHub git@github.com:Cerulean-Circle-GmbH/once.sh.git '~/.ssh/ids/ssh.marcel.iMac/private_key/donges.mcdonges.fritz.box.private_key'
  # ossh config.save.last
  ossh pull.id iMac

  cd /var/dev/Workspaces/2cuGitHub
  check dir /var/dev/Workspaces/2cuGitHub/once.sh/.git not exists \
    call git clone git@github.com:Cerulean-Circle-GmbH/once.sh.git
  check dir ~/oosh exists \
    call mv ~/oosh/ ~/oosh.https
  ln -s /var/dev/Workspaces/2cuGitHub/once.sh ~/oosh
  check dir ~/init.bak exists \
    call rm -r ~/init.bak
  check dir ~/init.bak not exists \
    call mv ~/init ~/init.bak

  
}

## oo.mode.fullDebug — removed: use `oo mode fullDebug` (worktree-based switching)

oo.cmd() # <cmd> <?packageName> # checks if <cmd> is installed and installes it with: $SUDO $PM <?packageName>
{
    current=$1
    shift
    package=$1

    if [ -z "$1" ]; then
        package=$current
    else
      shift
      package=$1
    fi   
    
    if [ -z "$OOSH_PM" ]; then
      console.log "no PM found...checking" 
      oo.pm.discover
    fi
    if ! [ -x "$(command -v $current)" ]; then
        console.log "no $current"
        if [ -n "$OOSH_PM" ]; then
          case $current in
            eamd)
              once load $current tla/EAMD/UcpComponentSupport/1.0.0/src/sh/eamd
              export PATH="$PATH:$ONCE_LOAD_DIR"
              hash -d eamd    #clears the command cache for eamd
              #hash -r   #clears the command cache completley
              ;;
            oosh)
              once load $current com/ceruleanCircle/EAM/1_infrastructure/OOSH/1.0.0/src/sh/oosh
              ;;
            once)
              once load once.sh tla/EAM/layer1/Thinglish/Once/latestServer/src/sh/once.sh
              cd $ONCE_LOAD_DIR
              mv once.sh once
              private.stage status
              ;;
            # npm)
            #   # if ! which $current > /dev/null; then
            #     once.su.npm.install
              # else
              #   console.log "$current allready installed!";
              # fi
              # exit;
            # ;;
            brew)
              if [ "$(uname -s)" = "Darwin" ]; then
                console.log "Installing Homebrew..."
                NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
                for _brew_path in /opt/homebrew/bin /usr/local/bin; do
                  if [ -x "$_brew_path/brew" ]; then
                    export PATH="$_brew_path:$PATH"
                    break
                  fi
                done
                oo.pm.discover
              else
                warn.log "brew is only available on macOS"
              fi
              ;;
            mkcert)
              once.su.mkcert.install
              ;;
            update)
              if [ -z "$OOSH_PM_UPDATED" ]; then
                if command -v apt-get >/dev/null 2>&1; then
                  OOSH_PM_UPDATED="$SUDO apt-get update"
                elif command -v dnf >/dev/null 2>&1; then
                  OOSH_PM_UPDATED="$SUDO dnf makecache"
                elif command -v yum >/dev/null 2>&1; then
                  OOSH_PM_UPDATED="$SUDO yum makecache"
                else
                  warn.log "No known package manager found for update"
                fi
                if [ -n "$OOSH_PM_UPDATED" ]; then
                  $OOSH_PM_UPDATED
                fi
              else
                warn.log "Package manager already updated with: $OOSH_PM_UPDATED"
              fi
              ;;
            errno)
              oo cmd python3 python3
              ;;
            *)
              if [ "$OOSH_PM" = "brew install" ]; then
                # Homebrew refuses to run as root since v2.0
                # ("Running Homebrew as root is extremely dangerous and no
                # longer supported"). When init/oosh has sudo-re-exec'd to
                # root before reaching `oo cmd <pkg>` (the standard install
                # path), drop privileges back to $SUDO_USER — sudo preserves
                # this var to the original invoking user. -H sets HOME to
                # that user's home so brew finds its config + cache there.
                # If we're truly root (no $SUDO_USER), fall through and let
                # brew error out loudly — the verify post-condition in
                # ossh.prereqs.install will surface the diagnostic.
                if [ "$(id -u)" = "0" ] && [ -n "$SUDO_USER" ]; then
                  sudo -u "$SUDO_USER" -H brew install $package
                else
                  brew install $package
                fi
              else
                $SUDO $OOSH_PM $package
              fi
            esac
        else
            console.log "no package manger"
        fi
        if [ -n "$1" ]; then
          shift
        fi
    else
      # Command exists but may be a BusyBox stub with limited functionality
      case $current in
        wget)
          if wget --help 2>&1 | grep -q "BusyBox"; then
            console.log "BusyBox wget detected — installing full wget"
            $SUDO $OOSH_PM wget
          fi
          ;;
      esac
    fi
    RETURN=$1
}

oo.prereqs.install() # # install the oosh install-time prereqs locally — git and curl — plus bash 4+ if the running shell is older
{
  # Ensure we know the PM. Idempotent — `oo cmd` also calls this if needed.
  [ -z "$OOSH_PM" ] && oo pm.discover

  # Delegate to the existing `oo cmd <pkg>` primitive — it already handles
  # "already installed" (command -v check), calls $SUDO $OOSH_PM <pkg>, and
  # knows per-distro package-name quirks. No new package-install logic here.
  local pkg rc=0
  for pkg in curl git; do
    if ! oo cmd "$pkg"; then
      error.log "oo.prereqs.install: failed to install $pkg"
      rc=1
    fi
  done

  # bash 4+ case is subtle: `oo cmd bash` short-circuits when bash is
  # already in PATH (oo:1689 `command -v` check), and macOS ships bash 3.2
  # at /bin/bash — so oo cmd bash would NO-OP and the upgrade never fires.
  # Bypass the existence check by dispatching directly to the PM. Same
  # case-sudo split oo cmd does at oo:1756-1759 (brew runs as caller; all
  # other PMs need $SUDO).
  if [ "${BASH_VERSINFO[0]:-0}" -lt 4 ]; then
    [ -z "$OOSH_PM" ] && oo pm.discover
    case "${OOSH_PM:-}" in
      "brew install"*) $OOSH_PM bash || rc=1 ;;
      *)               $SUDO $OOSH_PM bash || rc=1 ;;
    esac
    important.log "bash 4+ installed — re-exec the installer from a fresh shell to pick it up"
  fi
  return $rc
}

oo.pm() # # checks if the pm is well configured
{
  if [ -n "$OOSH_PM" ]; then
    console.log "package manager is set:"
    console.log "for OOSH: $OOSH_PM"
    console.log "for ONCE: $ONCE_PM"
    if [ "$OOSH_PM" = "$ONCE_PM" ]; then
      success.log "package manager well configured"
      return 0
    else
      warn.log "package manager differ!
      use
      
        oo pm.discover
      
      to fix that
      "
      return 1
    fi
  fi
  OOSH_PM=
  private.check.all.pm
  console.log "PM: $OOSH_PM"

  RETURN=$1
}

oo.pm.discover() # # finds out OS and corresponding package manager (pm)
{
  OOSH_PM=
  private.check.all.pm
  console.log "OOSH_PM: $OOSH_PM"
  config save

  RETURN=$1
}

private.check.pm()             # checks for a package manager
{

    local packageManager=$1
    local packageManagerCommand=$2


    if [ -z "$packageManagerCommand" ]; then
        package=$packageManager
    fi   
    if ! [ -x "$(command -v $packageManager)" ]; then
        debug.log "no $packageManager"
    else
        if [ -z "$OOSH_PM" ]; then
            export OOSH_PM=$packageManagerCommand
            export OS_CMD_GROUP_ADD=$3
            export OS_CMD_USER_ADD=$4
            echo "Package Manager found: using $OOSH_PM somePackage"
            if [ "$packageManager" = "apt-get" ]; then
                if [ -z "$OOSH_PM_UPDATED" ]; then
                  OOSH_PM_UPDATED="$SUDO apt-get update"
                  #if [ "$ONCE_PRIVILEGE" = root ]; then
                    $OOSH_PM_UPDATED
                  # else 
                  #   PM="sudo $PM"
                  # fi
                else
                  echo "in case of installation errors try to call: apt-get update"
                fi
            fi
            config save os.commands "OS_CMD"
        fi
    fi
}

private.check.all.pm()         # adds tools and configurations to package manager (brew, apt-get, addgroup, adduser, dpkg, pkg)
{

    private.check.pm brew "brew install"    
    #once.check.pm apt "apt add"
    private.check.pm apt-get "apt-get -y install" "groupadd -f" "useradd -g dev"
    private.check.pm dnf "dnf -y install" "groupadd -f" "useradd -g dev"
    private.check.pm yum "yum -y install" "groupadd -f" "useradd -g dev"
    private.check.pm apk "apk add" "addgroup" "adduser -g dev"
    private.check.pm dpkg "dpkg install"
    private.check.pm pkg "pkg install"
    private.check.pm pacman "pacman -S"

 
}

oo.install() { # <scriptClass> <path> # installs an external oo script from <path> into oosh/external as a link
  local class="$1"
  shift
  local path="$1"
  shift

  if [ -z "$path" ]; then
    this.absolutePath .
    path=$RESULT
  fi

  this.absolutePath $path
  path=$RESULT

  debug.log "path: $path"
  if [ -L "$OOSH_DIR/external/$class" ]; then
    console.log "updating links: $OOSH_DIR/external/$class -> $path/$class"
    rm $OOSH_DIR/external/$class
    ln -s $path/$class $OOSH_DIR/external/$class
  fi

  if [ -f "$path/$class" ]; then
    console.log "installing link: $class -> $path/$class"
    rm $OOSH_DIR/external/$class
    ln -s "$path/$class" "$OOSH_DIR/external/$class"
  fi

  if [ -f "$OOSH_DIR/$class" ]; then
    console.log "installing: $class -> $path/$class"
    mv "$OOSH_DIR/$class" "$path/$class"
    ln -s "$path/$class" "$OOSH_DIR/external/$class"
  fi

}

oo.install.completion() {
  compgen -d $OOSH_DIR/$1
}

oo.cmd.find() { # <cmd> # finds the <cmd> on the apt repositories
  private.oo.find.cmd "$@"; }

private.oo.find.cmd() { # <cmd> # finds the <cmd> on the apt repositories
  oo cmd apt-file
  apt-file update
  apt-file search --regexp ".*$1$"
}


oo.su() {
  if [ "$USER" = "root" ]; then
    cd $OOSH_DIR/su
    echo ${GREEN}root command are:
    ls -al
    echo ${NORMAL}
    bash
  else
    error.log "You are not root! 
        ${BOLD_YELLOW}USER: $USER"

    warn.log "trying to switch to root:
    
    Type user $USER"
    sudo su
  fi
}

oo.usage()
{
  local this=${0##*/}
  echo "You started" 
  echo "$0

  is the management interface for the oosh environment
  create new classes with: oo new <class>

  The oosh lifecycle
    1. oo mode.dev  # All development exclusively in dev mode
    2. oo update    # Updating oosh environment to the latest
    3. oo commit    # Commit local changes to the oosh dev global environment
    4. oo release   # Publish the dev environment to the main environment as well as switching to main mode. This is the commercial mode for using the oosh environment
    5. oo mode.dev  # Switch back do dev mode to start development cycle again


  ${CYAN}\$OOSH_DIR${NORMAL}           is where it is installed: $OOSH_DIR
  ${CYAN}\$OOSH_DIR/${GREEN}init/oosh${NORMAL} is the ${YELLOW}initial${NORMAL} installer based on the ${GREEN}sh${NORMAL} Shell
  ${CYAN}\$OOSH_DIR/${GREEN}this${NORMAL}      is ${YELLOW}starting${NORMAL} the oosh environment and provides common oo functionality

  Usage:
  $this: command   description and Parameter

      usage     prints this dialog while it will print the status when there are no parameters          
      ----      --------------------------"
  this.help
  echo "
  ${GREEN}Examples${NORMAL} 
    $this new myNewCommand
    $this update
  "
}

oo.parameter.completion.stage() {
  echo "dev"
  echo "testing"
}

oo.mode.base.set.completion.path() { compgen -d "$1"; }

oo.parameter.completion.baseBranch() {
  # Delegates to private.oo.completion.worktree.branches — see its
  # docstring for the create.result / worktree-list rationale.
  private.oo.completion.worktree.branches
}

oo.parameter.completion.branch() {
  # Delegates to private.oo.completion.worktree.branches — see its
  # docstring for the create.result / worktree-list rationale.
  private.oo.completion.worktree.branches
}

oo.parameter.completion.cmd() {
  compgen -c "$1" 2>/dev/null | head -20
}

oo.parameter.completion.packageName() {
  compgen -c "$1" 2>/dev/null | head -20
}

oo.start() # <method> <parameter> # default start and parameter processing
{
  #echo "sourcing init"
  source this

  # if [ -z "$1" ]; then
  #   status.discover "$@"
  #   return 0
  # fi

  this.start "$@"
}

oo.start "$@"

