diff --git a/scripts/newproject b/scripts/newproject index 8273def..1196d76 100755 --- a/scripts/newproject +++ b/scripts/newproject @@ -1,26 +1,32 @@ #!/usr/bin/env bash # -# newproject - Interactive project scaffolding tool +# newproject - Interactive project scaffolding wizard # Uses gum for beautiful terminal UI # set -e -# Colors -CYAN='\033[0;36m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' # No Color - # Config PROJECTS_DIR="$HOME/Projects" -TEMPLATE_REPO="git@git.htsn.io:hutson/template-project.git" +STIGNORE_FILE="$PROJECTS_DIR/.stignore" # Source central config if available [ -f ~/.secrets ] && source ~/.secrets [ -f ~/.hosts ] && source ~/.hosts +####################################### +# Check/Install gum +####################################### +if ! command -v gum &> /dev/null; then + echo "Installing gum (terminal UI toolkit)..." + if command -v brew &> /dev/null; then + brew install gum + else + echo "❌ Please install gum: https://github.com/charmbracelet/gum" + exit 1 + fi +fi + ####################################### # Header ####################################### @@ -69,11 +75,16 @@ PROJECT_TYPE=$(gum choose \ --header "Project Type") ####################################### -# Project Description +# Project Brief (Multi-line) ####################################### echo "" -gum style --foreground 245 "Brief project description:" -PROJECT_DESC=$(gum input --placeholder "A brief description of what this project does" --width 60) +gum style --foreground 245 "Describe your project (Ctrl+D or Esc when done):" +gum style --foreground 240 --italic "Include goals, features, tech preferences, constraints..." +echo "" +PROJECT_BRIEF=$(gum write --placeholder "Describe your project in detail..." --width 70 --height 8) + +# Extract first line as short description +PROJECT_DESC=$(echo "$PROJECT_BRIEF" | head -1) ####################################### # MCP Selection @@ -81,7 +92,6 @@ PROJECT_DESC=$(gum input --placeholder "A brief description of what this project echo "" gum style --foreground 245 "Select MCPs to enable (space to select, enter to confirm):" -# Define available MCPs with descriptions MCPS=$(gum choose --no-limit \ "exa (web search, research)" \ "Ref (documentation lookup)" \ @@ -96,18 +106,80 @@ MCPS=$(gum choose --no-limit \ --selected "exa (web search, research),Ref (documentation lookup)") ####################################### -# Git Options +# Git Remote Options ####################################### echo "" gum style --foreground 245 "Git repository options:" GIT_INIT=$(gum choose "Yes" "No" --header "Initialize git repository?") -GITEA_CREATE="No" +GIT_REMOTE="None" if [ "$GIT_INIT" = "Yes" ]; then - GITEA_CREATE=$(gum choose "Yes" "No" --header "Create repo on Gitea (git.htsn.io)?") + GIT_REMOTE=$(gum choose \ + "Gitea (git.htsn.io) - Private" \ + "GitHub (github.com) - Public" \ + "Both (Gitea + GitHub)" \ + "None (local only)" \ + --header "Where to push?") fi +####################################### +# Syncthing Options +####################################### +echo "" +gum style --foreground 245 "Syncthing sync options:" + +SYNCTHING_OPT=$(gum choose \ + "Exclude from sync (recommended for git repos)" \ + "Include in sync" \ + --header "Sync this project via Syncthing?") + +####################################### +# Network/Traefik Options +####################################### +echo "" +gum style --foreground 245 "Does this project need web access (subdomain routing)?" + +NETWORK_ACCESS=$(gum choose \ + "No web access needed" \ + "Local network only (10.10.10.x)" \ + "Local + Tailscale (remote access)" \ + "Public Internet (via Cloudflare)" \ + --header "Network access level") + +SUBDOMAIN="" +DEPLOY_TARGET="" +if [ "$NETWORK_ACCESS" != "No web access needed" ]; then + echo "" + gum style --foreground 245 "Subdomain for this project:" + SUBDOMAIN=$(gum input --placeholder "$PROJECT_NAME" --value "$PROJECT_NAME" --width 30) + SUBDOMAIN="${SUBDOMAIN}.htsn.io" + + echo "" + gum style --foreground 245 "Deployment target:" + DEPLOY_TARGET=$(gum choose \ + "docker-host (10.10.10.206)" \ + "trading-vm (10.10.10.221)" \ + "saltbox (10.10.10.100)" \ + "Other/Manual" \ + --header "Where will this deploy?") +fi + +####################################### +# Database Options +####################################### +echo "" +gum style --foreground 245 "Database requirements:" + +DATABASE=$(gum choose \ + "None" \ + "SQLite (local file)" \ + "PostgreSQL" \ + "TimescaleDB (time-series)" \ + "Redis (cache/queue)" \ + "PostgreSQL + Redis" \ + --header "Database") + ####################################### # Additional Options ####################################### @@ -116,28 +188,54 @@ gum style --foreground 245 "Additional options:" ADDITIONAL=$(gum choose --no-limit \ "spec-kit (spec-driven development)" \ + "Docker support" \ "pre-commit hooks" \ "GitHub Actions CI" \ - "Docker support" \ + ".env.example (from ~/.secrets)" \ --header "Additional features (space=select)") +####################################### +# License +####################################### +echo "" +gum style --foreground 245 "License:" + +LICENSE=$(gum choose \ + "MIT" \ + "Apache 2.0" \ + "Proprietary (no license file)" \ + "None" \ + --header "License type") + ####################################### # Confirm ####################################### echo "" + +# Build summary +SUMMARY="📋 Project Summary + +Name: $PROJECT_NAME +Type: $PROJECT_TYPE +Path: $PROJECT_PATH + +MCPs: $(echo "$MCPS" | tr '\n' ', ' | sed 's/, $//') + +Git: $GIT_INIT | Remote: $GIT_REMOTE +Syncthing: $SYNCTHING_OPT +Database: $DATABASE +License: $LICENSE" + +if [ -n "$SUBDOMAIN" ]; then + SUMMARY="$SUMMARY +Network: $NETWORK_ACCESS +Subdomain: $SUBDOMAIN +Deploy to: $DEPLOY_TARGET" +fi + gum style --foreground 212 --border-foreground 212 --border rounded \ - --align left --width 60 --margin "1 0" --padding "1 2" \ - "📋 Project Summary" \ - "" \ - "Name: $PROJECT_NAME" \ - "Type: $PROJECT_TYPE" \ - "Path: $PROJECT_PATH" \ - "Description: $PROJECT_DESC" \ - "" \ - "MCPs: $(echo "$MCPS" | tr '\n' ', ' | sed 's/, $//')" \ - "" \ - "Git: $GIT_INIT | Gitea: $GITEA_CREATE" \ - "Extras: $(echo "$ADDITIONAL" | tr '\n' ', ' | sed 's/, $//')" + --align left --width 65 --margin "1 0" --padding "1 2" \ + "$SUMMARY" echo "" CONFIRM=$(gum choose "Create Project" "Cancel" --header "Proceed?") @@ -153,26 +251,35 @@ fi echo "" gum spin --spinner dot --title "Creating project directory..." -- sleep 0.5 mkdir -p "$PROJECT_PATH" +mkdir -p "$PROJECT_PATH/src" +mkdir -p "$PROJECT_PATH/tests" +mkdir -p "$PROJECT_PATH/docs" ####################################### -# Generate .claude/settings.json (MCP config) +# Save Project Brief +####################################### +if [ -n "$PROJECT_BRIEF" ]; then + gum spin --spinner dot --title "Saving project brief..." -- sleep 0.2 + cat > "$PROJECT_PATH/docs/PROJECT_BRIEF.md" << BRIEF_EOF +# $PROJECT_NAME - Project Brief + +$PROJECT_BRIEF + +--- + +*Generated by newproject wizard on $(date '+%Y-%m-%d')* +BRIEF_EOF +fi + +####################################### +# Generate .claude/settings.json ####################################### gum spin --spinner dot --title "Generating MCP configuration..." -- sleep 0.3 mkdir -p "$PROJECT_PATH/.claude" -# Build disabled MCPs list ALL_MCPS=("exa" "Ref" "ticktick" "beeper" "airtable" "claude-in-chrome" "shopping" "proton-mail" "weather") -ENABLED_MCPS="" -# Parse selected MCPs -for mcp in "${ALL_MCPS[@]}"; do - if echo "$MCPS" | grep -qi "^$mcp "; then - ENABLED_MCPS="$ENABLED_MCPS $mcp" - fi -done - -# Generate settings.json with disabled MCPs cat > "$PROJECT_PATH/.claude/settings.json" << 'SETTINGS_EOF' { "mcpServers": { @@ -201,47 +308,54 @@ SETTINGS_EOF ####################################### gum spin --spinner dot --title "Generating CLAUDE.md..." -- sleep 0.3 -# Determine type-specific content case "$PROJECT_TYPE" in python-fastapi) TECH_STACK="Python 3.12+, FastAPI, SQLModel, Pydantic, Alembic" TYPE_CHECK_CMD="mypy --strict src/" LINT_CMD="ruff check src/ && black src/" TEST_CMD="pytest" + INSTALL_CMD="pip install -e '.[dev]'" + DEV_CMD="uvicorn src.main:app --reload" ;; python-generic) TECH_STACK="Python 3.12+" TYPE_CHECK_CMD="mypy --strict src/" LINT_CMD="ruff check src/ && black src/" TEST_CMD="pytest" + INSTALL_CMD="pip install -e '.[dev]'" + DEV_CMD="python -m src.main" ;; typescript-react) TECH_STACK="TypeScript, React 18, Bun, TailwindCSS" TYPE_CHECK_CMD="bun run tsc --noEmit" LINT_CMD="bun run lint" TEST_CMD="bun test" + INSTALL_CMD="bun install" + DEV_CMD="bun run dev" ;; typescript-node) TECH_STACK="TypeScript, Node.js/Bun" TYPE_CHECK_CMD="bun run tsc --noEmit" LINT_CMD="bun run lint" TEST_CMD="bun test" + INSTALL_CMD="bun install" + DEV_CMD="bun run dev" ;; generic) TECH_STACK="[Customize]" TYPE_CHECK_CMD="# Add type check command" LINT_CMD="# Add lint command" TEST_CMD="# Add test command" + INSTALL_CMD="# Add install command" + DEV_CMD="# Add dev command" ;; esac -# Check if spec-kit selected USE_SPECKIT="No" if echo "$ADDITIONAL" | grep -q "spec-kit"; then USE_SPECKIT="Yes" fi -# Generate CLAUDE.md cat > "$PROJECT_PATH/CLAUDE.md" << CLAUDE_EOF # $PROJECT_NAME - Claude Code Guidelines @@ -254,7 +368,7 @@ $PROJECT_DESC **Key Directories**: - \`src/\` - Main source code - \`tests/\` - Test files -- \`docs/\` - Documentation +- \`docs/\` - Documentation (see \`docs/PROJECT_BRIEF.md\` for full context) --- @@ -308,24 +422,15 @@ Run after completing significant features: ## Pre-Commit Checklist \`\`\`bash -# Type safety $TYPE_CHECK_CMD - -# Linting $LINT_CMD - -# Tests $TEST_CMD - -# Code review (for significant changes) -/code-review:code-review \`\`\` --- CLAUDE_EOF -# Add spec-kit section if selected if [ "$USE_SPECKIT" = "Yes" ]; then cat >> "$PROJECT_PATH/CLAUDE.md" << 'SPECKIT_EOF' ## Spec-Driven Development @@ -345,21 +450,18 @@ if [ "$USE_SPECKIT" = "Yes" ]; then SPECKIT_EOF fi -# Add MCP configuration section cat >> "$PROJECT_PATH/CLAUDE.md" << MCP_EOF ## MCP Configuration **Enabled MCPs for this project:** $(echo "$MCPS" | sed 's/^/- /') -**Disabled MCPs** are configured in \`.claude/settings.json\`. To re-enable, remove from the disabled list or use \`/config\` → MCP Servers. +**Disabled MCPs** are configured in \`.claude/settings.json\`. --- ## Central Configuration Reference -This project can access Hutson's central homelab configuration files. These are synced across all machines via Syncthing. - | File | Purpose | Usage | |------|---------|-------| | \`~/.secrets\` | API keys, tokens, credentials | \`source ~/.secrets\` then use \`\$VAR_NAME\` | @@ -370,13 +472,37 @@ This project can access Hutson's central homelab configuration files. These are 1. Add to the central files (\`~/.secrets\` or \`~/.hosts\`) 2. Files sync via Syncthing to all machines 3. Never commit secrets to git - use environment variables +MCP_EOF + +# Add deployment info if applicable +if [ -n "$SUBDOMAIN" ]; then + cat >> "$PROJECT_PATH/CLAUDE.md" << DEPLOY_EOF + +--- + +## Deployment + +| Setting | Value | +|---------|-------| +| **Subdomain** | https://$SUBDOMAIN | +| **Deploy Target** | $DEPLOY_TARGET | +| **Network Access** | $NETWORK_ACCESS | + +**Deploy command:** +\`\`\`bash +./scripts/deploy.sh +\`\`\` +DEPLOY_EOF +fi + +cat >> "$PROJECT_PATH/CLAUDE.md" << 'NOTES_EOF' --- ## Project-Specific Notes [Add project-specific patterns, gotchas, and conventions here] -MCP_EOF +NOTES_EOF ####################################### # Generate README.md @@ -392,10 +518,10 @@ $PROJECT_DESC \`\`\`bash # Install dependencies -# [Add install command] +$INSTALL_CMD # Run development server -# [Add dev command] +$DEV_CMD # Run tests $TEST_CMD @@ -404,6 +530,7 @@ $TEST_CMD ## Development See [CLAUDE.md](CLAUDE.md) for development guidelines and conventions. +See [docs/PROJECT_BRIEF.md](docs/PROJECT_BRIEF.md) for full project context. README_EOF ####################################### @@ -451,6 +578,332 @@ htmlcov/ logs/ GITIGNORE_EOF +####################################### +# Generate .env.example +####################################### +if echo "$ADDITIONAL" | grep -q ".env.example"; then + gum spin --spinner dot --title "Generating .env.example..." -- sleep 0.3 + + cat > "$PROJECT_PATH/.env.example" << 'ENVEX_EOF' +# Copy this to .env and fill in values +# Most values available in ~/.secrets + +# Application +ENV=development +LOG_LEVEL=INFO + +ENVEX_EOF + + # Add relevant env vars based on selections + if echo "$MCPS" | grep -qi "exa"; then + echo "# Exa (web search)" >> "$PROJECT_PATH/.env.example" + echo "EXA_API_KEY=\${EXA_API_KEY}" >> "$PROJECT_PATH/.env.example" + echo "" >> "$PROJECT_PATH/.env.example" + fi + + if [ "$DATABASE" = "PostgreSQL" ] || [ "$DATABASE" = "PostgreSQL + Redis" ] || [ "$DATABASE" = "TimescaleDB (time-series)" ]; then + echo "# Database" >> "$PROJECT_PATH/.env.example" + echo "DATABASE_URL=postgresql://user:password@localhost:5432/$PROJECT_NAME" >> "$PROJECT_PATH/.env.example" + echo "" >> "$PROJECT_PATH/.env.example" + fi + + if [ "$DATABASE" = "Redis (cache/queue)" ] || [ "$DATABASE" = "PostgreSQL + Redis" ]; then + echo "# Redis" >> "$PROJECT_PATH/.env.example" + echo "REDIS_URL=redis://localhost:6379" >> "$PROJECT_PATH/.env.example" + echo "" >> "$PROJECT_PATH/.env.example" + fi +fi + +####################################### +# License +####################################### +if [ "$LICENSE" = "MIT" ]; then + gum spin --spinner dot --title "Adding MIT license..." -- sleep 0.2 + cat > "$PROJECT_PATH/LICENSE" << 'MIT_EOF' +MIT License + +Copyright (c) 2026 Hutson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +MIT_EOF +elif [ "$LICENSE" = "Apache 2.0" ]; then + gum spin --spinner dot --title "Adding Apache 2.0 license..." -- sleep 0.2 + cat > "$PROJECT_PATH/LICENSE" << 'APACHE_EOF' + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + Copyright 2026 Hutson + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +APACHE_EOF +fi + +####################################### +# Docker support +####################################### +if echo "$ADDITIONAL" | grep -q "Docker support"; then + gum spin --spinner dot --title "Adding Docker support..." -- sleep 0.3 + + if [[ "$PROJECT_TYPE" == python* ]]; then + cat > "$PROJECT_PATH/Dockerfile" << 'DOCKER_EOF' +FROM python:3.12-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["python", "-m", "src.main"] +DOCKER_EOF + else + cat > "$PROJECT_PATH/Dockerfile" << 'DOCKER_EOF' +FROM oven/bun:1 + +WORKDIR /app + +COPY package.json bun.lockb ./ +RUN bun install --frozen-lockfile + +COPY . . + +CMD ["bun", "run", "start"] +DOCKER_EOF + fi + + # docker-compose with database if selected + cat > "$PROJECT_PATH/docker-compose.yml" << COMPOSE_EOF +version: '3.8' + +services: + app: + build: . + ports: + - "8000:8000" + environment: + - ENV=development + volumes: + - .:/app +COMPOSE_EOF + + if [ "$DATABASE" = "PostgreSQL" ] || [ "$DATABASE" = "PostgreSQL + Redis" ]; then + cat >> "$PROJECT_PATH/docker-compose.yml" << 'COMPOSE_PG_EOF' + depends_on: + - postgres + + postgres: + image: postgres:16-alpine + environment: + POSTGRES_USER: app + POSTGRES_PASSWORD: changeme + POSTGRES_DB: app + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + +volumes: + postgres_data: +COMPOSE_PG_EOF + fi + + if [ "$DATABASE" = "Redis (cache/queue)" ] || [ "$DATABASE" = "PostgreSQL + Redis" ]; then + cat >> "$PROJECT_PATH/docker-compose.yml" << 'COMPOSE_REDIS_EOF' + + redis: + image: redis:7-alpine + ports: + - "6379:6379" +COMPOSE_REDIS_EOF + fi + + if [ "$DATABASE" = "TimescaleDB (time-series)" ]; then + cat >> "$PROJECT_PATH/docker-compose.yml" << 'COMPOSE_TS_EOF' + depends_on: + - timescaledb + + timescaledb: + image: timescale/timescaledb:latest-pg16 + environment: + POSTGRES_USER: app + POSTGRES_PASSWORD: changeme + POSTGRES_DB: app + volumes: + - timescale_data:/var/lib/postgresql/data + ports: + - "5432:5432" + +volumes: + timescale_data: +COMPOSE_TS_EOF + fi +fi + +####################################### +# Spec-kit structure +####################################### +if [ "$USE_SPECKIT" = "Yes" ]; then + gum spin --spinner dot --title "Initializing spec-kit structure..." -- sleep 0.3 + mkdir -p "$PROJECT_PATH/specs" + mkdir -p "$PROJECT_PATH/plans" + mkdir -p "$PROJECT_PATH/tasks" + touch "$PROJECT_PATH/specs/.gitkeep" + touch "$PROJECT_PATH/plans/.gitkeep" + touch "$PROJECT_PATH/tasks/.gitkeep" +fi + +####################################### +# Deploy script (if web access needed) +####################################### +if [ -n "$SUBDOMAIN" ]; then + gum spin --spinner dot --title "Creating deploy script..." -- sleep 0.3 + mkdir -p "$PROJECT_PATH/scripts" + + # Extract deploy host + case "$DEPLOY_TARGET" in + "docker-host"*) DEPLOY_HOST="docker-host" ;; + "trading-vm"*) DEPLOY_HOST="trading-vm" ;; + "saltbox"*) DEPLOY_HOST="saltbox" ;; + *) DEPLOY_HOST="docker-host" ;; + esac + + cat > "$PROJECT_PATH/scripts/deploy.sh" << DEPLOY_EOF +#!/usr/bin/env bash +# Deploy $PROJECT_NAME to $DEPLOY_HOST + +set -e + +DEPLOY_HOST="$DEPLOY_HOST" +PROJECT_NAME="$PROJECT_NAME" +SUBDOMAIN="$SUBDOMAIN" + +echo "🚀 Deploying \$PROJECT_NAME to \$DEPLOY_HOST..." + +# Build and push +docker build -t \$PROJECT_NAME:latest . + +# Copy to remote (or use registry) +# docker save \$PROJECT_NAME:latest | ssh \$DEPLOY_HOST docker load + +echo "✅ Deployed to https://\$SUBDOMAIN" +DEPLOY_EOF + chmod +x "$PROJECT_PATH/scripts/deploy.sh" + + # Generate Traefik config stub + mkdir -p "$PROJECT_PATH/deploy" + cat > "$PROJECT_PATH/deploy/traefik.yml" << TRAEFIK_EOF +# Traefik dynamic configuration for $PROJECT_NAME +# Copy to /opt/traefik/config/$PROJECT_NAME.yml on traefik host + +http: + routers: + $PROJECT_NAME: + rule: "Host(\`$SUBDOMAIN\`)" + service: $PROJECT_NAME + entryPoints: + - websecure + tls: + certResolver: letsencrypt + + services: + $PROJECT_NAME: + loadBalancer: + servers: + - url: "http://${DEPLOY_HOST}:8000" +TRAEFIK_EOF + + # Note about network access in docs + cat > "$PROJECT_PATH/docs/DEPLOYMENT.md" << NETDOC_EOF +# Deployment Guide + +## Network Access: $NETWORK_ACCESS + +**Subdomain:** https://$SUBDOMAIN +**Deploy Target:** $DEPLOY_TARGET + +### Setup Steps + +1. **Deploy the application:** + \`\`\`bash + ./scripts/deploy.sh + \`\`\` + +2. **Configure Traefik:** + Copy \`deploy/traefik.yml\` to the Traefik config directory: + \`\`\`bash + scp deploy/traefik.yml traefik:/opt/traefik/config/$PROJECT_NAME.yml + \`\`\` + +NETDOC_EOF + + if [ "$NETWORK_ACCESS" = "Public Internet (via Cloudflare)" ]; then + cat >> "$PROJECT_PATH/docs/DEPLOYMENT.md" << 'CFDOC_EOF' +3. **Add Cloudflare DNS:** + - Add A record pointing to your public IP + - Or use Cloudflare Tunnel for added security + +4. **Verify SSL:** + Traefik will auto-provision Let's Encrypt certificate +CFDOC_EOF + elif [ "$NETWORK_ACCESS" = "Local + Tailscale (remote access)" ]; then + cat >> "$PROJECT_PATH/docs/DEPLOYMENT.md" << 'TSDOC_EOF' +3. **Tailscale Access:** + - Ensure deploy target is on Tailscale network + - Access via Tailscale IP or MagicDNS name + +4. **No public DNS needed** - access via Tailscale only +TSDOC_EOF + else + cat >> "$PROJECT_PATH/docs/DEPLOYMENT.md" << 'LOCDOC_EOF' +3. **Local Access Only:** + - Add entry to local DNS (Pi-hole) or /etc/hosts + - Access only from 10.10.10.x network +LOCDOC_EOF + fi +fi + +####################################### +# Syncthing Configuration +####################################### +if [ "$SYNCTHING_OPT" = "Exclude from sync (recommended for git repos)" ]; then + gum spin --spinner dot --title "Adding to Syncthing ignore list..." -- sleep 0.3 + + if [ -f "$STIGNORE_FILE" ]; then + # Check if already in ignore file + if ! grep -q "^$PROJECT_NAME$" "$STIGNORE_FILE" 2>/dev/null; then + echo "$PROJECT_NAME" >> "$STIGNORE_FILE" + fi + fi +fi + ####################################### # Git Init ####################################### @@ -467,81 +920,47 @@ Co-Authored-By: Claude Opus 4.5 '" fi ####################################### -# Gitea Repo +# Git Remotes ####################################### -if [ "$GITEA_CREATE" = "Yes" ] && [ -n "$GITEA_TOKEN" ]; then +GITEA_URL="" +GITHUB_URL="" + +if [[ "$GIT_REMOTE" == *"Gitea"* ]] && [ -n "$GITEA_TOKEN" ]; then gum spin --spinner dot --title "Creating Gitea repository..." -- sleep 0.5 - # Create repo via API RESPONSE=$(curl -s -X POST "https://git.htsn.io/api/v1/user/repos" \ -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"name\": \"$PROJECT_NAME\", \"description\": \"$PROJECT_DESC\", - \"private\": false, + \"private\": true, \"auto_init\": false }" 2>/dev/null) if echo "$RESPONSE" | grep -q "\"name\":\"$PROJECT_NAME\""; then - # Add remote and push git -C "$PROJECT_PATH" remote add origin "git@git.htsn.io:hutson/$PROJECT_NAME.git" 2>/dev/null || true gum spin --spinner dot --title "Pushing to Gitea..." -- \ git -C "$PROJECT_PATH" push -u origin main -q 2>/dev/null || true GITEA_URL="https://git.htsn.io/hutson/$PROJECT_NAME" - else - GITEA_URL="(failed to create)" fi -elif [ "$GITEA_CREATE" = "Yes" ]; then - GITEA_URL="(GITEA_TOKEN not set in ~/.secrets)" fi -####################################### -# Spec-kit init -####################################### -if [ "$USE_SPECKIT" = "Yes" ]; then - gum spin --spinner dot --title "Initializing spec-kit structure..." -- sleep 0.3 - mkdir -p "$PROJECT_PATH/specs" - mkdir -p "$PROJECT_PATH/plans" - mkdir -p "$PROJECT_PATH/tasks" - touch "$PROJECT_PATH/specs/.gitkeep" - touch "$PROJECT_PATH/plans/.gitkeep" - touch "$PROJECT_PATH/tasks/.gitkeep" -fi +if [[ "$GIT_REMOTE" == *"GitHub"* ]]; then + if command -v gh &> /dev/null; then + gum spin --spinner dot --title "Creating GitHub repository..." -- sleep 0.5 -####################################### -# Docker support -####################################### -if echo "$ADDITIONAL" | grep -q "Docker support"; then - gum spin --spinner dot --title "Adding Docker support..." -- sleep 0.3 + gh repo create "$PROJECT_NAME" --public --source="$PROJECT_PATH" --push 2>/dev/null || true + GITHUB_URL="https://github.com/$(gh api user -q .login)/$PROJECT_NAME" - cat > "$PROJECT_PATH/Dockerfile" << 'DOCKER_EOF' -# Customize this Dockerfile for your project -FROM python:3.12-slim - -WORKDIR /app - -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -COPY . . - -CMD ["python", "-m", "src.main"] -DOCKER_EOF - - cat > "$PROJECT_PATH/docker-compose.yml" << 'COMPOSE_EOF' -version: '3.8' - -services: - app: - build: . - ports: - - "8000:8000" - environment: - - ENV=development - volumes: - - .:/app -COMPOSE_EOF + # If both remotes, rename + if [ -n "$GITEA_URL" ]; then + git -C "$PROJECT_PATH" remote rename origin gitea 2>/dev/null || true + git -C "$PROJECT_PATH" remote add github "git@github.com:$(gh api user -q .login)/$PROJECT_NAME.git" 2>/dev/null || true + fi + else + GITHUB_URL="(gh CLI not installed - create manually)" + fi fi ####################################### @@ -558,15 +977,27 @@ echo "" gum style --foreground 245 "Next steps:" echo "" echo " cd $PROJECT_PATH" + if [ "$USE_SPECKIT" = "Yes" ]; then echo " /speckit.specify # Define requirements" else + echo " # Review docs/PROJECT_BRIEF.md" echo " # Start coding!" fi -if [ -n "$GITEA_URL" ] && [ "$GITEA_URL" != "(failed to create)" ] && [ "$GITEA_URL" != "(GITEA_TOKEN not set in ~/.secrets)" ]; then +if [ -n "$GITEA_URL" ] && [ "$GITEA_URL" != "(failed)" ]; then echo "" echo " Gitea: $GITEA_URL" fi +if [ -n "$GITHUB_URL" ] && [[ "$GITHUB_URL" != *"not installed"* ]]; then + echo "" + echo " GitHub: $GITHUB_URL" +fi + +if [ -n "$SUBDOMAIN" ]; then + echo "" + echo " Subdomain: https://$SUBDOMAIN (configure Traefik)" +fi + echo ""