timsquad 3.6.0 → 3.7.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 (431) hide show
  1. package/README.ko.md +103 -111
  2. package/README.md +100 -108
  3. package/dist/commands/init.js +4 -8
  4. package/dist/commands/init.js.map +1 -1
  5. package/dist/commands/{upgrade.d.ts → update.d.ts} +3 -3
  6. package/dist/commands/update.d.ts.map +1 -0
  7. package/dist/commands/{upgrade.js → update.js} +12 -17
  8. package/dist/commands/update.js.map +1 -0
  9. package/dist/daemon/event-queue.d.ts +3 -11
  10. package/dist/daemon/event-queue.d.ts.map +1 -1
  11. package/dist/daemon/event-queue.js +62 -203
  12. package/dist/daemon/event-queue.js.map +1 -1
  13. package/dist/daemon/index.d.ts +1 -1
  14. package/dist/daemon/index.d.ts.map +1 -1
  15. package/dist/daemon/index.js +15 -14
  16. package/dist/daemon/index.js.map +1 -1
  17. package/dist/index.js +3 -43
  18. package/dist/index.js.map +1 -1
  19. package/dist/lib/agent-generator.d.ts.map +1 -1
  20. package/dist/lib/agent-generator.js +10 -10
  21. package/dist/lib/agent-generator.js.map +1 -1
  22. package/dist/lib/compile-rules.d.ts.map +1 -1
  23. package/dist/lib/compile-rules.js +37 -4
  24. package/dist/lib/compile-rules.js.map +1 -1
  25. package/dist/lib/compiler.d.ts +1 -0
  26. package/dist/lib/compiler.d.ts.map +1 -1
  27. package/dist/lib/compiler.js +67 -11
  28. package/dist/lib/compiler.js.map +1 -1
  29. package/dist/{commands/log.d.ts → lib/log-utils.d.ts} +7 -15
  30. package/dist/lib/log-utils.d.ts.map +1 -0
  31. package/dist/lib/log-utils.js +347 -0
  32. package/dist/lib/log-utils.js.map +1 -0
  33. package/dist/lib/skill-generator.d.ts.map +1 -1
  34. package/dist/lib/skill-generator.js +4 -44
  35. package/dist/lib/skill-generator.js.map +1 -1
  36. package/dist/lib/ssot-map.d.ts.map +1 -1
  37. package/dist/lib/ssot-map.js +3 -0
  38. package/dist/lib/ssot-map.js.map +1 -1
  39. package/dist/lib/template.d.ts +10 -3
  40. package/dist/lib/template.d.ts.map +1 -1
  41. package/dist/lib/template.js +136 -22
  42. package/dist/lib/template.js.map +1 -1
  43. package/dist/lib/upgrade-backup.js +1 -1
  44. package/dist/lib/upgrade-backup.js.map +1 -1
  45. package/dist/types/config.d.ts.map +1 -1
  46. package/dist/types/config.js +34 -34
  47. package/dist/types/config.js.map +1 -1
  48. package/dist/types/feedback.d.ts +1 -54
  49. package/dist/types/feedback.d.ts.map +1 -1
  50. package/dist/types/feedback.js +1 -22
  51. package/dist/types/feedback.js.map +1 -1
  52. package/dist/types/project.d.ts +5 -0
  53. package/dist/types/project.d.ts.map +1 -1
  54. package/dist/types/project.js +15 -0
  55. package/dist/types/project.js.map +1 -1
  56. package/dist/types/ssot-map.d.ts +2 -0
  57. package/dist/types/ssot-map.d.ts.map +1 -1
  58. package/package.json +1 -1
  59. package/templates/base/agents/base/tsq-architect.md +1 -1
  60. package/templates/base/agents/base/tsq-dba.md +3 -1
  61. package/templates/base/agents/base/tsq-designer.md +3 -1
  62. package/templates/base/agents/base/tsq-developer.md +3 -1
  63. package/templates/base/agents/base/tsq-librarian.md +1 -1
  64. package/templates/base/agents/base/tsq-qa.md +3 -1
  65. package/templates/base/agents/base/tsq-security.md +3 -1
  66. package/templates/base/agents/overlays/platform/claude-code.md +2 -2
  67. package/templates/base/config.template.yaml +17 -28
  68. package/templates/base/knowledge/templates/task-result.md +5 -10
  69. package/templates/base/skills/_template/SKILL.md +1 -3
  70. package/templates/base/skills/{architecture → tsq-architecture}/SKILL.md +2 -2
  71. package/templates/base/skills/tsq-audit/SKILL.md +74 -0
  72. package/templates/base/skills/{methodology/bdd → tsq-bdd}/SKILL.md +14 -9
  73. package/templates/base/skills/{coding → tsq-coding}/SKILL.md +2 -4
  74. package/templates/base/skills/tsq-controller/SKILL.md +81 -0
  75. package/templates/base/skills/{mobile/dart → tsq-dart}/SKILL.md +5 -3
  76. package/templates/base/skills/{database → tsq-database}/SKILL.md +5 -2
  77. package/templates/base/skills/{methodology/ddd → tsq-ddd}/SKILL.md +15 -10
  78. package/templates/base/skills/{methodology/debugging → tsq-debugging}/SKILL.md +2 -2
  79. package/templates/base/skills/tsq-decompose/SKILL.md +117 -0
  80. package/templates/base/skills/tsq-delete/SKILL.md +72 -0
  81. package/templates/base/skills/{mobile/flutter → tsq-flutter}/SKILL.md +6 -3
  82. package/templates/base/skills/tsq-grill/SKILL.md +86 -0
  83. package/templates/base/skills/{backend/node → tsq-hono}/SKILL.md +6 -4
  84. package/templates/base/skills/tsq-librarian/SKILL.md +78 -0
  85. package/templates/base/skills/tsq-log/SKILL.md +30 -0
  86. package/templates/base/skills/{frontend/nextjs → tsq-nextjs}/SKILL.md +14 -9
  87. package/templates/base/skills/{planning → tsq-planning}/SKILL.md +2 -2
  88. package/templates/base/skills/{database/prisma → tsq-prisma}/SKILL.md +15 -9
  89. package/templates/base/skills/{product-audit → tsq-product-audit}/SKILL.md +2 -4
  90. package/templates/base/skills/{prompt-engineering → tsq-prompt}/SKILL.md +6 -4
  91. package/templates/base/skills/tsq-protocol/SKILL.md +85 -33
  92. package/templates/base/skills/{frontend/react → tsq-react}/SKILL.md +6 -3
  93. package/templates/base/skills/tsq-retro/SKILL.md +86 -0
  94. package/templates/base/skills/tsq-retro/references/feedback-guide.md +58 -0
  95. package/templates/base/skills/tsq-retro/references/improve-protocol.md +87 -0
  96. package/templates/base/skills/{security → tsq-security}/SKILL.md +2 -4
  97. package/templates/base/skills/{spec → tsq-spec}/SKILL.md +4 -6
  98. package/templates/base/skills/{stability-verification → tsq-stability}/SKILL.md +3 -3
  99. package/templates/base/skills/tsq-start/SKILL.md +90 -0
  100. package/templates/base/skills/tsq-start/references/onboarding-questions.md +177 -0
  101. package/templates/base/skills/tsq-status/SKILL.md +32 -0
  102. package/templates/base/skills/{methodology/tdd → tsq-tdd}/SKILL.md +6 -3
  103. package/templates/base/skills/{testing → tsq-testing}/SKILL.md +5 -7
  104. package/templates/base/skills/{typescript → tsq-typescript}/SKILL.md +2 -2
  105. package/templates/base/skills/{ui-design → tsq-ui}/SKILL.md +2 -2
  106. package/templates/base/skills/tsq-update/SKILL.md +48 -0
  107. package/templates/base/timsquad/constraints/competency-framework.xml +2 -2
  108. package/templates/base/timsquad/constraints/ssot-schema.xml +2 -2
  109. package/templates/base/timsquad/process/phase-checklist.yaml +1 -1
  110. package/templates/base/timsquad/process/state-machine.xml +2 -2
  111. package/templates/base/timsquad/process/validation-rules.xml +8 -8
  112. package/templates/base/timsquad/process/workflow-base.xml +8 -8
  113. package/templates/base/timsquad/retrospective/cycle-report.template.md +2 -2
  114. package/templates/base/timsquad/retrospective/patterns/failure-patterns.md +1 -1
  115. package/templates/base/timsquad/retrospective/patterns/success-patterns.md +2 -2
  116. package/templates/base/timsquad/retrospective/retrospective-state.xml +2 -2
  117. package/templates/base/timsquad/ssot/audit-trail-spec.template.md +155 -0
  118. package/templates/base/timsquad/ssot/compliance-matrix.template.md +105 -0
  119. package/templates/base/timsquad/ssot/component-map.template.md +181 -0
  120. package/templates/base/timsquad/ssot/data-design.template.md +4 -4
  121. package/templates/base/timsquad/ssot/deployment-spec.template.md +29 -22
  122. package/templates/base/timsquad/ssot/env-config.template.md +4 -2
  123. package/templates/base/timsquad/ssot/error-codes.template.md +3 -3
  124. package/templates/base/timsquad/ssot/functional-spec.template.md +40 -3
  125. package/templates/base/timsquad/ssot/glossary.template.md +2 -2
  126. package/templates/base/timsquad/ssot/infra-topology.template.md +191 -0
  127. package/templates/base/timsquad/ssot/integration-spec.template.md +2 -2
  128. package/templates/base/timsquad/ssot/monitoring-spec.template.md +185 -0
  129. package/templates/base/timsquad/ssot/navigation-map.template.md +154 -0
  130. package/templates/base/timsquad/ssot/performance-budget.template.md +132 -0
  131. package/templates/base/timsquad/ssot/planning.template.md +3 -3
  132. package/templates/base/timsquad/ssot/prd/_template.md +73 -0
  133. package/templates/base/timsquad/ssot/prd.template.md +10 -21
  134. package/templates/base/timsquad/ssot/requirements.template.md +3 -3
  135. package/templates/base/timsquad/ssot/sdk-spec.template.md +223 -0
  136. package/templates/base/timsquad/ssot/service-spec.template.md +3 -3
  137. package/templates/base/timsquad/ssot/state-machine.template.md +127 -0
  138. package/templates/base/timsquad/ssot/ui-ux-spec.template.md +43 -3
  139. package/templates/base/timsquad/ssot-map.template.yaml +28 -0
  140. package/templates/base/timsquad/state/workspace.xml +11 -11
  141. package/templates/platforms/claude-code/rules/adr-rules.md +1 -1
  142. package/templates/platforms/claude-code/rules/build-gate.md +1 -1
  143. package/templates/platforms/claude-code/rules/completion-verification.md +0 -2
  144. package/templates/platforms/claude-code/rules/context-monitor.md +1 -1
  145. package/templates/platforms/claude-code/rules/feedback-routing.md +2 -2
  146. package/templates/platforms/claude-code/rules/phase-management.md +2 -2
  147. package/templates/platforms/claude-code/rules/plan-review.md +2 -2
  148. package/templates/platforms/claude-code/rules/quality-guards.md +0 -2
  149. package/templates/platforms/claude-code/rules/sequence-management.md +15 -15
  150. package/templates/platforms/claude-code/rules/session-notes.md +1 -1
  151. package/templates/platforms/claude-code/rules/workspace-sync.md +1 -1
  152. package/templates/platforms/claude-code/scripts/build-gate.sh +6 -1
  153. package/templates/platforms/claude-code/scripts/change-scope-guard.sh +1 -4
  154. package/templates/platforms/claude-code/scripts/check-capability.sh +68 -0
  155. package/templates/platforms/claude-code/scripts/completion-guard.sh +62 -4
  156. package/templates/platforms/claude-code/scripts/context-restore.sh +33 -6
  157. package/templates/platforms/claude-code/scripts/phase-guard.sh +2 -5
  158. package/templates/platforms/claude-code/scripts/pre-compact.sh +3 -3
  159. package/templates/platforms/claude-code/scripts/safe-guard.sh +2 -5
  160. package/templates/platforms/claude-code/scripts/subagent-start.sh +11 -0
  161. package/templates/platforms/claude-code/scripts/subagent-stop.sh +11 -0
  162. package/templates/platforms/claude-code/settings.json +20 -74
  163. package/templates/project-types/api-backend/config.yaml +9 -5
  164. package/templates/project-types/api-backend/process/workflow.xml +2 -2
  165. package/templates/project-types/fintech/config.yaml +13 -19
  166. package/templates/project-types/fintech/ssot/audit-trail-spec.template.md +207 -0
  167. package/templates/project-types/fintech/ssot/compliance-matrix.template.md +187 -0
  168. package/templates/project-types/infra/config.yaml +7 -4
  169. package/templates/project-types/infra/process/workflow.xml +3 -3
  170. package/templates/project-types/mobile-app/config.yaml +8 -14
  171. package/templates/project-types/mobile-app/process/workflow.xml +4 -4
  172. package/templates/project-types/platform/config.yaml +8 -5
  173. package/templates/project-types/platform/process/workflow.xml +3 -3
  174. package/templates/project-types/web-app/config.yaml +9 -15
  175. package/templates/project-types/web-app/process/workflow.xml +6 -6
  176. package/templates/project-types/web-service/config.yaml +10 -19
  177. package/templates/project-types/web-service/process/workflow.xml +6 -6
  178. package/dist/commands/audit.d.ts +0 -22
  179. package/dist/commands/audit.d.ts.map +0 -1
  180. package/dist/commands/audit.js +0 -233
  181. package/dist/commands/audit.js.map +0 -1
  182. package/dist/commands/compile.d.ts +0 -3
  183. package/dist/commands/compile.d.ts.map +0 -1
  184. package/dist/commands/compile.js +0 -251
  185. package/dist/commands/compile.js.map +0 -1
  186. package/dist/commands/feedback.d.ts +0 -12
  187. package/dist/commands/feedback.d.ts.map +0 -1
  188. package/dist/commands/feedback.js +0 -382
  189. package/dist/commands/feedback.js.map +0 -1
  190. package/dist/commands/full.d.ts +0 -3
  191. package/dist/commands/full.d.ts.map +0 -1
  192. package/dist/commands/full.js +0 -88
  193. package/dist/commands/full.js.map +0 -1
  194. package/dist/commands/git/commit.d.ts +0 -3
  195. package/dist/commands/git/commit.d.ts.map +0 -1
  196. package/dist/commands/git/commit.js +0 -85
  197. package/dist/commands/git/commit.js.map +0 -1
  198. package/dist/commands/git/index.d.ts +0 -5
  199. package/dist/commands/git/index.d.ts.map +0 -1
  200. package/dist/commands/git/index.js +0 -5
  201. package/dist/commands/git/index.js.map +0 -1
  202. package/dist/commands/git/pr.d.ts +0 -3
  203. package/dist/commands/git/pr.d.ts.map +0 -1
  204. package/dist/commands/git/pr.js +0 -139
  205. package/dist/commands/git/pr.js.map +0 -1
  206. package/dist/commands/git/release.d.ts +0 -3
  207. package/dist/commands/git/release.d.ts.map +0 -1
  208. package/dist/commands/git/release.js +0 -153
  209. package/dist/commands/git/release.js.map +0 -1
  210. package/dist/commands/git/sync.d.ts +0 -3
  211. package/dist/commands/git/sync.d.ts.map +0 -1
  212. package/dist/commands/git/sync.js +0 -132
  213. package/dist/commands/git/sync.js.map +0 -1
  214. package/dist/commands/improve.d.ts +0 -3
  215. package/dist/commands/improve.d.ts.map +0 -1
  216. package/dist/commands/improve.js +0 -286
  217. package/dist/commands/improve.js.map +0 -1
  218. package/dist/commands/knowledge.d.ts +0 -3
  219. package/dist/commands/knowledge.d.ts.map +0 -1
  220. package/dist/commands/knowledge.js +0 -316
  221. package/dist/commands/knowledge.js.map +0 -1
  222. package/dist/commands/log.d.ts.map +0 -1
  223. package/dist/commands/log.js +0 -1468
  224. package/dist/commands/log.js.map +0 -1
  225. package/dist/commands/meta-index.d.ts +0 -3
  226. package/dist/commands/meta-index.d.ts.map +0 -1
  227. package/dist/commands/meta-index.js +0 -431
  228. package/dist/commands/meta-index.js.map +0 -1
  229. package/dist/commands/metrics.d.ts +0 -3
  230. package/dist/commands/metrics.d.ts.map +0 -1
  231. package/dist/commands/metrics.js +0 -843
  232. package/dist/commands/metrics.js.map +0 -1
  233. package/dist/commands/quick.d.ts +0 -3
  234. package/dist/commands/quick.d.ts.map +0 -1
  235. package/dist/commands/quick.js +0 -136
  236. package/dist/commands/quick.js.map +0 -1
  237. package/dist/commands/retro.d.ts +0 -3
  238. package/dist/commands/retro.d.ts.map +0 -1
  239. package/dist/commands/retro.js +0 -885
  240. package/dist/commands/retro.js.map +0 -1
  241. package/dist/commands/session.d.ts +0 -3
  242. package/dist/commands/session.d.ts.map +0 -1
  243. package/dist/commands/session.js +0 -346
  244. package/dist/commands/session.js.map +0 -1
  245. package/dist/commands/skills.d.ts +0 -12
  246. package/dist/commands/skills.d.ts.map +0 -1
  247. package/dist/commands/skills.js +0 -228
  248. package/dist/commands/skills.js.map +0 -1
  249. package/dist/commands/status.d.ts +0 -3
  250. package/dist/commands/status.d.ts.map +0 -1
  251. package/dist/commands/status.js +0 -127
  252. package/dist/commands/status.js.map +0 -1
  253. package/dist/commands/upgrade.d.ts.map +0 -1
  254. package/dist/commands/upgrade.js.map +0 -1
  255. package/dist/commands/watch.d.ts +0 -3
  256. package/dist/commands/watch.d.ts.map +0 -1
  257. package/dist/commands/watch.js +0 -213
  258. package/dist/commands/watch.js.map +0 -1
  259. package/dist/commands/workflow.d.ts +0 -5
  260. package/dist/commands/workflow.d.ts.map +0 -1
  261. package/dist/commands/workflow.js +0 -781
  262. package/dist/commands/workflow.js.map +0 -1
  263. package/templates/base/skills/audit/SKILL.md +0 -66
  264. package/templates/base/skills/controller/SKILL.md +0 -77
  265. package/templates/base/skills/controller/delegation/developer.md +0 -25
  266. package/templates/base/skills/controller/delegation/librarian.md +0 -33
  267. package/templates/base/skills/controller/delegation/reviewer.md +0 -19
  268. package/templates/base/skills/controller/triggers/phase-complete.md +0 -25
  269. package/templates/base/skills/controller/triggers/sequence-complete.md +0 -15
  270. package/templates/base/skills/controller/triggers/ssot-changed.md +0 -14
  271. package/templates/base/skills/controller/triggers/task-complete.md +0 -14
  272. package/templates/base/skills/librarian/SKILL.md +0 -53
  273. package/templates/base/skills/main-session-constraints/SKILL.md +0 -62
  274. package/templates/base/skills/retrospective/SKILL.md +0 -77
  275. package/templates/base/skills/review/SKILL.md +0 -72
  276. package/templates/base/skills/tsq-cli/SKILL.md +0 -73
  277. package/templates/base/skills/tsq-cli/references/cli-reference.md +0 -92
  278. package/templates/base/timsquad/feedback/feedback-router.sh +0 -341
  279. package/templates/base/timsquad/feedback/routing-rules.yaml +0 -352
  280. package/templates/platforms/claude-code/CLAUDE.md.template +0 -89
  281. package/templates/platforms/claude-code/rules/skill-suggest.md +0 -27
  282. package/templates/platforms/claude-code/scripts/skill-inject.sh +0 -216
  283. package/templates/platforms/claude-code/scripts/skill-rules.json +0 -95
  284. package/templates/platforms/claude-code/scripts/skill-suggest.sh +0 -105
  285. package/templates/platforms/claude-code/scripts/subagent-inject.sh +0 -53
  286. /package/templates/base/skills/{architecture → tsq-architecture}/references/adr-template.md +0 -0
  287. /package/templates/base/skills/{architecture → tsq-architecture}/references/api-design.md +0 -0
  288. /package/templates/base/skills/{methodology/bdd → tsq-bdd}/rules/gherkin-patterns.md +0 -0
  289. /package/templates/base/skills/{coding → tsq-coding}/rules/async-patterns.md +0 -0
  290. /package/templates/base/skills/{coding → tsq-coding}/rules/code-organization.md +0 -0
  291. /package/templates/base/skills/{coding → tsq-coding}/rules/error-handling.md +0 -0
  292. /package/templates/base/skills/{coding → tsq-coding}/rules/patterns.md +0 -0
  293. /package/templates/base/skills/{coding → tsq-coding}/rules/type-safety.md +0 -0
  294. /package/templates/base/skills/{controller → tsq-controller}/memory/.gitkeep +0 -0
  295. /package/templates/base/skills/{controller → tsq-controller}/references/README.md +0 -0
  296. /package/templates/base/skills/{controller → tsq-controller}/rules/README.md +0 -0
  297. /package/templates/base/skills/{mobile/dart → tsq-dart}/rules/async-patterns.md +0 -0
  298. /package/templates/base/skills/{mobile/dart → tsq-dart}/rules/code-style.md +0 -0
  299. /package/templates/base/skills/{mobile/dart → tsq-dart}/rules/null-safety.md +0 -0
  300. /package/templates/base/skills/{mobile/dart → tsq-dart}/rules/type-system.md +0 -0
  301. /package/templates/base/skills/{database → tsq-database}/rules/query-optimization.md +0 -0
  302. /package/templates/base/skills/{database → tsq-database}/rules/supabase-patterns.md +0 -0
  303. /package/templates/base/skills/{methodology/ddd → tsq-ddd}/rules/strategic-patterns.md +0 -0
  304. /package/templates/base/skills/{methodology/debugging → tsq-debugging}/references/root-cause-tracing.md +0 -0
  305. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/SKILL.md +0 -0
  306. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/references/ci-cd-pipeline.md +0 -0
  307. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/code-signing.md +0 -0
  308. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/codemagic-setup.md +0 -0
  309. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/fastlane-setup.md +0 -0
  310. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/github-actions.md +0 -0
  311. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/store-deployment.md +0 -0
  312. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/versioning.md +0 -0
  313. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/SKILL.md +0 -0
  314. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/references/i18n-architecture.md +0 -0
  315. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/rules/arb-files.md +0 -0
  316. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/rules/locale-switching.md +0 -0
  317. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/rules/localization-setup.md +0 -0
  318. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/rules/plural-gender.md +0 -0
  319. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/rules/text-direction.md +0 -0
  320. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/SKILL.md +0 -0
  321. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/references/monitoring-architecture.md +0 -0
  322. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/rules/analytics.md +0 -0
  323. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/rules/crashlytics-setup.md +0 -0
  324. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/rules/logging.md +0 -0
  325. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/rules/performance-monitoring.md +0 -0
  326. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/rules/sentry-integration.md +0 -0
  327. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/SKILL.md +0 -0
  328. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/references/api-client-architecture.md +0 -0
  329. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/caching.md +0 -0
  330. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/connectivity.md +0 -0
  331. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/dio-setup.md +0 -0
  332. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/error-handling.md +0 -0
  333. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/interceptors.md +0 -0
  334. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/retrofit-patterns.md +0 -0
  335. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/SKILL.md +0 -0
  336. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/references/notification-architecture.md +0 -0
  337. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/references/platform-setup.md +0 -0
  338. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/background-processing.md +0 -0
  339. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/deep-linking.md +0 -0
  340. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/fcm-setup.md +0 -0
  341. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/local-notifications.md +0 -0
  342. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/notification-handling.md +0 -0
  343. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/notification-permissions.md +0 -0
  344. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/rich-notifications.md +0 -0
  345. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/references/freezed-patterns.md +0 -0
  346. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/references/project-structure.md +0 -0
  347. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/animations.md +0 -0
  348. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/architecture.md +0 -0
  349. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/navigation-routing.md +0 -0
  350. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/performance.md +0 -0
  351. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/platform-adaptive.md +0 -0
  352. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/state-management.md +0 -0
  353. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/testing.md +0 -0
  354. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/widget-conventions.md +0 -0
  355. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/SKILL.md +0 -0
  356. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/references/mobile-security-checklist.md +0 -0
  357. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/api-key-protection.md +0 -0
  358. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/authentication.md +0 -0
  359. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/data-protection.md +0 -0
  360. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/obfuscation.md +0 -0
  361. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/secure-storage.md +0 -0
  362. /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/ssl-pinning.md +0 -0
  363. /package/templates/base/skills/{backend/node → tsq-hono}/rules/async-patterns.md +0 -0
  364. /package/templates/base/skills/{backend/node → tsq-hono}/rules/deployment.md +0 -0
  365. /package/templates/base/skills/{backend/node → tsq-hono}/rules/env-config.md +0 -0
  366. /package/templates/base/skills/{backend/node → tsq-hono}/rules/error-handling.md +0 -0
  367. /package/templates/base/skills/{backend/node → tsq-hono}/rules/hono-app-setup.md +0 -0
  368. /package/templates/base/skills/{backend/node → tsq-hono}/rules/jwt-auth.md +0 -0
  369. /package/templates/base/skills/{backend/node → tsq-hono}/rules/middleware.md +0 -0
  370. /package/templates/base/skills/{backend/node → tsq-hono}/rules/testing.md +0 -0
  371. /package/templates/base/skills/{frontend/nextjs → tsq-nextjs}/rules/app-router.md +0 -0
  372. /package/templates/base/skills/{planning → tsq-planning}/references/prd-guide.md +0 -0
  373. /package/templates/base/skills/{planning → tsq-planning}/references/requirements-guide.md +0 -0
  374. /package/templates/base/skills/{database/prisma → tsq-prisma}/rules/queries.md +0 -0
  375. /package/templates/base/skills/{database/prisma → tsq-prisma}/rules/schema-design.md +0 -0
  376. /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/01-security.md +0 -0
  377. /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/02-performance.md +0 -0
  378. /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/03-seo.md +0 -0
  379. /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/04-accessibility.md +0 -0
  380. /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/05-ui-ux.md +0 -0
  381. /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/06-architecture.md +0 -0
  382. /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/07-functional-requirements.md +0 -0
  383. /package/templates/base/skills/{product-audit → tsq-product-audit}/rules/audit-protocol.md +0 -0
  384. /package/templates/base/skills/{product-audit → tsq-product-audit}/rules/false-positive-guard.md +0 -0
  385. /package/templates/base/skills/{product-audit → tsq-product-audit}/rules/scoring-criteria.md +0 -0
  386. /package/templates/base/skills/{product-audit → tsq-product-audit}/templates/improvement-plan-template.md +0 -0
  387. /package/templates/base/skills/{product-audit → tsq-product-audit}/templates/report-template.md +0 -0
  388. /package/templates/base/skills/{frontend/react → tsq-react}/rules/_sections.md +0 -0
  389. /package/templates/base/skills/{frontend/react → tsq-react}/rules/anti-patterns.md +0 -0
  390. /package/templates/base/skills/{frontend/react → tsq-react}/rules/async-api-routes.md +0 -0
  391. /package/templates/base/skills/{frontend/react → tsq-react}/rules/async-defer-await.md +0 -0
  392. /package/templates/base/skills/{frontend/react → tsq-react}/rules/async-dependencies.md +0 -0
  393. /package/templates/base/skills/{frontend/react → tsq-react}/rules/async-parallel.md +0 -0
  394. /package/templates/base/skills/{frontend/react → tsq-react}/rules/async-suspense-boundaries.md +0 -0
  395. /package/templates/base/skills/{frontend/react → tsq-react}/rules/bundle-barrel-imports.md +0 -0
  396. /package/templates/base/skills/{frontend/react → tsq-react}/rules/bundle-defer-third-party.md +0 -0
  397. /package/templates/base/skills/{frontend/react → tsq-react}/rules/bundle-dynamic-imports.md +0 -0
  398. /package/templates/base/skills/{frontend/react → tsq-react}/rules/component-conventions.md +0 -0
  399. /package/templates/base/skills/{frontend/react → tsq-react}/rules/js-combine-iterations.md +0 -0
  400. /package/templates/base/skills/{frontend/react → tsq-react}/rules/js-early-exit.md +0 -0
  401. /package/templates/base/skills/{frontend/react → tsq-react}/rules/js-index-maps.md +0 -0
  402. /package/templates/base/skills/{frontend/react → tsq-react}/rules/js-set-map-lookups.md +0 -0
  403. /package/templates/base/skills/{frontend/react → tsq-react}/rules/rendering-conditional-render.md +0 -0
  404. /package/templates/base/skills/{frontend/react → tsq-react}/rules/rendering-content-visibility.md +0 -0
  405. /package/templates/base/skills/{frontend/react → tsq-react}/rules/rendering-hoist-jsx.md +0 -0
  406. /package/templates/base/skills/{frontend/react → tsq-react}/rules/rerender-defer-reads.md +0 -0
  407. /package/templates/base/skills/{frontend/react → tsq-react}/rules/rerender-derived-state.md +0 -0
  408. /package/templates/base/skills/{frontend/react → tsq-react}/rules/rerender-memo.md +0 -0
  409. /package/templates/base/skills/{frontend/react → tsq-react}/rules/rerender-transitions.md +0 -0
  410. /package/templates/base/skills/{frontend/react → tsq-react}/rules/server-after-nonblocking.md +0 -0
  411. /package/templates/base/skills/{frontend/react → tsq-react}/rules/server-cache-react.md +0 -0
  412. /package/templates/base/skills/{frontend/react → tsq-react}/rules/server-parallel-fetching.md +0 -0
  413. /package/templates/base/skills/{frontend/react → tsq-react}/rules/state-location.md +0 -0
  414. /package/templates/base/skills/{retrospective → tsq-retro}/references/improvement-template.md +0 -0
  415. /package/templates/base/skills/{security → tsq-security}/rules/auth-patterns.md +0 -0
  416. /package/templates/base/skills/{security → tsq-security}/rules/dependency-security.md +0 -0
  417. /package/templates/base/skills/{security → tsq-security}/rules/input-validation.md +0 -0
  418. /package/templates/base/skills/{security → tsq-security}/rules/owasp-examples.md +0 -0
  419. /package/templates/base/skills/{security → tsq-security}/rules/secrets-management.md +0 -0
  420. /package/templates/base/skills/{security → tsq-security}/scripts/check-secrets.sh +0 -0
  421. /package/templates/base/skills/{stability-verification → tsq-stability}/references/release-checklist.md +0 -0
  422. /package/templates/base/skills/{stability-verification → tsq-stability}/references/security-fix-patterns.md +0 -0
  423. /package/templates/base/skills/{stability-verification → tsq-stability}/rules/verification-layers.md +0 -0
  424. /package/templates/base/skills/{stability-verification → tsq-stability}/rules/verification-workflow.md +0 -0
  425. /package/templates/base/skills/{stability-verification → tsq-stability}/scripts/verify.sh +0 -0
  426. /package/templates/base/skills/{methodology/tdd → tsq-tdd}/rules/real-world-example.md +0 -0
  427. /package/templates/base/skills/{methodology/tdd → tsq-tdd}/rules/techniques.md +0 -0
  428. /package/templates/base/skills/{testing → tsq-testing}/references/e2e-stability.md +0 -0
  429. /package/templates/base/skills/{testing → tsq-testing}/references/testing-patterns.md +0 -0
  430. /package/templates/base/skills/{typescript → tsq-typescript}/rules/type-patterns.md +0 -0
  431. /package/templates/base/skills/{typescript → tsq-typescript}/rules/utility-types.md +0 -0
