timsquad 2.1.0 → 3.4.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 (438) hide show
  1. package/README.ko.md +288 -0
  2. package/README.md +170 -763
  3. package/dist/commands/compile.d.ts +3 -0
  4. package/dist/commands/compile.d.ts.map +1 -0
  5. package/dist/commands/compile.js +170 -0
  6. package/dist/commands/compile.js.map +1 -0
  7. package/dist/commands/daemon.d.ts +7 -0
  8. package/dist/commands/daemon.d.ts.map +1 -0
  9. package/dist/commands/daemon.js +229 -0
  10. package/dist/commands/daemon.js.map +1 -0
  11. package/dist/commands/feedback.d.ts +9 -0
  12. package/dist/commands/feedback.d.ts.map +1 -1
  13. package/dist/commands/feedback.js +235 -14
  14. package/dist/commands/feedback.js.map +1 -1
  15. package/dist/commands/full.js +2 -2
  16. package/dist/commands/full.js.map +1 -1
  17. package/dist/commands/init.d.ts.map +1 -1
  18. package/dist/commands/init.js +118 -22
  19. package/dist/commands/init.js.map +1 -1
  20. package/dist/commands/knowledge.d.ts +3 -0
  21. package/dist/commands/knowledge.d.ts.map +1 -0
  22. package/dist/commands/knowledge.js +316 -0
  23. package/dist/commands/knowledge.js.map +1 -0
  24. package/dist/commands/log.d.ts +27 -0
  25. package/dist/commands/log.d.ts.map +1 -1
  26. package/dist/commands/log.js +965 -0
  27. package/dist/commands/log.js.map +1 -1
  28. package/dist/commands/meta-index.d.ts +3 -0
  29. package/dist/commands/meta-index.d.ts.map +1 -0
  30. package/dist/commands/meta-index.js +401 -0
  31. package/dist/commands/meta-index.js.map +1 -0
  32. package/dist/commands/metrics.d.ts.map +1 -1
  33. package/dist/commands/metrics.js +239 -4
  34. package/dist/commands/metrics.js.map +1 -1
  35. package/dist/commands/retro.js +154 -6
  36. package/dist/commands/retro.js.map +1 -1
  37. package/dist/commands/skills.d.ts +12 -0
  38. package/dist/commands/skills.d.ts.map +1 -0
  39. package/dist/commands/skills.js +231 -0
  40. package/dist/commands/skills.js.map +1 -0
  41. package/dist/commands/upgrade.d.ts +8 -0
  42. package/dist/commands/upgrade.d.ts.map +1 -0
  43. package/dist/commands/upgrade.js +292 -0
  44. package/dist/commands/upgrade.js.map +1 -0
  45. package/dist/commands/workflow.d.ts +3 -0
  46. package/dist/commands/workflow.d.ts.map +1 -0
  47. package/dist/commands/workflow.js +607 -0
  48. package/dist/commands/workflow.js.map +1 -0
  49. package/dist/daemon/context-writer.d.ts +16 -0
  50. package/dist/daemon/context-writer.d.ts.map +1 -0
  51. package/dist/daemon/context-writer.js +35 -0
  52. package/dist/daemon/context-writer.js.map +1 -0
  53. package/dist/daemon/entry.d.ts +7 -0
  54. package/dist/daemon/entry.d.ts.map +1 -0
  55. package/dist/daemon/entry.js +17 -0
  56. package/dist/daemon/entry.js.map +1 -0
  57. package/dist/daemon/event-queue.d.ts +52 -0
  58. package/dist/daemon/event-queue.d.ts.map +1 -0
  59. package/dist/daemon/event-queue.js +255 -0
  60. package/dist/daemon/event-queue.js.map +1 -0
  61. package/dist/daemon/file-watcher.d.ts +19 -0
  62. package/dist/daemon/file-watcher.d.ts.map +1 -0
  63. package/dist/daemon/file-watcher.js +87 -0
  64. package/dist/daemon/file-watcher.js.map +1 -0
  65. package/dist/daemon/index.d.ts +29 -0
  66. package/dist/daemon/index.d.ts.map +1 -0
  67. package/dist/daemon/index.js +296 -0
  68. package/dist/daemon/index.js.map +1 -0
  69. package/dist/daemon/jsonl-watcher.d.ts +49 -0
  70. package/dist/daemon/jsonl-watcher.d.ts.map +1 -0
  71. package/dist/daemon/jsonl-watcher.js +258 -0
  72. package/dist/daemon/jsonl-watcher.js.map +1 -0
  73. package/dist/daemon/meta-cache.d.ts +63 -0
  74. package/dist/daemon/meta-cache.d.ts.map +1 -0
  75. package/dist/daemon/meta-cache.js +249 -0
  76. package/dist/daemon/meta-cache.js.map +1 -0
  77. package/dist/daemon/session-state.d.ts +19 -0
  78. package/dist/daemon/session-state.d.ts.map +1 -0
  79. package/dist/daemon/session-state.js +132 -0
  80. package/dist/daemon/session-state.js.map +1 -0
  81. package/dist/daemon/shutdown.d.ts +21 -0
  82. package/dist/daemon/shutdown.d.ts.map +1 -0
  83. package/dist/daemon/shutdown.js +164 -0
  84. package/dist/daemon/shutdown.js.map +1 -0
  85. package/dist/index.js +24 -3
  86. package/dist/index.js.map +1 -1
  87. package/dist/lib/agent-composer.d.ts +38 -0
  88. package/dist/lib/agent-composer.d.ts.map +1 -0
  89. package/dist/lib/agent-composer.js +128 -0
  90. package/dist/lib/agent-composer.js.map +1 -0
  91. package/dist/lib/agent-generator.d.ts +22 -0
  92. package/dist/lib/agent-generator.d.ts.map +1 -0
  93. package/dist/lib/agent-generator.js +150 -0
  94. package/dist/lib/agent-generator.js.map +1 -0
  95. package/dist/lib/ast-parser.d.ts +11 -0
  96. package/dist/lib/ast-parser.d.ts.map +1 -0
  97. package/dist/lib/ast-parser.js +282 -0
  98. package/dist/lib/ast-parser.js.map +1 -0
  99. package/dist/lib/compile-rules.d.ts +66 -0
  100. package/dist/lib/compile-rules.d.ts.map +1 -0
  101. package/dist/lib/compile-rules.js +114 -0
  102. package/dist/lib/compile-rules.js.map +1 -0
  103. package/dist/lib/compiler.d.ts +105 -0
  104. package/dist/lib/compiler.d.ts.map +1 -0
  105. package/dist/lib/compiler.js +368 -0
  106. package/dist/lib/compiler.js.map +1 -0
  107. package/dist/lib/config.d.ts +7 -2
  108. package/dist/lib/config.d.ts.map +1 -1
  109. package/dist/lib/config.js +34 -3
  110. package/dist/lib/config.js.map +1 -1
  111. package/dist/lib/meta-index.d.ts +19 -0
  112. package/dist/lib/meta-index.d.ts.map +1 -0
  113. package/dist/lib/meta-index.js +573 -0
  114. package/dist/lib/meta-index.js.map +1 -0
  115. package/dist/lib/project.js +1 -1
  116. package/dist/lib/project.js.map +1 -1
  117. package/dist/lib/skill-generator.d.ts +32 -0
  118. package/dist/lib/skill-generator.d.ts.map +1 -0
  119. package/dist/lib/skill-generator.js +187 -0
  120. package/dist/lib/skill-generator.js.map +1 -0
  121. package/dist/lib/template.d.ts +16 -2
  122. package/dist/lib/template.d.ts.map +1 -1
  123. package/dist/lib/template.js +115 -20
  124. package/dist/lib/template.js.map +1 -1
  125. package/dist/lib/ui-index.d.ts +12 -0
  126. package/dist/lib/ui-index.d.ts.map +1 -0
  127. package/dist/lib/ui-index.js +239 -0
  128. package/dist/lib/ui-index.js.map +1 -0
  129. package/dist/lib/ui-parser.d.ts +12 -0
  130. package/dist/lib/ui-parser.d.ts.map +1 -0
  131. package/dist/lib/ui-parser.js +472 -0
  132. package/dist/lib/ui-parser.js.map +1 -0
  133. package/dist/lib/update-check.d.ts +6 -0
  134. package/dist/lib/update-check.d.ts.map +1 -0
  135. package/dist/lib/update-check.js +121 -0
  136. package/dist/lib/update-check.js.map +1 -0
  137. package/dist/lib/upgrade-backup.d.ts +33 -0
  138. package/dist/lib/upgrade-backup.d.ts.map +1 -0
  139. package/dist/lib/upgrade-backup.js +101 -0
  140. package/dist/lib/upgrade-backup.js.map +1 -0
  141. package/dist/lib/version.d.ts +19 -0
  142. package/dist/lib/version.d.ts.map +1 -0
  143. package/dist/lib/version.js +35 -0
  144. package/dist/lib/version.js.map +1 -0
  145. package/dist/lib/workflow-state.d.ts +48 -0
  146. package/dist/lib/workflow-state.d.ts.map +1 -0
  147. package/dist/lib/workflow-state.js +67 -0
  148. package/dist/lib/workflow-state.js.map +1 -0
  149. package/dist/types/config.d.ts +103 -2
  150. package/dist/types/config.d.ts.map +1 -1
  151. package/dist/types/config.js +184 -9
  152. package/dist/types/config.js.map +1 -1
  153. package/dist/types/feedback.d.ts +7 -0
  154. package/dist/types/feedback.d.ts.map +1 -1
  155. package/dist/types/feedback.js +1 -1
  156. package/dist/types/feedback.js.map +1 -1
  157. package/dist/types/index.d.ts +3 -0
  158. package/dist/types/index.d.ts.map +1 -1
  159. package/dist/types/index.js +3 -0
  160. package/dist/types/index.js.map +1 -1
  161. package/dist/types/meta-index.d.ts +146 -0
  162. package/dist/types/meta-index.d.ts.map +1 -0
  163. package/dist/types/meta-index.js +7 -0
  164. package/dist/types/meta-index.js.map +1 -0
  165. package/dist/types/project.d.ts +19 -3
  166. package/dist/types/project.d.ts.map +1 -1
  167. package/dist/types/project.js +23 -0
  168. package/dist/types/project.js.map +1 -1
  169. package/dist/types/task-log.d.ts +208 -0
  170. package/dist/types/task-log.d.ts.map +1 -0
  171. package/dist/types/task-log.js +6 -0
  172. package/dist/types/task-log.js.map +1 -0
  173. package/dist/types/ui-meta.d.ts +118 -0
  174. package/dist/types/ui-meta.d.ts.map +1 -0
  175. package/dist/types/ui-meta.js +7 -0
  176. package/dist/types/ui-meta.js.map +1 -0
  177. package/package.json +12 -4
  178. package/templates/base/agents/base/tsq-architect.md +68 -0
  179. package/templates/base/agents/base/tsq-dba.md +56 -0
  180. package/templates/base/agents/base/tsq-designer.md +72 -0
  181. package/templates/base/agents/base/tsq-developer.md +67 -0
  182. package/templates/base/agents/base/tsq-qa.md +55 -0
  183. package/templates/base/agents/base/tsq-security.md +65 -0
  184. package/templates/base/agents/overlays/domain/general-web/_common.md +11 -0
  185. package/templates/base/agents/overlays/domain/mobile/_common.md +13 -0
  186. package/templates/base/agents/overlays/platform/claude-code.md +12 -0
  187. package/templates/base/config.template.yaml +213 -0
  188. package/templates/base/knowledge/checklists/accessibility.md +37 -0
  189. package/templates/base/knowledge/checklists/architecture-review.md +28 -0
  190. package/templates/base/knowledge/checklists/database-standards.md +84 -0
  191. package/templates/base/knowledge/checklists/design-reference.md +97 -0
  192. package/templates/base/knowledge/checklists/security.md +50 -0
  193. package/templates/base/knowledge/checklists/ssot-validation.md +19 -0
  194. package/templates/base/knowledge/domains/_template.md +16 -0
  195. package/templates/base/knowledge/platforms/_template.md +16 -0
  196. package/templates/base/knowledge/templates/sequence-report.md +44 -0
  197. package/templates/base/knowledge/templates/task-result.md +105 -0
  198. package/templates/base/skills/_template/SKILL.md +59 -0
  199. package/templates/base/skills/_template/references/_template.md +35 -0
  200. package/templates/base/skills/_template/rules/_sections.md +34 -0
  201. package/templates/base/skills/_template/rules/_template.md +32 -0
  202. package/templates/base/skills/_template/scripts/_template.sh +31 -0
  203. package/templates/base/skills/architecture/SKILL.md +54 -0
  204. package/templates/base/skills/architecture/references/adr-template.md +50 -0
  205. package/templates/base/skills/architecture/references/api-design.md +64 -0
  206. package/templates/base/skills/backend/node/SKILL.md +81 -0
  207. package/templates/base/skills/backend/node/rules/async-patterns.md +81 -0
  208. package/templates/base/skills/backend/node/rules/deployment.md +33 -0
  209. package/templates/base/skills/backend/node/rules/env-config.md +41 -0
  210. package/templates/base/skills/backend/node/rules/error-handling.md +83 -0
  211. package/templates/base/skills/backend/node/rules/hono-app-setup.md +98 -0
  212. package/templates/base/skills/backend/node/rules/jwt-auth.md +76 -0
  213. package/templates/base/skills/backend/node/rules/middleware.md +56 -0
  214. package/templates/base/skills/backend/node/rules/testing.md +82 -0
  215. package/templates/base/skills/coding/SKILL.md +47 -0
  216. package/templates/base/skills/coding/rules/patterns.md +81 -0
  217. package/templates/base/skills/controller/SKILL.md +111 -0
  218. package/templates/base/skills/controller/references/README.md +35 -0
  219. package/templates/base/skills/controller/rules/README.md +18 -0
  220. package/templates/base/skills/database/SKILL.md +98 -0
  221. package/templates/base/skills/database/prisma/SKILL.md +57 -0
  222. package/templates/base/skills/database/prisma/rules/queries.md +133 -0
  223. package/templates/base/skills/database/prisma/rules/schema-design.md +80 -0
  224. package/templates/base/skills/frontend/nextjs/SKILL.md +59 -0
  225. package/templates/base/skills/frontend/nextjs/rules/app-router.md +138 -0
  226. package/templates/base/skills/frontend/react/SKILL.md +86 -0
  227. package/templates/base/skills/frontend/react/rules/_sections.md +88 -0
  228. package/templates/base/skills/frontend/react/rules/anti-patterns.md +67 -0
  229. package/templates/base/skills/frontend/react/rules/async-api-routes.md +38 -0
  230. package/templates/base/skills/frontend/react/rules/async-defer-await.md +80 -0
  231. package/templates/base/skills/frontend/react/rules/async-dependencies.md +36 -0
  232. package/templates/base/skills/frontend/react/rules/async-parallel.md +28 -0
  233. package/templates/base/skills/frontend/react/rules/async-suspense-boundaries.md +99 -0
  234. package/templates/base/skills/frontend/react/rules/bundle-barrel-imports.md +59 -0
  235. package/templates/base/skills/frontend/react/rules/bundle-defer-third-party.md +49 -0
  236. package/templates/base/skills/frontend/react/rules/bundle-dynamic-imports.md +35 -0
  237. package/templates/base/skills/frontend/react/rules/component-conventions.md +74 -0
  238. package/templates/base/skills/frontend/react/rules/js-combine-iterations.md +32 -0
  239. package/templates/base/skills/frontend/react/rules/js-early-exit.md +50 -0
  240. package/templates/base/skills/frontend/react/rules/js-index-maps.md +37 -0
  241. package/templates/base/skills/frontend/react/rules/js-set-map-lookups.md +24 -0
  242. package/templates/base/skills/frontend/react/rules/rendering-conditional-render.md +40 -0
  243. package/templates/base/skills/frontend/react/rules/rendering-content-visibility.md +38 -0
  244. package/templates/base/skills/frontend/react/rules/rendering-hoist-jsx.md +46 -0
  245. package/templates/base/skills/frontend/react/rules/rerender-defer-reads.md +39 -0
  246. package/templates/base/skills/frontend/react/rules/rerender-derived-state.md +29 -0
  247. package/templates/base/skills/frontend/react/rules/rerender-memo.md +44 -0
  248. package/templates/base/skills/frontend/react/rules/rerender-transitions.md +40 -0
  249. package/templates/base/skills/frontend/react/rules/server-after-nonblocking.md +73 -0
  250. package/templates/base/skills/frontend/react/rules/server-cache-react.md +26 -0
  251. package/templates/base/skills/frontend/react/rules/server-parallel-fetching.md +79 -0
  252. package/templates/base/skills/frontend/react/rules/state-location.md +55 -0
  253. package/templates/base/skills/methodology/bdd/SKILL.md +69 -0
  254. package/templates/base/skills/methodology/bdd/rules/gherkin-patterns.md +113 -0
  255. package/templates/base/skills/methodology/ddd/SKILL.md +74 -0
  256. package/templates/base/skills/methodology/ddd/rules/strategic-patterns.md +98 -0
  257. package/templates/base/skills/methodology/debugging/SKILL.md +60 -0
  258. package/templates/base/skills/methodology/debugging/references/root-cause-tracing.md +84 -0
  259. package/templates/base/skills/methodology/tdd/SKILL.md +66 -0
  260. package/templates/base/skills/methodology/tdd/rules/real-world-example.md +88 -0
  261. package/templates/base/skills/methodology/tdd/rules/techniques.md +185 -0
  262. package/templates/base/skills/mobile/dart/SKILL.md +69 -0
  263. package/templates/base/skills/mobile/dart/rules/async-patterns.md +112 -0
  264. package/templates/base/skills/mobile/dart/rules/code-style.md +96 -0
  265. package/templates/base/skills/mobile/dart/rules/null-safety.md +84 -0
  266. package/templates/base/skills/mobile/dart/rules/type-system.md +111 -0
  267. package/templates/base/skills/mobile/flutter/SKILL.md +89 -0
  268. package/templates/base/skills/mobile/flutter/ci-cd/SKILL.md +82 -0
  269. package/templates/base/skills/mobile/flutter/ci-cd/references/ci-cd-pipeline.md +314 -0
  270. package/templates/base/skills/mobile/flutter/ci-cd/rules/code-signing.md +106 -0
  271. package/templates/base/skills/mobile/flutter/ci-cd/rules/codemagic-setup.md +116 -0
  272. package/templates/base/skills/mobile/flutter/ci-cd/rules/fastlane-setup.md +105 -0
  273. package/templates/base/skills/mobile/flutter/ci-cd/rules/github-actions.md +112 -0
  274. package/templates/base/skills/mobile/flutter/ci-cd/rules/store-deployment.md +106 -0
  275. package/templates/base/skills/mobile/flutter/ci-cd/rules/versioning.md +107 -0
  276. package/templates/base/skills/mobile/flutter/i18n/SKILL.md +78 -0
  277. package/templates/base/skills/mobile/flutter/i18n/references/i18n-architecture.md +225 -0
  278. package/templates/base/skills/mobile/flutter/i18n/rules/arb-files.md +182 -0
  279. package/templates/base/skills/mobile/flutter/i18n/rules/locale-switching.md +226 -0
  280. package/templates/base/skills/mobile/flutter/i18n/rules/localization-setup.md +137 -0
  281. package/templates/base/skills/mobile/flutter/i18n/rules/plural-gender.md +159 -0
  282. package/templates/base/skills/mobile/flutter/i18n/rules/text-direction.md +199 -0
  283. package/templates/base/skills/mobile/flutter/monitoring/SKILL.md +81 -0
  284. package/templates/base/skills/mobile/flutter/monitoring/references/monitoring-architecture.md +269 -0
  285. package/templates/base/skills/mobile/flutter/monitoring/rules/analytics.md +227 -0
  286. package/templates/base/skills/mobile/flutter/monitoring/rules/crashlytics-setup.md +195 -0
  287. package/templates/base/skills/mobile/flutter/monitoring/rules/logging.md +258 -0
  288. package/templates/base/skills/mobile/flutter/monitoring/rules/performance-monitoring.md +248 -0
  289. package/templates/base/skills/mobile/flutter/monitoring/rules/sentry-integration.md +249 -0
  290. package/templates/base/skills/mobile/flutter/networking/SKILL.md +88 -0
  291. package/templates/base/skills/mobile/flutter/networking/references/api-client-architecture.md +305 -0
  292. package/templates/base/skills/mobile/flutter/networking/rules/caching.md +212 -0
  293. package/templates/base/skills/mobile/flutter/networking/rules/connectivity.md +213 -0
  294. package/templates/base/skills/mobile/flutter/networking/rules/dio-setup.md +159 -0
  295. package/templates/base/skills/mobile/flutter/networking/rules/error-handling.md +209 -0
  296. package/templates/base/skills/mobile/flutter/networking/rules/interceptors.md +205 -0
  297. package/templates/base/skills/mobile/flutter/networking/rules/retrofit-patterns.md +194 -0
  298. package/templates/base/skills/mobile/flutter/push-notifications/SKILL.md +87 -0
  299. package/templates/base/skills/mobile/flutter/push-notifications/references/notification-architecture.md +340 -0
  300. package/templates/base/skills/mobile/flutter/push-notifications/references/platform-setup.md +286 -0
  301. package/templates/base/skills/mobile/flutter/push-notifications/rules/background-processing.md +308 -0
  302. package/templates/base/skills/mobile/flutter/push-notifications/rules/deep-linking.md +217 -0
  303. package/templates/base/skills/mobile/flutter/push-notifications/rules/fcm-setup.md +164 -0
  304. package/templates/base/skills/mobile/flutter/push-notifications/rules/local-notifications.md +262 -0
  305. package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-handling.md +210 -0
  306. package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-permissions.md +246 -0
  307. package/templates/base/skills/mobile/flutter/push-notifications/rules/rich-notifications.md +320 -0
  308. package/templates/base/skills/mobile/flutter/references/freezed-patterns.md +162 -0
  309. package/templates/base/skills/mobile/flutter/references/project-structure.md +170 -0
  310. package/templates/base/skills/mobile/flutter/rules/animations.md +112 -0
  311. package/templates/base/skills/mobile/flutter/rules/architecture.md +121 -0
  312. package/templates/base/skills/mobile/flutter/rules/navigation-routing.md +117 -0
  313. package/templates/base/skills/mobile/flutter/rules/performance.md +112 -0
  314. package/templates/base/skills/mobile/flutter/rules/platform-adaptive.md +126 -0
  315. package/templates/base/skills/mobile/flutter/rules/state-management.md +110 -0
  316. package/templates/base/skills/mobile/flutter/rules/testing.md +131 -0
  317. package/templates/base/skills/mobile/flutter/rules/widget-conventions.md +122 -0
  318. package/templates/base/skills/mobile/flutter/security/SKILL.md +86 -0
  319. package/templates/base/skills/mobile/flutter/security/references/mobile-security-checklist.md +168 -0
  320. package/templates/base/skills/mobile/flutter/security/rules/api-key-protection.md +206 -0
  321. package/templates/base/skills/mobile/flutter/security/rules/authentication.md +248 -0
  322. package/templates/base/skills/mobile/flutter/security/rules/data-protection.md +271 -0
  323. package/templates/base/skills/mobile/flutter/security/rules/obfuscation.md +213 -0
  324. package/templates/base/skills/mobile/flutter/security/rules/secure-storage.md +171 -0
  325. package/templates/base/skills/mobile/flutter/security/rules/ssl-pinning.md +197 -0
  326. package/templates/base/skills/planning/SKILL.md +58 -0
  327. package/templates/base/skills/planning/references/prd-guide.md +47 -0
  328. package/templates/base/skills/planning/references/requirements-guide.md +46 -0
  329. package/templates/base/skills/prompt-engineering/SKILL.md +103 -0
  330. package/templates/base/skills/retrospective/SKILL.md +102 -0
  331. package/templates/base/skills/security/SKILL.md +55 -0
  332. package/templates/base/skills/security/rules/owasp-examples.md +119 -0
  333. package/templates/base/skills/security/scripts/check-secrets.sh +55 -0
  334. package/templates/base/skills/testing/SKILL.md +63 -0
  335. package/templates/base/skills/testing/references/testing-patterns.md +103 -0
  336. package/templates/base/skills/tsq-protocol/SKILL.md +51 -0
  337. package/templates/base/skills/typescript/SKILL.md +67 -0
  338. package/templates/base/skills/typescript/rules/type-patterns.md +135 -0
  339. package/templates/base/skills/typescript/rules/utility-types.md +76 -0
  340. package/templates/base/skills/ui-design/SKILL.md +70 -0
  341. package/templates/{common → base}/timsquad/feedback/routing-rules.yaml +1 -1
  342. package/templates/{common → base}/timsquad/retrospective/metrics/metrics-schema.json +46 -1
  343. package/templates/platforms/claude-code/CLAUDE.md.template +89 -0
  344. package/templates/platforms/claude-code/rules/adr-rules.md +32 -0
  345. package/templates/platforms/claude-code/rules/feedback-routing.md +18 -0
  346. package/templates/platforms/claude-code/rules/phase-management.md +23 -0
  347. package/templates/platforms/claude-code/rules/reporting-format.md +26 -0
  348. package/templates/platforms/claude-code/rules/sequence-management.md +72 -0
  349. package/templates/platforms/claude-code/rules/workspace-sync.md +33 -0
  350. package/templates/platforms/claude-code/scripts/completion-guard.sh +57 -0
  351. package/templates/platforms/claude-code/scripts/phase-guard.sh +79 -0
  352. package/templates/platforms/claude-code/settings.json +98 -0
  353. package/templates/project-types/api-backend/config.yaml +227 -0
  354. package/templates/project-types/api-backend/process/workflow.xml +214 -0
  355. package/templates/project-types/fintech/config.yaml +151 -0
  356. package/templates/project-types/fintech/process/workflow.xml +316 -0
  357. package/templates/project-types/infra/config.yaml +327 -0
  358. package/templates/project-types/infra/process/workflow.xml +296 -0
  359. package/templates/project-types/mobile-app/config.yaml +123 -0
  360. package/templates/project-types/mobile-app/process/workflow.xml +191 -0
  361. package/templates/project-types/platform/config.yaml +254 -0
  362. package/templates/project-types/platform/process/workflow.xml +254 -0
  363. package/templates/project-types/web-app/config.yaml +198 -0
  364. package/templates/project-types/web-app/process/workflow.xml +210 -0
  365. package/templates/project-types/web-service/config.yaml +136 -0
  366. package/templates/project-types/web-service/process/workflow.xml +184 -0
  367. package/templates/common/CLAUDE.md.template +0 -254
  368. package/templates/common/claude/agents/tsq-dba.md +0 -311
  369. package/templates/common/claude/agents/tsq-designer.md +0 -323
  370. package/templates/common/claude/agents/tsq-developer.md +0 -177
  371. package/templates/common/claude/agents/tsq-planner.md +0 -190
  372. package/templates/common/claude/agents/tsq-prompter.md +0 -356
  373. package/templates/common/claude/agents/tsq-qa.md +0 -168
  374. package/templates/common/claude/agents/tsq-retro.md +0 -193
  375. package/templates/common/claude/agents/tsq-security.md +0 -221
  376. package/templates/common/claude/hooks/auto-metrics.sh +0 -165
  377. package/templates/common/claude/hooks/auto-worklog.sh +0 -245
  378. package/templates/common/claude/hooks/event-logger.sh +0 -208
  379. package/templates/common/claude/settings.json +0 -86
  380. package/templates/common/claude/skills/architecture/SKILL.md +0 -123
  381. package/templates/common/claude/skills/backend/node/SKILL.md +0 -1015
  382. package/templates/common/claude/skills/coding/SKILL.md +0 -171
  383. package/templates/common/claude/skills/database/prisma/SKILL.md +0 -357
  384. package/templates/common/claude/skills/frontend/nextjs/SKILL.md +0 -279
  385. package/templates/common/claude/skills/frontend/react/SKILL.md +0 -1729
  386. package/templates/common/claude/skills/methodology/bdd/SKILL.md +0 -234
  387. package/templates/common/claude/skills/methodology/ddd/SKILL.md +0 -311
  388. package/templates/common/claude/skills/methodology/tdd/SKILL.md +0 -512
  389. package/templates/common/claude/skills/planning/SKILL.md +0 -90
  390. package/templates/common/claude/skills/security/SKILL.md +0 -234
  391. package/templates/common/claude/skills/testing/SKILL.md +0 -146
  392. package/templates/common/claude/skills/typescript/SKILL.md +0 -435
  393. package/templates/common/config.template.yaml +0 -132
  394. /package/templates/{common → base}/timsquad/architectures/clean/ARCHITECTURE.md +0 -0
  395. /package/templates/{common → base}/timsquad/architectures/clean/backend.xml +0 -0
  396. /package/templates/{common → base}/timsquad/architectures/clean/frontend.xml +0 -0
  397. /package/templates/{common → base}/timsquad/architectures/fsd/ARCHITECTURE.md +0 -0
  398. /package/templates/{common → base}/timsquad/architectures/fsd/frontend.xml +0 -0
  399. /package/templates/{common → base}/timsquad/architectures/hexagonal/ARCHITECTURE.md +0 -0
  400. /package/templates/{common → base}/timsquad/architectures/hexagonal/backend.xml +0 -0
  401. /package/templates/{common → base}/timsquad/constraints/competency-framework.xml +0 -0
  402. /package/templates/{common → base}/timsquad/constraints/ssot-schema.xml +0 -0
  403. /package/templates/{common → base}/timsquad/feedback/feedback-router.sh +0 -0
  404. /package/templates/{common → base}/timsquad/generators/data-design.xml +0 -0
  405. /package/templates/{common → base}/timsquad/generators/prd.xml +0 -0
  406. /package/templates/{common → base}/timsquad/generators/requirements.xml +0 -0
  407. /package/templates/{common → base}/timsquad/generators/service-spec.xml +0 -0
  408. /package/templates/{common → base}/timsquad/logs/_example.md +0 -0
  409. /package/templates/{common → base}/timsquad/logs/_template.md +0 -0
  410. /package/templates/{common → base}/timsquad/patterns/cqrs.xml +0 -0
  411. /package/templates/{common → base}/timsquad/patterns/event-sourcing.xml +0 -0
  412. /package/templates/{common → base}/timsquad/patterns/repository.xml +0 -0
  413. /package/templates/{common → base}/timsquad/process/phase-checklist.yaml +0 -0
  414. /package/templates/{common → base}/timsquad/process/state-machine.xml +0 -0
  415. /package/templates/{common → base}/timsquad/process/validation-rules.xml +0 -0
  416. /package/templates/{common → base}/timsquad/process/workflow-base.xml +0 -0
  417. /package/templates/{common → base}/timsquad/retrospective/cycle-report.template.md +0 -0
  418. /package/templates/{common → base}/timsquad/retrospective/patterns/failure-patterns.md +0 -0
  419. /package/templates/{common → base}/timsquad/retrospective/patterns/success-patterns.md +0 -0
  420. /package/templates/{common → base}/timsquad/retrospective/retrospective-config.xml +0 -0
  421. /package/templates/{common → base}/timsquad/retrospective/retrospective-state.xml +0 -0
  422. /package/templates/{common → base}/timsquad/ssot/adr/ADR-000-template.md +0 -0
  423. /package/templates/{common → base}/timsquad/ssot/adr/ADR-001-example.md +0 -0
  424. /package/templates/{common → base}/timsquad/ssot/data-design.template.md +0 -0
  425. /package/templates/{common → base}/timsquad/ssot/deployment-spec.template.md +0 -0
  426. /package/templates/{common → base}/timsquad/ssot/env-config.template.md +0 -0
  427. /package/templates/{common → base}/timsquad/ssot/error-codes.template.md +0 -0
  428. /package/templates/{common → base}/timsquad/ssot/functional-spec.template.md +0 -0
  429. /package/templates/{common → base}/timsquad/ssot/glossary.template.md +0 -0
  430. /package/templates/{common → base}/timsquad/ssot/integration-spec.template.md +0 -0
  431. /package/templates/{common → base}/timsquad/ssot/planning.template.md +0 -0
  432. /package/templates/{common → base}/timsquad/ssot/prd.template.md +0 -0
  433. /package/templates/{common → base}/timsquad/ssot/requirements.template.md +0 -0
  434. /package/templates/{common → base}/timsquad/ssot/security-spec.template.md +0 -0
  435. /package/templates/{common → base}/timsquad/ssot/service-spec.template.md +0 -0
  436. /package/templates/{common → base}/timsquad/ssot/test-spec.template.md +0 -0
  437. /package/templates/{common → base}/timsquad/ssot/ui-ux-spec.template.md +0 -0
  438. /package/templates/{common → base}/timsquad/state/workspace.xml +0 -0
