#!/usr/bin/env bash set -euo pipefail PROJECT_RAW="${1:-}" if [[ -z "$PROJECT_RAW" ]]; then echo "Usage: $0 [--no-install]" echo "Example: $0 surveyable" exit 1 fi NO_INSTALL=false for arg in "$@"; do case "$arg" in --no-install) NO_INSTALL=true ;; esac done require_cmd() { if ! command -v "$1" >/dev/null 2>&1; then echo "Missing required command: $1" exit 1 fi } require_cmd dotnet require_cmd npm # Fix old npm config warning: # npm warn Unknown builtin config "globalignorefile" if npm config get globalignorefile >/dev/null 2>&1; then npm config delete globalignorefile >/dev/null 2>&1 || true fi to_pascal_case() { echo "$1" \ | tr '[:upper:]' '[:lower:]' \ | sed -E 's/[^a-z0-9]+/ /g' \ | awk '{ out=""; for (i=1; i<=NF; i++) { word=$i; out = out toupper(substr(word,1,1)) substr(word,2); } print out; }' } PROJECT_DIR="$PROJECT_RAW" PASCAL="$(to_pascal_case "$PROJECT_RAW")" if [[ -z "$PASCAL" ]]; then echo "Project name must contain at least one letter or number." exit 1 fi API_NAME="${PASCAL}.Api" TEST_NAME="${PASCAL}.Tests" SLNX_NAME="${PASCAL}.slnx" BACKEND_PORT=5080 FRONTEND_PORT=5173 COMPOSE_PORT=8080 echo "Project directory: $PROJECT_DIR" echo ".NET solution: $SLNX_NAME" echo "API project: $API_NAME" echo "Test project: $TEST_NAME" echo "Backend dev URL: http://0.0.0.0:$BACKEND_PORT" echo "Frontend dev URL: http://0.0.0.0:$FRONTEND_PORT" echo mkdir -p "$PROJECT_DIR" cd "$PROJECT_DIR" mkdir -p backend/src backend/tests docs deploy/caddy shared/openapi scripts ######################################## # Backend: .NET 10 API + Tests + .slnx ######################################## cd backend dotnet new sln -n "$PASCAL" if [[ ! -f "$SLNX_NAME" ]]; then echo "Expected $SLNX_NAME, but dotnet did not create it." echo "Detected SDK:" dotnet --version exit 1 fi dotnet new webapi -n "$API_NAME" -o "src/$API_NAME" --framework net10.0 dotnet new xunit -n "$TEST_NAME" -o "tests/$TEST_NAME" --framework net10.0 dotnet sln "$SLNX_NAME" add "src/$API_NAME/$API_NAME.csproj" dotnet sln "$SLNX_NAME" add "tests/$TEST_NAME/$TEST_NAME.csproj" dotnet add "tests/$TEST_NAME/$TEST_NAME.csproj" reference "src/$API_NAME/$API_NAME.csproj" API_DIR="src/$API_NAME" rm -f "$API_DIR/WeatherForecast.cs" || true mkdir -p "$API_DIR/Endpoints/Health" mkdir -p "$API_DIR/Contracts/Health" mkdir -p "$API_DIR/Domain" mkdir -p "$API_DIR/Infrastructure" mkdir -p "$API_DIR/Properties" cat > "$API_DIR/Properties/launchSettings.json" < "$API_DIR/Contracts/Health/HealthResponse.cs" < "$API_DIR/Contracts/Health/HelloResponse.cs" < "$API_DIR/Endpoints/Health/HealthEndpoints.cs" < Results.Ok(new HealthResponse("ok", DateTimeOffset.UtcNow))) .WithName("GetHealth") .WithOpenApi(); group.MapGet("/hello", () => Results.Ok(new HelloResponse("Hello from the .NET API"))) .WithName("GetHello") .WithOpenApi(); return group; } } EOF cat > "$API_DIR/Program.cs" <(options => { options.SerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase; }); const string DevCorsPolicy = "DevCors"; builder.Services.AddCors(options => { options.AddPolicy(DevCorsPolicy, policy => { policy .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials() .SetIsOriginAllowed(origin => { if (string.IsNullOrWhiteSpace(origin)) return false; var uri = new Uri(origin); return uri.Host is "localhost" or "127.0.0.1" || uri.Host.StartsWith("192.168.") || uri.Host.StartsWith("10.") || uri.Host.StartsWith("172."); }); }); }); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); app.UseCors(DevCorsPolicy); } app.MapHealthEndpoints(); app.Run(); EOF cat > "$API_DIR/Dockerfile" < src/app/main.ts <<'EOF' import { createApp } from 'vue' import App from '../App.vue' createApp(App).mount('#app') EOF sed -i 's|/src/main.ts|/src/app/main.ts|g' index.html cat > src/App.vue <<'EOF' EOF cat > src/pages/HomePage.vue <<'EOF' EOF cat > src/features/health/types.ts <<'EOF' export type HealthStatus = { status: string serverTimeUtc: string } EOF cat > src/features/health/api.ts < src/features/health/components/HealthPanel.vue <<'EOF' EOF cat > src/api/client.ts <({ baseUrl: import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:$BACKEND_PORT', }) EOF cat > src/api/schema.d.ts <<'EOF' export interface paths { '/api/health': { get: { responses: { 200: { content: { 'application/json': { status: string serverTimeUtc: string } } } } } } } EOF cat > scripts/fetch-openapi.mjs < vite.config.ts < Dockerfile < scripts/dev-backend.sh < scripts/dev-frontend.sh < scripts/update-openapi.sh < docker-compose.yml < deploy/caddy/Caddyfile <<'EOF' :80 { root * /srv encode gzip zstd handle /api/* { reverse_proxy api:8080 } handle { try_files {path} /index.html file_server } } EOF ######################################## # Docs + Agentic workflow ######################################## mkdir -p docs/FEATURES docs/TASKS docs/PROMPTS docs/DECISIONS cat > README.md <:$FRONTEND_PORT \`\`\` Backend: \`\`\`txt http://localhost:$BACKEND_PORT http://:$BACKEND_PORT \`\`\` ## Update frontend API types from backend The backend must be running first. \`\`\`bash ./scripts/update-openapi.sh \`\`\` This writes: \`\`\`txt shared/openapi/openapi.json frontend/src/api/schema.d.ts \`\`\` ## Docker Compose \`\`\`bash docker compose up --build \`\`\` Then open: \`\`\`txt http://localhost:$COMPOSE_PORT http://:$COMPOSE_PORT \`\`\` ## Solution \`\`\`bash dotnet build backend/$SLNX_NAME dotnet test backend/$SLNX_NAME \`\`\` ## Agentic workflow Start here: \`\`\`txt docs/AGENTIC_WORKFLOW.md \`\`\` Use feature specs, task files, and prompt templates instead of asking agents to work from vague chat history. EOF cat > docs/ARCHITECTURE.md < OpenAPI -> frontend TypeScript types \`\`\` EOF cat > docs/DEVELOPMENT_WORKFLOW.md < docs/AGENTIC_WORKFLOW.md <<'EOF' # Agentic Workflow This repository is designed to be worked on by humans and coding agents. The goal is to avoid the common failure mode where the first AI prompt creates a lot of code and every later prompt degrades because the agent loses architectural context. ## Core rule Agents do not work from vague chat history. Agents work from repository files: 1. `AGENTS.md` 2. `docs/ARCHITECTURE.md` 3. `docs/DEVELOPMENT_WORKFLOW.md` 4. `docs/FEATURES/.md` 5. `docs/TASKS//.md` 6. `docs/PROMPTS/.md` ## Workflow loop For every meaningful change: 1. Define or update a feature spec in `docs/FEATURES/` 2. Create a small task in `docs/TASKS//` 3. Use the correct prompt template from `docs/PROMPTS/` 4. Ask the agent to implement only that task 5. Review the diff 6. Run tests/builds 7. Update OpenAPI when backend contracts change 8. Commit the task separately ## Unit of work A task should usually be small enough to finish in one agent session. Good task examples: - Add `POST /api/auth/login` - Add `LoginPage.vue` - Add typed API wrapper for auth - Add validation for registration email - Add integration test for health endpoint Bad task examples: - Build auth - Make the whole SaaS - Redesign the app - Add billing and dashboard and admin ## Backend task pattern Backend changes should generally touch: ```txt backend/src/.Api/Endpoints// backend/src/.Api/Contracts// backend/tests/.Tests/ ``` Do not create new backend projects unless there is a clear architectural reason. ## Frontend task pattern Frontend changes should generally touch: ```txt frontend/src/features// frontend/src/pages/ frontend/src/api/ ``` Prefer feature folders over global folders. ## Contract workflow When backend endpoints or contracts change: ```bash ./scripts/dev-backend.sh ./scripts/update-openapi.sh ``` Then check frontend TypeScript errors and fix affected frontend code. ## Agent guardrails Agents must not: - invent architecture - perform broad unrelated refactors - manually duplicate generated API types - ignore existing docs - change deployment defaults without updating docs - silently replace the chosen backend/frontend structure Agents should: - make small changes - update feature specs when behavior changes - update task files if implementation scope changes - run or list validation commands EOF cat > docs/FEATURE_TEMPLATE.md <<'EOF' # Feature: ## Status Draft | In Progress | Complete ## Goal Describe the user-facing outcome. ## User stories - As a , I want , so that . ## Backend ### Endpoints - `GET /api/...` - `POST /api/...` ### Contracts - `` - `` ### Domain rules - Rule 1 - Rule 2 ## Frontend ### Pages - `` ### Components - `` ### State - Local component state - Store state, if needed ## API contract Describe what needs to appear in OpenAPI. ## Done when - [ ] Backend endpoint implemented - [ ] Backend tests added - [ ] OpenAPI updated - [ ] Frontend calls generated API types/client - [ ] Frontend UI implemented - [ ] Build/test passes ## Notes Architectural notes, edge cases, and decisions. EOF cat > docs/TASK_TEMPLATE.md <<'EOF' # Task: ## Feature `docs/FEATURES/.md` ## Goal One concrete change. ## Context What the agent needs to know before coding. ## Files likely to change - `backend/src/...` - `frontend/src/...` ## Constraints - Follow existing architecture. - Do not refactor unrelated files. - Do not manually duplicate generated API types. - Keep the diff focused. ## Implementation steps 1. Step one 2. Step two 3. Step three ## Done when - [ ] Code implemented - [ ] Tests/build pass - [ ] OpenAPI regenerated if backend contracts changed - [ ] Docs updated if behavior changed ## Validation commands ```bash dotnet test backend/*.slnx cd frontend && npm run build ``` EOF cat > docs/PROMPT_TEMPLATE.md <<'EOF' # General Agent Prompt Template You are working in this repository. ## Read first - `AGENTS.md` - `README.md` - `docs/ARCHITECTURE.md` - `docs/DEVELOPMENT_WORKFLOW.md` - `docs/AGENTIC_WORKFLOW.md` - `` - `` ## Task Implement: `` ## Rules - Implement only this task. - Do not refactor unrelated code. - Follow existing backend/frontend structure. - Backend feature code goes under `Endpoints/` and `Contracts/`. - Frontend feature code goes under `frontend/src/features/`. - If backend contracts change, update OpenAPI using `./scripts/update-openapi.sh`. - Do not manually duplicate generated API types. ## Output expected - Code changes - Tests where appropriate - Minimal docs update if behavior changed - Summary of what changed - Validation commands run EOF cat > docs/PROMPTS/backend-task.md <<'EOF' # Backend Task Prompt You are implementing a backend task in this repository. ## Read first - `AGENTS.md` - `docs/ARCHITECTURE.md` - `docs/DEVELOPMENT_WORKFLOW.md` - `docs/AGENTIC_WORKFLOW.md` - Relevant feature spec in `docs/FEATURES/` - Relevant task file in `docs/TASKS/` ## Instructions - Implement only the requested backend task. - Use the existing one-project backend structure. - Put endpoints under `Endpoints//`. - Put request/response contracts under `Contracts//`. - Add or update tests under `backend/tests/`. - Do not introduce repository/service layers unless the task explicitly requires it. - Do not split the backend into multiple projects. ## After coding Run or recommend: ```bash dotnet build backend/*.slnx dotnet test backend/*.slnx ./scripts/update-openapi.sh ``` EOF cat > docs/PROMPTS/frontend-task.md <<'EOF' # Frontend Task Prompt You are implementing a frontend task in this repository. ## Read first - `AGENTS.md` - `docs/ARCHITECTURE.md` - `docs/DEVELOPMENT_WORKFLOW.md` - `docs/AGENTIC_WORKFLOW.md` - Relevant feature spec in `docs/FEATURES/` - Relevant task file in `docs/TASKS/` ## Instructions - Implement only the requested frontend task. - Put feature code under `frontend/src/features//`. - Put route-level pages under `frontend/src/pages/`. - Use generated API types from `frontend/src/api/schema.d.ts`. - Do not manually recreate backend DTOs. - Do not create global services unless the feature truly needs shared behavior. ## After coding Run or recommend: ```bash cd frontend npm run build ``` EOF cat > docs/PROMPTS/debug.md <<'EOF' # Debug Prompt You are debugging a specific failure in this repository. ## Read first - `AGENTS.md` - `docs/ARCHITECTURE.md` - `docs/DEVELOPMENT_WORKFLOW.md` ## Error Paste the full error here. ## Instructions - Identify the most likely root cause. - Make the smallest safe fix. - Do not refactor unrelated code. - Explain how to verify the fix. ## Validation List the exact command(s) to run after the fix. EOF cat > docs/PROMPTS/refactor.md <<'EOF' # Refactor Prompt You are refactoring part of this repository. ## Read first - `AGENTS.md` - `docs/ARCHITECTURE.md` - `docs/AGENTIC_WORKFLOW.md` - Relevant feature spec ## Scope Describe the exact refactor boundary. ## Rules - Preserve behavior. - Do not add features. - Keep the diff focused. - Update docs if structure or workflow changes. - Run tests/builds after. ## Validation ```bash dotnet test backend/*.slnx cd frontend && npm run build ``` EOF cat > docs/FEATURES/health.md <<'EOF' # Feature: Health ## Status Complete ## Goal Verify that the frontend can call the backend through the generated OpenAPI TypeScript contract. ## Backend ### Endpoints - `GET /api/health` - `GET /api/hello` ### Contracts - `HealthResponse` - `HelloResponse` ## Frontend ### Components - `HealthPanel.vue` ### API - `features/health/api.ts` ## Done when - [x] Backend exposes health endpoint - [x] Frontend calls health endpoint - [x] OpenAPI can generate frontend types EOF mkdir -p docs/TASKS/health cat > docs/TASKS/health/001-verify-health-slice.md <<'EOF' # Task: Verify health vertical slice ## Feature `docs/FEATURES/health.md` ## Goal Verify the generated project works end-to-end. ## Steps 1. Run the backend. 2. Run the frontend. 3. Open the frontend from localhost and optionally from another LAN machine. 4. Regenerate OpenAPI types. 5. Build/test. ## Validation commands ```bash ./scripts/dev-backend.sh ./scripts/dev-frontend.sh ./scripts/update-openapi.sh dotnet test backend/*.slnx cd frontend && npm run build ``` ## Done when - [ ] Health panel shows backend status - [ ] OpenAPI types regenerate successfully - [ ] Backend tests pass - [ ] Frontend build passes EOF cat > docs/DECISIONS/0001-use-simple-dotnet-vue-monorepo.md < AGENTS.md <\` and \`Contracts/\`. - Frontend feature code goes under \`frontend/src/features/\`. - Frontend API types are generated from backend OpenAPI. - Do not manually duplicate backend DTOs into frontend code. - If backend DTOs or endpoints change, run \`./scripts/update-openapi.sh\`. - Dev servers must use HTTP and bind to \`0.0.0.0\` for LAN access. - Avoid broad refactors unless the task explicitly asks for one. ## Task discipline Agents should work from task files in \`docs/TASKS/\`. A good task: - has a clear goal - names the relevant feature - has a small scope - lists validation commands If no task exists, create one before implementing a meaningful feature. ## Validation For backend changes: \`\`\`bash dotnet build backend/$SLNX_NAME dotnet test backend/$SLNX_NAME \`\`\` For frontend changes: \`\`\`bash cd frontend npm run build \`\`\` For contract changes: \`\`\`bash ./scripts/update-openapi.sh \`\`\` EOF cat > .gitignore <<'EOF' # .NET bin/ obj/ TestResults/ *.user *.suo # Node node_modules/ dist/ .vite/ # Env .env .env.* # IDE .vscode/ .idea/ # OS .DS_Store Thumbs.db EOF echo echo "Bootstrap complete." echo echo "Next:" echo " cd $PROJECT_DIR" echo " ./scripts/dev-backend.sh" echo " ./scripts/dev-frontend.sh" echo echo "After backend is running:" echo " ./scripts/update-openapi.sh" echo echo "Agentic workflow:" echo " docs/AGENTIC_WORKFLOW.md"