@su-record/vibe 2.4.72 → 2.4.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/CLAUDE.md +216 -215
  2. package/README.md +4 -4
  3. package/agents/research/best-practices-agent.md +13 -13
  4. package/agents/research/codebase-patterns-agent.md +33 -33
  5. package/agents/research/framework-docs-agent.md +23 -23
  6. package/agents/research/security-advisory-agent.md +29 -29
  7. package/agents/review/architecture-reviewer.md +31 -31
  8. package/agents/review/complexity-reviewer.md +21 -21
  9. package/agents/review/data-integrity-reviewer.md +29 -29
  10. package/agents/review/git-history-reviewer.md +24 -24
  11. package/agents/review/performance-reviewer.md +29 -29
  12. package/agents/review/python-reviewer.md +53 -53
  13. package/agents/review/rails-reviewer.md +40 -40
  14. package/agents/review/react-reviewer.md +40 -40
  15. package/agents/review/security-reviewer.md +29 -29
  16. package/agents/review/simplicity-reviewer.md +24 -24
  17. package/agents/review/test-coverage-reviewer.md +31 -31
  18. package/agents/review/typescript-reviewer.md +41 -41
  19. package/commands/vibe.analyze.md +103 -7
  20. package/commands/vibe.reason.md +106 -0
  21. package/commands/vibe.review.md +123 -38
  22. package/commands/vibe.run.md +286 -223
  23. package/commands/vibe.spec.md +425 -186
  24. package/commands/vibe.utils.md +104 -3
  25. package/commands/vibe.verify.md +179 -86
  26. package/dist/cli/detect.js +40 -40
  27. package/dist/cli/detect.js.map +1 -1
  28. package/dist/cli/index.d.ts +1 -1
  29. package/dist/cli/index.js +1 -1
  30. package/dist/cli/llm.js +5 -5
  31. package/dist/cli/llm.js.map +1 -1
  32. package/dist/cli/setup.js +3 -3
  33. package/dist/cli/setup.js.map +1 -1
  34. package/dist/lib/ContextCompressor.js +1 -1
  35. package/dist/lib/ContextCompressor.js.map +1 -1
  36. package/dist/lib/MemoryManager.d.ts +13 -155
  37. package/dist/lib/MemoryManager.d.ts.map +1 -1
  38. package/dist/lib/MemoryManager.js +52 -617
  39. package/dist/lib/MemoryManager.js.map +1 -1
  40. package/dist/lib/gemini-api.js +12 -12
  41. package/dist/lib/gemini-api.js.map +1 -1
  42. package/dist/lib/gemini-oauth.js +22 -22
  43. package/dist/lib/gemini-oauth.js.map +1 -1
  44. package/dist/lib/gemini-storage.js +3 -3
  45. package/dist/lib/gemini-storage.js.map +1 -1
  46. package/dist/lib/gpt-api.js +11 -11
  47. package/dist/lib/gpt-api.js.map +1 -1
  48. package/dist/lib/gpt-oauth.js +28 -28
  49. package/dist/lib/gpt-oauth.js.map +1 -1
  50. package/dist/lib/gpt-storage.js +3 -3
  51. package/dist/lib/gpt-storage.js.map +1 -1
  52. package/dist/lib/memory/KnowledgeGraph.d.ts +34 -0
  53. package/dist/lib/memory/KnowledgeGraph.d.ts.map +1 -0
  54. package/dist/lib/memory/KnowledgeGraph.js +216 -0
  55. package/dist/lib/memory/KnowledgeGraph.js.map +1 -0
  56. package/dist/lib/memory/KnowledgeGraph.test.d.ts +2 -0
  57. package/dist/lib/memory/KnowledgeGraph.test.d.ts.map +1 -0
  58. package/dist/lib/memory/KnowledgeGraph.test.js +189 -0
  59. package/dist/lib/memory/KnowledgeGraph.test.js.map +1 -0
  60. package/dist/lib/memory/MemorySearch.d.ts +25 -0
  61. package/dist/lib/memory/MemorySearch.d.ts.map +1 -0
  62. package/dist/lib/memory/MemorySearch.js +85 -0
  63. package/dist/lib/memory/MemorySearch.js.map +1 -0
  64. package/dist/lib/memory/MemorySearch.test.d.ts +2 -0
  65. package/dist/lib/memory/MemorySearch.test.d.ts.map +1 -0
  66. package/dist/lib/memory/MemorySearch.test.js +149 -0
  67. package/dist/lib/memory/MemorySearch.test.js.map +1 -0
  68. package/dist/lib/memory/MemoryStorage.d.ts +77 -0
  69. package/dist/lib/memory/MemoryStorage.d.ts.map +1 -0
  70. package/dist/lib/memory/MemoryStorage.js +278 -0
  71. package/dist/lib/memory/MemoryStorage.js.map +1 -0
  72. package/dist/lib/memory/MemoryStorage.test.d.ts +2 -0
  73. package/dist/lib/memory/MemoryStorage.test.d.ts.map +1 -0
  74. package/dist/lib/memory/MemoryStorage.test.js +198 -0
  75. package/dist/lib/memory/MemoryStorage.test.js.map +1 -0
  76. package/dist/lib/memory/index.d.ts +4 -0
  77. package/dist/lib/memory/index.d.ts.map +1 -0
  78. package/dist/lib/memory/index.js +8 -0
  79. package/dist/lib/memory/index.js.map +1 -0
  80. package/dist/orchestrator/orchestrator.d.ts.map +1 -1
  81. package/dist/orchestrator/orchestrator.js +4 -6
  82. package/dist/orchestrator/orchestrator.js.map +1 -1
  83. package/dist/tools/convention/analyzeComplexity.d.ts +3 -1
  84. package/dist/tools/convention/analyzeComplexity.d.ts.map +1 -1
  85. package/dist/tools/convention/analyzeComplexity.js +102 -4
  86. package/dist/tools/convention/analyzeComplexity.js.map +1 -1
  87. package/dist/tools/convention/analyzeComplexity.test.d.ts +2 -0
  88. package/dist/tools/convention/analyzeComplexity.test.d.ts.map +1 -0
  89. package/dist/tools/convention/analyzeComplexity.test.js +207 -0
  90. package/dist/tools/convention/analyzeComplexity.test.js.map +1 -0
  91. package/dist/tools/convention/applyQualityRules.js +1 -1
  92. package/dist/tools/convention/applyQualityRules.js.map +1 -1
  93. package/dist/tools/convention/checkCouplingCohesion.js +2 -2
  94. package/dist/tools/convention/checkCouplingCohesion.js.map +1 -1
  95. package/dist/tools/convention/suggestImprovements.js +1 -1
  96. package/dist/tools/convention/suggestImprovements.js.map +1 -1
  97. package/dist/tools/convention/validateCodeQuality.d.ts +3 -1
  98. package/dist/tools/convention/validateCodeQuality.d.ts.map +1 -1
  99. package/dist/tools/convention/validateCodeQuality.js +145 -2
  100. package/dist/tools/convention/validateCodeQuality.js.map +1 -1
  101. package/dist/tools/convention/validateCodeQuality.test.d.ts +2 -0
  102. package/dist/tools/convention/validateCodeQuality.test.d.ts.map +1 -0
  103. package/dist/tools/convention/validateCodeQuality.test.js +230 -0
  104. package/dist/tools/convention/validateCodeQuality.test.js.map +1 -0
  105. package/dist/tools/memory/autoSaveContext.js +1 -1
  106. package/dist/tools/memory/autoSaveContext.js.map +1 -1
  107. package/dist/tools/memory/createMemoryTimeline.js +27 -27
  108. package/dist/tools/memory/createMemoryTimeline.js.map +1 -1
  109. package/dist/tools/memory/deleteMemory.js +1 -1
  110. package/dist/tools/memory/deleteMemory.js.map +1 -1
  111. package/dist/tools/memory/getMemoryGraph.js +24 -24
  112. package/dist/tools/memory/getMemoryGraph.js.map +1 -1
  113. package/dist/tools/memory/getSessionContext.js +36 -36
  114. package/dist/tools/memory/getSessionContext.js.map +1 -1
  115. package/dist/tools/memory/linkMemories.js +21 -21
  116. package/dist/tools/memory/linkMemories.js.map +1 -1
  117. package/dist/tools/memory/prioritizeMemory.js +1 -1
  118. package/dist/tools/memory/prioritizeMemory.js.map +1 -1
  119. package/dist/tools/memory/restoreSessionContext.js +1 -1
  120. package/dist/tools/memory/restoreSessionContext.js.map +1 -1
  121. package/dist/tools/memory/searchMemories.js +1 -1
  122. package/dist/tools/memory/searchMemories.js.map +1 -1
  123. package/dist/tools/memory/searchMemoriesAdvanced.js +42 -42
  124. package/dist/tools/memory/searchMemoriesAdvanced.js.map +1 -1
  125. package/dist/tools/memory/startSession.js +2 -2
  126. package/dist/tools/memory/startSession.js.map +1 -1
  127. package/dist/tools/memory/updateMemory.js +1 -1
  128. package/dist/tools/memory/updateMemory.js.map +1 -1
  129. package/dist/tools/semantic/analyzeDependencyGraph.js +38 -38
  130. package/dist/tools/semantic/analyzeDependencyGraph.js.map +1 -1
  131. package/dist/tools/semantic/findReferences.js +1 -1
  132. package/dist/tools/semantic/findReferences.js.map +1 -1
  133. package/dist/tools/semantic/findSymbol.js +1 -1
  134. package/dist/tools/semantic/findSymbol.js.map +1 -1
  135. package/dist/tools/time/getCurrentTime.js +1 -1
  136. package/dist/tools/time/getCurrentTime.js.map +1 -1
  137. package/dist/tools/ui/previewUiAscii.js +2 -2
  138. package/dist/tools/ui/previewUiAscii.js.map +1 -1
  139. package/hooks/hooks.json +11 -2
  140. package/hooks/scripts/llm-orchestrate.js +1 -1
  141. package/hooks/scripts/utils.js +31 -6
  142. package/languages/csharp-unity.md +82 -83
  143. package/languages/dart-flutter.md +89 -88
  144. package/languages/go.md +76 -75
  145. package/languages/java-spring.md +85 -84
  146. package/languages/kotlin-android.md +64 -63
  147. package/languages/python-django.md +83 -82
  148. package/languages/python-fastapi.md +82 -81
  149. package/languages/rust.md +75 -74
  150. package/languages/swift-ios.md +73 -72
  151. package/languages/typescript-electron.md +70 -71
  152. package/languages/typescript-nextjs.md +93 -92
  153. package/languages/typescript-node.md +64 -63
  154. package/languages/typescript-nuxt.md +113 -112
  155. package/languages/typescript-react-native.md +82 -81
  156. package/languages/typescript-react.md +76 -75
  157. package/languages/typescript-tauri.md +74 -75
  158. package/languages/typescript-vue.md +73 -72
  159. package/package.json +1 -1
  160. package/skills/git-worktree.md +25 -25
  161. package/skills/multi-llm-orchestration.md +4 -6
  162. package/skills/priority-todos.md +39 -39
  163. package/skills/vibe-capabilities.md +2 -2
  164. package/vibe/config.json +2 -2
