@seanmars/tospec 0.19.0-beta.0 → 0.19.0-beta.13

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/CHANGELOG.md +578 -0
  2. package/README.md +71 -77
  3. package/assets/dashboard/app.js +14 -3
  4. package/assets/dashboard/style.css +7 -0
  5. package/assets/metrics/app.js +22 -0
  6. package/assets/metrics/style.css +8 -0
  7. package/assets/rules/tospec/decision.md +3 -0
  8. package/assets/rules/tospec/single-source-of-truth.md +19 -0
  9. package/dist/cli/index.d.ts.map +1 -1
  10. package/dist/cli/index.js +266 -82
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/commands/config.d.ts +9 -17
  13. package/dist/commands/config.d.ts.map +1 -1
  14. package/dist/commands/config.js +374 -95
  15. package/dist/commands/config.js.map +1 -1
  16. package/dist/commands/dashboard.d.ts +64 -81
  17. package/dist/commands/dashboard.d.ts.map +1 -1
  18. package/dist/commands/dashboard.js +350 -218
  19. package/dist/commands/dashboard.js.map +1 -1
  20. package/dist/commands/decision.d.ts +41 -19
  21. package/dist/commands/decision.d.ts.map +1 -1
  22. package/dist/commands/decision.js +400 -70
  23. package/dist/commands/decision.js.map +1 -1
  24. package/dist/commands/metrics.d.ts +34 -48
  25. package/dist/commands/metrics.d.ts.map +1 -1
  26. package/dist/commands/metrics.js +74 -84
  27. package/dist/commands/metrics.js.map +1 -1
  28. package/dist/commands/shared-output.d.ts +24 -10
  29. package/dist/commands/shared-output.d.ts.map +1 -1
  30. package/dist/commands/shared-output.js +62 -11
  31. package/dist/commands/shared-output.js.map +1 -1
  32. package/dist/commands/show.d.ts +7 -0
  33. package/dist/commands/show.d.ts.map +1 -1
  34. package/dist/commands/show.js +39 -8
  35. package/dist/commands/show.js.map +1 -1
  36. package/dist/commands/validate.d.ts +47 -30
  37. package/dist/commands/validate.d.ts.map +1 -1
  38. package/dist/commands/validate.js +282 -108
  39. package/dist/commands/validate.js.map +1 -1
  40. package/dist/commands/workflow/index.d.ts +6 -10
  41. package/dist/commands/workflow/index.d.ts.map +1 -1
  42. package/dist/commands/workflow/index.js +6 -10
  43. package/dist/commands/workflow/index.js.map +1 -1
  44. package/dist/commands/workflow/instructions.d.ts +21 -8
  45. package/dist/commands/workflow/instructions.d.ts.map +1 -1
  46. package/dist/commands/workflow/instructions.js +254 -95
  47. package/dist/commands/workflow/instructions.js.map +1 -1
  48. package/dist/commands/workflow/new-change.d.ts +4 -5
  49. package/dist/commands/workflow/new-change.d.ts.map +1 -1
  50. package/dist/commands/workflow/new-change.js +90 -25
  51. package/dist/commands/workflow/new-change.js.map +1 -1
  52. package/dist/commands/workflow/schemas.d.ts +3 -5
  53. package/dist/commands/workflow/schemas.d.ts.map +1 -1
  54. package/dist/commands/workflow/schemas.js +37 -11
  55. package/dist/commands/workflow/schemas.js.map +1 -1
  56. package/dist/commands/workflow/shared.d.ts +48 -21
  57. package/dist/commands/workflow/shared.d.ts.map +1 -1
  58. package/dist/commands/workflow/shared.js +36 -33
  59. package/dist/commands/workflow/shared.js.map +1 -1
  60. package/dist/commands/workflow/status.d.ts +10 -6
  61. package/dist/commands/workflow/status.d.ts.map +1 -1
  62. package/dist/commands/workflow/status.js +84 -42
  63. package/dist/commands/workflow/status.js.map +1 -1
  64. package/dist/commands/workflow/templates.d.ts +10 -3
  65. package/dist/commands/workflow/templates.d.ts.map +1 -1
  66. package/dist/commands/workflow/templates.js +39 -40
  67. package/dist/commands/workflow/templates.js.map +1 -1
  68. package/dist/core/archive.d.ts +26 -21
  69. package/dist/core/archive.d.ts.map +1 -1
  70. package/dist/core/archive.js +415 -201
  71. package/dist/core/archive.js.map +1 -1
  72. package/dist/core/artifact-graph/graph.d.ts +29 -36
  73. package/dist/core/artifact-graph/graph.d.ts.map +1 -1
  74. package/dist/core/artifact-graph/graph.js +50 -58
  75. package/dist/core/artifact-graph/graph.js.map +1 -1
  76. package/dist/core/artifact-graph/index.d.ts +2 -2
  77. package/dist/core/artifact-graph/index.d.ts.map +1 -1
  78. package/dist/core/artifact-graph/index.js +2 -2
  79. package/dist/core/artifact-graph/index.js.map +1 -1
  80. package/dist/core/artifact-graph/instruction-loader.d.ts +105 -100
  81. package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -1
  82. package/dist/core/artifact-graph/instruction-loader.js +176 -110
  83. package/dist/core/artifact-graph/instruction-loader.js.map +1 -1
  84. package/dist/core/artifact-graph/outputs.d.ts +13 -6
  85. package/dist/core/artifact-graph/outputs.d.ts.map +1 -1
  86. package/dist/core/artifact-graph/outputs.js +146 -8
  87. package/dist/core/artifact-graph/outputs.js.map +1 -1
  88. package/dist/core/artifact-graph/resolver.d.ts +44 -63
  89. package/dist/core/artifact-graph/resolver.d.ts.map +1 -1
  90. package/dist/core/artifact-graph/resolver.js +85 -86
  91. package/dist/core/artifact-graph/resolver.js.map +1 -1
  92. package/dist/core/artifact-graph/schema.d.ts +0 -6
  93. package/dist/core/artifact-graph/schema.d.ts.map +1 -1
  94. package/dist/core/artifact-graph/schema.js +7 -32
  95. package/dist/core/artifact-graph/schema.js.map +1 -1
  96. package/dist/core/artifact-graph/state.d.ts +1 -8
  97. package/dist/core/artifact-graph/state.d.ts.map +1 -1
  98. package/dist/core/artifact-graph/state.js +2 -17
  99. package/dist/core/artifact-graph/state.js.map +1 -1
  100. package/dist/core/artifact-graph/stub-detection.d.ts +12 -0
  101. package/dist/core/artifact-graph/stub-detection.d.ts.map +1 -0
  102. package/dist/core/artifact-graph/stub-detection.js +39 -0
  103. package/dist/core/artifact-graph/stub-detection.js.map +1 -0
  104. package/dist/core/artifact-graph/types.d.ts +4 -0
  105. package/dist/core/artifact-graph/types.d.ts.map +1 -1
  106. package/dist/core/artifact-graph/types.js +30 -10
  107. package/dist/core/artifact-graph/types.js.map +1 -1
  108. package/dist/core/available-tools.d.ts +3 -12
  109. package/dist/core/available-tools.d.ts.map +1 -1
  110. package/dist/core/available-tools.js +4 -13
  111. package/dist/core/available-tools.js.map +1 -1
  112. package/dist/core/change-metadata/schema.d.ts +1 -1
  113. package/dist/core/change-metadata/schema.d.ts.map +1 -1
  114. package/dist/core/change-metadata/schema.js +10 -7
  115. package/dist/core/change-metadata/schema.js.map +1 -1
  116. package/dist/core/change-presenter.d.ts +23 -18
  117. package/dist/core/change-presenter.d.ts.map +1 -1
  118. package/dist/core/change-presenter.js +102 -43
  119. package/dist/core/change-presenter.js.map +1 -1
  120. package/dist/core/change-status-policy.d.ts +8 -1
  121. package/dist/core/change-status-policy.d.ts.map +1 -1
  122. package/dist/core/change-status-policy.js +25 -1
  123. package/dist/core/change-status-policy.js.map +1 -1
  124. package/dist/core/codex-metrics.d.ts +25 -45
  125. package/dist/core/codex-metrics.d.ts.map +1 -1
  126. package/dist/core/codex-metrics.js +44 -88
  127. package/dist/core/codex-metrics.js.map +1 -1
  128. package/dist/core/codex-residue.d.ts +22 -0
  129. package/dist/core/codex-residue.d.ts.map +1 -0
  130. package/dist/core/codex-residue.js +61 -0
  131. package/dist/core/codex-residue.js.map +1 -0
  132. package/dist/core/command-generation/adapters/claude.d.ts +2 -9
  133. package/dist/core/command-generation/adapters/claude.d.ts.map +1 -1
  134. package/dist/core/command-generation/adapters/claude.js +2 -12
  135. package/dist/core/command-generation/adapters/claude.js.map +1 -1
  136. package/dist/core/command-generation/adapters/index.d.ts +1 -9
  137. package/dist/core/command-generation/adapters/index.d.ts.map +1 -1
  138. package/dist/core/command-generation/adapters/index.js +1 -9
  139. package/dist/core/command-generation/adapters/index.js.map +1 -1
  140. package/dist/core/command-generation/generator.d.ts +0 -17
  141. package/dist/core/command-generation/generator.d.ts.map +1 -1
  142. package/dist/core/command-generation/generator.js +0 -17
  143. package/dist/core/command-generation/generator.js.map +1 -1
  144. package/dist/core/command-generation/index.d.ts +2 -5
  145. package/dist/core/command-generation/index.d.ts.map +1 -1
  146. package/dist/core/command-generation/index.js +0 -9
  147. package/dist/core/command-generation/index.js.map +1 -1
  148. package/dist/core/command-generation/types.d.ts +10 -36
  149. package/dist/core/command-generation/types.d.ts.map +1 -1
  150. package/dist/core/command-generation/types.js +0 -6
  151. package/dist/core/command-generation/types.js.map +1 -1
  152. package/dist/core/command-generation/yaml.d.ts +3 -18
  153. package/dist/core/command-generation/yaml.d.ts.map +1 -1
  154. package/dist/core/command-generation/yaml.js +5 -23
  155. package/dist/core/command-generation/yaml.js.map +1 -1
  156. package/dist/core/config-prompts.d.ts +2 -4
  157. package/dist/core/config-prompts.d.ts.map +1 -1
  158. package/dist/core/config-prompts.js +2 -7
  159. package/dist/core/config-prompts.js.map +1 -1
  160. package/dist/core/config-schema.d.ts +8 -53
  161. package/dist/core/config-schema.d.ts.map +1 -1
  162. package/dist/core/config-schema.js +49 -62
  163. package/dist/core/config-schema.js.map +1 -1
  164. package/dist/core/config.d.ts +56 -0
  165. package/dist/core/config.d.ts.map +1 -1
  166. package/dist/core/config.js +73 -2
  167. package/dist/core/config.js.map +1 -1
  168. package/dist/core/converters/json-converter.d.ts.map +1 -1
  169. package/dist/core/dashboard-activity.d.ts +7 -9
  170. package/dist/core/dashboard-activity.d.ts.map +1 -1
  171. package/dist/core/dashboard-activity.js +26 -24
  172. package/dist/core/dashboard-activity.js.map +1 -1
  173. package/dist/core/dashboard-data.d.ts +40 -22
  174. package/dist/core/dashboard-data.d.ts.map +1 -1
  175. package/dist/core/dashboard-data.js +84 -68
  176. package/dist/core/dashboard-data.js.map +1 -1
  177. package/dist/core/global-config.d.ts +24 -53
  178. package/dist/core/global-config.d.ts.map +1 -1
  179. package/dist/core/global-config.js +43 -62
  180. package/dist/core/global-config.js.map +1 -1
  181. package/dist/core/init.d.ts +29 -11
  182. package/dist/core/init.d.ts.map +1 -1
  183. package/dist/core/init.js +232 -164
  184. package/dist/core/init.js.map +1 -1
  185. package/dist/core/list.d.ts +1 -1
  186. package/dist/core/list.d.ts.map +1 -1
  187. package/dist/core/list.js +121 -28
  188. package/dist/core/list.js.map +1 -1
  189. package/dist/core/local-server.d.ts +63 -39
  190. package/dist/core/local-server.d.ts.map +1 -1
  191. package/dist/core/local-server.js +99 -53
  192. package/dist/core/local-server.js.map +1 -1
  193. package/dist/core/markdown-render.d.ts +25 -0
  194. package/dist/core/markdown-render.d.ts.map +1 -0
  195. package/dist/core/markdown-render.js +94 -0
  196. package/dist/core/markdown-render.js.map +1 -0
  197. package/dist/core/migrate.d.ts +32 -15
  198. package/dist/core/migrate.d.ts.map +1 -1
  199. package/dist/core/migrate.js +220 -108
  200. package/dist/core/migrate.js.map +1 -1
  201. package/dist/core/parsers/change-parser.d.ts +7 -10
  202. package/dist/core/parsers/change-parser.d.ts.map +1 -1
  203. package/dist/core/parsers/change-parser.js +48 -56
  204. package/dist/core/parsers/change-parser.js.map +1 -1
  205. package/dist/core/parsers/markdown-parser.d.ts +8 -9
  206. package/dist/core/parsers/markdown-parser.d.ts.map +1 -1
  207. package/dist/core/parsers/markdown-parser.js +31 -22
  208. package/dist/core/parsers/markdown-parser.js.map +1 -1
  209. package/dist/core/parsers/requirement-blocks.d.ts +53 -11
  210. package/dist/core/parsers/requirement-blocks.d.ts.map +1 -1
  211. package/dist/core/parsers/requirement-blocks.js +200 -60
  212. package/dist/core/parsers/requirement-blocks.js.map +1 -1
  213. package/dist/core/parsers/requirement-text.d.ts +73 -79
  214. package/dist/core/parsers/requirement-text.d.ts.map +1 -1
  215. package/dist/core/parsers/requirement-text.js +137 -79
  216. package/dist/core/parsers/requirement-text.js.map +1 -1
  217. package/dist/core/parsers/spec-structure.d.ts +1 -1
  218. package/dist/core/parsers/spec-structure.d.ts.map +1 -1
  219. package/dist/core/parsers/spec-structure.js +30 -3
  220. package/dist/core/parsers/spec-structure.js.map +1 -1
  221. package/dist/core/planning-home.js.map +1 -1
  222. package/dist/core/profiles.d.ts +3 -10
  223. package/dist/core/profiles.d.ts.map +1 -1
  224. package/dist/core/profiles.js +5 -12
  225. package/dist/core/profiles.js.map +1 -1
  226. package/dist/core/project-config.d.ts +43 -44
  227. package/dist/core/project-config.d.ts.map +1 -1
  228. package/dist/core/project-config.js +107 -82
  229. package/dist/core/project-config.js.map +1 -1
  230. package/dist/core/project-layout.d.ts +10 -18
  231. package/dist/core/project-layout.d.ts.map +1 -1
  232. package/dist/core/project-layout.js +16 -26
  233. package/dist/core/project-layout.js.map +1 -1
  234. package/dist/core/root-selection.d.ts +11 -7
  235. package/dist/core/root-selection.d.ts.map +1 -1
  236. package/dist/core/root-selection.js +7 -8
  237. package/dist/core/root-selection.js.map +1 -1
  238. package/dist/core/rules.d.ts +10 -0
  239. package/dist/core/rules.d.ts.map +1 -0
  240. package/dist/core/rules.js +43 -0
  241. package/dist/core/rules.js.map +1 -0
  242. package/dist/core/schema-names.d.ts +16 -0
  243. package/dist/core/schema-names.d.ts.map +1 -0
  244. package/dist/core/schema-names.js +16 -0
  245. package/dist/core/schema-names.js.map +1 -0
  246. package/dist/core/schemas/base.schema.d.ts +3 -0
  247. package/dist/core/schemas/base.schema.d.ts.map +1 -1
  248. package/dist/core/schemas/base.schema.js +22 -6
  249. package/dist/core/schemas/base.schema.js.map +1 -1
  250. package/dist/core/schemas/change.schema.d.ts +16 -0
  251. package/dist/core/schemas/change.schema.d.ts.map +1 -1
  252. package/dist/core/schemas/change.schema.js +41 -10
  253. package/dist/core/schemas/change.schema.js.map +1 -1
  254. package/dist/core/schemas/spec.schema.d.ts +2 -0
  255. package/dist/core/schemas/spec.schema.d.ts.map +1 -1
  256. package/dist/core/shared/index.d.ts +3 -8
  257. package/dist/core/shared/index.d.ts.map +1 -1
  258. package/dist/core/shared/index.js +3 -8
  259. package/dist/core/shared/index.js.map +1 -1
  260. package/dist/core/shared/rules-generation.d.ts +27 -8
  261. package/dist/core/shared/rules-generation.d.ts.map +1 -1
  262. package/dist/core/shared/rules-generation.js +151 -16
  263. package/dist/core/shared/rules-generation.js.map +1 -1
  264. package/dist/core/shared/skill-generation.d.ts +38 -53
  265. package/dist/core/shared/skill-generation.d.ts.map +1 -1
  266. package/dist/core/shared/skill-generation.js +82 -51
  267. package/dist/core/shared/skill-generation.js.map +1 -1
  268. package/dist/core/shared/tool-detection.d.ts +40 -62
  269. package/dist/core/shared/tool-detection.d.ts.map +1 -1
  270. package/dist/core/shared/tool-detection.js +88 -80
  271. package/dist/core/shared/tool-detection.js.map +1 -1
  272. package/dist/core/skill-metrics.d.ts +36 -63
  273. package/dist/core/skill-metrics.d.ts.map +1 -1
  274. package/dist/core/skill-metrics.js +34 -73
  275. package/dist/core/skill-metrics.js.map +1 -1
  276. package/dist/core/spec-presenter.d.ts.map +1 -1
  277. package/dist/core/spec-presenter.js +6 -6
  278. package/dist/core/spec-presenter.js.map +1 -1
  279. package/dist/core/specs-apply.d.ts +24 -23
  280. package/dist/core/specs-apply.d.ts.map +1 -1
  281. package/dist/core/specs-apply.js +188 -173
  282. package/dist/core/specs-apply.js.map +1 -1
  283. package/dist/core/templates/fragments/interview.d.ts +2 -6
  284. package/dist/core/templates/fragments/interview.d.ts.map +1 -1
  285. package/dist/core/templates/fragments/interview.js +2 -6
  286. package/dist/core/templates/fragments/interview.js.map +1 -1
  287. package/dist/core/templates/fragments/next-step.d.ts +4 -8
  288. package/dist/core/templates/fragments/next-step.d.ts.map +1 -1
  289. package/dist/core/templates/fragments/next-step.js +4 -8
  290. package/dist/core/templates/fragments/next-step.js.map +1 -1
  291. package/dist/core/templates/fragments/verify.d.ts +9 -12
  292. package/dist/core/templates/fragments/verify.d.ts.map +1 -1
  293. package/dist/core/templates/fragments/verify.js +9 -12
  294. package/dist/core/templates/fragments/verify.js.map +1 -1
  295. package/dist/core/templates/index.d.ts +0 -6
  296. package/dist/core/templates/index.d.ts.map +1 -1
  297. package/dist/core/templates/index.js +0 -7
  298. package/dist/core/templates/index.js.map +1 -1
  299. package/dist/core/templates/skill-templates.d.ts +1 -5
  300. package/dist/core/templates/skill-templates.d.ts.map +1 -1
  301. package/dist/core/templates/skill-templates.js +0 -5
  302. package/dist/core/templates/skill-templates.js.map +1 -1
  303. package/dist/core/templates/types.d.ts +3 -7
  304. package/dist/core/templates/types.d.ts.map +1 -1
  305. package/dist/core/templates/types.js +0 -3
  306. package/dist/core/templates/types.js.map +1 -1
  307. package/dist/core/templates/workflows/apply.d.ts +3 -9
  308. package/dist/core/templates/workflows/apply.d.ts.map +1 -1
  309. package/dist/core/templates/workflows/apply.js +9 -12
  310. package/dist/core/templates/workflows/apply.js.map +1 -1
  311. package/dist/core/templates/workflows/archive.d.ts +0 -6
  312. package/dist/core/templates/workflows/archive.d.ts.map +1 -1
  313. package/dist/core/templates/workflows/archive.js +7 -5
  314. package/dist/core/templates/workflows/archive.js.map +1 -1
  315. package/dist/core/templates/workflows/decision.js +4 -4
  316. package/dist/core/templates/workflows/decision.js.map +1 -1
  317. package/dist/core/templates/workflows/explore.js +1 -1
  318. package/dist/core/templates/workflows/grill.d.ts.map +1 -1
  319. package/dist/core/templates/workflows/grill.js +0 -2
  320. package/dist/core/templates/workflows/grill.js.map +1 -1
  321. package/dist/core/templates/workflows/issue.d.ts +0 -6
  322. package/dist/core/templates/workflows/issue.d.ts.map +1 -1
  323. package/dist/core/templates/workflows/issue.js +3 -0
  324. package/dist/core/templates/workflows/issue.js.map +1 -1
  325. package/dist/core/templates/workflows/propose.d.ts +0 -6
  326. package/dist/core/templates/workflows/propose.d.ts.map +1 -1
  327. package/dist/core/templates/workflows/propose.js +0 -1
  328. package/dist/core/templates/workflows/propose.js.map +1 -1
  329. package/dist/core/templates/workflows/sync.d.ts +2 -8
  330. package/dist/core/templates/workflows/sync.d.ts.map +1 -1
  331. package/dist/core/templates/workflows/sync.js +2 -2
  332. package/dist/core/templates/workflows/sync.js.map +1 -1
  333. package/dist/core/templates/workflows/update.d.ts +0 -6
  334. package/dist/core/templates/workflows/update.d.ts.map +1 -1
  335. package/dist/core/templates/workflows/update.js.map +1 -1
  336. package/dist/core/update.d.ts +26 -21
  337. package/dist/core/update.d.ts.map +1 -1
  338. package/dist/core/update.js +165 -116
  339. package/dist/core/update.js.map +1 -1
  340. package/dist/core/user-state-migration.d.ts +13 -15
  341. package/dist/core/user-state-migration.d.ts.map +1 -1
  342. package/dist/core/user-state-migration.js +16 -20
  343. package/dist/core/user-state-migration.js.map +1 -1
  344. package/dist/core/validation/constants.d.ts +19 -25
  345. package/dist/core/validation/constants.d.ts.map +1 -1
  346. package/dist/core/validation/constants.js +25 -20
  347. package/dist/core/validation/constants.js.map +1 -1
  348. package/dist/core/validation/prose-length.d.ts +15 -0
  349. package/dist/core/validation/prose-length.d.ts.map +1 -0
  350. package/dist/core/validation/prose-length.js +29 -0
  351. package/dist/core/validation/prose-length.js.map +1 -0
  352. package/dist/core/validation/purpose-placeholder.d.ts +9 -16
  353. package/dist/core/validation/purpose-placeholder.d.ts.map +1 -1
  354. package/dist/core/validation/purpose-placeholder.js +30 -44
  355. package/dist/core/validation/purpose-placeholder.js.map +1 -1
  356. package/dist/core/validation/section-validator.d.ts +4 -4
  357. package/dist/core/validation/section-validator.d.ts.map +1 -1
  358. package/dist/core/validation/section-validator.js +43 -7
  359. package/dist/core/validation/section-validator.js.map +1 -1
  360. package/dist/core/validation/task-numbering.d.ts +6 -3
  361. package/dist/core/validation/task-numbering.d.ts.map +1 -1
  362. package/dist/core/validation/task-numbering.js +23 -11
  363. package/dist/core/validation/task-numbering.js.map +1 -1
  364. package/dist/core/validation/types.d.ts +18 -0
  365. package/dist/core/validation/types.d.ts.map +1 -1
  366. package/dist/core/validation/types.js +12 -1
  367. package/dist/core/validation/types.js.map +1 -1
  368. package/dist/core/validation/validator.d.ts +50 -51
  369. package/dist/core/validation/validator.d.ts.map +1 -1
  370. package/dist/core/validation/validator.js +486 -263
  371. package/dist/core/validation/validator.js.map +1 -1
  372. package/dist/prompts/searchable-multi-select.d.ts +3 -8
  373. package/dist/prompts/searchable-multi-select.d.ts.map +1 -1
  374. package/dist/prompts/searchable-multi-select.js +16 -39
  375. package/dist/prompts/searchable-multi-select.js.map +1 -1
  376. package/dist/utils/change-metadata.d.ts +11 -50
  377. package/dist/utils/change-metadata.d.ts.map +1 -1
  378. package/dist/utils/change-metadata.js +48 -67
  379. package/dist/utils/change-metadata.js.map +1 -1
  380. package/dist/utils/change-utils.d.ts +31 -54
  381. package/dist/utils/change-utils.d.ts.map +1 -1
  382. package/dist/utils/change-utils.js +143 -100
  383. package/dist/utils/change-utils.js.map +1 -1
  384. package/dist/utils/file-lock.d.ts +39 -0
  385. package/dist/utils/file-lock.d.ts.map +1 -0
  386. package/dist/utils/file-lock.js +149 -0
  387. package/dist/utils/file-lock.js.map +1 -0
  388. package/dist/utils/file-system.d.ts +12 -32
  389. package/dist/utils/file-system.d.ts.map +1 -1
  390. package/dist/utils/file-system.js +16 -40
  391. package/dist/utils/file-system.js.map +1 -1
  392. package/dist/utils/frontmatter.d.ts +7 -11
  393. package/dist/utils/frontmatter.d.ts.map +1 -1
  394. package/dist/utils/frontmatter.js +11 -11
  395. package/dist/utils/frontmatter.js.map +1 -1
  396. package/dist/utils/interactive.d.ts +4 -9
  397. package/dist/utils/interactive.d.ts.map +1 -1
  398. package/dist/utils/interactive.js +2 -4
  399. package/dist/utils/interactive.js.map +1 -1
  400. package/dist/utils/item-discovery.d.ts +15 -10
  401. package/dist/utils/item-discovery.d.ts.map +1 -1
  402. package/dist/utils/item-discovery.js +42 -47
  403. package/dist/utils/item-discovery.js.map +1 -1
  404. package/dist/utils/link.d.ts +13 -4
  405. package/dist/utils/link.d.ts.map +1 -1
  406. package/dist/utils/link.js +13 -4
  407. package/dist/utils/link.js.map +1 -1
  408. package/dist/utils/match.js.map +1 -1
  409. package/dist/utils/requirement-diff.d.ts +13 -23
  410. package/dist/utils/requirement-diff.d.ts.map +1 -1
  411. package/dist/utils/requirement-diff.js +13 -23
  412. package/dist/utils/requirement-diff.js.map +1 -1
  413. package/dist/utils/spec-files.d.ts +10 -11
  414. package/dist/utils/spec-files.d.ts.map +1 -1
  415. package/dist/utils/spec-files.js +31 -22
  416. package/dist/utils/spec-files.js.map +1 -1
  417. package/dist/utils/task-progress.d.ts +11 -9
  418. package/dist/utils/task-progress.d.ts.map +1 -1
  419. package/dist/utils/task-progress.js +53 -32
  420. package/dist/utils/task-progress.js.map +1 -1
  421. package/dist/utils/timestamp.d.ts +5 -8
  422. package/dist/utils/timestamp.d.ts.map +1 -1
  423. package/dist/utils/timestamp.js +5 -8
  424. package/dist/utils/timestamp.js.map +1 -1
  425. package/package.json +9 -10
  426. package/schemas/decision/templates/decision.md +3 -1
  427. package/schemas/decision/templates/index.md +2 -2
  428. package/schemas/issue/schema.yaml +11 -2
  429. package/schemas/issue/templates/spec.md +37 -3
  430. package/schemas/sdd/schema.yaml +24 -1
  431. package/schemas/sdd/templates/spec.md +37 -3
