From 2ea1920a89bbceccfba652d6243459fd3a9bdfdf Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 07:49:16 +0000 Subject: [PATCH 01/26] refactor: remove unused test CLI tools - cmd/test-adapter: manual adapter testing utility - cmd/test-rbac: manual RBAC testing utility --- cmd/test-adapter/main.go | 176 --------------------------------------- cmd/test-rbac/main.go | 97 --------------------- 2 files changed, 273 deletions(-) delete mode 100644 cmd/test-adapter/main.go delete mode 100644 cmd/test-rbac/main.go diff --git a/cmd/test-adapter/main.go b/cmd/test-adapter/main.go deleted file mode 100644 index ee1f9d1..0000000 --- a/cmd/test-adapter/main.go +++ /dev/null @@ -1,176 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/DevSymphony/sym-cli/internal/adapter" - "github.com/DevSymphony/sym-cli/internal/adapter/eslint" - "github.com/DevSymphony/sym-cli/internal/adapter/prettier" - "github.com/DevSymphony/sym-cli/internal/adapter/tsc" -) - -// test-adapter is a CLI tool to test individual adapters -// Usage: go run cmd/test-adapter/main.go [files...] -// -// Examples: -// go run cmd/test-adapter/main.go eslint src/app.js -// go run cmd/test-adapter/main.go tsc -// go run cmd/test-adapter/main.go prettier --check - -func main() { - if len(os.Args) < 2 { - fmt.Println("Usage: test-adapter [files...]") - fmt.Println("\nAvailable adapters:") - fmt.Println(" - eslint") - fmt.Println(" - prettier") - fmt.Println(" - tsc") - fmt.Println("\nExamples:") - fmt.Println(" test-adapter eslint") - fmt.Println(" test-adapter tsc") - fmt.Println(" test-adapter prettier --check") - os.Exit(1) - } - - adapterName := os.Args[1] - files := os.Args[2:] - - // Get current directory - workDir, err := os.Getwd() - if err != nil { - fmt.Printf("Error: %v\n", err) - os.Exit(1) - } - - symDir := filepath.Join(workDir, ".sym") - toolsDir := filepath.Join(os.Getenv("HOME"), ".sym", "tools") - - // Create adapter - var adp adapter.Adapter - switch adapterName { - case "eslint": - adp = eslint.NewAdapter(toolsDir, workDir) - case "prettier": - adp = prettier.NewAdapter(toolsDir, workDir) - case "tsc": - adp = tsc.NewAdapter(toolsDir, workDir) - default: - fmt.Printf("Unknown adapter: %s\n", adapterName) - os.Exit(1) - } - - fmt.Printf("๐Ÿ”ง Testing adapter: %s\n", adp.Name()) - fmt.Printf("๐Ÿ“ Working directory: %s\n", workDir) - fmt.Printf("๐Ÿ“ .sym directory: %s\n", symDir) - fmt.Printf("๐Ÿ“ Tools directory: %s\n\n", toolsDir) - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - // Check availability - fmt.Printf("๐Ÿ” Checking availability...\n") - if err := adp.CheckAvailability(ctx); err != nil { - fmt.Printf("โš ๏ธ Not available: %v\n", err) - fmt.Printf("๐Ÿ“ฆ Installing...\n") - if err := adp.Install(ctx, adapter.InstallConfig{ - ToolsDir: toolsDir, - }); err != nil { - fmt.Printf("โŒ Installation failed: %v\n", err) - os.Exit(1) - } - fmt.Printf("โœ… Installed successfully\n\n") - } else { - fmt.Printf("โœ… Available\n\n") - } - - // Load config from .sym directory - var config []byte - configPath := getConfigPath(symDir, adapterName) - - fmt.Printf("๐Ÿ“„ Looking for config: %s\n", configPath) - if data, err := os.ReadFile(configPath); err == nil { - config = data - fmt.Printf("โœ… Using config from %s\n\n", configPath) - } else { - fmt.Printf("โš ๏ธ No config found, using default\n\n") - config = []byte("{}") - } - - // If no files specified, let the adapter use its default file discovery - if len(files) == 0 { - fmt.Printf("๐Ÿ“‚ No files specified, adapter will use its default file discovery\n\n") - } else { - fmt.Printf("๐Ÿ“‚ Files to check: %v\n\n", files) - } - - // Execute adapter - fmt.Printf("๐Ÿš€ Running %s...\n", adapterName) - output, err := adp.Execute(ctx, config, files) - if err != nil { - fmt.Printf("โŒ Execution failed: %v\n", err) - os.Exit(1) - } - - // Display results - fmt.Printf("\n๐Ÿ“Š Results:\n") - fmt.Printf("Exit Code: %d\n", output.ExitCode) - fmt.Printf("Duration: %s\n\n", output.Duration) - - if output.Stdout != "" { - fmt.Printf("๐Ÿ“ค Stdout:\n%s\n\n", output.Stdout) - } - - if output.Stderr != "" { - fmt.Printf("๐Ÿ“ค Stderr:\n%s\n\n", output.Stderr) - } - - // Parse violations - violations, err := adp.ParseOutput(output) - if err != nil { - fmt.Printf("โš ๏ธ Failed to parse output: %v\n", err) - } else { - fmt.Printf("๐Ÿ” Found %d violation(s):\n\n", len(violations)) - for i, v := range violations { - fmt.Printf("[%d] %s:%d:%d\n", i+1, v.File, v.Line, v.Column) - fmt.Printf(" Severity: %s\n", v.Severity) - fmt.Printf(" Message: %s\n", v.Message) - if v.RuleID != "" { - fmt.Printf(" Rule: %s\n", v.RuleID) - } - fmt.Printf("\n") - } - } - - // Print summary as JSON - summary := map[string]interface{}{ - "adapter": adapterName, - "exitCode": output.ExitCode, - "duration": output.Duration, - "violations": len(violations), - } - - summaryJSON, _ := json.MarshalIndent(summary, "", " ") - fmt.Printf("\n๐Ÿ“‹ Summary:\n%s\n", string(summaryJSON)) - - // Exit with appropriate code - if output.ExitCode != 0 || len(violations) > 0 { - os.Exit(1) - } -} - -func getConfigPath(symDir, adapterName string) string { - switch adapterName { - case "eslint": - return filepath.Join(symDir, ".eslintrc.json") - case "prettier": - return filepath.Join(symDir, ".prettierrc.json") - case "tsc": - return filepath.Join(symDir, "tsconfig.json") - default: - return filepath.Join(symDir, adapterName+".json") - } -} diff --git a/cmd/test-rbac/main.go b/cmd/test-rbac/main.go deleted file mode 100644 index 8466ab3..0000000 --- a/cmd/test-rbac/main.go +++ /dev/null @@ -1,97 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/DevSymphony/sym-cli/internal/roles" -) - -func main() { - // Change to test directory - if err := os.Chdir("/tmp/rbac-test"); err != nil { - fmt.Printf("โŒ Failed to change directory: %v\n", err) - return - } - - fmt.Println("๐Ÿงช RBAC ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ ์‹œ์ž‘") - fmt.Println("================================================================") - - // Test scenarios - testCases := []struct { - name string - username string - files []string - }{ - { - name: "Frontend Dev - ํ—ˆ์šฉ๋œ ํŒŒ์ผ๋งŒ", - username: "alice", - files: []string{ - "src/components/Button.js", - "src/components/ui/Modal.js", - "src/hooks/useAuth.js", - }, - }, - { - name: "Frontend Dev - ๊ฑฐ๋ถ€๋œ ํŒŒ์ผ ํฌํ•จ", - username: "alice", - files: []string{ - "src/components/Button.js", - "src/core/engine.js", - "src/api/client.js", - }, - }, - { - name: "Senior Dev - ๋ชจ๋“  ํŒŒ์ผ", - username: "charlie", - files: []string{ - "src/components/Button.js", - "src/core/engine.js", - "src/api/client.js", - "src/utils/helper.js", - }, - }, - { - name: "Viewer - ์ฝ๊ธฐ ์ „์šฉ", - username: "david", - files: []string{ - "src/components/Button.js", - }, - }, - { - name: "Frontend Dev - ํ˜ผํ•ฉ ์ผ€์ด์Šค", - username: "bob", - files: []string{ - "src/hooks/useData.js", - "src/core/config.js", - "src/utils/format.js", - "src/components/Header.js", - }, - }, - } - - for i, tc := range testCases { - fmt.Printf("\n๐Ÿ“‹ ํ…Œ์ŠคํŠธ %d: %s\n", i+1, tc.name) - fmt.Printf(" ์‚ฌ์šฉ์ž: %s\n", tc.username) - fmt.Printf(" ํŒŒ์ผ ์ˆ˜: %d๊ฐœ\n", len(tc.files)) - - result, err := roles.ValidateFilePermissions(tc.username, tc.files) - if err != nil { - fmt.Printf(" โŒ ์˜ค๋ฅ˜: %v\n", err) - continue - } - - if result.Allowed { - fmt.Printf(" โœ… ๊ฒฐ๊ณผ: ๋ชจ๋“  ํŒŒ์ผ ์ˆ˜์ • ๊ฐ€๋Šฅ\n") - } else { - fmt.Printf(" โŒ ๊ฒฐ๊ณผ: %d๊ฐœ ํŒŒ์ผ ์ˆ˜์ • ๋ถˆ๊ฐ€\n", len(result.DeniedFiles)) - fmt.Printf(" ๊ฑฐ๋ถ€๋œ ํŒŒ์ผ:\n") - for _, file := range result.DeniedFiles { - fmt.Printf(" - %s\n", file) - } - } - } - - fmt.Println("\n================================================================") - fmt.Println("โœ… ํ…Œ์ŠคํŠธ ์™„๋ฃŒ!") -} From 7722419b514e19c15e831a1c67b0e7222483f232 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 07:51:47 +0000 Subject: [PATCH 02/26] refactor: remove unused examples directory --- examples/check-access.sh | 51 ---------------------------------- examples/deploy.sh | 60 ---------------------------------------- examples/roles.json | 15 ---------- 3 files changed, 126 deletions(-) delete mode 100644 examples/check-access.sh delete mode 100644 examples/deploy.sh delete mode 100644 examples/roles.json diff --git a/examples/check-access.sh b/examples/check-access.sh deleted file mode 100644 index fe026db..0000000 --- a/examples/check-access.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -# ๊ฐ„๋‹จํ•œ ์•ก์„ธ์Šค ์ฒดํฌ ์Šคํฌ๋ฆฝํŠธ - -# ํ˜„์žฌ ์‚ฌ์šฉ์ž ์ •๋ณด -USER=$(symphony whoami --json 2>/dev/null | jq -r '.username') -ROLE=$(symphony my-role --json 2>/dev/null | jq -r '.role') -REPO=$(symphony my-role --json 2>/dev/null | jq -r '"\(.owner)/\(.repo)"') - -if [ -z "$USER" ] || [ "$USER" == "null" ]; then - echo "โŒ Not logged in" - echo "Run: symphony login" - exit 1 -fi - -echo "Access Check Report" -echo "===================" -echo "User: $USER" -echo "Repository: $REPO" -echo "Role: $ROLE" -echo "" - -# ์—ญํ• ๋ณ„ ๊ถŒํ•œ ํ‘œ์‹œ -case "$ROLE" in - "admin") - echo "Permissions:" - echo " โœ“ Read files" - echo " โœ“ Write files" - echo " โœ“ Deploy to production" - echo " โœ“ Manage roles" - ;; - "developer") - echo "Permissions:" - echo " โœ“ Read files" - echo " โœ“ Write files" - echo " โœ“ Deploy to staging" - echo " โœ— Deploy to production" - echo " โœ— Manage roles" - ;; - "viewer") - echo "Permissions:" - echo " โœ“ Read files" - echo " โœ— Write files" - echo " โœ— Deploy" - echo " โœ— Manage roles" - ;; - "none") - echo "โš ๏ธ No permissions assigned" - echo "Contact an admin to get access" - ;; -esac diff --git a/examples/deploy.sh b/examples/deploy.sh deleted file mode 100644 index 3a7bc26..0000000 --- a/examples/deploy.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -# ๋ฐฐํฌ ๊ถŒํ•œ ์ฒดํฌ ์˜ˆ์ œ ์Šคํฌ๋ฆฝํŠธ - -echo "๐Ÿš€ Deployment Permission Check" -echo "==============================" - -# Symphony CLI API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ญํ•  ํ™•์ธ -echo "Checking your role..." - -ROLE=$(symphony my-role --json 2>/dev/null | jq -r '.role') - -if [ $? -ne 0 ]; then - echo "โŒ Error: Failed to check role" - echo " Make sure you are logged in: symphony login" - exit 1 -fi - -echo "Current role: $ROLE" - -# ์—ญํ• ์— ๋”ฐ๋ฅธ ๊ถŒํ•œ ์ฒดํฌ -case "$ROLE" in - "admin") - echo "โœ“ Admin access: Full deployment permissions" - DEPLOYMENT_ENV="production" - ;; - "developer") - echo "โœ“ Developer access: Staging deployment only" - DEPLOYMENT_ENV="staging" - ;; - "viewer"|"none") - echo "โŒ Error: Insufficient permissions for deployment" - echo " Your role ($ROLE) does not allow deployments" - echo " Contact an admin to get developer or admin access" - exit 1 - ;; - *) - echo "โŒ Error: Unknown role: $ROLE" - exit 1 - ;; -esac - -echo "" -echo "โœ“ Permission check passed" -echo "Deploying to: $DEPLOYMENT_ENV" -echo "" - -# ์‹ค์ œ ๋ฐฐํฌ ๋กœ์ง (์˜ˆ์‹œ) -echo "Running deployment..." -echo " - Building application..." -# npm run build - -echo " - Running tests..." -# npm test - -echo " - Deploying to $DEPLOYMENT_ENV..." -# ./deploy-to-$DEPLOYMENT_ENV.sh - -echo "" -echo "โœ… Deployment completed successfully!" diff --git a/examples/roles.json b/examples/roles.json deleted file mode 100644 index 5a96412..0000000 --- a/examples/roles.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "admin": [ - "alice", - "bob" - ], - "developer": [ - "charlie", - "david", - "eve" - ], - "viewer": [ - "frank", - "grace" - ] -} From 7bfb5f0c66fd60ddd5bcd66d00dc661037e3785b Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 07:56:10 +0000 Subject: [PATCH 03/26] refactor: remove unused scripts directory - build.sh (duplicated with Makefile) - validate-eslint.sh, validate-checkstyle.sh - installers/ (replaced by npm publish) --- docs/LINTER_VALIDATION.md | 19 +-- scripts/build.sh | 59 ------- scripts/installers/install-unix.sh | 197 ----------------------- scripts/installers/install-windows.ps1 | 129 --------------- scripts/installers/uninstall-unix.sh | 129 --------------- scripts/installers/uninstall-windows.ps1 | 128 --------------- scripts/validate-checkstyle.sh | 29 ---- scripts/validate-eslint.sh | 36 ----- 8 files changed, 1 insertion(+), 725 deletions(-) delete mode 100644 scripts/build.sh delete mode 100644 scripts/installers/install-unix.sh delete mode 100644 scripts/installers/install-windows.ps1 delete mode 100644 scripts/installers/uninstall-unix.sh delete mode 100644 scripts/installers/uninstall-windows.ps1 delete mode 100755 scripts/validate-checkstyle.sh delete mode 100755 scripts/validate-eslint.sh diff --git a/docs/LINTER_VALIDATION.md b/docs/LINTER_VALIDATION.md index c425461..7f034ca 100644 --- a/docs/LINTER_VALIDATION.md +++ b/docs/LINTER_VALIDATION.md @@ -20,18 +20,6 @@ The convert feature generates linter-specific configurations from natural langua - **SonarQube**: Multi-language static analysis - **LLM Validator**: Custom rules that cannot be expressed in traditional linters -## Validation Scripts - -### Validate ESLint Config -```bash -./scripts/validate-eslint.sh -``` - -### Validate Checkstyle Config -```bash -./scripts/validate-checkstyle.sh -``` - ## Engine Assignment Each rule in `code-policy.json` has an `engine` field that specifies which tool validates it: @@ -49,12 +37,7 @@ Each rule in `code-policy.json` has an `engine` field that specifies which tool ```bash sym convert -i user-policy.json --targets eslint,checkstyle,pmd ``` -3. **Validate** generated configs: - ```bash - ./scripts/validate-eslint.sh - ./scripts/validate-checkstyle.sh - ``` -4. **Run linters** on git changes: +3. **Run linters** on git changes: ```bash # JavaScript/TypeScript eslint --config .sym/.eslintrc.json src/**/*.{js,ts} diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100644 index ff9883c..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash -# Symphony Multi-Platform Build Script - -set -e - -# Set UTF-8 encoding for proper emoji display -export LC_ALL="${LC_ALL:-en_US.UTF-8}" -export LANG="${LANG:-en_US.UTF-8}" - -# Ensure terminal supports UTF-8 -if [ -z "$LC_ALL" ] && [ -z "$LANG" ]; then - export LC_ALL=C.UTF-8 - export LANG=C.UTF-8 -fi - -VERSION=${1:-"1.0.0"} -OUTPUT_DIR="dist" - -echo "๐ŸŽต Building Symphony v${VERSION}" -echo "" - -# Clean previous builds -echo "๐Ÿงน Cleaning previous builds..." -rm -rf ${OUTPUT_DIR} -mkdir -p ${OUTPUT_DIR} - -# Build CSS first -echo "๐ŸŽจ Building Tailwind CSS..." -npm run build:css - -# Build for each platform -echo "" -echo "๐Ÿ“ฆ Building binaries..." - -# Windows AMD64 -echo " โš™๏ธ Building for Windows (amd64)..." -GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o ${OUTPUT_DIR}/symphony-windows-amd64.exe . - -# macOS AMD64 -echo " โš™๏ธ Building for macOS (amd64)..." -GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o ${OUTPUT_DIR}/symphony-darwin-amd64 . - -# macOS ARM64 (Apple Silicon) -echo " โš™๏ธ Building for macOS (arm64)..." -GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o ${OUTPUT_DIR}/symphony-darwin-arm64 . - -# Linux AMD64 -echo " โš™๏ธ Building for Linux (amd64)..." -GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o ${OUTPUT_DIR}/symphony-linux-amd64 . - -# Linux ARM64 -echo " โš™๏ธ Building for Linux (arm64)..." -GOOS=linux GOARCH=arm64 go build -ldflags "-s -w" -o ${OUTPUT_DIR}/symphony-linux-arm64 . - -echo "" -echo "โœ… Build complete!" -echo "" -echo "๐Ÿ“‚ Output directory: ${OUTPUT_DIR}/" -ls -lh ${OUTPUT_DIR}/ diff --git a/scripts/installers/install-unix.sh b/scripts/installers/install-unix.sh deleted file mode 100644 index f6ec16a..0000000 --- a/scripts/installers/install-unix.sh +++ /dev/null @@ -1,197 +0,0 @@ -#!/bin/bash -# Symphony macOS/Linux Installer - -set -e - -# Set UTF-8 encoding for proper emoji and Korean display -export LC_ALL="${LC_ALL:-en_US.UTF-8}" -export LANG="${LANG:-en_US.UTF-8}" -export LANGUAGE="${LANGUAGE:-en_US:en}" - -# Ensure terminal supports UTF-8 -if [ -z "$LC_ALL" ] && [ -z "$LANG" ]; then - export LC_ALL=C.UTF-8 - export LANG=C.UTF-8 -fi - -BLUE='\033[0;34m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' # No Color - -echo -e "${BLUE}๐ŸŽต Symphony Installer${NC}" -echo "" - -# Detect OS and architecture -OS=$(uname -s | tr '[:upper:]' '[:lower:]') -ARCH=$(uname -m) - -case "$ARCH" in - x86_64) - ARCH="amd64" - ;; - arm64|aarch64) - ARCH="arm64" - ;; - *) - echo -e "${RED}โŒ Unsupported architecture: $ARCH${NC}" - exit 1 - ;; -esac - -BINARY_NAME="symphony-${OS}-${ARCH}" - -echo -e "${GREEN}โœ“ Detected: $OS ($ARCH)${NC}" -echo "" - -# Check if binary exists -if [ ! -f "$BINARY_NAME" ]; then - echo -e "${RED}โŒ Error: $BINARY_NAME not found in current directory${NC}" - echo -e "${YELLOW} Please run this script from the dist/ directory${NC}" - exit 1 -fi - -# Determine installation method -if [ "$EUID" -eq 0 ] || sudo -n true 2>/dev/null; then - # Running as root or can sudo - INSTALL_DIR="/usr/local/bin" - USE_SUDO=true - echo -e "${GREEN}โœ“ Will install to system directory: $INSTALL_DIR${NC}" -else - # Install to user directory - INSTALL_DIR="$HOME/.local/bin" - USE_SUDO=false - echo -e "${YELLOW}โš  Installing to user directory: $INSTALL_DIR${NC}" - echo -e "${YELLOW} (Run with sudo to install system-wide)${NC}" -fi - -echo "" -echo -e "${BLUE}๐Ÿ“‚ Installation directory: $INSTALL_DIR${NC}" -echo "" - -# Ask for confirmation -read -p "Continue with installation? (Y/n) " -n 1 -r -echo -if [[ $REPLY =~ ^[Nn]$ ]]; then - echo -e "${RED}โŒ Installation cancelled${NC}" - exit 1 -fi - -# Create installation directory -echo -e "${BLUE}๐Ÿ“ Creating installation directory...${NC}" -if [ "$USE_SUDO" = true ]; then - sudo mkdir -p "$INSTALL_DIR" -else - mkdir -p "$INSTALL_DIR" -fi - -# Copy binary -echo -e "${BLUE}๐Ÿ“‹ Installing Symphony binary...${NC}" -if [ "$USE_SUDO" = true ]; then - sudo cp "$BINARY_NAME" "$INSTALL_DIR/symphony" - sudo chmod 755 "$INSTALL_DIR/symphony" -else - cp "$BINARY_NAME" "$INSTALL_DIR/symphony" - chmod 755 "$INSTALL_DIR/symphony" -fi - -# Configure PATH -echo "" -echo -e "${BLUE}๐Ÿ”ง Configuring PATH...${NC}" - -PATH_CONFIGURED=false - -# Check if already in PATH -if echo "$PATH" | grep -q "$INSTALL_DIR"; then - echo -e "${GREEN}โœ“ $INSTALL_DIR is already in PATH${NC}" - PATH_CONFIGURED=true -elif [ "$INSTALL_DIR" = "/usr/local/bin" ]; then - echo -e "${GREEN}โœ“ /usr/local/bin is typically in PATH by default${NC}" - PATH_CONFIGURED=true -else - # Need to add to PATH - echo -e "${YELLOW}โš  $INSTALL_DIR is not in PATH${NC}" - echo "" - read -p "Add to PATH automatically? (Y/n) " -n 1 -r - echo - - if [[ ! $REPLY =~ ^[Nn]$ ]]; then - # Detect shell - SHELL_NAME=$(basename "$SHELL") - case "$SHELL_NAME" in - bash) - SHELL_RC="$HOME/.bashrc" - ;; - zsh) - SHELL_RC="$HOME/.zshrc" - ;; - fish) - SHELL_RC="$HOME/.config/fish/config.fish" - ;; - *) - SHELL_RC="$HOME/.profile" - ;; - esac - - echo -e "${BLUE}๐Ÿ“ Adding to $SHELL_RC...${NC}" - - # Add to shell config - if [ "$SHELL_NAME" = "fish" ]; then - echo "" >> "$SHELL_RC" - echo "# Symphony" >> "$SHELL_RC" - echo "set -gx PATH $INSTALL_DIR \$PATH" >> "$SHELL_RC" - else - echo "" >> "$SHELL_RC" - echo "# Symphony" >> "$SHELL_RC" - echo "export PATH=\"$INSTALL_DIR:\$PATH\"" >> "$SHELL_RC" - fi - - echo -e "${GREEN}โœ“ Added to $SHELL_RC${NC}" - echo -e "${YELLOW}โš  Note: Run 'source $SHELL_RC' or restart your terminal${NC}" - PATH_CONFIGURED=true - - # Update current session - export PATH="$INSTALL_DIR:$PATH" - else - echo -e "${YELLOW}โŠ˜ Skipped PATH configuration${NC}" - echo -e "${YELLOW} To use Symphony, either:${NC}" - echo -e "${YELLOW} 1. Run: $INSTALL_DIR/symphony${NC}" - echo -e "${YELLOW} 2. Add '$INSTALL_DIR' to your PATH manually${NC}" - fi -fi - -# Verify installation -echo "" -echo -e "${BLUE}๐Ÿ” Verifying installation...${NC}" - -if [ -f "$INSTALL_DIR/symphony" ]; then - echo -e "${GREEN}โœ“ Binary installed successfully${NC}" - - # Try to run symphony - if "$INSTALL_DIR/symphony" whoami --help &>/dev/null; then - echo -e "${GREEN}โœ“ Symphony is executable${NC}" - else - echo -e "${YELLOW}โš  Warning: Could not execute symphony${NC}" - fi -else - echo -e "${RED}โŒ Installation failed: binary not found${NC}" - exit 1 -fi - -echo "" -echo -e "${GREEN}โœ… Symphony installed successfully!${NC}" -echo "" -echo -e "${BLUE}๐Ÿ“– Next steps:${NC}" -echo -e " ${NC}1. Configure: ${GREEN}symphony config${NC}" -echo -e " ${NC}2. Login: ${GREEN}symphony login${NC}" -echo -e " ${NC}3. Init repo: ${GREEN}symphony init${NC}" -echo -e " ${NC}4. Dashboard: ${GREEN}symphony dashboard${NC}" -echo "" -echo -e "${BLUE}๐Ÿ“š Documentation: See README.md${NC}" -echo "" - -if [ "$PATH_CONFIGURED" = false ]; then - echo -e "${YELLOW}โš  Remember to add $INSTALL_DIR to your PATH!${NC}" - echo "" -fi diff --git a/scripts/installers/install-windows.ps1 b/scripts/installers/install-windows.ps1 deleted file mode 100644 index 0b33b05..0000000 --- a/scripts/installers/install-windows.ps1 +++ /dev/null @@ -1,129 +0,0 @@ -๏ปฟ# Symphony Windows Installer -# Run with: powershell -ExecutionPolicy Bypass -File install-windows.ps1 - -$ErrorActionPreference = "Stop" - -# Set console encoding to UTF-8 for proper Korean display -$OutputEncoding = [System.Text.Encoding]::UTF8 -[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 -$PSDefaultParameterValues['*:Encoding'] = 'utf8' - -# Set console code page to UTF-8 (65001) -chcp 65001 > $null - -Write-Host "๐ŸŽต Symphony Windows Installer" -ForegroundColor Cyan -Write-Host "" - -# Check if running as Administrator -$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) -$isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - -# Determine installation directory -if ($isAdmin) { - $installDir = "C:\Program Files\Symphony" - Write-Host "โœ“ Running with administrator privileges" -ForegroundColor Green -} else { - $installDir = "$env:LOCALAPPDATA\Symphony" - Write-Host "โš  Not running as administrator - installing to user directory" -ForegroundColor Yellow -} - -Write-Host "๐Ÿ“‚ Installation directory: $installDir" -ForegroundColor Cyan -Write-Host "" - -# Ask for confirmation -$confirmation = Read-Host "Continue with installation? (Y/n)" -if ($confirmation -eq 'n' -or $confirmation -eq 'N') { - Write-Host "โŒ Installation cancelled" -ForegroundColor Red - exit 1 -} - -# Create installation directory -Write-Host "๐Ÿ“ Creating installation directory..." -New-Item -ItemType Directory -Force -Path $installDir | Out-Null - -# Copy binary -$binaryName = "symphony-windows-amd64.exe" -$targetBinary = "$installDir\symphony.exe" - -if (Test-Path $binaryName) { - Write-Host "๐Ÿ“‹ Copying Symphony binary..." - Copy-Item -Path $binaryName -Destination $targetBinary -Force -} else { - Write-Host "โŒ Error: $binaryName not found in current directory" -ForegroundColor Red - Write-Host " Please run this script from the dist/ directory" -ForegroundColor Yellow - exit 1 -} - -# Add to PATH -Write-Host "" -Write-Host "๐Ÿ”ง Configuring PATH environment variable..." -ForegroundColor Cyan - -$addToPath = Read-Host "Add Symphony to PATH? This allows you to run 'symphony' from anywhere (Y/n)" -if ($addToPath -ne 'n' -and $addToPath -ne 'N') { - try { - if ($isAdmin) { - # System PATH (requires admin) - $envTarget = [System.EnvironmentVariableTarget]::Machine - $pathScope = "system" - } else { - # User PATH - $envTarget = [System.EnvironmentVariableTarget]::User - $pathScope = "user" - } - - $currentPath = [Environment]::GetEnvironmentVariable("Path", $envTarget) - - if ($currentPath -notlike "*$installDir*") { - $newPath = "$currentPath;$installDir" - [Environment]::SetEnvironmentVariable("Path", $newPath, $envTarget) - Write-Host "โœ“ Added to $pathScope PATH" -ForegroundColor Green - - # Update current session PATH - $env:Path = "$env:Path;$installDir" - - Write-Host "" - Write-Host "โš  Note: You may need to restart your terminal for PATH changes to take effect" -ForegroundColor Yellow - } else { - Write-Host "โœ“ Already in PATH" -ForegroundColor Green - } - } catch { - Write-Host "โŒ Failed to update PATH: $_" -ForegroundColor Red - Write-Host " You can manually add '$installDir' to your PATH" -ForegroundColor Yellow - } -} else { - Write-Host "โŠ˜ Skipped PATH configuration" -ForegroundColor Yellow - Write-Host " To use Symphony, either:" -ForegroundColor Yellow - Write-Host " 1. Run: $targetBinary" -ForegroundColor Yellow - Write-Host " 2. Manually add '$installDir' to your PATH" -ForegroundColor Yellow -} - -# Verify installation -Write-Host "" -Write-Host "๐Ÿ” Verifying installation..." -ForegroundColor Cyan - -if (Test-Path $targetBinary) { - Write-Host "โœ“ Binary installed successfully" -ForegroundColor Green - - # Try to run symphony version check - try { - $version = & $targetBinary whoami --help 2>&1 - Write-Host "โœ“ Symphony is executable" -ForegroundColor Green - } catch { - Write-Host "โš  Warning: Could not execute symphony" -ForegroundColor Yellow - } -} else { - Write-Host "โŒ Installation failed: binary not found" -ForegroundColor Red - exit 1 -} - -Write-Host "" -Write-Host "โœ… Symphony installed successfully!" -ForegroundColor Green -Write-Host "" -Write-Host "๐Ÿ“– Next steps:" -ForegroundColor Cyan -Write-Host " 1. Configure: symphony config" -ForegroundColor White -Write-Host " 2. Login: symphony login" -ForegroundColor White -Write-Host " 3. Init repo: symphony init" -ForegroundColor White -Write-Host " 4. Dashboard: symphony dashboard" -ForegroundColor White -Write-Host "" -Write-Host "๐Ÿ“š Documentation: See README.md" -ForegroundColor Cyan -Write-Host "" diff --git a/scripts/installers/uninstall-unix.sh b/scripts/installers/uninstall-unix.sh deleted file mode 100644 index 17317c2..0000000 --- a/scripts/installers/uninstall-unix.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/bin/bash -# Symphony macOS/Linux Uninstaller - -set -e - -# Set UTF-8 encoding for proper emoji and Korean display -export LC_ALL="${LC_ALL:-en_US.UTF-8}" -export LANG="${LANG:-en_US.UTF-8}" -export LANGUAGE="${LANGUAGE:-en_US:en}" - -# Ensure terminal supports UTF-8 -if [ -z "$LC_ALL" ] && [ -z "$LANG" ]; then - export LC_ALL=C.UTF-8 - export LANG=C.UTF-8 -fi - -BLUE='\033[0;34m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' # No Color - -echo -e "${BLUE}๐ŸŽต Symphony Uninstaller${NC}" -echo "" - -# Check possible installation locations -POSSIBLE_LOCATIONS=( - "/usr/local/bin/symphony" - "$HOME/.local/bin/symphony" -) - -FOUND_LOCATIONS=() -for loc in "${POSSIBLE_LOCATIONS[@]}"; do - if [ -f "$loc" ]; then - FOUND_LOCATIONS+=("$loc") - fi -done - -if [ ${#FOUND_LOCATIONS[@]} -eq 0 ]; then - echo -e "${RED}โŒ Symphony installation not found${NC}" - echo "" - echo -e "${YELLOW}Checked locations:${NC}" - for loc in "${POSSIBLE_LOCATIONS[@]}"; do - echo -e "${YELLOW} - $loc${NC}" - done - exit 1 -fi - -echo -e "${BLUE}๐Ÿ“‚ Found Symphony installations:${NC}" -for loc in "${FOUND_LOCATIONS[@]}"; do - echo -e " ${NC}- $loc" -done -echo "" - -# Ask for confirmation -read -p "Uninstall Symphony? (y/N) " -n 1 -r -echo -if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo -e "${RED}โŒ Uninstallation cancelled${NC}" - exit 1 -fi - -# Remove installations -for binary_path in "${FOUND_LOCATIONS[@]}"; do - echo "" - echo -e "${BLUE}๐Ÿ—‘๏ธ Removing $binary_path...${NC}" - - if [ -w "$binary_path" ]; then - rm -f "$binary_path" - echo -e "${GREEN}โœ“ Removed${NC}" - else - # Try with sudo - if sudo rm -f "$binary_path" 2>/dev/null; then - echo -e "${GREEN}โœ“ Removed (with sudo)${NC}" - else - echo -e "${RED}โŒ Failed to remove${NC}" - echo -e "${YELLOW} Try: sudo rm $binary_path${NC}" - fi - fi -done - -# Clean PATH entries from shell configs -echo "" -echo -e "${BLUE}๐Ÿ”ง Cleaning shell configuration files...${NC}" - -SHELL_CONFIGS=( - "$HOME/.bashrc" - "$HOME/.zshrc" - "$HOME/.profile" - "$HOME/.config/fish/config.fish" -) - -for config in "${SHELL_CONFIGS[@]}"; do - if [ -f "$config" ]; then - # Check if file contains Symphony PATH entries - if grep -q "# Symphony" "$config" 2>/dev/null; then - # Create backup - cp "$config" "$config.backup" - - # Remove Symphony lines - sed -i.tmp '/# Symphony/,+1d' "$config" 2>/dev/null || \ - sed -i '' '/# Symphony/,+1d' "$config" 2>/dev/null - - echo -e "${GREEN}โœ“ Cleaned $(basename $config)${NC}" - fi - fi -done - -# Optional: Remove config files -echo "" -read -p "Remove configuration files in ~/.config/symphony? (y/N) " -n 1 -r -echo - -if [[ $REPLY =~ ^[Yy]$ ]]; then - CONFIG_DIR="$HOME/.config/symphony" - if [ -d "$CONFIG_DIR" ]; then - rm -rf "$CONFIG_DIR" - echo -e "${GREEN}โœ“ Configuration files removed${NC}" - else - echo -e "${YELLOW}โŠ˜ No configuration files found${NC}" - fi -fi - -echo "" -echo -e "${GREEN}โœ… Symphony uninstalled successfully!${NC}" -echo "" -echo -e "${YELLOW}โš  You may need to restart your terminal or run:${NC}" -echo -e "${YELLOW} source ~/.bashrc (or ~/.zshrc, etc.)${NC}" -echo "" diff --git a/scripts/installers/uninstall-windows.ps1 b/scripts/installers/uninstall-windows.ps1 deleted file mode 100644 index cecf0eb..0000000 --- a/scripts/installers/uninstall-windows.ps1 +++ /dev/null @@ -1,128 +0,0 @@ -๏ปฟ# Symphony Windows Uninstaller -# Run with: powershell -ExecutionPolicy Bypass -File uninstall-windows.ps1 - -$ErrorActionPreference = "Stop" - -# Set console encoding to UTF-8 for proper Korean display -$OutputEncoding = [System.Text.Encoding]::UTF8 -[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 -$PSDefaultParameterValues['*:Encoding'] = 'utf8' - -# Set console code page to UTF-8 (65001) -chcp 65001 > $null - -Write-Host "๐ŸŽต Symphony Windows Uninstaller" -ForegroundColor Cyan -Write-Host "" - -# Check possible installation locations -$possibleLocations = @( - "C:\Program Files\Symphony", - "$env:LOCALAPPDATA\Symphony" -) - -$foundLocations = @() -foreach ($loc in $possibleLocations) { - if (Test-Path "$loc\symphony.exe") { - $foundLocations += $loc - } -} - -if ($foundLocations.Count -eq 0) { - Write-Host "โŒ Symphony installation not found" -ForegroundColor Red - Write-Host "" - Write-Host "Checked locations:" -ForegroundColor Yellow - foreach ($loc in $possibleLocations) { - Write-Host " - $loc" -ForegroundColor Yellow - } - exit 1 -} - -Write-Host "๐Ÿ“‚ Found Symphony installations:" -ForegroundColor Cyan -foreach ($loc in $foundLocations) { - Write-Host " - $loc" -ForegroundColor White -} -Write-Host "" - -# Ask for confirmation -$confirmation = Read-Host "Uninstall Symphony? (y/N)" -if ($confirmation -ne 'y' -and $confirmation -ne 'Y') { - Write-Host "โŒ Uninstallation cancelled" -ForegroundColor Red - exit 1 -} - -# Check if running as Administrator -$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) -$isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - -# Remove installations -foreach ($installDir in $foundLocations) { - Write-Host "" - Write-Host "๐Ÿ—‘๏ธ Removing $installDir..." -ForegroundColor Cyan - - try { - Remove-Item -Path $installDir -Recurse -Force - Write-Host "โœ“ Removed" -ForegroundColor Green - } catch { - if ($installDir -like "C:\Program Files*" -and -not $isAdmin) { - Write-Host "โŒ Failed: Administrator privileges required" -ForegroundColor Red - Write-Host " Please run this script as Administrator" -ForegroundColor Yellow - } else { - Write-Host "โŒ Failed: $_" -ForegroundColor Red - } - } -} - -# Remove from PATH -Write-Host "" -Write-Host "๐Ÿ”ง Cleaning PATH environment variable..." -ForegroundColor Cyan - -foreach ($installDir in $foundLocations) { - # Try system PATH (requires admin) - if ($isAdmin) { - try { - $envTarget = [System.EnvironmentVariableTarget]::Machine - $currentPath = [Environment]::GetEnvironmentVariable("Path", $envTarget) - - if ($currentPath -like "*$installDir*") { - $newPath = ($currentPath.Split(';') | Where-Object { $_ -ne $installDir }) -join ';' - [Environment]::SetEnvironmentVariable("Path", $newPath, $envTarget) - Write-Host "โœ“ Removed from system PATH" -ForegroundColor Green - } - } catch { - Write-Host "โš  Could not update system PATH: $_" -ForegroundColor Yellow - } - } - - # Try user PATH - try { - $envTarget = [System.EnvironmentVariableTarget]::User - $currentPath = [Environment]::GetEnvironmentVariable("Path", $envTarget) - - if ($currentPath -like "*$installDir*") { - $newPath = ($currentPath.Split(';') | Where-Object { $_ -ne $installDir }) -join ';' - [Environment]::SetEnvironmentVariable("Path", $newPath, $envTarget) - Write-Host "โœ“ Removed from user PATH" -ForegroundColor Green - } - } catch { - Write-Host "โš  Could not update user PATH: $_" -ForegroundColor Yellow - } -} - -# Optional: Remove config files -Write-Host "" -$removeConfig = Read-Host "Remove configuration files in $env:USERPROFILE\.config\symphony? (y/N)" -if ($removeConfig -eq 'y' -or $removeConfig -eq 'Y') { - $configDir = "$env:USERPROFILE\.config\symphony" - if (Test-Path $configDir) { - Remove-Item -Path $configDir -Recurse -Force - Write-Host "โœ“ Configuration files removed" -ForegroundColor Green - } else { - Write-Host "โŠ˜ No configuration files found" -ForegroundColor Yellow - } -} - -Write-Host "" -Write-Host "โœ… Symphony uninstalled successfully!" -ForegroundColor Green -Write-Host "" -Write-Host "โš  You may need to restart your terminal" -ForegroundColor Yellow -Write-Host "" diff --git a/scripts/validate-checkstyle.sh b/scripts/validate-checkstyle.sh deleted file mode 100755 index 1f514b3..0000000 --- a/scripts/validate-checkstyle.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# Validates generated Checkstyle configuration - -set -e - -CHECKSTYLE_XML=".sym/checkstyle.xml" - -if [ ! -f "$CHECKSTYLE_XML" ]; then - echo "Error: $CHECKSTYLE_XML not found" - exit 1 -fi - -echo "Validating Checkstyle configuration..." - -# Check if file is valid XML -if ! xmllint --noout "$CHECKSTYLE_XML" 2>/dev/null; then - echo "Error: Invalid XML in $CHECKSTYLE_XML" - exit 1 -fi - -# Check required structure -if ! xmllint --xpath "//module[@name='Checker']" "$CHECKSTYLE_XML" > /dev/null 2>&1; then - echo "Error: Missing Checker module in $CHECKSTYLE_XML" - exit 1 -fi - -# Count modules -MODULE_COUNT=$(xmllint --xpath "count(//module[@name='TreeWalker']/module)" "$CHECKSTYLE_XML") -echo "โœ“ Valid Checkstyle config with $MODULE_COUNT modules" diff --git a/scripts/validate-eslint.sh b/scripts/validate-eslint.sh deleted file mode 100755 index 2e17f56..0000000 --- a/scripts/validate-eslint.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# Validates generated ESLint configuration - -set -e - -ESLINTRC=".sym/.eslintrc.json" - -if [ ! -f "$ESLINTRC" ]; then - echo "Error: $ESLINTRC not found" - exit 1 -fi - -echo "Validating ESLint configuration..." - -# Check if file is valid JSON -if ! jq empty "$ESLINTRC" 2>/dev/null; then - echo "Error: Invalid JSON in $ESLINTRC" - exit 1 -fi - -# Check required fields -if ! jq -e '.rules' "$ESLINTRC" > /dev/null; then - echo "Error: Missing 'rules' field in $ESLINTRC" - exit 1 -fi - -# Count rules -RULE_COUNT=$(jq '.rules | length' "$ESLINTRC") -echo "โœ“ Valid ESLint config with $RULE_COUNT rules" - -# Optional: Run eslint --print-config if eslint is installed -if command -v eslint &> /dev/null; then - echo "โœ“ ESLint validation passed" -else - echo "โ„น eslint not installed, skipping full validation" -fi From 3e4ed8f624f98fdfe8ad1aa81eecfbf2db2428ef Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 08:06:29 +0000 Subject: [PATCH 04/26] refactor: move testdata to tests/testdata - Consolidate test data under tests/ directory - Update documentation references --- AGENTS.md | 10 +++---- README.md | 27 ++++++------------- docs/LINTER_VALIDATION.md | 6 ++--- tests/TESTING_GUIDE.md | 6 ++--- {testdata => tests/testdata}/README.md | 0 .../testdata}/java/ast/AstViolations.java | 0 .../testdata}/java/ast/ValidAst.java | 0 .../testdata}/java/ast/code-policy.json | 0 .../java/length/LengthViolations.java | 0 .../testdata}/java/length/ValidLength.java | 0 .../testdata}/java/length/code-policy.json | 0 .../java/pattern/NamingViolations.java | 0 .../testdata}/java/pattern/ValidNaming.java | 0 .../testdata}/java/pattern/code-policy.json | 0 .../testdata}/java/style/StyleViolations.java | 0 .../testdata}/java/style/ValidStyle.java | 0 .../testdata}/java/style/code-policy.json | 0 .../testdata}/javascript/ast/code-policy.json | 0 .../javascript/ast/naming-violations.js | 0 .../testdata}/javascript/ast/valid.js | 0 .../javascript/length/code-policy.json | 0 .../javascript/length/length-violations.js | 0 .../testdata}/javascript/length/valid.js | 0 .../javascript/pattern/code-policy.json | 0 .../javascript/pattern/naming-violations.js | 0 .../javascript/pattern/security-violations.js | 0 .../testdata}/javascript/pattern/valid.js | 0 .../javascript/style/code-policy.json | 0 .../javascript/style/style-violations.js | 0 .../testdata}/javascript/style/valid.js | 0 .../typescript/typechecker/code-policy.json | 0 .../typechecker/strict-mode-errors.ts | 0 .../typescript/typechecker/type-errors.ts | 0 .../testdata}/typescript/typechecker/valid.ts | 0 34 files changed, 19 insertions(+), 30 deletions(-) rename {testdata => tests/testdata}/README.md (100%) rename {testdata => tests/testdata}/java/ast/AstViolations.java (100%) rename {testdata => tests/testdata}/java/ast/ValidAst.java (100%) rename {testdata => tests/testdata}/java/ast/code-policy.json (100%) rename {testdata => tests/testdata}/java/length/LengthViolations.java (100%) rename {testdata => tests/testdata}/java/length/ValidLength.java (100%) rename {testdata => tests/testdata}/java/length/code-policy.json (100%) rename {testdata => tests/testdata}/java/pattern/NamingViolations.java (100%) rename {testdata => tests/testdata}/java/pattern/ValidNaming.java (100%) rename {testdata => tests/testdata}/java/pattern/code-policy.json (100%) rename {testdata => tests/testdata}/java/style/StyleViolations.java (100%) rename {testdata => tests/testdata}/java/style/ValidStyle.java (100%) rename {testdata => tests/testdata}/java/style/code-policy.json (100%) rename {testdata => tests/testdata}/javascript/ast/code-policy.json (100%) rename {testdata => tests/testdata}/javascript/ast/naming-violations.js (100%) rename {testdata => tests/testdata}/javascript/ast/valid.js (100%) rename {testdata => tests/testdata}/javascript/length/code-policy.json (100%) rename {testdata => tests/testdata}/javascript/length/length-violations.js (100%) rename {testdata => tests/testdata}/javascript/length/valid.js (100%) rename {testdata => tests/testdata}/javascript/pattern/code-policy.json (100%) rename {testdata => tests/testdata}/javascript/pattern/naming-violations.js (100%) rename {testdata => tests/testdata}/javascript/pattern/security-violations.js (100%) rename {testdata => tests/testdata}/javascript/pattern/valid.js (100%) rename {testdata => tests/testdata}/javascript/style/code-policy.json (100%) rename {testdata => tests/testdata}/javascript/style/style-violations.js (100%) rename {testdata => tests/testdata}/javascript/style/valid.js (100%) rename {testdata => tests/testdata}/typescript/typechecker/code-policy.json (100%) rename {testdata => tests/testdata}/typescript/typechecker/strict-mode-errors.ts (100%) rename {testdata => tests/testdata}/typescript/typechecker/type-errors.ts (100%) rename {testdata => tests/testdata}/typescript/typechecker/valid.ts (100%) diff --git a/AGENTS.md b/AGENTS.md index 5dfb7bb..fe2face 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,11 +24,11 @@ - ํŒŒ์ผ์€ ๋ฉ€ํ‹ฐ ๋ชจ๋“ˆ ํ˜•ํƒœ๋กœ ๊ฐ ๊ธฐ๋Šฅ๋ณ„๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์–ด์•ผ ํ•ด ## ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๊ฐœ๋ฐœ ๋ช…๋ น์–ด -- ์žฌํ˜„์„ฑ์„ ์œ„ํ•ด Make ํƒ€๊นƒ ๋˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์„ ํ˜ธํ•˜์„ธ์š”: - - `make dev` ๋˜๋Š” `./scripts/dev` โ€” ์ž๋™ ๋ฆฌ๋กœ๋“œ๋กœ ๋กœ์ปฌ์—์„œ ์•ฑ ์‹คํ–‰ - - `make test` ๋˜๋Š” `./scripts/test` โ€” ์ „์ฒด ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ์‹คํ–‰ - - `make lint` ๋˜๋Š” `./scripts/lint` โ€” ๋ฆฐํ„ฐ/ํฌ๋งคํ„ฐ ์‹คํ–‰ - - `make build` ๋˜๋Š” `./scripts/build` โ€” ๋ฆด๋ฆฌ์Šค ์•„ํ‹ฐํŒฉํŠธ ์ƒ์„ฑ +- ์žฌํ˜„์„ฑ์„ ์œ„ํ•ด Make ํƒ€๊นƒ์„ ์„ ํ˜ธํ•˜์„ธ์š”: + - `make build` โ€” ํ˜„์žฌ ํ”Œ๋žซํผ์šฉ ๋นŒ๋“œ + - `make build-all` โ€” ๋ชจ๋“  ํ”Œ๋žซํผ์šฉ ๋นŒ๋“œ + - `make test` โ€” ์ „์ฒด ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ์‹คํ–‰ + - `make lint` โ€” ๋ฆฐํ„ฐ/ํฌ๋งคํ„ฐ ์‹คํ–‰ - ์„œ๋น„์Šค ์‚ฌ์ „ ์š”๊ตฌ์‚ฌํ•ญ(DB, ํ, ํ™˜๊ฒฝ ๋ณ€์ˆ˜)์€ `docs/`์— ๋ฌธ์„œํ™”ํ•˜์„ธ์š”. - Go ์ „์šฉ ๊ถŒ์žฅ ๋ช…๋ น(๊ฐ€๋Šฅํ•˜๋ฉด Make ํƒ€๊นƒ์œผ๋กœ ๋ž˜ํ•‘): diff --git a/README.md b/README.md index c4b1f13..dd562a4 100644 --- a/README.md +++ b/README.md @@ -289,22 +289,11 @@ sym-cli/ โ”‚ โ””โ”€โ”€ adapter/ # ESLint, Prettier ์–ด๋Œ‘ํ„ฐ โ”œโ”€โ”€ pkg/ โ”‚ โ””โ”€โ”€ schema/ # ์Šคํ‚ค๋งˆ ํƒ€์ž… ์ •์˜ -โ”œโ”€โ”€ scripts/ # ๋นŒ๋“œ ์Šคํฌ๋ฆฝํŠธ -โ”œโ”€โ”€ examples/ # ์˜ˆ์ œ ํŒŒ์ผ โ”œโ”€โ”€ tests/ # ํ…Œ์ŠคํŠธ -โ”œโ”€โ”€ testdata/ # ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ -โ”‚ โ”œโ”€โ”€ javascript/ # JavaScript ํ…Œ์ŠคํŠธ ํŒŒ์ผ -โ”‚ โ”‚ โ”œโ”€โ”€ pattern/ # ํŒจํ„ด ๋งค์นญ ํ…Œ์ŠคํŠธ -โ”‚ โ”‚ โ”œโ”€โ”€ length/ # ๊ธธ์ด ์ œํ•œ ํ…Œ์ŠคํŠธ -โ”‚ โ”‚ โ”œโ”€โ”€ style/ # ์ฝ”๋“œ ์Šคํƒ€์ผ ํ…Œ์ŠคํŠธ -โ”‚ โ”‚ โ””โ”€โ”€ ast/ # AST ๊ตฌ์กฐ ํ…Œ์ŠคํŠธ -โ”‚ โ”œโ”€โ”€ typescript/ # TypeScript ํ…Œ์ŠคํŠธ ํŒŒ์ผ -โ”‚ โ”‚ โ””โ”€โ”€ typechecker/ # ํƒ€์ž… ์ฒดํ‚น ํ…Œ์ŠคํŠธ -โ”‚ โ””โ”€โ”€ java/ # Java ํ…Œ์ŠคํŠธ ํŒŒ์ผ -โ”‚ โ”œโ”€โ”€ pattern/ # ํŒจํ„ด ๋งค์นญ ํ…Œ์ŠคํŠธ -โ”‚ โ”œโ”€โ”€ length/ # ๊ธธ์ด ์ œํ•œ ํ…Œ์ŠคํŠธ -โ”‚ โ”œโ”€โ”€ style/ # ์ฝ”๋“œ ์Šคํƒ€์ผ ํ…Œ์ŠคํŠธ -โ”‚ โ””โ”€โ”€ ast/ # AST ๊ตฌ์กฐ ํ…Œ์ŠคํŠธ +โ”‚ โ””โ”€โ”€ testdata/ # ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ +โ”‚ โ”œโ”€โ”€ javascript/ # JavaScript ํ…Œ์ŠคํŠธ ํŒŒ์ผ +โ”‚ โ”œโ”€โ”€ typescript/ # TypeScript ํ…Œ์ŠคํŠธ ํŒŒ์ผ +โ”‚ โ””โ”€โ”€ java/ # Java ํ…Œ์ŠคํŠธ ํŒŒ์ผ โ”œโ”€โ”€ .sym/ # ์ •์ฑ… ๋ฐ ์—ญํ•  ํŒŒ์ผ (gitignore) โ”œโ”€โ”€ Makefile โ””โ”€โ”€ README.md @@ -349,12 +338,12 @@ go test ./tests/integration/... -v ``` **ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ**: -- `testdata/javascript/`: JavaScript ์—”์ง„ ํ…Œ์ŠคํŠธ (pattern, length, style, ast) -- `testdata/typescript/`: TypeScript ํƒ€์ž…์ฒด์ปค ํ…Œ์ŠคํŠธ -- `testdata/java/`: Java ์—”์ง„ ํ…Œ์ŠคํŠธ (Checkstyle, PMD ๊ฒ€์ฆ) +- `tests/testdata/javascript/`: JavaScript ์—”์ง„ ํ…Œ์ŠคํŠธ (pattern, length, style, ast) +- `tests/testdata/typescript/`: TypeScript ํƒ€์ž…์ฒด์ปค ํ…Œ์ŠคํŠธ +- `tests/testdata/java/`: Java ์—”์ง„ ํ…Œ์ŠคํŠธ (Checkstyle, PMD ๊ฒ€์ฆ) ๊ฐ ๋””๋ ‰ํ† ๋ฆฌ๋Š” ์œ„๋ฐ˜ ์ผ€์ด์Šค์™€ ์ •์ƒ ์ผ€์ด์Šค๋ฅผ ํฌํ•จํ•˜์—ฌ ๊ฒ€์ฆ ์—”์ง„์˜ ์ •ํ™•์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. -์ž์„ธํ•œ ๋‚ด์šฉ์€ [testdata/README.md](testdata/README.md)๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. +์ž์„ธํ•œ ๋‚ด์šฉ์€ [tests/testdata/README.md](tests/testdata/README.md)๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ๋ฆฌํฌํŠธ๋Š” [์—ฌ๊ธฐ](https://devsymphony.github.io/sym-cli/coverage.html)์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. diff --git a/docs/LINTER_VALIDATION.md b/docs/LINTER_VALIDATION.md index 7f034ca..0dae82b 100644 --- a/docs/LINTER_VALIDATION.md +++ b/docs/LINTER_VALIDATION.md @@ -74,10 +74,10 @@ Rules with `engine: "llm-validator"` cannot be checked by traditional linters an ### Integration Test Data -Validation engines are tested using structured test data in `testdata/`: +Validation engines are tested using structured test data in `tests/testdata/`: ``` -testdata/ +tests/testdata/ โ”œโ”€โ”€ javascript/ # ESLint-based validation tests โ”‚ โ”œโ”€โ”€ pattern/ # Naming conventions, regex patterns โ”‚ โ”œโ”€โ”€ length/ # Line/function length limits @@ -110,4 +110,4 @@ go test ./tests/integration/... -v -run TestASTEngine go test ./tests/integration/... -v -run TestTypeChecker ``` -For detailed test data structure, see [testdata/README.md](../testdata/README.md). +For detailed test data structure, see [tests/testdata/README.md](../tests/testdata/README.md). diff --git a/tests/TESTING_GUIDE.md b/tests/TESTING_GUIDE.md index 9f0dce1..b2612cb 100644 --- a/tests/TESTING_GUIDE.md +++ b/tests/TESTING_GUIDE.md @@ -143,10 +143,10 @@ go test -v ./tests/e2e/... -timeout 5m ### testdata ๋””๋ ‰ํ† ๋ฆฌ -๊ฒ€์ฆ ์—”์ง„์˜ ์ •ํ™•์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋Š” `testdata/` ๋””๋ ‰ํ† ๋ฆฌ์— ์—”์ง„๋ณ„, ์–ธ์–ด๋ณ„๋กœ ๊ตฌ์กฐํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค: +๊ฒ€์ฆ ์—”์ง„์˜ ์ •ํ™•์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋Š” `tests/testdata/` ๋””๋ ‰ํ† ๋ฆฌ์— ์—”์ง„๋ณ„, ์–ธ์–ด๋ณ„๋กœ ๊ตฌ์กฐํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค: ``` -testdata/ +tests/testdata/ โ”œโ”€โ”€ javascript/ โ”‚ โ”œโ”€โ”€ pattern/ # ํŒจํ„ด ๋งค์นญ ๋ฐ ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜ ํ…Œ์ŠคํŠธ โ”‚ โ”‚ โ”œโ”€โ”€ naming-violations.js @@ -192,7 +192,7 @@ testdata/ - AST Engine: ๊ตฌ์กฐ ๊ฒ€์ฆ (ESLint/PMD) - TypeChecker Engine: ํƒ€์ž… ๊ฒ€์ฆ (TSC) -์ž์„ธํ•œ ๋‚ด์šฉ์€ [testdata/README.md](../testdata/README.md)๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. +์ž์„ธํ•œ ๋‚ด์šฉ์€ [testdata/README.md](testdata/README.md)๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. ## E2E ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ diff --git a/testdata/README.md b/tests/testdata/README.md similarity index 100% rename from testdata/README.md rename to tests/testdata/README.md diff --git a/testdata/java/ast/AstViolations.java b/tests/testdata/java/ast/AstViolations.java similarity index 100% rename from testdata/java/ast/AstViolations.java rename to tests/testdata/java/ast/AstViolations.java diff --git a/testdata/java/ast/ValidAst.java b/tests/testdata/java/ast/ValidAst.java similarity index 100% rename from testdata/java/ast/ValidAst.java rename to tests/testdata/java/ast/ValidAst.java diff --git a/testdata/java/ast/code-policy.json b/tests/testdata/java/ast/code-policy.json similarity index 100% rename from testdata/java/ast/code-policy.json rename to tests/testdata/java/ast/code-policy.json diff --git a/testdata/java/length/LengthViolations.java b/tests/testdata/java/length/LengthViolations.java similarity index 100% rename from testdata/java/length/LengthViolations.java rename to tests/testdata/java/length/LengthViolations.java diff --git a/testdata/java/length/ValidLength.java b/tests/testdata/java/length/ValidLength.java similarity index 100% rename from testdata/java/length/ValidLength.java rename to tests/testdata/java/length/ValidLength.java diff --git a/testdata/java/length/code-policy.json b/tests/testdata/java/length/code-policy.json similarity index 100% rename from testdata/java/length/code-policy.json rename to tests/testdata/java/length/code-policy.json diff --git a/testdata/java/pattern/NamingViolations.java b/tests/testdata/java/pattern/NamingViolations.java similarity index 100% rename from testdata/java/pattern/NamingViolations.java rename to tests/testdata/java/pattern/NamingViolations.java diff --git a/testdata/java/pattern/ValidNaming.java b/tests/testdata/java/pattern/ValidNaming.java similarity index 100% rename from testdata/java/pattern/ValidNaming.java rename to tests/testdata/java/pattern/ValidNaming.java diff --git a/testdata/java/pattern/code-policy.json b/tests/testdata/java/pattern/code-policy.json similarity index 100% rename from testdata/java/pattern/code-policy.json rename to tests/testdata/java/pattern/code-policy.json diff --git a/testdata/java/style/StyleViolations.java b/tests/testdata/java/style/StyleViolations.java similarity index 100% rename from testdata/java/style/StyleViolations.java rename to tests/testdata/java/style/StyleViolations.java diff --git a/testdata/java/style/ValidStyle.java b/tests/testdata/java/style/ValidStyle.java similarity index 100% rename from testdata/java/style/ValidStyle.java rename to tests/testdata/java/style/ValidStyle.java diff --git a/testdata/java/style/code-policy.json b/tests/testdata/java/style/code-policy.json similarity index 100% rename from testdata/java/style/code-policy.json rename to tests/testdata/java/style/code-policy.json diff --git a/testdata/javascript/ast/code-policy.json b/tests/testdata/javascript/ast/code-policy.json similarity index 100% rename from testdata/javascript/ast/code-policy.json rename to tests/testdata/javascript/ast/code-policy.json diff --git a/testdata/javascript/ast/naming-violations.js b/tests/testdata/javascript/ast/naming-violations.js similarity index 100% rename from testdata/javascript/ast/naming-violations.js rename to tests/testdata/javascript/ast/naming-violations.js diff --git a/testdata/javascript/ast/valid.js b/tests/testdata/javascript/ast/valid.js similarity index 100% rename from testdata/javascript/ast/valid.js rename to tests/testdata/javascript/ast/valid.js diff --git a/testdata/javascript/length/code-policy.json b/tests/testdata/javascript/length/code-policy.json similarity index 100% rename from testdata/javascript/length/code-policy.json rename to tests/testdata/javascript/length/code-policy.json diff --git a/testdata/javascript/length/length-violations.js b/tests/testdata/javascript/length/length-violations.js similarity index 100% rename from testdata/javascript/length/length-violations.js rename to tests/testdata/javascript/length/length-violations.js diff --git a/testdata/javascript/length/valid.js b/tests/testdata/javascript/length/valid.js similarity index 100% rename from testdata/javascript/length/valid.js rename to tests/testdata/javascript/length/valid.js diff --git a/testdata/javascript/pattern/code-policy.json b/tests/testdata/javascript/pattern/code-policy.json similarity index 100% rename from testdata/javascript/pattern/code-policy.json rename to tests/testdata/javascript/pattern/code-policy.json diff --git a/testdata/javascript/pattern/naming-violations.js b/tests/testdata/javascript/pattern/naming-violations.js similarity index 100% rename from testdata/javascript/pattern/naming-violations.js rename to tests/testdata/javascript/pattern/naming-violations.js diff --git a/testdata/javascript/pattern/security-violations.js b/tests/testdata/javascript/pattern/security-violations.js similarity index 100% rename from testdata/javascript/pattern/security-violations.js rename to tests/testdata/javascript/pattern/security-violations.js diff --git a/testdata/javascript/pattern/valid.js b/tests/testdata/javascript/pattern/valid.js similarity index 100% rename from testdata/javascript/pattern/valid.js rename to tests/testdata/javascript/pattern/valid.js diff --git a/testdata/javascript/style/code-policy.json b/tests/testdata/javascript/style/code-policy.json similarity index 100% rename from testdata/javascript/style/code-policy.json rename to tests/testdata/javascript/style/code-policy.json diff --git a/testdata/javascript/style/style-violations.js b/tests/testdata/javascript/style/style-violations.js similarity index 100% rename from testdata/javascript/style/style-violations.js rename to tests/testdata/javascript/style/style-violations.js diff --git a/testdata/javascript/style/valid.js b/tests/testdata/javascript/style/valid.js similarity index 100% rename from testdata/javascript/style/valid.js rename to tests/testdata/javascript/style/valid.js diff --git a/testdata/typescript/typechecker/code-policy.json b/tests/testdata/typescript/typechecker/code-policy.json similarity index 100% rename from testdata/typescript/typechecker/code-policy.json rename to tests/testdata/typescript/typechecker/code-policy.json diff --git a/testdata/typescript/typechecker/strict-mode-errors.ts b/tests/testdata/typescript/typechecker/strict-mode-errors.ts similarity index 100% rename from testdata/typescript/typechecker/strict-mode-errors.ts rename to tests/testdata/typescript/typechecker/strict-mode-errors.ts diff --git a/testdata/typescript/typechecker/type-errors.ts b/tests/testdata/typescript/typechecker/type-errors.ts similarity index 100% rename from testdata/typescript/typechecker/type-errors.ts rename to tests/testdata/typescript/typechecker/type-errors.ts diff --git a/testdata/typescript/typechecker/valid.ts b/tests/testdata/typescript/typechecker/valid.ts similarity index 100% rename from testdata/typescript/typechecker/valid.ts rename to tests/testdata/typescript/typechecker/valid.ts From 92400d953b91f89df188ba32542238763aa69aff Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 08:08:35 +0000 Subject: [PATCH 05/26] fix: skip prettier integration test when not installed --- internal/adapter/prettier/executor_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/adapter/prettier/executor_test.go b/internal/adapter/prettier/executor_test.go index f101bd0..99bfa01 100644 --- a/internal/adapter/prettier/executor_test.go +++ b/internal/adapter/prettier/executor_test.go @@ -116,7 +116,8 @@ func TestExecute_Integration(t *testing.T) { return } + // output can be nil if Prettier is not installed if output == nil { - t.Error("Expected non-nil output") + t.Skip("Skipping: Prettier not available in this environment") } } From e1d6e8ed415639415008b49569a04cddf6736c90 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 08:13:28 +0000 Subject: [PATCH 06/26] refactor: remove test_validator.go and cleanup .gitignore --- .gitignore | 35 ++++++++++------ test_validator.go | 103 ---------------------------------------------- 2 files changed, 23 insertions(+), 115 deletions(-) delete mode 100644 test_validator.go diff --git a/.gitignore b/.gitignore index 37bc077..08bba7b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,31 @@ -.claude/ +# Build outputs bin/ -!npm/bin/ -!npm/bin/*.js -npm/bin/sym-* +dist/ +*.exe + +# Dependencies +node_modules/ # Test coverage coverage.out coverage.html -node_modules/ -.sym/ -*.coverprofile coverage.txt +*.coverprofile + +# IDE +.vscode/ +.cursor/ +.idea/ + +# OS +.DS_Store -# Symphony API key configuration -.sym/.env -.vscode -.cursor -sym-cli.exe +# Project specific +.claude/ +.sym/ .mcp.json + +# npm package binaries (keep JS wrapper) +!npm/bin/ +!npm/bin/*.js +npm/bin/sym-* diff --git a/test_validator.go b/test_validator.go deleted file mode 100644 index 8f2ea97..0000000 --- a/test_validator.go +++ /dev/null @@ -1,103 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/DevSymphony/sym-cli/internal/validator" - "github.com/DevSymphony/sym-cli/pkg/schema" -) - -func main() { - // Create a simple test policy inline with multiple engine types - policy := &schema.CodePolicy{ - Version: "1.0.0", - Rules: []schema.PolicyRule{ - { - ID: "test-max-len", - Enabled: true, - Category: "style", - Severity: "warning", - Desc: "Lines should not exceed 120 characters", - When: &schema.Selector{ - Languages: []string{"javascript"}, - }, - Check: map[string]any{ - "engine": "length", - "scope": "line", - "max": 120, - }, - Message: "Line too long (max 120 characters)", - }, - { - ID: "test-pattern", - Enabled: true, - Category: "security", - Severity: "error", - Desc: "No hardcoded API keys", - When: &schema.Selector{ - Languages: []string{"javascript"}, - }, - Check: map[string]any{ - "engine": "pattern", - "pattern": "sk-[a-zA-Z0-9]{30,}", - "target": "content", - }, - Message: "Hardcoded API key detected", - }, - }, - Enforce: schema.EnforceSettings{ - Stages: []string{"pre-commit"}, - FailOn: []string{"error"}, - }, - } - - fmt.Printf("๐Ÿ“‹ Testing validator with %d rule(s)\n\n", len(policy.Rules)) - - // Create validator - v := validator.NewValidator(policy, true) - defer func() { - if err := v.Close(); err != nil { - fmt.Fprintf(os.Stderr, "Warning: failed to close validator: %v\n", err) - } - }() - - // Test files - testFiles := []string{ - "tests/e2e/examples/bad-example.js", - "tests/e2e/examples/good-example.js", - } - - for _, file := range testFiles { - // Check if file exists - if _, err := os.Stat(file); os.IsNotExist(err) { - fmt.Printf("โš ๏ธ Skipping %s (file not found)\n\n", file) - continue - } - - fmt.Printf("โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n") - fmt.Printf("Testing: %s\n", file) - fmt.Printf("โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n\n") - - result, err := v.Validate(file) - if err != nil { - fmt.Printf("โŒ Validation error: %v\n\n", err) - continue - } - - if result.Passed { - fmt.Printf("\nโœ… PASSED: No violations\n\n") - } else { - fmt.Printf("\nโŒ FAILED: %d violation(s) found:\n", len(result.Violations)) - for i, violation := range result.Violations { - fmt.Printf("\n%d. [%s] %s\n", i+1, violation.Severity, violation.RuleID) - fmt.Printf(" File: %s:%d:%d\n", violation.File, violation.Line, violation.Column) - fmt.Printf(" Message: %s\n", violation.Message) - } - fmt.Println() - } - } - - fmt.Printf("โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n") - fmt.Printf("โœ… Validator test complete!\n") -} From 9c14134e1de1bea3515914af9cddf8ded127cfb4 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 08:29:40 +0000 Subject: [PATCH 07/26] docs: merge SETUP.md into README.md - Add prerequisites section with required tools - Add external tools auto-installation info - Add troubleshooting section --- README.md | 46 +++++++++++++++++ SETUP.md | 150 ------------------------------------------------------ 2 files changed, 46 insertions(+), 150 deletions(-) delete mode 100644 SETUP.md diff --git a/README.md b/README.md index dd562a4..0f88cd6 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,21 @@ sym-cli/ ## ๐Ÿ”ง ๊ฐœ๋ฐœ +### ํ•„์ˆ˜ ๋„๊ตฌ + +| ๋„๊ตฌ | ๋ฒ„์ „ | ์šฉ๋„ | +|------|------|------| +| Go | 1.21+ | CLI ๋นŒ๋“œ ๋ฐ ์‹คํ–‰ | +| Node.js & npm | 18+ | ESLint, Prettier, TSC ์–ด๋Œ‘ํ„ฐ | +| Java JDK | 21+ (์„ ํƒ) | Checkstyle, PMD ์–ด๋Œ‘ํ„ฐ | + +```bash +# ์„ค์น˜ ํ™•์ธ +go version # go1.21 ์ด์ƒ +node --version # v18 ์ด์ƒ +java -version # openjdk 21 ์ด์ƒ (Java ๊ฒ€์ฆ ์‹œ ํ•„์š”) +``` + ### ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ • ```bash @@ -311,6 +326,20 @@ make setup make watch-css ``` +### ์™ธ๋ถ€ ๋„๊ตฌ ์ž๋™ ์„ค์น˜ + +CLI๋Š” ๊ฒ€์ฆ ๋„๊ตฌ๋ฅผ ์ฒ˜์Œ ์‚ฌ์šฉํ•  ๋•Œ ์ž๋™์œผ๋กœ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค: + +``` +~/.symphony/tools/ +โ”œโ”€โ”€ node_modules/ # npm์œผ๋กœ ์„ค์น˜ +โ”‚ โ”œโ”€โ”€ eslint/ +โ”‚ โ”œโ”€โ”€ prettier/ +โ”‚ โ””โ”€โ”€ typescript/ +โ”œโ”€โ”€ checkstyle-10.26.1.jar # Maven Central์—์„œ ๋‹ค์šด๋กœ๋“œ +โ””โ”€โ”€ pmd-/ # GitHub Releases์—์„œ ๋‹ค์šด๋กœ๋“œ +``` + ### ๋นŒ๋“œ ```bash @@ -363,6 +392,23 @@ make tidy make clean ``` +### ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… + +**Java ํ…Œ์ŠคํŠธ ์‹คํŒจ ("java not found")** +```bash +# Ubuntu/Debian +sudo apt-get install -y default-jdk + +# macOS +brew install openjdk@21 +``` + +**ESLint/Prettier ์„ค์น˜ ์‹คํŒจ** +```bash +npm --version # npm ์„ค์น˜ ํ™•์ธ +cd ~/.symphony/tools && npm install eslint@^8.0.0 prettier@latest +``` + ## ๐Ÿ“‹ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ```bash diff --git a/SETUP.md b/SETUP.md deleted file mode 100644 index 2bcbd83..0000000 --- a/SETUP.md +++ /dev/null @@ -1,150 +0,0 @@ -# Setup Guide - -This document describes the development environment setup and external tool requirements for the Symphony CLI project. - -## Prerequisites - -### Required Tools - -#### 1. Go (Required) -- **Version**: Go 1.21 or higher -- **Purpose**: Build and run the CLI -- **Installation**: https://go.dev/doc/install - -#### 2. Node.js & npm (Required for JavaScript/TypeScript validation) -- **Version**: Node.js 18+ with npm -- **Purpose**: Run ESLint, Prettier, TSC adapters -- **Installation**: https://nodejs.org/ -- **Used by engines**: Pattern, Length, Style, AST, TypeChecker - -#### 3. Java JDK (Required for Java validation) -- **Version**: Java 21 or higher (OpenJDK recommended) -- **Purpose**: Run Checkstyle and PMD adapters -- **Installation**: - ```bash - # Ubuntu/Debian - sudo apt-get update - sudo apt-get install -y default-jdk - - # macOS (Homebrew) - brew install openjdk@21 - - # Verify installation - java -version - ``` -- **Used by engines**: Pattern (Java), Length (Java), Style (Java), AST (Java) -- **Note**: Java was installed on 2025-11-16 for integration testing - -### Optional Tools - -#### Git -- Required for running integration tests that involve git operations -- Most systems have git pre-installed - ---- - -## External Tool Installation - -The CLI automatically installs external validation tools when first needed: - -### JavaScript/TypeScript Tools -- **ESLint**: Installed to `~/.symphony/tools/node_modules/eslint` via npm -- **Prettier**: Installed to `~/.symphony/tools/node_modules/prettier` via npm -- **TypeScript**: Installed to `~/.symphony/tools/node_modules/typescript` via npm - -### Java Tools -- **Checkstyle**: Downloaded to `~/.symphony/tools/checkstyle-10.26.1.jar` from Maven Central -- **PMD**: Downloaded to `~/.symphony/tools/pmd-/` from GitHub Releases - -**Auto-installation happens when:** -1. A validation rule is executed for the first time -2. The engine checks if the adapter is available (`CheckAvailability()`) -3. If not found, the engine calls `Install()` to download and set up the tool - ---- - -## Running Tests - -### Unit Tests -```bash -# Run all unit tests -go test ./... - -# Run with coverage -go test -cover ./... -``` - -### Integration Tests -```bash -# Run all integration tests -go test ./tests/integration/... - -# Run specific test suite -go test ./tests/integration/validator_policy_test.go ./tests/integration/helper.go -v - -# Skip integration tests in short mode -go test -short ./... -``` - -**Note**: Integration tests require: -- Node.js and npm (for JavaScript/TypeScript tests) -- Java JDK (for Java tests) - ---- - -## Troubleshooting - -### Java Tests Fail with "java not found" -```bash -# Install Java JDK -sudo apt-get install -y default-jdk - -# Verify installation -java -version # Should show: openjdk version "21.0.8" -``` - -### ESLint/Prettier Installation Fails -```bash -# Ensure npm is available -npm --version - -# Manually install tools -cd ~/.symphony/tools -npm install eslint@^8.0.0 prettier@latest -``` - -### Checkstyle Download Fails (HTTP 404) -- The Checkstyle version was updated to 10.26.1 on 2025-11-16 -- If you see 404 errors, check `/workspace/internal/adapter/checkstyle/adapter.go` for the correct version -- Maven Central URL: https://repo1.maven.org/maven2/com/puppycrawl/tools/checkstyle/ - ---- - -## Development Environment - -### Recommended VS Code Extensions -- Go (golang.go) -- YAML (redhat.vscode-yaml) -- JSON (vscode.json-language-features) - -### Directory Structure -``` -~/.symphony/tools/ # Auto-installed external tools - โ”œโ”€โ”€ node_modules/ # JavaScript/TypeScript tools - โ”‚ โ”œโ”€โ”€ eslint/ - โ”‚ โ”œโ”€โ”€ prettier/ - โ”‚ โ””โ”€โ”€ typescript/ - โ”œโ”€โ”€ checkstyle-10.26.1.jar # Java style checker - โ””โ”€โ”€ pmd-/ # Java static analyzer -``` - ---- - -## Installation History - -### 2025-11-16: Java JDK Installation -- **Installed**: OpenJDK 21.0.8 -- **Reason**: Required for Java validation integration tests -- **Method**: `sudo apt-get install -y default-jdk` -- **Verification**: `java -version` shows OpenJDK 21.0.8+9 -- **Impact**: Enables Checkstyle and PMD adapters for Java code validation From 2128ec52dd9a5b1b5e6e55696beef8d4816e95ac Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 16:29:01 +0000 Subject: [PATCH 08/26] refactor: remove unused legacy code - Delete engine.go (unused Engine interface, EngineRegistry) - Delete CanAutoFix() stub method - Delete AutoFix() stub method --- internal/validator/engine.go | 72 --------------------------------- internal/validator/validator.go | 10 ----- 2 files changed, 82 deletions(-) delete mode 100644 internal/validator/engine.go diff --git a/internal/validator/engine.go b/internal/validator/engine.go deleted file mode 100644 index 3a646c1..0000000 --- a/internal/validator/engine.go +++ /dev/null @@ -1,72 +0,0 @@ -package validator - -import ( - "context" - - "github.com/DevSymphony/sym-cli/pkg/schema" -) - -// Engine represents a validation engine that can check code against rules -type Engine interface { - // Name returns the engine name (e.g., "eslint", "llm-validator", "checkstyle") - Name() string - - // CanHandle checks if this engine can handle the given rule - CanHandle(rule schema.PolicyRule) bool - - // Returns violations found - Execute(ctx context.Context, files []string, rules []schema.PolicyRule) ([]Violation, error) -} - -// EngineRegistry manages available validation engines -type EngineRegistry struct { - engines map[string]Engine -} - -// NewEngineRegistry creates a new engine registry -func NewEngineRegistry() *EngineRegistry { - return &EngineRegistry{ - engines: make(map[string]Engine), - } -} - -// Register registers a validation engine -func (r *EngineRegistry) Register(engine Engine) { - r.engines[engine.Name()] = engine -} - -// Get retrieves an engine by name -func (r *EngineRegistry) Get(name string) (Engine, bool) { - engine, ok := r.engines[name] - return engine, ok -} - -// GetEngineForRule finds the appropriate engine for a rule -func (r *EngineRegistry) GetEngineForRule(rule schema.PolicyRule) Engine { - // Check if rule specifies an engine - if engineName, ok := rule.Check["engine"].(string); ok { - if engine, exists := r.engines[engineName]; exists { - if engine.CanHandle(rule) { - return engine - } - } - } - - // Fallback: find any engine that can handle this rule - for _, engine := range r.engines { - if engine.CanHandle(rule) { - return engine - } - } - - return nil -} - -// ListEngines returns all registered engines -func (r *EngineRegistry) ListEngines() []string { - names := make([]string, 0, len(r.engines)) - for name := range r.engines { - names = append(names, name) - } - return names -} \ No newline at end of file diff --git a/internal/validator/validator.go b/internal/validator/validator.go index 9ebadd6..752471a 100644 --- a/internal/validator/validator.go +++ b/internal/validator/validator.go @@ -713,13 +713,3 @@ func (v *Validator) Close() error { } return nil } - -// CanAutoFix checks if violations can be auto-fixed -func (v *Result) CanAutoFix() bool { - return false -} - -// AutoFix attempts to automatically fix violations -func (v *Validator) AutoFix(result *Result) error { - return fmt.Errorf("auto-fix not implemented yet") -} From d0b596b98ff3af7a1ffb174edcd1d587756344ff Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 16:37:41 +0000 Subject: [PATCH 09/26] refactor: remove unused Validate method and Result struct - Remove Validate() method that was not used anywhere in production code - Remove Result struct that was only used by Validate() - Remove checkRBAC() and selectFilesForRule() helper methods - ValidateChanges() is the active API for git change validation --- internal/validator/validator.go | 171 -------------------------------- 1 file changed, 171 deletions(-) diff --git a/internal/validator/validator.go b/internal/validator/validator.go index 752471a..6792412 100644 --- a/internal/validator/validator.go +++ b/internal/validator/validator.go @@ -33,12 +33,6 @@ type Violation struct { ExecutionMs int64 // execution time in milliseconds } -// Result represents validation result -type Result struct { - Violations []Violation - Passed bool -} - // Validator validates code against policy using adapters directly // This replaces the old engine-based architecture type Validator struct { @@ -81,90 +75,6 @@ func (v *Validator) SetLLMClient(client *llm.Client) { v.llmClient = client } -// Validate validates the given path using adapters directly -func (v *Validator) Validate(path string) (*Result, error) { - if v.policy == nil { - return nil, fmt.Errorf("policy is not loaded") - } - - result := &Result{ - Violations: make([]Violation, 0), - Passed: true, - } - - if v.verbose { - fmt.Printf("๐Ÿ” Validating %s against %d rule(s)...\n", path, len(v.policy.Rules)) - } - - // Check RBAC permissions if enabled - if err := v.checkRBAC(path, result); err != nil { - if v.verbose { - fmt.Printf("โš ๏ธ RBAC check error: %v\n", err) - } - } - - // Validate each enabled rule - for _, rule := range v.policy.Rules { - if !rule.Enabled { - continue - } - - engineName := getEngineName(rule) - if engineName == "" { - if v.verbose { - fmt.Printf("โš ๏ธ Rule %s has no engine specified, skipping\n", rule.ID) - } - continue - } - - // Get files that match this rule's selector - files, err := v.selectFilesForRule(path, &rule) - if err != nil { - fmt.Printf("โš ๏ธ Failed to select files for rule %s: %v\n", rule.ID, err) - continue - } - - if len(files) == 0 { - if v.verbose { - fmt.Printf(" Rule %s: no matching files\n", rule.ID) - } - continue - } - - if v.verbose { - fmt.Printf(" Rule %s (%s): checking %d file(s)...\n", rule.ID, engineName, len(files)) - } - - // Execute validation based on engine type - violations, err := v.executeRule(engineName, rule, files) - if err != nil { - fmt.Printf("โš ๏ธ Validation failed for rule %s: %v\n", rule.ID, err) - continue - } - - if len(violations) > 0 { - result.Violations = append(result.Violations, violations...) - if v.verbose { - fmt.Printf(" โŒ Found %d violation(s)\n", len(violations)) - } - } else if v.verbose { - fmt.Printf(" โœ“ Passed\n") - } - } - - result.Passed = len(result.Violations) == 0 - - if v.verbose { - if result.Passed { - fmt.Printf("\nโœ… Validation passed: no violations found\n") - } else { - fmt.Printf("\nโŒ Validation failed: %d violation(s) found\n", len(result.Violations)) - } - } - - return result, nil -} - // executeRule executes a rule using the appropriate adapter func (v *Validator) executeRule(engineName string, rule schema.PolicyRule, files []string) ([]Violation, error) { // Special case: LLM validator @@ -429,87 +339,6 @@ func getEngineName(rule schema.PolicyRule) string { return "" } -// checkRBAC checks RBAC permissions -func (v *Validator) checkRBAC(path string, result *Result) error { - if v.policy.Enforce.RBACConfig == nil || !v.policy.Enforce.RBACConfig.Enabled { - return nil - } - - username, err := git.GetCurrentUser() - if err != nil { - return err - } - - if v.verbose { - fmt.Printf("๐Ÿ” Checking RBAC permissions for user: %s\n", username) - } - - fileInfo, err := os.Stat(path) - if err == nil && !fileInfo.IsDir() { - rbacResult, err := roles.ValidateFilePermissions(username, []string{path}) - if err != nil { - return err - } - - if !rbacResult.Allowed { - for _, deniedFile := range rbacResult.DeniedFiles { - result.Violations = append(result.Violations, Violation{ - RuleID: "rbac-permission-denied", - Severity: "error", - Message: fmt.Sprintf("User '%s' does not have permission to modify this file", username), - File: deniedFile, - Line: 0, - Column: 0, - }) - } - result.Passed = false - - if v.verbose { - fmt.Printf("โŒ RBAC: %d file(s) denied for user %s\n", len(rbacResult.DeniedFiles), username) - } - } else if v.verbose { - fmt.Printf("โœ“ RBAC: User %s has permission to modify all files\n", username) - } - } - - return nil -} - -// selectFilesForRule selects files that match the rule's selector -func (v *Validator) selectFilesForRule(basePath string, rule *schema.PolicyRule) ([]string, error) { - fileInfo, err := os.Stat(basePath) - if err != nil { - return nil, err - } - - if !fileInfo.IsDir() { - // Single file - check if it matches selector - if rule.When != nil { - lang := GetLanguageFromFile(basePath) - if len(rule.When.Languages) > 0 { - matched := false - for _, ruleLang := range rule.When.Languages { - if ruleLang == lang { - matched = true - break - } - } - if !matched { - return []string{}, nil - } - } - } - return []string{basePath}, nil - } - - // Directory - use selector to find files - if rule.When == nil { - return v.selector.SelectFiles(nil) - } - - return v.selector.SelectFiles(rule.When) -} - // ValidateChanges validates git changes using adapters directly func (v *Validator) ValidateChanges(ctx context.Context, changes []GitChange) (*ValidationResult, error) { if v.policy == nil { From 6da13e0a3cbda53c85e5a21ea5f7fcef7bd68f2b Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 16:46:13 +0000 Subject: [PATCH 10/26] refactor: remove deprecated IsAdmin function - Remove IsAdmin() function marked as deprecated - Function was unused across entire codebase - Replaced by RBAC canEditPolicy permission system --- internal/roles/roles.go | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/internal/roles/roles.go b/internal/roles/roles.go index 75bd1dc..298e5d6 100644 --- a/internal/roles/roles.go +++ b/internal/roles/roles.go @@ -86,26 +86,6 @@ func GetUserRole(username string) (string, error) { return "none", nil } -// IsAdmin checks if a user is in the "admin" role -// NOTE: This is deprecated. Use RBAC canEditPolicy permission instead. -func IsAdmin(username string) (bool, error) { - roles, err := LoadRoles() - if err != nil { - return false, err - } - - // Check if admin role exists and contains the user - if adminUsers, exists := roles["admin"]; exists { - for _, admin := range adminUsers { - if admin == username { - return true, nil - } - } - } - - return false, nil -} - // RolesExists checks if roles.json file exists func RolesExists() (bool, error) { rolesPath, err := GetRolesPath() From 6fbeb18907b724e781073ed5af6b791fda821a8c Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 16:55:28 +0000 Subject: [PATCH 11/26] refactor: remove unused code from policy package - Remove Loader.verbose field that was never read - Remove SaveCodePolicy() method that was never called - Remove ApplyTemplate() wrapper function (use GetTemplate directly) --- internal/policy/loader.go | 19 +------------------ internal/policy/templates.go | 5 ----- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/internal/policy/loader.go b/internal/policy/loader.go index c0205d6..a7edb09 100644 --- a/internal/policy/loader.go +++ b/internal/policy/loader.go @@ -10,14 +10,11 @@ import ( // Loader handles loading policy files type Loader struct { - verbose bool } // NewLoader creates a new policy loader func NewLoader(verbose bool) *Loader { - return &Loader{ - verbose: verbose, - } + return &Loader{} } // LoadUserPolicy loads user-friendly policy (A schema) @@ -49,17 +46,3 @@ func (l *Loader) LoadCodePolicy(path string) (*schema.CodePolicy, error) { return &policy, nil } - -// SaveCodePolicy saves policy to file -func (l *Loader) SaveCodePolicy(path string, policy *schema.CodePolicy) error { - data, err := json.MarshalIndent(policy, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal policy: %w", err) - } - - if err := os.WriteFile(path, data, 0644); err != nil { - return fmt.Errorf("failed to write policy file: %w", err) - } - - return nil -} diff --git a/internal/policy/templates.go b/internal/policy/templates.go index 39e0f7e..4995bfc 100644 --- a/internal/policy/templates.go +++ b/internal/policy/templates.go @@ -99,8 +99,3 @@ func GetTemplate(name string) (*schema.UserPolicy, error) { return &policy, nil } - -// ApplyTemplate applies a template to create a new policy -func ApplyTemplate(templateName string) (*schema.UserPolicy, error) { - return GetTemplate(templateName) -} From 854b8015e6e570f8e7c18fa9958e170a320108b1 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Mon, 24 Nov 2025 17:07:14 +0000 Subject: [PATCH 12/26] refactor: remove HTTP mode and incomplete tests Remove HTTP mode components: - Delete startHTTPServer, handleHealthCheck, handleHTTPRequest methods - Delete handleInitialize, handleToolsList, handleToolsCall methods - Delete JSONRPCRequest, JSONRPCResponse types (keep RPCError for internal use) - Remove Server.host field - Update NewServer signature (remove host parameter) - Simplify Start() method to stdio-only - Update cmd/mcp.go to remove --host flag Remove incomplete test: - Delete TestFilterConventionsWithDefaults and helper types - Test only logged JSON without assertions MCP server now uses stdio mode exclusively for integration with Claude Desktop, Claude Code, Cursor, and other MCP clients. --- internal/cmd/mcp.go | 13 +- internal/mcp/server.go | 274 +----------------------------------- internal/mcp/server_test.go | 50 ------- 3 files changed, 11 insertions(+), 326 deletions(-) diff --git a/internal/cmd/mcp.go b/internal/cmd/mcp.go index e38c4db..ccdd75e 100644 --- a/internal/cmd/mcp.go +++ b/internal/cmd/mcp.go @@ -14,7 +14,6 @@ import ( var ( mcpConfig string - mcpHost string mcpPort int ) @@ -22,16 +21,15 @@ var mcpCmd = &cobra.Command{ Use: "mcp", Short: "Start MCP server to integrate with LLM tools", Long: `Start Model Context Protocol (MCP) server. -LLM-based coding tools can query conventions and validate code through stdio or HTTP. +LLM-based coding tools can query conventions and validate code through stdio. Tools provided by MCP server: - query_conventions: Query conventions for given context - validate_code: Validate code compliance with conventions -By default, communicates via stdio. If --port is specified, starts HTTP server.`, +Communicates via stdio for integration with Claude Desktop, Claude Code, Cursor, and other MCP clients.`, Example: ` sym mcp - sym mcp --config code-policy.json - sym mcp --port 4000 --host 0.0.0.0`, + sym mcp --config code-policy.json`, RunE: runMCP, } @@ -39,8 +37,7 @@ func init() { rootCmd.AddCommand(mcpCmd) mcpCmd.Flags().StringVarP(&mcpConfig, "config", "c", "", "policy file path (code-policy.json)") - mcpCmd.Flags().StringVar(&mcpHost, "host", "127.0.0.1", "server host (HTTP mode only)") - mcpCmd.Flags().IntVarP(&mcpPort, "port", "p", 0, "server port (0 = stdio mode, >0 = HTTP mode)") + mcpCmd.Flags().IntVarP(&mcpPort, "port", "p", 0, "server port (unused, kept for compatibility)") } func runMCP(cmd *cobra.Command, args []string) error { @@ -81,7 +78,7 @@ func runMCP(cmd *cobra.Command, args []string) error { } // Start MCP server - it will handle conversion automatically if needed - server := mcp.NewServer(mcpHost, mcpPort, configPath) + server := mcp.NewServer(mcpPort, configPath) return server.Start() } diff --git a/internal/mcp/server.go b/internal/mcp/server.go index 6f9ff20..bec8f6d 100644 --- a/internal/mcp/server.go +++ b/internal/mcp/server.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "net/http" "os" "path/filepath" "strings" @@ -78,9 +77,8 @@ func ConvertPolicyWithLLM(userPolicyPath, codePolicyPath string) error { } // Server is a MCP (Model Context Protocol) server. -// It communicates via JSON-RPC over stdio or HTTP. +// It communicates via JSON-RPC over stdio. type Server struct { - host string port int configPath string userPolicy *schema.UserPolicy @@ -89,9 +87,8 @@ type Server struct { } // NewServer creates a new MCP server instance. -func NewServer(host string, port int, configPath string) *Server { +func NewServer(port int, configPath string) *Server { return &Server{ - host: host, port: port, configPath: configPath, loader: policy.NewLoader(false), // verbose = false for MCP @@ -99,7 +96,7 @@ func NewServer(host string, port int, configPath string) *Server { } // Start starts the MCP server. -// It communicates via JSON-RPC over stdio or HTTP. +// It communicates via JSON-RPC over stdio. func (s *Server) Start() error { // Determine the directory to look for policy files var dir string @@ -180,138 +177,17 @@ func (s *Server) Start() error { } } - if s.port > 0 { - return s.startHTTPServer() - } - fmt.Fprintln(os.Stderr, "Symphony MCP server started (stdio mode)") - fmt.Fprintf(os.Stderr, "Listening on: %s:%d\n", s.host, s.port) fmt.Fprintln(os.Stderr, "Available tools: query_conventions, validate_code") // Use official MCP go-sdk for stdio to ensure spec-compliant framing and lifecycle return s.runStdioWithSDK(context.Background()) } -// startHTTPServer starts HTTP server for JSON-RPC. -func (s *Server) startHTTPServer() error { - addr := fmt.Sprintf("%s:%d", s.host, s.port) - - mux := http.NewServeMux() - mux.HandleFunc("/", s.handleHTTPRequest) - mux.HandleFunc("/health", s.handleHealthCheck) - - server := &http.Server{ - Addr: addr, - Handler: mux, - ReadHeaderTimeout: 10 * time.Second, - ReadTimeout: 30 * time.Second, - WriteTimeout: 30 * time.Second, - IdleTimeout: 60 * time.Second, - MaxHeaderBytes: 1 << 20, // 1MB - } - - fmt.Fprintf(os.Stderr, "Symphony MCP server started (HTTP mode)\n") - fmt.Fprintf(os.Stderr, "Listening on: http://%s\n", addr) - fmt.Fprintf(os.Stderr, "Available tools: query_conventions, validate_code\n") - - return server.ListenAndServe() -} - -// handleHealthCheck handles health check requests. -func (s *Server) handleHealthCheck(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - if err := json.NewEncoder(w).Encode(map[string]interface{}{ - "status": "ok", - "version": "1.0.0", - }); err != nil { - // Log error but don't fail - headers already sent - _ = err - } -} - -// handleHTTPRequest handles HTTP JSON-RPC requests. -func (s *Server) handleHTTPRequest(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "method not allowed", http.StatusMethodNotAllowed) - return - } - - var req JSONRPCRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusBadRequest) - _ = json.NewEncoder(w).Encode(JSONRPCResponse{ - JSONRPC: "2.0", - Error: &RPCError{ - Code: -32700, - Message: "parse error", - }, - ID: nil, - }) - return - } - - var result interface{} - var rpcErr *RPCError - - switch req.Method { - case "initialize": - result, rpcErr = s.handleInitialize(req.Params) - case "initialized": - // Notification - no response needed, but we'll send empty result - result = nil - case "tools/list": - result, rpcErr = s.handleToolsList(req.Params) - case "tools/call": - result, rpcErr = s.handleToolsCall(req.Params) - case "query_conventions": - result, rpcErr = s.handleQueryConventions(req.Params) - case "validate_code": - result, rpcErr = s.handleValidateCode(req.Params) - default: - rpcErr = &RPCError{ - Code: -32601, - Message: fmt.Sprintf("method not found: %s", req.Method), - } - } - - resp := JSONRPCResponse{ - JSONRPC: "2.0", - Result: result, - Error: rpcErr, - ID: req.ID, - } - - w.Header().Set("Content-Type", "application/json") - if rpcErr != nil { - w.WriteHeader(http.StatusBadRequest) - } else { - w.WriteHeader(http.StatusOK) - } - _ = json.NewEncoder(w).Encode(resp) -} - -// JSONRPCRequest is a JSON-RPC 2.0 request. -type JSONRPCRequest struct { - JSONRPC string `json:"jsonrpc"` - Method string `json:"method"` - Params map[string]interface{} `json:"params"` - ID interface{} `json:"id"` -} - -// JSONRPCResponse is a JSON-RPC 2.0 response. -type JSONRPCResponse struct { - JSONRPC string `json:"jsonrpc"` - Result interface{} `json:"result,omitempty"` - Error *RPCError `json:"error,omitempty"` - ID interface{} `json:"id"` -} - -// RPCError is a JSON-RPC error. +// RPCError is an error type used for internal error handling. type RPCError struct { - Code int `json:"code"` - Message string `json:"message"` + Code int + Message string } // QueryConventionsInput represents the input schema for the query_conventions tool (go-sdk). @@ -724,144 +600,6 @@ func (s *Server) getValidationPolicy() (*schema.CodePolicy, error) { return nil, fmt.Errorf("no code policy loaded - validation requires code policy") } -// handleInitialize handles MCP initialize request. -// This is the first request from the client to establish protocol version and capabilities. -func (s *Server) handleInitialize(params map[string]interface{}) (interface{}, *RPCError) { - return map[string]interface{}{ - "protocolVersion": "2024-11-05", - "capabilities": map[string]interface{}{ - "tools": map[string]interface{}{}, - }, - "serverInfo": map[string]interface{}{ - "name": "symphony", - "version": "1.0.0", - }, - "instructions": `Symphony Code Convention Enforcer - -MANDATORY WORKFLOW for all coding tasks: - -STEP 1 [BEFORE CODE]: Query Conventions -โ†’ Call query_conventions tool FIRST before writing any code -โ†’ Filter by category (security, style, architecture, etc.) -โ†’ Filter by language/files you'll work with -โ†’ Review and understand the conventions - -STEP 2 [DURING CODE]: Write Code -โ†’ Implement your code following the conventions from Step 1 -โ†’ Keep security, style, and architecture guidelines in mind - -STEP 3 [AFTER CODE]: Validate Code -โ†’ Call validate_code tool LAST after completing implementation -โ†’ MANDATORY: Must validate before marking task complete -โ†’ Fix any violations found and re-validate -โ†’ Only proceed when validation passes with no errors - -This 3-step workflow ensures all code meets project standards. Never skip steps 1 and 3.`, - }, nil -} - -// handleToolsList handles tools/list request. -// Returns the list of available tools that clients can call. -func (s *Server) handleToolsList(params map[string]interface{}) (interface{}, *RPCError) { - tools := []map[string]interface{}{ - { - "name": "query_conventions", - "description": `โš ๏ธ CALL THIS FIRST - BEFORE WRITING ANY CODE โš ๏ธ - -This tool is MANDATORY before you start coding. Query project conventions to understand what rules your code must follow. - -Usage: -- Filter by category: security, style, error_handling, architecture, performance, testing, documentation -- Filter by languages: javascript, typescript, python, go, java, etc. - -Example: Before adding a login feature, call query_conventions(category="security") first. - -DO NOT write code before calling this tool. Violations will be caught by validate_code later.`, - "inputSchema": map[string]interface{}{ - "type": "object", - "properties": map[string]interface{}{ - "category": map[string]interface{}{ - "type": "string", - "description": "Filter by category (optional). Leave empty or use 'all' to fetch all categories. Options: security, style, documentation, error_handling, architecture, performance, testing", - }, - "languages": map[string]interface{}{ - "type": "array", - "items": map[string]string{"type": "string"}, - "description": "Programming languages to filter by (optional). Leave empty to get conventions for all languages. Examples: go, javascript, typescript, python, java", - }, - }, - }, - }, - { - "name": "validate_code", - "description": `[STEP 3 - ALWAYS CALL LAST] Validate your git changes against all project conventions. - -CRITICAL WORKFLOW: -1. Call this tool AFTER you have written or modified code -2. MANDATORY: Always validate before considering the task complete -3. If violations are found, fix them and validate again -4. Only mark the task as done after validation passes with no errors - -This tool automatically validates: -- All STAGED changes (git add) -- All UNSTAGED changes (modified but not staged) -- Only checks the ADDED/MODIFIED lines in your diffs (not entire files) - -This is the final quality gate. Never skip this validation step. - -The tool will check your changes for: -- Security violations (hardcoded secrets, SQL injection, XSS, etc.) -- Style violations (formatting, naming, documentation) -- Architecture violations (separation of concerns, patterns) -- Error handling violations (missing error checks, empty catch blocks) - -If violations are found, you MUST fix them before proceeding.`, - "inputSchema": map[string]interface{}{ - "type": "object", - "properties": map[string]interface{}{ - "role": map[string]interface{}{ - "type": "string", - "description": "RBAC role for validation (optional)", - }, - }, - }, - }, - } - - return map[string]interface{}{ - "tools": tools, - }, nil -} - -// handleToolsCall handles tools/call request. -// This routes tool calls to the appropriate handler based on tool name. -func (s *Server) handleToolsCall(params map[string]interface{}) (interface{}, *RPCError) { - toolName, ok := params["name"].(string) - if !ok { - return nil, &RPCError{ - Code: -32602, - Message: "tool name is required", - } - } - - arguments, ok := params["arguments"].(map[string]interface{}) - if !ok { - arguments = make(map[string]interface{}) - } - - switch toolName { - case "query_conventions": - return s.handleQueryConventions(arguments) - case "validate_code": - return s.handleValidateCode(arguments) - default: - return nil, &RPCError{ - Code: -32601, - Message: fmt.Sprintf("unknown tool: %s", toolName), - } - } -} - // needsConversion checks if user policy needs to be converted to code policy. // Returns true if: // 1. code-policy.json doesn't exist, OR diff --git a/internal/mcp/server_test.go b/internal/mcp/server_test.go index 30093a6..a8686e6 100644 --- a/internal/mcp/server_test.go +++ b/internal/mcp/server_test.go @@ -1,7 +1,6 @@ package mcp import ( - "encoding/json" "os" "path/filepath" "testing" @@ -209,52 +208,3 @@ func TestQueryConventions(t *testing.T) { assert.NotContains(t, text, "DOC-001") }) } - -func TestFilterConventionsWithDefaults(t *testing.T) { - // Create test server with user policy that has defaults - userPolicy := &UserPolicyForTest{ - Defaults: DefaultsForTest{ - Severity: "error", - }, - Rules: []UserRuleForTest{ - { - ID: "TEST-001", - Say: "Test rule without severity", - Category: "testing", - Languages: []string{"go"}, - // No severity or message specified - }, - { - ID: "TEST-002", - Say: "Test rule with severity", - Category: "testing", - Languages: []string{"go"}, - Severity: "warning", - Message: "Custom message", - }, - }, - } - - // Convert to JSON and back to ensure proper structure - data, _ := json.Marshal(userPolicy) - t.Logf("User policy: %s", string(data)) -} - -// Test helper types to match schema.UserPolicy structure -type UserPolicyForTest struct { - Defaults DefaultsForTest `json:"defaults"` - Rules []UserRuleForTest `json:"rules"` -} - -type DefaultsForTest struct { - Severity string `json:"severity"` -} - -type UserRuleForTest struct { - ID string `json:"id"` - Say string `json:"say"` - Category string `json:"category"` - Languages []string `json:"languages"` - Severity string `json:"severity,omitempty"` - Message string `json:"message,omitempty"` -} From 322507321cc8e8944cb6ef5e7e420624dede64ef Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 01:58:01 +0000 Subject: [PATCH 13/26] refactor: remove unused client options and method Remove unused exported symbols that were never called: - WithMaxTokens() - default value always used - WithTemperature() - default value always used - WithVerbose() - never enabled - CheckAvailability() - never integrated into workflow Active API remains: NewClient, WithModel, WithTimeout, Complete --- internal/llm/client.go | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/internal/llm/client.go b/internal/llm/client.go index 757403b..b386866 100644 --- a/internal/llm/client.go +++ b/internal/llm/client.go @@ -40,20 +40,6 @@ func WithModel(model string) ClientOption { } } -// WithMaxTokens sets the maximum tokens for responses -func WithMaxTokens(maxTokens int) ClientOption { - return func(c *Client) { - c.maxTokens = maxTokens - } -} - -// WithTemperature sets the sampling temperature -func WithTemperature(temperature float64) ClientOption { - return func(c *Client) { - c.temperature = temperature - } -} - // WithTimeout sets the HTTP client timeout func WithTimeout(timeout time.Duration) ClientOption { return func(c *Client) { @@ -61,13 +47,6 @@ func WithTimeout(timeout time.Duration) ClientOption { } } -// WithVerbose enables verbose logging -func WithVerbose(verbose bool) ClientOption { - return func(c *Client) { - c.verbose = verbose - } -} - // NewClient creates a new OpenAI API client func NewClient(apiKey string, opts ...ClientOption) *Client { if apiKey == "" { @@ -203,18 +182,3 @@ func (c *Client) Complete(ctx context.Context, systemPrompt, userPrompt string) return content, nil } - -// CheckAvailability checks if the OpenAI API is available -func (c *Client) CheckAvailability(ctx context.Context) error { - if c.apiKey == "" { - return fmt.Errorf("OPENAI_API_KEY environment variable not set") - } - - // Simple test request - _, err := c.Complete(ctx, "You are a test assistant.", "Say 'OK'") - if err != nil { - return fmt.Errorf("OpenAI API not available: %w", err) - } - - return nil -} From 9e90cd191777e39420e118700788221fa6358e2c Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 02:12:58 +0000 Subject: [PATCH 14/26] refactor: remove unused OAuthTokenResponse fields Remove TokenType and Scope fields from OAuthTokenResponse struct. These fields were decoded from JSON but never read or used anywhere. Only AccessToken is needed for the OAuth flow. --- internal/github/client.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/github/client.go b/internal/github/client.go index 4f2e1f4..90fb3b7 100644 --- a/internal/github/client.go +++ b/internal/github/client.go @@ -24,8 +24,6 @@ type User struct { type OAuthTokenResponse struct { AccessToken string `json:"access_token"` - TokenType string `json:"token_type"` - Scope string `json:"scope"` } // NewClient creates a new GitHub API client From cea642fbaf76f201e0d528d13aa80d95c7ddd030 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 02:26:45 +0000 Subject: [PATCH 15/26] refactor: unexport internal-only symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LanguageLinterMapping, LLMValidatorEngine๋ฅผ unexport - CheckstyleModule, CheckstyleProperty, CheckstyleConfig๋ฅผ unexport - PMDRuleset, PMDRule๋ฅผ unexport --- internal/converter/converter.go | 20 +++++------ internal/converter/linters/checkstyle.go | 42 ++++++++++++------------ internal/converter/linters/pmd.go | 20 +++++------ 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/internal/converter/converter.go b/internal/converter/converter.go index b58a4ab..025cdce 100644 --- a/internal/converter/converter.go +++ b/internal/converter/converter.go @@ -14,8 +14,8 @@ import ( "github.com/DevSymphony/sym-cli/pkg/schema" ) -// LanguageLinterMapping defines which linters are available for each language -var LanguageLinterMapping = map[string][]string{ +// languageLinterMapping defines which linters are available for each language +var languageLinterMapping = map[string][]string{ "javascript": {"eslint", "prettier"}, "js": {"eslint", "prettier"}, "typescript": {"tsc", "eslint", "prettier"}, @@ -25,8 +25,8 @@ var LanguageLinterMapping = map[string][]string{ "java": {"checkstyle", "pmd"}, } -// Special linter for rules that don't fit any specific external linter -const LLMValidatorEngine = "llm-validator" +// llmValidatorEngine is the special linter for rules that don't fit any specific external linter +const llmValidatorEngine = "llm-validator" // Converter is the main converter with language-based routing type Converter struct { @@ -115,7 +115,7 @@ func (c *Converter) Convert(ctx context.Context, userPolicy *schema.UserPolicy) } // Skip llm-validator - it will be handled in CodePolicy only - if linterName == LLMValidatorEngine { + if linterName == llmValidatorEngine { continue } @@ -189,7 +189,7 @@ func (c *Converter) Convert(ctx context.Context, userPolicy *schema.UserPolicy) } // Special handling for LLM validator - ensure required fields - if linterName == LLMValidatorEngine { + if linterName == llmValidatorEngine { // LLM validator MUST have 'when' selector for file filtering if policyRule.When == nil { // Use languages from rule or defaults @@ -214,7 +214,7 @@ func (c *Converter) Convert(ctx context.Context, userPolicy *schema.UserPolicy) } // Add selector if languages are specified (for non-LLM linters) - if linterName != LLMValidatorEngine && (len(userRule.Languages) > 0 || len(userRule.Include) > 0 || len(userRule.Exclude) > 0) { + if linterName != llmValidatorEngine && (len(userRule.Languages) > 0 || len(userRule.Include) > 0 || len(userRule.Exclude) > 0) { policyRule.When = &schema.Selector{ Languages: userRule.Languages, Include: userRule.Include, @@ -274,7 +274,7 @@ func (c *Converter) routeRulesWithLLM(ctx context.Context, userPolicy *schema.Us availableLinters := c.getAvailableLinters(languages) if len(availableLinters) == 0 { // No language-specific linters, use llm-validator - linterRules[LLMValidatorEngine] = append(linterRules[LLMValidatorEngine], rule) + linterRules[llmValidatorEngine] = append(linterRules[llmValidatorEngine], rule) continue } @@ -283,7 +283,7 @@ func (c *Converter) routeRulesWithLLM(ctx context.Context, userPolicy *schema.Us if len(selectedLinters) == 0 { // LLM couldn't map to any linter, use llm-validator - linterRules[LLMValidatorEngine] = append(linterRules[LLMValidatorEngine], rule) + linterRules[llmValidatorEngine] = append(linterRules[llmValidatorEngine], rule) } else { // Add rule to selected linters for _, linter := range selectedLinters { @@ -304,7 +304,7 @@ func (c *Converter) getAvailableLinters(languages []string) []string { linterSet := make(map[string]bool) for _, lang := range languages { - if linters, ok := LanguageLinterMapping[lang]; ok { + if linters, ok := languageLinterMapping[lang]; ok { for _, linter := range linters { linterSet[linter] = true } diff --git a/internal/converter/linters/checkstyle.go b/internal/converter/linters/checkstyle.go index d90bb57..e92c69a 100644 --- a/internal/converter/linters/checkstyle.go +++ b/internal/converter/linters/checkstyle.go @@ -30,26 +30,26 @@ func (c *CheckstyleLinterConverter) SupportedLanguages() []string { return []string{"java"} } -// CheckstyleModule represents a Checkstyle module -type CheckstyleModule struct { - XMLName xml.Name `xml:"module"` - Name string `xml:"name,attr"` - Properties []CheckstyleProperty `xml:"property,omitempty"` - Modules []CheckstyleModule `xml:"module,omitempty"` +// checkstyleModule represents a Checkstyle module +type checkstyleModule struct { + XMLName xml.Name `xml:"module"` + Name string `xml:"name,attr"` + Properties []checkstyleProperty `xml:"property,omitempty"` + Modules []checkstyleModule `xml:"module,omitempty"` } -// CheckstyleProperty represents a property -type CheckstyleProperty struct { +// checkstyleProperty represents a property +type checkstyleProperty struct { XMLName xml.Name `xml:"property"` Name string `xml:"name,attr"` Value string `xml:"value,attr"` } -// CheckstyleConfig represents root configuration -type CheckstyleConfig struct { +// checkstyleConfig represents root configuration +type checkstyleConfig struct { XMLName xml.Name `xml:"module"` Name string `xml:"name,attr"` - Modules []CheckstyleModule `xml:"module"` + Modules []checkstyleModule `xml:"module"` } // ConvertRules converts user rules to Checkstyle configuration using LLM @@ -61,7 +61,7 @@ func (c *CheckstyleLinterConverter) ConvertRules(ctx context.Context, rules []sc // Convert rules in parallel type moduleResult struct { index int - module *CheckstyleModule + module *checkstyleModule err error } @@ -88,7 +88,7 @@ func (c *CheckstyleLinterConverter) ConvertRules(ctx context.Context, rules []sc }() // Collect modules - var modules []CheckstyleModule + var modules []checkstyleModule var errors []string for result := range results { @@ -107,14 +107,14 @@ func (c *CheckstyleLinterConverter) ConvertRules(ctx context.Context, rules []sc } // Build Checkstyle configuration - treeWalker := CheckstyleModule{ + treeWalker := checkstyleModule{ Name: "TreeWalker", Modules: modules, } - config := CheckstyleConfig{ + config := checkstyleConfig{ Name: "Checker", - Modules: []CheckstyleModule{treeWalker}, + Modules: []checkstyleModule{treeWalker}, } // Marshal to XML @@ -139,7 +139,7 @@ func (c *CheckstyleLinterConverter) ConvertRules(ctx context.Context, rules []sc } // convertSingleRule converts a single rule using LLM -func (c *CheckstyleLinterConverter) convertSingleRule(ctx context.Context, rule schema.UserRule, llmClient *llm.Client) (*CheckstyleModule, error) { +func (c *CheckstyleLinterConverter) convertSingleRule(ctx context.Context, rule schema.UserRule, llmClient *llm.Client) (*checkstyleModule, error) { systemPrompt := `You are a Checkstyle configuration expert. Convert natural language Java coding rules to Checkstyle modules. Return ONLY a JSON object (no markdown fences): @@ -211,20 +211,20 @@ Output: } // Build module - module := &CheckstyleModule{ + module := &checkstyleModule{ Name: result.ModuleName, - Properties: []CheckstyleProperty{}, + Properties: []checkstyleProperty{}, } // Add severity - module.Properties = append(module.Properties, CheckstyleProperty{ + module.Properties = append(module.Properties, checkstyleProperty{ Name: "severity", Value: mapCheckstyleSeverity(result.Severity), }) // Add other properties for key, value := range result.Properties { - module.Properties = append(module.Properties, CheckstyleProperty{ + module.Properties = append(module.Properties, checkstyleProperty{ Name: key, Value: value, }) diff --git a/internal/converter/linters/pmd.go b/internal/converter/linters/pmd.go index ea7e577..75e2939 100644 --- a/internal/converter/linters/pmd.go +++ b/internal/converter/linters/pmd.go @@ -30,19 +30,19 @@ func (c *PMDLinterConverter) SupportedLanguages() []string { return []string{"java"} } -// PMDRuleset represents PMD ruleset -type PMDRuleset struct { +// pmdRuleset represents PMD ruleset +type pmdRuleset struct { XMLName xml.Name `xml:"ruleset"` Name string `xml:"name,attr"` XMLNS string `xml:"xmlns,attr"` XMLNSXSI string `xml:"xmlns:xsi,attr"` XSISchema string `xml:"xsi:schemaLocation,attr"` Description string `xml:"description"` - Rules []PMDRule `xml:"rule"` + Rules []pmdRule `xml:"rule"` } -// PMDRule represents a PMD rule -type PMDRule struct { +// pmdRule represents a PMD rule +type pmdRule struct { XMLName xml.Name `xml:"rule"` Ref string `xml:"ref,attr"` Priority int `xml:"priority,omitempty"` @@ -57,7 +57,7 @@ func (c *PMDLinterConverter) ConvertRules(ctx context.Context, rules []schema.Us // Convert rules in parallel type ruleResult struct { index int - rule *PMDRule + rule *pmdRule err error } @@ -84,7 +84,7 @@ func (c *PMDLinterConverter) ConvertRules(ctx context.Context, rules []schema.Us }() // Collect rules - var pmdRules []PMDRule + var pmdRules []pmdRule var errors []string for result := range results { @@ -103,7 +103,7 @@ func (c *PMDLinterConverter) ConvertRules(ctx context.Context, rules []schema.Us } // Build PMD ruleset - ruleset := PMDRuleset{ + ruleset := pmdRuleset{ Name: "Symphony Rules", XMLNS: "http://pmd.sourceforge.net/ruleset/2.0.0", XMLNSXSI: "http://www.w3.org/2001/XMLSchema-instance", @@ -129,7 +129,7 @@ func (c *PMDLinterConverter) ConvertRules(ctx context.Context, rules []schema.Us } // convertSingleRule converts a single rule using LLM -func (c *PMDLinterConverter) convertSingleRule(ctx context.Context, rule schema.UserRule, llmClient *llm.Client) (*PMDRule, error) { +func (c *PMDLinterConverter) convertSingleRule(ctx context.Context, rule schema.UserRule, llmClient *llm.Client) (*pmdRule, error) { systemPrompt := `You are a PMD configuration expert. Convert natural language Java coding rules to PMD rule references. Return ONLY a JSON object (no markdown fences): @@ -189,7 +189,7 @@ Output: return nil, nil } - return &PMDRule{ + return &pmdRule{ Ref: result.RuleRef, Priority: result.Priority, }, nil From 7ecf4874f25be57fd8777bdf77c35a15a9ec7592 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 02:28:57 +0000 Subject: [PATCH 16/26] refactor: unexport EnsureConfigDir function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EnsureConfigDir โ†’ ensureConfigDir (๋‚ด๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉ) --- internal/config/config.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index e77caaf..1950d87 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -44,8 +44,8 @@ func init() { tokenPath = filepath.Join(configDir, "token.json") } -// EnsureConfigDir creates the config directory if it doesn't exist -func EnsureConfigDir() error { +// ensureConfigDir creates the config directory if it doesn't exist +func ensureConfigDir() error { return os.MkdirAll(configDir, 0700) } @@ -70,7 +70,7 @@ func LoadConfig() (*Config, error) { // SaveConfig saves the configuration to file func SaveConfig(cfg *Config) error { - if err := EnsureConfigDir(); err != nil { + if err := ensureConfigDir(); err != nil { return err } @@ -103,7 +103,7 @@ func LoadToken() (*Token, error) { // SaveToken saves the access token to file func SaveToken(token *Token) error { - if err := EnsureConfigDir(); err != nil { + if err := ensureConfigDir(); err != nil { return err } From 1c195423c7be586b5546393315f44fa187eedf79 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 02:36:13 +0000 Subject: [PATCH 17/26] refactor: remove unused export.go and port parameter --- internal/cmd/export.go | 40 ---------------------------------------- internal/cmd/mcp.go | 8 ++------ internal/cmd/root.go | 4 +--- internal/mcp/server.go | 4 +--- 4 files changed, 4 insertions(+), 52 deletions(-) delete mode 100644 internal/cmd/export.go diff --git a/internal/cmd/export.go b/internal/cmd/export.go deleted file mode 100644 index e09e5f7..0000000 --- a/internal/cmd/export.go +++ /dev/null @@ -1,40 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -var exportCmd = &cobra.Command{ - Use: "export [path]", - Short: "ํ˜„์žฌ ์ž‘์—…์— ํ•„์š”ํ•œ ์ปจ๋ฒค์…˜์„ ์ถ”์ถœํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค", - Long: `ํ˜„์žฌ ์ž‘์—… ์ปจํ…์ŠคํŠธ์— ๋งž๋Š” ๊ด€๋ จ ์ปจ๋ฒค์…˜๋งŒ ์ถ”์ถœํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. -LLM์ด ์ž‘์—… ์‹œ ์ปจํ…์ŠคํŠธ์— ํฌํ•จํ•  ์ˆ˜ ์žˆ๋„๋ก ์ตœ์ ํ™”๋œ ํ˜•ํƒœ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.`, - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - path := "." - if len(args) > 0 { - path = args[0] - } - - context, _ := cmd.Flags().GetString("context") - format, _ := cmd.Flags().GetString("format") - - fmt.Printf("Exporting conventions for: %s\n", path) - fmt.Printf("Context: %s\n", context) - fmt.Printf("Format: %s\n", format) - - // TODO: ์‹ค์ œ ๋‚ด๋ณด๋‚ด๊ธฐ ๋กœ์ง ๊ตฌํ˜„ - return nil - }, -} - -func init() { - rootCmd.AddCommand(exportCmd) - - exportCmd.Flags().StringP("context", "c", "", "work context description") - exportCmd.Flags().StringP("format", "f", "text", "output format (text|json|markdown)") - exportCmd.Flags().StringSlice("files", []string{}, "files being modified") - exportCmd.Flags().StringSlice("languages", []string{}, "programming languages involved") -} diff --git a/internal/cmd/mcp.go b/internal/cmd/mcp.go index ccdd75e..3b726bf 100644 --- a/internal/cmd/mcp.go +++ b/internal/cmd/mcp.go @@ -12,10 +12,7 @@ import ( "github.com/spf13/cobra" ) -var ( - mcpConfig string - mcpPort int -) +var mcpConfig string var mcpCmd = &cobra.Command{ Use: "mcp", @@ -37,7 +34,6 @@ func init() { rootCmd.AddCommand(mcpCmd) mcpCmd.Flags().StringVarP(&mcpConfig, "config", "c", "", "policy file path (code-policy.json)") - mcpCmd.Flags().IntVarP(&mcpPort, "port", "p", 0, "server port (unused, kept for compatibility)") } func runMCP(cmd *cobra.Command, args []string) error { @@ -78,7 +74,7 @@ func runMCP(cmd *cobra.Command, args []string) error { } // Start MCP server - it will handle conversion automatically if needed - server := mcp.NewServer(mcpPort, configPath) + server := mcp.NewServer(configPath) return server.Start() } diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 01f1228..92553c8 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -47,9 +47,7 @@ func init() { rootCmd.AddCommand(policyCmd) // Note: mcpCmd is registered in mcp.go's init() - // sym-cli core commands (in development) + // sym-cli core commands rootCmd.AddCommand(convertCmd) rootCmd.AddCommand(validateCmd) - // TODO: implement export command - // rootCmd.AddCommand(exportCmd) } diff --git a/internal/mcp/server.go b/internal/mcp/server.go index bec8f6d..35bb217 100644 --- a/internal/mcp/server.go +++ b/internal/mcp/server.go @@ -79,7 +79,6 @@ func ConvertPolicyWithLLM(userPolicyPath, codePolicyPath string) error { // Server is a MCP (Model Context Protocol) server. // It communicates via JSON-RPC over stdio. type Server struct { - port int configPath string userPolicy *schema.UserPolicy codePolicy *schema.CodePolicy @@ -87,9 +86,8 @@ type Server struct { } // NewServer creates a new MCP server instance. -func NewServer(port int, configPath string) *Server { +func NewServer(configPath string) *Server { return &Server{ - port: port, configPath: configPath, loader: policy.NewLoader(false), // verbose = false for MCP } From fbaa8b0afc46099673dbe8c91d0fecd57ee2528a Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 02:39:33 +0000 Subject: [PATCH 18/26] refactor: unexport internal-only response types --- internal/auth/server.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/auth/server.go b/internal/auth/server.go index aab1031..9b08097 100644 --- a/internal/auth/server.go +++ b/internal/auth/server.go @@ -11,15 +11,15 @@ import ( "github.com/pkg/browser" ) -// SessionResponse is the response from /authStart -type SessionResponse struct { +// sessionResponse is the response from /authStart +type sessionResponse struct { SessionCode string `json:"session_code"` AuthURL string `json:"auth_url"` ExpiresIn int `json:"expires_in"` } -// StatusResponse is the response from /authStatus -type StatusResponse struct { +// statusResponse is the response from /authStatus +type statusResponse struct { Status string `json:"status"` Message string `json:"message,omitempty"` Error string `json:"error,omitempty"` @@ -64,7 +64,7 @@ func AuthenticateWithServer(serverURL string) (string, string, error) { } // startAuthSession starts a new authentication session -func startAuthSession(serverURL string) (*SessionResponse, error) { +func startAuthSession(serverURL string) (*sessionResponse, error) { url := serverURL + "/authStart" requestBody := map[string]string{ @@ -87,7 +87,7 @@ func startAuthSession(serverURL string) (*SessionResponse, error) { return nil, fmt.Errorf("server returned error: %s (status: %d)", string(body), resp.StatusCode) } - var session SessionResponse + var session sessionResponse if err := json.NewDecoder(resp.Body).Decode(&session); err != nil { return nil, fmt.Errorf("failed to parse server response: %w", err) } @@ -147,7 +147,7 @@ func pollForToken(serverURL, sessionCode string, expiresIn int) (string, string, } // checkAuthStatus checks the authentication status -func checkAuthStatus(url string) (*StatusResponse, error) { +func checkAuthStatus(url string) (*statusResponse, error) { resp, err := http.Get(url) if err != nil { return nil, err @@ -160,14 +160,14 @@ func checkAuthStatus(url string) (*StatusResponse, error) { if resp.StatusCode == http.StatusGone { // Session expired - var status StatusResponse + var status statusResponse _ = json.NewDecoder(resp.Body).Decode(&status) return &status, nil } if resp.StatusCode == http.StatusForbidden { // Denied - var status StatusResponse + var status statusResponse _ = json.NewDecoder(resp.Body).Decode(&status) return &status, nil } @@ -177,7 +177,7 @@ func checkAuthStatus(url string) (*StatusResponse, error) { return nil, fmt.Errorf("server error: %s (status: %d)", string(body), resp.StatusCode) } - var status StatusResponse + var status statusResponse if err := json.NewDecoder(resp.Body).Decode(&status); err != nil { return nil, err } From b0a8fee8dab0d5e2c23e8332cbc9eba8b7d2099e Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 02:42:39 +0000 Subject: [PATCH 19/26] refactor: remove unused registry methods and unexport errors --- internal/adapter/registry/errors.go | 10 ++++---- internal/adapter/registry/registry.go | 33 ++++----------------------- 2 files changed, 9 insertions(+), 34 deletions(-) diff --git a/internal/adapter/registry/errors.go b/internal/adapter/registry/errors.go index aff2741..5dd7688 100644 --- a/internal/adapter/registry/errors.go +++ b/internal/adapter/registry/errors.go @@ -2,14 +2,14 @@ package registry import "fmt" -// ErrAdapterNotFound is returned when no adapter is found for the given tool name. -type ErrAdapterNotFound struct { +// errAdapterNotFound is returned when no adapter is found for the given tool name. +type errAdapterNotFound struct { ToolName string } -func (e *ErrAdapterNotFound) Error() string { +func (e *errAdapterNotFound) Error() string { return fmt.Sprintf("adapter not found: %s", e.ToolName) } -// ErrNilAdapter is returned when trying to register a nil adapter. -var ErrNilAdapter = fmt.Errorf("cannot register nil adapter") +// errNilAdapter is returned when trying to register a nil adapter. +var errNilAdapter = fmt.Errorf("cannot register nil adapter") diff --git a/internal/adapter/registry/registry.go b/internal/adapter/registry/registry.go index 4bb3333..3e87958 100644 --- a/internal/adapter/registry/registry.go +++ b/internal/adapter/registry/registry.go @@ -22,10 +22,10 @@ func NewRegistry() *Registry { } // Register adds an adapter to the registry. -// Returns ErrNilAdapter if the adapter is nil. +// Returns errNilAdapter if the adapter is nil. func (r *Registry) Register(adp adapter.Adapter) error { if adp == nil { - return ErrNilAdapter + return errNilAdapter } r.mu.Lock() @@ -38,40 +38,15 @@ func (r *Registry) Register(adp adapter.Adapter) error { } // GetAdapter finds an adapter by tool name (e.g., "eslint", "prettier", "tsc"). -// Returns the adapter, or ErrAdapterNotFound if not registered. +// Returns the adapter, or errAdapterNotFound if not registered. func (r *Registry) GetAdapter(toolName string) (adapter.Adapter, error) { r.mu.RLock() defer r.mu.RUnlock() adp, ok := r.adapters[toolName] if !ok { - return nil, &ErrAdapterNotFound{ToolName: toolName} + return nil, &errAdapterNotFound{ToolName: toolName} } return adp, nil } - -// GetAll returns all registered adapters. -func (r *Registry) GetAll() []adapter.Adapter { - r.mu.RLock() - defer r.mu.RUnlock() - - // Return a copy to prevent external modification - result := make([]adapter.Adapter, 0, len(r.adapters)) - for _, adp := range r.adapters { - result = append(result, adp) - } - return result -} - -// ListTools returns all registered tool names. -func (r *Registry) ListTools() []string { - r.mu.RLock() - defer r.mu.RUnlock() - - tools := make([]string, 0, len(r.adapters)) - for name := range r.adapters { - tools = append(tools, name) - } - return tools -} From 7e4be3cc48e644cbb085ba5ff6625a1ac7d65766 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 02:53:33 +0000 Subject: [PATCH 20/26] refactor: remove dead TestMatchPattern test --- tests/integration/rbac_test.go | 49 ---------------------------------- 1 file changed, 49 deletions(-) diff --git a/tests/integration/rbac_test.go b/tests/integration/rbac_test.go index 6b1e86f..16493fe 100644 --- a/tests/integration/rbac_test.go +++ b/tests/integration/rbac_test.go @@ -4,57 +4,8 @@ import ( "testing" "github.com/DevSymphony/sym-cli/internal/roles" - "github.com/DevSymphony/sym-cli/pkg/schema" ) -// Test matchPattern function with various glob patterns -func TestMatchPattern(t *testing.T) { - tests := []struct { - pattern string - path string - expected bool - }{ - // ** patterns - {"src/**", "src/components/Button.js", true}, - {"src/**", "src/utils/helper.js", true}, - {"src/**", "lib/main.js", false}, - {"src/components/**", "src/components/ui/Button.js", true}, - {"src/components/**", "src/utils/helper.js", false}, - - // ** with suffix - {"**/*.js", "src/components/Button.js", true}, - {"**/*.js", "lib/utils.js", true}, - {"**/*.js", "src/styles.css", false}, - {"src/**/test", "src/components/test", true}, - {"src/**/test", "src/a/b/c/test", true}, - - // * patterns - {"src/*.js", "src/main.js", true}, - {"src/*.js", "src/components/Button.js", false}, - - // Exact match - {"src/main.js", "src/main.js", true}, - {"src/main.js", "src/app.js", false}, - - // Directory prefix - {"src/components/", "src/components/Button.js", true}, - {"src/components/", "src/utils/helper.js", false}, - } - - for _, tt := range tests { - // Since matchPattern is not exported, we'll test through checkFilePermission - role := &schema.UserRole{ - AllowWrite: []string{tt.pattern}, - DenyWrite: []string{}, - } - // This will use matchPattern internally - _ = role - // We can't directly test matchPattern since it's not exported - // So this test is commented out for now - t.Skip("matchPattern is not exported, test through integration") - } -} - // Test complex RBAC scenarios with admin, developer, viewer roles func TestComplexRBACPatterns(t *testing.T) { tests := []struct { From 4f4dcc809c4c59d6031cbb7602f38a18e7e0455a Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 03:43:15 +0000 Subject: [PATCH 21/26] refactor: remove unused helper.go.disabled file --- tests/integration/helper.go.disabled | 85 ---------------------------- 1 file changed, 85 deletions(-) delete mode 100644 tests/integration/helper.go.disabled diff --git a/tests/integration/helper.go.disabled b/tests/integration/helper.go.disabled deleted file mode 100644 index 0caba76..0000000 --- a/tests/integration/helper.go.disabled +++ /dev/null @@ -1,85 +0,0 @@ -package integration - -import ( - "os" - "path/filepath" - "testing" - - "github.com/DevSymphony/sym-cli/internal/policy" - "github.com/DevSymphony/sym-cli/internal/validator" - "github.com/DevSymphony/sym-cli/pkg/schema" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// getTestdataDir returns the path to the testdata directory -func getTestdataDir(t *testing.T) string { - t.Helper() - - // Get current working directory - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("Failed to get working directory: %v", err) - } - - // Go up two levels from tests/integration to project root - projectRoot := filepath.Join(cwd, "../..") - - return projectRoot -} - -// loadPolicyFromTestdata loads a code-policy.json from testdata directory -func loadPolicyFromTestdata(t *testing.T, relativePath string) *schema.CodePolicy { - t.Helper() - loader := policy.NewLoader(false) - policyPath := filepath.Join(getTestdataDir(t), relativePath) - pol, err := loader.LoadCodePolicy(policyPath) - require.NoError(t, err, "Failed to load policy from %s", relativePath) - require.NotNil(t, pol, "Policy should not be nil") - return pol -} - -// createTestValidator creates a validator with given policy and registers cleanup -func createTestValidator(t *testing.T, pol *schema.CodePolicy) *validator.Validator { - t.Helper() - v := validator.NewValidator(pol, testing.Verbose()) - t.Cleanup(func() { - if err := v.Close(); err != nil { - t.Logf("Warning: failed to close validator: %v", err) - } - }) - return v -} - -// assertViolationsDetected asserts that violations are found and logs them -func assertViolationsDetected(t *testing.T, result *validator.Result) { - t.Helper() - assert.False(t, result.Passed, "Should detect violations") - assert.Greater(t, len(result.Violations), 0, "Should have violations") - - // Log violations for debugging - if len(result.Violations) > 0 { - t.Logf("Found %d violation(s):", len(result.Violations)) - for i, v := range result.Violations { - t.Logf(" %d. [%s] %s at %s:%d:%d (severity: %s)", - i+1, v.RuleID, v.Message, v.File, v.Line, v.Column, v.Severity) - } - } -} - -// assertNoPolicyViolations asserts that no violations are found -func assertNoPolicyViolations(t *testing.T, result *validator.Result) { - t.Helper() - if !result.Passed || len(result.Violations) > 0 { - // Log violations if any for debugging - if len(result.Violations) > 0 { - t.Logf("Unexpected violations found:") - for i, v := range result.Violations { - t.Logf(" %d. [%s] %s at %s:%d:%d", - i+1, v.RuleID, v.Message, v.File, v.Line, v.Column) - } - } - } - assert.True(t, result.Passed, "Should pass validation") - assert.Equal(t, 0, len(result.Violations), "Should have no violations") -} From b55306b2aaa3df2ec1174cd477d6a9131f2cf14e Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 03:45:08 +0000 Subject: [PATCH 22/26] refactor: remove unused testdata directory --- tests/testdata/README.md | 111 ---------------- tests/testdata/java/ast/AstViolations.java | 59 --------- tests/testdata/java/ast/ValidAst.java | 77 ----------- tests/testdata/java/ast/code-policy.json | 87 ------------- .../java/length/LengthViolations.java | 77 ----------- tests/testdata/java/length/ValidLength.java | 52 -------- tests/testdata/java/length/code-policy.json | 60 --------- .../java/pattern/NamingViolations.java | 31 ----- tests/testdata/java/pattern/ValidNaming.java | 30 ----- tests/testdata/java/pattern/code-policy.json | 94 -------------- .../testdata/java/style/StyleViolations.java | 54 -------- tests/testdata/java/style/ValidStyle.java | 63 --------- tests/testdata/java/style/code-policy.json | 122 ------------------ .../testdata/javascript/ast/code-policy.json | 63 --------- .../javascript/ast/naming-violations.js | 29 ----- tests/testdata/javascript/ast/valid.js | 28 ---- .../javascript/length/code-policy.json | 60 --------- .../javascript/length/length-violations.js | 65 ---------- tests/testdata/javascript/length/valid.js | 28 ---- .../javascript/pattern/code-policy.json | 81 ------------ .../javascript/pattern/naming-violations.js | 29 ----- .../javascript/pattern/security-violations.js | 18 --- tests/testdata/javascript/pattern/valid.js | 28 ---- .../javascript/style/code-policy.json | 74 ----------- .../javascript/style/style-violations.js | 31 ----- tests/testdata/javascript/style/valid.js | 28 ---- tests/testdata/test_violation.go | 9 -- .../typescript/typechecker/code-policy.json | 60 --------- .../typechecker/strict-mode-errors.ts | 29 ----- .../typescript/typechecker/type-errors.ts | 41 ------ .../testdata/typescript/typechecker/valid.ts | 34 ----- tests/testdata/user-policy-example.json | 50 ------- 32 files changed, 1702 deletions(-) delete mode 100644 tests/testdata/README.md delete mode 100644 tests/testdata/java/ast/AstViolations.java delete mode 100644 tests/testdata/java/ast/ValidAst.java delete mode 100644 tests/testdata/java/ast/code-policy.json delete mode 100644 tests/testdata/java/length/LengthViolations.java delete mode 100644 tests/testdata/java/length/ValidLength.java delete mode 100644 tests/testdata/java/length/code-policy.json delete mode 100644 tests/testdata/java/pattern/NamingViolations.java delete mode 100644 tests/testdata/java/pattern/ValidNaming.java delete mode 100644 tests/testdata/java/pattern/code-policy.json delete mode 100644 tests/testdata/java/style/StyleViolations.java delete mode 100644 tests/testdata/java/style/ValidStyle.java delete mode 100644 tests/testdata/java/style/code-policy.json delete mode 100644 tests/testdata/javascript/ast/code-policy.json delete mode 100644 tests/testdata/javascript/ast/naming-violations.js delete mode 100644 tests/testdata/javascript/ast/valid.js delete mode 100644 tests/testdata/javascript/length/code-policy.json delete mode 100644 tests/testdata/javascript/length/length-violations.js delete mode 100644 tests/testdata/javascript/length/valid.js delete mode 100644 tests/testdata/javascript/pattern/code-policy.json delete mode 100644 tests/testdata/javascript/pattern/naming-violations.js delete mode 100644 tests/testdata/javascript/pattern/security-violations.js delete mode 100644 tests/testdata/javascript/pattern/valid.js delete mode 100644 tests/testdata/javascript/style/code-policy.json delete mode 100644 tests/testdata/javascript/style/style-violations.js delete mode 100644 tests/testdata/javascript/style/valid.js delete mode 100644 tests/testdata/test_violation.go delete mode 100644 tests/testdata/typescript/typechecker/code-policy.json delete mode 100644 tests/testdata/typescript/typechecker/strict-mode-errors.ts delete mode 100644 tests/testdata/typescript/typechecker/type-errors.ts delete mode 100644 tests/testdata/typescript/typechecker/valid.ts delete mode 100644 tests/testdata/user-policy-example.json diff --git a/tests/testdata/README.md b/tests/testdata/README.md deleted file mode 100644 index 0267f2d..0000000 --- a/tests/testdata/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Test Data Directory - -This directory contains test data files for integration testing of the validation engines. - -## Directory Structure - -``` -testdata/ -โ”œโ”€โ”€ javascript/ -โ”‚ โ”œโ”€โ”€ pattern/ # Pattern matching and naming convention tests -โ”‚ โ”œโ”€โ”€ length/ # Line, file, and function length tests -โ”‚ โ”œโ”€โ”€ style/ # Code formatting and style tests -โ”‚ โ””โ”€โ”€ ast/ # AST-based structural tests -โ”œโ”€โ”€ typescript/ -โ”‚ โ””โ”€โ”€ typechecker/ # Type checking tests -โ””โ”€โ”€ java/ - โ”œโ”€โ”€ pattern/ # Pattern matching and naming convention tests - โ”œโ”€โ”€ length/ # Line, method, and parameter length tests - โ”œโ”€โ”€ style/ # Code formatting and style tests - โ””โ”€โ”€ ast/ # AST-based structural tests -``` - -## Engine Types - -### Pattern Engine -Tests regex-based pattern matching and naming conventions. - -**JavaScript Files:** -- `naming-violations.js` - Snake_case and incorrect naming patterns -- `security-violations.js` - Hardcoded secrets and security issues -- `valid.js` - Correct naming conventions - -**Java Files:** -- `NamingViolations.java` - Invalid class, method, variable names -- `ValidNaming.java` - Correct PascalCase and camelCase usage - -### Length Engine -Tests line length, file length, and parameter count limits. - -**JavaScript Files:** -- `length-violations.js` - Long lines, long functions, too many parameters -- `valid.js` - Proper length constraints - -**Java Files:** -- `LengthViolations.java` - Long lines, long methods, too many parameters -- `ValidLength.java` - Proper length constraints - -### Style Engine -Tests code formatting and style conventions. - -**JavaScript Files:** -- `style-violations.js` - Bad indentation, spacing, quotes -- `valid.js` - Proper formatting - -**Java Files:** -- `StyleViolations.java` - Inconsistent indentation, brace placement, spacing -- `ValidStyle.java` - Standard Java formatting - -### AST Engine -Tests structural patterns via Abstract Syntax Tree analysis. - -**JavaScript Files:** -- `naming-violations.js` - AST-level naming issues -- `valid.js` - Valid AST structure - -**Java Files:** -- `AstViolations.java` - Empty catch blocks, System.out usage, missing docs -- `ValidAst.java` - Proper exception handling and structure - -### TypeChecker Engine -Tests type safety and TypeScript-specific checks. - -**TypeScript Files:** -- `type-errors.ts` - Type mismatches and errors -- `strict-mode-errors.ts` - Strict mode violations -- `valid.ts` - Correct type usage - -## File Naming Conventions - -- **Violations**: Files containing rule violations are named `*-violations.*` or `*Violations.*` -- **Valid**: Files with compliant code are named `valid.*` or `Valid*.*` -- **Specific**: Files testing specific issues use descriptive names (e.g., `security-violations.js`) - -## Adding New Test Data - -When adding new test data: - -1. Choose the appropriate engine directory (`pattern`, `length`, `style`, `ast`, `typechecker`) -2. Create both violation and valid files for comprehensive testing -3. Add clear comments explaining what each violation tests -4. Update integration tests to reference new files -5. Ensure files compile/parse correctly for their language - -## Integration Test Usage - -These files are referenced by integration tests in `tests/integration/*_integration_test.go`: - -- `pattern_integration_test.go` - Uses pattern engine test data -- `length_integration_test.go` - Uses length engine test data -- `style_integration_test.go` - Uses style engine test data -- `ast_integration_test.go` - Uses ast engine test data -- `typechecker_integration_test.go` - Uses typechecker engine test data - -## Validation Engines - -Each engine uses specific adapters: - -- **JavaScript/TypeScript**: ESLint, Prettier, TSC -- **Java**: Checkstyle, PMD - -Test data files should reflect the validation capabilities of these underlying tools. diff --git a/tests/testdata/java/ast/AstViolations.java b/tests/testdata/java/ast/AstViolations.java deleted file mode 100644 index 43962ec..0000000 --- a/tests/testdata/java/ast/AstViolations.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Test file with AST-level violations - * Contains structural issues detectable via AST analysis - */ -package com.example; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; - -public class AstViolations { - - // VIOLATION: System.out usage in production code - public void debugPrint(String message) { - System.out.println("Debug: " + message); - } - - // VIOLATION: File I/O without try-catch - public String readFileUnsafe(String path) { - FileReader reader = new FileReader(path); - return "content"; - } - - // VIOLATION: Empty catch block - public void emptyCatch() { - try { - riskyOperation(); - } catch (Exception e) { - // Empty catch - swallows exception - } - } - - // VIOLATION: Generic exception catch - public void catchGeneric() { - try { - riskyOperation(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - // VIOLATION: Missing method documentation - public int calculate(int a, int b, int c) { - return a + b * c; - } - - private void riskyOperation() throws IOException { - File file = new File("test.txt"); - if (!file.exists()) { - throw new IOException("File not found"); - } - } - - public static void main(String[] args) { - AstViolations obj = new AstViolations(); - obj.debugPrint("test"); - obj.emptyCatch(); - } -} diff --git a/tests/testdata/java/ast/ValidAst.java b/tests/testdata/java/ast/ValidAst.java deleted file mode 100644 index 004b1cd..0000000 --- a/tests/testdata/java/ast/ValidAst.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Test file with valid AST structure - * Demonstrates proper exception handling and code structure - */ -package com.example; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.logging.Logger; - -public class ValidAst { - - private static final Logger LOGGER = Logger.getLogger(ValidAst.class.getName()); - - /** - * Reads file content safely with proper exception handling - * - * @param path the file path to read - * @return the file content - * @throws IOException if file cannot be read - */ - public String readFileSafe(String path) throws IOException { - try (FileReader reader = new FileReader(path)) { - return "content"; - } catch (IOException e) { - LOGGER.severe("Failed to read file: " + path); - throw e; - } - } - - /** - * Performs calculation with proper error handling - * - * @param a first operand - * @param b second operand - * @param c third operand - * @return calculated result - */ - public int calculate(int a, int b, int c) { - return a + b * c; - } - - /** - * Processes data with specific exception handling - */ - public void processWithSpecificCatch() { - try { - riskyOperation(); - } catch (IOException e) { - LOGGER.warning("I/O error during processing: " + e.getMessage()); - handleError(e); - } - } - - private void riskyOperation() throws IOException { - File file = new File("test.txt"); - if (!file.exists()) { - throw new IOException("File not found"); - } - } - - private void handleError(Exception e) { - LOGGER.severe("Error handled: " + e.getMessage()); - } - - /** - * Main entry point for the application. - * @param args command line arguments - */ - public static void main(String[] args) { - ValidAst obj = new ValidAst(); - obj.processWithSpecificCatch(); - int result = obj.calculate(1, 2, 3); - LOGGER.info("Result: " + result); - } -} diff --git a/tests/testdata/java/ast/code-policy.json b/tests/testdata/java/ast/code-policy.json deleted file mode 100644 index 147ed81..0000000 --- a/tests/testdata/java/ast/code-policy.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "version": "1.0.0", - "rules": [ - { - "id": "JAVA-AST-NO-SYSTEM-OUT", - "enabled": true, - "category": "error_handling", - "severity": "error", - "desc": "Avoid using System.out for logging", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "ast", - "language": "java", - "node": "MethodCallExpr", - "where": { - "scope": "System.out", - "name": "println" - } - }, - "message": "Use a proper logging framework instead of System.out" - }, - { - "id": "JAVA-AST-EMPTY-CATCH", - "enabled": true, - "category": "error_handling", - "severity": "error", - "desc": "Empty catch blocks are not allowed", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "ast", - "language": "java", - "node": "CatchClause", - "where": { - "body.statements.size": 0 - } - }, - "message": "Catch block must handle the exception or at least log it" - }, - { - "id": "JAVA-AST-GENERIC-EXCEPTION", - "enabled": true, - "category": "error_handling", - "severity": "error", - "desc": "Avoid catching generic Exception", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "ast", - "language": "java", - "node": "CatchClause", - "where": { - "parameter.type.name": "Exception" - } - }, - "message": "Catch specific exception types instead of generic Exception" - }, - { - "id": "JAVA-AST-MISSING-JAVADOC", - "enabled": true, - "category": "documentation", - "severity": "error", - "desc": "Public methods must have Javadoc", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "ast", - "language": "java", - "node": "MethodDeclaration", - "where": { - "isPublic": true, - "hasJavadoc": false - } - }, - "message": "Public methods must have Javadoc documentation" - } - ], - "enforce": { - "stages": ["pre-commit"], - "fail_on": ["error"] - } -} diff --git a/tests/testdata/java/length/LengthViolations.java b/tests/testdata/java/length/LengthViolations.java deleted file mode 100644 index ef9b0f2..0000000 --- a/tests/testdata/java/length/LengthViolations.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Test file with length violations - * Contains violations for line length, method length, and parameter count - */ -package com.example; - -public class LengthViolations { - - // VIOLATION: Line length exceeds 100 characters - private static final String VERY_LONG_CONSTANT_NAME_THAT_EXCEEDS_THE_MAXIMUM_LINE_LENGTH_AND_SHOULD_BE_FLAGGED = "test-value"; - - // VIOLATION: Too many parameters (more than 4) - public String processData(String firstName, String lastName, String email, String phone, String address, String city) { - return firstName + " " + lastName + " - " + email; - } - - // VIOLATION: Method is too long (more than 50 lines) - public void veryLongMethod() { - int line1 = 1; - int line2 = 2; - int line3 = 3; - int line4 = 4; - int line5 = 5; - int line6 = 6; - int line7 = 7; - int line8 = 8; - int line9 = 9; - int line10 = 10; - int line11 = 11; - int line12 = 12; - int line13 = 13; - int line14 = 14; - int line15 = 15; - int line16 = 16; - int line17 = 17; - int line18 = 18; - int line19 = 19; - int line20 = 20; - int line21 = 21; - int line22 = 22; - int line23 = 23; - int line24 = 24; - int line25 = 25; - int line26 = 26; - int line27 = 27; - int line28 = 28; - int line29 = 29; - int line30 = 30; - int line31 = 31; - int line32 = 32; - int line33 = 33; - int line34 = 34; - int line35 = 35; - int line36 = 36; - int line37 = 37; - int line38 = 38; - int line39 = 39; - int line40 = 40; - int line41 = 41; - int line42 = 42; - int line43 = 43; - int line44 = 44; - int line45 = 45; - int line46 = 46; - int line47 = 47; - int line48 = 48; - int line49 = 49; - int line50 = 50; - int line51 = 51; - System.out.println("Line: " + line51); - } - - public static void main(String[] args) { - LengthViolations obj = new LengthViolations(); - obj.veryLongMethod(); - } -} diff --git a/tests/testdata/java/length/ValidLength.java b/tests/testdata/java/length/ValidLength.java deleted file mode 100644 index c8e3996..0000000 --- a/tests/testdata/java/length/ValidLength.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Test file with valid length constraints - * All lines, methods, and parameter counts are within limits - */ -package com.example; - -public class ValidLength { - - private static final String CONFIG = "config-value"; - - // Correct: 4 parameters or fewer - public String formatUser(String name, String email, String role) { - return name + " (" + email + ") - " + role; - } - - // Correct: Short, focused method - public int add(int a, int b) { - return a + b; - } - - // Correct: Method within reasonable length - public void processRequest() { - String input = readInput(); - String validated = validate(input); - String result = transform(validated); - save(result); - } - - private String readInput() { - return "input"; - } - - private String validate(String input) { - if (input == null || input.isEmpty()) { - throw new IllegalArgumentException("Invalid input"); - } - return input; - } - - private String transform(String data) { - return data.toUpperCase(); - } - - private void save(String data) { - System.out.println("Saved: " + data); - } - - public static void main(String[] args) { - ValidLength obj = new ValidLength(); - obj.processRequest(); - } -} diff --git a/tests/testdata/java/length/code-policy.json b/tests/testdata/java/length/code-policy.json deleted file mode 100644 index 42dd961..0000000 --- a/tests/testdata/java/length/code-policy.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "version": "1.0.0", - "rules": [ - { - "id": "JAVA-LENGTH-MAX-LINE", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Line length must not exceed 100 characters", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "length", - "language": "java", - "scope": "line", - "max": 100 - }, - "message": "Line length must not exceed 100 characters" - }, - { - "id": "JAVA-LENGTH-MAX-METHOD", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Method length must not exceed 50 lines", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "length", - "language": "java", - "scope": "method", - "max": 50 - }, - "message": "Method length must not exceed 50 lines" - }, - { - "id": "JAVA-LENGTH-MAX-PARAMS", - "enabled": true, - "category": "formatting", - "severity": "warning", - "desc": "Methods should have at most 4 parameters", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "length", - "language": "java", - "scope": "params", - "max": 4 - }, - "message": "Methods should have at most 4 parameters" - } - ], - "enforce": { - "stages": ["pre-commit"], - "fail_on": ["error"] - } -} diff --git a/tests/testdata/java/pattern/NamingViolations.java b/tests/testdata/java/pattern/NamingViolations.java deleted file mode 100644 index e021999..0000000 --- a/tests/testdata/java/pattern/NamingViolations.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Test file with naming convention violations - * Violates Checkstyle naming rules - */ -package com.example; - -// VIOLATION: Class name should start with uppercase (PascalCase) -public class invalidClassName { - - // VIOLATION: Constant should be UPPER_SNAKE_CASE - private static final String apiKey = "sk-1234567890"; - - // VIOLATION: Variable name using UPPER_SNAKE_CASE (should be camelCase) - private int BAD_VARIABLE = 100; - - // VIOLATION: Method name starts with uppercase (should be camelCase) - public void BadMethod() { - System.out.println("This method has bad naming"); - } - - // VIOLATION: Method parameter uses snake_case - public String processData(String user_name) { - return "Hello " + user_name; - } - - // VIOLATION: Multiple naming issues - public static void MAIN(String[] args) { - invalidClassName obj = new invalidClassName(); - obj.BadMethod(); - } -} diff --git a/tests/testdata/java/pattern/ValidNaming.java b/tests/testdata/java/pattern/ValidNaming.java deleted file mode 100644 index 995cfec..0000000 --- a/tests/testdata/java/pattern/ValidNaming.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Test file with correct naming conventions - * Complies with Checkstyle naming rules - */ -package com.example; - -public class ValidNaming { - - // Correct: Constant in UPPER_SNAKE_CASE - private static final String API_KEY = "from-environment"; - - // Correct: Variable in camelCase - private int goodVariable = 100; - - // Correct: Method in camelCase - public void goodMethod() { - System.out.println("This method has good naming"); - } - - // Correct: Parameter in camelCase - public String processData(String userName) { - return "Hello " + userName; - } - - // Correct: Main method - public static void main(String[] args) { - ValidNaming obj = new ValidNaming(); - obj.goodMethod(); - } -} diff --git a/tests/testdata/java/pattern/code-policy.json b/tests/testdata/java/pattern/code-policy.json deleted file mode 100644 index 6f4a174..0000000 --- a/tests/testdata/java/pattern/code-policy.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "version": "1.0.0", - "rules": [ - { - "id": "JAVA-PATTERN-CLASS-PASCAL", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Class names must be PascalCase", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "pattern", - "language": "java", - "target": "TypeName", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - }, - "message": "Class names must be PascalCase (e.g., ValidNaming)" - }, - { - "id": "JAVA-PATTERN-METHOD-CAMEL", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Method names must be camelCase", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "pattern", - "language": "java", - "target": "MethodName", - "pattern": "^[a-z][a-zA-Z0-9]*$" - }, - "message": "Method names must be camelCase (e.g., goodMethod)" - }, - { - "id": "JAVA-PATTERN-VAR-CAMEL", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Variable names must be camelCase", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "pattern", - "language": "java", - "target": "MemberName", - "pattern": "^[a-z][a-zA-Z0-9]*$" - }, - "message": "Variable names must be camelCase (e.g., goodVariable)" - }, - { - "id": "JAVA-PATTERN-CONST-UPPER", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Constants must be UPPER_SNAKE_CASE", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "pattern", - "language": "java", - "target": "ConstantName", - "pattern": "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$" - }, - "message": "Constants must be UPPER_SNAKE_CASE (e.g., API_KEY)" - }, - { - "id": "JAVA-PATTERN-PARAM-CAMEL", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Parameter names must be camelCase", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "pattern", - "language": "java", - "target": "ParameterName", - "pattern": "^[a-z][a-zA-Z0-9]*$" - }, - "message": "Parameter names must be camelCase (e.g., userName)" - } - ], - "enforce": { - "stages": ["pre-commit"], - "fail_on": ["error"] - } -} diff --git a/tests/testdata/java/style/StyleViolations.java b/tests/testdata/java/style/StyleViolations.java deleted file mode 100644 index 97d8f7b..0000000 --- a/tests/testdata/java/style/StyleViolations.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Test file with style violations - * Contains violations for indentation, spacing, and formatting - */ -package com.example; - -public class StyleViolations { - -// VIOLATION: Missing indentation for class member -private String name; - - // VIOLATION: Inconsistent indentation - public void badIndentation() { - int x = 1; - int y = 2; - int z = 3; - System.out.println(x + y + z); - } - - // VIOLATION: Opening brace on next line (Java convention: same line) - public void badBracePlacement() - { - System.out.println("Bad brace placement"); - } - - // VIOLATION: Multiple statements on one line - public void multipleStatements() { int a = 1; int b = 2; System.out.println(a + b); } - - // VIOLATION: No space after if/for keywords - public void noSpaceAfterKeyword() { - if(true){ - for(int i=0;i<10;i++){ - System.out.println(i); - } - } - } - - // VIOLATION: Inconsistent spacing around operators - public int badOperatorSpacing() { - int result=10+20*30/5-2; - return result; - } - - // VIOLATION: Long line exceeding typical style guide limit - private static final String EXTREMELY_LONG_LINE_THAT_EXCEEDS_REASONABLE_LENGTH_LIMITS_AND_SHOULD_BE_WRAPPED_OR_REFACTORED = "value"; - - // VIOLATION: Missing blank line before method - public void missingBlankLine() { - System.out.println("No blank line above"); - } - public void anotherMethod() { - System.out.println("Methods too close together"); - } -} diff --git a/tests/testdata/java/style/ValidStyle.java b/tests/testdata/java/style/ValidStyle.java deleted file mode 100644 index 2b2b5db..0000000 --- a/tests/testdata/java/style/ValidStyle.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Test file with valid Java style - * Follows standard Java formatting conventions - */ -package com.example; - -public class ValidStyle { - - private String name; - private int value; - - public ValidStyle() { - this.name = "default"; - this.value = 0; - } - - public void properIndentation() { - int x = 1; - int y = 2; - int z = 3; - System.out.println(x + y + z); - } - - public void properBracePlacement() { - System.out.println("Correct brace placement"); - } - - public void properSpacing() { - if (true) { - for (int i = 0; i < 10; i++) { - System.out.println(i); - } - } - } - - public int properOperatorSpacing() { - int result = 10 + 20 * 30 / 5 - 2; - return result; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getValue() { - return value; - } - - public void setValue(int value) { - this.value = value; - } - - public static void main(String[] args) { - ValidStyle obj = new ValidStyle(); - obj.properIndentation(); - obj.properSpacing(); - System.out.println("Result: " + obj.properOperatorSpacing()); - } -} diff --git a/tests/testdata/java/style/code-policy.json b/tests/testdata/java/style/code-policy.json deleted file mode 100644 index afd629f..0000000 --- a/tests/testdata/java/style/code-policy.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "version": "1.0.0", - "rules": [ - { - "id": "JAVA-STYLE-INDENT", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Use consistent 4-space indentation", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "style", - "language": "java", - "indent": 4, - "indentStyle": "space" - }, - "message": "Use 4 spaces for indentation" - }, - { - "id": "JAVA-STYLE-BRACE-SAME-LINE", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Opening brace must be on same line", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "style", - "language": "java", - "braceStyle": "same-line" - }, - "message": "Opening brace should be on the same line" - }, - { - "id": "JAVA-STYLE-SPACE-AFTER-KEYWORD", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Require space after control keywords", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "style", - "language": "java", - "spaceAfterKeyword": true - }, - "message": "Add space after control keywords (if, for, while, etc.)" - }, - { - "id": "JAVA-STYLE-OPERATOR-SPACING", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Require spacing around operators", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "style", - "language": "java", - "spaceAroundOperators": true - }, - "message": "Add spaces around operators" - }, - { - "id": "JAVA-STYLE-MAX-LINE", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Maximum line length of 120 characters", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "style", - "language": "java", - "printWidth": 120 - }, - "message": "Line length must not exceed 120 characters" - }, - { - "id": "JAVA-STYLE-BLANK-LINE-SEPARATOR", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Require blank lines between methods", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "style", - "language": "java", - "blankLinesBetweenMethods": true - }, - "message": "Add blank line between method declarations" - }, - { - "id": "JAVA-STYLE-ONE-STATEMENT-PER-LINE", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Only one statement per line", - "when": { - "languages": ["java"] - }, - "check": { - "engine": "style", - "language": "java", - "oneStatementPerLine": true - }, - "message": "Place each statement on its own line" - } - ], - "enforce": { - "stages": ["pre-commit"], - "fail_on": ["error"] - } -} diff --git a/tests/testdata/javascript/ast/code-policy.json b/tests/testdata/javascript/ast/code-policy.json deleted file mode 100644 index aa5171d..0000000 --- a/tests/testdata/javascript/ast/code-policy.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "version": "1.0.0", - "rules": [ - { - "id": "JS-AST-CLASS-NAMING", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Class declarations must be PascalCase", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "pattern", - "language": "javascript", - "target": "identifier", - "context": "class", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - }, - "message": "Class names must be PascalCase" - }, - { - "id": "JS-AST-FUNCTION-NAMING", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Function declarations must be camelCase", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "pattern", - "language": "javascript", - "target": "identifier", - "context": "function", - "pattern": "^[a-z][a-zA-Z0-9]*$" - }, - "message": "Function names must be camelCase" - }, - { - "id": "JS-AST-VAR-NAMING", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Variable declarations must be camelCase", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "pattern", - "language": "javascript", - "target": "identifier", - "context": "variable", - "pattern": "^[a-z][a-zA-Z0-9]*$" - }, - "message": "Variable names must be camelCase" - } - ], - "enforce": { - "stages": ["pre-commit"], - "fail_on": ["error"] - } -} diff --git a/tests/testdata/javascript/ast/naming-violations.js b/tests/testdata/javascript/ast/naming-violations.js deleted file mode 100644 index 196df75..0000000 --- a/tests/testdata/javascript/ast/naming-violations.js +++ /dev/null @@ -1,29 +0,0 @@ -// File with naming convention violations - -// Bad: snake_case function name (should be camelCase) -function bad_function_name() { - return 'test'; -} - -// Bad: lowercase class name (should be PascalCase) -class lowercase_class { - constructor() { - this.value = 0; - } -} - -// Bad: variable with uppercase -var BAD_VARIABLE = 'test'; - -// Good examples for comparison -function goodFunctionName() { - return 'test'; -} - -class GoodClassName { - constructor() { - this.value = 0; - } -} - -const goodVariable = 'test'; diff --git a/tests/testdata/javascript/ast/valid.js b/tests/testdata/javascript/ast/valid.js deleted file mode 100644 index edebf0f..0000000 --- a/tests/testdata/javascript/ast/valid.js +++ /dev/null @@ -1,28 +0,0 @@ -// Valid JavaScript file for testing - -class Calculator { - constructor() { - this.result = 0; - } - - add(a, b) { - return a + b; - } - - subtract(a, b) { - return a - b; - } - - multiply(a, b) { - return a * b; - } - - divide(a, b) { - if (b === 0) { - throw new Error('Division by zero'); - } - return a / b; - } -} - -module.exports = Calculator; diff --git a/tests/testdata/javascript/length/code-policy.json b/tests/testdata/javascript/length/code-policy.json deleted file mode 100644 index a1487bc..0000000 --- a/tests/testdata/javascript/length/code-policy.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "version": "1.0.0", - "rules": [ - { - "id": "JS-LENGTH-MAX-LINE", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Line length must not exceed 100 characters", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "length", - "language": "javascript", - "scope": "line", - "max": 100 - }, - "message": "Line length must not exceed 100 characters" - }, - { - "id": "JS-LENGTH-MAX-FUNCTION", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Function length must not exceed 50 lines", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "length", - "language": "javascript", - "scope": "function", - "max": 50 - }, - "message": "Function length must not exceed 50 lines" - }, - { - "id": "JS-LENGTH-MAX-PARAMS", - "enabled": true, - "category": "formatting", - "severity": "warning", - "desc": "Functions should have at most 4 parameters", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "length", - "language": "javascript", - "scope": "params", - "max": 4 - }, - "message": "Functions should have at most 4 parameters" - } - ], - "enforce": { - "stages": ["pre-commit"], - "fail_on": ["error"] - } -} diff --git a/tests/testdata/javascript/length/length-violations.js b/tests/testdata/javascript/length/length-violations.js deleted file mode 100644 index 0ef2874..0000000 --- a/tests/testdata/javascript/length/length-violations.js +++ /dev/null @@ -1,65 +0,0 @@ -// File with length violations - -// Bad: line too long (over 100 characters) -const reallyLongVariableNameThatExceedsTheMaximumLineLengthSetByOurLinterConfigurationAndShouldBeReported = 'test'; - -// Bad: function with too many parameters (over 4) -function tooManyParameters(param1, param2, param3, param4, param5, param6) { - return param1 + param2 + param3 + param4 + param5 + param6; -} - -// Bad: function with too many lines (over 50) -function veryLongFunction() { - const line1 = 1; - const line2 = 2; - const line3 = 3; - const line4 = 4; - const line5 = 5; - const line6 = 6; - const line7 = 7; - const line8 = 8; - const line9 = 9; - const line10 = 10; - const line11 = 11; - const line12 = 12; - const line13 = 13; - const line14 = 14; - const line15 = 15; - const line16 = 16; - const line17 = 17; - const line18 = 18; - const line19 = 19; - const line20 = 20; - const line21 = 21; - const line22 = 22; - const line23 = 23; - const line24 = 24; - const line25 = 25; - const line26 = 26; - const line27 = 27; - const line28 = 28; - const line29 = 29; - const line30 = 30; - const line31 = 31; - const line32 = 32; - const line33 = 33; - const line34 = 34; - const line35 = 35; - const line36 = 36; - const line37 = 37; - const line38 = 38; - const line39 = 39; - const line40 = 40; - const line41 = 41; - const line42 = 42; - const line43 = 43; - const line44 = 44; - const line45 = 45; - const line46 = 46; - const line47 = 47; - const line48 = 48; - const line49 = 49; - const line50 = 50; - const line51 = 51; - return line51; -} diff --git a/tests/testdata/javascript/length/valid.js b/tests/testdata/javascript/length/valid.js deleted file mode 100644 index edebf0f..0000000 --- a/tests/testdata/javascript/length/valid.js +++ /dev/null @@ -1,28 +0,0 @@ -// Valid JavaScript file for testing - -class Calculator { - constructor() { - this.result = 0; - } - - add(a, b) { - return a + b; - } - - subtract(a, b) { - return a - b; - } - - multiply(a, b) { - return a * b; - } - - divide(a, b) { - if (b === 0) { - throw new Error('Division by zero'); - } - return a / b; - } -} - -module.exports = Calculator; diff --git a/tests/testdata/javascript/pattern/code-policy.json b/tests/testdata/javascript/pattern/code-policy.json deleted file mode 100644 index 2e29e08..0000000 --- a/tests/testdata/javascript/pattern/code-policy.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "version": "1.0.0", - "rules": [ - { - "id": "JS-PATTERN-CLASS-PASCAL", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Class names must be PascalCase", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "pattern", - "language": "javascript", - "target": "identifier", - "pattern": "^[A-Z][a-zA-Z0-9]*$", - "scope": "class" - }, - "message": "Class names must be PascalCase (e.g., Calculator)" - }, - { - "id": "JS-PATTERN-FUNCTION-CAMEL", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Function names must be camelCase", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "pattern", - "language": "javascript", - "target": "identifier", - "pattern": "^[a-z][a-zA-Z0-9]*$", - "scope": "function" - }, - "message": "Function names must be camelCase (e.g., goodFunctionName)" - }, - { - "id": "JS-PATTERN-VAR-CAMEL", - "enabled": true, - "category": "naming", - "severity": "error", - "desc": "Variable names must be camelCase", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "pattern", - "language": "javascript", - "target": "identifier", - "pattern": "^[a-z][a-zA-Z0-9]*$", - "scope": "variable" - }, - "message": "Variable names must be camelCase (e.g., goodVariable)" - }, - { - "id": "JS-PATTERN-NO-SECRETS", - "enabled": true, - "category": "security", - "severity": "error", - "desc": "No hardcoded secrets allowed", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "pattern", - "language": "javascript", - "target": "content", - "pattern": "(api[_-]?key|password|secret|token)\\s*=\\s*['\"][^'\"]+['\"]", - "flags": "i" - }, - "message": "Do not hardcode secrets. Use environment variables (process.env)" - } - ], - "enforce": { - "stages": ["pre-commit"], - "fail_on": ["error"] - } -} diff --git a/tests/testdata/javascript/pattern/naming-violations.js b/tests/testdata/javascript/pattern/naming-violations.js deleted file mode 100644 index 196df75..0000000 --- a/tests/testdata/javascript/pattern/naming-violations.js +++ /dev/null @@ -1,29 +0,0 @@ -// File with naming convention violations - -// Bad: snake_case function name (should be camelCase) -function bad_function_name() { - return 'test'; -} - -// Bad: lowercase class name (should be PascalCase) -class lowercase_class { - constructor() { - this.value = 0; - } -} - -// Bad: variable with uppercase -var BAD_VARIABLE = 'test'; - -// Good examples for comparison -function goodFunctionName() { - return 'test'; -} - -class GoodClassName { - constructor() { - this.value = 0; - } -} - -const goodVariable = 'test'; diff --git a/tests/testdata/javascript/pattern/security-violations.js b/tests/testdata/javascript/pattern/security-violations.js deleted file mode 100644 index 414ced5..0000000 --- a/tests/testdata/javascript/pattern/security-violations.js +++ /dev/null @@ -1,18 +0,0 @@ -// File with security violations - -// Bad: hardcoded API key -const API_KEY = 'sk-1234567890abcdef1234567890abcdef'; - -// Bad: hardcoded password -const password = 'mySecretPassword123'; - -// Bad: hardcoded secret -const client_secret = 'super-secret-value-12345'; - -// Bad: hardcoded token -const access_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'; - -// Good: using environment variables -const apiKey = process.env.API_KEY; -const userPassword = process.env.PASSWORD; -const clientSecret = process.env.CLIENT_SECRET; diff --git a/tests/testdata/javascript/pattern/valid.js b/tests/testdata/javascript/pattern/valid.js deleted file mode 100644 index edebf0f..0000000 --- a/tests/testdata/javascript/pattern/valid.js +++ /dev/null @@ -1,28 +0,0 @@ -// Valid JavaScript file for testing - -class Calculator { - constructor() { - this.result = 0; - } - - add(a, b) { - return a + b; - } - - subtract(a, b) { - return a - b; - } - - multiply(a, b) { - return a * b; - } - - divide(a, b) { - if (b === 0) { - throw new Error('Division by zero'); - } - return a / b; - } -} - -module.exports = Calculator; diff --git a/tests/testdata/javascript/style/code-policy.json b/tests/testdata/javascript/style/code-policy.json deleted file mode 100644 index 987ecc4..0000000 --- a/tests/testdata/javascript/style/code-policy.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "version": "1.0.0", - "rules": [ - { - "id": "JS-STYLE-INDENT", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Use consistent 2-space indentation", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "style", - "language": "javascript", - "indent": 2, - "indentStyle": "space" - }, - "message": "Use 2 spaces for indentation" - }, - { - "id": "JS-STYLE-QUOTES", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Use single quotes for strings", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "style", - "language": "javascript", - "quotes": "single" - }, - "message": "Use single quotes for strings" - }, - { - "id": "JS-STYLE-SEMI", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Require semicolons", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "style", - "language": "javascript", - "semi": true - }, - "message": "Statements must end with semicolons" - }, - { - "id": "JS-STYLE-MAX-LINE", - "enabled": true, - "category": "formatting", - "severity": "error", - "desc": "Maximum line length of 100 characters", - "when": { - "languages": ["javascript"] - }, - "check": { - "engine": "style", - "language": "javascript", - "printWidth": 100 - }, - "message": "Line length must not exceed 100 characters" - } - ], - "enforce": { - "stages": ["pre-commit"], - "fail_on": ["error"] - } -} diff --git a/tests/testdata/javascript/style/style-violations.js b/tests/testdata/javascript/style/style-violations.js deleted file mode 100644 index 73ab57c..0000000 --- a/tests/testdata/javascript/style/style-violations.js +++ /dev/null @@ -1,31 +0,0 @@ -// File with style violations - -// Bad: inconsistent indentation -function badIndentation() { -const x = 1; - const y = 2; - const z = 3; - return x + y + z; -} - -// Bad: double quotes (should be single) -const message = "Hello World"; - -// Bad: missing semicolons -const a = 1 -const b = 2 -const c = 3 - -// Bad: long line exceeding 100 characters -const veryLongLineHereThisIsWayTooLongAndShouldBeReportedByTheLinterAsAViolationOfTheLineLength = true; - -// Bad: multiple statements on one line -const x = 1; const y = 2; const z = 3; - -// Good examples -function goodIndentation() { - const x = 1; - const y = 2; - const z = 3; - return x + y + z; -} diff --git a/tests/testdata/javascript/style/valid.js b/tests/testdata/javascript/style/valid.js deleted file mode 100644 index edebf0f..0000000 --- a/tests/testdata/javascript/style/valid.js +++ /dev/null @@ -1,28 +0,0 @@ -// Valid JavaScript file for testing - -class Calculator { - constructor() { - this.result = 0; - } - - add(a, b) { - return a + b; - } - - subtract(a, b) { - return a - b; - } - - multiply(a, b) { - return a * b; - } - - divide(a, b) { - if (b === 0) { - throw new Error('Division by zero'); - } - return a / b; - } -} - -module.exports = Calculator; diff --git a/tests/testdata/test_violation.go b/tests/testdata/test_violation.go deleted file mode 100644 index 48c983b..0000000 --- a/tests/testdata/test_violation.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import "fmt" - -func main() { - // Hardcoded API key - should violate security rule - apiKey := "sk-1234567890abcdef" - fmt.Println(apiKey) -} diff --git a/tests/testdata/typescript/typechecker/code-policy.json b/tests/testdata/typescript/typechecker/code-policy.json deleted file mode 100644 index 12498ea..0000000 --- a/tests/testdata/typescript/typechecker/code-policy.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "version": "1.0.0", - "rules": [ - { - "id": "TS-TYPE-STRICT", - "enabled": true, - "category": "typing", - "severity": "error", - "desc": "Enable strict type checking", - "when": { - "languages": ["typescript"] - }, - "check": { - "engine": "typechecker", - "language": "typescript", - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true - }, - "message": "Strict type checking enabled - all type errors must be resolved" - }, - { - "id": "TS-TYPE-NO-IMPLICIT-ANY", - "enabled": true, - "category": "typing", - "severity": "error", - "desc": "Disallow implicit any types", - "when": { - "languages": ["typescript"] - }, - "check": { - "engine": "typechecker", - "language": "typescript", - "noImplicitAny": true - }, - "message": "Variables and parameters must have explicit type annotations" - }, - { - "id": "TS-TYPE-RETURN-TYPE", - "enabled": true, - "category": "typing", - "severity": "error", - "desc": "Functions must have explicit return types", - "when": { - "languages": ["typescript"] - }, - "check": { - "engine": "typechecker", - "language": "typescript", - "noImplicitReturns": true - }, - "message": "Functions must have explicit return type annotations" - } - ], - "enforce": { - "stages": ["pre-commit"], - "fail_on": ["error"] - } -} diff --git a/tests/testdata/typescript/typechecker/strict-mode-errors.ts b/tests/testdata/typescript/typechecker/strict-mode-errors.ts deleted file mode 100644 index e650f08..0000000 --- a/tests/testdata/typescript/typechecker/strict-mode-errors.ts +++ /dev/null @@ -1,29 +0,0 @@ -// File with strict mode violations - -// Error: Variable 'x' implicitly has an 'any' type -let x; -x = 10; -x = 'string'; - -// Error: Parameter 'input' implicitly has an 'any' type -function processInput(input) { - return input.toUpperCase(); -} - -// Error: Function lacks return type annotation -function calculate(a: number, b: number) { - return a + b; -} - -// Error: Object is possibly 'undefined' -function getUserName(user: { name?: string }) { - return user.name.toUpperCase(); // name might be undefined -} - -// Error: Not all code paths return a value -function getValue(condition: boolean): string { - if (condition) { - return 'yes'; - } - // Missing return for false case -} diff --git a/tests/testdata/typescript/typechecker/type-errors.ts b/tests/testdata/typescript/typechecker/type-errors.ts deleted file mode 100644 index 4ee1a68..0000000 --- a/tests/testdata/typescript/typechecker/type-errors.ts +++ /dev/null @@ -1,41 +0,0 @@ -// File with TypeScript type errors - -interface Person { - name: string; - age: number; -} - -// Error: Type 'string' is not assignable to type 'number' -const person: Person = { - name: 'John', - age: 'thirty' // Should be number -}; - -// Error: Property 'email' does not exist on type 'Person' -function printEmail(p: Person) { - console.log(p.email); // email doesn't exist -} - -// Error: Cannot find name 'undefinedVariable' -const result = undefinedVariable + 10; - -// Error: Argument of type 'number' is not assignable to parameter of type 'string' -function greet(name: string): string { - return `Hello, ${name}`; -} -greet(123); // Should be string - -// Error: Object is possibly 'null' -function getLength(str: string | null) { - return str.length; // str might be null -} - -// Error: 'this' implicitly has type 'any' -const obj = { - value: 10, - getValue: function() { - return function() { - return this.value; // 'this' has wrong context - }; - } -}; diff --git a/tests/testdata/typescript/typechecker/valid.ts b/tests/testdata/typescript/typechecker/valid.ts deleted file mode 100644 index 02eb210..0000000 --- a/tests/testdata/typescript/typechecker/valid.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Valid TypeScript file for testing - -interface User { - id: number; - name: string; - email: string; -} - -class UserService { - private users: User[] = []; - - addUser(user: User): void { - this.users.push(user); - } - - getUser(id: number): User | undefined { - return this.users.find(u => u.id === id); - } - - getAllUsers(): User[] { - return [...this.users]; - } - - removeUser(id: number): boolean { - const index = this.users.findIndex(u => u.id === id); - if (index !== -1) { - this.users.splice(index, 1); - return true; - } - return false; - } -} - -export { User, UserService }; diff --git a/tests/testdata/user-policy-example.json b/tests/testdata/user-policy-example.json deleted file mode 100644 index 3e5cb0d..0000000 --- a/tests/testdata/user-policy-example.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "version": "1.0.0", - "defaults": { - "severity": "error", - "autofix": false - }, - "rules": [ - { - "id": "naming-class-pascalcase", - "say": "Class names must be PascalCase", - "category": "naming", - "languages": ["javascript", "typescript", "java"], - "params": { - "case": "PascalCase" - } - }, - { - "id": "length-max-line", - "say": "Maximum line length is 100 characters", - "category": "length", - "params": { - "max": 100 - } - }, - { - "id": "style-indent", - "say": "Use 4 spaces for indentation", - "category": "style", - "languages": ["javascript", "typescript", "java"], - "params": { - "indent": 4 - } - }, - { - "id": "security-no-hardcoded-secrets", - "say": "Do not hardcode secrets, API keys, or passwords", - "category": "security", - "severity": "error" - }, - { - "id": "complexity-max", - "say": "Maximum cyclomatic complexity is 10", - "category": "complexity", - "languages": ["javascript", "typescript", "java"], - "params": { - "complexity": 10 - } - } - ] -} From 36ff9d15ae80ef3e0d863aae8624cfeffa827511 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 03:57:08 +0000 Subject: [PATCH 23/26] docs: remove emojis, translate to Korean, fix outdated references - README.md: remove all emojis, remove deleted testdata/engine references - AGENTS.md: complete incomplete final sentence - docs/CONVERT_FEATURE.md: translate to Korean - docs/CONVERT_USAGE.md: translate to Korean, remove deleted testdata reference - docs/LINTER_VALIDATION.md: translate to Korean, remove deleted testdata references - tests/TESTING_GUIDE.md: remove deleted testdata section --- AGENTS.md | 2 +- README.md | 60 +++--- docs/CONVERT_FEATURE.md | 384 ++++++++++++++------------------------ docs/CONVERT_USAGE.md | 295 +++++++++++++++-------------- docs/LINTER_VALIDATION.md | 88 +++------ tests/TESTING_GUIDE.md | 55 ------ 6 files changed, 338 insertions(+), 546 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fe2face..c2e9384 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -119,4 +119,4 @@ - ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋Š” ํŒŒ์ผ์—๋„ ์ด ๋ฌธ์„œ์˜ ์ง€์นจ์„ ๋”ฐ๋ฅด์„ธ์š”. ## ํ…Œ์ŠคํŠธ -ํ…Œ์ŠคํŠธ๋Š” ๋ฐ˜๋“œ์‹œ ์ž‘์„ฑํ•ด์•ผ ํ•˜๊ณ  \ No newline at end of file +ํ…Œ์ŠคํŠธ๋Š” ๋ฐ˜๋“œ์‹œ ์ž‘์„ฑํ•ด์•ผ ํ•˜๊ณ , ๋ณ€๊ฒฝ๋œ ์ฝ”๋“œ์˜ ๋ผ์ธ ์ปค๋ฒ„๋ฆฌ์ง€ 80% ์ด์ƒ์„ ๋ชฉํ‘œ๋กœ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. \ No newline at end of file diff --git a/README.md b/README.md index 0f88cd6..d8ff67e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ๐ŸŽต Symphony (sym) +# Symphony (sym) GitHub Repository Role & Policy Management Tool with Code Convention Validation @@ -7,18 +7,18 @@ Symphony๋Š” GitHub OAuth ์ธ์ฆ์„ ํ†ตํ•œ ์—ญํ•  ๊ธฐ๋ฐ˜ ํŒŒ์ผ ์ ‘๊ทผ ๊ถŒํ•œ ๋ฐ ## ๊ฐœ์š” -> **โœจ ๋น ๋ฅธ ์‹œ์ž‘:** `sym login` ํ•œ ๋ฒˆ์ด๋ฉด ๋! OAuth App ์„ค์ • ๋ถˆํ•„์š”. +> **๋น ๋ฅธ ์‹œ์ž‘:** `sym login` ํ•œ ๋ฒˆ์ด๋ฉด ๋! OAuth App ์„ค์ • ๋ถˆํ•„์š”. -## โœจ ์ฃผ์š” ๊ธฐ๋Šฅ +## ์ฃผ์š” ๊ธฐ๋Šฅ -### ๐Ÿ” ์—ญํ•  ๋ฐ ๊ถŒํ•œ ๊ด€๋ฆฌ +### ์—ญํ•  ๋ฐ ๊ถŒํ•œ ๊ด€๋ฆฌ - **CLI ์ธํ„ฐํŽ˜์ด์Šค**: ์‚ฌ์šฉ์ž ์—ญํ•  ๋ฐ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ •๋ณด ๋น ๋ฅธ ์กฐํšŒ - **์›น ๋Œ€์‹œ๋ณด๋“œ**: ๊ถŒํ•œ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์‹œ๊ฐ์  ์ธํ„ฐํŽ˜์ด์Šค (ํฌํŠธ 8787) - **OAuth ์ธ์ฆ**: ์•ˆ์ „ํ•œ GitHub/GHES ์ธ์ฆ - **๋™์  ์—ญํ•  ์‹œ์Šคํ…œ**: ์ปค์Šคํ…€ ์—ญํ•  ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ - **JSON API**: ์Šคํฌ๋ฆฝํŒ…์„ ์œ„ํ•œ ๊ธฐ๊ณ„ ํŒ๋… ๊ฐ€๋Šฅ ์ถœ๋ ฅ -### ๐Ÿ“ ์ •์ฑ… ํŽธ์ง‘๊ธฐ (Policy Editor) +### ์ •์ฑ… ํŽธ์ง‘๊ธฐ (Policy Editor) - **RBAC (Role-Based Access Control)**: ์—ญํ• ๋ณ„ ํŒŒ์ผ ์ ‘๊ทผ ๊ถŒํ•œ ๋ฐ ์‹œ์Šคํ…œ ๊ถŒํ•œ ์„ค์ • - **์ฝ”๋”ฉ ๊ทœ์น™ ๊ด€๋ฆฌ**: ํ”„๋กœ์ ํŠธ๋ณ„ ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜ ๋ฐ ์ •์ฑ… ์ •์˜ - **ํ…œํ”Œ๋ฆฟ ์‹œ์Šคํ…œ**: React, Vue, Node.js, Python, Go, TypeScript ํ…œํ”Œ๋ฆฟ ์ œ๊ณต @@ -34,21 +34,21 @@ Symphony๋Š” GitHub OAuth ์ธ์ฆ์„ ํ†ตํ•œ ์—ญํ•  ๊ธฐ๋ฐ˜ ํŒŒ์ผ ์ ‘๊ทผ ๊ถŒํ•œ ๋ฐ - JSON ์ถœ๋ ฅ์„ ํ†ตํ•œ LLM ๋„๊ตฌ ์—ฐ๋™ - ์ปจํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ์ปจ๋ฒค์…˜ ์ถ”์ถœ -### ๐Ÿ” ์ฝ”๋“œ ์ปจ๋ฒค์…˜ ๊ฒ€์ฆ (๊ฐœ๋ฐœ ์ค‘) +### ์ฝ”๋“œ ์ปจ๋ฒค์…˜ ๊ฒ€์ฆ (๊ฐœ๋ฐœ ์ค‘) - **์ž์—ฐ์–ด ๊ธฐ๋ฐ˜ ์ปจ๋ฒค์…˜ ์ •์˜**: `.sym/user-policy.json`์— ์ž์—ฐ์–ด๋กœ ๊ทœ์น™ ์ž‘์„ฑ - **์Šคํ‚ค๋งˆ ๋ณ€ํ™˜**: A ์Šคํ‚ค๋งˆ (์‚ฌ์šฉ์ž ์ž…๋ ฅ) โ†’ B ์Šคํ‚ค๋งˆ (๊ฒ€์ฆ ์—”์ง„์šฉ) - **๋‹ค์ค‘ ๊ฒ€์ฆ ์—”์ง„**: Pattern, Length, Style, AST ์—”์ง„ ์ง€์› - **LLM ๋„๊ตฌ ์—ฐ๋™**: JSON ์ถœ๋ ฅ์„ ํ†ตํ•œ AI ์ฝ”๋”ฉ ๋„๊ตฌ ์—ฐ๋™ - **์ปจํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ์ถ”์ถœ**: ์ž‘์—… ์ปจํ…์ŠคํŠธ์— ๋งž๋Š” ์ปจ๋ฒค์…˜๋งŒ ์ถ”์ถœ -### ๐Ÿ› ๏ธ ๊ธฐ์ˆ  ์Šคํƒ +### ๊ธฐ์ˆ  ์Šคํƒ - **๋‹จ์ผ ๋ฐ”์ด๋„ˆ๋ฆฌ**: ๋Ÿฐํƒ€์ž„ ์˜์กด์„ฑ ๋ถˆํ•„์š” - **์ž„๋ฒ ๋””๋“œ ์—์…‹**: go:embed๋ฅผ ํ†ตํ•œ ์ •์  ํŒŒ์ผ ๋‚ด์žฅ (HTML, CSS, JS, SVG) - **ํ”„๋กœ๋•์…˜ CSS**: Tailwind CSS ๋นŒ๋“œ ์‹œ์Šคํ…œ - **๋ฉ€ํ‹ฐํ”Œ๋žซํผ**: Windows, macOS (Intel/ARM), Linux (AMD64/ARM64) ์ง€์› - **UTF-8 ์ง€์›**: ํ•œ๊ธ€ ๋ฐ ์ด๋ชจํ‹ฐ์ฝ˜ ์™„๋ฒฝ ์ง€์› -## ๐Ÿ“ฆ ์„ค์น˜ +## ์„ค์น˜ ### MCP ์„œ๋ฒ„๋กœ ์„ค์น˜ (๊ถŒ์žฅ - AI ์ฝ”๋”ฉ ๋„๊ตฌ) @@ -137,7 +137,7 @@ go install github.com/DevSymphony/sym-cli/cmd/sym@latest [System.Environment]::SetEnvironmentVariable('Path', $env:Path + ';D:\Git\sym-cli\bin', 'User') ``` -## ๐Ÿš€ ๋น ๋ฅธ ์‹œ์ž‘ +## ๋น ๋ฅธ ์‹œ์ž‘ ### MCP ์„œ๋ฒ„ ๋ชจ๋“œ (AI ์ฝ”๋”ฉ ๋„๊ตฌ์™€ ํ•จ๊ป˜) @@ -253,7 +253,7 @@ sym policy validate sym policy history ``` -## ๐Ÿ“ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ +## ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ``` sym-cli/ @@ -269,8 +269,7 @@ sym-cli/ โ”‚ โ”‚ โ”œโ”€โ”€ whoami.go # ์‚ฌ์šฉ์ž ์ •๋ณด โ”‚ โ”‚ โ”œโ”€โ”€ policy.go # ์ •์ฑ… ๊ด€๋ฆฌ โ”‚ โ”‚ โ”œโ”€โ”€ convert.go # ์Šคํ‚ค๋งˆ ๋ณ€ํ™˜ (๊ฐœ๋ฐœ ์ค‘) -โ”‚ โ”‚ โ”œโ”€โ”€ validate.go # ์ฝ”๋“œ ๊ฒ€์ฆ (๊ฐœ๋ฐœ ์ค‘) -โ”‚ โ”‚ โ””โ”€โ”€ export.go # ์ปจ๋ฒค์…˜ ๋‚ด๋ณด๋‚ด๊ธฐ (๊ฐœ๋ฐœ ์ค‘) +โ”‚ โ”‚ โ””โ”€โ”€ validate.go # ์ฝ”๋“œ ๊ฒ€์ฆ (๊ฐœ๋ฐœ ์ค‘) โ”‚ โ”œโ”€โ”€ auth/ # OAuth ์ธ์ฆ โ”‚ โ”œโ”€โ”€ config/ # ์„ค์ • ๊ด€๋ฆฌ โ”‚ โ”œโ”€โ”€ git/ # Git ์œ ํ‹ธ๋ฆฌํ‹ฐ @@ -281,25 +280,16 @@ sym-cli/ โ”‚ โ”‚ โ””โ”€โ”€ static/ # HTML, CSS, JS (์ž„๋ฒ ๋””๋“œ) โ”‚ โ”œโ”€โ”€ validator/ # ๊ฒ€์ฆ ๋กœ์ง (๊ฐœ๋ฐœ ์ค‘) โ”‚ โ”œโ”€โ”€ converter/ # ์Šคํ‚ค๋งˆ ๋ณ€ํ™˜ (๊ฐœ๋ฐœ ์ค‘) -โ”‚ โ”œโ”€โ”€ engine/ # ๊ฒ€์ฆ ์—”์ง„ -โ”‚ โ”‚ โ”œโ”€โ”€ pattern/ # ํŒจํ„ด ๊ฒ€์ฆ ์—”์ง„ -โ”‚ โ”‚ โ”œโ”€โ”€ length/ # ๊ธธ์ด ๊ฒ€์ฆ ์—”์ง„ -โ”‚ โ”‚ โ”œโ”€โ”€ style/ # ์Šคํƒ€์ผ ๊ฒ€์ฆ ์—”์ง„ -โ”‚ โ”‚ โ””โ”€โ”€ ast/ # AST ๊ฒ€์ฆ ์—”์ง„ โ”‚ โ””โ”€โ”€ adapter/ # ESLint, Prettier ์–ด๋Œ‘ํ„ฐ โ”œโ”€โ”€ pkg/ โ”‚ โ””โ”€โ”€ schema/ # ์Šคํ‚ค๋งˆ ํƒ€์ž… ์ •์˜ โ”œโ”€โ”€ tests/ # ํ…Œ์ŠคํŠธ -โ”‚ โ””โ”€โ”€ testdata/ # ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ -โ”‚ โ”œโ”€โ”€ javascript/ # JavaScript ํ…Œ์ŠคํŠธ ํŒŒ์ผ -โ”‚ โ”œโ”€โ”€ typescript/ # TypeScript ํ…Œ์ŠคํŠธ ํŒŒ์ผ -โ”‚ โ””โ”€โ”€ java/ # Java ํ…Œ์ŠคํŠธ ํŒŒ์ผ โ”œโ”€โ”€ .sym/ # ์ •์ฑ… ๋ฐ ์—ญํ•  ํŒŒ์ผ (gitignore) โ”œโ”€โ”€ Makefile โ””โ”€โ”€ README.md ``` -## ๐Ÿ”ง ๊ฐœ๋ฐœ +## ๊ฐœ๋ฐœ ### ํ•„์ˆ˜ ๋„๊ตฌ @@ -360,20 +350,12 @@ make build-css make test # ํŠน์ • ํŒจํ‚ค์ง€ ํ…Œ์ŠคํŠธ -go test ./internal/engine/pattern/... -v +go test ./internal/adapter/... -v -# ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ (JavaScript, TypeScript, Java) +# ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ go test ./tests/integration/... -v ``` -**ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ**: -- `tests/testdata/javascript/`: JavaScript ์—”์ง„ ํ…Œ์ŠคํŠธ (pattern, length, style, ast) -- `tests/testdata/typescript/`: TypeScript ํƒ€์ž…์ฒด์ปค ํ…Œ์ŠคํŠธ -- `tests/testdata/java/`: Java ์—”์ง„ ํ…Œ์ŠคํŠธ (Checkstyle, PMD ๊ฒ€์ฆ) - -๊ฐ ๋””๋ ‰ํ† ๋ฆฌ๋Š” ์œ„๋ฐ˜ ์ผ€์ด์Šค์™€ ์ •์ƒ ์ผ€์ด์Šค๋ฅผ ํฌํ•จํ•˜์—ฌ ๊ฒ€์ฆ ์—”์ง„์˜ ์ •ํ™•์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. -์ž์„ธํ•œ ๋‚ด์šฉ์€ [tests/testdata/README.md](tests/testdata/README.md)๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. - ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ๋ฆฌํฌํŠธ๋Š” [์—ฌ๊ธฐ](https://devsymphony.github.io/sym-cli/coverage.html)์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ### ์ฝ”๋“œ ํ’ˆ์งˆ @@ -409,7 +391,7 @@ npm --version # npm ์„ค์น˜ ํ™•์ธ cd ~/.symphony/tools && npm install eslint@^8.0.0 prettier@latest ``` -## ๐Ÿ“‹ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ +## ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ```bash # ์ธ์ฆ ๋ชจ๋“œ (๊ธฐ๋ณธ๊ฐ’: server) @@ -419,7 +401,7 @@ export SYM_AUTH_MODE=server export SYM_SERVER_URL=https://symphony-server-98207.web.app ``` -## ๐Ÿ—‚๏ธ ํŒŒ์ผ ๋ฐ ๋””๋ ‰ํ† ๋ฆฌ +## ํŒŒ์ผ ๋ฐ ๋””๋ ‰ํ† ๋ฆฌ ### ์„ค์ • ํŒŒ์ผ ์œ„์น˜ @@ -474,7 +456,7 @@ export SYM_SERVER_URL=https://symphony-server-98207.web.app } ``` -## ๐ŸŽฏ ์‚ฌ์šฉ ์‚ฌ๋ก€ +## ์‚ฌ์šฉ ์‚ฌ๋ก€ ### ํŒ€ ํ˜‘์—… ์‹œ๋‚˜๋ฆฌ์˜ค @@ -494,15 +476,15 @@ export SYM_SERVER_URL=https://symphony-server-98207.web.app 6. ์ž๋™ ์ €์žฅ ํ™œ์„ฑํ™” (30์ดˆ๋งˆ๋‹ค) 7. Git ์ปค๋ฐ‹ ๋ฐ ํ‘ธ์‹œ -## ๐Ÿ“ ๋ผ์ด์„ ์Šค +## ๋ผ์ด์„ ์Šค MIT License -## ๐Ÿค ๊ธฐ์—ฌ +## ๊ธฐ์—ฌ Contributions are welcome! Please feel free to submit a Pull Request. -## ๐Ÿ“ž ์ง€์› +## ์ง€์› - GitHub Issues: [https://github.com/DevSymphony/sym-cli/issues](https://github.com/DevSymphony/sym-cli/issues) @@ -510,7 +492,7 @@ Contributions are welcome! Please feel free to submit a Pull Request. **Note:** ์ฝ”๋“œ ๊ฒ€์ฆ ๊ธฐ๋Šฅ (`convert`, `validate`, `export`)์€ ํ˜„์žฌ ๊ฐœ๋ฐœ ์ค‘์ž…๋‹ˆ๋‹ค. -## ๐Ÿ“Š ํŒจํ‚ค์ง€ ๊ตฌ์กฐ ๋ฐ ์˜์กด์„ฑ +## ํŒจํ‚ค์ง€ ๊ตฌ์กฐ ๋ฐ ์˜์กด์„ฑ ```mermaid graph TB diff --git a/docs/CONVERT_FEATURE.md b/docs/CONVERT_FEATURE.md index 1916700..23ece53 100644 --- a/docs/CONVERT_FEATURE.md +++ b/docs/CONVERT_FEATURE.md @@ -1,121 +1,121 @@ -# Convert Feature - Multi-Target Linter Configuration Generator +# Convert ๊ธฐ๋Šฅ - ๋‹ค์ค‘ ํƒ€๊ฒŸ Linter ์„ค์ • ์ƒ์„ฑ๊ธฐ -## Overview +## ๊ฐœ์š” -The enhanced `convert` command transforms natural language coding conventions into linter-specific configuration files using LLM-powered inference. +`convert` ๋ช…๋ น์–ด๋Š” ์ž์—ฐ์–ด ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜์„ LLM ๊ธฐ๋ฐ˜ ์ถ”๋ก ์„ ํ†ตํ•ด linter๋ณ„ ์„ค์ • ํŒŒ์ผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. -## Features +## ๊ธฐ๋Šฅ -- **LLM-Powered Inference**: Uses OpenAI API to intelligently analyze natural language rules -- **Multi-Target Support**: Generates configurations for multiple linters simultaneously +- **LLM ๊ธฐ๋ฐ˜ ์ถ”๋ก **: OpenAI API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž์—ฐ์–ด ๊ทœ์น™์„ ๋ถ„์„ +- **๋‹ค์ค‘ ํƒ€๊ฒŸ ์ง€์›**: ์—ฌ๋Ÿฌ linter ์„ค์ •์„ ๋™์‹œ์— ์ƒ์„ฑ - **ESLint** (JavaScript/TypeScript) - **Checkstyle** (Java) - **PMD** (Java) -- **Fallback Mechanism**: Pattern-based inference when LLM is unavailable -- **Confidence Scoring**: Tracks inference confidence and warns on low-confidence conversions -- **1:N Rule Mapping**: One user rule can generate multiple linter rules -- **Caching**: Minimizes API calls by caching inference results +- **ํด๋ฐฑ ๋ฉ”์ปค๋‹ˆ์ฆ˜**: LLM ์‚ฌ์šฉ ๋ถˆ๊ฐ€ ์‹œ ํŒจํ„ด ๊ธฐ๋ฐ˜ ์ถ”๋ก  +- **์‹ ๋ขฐ๋„ ์ ์ˆ˜**: ์ถ”๋ก  ์‹ ๋ขฐ๋„๋ฅผ ์ถ”์ ํ•˜๊ณ  ๋‚ฎ์€ ์‹ ๋ขฐ๋„์— ๋Œ€ํ•ด ๊ฒฝ๊ณ  +- **1:N ๊ทœ์น™ ๋งคํ•‘**: ํ•˜๋‚˜์˜ ์‚ฌ์šฉ์ž ๊ทœ์น™์ด ์—ฌ๋Ÿฌ linter ๊ทœ์น™์„ ์ƒ์„ฑ +- **์บ์‹ฑ**: ์ถ”๋ก  ๊ฒฐ๊ณผ๋ฅผ ์บ์‹ฑํ•˜์—ฌ API ํ˜ธ์ถœ ์ตœ์†Œํ™” -## Architecture +## ์•„ํ‚คํ…์ฒ˜ ``` User Policy (user-policy.json) - โ†“ + | Converter - โ†“ - LLM Inference (OpenAI API) โ† Fallback (Pattern Matching) - โ†“ + | + LLM Inference (OpenAI API) <- Fallback (Pattern Matching) + | Rule Intent Detection - โ†“ + | Linter Converters - โ”œโ”€โ”€ ESLint Converter โ†’ .eslintrc.json - โ”œโ”€โ”€ Checkstyle Converter โ†’ checkstyle.xml - โ””โ”€โ”€ PMD Converter โ†’ pmd-ruleset.xml + +-- ESLint Converter -> .eslintrc.json + +-- Checkstyle Converter -> checkstyle.xml + +-- PMD Converter -> pmd-ruleset.xml ``` -### Package Structure +### ํŒจํ‚ค์ง€ ๊ตฌ์กฐ ``` internal/ -โ”œโ”€โ”€ llm/ -โ”‚ โ”œโ”€โ”€ client.go # OpenAI API client -โ”‚ โ”œโ”€โ”€ inference.go # Rule inference engine -โ”‚ โ””โ”€โ”€ types.go # Intent and result types -โ”œโ”€โ”€ converter/ -โ”‚ โ”œโ”€โ”€ converter.go # Main conversion logic -โ”‚ โ””โ”€โ”€ linters/ -โ”‚ โ”œโ”€โ”€ converter.go # Linter converter interface -โ”‚ โ”œโ”€โ”€ registry.go # Converter registry -โ”‚ โ”œโ”€โ”€ eslint.go # ESLint converter -โ”‚ โ”œโ”€โ”€ checkstyle.go # Checkstyle converter -โ”‚ โ””โ”€โ”€ pmd.go # PMD converter -โ””โ”€โ”€ cmd/ - โ””โ”€โ”€ convert.go # CLI command ++-- llm/ +| +-- client.go # OpenAI API ํด๋ผ์ด์–ธํŠธ +| +-- inference.go # ๊ทœ์น™ ์ถ”๋ก  ์—”์ง„ +| +-- types.go # Intent ๋ฐ ๊ฒฐ๊ณผ ํƒ€์ž… ++-- converter/ +| +-- converter.go # ๋ฉ”์ธ ๋ณ€ํ™˜ ๋กœ์ง +| +-- linters/ +| +-- converter.go # Linter ๋ณ€ํ™˜๊ธฐ ์ธํ„ฐํŽ˜์ด์Šค +| +-- registry.go # ๋ณ€ํ™˜๊ธฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ +| +-- eslint.go # ESLint ๋ณ€ํ™˜๊ธฐ +| +-- checkstyle.go # Checkstyle ๋ณ€ํ™˜๊ธฐ +| +-- pmd.go # PMD ๋ณ€ํ™˜๊ธฐ ++-- cmd/ + +-- convert.go # CLI ๋ช…๋ น์–ด ``` -## Usage +## ์‚ฌ์šฉ๋ฒ• -### Basic Usage +### ๊ธฐ๋ณธ ์‚ฌ์šฉ ```bash -# Convert to all supported linters +# ๋ชจ๋“  ์ง€์› linter๋กœ ๋ณ€ํ™˜ sym convert -i user-policy.json --targets all --output-dir .linters -# Convert only for JavaScript/TypeScript +# JavaScript/TypeScript๋งŒ sym convert -i user-policy.json --targets eslint --output-dir .linters -# Convert for Java +# Java๋งŒ sym convert -i user-policy.json --targets checkstyle,pmd --output-dir .linters ``` -### Advanced Options +### ๊ณ ๊ธ‰ ์˜ต์…˜ ```bash -# Use specific OpenAI model +# ํŠน์ • OpenAI ๋ชจ๋ธ ์‚ฌ์šฉ sym convert -i user-policy.json \ --targets all \ --output-dir .linters \ --openai-model gpt-4o -# Adjust confidence threshold +# ์‹ ๋ขฐ๋„ ์ž„๊ณ„๊ฐ’ ์กฐ์ • sym convert -i user-policy.json \ --targets eslint \ --output-dir .linters \ --confidence-threshold 0.8 -# Enable verbose output +# ์ƒ์„ธ ์ถœ๋ ฅ ํ™œ์„ฑํ™” sym convert -i user-policy.json \ --targets all \ --output-dir .linters \ --verbose ``` -### Legacy Mode +### ๋ ˆ๊ฑฐ์‹œ ๋ชจ๋“œ ```bash -# Generate only internal code-policy.json (no linter configs) +# ๋‚ด๋ถ€ code-policy.json๋งŒ ์ƒ์„ฑ (linter ์„ค์ • ์—†์Œ) sym convert -i user-policy.json -o code-policy.json ``` -## Configuration +## ์„ค์ • -### Environment Variables +### ํ™˜๊ฒฝ ๋ณ€์ˆ˜ -- `OPENAI_API_KEY`: OpenAI API key (required for LLM inference) - - If not set, fallback pattern-based inference is used +- `OPENAI_API_KEY`: OpenAI API ํ‚ค (LLM ์ถ”๋ก ์— ํ•„์š”) + - ๋ฏธ์„ค์ • ์‹œ ํด๋ฐฑ ํŒจํ„ด ๊ธฐ๋ฐ˜ ์ถ”๋ก  ์‚ฌ์šฉ -### Flags +### ํ”Œ๋ž˜๊ทธ -- `--targets`: Target linters (comma-separated or "all") -- `--output-dir`: Output directory for generated files -- `--openai-model`: OpenAI model to use (default: gpt-4o) -- `--confidence-threshold`: Minimum confidence for inference (default: 0.7) -- `--timeout`: API call timeout in seconds (default: 30) -- `--verbose`: Enable verbose logging +- `--targets`: ํƒ€๊ฒŸ linter (์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„ ๋˜๋Š” "all") +- `--output-dir`: ์ƒ์„ฑ ํŒŒ์ผ ์ถœ๋ ฅ ๋””๋ ‰ํ† ๋ฆฌ +- `--openai-model`: ์‚ฌ์šฉํ•  OpenAI ๋ชจ๋ธ (๊ธฐ๋ณธ๊ฐ’: gpt-4o) +- `--confidence-threshold`: ์ถ”๋ก  ์ตœ์†Œ ์‹ ๋ขฐ๋„ (๊ธฐ๋ณธ๊ฐ’: 0.7) +- `--timeout`: API ํ˜ธ์ถœ ํƒ€์ž„์•„์›ƒ ์ดˆ (๊ธฐ๋ณธ๊ฐ’: 30) +- `--verbose`: ์ƒ์„ธ ๋กœ๊น… ํ™œ์„ฑํ™” -## User Policy Schema +## ์‚ฌ์šฉ์ž ์ •์ฑ… ์Šคํ‚ค๋งˆ -### Example +### ์˜ˆ์‹œ ```json { @@ -127,7 +127,7 @@ sym convert -i user-policy.json -o code-policy.json "rules": [ { "id": "naming-class-pascalcase", - "say": "Class names must be PascalCase", + "say": "ํด๋ž˜์Šค ์ด๋ฆ„์€ PascalCase์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค", "category": "naming", "languages": ["javascript", "typescript", "java"], "params": { @@ -136,7 +136,7 @@ sym convert -i user-policy.json -o code-policy.json }, { "id": "length-max-line", - "say": "Maximum line length is 100 characters", + "say": "ํ•œ ์ค„์€ ์ตœ๋Œ€ 100์ž์ž…๋‹ˆ๋‹ค", "category": "length", "params": { "max": 100 @@ -146,35 +146,35 @@ sym convert -i user-policy.json -o code-policy.json } ``` -### Supported Categories +### ์ง€์› ์นดํ…Œ๊ณ ๋ฆฌ -- `naming`: Identifier naming conventions -- `length`: Size constraints (line/file/function length) -- `style`: Code formatting (indentation, quotes, semicolons) -- `complexity`: Cyclomatic/cognitive complexity -- `security`: Security-related rules -- `error_handling`: Exception handling patterns -- `dependency`: Import/dependency restrictions +- `naming`: ์‹๋ณ„์ž ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜ +- `length`: ํฌ๊ธฐ ์ œ์•ฝ (๋ผ์ธ/ํŒŒ์ผ/ํ•จ์ˆ˜ ๊ธธ์ด) +- `style`: ์ฝ”๋“œ ํฌ๋งทํŒ… (๋“ค์—ฌ์“ฐ๊ธฐ, ๋”ฐ์˜ดํ‘œ, ์„ธ๋ฏธ์ฝœ๋ก ) +- `complexity`: ์ˆœํ™˜/์ธ์ง€ ๋ณต์žก๋„ +- `security`: ๋ณด์•ˆ ๊ด€๋ จ ๊ทœ์น™ +- `error_handling`: ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํŒจํ„ด +- `dependency`: import/์˜์กด์„ฑ ์ œํ•œ -### Supported Engine Types +### ์ง€์› ์—”์ง„ ํƒ€์ž… -- `pattern`: Naming conventions, forbidden patterns, import restrictions -- `length`: Line/file/function length, parameter count -- `style`: Indentation, quotes, semicolons, whitespace -- `ast`: Cyclomatic complexity, nesting depth -- `custom`: Rules that don't fit other categories +- `pattern`: ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜, ๊ธˆ์ง€ ํŒจํ„ด, import ์ œํ•œ +- `length`: ๋ผ์ธ/ํŒŒ์ผ/ํ•จ์ˆ˜ ๊ธธ์ด, ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆ˜ +- `style`: ๋“ค์—ฌ์“ฐ๊ธฐ, ๋”ฐ์˜ดํ‘œ, ์„ธ๋ฏธ์ฝœ๋ก , ๊ณต๋ฐฑ +- `ast`: ์ˆœํ™˜ ๋ณต์žก๋„, ์ค‘์ฒฉ ๊นŠ์ด +- `custom`: ๊ธฐํƒ€ ์นดํ…Œ๊ณ ๋ฆฌ์— ๋งž์ง€ ์•Š๋Š” ๊ทœ์น™ -## Output Files +## ์ถœ๋ ฅ ํŒŒ์ผ -### Generated Files +### ์ƒ์„ฑ ํŒŒ์ผ -1. **`.eslintrc.json`**: ESLint configuration for JavaScript/TypeScript -2. **`checkstyle.xml`**: Checkstyle configuration for Java -3. **`pmd-ruleset.xml`**: PMD ruleset for Java -4. **`code-policy.json`**: Internal validation policy -5. **`conversion-report.json`**: Detailed conversion report +1. **`.eslintrc.json`**: JavaScript/TypeScript์šฉ ESLint ์„ค์ • +2. **`checkstyle.xml`**: Java์šฉ Checkstyle ์„ค์ • +3. **`pmd-ruleset.xml`**: Java์šฉ PMD ๊ทœ์น™์…‹ +4. **`code-policy.json`**: ๋‚ด๋ถ€ ๊ฒ€์ฆ ์ •์ฑ… +5. **`conversion-report.json`**: ์ƒ์„ธ ๋ณ€ํ™˜ ๋ฆฌํฌํŠธ -### Conversion Report Format +### ๋ณ€ํ™˜ ๋ฆฌํฌํŠธ ํ˜•์‹ ```json { @@ -192,202 +192,96 @@ sym convert -i user-policy.json -o code-policy.json } }, "warnings": [ - "eslint: Rule 2: low confidence (0.40 < 0.70): Maximum line length is 100 characters" + "eslint: Rule 2: low confidence (0.40 < 0.70): ํ•œ ์ค„์€ ์ตœ๋Œ€ 100์ž์ž…๋‹ˆ๋‹ค" ] } ``` -## LLM Inference +## LLM ์ถ”๋ก  -### How It Works +### ๋™์ž‘ ๋ฐฉ์‹ -1. **Cache Check**: First checks if the rule has been inferred before -2. **LLM Analysis**: Sends rule to OpenAI API with structured prompt -3. **Intent Extraction**: Parses JSON response to extract: - - Engine type (pattern/length/style/ast) - - Category (naming/security/etc.) - - Target (identifier/content/import) - - Scope (line/file/function) - - Parameters (max, min, case, etc.) - - Confidence score (0.0-1.0) -4. **Fallback**: If LLM fails, uses pattern matching -5. **Conversion**: Maps intent to linter-specific rules +1. **์บ์‹œ ํ™•์ธ**: ๋จผ์ € ๊ทœ์น™์ด ์ด์ „์— ์ถ”๋ก ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ +2. **LLM ๋ถ„์„**: ๊ตฌ์กฐํ™”๋œ ํ”„๋กฌํ”„ํŠธ์™€ ํ•จ๊ป˜ OpenAI API๋กœ ๊ทœ์น™ ์ „์†ก +3. **Intent ์ถ”์ถœ**: JSON ์‘๋‹ต์„ ํŒŒ์‹ฑํ•˜์—ฌ ์ถ”์ถœ: + - ์—”์ง„ ํƒ€์ž… (pattern/length/style/ast) + - ์นดํ…Œ๊ณ ๋ฆฌ (naming/security ๋“ฑ) + - ํƒ€๊ฒŸ (identifier/content/import) + - ๋ฒ”์œ„ (line/file/function) + - ํŒŒ๋ผ๋ฏธํ„ฐ (max, min, case ๋“ฑ) + - ์‹ ๋ขฐ๋„ ์ ์ˆ˜ (0.0-1.0) +4. **ํด๋ฐฑ**: LLM ์‹คํŒจ ์‹œ ํŒจํ„ด ๋งค์นญ ์‚ฌ์šฉ +5. **๋ณ€ํ™˜**: Intent๋ฅผ linter๋ณ„ ๊ทœ์น™์œผ๋กœ ๋งคํ•‘ -### System Prompt +### ํด๋ฐฑ ์ถ”๋ก  -The LLM is instructed to: -- Analyze natural language coding rules -- Extract structured intent with high precision -- Provide confidence scores for interpretations -- Return results in strict JSON format +LLM ์‚ฌ์šฉ ๋ถˆ๊ฐ€ ์‹œ ํŒจํ„ด ๊ธฐ๋ฐ˜ ๊ทœ์น™์ด ๊ฐ์ง€: +- **๋„ค์ด๋ฐ ๊ทœ์น™**: "PascalCase", "camelCase", "name" ๋“ฑ์˜ ํ‚ค์›Œ๋“œ +- **๊ธธ์ด ๊ทœ์น™**: "line", "length", "max", "characters" ๋“ฑ์˜ ํ‚ค์›Œ๋“œ +- **์Šคํƒ€์ผ ๊ทœ์น™**: "indent", "spaces", "tabs", "quote" ๋“ฑ์˜ ํ‚ค์›Œ๋“œ +- **๋ณด์•ˆ ๊ทœ์น™**: "secret", "password", "hardcoded" ๋“ฑ์˜ ํ‚ค์›Œ๋“œ +- **import ๊ทœ์น™**: "import", "dependency", "layer" ๋“ฑ์˜ ํ‚ค์›Œ๋“œ -### Fallback Inference +## ํ…Œ์ŠคํŠธ -When LLM is unavailable, pattern-based rules detect: -- **Naming rules**: Keywords like "PascalCase", "camelCase", "name" -- **Length rules**: Keywords like "line", "length", "max", "characters" -- **Style rules**: Keywords like "indent", "spaces", "tabs", "quote" -- **Security rules**: Keywords like "secret", "password", "hardcoded" -- **Import rules**: Keywords like "import", "dependency", "layer" - -## Example: Rule Conversion Flow - -### Input Rule -```json -{ - "say": "Class names must be PascalCase", - "category": "naming", - "languages": ["javascript", "java"] -} -``` - -### LLM Inference Result -```json -{ - "engine": "pattern", - "category": "naming", - "target": "identifier", - "params": {"case": "PascalCase"}, - "confidence": 0.95 -} -``` - -### ESLint Output -```json -{ - "rules": { - "id-match": ["error", "^[A-Z][a-zA-Z0-9]*$", { - "properties": false, - "classFields": false, - "onlyDeclarations": true - }] - } -} -``` - -### Checkstyle Output -```xml - - - - -``` - -### PMD Output -```xml - - 1 - -``` - -## Testing - -### Unit Tests +### ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ```bash -# Run all tests +# ๋ชจ๋“  ํ…Œ์ŠคํŠธ ์‹คํ–‰ go test ./... -# Run LLM inference tests +# LLM ์ถ”๋ก  ํ…Œ์ŠคํŠธ go test ./internal/llm/... -# Run linter converter tests +# Linter ๋ณ€ํ™˜๊ธฐ ํ…Œ์ŠคํŠธ go test ./internal/converter/linters/... ``` -### Integration Test - -```bash -# Test with example policy -./bin/sym convert \ - -i tests/testdata/user-policy-example.json \ - --targets all \ - --output-dir /tmp/test-output \ - --verbose -``` - -## Limitations +## ์ œํ•œ์‚ฌํ•ญ ### ESLint -- Limited support for complex AST patterns -- Some rules require custom ESLint plugins -- Style rules may conflict with Prettier +- ๋ณต์žกํ•œ AST ํŒจํ„ด ์ง€์› ์ œํ•œ +- ์ผ๋ถ€ ๊ทœ์น™์€ ์ปค์Šคํ…€ ESLint ํ”Œ๋Ÿฌ๊ทธ์ธ ํ•„์š” +- ์Šคํƒ€์ผ ๊ทœ์น™์ด Prettier์™€ ์ถฉ๋Œ ๊ฐ€๋Šฅ ### Checkstyle -- Module configuration can be complex -- Some rules require additional checks -- Limited support for custom patterns +- ๋ชจ๋“ˆ ์„ค์ •์ด ๋ณต์žกํ•  ์ˆ˜ ์žˆ์Œ +- ์ผ๋ถ€ ๊ทœ์น™์€ ์ถ”๊ฐ€ ์ฒดํฌ ํ•„์š” +- ์ปค์Šคํ…€ ํŒจํ„ด ์ง€์› ์ œํ•œ ### PMD -- Rule references must match PMD versions -- Property configuration varies by rule -- Some categories have limited coverage - -### LLM Inference -- Requires OpenAI API key (costs apply) -- May produce incorrect interpretations for complex rules -- Confidence scores are estimates -- Network dependency and latency - -## Future Enhancements - -- [ ] Support for additional linters (Pylint, RuboCop, etc.) -- [ ] Custom rule templates -- [ ] Rule conflict detection -- [ ] Interactive mode for ambiguous rules -- [ ] Cost estimation for LLM API calls -- [ ] Local LLM support (Ollama, etc.) -- [ ] Rule similarity clustering -- [ ] Automatic rule categorization -- [ ] Multi-language rule mapping optimization - -## Troubleshooting - -### "OPENAI_API_KEY not set" -- Set environment variable: `export OPENAI_API_KEY=sk-...` -- Or use fallback mode (lower accuracy) - -### "low confidence" warnings -- Increase `--confidence-threshold` to reduce warnings -- Provide more specific `category` and `params` in rules -- Use LLM instead of fallback for better accuracy - -### Generated rules don't work -- Check linter version compatibility -- Verify rule syntax in linter documentation -- Adjust rule parameters manually if needed -- Report issue with conversion-report.json +- ๊ทœ์น™ ์ฐธ์กฐ๋Š” PMD ๋ฒ„์ „๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•จ +- ์†์„ฑ ์„ค์ •์ด ๊ทœ์น™๋งˆ๋‹ค ๋‹ค๋ฆ„ +- ์ผ๋ถ€ ์นดํ…Œ๊ณ ๋ฆฌ์˜ ์ปค๋ฒ„๋ฆฌ์ง€ ์ œํ•œ -### Slow conversion -- Reduce number of rules -- Use caching (re-run with same rules) -- Increase `--timeout` for large rule sets -- Use faster OpenAI model (gpt-4o) +### LLM ์ถ”๋ก  +- OpenAI API ํ‚ค ํ•„์š” (๋น„์šฉ ๋ฐœ์ƒ) +- ๋ณต์žกํ•œ ๊ทœ์น™์— ๋Œ€ํ•ด ์ž˜๋ชป๋œ ํ•ด์„ ๊ฐ€๋Šฅ +- ์‹ ๋ขฐ๋„ ์ ์ˆ˜๋Š” ์ถ”์ •์น˜ +- ๋„คํŠธ์›Œํฌ ์˜์กด์„ฑ ๋ฐ ์ง€์—ฐ -## Performance +## ์„ฑ๋Šฅ -### Benchmarks (5 rules, no cache) +### ๋ฒค์น˜๋งˆํฌ (5๊ฐœ ๊ทœ์น™, ์บ์‹œ ์—†์Œ) -- **With LLM (gpt-4o)**: ~5-10 seconds -- **Fallback only**: <1 second -- **With cache**: <100ms +- **LLM ์‚ฌ์šฉ (gpt-4o)**: ์•ฝ 5-10์ดˆ +- **ํด๋ฐฑ๋งŒ**: 1์ดˆ ๋ฏธ๋งŒ +- **์บ์‹œ ์ ์šฉ**: 100ms ๋ฏธ๋งŒ -### Cost Estimation +### ๋น„์šฉ ์ถ”์ • -- **gpt-4o**: ~$0.001 per rule -- **gpt-4o**: ~$0.01 per rule -- **Caching**: Reduces cost by ~90% for repeated rules +- **gpt-4o**: ๊ทœ์น™๋‹น ์•ฝ $0.001 +- **์บ์‹ฑ**: ๋ฐ˜๋ณต ๊ทœ์น™์— ๋Œ€ํ•ด ๋น„์šฉ ์•ฝ 90% ์ ˆ๊ฐ -## Contributing +## ๊ธฐ์—ฌ -When adding new linter support: +์ƒˆ linter ์ง€์› ์ถ”๊ฐ€ ์‹œ: -1. Implement `LinterConverter` interface in `internal/converter/linters/` -2. Register converter in `init()` function -3. Add tests in `*_test.go` -4. Update this documentation -5. Add example output to `tests/testdata/` +1. `internal/converter/linters/`์— `LinterConverter` ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ +2. `init()` ํ•จ์ˆ˜์—์„œ ๋ณ€ํ™˜๊ธฐ ๋“ฑ๋ก +3. `*_test.go`์— ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ +4. ์ด ๋ฌธ์„œ ์—…๋ฐ์ดํŠธ -## License +## ๋ผ์ด์„ ์Šค -Same as sym-cli project license. +sym-cli ํ”„๋กœ์ ํŠธ ๋ผ์ด์„ ์Šค์™€ ๋™์ผ diff --git a/docs/CONVERT_USAGE.md b/docs/CONVERT_USAGE.md index 5fa8cf8..6ea61a5 100644 --- a/docs/CONVERT_USAGE.md +++ b/docs/CONVERT_USAGE.md @@ -1,66 +1,66 @@ -# Convert Command Usage Guide +# Convert ๋ช…๋ น์–ด ์‚ฌ์šฉ ๊ฐ€์ด๋“œ -## Quick Start +## ๋น ๋ฅธ ์‹œ์ž‘ -Convert natural language rules to linter configurations: +์ž์—ฐ์–ด ๊ทœ์น™์„ linter ์„ค์ •์œผ๋กœ ๋ณ€ํ™˜: ```bash -# Convert to all supported linters (outputs to /.sym) +# ๋ชจ๋“  ์ง€์› linter๋กœ ๋ณ€ํ™˜ (์ถœ๋ ฅ: /.sym) sym convert -i user-policy.json --targets all -# Convert only for JavaScript/TypeScript +# JavaScript/TypeScript๋งŒ sym convert -i user-policy.json --targets eslint -# Convert for Java +# Java๋งŒ sym convert -i user-policy.json --targets checkstyle,pmd ``` -## Default Output Directory +## ๊ธฐ๋ณธ ์ถœ๋ ฅ ๋””๋ ‰ํ† ๋ฆฌ -**Important**: The convert command automatically creates a `.sym` directory at your git repository root and saves all generated files there. +**์ค‘์š”**: convert ๋ช…๋ น์–ด๋Š” Git ์ €์žฅ์†Œ ๋ฃจํŠธ์— `.sym` ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ž๋™ ์ƒ์„ฑํ•˜๊ณ  ๋ชจ๋“  ํŒŒ์ผ์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. -### Directory Structure +### ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ``` your-project/ -โ”œโ”€โ”€ .git/ -โ”œโ”€โ”€ .sym/ # Auto-generated -โ”‚ โ”œโ”€โ”€ .eslintrc.json # ESLint config -โ”‚ โ”œโ”€โ”€ checkstyle.xml # Checkstyle config -โ”‚ โ”œโ”€โ”€ pmd-ruleset.xml # PMD config -โ”‚ โ”œโ”€โ”€ code-policy.json # Internal policy -โ”‚ โ””โ”€โ”€ conversion-report.json # Conversion report -โ”œโ”€โ”€ src/ -โ””โ”€โ”€ user-policy.json # Your input file ++-- .git/ ++-- .sym/ # ์ž๋™ ์ƒ์„ฑ +| +-- .eslintrc.json # ESLint ์„ค์ • +| +-- checkstyle.xml # Checkstyle ์„ค์ • +| +-- pmd-ruleset.xml # PMD ์„ค์ • +| +-- code-policy.json # ๋‚ด๋ถ€ ์ •์ฑ… +| +-- conversion-report.json # ๋ณ€ํ™˜ ๋ฆฌํฌํŠธ ++-- src/ ++-- user-policy.json # ์ž…๋ ฅ ํŒŒ์ผ ``` -### Why .sym? +### ์™œ .sym์ธ๊ฐ€? -- **Consistent location**: Always at git root, easy to find -- **Version control**: Add to `.gitignore` to keep generated files out of git -- **CI/CD friendly**: Scripts can always find configs at `/.sym` +- **์ผ๊ด€๋œ ์œ„์น˜**: ํ•ญ์ƒ Git ๋ฃจํŠธ์— ์žˆ์–ด ์ฐพ๊ธฐ ์‰ฌ์›€ +- **๋ฒ„์ „ ๊ด€๋ฆฌ**: `.gitignore`์— ์ถ”๊ฐ€ํ•˜์—ฌ ์ƒ์„ฑ ํŒŒ์ผ์„ Git์—์„œ ์ œ์™ธ ๊ฐ€๋Šฅ +- **CI/CD ์นœํ™”์ **: ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํ•ญ์ƒ `/.sym`์—์„œ ์„ค์ • ์ฐพ์Œ -### Custom Output Directory +### ์ปค์Šคํ…€ ์ถœ๋ ฅ ๋””๋ ‰ํ† ๋ฆฌ -If you need a different location: +๋‹ค๋ฅธ ์œ„์น˜๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ: ```bash sym convert -i user-policy.json --targets all --output-dir ./custom-dir ``` -## Prerequisites +## ์‚ฌ์ „ ์š”๊ตฌ์‚ฌํ•ญ -1. **Git repository**: Run the command from within a git repository -2. **OpenAI API key** (optional): Set `OPENAI_API_KEY` for better inference +1. **Git ์ €์žฅ์†Œ**: Git ์ €์žฅ์†Œ ๋‚ด์—์„œ ๋ช…๋ น์–ด ์‹คํ–‰ +2. **OpenAI API ํ‚ค** (์„ ํƒ): ๋” ๋‚˜์€ ์ถ”๋ก ์„ ์œ„ํ•ด `OPENAI_API_KEY` ์„ค์ • ```bash export OPENAI_API_KEY=sk-... ``` -Without API key, fallback pattern matching is used (lower accuracy). +API ํ‚ค ์—†์ด๋„ ํด๋ฐฑ ํŒจํ„ด ๋งค์นญ ์‚ฌ์šฉ (์ •ํ™•๋„ ๋‚ฎ์Œ) -## User Policy File +## ์‚ฌ์šฉ์ž ์ •์ฑ… ํŒŒ์ผ -Create a `user-policy.json` with natural language rules: +์ž์—ฐ์–ด ๊ทœ์น™์œผ๋กœ `user-policy.json` ์ž‘์„ฑ: ```json { @@ -71,134 +71,132 @@ Create a `user-policy.json` with natural language rules: }, "rules": [ { - "say": "Class names must be PascalCase", + "say": "ํด๋ž˜์Šค ์ด๋ฆ„์€ PascalCase์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค", "category": "naming", "languages": ["javascript", "typescript", "java"] }, { - "say": "Maximum line length is 100 characters", + "say": "ํ•œ ์ค„์€ ์ตœ๋Œ€ 100์ž์ž…๋‹ˆ๋‹ค", "category": "length" }, { - "say": "Use 4 spaces for indentation", + "say": "๋“ค์—ฌ์“ฐ๊ธฐ๋Š” 4์นธ ๊ณต๋ฐฑ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค", "category": "style" } ] } ``` -## Command Options +## ๋ช…๋ น์–ด ์˜ต์…˜ -### Basic Options +### ๊ธฐ๋ณธ ์˜ต์…˜ -- `-i, --input`: Input user policy file (default: `user-policy.json`) -- `--targets`: Target linters (comma-separated or `all`) +- `-i, --input`: ์ž…๋ ฅ ์‚ฌ์šฉ์ž ์ •์ฑ… ํŒŒ์ผ (๊ธฐ๋ณธ๊ฐ’: `user-policy.json`) +- `--targets`: ํƒ€๊ฒŸ linter (์‰ผํ‘œ ๊ตฌ๋ถ„ ๋˜๋Š” `all`) - `eslint` - JavaScript/TypeScript - `checkstyle` - Java - `pmd` - Java - - `all` - All supported linters + - `all` - ๋ชจ๋“  ์ง€์› linter -### Advanced Options +### ๊ณ ๊ธ‰ ์˜ต์…˜ -- `--output-dir`: Custom output directory (default: `/.sym`) -- `--openai-model`: OpenAI model (default: `gpt-4o`) - - `gpt-4o` - Fast, cheap, good quality - - `gpt-4o` - Slower, more expensive, best quality -- `--confidence-threshold`: Minimum confidence (default: `0.7`) - - Range: 0.0 to 1.0 - - Lower values = more rules converted, more warnings -- `--timeout`: API timeout in seconds (default: `30`) -- `-v, --verbose`: Enable detailed logging +- `--output-dir`: ์ปค์Šคํ…€ ์ถœ๋ ฅ ๋””๋ ‰ํ† ๋ฆฌ (๊ธฐ๋ณธ๊ฐ’: `/.sym`) +- `--openai-model`: OpenAI ๋ชจ๋ธ (๊ธฐ๋ณธ๊ฐ’: `gpt-4o`) +- `--confidence-threshold`: ์ตœ์†Œ ์‹ ๋ขฐ๋„ (๊ธฐ๋ณธ๊ฐ’: `0.7`) + - ๋ฒ”์œ„: 0.0 ~ 1.0 + - ๋‚ฎ์€ ๊ฐ’ = ๋” ๋งŽ์€ ๊ทœ์น™ ๋ณ€ํ™˜, ๋” ๋งŽ์€ ๊ฒฝ๊ณ  +- `--timeout`: API ํƒ€์ž„์•„์›ƒ ์ดˆ (๊ธฐ๋ณธ๊ฐ’: `30`) +- `-v, --verbose`: ์ƒ์„ธ ๋กœ๊น… ํ™œ์„ฑํ™” -### Legacy Mode +### ๋ ˆ๊ฑฐ์‹œ ๋ชจ๋“œ -Generate only internal `code-policy.json`: +๋‚ด๋ถ€ `code-policy.json`๋งŒ ์ƒ์„ฑ: ```bash sym convert -i user-policy.json -o code-policy.json ``` -## Example Workflows +## ์˜ˆ์ œ ์›Œํฌํ”Œ๋กœ์šฐ -### JavaScript/TypeScript Project +### JavaScript/TypeScript ํ”„๋กœ์ ํŠธ ```bash -# 1. Create user-policy.json +# 1. user-policy.json ์ž‘์„ฑ cat > user-policy.json <> .gitignore -# But commit user-policy.json +# user-policy.json์€ ์ปค๋ฐ‹ git add user-policy.json git commit -m "Add coding conventions policy" ``` -### Sharing Configs +### ์„ค์ • ๊ณต์œ  ```bash -# Share with team +# ํŒ€๊ณผ ๊ณต์œ  git add .sym/*.{json,xml} git commit -m "Add generated linter configs" -# Or regenerate on each machine -# (Each developer runs: sym convert -i user-policy.json --targets all) +# ๋˜๋Š” ๊ฐ ๋จธ์‹ ์—์„œ ์žฌ์ƒ์„ฑ +# (๊ฐ ๊ฐœ๋ฐœ์ž๊ฐ€ ์‹คํ–‰: sym convert -i user-policy.json --targets all) ``` -### Updating Rules +### ๊ทœ์น™ ์—…๋ฐ์ดํŠธ ```bash -# 1. Edit user-policy.json -# 2. Regenerate configs +# 1. user-policy.json ํŽธ์ง‘ +# 2. ์„ค์ • ์žฌ์ƒ์„ฑ sym convert -i user-policy.json --targets all -# 3. Review changes +# 3. ๋ณ€๊ฒฝ์‚ฌํ•ญ ๊ฒ€ํ†  git diff .sym/ -# 4. Apply to project +# 4. ํ”„๋กœ์ ํŠธ์— ์ ์šฉ npx eslint --config .sym/.eslintrc.json src/ ``` -## Next Steps +## ๋‹ค์Œ ๋‹จ๊ณ„ -- [Full Feature Documentation](CONVERT_FEATURE.md) -- [User Policy Schema Reference](../tests/testdata/user-policy-example.json) -- [Contributing Guide](../AGENTS.md) +- [์ „์ฒด ๊ธฐ๋Šฅ ๋ฌธ์„œ](CONVERT_FEATURE.md) +- [๊ธฐ์—ฌ ๊ฐ€์ด๋“œ](../AGENTS.md) diff --git a/docs/LINTER_VALIDATION.md b/docs/LINTER_VALIDATION.md index 0dae82b..3c085c7 100644 --- a/docs/LINTER_VALIDATION.md +++ b/docs/LINTER_VALIDATION.md @@ -1,43 +1,43 @@ -# Linter Configuration Validation +# Linter ์„ค์ • ๊ฒ€์ฆ -## Purpose +## ๋ชฉ์  -The convert feature generates linter-specific configurations from natural language coding conventions. These configurations are used to validate code changes tracked by git. +convert ๊ธฐ๋Šฅ์€ ์ž์—ฐ์–ด ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜์—์„œ linter๋ณ„ ์„ค์ •์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ Git์œผ๋กœ ์ถ”์ ๋˜๋Š” ์ฝ”๋“œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ฒ€์ฆํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. -## Supported Linters +## ์ง€์› Linter ### JavaScript/TypeScript -- **ESLint**: Validates JS/TS code style, patterns, and best practices -- Output: `.sym/.eslintrc.json` +- **ESLint**: JS/TS ์ฝ”๋“œ ์Šคํƒ€์ผ, ํŒจํ„ด, ๋ชจ๋ฒ” ์‚ฌ๋ก€ ๊ฒ€์ฆ +- ์ถœ๋ ฅ: `.sym/.eslintrc.json` ### Java -- **Checkstyle**: Validates Java code formatting and style -- Output: `.sym/checkstyle.xml` -- **PMD**: Validates Java code quality and detects code smells -- Output: `.sym/pmd-ruleset.xml` +- **Checkstyle**: Java ์ฝ”๋“œ ํฌ๋งทํŒ… ๋ฐ ์Šคํƒ€์ผ ๊ฒ€์ฆ +- ์ถœ๋ ฅ: `.sym/checkstyle.xml` +- **PMD**: Java ์ฝ”๋“œ ํ’ˆ์งˆ ๊ฒ€์ฆ ๋ฐ ์ฝ”๋“œ ์Šค๋ฉœ ๊ฐ์ง€ +- ์ถœ๋ ฅ: `.sym/pmd-ruleset.xml` -### Future Support -- **SonarQube**: Multi-language static analysis -- **LLM Validator**: Custom rules that cannot be expressed in traditional linters +### ํ–ฅํ›„ ์ง€์› ์˜ˆ์ • +- **SonarQube**: ๋‹ค์ค‘ ์–ธ์–ด ์ •์  ๋ถ„์„ +- **LLM Validator**: ์ „ํ†ต์ ์ธ linter๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†๋Š” ์ปค์Šคํ…€ ๊ทœ์น™ -## Engine Assignment +## ์—”์ง„ ํ• ๋‹น -Each rule in `code-policy.json` has an `engine` field that specifies which tool validates it: +`code-policy.json`์˜ ๊ฐ ๊ทœ์น™์—๋Š” ๊ฒ€์ฆ ๋„๊ตฌ๋ฅผ ์ง€์ •ํ•˜๋Š” `engine` ํ•„๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค: -- `eslint`: Rule converted to ESLint configuration -- `checkstyle`: Rule converted to Checkstyle module -- `pmd`: Rule converted to PMD ruleset -- `sonarqube`: Future support -- `llm-validator`: Complex rules requiring LLM analysis +- `eslint`: ESLint ์„ค์ •์œผ๋กœ ๋ณ€ํ™˜๋œ ๊ทœ์น™ +- `checkstyle`: Checkstyle ๋ชจ๋“ˆ๋กœ ๋ณ€ํ™˜๋œ ๊ทœ์น™ +- `pmd`: PMD ๊ทœ์น™์…‹์œผ๋กœ ๋ณ€ํ™˜๋œ ๊ทœ์น™ +- `sonarqube`: ํ–ฅํ›„ ์ง€์› ์˜ˆ์ • +- `llm-validator`: LLM ๋ถ„์„์ด ํ•„์š”ํ•œ ๋ณต์žกํ•œ ๊ทœ์น™ -## Example Workflow +## ์˜ˆ์ œ ์›Œํฌํ”Œ๋กœ์šฐ -1. **Define conventions** in `user-policy.json` -2. **Convert** to linter configs: +1. `user-policy.json`์— **์ปจ๋ฒค์…˜ ์ •์˜** +2. linter ์„ค์ •์œผ๋กœ **๋ณ€ํ™˜**: ```bash sym convert -i user-policy.json --targets eslint,checkstyle,pmd ``` -3. **Run linters** on git changes: +3. Git ๋ณ€๊ฒฝ์‚ฌํ•ญ์— **linter ์‹คํ–‰**: ```bash # JavaScript/TypeScript eslint --config .sym/.eslintrc.json src/**/*.{js,ts} @@ -47,9 +47,9 @@ Each rule in `code-policy.json` has an `engine` field that specifies which tool pmd check -R .sym/pmd-ruleset.xml -d src/ ``` -## Code Policy Schema +## ์ฝ”๋“œ ์ •์ฑ… ์Šคํ‚ค๋งˆ -Generated `code-policy.json` contains: +์ƒ์„ฑ๋œ `code-policy.json` ๋‚ด์šฉ: ```json { "version": "1.0.0", @@ -68,46 +68,20 @@ Generated `code-policy.json` contains: } ``` -Rules with `engine: "llm-validator"` cannot be checked by traditional linters and require custom LLM-based validation. +`engine: "llm-validator"` ๊ทœ์น™์€ ์ „ํ†ต์ ์ธ linter๋กœ ์ฒดํฌํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ์ปค์Šคํ…€ LLM ๊ธฐ๋ฐ˜ ๊ฒ€์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. -## Testing +## ํ…Œ์ŠคํŠธ -### Integration Test Data - -Validation engines are tested using structured test data in `tests/testdata/`: - -``` -tests/testdata/ -โ”œโ”€โ”€ javascript/ # ESLint-based validation tests -โ”‚ โ”œโ”€โ”€ pattern/ # Naming conventions, regex patterns -โ”‚ โ”œโ”€โ”€ length/ # Line/function length limits -โ”‚ โ”œโ”€โ”€ style/ # Code formatting -โ”‚ โ””โ”€โ”€ ast/ # AST structure validation -โ”œโ”€โ”€ typescript/ # TSC-based validation tests -โ”‚ โ””โ”€โ”€ typechecker/ # Type checking tests -โ””โ”€โ”€ java/ # Checkstyle/PMD-based validation tests - โ”œโ”€โ”€ pattern/ # Naming conventions (PascalCase, camelCase) - โ”œโ”€โ”€ length/ # Line/method/parameter length limits - โ”œโ”€โ”€ style/ # Java formatting conventions - โ””โ”€โ”€ ast/ # Code structure (exception handling, etc.) -``` - -Each directory contains: -- **Violation files**: Code that violates conventions (e.g., `NamingViolations.java`) -- **Valid files**: Code that complies with conventions (e.g., `ValidNaming.java`) - -### Running Integration Tests +### ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ```bash -# All integration tests +# ๋ชจ๋“  ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ go test ./tests/integration/... -v -# Specific engine tests +# ํŠน์ • ์—”์ง„ ํ…Œ์ŠคํŠธ go test ./tests/integration/... -v -run TestPatternEngine go test ./tests/integration/... -v -run TestLengthEngine go test ./tests/integration/... -v -run TestStyleEngine go test ./tests/integration/... -v -run TestASTEngine go test ./tests/integration/... -v -run TestTypeChecker ``` - -For detailed test data structure, see [tests/testdata/README.md](../tests/testdata/README.md). diff --git a/tests/TESTING_GUIDE.md b/tests/TESTING_GUIDE.md index b2612cb..20f1c7a 100644 --- a/tests/TESTING_GUIDE.md +++ b/tests/TESTING_GUIDE.md @@ -139,61 +139,6 @@ go test -v ./tests/e2e/... -timeout 5m - [ ] ์œ„๋ฐ˜์‚ฌํ•ญ ๋ณด๊ณ ์„œ ์ƒ์„ฑ - [ ] ์ข…๋ฃŒ ์ฝ”๋“œ ์„ค์ • (์œ„๋ฐ˜ ์‹œ 1) -## ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ - -### testdata ๋””๋ ‰ํ† ๋ฆฌ - -๊ฒ€์ฆ ์—”์ง„์˜ ์ •ํ™•์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋Š” `tests/testdata/` ๋””๋ ‰ํ† ๋ฆฌ์— ์—”์ง„๋ณ„, ์–ธ์–ด๋ณ„๋กœ ๊ตฌ์กฐํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค: - -``` -tests/testdata/ -โ”œโ”€โ”€ javascript/ -โ”‚ โ”œโ”€โ”€ pattern/ # ํŒจํ„ด ๋งค์นญ ๋ฐ ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜ ํ…Œ์ŠคํŠธ -โ”‚ โ”‚ โ”œโ”€โ”€ naming-violations.js -โ”‚ โ”‚ โ”œโ”€โ”€ security-violations.js -โ”‚ โ”‚ โ””โ”€โ”€ valid.js -โ”‚ โ”œโ”€โ”€ length/ # ๋ผ์ธ/ํ•จ์ˆ˜ ๊ธธ์ด ์ œํ•œ ํ…Œ์ŠคํŠธ -โ”‚ โ”‚ โ”œโ”€โ”€ length-violations.js -โ”‚ โ”‚ โ””โ”€โ”€ valid.js -โ”‚ โ”œโ”€โ”€ style/ # ์ฝ”๋“œ ์Šคํƒ€์ผ ๋ฐ ํฌ๋งทํŒ… ํ…Œ์ŠคํŠธ -โ”‚ โ”‚ โ”œโ”€โ”€ style-violations.js -โ”‚ โ”‚ โ””โ”€โ”€ valid.js -โ”‚ โ””โ”€โ”€ ast/ # AST ๊ตฌ์กฐ ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ -โ”‚ โ”œโ”€โ”€ naming-violations.js -โ”‚ โ””โ”€โ”€ valid.js -โ”œโ”€โ”€ typescript/ -โ”‚ โ””โ”€โ”€ typechecker/ # ํƒ€์ž… ์ฒดํ‚น ํ…Œ์ŠคํŠธ -โ”‚ โ”œโ”€โ”€ type-errors.ts -โ”‚ โ”œโ”€โ”€ strict-mode-errors.ts -โ”‚ โ””โ”€โ”€ valid.ts -โ””โ”€โ”€ java/ - โ”œโ”€โ”€ pattern/ # Checkstyle ํŒจํ„ด ํ…Œ์ŠคํŠธ - โ”‚ โ”œโ”€โ”€ NamingViolations.java - โ”‚ โ””โ”€โ”€ ValidNaming.java - โ”œโ”€โ”€ length/ # Checkstyle ๊ธธ์ด ์ œํ•œ ํ…Œ์ŠคํŠธ - โ”‚ โ”œโ”€โ”€ LengthViolations.java - โ”‚ โ””โ”€โ”€ ValidLength.java - โ”œโ”€โ”€ style/ # Checkstyle ์Šคํƒ€์ผ ํ…Œ์ŠคํŠธ - โ”‚ โ”œโ”€โ”€ StyleViolations.java - โ”‚ โ””โ”€โ”€ ValidStyle.java - โ””โ”€โ”€ ast/ # PMD AST ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ - โ”œโ”€โ”€ AstViolations.java - โ””โ”€โ”€ ValidAst.java -``` - -**ํŒŒ์ผ ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜**: -- `*-violations.*` / `*Violations.*`: ๊ทœ์น™ ์œ„๋ฐ˜ ์ผ€์ด์Šค -- `valid.*` / `Valid*.*`: ๊ทœ์น™ ์ค€์ˆ˜ ์ผ€์ด์Šค - -๊ฐ ์—”์ง„์€ ํ•ด๋‹น ์–ธ์–ด์˜ testdata๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค: -- Pattern Engine: ์ •๊ทœ์‹ ํŒจํ„ด ๊ฒ€์ฆ (ESLint/Checkstyle) -- Length Engine: ๊ธธ์ด ์ œํ•œ ๊ฒ€์ฆ (ESLint/Checkstyle) -- Style Engine: ํฌ๋งทํŒ… ๊ฒ€์ฆ (Prettier/Checkstyle) -- AST Engine: ๊ตฌ์กฐ ๊ฒ€์ฆ (ESLint/PMD) -- TypeChecker Engine: ํƒ€์ž… ๊ฒ€์ฆ (TSC) - -์ž์„ธํ•œ ๋‚ด์šฉ์€ [testdata/README.md](testdata/README.md)๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. - ## E2E ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ### ์ž์—ฐ์–ด ์ปจ๋ฒค์…˜ ์˜ˆ์‹œ From 640d6c1295499fbbdb77d887da24dd164503d301 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 04:02:45 +0000 Subject: [PATCH 24/26] docs: simplify README with quick start guide at top - Reduce from 703 to 125 lines - Move quick start guide to top - MCP auto-configured during init - Remove redundant content --- README.md | 710 +++++------------------------------------------------- 1 file changed, 66 insertions(+), 644 deletions(-) diff --git a/README.md b/README.md index d8ff67e..5bf937a 100644 --- a/README.md +++ b/README.md @@ -1,702 +1,124 @@ # Symphony (sym) -GitHub Repository Role & Policy Management Tool with Code Convention Validation +์ž์—ฐ์–ด ๊ธฐ๋ฐ˜ ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜ ๊ด€๋ฆฌ ๋ฐ ๊ฒ€์ฆ ๋„๊ตฌ -Symphony๋Š” GitHub OAuth ์ธ์ฆ์„ ํ†ตํ•œ ์—ญํ•  ๊ธฐ๋ฐ˜ ํŒŒ์ผ ์ ‘๊ทผ ๊ถŒํ•œ ๋ฐ ์ฝ”๋”ฉ ์ •์ฑ… ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ CLI/Web ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ž…๋‹ˆ๋‹ค. ์ž์—ฐ์–ด๋กœ ์ •์˜๋œ ์ปจ๋ฒค์…˜์„ ๊ฒ€์ฆํ•˜๋Š” LLM ์นœํ™”์  linter ๊ธฐ๋Šฅ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. [![Test Coverage](https://img.shields.io/badge/coverage-view%20report-blue)](https://devsymphony.github.io/sym-cli/coverage.html) -## ๊ฐœ์š” - -> **๋น ๋ฅธ ์‹œ์ž‘:** `sym login` ํ•œ ๋ฒˆ์ด๋ฉด ๋! OAuth App ์„ค์ • ๋ถˆํ•„์š”. - -## ์ฃผ์š” ๊ธฐ๋Šฅ - -### ์—ญํ•  ๋ฐ ๊ถŒํ•œ ๊ด€๋ฆฌ -- **CLI ์ธํ„ฐํŽ˜์ด์Šค**: ์‚ฌ์šฉ์ž ์—ญํ•  ๋ฐ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ •๋ณด ๋น ๋ฅธ ์กฐํšŒ -- **์›น ๋Œ€์‹œ๋ณด๋“œ**: ๊ถŒํ•œ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์‹œ๊ฐ์  ์ธํ„ฐํŽ˜์ด์Šค (ํฌํŠธ 8787) -- **OAuth ์ธ์ฆ**: ์•ˆ์ „ํ•œ GitHub/GHES ์ธ์ฆ -- **๋™์  ์—ญํ•  ์‹œ์Šคํ…œ**: ์ปค์Šคํ…€ ์—ญํ•  ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ -- **JSON API**: ์Šคํฌ๋ฆฝํŒ…์„ ์œ„ํ•œ ๊ธฐ๊ณ„ ํŒ๋… ๊ฐ€๋Šฅ ์ถœ๋ ฅ - -### ์ •์ฑ… ํŽธ์ง‘๊ธฐ (Policy Editor) -- **RBAC (Role-Based Access Control)**: ์—ญํ• ๋ณ„ ํŒŒ์ผ ์ ‘๊ทผ ๊ถŒํ•œ ๋ฐ ์‹œ์Šคํ…œ ๊ถŒํ•œ ์„ค์ • -- **์ฝ”๋”ฉ ๊ทœ์น™ ๊ด€๋ฆฌ**: ํ”„๋กœ์ ํŠธ๋ณ„ ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜ ๋ฐ ์ •์ฑ… ์ •์˜ -- **ํ…œํ”Œ๋ฆฟ ์‹œ์Šคํ…œ**: React, Vue, Node.js, Python, Go, TypeScript ํ…œํ”Œ๋ฆฟ ์ œ๊ณต -- **ํžˆ์Šคํ† ๋ฆฌ ์ถ”์ **: ์ •์ฑ… ๋ณ€๊ฒฝ ์ด๋ ฅ ์กฐํšŒ (Git ๊ธฐ๋ฐ˜) -- **์ž๋™ ์ €์žฅ**: 30์ดˆ๋งˆ๋‹ค ์ž๋™ ์ €์žฅ (์„ ํƒ ๊ฐ€๋Šฅ) -- **์•ˆ์ „์žฅ์น˜**: ์ตœ์†Œ 1๋ช…์˜ ์ •์ฑ… ํŽธ์ง‘์ž ๋ณด์žฅ, ์—ญํ•  ์‚ญ์ œ ๋ณดํ˜ธ -- **๊ถŒํ•œ ๊ธฐ๋ฐ˜ UI**: ๊ถŒํ•œ์— ๋”ฐ๋ฅธ ์ฝ๊ธฐ ์ „์šฉ ๋ชจ๋“œ ์ž๋™ ์ ์šฉ -- ์ž์—ฐ์–ด ๊ธฐ๋ฐ˜ ์ปจ๋ฒค์…˜ ์ •์˜ -- **LLM ๊ธฐ๋ฐ˜ ์ž๋™ ๋ณ€ํ™˜**: OpenAI API๋กœ ์ž์—ฐ์–ด ๊ทœ์น™์„ linter ์„ค์ •์œผ๋กœ ์ž๋™ ๋ณ€ํ™˜ -- **๋‹ค์ค‘ Linter ์ง€์›**: ESLint, Checkstyle, PMD ๋“ฑ ์—ฌ๋Ÿฌ linter ์„ค์ • ํŒŒ์ผ ๋™์‹œ ์ƒ์„ฑ -- ์ฝ”๋“œ ์Šคํƒ€์ผ ๋ฐ ์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™ ๊ฒ€์ฆ -- RBAC ๊ธฐ๋ฐ˜ ํŒŒ์ผ ์ ‘๊ทผ ์ œ์–ด -- JSON ์ถœ๋ ฅ์„ ํ†ตํ•œ LLM ๋„๊ตฌ ์—ฐ๋™ -- ์ปจํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ์ปจ๋ฒค์…˜ ์ถ”์ถœ - -### ์ฝ”๋“œ ์ปจ๋ฒค์…˜ ๊ฒ€์ฆ (๊ฐœ๋ฐœ ์ค‘) -- **์ž์—ฐ์–ด ๊ธฐ๋ฐ˜ ์ปจ๋ฒค์…˜ ์ •์˜**: `.sym/user-policy.json`์— ์ž์—ฐ์–ด๋กœ ๊ทœ์น™ ์ž‘์„ฑ -- **์Šคํ‚ค๋งˆ ๋ณ€ํ™˜**: A ์Šคํ‚ค๋งˆ (์‚ฌ์šฉ์ž ์ž…๋ ฅ) โ†’ B ์Šคํ‚ค๋งˆ (๊ฒ€์ฆ ์—”์ง„์šฉ) -- **๋‹ค์ค‘ ๊ฒ€์ฆ ์—”์ง„**: Pattern, Length, Style, AST ์—”์ง„ ์ง€์› -- **LLM ๋„๊ตฌ ์—ฐ๋™**: JSON ์ถœ๋ ฅ์„ ํ†ตํ•œ AI ์ฝ”๋”ฉ ๋„๊ตฌ ์—ฐ๋™ -- **์ปจํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ์ถ”์ถœ**: ์ž‘์—… ์ปจํ…์ŠคํŠธ์— ๋งž๋Š” ์ปจ๋ฒค์…˜๋งŒ ์ถ”์ถœ - -### ๊ธฐ์ˆ  ์Šคํƒ -- **๋‹จ์ผ ๋ฐ”์ด๋„ˆ๋ฆฌ**: ๋Ÿฐํƒ€์ž„ ์˜์กด์„ฑ ๋ถˆํ•„์š” -- **์ž„๋ฒ ๋””๋“œ ์—์…‹**: go:embed๋ฅผ ํ†ตํ•œ ์ •์  ํŒŒ์ผ ๋‚ด์žฅ (HTML, CSS, JS, SVG) -- **ํ”„๋กœ๋•์…˜ CSS**: Tailwind CSS ๋นŒ๋“œ ์‹œ์Šคํ…œ -- **๋ฉ€ํ‹ฐํ”Œ๋žซํผ**: Windows, macOS (Intel/ARM), Linux (AMD64/ARM64) ์ง€์› -- **UTF-8 ์ง€์›**: ํ•œ๊ธ€ ๋ฐ ์ด๋ชจํ‹ฐ์ฝ˜ ์™„๋ฒฝ ์ง€์› - -## ์„ค์น˜ - -### MCP ์„œ๋ฒ„๋กœ ์„ค์น˜ (๊ถŒ์žฅ - AI ์ฝ”๋”ฉ ๋„๊ตฌ) - -**Claude Code ์›ํด๋ฆญ ์„ค์น˜**: -```bash -claude mcp add symphony npx @dev-symphony/sym@latest mcp -``` - -**์ˆ˜๋™ MCP ์„ค์ •** (Claude Desktop / Cursor / Continue.dev): - -config ํŒŒ์ผ ์œ„์น˜: -- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` -- Windows: `%APPDATA%/Claude/claude_desktop_config.json` -- Linux: `~/.config/Claude/claude_desktop_config.json` - -์„ค์ • ์ถ”๊ฐ€: -```json -{ - "mcpServers": { - "symphony": { - "command": "npx", - "args": ["-y", "@dev-symphony/sym@latest", "mcp"], - "env": { - "SYM_POLICY_PATH": "${workspaceFolder}/.sym/user-policy.json" - } - } - } -} -``` - -Claude Desktop ์žฌ์‹œ์ž‘ ํ›„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ! - -### npm ๊ธ€๋กœ๋ฒŒ ์„ค์น˜ - -```bash -npm install -g @dev-symphony/sym -``` - -### ๋ฐ”์ด๋„ˆ๋ฆฌ ๋‹ค์šด๋กœ๋“œ - -GitHub Releases ํŽ˜์ด์ง€์—์„œ ํ”Œ๋žซํผ์— ๋งž๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - -#### GPG ์„œ๋ช… ๊ฒ€์ฆ (๊ถŒ์žฅ) - -๋ฆด๋ฆฌ์Šค ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” GPG๋กœ ์„œ๋ช…๋ฉ๋‹ˆ๋‹ค. ๋‹ค์šด๋กœ๋“œํ•œ ํŒŒ์ผ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ๊ฒ€์ฆํ•˜๋ ค๋ฉด: - -```bash -# 1. GPG ๊ณต๊ฐœํ‚ค ๊ฐ€์ ธ์˜ค๊ธฐ (์ตœ์ดˆ 1ํšŒ) -gpg --keyserver keys.openpgp.org --recv-keys [GPG_KEY_ID] - -# 2. ์„œ๋ช… ๊ฒ€์ฆ -gpg --verify sym-linux-amd64.asc sym-linux-amd64 -``` - -์„œ๋ช…์ด ์œ ํšจํ•˜๋ฉด `Good signature from "DevSymphony"` ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. - -### ์†Œ์Šค์—์„œ ๋นŒ๋“œ - -```bash -# ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํด๋ก  -git clone https://github.com/DevSymphony/sym-cli.git -cd sym-cli - -# ์˜์กด์„ฑ ์„ค์น˜ ๋ฐ ๋นŒ๋“œ -make setup -make build - -# Windows์˜ ๊ฒฝ์šฐ bin/sym.exe ์ƒ์„ฑ๋จ -# Unix ๊ณ„์—ด์˜ ๊ฒฝ์šฐ bin/sym ์ƒ์„ฑ๋จ -``` - -### ์‹œ์Šคํ…œ์— ์„ค์น˜ - -```bash -# GOPATH/bin์— ์„ค์น˜ -make install - -# ๋˜๋Š” ์ง์ ‘ ์„ค์น˜ -go install github.com/DevSymphony/sym-cli/cmd/sym@latest -``` - -### PATH ์„ค์ • (Windows) - -```powershell -# ์‚ฌ์šฉ์ž ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์ถ”๊ฐ€ -[System.Environment]::SetEnvironmentVariable('Path', $env:Path + ';D:\Git\sym-cli\bin', 'User') -``` - ## ๋น ๋ฅธ ์‹œ์ž‘ -### MCP ์„œ๋ฒ„ ๋ชจ๋“œ (AI ์ฝ”๋”ฉ ๋„๊ตฌ์™€ ํ•จ๊ป˜) - -Symphony๋ฅผ MCP ์„œ๋ฒ„๋กœ ์‹คํ–‰ํ•˜์—ฌ Claude, Cursor, Continue.dev ๋“ฑ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ: +### 1. ์„ค์น˜ ```bash -# stdio ๋ชจ๋“œ (๊ธฐ๋ณธ - AI ๋„๊ตฌ ์—ฐ๋™์šฉ) -sym mcp - -# HTTP ๋ชจ๋“œ (๋””๋ฒ„๊น…/ํ…Œ์ŠคํŠธ์šฉ) -sym mcp --port 4000 - -# ์ปค์Šคํ…€ ์ •์ฑ… ํŒŒ์ผ ์ง€์ • -sym mcp --config ./custom-policy.json +npm i -g @dev-symphony/sym ``` -**Claude์—๊ฒŒ ๋ฌผ์–ด๋ณด๊ธฐ**: -- "์ด ํ”„๋กœ์ ํŠธ์˜ ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜์€ ๋ญ์•ผ?" -- "์ด ์ฝ”๋“œ๊ฐ€ ์ปจ๋ฒค์…˜์„ ์ง€ํ‚ค๋Š”์ง€ ๊ฒ€์ฆํ•ด์ค˜" -- "Go ์ฝ”๋“œ ์ž‘์„ฑ ์‹œ ์ฃผ์˜ํ•  ์ ์€?" - -MCP ์„ค์น˜ ๋ฐฉ๋ฒ•์€ [์„ค์น˜](#-์„ค์น˜) ์„น์…˜ ์ฐธ๊ณ . - ---- - -### 1. ์ดˆ๊ธฐ ์„ค์ • ๋ฐ ๋กœ๊ทธ์ธ +### 2. ์ดˆ๊ธฐํ™” ```bash -# ์„ค์ • (์„ ํƒ์‚ฌํ•ญ - ๊ธฐ๋ณธ ์„œ๋ฒ„ ์‚ฌ์šฉ ์‹œ ์ƒ๋žต ๊ฐ€๋Šฅ) -sym config - # GitHub OAuth ๋กœ๊ทธ์ธ sym login -# ํ˜„์žฌ ์‚ฌ์šฉ์ž ํ™•์ธ -sym whoami -``` - -### 2. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ดˆ๊ธฐํ™” -์ž์—ฐ์–ด ์ •์ฑ…์„ linter ์„ค์ • ํŒŒ์ผ๋กœ ์ž๋™ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. - -```bash -# ๋ชจ๋“  ์ง€์› linter ์„ค์ • ํŒŒ์ผ ์ƒ์„ฑ (์ถœ๋ ฅ: /.sym) -sym convert -i user-policy.json --targets all - -# JavaScript/TypeScript๋งŒ -sym convert -i user-policy.json --targets eslint - -# Java๋งŒ -sym convert -i user-policy.json --targets checkstyle,pmd - -# ์ƒ์„ฑ๋˜๋Š” ํŒŒ์ผ๋“ค: -# - .sym/.eslintrc.json (JavaScript/TypeScript) -# - .sym/checkstyle.xml (Java) -# - .sym/pmd-ruleset.xml (Java) -# - .sym/code-policy.json (๋‚ด๋ถ€ ๊ฒ€์ฆ์šฉ) -# - .sym/conversion-report.json -``` - -**์ฐธ๊ณ **: [Convert ๋ช…๋ น์–ด ์ƒ์„ธ ๊ฐ€์ด๋“œ](docs/CONVERT_USAGE.md) - -### 3. ์ฝ”๋“œ ๊ฒ€์ฆ - -์ž‘์„ฑํ•œ ์ฝ”๋“œ๊ฐ€ ์ปจ๋ฒค์…˜์„ ์ค€์ˆ˜ํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. - -```bash -# Git ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋กœ ์ด๋™ -cd /path/to/your/repo - -# ์—ญํ•  ๋ฐ ์ •์ฑ… ํŒŒ์ผ ์ดˆ๊ธฐํ™” (.sym/ ํด๋”์— ์ƒ์„ฑ) +# ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐํ™” (.sym/ ํด๋” ์ƒ์„ฑ, MCP ์ž๋™ ์„ค์ •) sym init - -# ์ƒ์„ฑ๋œ ํŒŒ์ผ ํ™•์ธ -cat .sym/roles.json -cat .sym/user-policy.json - -# Git์— ์ปค๋ฐ‹ ๋ฐ ํ‘ธ์‹œ -git add .sym/ -git commit -m "Initialize Symphony roles and policy" -git push ``` -### 3. ์›น ๋Œ€์‹œ๋ณด๋“œ ์‹คํ–‰ +> **์ฐธ๊ณ **: LLM ๊ธฐ๋ฐ˜ ์ปจ๋ฒค์…˜ ๋ณ€ํ™˜์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด `OPENAI_API_KEY` ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. -```bash -# ๋Œ€์‹œ๋ณด๋“œ ์‹œ์ž‘ (http://localhost:8787) -sym dashboard - -# ๋‹ค๋ฅธ ํฌํŠธ ์‚ฌ์šฉ -sym dashboard --port 8080 -``` - -### 4. ์—ญํ•  ํ™•์ธ - -```bash -# ๋‚ด ์—ญํ•  ํ™•์ธ -sym my-role - -# ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ์—ญํ•  ํ™•์ธ -sym my-role --user username -``` - -### 5. ์ •์ฑ… ๊ด€๋ฆฌ +### 3. ์‚ฌ์šฉ +**์›น ๋Œ€์‹œ๋ณด๋“œ๋กœ ์ปจ๋ฒค์…˜ ํŽธ์ง‘:** ```bash -# ์ •์ฑ… ํŒŒ์ผ ๊ฒฝ๋กœ ํ™•์ธ -sym policy path - -# ์ •์ฑ… ํŒŒ์ผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ -sym policy validate - -# ์ •์ฑ… ๋ณ€๊ฒฝ ํžˆ์Šคํ† ๋ฆฌ -sym policy history -``` - -## ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ - -``` -sym-cli/ -โ”œโ”€โ”€ cmd/sym/ # CLI ์ง„์ž…์  -โ”œโ”€โ”€ internal/ -โ”‚ โ”œโ”€โ”€ cmd/ # Cobra ์ปค๋งจ๋“œ ์ •์˜ -โ”‚ โ”‚ โ”œโ”€โ”€ config.go # ์„ค์ • ๊ด€๋ฆฌ -โ”‚ โ”‚ โ”œโ”€โ”€ login.go # OAuth ๋กœ๊ทธ์ธ -โ”‚ โ”‚ โ”œโ”€โ”€ logout.go # ๋กœ๊ทธ์•„์›ƒ -โ”‚ โ”‚ โ”œโ”€โ”€ init.go # ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ดˆ๊ธฐํ™” -โ”‚ โ”‚ โ”œโ”€โ”€ dashboard.go # ์›น ๋Œ€์‹œ๋ณด๋“œ -โ”‚ โ”‚ โ”œโ”€โ”€ my_role.go # ์—ญํ•  ์กฐํšŒ -โ”‚ โ”‚ โ”œโ”€โ”€ whoami.go # ์‚ฌ์šฉ์ž ์ •๋ณด -โ”‚ โ”‚ โ”œโ”€โ”€ policy.go # ์ •์ฑ… ๊ด€๋ฆฌ -โ”‚ โ”‚ โ”œโ”€โ”€ convert.go # ์Šคํ‚ค๋งˆ ๋ณ€ํ™˜ (๊ฐœ๋ฐœ ์ค‘) -โ”‚ โ”‚ โ””โ”€โ”€ validate.go # ์ฝ”๋“œ ๊ฒ€์ฆ (๊ฐœ๋ฐœ ์ค‘) -โ”‚ โ”œโ”€โ”€ auth/ # OAuth ์ธ์ฆ -โ”‚ โ”œโ”€โ”€ config/ # ์„ค์ • ๊ด€๋ฆฌ -โ”‚ โ”œโ”€โ”€ git/ # Git ์œ ํ‹ธ๋ฆฌํ‹ฐ -โ”‚ โ”œโ”€โ”€ github/ # GitHub API ํด๋ผ์ด์–ธํŠธ -โ”‚ โ”œโ”€โ”€ roles/ # ์—ญํ•  ๊ด€๋ฆฌ -โ”‚ โ”œโ”€โ”€ policy/ # ์ •์ฑ… ๋กœ๋”ฉ/ํŒŒ์‹ฑ -โ”‚ โ”œโ”€โ”€ server/ # ์›น ๋Œ€์‹œ๋ณด๋“œ ์„œ๋ฒ„ -โ”‚ โ”‚ โ””โ”€โ”€ static/ # HTML, CSS, JS (์ž„๋ฒ ๋””๋“œ) -โ”‚ โ”œโ”€โ”€ validator/ # ๊ฒ€์ฆ ๋กœ์ง (๊ฐœ๋ฐœ ์ค‘) -โ”‚ โ”œโ”€โ”€ converter/ # ์Šคํ‚ค๋งˆ ๋ณ€ํ™˜ (๊ฐœ๋ฐœ ์ค‘) -โ”‚ โ””โ”€โ”€ adapter/ # ESLint, Prettier ์–ด๋Œ‘ํ„ฐ -โ”œโ”€โ”€ pkg/ -โ”‚ โ””โ”€โ”€ schema/ # ์Šคํ‚ค๋งˆ ํƒ€์ž… ์ •์˜ -โ”œโ”€โ”€ tests/ # ํ…Œ์ŠคํŠธ -โ”œโ”€โ”€ .sym/ # ์ •์ฑ… ๋ฐ ์—ญํ•  ํŒŒ์ผ (gitignore) -โ”œโ”€โ”€ Makefile -โ””โ”€โ”€ README.md -``` - -## ๊ฐœ๋ฐœ - -### ํ•„์ˆ˜ ๋„๊ตฌ - -| ๋„๊ตฌ | ๋ฒ„์ „ | ์šฉ๋„ | -|------|------|------| -| Go | 1.21+ | CLI ๋นŒ๋“œ ๋ฐ ์‹คํ–‰ | -| Node.js & npm | 18+ | ESLint, Prettier, TSC ์–ด๋Œ‘ํ„ฐ | -| Java JDK | 21+ (์„ ํƒ) | Checkstyle, PMD ์–ด๋Œ‘ํ„ฐ | - -```bash -# ์„ค์น˜ ํ™•์ธ -go version # go1.21 ์ด์ƒ -node --version # v18 ์ด์ƒ -java -version # openjdk 21 ์ด์ƒ (Java ๊ฒ€์ฆ ์‹œ ํ•„์š”) -``` - -### ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ • - -```bash -# ๊ฐœ๋ฐœ ์˜์กด์„ฑ ์„ค์น˜ (Go tools, npm packages) -make setup - -# CSS ๊ฐ์‹œ ๋ชจ๋“œ (๊ฐœ๋ฐœ ์ค‘ ์ž๋™ ๋ฆฌ๋นŒ๋“œ) -make watch-css -``` - -### ์™ธ๋ถ€ ๋„๊ตฌ ์ž๋™ ์„ค์น˜ - -CLI๋Š” ๊ฒ€์ฆ ๋„๊ตฌ๋ฅผ ์ฒ˜์Œ ์‚ฌ์šฉํ•  ๋•Œ ์ž๋™์œผ๋กœ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค: - -``` -~/.symphony/tools/ -โ”œโ”€โ”€ node_modules/ # npm์œผ๋กœ ์„ค์น˜ -โ”‚ โ”œโ”€โ”€ eslint/ -โ”‚ โ”œโ”€โ”€ prettier/ -โ”‚ โ””โ”€โ”€ typescript/ -โ”œโ”€โ”€ checkstyle-10.26.1.jar # Maven Central์—์„œ ๋‹ค์šด๋กœ๋“œ -โ””โ”€โ”€ pmd-/ # GitHub Releases์—์„œ ๋‹ค์šด๋กœ๋“œ -``` - -### ๋นŒ๋“œ - -```bash -# ํ˜„์žฌ ํ”Œ๋žซํผ์šฉ ๋นŒ๋“œ (CSS ์ž๋™ ๋นŒ๋“œ ํฌํ•จ) -make build - -# ๋ชจ๋“  ํ”Œ๋žซํผ์šฉ ๋นŒ๋“œ -make build-all - -# CSS๋งŒ ๋นŒ๋“œ -make build-css -``` - -### ํ…Œ์ŠคํŠธ - -```bash -# ์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰ (์ปค๋ฒ„๋ฆฌ์ง€ ๋ฆฌํฌํŠธ ์ƒ์„ฑ) -make test - -# ํŠน์ • ํŒจํ‚ค์ง€ ํ…Œ์ŠคํŠธ -go test ./internal/adapter/... -v - -# ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ -go test ./tests/integration/... -v -``` - -ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ๋ฆฌํฌํŠธ๋Š” [์—ฌ๊ธฐ](https://devsymphony.github.io/sym-cli/coverage.html)์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - -### ์ฝ”๋“œ ํ’ˆ์งˆ - -```bash -# ํฌ๋งทํŒ… -make fmt - -# ๋ฆฐํŒ… -make lint - -# ์˜์กด์„ฑ ์ •๋ฆฌ -make tidy - -# ํด๋ฆฐ์—… -make clean -``` - -### ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… - -**Java ํ…Œ์ŠคํŠธ ์‹คํŒจ ("java not found")** -```bash -# Ubuntu/Debian -sudo apt-get install -y default-jdk - -# macOS -brew install openjdk@21 -``` - -**ESLint/Prettier ์„ค์น˜ ์‹คํŒจ** -```bash -npm --version # npm ์„ค์น˜ ํ™•์ธ -cd ~/.symphony/tools && npm install eslint@^8.0.0 prettier@latest +sym dashboard +# http://localhost:8787 ์—์„œ ์—ญํ• /์ปจ๋ฒค์…˜ ํŽธ์ง‘ ``` -## ํ™˜๊ฒฝ ๋ณ€์ˆ˜ - -```bash -# ์ธ์ฆ ๋ชจ๋“œ (๊ธฐ๋ณธ๊ฐ’: server) -export SYM_AUTH_MODE=server - -# Symphony ์ธ์ฆ ์„œ๋ฒ„ URL -export SYM_SERVER_URL=https://symphony-server-98207.web.app -``` +## ์ฃผ์š” ๊ธฐ๋Šฅ -## ํŒŒ์ผ ๋ฐ ๋””๋ ‰ํ† ๋ฆฌ +- ์ž์—ฐ์–ด๋กœ ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜ ์ •์˜ (`.sym/user-policy.json`) +- RBAC ๊ธฐ๋ฐ˜ ํŒŒ์ผ ์ ‘๊ทผ ๊ถŒํ•œ ๊ด€๋ฆฌ +- LLM์œผ๋กœ ์ž์—ฐ์–ด ๊ทœ์น™์„ ESLint/Checkstyle/PMD ์„ค์ •์œผ๋กœ ๋ณ€ํ™˜ +- MCP ์„œ๋ฒ„๋กœ Claude, Cursor ๋“ฑ AI ๋„๊ตฌ์™€ ์—ฐ๋™ +- ์›น ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์‹œ๊ฐ์ ์œผ๋กœ ์ •์ฑ… ํŽธ์ง‘ -### ์„ค์ • ํŒŒ์ผ ์œ„์น˜ +## MCP ์„ค์ • -- **์„ค์ •**: `~/.config/sym/config.json` -- **ํ† ํฐ**: `~/.config/sym/token.json` -- **์—ญํ• **: `.sym/roles.json` (ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ) -- **์ •์ฑ…**: `.sym/user-policy.json` (ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ) +`sym init` ์‹คํ–‰ ์‹œ MCP๊ฐ€ ์ž๋™์œผ๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. -### .sym/roles.json ์˜ˆ์‹œ +์ˆ˜๋™ ์„ค์ •์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ: ```json { - "admin": ["alice", "bob"], - "developer": ["charlie", "david"], - "viewer": ["eve"] + "mcpServers": { + "symphony": { + "command": "npx", + "args": ["-y", "@dev-symphony/sym@latest", "mcp"] + } + } } ``` -### .sym/user-policy.json ์˜ˆ์‹œ +## CLI ๋ช…๋ น์–ด + +| ๋ช…๋ น์–ด | ์„ค๋ช… | +|--------|------| +| `sym login` | GitHub OAuth ๋กœ๊ทธ์ธ | +| `sym init` | ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐํ™” (.sym/ ์ƒ์„ฑ) | +| `sym dashboard` | ์›น ๋Œ€์‹œ๋ณด๋“œ ์‹คํ–‰ (ํฌํŠธ 8787) | +| `sym my-role` | ๋‚ด ์—ญํ•  ํ™•์ธ | +| `sym policy validate` | ์ •์ฑ… ํŒŒ์ผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ | +| `sym convert -i user-policy.json --targets all` | ์ปจ๋ฒค์…˜์„ linter ์„ค์ •์œผ๋กœ ๋ณ€ํ™˜ | +| `sym mcp` | MCP ์„œ๋ฒ„ ์‹คํ–‰ | + +## ์ •์ฑ… ํŒŒ์ผ ์˜ˆ์‹œ + +`.sym/user-policy.json`: ```json { "version": "1.0.0", "rbac": { "roles": { - "admin": { - "allowWrite": ["**/*"], - "canEditPolicy": true, - "canEditRoles": true - }, - "developer": { - "allowWrite": ["src/**", "tests/**", "docs/**"], - "denyWrite": [".sym/**", "config/**"], - "canEditPolicy": false, - "canEditRoles": false - } + "admin": { "allowWrite": ["**/*"] }, + "developer": { "allowWrite": ["src/**"], "denyWrite": [".sym/**"] } } }, - "defaults": { - "languages": ["go", "javascript"], - "severity": "error", - "autofix": true - }, "rules": [ - { - "no": 1, - "say": "ํŒจํ‚ค์ง€ ์ด๋ฆ„์€ ์†Œ๋ฌธ์ž ํ•œ ๋‹จ์–ด๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค", - "category": "naming", - "example": "// Good: package user\n// Bad: package UserManagement" - } + { "say": "ํด๋ž˜์Šค ์ด๋ฆ„์€ PascalCase", "category": "naming" }, + { "say": "ํ•œ ์ค„์€ 100์ž ์ดํ•˜", "category": "formatting" } ] } ``` -## ์‚ฌ์šฉ ์‚ฌ๋ก€ +`.sym/roles.json`: -### ํŒ€ ํ˜‘์—… ์‹œ๋‚˜๋ฆฌ์˜ค - -1. **ํ”„๋กœ์ ํŠธ ๊ด€๋ฆฌ์ž**๊ฐ€ `sym init`์œผ๋กœ ์—ญํ• /์ •์ฑ… ์„ค์ • -2. ํŒ€์›๋“ค์ด ์ €์žฅ์†Œ ํด๋ก  ํ›„ `sym login`์œผ๋กœ ์ธ์ฆ -3. `sym my-role`๋กœ ์ž์‹ ์˜ ๊ถŒํ•œ ํ™•์ธ -4. `sym dashboard`๋กœ ์ •์ฑ… ๋ฐ ์—ญํ•  ๊ด€๋ฆฌ -5. Git์œผ๋กœ ๋ณ€๊ฒฝ์‚ฌํ•ญ ์ถ”์  ๋ฐ ์ด๋ ฅ ๊ด€๋ฆฌ - -### ์ •์ฑ… ํŽธ์ง‘ ์‹œ๋‚˜๋ฆฌ์˜ค - -1. ์›น ๋Œ€์‹œ๋ณด๋“œ ์‹คํ–‰: `sym dashboard` -2. ๋ธŒ๋ผ์šฐ์ €์—์„œ `http://localhost:8787` ์ ‘์† -3. ์—ญํ•  ๋ฐ ๊ถŒํ•œ ์„ค์ • -4. ์ฝ”๋”ฉ ๊ทœ์น™ ์ถ”๊ฐ€/์ˆ˜์ • -5. ํ…œํ”Œ๋ฆฟ ์ ์šฉ (React, Vue, Node.js ๋“ฑ) -6. ์ž๋™ ์ €์žฅ ํ™œ์„ฑํ™” (30์ดˆ๋งˆ๋‹ค) -7. Git ์ปค๋ฐ‹ ๋ฐ ํ‘ธ์‹œ - -## ๋ผ์ด์„ ์Šค - -MIT License - -## ๊ธฐ์—ฌ - -Contributions are welcome! Please feel free to submit a Pull Request. +```json +{ + "admin": ["alice"], + "developer": ["bob", "charlie"] +} +``` -## ์ง€์› +## ๊ฐœ๋ฐœ -- GitHub Issues: [https://github.com/DevSymphony/sym-cli/issues](https://github.com/DevSymphony/sym-cli/issues) - ---- - -**Note:** ์ฝ”๋“œ ๊ฒ€์ฆ ๊ธฐ๋Šฅ (`convert`, `validate`, `export`)์€ ํ˜„์žฌ ๊ฐœ๋ฐœ ์ค‘์ž…๋‹ˆ๋‹ค. - -## ํŒจํ‚ค์ง€ ๊ตฌ์กฐ ๋ฐ ์˜์กด์„ฑ - -```mermaid -graph TB - subgraph "๋ฉ”์ธ ์ง„์ž…์ " - main[cmd/sym
main] - end - - subgraph "CLI ๊ณ„์ธต" - cmd[internal/cmd
Cobra Commands] - end - - subgraph "์ค‘์•™ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ" - schema[pkg/schema
Types] - end - - subgraph "๊ธฐ๋ณธ ์œ ํ‹ธ๋ฆฌํ‹ฐ" - config[internal/config] - git[internal/git] - github[internal/github] - llm[internal/llm] - end - - subgraph "๋„๋ฉ”์ธ ๊ณ„์ธต" - auth[internal/auth] - - subgraph converter_group["internal/converter"] - converter[converter] - conv_linters[linters] - end - - policy[internal/policy] - end - - subgraph "๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง" - roles[internal/roles] - - subgraph adapter_group["internal/adapter"] - adapter[adapter] - adapter_eslint[eslint] - adapter_prettier[prettier] - adapter_tsc[tsc] - adapter_checkstyle[checkstyle] - adapter_pmd[pmd] - adapter_registry[registry] - end - - subgraph engine_group["internal/engine"] - engine[engine] - engine_core[core] - engine_registry[registry] - engine_pattern[pattern] - engine_length[length] - engine_style[style] - engine_ast[ast] - engine_llm[llm engine] - engine_typechecker[typechecker] - end - - validator[internal/validator] - end - - subgraph "ํ†ตํ•ฉ ๊ณ„์ธต" - mcp[internal/mcp] - server[internal/server] - end - - %% main ์˜์กด์„ฑ - main --> cmd - - %% cmd ์˜์กด์„ฑ - cmd --> auth - cmd --> config - cmd --> converter - cmd --> git - cmd --> github - cmd --> llm - cmd --> mcp - cmd --> policy - cmd --> roles - cmd --> server - cmd --> validator - cmd --> schema - - %% auth ์˜์กด์„ฑ - auth --> config - auth --> github - - %% converter ์˜์กด์„ฑ - converter --> llm - converter --> schema - conv_linters --> converter - - %% policy ์˜์กด์„ฑ - policy --> git - policy --> schema - - %% roles ์˜์กด์„ฑ - roles --> git - roles --> policy - roles --> schema - - %% adapter ์„œ๋ธŒํŒจํ‚ค์ง€ - adapter_eslint --> adapter - adapter_prettier --> adapter - adapter_tsc --> adapter - adapter_checkstyle --> adapter - adapter_pmd --> adapter - adapter_registry --> adapter - adapter --> engine_core - - %% engine ์„œ๋ธŒํŒจํ‚ค์ง€ - engine_pattern --> engine_core - engine_pattern --> adapter_eslint - engine_length --> engine_core - engine_length --> adapter_eslint - engine_style --> engine_core - engine_style --> adapter_eslint - engine_style --> adapter_prettier - engine_ast --> engine_core - engine_ast --> adapter_eslint - engine_ast --> adapter_checkstyle - engine_ast --> adapter_pmd - engine_llm --> engine_core - engine_llm --> llm - engine_typechecker --> engine_core - engine_typechecker --> adapter_tsc - engine_registry --> engine_core - engine --> engine_registry - - %% validator ์˜์กด์„ฑ - validator --> engine - validator --> llm - validator --> roles - validator --> git - validator --> schema - - %% mcp ์˜์กด์„ฑ - mcp --> converter - mcp --> git - mcp --> llm - mcp --> policy - mcp --> validator - mcp --> schema - - %% server ์˜์กด์„ฑ - server --> config - server --> git - server --> github - server --> policy - server --> roles - server --> schema - - %% llm์˜ schema ์˜์กด์„ฑ - llm --> schema - - classDef mainEntry fill:#e03131,stroke:#a61e4d,color:#fff,stroke-width:3px - classDef cliLayer fill:#ff6b6b,stroke:#c92a2a,color:#fff - classDef core fill:#20c997,stroke:#087f5b,color:#fff - classDef leaf fill:#51cf66,stroke:#2f9e44,color:#fff - classDef domain fill:#74c0fc,stroke:#1971c2,color:#fff - classDef business fill:#ffd43b,stroke:#f08c00,color:#000 - classDef integration fill:#da77f2,stroke:#9c36b5,color:#fff - classDef subpkg fill:#f8f9fa,stroke:#868e96,color:#000 - - class main mainEntry - class cmd cliLayer - class schema core - class config,git,github,llm leaf - class auth,converter,policy domain - class roles,adapter,engine,validator business - class mcp,server integration - class adapter_eslint,adapter_prettier,adapter_tsc,adapter_checkstyle,adapter_pmd,adapter_registry,conv_linters subpkg - class engine_core,engine_registry,engine_pattern,engine_length,engine_style,engine_ast,engine_llm,engine_typechecker subpkg +```bash +make setup # ์˜์กด์„ฑ ์„ค์น˜ +make build # ๋นŒ๋“œ +make test # ํ…Œ์ŠคํŠธ +make lint # ๋ฆฐํŠธ ``` -### ํŒจํ‚ค์ง€ ๊ณ„์ธต ๊ตฌ์กฐ - -**๋ฉ”์ธ ์ง„์ž…์ ** -- `cmd/sym`: main ํŒจํ‚ค์ง€ (โ†’ internal/cmd) +**ํ•„์ˆ˜ ๋„๊ตฌ:** Go 1.21+, Node.js 18+ -**CLI ๊ณ„์ธต** -- `internal/cmd`: Cobra ๊ธฐ๋ฐ˜ CLI ์ปค๋งจ๋“œ ๊ตฌํ˜„ (โ†’ ๋ชจ๋“  internal ํŒจํ‚ค์ง€) +## ๋ฌธ์„œ -**์ค‘์•™ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ** -- `pkg/schema`: UserPolicy(A Schema) ๋ฐ CodePolicy(B Schema) ํƒ€์ž… ์ •์˜ +- [Convert ๊ธฐ๋Šฅ ๊ฐ€์ด๋“œ](docs/CONVERT_FEATURE.md) +- [Convert ์‚ฌ์šฉ๋ฒ•](docs/CONVERT_USAGE.md) +- [Linter ๊ฒ€์ฆ](docs/LINTER_VALIDATION.md) -**Tier 0: ๊ธฐ๋ณธ ์œ ํ‹ธ๋ฆฌํ‹ฐ** (์˜์กด์„ฑ ์—†์Œ) -- `internal/config`: ์ „์—ญ ์„ค์ • ๋ฐ ํ† ํฐ ๊ด€๋ฆฌ -- `internal/git`: Git ์ €์žฅ์†Œ ์ž‘์—… -- `internal/github`: GitHub API ํด๋ผ์ด์–ธํŠธ -- `internal/llm`: OpenAI API ํด๋ผ์ด์–ธํŠธ (โ†’ schema) +## ๋ผ์ด์„ ์Šค -**Tier 1: ๋„๋ฉ”์ธ ๊ณ„์ธต** -- `internal/auth`: GitHub OAuth ์ธ์ฆ (โ†’ config, github) -- `internal/converter`: ์ •์ฑ… ๋ณ€ํ™˜ (โ†’ llm, schema) -- `internal/policy`: ์ •์ฑ… ํŒŒ์ผ ๊ด€๋ฆฌ (โ†’ git, schema) +MIT License -**Tier 2: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง** -- `internal/roles`: RBAC ๊ตฌํ˜„ (โ†’ git, policy, schema) -- `internal/adapter` โ†” `internal/engine`: ๊ฒ€์ฆ ๋„๊ตฌ ์–ด๋Œ‘ํ„ฐ ๋ฐ ์—”์ง„ (์ˆœํ™˜ ์˜์กด์„ฑ) - - Adapters: ESLint, Prettier, TSC, Checkstyle, PMD - - Engines: Pattern, Length, Style, AST, LLM, TypeChecker -- `internal/validator`: ๊ฒ€์ฆ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ (โ†’ engine, llm, roles, git, schema) +## ์ง€์› -**Tier 3: ํ†ตํ•ฉ ๊ณ„์ธต** -- `internal/mcp`: MCP ์„œ๋ฒ„ (โ†’ converter, git, llm, policy, validator, schema) -- `internal/server`: ์›น ๋Œ€์‹œ๋ณด๋“œ (โ†’ config, git, github, policy, roles, schema) +- GitHub Issues: https://github.com/DevSymphony/sym-cli/issues From 3daee3c153767416f8affd766afa97c18ac9a76b Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 04:03:25 +0000 Subject: [PATCH 25/26] docs: add quick start with sym login/init --- npm/README.md | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/npm/README.md b/npm/README.md index 26c9526..fa0588f 100644 --- a/npm/README.md +++ b/npm/README.md @@ -2,29 +2,24 @@ LLM-friendly convention linter for AI coding tools. -## Installation - -### One-line MCP Setup - -```bash -claude mcp add symphony npx @dev-symphony/sym@latest mcp -``` - -### Direct Installation +## Quick Start ```bash +# 1. Install npm install -g @dev-symphony/sym + +# 2. Initialize (GitHub OAuth login + MCP auto-setup) +sym login +sym init ``` -## Usage +> **Note**: `OPENAI_API_KEY` environment variable is required for LLM-based convention conversion. -### MCP Configuration +## MCP Configuration -Add to your MCP config file: +MCP is auto-configured during `sym init`. -- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` -- Windows: `%APPDATA%/Claude/claude_desktop_config.json` -- Linux: `~/.config/Claude/claude_desktop_config.json` +For manual setup: ```json { From ad951d3d463fdecf183e30f5a27782e178729be3 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 25 Nov 2025 04:05:38 +0000 Subject: [PATCH 26/26] chore: bump version to 0.1.5 in package.json --- npm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/package.json b/npm/package.json index 378c497..7d5bb41 100644 --- a/npm/package.json +++ b/npm/package.json @@ -1,6 +1,6 @@ { "name": "@dev-symphony/sym", - "version": "0.1.4", + "version": "0.1.5", "description": "Symphony - LLM-friendly convention linter for AI coding assistants", "keywords": [ "mcp",