openspecui 0.0.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 (311) hide show
  1. package/.gitmodules +3 -0
  2. package/CHAT.md +3 -0
  3. package/package.json +12 -0
  4. package/references/openspec/.changeset/README.md +6 -0
  5. package/references/openspec/.changeset/config.json +12 -0
  6. package/references/openspec/.coderabbit.yaml +11 -0
  7. package/references/openspec/.devcontainer/README.md +92 -0
  8. package/references/openspec/.devcontainer/devcontainer.json +68 -0
  9. package/references/openspec/.github/CODEOWNERS +2 -0
  10. package/references/openspec/.github/workflows/ci.yml +222 -0
  11. package/references/openspec/.github/workflows/release-prepare.yml +50 -0
  12. package/references/openspec/AGENTS.md +18 -0
  13. package/references/openspec/CHANGELOG.md +205 -0
  14. package/references/openspec/LICENSE +22 -0
  15. package/references/openspec/README.md +374 -0
  16. package/references/openspec/assets/openspec_dashboard.png +0 -0
  17. package/references/openspec/assets/openspec_pixel_dark.svg +89 -0
  18. package/references/openspec/assets/openspec_pixel_light.svg +89 -0
  19. package/references/openspec/bin/openspec.js +3 -0
  20. package/references/openspec/build.js +31 -0
  21. package/references/openspec/openspec/AGENTS.md +454 -0
  22. package/references/openspec/openspec/changes/IMPLEMENTATION_ORDER.md +68 -0
  23. package/references/openspec/openspec/changes/add-antigravity-support/proposal.md +11 -0
  24. package/references/openspec/openspec/changes/add-antigravity-support/specs/cli-init/spec.md +9 -0
  25. package/references/openspec/openspec/changes/add-antigravity-support/specs/cli-update/spec.md +8 -0
  26. package/references/openspec/openspec/changes/add-antigravity-support/tasks.md +12 -0
  27. package/references/openspec/openspec/changes/add-scaffold-command/proposal.md +11 -0
  28. package/references/openspec/openspec/changes/add-scaffold-command/specs/cli-scaffold/spec.md +36 -0
  29. package/references/openspec/openspec/changes/add-scaffold-command/tasks.md +12 -0
  30. package/references/openspec/openspec/changes/archive/2025-01-11-add-update-command/design.md +86 -0
  31. package/references/openspec/openspec/changes/archive/2025-01-11-add-update-command/proposal.md +29 -0
  32. package/references/openspec/openspec/changes/archive/2025-01-11-add-update-command/specs/cli-update/spec.md +59 -0
  33. package/references/openspec/openspec/changes/archive/2025-01-11-add-update-command/tasks.md +20 -0
  34. package/references/openspec/openspec/changes/archive/2025-01-13-add-list-command/proposal.md +20 -0
  35. package/references/openspec/openspec/changes/archive/2025-01-13-add-list-command/specs/cli-list/spec.md +69 -0
  36. package/references/openspec/openspec/changes/archive/2025-01-13-add-list-command/tasks.md +26 -0
  37. package/references/openspec/openspec/changes/archive/2025-08-05-initialize-typescript-project/design.md +64 -0
  38. package/references/openspec/openspec/changes/archive/2025-08-05-initialize-typescript-project/proposal.md +18 -0
  39. package/references/openspec/openspec/changes/archive/2025-08-05-initialize-typescript-project/tasks.md +25 -0
  40. package/references/openspec/openspec/changes/archive/2025-08-06-add-init-command/design.md +104 -0
  41. package/references/openspec/openspec/changes/archive/2025-08-06-add-init-command/proposal.md +30 -0
  42. package/references/openspec/openspec/changes/archive/2025-08-06-add-init-command/specs/cli-init/spec.md +148 -0
  43. package/references/openspec/openspec/changes/archive/2025-08-06-add-init-command/tasks.md +38 -0
  44. package/references/openspec/openspec/changes/archive/2025-08-06-adopt-future-state-storage/proposal.md +24 -0
  45. package/references/openspec/openspec/changes/archive/2025-08-06-adopt-future-state-storage/specs/openspec-conventions/spec.md +120 -0
  46. package/references/openspec/openspec/changes/archive/2025-08-06-adopt-future-state-storage/tasks.md +38 -0
  47. package/references/openspec/openspec/changes/archive/2025-08-11-add-complexity-guidelines/proposal.md +13 -0
  48. package/references/openspec/openspec/changes/archive/2025-08-11-add-complexity-guidelines/specs/openspec-docs/README.md +472 -0
  49. package/references/openspec/openspec/changes/archive/2025-08-11-add-complexity-guidelines/tasks.md +9 -0
  50. package/references/openspec/openspec/changes/archive/2025-08-13-add-archive-command/proposal.md +15 -0
  51. package/references/openspec/openspec/changes/archive/2025-08-13-add-archive-command/specs/cli-archive/spec.md +111 -0
  52. package/references/openspec/openspec/changes/archive/2025-08-13-add-archive-command/tasks.md +44 -0
  53. package/references/openspec/openspec/changes/archive/2025-08-13-add-diff-command/proposal.md +19 -0
  54. package/references/openspec/openspec/changes/archive/2025-08-13-add-diff-command/specs/cli-diff/spec.md +77 -0
  55. package/references/openspec/openspec/changes/archive/2025-08-13-add-diff-command/tasks.md +23 -0
  56. package/references/openspec/openspec/changes/archive/2025-08-19-add-change-commands/design.md +56 -0
  57. package/references/openspec/openspec/changes/archive/2025-08-19-add-change-commands/proposal.md +17 -0
  58. package/references/openspec/openspec/changes/archive/2025-08-19-add-change-commands/specs/cli-change/spec.md +48 -0
  59. package/references/openspec/openspec/changes/archive/2025-08-19-add-change-commands/specs/cli-list/spec.md +12 -0
  60. package/references/openspec/openspec/changes/archive/2025-08-19-add-change-commands/tasks.md +34 -0
  61. package/references/openspec/openspec/changes/archive/2025-08-19-add-interactive-show-command/proposal.md +20 -0
  62. package/references/openspec/openspec/changes/archive/2025-08-19-add-interactive-show-command/specs/cli-change/spec.md +23 -0
  63. package/references/openspec/openspec/changes/archive/2025-08-19-add-interactive-show-command/specs/cli-show/spec.md +83 -0
  64. package/references/openspec/openspec/changes/archive/2025-08-19-add-interactive-show-command/specs/cli-spec/spec.md +23 -0
  65. package/references/openspec/openspec/changes/archive/2025-08-19-add-interactive-show-command/tasks.md +142 -0
  66. package/references/openspec/openspec/changes/archive/2025-08-19-add-skip-specs-archive-option/proposal.md +13 -0
  67. package/references/openspec/openspec/changes/archive/2025-08-19-add-skip-specs-archive-option/specs/cli-archive/spec.md +191 -0
  68. package/references/openspec/openspec/changes/archive/2025-08-19-add-skip-specs-archive-option/tasks.md +57 -0
  69. package/references/openspec/openspec/changes/archive/2025-08-19-add-spec-commands/design.md +45 -0
  70. package/references/openspec/openspec/changes/archive/2025-08-19-add-spec-commands/proposal.md +19 -0
  71. package/references/openspec/openspec/changes/archive/2025-08-19-add-spec-commands/specs/cli-spec/spec.md +43 -0
  72. package/references/openspec/openspec/changes/archive/2025-08-19-add-spec-commands/tasks.md +22 -0
  73. package/references/openspec/openspec/changes/archive/2025-08-19-add-zod-validation/design.md +104 -0
  74. package/references/openspec/openspec/changes/archive/2025-08-19-add-zod-validation/proposal.md +22 -0
  75. package/references/openspec/openspec/changes/archive/2025-08-19-add-zod-validation/specs/cli-archive/spec.md +18 -0
  76. package/references/openspec/openspec/changes/archive/2025-08-19-add-zod-validation/specs/cli-diff/spec.md +12 -0
  77. package/references/openspec/openspec/changes/archive/2025-08-19-add-zod-validation/tasks.md +59 -0
  78. package/references/openspec/openspec/changes/archive/2025-08-19-adopt-delta-based-changes/proposal.md +93 -0
  79. package/references/openspec/openspec/changes/archive/2025-08-19-adopt-delta-based-changes/specs/cli-archive/spec.md +48 -0
  80. package/references/openspec/openspec/changes/archive/2025-08-19-adopt-delta-based-changes/specs/cli-diff/spec.md +45 -0
  81. package/references/openspec/openspec/changes/archive/2025-08-19-adopt-delta-based-changes/specs/openspec-conventions/spec.md +101 -0
  82. package/references/openspec/openspec/changes/archive/2025-08-19-adopt-delta-based-changes/tasks.md +55 -0
  83. package/references/openspec/openspec/changes/archive/2025-08-19-adopt-verb-noun-cli-structure/design.md +19 -0
  84. package/references/openspec/openspec/changes/archive/2025-08-19-adopt-verb-noun-cli-structure/proposal.md +67 -0
  85. package/references/openspec/openspec/changes/archive/2025-08-19-adopt-verb-noun-cli-structure/specs/cli-list/spec.md +57 -0
  86. package/references/openspec/openspec/changes/archive/2025-08-19-adopt-verb-noun-cli-structure/specs/openspec-conventions/spec.md +23 -0
  87. package/references/openspec/openspec/changes/archive/2025-08-19-adopt-verb-noun-cli-structure/tasks.md +27 -0
  88. package/references/openspec/openspec/changes/archive/2025-08-19-bulk-validation-interactive-selection/proposal.md +20 -0
  89. package/references/openspec/openspec/changes/archive/2025-08-19-bulk-validation-interactive-selection/specs/cli-change/spec.md +22 -0
  90. package/references/openspec/openspec/changes/archive/2025-08-19-bulk-validation-interactive-selection/specs/cli-spec/spec.md +23 -0
  91. package/references/openspec/openspec/changes/archive/2025-08-19-bulk-validation-interactive-selection/specs/cli-validate/spec.md +149 -0
  92. package/references/openspec/openspec/changes/archive/2025-08-19-bulk-validation-interactive-selection/tasks.md +81 -0
  93. package/references/openspec/openspec/changes/archive/2025-08-19-fix-update-tool-selection/proposal.md +40 -0
  94. package/references/openspec/openspec/changes/archive/2025-08-19-fix-update-tool-selection/specs/cli-update/spec.md +23 -0
  95. package/references/openspec/openspec/changes/archive/2025-08-19-fix-update-tool-selection/tasks.md +21 -0
  96. package/references/openspec/openspec/changes/archive/2025-08-19-improve-validate-error-messages/proposal.md +25 -0
  97. package/references/openspec/openspec/changes/archive/2025-08-19-improve-validate-error-messages/specs/cli-validate/spec.md +55 -0
  98. package/references/openspec/openspec/changes/archive/2025-08-19-improve-validate-error-messages/tasks.md +21 -0
  99. package/references/openspec/openspec/changes/archive/2025-08-19-structured-spec-format/proposal.md +36 -0
  100. package/references/openspec/openspec/changes/archive/2025-08-19-structured-spec-format/specs/openspec-conventions/spec.md +192 -0
  101. package/references/openspec/openspec/changes/archive/2025-08-19-structured-spec-format/tasks.md +19 -0
  102. package/references/openspec/openspec/changes/archive/2025-09-12-add-view-dashboard-command/proposal.md +38 -0
  103. package/references/openspec/openspec/changes/archive/2025-09-12-add-view-dashboard-command/specs/cli-view/spec.md +109 -0
  104. package/references/openspec/openspec/changes/archive/2025-09-12-add-view-dashboard-command/tasks.md +47 -0
  105. package/references/openspec/openspec/changes/archive/2025-09-29-add-agents-md-config/proposal.md +28 -0
  106. package/references/openspec/openspec/changes/archive/2025-09-29-add-agents-md-config/specs/cli-init/spec.md +71 -0
  107. package/references/openspec/openspec/changes/archive/2025-09-29-add-agents-md-config/specs/cli-update/spec.md +41 -0
  108. package/references/openspec/openspec/changes/archive/2025-09-29-add-agents-md-config/tasks.md +17 -0
  109. package/references/openspec/openspec/changes/archive/2025-09-29-add-multi-agent-init/proposal.md +35 -0
  110. package/references/openspec/openspec/changes/archive/2025-09-29-add-multi-agent-init/specs/cli-init/spec.md +45 -0
  111. package/references/openspec/openspec/changes/archive/2025-09-29-add-multi-agent-init/tasks.md +16 -0
  112. package/references/openspec/openspec/changes/archive/2025-09-29-add-slash-command-support/proposal.md +119 -0
  113. package/references/openspec/openspec/changes/archive/2025-09-29-add-slash-command-support/specs/cli-init/spec.md +21 -0
  114. package/references/openspec/openspec/changes/archive/2025-09-29-add-slash-command-support/specs/cli-update/spec.md +22 -0
  115. package/references/openspec/openspec/changes/archive/2025-09-29-add-slash-command-support/tasks.md +20 -0
  116. package/references/openspec/openspec/changes/archive/2025-09-29-improve-cli-e2e-plan/proposal.md +19 -0
  117. package/references/openspec/openspec/changes/archive/2025-09-29-improve-cli-e2e-plan/tasks.md +9 -0
  118. package/references/openspec/openspec/changes/archive/2025-09-29-improve-deterministic-tests/proposal.md +78 -0
  119. package/references/openspec/openspec/changes/archive/2025-09-29-improve-deterministic-tests/tasks.md +25 -0
  120. package/references/openspec/openspec/changes/archive/2025-09-29-improve-init-onboarding/proposal.md +13 -0
  121. package/references/openspec/openspec/changes/archive/2025-09-29-improve-init-onboarding/specs/cli-init/spec.md +92 -0
  122. package/references/openspec/openspec/changes/archive/2025-09-29-improve-init-onboarding/tasks.md +12 -0
  123. package/references/openspec/openspec/changes/archive/2025-09-29-remove-diff-command/proposal.md +81 -0
  124. package/references/openspec/openspec/changes/archive/2025-09-29-remove-diff-command/tasks.md +37 -0
  125. package/references/openspec/openspec/changes/archive/2025-09-29-sort-active-changes-by-progress/proposal.md +25 -0
  126. package/references/openspec/openspec/changes/archive/2025-09-29-sort-active-changes-by-progress/specs/cli-view/spec.md +9 -0
  127. package/references/openspec/openspec/changes/archive/2025-09-29-sort-active-changes-by-progress/tasks.md +8 -0
  128. package/references/openspec/openspec/changes/archive/2025-09-29-update-agent-file-name/proposal.md +29 -0
  129. package/references/openspec/openspec/changes/archive/2025-09-29-update-agent-file-name/specs/cli-init/spec.md +40 -0
  130. package/references/openspec/openspec/changes/archive/2025-09-29-update-agent-file-name/specs/cli-update/spec.md +22 -0
  131. package/references/openspec/openspec/changes/archive/2025-09-29-update-agent-file-name/specs/openspec-conventions/spec.md +27 -0
  132. package/references/openspec/openspec/changes/archive/2025-09-29-update-agent-file-name/tasks.md +22 -0
  133. package/references/openspec/openspec/changes/archive/2025-09-29-update-agent-instructions/design.md +130 -0
  134. package/references/openspec/openspec/changes/archive/2025-09-29-update-agent-instructions/proposal.md +117 -0
  135. package/references/openspec/openspec/changes/archive/2025-09-29-update-agent-instructions/tasks.md +69 -0
  136. package/references/openspec/openspec/changes/archive/2025-09-29-update-markdown-parser-crlf/proposal.md +19 -0
  137. package/references/openspec/openspec/changes/archive/2025-09-29-update-markdown-parser-crlf/specs/cli-validate/spec.md +9 -0
  138. package/references/openspec/openspec/changes/archive/2025-09-29-update-markdown-parser-crlf/tasks.md +11 -0
  139. package/references/openspec/openspec/changes/archive/2025-10-14-add-codex-slash-command-support/proposal.md +25 -0
  140. package/references/openspec/openspec/changes/archive/2025-10-14-add-codex-slash-command-support/specs/cli-init/spec.md +56 -0
  141. package/references/openspec/openspec/changes/archive/2025-10-14-add-codex-slash-command-support/specs/cli-update/spec.md +41 -0
  142. package/references/openspec/openspec/changes/archive/2025-10-14-add-codex-slash-command-support/tasks.md +19 -0
  143. package/references/openspec/openspec/changes/archive/2025-10-14-add-github-copilot-prompts/proposal.md +25 -0
  144. package/references/openspec/openspec/changes/archive/2025-10-14-add-github-copilot-prompts/specs/cli-init/spec.md +48 -0
  145. package/references/openspec/openspec/changes/archive/2025-10-14-add-github-copilot-prompts/specs/cli-update/spec.md +48 -0
  146. package/references/openspec/openspec/changes/archive/2025-10-14-add-github-copilot-prompts/tasks.md +30 -0
  147. package/references/openspec/openspec/changes/archive/2025-10-14-add-kilocode-workflows/proposal.md +17 -0
  148. package/references/openspec/openspec/changes/archive/2025-10-14-add-kilocode-workflows/specs/cli-init/spec.md +43 -0
  149. package/references/openspec/openspec/changes/archive/2025-10-14-add-kilocode-workflows/specs/cli-update/spec.md +27 -0
  150. package/references/openspec/openspec/changes/archive/2025-10-14-add-kilocode-workflows/tasks.md +15 -0
  151. package/references/openspec/openspec/changes/archive/2025-10-14-add-non-interactive-init-options/proposal.md +12 -0
  152. package/references/openspec/openspec/changes/archive/2025-10-14-add-non-interactive-init-options/specs/cli-init/spec.md +39 -0
  153. package/references/openspec/openspec/changes/archive/2025-10-14-add-non-interactive-init-options/tasks.md +17 -0
  154. package/references/openspec/openspec/changes/archive/2025-10-14-add-windsurf-workflows/proposal.md +17 -0
  155. package/references/openspec/openspec/changes/archive/2025-10-14-add-windsurf-workflows/specs/cli-init/spec.md +42 -0
  156. package/references/openspec/openspec/changes/archive/2025-10-14-add-windsurf-workflows/specs/cli-update/spec.md +27 -0
  157. package/references/openspec/openspec/changes/archive/2025-10-14-add-windsurf-workflows/tasks.md +17 -0
  158. package/references/openspec/openspec/changes/archive/2025-10-14-enhance-validation-error-messages/proposal.md +12 -0
  159. package/references/openspec/openspec/changes/archive/2025-10-14-enhance-validation-error-messages/specs/cli-validate/spec.md +39 -0
  160. package/references/openspec/openspec/changes/archive/2025-10-14-enhance-validation-error-messages/tasks.md +12 -0
  161. package/references/openspec/openspec/changes/archive/2025-10-14-improve-agent-instruction-usability/proposal.md +12 -0
  162. package/references/openspec/openspec/changes/archive/2025-10-14-improve-agent-instruction-usability/specs/docs-agent-instructions/spec.md +33 -0
  163. package/references/openspec/openspec/changes/archive/2025-10-14-improve-agent-instruction-usability/tasks.md +11 -0
  164. package/references/openspec/openspec/changes/archive/2025-10-14-slim-root-agents-file/proposal.md +13 -0
  165. package/references/openspec/openspec/changes/archive/2025-10-14-slim-root-agents-file/tasks.md +15 -0
  166. package/references/openspec/openspec/changes/archive/2025-10-14-update-cli-init-enter-selection/proposal.md +14 -0
  167. package/references/openspec/openspec/changes/archive/2025-10-14-update-cli-init-enter-selection/specs/cli-init/spec.md +10 -0
  168. package/references/openspec/openspec/changes/archive/2025-10-14-update-cli-init-enter-selection/tasks.md +8 -0
  169. package/references/openspec/openspec/changes/archive/2025-10-14-update-cli-init-root-agents/proposal.md +15 -0
  170. package/references/openspec/openspec/changes/archive/2025-10-14-update-cli-init-root-agents/specs/cli-init/spec.md +32 -0
  171. package/references/openspec/openspec/changes/archive/2025-10-14-update-cli-init-root-agents/specs/cli-update/spec.md +10 -0
  172. package/references/openspec/openspec/changes/archive/2025-10-14-update-cli-init-root-agents/tasks.md +11 -0
  173. package/references/openspec/openspec/changes/archive/2025-10-14-update-release-automation/proposal.md +49 -0
  174. package/references/openspec/openspec/changes/archive/2025-10-14-update-release-automation/tasks.md +12 -0
  175. package/references/openspec/openspec/changes/archive/2025-10-22-add-archive-command-arguments/proposal.md +17 -0
  176. package/references/openspec/openspec/changes/archive/2025-10-22-add-archive-command-arguments/specs/cli-update/spec.md +32 -0
  177. package/references/openspec/openspec/changes/archive/2025-10-22-add-archive-command-arguments/tasks.md +15 -0
  178. package/references/openspec/openspec/changes/archive/2025-10-22-add-cline-support/proposal.md +15 -0
  179. package/references/openspec/openspec/changes/archive/2025-10-22-add-cline-support/specs/cli-init/spec.md +97 -0
  180. package/references/openspec/openspec/changes/archive/2025-10-22-add-cline-support/tasks.md +19 -0
  181. package/references/openspec/openspec/changes/archive/2025-10-22-add-crush-support/proposal.md +13 -0
  182. package/references/openspec/openspec/changes/archive/2025-10-22-add-crush-support/specs/cli-init/spec.md +67 -0
  183. package/references/openspec/openspec/changes/archive/2025-10-22-add-crush-support/tasks.md +7 -0
  184. package/references/openspec/openspec/changes/archive/2025-10-22-add-factory-slash-commands/proposal.md +12 -0
  185. package/references/openspec/openspec/changes/archive/2025-10-22-add-factory-slash-commands/specs/cli-init/spec.md +54 -0
  186. package/references/openspec/openspec/changes/archive/2025-10-22-add-factory-slash-commands/specs/cli-update/spec.md +54 -0
  187. package/references/openspec/openspec/changes/archive/2025-10-22-add-factory-slash-commands/tasks.md +11 -0
  188. package/references/openspec/openspec/changes/fix-cline-workflows-implementation/proposal.md +13 -0
  189. package/references/openspec/openspec/changes/fix-cline-workflows-implementation/specs/cli-init/spec.md +11 -0
  190. package/references/openspec/openspec/changes/fix-cline-workflows-implementation/tasks.md +13 -0
  191. package/references/openspec/openspec/changes/make-validation-scope-aware/proposal.md +12 -0
  192. package/references/openspec/openspec/changes/make-validation-scope-aware/specs/cli-validate/spec.md +25 -0
  193. package/references/openspec/openspec/changes/make-validation-scope-aware/tasks.md +16 -0
  194. package/references/openspec/openspec/project.md +53 -0
  195. package/references/openspec/openspec/specs/cli-archive/spec.md +210 -0
  196. package/references/openspec/openspec/specs/cli-change/spec.md +91 -0
  197. package/references/openspec/openspec/specs/cli-init/spec.md +311 -0
  198. package/references/openspec/openspec/specs/cli-list/spec.md +103 -0
  199. package/references/openspec/openspec/specs/cli-show/spec.md +85 -0
  200. package/references/openspec/openspec/specs/cli-spec/spec.md +87 -0
  201. package/references/openspec/openspec/specs/cli-update/spec.md +190 -0
  202. package/references/openspec/openspec/specs/cli-validate/spec.md +218 -0
  203. package/references/openspec/openspec/specs/cli-view/spec.md +105 -0
  204. package/references/openspec/openspec/specs/docs-agent-instructions/spec.md +38 -0
  205. package/references/openspec/openspec/specs/openspec-conventions/spec.md +474 -0
  206. package/references/openspec/openspec-parallel-merge-plan.md +98 -0
  207. package/references/openspec/package.json +73 -0
  208. package/references/openspec/pnpm-lock.yaml +2324 -0
  209. package/references/openspec/scripts/pack-version-check.mjs +111 -0
  210. package/references/openspec/src/cli/index.ts +253 -0
  211. package/references/openspec/src/commands/change.ts +291 -0
  212. package/references/openspec/src/commands/show.ts +139 -0
  213. package/references/openspec/src/commands/spec.ts +250 -0
  214. package/references/openspec/src/commands/validate.ts +305 -0
  215. package/references/openspec/src/core/archive.ts +606 -0
  216. package/references/openspec/src/core/config.ts +41 -0
  217. package/references/openspec/src/core/configurators/agents.ts +23 -0
  218. package/references/openspec/src/core/configurators/base.ts +6 -0
  219. package/references/openspec/src/core/configurators/claude.ts +23 -0
  220. package/references/openspec/src/core/configurators/cline.ts +23 -0
  221. package/references/openspec/src/core/configurators/codebuddy.ts +24 -0
  222. package/references/openspec/src/core/configurators/costrict.ts +23 -0
  223. package/references/openspec/src/core/configurators/iflow.ts +23 -0
  224. package/references/openspec/src/core/configurators/qoder.ts +53 -0
  225. package/references/openspec/src/core/configurators/qwen.ts +47 -0
  226. package/references/openspec/src/core/configurators/registry.ts +49 -0
  227. package/references/openspec/src/core/configurators/slash/amazon-q.ts +51 -0
  228. package/references/openspec/src/core/configurators/slash/antigravity.ts +28 -0
  229. package/references/openspec/src/core/configurators/slash/auggie.ts +37 -0
  230. package/references/openspec/src/core/configurators/slash/base.ts +95 -0
  231. package/references/openspec/src/core/configurators/slash/claude.ts +42 -0
  232. package/references/openspec/src/core/configurators/slash/cline.ts +27 -0
  233. package/references/openspec/src/core/configurators/slash/codebuddy.ts +43 -0
  234. package/references/openspec/src/core/configurators/slash/codex.ts +126 -0
  235. package/references/openspec/src/core/configurators/slash/costrict.ts +36 -0
  236. package/references/openspec/src/core/configurators/slash/crush.ts +42 -0
  237. package/references/openspec/src/core/configurators/slash/cursor.ts +42 -0
  238. package/references/openspec/src/core/configurators/slash/factory.ts +41 -0
  239. package/references/openspec/src/core/configurators/slash/gemini.ts +27 -0
  240. package/references/openspec/src/core/configurators/slash/github-copilot.ts +39 -0
  241. package/references/openspec/src/core/configurators/slash/iflow.ts +42 -0
  242. package/references/openspec/src/core/configurators/slash/kilocode.ts +21 -0
  243. package/references/openspec/src/core/configurators/slash/opencode.ts +83 -0
  244. package/references/openspec/src/core/configurators/slash/qoder.ts +84 -0
  245. package/references/openspec/src/core/configurators/slash/qwen.ts +55 -0
  246. package/references/openspec/src/core/configurators/slash/registry.ts +81 -0
  247. package/references/openspec/src/core/configurators/slash/roocode.ts +27 -0
  248. package/references/openspec/src/core/configurators/slash/toml-base.ts +66 -0
  249. package/references/openspec/src/core/configurators/slash/windsurf.ts +27 -0
  250. package/references/openspec/src/core/converters/json-converter.ts +61 -0
  251. package/references/openspec/src/core/index.ts +2 -0
  252. package/references/openspec/src/core/init.ts +986 -0
  253. package/references/openspec/src/core/list.ts +104 -0
  254. package/references/openspec/src/core/parsers/change-parser.ts +234 -0
  255. package/references/openspec/src/core/parsers/markdown-parser.ts +237 -0
  256. package/references/openspec/src/core/parsers/requirement-blocks.ts +234 -0
  257. package/references/openspec/src/core/schemas/base.schema.ts +20 -0
  258. package/references/openspec/src/core/schemas/change.schema.ts +42 -0
  259. package/references/openspec/src/core/schemas/index.ts +20 -0
  260. package/references/openspec/src/core/schemas/spec.schema.ts +17 -0
  261. package/references/openspec/src/core/styles/palette.ts +8 -0
  262. package/references/openspec/src/core/templates/agents-root-stub.ts +16 -0
  263. package/references/openspec/src/core/templates/agents-template.ts +457 -0
  264. package/references/openspec/src/core/templates/claude-template.ts +1 -0
  265. package/references/openspec/src/core/templates/cline-template.ts +1 -0
  266. package/references/openspec/src/core/templates/costrict-template.ts +1 -0
  267. package/references/openspec/src/core/templates/index.ts +50 -0
  268. package/references/openspec/src/core/templates/project-template.ts +38 -0
  269. package/references/openspec/src/core/templates/slash-command-templates.ts +60 -0
  270. package/references/openspec/src/core/update.ts +129 -0
  271. package/references/openspec/src/core/validation/constants.ts +48 -0
  272. package/references/openspec/src/core/validation/types.ts +19 -0
  273. package/references/openspec/src/core/validation/validator.ts +448 -0
  274. package/references/openspec/src/core/view.ts +189 -0
  275. package/references/openspec/src/index.ts +2 -0
  276. package/references/openspec/src/utils/file-system.ts +187 -0
  277. package/references/openspec/src/utils/index.ts +2 -0
  278. package/references/openspec/src/utils/interactive.ts +7 -0
  279. package/references/openspec/src/utils/item-discovery.ts +45 -0
  280. package/references/openspec/src/utils/match.ts +26 -0
  281. package/references/openspec/src/utils/task-progress.ts +43 -0
  282. package/references/openspec/test/cli-e2e/basic.test.ts +156 -0
  283. package/references/openspec/test/commands/change.interactive-show.test.ts +45 -0
  284. package/references/openspec/test/commands/change.interactive-validate.test.ts +48 -0
  285. package/references/openspec/test/commands/show.test.ts +123 -0
  286. package/references/openspec/test/commands/spec.interactive-show.test.ts +44 -0
  287. package/references/openspec/test/commands/spec.interactive-validate.test.ts +44 -0
  288. package/references/openspec/test/commands/spec.test.ts +324 -0
  289. package/references/openspec/test/commands/validate.enriched-output.test.ts +49 -0
  290. package/references/openspec/test/commands/validate.test.ts +133 -0
  291. package/references/openspec/test/core/archive.test.ts +680 -0
  292. package/references/openspec/test/core/commands/change-command.list.test.ts +76 -0
  293. package/references/openspec/test/core/commands/change-command.show-validate.test.ts +111 -0
  294. package/references/openspec/test/core/converters/json-converter.test.ts +184 -0
  295. package/references/openspec/test/core/init.test.ts +1710 -0
  296. package/references/openspec/test/core/list.test.ts +165 -0
  297. package/references/openspec/test/core/parsers/change-parser.test.ts +52 -0
  298. package/references/openspec/test/core/parsers/markdown-parser.test.ts +291 -0
  299. package/references/openspec/test/core/update.test.ts +1642 -0
  300. package/references/openspec/test/core/validation.enriched-messages.test.ts +74 -0
  301. package/references/openspec/test/core/validation.test.ts +489 -0
  302. package/references/openspec/test/core/view.test.ts +79 -0
  303. package/references/openspec/test/fixtures/tmp-init/openspec/changes/c1/proposal.md +7 -0
  304. package/references/openspec/test/fixtures/tmp-init/openspec/changes/c1/specs/alpha/spec.md +8 -0
  305. package/references/openspec/test/fixtures/tmp-init/openspec/specs/alpha/spec.md +12 -0
  306. package/references/openspec/test/helpers/run-cli.ts +139 -0
  307. package/references/openspec/test/utils/file-system.test.ts +211 -0
  308. package/references/openspec/test/utils/marker-updates.test.ts +287 -0
  309. package/references/openspec/tsconfig.json +21 -0
  310. package/references/openspec/vitest.config.ts +25 -0
  311. package/references/openspec/vitest.setup.ts +6 -0
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { execSync } from 'child_process';
5
+
6
+ describe('change show (interactive behavior)', () => {
7
+ const projectRoot = process.cwd();
8
+ const testDir = path.join(projectRoot, 'test-change-show-tmp');
9
+ const changesDir = path.join(testDir, 'openspec', 'changes');
10
+ const bin = path.join(projectRoot, 'bin', 'openspec.js');
11
+
12
+
13
+ beforeEach(async () => {
14
+ await fs.mkdir(changesDir, { recursive: true });
15
+ const content = `# Change: Demo\n\n## Why\n\n## What Changes\n- x`;
16
+ await fs.mkdir(path.join(changesDir, 'demo'), { recursive: true });
17
+ await fs.writeFile(path.join(changesDir, 'demo', 'proposal.md'), content, 'utf-8');
18
+ });
19
+
20
+ afterEach(async () => {
21
+ await fs.rm(testDir, { recursive: true, force: true });
22
+ });
23
+
24
+ it('prints list hint and exits non-zero when no arg and non-interactive', () => {
25
+ const originalCwd = process.cwd();
26
+ const originalEnv = { ...process.env };
27
+ try {
28
+ process.chdir(testDir);
29
+ process.env.OPEN_SPEC_INTERACTIVE = '0';
30
+ let err: any;
31
+ try {
32
+ execSync(`node ${bin} change show`, { encoding: 'utf-8' });
33
+ } catch (e) { err = e; }
34
+ expect(err).toBeDefined();
35
+ expect(err.status).not.toBe(0);
36
+ expect(err.stderr.toString()).toContain('Available IDs:');
37
+ expect(err.stderr.toString()).toContain('openspec change list');
38
+ } finally {
39
+ process.chdir(originalCwd);
40
+ process.env = originalEnv;
41
+ }
42
+ });
43
+ });
44
+
45
+
@@ -0,0 +1,48 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { execSync } from 'child_process';
5
+
6
+ // Note: We cannot truly simulate TTY prompts in this test runner easily.
7
+ // Instead, we verify non-interactive fallback behavior and basic invocation.
8
+
9
+ describe('change validate (interactive behavior)', () => {
10
+ const projectRoot = process.cwd();
11
+ const testDir = path.join(projectRoot, 'test-change-validate-tmp');
12
+ const changesDir = path.join(testDir, 'openspec', 'changes');
13
+ const bin = path.join(projectRoot, 'bin', 'openspec.js');
14
+
15
+
16
+ beforeEach(async () => {
17
+ await fs.mkdir(changesDir, { recursive: true });
18
+ const content = `# Change: Demo\n\n## Why\nBecause reasons that are sufficiently long.\n\n## What Changes\n- **spec-x:** Add something`;
19
+ await fs.mkdir(path.join(changesDir, 'demo'), { recursive: true });
20
+ await fs.writeFile(path.join(changesDir, 'demo', 'proposal.md'), content, 'utf-8');
21
+ });
22
+
23
+ afterEach(async () => {
24
+ await fs.rm(testDir, { recursive: true, force: true });
25
+ });
26
+
27
+ it('prints list hint and exits non-zero when no arg and non-interactive', () => {
28
+ const originalCwd = process.cwd();
29
+ const originalEnv = { ...process.env };
30
+ try {
31
+ process.chdir(testDir);
32
+ process.env.OPEN_SPEC_INTERACTIVE = '0';
33
+ let err: any;
34
+ try {
35
+ execSync(`node ${bin} change validate`, { encoding: 'utf-8' });
36
+ } catch (e) { err = e; }
37
+ expect(err).toBeDefined();
38
+ expect(err.status).not.toBe(0);
39
+ expect(err.stderr.toString()).toContain('Available IDs:');
40
+ expect(err.stderr.toString()).toContain('openspec change list');
41
+ } finally {
42
+ process.chdir(originalCwd);
43
+ process.env = originalEnv;
44
+ }
45
+ });
46
+ });
47
+
48
+
@@ -0,0 +1,123 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { execSync } from 'child_process';
5
+
6
+ describe('top-level show command', () => {
7
+ const projectRoot = process.cwd();
8
+ const testDir = path.join(projectRoot, 'test-show-command-tmp');
9
+ const changesDir = path.join(testDir, 'openspec', 'changes');
10
+ const specsDir = path.join(testDir, 'openspec', 'specs');
11
+ const openspecBin = path.join(projectRoot, 'bin', 'openspec.js');
12
+
13
+
14
+ beforeEach(async () => {
15
+ await fs.mkdir(changesDir, { recursive: true });
16
+ await fs.mkdir(specsDir, { recursive: true });
17
+
18
+ const changeContent = `# Change: Demo\n\n## Why\nBecause reasons.\n\n## What Changes\n- **auth:** Add requirement\n`;
19
+ await fs.mkdir(path.join(changesDir, 'demo'), { recursive: true });
20
+ await fs.writeFile(path.join(changesDir, 'demo', 'proposal.md'), changeContent, 'utf-8');
21
+
22
+ const specContent = `## Purpose\nAuth spec.\n\n## Requirements\n\n### Requirement: User Authentication\nText\n`;
23
+ await fs.mkdir(path.join(specsDir, 'auth'), { recursive: true });
24
+ await fs.writeFile(path.join(specsDir, 'auth', 'spec.md'), specContent, 'utf-8');
25
+ });
26
+
27
+ afterEach(async () => {
28
+ await fs.rm(testDir, { recursive: true, force: true });
29
+ });
30
+
31
+ it('prints hint and non-zero exit when no args and non-interactive', () => {
32
+ const originalCwd = process.cwd();
33
+ const originalEnv = { ...process.env };
34
+ try {
35
+ process.chdir(testDir);
36
+ process.env.OPEN_SPEC_INTERACTIVE = '0';
37
+ let err: any;
38
+ try {
39
+ execSync(`node ${openspecBin} show`, { encoding: 'utf-8' });
40
+ } catch (e) { err = e; }
41
+ expect(err).toBeDefined();
42
+ expect(err.status).not.toBe(0);
43
+ const stderr = err.stderr.toString();
44
+ expect(stderr).toContain('Nothing to show.');
45
+ expect(stderr).toContain('openspec show <item>');
46
+ expect(stderr).toContain('openspec change show');
47
+ expect(stderr).toContain('openspec spec show');
48
+ } finally {
49
+ process.chdir(originalCwd);
50
+ process.env = originalEnv;
51
+ }
52
+ });
53
+
54
+ it('auto-detects change id and supports --json', () => {
55
+ const originalCwd = process.cwd();
56
+ try {
57
+ process.chdir(testDir);
58
+ const output = execSync(`node ${openspecBin} show demo --json`, { encoding: 'utf-8' });
59
+ const json = JSON.parse(output);
60
+ expect(json.id).toBe('demo');
61
+ expect(Array.isArray(json.deltas)).toBe(true);
62
+ } finally {
63
+ process.chdir(originalCwd);
64
+ }
65
+ });
66
+
67
+ it('auto-detects spec id and supports spec-only flags', () => {
68
+ const originalCwd = process.cwd();
69
+ try {
70
+ process.chdir(testDir);
71
+ const output = execSync(`node ${openspecBin} show auth --json --requirements`, { encoding: 'utf-8' });
72
+ const json = JSON.parse(output);
73
+ expect(json.id).toBe('auth');
74
+ expect(Array.isArray(json.requirements)).toBe(true);
75
+ } finally {
76
+ process.chdir(originalCwd);
77
+ }
78
+ });
79
+
80
+ it('handles ambiguity and suggests --type', async () => {
81
+ // create matching spec and change named 'foo'
82
+ await fs.mkdir(path.join(changesDir, 'foo'), { recursive: true });
83
+ await fs.writeFile(path.join(changesDir, 'foo', 'proposal.md'), '# Change: Foo\n\n## Why\n\n## What Changes\n', 'utf-8');
84
+ await fs.mkdir(path.join(specsDir, 'foo'), { recursive: true });
85
+ await fs.writeFile(path.join(specsDir, 'foo', 'spec.md'), '## Purpose\n\n## Requirements\n\n### Requirement: R\nX', 'utf-8');
86
+
87
+ const originalCwd = process.cwd();
88
+ try {
89
+ process.chdir(testDir);
90
+ let err: any;
91
+ try {
92
+ execSync(`node ${openspecBin} show foo`, { encoding: 'utf-8' });
93
+ } catch (e) { err = e; }
94
+ expect(err).toBeDefined();
95
+ expect(err.status).not.toBe(0);
96
+ const stderr = err.stderr.toString();
97
+ expect(stderr).toContain('Ambiguous item');
98
+ expect(stderr).toContain('--type change|spec');
99
+ } finally {
100
+ process.chdir(originalCwd);
101
+ }
102
+ });
103
+
104
+ it('prints nearest matches when not found', () => {
105
+ const originalCwd = process.cwd();
106
+ try {
107
+ process.chdir(testDir);
108
+ let err: any;
109
+ try {
110
+ execSync(`node ${openspecBin} show unknown-item`, { encoding: 'utf-8' });
111
+ } catch (e) { err = e; }
112
+ expect(err).toBeDefined();
113
+ expect(err.status).not.toBe(0);
114
+ const stderr = err.stderr.toString();
115
+ expect(stderr).toContain("Unknown item 'unknown-item'");
116
+ expect(stderr).toContain('Did you mean:');
117
+ } finally {
118
+ process.chdir(originalCwd);
119
+ }
120
+ });
121
+ });
122
+
123
+
@@ -0,0 +1,44 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { execSync } from 'child_process';
5
+
6
+ describe('spec show (interactive behavior)', () => {
7
+ const projectRoot = process.cwd();
8
+ const testDir = path.join(projectRoot, 'test-spec-show-tmp');
9
+ const specsDir = path.join(testDir, 'openspec', 'specs');
10
+ const bin = path.join(projectRoot, 'bin', 'openspec.js');
11
+
12
+
13
+ beforeEach(async () => {
14
+ await fs.mkdir(specsDir, { recursive: true });
15
+ const content = `## Purpose\nX\n\n## Requirements\n\n### Requirement: R\nText`;
16
+ await fs.mkdir(path.join(specsDir, 's1'), { recursive: true });
17
+ await fs.writeFile(path.join(specsDir, 's1', 'spec.md'), content, 'utf-8');
18
+ });
19
+
20
+ afterEach(async () => {
21
+ await fs.rm(testDir, { recursive: true, force: true });
22
+ });
23
+
24
+ it('errors when no arg and non-interactive', () => {
25
+ const originalCwd = process.cwd();
26
+ const originalEnv = { ...process.env };
27
+ try {
28
+ process.chdir(testDir);
29
+ process.env.OPEN_SPEC_INTERACTIVE = '0';
30
+ let err: any;
31
+ try {
32
+ execSync(`node ${bin} spec show`, { encoding: 'utf-8' });
33
+ } catch (e) { err = e; }
34
+ expect(err).toBeDefined();
35
+ expect(err.status).not.toBe(0);
36
+ expect(err.stderr.toString()).toContain('Missing required argument <spec-id>');
37
+ } finally {
38
+ process.chdir(originalCwd);
39
+ process.env = originalEnv;
40
+ }
41
+ });
42
+ });
43
+
44
+
@@ -0,0 +1,44 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { execSync } from 'child_process';
5
+
6
+ describe('spec validate (interactive behavior)', () => {
7
+ const projectRoot = process.cwd();
8
+ const testDir = path.join(projectRoot, 'test-spec-validate-tmp');
9
+ const specsDir = path.join(testDir, 'openspec', 'specs');
10
+ const bin = path.join(projectRoot, 'bin', 'openspec.js');
11
+
12
+
13
+ beforeEach(async () => {
14
+ await fs.mkdir(specsDir, { recursive: true });
15
+ const content = `## Purpose\nValid spec for interactive test.\n\n## Requirements\n\n### Requirement: X\nText`;
16
+ await fs.mkdir(path.join(specsDir, 's1'), { recursive: true });
17
+ await fs.writeFile(path.join(specsDir, 's1', 'spec.md'), content, 'utf-8');
18
+ });
19
+
20
+ afterEach(async () => {
21
+ await fs.rm(testDir, { recursive: true, force: true });
22
+ });
23
+
24
+ it('errors when no arg and non-interactive', () => {
25
+ const originalCwd = process.cwd();
26
+ const originalEnv = { ...process.env };
27
+ try {
28
+ process.chdir(testDir);
29
+ process.env.OPEN_SPEC_INTERACTIVE = '0';
30
+ let err: any;
31
+ try {
32
+ execSync(`node ${bin} spec validate`, { encoding: 'utf-8' });
33
+ } catch (e) { err = e; }
34
+ expect(err).toBeDefined();
35
+ expect(err.status).not.toBe(0);
36
+ expect(err.stderr.toString()).toContain('Missing required argument <spec-id>');
37
+ } finally {
38
+ process.chdir(originalCwd);
39
+ process.env = originalEnv;
40
+ }
41
+ });
42
+ });
43
+
44
+
@@ -0,0 +1,324 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { execSync } from 'child_process';
5
+
6
+ describe('spec command', () => {
7
+ const projectRoot = process.cwd();
8
+ const testDir = path.join(projectRoot, 'test-spec-command-tmp');
9
+ const specsDir = path.join(testDir, 'openspec', 'specs');
10
+ const openspecBin = path.join(projectRoot, 'bin', 'openspec.js');
11
+
12
+
13
+ beforeEach(async () => {
14
+ await fs.mkdir(specsDir, { recursive: true });
15
+
16
+ // Create test spec files
17
+ const testSpec = `## Purpose
18
+ This is a test specification for the authentication system.
19
+
20
+ ## Requirements
21
+
22
+ ### Requirement: User Authentication
23
+ The system SHALL provide secure user authentication
24
+
25
+ #### Scenario: Successful login
26
+ - **GIVEN** a user with valid credentials
27
+ - **WHEN** they submit the login form
28
+ - **THEN** they are authenticated
29
+
30
+ ### Requirement: Password Reset
31
+ The system SHALL allow users to reset their password
32
+
33
+ #### Scenario: Reset via email
34
+ - **GIVEN** a user with a registered email
35
+ - **WHEN** they request a password reset
36
+ - **THEN** they receive a reset link`;
37
+
38
+ await fs.mkdir(path.join(specsDir, 'auth'), { recursive: true });
39
+ await fs.writeFile(path.join(specsDir, 'auth', 'spec.md'), testSpec);
40
+
41
+ const testSpec2 = `## Purpose
42
+ This specification defines the payment processing system.
43
+
44
+ ## Requirements
45
+
46
+ ### Requirement: Process Payments
47
+ The system SHALL process credit card payments securely`;
48
+
49
+ await fs.mkdir(path.join(specsDir, 'payment'), { recursive: true });
50
+ await fs.writeFile(path.join(specsDir, 'payment', 'spec.md'), testSpec2);
51
+ });
52
+
53
+ afterEach(async () => {
54
+ await fs.rm(testDir, { recursive: true, force: true });
55
+ });
56
+
57
+ describe('spec show', () => {
58
+ it('should display spec in text format', () => {
59
+ const originalCwd = process.cwd();
60
+ try {
61
+ process.chdir(testDir);
62
+ const output = execSync(`node ${openspecBin} spec show auth`, {
63
+ encoding: 'utf-8'
64
+ });
65
+
66
+ // Raw passthrough should match spec.md content
67
+ const raw = execSync(`cat ${path.join(specsDir, 'auth', 'spec.md')}`, { encoding: 'utf-8' });
68
+ expect(output.trim()).toBe(raw.trim());
69
+ } finally {
70
+ process.chdir(originalCwd);
71
+ }
72
+ });
73
+
74
+ it('should output spec as JSON with --json flag', () => {
75
+ const originalCwd = process.cwd();
76
+ try {
77
+ process.chdir(testDir);
78
+ const output = execSync(`node ${openspecBin} spec show auth --json`, {
79
+ encoding: 'utf-8'
80
+ });
81
+
82
+ const json = JSON.parse(output);
83
+ expect(json.id).toBe('auth');
84
+ expect(json.title).toBe('auth');
85
+ expect(json.overview).toContain('test specification');
86
+ expect(json.requirements).toHaveLength(2);
87
+ expect(json.metadata.format).toBe('openspec');
88
+ } finally {
89
+ process.chdir(originalCwd);
90
+ }
91
+ });
92
+
93
+ it('should filter to show only requirements with --requirements flag (JSON only)', () => {
94
+ const originalCwd = process.cwd();
95
+ try {
96
+ process.chdir(testDir);
97
+ const output = execSync(`node ${openspecBin} spec show auth --json --requirements`, {
98
+ encoding: 'utf-8'
99
+ });
100
+
101
+ const json = JSON.parse(output);
102
+ expect(json.requirements).toHaveLength(2);
103
+ // Scenarios should be excluded when --requirements is used
104
+ expect(json.requirements.every((r: any) => Array.isArray(r.scenarios) && r.scenarios.length === 0)).toBe(true);
105
+ } finally {
106
+ process.chdir(originalCwd);
107
+ }
108
+ });
109
+
110
+ it('should exclude scenarios with --no-scenarios flag (JSON only)', () => {
111
+ const originalCwd = process.cwd();
112
+ try {
113
+ process.chdir(testDir);
114
+ const output = execSync(`node ${openspecBin} spec show auth --json --no-scenarios`, {
115
+ encoding: 'utf-8'
116
+ });
117
+
118
+ const json = JSON.parse(output);
119
+ expect(json.requirements).toHaveLength(2);
120
+ expect(json.requirements.every((r: any) => Array.isArray(r.scenarios) && r.scenarios.length === 0)).toBe(true);
121
+ } finally {
122
+ process.chdir(originalCwd);
123
+ }
124
+ });
125
+
126
+ it('should show specific requirement with -r flag (JSON only)', () => {
127
+ const originalCwd = process.cwd();
128
+ try {
129
+ process.chdir(testDir);
130
+ const output = execSync(`node ${openspecBin} spec show auth --json -r 1`, {
131
+ encoding: 'utf-8'
132
+ });
133
+
134
+ const json = JSON.parse(output);
135
+ expect(json.requirements).toHaveLength(1);
136
+ expect(json.requirements[0].text).toContain('The system SHALL provide secure user authentication');
137
+ } finally {
138
+ process.chdir(originalCwd);
139
+ }
140
+ });
141
+
142
+ it('should return JSON with filtered requirements', () => {
143
+ const originalCwd = process.cwd();
144
+ try {
145
+ process.chdir(testDir);
146
+ const output = execSync(`node ${openspecBin} spec show auth --json --no-scenarios`, {
147
+ encoding: 'utf-8'
148
+ });
149
+
150
+ const json = JSON.parse(output);
151
+ expect(json.requirements).toHaveLength(2);
152
+ expect(json.requirements[0].scenarios).toHaveLength(0);
153
+ } finally {
154
+ process.chdir(originalCwd);
155
+ }
156
+ });
157
+ });
158
+
159
+ describe('spec list', () => {
160
+ it('should list all available specs (IDs only by default)', () => {
161
+ const originalCwd = process.cwd();
162
+ try {
163
+ process.chdir(testDir);
164
+ const output = execSync(`node ${openspecBin} spec list`, {
165
+ encoding: 'utf-8'
166
+ });
167
+
168
+ expect(output).toContain('auth');
169
+ expect(output).toContain('payment');
170
+ // Default should not include counts or teasers
171
+ expect(output).not.toMatch(/Requirements:\s*\d+/);
172
+ } finally {
173
+ process.chdir(originalCwd);
174
+ }
175
+ });
176
+
177
+ it('should output spec list as JSON with --json flag', () => {
178
+ const originalCwd = process.cwd();
179
+ try {
180
+ process.chdir(testDir);
181
+ const output = execSync(`node ${openspecBin} spec list --json`, {
182
+ encoding: 'utf-8'
183
+ });
184
+
185
+ const json = JSON.parse(output);
186
+ expect(json).toHaveLength(2);
187
+ expect(json.find((s: any) => s.id === 'auth')).toBeDefined();
188
+ expect(json.find((s: any) => s.id === 'payment')).toBeDefined();
189
+ expect(json[0].requirementCount).toBeDefined();
190
+ } finally {
191
+ process.chdir(originalCwd);
192
+ }
193
+ });
194
+ });
195
+
196
+ describe('spec validate', () => {
197
+ it('should validate a valid spec', () => {
198
+ const originalCwd = process.cwd();
199
+ try {
200
+ process.chdir(testDir);
201
+ const output = execSync(`node ${openspecBin} spec validate auth`, {
202
+ encoding: 'utf-8'
203
+ });
204
+
205
+ expect(output).toContain("Specification 'auth' is valid");
206
+ } finally {
207
+ process.chdir(originalCwd);
208
+ }
209
+ });
210
+
211
+ it('should output validation report as JSON with --json flag', () => {
212
+ const originalCwd = process.cwd();
213
+ try {
214
+ process.chdir(testDir);
215
+ const output = execSync(`node ${openspecBin} spec validate auth --json`, {
216
+ encoding: 'utf-8'
217
+ });
218
+
219
+ const json = JSON.parse(output);
220
+ expect(json.valid).toBeDefined();
221
+ expect(json.issues).toBeDefined();
222
+ expect(json.summary).toBeDefined();
223
+ expect(json.summary.errors).toBeDefined();
224
+ expect(json.summary.warnings).toBeDefined();
225
+ } finally {
226
+ process.chdir(originalCwd);
227
+ }
228
+ });
229
+
230
+ it('should validate with strict mode', () => {
231
+ const originalCwd = process.cwd();
232
+ try {
233
+ process.chdir(testDir);
234
+ const output = execSync(`node ${openspecBin} spec validate auth --strict --json`, {
235
+ encoding: 'utf-8'
236
+ });
237
+
238
+ const json = JSON.parse(output);
239
+ expect(json.valid).toBeDefined();
240
+ // In strict mode, warnings also affect validity
241
+ } finally {
242
+ process.chdir(originalCwd);
243
+ }
244
+ });
245
+
246
+ it('should detect invalid spec structure', async () => {
247
+ const invalidSpec = `## Purpose
248
+
249
+ ## Requirements
250
+ This section has no actual requirements`;
251
+
252
+ await fs.mkdir(path.join(specsDir, 'invalid'), { recursive: true });
253
+ await fs.writeFile(path.join(specsDir, 'invalid', 'spec.md'), invalidSpec);
254
+
255
+ const originalCwd = process.cwd();
256
+ try {
257
+ process.chdir(testDir);
258
+
259
+ // This should exit with non-zero code
260
+ let exitCode = 0;
261
+ try {
262
+ execSync(`node ${openspecBin} spec validate invalid`, {
263
+ encoding: 'utf-8'
264
+ });
265
+ } catch (error: any) {
266
+ exitCode = error.status;
267
+ }
268
+
269
+ expect(exitCode).not.toBe(0);
270
+ } finally {
271
+ process.chdir(originalCwd);
272
+ }
273
+ });
274
+ });
275
+
276
+ describe('error handling', () => {
277
+ it('should handle non-existent spec gracefully', () => {
278
+ const originalCwd = process.cwd();
279
+ try {
280
+ process.chdir(testDir);
281
+
282
+ let error: any;
283
+ try {
284
+ execSync(`node ${openspecBin} spec show nonexistent`, {
285
+ encoding: 'utf-8'
286
+ });
287
+ } catch (e) {
288
+ error = e;
289
+ }
290
+
291
+ expect(error).toBeDefined();
292
+ expect(error.status).not.toBe(0);
293
+ expect(error.stderr.toString()).toContain('not found');
294
+ } finally {
295
+ process.chdir(originalCwd);
296
+ }
297
+ });
298
+
299
+ it('should handle missing specs directory gracefully', async () => {
300
+ await fs.rm(specsDir, { recursive: true, force: true });
301
+ const originalCwd = process.cwd();
302
+ try {
303
+ process.chdir(testDir);
304
+ const output = execSync(`node ${openspecBin} spec list`, { encoding: 'utf-8' });
305
+ expect(output.trim()).toBe('No items found');
306
+ } finally {
307
+ process.chdir(originalCwd);
308
+ }
309
+ });
310
+
311
+ it('should honor --no-color (no ANSI escapes)', () => {
312
+ const originalCwd = process.cwd();
313
+ try {
314
+ process.chdir(testDir);
315
+ const output = execSync(`node ${openspecBin} --no-color spec list --long`, { encoding: 'utf-8' });
316
+ // Basic ANSI escape pattern
317
+ const hasAnsi = /\u001b\[[0-9;]*m/.test(output);
318
+ expect(hasAnsi).toBe(false);
319
+ } finally {
320
+ process.chdir(originalCwd);
321
+ }
322
+ });
323
+ });
324
+ });
@@ -0,0 +1,49 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { execSync } from 'child_process';
5
+
6
+ describe('validate command enriched human output', () => {
7
+ const projectRoot = process.cwd();
8
+ const testDir = path.join(projectRoot, 'test-validate-enriched-tmp');
9
+ const changesDir = path.join(testDir, 'openspec', 'changes');
10
+ const bin = path.join(projectRoot, 'bin', 'openspec.js');
11
+
12
+
13
+ beforeEach(async () => {
14
+ await fs.mkdir(changesDir, { recursive: true });
15
+ });
16
+
17
+ afterEach(async () => {
18
+ await fs.rm(testDir, { recursive: true, force: true });
19
+ });
20
+
21
+ it('prints Next steps footer and guidance on invalid change', () => {
22
+ const changeContent = `# Test Change\n\n## Why\nThis is a sufficiently long explanation to pass the why length requirement for validation purposes.\n\n## What Changes\nThere are changes proposed, but no delta specs provided yet.`;
23
+ const changeId = 'c-next-steps';
24
+ const changePath = path.join(changesDir, changeId);
25
+ execSync(`mkdir -p ${changePath}`);
26
+ execSync(`bash -lc "cat > ${path.join(changePath, 'proposal.md')} <<'EOF'\n${changeContent}\nEOF"`);
27
+
28
+ const originalCwd = process.cwd();
29
+ try {
30
+ process.chdir(testDir);
31
+ let code = 0;
32
+ let stderr = '';
33
+ try {
34
+ execSync(`node ${bin} change validate ${changeId}`, { encoding: 'utf-8', stdio: 'pipe' });
35
+ } catch (e: any) {
36
+ code = e?.status ?? 1;
37
+ stderr = e?.stderr?.toString?.() ?? '';
38
+ }
39
+ expect(code).not.toBe(0);
40
+ expect(stderr).toContain('has issues');
41
+ expect(stderr).toContain('Next steps:');
42
+ expect(stderr).toContain('openspec change show');
43
+ } finally {
44
+ process.chdir(originalCwd);
45
+ }
46
+ });
47
+ });
48
+
49
+