timsquad 2.0.0 → 3.3.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 (353) hide show
  1. package/README.md +168 -234
  2. package/dist/commands/daemon.d.ts +7 -0
  3. package/dist/commands/daemon.d.ts.map +1 -0
  4. package/dist/commands/daemon.js +140 -0
  5. package/dist/commands/daemon.js.map +1 -0
  6. package/dist/commands/feedback.d.ts +9 -0
  7. package/dist/commands/feedback.d.ts.map +1 -1
  8. package/dist/commands/feedback.js +251 -11
  9. package/dist/commands/feedback.js.map +1 -1
  10. package/dist/commands/full.js +2 -2
  11. package/dist/commands/full.js.map +1 -1
  12. package/dist/commands/git/commit.d.ts.map +1 -1
  13. package/dist/commands/git/commit.js +1 -4
  14. package/dist/commands/git/commit.js.map +1 -1
  15. package/dist/commands/improve.d.ts +3 -0
  16. package/dist/commands/improve.d.ts.map +1 -0
  17. package/dist/commands/improve.js +286 -0
  18. package/dist/commands/improve.js.map +1 -0
  19. package/dist/commands/init.d.ts.map +1 -1
  20. package/dist/commands/init.js +110 -22
  21. package/dist/commands/init.js.map +1 -1
  22. package/dist/commands/knowledge.d.ts +3 -0
  23. package/dist/commands/knowledge.d.ts.map +1 -0
  24. package/dist/commands/knowledge.js +316 -0
  25. package/dist/commands/knowledge.js.map +1 -0
  26. package/dist/commands/log.d.ts +27 -0
  27. package/dist/commands/log.d.ts.map +1 -1
  28. package/dist/commands/log.js +1167 -2
  29. package/dist/commands/log.js.map +1 -1
  30. package/dist/commands/meta-index.d.ts +3 -0
  31. package/dist/commands/meta-index.d.ts.map +1 -0
  32. package/dist/commands/meta-index.js +401 -0
  33. package/dist/commands/meta-index.js.map +1 -0
  34. package/dist/commands/metrics.d.ts.map +1 -1
  35. package/dist/commands/metrics.js +640 -100
  36. package/dist/commands/metrics.js.map +1 -1
  37. package/dist/commands/retro.d.ts.map +1 -1
  38. package/dist/commands/retro.js +606 -58
  39. package/dist/commands/retro.js.map +1 -1
  40. package/dist/commands/session.d.ts +3 -0
  41. package/dist/commands/session.d.ts.map +1 -0
  42. package/dist/commands/session.js +346 -0
  43. package/dist/commands/session.js.map +1 -0
  44. package/dist/commands/upgrade.d.ts +8 -0
  45. package/dist/commands/upgrade.d.ts.map +1 -0
  46. package/dist/commands/upgrade.js +287 -0
  47. package/dist/commands/upgrade.js.map +1 -0
  48. package/dist/commands/workflow.d.ts +3 -0
  49. package/dist/commands/workflow.d.ts.map +1 -0
  50. package/dist/commands/workflow.js +607 -0
  51. package/dist/commands/workflow.js.map +1 -0
  52. package/dist/daemon/context-writer.d.ts +16 -0
  53. package/dist/daemon/context-writer.d.ts.map +1 -0
  54. package/dist/daemon/context-writer.js +35 -0
  55. package/dist/daemon/context-writer.js.map +1 -0
  56. package/dist/daemon/entry.d.ts +7 -0
  57. package/dist/daemon/entry.d.ts.map +1 -0
  58. package/dist/daemon/entry.js +17 -0
  59. package/dist/daemon/entry.js.map +1 -0
  60. package/dist/daemon/event-queue.d.ts +52 -0
  61. package/dist/daemon/event-queue.d.ts.map +1 -0
  62. package/dist/daemon/event-queue.js +255 -0
  63. package/dist/daemon/event-queue.js.map +1 -0
  64. package/dist/daemon/file-watcher.d.ts +19 -0
  65. package/dist/daemon/file-watcher.d.ts.map +1 -0
  66. package/dist/daemon/file-watcher.js +87 -0
  67. package/dist/daemon/file-watcher.js.map +1 -0
  68. package/dist/daemon/index.d.ts +28 -0
  69. package/dist/daemon/index.d.ts.map +1 -0
  70. package/dist/daemon/index.js +204 -0
  71. package/dist/daemon/index.js.map +1 -0
  72. package/dist/daemon/jsonl-watcher.d.ts +49 -0
  73. package/dist/daemon/jsonl-watcher.d.ts.map +1 -0
  74. package/dist/daemon/jsonl-watcher.js +258 -0
  75. package/dist/daemon/jsonl-watcher.js.map +1 -0
  76. package/dist/daemon/meta-cache.d.ts +62 -0
  77. package/dist/daemon/meta-cache.d.ts.map +1 -0
  78. package/dist/daemon/meta-cache.js +240 -0
  79. package/dist/daemon/meta-cache.js.map +1 -0
  80. package/dist/daemon/shutdown.d.ts +21 -0
  81. package/dist/daemon/shutdown.d.ts.map +1 -0
  82. package/dist/daemon/shutdown.js +158 -0
  83. package/dist/daemon/shutdown.js.map +1 -0
  84. package/dist/index.js +24 -3
  85. package/dist/index.js.map +1 -1
  86. package/dist/lib/agent-composer.d.ts +38 -0
  87. package/dist/lib/agent-composer.d.ts.map +1 -0
  88. package/dist/lib/agent-composer.js +128 -0
  89. package/dist/lib/agent-composer.js.map +1 -0
  90. package/dist/lib/agent-generator.d.ts +22 -0
  91. package/dist/lib/agent-generator.d.ts.map +1 -0
  92. package/dist/lib/agent-generator.js +150 -0
  93. package/dist/lib/agent-generator.js.map +1 -0
  94. package/dist/lib/ast-parser.d.ts +11 -0
  95. package/dist/lib/ast-parser.d.ts.map +1 -0
  96. package/dist/lib/ast-parser.js +282 -0
  97. package/dist/lib/ast-parser.js.map +1 -0
  98. package/dist/lib/config.d.ts +6 -2
  99. package/dist/lib/config.d.ts.map +1 -1
  100. package/dist/lib/config.js +27 -3
  101. package/dist/lib/config.js.map +1 -1
  102. package/dist/lib/meta-index.d.ts +19 -0
  103. package/dist/lib/meta-index.d.ts.map +1 -0
  104. package/dist/lib/meta-index.js +573 -0
  105. package/dist/lib/meta-index.js.map +1 -0
  106. package/dist/lib/project.js +1 -1
  107. package/dist/lib/project.js.map +1 -1
  108. package/dist/lib/skill-generator.d.ts +32 -0
  109. package/dist/lib/skill-generator.d.ts.map +1 -0
  110. package/dist/lib/skill-generator.js +187 -0
  111. package/dist/lib/skill-generator.js.map +1 -0
  112. package/dist/lib/template.d.ts +16 -2
  113. package/dist/lib/template.d.ts.map +1 -1
  114. package/dist/lib/template.js +175 -21
  115. package/dist/lib/template.js.map +1 -1
  116. package/dist/lib/ui-index.d.ts +12 -0
  117. package/dist/lib/ui-index.d.ts.map +1 -0
  118. package/dist/lib/ui-index.js +239 -0
  119. package/dist/lib/ui-index.js.map +1 -0
  120. package/dist/lib/ui-parser.d.ts +12 -0
  121. package/dist/lib/ui-parser.d.ts.map +1 -0
  122. package/dist/lib/ui-parser.js +472 -0
  123. package/dist/lib/ui-parser.js.map +1 -0
  124. package/dist/lib/update-check.d.ts +6 -0
  125. package/dist/lib/update-check.d.ts.map +1 -0
  126. package/dist/lib/update-check.js +121 -0
  127. package/dist/lib/update-check.js.map +1 -0
  128. package/dist/lib/upgrade-backup.d.ts +33 -0
  129. package/dist/lib/upgrade-backup.d.ts.map +1 -0
  130. package/dist/lib/upgrade-backup.js +101 -0
  131. package/dist/lib/upgrade-backup.js.map +1 -0
  132. package/dist/lib/version.d.ts +19 -0
  133. package/dist/lib/version.d.ts.map +1 -0
  134. package/dist/lib/version.js +35 -0
  135. package/dist/lib/version.js.map +1 -0
  136. package/dist/lib/workflow-state.d.ts +48 -0
  137. package/dist/lib/workflow-state.d.ts.map +1 -0
  138. package/dist/lib/workflow-state.js +67 -0
  139. package/dist/lib/workflow-state.js.map +1 -0
  140. package/dist/types/config.d.ts +102 -2
  141. package/dist/types/config.d.ts.map +1 -1
  142. package/dist/types/config.js +173 -9
  143. package/dist/types/config.js.map +1 -1
  144. package/dist/types/feedback.d.ts +59 -1
  145. package/dist/types/feedback.d.ts.map +1 -1
  146. package/dist/types/feedback.js +1 -4
  147. package/dist/types/feedback.js.map +1 -1
  148. package/dist/types/index.d.ts +3 -0
  149. package/dist/types/index.d.ts.map +1 -1
  150. package/dist/types/index.js +3 -0
  151. package/dist/types/index.js.map +1 -1
  152. package/dist/types/meta-index.d.ts +146 -0
  153. package/dist/types/meta-index.d.ts.map +1 -0
  154. package/dist/types/meta-index.js +7 -0
  155. package/dist/types/meta-index.js.map +1 -0
  156. package/dist/types/project.d.ts +19 -3
  157. package/dist/types/project.d.ts.map +1 -1
  158. package/dist/types/project.js +24 -1
  159. package/dist/types/project.js.map +1 -1
  160. package/dist/types/task-log.d.ts +208 -0
  161. package/dist/types/task-log.d.ts.map +1 -0
  162. package/dist/types/task-log.js +6 -0
  163. package/dist/types/task-log.js.map +1 -0
  164. package/dist/types/ui-meta.d.ts +118 -0
  165. package/dist/types/ui-meta.d.ts.map +1 -0
  166. package/dist/types/ui-meta.js +7 -0
  167. package/dist/types/ui-meta.js.map +1 -0
  168. package/package.json +12 -4
  169. package/templates/base/agents/base/tsq-architect.md +68 -0
  170. package/templates/base/agents/base/tsq-dba.md +56 -0
  171. package/templates/base/agents/base/tsq-designer.md +72 -0
  172. package/templates/base/agents/base/tsq-developer.md +67 -0
  173. package/templates/base/agents/base/tsq-qa.md +55 -0
  174. package/templates/base/agents/base/tsq-security.md +65 -0
  175. package/templates/base/agents/overlays/domain/general-web/_common.md +11 -0
  176. package/templates/base/agents/overlays/platform/claude-code.md +12 -0
  177. package/templates/base/config.template.yaml +213 -0
  178. package/templates/base/knowledge/checklists/accessibility.md +37 -0
  179. package/templates/base/knowledge/checklists/architecture-review.md +28 -0
  180. package/templates/base/knowledge/checklists/database-standards.md +84 -0
  181. package/templates/base/knowledge/checklists/design-reference.md +97 -0
  182. package/templates/base/knowledge/checklists/security.md +50 -0
  183. package/templates/base/knowledge/checklists/ssot-validation.md +19 -0
  184. package/templates/base/knowledge/domains/_template.md +16 -0
  185. package/templates/base/knowledge/platforms/_template.md +16 -0
  186. package/templates/base/knowledge/templates/sequence-report.md +44 -0
  187. package/templates/base/knowledge/templates/task-result.md +105 -0
  188. package/templates/base/skills/_template/SKILL.md +59 -0
  189. package/templates/base/skills/_template/references/_template.md +35 -0
  190. package/templates/base/skills/_template/rules/_sections.md +34 -0
  191. package/templates/base/skills/_template/rules/_template.md +32 -0
  192. package/templates/base/skills/_template/scripts/_template.sh +31 -0
  193. package/templates/base/skills/architecture/SKILL.md +54 -0
  194. package/templates/base/skills/architecture/references/adr-template.md +50 -0
  195. package/templates/base/skills/architecture/references/api-design.md +64 -0
  196. package/templates/base/skills/backend/node/SKILL.md +81 -0
  197. package/templates/base/skills/backend/node/rules/async-patterns.md +81 -0
  198. package/templates/base/skills/backend/node/rules/deployment.md +33 -0
  199. package/templates/base/skills/backend/node/rules/env-config.md +41 -0
  200. package/templates/base/skills/backend/node/rules/error-handling.md +83 -0
  201. package/templates/base/skills/backend/node/rules/hono-app-setup.md +98 -0
  202. package/templates/base/skills/backend/node/rules/jwt-auth.md +76 -0
  203. package/templates/base/skills/backend/node/rules/middleware.md +56 -0
  204. package/templates/base/skills/backend/node/rules/testing.md +82 -0
  205. package/templates/base/skills/coding/SKILL.md +47 -0
  206. package/templates/base/skills/coding/rules/patterns.md +81 -0
  207. package/templates/base/skills/database/SKILL.md +98 -0
  208. package/templates/base/skills/database/prisma/SKILL.md +57 -0
  209. package/templates/base/skills/database/prisma/rules/queries.md +133 -0
  210. package/templates/base/skills/database/prisma/rules/schema-design.md +80 -0
  211. package/templates/base/skills/frontend/nextjs/SKILL.md +59 -0
  212. package/templates/base/skills/frontend/nextjs/rules/app-router.md +138 -0
  213. package/templates/base/skills/frontend/react/SKILL.md +86 -0
  214. package/templates/base/skills/frontend/react/rules/_sections.md +88 -0
  215. package/templates/base/skills/frontend/react/rules/anti-patterns.md +67 -0
  216. package/templates/base/skills/frontend/react/rules/async-api-routes.md +38 -0
  217. package/templates/base/skills/frontend/react/rules/async-defer-await.md +80 -0
  218. package/templates/base/skills/frontend/react/rules/async-dependencies.md +36 -0
  219. package/templates/base/skills/frontend/react/rules/async-parallel.md +28 -0
  220. package/templates/base/skills/frontend/react/rules/async-suspense-boundaries.md +99 -0
  221. package/templates/base/skills/frontend/react/rules/bundle-barrel-imports.md +59 -0
  222. package/templates/base/skills/frontend/react/rules/bundle-defer-third-party.md +49 -0
  223. package/templates/base/skills/frontend/react/rules/bundle-dynamic-imports.md +35 -0
  224. package/templates/base/skills/frontend/react/rules/component-conventions.md +74 -0
  225. package/templates/base/skills/frontend/react/rules/js-combine-iterations.md +32 -0
  226. package/templates/base/skills/frontend/react/rules/js-early-exit.md +50 -0
  227. package/templates/base/skills/frontend/react/rules/js-index-maps.md +37 -0
  228. package/templates/base/skills/frontend/react/rules/js-set-map-lookups.md +24 -0
  229. package/templates/base/skills/frontend/react/rules/rendering-conditional-render.md +40 -0
  230. package/templates/base/skills/frontend/react/rules/rendering-content-visibility.md +38 -0
  231. package/templates/base/skills/frontend/react/rules/rendering-hoist-jsx.md +46 -0
  232. package/templates/base/skills/frontend/react/rules/rerender-defer-reads.md +39 -0
  233. package/templates/base/skills/frontend/react/rules/rerender-derived-state.md +29 -0
  234. package/templates/base/skills/frontend/react/rules/rerender-memo.md +44 -0
  235. package/templates/base/skills/frontend/react/rules/rerender-transitions.md +40 -0
  236. package/templates/base/skills/frontend/react/rules/server-after-nonblocking.md +73 -0
  237. package/templates/base/skills/frontend/react/rules/server-cache-react.md +26 -0
  238. package/templates/base/skills/frontend/react/rules/server-parallel-fetching.md +79 -0
  239. package/templates/base/skills/frontend/react/rules/state-location.md +55 -0
  240. package/templates/base/skills/methodology/bdd/SKILL.md +69 -0
  241. package/templates/base/skills/methodology/bdd/rules/gherkin-patterns.md +113 -0
  242. package/templates/base/skills/methodology/ddd/SKILL.md +74 -0
  243. package/templates/base/skills/methodology/ddd/rules/strategic-patterns.md +98 -0
  244. package/templates/base/skills/methodology/debugging/SKILL.md +60 -0
  245. package/templates/base/skills/methodology/debugging/references/root-cause-tracing.md +84 -0
  246. package/templates/base/skills/methodology/tdd/SKILL.md +66 -0
  247. package/templates/base/skills/methodology/tdd/rules/real-world-example.md +88 -0
  248. package/templates/base/skills/methodology/tdd/rules/techniques.md +185 -0
  249. package/templates/base/skills/planning/SKILL.md +58 -0
  250. package/templates/base/skills/planning/references/prd-guide.md +47 -0
  251. package/templates/base/skills/planning/references/requirements-guide.md +46 -0
  252. package/templates/base/skills/prompt-engineering/SKILL.md +103 -0
  253. package/templates/base/skills/retrospective/SKILL.md +102 -0
  254. package/templates/base/skills/security/SKILL.md +55 -0
  255. package/templates/base/skills/security/rules/owasp-examples.md +119 -0
  256. package/templates/base/skills/security/scripts/check-secrets.sh +55 -0
  257. package/templates/base/skills/testing/SKILL.md +63 -0
  258. package/templates/base/skills/testing/references/testing-patterns.md +103 -0
  259. package/templates/base/skills/tsq-protocol/SKILL.md +51 -0
  260. package/templates/base/skills/typescript/SKILL.md +67 -0
  261. package/templates/base/skills/typescript/rules/type-patterns.md +135 -0
  262. package/templates/base/skills/typescript/rules/utility-types.md +76 -0
  263. package/templates/base/skills/ui-design/SKILL.md +70 -0
  264. package/templates/{common → base}/timsquad/feedback/routing-rules.yaml +1 -1
  265. package/templates/base/timsquad/process/phase-checklist.yaml +174 -0
  266. package/templates/{common → base}/timsquad/process/state-machine.xml +12 -0
  267. package/templates/{common → base}/timsquad/process/workflow-base.xml +124 -0
  268. package/templates/{common → base}/timsquad/retrospective/metrics/metrics-schema.json +46 -1
  269. package/templates/platforms/claude-code/CLAUDE.md.template +64 -0
  270. package/templates/platforms/claude-code/rules/adr-rules.md +32 -0
  271. package/templates/platforms/claude-code/rules/feedback-routing.md +18 -0
  272. package/templates/platforms/claude-code/rules/phase-management.md +23 -0
  273. package/templates/platforms/claude-code/rules/reporting-format.md +26 -0
  274. package/templates/platforms/claude-code/rules/sequence-management.md +72 -0
  275. package/templates/platforms/claude-code/rules/workspace-sync.md +33 -0
  276. package/templates/platforms/claude-code/settings.json +26 -0
  277. package/templates/project-types/api-backend/config.yaml +227 -0
  278. package/templates/project-types/api-backend/process/workflow.xml +214 -0
  279. package/templates/project-types/fintech/config.yaml +151 -0
  280. package/templates/project-types/fintech/process/workflow.xml +316 -0
  281. package/templates/project-types/infra/config.yaml +327 -0
  282. package/templates/project-types/infra/process/workflow.xml +296 -0
  283. package/templates/project-types/platform/config.yaml +254 -0
  284. package/templates/project-types/platform/process/workflow.xml +254 -0
  285. package/templates/project-types/web-app/config.yaml +198 -0
  286. package/templates/project-types/web-app/process/workflow.xml +210 -0
  287. package/templates/project-types/web-service/config.yaml +136 -0
  288. package/templates/project-types/web-service/process/workflow.xml +184 -0
  289. package/templates/common/CLAUDE.md.template +0 -254
  290. package/templates/common/claude/agents/tsq-dba.md +0 -290
  291. package/templates/common/claude/agents/tsq-designer.md +0 -304
  292. package/templates/common/claude/agents/tsq-developer.md +0 -118
  293. package/templates/common/claude/agents/tsq-planner.md +0 -90
  294. package/templates/common/claude/agents/tsq-prompter.md +0 -336
  295. package/templates/common/claude/agents/tsq-qa.md +0 -134
  296. package/templates/common/claude/agents/tsq-retro.md +0 -168
  297. package/templates/common/claude/agents/tsq-security.md +0 -190
  298. package/templates/common/claude/skills/architecture/SKILL.md +0 -123
  299. package/templates/common/claude/skills/backend/node/SKILL.md +0 -1015
  300. package/templates/common/claude/skills/coding/SKILL.md +0 -171
  301. package/templates/common/claude/skills/database/prisma/SKILL.md +0 -357
  302. package/templates/common/claude/skills/frontend/nextjs/SKILL.md +0 -279
  303. package/templates/common/claude/skills/frontend/react/SKILL.md +0 -1729
  304. package/templates/common/claude/skills/methodology/bdd/SKILL.md +0 -234
  305. package/templates/common/claude/skills/methodology/ddd/SKILL.md +0 -311
  306. package/templates/common/claude/skills/methodology/tdd/SKILL.md +0 -512
  307. package/templates/common/claude/skills/planning/SKILL.md +0 -90
  308. package/templates/common/claude/skills/security/SKILL.md +0 -234
  309. package/templates/common/claude/skills/testing/SKILL.md +0 -146
  310. package/templates/common/claude/skills/typescript/SKILL.md +0 -435
  311. package/templates/common/config.template.yaml +0 -131
  312. /package/templates/{common → base}/timsquad/architectures/clean/ARCHITECTURE.md +0 -0
  313. /package/templates/{common → base}/timsquad/architectures/clean/backend.xml +0 -0
  314. /package/templates/{common → base}/timsquad/architectures/clean/frontend.xml +0 -0
  315. /package/templates/{common → base}/timsquad/architectures/fsd/ARCHITECTURE.md +0 -0
  316. /package/templates/{common → base}/timsquad/architectures/fsd/frontend.xml +0 -0
  317. /package/templates/{common → base}/timsquad/architectures/hexagonal/ARCHITECTURE.md +0 -0
  318. /package/templates/{common → base}/timsquad/architectures/hexagonal/backend.xml +0 -0
  319. /package/templates/{common → base}/timsquad/constraints/competency-framework.xml +0 -0
  320. /package/templates/{common → base}/timsquad/constraints/ssot-schema.xml +0 -0
  321. /package/templates/{common → base}/timsquad/feedback/feedback-router.sh +0 -0
  322. /package/templates/{common → base}/timsquad/generators/data-design.xml +0 -0
  323. /package/templates/{common → base}/timsquad/generators/prd.xml +0 -0
  324. /package/templates/{common → base}/timsquad/generators/requirements.xml +0 -0
  325. /package/templates/{common → base}/timsquad/generators/service-spec.xml +0 -0
  326. /package/templates/{common → base}/timsquad/logs/_example.md +0 -0
  327. /package/templates/{common → base}/timsquad/logs/_template.md +0 -0
  328. /package/templates/{common → base}/timsquad/patterns/cqrs.xml +0 -0
  329. /package/templates/{common → base}/timsquad/patterns/event-sourcing.xml +0 -0
  330. /package/templates/{common → base}/timsquad/patterns/repository.xml +0 -0
  331. /package/templates/{common → base}/timsquad/process/validation-rules.xml +0 -0
  332. /package/templates/{common → base}/timsquad/retrospective/cycle-report.template.md +0 -0
  333. /package/templates/{common → base}/timsquad/retrospective/patterns/failure-patterns.md +0 -0
  334. /package/templates/{common → base}/timsquad/retrospective/patterns/success-patterns.md +0 -0
  335. /package/templates/{common → base}/timsquad/retrospective/retrospective-config.xml +0 -0
  336. /package/templates/{common → base}/timsquad/retrospective/retrospective-state.xml +0 -0
  337. /package/templates/{common → base}/timsquad/ssot/adr/ADR-000-template.md +0 -0
  338. /package/templates/{common → base}/timsquad/ssot/adr/ADR-001-example.md +0 -0
  339. /package/templates/{common → base}/timsquad/ssot/data-design.template.md +0 -0
  340. /package/templates/{common → base}/timsquad/ssot/deployment-spec.template.md +0 -0
  341. /package/templates/{common → base}/timsquad/ssot/env-config.template.md +0 -0
  342. /package/templates/{common → base}/timsquad/ssot/error-codes.template.md +0 -0
  343. /package/templates/{common → base}/timsquad/ssot/functional-spec.template.md +0 -0
  344. /package/templates/{common → base}/timsquad/ssot/glossary.template.md +0 -0
  345. /package/templates/{common → base}/timsquad/ssot/integration-spec.template.md +0 -0
  346. /package/templates/{common → base}/timsquad/ssot/planning.template.md +0 -0
  347. /package/templates/{common → base}/timsquad/ssot/prd.template.md +0 -0
  348. /package/templates/{common → base}/timsquad/ssot/requirements.template.md +0 -0
  349. /package/templates/{common → base}/timsquad/ssot/security-spec.template.md +0 -0
  350. /package/templates/{common → base}/timsquad/ssot/service-spec.template.md +0 -0
  351. /package/templates/{common → base}/timsquad/ssot/test-spec.template.md +0 -0
  352. /package/templates/{common → base}/timsquad/ssot/ui-ux-spec.template.md +0 -0
  353. /package/templates/{common → base}/timsquad/state/workspace.xml +0 -0