package/README.md CHANGED
@@ -1,137 +1,132 @@
1
1
  # tospec
2
2
 
3
- > Spec-driven development CLI — 用 schema 定義文件與流程, 讓每個開發階段都能被檢查. 進度由檔案推算, 工作方法寫在 Skill 裡.
3
+ > 用 schema 定義文件與流程的 spec-driven development CLI. 進度由檔案推算, 工作方法寫在 Skill 裡.
4
4
 
5
- tospec 把需求釐清, 規格, 設計, 任務, 實作到歸檔串成一套工作流程. Skill 協助 AI 工具 (Claude Code / Codex) 處理各階段, CLI 負責建立檔案, 檢查狀態與驗證規格格式. 它不另外維護狀態資料庫; `status` 會依 change 目錄裡有哪些檔案, 推算目前進度.
5
+ 寫需求時最常遇到的問題不是「不會寫」, 而是「寫到一半就跳去實作, 最後規格跟程式對不上」. tospec 把釐清需求, 寫規格, 設計, 拆任務, 實作, 歸檔串成一條線: Skill 帶著 AI 工具 (Claude Code / Codex) 走每一步, CLI 負責建檔, 看狀態, 驗證格式.
6
+
7
+ 它沒有狀態資料庫. `tospec status` 看 change 目錄裡有哪些檔案, 就知道走到哪一步; 檔案出現, 狀態就前進.
6
8
 
