Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ coverage.txt
!npm/bin/
!npm/bin/*.js
npm/bin/sym-*

CLAUDE.md
AGENTS.md
1 change: 1 addition & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ AI 코딩 도구(Claude Code, Cursor 등)와 stdio를 통해 통신합니다.
|------|-------------|
| `query_conventions` | 프로젝트 컨벤션 조회 |
| `validate_code` | 코드 변경사항 검증 |
| `list_category` | 카테고리 목록 조회 |

#### HTTP Server (`internal/server`)

Expand Down
106 changes: 105 additions & 1 deletion docs/COMMAND.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Symphony (`sym`)는 코드 컨벤션 관리와 RBAC(역할 기반 접근 제어)
- [sym policy validate](#sym-policy-validate)
- [sym convert](#sym-convert)
- [sym validate](#sym-validate)
- [sym category](#sym-category)
- [sym mcp](#sym-mcp)
- [sym llm](#sym-llm)
- [sym llm status](#sym-llm-status)
Expand All @@ -39,6 +40,7 @@ Symphony (`sym`)는 코드 컨벤션 관리와 RBAC(역할 기반 접근 제어)
- [MCP 도구 스키마](#mcp-도구-스키마)
- [query\_conventions](#query_conventions)
- [validate\_code](#validate_code)
- [list\_category](#list_category)
- [등록 방법](#등록-방법)
- [LLM 프로바이더](#llm-프로바이더)
- [지원 프로바이더](#지원-프로바이더)
Expand Down Expand Up @@ -104,6 +106,7 @@ sym
│ └── validate # 정책 파일 유효성 검사
├── convert # 정책 → 린터 설정 변환
├── validate # Git 변경사항 검증
├── category # 카테고리 목록 조회
├── mcp # MCP 서버 실행
├── llm # LLM 프로바이더 관리
│ ├── status # 현재 설정 확인
Expand All @@ -124,7 +127,7 @@ sym

**수행 작업**:
1. `.sym/roles.json` 생성 (기본 역할: admin, developer, viewer)
2. `.sym/user-policy.json` 생성 (기본 RBAC 설정)
2. `.sym/user-policy.json` 생성 (기본 카테고리 7개 + RBAC 설정)
3. `.sym/config.json` 생성 (기본 설정)
4. 역할을 admin으로 설정 (대시보드에서 변경 가능)
5. MCP 서버 등록 (선택적)
Expand Down Expand Up @@ -367,13 +370,76 @@ sym validate --timeout 60

---

### sym category

**설명**: 사용 가능한 모든 컨벤션 카테고리와 설명을 표시합니다.

user-policy.json에 정의된 카테고리를 표시합니다. `sym init` 실행 시 7개의 기본 카테고리(security, style, documentation, error_handling, architecture, performance, testing)가 생성됩니다. 사용자는 이 카테고리를 수정, 삭제하거나 새로운 카테고리를 추가할 수 있습니다.

**문법**:
```
sym category
```

**예시**:
```bash
# 카테고리 목록 조회
sym category
```

**출력 예시**:
```
[Convention Categories] 7 categories available

• security
Security rules (authentication, authorization, vulnerability prevention, etc.)

• style
Code style and formatting rules

• documentation
Documentation rules (comments, docstrings, etc.)

• error_handling
Error handling and exception management rules

• architecture
Code structure and architecture rules

• performance
Performance optimization rules

• testing
Testing rules (coverage, test patterns, etc.)
```

**사용자 정의 카테고리**:

user-policy.json에 `category` 필드를 추가하여 사용자 정의 카테고리를 추가하거나 기존 카테고리 설명을 변경할 수 있습니다:

```json
{
"version": "1.0",
"category": [
{"name": "security", "description": "보안 관련 규칙 (인증, 인가, 취약점 방지 등)"},
{"name": "naming", "description": "네이밍 컨벤션 규칙 (변수, 함수, 클래스 등)"}
],
"rules": [...]
}
```

**관련 파일**: `internal/cmd/category.go`

---

### sym mcp

**설명**: MCP(Model Context Protocol) 서버를 시작합니다. LLM 기반 코딩 도구가 stdio를 통해 컨벤션을 쿼리하고 코드를 검증할 수 있습니다.

**제공되는 MCP 도구**:
- `query_conventions`: 주어진 컨텍스트에 대한 컨벤션 쿼리
- `validate_code`: 코드의 컨벤션 준수 여부 검증
- `list_category`: 사용 가능한 카테고리 목록 조회

**통신 방식**: stdio (Claude Desktop, Claude Code, Cursor 등 MCP 클라이언트와 통합)

Expand Down Expand Up @@ -622,6 +688,44 @@ Git 변경사항을 프로젝트 컨벤션에 대해 검증합니다.
|----------|------|------|------|
| `role` | string | 아니오 | 검증용 RBAC 역할 (선택) |

#### list_category

사용 가능한 모든 카테고리와 설명을 반환합니다.

user-policy.json에 정의된 카테고리를 반환합니다. 카테고리가 없으면 `sym init`을 실행하라는 안내 메시지를 반환합니다.

**입력 스키마**:

파라미터 없음 (모든 카테고리 반환)

**출력 예시**:
```
Available categories (7):

• security
Security rules (authentication, authorization, vulnerability prevention, etc.)

• style
Code style and formatting rules

• documentation
Documentation rules (comments, docstrings, etc.)

• error_handling
Error handling and exception management rules

• architecture
Code structure and architecture rules

• performance
Performance optimization rules

• testing
Testing rules (coverage, test patterns, etc.)

Use query_conventions with a specific category to get rules for that category.
```

### 등록 방법

```bash
Expand Down
3 changes: 3 additions & 0 deletions internal/cmd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ cmd/
├── llm.go # sym llm status|test|setup 명령어 (LLM 관리)
├── mcp.go # sym mcp 명령어 (MCP 서버)
├── mcp_register.go # MCP 서버 등록 헬퍼 함수
├── category.go # sym category 명령어 (카테고리 목록)
├── survey_templates.go # 커스텀 survey UI 템플릿
└── README.md
```
Expand Down Expand Up @@ -84,6 +85,7 @@ cmd/
| `llmTestCmd` | llm.go:40 | llm test 명령어 |
| `llmSetupCmd` | llm.go:47 | llm setup 명령어 |
| `mcpCmd` | mcp.go:15 | mcp 명령어 |
| `categoryCmd` | category.go:10 | category 명령어 |

#### 명령어 실행 함수

Expand All @@ -100,6 +102,7 @@ cmd/
| `runLLMTest(cmd, args)` | llm.go:107 | llm test 실행 |
| `runLLMSetup(cmd, args)` | llm.go:142 | llm setup 실행 |
| `runMCP(cmd, args)` | mcp.go:37 | mcp 실행 |
| `runCategory(cmd, args)` | category.go:27 | category 실행 |

#### 헬퍼 함수 - 초기화

Expand Down
52 changes: 52 additions & 0 deletions internal/cmd/category.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package cmd

import (
"fmt"

"github.com/DevSymphony/sym-cli/internal/roles"
"github.com/spf13/cobra"
)

var categoryCmd = &cobra.Command{
Use: "category",
Short: "List all available convention categories",
Long: `List all convention categories with their descriptions.

Categories are defined in user-policy.json and can be customized by the user.
Run 'sym init' to create default categories (security, style, documentation,
error_handling, architecture, performance, testing).

You can add, remove, or modify categories directly in user-policy.json.`,
RunE: runCategory,
}

func init() {
rootCmd.AddCommand(categoryCmd)
}

func runCategory(cmd *cobra.Command, args []string) error {
// Load categories from user-policy.json
userPolicy, err := roles.LoadUserPolicyFromRepo()
if err != nil {
printWarn("Failed to load user-policy.json")
fmt.Println("Run 'sym init' to create default categories")
return nil
}

categories := userPolicy.Category
if len(categories) == 0 {
printWarn("No categories defined in user-policy.json")
fmt.Println("Run 'sym init' to create default categories")
return nil
}

printTitle("Convention Categories", fmt.Sprintf("%d categories available", len(categories)))
fmt.Println()

for _, cat := range categories {
fmt.Printf(" %s %s\n", colorize(bold, "•"), colorize(cyan, cat.Name))
fmt.Printf(" %s\n\n", cat.Description)
}

return nil
}
11 changes: 10 additions & 1 deletion internal/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,18 @@ func createDefaultPolicy() error {
return nil
}

// Create default policy with admin, developer, viewer RBAC roles
// Create default policy with categories and RBAC roles
defaultPolicy := &schema.UserPolicy{
Version: "1.0.0",
Category: []schema.CategoryDef{
{Name: "security", Description: "Security rules (authentication, authorization, vulnerability prevention, etc.)"},
{Name: "style", Description: "Code style and formatting rules"},
{Name: "documentation", Description: "Documentation rules (comments, docstrings, etc.)"},
{Name: "error_handling", Description: "Error handling and exception management rules"},
{Name: "architecture", Description: "Code structure and architecture rules"},
{Name: "performance", Description: "Performance optimization rules"},
{Name: "testing", Description: "Testing rules (coverage, test patterns, etc.)"},
},
RBAC: &schema.UserRBAC{
Roles: map[string]schema.UserRole{
"admin": {
Expand Down
3 changes: 2 additions & 1 deletion internal/mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ mcp/
| `RPCError` | server.go:184 | JSON-RPC 에러 타입 |
| `QueryConventionsInput` | server.go:190 | query_conventions 입력 스키마 |
| `ValidateCodeInput` | server.go:196 | validate_code 입력 스키마 |
| `ListCategoryInput` | server.go:202 | list_category 입력 스키마 |
| `QueryConventionsRequest` | server.go:244 | 컨벤션 조회 요청 |
| `ConventionItem` | server.go:250 | 컨벤션 항목 |
| `ValidateCodeRequest` | server.go:411 | 검증 요청 |
Expand Down Expand Up @@ -92,5 +93,5 @@ mcp/

## 참고 문헌

- [MCP 도구 스키마](../../docs/COMMAND.md#mcp-도구-스키마) - query_conventions, validate_code 입력/출력 스펙
- [MCP 도구 스키마](../../docs/COMMAND.md#mcp-도구-스키마) - query_conventions, validate_code, list_category 입력/출력 스펙
- [MCP 통합 가이드](../../docs/COMMAND.md#mcp-통합) - 지원 도구 및 등록 방법
49 changes: 48 additions & 1 deletion internal/mcp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (s *Server) Start() error {
}

fmt.Fprintln(os.Stderr, "Symphony MCP server started (stdio mode)")
fmt.Fprintln(os.Stderr, "Available tools: query_conventions, validate_code")
fmt.Fprintln(os.Stderr, "Available tools: query_conventions, validate_code, list_category")

// Use official MCP go-sdk for stdio to ensure spec-compliant framing and lifecycle
return s.runStdioWithSDK(context.Background())
Expand All @@ -197,6 +197,11 @@ type ValidateCodeInput struct {
Role string `json:"role,omitempty" jsonschema:"RBAC role for validation (optional)"`
}

// ListCategoryInput represents the input schema for the list_category tool (go-sdk).
type ListCategoryInput struct {
// No parameters - returns all categories
}

// runStdioWithSDK runs a spec-compliant MCP server over stdio using the official go-sdk.
func (s *Server) runStdioWithSDK(ctx context.Context) error {
server := sdkmcp.NewServer(&sdkmcp.Implementation{
Expand Down Expand Up @@ -236,6 +241,18 @@ func (s *Server) runStdioWithSDK(ctx context.Context) error {
return nil, result.(map[string]any), nil
})

// Tool: list_category
sdkmcp.AddTool(server, &sdkmcp.Tool{
Name: "list_category",
Description: "List all available convention categories with descriptions. Use this to discover what categories exist before querying conventions.",
}, func(ctx context.Context, req *sdkmcp.CallToolRequest, input ListCategoryInput) (*sdkmcp.CallToolResult, map[string]any, error) {
result, rpcErr := s.handleListCategory()
if rpcErr != nil {
return &sdkmcp.CallToolResult{IsError: true}, nil, fmt.Errorf("%s", rpcErr.Message)
}
return nil, result.(map[string]any), nil
})

// Run the server over stdio until the client disconnects
return server.Run(ctx, &sdkmcp.StdioTransport{})
}
Expand Down Expand Up @@ -808,3 +825,33 @@ func (s *Server) saveValidationResults(result *validator.ValidationResult, viola
fmt.Fprintf(os.Stderr, "✓ Validation results saved to %s\n", resultsPath)
return nil
}

// handleListCategory handles category listing requests.
func (s *Server) handleListCategory() (interface{}, *RPCError) {
category := s.getCategory()

var textContent string
if len(category) == 0 {
textContent = "No categories defined in user-policy.json.\n\nRun 'sym init' to create default categories."
} else {
textContent = fmt.Sprintf("Available categories (%d):\n\n", len(category))
for _, cat := range category {
textContent += fmt.Sprintf("• %s\n %s\n\n", cat.Name, cat.Description)
}
textContent += "Use query_conventions with a specific category to get rules for that category."
}

return map[string]interface{}{
"content": []map[string]interface{}{
{"type": "text", "text": textContent},
},
}, nil
}

// getCategory returns categories from user-policy.json.
func (s *Server) getCategory() []schema.CategoryDef {
if s.userPolicy != nil {
return s.userPolicy.Category
}
return nil
}
Loading