@seanmars/tospec 0.4.2
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/LICENSE +21 -0
- package/README.md +114 -0
- package/assets/dashboard/app.js +1032 -0
- package/assets/dashboard/chart.umd.js +14 -0
- package/assets/dashboard/index.html +30 -0
- package/assets/dashboard/style.css +675 -0
- package/bin/tospec.js +5 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +330 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/config.d.ts +36 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +486 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/dashboard.d.ts +40 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +385 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/shared-output.d.ts +24 -0
- package/dist/commands/shared-output.d.ts.map +1 -0
- package/dist/commands/shared-output.js +51 -0
- package/dist/commands/shared-output.js.map +1 -0
- package/dist/commands/show.d.ts +17 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +164 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/validate.d.ts +51 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +416 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/workflow/index.d.ts +17 -0
- package/dist/commands/workflow/index.d.ts.map +1 -0
- package/dist/commands/workflow/index.js +12 -0
- package/dist/commands/workflow/index.js.map +1 -0
- package/dist/commands/workflow/instructions.d.ts +35 -0
- package/dist/commands/workflow/instructions.d.ts.map +1 -0
- package/dist/commands/workflow/instructions.js +359 -0
- package/dist/commands/workflow/instructions.js.map +1 -0
- package/dist/commands/workflow/new-change.d.ts +17 -0
- package/dist/commands/workflow/new-change.d.ts.map +1 -0
- package/dist/commands/workflow/new-change.js +97 -0
- package/dist/commands/workflow/new-change.js.map +1 -0
- package/dist/commands/workflow/schemas.d.ts +10 -0
- package/dist/commands/workflow/schemas.d.ts.map +1 -0
- package/dist/commands/workflow/schemas.js +33 -0
- package/dist/commands/workflow/schemas.js.map +1 -0
- package/dist/commands/workflow/shared.d.ts +63 -0
- package/dist/commands/workflow/shared.d.ts.map +1 -0
- package/dist/commands/workflow/shared.js +111 -0
- package/dist/commands/workflow/shared.js.map +1 -0
- package/dist/commands/workflow/status.d.ts +14 -0
- package/dist/commands/workflow/status.d.ts.map +1 -0
- package/dist/commands/workflow/status.js +77 -0
- package/dist/commands/workflow/status.js.map +1 -0
- package/dist/commands/workflow/templates.d.ts +16 -0
- package/dist/commands/workflow/templates.d.ts.map +1 -0
- package/dist/commands/workflow/templates.js +60 -0
- package/dist/commands/workflow/templates.js.map +1 -0
- package/dist/core/archive.d.ts +33 -0
- package/dist/core/archive.d.ts.map +1 -0
- package/dist/core/archive.js +585 -0
- package/dist/core/archive.js.map +1 -0
- package/dist/core/artifact-graph/graph.d.ts +56 -0
- package/dist/core/artifact-graph/graph.d.ts.map +1 -0
- package/dist/core/artifact-graph/graph.js +141 -0
- package/dist/core/artifact-graph/graph.js.map +1 -0
- package/dist/core/artifact-graph/index.d.ts +9 -0
- package/dist/core/artifact-graph/index.d.ts.map +1 -0
- package/dist/core/artifact-graph/index.js +14 -0
- package/dist/core/artifact-graph/index.js.map +1 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +188 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -0
- package/dist/core/artifact-graph/instruction-loader.js +243 -0
- package/dist/core/artifact-graph/instruction-loader.js.map +1 -0
- package/dist/core/artifact-graph/outputs.d.ts +14 -0
- package/dist/core/artifact-graph/outputs.d.ts.map +1 -0
- package/dist/core/artifact-graph/outputs.js +39 -0
- package/dist/core/artifact-graph/outputs.js.map +1 -0
- package/dist/core/artifact-graph/resolver.d.ts +81 -0
- package/dist/core/artifact-graph/resolver.d.ts.map +1 -0
- package/dist/core/artifact-graph/resolver.js +257 -0
- package/dist/core/artifact-graph/resolver.js.map +1 -0
- package/dist/core/artifact-graph/schema.d.ts +13 -0
- package/dist/core/artifact-graph/schema.d.ts.map +1 -0
- package/dist/core/artifact-graph/schema.js +108 -0
- package/dist/core/artifact-graph/schema.js.map +1 -0
- package/dist/core/artifact-graph/state.d.ts +12 -0
- package/dist/core/artifact-graph/state.d.ts.map +1 -0
- package/dist/core/artifact-graph/state.js +31 -0
- package/dist/core/artifact-graph/state.js.map +1 -0
- package/dist/core/artifact-graph/types.d.ts +55 -0
- package/dist/core/artifact-graph/types.d.ts.map +1 -0
- package/dist/core/artifact-graph/types.js +38 -0
- package/dist/core/artifact-graph/types.js.map +1 -0
- package/dist/core/available-tools.d.ts +17 -0
- package/dist/core/available-tools.d.ts.map +1 -0
- package/dist/core/available-tools.js +43 -0
- package/dist/core/available-tools.js.map +1 -0
- package/dist/core/change-metadata/index.d.ts +2 -0
- package/dist/core/change-metadata/index.d.ts.map +1 -0
- package/dist/core/change-metadata/index.js +2 -0
- package/dist/core/change-metadata/index.js.map +1 -0
- package/dist/core/change-metadata/schema.d.ts +12 -0
- package/dist/core/change-metadata/schema.d.ts.map +1 -0
- package/dist/core/change-metadata/schema.js +15 -0
- package/dist/core/change-metadata/schema.js.map +1 -0
- package/dist/core/change-presenter.d.ts +24 -0
- package/dist/core/change-presenter.d.ts.map +1 -0
- package/dist/core/change-presenter.js +135 -0
- package/dist/core/change-presenter.js.map +1 -0
- package/dist/core/change-status-policy.d.ts +35 -0
- package/dist/core/change-status-policy.d.ts.map +1 -0
- package/dist/core/change-status-policy.js +34 -0
- package/dist/core/change-status-policy.js.map +1 -0
- package/dist/core/command-generation/adapters/claude.d.ts +14 -0
- package/dist/core/command-generation/adapters/claude.d.ts.map +1 -0
- package/dist/core/command-generation/adapters/claude.js +38 -0
- package/dist/core/command-generation/adapters/claude.js.map +1 -0
- package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
- package/dist/core/command-generation/adapters/cursor.d.ts.map +1 -0
- package/dist/core/command-generation/adapters/cursor.js +31 -0
- package/dist/core/command-generation/adapters/cursor.js.map +1 -0
- package/dist/core/command-generation/adapters/index.d.ts +8 -0
- package/dist/core/command-generation/adapters/index.d.ts.map +1 -0
- package/dist/core/command-generation/adapters/index.js +8 -0
- package/dist/core/command-generation/adapters/index.js.map +1 -0
- package/dist/core/command-generation/generator.d.ts +21 -0
- package/dist/core/command-generation/generator.d.ts.map +1 -0
- package/dist/core/command-generation/generator.js +27 -0
- package/dist/core/command-generation/generator.js.map +1 -0
- package/dist/core/command-generation/index.d.ts +22 -0
- package/dist/core/command-generation/index.d.ts.map +1 -0
- package/dist/core/command-generation/index.js +24 -0
- package/dist/core/command-generation/index.js.map +1 -0
- package/dist/core/command-generation/registry.d.ts +24 -0
- package/dist/core/command-generation/registry.d.ts.map +1 -0
- package/dist/core/command-generation/registry.js +38 -0
- package/dist/core/command-generation/registry.js.map +1 -0
- package/dist/core/command-generation/types.d.ts +56 -0
- package/dist/core/command-generation/types.d.ts.map +1 -0
- package/dist/core/command-generation/types.js +8 -0
- package/dist/core/command-generation/types.js.map +1 -0
- package/dist/core/command-generation/yaml.d.ts +22 -0
- package/dist/core/command-generation/yaml.d.ts.map +1 -0
- package/dist/core/command-generation/yaml.js +38 -0
- package/dist/core/command-generation/yaml.js.map +1 -0
- package/dist/core/config-prompts.d.ts +9 -0
- package/dist/core/config-prompts.d.ts.map +1 -0
- package/dist/core/config-prompts.js +34 -0
- package/dist/core/config-prompts.js.map +1 -0
- package/dist/core/config-schema.d.ts +87 -0
- package/dist/core/config-schema.d.ts.map +1 -0
- package/dist/core/config-schema.js +239 -0
- package/dist/core/config-schema.js.map +1 -0
- package/dist/core/config.d.ts +19 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +14 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/converters/json-converter.d.ts +6 -0
- package/dist/core/converters/json-converter.d.ts.map +1 -0
- package/dist/core/converters/json-converter.js +51 -0
- package/dist/core/converters/json-converter.js.map +1 -0
- package/dist/core/dashboard-activity.d.ts +50 -0
- package/dist/core/dashboard-activity.d.ts.map +1 -0
- package/dist/core/dashboard-activity.js +147 -0
- package/dist/core/dashboard-activity.js.map +1 -0
- package/dist/core/dashboard-data.d.ts +100 -0
- package/dist/core/dashboard-data.d.ts.map +1 -0
- package/dist/core/dashboard-data.js +368 -0
- package/dist/core/dashboard-data.js.map +1 -0
- package/dist/core/global-config.d.ts +51 -0
- package/dist/core/global-config.d.ts.map +1 -0
- package/dist/core/global-config.js +124 -0
- package/dist/core/global-config.js.map +1 -0
- package/dist/core/id.d.ts +17 -0
- package/dist/core/id.d.ts.map +1 -0
- package/dist/core/id.js +30 -0
- package/dist/core/id.js.map +1 -0
- package/dist/core/init.d.ts +34 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +495 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/list.d.ts +12 -0
- package/dist/core/list.d.ts.map +1 -0
- package/dist/core/list.js +192 -0
- package/dist/core/list.js.map +1 -0
- package/dist/core/migrate.d.ts +52 -0
- package/dist/core/migrate.d.ts.map +1 -0
- package/dist/core/migrate.js +300 -0
- package/dist/core/migrate.js.map +1 -0
- package/dist/core/parsers/change-parser.d.ts +19 -0
- package/dist/core/parsers/change-parser.d.ts.map +1 -0
- package/dist/core/parsers/change-parser.js +204 -0
- package/dist/core/parsers/change-parser.js.map +1 -0
- package/dist/core/parsers/markdown-parser.d.ts +29 -0
- package/dist/core/parsers/markdown-parser.d.ts.map +1 -0
- package/dist/core/parsers/markdown-parser.js +181 -0
- package/dist/core/parsers/markdown-parser.js.map +1 -0
- package/dist/core/parsers/requirement-blocks.d.ts +37 -0
- package/dist/core/parsers/requirement-blocks.d.ts.map +1 -0
- package/dist/core/parsers/requirement-blocks.js +201 -0
- package/dist/core/parsers/requirement-blocks.js.map +1 -0
- package/dist/core/parsers/requirement-text.d.ts +55 -0
- package/dist/core/parsers/requirement-text.d.ts.map +1 -0
- package/dist/core/parsers/requirement-text.js +136 -0
- package/dist/core/parsers/requirement-text.js.map +1 -0
- package/dist/core/parsers/spec-structure.d.ts +9 -0
- package/dist/core/parsers/spec-structure.d.ts.map +1 -0
- package/dist/core/parsers/spec-structure.js +88 -0
- package/dist/core/parsers/spec-structure.js.map +1 -0
- package/dist/core/planning-home.d.ts +16 -0
- package/dist/core/planning-home.d.ts.map +1 -0
- package/dist/core/planning-home.js +67 -0
- package/dist/core/planning-home.js.map +1 -0
- package/dist/core/profiles.d.ts +24 -0
- package/dist/core/profiles.d.ts.map +1 -0
- package/dist/core/profiles.js +27 -0
- package/dist/core/profiles.js.map +1 -0
- package/dist/core/project-config.d.ts +100 -0
- package/dist/core/project-config.d.ts.map +1 -0
- package/dist/core/project-config.js +389 -0
- package/dist/core/project-config.js.map +1 -0
- package/dist/core/references.d.ts +40 -0
- package/dist/core/references.d.ts.map +1 -0
- package/dist/core/references.js +11 -0
- package/dist/core/references.js.map +1 -0
- package/dist/core/root-selection.d.ts +57 -0
- package/dist/core/root-selection.d.ts.map +1 -0
- package/dist/core/root-selection.js +98 -0
- package/dist/core/root-selection.js.map +1 -0
- package/dist/core/schemas/base.schema.d.ts +13 -0
- package/dist/core/schemas/base.schema.d.ts.map +1 -0
- package/dist/core/schemas/base.schema.js +18 -0
- package/dist/core/schemas/base.schema.js.map +1 -0
- package/dist/core/schemas/change.schema.d.ts +73 -0
- package/dist/core/schemas/change.schema.d.ts.map +1 -0
- package/dist/core/schemas/change.schema.js +31 -0
- package/dist/core/schemas/change.schema.js.map +1 -0
- package/dist/core/schemas/index.d.ts +4 -0
- package/dist/core/schemas/index.d.ts.map +1 -0
- package/dist/core/schemas/index.js +4 -0
- package/dist/core/schemas/index.js.map +1 -0
- package/dist/core/schemas/spec.schema.d.ts +18 -0
- package/dist/core/schemas/spec.schema.d.ts.map +1 -0
- package/dist/core/schemas/spec.schema.js +15 -0
- package/dist/core/schemas/spec.schema.js.map +1 -0
- package/dist/core/shared/index.d.ts +8 -0
- package/dist/core/shared/index.d.ts.map +1 -0
- package/dist/core/shared/index.js +8 -0
- package/dist/core/shared/index.js.map +1 -0
- package/dist/core/shared/skill-generation.d.ts +63 -0
- package/dist/core/shared/skill-generation.d.ts.map +1 -0
- package/dist/core/shared/skill-generation.js +125 -0
- package/dist/core/shared/skill-generation.js.map +1 -0
- package/dist/core/shared/tool-detection.d.ts +96 -0
- package/dist/core/shared/tool-detection.d.ts.map +1 -0
- package/dist/core/shared/tool-detection.js +207 -0
- package/dist/core/shared/tool-detection.js.map +1 -0
- package/dist/core/spec-presenter.d.ts +17 -0
- package/dist/core/spec-presenter.d.ts.map +1 -0
- package/dist/core/spec-presenter.js +98 -0
- package/dist/core/spec-presenter.js.map +1 -0
- package/dist/core/specs-apply.d.ts +78 -0
- package/dist/core/specs-apply.d.ts.map +1 -0
- package/dist/core/specs-apply.js +429 -0
- package/dist/core/specs-apply.js.map +1 -0
- package/dist/core/templates/index.d.ts +8 -0
- package/dist/core/templates/index.d.ts.map +1 -0
- package/dist/core/templates/index.js +9 -0
- package/dist/core/templates/index.js.map +1 -0
- package/dist/core/templates/skill-templates.d.ts +14 -0
- package/dist/core/templates/skill-templates.d.ts.map +1 -0
- package/dist/core/templates/skill-templates.js +13 -0
- package/dist/core/templates/skill-templates.js.map +1 -0
- package/dist/core/templates/types.d.ts +20 -0
- package/dist/core/templates/types.d.ts.map +1 -0
- package/dist/core/templates/types.js +5 -0
- package/dist/core/templates/types.js.map +1 -0
- package/dist/core/templates/workflows/apply.d.ts +10 -0
- package/dist/core/templates/workflows/apply.d.ts.map +1 -0
- package/dist/core/templates/workflows/apply.js +81 -0
- package/dist/core/templates/workflows/apply.js.map +1 -0
- package/dist/core/templates/workflows/archive.d.ts +10 -0
- package/dist/core/templates/workflows/archive.d.ts.map +1 -0
- package/dist/core/templates/workflows/archive.js +75 -0
- package/dist/core/templates/workflows/archive.js.map +1 -0
- package/dist/core/templates/workflows/explore.d.ts +10 -0
- package/dist/core/templates/workflows/explore.d.ts.map +1 -0
- package/dist/core/templates/workflows/explore.js +75 -0
- package/dist/core/templates/workflows/explore.js.map +1 -0
- package/dist/core/templates/workflows/issue.d.ts +10 -0
- package/dist/core/templates/workflows/issue.d.ts.map +1 -0
- package/dist/core/templates/workflows/issue.js +89 -0
- package/dist/core/templates/workflows/issue.js.map +1 -0
- package/dist/core/templates/workflows/propose.d.ts +10 -0
- package/dist/core/templates/workflows/propose.d.ts.map +1 -0
- package/dist/core/templates/workflows/propose.js +77 -0
- package/dist/core/templates/workflows/propose.js.map +1 -0
- package/dist/core/templates/workflows/sync.d.ts +14 -0
- package/dist/core/templates/workflows/sync.d.ts.map +1 -0
- package/dist/core/templates/workflows/sync.js +86 -0
- package/dist/core/templates/workflows/sync.js.map +1 -0
- package/dist/core/templates/workflows/update.d.ts +10 -0
- package/dist/core/templates/workflows/update.d.ts.map +1 -0
- package/dist/core/templates/workflows/update.js +99 -0
- package/dist/core/templates/workflows/update.js.map +1 -0
- package/dist/core/templates/workflows/verify.d.ts +9 -0
- package/dist/core/templates/workflows/verify.d.ts.map +1 -0
- package/dist/core/templates/workflows/verify.js +44 -0
- package/dist/core/templates/workflows/verify.js.map +1 -0
- package/dist/core/update.d.ts +46 -0
- package/dist/core/update.d.ts.map +1 -0
- package/dist/core/update.js +296 -0
- package/dist/core/update.js.map +1 -0
- package/dist/core/validation/constants.d.ts +34 -0
- package/dist/core/validation/constants.d.ts.map +1 -0
- package/dist/core/validation/constants.js +40 -0
- package/dist/core/validation/constants.js.map +1 -0
- package/dist/core/validation/section-validator.d.ts +19 -0
- package/dist/core/validation/section-validator.d.ts.map +1 -0
- package/dist/core/validation/section-validator.js +59 -0
- package/dist/core/validation/section-validator.js.map +1 -0
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.d.ts.map +1 -0
- package/dist/core/validation/types.js +2 -0
- package/dist/core/validation/types.js.map +1 -0
- package/dist/core/validation/validator.d.ts +51 -0
- package/dist/core/validation/validator.d.ts.map +1 -0
- package/dist/core/validation/validator.js +469 -0
- package/dist/core/validation/validator.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/searchable-multi-select.d.ts +28 -0
- package/dist/prompts/searchable-multi-select.d.ts.map +1 -0
- package/dist/prompts/searchable-multi-select.js +158 -0
- package/dist/prompts/searchable-multi-select.js.map +1 -0
- package/dist/ui/welcome-screen.d.ts +5 -0
- package/dist/ui/welcome-screen.d.ts.map +1 -0
- package/dist/ui/welcome-screen.js +22 -0
- package/dist/ui/welcome-screen.js.map +1 -0
- package/dist/utils/change-metadata.d.ts +55 -0
- package/dist/utils/change-metadata.d.ts.map +1 -0
- package/dist/utils/change-metadata.js +141 -0
- package/dist/utils/change-metadata.js.map +1 -0
- package/dist/utils/change-utils.d.ts +73 -0
- package/dist/utils/change-utils.d.ts.map +1 -0
- package/dist/utils/change-utils.js +194 -0
- package/dist/utils/change-utils.js.map +1 -0
- package/dist/utils/command-references.d.ts +18 -0
- package/dist/utils/command-references.d.ts.map +1 -0
- package/dist/utils/command-references.js +20 -0
- package/dist/utils/command-references.js.map +1 -0
- package/dist/utils/file-system.d.ts +41 -0
- package/dist/utils/file-system.d.ts.map +1 -0
- package/dist/utils/file-system.js +320 -0
- package/dist/utils/file-system.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/interactive.d.ts +18 -0
- package/dist/utils/interactive.d.ts.map +1 -0
- package/dist/utils/interactive.js +21 -0
- package/dist/utils/interactive.js.map +1 -0
- package/dist/utils/item-discovery.d.ts +14 -0
- package/dist/utils/item-discovery.d.ts.map +1 -0
- package/dist/utils/item-discovery.js +104 -0
- package/dist/utils/item-discovery.js.map +1 -0
- package/dist/utils/link.d.ts +22 -0
- package/dist/utils/link.d.ts.map +1 -0
- package/dist/utils/link.js +85 -0
- package/dist/utils/link.js.map +1 -0
- package/dist/utils/match.d.ts +3 -0
- package/dist/utils/match.d.ts.map +1 -0
- package/dist/utils/match.js +22 -0
- package/dist/utils/match.js.map +1 -0
- package/dist/utils/shell-detection.d.ts +20 -0
- package/dist/utils/shell-detection.d.ts.map +1 -0
- package/dist/utils/shell-detection.js +41 -0
- package/dist/utils/shell-detection.js.map +1 -0
- package/dist/utils/task-progress.d.ts +32 -0
- package/dist/utils/task-progress.d.ts.map +1 -0
- package/dist/utils/task-progress.js +129 -0
- package/dist/utils/task-progress.js.map +1 -0
- package/dist/utils/timestamp.d.ts +9 -0
- package/dist/utils/timestamp.d.ts.map +1 -0
- package/dist/utils/timestamp.js +18 -0
- package/dist/utils/timestamp.js.map +1 -0
- package/package.json +55 -0
- package/schemas/issue/schema.yaml +34 -0
- package/schemas/issue/templates/spec.md +13 -0
- package/schemas/issue/templates/task.md +21 -0
- package/schemas/issue/templates/ticket.md +2 -0
- package/schemas/sdd/schema.yaml +55 -0
- package/schemas/sdd/templates/design.md +15 -0
- package/schemas/sdd/templates/proposal.md +15 -0
- package/schemas/sdd/templates/spec.md +13 -0
- package/schemas/sdd/templates/tasks.md +11 -0
- package/schemas/sdd/templates/ticket.md +2 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { isInteractive } from '../utils/interactive.js';
|
|
2
|
+
import { getActiveChangeIds, getSpecIds } from '../utils/item-discovery.js';
|
|
3
|
+
import { resolveRootForCommand, toRootOutput, } from '../core/root-selection.js';
|
|
4
|
+
import { ChangePresenter } from '../core/change-presenter.js';
|
|
5
|
+
import { SpecPresenter } from '../core/spec-presenter.js';
|
|
6
|
+
import { nearestMatches } from '../utils/match.js';
|
|
7
|
+
const CHANGE_FLAG_KEYS = new Set(['deltasOnly', 'requirementsOnly']);
|
|
8
|
+
const SPEC_FLAG_KEYS = new Set(['requirements', 'scenarios', 'requirement']);
|
|
9
|
+
export class ShowCommand {
|
|
10
|
+
async execute(itemName, options = {}) {
|
|
11
|
+
const root = await resolveRootForCommand(options, { json: options.json });
|
|
12
|
+
if (!root) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const interactive = isInteractive(options);
|
|
16
|
+
const typeOverride = this.normalizeType(options.type);
|
|
17
|
+
if (!itemName) {
|
|
18
|
+
if (interactive) {
|
|
19
|
+
const { select } = await import('@inquirer/prompts');
|
|
20
|
+
const type = await select({
|
|
21
|
+
message: 'What would you like to show?',
|
|
22
|
+
choices: [
|
|
23
|
+
{ name: 'Change', value: 'change' },
|
|
24
|
+
{ name: 'Spec', value: 'spec' },
|
|
25
|
+
],
|
|
26
|
+
});
|
|
27
|
+
await this.runInteractiveByType(type, options, root);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
this.printNonInteractiveHint(root);
|
|
31
|
+
process.exitCode = 1;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
await this.showDirect(itemName, { typeOverride, options, root });
|
|
35
|
+
}
|
|
36
|
+
normalizeType(value) {
|
|
37
|
+
if (!value)
|
|
38
|
+
return undefined;
|
|
39
|
+
const v = value.toLowerCase();
|
|
40
|
+
if (v === 'change' || v === 'spec')
|
|
41
|
+
return v;
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
delegateOptions(root, options) {
|
|
45
|
+
return {
|
|
46
|
+
...options,
|
|
47
|
+
...(options.json ? { rootOutput: toRootOutput(root) } : {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async runInteractiveByType(type, options, root) {
|
|
51
|
+
const { select } = await import('@inquirer/prompts');
|
|
52
|
+
if (type === 'change') {
|
|
53
|
+
const changes = await getActiveChangeIds(root.path);
|
|
54
|
+
if (changes.length === 0) {
|
|
55
|
+
console.error('No changes found.');
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const picked = await select({ message: 'Pick a change', choices: changes.map(id => ({ name: id, value: id })) });
|
|
60
|
+
const cmd = new ChangePresenter(root.path);
|
|
61
|
+
await cmd.show(picked, this.delegateOptions(root, options));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const specs = await getSpecIds(root.path);
|
|
65
|
+
if (specs.length === 0) {
|
|
66
|
+
console.error('No specs found.');
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const picked = await select({ message: 'Pick a spec', choices: specs.map(id => ({ name: id, value: id })) });
|
|
71
|
+
const cmd = new SpecPresenter(root.path);
|
|
72
|
+
await cmd.show(picked, this.delegateOptions(root, options));
|
|
73
|
+
}
|
|
74
|
+
async showDirect(itemName, params) {
|
|
75
|
+
const root = params.root;
|
|
76
|
+
// Optimize lookups when type is pre-specified
|
|
77
|
+
let isChange = false;
|
|
78
|
+
let isSpec = false;
|
|
79
|
+
let changes = [];
|
|
80
|
+
let specs = [];
|
|
81
|
+
if (params.typeOverride === 'change') {
|
|
82
|
+
changes = await getActiveChangeIds(root.path);
|
|
83
|
+
isChange = changes.includes(itemName);
|
|
84
|
+
}
|
|
85
|
+
else if (params.typeOverride === 'spec') {
|
|
86
|
+
specs = await getSpecIds(root.path);
|
|
87
|
+
isSpec = specs.includes(itemName);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
[changes, specs] = await Promise.all([getActiveChangeIds(root.path), getSpecIds(root.path)]);
|
|
91
|
+
isChange = changes.includes(itemName);
|
|
92
|
+
isSpec = specs.includes(itemName);
|
|
93
|
+
}
|
|
94
|
+
const resolvedType = params.typeOverride ?? (isChange ? 'change' : isSpec ? 'spec' : undefined);
|
|
95
|
+
if (!resolvedType) {
|
|
96
|
+
const suggestions = nearestMatches(itemName, [...changes, ...specs]);
|
|
97
|
+
const message = suggestions.length
|
|
98
|
+
? `Unknown item '${itemName}'. Did you mean: ${suggestions.join(', ')}?`
|
|
99
|
+
: `Unknown item '${itemName}'.`;
|
|
100
|
+
if (params.options.json) {
|
|
101
|
+
console.log(JSON.stringify({ status: [{ severity: 'error', code: 'unknown_item', message }] }, null, 2));
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.error(message);
|
|
105
|
+
}
|
|
106
|
+
process.exitCode = 1;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!params.typeOverride && isChange && isSpec) {
|
|
110
|
+
if (params.options.json) {
|
|
111
|
+
console.log(JSON.stringify({
|
|
112
|
+
status: [
|
|
113
|
+
{
|
|
114
|
+
severity: 'error',
|
|
115
|
+
code: 'ambiguous_item',
|
|
116
|
+
message: `Ambiguous item '${itemName}' matches both a change and a spec.`,
|
|
117
|
+
fix: 'Pass --type change|spec.',
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
}, null, 2));
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
console.error(`Ambiguous item '${itemName}' matches both a change and a spec.`);
|
|
125
|
+
console.error('Pass --type change|spec, or use: tospec show <item> --type change / --type spec');
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this.warnIrrelevantFlags(resolvedType, params.options);
|
|
130
|
+
if (resolvedType === 'change') {
|
|
131
|
+
const cmd = new ChangePresenter(root.path);
|
|
132
|
+
await cmd.show(itemName, this.delegateOptions(root, params.options));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const cmd = new SpecPresenter(root.path);
|
|
136
|
+
await cmd.show(itemName, this.delegateOptions(root, params.options));
|
|
137
|
+
}
|
|
138
|
+
printNonInteractiveHint(root) {
|
|
139
|
+
console.error('Nothing to show. Try one of:');
|
|
140
|
+
console.error(' tospec show <item>');
|
|
141
|
+
console.error(' tospec show <item> --type change');
|
|
142
|
+
console.error(' tospec show <item> --type spec');
|
|
143
|
+
console.error('Or run in an interactive terminal.');
|
|
144
|
+
}
|
|
145
|
+
warnIrrelevantFlags(type, options) {
|
|
146
|
+
const irrelevant = [];
|
|
147
|
+
if (type === 'change') {
|
|
148
|
+
for (const k of SPEC_FLAG_KEYS)
|
|
149
|
+
if (k in options)
|
|
150
|
+
irrelevant.push(k);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
for (const k of CHANGE_FLAG_KEYS)
|
|
154
|
+
if (k in options)
|
|
155
|
+
irrelevant.push(k);
|
|
156
|
+
}
|
|
157
|
+
if (irrelevant.length > 0) {
|
|
158
|
+
console.error(`Warning: Ignoring flags not applicable to ${type}: ${irrelevant.join(', ')}`);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=show.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"show.js","sourceRoot":"","sources":["../../src/commands/show.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,EACL,qBAAqB,EACrB,YAAY,GAGb,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAInD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC,CAAC;AACrE,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC;AAS7E,MAAM,OAAO,WAAW;IACtB,KAAK,CAAC,OAAO,CAAC,QAAiB,EAAE,UAA8B,EAAE;QAC/D,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QAED,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAEtD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,WAAW,EAAE,CAAC;gBAChB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;gBACrD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAW;oBAClC,OAAO,EAAE,8BAA8B;oBACvC,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAiB,EAAE;wBAC5C,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAe,EAAE;qBACzC;iBACF,CAAC,CAAC;gBACH,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;gBACrD,OAAO;YACT,CAAC;YACD,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC;YACnC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;IAEO,aAAa,CAAC,KAAc;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,MAAM;YAAE,OAAO,CAAC,CAAC;QAC7C,OAAO,SAAS,CAAC;IACnB,CAAC;IAEO,eAAe,CAAC,IAAwB,EAAE,OAA2B;QAC3E,OAAO;YACL,GAAG,OAAO;YACV,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5D,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,oBAAoB,CAChC,IAAc,EACd,OAA2B,EAC3B,IAAwB;QAExB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QACrD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBACnC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;gBACrB,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAS,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YACzH,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAQ,CAAC,CAAC;YACnE,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACjC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAS,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACrH,MAAM,GAAG,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAQ,CAAC,CAAC;IACrE,CAAC;IAEO,KAAK,CAAC,UAAU,CACtB,QAAgB,EAChB,MAA0F;QAE1F,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACzB,8CAA8C;QAC9C,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,IAAI,OAAO,GAAa,EAAE,CAAC;QAC3B,IAAI,KAAK,GAAa,EAAE,CAAC;QACzB,IAAI,MAAM,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;YACrC,OAAO,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;aAAM,IAAI,MAAM,CAAC,YAAY,KAAK,MAAM,EAAE,CAAC;YAC1C,KAAK,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpC,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC7F,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACtC,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAEhG,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,GAAG,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC;YACrE,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM;gBAChC,CAAC,CAAC,iBAAiB,QAAQ,oBAAoB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBACxE,CAAC,CAAC,iBAAiB,QAAQ,IAAI,CAAC;YAClC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBACxB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,EAClE,IAAI,EACJ,CAAC,CACF,CACF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACzB,CAAC;YACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,QAAQ,IAAI,MAAM,EAAE,CAAC;YAC/C,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBACxB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;oBACE,MAAM,EAAE;wBACN;4BACE,QAAQ,EAAE,OAAO;4BACjB,IAAI,EAAE,gBAAgB;4BACtB,OAAO,EAAE,mBAAmB,QAAQ,qCAAqC;4BACzE,GAAG,EAAE,0BAA0B;yBAChC;qBACF;iBACF,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;gBACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;gBACrB,OAAO;YACT,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,mBAAmB,QAAQ,qCAAqC,CAAC,CAAC;YAChF,OAAO,CAAC,KAAK,CAAC,iFAAiF,CAAC,CAAC;YACjG,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,mBAAmB,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACvD,IAAI,YAAY,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAQ,CAAC,CAAC;YAC5E,OAAO;QACT,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAQ,CAAC,CAAC;IAC9E,CAAC;IAEO,uBAAuB,CAAC,IAAwB;QACtD,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAC9C,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACtC,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACpD,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAClD,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACtD,CAAC;IAEO,mBAAmB,CAAC,IAAc,EAAE,OAA6B;QACvE,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,KAAK,MAAM,CAAC,IAAI,cAAc;gBAAE,IAAI,CAAC,IAAI,OAAO;oBAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvE,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,CAAC,IAAI,gBAAgB;gBAAE,IAAI,CAAC,IAAI,OAAO;oBAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,6CAA6C,IAAI,KAAK,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7F,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ValidationIssue, ValidationReport } from '../core/validation/types.js';
|
|
2
|
+
interface ExecuteOptions {
|
|
3
|
+
all?: boolean;
|
|
4
|
+
changes?: boolean;
|
|
5
|
+
specs?: boolean;
|
|
6
|
+
type?: string;
|
|
7
|
+
strict?: boolean;
|
|
8
|
+
json?: boolean;
|
|
9
|
+
noInteractive?: boolean;
|
|
10
|
+
interactive?: boolean;
|
|
11
|
+
concurrency?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class ValidateCommand {
|
|
14
|
+
execute(itemName: string | undefined, options?: ExecuteOptions): Promise<void>;
|
|
15
|
+
private normalizeType;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve change IDs by directory existence within the resolved root — the
|
|
18
|
+
* same rule `tospec status`/`instructions` use (`getAvailableChanges`) —
|
|
19
|
+
* rather than requiring `proposal.md`. This lets `validate` resolve a
|
|
20
|
+
* scaffolded or still-authoring change that the sibling commands already
|
|
21
|
+
* resolve (#1182). Sorted to preserve the prior `getActiveChangeIds` ordering.
|
|
22
|
+
*/
|
|
23
|
+
private listChangeIds;
|
|
24
|
+
private runInteractiveSelector;
|
|
25
|
+
private printNonInteractiveHint;
|
|
26
|
+
private validateDirectItem;
|
|
27
|
+
private validateByType;
|
|
28
|
+
private printReport;
|
|
29
|
+
private printNextSteps;
|
|
30
|
+
private runBulkValidation;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validates schema-declared `requiredSections` for ticket/design/task-style
|
|
34
|
+
* artifacts that have no Requirement grammar of their own. Only artifacts
|
|
35
|
+
* that (a) declare a `validation` block and (b) already have an output file
|
|
36
|
+
* on disk are checked — artifacts not yet produced are `status`'s concern,
|
|
37
|
+
* not validate's.
|
|
38
|
+
*/
|
|
39
|
+
export declare function validateArtifactSections(projectRoot: string, changeDir: string): Promise<ValidationIssue[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Whether a change's schema requires at least one delta spec. False only
|
|
42
|
+
* when the schema declares `specs` as `optional` and this change produced
|
|
43
|
+
* none — a pure fix with no behavioral change (e.g. an issue schema change)
|
|
44
|
+
* is a legitimate, complete change, not a validation failure. Falls back to
|
|
45
|
+
* requiring deltas (the stricter, pre-existing behavior) when the schema
|
|
46
|
+
* can't be resolved.
|
|
47
|
+
*/
|
|
48
|
+
export declare function deltaSpecsRequired(projectRoot: string, changeDir: string): Promise<boolean>;
|
|
49
|
+
export declare function mergeSectionIssues(report: ValidationReport, extra: ValidationIssue[], strict: boolean): ValidationReport;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAkBrF,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAUD,qBAAa,eAAe;IACpB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCxF,OAAO,CAAC,aAAa;IAOrB;;;;;;OAMG;YACW,aAAa;YAKb,sBAAsB;IA8BpC,OAAO,CAAC,uBAAuB;YASjB,kBAAkB;YAyDlB,cAAc;IAyB5B,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,cAAc;YAeR,iBAAiB;CA0HhC;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CA6BjH;AAID;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAcjG;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAQxH"}
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import { Validator } from '../core/validation/validator.js';
|
|
4
|
+
import { validateSections } from '../core/validation/section-validator.js';
|
|
5
|
+
import { resolveSchema, getSchemaDir } from '../core/artifact-graph/resolver.js';
|
|
6
|
+
import { resolveArtifactOutputs } from '../core/artifact-graph/outputs.js';
|
|
7
|
+
import { resolveSchemaForChange } from '../utils/change-metadata.js';
|
|
8
|
+
import { FileSystemUtils } from '../utils/file-system.js';
|
|
9
|
+
import { resolveRootForCommand, toRootOutput, } from '../core/root-selection.js';
|
|
10
|
+
import { isInteractive, resolveNoInteractive } from '../utils/interactive.js';
|
|
11
|
+
import { getSpecIds } from '../utils/item-discovery.js';
|
|
12
|
+
import { getAvailableChanges } from './workflow/shared.js';
|
|
13
|
+
import { nearestMatches } from '../utils/match.js';
|
|
14
|
+
export class ValidateCommand {
|
|
15
|
+
async execute(itemName, options = {}) {
|
|
16
|
+
const root = await resolveRootForCommand(options, { json: options.json });
|
|
17
|
+
if (!root) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const interactive = isInteractive(options);
|
|
21
|
+
// Handle bulk flags first
|
|
22
|
+
if (options.all || options.changes || options.specs) {
|
|
23
|
+
await this.runBulkValidation(root, {
|
|
24
|
+
changes: !!options.all || !!options.changes,
|
|
25
|
+
specs: !!options.all || !!options.specs,
|
|
26
|
+
}, { strict: !!options.strict, json: !!options.json, concurrency: options.concurrency, noInteractive: resolveNoInteractive(options) });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// No item and no flags
|
|
30
|
+
if (!itemName) {
|
|
31
|
+
if (interactive) {
|
|
32
|
+
await this.runInteractiveSelector(root, { strict: !!options.strict, json: !!options.json, concurrency: options.concurrency });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.printNonInteractiveHint(root);
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Direct item validation with type detection or override
|
|
40
|
+
const typeOverride = this.normalizeType(options.type);
|
|
41
|
+
await this.validateDirectItem(root, itemName, { typeOverride, strict: !!options.strict, json: !!options.json });
|
|
42
|
+
}
|
|
43
|
+
normalizeType(value) {
|
|
44
|
+
if (!value)
|
|
45
|
+
return undefined;
|
|
46
|
+
const v = value.toLowerCase();
|
|
47
|
+
if (v === 'change' || v === 'spec')
|
|
48
|
+
return v;
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve change IDs by directory existence within the resolved root — the
|
|
53
|
+
* same rule `tospec status`/`instructions` use (`getAvailableChanges`) —
|
|
54
|
+
* rather than requiring `proposal.md`. This lets `validate` resolve a
|
|
55
|
+
* scaffolded or still-authoring change that the sibling commands already
|
|
56
|
+
* resolve (#1182). Sorted to preserve the prior `getActiveChangeIds` ordering.
|
|
57
|
+
*/
|
|
58
|
+
async listChangeIds(root) {
|
|
59
|
+
const ids = await getAvailableChanges(root.path, root.changesDir);
|
|
60
|
+
return ids.sort();
|
|
61
|
+
}
|
|
62
|
+
async runInteractiveSelector(root, opts) {
|
|
63
|
+
const { select } = await import('@inquirer/prompts');
|
|
64
|
+
const choice = await select({
|
|
65
|
+
message: 'What would you like to validate?',
|
|
66
|
+
choices: [
|
|
67
|
+
{ name: 'All (changes + specs)', value: 'all' },
|
|
68
|
+
{ name: 'All changes', value: 'changes' },
|
|
69
|
+
{ name: 'All specs', value: 'specs' },
|
|
70
|
+
{ name: 'Pick a specific change or spec', value: 'one' },
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
if (choice === 'all')
|
|
74
|
+
return this.runBulkValidation(root, { changes: true, specs: true }, opts);
|
|
75
|
+
if (choice === 'changes')
|
|
76
|
+
return this.runBulkValidation(root, { changes: true, specs: false }, opts);
|
|
77
|
+
if (choice === 'specs')
|
|
78
|
+
return this.runBulkValidation(root, { changes: false, specs: true }, opts);
|
|
79
|
+
// one
|
|
80
|
+
const [changes, specs] = await Promise.all([this.listChangeIds(root), getSpecIds(root.path)]);
|
|
81
|
+
const items = [];
|
|
82
|
+
items.push(...changes.map(id => ({ name: `change/${id}`, value: { type: 'change', id } })));
|
|
83
|
+
items.push(...specs.map(id => ({ name: `spec/${id}`, value: { type: 'spec', id } })));
|
|
84
|
+
if (items.length === 0) {
|
|
85
|
+
console.error('No items found to validate.');
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const picked = await select({ message: 'Pick an item', choices: items });
|
|
90
|
+
await this.validateByType(root, picked.type, picked.id, opts);
|
|
91
|
+
}
|
|
92
|
+
printNonInteractiveHint(root) {
|
|
93
|
+
console.error('Nothing to validate. Try one of:');
|
|
94
|
+
console.error(` ${'tospec validate --all'}`);
|
|
95
|
+
console.error(` ${'tospec validate --changes'}`);
|
|
96
|
+
console.error(` ${'tospec validate --specs'}`);
|
|
97
|
+
console.error(` ${'tospec validate <item-name>'}`);
|
|
98
|
+
console.error('Or run in an interactive terminal.');
|
|
99
|
+
}
|
|
100
|
+
async validateDirectItem(root, itemName, opts) {
|
|
101
|
+
const [changes, specs] = await Promise.all([this.listChangeIds(root), getSpecIds(root.path)]);
|
|
102
|
+
const isChange = changes.includes(itemName);
|
|
103
|
+
const isSpec = specs.includes(itemName);
|
|
104
|
+
const type = opts.typeOverride ?? (isChange ? 'change' : isSpec ? 'spec' : undefined);
|
|
105
|
+
if (!type) {
|
|
106
|
+
const suggestions = nearestMatches(itemName, [...changes, ...specs]);
|
|
107
|
+
const message = suggestions.length
|
|
108
|
+
? `Unknown item '${itemName}'. Did you mean: ${suggestions.join(', ')}?`
|
|
109
|
+
: `Unknown item '${itemName}'.`;
|
|
110
|
+
if (opts.json) {
|
|
111
|
+
console.log(JSON.stringify({ status: [{ severity: 'error', code: 'unknown_item', message }] }, null, 2));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
console.error(message);
|
|
115
|
+
}
|
|
116
|
+
process.exitCode = 1;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (!opts.typeOverride && isChange && isSpec) {
|
|
120
|
+
if (opts.json) {
|
|
121
|
+
console.log(JSON.stringify({
|
|
122
|
+
status: [
|
|
123
|
+
{
|
|
124
|
+
severity: 'error',
|
|
125
|
+
code: 'ambiguous_item',
|
|
126
|
+
message: `Ambiguous item '${itemName}' matches both a change and a spec.`,
|
|
127
|
+
fix: 'Pass --type change|spec.',
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
}, null, 2));
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
console.error(`Ambiguous item '${itemName}' matches both a change and a spec.`);
|
|
135
|
+
console.error('Pass --type change|spec, or use: tospec validate --changes / tospec validate --specs');
|
|
136
|
+
process.exitCode = 1;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
await this.validateByType(root, type, itemName, opts);
|
|
140
|
+
}
|
|
141
|
+
async validateByType(root, type, id, opts) {
|
|
142
|
+
const validator = new Validator(opts.strict);
|
|
143
|
+
if (type === 'change') {
|
|
144
|
+
const changeDir = path.join(root.changesDir, id);
|
|
145
|
+
const start = Date.now();
|
|
146
|
+
const requireDeltas = await deltaSpecsRequired(root.path, changeDir);
|
|
147
|
+
const [deltaReport, sectionIssues] = await Promise.all([
|
|
148
|
+
requireDeltas ? validator.validateChangeDeltaSpecs(changeDir) : Promise.resolve(EMPTY_REPORT),
|
|
149
|
+
validateArtifactSections(root.path, changeDir),
|
|
150
|
+
]);
|
|
151
|
+
const report = mergeSectionIssues(deltaReport, sectionIssues, opts.strict);
|
|
152
|
+
const durationMs = Date.now() - start;
|
|
153
|
+
this.printReport('change', id, report, durationMs, opts.json, root);
|
|
154
|
+
// Non-zero exit if invalid (keeps enriched output test semantics)
|
|
155
|
+
process.exitCode = report.valid ? 0 : 1;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const file = path.join(root.specsDir, id, 'spec.md');
|
|
159
|
+
const start = Date.now();
|
|
160
|
+
const report = await validator.validateSpec(file);
|
|
161
|
+
const durationMs = Date.now() - start;
|
|
162
|
+
this.printReport('spec', id, report, durationMs, opts.json, root);
|
|
163
|
+
process.exitCode = report.valid ? 0 : 1;
|
|
164
|
+
}
|
|
165
|
+
printReport(type, id, report, durationMs, json, root) {
|
|
166
|
+
if (json) {
|
|
167
|
+
const out = { items: [{ id, type, valid: report.valid, issues: report.issues, durationMs }], summary: { totals: { items: 1, passed: report.valid ? 1 : 0, failed: report.valid ? 0 : 1 }, byType: { [type]: { items: 1, passed: report.valid ? 1 : 0, failed: report.valid ? 0 : 1 } } }, version: '1.0', root: toRootOutput(root) };
|
|
168
|
+
console.log(JSON.stringify(out, null, 2));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (report.valid) {
|
|
172
|
+
console.log(`${type === 'change' ? 'Change' : 'Specification'} '${id}' is valid`);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
console.error(`${type === 'change' ? 'Change' : 'Specification'} '${id}' has issues`);
|
|
176
|
+
for (const issue of report.issues) {
|
|
177
|
+
const label = issue.level === 'ERROR' ? 'ERROR' : issue.level;
|
|
178
|
+
const prefix = issue.level === 'ERROR' ? '✗' : issue.level === 'WARNING' ? '⚠' : 'ℹ';
|
|
179
|
+
console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`);
|
|
180
|
+
}
|
|
181
|
+
this.printNextSteps(type, id, root);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
printNextSteps(type, id, root) {
|
|
185
|
+
const bullets = [];
|
|
186
|
+
if (type === 'change') {
|
|
187
|
+
bullets.push('- Ensure change has deltas in specs/: use headers ## ADDED/MODIFIED/REMOVED/RENAMED Requirements');
|
|
188
|
+
bullets.push('- Each requirement MUST include at least one #### Scenario: block');
|
|
189
|
+
bullets.push(`- Debug parsed deltas: ${`tospec show ${id} --json --deltas-only`}`);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
bullets.push('- Ensure spec includes ## Purpose and ## Requirements sections');
|
|
193
|
+
bullets.push('- Each requirement MUST include at least one #### Scenario: block');
|
|
194
|
+
bullets.push('- Re-run with --json to see structured report');
|
|
195
|
+
}
|
|
196
|
+
console.error('Next steps:');
|
|
197
|
+
bullets.forEach(b => console.error(` ${b}`));
|
|
198
|
+
}
|
|
199
|
+
async runBulkValidation(root, scope, opts) {
|
|
200
|
+
const showProgress = !opts.json && !opts.noInteractive;
|
|
201
|
+
const [changeIds, specIds] = await Promise.all([
|
|
202
|
+
scope.changes ? this.listChangeIds(root) : Promise.resolve([]),
|
|
203
|
+
scope.specs ? getSpecIds(root.path) : Promise.resolve([]),
|
|
204
|
+
]);
|
|
205
|
+
const DEFAULT_CONCURRENCY = 6;
|
|
206
|
+
const maxSuggestions = 5; // used by nearestMatches
|
|
207
|
+
const concurrency = normalizeConcurrency(opts.concurrency) ?? normalizeConcurrency(process.env.TOSPEC_CONCURRENCY) ?? DEFAULT_CONCURRENCY;
|
|
208
|
+
const validator = new Validator(opts.strict);
|
|
209
|
+
const queue = [];
|
|
210
|
+
for (const id of changeIds) {
|
|
211
|
+
queue.push(async () => {
|
|
212
|
+
const start = Date.now();
|
|
213
|
+
const changeDir = path.join(root.changesDir, id);
|
|
214
|
+
const requireDeltas = await deltaSpecsRequired(root.path, changeDir);
|
|
215
|
+
const [deltaReport, sectionIssues] = await Promise.all([
|
|
216
|
+
requireDeltas ? validator.validateChangeDeltaSpecs(changeDir) : Promise.resolve(EMPTY_REPORT),
|
|
217
|
+
validateArtifactSections(root.path, changeDir),
|
|
218
|
+
]);
|
|
219
|
+
const report = mergeSectionIssues(deltaReport, sectionIssues, opts.strict);
|
|
220
|
+
const durationMs = Date.now() - start;
|
|
221
|
+
return { id, type: 'change', valid: report.valid, issues: report.issues, durationMs };
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
for (const id of specIds) {
|
|
225
|
+
queue.push(async () => {
|
|
226
|
+
const start = Date.now();
|
|
227
|
+
const file = path.join(root.specsDir, id, 'spec.md');
|
|
228
|
+
const report = await validator.validateSpec(file);
|
|
229
|
+
const durationMs = Date.now() - start;
|
|
230
|
+
return { id, type: 'spec', valid: report.valid, issues: report.issues, durationMs };
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (queue.length === 0) {
|
|
234
|
+
const summary = {
|
|
235
|
+
totals: { items: 0, passed: 0, failed: 0 },
|
|
236
|
+
byType: {
|
|
237
|
+
...(scope.changes ? { change: { items: 0, passed: 0, failed: 0 } } : {}),
|
|
238
|
+
...(scope.specs ? { spec: { items: 0, passed: 0, failed: 0 } } : {}),
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
if (opts.json) {
|
|
242
|
+
const out = { items: [], summary, version: '1.0', root: toRootOutput(root) };
|
|
243
|
+
console.log(JSON.stringify(out, null, 2));
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
console.log('No items found to validate.');
|
|
247
|
+
}
|
|
248
|
+
process.exitCode = 0;
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (showProgress) {
|
|
252
|
+
console.log(`Validating ${queue.length} item(s)...`);
|
|
253
|
+
}
|
|
254
|
+
const results = [];
|
|
255
|
+
let index = 0;
|
|
256
|
+
let running = 0;
|
|
257
|
+
let passed = 0;
|
|
258
|
+
let failed = 0;
|
|
259
|
+
await new Promise((resolve) => {
|
|
260
|
+
const next = () => {
|
|
261
|
+
while (running < concurrency && index < queue.length) {
|
|
262
|
+
const currentIndex = index++;
|
|
263
|
+
const task = queue[currentIndex];
|
|
264
|
+
running++;
|
|
265
|
+
task()
|
|
266
|
+
.then(res => {
|
|
267
|
+
results.push(res);
|
|
268
|
+
if (res.valid)
|
|
269
|
+
passed++;
|
|
270
|
+
else
|
|
271
|
+
failed++;
|
|
272
|
+
})
|
|
273
|
+
.catch((error) => {
|
|
274
|
+
const message = error?.message || 'Unknown error';
|
|
275
|
+
const res = { id: getPlannedId(currentIndex, changeIds, specIds) ?? 'unknown', type: getPlannedType(currentIndex, changeIds, specIds) ?? 'change', valid: false, issues: [{ level: 'ERROR', path: 'file', message }], durationMs: 0 };
|
|
276
|
+
results.push(res);
|
|
277
|
+
failed++;
|
|
278
|
+
})
|
|
279
|
+
.finally(() => {
|
|
280
|
+
running--;
|
|
281
|
+
if (index >= queue.length && running === 0)
|
|
282
|
+
resolve();
|
|
283
|
+
else
|
|
284
|
+
next();
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
next();
|
|
289
|
+
});
|
|
290
|
+
results.sort((a, b) => a.id.localeCompare(b.id));
|
|
291
|
+
const summary = {
|
|
292
|
+
totals: { items: results.length, passed, failed },
|
|
293
|
+
byType: {
|
|
294
|
+
...(scope.changes ? { change: summarizeType(results, 'change') } : {}),
|
|
295
|
+
...(scope.specs ? { spec: summarizeType(results, 'spec') } : {}),
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
if (opts.json) {
|
|
299
|
+
const out = { items: results, summary, version: '1.0', root: toRootOutput(root) };
|
|
300
|
+
console.log(JSON.stringify(out, null, 2));
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
for (const res of results) {
|
|
304
|
+
if (res.valid)
|
|
305
|
+
console.log(`✓ ${res.type}/${res.id}`);
|
|
306
|
+
else
|
|
307
|
+
console.error(`✗ ${res.type}/${res.id}`);
|
|
308
|
+
}
|
|
309
|
+
console.log(`Totals: ${summary.totals.passed} passed, ${summary.totals.failed} failed (${summary.totals.items} items)`);
|
|
310
|
+
const firstFailure = results.find((res) => !res.valid);
|
|
311
|
+
if (firstFailure) {
|
|
312
|
+
console.log(`Details: tospec validate ${firstFailure.id} --type ${firstFailure.type}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
process.exitCode = failed > 0 ? 1 : 0;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Validates schema-declared `requiredSections` for ticket/design/task-style
|
|
320
|
+
* artifacts that have no Requirement grammar of their own. Only artifacts
|
|
321
|
+
* that (a) declare a `validation` block and (b) already have an output file
|
|
322
|
+
* on disk are checked — artifacts not yet produced are `status`'s concern,
|
|
323
|
+
* not validate's.
|
|
324
|
+
*/
|
|
325
|
+
export async function validateArtifactSections(projectRoot, changeDir) {
|
|
326
|
+
const issues = [];
|
|
327
|
+
let schemaName;
|
|
328
|
+
let schema;
|
|
329
|
+
try {
|
|
330
|
+
schemaName = resolveSchemaForChange(changeDir, undefined, projectRoot);
|
|
331
|
+
schema = resolveSchema(schemaName, projectRoot);
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
return issues;
|
|
335
|
+
}
|
|
336
|
+
const schemaDir = getSchemaDir(schemaName, projectRoot);
|
|
337
|
+
for (const artifact of schema.artifacts) {
|
|
338
|
+
if (!artifact.validation)
|
|
339
|
+
continue;
|
|
340
|
+
for (const file of resolveArtifactOutputs(changeDir, artifact.generates)) {
|
|
341
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
342
|
+
const relPath = FileSystemUtils.toPosixPath(path.relative(changeDir, file));
|
|
343
|
+
const templateRef = schemaDir
|
|
344
|
+
? FileSystemUtils.toPosixPath(path.join(path.relative(projectRoot, schemaDir), 'templates', artifact.template))
|
|
345
|
+
: undefined;
|
|
346
|
+
issues.push(...validateSections(content, artifact.validation, { path: relPath, templateRef }));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return issues;
|
|
350
|
+
}
|
|
351
|
+
const EMPTY_REPORT = { valid: true, issues: [], summary: { errors: 0, warnings: 0, info: 0 } };
|
|
352
|
+
/**
|
|
353
|
+
* Whether a change's schema requires at least one delta spec. False only
|
|
354
|
+
* when the schema declares `specs` as `optional` and this change produced
|
|
355
|
+
* none — a pure fix with no behavioral change (e.g. an issue schema change)
|
|
356
|
+
* is a legitimate, complete change, not a validation failure. Falls back to
|
|
357
|
+
* requiring deltas (the stricter, pre-existing behavior) when the schema
|
|
358
|
+
* can't be resolved.
|
|
359
|
+
*/
|
|
360
|
+
export async function deltaSpecsRequired(projectRoot, changeDir) {
|
|
361
|
+
let schemaName;
|
|
362
|
+
let schema;
|
|
363
|
+
try {
|
|
364
|
+
schemaName = resolveSchemaForChange(changeDir, undefined, projectRoot);
|
|
365
|
+
schema = resolveSchema(schemaName, projectRoot);
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
const specsArtifact = schema.artifacts.find((a) => a.id === 'specs');
|
|
371
|
+
if (!specsArtifact?.optional)
|
|
372
|
+
return true;
|
|
373
|
+
return resolveArtifactOutputs(changeDir, specsArtifact.generates).length > 0;
|
|
374
|
+
}
|
|
375
|
+
export function mergeSectionIssues(report, extra, strict) {
|
|
376
|
+
if (extra.length === 0)
|
|
377
|
+
return report;
|
|
378
|
+
const issues = [...report.issues, ...extra];
|
|
379
|
+
const errors = issues.filter(i => i.level === 'ERROR').length;
|
|
380
|
+
const warnings = issues.filter(i => i.level === 'WARNING').length;
|
|
381
|
+
const info = issues.filter(i => i.level === 'INFO').length;
|
|
382
|
+
const valid = strict ? errors === 0 && warnings === 0 : errors === 0;
|
|
383
|
+
return { valid, issues, summary: { errors, warnings, info } };
|
|
384
|
+
}
|
|
385
|
+
function summarizeType(results, type) {
|
|
386
|
+
const filtered = results.filter(r => r.type === type);
|
|
387
|
+
const items = filtered.length;
|
|
388
|
+
const passed = filtered.filter(r => r.valid).length;
|
|
389
|
+
const failed = items - passed;
|
|
390
|
+
return { items, passed, failed };
|
|
391
|
+
}
|
|
392
|
+
function normalizeConcurrency(value) {
|
|
393
|
+
if (!value)
|
|
394
|
+
return undefined;
|
|
395
|
+
const n = parseInt(value, 10);
|
|
396
|
+
if (Number.isNaN(n) || n <= 0)
|
|
397
|
+
return undefined;
|
|
398
|
+
return n;
|
|
399
|
+
}
|
|
400
|
+
function getPlannedId(index, changeIds, specIds) {
|
|
401
|
+
const totalChanges = changeIds.length;
|
|
402
|
+
if (index < totalChanges)
|
|
403
|
+
return changeIds[index];
|
|
404
|
+
const specIndex = index - totalChanges;
|
|
405
|
+
return specIds[specIndex];
|
|
406
|
+
}
|
|
407
|
+
function getPlannedType(index, changeIds, specIds) {
|
|
408
|
+
const totalChanges = changeIds.length;
|
|
409
|
+
if (index < totalChanges)
|
|
410
|
+
return 'change';
|
|
411
|
+
const specIndex = index - totalChanges;
|
|
412
|
+
if (specIndex >= 0 && specIndex < specIds.length)
|
|
413
|
+
return 'spec';
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
//# sourceMappingURL=validate.js.map
|