7
9
  ## 安裝
8
10
 
9
- 需要 Node.js `>=22.22.0`.
11
+ 需要 Node.js 22.22.0 以上.
10
12
 
11
13
  ```bash
12
14
  npm install -g @seanmars/tospec
13
- ```
14
-
15
- 安裝後即可使用 `tospec` 指令:
16
-
17
- ```bash
18
15
  tospec --help
19
- tospec --version
20
16
  ```
21
17
 
22
- 也可以不安裝, 直接用 `npx`:
23
-
24
- ```bash
25
- npx @seanmars/tospec --help
26
- ```
18
+ 不想全域安裝也可以直接 `npx @seanmars/tospec --help`.
27
19
 
28
20
  ## 快速開始
29
21
 
30
22
  ```bash
31
- # 1. 在專案根目錄初始化, 並安裝 Claude Code 的 Skill
23
+ # 初始化專案, 順便裝好 Claude Code 的 Skill
32
24
  tospec init --tools claude
33
25
 
34
- # 2. 建立一個新需求 change
26
+ # 開一個新需求
35
27
  tospec new change add-dark-mode --schema sdd
36
28
 
37
- # 3. 查看目前各文件的狀態
29
+ # 看這個 change 走到哪了
38
30
  tospec status --change add-dark-mode
39
31
 
40
- # 4. 驗證文件格式
32
+ # 檢查文件格式
41
33
  tospec validate add-dark-mode
42
34
 
43
- # 5. 完成後歸檔, 把規格合併進主 specs
35
+ # 做完了, 把規格合併進主 specs 並歸檔
44
36
  tospec archive add-dark-mode
45
37
  ```
