@seanmars/tospec 0.19.0-beta.8 → 0.21.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 (423) hide show
  1. package/CHANGELOG.md +89 -296
  2. package/README.md +69 -82
  3. package/assets/dashboard/app.js +11 -0
  4. package/assets/dashboard/style.css +7 -0
  5. package/bin/tospec.js +1 -1
  6. package/dist/cli/index.d.ts +6 -1
  7. package/dist/cli/index.d.ts.map +1 -1
  8. package/dist/cli/index.js +120 -112
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/commands/config.d.ts +9 -17
  11. package/dist/commands/config.d.ts.map +1 -1
  12. package/dist/commands/config.js +300 -145
  13. package/dist/commands/config.js.map +1 -1
  14. package/dist/commands/dashboard.d.ts +57 -99
  15. package/dist/commands/dashboard.d.ts.map +1 -1
  16. package/dist/commands/dashboard.js +252 -330
  17. package/dist/commands/dashboard.js.map +1 -1
  18. package/dist/commands/decision.d.ts +38 -27
  19. package/dist/commands/decision.d.ts.map +1 -1
  20. package/dist/commands/decision.js +301 -131
  21. package/dist/commands/decision.js.map +1 -1
  22. package/dist/commands/metrics.d.ts +28 -51
  23. package/dist/commands/metrics.d.ts.map +1 -1
  24. package/dist/commands/metrics.js +62 -93
  25. package/dist/commands/metrics.js.map +1 -1
  26. package/dist/commands/shared-output.d.ts +12 -27
  27. package/dist/commands/shared-output.d.ts.map +1 -1
  28. package/dist/commands/shared-output.js +22 -45
  29. package/dist/commands/shared-output.js.map +1 -1
  30. package/dist/commands/show.d.ts +4 -7
  31. package/dist/commands/show.d.ts.map +1 -1
  32. package/dist/commands/show.js +23 -11
  33. package/dist/commands/show.js.map +1 -1
  34. package/dist/commands/validate.d.ts +34 -58
  35. package/dist/commands/validate.d.ts.map +1 -1
  36. package/dist/commands/validate.js +228 -142
  37. package/dist/commands/validate.js.map +1 -1
  38. package/dist/commands/workflow/index.d.ts +1 -5
  39. package/dist/commands/workflow/index.d.ts.map +1 -1
  40. package/dist/commands/workflow/index.js +1 -5
  41. package/dist/commands/workflow/index.js.map +1 -1
  42. package/dist/commands/workflow/instructions.d.ts +14 -24
  43. package/dist/commands/workflow/instructions.d.ts.map +1 -1
  44. package/dist/commands/workflow/instructions.js +230 -132
  45. package/dist/commands/workflow/instructions.js.map +1 -1
  46. package/dist/commands/workflow/new-change.d.ts +2 -5
  47. package/dist/commands/workflow/new-change.d.ts.map +1 -1
  48. package/dist/commands/workflow/new-change.js +75 -34
  49. package/dist/commands/workflow/new-change.js.map +1 -1
  50. package/dist/commands/workflow/schemas.d.ts +1 -5
  51. package/dist/commands/workflow/schemas.d.ts.map +1 -1
  52. package/dist/commands/workflow/schemas.js +6 -17
  53. package/dist/commands/workflow/schemas.js.map +1 -1
  54. package/dist/commands/workflow/shared.d.ts +37 -42
  55. package/dist/commands/workflow/shared.d.ts.map +1 -1
  56. package/dist/commands/workflow/shared.js +23 -54
  57. package/dist/commands/workflow/shared.js.map +1 -1
  58. package/dist/commands/workflow/status.d.ts +7 -17
  59. package/dist/commands/workflow/status.d.ts.map +1 -1
  60. package/dist/commands/workflow/status.js +57 -72
  61. package/dist/commands/workflow/status.js.map +1 -1
  62. package/dist/commands/workflow/templates.d.ts +8 -8
  63. package/dist/commands/workflow/templates.d.ts.map +1 -1
  64. package/dist/commands/workflow/templates.js +32 -46
  65. package/dist/commands/workflow/templates.js.map +1 -1
  66. package/dist/core/archive.d.ts +33 -31
  67. package/dist/core/archive.d.ts.map +1 -1
  68. package/dist/core/archive.js +322 -284
  69. package/dist/core/archive.js.map +1 -1
  70. package/dist/core/artifact-graph/graph.d.ts +25 -42
  71. package/dist/core/artifact-graph/graph.d.ts.map +1 -1
  72. package/dist/core/artifact-graph/graph.js +45 -63
  73. package/dist/core/artifact-graph/graph.js.map +1 -1
  74. package/dist/core/artifact-graph/index.d.ts +1 -1
  75. package/dist/core/artifact-graph/index.d.ts.map +1 -1
  76. package/dist/core/artifact-graph/index.js +1 -1
  77. package/dist/core/artifact-graph/index.js.map +1 -1
  78. package/dist/core/artifact-graph/instruction-loader.d.ts +54 -120
  79. package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -1
  80. package/dist/core/artifact-graph/instruction-loader.js +129 -111
  81. package/dist/core/artifact-graph/instruction-loader.js.map +1 -1
  82. package/dist/core/artifact-graph/outputs.d.ts +9 -23
  83. package/dist/core/artifact-graph/outputs.d.ts.map +1 -1
  84. package/dist/core/artifact-graph/outputs.js +45 -38
  85. package/dist/core/artifact-graph/outputs.js.map +1 -1
  86. package/dist/core/artifact-graph/resolver.d.ts +36 -81
  87. package/dist/core/artifact-graph/resolver.d.ts.map +1 -1
  88. package/dist/core/artifact-graph/resolver.js +60 -101
  89. package/dist/core/artifact-graph/resolver.js.map +1 -1
  90. package/dist/core/artifact-graph/schema.d.ts +0 -6
  91. package/dist/core/artifact-graph/schema.d.ts.map +1 -1
  92. package/dist/core/artifact-graph/schema.js +7 -32
  93. package/dist/core/artifact-graph/schema.js.map +1 -1
  94. package/dist/core/artifact-graph/state.d.ts +1 -8
  95. package/dist/core/artifact-graph/state.d.ts.map +1 -1
  96. package/dist/core/artifact-graph/state.js +2 -17
  97. package/dist/core/artifact-graph/state.js.map +1 -1
  98. package/dist/core/artifact-graph/stub-detection.d.ts +6 -14
  99. package/dist/core/artifact-graph/stub-detection.d.ts.map +1 -1
  100. package/dist/core/artifact-graph/stub-detection.js +13 -16
  101. package/dist/core/artifact-graph/stub-detection.js.map +1 -1
  102. package/dist/core/artifact-graph/types.d.ts +4 -0
  103. package/dist/core/artifact-graph/types.d.ts.map +1 -1
  104. package/dist/core/artifact-graph/types.js +46 -11
  105. package/dist/core/artifact-graph/types.js.map +1 -1
  106. package/dist/core/available-tools.d.ts +3 -12
  107. package/dist/core/available-tools.d.ts.map +1 -1
  108. package/dist/core/available-tools.js +4 -13
  109. package/dist/core/available-tools.js.map +1 -1
  110. package/dist/core/change-metadata/schema.d.ts +1 -1
  111. package/dist/core/change-metadata/schema.d.ts.map +1 -1
  112. package/dist/core/change-metadata/schema.js +10 -7
  113. package/dist/core/change-metadata/schema.js.map +1 -1
  114. package/dist/core/change-presenter.d.ts +16 -27
  115. package/dist/core/change-presenter.d.ts.map +1 -1
  116. package/dist/core/change-presenter.js +53 -53
  117. package/dist/core/change-presenter.js.map +1 -1
  118. package/dist/core/change-status-policy.d.ts +4 -8
  119. package/dist/core/change-status-policy.d.ts.map +1 -1
  120. package/dist/core/change-status-policy.js +9 -17
  121. package/dist/core/change-status-policy.js.map +1 -1
  122. package/dist/core/codex-metrics.d.ts +25 -45
  123. package/dist/core/codex-metrics.d.ts.map +1 -1
  124. package/dist/core/codex-metrics.js +44 -88
  125. package/dist/core/codex-metrics.js.map +1 -1
  126. package/dist/core/codex-residue.d.ts +14 -15
  127. package/dist/core/codex-residue.d.ts.map +1 -1
  128. package/dist/core/codex-residue.js +18 -22
  129. package/dist/core/codex-residue.js.map +1 -1
  130. package/dist/core/command-generation/adapters/claude.d.ts +2 -9
  131. package/dist/core/command-generation/adapters/claude.d.ts.map +1 -1
  132. package/dist/core/command-generation/adapters/claude.js +2 -12
  133. package/dist/core/command-generation/adapters/claude.js.map +1 -1
  134. package/dist/core/command-generation/adapters/index.d.ts +1 -9
  135. package/dist/core/command-generation/adapters/index.d.ts.map +1 -1
  136. package/dist/core/command-generation/adapters/index.js +1 -9
  137. package/dist/core/command-generation/adapters/index.js.map +1 -1
  138. package/dist/core/command-generation/generator.d.ts +0 -17
  139. package/dist/core/command-generation/generator.d.ts.map +1 -1
  140. package/dist/core/command-generation/generator.js +0 -17
  141. package/dist/core/command-generation/generator.js.map +1 -1
  142. package/dist/core/command-generation/index.d.ts +2 -5
  143. package/dist/core/command-generation/index.d.ts.map +1 -1
  144. package/dist/core/command-generation/index.js +0 -9
  145. package/dist/core/command-generation/index.js.map +1 -1
  146. package/dist/core/command-generation/types.d.ts +10 -36
  147. package/dist/core/command-generation/types.d.ts.map +1 -1
  148. package/dist/core/command-generation/types.js +0 -6
  149. package/dist/core/command-generation/types.js.map +1 -1
  150. package/dist/core/command-generation/yaml.d.ts +3 -18
  151. package/dist/core/command-generation/yaml.d.ts.map +1 -1
  152. package/dist/core/command-generation/yaml.js +5 -23
  153. package/dist/core/command-generation/yaml.js.map +1 -1
  154. package/dist/core/config-prompts.d.ts +2 -4
  155. package/dist/core/config-prompts.d.ts.map +1 -1
  156. package/dist/core/config-prompts.js +2 -7
  157. package/dist/core/config-prompts.js.map +1 -1
  158. package/dist/core/config-schema.d.ts +7 -41
  159. package/dist/core/config-schema.d.ts.map +1 -1
  160. package/dist/core/config-schema.js +35 -74
  161. package/dist/core/config-schema.js.map +1 -1
  162. package/dist/core/config.d.ts +25 -49
  163. package/dist/core/config.d.ts.map +1 -1
  164. package/dist/core/config.js +22 -45
  165. package/dist/core/config.js.map +1 -1
  166. package/dist/core/dashboard-activity.d.ts +7 -9
  167. package/dist/core/dashboard-activity.d.ts.map +1 -1
  168. package/dist/core/dashboard-activity.js +34 -25
  169. package/dist/core/dashboard-activity.js.map +1 -1
  170. package/dist/core/dashboard-data.d.ts +35 -22
  171. package/dist/core/dashboard-data.d.ts.map +1 -1
  172. package/dist/core/dashboard-data.js +57 -72
  173. package/dist/core/dashboard-data.js.map +1 -1
  174. package/dist/core/global-config.d.ts +24 -53
  175. package/dist/core/global-config.d.ts.map +1 -1
  176. package/dist/core/global-config.js +38 -67
  177. package/dist/core/global-config.js.map +1 -1
  178. package/dist/core/init.d.ts +12 -28
  179. package/dist/core/init.d.ts.map +1 -1
  180. package/dist/core/init.js +93 -169
  181. package/dist/core/init.js.map +1 -1
  182. package/dist/core/list.d.ts.map +1 -1
  183. package/dist/core/list.js +95 -41
  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 +15 -23
  190. package/dist/core/markdown-render.d.ts.map +1 -1
  191. package/dist/core/markdown-render.js +25 -34
  192. package/dist/core/markdown-render.js.map +1 -1
  193. package/dist/core/migrate.d.ts +19 -16
  194. package/dist/core/migrate.d.ts.map +1 -1
  195. package/dist/core/migrate.js +162 -136
  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 +23 -30
  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 +8 -14
  229. package/dist/core/root-selection.d.ts.map +1 -1
  230. package/dist/core/root-selection.js +3 -6
  231. package/dist/core/root-selection.js.map +1 -1
  232. package/dist/core/rules.d.ts.map +1 -1
  233. package/dist/core/rules.js +2 -3
  234. package/dist/core/rules.js.map +1 -1
  235. package/dist/core/schema-names.d.ts +16 -0
  236. package/dist/core/schema-names.d.ts.map +1 -0
  237. package/dist/core/schema-names.js +16 -0
  238. package/dist/core/schema-names.js.map +1 -0
  239. package/dist/core/schemas/base.schema.d.ts.map +1 -1
  240. package/dist/core/schemas/base.schema.js +6 -12
  241. package/dist/core/schemas/base.schema.js.map +1 -1
  242. package/dist/core/schemas/change.schema.d.ts +8 -0
  243. package/dist/core/schemas/change.schema.d.ts.map +1 -1
  244. package/dist/core/schemas/change.schema.js +41 -10
  245. package/dist/core/schemas/change.schema.js.map +1 -1
  246. package/dist/core/shared/index.d.ts +2 -7
  247. package/dist/core/shared/index.d.ts.map +1 -1
  248. package/dist/core/shared/index.js +2 -7
  249. package/dist/core/shared/index.js.map +1 -1
  250. package/dist/core/shared/rules-generation.d.ts +5 -15
  251. package/dist/core/shared/rules-generation.d.ts.map +1 -1
  252. package/dist/core/shared/rules-generation.js +33 -37
  253. package/dist/core/shared/rules-generation.js.map +1 -1
  254. package/dist/core/shared/skill-generation.d.ts +28 -43
  255. package/dist/core/shared/skill-generation.d.ts.map +1 -1
  256. package/dist/core/shared/skill-generation.js +82 -51
  257. package/dist/core/shared/skill-generation.js.map +1 -1
  258. package/dist/core/shared/tool-detection.d.ts +35 -76
  259. package/dist/core/shared/tool-detection.d.ts.map +1 -1
  260. package/dist/core/shared/tool-detection.js +73 -93
  261. package/dist/core/shared/tool-detection.js.map +1 -1
  262. package/dist/core/skill-metrics.d.ts +36 -63
  263. package/dist/core/skill-metrics.d.ts.map +1 -1
  264. package/dist/core/skill-metrics.js +34 -73
  265. package/dist/core/skill-metrics.js.map +1 -1
  266. package/dist/core/spec-presenter.d.ts.map +1 -1
  267. package/dist/core/spec-presenter.js +5 -10
  268. package/dist/core/spec-presenter.js.map +1 -1
  269. package/dist/core/specs-apply.d.ts +16 -31
  270. package/dist/core/specs-apply.d.ts.map +1 -1
  271. package/dist/core/specs-apply.js +146 -195
  272. package/dist/core/specs-apply.js.map +1 -1
  273. package/dist/core/templates/fragments/interview.d.ts +2 -6
  274. package/dist/core/templates/fragments/interview.d.ts.map +1 -1
  275. package/dist/core/templates/fragments/interview.js +2 -6
  276. package/dist/core/templates/fragments/interview.js.map +1 -1
  277. package/dist/core/templates/fragments/next-step.d.ts +4 -8
  278. package/dist/core/templates/fragments/next-step.d.ts.map +1 -1
  279. package/dist/core/templates/fragments/next-step.js +4 -8
  280. package/dist/core/templates/fragments/next-step.js.map +1 -1
  281. package/dist/core/templates/fragments/validate.d.ts +13 -0
  282. package/dist/core/templates/fragments/validate.d.ts.map +1 -0
  283. package/dist/core/templates/fragments/validate.js +13 -0
  284. package/dist/core/templates/fragments/validate.js.map +1 -0
  285. package/dist/core/templates/fragments/verify.d.ts +9 -12
  286. package/dist/core/templates/fragments/verify.d.ts.map +1 -1
  287. package/dist/core/templates/fragments/verify.js +9 -12
  288. package/dist/core/templates/fragments/verify.js.map +1 -1
  289. package/dist/core/templates/index.d.ts +0 -6
  290. package/dist/core/templates/index.d.ts.map +1 -1
  291. package/dist/core/templates/index.js +0 -7
  292. package/dist/core/templates/index.js.map +1 -1
  293. package/dist/core/templates/skill-templates.d.ts +1 -5
  294. package/dist/core/templates/skill-templates.d.ts.map +1 -1
  295. package/dist/core/templates/skill-templates.js +0 -5
  296. package/dist/core/templates/skill-templates.js.map +1 -1
  297. package/dist/core/templates/types.d.ts +3 -7
  298. package/dist/core/templates/types.d.ts.map +1 -1
  299. package/dist/core/templates/types.js +0 -3
  300. package/dist/core/templates/types.js.map +1 -1
  301. package/dist/core/templates/workflows/apply.d.ts +3 -9
  302. package/dist/core/templates/workflows/apply.d.ts.map +1 -1
  303. package/dist/core/templates/workflows/apply.js +11 -13
  304. package/dist/core/templates/workflows/apply.js.map +1 -1
  305. package/dist/core/templates/workflows/archive.d.ts +0 -6
  306. package/dist/core/templates/workflows/archive.d.ts.map +1 -1
  307. package/dist/core/templates/workflows/archive.js +16 -5
  308. package/dist/core/templates/workflows/archive.js.map +1 -1
  309. package/dist/core/templates/workflows/decision.js +3 -3
  310. package/dist/core/templates/workflows/decision.js.map +1 -1
  311. package/dist/core/templates/workflows/explore.js +1 -1
  312. package/dist/core/templates/workflows/grill.d.ts.map +1 -1
  313. package/dist/core/templates/workflows/grill.js +0 -2
  314. package/dist/core/templates/workflows/grill.js.map +1 -1
  315. package/dist/core/templates/workflows/issue.d.ts +0 -6
  316. package/dist/core/templates/workflows/issue.d.ts.map +1 -1
  317. package/dist/core/templates/workflows/issue.js +3 -2
  318. package/dist/core/templates/workflows/issue.js.map +1 -1
  319. package/dist/core/templates/workflows/propose.d.ts +0 -6
  320. package/dist/core/templates/workflows/propose.d.ts.map +1 -1
  321. package/dist/core/templates/workflows/propose.js +2 -2
  322. package/dist/core/templates/workflows/propose.js.map +1 -1
  323. package/dist/core/templates/workflows/sync.d.ts +2 -8
  324. package/dist/core/templates/workflows/sync.d.ts.map +1 -1
  325. package/dist/core/templates/workflows/sync.js +4 -3
  326. package/dist/core/templates/workflows/sync.js.map +1 -1
  327. package/dist/core/templates/workflows/update.d.ts +0 -6
  328. package/dist/core/templates/workflows/update.d.ts.map +1 -1
  329. package/dist/core/templates/workflows/update.js +9 -2
  330. package/dist/core/templates/workflows/update.js.map +1 -1
  331. package/dist/core/update.d.ts +10 -36
  332. package/dist/core/update.d.ts.map +1 -1
  333. package/dist/core/update.js +59 -126
  334. package/dist/core/update.js.map +1 -1
  335. package/dist/core/user-state-migration.d.ts +13 -15
  336. package/dist/core/user-state-migration.d.ts.map +1 -1
  337. package/dist/core/user-state-migration.js +30 -23
  338. package/dist/core/user-state-migration.js.map +1 -1
  339. package/dist/core/validation/constants.d.ts +4 -10
  340. package/dist/core/validation/constants.d.ts.map +1 -1
  341. package/dist/core/validation/constants.js +25 -25
  342. package/dist/core/validation/constants.js.map +1 -1
  343. package/dist/core/validation/prose-length.d.ts +15 -0
  344. package/dist/core/validation/prose-length.d.ts.map +1 -0
  345. package/dist/core/validation/prose-length.js +29 -0
  346. package/dist/core/validation/prose-length.js.map +1 -0
  347. package/dist/core/validation/purpose-placeholder.d.ts +9 -16
  348. package/dist/core/validation/purpose-placeholder.d.ts.map +1 -1
  349. package/dist/core/validation/purpose-placeholder.js +30 -44
  350. package/dist/core/validation/purpose-placeholder.js.map +1 -1
  351. package/dist/core/validation/section-validator.d.ts +4 -4
  352. package/dist/core/validation/section-validator.d.ts.map +1 -1
  353. package/dist/core/validation/section-validator.js +30 -12
  354. package/dist/core/validation/section-validator.js.map +1 -1
  355. package/dist/core/validation/task-numbering.d.ts +6 -3
  356. package/dist/core/validation/task-numbering.d.ts.map +1 -1
  357. package/dist/core/validation/task-numbering.js +23 -11
  358. package/dist/core/validation/task-numbering.js.map +1 -1
  359. package/dist/core/validation/types.d.ts +18 -0
  360. package/dist/core/validation/types.d.ts.map +1 -1
  361. package/dist/core/validation/types.js +12 -1
  362. package/dist/core/validation/types.js.map +1 -1
  363. package/dist/core/validation/validator.d.ts +42 -68
  364. package/dist/core/validation/validator.d.ts.map +1 -1
  365. package/dist/core/validation/validator.js +474 -285
  366. package/dist/core/validation/validator.js.map +1 -1
  367. package/dist/prompts/searchable-multi-select.d.ts +3 -8
  368. package/dist/prompts/searchable-multi-select.d.ts.map +1 -1
  369. package/dist/prompts/searchable-multi-select.js +16 -39
  370. package/dist/prompts/searchable-multi-select.js.map +1 -1
  371. package/dist/utils/change-metadata.d.ts +11 -50
  372. package/dist/utils/change-metadata.d.ts.map +1 -1
  373. package/dist/utils/change-metadata.js +48 -67
  374. package/dist/utils/change-metadata.js.map +1 -1
  375. package/dist/utils/change-utils.d.ts +24 -76
  376. package/dist/utils/change-utils.d.ts.map +1 -1
  377. package/dist/utils/change-utils.js +95 -142
  378. package/dist/utils/change-utils.js.map +1 -1
  379. package/dist/utils/file-lock.d.ts +39 -0
  380. package/dist/utils/file-lock.d.ts.map +1 -0
  381. package/dist/utils/file-lock.js +149 -0
  382. package/dist/utils/file-lock.js.map +1 -0
  383. package/dist/utils/file-system.d.ts +12 -32
  384. package/dist/utils/file-system.d.ts.map +1 -1
  385. package/dist/utils/file-system.js +41 -45
  386. package/dist/utils/file-system.js.map +1 -1
  387. package/dist/utils/frontmatter.d.ts +7 -11
  388. package/dist/utils/frontmatter.d.ts.map +1 -1
  389. package/dist/utils/frontmatter.js +11 -11
  390. package/dist/utils/frontmatter.js.map +1 -1
  391. package/dist/utils/interactive.d.ts +4 -9
  392. package/dist/utils/interactive.d.ts.map +1 -1
  393. package/dist/utils/interactive.js +2 -4
  394. package/dist/utils/interactive.js.map +1 -1
  395. package/dist/utils/item-discovery.d.ts +10 -20
  396. package/dist/utils/item-discovery.d.ts.map +1 -1
  397. package/dist/utils/item-discovery.js +31 -55
  398. package/dist/utils/item-discovery.js.map +1 -1
  399. package/dist/utils/link.d.ts +9 -18
  400. package/dist/utils/link.d.ts.map +1 -1
  401. package/dist/utils/link.js +9 -18
  402. package/dist/utils/link.js.map +1 -1
  403. package/dist/utils/requirement-diff.d.ts +13 -23
  404. package/dist/utils/requirement-diff.d.ts.map +1 -1
  405. package/dist/utils/requirement-diff.js +13 -23
  406. package/dist/utils/requirement-diff.js.map +1 -1
  407. package/dist/utils/spec-files.d.ts +7 -20
  408. package/dist/utils/spec-files.d.ts.map +1 -1
  409. package/dist/utils/spec-files.js +26 -51
  410. package/dist/utils/spec-files.js.map +1 -1
  411. package/dist/utils/task-progress.d.ts +11 -9
  412. package/dist/utils/task-progress.d.ts.map +1 -1
  413. package/dist/utils/task-progress.js +53 -32
  414. package/dist/utils/task-progress.js.map +1 -1
  415. package/dist/utils/timestamp.d.ts +5 -8
  416. package/dist/utils/timestamp.d.ts.map +1 -1
  417. package/dist/utils/timestamp.js +5 -8
  418. package/dist/utils/timestamp.js.map +1 -1
  419. package/package.json +2 -3
  420. package/schemas/issue/schema.yaml +8 -1
  421. package/schemas/issue/templates/spec.md +20 -3
  422. package/schemas/sdd/schema.yaml +20 -1
  423. package/schemas/sdd/templates/spec.md +20 -3
