L0b (factory bootstrap): - bootstrap.ps1 (Windows): pre-checks, interactive config, clone .factory, home.toml - bootstrap.sh (Linux/Mac): same functionality for bash - Idempotent re-run with home.toml verification L1 (Go CLI): - factory.yaml: manifest schema with components, deps, health-checks - cmd/factory: Cobra CLI (install/update/status/diagnose) - pkg/config: home.toml reader - pkg/manifest: factory.yaml parser + topological sort - pkg/health: health-check logic (file/dir/mcp_tool/command) Scripts: - build.sh: cross-compile for linux/win/darwin × amd64/arm64 - build.ps1: PowerShell cross-compile Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
183 lines
5.5 KiB
Bash
183 lines
5.5 KiB
Bash
#!/usr/bin/env bash
|
|
# L0b Factory Bootstrap — Linux/macOS
|
|
# Auto-generated from install-log.md field-test 2026-05-06
|
|
|
|
set -euo pipefail
|
|
|
|
GITEA_DEFAULT="https://git.kzntsv.site"
|
|
ORG_DEFAULT="OpeItcLoc03"
|
|
FACTORY_REPO_DEFAULT="factory"
|
|
HOME_TOML="$HOME/.config/factory/home.toml"
|
|
PROJECTS_DIR_DEFAULT="$HOME/projects"
|
|
|
|
step() { echo -e "\n▶ $*"; }
|
|
ok() { echo "✓ $*"; }
|
|
skip() { echo "⊘ $*"; }
|
|
gap() { echo "⚠ $*" >&2; }
|
|
|
|
check_cmd() {
|
|
command -v "$1" &>/dev/null
|
|
}
|
|
|
|
# Pre-checks
|
|
step "L0b Pre-checks"
|
|
|
|
missed=()
|
|
check_cmd git || missed+=("git (install: brew install git / apt install git)")
|
|
check_cmd curl || missed+=("curl (install: brew install curl / apt install curl)")
|
|
check_cmd mise || missed+=("mise (install: curl https://mise.run | bash)")
|
|
check_cmd claude || missed+=("claude (install: curl https://claude.ai/install.sh | bash)")
|
|
|
|
if [[ ${#missed[@]} -gt 0 ]]; then
|
|
gap "Missing dependencies:"
|
|
for m in "${missed[@]}"; do
|
|
echo " - $m"
|
|
done
|
|
echo ""
|
|
echo "Install missing tools and re-run bootstrap."
|
|
exit 1
|
|
fi
|
|
ok "Pre-checks passed"
|
|
|
|
# Check if already bootstrapped
|
|
if [[ -f "$HOME_TOML" ]]; then
|
|
skip "Already bootstrapped. Re-run mode: verify only."
|
|
|
|
if [[ $(grep -oP "projects_dir\s*=\s*'\K[^']+" "$HOME_TOML" 2>/dev/null) ]]; then
|
|
existing_dir=$(grep -oP "projects_dir\s*=\s*'\K[^']+" "$HOME_TOML")
|
|
echo " projects_dir: $existing_dir"
|
|
|
|
if [[ -d "$existing_dir/.factory" ]]; then
|
|
ok ".factory exists"
|
|
else
|
|
gap ".factory missing at $existing_dir/.factory"
|
|
exit 1
|
|
fi
|
|
fi
|
|
ok "Bootstrap verified. Exiting (no-op)."
|
|
exit 0
|
|
fi
|
|
|
|
# Interactive: ask for configuration
|
|
step "Configuration"
|
|
|
|
read -rp "Gitea URL [$GITEA_DEFAULT]: " gitea
|
|
gitea=${gitea:-$GITEA_DEFAULT}
|
|
|
|
read -rp "Gitea org/user [$ORG_DEFAULT]: " org
|
|
org=${org:-$ORG_DEFAULT}
|
|
|
|
read -rp "Factory repo name [$FACTORY_REPO_DEFAULT]: " factoryRepo
|
|
factoryRepo=${factoryRepo:-$FACTORY_REPO_DEFAULT}
|
|
|
|
read -rp "Projects directory [$PROJECTS_DIR_DEFAULT]: " projectsDir
|
|
projectsDir=${projectsDir:-$PROJECTS_DIR_DEFAULT}
|
|
|
|
read -rp "AI client [claude-code/copilot-cli/gemini-cli] (default: claude-code): " client
|
|
client=${client:-claude-code}
|
|
|
|
# Validate projects_dir
|
|
if [[ ! -d "$projectsDir" ]]; then
|
|
read -rp "Directory '$projectsDir' does not exist. Create? [Y/n] " create
|
|
if [[ "$create" != "n" && "$create" != "N" ]]; then
|
|
mkdir -p "$projectsDir"
|
|
ok "Created $projectsDir"
|
|
else
|
|
gap "Cannot continue without projects_dir"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Clone .factory
|
|
step "Cloning .factory repo"
|
|
|
|
factoryLocal="$projectsDir/.factory"
|
|
factoryUrl="$gitea/$org/$factoryRepo.git"
|
|
|
|
if [[ -d "$factoryLocal" ]]; then
|
|
skip ".factory already exists, skipping clone"
|
|
|
|
if [[ ! -d "$factoryLocal/.git" ]]; then
|
|
gap "$factoryLocal exists but is not a git repo. Remove it and re-run."
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "Cloning from: $factoryUrl"
|
|
|
|
# Check if we have credentials for gitea
|
|
need_auth=false
|
|
if ! git ls-remote "$factoryUrl" &>/dev/null; then
|
|
need_auth=true
|
|
fi
|
|
|
|
if [[ "$need_auth" == true ]]; then
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo " PAT REQUIRED"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
echo "1. Open in browser: $gitea/user/settings/applications"
|
|
echo "2. Generate PAT with scopes: read:repository, write:repository, read:user"
|
|
echo "3. Press Enter when ready..."
|
|
echo ""
|
|
read -r
|
|
|
|
echo "Cloning with Git Credential Manager prompt..."
|
|
fi
|
|
|
|
git clone "$factoryUrl" "$factoryLocal" || {
|
|
gap "Clone failed. Check URL and credentials."
|
|
exit 1
|
|
}
|
|
ok "Cloned to $factoryLocal"
|
|
fi
|
|
|
|
# Write home.toml
|
|
step "Writing ~/.config/factory/home.toml"
|
|
|
|
configDir="$HOME/.config/factory"
|
|
mkdir -p "$configDir"
|
|
|
|
installed_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
cat > "$configDir/home.toml" <<EOF
|
|
# factory home — single source of machine-level state
|
|
# auto-generated by L0b bootstrap; safe to edit by hand
|
|
|
|
projects_dir = '$projectsDir'
|
|
client = '$client'
|
|
installed_at = '$installed_at'
|
|
EOF
|
|
|
|
ok "Wrote $HOME_TOML"
|
|
|
|
# Print next steps
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo " L0b COMPLETE"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
echo "Factory bootstrapped. Next steps (manual):"
|
|
echo ""
|
|
echo "1. Start Claude Code:"
|
|
echo " cd $factoryLocal"
|
|
echo " claude"
|
|
echo ""
|
|
echo "2. In Claude, run:"
|
|
echo " /login"
|
|
echo " /plugin install superpowers@claude-plugins-official"
|
|
echo " /plugin install context7@claude-plugins-official"
|
|
echo " /reload-plugins"
|
|
echo " /exit"
|
|
echo ""
|
|
echo "3. Run setup skills:"
|
|
echo " cd $projectsDir"
|
|
echo " claude"
|
|
echo " /setup-projects-meta"
|
|
echo " /setup-interns"
|
|
echo " /setup-tasks"
|
|
echo " /setup-wiki"
|
|
echo ""
|
|
echo "See .wiki/concepts/factory-bootstrap.md for full design."
|
|
echo "Re-run this script anytime — it's idempotent."
|