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,606 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import { select, confirm } from '@inquirer/prompts';
4
+ import { FileSystemUtils } from '../utils/file-system.js';
5
+ import { getTaskProgressForChange, formatTaskStatus } from '../utils/task-progress.js';
6
+ import { Validator } from './validation/validator.js';
7
+ import chalk from 'chalk';
8
+ import {
9
+ extractRequirementsSection,
10
+ parseDeltaSpec,
11
+ normalizeRequirementName,
12
+ type RequirementBlock,
13
+ } from './parsers/requirement-blocks.js';
14
+
15
+ interface SpecUpdate {
16
+ source: string;
17
+ target: string;
18
+ exists: boolean;
19
+ }
20
+
21
+ export class ArchiveCommand {
22
+ async execute(
23
+ changeName?: string,
24
+ options: { yes?: boolean; skipSpecs?: boolean; noValidate?: boolean; validate?: boolean } = {}
25
+ ): Promise<void> {
26
+ const targetPath = '.';
27
+ const changesDir = path.join(targetPath, 'openspec', 'changes');
28
+ const archiveDir = path.join(changesDir, 'archive');
29
+ const mainSpecsDir = path.join(targetPath, 'openspec', 'specs');
30
+
31
+ // Check if changes directory exists
32
+ try {
33
+ await fs.access(changesDir);
34
+ } catch {
35
+ throw new Error("No OpenSpec changes directory found. Run 'openspec init' first.");
36
+ }
37
+
38
+ // Get change name interactively if not provided
39
+ if (!changeName) {
40
+ const selectedChange = await this.selectChange(changesDir);
41
+ if (!selectedChange) {
42
+ console.log('No change selected. Aborting.');
43
+ return;
44
+ }
45
+ changeName = selectedChange;
46
+ }
47
+
48
+ const changeDir = path.join(changesDir, changeName);
49
+
50
+ // Verify change exists
51
+ try {
52
+ const stat = await fs.stat(changeDir);
53
+ if (!stat.isDirectory()) {
54
+ throw new Error(`Change '${changeName}' not found.`);
55
+ }
56
+ } catch {
57
+ throw new Error(`Change '${changeName}' not found.`);
58
+ }
59
+
60
+ const skipValidation = options.validate === false || options.noValidate === true;
61
+
62
+ // Validate specs and change before archiving
63
+ if (!skipValidation) {
64
+ const validator = new Validator();
65
+ let hasValidationErrors = false;
66
+
67
+ // Validate proposal.md (non-blocking unless strict mode desired in future)
68
+ const changeFile = path.join(changeDir, 'proposal.md');
69
+ try {
70
+ await fs.access(changeFile);
71
+ const changeReport = await validator.validateChange(changeFile);
72
+ // Proposal validation is informative only (do not block archive)
73
+ if (!changeReport.valid) {
74
+ console.log(chalk.yellow(`\nProposal warnings in proposal.md (non-blocking):`));
75
+ for (const issue of changeReport.issues) {
76
+ const symbol = issue.level === 'ERROR' ? '⚠' : (issue.level === 'WARNING' ? '⚠' : 'ℹ');
77
+ console.log(chalk.yellow(` ${symbol} ${issue.message}`));
78
+ }
79
+ }
80
+ } catch {
81
+ // Change file doesn't exist, skip validation
82
+ }
83
+
84
+ // Validate delta-formatted spec files under the change directory if present
85
+ const changeSpecsDir = path.join(changeDir, 'specs');
86
+ let hasDeltaSpecs = false;
87
+ try {
88
+ const candidates = await fs.readdir(changeSpecsDir, { withFileTypes: true });
89
+ for (const c of candidates) {
90
+ if (c.isDirectory()) {
91
+ try {
92
+ const candidatePath = path.join(changeSpecsDir, c.name, 'spec.md');
93
+ await fs.access(candidatePath);
94
+ const content = await fs.readFile(candidatePath, 'utf-8');
95
+ if (/^##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements/m.test(content)) {
96
+ hasDeltaSpecs = true;
97
+ break;
98
+ }
99
+ } catch {}
100
+ }
101
+ }
102
+ } catch {}
103
+ if (hasDeltaSpecs) {
104
+ const deltaReport = await validator.validateChangeDeltaSpecs(changeDir);
105
+ if (!deltaReport.valid) {
106
+ hasValidationErrors = true;
107
+ console.log(chalk.red(`\nValidation errors in change delta specs:`));
108
+ for (const issue of deltaReport.issues) {
109
+ if (issue.level === 'ERROR') {
110
+ console.log(chalk.red(` ✗ ${issue.message}`));
111
+ } else if (issue.level === 'WARNING') {
112
+ console.log(chalk.yellow(` ⚠ ${issue.message}`));
113
+ }
114
+ }
115
+ }
116
+ }
117
+
118
+ if (hasValidationErrors) {
119
+ console.log(chalk.red('\nValidation failed. Please fix the errors before archiving.'));
120
+ console.log(chalk.yellow('To skip validation (not recommended), use --no-validate flag.'));
121
+ return;
122
+ }
123
+ } else {
124
+ // Log warning when validation is skipped
125
+ const timestamp = new Date().toISOString();
126
+
127
+ if (!options.yes) {
128
+ const proceed = await confirm({
129
+ message: chalk.yellow('⚠️ WARNING: Skipping validation may archive invalid specs. Continue? (y/N)'),
130
+ default: false
131
+ });
132
+ if (!proceed) {
133
+ console.log('Archive cancelled.');
134
+ return;
135
+ }
136
+ } else {
137
+ console.log(chalk.yellow(`\n⚠️ WARNING: Skipping validation may archive invalid specs.`));
138
+ }
139
+
140
+ console.log(chalk.yellow(`[${timestamp}] Validation skipped for change: ${changeName}`));
141
+ console.log(chalk.yellow(`Affected files: ${changeDir}`));
142
+ }
143
+
144
+ // Show progress and check for incomplete tasks
145
+ const progress = await getTaskProgressForChange(changesDir, changeName);
146
+ const status = formatTaskStatus(progress);
147
+ console.log(`Task status: ${status}`);
148
+
149
+ const incompleteTasks = Math.max(progress.total - progress.completed, 0);
150
+ if (incompleteTasks > 0) {
151
+ if (!options.yes) {
152
+ const proceed = await confirm({
153
+ message: `Warning: ${incompleteTasks} incomplete task(s) found. Continue?`,
154
+ default: false
155
+ });
156
+ if (!proceed) {
157
+ console.log('Archive cancelled.');
158
+ return;
159
+ }
160
+ } else {
161
+ console.log(`Warning: ${incompleteTasks} incomplete task(s) found. Continuing due to --yes flag.`);
162
+ }
163
+ }
164
+
165
+ // Handle spec updates unless skipSpecs flag is set
166
+ if (options.skipSpecs) {
167
+ console.log('Skipping spec updates (--skip-specs flag provided).');
168
+ } else {
169
+ // Find specs to update
170
+ const specUpdates = await this.findSpecUpdates(changeDir, mainSpecsDir);
171
+
172
+ if (specUpdates.length > 0) {
173
+ console.log('\nSpecs to update:');
174
+ for (const update of specUpdates) {
175
+ const status = update.exists ? 'update' : 'create';
176
+ const capability = path.basename(path.dirname(update.target));
177
+ console.log(` ${capability}: ${status}`);
178
+ }
179
+
180
+ let shouldUpdateSpecs = true;
181
+ if (!options.yes) {
182
+ shouldUpdateSpecs = await confirm({
183
+ message: 'Proceed with spec updates?',
184
+ default: true
185
+ });
186
+ if (!shouldUpdateSpecs) {
187
+ console.log('Skipping spec updates. Proceeding with archive.');
188
+ }
189
+ }
190
+
191
+ if (shouldUpdateSpecs) {
192
+ // Prepare all updates first (validation pass, no writes)
193
+ const prepared: Array<{ update: SpecUpdate; rebuilt: string; counts: { added: number; modified: number; removed: number; renamed: number } }> = [];
194
+ try {
195
+ for (const update of specUpdates) {
196
+ const built = await this.buildUpdatedSpec(update, changeName!);
197
+ prepared.push({ update, rebuilt: built.rebuilt, counts: built.counts });
198
+ }
199
+ } catch (err: any) {
200
+ console.log(String(err.message || err));
201
+ console.log('Aborted. No files were changed.');
202
+ return;
203
+ }
204
+
205
+ // All validations passed; pre-validate rebuilt full spec and then write files and display counts
206
+ let totals = { added: 0, modified: 0, removed: 0, renamed: 0 };
207
+ for (const p of prepared) {
208
+ const specName = path.basename(path.dirname(p.update.target));
209
+ if (!skipValidation) {
210
+ const report = await new Validator().validateSpecContent(specName, p.rebuilt);
211
+ if (!report.valid) {
212
+ console.log(chalk.red(`\nValidation errors in rebuilt spec for ${specName} (will not write changes):`));
213
+ for (const issue of report.issues) {
214
+ if (issue.level === 'ERROR') console.log(chalk.red(` ✗ ${issue.message}`));
215
+ else if (issue.level === 'WARNING') console.log(chalk.yellow(` ⚠ ${issue.message}`));
216
+ }
217
+ console.log('Aborted. No files were changed.');
218
+ return;
219
+ }
220
+ }
221
+ await this.writeUpdatedSpec(p.update, p.rebuilt, p.counts);
222
+ totals.added += p.counts.added;
223
+ totals.modified += p.counts.modified;
224
+ totals.removed += p.counts.removed;
225
+ totals.renamed += p.counts.renamed;
226
+ }
227
+ console.log(
228
+ `Totals: + ${totals.added}, ~ ${totals.modified}, - ${totals.removed}, → ${totals.renamed}`
229
+ );
230
+ console.log('Specs updated successfully.');
231
+ }
232
+ }
233
+ }
234
+
235
+ // Create archive directory with date prefix
236
+ const archiveName = `${this.getArchiveDate()}-${changeName}`;
237
+ const archivePath = path.join(archiveDir, archiveName);
238
+
239
+ // Check if archive already exists
240
+ try {
241
+ await fs.access(archivePath);
242
+ throw new Error(`Archive '${archiveName}' already exists.`);
243
+ } catch (error: any) {
244
+ if (error.code !== 'ENOENT') {
245
+ throw error;
246
+ }
247
+ }
248
+
249
+ // Create archive directory if needed
250
+ await fs.mkdir(archiveDir, { recursive: true });
251
+
252
+ // Move change to archive
253
+ await fs.rename(changeDir, archivePath);
254
+
255
+ console.log(`Change '${changeName}' archived as '${archiveName}'.`);
256
+ }
257
+
258
+ private async selectChange(changesDir: string): Promise<string | null> {
259
+ // Get all directories in changes (excluding archive)
260
+ const entries = await fs.readdir(changesDir, { withFileTypes: true });
261
+ const changeDirs = entries
262
+ .filter(entry => entry.isDirectory() && entry.name !== 'archive')
263
+ .map(entry => entry.name)
264
+ .sort();
265
+
266
+ if (changeDirs.length === 0) {
267
+ console.log('No active changes found.');
268
+ return null;
269
+ }
270
+
271
+ // Build choices with progress inline to avoid duplicate lists
272
+ let choices: Array<{ name: string; value: string }> = changeDirs.map(name => ({ name, value: name }));
273
+ try {
274
+ const progressList: Array<{ id: string; status: string }> = [];
275
+ for (const id of changeDirs) {
276
+ const progress = await getTaskProgressForChange(changesDir, id);
277
+ const status = formatTaskStatus(progress);
278
+ progressList.push({ id, status });
279
+ }
280
+ const nameWidth = Math.max(...progressList.map(p => p.id.length));
281
+ choices = progressList.map(p => ({
282
+ name: `${p.id.padEnd(nameWidth)} ${p.status}`,
283
+ value: p.id
284
+ }));
285
+ } catch {
286
+ // If anything fails, fall back to simple names
287
+ choices = changeDirs.map(name => ({ name, value: name }));
288
+ }
289
+
290
+ try {
291
+ const answer = await select({
292
+ message: 'Select a change to archive',
293
+ choices
294
+ });
295
+ return answer;
296
+ } catch (error) {
297
+ // User cancelled (Ctrl+C)
298
+ return null;
299
+ }
300
+ }
301
+
302
+ // Deprecated: replaced by shared task-progress utilities
303
+ private async checkIncompleteTasks(_tasksPath: string): Promise<number> {
304
+ return 0;
305
+ }
306
+
307
+ private async findSpecUpdates(changeDir: string, mainSpecsDir: string): Promise<SpecUpdate[]> {
308
+ const updates: SpecUpdate[] = [];
309
+ const changeSpecsDir = path.join(changeDir, 'specs');
310
+
311
+ try {
312
+ const entries = await fs.readdir(changeSpecsDir, { withFileTypes: true });
313
+
314
+ for (const entry of entries) {
315
+ if (entry.isDirectory()) {
316
+ const specFile = path.join(changeSpecsDir, entry.name, 'spec.md');
317
+ const targetFile = path.join(mainSpecsDir, entry.name, 'spec.md');
318
+
319
+ try {
320
+ await fs.access(specFile);
321
+
322
+ // Check if target exists
323
+ let exists = false;
324
+ try {
325
+ await fs.access(targetFile);
326
+ exists = true;
327
+ } catch {
328
+ exists = false;
329
+ }
330
+
331
+ updates.push({
332
+ source: specFile,
333
+ target: targetFile,
334
+ exists
335
+ });
336
+ } catch {
337
+ // Source spec doesn't exist, skip
338
+ }
339
+ }
340
+ }
341
+ } catch {
342
+ // No specs directory in change
343
+ }
344
+
345
+ return updates;
346
+ }
347
+
348
+ private async buildUpdatedSpec(update: SpecUpdate, changeName: string): Promise<{ rebuilt: string; counts: { added: number; modified: number; removed: number; renamed: number } }> {
349
+ // Read change spec content (delta-format expected)
350
+ const changeContent = await fs.readFile(update.source, 'utf-8');
351
+
352
+ // Parse deltas from the change spec file
353
+ const plan = parseDeltaSpec(changeContent);
354
+ const specName = path.basename(path.dirname(update.target));
355
+
356
+ // Pre-validate duplicates within sections
357
+ const addedNames = new Set<string>();
358
+ for (const add of plan.added) {
359
+ const name = normalizeRequirementName(add.name);
360
+ if (addedNames.has(name)) {
361
+ throw new Error(
362
+ `${specName} validation failed - duplicate requirement in ADDED for header "### Requirement: ${add.name}"`
363
+ );
364
+ }
365
+ addedNames.add(name);
366
+ }
367
+ const modifiedNames = new Set<string>();
368
+ for (const mod of plan.modified) {
369
+ const name = normalizeRequirementName(mod.name);
370
+ if (modifiedNames.has(name)) {
371
+ throw new Error(
372
+ `${specName} validation failed - duplicate requirement in MODIFIED for header "### Requirement: ${mod.name}"`
373
+ );
374
+ }
375
+ modifiedNames.add(name);
376
+ }
377
+ const removedNamesSet = new Set<string>();
378
+ for (const rem of plan.removed) {
379
+ const name = normalizeRequirementName(rem);
380
+ if (removedNamesSet.has(name)) {
381
+ throw new Error(
382
+ `${specName} validation failed - duplicate requirement in REMOVED for header "### Requirement: ${rem}"`
383
+ );
384
+ }
385
+ removedNamesSet.add(name);
386
+ }
387
+ const renamedFromSet = new Set<string>();
388
+ const renamedToSet = new Set<string>();
389
+ for (const { from, to } of plan.renamed) {
390
+ const fromNorm = normalizeRequirementName(from);
391
+ const toNorm = normalizeRequirementName(to);
392
+ if (renamedFromSet.has(fromNorm)) {
393
+ throw new Error(
394
+ `${specName} validation failed - duplicate FROM in RENAMED for header "### Requirement: ${from}"`
395
+ );
396
+ }
397
+ if (renamedToSet.has(toNorm)) {
398
+ throw new Error(
399
+ `${specName} validation failed - duplicate TO in RENAMED for header "### Requirement: ${to}"`
400
+ );
401
+ }
402
+ renamedFromSet.add(fromNorm);
403
+ renamedToSet.add(toNorm);
404
+ }
405
+
406
+ // Pre-validate cross-section conflicts
407
+ const conflicts: Array<{ name: string; a: string; b: string }> = [];
408
+ for (const n of modifiedNames) {
409
+ if (removedNamesSet.has(n)) conflicts.push({ name: n, a: 'MODIFIED', b: 'REMOVED' });
410
+ if (addedNames.has(n)) conflicts.push({ name: n, a: 'MODIFIED', b: 'ADDED' });
411
+ }
412
+ for (const n of addedNames) {
413
+ if (removedNamesSet.has(n)) conflicts.push({ name: n, a: 'ADDED', b: 'REMOVED' });
414
+ }
415
+ // Renamed interplay: MODIFIED must reference the NEW header, not FROM
416
+ for (const { from, to } of plan.renamed) {
417
+ const fromNorm = normalizeRequirementName(from);
418
+ const toNorm = normalizeRequirementName(to);
419
+ if (modifiedNames.has(fromNorm)) {
420
+ throw new Error(
421
+ `${specName} validation failed - when a rename exists, MODIFIED must reference the NEW header "### Requirement: ${to}"`
422
+ );
423
+ }
424
+ // Detect ADDED colliding with a RENAMED TO
425
+ if (addedNames.has(toNorm)) {
426
+ throw new Error(
427
+ `${specName} validation failed - RENAMED TO header collides with ADDED for "### Requirement: ${to}"`
428
+ );
429
+ }
430
+ }
431
+ if (conflicts.length > 0) {
432
+ const c = conflicts[0];
433
+ throw new Error(
434
+ `${specName} validation failed - requirement present in multiple sections (${c.a} and ${c.b}) for header "### Requirement: ${c.name}"`
435
+ );
436
+ }
437
+ const hasAnyDelta = (plan.added.length + plan.modified.length + plan.removed.length + plan.renamed.length) > 0;
438
+ if (!hasAnyDelta) {
439
+ throw new Error(
440
+ `Delta parsing found no operations for ${path.basename(path.dirname(update.source))}. ` +
441
+ `Provide ADDED/MODIFIED/REMOVED/RENAMED sections in change spec.`
442
+ );
443
+ }
444
+
445
+ // Load or create base target content
446
+ let targetContent: string;
447
+ try {
448
+ targetContent = await fs.readFile(update.target, 'utf-8');
449
+ } catch {
450
+ // Target spec does not exist; only ADDED operations are permitted
451
+ if (plan.modified.length > 0 || plan.removed.length > 0 || plan.renamed.length > 0) {
452
+ throw new Error(
453
+ `${specName}: target spec does not exist; only ADDED requirements are allowed for new specs.`
454
+ );
455
+ }
456
+ targetContent = this.buildSpecSkeleton(specName, changeName);
457
+ }
458
+
459
+ // Extract requirements section and build name->block map
460
+ const parts = extractRequirementsSection(targetContent);
461
+ const nameToBlock = new Map<string, RequirementBlock>();
462
+ for (const block of parts.bodyBlocks) {
463
+ nameToBlock.set(normalizeRequirementName(block.name), block);
464
+ }
465
+
466
+ // Apply operations in order: RENAMED → REMOVED → MODIFIED → ADDED
467
+ // RENAMED
468
+ for (const r of plan.renamed) {
469
+ const from = normalizeRequirementName(r.from);
470
+ const to = normalizeRequirementName(r.to);
471
+ if (!nameToBlock.has(from)) {
472
+ throw new Error(
473
+ `${specName} RENAMED failed for header "### Requirement: ${r.from}" - source not found`
474
+ );
475
+ }
476
+ if (nameToBlock.has(to)) {
477
+ throw new Error(
478
+ `${specName} RENAMED failed for header "### Requirement: ${r.to}" - target already exists`
479
+ );
480
+ }
481
+ const block = nameToBlock.get(from)!;
482
+ const newHeader = `### Requirement: ${to}`;
483
+ const rawLines = block.raw.split('\n');
484
+ rawLines[0] = newHeader;
485
+ const renamedBlock: RequirementBlock = {
486
+ headerLine: newHeader,
487
+ name: to,
488
+ raw: rawLines.join('\n'),
489
+ };
490
+ nameToBlock.delete(from);
491
+ nameToBlock.set(to, renamedBlock);
492
+ }
493
+
494
+ // REMOVED
495
+ for (const name of plan.removed) {
496
+ const key = normalizeRequirementName(name);
497
+ if (!nameToBlock.has(key)) {
498
+ throw new Error(
499
+ `${specName} REMOVED failed for header "### Requirement: ${name}" - not found`
500
+ );
501
+ }
502
+ nameToBlock.delete(key);
503
+ }
504
+
505
+ // MODIFIED
506
+ for (const mod of plan.modified) {
507
+ const key = normalizeRequirementName(mod.name);
508
+ if (!nameToBlock.has(key)) {
509
+ throw new Error(
510
+ `${specName} MODIFIED failed for header "### Requirement: ${mod.name}" - not found`
511
+ );
512
+ }
513
+ // Replace block with provided raw (ensure header line matches key)
514
+ const modHeaderMatch = mod.raw.split('\n')[0].match(/^###\s*Requirement:\s*(.+)\s*$/);
515
+ if (!modHeaderMatch || normalizeRequirementName(modHeaderMatch[1]) !== key) {
516
+ throw new Error(
517
+ `${specName} MODIFIED failed for header "### Requirement: ${mod.name}" - header mismatch in content`
518
+ );
519
+ }
520
+ nameToBlock.set(key, mod);
521
+ }
522
+
523
+ // ADDED
524
+ for (const add of plan.added) {
525
+ const key = normalizeRequirementName(add.name);
526
+ if (nameToBlock.has(key)) {
527
+ throw new Error(
528
+ `${specName} ADDED failed for header "### Requirement: ${add.name}" - already exists`
529
+ );
530
+ }
531
+ nameToBlock.set(key, add);
532
+ }
533
+
534
+ // Duplicates within resulting map are implicitly prevented by key uniqueness.
535
+
536
+ // Recompose requirements section preserving original ordering where possible
537
+ const keptOrder: RequirementBlock[] = [];
538
+ const seen = new Set<string>();
539
+ for (const block of parts.bodyBlocks) {
540
+ const key = normalizeRequirementName(block.name);
541
+ const replacement = nameToBlock.get(key);
542
+ if (replacement) {
543
+ keptOrder.push(replacement);
544
+ seen.add(key);
545
+ }
546
+ }
547
+ // Append any newly added that were not in original order
548
+ for (const [key, block] of nameToBlock.entries()) {
549
+ if (!seen.has(key)) {
550
+ keptOrder.push(block);
551
+ }
552
+ }
553
+
554
+ const reqBody = [
555
+ parts.preamble && parts.preamble.trim() ? parts.preamble.trimEnd() : ''
556
+ ]
557
+ .filter(Boolean)
558
+ .concat(keptOrder.map(b => b.raw))
559
+ .join('\n\n')
560
+ .trimEnd();
561
+
562
+ const rebuilt = [
563
+ parts.before.trimEnd(),
564
+ parts.headerLine,
565
+ reqBody,
566
+ parts.after
567
+ ]
568
+ .filter((s, idx) => !(idx === 0 && s === ''))
569
+ .join('\n')
570
+ .replace(/\n{3,}/g, '\n\n');
571
+
572
+ return {
573
+ rebuilt,
574
+ counts: {
575
+ added: plan.added.length,
576
+ modified: plan.modified.length,
577
+ removed: plan.removed.length,
578
+ renamed: plan.renamed.length,
579
+ }
580
+ };
581
+ }
582
+
583
+ private async writeUpdatedSpec(update: SpecUpdate, rebuilt: string, counts: { added: number; modified: number; removed: number; renamed: number }): Promise<void> {
584
+ // Create target directory if needed
585
+ const targetDir = path.dirname(update.target);
586
+ await fs.mkdir(targetDir, { recursive: true });
587
+ await fs.writeFile(update.target, rebuilt);
588
+
589
+ const specName = path.basename(path.dirname(update.target));
590
+ console.log(`Applying changes to openspec/specs/${specName}/spec.md:`);
591
+ if (counts.added) console.log(` + ${counts.added} added`);
592
+ if (counts.modified) console.log(` ~ ${counts.modified} modified`);
593
+ if (counts.removed) console.log(` - ${counts.removed} removed`);
594
+ if (counts.renamed) console.log(` → ${counts.renamed} renamed`);
595
+ }
596
+
597
+ private buildSpecSkeleton(specFolderName: string, changeName: string): string {
598
+ const titleBase = specFolderName;
599
+ return `# ${titleBase} Specification\n\n## Purpose\nTBD - created by archiving change ${changeName}. Update Purpose after archive.\n\n## Requirements\n`;
600
+ }
601
+
602
+ private getArchiveDate(): string {
603
+ // Returns date in YYYY-MM-DD format
604
+ return new Date().toISOString().split('T')[0];
605
+ }
606
+ }
@@ -0,0 +1,41 @@
1
+ export const OPENSPEC_DIR_NAME = 'openspec';
2
+
3
+ export const OPENSPEC_MARKERS = {
4
+ start: '<!-- OPENSPEC:START -->',
5
+ end: '<!-- OPENSPEC:END -->'
6
+ };
7
+
8
+ export interface OpenSpecConfig {
9
+ aiTools: string[];
10
+ }
11
+
12
+ export interface AIToolOption {
13
+ name: string;
14
+ value: string;
15
+ available: boolean;
16
+ successLabel?: string;
17
+ }
18
+
19
+ export const AI_TOOLS: AIToolOption[] = [
20
+ { name: 'Amazon Q Developer', value: 'amazon-q', available: true, successLabel: 'Amazon Q Developer' },
21
+ { name: 'Antigravity', value: 'antigravity', available: true, successLabel: 'Antigravity' },
22
+ { name: 'Auggie (Augment CLI)', value: 'auggie', available: true, successLabel: 'Auggie' },
23
+ { name: 'Claude Code', value: 'claude', available: true, successLabel: 'Claude Code' },
24
+ { name: 'Cline', value: 'cline', available: true, successLabel: 'Cline' },
25
+ { name: 'Codex', value: 'codex', available: true, successLabel: 'Codex' },
26
+ { name: 'CodeBuddy Code (CLI)', value: 'codebuddy', available: true, successLabel: 'CodeBuddy Code' },
27
+ { name: 'CoStrict', value: 'costrict', available: true, successLabel: 'CoStrict' },
28
+ { name: 'Crush', value: 'crush', available: true, successLabel: 'Crush' },
29
+ { name: 'Cursor', value: 'cursor', available: true, successLabel: 'Cursor' },
30
+ { name: 'Factory Droid', value: 'factory', available: true, successLabel: 'Factory Droid' },
31
+ { name: 'Gemini CLI', value: 'gemini', available: true, successLabel: 'Gemini CLI' },
32
+ { name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot' },
33
+ { name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow' },
34
+ { name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code' },
35
+ { name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode' },
36
+ { name: 'Qoder (CLI)', value: 'qoder', available: true, successLabel: 'Qoder' },
37
+ { name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code' },
38
+ { name: 'RooCode', value: 'roocode', available: true, successLabel: 'RooCode' },
39
+ { name: 'Windsurf', value: 'windsurf', available: true, successLabel: 'Windsurf' },
40
+ { name: 'AGENTS.md (works with Amp, VS Code, …)', value: 'agents', available: false, successLabel: 'your AGENTS.md-compatible assistant' }
41
+ ];
@@ -0,0 +1,23 @@
1
+ import path from 'path';
2
+ import { ToolConfigurator } from './base.js';
3
+ import { FileSystemUtils } from '../../utils/file-system.js';
4
+ import { TemplateManager } from '../templates/index.js';
5
+ import { OPENSPEC_MARKERS } from '../config.js';
6
+
7
+ export class AgentsStandardConfigurator implements ToolConfigurator {
8
+ name = 'AGENTS.md standard';
9
+ configFileName = 'AGENTS.md';
10
+ isAvailable = true;
11
+
12
+ async configure(projectPath: string, _openspecDir: string): Promise<void> {
13
+ const filePath = path.join(projectPath, this.configFileName);
14
+ const content = TemplateManager.getAgentsStandardTemplate();
15
+
16
+ await FileSystemUtils.updateFileWithMarkers(
17
+ filePath,
18
+ content,
19
+ OPENSPEC_MARKERS.start,
20
+ OPENSPEC_MARKERS.end
21
+ );
22
+ }
23
+ }
@@ -0,0 +1,6 @@
1
+ export interface ToolConfigurator {
2
+ name: string;
3
+ configFileName: string;
4
+ isAvailable: boolean;
5
+ configure(projectPath: string, openspecDir: string): Promise<void>;
6
+ }