@@ -1,73 +1,68 @@
1
1
  /**
2
- * Instructions Command
3
- *
4
- * Generates enriched instructions for creating artifacts or applying tasks.
5
- * Includes both artifact instructions and apply instructions.
2
+ * `tospec instructions` — enriched briefs for creating an artifact, or for the
3
+ * apply phase.
6
4
  */
7
5
  import path from 'path';
8
6
  import * as fs from 'fs';
9
7
  import { loadChangeContext, generateInstructions, resolveSchema, resolveArtifactOutputs, } from '../../core/artifact-graph/index.js';
10
8
  import { getChangeDir, resolveCurrentPlanningHomeSync, } from '../../core/planning-home.js';
11
9
  import { resolveRootForCommand, toPlanningHome, toRootOutput, } from '../../core/root-selection.js';
12
- import { readProjectConfig } from '../../core/project-config.js';
13
- import { parseTasksFromContent } from '../../utils/task-progress.js';
10
+ import { APPLY_RULES_KEY, readProjectConfigWithDiagnostics } from '../../core/project-config.js';
11
+ import { parseTasksFromContent, resolveTaskFiles } from '../../utils/task-progress.js';
14
12
  import { validateChangeExists, validateSchemaExists, assertChangeSchema, } from './shared.js';
15
13
  import { emitSuccess } from '../shared-output.js';