46
38
 
47
- `--tools` 可填 `claude`, `codex`, `all` 或 `none`. 初始化後會建立 `tospec/` 目錄與 Skill; Claude Code 另外會產生 `/tosx:<name>` 快捷指令.
39
+ `--tools` 可填 `claude`, `agents`, `all` 或 `none`. Skill 一律先寫進 `.agents/skills/` 這份 canonical 副本, 再複製到各工具目錄, 所以 `claude` 隱含 `agents`. Codex 直接讀 `.agents/`, 不會另外產生 `.codex/`. `none` 只建 `tospec/` 目錄, 不碰任何 agent 內容.
40
+
41
+ `.claude/skills/` 是複本不是連結, 因為 junction 與 symlink 進 git 的記錄方式不同, 會讓追蹤內容取決於貢獻者的作業系統. 改了 `.agents/skills/` 之後要跑 `tospec update` 才會同步過去.
48
42
 
49
43
  ## 兩種工作流程
50
44
 
51
- 建立 change 時用 `--schema` 選擇流程:
45
+ 開 change 時用 `--schema` 選:
52
46
 
53
47
  | schema | 用途 | 文件流程 |
54
48
  |--------|------|----------|
55
49
  | `sdd` | 開發新需求 | `proposal → specs / design → tasks` |