@@ -1,32 +1,33 @@
1
- # 🍎 Swift + iOS 품질 규칙
1
+ # Swift + iOS Quality Rules
2
2
 
3
- ## 핵심 원칙 (core에서 상속)
3
+ ## Core Principles (inherited from core)
4
4
 
5
5
  ```markdown
6
- 단일 책임 (SRP)
7
- 중복 제거 (DRY)
8
- 재사용성
9
- ✅ 낮은 복잡도
10
- 함수 ≤ 30줄
11
- 중첩 3단계
12
- Cyclomatic complexity ≤ 10
6
+ # Core Principles (inherited from core)
7
+ Single Responsibility (SRP)
8
+ No Duplication (DRY)
9
+ Reusability
10
+ Low Complexity
11
+ Function <= 30 lines
12
+ Nesting <= 3 levels
13
+ Cyclomatic complexity <= 10
13
14
  ```
14
15
 
15
- ## Swift/iOS 특화 규칙
16
+ ## Swift/iOS Specific Rules
16
17
 
17
- ### 1. SwiftUI 기본 구조
18
+ ### 1. SwiftUI Basic Structure
18
19
 
19
20
  ```swift
20
- // View 구조
21
+ // Good: View structure
21
22
  import SwiftUI
22
23
 
23
24
  struct UserProfileView: View {
24
- // 1. 상태 바인딩
25
+ // 1. State and bindings
25
26
  @StateObject private var viewModel: UserProfileViewModel
26
27
  @State private var isEditing = false
27
28
  @Binding var selectedUser: User?
28
29
 
29
- // 2. 환경 변수
30
+ // 2. Environment variables
30
31
  @Environment(\.dismiss) private var dismiss
31
32
  @EnvironmentObject private var authManager: AuthManager
32
33
 
@@ -34,14 +35,14 @@ struct UserProfileView: View {
34
35
  var body: some View {
35
36
  NavigationStack {
36
37
  content
37
- .navigationTitle("프로필")
38
+ .navigationTitle("Profile")
38
39
  .toolbar { toolbarContent }
39
40
  .sheet(isPresented: $isEditing) { editSheet }
40
41
  }
41
42
  .task { await viewModel.loadUser() }
42
43
  }
43
44
 
44
- // 4. 컴포넌트 분리
45
+ // 4. Separate view components
45
46
  @ViewBuilder
46
47
  private var content: some View {
47
48
  if viewModel.isLoading {
@@ -55,9 +56,9 @@ struct UserProfileView: View {
55
56
 
56
57
  private func userContent(_ user: User) -> some View {
57
58
  List {
58
- Section("기본 정보") {
59
- LabeledContent("이름", value: user.name)
60
- LabeledContent("이메일", value: user.email)
59
+ Section("Basic Info") {
60
+ LabeledContent("Name", value: user.name)
61
+ LabeledContent("Email", value: user.email)
61
62
  }
62
63
  }
63
64
  }
@@ -65,7 +66,7 @@ struct UserProfileView: View {
65
66
  @ToolbarContentBuilder
66
67
  private var toolbarContent: some ToolbarContent {
67
68
  ToolbarItem(placement: .topBarTrailing) {
68
- Button("편집") { isEditing = true }
69
+ Button("Edit") { isEditing = true }
69
70
  }
70
71
  }
71
72
  }
@@ -74,18 +75,18 @@ struct UserProfileView: View {
74
75
  ### 2. ViewModel (MVVM)
75
76
 
76
77
  ```swift