16
- // -----------------------------------------------------------------------------
17
- // Types
18
- // -----------------------------------------------------------------------------
19
14
  /**
20
- * The `--json` null-shape.
21
- *
22
- * This command's success payload is flat — `instruction`, `state`, `tasks` and
23
- * the rest sit at the top level, and the skill templates read them from there —
24
- * so there is no wrapper key to null. The shape therefore nulls `instruction`,
25
- * which both success shapes (artifact and apply) actually carry; the previous
26
- * `instructions: null` named a key that appeared in neither, so the rule
27
- * "a failure payload mirrors its success payload with the data keys nulled"
28
- * could not be checked against anything.
15
+ * The `--json` null-shape. This command's success payload is flat, so there is
16
+ * no wrapper key to null; `instruction` is the one key both success shapes
17
+ * (artifact and apply) carry.
29
18
  *
30
- * Exported so the commander-level shape table and this command's own catch
31
- * block cannot drift apart — they did, and only one of them carried `root`.
19
+ * Exported so the commander-level shape table and this command's catch block
20
+ * cannot drift apart.
32
21
  */
33
22
  export const INSTRUCTIONS_FAILURE_PAYLOAD = {
34
23
  instruction: null,
35
24
  root: null,
36
25
  };
37
26
  /**
38
- * The one argument this command accepts that is not a schema artifact: it asks
39
- * for the apply phase rather than a document to write.
40
- *
41
- * Exported so the CLI's dispatch and the "valid arguments" list read the same
42
- * value — they did not, and the list left `apply` out (round 4 report 13).
27
+ * The one argument this command accepts that is not a schema artifact. Exported
28
+ * so the CLI's dispatch and the "valid arguments" list read one value.
43
29
  */
