moai-adk 0.34.0__py3-none-any.whl → 1.1.0__py3-none-any.whl

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 (524) hide show
  1. moai_adk/__main__.py +136 -5
  2. moai_adk/astgrep/__init__.py +37 -0
  3. moai_adk/astgrep/analyzer.py +522 -0
  4. moai_adk/astgrep/models.py +124 -0
  5. moai_adk/astgrep/rules.py +179 -0
  6. moai_adk/cli/commands/analyze.py +11 -2
  7. moai_adk/cli/commands/doctor.py +7 -1
  8. moai_adk/cli/commands/init.py +321 -11
  9. moai_adk/cli/commands/language.py +7 -1
  10. moai_adk/cli/commands/rank.py +449 -0
  11. moai_adk/cli/commands/status.py +7 -1
  12. moai_adk/cli/commands/switch.py +325 -0
  13. moai_adk/cli/commands/update.py +296 -23
  14. moai_adk/cli/prompts/init_prompts.py +362 -66
  15. moai_adk/cli/prompts/translations/__init__.py +573 -0
  16. moai_adk/cli/ui/prompts.py +61 -2
  17. moai_adk/cli/worktree/cli.py +106 -1
  18. moai_adk/cli/worktree/manager.py +155 -0
  19. moai_adk/core/config/unified.py +244 -63
  20. moai_adk/core/credentials.py +264 -0
  21. moai_adk/core/error_recovery_system.py +22 -4
  22. moai_adk/core/git/conflict_detector.py +10 -1
  23. moai_adk/core/git/event_detector.py +16 -5
  24. moai_adk/core/integration/engine.py +2 -2
  25. moai_adk/core/integration/integration_tester.py +5 -5
  26. moai_adk/core/language_config_resolver.py +9 -3
  27. moai_adk/core/merge/analyzer.py +509 -324
  28. moai_adk/core/migration/alfred_to_moai_migrator.py +7 -1
  29. moai_adk/core/migration/backup_manager.py +54 -4
  30. moai_adk/core/migration/file_migrator.py +174 -2
  31. moai_adk/core/migration/interactive_checkbox_ui.py +42 -31
  32. moai_adk/core/migration/version_detector.py +123 -19
  33. moai_adk/core/migration/version_migrator.py +44 -9
  34. moai_adk/core/model_allocator.py +241 -0
  35. moai_adk/core/project/backup_utils.py +12 -2
  36. moai_adk/core/project/initializer.py +44 -87
  37. moai_adk/core/project/phase_executor.py +95 -33
  38. moai_adk/core/project/validator.py +16 -1
  39. moai_adk/core/quality/trust_checker.py +30 -10
  40. moai_adk/core/rollback_manager.py +60 -25
  41. moai_adk/core/template/backup.py +88 -6
  42. moai_adk/core/template/config.py +33 -9
  43. moai_adk/core/template/merger.py +34 -8
  44. moai_adk/core/template/processor.py +334 -11
  45. moai_adk/core/template_engine.py +10 -1
  46. moai_adk/core/template_variable_synchronizer.py +16 -2
  47. moai_adk/core/version_sync.py +54 -6
  48. moai_adk/foundation/__init__.py +1 -20
  49. moai_adk/foundation/testing.py +1 -1
  50. moai_adk/loop/__init__.py +54 -0
  51. moai_adk/loop/controller.py +305 -0
  52. moai_adk/loop/feedback.py +230 -0
  53. moai_adk/loop/state.py +209 -0
  54. moai_adk/loop/storage.py +220 -0
  55. moai_adk/lsp/__init__.py +70 -0
  56. moai_adk/lsp/client.py +320 -0
  57. moai_adk/lsp/models.py +261 -0
  58. moai_adk/lsp/protocol.py +404 -0
  59. moai_adk/lsp/server_manager.py +248 -0
  60. moai_adk/project/configuration.py +8 -1
  61. moai_adk/py.typed +0 -0
  62. moai_adk/ralph/__init__.py +37 -0
  63. moai_adk/ralph/engine.py +307 -0
  64. moai_adk/rank/__init__.py +21 -0
  65. moai_adk/rank/auth.py +425 -0
  66. moai_adk/rank/client.py +557 -0
  67. moai_adk/rank/config.py +147 -0
  68. moai_adk/rank/hook.py +1503 -0
  69. moai_adk/rank/py.typed +0 -0
  70. moai_adk/statusline/__init__.py +3 -0
  71. moai_adk/statusline/enhanced_output_style_detector.py +5 -5
  72. moai_adk/statusline/main.py +20 -1
  73. moai_adk/statusline/memory_collector.py +268 -0
  74. moai_adk/statusline/renderer.py +54 -38
  75. moai_adk/tag_system/__init__.py +48 -0
  76. moai_adk/tag_system/atomic_ops.py +117 -0
  77. moai_adk/tag_system/linkage.py +335 -0
  78. moai_adk/tag_system/parser.py +176 -0
  79. moai_adk/tag_system/validator.py +200 -0
  80. moai_adk/templates/.claude/agents/moai/builder-agent.md +19 -3
  81. moai_adk/templates/.claude/agents/moai/builder-command.md +62 -16
  82. moai_adk/templates/.claude/agents/moai/builder-plugin.md +763 -0
  83. moai_adk/templates/.claude/agents/moai/builder-skill.md +21 -5
  84. moai_adk/templates/.claude/agents/moai/expert-backend.md +103 -39
  85. moai_adk/templates/.claude/agents/moai/expert-debug.md +9 -3
  86. moai_adk/templates/.claude/agents/moai/expert-devops.md +16 -14
  87. moai_adk/templates/.claude/agents/moai/expert-frontend.md +45 -31
  88. moai_adk/templates/.claude/agents/moai/expert-performance.md +13 -9
  89. moai_adk/templates/.claude/agents/moai/expert-refactoring.md +228 -0
  90. moai_adk/templates/.claude/agents/moai/expert-security.md +19 -3
  91. moai_adk/templates/.claude/agents/moai/expert-testing.md +13 -9
  92. moai_adk/templates/.claude/agents/moai/manager-claude-code.md +8 -2
  93. moai_adk/templates/.claude/agents/moai/manager-docs.md +10 -5
  94. moai_adk/templates/.claude/agents/moai/manager-git.md +99 -27
  95. moai_adk/templates/.claude/agents/moai/manager-project.md +87 -7
  96. moai_adk/templates/.claude/agents/moai/manager-quality.md +22 -5
  97. moai_adk/templates/.claude/agents/moai/manager-spec.md +8 -2
  98. moai_adk/templates/.claude/agents/moai/manager-strategy.md +45 -14
  99. moai_adk/templates/.claude/agents/moai/manager-tdd.md +16 -3
  100. moai_adk/templates/.claude/commands/moai/0-project.md +239 -1185
  101. moai_adk/templates/.claude/commands/moai/1-plan.md +383 -363
  102. moai_adk/templates/.claude/commands/moai/2-run.md +254 -347
  103. moai_adk/templates/.claude/commands/moai/3-sync.md +174 -100
  104. moai_adk/templates/.claude/commands/moai/9-feedback.md +49 -33
  105. moai_adk/templates/.claude/commands/moai/alfred.md +339 -0
  106. moai_adk/templates/.claude/commands/moai/cancel-loop.md +163 -0
  107. moai_adk/templates/.claude/commands/moai/fix.md +264 -0
  108. moai_adk/templates/.claude/commands/moai/loop.md +363 -0
  109. moai_adk/templates/.claude/hooks/moai/lib/README.md +143 -0
  110. moai_adk/templates/.claude/hooks/moai/lib/__init__.py +37 -81
  111. moai_adk/templates/.claude/hooks/moai/lib/alfred_detector.py +105 -0
  112. moai_adk/templates/.claude/hooks/moai/lib/atomic_write.py +122 -0
  113. moai_adk/templates/.claude/hooks/moai/lib/checkpoint.py +4 -1
  114. moai_adk/templates/.claude/hooks/moai/lib/common.py +35 -5
  115. moai_adk/templates/.claude/hooks/moai/lib/config.py +376 -0
  116. moai_adk/templates/.claude/hooks/moai/lib/config_manager.py +24 -28
  117. moai_adk/templates/.claude/hooks/moai/lib/config_validator.py +14 -14
  118. moai_adk/templates/.claude/hooks/moai/lib/enhanced_output_style_detector.py +372 -0
  119. moai_adk/templates/.claude/hooks/moai/lib/exceptions.py +171 -0
  120. moai_adk/templates/.claude/hooks/moai/lib/file_utils.py +95 -0
  121. moai_adk/templates/.claude/hooks/moai/lib/git_collector.py +190 -0
  122. moai_adk/templates/.claude/hooks/moai/lib/git_operations_manager.py +15 -13
  123. moai_adk/templates/.claude/hooks/moai/lib/language_detector.py +298 -0
  124. moai_adk/templates/.claude/hooks/moai/lib/language_validator.py +125 -25
  125. moai_adk/templates/.claude/hooks/moai/lib/main.py +341 -0
  126. moai_adk/templates/.claude/hooks/moai/lib/memory_collector.py +268 -0
  127. moai_adk/templates/.claude/hooks/moai/lib/metrics_tracker.py +78 -0
  128. moai_adk/templates/.claude/hooks/moai/lib/models.py +9 -7
  129. moai_adk/templates/.claude/hooks/moai/lib/path_utils.py +204 -13
  130. moai_adk/templates/.claude/hooks/moai/lib/project.py +23 -14
  131. moai_adk/templates/.claude/hooks/moai/lib/renderer.py +359 -0
  132. moai_adk/templates/.claude/hooks/moai/lib/tag_linkage.py +333 -0
  133. moai_adk/templates/.claude/hooks/moai/lib/tag_parser.py +176 -0
  134. moai_adk/templates/.claude/hooks/moai/lib/tag_validator.py +200 -0
  135. moai_adk/templates/.claude/hooks/moai/lib/timeout.py +5 -5
  136. moai_adk/templates/.claude/hooks/moai/lib/tool_registry.py +896 -0
  137. moai_adk/templates/.claude/hooks/moai/lib/unified_timeout_manager.py +30 -18
  138. moai_adk/templates/.claude/hooks/moai/lib/update_checker.py +129 -0
  139. moai_adk/templates/.claude/hooks/moai/lib/version_reader.py +741 -0
  140. moai_adk/templates/.claude/hooks/moai/post_tool__ast_grep_scan.py +276 -0
  141. moai_adk/templates/.claude/hooks/moai/post_tool__code_formatter.py +255 -0
  142. moai_adk/templates/.claude/hooks/moai/post_tool__coverage_guard.py +325 -0
  143. moai_adk/templates/.claude/hooks/moai/post_tool__linter.py +315 -0
  144. moai_adk/templates/.claude/hooks/moai/post_tool__lsp_diagnostic.py +508 -0
  145. moai_adk/templates/.claude/hooks/moai/pre_commit__tag_validator.py +287 -0
  146. moai_adk/templates/.claude/hooks/moai/pre_tool__security_guard.py +268 -0
  147. moai_adk/templates/.claude/hooks/moai/pre_tool__tdd_enforcer.py +208 -0
  148. moai_adk/templates/.claude/hooks/moai/session_end__auto_cleanup.py +93 -61
  149. moai_adk/templates/.claude/hooks/moai/session_end__rank_submit.py +69 -0
  150. moai_adk/templates/.claude/hooks/moai/session_start__show_project_info.py +165 -70
  151. moai_adk/templates/.claude/hooks/moai/shared/utils/announcement_translator.py +206 -0
  152. moai_adk/templates/.claude/hooks/moai/stop__loop_controller.py +621 -0
  153. moai_adk/templates/.claude/output-styles/moai/alfred.md +758 -0
  154. moai_adk/templates/.claude/output-styles/moai/r2d2.md +86 -3
  155. moai_adk/templates/.claude/output-styles/moai/yoda.md +2 -2
  156. moai_adk/templates/.claude/settings.json +154 -77
  157. moai_adk/templates/.claude/skills/moai-docs-generation/SKILL.md +252 -198
  158. moai_adk/templates/.claude/skills/moai-docs-generation/examples.md +169 -323
  159. moai_adk/templates/.claude/skills/moai-docs-generation/modules/README.md +39 -27
  160. moai_adk/templates/.claude/skills/moai-docs-generation/modules/api-documentation.md +115 -125
  161. moai_adk/templates/.claude/skills/moai-docs-generation/modules/code-documentation.md +150 -150
  162. moai_adk/templates/.claude/skills/moai-docs-generation/modules/multi-format-output.md +182 -175
  163. moai_adk/templates/.claude/skills/moai-docs-generation/modules/user-guides.md +198 -138
  164. moai_adk/templates/.claude/skills/moai-docs-generation/reference.md +226 -320
  165. moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +43 -222
  166. moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +75 -219
  167. moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +103 -463
  168. moai_adk/templates/.claude/skills/moai-domain-frontend/modules/component-architecture.md +723 -0
  169. moai_adk/templates/.claude/skills/moai-domain-frontend/modules/nextjs16-patterns.md +713 -0
  170. moai_adk/templates/.claude/skills/moai-domain-frontend/modules/performance-optimization.md +694 -0
  171. moai_adk/templates/.claude/skills/moai-domain-frontend/modules/react19-patterns.md +591 -0
  172. moai_adk/templates/.claude/skills/moai-domain-frontend/modules/state-management.md +680 -0
  173. moai_adk/templates/.claude/skills/moai-domain-frontend/modules/vue35-patterns.md +802 -0
  174. moai_adk/templates/.claude/skills/moai-domain-uiux/SKILL.md +118 -339
  175. moai_adk/templates/.claude/skills/moai-formats-data/SKILL.md +74 -377
  176. moai_adk/templates/.claude/skills/moai-formats-data/modules/README.md +299 -70
  177. moai_adk/templates/.claude/skills/moai-foundation-claude/SKILL.md +205 -182
  178. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/advanced-agent-patterns.md +370 -0
  179. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-cli-reference-official.md +420 -0
  180. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-custom-slash-commands-official.md +32 -22
  181. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-devcontainers-official.md +381 -0
  182. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-discover-plugins-official.md +379 -0
  183. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-headless-official.md +378 -0
  184. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-hooks-official.md +110 -0
  185. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-plugin-marketplaces-official.md +308 -0
  186. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-plugins-official.md +640 -0
  187. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sandboxing-official.md +282 -0
  188. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-skills-official.md +425 -71
  189. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-statusline-official.md +293 -0
  190. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sub-agents-official.md +325 -143
  191. moai_adk/templates/.claude/skills/moai-foundation-context/SKILL.md +96 -316
  192. moai_adk/templates/.claude/skills/moai-foundation-core/SKILL.md +116 -294
  193. moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-advanced.md +279 -0
  194. moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-implementation.md +267 -0
  195. moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-patterns.md +121 -650
  196. moai_adk/templates/.claude/skills/moai-foundation-core/modules/patterns.md +22 -0
  197. moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-ears-format.md +200 -0
  198. moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-first-tdd.md +37 -730
  199. moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-tdd-implementation.md +275 -0
  200. moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-framework.md +77 -819
  201. moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-implementation.md +244 -0
  202. moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-validation.md +219 -0
  203. moai_adk/templates/.claude/skills/moai-foundation-philosopher/SKILL.md +14 -18
  204. moai_adk/templates/.claude/skills/moai-foundation-quality/SKILL.md +86 -270
  205. moai_adk/templates/.claude/skills/moai-framework-electron/SKILL.md +288 -0
  206. moai_adk/templates/.claude/skills/moai-framework-electron/examples.md +2082 -0
  207. moai_adk/templates/.claude/skills/moai-framework-electron/reference.md +1649 -0
  208. moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +76 -582
  209. moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +1239 -0
  210. moai_adk/templates/.claude/skills/moai-lang-cpp/modules/advanced-patterns.md +401 -0
  211. moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +1136 -0
  212. moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +82 -436
  213. moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +585 -0
  214. moai_adk/templates/.claude/skills/moai-lang-csharp/modules/aspnet-core.md +627 -0
  215. moai_adk/templates/.claude/skills/moai-lang-csharp/modules/blazor-components.md +767 -0
  216. moai_adk/templates/.claude/skills/moai-lang-csharp/modules/cqrs-validation.md +626 -0
  217. moai_adk/templates/.claude/skills/moai-lang-csharp/modules/csharp12-features.md +580 -0
  218. moai_adk/templates/.claude/skills/moai-lang-csharp/modules/efcore-patterns.md +622 -0
  219. moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +403 -0
  220. moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +65 -542
  221. moai_adk/templates/.claude/skills/moai-lang-elixir/examples.md +1171 -0
  222. moai_adk/templates/.claude/skills/moai-lang-elixir/modules/advanced-patterns.md +531 -0
  223. moai_adk/templates/.claude/skills/moai-lang-elixir/reference.md +889 -0
  224. moai_adk/templates/.claude/skills/moai-lang-flutter/SKILL.md +32 -405
  225. moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +114 -293
  226. moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +83 -307
  227. moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +179 -0
  228. moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +973 -0
  229. moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +1543 -0
  230. moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +42 -279
  231. moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +94 -556
  232. moai_adk/templates/.claude/skills/moai-lang-php/examples.md +1608 -0
  233. moai_adk/templates/.claude/skills/moai-lang-php/modules/advanced-patterns.md +538 -0
  234. moai_adk/templates/.claude/skills/moai-lang-php/reference.md +1323 -0
  235. moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +108 -358
  236. moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +84 -482
  237. moai_adk/templates/.claude/skills/moai-lang-r/examples.md +1154 -0
  238. moai_adk/templates/.claude/skills/moai-lang-r/modules/advanced-patterns.md +489 -0
  239. moai_adk/templates/.claude/skills/moai-lang-r/reference.md +1087 -0
  240. moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +106 -610
  241. moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +1106 -0
  242. moai_adk/templates/.claude/skills/moai-lang-ruby/modules/advanced-patterns.md +309 -0
  243. moai_adk/templates/.claude/skills/moai-lang-ruby/modules/testing-patterns.md +306 -0
  244. moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +1024 -0
  245. moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +51 -265
  246. moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +106 -442
  247. moai_adk/templates/.claude/skills/moai-lang-scala/modules/akka-actors.md +479 -0
  248. moai_adk/templates/.claude/skills/moai-lang-scala/modules/cats-effect.md +489 -0
  249. moai_adk/templates/.claude/skills/moai-lang-scala/modules/functional-programming.md +460 -0
  250. moai_adk/templates/.claude/skills/moai-lang-scala/modules/spark-data.md +498 -0
  251. moai_adk/templates/.claude/skills/moai-lang-scala/modules/zio-patterns.md +541 -0
  252. moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +88 -457
  253. moai_adk/templates/.claude/skills/moai-lang-swift/modules/combine-reactive.md +256 -0
  254. moai_adk/templates/.claude/skills/moai-lang-swift/modules/concurrency.md +270 -0
  255. moai_adk/templates/.claude/skills/moai-lang-swift/modules/swift6-features.md +265 -0
  256. moai_adk/templates/.claude/skills/moai-lang-swift/modules/swiftui-patterns.md +314 -0
  257. moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +75 -283
  258. moai_adk/templates/.claude/skills/moai-library-mermaid/SKILL.md +97 -252
  259. moai_adk/templates/.claude/skills/moai-library-nextra/SKILL.md +64 -240
  260. moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-patterns.md +331 -12
  261. moai_adk/templates/.claude/skills/moai-library-nextra/modules/configuration.md +330 -37
  262. moai_adk/templates/.claude/skills/moai-library-shadcn/SKILL.md +90 -287
  263. moai_adk/templates/.claude/skills/moai-platform-auth0/SKILL.md +200 -206
  264. moai_adk/templates/.claude/skills/moai-platform-auth0/examples.md +2446 -0
  265. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/adaptive-mfa.md +233 -0
  266. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/akamai-integration.md +214 -0
  267. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/application-credentials.md +280 -0
  268. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/attack-protection-log-events.md +224 -0
  269. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/attack-protection-overview.md +140 -0
  270. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/bot-detection.md +144 -0
  271. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/breached-password-detection.md +187 -0
  272. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/brute-force-protection.md +189 -0
  273. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/certifications.md +282 -0
  274. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/compliance-overview.md +263 -0
  275. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/continuous-session-protection.md +307 -0
  276. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/customize-mfa.md +177 -0
  277. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/dpop-implementation.md +283 -0
  278. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/fapi-implementation.md +259 -0
  279. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/gdpr-compliance.md +313 -0
  280. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/guardian-configuration.md +269 -0
  281. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/highly-regulated-identity.md +272 -0
  282. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/jwt-fundamentals.md +248 -0
  283. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mdl-verification.md +210 -0
  284. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-api-management.md +278 -0
  285. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-factors.md +226 -0
  286. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-overview.md +174 -0
  287. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mtls-sender-constraining.md +316 -0
  288. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/ropg-flow-mfa.md +216 -0
  289. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/security-center.md +325 -0
  290. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/security-guidance.md +277 -0
  291. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/state-parameters.md +177 -0
  292. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/step-up-authentication.md +251 -0
  293. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/suspicious-ip-throttling.md +240 -0
  294. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/tenant-access-control.md +179 -0
  295. moai_adk/templates/.claude/skills/moai-platform-auth0/modules/webauthn-fido.md +235 -0
  296. moai_adk/templates/.claude/skills/moai-platform-auth0/reference.md +224 -0
  297. moai_adk/templates/.claude/skills/moai-platform-clerk/SKILL.md +75 -330
  298. moai_adk/templates/.claude/skills/moai-platform-clerk/examples.md +1426 -0
  299. moai_adk/templates/.claude/skills/moai-platform-clerk/modules/advanced-patterns.md +417 -0
  300. moai_adk/templates/.claude/skills/moai-platform-clerk/reference.md +273 -0
  301. moai_adk/templates/.claude/skills/moai-platform-convex/SKILL.md +100 -340
  302. moai_adk/templates/.claude/skills/moai-platform-convex/examples.md +506 -0
  303. moai_adk/templates/.claude/skills/moai-platform-convex/modules/auth-integration.md +421 -0
  304. moai_adk/templates/.claude/skills/moai-platform-convex/modules/file-storage.md +474 -0
  305. moai_adk/templates/.claude/skills/moai-platform-convex/modules/reactive-queries.md +302 -0
  306. moai_adk/templates/.claude/skills/moai-platform-convex/modules/server-functions.md +452 -0
  307. moai_adk/templates/.claude/skills/moai-platform-convex/reference.md +385 -0
  308. moai_adk/templates/.claude/skills/moai-platform-firebase-auth/SKILL.md +113 -326
  309. moai_adk/templates/.claude/skills/moai-platform-firebase-auth/examples.md +514 -0
  310. moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/custom-claims.md +374 -0
  311. moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/phone-auth.md +372 -0
  312. moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/social-auth.md +339 -0
  313. moai_adk/templates/.claude/skills/moai-platform-firebase-auth/reference.md +382 -0
  314. moai_adk/templates/.claude/skills/moai-platform-firestore/SKILL.md +71 -302
  315. moai_adk/templates/.claude/skills/moai-platform-firestore/examples.md +445 -0
  316. moai_adk/templates/.claude/skills/moai-platform-firestore/modules/offline-cache.md +392 -0
  317. moai_adk/templates/.claude/skills/moai-platform-firestore/modules/realtime-listeners.md +441 -0
  318. moai_adk/templates/.claude/skills/moai-platform-firestore/modules/security-rules.md +352 -0
  319. moai_adk/templates/.claude/skills/moai-platform-firestore/modules/transactions.md +452 -0
  320. moai_adk/templates/.claude/skills/moai-platform-firestore/reference.md +322 -0
  321. moai_adk/templates/.claude/skills/moai-platform-neon/SKILL.md +101 -412
  322. moai_adk/templates/.claude/skills/moai-platform-neon/examples.md +470 -0
  323. moai_adk/templates/.claude/skills/moai-platform-neon/modules/auto-scaling.md +349 -0
  324. moai_adk/templates/.claude/skills/moai-platform-neon/modules/branching-workflows.md +354 -0
  325. moai_adk/templates/.claude/skills/moai-platform-neon/modules/connection-pooling.md +412 -0
  326. moai_adk/templates/.claude/skills/moai-platform-neon/modules/pitr-backups.md +458 -0
  327. moai_adk/templates/.claude/skills/moai-platform-neon/reference.md +272 -0
  328. moai_adk/templates/.claude/skills/moai-platform-railway/SKILL.md +96 -327
  329. moai_adk/templates/.claude/skills/moai-platform-railway/examples.md +539 -0
  330. moai_adk/templates/.claude/skills/moai-platform-railway/modules/docker-deployment.md +261 -0
  331. moai_adk/templates/.claude/skills/moai-platform-railway/modules/multi-service.md +291 -0
  332. moai_adk/templates/.claude/skills/moai-platform-railway/modules/networking-domains.md +338 -0
  333. moai_adk/templates/.claude/skills/moai-platform-railway/modules/volumes-storage.md +353 -0
  334. moai_adk/templates/.claude/skills/moai-platform-railway/reference.md +374 -0
  335. moai_adk/templates/.claude/skills/moai-platform-supabase/SKILL.md +103 -428
  336. moai_adk/templates/.claude/skills/moai-platform-supabase/examples.md +502 -0
  337. moai_adk/templates/.claude/skills/moai-platform-supabase/modules/auth-integration.md +384 -0
  338. moai_adk/templates/.claude/skills/moai-platform-supabase/modules/edge-functions.md +371 -0
  339. moai_adk/templates/.claude/skills/moai-platform-supabase/modules/postgresql-pgvector.md +231 -0
  340. moai_adk/templates/.claude/skills/moai-platform-supabase/modules/realtime-presence.md +354 -0
  341. moai_adk/templates/.claude/skills/moai-platform-supabase/modules/row-level-security.md +286 -0
  342. moai_adk/templates/.claude/skills/moai-platform-supabase/modules/storage-cdn.md +319 -0
  343. moai_adk/templates/.claude/skills/moai-platform-supabase/modules/typescript-patterns.md +453 -0
  344. moai_adk/templates/.claude/skills/moai-platform-supabase/reference.md +284 -0
  345. moai_adk/templates/.claude/skills/moai-platform-vercel/SKILL.md +96 -446
  346. moai_adk/templates/.claude/skills/moai-platform-vercel/examples.md +502 -0
  347. moai_adk/templates/.claude/skills/moai-platform-vercel/modules/analytics-speed.md +348 -0
  348. moai_adk/templates/.claude/skills/moai-platform-vercel/modules/deployment-config.md +344 -0
  349. moai_adk/templates/.claude/skills/moai-platform-vercel/modules/edge-functions.md +222 -0
  350. moai_adk/templates/.claude/skills/moai-platform-vercel/modules/isr-caching.md +306 -0
  351. moai_adk/templates/.claude/skills/moai-platform-vercel/modules/kv-storage.md +399 -0
  352. moai_adk/templates/.claude/skills/moai-platform-vercel/reference.md +360 -0
  353. moai_adk/templates/.claude/skills/moai-tool-ast-grep/SKILL.md +193 -0
  354. moai_adk/templates/.claude/skills/moai-tool-ast-grep/examples.md +1099 -0
  355. moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/language-specific.md +307 -0
  356. moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/pattern-syntax.md +237 -0
  357. moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/refactoring-patterns.md +260 -0
  358. moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/security-rules.md +239 -0
  359. moai_adk/templates/.claude/skills/moai-tool-ast-grep/reference.md +288 -0
  360. moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/go.yml +90 -0
  361. moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/python.yml +101 -0
  362. moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/typescript.yml +83 -0
  363. moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/quality/complexity-check.yml +94 -0
  364. moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/quality/deprecated-apis.yml +84 -0
  365. moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/secrets-detection.yml +89 -0
  366. moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/sql-injection.yml +45 -0
  367. moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/xss-prevention.yml +50 -0
  368. moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/sgconfig.yml +54 -0
  369. moai_adk/templates/.claude/skills/moai-workflow-jit-docs/SKILL.md +225 -423
  370. moai_adk/templates/.claude/skills/moai-workflow-loop/SKILL.md +197 -0
  371. moai_adk/templates/.claude/skills/moai-workflow-loop/examples.md +1063 -0
  372. moai_adk/templates/.claude/skills/moai-workflow-loop/reference.md +1414 -0
  373. moai_adk/templates/.claude/skills/moai-workflow-project/SKILL.md +211 -314
  374. moai_adk/templates/.claude/skills/moai-workflow-project/schemas/tab_schema.json +15 -43
  375. moai_adk/templates/.claude/skills/moai-workflow-spec/SKILL.md +119 -316
  376. moai_adk/templates/.claude/skills/moai-workflow-spec/modules/advanced-patterns.md +237 -0
  377. moai_adk/templates/.claude/skills/moai-workflow-templates/SKILL.md +96 -203
  378. moai_adk/templates/.claude/skills/moai-workflow-testing/SKILL.md +201 -388
  379. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/README.md +52 -3
  380. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/ai-debugging.md +263 -806
  381. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/context7-integration.md +286 -0
  382. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/review-workflows.md +500 -0
  383. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/relevance-analysis.md +154 -0
  384. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/safety-analysis.md +148 -0
  385. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/scoring-algorithms.md +196 -0
  386. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/timeliness-analysis.md +168 -0
  387. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/truthfulness-analysis.md +136 -0
  388. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/usability-analysis.md +153 -0
  389. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework.md +257 -0
  390. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review.md +191 -1344
  391. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/analysis-patterns.md +340 -0
  392. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/core-classes.md +299 -0
  393. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/tool-integration.md +380 -0
  394. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/debugging/debugging-workflows.md +451 -0
  395. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/debugging/error-analysis.md +442 -0
  396. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance/optimization-patterns.md +473 -0
  397. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance/profiling-techniques.md +481 -0
  398. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/ai-optimization.md +241 -0
  399. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/bottleneck-detection.md +397 -0
  400. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/optimization-plan.md +315 -0
  401. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/profiler-core.md +277 -0
  402. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/real-time-monitoring.md +187 -0
  403. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization.md +287 -1194
  404. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/quality-metrics.md +415 -0
  405. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/refactoring/ai-workflows.md +620 -0
  406. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/refactoring/patterns.md +692 -0
  407. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/security-analysis.md +429 -0
  408. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/smart-refactoring.md +262 -1192
  409. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/static-analysis.md +438 -0
  410. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd/core-classes.md +397 -0
  411. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/advanced-features.md +494 -0
  412. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/red-green-refactor.md +316 -0
  413. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/test-generation.md +471 -0
  414. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/test-patterns.md +371 -0
  415. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7.md +227 -1222
  416. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/trust5-validation.md +428 -0
  417. moai_adk/templates/.claude/skills/moai-workflow-worktree/SKILL.md +228 -0
  418. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/integration-patterns.md +149 -0
  419. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/moai-adk-integration.md +245 -0
  420. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-advanced.md +310 -0
  421. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-development.md +202 -0
  422. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-workflows.md +302 -0
  423. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/registry-architecture.md +271 -0
  424. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/resource-optimization.md +300 -0
  425. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/tools-integration.md +280 -0
  426. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/troubleshooting.md +397 -0
  427. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/worktree-commands.md +296 -0
  428. moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/worktree-management.md +217 -0
  429. moai_adk/templates/.git-hooks/pre-push +162 -59
  430. moai_adk/templates/.github/workflows/ci-universal.yml +934 -133
  431. moai_adk/templates/.gitignore +65 -107
  432. moai_adk/templates/.lsp.json +152 -0
  433. moai_adk/templates/.mcp.json +2 -20
  434. moai_adk/templates/.moai/announcements/en.json +18 -0
  435. moai_adk/templates/.moai/announcements/ja.json +18 -0
  436. moai_adk/templates/.moai/announcements/ko.json +18 -0
  437. moai_adk/templates/.moai/announcements/zh.json +18 -0
  438. moai_adk/templates/.moai/config/config.yaml +8 -2
  439. moai_adk/templates/.moai/config/multilingual-triggers.yaml +213 -0
  440. moai_adk/templates/.moai/config/sections/language.yaml +2 -2
  441. moai_adk/templates/.moai/config/sections/llm.yaml +41 -0
  442. moai_adk/templates/.moai/config/sections/pricing.yaml +30 -0
  443. moai_adk/templates/.moai/config/sections/project.yaml +2 -2
  444. moai_adk/templates/.moai/config/sections/quality.yaml +43 -5
  445. moai_adk/templates/.moai/config/sections/ralph.yaml +55 -0
  446. moai_adk/templates/.moai/config/sections/system.yaml +46 -1
  447. moai_adk/templates/.moai/config/sections/user.yaml +1 -1
  448. moai_adk/templates/.moai/config/statusline-config.yaml +2 -2
  449. moai_adk/templates/.moai/llm-configs/glm.json +22 -0
  450. moai_adk/templates/CLAUDE.ja.md +343 -0
  451. moai_adk/templates/CLAUDE.ko.md +343 -0
  452. moai_adk/templates/CLAUDE.md +200 -499
  453. moai_adk/templates/CLAUDE.zh.md +343 -0
  454. moai_adk/utils/common.py +37 -0
  455. moai_adk/version.py +1 -1
  456. moai_adk-1.1.0.dist-info/METADATA +2443 -0
  457. moai_adk-1.1.0.dist-info/RECORD +701 -0
  458. {moai_adk-0.34.0.dist-info → moai_adk-1.1.0.dist-info}/entry_points.txt +2 -0
  459. moai_adk-1.1.0.dist-info/licenses/LICENSE +99 -0
  460. moai_adk/core/config/auto_spec_config.py +0 -340
  461. moai_adk/core/hooks/post_tool_auto_spec_completion.py +0 -901
  462. moai_adk/core/spec/confidence_scoring.py +0 -680
  463. moai_adk/core/spec/ears_template_engine.py +0 -1247
  464. moai_adk/core/spec/quality_validator.py +0 -687
  465. moai_adk/templates/.claude/agents/moai/ai-nano-banana.md +0 -670
  466. moai_adk/templates/.claude/agents/moai/expert-database.md +0 -777
  467. moai_adk/templates/.claude/agents/moai/expert-uiux.md +0 -1041
  468. moai_adk/templates/.claude/agents/moai/mcp-context7.md +0 -458
  469. moai_adk/templates/.claude/agents/moai/mcp-figma.md +0 -1607
  470. moai_adk/templates/.claude/agents/moai/mcp-notion.md +0 -789
  471. moai_adk/templates/.claude/agents/moai/mcp-playwright.md +0 -469
  472. moai_adk/templates/.claude/agents/moai/mcp-sequential-thinking.md +0 -1032
  473. moai_adk/templates/.claude/skills/moai-ai-nano-banana/SKILL.md +0 -438
  474. moai_adk/templates/.claude/skills/moai-ai-nano-banana/examples.md +0 -431
  475. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/design-system-tokens.md +0 -405
  476. moai_adk/templates/.claude/skills/moai-library-nextra/advanced-patterns.md +0 -336
  477. moai_adk/templates/.claude/skills/moai-mcp-figma/SKILL.md +0 -402
  478. moai_adk/templates/.claude/skills/moai-mcp-figma/advanced-patterns.md +0 -607
  479. moai_adk/templates/.claude/skills/moai-mcp-notion/SKILL.md +0 -300
  480. moai_adk/templates/.claude/skills/moai-mcp-notion/advanced-patterns.md +0 -537
  481. moai_adk/templates/.claude/skills/moai-workflow-project/__init__.py +0 -520
  482. moai_adk/templates/.claude/skills/moai-workflow-project/complete_workflow_demo_fixed.py +0 -574
  483. moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_project_setup.py +0 -317
  484. moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_workflow_demo.py +0 -663
  485. moai_adk/templates/.claude/skills/moai-workflow-project/examples/config-migration-example.json +0 -190
  486. moai_adk/templates/.claude/skills/moai-workflow-project/examples/question-examples.json +0 -175
  487. moai_adk/templates/.claude/skills/moai-workflow-project/examples/quick_start.py +0 -196
  488. moai_adk/templates/.claude/skills/moai-workflow-project/modules/__init__.py +0 -17
  489. moai_adk/templates/.claude/skills/moai-workflow-project/modules/advanced-patterns.md +0 -158
  490. moai_adk/templates/.claude/skills/moai-workflow-project/modules/ask_user_integration.py +0 -340
  491. moai_adk/templates/.claude/skills/moai-workflow-project/modules/batch_questions.py +0 -713
  492. moai_adk/templates/.claude/skills/moai-workflow-project/modules/config_manager.py +0 -538
  493. moai_adk/templates/.claude/skills/moai-workflow-project/modules/documentation_manager.py +0 -1336
  494. moai_adk/templates/.claude/skills/moai-workflow-project/modules/language_initializer.py +0 -730
  495. moai_adk/templates/.claude/skills/moai-workflow-project/modules/migration_manager.py +0 -608
  496. moai_adk/templates/.claude/skills/moai-workflow-project/modules/template_optimizer.py +0 -1005
  497. moai_adk/templates/.claude/skills/moai-workflow-project/test_integration_simple.py +0 -436
  498. moai_adk/templates/.claude/skills/moai-worktree/SKILL.md +0 -411
  499. moai_adk/templates/.claude/skills/moai-worktree/modules/integration-patterns.md +0 -982
  500. moai_adk/templates/.claude/skills/moai-worktree/modules/parallel-development.md +0 -778
  501. moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-commands.md +0 -646
  502. moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-management.md +0 -782
  503. moai_adk/templates/.moai/config/questions/_schema.yaml +0 -151
  504. moai_adk/templates/.moai/config/questions/tab0-init.yaml +0 -251
  505. moai_adk/templates/.moai/config/questions/tab1-user.yaml +0 -108
  506. moai_adk/templates/.moai/config/questions/tab2-project.yaml +0 -81
  507. moai_adk/templates/.moai/config/questions/tab3-git.yaml +0 -634
  508. moai_adk/templates/.moai/config/questions/tab4-quality.yaml +0 -170
  509. moai_adk/templates/.moai/config/questions/tab5-system.yaml +0 -87
  510. moai_adk/templates/.moai/scripts/setup-glm.py +0 -136
  511. moai_adk-0.34.0.dist-info/METADATA +0 -2999
  512. moai_adk-0.34.0.dist-info/RECORD +0 -463
  513. moai_adk-0.34.0.dist-info/licenses/LICENSE +0 -21
  514. /moai_adk/foundation/{git.py → git/__init__.py} +0 -0
  515. /moai_adk/templates/.claude/skills/moai-library-mermaid/{advanced-patterns.md → modules/advanced-patterns.md} +0 -0
  516. /moai_adk/templates/.claude/skills/moai-library-mermaid/{optimization.md → modules/optimization.md} +0 -0
  517. /moai_adk/templates/.claude/skills/moai-library-nextra/{optimization.md → modules/optimization.md} +0 -0
  518. /moai_adk/templates/.claude/skills/moai-workflow-jit-docs/{advanced-patterns.md → modules/advanced-patterns.md} +0 -0
  519. /moai_adk/templates/.claude/skills/moai-workflow-jit-docs/{optimization.md → modules/optimization.md} +0 -0
  520. /moai_adk/templates/.claude/skills/moai-workflow-testing/{advanced-patterns.md → modules/advanced-patterns.md} +0 -0
  521. /moai_adk/templates/.claude/skills/moai-workflow-testing/{optimization.md → modules/optimization.md} +0 -0
  522. /moai_adk/templates/.claude/skills/{moai-worktree → moai-workflow-worktree}/examples.md +0 -0
  523. /moai_adk/templates/.claude/skills/{moai-worktree → moai-workflow-worktree}/reference.md +0 -0
  524. {moai_adk-0.34.0.dist-info → moai_adk-1.1.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,2082 @@
1
+ # Electron Framework Examples
2
+
3
+ Production-ready code examples for Electron 33+ desktop application development.
4
+
5
+ ---
6
+
7
+ ## Complete Electron App Setup with Vite
8
+
9
+ ### Package Configuration
10
+
11
+ ```json
12
+ // package.json
13
+ {
14
+ "name": "electron-app",
15
+ "version": "1.0.0",
16
+ "main": "dist/main/index.js",
17
+ "scripts": {
18
+ "dev": "electron-vite dev",
19
+ "build": "electron-vite build",
20
+ "preview": "electron-vite preview",
21
+ "package": "electron-builder",
22
+ "package:mac": "electron-builder --mac",
23
+ "package:win": "electron-builder --win",
24
+ "package:linux": "electron-builder --linux",
25
+ "postinstall": "electron-builder install-app-deps"
26
+ },
27
+ "dependencies": {
28
+ "electron-store": "^8.1.0",
29
+ "electron-updater": "^6.1.7"
30
+ },
31
+ "devDependencies": {
32
+ "@electron-toolkit/preload": "^3.0.0",
33
+ "@electron-toolkit/utils": "^3.0.0",
34
+ "@types/node": "^20.10.0",
35
+ "@vitejs/plugin-react": "^4.2.0",
36
+ "electron": "^33.0.0",
37
+ "electron-builder": "^24.9.1",
38
+ "electron-vite": "^2.0.0",
39
+ "react": "^18.2.0",
40
+ "react-dom": "^18.2.0",
41
+ "typescript": "^5.3.0",
42
+ "vite": "^5.0.0"
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Electron Vite Configuration
48
+
49
+ ```typescript
50
+ // electron.vite.config.ts
51
+ import { defineConfig, externalizeDepsPlugin } from "electron-vite";
52
+ import react from "@vitejs/plugin-react";
53
+ import { resolve } from "path";
54
+
55
+ export default defineConfig({
56
+ main: {
57
+ plugins: [externalizeDepsPlugin()],
58
+ build: {
59
+ rollupOptions: {
60
+ input: {
61
+ index: resolve(__dirname, "src/main/index.ts"),
62
+ },
63
+ },
64
+ },
65
+ },
66
+ preload: {
67
+ plugins: [externalizeDepsPlugin()],
68
+ build: {
69
+ rollupOptions: {
70
+ input: {
71
+ index: resolve(__dirname, "src/preload/index.ts"),
72
+ },
73
+ },
74
+ },
75
+ },
76
+ renderer: {
77
+ root: resolve(__dirname, "src/renderer"),
78
+ plugins: [react()],
79
+ build: {
80
+ rollupOptions: {
81
+ input: {
82
+ index: resolve(__dirname, "src/renderer/index.html"),
83
+ },
84
+ },
85
+ },
86
+ },
87
+ });
88
+ ```
89
+
90
+ ### Main Process Entry Point
91
+
92
+ ```typescript
93
+ // src/main/index.ts
94
+ import { app, BrowserWindow, ipcMain, session, shell } from "electron";
95
+ import { join } from "path";
96
+ import { electronApp, optimizer, is } from "@electron-toolkit/utils";
97
+ import { registerIpcHandlers } from "./ipc";
98
+ import { UpdateService } from "./services/updater";
99
+ import { WindowManager } from "./windows/window-manager";
100
+
101
+ const windowManager = new WindowManager();
102
+ const updateService = new UpdateService();
103
+
104
+ async function createMainWindow(): Promise<BrowserWindow> {
105
+ const mainWindow = windowManager.createWindow("main", {
106
+ width: 1200,
107
+ height: 800,
108
+ minWidth: 800,
109
+ minHeight: 600,
110
+ titleBarStyle: "hiddenInset",
111
+ trafficLightPosition: { x: 15, y: 15 },
112
+ webPreferences: {
113
+ preload: join(__dirname, "../preload/index.js"),
114
+ sandbox: true,
115
+ contextIsolation: true,
116
+ nodeIntegration: false,
117
+ },
118
+ });
119
+
120
+ // Open external links in default browser
121
+ mainWindow.webContents.setWindowOpenHandler(({ url }) => {
122
+ shell.openExternal(url);
123
+ return { action: "deny" };
124
+ });
125
+
126
+ // Load app
127
+ if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
128
+ mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]);
129
+ mainWindow.webContents.openDevTools({ mode: "detach" });
130
+ } else {
131
+ mainWindow.loadFile(join(__dirname, "../renderer/index.html"));
132
+ }
133
+
134
+ return mainWindow;
135
+ }
136
+
137
+ app.whenReady().then(async () => {
138
+ // Set app user model ID for Windows
139
+ electronApp.setAppUserModelId("com.example.myapp");
140
+
141
+ // Watch for shortcuts to optimize new windows
142
+ app.on("browser-window-created", (_, window) => {
143
+ optimizer.watchWindowShortcuts(window);
144
+ });
145
+
146
+ // Configure session security
147
+ configureSession();
148
+
149
+ // Register IPC handlers
150
+ registerIpcHandlers();
151
+
152
+ // Create main window
153
+ const mainWindow = await createMainWindow();
154
+
155
+ // Initialize auto-updater
156
+ if (!is.dev) {
157
+ updateService.initialize(mainWindow);
158
+ updateService.checkForUpdates();
159
+ }
160
+
161
+ app.on("activate", () => {
162
+ if (BrowserWindow.getAllWindows().length === 0) {
163
+ createMainWindow();
164
+ }
165
+ });
166
+ });
167
+
168
+ app.on("window-all-closed", () => {
169
+ if (process.platform !== "darwin") {
170
+ app.quit();
171
+ }
172
+ });
173
+
174
+ function configureSession(): void {
175
+ // Content Security Policy
176
+ session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
177
+ callback({
178
+ responseHeaders: {
179
+ ...details.responseHeaders,
180
+ "Content-Security-Policy": [
181
+ "default-src 'self'; " +
182
+ "script-src 'self'; " +
183
+ "style-src 'self' 'unsafe-inline'; " +
184
+ "img-src 'self' data: https:; " +
185
+ "font-src 'self' data:; " +
186
+ "connect-src 'self' https://api.github.com",
187
+ ],
188
+ },
189
+ });
190
+ });
191
+
192
+ // Permission handler
193
+ session.defaultSession.setPermissionRequestHandler(
194
+ (webContents, permission, callback) => {
195
+ const allowedPermissions = ["notifications", "clipboard-read"];
196
+ callback(allowedPermissions.includes(permission));
197
+ },
198
+ );
199
+
200
+ // Block navigation to external URLs
201
+ session.defaultSession.setPermissionCheckHandler(() => false);
202
+ }
203
+
204
+ // Single instance lock
205
+ const gotSingleLock = app.requestSingleInstanceLock();
206
+ if (!gotSingleLock) {
207
+ app.quit();
208
+ } else {
209
+ app.on("second-instance", (_event, _commandLine, _workingDirectory) => {
210
+ const mainWindow = windowManager.getWindow("main");
211
+ if (mainWindow) {
212
+ if (mainWindow.isMinimized()) mainWindow.restore();
213
+ mainWindow.focus();
214
+ }
215
+ });
216
+ }
217
+ ```
218
+
219
+ ---
220
+
221
+ ## Type-Safe IPC Implementation
222
+
223
+ ### Shared Type Definitions
224
+
225
+ ```typescript
226
+ // src/shared/types/ipc.ts
227
+ export interface FileInfo {
228
+ path: string;
229
+ content: string;
230
+ encoding?: BufferEncoding;
231
+ }
232
+
233
+ export interface SaveResult {
234
+ success: boolean;
235
+ path: string;
236
+ error?: string;
237
+ }
238
+
239
+ export interface DialogOptions {
240
+ title?: string;
241
+ filters?: { name: string; extensions: string[] }[];
242
+ defaultPath?: string;
243
+ }
244
+
245
+ export interface StorageItem<T = unknown> {
246
+ key: string;
247
+ value: T;
248
+ timestamp?: number;
249
+ }
250
+
251
+ // IPC Channel Definitions
252
+ export interface IpcMainToRenderer {
253
+ "app:update-available": { version: string; releaseNotes: string };
254
+ "app:update-progress": {
255
+ percent: number;
256
+ transferred: number;
257
+ total: number;
258
+ };
259
+ "app:update-downloaded": { version: string };
260
+ "window:maximize-change": boolean;
261
+ "file:external-open": { path: string };
262
+ }
263
+
264
+ export interface IpcRendererToMain {
265
+ // File operations
266
+ "file:open-dialog": DialogOptions;
267
+ "file:save-dialog": DialogOptions;
268
+ "file:read": string;
269
+ "file:write": { path: string; content: string };
270
+ "file:exists": string;
271
+
272
+ // Window operations
273
+ "window:minimize": void;
274
+ "window:maximize": void;
275
+ "window:close": void;
276
+ "window:is-maximized": void;
277
+
278
+ // Storage operations
279
+ "storage:get": string;
280
+ "storage:set": StorageItem;
281
+ "storage:delete": string;
282
+ "storage:clear": void;
283
+
284
+ // App operations
285
+ "app:get-version": void;
286
+ "app:get-path": "home" | "appData" | "userData" | "temp" | "downloads";
287
+ "app:open-external": string;
288
+
289
+ // Update operations
290
+ "update:check": void;
291
+ "update:download": void;
292
+ "update:install": void;
293
+ }
294
+
295
+ // Return types for IPC handlers
296
+ export interface IpcReturnTypes {
297
+ "file:open-dialog": FileInfo | null;
298
+ "file:save-dialog": string | null;
299
+ "file:read": string;
300
+ "file:write": SaveResult;
301
+ "file:exists": boolean;
302
+ "window:minimize": void;
303
+ "window:maximize": void;
304
+ "window:close": void;
305
+ "window:is-maximized": boolean;
306
+ "storage:get": unknown;
307
+ "storage:set": void;
308
+ "storage:delete": void;
309
+ "storage:clear": void;
310
+ "app:get-version": string;
311
+ "app:get-path": string;
312
+ "app:open-external": void;
313
+ "update:check": void;
314
+ "update:download": void;
315
+ "update:install": void;
316
+ }
317
+ ```
318
+
319
+ ### Main Process IPC Handlers
320
+
321
+ ```typescript
322
+ // src/main/ipc/index.ts
323
+ import { ipcMain, dialog, app, shell, BrowserWindow } from "electron";
324
+ import { readFile, writeFile, access } from "fs/promises";
325
+ import { constants } from "fs";
326
+ import Store from "electron-store";
327
+ import { z } from "zod";
328
+ import type {
329
+ DialogOptions,
330
+ FileInfo,
331
+ SaveResult,
332
+ StorageItem,
333
+ } from "../../shared/types/ipc";
334
+
335
+ const store = new Store({
336
+ encryptionKey: process.env.STORE_ENCRYPTION_KEY,
337
+ });
338
+
339
+ // Validation schemas
340
+ const FilePathSchema = z
341
+ .string()
342
+ .min(1)
343
+ .refine(
344
+ (path) => {
345
+ const normalized = path.replace(/\\/g, "/");
346
+ return !normalized.includes("..") && !normalized.includes("\0");
347
+ },
348
+ { message: "Invalid file path" },
349
+ );
350
+
351
+ const StorageKeySchema = z
352
+ .string()
353
+ .min(1)
354
+ .max(256)
355
+ .regex(/^[a-zA-Z0-9_.-]+$/);
356
+
357
+ export function registerIpcHandlers(): void {
358
+ // File operations
359
+ ipcMain.handle(
360
+ "file:open-dialog",
361
+ async (_event, options: DialogOptions): Promise<FileInfo | null> => {
362
+ const result = await dialog.showOpenDialog({
363
+ title: options.title ?? "Open File",
364
+ properties: ["openFile"],
365
+ filters: options.filters ?? [{ name: "All Files", extensions: ["*"] }],
366
+ defaultPath: options.defaultPath,
367
+ });
368
+
369
+ if (result.canceled || result.filePaths.length === 0) {
370
+ return null;
371
+ }
372
+
373
+ const path = result.filePaths[0];
374
+ const content = await readFile(path, "utf-8");
375
+ return { path, content };
376
+ },
377
+ );
378
+
379
+ ipcMain.handle(
380
+ "file:save-dialog",
381
+ async (_event, options: DialogOptions): Promise<string | null> => {
382
+ const result = await dialog.showSaveDialog({
383
+ title: options.title ?? "Save File",
384
+ filters: options.filters ?? [{ name: "All Files", extensions: ["*"] }],
385
+ defaultPath: options.defaultPath,
386
+ });
387
+
388
+ return result.canceled ? null : (result.filePath ?? null);
389
+ },
390
+ );
391
+
392
+ ipcMain.handle("file:read", async (_event, path: string): Promise<string> => {
393
+ const validPath = FilePathSchema.parse(path);
394
+ return readFile(validPath, "utf-8");
395
+ });
396
+
397
+ ipcMain.handle(
398
+ "file:write",
399
+ async (
400
+ _event,
401
+ { path, content }: { path: string; content: string },
402
+ ): Promise<SaveResult> => {
403
+ try {
404
+ const validPath = FilePathSchema.parse(path);
405
+ await writeFile(validPath, content, "utf-8");
406
+ return { success: true, path: validPath };
407
+ } catch (error) {
408
+ return {
409
+ success: false,
410
+ path,
411
+ error: error instanceof Error ? error.message : "Unknown error",
412
+ };
413
+ }
414
+ },
415
+ );
416
+
417
+ ipcMain.handle(
418
+ "file:exists",
419
+ async (_event, path: string): Promise<boolean> => {
420
+ try {
421
+ const validPath = FilePathSchema.parse(path);
422
+ await access(validPath, constants.F_OK);
423
+ return true;
424
+ } catch {
425
+ return false;
426
+ }
427
+ },
428
+ );
429
+
430
+ // Window operations
431
+ ipcMain.handle("window:minimize", (event): void => {
432
+ BrowserWindow.fromWebContents(event.sender)?.minimize();
433
+ });
434
+
435
+ ipcMain.handle("window:maximize", (event): void => {
436
+ const window = BrowserWindow.fromWebContents(event.sender);
437
+ if (window?.isMaximized()) {
438
+ window.unmaximize();
439
+ } else {
440
+ window?.maximize();
441
+ }
442
+ });
443
+
444
+ ipcMain.handle("window:close", (event): void => {
445
+ BrowserWindow.fromWebContents(event.sender)?.close();
446
+ });
447
+
448
+ ipcMain.handle("window:is-maximized", (event): boolean => {
449
+ return BrowserWindow.fromWebContents(event.sender)?.isMaximized() ?? false;
450
+ });
451
+
452
+ // Storage operations
453
+ ipcMain.handle("storage:get", (_event, key: string): unknown => {
454
+ const validKey = StorageKeySchema.parse(key);
455
+ return store.get(validKey);
456
+ });
457
+
458
+ ipcMain.handle("storage:set", (_event, { key, value }: StorageItem): void => {
459
+ const validKey = StorageKeySchema.parse(key);
460
+ store.set(validKey, value);
461
+ });
462
+
463
+ ipcMain.handle("storage:delete", (_event, key: string): void => {
464
+ const validKey = StorageKeySchema.parse(key);
465
+ store.delete(validKey);
466
+ });
467
+
468
+ ipcMain.handle("storage:clear", (): void => {
469
+ store.clear();
470
+ });
471
+
472
+ // App operations
473
+ ipcMain.handle("app:get-version", (): string => {
474
+ return app.getVersion();
475
+ });
476
+
477
+ ipcMain.handle(
478
+ "app:get-path",
479
+ (
480
+ _event,
481
+ name: "home" | "appData" | "userData" | "temp" | "downloads",
482
+ ): string => {
483
+ return app.getPath(name);
484
+ },
485
+ );
486
+
487
+ ipcMain.handle(
488
+ "app:open-external",
489
+ async (_event, url: string): Promise<void> => {
490
+ // Validate URL before opening
491
+ const parsedUrl = new URL(url);
492
+ if (parsedUrl.protocol === "https:" || parsedUrl.protocol === "http:") {
493
+ await shell.openExternal(url);
494
+ }
495
+ },
496
+ );
497
+ }
498
+ ```
499
+
500
+ ### Preload Script with Full API
501
+
502
+ ```typescript
503
+ // src/preload/index.ts
504
+ import { contextBridge, ipcRenderer, IpcRendererEvent } from "electron";
505
+ import type {
506
+ DialogOptions,
507
+ FileInfo,
508
+ SaveResult,
509
+ StorageItem,
510
+ IpcMainToRenderer,
511
+ } from "../shared/types/ipc";
512
+
513
+ // Type-safe event listener helper
514
+ type EventCallback<T> = (data: T) => void;
515
+ type Unsubscribe = () => void;
516
+
517
+ function createEventListener<K extends keyof IpcMainToRenderer>(
518
+ channel: K,
519
+ callback: EventCallback<IpcMainToRenderer[K]>,
520
+ ): Unsubscribe {
521
+ const handler = (_event: IpcRendererEvent, data: IpcMainToRenderer[K]) => {
522
+ callback(data);
523
+ };
524
+ ipcRenderer.on(channel, handler);
525
+ return () => ipcRenderer.removeListener(channel, handler);
526
+ }
527
+
528
+ const electronAPI = {
529
+ // File operations
530
+ file: {
531
+ openDialog: (options: DialogOptions = {}): Promise<FileInfo | null> =>
532
+ ipcRenderer.invoke("file:open-dialog", options),
533
+ saveDialog: (options: DialogOptions = {}): Promise<string | null> =>
534
+ ipcRenderer.invoke("file:save-dialog", options),
535
+ read: (path: string): Promise<string> =>
536
+ ipcRenderer.invoke("file:read", path),
537
+ write: (path: string, content: string): Promise<SaveResult> =>
538
+ ipcRenderer.invoke("file:write", { path, content }),
539
+ exists: (path: string): Promise<boolean> =>
540
+ ipcRenderer.invoke("file:exists", path),
541
+ },
542
+
543
+ // Window operations
544
+ window: {
545
+ minimize: (): Promise<void> => ipcRenderer.invoke("window:minimize"),
546
+ maximize: (): Promise<void> => ipcRenderer.invoke("window:maximize"),
547
+ close: (): Promise<void> => ipcRenderer.invoke("window:close"),
548
+ isMaximized: (): Promise<boolean> =>
549
+ ipcRenderer.invoke("window:is-maximized"),
550
+ onMaximizeChange: (callback: (isMaximized: boolean) => void): Unsubscribe =>
551
+ createEventListener("window:maximize-change", callback),
552
+ },
553
+
554
+ // Storage operations
555
+ storage: {
556
+ get: <T = unknown>(key: string): Promise<T | undefined> =>
557
+ ipcRenderer.invoke("storage:get", key) as Promise<T | undefined>,
558
+ set: <T = unknown>(key: string, value: T): Promise<void> =>
559
+ ipcRenderer.invoke("storage:set", { key, value } as StorageItem<T>),
560
+ delete: (key: string): Promise<void> =>
561
+ ipcRenderer.invoke("storage:delete", key),
562
+ clear: (): Promise<void> => ipcRenderer.invoke("storage:clear"),
563
+ },
564
+
565
+ // App operations
566
+ app: {
567
+ getVersion: (): Promise<string> => ipcRenderer.invoke("app:get-version"),
568
+ getPath: (
569
+ name: "home" | "appData" | "userData" | "temp" | "downloads",
570
+ ): Promise<string> => ipcRenderer.invoke("app:get-path", name),
571
+ openExternal: (url: string): Promise<void> =>
572
+ ipcRenderer.invoke("app:open-external", url),
573
+ },
574
+
575
+ // Update operations
576
+ update: {
577
+ check: (): Promise<void> => ipcRenderer.invoke("update:check"),
578
+ download: (): Promise<void> => ipcRenderer.invoke("update:download"),
579
+ install: (): Promise<void> => ipcRenderer.invoke("update:install"),
580
+ onAvailable: (
581
+ callback: (info: { version: string; releaseNotes: string }) => void,
582
+ ): Unsubscribe => createEventListener("app:update-available", callback),
583
+ onProgress: (
584
+ callback: (progress: {
585
+ percent: number;
586
+ transferred: number;
587
+ total: number;
588
+ }) => void,
589
+ ): Unsubscribe => createEventListener("app:update-progress", callback),
590
+ onDownloaded: (
591
+ callback: (info: { version: string }) => void,
592
+ ): Unsubscribe => createEventListener("app:update-downloaded", callback),
593
+ },
594
+
595
+ // Platform info
596
+ platform: {
597
+ isMac: process.platform === "darwin",
598
+ isWindows: process.platform === "win32",
599
+ isLinux: process.platform === "linux",
600
+ },
601
+ };
602
+
603
+ contextBridge.exposeInMainWorld("electronAPI", electronAPI);
604
+
605
+ // Type declaration for renderer
606
+ export type ElectronAPI = typeof electronAPI;
607
+
608
+ declare global {
609
+ interface Window {
610
+ electronAPI: ElectronAPI;
611
+ }
612
+ }
613
+ ```
614
+
615
+ ---
616
+
617
+ ## Auto-Update Integration
618
+
619
+ ### Complete Update Service
620
+
621
+ ```typescript
622
+ // src/main/services/updater.ts
623
+ import {
624
+ autoUpdater,
625
+ UpdateInfo,
626
+ ProgressInfo,
627
+ UpdateDownloadedEvent,
628
+ } from "electron-updater";
629
+ import { BrowserWindow, dialog, Notification } from "electron";
630
+ import log from "electron-log";
631
+
632
+ export interface UpdateServiceOptions {
633
+ /** Check for updates on startup */
634
+ checkOnStartup?: boolean;
635
+ /** Auto-download updates */
636
+ autoDownload?: boolean;
637
+ /** Auto-install on quit */
638
+ autoInstallOnAppQuit?: boolean;
639
+ /** Check interval in milliseconds (default: 1 hour) */
640
+ checkInterval?: number;
641
+ }
642
+
643
+ export class UpdateService {
644
+ private mainWindow: BrowserWindow | null = null;
645
+ private checkIntervalId: NodeJS.Timeout | null = null;
646
+
647
+ constructor(
648
+ private options: UpdateServiceOptions = {
649
+ checkOnStartup: true,
650
+ autoDownload: false,
651
+ autoInstallOnAppQuit: true,
652
+ checkInterval: 3600000,
653
+ },
654
+ ) {
655
+ // Configure logging
656
+ autoUpdater.logger = log;
657
+ log.transports.file.level = "info";
658
+ }
659
+
660
+ initialize(window: BrowserWindow): void {
661
+ this.mainWindow = window;
662
+
663
+ // Configure auto-updater
664
+ autoUpdater.autoDownload = this.options.autoDownload ?? false;
665
+ autoUpdater.autoInstallOnAppQuit =
666
+ this.options.autoInstallOnAppQuit ?? true;
667
+
668
+ // Set up event handlers
669
+ this.setupEventHandlers();
670
+
671
+ // Check on startup if enabled
672
+ if (this.options.checkOnStartup) {
673
+ // Delay initial check to let app settle
674
+ setTimeout(() => this.checkForUpdates(), 5000);
675
+ }
676
+
677
+ // Set up periodic checking
678
+ if (this.options.checkInterval && this.options.checkInterval > 0) {
679
+ this.checkIntervalId = setInterval(
680
+ () => this.checkForUpdates(),
681
+ this.options.checkInterval,
682
+ );
683
+ }
684
+ }
685
+
686
+ private setupEventHandlers(): void {
687
+ autoUpdater.on("checking-for-update", () => {
688
+ log.info("Checking for updates...");
689
+ });
690
+
691
+ autoUpdater.on("update-available", (info: UpdateInfo) => {
692
+ log.info("Update available:", info.version);
693
+ this.notifyUpdateAvailable(info);
694
+ });
695
+
696
+ autoUpdater.on("update-not-available", () => {
697
+ log.info("No updates available");
698
+ });
699
+
700
+ autoUpdater.on("error", (error: Error) => {
701
+ log.error("Update error:", error.message);
702
+ this.notifyError(error);
703
+ });
704
+
705
+ autoUpdater.on("download-progress", (progress: ProgressInfo) => {
706
+ log.info(`Download progress: ${progress.percent.toFixed(1)}%`);
707
+ this.mainWindow?.webContents.send("app:update-progress", {
708
+ percent: progress.percent,
709
+ transferred: progress.transferred,
710
+ total: progress.total,
711
+ });
712
+ });
713
+
714
+ autoUpdater.on("update-downloaded", (event: UpdateDownloadedEvent) => {
715
+ log.info("Update downloaded:", event.version);
716
+ this.notifyUpdateDownloaded(event);
717
+ });
718
+ }
719
+
720
+ async checkForUpdates(): Promise<void> {
721
+ try {
722
+ await autoUpdater.checkForUpdates();
723
+ } catch (error) {
724
+ log.error("Failed to check for updates:", error);
725
+ }
726
+ }
727
+
728
+ async downloadUpdate(): Promise<void> {
729
+ try {
730
+ await autoUpdater.downloadUpdate();
731
+ } catch (error) {
732
+ log.error("Failed to download update:", error);
733
+ }
734
+ }
735
+
736
+ installUpdate(): void {
737
+ autoUpdater.quitAndInstall(false, true);
738
+ }
739
+
740
+ private async notifyUpdateAvailable(info: UpdateInfo): Promise<void> {
741
+ // Send to renderer
742
+ this.mainWindow?.webContents.send("app:update-available", {
743
+ version: info.version,
744
+ releaseNotes:
745
+ typeof info.releaseNotes === "string"
746
+ ? info.releaseNotes
747
+ : (info.releaseNotes
748
+ ?.map((n) => `${n.version}: ${n.note}`)
749
+ .join("\n") ?? ""),
750
+ });
751
+
752
+ // Show system notification if supported
753
+ if (Notification.isSupported()) {
754
+ new Notification({
755
+ title: "Update Available",
756
+ body: `Version ${info.version} is available for download.`,
757
+ }).show();
758
+ }
759
+
760
+ // Show dialog
761
+ const result = await dialog.showMessageBox(this.mainWindow!, {
762
+ type: "info",
763
+ title: "Update Available",
764
+ message: `A new version (${info.version}) is available.`,
765
+ detail: "Would you like to download and install it?",
766
+ buttons: ["Download", "Later"],
767
+ defaultId: 0,
768
+ cancelId: 1,
769
+ });
770
+
771
+ if (result.response === 0) {
772
+ this.downloadUpdate();
773
+ }
774
+ }
775
+
776
+ private async notifyUpdateDownloaded(
777
+ event: UpdateDownloadedEvent,
778
+ ): Promise<void> {
779
+ // Send to renderer
780
+ this.mainWindow?.webContents.send("app:update-downloaded", {
781
+ version: event.version,
782
+ });
783
+
784
+ // Show dialog
785
+ const result = await dialog.showMessageBox(this.mainWindow!, {
786
+ type: "info",
787
+ title: "Update Ready",
788
+ message: `Version ${event.version} has been downloaded.`,
789
+ detail: "Would you like to restart and install it now?",
790
+ buttons: ["Restart Now", "Later"],
791
+ defaultId: 0,
792
+ cancelId: 1,
793
+ });
794
+
795
+ if (result.response === 0) {
796
+ this.installUpdate();
797
+ }
798
+ }
799
+
800
+ private notifyError(error: Error): void {
801
+ dialog.showErrorBox(
802
+ "Update Error",
803
+ `An error occurred while updating: ${error.message}`,
804
+ );
805
+ }
806
+
807
+ dispose(): void {
808
+ if (this.checkIntervalId) {
809
+ clearInterval(this.checkIntervalId);
810
+ this.checkIntervalId = null;
811
+ }
812
+ }
813
+ }
814
+ ```
815
+
816
+ ---
817
+
818
+ ## System Tray and Native Menu
819
+
820
+ ### System Tray Service
821
+
822
+ ```typescript
823
+ // src/main/services/tray.ts
824
+ import {
825
+ Tray,
826
+ Menu,
827
+ MenuItemConstructorOptions,
828
+ app,
829
+ nativeImage,
830
+ BrowserWindow,
831
+ } from "electron";
832
+ import { join } from "path";
833
+ import { is } from "@electron-toolkit/utils";
834
+
835
+ export class TrayService {
836
+ private tray: Tray | null = null;
837
+ private mainWindow: BrowserWindow | null = null;
838
+
839
+ initialize(mainWindow: BrowserWindow): void {
840
+ this.mainWindow = mainWindow;
841
+
842
+ // Create tray icon
843
+ const iconPath = is.dev
844
+ ? join(__dirname, "../../resources/icons/tray.png")
845
+ : join(process.resourcesPath, "icons/tray.png");
846
+
847
+ // Use template icon on macOS
848
+ const icon = nativeImage.createFromPath(iconPath);
849
+ if (process.platform === "darwin") {
850
+ icon.setTemplateImage(true);
851
+ }
852
+
853
+ this.tray = new Tray(icon);
854
+ this.tray.setToolTip(app.getName());
855
+
856
+ // Set context menu
857
+ this.updateContextMenu();
858
+
859
+ // Click behavior
860
+ this.tray.on("click", () => {
861
+ this.toggleMainWindow();
862
+ });
863
+
864
+ // Double-click to show (Windows)
865
+ this.tray.on("double-click", () => {
866
+ this.showMainWindow();
867
+ });
868
+ }
869
+
870
+ updateContextMenu(additionalItems: MenuItemConstructorOptions[] = []): void {
871
+ if (!this.tray) return;
872
+
873
+ const contextMenu = Menu.buildFromTemplate([
874
+ {
875
+ label: "Show App",
876
+ click: () => this.showMainWindow(),
877
+ },
878
+ { type: "separator" },
879
+ ...additionalItems,
880
+ { type: "separator" },
881
+ {
882
+ label: "Preferences",
883
+ accelerator: "CmdOrCtrl+,",
884
+ click: () => this.openPreferences(),
885
+ },
886
+ { type: "separator" },
887
+ {
888
+ label: "Check for Updates",
889
+ click: () => this.checkForUpdates(),
890
+ },
891
+ { type: "separator" },
892
+ {
893
+ label: "Quit",
894
+ accelerator: "CmdOrCtrl+Q",
895
+ click: () => app.quit(),
896
+ },
897
+ ]);
898
+
899
+ this.tray.setContextMenu(contextMenu);
900
+ }
901
+
902
+ private toggleMainWindow(): void {
903
+ if (!this.mainWindow) return;
904
+
905
+ if (this.mainWindow.isVisible()) {
906
+ if (this.mainWindow.isFocused()) {
907
+ this.mainWindow.hide();
908
+ } else {
909
+ this.mainWindow.focus();
910
+ }
911
+ } else {
912
+ this.showMainWindow();
913
+ }
914
+ }
915
+
916
+ private showMainWindow(): void {
917
+ if (!this.mainWindow) return;
918
+
919
+ this.mainWindow.show();
920
+ this.mainWindow.focus();
921
+
922
+ // Restore if minimized
923
+ if (this.mainWindow.isMinimized()) {
924
+ this.mainWindow.restore();
925
+ }
926
+ }
927
+
928
+ private openPreferences(): void {
929
+ // Emit event or open preferences window
930
+ this.mainWindow?.webContents.send("app:open-preferences");
931
+ }
932
+
933
+ private checkForUpdates(): void {
934
+ this.mainWindow?.webContents.send("app:check-updates");
935
+ }
936
+
937
+ setBadge(text: string): void {
938
+ if (process.platform === "darwin") {
939
+ app.dock.setBadge(text);
940
+ } else if (this.tray) {
941
+ // Update tray title/tooltip for other platforms
942
+ this.tray.setTitle(text);
943
+ }
944
+ }
945
+
946
+ destroy(): void {
947
+ this.tray?.destroy();
948
+ this.tray = null;
949
+ }
950
+ }
951
+ ```
952
+
953
+ ### Application Menu
954
+
955
+ ```typescript
956
+ // src/main/services/menu.ts
957
+ import {
958
+ Menu,
959
+ app,
960
+ shell,
961
+ BrowserWindow,
962
+ MenuItemConstructorOptions,
963
+ } from "electron";
964
+
965
+ export function createApplicationMenu(mainWindow: BrowserWindow): void {
966
+ const isMac = process.platform === "darwin";
967
+
968
+ const template: MenuItemConstructorOptions[] = [
969
+ // App menu (macOS only)
970
+ ...(isMac
971
+ ? [
972
+ {
973
+ label: app.name,
974
+ submenu: [
975
+ { role: "about" as const },
976
+ { type: "separator" as const },
977
+ {
978
+ label: "Preferences",
979
+ accelerator: "CmdOrCtrl+,",
980
+ click: () =>
981
+ mainWindow.webContents.send("app:open-preferences"),
982
+ },
983
+ { type: "separator" as const },
984
+ { role: "services" as const },
985
+ { type: "separator" as const },
986
+ { role: "hide" as const },
987
+ { role: "hideOthers" as const },
988
+ { role: "unhide" as const },
989
+ { type: "separator" as const },
990
+ { role: "quit" as const },
991
+ ],
992
+ } as MenuItemConstructorOptions,
993
+ ]
994
+ : []),
995
+
996
+ // File menu
997
+ {
998
+ label: "File",
999
+ submenu: [
1000
+ {
1001
+ label: "New",
1002
+ accelerator: "CmdOrCtrl+N",
1003
+ click: () => mainWindow.webContents.send("file:new"),
1004
+ },
1005
+ {
1006
+ label: "Open...",
1007
+ accelerator: "CmdOrCtrl+O",
1008
+ click: () => mainWindow.webContents.send("file:open"),
1009
+ },
1010
+ { type: "separator" },
1011
+ {
1012
+ label: "Save",
1013
+ accelerator: "CmdOrCtrl+S",
1014
+ click: () => mainWindow.webContents.send("file:save"),
1015
+ },
1016
+ {
1017
+ label: "Save As...",
1018
+ accelerator: "CmdOrCtrl+Shift+S",
1019
+ click: () => mainWindow.webContents.send("file:save-as"),
1020
+ },
1021
+ { type: "separator" },
1022
+ isMac ? { role: "close" } : { role: "quit" },
1023
+ ],
1024
+ },
1025
+
1026
+ // Edit menu
1027
+ {
1028
+ label: "Edit",
1029
+ submenu: [
1030
+ { role: "undo" },
1031
+ { role: "redo" },
1032
+ { type: "separator" },
1033
+ { role: "cut" },
1034
+ { role: "copy" },
1035
+ { role: "paste" },
1036
+ { role: "delete" },
1037
+ { type: "separator" },
1038
+ { role: "selectAll" },
1039
+ ...(isMac
1040
+ ? [
1041
+ { type: "separator" as const },
1042
+ {
1043
+ label: "Speech",
1044
+ submenu: [
1045
+ { role: "startSpeaking" as const },
1046
+ { role: "stopSpeaking" as const },
1047
+ ],
1048
+ },
1049
+ ]
1050
+ : []),
1051
+ ],
1052
+ },
1053
+
1054
+ // View menu
1055
+ {
1056
+ label: "View",
1057
+ submenu: [
1058
+ { role: "reload" },
1059
+ { role: "forceReload" },
1060
+ { role: "toggleDevTools" },
1061
+ { type: "separator" },
1062
+ { role: "resetZoom" },
1063
+ { role: "zoomIn" },
1064
+ { role: "zoomOut" },
1065
+ { type: "separator" },
1066
+ { role: "togglefullscreen" },
1067
+ ],
1068
+ },
1069
+
1070
+ // Window menu
1071
+ {
1072
+ label: "Window",
1073
+ submenu: [
1074
+ { role: "minimize" },
1075
+ { role: "zoom" },
1076
+ ...(isMac
1077
+ ? [
1078
+ { type: "separator" as const },
1079
+ { role: "front" as const },
1080
+ { type: "separator" as const },
1081
+ { role: "window" as const },
1082
+ ]
1083
+ : [{ role: "close" as const }]),
1084
+ ],
1085
+ },
1086
+
1087
+ // Help menu
1088
+ {
1089
+ label: "Help",
1090
+ submenu: [
1091
+ {
1092
+ label: "Documentation",
1093
+ click: () => shell.openExternal("https://docs.example.com"),
1094
+ },
1095
+ {
1096
+ label: "Release Notes",
1097
+ click: () =>
1098
+ shell.openExternal("https://github.com/example/repo/releases"),
1099
+ },
1100
+ { type: "separator" },
1101
+ {
1102
+ label: "Report Issue",
1103
+ click: () =>
1104
+ shell.openExternal("https://github.com/example/repo/issues"),
1105
+ },
1106
+ ],
1107
+ },
1108
+ ];
1109
+
1110
+ const menu = Menu.buildFromTemplate(template);
1111
+ Menu.setApplicationMenu(menu);
1112
+ }
1113
+ ```
1114
+
1115
+ ---
1116
+
1117
+ ## Window State Persistence
1118
+
1119
+ ### Window Manager with State
1120
+
1121
+ ```typescript
1122
+ // src/main/windows/window-manager.ts
1123
+ import {
1124
+ BrowserWindow,
1125
+ BrowserWindowConstructorOptions,
1126
+ screen,
1127
+ Rectangle,
1128
+ } from "electron";
1129
+ import Store from "electron-store";
1130
+ import { join } from "path";
1131
+
1132
+ interface WindowState {
1133
+ width: number;
1134
+ height: number;
1135
+ x: number | undefined;
1136
+ y: number | undefined;
1137
+ isMaximized: boolean;
1138
+ isFullScreen: boolean;
1139
+ }
1140
+
1141
+ interface WindowConfig {
1142
+ id: string;
1143
+ defaultWidth: number;
1144
+ defaultHeight: number;
1145
+ minWidth?: number;
1146
+ minHeight?: number;
1147
+ }
1148
+
1149
+ const windowStateStore = new Store<Record<string, WindowState>>({
1150
+ name: "window-state",
1151
+ });
1152
+
1153
+ export class WindowManager {
1154
+ private windows = new Map<string, BrowserWindow>();
1155
+ private stateUpdateDebounce = new Map<string, NodeJS.Timeout>();
1156
+
1157
+ createWindow(
1158
+ id: string,
1159
+ options: BrowserWindowConstructorOptions = {},
1160
+ ): BrowserWindow {
1161
+ // Get saved state or calculate default
1162
+ const savedState = this.getWindowState(id);
1163
+ const { width, height } = screen.getPrimaryDisplay().workAreaSize;
1164
+
1165
+ const defaultConfig: WindowConfig = {
1166
+ id,
1167
+ defaultWidth: Math.floor(width * 0.8),
1168
+ defaultHeight: Math.floor(height * 0.8),
1169
+ minWidth: options.minWidth ?? 400,
1170
+ minHeight: options.minHeight ?? 300,
1171
+ };
1172
+
1173
+ // Calculate initial bounds
1174
+ const bounds = this.calculateBounds(savedState, defaultConfig);
1175
+
1176
+ const window = new BrowserWindow({
1177
+ ...bounds,
1178
+ show: false,
1179
+ ...options,
1180
+ webPreferences: {
1181
+ preload: join(__dirname, "../preload/index.js"),
1182
+ sandbox: true,
1183
+ contextIsolation: true,
1184
+ nodeIntegration: false,
1185
+ ...options.webPreferences,
1186
+ },
1187
+ });
1188
+
1189
+ // Restore maximized/fullscreen state
1190
+ if (savedState?.isMaximized) {
1191
+ window.maximize();
1192
+ }
1193
+ if (savedState?.isFullScreen) {
1194
+ window.setFullScreen(true);
1195
+ }
1196
+
1197
+ // Show when ready
1198
+ window.once("ready-to-show", () => {
1199
+ window.show();
1200
+ });
1201
+
1202
+ // Track state changes
1203
+ this.trackWindowState(id, window);
1204
+
1205
+ // Handle close
1206
+ window.on("closed", () => {
1207
+ this.windows.delete(id);
1208
+ const timeout = this.stateUpdateDebounce.get(id);
1209
+ if (timeout) {
1210
+ clearTimeout(timeout);
1211
+ this.stateUpdateDebounce.delete(id);
1212
+ }
1213
+ });
1214
+
1215
+ this.windows.set(id, window);
1216
+ return window;
1217
+ }
1218
+
1219
+ getWindow(id: string): BrowserWindow | undefined {
1220
+ const window = this.windows.get(id);
1221
+ if (window && !window.isDestroyed()) {
1222
+ return window;
1223
+ }
1224
+ return undefined;
1225
+ }
1226
+
1227
+ getAllWindows(): BrowserWindow[] {
1228
+ return Array.from(this.windows.values()).filter((w) => !w.isDestroyed());
1229
+ }
1230
+
1231
+ closeWindow(id: string): void {
1232
+ const window = this.getWindow(id);
1233
+ if (window) {
1234
+ window.close();
1235
+ }
1236
+ }
1237
+
1238
+ closeAll(): void {
1239
+ for (const window of this.getAllWindows()) {
1240
+ window.close();
1241
+ }
1242
+ }
1243
+
1244
+ private getWindowState(id: string): WindowState | undefined {
1245
+ return windowStateStore.get(id);
1246
+ }
1247
+
1248
+ private saveWindowState(id: string, state: WindowState): void {
1249
+ windowStateStore.set(id, state);
1250
+ }
1251
+
1252
+ private calculateBounds(
1253
+ savedState: WindowState | undefined,
1254
+ config: WindowConfig,
1255
+ ): Rectangle {
1256
+ const { width, height, x, y } = screen.getPrimaryDisplay().workAreaSize;
1257
+
1258
+ // Use saved state if available and valid
1259
+ if (savedState && this.isValidBounds(savedState)) {
1260
+ return {
1261
+ width: savedState.width,
1262
+ height: savedState.height,
1263
+ x: savedState.x ?? Math.floor((width - savedState.width) / 2),
1264
+ y: savedState.y ?? Math.floor((height - savedState.height) / 2),
1265
+ };
1266
+ }
1267
+
1268
+ // Center window with default size
1269
+ return {
1270
+ width: config.defaultWidth,
1271
+ height: config.defaultHeight,
1272
+ x: Math.floor((width - config.defaultWidth) / 2),
1273
+ y: Math.floor((height - config.defaultHeight) / 2),
1274
+ };
1275
+ }
1276
+
1277
+ private isValidBounds(state: WindowState): boolean {
1278
+ const displays = screen.getAllDisplays();
1279
+
1280
+ // Check if window is visible on any display
1281
+ return displays.some((display) => {
1282
+ const { x, y, width, height } = display.bounds;
1283
+ const windowX = state.x ?? 0;
1284
+ const windowY = state.y ?? 0;
1285
+
1286
+ return (
1287
+ windowX >= x - state.width &&
1288
+ windowX <= x + width &&
1289
+ windowY >= y - state.height &&
1290
+ windowY <= y + height
1291
+ );
1292
+ });
1293
+ }
1294
+
1295
+ private trackWindowState(id: string, window: BrowserWindow): void {
1296
+ const saveState = (): void => {
1297
+ // Debounce state updates
1298
+ const existing = this.stateUpdateDebounce.get(id);
1299
+ if (existing) {
1300
+ clearTimeout(existing);
1301
+ }
1302
+
1303
+ this.stateUpdateDebounce.set(
1304
+ id,
1305
+ setTimeout(() => {
1306
+ if (window.isDestroyed()) return;
1307
+
1308
+ const bounds = window.getBounds();
1309
+ this.saveWindowState(id, {
1310
+ width: bounds.width,
1311
+ height: bounds.height,
1312
+ x: bounds.x,
1313
+ y: bounds.y,
1314
+ isMaximized: window.isMaximized(),
1315
+ isFullScreen: window.isFullScreen(),
1316
+ });
1317
+ }, 500),
1318
+ );
1319
+ };
1320
+
1321
+ window.on("resize", saveState);
1322
+ window.on("move", saveState);
1323
+ window.on("maximize", saveState);
1324
+ window.on("unmaximize", saveState);
1325
+ window.on("enter-full-screen", saveState);
1326
+ window.on("leave-full-screen", saveState);
1327
+
1328
+ // Save on close
1329
+ window.on("close", () => {
1330
+ if (!window.isDestroyed()) {
1331
+ const bounds = window.getBounds();
1332
+ this.saveWindowState(id, {
1333
+ width: bounds.width,
1334
+ height: bounds.height,
1335
+ x: bounds.x,
1336
+ y: bounds.y,
1337
+ isMaximized: window.isMaximized(),
1338
+ isFullScreen: window.isFullScreen(),
1339
+ });
1340
+ }
1341
+ });
1342
+ }
1343
+ }
1344
+
1345
+ export const windowManager = new WindowManager();
1346
+ ```
1347
+
1348
+ ---
1349
+
1350
+ ## Secure File Operations
1351
+
1352
+ ### File Service with Validation
1353
+
1354
+ ```typescript
1355
+ // src/main/services/file-service.ts
1356
+ import {
1357
+ readFile,
1358
+ writeFile,
1359
+ access,
1360
+ mkdir,
1361
+ stat,
1362
+ readdir,
1363
+ unlink,
1364
+ rename,
1365
+ } from "fs/promises";
1366
+ import { constants, createReadStream, createWriteStream } from "fs";
1367
+ import { join, dirname, basename, extname, normalize, resolve } from "path";
1368
+ import { app } from "electron";
1369
+ import { z } from "zod";
1370
+ import { pipeline } from "stream/promises";
1371
+ import { createHash } from "crypto";
1372
+
1373
+ // Validation schemas
1374
+ const SafePathSchema = z
1375
+ .string()
1376
+ .min(1)
1377
+ .max(4096)
1378
+ .refine(
1379
+ (path) => {
1380
+ const normalized = normalize(path);
1381
+ // Prevent path traversal
1382
+ return !normalized.includes("..") && !normalized.includes("\0");
1383
+ },
1384
+ { message: "Invalid path: potential path traversal detected" },
1385
+ );
1386
+
1387
+ const FileNameSchema = z
1388
+ .string()
1389
+ .min(1)
1390
+ .max(255)
1391
+ .regex(/^[^<>:"/\\|?*\x00-\x1f]+$/, {
1392
+ message: "Invalid filename: contains reserved characters",
1393
+ });
1394
+
1395
+ export interface FileResult<T = unknown> {
1396
+ success: boolean;
1397
+ data?: T;
1398
+ error?: string;
1399
+ }
1400
+
1401
+ export interface FileMetadata {
1402
+ name: string;
1403
+ path: string;
1404
+ size: number;
1405
+ isDirectory: boolean;
1406
+ created: Date;
1407
+ modified: Date;
1408
+ extension: string;
1409
+ }
1410
+
1411
+ export class FileService {
1412
+ private allowedDirectories: string[];
1413
+
1414
+ constructor() {
1415
+ // Define allowed directories for file operations
1416
+ this.allowedDirectories = [
1417
+ app.getPath("documents"),
1418
+ app.getPath("downloads"),
1419
+ app.getPath("userData"),
1420
+ app.getPath("temp"),
1421
+ ];
1422
+ }
1423
+
1424
+ async read(filePath: string): Promise<FileResult<string>> {
1425
+ try {
1426
+ const validPath = this.validatePath(filePath);
1427
+ const content = await readFile(validPath, "utf-8");
1428
+ return { success: true, data: content };
1429
+ } catch (error) {
1430
+ return this.handleError(error);
1431
+ }
1432
+ }
1433
+
1434
+ async readBinary(filePath: string): Promise<FileResult<Buffer>> {
1435
+ try {
1436
+ const validPath = this.validatePath(filePath);
1437
+ const content = await readFile(validPath);
1438
+ return { success: true, data: content };
1439
+ } catch (error) {
1440
+ return this.handleError(error);
1441
+ }
1442
+ }
1443
+
1444
+ async write(
1445
+ filePath: string,
1446
+ content: string | Buffer,
1447
+ ): Promise<FileResult<void>> {
1448
+ try {
1449
+ const validPath = this.validatePath(filePath);
1450
+
1451
+ // Ensure directory exists
1452
+ await mkdir(dirname(validPath), { recursive: true });
1453
+
1454
+ await writeFile(validPath, content);
1455
+ return { success: true };
1456
+ } catch (error) {
1457
+ return this.handleError(error);
1458
+ }
1459
+ }
1460
+
1461
+ async exists(filePath: string): Promise<boolean> {
1462
+ try {
1463
+ const validPath = this.validatePath(filePath);
1464
+ await access(validPath, constants.F_OK);
1465
+ return true;
1466
+ } catch {
1467
+ return false;
1468
+ }
1469
+ }
1470
+
1471
+ async getMetadata(filePath: string): Promise<FileResult<FileMetadata>> {
1472
+ try {
1473
+ const validPath = this.validatePath(filePath);
1474
+ const stats = await stat(validPath);
1475
+
1476
+ return {
1477
+ success: true,
1478
+ data: {
1479
+ name: basename(validPath),
1480
+ path: validPath,
1481
+ size: stats.size,
1482
+ isDirectory: stats.isDirectory(),
1483
+ created: stats.birthtime,
1484
+ modified: stats.mtime,
1485
+ extension: extname(validPath),
1486
+ },
1487
+ };
1488
+ } catch (error) {
1489
+ return this.handleError(error);
1490
+ }
1491
+ }
1492
+
1493
+ async listDirectory(dirPath: string): Promise<FileResult<FileMetadata[]>> {
1494
+ try {
1495
+ const validPath = this.validatePath(dirPath);
1496
+ const entries = await readdir(validPath, { withFileTypes: true });
1497
+
1498
+ const metadata: FileMetadata[] = await Promise.all(
1499
+ entries.map(async (entry) => {
1500
+ const entryPath = join(validPath, entry.name);
1501
+ const stats = await stat(entryPath);
1502
+
1503
+ return {
1504
+ name: entry.name,
1505
+ path: entryPath,
1506
+ size: stats.size,
1507
+ isDirectory: entry.isDirectory(),
1508
+ created: stats.birthtime,
1509
+ modified: stats.mtime,
1510
+ extension: entry.isDirectory() ? "" : extname(entry.name),
1511
+ };
1512
+ }),
1513
+ );
1514
+
1515
+ return { success: true, data: metadata };
1516
+ } catch (error) {
1517
+ return this.handleError(error);
1518
+ }
1519
+ }
1520
+
1521
+ async delete(filePath: string): Promise<FileResult<void>> {
1522
+ try {
1523
+ const validPath = this.validatePath(filePath);
1524
+ await unlink(validPath);
1525
+ return { success: true };
1526
+ } catch (error) {
1527
+ return this.handleError(error);
1528
+ }
1529
+ }
1530
+
1531
+ async move(sourcePath: string, destPath: string): Promise<FileResult<void>> {
1532
+ try {
1533
+ const validSource = this.validatePath(sourcePath);
1534
+ const validDest = this.validatePath(destPath);
1535
+
1536
+ await mkdir(dirname(validDest), { recursive: true });
1537
+ await rename(validSource, validDest);
1538
+
1539
+ return { success: true };
1540
+ } catch (error) {
1541
+ return this.handleError(error);
1542
+ }
1543
+ }
1544
+
1545
+ async copy(sourcePath: string, destPath: string): Promise<FileResult<void>> {
1546
+ try {
1547
+ const validSource = this.validatePath(sourcePath);
1548
+ const validDest = this.validatePath(destPath);
1549
+
1550
+ await mkdir(dirname(validDest), { recursive: true });
1551
+
1552
+ await pipeline(
1553
+ createReadStream(validSource),
1554
+ createWriteStream(validDest),
1555
+ );
1556
+
1557
+ return { success: true };
1558
+ } catch (error) {
1559
+ return this.handleError(error);
1560
+ }
1561
+ }
1562
+
1563
+ async getHash(
1564
+ filePath: string,
1565
+ algorithm: "md5" | "sha256" = "sha256",
1566
+ ): Promise<FileResult<string>> {
1567
+ try {
1568
+ const validPath = this.validatePath(filePath);
1569
+ const hash = createHash(algorithm);
1570
+ const stream = createReadStream(validPath);
1571
+
1572
+ return new Promise((resolve) => {
1573
+ stream.on("data", (chunk) => hash.update(chunk));
1574
+ stream.on("end", () => {
1575
+ resolve({ success: true, data: hash.digest("hex") });
1576
+ });
1577
+ stream.on("error", (error) => {
1578
+ resolve(this.handleError(error));
1579
+ });
1580
+ });
1581
+ } catch (error) {
1582
+ return this.handleError(error);
1583
+ }
1584
+ }
1585
+
1586
+ private validatePath(filePath: string): string {
1587
+ // Validate path format
1588
+ const safePath = SafePathSchema.parse(filePath);
1589
+
1590
+ // Resolve to absolute path
1591
+ const absolutePath = resolve(safePath);
1592
+
1593
+ // Validate filename if applicable
1594
+ const fileName = basename(absolutePath);
1595
+ if (fileName && !fileName.startsWith(".")) {
1596
+ FileNameSchema.parse(fileName);
1597
+ }
1598
+
1599
+ // Optional: Check if path is within allowed directories
1600
+ // Uncomment if you want to restrict file access
1601
+ // this.validateAllowedDirectory(absolutePath);
1602
+
1603
+ return absolutePath;
1604
+ }
1605
+
1606
+ private validateAllowedDirectory(absolutePath: string): void {
1607
+ const isAllowed = this.allowedDirectories.some((dir) =>
1608
+ absolutePath.startsWith(dir),
1609
+ );
1610
+
1611
+ if (!isAllowed) {
1612
+ throw new Error(`Access denied: path is outside allowed directories`);
1613
+ }
1614
+ }
1615
+
1616
+ private handleError<T>(error: unknown): FileResult<T> {
1617
+ const message =
1618
+ error instanceof Error ? error.message : "Unknown error occurred";
1619
+ return { success: false, error: message };
1620
+ }
1621
+ }
1622
+
1623
+ export const fileService = new FileService();
1624
+ ```
1625
+
1626
+ ---
1627
+
1628
+ ## React Renderer Integration
1629
+
1630
+ ### Electron API Hook
1631
+
1632
+ ```typescript
1633
+ // src/renderer/src/hooks/useElectron.ts
1634
+ import { useEffect, useState, useCallback } from "react";
1635
+
1636
+ // Type from preload
1637
+ type ElectronAPI = Window["electronAPI"];
1638
+
1639
+ export function useElectron(): ElectronAPI {
1640
+ if (!window.electronAPI) {
1641
+ throw new Error("Electron API not available. Are you running in Electron?");
1642
+ }
1643
+ return window.electronAPI;
1644
+ }
1645
+
1646
+ export function useWindowMaximized(): boolean {
1647
+ const [isMaximized, setIsMaximized] = useState(false);
1648
+ const electron = useElectron();
1649
+
1650
+ useEffect(() => {
1651
+ // Get initial state
1652
+ electron.window.isMaximized().then(setIsMaximized);
1653
+
1654
+ // Subscribe to changes
1655
+ const unsubscribe = electron.window.onMaximizeChange(setIsMaximized);
1656
+ return unsubscribe;
1657
+ }, [electron]);
1658
+
1659
+ return isMaximized;
1660
+ }
1661
+
1662
+ export function useAutoUpdate() {
1663
+ const electron = useElectron();
1664
+ const [updateAvailable, setUpdateAvailable] = useState<{
1665
+ version: string;
1666
+ releaseNotes: string;
1667
+ } | null>(null);
1668
+ const [downloadProgress, setDownloadProgress] = useState<{
1669
+ percent: number;
1670
+ transferred: number;
1671
+ total: number;
1672
+ } | null>(null);
1673
+ const [updateReady, setUpdateReady] = useState(false);
1674
+
1675
+ useEffect(() => {
1676
+ const unsubAvailable = electron.update.onAvailable((info) => {
1677
+ setUpdateAvailable(info);
1678
+ });
1679
+
1680
+ const unsubProgress = electron.update.onProgress((progress) => {
1681
+ setDownloadProgress(progress);
1682
+ });
1683
+
1684
+ const unsubDownloaded = electron.update.onDownloaded(() => {
1685
+ setUpdateReady(true);
1686
+ setDownloadProgress(null);
1687
+ });
1688
+
1689
+ return () => {
1690
+ unsubAvailable();
1691
+ unsubProgress();
1692
+ unsubDownloaded();
1693
+ };
1694
+ }, [electron]);
1695
+
1696
+ const checkForUpdates = useCallback(() => {
1697
+ electron.update.check();
1698
+ }, [electron]);
1699
+
1700
+ const downloadUpdate = useCallback(() => {
1701
+ electron.update.download();
1702
+ }, [electron]);
1703
+
1704
+ const installUpdate = useCallback(() => {
1705
+ electron.update.install();
1706
+ }, [electron]);
1707
+
1708
+ return {
1709
+ updateAvailable,
1710
+ downloadProgress,
1711
+ updateReady,
1712
+ checkForUpdates,
1713
+ downloadUpdate,
1714
+ installUpdate,
1715
+ };
1716
+ }
1717
+
1718
+ export function useStorage<T>(
1719
+ key: string,
1720
+ defaultValue: T,
1721
+ ): [T, (value: T) => Promise<void>, boolean] {
1722
+ const electron = useElectron();
1723
+ const [value, setValue] = useState<T>(defaultValue);
1724
+ const [loading, setLoading] = useState(true);
1725
+
1726
+ useEffect(() => {
1727
+ electron.storage.get<T>(key).then((stored) => {
1728
+ if (stored !== undefined) {
1729
+ setValue(stored);
1730
+ }
1731
+ setLoading(false);
1732
+ });
1733
+ }, [electron, key]);
1734
+
1735
+ const updateValue = useCallback(
1736
+ async (newValue: T) => {
1737
+ setValue(newValue);
1738
+ await electron.storage.set(key, newValue);
1739
+ },
1740
+ [electron, key],
1741
+ );
1742
+
1743
+ return [value, updateValue, loading];
1744
+ }
1745
+ ```
1746
+
1747
+ ### Custom Title Bar Component
1748
+
1749
+ ```tsx
1750
+ // src/renderer/src/components/TitleBar.tsx
1751
+ import { useElectron, useWindowMaximized } from "../hooks/useElectron";
1752
+ import styles from "./TitleBar.module.css";
1753
+
1754
+ interface TitleBarProps {
1755
+ title?: string;
1756
+ }
1757
+
1758
+ export function TitleBar({ title = "My App" }: TitleBarProps) {
1759
+ const electron = useElectron();
1760
+ const isMaximized = useWindowMaximized();
1761
+
1762
+ return (
1763
+ <div className={styles.titleBar}>
1764
+ {/* Drag region */}
1765
+ <div className={styles.dragRegion}>
1766
+ <span className={styles.title}>{title}</span>
1767
+ </div>
1768
+
1769
+ {/* Window controls */}
1770
+ <div className={styles.windowControls}>
1771
+ {!electron.platform.isMac && (
1772
+ <>
1773
+ <button
1774
+ className={styles.controlButton}
1775
+ onClick={() => electron.window.minimize()}
1776
+ aria-label="Minimize"
1777
+ >
1778
+ <MinimizeIcon />
1779
+ </button>
1780
+ <button
1781
+ className={styles.controlButton}
1782
+ onClick={() => electron.window.maximize()}
1783
+ aria-label={isMaximized ? "Restore" : "Maximize"}
1784
+ >
1785
+ {isMaximized ? <RestoreIcon /> : <MaximizeIcon />}
1786
+ </button>
1787
+ <button
1788
+ className={`${styles.controlButton} ${styles.closeButton}`}
1789
+ onClick={() => electron.window.close()}
1790
+ aria-label="Close"
1791
+ >
1792
+ <CloseIcon />
1793
+ </button>
1794
+ </>
1795
+ )}
1796
+ </div>
1797
+ </div>
1798
+ );
1799
+ }
1800
+
1801
+ // Icon components
1802
+ function MinimizeIcon() {
1803
+ return (
1804
+ <svg width="10" height="1" viewBox="0 0 10 1">
1805
+ <rect fill="currentColor" width="10" height="1" />
1806
+ </svg>
1807
+ );
1808
+ }
1809
+
1810
+ function MaximizeIcon() {
1811
+ return (
1812
+ <svg width="10" height="10" viewBox="0 0 10 10">
1813
+ <rect
1814
+ fill="none"
1815
+ stroke="currentColor"
1816
+ strokeWidth="1"
1817
+ x="0.5"
1818
+ y="0.5"
1819
+ width="9"
1820
+ height="9"
1821
+ />
1822
+ </svg>
1823
+ );
1824
+ }
1825
+
1826
+ function RestoreIcon() {
1827
+ return (
1828
+ <svg width="10" height="10" viewBox="0 0 10 10">
1829
+ <rect
1830
+ fill="none"
1831
+ stroke="currentColor"
1832
+ strokeWidth="1"
1833
+ x="2.5"
1834
+ y="0.5"
1835
+ width="7"
1836
+ height="7"
1837
+ />
1838
+ <rect
1839
+ fill="none"
1840
+ stroke="currentColor"
1841
+ strokeWidth="1"
1842
+ x="0.5"
1843
+ y="2.5"
1844
+ width="7"
1845
+ height="7"
1846
+ />
1847
+ </svg>
1848
+ );
1849
+ }
1850
+
1851
+ function CloseIcon() {
1852
+ return (
1853
+ <svg width="10" height="10" viewBox="0 0 10 10">
1854
+ <path
1855
+ fill="currentColor"
1856
+ d="M1 0L0 1l4 4-4 4 1 1 4-4 4 4 1-1-4-4 4-4-1-1-4 4-4-4z"
1857
+ />
1858
+ </svg>
1859
+ );
1860
+ }
1861
+ ```
1862
+
1863
+ ```css
1864
+ /* src/renderer/src/components/TitleBar.module.css */
1865
+ .titleBar {
1866
+ display: flex;
1867
+ height: 32px;
1868
+ background: var(--titlebar-bg, #2d2d2d);
1869
+ color: var(--titlebar-color, #ffffff);
1870
+ user-select: none;
1871
+ }
1872
+
1873
+ .dragRegion {
1874
+ flex: 1;
1875
+ display: flex;
1876
+ align-items: center;
1877
+ padding-left: 12px;
1878
+ -webkit-app-region: drag;
1879
+ }
1880
+
1881
+ .title {
1882
+ font-size: 12px;
1883
+ font-weight: 500;
1884
+ }
1885
+
1886
+ .windowControls {
1887
+ display: flex;
1888
+ -webkit-app-region: no-drag;
1889
+ }
1890
+
1891
+ .controlButton {
1892
+ width: 46px;
1893
+ height: 32px;
1894
+ display: flex;
1895
+ align-items: center;
1896
+ justify-content: center;
1897
+ background: transparent;
1898
+ border: none;
1899
+ color: inherit;
1900
+ cursor: pointer;
1901
+ transition: background-color 0.1s;
1902
+ }
1903
+
1904
+ .controlButton:hover {
1905
+ background: rgba(255, 255, 255, 0.1);
1906
+ }
1907
+
1908
+ .controlButton:active {
1909
+ background: rgba(255, 255, 255, 0.2);
1910
+ }
1911
+
1912
+ .closeButton:hover {
1913
+ background: #e81123;
1914
+ }
1915
+ ```
1916
+
1917
+ ---
1918
+
1919
+ ## Testing with Playwright
1920
+
1921
+ ### E2E Test Setup
1922
+
1923
+ ```typescript
1924
+ // e2e/electron.spec.ts
1925
+ import { test, expect, _electron as electron } from "@playwright/test";
1926
+ import type { ElectronApplication, Page } from "@playwright/test";
1927
+
1928
+ let app: ElectronApplication;
1929
+ let page: Page;
1930
+
1931
+ test.beforeAll(async () => {
1932
+ // Launch Electron app
1933
+ app = await electron.launch({
1934
+ args: ["."],
1935
+ env: {
1936
+ ...process.env,
1937
+ NODE_ENV: "test",
1938
+ },
1939
+ });
1940
+
1941
+ // Get the first window
1942
+ page = await app.firstWindow();
1943
+
1944
+ // Wait for app to be ready
1945
+ await page.waitForLoadState("domcontentloaded");
1946
+ });
1947
+
1948
+ test.afterAll(async () => {
1949
+ await app.close();
1950
+ });
1951
+
1952
+ test.describe("Main Window", () => {
1953
+ test("should display title", async () => {
1954
+ const title = await page.title();
1955
+ expect(title).toBe("My App");
1956
+ });
1957
+
1958
+ test("should have correct dimensions", async () => {
1959
+ const { width, height } = page.viewportSize()!;
1960
+ expect(width).toBeGreaterThanOrEqual(800);
1961
+ expect(height).toBeGreaterThanOrEqual(600);
1962
+ });
1963
+
1964
+ test("should show main content", async () => {
1965
+ await expect(page.locator("#app")).toBeVisible();
1966
+ });
1967
+ });
1968
+
1969
+ test.describe("Window Controls", () => {
1970
+ test("should minimize window", async () => {
1971
+ // Click minimize button
1972
+ await page.click('[aria-label="Minimize"]');
1973
+
1974
+ // Verify window is minimized
1975
+ const isMinimized = await app.evaluate(({ BrowserWindow }) => {
1976
+ const window = BrowserWindow.getAllWindows()[0];
1977
+ return window.isMinimized();
1978
+ });
1979
+
1980
+ expect(isMinimized).toBe(true);
1981
+
1982
+ // Restore for next tests
1983
+ await app.evaluate(({ BrowserWindow }) => {
1984
+ const window = BrowserWindow.getAllWindows()[0];
1985
+ window.restore();
1986
+ });
1987
+ });
1988
+
1989
+ test("should maximize/restore window", async () => {
1990
+ // Click maximize button
1991
+ await page.click('[aria-label="Maximize"]');
1992
+
1993
+ // Verify window is maximized
1994
+ let isMaximized = await app.evaluate(({ BrowserWindow }) => {
1995
+ const window = BrowserWindow.getAllWindows()[0];
1996
+ return window.isMaximized();
1997
+ });
1998
+
1999
+ expect(isMaximized).toBe(true);
2000
+
2001
+ // Click restore button
2002
+ await page.click('[aria-label="Restore"]');
2003
+
2004
+ // Verify window is not maximized
2005
+ isMaximized = await app.evaluate(({ BrowserWindow }) => {
2006
+ const window = BrowserWindow.getAllWindows()[0];
2007
+ return window.isMaximized();
2008
+ });
2009
+
2010
+ expect(isMaximized).toBe(false);
2011
+ });
2012
+ });
2013
+
2014
+ test.describe("IPC Communication", () => {
2015
+ test("should get app version", async () => {
2016
+ const version = await page.evaluate(async () => {
2017
+ return window.electronAPI.app.getVersion();
2018
+ });
2019
+
2020
+ expect(version).toMatch(/^\d+\.\d+\.\d+$/);
2021
+ });
2022
+
2023
+ test("should access storage", async () => {
2024
+ // Set value
2025
+ await page.evaluate(async () => {
2026
+ await window.electronAPI.storage.set("test-key", { foo: "bar" });
2027
+ });
2028
+
2029
+ // Get value
2030
+ const value = await page.evaluate(async () => {
2031
+ return window.electronAPI.storage.get("test-key");
2032
+ });
2033
+
2034
+ expect(value).toEqual({ foo: "bar" });
2035
+
2036
+ // Clean up
2037
+ await page.evaluate(async () => {
2038
+ await window.electronAPI.storage.delete("test-key");
2039
+ });
2040
+ });
2041
+ });
2042
+
2043
+ test.describe("File Operations", () => {
2044
+ test("should check if file exists", async () => {
2045
+ const exists = await page.evaluate(async () => {
2046
+ return window.electronAPI.file.exists("package.json");
2047
+ });
2048
+
2049
+ expect(exists).toBe(true);
2050
+ });
2051
+ });
2052
+ ```
2053
+
2054
+ ### Playwright Configuration
2055
+
2056
+ ```typescript
2057
+ // playwright.config.ts
2058
+ import { defineConfig } from "@playwright/test";
2059
+
2060
+ export default defineConfig({
2061
+ testDir: "./e2e",
2062
+ timeout: 30000,
2063
+ expect: {
2064
+ timeout: 5000,
2065
+ },
2066
+ fullyParallel: false, // Electron tests should run sequentially
2067
+ forbidOnly: !!process.env.CI,
2068
+ retries: process.env.CI ? 2 : 0,
2069
+ workers: 1,
2070
+ reporter: "html",
2071
+ use: {
2072
+ trace: "on-first-retry",
2073
+ screenshot: "only-on-failure",
2074
+ },
2075
+ });
2076
+ ```
2077
+
2078
+ ---
2079
+
2080
+ Version: 1.1.0
2081
+ Last Updated: 2026-01-10
2082
+ Changes: Aligned with SKILL.md v1.1.0 updates