sauron-cli 1.3.0 → 1.4.1

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 (655) hide show
  1. package/.genesis/prompts/CLI Init_ Atualiza/303/247/303/243o com Mem/303/263ria.md" +133 -0
  2. package/.genesis/prompts/prompt-sauron-update-intelligence.md +20 -0
  3. package/README.md +4 -2
  4. package/dist/index.js +59 -15
  5. package/package.json +1 -1
  6. package/templates/.agents/rules/memory.md +68 -68
  7. package/templates/.agents/skills/wiki/SKILL.md +14 -14
  8. package/55ea973d-e255-4f08-a313-a5d68243bd2d.png +0 -0
  9. package/OpenSpec-main/.actrc +0 -1
  10. package/OpenSpec-main/.changeset/README.md +0 -97
  11. package/OpenSpec-main/.changeset/config.json +0 -15
  12. package/OpenSpec-main/.coderabbit.yaml +0 -11
  13. package/OpenSpec-main/.devcontainer/README.md +0 -92
  14. package/OpenSpec-main/.devcontainer/devcontainer.json +0 -68
  15. package/OpenSpec-main/.github/CODEOWNERS +0 -2
  16. package/OpenSpec-main/.github/workflows/README.md +0 -20
  17. package/OpenSpec-main/.github/workflows/ci.yml +0 -346
  18. package/OpenSpec-main/.github/workflows/release-prepare.yml +0 -60
  19. package/OpenSpec-main/CHANGELOG.md +0 -594
  20. package/OpenSpec-main/LICENSE +0 -22
  21. package/OpenSpec-main/MAINTAINERS.md +0 -17
  22. package/OpenSpec-main/README.md +0 -206
  23. package/OpenSpec-main/README_OLD.md +0 -475
  24. package/OpenSpec-main/assets/openspec_bg.png +0 -0
  25. package/OpenSpec-main/assets/openspec_dashboard.png +0 -0
  26. package/OpenSpec-main/assets/openspec_pixel_dark.svg +0 -89
  27. package/OpenSpec-main/assets/openspec_pixel_light.svg +0 -89
  28. package/OpenSpec-main/bin/openspec.js +0 -5
  29. package/OpenSpec-main/build.js +0 -31
  30. package/OpenSpec-main/eslint.config.js +0 -42
  31. package/OpenSpec-main/flake.lock +0 -27
  32. package/OpenSpec-main/flake.nix +0 -114
  33. package/OpenSpec-main/openspec/changes/IMPLEMENTATION_ORDER.md +0 -68
  34. package/OpenSpec-main/openspec/changes/add-artifact-regeneration-support/proposal.md +0 -136
  35. package/OpenSpec-main/openspec/changes/add-change-stacking-awareness/.openspec.yaml +0 -2
  36. package/OpenSpec-main/openspec/changes/add-change-stacking-awareness/proposal.md +0 -93
  37. package/OpenSpec-main/openspec/changes/add-change-stacking-awareness/specs/change-creation/spec.md +0 -15
  38. package/OpenSpec-main/openspec/changes/add-change-stacking-awareness/specs/change-stacking-workflow/spec.md +0 -65
  39. package/OpenSpec-main/openspec/changes/add-change-stacking-awareness/specs/cli-change/spec.md +0 -27
  40. package/OpenSpec-main/openspec/changes/add-change-stacking-awareness/specs/openspec-conventions/spec.md +0 -29
  41. package/OpenSpec-main/openspec/changes/add-change-stacking-awareness/tasks.md +0 -39
  42. package/OpenSpec-main/openspec/changes/add-global-install-scope/.openspec.yaml +0 -2
  43. package/OpenSpec-main/openspec/changes/add-global-install-scope/design.md +0 -161
  44. package/OpenSpec-main/openspec/changes/add-global-install-scope/proposal.md +0 -101
  45. package/OpenSpec-main/openspec/changes/add-global-install-scope/specs/ai-tool-paths/spec.md +0 -35
  46. package/OpenSpec-main/openspec/changes/add-global-install-scope/specs/cli-config/spec.md +0 -21
  47. package/OpenSpec-main/openspec/changes/add-global-install-scope/specs/cli-init/spec.md +0 -28
  48. package/OpenSpec-main/openspec/changes/add-global-install-scope/specs/cli-update/spec.md +0 -34
  49. package/OpenSpec-main/openspec/changes/add-global-install-scope/specs/command-generation/spec.md +0 -22
  50. package/OpenSpec-main/openspec/changes/add-global-install-scope/specs/global-config/spec.md +0 -24
  51. package/OpenSpec-main/openspec/changes/add-global-install-scope/specs/installation-scope/spec.md +0 -71
  52. package/OpenSpec-main/openspec/changes/add-global-install-scope/tasks.md +0 -61
  53. package/OpenSpec-main/openspec/changes/add-qa-smoke-harness/.openspec.yaml +0 -2
  54. package/OpenSpec-main/openspec/changes/add-qa-smoke-harness/proposal.md +0 -45
  55. package/OpenSpec-main/openspec/changes/add-qa-smoke-harness/specs/developer-qa-workflow/spec.md +0 -49
  56. package/OpenSpec-main/openspec/changes/add-tool-command-surface-capabilities/.openspec.yaml +0 -2
  57. package/OpenSpec-main/openspec/changes/add-tool-command-surface-capabilities/proposal.md +0 -111
  58. package/OpenSpec-main/openspec/changes/add-tool-command-surface-capabilities/specs/cli-init/spec.md +0 -121
  59. package/OpenSpec-main/openspec/changes/add-tool-command-surface-capabilities/specs/cli-update/spec.md +0 -48
  60. package/OpenSpec-main/openspec/changes/add-tool-command-surface-capabilities/tasks.md +0 -53
  61. package/OpenSpec-main/openspec/changes/archive/2025-01-11-add-update-command/design.md +0 -86
  62. package/OpenSpec-main/openspec/changes/archive/2025-01-11-add-update-command/proposal.md +0 -29
  63. package/OpenSpec-main/openspec/changes/archive/2025-01-11-add-update-command/specs/cli-update/spec.md +0 -59
  64. package/OpenSpec-main/openspec/changes/archive/2025-01-11-add-update-command/tasks.md +0 -20
  65. package/OpenSpec-main/openspec/changes/archive/2025-01-13-add-list-command/proposal.md +0 -20
  66. package/OpenSpec-main/openspec/changes/archive/2025-01-13-add-list-command/specs/cli-list/spec.md +0 -69
  67. package/OpenSpec-main/openspec/changes/archive/2025-01-13-add-list-command/tasks.md +0 -26
  68. package/OpenSpec-main/openspec/changes/archive/2025-08-05-initialize-typescript-project/design.md +0 -64
  69. package/OpenSpec-main/openspec/changes/archive/2025-08-05-initialize-typescript-project/proposal.md +0 -18
  70. package/OpenSpec-main/openspec/changes/archive/2025-08-05-initialize-typescript-project/tasks.md +0 -25
  71. package/OpenSpec-main/openspec/changes/archive/2025-08-06-add-init-command/design.md +0 -104
  72. package/OpenSpec-main/openspec/changes/archive/2025-08-06-add-init-command/proposal.md +0 -30
  73. package/OpenSpec-main/openspec/changes/archive/2025-08-06-add-init-command/specs/cli-init/spec.md +0 -148
  74. package/OpenSpec-main/openspec/changes/archive/2025-08-06-add-init-command/tasks.md +0 -38
  75. package/OpenSpec-main/openspec/changes/archive/2025-08-06-adopt-future-state-storage/proposal.md +0 -24
  76. package/OpenSpec-main/openspec/changes/archive/2025-08-06-adopt-future-state-storage/specs/openspec-conventions/spec.md +0 -120
  77. package/OpenSpec-main/openspec/changes/archive/2025-08-06-adopt-future-state-storage/tasks.md +0 -38
  78. package/OpenSpec-main/openspec/changes/archive/2025-08-11-add-complexity-guidelines/proposal.md +0 -13
  79. package/OpenSpec-main/openspec/changes/archive/2025-08-11-add-complexity-guidelines/specs/openspec-docs/README.md +0 -472
  80. package/OpenSpec-main/openspec/changes/archive/2025-08-11-add-complexity-guidelines/tasks.md +0 -9
  81. package/OpenSpec-main/openspec/changes/archive/2025-08-13-add-archive-command/proposal.md +0 -15
  82. package/OpenSpec-main/openspec/changes/archive/2025-08-13-add-archive-command/specs/cli-archive/spec.md +0 -111
  83. package/OpenSpec-main/openspec/changes/archive/2025-08-13-add-archive-command/tasks.md +0 -44
  84. package/OpenSpec-main/openspec/changes/archive/2025-08-13-add-diff-command/proposal.md +0 -19
  85. package/OpenSpec-main/openspec/changes/archive/2025-08-13-add-diff-command/specs/cli-diff/spec.md +0 -77
  86. package/OpenSpec-main/openspec/changes/archive/2025-08-13-add-diff-command/tasks.md +0 -23
  87. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-change-commands/design.md +0 -56
  88. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-change-commands/proposal.md +0 -17
  89. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-change-commands/specs/cli-change/spec.md +0 -48
  90. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-change-commands/specs/cli-list/spec.md +0 -12
  91. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-change-commands/tasks.md +0 -34
  92. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-interactive-show-command/proposal.md +0 -20
  93. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-interactive-show-command/specs/cli-change/spec.md +0 -23
  94. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-interactive-show-command/specs/cli-show/spec.md +0 -83
  95. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-interactive-show-command/specs/cli-spec/spec.md +0 -23
  96. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-interactive-show-command/tasks.md +0 -142
  97. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-skip-specs-archive-option/proposal.md +0 -13
  98. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-skip-specs-archive-option/specs/cli-archive/spec.md +0 -191
  99. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-skip-specs-archive-option/tasks.md +0 -57
  100. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-spec-commands/design.md +0 -45
  101. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-spec-commands/proposal.md +0 -19
  102. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-spec-commands/specs/cli-spec/spec.md +0 -43
  103. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-spec-commands/tasks.md +0 -22
  104. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-zod-validation/design.md +0 -104
  105. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-zod-validation/proposal.md +0 -22
  106. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-zod-validation/specs/cli-archive/spec.md +0 -18
  107. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-zod-validation/specs/cli-diff/spec.md +0 -12
  108. package/OpenSpec-main/openspec/changes/archive/2025-08-19-add-zod-validation/tasks.md +0 -59
  109. package/OpenSpec-main/openspec/changes/archive/2025-08-19-adopt-delta-based-changes/proposal.md +0 -93
  110. package/OpenSpec-main/openspec/changes/archive/2025-08-19-adopt-delta-based-changes/specs/cli-archive/spec.md +0 -48
  111. package/OpenSpec-main/openspec/changes/archive/2025-08-19-adopt-delta-based-changes/specs/cli-diff/spec.md +0 -45
  112. package/OpenSpec-main/openspec/changes/archive/2025-08-19-adopt-delta-based-changes/specs/openspec-conventions/spec.md +0 -101
  113. package/OpenSpec-main/openspec/changes/archive/2025-08-19-adopt-delta-based-changes/tasks.md +0 -55
  114. package/OpenSpec-main/openspec/changes/archive/2025-08-19-adopt-verb-noun-cli-structure/design.md +0 -19
  115. package/OpenSpec-main/openspec/changes/archive/2025-08-19-adopt-verb-noun-cli-structure/proposal.md +0 -67
  116. package/OpenSpec-main/openspec/changes/archive/2025-08-19-adopt-verb-noun-cli-structure/specs/cli-list/spec.md +0 -57
  117. package/OpenSpec-main/openspec/changes/archive/2025-08-19-adopt-verb-noun-cli-structure/specs/openspec-conventions/spec.md +0 -23
  118. package/OpenSpec-main/openspec/changes/archive/2025-08-19-adopt-verb-noun-cli-structure/tasks.md +0 -27
  119. package/OpenSpec-main/openspec/changes/archive/2025-08-19-bulk-validation-interactive-selection/proposal.md +0 -20
  120. package/OpenSpec-main/openspec/changes/archive/2025-08-19-bulk-validation-interactive-selection/specs/cli-change/spec.md +0 -22
  121. package/OpenSpec-main/openspec/changes/archive/2025-08-19-bulk-validation-interactive-selection/specs/cli-spec/spec.md +0 -23
  122. package/OpenSpec-main/openspec/changes/archive/2025-08-19-bulk-validation-interactive-selection/specs/cli-validate/spec.md +0 -149
  123. package/OpenSpec-main/openspec/changes/archive/2025-08-19-bulk-validation-interactive-selection/tasks.md +0 -81
  124. package/OpenSpec-main/openspec/changes/archive/2025-08-19-fix-update-tool-selection/proposal.md +0 -40
  125. package/OpenSpec-main/openspec/changes/archive/2025-08-19-fix-update-tool-selection/specs/cli-update/spec.md +0 -23
  126. package/OpenSpec-main/openspec/changes/archive/2025-08-19-fix-update-tool-selection/tasks.md +0 -21
  127. package/OpenSpec-main/openspec/changes/archive/2025-08-19-improve-validate-error-messages/proposal.md +0 -25
  128. package/OpenSpec-main/openspec/changes/archive/2025-08-19-improve-validate-error-messages/specs/cli-validate/spec.md +0 -55
  129. package/OpenSpec-main/openspec/changes/archive/2025-08-19-improve-validate-error-messages/tasks.md +0 -21
  130. package/OpenSpec-main/openspec/changes/archive/2025-08-19-structured-spec-format/proposal.md +0 -36
  131. package/OpenSpec-main/openspec/changes/archive/2025-08-19-structured-spec-format/specs/openspec-conventions/spec.md +0 -192
  132. package/OpenSpec-main/openspec/changes/archive/2025-08-19-structured-spec-format/tasks.md +0 -19
  133. package/OpenSpec-main/openspec/changes/archive/2025-09-12-add-view-dashboard-command/proposal.md +0 -38
  134. package/OpenSpec-main/openspec/changes/archive/2025-09-12-add-view-dashboard-command/specs/cli-view/spec.md +0 -109
  135. package/OpenSpec-main/openspec/changes/archive/2025-09-12-add-view-dashboard-command/tasks.md +0 -47
  136. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-agents-md-config/proposal.md +0 -28
  137. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-agents-md-config/specs/cli-init/spec.md +0 -71
  138. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-agents-md-config/specs/cli-update/spec.md +0 -41
  139. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-agents-md-config/tasks.md +0 -17
  140. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-multi-agent-init/proposal.md +0 -35
  141. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-multi-agent-init/specs/cli-init/spec.md +0 -45
  142. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-multi-agent-init/tasks.md +0 -16
  143. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-slash-command-support/proposal.md +0 -119
  144. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-slash-command-support/specs/cli-init/spec.md +0 -21
  145. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-slash-command-support/specs/cli-update/spec.md +0 -22
  146. package/OpenSpec-main/openspec/changes/archive/2025-09-29-add-slash-command-support/tasks.md +0 -20
  147. package/OpenSpec-main/openspec/changes/archive/2025-09-29-improve-cli-e2e-plan/proposal.md +0 -19
  148. package/OpenSpec-main/openspec/changes/archive/2025-09-29-improve-cli-e2e-plan/tasks.md +0 -9
  149. package/OpenSpec-main/openspec/changes/archive/2025-09-29-improve-deterministic-tests/proposal.md +0 -78
  150. package/OpenSpec-main/openspec/changes/archive/2025-09-29-improve-deterministic-tests/tasks.md +0 -25
  151. package/OpenSpec-main/openspec/changes/archive/2025-09-29-improve-init-onboarding/proposal.md +0 -13
  152. package/OpenSpec-main/openspec/changes/archive/2025-09-29-improve-init-onboarding/specs/cli-init/spec.md +0 -92
  153. package/OpenSpec-main/openspec/changes/archive/2025-09-29-improve-init-onboarding/tasks.md +0 -12
  154. package/OpenSpec-main/openspec/changes/archive/2025-09-29-remove-diff-command/proposal.md +0 -81
  155. package/OpenSpec-main/openspec/changes/archive/2025-09-29-remove-diff-command/tasks.md +0 -37
  156. package/OpenSpec-main/openspec/changes/archive/2025-09-29-sort-active-changes-by-progress/proposal.md +0 -25
  157. package/OpenSpec-main/openspec/changes/archive/2025-09-29-sort-active-changes-by-progress/specs/cli-view/spec.md +0 -9
  158. package/OpenSpec-main/openspec/changes/archive/2025-09-29-sort-active-changes-by-progress/tasks.md +0 -8
  159. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-agent-file-name/proposal.md +0 -29
  160. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-agent-file-name/specs/cli-init/spec.md +0 -40
  161. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-agent-file-name/specs/cli-update/spec.md +0 -22
  162. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-agent-file-name/specs/openspec-conventions/spec.md +0 -27
  163. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-agent-file-name/tasks.md +0 -22
  164. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-agent-instructions/design.md +0 -130
  165. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-agent-instructions/proposal.md +0 -117
  166. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-agent-instructions/tasks.md +0 -69
  167. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-markdown-parser-crlf/proposal.md +0 -19
  168. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-markdown-parser-crlf/specs/cli-validate/spec.md +0 -9
  169. package/OpenSpec-main/openspec/changes/archive/2025-09-29-update-markdown-parser-crlf/tasks.md +0 -11
  170. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-codex-slash-command-support/proposal.md +0 -25
  171. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-codex-slash-command-support/specs/cli-init/spec.md +0 -56
  172. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-codex-slash-command-support/specs/cli-update/spec.md +0 -41
  173. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-codex-slash-command-support/tasks.md +0 -19
  174. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-github-copilot-prompts/proposal.md +0 -25
  175. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-github-copilot-prompts/specs/cli-init/spec.md +0 -48
  176. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-github-copilot-prompts/specs/cli-update/spec.md +0 -48
  177. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-github-copilot-prompts/tasks.md +0 -30
  178. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-kilocode-workflows/proposal.md +0 -17
  179. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-kilocode-workflows/specs/cli-init/spec.md +0 -43
  180. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-kilocode-workflows/specs/cli-update/spec.md +0 -27
  181. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-kilocode-workflows/tasks.md +0 -15
  182. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-non-interactive-init-options/proposal.md +0 -12
  183. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-non-interactive-init-options/specs/cli-init/spec.md +0 -39
  184. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-non-interactive-init-options/tasks.md +0 -17
  185. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-windsurf-workflows/proposal.md +0 -17
  186. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-windsurf-workflows/specs/cli-init/spec.md +0 -42
  187. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-windsurf-workflows/specs/cli-update/spec.md +0 -27
  188. package/OpenSpec-main/openspec/changes/archive/2025-10-14-add-windsurf-workflows/tasks.md +0 -17
  189. package/OpenSpec-main/openspec/changes/archive/2025-10-14-enhance-validation-error-messages/proposal.md +0 -12
  190. package/OpenSpec-main/openspec/changes/archive/2025-10-14-enhance-validation-error-messages/specs/cli-validate/spec.md +0 -39
  191. package/OpenSpec-main/openspec/changes/archive/2025-10-14-enhance-validation-error-messages/tasks.md +0 -12
  192. package/OpenSpec-main/openspec/changes/archive/2025-10-14-improve-agent-instruction-usability/proposal.md +0 -12
  193. package/OpenSpec-main/openspec/changes/archive/2025-10-14-improve-agent-instruction-usability/specs/docs-agent-instructions/spec.md +0 -33
  194. package/OpenSpec-main/openspec/changes/archive/2025-10-14-improve-agent-instruction-usability/tasks.md +0 -11
  195. package/OpenSpec-main/openspec/changes/archive/2025-10-14-slim-root-agents-file/proposal.md +0 -13
  196. package/OpenSpec-main/openspec/changes/archive/2025-10-14-slim-root-agents-file/tasks.md +0 -15
  197. package/OpenSpec-main/openspec/changes/archive/2025-10-14-update-cli-init-enter-selection/proposal.md +0 -14
  198. package/OpenSpec-main/openspec/changes/archive/2025-10-14-update-cli-init-enter-selection/specs/cli-init/spec.md +0 -10
  199. package/OpenSpec-main/openspec/changes/archive/2025-10-14-update-cli-init-enter-selection/tasks.md +0 -8
  200. package/OpenSpec-main/openspec/changes/archive/2025-10-14-update-cli-init-root-agents/proposal.md +0 -15
  201. package/OpenSpec-main/openspec/changes/archive/2025-10-14-update-cli-init-root-agents/specs/cli-init/spec.md +0 -32
  202. package/OpenSpec-main/openspec/changes/archive/2025-10-14-update-cli-init-root-agents/specs/cli-update/spec.md +0 -10
  203. package/OpenSpec-main/openspec/changes/archive/2025-10-14-update-cli-init-root-agents/tasks.md +0 -11
  204. package/OpenSpec-main/openspec/changes/archive/2025-10-14-update-release-automation/proposal.md +0 -49
  205. package/OpenSpec-main/openspec/changes/archive/2025-10-14-update-release-automation/tasks.md +0 -12
  206. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-archive-command-arguments/proposal.md +0 -17
  207. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-archive-command-arguments/specs/cli-update/spec.md +0 -32
  208. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-archive-command-arguments/tasks.md +0 -15
  209. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-cline-support/proposal.md +0 -15
  210. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-cline-support/specs/cli-init/spec.md +0 -97
  211. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-cline-support/tasks.md +0 -19
  212. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-crush-support/proposal.md +0 -13
  213. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-crush-support/specs/cli-init/spec.md +0 -67
  214. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-crush-support/tasks.md +0 -7
  215. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-factory-slash-commands/proposal.md +0 -12
  216. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-factory-slash-commands/specs/cli-init/spec.md +0 -54
  217. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-factory-slash-commands/specs/cli-update/spec.md +0 -54
  218. package/OpenSpec-main/openspec/changes/archive/2025-10-22-add-factory-slash-commands/tasks.md +0 -11
  219. package/OpenSpec-main/openspec/changes/archive/2025-11-06-add-shell-completions/design.md +0 -525
  220. package/OpenSpec-main/openspec/changes/archive/2025-11-06-add-shell-completions/proposal.md +0 -29
  221. package/OpenSpec-main/openspec/changes/archive/2025-11-06-add-shell-completions/specs/cli-completion/spec.md +0 -300
  222. package/OpenSpec-main/openspec/changes/archive/2025-11-06-add-shell-completions/tasks.md +0 -81
  223. package/OpenSpec-main/openspec/changes/archive/2025-12-20-add-global-config-dir/design.md +0 -105
  224. package/OpenSpec-main/openspec/changes/archive/2025-12-20-add-global-config-dir/proposal.md +0 -20
  225. package/OpenSpec-main/openspec/changes/archive/2025-12-20-add-global-config-dir/specs/global-config/spec.md +0 -76
  226. package/OpenSpec-main/openspec/changes/archive/2025-12-20-add-global-config-dir/tasks.md +0 -26
  227. package/OpenSpec-main/openspec/changes/archive/2025-12-21-add-config-command/design.md +0 -89
  228. package/OpenSpec-main/openspec/changes/archive/2025-12-21-add-config-command/proposal.md +0 -60
  229. package/OpenSpec-main/openspec/changes/archive/2025-12-21-add-config-command/specs/cli-config/spec.md +0 -213
  230. package/OpenSpec-main/openspec/changes/archive/2025-12-21-add-config-command/tasks.md +0 -28
  231. package/OpenSpec-main/openspec/changes/archive/2025-12-23-extend-shell-completions/proposal.md +0 -15
  232. package/OpenSpec-main/openspec/changes/archive/2025-12-23-extend-shell-completions/specs/cli-completion/spec.md +0 -328
  233. package/OpenSpec-main/openspec/changes/archive/2025-12-23-extend-shell-completions/tasks.md +0 -49
  234. package/OpenSpec-main/openspec/changes/archive/2025-12-24-add-artifact-graph-core/design.md +0 -197
  235. package/OpenSpec-main/openspec/changes/archive/2025-12-24-add-artifact-graph-core/proposal.md +0 -18
  236. package/OpenSpec-main/openspec/changes/archive/2025-12-24-add-artifact-graph-core/specs/artifact-graph/spec.md +0 -103
  237. package/OpenSpec-main/openspec/changes/archive/2025-12-24-add-artifact-graph-core/tasks.md +0 -61
  238. package/OpenSpec-main/openspec/changes/archive/2025-12-25-add-change-manager/design.md +0 -74
  239. package/OpenSpec-main/openspec/changes/archive/2025-12-25-add-change-manager/proposal.md +0 -45
  240. package/OpenSpec-main/openspec/changes/archive/2025-12-25-add-change-manager/specs/change-creation/spec.md +0 -63
  241. package/OpenSpec-main/openspec/changes/archive/2025-12-25-add-change-manager/tasks.md +0 -30
  242. package/OpenSpec-main/openspec/changes/archive/2025-12-28-add-artifact-workflow-cli/design.md +0 -112
  243. package/OpenSpec-main/openspec/changes/archive/2025-12-28-add-artifact-workflow-cli/proposal.md +0 -33
  244. package/OpenSpec-main/openspec/changes/archive/2025-12-28-add-artifact-workflow-cli/specs/cli-artifact-workflow/spec.md +0 -153
  245. package/OpenSpec-main/openspec/changes/archive/2025-12-28-add-artifact-workflow-cli/tasks.md +0 -48
  246. package/OpenSpec-main/openspec/changes/archive/2025-12-28-add-instruction-loader/design.md +0 -149
  247. package/OpenSpec-main/openspec/changes/archive/2025-12-28-add-instruction-loader/proposal.md +0 -20
  248. package/OpenSpec-main/openspec/changes/archive/2025-12-28-add-instruction-loader/specs/instruction-loader/spec.md +0 -70
  249. package/OpenSpec-main/openspec/changes/archive/2025-12-28-add-instruction-loader/tasks.md +0 -13
  250. package/OpenSpec-main/openspec/changes/archive/2025-12-28-restructure-schema-directories/design.md +0 -129
  251. package/OpenSpec-main/openspec/changes/archive/2025-12-28-restructure-schema-directories/proposal.md +0 -20
  252. package/OpenSpec-main/openspec/changes/archive/2025-12-28-restructure-schema-directories/specs/artifact-graph/spec.md +0 -49
  253. package/OpenSpec-main/openspec/changes/archive/2025-12-28-restructure-schema-directories/tasks.md +0 -32
  254. package/OpenSpec-main/openspec/changes/archive/2025-12-29-unify-change-state-model/design.md +0 -151
  255. package/OpenSpec-main/openspec/changes/archive/2025-12-29-unify-change-state-model/proposal.md +0 -101
  256. package/OpenSpec-main/openspec/changes/archive/2025-12-29-unify-change-state-model/specs/cli-artifact-workflow/spec.md +0 -109
  257. package/OpenSpec-main/openspec/changes/archive/2025-12-29-unify-change-state-model/specs/cli-view/spec.md +0 -60
  258. package/OpenSpec-main/openspec/changes/archive/2025-12-29-unify-change-state-model/tasks.md +0 -25
  259. package/OpenSpec-main/openspec/changes/archive/2025-12-30-add-antigravity-support/proposal.md +0 -11
  260. package/OpenSpec-main/openspec/changes/archive/2025-12-30-add-antigravity-support/specs/cli-init/spec.md +0 -105
  261. package/OpenSpec-main/openspec/changes/archive/2025-12-30-add-antigravity-support/specs/cli-update/spec.md +0 -92
  262. package/OpenSpec-main/openspec/changes/archive/2025-12-30-add-antigravity-support/tasks.md +0 -12
  263. package/OpenSpec-main/openspec/changes/archive/2025-12-30-fix-cline-workflows-implementation/proposal.md +0 -13
  264. package/OpenSpec-main/openspec/changes/archive/2025-12-30-fix-cline-workflows-implementation/specs/cli-init/spec.md +0 -105
  265. package/OpenSpec-main/openspec/changes/archive/2025-12-30-fix-cline-workflows-implementation/specs/cli-update/spec.md +0 -92
  266. package/OpenSpec-main/openspec/changes/archive/2025-12-30-fix-cline-workflows-implementation/tasks.md +0 -13
  267. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-agent-schema-selection/proposal.md +0 -26
  268. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-agent-schema-selection/tasks.md +0 -32
  269. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-per-change-schema-metadata/design.md +0 -147
  270. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-per-change-schema-metadata/proposal.md +0 -29
  271. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-per-change-schema-metadata/specs/cli-artifact-workflow/spec.md +0 -98
  272. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-per-change-schema-metadata/tasks.md +0 -29
  273. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-specs-apply-command/.openspec.yaml +0 -2
  274. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-specs-apply-command/design.md +0 -77
  275. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-specs-apply-command/proposal.md +0 -32
  276. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-specs-apply-command/specs/specs-sync-skill/spec.md +0 -67
  277. package/OpenSpec-main/openspec/changes/archive/2026-01-06-add-specs-apply-command/tasks.md +0 -40
  278. package/OpenSpec-main/openspec/changes/archive/2026-01-06-make-apply-instructions-schema-aware/proposal.md +0 -138
  279. package/OpenSpec-main/openspec/changes/archive/2026-01-06-make-apply-instructions-schema-aware/specs/cli-artifact-workflow/spec.md +0 -60
  280. package/OpenSpec-main/openspec/changes/archive/2026-01-06-make-apply-instructions-schema-aware/tasks.md +0 -35
  281. package/OpenSpec-main/openspec/changes/archive/2026-01-06-opsx-archive-command/.openspec.yaml +0 -2
  282. package/OpenSpec-main/openspec/changes/archive/2026-01-06-opsx-archive-command/design.md +0 -84
  283. package/OpenSpec-main/openspec/changes/archive/2026-01-06-opsx-archive-command/proposal.md +0 -28
  284. package/OpenSpec-main/openspec/changes/archive/2026-01-06-opsx-archive-command/specs/opsx-archive-skill/spec.md +0 -122
  285. package/OpenSpec-main/openspec/changes/archive/2026-01-06-opsx-archive-command/tasks.md +0 -23
  286. package/OpenSpec-main/openspec/changes/archive/2026-01-07-add-nix-flake-support/.openspec.yaml +0 -2
  287. package/OpenSpec-main/openspec/changes/archive/2026-01-07-add-nix-flake-support/design.md +0 -94
  288. package/OpenSpec-main/openspec/changes/archive/2026-01-07-add-nix-flake-support/proposal.md +0 -25
  289. package/OpenSpec-main/openspec/changes/archive/2026-01-07-add-nix-flake-support/specs/nix-flake-support/spec.md +0 -79
  290. package/OpenSpec-main/openspec/changes/archive/2026-01-07-add-nix-flake-support/tasks.md +0 -65
  291. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-flake-update-script/.openspec.yaml +0 -2
  292. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-flake-update-script/design.md +0 -117
  293. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-flake-update-script/proposal.md +0 -23
  294. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-flake-update-script/specs/flake-update-script/spec.md +0 -93
  295. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-flake-update-script/tasks.md +0 -55
  296. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-posthog-analytics/.openspec.yaml +0 -2
  297. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-posthog-analytics/design.md +0 -175
  298. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-posthog-analytics/proposal.md +0 -37
  299. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-posthog-analytics/specs/global-config/spec.md +0 -21
  300. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-posthog-analytics/specs/telemetry/spec.md +0 -116
  301. package/OpenSpec-main/openspec/changes/archive/2026-01-09-add-posthog-analytics/tasks.md +0 -47
  302. package/OpenSpec-main/openspec/changes/archive/2026-01-09-fix-codebuddy-frontmatter-fields/proposal.md +0 -16
  303. package/OpenSpec-main/openspec/changes/archive/2026-01-09-fix-codebuddy-frontmatter-fields/specs/cli-init/spec.md +0 -75
  304. package/OpenSpec-main/openspec/changes/archive/2026-01-09-fix-codebuddy-frontmatter-fields/specs/cli-update/spec.md +0 -56
  305. package/OpenSpec-main/openspec/changes/archive/2026-01-09-fix-codebuddy-frontmatter-fields/tasks.md +0 -6
  306. package/OpenSpec-main/openspec/changes/archive/2026-01-15-add-nix-ci-validation/design.md +0 -206
  307. package/OpenSpec-main/openspec/changes/archive/2026-01-15-add-nix-ci-validation/proposal.md +0 -21
  308. package/OpenSpec-main/openspec/changes/archive/2026-01-15-add-nix-ci-validation/specs/ci-nix-validation/spec.md +0 -104
  309. package/OpenSpec-main/openspec/changes/archive/2026-01-15-add-nix-ci-validation/tasks.md +0 -49
  310. package/OpenSpec-main/openspec/changes/archive/2026-01-30-opencode-command-references/.openspec.yaml +0 -2
  311. package/OpenSpec-main/openspec/changes/archive/2026-01-30-opencode-command-references/README.md +0 -3
  312. package/OpenSpec-main/openspec/changes/archive/2026-01-30-opencode-command-references/design.md +0 -70
  313. package/OpenSpec-main/openspec/changes/archive/2026-01-30-opencode-command-references/proposal.md +0 -32
  314. package/OpenSpec-main/openspec/changes/archive/2026-01-30-opencode-command-references/specs/no-changes.md +0 -9
  315. package/OpenSpec-main/openspec/changes/archive/2026-01-30-opencode-command-references/tasks.md +0 -22
  316. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-feedback-command/proposal.md +0 -20
  317. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-feedback-command/specs/cli-feedback/spec.md +0 -188
  318. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-feedback-command/tasks.md +0 -30
  319. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-opsx-onboard-skill/.openspec.yaml +0 -2
  320. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-opsx-onboard-skill/design.md +0 -115
  321. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-opsx-onboard-skill/proposal.md +0 -27
  322. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-opsx-onboard-skill/specs/opsx-onboard-skill/spec.md +0 -162
  323. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-opsx-onboard-skill/tasks.md +0 -21
  324. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-verify-skill/design.md +0 -96
  325. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-verify-skill/proposal.md +0 -48
  326. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-verify-skill/specs/opsx-verify-skill/spec.md +0 -190
  327. package/OpenSpec-main/openspec/changes/archive/2026-02-17-add-verify-skill/tasks.md +0 -15
  328. package/OpenSpec-main/openspec/changes/archive/2026-02-17-merge-init-experimental/.openspec.yaml +0 -2
  329. package/OpenSpec-main/openspec/changes/archive/2026-02-17-merge-init-experimental/design.md +0 -193
  330. package/OpenSpec-main/openspec/changes/archive/2026-02-17-merge-init-experimental/proposal.md +0 -32
  331. package/OpenSpec-main/openspec/changes/archive/2026-02-17-merge-init-experimental/specs/cli-init/spec.md +0 -176
  332. package/OpenSpec-main/openspec/changes/archive/2026-02-17-merge-init-experimental/specs/legacy-cleanup/spec.md +0 -158
  333. package/OpenSpec-main/openspec/changes/archive/2026-02-17-merge-init-experimental/tasks.md +0 -67
  334. package/OpenSpec-main/openspec/changes/archive/2026-02-17-multi-provider-skill-generation/.openspec.yaml +0 -2
  335. package/OpenSpec-main/openspec/changes/archive/2026-02-17-multi-provider-skill-generation/design.md +0 -144
  336. package/OpenSpec-main/openspec/changes/archive/2026-02-17-multi-provider-skill-generation/proposal.md +0 -36
  337. package/OpenSpec-main/openspec/changes/archive/2026-02-17-multi-provider-skill-generation/specs/ai-tool-paths/spec.md +0 -63
  338. package/OpenSpec-main/openspec/changes/archive/2026-02-17-multi-provider-skill-generation/specs/cli-artifact-workflow/spec.md +0 -60
  339. package/OpenSpec-main/openspec/changes/archive/2026-02-17-multi-provider-skill-generation/specs/command-generation/spec.md +0 -98
  340. package/OpenSpec-main/openspec/changes/archive/2026-02-17-multi-provider-skill-generation/tasks.md +0 -55
  341. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-config/.openspec.yaml +0 -2
  342. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-config/design.md +0 -665
  343. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-config/proposal.md +0 -774
  344. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-config/specs/config-loading/spec.md +0 -119
  345. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-config/specs/context-injection/spec.md +0 -51
  346. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-config/specs/rules-injection/spec.md +0 -99
  347. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-config/specs/schema-resolution/spec.md +0 -83
  348. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-config/tasks.md +0 -72
  349. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-local-schemas/.openspec.yaml +0 -2
  350. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-local-schemas/design.md +0 -117
  351. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-local-schemas/proposal.md +0 -167
  352. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-local-schemas/specs/schema-resolution/spec.md +0 -88
  353. package/OpenSpec-main/openspec/changes/archive/2026-02-17-project-local-schemas/tasks.md +0 -28
  354. package/OpenSpec-main/openspec/changes/archive/2026-02-17-schema-management-cli/.openspec.yaml +0 -2
  355. package/OpenSpec-main/openspec/changes/archive/2026-02-17-schema-management-cli/design.md +0 -113
  356. package/OpenSpec-main/openspec/changes/archive/2026-02-17-schema-management-cli/proposal.md +0 -55
  357. package/OpenSpec-main/openspec/changes/archive/2026-02-17-schema-management-cli/specs/schema-fork-command/spec.md +0 -66
  358. package/OpenSpec-main/openspec/changes/archive/2026-02-17-schema-management-cli/specs/schema-init-command/spec.md +0 -71
  359. package/OpenSpec-main/openspec/changes/archive/2026-02-17-schema-management-cli/specs/schema-validate-command/spec.md +0 -86
  360. package/OpenSpec-main/openspec/changes/archive/2026-02-17-schema-management-cli/specs/schema-which-command/spec.md +0 -65
  361. package/OpenSpec-main/openspec/changes/archive/2026-02-17-schema-management-cli/tasks.md +0 -67
  362. package/OpenSpec-main/openspec/changes/archive/2026-04-23-add-kimi-cli-skills-only-support/.openspec.yaml +0 -2
  363. package/OpenSpec-main/openspec/changes/archive/2026-04-23-add-kimi-cli-skills-only-support/README.md +0 -3
  364. package/OpenSpec-main/openspec/changes/archive/2026-04-23-add-kimi-cli-skills-only-support/design.md +0 -85
  365. package/OpenSpec-main/openspec/changes/archive/2026-04-23-add-kimi-cli-skills-only-support/proposal.md +0 -38
  366. package/OpenSpec-main/openspec/changes/archive/2026-04-23-add-kimi-cli-skills-only-support/specs/ai-tool-paths/spec.md +0 -12
  367. package/OpenSpec-main/openspec/changes/archive/2026-04-23-add-kimi-cli-skills-only-support/specs/cli-init/spec.md +0 -37
  368. package/OpenSpec-main/openspec/changes/archive/2026-04-23-add-kimi-cli-skills-only-support/tasks.md +0 -22
  369. package/OpenSpec-main/openspec/changes/archive/2026-05-04-workspace-foundation/design.md +0 -208
  370. package/OpenSpec-main/openspec/changes/archive/2026-05-04-workspace-foundation/proposal.md +0 -142
  371. package/OpenSpec-main/openspec/changes/archive/2026-05-04-workspace-foundation/specs/openspec-conventions/spec.md +0 -29
  372. package/OpenSpec-main/openspec/changes/archive/2026-05-04-workspace-foundation/specs/workspace-foundation/spec.md +0 -199
  373. package/OpenSpec-main/openspec/changes/archive/2026-05-04-workspace-foundation/tasks.md +0 -56
  374. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-create-and-register-repos/design.md +0 -356
  375. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-create-and-register-repos/proposal.md +0 -128
  376. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-create-and-register-repos/specs/cli-artifact-workflow/spec.md +0 -24
  377. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-create-and-register-repos/specs/workspace-foundation/spec.md +0 -35
  378. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-create-and-register-repos/specs/workspace-links/spec.md +0 -356
  379. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-create-and-register-repos/tasks.md +0 -121
  380. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-open-agent-context/design.md +0 -266
  381. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-open-agent-context/proposal.md +0 -65
  382. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-open-agent-context/specs/workspace-foundation/spec.md +0 -76
  383. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-open-agent-context/specs/workspace-open/spec.md +0 -199
  384. package/OpenSpec-main/openspec/changes/archive/2026-05-06-workspace-open-agent-context/tasks.md +0 -89
  385. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/design.md +0 -242
  386. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/proposal.md +0 -78
  387. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/specs/artifact-graph/spec.md +0 -36
  388. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/specs/change-creation/spec.md +0 -42
  389. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/specs/cli-artifact-workflow/spec.md +0 -100
  390. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/specs/cli-config/spec.md +0 -55
  391. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/specs/cli-update/spec.md +0 -21
  392. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/specs/openspec-conventions/spec.md +0 -32
  393. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/specs/schema-resolution/spec.md +0 -25
  394. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/specs/workspace-change-planning/spec.md +0 -67
  395. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/specs/workspace-links/spec.md +0 -163
  396. package/OpenSpec-main/openspec/changes/archive/2026-05-14-workspace-change-planning/tasks.md +0 -133
  397. package/OpenSpec-main/openspec/changes/fix-opencode-commands-directory/.openspec.yaml +0 -2
  398. package/OpenSpec-main/openspec/changes/fix-opencode-commands-directory/design.md +0 -48
  399. package/OpenSpec-main/openspec/changes/fix-opencode-commands-directory/proposal.md +0 -26
  400. package/OpenSpec-main/openspec/changes/fix-opencode-commands-directory/specs/command-generation/spec.md +0 -63
  401. package/OpenSpec-main/openspec/changes/fix-opencode-commands-directory/tasks.md +0 -19
  402. package/OpenSpec-main/openspec/changes/graceful-status-no-changes/.openspec.yaml +0 -2
  403. package/OpenSpec-main/openspec/changes/graceful-status-no-changes/design.md +0 -38
  404. package/OpenSpec-main/openspec/changes/graceful-status-no-changes/proposal.md +0 -25
  405. package/OpenSpec-main/openspec/changes/graceful-status-no-changes/specs/graceful-status-empty/spec.md +0 -27
  406. package/OpenSpec-main/openspec/changes/graceful-status-no-changes/tasks.md +0 -16
  407. package/OpenSpec-main/openspec/changes/schema-alias-support/.openspec.yaml +0 -2
  408. package/OpenSpec-main/openspec/changes/schema-alias-support/proposal.md +0 -28
  409. package/OpenSpec-main/openspec/changes/simplify-skill-installation/.openspec.yaml +0 -2
  410. package/OpenSpec-main/openspec/changes/simplify-skill-installation/design.md +0 -288
  411. package/OpenSpec-main/openspec/changes/simplify-skill-installation/proposal.md +0 -202
  412. package/OpenSpec-main/openspec/changes/simplify-skill-installation/specs/cli-init/spec.md +0 -199
  413. package/OpenSpec-main/openspec/changes/simplify-skill-installation/specs/cli-update/spec.md +0 -177
  414. package/OpenSpec-main/openspec/changes/simplify-skill-installation/specs/profiles/spec.md +0 -142
  415. package/OpenSpec-main/openspec/changes/simplify-skill-installation/specs/propose-workflow/spec.md +0 -42
  416. package/OpenSpec-main/openspec/changes/simplify-skill-installation/tasks.md +0 -132
  417. package/OpenSpec-main/openspec/changes/unify-template-generation-pipeline/.openspec.yaml +0 -2
  418. package/OpenSpec-main/openspec/changes/unify-template-generation-pipeline/design.md +0 -149
  419. package/OpenSpec-main/openspec/changes/unify-template-generation-pipeline/proposal.md +0 -47
  420. package/OpenSpec-main/openspec/changes/unify-template-generation-pipeline/specs/template-artifact-pipeline/spec.md +0 -89
  421. package/OpenSpec-main/openspec/changes/unify-template-generation-pipeline/tasks.md +0 -41
  422. package/OpenSpec-main/openspec/changes/workspace-agent-guidance/.openspec.yaml +0 -2
  423. package/OpenSpec-main/openspec/changes/workspace-agent-guidance/proposal.md +0 -100
  424. package/OpenSpec-main/openspec/changes/workspace-apply-repo-slice/proposal.md +0 -58
  425. package/OpenSpec-main/openspec/changes/workspace-reimplementation-roadmap/HISTORICAL_DIRECTION.md +0 -511
  426. package/OpenSpec-main/openspec/changes/workspace-reimplementation-roadmap/POC_REFERENCE_GUIDE.md +0 -266
  427. package/OpenSpec-main/openspec/changes/workspace-reimplementation-roadmap/README.md +0 -107
  428. package/OpenSpec-main/openspec/changes/workspace-reimplementation-roadmap/START_HERE.md +0 -105
  429. package/OpenSpec-main/openspec/changes/workspace-reimplementation-roadmap/proposal.md +0 -62
  430. package/OpenSpec-main/openspec/changes/workspace-verify-and-archive/proposal.md +0 -57
  431. package/OpenSpec-main/openspec/config.yaml +0 -36
  432. package/OpenSpec-main/openspec/explorations/explore-workflow-ux.md +0 -116
  433. package/OpenSpec-main/openspec/explorations/workspace-architecture.md +0 -857
  434. package/OpenSpec-main/openspec/explorations/workspace-roadmap.md +0 -367
  435. package/OpenSpec-main/openspec/explorations/workspace-user-journeys.md +0 -2259
  436. package/OpenSpec-main/openspec/explorations/workspace-ux-simplification.md +0 -491
  437. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/.initiative.yaml +0 -27
  438. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/README.md +0 -33
  439. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/decisions.md +0 -204
  440. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/direction.md +0 -447
  441. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/questions.md +0 -23
  442. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/roadmap.md +0 -759
  443. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/tasks.md +0 -308
  444. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/01-lock-the-direction/evidence.md +0 -154
  445. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/01-lock-the-direction/plan.md +0 -90
  446. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/01-lock-the-direction/tasks.md +0 -44
  447. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/02-stabilize-workspace-as-local-view/evidence.md +0 -68
  448. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/02-stabilize-workspace-as-local-view/plan.md +0 -80
  449. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/02-stabilize-workspace-as-local-view/tasks.md +0 -23
  450. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/03-add-context-store-foundation/evidence.md +0 -43
  451. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/03-add-context-store-foundation/plan.md +0 -85
  452. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/03-add-context-store-foundation/tasks.md +0 -17
  453. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/04-add-collection-foundation/evidence.md +0 -77
  454. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/04-add-collection-foundation/plan.md +0 -198
  455. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/04-add-collection-foundation/tasks.md +0 -14
  456. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/05-ship-initiative-mvp/evidence.md +0 -99
  457. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/05-ship-initiative-mvp/plan.md +0 -236
  458. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/05-ship-initiative-mvp/tasks.md +0 -21
  459. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/06-add-minimal-context-store-ux/evidence.md +0 -97
  460. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/06-add-minimal-context-store-ux/plan.md +0 -333
  461. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/06-add-minimal-context-store-ux/tasks.md +0 -29
  462. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/07-add-agent-first-initiative-discovery/evidence.md +0 -97
  463. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/07-add-agent-first-initiative-discovery/plan.md +0 -184
  464. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/07-add-agent-first-initiative-discovery/tasks.md +0 -27
  465. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/08-connect-repo-local-changes-to-initiatives/evidence.md +0 -239
  466. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/08-connect-repo-local-changes-to-initiatives/plan.md +0 -279
  467. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/08-connect-repo-local-changes-to-initiatives/tasks.md +0 -22
  468. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/09-add-initiative-resolve/decision-review.md +0 -64
  469. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/09-add-initiative-resolve/evidence.md +0 -106
  470. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/09-add-initiative-resolve/plan.md +0 -141
  471. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/09-add-initiative-resolve/tasks.md +0 -22
  472. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/10-let-workspaces-open-initiatives/plan.md +0 -430
  473. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/10-let-workspaces-open-initiatives/tasks.md +0 -43
  474. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/11-manual-beta-reality-pass/notes.md +0 -289
  475. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/11-manual-beta-reality-pass/plan.md +0 -39
  476. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/11-manual-beta-reality-pass/tasks.md +0 -8
  477. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/12-context-store-first-run-and-cleanup-ux/evidence.md +0 -45
  478. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/12-context-store-first-run-and-cleanup-ux/plan.md +0 -150
  479. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/12-context-store-first-run-and-cleanup-ux/tasks.md +0 -23
  480. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/evidence.md +0 -25
  481. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/plan.md +0 -98
  482. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/tasks.md +0 -25
  483. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/14-workspaces-beta-guide-split/plan.md +0 -37
  484. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/14-workspaces-beta-guide-split/tasks.md +0 -9
  485. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/15-context-store-project-roots-and-schema-led-initiatives/evidence.md +0 -140
  486. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/15-context-store-project-roots-and-schema-led-initiatives/plan.md +0 -344
  487. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/15-context-store-project-roots-and-schema-led-initiatives/tasks.md +0 -39
  488. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/16-add-escalation-ux/plan.md +0 -26
  489. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/16-add-escalation-ux/tasks.md +0 -7
  490. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/17-harden-team-shared-coordination/plan.md +0 -25
  491. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/17-harden-team-shared-coordination/tasks.md +0 -7
  492. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/18-explore-initiative-hosted-target-bound-change-artifacts/evidence.md +0 -397
  493. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/18-explore-initiative-hosted-target-bound-change-artifacts/plan.md +0 -180
  494. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/18-explore-initiative-hosted-target-bound-change-artifacts/tasks.md +0 -28
  495. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/19-review-workspace-beta-compatibility-before-public-release/plan.md +0 -62
  496. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/19-review-workspace-beta-compatibility-before-public-release/tasks.md +0 -16
  497. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/proposed-initiative-next-agent-handoff-ux/evidence.md +0 -47
  498. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/proposed-initiative-next-agent-handoff-ux/plan.md +0 -90
  499. package/OpenSpec-main/openspec/initiatives/context-store-and-initiatives/work-items/proposed-initiative-next-agent-handoff-ux/tasks.md +0 -18
  500. package/OpenSpec-main/openspec/specs/ai-tool-paths/spec.md +0 -63
  501. package/OpenSpec-main/openspec/specs/artifact-graph/spec.md +0 -165
  502. package/OpenSpec-main/openspec/specs/change-creation/spec.md +0 -108
  503. package/OpenSpec-main/openspec/specs/ci-nix-validation/spec.md +0 -107
  504. package/OpenSpec-main/openspec/specs/cli-archive/spec.md +0 -210
  505. package/OpenSpec-main/openspec/specs/cli-artifact-workflow/spec.md +0 -398
  506. package/OpenSpec-main/openspec/specs/cli-change/spec.md +0 -92
  507. package/OpenSpec-main/openspec/specs/cli-completion/spec.md +0 -432
  508. package/OpenSpec-main/openspec/specs/cli-config/spec.md +0 -318
  509. package/OpenSpec-main/openspec/specs/cli-feedback/spec.md +0 -193
  510. package/OpenSpec-main/openspec/specs/cli-init/spec.md +0 -269
  511. package/OpenSpec-main/openspec/specs/cli-list/spec.md +0 -103
  512. package/OpenSpec-main/openspec/specs/cli-show/spec.md +0 -86
  513. package/OpenSpec-main/openspec/specs/cli-spec/spec.md +0 -88
  514. package/OpenSpec-main/openspec/specs/cli-update/spec.md +0 -229
  515. package/OpenSpec-main/openspec/specs/cli-validate/spec.md +0 -219
  516. package/OpenSpec-main/openspec/specs/cli-view/spec.md +0 -129
  517. package/OpenSpec-main/openspec/specs/command-generation/spec.md +0 -97
  518. package/OpenSpec-main/openspec/specs/config-loading/spec.md +0 -122
  519. package/OpenSpec-main/openspec/specs/context-injection/spec.md +0 -53
  520. package/OpenSpec-main/openspec/specs/docs-agent-instructions/spec.md +0 -62
  521. package/OpenSpec-main/openspec/specs/global-config/spec.md +0 -101
  522. package/OpenSpec-main/openspec/specs/instruction-loader/spec.md +0 -70
  523. package/OpenSpec-main/openspec/specs/legacy-cleanup/spec.md +0 -163
  524. package/OpenSpec-main/openspec/specs/openspec-conventions/spec.md +0 -556
  525. package/OpenSpec-main/openspec/specs/opsx-archive-skill/spec.md +0 -128
  526. package/OpenSpec-main/openspec/specs/opsx-onboard-skill/spec.md +0 -167
  527. package/OpenSpec-main/openspec/specs/opsx-verify-skill/spec.md +0 -189
  528. package/OpenSpec-main/openspec/specs/rules-injection/spec.md +0 -102
  529. package/OpenSpec-main/openspec/specs/schema-fork-command/spec.md +0 -71
  530. package/OpenSpec-main/openspec/specs/schema-init-command/spec.md +0 -76
  531. package/OpenSpec-main/openspec/specs/schema-resolution/spec.md +0 -201
  532. package/OpenSpec-main/openspec/specs/schema-validate-command/spec.md +0 -91
  533. package/OpenSpec-main/openspec/specs/schema-which-command/spec.md +0 -70
  534. package/OpenSpec-main/openspec/specs/specs-sync-skill/spec.md +0 -72
  535. package/OpenSpec-main/openspec/specs/telemetry/spec.md +0 -122
  536. package/OpenSpec-main/openspec/specs/workspace-change-planning/spec.md +0 -71
  537. package/OpenSpec-main/openspec/specs/workspace-foundation/spec.md +0 -279
  538. package/OpenSpec-main/openspec/specs/workspace-links/spec.md +0 -529
  539. package/OpenSpec-main/openspec/specs/workspace-open/spec.md +0 -205
  540. package/OpenSpec-main/openspec-parallel-merge-plan.md +0 -98
  541. package/OpenSpec-main/package-lock.json +0 -4978
  542. package/OpenSpec-main/package.json +0 -84
  543. package/OpenSpec-main/pnpm-lock.yaml +0 -3187
  544. package/OpenSpec-main/schemas/spec-driven/schema.yaml +0 -153
  545. package/OpenSpec-main/schemas/spec-driven/templates/design.md +0 -19
  546. package/OpenSpec-main/schemas/spec-driven/templates/proposal.md +0 -23
  547. package/OpenSpec-main/schemas/spec-driven/templates/spec.md +0 -8
  548. package/OpenSpec-main/schemas/spec-driven/templates/tasks.md +0 -9
  549. package/OpenSpec-main/schemas/workspace-planning/schema.yaml +0 -72
  550. package/OpenSpec-main/schemas/workspace-planning/templates/design.md +0 -33
  551. package/OpenSpec-main/schemas/workspace-planning/templates/proposal.md +0 -28
  552. package/OpenSpec-main/schemas/workspace-planning/templates/spec.md +0 -9
  553. package/OpenSpec-main/schemas/workspace-planning/templates/tasks.md +0 -15
  554. package/OpenSpec-main/scripts/README.md +0 -37
  555. package/OpenSpec-main/scripts/pack-version-check.mjs +0 -111
  556. package/OpenSpec-main/scripts/postinstall.js +0 -83
  557. package/OpenSpec-main/scripts/test-postinstall.sh +0 -57
  558. package/OpenSpec-main/scripts/update-flake.sh +0 -128
  559. package/OpenSpec-main/test/cli-e2e/basic.test.ts +0 -205
  560. package/OpenSpec-main/test/commands/artifact-workflow.test.ts +0 -1063
  561. package/OpenSpec-main/test/commands/change-initiative-link.test.ts +0 -532
  562. package/OpenSpec-main/test/commands/change.interactive-show.test.ts +0 -45
  563. package/OpenSpec-main/test/commands/change.interactive-validate.test.ts +0 -48
  564. package/OpenSpec-main/test/commands/completion.test.ts +0 -278
  565. package/OpenSpec-main/test/commands/config-profile.test.ts +0 -532
  566. package/OpenSpec-main/test/commands/config.test.ts +0 -285
  567. package/OpenSpec-main/test/commands/context-store.test.ts +0 -692
  568. package/OpenSpec-main/test/commands/feedback.test.ts +0 -429
  569. package/OpenSpec-main/test/commands/initiative.test.ts +0 -907
  570. package/OpenSpec-main/test/commands/schema.test.ts +0 -467
  571. package/OpenSpec-main/test/commands/show.test.ts +0 -123
  572. package/OpenSpec-main/test/commands/spec.interactive-show.test.ts +0 -44
  573. package/OpenSpec-main/test/commands/spec.interactive-validate.test.ts +0 -44
  574. package/OpenSpec-main/test/commands/spec.test.ts +0 -324
  575. package/OpenSpec-main/test/commands/validate.enriched-output.test.ts +0 -49
  576. package/OpenSpec-main/test/commands/validate.test.ts +0 -147
  577. package/OpenSpec-main/test/commands/workspace-initiative-open.test.ts +0 -638
  578. package/OpenSpec-main/test/commands/workspace-open.test.ts +0 -123
  579. package/OpenSpec-main/test/commands/workspace.interactive.test.ts +0 -696
  580. package/OpenSpec-main/test/commands/workspace.test.ts +0 -1812
  581. package/OpenSpec-main/test/core/archive.test.ts +0 -869
  582. package/OpenSpec-main/test/core/artifact-graph/graph.test.ts +0 -268
  583. package/OpenSpec-main/test/core/artifact-graph/instruction-loader.test.ts +0 -609
  584. package/OpenSpec-main/test/core/artifact-graph/outputs.test.ts +0 -175
  585. package/OpenSpec-main/test/core/artifact-graph/resolver.test.ts +0 -651
  586. package/OpenSpec-main/test/core/artifact-graph/schema.test.ts +0 -207
  587. package/OpenSpec-main/test/core/artifact-graph/state.test.ts +0 -174
  588. package/OpenSpec-main/test/core/artifact-graph/workflow.integration.test.ts +0 -182
  589. package/OpenSpec-main/test/core/available-tools.test.ts +0 -167
  590. package/OpenSpec-main/test/core/collections/initiatives/operations.test.ts +0 -342
  591. package/OpenSpec-main/test/core/collections/initiatives/resolution.test.ts +0 -21
  592. package/OpenSpec-main/test/core/collections/initiatives/schema.test.ts +0 -201
  593. package/OpenSpec-main/test/core/collections/initiatives/templates.test.ts +0 -74
  594. package/OpenSpec-main/test/core/collections/runtime.test.ts +0 -214
  595. package/OpenSpec-main/test/core/command-generation/adapters.test.ts +0 -710
  596. package/OpenSpec-main/test/core/command-generation/generator.test.ts +0 -110
  597. package/OpenSpec-main/test/core/command-generation/registry.test.ts +0 -108
  598. package/OpenSpec-main/test/core/command-generation/types.test.ts +0 -79
  599. package/OpenSpec-main/test/core/commands/change-command.list.test.ts +0 -76
  600. package/OpenSpec-main/test/core/commands/change-command.show-validate.test.ts +0 -111
  601. package/OpenSpec-main/test/core/completions/command-registry.test.ts +0 -201
  602. package/OpenSpec-main/test/core/completions/completion-provider.test.ts +0 -288
  603. package/OpenSpec-main/test/core/completions/generators/bash-generator.test.ts +0 -586
  604. package/OpenSpec-main/test/core/completions/generators/fish-generator.test.ts +0 -549
  605. package/OpenSpec-main/test/core/completions/generators/powershell-generator.test.ts +0 -621
  606. package/OpenSpec-main/test/core/completions/generators/zsh-generator.test.ts +0 -425
  607. package/OpenSpec-main/test/core/completions/installers/bash-installer.test.ts +0 -484
  608. package/OpenSpec-main/test/core/completions/installers/fish-installer.test.ts +0 -321
  609. package/OpenSpec-main/test/core/completions/installers/powershell-installer.test.ts +0 -824
  610. package/OpenSpec-main/test/core/completions/installers/zsh-installer.test.ts +0 -750
  611. package/OpenSpec-main/test/core/config-schema.test.ts +0 -340
  612. package/OpenSpec-main/test/core/context-store/foundation.test.ts +0 -364
  613. package/OpenSpec-main/test/core/context-store/registry.test.ts +0 -599
  614. package/OpenSpec-main/test/core/converters/json-converter.test.ts +0 -184
  615. package/OpenSpec-main/test/core/global-config.test.ts +0 -371
  616. package/OpenSpec-main/test/core/init.test.ts +0 -786
  617. package/OpenSpec-main/test/core/legacy-cleanup.test.ts +0 -1162
  618. package/OpenSpec-main/test/core/list.test.ts +0 -165
  619. package/OpenSpec-main/test/core/migration.test.ts +0 -150
  620. package/OpenSpec-main/test/core/parsers/change-parser.test.ts +0 -52
  621. package/OpenSpec-main/test/core/parsers/markdown-parser.test.ts +0 -355
  622. package/OpenSpec-main/test/core/parsers/requirement-blocks.test.ts +0 -46
  623. package/OpenSpec-main/test/core/planning-home.test.ts +0 -120
  624. package/OpenSpec-main/test/core/profile-sync-drift.test.ts +0 -92
  625. package/OpenSpec-main/test/core/profiles.test.ts +0 -63
  626. package/OpenSpec-main/test/core/project-config.test.ts +0 -610
  627. package/OpenSpec-main/test/core/shared/skill-generation.test.ts +0 -301
  628. package/OpenSpec-main/test/core/shared/tool-detection.test.ts +0 -333
  629. package/OpenSpec-main/test/core/templates/skill-templates-parity.test.ts +0 -172
  630. package/OpenSpec-main/test/core/update.test.ts +0 -1810
  631. package/OpenSpec-main/test/core/validation.enriched-messages.test.ts +0 -74
  632. package/OpenSpec-main/test/core/validation.test.ts +0 -680
  633. package/OpenSpec-main/test/core/view.test.ts +0 -129
  634. package/OpenSpec-main/test/core/workspace/foundation.test.ts +0 -694
  635. package/OpenSpec-main/test/core/workspace/legacy-state.test.ts +0 -221
  636. package/OpenSpec-main/test/core/workspace/skills.test.ts +0 -69
  637. package/OpenSpec-main/test/fixtures/tmp-init/openspec/changes/c1/proposal.md +0 -7
  638. package/OpenSpec-main/test/fixtures/tmp-init/openspec/changes/c1/specs/alpha/spec.md +0 -8
  639. package/OpenSpec-main/test/fixtures/tmp-init/openspec/specs/alpha/spec.md +0 -12
  640. package/OpenSpec-main/test/helpers/path-env.ts +0 -26
  641. package/OpenSpec-main/test/helpers/run-cli.ts +0 -150
  642. package/OpenSpec-main/test/prompts/searchable-multi-select.test.ts +0 -220
  643. package/OpenSpec-main/test/specs/source-specs-normalization.test.ts +0 -63
  644. package/OpenSpec-main/test/telemetry/config.test.ts +0 -298
  645. package/OpenSpec-main/test/telemetry/index.test.ts +0 -219
  646. package/OpenSpec-main/test/utils/change-metadata.test.ts +0 -368
  647. package/OpenSpec-main/test/utils/change-utils.test.ts +0 -201
  648. package/OpenSpec-main/test/utils/command-references.test.ts +0 -83
  649. package/OpenSpec-main/test/utils/file-system.test.ts +0 -322
  650. package/OpenSpec-main/test/utils/interactive.test.ts +0 -125
  651. package/OpenSpec-main/test/utils/marker-updates.test.ts +0 -448
  652. package/OpenSpec-main/test/utils/shell-detection.test.ts +0 -185
  653. package/OpenSpec-main/vitest.config.ts +0 -47
  654. package/OpenSpec-main/vitest.setup.ts +0 -15
  655. package/Scanner CLI Inteligente para Projetos.md +0 -433