44
30
  export const APPLY_ARGUMENT = 'apply';
45
- // -----------------------------------------------------------------------------
46
- // Artifact Instructions Command
47
- // -----------------------------------------------------------------------------
48
- export async function instructionsCommand(artifactId, options) {
49
- const root = await resolveRootForCommand(options, { json: options.json });
31
+ async function resolveInstructionsTarget(options) {
32
+ // No implicit root, as `status`: "Change 'x' not found. No changes exist."
33
+ // about a directory that is no project sends the agent to `new change`
34
+ // instead of `init`.
35
+ const root = await resolveRootForCommand(options, {
36
+ json: options.json,
37
+ allowImplicitRoot: false,
38
+ failurePayload: INSTRUCTIONS_FAILURE_PAYLOAD,
39
+ });
50
40
  if (!root) {
51
- return;
41
+ return null;
52
42
  }
53
- const planningHome = toPlanningHome(root);
54
43
  const projectRoot = root.path;
55
- // Validate the schema before resolving the change (round 4 report 15): after
56
- // it, the guard never fired in an empty project, so the caller was told to
57
- // create a change first and only then learned the schema was never usable.
58
44
  if (options.schema) {
59
45
  validateSchemaExists(options.schema, projectRoot);
60
46
  assertChangeSchema(options.schema);
61
47
  }
62
- const changeName = await validateChangeExists(options.change, projectRoot, root.changesDir, { newChangeHint: 'tospec new change <name>' });
63
- // loadChangeContext will auto-detect schema from metadata if not provided
48
+ const changeName = await validateChangeExists(options.change, projectRoot, root.changesDir, {
49
+ newChangeHint: 'tospec new change <name>',
50
+ });
51
+ return { root, planningHome: toPlanningHome(root), projectRoot, changeName };
52
+ }
53
+ export async function instructionsCommand(artifactId, options) {
54
+ const target = await resolveInstructionsTarget(options);
55
+ if (!target) {
56
+ return;
57
+ }
58
+ const { root, planningHome, projectRoot, changeName } = target;
59
+ // The schema comes from the change's metadata when not given.
64
60
  const context = loadChangeContext(projectRoot, changeName, options.schema, {
65
61
  changeDir: getChangeDir(planningHome, changeName),
66
62
  planningHome,
67
63
  });
68
- // Round 4 report 13. The list came from the schema's artifacts alone, so it
69
- // omitted `apply` — the one argument this command accepts that is not an
70
- // artifact, and the one a reader who mistyped is most likely to have wanted.
64
+ // Includes `apply`: the schema's artifacts alone omit the one valid argument
65
+ // that is not an artifact.
71
66
  const validArgumentList = () => [...context.graph.getAllArtifacts().map((a) => a.id), APPLY_ARGUMENT].join('\n ');
72
67
  if (!artifactId) {
73
68
  throw new Error(`Missing required argument <artifact>. Valid arguments:\n ${validArgumentList()}`);
@@ -76,39 +71,129 @@ export async function instructionsCommand(artifactId, options) {
76
71
  if (!artifact) {
77
72
  throw new Error(`Artifact '${artifactId}' not found in schema '${context.schemaName}'. Valid arguments:\n ${validArgumentList()}`);
78
73
  }
79
- // readProjectConfig never throws: missing/unparseable configs are null.
80
- const projectConfig = readProjectConfig(root.path);
74
+ // Never throws: a missing or unparseable config comes back null with the
75
+ // reason in `diagnostics`.
76
+ const { config: projectConfig, diagnostics: configDiagnostics } = readProjectConfigWithDiagnostics(root.path);
81
77
  const instructions = generateInstructions(context, artifactId, projectRoot, {
82
78
  projectConfig,
79
+ configDiagnostics,
83
80
  });
84
- const isBlocked = instructions.dependencies.some((d) => !d.done);
81
+ // The graph's own rule, not `done` alone: an optional dependency, or one the
82
+ // change declared away (skip_specs), is met without a file. Reading `done`
83
+ // told an sdd change with skip_specs that `tasks` was blocked on `specs` —
84
+ // while `status` called it ready — and sent the agent to write the specs the
85
+ // change had just declared it has none of.
86
+ const unmetDependencies = instructions.dependencies
87
+ .filter((d) => !d.done &&
88
+ !(context.skippedArtifacts?.has(d.id) ?? false) &&
89
+ context.graph.getArtifact(d.id)?.optional !== true)
90
+ .map((d) => d.id);
91
+ // `status` says "skipped" for this artifact, so a brief that reads as a
92
+ // plain "write it" sends an agent into the contradiction `validate` rejects
93
+ // (marker set, spec files present). Said first, in both output modes.
94
+ const isSkipped = context.skippedArtifacts?.has(artifactId) ?? false;
85
95
  if (options.json) {
86
- emitSuccess({ ...instructions }, toRootOutput(root));
96
+ // `configWarnings` becomes `status[]`, the envelope field every command
97
+ // reports non-fatal trouble in.
98
+ const { configWarnings, ...payload } = instructions;
99
+ // First, ahead of any config warning: it is the one entry telling an agent
100
+ // not to write the artifact yet. The alternative is inferring it from
101
+ // `dependencies[].done`.
102
+ const status = [
103
+ ...skippedDiagnostics(instructions, changeName, isSkipped),
104
+ ...blockedDiagnostics(instructions, changeName, unmetDependencies),
105
+ ...(configWarnings ?? []).map(({ code, message }) => ({
106
+ severity: 'warning',
107
+ code,
108
+ message,
109
+ })),
110
+ ];
111
+ emitSuccess({
112
+ ...payload,
113
+ ...(status.length ? { status } : {}),
114
+ }, toRootOutput(root));
87
115
  return;
88
116
  }
89
- printInstructionsText(instructions, isBlocked);
117
+ printInstructionsText(instructions, unmetDependencies, { skipped: isSkipped });
118
+ }
119
+ /**
120
+ * The "declared away" warning, as a `status[]` entry — the JSON half of the
121
+ * `<warning>` block the human output prints. Empty unless the change's
122
+ * metadata skips this artifact.
123
+ */
124
+ function skippedDiagnostics(instructions, changeName, skipped) {
125
+ if (!skipped)
126
+ return [];
127
+ return [
128
+ {
129
+ severity: 'warning',
130
+ code: 'artifact_skipped',
131
+ message: skippedArtifactMessage(instructions.artifactId, changeName),
132
+ fix: `Remove skip_specs from the change's .tospec.yaml before writing ${instructions.artifactId}.`,
133
+ },
134
+ ];
135
+ }
136
+ function skippedArtifactMessage(artifactId, changeName) {
137
+ return `Change '${changeName}' declared '${artifactId}' away (skip_specs: true in .tospec.yaml). Writing it now leaves the marker contradicting the files, which validation rejects.`;
90
138
  }
91
- export function printInstructionsText(instructions, isBlocked) {
92
- const { artifactId, changeName, schemaName, changeDir, resolvedOutputPath, description, instruction, context, rules, template, validation, dependencies, unlocks, } = instructions;
93
- // Opening tag
139
+ /**
140
+ * The unmet-dependency warning, as a `status[]` entry — the JSON half of the
141
+ * `<warning>` block the human output prints. Empty when nothing is missing, so
142
+ * the caller can concatenate it unconditionally.
143
+ */
144
+ function blockedDiagnostics(instructions, changeName, missing) {
145
+ if (missing.length === 0)
146
+ return [];
147
+ return [
148
+ {
149
+ severity: 'warning',
150
+ code: 'artifact_blocked',
151
+ message: `Artifact '${instructions.artifactId}' has unmet dependencies: ${missing.join(', ')}. Complete them first or proceed with caution.`,
152
+ fix: `Run: tospec instructions ${missing[0]} --change ${changeName}`,
153
+ },
154
+ ];
155
+ }
156
+ export function printInstructionsText(instructions, unmetDependencies, flags = {}) {
157
+ const { artifactId, changeName, schemaName, changeDir, resolvedOutputPath, description, instruction, context, rules, template, validation, dependencies, unlocks, changeMetadata, } = instructions;
94
158
  console.log(`<artifact id="${artifactId}" change="${changeName}" schema="${schemaName}">`);
95
159
  console.log();
96
- // Warning for blocked artifacts
97
- if (isBlocked) {
98
- const missing = dependencies.filter((d) => !d.done).map((d) => d.id);
160
+ if (flags.skipped) {
161
+ console.log('<warning>');
162
+ console.log(skippedArtifactMessage(artifactId, changeName));
163
+ console.log(`Remove skip_specs from the change's .tospec.yaml before writing ${artifactId}.`);
164
+ console.log('</warning>');
165
+ console.log();
166
+ }
167
+ if (unmetDependencies.length > 0) {
99
168
  console.log('<warning>');
100
169
  console.log('This artifact has unmet dependencies. Complete them first or proceed with caution.');
101
- console.log(`Missing: ${missing.join(', ')}`);
170
+ console.log(`Missing: ${unmetDependencies.join(', ')}`);
102
171
  console.log('</warning>');
103
172
  console.log();
104
173
  }
105
- // Task directive
106
174
  console.log('<task>');
107
175
  console.log(`Create the ${artifactId} artifact for change "${changeName}".`);
108
176
  console.log(description);
109
177
  console.log('</task>');
110
178
  console.log();
111
- // Project context (AI constraint - do not include in output)
179
+ // What `tospec new change` recorded, same as the JSON payload's
180
+ // `changeMetadata`: without it the text reader never learns the goal or which
181
+ // ADRs drove the change.
182
+ const decisions = changeMetadata.decisions ?? [];
183
+ if (changeMetadata.goal || decisions.length > 0) {
184
+ console.log('<change_context>');
185
+ if (changeMetadata.goal) {
186
+ console.log(`Goal: ${changeMetadata.goal}`);
187
+ }
188
+ if (decisions.length > 0) {
189
+ console.log('Decisions driving this change (read them under tospec/decisions/):');
190
+ for (const file of decisions) {
191
+ console.log(`- ${file}`);
192
+ }
193
+ }
194
+ console.log('</change_context>');
195
+ console.log();
196
+ }
112
197
  if (context) {
113
198
  console.log('<project_context>');
114
199
  console.log('<!-- This is background information for you. Do NOT include this in your output. -->');
@@ -116,7 +201,6 @@ export function printInstructionsText(instructions, isBlocked) {
116
201
  console.log('</project_context>');
117
202
  console.log();
118
203
  }
119
- // Rules (AI constraint - do not include in output)
120
204
  if (rules && rules.length > 0) {
121
205
  console.log('<rules>');
122
206
  console.log('<!-- These are constraints for you to follow. Do NOT include this in your output. -->');
@@ -126,7 +210,6 @@ export function printInstructionsText(instructions, isBlocked) {
126
210
  console.log('</rules>');
127
211
  console.log();
128
212
  }
129
- // Dependencies (files to read for context)
130
213
  if (dependencies.length > 0) {
131
214
  console.log('<dependencies>');
132
215
  console.log('Read the current contents of these files before creating this artifact (re-read them from disk even if you saw them earlier - they may have been edited since):');
@@ -142,29 +225,23 @@ export function printInstructionsText(instructions, isBlocked) {
142
225
  console.log('</dependencies>');
143
226
  console.log();
144
227
  }
145
- // Output location
146
228
  console.log('<output>');
147
229
  console.log(`Write to: ${resolvedOutputPath}`);
148
230
  console.log('</output>');
149
231
  console.log();
150
- // Instruction (guidance)
151
232
  if (instruction) {
152
233
  console.log('<instruction>');
153
234
  console.log(instruction.trim());
154
235
  console.log('</instruction>');
155
236
  console.log();
156
237
  }
157
- // Template
158
238
  console.log('<template>');
159
239
  console.log('<!-- Use this as the structure for your output file. Fill in the sections. -->');
160
240
  console.log(template.trim());
161
241
  console.log('</template>');
162
242
  console.log();
163
- // The schema's own acceptance criteria, rendered only when it declares some.
164
- // This block used to be a fixed placeholder, so the one thing an agent needed
165
- // in order not to fail validation was the one thing the brief left out — and
166
- // an empty block claimed the artifact had no criteria at all. Printed like
167
- // <project_context> and <rules>: present when there is something to say.
243
+ // The schema's own acceptance criteria, present only when it declares some:
244
+ // an empty block would claim there are none.
168
245
  if (validation) {
169
246
  console.log('<success_criteria>');
170
247
  if (validation.requiredSections.length > 0) {
@@ -173,26 +250,23 @@ export function printInstructionsText(instructions, isBlocked) {
173
250
  for (const [section, minLength] of Object.entries(validation.minSectionLength)) {
174
251
  console.log(`Section "${section}" must be at least ${minLength} characters.`);
175
252
  }
253
+ for (const [section, maxLength] of Object.entries(validation.maxSectionLength ?? {})) {
254
+ console.log(`Section "${section}" should stay under ${maxLength} characters.`);
255
+ }
176
256
  console.log('</success_criteria>');
177
257
  console.log();
178
258
  }
179
- // Unlocks
180
259
  if (unlocks.length > 0) {
181
260
  console.log('<unlocks>');
182
261
  console.log(`Completing this artifact enables: ${unlocks.join(', ')}`);
183
262
  console.log('</unlocks>');
184
263
  console.log();
185
264
  }
186
- // Closing tag
187
265
  console.log('</artifact>');
188
266
  }
189
- // -----------------------------------------------------------------------------
190
- // Apply Instructions Command
191
- // -----------------------------------------------------------------------------
192
267
  /**
193
- * Maps a change's schema to the planning skill that produces its artifacts,
194
- * so blocked-state instructions point at a skill that actually exists
195
- * (see skill-generation.ts for the generated list).
268
+ * The planning skill that produces a schema's artifacts, so blocked-state
269
+ * instructions point at a skill that exists.
196
270
  */
197
271
  function planningSkillFor(schemaName) {
198
272
  if (schemaName === 'issue')
@@ -202,44 +276,50 @@ function planningSkillFor(schemaName) {
202
276
  return 'tospec-propose (sdd) or tospec-issue (issue)';
203
277
  }
204
278
  /**
205
- * Parses tasks.md content and extracts task items with their completion status.
279
+ * How to name the tracked task file in a message to the user. `path.basename`
280
+ * alone turns a glob (`tasks/*.md`) into a bare `*.md`, which names no file, so
281
+ * a glob keeps the `tracks` value as the schema authored it.
282
+ */
283
+ function describeTracksFile(tracksFile) {
284
+ return /[*?[\]]/.test(tracksFile) ? tracksFile : path.basename(tracksFile);
285
+ }
286
+ /**
206
287
  * Shares `parseTasksFromContent` with task progress so the apply instructions
207
288
  * and the reported `x/y` can never disagree about what counts as a task.
208
289
  */
209
290
  function parseTasksFile(content) {
210
- return parseTasksFromContent(content).map((task, i) => ({
211
- // The number the task carries in the file ("1.2"), not this array's index.
212
- // They diverge the moment a slice starts at 2.1 or a task is deleted, and
213
- // the id is the field a caller would naturally quote back to the user —
214
- // while the real numbering sat buried at the head of `description`.
215
- // Falls back to the ordinal for a task written without one.
216
- id: /^(\d+(?:\.\d+)*)\s/.exec(task.text)?.[1] ?? `${i + 1}`,
217
- description: task.text,
218
- done: task.done,
219
- }));
291
+ return parseTasksFromContent(content).map((task, i) => {
292
+ // The number the file gives the task, not this array's index: they diverge
293
+ // the moment a slice starts at 2.1 or a task is deleted, and the id is what
294
+ // a caller quotes back. Falls back to the ordinal when there is none.
295
+ const numbered = /^(\d+(?:\.\d+)*)\s+(.*)$/.exec(task.text);
296
+ return {
297
+ id: numbered?.[1] ?? `${i + 1}`,
298
+ // Without that number, since `id` carries it and a consumer joining the
299
+ // two would render "1.1 1.1 Write a failing test".
300
+ description: numbered?.[2] ?? task.text,
301
+ done: task.done,
302
+ };
303
+ });
220
304
  }
221
305
  /**
222
- * Generates apply instructions for implementing tasks from a change.
223
- * Schema-aware: reads apply phase configuration from schema to determine
224
- * required artifacts, tracking file, and instruction.
306
+ * Apply instructions for a change, from the schema's `apply` block: required
307
+ * artifacts, tracking file, and instruction.
225
308
  */
226
309
  export async function generateApplyInstructions(projectRoot, changeName, schemaName, options = {}) {
227
310
  const planningHome = options.planningHome ?? resolveCurrentPlanningHomeSync({ startPath: projectRoot });
228
- // loadChangeContext will auto-detect schema from metadata if not provided
311
+ // The schema comes from the change's metadata when not given.
229
312
  const context = loadChangeContext(projectRoot, changeName, schemaName, {
230
313
  changeDir: getChangeDir(planningHome, changeName),
231
314
  planningHome,
232
315
  });
233
316
  const changeDir = context.changeDir;
234
- // Get the full schema to access the apply phase configuration
235
317
  const schema = resolveSchema(context.schemaName, projectRoot);
236
318
  const applyConfig = schema.apply;
237
- // Determine required artifacts and tracking file from schema
238
- // Fallback: if no apply block, require all artifacts
319
+ // No apply block: require every artifact.
239
320
  const requiredArtifactIds = applyConfig?.requires ?? schema.artifacts.map((a) => a.id);
240
321
  const tracksFile = applyConfig?.tracks ?? null;
241
322
  const schemaInstruction = applyConfig?.instruction ?? null;
242
- // Check which required artifacts are missing
243
323
  const missingArtifacts = [];
244
324
  for (const artifactId of requiredArtifactIds) {
245
325
  const artifact = schema.artifacts.find((a) => a.id === artifactId);
@@ -247,7 +327,6 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
247
327
  missingArtifacts.push(artifactId);
248
328
  }
249
329
  }
250
- // Build context files from all existing artifacts in schema
251
330
  const contextFiles = {};
252
331
  for (const artifact of schema.artifacts) {
253
332
  const outputs = resolveArtifactOutputs(changeDir, artifact.generates);
@@ -255,22 +334,22 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
255
334
  contextFiles[artifact.id] = outputs;
256
335
  }
257
336
  }
258
- // Parse tasks if tracking file exists
337
+ // Through `resolveTaskFiles`, the resolver `list`, the dashboard and archive's
338
+ // completeness gate share: `apply.tracks` names an artifact by its `generates`
339
+ // value, which may be a glob (`tasks/*.md`). Treated as a literal path it
340
+ // finds no file, so a multi-file schema reports `blocked` and `0/0`.
259
341
  let tasks = [];
260
342
  let tracksFileExists = false;
261
343
  if (tracksFile) {
262
- const tracksPath = path.join(changeDir, tracksFile);
263
- tracksFileExists = fs.existsSync(tracksPath);
264
- if (tracksFileExists) {
265
- const tasksContent = await fs.promises.readFile(tracksPath, 'utf-8');
266
- tasks = parseTasksFile(tasksContent);
344
+ const trackedFiles = resolveTaskFiles(changeDir, projectRoot).filter((file) => fs.existsSync(file));
345
+ tracksFileExists = trackedFiles.length > 0;
346
+ for (const file of trackedFiles) {
347
+ tasks.push(...parseTasksFile(await fs.promises.readFile(file, 'utf-8')));
267
348
  }
268
349
  }
269
- // Calculate progress
270
350
  const total = tasks.length;
271
351
  const complete = tasks.filter((t) => t.done).length;
272
352
  const remaining = total - complete;
273
- // Determine state and instruction
274
353
  let state;
275
354
  let instruction;
276
355
  const planningSkill = planningSkillFor(context.schemaName);
@@ -279,14 +358,12 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
279
358
  instruction = `Cannot apply this change yet. Missing artifacts: ${missingArtifacts.join(', ')}.\nStop and send the user back to ${planningSkill} to create the missing artifacts first.`;
280
359
  }
281
360
  else if (tracksFile && !tracksFileExists) {
282
- // Tracking file configured but doesn't exist yet
283
- const tracksFilename = path.basename(tracksFile);
361
+ const tracksFilename = describeTracksFile(tracksFile);
284
362
  state = 'blocked';
285
363
  instruction = `The ${tracksFilename} file is missing and must be created.\nStop and send the user back to ${planningSkill} to generate the tracking file.`;
286
364
  }
287
365
  else if (tracksFile && tracksFileExists && total === 0) {
288
- // Tracking file exists but contains no tasks
289
- const tracksFilename = path.basename(tracksFile);
366
+ const tracksFilename = describeTracksFile(tracksFile);
290
367
  state = 'blocked';
291
368
  instruction = `The ${tracksFilename} file exists but contains no tasks.\nAdd tasks to ${tracksFilename} or stop and send the user back to ${planningSkill} to regenerate it.`;
292
369
  }
@@ -295,7 +372,6 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
295
372
  instruction = 'All tasks are complete! This change is ready to be archived.\nConsider running tests and reviewing the changes before archiving.';
296
373
  }
297
374
  else if (!tracksFile) {
298
- // No tracking file configured in schema - ready to apply
299
375
  state = 'ready';
300
376
  instruction = schemaInstruction?.trim() ?? 'All required artifacts complete. Proceed with implementation.';
301
377
  }
@@ -303,12 +379,10 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
303
379
  state = 'ready';
304
380
  instruction = schemaInstruction?.trim() ?? 'Read context files, work through pending tasks, mark complete as you go.\nPause if you hit blockers or need clarification.';
305
381
  }
306
- // Schema artifacts that do not exist yet but that `apply.requires` does not
307
- // insist on. Reported, not blocking: sdd's apply requires only `tasks`, so a
308
- // change missing proposal/design/specs still reaches `state: "ready"` — while
309
- // the instruction it is handed says things like "test only the seam design.md
310
- // chose". Naming the absent files is what stops that reading as a reference to
311
- // something the agent simply failed to find.
382
+ // Absent artifacts `apply.requires` does not insist on: reported, not
383
+ // blocking. sdd's apply requires only `tasks`, so a change missing
384
+ // proposal/design/specs reaches `ready` while its instruction still refers
385
+ // to them.
312
386
  const missingContext = schema.artifacts
313
387
  .filter((artifact) => !contextFiles[artifact.id] && !missingArtifacts.includes(artifact.id))
314
388
  .map((artifact) => artifact.id);
@@ -317,6 +391,10 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
317
391
 
318
392
  Not present in this change: ${missingContext.join(', ')}. Ignore any part of the above that refers to them.`;
319
393
  }
394
+ // The project's own context and rules, as every artifact's instructions carry.
395
+ const { config: projectConfig, diagnostics: configWarnings } = readProjectConfigWithDiagnostics(projectRoot);
396
+ const configContext = projectConfig?.context?.trim() || undefined;
397
+ const applyRules = projectConfig?.rules?.[APPLY_RULES_KEY];
320
398
  return {
321
399
  changeName,
322
400
  changeDir,
@@ -328,39 +406,61 @@ Not present in this change: ${missingContext.join(', ')}. Ignore any part of the
328
406
  missingArtifacts: missingArtifacts.length > 0 ? missingArtifacts : undefined,
329
407
  missingContext: missingContext.length > 0 ? missingContext : undefined,
330
408
  instruction,
409
+ ...(configContext ? { context: configContext } : {}),
410
+ ...(applyRules?.length ? { rules: applyRules } : {}),
411
+ ...(configWarnings.length ? { configWarnings } : {}),
331
412
  };
332
413
  }
333
414
  export async function applyInstructionsCommand(options) {
334
- const root = await resolveRootForCommand(options, { json: options.json });
335
- if (!root) {
415
+ const target = await resolveInstructionsTarget(options);
416
+ if (!target) {
336
417
  return;
337
418
  }
338
- const planningHome = toPlanningHome(root);
339
- const projectRoot = root.path;
340
- // Validate the schema before resolving the change (round 4 report 15): after
341
- // it, the guard never fired in an empty project, so the caller was told to
342
- // create a change first and only then learned the schema was never usable.
343
- if (options.schema) {
344
- validateSchemaExists(options.schema, projectRoot);
345
- assertChangeSchema(options.schema);
346
- }
347
- const changeName = await validateChangeExists(options.change, projectRoot, root.changesDir, { newChangeHint: 'tospec new change <name>' });
348
- // generateApplyInstructions uses loadChangeContext which auto-detects schema
419
+ const { root, planningHome, projectRoot, changeName } = target;
349
420
  const instructions = await generateApplyInstructions(projectRoot, changeName, options.schema, {
350
421
  planningHome,
351
422
  });
352
423
  if (options.json) {
353
- emitSuccess({ ...instructions }, toRootOutput(root));
424
+ // As in the artifact payload: `configWarnings` becomes the envelope's
425
+ // `status[]`, not a field of its own.
426
+ const { configWarnings, ...payload } = instructions;
427
+ emitSuccess({
428
+ ...payload,
429
+ ...(configWarnings?.length
430
+ ? {
431
+ status: configWarnings.map(({ code, message }) => ({
432
+ severity: 'warning',
433
+ code,
434
+ message,
435
+ })),
436
+ }
437
+ : {}),
438
+ }, toRootOutput(root));
354
439
  return;
355
440
  }
356
441
  printApplyInstructionsText(instructions);
357
442
  }
358
443
  export function printApplyInstructionsText(instructions) {
359
- const { changeName, schemaName, contextFiles, progress, tasks, state, missingArtifacts, instruction } = instructions;
444
+ const { changeName, schemaName, contextFiles, progress, tasks, state, missingArtifacts, instruction, context, rules, } = instructions;
360
445
  console.log(`## Apply: ${changeName}`);
361
446
  console.log(`Schema: ${schemaName}`);
362
447
  console.log();
363
- // Warning for blocked state
448
+ // The same tags the artifact instructions use, so an agent reading either
449
+ // treats project constraints the same way.
450
+ if (context) {
451
+ console.log('<project_context>');
452
+ console.log(context);
453
+ console.log('</project_context>');
454
+ console.log();
455
+ }
456
+ if (rules && rules.length > 0) {
457
+ console.log('<rules>');
458
+ for (const rule of rules) {
459
+ console.log(`- ${rule}`);
460
+ }
461
+ console.log('</rules>');
462
+ console.log();
463
+ }
364
464
  if (state === 'blocked' && missingArtifacts) {
365
465
  console.log('### ⚠️ Blocked');
366
466
  console.log();
@@ -368,7 +468,6 @@ export function printApplyInstructionsText(instructions) {
368
468
  console.log(`Use the ${planningSkillFor(schemaName)} skill to create these first.`);
369
469
  console.log();
370
470
  }
371
- // Context files (dynamically from schema)
372
471
  const contextFileEntries = Object.entries(contextFiles);
373
472
  if (contextFileEntries.length > 0) {
374
473
  console.log('### Context Files');
@@ -379,7 +478,6 @@ export function printApplyInstructionsText(instructions) {
379
478
  }
380
479
  console.log();
381
480
  }
382
- // Progress (only show if we have tracking)
383
481
  if (progress.total > 0 || tasks.length > 0) {
384
482
  console.log('### Progress');
385
483
  if (state === 'all_done') {
@@ -390,16 +488,16 @@ export function printApplyInstructionsText(instructions) {
390
488
  }
391
489
  console.log();
392
490
  }
393
- // Tasks
394
491
  if (tasks.length > 0) {
395
492
  console.log('### Tasks');
396
493
  for (const task of tasks) {
397
494
  const checkbox = task.done ? '[x]' : '[ ]';
398
- console.log(`- ${checkbox} ${task.description}`);
495
+ // Joined again here: the number is stored apart only so a caller can
496
+ // address a task by it.
497
+ console.log(`- ${checkbox} ${task.id} ${task.description}`);
399
498
  }
400
499
  console.log();
401
500
  }
402
- // Instruction
403
501
  console.log('### Instruction');
404
502
  console.log(instruction);
405
503
  }