#!/usr/bin/env bash set -euo pipefail usage() { cat <<'USAGE' Usage: init_workspace.sh --name NAME [options] Required: --name NAME Project name Optional: --alias ALIAS Project alias (default: slugified name) --desc DESC Project description --owner OWNER Project owner --scale small|medium|large Project scale (default: medium) --risk low|medium|high Risk level (default: medium) --gate-architecture enabled|disabled Optional gate (default: disabled) --gate-code-review enabled|disabled Optional gate (default: disabled) --gate-security enabled|disabled Optional gate (default: disabled) --gate-privacy enabled|disabled Optional gate (default: disabled) --git-enabled true|false Git policy enabled (default: false) --git-commit-format FORMAT Commit format (default: "[role][phase] summary - reason") --output PATH Output directory (default: ./workspace/specs/{alias}-YYYYMMDD-HHMM) -h, --help Show help USAGE } die() { echo "error: $*" >&2 exit 1 } command -v python3 >/dev/null 2>&1 || die "python3 is required" project_name="" project_alias="" project_desc="" project_owner="" scale="medium" risk="medium" _gate_arch="disabled" _gate_code_review="disabled" _gate_security="disabled" _gate_privacy="disabled" git_enabled="false" git_commit_format="[role][phase] summary - reason" output="" while [[ $# -gt 0 ]]; do case "$1" in --name) project_name="$2"; shift 2;; --alias) project_alias="$2"; shift 2;; --desc) project_desc="$2"; shift 2;; --owner) project_owner="$2"; shift 2;; --scale) scale="$2"; shift 2;; --risk) risk="$2"; shift 2;; --gate-architecture) _gate_arch="$2"; shift 2;; --gate-code-review) _gate_code_review="$2"; shift 2;; --gate-security) _gate_security="$2"; shift 2;; --gate-privacy) _gate_privacy="$2"; shift 2;; --git-enabled) git_enabled="$2"; shift 2;; --git-commit-format) git_commit_format="$2"; shift 2;; --output) output="$2"; shift 2;; -h|--help) usage; exit 0;; *) die "unknown arg: $1";; esac done [[ -z "$project_name" ]] && die "--name is required" if [[ -z "$project_alias" ]]; then project_alias=$(echo "$project_name" | tr '[:upper:] ' '[:lower:]-' | tr -cd 'a-z0-9-') fi if [[ -z "$output" ]]; then output="./workspace/specs/${project_alias}-$(date +%Y%m%d-%H%M)" fi if [[ -e "$output" ]]; then die "output already exists: $output" fi script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" template_dir="${script_dir}/../assets/workspace_template" mkdir -p "$output" cp -R "$template_dir"/. "$output"/ date_str="$(date +%Y-%m-%d)" export TG_PROJECT_NAME="$project_name" export TG_PROJECT_ALIAS="$project_alias" export TG_PROJECT_DESC="$project_desc" export TG_PROJECT_OWNER="$project_owner" export TG_DATE="$date_str" export TG_SCALE="$scale" export TG_RISK="$risk" export TG_GATE_ARCH="$_gate_arch" export TG_GATE_CODE_REVIEW="$_gate_code_review" export TG_GATE_SECURITY="$_gate_security" export TG_GATE_PRIVACY="$_gate_privacy" export TG_GIT_ENABLED="$git_enabled" export TG_GIT_COMMIT_FORMAT="$git_commit_format" export TG_CURRENT_PHASE="demand" export TG_CURRENT_ROUND="1" export TG_P0_COUNT="0" export TG_LAST_DECISION="-" export TG_NEXT_ACTION="collect requirements" replace_file() { python3 - "$1" <<'PY' import os from pathlib import Path path = Path(os.environ["TARGET_FILE"]) data = path.read_text() repl = { "project_name": os.environ.get("TG_PROJECT_NAME", ""), "project_alias": os.environ.get("TG_PROJECT_ALIAS", ""), "project_desc": os.environ.get("TG_PROJECT_DESC", ""), "project_owner": os.environ.get("TG_PROJECT_OWNER", ""), "date": os.environ.get("TG_DATE", ""), "current_phase": os.environ.get("TG_CURRENT_PHASE", ""), "current_round": os.environ.get("TG_CURRENT_ROUND", ""), "p0_count": os.environ.get("TG_P0_COUNT", ""), "last_decision": os.environ.get("TG_LAST_DECISION", ""), "next_action": os.environ.get("TG_NEXT_ACTION", ""), } for k, v in repl.items(): data = data.replace("{{" + k + "}}", v) # project.yaml optional fields if path.name == "project.yaml": data = data.replace("small|medium|large", os.environ.get("TG_SCALE", "medium")) data = data.replace("low|medium|high", os.environ.get("TG_RISK", "medium")) data = data.replace("enabled|disabled", "enabled" if os.environ.get("TG_GATE_ARCH") == "enabled" else "disabled", 1) data = data.replace("enabled|disabled", "enabled" if os.environ.get("TG_GATE_CODE_REVIEW") == "enabled" else "disabled", 1) data = data.replace("enabled|disabled", "enabled" if os.environ.get("TG_GATE_SECURITY") == "enabled" else "disabled", 1) data = data.replace("enabled|disabled", "enabled" if os.environ.get("TG_GATE_PRIVACY") == "enabled" else "disabled", 1) data = data.replace("true|false", "true" if os.environ.get("TG_GIT_ENABLED") == "true" else "false", 1) data = data.replace("[role][phase] summary - reason", os.environ.get("TG_GIT_COMMIT_FORMAT", "[role][phase] summary - reason")) path.write_text(data) PY } for f in \ "$output/00_meta/project.yaml" \ "$output/00_meta/session.yaml" \ "$output/00_meta/summary.md" \ "$output/00_meta/decision_log.md" \ "$output/00_meta/status.md"; do export TARGET_FILE="$f" replace_file "$f" done echo "Workspace initialized at: $output"