56
- | `issue` | 修正問題 | `task`(根因診斷) + 選配 `specs` |
50
+ | `issue` | 修正問題 | `task` (根因診斷) + 選配 `specs` |
57
51
 
58
- 兩者都在 **apply** 階段實作, 最後由 **archive** 判斷是否需要先 **sync** 再歸檔. apply 以完整測試與 `tospec validate` 通過為完成標準; **verify (雙軸審查) 是 opt-in**, 只有使用者主動要求時才執行, apply 不會自己跑, 也不會主動詢問.
52
+ 兩者都在 **apply** 階段實作, 最後由 **archive** 判斷要不要先 **sync** 再歸檔. apply 的完成標準是測試與 `tospec validate` 通過; 雙軸審查 (verify) 是 opt-in, 使用者開口才做.
59
53
 
60
- 文件狀態的判斷方式:
54
+ 文件狀態只有四種:
61
55
 
62
- - `done` — 對應檔案已存在
63
- - `ready` — 前置文件都已建立, 這份還沒有, 可以開始
64
- - `blocked` — 至少一個前置文件還沒建立
65
- - `skipped` — 選配文件未建立, 不阻擋流程
56
+ - `done`: 檔案已存在
57
+ - `ready`: 前置文件都齊了, 這份還沒寫, 可以開始
58
+ - `blocked`: 至少一份前置文件還沒建立
59
+ - `skipped`: 選配文件沒建, 不擋流程
66
60
 
67
- ## Skill 工作流程
61
+ ## Skill
68
62
 
69
- 工作方法寫在 Skill 裡, CLI 只負責檔案與驗證. `tospec init` 會安裝 10 個 workflow Skill:
63
+ `tospec init` 會裝 10 個 workflow Skill, Claude Code 另外會有對應的 `/tosx:<name>` 快捷指令.
70
64
 
71
65
  | Skill | 作用 |
72
66
  |-------|------|
73
- | `tospec-explore` | 以訪談收斂還很模糊的需求或問題 |
74
- | `tospec-grill` | 對任何產出 (計畫/決策/設計) 做四象限壓力測試 |
75
- | `tospec-propose` | 把已討論清楚的需求整理成完整 sdd change |
76
- | `tospec-issue` | 診斷 bug 至根因, 產出 issue change |
77
- | `tospec-decision` | 記錄架構決策為永久 ADR |
67
+ | `tospec-explore` | 用訪談收斂還很模糊的需求或問題 |
68
+ | `tospec-grill` | 對計畫, 決策, 設計做壓力測試 |
69
+ | `tospec-propose` | 把討論清楚的需求整理成完整的 sdd change |
70
+ | `tospec-issue` | 診斷 bug 到根因, 產出 issue change |
71
+ | `tospec-decision` | 把架構決策記成永久 ADR |
78
72
  | `tospec-update` | 修訂 change 的規劃文件並保持一致 |
79
- | `tospec-apply` | 逐項實作任務 (不強制寫測試); verify 需使用者主動要求 |
73
+ | `tospec-apply` | 逐項實作任務 |
80
74
  | `tospec-apply-with-tdd` | 同上, 但強制先寫測試 (red → green) |
81
75
  | `tospec-sync` | 以程式碼為準核對規格, 產出 sync-report |
82
- | `tospec-archive` | 判斷是否需要 sync, 然後歸檔完成的 change |
76
+ | `tospec-archive` | 判斷要不要 sync, 然後歸檔 |
83
77
 
