Skip to main content

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 references
  • docs/architecture/ - Technical decisions and diagrams
  • docs/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 documentation
  • src/ - Source code
  • tests/ - Test files

AI Agent Guidelines

  1. Read docs/original-research/ before making recommendations
  2. Follow existing code patterns
  3. 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:

  1. subagent_type is ALWAYS "general-purpose"
  2. prompt MUST start with "Use [agent-name] subagent to"
  3. 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:

  1. Navigate to your project: cd {project_name}
  2. Use AI Command Router: cr "what you want to do"
  3. Invoke agents using the Task Tool Pattern
  4. 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()