@@ -1,9 +1,12 @@
1
1
  import path from 'path';
2
- import { colors, printHeader, printError, printSuccess } from '../utils/colors.js';
2
+ import fs from 'fs-extra';
3
+ import { colors, printHeader, printError, printSuccess, printKeyValue } from '../utils/colors.js';
3
4
  import { findProjectRoot } from '../lib/project.js';
4
5
  import { exists, readFile, writeFile, listFiles } from '../utils/fs.js';
5
- import { getDateString, getTimeString } from '../utils/date.js';
6
+ import { getDateString, getTimeString, getTimestamp } from '../utils/date.js';
7
+ import { loadWorkflowState } from '../lib/workflow-state.js';
6
8
  const LOG_TYPES = ['work', 'decision', 'error', 'feedback', 'handoff'];
9
+ const LOG_SCHEMA_VERSION = '1.0.0';
7
10
  export function registerLogCommand(program) {
8
11
  const logCmd = program
9
12
  .command('log')
@@ -86,6 +89,188 @@ export function registerLogCommand(program) {
86
89
  process.exit(1);
87
90
  }
88
91
  });
92
+ // tsq log compact
93
+ logCmd
94
+ .command('compact')
95
+ .description('Compress old logs (session JSONL → summary, merge old md)')
96
+ .option('--days <n>', 'Keep logs newer than N days', '7')
97
+ .option('--dry-run', 'Show what would be compacted without changes')
98
+ .action(async (options) => {
99
+ try {
100
+ await compactLogs(parseInt(options.days, 10), !!options.dryRun);
101
+ }
102
+ catch (error) {
103
+ printError(error.message);
104
+ process.exit(1);
105
+ }
106
+ });
107
+ // ── tsq log enrich <agent> ──
108
+ logCmd
109
+ .command('enrich <agent>')
110
+ .description('Enrich latest task log with semantic data')
111
+ .requiredOption('--json <data>', 'Semantic data as JSON string')
112
+ .action(async (agent, options) => {
113
+ try {
114
+ await enrichTaskLog(agent, options.json);
115
+ }
116
+ catch (error) {
117
+ printError(error.message);
118
+ process.exit(1);
119
+ }
120
+ });
121
+ // ── tsq log task <subcommand> ──
122
+ const taskCmd = logCmd
123
+ .command('task')
124
+ .description('L1 Task log management');
125
+ taskCmd
126
+ .command('list')
127
+ .description('List task logs')
128
+ .option('--agent <name>', 'Filter by agent')
129
+ .action(async (options) => {
130
+ try {
131
+ await listTaskLogs(options.agent);
132
+ }
133
+ catch (error) {
134
+ printError(error.message);
135
+ process.exit(1);
136
+ }
137
+ });
138
+ taskCmd
139
+ .command('view <file>')
140
+ .description('View a task log')
141
+ .action(async (file) => {
142
+ try {
143
+ await viewTaskLog(file);
144
+ }
145
+ catch (error) {
146
+ printError(error.message);
147
+ process.exit(1);
148
+ }
149
+ });
150
+ taskCmd
151
+ .command('stats')
152
+ .description('Task log statistics')
153
+ .action(async () => {
154
+ try {
155
+ await showTaskStats();
156
+ }
157
+ catch (error) {
158
+ printError(error.message);
159
+ process.exit(1);
160
+ }
161
+ });
162
+ // ── tsq log sequence <subcommand> ──
163
+ const seqCmd = logCmd
164
+ .command('sequence')
165
+ .description('L2 Sequence log management');
166
+ seqCmd
167
+ .command('list')
168
+ .description('List sequence logs')
169
+ .action(async () => {
170
+ try {
171
+ await listSequenceLogs();
172
+ }
173
+ catch (error) {
174
+ printError(error.message);
175
+ process.exit(1);
176
+ }
177
+ });
178
+ seqCmd
179
+ .command('view <seq-id>')
180
+ .description('View a sequence log')
181
+ .action(async (seqId) => {
182
+ try {
183
+ await viewSequenceLog(seqId);
184
+ }
185
+ catch (error) {
186
+ printError(error.message);
187
+ process.exit(1);
188
+ }
189
+ });
190
+ seqCmd
191
+ .command('create <seq-id>')
192
+ .description('Create sequence log from task logs')
193
+ .requiredOption('--phase <id>', 'Phase ID')
194
+ .requiredOption('--report <path>', 'Architect report path')
195
+ .option('--verdict <v>', 'proceed or hold', 'proceed')
196
+ .option('--conditions <c>', 'Comma-separated conditions')
197
+ .action(async (seqId, options) => {
198
+ try {
199
+ await createSequenceLog(seqId, options);
200
+ }
201
+ catch (error) {
202
+ printError(error.message);
203
+ process.exit(1);
204
+ }
205
+ });
206
+ seqCmd
207
+ .command('check <seq-id>')
208
+ .description('Check task log completeness for a sequence')
209
+ .action(async (seqId) => {
210
+ try {
211
+ await checkSequence(seqId);
212
+ }
213
+ catch (error) {
214
+ printError(error.message);
215
+ process.exit(1);
216
+ }
217
+ });
218
+ // ── tsq log phase <subcommand> ──
219
+ const phaseCmd = logCmd
220
+ .command('phase')
221
+ .description('L3 Phase log management');
222
+ phaseCmd
223
+ .command('list')
224
+ .description('List phase logs')
225
+ .action(async () => {
226
+ try {
227
+ await listPhaseLogs();
228
+ }
229
+ catch (error) {
230
+ printError(error.message);
231
+ process.exit(1);
232
+ }
233
+ });
234
+ phaseCmd
235
+ .command('view <phase-id>')
236
+ .description('View a phase log')
237
+ .action(async (phaseId) => {
238
+ try {
239
+ await viewPhaseLog(phaseId);
240
+ }
241
+ catch (error) {
242
+ printError(error.message);
243
+ process.exit(1);
244
+ }
245
+ });
246
+ phaseCmd
247
+ .command('create <phase-id>')
248
+ .description('Create phase log')
249
+ .requiredOption('--sequences <ids>', 'Comma-separated sequence IDs')
250
+ .option('--retro-keep <items>', 'Retrospective: keep (comma-separated)')
251
+ .option('--retro-problem <items>', 'Retrospective: problems (comma-separated)')
252
+ .option('--retro-try <items>', 'Retrospective: try (comma-separated)')
253
+ .action(async (phaseId, options) => {
254
+ try {
255
+ await createPhaseLog(phaseId, options);
256
+ }
257
+ catch (error) {
258
+ printError(error.message);
259
+ process.exit(1);
260
+ }
261
+ });
262
+ phaseCmd
263
+ .command('gate <phase-id>')
264
+ .description('Check phase transition gate')
265
+ .action(async (phaseId) => {
266
+ try {
267
+ await checkPhaseGate(phaseId);
268
+ }
269
+ catch (error) {
270
+ printError(error.message);
271
+ process.exit(1);
272
+ }
273
+ });
89
274
  }