84
- 典型流程: `explore` (想清楚) → `propose` (寫成 change) → `apply` (實作) → `sync` (需要時核對規格) → `archive` (歸檔). 修 bug 則從 `issue` 開始, 走同樣的 apply → archive 收尾.
78
+ 典型流程: `explore` 想清楚, `propose` 寫成 change, `apply` 實作, 需要時 `sync` 核對規格, 最後 `archive`. 修 bug 從 `issue` 開始, 一樣走 apply → archive 收尾.
85
79
 
86
80
  ## CLI 指令
87
81
 
88
- 多數指令支援 `--json`, 方便 AI 工具讀取結果.
82
+ 多數指令支援 `--json`, 給 AI 工具讀. 封包格式與 exit code 見 [Agent 契約](./docs/agent-contract.md).
89
83
 
90
84
  ### 專案管理
91
85
 
92
- | 指令 | 作用 |
93
- |------|------|
94
- | `tospec init [path]` | 初始化專案, 建立 `tospec/` 與 Skill. 選項: `--tools <claude\|codex\|all\|none>`, `--force`, `--profile <name>` |
95
- | `tospec update [path]` | 重新產生已設定的 Skill, 修復連結或複製檔案. 選項: `--force` |
96
- | `tospec migrate [openspec-dir]` | 把 OpenSpec 專案轉成 tospec 格式 (預設讀 `./openspec`). 選項: `-f, --force` |
97
- | `tospec config <sub>` | 全域設定. 子命令: `path`, `list`, `get <key>`, `set <key> <value>`, `unset <key>`, `reset`, `edit`, `profile [preset]` (調整要安裝的 Skill) |
98
-
99
- ### 建立與查詢
86
+ | 指令 | 作用 | 選項 |
87
+ |------|------|------|
88
+ | `tospec init [path]` | 初始化專案, 建立 `tospec/`, Skill 與 workflow 規則 | `--tools`, `--force`, `--profile <name>`, `--json` |
89
+ | `tospec update [path]` | 重新產生 Skill 並同步到各工具目錄 | `--force`, `--json` |
90
+ | `tospec rules [path]` | 只刷新 workflow 規則檔, 不動 Skill 與設定 | `--json` |
91
+ | `tospec migrate [openspec-dir]` | 把 OpenSpec 專案轉成 tospec 格式 (預設讀 `./openspec`) | `-f, --force`, `--json` |
92
+ | `tospec config <sub>` | 全域設定: `path`, `list`, `get`, `set`, `unset`, `reset`, `edit`, `profile [preset]` | 各子命令多支援 `--json` |
100
93
 
101
- | 指令 | 作用 |
102
- |------|------|
103
- | `tospec new change <name>` | 建立 change 目錄與 ticket 索引檔. 選項: `--schema <sdd\|issue>`, `--description <text>`, `--goal <text>`, `--decisions <files>`, `--json` |
104
- | `tospec list` | 列出進行中的 change (預設). 選項: `--specs`, `--changes`, `--sort <recent\|name>`, `--type <requirement\|issue>`, `--json` |
105
- | `tospec show [item-name]` | 顯示 change 或 spec 內容. 選項: `--type <change\|spec>`, `--deltas-only`, `--requirements`, `--no-scenarios`, `-r, --requirement <id>`, `--no-interactive`, `--json` |
106
- | `tospec status` | 依檔案與 schema 顯示各文件狀態. 選項: `--change <id>`, `--schema <name>`, `--json` |
107
- | `tospec decision new <topic>` | 建立 ADR (`tospec/decisions/<yyyyMMdd_HHmmss>-<topic>.md`) 與索引列. 選項: `--title <text>`, `--summary <text>`, `--date <yyyyMMdd_HHmmss>`, `--status <proposed\|accepted\|superseded>`, `--force`, `--json` |
108
- | `tospec decision list` | 以索引表列出 ADR. 選項: `--status <proposed\|accepted\|superseded>`, `--sort <date\|name>`, `--json` |
109
- | `tospec skill-metrics` | 開啟本機網頁, 以圖表呈現每個 Skill 的耗時. 選項: `--no-open` |
94
+ workflow 規則檔 (`.agents/rules/tospec/single-source-of-truth.md` 與 `.claude/` 下的複本) 由套件產生, 內含版本標記與內容雜湊. 一旦偵測到手改, `init` / `update` / `rules` 都會在寫入任何檔案前整次失敗, `--force` 也不例外. 想保留修改, 先備份再刪掉該檔重跑.
110
95
 
111
- `tospec skill-metrics` 會在 loopback 上啟動一台伺服器, 印出網址、開啟瀏覽器, 並停在前景直到 Ctrl+C. 連接埠固定從 **26693** 開始; 若已被占用則自動往上遞增直到找到可用的, 所以再開一個也不會失敗. 網頁是唯一的輸出形式, 沒有表格也沒有 `--json`; `--no-open` 只印網址而不開瀏覽器, 供沒有瀏覽器的環境使用.
96
+ ### 建立與查詢
112
97
 
113
- 頁面共五張圖: 各 Skill 的總投入時間 (實色為 engaged, 凹陷部分為 idle, 整根長度即 span)、典型的單次時長 (engaged 與 span 的中位數)、每次執行的分佈 (每一次 run 都是一個點)、每日投入時間, 以及執行時間軸. 總量與中位數刻意分成兩張: 一個 Skill 可能總量第一但每次都很短, 那是次數多而不是每次慢.
98
+ | 指令 | 作用 | 選項 |
99
+ |------|------|------|
100
+ | `tospec new change <name>` | 建立 change 目錄與 ticket | `--schema <sdd\|issue>`, `--description`, `--goal`, `--decisions <files>`, `--json` |
101
+ | `tospec list` | 列出進行中的 change | `--specs`, `--changes`, `--sort <recent\|name>`, `--type <requirement\|issue>`, `--json` |
102
+ | `tospec show [item-name]` | 顯示 change 或 spec | `--type <change\|spec>`, `--diff`, `--requirements`, `--no-scenarios`, `-r <id>`, `--no-interactive`, `--json` |
103
+ | `tospec status` | 顯示各文件狀態 | `--change <id>`, `--all`, `--schema <name>`, `--json` |
104
+ | `tospec decision new <topic>` | 建立 ADR 與索引列 | `--title`, `--summary`, `--date`, `--status`, `--force`, `--json` |
105
+ | `tospec decision list` | 列出 ADR | `--status`, `--sort <date\|name>`, `--reindex`, `--json` |
106
+ | `tospec skill-metrics` | 開本機網頁看每個 Skill 的耗時 | `--no-open` |
114
107
 
115
- 時間範圍、idle gap、split gap 都可在頁面上調整並即時重算, 選擇會記在瀏覽器中; 「只看 tospec workflow」單純隱藏列, 不重新計算. 右上角可切換主題與語言 (English / 正體中文), 兩者同樣會被記住. 終端機輸出維持英文, 與其他指令一致.
108
+ `show --diff` 會把 change 裡每個 requirement 對照主 specs 顯示差異, 包括 REMOVED 會刪掉的那段.
116
109
 
