@seanmars/tospec 0.19.0-beta.7 → 0.20.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 (429) hide show
  1. package/CHANGELOG.md +60 -231
  2. package/README.md +69 -82
  3. package/assets/dashboard/app.js +14 -3
  4. package/assets/dashboard/style.css +7 -0
  5. package/assets/rules/tospec/decision.md +3 -0
  6. package/dist/cli/index.d.ts.map +1 -1
  7. package/dist/cli/index.js +227 -86
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/commands/config.d.ts +9 -17
  10. package/dist/commands/config.d.ts.map +1 -1
  11. package/dist/commands/config.js +366 -107
  12. package/dist/commands/config.js.map +1 -1
  13. package/dist/commands/dashboard.d.ts +57 -99
  14. package/dist/commands/dashboard.d.ts.map +1 -1
  15. package/dist/commands/dashboard.js +224 -282
  16. package/dist/commands/dashboard.js.map +1 -1
  17. package/dist/commands/decision.d.ts +41 -19
  18. package/dist/commands/decision.d.ts.map +1 -1
  19. package/dist/commands/decision.js +385 -79
  20. package/dist/commands/decision.js.map +1 -1
  21. package/dist/commands/metrics.d.ts +28 -51
  22. package/dist/commands/metrics.d.ts.map +1 -1
  23. package/dist/commands/metrics.js +62 -93
  24. package/dist/commands/metrics.js.map +1 -1
  25. package/dist/commands/shared-output.d.ts +18 -21
  26. package/dist/commands/shared-output.d.ts.map +1 -1
  27. package/dist/commands/shared-output.js +49 -23
  28. package/dist/commands/shared-output.js.map +1 -1
  29. package/dist/commands/show.d.ts +7 -0
  30. package/dist/commands/show.d.ts.map +1 -1
  31. package/dist/commands/show.js +29 -7
  32. package/dist/commands/show.js.map +1 -1
  33. package/dist/commands/validate.d.ts +43 -45
  34. package/dist/commands/validate.d.ts.map +1 -1
  35. package/dist/commands/validate.js +262 -129
  36. package/dist/commands/validate.js.map +1 -1
  37. package/dist/commands/workflow/index.d.ts +6 -10
  38. package/dist/commands/workflow/index.d.ts.map +1 -1
  39. package/dist/commands/workflow/index.js +6 -10
  40. package/dist/commands/workflow/index.js.map +1 -1
  41. package/dist/commands/workflow/instructions.d.ts +21 -8
  42. package/dist/commands/workflow/instructions.d.ts.map +1 -1
  43. package/dist/commands/workflow/instructions.js +254 -95
  44. package/dist/commands/workflow/instructions.js.map +1 -1
  45. package/dist/commands/workflow/new-change.d.ts +4 -5
  46. package/dist/commands/workflow/new-change.d.ts.map +1 -1
  47. package/dist/commands/workflow/new-change.js +90 -25
  48. package/dist/commands/workflow/new-change.js.map +1 -1
  49. package/dist/commands/workflow/schemas.d.ts +3 -5
  50. package/dist/commands/workflow/schemas.d.ts.map +1 -1
  51. package/dist/commands/workflow/schemas.js +37 -11
  52. package/dist/commands/workflow/schemas.js.map +1 -1
  53. package/dist/commands/workflow/shared.d.ts +48 -21
  54. package/dist/commands/workflow/shared.d.ts.map +1 -1
  55. package/dist/commands/workflow/shared.js +36 -33
  56. package/dist/commands/workflow/shared.js.map +1 -1
  57. package/dist/commands/workflow/status.d.ts +10 -6
  58. package/dist/commands/workflow/status.d.ts.map +1 -1
  59. package/dist/commands/workflow/status.js +81 -42
  60. package/dist/commands/workflow/status.js.map +1 -1
  61. package/dist/commands/workflow/templates.d.ts +10 -3
  62. package/dist/commands/workflow/templates.d.ts.map +1 -1
  63. package/dist/commands/workflow/templates.js +39 -40
  64. package/dist/commands/workflow/templates.js.map +1 -1
  65. package/dist/core/archive.d.ts +26 -21
  66. package/dist/core/archive.d.ts.map +1 -1
  67. package/dist/core/archive.js +378 -235
  68. package/dist/core/archive.js.map +1 -1
  69. package/dist/core/artifact-graph/graph.d.ts +25 -42
  70. package/dist/core/artifact-graph/graph.d.ts.map +1 -1
  71. package/dist/core/artifact-graph/graph.js +45 -63
  72. package/dist/core/artifact-graph/graph.js.map +1 -1
  73. package/dist/core/artifact-graph/index.d.ts +1 -1
  74. package/dist/core/artifact-graph/index.d.ts.map +1 -1
  75. package/dist/core/artifact-graph/index.js +1 -1
  76. package/dist/core/artifact-graph/index.js.map +1 -1
  77. package/dist/core/artifact-graph/instruction-loader.d.ts +97 -103
  78. package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -1
  79. package/dist/core/artifact-graph/instruction-loader.js +146 -119
  80. package/dist/core/artifact-graph/instruction-loader.js.map +1 -1
  81. package/dist/core/artifact-graph/outputs.d.ts +9 -23
  82. package/dist/core/artifact-graph/outputs.d.ts.map +1 -1
  83. package/dist/core/artifact-graph/outputs.js +45 -38
  84. package/dist/core/artifact-graph/outputs.js.map +1 -1
  85. package/dist/core/artifact-graph/resolver.d.ts +44 -63
  86. package/dist/core/artifact-graph/resolver.d.ts.map +1 -1
  87. package/dist/core/artifact-graph/resolver.js +85 -86
  88. package/dist/core/artifact-graph/resolver.js.map +1 -1
  89. package/dist/core/artifact-graph/schema.d.ts +0 -6
  90. package/dist/core/artifact-graph/schema.d.ts.map +1 -1
  91. package/dist/core/artifact-graph/schema.js +7 -32
  92. package/dist/core/artifact-graph/schema.js.map +1 -1
  93. package/dist/core/artifact-graph/state.d.ts +1 -8
  94. package/dist/core/artifact-graph/state.d.ts.map +1 -1
  95. package/dist/core/artifact-graph/state.js +2 -17
  96. package/dist/core/artifact-graph/state.js.map +1 -1
  97. package/dist/core/artifact-graph/stub-detection.d.ts +12 -0
  98. package/dist/core/artifact-graph/stub-detection.d.ts.map +1 -0
  99. package/dist/core/artifact-graph/stub-detection.js +39 -0
  100. package/dist/core/artifact-graph/stub-detection.js.map +1 -0
  101. package/dist/core/artifact-graph/types.d.ts +4 -0
  102. package/dist/core/artifact-graph/types.d.ts.map +1 -1
  103. package/dist/core/artifact-graph/types.js +30 -10
  104. package/dist/core/artifact-graph/types.js.map +1 -1
  105. package/dist/core/available-tools.d.ts +3 -12
  106. package/dist/core/available-tools.d.ts.map +1 -1
  107. package/dist/core/available-tools.js +4 -13
  108. package/dist/core/available-tools.js.map +1 -1
  109. package/dist/core/change-metadata/schema.d.ts +1 -1
  110. package/dist/core/change-metadata/schema.d.ts.map +1 -1
  111. package/dist/core/change-metadata/schema.js +10 -7
  112. package/dist/core/change-metadata/schema.js.map +1 -1
  113. package/dist/core/change-presenter.d.ts +23 -18
  114. package/dist/core/change-presenter.d.ts.map +1 -1
  115. package/dist/core/change-presenter.js +102 -43
  116. package/dist/core/change-presenter.js.map +1 -1
  117. package/dist/core/change-status-policy.d.ts +8 -1
  118. package/dist/core/change-status-policy.d.ts.map +1 -1
  119. package/dist/core/change-status-policy.js +25 -1
  120. package/dist/core/change-status-policy.js.map +1 -1
  121. package/dist/core/codex-metrics.d.ts +25 -45
  122. package/dist/core/codex-metrics.d.ts.map +1 -1
  123. package/dist/core/codex-metrics.js +44 -88
  124. package/dist/core/codex-metrics.js.map +1 -1
  125. package/dist/core/codex-residue.d.ts +14 -15
  126. package/dist/core/codex-residue.d.ts.map +1 -1
  127. package/dist/core/codex-residue.js +18 -22
  128. package/dist/core/codex-residue.js.map +1 -1
  129. package/dist/core/command-generation/adapters/claude.d.ts +2 -9
  130. package/dist/core/command-generation/adapters/claude.d.ts.map +1 -1
  131. package/dist/core/command-generation/adapters/claude.js +2 -12
  132. package/dist/core/command-generation/adapters/claude.js.map +1 -1
  133. package/dist/core/command-generation/adapters/index.d.ts +1 -9
  134. package/dist/core/command-generation/adapters/index.d.ts.map +1 -1
  135. package/dist/core/command-generation/adapters/index.js +1 -9
  136. package/dist/core/command-generation/adapters/index.js.map +1 -1
  137. package/dist/core/command-generation/generator.d.ts +0 -17
  138. package/dist/core/command-generation/generator.d.ts.map +1 -1
  139. package/dist/core/command-generation/generator.js +0 -17
  140. package/dist/core/command-generation/generator.js.map +1 -1
  141. package/dist/core/command-generation/index.d.ts +2 -5
  142. package/dist/core/command-generation/index.d.ts.map +1 -1
  143. package/dist/core/command-generation/index.js +0 -9
  144. package/dist/core/command-generation/index.js.map +1 -1
  145. package/dist/core/command-generation/types.d.ts +10 -36
  146. package/dist/core/command-generation/types.d.ts.map +1 -1
  147. package/dist/core/command-generation/types.js +0 -6
  148. package/dist/core/command-generation/types.js.map +1 -1
  149. package/dist/core/command-generation/yaml.d.ts +3 -18
  150. package/dist/core/command-generation/yaml.d.ts.map +1 -1
  151. package/dist/core/command-generation/yaml.js +5 -23
  152. package/dist/core/command-generation/yaml.js.map +1 -1
  153. package/dist/core/config-prompts.d.ts +2 -4
  154. package/dist/core/config-prompts.d.ts.map +1 -1
  155. package/dist/core/config-prompts.js +2 -7
  156. package/dist/core/config-prompts.js.map +1 -1
  157. package/dist/core/config-schema.d.ts +8 -53
  158. package/dist/core/config-schema.d.ts.map +1 -1
  159. package/dist/core/config-schema.js +49 -62
  160. package/dist/core/config-schema.js.map +1 -1
  161. package/dist/core/config.d.ts +39 -26
  162. package/dist/core/config.d.ts.map +1 -1
  163. package/dist/core/config.js +36 -22
  164. package/dist/core/config.js.map +1 -1
  165. package/dist/core/dashboard-activity.d.ts +7 -9
  166. package/dist/core/dashboard-activity.d.ts.map +1 -1
  167. package/dist/core/dashboard-activity.js +26 -24
  168. package/dist/core/dashboard-activity.js.map +1 -1
  169. package/dist/core/dashboard-data.d.ts +40 -22
  170. package/dist/core/dashboard-data.d.ts.map +1 -1
  171. package/dist/core/dashboard-data.js +62 -70
  172. package/dist/core/dashboard-data.js.map +1 -1
  173. package/dist/core/global-config.d.ts +24 -53
  174. package/dist/core/global-config.d.ts.map +1 -1
  175. package/dist/core/global-config.js +38 -67
  176. package/dist/core/global-config.js.map +1 -1
  177. package/dist/core/init.d.ts +25 -19
  178. package/dist/core/init.d.ts.map +1 -1
  179. package/dist/core/init.js +168 -181
  180. package/dist/core/init.js.map +1 -1
  181. package/dist/core/list.d.ts +1 -1
  182. package/dist/core/list.d.ts.map +1 -1
  183. package/dist/core/list.js +121 -28
  184. package/dist/core/list.js.map +1 -1
  185. package/dist/core/local-server.d.ts +41 -83
  186. package/dist/core/local-server.d.ts.map +1 -1
  187. package/dist/core/local-server.js +53 -98
  188. package/dist/core/local-server.js.map +1 -1
  189. package/dist/core/markdown-render.d.ts +25 -0
  190. package/dist/core/markdown-render.d.ts.map +1 -0
  191. package/dist/core/markdown-render.js +94 -0
  192. package/dist/core/markdown-render.js.map +1 -0
  193. package/dist/core/migrate.d.ts +29 -14
  194. package/dist/core/migrate.d.ts.map +1 -1
  195. package/dist/core/migrate.js +184 -122
  196. package/dist/core/migrate.js.map +1 -1
  197. package/dist/core/parsers/change-parser.d.ts +7 -10
  198. package/dist/core/parsers/change-parser.d.ts.map +1 -1
  199. package/dist/core/parsers/change-parser.js +48 -56
  200. package/dist/core/parsers/change-parser.js.map +1 -1
  201. package/dist/core/parsers/markdown-parser.d.ts +8 -9
  202. package/dist/core/parsers/markdown-parser.d.ts.map +1 -1
  203. package/dist/core/parsers/markdown-parser.js +31 -22
  204. package/dist/core/parsers/markdown-parser.js.map +1 -1
  205. package/dist/core/parsers/requirement-blocks.d.ts +43 -15
  206. package/dist/core/parsers/requirement-blocks.d.ts.map +1 -1
  207. package/dist/core/parsers/requirement-blocks.js +142 -70
  208. package/dist/core/parsers/requirement-blocks.js.map +1 -1
  209. package/dist/core/parsers/requirement-text.d.ts +73 -79
  210. package/dist/core/parsers/requirement-text.d.ts.map +1 -1
  211. package/dist/core/parsers/requirement-text.js +137 -79
  212. package/dist/core/parsers/requirement-text.js.map +1 -1
  213. package/dist/core/parsers/spec-structure.d.ts.map +1 -1
  214. package/dist/core/parsers/spec-structure.js +10 -6
  215. package/dist/core/parsers/spec-structure.js.map +1 -1
  216. package/dist/core/profiles.d.ts +3 -10
  217. package/dist/core/profiles.d.ts.map +1 -1
  218. package/dist/core/profiles.js +5 -12
  219. package/dist/core/profiles.js.map +1 -1
  220. package/dist/core/project-config.d.ts +43 -44
  221. package/dist/core/project-config.d.ts.map +1 -1
  222. package/dist/core/project-config.js +107 -82
  223. package/dist/core/project-config.js.map +1 -1
  224. package/dist/core/project-layout.d.ts +9 -17
  225. package/dist/core/project-layout.d.ts.map +1 -1
  226. package/dist/core/project-layout.js +16 -26
  227. package/dist/core/project-layout.js.map +1 -1
  228. package/dist/core/root-selection.d.ts +11 -7
  229. package/dist/core/root-selection.d.ts.map +1 -1
  230. package/dist/core/root-selection.js +7 -8
  231. package/dist/core/root-selection.js.map +1 -1
  232. package/dist/core/rules.d.ts +6 -1
  233. package/dist/core/rules.d.ts.map +1 -1
  234. package/dist/core/rules.js +23 -2
  235. package/dist/core/rules.js.map +1 -1
  236. package/dist/core/schema-names.d.ts +16 -0
  237. package/dist/core/schema-names.d.ts.map +1 -0
  238. package/dist/core/schema-names.js +16 -0
  239. package/dist/core/schema-names.js.map +1 -0
  240. package/dist/core/schemas/base.schema.d.ts +3 -0
  241. package/dist/core/schemas/base.schema.d.ts.map +1 -1
  242. package/dist/core/schemas/base.schema.js +22 -6
  243. package/dist/core/schemas/base.schema.js.map +1 -1
  244. package/dist/core/schemas/change.schema.d.ts +16 -0
  245. package/dist/core/schemas/change.schema.d.ts.map +1 -1
  246. package/dist/core/schemas/change.schema.js +41 -10
  247. package/dist/core/schemas/change.schema.js.map +1 -1
  248. package/dist/core/schemas/spec.schema.d.ts +2 -0
  249. package/dist/core/schemas/spec.schema.d.ts.map +1 -1
  250. package/dist/core/shared/index.d.ts +3 -8
  251. package/dist/core/shared/index.d.ts.map +1 -1
  252. package/dist/core/shared/index.js +3 -8
  253. package/dist/core/shared/index.js.map +1 -1
  254. package/dist/core/shared/rules-generation.d.ts +19 -8
  255. package/dist/core/shared/rules-generation.d.ts.map +1 -1
  256. package/dist/core/shared/rules-generation.js +117 -50
  257. package/dist/core/shared/rules-generation.js.map +1 -1
  258. package/dist/core/shared/skill-generation.d.ts +28 -43
  259. package/dist/core/shared/skill-generation.d.ts.map +1 -1
  260. package/dist/core/shared/skill-generation.js +82 -51
  261. package/dist/core/shared/skill-generation.js.map +1 -1
  262. package/dist/core/shared/tool-detection.d.ts +39 -61
  263. package/dist/core/shared/tool-detection.d.ts.map +1 -1
  264. package/dist/core/shared/tool-detection.js +88 -80
  265. package/dist/core/shared/tool-detection.js.map +1 -1
  266. package/dist/core/skill-metrics.d.ts +36 -63
  267. package/dist/core/skill-metrics.d.ts.map +1 -1
  268. package/dist/core/skill-metrics.js +34 -73
  269. package/dist/core/skill-metrics.js.map +1 -1
  270. package/dist/core/spec-presenter.js +6 -6
  271. package/dist/core/spec-presenter.js.map +1 -1
  272. package/dist/core/specs-apply.d.ts +22 -23
  273. package/dist/core/specs-apply.d.ts.map +1 -1
  274. package/dist/core/specs-apply.js +166 -193
  275. package/dist/core/specs-apply.js.map +1 -1
  276. package/dist/core/templates/fragments/interview.d.ts +2 -6
  277. package/dist/core/templates/fragments/interview.d.ts.map +1 -1
  278. package/dist/core/templates/fragments/interview.js +2 -6
  279. package/dist/core/templates/fragments/interview.js.map +1 -1
  280. package/dist/core/templates/fragments/next-step.d.ts +4 -8
  281. package/dist/core/templates/fragments/next-step.d.ts.map +1 -1
  282. package/dist/core/templates/fragments/next-step.js +4 -8
  283. package/dist/core/templates/fragments/next-step.js.map +1 -1
  284. package/dist/core/templates/fragments/validate.d.ts +13 -0
  285. package/dist/core/templates/fragments/validate.d.ts.map +1 -0
  286. package/dist/core/templates/fragments/validate.js +13 -0
  287. package/dist/core/templates/fragments/validate.js.map +1 -0
  288. package/dist/core/templates/fragments/verify.d.ts +9 -12
  289. package/dist/core/templates/fragments/verify.d.ts.map +1 -1
  290. package/dist/core/templates/fragments/verify.js +9 -12
  291. package/dist/core/templates/fragments/verify.js.map +1 -1
  292. package/dist/core/templates/index.d.ts +0 -6
  293. package/dist/core/templates/index.d.ts.map +1 -1
  294. package/dist/core/templates/index.js +0 -7
  295. package/dist/core/templates/index.js.map +1 -1
  296. package/dist/core/templates/skill-templates.d.ts +1 -5
  297. package/dist/core/templates/skill-templates.d.ts.map +1 -1
  298. package/dist/core/templates/skill-templates.js +0 -5
  299. package/dist/core/templates/skill-templates.js.map +1 -1
  300. package/dist/core/templates/types.d.ts +3 -7
  301. package/dist/core/templates/types.d.ts.map +1 -1
  302. package/dist/core/templates/types.js +0 -3
  303. package/dist/core/templates/types.js.map +1 -1
  304. package/dist/core/templates/workflows/apply.d.ts +3 -9
  305. package/dist/core/templates/workflows/apply.d.ts.map +1 -1
  306. package/dist/core/templates/workflows/apply.js +11 -13
  307. package/dist/core/templates/workflows/apply.js.map +1 -1
  308. package/dist/core/templates/workflows/archive.d.ts +0 -6
  309. package/dist/core/templates/workflows/archive.d.ts.map +1 -1
  310. package/dist/core/templates/workflows/archive.js +19 -6
  311. package/dist/core/templates/workflows/archive.js.map +1 -1
  312. package/dist/core/templates/workflows/decision.js +4 -4
  313. package/dist/core/templates/workflows/decision.js.map +1 -1
  314. package/dist/core/templates/workflows/explore.js +1 -1
  315. package/dist/core/templates/workflows/grill.d.ts.map +1 -1
  316. package/dist/core/templates/workflows/grill.js +0 -2
  317. package/dist/core/templates/workflows/grill.js.map +1 -1
  318. package/dist/core/templates/workflows/issue.d.ts +0 -6
  319. package/dist/core/templates/workflows/issue.d.ts.map +1 -1
  320. package/dist/core/templates/workflows/issue.js +3 -2
  321. package/dist/core/templates/workflows/issue.js.map +1 -1
  322. package/dist/core/templates/workflows/propose.d.ts +0 -6
  323. package/dist/core/templates/workflows/propose.d.ts.map +1 -1
  324. package/dist/core/templates/workflows/propose.js +2 -2
  325. package/dist/core/templates/workflows/propose.js.map +1 -1
  326. package/dist/core/templates/workflows/sync.d.ts +2 -8
  327. package/dist/core/templates/workflows/sync.d.ts.map +1 -1
  328. package/dist/core/templates/workflows/sync.js +4 -3
  329. package/dist/core/templates/workflows/sync.js.map +1 -1
  330. package/dist/core/templates/workflows/update.d.ts +0 -6
  331. package/dist/core/templates/workflows/update.d.ts.map +1 -1
  332. package/dist/core/templates/workflows/update.js +9 -2
  333. package/dist/core/templates/workflows/update.js.map +1 -1
  334. package/dist/core/update.d.ts +23 -23
  335. package/dist/core/update.d.ts.map +1 -1
  336. package/dist/core/update.js +139 -118
  337. package/dist/core/update.js.map +1 -1
  338. package/dist/core/user-state-migration.d.ts +13 -15
  339. package/dist/core/user-state-migration.d.ts.map +1 -1
  340. package/dist/core/user-state-migration.js +16 -20
  341. package/dist/core/user-state-migration.js.map +1 -1
  342. package/dist/core/validation/constants.d.ts +4 -10
  343. package/dist/core/validation/constants.d.ts.map +1 -1
  344. package/dist/core/validation/constants.js +25 -20
  345. package/dist/core/validation/constants.js.map +1 -1
  346. package/dist/core/validation/prose-length.d.ts +15 -0
  347. package/dist/core/validation/prose-length.d.ts.map +1 -0
  348. package/dist/core/validation/prose-length.js +29 -0
  349. package/dist/core/validation/prose-length.js.map +1 -0
  350. package/dist/core/validation/purpose-placeholder.d.ts +9 -16
  351. package/dist/core/validation/purpose-placeholder.d.ts.map +1 -1
  352. package/dist/core/validation/purpose-placeholder.js +30 -44
  353. package/dist/core/validation/purpose-placeholder.js.map +1 -1
  354. package/dist/core/validation/section-validator.d.ts +4 -4
  355. package/dist/core/validation/section-validator.d.ts.map +1 -1
  356. package/dist/core/validation/section-validator.js +43 -7
  357. package/dist/core/validation/section-validator.js.map +1 -1
  358. package/dist/core/validation/task-numbering.d.ts +6 -3
  359. package/dist/core/validation/task-numbering.d.ts.map +1 -1
  360. package/dist/core/validation/task-numbering.js +23 -11
  361. package/dist/core/validation/task-numbering.js.map +1 -1
  362. package/dist/core/validation/types.d.ts +18 -0
  363. package/dist/core/validation/types.d.ts.map +1 -1
  364. package/dist/core/validation/types.js +12 -1
  365. package/dist/core/validation/types.js.map +1 -1
  366. package/dist/core/validation/validator.d.ts +43 -63
  367. package/dist/core/validation/validator.d.ts.map +1 -1
  368. package/dist/core/validation/validator.js +500 -267
  369. package/dist/core/validation/validator.js.map +1 -1
  370. package/dist/prompts/searchable-multi-select.d.ts +3 -8
  371. package/dist/prompts/searchable-multi-select.d.ts.map +1 -1
  372. package/dist/prompts/searchable-multi-select.js +16 -39
  373. package/dist/prompts/searchable-multi-select.js.map +1 -1
  374. package/dist/utils/change-metadata.d.ts +11 -50
  375. package/dist/utils/change-metadata.d.ts.map +1 -1
  376. package/dist/utils/change-metadata.js +48 -67
  377. package/dist/utils/change-metadata.js.map +1 -1
  378. package/dist/utils/change-utils.d.ts +30 -54
  379. package/dist/utils/change-utils.d.ts.map +1 -1
  380. package/dist/utils/change-utils.js +133 -91
  381. package/dist/utils/change-utils.js.map +1 -1
  382. package/dist/utils/file-lock.d.ts +39 -0
  383. package/dist/utils/file-lock.d.ts.map +1 -0
  384. package/dist/utils/file-lock.js +149 -0
  385. package/dist/utils/file-lock.js.map +1 -0
  386. package/dist/utils/file-system.d.ts +12 -32
  387. package/dist/utils/file-system.d.ts.map +1 -1
  388. package/dist/utils/file-system.js +16 -40
  389. package/dist/utils/file-system.js.map +1 -1
  390. package/dist/utils/frontmatter.d.ts +7 -11
  391. package/dist/utils/frontmatter.d.ts.map +1 -1
  392. package/dist/utils/frontmatter.js +11 -11
  393. package/dist/utils/frontmatter.js.map +1 -1
  394. package/dist/utils/interactive.d.ts +4 -9
  395. package/dist/utils/interactive.d.ts.map +1 -1
  396. package/dist/utils/interactive.js +2 -4
  397. package/dist/utils/interactive.js.map +1 -1
  398. package/dist/utils/item-discovery.d.ts +15 -10
  399. package/dist/utils/item-discovery.d.ts.map +1 -1
  400. package/dist/utils/item-discovery.js +42 -47
  401. package/dist/utils/item-discovery.js.map +1 -1
  402. package/dist/utils/link.d.ts +9 -18
  403. package/dist/utils/link.d.ts.map +1 -1
  404. package/dist/utils/link.js +9 -18
  405. package/dist/utils/link.js.map +1 -1
  406. package/dist/utils/requirement-diff.d.ts +13 -23
  407. package/dist/utils/requirement-diff.d.ts.map +1 -1
  408. package/dist/utils/requirement-diff.js +13 -23
  409. package/dist/utils/requirement-diff.js.map +1 -1
  410. package/dist/utils/spec-files.d.ts +7 -20
  411. package/dist/utils/spec-files.d.ts.map +1 -1
  412. package/dist/utils/spec-files.js +26 -51
  413. package/dist/utils/spec-files.js.map +1 -1
  414. package/dist/utils/task-progress.d.ts +11 -9
  415. package/dist/utils/task-progress.d.ts.map +1 -1
  416. package/dist/utils/task-progress.js +53 -32
  417. package/dist/utils/task-progress.js.map +1 -1
  418. package/dist/utils/timestamp.d.ts +5 -8
  419. package/dist/utils/timestamp.d.ts.map +1 -1
  420. package/dist/utils/timestamp.js +5 -8
  421. package/dist/utils/timestamp.js.map +1 -1
  422. package/package.json +2 -3
  423. package/schemas/decision/templates/decision.md +3 -1
  424. package/schemas/decision/templates/index.md +2 -2
  425. package/schemas/issue/schema.yaml +8 -1
  426. package/schemas/issue/templates/spec.md +27 -2
  427. package/schemas/sdd/schema.yaml +20 -1
  428. package/schemas/sdd/templates/spec.md +27 -2
  429. /package/assets/rules/tospec/{single-sourc-of-truth.md → single-source-of-truth.md} +0 -0
