#!/bin/bash # # Design System Server (DSS) - Project Discovery Script # # Non-intrusive analysis of project structure, dependencies, and health. # Outputs JSON for UI consumption. # # Usage: ./discover.sh [project_path] [--full] # set -e PROJECT_PATH="${1:-.}" FULL_SCAN="${2:-}" OUTPUT_DIR="${PROJECT_PATH}/.dss" TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") # Create output directory mkdir -p "$OUTPUT_DIR" # Colors for terminal output (only if interactive) if [ -t 1 ]; then GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' NC='\033[0m' else GREEN='' BLUE='' YELLOW='' NC='' fi log() { echo -e "${BLUE}[DSS]${NC} $1" >&2 } # === Project Type Detection === detect_project_type() { local types=() [ -f "$PROJECT_PATH/package.json" ] && types+=("nodejs") [ -f "$PROJECT_PATH/requirements.txt" ] || [ -f "$PROJECT_PATH/pyproject.toml" ] && types+=("python") [ -f "$PROJECT_PATH/Cargo.toml" ] && types+=("rust") [ -f "$PROJECT_PATH/go.mod" ] && types+=("go") [ -f "$PROJECT_PATH/pom.xml" ] || [ -f "$PROJECT_PATH/build.gradle" ] && types+=("java") [ -f "$PROJECT_PATH/Gemfile" ] && types+=("ruby") [ -f "$PROJECT_PATH/composer.json" ] && types+=("php") echo "${types[@]:-unknown}" } # === Framework Detection === detect_frameworks() { local frameworks=() if [ -f "$PROJECT_PATH/package.json" ]; then local pkg=$(cat "$PROJECT_PATH/package.json") echo "$pkg" | grep -q '"react"' && frameworks+=("react") echo "$pkg" | grep -q '"vue"' && frameworks+=("vue") echo "$pkg" | grep -q '"@angular/core"' && frameworks+=("angular") echo "$pkg" | grep -q '"svelte"' && frameworks+=("svelte") echo "$pkg" | grep -q '"next"' && frameworks+=("nextjs") echo "$pkg" | grep -q '"nuxt"' && frameworks+=("nuxt") echo "$pkg" | grep -q '"express"' && frameworks+=("express") echo "$pkg" | grep -q '"fastify"' && frameworks+=("fastify") echo "$pkg" | grep -q '"tailwindcss"' && frameworks+=("tailwind") echo "$pkg" | grep -q '"@emotion"' && frameworks+=("emotion") echo "$pkg" | grep -q '"styled-components"' && frameworks+=("styled-components") fi if [ -f "$PROJECT_PATH/requirements.txt" ]; then grep -q "fastapi" "$PROJECT_PATH/requirements.txt" && frameworks+=("fastapi") grep -q "django" "$PROJECT_PATH/requirements.txt" && frameworks+=("django") grep -q "flask" "$PROJECT_PATH/requirements.txt" && frameworks+=("flask") fi echo "${frameworks[@]:-none}" } # === Design System Detection === detect_design_system() { local ds_info='{"detected":false}' # Check for common design system indicators if [ -f "$PROJECT_PATH/package.json" ]; then local pkg=$(cat "$PROJECT_PATH/package.json") if echo "$pkg" | grep -qE '"(@chakra-ui|@mui|antd|@radix-ui|@headlessui)"'; then ds_info='{"detected":true,"type":"library"}' fi fi # Check for custom design tokens if find "$PROJECT_PATH" -maxdepth 3 -name "tokens.css" -o -name "tokens.json" -o -name "design-tokens.*" 2>/dev/null | grep -q .; then ds_info='{"detected":true,"type":"custom","has_tokens":true}' fi # Check for Figma integration if find "$PROJECT_PATH" -maxdepth 3 -name ".figmarc" -o -name "figma.config.*" 2>/dev/null | grep -q .; then ds_info=$(echo "$ds_info" | sed 's/}$/,"figma_connected":true}/') fi echo "$ds_info" } # === File Statistics === get_file_stats() { local total_files=$(find "$PROJECT_PATH" -type f ! -path "*/node_modules/*" ! -path "*/.git/*" ! -path "*/dist/*" ! -path "*/__pycache__/*" 2>/dev/null | wc -l) local js_files=$(find "$PROJECT_PATH" -type f \( -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" \) ! -path "*/node_modules/*" 2>/dev/null | wc -l) local css_files=$(find "$PROJECT_PATH" -type f \( -name "*.css" -o -name "*.scss" -o -name "*.less" \) ! -path "*/node_modules/*" 2>/dev/null | wc -l) local py_files=$(find "$PROJECT_PATH" -type f -name "*.py" ! -path "*/__pycache__/*" 2>/dev/null | wc -l) local component_files=$(find "$PROJECT_PATH" -type f \( -name "*.jsx" -o -name "*.tsx" -o -name "*.vue" -o -name "*.svelte" \) ! -path "*/node_modules/*" 2>/dev/null | wc -l) cat </dev/null || echo 0) local dev_count=$(jq '.devDependencies | length // 0' "$PROJECT_PATH/package.json" 2>/dev/null || echo 0) local total=$((prod_count + dev_count)) deps="{\"production\":$prod_count,\"development\":$dev_count,\"total\":$total}" fi if [ -f "$PROJECT_PATH/requirements.txt" ]; then local py_deps=$(grep -v "^#" "$PROJECT_PATH/requirements.txt" | grep -v "^$" | wc -l) deps="{\"python\":$py_deps,\"total\":$py_deps}" fi echo "$deps" } # === Git Analysis === analyze_git() { if [ ! -d "$PROJECT_PATH/.git" ]; then echo '{"is_repo":false}' return fi cd "$PROJECT_PATH" local branch=$(git branch --show-current 2>/dev/null || echo "unknown") local commits=$(git rev-list --count HEAD 2>/dev/null || echo 0) local contributors=$(git log --format='%ae' | sort -u | wc -l 2>/dev/null || echo 0) local last_commit=$(git log -1 --format='%ci' 2>/dev/null || echo "unknown") local uncommitted=$(git status --porcelain 2>/dev/null | wc -l || echo 0) cat </dev/null | head -50) # Join array local joined=$(IFS=,; echo "${components[*]}") echo "[$joined]" } # === Health Score === calculate_health_score() { local score=100 local issues=() # Check for package-lock or yarn.lock if [ -f "$PROJECT_PATH/package.json" ]; then if [ ! -f "$PROJECT_PATH/package-lock.json" ] && [ ! -f "$PROJECT_PATH/yarn.lock" ] && [ ! -f "$PROJECT_PATH/pnpm-lock.yaml" ]; then score=$((score - 10)) issues+=("\"No lock file found\"") fi fi # Check for .gitignore if [ -d "$PROJECT_PATH/.git" ] && [ ! -f "$PROJECT_PATH/.gitignore" ]; then score=$((score - 5)) issues+=("\"Missing .gitignore\"") fi # Check for README if [ ! -f "$PROJECT_PATH/README.md" ] && [ ! -f "$PROJECT_PATH/README" ]; then score=$((score - 5)) issues+=("\"Missing README\"") fi # Check for tests if ! find "$PROJECT_PATH" -maxdepth 3 -type d \( -name "test" -o -name "tests" -o -name "__tests__" -o -name "spec" \) 2>/dev/null | grep -q .; then score=$((score - 10)) issues+=("\"No test directory found\"") fi # Check for TypeScript if [ -f "$PROJECT_PATH/package.json" ] && ! [ -f "$PROJECT_PATH/tsconfig.json" ]; then if grep -q "typescript" "$PROJECT_PATH/package.json" 2>/dev/null; then score=$((score - 5)) issues+=("\"TypeScript installed but no tsconfig.json\"") fi fi local joined_issues=$(IFS=,; echo "${issues[*]}") cat </dev/null) local total_files=$(echo "$css_files" | grep -c . || echo 0) local has_variables=false local has_custom_properties=false local preprocessor="none" if echo "$css_files" | grep -q ".scss"; then preprocessor="sass" fi if [ -n "$css_files" ]; then for file in $css_files; do if grep -q -- "--" "$file" 2>/dev/null; then has_custom_properties=true fi if grep -q "\\\$" "$file" 2>/dev/null; then has_variables=true fi done fi cat < "$OUTPUT_DIR/discovery.json" </dev/null || echo '["unknown"]'), "frameworks": $(echo "$FRAMEWORKS" | jq -R 'split(" ")' 2>/dev/null || echo '[]') }, "design_system": $DESIGN_SYSTEM, "files": $FILE_STATS, "dependencies": $DEPENDENCIES, "git": $GIT_INFO, "health": $HEALTH, "css": $CSS_INFO, "components": $COMPONENTS } EOF log "Discovery complete: $OUTPUT_DIR/discovery.json" # Output the JSON cat "$OUTPUT_DIR/discovery.json"