#!/usr/bin/env bash # ───────────────────────────────────────────────────────────────── # D.intelligence Agent Corps — Skill Bundle Installer # Version: 1.0.0 # ───────────────────────────────────────────────────────────────── # # Installs the D.intelligence Agent Corps skill bundle for # Claude Code and/or Claude Desktop on any macOS/Linux device. # # Usage: # ./install.sh # Interactive mode # ./install.sh --all # Install everything # ./install.sh --code-only # Claude Code skills only # ./install.sh --desktop-only # Claude Desktop skills only # ./install.sh --dry-run # Preview without changes # ./install.sh --uninstall # Remove installed skills # # Prerequisites: # - Python >= 3.12 # - pip or uv (for Python dependencies) # - Claude Code or Claude Desktop installed # - Git (to clone the repository if not already present) # # ───────────────────────────────────────────────────────────────── set -eu # ── Constants ──────────────────────────────────────────────────── VERSION="1.0.0" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SKILLS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" SHARED_DIR="$SCRIPT_DIR" # Skill definitions: number|name|description SKILLS=( "70|dintel-brand-guardian|Brand compliance review & content generation" "71|dintel-brand-editor|Brand-compliant copywriting & style evaluation" "72|dintel-doc-secretary|Document formatting, meeting notes, reports" "73|dintel-quotation-mgr|Quotation generation with multi-agent sub-system" "74|dintel-service-architect|Service scope design & module recommendation" "75|dintel-marketing-mgr|Marketing content pipeline management" "76|dintel-backoffice-mgr|Administrative operations, invoicing, contracts" "77|dintel-account-mgr|Client relationship management & monitoring" "88|dintel-skill-update|Cross-skill consistency management (meta-agent)" ) # Color output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # No Color # ── Helper Functions ───────────────────────────────────────────── log_info() { echo -e "${BLUE}[INFO]${NC} $*"; } log_success() { echo -e "${GREEN}[OK]${NC} $*"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } log_error() { echo -e "${RED}[ERR]${NC} $*"; } log_step() { echo -e "\n${BOLD}${CYAN}── $* ──${NC}"; } DRY_RUN=false INSTALL_CODE=false INSTALL_DESKTOP=false UNINSTALL=false # ── Platform Detection ─────────────────────────────────────────── detect_platform() { local os os="$(uname -s)" case "$os" in Darwin) PLATFORM="macos" ;; Linux) PLATFORM="linux" ;; *) log_error "Unsupported platform: $os"; exit 1 ;; esac } detect_claude_paths() { # Claude Code config CLAUDE_CODE_CONFIG="$HOME/.claude.json" CLAUDE_CODE_DIR="$HOME/.claude" # Claude Desktop config (platform-dependent) if [[ "$PLATFORM" == "macos" ]]; then CLAUDE_DESKTOP_CONFIG="$HOME/Library/Application Support/Claude/claude_desktop_config.json" CLAUDE_DESKTOP_DIR="$HOME/Library/Application Support/Claude" else CLAUDE_DESKTOP_CONFIG="$HOME/.config/Claude/claude_desktop_config.json" CLAUDE_DESKTOP_DIR="$HOME/.config/Claude" fi # Check which Claude products are installed HAS_CLAUDE_CODE=false HAS_CLAUDE_DESKTOP=false if command -v claude &>/dev/null || [[ -f "$CLAUDE_CODE_CONFIG" ]]; then HAS_CLAUDE_CODE=true fi if [[ -d "$CLAUDE_DESKTOP_DIR" ]]; then HAS_CLAUDE_DESKTOP=true fi } detect_python() { PYTHON_CMD="" for cmd in python3.13 python3.12 python3; do if command -v "$cmd" &>/dev/null; then local ver ver="$($cmd --version 2>&1 | grep -oE '[0-9]+\.[0-9]+')" local major minor major="${ver%%.*}" minor="${ver#*.}" if [[ "$major" -ge 3 && "$minor" -ge 12 ]]; then PYTHON_CMD="$cmd" PYTHON_VERSION="$ver" return fi fi done log_warn "Python >= 3.12 not found. Python utilities will not be installed." } detect_package_manager() { PKG_MGR="" if command -v uv &>/dev/null; then PKG_MGR="uv" elif command -v pip3 &>/dev/null; then PKG_MGR="pip3" elif command -v pip &>/dev/null; then PKG_MGR="pip" fi } # ── Parse Arguments ────────────────────────────────────────────── parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --all) INSTALL_CODE=true INSTALL_DESKTOP=true ;; --code-only) INSTALL_CODE=true ;; --desktop-only) INSTALL_DESKTOP=true ;; --dry-run) DRY_RUN=true ;; --uninstall) UNINSTALL=true ;; --help|-h) show_help exit 0 ;; *) log_error "Unknown option: $1" show_help exit 1 ;; esac shift done } show_help() { cat <<'HELP' D.intelligence Agent Corps — Skill Bundle Installer v1.0.0 Usage: ./install.sh Interactive mode ./install.sh --all Install for both Claude Code and Desktop ./install.sh --code-only Claude Code only ./install.sh --desktop-only Claude Desktop only ./install.sh --dry-run Preview without making changes ./install.sh --uninstall Remove installed skills Skills Included: #70 Brand Guardian Brand compliance review #71 Brand Editor Copywriting & style evaluation #72 Doc Secretary Document formatting & reports #73 Quotation Manager Quotation generation (multi-agent) #74 Service Architect Service scope & module design #75 Marketing Manager Content pipeline management #76 Back Office Manager Admin, invoicing, contracts #77 Account Manager Client relationship & monitoring #88 Skill Update Cross-skill consistency (meta-agent) Shared Infrastructure: dintel-shared/ Python package + reference docs HELP } # ── Pre-flight Checks ──────────────────────────────────────────── preflight() { log_step "Pre-flight Checks" detect_platform log_info "Platform: $PLATFORM" detect_claude_paths if $HAS_CLAUDE_CODE; then log_success "Claude Code detected" else log_warn "Claude Code not detected" fi if $HAS_CLAUDE_DESKTOP; then log_success "Claude Desktop detected" else log_warn "Claude Desktop not detected" fi detect_python if [[ -n "$PYTHON_CMD" ]]; then log_success "Python $PYTHON_VERSION ($PYTHON_CMD)" fi detect_package_manager if [[ -n "$PKG_MGR" ]]; then log_success "Package manager: $PKG_MGR" else log_warn "No pip/uv found — skipping Python package install" fi # Verify skill directories exist local missing=0 for entry in "${SKILLS[@]}"; do IFS='|' read -r num name desc <<< "$entry" local dir="$SKILLS_ROOT/${num}-${name}" if [[ ! -d "$dir" ]]; then log_error "Missing skill directory: ${num}-${name}/" ((missing++)) fi done if [[ $missing -gt 0 ]]; then log_error "$missing skill directories not found. Is the repository complete?" exit 1 fi log_success "All 9 skill directories present" # Verify shared infrastructure if [[ ! -f "$SHARED_DIR/pyproject.toml" ]]; then log_error "dintel-shared/pyproject.toml not found" exit 1 fi if [[ ! -f "$SHARED_DIR/src/dintel/brand.py" ]]; then log_error "dintel-shared/src/dintel/brand.py not found" exit 1 fi log_success "Shared infrastructure intact" } # ── Interactive Mode ───────────────────────────────────────────── interactive_prompt() { if $INSTALL_CODE || $INSTALL_DESKTOP || $UNINSTALL; then return # Already specified via flags fi echo "" echo -e "${BOLD}D.intelligence Agent Corps — Skill Bundle Installer v${VERSION}${NC}" echo "" echo " Available targets:" if $HAS_CLAUDE_CODE; then echo -e " ${GREEN}[1]${NC} Claude Code (code/CLAUDE.md directives)" else echo -e " ${RED}[1]${NC} Claude Code (not detected)" fi if $HAS_CLAUDE_DESKTOP; then echo -e " ${GREEN}[2]${NC} Claude Desktop (desktop/SKILL.md skills)" else echo -e " ${RED}[2]${NC} Claude Desktop (not detected)" fi echo -e " ${GREEN}[3]${NC} Both" echo "" read -rp " Select target [1/2/3]: " choice case "$choice" in 1) INSTALL_CODE=true ;; 2) INSTALL_DESKTOP=true ;; 3) INSTALL_CODE=true; INSTALL_DESKTOP=true ;; *) log_error "Invalid choice" exit 1 ;; esac } # ── Install Python Environment ─────────────────────────────────── install_python_env() { if [[ -z "$PYTHON_CMD" || -z "$PKG_MGR" ]]; then log_warn "Skipping Python environment setup (missing Python or pip)" return fi log_step "Python Environment — dintel-shared" local venv_dir="$SHARED_DIR/.venv" if $DRY_RUN; then log_info "[DRY-RUN] Would create venv at: $venv_dir" log_info "[DRY-RUN] Would install dintel package in editable mode" return fi # Create virtual environment if it doesn't exist if [[ ! -d "$venv_dir" ]]; then log_info "Creating virtual environment..." "$PYTHON_CMD" -m venv "$venv_dir" log_success "Virtual environment created at $venv_dir" else log_info "Virtual environment already exists" fi # Activate and install log_info "Installing dintel package (editable mode)..." "$venv_dir/bin/pip" install --quiet --upgrade pip "$venv_dir/bin/pip" install --quiet -e "$SHARED_DIR" log_success "dintel package installed" # Verify if "$venv_dir/bin/python" -c "from dintel.brand import BRAND_NAME; print(f' Brand: {BRAND_NAME}')" 2>/dev/null; then log_success "Package verification passed" else log_error "Package verification failed" return 1 fi } # ── Install Claude Code Skills ─────────────────────────────────── install_code_skills() { if ! $INSTALL_CODE; then return; fi log_step "Claude Code Skills (code/CLAUDE.md)" # Claude Code skills are activated by project CLAUDE.md includes. # We create a project-level CLAUDE.md fragment that includes all skills. local target_dir if [[ -t 0 ]]; then read -rp " Service Package repo path [default: current dir]: " target_dir else target_dir="" fi target_dir="${target_dir:-$(pwd)}" # Resolve to absolute target_dir="$(cd "$target_dir" 2>/dev/null && pwd)" || { log_error "Invalid path: $target_dir" return 1 } local claude_md="$target_dir/CLAUDE.md" local include_block="" # Generate include block for CLAUDE.md include_block+=$'\n'"# ── D.intelligence Agent Corps Skills ────────────────────────"$'\n' include_block+="# Auto-generated by dintel-shared/install.sh v${VERSION}"$'\n' include_block+="# Skills are loaded from: ${SKILLS_ROOT}"$'\n' include_block+="#"$'\n' include_block+="# To activate a specific agent in Claude Code, reference its CLAUDE.md:"$'\n' for entry in "${SKILLS[@]}"; do IFS='|' read -r num name desc <<< "$entry" local skill_claude_md="$SKILLS_ROOT/${num}-${name}/code/CLAUDE.md" if [[ -f "$skill_claude_md" ]]; then include_block+="# Agent #${num} (${name}): ${skill_claude_md}"$'\n' fi done include_block+="# ─────────────────────────────────────────────────────────────"$'\n' if $DRY_RUN; then log_info "[DRY-RUN] Would append agent index to: $claude_md" echo "$include_block" return fi # Create symlinks in the project for easy access local agents_dir="$target_dir/.claude-agents" mkdir -p "$agents_dir" for entry in "${SKILLS[@]}"; do IFS='|' read -r num name desc <<< "$entry" local src="$SKILLS_ROOT/${num}-${name}/code/CLAUDE.md" local dest="$agents_dir/${num}-${name}.md" if [[ -f "$src" ]]; then ln -sf "$src" "$dest" log_success "#${num} ${name} → symlinked" fi done # Symlink shared library ln -sf "$SHARED_DIR" "$agents_dir/dintel-shared" log_success "dintel-shared → symlinked" # Write a loader CLAUDE.md fragment cat > "$agents_dir/README.md" <> "$agents_dir/README.md" done cat >> "$agents_dir/README.md" < "$skills_target/dintel-index.md" <> "$skills_target/INDEX.md" done cat >> "$skills_target/INDEX.md" <