Enhance project wizard with comprehensive options
New features: - Multi-line project brief input (saved to docs/PROJECT_BRIEF.md) - Git remote options: Gitea, GitHub, both, or neither - Syncthing sync/exclude option (auto-updates .stignore) - Network access levels: local, Tailscale, public Internet - Subdomain routing with Traefik config generation - Database selection: SQLite, PostgreSQL, TimescaleDB, Redis - License picker: MIT, Apache 2.0, proprietary - .env.example generation from selected MCPs/databases - Deploy script and deployment docs for web projects - Auto-install gum if missing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,26 +1,32 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# newproject - Interactive project scaffolding tool
|
# newproject - Interactive project scaffolding wizard
|
||||||
# Uses gum for beautiful terminal UI
|
# Uses gum for beautiful terminal UI
|
||||||
#
|
#
|
||||||
|
|
||||||
set -e
|
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
|
# Config
|
||||||
PROJECTS_DIR="$HOME/Projects"
|
PROJECTS_DIR="$HOME/Projects"
|
||||||
TEMPLATE_REPO="git@git.htsn.io:hutson/template-project.git"
|
STIGNORE_FILE="$PROJECTS_DIR/.stignore"
|
||||||
|
|
||||||
# Source central config if available
|
# Source central config if available
|
||||||
[ -f ~/.secrets ] && source ~/.secrets
|
[ -f ~/.secrets ] && source ~/.secrets
|
||||||
[ -f ~/.hosts ] && source ~/.hosts
|
[ -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
|
# Header
|
||||||
#######################################
|
#######################################
|
||||||
@@ -69,11 +75,16 @@ PROJECT_TYPE=$(gum choose \
|
|||||||
--header "Project Type")
|
--header "Project Type")
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Project Description
|
# Project Brief (Multi-line)
|
||||||
#######################################
|
#######################################
|
||||||
echo ""
|
echo ""
|
||||||
gum style --foreground 245 "Brief project description:"
|
gum style --foreground 245 "Describe your project (Ctrl+D or Esc when done):"
|
||||||
PROJECT_DESC=$(gum input --placeholder "A brief description of what this project does" --width 60)
|
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
|
# MCP Selection
|
||||||
@@ -81,7 +92,6 @@ PROJECT_DESC=$(gum input --placeholder "A brief description of what this project
|
|||||||
echo ""
|
echo ""
|
||||||
gum style --foreground 245 "Select MCPs to enable (space to select, enter to confirm):"
|
gum style --foreground 245 "Select MCPs to enable (space to select, enter to confirm):"
|
||||||
|
|
||||||
# Define available MCPs with descriptions
|
|
||||||
MCPS=$(gum choose --no-limit \
|
MCPS=$(gum choose --no-limit \
|
||||||
"exa (web search, research)" \
|
"exa (web search, research)" \
|
||||||
"Ref (documentation lookup)" \
|
"Ref (documentation lookup)" \
|
||||||
@@ -96,18 +106,80 @@ MCPS=$(gum choose --no-limit \
|
|||||||
--selected "exa (web search, research),Ref (documentation lookup)")
|
--selected "exa (web search, research),Ref (documentation lookup)")
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Git Options
|
# Git Remote Options
|
||||||
#######################################
|
#######################################
|
||||||
echo ""
|
echo ""
|
||||||
gum style --foreground 245 "Git repository options:"
|
gum style --foreground 245 "Git repository options:"
|
||||||
|
|
||||||
GIT_INIT=$(gum choose "Yes" "No" --header "Initialize git repository?")
|
GIT_INIT=$(gum choose "Yes" "No" --header "Initialize git repository?")
|
||||||
|
|
||||||
GITEA_CREATE="No"
|
GIT_REMOTE="None"
|
||||||
if [ "$GIT_INIT" = "Yes" ]; then
|
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
|
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
|
# Additional Options
|
||||||
#######################################
|
#######################################
|
||||||
@@ -116,28 +188,54 @@ gum style --foreground 245 "Additional options:"
|
|||||||
|
|
||||||
ADDITIONAL=$(gum choose --no-limit \
|
ADDITIONAL=$(gum choose --no-limit \
|
||||||
"spec-kit (spec-driven development)" \
|
"spec-kit (spec-driven development)" \
|
||||||
|
"Docker support" \
|
||||||
"pre-commit hooks" \
|
"pre-commit hooks" \
|
||||||
"GitHub Actions CI" \
|
"GitHub Actions CI" \
|
||||||
"Docker support" \
|
".env.example (from ~/.secrets)" \
|
||||||
--header "Additional features (space=select)")
|
--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
|
# Confirm
|
||||||
#######################################
|
#######################################
|
||||||
echo ""
|
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 \
|
gum style --foreground 212 --border-foreground 212 --border rounded \
|
||||||
--align left --width 60 --margin "1 0" --padding "1 2" \
|
--align left --width 65 --margin "1 0" --padding "1 2" \
|
||||||
"📋 Project Summary" \
|
"$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/, $//')"
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
CONFIRM=$(gum choose "Create Project" "Cancel" --header "Proceed?")
|
CONFIRM=$(gum choose "Create Project" "Cancel" --header "Proceed?")
|
||||||
@@ -153,26 +251,35 @@ fi
|
|||||||
echo ""
|
echo ""
|
||||||
gum spin --spinner dot --title "Creating project directory..." -- sleep 0.5
|
gum spin --spinner dot --title "Creating project directory..." -- sleep 0.5
|
||||||
mkdir -p "$PROJECT_PATH"
|
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
|
gum spin --spinner dot --title "Generating MCP configuration..." -- sleep 0.3
|
||||||
|
|
||||||
mkdir -p "$PROJECT_PATH/.claude"
|
mkdir -p "$PROJECT_PATH/.claude"
|
||||||
|
|
||||||
# Build disabled MCPs list
|
|
||||||
ALL_MCPS=("exa" "Ref" "ticktick" "beeper" "airtable" "claude-in-chrome" "shopping" "proton-mail" "weather")
|
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'
|
cat > "$PROJECT_PATH/.claude/settings.json" << 'SETTINGS_EOF'
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
@@ -201,47 +308,54 @@ SETTINGS_EOF
|
|||||||
#######################################
|
#######################################
|
||||||
gum spin --spinner dot --title "Generating CLAUDE.md..." -- sleep 0.3
|
gum spin --spinner dot --title "Generating CLAUDE.md..." -- sleep 0.3
|
||||||
|
|
||||||
# Determine type-specific content
|
|
||||||
case "$PROJECT_TYPE" in
|
case "$PROJECT_TYPE" in
|
||||||
python-fastapi)
|
python-fastapi)
|
||||||
TECH_STACK="Python 3.12+, FastAPI, SQLModel, Pydantic, Alembic"
|
TECH_STACK="Python 3.12+, FastAPI, SQLModel, Pydantic, Alembic"
|
||||||
TYPE_CHECK_CMD="mypy --strict src/"
|
TYPE_CHECK_CMD="mypy --strict src/"
|
||||||
LINT_CMD="ruff check src/ && black src/"
|
LINT_CMD="ruff check src/ && black src/"
|
||||||
TEST_CMD="pytest"
|
TEST_CMD="pytest"
|
||||||
|
INSTALL_CMD="pip install -e '.[dev]'"
|
||||||
|
DEV_CMD="uvicorn src.main:app --reload"
|
||||||
;;
|
;;
|
||||||
python-generic)
|
python-generic)
|
||||||
TECH_STACK="Python 3.12+"
|
TECH_STACK="Python 3.12+"
|
||||||
TYPE_CHECK_CMD="mypy --strict src/"
|
TYPE_CHECK_CMD="mypy --strict src/"
|
||||||
LINT_CMD="ruff check src/ && black src/"
|
LINT_CMD="ruff check src/ && black src/"
|
||||||
TEST_CMD="pytest"
|
TEST_CMD="pytest"
|
||||||
|
INSTALL_CMD="pip install -e '.[dev]'"
|
||||||
|
DEV_CMD="python -m src.main"
|
||||||
;;
|
;;
|
||||||
typescript-react)
|
typescript-react)
|
||||||
TECH_STACK="TypeScript, React 18, Bun, TailwindCSS"
|
TECH_STACK="TypeScript, React 18, Bun, TailwindCSS"
|
||||||
TYPE_CHECK_CMD="bun run tsc --noEmit"
|
TYPE_CHECK_CMD="bun run tsc --noEmit"
|
||||||
LINT_CMD="bun run lint"
|
LINT_CMD="bun run lint"
|
||||||
TEST_CMD="bun test"
|
TEST_CMD="bun test"
|
||||||
|
INSTALL_CMD="bun install"
|
||||||
|
DEV_CMD="bun run dev"
|
||||||
;;
|
;;
|
||||||
typescript-node)
|
typescript-node)
|
||||||
TECH_STACK="TypeScript, Node.js/Bun"
|
TECH_STACK="TypeScript, Node.js/Bun"
|
||||||
TYPE_CHECK_CMD="bun run tsc --noEmit"
|
TYPE_CHECK_CMD="bun run tsc --noEmit"
|
||||||
LINT_CMD="bun run lint"
|
LINT_CMD="bun run lint"
|
||||||
TEST_CMD="bun test"
|
TEST_CMD="bun test"
|
||||||
|
INSTALL_CMD="bun install"
|
||||||
|
DEV_CMD="bun run dev"
|
||||||
;;
|
;;
|
||||||
generic)
|
generic)
|
||||||
TECH_STACK="[Customize]"
|
TECH_STACK="[Customize]"
|
||||||
TYPE_CHECK_CMD="# Add type check command"
|
TYPE_CHECK_CMD="# Add type check command"
|
||||||
LINT_CMD="# Add lint command"
|
LINT_CMD="# Add lint command"
|
||||||
TEST_CMD="# Add test command"
|
TEST_CMD="# Add test command"
|
||||||
|
INSTALL_CMD="# Add install command"
|
||||||
|
DEV_CMD="# Add dev command"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Check if spec-kit selected
|
|
||||||
USE_SPECKIT="No"
|
USE_SPECKIT="No"
|
||||||
if echo "$ADDITIONAL" | grep -q "spec-kit"; then
|
if echo "$ADDITIONAL" | grep -q "spec-kit"; then
|
||||||
USE_SPECKIT="Yes"
|
USE_SPECKIT="Yes"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Generate CLAUDE.md
|
|
||||||
cat > "$PROJECT_PATH/CLAUDE.md" << CLAUDE_EOF
|
cat > "$PROJECT_PATH/CLAUDE.md" << CLAUDE_EOF
|
||||||
# $PROJECT_NAME - Claude Code Guidelines
|
# $PROJECT_NAME - Claude Code Guidelines
|
||||||
|
|
||||||
@@ -254,7 +368,7 @@ $PROJECT_DESC
|
|||||||
**Key Directories**:
|
**Key Directories**:
|
||||||
- \`src/\` - Main source code
|
- \`src/\` - Main source code
|
||||||
- \`tests/\` - Test files
|
- \`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
|
## Pre-Commit Checklist
|
||||||
|
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
# Type safety
|
|
||||||
$TYPE_CHECK_CMD
|
$TYPE_CHECK_CMD
|
||||||
|
|
||||||
# Linting
|
|
||||||
$LINT_CMD
|
$LINT_CMD
|
||||||
|
|
||||||
# Tests
|
|
||||||
$TEST_CMD
|
$TEST_CMD
|
||||||
|
|
||||||
# Code review (for significant changes)
|
|
||||||
/code-review:code-review
|
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
CLAUDE_EOF
|
CLAUDE_EOF
|
||||||
|
|
||||||
# Add spec-kit section if selected
|
|
||||||
if [ "$USE_SPECKIT" = "Yes" ]; then
|
if [ "$USE_SPECKIT" = "Yes" ]; then
|
||||||
cat >> "$PROJECT_PATH/CLAUDE.md" << 'SPECKIT_EOF'
|
cat >> "$PROJECT_PATH/CLAUDE.md" << 'SPECKIT_EOF'
|
||||||
## Spec-Driven Development
|
## Spec-Driven Development
|
||||||
@@ -345,21 +450,18 @@ if [ "$USE_SPECKIT" = "Yes" ]; then
|
|||||||
SPECKIT_EOF
|
SPECKIT_EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add MCP configuration section
|
|
||||||
cat >> "$PROJECT_PATH/CLAUDE.md" << MCP_EOF
|
cat >> "$PROJECT_PATH/CLAUDE.md" << MCP_EOF
|
||||||
## MCP Configuration
|
## MCP Configuration
|
||||||
|
|
||||||
**Enabled MCPs for this project:**
|
**Enabled MCPs for this project:**
|
||||||
$(echo "$MCPS" | sed 's/^/- /')
|
$(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
|
## Central Configuration Reference
|
||||||
|
|
||||||
This project can access Hutson's central homelab configuration files. These are synced across all machines via Syncthing.
|
|
||||||
|
|
||||||
| File | Purpose | Usage |
|
| File | Purpose | Usage |
|
||||||
|------|---------|-------|
|
|------|---------|-------|
|
||||||
| \`~/.secrets\` | API keys, tokens, credentials | \`source ~/.secrets\` then use \`\$VAR_NAME\` |
|
| \`~/.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\`)
|
1. Add to the central files (\`~/.secrets\` or \`~/.hosts\`)
|
||||||
2. Files sync via Syncthing to all machines
|
2. Files sync via Syncthing to all machines
|
||||||
3. Never commit secrets to git - use environment variables
|
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
|
## Project-Specific Notes
|
||||||
|
|
||||||
[Add project-specific patterns, gotchas, and conventions here]
|
[Add project-specific patterns, gotchas, and conventions here]
|
||||||
MCP_EOF
|
NOTES_EOF
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Generate README.md
|
# Generate README.md
|
||||||
@@ -392,10 +518,10 @@ $PROJECT_DESC
|
|||||||
|
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
# [Add install command]
|
$INSTALL_CMD
|
||||||
|
|
||||||
# Run development server
|
# Run development server
|
||||||
# [Add dev command]
|
$DEV_CMD
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
$TEST_CMD
|
$TEST_CMD
|
||||||
@@ -404,6 +530,7 @@ $TEST_CMD
|
|||||||
## Development
|
## Development
|
||||||
|
|
||||||
See [CLAUDE.md](CLAUDE.md) for development guidelines and conventions.
|
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
|
README_EOF
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
@@ -451,6 +578,332 @@ htmlcov/
|
|||||||
logs/
|
logs/
|
||||||
GITIGNORE_EOF
|
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
|
# Git Init
|
||||||
#######################################
|
#######################################
|
||||||
@@ -467,81 +920,47 @@ Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>'"
|
|||||||
fi
|
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
|
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" \
|
RESPONSE=$(curl -s -X POST "https://git.htsn.io/api/v1/user/repos" \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{
|
-d "{
|
||||||
\"name\": \"$PROJECT_NAME\",
|
\"name\": \"$PROJECT_NAME\",
|
||||||
\"description\": \"$PROJECT_DESC\",
|
\"description\": \"$PROJECT_DESC\",
|
||||||
\"private\": false,
|
\"private\": true,
|
||||||
\"auto_init\": false
|
\"auto_init\": false
|
||||||
}" 2>/dev/null)
|
}" 2>/dev/null)
|
||||||
|
|
||||||
if echo "$RESPONSE" | grep -q "\"name\":\"$PROJECT_NAME\""; then
|
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
|
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..." -- \
|
gum spin --spinner dot --title "Pushing to Gitea..." -- \
|
||||||
git -C "$PROJECT_PATH" push -u origin main -q 2>/dev/null || true
|
git -C "$PROJECT_PATH" push -u origin main -q 2>/dev/null || true
|
||||||
GITEA_URL="https://git.htsn.io/hutson/$PROJECT_NAME"
|
GITEA_URL="https://git.htsn.io/hutson/$PROJECT_NAME"
|
||||||
else
|
|
||||||
GITEA_URL="(failed to create)"
|
|
||||||
fi
|
fi
|
||||||
elif [ "$GITEA_CREATE" = "Yes" ]; then
|
|
||||||
GITEA_URL="(GITEA_TOKEN not set in ~/.secrets)"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#######################################
|
if [[ "$GIT_REMOTE" == *"GitHub"* ]]; then
|
||||||
# Spec-kit init
|
if command -v gh &> /dev/null; then
|
||||||
#######################################
|
gum spin --spinner dot --title "Creating GitHub repository..." -- sleep 0.5
|
||||||
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
|
|
||||||
|
|
||||||
#######################################
|
gh repo create "$PROJECT_NAME" --public --source="$PROJECT_PATH" --push 2>/dev/null || true
|
||||||
# Docker support
|
GITHUB_URL="https://github.com/$(gh api user -q .login)/$PROJECT_NAME"
|
||||||
#######################################
|
|
||||||
if echo "$ADDITIONAL" | grep -q "Docker support"; then
|
|
||||||
gum spin --spinner dot --title "Adding Docker support..." -- sleep 0.3
|
|
||||||
|
|
||||||
cat > "$PROJECT_PATH/Dockerfile" << 'DOCKER_EOF'
|
# If both remotes, rename
|
||||||
# Customize this Dockerfile for your project
|
if [ -n "$GITEA_URL" ]; then
|
||||||
FROM python:3.12-slim
|
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
|
||||||
WORKDIR /app
|
fi
|
||||||
|
else
|
||||||
COPY requirements.txt .
|
GITHUB_URL="(gh CLI not installed - create manually)"
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
fi
|
||||||
|
|
||||||
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
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
@@ -558,15 +977,27 @@ echo ""
|
|||||||
gum style --foreground 245 "Next steps:"
|
gum style --foreground 245 "Next steps:"
|
||||||
echo ""
|
echo ""
|
||||||
echo " cd $PROJECT_PATH"
|
echo " cd $PROJECT_PATH"
|
||||||
|
|
||||||
if [ "$USE_SPECKIT" = "Yes" ]; then
|
if [ "$USE_SPECKIT" = "Yes" ]; then
|
||||||
echo " /speckit.specify # Define requirements"
|
echo " /speckit.specify # Define requirements"
|
||||||
else
|
else
|
||||||
|
echo " # Review docs/PROJECT_BRIEF.md"
|
||||||
echo " # Start coding!"
|
echo " # Start coding!"
|
||||||
fi
|
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 ""
|
||||||
echo " Gitea: $GITEA_URL"
|
echo " Gitea: $GITEA_URL"
|
||||||
fi
|
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 ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user