77
- // ViewModel with @Observable (iOS 17+)
78
+ // Good: ViewModel with @Observable (iOS 17+)
78
79
  import Foundation
79
80
  import Observation
80
81
 
81
82
  @Observable
82
83
  final class UserProfileViewModel {
83
- // 상태
84
+ // State
84
85
  private(set) var user: User?
85
86
  private(set) var isLoading = false
86
87
  private(set) var error: AppError?
87
88
 
88
- // 의존성
89
+ // Dependencies
89
90
  private let userRepository: UserRepository
90
91
  private let userId: String
91
92
 
@@ -117,7 +118,7 @@ final class UserProfileViewModel {
117
118
  }
118
119
  }
119
120
 
120
- // ViewModel with ObservableObject (iOS 13+)
121
+ // Good: ViewModel with ObservableObject (iOS 13+)
121
122
  import Combine
122
123
 
123
124
  final class UserListViewModel: ObservableObject {
@@ -161,10 +162,10 @@ final class UserListViewModel: ObservableObject {
161
162
  }
162
163
  ```
163
164
 
164
- ### 3. Repository 패턴
165
+ ### 3. Repository Pattern
165
166
 
166
167
  ```swift
167
- // Protocol 정의
168
+ // Good: Protocol definition
168
169
  protocol UserRepository {
169
170
  func fetchUsers() async throws -> [User]
170
171
  func fetchUser(id: String) async throws -> User
@@ -173,7 +174,7 @@ protocol UserRepository {
173
174
  func deleteUser(id: String) async throws
174
175
  }
175
176
 
176
- // 구현체
177
+ // Good: Implementation
177
178
  final class DefaultUserRepository: UserRepository {
178
179
  private let apiClient: APIClient
179
180
  private let cache: CacheManager
@@ -184,18 +185,18 @@ final class DefaultUserRepository: UserRepository {
184
185
  }
185
186
 
186
187
  func fetchUser(id: String) async throws -> User {
187
- // 캐시 확인
188
+ // Check cache
188
189
  if let cached: User = cache.get(key: "user_\(id)") {
189
190
  return cached
190
191
  }
191
192
 
192
- // API 호출
193
+ // API call
193
194
  let user: User = try await apiClient.request(
194
195
  endpoint: .user(id: id),
195
196
  method: .get
196
197
  )
197
198
 
198
- // 캐시 저장
199
+ // Save to cache
199
200
  cache.set(key: "user_\(id)", value: user, ttl: 300)
200
201
 
201
202
  return user
@@ -210,10 +211,10 @@ final class DefaultUserRepository: UserRepository {
210
211
  }
211
212
  ```
212
213
 
213
- ### 4. 에러 처리
214
+ ### 4. Error Handling
214
215
 
215
216
  ```swift
216
- // 커스텀 에러 정의
217
+ // Good: Custom error definition
217
218
  enum AppError: LocalizedError {
218
219
  case networkError(underlying: Error)
219
220
  case decodingError(underlying: Error)
@@ -225,17 +226,17 @@ enum AppError: LocalizedError {
225
226
  var errorDescription: String? {
226
227
  switch self {
227
228
  case .networkError:
228
- return "네트워크 연결을 확인해주세요"
229
+ return "Please check your network connection"
229
230
  case .decodingError:
230
- return "데이터를 처리할 없습니다"
231
+ return "Unable to process data"
231
232
  case .notFound(let resource, let id):
232
- return "\(resource)을(를) 찾을 없습니다 (ID: \(id))"
233
+ return "\(resource) not found (ID: \(id))"
233
234
  case .unauthorized:
234
- return "로그인이 필요합니다"
235
+ return "Login required"
235
236
  case .serverError(let message):
236
- return "서버 오류: \(message)"
237
+ return "Server error: \(message)"
237
238
  case .unknown:
238
- return " 없는 오류가 발생했습니다"
239
+ return "An unknown error occurred"
239
240
  }
240
241
  }
241
242
 
@@ -256,7 +257,7 @@ enum AppError: LocalizedError {
256
257
  }
257
258
  }
258
259
 
259
- // Result 타입 활용
260
+ // Good: Result type usage
260
261
  func loadData() async -> Result<User, AppError> {
261
262
  do {
262
263
  let user = try await repository.fetchUser(id: userId)
@@ -267,10 +268,10 @@ func loadData() async -> Result<User, AppError> {
267
268
  }
268
269
  ```
269
270
 
270
- ### 5. 네트워킹 (async/await)
271
+ ### 5. Networking (async/await)
271
272
 
272
273
  ```swift
273
- // API 클라이언트
274
+ // Good: API Client
274
275
  final class APIClient {
275
276
  static let shared = APIClient()
276
277
 
@@ -295,7 +296,7 @@ final class APIClient {
295
296
  request.httpMethod = method.rawValue
296
297
  request.setValue("application/json", forHTTPHeaderField: "Content-Type")
297
298
 
298
- // 인증 토큰
299
+ // Auth token
299
300
  if let token = AuthManager.shared.accessToken {
300
301
  request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
301
302
  }
@@ -324,7 +325,7 @@ final class APIClient {
324
325
  }
325
326
  }
326
327
 
327
- // Endpoint 정의
328
+ // Good: Endpoint definition
328
329
  enum Endpoint {
329
330
  case users
330
331
  case user(id: String)
@@ -353,10 +354,10 @@ enum Endpoint {
353
354
  }
354
355
  ```
355
356
 
356
- ### 6. 의존성 주입
357
+ ### 6. Dependency Injection
357
358
 
358
359
  ```swift
359
- // Environment를 통한 DI (SwiftUI)
360
+ // Good: DI via Environment (SwiftUI)
360
361
  private struct UserRepositoryKey: EnvironmentKey {
361
362
  static let defaultValue: UserRepository = DefaultUserRepository()
362
363
  }
@@ -368,7 +369,7 @@ extension EnvironmentValues {
368
369
  }
369
370
  }
370
371
 
371
- // 사용
372
+ // Usage
372
373
  struct ContentView: View {
373
374
  @Environment(\.userRepository) private var userRepository
374
375
 
@@ -377,7 +378,7 @@ struct ContentView: View {
377
378
  }
378
379
  }
379
380
 
380
- // Container 패턴
381
+ // Good: Container pattern
381
382
  final class DIContainer {
382
383
  static let shared = DIContainer()
383
384
 
@@ -393,13 +394,13 @@ final class DIContainer {
393
394
  }
394
395
  ```
395
396
 
396
- ### 7. 테스트
397
+ ### 7. Testing
397
398
 
398
399
  ```swift
399
400
  import XCTest
400
401
  @testable import MyApp
401
402
 
402
- // Mock Repository
403
+ // Good: Mock Repository
403
404
  final class MockUserRepository: UserRepository {
404
405
  var fetchUsersResult: Result<[User], Error> = .success([])
405
406
  var fetchUserResult: Result<User, Error> = .failure(AppError.notFound(resource: "User", id: ""))
@@ -412,10 +413,10 @@ final class MockUserRepository: UserRepository {
412
413
  try fetchUserResult.get()
413
414
  }
414
415
 
415
- // ... 다른 메서드
416
+ // ... other methods
416
417
  }
417
418
 
418
- // ViewModel 테스트
419
+ // Good: ViewModel test
419
420
  final class UserListViewModelTests: XCTestCase {
420
421
  var sut: UserListViewModel!
421
422
  var mockRepository: MockUserRepository!
@@ -432,11 +433,11 @@ final class UserListViewModelTests: XCTestCase {
432
433
  super.tearDown()
433
434
  }
434
435
 
435
- func test_loadUsers_성공시_users가_업데이트된다() async {
436
+ func test_loadUsers_onSuccess_updatesUsers() async {
436
437
  // Given
437
438
  let expectedUsers = [
438
- User(id: "1", name: "테스트1", email: "test1@example.com"),
439
- User(id: "2", name: "테스트2", email: "test2@example.com")
439
+ User(id: "1", name: "Test1", email: "test1@example.com"),
440
+ User(id: "2", name: "Test2", email: "test2@example.com")
440
441
  ]
441
442
  mockRepository.fetchUsersResult = .success(expectedUsers)
442
443
 
@@ -448,30 +449,30 @@ final class UserListViewModelTests: XCTestCase {
448
449
  XCTAssertFalse(sut.isLoading)
449
450
  }
450
451
 
451
- func test_filteredUsers_검색어가_있으면_필터링된다() {
452
+ func test_filteredUsers_withSearchText_filtersCorrectly() {
452
453
  // Given
453
454
  sut.users = [
454
- User(id: "1", name: "홍길동", email: "hong@example.com"),
455
- User(id: "2", name: "김철수", email: "kim@example.com")
455
+ User(id: "1", name: "John Doe", email: "john@example.com"),
456
+ User(id: "2", name: "Jane Smith", email: "jane@example.com")
456
457
  ]
457
458
 
458
459
  // When
459
- sut.searchText = ""
460
+ sut.searchText = "John"
460
461
 
461
462
  // Then
462
463
  XCTAssertEqual(sut.filteredUsers.count, 1)
463
- XCTAssertEqual(sut.filteredUsers.first?.name, "홍길동")
464
+ XCTAssertEqual(sut.filteredUsers.first?.name, "John Doe")
464
465
  }
465
466
  }
466
467
  ```
467
468
 
468
- ## 파일 구조
469
+ ## File Structure
469
470
 
470
- ```
471
+ ```text
471
472
  Project/
472
473
  ├── App/
473
- │ ├── ProjectApp.swift # 진입점
474
- │ └── DIContainer.swift # 의존성 컨테이너
474
+ │ ├── ProjectApp.swift # App entry point
475
+ │ └── DIContainer.swift # Dependency container
475
476
  ├── Features/
476
477
  │ ├── Auth/
477
478
  │ │ ├── Views/
@@ -504,13 +505,13 @@ Project/
504
505
  └── UITests/
505
506
  ```
506
507
 
507
- ## 체크리스트
508
+ ## Checklist
508
509
 
509
- - [ ] @Observable 또는 @ObservableObject 사용
510
- - [ ] MVVM 패턴 준수
511
- - [ ] async/await로 비동기 처리
512
- - [ ] Protocol로 의존성 추상화
513
- - [ ] @MainActor로 UI 업데이트 보장
514
- - [ ] LocalizedError로 에러 메시지 정의
515
- - [ ] @ViewBuilder로 조건부 분리
516
- - [ ] 테스트 가능한 구조 (Mock 주입)
510
+ - [ ] Use @Observable or @ObservableObject
511
+ - [ ] Follow MVVM pattern
512
+ - [ ] Handle async with async/await
513
+ - [ ] Abstract dependencies with Protocol
514
+ - [ ] Ensure UI updates with @MainActor
515
+ - [ ] Define error messages with LocalizedError
516
+ - [ ] Separate conditional views with @ViewBuilder
517
+ - [ ] Testable structure (Mock injection)
@@ -1,45 +1,44 @@
1
- # TypeScript + Electron 품질 규칙
1
+ # TypeScript + Electron Quality Rules
2
2
 
3
- ## 핵심 원칙 (core에서 상속)
3
+ ## Core Principles (inherited from core)
4
4
 
5
5
  ```markdown
6
- 단일 책임 (SRP)
7
- 중복 제거 (DRY)
8
- 재사용성
9
- ✅ 낮은 복잡도
10
- 함수 ≤ 30줄
11
- 중첩 3단계
12
- Cyclomatic complexity ≤ 10
6
+ # Core Principles (inherited from core)
7
+ Single Responsibility (SRP)
8
+ No Duplication (DRY)
9
+ Reusability
10
+ Low Complexity
11
+ Function <= 30 lines
12
+ Nesting <= 3 levels
13
+ Cyclomatic complexity <= 10
13
14
  ```
14
15
 
15
- ## Electron 아키텍처 이해
16
+ ## Electron Architecture Understanding
16
17
 
17
- ```
18
- ┌─────────────────────────────────────────────┐
19
- │ Main Process (Node.js) │
20
- - 생명주기 관리 │
21
- - 시스템 API (파일, 네트워크) │
22
- │ - BrowserWindow 생성/관리 │
23
- ├─────────────────────────────────────────────┤
24
- │ Preload Script (격리된 컨텍스트) │
25
- - contextBridge로 API 노출 │
26
- │ - Main ↔ Renderer 브릿지 │
27
- ├─────────────────────────────────────────────┤
28
- │ Renderer Process (Chromium)
29
- - UI 렌더링 (React/Vue/etc) │
30
- │ - window.electronAPI 사용 │
31
- └─────────────────────────────────────────────┘
18
+ ```text
19
+ Main Process (Node.js)
20
+ - App lifecycle management
21
+ - System APIs (file, network)
22
+ - BrowserWindow creation/management
23
+
24
+ Preload Script (Isolated Context)
25
+ - Expose APIs via contextBridge
26
+ - Main <-> Renderer bridge
27
+
28
+ Renderer Process (Chromium)
29
+ - UI rendering (React/Vue/etc)
30
+ - Use window.electronAPI
32
31
  ```
33
32
 
34
- ## TypeScript/Electron 특화 규칙
33
+ ## TypeScript/Electron Specific Rules
35
34
 
36
- ### 1. 프로세스 분리 필수
35
+ ### 1. Process Separation Required
37
36
 
38
37
  ```typescript
39
- // Renderer에서 직접 Node.js 사용 (보안 취약)
40
- // nodeIntegration: true 금지!
38
+ // Bad: Direct Node.js usage in Renderer (security vulnerability)
39
+ // nodeIntegration: true is prohibited!
41
40
 
42
- // Main Process (main.ts)
41
+ // Good: Main Process (main.ts)
43
42
  import { app, BrowserWindow, ipcMain } from 'electron';
44
43
  import path from 'path';
45
44
 
@@ -49,9 +48,9 @@ function createWindow(): BrowserWindow {
49
48
  height: 600,
50
49
  webPreferences: {
51
50
  preload: path.join(__dirname, 'preload.js'),
52
- contextIsolation: true, // 필수!
53
- nodeIntegration: false, // 필수!
54
- sandbox: true // 권장
51
+ contextIsolation: true, // Required!
52
+ nodeIntegration: false, // Required!
53
+ sandbox: true // Recommended
55
54
  }
56
55
  });
57
56
 
@@ -62,13 +61,13 @@ function createWindow(): BrowserWindow {
62
61
  app.whenReady().then(createWindow);
63
62
  ```
64
63
 
65
- ### 2. Preload Script 패턴
64
+ ### 2. Preload Script Pattern
66
65
 
67
66
  ```typescript
68
67
  // preload.ts
69
68
  import { contextBridge, ipcRenderer } from 'electron';
70
69
 
71
- // 타입 정의
70
+ // Good: Type definition
72
71
  interface ElectronAPI {
73
72
  readFile: (path: string) => Promise<string>;
74
73
  writeFile: (path: string, content: string) => Promise<void>;
@@ -76,7 +75,7 @@ interface ElectronAPI {
76
75
  platform: NodeJS.Platform;
77
76
  }
78
77
 
79
- // 안전하게 API 노출
78
+ // Good: Safely expose API
80
79
  contextBridge.exposeInMainWorld('electronAPI', {
81
80
  readFile: (path: string) => ipcRenderer.invoke('read-file', path),
82
81
  writeFile: (path: string, content: string) =>
@@ -89,7 +88,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
89
88
  platform: process.platform
90
89
  } satisfies ElectronAPI);
91
90
 
92
- // 타입 선언 (renderer에서 사용)
91
+ // Good: Type declaration (for use in renderer)
93
92
  declare global {
94
93
  interface Window {
95
94
  electronAPI: ElectronAPI;
@@ -97,7 +96,7 @@ declare global {
97
96
  }
98
97
  ```
99
98
 
100
- ### 3. IPC 통신 타입 안전성
99
+ ### 3. IPC Communication Type Safety
101
100
 
102
101
  ```typescript
103
102
  // shared/ipc-types.ts
@@ -120,7 +119,7 @@ export interface AppInfo {
120
119
  import { ipcMain } from 'electron';
121
120
  import fs from 'fs/promises';
122
121
 
123
- // 타입 안전한 핸들러
122
+ // Good: Type-safe handler
124
123
  ipcMain.handle('read-file', async (_event, path: string): Promise<string> => {
125
124
  return fs.readFile(path, 'utf-8');
126
125
  });
@@ -141,12 +140,12 @@ ipcMain.handle('get-app-info', async (): Promise<AppInfo> => {
141
140
  });
142
141
  ```
143
142
 
144
- ### 4. Renderer에서 IPC 사용
143
+ ### 4. IPC Usage in Renderer
145
144
 
146
145
  ```typescript
147
146
  // renderer/hooks/useElectron.ts
148
147
 
149
- // Custom Hook
148
+ // Good: Custom Hook
150
149
  function useFileReader() {
151
150
  const [content, setContent] = useState<string | null>(null);
152
151
  const [loading, setLoading] = useState(false);
@@ -171,7 +170,7 @@ function useFileReader() {
171
170
  return { content, loading, error, readFile };
172
171
  }
173
172
 
174
- // 이벤트 구독 Hook
173
+ // Good: Event subscription Hook
175
174
  function useFileWatcher(onChanged: (path: string) => void) {
176
175
  useEffect(() => {
177
176
  const unsubscribe = window.electronAPI.onFileChanged(onChanged);
@@ -180,13 +179,13 @@ function useFileWatcher(onChanged: (path: string) => void) {
180
179
  }
181
180
  ```
182
181
 
183
- ### 5. 관리
182
+ ### 5. Window Management
184
183
 
185
184
  ```typescript
186
185
  // main.ts
187
186
  import { BrowserWindow, screen } from 'electron';
188
187
 
189
- // 상태 저장/복원
188
+ // Good: Save/restore window state
190
189
  interface WindowState {
191
190
  x?: number;
192
191
  y?: number;
@@ -214,7 +213,7 @@ function createWindowWithState(): BrowserWindow {
214
213
  win.maximize();
215
214
  }
216
215
 
217
- // 상태 변경 저장
216
+ // Save state on change
218
217
  win.on('close', () => {
219
218
  saveWindowState({
220
219
  ...win.getBounds(),
@@ -225,7 +224,7 @@ function createWindowWithState(): BrowserWindow {
225
224
  return win;
226
225
  }
227
226
 
228
- // 다중 관리
227
+ // Good: Multiple window management
229
228
  const windows = new Map<string, BrowserWindow>();
230
229
 
231
230
  function getOrCreateWindow(id: string): BrowserWindow {
@@ -242,12 +241,12 @@ function getOrCreateWindow(id: string): BrowserWindow {
242
241
  }
243
242
  ```
244
243
 
245
- ### 6. 메뉴 구성
244
+ ### 6. Menu Configuration
246
245
 
247
246
  ```typescript
248
247
  import { Menu, MenuItemConstructorOptions } from 'electron';
249
248
 
250
- // 플랫폼별 메뉴
249
+ // Good: Platform-specific menu
251
250
  function createMenu(): Menu {
252
251
  const isMac = process.platform === 'darwin';
253
252
 
@@ -283,18 +282,18 @@ function createMenu(): Menu {
283
282
  }
284
283
  ```
285
284
 
286
- ### 7. 자동 업데이트
285
+ ### 7. Auto Update
287
286
 
288
287
  ```typescript
289
288
  import { autoUpdater } from 'electron-updater';
290
289
 
291
- // 자동 업데이트 설정
290
+ // Good: Auto update setup
292
291
  function setupAutoUpdater(): void {
293
292
  autoUpdater.autoDownload = false;
294
293
  autoUpdater.autoInstallOnAppQuit = true;
295
294
 
296
295
  autoUpdater.on('update-available', (info) => {
297
- // 사용자에게 알림
296
+ // Notify user
298
297
  dialog.showMessageBox({
299
298
  type: 'info',
300
299
  title: 'Update Available',
@@ -320,15 +319,15 @@ function setupAutoUpdater(): void {
320
319
  });
321
320
  });
322
321
 
323
- // 시작 업데이트 확인
322
+ // Check for updates on app start
324
323
  autoUpdater.checkForUpdates();
325
324
  }
326
325
  ```
327
326
 
328
- ### 8. 보안 체크리스트
327
+ ### 8. Security Checklist
329
328
 
330
329
  ```typescript
331
- // 보안 설정 검증
330
+ // Good: Validate security settings
332
331
  function validateSecuritySettings(win: BrowserWindow): void {
333
332
  const webPrefs = win.webContents.getWebPreferences();
334
333
 
@@ -343,9 +342,9 @@ function validateSecuritySettings(win: BrowserWindow): void {
343
342
  }
344
343
  }
345
344
 
346
- // 외부 링크 처리
345
+ // Good: Handle external links
347
346
  win.webContents.setWindowOpenHandler(({ url }) => {
348
- // 외부 URL은 시스템 브라우저에서 열기
347
+ // Open external URLs in system browser
349
348
  if (url.startsWith('https://')) {
350
349
  shell.openExternal(url);
351
350
  }
@@ -353,9 +352,9 @@ win.webContents.setWindowOpenHandler(({ url }) => {
353
352
  });
354
353
  ```
355
354
 
356
- ## 폴더 구조 권장
355
+ ## Recommended Folder Structure
357
356
 
358
- ```
357
+ ```text
359
358
  my-electron-app/
360
359
  ├── src/
361
360
  │ ├── main/ # Main Process
@@ -368,13 +367,13 @@ my-electron-app/
368
367
  │ │ ├── components/
369
368
  │ │ ├── hooks/
370
369
  │ │ └── App.tsx
371
- │ └── shared/ # 공유 타입
370
+ │ └── shared/ # Shared types
372
371
  │ └── ipc-types.ts
373
372
  ├── electron-builder.yml
374
373
  └── package.json
375
374
  ```
376
375
 
377
- ## 빌드 설정 (electron-builder)
376
+ ## Build Configuration (electron-builder)
378
377
 
379
378
  ```yaml
380
379
  # electron-builder.yml
@@ -394,14 +393,14 @@ linux:
394
393
  target: [AppImage, deb]
395
394
  ```
396
395
 
397
- ## 체크리스트
398
-
399
- - [ ] `contextIsolation: true` 설정
400
- - [ ] `nodeIntegration: false` 설정
401
- - [ ] Preload script로만 API 노출
402
- - [ ] IPC 채널 타입 정의
403
- - [ ] 외부 링크 처리 (setWindowOpenHandler)
404
- - [ ] 상태 저장/복원
405
- - [ ] 자동 업데이트 설정
406
- - [ ] 플랫폼별 메뉴 구성
407
- - [ ] CSP 헤더 설정
396
+ ## Checklist
397
+
398
+ - [ ] `contextIsolation: true` configured
399
+ - [ ] `nodeIntegration: false` configured
400
+ - [ ] Expose APIs only through preload script
401
+ - [ ] Define IPC channel types
402
+ - [ ] Handle external links (setWindowOpenHandler)
403
+ - [ ] Save/restore window state
404
+ - [ ] Auto update setup
405
+ - [ ] Platform-specific menu configuration
406
+ - [ ] CSP header configured