@@ -1,1812 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
- import * as fs from 'node:fs';
3
- import * as os from 'node:os';
4
- import * as path from 'node:path';
5
-
6
- import { COMMAND_REGISTRY } from '../../src/core/completions/command-registry.js';
7
- import {
8
- createManagedWorkspace,
9
- resolveExistingDirectory,
10
- } from '../../src/commands/workspace/operations.js';
11
- import {
12
- WORKSPACE_CHANGES_DIR_NAME,
13
- WORKSPACE_GUIDANCE_END_MARKER,
14
- WORKSPACE_GUIDANCE_START_MARKER,
15
- WORKSPACE_METADATA_DIR_NAME,
16
- getWorkspaceCodeWorkspacePath,
17
- getManagedWorkspaceRoot,
18
- getWorkspaceRegistryPath,
19
- getWorkspaceViewStatePath,
20
- parseWorkspaceViewState,
21
- } from '../../src/core/workspace/index.js';
22
- import {
23
- WORKSPACE_LEGACY_LOCAL_STATE_FILE_NAME,
24
- WORKSPACE_LEGACY_SHARED_STATE_FILE_NAME,
25
- } from '../../src/core/workspace/legacy-state.js';
26
- import { FileSystemUtils } from '../../src/utils/file-system.js';
27
- import { withPrependedPathEnv } from '../helpers/path-env.js';
28
- import { runCLI, type RunCLIResult } from '../helpers/run-cli.js';
29
-
30
- describe('workspace command', () => {
31
- let tempDir: string;
32
- let dataHome: string;
33
- let configHome: string;
34
- let env: NodeJS.ProcessEnv;
35
-
36
- beforeEach(() => {
37
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-workspace-command-'));
38
- dataHome = path.join(tempDir, 'data');
39
- configHome = path.join(tempDir, 'config');
40
- env = {
41
- XDG_DATA_HOME: dataHome,
42
- XDG_CONFIG_HOME: configHome,
43
- OPEN_SPEC_INTERACTIVE: '0',
44
- OPENSPEC_TELEMETRY: '0',
45
- };
46
- });
47
-
48
- afterEach(() => {
49
- fs.rmSync(tempDir, { recursive: true, force: true });
50
- });
51
-
52
- function mkdir(relativePath: string): string {
53
- const dir = path.join(tempDir, relativePath);
54
- fs.mkdirSync(dir, { recursive: true });
55
- return dir;
56
- }
57
-
58
- function expectedExistingPath(existingPath: string): string {
59
- return fs.realpathSync.native(existingPath);
60
- }
61
-
62
- function expectSameExistingPath(actualPath: string | null, expectedPath: string): void {
63
- expect(actualPath).not.toBeNull();
64
- expect(fs.realpathSync.native(actualPath as string)).toBe(fs.realpathSync.native(expectedPath));
65
- }
66
-
67
- function parseJson(result: RunCLIResult): any {
68
- try {
69
- return JSON.parse(result.stdout);
70
- } catch (error) {
71
- throw new Error(
72
- `Could not parse JSON.\nCommand: ${result.command}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}\n${String(error)}`
73
- );
74
- }
75
- }
76
-
77
- function createFakeExecutable(name: string): { binDir: string; logPath: string } {
78
- const binDir = path.join(tempDir, 'fake-bin');
79
- const logPath = path.join(tempDir, `${name}-launch.json`);
80
- const recorderPath = path.join(binDir, 'record-launch.cjs');
81
- fs.mkdirSync(binDir, { recursive: true });
82
- fs.writeFileSync(
83
- recorderPath,
84
- "const fs = require('node:fs');\nfs.writeFileSync(process.env.OPENSPEC_FAKE_OPEN_LOG, JSON.stringify({ cwd: process.cwd(), args: process.argv.slice(2) }));\n"
85
- );
86
-
87
- const posixExecutable = path.join(binDir, name);
88
- fs.writeFileSync(posixExecutable, '#!/bin/sh\nnode "$OPENSPEC_FAKE_OPEN_RECORDER" "$@"\n');
89
- fs.chmodSync(posixExecutable, 0o755);
90
- fs.writeFileSync(
91
- path.join(binDir, `${name}.cmd`),
92
- '@echo off\r\nnode "%OPENSPEC_FAKE_OPEN_RECORDER%" %*\r\n'
93
- );
94
-
95
- return { binDir, logPath };
96
- }
97
-
98
- function envWithFakeExecutable(fake: { binDir: string; logPath: string }): NodeJS.ProcessEnv {
99
- return {
100
- ...withPrependedPathEnv(env, fake.binDir),
101
- OPENSPEC_FAKE_OPEN_RECORDER: path.join(fake.binDir, 'record-launch.cjs'),
102
- OPENSPEC_FAKE_OPEN_LOG: fake.logPath,
103
- };
104
- }
105
-
106
- function readLaunchLog(logPath: string): { cwd: string; args: string[] } {
107
- return JSON.parse(fs.readFileSync(logPath, 'utf-8'));
108
- }
109
-
110
- async function setupWorkspace(
111
- name = 'platform',
112
- links: string[] = [],
113
- extraArgs: string[] = []
114
- ): Promise<any> {
115
- const result = await runCLI(
116
- [
117
- 'workspace',
118
- 'setup',
119
- '--no-interactive',
120
- '--json',
121
- '--name',
122
- name,
123
- ...links.flatMap((link) => ['--link', link]),
124
- ...extraArgs,
125
- ],
126
- { cwd: tempDir, env }
127
- );
128
- expect(result.exitCode).toBe(0);
129
- return parseJson(result);
130
- }
131
-
132
- function readWorkspaceState(workspaceRoot: string) {
133
- return parseWorkspaceViewState(fs.readFileSync(getWorkspaceViewStatePath(workspaceRoot), 'utf-8'));
134
- }
135
-
136
- function writeGlobalConfig(config: Record<string, unknown>): void {
137
- const configDir = path.join(configHome, 'openspec');
138
- fs.mkdirSync(configDir, { recursive: true });
139
- fs.writeFileSync(path.join(configDir, 'config.json'), `${JSON.stringify(config, null, 2)}\n`);
140
- }
141
-
142
- it('sets up a workspace with required links, records local state, and lists it through ls', async () => {
143
- const api = mkdir('repos/api');
144
- mkdir('repos/api/openspec/specs');
145
- const checkout = mkdir('repos/platform/apps/checkout');
146
- const expectedApi = expectedExistingPath(api);
147
- const expectedCheckout = expectedExistingPath(checkout);
148
-
149
- const setup = await setupWorkspace('platform', [`api=${api}`, checkout]);
150
- const workspaceRoot = setup.workspace.root;
151
- const expectedWorkspaceRoot = expectedExistingPath(workspaceRoot);
152
-
153
- expect(setup.status).toEqual([]);
154
- expect(setup.workspace.name).toBe('platform');
155
- expect(setup.workspace.links).toEqual([
156
- expect.objectContaining({
157
- name: 'api',
158
- path: expectedApi,
159
- repo_specs_path: path.join(expectedApi, 'openspec', 'specs'),
160
- status: [],
161
- }),
162
- expect.objectContaining({
163
- name: 'checkout',
164
- path: expectedCheckout,
165
- repo_specs_path: null,
166
- status: [],
167
- }),
168
- ]);
169
-
170
- const workspaceState = readWorkspaceState(workspaceRoot);
171
-
172
- expect(workspaceState).toEqual({
173
- version: 1,
174
- name: 'platform',
175
- context: null,
176
- links: {
177
- api: expectedApi,
178
- checkout: expectedCheckout,
179
- },
180
- });
181
- expect(workspaceState.preferred_opener).toBeUndefined();
182
- expect(fs.existsSync(getWorkspaceRegistryPath({ globalDataDir: path.join(dataHome, 'openspec') }))).toBe(false);
183
- expect(fs.existsSync(path.join(workspaceRoot, '.gitignore'))).toBe(false);
184
- expect(fs.existsSync(path.join(workspaceRoot, WORKSPACE_CHANGES_DIR_NAME))).toBe(false);
185
- expect(fs.readFileSync(path.join(workspaceRoot, 'AGENTS.md'), 'utf-8')).toContain(
186
- 'OpenSpec Workspace Guidance'
187
- );
188
- expect(JSON.parse(fs.readFileSync(getWorkspaceCodeWorkspacePath(workspaceRoot, 'platform'), 'utf-8')).folders).toEqual([
189
- {
190
- name: 'api',
191
- path: expectedApi,
192
- },
193
- {
194
- name: 'checkout',
195
- path: expectedCheckout,
196
- },
197
- {
198
- name: 'OpenSpec workspace',
199
- path: '.',
200
- },
201
- ]);
202
-
203
- const list = await runCLI(['workspace', 'ls', '--json'], { cwd: tempDir, env });
204
- expect(list.exitCode).toBe(0);
205
- const listPayload = parseJson(list);
206
- expect(listPayload.workspaces).toEqual([
207
- expect.objectContaining({
208
- name: 'platform',
209
- root: expectedWorkspaceRoot,
210
- links: [
211
- expect.objectContaining({ name: 'api', path: expectedApi, status: [] }),
212
- expect.objectContaining({ name: 'checkout', path: expectedCheckout, status: [] }),
213
- ],
214
- status: [],
215
- }),
216
- ]);
217
-
218
- const doctor = await runCLI(['workspace', 'doctor', '--workspace', 'platform', '--json'], {
219
- cwd: tempDir,
220
- env,
221
- });
222
- expect(doctor.exitCode).toBe(0);
223
- expect(parseJson(doctor).workspace.links).toEqual([
224
- expect.objectContaining({ name: 'api', path: expectedApi, status: [] }),
225
- expect.objectContaining({ name: 'checkout', path: expectedCheckout, status: [] }),
226
- ]);
227
- });
228
-
229
- it('keeps non-interactive setup compatible by skipping skills when --tools is omitted', async () => {
230
- const api = mkdir('repos/api');
231
- const setup = await setupWorkspace('skip-skills', [`api=${api}`]);
232
-
233
- expect(setup.workspace_skills).toEqual(
234
- expect.objectContaining({
235
- selected_agents: [],
236
- generated: [],
237
- refreshed: [],
238
- failed: [],
239
- skipped: [
240
- expect.objectContaining({
241
- reason: 'tools_omitted',
242
- message: expect.stringContaining('openspec workspace update --tools <ids>'),
243
- }),
244
- ],
245
- })
246
- );
247
- expect(readWorkspaceState(setup.workspace.root).workspace_skills).toBeUndefined();
248
- expect(fs.existsSync(path.join(setup.workspace.root, '.codex'))).toBe(false);
249
- });
250
-
251
- it('installs profile-selected workspace skills in the workspace root only', async () => {
252
- const api = mkdir('repos/api');
253
- const linkedEntriesBefore = fs.readdirSync(api).sort();
254
- const codexHome = path.join(tempDir, 'codex-home');
255
- writeGlobalConfig({
256
- profile: 'custom',
257
- delivery: 'commands',
258
- workflows: ['apply', 'archive'],
259
- });
260
-
261
- const result = await runCLI(
262
- [
263
- 'workspace',
264
- 'setup',
265
- '--no-interactive',
266
- '--json',
267
- '--name',
268
- 'skill-root',
269
- '--link',
270
- `api=${api}`,
271
- '--opener',
272
- 'codex',
273
- '--tools',
274
- 'codex',
275
- ],
276
- {
277
- cwd: tempDir,
278
- env: {
279
- ...env,
280
- CODEX_HOME: codexHome,
281
- },
282
- }
283
- );
284
-
285
- expect(result.exitCode).toBe(0);
286
- const payload = parseJson(result);
287
- const workspaceRoot = payload.workspace.root;
288
- expect(payload.workspace_skills).toEqual(
289
- expect.objectContaining({
290
- profile: 'custom',
291
- delivery: 'commands',
292
- workflow_ids: ['apply', 'archive'],
293
- selected_agents: ['codex'],
294
- skills_only: true,
295
- delivery_notice: expect.stringContaining('skills only'),
296
- generated: [
297
- expect.objectContaining({
298
- tool_id: 'codex',
299
- workflow_ids: ['apply', 'archive'],
300
- }),
301
- ],
302
- refreshed: [],
303
- failed: [],
304
- })
305
- );
306
-
307
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-apply-change', 'SKILL.md'))).toBe(true);
308
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-archive-change', 'SKILL.md'))).toBe(true);
309
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-propose', 'SKILL.md'))).toBe(false);
310
- expect(fs.existsSync(path.join(codexHome, 'prompts'))).toBe(false);
311
- expect(fs.readdirSync(api).sort()).toEqual(linkedEntriesBefore);
312
- expect(fs.existsSync(path.join(api, '.codex'))).toBe(false);
313
-
314
- expect(readWorkspaceState(workspaceRoot).workspace_skills).toEqual(
315
- expect.objectContaining({
316
- selected_agents: ['codex'],
317
- last_applied_profile: 'custom',
318
- last_applied_delivery: 'commands',
319
- last_applied_workflow_ids: ['apply', 'archive'],
320
- last_applied_at: expect.any(String),
321
- })
322
- );
323
- });
324
-
325
- it('supports --tools none and records an empty workspace skill selection', async () => {
326
- const api = mkdir('repos/api');
327
- const setup = await setupWorkspace('skills-none', [`api=${api}`], ['--tools', 'none']);
328
-
329
- expect(setup.workspace_skills).toEqual(
330
- expect.objectContaining({
331
- selected_agents: [],
332
- generated: [],
333
- refreshed: [],
334
- failed: [],
335
- skipped: [
336
- expect.objectContaining({
337
- reason: 'no_agents_selected',
338
- }),
339
- ],
340
- })
341
- );
342
- expect(readWorkspaceState(setup.workspace.root).workspace_skills).toEqual(
343
- expect.objectContaining({
344
- selected_agents: [],
345
- last_applied_workflow_ids: ['propose', 'explore', 'apply', 'sync', 'archive'],
346
- })
347
- );
348
- });
349
-
350
- it('updates stored workspace skills from the current workspace and clears profile drift', async () => {
351
- const api = mkdir('repos/api');
352
- const linkedEntriesBefore = fs.readdirSync(api).sort();
353
- writeGlobalConfig({
354
- profile: 'custom',
355
- delivery: 'commands',
356
- workflows: ['apply', 'verify'],
357
- });
358
- const setup = await setupWorkspace('profile-sync', [`api=${api}`], ['--tools', 'codex']);
359
- const workspaceRoot = setup.workspace.root;
360
- const customSkillDir = path.join(workspaceRoot, '.codex', 'skills', 'custom-note');
361
- fs.mkdirSync(customSkillDir, { recursive: true });
362
- fs.writeFileSync(path.join(customSkillDir, 'README.md'), 'user-owned\n');
363
-
364
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-apply-change', 'SKILL.md'))).toBe(true);
365
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-verify-change', 'SKILL.md'))).toBe(true);
366
-
367
- writeGlobalConfig({
368
- profile: 'core',
369
- delivery: 'commands',
370
- });
371
-
372
- const drift = await runCLI(
373
- ['workspace', 'doctor', '--workspace', 'profile-sync', '--json'],
374
- { cwd: tempDir, env }
375
- );
376
- expect(drift.exitCode).toBe(0);
377
- expect(parseJson(drift).workspace.status).toContainEqual(
378
- expect.objectContaining({
379
- code: 'workspace_skills_out_of_sync',
380
- fix: 'openspec workspace update --workspace profile-sync',
381
- })
382
- );
383
-
384
- const update = await runCLI(['workspace', 'update', '--json'], {
385
- cwd: workspaceRoot,
386
- env,
387
- });
388
- expect(update.exitCode).toBe(0);
389
- const payload = parseJson(update);
390
-
391
- expect(payload.workspace.name).toBe('profile-sync');
392
- expect(payload.workspace_skills).toEqual(
393
- expect.objectContaining({
394
- profile: 'core',
395
- delivery: 'commands',
396
- workflow_ids: ['propose', 'explore', 'apply', 'sync', 'archive'],
397
- selected_agents: ['codex'],
398
- skills_only: true,
399
- delivery_notice: expect.stringContaining('skills only'),
400
- refreshed: [
401
- expect.objectContaining({
402
- tool_id: 'codex',
403
- workflow_ids: ['propose', 'explore', 'apply', 'sync', 'archive'],
404
- }),
405
- ],
406
- removed: [
407
- expect.objectContaining({
408
- tool_id: 'codex',
409
- reason: 'workflow_unselected',
410
- workflow_ids: ['verify'],
411
- }),
412
- ],
413
- failed: [],
414
- })
415
- );
416
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-propose', 'SKILL.md'))).toBe(true);
417
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-explore', 'SKILL.md'))).toBe(true);
418
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-sync-specs', 'SKILL.md'))).toBe(true);
419
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-archive-change', 'SKILL.md'))).toBe(true);
420
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-verify-change'))).toBe(false);
421
- expect(fs.existsSync(path.join(customSkillDir, 'README.md'))).toBe(true);
422
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'prompts'))).toBe(false);
423
- expect(fs.readdirSync(api).sort()).toEqual(linkedEntriesBefore);
424
- expect(fs.existsSync(path.join(api, '.codex'))).toBe(false);
425
- expect(readWorkspaceState(workspaceRoot).workspace_skills).toEqual(
426
- expect.objectContaining({
427
- selected_agents: ['codex'],
428
- last_applied_profile: 'core',
429
- last_applied_delivery: 'commands',
430
- last_applied_workflow_ids: ['propose', 'explore', 'apply', 'sync', 'archive'],
431
- })
432
- );
433
-
434
- const clean = await runCLI(
435
- ['workspace', 'doctor', '--workspace', 'profile-sync', '--json'],
436
- { cwd: tempDir, env }
437
- );
438
- expect(clean.exitCode).toBe(0);
439
- expect(parseJson(clean).workspace.status).not.toContainEqual(
440
- expect.objectContaining({
441
- code: 'workspace_skills_out_of_sync',
442
- })
443
- );
444
- });
445
-
446
- it('does not route openspec update through workspace update from a workspace root', async () => {
447
- const api = mkdir('repos/api');
448
- const linkedEntriesBefore = fs.readdirSync(api).sort();
449
- writeGlobalConfig({
450
- profile: 'custom',
451
- delivery: 'commands',
452
- workflows: ['apply'],
453
- });
454
- const setup = await setupWorkspace('update-redirect', [`api=${api}`], ['--tools', 'codex']);
455
- const workspaceRoot = setup.workspace.root;
456
- const workspaceStateBefore = fs.readFileSync(getWorkspaceViewStatePath(workspaceRoot), 'utf-8');
457
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-apply-change', 'SKILL.md'))).toBe(true);
458
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-propose', 'SKILL.md'))).toBe(false);
459
-
460
- writeGlobalConfig({
461
- profile: 'core',
462
- delivery: 'commands',
463
- });
464
-
465
- const update = await runCLI(['update'], {
466
- cwd: workspaceRoot,
467
- env,
468
- });
469
- expect(update.exitCode).toBe(1);
470
- expect(`${update.stdout}\n${update.stderr}`).toContain('Run `openspec workspace update`');
471
- expect(update.stdout).not.toContain('Workspace update complete');
472
- expect(update.stdout).not.toContain('not in the managed local workspace views list');
473
- expect(fs.readFileSync(getWorkspaceViewStatePath(workspaceRoot), 'utf-8')).toBe(workspaceStateBefore);
474
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-propose', 'SKILL.md'))).toBe(false);
475
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-sync-specs', 'SKILL.md'))).toBe(false);
476
- expect(fs.readdirSync(api).sort()).toEqual(linkedEntriesBefore);
477
- expect(fs.existsSync(path.join(api, '.codex'))).toBe(false);
478
- });
479
-
480
- it('updates repo-local project targets nested under a workspace without touching workspace state', async () => {
481
- const api = mkdir('repos/api');
482
- writeGlobalConfig({
483
- profile: 'custom',
484
- delivery: 'commands',
485
- workflows: ['apply'],
486
- });
487
- const setup = await setupWorkspace('nested-update-target', [`api=${api}`], ['--tools', 'codex']);
488
- const workspaceRoot = setup.workspace.root;
489
- const workspaceStateBefore = fs.readFileSync(getWorkspaceViewStatePath(workspaceRoot), 'utf-8');
490
- const nestedRepo = path.join(workspaceRoot, 'repos', 'nested-api');
491
- fs.mkdirSync(path.join(nestedRepo, 'openspec'), { recursive: true });
492
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-apply-change', 'SKILL.md'))).toBe(true);
493
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-propose', 'SKILL.md'))).toBe(false);
494
-
495
- writeGlobalConfig({
496
- profile: 'core',
497
- delivery: 'commands',
498
- });
499
-
500
- const update = await runCLI(['update', nestedRepo], {
501
- cwd: tempDir,
502
- env,
503
- });
504
-
505
- expect(update.exitCode).toBe(0);
506
- expect(update.stdout).toContain('No configured tools found');
507
- expect(`${update.stdout}\n${update.stderr}`).not.toContain('Run `openspec workspace update`');
508
- expect(update.stdout).not.toContain('Workspace update complete');
509
- expect(fs.readFileSync(getWorkspaceViewStatePath(workspaceRoot), 'utf-8')).toBe(workspaceStateBefore);
510
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-propose', 'SKILL.md'))).toBe(false);
511
- });
512
-
513
- it('does not touch workspace state when updating repo-local projects with foreign workspace.yaml', async () => {
514
- const existingApi = mkdir('repos/existing-api');
515
- writeGlobalConfig({
516
- profile: 'custom',
517
- delivery: 'commands',
518
- workflows: ['apply'],
519
- });
520
- const existingWorkspace = await setupWorkspace('known-workspace', [`api=${existingApi}`], ['--tools', 'codex']);
521
- const existingWorkspaceRoot = existingWorkspace.workspace.root;
522
- const existingWorkspaceStateBefore = fs.readFileSync(
523
- getWorkspaceViewStatePath(existingWorkspaceRoot),
524
- 'utf-8'
525
- );
526
- expect(fs.existsSync(path.join(existingWorkspaceRoot, '.codex', 'skills', 'openspec-apply-change', 'SKILL.md'))).toBe(true);
527
- expect(fs.existsSync(path.join(existingWorkspaceRoot, '.codex', 'skills', 'openspec-propose', 'SKILL.md'))).toBe(false);
528
-
529
- writeGlobalConfig({
530
- profile: 'core',
531
- delivery: 'commands',
532
- });
533
-
534
- const repoRoot = mkdir('repos/foreign-tool');
535
- fs.mkdirSync(path.join(repoRoot, 'openspec'), { recursive: true });
536
- const foreignWorkspaceYaml = `tool_workspace:
537
- projects:
538
- - name: example
539
- path: ./service
540
- `;
541
- fs.writeFileSync(path.join(repoRoot, 'workspace.yaml'), foreignWorkspaceYaml);
542
-
543
- const update = await runCLI(['update'], {
544
- cwd: repoRoot,
545
- env,
546
- });
547
-
548
- expect(update.exitCode).toBe(0);
549
- expect(update.stdout).not.toContain('Workspace update complete');
550
- expect(update.stderr).not.toContain('Invalid workspace state');
551
- expect(update.stdout).toContain('No configured tools found');
552
- expect(fs.readFileSync(getWorkspaceViewStatePath(existingWorkspaceRoot), 'utf-8')).toBe(
553
- existingWorkspaceStateBefore
554
- );
555
- expect(fs.existsSync(path.join(existingWorkspaceRoot, '.codex', 'skills', 'openspec-propose', 'SKILL.md'))).toBe(false);
556
- expect(fs.readFileSync(path.join(repoRoot, 'workspace.yaml'), 'utf-8')).toBe(
557
- foreignWorkspaceYaml
558
- );
559
- expect(fs.existsSync(path.join(repoRoot, WORKSPACE_METADATA_DIR_NAME))).toBe(false);
560
- expect(fs.existsSync(path.join(repoRoot, WORKSPACE_CHANGES_DIR_NAME))).toBe(false);
561
- expect(fs.readdirSync(repoRoot).some((entry) => entry.endsWith('.code-workspace'))).toBe(false);
562
- expect(fs.existsSync(getWorkspaceRegistryPath({ globalDataDir: path.join(dataHome, 'openspec') }))).toBe(false);
563
- });
564
-
565
- it('does not update a workspace passed to openspec update even when another workspace is known', async () => {
566
- const firstApi = mkdir('repos/first-api');
567
- const secondApi = mkdir('repos/second-api');
568
- writeGlobalConfig({
569
- profile: 'custom',
570
- delivery: 'commands',
571
- workflows: ['apply'],
572
- });
573
- const first = await setupWorkspace('target-first', [`api=${firstApi}`], ['--tools', 'codex']);
574
- const second = await setupWorkspace('target-second', [`api=${secondApi}`], ['--tools', 'codex']);
575
- const firstWorkspaceStateBefore = fs.readFileSync(getWorkspaceViewStatePath(first.workspace.root), 'utf-8');
576
- const secondWorkspaceStateBefore = fs.readFileSync(getWorkspaceViewStatePath(second.workspace.root), 'utf-8');
577
-
578
- writeGlobalConfig({
579
- profile: 'core',
580
- delivery: 'commands',
581
- });
582
-
583
- const update = await runCLI(
584
- ['update', first.workspace.root],
585
- { cwd: tempDir, env }
586
- );
587
-
588
- expect(update.exitCode).toBe(1);
589
- expect(`${update.stdout}\n${update.stderr}`).toContain('Run `openspec workspace update`');
590
- expect(update.stdout).not.toContain('Workspace update complete');
591
- expect(update.stdout).not.toContain('Multiple OpenSpec workspaces are known');
592
- expect(fs.readFileSync(getWorkspaceViewStatePath(first.workspace.root), 'utf-8')).toBe(
593
- firstWorkspaceStateBefore
594
- );
595
- expect(fs.readFileSync(getWorkspaceViewStatePath(second.workspace.root), 'utf-8')).toBe(
596
- secondWorkspaceStateBefore
597
- );
598
- expect(fs.existsSync(path.join(first.workspace.root, '.codex', 'skills', 'openspec-propose', 'SKILL.md'))).toBe(false);
599
- expect(fs.existsSync(path.join(second.workspace.root, '.codex', 'skills', 'openspec-propose', 'SKILL.md'))).toBe(false);
600
- });
601
-
602
- it('supports named and flag-selected workspace updates with explicit agent changes', async () => {
603
- const api = mkdir('repos/api');
604
- writeGlobalConfig({
605
- profile: 'custom',
606
- delivery: 'skills',
607
- workflows: ['apply'],
608
- });
609
- const setup = await setupWorkspace('agent-change', [`api=${api}`], ['--tools', 'codex']);
610
- const workspaceRoot = setup.workspace.root;
611
- const userSkillDir = path.join(workspaceRoot, '.codex', 'skills', 'user-skill');
612
- fs.mkdirSync(userSkillDir, { recursive: true });
613
- fs.writeFileSync(path.join(userSkillDir, 'SKILL.md'), 'user-owned\n');
614
-
615
- const addAgent = await runCLI(
616
- ['workspace', 'update', 'agent-change', '--tools', 'codex,claude', '--json'],
617
- { cwd: tempDir, env }
618
- );
619
- expect(addAgent.exitCode).toBe(0);
620
- const addPayload = parseJson(addAgent);
621
- expect(addPayload.workspace_skills.refreshed).toEqual([
622
- expect.objectContaining({ tool_id: 'codex', workflow_ids: ['apply'] }),
623
- ]);
624
- expect(addPayload.workspace_skills.added).toEqual([
625
- expect.objectContaining({ tool_id: 'claude', workflow_ids: ['apply'] }),
626
- ]);
627
- expect(fs.existsSync(path.join(workspaceRoot, '.claude', 'skills', 'openspec-apply-change', 'SKILL.md'))).toBe(true);
628
- expect(readWorkspaceState(workspaceRoot).workspace_skills?.selected_agents).toEqual(['codex', 'claude']);
629
-
630
- const removeAgent = await runCLI(
631
- ['workspace', 'update', '--workspace', 'agent-change', '--tools', 'claude', '--json'],
632
- { cwd: tempDir, env }
633
- );
634
- expect(removeAgent.exitCode).toBe(0);
635
- const removePayload = parseJson(removeAgent);
636
- expect(removePayload.workspace_skills.removed).toEqual([
637
- expect.objectContaining({
638
- tool_id: 'codex',
639
- reason: 'agent_unselected',
640
- workflow_ids: ['apply'],
641
- }),
642
- ]);
643
- expect(removePayload.workspace_skills.refreshed).toEqual([
644
- expect.objectContaining({ tool_id: 'claude', workflow_ids: ['apply'] }),
645
- ]);
646
- expect(fs.existsSync(path.join(workspaceRoot, '.codex', 'skills', 'openspec-apply-change'))).toBe(false);
647
- expect(fs.existsSync(path.join(userSkillDir, 'SKILL.md'))).toBe(true);
648
- expect(readWorkspaceState(workspaceRoot).workspace_skills?.selected_agents).toEqual(['claude']);
649
- });
650
-
651
- it('does not remove unmanaged skill directories that collide with OpenSpec workflow names', async () => {
652
- const api = mkdir('repos/api');
653
- writeGlobalConfig({
654
- profile: 'custom',
655
- delivery: 'skills',
656
- workflows: ['verify'],
657
- });
658
- const setup = await setupWorkspace('unmanaged-collision', [`api=${api}`], ['--tools', 'codex']);
659
- const workspaceRoot = setup.workspace.root;
660
- const collidingSkillDir = path.join(workspaceRoot, '.codex', 'skills', 'openspec-verify-change');
661
- fs.writeFileSync(path.join(collidingSkillDir, 'SKILL.md'), 'name: user-owned-verify\n');
662
-
663
- const update = await runCLI(
664
- ['workspace', 'update', '--workspace', 'unmanaged-collision', '--tools', 'none', '--json'],
665
- { cwd: tempDir, env }
666
- );
667
-
668
- expect(update.exitCode).toBe(0);
669
- expect(parseJson(update).workspace_skills.removed).toEqual([]);
670
- expect(fs.existsSync(path.join(collidingSkillDir, 'SKILL.md'))).toBe(true);
671
- expect(readWorkspaceState(workspaceRoot).workspace_skills?.selected_agents).toEqual([]);
672
- });
673
-
674
- it('does not record workspace skills as applied when an update fails', async () => {
675
- const api = mkdir('repos/api');
676
- writeGlobalConfig({
677
- profile: 'custom',
678
- delivery: 'skills',
679
- workflows: ['apply'],
680
- });
681
- const setup = await setupWorkspace('failed-update-state', [`api=${api}`], ['--tools', 'codex']);
682
- const workspaceRoot = setup.workspace.root;
683
- const blockingSkillPath = path.join(workspaceRoot, '.codex', 'skills', 'openspec-propose');
684
- fs.writeFileSync(blockingSkillPath, 'blocks generated skill directory\n');
685
-
686
- writeGlobalConfig({
687
- profile: 'core',
688
- delivery: 'skills',
689
- });
690
-
691
- const update = await runCLI(
692
- ['workspace', 'update', '--workspace', 'failed-update-state', '--json'],
693
- { cwd: tempDir, env }
694
- );
695
-
696
- expect(update.exitCode).toBe(1);
697
- expect(parseJson(update).workspace_skills.failed).toEqual([
698
- expect.objectContaining({
699
- tool_id: 'codex',
700
- }),
701
- ]);
702
- expect(readWorkspaceState(workspaceRoot).workspace_skills).toEqual(
703
- expect.objectContaining({
704
- selected_agents: ['codex'],
705
- last_applied_profile: 'custom',
706
- last_applied_workflow_ids: ['apply'],
707
- })
708
- );
709
- });
710
-
711
- it('reports a no-op workspace update when no stored skill selection exists', async () => {
712
- const api = mkdir('repos/api');
713
- const linkedEntriesBefore = fs.readdirSync(api).sort();
714
- const setup = await setupWorkspace('no-stored-skills', [`api=${api}`]);
715
- const agentsPath = path.join(setup.workspace.root, 'AGENTS.md');
716
- fs.writeFileSync(
717
- agentsPath,
718
- `# User Notes
719
-
720
- ${WORKSPACE_GUIDANCE_START_MARKER}
721
- # OpenSpec Workspace Guidance
722
-
723
- Use \`changes/\` for workspace-level planning.
724
- ${WORKSPACE_GUIDANCE_END_MARKER}
725
- `
726
- );
727
-
728
- const update = await runCLI(
729
- ['workspace', 'update', '--workspace', 'no-stored-skills', '--json'],
730
- { cwd: tempDir, env }
731
- );
732
- expect(update.exitCode).toBe(0);
733
- expect(parseJson(update).workspace_skills).toEqual(
734
- expect.objectContaining({
735
- selected_agents: [],
736
- generated: [],
737
- added: [],
738
- refreshed: [],
739
- removed: [],
740
- failed: [],
741
- skipped: [
742
- expect.objectContaining({
743
- reason: 'no_stored_agent_selection',
744
- message: expect.stringContaining('--tools <ids>'),
745
- }),
746
- ],
747
- })
748
- );
749
- const agentsContent = fs.readFileSync(agentsPath, 'utf-8');
750
- expect(agentsContent).toContain('# User Notes');
751
- expect(agentsContent).toContain(
752
- 'Use initiatives for durable cross-team or cross-repo intent'
753
- );
754
- expect(agentsContent).not.toContain('Use `changes/` for workspace-level planning');
755
- expect(fs.readdirSync(api).sort()).toEqual(linkedEntriesBefore);
756
- expect(readWorkspaceState(setup.workspace.root).workspace_skills).toBeUndefined();
757
- expect(fs.existsSync(path.join(setup.workspace.root, '.codex'))).toBe(false);
758
- });
759
-
760
- it('rejects invalid workspace setup tool IDs with structured JSON status', async () => {
761
- const api = mkdir('repos/api');
762
- const invalid = await runCLI(
763
- [
764
- 'workspace',
765
- 'setup',
766
- '--no-interactive',
767
- '--json',
768
- '--name',
769
- 'invalid-skills',
770
- '--link',
771
- `api=${api}`,
772
- '--tools',
773
- 'codex,not-real',
774
- ],
775
- { cwd: tempDir, env }
776
- );
777
-
778
- expect(invalid.exitCode).toBe(1);
779
- expect(parseJson(invalid).status[0]).toEqual(
780
- expect.objectContaining({
781
- code: 'invalid_workspace_setup_tools',
782
- target: 'workspace.skills',
783
- message: expect.stringContaining('not-real'),
784
- })
785
- );
786
-
787
- const setup = await setupWorkspace('update-invalid-skills', [`api=${api}`]);
788
- const invalidUpdate = await runCLI(
789
- [
790
- 'workspace',
791
- 'update',
792
- '--workspace',
793
- 'update-invalid-skills',
794
- '--json',
795
- '--tools',
796
- 'codex,not-real',
797
- ],
798
- { cwd: tempDir, env }
799
- );
800
- expect(invalidUpdate.exitCode).toBe(1);
801
- expect(parseJson(invalidUpdate).status[0]).toEqual(
802
- expect.objectContaining({
803
- code: 'invalid_workspace_update_tools',
804
- target: 'workspace.skills',
805
- message: expect.stringContaining('not-real'),
806
- })
807
- );
808
- expect(readWorkspaceState(setup.workspace.root).workspace_skills).toBeUndefined();
809
- });
810
-
811
- it('preserves equals signs in inferred and explicit setup link paths', async () => {
812
- const inferred = mkdir('repos/foo=bar');
813
- const explicit = mkdir('repos/api=service');
814
- const expectedInferred = expectedExistingPath(inferred);
815
- const expectedExplicit = expectedExistingPath(explicit);
816
-
817
- const setup = await setupWorkspace('equals-paths', [inferred, `api=${explicit}`]);
818
-
819
- expect(setup.workspace.links).toEqual([
820
- expect.objectContaining({
821
- name: 'api',
822
- path: expectedExplicit,
823
- status: [],
824
- }),
825
- expect.objectContaining({
826
- name: 'foo=bar',
827
- path: expectedInferred,
828
- status: [],
829
- }),
830
- ]);
831
-
832
- const workspaceState = readWorkspaceState(setup.workspace.root);
833
- expect(workspaceState.links).toEqual({
834
- api: expectedExplicit,
835
- 'foo=bar': expectedInferred,
836
- });
837
- });
838
-
839
- it('stores non-interactive preferred openers only when --opener is provided', async () => {
840
- const api = mkdir('repos/api');
841
- const codex = await setupWorkspace('codex-workspace', [`api=${api}`], ['--opener', 'codex-cli']);
842
- const legacyCodex = await setupWorkspace('legacy-codex-workspace', [`api=${api}`], ['--opener', 'codex']);
843
- const editor = await setupWorkspace('editor-workspace', [`api=${api}`], ['--opener', 'editor']);
844
- const unset = await setupWorkspace('unset-workspace', [`api=${api}`]);
845
-
846
- expect(readWorkspaceState(codex.workspace.root).preferred_opener).toEqual({
847
- kind: 'agent',
848
- id: 'codex-cli',
849
- });
850
- expect(readWorkspaceState(legacyCodex.workspace.root).preferred_opener).toEqual({
851
- kind: 'agent',
852
- id: 'codex-cli',
853
- });
854
- expect(readWorkspaceState(editor.workspace.root).preferred_opener).toEqual({
855
- kind: 'editor',
856
- id: 'vscode',
857
- });
858
- expect(readWorkspaceState(unset.workspace.root).preferred_opener).toBeUndefined();
859
-
860
- const invalid = await runCLI(
861
- [
862
- 'workspace',
863
- 'setup',
864
- '--no-interactive',
865
- '--json',
866
- '--name',
867
- 'invalid-opener',
868
- '--link',
869
- `api=${api}`,
870
- '--opener',
871
- 'cursor',
872
- ],
873
- { cwd: tempDir, env }
874
- );
875
- expect(invalid.exitCode).toBe(1);
876
- expect(parseJson(invalid).status[0]).toEqual(
877
- expect.objectContaining({
878
- code: 'unsupported_workspace_opener',
879
- target: 'workspace.opener',
880
- })
881
- );
882
- });
883
-
884
- it('resolves relative setup, link, and relink paths before storing local state', async () => {
885
- const project = mkdir('project');
886
- fs.mkdirSync(path.join(project, 'repos', 'api'), { recursive: true });
887
- fs.mkdirSync(path.join(project, 'services', 'billing'), { recursive: true });
888
- fs.mkdirSync(path.join(project, 'archive', 'billing'), { recursive: true });
889
-
890
- const setup = await runCLI(
891
- [
892
- 'workspace',
893
- 'setup',
894
- '--no-interactive',
895
- '--json',
896
- '--name',
897
- 'platform',
898
- '--link',
899
- 'repos/api',
900
- ],
901
- { cwd: project, env }
902
- );
903
- expect(setup.exitCode).toBe(0);
904
-
905
- const setupPayload = parseJson(setup);
906
- expectSameExistingPath(
907
- readWorkspaceState(setupPayload.workspace.root).links.api ?? null,
908
- path.join(project, 'repos', 'api')
909
- );
910
-
911
- const link = await runCLI(['workspace', 'link', 'services/billing', '--json'], {
912
- cwd: project,
913
- env,
914
- });
915
- expect(link.exitCode).toBe(0);
916
- const linkPayload = parseJson(link).link;
917
- expect(linkPayload).toEqual(
918
- expect.objectContaining({
919
- name: 'billing',
920
- path: expect.any(String),
921
- })
922
- );
923
- expectSameExistingPath(linkPayload.path, path.join(project, 'services', 'billing'));
924
-
925
- const relink = await runCLI(
926
- ['workspace', 'relink', 'billing', 'archive/billing', '--json'],
927
- { cwd: project, env }
928
- );
929
- expect(relink.exitCode).toBe(0);
930
- const relinkPayload = parseJson(relink).link;
931
- expect(relinkPayload).toEqual(
932
- expect.objectContaining({
933
- name: 'billing',
934
- path: expect.any(String),
935
- })
936
- );
937
- expectSameExistingPath(relinkPayload.path, path.join(project, 'archive', 'billing'));
938
-
939
- const workspaceLinks = readWorkspaceState(setupPayload.workspace.root).links;
940
- expect(Object.keys(workspaceLinks).sort()).toEqual(['api', 'billing']);
941
- expectSameExistingPath(workspaceLinks.api ?? null, path.join(project, 'repos', 'api'));
942
- expectSameExistingPath(workspaceLinks.billing ?? null, path.join(project, 'archive', 'billing'));
943
- });
944
-
945
- it('canonicalizes existing link directories on Windows before storing local paths', async () => {
946
- const api = mkdir('repos/api');
947
- const canonicalApi = path.join(tempDir, 'canonical', 'api');
948
- const originalPlatform = process.platform;
949
- const canonicalize = vi
950
- .spyOn(FileSystemUtils, 'canonicalizeExistingPath')
951
- .mockImplementation((targetPath) => (targetPath === api ? canonicalApi : targetPath));
952
-
953
- Object.defineProperty(process, 'platform', { value: 'win32' });
954
-
955
- try {
956
- await expect(resolveExistingDirectory(api)).resolves.toBe(canonicalApi);
957
- expect(canonicalize).toHaveBeenCalledWith(api);
958
- } finally {
959
- canonicalize.mockRestore();
960
- Object.defineProperty(process, 'platform', { value: originalPlatform });
961
- }
962
- });
963
-
964
- it('rejects duplicate setup link names without creating or rewriting a workspace', async () => {
965
- const firstApi = mkdir('repos/current/api');
966
- const secondApi = mkdir('repos/archive/api');
967
- const expectedFirstApi = expectedExistingPath(firstApi);
968
-
969
- const duplicate = await runCLI(
970
- [
971
- 'workspace',
972
- 'setup',
973
- '--no-interactive',
974
- '--json',
975
- '--name',
976
- 'platform',
977
- '--link',
978
- firstApi,
979
- '--link',
980
- secondApi,
981
- ],
982
- { cwd: tempDir, env }
983
- );
984
-
985
- expect(duplicate.exitCode).toBe(1);
986
- expect(parseJson(duplicate).status[0]).toEqual(
987
- expect.objectContaining({
988
- code: 'duplicate_link_name',
989
- message: expect.stringContaining(expectedFirstApi),
990
- fix: expect.stringContaining('--link api-alt='),
991
- })
992
- );
993
- expect(fs.existsSync(getWorkspaceRegistryPath({ globalDataDir: path.join(dataHome, 'openspec') }))).toBe(false);
994
- });
995
-
996
- it('removes a partially created workspace when setup fails after creating the root', async () => {
997
- const api = mkdir('repos/api');
998
- const originalDataHome = process.env.XDG_DATA_HOME;
999
- process.env.XDG_DATA_HOME = dataHome;
1000
- const writeFileSpy = vi
1001
- .spyOn(FileSystemUtils, 'writeFile')
1002
- .mockRejectedValueOnce(new Error('disk full'));
1003
-
1004
- try {
1005
- await expect(createManagedWorkspace('platform', { api })).rejects.toMatchObject({
1006
- status: {
1007
- code: 'workspace_create_failed',
1008
- },
1009
- });
1010
- } finally {
1011
- writeFileSpy.mockRestore();
1012
- if (originalDataHome === undefined) {
1013
- delete process.env.XDG_DATA_HOME;
1014
- } else {
1015
- process.env.XDG_DATA_HOME = originalDataHome;
1016
- }
1017
- }
1018
-
1019
- const globalDataDir = path.join(dataHome, 'openspec');
1020
- expect(fs.existsSync(getManagedWorkspaceRoot('platform', { globalDataDir }))).toBe(false);
1021
- expect(fs.existsSync(getWorkspaceRegistryPath({ globalDataDir }))).toBe(false);
1022
- });
1023
-
1024
- it('rejects existing workspace names without overwriting workspace state', async () => {
1025
- const api = mkdir('repos/api');
1026
- const web = mkdir('repos/web');
1027
- const setup = await setupWorkspace('platform', [`api=${api}`]);
1028
- const workspaceRoot = setup.workspace.root;
1029
- const viewBefore = fs.readFileSync(getWorkspaceViewStatePath(workspaceRoot), 'utf-8');
1030
- const markerPath = path.join(workspaceRoot, 'sentinel.txt');
1031
- fs.writeFileSync(markerPath, 'keep me');
1032
-
1033
- const duplicate = await runCLI(
1034
- [
1035
- 'workspace',
1036
- 'setup',
1037
- '--no-interactive',
1038
- '--json',
1039
- '--name',
1040
- 'platform',
1041
- '--link',
1042
- `web=${web}`,
1043
- ],
1044
- { cwd: tempDir, env }
1045
- );
1046
-
1047
- expect(duplicate.exitCode).toBe(1);
1048
- expect(parseJson(duplicate).status[0]).toEqual(
1049
- expect.objectContaining({
1050
- code: 'workspace_already_exists',
1051
- target: 'workspace.name',
1052
- })
1053
- );
1054
- expect(fs.readFileSync(getWorkspaceViewStatePath(workspaceRoot), 'utf-8')).toBe(viewBefore);
1055
- expect(fs.readFileSync(markerPath, 'utf-8')).toBe('keep me');
1056
- });
1057
-
1058
- it('fails setup cleanly for missing automation inputs and JSON without no-interactive', async () => {
1059
- const api = mkdir('repos/api');
1060
-
1061
- const noWorkspaces = await runCLI(['workspace', 'list'], { cwd: tempDir, env });
1062
- expect(noWorkspaces.exitCode).toBe(0);
1063
- expect(noWorkspaces.stdout).toContain("No OpenSpec workspaces found. Run 'openspec workspace setup' first.");
1064
-
1065
- const missing = await runCLI(['workspace', 'setup', '--no-interactive', '--json'], {
1066
- cwd: tempDir,
1067
- env,
1068
- });
1069
- expect(missing.exitCode).toBe(1);
1070
- expect(parseJson(missing).status[0]).toEqual(
1071
- expect.objectContaining({
1072
- code: 'missing_setup_inputs',
1073
- severity: 'error',
1074
- })
1075
- );
1076
-
1077
- const jsonInteractive = await runCLI(
1078
- ['workspace', 'setup', '--json', '--name', 'platform', '--link', api],
1079
- { cwd: tempDir, env }
1080
- );
1081
- expect(jsonInteractive.exitCode).toBe(1);
1082
- expect(parseJson(jsonInteractive).status[0]).toEqual(
1083
- expect.objectContaining({
1084
- code: 'setup_json_requires_no_interactive',
1085
- })
1086
- );
1087
-
1088
- const invalidName = await runCLI(
1089
- ['workspace', 'setup', '--no-interactive', '--json', '--name', 'Bad_Name', '--link', api],
1090
- { cwd: tempDir, env }
1091
- );
1092
- expect(invalidName.exitCode).toBe(1);
1093
- expect(parseJson(invalidName).status[0]).toEqual(
1094
- expect.objectContaining({
1095
- code: 'invalid_workspace_name',
1096
- message: expect.stringContaining('kebab-case'),
1097
- })
1098
- );
1099
-
1100
- const noKnown = await runCLI(['workspace', 'doctor', '--json'], { cwd: tempDir, env });
1101
- expect(noKnown.exitCode).toBe(1);
1102
- expect(parseJson(noKnown).status[0]).toEqual(
1103
- expect.objectContaining({
1104
- code: 'no_known_workspaces',
1105
- })
1106
- );
1107
- });
1108
-
1109
- it('rejects missing setup, link, and relink paths with structured status', async () => {
1110
- const api = mkdir('repos/api');
1111
- const billing = mkdir('repos/billing');
1112
-
1113
- const missingSetupPath = await runCLI(
1114
- [
1115
- 'workspace',
1116
- 'setup',
1117
- '--no-interactive',
1118
- '--json',
1119
- '--name',
1120
- 'missing-setup-path',
1121
- '--link',
1122
- 'missing-api',
1123
- ],
1124
- { cwd: tempDir, env }
1125
- );
1126
- expect(missingSetupPath.exitCode).toBe(1);
1127
- expect(parseJson(missingSetupPath).status[0]).toEqual(
1128
- expect.objectContaining({
1129
- code: 'linked_path_missing',
1130
- target: 'link.path',
1131
- })
1132
- );
1133
-
1134
- await setupWorkspace('platform', [`api=${api}`]);
1135
-
1136
- const missingLinkPath = await runCLI(
1137
- ['workspace', 'link', 'missing-service', '--json'],
1138
- { cwd: tempDir, env }
1139
- );
1140
- expect(missingLinkPath.exitCode).toBe(1);
1141
- expect(parseJson(missingLinkPath).status[0]).toEqual(
1142
- expect.objectContaining({
1143
- code: 'linked_path_missing',
1144
- target: 'link.path',
1145
- })
1146
- );
1147
-
1148
- const link = await runCLI(['workspace', 'link', 'billing', billing, '--json'], {
1149
- cwd: tempDir,
1150
- env,
1151
- });
1152
- expect(link.exitCode).toBe(0);
1153
-
1154
- const missingRelinkPath = await runCLI(
1155
- ['workspace', 'relink', 'billing', 'missing-billing', '--json'],
1156
- { cwd: tempDir, env }
1157
- );
1158
- expect(missingRelinkPath.exitCode).toBe(1);
1159
- expect(parseJson(missingRelinkPath).status[0]).toEqual(
1160
- expect.objectContaining({
1161
- code: 'linked_path_missing',
1162
- target: 'link.path',
1163
- })
1164
- );
1165
- });
1166
-
1167
- it('links, rejects duplicate link names, relinks, and reports unknown relinks', async () => {
1168
- const api = mkdir('repos/api');
1169
- const billing = mkdir('repos/platform/services/billing');
1170
- const billingNew = mkdir('repos/archive/billing');
1171
- const duplicate = mkdir('repos/duplicate-billing');
1172
- const expectedBilling = expectedExistingPath(billing);
1173
- const expectedBillingNew = expectedExistingPath(billingNew);
1174
-
1175
- await setupWorkspace('platform', [`api=${api}`]);
1176
-
1177
- const link = await runCLI(['workspace', 'link', billing, '--json'], { cwd: tempDir, env });
1178
- expect(link.exitCode).toBe(0);
1179
- expect(parseJson(link).link).toEqual(
1180
- expect.objectContaining({
1181
- name: 'billing',
1182
- path: expectedBilling,
1183
- status: [],
1184
- })
1185
- );
1186
-
1187
- const duplicateResult = await runCLI(
1188
- ['workspace', 'link', 'billing', duplicate, '--json'],
1189
- { cwd: tempDir, env }
1190
- );
1191
- expect(duplicateResult.exitCode).toBe(1);
1192
- expect(parseJson(duplicateResult).status[0]).toEqual(
1193
- expect.objectContaining({
1194
- code: 'duplicate_link_name',
1195
- message: expect.stringContaining('already uses that name'),
1196
- })
1197
- );
1198
-
1199
- const relink = await runCLI(['workspace', 'relink', 'billing', billingNew, '--json'], {
1200
- cwd: tempDir,
1201
- env,
1202
- });
1203
- expect(relink.exitCode).toBe(0);
1204
- expect(parseJson(relink).link).toEqual(
1205
- expect.objectContaining({
1206
- name: 'billing',
1207
- path: expectedBillingNew,
1208
- })
1209
- );
1210
-
1211
- const unknown = await runCLI(['workspace', 'relink', 'web', billingNew, '--json'], {
1212
- cwd: tempDir,
1213
- env,
1214
- });
1215
- expect(unknown.exitCode).toBe(1);
1216
- expect(parseJson(unknown).status[0]).toEqual(
1217
- expect.objectContaining({
1218
- code: 'unknown_link_name',
1219
- })
1220
- );
1221
- });
1222
-
1223
- it('links monorepo folders without editing the linked folder', async () => {
1224
- const api = mkdir('repos/api');
1225
- const packageDir = mkdir('monorepo/apps/checkout');
1226
- const expectedPackageDir = expectedExistingPath(packageDir);
1227
- const sentinelPath = path.join(packageDir, 'package.json');
1228
- fs.writeFileSync(sentinelPath, '{"name":"checkout"}\n');
1229
- const entriesBefore = fs.readdirSync(packageDir).sort();
1230
-
1231
- await setupWorkspace('platform', [`api=${api}`]);
1232
-
1233
- const link = await runCLI(['workspace', 'link', packageDir, '--json'], {
1234
- cwd: tempDir,
1235
- env,
1236
- });
1237
-
1238
- expect(link.exitCode).toBe(0);
1239
- expect(parseJson(link).link).toEqual(
1240
- expect.objectContaining({
1241
- name: 'checkout',
1242
- path: expectedPackageDir,
1243
- })
1244
- );
1245
- expect(fs.readFileSync(sentinelPath, 'utf-8')).toBe('{"name":"checkout"}\n');
1246
- expect(fs.readdirSync(packageDir).sort()).toEqual(entriesBefore);
1247
- expect(fs.existsSync(path.join(packageDir, 'openspec'))).toBe(false);
1248
- expect(fs.existsSync(path.join(packageDir, WORKSPACE_METADATA_DIR_NAME))).toBe(false);
1249
- });
1250
-
1251
- it('fails link and relink without rewriting malformed workspace state', async () => {
1252
- const api = mkdir('repos/api');
1253
- const billing = mkdir('repos/billing');
1254
- const setup = await setupWorkspace('broken-local', [`api=${api}`]);
1255
- const statePath = getWorkspaceViewStatePath(setup.workspace.root);
1256
- const malformedState = 'version: 1\npaths: []\n';
1257
- fs.writeFileSync(statePath, malformedState);
1258
-
1259
- const link = await runCLI(
1260
- ['workspace', 'link', 'billing', billing, '--workspace', 'broken-local', '--json'],
1261
- { cwd: tempDir, env }
1262
- );
1263
- expect(link.exitCode).toBe(1);
1264
- expect(parseJson(link).status[0]).toEqual(
1265
- expect.objectContaining({
1266
- code: 'workspace_state_invalid',
1267
- target: 'workspace.state',
1268
- })
1269
- );
1270
- expect(fs.readFileSync(statePath, 'utf-8')).toBe(malformedState);
1271
-
1272
- const relink = await runCLI(
1273
- ['workspace', 'relink', 'api', billing, '--workspace', 'broken-local', '--json'],
1274
- { cwd: tempDir, env }
1275
- );
1276
- expect(relink.exitCode).toBe(1);
1277
- expect(parseJson(relink).status[0]).toEqual(
1278
- expect.objectContaining({
1279
- code: 'workspace_state_invalid',
1280
- target: 'workspace.state',
1281
- })
1282
- );
1283
- expect(fs.readFileSync(statePath, 'utf-8')).toBe(malformedState);
1284
- });
1285
-
1286
- it('drops deleted managed workspace roots from scanned workspace selection', async () => {
1287
- const api = mkdir('repos/api');
1288
- const setup = await setupWorkspace('platform', [`api=${api}`]);
1289
- const registryPath = getWorkspaceRegistryPath({ globalDataDir: path.join(dataHome, 'openspec') });
1290
- expect(fs.existsSync(registryPath)).toBe(false);
1291
-
1292
- fs.rmSync(setup.workspace.root, { recursive: true, force: true });
1293
-
1294
- const list = await runCLI(['workspace', 'list', '--json'], { cwd: tempDir, env });
1295
- expect(list.exitCode).toBe(0);
1296
- expect(parseJson(list).workspaces).toEqual([]);
1297
-
1298
- const doctor = await runCLI(['workspace', 'doctor', '--workspace', 'platform', '--json'], {
1299
- cwd: tempDir,
1300
- env,
1301
- });
1302
- expect(doctor.exitCode).toBe(1);
1303
- expect(parseJson(doctor).status[0]).toEqual(
1304
- expect.objectContaining({
1305
- code: 'workspace_not_found',
1306
- })
1307
- );
1308
- expect(fs.existsSync(registryPath)).toBe(false);
1309
- });
1310
-
1311
- it('reports malformed workspace state in list and doctor without rewriting files', async () => {
1312
- const api = mkdir('repos/api');
1313
- const setup = await setupWorkspace('doctor-local-invalid', [`api=${api}`]);
1314
- const statePath = getWorkspaceViewStatePath(setup.workspace.root);
1315
- const registryPath = getWorkspaceRegistryPath({ globalDataDir: path.join(dataHome, 'openspec') });
1316
- const malformedState = 'version: 1\npaths: []\n';
1317
- expect(fs.existsSync(registryPath)).toBe(false);
1318
- fs.writeFileSync(statePath, malformedState);
1319
-
1320
- const list = await runCLI(['workspace', 'list', '--json'], { cwd: tempDir, env });
1321
- expect(list.exitCode).toBe(0);
1322
- expect(parseJson(list).workspaces[0].status[0]).toEqual(
1323
- expect.objectContaining({
1324
- code: 'workspace_state_invalid',
1325
- })
1326
- );
1327
-
1328
- const humanList = await runCLI(['workspace', 'list'], { cwd: tempDir, env });
1329
- expect(humanList.exitCode).toBe(0);
1330
- expect(humanList.stdout).toContain('Workspace state could not be read');
1331
-
1332
- const doctor = await runCLI(
1333
- ['workspace', 'doctor', '--workspace', 'doctor-local-invalid', '--json'],
1334
- { cwd: tempDir, env }
1335
- );
1336
- expect(doctor.exitCode).toBe(0);
1337
- const doctorPayload = parseJson(doctor);
1338
- expect(doctorPayload.workspace.status[0]).toEqual(
1339
- expect.objectContaining({
1340
- code: 'workspace_state_invalid',
1341
- target: 'workspace.root',
1342
- })
1343
- );
1344
- expect(doctorPayload.workspace.links).toEqual([]);
1345
- expect(fs.readFileSync(statePath, 'utf-8')).toBe(malformedState);
1346
- expect(fs.existsSync(registryPath)).toBe(false);
1347
- });
1348
-
1349
- it('reports missing linked paths without repairing workspace state', async () => {
1350
- const api = mkdir('repos/api');
1351
- const localOnly = mkdir('repos/local-only');
1352
- const setup = await setupWorkspace('platform', [`api=${api}`]);
1353
- const workspaceRoot = setup.workspace.root;
1354
- const registryPath = getWorkspaceRegistryPath({ globalDataDir: path.join(dataHome, 'openspec') });
1355
- const missingApiPath = path.join(tempDir, 'repos', 'missing-api');
1356
- const viewState = `version: 1
1357
- name: platform
1358
- context: null
1359
- links:
1360
- api: ${missingApiPath}
1361
- local-only: ${localOnly}
1362
- `;
1363
- fs.writeFileSync(getWorkspaceViewStatePath(workspaceRoot), viewState);
1364
- expect(fs.existsSync(registryPath)).toBe(false);
1365
-
1366
- const doctor = await runCLI(['workspace', 'doctor', '--workspace', 'platform', '--json'], {
1367
- cwd: tempDir,
1368
- env,
1369
- });
1370
-
1371
- expect(doctor.exitCode).toBe(0);
1372
- const payload = parseJson(doctor);
1373
- expect(payload.workspace.status).toEqual([]);
1374
- expect(payload.workspace.links).toEqual([
1375
- expect.objectContaining({
1376
- name: 'api',
1377
- path: missingApiPath,
1378
- status: [
1379
- expect.objectContaining({
1380
- code: 'linked_path_missing',
1381
- fix: expect.stringContaining('workspace relink api'),
1382
- }),
1383
- ],
1384
- }),
1385
- expect.objectContaining({
1386
- name: 'local-only',
1387
- path: expect.any(String),
1388
- status: [],
1389
- }),
1390
- ]);
1391
- expectSameExistingPath(
1392
- payload.workspace.links.find((link: any) => link.name === 'local-only')?.path ?? null,
1393
- localOnly
1394
- );
1395
- expect(fs.readFileSync(getWorkspaceViewStatePath(workspaceRoot), 'utf-8')).toBe(viewState);
1396
- expect(fs.existsSync(registryPath)).toBe(false);
1397
- });
1398
-
1399
- it('uses current unlisted legacy workspaces for doctor and link without writing a registry', async () => {
1400
- const manualRoot = path.join(tempDir, 'manual-workspace');
1401
- const nested = path.join(manualRoot, WORKSPACE_CHANGES_DIR_NAME, 'add-billing');
1402
- const api = mkdir('repos/api');
1403
-
1404
- fs.mkdirSync(path.join(manualRoot, WORKSPACE_METADATA_DIR_NAME), { recursive: true });
1405
- fs.mkdirSync(nested, { recursive: true });
1406
- fs.writeFileSync(
1407
- path.join(manualRoot, WORKSPACE_METADATA_DIR_NAME, WORKSPACE_LEGACY_SHARED_STATE_FILE_NAME),
1408
- 'version: 1\nname: manual-workspace\nlinks: {}\n'
1409
- );
1410
- fs.writeFileSync(
1411
- path.join(manualRoot, WORKSPACE_METADATA_DIR_NAME, WORKSPACE_LEGACY_LOCAL_STATE_FILE_NAME),
1412
- 'version: 1\npaths: {}\n'
1413
- );
1414
-
1415
- const registryPath = getWorkspaceRegistryPath({ globalDataDir: path.join(dataHome, 'openspec') });
1416
- const doctor = await runCLI(['workspace', 'doctor', '--json'], { cwd: nested, env });
1417
- expect(doctor.exitCode).toBe(0);
1418
- expect(parseJson(doctor).status[0]).toEqual(
1419
- expect.objectContaining({
1420
- code: 'workspace_not_in_known_views',
1421
- severity: 'warning',
1422
- })
1423
- );
1424
- expect(fs.existsSync(registryPath)).toBe(false);
1425
-
1426
- const link = await runCLI(['workspace', 'link', 'api', api, '--json'], {
1427
- cwd: nested,
1428
- env,
1429
- });
1430
- expect(link.exitCode).toBe(0);
1431
- expect(parseJson(link).status[0]).toEqual(
1432
- expect.objectContaining({
1433
- code: 'workspace_not_in_known_views',
1434
- })
1435
- );
1436
-
1437
- expect(fs.existsSync(registryPath)).toBe(false);
1438
- });
1439
-
1440
- it('fails JSON workspace selection when multiple known workspaces are available', async () => {
1441
- const api = mkdir('repos/api');
1442
- const web = mkdir('repos/web');
1443
-
1444
- await setupWorkspace('platform', [`api=${api}`]);
1445
- await setupWorkspace('checkout-web', [`web=${web}`]);
1446
-
1447
- const doctor = await runCLI(['workspace', 'doctor', '--json'], { cwd: tempDir, env });
1448
- expect(doctor.exitCode).toBe(1);
1449
- expect(parseJson(doctor).status[0]).toEqual(
1450
- expect.objectContaining({
1451
- code: 'workspace_selection_ambiguous',
1452
- fix: expect.stringContaining('--workspace <name>'),
1453
- })
1454
- );
1455
- });
1456
-
1457
- it('uses --workspace for explicit selection and reports unknown workspace names', async () => {
1458
- const api = mkdir('repos/api');
1459
- const web = mkdir('repos/web');
1460
-
1461
- await setupWorkspace('platform', [`api=${api}`]);
1462
- const checkout = await setupWorkspace('checkout-web', [`web=${web}`]);
1463
-
1464
- const doctor = await runCLI(
1465
- ['workspace', 'doctor', '--workspace', 'checkout-web', '--json'],
1466
- { cwd: tempDir, env }
1467
- );
1468
- expect(doctor.exitCode).toBe(0);
1469
- expect(parseJson(doctor).workspace).toEqual(
1470
- expect.objectContaining({
1471
- name: 'checkout-web',
1472
- root: expectedExistingPath(checkout.workspace.root),
1473
- })
1474
- );
1475
-
1476
- const unknown = await runCLI(
1477
- ['workspace', 'doctor', '--workspace', 'unknown-workspace', '--json'],
1478
- { cwd: tempDir, env }
1479
- );
1480
- expect(unknown.exitCode).toBe(1);
1481
- expect(parseJson(unknown).status[0]).toEqual(
1482
- expect.objectContaining({
1483
- code: 'workspace_not_found',
1484
- target: 'workspace.name',
1485
- })
1486
- );
1487
- });
1488
-
1489
- it('fails non-interactive ambiguous workspace selection in human output mode', async () => {
1490
- const api = mkdir('repos/api');
1491
- const web = mkdir('repos/web');
1492
-
1493
- await setupWorkspace('platform', [`api=${api}`]);
1494
- await setupWorkspace('checkout-web', [`web=${web}`]);
1495
-
1496
- const doctor = await runCLI(['workspace', 'doctor', '--no-interactive'], {
1497
- cwd: tempDir,
1498
- env,
1499
- });
1500
-
1501
- expect(doctor.exitCode).toBe(1);
1502
- expect(doctor.stderr).toContain('Multiple OpenSpec workspaces are known.');
1503
- expect(doctor.stderr).toContain('Pass --workspace <name>.');
1504
- expect(doctor.stderr).toContain('openspec workspace doctor --workspace <name>');
1505
- });
1506
-
1507
- it('opens a workspace through VS Code editor and agent overrides without changing stored preference', async () => {
1508
- const api = mkdir('repos/api');
1509
- const expectedApi = expectedExistingPath(api);
1510
- const web = mkdir('repos/web');
1511
- const setup = await setupWorkspace('platform', [`api=${api}`, `web=${web}`], ['--opener', 'editor']);
1512
- fs.rmSync(web, { recursive: true, force: true });
1513
- const code = createFakeExecutable('code');
1514
-
1515
- const editorOpen = await runCLI(['workspace', 'open', 'platform', '--no-interactive'], {
1516
- cwd: tempDir,
1517
- env: envWithFakeExecutable(code),
1518
- });
1519
-
1520
- expect(editorOpen.exitCode).toBe(0);
1521
- expect(editorOpen.stdout).toContain('Opening workspace: platform');
1522
- expect(editorOpen.stdout).toContain('Opener: VS Code editor');
1523
- expect(editorOpen.stdout).toContain('web ->');
1524
- const workspaceFolders = JSON.parse(
1525
- fs.readFileSync(getWorkspaceCodeWorkspacePath(setup.workspace.root, 'platform'), 'utf-8')
1526
- ).folders;
1527
- expect(workspaceFolders).toEqual([
1528
- {
1529
- name: 'api',
1530
- path: expectedApi,
1531
- },
1532
- {
1533
- name: 'OpenSpec workspace',
1534
- path: '.',
1535
- },
1536
- ]);
1537
- const editorLaunch = readLaunchLog(code.logPath);
1538
- expect(fs.realpathSync.native(editorLaunch.cwd)).toBe(
1539
- fs.realpathSync.native(setup.workspace.root)
1540
- );
1541
- expect(editorLaunch.args).toEqual([
1542
- getWorkspaceCodeWorkspacePath(expectedExistingPath(setup.workspace.root), 'platform'),
1543
- ]);
1544
-
1545
- const currentWorkspaceOpen = await runCLI(['workspace', 'open', '--editor', '--no-interactive'], {
1546
- cwd: setup.workspace.root,
1547
- env: envWithFakeExecutable(code),
1548
- });
1549
- expect(currentWorkspaceOpen.exitCode).toBe(0);
1550
-
1551
- const codex = createFakeExecutable('codex');
1552
- const codexOpen = await runCLI(
1553
- ['workspace', 'open', '--workspace', 'platform', '--agent', 'codex', '--no-interactive'],
1554
- {
1555
- cwd: tempDir,
1556
- env: envWithFakeExecutable(codex),
1557
- }
1558
- );
1559
-
1560
- expect(codexOpen.exitCode).toBe(0);
1561
- const codexLaunch = readLaunchLog(codex.logPath);
1562
- expect(fs.realpathSync.native(codexLaunch.cwd)).toBe(
1563
- fs.realpathSync.native(setup.workspace.root)
1564
- );
1565
- expect(codexLaunch.args).toEqual([
1566
- '--sandbox',
1567
- 'workspace-write',
1568
- '--add-dir',
1569
- expectedApi,
1570
- 'Open this OpenSpec workspace.',
1571
- ]);
1572
- expect(readWorkspaceState(setup.workspace.root).preferred_opener).toEqual({
1573
- kind: 'editor',
1574
- id: 'vscode',
1575
- });
1576
- });
1577
-
1578
- it('reports workspace open selection errors', async () => {
1579
- const api = mkdir('repos/api');
1580
- const web = mkdir('repos/web');
1581
-
1582
- const noKnown = await runCLI(['workspace', 'open', '--no-interactive'], {
1583
- cwd: tempDir,
1584
- env,
1585
- });
1586
- expect(noKnown.exitCode).toBe(1);
1587
- expect(noKnown.stderr).toContain("No known OpenSpec workspaces. Run 'openspec workspace setup' first.");
1588
-
1589
- await setupWorkspace('platform', [`api=${api}`]);
1590
- await setupWorkspace('checkout-web', [`web=${web}`]);
1591
-
1592
- const conflict = await runCLI(
1593
- ['workspace', 'open', 'platform', '--workspace', 'checkout-web', '--editor', '--no-interactive'],
1594
- { cwd: tempDir, env }
1595
- );
1596
- expect(conflict.exitCode).toBe(1);
1597
- expect(conflict.stderr).toContain("positional 'platform'");
1598
- expect(conflict.stderr).toContain("--workspace 'checkout-web'");
1599
-
1600
- const ambiguous = await runCLI(['workspace', 'open', '--no-interactive'], {
1601
- cwd: tempDir,
1602
- env,
1603
- });
1604
- expect(ambiguous.exitCode).toBe(1);
1605
- expect(ambiguous.stderr).toContain('Known workspaces: checkout-web, platform');
1606
-
1607
- const jsonAmbiguous = await runCLI(['workspace', 'open', '--json'], {
1608
- cwd: tempDir,
1609
- env,
1610
- });
1611
- expect(jsonAmbiguous.exitCode).toBe(1);
1612
- expect(parseJson(jsonAmbiguous).status[0]).toEqual(
1613
- expect.objectContaining({
1614
- code: 'workspace_selection_ambiguous',
1615
- })
1616
- );
1617
- });
1618
-
1619
- it('reports unsupported workspace open options before workspace selection', async () => {
1620
- const unsupported = await runCLI(['workspace', 'open', '--prepare-only'], {
1621
- cwd: tempDir,
1622
- env,
1623
- });
1624
- expect(unsupported.exitCode).toBe(1);
1625
- expect(unsupported.stderr).toContain('future context/query surface');
1626
-
1627
- const changeUnsupported = await runCLI(['workspace', 'open', '--change', 'add-api'], {
1628
- cwd: tempDir,
1629
- env,
1630
- });
1631
- expect(changeUnsupported.exitCode).toBe(1);
1632
- expect(changeUnsupported.stderr).toContain('root workspace open only');
1633
-
1634
- const openerConflict = await runCLI(
1635
- ['workspace', 'open', 'platform', '--agent', 'codex-cli', '--editor', '--no-interactive'],
1636
- {
1637
- cwd: tempDir,
1638
- env,
1639
- }
1640
- );
1641
- expect(openerConflict.exitCode).toBe(1);
1642
- expect(openerConflict.stderr).toContain('either --agent <tool> or --editor');
1643
- });
1644
-
1645
- it('reports unset and unavailable workspace opener errors', async () => {
1646
- const api = mkdir('repos/api');
1647
- const platform = await setupWorkspace('platform', [`api=${api}`]);
1648
-
1649
- const unset = await runCLI(['workspace', 'open', 'platform', '--no-interactive'], {
1650
- cwd: tempDir,
1651
- env,
1652
- });
1653
- expect(unset.exitCode).toBe(1);
1654
- expect(unset.stderr).toContain('does not have a preferred opener');
1655
-
1656
- fs.writeFileSync(
1657
- getWorkspaceViewStatePath(platform.workspace.root),
1658
- `version: 1
1659
- name: platform
1660
- context: null
1661
- links:
1662
- api: ${api}
1663
- preferred_opener:
1664
- kind: editor
1665
- id: vscode
1666
- `
1667
- );
1668
- const unavailable = await runCLI(['workspace', 'open', 'platform', '--no-interactive'], {
1669
- cwd: tempDir,
1670
- env: {
1671
- ...env,
1672
- PATH: '',
1673
- },
1674
- });
1675
- expect(unavailable.exitCode).toBe(1);
1676
- expect(unavailable.stderr).toContain("'code' was not found on PATH");
1677
- expect(unavailable.stderr).toContain(
1678
- getWorkspaceCodeWorkspacePath(expectedExistingPath(platform.workspace.root), 'platform')
1679
- );
1680
- });
1681
-
1682
- it('prints readable human output for setup, list, and doctor', async () => {
1683
- const api = mkdir('repos/api');
1684
- const expectedApi = expectedExistingPath(api);
1685
-
1686
- const setup = await runCLI(
1687
- ['workspace', 'setup', '--no-interactive', '--name', 'platform', '--link', `api=${api}`],
1688
- { cwd: tempDir, env }
1689
- );
1690
- expect(setup.exitCode).toBe(0);
1691
- expect(setup.stdout).toContain('Workspace setup complete');
1692
- expect(setup.stdout).toContain('OpenSpec workspaces (1)');
1693
- expect(setup.stdout).toContain('Location:');
1694
- expect(setup.stdout).not.toContain('Root:');
1695
- expect(setup.stdout).toContain('Linked repos or folders (1):');
1696
- expect(setup.stdout).toContain(`api -> ${expectedApi}`);
1697
- expect(setup.stdout).toContain('Workspace check:');
1698
- expect(setup.stdout).toContain('No workspace issues found.');
1699
- expect(setup.stdout).toContain('Next useful commands:');
1700
-
1701
- const list = await runCLI(['workspace', 'list'], { cwd: tempDir, env });
1702
- expect(list.exitCode).toBe(0);
1703
- expect(list.stdout).toContain('OpenSpec workspaces (1)');
1704
- expect(list.stdout).toContain('platform');
1705
- expect(list.stdout).toContain('Location:');
1706
- expect(list.stdout).not.toContain('Root:');
1707
- expect(list.stdout).toContain('Linked repos or folders (1):');
1708
- expect(list.stdout).toContain(`api -> ${expectedApi}`);
1709
-
1710
- const doctor = await runCLI(['workspace', 'doctor', '--workspace', 'platform'], {
1711
- cwd: tempDir,
1712
- env,
1713
- });
1714
- expect(doctor.exitCode).toBe(0);
1715
- expect(doctor.stdout).toContain('Workspace: platform');
1716
- expect(doctor.stdout).toContain('Location:');
1717
- expect(doctor.stdout).not.toContain('Root:');
1718
- expect(doctor.stdout).toContain('Linked repos or folders:');
1719
- expect(doctor.stdout).toContain('No workspace issues found.');
1720
- });
1721
-
1722
- it('does not expose workspace create as a public command', async () => {
1723
- const help = await runCLI(['workspace', '--help'], { cwd: tempDir, env });
1724
- expect(help.exitCode).toBe(0);
1725
- expect(help.stdout).toContain('setup');
1726
- expect(help.stdout).toContain('update');
1727
- expect(help.stdout).toContain('link');
1728
- expect(help.stdout).toContain('relink');
1729
- expect(help.stdout).not.toMatch(/\bcreate\b/u);
1730
-
1731
- const updateHelp = await runCLI(['workspace', 'update', '--help'], { cwd: tempDir, env });
1732
- expect(updateHelp.exitCode).toBe(0);
1733
- expect(updateHelp.stdout).toContain('guidance and agent skills');
1734
- expect(updateHelp.stdout).toContain('--workspace');
1735
- expect(updateHelp.stdout).toContain('--tools');
1736
- expect(updateHelp.stdout).toMatch(/Global\s+profile\s+selects workflows/u);
1737
- });
1738
-
1739
- it('registers workspace subcommands for shell completions', () => {
1740
- const workspace = COMMAND_REGISTRY.find((command) => command.name === 'workspace');
1741
- const setup = workspace?.subcommands?.find((command) => command.name === 'setup');
1742
- const link = workspace?.subcommands?.find((command) => command.name === 'link');
1743
- const relink = workspace?.subcommands?.find((command) => command.name === 'relink');
1744
- const update = workspace?.subcommands?.find((command) => command.name === 'update');
1745
- const open = workspace?.subcommands?.find((command) => command.name === 'open');
1746
-
1747
- expect(workspace?.subcommands?.map((command) => command.name)).toEqual([
1748
- 'setup',
1749
- 'list',
1750
- 'ls',
1751
- 'link',
1752
- 'relink',
1753
- 'doctor',
1754
- 'update',
1755
- 'open',
1756
- ]);
1757
- expect(setup?.flags?.some((flag) => flag.name === 'opener')).toBe(true);
1758
- expect(setup?.flags?.find((flag) => flag.name === 'tools')?.description).toContain(
1759
- 'Install OpenSpec skills'
1760
- );
1761
- expect(setup?.flags?.find((flag) => flag.name === 'opener')?.values).toEqual([
1762
- 'codex-cli',
1763
- 'claude',
1764
- 'github-copilot',
1765
- 'editor',
1766
- ]);
1767
- expect(link?.positionals).toEqual([
1768
- { name: 'name-or-path', type: 'path', optional: true },
1769
- { name: 'path', type: 'path', optional: true },
1770
- ]);
1771
- expect(relink?.positionals).toEqual([
1772
- { name: 'name' },
1773
- { name: 'path', type: 'path' },
1774
- ]);
1775
- expect(update?.positionals).toEqual([
1776
- { name: 'name', optional: true },
1777
- ]);
1778
- expect(update?.flags?.map((flag) => flag.name)).toEqual([
1779
- 'workspace',
1780
- 'tools',
1781
- 'json',
1782
- 'no-interactive',
1783
- ]);
1784
- expect(update?.description).toContain('guidance and agent skills');
1785
- expect(update?.flags?.find((flag) => flag.name === 'tools')?.description).toContain(
1786
- 'global profile selects workflows'
1787
- );
1788
- expect(update?.flags?.find((flag) => flag.name === 'tools')?.description).toContain(
1789
- 'skills-only'
1790
- );
1791
- expect(open?.positionals).toEqual([
1792
- { name: 'name', optional: true },
1793
- ]);
1794
- expect(open?.flags?.find((flag) => flag.name === 'agent')?.values).toEqual([
1795
- 'codex-cli',
1796
- 'claude',
1797
- 'github-copilot',
1798
- ]);
1799
- expect(open?.flags?.map((flag) => flag.name)).toEqual([
1800
- 'workspace',
1801
- 'initiative',
1802
- 'store',
1803
- 'store-path',
1804
- 'agent',
1805
- 'editor',
1806
- 'prepare-only',
1807
- 'json',
1808
- 'change',
1809
- 'no-interactive',
1810
- ]);
1811
- });
1812
- });