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