117
- 資料同時來自 Claude Code 與 Codex 兩種工具的紀錄, 一律唯讀且不寫入任何檔案. Claude Code 讀取 transcript 的 `attributionSkill` 欄位 (只存在於較新的 session, 舊紀錄無法回溯統計); Codex 的 session 沒有對應欄位, 因此改用推論 — 只要偵測到某個 Skill 的 `SKILL.md` 被讀取, 就視為該 Skill 開始執行. 由於 Codex 的歸屬是推論而非直接紀錄, 兩者精準度不同, 頁面上一律分開呈現、絕不合併成同一個數字, Codex 的數字並附上這項說明. 每一列的標籤結尾都會標上來源工具 (Claude/Codex), 兩者皆標而非只標其中一方, 以免另一方被讀成預設值. 頁面在統計為空時, 會分別指出兩種工具各自屬於哪一種情況.
110
+ `skill-metrics` 讀 Claude Code 與 Codex 的 session 紀錄, 只讀不寫. Claude Code 是直接讀 transcript 的歸屬欄位 (舊 session 沒有, 統計不到); Codex 沒有對應欄位, 改用「讀了哪個 `SKILL.md`」推論. 兩者精準度不同, 頁面上分開呈現, 不合併.
118
111
 
119
112
  ### 驗證與歸檔
120
113
 
121
- | 指令 | 作用 |
122
- |------|------|
123
- | `tospec validate [item-name]` | 檢查必要段落與 delta spec 格式. 選項: `--all`, `--changes`, `--specs`, `--type <change\|spec>`, `--strict`, `--concurrency <n>`, `--no-interactive`, `--json` |
124
- | `tospec archive [change-name]` | 重新驗證, 合併 delta specs 到主 specs, 再把 change 移到 archive. 選項: `-y, --yes`, `--skip-specs`, `--no-validate`, `--require-sync`, `--json` |
114
+ | 指令 | 作用 | 選項 |
115
+ |------|------|------|
116
+ | `tospec validate [item-name]` | 檢查必要段落與 delta spec 格式 | `--all`, `--changes`, `--specs`, `--type`, `--strict`, `--concurrency <n>`, `--no-interactive`, `--json` |
117
+ | `tospec archive [change-name]` | 重新驗證, 合併 delta specs, 把 change 移到 archive | `-y`, `--skip-specs`, `--no-validate`, `--require-sync`, `--json` |
125
118
 
126
- delta spec 的位置固定為 `specs/<capability>/spec.md`, **恰好一層**: 更深的巢狀目錄不會被合併, 驗證會直接報錯. 完全沒有行為變更的 change (工具, CI, 純文件) 在 `.tospec.yaml` 設 `skip_specs: true` 宣告即可, 不要為了通過驗證捏造 requirement.
119
+ delta spec 固定放在 `specs/<capability>/spec.md`, 恰好一層, 更深的目錄不會被合併. 完全沒有行為變更的 change (工具, CI, 純文件) 在 `.tospec.yaml` 設 `skip_specs: true` 就好, 不要為了過驗證捏造 requirement.
120
+
121
+ requirement 內文慣例上要有 SHALL 或 MUST, 缺了是 WARNING; `--strict` 才會把它當錯誤.
127
122
 
128
123
  ### 給 AI 工具的指令
129
124
 
130
- | 指令 | 作用 |
131
- |------|------|
132
- | `tospec instructions [artifact]` | 回傳建立文件或套用任務的完整指引 (artifact 填 `apply` 時回傳實作指引). 選項: `--change <id>`, `--schema <name>`, `--json` |
133
- | `tospec schemas` | 列出可用的工作流程 schema 與其文件. 選項: `--json` |
134
- | `tospec templates` | 顯示指定 schema 中各文件範本的實際路徑. 選項: `--schema <name>`, `--json` |
125
+ | 指令 | 作用 | 選項 |
126
+ |------|------|------|
127
+ | `tospec instructions [artifact]` | 回傳建立文件的完整指引; `artifact` 填 `apply` 回傳實作指引 | `--change <id>`, `--schema <name>`, `--json` |
128
+ | `tospec schemas` | 列出可用的 schema 與其文件 | `--json` |
129
+ | `tospec templates` | 顯示 schema 中各文件範本的路徑 | `--schema <name>`, `--json` |
135
130
 
136
131
  ### Dashboard
137
132
 
@@ -139,16 +134,15 @@ delta spec 的位置固定為 `specs/<capability>/spec.md`, **恰好一層**:
139
134
  tospec dashboard [path]
