cfsa-antigravity 1.0.0

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 (378) hide show
  1. package/bin/cli.mjs +324 -0
  2. package/package.json +34 -0
  3. package/template/.agent/instructions/commands.md +48 -0
  4. package/template/.agent/instructions/patterns.md +61 -0
  5. package/template/.agent/instructions/structure.md +29 -0
  6. package/template/.agent/instructions/tech-stack.md +43 -0
  7. package/template/.agent/instructions/workflow.md +41 -0
  8. package/template/.agent/kit-sync.md +15 -0
  9. package/template/.agent/rules/boundary-not-placeholder.md +146 -0
  10. package/template/.agent/rules/completion-checklist.md +48 -0
  11. package/template/.agent/rules/decision-classification.md +103 -0
  12. package/template/.agent/rules/extensibility.md +47 -0
  13. package/template/.agent/rules/question-vs-command.md +81 -0
  14. package/template/.agent/rules/security-first.md +43 -0
  15. package/template/.agent/rules/specificity-standards.md +54 -0
  16. package/template/.agent/rules/tdd-contract-first.md +57 -0
  17. package/template/.agent/rules/vertical-slices.md +42 -0
  18. package/template/.agent/skill-library/MANIFEST.md +480 -0
  19. package/template/.agent/skill-library/README.md +38 -0
  20. package/template/.agent/skill-library/meta/brand-guidelines/SKILL.md +73 -0
  21. package/template/.agent/skill-library/meta/claude-code/README.md +9 -0
  22. package/template/.agent/skill-library/meta/claude-code/agent-development/SKILL.md +415 -0
  23. package/template/.agent/skill-library/meta/claude-code/hook-development/SKILL.md +712 -0
  24. package/template/.agent/skill-library/meta/claude-code/plugin-structure/SKILL.md +476 -0
  25. package/template/.agent/skill-library/meta/git-advanced/SKILL.md +972 -0
  26. package/template/.agent/skill-library/meta/mcp-builder/SKILL.md +236 -0
  27. package/template/.agent/skill-library/meta/product-marketing-context/SKILL.md +241 -0
  28. package/template/.agent/skill-library/meta/regex-patterns/SKILL.md +751 -0
  29. package/template/.agent/skill-library/meta/tmux-processes/SKILL.md +210 -0
  30. package/template/.agent/skill-library/meta/using-tmux-for-interactive-commands/SKILL.md +178 -0
  31. package/template/.agent/skill-library/stack/3d/threejs-pro/SKILL.md +300 -0
  32. package/template/.agent/skill-library/stack/ai/ai-sdk/SKILL.md +77 -0
  33. package/template/.agent/skill-library/stack/ai/langchain/SKILL.md +530 -0
  34. package/template/.agent/skill-library/stack/ai/ollama/SKILL.md +321 -0
  35. package/template/.agent/skill-library/stack/ai/openai-sdk/SKILL.md +549 -0
  36. package/template/.agent/skill-library/stack/analytics/google-analytics/SKILL.md +153 -0
  37. package/template/.agent/skill-library/stack/api/graphql/SKILL.md +1061 -0
  38. package/template/.agent/skill-library/stack/api/trpc/SKILL.md +576 -0
  39. package/template/.agent/skill-library/stack/auth/authjs/SKILL.md +569 -0
  40. package/template/.agent/skill-library/stack/auth/clerk/SKILL.md +590 -0
  41. package/template/.agent/skill-library/stack/auth/firebase-auth/SKILL.md +734 -0
  42. package/template/.agent/skill-library/stack/cms/payload-cms/SKILL.md +573 -0
  43. package/template/.agent/skill-library/stack/cms/shopify/SKILL.md +1193 -0
  44. package/template/.agent/skill-library/stack/cms/wordpress/SKILL.md +1104 -0
  45. package/template/.agent/skill-library/stack/css/sass-scss/SKILL.md +1121 -0
  46. package/template/.agent/skill-library/stack/css/tailwind-css-patterns/SKILL.md +863 -0
  47. package/template/.agent/skill-library/stack/css/tailwind-design-system/SKILL.md +490 -0
  48. package/template/.agent/skill-library/stack/css/vanilla-css/SKILL.md +1078 -0
  49. package/template/.agent/skill-library/stack/databases/clickhouse/SKILL.md +311 -0
  50. package/template/.agent/skill-library/stack/databases/influxdb/SKILL.md +280 -0
  51. package/template/.agent/skill-library/stack/databases/lancedb/SKILL.md +415 -0
  52. package/template/.agent/skill-library/stack/databases/mongodb/SKILL.md +1169 -0
  53. package/template/.agent/skill-library/stack/databases/neo4j/SKILL.md +839 -0
  54. package/template/.agent/skill-library/stack/databases/pgvector/SKILL.md +241 -0
  55. package/template/.agent/skill-library/stack/databases/pinecone/SKILL.md +212 -0
  56. package/template/.agent/skill-library/stack/databases/postgresql/SKILL.md +658 -0
  57. package/template/.agent/skill-library/stack/databases/qdrant/SKILL.md +312 -0
  58. package/template/.agent/skill-library/stack/databases/redis/SKILL.md +1079 -0
  59. package/template/.agent/skill-library/stack/databases/spacetimedb/SKILL.md +532 -0
  60. package/template/.agent/skill-library/stack/databases/sqlite/SKILL.md +1132 -0
  61. package/template/.agent/skill-library/stack/databases/supabase/SKILL.md +640 -0
  62. package/template/.agent/skill-library/stack/databases/surrealdb-expert/SKILL.md +945 -0
  63. package/template/.agent/skill-library/stack/databases/timescaledb/SKILL.md +745 -0
  64. package/template/.agent/skill-library/stack/databases/weaviate/SKILL.md +218 -0
  65. package/template/.agent/skill-library/stack/devops/github-actions/SKILL.md +554 -0
  66. package/template/.agent/skill-library/stack/devops/kubernetes/SKILL.md +950 -0
  67. package/template/.agent/skill-library/stack/devops/nginx/SKILL.md +841 -0
  68. package/template/.agent/skill-library/stack/devops/terraform/SKILL.md +860 -0
  69. package/template/.agent/skill-library/stack/email/resend/SKILL.md +391 -0
  70. package/template/.agent/skill-library/stack/engines/godot/SKILL.md +488 -0
  71. package/template/.agent/skill-library/stack/extensions/chrome-extension/SKILL.md +375 -0
  72. package/template/.agent/skill-library/stack/extensions/vscode-extension/SKILL.md +453 -0
  73. package/template/.agent/skill-library/stack/frameworks/astro-framework/SKILL.md +162 -0
  74. package/template/.agent/skill-library/stack/frameworks/electron/SKILL.md +1286 -0
  75. package/template/.agent/skill-library/stack/frameworks/fastapi/SKILL.md +650 -0
  76. package/template/.agent/skill-library/stack/frameworks/hono/SKILL.md +90 -0
  77. package/template/.agent/skill-library/stack/frameworks/nestjs/SKILL.md +878 -0
  78. package/template/.agent/skill-library/stack/frameworks/nextjs/SKILL.md +635 -0
  79. package/template/.agent/skill-library/stack/frameworks/nuxt/SKILL.md +564 -0
  80. package/template/.agent/skill-library/stack/frameworks/sveltekit/SKILL.md +614 -0
  81. package/template/.agent/skill-library/stack/frameworks/tauri/SKILL.md +920 -0
  82. package/template/.agent/skill-library/stack/gamedev/godot/SKILL.md +1032 -0
  83. package/template/.agent/skill-library/stack/gamedev/unity/SKILL.md +1175 -0
  84. package/template/.agent/skill-library/stack/hosting/aws/SKILL.md +467 -0
  85. package/template/.agent/skill-library/stack/hosting/cloudflare/SKILL.md +201 -0
  86. package/template/.agent/skill-library/stack/hosting/docker-expert/SKILL.md +409 -0
  87. package/template/.agent/skill-library/stack/hosting/vercel/SKILL.md +484 -0
  88. package/template/.agent/skill-library/stack/languages/bash-scripting/SKILL.md +773 -0
  89. package/template/.agent/skill-library/stack/languages/c-cpp/SKILL.md +712 -0
  90. package/template/.agent/skill-library/stack/languages/gdscript/SKILL.md +789 -0
  91. package/template/.agent/skill-library/stack/languages/go/SKILL.md +664 -0
  92. package/template/.agent/skill-library/stack/languages/java/SKILL.md +778 -0
  93. package/template/.agent/skill-library/stack/languages/kotlin/SKILL.md +665 -0
  94. package/template/.agent/skill-library/stack/languages/python/SKILL.md +678 -0
  95. package/template/.agent/skill-library/stack/languages/rust/SKILL.md +673 -0
  96. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/SKILL.md +141 -0
  97. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/advanced-generics.md +90 -0
  98. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/branded-types.md +57 -0
  99. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/builder-pattern.md +71 -0
  100. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/common-pitfalls.md +135 -0
  101. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/conditional-types.md +27 -0
  102. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/decorators.md +98 -0
  103. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/discriminated-unions.md +62 -0
  104. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/mapped-types.md +53 -0
  105. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/performance-best-practices.md +104 -0
  106. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/template-literal-types.md +49 -0
  107. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/testing-types.md +112 -0
  108. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-guards.md +70 -0
  109. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-inference.md +101 -0
  110. package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/utility-types.md +98 -0
  111. package/template/.agent/skill-library/stack/languages/vanilla-javascript/SKILL.md +803 -0
  112. package/template/.agent/skill-library/stack/messaging/kafka/SKILL.md +235 -0
  113. package/template/.agent/skill-library/stack/mobile/expo-react-native/SKILL.md +665 -0
  114. package/template/.agent/skill-library/stack/mobile/flutter/SKILL.md +316 -0
  115. package/template/.agent/skill-library/stack/mobile/react-native/SKILL.md +337 -0
  116. package/template/.agent/skill-library/stack/monitoring/posthog/SKILL.md +396 -0
  117. package/template/.agent/skill-library/stack/monitoring/sentry/SKILL.md +509 -0
  118. package/template/.agent/skill-library/stack/observability/datadog/SKILL.md +179 -0
  119. package/template/.agent/skill-library/stack/observability/distributed-tracing/SKILL.md +140 -0
  120. package/template/.agent/skill-library/stack/observability/logging-best-practices/SKILL.md +168 -0
  121. package/template/.agent/skill-library/stack/observability/opentelemetry/SKILL.md +164 -0
  122. package/template/.agent/skill-library/stack/observability/prometheus-grafana/SKILL.md +246 -0
  123. package/template/.agent/skill-library/stack/observability/python-observability/SKILL.md +158 -0
  124. package/template/.agent/skill-library/stack/orm/drizzle-orm/SKILL.md +613 -0
  125. package/template/.agent/skill-library/stack/orm/prisma/SKILL.md +744 -0
  126. package/template/.agent/skill-library/stack/payments/lemonsqueezy/SKILL.md +393 -0
  127. package/template/.agent/skill-library/stack/payments/stripe-integration/SKILL.md +457 -0
  128. package/template/.agent/skill-library/stack/queue/bullmq/SKILL.md +385 -0
  129. package/template/.agent/skill-library/stack/queue/inngest/SKILL.md +438 -0
  130. package/template/.agent/skill-library/stack/realtime/socketio/SKILL.md +595 -0
  131. package/template/.agent/skill-library/stack/search/elasticsearch/SKILL.md +248 -0
  132. package/template/.agent/skill-library/stack/search/meilisearch/SKILL.md +385 -0
  133. package/template/.agent/skill-library/stack/security/crypto-patterns/SKILL.md +437 -0
  134. package/template/.agent/skill-library/stack/security/csp-cors-headers/SKILL.md +588 -0
  135. package/template/.agent/skill-library/stack/security/dependency-auditing/SKILL.md +560 -0
  136. package/template/.agent/skill-library/stack/security/input-sanitization/SKILL.md +430 -0
  137. package/template/.agent/skill-library/stack/security/owasp-web-security/SKILL.md +421 -0
  138. package/template/.agent/skill-library/stack/state/tanstack-query/SKILL.md +637 -0
  139. package/template/.agent/skill-library/stack/state/zustand/SKILL.md +483 -0
  140. package/template/.agent/skill-library/stack/storage/aws-s3/SKILL.md +415 -0
  141. package/template/.agent/skill-library/stack/testing/playwright/SKILL.md +641 -0
  142. package/template/.agent/skill-library/stack/testing/storybook/SKILL.md +923 -0
  143. package/template/.agent/skill-library/stack/testing/testing-library/SKILL.md +872 -0
  144. package/template/.agent/skill-library/stack/testing/vitest/SKILL.md +714 -0
  145. package/template/.agent/skill-library/stack/ui/react-best-practices/SKILL.md +877 -0
  146. package/template/.agent/skill-library/stack/ui/react-composition-patterns/SKILL.md +1107 -0
  147. package/template/.agent/skill-library/stack/ui/react-flow/SKILL.md +425 -0
  148. package/template/.agent/skill-library/stack/ui/shadcn-ui/SKILL.md +703 -0
  149. package/template/.agent/skill-library/surface/api/api-caching/SKILL.md +458 -0
  150. package/template/.agent/skill-library/surface/api/api-documentation-openapi/SKILL.md +697 -0
  151. package/template/.agent/skill-library/surface/api/api-error-handling/SKILL.md +478 -0
  152. package/template/.agent/skill-library/surface/api/api-security-checklist/SKILL.md +147 -0
  153. package/template/.agent/skill-library/surface/api/api-versioning/SKILL.md +420 -0
  154. package/template/.agent/skill-library/surface/api/email-best-practices/SKILL.md +59 -0
  155. package/template/.agent/skill-library/surface/api/rate-limiting-abuse-protection/SKILL.md +147 -0
  156. package/template/.agent/skill-library/surface/api/rest-api-design/SKILL.md +478 -0
  157. package/template/.agent/skill-library/surface/api/webhook-design/SKILL.md +752 -0
  158. package/template/.agent/skill-library/surface/cli/cli-configuration-management/SKILL.md +445 -0
  159. package/template/.agent/skill-library/surface/cli/cli-error-diagnostics/SKILL.md +515 -0
  160. package/template/.agent/skill-library/surface/cli/cli-shell-integration/SKILL.md +479 -0
  161. package/template/.agent/skill-library/surface/cli/cli-ux-design/SKILL.md +477 -0
  162. package/template/.agent/skill-library/surface/desktop/desktop-app-distribution/SKILL.md +416 -0
  163. package/template/.agent/skill-library/surface/desktop/desktop-security-sandboxing/SKILL.md +407 -0
  164. package/template/.agent/skill-library/surface/desktop/desktop-ux-conventions/SKILL.md +361 -0
  165. package/template/.agent/skill-library/surface/desktop/native-os-integration/SKILL.md +563 -0
  166. package/template/.agent/skill-library/surface/extension/browser-extension-patterns/SKILL.md +482 -0
  167. package/template/.agent/skill-library/surface/extension/plugin-architecture-design/SKILL.md +632 -0
  168. package/template/.agent/skill-library/surface/extension/vscode-extension-development/SKILL.md +728 -0
  169. package/template/.agent/skill-library/surface/mobile/app-store-submission/SKILL.md +304 -0
  170. package/template/.agent/skill-library/surface/mobile/mobile-offline-sync/SKILL.md +443 -0
  171. package/template/.agent/skill-library/surface/mobile/mobile-responsive-patterns/SKILL.md +432 -0
  172. package/template/.agent/skill-library/surface/mobile/push-notifications/SKILL.md +495 -0
  173. package/template/.agent/skill-library/surface/web/accessibility-compliance/SKILL.md +827 -0
  174. package/template/.agent/skill-library/surface/web/ai-seo/SKILL.md +398 -0
  175. package/template/.agent/skill-library/surface/web/ai-seo/references/content-patterns.md +285 -0
  176. package/template/.agent/skill-library/surface/web/ai-seo/references/platform-ranking-factors.md +152 -0
  177. package/template/.agent/skill-library/surface/web/analytics-tracking/SKILL.md +309 -0
  178. package/template/.agent/skill-library/surface/web/analytics-tracking/references/event-library.md +260 -0
  179. package/template/.agent/skill-library/surface/web/analytics-tracking/references/ga4-implementation.md +300 -0
  180. package/template/.agent/skill-library/surface/web/analytics-tracking/references/gtm-implementation.md +390 -0
  181. package/template/.agent/skill-library/surface/web/authentication-ui-flows/SKILL.md +530 -0
  182. package/template/.agent/skill-library/surface/web/dark-mode-theming/SKILL.md +516 -0
  183. package/template/.agent/skill-library/surface/web/design-reference-data/SKILL.md +105 -0
  184. package/template/.agent/skill-library/surface/web/design-reference-data/data/charts.csv +26 -0
  185. package/template/.agent/skill-library/surface/web/design-reference-data/data/colors.csv +97 -0
  186. package/template/.agent/skill-library/surface/web/design-reference-data/data/landing.csv +31 -0
  187. package/template/.agent/skill-library/surface/web/design-reference-data/data/styles.csv +59 -0
  188. package/template/.agent/skill-library/surface/web/design-reference-data/data/typography.csv +58 -0
  189. package/template/.agent/skill-library/surface/web/design-reference-data/data/ux-guidelines.csv +100 -0
  190. package/template/.agent/skill-library/surface/web/design-reference-data/scripts/core.py +258 -0
  191. package/template/.agent/skill-library/surface/web/design-reference-data/scripts/design_system.py +1067 -0
  192. package/template/.agent/skill-library/surface/web/design-reference-data/scripts/search.py +106 -0
  193. package/template/.agent/skill-library/surface/web/form-handling-validation/SKILL.md +675 -0
  194. package/template/.agent/skill-library/surface/web/frontend-design/SKILL.md +1393 -0
  195. package/template/.agent/skill-library/surface/web/frontend-design/templates/cppn-hero.tsx +299 -0
  196. package/template/.agent/skill-library/surface/web/frontend-design/templates/wave-hero.tsx +875 -0
  197. package/template/.agent/skill-library/surface/web/frontend-verification/SKILL.md +111 -0
  198. package/template/.agent/skill-library/surface/web/frontend-verification/scripts/ux_audit.py +739 -0
  199. package/template/.agent/skill-library/surface/web/i18n-localization/SKILL.md +154 -0
  200. package/template/.agent/skill-library/surface/web/offline-first-pwa/SKILL.md +657 -0
  201. package/template/.agent/skill-library/surface/web/page-cro/SKILL.md +182 -0
  202. package/template/.agent/skill-library/surface/web/page-cro/references/experiments.md +248 -0
  203. package/template/.agent/skill-library/surface/web/programmatic-seo/SKILL.md +238 -0
  204. package/template/.agent/skill-library/surface/web/programmatic-seo/references/playbooks.md +308 -0
  205. package/template/.agent/skill-library/surface/web/schema-markup/SKILL.md +179 -0
  206. package/template/.agent/skill-library/surface/web/schema-markup/references/schema-examples.md +398 -0
  207. package/template/.agent/skill-library/surface/web/seo-audit/SKILL.md +394 -0
  208. package/template/.agent/skill-library/surface/web/seo-audit/references/ai-writing-detection.md +200 -0
  209. package/template/.agent/skill-library/surface/web/web-performance-optimization/SKILL.md +646 -0
  210. package/template/.agent/skill-library/surface/web/web-scraping/SKILL.md +58 -0
  211. package/template/.agent/skills/accessibility/SKILL.md +522 -0
  212. package/template/.agent/skills/accessibility/references/WCAG.md +162 -0
  213. package/template/.agent/skills/adversarial-review/SKILL.md +90 -0
  214. package/template/.agent/skills/antigravity-workflows/SKILL.md +81 -0
  215. package/template/.agent/skills/antigravity-workflows/resources/implementation-playbook.md +36 -0
  216. package/template/.agent/skills/api-design-principles/SKILL.md +37 -0
  217. package/template/.agent/skills/api-design-principles/assets/api-design-checklist.md +155 -0
  218. package/template/.agent/skills/api-design-principles/assets/rest-api-template.py +182 -0
  219. package/template/.agent/skills/api-design-principles/references/graphql-schema-design.md +583 -0
  220. package/template/.agent/skills/api-design-principles/references/rest-best-practices.md +408 -0
  221. package/template/.agent/skills/api-design-principles/resources/implementation-playbook.md +513 -0
  222. package/template/.agent/skills/api-versioning/SKILL.md +420 -0
  223. package/template/.agent/skills/architecture-mapping/SKILL.md +219 -0
  224. package/template/.agent/skills/bootstrap-agents/SKILL.md +259 -0
  225. package/template/.agent/skills/brainstorming/SKILL.md +236 -0
  226. package/template/.agent/skills/brand-guidelines/SKILL.md +44 -0
  227. package/template/.agent/skills/clean-code/SKILL.md +94 -0
  228. package/template/.agent/skills/code-review-pro/SKILL.md +152 -0
  229. package/template/.agent/skills/concise-planning/SKILL.md +68 -0
  230. package/template/.agent/skills/cross-layer-consistency/SKILL.md +117 -0
  231. package/template/.agent/skills/database-schema-design/SKILL.md +429 -0
  232. package/template/.agent/skills/deployment-procedures/SKILL.md +241 -0
  233. package/template/.agent/skills/design-anti-cliche/SKILL.md +159 -0
  234. package/template/.agent/skills/design-direction/SKILL.md +45 -0
  235. package/template/.agent/skills/error-handling-patterns/SKILL.md +721 -0
  236. package/template/.agent/skills/find-skills/SKILL.md +145 -0
  237. package/template/.agent/skills/git-advanced/SKILL.md +972 -0
  238. package/template/.agent/skills/git-workflow/SKILL.md +420 -0
  239. package/template/.agent/skills/idea-extraction/SKILL.md +271 -0
  240. package/template/.agent/skills/logging-best-practices/SKILL.md +851 -0
  241. package/template/.agent/skills/migration-management/SKILL.md +384 -0
  242. package/template/.agent/skills/minimalist-surgical-development/SKILL.md +69 -0
  243. package/template/.agent/skills/parallel-agents/SKILL.md +165 -0
  244. package/template/.agent/skills/parallel-debugging/SKILL.md +135 -0
  245. package/template/.agent/skills/parallel-feature-development/SKILL.md +166 -0
  246. package/template/.agent/skills/performance-budgeting/SKILL.md +144 -0
  247. package/template/.agent/skills/pipeline-rubrics/SKILL.md +51 -0
  248. package/template/.agent/skills/pipeline-rubrics/references/architecture-rubric.md +19 -0
  249. package/template/.agent/skills/pipeline-rubrics/references/be-rubric.md +21 -0
  250. package/template/.agent/skills/pipeline-rubrics/references/fe-rubric.md +20 -0
  251. package/template/.agent/skills/pipeline-rubrics/references/ia-rubric.md +19 -0
  252. package/template/.agent/skills/pipeline-rubrics/references/scoring.md +28 -0
  253. package/template/.agent/skills/pipeline-rubrics/references/vision-rubric.md +11 -0
  254. package/template/.agent/skills/prd-templates/SKILL.md +88 -0
  255. package/template/.agent/skills/prd-templates/references/architecture-design-template.md +88 -0
  256. package/template/.agent/skills/prd-templates/references/be-spec-template.md +101 -0
  257. package/template/.agent/skills/prd-templates/references/data-placement-template.md +74 -0
  258. package/template/.agent/skills/prd-templates/references/decomposition-templates.md +211 -0
  259. package/template/.agent/skills/prd-templates/references/design-system-decisions.md +198 -0
  260. package/template/.agent/skills/prd-templates/references/engineering-standards-template.md +124 -0
  261. package/template/.agent/skills/prd-templates/references/fe-classification-procedures.md +47 -0
  262. package/template/.agent/skills/prd-templates/references/fe-spec-template.md +84 -0
  263. package/template/.agent/skills/prd-templates/references/infrastructure-report-template.md +71 -0
  264. package/template/.agent/skills/prd-templates/references/operational-templates.md +116 -0
  265. package/template/.agent/skills/prd-templates/references/placeholder-guard-template.md +21 -0
  266. package/template/.agent/skills/prd-templates/references/surface-model.md +61 -0
  267. package/template/.agent/skills/prd-templates/references/vision-template.md +66 -0
  268. package/template/.agent/skills/prompt-engineer/README.md +659 -0
  269. package/template/.agent/skills/prompt-engineer/SKILL.md +249 -0
  270. package/template/.agent/skills/regex-patterns/SKILL.md +751 -0
  271. package/template/.agent/skills/resolve-ambiguity/SKILL.md +278 -0
  272. package/template/.agent/skills/rest-api-design/SKILL.md +478 -0
  273. package/template/.agent/skills/security-scanning-security-hardening/SKILL.md +231 -0
  274. package/template/.agent/skills/session-continuity/SKILL.md +730 -0
  275. package/template/.agent/skills/session-continuity/protocols/01-session-resumption.md +38 -0
  276. package/template/.agent/skills/session-continuity/protocols/02-progress-generation.md +85 -0
  277. package/template/.agent/skills/session-continuity/protocols/03-progress-update.md +70 -0
  278. package/template/.agent/skills/session-continuity/protocols/04-pattern-extraction.md +60 -0
  279. package/template/.agent/skills/session-continuity/protocols/05-session-close.md +37 -0
  280. package/template/.agent/skills/session-continuity/protocols/06-decision-analysis.md +84 -0
  281. package/template/.agent/skills/session-continuity/protocols/07-spec-pipeline-generation.md +48 -0
  282. package/template/.agent/skills/session-continuity/protocols/08-spec-pipeline-update.md +43 -0
  283. package/template/.agent/skills/session-continuity/protocols/09-parallel-claim.md +122 -0
  284. package/template/.agent/skills/session-continuity/protocols/10-placeholder-verification-gate.md +104 -0
  285. package/template/.agent/skills/session-continuity/protocols/ambiguity-gates.md +48 -0
  286. package/template/.agent/skills/skill-creator/LICENSE.txt +202 -0
  287. package/template/.agent/skills/skill-creator/README.md +270 -0
  288. package/template/.agent/skills/skill-creator/SKILL.md +590 -0
  289. package/template/.agent/skills/skill-creator/references/output-patterns.md +82 -0
  290. package/template/.agent/skills/skill-creator/references/workflows.md +28 -0
  291. package/template/.agent/skills/skill-creator/scripts/init_skill.py +303 -0
  292. package/template/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
  293. package/template/.agent/skills/skill-creator/scripts/quick_validate.py +95 -0
  294. package/template/.agent/skills/spec-writing/SKILL.md +110 -0
  295. package/template/.agent/skills/systematic-debugging/CREATION-LOG.md +119 -0
  296. package/template/.agent/skills/systematic-debugging/SKILL.md +297 -0
  297. package/template/.agent/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  298. package/template/.agent/skills/systematic-debugging/condition-based-waiting.md +115 -0
  299. package/template/.agent/skills/systematic-debugging/defense-in-depth.md +122 -0
  300. package/template/.agent/skills/systematic-debugging/find-polluter.sh +63 -0
  301. package/template/.agent/skills/systematic-debugging/root-cause-tracing.md +169 -0
  302. package/template/.agent/skills/systematic-debugging/test-academic.md +14 -0
  303. package/template/.agent/skills/systematic-debugging/test-pressure-1.md +58 -0
  304. package/template/.agent/skills/systematic-debugging/test-pressure-2.md +68 -0
  305. package/template/.agent/skills/systematic-debugging/test-pressure-3.md +69 -0
  306. package/template/.agent/skills/tdd-workflow/SKILL.md +409 -0
  307. package/template/.agent/skills/tech-stack-catalog/SKILL.md +49 -0
  308. package/template/.agent/skills/tech-stack-catalog/references/constraint-questions.md +21 -0
  309. package/template/.agent/skills/tech-stack-catalog/references/dev-tooling-decisions.md +37 -0
  310. package/template/.agent/skills/tech-stack-catalog/references/surface-decision-tables.md +69 -0
  311. package/template/.agent/skills/technical-writer/SKILL.md +242 -0
  312. package/template/.agent/skills/testing-strategist/SKILL.md +932 -0
  313. package/template/.agent/skills/verification-before-completion/SKILL.md +145 -0
  314. package/template/.agent/skills/workflow-automation/SKILL.md +73 -0
  315. package/template/.agent/workflows/audit-ambiguity-execute.md +165 -0
  316. package/template/.agent/workflows/audit-ambiguity-rubrics.md +83 -0
  317. package/template/.agent/workflows/audit-ambiguity.md +64 -0
  318. package/template/.agent/workflows/bootstrap-agents-fill.md +201 -0
  319. package/template/.agent/workflows/bootstrap-agents-provision.md +197 -0
  320. package/template/.agent/workflows/bootstrap-agents.md +66 -0
  321. package/template/.agent/workflows/create-prd-architecture.md +119 -0
  322. package/template/.agent/workflows/create-prd-compile.md +138 -0
  323. package/template/.agent/workflows/create-prd-design-system.md +135 -0
  324. package/template/.agent/workflows/create-prd-security.md +113 -0
  325. package/template/.agent/workflows/create-prd-stack.md +91 -0
  326. package/template/.agent/workflows/create-prd.md +168 -0
  327. package/template/.agent/workflows/decompose-architecture-structure.md +82 -0
  328. package/template/.agent/workflows/decompose-architecture-validate.md +119 -0
  329. package/template/.agent/workflows/decompose-architecture.md +111 -0
  330. package/template/.agent/workflows/evolve-contract.md +98 -0
  331. package/template/.agent/workflows/evolve-feature-cascade.md +140 -0
  332. package/template/.agent/workflows/evolve-feature-classify.md +116 -0
  333. package/template/.agent/workflows/evolve-feature.md +56 -0
  334. package/template/.agent/workflows/ideate-discover.md +144 -0
  335. package/template/.agent/workflows/ideate-extract.md +129 -0
  336. package/template/.agent/workflows/ideate-validate.md +117 -0
  337. package/template/.agent/workflows/ideate.md +113 -0
  338. package/template/.agent/workflows/implement-slice-setup.md +113 -0
  339. package/template/.agent/workflows/implement-slice-tdd.md +198 -0
  340. package/template/.agent/workflows/implement-slice.md +50 -0
  341. package/template/.agent/workflows/plan-phase.md +202 -0
  342. package/template/.agent/workflows/propagate-decision-apply.md +135 -0
  343. package/template/.agent/workflows/propagate-decision-scan.md +147 -0
  344. package/template/.agent/workflows/propagate-decision.md +56 -0
  345. package/template/.agent/workflows/remediate-pipeline-assess.md +138 -0
  346. package/template/.agent/workflows/remediate-pipeline-execute.md +135 -0
  347. package/template/.agent/workflows/remediate-pipeline.md +55 -0
  348. package/template/.agent/workflows/resolve-ambiguity.md +82 -0
  349. package/template/.agent/workflows/sync-kit.md +209 -0
  350. package/template/.agent/workflows/update-architecture-map.md +74 -0
  351. package/template/.agent/workflows/validate-phase.md +219 -0
  352. package/template/.agent/workflows/verify-infrastructure.md +207 -0
  353. package/template/.agent/workflows/write-architecture-spec-deepen.md +139 -0
  354. package/template/.agent/workflows/write-architecture-spec-design.md +202 -0
  355. package/template/.agent/workflows/write-architecture-spec.md +63 -0
  356. package/template/.agent/workflows/write-be-spec-classify.md +165 -0
  357. package/template/.agent/workflows/write-be-spec-write.md +98 -0
  358. package/template/.agent/workflows/write-be-spec.md +76 -0
  359. package/template/.agent/workflows/write-fe-spec-classify.md +170 -0
  360. package/template/.agent/workflows/write-fe-spec-write.md +94 -0
  361. package/template/.agent/workflows/write-fe-spec.md +71 -0
  362. package/template/AGENTS.md +176 -0
  363. package/template/GEMINI.md +177 -0
  364. package/template/docs/README.md +187 -0
  365. package/template/docs/audits/.gitkeep +0 -0
  366. package/template/docs/audits/README.md +10 -0
  367. package/template/docs/plans/.gitkeep +0 -0
  368. package/template/docs/plans/README.md +21 -0
  369. package/template/docs/plans/be/.gitkeep +0 -0
  370. package/template/docs/plans/be/README.md +11 -0
  371. package/template/docs/plans/fe/.gitkeep +0 -0
  372. package/template/docs/plans/fe/README.md +11 -0
  373. package/template/docs/plans/ia/.gitkeep +0 -0
  374. package/template/docs/plans/ia/README.md +17 -0
  375. package/template/docs/plans/ia/deep-dives/.gitkeep +0 -0
  376. package/template/docs/plans/ia/deep-dives/README.md +5 -0
  377. package/template/docs/plans/phases/.gitkeep +0 -0
  378. package/template/docs/plans/phases/README.md +11 -0