@@ -1,885 +0,0 @@
1
- import path from 'path';
2
- import fs from 'fs-extra';
3
- import { execSync, execFileSync } from 'child_process';
4
- import { colors, printHeader, printError, printSuccess, printWarning, printKeyValue } from '../utils/colors.js';
5
- import { findProjectRoot, getProjectInfo } from '../lib/project.js';
6
- import { exists, writeFile, listFiles } from '../utils/fs.js';
7
- import { getTimestamp, getDateString } from '../utils/date.js';
8
- import { promptText, promptConfirm } from '../utils/prompts.js';
9
- export function registerRetroCommand(program) {
10
- const retroCmd = program
11
- .command('retro')
12
- .description('Retrospective learning system');
13
- // tsq retro phase <phase-name>
14
- retroCmd
15
- .command('phase <phase>')
16
- .description('Run phase-level retrospective (saves locally)')
17
- .action(async (phase) => {
18
- try {
19
- await runPhaseRetro(phase);
20
- }
21
- catch (error) {
22
- printError(error.message);
23
- process.exit(1);
24
- }
25
- });
26
- // tsq retro start
27
- retroCmd
28
- .command('start')
29
- .description('Start a new retrospective cycle')
30
- .action(async () => {
31
- try {
32
- await startRetro();
33
- }
34
- catch (error) {
35
- printError(error.message);
36
- process.exit(1);
37
- }
38
- });
39
- // tsq retro collect
40
- retroCmd
41
- .command('collect')
42
- .description('Collect logs and metrics')
43
- .action(async () => {
44
- try {
45
- await collectRetro();
46
- }
47
- catch (error) {
48
- printError(error.message);
49
- process.exit(1);
50
- }
51
- });
52
- // tsq retro analyze
53
- retroCmd
54
- .command('analyze')
55
- .description('Analyze patterns (requires Claude)')
56
- .action(async () => {
57
- try {
58
- await analyzeRetro();
59
- }
60
- catch (error) {
61
- printError(error.message);
62
- process.exit(1);
63
- }
64
- });
65
- // tsq retro report
66
- retroCmd
67
- .command('report')
68
- .description('Generate aggregated report + create GitHub Issue')
69
- .option('--local', 'Skip GitHub Issue creation')
70
- .action(async (options) => {
71
- try {
72
- await generateReport(options.local);
73
- }
74
- catch (error) {
75
- printError(error.message);
76
- process.exit(1);
77
- }
78
- });
79
- // tsq retro apply [pattern-id]
80
- retroCmd
81
- .command('apply [patternId]')
82
- .description('Apply improvements (optionally apply specific pattern to skill)')
83
- .option('--skill <name>', 'Target skill to apply pattern to')
84
- .action(async (patternId, options) => {
85
- try {
86
- if (patternId && options.skill) {
87
- await applyPatternToSkill(patternId, options.skill);
88
- }
89
- else {
90
- await applyImprovements();
91
- }
92
- }
93
- catch (error) {
94
- printError(error.message);
95
- process.exit(1);
96
- }
97
- });
98
- // tsq retro auto
99
- retroCmd
100
- .command('auto')
101
- .description('Run full retrospective cycle automatically (start → collect → report → apply)')
102
- .option('--local', 'Skip GitHub Issue creation')
103
- .action(async (options) => {
104
- try {
105
- await runAutoRetro(options.local);
106
- }
107
- catch (error) {
108
- printError(error.message);
109
- process.exit(1);
110
- }
111
- });
112
- // tsq retro status
113
- retroCmd
114
- .command('status')
115
- .description('Show retrospective status')
116
- .action(async () => {
117
- try {
118
- await showRetroStatus();
119
- }
120
- catch (error) {
121
- printError(error.message);
122
- process.exit(1);
123
- }
124
- });
125
- }
126
- // ============================================================
127
- // Phase-level retro
128
- // ============================================================
129
- const VALID_PHASES = ['planning', 'design', 'implementation', 'review', 'security', 'deployment'];
130
- async function runPhaseRetro(phase) {
131
- if (!VALID_PHASES.includes(phase)) {
132
- throw new Error(`Invalid phase: ${phase}. Valid: ${VALID_PHASES.join(', ')}`);
133
- }
134
- const projectRoot = await findProjectRoot();
135
- if (!projectRoot) {
136
- throw new Error('Not a TimSquad project');
137
- }
138
- const project = await getProjectInfo(projectRoot);
139
- printHeader(`Phase Retrospective: ${phase}`);
140
- console.log(colors.dim('KPT 프레임워크로 Phase 회고를 진행합니다.\n'));
141
- // Interactive KPT input
142
- console.log(colors.header('Keep (잘 된 것)'));
143
- const keepInput = await promptText('쉼표로 구분하여 입력:', '');
144
- const keep = keepInput.split(',').map(s => s.trim()).filter(Boolean);
145
- console.log('');
146
- console.log(colors.header('Problem (문제점)'));
147
- const problemInput = await promptText('쉼표로 구분하여 입력:', '');
148
- const problem = problemInput.split(',').map(s => s.trim()).filter(Boolean);
149
- console.log('');
150
- console.log(colors.header('Try (다음에 시도할 것)'));
151
- const tryInput = await promptText('쉼표로 구분하여 입력:', '');
152
- const tryNext = tryInput.split(',').map(s => s.trim()).filter(Boolean);
153
- // Create phase retro entry
154
- const entry = {
155
- id: `PR-${phase}-${Date.now()}`,
156
- timestamp: getTimestamp(),
157
- project: project.name,
158
- phase,
159
- keep,
160
- problem,
161
- try_next: tryNext,
162
- tags: [phase, project.type],
163
- };
164
- // Save locally
165
- const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
166
- await fs.ensureDir(feedbackDir);
167
- const fileName = `phase-${phase}-${getDateString()}.json`;
168
- await fs.writeJson(path.join(feedbackDir, fileName), entry, { spaces: 2 });
169
- console.log('');
170
- printSuccess(`Phase 회고 저장: .timsquad/feedback/${fileName}`);
171
- // Show summary
172
- console.log('');
173
- printKeyValue('Keep', keep.length > 0 ? keep.join(', ') : '(없음)');
174
- printKeyValue('Problem', problem.length > 0 ? problem.join(', ') : '(없음)');
175
- printKeyValue('Try', tryNext.length > 0 ? tryNext.join(', ') : '(없음)');
176
- console.log(colors.dim('\n전체 리포트 생성: tsq retro report'));
177
- }
178
- // ============================================================
179
- // Existing retro flow
180
- // ============================================================
181
- async function getRetroState(projectRoot) {
182
- const statePath = path.join(projectRoot, '.timsquad', 'retrospective', 'state.json');
183
- if (!await exists(statePath)) {
184
- return { currentCycle: 0, status: 'idle' };
185
- }
186
- return fs.readJson(statePath);
187
- }
188
- async function saveRetroState(projectRoot, state) {
189
- const statePath = path.join(projectRoot, '.timsquad', 'retrospective', 'state.json');
190
- await fs.ensureDir(path.dirname(statePath));
191
- await fs.writeJson(statePath, state, { spaces: 2 });
192
- }
193
- async function startRetro() {
194
- const projectRoot = await findProjectRoot();
195
- if (!projectRoot) {
196
- throw new Error('Not a TimSquad project');
197
- }
198
- const state = await getRetroState(projectRoot);
199
- if (state.status !== 'idle') {
200
- throw new Error(`Retrospective cycle ${state.currentCycle} is already in progress (${state.status})`);
201
- }
202
- state.currentCycle += 1;
203
- state.status = 'collecting';
204
- state.startedAt = getTimestamp();
205
- await saveRetroState(projectRoot, state);
206
- printSuccess(`Started retrospective cycle ${state.currentCycle}`);
207
- console.log(colors.dim('\nNext: Run "tsq retro collect" to collect metrics'));
208
- }
209
- async function collectRetro() {
210
- const projectRoot = await findProjectRoot();
211
- if (!projectRoot) {
212
- throw new Error('Not a TimSquad project');
213
- }
214
- const state = await getRetroState(projectRoot);
215
- if (state.status !== 'collecting') {
216
- throw new Error('No active retrospective. Run "tsq retro start" first');
217
- }
218
- printHeader(`Collecting Metrics - Cycle ${state.currentCycle}`);
219
- // Collect log stats
220
- const logsDir = path.join(projectRoot, '.timsquad', 'logs');
221
- const logFiles = await listFiles('*.md', logsDir);
222
- const recentLogs = logFiles.filter(f => !f.startsWith('_'));
223
- // Count by agent
224
- const agentStats = {};
225
- for (const file of recentLogs) {
226
- const match = file.match(/-([a-z]+)\.md$/);
227
- if (match) {
228
- const agent = match[1];
229
- agentStats[agent] = (agentStats[agent] || 0) + 1;
230
- }
231
- }
232
- // Create metrics JSON
233
- const metrics = {
234
- cycle: state.currentCycle,
235
- collectedAt: getTimestamp(),
236
- raw_data: {
237
- log_files: recentLogs.length,
238
- agents: agentStats,
239
- },
240
- summary: {
241
- total_logs: recentLogs.length,
242
- agents_active: Object.keys(agentStats).length,
243
- },
244
- };
245
- const metricsPath = path.join(projectRoot, '.timsquad', 'retrospective', 'metrics', `cycle-${state.currentCycle}.json`);
246
- await fs.ensureDir(path.dirname(metricsPath));
247
- await fs.writeJson(metricsPath, metrics, { spaces: 2 });
248
- state.status = 'analyzing';
249
- state.collectedAt = getTimestamp();
250
- await saveRetroState(projectRoot, state);
251
- printSuccess('Metrics collected');
252
- printKeyValue('Log files', String(recentLogs.length));
253
- printKeyValue('Active agents', String(Object.keys(agentStats).length));
254
- console.log(colors.dim('\nNext: Run "tsq retro analyze" to analyze patterns'));
255
- }
256
- async function analyzeRetro() {
257
- const projectRoot = await findProjectRoot();
258
- if (!projectRoot) {
259
- throw new Error('Not a TimSquad project');
260
- }
261
- const state = await getRetroState(projectRoot);
262
- if (state.status !== 'analyzing') {
263
- throw new Error('Collect metrics first. Run "tsq retro collect"');
264
- }
265
- printHeader(`Analyzing Patterns - Cycle ${state.currentCycle}`);
266
- const analysis = await runPatternAnalysis(projectRoot, state.currentCycle);
267
- // 결과 저장
268
- const analysisDir = path.join(projectRoot, '.timsquad', 'retrospective', 'analysis');
269
- await fs.ensureDir(analysisDir);
270
- await fs.writeJson(path.join(analysisDir, `cycle-${state.currentCycle}-analysis.json`), analysis, { spaces: 2 });
271
- state.status = 'reporting';
272
- state.analyzedAt = getTimestamp();
273
- await saveRetroState(projectRoot, state);
274
- // 결과 출력
275
- printSuccess('Pattern analysis completed');
276
- printKeyValue('Sessions analyzed', String(analysis.sessions));
277
- printKeyValue('Agents', String(Object.keys(analysis.agents).length));
278
- printKeyValue('Flags', String(analysis.flags.length));
279
- if (analysis.flags.length > 0) {
280
- console.log(colors.warning('\n⚠ Detected issues:'));
281
- for (const flag of analysis.flags) {
282
- console.log(colors.dim(` - [${flag.severity}] ${flag.message}`));
283
- }
284
- }
285
- console.log(colors.dim('\nNext: Run "tsq retro report" to generate report'));
286
- }
287
- async function runPatternAnalysis(projectRoot, cycle) {
288
- const sessionsDir = path.join(projectRoot, '.timsquad', 'logs', 'sessions');
289
- const agentStats = {};
290
- const toolStats = {};
291
- const fileModCounts = {};
292
- let sessionCount = 0;
293
- // 세션 로그 파싱
294
- if (await exists(sessionsDir)) {
295
- const logFiles = await listFiles('*.jsonl', sessionsDir);
296
- sessionCount = logFiles.length;
297
- for (const file of logFiles) {
298
- try {
299
- const content = await fs.readFile(path.join(sessionsDir, file), 'utf-8');
300
- for (const line of content.split('\n')) {
301
- const trimmed = line.trim();
302
- if (!trimmed)
303
- continue;
304
- try {
305
- const ev = JSON.parse(trimmed);
306
- // 에이전트 통계
307
- if (ev.event === 'SubagentStart' && ev.detail?.subagent_type) {
308
- const agent = ev.detail.subagent_type;
309
- if (!agentStats[agent])
310
- agentStats[agent] = { calls: 0, failures: 0 };
311
- agentStats[agent].calls++;
312
- }
313
- // 도구 통계
314
- if (ev.event === 'PostToolUse' && ev.tool) {
315
- if (!toolStats[ev.tool])
316
- toolStats[ev.tool] = { uses: 0, failures: 0 };
317
- toolStats[ev.tool].uses++;
318
- }
319
- if (ev.event === 'PostToolUseFailure' && ev.tool) {
320
- if (!toolStats[ev.tool])
321
- toolStats[ev.tool] = { uses: 0, failures: 0 };
322
- toolStats[ev.tool].failures++;
323
- }
324
- }
325
- catch { /* skip malformed line */ }
326
- }
327
- }
328
- catch { /* skip unreadable file */ }
329
- }
330
- }
331
- // L1 태스크 로그에서 rework 파일 탐지
332
- const tasksDir = path.join(projectRoot, '.timsquad', 'logs', 'tasks');
333
- if (await exists(tasksDir)) {
334
- const taskFiles = await listFiles('*.json', tasksDir);
335
- for (const file of taskFiles) {
336
- try {
337
- const task = await fs.readJson(path.join(tasksDir, file));
338
- if (task.mechanical?.files) {
339
- for (const f of task.mechanical.files) {
340
- fileModCounts[f.path] = (fileModCounts[f.path] || 0) + 1;
341
- }
342
- }
343
- // 에이전트 실패 추적 (status !== 'completed')
344
- if (task.agent && task.status !== 'completed') {
345
- if (!agentStats[task.agent])
346
- agentStats[task.agent] = { calls: 0, failures: 0 };
347
- agentStats[task.agent].failures++;
348
- }
349
- }
350
- catch { /* skip */ }
351
- }
352
- }
353
- // rework 파일 (3회+ 수정)
354
- const reworkFiles = Object.entries(fileModCounts)
355
- .filter(([, count]) => count >= 3)
356
- .sort(([, a], [, b]) => b - a)
357
- .map(([filePath, modifications]) => ({ path: filePath, modifications }));
358
- // 에이전트별 실패율 계산
359
- const agents = {};
360
- for (const [name, stat] of Object.entries(agentStats)) {
361
- const total = stat.calls + stat.failures;
362
- agents[name] = {
363
- calls: stat.calls,
364
- failures: stat.failures,
365
- failureRate: total > 0 ? Math.round((stat.failures / total) * 100) : 0,
366
- };
367
- }
368
- // 도구별 실패율 계산
369
- const tools = {};
370
- for (const [name, stat] of Object.entries(toolStats)) {
371
- const total = stat.uses + stat.failures;
372
- tools[name] = {
373
- uses: stat.uses,
374
- failures: stat.failures,
375
- failureRate: total > 0 ? Math.round((stat.failures / total) * 100) : 0,
376
- };
377
- }
378
- // 이상 탐지 플래그
379
- const flags = [];
380
- // 에이전트 실패율 > 20%
381
- for (const [name, stat] of Object.entries(agents)) {
382
- if (stat.failureRate > 20 && (stat.calls + stat.failures) >= 3) {
383
- flags.push({
384
- severity: 'warn',
385
- category: 'agent-failure',
386
- message: `Agent "${name}" failure rate ${stat.failureRate}% (${stat.failures}/${stat.calls + stat.failures})`,
387
- });
388
- }
389
- }
390
- // 도구 실패율 > 30% (3회 이상 사용)
391
- for (const [name, stat] of Object.entries(tools)) {
392
- if (stat.failureRate > 30 && (stat.uses + stat.failures) >= 3) {
393
- flags.push({
394
- severity: 'warn',
395
- category: 'tool-failure',
396
- message: `Tool "${name}" failure rate ${stat.failureRate}% (${stat.failures}/${stat.uses + stat.failures})`,
397
- });
398
- }
399
- }
400
- // rework 파일
401
- for (const rw of reworkFiles.slice(0, 5)) {
402
- flags.push({
403
- severity: 'info',
404
- category: 'rework',
405
- message: `File "${rw.path}" modified ${rw.modifications} times (possible rework)`,
406
- });
407
- }
408
- return {
409
- cycle,
410
- analyzedAt: getTimestamp(),
411
- sessions: sessionCount,
412
- agents,
413
- tools,
414
- reworkFiles,
415
- flags,
416
- };
417
- }
418
- // ============================================================
419
- // Aggregated report + GitHub Issue
420
- // ============================================================
421
- async function loadFeedbackEntries(projectRoot) {
422
- const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
423
- if (!await exists(feedbackDir))
424
- return [];
425
- const files = await listFiles('FB-*.json', feedbackDir);
426
- const entries = [];
427
- for (const file of files) {
428
- try {
429
- const data = await fs.readJson(path.join(feedbackDir, file));
430
- entries.push(data);
431
- }
432
- catch {
433
- // skip invalid files
434
- }
435
- }
436
- return entries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
437
- }
438
- async function loadPhaseRetros(projectRoot) {
439
- const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
440
- if (!await exists(feedbackDir))
441
- return [];
442
- const files = await listFiles('phase-*.json', feedbackDir);
443
- const entries = [];
444
- for (const file of files) {
445
- try {
446
- const data = await fs.readJson(path.join(feedbackDir, file));
447
- entries.push(data);
448
- }
449
- catch {
450
- // skip invalid files
451
- }
452
- }
453
- return entries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
454
- }
455
- function buildAggregatedReport(project, phases, feedbacks) {
456
- const byLevel = {};
457
- const byPhase = {};
458
- const issueCount = {};
459
- for (const fb of feedbacks) {
460
- const levelKey = `Level ${fb.level ?? '?'}`;
461
- byLevel[levelKey] = (byLevel[levelKey] || 0) + 1;
462
- if (fb.phase) {
463
- byPhase[fb.phase] = (byPhase[fb.phase] || 0) + 1;
464
- }
465
- if (fb.trigger) {
466
- issueCount[fb.trigger] = (issueCount[fb.trigger] || 0) + 1;
467
- }
468
- }
469
- for (const pr of phases) {
470
- byPhase[pr.phase] = (byPhase[pr.phase] || 0) + pr.problem.length;
471
- for (const p of pr.problem) {
472
- issueCount[p] = (issueCount[p] || 0) + 1;
473
- }
474
- }
475
- const topIssues = Object.entries(issueCount)
476
- .sort(([, a], [, b]) => b - a)
477
- .slice(0, 5)
478
- .map(([issue]) => issue);
479
- const timestamps = [
480
- ...phases.map(p => p.timestamp),
481
- ...feedbacks.map(f => f.timestamp),
482
- ].sort();
483
- return {
484
- project: project.name,
485
- generated_at: getTimestamp(),
486
- period: {
487
- start: timestamps[0] || getTimestamp(),
488
- end: timestamps[timestamps.length - 1] || getTimestamp(),
489
- },
490
- phases,
491
- feedbacks,
492
- summary: {
493
- total_feedbacks: feedbacks.length,
494
- by_level: byLevel,
495
- by_phase: byPhase,
496
- top_issues: topIssues,
497
- },
498
- };
499
- }
500
- function renderReportMarkdown(report) {
501
- const lines = [];
502
- lines.push(`# [retro] ${report.project} - Retrospective Report`);
503
- lines.push('');
504
- lines.push(`> Generated: ${report.generated_at}`);
505
- lines.push(`> Period: ${report.period.start?.split('T')[0] ?? 'N/A'} ~ ${report.period.end?.split('T')[0] ?? 'N/A'}`);
506
- lines.push('');
507
- // Summary
508
- lines.push('## Summary');
509
- lines.push('');
510
- lines.push('| Metric | Value |');
511
- lines.push('|--------|-------|');
512
- lines.push(`| Phase retros | ${report.phases.length} |`);
513
- lines.push(`| Feedbacks | ${report.summary.total_feedbacks} |`);
514
- for (const [level, count] of Object.entries(report.summary.by_level)) {
515
- lines.push(`| ${level} | ${count} |`);
516
- }
517
- lines.push('');
518
- // Top issues
519
- if (report.summary.top_issues.length > 0) {
520
- lines.push('## Top Issues');
521
- lines.push('');
522
- for (const issue of report.summary.top_issues) {
523
- lines.push(`- ${issue}`);
524
- }
525
- lines.push('');
526
- }
527
- // Phase retros
528
- if (report.phases.length > 0) {
529
- lines.push('## Phase Retrospectives');
530
- lines.push('');
531
- for (const phase of report.phases) {
532
- lines.push(`### ${phase.phase} (${phase.timestamp?.split('T')[0] ?? 'N/A'})`);
533
- lines.push('');
534
- if (phase.keep.length > 0) {
535
- lines.push('**Keep:**');
536
- for (const k of phase.keep)
537
- lines.push(`- ${k}`);
538
- lines.push('');
539
- }
540
- if (phase.problem.length > 0) {
541
- lines.push('**Problem:**');
542
- for (const p of phase.problem)
543
- lines.push(`- ${p}`);
544
- lines.push('');
545
- }
546
- if (phase.try_next.length > 0) {
547
- lines.push('**Try:**');
548
- for (const t of phase.try_next)
549
- lines.push(`- ${t}`);
550
- lines.push('');
551
- }
552
- }
553
- }
554
- // Feedbacks
555
- if (report.feedbacks.length > 0) {
556
- lines.push('## Feedbacks');
557
- lines.push('');
558
- lines.push('| Date | Level | Trigger | Message |');
559
- lines.push('|------|:-----:|---------|---------|');
560
- for (const fb of report.feedbacks) {
561
- const date = fb.timestamp?.split('T')[0] ?? 'N/A';
562
- lines.push(`| ${date} | ${fb.level ?? '-'} | ${fb.trigger ?? '-'} | ${fb.message.substring(0, 60)} |`);
563
- }
564
- lines.push('');
565
- }
566
- lines.push('## Improvement Suggestions');
567
- lines.push('');
568
- for (const issue of report.summary.top_issues) {
569
- lines.push(`- [ ] Address: ${issue}`);
570
- }
571
- lines.push('');
572
- lines.push('---');
573
- lines.push('Generated by TimSquad v2.0.0');
574
- return lines.join('\n');
575
- }
576
- function createGitHubIssue(title, body) {
577
- try {
578
- execSync('gh --version', { stdio: 'ignore' });
579
- // Write body to temp file to avoid shell escaping issues
580
- const tmpFile = path.join('/tmp', `tsq-retro-${Date.now()}.md`);
581
- fs.writeFileSync(tmpFile, body, 'utf-8');
582
- const result = execFileSync('gh', ['issue', 'create', '--repo', 'ericson/timsquad', '--title', title, '--label', 'retro-feedback', '--body-file', tmpFile], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
583
- fs.removeSync(tmpFile);
584
- return result.trim();
585
- }
586
- catch {
587
- return null;
588
- }
589
- }
590
- async function generateReport(localOnly) {
591
- const projectRoot = await findProjectRoot();
592
- if (!projectRoot) {
593
- throw new Error('Not a TimSquad project');
594
- }
595
- const project = await getProjectInfo(projectRoot);
596
- printHeader('Generating Aggregated Report');
597
- // Load all local feedback
598
- const phases = await loadPhaseRetros(projectRoot);
599
- const feedbacks = await loadFeedbackEntries(projectRoot);
600
- if (phases.length === 0 && feedbacks.length === 0) {
601
- console.log(colors.warning('\n⚠ No feedback data found.'));
602
- console.log(colors.dim(' Run "tsq retro phase <name>" or "tsq feedback <message>" first.\n'));
603
- return;
604
- }
605
- printKeyValue('Phase retros', String(phases.length));
606
- printKeyValue('Feedbacks', String(feedbacks.length));
607
- // Build aggregated report
608
- const report = buildAggregatedReport(project, phases, feedbacks);
609
- const markdown = renderReportMarkdown(report);
610
- // Save report locally
611
- const state = await getRetroState(projectRoot);
612
- const cycleNum = state.currentCycle > 0 ? state.currentCycle : 1;
613
- const reportDir = path.join(projectRoot, '.timsquad', 'retrospective', 'cycles');
614
- await fs.ensureDir(reportDir);
615
- const reportPath = path.join(reportDir, `cycle-${cycleNum}.md`);
616
- await writeFile(reportPath, markdown);
617
- const jsonPath = path.join(reportDir, `cycle-${cycleNum}.json`);
618
- await fs.writeJson(jsonPath, report, { spaces: 2 });
619
- console.log('');
620
- printSuccess(`Report saved: cycle-${cycleNum}.md`);
621
- console.log(colors.path(` ${reportPath}`));
622
- // Create GitHub Issue (unless --local)
623
- if (!localOnly) {
624
- console.log('');
625
- const shouldCreate = await promptConfirm('GitHub Issue를 생성하시겠습니까?', true);
626
- if (shouldCreate) {
627
- const issueTitle = `[retro] ${project.name} - Cycle ${cycleNum}`;
628
- const issueUrl = createGitHubIssue(issueTitle, markdown);
629
- if (issueUrl) {
630
- console.log('');
631
- printSuccess(`GitHub Issue created: ${issueUrl}`);
632
- }
633
- else {
634
- console.log(colors.warning('\n⚠ GitHub Issue 생성 실패 (gh CLI 확인 필요)'));
635
- console.log(colors.dim(' 수동 생성: gh issue create --repo ericson/timsquad'));
636
- }
637
- }
638
- }
639
- // Update retro state
640
- state.status = 'applying';
641
- await saveRetroState(projectRoot, state);
642
- console.log(colors.dim('\nNext: Review report and run "tsq retro apply" to complete'));
643
- }
644
- async function applyPatternToSkill(patternId, skillName) {
645
- const projectRoot = await findProjectRoot();
646
- if (!projectRoot)
647
- throw new Error('Not a TimSquad project');
648
- // Find skill directory
649
- const skillDir = path.join(projectRoot, '.claude', 'skills', skillName);
650
- if (!await exists(skillDir)) {
651
- throw new Error(`Skill "${skillName}" not found at ${skillDir}`);
652
- }
653
- const rulesDir = path.join(skillDir, 'rules');
654
- await fs.ensureDir(rulesDir);
655
- // Create rule file from pattern
656
- const ruleFile = path.join(rulesDir, `${patternId}.md`);
657
- if (await exists(ruleFile)) {
658
- printWarning(`Rule file already exists: ${ruleFile}`);
659
- return;
660
- }
661
- // Generate rule template
662
- const content = [
663
- '---',
664
- `title: ${patternId.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase())}`,
665
- 'impact: MEDIUM',
666
- `tags: ${skillName}, retrospective, ${patternId}`,
667
- '---',
668
- '',
669
- `# ${patternId}`,
670
- '',
671
- `Pattern applied from retrospective.`,
672
- '',
673
- '## Context',
674
- '',
675
- '<!-- Describe the problem this pattern addresses -->',
676
- '',
677
- '## Rule',
678
- '',
679
- '<!-- Describe the correct approach -->',
680
- '',
681
- '## Examples',
682
- '',
683
- '<!-- Add Incorrect/Correct examples -->',
684
- '',
685
- ].join('\n');
686
- await fs.writeFile(ruleFile, content);
687
- // Update SKILL.md resources if possible
688
- const skillMd = path.join(skillDir, 'SKILL.md');
689
- if (await exists(skillMd)) {
690
- printSuccess(`Pattern "${patternId}" applied to skill "${skillName}"`);
691
- console.log(colors.dim(` Rule file: ${ruleFile}`));
692
- console.log(colors.dim(` Edit the generated template and update ${skillMd} Resources table`));
693
- }
694
- }
695
- async function applyImprovements() {
696
- const projectRoot = await findProjectRoot();
697
- if (!projectRoot) {
698
- throw new Error('Not a TimSquad project');
699
- }
700
- const state = await getRetroState(projectRoot);
701
- if (state.status !== 'applying') {
702
- throw new Error('Generate report first. Run "tsq retro report"');
703
- }
704
- console.log(colors.warning('\n⚠ Improvement application requires manual review'));
705
- console.log(colors.dim(' 1. Review the generated report'));
706
- console.log(colors.dim(' 2. Update templates/prompts as needed'));
707
- console.log(colors.dim(' 3. This cycle will be marked as complete\n'));
708
- // Archive processed feedback
709
- const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
710
- const archiveDir = path.join(feedbackDir, `archive-cycle-${state.currentCycle}`);
711
- if (await exists(feedbackDir)) {
712
- const files = await listFiles('*.json', feedbackDir);
713
- if (files.length > 0) {
714
- await fs.ensureDir(archiveDir);
715
- for (const file of files) {
716
- await fs.move(path.join(feedbackDir, file), path.join(archiveDir, file), { overwrite: true });
717
- }
718
- console.log(colors.dim(` Feedback archived: ${archiveDir}`));
719
- }
720
- }
721
- state.status = 'idle';
722
- await saveRetroState(projectRoot, state);
723
- printSuccess(`Retrospective cycle ${state.currentCycle} completed!`);
724
- }
725
- // ============================================================
726
- // Auto retro (전체 사이클 자동 실행)
727
- // start → collect → (analyze skip) → report → apply
728
- // ============================================================
729
- async function runAutoRetro(localOnly) {
730
- const projectRoot = await findProjectRoot();
731
- if (!projectRoot) {
732
- throw new Error('Not a TimSquad project');
733
- }
734
- printHeader('Auto Retrospective');
735
- console.log(colors.dim('전체 회고 사이클을 자동으로 실행합니다.\n'));
736
- // Step 1: Start
737
- let state = await getRetroState(projectRoot);
738
- if (state.status !== 'idle') {
739
- console.log(colors.warning(`⚠ 기존 cycle ${state.currentCycle} (${state.status}) 가 진행 중입니다.`));
740
- console.log(colors.dim(' 기존 사이클을 이어서 진행합니다.\n'));
741
- }
742
- else {
743
- state.currentCycle += 1;
744
- state.status = 'collecting';
745
- state.startedAt = getTimestamp();
746
- await saveRetroState(projectRoot, state);
747
- printSuccess(`[1/4] Cycle ${state.currentCycle} started`);
748
- }
749
- // Step 2: Collect
750
- if (state.status === 'collecting') {
751
- const logsDir = path.join(projectRoot, '.timsquad', 'logs');
752
- const logFiles = await listFiles('*.md', logsDir);
753
- const recentLogs = logFiles.filter(f => !f.startsWith('_'));
754
- const agentStats = {};
755
- for (const file of recentLogs) {
756
- const match = file.match(/-([a-z]+)\.md$/);
757
- if (match) {
758
- agentStats[match[1]] = (agentStats[match[1]] || 0) + 1;
759
- }
760
- }
761
- const metricsPath = path.join(projectRoot, '.timsquad', 'retrospective', 'metrics', `cycle-${state.currentCycle}.json`);
762
- await fs.ensureDir(path.dirname(metricsPath));
763
- await fs.writeJson(metricsPath, {
764
- cycle: state.currentCycle,
765
- collectedAt: getTimestamp(),
766
- raw_data: { log_files: recentLogs.length, agents: agentStats },
767
- summary: { total_logs: recentLogs.length, agents_active: Object.keys(agentStats).length },
768
- }, { spaces: 2 });
769
- state.status = 'analyzing';
770
- state.collectedAt = getTimestamp();
771
- await saveRetroState(projectRoot, state);
772
- printSuccess(`[2/4] Metrics collected (${recentLogs.length} logs, ${Object.keys(agentStats).length} agents)`);
773
- }
774
- // Step 3: Pattern analysis
775
- if (state.status === 'analyzing') {
776
- const analysis = await runPatternAnalysis(projectRoot, state.currentCycle);
777
- const analysisDir = path.join(projectRoot, '.timsquad', 'retrospective', 'analysis');
778
- await fs.ensureDir(analysisDir);
779
- await fs.writeJson(path.join(analysisDir, `cycle-${state.currentCycle}-analysis.json`), analysis, { spaces: 2 });
780
- state.status = 'reporting';
781
- state.analyzedAt = getTimestamp();
782
- await saveRetroState(projectRoot, state);
783
- printSuccess(`[3/4] Analysis completed (${analysis.flags.length} flags)`);
784
- }
785
- // Step 4: Report + Apply
786
- if (state.status === 'reporting') {
787
- const project = await getProjectInfo(projectRoot);
788
- const phases = await loadPhaseRetros(projectRoot);
789
- const feedbacks = await loadFeedbackEntries(projectRoot);
790
- if (phases.length === 0 && feedbacks.length === 0) {
791
- console.log(colors.dim('\n 피드백 데이터 없음 - 빈 리포트 생성을 건너뜁니다.'));
792
- }
793
- else {
794
- const report = buildAggregatedReport(project, phases, feedbacks);
795
- const markdown = renderReportMarkdown(report);
796
- const reportDir = path.join(projectRoot, '.timsquad', 'retrospective', 'cycles');
797
- await fs.ensureDir(reportDir);
798
- await writeFile(path.join(reportDir, `cycle-${state.currentCycle}.md`), markdown);
799
- await fs.writeJson(path.join(reportDir, `cycle-${state.currentCycle}.json`), report, { spaces: 2 });
800
- printKeyValue(' Phase retros', String(phases.length));
801
- printKeyValue(' Feedbacks', String(feedbacks.length));
802
- // GitHub Issue (auto mode에서는 --local이 아니면 자동 생성 시도)
803
- if (!localOnly) {
804
- const issueTitle = `[retro] ${project.name} - Cycle ${state.currentCycle}`;
805
- const issueUrl = createGitHubIssue(issueTitle, markdown);
806
- if (issueUrl) {
807
- printSuccess(` GitHub Issue: ${issueUrl}`);
808
- }
809
- }
810
- }
811
- // Apply: archive feedback
812
- const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
813
- const archiveDir = path.join(feedbackDir, `archive-cycle-${state.currentCycle}`);
814
- if (await exists(feedbackDir)) {
815
- const files = await listFiles('*.json', feedbackDir);
816
- if (files.length > 0) {
817
- await fs.ensureDir(archiveDir);
818
- for (const file of files) {
819
- await fs.move(path.join(feedbackDir, file), path.join(archiveDir, file), { overwrite: true });
820
- }
821
- }
822
- }
823
- state.status = 'idle';
824
- await saveRetroState(projectRoot, state);
825
- printSuccess(`[4/4] Cycle ${state.currentCycle} completed`);
826
- }
827
- // Bonus: retro → improve 자동 연결
828
- // GitHub Issue가 생성되었으면 improve fetch+analyze 자동 실행
829
- try {
830
- execSync('gh --version', { stdio: 'ignore' });
831
- console.log(colors.dim('\n Improvement analysis 자동 실행 중...'));
832
- try {
833
- execFileSync('npx', ['tsq', 'improve', 'fetch', '--limit', '20'], {
834
- cwd: projectRoot,
835
- stdio: 'ignore',
836
- timeout: 15000,
837
- });
838
- execFileSync('npx', ['tsq', 'improve', 'analyze'], {
839
- cwd: projectRoot,
840
- stdio: 'ignore',
841
- timeout: 15000,
842
- });
843
- printSuccess(' Improvement analysis completed');
844
- console.log(colors.dim(' 결과 확인: tsq improve summary'));
845
- }
846
- catch {
847
- console.log(colors.dim(' Improvement analysis skipped (no issues or error)'));
848
- }
849
- }
850
- catch {
851
- // gh not available, skip
852
- }
853
- console.log(colors.dim('\nTip: tsq retro status 로 결과 확인'));
854
- }
855
- async function showRetroStatus() {
856
- const projectRoot = await findProjectRoot();
857
- if (!projectRoot) {
858
- throw new Error('Not a TimSquad project');
859
- }
860
- const state = await getRetroState(projectRoot);
861
- printHeader('Retrospective Status');
862
- printKeyValue('Current Cycle', String(state.currentCycle));
863
- printKeyValue('Status', state.status);
864
- if (state.startedAt) {
865
- printKeyValue('Started', state.startedAt?.split('T')[0] ?? 'N/A');
866
- }
867
- // Count pending feedback
868
- const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
869
- if (await exists(feedbackDir)) {
870
- const phaseFiles = await listFiles('phase-*.json', feedbackDir);
871
- const fbFiles = await listFiles('FB-*.json', feedbackDir);
872
- console.log('');
873
- printKeyValue('Pending phase retros', String(phaseFiles.length));
874
- printKeyValue('Pending feedbacks', String(fbFiles.length));
875
- }
876
- // List completed cycles
877
- const cyclesDir = path.join(projectRoot, '.timsquad', 'retrospective', 'cycles');
878
- if (await exists(cyclesDir)) {
879
- const cycles = await listFiles('cycle-*.md', cyclesDir);
880
- if (cycles.length > 0) {
881
- console.log(colors.dim(`\nCompleted cycles: ${cycles.length}`));
882
- }
883
- }
884
- }
885
- //# sourceMappingURL=retro.js.map