140
135
  ```
141
136
 
142
- 啟動本機唯讀 web dashboard, 檢視 change 與 specs 進度. 選項: `-p, --port <n>` (預設 5620), `--host <addr>` (預設 127.0.0.1), `-o, --open` (啟動後開啟瀏覽器), `-d, --detach` (背景執行), `--stop` (停止本專案的 dashboard), `-l, --list` (列出所有執行中的 dashboard), `--allow-remote` (允許綁定非 loopback 位址, 會對外網開放).
143
-
144
- ## 文件
137
+ 本機 web dashboard, 看 change 與 specs 進度. 唯一的寫入是勾 task checkbox, 已歸檔或 sync 通過的 change 不能勾.
145
138
 
146
- - [Agent 契約](./docs/agent-contract.md) —— `--json` 的成功/失敗封包與 exit code
147
- - [自訂 schema](./docs/custom-schema.md) —— `schema.yaml` 欄位, 解析順序, 佈局限制
139
+ 選項: `-p, --port <n>` (預設 5620), `--host <addr>` (預設 127.0.0.1), `-o, --open`, `-d, --detach`, `--stop`, `-l, --list`, `--allow-remote` (綁非 loopback 位址, 會對外網開放).
148
140
 
149
- ## 更新日誌
141
+ ## 文件
150
142
 
151
- 各版本的變更與其根因說明: [CHANGELOG.md](./CHANGELOG.md)
143
+ - [Agent 契約](./docs/agent-contract.md): `--json` 的成功/失敗封包與 exit code
144
+ - [自訂 schema](./docs/custom-schema.md): `schema.yaml` 欄位, 解析順序, 佈局限制
145
+ - [CHANGELOG](./CHANGELOG.md): 各版本的變更與根因說明
152
146
 
153
147
  ## 授權
154
148
 
@@ -587,6 +587,10 @@ function updateHealthStrip(staleDays) {
587
587
  if (parseErrors) {
588
588
  items.push(healthItem(`${parseErrors} ticket parse error(s)`, 'tickets-panel'));
589
589
  }
590
+ const unreadable = overview.changes.filter((c) => c.unreadable).length;
591
+ if (unreadable) {
592
+ items.push(healthItem(`${unreadable} change(s) with unreadable metadata`, 'changes-panel'));
593
+ }
590
594
  if (activity && activity.available && activity.specDrift) {
591
595
  items.push(el('span', { class: 'health-item', text: 'spec drift: code moved, tospec/ did not (7d)' }));
592
596
  }
@@ -614,6 +618,13 @@ function changeRow(c, staleDays, changed) {
614
618
  row.classList.add('row-ready');
615
619
  row.append(el('span', { class: 'badge badge-ready', text: 'ready' }));
616
620
  }
621
+ if (c.unreadable) {
622
+ // Ahead of every other badge: the counts beside it were all computed from
623
+ // metadata that would not parse, so they are zeros, not measurements.
624
+ const badge = el('span', { class: 'badge badge-unreadable', text: 'unreadable' });
625
+ badge.title = c.unreadable;
626
+ row.append(badge);
627
+ }
617
628
  if (c.artifactSummary.blocked > 0) {
618
629
  row.append(el('span', { class: 'badge badge-blocked-warn', text: 'blocked' }));
619
630
  }
@@ -840,7 +851,7 @@ function fileAccordion(file) {
840
851
  body.textContent = 'Loading...';
841
852
  try {
842
853
  const data = await api('/api/render?file=' + encodeURIComponent(file.path));
843
- body.innerHTML = data.html; // controlled container; server-rendered markdown
854
+ body.innerHTML = data.html; // sanitized by core/markdown-render.ts - raw HTML is escaped there, not here
844
855
  } catch (e) {
845
856
  loaded = false;
846
857
  body.textContent = 'Failed to render (' + (e.status || 'error') + ').';
@@ -1008,7 +1019,7 @@ async function renderSpecDetail(id) {
1008
1019
 
1009
1020
  try {
1010
1021
  const data = await api('/api/render?file=' + encodeURIComponent(`tospec/specs/${id}/spec.md`));
1011
- body.innerHTML = data.html; // controlled container; server-rendered markdown
1022
+ body.innerHTML = data.html; // sanitized by core/markdown-render.ts - raw HTML is escaped there, not here
1012
1023
  } catch {
1013
1024
  body.textContent = 'Failed to render spec.';
1014
1025
  }
@@ -1063,7 +1074,7 @@ async function renderDecisionContent(pane, file) {
1063
1074
  );
1064
1075
  try {
1065
1076
  const data = await api('/api/render?file=' + encodeURIComponent(d.path));
1066
- body.innerHTML = data.html; // controlled container; server-rendered markdown
1077
+ body.innerHTML = data.html; // sanitized by core/markdown-render.ts - raw HTML is escaped there, not here
1067
1078
  enhanceDecisionQA(body, d.content); // 決策過程 → Q/A cards (client-side, graceful fallback)
1068
1079
  } catch {
1069
1080
  body.textContent = 'Failed to render decision.';
@@ -671,6 +671,13 @@ a.row:hover {
671
671
  color: var(--error);
672
672
  }
673
673
 
674
+ /* A change whose .tospec.yaml would not parse: every count beside it is a zero
675
+ rather than a measurement, so it is marked as an error, not a progress state. */
676
+ .badge-unreadable {
677
+ background: var(--error-bg);
678
+ color: var(--error);
679
+ }
680
+
674
681
  /* -- artifact blocks & status -- */
675
682
  .blocks {
676
683
  display: inline-flex;
@@ -52,6 +52,7 @@ const STRINGS = {
52
52
  'control.theme': 'Theme',
53
53
  'control.language': 'Language',
54
54
  'control.refresh': '↻ Refresh',
55
+ 'control.capturedAt': 'Data captured at {time} — Refresh to re-read',
55
56
  'range.30': 'Last 30 days',
56
57
  'range.90': 'Last 90 days',
57
58
  'range.365': 'Last year',
@@ -118,6 +119,7 @@ const STRINGS = {
118
119
  'control.theme': '主題',
119
120
  'control.language': '語言',
120
121
  'control.refresh': '↻ 重新讀取',
122
+ 'control.capturedAt': '資料擷取於 {time}, 按重新讀取以更新',
121
123
  'range.30': '最近 30 天',
122
124
  'range.90': '最近 90 天',
123
125
  'range.365': '最近一年',
@@ -776,11 +778,31 @@ function renderControls() {
776
778
  // The report measures the session viewing it, so what is on screen goes
777
779
  // stale while it is being read. This is the only way to pick that up: every
778
780
  // other control recomputes from the parse taken when the server started.
781
+ capturedAtNote(),
779
782
  refreshButton(),
780
783
  ]),
781
784
  ]);
782
785
  }
783
786
 
787
+ /**
788
+ * Without this the page presented a startup snapshot as if it were live: run a
789
+ * skill, reload the browser, and the numbers were unchanged with nothing on
790
+ * screen explaining why. The cache itself is deliberate (the URL is only printed
791
+ * once the report behind it can be produced), so stating the data's age is more
792
+ * honest than removing it.
793
+ */
794
+ function capturedAtNote() {
795
+ const captured = current && current.capturedAt;
796
+ if (!captured) return el('span', { class: 'captured-at' });
797
+ const when = new Date(captured);
798
+ const time = Number.isNaN(when.getTime()) ? captured : when.toLocaleTimeString();
799
+ return el('span', {
800
+ class: 'captured-at',
801
+ title: captured,
802
+ text: t('control.capturedAt').replace('{time}', time),
803
+ });
804
+ }
805
+
784
806
  function refreshButton() {
785
807
  const button = el('button', { type: 'button', class: 'refresh', text: t('control.refresh') });
786
808
  button.addEventListener('click', () => void load(true));
@@ -191,3 +191,11 @@ body {
191
191
  gap: 6px;
192
192
  padding-bottom: 6px;
193
193
  }
194
+
195
+ /* States when the numbers were read, so a startup snapshot is not mistaken for
196
+ live data. Sits beside Refresh, which is the control that changes it. */
197
+ .captured-at {
198
+ align-self: center;
199
+ font-size: 12px;
200
+ color: var(--muted);
201
+ }
@@ -0,0 +1,3 @@
1
+ # Decision
2
+
3
+ Any material decision reached during discussion must be recorded with /tospec-decision, which creates a decision file and captures the relevant decision details.
@@ -0,0 +1,19 @@
1
+ # Single Source of Truth
2
+
3
+ **Priority: MUST. This rule outranks any conflicting statement you find in documentation, comments, planning artifacts, or earlier conversation.**
4
+
5
+ ## 1. Source code is authoritative
6
+
7
+ The source code in this repository is the only authoritative description of how the system actually behaves. Specifications, design documents, READMEs, changelogs, code comments, and prior summaries are secondary: each records intent at the moment it was written, and each drifts as the code moves on.
8
+
9
+ - When a document contradicts the code, **the code is correct**. Report the discrepancy; do not change the code to match the document unless the user explicitly asks for that.
10
+ - Before asserting how something works, verify it in the code. An answer sourced from a document alone is unverified.
11
+
12
+ ## 2. Archived documents are presumed stale
13
+
14
+ Do not read files under `tospec/changes/archive/` or `tospec/tickets/archive/` unless one of these holds:
15
+
16
+ - the task genuinely cannot be completed without them, or
17
+ - the user explicitly asks you to.
18
+
19
+ These directories store snapshots of finished work. Nothing updates them when the code changes, so treating them as current will make you describe behavior that no longer exists.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAgC,MAAM,WAAW,CAAC;AAwElE,QAAA,MAAM,OAAO,SAAgB,CAAC;AA6Y9B,OAAO,EAAE,OAAO,EAAE,CAAC;AAEnB,wBAAgB,MAAM,CAAC,IAAI,WAAe,GAAG,IAAI,CAEhD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAgD,MAAM,WAAW,CAAC;AAmIlF,QAAA,MAAM,OAAO,SAAgB,CAAC;AA+f9B,OAAO,EAAE,OAAO,EAAE,CAAC;AAiBnB,wBAAgB,MAAM,CAAC,IAAI,WAAe,GAAG,IAAI,CAiChD"}