90
275
  async function addLog(agent, type, message) {
91
276
  const projectRoot = await findProjectRoot();
@@ -268,4 +453,984 @@ async function readFromStdin() {
268
453
  setTimeout(() => resolve(''), 100);
269
454
  });
270
455
  }
456
+ async function compactLogs(days, dryRun) {
457
+ const projectRoot = await findProjectRoot();
458
+ if (!projectRoot) {
459
+ throw new Error('Not a TimSquad project');
460
+ }
461
+ printHeader('Log Compact');
462
+ printKeyValue('Keep newer than', `${days} days`);
463
+ if (dryRun) {
464
+ console.log(colors.warning('DRY RUN - no files will be modified\n'));
465
+ }
466
+ const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
467
+ const cutoffStr = cutoffDate.toISOString().split('T')[0];
468
+ let jsonlCompacted = 0;
469
+ let mdMerged = 0;
470
+ let bytesFreed = 0;
471
+ // ── 1. 세션 JSONL 압축 ──
472
+ const sessionsDir = path.join(projectRoot, '.timsquad', 'logs', 'sessions');
473
+ if (await exists(sessionsDir)) {
474
+ const jsonlFiles = await listFiles('*.jsonl', sessionsDir);
475
+ const oldJsonl = jsonlFiles.filter(f => {
476
+ const dateStr = f.split('-').slice(0, 3).join('-');
477
+ return dateStr < cutoffStr;
478
+ });
479
+ if (oldJsonl.length > 0) {
480
+ console.log(colors.subheader(`\n Session JSONL: ${oldJsonl.length} files to compact`));
481
+ const summaries = [];
482
+ for (const file of oldJsonl) {
483
+ const filePath = path.join(sessionsDir, file);
484
+ const stat = await fs.stat(filePath);
485
+ bytesFreed += stat.size;
486
+ try {
487
+ const content = await readFile(filePath);
488
+ const lines = content.split('\n').filter(l => l.trim());
489
+ let toolUses = 0;
490
+ let failures = 0;
491
+ let subagents = 0;
492
+ let tokenInput = 0;
493
+ let tokenOutput = 0;
494
+ let cacheCreate = 0;
495
+ let cacheRead = 0;
496
+ for (const line of lines) {
497
+ try {
498
+ const ev = JSON.parse(line);
499
+ if (ev.event === 'PostToolUse')
500
+ toolUses++;
501
+ if (ev.event === 'PostToolUseFailure')
502
+ failures++;
503
+ if (ev.event === 'SubagentStart')
504
+ subagents++;
505
+ if (ev.event === 'SessionEnd' && ev.total_usage) {
506
+ tokenInput += ev.total_usage.total_input || 0;
507
+ tokenOutput += ev.total_usage.total_output || 0;
508
+ cacheCreate += ev.total_usage.total_cache_create || 0;
509
+ cacheRead += ev.total_usage.total_cache_read || 0;
510
+ }
511
+ }
512
+ catch { /* skip */ }
513
+ }
514
+ const dateStr = file.split('-').slice(0, 3).join('-');
515
+ const sessionId = file.replace(/\.jsonl$/, '').split('-').slice(3).join('-');
516
+ summaries.push({
517
+ file,
518
+ date: dateStr,
519
+ session: sessionId,
520
+ events: lines.length,
521
+ toolUses,
522
+ failures,
523
+ subagents,
524
+ tokens: { input: tokenInput, output: tokenOutput, cacheCreate, cacheRead },
525
+ });
526
+ if (!dryRun) {
527
+ await fs.remove(filePath);
528
+ }
529
+ jsonlCompacted++;
530
+ console.log(colors.dim(` ${dryRun ? '[dry-run] ' : ''}${file} (${lines.length} events, ${formatSize(stat.size)})`));
531
+ }
532
+ catch {
533
+ // skip unreadable files
534
+ }
535
+ }
536
+ // 압축 요약 저장
537
+ if (!dryRun && summaries.length > 0) {
538
+ const archiveDir = path.join(sessionsDir, 'archive');
539
+ await fs.ensureDir(archiveDir);
540
+ const monthGroups = {};
541
+ for (const s of summaries) {
542
+ const month = s.date.slice(0, 7); // YYYY-MM
543
+ if (!monthGroups[month])
544
+ monthGroups[month] = [];
545
+ monthGroups[month].push(s);
546
+ }
547
+ for (const [month, group] of Object.entries(monthGroups)) {
548
+ const archivePath = path.join(archiveDir, `${month}-summary.json`);
549
+ let existing = [];
550
+ if (await exists(archivePath)) {
551
+ try {
552
+ const data = await fs.readJson(archivePath);
553
+ existing = data.sessions || [];
554
+ }
555
+ catch { /* start fresh */ }
556
+ }
557
+ await fs.writeJson(archivePath, {
558
+ compactedAt: getTimestamp(),
559
+ month,
560
+ sessions: [...existing, ...group],
561
+ totals: {
562
+ sessions: existing.length + group.length,
563
+ events: [...existing, ...group].reduce((a, s) => a + s.events, 0),
564
+ toolUses: [...existing, ...group].reduce((a, s) => a + s.toolUses, 0),
565
+ failures: [...existing, ...group].reduce((a, s) => a + s.failures, 0),
566
+ },
567
+ }, { spaces: 2 });
568
+ }
569
+ }
570
+ }
571
+ }
572
+ // ── 2. 작업 로그 MD 병합 ──
573
+ const logsDir = path.join(projectRoot, '.timsquad', 'logs');
574
+ if (await exists(logsDir)) {
575
+ const mdFiles = await listFiles('????-??-??-*.md', logsDir);
576
+ const oldMd = mdFiles.filter(f => {
577
+ const match = f.match(/^(\d{4}-\d{2}-\d{2})-/);
578
+ return match && match[1] < cutoffStr;
579
+ });
580
+ if (oldMd.length > 0) {
581
+ console.log(colors.subheader(`\n Work Logs MD: ${oldMd.length} files to merge`));
582
+ // 월별 그룹핑
583
+ const monthGroups = {};
584
+ for (const file of oldMd) {
585
+ const month = file.slice(0, 7);
586
+ if (!monthGroups[month])
587
+ monthGroups[month] = [];
588
+ monthGroups[month].push(file);
589
+ }
590
+ for (const [month, files] of Object.entries(monthGroups)) {
591
+ const mergedPath = path.join(logsDir, `${month}-archive.md`);
592
+ let mergedContent = '';
593
+ if (await exists(mergedPath)) {
594
+ mergedContent = await readFile(mergedPath);
595
+ }
596
+ else {
597
+ mergedContent = `# Archive - ${month}\n\n> ${files.length} log files merged by tsq log compact\n\n---\n`;
598
+ }
599
+ for (const file of files.sort()) {
600
+ const filePath = path.join(logsDir, file);
601
+ const stat = await fs.stat(filePath);
602
+ bytesFreed += stat.size;
603
+ try {
604
+ const content = await readFile(filePath);
605
+ mergedContent += `\n\n<!-- ${file} -->\n${content}`;
606
+ if (!dryRun) {
607
+ await fs.remove(filePath);
608
+ }
609
+ mdMerged++;
610
+ console.log(colors.dim(` ${dryRun ? '[dry-run] ' : ''}${file} → ${month}-archive.md`));
611
+ }
612
+ catch { /* skip */ }
613
+ }
614
+ if (!dryRun) {
615
+ await writeFile(mergedPath, mergedContent);
616
+ }
617
+ }
618
+ }
619
+ }
620
+ // ── 결과 ──
621
+ console.log('');
622
+ if (jsonlCompacted + mdMerged === 0) {
623
+ console.log(colors.dim('No files older than threshold. Nothing to compact.'));
624
+ }
625
+ else {
626
+ printSuccess(`Compacted: ${jsonlCompacted} JSONL + ${mdMerged} MD files`);
627
+ printKeyValue('Space freed', `~${formatSize(bytesFreed)}`);
628
+ if (dryRun) {
629
+ console.log(colors.dim('\nRun without --dry-run to apply changes.'));
630
+ }
631
+ }
632
+ }
633
+ function formatSize(bytes) {
634
+ if (bytes < 1024)
635
+ return `${bytes}B`;
636
+ if (bytes < 1024 * 1024)
637
+ return `${(bytes / 1024).toFixed(1)}KB`;
638
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
639
+ }
640
+ // ============================================================
641
+ // Task Log Helpers
642
+ // ============================================================
643
+ export function getTasksDir(projectRoot) {
644
+ return path.join(projectRoot, '.timsquad', 'logs', 'tasks');
645
+ }
646
+ export function getSequencesDir(projectRoot) {
647
+ return path.join(projectRoot, '.timsquad', 'logs', 'sequences');
648
+ }
649
+ export function getPhasesDir(projectRoot) {
650
+ return path.join(projectRoot, '.timsquad', 'logs', 'phases');
651
+ }
652
+ async function findLatestTaskLog(projectRoot, agent) {
653
+ const tasksDir = getTasksDir(projectRoot);
654
+ if (!await exists(tasksDir))
655
+ return null;
656
+ // Flat structure: *-{agent}.json
657
+ const flatFiles = await listFiles(`*-${agent}.json`, tasksDir);
658
+ // Nested structure: look inside subdirectories
659
+ const nestedFiles = [];
660
+ try {
661
+ const entries = await fs.readdir(tasksDir, { withFileTypes: true });
662
+ for (const entry of entries) {
663
+ if (entry.isDirectory()) {
664
+ const subFiles = await listFiles(`*-${agent}.json`, path.join(tasksDir, entry.name));
665
+ nestedFiles.push(...subFiles.map(f => path.join(entry.name, f)));
666
+ }
667
+ }
668
+ }
669
+ catch { /* no subdirectories */ }
670
+ const allFiles = [...flatFiles, ...nestedFiles].sort().reverse();
671
+ return allFiles.length > 0 ? allFiles[0] : null;
672
+ }
673
+ export async function loadAllTaskLogs(projectRoot, agent) {
674
+ const tasksDir = getTasksDir(projectRoot);
675
+ if (!await exists(tasksDir))
676
+ return [];
677
+ const pattern = agent ? `*-${agent}.json` : '*.json';
678
+ const files = await listFiles(pattern, tasksDir);
679
+ const results = [];
680
+ for (const file of files) {
681
+ try {
682
+ const data = await fs.readJson(path.join(tasksDir, file));
683
+ results.push({ file, data });
684
+ }
685
+ catch { /* skip malformed */ }
686
+ }
687
+ return results.sort((a, b) => a.file.localeCompare(b.file));
688
+ }
689
+ function mergeSemantic(existing, incoming) {
690
+ return {
691
+ summary: incoming.summary ?? existing.summary,
692
+ techniques: incoming.techniques ?? existing.techniques,
693
+ ssot_refs: incoming.ssot_refs ?? existing.ssot_refs,
694
+ decisions: incoming.decisions ?? existing.decisions,
695
+ issues: incoming.issues ?? existing.issues,
696
+ };
697
+ }
698
+ export function hasSemantic(sem) {
699
+ if (!sem)
700
+ return false;
701
+ return !!(sem.summary || sem.techniques?.length || sem.ssot_refs?.length ||
702
+ sem.decisions?.length || sem.issues?.length);
703
+ }
704
+ // ============================================================
705
+ // Step 2: tsq log enrich
706
+ // ============================================================
707
+ async function enrichTaskLog(agent, jsonData) {
708
+ const projectRoot = await findProjectRoot();
709
+ if (!projectRoot)
710
+ throw new Error('Not a TimSquad project');
711
+ const latestFile = await findLatestTaskLog(projectRoot, agent);
712
+ if (!latestFile) {
713
+ throw new Error(`No task log found for agent: ${agent}`);
714
+ }
715
+ const filePath = path.join(getTasksDir(projectRoot), latestFile);
716
+ let incoming;
717
+ try {
718
+ incoming = JSON.parse(jsonData);
719
+ }
720
+ catch {
721
+ throw new Error('Invalid JSON data');
722
+ }
723
+ const taskLog = await fs.readJson(filePath);
724
+ taskLog.semantic = mergeSemantic(taskLog.semantic || {}, incoming);
725
+ await fs.writeJson(filePath, taskLog, { spaces: 2 });
726
+ printSuccess(`Enriched: ${latestFile}`);
727
+ if (taskLog.semantic.summary) {
728
+ printKeyValue('Summary', taskLog.semantic.summary);
729
+ }
730
+ if (taskLog.semantic.techniques?.length) {
731
+ printKeyValue('Techniques', taskLog.semantic.techniques.map(t => t.name).join(', '));
732
+ }
733
+ // Auto-feedback: L2/L3 이슈 → 자동 피드백 라우팅
734
+ await autoFeedbackFromIssues(projectRoot, taskLog.semantic);
735
+ }
736
+ // ============================================================
737
+ // Step 3: tsq log task (L1 views)
738
+ // ============================================================
739
+ async function listTaskLogs(agent) {
740
+ const projectRoot = await findProjectRoot();
741
+ if (!projectRoot)
742
+ throw new Error('Not a TimSquad project');
743
+ const logs = await loadAllTaskLogs(projectRoot, agent);
744
+ if (logs.length === 0) {
745
+ console.log(colors.dim('No task logs found'));
746
+ return;
747
+ }
748
+ printHeader(`Task Logs (${logs.length})`);
749
+ for (const { file, data } of logs.reverse()) {
750
+ const sem = hasSemantic(data.semantic) ? '✓' : '○';
751
+ const status = data.status === 'completed' ? colors.success('✓') : colors.error('✗');
752
+ const agentName = colors.agent(data.agent || '?');
753
+ const date = file.slice(0, 10);
754
+ console.log(` ${status} ${sem} ${colors.dim(date)} ${agentName} ${colors.dim(file)}`);
755
+ if (data.semantic?.summary) {
756
+ console.log(` ${colors.dim(data.semantic.summary.slice(0, 80))}`);
757
+ }
758
+ }
759
+ }
760
+ async function viewTaskLog(file) {
761
+ const projectRoot = await findProjectRoot();
762
+ if (!projectRoot)
763
+ throw new Error('Not a TimSquad project');
764
+ const filePath = path.join(getTasksDir(projectRoot), file);
765
+ if (!await exists(filePath)) {
766
+ throw new Error(`Task log not found: ${file}`);
767
+ }
768
+ const data = await fs.readJson(filePath);
769
+ printHeader(`Task Log: ${file}`);
770
+ // Basic info
771
+ printKeyValue('Agent', data.agent);
772
+ printKeyValue('Status', data.status);
773
+ printKeyValue('Completed', data.completed_at || 'N/A');
774
+ if (data.duration_ms)
775
+ printKeyValue('Duration', `${(data.duration_ms / 1000).toFixed(1)}s`);
776
+ // Mechanical
777
+ console.log(colors.subheader('\n Mechanical'));
778
+ if (data.mechanical?.files?.length) {
779
+ for (const f of data.mechanical.files) {
780
+ console.log(` ${f.action}: ${colors.path(f.path)}`);
781
+ }
782
+ }
783
+ else {
784
+ console.log(colors.dim(' (no file changes)'));
785
+ }
786
+ // Semantic
787
+ console.log(colors.subheader('\n Semantic'));
788
+ if (hasSemantic(data.semantic)) {
789
+ if (data.semantic.summary)
790
+ printKeyValue(' Summary', data.semantic.summary);
791
+ if (data.semantic.techniques?.length) {
792
+ console.log(' Techniques:');
793
+ for (const t of data.semantic.techniques) {
794
+ console.log(` - ${t.name}: ${colors.dim(t.reason)}`);
795
+ }
796
+ }
797
+ if (data.semantic.ssot_refs?.length) {
798
+ console.log(' SSOT Refs:');
799
+ for (const r of data.semantic.ssot_refs) {
800
+ const icon = r.status === 'aligned' ? '✓' : r.status === 'misaligned' ? '✗' : '~';
801
+ console.log(` ${icon} ${r.doc} → ${r.section} (${r.status})`);
802
+ }
803
+ }
804
+ if (data.semantic.decisions?.length) {
805
+ console.log(' Decisions:');
806
+ for (const d of data.semantic.decisions) {
807
+ console.log(` - ${d.decision}`);
808
+ console.log(` ${colors.dim(d.rationale)}`);
809
+ }
810
+ }
811
+ if (data.semantic.issues?.length) {
812
+ console.log(' Issues:');
813
+ for (const i of data.semantic.issues) {
814
+ console.log(` L${i.level}: ${i.description}`);
815
+ }
816
+ }
817
+ }
818
+ else {
819
+ console.log(colors.dim(' (empty — run tsq log enrich to populate)'));
820
+ }
821
+ }
822
+ async function showTaskStats() {
823
+ const projectRoot = await findProjectRoot();
824
+ if (!projectRoot)
825
+ throw new Error('Not a TimSquad project');
826
+ const logs = await loadAllTaskLogs(projectRoot);
827
+ if (logs.length === 0) {
828
+ console.log(colors.dim('No task logs found'));
829
+ return;
830
+ }
831
+ const stats = {
832
+ total: logs.length,
833
+ completed: 0, failed: 0, successRate: 0,
834
+ byAgent: {},
835
+ totalFilesChanged: 0, avgFilesPerTask: 0, fileActions: {},
836
+ withErrors: 0, errorTypes: {},
837
+ withSemantic: 0, semanticCoverage: 0,
838
+ };
839
+ for (const { data } of logs) {
840
+ // Status
841
+ if (data.status === 'completed' || data.status === 'success')
842
+ stats.completed++;
843
+ else
844
+ stats.failed++;
845
+ // By agent
846
+ const a = data.agent || 'unknown';
847
+ if (!stats.byAgent[a])
848
+ stats.byAgent[a] = { total: 0, completed: 0, failed: 0 };
849
+ stats.byAgent[a].total++;
850
+ if (data.status === 'completed' || data.status === 'success')
851
+ stats.byAgent[a].completed++;
852
+ else
853
+ stats.byAgent[a].failed++;
854
+ // Files
855
+ const files = data.mechanical?.files || [];
856
+ stats.totalFilesChanged += files.length;
857
+ for (const f of files) {
858
+ stats.fileActions[f.action] = (stats.fileActions[f.action] || 0) + 1;
859
+ }
860
+ // Errors
861
+ if (data.error) {
862
+ stats.withErrors++;
863
+ const t = data.error.type || 'unknown';
864
+ stats.errorTypes[t] = (stats.errorTypes[t] || 0) + 1;
865
+ }
866
+ // Semantic
867
+ if (hasSemantic(data.semantic))
868
+ stats.withSemantic++;
869
+ }
870
+ stats.successRate = Math.round((stats.completed / stats.total) * 100);
871
+ stats.avgFilesPerTask = Math.round((stats.totalFilesChanged / stats.total) * 10) / 10;
872
+ stats.semanticCoverage = Math.round((stats.withSemantic / stats.total) * 100);
873
+ printHeader('Task Log Statistics');
874
+ printKeyValue('Total Tasks', String(stats.total));
875
+ printKeyValue('Success Rate', `${stats.successRate}% (${stats.completed}/${stats.total})`);
876
+ printKeyValue('Semantic Coverage', `${stats.semanticCoverage}% (${stats.withSemantic}/${stats.total})`);
877
+ printKeyValue('Files Changed', `${stats.totalFilesChanged} (avg ${stats.avgFilesPerTask}/task)`);
878
+ if (Object.keys(stats.byAgent).length > 1) {
879
+ console.log(colors.subheader('\n By Agent'));
880
+ for (const [agent, s] of Object.entries(stats.byAgent)) {
881
+ const rate = Math.round((s.completed / s.total) * 100);
882
+ console.log(` ${colors.agent(agent)}: ${s.total} tasks, ${rate}% success`);
883
+ }
884
+ }
885
+ if (stats.withErrors > 0) {
886
+ console.log(colors.subheader('\n Errors'));
887
+ printKeyValue(' Tasks with errors', String(stats.withErrors));
888
+ for (const [type, count] of Object.entries(stats.errorTypes)) {
889
+ console.log(` ${type}: ${count}`);
890
+ }
891
+ }
892
+ }
893
+ // ============================================================
894
+ // Step 4: tsq log sequence (L2)
895
+ // ============================================================
896
+ export async function loadSequenceTaskLogs(projectRoot, seqId) {
897
+ const tasksDir = getTasksDir(projectRoot);
898
+ if (!await exists(tasksDir))
899
+ return [];
900
+ const results = [];
901
+ // 1. Check nested directory: tasks/{SEQ-ID}/*.json
902
+ const seqDir = path.join(tasksDir, seqId);
903
+ if (await exists(seqDir)) {
904
+ const files = await listFiles('*.json', seqDir);
905
+ for (const file of files) {
906
+ try {
907
+ const data = await fs.readJson(path.join(seqDir, file));
908
+ results.push({ file: path.join(seqId, file), data });
909
+ }
910
+ catch { /* skip */ }
911
+ }
912
+ }
913
+ // 2. Check flat files with trace.sequence_id matching
914
+ const flatFiles = await listFiles('*.json', tasksDir);
915
+ for (const file of flatFiles) {
916
+ try {
917
+ const data = await fs.readJson(path.join(tasksDir, file));
918
+ if (data.trace?.sequence_id === seqId) {
919
+ // Avoid duplicates from nested
920
+ if (!results.some(r => r.file === file)) {
921
+ results.push({ file, data });
922
+ }
923
+ }
924
+ }
925
+ catch { /* skip */ }
926
+ }
927
+ return results.sort((a, b) => a.file.localeCompare(b.file));
928
+ }
929
+ function aggregateSequenceStats(taskLogs) {
930
+ let success = 0, failure = 0, error = 0, rework = 0;
931
+ const durations = [];
932
+ for (const { file, data } of taskLogs) {
933
+ const s = data.status;
934
+ if (s === 'completed' || s === 'success')
935
+ success++;
936
+ else if (s === 'failure')
937
+ failure++;
938
+ else if (s === 'error')
939
+ error++;
940
+ if (data.duration_ms)
941
+ durations.push(data.duration_ms);
942
+ // Simple rework detection: same agent + file overlap with earlier task
943
+ const changedFiles = new Set(data.mechanical?.files?.map(f => f.path) || []);
944
+ for (const other of taskLogs) {
945
+ if (other.file === file)
946
+ continue;
947
+ if (other.data.agent === data.agent && other.file < file) {
948
+ const otherFiles = other.data.mechanical?.files?.map(f => f.path) || [];
949
+ if (otherFiles.some(f => changedFiles.has(f))) {
950
+ rework++;
951
+ break;
952
+ }
953
+ }
954
+ }
955
+ }
956
+ const total = taskLogs.length;
957
+ return {
958
+ total, success, failure, error, rework,
959
+ first_pass_success_rate: total > 0 ? Math.round(((success) / total) * 100) / 100 : 0,
960
+ final_success_rate: total > 0 ? Math.round(((success) / total) * 100) / 100 : 0,
961
+ durations,
962
+ };
963
+ }
964
+ export function makeAxisPlaceholder() {
965
+ return { verdict: 'n/a', details: 'See architect report', issues: [] };
966
+ }
967
+ async function listSequenceLogs() {
968
+ const projectRoot = await findProjectRoot();
969
+ if (!projectRoot)
970
+ throw new Error('Not a TimSquad project');
971
+ const seqDir = getSequencesDir(projectRoot);
972
+ if (!await exists(seqDir)) {
973
+ console.log(colors.dim('No sequence logs found'));
974
+ return;
975
+ }
976
+ const files = await listFiles('*.json', seqDir);
977
+ if (files.length === 0) {
978
+ console.log(colors.dim('No sequence logs found'));
979
+ return;
980
+ }
981
+ printHeader(`Sequence Logs (${files.length})`);
982
+ for (const file of files.sort()) {
983
+ try {
984
+ const data = await fs.readJson(path.join(seqDir, file));
985
+ const seqId = data.trace.sequence_id;
986
+ const status = data.execution.status;
987
+ const verdict = data.verdict.proceed ? colors.success('PROCEED') : colors.error('HOLD');
988
+ const tasks = `${data.tasks.success}/${data.tasks.total} tasks`;
989
+ console.log(` ${verdict} ${colors.agent(seqId)} [${status}] ${tasks} ${colors.dim(data.trace.phase_id)}`);
990
+ }
991
+ catch {
992
+ console.log(` ${colors.dim(file)} (unreadable)`);
993
+ }
994
+ }
995
+ }
996
+ async function viewSequenceLog(seqId) {
997
+ const projectRoot = await findProjectRoot();
998
+ if (!projectRoot)
999
+ throw new Error('Not a TimSquad project');
1000
+ const filePath = path.join(getSequencesDir(projectRoot), `${seqId}.json`);
1001
+ if (!await exists(filePath)) {
1002
+ throw new Error(`Sequence log not found: ${seqId}`);
1003
+ }
1004
+ const data = await fs.readJson(filePath);
1005
+ printHeader(`Sequence: ${seqId}`);
1006
+ printKeyValue('Phase', data.trace.phase_id);
1007
+ printKeyValue('Status', data.execution.status);
1008
+ printKeyValue('Duration', `${(data.execution.duration_ms / 1000).toFixed(1)}s`);
1009
+ printKeyValue('Verdict', data.verdict.proceed ? 'PROCEED' : 'HOLD');
1010
+ console.log(colors.subheader('\n Tasks'));
1011
+ printKeyValue(' Total', String(data.tasks.total));
1012
+ printKeyValue(' Success', String(data.tasks.success));
1013
+ printKeyValue(' Failure', String(data.tasks.failure));
1014
+ printKeyValue(' Rework', String(data.tasks.rework));
1015
+ printKeyValue(' Success Rate', `${(data.tasks.final_success_rate * 100).toFixed(0)}%`);
1016
+ console.log(colors.subheader('\n Analysis'));
1017
+ for (const [axis, result] of Object.entries(data.analysis)) {
1018
+ const r = result;
1019
+ const icon = r.verdict === 'pass' ? '✓' : r.verdict === 'fail' ? '✗' : r.verdict === 'warn' ? '!' : '–';
1020
+ console.log(` ${icon} ${axis}: ${r.verdict} — ${colors.dim(r.details)}`);
1021
+ }
1022
+ console.log(colors.subheader('\n DORA'));
1023
+ printKeyValue(' Change Failure Rate', `${(data.dora_derived.change_failure_rate * 100).toFixed(0)}%`);
1024
+ printKeyValue(' Rework Rate', `${(data.dora_derived.rework_rate * 100).toFixed(0)}%`);
1025
+ printKeyValue(' Mean Task Duration', `${(data.dora_derived.mean_task_duration_ms / 1000).toFixed(1)}s`);
1026
+ if (data.verdict.conditions.length > 0) {
1027
+ console.log(colors.subheader('\n Conditions'));
1028
+ for (const c of data.verdict.conditions) {
1029
+ console.log(` - ${c}`);
1030
+ }
1031
+ }
1032
+ printKeyValue('\n Report', data.verdict.report_path);
1033
+ }
1034
+ /**
1035
+ * Core: build SequenceLogEntry data (no I/O side effects except reading task logs)
1036
+ */
1037
+ export async function buildSequenceLogData(projectRoot, seqId, options) {
1038
+ const taskLogs = await loadSequenceTaskLogs(projectRoot, seqId);
1039
+ if (taskLogs.length === 0) {
1040
+ const tasksDir = getTasksDir(projectRoot);
1041
+ if (await exists(tasksDir)) {
1042
+ const allFiles = await listFiles('*.json', tasksDir);
1043
+ for (const file of allFiles) {
1044
+ if (file.toLowerCase().includes(seqId.toLowerCase())) {
1045
+ try {
1046
+ const data = await fs.readJson(path.join(tasksDir, file));
1047
+ taskLogs.push({ file, data });
1048
+ }
1049
+ catch { /* skip */ }
1050
+ }
1051
+ }
1052
+ }
1053
+ }
1054
+ const stats = aggregateSequenceStats(taskLogs);
1055
+ const now = getTimestamp();
1056
+ const timestamps = taskLogs
1057
+ .map(t => t.data.completed_at ? new Date(t.data.completed_at).getTime() : 0)
1058
+ .filter(t => t > 0);
1059
+ const startedAt = taskLogs[0]?.data.started_at || taskLogs[0]?.data.completed_at || now;
1060
+ const duration = timestamps.length >= 2
1061
+ ? Math.max(...timestamps) - Math.min(...timestamps)
1062
+ : 0;
1063
+ const meanDuration = stats.durations.length > 0
1064
+ ? stats.durations.reduce((a, b) => a + b, 0) / stats.durations.length
1065
+ : 0;
1066
+ return {
1067
+ schema_version: LOG_SCHEMA_VERSION,
1068
+ trace: { phase_id: options.phase, sequence_id: seqId },
1069
+ execution: {
1070
+ status: stats.failure > 0 ? 'partial' : 'completed',
1071
+ started_at: startedAt,
1072
+ completed_at: now,
1073
+ duration_ms: duration,
1074
+ },
1075
+ tasks: {
1076
+ total: stats.total,
1077
+ success: stats.success,
1078
+ failure: stats.failure,
1079
+ error: stats.error,
1080
+ rework: stats.rework,
1081
+ first_pass_success_rate: stats.first_pass_success_rate,
1082
+ final_success_rate: stats.final_success_rate,
1083
+ },
1084
+ analysis: {
1085
+ axis_1_consistency: makeAxisPlaceholder(),
1086
+ axis_2_ssot_conformance: makeAxisPlaceholder(),
1087
+ axis_3_cross_sequence: { ...makeAxisPlaceholder(), prev_sequence: null },
1088
+ },
1089
+ dora_derived: {
1090
+ change_failure_rate: stats.total > 0 ? stats.failure / stats.total : 0,
1091
+ rework_rate: stats.total > 0 ? stats.rework / stats.total : 0,
1092
+ mean_task_duration_ms: meanDuration,
1093
+ recovery_time_ms: null,
1094
+ },
1095
+ verdict: {
1096
+ proceed: options.verdict !== 'hold',
1097
+ conditions: options.conditions ? options.conditions.split(',').map(c => c.trim()) : [],
1098
+ report_path: options.report,
1099
+ },
1100
+ };
1101
+ }
1102
+ /**
1103
+ * Auto-feedback: L2/L3 이슈가 enrich로 들어오면 자동 피드백 라우팅
1104
+ */
1105
+ async function autoFeedbackFromIssues(projectRoot, semantic) {
1106
+ const issues = semantic.issues || [];
1107
+ const l2l3Issues = issues.filter(i => i.level >= 2);
1108
+ if (l2l3Issues.length === 0)
1109
+ return;
1110
+ try {
1111
+ const state = await loadWorkflowState(projectRoot);
1112
+ if (!state.automation.feedback)
1113
+ return;
1114
+ for (const issue of l2l3Issues) {
1115
+ try {
1116
+ const { execSync } = await import('child_process');
1117
+ execSync(`npx tsq feedback route "${issue.description.replace(/"/g, '\\"')}"`, { cwd: projectRoot, timeout: 10000, stdio: 'ignore' });
1118
+ }
1119
+ catch { /* 피드백 라우팅 실패 무시 */ }
1120
+ }
1121
+ }
1122
+ catch { /* 자동 피드백 실패 무시 */ }
1123
+ }
1124
+ /**
1125
+ * Core: build PhaseGateResult (no I/O side effects except reading sequence logs)
1126
+ */
1127
+ export async function buildPhaseGateData(projectRoot, phaseId) {
1128
+ const seqDir = getSequencesDir(projectRoot);
1129
+ const result = {
1130
+ phase_id: phaseId,
1131
+ can_transition: true,
1132
+ missing_sequences: [],
1133
+ missing_reports: [],
1134
+ blocking_conditions: [],
1135
+ };
1136
+ if (!await exists(seqDir)) {
1137
+ result.can_transition = false;
1138
+ result.blocking_conditions.push('No sequence logs directory');
1139
+ return result;
1140
+ }
1141
+ const files = await listFiles('*.json', seqDir);
1142
+ const phaseSeqs = [];
1143
+ for (const file of files) {
1144
+ try {
1145
+ const data = await fs.readJson(path.join(seqDir, file));
1146
+ if (data.trace.phase_id === phaseId) {
1147
+ phaseSeqs.push(data);
1148
+ }
1149
+ }
1150
+ catch { /* skip */ }
1151
+ }
1152
+ if (phaseSeqs.length === 0) {
1153
+ result.can_transition = false;
1154
+ result.blocking_conditions.push('No sequence logs found for this phase');
1155
+ return result;
1156
+ }
1157
+ for (const seq of phaseSeqs) {
1158
+ if (!seq.verdict.proceed) {
1159
+ result.can_transition = false;
1160
+ result.blocking_conditions.push(`${seq.trace.sequence_id}: verdict is HOLD`);
1161
+ }
1162
+ if (seq.verdict.report_path) {
1163
+ const reportPath = path.resolve(projectRoot, seq.verdict.report_path);
1164
+ if (!await exists(reportPath)) {
1165
+ result.can_transition = false;
1166
+ result.missing_reports.push(seq.verdict.report_path);
1167
+ }
1168
+ }
1169
+ if (seq.verdict.conditions.length > 0) {
1170
+ for (const c of seq.verdict.conditions) {
1171
+ result.blocking_conditions.push(`${seq.trace.sequence_id}: condition "${c}"`);
1172
+ }
1173
+ }
1174
+ }
1175
+ // Check unresolved L2/L3 feedback
1176
+ try {
1177
+ const state = await loadWorkflowState(projectRoot);
1178
+ if (state.pending_feedback?.length > 0) {
1179
+ const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
1180
+ for (const fbId of state.pending_feedback) {
1181
+ const fbPath = path.join(feedbackDir, `${fbId}.json`);
1182
+ if (await exists(fbPath)) {
1183
+ const fb = await fs.readJson(fbPath);
1184
+ if (!fb.status || fb.status === 'open' || fb.status === 'in_review') {
1185
+ result.can_transition = false;
1186
+ result.blocking_conditions.push(`Unresolved L${fb.level} feedback: ${fbId} (${fb.trigger})`);
1187
+ }
1188
+ }
1189
+ }
1190
+ }
1191
+ }
1192
+ catch { /* workflow state unavailable — skip feedback check */ }
1193
+ return result;
1194
+ }
1195
+ async function createSequenceLog(seqId, options) {
1196
+ const projectRoot = await findProjectRoot();
1197
+ if (!projectRoot)
1198
+ throw new Error('Not a TimSquad project');
1199
+ const entry = await buildSequenceLogData(projectRoot, seqId, options);
1200
+ const seqDir = getSequencesDir(projectRoot);
1201
+ await fs.ensureDir(seqDir);
1202
+ await fs.writeJson(path.join(seqDir, `${seqId}.json`), entry, { spaces: 2 });
1203
+ printSuccess(`Sequence log created: ${seqId}.json`);
1204
+ printKeyValue('Tasks', `${entry.tasks.success}/${entry.tasks.total} success`);
1205
+ printKeyValue('Verdict', entry.verdict.proceed ? 'PROCEED' : 'HOLD');
1206
+ }
1207
+ async function checkSequence(seqId) {
1208
+ const projectRoot = await findProjectRoot();
1209
+ if (!projectRoot)
1210
+ throw new Error('Not a TimSquad project');
1211
+ const taskLogs = await loadSequenceTaskLogs(projectRoot, seqId);
1212
+ printHeader(`Sequence Check: ${seqId}`);
1213
+ printKeyValue('Task Logs Found', String(taskLogs.length));
1214
+ if (taskLogs.length === 0) {
1215
+ console.log(colors.dim(' No task logs found for this sequence'));
1216
+ return;
1217
+ }
1218
+ let withSemantic = 0;
1219
+ for (const { file, data } of taskLogs) {
1220
+ const sem = hasSemantic(data.semantic) ? colors.success('✓ semantic') : colors.warning('○ no semantic');
1221
+ const status = data.status === 'completed' ? '✓' : '✗';
1222
+ console.log(` ${status} ${colors.agent(data.agent || '?')} ${sem} ${colors.dim(file)}`);
1223
+ if (hasSemantic(data.semantic))
1224
+ withSemantic++;
1225
+ }
1226
+ const coverage = Math.round((withSemantic / taskLogs.length) * 100);
1227
+ console.log('');
1228
+ printKeyValue('Semantic Coverage', `${coverage}% (${withSemantic}/${taskLogs.length})`);
1229
+ if (coverage < 100) {
1230
+ console.log(colors.warning('\n ⚠ Run tsq log enrich for tasks missing semantic data'));
1231
+ }
1232
+ }
1233
+ // ============================================================
1234
+ // Step 5: tsq log phase (L3) + Gate
1235
+ // ============================================================
1236
+ async function listPhaseLogs() {
1237
+ const projectRoot = await findProjectRoot();
1238
+ if (!projectRoot)
1239
+ throw new Error('Not a TimSquad project');
1240
+ const phaseDir = getPhasesDir(projectRoot);
1241
+ if (!await exists(phaseDir)) {
1242
+ console.log(colors.dim('No phase logs found'));
1243
+ return;
1244
+ }
1245
+ const files = await listFiles('*.json', phaseDir);
1246
+ if (files.length === 0) {
1247
+ console.log(colors.dim('No phase logs found'));
1248
+ return;
1249
+ }
1250
+ printHeader(`Phase Logs (${files.length})`);
1251
+ for (const file of files.sort()) {
1252
+ try {
1253
+ const data = await fs.readJson(path.join(phaseDir, file));
1254
+ const phaseId = data.trace.phase_id;
1255
+ const status = data.execution.status;
1256
+ const seqs = `${data.sequences.completed}/${data.sequences.total} sequences`;
1257
+ console.log(` ${colors.agent(phaseId)} [${status}] ${seqs}`);
1258
+ }
1259
+ catch {
1260
+ console.log(` ${colors.dim(file)} (unreadable)`);
1261
+ }
1262
+ }
1263
+ }
1264
+ async function viewPhaseLog(phaseId) {
1265
+ const projectRoot = await findProjectRoot();
1266
+ if (!projectRoot)
1267
+ throw new Error('Not a TimSquad project');
1268
+ const filePath = path.join(getPhasesDir(projectRoot), `${phaseId}.json`);
1269
+ if (!await exists(filePath)) {
1270
+ throw new Error(`Phase log not found: ${phaseId}`);
1271
+ }
1272
+ const data = await fs.readJson(filePath);
1273
+ printHeader(`Phase: ${phaseId}`);
1274
+ printKeyValue('Status', data.execution.status);
1275
+ printKeyValue('Duration', `${(data.execution.duration_ms / 1000 / 60).toFixed(1)} min`);
1276
+ printKeyValue('Sessions', String(data.execution.sessions_count));
1277
+ console.log(colors.subheader('\n Sequences'));
1278
+ printKeyValue(' Total', String(data.sequences.total));
1279
+ printKeyValue(' Completed', String(data.sequences.completed));
1280
+ printKeyValue(' Blocked', String(data.sequences.blocked));
1281
+ console.log(` IDs: ${data.sequences.ids.join(', ')}`);
1282
+ console.log(colors.subheader('\n Metrics'));
1283
+ printKeyValue(' Tasks', String(data.aggregate_metrics.total_tasks));
1284
+ printKeyValue(' Success Rate', `${(data.aggregate_metrics.task_success_rate * 100).toFixed(0)}%`);
1285
+ printKeyValue(' Rework Rate', `${(data.aggregate_metrics.task_rework_rate * 100).toFixed(0)}%`);
1286
+ printKeyValue(' Files Changed', String(data.aggregate_metrics.total_files_changed));
1287
+ printKeyValue(' SSOT Conformance', `${(data.aggregate_metrics.ssot_conformance_rate * 100).toFixed(0)}%`);
1288
+ console.log(colors.subheader('\n DORA'));
1289
+ printKeyValue(' Lead Time', `${(data.dora_derived.lead_time_ms / 1000 / 60).toFixed(1)} min`);
1290
+ printKeyValue(' Change Failure Rate', `${(data.dora_derived.change_failure_rate * 100).toFixed(0)}%`);
1291
+ if (data.retrospective.keep.length || data.retrospective.problem.length || data.retrospective.try.length) {
1292
+ console.log(colors.subheader('\n Retrospective'));
1293
+ if (data.retrospective.keep.length) {
1294
+ console.log(' Keep:');
1295
+ data.retrospective.keep.forEach(k => console.log(` + ${k}`));
1296
+ }
1297
+ if (data.retrospective.problem.length) {
1298
+ console.log(' Problem:');
1299
+ data.retrospective.problem.forEach(p => console.log(` - ${p}`));
1300
+ }
1301
+ if (data.retrospective.try.length) {
1302
+ console.log(' Try:');
1303
+ data.retrospective.try.forEach(t => console.log(` → ${t}`));
1304
+ }
1305
+ }
1306
+ }
1307
+ async function createPhaseLog(phaseId, options) {
1308
+ const projectRoot = await findProjectRoot();
1309
+ if (!projectRoot)
1310
+ throw new Error('Not a TimSquad project');
1311
+ const seqIds = options.sequences.split(',').map(s => s.trim());
1312
+ const seqDir = getSequencesDir(projectRoot);
1313
+ const now = getTimestamp();
1314
+ // Load sequence logs
1315
+ const seqLogs = [];
1316
+ let completed = 0, blocked = 0;
1317
+ for (const id of seqIds) {
1318
+ const seqPath = path.join(seqDir, `${id}.json`);
1319
+ if (await exists(seqPath)) {
1320
+ const data = await fs.readJson(seqPath);
1321
+ seqLogs.push(data);
1322
+ if (data.execution.status === 'completed')
1323
+ completed++;
1324
+ else
1325
+ blocked++;
1326
+ }
1327
+ else {
1328
+ blocked++;
1329
+ }
1330
+ }
1331
+ // Aggregate
1332
+ let totalTasks = 0, totalSuccess = 0, totalRework = 0, totalFiles = 0;
1333
+ let totalIssues1 = 0, totalIssues2 = 0, totalIssues3 = 0;
1334
+ const durations = [];
1335
+ for (const seq of seqLogs) {
1336
+ totalTasks += seq.tasks.total;
1337
+ totalSuccess += seq.tasks.success;
1338
+ totalRework += seq.tasks.rework;
1339
+ durations.push(seq.execution.duration_ms);
1340
+ // Count issues from analysis axes
1341
+ for (const axis of Object.values(seq.analysis)) {
1342
+ const r = axis;
1343
+ for (const issue of r.issues) {
1344
+ if (issue.level === 1)
1345
+ totalIssues1++;
1346
+ else if (issue.level === 2)
1347
+ totalIssues2++;
1348
+ else
1349
+ totalIssues3++;
1350
+ }
1351
+ }
1352
+ }
1353
+ // Collect total files from task logs
1354
+ for (const seqId of seqIds) {
1355
+ const taskLogs = await loadSequenceTaskLogs(projectRoot, seqId);
1356
+ for (const { data } of taskLogs) {
1357
+ totalFiles += data.mechanical?.files?.length || 0;
1358
+ }
1359
+ }
1360
+ const timestamps = seqLogs
1361
+ .map(s => [new Date(s.execution.started_at).getTime(), new Date(s.execution.completed_at).getTime()])
1362
+ .flat()
1363
+ .filter(t => t > 0);
1364
+ const leadTime = timestamps.length >= 2 ? Math.max(...timestamps) - Math.min(...timestamps) : 0;
1365
+ const entry = {
1366
+ schema_version: LOG_SCHEMA_VERSION,
1367
+ trace: { phase_id: phaseId },
1368
+ execution: {
1369
+ status: blocked > 0 ? 'aborted' : 'completed',
1370
+ started_at: seqLogs[0]?.execution.started_at || now,
1371
+ completed_at: now,
1372
+ duration_ms: leadTime,
1373
+ sessions_count: seqLogs.length,
1374
+ },
1375
+ sequences: { total: seqIds.length, completed, blocked, ids: seqIds },
1376
+ aggregate_metrics: {
1377
+ total_tasks: totalTasks,
1378
+ task_success_rate: totalTasks > 0 ? totalSuccess / totalTasks : 0,
1379
+ task_rework_rate: totalTasks > 0 ? totalRework / totalTasks : 0,
1380
+ total_files_changed: totalFiles,
1381
+ total_issues: { level_1: totalIssues1, level_2: totalIssues2, level_3: totalIssues3 },
1382
+ ssot_conformance_rate: 0, // Placeholder — from architect reports
1383
+ mean_sequence_duration_ms: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
1384
+ },
1385
+ dora_derived: {
1386
+ lead_time_ms: leadTime,
1387
+ change_failure_rate: totalTasks > 0 ? (totalTasks - totalSuccess) / totalTasks : 0,
1388
+ mean_recovery_time_ms: null,
1389
+ },
1390
+ planning: {
1391
+ original_sequences: seqIds,
1392
+ added_sequences: [],
1393
+ removed_sequences: [],
1394
+ scope_changes: [],
1395
+ plan_adherence_rate: 1,
1396
+ },
1397
+ retrospective: {
1398
+ keep: options.retroKeep ? options.retroKeep.split(',').map(s => s.trim()) : [],
1399
+ problem: options.retroProblem ? options.retroProblem.split(',').map(s => s.trim()) : [],
1400
+ try: options.retroTry ? options.retroTry.split(',').map(s => s.trim()) : [],
1401
+ },
1402
+ knowledge_extracted: [],
1403
+ };
1404
+ const phaseDir = getPhasesDir(projectRoot);
1405
+ await fs.ensureDir(phaseDir);
1406
+ await fs.writeJson(path.join(phaseDir, `${phaseId}.json`), entry, { spaces: 2 });
1407
+ printSuccess(`Phase log created: ${phaseId}.json`);
1408
+ printKeyValue('Sequences', `${completed}/${seqIds.length} completed`);
1409
+ printKeyValue('Tasks', `${totalSuccess}/${totalTasks} success`);
1410
+ }
1411
+ async function checkPhaseGate(phaseId) {
1412
+ const projectRoot = await findProjectRoot();
1413
+ if (!projectRoot)
1414
+ throw new Error('Not a TimSquad project');
1415
+ const result = await buildPhaseGateData(projectRoot, phaseId);
1416
+ printHeader(`Phase Gate: ${phaseId}`);
1417
+ if (result.can_transition) {
1418
+ console.log(colors.success('\n ✓ PASSED — Phase transition allowed'));
1419
+ }
1420
+ else {
1421
+ console.log(colors.error('\n ✗ BLOCKED — Phase transition denied'));
1422
+ if (result.missing_reports.length > 0) {
1423
+ console.log(colors.subheader('\n Missing Reports'));
1424
+ for (const r of result.missing_reports) {
1425
+ console.log(` - ${r}`);
1426
+ }
1427
+ }
1428
+ if (result.blocking_conditions.length > 0) {
1429
+ console.log(colors.subheader('\n Blocking Conditions'));
1430
+ for (const c of result.blocking_conditions) {
1431
+ console.log(` - ${c}`);
1432
+ }
1433
+ }
1434
+ }
1435
+ }
271
1436
  //# sourceMappingURL=log.js.map