scripts-onboard-wizard
#!/usr/bin/env python3 """
title: "Get script directory for path resolution (works from any cwd)" component_type: script version: "1.0.0" audience: contributor status: stable summary: "CODITECT Onboarding Wizard - Automated Setup and Verification" keywords: ['analysis', 'api', 'git', 'onboard', 'wizard'] tokens: ~500 created: 2025-12-22 updated: 2025-12-22 script_name: "onboard-wizard.py" language: python executable: true usage: "python3 scripts/onboard-wizard.py [options]" python_version: "3.10+" dependencies: [] modifies_files: false network_access: false requires_auth: false
CODITECT Onboarding Wizard - Automated Setup and Verification
Interactive wizard for new user onboarding with environment checks, project scaffolding, and progress tracking.
Usage: python3 scripts/onboard-wizard.py # Interactive mode python3 scripts/onboard-wizard.py --check-env # Environment check only python3 scripts/onboard-wizard.py --auto-setup # Non-interactive setup python3 scripts/onboard-wizard.py --status # Show progress """
import argparse import json import os import shutil import subprocess import sys from datetime import datetime, timezone from pathlib import Path from typing import Optional
Get script directory for path resolution (works from any cwd)
SCRIPT_DIR = Path(file).resolve().parent CORE_ROOT = SCRIPT_DIR.parent
ANSI colors for terminal output
Shared Colors module (consolidates 36 duplicate definitions)
_script_dir = Path(file).parent sys.path.insert(0, str(_script_dir / "core")) from colors import Colors
def print_success(msg: str) -> None: print(f"{Colors.GREEN}✓{Colors.END} {msg}")
def print_warning(msg: str) -> None: print(f"{Colors.YELLOW}⚠{Colors.END} {msg}")
def print_error(msg: str) -> None: print(f"{Colors.RED}✗{Colors.END} {msg}")
def print_info(msg: str) -> None: print(f"{Colors.BLUE}ℹ{Colors.END} {msg}")
def print_step(msg: str) -> None: print(f"\n{Colors.CYAN}{Colors.BOLD}▶ {msg}{Colors.END}")
def print_celebration(msg: str) -> None: print(f"\n{Colors.GREEN}{Colors.BOLD}🎉 {msg}{Colors.END}\n")
def run_command(cmd: list[str], capture: bool = True) -> tuple[int, str, str]: """Run a shell command and return (returncode, stdout, stderr).""" try: result = subprocess.run( cmd, capture_output=capture, text=True, timeout=30 ) return result.returncode, result.stdout.strip(), result.stderr.strip() except FileNotFoundError: return 1, "", f"Command not found: {cmd[0]}" except subprocess.TimeoutExpired: return 1, "", "Command timed out" except Exception as e: return 1, "", str(e)
def check_git() -> tuple[bool, str]: """Check Git installation and version.""" code, stdout, stderr = run_command(["git", "--version"]) if code != 0: return False, "Git not installed"
# Parse version (e.g., "git version 2.39.0")
try:
version_str = stdout.split()[-1]
major, minor = map(int, version_str.split(".")[:2])
if major >= 2 and minor >= 25:
return True, f"Git {version_str}"
else:
return False, f"Git {version_str} (need 2.25+)"
except (ValueError, IndexError):
return True, stdout # Can't parse but command worked
def check_python() -> tuple[bool, str]: """Check Python installation and version.""" code, stdout, stderr = run_command(["python3", "--version"]) if code != 0: return False, "Python 3 not installed"
# Parse version (e.g., "Python 3.11.4")
try:
version_str = stdout.split()[-1]
major, minor = map(int, version_str.split(".")[:2])
if major >= 3 and minor >= 10:
return True, f"Python {version_str}"
else:
return False, f"Python {version_str} (need 3.10+)"
except (ValueError, IndexError):
return True, stdout
def check_claude() -> tuple[bool, str]: """Check Claude Code CLI installation.""" code, stdout, stderr = run_command(["claude", "--version"]) if code != 0: return False, "Claude Code CLI not installed" return True, f"Claude Code {stdout}" if stdout else "Claude Code installed"
def check_coditect_config(project_path: str) -> dict: """Check CODITECT configuration status.""" path = Path(project_path) return { "has_coditect": (path / ".coditect").exists(), "has_claude": (path / ".claude").exists(), "has_docs": (path / "docs").exists(), "has_git": (path / ".git").exists(), "has_readme": (path / "README.md").exists(), "has_claude_md": (path / "CLAUDE.md").exists(), }
def check_environment() -> dict: """Run all environment checks.""" print_step("Checking Environment")
results = {}
# Git check
ok, msg = check_git()
results["git"] = {"ok": ok, "message": msg}
if ok:
print_success(msg)
else:
print_error(msg)
print_info("Fix: brew install git (macOS) or sudo apt install git (Linux)")
# Python check
ok, msg = check_python()
results["python"] = {"ok": ok, "message": msg}
if ok:
print_success(msg)
else:
print_error(msg)
print_info("Fix: brew install python@3.11 or use pyenv")
# Claude Code check
ok, msg = check_claude()
results["claude"] = {"ok": ok, "message": msg}
if ok:
print_success(msg)
else:
print_error(msg)
print_info("Fix: npm install -g @anthropic-ai/claude-code")
# Summary
all_ok = all(r["ok"] for r in results.values())
results["all_ok"] = all_ok
if all_ok:
print_celebration("Environment verified!")
else:
print_warning("Some requirements need attention")
return results
def get_shell_config_path() -> Optional[Path]: """Determine user's shell config file.""" shell = os.environ.get("SHELL", "/bin/bash")
if "zsh" in shell:
return Path.home() / ".zshrc"
elif "bash" in shell:
bashrc = Path.home() / ".bashrc"
bash_profile = Path.home() / ".bash_profile"
return bashrc if bashrc.exists() else bash_profile
else:
return None
def setup_shell_aliases() -> bool: """Add CODITECT aliases to shell config.""" print_step("Setting Up Shell Aliases")
config_path = get_shell_config_path()
if not config_path:
print_warning("Could not determine shell config file")
return False
aliases = '''
CODITECT AI Command Router
alias cr='claude "/suggest-agent"' alias cri='claude --interactive "/suggest-agent"' '''
# Check if aliases already exist
if config_path.exists():
content = config_path.read_text()
if "alias cr=" in content:
print_info("Aliases already configured")
return True
# Add aliases
try:
with open(config_path, "a") as f:
f.write(f"\n{aliases}")
print_success(f"Added aliases to {config_path}")
print_info(f"Run: source {config_path}")
return True
except Exception as e:
print_error(f"Could not update {config_path}: {e}")
return False
def create_project_structure(project_name: str, project_path: Optional[str] = None) -> bool: """Create CODITECT project structure.""" print_step(f"Creating Project: {project_name}")
path = Path(project_path) if project_path else Path.cwd() / project_name
try:
# Create directories
directories = [
".coditect",
"docs/original-research",
"docs/architecture",
"docs/project-management",
"src",
"tests",
]
for dir_path in directories:
(path / dir_path).mkdir(parents=True, exist_ok=True)
print_success(f"Created {dir_path}/")
# Create .claude symlink
claude_link = path / ".claude"
if not claude_link.exists():
claude_link.symlink_to(".coditect")
print_success("Created .claude -> .coditect symlink")
# Create component-activation-status.json
activation_status = {
"version": "1.0.0",
"created_at": datetime.now(timezone.utc).isoformat(),
"activation_summary": {
"total_components": 0,
"activated": 0,
"deactivated": 0
},
"components": []
}
activation_path = path / ".coditect/component-activation-status.json"
activation_path.write_text(json.dumps(activation_status, indent=2))
print_success("Created component-activation-status.json")
# Create README.md
readme_content = f"""# {project_name}
Created with CODITECT Framework
Overview
[Describe your project here]
Getting Started
# Navigate to project
cd {project_name}
# Use AI Command Router for guidance
cr "what I want to do"
Documentation
docs/original-research/- Research materials and referencesdocs/architecture/- Technical decisions and diagramsdocs/project-management/- Plans and task lists
CODITECT Integration
This project uses CODITECT for AI-assisted development:
.coditect/- Framework configuration.claude -> .coditect- Claude Code compatibility
Generated by CODITECT Onboarding Wizard """ (path / "README.md").write_text(readme_content) print_success("Created README.md")
# Create CLAUDE.md
claude_md_content = f"""# {project_name} - AI Agent Context
Project Overview
[Brief description of your project]
Key Files
docs/- Project documentationsrc/- Source codetests/- Test files
AI Agent Guidelines
- Read docs/original-research/ before making recommendations
- Follow existing code patterns
- Document significant changes
Created: {datetime.now().strftime("%Y-%m-%d")} Framework: CODITECT v1.0 """ (path / "CLAUDE.md").write_text(claude_md_content) print_success("Created CLAUDE.md")
# Create .gitignore
gitignore_content = """# Python
pycache/ *.py[cod] .venv/ venv/ .env
IDE
.idea/ .vscode/ *.swp
OS
.DS_Store Thumbs.db
CODITECT (ADR-118 Four-Tier Database Architecture)
context-storage/org.db context-storage/sessions.db context-storage/platform.db *.jsonl """ (path / ".gitignore").write_text(gitignore_content) print_success("Created .gitignore")
# Create docs/original-research/README.md
research_readme = """# Original Research
Store your research materials, references, and background information here.
Contents
- Market research
- Competitor analysis
- Technical references
- User research
Usage
Place any relevant documents, links, or notes that inform your project decisions. """ (path / "docs/original-research/README.md").write_text(research_readme) print_success("Created docs/original-research/README.md")
# Initialize git if not exists
if not (path / ".git").exists():
code, _, _ = run_command(["git", "init"], capture=True)
if code == 0:
print_success("Initialized git repository")
# Initial commit
os.chdir(path)
run_command(["git", "add", "."])
run_command(["git", "commit", "-m", "Initial commit from CODITECT onboarding"])
print_success("Created initial commit")
print_celebration(f"Project '{project_name}' created successfully!")
return True
except Exception as e:
print_error(f"Error creating project: {e}")
return False
def save_progress(progress: dict, project_path: str = ".") -> None: """Save onboarding progress to file.""" progress_file = Path(project_path) / ".coditect/onboarding-progress.json" progress_file.parent.mkdir(parents=True, exist_ok=True) progress_file.write_text(json.dumps(progress, indent=2))
def load_progress(project_path: str = ".") -> Optional[dict]: """Load onboarding progress from file.""" progress_file = Path(project_path) / ".coditect/onboarding-progress.json" if progress_file.exists(): return json.loads(progress_file.read_text()) return None
def show_status(project_path: str = ".") -> None: """Show current onboarding status.""" print_step("Onboarding Status")
progress = load_progress(project_path)
if not progress:
print_info("No onboarding progress found")
print_info("Run: /onboard to start")
return
print(f"\nStarted: {progress.get('started_at', 'Unknown')}")
print(f"Experience: {progress.get('experience_level', 'Unknown')}")
print(f"Goal: {progress.get('goal', 'Unknown')}")
print("\nPhases:")
phases = progress.get("phases", {})
for phase, data in phases.items():
status = "✓" if data.get("completed") else "○"
completed_at = data.get("at", "")
print(f" {status} {phase}: {completed_at if data.get('completed') else 'pending'}")
if progress.get("completed_at"):
print_celebration(f"Onboarding completed at {progress['completed_at']}")
def interactive_onboarding() -> None: """Run interactive onboarding wizard.""" print(f"\n{Colors.CYAN}{Colors.BOLD}═══════════════════════════════════════════════════════════════") print(" CODITECT ONBOARDING WIZARD") print(f"═══════════════════════════════════════════════════════════════{Colors.END}\n")
print("Welcome! I'll help you get started with CODITECT in about 10 minutes.\n")
# Initialize progress
progress = {
"started_at": datetime.now(timezone.utc).isoformat(),
"phases": {}
}
# Phase 1: Experience Assessment
print_step("Quick Assessment")
print("\nWhat's your experience level?")
print(" 1) New to software development")
print(" 2) Some coding experience")
print(" 3) Professional developer")
print(" 4) Already familiar with CODITECT")
exp_choice = input("\nYour choice (1-4): ").strip()
exp_map = {"1": "beginner", "2": "intermediate", "3": "advanced", "4": "expert"}
progress["experience_level"] = exp_map.get(exp_choice, "intermediate")
print("\nWhat's your goal today?")
print(" 1) Learn CODITECT quickly (30 min)")
print(" 2) Complete certification training (4-6 hours)")
print(" 3) Start a specific project immediately")
goal_choice = input("\nYour choice (1-3): ").strip()
goal_map = {"1": "quick_learn", "2": "certification", "3": "project_first"}
progress["goal"] = goal_map.get(goal_choice, "project_first")
progress["phases"]["welcome"] = {
"completed": True,
"at": datetime.now(timezone.utc).isoformat()
}
# Phase 2: Environment Check
env_results = check_environment()
progress["phases"]["environment"] = {
"completed": env_results["all_ok"],
"at": datetime.now(timezone.utc).isoformat(),
"details": {k: v["ok"] for k, v in env_results.items() if k != "all_ok"}
}
if not env_results["all_ok"]:
print_warning("Please fix the environment issues above and run again.")
save_progress(progress)
return
# Phase 3: Shell Setup
print_step("Shell Setup")
setup_choice = input("\nWould you like me to add the AI Command Router aliases? (y/n): ").strip().lower()
if setup_choice == "y":
shell_ok = setup_shell_aliases()
else:
shell_ok = True
print_info("Skipped shell setup")
progress["phases"]["shell_setup"] = {
"completed": True,
"at": datetime.now(timezone.utc).isoformat()
}
# Phase 4: Project Creation
print_step("Project Creation")
project_name = input("\nWhat would you like to name your project? ").strip()
if not project_name:
project_name = "my-coditect-project"
project_ok = create_project_structure(project_name)
progress["phases"]["project_creation"] = {
"completed": project_ok,
"at": datetime.now(timezone.utc).isoformat(),
"project_name": project_name
}
# Phase 5: First Agent Info
print_step("Task Tool Pattern (CRITICAL)")
print("""
The Task Tool Pattern is the ONLY way to invoke CODITECT agents correctly:
Task(
subagent_type="general-purpose",
prompt="Use [agent-name] subagent to [detailed task]"
)
Three rules:
- subagent_type is ALWAYS "general-purpose"
- prompt MUST start with "Use [agent-name] subagent to"
- Include detailed requirements in your prompt
Example: Task( subagent_type="general-purpose", prompt="Use orchestrator subagent to create a project plan for a recipe management API with authentication and search" ) """)
input("\nPress Enter when you've read this...")
progress["phases"]["first_agent"] = {
"completed": True,
"at": datetime.now(timezone.utc).isoformat()
}
# Phase 6: Session Management Info
print_step("Session Management")
print("""
Before ending your session, save your context:
/cx "description of work" # Quick capture
/cxs # Full session save
For major milestones:
python3 .coditect/scripts/create-checkpoint.py "Description" --auto-push
This preserves your context so you can resume where you left off. """)
input("\nPress Enter when you've read this...")
progress["phases"]["session_management"] = {
"completed": True,
"at": datetime.now(timezone.utc).isoformat()
}
# Phase 7: Next Steps
print_step("Next Steps")
print(f"""
Based on your goal ({progress['goal']}), here's what to do next:
- Navigate to your project: cd {project_name}
- Use AI Command Router: cr "what you want to do"
- Invoke agents using the Task Tool Pattern
- Save context before ending: /cxs
Resources:
-
Quick Start: docs/01-getting-started/USER-QUICK-START.md
-
Training: docs/11-training-certification/CODITECT-OPERATOR-TRAINING-SYSTEM.md
-
FAQ: docs/11-training-certification/CODITECT-OPERATOR-FAQ.md """)
progress["phases"]["next_steps"] = { "completed": True, "at": datetime.now(timezone.utc).isoformat() }
progress["completed_at"] = datetime.now(timezone.utc).isoformat()
Save progress
save_progress(progress, project_name)
print_celebration("Onboarding Complete!") print(f"You're now a beginning CODITECT Operator. Happy building!\n")
def main(): parser = argparse.ArgumentParser( description="CODITECT Onboarding Wizard", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: python3 scripts/onboard-wizard.py # Interactive onboarding python3 scripts/onboard-wizard.py --check-env # Environment check only python3 scripts/onboard-wizard.py --status # Show progress """ )
parser.add_argument("--check-env", action="store_true",
help="Check environment only")
parser.add_argument("--auto-setup", action="store_true",
help="Non-interactive setup with defaults")
parser.add_argument("--project-name", type=str, default="my-project",
help="Project name for auto-setup")
parser.add_argument("--status", action="store_true",
help="Show onboarding progress")
parser.add_argument("--reset", action="store_true",
help="Reset onboarding progress")
args = parser.parse_args()
if args.check_env:
check_environment()
elif args.status:
show_status()
elif args.reset:
progress_file = Path(".coditect/onboarding-progress.json")
if progress_file.exists():
progress_file.unlink()
print_success("Onboarding progress reset")
else:
print_info("No progress to reset")
elif args.auto_setup:
check_environment()
setup_shell_aliases()
create_project_structure(args.project_name)
else:
interactive_onboarding()
if name == "main": main()