@@ -2,9 +2,6 @@ import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import fg from 'fast-glob';
4
4
  import { FileSystemUtils } from '../../utils/file-system.js';
5
- /**
6
- * Checks if a path contains glob pattern characters.
7
- */
8
5
  export function isGlobPattern(pattern) {
9
6
  return pattern.includes('*') || pattern.includes('?') || pattern.includes('[');
10
7
  }
@@ -12,17 +9,13 @@ export function isGlobPattern(pattern) {
12
9
  * A pattern whose wildcard occupies exactly one whole path segment, with every
13
10
  * other segment literal — `specs/*` or `specs/<*>/spec.md`.
14
11
  *
15
- * This is the only glob shape any shipped schema declares (`specs/*<!---->/spec.md`, in
16
- * both `sdd` and `issue`), and the delta-spec layout is fixed at exactly one
17
- * directory level, so it is the only shape that occurs in practice. Answering it
18
- * with `readdir` + `stat` skips a general glob engine's per-call pattern
19
- * compilation and stream setup — the dominant cost on the dashboard's hot path,
20
- * where the whole aggregation re-runs on every file save.
12
+ * The only glob shape any shipped schema declares. Answering it with `readdir` +
13
+ * `stat` skips a glob engine's per-call pattern compilation and stream setup —
14
+ * the dominant cost on the dashboard's hot path, which re-runs the whole
15
+ * aggregation on every file save.
21
16
  *
22
- * Returns `null` when the pattern is any other shape, so the caller falls back
23
- * to fast-glob. `generates` is schema-authored and a user schema may declare
24
- * something this cannot express; the fast path is an optimisation, not a
25
- * narrowing of the contract.
17
+ * `null` for any other shape, so the caller falls back to fast-glob: the fast
18
+ * path is an optimisation, not a narrowing of the contract.
26
19
  */
27
20
  function splitSingleWildcard(generates) {
28
21
  const segments = FileSystemUtils.toPosixPath(generates).split('/');
@@ -40,16 +33,40 @@ function splitSingleWildcard(generates) {
40
33
  return null;
41
34
  return { prefix: segments.slice(0, wildcardIndex), suffix: segments.slice(wildcardIndex + 1) };
42
35
  }
43
- /** Shared tail so the sync and async resolvers cannot drift in how they finish. */
36
+ /** Shared tail, so the sync and async resolvers finish identically. */
44
37
  function finalizeMatches(matches) {
45
38
  return Array.from(new Set(matches)).sort();
46
39
  }
40
+ /**
41
+ * Whether each fixed segment after the wildcard is spelled on disk exactly as
42
+ * the pattern spells it. `stat` answers case-insensitively on Windows and
43
+ * macOS while fast-glob matches names literally, so without this the fast
44
+ * path counted `SPEC.MD` as done and the glob path did not.
45
+ */
46
+ function hasExactSuffixSpelling(dir, suffix) {
47
+ let current = dir;
48
+ for (const segment of suffix) {
49
+ if (!fs.readdirSync(current).includes(segment))
50
+ return false;
51
+ current = path.join(current, segment);
52
+ }
53
+ return true;
54
+ }
55
+ async function hasExactSuffixSpellingAsync(dir, suffix) {
56
+ let current = dir;
57
+ for (const segment of suffix) {
58
+ if (!(await fs.promises.readdir(current)).includes(segment))
59
+ return false;
60
+ current = path.join(current, segment);
61
+ }
62
+ return true;
63
+ }
47
64
  function globFallbackOptions(changeDir) {
48
65
  return { cwd: changeDir, onlyFiles: true, absolute: true };
49
66
  }
50
67
  /**
51
- * Resolves an artifact's output path(s) to concrete files that currently exist.
52
- * Returns absolute file paths. Glob matches are sorted for deterministic output.
68
+ * Absolute paths of the files an artifact's `generates` currently resolves to.
69
+ * Sorted, for deterministic output.
53
70
  */
54
71
  export function resolveArtifactOutputs(changeDir, generates) {
55
72
  if (!isGlobPattern(generates)) {
@@ -81,10 +98,10 @@ export function resolveArtifactOutputs(changeDir, generates) {
81
98
  continue;
82
99
  const candidate = path.join(baseDir, entry, ...single.suffix);
83
100
  try {
84
- // statSync (not the Dirent type) so symlinks resolve the way fast-glob's
85
- // default followSymbolicLinks does, and so `onlyFiles` still holds for a
101
+ // statSync, not the Dirent type, so symlinks resolve as fast-glob's
102
+ // default followSymbolicLinks does and `onlyFiles` still holds for a
86
103
  // *directory* named spec.md.
87
- if (fs.statSync(candidate).isFile()) {
104
+ if (fs.statSync(candidate).isFile() && hasExactSuffixSpelling(path.join(baseDir, entry), single.suffix)) {
88
105
  matches.push(FileSystemUtils.canonicalizeExistingPath(candidate));
89
106
  }
90
107
  }
@@ -101,24 +118,16 @@ export function resolveArtifactOutputs(changeDir, generates) {
101
118
  return finalizeMatches(matches);
102
119
  }
103
120
  /**
104
- * Async twin of {@link resolveArtifactOutputs}, for `collectChangeDetail` — its
105
- * only caller, and the reason it is worth keeping a second implementation.
106
- * A detail pane is fetched while the overview may still be aggregating, so its
107
- * per-artifact resolution is the one that has something to interleave with.
121
+ * Async twin of {@link resolveArtifactOutputs}, for `collectChangeDetail`: a
122
+ * detail pane is fetched while the overview may still be aggregating, so it has
123
+ * something to interleave with.
108
124
  *
109
- * It does **not** serve the save-triggered overview, despite what the change
110
- * that introduced it was named after. `collectOverview` reaches this module only
111
- * through `artifactSummaryFor` → `formatChangeStatus` → `artifactOutputExists`
112
- * and through `listTasksForChange`, both synchronous — deliberately, and
113
- * measured: dashboard-data.ts records 2.1 ms as the longest stall across 30
114
- * changes with the sync resolver in place, because the awaits already sitting in
115
- * that loop break the work across iterations. What removed the cost there was
116
- * the single-wildcard `readdir` fast path above, not asynchrony.
125
+ * It does **not** serve the save-triggered overview, which reaches this module
126
+ * only through synchronous paths — see the measurement in dashboard-data.ts.
117
127
  *
118
128
  * The duplicated branch structure is held to the sync version by the parity
119
- * cases in test/core/artifact-graph/outputs.test.ts, which run both resolvers
120
- * over each branch — non-glob, fast path, fast-glob fallback, missing prefix —
121
- * and assert identical results. Any behaviour added to one belongs in that list.
129
+ * cases in test/core/artifact-graph/outputs.test.ts. Any behaviour added to one
130
+ * belongs in that list.
122
131
  */
123
132
  export async function resolveArtifactOutputsAsync(changeDir, generates) {
124
133
  if (!isGlobPattern(generates)) {
@@ -147,7 +156,8 @@ export async function resolveArtifactOutputsAsync(changeDir, generates) {
147
156
  return null;
148
157
  const candidate = path.join(baseDir, entry, ...single.suffix);
149
158
  try {
150
- return (await fs.promises.stat(candidate)).isFile()
159
+ return (await fs.promises.stat(candidate)).isFile() &&
160
+ (await hasExactSuffixSpellingAsync(path.join(baseDir, entry), single.suffix))
151
161
  ? FileSystemUtils.canonicalizeExistingPath(candidate)
152
162
  : null;
153
163
  }
@@ -161,9 +171,6 @@ export async function resolveArtifactOutputsAsync(changeDir, generates) {
161
171
  const matches = (await fg(normalizedPattern, globFallbackOptions(changeDir))).map((match) => FileSystemUtils.canonicalizeExistingPath(path.normalize(match)));
162
172
  return finalizeMatches(matches);
163
173
  }
164
- /**
165
- * Checks if an artifact has at least one resolved output file.
166
- */
167
174
  export function artifactOutputExists(changeDir, generates) {
168
175
  return resolveArtifactOutputs(changeDir, generates).length > 0;
169
176
  }
@@ -1 +1 @@
1
- {"version":3,"file":"outputs.js","sourceRoot":"","sources":["../../../src/core/artifact-graph/outputs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"outputs.js","sourceRoot":"","sources":["../../../src/core/artifact-graph/outputs.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,11 @@
1
1
  import * as fs from 'node:fs';
2
2
  import type { SchemaYaml } from './types.js';
3
- /**
4
- * Error thrown when loading a schema fails.
5
- */
6
3
  export declare class SchemaLoadError extends Error {
7
4
  readonly schemaPath: string;
8
5
  readonly cause?: Error | undefined;
9
6
  constructor(message: string, schemaPath: string, cause?: Error | undefined);
10
7
  }
11
- /**
12
- * Gets the package's built-in schemas directory path.
13
- * Uses import.meta.url to resolve relative to the current module.
14
- */
8
+ /** Walks up from dist/core/artifact-graph/ to the package root's schemas/. */
15
9
  export declare function getPackageSchemasDir(): string;
16
10
  /**
17
11
  * The user's schema override directory: inside the config directory, unless
@@ -19,70 +13,50 @@ export declare function getPackageSchemasDir(): string;
19
13
  * than adding to it, so exactly one user-level override directory is ever live.
20
14
  */
21
15
  export declare function getUserSchemasDir(): string;
22
- /**
23
- * Gets the project-local schemas directory path.
24
- * @param projectRoot - The project root directory
25
- * @returns The path to the project's schemas directory
26
- */
27
16
  export declare function getProjectSchemasDir(projectRoot: string): string;
28
17
  /**
29
- * Whether a directory entry is a schema-directory candidate. Returns true for
30
- * real directories and for symlinks whose target is a directory. `Dirent`'s
31
- * `isDirectory()` reports the raw entry type, so a symlink pointing at a
32
- * directory has `isDirectory() === false`; such entries are dereferenced via
33
- * `statSync` to admit symlinked schema dirs while still rejecting
34
- * symlinks-to-files and broken/dangling links.
18
+ * True for real directories and for symlinks pointing at one. `Dirent`'s
19
+ * `isDirectory()` reports the raw entry type, so a symlinked schema dir needs
20
+ * the `statSync` dereference below.
35
21
  */
36
22
  export declare function isSchemaDir(parentDir: string, entry: fs.Dirent): boolean;
37
- /**
38
- * Resolves a schema name to its directory path.
39
- *
40
- * Resolution order (when projectRoot is provided):
41
- * 1. Project-local: <projectRoot>/tospec/schemas/<name>/schema.yaml
42
- * 2. User override: ${XDG_DATA_HOME}/tospec/schemas/<name>/schema.yaml
43
- * 3. Package built-in: <package>/schemas/<name>/schema.yaml
44
- *
45
- * When projectRoot is not provided, only user override and package built-in are checked
46
- * (backward compatible behavior).
47
- *
48
- * @param name - Schema name (e.g., "sdd")
49
- * @param projectRoot - Optional project root directory for project-local schema resolution
50
- * @returns The path to the schema directory, or null if not found
51
- */
52
23
  /**
53
24
  * Identity of a file's current contents, for cache invalidation. Null when the
54
25
  * file cannot be stat'd, which disables caching for that path rather than
55
26
  * risking a stale hit.
56
27
  */
57
28
  export declare function fileStamp(filePath: string): string | null;
58
- export declare function getSchemaDir(name: string, projectRoot?: string): string | null;
59
29
  /**
60
- * Resolves a schema name to a SchemaYaml object.
61
- *
62
- * Resolution order (when projectRoot is provided):
63
- * 1. Project-local: <projectRoot>/tospec/schemas/<name>/schema.yaml
64
- * 2. User override: ${XDG_DATA_HOME}/tospec/schemas/<name>/schema.yaml
65
- * 3. Package built-in: <package>/schemas/<name>/schema.yaml
30
+ * The directory holding `<name>/schema.yaml` and the layer it came from, or
31
+ * null when no layer has one. Project-local wins over the user override, which
32
+ * wins over the package built-in; without `projectRoot` only the last two are
33
+ * searched.
66
34
  *
67
- * When projectRoot is not provided, only user override and package built-in are checked
68
- * (backward compatible behavior).
69
- *
70
- * @param name - Schema name (e.g., "sdd")
71
- * @param projectRoot - Optional project root directory for project-local schema resolution
72
- * @returns The resolved schema object
73
- * @throws Error if schema is not found in any location
35
+ * The layer is resolved here rather than re-derived from the path by each
36
+ * caller: a directory is the *whole* schema, templates included, so "which layer
37
+ * won" is what explains a file the winning layer does not have.
74
38
  */
75
- export declare function resolveSchema(name: string, projectRoot?: string): SchemaYaml;
39
+ export declare function getSchemaDirWithSource(name: string, projectRoot?: string): {
40
+ dir: string;
41
+ source: SchemaInfo['source'];
42
+ } | null;
43
+ /** As `getSchemaDirWithSource`, for callers that only need the path. */
44
+ export declare function getSchemaDir(name: string, projectRoot?: string): string | null;
76
45
  /**
77
- * Lists all available schema names.
78
- * Combines project-local, user override, and package built-in schemas.
46
+ * The parsed schema, resolved through `getSchemaDir`'s precedence order.
79
47
  *
80
- * @param projectRoot - Optional project root directory for project-local schema resolution
48
+ * @throws SchemaLoadError when the file cannot be read or does not parse.
81
49
  */
50
+ export declare function resolveSchema(name: string, projectRoot?: string): SchemaYaml;
51
+ /** Every schema name on disk across all three layers. */
82
52
  export declare function listSchemas(projectRoot?: string): string[];
83
53
  /**
84
- * Schema info with metadata (name, description, artifacts).
54
+ * Names of the schemas that actually load — what an "Available schemas:" list
55
+ * has to mean. `listSchemas` reports directory names, so a schema.yaml with a
56
+ * typo in it would be offered as a choice. Anything phrased as advice to the
57
+ * user belongs here.
85
58
  */
59
+ export declare function listLoadableSchemas(projectRoot?: string): string[];
86
60
  export interface SchemaInfo {
87
61
  name: string;
88
62
  description: string;
@@ -90,28 +64,35 @@ export interface SchemaInfo {
90
64
  source: 'project' | 'user' | 'package';
91
65
  /**
92
66
  * Absolute path to the directory holding schema.yaml (and its templates/).
93
- * `source` says which of the three layers won; this says which file to open —
94
- * the question a schema author actually has when a resolution surprises them.
67
+ * `source` says which of the three layers won; this says which file to open.
95
68
  */
96
69
  path: string;
70
+ /**
71
+ * Why this schema could not be loaded, on a broken entry only (which also has
72
+ * an empty `artifacts` list). Carrying the failure in place rather than
73
+ * dropping the entry is the same rule `list` follows for an unreadable change:
74
+ * tospec/decisions/20260917_234821-batch-commands-degrade-per-item.md
75
+ */
76
+ error?: string;
97
77
  }
98
78
  /**
99
- * Lists all available schemas with their descriptions and artifact lists.
100
- * Useful for agent skills to present schema selection to users.
79
+ * Every schema with its description and artifact list, for agent skills that
80
+ * present a schema choice.
101
81
  *
102
- * @param projectRoot - Optional project root directory for project-local schema resolution
82
+ * A schema that fails to parse is reported with `error` set, but only when no
83
+ * lower-precedence layer supplies a working schema of that name — a broken
84
+ * project override must not hide the package schema it shadows.
103
85
  */
104
86
  export declare function listSchemasWithInfo(projectRoot?: string): SchemaInfo[];
105
87
  /**
106
88
  * Union of every artifact ID across every available schema.
107
89
  *
108
- * Config keys that are scoped per-artifact but stored globally (`rules:`) can
109
- * legitimately name an artifact from any schema, so they must be checked
110
- * against the union rather than whichever schema the current change uses.
90
+ * Config keys scoped per-artifact but stored globally (`rules:`) can
91
+ * legitimately name an artifact from any schema, so they are checked against the
92
+ * union rather than the current change's schema.
111
93
  *
112
- * Built on listSchemasWithInfo so it inherits that function's precedence
113
- * handling and its tolerance for a schema that fails to parse — one broken
114
- * custom schema must not turn every rules key into an unknown ID.
94
+ * Built on listSchemasWithInfo to inherit its tolerance for a schema that fails
95
+ * to parse: one broken custom schema must not turn every rules key unknown.
115
96
  */
116
97
  export declare function collectAllArtifactIds(projectRoot?: string): Set<string>;
117
98
  //# sourceMappingURL=resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/core/artifact-graph/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAK9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,UAAU,EAAE,MAAM;aAClB,KAAK,CAAC,EAAE,KAAK;IAH/B,YACE,OAAO,EAAE,MAAM,EACC,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,KAAK,YAAA,EAI9B;CACF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAI7C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AAWD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,GAAG,OAAO,CAYxE;AAED;;;;;;;;;;;;;;GAcG;AACH;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOzD;AAoCD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,GAAG,IAAI,CASf;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,CA0D5E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ1D;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;IACvC;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE,CA2BtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAEvE"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/core/artifact-graph/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAK9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,UAAU,EAAE,MAAM;aAClB,KAAK,CAAC,EAAE,KAAK;IAH/B,YACE,OAAO,EAAE,MAAM,EACC,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,KAAK,YAAA,EAI9B;CACF;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,IAAI,MAAM,CAG7C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AAUD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,GAAG,OAAO,CAYxE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOzD;AA+CD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,GACnB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;CAAE,GAAG,IAAI,CAStD;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,GAAG,IAAI,CAEf;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,CA+C5E;AAED,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ1D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAIlE;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;IACvC;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE,CAuCtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAEvE"}
@@ -3,9 +3,6 @@ import * as path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { getGlobalConfigDir, getLegacyDataDir } from '../global-config.js';
5
5
  import { parseSchema, SchemaValidationError } from './schema.js';
6
- /**
7
- * Error thrown when loading a schema fails.
8
- */
9
6
  export class SchemaLoadError extends Error {
10
7
  schemaPath;
11
8
  cause;
@@ -16,13 +13,9 @@ export class SchemaLoadError extends Error {
16
13
  this.name = 'SchemaLoadError';
17
14
  }
18
15
  }
19
- /**
20
- * Gets the package's built-in schemas directory path.
21
- * Uses import.meta.url to resolve relative to the current module.
22
- */
16
+ /** Walks up from dist/core/artifact-graph/ to the package root's schemas/. */
23
17
  export function getPackageSchemasDir() {
24
18
  const currentFile = fileURLToPath(import.meta.url);
25
- // Navigate from dist/core/artifact-graph/ to package root's schemas/
26
19
  return path.join(path.dirname(currentFile), '..', '..', '..', 'schemas');
27
20
  }
28
21
  /**
@@ -38,27 +31,18 @@ export function getUserSchemasDir() {
38
31
  }
39
32
  /**
40
33
  * Where overrides sit until an install migrates. Ranked below the current user
41
- * directory, so a migrated copy wins and a not-yet-migrated one still applies —
42
- * per schema name, because every lookup walks this list name by name.
34
+ * directory, so a migrated copy wins and a not-yet-migrated one still applies.
43
35
  */
44
36
  function getLegacySchemasDir() {
45
37
  return path.join(getLegacyDataDir(), 'schemas');
46
38
  }
47
- /**
48
- * Gets the project-local schemas directory path.
49
- * @param projectRoot - The project root directory
50
- * @returns The path to the project's schemas directory
51
- */
52
39
  export function getProjectSchemasDir(projectRoot) {
53
40
  return path.join(projectRoot, 'tospec', 'schemas');
54
41
  }
55
42
  /**
56
- * Whether a directory entry is a schema-directory candidate. Returns true for
57
- * real directories and for symlinks whose target is a directory. `Dirent`'s
58
- * `isDirectory()` reports the raw entry type, so a symlink pointing at a
59
- * directory has `isDirectory() === false`; such entries are dereferenced via
60
- * `statSync` to admit symlinked schema dirs while still rejecting
61
- * symlinks-to-files and broken/dangling links.
43
+ * True for real directories and for symlinks pointing at one. `Dirent`'s
44
+ * `isDirectory()` reports the raw entry type, so a symlinked schema dir needs
45
+ * the `statSync` dereference below.
62
46
  */
63
47
  export function isSchemaDir(parentDir, entry) {
64
48
  if (entry.isDirectory()) {
@@ -74,21 +58,6 @@ export function isSchemaDir(parentDir, entry) {
74
58
  }
75
59
  return false;
76
60
  }
77
- /**
78
- * Resolves a schema name to its directory path.
79
- *
80
- * Resolution order (when projectRoot is provided):
81
- * 1. Project-local: <projectRoot>/tospec/schemas/<name>/schema.yaml
82
- * 2. User override: ${XDG_DATA_HOME}/tospec/schemas/<name>/schema.yaml
83
- * 3. Package built-in: <package>/schemas/<name>/schema.yaml
84
- *
85
- * When projectRoot is not provided, only user override and package built-in are checked
86
- * (backward compatible behavior).
87
- *
88
- * @param name - Schema name (e.g., "sdd")
89
- * @param projectRoot - Optional project root directory for project-local schema resolution
90
- * @returns The path to the schema directory, or null if not found
91
- */
92
61
  /**
93
62
  * Identity of a file's current contents, for cache invalidation. Null when the
94
63
  * file cannot be stat'd, which disables caching for that path rather than
@@ -105,6 +74,16 @@ export function fileStamp(filePath) {
105
74
  }
106
75
  /** Parsed schemas keyed by path, invalidated when the file's stamp changes. */
107
76
  const schemaCache = new Map();
77
+ /**
78
+ * One wording for a schema that will not parse, so `tospec schemas` and
79
+ * `tospec templates --schema <name>` say the same thing about the same file.
80
+ */
81
+ function schemaParseFailureMessage(schemaPath, error) {
82
+ const detail = error instanceof Error ? error.message : String(error);
83
+ return error instanceof SchemaValidationError
84
+ ? `Invalid schema at '${schemaPath}': ${detail}`
85
+ : `Failed to parse schema at '${schemaPath}': ${detail}`;
86
+ }
108
87
  /**
109
88
  * The schema search path, **lowest precedence first** (package → user →
110
89
  * project). Every lookup and listing walks this one list, so adding a source
@@ -113,9 +92,9 @@ const schemaCache = new Map();
113
92
  function schemaSources(projectRoot) {
114
93
  return [
115
94
  { dir: getPackageSchemasDir(), source: 'package' },
116
- // Both report 'user' — the legacy entry is one address for the same layer,
117
- // and a 'legacy' value would widen the agent-facing JSON contract with
118
- // something that stops existing once migration runs.
95
+ // Both report 'user': the legacy entry is another address for the same
96
+ // layer, and a 'legacy' value would widen the agent-facing JSON contract
97
+ // with something that stops existing once migration runs.
119
98
  { dir: getLegacySchemasDir(), source: 'user' },
120
99
  { dir: getUserSchemasDir(), source: 'user' },
121
100
  ...(projectRoot
@@ -132,51 +111,54 @@ function scanSchemaDir(dir) {
132
111
  .filter((entry) => isSchemaDir(dir, entry) && fs.existsSync(path.join(dir, entry.name, 'schema.yaml')))
133
112
  .map((entry) => entry.name);
134
113
  }
135
- export function getSchemaDir(name, projectRoot) {
114
+ /**
115
+ * The directory holding `<name>/schema.yaml` and the layer it came from, or
116
+ * null when no layer has one. Project-local wins over the user override, which
117
+ * wins over the package built-in; without `projectRoot` only the last two are
118
+ * searched.
119
+ *
120
+ * The layer is resolved here rather than re-derived from the path by each
121
+ * caller: a directory is the *whole* schema, templates included, so "which layer
122
+ * won" is what explains a file the winning layer does not have.
123
+ */
124
+ export function getSchemaDirWithSource(name, projectRoot) {
136
125
  // Highest precedence first: project overrides user overrides package.
137
- for (const { dir } of schemaSources(projectRoot).reverse()) {
126
+ for (const { dir, source } of schemaSources(projectRoot).reverse()) {
138
127
  const candidate = path.join(dir, name);
139
128
  if (fs.existsSync(path.join(candidate, 'schema.yaml'))) {
140
- return candidate;
129
+ return { dir: candidate, source };
141
130
  }
142
131
  }
143
132
  return null;
144
133
  }
134
+ /** As `getSchemaDirWithSource`, for callers that only need the path. */
135
+ export function getSchemaDir(name, projectRoot) {
136
+ return getSchemaDirWithSource(name, projectRoot)?.dir ?? null;
137
+ }
145
138
  /**
146
- * Resolves a schema name to a SchemaYaml object.
147
- *
148
- * Resolution order (when projectRoot is provided):
149
- * 1. Project-local: <projectRoot>/tospec/schemas/<name>/schema.yaml
150
- * 2. User override: ${XDG_DATA_HOME}/tospec/schemas/<name>/schema.yaml
151
- * 3. Package built-in: <package>/schemas/<name>/schema.yaml
139
+ * The parsed schema, resolved through `getSchemaDir`'s precedence order.
152
140
  *
153
- * When projectRoot is not provided, only user override and package built-in are checked
154
- * (backward compatible behavior).
155
- *
156
- * @param name - Schema name (e.g., "sdd")
157
- * @param projectRoot - Optional project root directory for project-local schema resolution
158
- * @returns The resolved schema object
159
- * @throws Error if schema is not found in any location
141
+ * @throws SchemaLoadError when the file cannot be read or does not parse.
160
142
  */
161
143
  export function resolveSchema(name, projectRoot) {
162
- // Normalize name (remove .yaml extension if provided)
163
144
  const normalizedName = name.replace(/\.ya?ml$/, '');
164
145
  const schemaDir = getSchemaDir(normalizedName, projectRoot);
165
146
  if (!schemaDir) {
166
- const availableSchemas = listSchemas(projectRoot);
147
+ // Loadable, not merely present: a name whose schema.yaml does not parse is
148
+ // a choice that cannot work.
149
+ const availableSchemas = listLoadableSchemas(projectRoot);
167
150
  throw new Error(`Schema '${normalizedName}' not found. Available schemas: ${availableSchemas.join(', ')}`);
168
151
  }
169
152
  const schemaPath = path.join(schemaDir, 'schema.yaml');
170
- // A single status/list run resolves the same schema once per change, and the
171
- // dashboard re-resolves on every refresh; the yaml + Zod + cycle-check parse
172
- // dominates that cost. One statSync decides whether it can be skipped.
153
+ // A status/list run resolves the same schema once per change and the dashboard
154
+ // re-resolves on every refresh, where the yaml + Zod + cycle-check parse
155
+ // dominates. One statSync decides whether it can be skipped.
173
156
  const stamp = fileStamp(schemaPath);
174
157
  const cached = stamp === null ? undefined : schemaCache.get(schemaPath);
175
158
  if (cached && cached.stamp === stamp) {
176
159
  // Cloned so a caller mutating its schema cannot corrupt later resolutions.
177
160
  return structuredClone(cached.schema);
178
161
  }
179
- // Load and parse the schema
180
162
  let content;
181
163
  try {
182
164
  content = fs.readFileSync(schemaPath, 'utf-8');
@@ -193,19 +175,11 @@ export function resolveSchema(name, projectRoot) {
193
175
  return schema;
194
176
  }
195
177
  catch (err) {
196
- if (err instanceof SchemaValidationError) {
197
- throw new SchemaLoadError(`Invalid schema at '${schemaPath}': ${err.message}`, schemaPath, err);
198
- }
199
- const parseError = err instanceof Error ? err : new Error(String(err));
200
- throw new SchemaLoadError(`Failed to parse schema at '${schemaPath}': ${parseError.message}`, schemaPath, parseError);
178
+ const cause = err instanceof Error ? err : new Error(String(err));
179
+ throw new SchemaLoadError(schemaParseFailureMessage(schemaPath, err), schemaPath, cause);
201
180
  }
202
181
  }
203
- /**
204
- * Lists all available schema names.
205
- * Combines project-local, user override, and package built-in schemas.
206
- *
207
- * @param projectRoot - Optional project root directory for project-local schema resolution
208
- */
182
+ /** Every schema name on disk across all three layers. */
209
183
  export function listSchemas(projectRoot) {
210
184
  const schemas = new Set();
211
185
  for (const { dir } of schemaSources(projectRoot)) {
@@ -216,17 +190,33 @@ export function listSchemas(projectRoot) {
216
190
  return Array.from(schemas).sort();
217
191
  }
218
192
  /**
219
- * Lists all available schemas with their descriptions and artifact lists.
220
- * Useful for agent skills to present schema selection to users.
193
+ * Names of the schemas that actually load — what an "Available schemas:" list
194
+ * has to mean. `listSchemas` reports directory names, so a schema.yaml with a
195
+ * typo in it would be offered as a choice. Anything phrased as advice to the
196
+ * user belongs here.
197
+ */
198
+ export function listLoadableSchemas(projectRoot) {
199
+ return listSchemasWithInfo(projectRoot)
200
+ .filter((schema) => schema.error === undefined)
201
+ .map((schema) => schema.name);
202
+ }
203
+ /**
204
+ * Every schema with its description and artifact list, for agent skills that
205
+ * present a schema choice.
221
206
  *
222
- * @param projectRoot - Optional project root directory for project-local schema resolution
207
+ * A schema that fails to parse is reported with `error` set, but only when no
208
+ * lower-precedence layer supplies a working schema of that name — a broken
209
+ * project override must not hide the package schema it shadows.
223
210
  */
224
211
  export function listSchemasWithInfo(projectRoot) {
225
212
  const schemas = [];
226
213
  const seenNames = new Set();
214
+ // Keyed by name so the highest-precedence failure is the one reported, and a
215
+ // later success removes it.
216
+ const broken = new Map();
227
217
  // Highest precedence first, so an overridden schema is skipped rather than
228
- // replaced. A schema that fails to parse is not marked seen — the next
229
- // source down still gets a chance to supply it.
218
+ // replaced. A schema that fails to parse is not marked seen — the next source
219
+ // down still gets a chance to supply it.
230
220
  for (const { dir, source } of schemaSources(projectRoot).reverse()) {
231
221
  for (const name of scanSchemaDir(dir)) {
232
222
  if (seenNames.has(name))
@@ -241,24 +231,33 @@ export function listSchemasWithInfo(projectRoot) {
241
231
  path: path.join(dir, name),
242
232
  });
243
233
  seenNames.add(name);
234
+ broken.delete(name);
244
235
  }
245
- catch {
246
- // Skip invalid schemas
236
+ catch (error) {
237
+ if (broken.has(name))
238
+ continue;
239
+ broken.set(name, {
240
+ name,
241
+ description: '',
242
+ artifacts: [],
243
+ source,
244
+ path: path.join(dir, name),
245
+ error: schemaParseFailureMessage(path.join(dir, name, 'schema.yaml'), error),
246
+ });
247
247
  }
248
248
  }
249
249
  }
250
- return schemas.sort((a, b) => a.name.localeCompare(b.name));
250
+ return [...schemas, ...broken.values()].sort((a, b) => a.name.localeCompare(b.name));
251
251
  }
252
252
  /**
253
253
  * Union of every artifact ID across every available schema.
254
254
  *
255
- * Config keys that are scoped per-artifact but stored globally (`rules:`) can
256
- * legitimately name an artifact from any schema, so they must be checked
257
- * against the union rather than whichever schema the current change uses.
255
+ * Config keys scoped per-artifact but stored globally (`rules:`) can
256
+ * legitimately name an artifact from any schema, so they are checked against the
257
+ * union rather than the current change's schema.
258
258
  *
259
- * Built on listSchemasWithInfo so it inherits that function's precedence
260
- * handling and its tolerance for a schema that fails to parse — one broken
261
- * custom schema must not turn every rules key into an unknown ID.
259
+ * Built on listSchemasWithInfo to inherit its tolerance for a schema that fails
260
+ * to parse: one broken custom schema must not turn every rules key unknown.
262
261
  */
263
262
  export function collectAllArtifactIds(projectRoot) {
264
263
  return new Set(listSchemasWithInfo(projectRoot).flatMap((schema) => schema.artifacts));