@@ -0,0 +1,1286 @@
1
+ ---
2
+ name: electron
3
+ description: Provides comprehensive Electron desktop application development patterns including main/renderer process architecture, IPC communication (ipcMain, ipcRenderer, contextBridge), preload scripts, security best practices, BrowserWindow management, auto-updater, native menus, system tray, file dialogs, protocol handlers, Electron Forge and electron-builder, performance optimization, memory management, crash reporting, and testing strategies. Use when building cross-platform desktop apps with Electron.
4
+ version: 1.0.0
5
+ allowed-tools: Read, Write, Edit, Glob, Grep, Bash
6
+ ---
7
+
8
+ # Electron Desktop Application Development Patterns
9
+
10
+ ## Overview
11
+
12
+ Expert guide for building cross-platform desktop applications with Electron. Covers the multi-process architecture, secure IPC communication, window management, native OS integration, packaging, performance optimization, and testing. Targets Electron 30+ with modern security defaults.
13
+
14
+ ## When to Use
15
+
16
+ - Building cross-platform desktop applications (Windows, macOS, Linux)
17
+ - When you need full Node.js access for backend operations
18
+ - When Chromium rendering consistency across platforms matters
19
+ - When the team has strong JavaScript/TypeScript expertise
20
+ - Building apps that embed web content or wrap existing web apps
21
+ - When rich ecosystem of npm packages is an advantage
22
+ - Building IDEs, communication apps, media tools, or developer utilities
23
+
24
+ ## Instructions
25
+
26
+ 1. **Enable all security defaults**: contextIsolation, sandbox, no nodeIntegration
27
+ 2. **Use preload scripts** for all main-to-renderer communication
28
+ 3. **Validate all IPC messages** on both sides
29
+ 4. **Use contextBridge** to expose a minimal, typed API
30
+ 5. **Never load remote content** without strict CSP and webview sandboxing
31
+ 6. **Manage window lifecycle** explicitly (remember bounds, handle close vs quit)
32
+ 7. **Profile memory** regularly -- Electron apps are prone to leaks
33
+
34
+ ## Examples
35
+
36
+ ### Project Structure
37
+
38
+ ```
39
+ my-electron-app/
40
+ src/
41
+ main/
42
+ index.ts # Main process entry
43
+ ipc-handlers.ts # IPC handler registration
44
+ menu.ts # Application menu
45
+ tray.ts # System tray
46
+ updater.ts # Auto-update logic
47
+ windows/
48
+ main-window.ts # Main window factory
49
+ settings-window.ts
50
+ preload/
51
+ index.ts # Preload script
52
+ api.ts # contextBridge API definition
53
+ renderer/
54
+ index.html # Entry HTML
55
+ App.tsx # React/Vue/Svelte app
56
+ lib/
57
+ electron-api.ts # Typed wrapper for exposed API
58
+ electron-builder.yml # Build configuration
59
+ forge.config.ts # Electron Forge config (alternative)
60
+ package.json
61
+ tsconfig.json
62
+ ```
63
+
64
+ ## Constraints and Warnings
65
+
66
+ - **Bundle size**: Electron ships Chromium + Node.js (~150MB minimum)
67
+ - **Memory usage**: Each window is a separate Chromium process (50-100MB+)
68
+ - **Startup time**: Cold start can be 2-5 seconds depending on app complexity
69
+ - **Security surface**: Node.js in main process has full system access
70
+ - **Update complexity**: Auto-updates require code signing on macOS and Windows
71
+ - **macOS notarization**: Required for distribution outside the App Store
72
+ - **Native modules**: Must be rebuilt for Electron's Node.js version
73
+ - **Chromium version**: Locked to Electron's bundled version, not the user's browser
74
+
75
+ ## Core Concepts
76
+
77
+ ### Process Architecture
78
+
79
+ Electron has two types of processes:
80
+
81
+ ```
82
+ Main Process (Node.js)
83
+ - Runs in Node.js environment
84
+ - Has full system access (file system, network, OS APIs)
85
+ - Creates and manages BrowserWindows
86
+ - Handles IPC from renderer processes
87
+ - Manages app lifecycle, menus, tray, dialogs
88
+
89
+ Renderer Process (Chromium)
90
+ - Runs in a Chromium browser context
91
+ - One process per BrowserWindow
92
+ - No direct Node.js access (when properly configured)
93
+ - Communicates with main process via IPC through preload
94
+
95
+ Preload Script (Bridge)
96
+ - Runs before renderer page loads
97
+ - Has access to a limited set of Node.js/Electron APIs
98
+ - Uses contextBridge to expose safe API to renderer
99
+ - The ONLY bridge between renderer and main process
100
+ ```
101
+
102
+ ### Main Process Entry Point
103
+
104
+ ```typescript
105
+ // src/main/index.ts
106
+ import { app, BrowserWindow } from "electron";
107
+ import path from "node:path";
108
+ import { registerIpcHandlers } from "./ipc-handlers";
109
+ import { createMainWindow } from "./windows/main-window";
110
+ import { createAppMenu } from "./menu";
111
+ import { setupTray } from "./tray";
112
+ import { setupAutoUpdater } from "./updater";
113
+
114
+ // Handle single instance lock
115
+ const gotLock = app.requestSingleInstanceLock();
116
+ if (!gotLock) {
117
+ app.quit();
118
+ }
119
+
120
+ // Store reference to prevent garbage collection
121
+ let mainWindow: BrowserWindow | null = null;
122
+
123
+ app.whenReady().then(async () => {
124
+ // Register IPC handlers BEFORE creating windows
125
+ registerIpcHandlers();
126
+
127
+ // Create the main window
128
+ mainWindow = createMainWindow();
129
+
130
+ // Set up application menu
131
+ createAppMenu();
132
+
133
+ // Set up system tray
134
+ setupTray(mainWindow);
135
+
136
+ // Check for updates (production only)
137
+ if (!app.isPackaged) {
138
+ // Skip in development
139
+ } else {
140
+ setupAutoUpdater(mainWindow);
141
+ }
142
+
143
+ // macOS: re-create window when dock icon clicked
144
+ app.on("activate", () => {
145
+ if (BrowserWindow.getAllWindows().length === 0) {
146
+ mainWindow = createMainWindow();
147
+ }
148
+ });
149
+ });
150
+
151
+ // Handle second instance (single instance lock)
152
+ app.on("second-instance", (_event, _commandLine, _workingDirectory) => {
153
+ if (mainWindow) {
154
+ if (mainWindow.isMinimized()) mainWindow.restore();
155
+ mainWindow.focus();
156
+ }
157
+ });
158
+
159
+ // Quit when all windows closed (except macOS)
160
+ app.on("window-all-closed", () => {
161
+ if (process.platform !== "darwin") {
162
+ app.quit();
163
+ }
164
+ });
165
+
166
+ // Security: prevent new webview/window creation
167
+ app.on("web-contents-created", (_event, contents) => {
168
+ // Prevent navigation to external URLs
169
+ contents.on("will-navigate", (event, url) => {
170
+ const parsedUrl = new URL(url);
171
+ if (parsedUrl.origin !== "http://localhost:5173") {
172
+ event.preventDefault();
173
+ }
174
+ });
175
+
176
+ // Prevent opening new windows
177
+ contents.setWindowOpenHandler(({ url }) => {
178
+ // Open external links in the default browser
179
+ if (url.startsWith("https://")) {
180
+ import("electron").then(({ shell }) => shell.openExternal(url));
181
+ }
182
+ return { action: "deny" };
183
+ });
184
+ });
185
+ ```
186
+
187
+ ### BrowserWindow Management
188
+
189
+ ```typescript
190
+ // src/main/windows/main-window.ts
191
+ import { BrowserWindow, screen } from "electron";
192
+ import path from "node:path";
193
+ import { getWindowState, saveWindowState } from "../window-state";
194
+
195
+ export function createMainWindow(): BrowserWindow {
196
+ // Restore previous window position/size
197
+ const savedState = getWindowState("main", {
198
+ width: 1200,
199
+ height: 800,
200
+ });
201
+
202
+ const win = new BrowserWindow({
203
+ width: savedState.width,
204
+ height: savedState.height,
205
+ x: savedState.x,
206
+ y: savedState.y,
207
+ minWidth: 800,
208
+ minHeight: 600,
209
+ title: "My Application",
210
+ show: false, // Show after ready-to-show to prevent flash
211
+ backgroundColor: "#1a1a2e",
212
+ webPreferences: {
213
+ preload: path.join(__dirname, "../preload/index.js"),
214
+ // Security defaults (Electron 30+)
215
+ contextIsolation: true, // MUST be true
216
+ nodeIntegration: false, // MUST be false
217
+ sandbox: true, // MUST be true
218
+ webSecurity: true, // MUST be true
219
+ allowRunningInsecureContent: false,
220
+ },
221
+ });
222
+
223
+ // Show when ready (prevents white flash)
224
+ win.once("ready-to-show", () => {
225
+ win.show();
226
+ });
227
+
228
+ // Save window state on close
229
+ win.on("close", () => {
230
+ saveWindowState("main", win.getBounds());
231
+ });
232
+
233
+ // Load the app
234
+ if (process.env.NODE_ENV === "development") {
235
+ win.loadURL("http://localhost:5173");
236
+ win.webContents.openDevTools();
237
+ } else {
238
+ win.loadFile(path.join(__dirname, "../renderer/index.html"));
239
+ }
240
+
241
+ return win;
242
+ }
243
+ ```
244
+
245
+ ### Window State Persistence
246
+
247
+ ```typescript
248
+ // src/main/window-state.ts
249
+ import { app } from "electron";
250
+ import fs from "node:fs";
251
+ import path from "node:path";
252
+
253
+ interface WindowState {
254
+ x?: number;
255
+ y?: number;
256
+ width: number;
257
+ height: number;
258
+ }
259
+
260
+ const stateFile = path.join(app.getPath("userData"), "window-state.json");
261
+
262
+ function loadStateFile(): Record<string, WindowState> {
263
+ try {
264
+ const data = fs.readFileSync(stateFile, "utf-8");
265
+ return JSON.parse(data);
266
+ } catch {
267
+ return {};
268
+ }
269
+ }
270
+
271
+ export function getWindowState(
272
+ key: string,
273
+ defaults: WindowState
274
+ ): WindowState {
275
+ const states = loadStateFile();
276
+ return states[key] || defaults;
277
+ }
278
+
279
+ export function saveWindowState(
280
+ key: string,
281
+ bounds: Electron.Rectangle
282
+ ): void {
283
+ const states = loadStateFile();
284
+ states[key] = {
285
+ x: bounds.x,
286
+ y: bounds.y,
287
+ width: bounds.width,
288
+ height: bounds.height,
289
+ };
290
+ fs.writeFileSync(stateFile, JSON.stringify(states, null, 2));
291
+ }
292
+ ```
293
+
294
+ ### Preload Script and contextBridge
295
+
296
+ ```typescript
297
+ // src/preload/index.ts
298
+ import { contextBridge, ipcRenderer } from "electron";
299
+
300
+ // Define the API exposed to the renderer
301
+ const electronAPI = {
302
+ // --- File Operations ---
303
+ readFile: (filePath: string): Promise<string> =>
304
+ ipcRenderer.invoke("file:read", filePath),
305
+
306
+ writeFile: (filePath: string, content: string): Promise<void> =>
307
+ ipcRenderer.invoke("file:write", filePath, content),
308
+
309
+ showOpenDialog: (
310
+ options: Electron.OpenDialogOptions
311
+ ): Promise<Electron.OpenDialogReturnValue> =>
312
+ ipcRenderer.invoke("dialog:open", options),
313
+
314
+ showSaveDialog: (
315
+ options: Electron.SaveDialogOptions
316
+ ): Promise<Electron.SaveDialogReturnValue> =>
317
+ ipcRenderer.invoke("dialog:save", options),
318
+
319
+ // --- App Info ---
320
+ getAppVersion: (): Promise<string> =>
321
+ ipcRenderer.invoke("app:version"),
322
+
323
+ getPlatform: (): NodeJS.Platform =>
324
+ process.platform as NodeJS.Platform,
325
+
326
+ // --- Window Controls ---
327
+ minimizeWindow: (): void =>
328
+ ipcRenderer.send("window:minimize"),
329
+
330
+ maximizeWindow: (): void =>
331
+ ipcRenderer.send("window:maximize"),
332
+
333
+ closeWindow: (): void =>
334
+ ipcRenderer.send("window:close"),
335
+
336
+ // --- Events (main -> renderer) ---
337
+ onUpdateAvailable: (callback: (version: string) => void) => {
338
+ const handler = (_event: Electron.IpcRendererEvent, version: string) =>
339
+ callback(version);
340
+ ipcRenderer.on("update:available", handler);
341
+ // Return cleanup function
342
+ return () => ipcRenderer.removeListener("update:available", handler);
343
+ },
344
+
345
+ onMenuAction: (callback: (action: string) => void) => {
346
+ const handler = (_event: Electron.IpcRendererEvent, action: string) =>
347
+ callback(action);
348
+ ipcRenderer.on("menu:action", handler);
349
+ return () => ipcRenderer.removeListener("menu:action", handler);
350
+ },
351
+ };
352
+
353
+ // Expose to renderer via window.electronAPI
354
+ contextBridge.exposeInMainWorld("electronAPI", electronAPI);
355
+
356
+ // Export type for use in renderer
357
+ export type ElectronAPI = typeof electronAPI;
358
+ ```
359
+
360
+ ### IPC Handlers (Main Process)
361
+
362
+ ```typescript
363
+ // src/main/ipc-handlers.ts
364
+ import { ipcMain, dialog, app, BrowserWindow } from "electron";
365
+ import fs from "node:fs/promises";
366
+ import path from "node:path";
367
+
368
+ export function registerIpcHandlers(): void {
369
+ // --- File Operations ---
370
+ ipcMain.handle(
371
+ "file:read",
372
+ async (_event, filePath: string): Promise<string> => {
373
+ // Validate path to prevent directory traversal
374
+ const resolved = path.resolve(filePath);
375
+ if (!isAllowedPath(resolved)) {
376
+ throw new Error("Access denied: path outside allowed directories");
377
+ }
378
+ return fs.readFile(resolved, "utf-8");
379
+ }
380
+ );
381
+
382
+ ipcMain.handle(
383
+ "file:write",
384
+ async (_event, filePath: string, content: string): Promise<void> => {
385
+ const resolved = path.resolve(filePath);
386
+ if (!isAllowedPath(resolved)) {
387
+ throw new Error("Access denied: path outside allowed directories");
388
+ }
389
+ await fs.writeFile(resolved, content, "utf-8");
390
+ }
391
+ );
392
+
393
+ // --- Dialogs ---
394
+ ipcMain.handle(
395
+ "dialog:open",
396
+ async (event, options: Electron.OpenDialogOptions) => {
397
+ const win = BrowserWindow.fromWebContents(event.sender);
398
+ if (!win) throw new Error("No window found");
399
+ return dialog.showOpenDialog(win, options);
400
+ }
401
+ );
402
+
403
+ ipcMain.handle(
404
+ "dialog:save",
405
+ async (event, options: Electron.SaveDialogOptions) => {
406
+ const win = BrowserWindow.fromWebContents(event.sender);
407
+ if (!win) throw new Error("No window found");
408
+ return dialog.showSaveDialog(win, options);
409
+ }
410
+ );
411
+
412
+ // --- App Info ---
413
+ ipcMain.handle("app:version", () => app.getVersion());
414
+
415
+ // --- Window Controls ---
416
+ ipcMain.on("window:minimize", (event) => {
417
+ BrowserWindow.fromWebContents(event.sender)?.minimize();
418
+ });
419
+
420
+ ipcMain.on("window:maximize", (event) => {
421
+ const win = BrowserWindow.fromWebContents(event.sender);
422
+ if (win) {
423
+ win.isMaximized() ? win.unmaximize() : win.maximize();
424
+ }
425
+ });
426
+
427
+ ipcMain.on("window:close", (event) => {
428
+ BrowserWindow.fromWebContents(event.sender)?.close();
429
+ });
430
+ }
431
+
432
+ // Security: validate file paths
433
+ function isAllowedPath(filePath: string): boolean {
434
+ const userDataPath = app.getPath("userData");
435
+ const documentsPath = app.getPath("documents");
436
+ const allowedRoots = [userDataPath, documentsPath];
437
+
438
+ return allowedRoots.some((root) => filePath.startsWith(root));
439
+ }
440
+ ```
441
+
442
+ ### Typed API in Renderer
443
+
444
+ ```typescript
445
+ // src/renderer/lib/electron-api.ts
446
+ // Import the type from preload (build-time only, not runtime)
447
+ import type { ElectronAPI } from "../../preload/index";
448
+
449
+ // Augment the Window interface
450
+ declare global {
451
+ interface Window {
452
+ electronAPI: ElectronAPI;
453
+ }
454
+ }
455
+
456
+ // Re-export for convenient access
457
+ export const electronAPI = window.electronAPI;
458
+ ```
459
+
460
+ ```tsx
461
+ // src/renderer/App.tsx
462
+ import { useState, useEffect } from "react";
463
+ import { electronAPI } from "./lib/electron-api";
464
+
465
+ function App() {
466
+ const [version, setVersion] = useState("");
467
+ const [fileContent, setFileContent] = useState("");
468
+
469
+ useEffect(() => {
470
+ electronAPI.getAppVersion().then(setVersion);
471
+
472
+ // Listen for menu actions
473
+ const cleanup = electronAPI.onMenuAction((action) => {
474
+ switch (action) {
475
+ case "open-file":
476
+ handleOpenFile();
477
+ break;
478
+ case "save-file":
479
+ handleSaveFile();
480
+ break;
481
+ }
482
+ });
483
+
484
+ return cleanup;
485
+ }, []);
486
+
487
+ async function handleOpenFile() {
488
+ const result = await electronAPI.showOpenDialog({
489
+ filters: [
490
+ { name: "Text Files", extensions: ["txt", "md"] },
491
+ { name: "All Files", extensions: ["*"] },
492
+ ],
493
+ properties: ["openFile"],
494
+ });
495
+
496
+ if (!result.canceled && result.filePaths[0]) {
497
+ const content = await electronAPI.readFile(result.filePaths[0]);
498
+ setFileContent(content);
499
+ }
500
+ }
501
+
502
+ async function handleSaveFile() {
503
+ const result = await electronAPI.showSaveDialog({
504
+ filters: [{ name: "Text Files", extensions: ["txt"] }],
505
+ });
506
+
507
+ if (!result.canceled && result.filePath) {
508
+ await electronAPI.writeFile(result.filePath, fileContent);
509
+ }
510
+ }
511
+
512
+ return (
513
+ <div>
514
+ <header>
515
+ <span>My App v{version}</span>
516
+ <div className="window-controls">
517
+ <button onClick={() => electronAPI.minimizeWindow()}>-</button>
518
+ <button onClick={() => electronAPI.maximizeWindow()}>[]</button>
519
+ <button onClick={() => electronAPI.closeWindow()}>X</button>
520
+ </div>
521
+ </header>
522
+ <main>
523
+ <textarea
524
+ value={fileContent}
525
+ onChange={(e) => setFileContent(e.target.value)}
526
+ />
527
+ </main>
528
+ </div>
529
+ );
530
+ }
531
+ ```
532
+
533
+ ### IPC Patterns: invoke vs send
534
+
535
+ ```typescript
536
+ // PATTERN 1: invoke/handle (request-response, returns a promise)
537
+ // Use for: requesting data, performing actions that return results
538
+
539
+ // Main process:
540
+ ipcMain.handle("db:query", async (_event, sql: string) => {
541
+ return database.query(sql);
542
+ });
543
+
544
+ // Renderer (via preload):
545
+ const result = await electronAPI.queryDatabase("SELECT * FROM users");
546
+
547
+ // PATTERN 2: send/on (fire-and-forget, no response)
548
+ // Use for: notifications, window control, one-way commands
549
+
550
+ // Renderer sends:
551
+ ipcRenderer.send("window:minimize");
552
+
553
+ // Main process listens:
554
+ ipcMain.on("window:minimize", (event) => {
555
+ BrowserWindow.fromWebContents(event.sender)?.minimize();
556
+ });
557
+
558
+ // PATTERN 3: Main-to-renderer (push from main process)
559
+ // Use for: update notifications, menu clicks, system events
560
+
561
+ // Main process pushes:
562
+ mainWindow.webContents.send("update:available", "2.0.0");
563
+
564
+ // Renderer listens (via preload):
565
+ ipcRenderer.on("update:available", (_event, version) => {
566
+ showUpdateBanner(version);
567
+ });
568
+ ```
569
+
570
+ ### Native Menus
571
+
572
+ ```typescript
573
+ // src/main/menu.ts
574
+ import { Menu, app, BrowserWindow, shell } from "electron";
575
+
576
+ export function createAppMenu(): void {
577
+ const isMac = process.platform === "darwin";
578
+
579
+ const template: Electron.MenuItemConstructorOptions[] = [
580
+ // macOS app menu
581
+ ...(isMac
582
+ ? [
583
+ {
584
+ label: app.name,
585
+ submenu: [
586
+ { role: "about" as const },
587
+ { type: "separator" as const },
588
+ { role: "services" as const },
589
+ { type: "separator" as const },
590
+ { role: "hide" as const },
591
+ { role: "hideOthers" as const },
592
+ { role: "unhide" as const },
593
+ { type: "separator" as const },
594
+ { role: "quit" as const },
595
+ ],
596
+ },
597
+ ]
598
+ : []),
599
+
600
+ // File menu
601
+ {
602
+ label: "File",
603
+ submenu: [
604
+ {
605
+ label: "Open File",
606
+ accelerator: "CmdOrCtrl+O",
607
+ click: (_item, window) => {
608
+ window?.webContents.send("menu:action", "open-file");
609
+ },
610
+ },
611
+ {
612
+ label: "Save",
613
+ accelerator: "CmdOrCtrl+S",
614
+ click: (_item, window) => {
615
+ window?.webContents.send("menu:action", "save-file");
616
+ },
617
+ },
618
+ { type: "separator" },
619
+ isMac ? { role: "close" } : { role: "quit" },
620
+ ],
621
+ },
622
+
623
+ // Edit menu
624
+ {
625
+ label: "Edit",
626
+ submenu: [
627
+ { role: "undo" },
628
+ { role: "redo" },
629
+ { type: "separator" },
630
+ { role: "cut" },
631
+ { role: "copy" },
632
+ { role: "paste" },
633
+ { role: "selectAll" },
634
+ ],
635
+ },
636
+
637
+ // View menu
638
+ {
639
+ label: "View",
640
+ submenu: [
641
+ { role: "reload" },
642
+ { role: "forceReload" },
643
+ { role: "toggleDevTools" },
644
+ { type: "separator" },
645
+ { role: "resetZoom" },
646
+ { role: "zoomIn" },
647
+ { role: "zoomOut" },
648
+ { type: "separator" },
649
+ { role: "togglefullscreen" },
650
+ ],
651
+ },
652
+
653
+ // Help menu
654
+ {
655
+ label: "Help",
656
+ submenu: [
657
+ {
658
+ label: "Documentation",
659
+ click: () => {
660
+ shell.openExternal("https://docs.myapp.com");
661
+ },
662
+ },
663
+ ],
664
+ },
665
+ ];
666
+
667
+ const menu = Menu.buildFromTemplate(template);
668
+ Menu.setApplicationMenu(menu);
669
+ }
670
+ ```
671
+
672
+ ### System Tray
673
+
674
+ ```typescript
675
+ // src/main/tray.ts
676
+ import { Tray, Menu, nativeImage, BrowserWindow, app } from "electron";
677
+ import path from "node:path";
678
+
679
+ let tray: Tray | null = null;
680
+
681
+ export function setupTray(mainWindow: BrowserWindow): void {
682
+ const iconPath = path.join(__dirname, "../../resources/tray-icon.png");
683
+ const icon = nativeImage.createFromPath(iconPath).resize({ width: 16, height: 16 });
684
+
685
+ tray = new Tray(icon);
686
+
687
+ const contextMenu = Menu.buildFromTemplate([
688
+ {
689
+ label: "Show App",
690
+ click: () => {
691
+ mainWindow.show();
692
+ mainWindow.focus();
693
+ },
694
+ },
695
+ {
696
+ label: "Minimize to Tray",
697
+ click: () => {
698
+ mainWindow.hide();
699
+ },
700
+ },
701
+ { type: "separator" },
702
+ {
703
+ label: "Quit",
704
+ click: () => {
705
+ app.quit();
706
+ },
707
+ },
708
+ ]);
709
+
710
+ tray.setToolTip("My Application");
711
+ tray.setContextMenu(contextMenu);
712
+
713
+ // Click tray icon to show window
714
+ tray.on("click", () => {
715
+ if (mainWindow.isVisible()) {
716
+ mainWindow.hide();
717
+ } else {
718
+ mainWindow.show();
719
+ mainWindow.focus();
720
+ }
721
+ });
722
+
723
+ // Minimize to tray instead of closing
724
+ mainWindow.on("close", (event) => {
725
+ if (!app.isQuitting) {
726
+ event.preventDefault();
727
+ mainWindow.hide();
728
+ }
729
+ });
730
+ }
731
+ ```
732
+
733
+ ### Auto-Updater
734
+
735
+ ```typescript
736
+ // src/main/updater.ts
737
+ import { autoUpdater } from "electron-updater";
738
+ import { BrowserWindow } from "electron";
739
+ import log from "electron-log";
740
+
741
+ export function setupAutoUpdater(mainWindow: BrowserWindow): void {
742
+ // Configure logging
743
+ autoUpdater.logger = log;
744
+
745
+ // Disable auto-download (let user decide)
746
+ autoUpdater.autoDownload = false;
747
+ autoUpdater.autoInstallOnAppQuit = true;
748
+
749
+ autoUpdater.on("checking-for-update", () => {
750
+ log.info("Checking for updates...");
751
+ });
752
+
753
+ autoUpdater.on("update-available", (info) => {
754
+ log.info("Update available:", info.version);
755
+ mainWindow.webContents.send("update:available", info.version);
756
+ });
757
+
758
+ autoUpdater.on("update-not-available", () => {
759
+ log.info("No updates available");
760
+ });
761
+
762
+ autoUpdater.on("download-progress", (progress) => {
763
+ mainWindow.webContents.send("update:progress", {
764
+ percent: progress.percent,
765
+ bytesPerSecond: progress.bytesPerSecond,
766
+ transferred: progress.transferred,
767
+ total: progress.total,
768
+ });
769
+ });
770
+
771
+ autoUpdater.on("update-downloaded", () => {
772
+ mainWindow.webContents.send("update:downloaded");
773
+ });
774
+
775
+ autoUpdater.on("error", (err) => {
776
+ log.error("Update error:", err);
777
+ });
778
+
779
+ // Check for updates every 4 hours
780
+ autoUpdater.checkForUpdates();
781
+ setInterval(
782
+ () => autoUpdater.checkForUpdates(),
783
+ 4 * 60 * 60 * 1000
784
+ );
785
+ }
786
+
787
+ // Called from IPC when user clicks "Install Update"
788
+ export function installUpdate(): void {
789
+ autoUpdater.quitAndInstall();
790
+ }
791
+
792
+ // Called from IPC when user clicks "Download Update"
793
+ export function downloadUpdate(): void {
794
+ autoUpdater.downloadUpdate();
795
+ }
796
+ ```
797
+
798
+ ### Protocol Handlers
799
+
800
+ ```typescript
801
+ // src/main/index.ts
802
+ import { app, protocol, net } from "electron";
803
+ import path from "node:path";
804
+ import fs from "node:fs";
805
+
806
+ // Register a custom protocol for loading app assets
807
+ app.whenReady().then(() => {
808
+ // Handle app:// protocol for loading local files securely
809
+ protocol.handle("app", (request) => {
810
+ const url = new URL(request.url);
811
+ const filePath = path.join(
812
+ __dirname,
813
+ "../renderer",
814
+ url.pathname
815
+ );
816
+
817
+ // Security: prevent path traversal
818
+ const resolvedPath = path.resolve(filePath);
819
+ const rendererDir = path.resolve(__dirname, "../renderer");
820
+
821
+ if (!resolvedPath.startsWith(rendererDir)) {
822
+ return new Response("Forbidden", { status: 403 });
823
+ }
824
+
825
+ return net.fetch(`file://${resolvedPath}`);
826
+ });
827
+ });
828
+
829
+ // Register as default protocol handler (deep linking)
830
+ if (process.defaultApp) {
831
+ if (process.argv.length >= 2) {
832
+ app.setAsDefaultProtocolClient("myapp", process.execPath, [
833
+ path.resolve(process.argv[1]),
834
+ ]);
835
+ }
836
+ } else {
837
+ app.setAsDefaultProtocolClient("myapp");
838
+ }
839
+
840
+ // Handle protocol URLs on macOS
841
+ app.on("open-url", (_event, url) => {
842
+ handleDeepLink(url);
843
+ });
844
+
845
+ function handleDeepLink(url: string): void {
846
+ const parsed = new URL(url);
847
+ // myapp://open?file=/path/to/file
848
+ if (parsed.hostname === "open") {
849
+ const filePath = parsed.searchParams.get("file");
850
+ if (filePath) {
851
+ // Send to renderer
852
+ BrowserWindow.getAllWindows()[0]?.webContents.send(
853
+ "deep-link:open-file",
854
+ filePath
855
+ );
856
+ }
857
+ }
858
+ }
859
+ ```
860
+
861
+ ## Security Best Practices
862
+
863
+ ### Security Checklist
864
+
865
+ ```typescript
866
+ // MANDATORY security settings for every BrowserWindow
867
+ const win = new BrowserWindow({
868
+ webPreferences: {
869
+ // These MUST be set correctly:
870
+ contextIsolation: true, // Isolate preload from renderer
871
+ nodeIntegration: false, // No Node.js in renderer
872
+ sandbox: true, // OS-level sandboxing
873
+ webSecurity: true, // Same-origin policy
874
+ allowRunningInsecureContent: false,
875
+
876
+ // These should be disabled unless specifically needed:
877
+ webviewTag: false, // No <webview> tags
878
+ navigateOnDragDrop: false, // No drag-and-drop navigation
879
+
880
+ // Preload script is the ONLY bridge
881
+ preload: path.join(__dirname, "preload.js"),
882
+ },
883
+ });
884
+ ```
885
+
886
+ ### Content Security Policy
887
+
888
+ ```html
889
+ <!-- src/renderer/index.html -->
890
+ <meta
891
+ http-equiv="Content-Security-Policy"
892
+ content="
893
+ default-src 'self';
894
+ script-src 'self';
895
+ style-src 'self' 'unsafe-inline';
896
+ img-src 'self' data: https:;
897
+ font-src 'self';
898
+ connect-src 'self' https://api.myapp.com;
899
+ "
900
+ />
901
+ ```
902
+
903
+ ### IPC Validation
904
+
905
+ ```typescript
906
+ // src/main/ipc-handlers.ts
907
+ import { z } from "zod";
908
+
909
+ // Define schemas for IPC messages
910
+ const FileReadSchema = z.object({
911
+ path: z.string().min(1).max(1024),
912
+ });
913
+
914
+ const FileWriteSchema = z.object({
915
+ path: z.string().min(1).max(1024),
916
+ content: z.string().max(10 * 1024 * 1024), // 10MB max
917
+ });
918
+
919
+ ipcMain.handle("file:read", async (_event, rawPath: unknown) => {
920
+ // Validate input
921
+ const parsed = FileReadSchema.safeParse({ path: rawPath });
922
+ if (!parsed.success) {
923
+ throw new Error(`Invalid input: ${parsed.error.message}`);
924
+ }
925
+
926
+ const filePath = path.resolve(parsed.data.path);
927
+ if (!isAllowedPath(filePath)) {
928
+ throw new Error("Access denied");
929
+ }
930
+
931
+ return fs.readFile(filePath, "utf-8");
932
+ });
933
+ ```
934
+
935
+ ## Performance Optimization
936
+
937
+ ### Startup Time
938
+
939
+ ```typescript
940
+ // Lazy-load heavy modules
941
+ app.whenReady().then(async () => {
942
+ // Show window immediately with skeleton
943
+ const win = createMainWindow();
944
+
945
+ // Load heavy modules after window is visible
946
+ const { initializeDatabase } = await import("./database");
947
+ const { syncData } = await import("./sync");
948
+
949
+ await initializeDatabase();
950
+ await syncData();
951
+
952
+ // Tell renderer initialization is complete
953
+ win.webContents.send("app:ready");
954
+ });
955
+ ```
956
+
957
+ ### Memory Management
958
+
959
+ ```typescript
960
+ // Destroy windows properly to free memory
961
+ function closeSettingsWindow(): void {
962
+ if (settingsWindow) {
963
+ settingsWindow.destroy(); // Not just close()
964
+ settingsWindow = null; // Allow garbage collection
965
+ }
966
+ }
967
+
968
+ // Monitor memory usage
969
+ setInterval(() => {
970
+ const usage = process.memoryUsage();
971
+ if (usage.heapUsed > 500 * 1024 * 1024) { // 500MB threshold
972
+ console.warn("High memory usage:", {
973
+ heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`,
974
+ rss: `${Math.round(usage.rss / 1024 / 1024)}MB`,
975
+ });
976
+ }
977
+ }, 30_000);
978
+
979
+ // Renderer: clean up event listeners
980
+ useEffect(() => {
981
+ const cleanup = electronAPI.onMenuAction(handleAction);
982
+ return () => cleanup(); // Always clean up
983
+ }, []);
984
+ ```
985
+
986
+ ### Renderer Performance
987
+
988
+ ```typescript
989
+ // Use web workers for heavy computation
990
+ const worker = new Worker(new URL("./worker.ts", import.meta.url));
991
+
992
+ worker.postMessage({ type: "parse", data: largeFileContent });
993
+ worker.onmessage = (event) => {
994
+ setResults(event.data);
995
+ };
996
+
997
+ // Use virtual scrolling for large lists
998
+ // (Use a library like react-window or tanstack-virtual)
999
+
1000
+ // Debounce IPC calls
1001
+ import { debounce } from "./utils";
1002
+
1003
+ const debouncedSave = debounce(async (content: string) => {
1004
+ await electronAPI.writeFile(currentFilePath, content);
1005
+ }, 1000);
1006
+ ```
1007
+
1008
+ ## Build and Distribution
1009
+
1010
+ ### Electron Forge Configuration
1011
+
1012
+ ```typescript
1013
+ // forge.config.ts
1014
+ import type { ForgeConfig } from "@electron-forge/shared-types";
1015
+ import { MakerSquirrel } from "@electron-forge/maker-squirrel";
1016
+ import { MakerDMG } from "@electron-forge/maker-dmg";
1017
+ import { MakerDeb } from "@electron-forge/maker-deb";
1018
+ import { MakerRpm } from "@electron-forge/maker-rpm";
1019
+ import { VitePlugin } from "@electron-forge/plugin-vite";
1020
+
1021
+ const config: ForgeConfig = {
1022
+ packagerConfig: {
1023
+ name: "My App",
1024
+ executableName: "my-app",
1025
+ icon: "./resources/icon",
1026
+ asar: true,
1027
+ appBundleId: "com.mycompany.myapp",
1028
+ // macOS signing
1029
+ osxSign: {},
1030
+ osxNotarize: {
1031
+ appleId: process.env.APPLE_ID || "",
1032
+ appleIdPassword: process.env.APPLE_PASSWORD || "",
1033
+ teamId: process.env.APPLE_TEAM_ID || "",
1034
+ },
1035
+ },
1036
+ makers: [
1037
+ new MakerSquirrel({
1038
+ name: "my-app",
1039
+ setupIcon: "./resources/icon.ico",
1040
+ }),
1041
+ new MakerDMG({
1042
+ icon: "./resources/icon.icns",
1043
+ }),
1044
+ new MakerDeb({
1045
+ options: {
1046
+ icon: "./resources/icon.png",
1047
+ categories: ["Utility"],
1048
+ },
1049
+ }),
1050
+ new MakerRpm({}),
1051
+ ],
1052
+ plugins: [
1053
+ new VitePlugin({
1054
+ build: [
1055
+ { entry: "src/main/index.ts", config: "vite.main.config.ts" },
1056
+ { entry: "src/preload/index.ts", config: "vite.preload.config.ts" },
1057
+ ],
1058
+ renderer: [
1059
+ {
1060
+ name: "main_window",
1061
+ config: "vite.renderer.config.ts",
1062
+ },
1063
+ ],
1064
+ }),
1065
+ ],
1066
+ };
1067
+
1068
+ export default config;
1069
+ ```
1070
+
1071
+ ### electron-builder Configuration
1072
+
1073
+ ```yaml
1074
+ # electron-builder.yml
1075
+ appId: com.mycompany.myapp
1076
+ productName: My App
1077
+ directories:
1078
+ output: dist
1079
+ buildResources: resources
1080
+
1081
+ files:
1082
+ - "out/**/*"
1083
+ - "package.json"
1084
+
1085
+ asar: true
1086
+
1087
+ win:
1088
+ target:
1089
+ - nsis
1090
+ - portable
1091
+ icon: resources/icon.ico
1092
+
1093
+ mac:
1094
+ target:
1095
+ - dmg
1096
+ - zip
1097
+ icon: resources/icon.icns
1098
+ category: public.app-category.developer-tools
1099
+ hardenedRuntime: true
1100
+ gatekeeperAssess: false
1101
+ entitlements: build/entitlements.mac.plist
1102
+
1103
+ linux:
1104
+ target:
1105
+ - AppImage
1106
+ - deb
1107
+ - rpm
1108
+ icon: resources/icons
1109
+ category: Utility
1110
+
1111
+ publish:
1112
+ provider: github
1113
+ owner: mycompany
1114
+ repo: my-app
1115
+ ```
1116
+
1117
+ ### Build Commands
1118
+
1119
+ ```bash
1120
+ # Electron Forge
1121
+ npx electron-forge start # Development
1122
+ npx electron-forge package # Package (no installer)
1123
+ npx electron-forge make # Create distributable
1124
+
1125
+ # electron-builder
1126
+ npx electron-builder --mac
1127
+ npx electron-builder --win
1128
+ npx electron-builder --linux
1129
+ npx electron-builder --mac --win --linux # All platforms
1130
+ ```
1131
+
1132
+ ## Crash Reporting
1133
+
1134
+ ```typescript
1135
+ // src/main/index.ts
1136
+ import { crashReporter } from "electron";
1137
+
1138
+ crashReporter.start({
1139
+ productName: "My App",
1140
+ submitURL: "https://crashes.myapp.com/submit",
1141
+ uploadToServer: true,
1142
+ compress: true,
1143
+ extra: {
1144
+ appVersion: app.getVersion(),
1145
+ platform: process.platform,
1146
+ },
1147
+ });
1148
+
1149
+ // Handle uncaught exceptions
1150
+ process.on("uncaughtException", (error) => {
1151
+ log.error("Uncaught exception:", error);
1152
+ // Show error dialog to user
1153
+ dialog.showErrorBox(
1154
+ "Application Error",
1155
+ "An unexpected error occurred. The app will now restart."
1156
+ );
1157
+ app.relaunch();
1158
+ app.exit(1);
1159
+ });
1160
+ ```
1161
+
1162
+ ## Testing
1163
+
1164
+ ### Main Process Tests
1165
+
1166
+ ```typescript
1167
+ // tests/main/ipc-handlers.test.ts
1168
+ import { describe, it, expect, vi, beforeEach } from "vitest";
1169
+
1170
+ // Mock Electron modules
1171
+ vi.mock("electron", () => ({
1172
+ ipcMain: {
1173
+ handle: vi.fn(),
1174
+ on: vi.fn(),
1175
+ },
1176
+ app: {
1177
+ getPath: vi.fn(() => "/mock/user/data"),
1178
+ getVersion: vi.fn(() => "1.0.0"),
1179
+ },
1180
+ dialog: {
1181
+ showOpenDialog: vi.fn(),
1182
+ },
1183
+ BrowserWindow: {
1184
+ fromWebContents: vi.fn(),
1185
+ },
1186
+ }));
1187
+
1188
+ describe("IPC Handlers", () => {
1189
+ it("should validate file paths", () => {
1190
+ const isAllowed = isAllowedPath("/mock/user/data/file.txt");
1191
+ expect(isAllowed).toBe(true);
1192
+ });
1193
+
1194
+ it("should reject paths outside allowed directories", () => {
1195
+ const isAllowed = isAllowedPath("/etc/passwd");
1196
+ expect(isAllowed).toBe(false);
1197
+ });
1198
+ });
1199
+ ```
1200
+
1201
+ ### E2E Tests with Playwright
1202
+
1203
+ ```typescript
1204
+ // tests/e2e/app.spec.ts
1205
+ import { test, expect, _electron as electron } from "@playwright/test";
1206
+
1207
+ let electronApp: Awaited<ReturnType<typeof electron.launch>>;
1208
+ let page: Awaited<ReturnType<typeof electronApp.firstWindow>>;
1209
+
1210
+ test.beforeAll(async () => {
1211
+ electronApp = await electron.launch({
1212
+ args: ["."],
1213
+ env: { NODE_ENV: "test" },
1214
+ });
1215
+ page = await electronApp.firstWindow();
1216
+ });
1217
+
1218
+ test.afterAll(async () => {
1219
+ await electronApp.close();
1220
+ });
1221
+
1222
+ test("should show the main window", async () => {
1223
+ const title = await page.title();
1224
+ expect(title).toBe("My Application");
1225
+ });
1226
+
1227
+ test("should display app version", async () => {
1228
+ const version = await page.locator("[data-testid='app-version']").textContent();
1229
+ expect(version).toMatch(/v\d+\.\d+\.\d+/);
1230
+ });
1231
+
1232
+ test("should open file dialog", async () => {
1233
+ // Mock the dialog at the Electron level
1234
+ await electronApp.evaluate(async ({ dialog }) => {
1235
+ dialog.showOpenDialog = async () => ({
1236
+ canceled: false,
1237
+ filePaths: ["/tmp/test.txt"],
1238
+ });
1239
+ });
1240
+
1241
+ await page.click("button:has-text('Open File')");
1242
+ // Verify file content is displayed
1243
+ await expect(page.locator("textarea")).not.toBeEmpty();
1244
+ });
1245
+ ```
1246
+
1247
+ ## Anti-Patterns
1248
+
1249
+ | Anti-Pattern | Why It Is Bad | Correct Approach |
1250
+ |--------------|---------------|------------------|
1251
+ | `nodeIntegration: true` | Full Node.js in renderer, massive attack surface | `nodeIntegration: false` + preload |
1252
+ | `contextIsolation: false` | Preload shares context with renderer | `contextIsolation: true` + contextBridge |
1253
+ | `webSecurity: false` | Disables same-origin policy | Keep `webSecurity: true` always |
1254
+ | Direct `require()` in renderer | Security hole, bypasses sandbox | Use contextBridge exposed API |
1255
+ | `remote` module | Deprecated, synchronous IPC, security risk | Use `invoke`/`handle` pattern |
1256
+ | Exposing entire `ipcRenderer` | Renderer can call any IPC channel | Expose specific, named functions only |
1257
+ | Storing secrets in renderer | Webview is inspectable via DevTools | Store in main process, use env vars |
1258
+ | Not destroying closed windows | Memory leak, window references persist | `window.destroy()` + null reference |
1259
+ | Synchronous IPC (`sendSync`) | Blocks renderer thread | Use `invoke` (async) |
1260
+ | Loading remote URLs without CSP | XSS and code injection risk | Strict CSP for all content |
1261
+ | Global `require` in preload | Exposes Node.js modules to renderer | Only expose needed functions via contextBridge |
1262
+ | `shell.openExternal(userInput)` | Arbitrary command execution | Validate URLs against allowlist |
1263
+
1264
+ ## Best Practices
1265
+
1266
+ 1. **Preload is the only bridge**: Never expose Node.js APIs directly to the renderer
1267
+ 2. **Validate all IPC input**: Use Zod schemas for IPC message validation
1268
+ 3. **Minimal API surface**: Only expose exactly what the renderer needs
1269
+ 4. **Type-safe IPC**: Define TypeScript types for all IPC channels
1270
+ 5. **Window state persistence**: Save and restore window position/size
1271
+ 6. **Graceful degradation**: Handle cases where IPC fails or times out
1272
+ 7. **Single instance lock**: Prevent multiple app instances from running
1273
+ 8. **Code sign everything**: Required for macOS notarization and Windows SmartScreen
1274
+ 9. **Lazy load heavy modules**: Import expensive modules after window is visible
1275
+ 10. **Monitor memory**: Set up alerts for excessive memory usage
1276
+ 11. **Test IPC handlers**: Unit test main process handlers without Electron
1277
+ 12. **Use Playwright for E2E**: Official Electron testing support
1278
+
1279
+ ## References
1280
+
1281
+ - Electron Documentation: https://www.electronjs.org/docs
1282
+ - Electron Security: https://www.electronjs.org/docs/latest/tutorial/security
1283
+ - Electron Forge: https://www.electronforge.io
1284
+ - electron-builder: https://www.electron.build
1285
+ - Electron Fiddle: https://www.electronjs.org/fiddle
1286
+ - Playwright Electron: https://playwright.dev/docs/api/class-electron