@@ -0,0 +1,308 @@
1
+ ---
2
+ title: Background Processing
3
+ impact: HIGH
4
+ impactDescription: "백그라운드 미처리 → 데이터 미동기화, 사용자 재진입 시 stale 데이터"
5
+ tags: workmanager, background-fetch, isolate, periodic-task
6
+ ---
7
+
8
+ ## Background Processing
9
+
10
+ **Impact: HIGH (백그라운드 미처리 → 데이터 미동기화, 사용자 재진입 시 stale 데이터)**
11
+
12
+ workmanager 패키지 기반 백그라운드 태스크. 주기적 동기화, 데이터 프리페치, 오프라인 큐 처리.
13
+
14
+ ### 의존성
15
+
16
+ ```yaml
17
+ dependencies:
18
+ workmanager: ^0.5.2
19
+ ```
20
+
21
+ ### 초기화
22
+
23
+ **Incorrect (콜백을 클래스 메서드로 등록):**
24
+ ```dart
25
+ class BackgroundService {
26
+ // 클래스 인스턴스 메서드 → top-level 아니므로 실패
27
+ void callbackDispatcher() {
28
+ Workmanager().executeTask((task, inputData) async {
29
+ return true;
30
+ });
31
+ }
32
+ }
33
+ ```
34
+
35
+ **Correct (top-level 함수로 콜백 등록):**
36
+ ```dart
37
+ // background_tasks.dart — top-level 함수 필수
38
+ @pragma('vm:entry-point')
39
+ void callbackDispatcher() {
40
+ Workmanager().executeTask((taskName, inputData) async {
41
+ // 백그라운드 Isolate에서 실행
42
+ // → 앱 상태, Provider, Navigator 접근 불가
43
+ // → DB, SharedPreferences, HTTP 직접 사용
44
+
45
+ switch (taskName) {
46
+ case BackgroundTasks.syncMatches:
47
+ return await _syncMatches();
48
+ case BackgroundTasks.cleanCache:
49
+ return await _cleanCache();
50
+ case BackgroundTasks.uploadPendingData:
51
+ return await _uploadPendingData(inputData);
52
+ case Workmanager.iOSBackgroundTask:
53
+ // iOS 백그라운드 fetch (시스템이 자동 호출)
54
+ return await _syncMatches();
55
+ default:
56
+ return Future.value(true);
57
+ }
58
+ });
59
+ }
60
+
61
+ /// 태스크 이름 상수
62
+ abstract class BackgroundTasks {
63
+ static const syncMatches = 'sync_matches';
64
+ static const cleanCache = 'clean_cache';
65
+ static const uploadPendingData = 'upload_pending_data';
66
+ }
67
+
68
+ // main.dart
69
+ Future<void> main() async {
70
+ WidgetsFlutterBinding.ensureInitialized();
71
+
72
+ // Workmanager 초기화
73
+ await Workmanager().initialize(
74
+ callbackDispatcher,
75
+ isInDebugMode: kDebugMode, // 디버그 시 알림으로 태스크 실행 확인
76
+ );
77
+
78
+ runApp(const ProviderScope(child: MyApp()));
79
+ }
80
+ ```
81
+
82
+ ### 주기적 태스크 등록
83
+
84
+ ```dart
85
+ class BackgroundTaskManager {
86
+ /// 앱 시작 시 또는 로그인 후 등록
87
+ Future<void> registerPeriodicTasks() async {
88
+ // === 매치 데이터 동기화 (15분 간격) ===
89
+ await Workmanager().registerPeriodicTask(
90
+ 'sync-matches-periodic', // uniqueName (취소용)
91
+ BackgroundTasks.syncMatches,
92
+ frequency: const Duration(minutes: 15), // 최소 15분 (OS 제어)
93
+ constraints: Constraints(
94
+ networkType: NetworkType.connected, // 네트워크 필요
95
+ requiresBatteryNotLow: true, // 배터리 부족 시 스킵
96
+ ),
97
+ existingWorkPolicy: ExistingWorkPolicy.keep, // 기존 등록 유지
98
+ backoffPolicy: BackoffPolicy.exponential,
99
+ initialDelay: const Duration(minutes: 5),
100
+ );
101
+
102
+ // === 캐시 정리 (1일 간격) ===
103
+ await Workmanager().registerPeriodicTask(
104
+ 'clean-cache-daily',
105
+ BackgroundTasks.cleanCache,
106
+ frequency: const Duration(hours: 24),
107
+ constraints: Constraints(
108
+ requiresCharging: true, // 충전 중일 때만
109
+ requiresDeviceIdle: true, // 유휴 상태일 때만 (Android)
110
+ ),
111
+ );
112
+ }
113
+
114
+ /// 1회성 태스크 (즉시 또는 지연 실행)
115
+ Future<void> scheduleOneOffTask({
116
+ required String data,
117
+ }) async {
118
+ await Workmanager().registerOneOffTask(
119
+ 'upload-pending-${DateTime.now().millisecondsSinceEpoch}',
120
+ BackgroundTasks.uploadPendingData,
121
+ inputData: {'data': data}, // 태스크에 전달할 데이터
122
+ constraints: Constraints(
123
+ networkType: NetworkType.connected,
124
+ ),
125
+ initialDelay: const Duration(seconds: 10),
126
+ );
127
+ }
128
+
129
+ /// 모든 태스크 취소 (로그아웃 시)
130
+ Future<void> cancelAll() async {
131
+ await Workmanager().cancelAll();
132
+ }
133
+
134
+ /// 특정 태스크 취소
135
+ Future<void> cancelTask(String uniqueName) async {
136
+ await Workmanager().cancelByUniqueName(uniqueName);
137
+ }
138
+ }
139
+ ```
140
+
141
+ ### 백그라운드 태스크 구현
142
+
143
+ ```dart
144
+ /// 매치 데이터 동기화 (백그라운드 Isolate에서 실행)
145
+ Future<bool> _syncMatches() async {
146
+ try {
147
+ // 직접 HTTP 호출 (Dio/http 패키지)
148
+ // Provider/Repository 사용 불가 → 직접 구현
149
+ final client = http.Client();
150
+ final prefs = await SharedPreferences.getInstance();
151
+ final token = prefs.getString('auth_token');
152
+
153
+ if (token == null) return true; // 미로그인 → 스킵
154
+
155
+ final response = await client.get(
156
+ Uri.parse('https://api.example.com/matches/sync'),
157
+ headers: {'Authorization': 'Bearer $token'},
158
+ );
159
+
160
+ if (response.statusCode == 200) {
161
+ final data = jsonDecode(response.body) as List;
162
+ // 로컬 DB에 저장 (SQLite/Hive 직접 접근)
163
+ final db = await openDatabase('app.db');
164
+ for (final match in data) {
165
+ await db.insert(
166
+ 'matches',
167
+ match as Map<String, dynamic>,
168
+ conflictAlgorithm: ConflictAlgorithm.replace,
169
+ );
170
+ }
171
+ }
172
+
173
+ client.close();
174
+ return true; // 성공
175
+ } catch (e) {
176
+ debugPrint('Background sync failed: $e');
177
+ return false; // 실패 → OS가 재시도 스케줄
178
+ }
179
+ }
180
+
181
+ /// 오프라인 데이터 업로드 (네트워크 복구 시)
182
+ Future<bool> _uploadPendingData(Map<String, dynamic>? inputData) async {
183
+ try {
184
+ final prefs = await SharedPreferences.getInstance();
185
+ final pendingJson = prefs.getString('pending_uploads');
186
+ if (pendingJson == null) return true;
187
+
188
+ final pendingList = jsonDecode(pendingJson) as List;
189
+ final client = http.Client();
190
+ final token = prefs.getString('auth_token');
191
+
192
+ for (final item in pendingList) {
193
+ await client.post(
194
+ Uri.parse('https://api.example.com/data'),
195
+ headers: {
196
+ 'Authorization': 'Bearer $token',
197
+ 'Content-Type': 'application/json',
198
+ },
199
+ body: jsonEncode(item),
200
+ );
201
+ }
202
+
203
+ // 업로드 완료 → 대기열 비우기
204
+ await prefs.remove('pending_uploads');
205
+ client.close();
206
+ return true;
207
+ } catch (e) {
208
+ return false;
209
+ }
210
+ }
211
+
212
+ /// 캐시 정리
213
+ Future<bool> _cleanCache() async {
214
+ try {
215
+ final cacheDir = await getTemporaryDirectory();
216
+ final now = DateTime.now();
217
+
218
+ await for (final entity in cacheDir.list()) {
219
+ if (entity is File) {
220
+ final stat = await entity.stat();
221
+ // 7일 이상 된 캐시 파일 삭제
222
+ if (now.difference(stat.modified).inDays > 7) {
223
+ await entity.delete();
224
+ }
225
+ }
226
+ }
227
+ return true;
228
+ } catch (e) {
229
+ return false;
230
+ }
231
+ }
232
+ ```
233
+
234
+ ### iOS 백그라운드 설정
235
+
236
+ ```dart
237
+ // iOS는 BGTaskScheduler 기반
238
+ // Info.plist에 등록 필요:
239
+ // <key>BGTaskSchedulerPermittedIdentifiers</key>
240
+ // <array>
241
+ // <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
242
+ // </array>
243
+ //
244
+ // Runner/AppDelegate.swift에서 추가 설정 불필요 (workmanager가 자동 처리)
245
+ //
246
+ // iOS 제약사항:
247
+ // - 주기적 태스크 최소 간격: OS가 결정 (15분보다 길 수 있음)
248
+ // - 실행 시점: OS 최적화에 따라 지연 가능
249
+ // - 배터리/네트워크 상태에 따라 스킵 가능
250
+ // - Background App Refresh가 꺼져 있으면 미실행
251
+ ```
252
+
253
+ ### Isolate 직접 사용 (무거운 연산)
254
+
255
+ ```dart
256
+ /// 이미지 처리, 대량 데이터 파싱 등 CPU 집약적 태스크
257
+ Future<List<MatchSummary>> processMatchDataInBackground(
258
+ String rawJson) async {
259
+ // compute() → 별도 Isolate에서 실행 (UI 스레드 블로킹 방지)
260
+ return await compute(_parseMatchData, rawJson);
261
+ }
262
+
263
+ // top-level 또는 static 함수만 가능
264
+ List<MatchSummary> _parseMatchData(String rawJson) {
265
+ final data = jsonDecode(rawJson) as List;
266
+ return data.map((e) => MatchSummary.fromJson(e)).toList();
267
+ }
268
+
269
+ /// 장기 실행 Isolate (양방향 통신)
270
+ Future<void> startLongRunningIsolate() async {
271
+ final receivePort = ReceivePort();
272
+ await Isolate.spawn(_isolateEntryPoint, receivePort.sendPort);
273
+
274
+ receivePort.listen((message) {
275
+ if (message is SendPort) {
276
+ // 메인 → Isolate 통신용 SendPort
277
+ message.send({'action': 'process', 'data': '...'});
278
+ } else {
279
+ // Isolate → 메인 결과 수신
280
+ debugPrint('Isolate result: $message');
281
+ }
282
+ });
283
+ }
284
+
285
+ void _isolateEntryPoint(SendPort mainSendPort) {
286
+ final receivePort = ReceivePort();
287
+ mainSendPort.send(receivePort.sendPort);
288
+
289
+ receivePort.listen((message) {
290
+ // 메인에서 받은 데이터 처리
291
+ final result = _heavyComputation(message);
292
+ mainSendPort.send(result);
293
+ });
294
+ }
295
+ ```
296
+
297
+ ### 규칙
298
+
299
+ - `callbackDispatcher` → top-level 함수, `@pragma('vm:entry-point')` 필수
300
+ - 백그라운드 태스크 → Provider/Navigator/BuildContext 접근 불가
301
+ - HTTP/DB → 직접 인스턴스 생성 (앱 상태와 독립)
302
+ - 주기적 태스크 최소 간격 → 15분 (OS가 실제 실행 시점 결정)
303
+ - `constraints` → 네트워크/배터리/충전 조건 설정 (배터리 소모 방지)
304
+ - `existingWorkPolicy.keep` → 중복 등록 방지 (앱 재시작 시)
305
+ - 태스크 반환값 → `true` (성공), `false` (실패 → OS 재시도)
306
+ - 로그아웃 시 → `cancelAll()` 로 모든 백그라운드 태스크 해제
307
+ - CPU 집약적 작업 → `compute()` 또는 `Isolate.spawn` (UI 스레드 보호)
308
+ - iOS → Background App Refresh 설정에 의존, 실행 보장 없음
@@ -0,0 +1,217 @@
1
+ ---
2
+ title: Deep Linking from Notifications
3
+ impact: HIGH
4
+ impactDescription: "탭 후 홈으로만 이동 → 전환율 30% 하락, 딥링크 → 전환율 2-3x"
5
+ tags: deep-link, go-router, payload, navigation
6
+ ---
7
+
8
+ ## Deep Linking from Notifications
9
+
10
+ **Impact: HIGH (탭 후 홈으로만 이동 → 전환율 30% 하락, 딥링크 → 전환율 2-3x)**
11
+
12
+ 알림 탭 → 특정 화면 네비게이션. FCM 페이로드 → go_router 딥링크 통합.
13
+
14
+ ### 페이로드 설계
15
+
16
+ **Incorrect (구조 없는 페이로드):**
17
+ ```dart
18
+ // 서버에서 보내는 데이터
19
+ // { "data": { "screen": "match_detail_123" } }
20
+ // → 파싱 로직이 복잡해지고, 새 화면 추가 시 분기문 증가
21
+ ```
22
+
23
+ **Correct (타입 + ID 분리, 라우트 매핑):**
24
+ ```dart
25
+ // 서버에서 보내는 데이터 구조
26
+ // {
27
+ // "data": {
28
+ // "type": "match", // 알림 카테고리
29
+ // "id": "match_123", // 리소스 ID
30
+ // "action": "invite", // 세부 액션 (선택)
31
+ // "route": "/match/match_123" // 직접 라우트 (선택, 백업)
32
+ // }
33
+ // }
34
+
35
+ /// 페이로드 모델
36
+ class NotificationPayload {
37
+ final String type;
38
+ final String? id;
39
+ final String? action;
40
+ final String? route;
41
+
42
+ const NotificationPayload({
43
+ required this.type,
44
+ this.id,
45
+ this.action,
46
+ this.route,
47
+ });
48
+
49
+ factory NotificationPayload.fromMap(Map<String, dynamic> data) {
50
+ return NotificationPayload(
51
+ type: data['type'] as String? ?? 'default',
52
+ id: data['id'] as String?,
53
+ action: data['action'] as String?,
54
+ route: data['route'] as String?,
55
+ );
56
+ }
57
+
58
+ /// 페이로드 → go_router 경로 변환
59
+ String toRoute() {
60
+ // 직접 라우트가 있으면 우선 사용
61
+ if (route != null) return route!;
62
+
63
+ // 타입 + ID 기반 라우트 생성
64
+ return switch (type) {
65
+ 'match' => id != null ? '/match/$id' : '/matches',
66
+ 'chat' => id != null ? '/chat/$id' : '/chat',
67
+ 'profile' => id != null ? '/profile/$id' : '/profile',
68
+ 'announcement' => '/announcements',
69
+ 'reminder' => id != null ? '/match/$id' : '/matches',
70
+ _ => '/notifications',
71
+ };
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### go_router 통합
77
+
78
+ ```dart
79
+ /// 알림 딥링크 네비게이션 서비스
80
+ class NotificationNavigator {
81
+ final GoRouter _router;
82
+
83
+ NotificationNavigator({required GoRouter router}) : _router = router;
84
+
85
+ /// FCM RemoteMessage에서 네비게이션
86
+ void navigateFromMessage(RemoteMessage message) {
87
+ final payload = NotificationPayload.fromMap(message.data);
88
+ _navigate(payload);
89
+ }
90
+
91
+ /// flutter_local_notifications 페이로드에서 네비게이션
92
+ void navigateFromLocalPayload(String? payloadJson) {
93
+ if (payloadJson == null) return;
94
+ try {
95
+ final data = jsonDecode(payloadJson) as Map<String, dynamic>;
96
+ final payload = NotificationPayload.fromMap(data);
97
+ _navigate(payload);
98
+ } catch (e) {
99
+ debugPrint('Invalid notification payload: $e');
100
+ _router.go('/notifications');
101
+ }
102
+ }
103
+
104
+ void _navigate(NotificationPayload payload) {
105
+ final route = payload.toRoute();
106
+
107
+ // push vs go 결정:
108
+ // - go: 스택을 교체 (뒤로가기 시 홈으로)
109
+ // - push: 스택에 추가 (뒤로가기 시 이전 화면으로)
110
+ // 알림에서는 push가 일반적 (사용자가 돌아갈 수 있도록)
111
+ _router.push(route);
112
+ }
113
+ }
114
+
115
+ // Riverpod Provider
116
+ final notificationNavigatorProvider = Provider<NotificationNavigator>((ref) {
117
+ return NotificationNavigator(router: ref.watch(routerProvider));
118
+ });
119
+ ```
120
+
121
+ ### 3가지 앱 상태에서 딥링크 처리
122
+
123
+ ```dart
124
+ class NotificationDeepLinkHandler {
125
+ final NotificationNavigator _navigator;
126
+ final FirebaseMessaging _messaging = FirebaseMessaging.instance;
127
+ final FlutterLocalNotificationsPlugin _localPlugin;
128
+
129
+ NotificationDeepLinkHandler({
130
+ required NotificationNavigator navigator,
131
+ required FlutterLocalNotificationsPlugin localPlugin,
132
+ }) : _navigator = navigator,
133
+ _localPlugin = localPlugin;
134
+
135
+ Future<void> initialize() async {
136
+ // === 1. 종료 상태 (Terminated) → 알림 탭으로 앱 시작 ===
137
+ final initialMessage = await _messaging.getInitialMessage();
138
+ if (initialMessage != null) {
139
+ // 라우터 초기화 대기
140
+ await Future.delayed(const Duration(milliseconds: 800));
141
+ _navigator.navigateFromMessage(initialMessage);
142
+ }
143
+
144
+ // === 2. 백그라운드 → 알림 탭으로 앱 복귀 ===
145
+ FirebaseMessaging.onMessageOpenedApp.listen((message) {
146
+ _navigator.navigateFromMessage(message);
147
+ });
148
+
149
+ // === 3. 로컬 알림 탭 처리 ===
150
+ // flutter_local_notifications의 앱 시작 시 탭 확인
151
+ final launchDetails =
152
+ await _localPlugin.getNotificationAppLaunchDetails();
153
+ if (launchDetails?.didNotificationLaunchApp == true) {
154
+ final response = launchDetails!.notificationResponse;
155
+ if (response != null) {
156
+ await Future.delayed(const Duration(milliseconds: 800));
157
+ _navigator.navigateFromLocalPayload(response.payload);
158
+ }
159
+ }
160
+ }
161
+ }
162
+ ```
163
+
164
+ ### 인증 가드와 딥링크 조합
165
+
166
+ ```dart
167
+ // go_router redirect에서 딥링크 보존
168
+ GoRouter(
169
+ redirect: (context, state) {
170
+ final isLoggedIn = /* auth check */;
171
+ final isAuthRoute = state.matchedLocation.startsWith('/auth');
172
+
173
+ if (!isLoggedIn && !isAuthRoute) {
174
+ // 딥링크 목적지를 쿼리 파라미터로 보존
175
+ final redirectTo = state.matchedLocation;
176
+ return '/auth/login?redirect=${Uri.encodeComponent(redirectTo)}';
177
+ }
178
+ if (isLoggedIn && isAuthRoute) {
179
+ // 로그인 완료 → 원래 딥링크 목적지로 이동
180
+ final redirect = state.uri.queryParameters['redirect'];
181
+ return redirect != null ? Uri.decodeComponent(redirect) : '/';
182
+ }
183
+ return null;
184
+ },
185
+ // ...routes
186
+ );
187
+ ```
188
+
189
+ ### 알림 분석 (Analytics)
190
+
191
+ ```dart
192
+ /// 알림 탭 이벤트 추적
193
+ void _navigate(NotificationPayload payload) {
194
+ // 분석 이벤트 기록
195
+ AnalyticsService.instance.logEvent(
196
+ 'notification_opened',
197
+ parameters: {
198
+ 'type': payload.type,
199
+ 'action': payload.action ?? 'tap',
200
+ 'source': 'push',
201
+ },
202
+ );
203
+
204
+ final route = payload.toRoute();
205
+ _router.push(route);
206
+ }
207
+ ```
208
+
209
+ ### 규칙
210
+
211
+ - 페이로드 구조 → `type` + `id` + `action` 표준화, 서버와 합의
212
+ - `toRoute()` → 중앙 라우트 매핑 (분산 switch 문 방지)
213
+ - 3가지 상태 모두 처리: `getInitialMessage` + `onMessageOpenedApp` + 로컬 알림 launch
214
+ - `getInitialMessage()` → cold start 시 라우터 초기화 대기 후 네비게이션
215
+ - 인증 가드 → 딥링크 목적지 보존 (`?redirect=` 쿼리 파라미터)
216
+ - 알림 탭 시 `push` 사용 (스택 추가 → 뒤로가기 가능)
217
+ - 알림 탭 분석 이벤트 필수 (열림률, 전환율 측정)
@@ -0,0 +1,164 @@
1
+ ---
2
+ title: FCM Setup & Token Management
3
+ impact: CRITICAL
4
+ impactDescription: "알림 미수신 → 사용자 이탈, 토큰 미갱신 → 전달률 하락"
5
+ tags: fcm, firebase, token, setup
6
+ ---
7
+
8
+ ## FCM Setup & Token Management
9
+
10
+ **Impact: CRITICAL (알림 미수신 → 사용자 이탈, 토큰 미갱신 → 전달률 하락)**
11
+
12
+ FCM 초기화, 토큰 관리, 서버 동기화. 앱 시작 시 반드시 설정해야 하는 핵심 인프라.
13
+
14
+ ### 의존성
15
+
16
+ ```yaml
17
+ # pubspec.yaml
18
+ dependencies:
19
+ firebase_core: ^3.8.0
20
+ firebase_messaging: ^15.1.0
21
+ flutter_local_notifications: ^18.0.0
22
+ ```
23
+
24
+ ### 초기화
25
+
26
+ **Incorrect (순서 무시, 백그라운드 핸들러 미등록):**
27
+ ```dart
28
+ void main() {
29
+ runApp(const MyApp());
30
+ // Firebase 초기화 없이 FCM 사용 → 크래시
31
+ }
32
+ ```
33
+
34
+ **Correct (완전한 초기화 순서):**
35
+ ```dart
36
+ // top-level 함수 — 클래스 멤버/클로저 불가
37
+ @pragma('vm:entry-point')
38
+ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
39
+ await Firebase.initializeApp();
40
+ // 백그라운드 메시지 처리 (DB 저장, 로컬 알림 등)
41
+ // 주의: 여기서 UI 코드, Navigator, BuildContext 접근 불가
42
+ debugPrint('Background message: ${message.messageId}');
43
+ }
44
+
45
+ Future<void> main() async {
46
+ WidgetsFlutterBinding.ensureInitialized();
47
+
48
+ // 1. Firebase 초기화
49
+ await Firebase.initializeApp();
50
+
51
+ // 2. 백그라운드 메시지 핸들러 등록 (main 에서 1회)
52
+ FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
53
+
54
+ // 3. 로컬 알림 초기화 (포그라운드 표시용)
55
+ await NotificationService.instance.initialize();
56
+
57
+ runApp(const ProviderScope(child: MyApp()));
58
+ }
59
+ ```
60
+
61
+ ### FCM 토큰 관리
62
+
63
+ **Incorrect (토큰 갱신 미처리):**
64
+ ```dart
65
+ // 앱 시작 시 토큰 1번만 가져오고 끝
66
+ final token = await FirebaseMessaging.instance.getToken();
67
+ await sendTokenToServer(token!);
68
+ // → 토큰 갱신되면 서버에 잘못된 토큰 → 알림 전달 실패
69
+ ```
70
+
71
+ **Correct (토큰 갱신 구독 + 서버 동기화):**
72
+ ```dart
73
+ class FcmTokenManager {
74
+ final FirebaseMessaging _messaging = FirebaseMessaging.instance;
75
+ final AuthRepository _authRepo;
76
+
77
+ FcmTokenManager({required AuthRepository authRepo}) : _authRepo = authRepo;
78
+
79
+ /// 초기 토큰 등록 + 갱신 구독
80
+ Future<void> initialize() async {
81
+ // 초기 토큰
82
+ final token = await _messaging.getToken();
83
+ if (token != null) {
84
+ await _syncTokenToServer(token);
85
+ }
86
+
87
+ // 토큰 갱신 구독 (앱 재설치, OS 토큰 리프레시 등)
88
+ _messaging.onTokenRefresh.listen(_syncTokenToServer);
89
+ }
90
+
91
+ Future<void> _syncTokenToServer(String token) async {
92
+ try {
93
+ await _authRepo.updateFcmToken(token);
94
+ } catch (e) {
95
+ // 실패 시 로컬 저장 → 다음 앱 시작 시 재시도
96
+ await _saveTokenLocally(token);
97
+ }
98
+ }
99
+
100
+ /// 로그아웃 시 토큰 해제
101
+ Future<void> deleteToken() async {
102
+ await _messaging.deleteToken();
103
+ await _authRepo.removeFcmToken();
104
+ }
105
+
106
+ Future<void> _saveTokenLocally(String token) async {
107
+ // SharedPreferences 등에 저장, 다음 시작 시 재동기화
108
+ }
109
+ }
110
+
111
+ // Riverpod Provider
112
+ final fcmTokenManagerProvider = Provider<FcmTokenManager>((ref) {
113
+ return FcmTokenManager(authRepo: ref.watch(authRepositoryProvider));
114
+ });
115
+ ```
116
+
117
+ ### iOS APNs 토큰 (선택)
118
+
119
+ ```dart
120
+ // iOS에서 APNs 토큰 직접 필요한 경우 (서버에서 APNs 직접 발송 시)
121
+ final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
122
+ // 대부분의 경우 FCM이 APNs 토큰을 자동 관리하므로 직접 사용 불필요
123
+ ```
124
+
125
+ ### 토픽 구독
126
+
127
+ ```dart
128
+ class TopicManager {
129
+ final FirebaseMessaging _messaging = FirebaseMessaging.instance;
130
+
131
+ /// 사용자 관심사 기반 토픽 구독
132
+ Future<void> subscribeToUserTopics(UserPreferences prefs) async {
133
+ // 전체 공지
134
+ await _messaging.subscribeToTopic('announcements');
135
+
136
+ // 관심 스포츠
137
+ for (final sport in prefs.favoriteSports) {
138
+ await _messaging.subscribeToTopic('sport_${sport.name}');
139
+ }
140
+
141
+ // 지역
142
+ if (prefs.region != null) {
143
+ await _messaging.subscribeToTopic('region_${prefs.region}');
144
+ }
145
+ }
146
+
147
+ /// 로그아웃 시 토픽 해제
148
+ Future<void> unsubscribeAll(UserPreferences prefs) async {
149
+ await _messaging.unsubscribeFromTopic('announcements');
150
+ for (final sport in prefs.favoriteSports) {
151
+ await _messaging.unsubscribeFromTopic('sport_${sport.name}');
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ ### 규칙
158
+
159
+ - `Firebase.initializeApp()` → `WidgetsFlutterBinding.ensureInitialized()` 직후
160
+ - `onBackgroundMessage` → `main()` 에서 등록, top-level 함수만 가능
161
+ - `@pragma('vm:entry-point')` → 백그라운드 핸들러에 필수 (트리쉐이킹 방지)
162
+ - FCM 토큰 → 서버 동기화 필수 + `onTokenRefresh` 구독
163
+ - 로그아웃 시 `deleteToken()` + 서버에서 토큰 제거
164
+ - 토픽 구독 → 사용자 설정 변경 시 구독/해제 동기화