@seanmars/tospec 0.19.0-beta.0 → 0.19.0-beta.13
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/CHANGELOG.md +578 -0
- package/README.md +71 -77
- package/assets/dashboard/app.js +14 -3
- package/assets/dashboard/style.css +7 -0
- package/assets/metrics/app.js +22 -0
- package/assets/metrics/style.css +8 -0
- package/assets/rules/tospec/decision.md +3 -0
- package/assets/rules/tospec/single-source-of-truth.md +19 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +266 -82
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/config.d.ts +9 -17
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +374 -95
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/dashboard.d.ts +64 -81
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +350 -218
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/decision.d.ts +41 -19
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +400 -70
- package/dist/commands/decision.js.map +1 -1
- package/dist/commands/metrics.d.ts +34 -48
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +74 -84
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/shared-output.d.ts +24 -10
- package/dist/commands/shared-output.d.ts.map +1 -1
- package/dist/commands/shared-output.js +62 -11
- package/dist/commands/shared-output.js.map +1 -1
- package/dist/commands/show.d.ts +7 -0
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +39 -8
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/validate.d.ts +47 -30
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +282 -108
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/workflow/index.d.ts +6 -10
- package/dist/commands/workflow/index.d.ts.map +1 -1
- package/dist/commands/workflow/index.js +6 -10
- package/dist/commands/workflow/index.js.map +1 -1
- package/dist/commands/workflow/instructions.d.ts +21 -8
- package/dist/commands/workflow/instructions.d.ts.map +1 -1
- package/dist/commands/workflow/instructions.js +254 -95
- package/dist/commands/workflow/instructions.js.map +1 -1
- package/dist/commands/workflow/new-change.d.ts +4 -5
- package/dist/commands/workflow/new-change.d.ts.map +1 -1
- package/dist/commands/workflow/new-change.js +90 -25
- package/dist/commands/workflow/new-change.js.map +1 -1
- package/dist/commands/workflow/schemas.d.ts +3 -5
- package/dist/commands/workflow/schemas.d.ts.map +1 -1
- package/dist/commands/workflow/schemas.js +37 -11
- package/dist/commands/workflow/schemas.js.map +1 -1
- package/dist/commands/workflow/shared.d.ts +48 -21
- package/dist/commands/workflow/shared.d.ts.map +1 -1
- package/dist/commands/workflow/shared.js +36 -33
- package/dist/commands/workflow/shared.js.map +1 -1
- package/dist/commands/workflow/status.d.ts +10 -6
- package/dist/commands/workflow/status.d.ts.map +1 -1
- package/dist/commands/workflow/status.js +84 -42
- package/dist/commands/workflow/status.js.map +1 -1
- package/dist/commands/workflow/templates.d.ts +10 -3
- package/dist/commands/workflow/templates.d.ts.map +1 -1
- package/dist/commands/workflow/templates.js +39 -40
- package/dist/commands/workflow/templates.js.map +1 -1
- package/dist/core/archive.d.ts +26 -21
- package/dist/core/archive.d.ts.map +1 -1
- package/dist/core/archive.js +415 -201
- package/dist/core/archive.js.map +1 -1
- package/dist/core/artifact-graph/graph.d.ts +29 -36
- package/dist/core/artifact-graph/graph.d.ts.map +1 -1
- package/dist/core/artifact-graph/graph.js +50 -58
- package/dist/core/artifact-graph/graph.js.map +1 -1
- package/dist/core/artifact-graph/index.d.ts +2 -2
- package/dist/core/artifact-graph/index.d.ts.map +1 -1
- package/dist/core/artifact-graph/index.js +2 -2
- package/dist/core/artifact-graph/index.js.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +105 -100
- package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.js +176 -110
- package/dist/core/artifact-graph/instruction-loader.js.map +1 -1
- package/dist/core/artifact-graph/outputs.d.ts +13 -6
- package/dist/core/artifact-graph/outputs.d.ts.map +1 -1
- package/dist/core/artifact-graph/outputs.js +146 -8
- package/dist/core/artifact-graph/outputs.js.map +1 -1
- package/dist/core/artifact-graph/resolver.d.ts +44 -63
- package/dist/core/artifact-graph/resolver.d.ts.map +1 -1
- package/dist/core/artifact-graph/resolver.js +85 -86
- package/dist/core/artifact-graph/resolver.js.map +1 -1
- package/dist/core/artifact-graph/schema.d.ts +0 -6
- package/dist/core/artifact-graph/schema.d.ts.map +1 -1
- package/dist/core/artifact-graph/schema.js +7 -32
- package/dist/core/artifact-graph/schema.js.map +1 -1
- package/dist/core/artifact-graph/state.d.ts +1 -8
- package/dist/core/artifact-graph/state.d.ts.map +1 -1
- package/dist/core/artifact-graph/state.js +2 -17
- package/dist/core/artifact-graph/state.js.map +1 -1
- package/dist/core/artifact-graph/stub-detection.d.ts +12 -0
- package/dist/core/artifact-graph/stub-detection.d.ts.map +1 -0
- package/dist/core/artifact-graph/stub-detection.js +39 -0
- package/dist/core/artifact-graph/stub-detection.js.map +1 -0
- package/dist/core/artifact-graph/types.d.ts +4 -0
- package/dist/core/artifact-graph/types.d.ts.map +1 -1
- package/dist/core/artifact-graph/types.js +30 -10
- package/dist/core/artifact-graph/types.js.map +1 -1
- package/dist/core/available-tools.d.ts +3 -12
- package/dist/core/available-tools.d.ts.map +1 -1
- package/dist/core/available-tools.js +4 -13
- package/dist/core/available-tools.js.map +1 -1
- package/dist/core/change-metadata/schema.d.ts +1 -1
- package/dist/core/change-metadata/schema.d.ts.map +1 -1
- package/dist/core/change-metadata/schema.js +10 -7
- package/dist/core/change-metadata/schema.js.map +1 -1
- package/dist/core/change-presenter.d.ts +23 -18
- package/dist/core/change-presenter.d.ts.map +1 -1
- package/dist/core/change-presenter.js +102 -43
- package/dist/core/change-presenter.js.map +1 -1
- package/dist/core/change-status-policy.d.ts +8 -1
- package/dist/core/change-status-policy.d.ts.map +1 -1
- package/dist/core/change-status-policy.js +25 -1
- package/dist/core/change-status-policy.js.map +1 -1
- package/dist/core/codex-metrics.d.ts +25 -45
- package/dist/core/codex-metrics.d.ts.map +1 -1
- package/dist/core/codex-metrics.js +44 -88
- package/dist/core/codex-metrics.js.map +1 -1
- package/dist/core/codex-residue.d.ts +22 -0
- package/dist/core/codex-residue.d.ts.map +1 -0
- package/dist/core/codex-residue.js +61 -0
- package/dist/core/codex-residue.js.map +1 -0
- package/dist/core/command-generation/adapters/claude.d.ts +2 -9
- package/dist/core/command-generation/adapters/claude.d.ts.map +1 -1
- package/dist/core/command-generation/adapters/claude.js +2 -12
- package/dist/core/command-generation/adapters/claude.js.map +1 -1
- package/dist/core/command-generation/adapters/index.d.ts +1 -9
- package/dist/core/command-generation/adapters/index.d.ts.map +1 -1
- package/dist/core/command-generation/adapters/index.js +1 -9
- package/dist/core/command-generation/adapters/index.js.map +1 -1
- package/dist/core/command-generation/generator.d.ts +0 -17
- package/dist/core/command-generation/generator.d.ts.map +1 -1
- package/dist/core/command-generation/generator.js +0 -17
- package/dist/core/command-generation/generator.js.map +1 -1
- package/dist/core/command-generation/index.d.ts +2 -5
- package/dist/core/command-generation/index.d.ts.map +1 -1
- package/dist/core/command-generation/index.js +0 -9
- package/dist/core/command-generation/index.js.map +1 -1
- package/dist/core/command-generation/types.d.ts +10 -36
- package/dist/core/command-generation/types.d.ts.map +1 -1
- package/dist/core/command-generation/types.js +0 -6
- package/dist/core/command-generation/types.js.map +1 -1
- package/dist/core/command-generation/yaml.d.ts +3 -18
- package/dist/core/command-generation/yaml.d.ts.map +1 -1
- package/dist/core/command-generation/yaml.js +5 -23
- package/dist/core/command-generation/yaml.js.map +1 -1
- package/dist/core/config-prompts.d.ts +2 -4
- package/dist/core/config-prompts.d.ts.map +1 -1
- package/dist/core/config-prompts.js +2 -7
- package/dist/core/config-prompts.js.map +1 -1
- package/dist/core/config-schema.d.ts +8 -53
- package/dist/core/config-schema.d.ts.map +1 -1
- package/dist/core/config-schema.js +49 -62
- package/dist/core/config-schema.js.map +1 -1
- package/dist/core/config.d.ts +56 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +73 -2
- package/dist/core/config.js.map +1 -1
- package/dist/core/converters/json-converter.d.ts.map +1 -1
- package/dist/core/dashboard-activity.d.ts +7 -9
- package/dist/core/dashboard-activity.d.ts.map +1 -1
- package/dist/core/dashboard-activity.js +26 -24
- package/dist/core/dashboard-activity.js.map +1 -1
- package/dist/core/dashboard-data.d.ts +40 -22
- package/dist/core/dashboard-data.d.ts.map +1 -1
- package/dist/core/dashboard-data.js +84 -68
- package/dist/core/dashboard-data.js.map +1 -1
- package/dist/core/global-config.d.ts +24 -53
- package/dist/core/global-config.d.ts.map +1 -1
- package/dist/core/global-config.js +43 -62
- package/dist/core/global-config.js.map +1 -1
- package/dist/core/init.d.ts +29 -11
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +232 -164
- package/dist/core/init.js.map +1 -1
- package/dist/core/list.d.ts +1 -1
- package/dist/core/list.d.ts.map +1 -1
- package/dist/core/list.js +121 -28
- package/dist/core/list.js.map +1 -1
- package/dist/core/local-server.d.ts +63 -39
- package/dist/core/local-server.d.ts.map +1 -1
- package/dist/core/local-server.js +99 -53
- package/dist/core/local-server.js.map +1 -1
- package/dist/core/markdown-render.d.ts +25 -0
- package/dist/core/markdown-render.d.ts.map +1 -0
- package/dist/core/markdown-render.js +94 -0
- package/dist/core/markdown-render.js.map +1 -0
- package/dist/core/migrate.d.ts +32 -15
- package/dist/core/migrate.d.ts.map +1 -1
- package/dist/core/migrate.js +220 -108
- package/dist/core/migrate.js.map +1 -1
- package/dist/core/parsers/change-parser.d.ts +7 -10
- package/dist/core/parsers/change-parser.d.ts.map +1 -1
- package/dist/core/parsers/change-parser.js +48 -56
- package/dist/core/parsers/change-parser.js.map +1 -1
- package/dist/core/parsers/markdown-parser.d.ts +8 -9
- package/dist/core/parsers/markdown-parser.d.ts.map +1 -1
- package/dist/core/parsers/markdown-parser.js +31 -22
- package/dist/core/parsers/markdown-parser.js.map +1 -1
- package/dist/core/parsers/requirement-blocks.d.ts +53 -11
- package/dist/core/parsers/requirement-blocks.d.ts.map +1 -1
- package/dist/core/parsers/requirement-blocks.js +200 -60
- package/dist/core/parsers/requirement-blocks.js.map +1 -1
- package/dist/core/parsers/requirement-text.d.ts +73 -79
- package/dist/core/parsers/requirement-text.d.ts.map +1 -1
- package/dist/core/parsers/requirement-text.js +137 -79
- package/dist/core/parsers/requirement-text.js.map +1 -1
- package/dist/core/parsers/spec-structure.d.ts +1 -1
- package/dist/core/parsers/spec-structure.d.ts.map +1 -1
- package/dist/core/parsers/spec-structure.js +30 -3
- package/dist/core/parsers/spec-structure.js.map +1 -1
- package/dist/core/planning-home.js.map +1 -1
- package/dist/core/profiles.d.ts +3 -10
- package/dist/core/profiles.d.ts.map +1 -1
- package/dist/core/profiles.js +5 -12
- package/dist/core/profiles.js.map +1 -1
- package/dist/core/project-config.d.ts +43 -44
- package/dist/core/project-config.d.ts.map +1 -1
- package/dist/core/project-config.js +107 -82
- package/dist/core/project-config.js.map +1 -1
- package/dist/core/project-layout.d.ts +10 -18
- package/dist/core/project-layout.d.ts.map +1 -1
- package/dist/core/project-layout.js +16 -26
- package/dist/core/project-layout.js.map +1 -1
- package/dist/core/root-selection.d.ts +11 -7
- package/dist/core/root-selection.d.ts.map +1 -1
- package/dist/core/root-selection.js +7 -8
- package/dist/core/root-selection.js.map +1 -1
- package/dist/core/rules.d.ts +10 -0
- package/dist/core/rules.d.ts.map +1 -0
- package/dist/core/rules.js +43 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/schema-names.d.ts +16 -0
- package/dist/core/schema-names.d.ts.map +1 -0
- package/dist/core/schema-names.js +16 -0
- package/dist/core/schema-names.js.map +1 -0
- package/dist/core/schemas/base.schema.d.ts +3 -0
- package/dist/core/schemas/base.schema.d.ts.map +1 -1
- package/dist/core/schemas/base.schema.js +22 -6
- package/dist/core/schemas/base.schema.js.map +1 -1
- package/dist/core/schemas/change.schema.d.ts +16 -0
- package/dist/core/schemas/change.schema.d.ts.map +1 -1
- package/dist/core/schemas/change.schema.js +41 -10
- package/dist/core/schemas/change.schema.js.map +1 -1
- package/dist/core/schemas/spec.schema.d.ts +2 -0
- package/dist/core/schemas/spec.schema.d.ts.map +1 -1
- package/dist/core/shared/index.d.ts +3 -8
- package/dist/core/shared/index.d.ts.map +1 -1
- package/dist/core/shared/index.js +3 -8
- package/dist/core/shared/index.js.map +1 -1
- package/dist/core/shared/rules-generation.d.ts +27 -8
- package/dist/core/shared/rules-generation.d.ts.map +1 -1
- package/dist/core/shared/rules-generation.js +151 -16
- package/dist/core/shared/rules-generation.js.map +1 -1
- package/dist/core/shared/skill-generation.d.ts +38 -53
- package/dist/core/shared/skill-generation.d.ts.map +1 -1
- package/dist/core/shared/skill-generation.js +82 -51
- package/dist/core/shared/skill-generation.js.map +1 -1
- package/dist/core/shared/tool-detection.d.ts +40 -62
- package/dist/core/shared/tool-detection.d.ts.map +1 -1
- package/dist/core/shared/tool-detection.js +88 -80
- package/dist/core/shared/tool-detection.js.map +1 -1
- package/dist/core/skill-metrics.d.ts +36 -63
- package/dist/core/skill-metrics.d.ts.map +1 -1
- package/dist/core/skill-metrics.js +34 -73
- package/dist/core/skill-metrics.js.map +1 -1
- package/dist/core/spec-presenter.d.ts.map +1 -1
- package/dist/core/spec-presenter.js +6 -6
- package/dist/core/spec-presenter.js.map +1 -1
- package/dist/core/specs-apply.d.ts +24 -23
- package/dist/core/specs-apply.d.ts.map +1 -1
- package/dist/core/specs-apply.js +188 -173
- package/dist/core/specs-apply.js.map +1 -1
- package/dist/core/templates/fragments/interview.d.ts +2 -6
- package/dist/core/templates/fragments/interview.d.ts.map +1 -1
- package/dist/core/templates/fragments/interview.js +2 -6
- package/dist/core/templates/fragments/interview.js.map +1 -1
- package/dist/core/templates/fragments/next-step.d.ts +4 -8
- package/dist/core/templates/fragments/next-step.d.ts.map +1 -1
- package/dist/core/templates/fragments/next-step.js +4 -8
- package/dist/core/templates/fragments/next-step.js.map +1 -1
- package/dist/core/templates/fragments/verify.d.ts +9 -12
- package/dist/core/templates/fragments/verify.d.ts.map +1 -1
- package/dist/core/templates/fragments/verify.js +9 -12
- package/dist/core/templates/fragments/verify.js.map +1 -1
- package/dist/core/templates/index.d.ts +0 -6
- package/dist/core/templates/index.d.ts.map +1 -1
- package/dist/core/templates/index.js +0 -7
- package/dist/core/templates/index.js.map +1 -1
- package/dist/core/templates/skill-templates.d.ts +1 -5
- package/dist/core/templates/skill-templates.d.ts.map +1 -1
- package/dist/core/templates/skill-templates.js +0 -5
- package/dist/core/templates/skill-templates.js.map +1 -1
- package/dist/core/templates/types.d.ts +3 -7
- package/dist/core/templates/types.d.ts.map +1 -1
- package/dist/core/templates/types.js +0 -3
- package/dist/core/templates/types.js.map +1 -1
- package/dist/core/templates/workflows/apply.d.ts +3 -9
- package/dist/core/templates/workflows/apply.d.ts.map +1 -1
- package/dist/core/templates/workflows/apply.js +9 -12
- package/dist/core/templates/workflows/apply.js.map +1 -1
- package/dist/core/templates/workflows/archive.d.ts +0 -6
- package/dist/core/templates/workflows/archive.d.ts.map +1 -1
- package/dist/core/templates/workflows/archive.js +7 -5
- package/dist/core/templates/workflows/archive.js.map +1 -1
- package/dist/core/templates/workflows/decision.js +4 -4
- package/dist/core/templates/workflows/decision.js.map +1 -1
- package/dist/core/templates/workflows/explore.js +1 -1
- package/dist/core/templates/workflows/grill.d.ts.map +1 -1
- package/dist/core/templates/workflows/grill.js +0 -2
- package/dist/core/templates/workflows/grill.js.map +1 -1
- package/dist/core/templates/workflows/issue.d.ts +0 -6
- package/dist/core/templates/workflows/issue.d.ts.map +1 -1
- package/dist/core/templates/workflows/issue.js +3 -0
- package/dist/core/templates/workflows/issue.js.map +1 -1
- package/dist/core/templates/workflows/propose.d.ts +0 -6
- package/dist/core/templates/workflows/propose.d.ts.map +1 -1
- package/dist/core/templates/workflows/propose.js +0 -1
- package/dist/core/templates/workflows/propose.js.map +1 -1
- package/dist/core/templates/workflows/sync.d.ts +2 -8
- package/dist/core/templates/workflows/sync.d.ts.map +1 -1
- package/dist/core/templates/workflows/sync.js +2 -2
- package/dist/core/templates/workflows/sync.js.map +1 -1
- package/dist/core/templates/workflows/update.d.ts +0 -6
- package/dist/core/templates/workflows/update.d.ts.map +1 -1
- package/dist/core/templates/workflows/update.js.map +1 -1
- package/dist/core/update.d.ts +26 -21
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +165 -116
- package/dist/core/update.js.map +1 -1
- package/dist/core/user-state-migration.d.ts +13 -15
- package/dist/core/user-state-migration.d.ts.map +1 -1
- package/dist/core/user-state-migration.js +16 -20
- package/dist/core/user-state-migration.js.map +1 -1
- package/dist/core/validation/constants.d.ts +19 -25
- package/dist/core/validation/constants.d.ts.map +1 -1
- package/dist/core/validation/constants.js +25 -20
- package/dist/core/validation/constants.js.map +1 -1
- package/dist/core/validation/prose-length.d.ts +15 -0
- package/dist/core/validation/prose-length.d.ts.map +1 -0
- package/dist/core/validation/prose-length.js +29 -0
- package/dist/core/validation/prose-length.js.map +1 -0
- package/dist/core/validation/purpose-placeholder.d.ts +9 -16
- package/dist/core/validation/purpose-placeholder.d.ts.map +1 -1
- package/dist/core/validation/purpose-placeholder.js +30 -44
- package/dist/core/validation/purpose-placeholder.js.map +1 -1
- package/dist/core/validation/section-validator.d.ts +4 -4
- package/dist/core/validation/section-validator.d.ts.map +1 -1
- package/dist/core/validation/section-validator.js +43 -7
- package/dist/core/validation/section-validator.js.map +1 -1
- package/dist/core/validation/task-numbering.d.ts +6 -3
- package/dist/core/validation/task-numbering.d.ts.map +1 -1
- package/dist/core/validation/task-numbering.js +23 -11
- package/dist/core/validation/task-numbering.js.map +1 -1
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.d.ts.map +1 -1
- package/dist/core/validation/types.js +12 -1
- package/dist/core/validation/types.js.map +1 -1
- package/dist/core/validation/validator.d.ts +50 -51
- package/dist/core/validation/validator.d.ts.map +1 -1
- package/dist/core/validation/validator.js +486 -263
- package/dist/core/validation/validator.js.map +1 -1
- package/dist/prompts/searchable-multi-select.d.ts +3 -8
- package/dist/prompts/searchable-multi-select.d.ts.map +1 -1
- package/dist/prompts/searchable-multi-select.js +16 -39
- package/dist/prompts/searchable-multi-select.js.map +1 -1
- package/dist/utils/change-metadata.d.ts +11 -50
- package/dist/utils/change-metadata.d.ts.map +1 -1
- package/dist/utils/change-metadata.js +48 -67
- package/dist/utils/change-metadata.js.map +1 -1
- package/dist/utils/change-utils.d.ts +31 -54
- package/dist/utils/change-utils.d.ts.map +1 -1
- package/dist/utils/change-utils.js +143 -100
- package/dist/utils/change-utils.js.map +1 -1
- package/dist/utils/file-lock.d.ts +39 -0
- package/dist/utils/file-lock.d.ts.map +1 -0
- package/dist/utils/file-lock.js +149 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/dist/utils/file-system.d.ts +12 -32
- package/dist/utils/file-system.d.ts.map +1 -1
- package/dist/utils/file-system.js +16 -40
- package/dist/utils/file-system.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +7 -11
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +11 -11
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/interactive.d.ts +4 -9
- package/dist/utils/interactive.d.ts.map +1 -1
- package/dist/utils/interactive.js +2 -4
- package/dist/utils/interactive.js.map +1 -1
- package/dist/utils/item-discovery.d.ts +15 -10
- package/dist/utils/item-discovery.d.ts.map +1 -1
- package/dist/utils/item-discovery.js +42 -47
- package/dist/utils/item-discovery.js.map +1 -1
- package/dist/utils/link.d.ts +13 -4
- package/dist/utils/link.d.ts.map +1 -1
- package/dist/utils/link.js +13 -4
- package/dist/utils/link.js.map +1 -1
- package/dist/utils/match.js.map +1 -1
- package/dist/utils/requirement-diff.d.ts +13 -23
- package/dist/utils/requirement-diff.d.ts.map +1 -1
- package/dist/utils/requirement-diff.js +13 -23
- package/dist/utils/requirement-diff.js.map +1 -1
- package/dist/utils/spec-files.d.ts +10 -11
- package/dist/utils/spec-files.d.ts.map +1 -1
- package/dist/utils/spec-files.js +31 -22
- package/dist/utils/spec-files.js.map +1 -1
- package/dist/utils/task-progress.d.ts +11 -9
- package/dist/utils/task-progress.d.ts.map +1 -1
- package/dist/utils/task-progress.js +53 -32
- package/dist/utils/task-progress.js.map +1 -1
- package/dist/utils/timestamp.d.ts +5 -8
- package/dist/utils/timestamp.d.ts.map +1 -1
- package/dist/utils/timestamp.js +5 -8
- package/dist/utils/timestamp.js.map +1 -1
- package/package.json +9 -10
- package/schemas/decision/templates/decision.md +3 -1
- package/schemas/decision/templates/index.md +2 -2
- package/schemas/issue/schema.yaml +11 -2
- package/schemas/issue/templates/spec.md +37 -3
- package/schemas/sdd/schema.yaml +24 -1
- package/schemas/sdd/templates/spec.md +37 -3
package/dist/cli/index.js
CHANGED
|
@@ -1,45 +1,79 @@
|
|
|
1
|
-
import { emitFailure } from '../commands/shared-output.js';
|
|
2
|
-
import { Command, Option, InvalidArgumentError } from 'commander';
|
|
1
|
+
import { emitFailure, isCliAbort, emitFailureStatus, normalizeCommanderCode, } from '../commands/shared-output.js';
|
|
2
|
+
import { Command, CommanderError, Option, InvalidArgumentError } from 'commander';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { promises as fs } from 'fs';
|
|
7
7
|
import { AI_TOOLS } from '../core/config.js';
|
|
8
|
-
import { UpdateCommand } from '../core/update.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { MigrateCommand } from '../core/migrate.js';
|
|
8
|
+
import { UpdateCommand, UPDATE_FAILURE_PAYLOAD } from '../core/update.js';
|
|
9
|
+
import { INIT_FAILURE_PAYLOAD } from '../core/init.js';
|
|
10
|
+
import { RULES_FAILURE_PAYLOAD } from '../core/rules.js';
|
|
11
|
+
import { MigrateCommand, MIGRATE_FAILURE_PAYLOAD } from '../core/migrate.js';
|
|
12
12
|
import { resolveRootForCommand, resolveTospecRoot, isRootSelectionError, toRootOutput, } from '../core/root-selection.js';
|
|
13
|
-
import { ValidateCommand } from '../commands/validate.js';
|
|
14
|
-
import { ShowCommand } from '../commands/show.js';
|
|
13
|
+
import { ValidateCommand, VALIDATE_FAILURE_PAYLOAD } from '../commands/validate.js';
|
|
14
|
+
import { ShowCommand, SHOW_FAILURE_PAYLOAD } from '../commands/show.js';
|
|
15
15
|
import { isInteractive } from '../utils/interactive.js';
|
|
16
|
-
import { registerConfigCommand } from '../commands/config.js';
|
|
17
|
-
import { registerDecisionCommand } from '../commands/decision.js';
|
|
16
|
+
import { registerConfigCommand, CONFIG_FAILURE_PAYLOADS } from '../commands/config.js';
|
|
17
|
+
import { registerDecisionCommand, DECISION_NEW_FAILURE_PAYLOAD, DECISION_LIST_FAILURE_PAYLOAD, } from '../commands/decision.js';
|
|
18
18
|
import { registerMetricsCommand } from '../commands/metrics.js';
|
|
19
|
-
import { statusCommand, BATCH_STATUS_FAILURE_PAYLOAD, instructionsCommand, applyInstructionsCommand, templatesCommand, schemasCommand, newChangeCommand, DEFAULT_SCHEMA, } from '../commands/workflow/index.js';
|
|
19
|
+
import { statusCommand, BATCH_STATUS_FAILURE_PAYLOAD, STATUS_FAILURE_PAYLOAD, instructionsCommand, applyInstructionsCommand, INSTRUCTIONS_FAILURE_PAYLOAD, APPLY_ARGUMENT, templatesCommand, TEMPLATES_FAILURE_PAYLOAD, schemasCommand, SCHEMAS_FAILURE_PAYLOAD, newChangeCommand, NEW_CHANGE_FAILURE_PAYLOAD, DEFAULT_SCHEMA, } from '../commands/workflow/index.js';
|
|
20
20
|
/**
|
|
21
21
|
* The options the user actually typed.
|
|
22
22
|
*
|
|
23
|
-
* Commander materialises a negatable option's default
|
|
24
|
-
* `scenarios: true`
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* ignored `scenarios` flag on every single run.
|
|
28
|
-
*
|
|
29
|
-
* Dropping the defaults here makes the two questions the same, rather than
|
|
30
|
-
* teaching each consumer a table of per-flag defaults to keep in sync. Safe
|
|
31
|
-
* because every consumer already treats a missing flag and its default
|
|
32
|
-
* identically (`options.scenarios !== false`, `options.interactive === false`).
|
|
23
|
+
* Commander materialises a negatable option's default (`--no-scenarios` puts
|
|
24
|
+
* `scenarios: true` in even when nobody typed it), so a body testing presence
|
|
25
|
+
* answers the wrong question. Safe to drop the defaults here because every
|
|
26
|
+
* consumer treats a missing flag and its default identically.
|
|
33
27
|
*/
|
|
34
28
|
function userSuppliedOptions(options, command) {
|
|
35
29
|
if (!options)
|
|
36
30
|
return {};
|
|
37
31
|
return Object.fromEntries(Object.entries(options).filter(([key]) => command.getOptionValueSource(key) !== 'default'));
|
|
38
32
|
}
|
|
33
|
+
/** `--concurrency` / `--port`: digits only, so `''`, `1.5` and `0x10` are all refused. */
|
|
34
|
+
function positiveIntegerArg(value) {
|
|
35
|
+
if (!/^\d+$/.test(value.trim()) || Number(value) <= 0) {
|
|
36
|
+
throw new InvalidArgumentError('Expected a positive integer.');
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
function portArg(value) {
|
|
41
|
+
if (!/^\d+$/.test(value.trim()) || Number(value) > 65535) {
|
|
42
|
+
throw new InvalidArgumentError('Expected an integer between 0 and 65535.');
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
const jsonFailureShapes = new WeakMap();
|
|
47
|
+
function withJsonFailureShape(command, shape) {
|
|
48
|
+
jsonFailureShapes.set(command, shape);
|
|
49
|
+
return command;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Makes commander throw instead of exiting, for the whole command tree. Applied
|
|
53
|
+
* after every subcommand is registered: commander copies `_exitCallback` into a
|
|
54
|
+
* subcommand at `.command()` time, so setting it on the root alone leaves every
|
|
55
|
+
* subcommand still calling `process.exit`.
|
|
56
|
+
*/
|
|
57
|
+
function throwOnCommanderExit(command) {
|
|
58
|
+
command.exitOverride((error) => {
|
|
59
|
+
// Attach the command so runCli can recover its null-shape; commander's
|
|
60
|
+
// error carries only a code and a message.
|
|
61
|
+
error.tospecCommand = command;
|
|
62
|
+
throw error;
|
|
63
|
+
});
|
|
64
|
+
for (const child of command.commands)
|
|
65
|
+
throwOnCommanderExit(child);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The root output for a command that takes its root as a path argument rather
|
|
69
|
+
* than searching: `init`, `update`, `rules`, `migrate`. Their success payloads
|
|
70
|
+
* report `source: 'explicit'`, so their failures say the same.
|
|
71
|
+
*/
|
|
72
|
+
function explicitRootOutput(targetPath) {
|
|
73
|
+
return { path: path.resolve(targetPath), source: 'explicit' };
|
|
74
|
+
}
|
|
75
|
+
/** The agent contract: one JSON document on stdout per `--json` failure. */
|
|
39
76
|
function failWithError(error, json) {
|
|
40
|
-
// The agent contract: every --json failure leaves exactly one JSON
|
|
41
|
-
// document on stdout (the command's null-shape plus a status array).
|
|
42
|
-
// Delegates to the one shared failure sink (shared-output.emitFailure).
|
|
43
77
|
emitFailure(json?.enabled, json?.payload ?? {}, error, json?.fallbackCode ?? 'command_error');
|
|
44
78
|
}
|
|
45
79
|
const program = new Command();
|
|
@@ -49,20 +83,24 @@ program
|
|
|
49
83
|
.name('tospec')
|
|
50
84
|
.description('Spec-driven development CLI for requirements and issue workflows')
|
|
51
85
|
.version(version);
|
|
52
|
-
// Kept for backward compatibility
|
|
53
|
-
//
|
|
86
|
+
// Kept for backward compatibility; output has no color to disable, so this is a
|
|
87
|
+
// no-op.
|
|
54
88
|
program.option('--no-color', 'Disable color output (no-op: output is plain text)');
|
|
55
|
-
// -----------------------------------------------------------------------------
|
|
56
|
-
// Install layer
|
|
57
|
-
// -----------------------------------------------------------------------------
|
|
58
89
|
const availableToolIds = AI_TOOLS.filter((tool) => tool.skillsDir).map((tool) => tool.value);
|
|
59
|
-
|
|
90
|
+
// Derived from the same table so a future dependency documents itself: a value
|
|
91
|
+
// that drags another target in otherwise reads as "pick this or don't".
|
|
92
|
+
const toolDependencyNotes = AI_TOOLS
|
|
93
|
+
.filter((tool) => tool.requires?.length)
|
|
94
|
+
.map((tool) => `${tool.value} implies ${tool.requires.join(', ')}`)
|
|
95
|
+
.join('; ');
|
|
96
|
+
const toolsOptionDescription = `Configure AI tools non-interactively. Use "all", "none", or a comma-separated list of: ${availableToolIds.join(', ')}${toolDependencyNotes ? ` (${toolDependencyNotes})` : ''}`;
|
|
60
97
|
program
|
|
61
98
|
.command('init [path]')
|
|
62
99
|
.description('Initialize tospec in your project')
|
|
63
100
|
.option('--tools <tools>', toolsOptionDescription)
|
|
64
|
-
.option('--force', 'Refresh existing generated files without prompting')
|
|
101
|
+
.option('--force', 'Refresh existing generated files without prompting; does not bypass rule conflicts')
|
|
65
102
|
.option('--profile <profile>', 'Override global config profile (core or custom)')
|
|
103
|
+
.option('--json', 'Output as JSON (for programmatic use)')
|
|
66
104
|
.action(async (targetPath = '.', options) => {
|
|
67
105
|
try {
|
|
68
106
|
const resolvedPath = path.resolve(targetPath);
|
|
@@ -88,37 +126,70 @@ program
|
|
|
88
126
|
tools: options?.tools,
|
|
89
127
|
force: options?.force,
|
|
90
128
|
profile: options?.profile,
|
|
129
|
+
json: options?.json,
|
|
91
130
|
});
|
|
92
131
|
await initCommand.execute(targetPath);
|
|
93
132
|
}
|
|
94
133
|
catch (error) {
|
|
95
|
-
failWithError(error
|
|
134
|
+
failWithError(error, {
|
|
135
|
+
enabled: options?.json,
|
|
136
|
+
payload: { ...INIT_FAILURE_PAYLOAD, root: explicitRootOutput(targetPath) },
|
|
137
|
+
fallbackCode: 'init_error',
|
|
138
|
+
});
|
|
96
139
|
}
|
|
97
140
|
});
|
|
98
141
|
program
|
|
99
142
|
.command('update [path]')
|
|
100
143
|
.description('Update tospec instruction files')
|
|
101
|
-
.option('--force', 'Force update even when tools are up to date')
|
|
144
|
+
.option('--force', 'Force update even when tools are up to date; does not bypass rule conflicts')
|
|
145
|
+
.option('--json', 'Output as JSON (for programmatic use)')
|
|
102
146
|
.action(async (targetPath = '.', options) => {
|
|
103
147
|
try {
|
|
104
|
-
const updateCommand = new UpdateCommand({ force: options?.force });
|
|
148
|
+
const updateCommand = new UpdateCommand({ force: options?.force, json: options?.json });
|
|
105
149
|
await updateCommand.execute(targetPath);
|
|
106
150
|
}
|
|
107
151
|
catch (error) {
|
|
108
|
-
failWithError(error
|
|
152
|
+
failWithError(error, {
|
|
153
|
+
enabled: options?.json,
|
|
154
|
+
payload: { ...UPDATE_FAILURE_PAYLOAD, root: explicitRootOutput(targetPath) },
|
|
155
|
+
fallbackCode: 'update_error',
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
program
|
|
160
|
+
.command('rules [path]')
|
|
161
|
+
.description('Refresh workflow rules for tools with installed tospec skills')
|
|
162
|
+
.option('--json', 'Output as JSON (for programmatic use)')
|
|
163
|
+
.action(async (targetPath = '.', options) => {
|
|
164
|
+
try {
|
|
165
|
+
const { RulesCommand } = await import('../core/rules.js');
|
|
166
|
+
await new RulesCommand().execute(targetPath, { json: options?.json });
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
failWithError(error, {
|
|
170
|
+
enabled: options?.json,
|
|
171
|
+
payload: { ...RULES_FAILURE_PAYLOAD, root: explicitRootOutput(targetPath) },
|
|
172
|
+
fallbackCode: 'rules_error',
|
|
173
|
+
});
|
|
109
174
|
}
|
|
110
175
|
});
|
|
111
176
|
program
|
|
112
177
|
.command('migrate [openspec-dir]')
|
|
113
178
|
.description('Migrate an OpenSpec project into tospec format (default source: ./openspec)')
|
|
114
179
|
.option('-f, --force', 'Skip confirmation prompt')
|
|
180
|
+
.option('--json', 'Output as JSON (non-interactive; requires --force)')
|
|
115
181
|
.action(async (openspecDir, options) => {
|
|
116
182
|
try {
|
|
117
183
|
const migrateCommand = new MigrateCommand();
|
|
118
184
|
await migrateCommand.execute(openspecDir, options);
|
|
119
185
|
}
|
|
120
186
|
catch (error) {
|
|
121
|
-
failWithError(error
|
|
187
|
+
failWithError(error, {
|
|
188
|
+
enabled: options?.json,
|
|
189
|
+
// migrate always writes `tospec/` into the cwd, so the root is known here.
|
|
190
|
+
payload: { ...MIGRATE_FAILURE_PAYLOAD, root: explicitRootOutput(process.cwd()) },
|
|
191
|
+
fallbackCode: 'migrate_error',
|
|
192
|
+
});
|
|
122
193
|
}
|
|
123
194
|
});
|
|
124
195
|
program
|
|
@@ -130,31 +201,53 @@ program
|
|
|
130
201
|
.option('--type <type>', 'Filter changes by type: "requirement" or "issue"')
|
|
131
202
|
.option('--json', 'Output as JSON (for programmatic use)')
|
|
132
203
|
.action(async (options) => {
|
|
204
|
+
// Resolve first so a failure can still report where the CLI was pointed.
|
|
205
|
+
// The data key is `null`, never `[]`: an empty array is indistinguishable
|
|
206
|
+
// from the successful "no active changes" answer.
|
|
207
|
+
let resolvedRoot = null;
|
|
208
|
+
const failurePayload = () => ({
|
|
209
|
+
...(options?.specs ? { specs: null } : { changes: null }),
|
|
210
|
+
root: resolvedRoot ? toRootOutput(resolvedRoot) : null,
|
|
211
|
+
});
|
|
133
212
|
try {
|
|
134
|
-
|
|
135
|
-
throw new Error(`Invalid --type '${options.type}'. Expected "requirement" or "issue".`);
|
|
136
|
-
}
|
|
137
|
-
const root = await resolveRootForCommand({}, {
|
|
213
|
+
resolvedRoot = await resolveRootForCommand({}, {
|
|
138
214
|
json: options?.json,
|
|
139
|
-
|
|
215
|
+
// An implicit root means no ancestor is a tospec project, and listing
|
|
216
|
+
// "no changes" there answers about a place that was never checked.
|
|
217
|
+
allowImplicitRoot: false,
|
|
218
|
+
failurePayload: failurePayload(),
|
|
140
219
|
});
|
|
141
|
-
if (!
|
|
220
|
+
if (!resolvedRoot) {
|
|
142
221
|
return;
|
|
143
222
|
}
|
|
223
|
+
if (options?.type && options.type !== 'requirement' && options.type !== 'issue') {
|
|
224
|
+
throw new Error(`Invalid --type '${options.type}'. Expected "requirement" or "issue".`);
|
|
225
|
+
}
|
|
226
|
+
// Two listings with no ordering between them; picking one silently
|
|
227
|
+
// answers a question the user did not ask.
|
|
228
|
+
if (options?.specs && options?.changes) {
|
|
229
|
+
throw new Error('Pass either --specs or --changes, not both: they select different listings and there is no ordering between them.');
|
|
230
|
+
}
|
|
231
|
+
// Specs have no type. A warning, not an error: the requested listing is
|
|
232
|
+
// still well-defined. On stderr, so `--json` stdout stays one document.
|
|
233
|
+
if (options?.specs && options?.type) {
|
|
234
|
+
console.error('Warning: Ignoring flags not applicable to --specs: type');
|
|
235
|
+
}
|
|
236
|
+
const { ListCommand } = await import('../core/list.js');
|
|
144
237
|
const listCommand = new ListCommand();
|
|
145
238
|
const mode = options?.specs ? 'specs' : 'changes';
|
|
146
239
|
const sort = options?.sort === 'name' ? 'name' : 'recent';
|
|
147
|
-
await listCommand.execute(
|
|
240
|
+
await listCommand.execute(resolvedRoot.path, mode, {
|
|
148
241
|
sort,
|
|
149
242
|
json: options?.json,
|
|
150
243
|
type: options?.type,
|
|
151
|
-
...(options?.json ? { root: toRootOutput(
|
|
244
|
+
...(options?.json ? { root: toRootOutput(resolvedRoot) } : {}),
|
|
152
245
|
});
|
|
153
246
|
}
|
|
154
247
|
catch (error) {
|
|
155
248
|
failWithError(error, {
|
|
156
249
|
enabled: options?.json,
|
|
157
|
-
payload:
|
|
250
|
+
payload: failurePayload(),
|
|
158
251
|
fallbackCode: 'list_error',
|
|
159
252
|
});
|
|
160
253
|
}
|
|
@@ -163,12 +256,13 @@ program
|
|
|
163
256
|
.command('archive [change-name]')
|
|
164
257
|
.description('Archive a completed change and update main specs')
|
|
165
258
|
.option('-y, --yes', 'Skip confirmation prompts')
|
|
166
|
-
.option('--skip-specs', 'Skip
|
|
259
|
+
.option('--skip-specs', 'Skip merging delta specs into the main specs for this run only; validation still applies, so an sdd change with no deltas also needs skip_specs: true in its .tospec.yaml (schemas whose specs are optional, such as issue, do not)')
|
|
167
260
|
.option('--no-validate', 'Skip validation (not recommended, requires confirmation)')
|
|
168
261
|
.option('--require-sync', 'Require a passing sync-report.md in the change directory before archiving')
|
|
169
262
|
.option('--json', 'Output as JSON (non-interactive)')
|
|
170
263
|
.action(async (changeName, options) => {
|
|
171
264
|
try {
|
|
265
|
+
const { ArchiveCommand } = await import('../core/archive.js');
|
|
172
266
|
const archiveCommand = new ArchiveCommand();
|
|
173
267
|
await archiveCommand.execute(changeName, options);
|
|
174
268
|
}
|
|
@@ -182,17 +276,13 @@ program
|
|
|
182
276
|
.option('--all', 'Validate all changes and specs')
|
|
183
277
|
.option('--changes', 'Validate all changes')
|
|
184
278
|
.option('--specs', 'Validate all specs')
|
|
185
|
-
|
|
279
|
+
// `.choices`, as `show --type` does: an unknown value used to fall through
|
|
280
|
+
// normalizeType as undefined and silently validate by auto-detection.
|
|
281
|
+
.addOption(new Option('--type <type>', 'Specify item type when ambiguous').choices(['change', 'spec']))
|
|
186
282
|
.option('--strict', 'Enable strict validation mode')
|
|
187
283
|
.option('--json', 'Output validation results as JSON')
|
|
188
284
|
.addOption(new Option('--concurrency <n>', 'Max concurrent validations (defaults to env TOSPEC_CONCURRENCY or 6)')
|
|
189
|
-
.argParser(
|
|
190
|
-
const n = parseInt(value, 10);
|
|
191
|
-
if (Number.isNaN(n) || n <= 0 || String(n) !== value.trim()) {
|
|
192
|
-
throw new InvalidArgumentError('Expected a positive integer.');
|
|
193
|
-
}
|
|
194
|
-
return value;
|
|
195
|
-
}))
|
|
285
|
+
.argParser(positiveIntegerArg))
|
|
196
286
|
.option('--no-interactive', 'Disable interactive prompts')
|
|
197
287
|
.action(async (itemName, options) => {
|
|
198
288
|
try {
|
|
@@ -200,17 +290,26 @@ program
|
|
|
200
290
|
await validateCommand.execute(itemName, options);
|
|
201
291
|
}
|
|
202
292
|
catch (error) {
|
|
203
|
-
failWithError(error, {
|
|
293
|
+
failWithError(error, {
|
|
294
|
+
enabled: options?.json,
|
|
295
|
+
payload: VALIDATE_FAILURE_PAYLOAD,
|
|
296
|
+
fallbackCode: 'validate_error',
|
|
297
|
+
});
|
|
204
298
|
}
|
|
205
299
|
});
|
|
206
300
|
program
|
|
207
301
|
.command('show [item-name]')
|
|
208
302
|
.description('Show a change or spec')
|
|
209
303
|
.option('--json', 'Output as JSON')
|
|
210
|
-
.
|
|
304
|
+
.addOption(new Option('--type <type>', 'Specify item type when ambiguous: change|spec').choices([
|
|
305
|
+
'change',
|
|
306
|
+
'spec',
|
|
307
|
+
]))
|
|
211
308
|
.option('--no-interactive', 'Disable interactive prompts')
|
|
212
|
-
|
|
213
|
-
|
|
309
|
+
// No-ops kept for compatibility: a change's --json payload is always
|
|
310
|
+
// id/title/deltaCount/deltas, with no wider shape to narrow.
|
|
311
|
+
.option('--deltas-only', 'No-op: a change\'s JSON is always deltas (kept for compatibility)')
|
|
312
|
+
.option('--requirements-only', 'No-op: deprecated alias for --deltas-only')
|
|
214
313
|
.option('--diff', 'Show per-requirement diffs against the main specs (change)')
|
|
215
314
|
.option('--requirements', 'JSON only: Show only requirements (exclude scenarios)')
|
|
216
315
|
.option('--no-scenarios', 'JSON only: Exclude scenario content')
|
|
@@ -218,17 +317,16 @@ program
|
|
|
218
317
|
.action(async (itemName, options, command) => {
|
|
219
318
|
try {
|
|
220
319
|
const showCommand = new ShowCommand();
|
|
221
|
-
// Defaults stripped so warnIrrelevantFlags reports the flags the user
|
|
222
|
-
// typed, not the ones commander filled in.
|
|
223
320
|
await showCommand.execute(itemName, userSuppliedOptions(options, command));
|
|
224
321
|
}
|
|
225
322
|
catch (error) {
|
|
226
|
-
failWithError(error, {
|
|
323
|
+
failWithError(error, {
|
|
324
|
+
enabled: options?.json,
|
|
325
|
+
payload: SHOW_FAILURE_PAYLOAD,
|
|
326
|
+
fallbackCode: 'show_error',
|
|
327
|
+
});
|
|
227
328
|
}
|
|
228
329
|
});
|
|
229
|
-
// ═══════════════════════════════════════════════════════════
|
|
230
|
-
// Workflow / engine commands (agent contract)
|
|
231
|
-
// ═══════════════════════════════════════════════════════════
|
|
232
330
|
program
|
|
233
331
|
.command('status')
|
|
234
332
|
.description('Display artifact completion status for a change')
|
|
@@ -243,9 +341,7 @@ program
|
|
|
243
341
|
catch (error) {
|
|
244
342
|
failWithError(error, {
|
|
245
343
|
enabled: options.json,
|
|
246
|
-
|
|
247
|
-
// pre-existing contract and stays payload-free.
|
|
248
|
-
payload: options.all ? BATCH_STATUS_FAILURE_PAYLOAD : undefined,
|
|
344
|
+
payload: options.all ? BATCH_STATUS_FAILURE_PAYLOAD : STATUS_FAILURE_PAYLOAD,
|
|
249
345
|
fallbackCode: 'change_error',
|
|
250
346
|
});
|
|
251
347
|
}
|
|
@@ -258,7 +354,7 @@ program
|
|
|
258
354
|
.option('--json', 'Output as JSON')
|
|
259
355
|
.action(async (artifactId, options) => {
|
|
260
356
|
try {
|
|
261
|
-
if (artifactId ===
|
|
357
|
+
if (artifactId === APPLY_ARGUMENT) {
|
|
262
358
|
await applyInstructionsCommand(options);
|
|
263
359
|
}
|
|
264
360
|
else {
|
|
@@ -266,7 +362,11 @@ program
|
|
|
266
362
|
}
|
|
267
363
|
}
|
|
268
364
|
catch (error) {
|
|
269
|
-
failWithError(error, {
|
|
365
|
+
failWithError(error, {
|
|
366
|
+
enabled: options.json,
|
|
367
|
+
payload: INSTRUCTIONS_FAILURE_PAYLOAD,
|
|
368
|
+
fallbackCode: 'change_error',
|
|
369
|
+
});
|
|
270
370
|
}
|
|
271
371
|
});
|
|
272
372
|
program
|
|
@@ -279,7 +379,7 @@ program
|
|
|
279
379
|
await templatesCommand(options);
|
|
280
380
|
}
|
|
281
381
|
catch (error) {
|
|
282
|
-
failWithError(error, { enabled: options.json, payload:
|
|
382
|
+
failWithError(error, { enabled: options.json, payload: TEMPLATES_FAILURE_PAYLOAD, fallbackCode: 'templates_error' });
|
|
283
383
|
}
|
|
284
384
|
});
|
|
285
385
|
program
|
|
@@ -291,7 +391,7 @@ program
|
|
|
291
391
|
await schemasCommand(options);
|
|
292
392
|
}
|
|
293
393
|
catch (error) {
|
|
294
|
-
failWithError(error, { enabled: options.json, payload:
|
|
394
|
+
failWithError(error, { enabled: options.json, payload: SCHEMAS_FAILURE_PAYLOAD, fallbackCode: 'schemas_error' });
|
|
295
395
|
}
|
|
296
396
|
});
|
|
297
397
|
const newCmd = program.command('new').description('Create new items');
|
|
@@ -315,8 +415,10 @@ newCmd
|
|
|
315
415
|
});
|
|
316
416
|
program
|
|
317
417
|
.command('dashboard [path]')
|
|
318
|
-
.description('Start a local
|
|
319
|
-
|
|
418
|
+
.description('Start a local dashboard; task checkbox updates are the only writes, confined to this tospec root and blocked for archived or sync-certified changes')
|
|
419
|
+
// Parsed like --concurrency: `Number('')` is 0, so a bare `--port ''` used to
|
|
420
|
+
// pass the range check and bind an OS-chosen port.
|
|
421
|
+
.addOption(new Option('-p, --port <n>', 'Port to listen on (0-65535)').default('5620').argParser(portArg))
|
|
320
422
|
.option('--host <addr>', 'Host to bind', '127.0.0.1')
|
|
321
423
|
.option('-o, --open', 'Open the dashboard in a browser after starting')
|
|
322
424
|
.option('-d, --detach', 'Run the dashboard in the background and return immediately')
|
|
@@ -326,10 +428,10 @@ program
|
|
|
326
428
|
.action(async (targetPath = '.', options) => {
|
|
327
429
|
try {
|
|
328
430
|
const dashboard = await import('../commands/dashboard.js');
|
|
329
|
-
// --list is global:
|
|
431
|
+
// --list is global: no tospec project required.
|
|
330
432
|
if (options?.list) {
|
|
331
|
-
// Same candidate set as --stop,
|
|
332
|
-
//
|
|
433
|
+
// Same candidate set as --stop, which points here when it cannot
|
|
434
|
+
// prompt, so the two never disagree about what is running.
|
|
333
435
|
const running = await dashboard.collectStoppableDashboards(null);
|
|
334
436
|
if (running.length === 0) {
|
|
335
437
|
console.log('No dashboards are running.');
|
|
@@ -345,7 +447,7 @@ program
|
|
|
345
447
|
const startPath = path.resolve(targetPath);
|
|
346
448
|
// --stop is project-independent, like --list: a path with no tospec root
|
|
347
449
|
// has no dashboard of its own, which is the cue to offer the full list
|
|
348
|
-
// rather than fail.
|
|
450
|
+
// rather than fail.
|
|
349
451
|
if (options?.stop) {
|
|
350
452
|
const stopRoot = await resolveTospecRoot({ startPath, allowImplicitRoot: false }).catch((err) => {
|
|
351
453
|
if (isRootSelectionError(err))
|
|
@@ -360,9 +462,8 @@ program
|
|
|
360
462
|
}
|
|
361
463
|
let targets = dashboard.selectStopTargets(candidates, stopRootPath);
|
|
362
464
|
if (targets.length === 0) {
|
|
363
|
-
// Nothing
|
|
364
|
-
//
|
|
365
|
-
// non-interactively, where there is nobody to answer the prompt.
|
|
465
|
+
// Nothing matches this path, so let the user pick. Reachable
|
|
466
|
+
// non-interactively too, where there is nobody to answer.
|
|
366
467
|
const listed = candidates.map((d) => ` ${dashboard.formatRunningDashboard(d, ' ')}`);
|
|
367
468
|
if (!isInteractive()) {
|
|
368
469
|
throw new dashboard.DashboardStopError(['No dashboard for this path, and no terminal to choose one from.', ...listed].join('\n'), 'Re-run from an interactive terminal, or see tospec dashboard --list.');
|
|
@@ -392,10 +493,12 @@ program
|
|
|
392
493
|
}
|
|
393
494
|
return;
|
|
394
495
|
}
|
|
496
|
+
// Already validated by portArg; the default is the only unparsed value.
|
|
497
|
+
const portValue = Number(options?.port ?? 5620);
|
|
395
498
|
// Starting a dashboard still requires a root — only --stop is lenient.
|
|
396
499
|
const root = await resolveTospecRoot({ startPath, allowImplicitRoot: false });
|
|
397
500
|
const runOpts = {
|
|
398
|
-
port:
|
|
501
|
+
port: portValue,
|
|
399
502
|
host: options?.host ?? '127.0.0.1',
|
|
400
503
|
open: options?.open,
|
|
401
504
|
allowRemote: options?.allowRemote,
|
|
@@ -413,9 +516,90 @@ program
|
|
|
413
516
|
registerConfigCommand(program);
|
|
414
517
|
registerDecisionCommand(program);
|
|
415
518
|
registerMetricsCommand(program);
|
|
519
|
+
function commandAt(...names) {
|
|
520
|
+
let current = program;
|
|
521
|
+
for (const name of names) {
|
|
522
|
+
const next = current.commands.find((c) => c.name() === name);
|
|
523
|
+
if (!next)
|
|
524
|
+
throw new Error(`No such command to register a JSON failure shape for: ${names.join(' ')}`);
|
|
525
|
+
current = next;
|
|
526
|
+
}
|
|
527
|
+
return current;
|
|
528
|
+
}
|
|
529
|
+
// The `--json` null-shapes in one table, so the contract rule (a failure payload
|
|
530
|
+
// mirrors its success payload with the data keys nulled) is checkable in one
|
|
531
|
+
// place. Each entry reuses the object the command's own catch block passes to
|
|
532
|
+
// `failWithError`, never a literal: commander gives every command two failure
|
|
533
|
+
// layers, and copies drift.
|
|
534
|
+
withJsonFailureShape(commandAt('init'), () => INIT_FAILURE_PAYLOAD);
|
|
535
|
+
withJsonFailureShape(commandAt('update'), () => UPDATE_FAILURE_PAYLOAD);
|
|
536
|
+
withJsonFailureShape(commandAt('rules'), () => RULES_FAILURE_PAYLOAD);
|
|
537
|
+
withJsonFailureShape(commandAt('migrate'), () => MIGRATE_FAILURE_PAYLOAD);
|
|
538
|
+
withJsonFailureShape(commandAt('list'), (argv) => argv.includes('--specs') ? { specs: null, root: null } : { changes: null, root: null });
|
|
539
|
+
withJsonFailureShape(commandAt('archive'), () => ({ archive: null, root: null }));
|
|
540
|
+
withJsonFailureShape(commandAt('validate'), () => VALIDATE_FAILURE_PAYLOAD);
|
|
541
|
+
withJsonFailureShape(commandAt('show'), () => SHOW_FAILURE_PAYLOAD);
|
|
542
|
+
withJsonFailureShape(commandAt('status'), (argv) => argv.includes('--all') ? BATCH_STATUS_FAILURE_PAYLOAD : STATUS_FAILURE_PAYLOAD);
|
|
543
|
+
withJsonFailureShape(commandAt('instructions'), () => INSTRUCTIONS_FAILURE_PAYLOAD);
|
|
544
|
+
withJsonFailureShape(commandAt('templates'), () => TEMPLATES_FAILURE_PAYLOAD);
|
|
545
|
+
withJsonFailureShape(commandAt('schemas'), () => SCHEMAS_FAILURE_PAYLOAD);
|
|
546
|
+
withJsonFailureShape(commandAt('new', 'change'), () => NEW_CHANGE_FAILURE_PAYLOAD);
|
|
547
|
+
withJsonFailureShape(commandAt('decision', 'new'), () => DECISION_NEW_FAILURE_PAYLOAD);
|
|
548
|
+
withJsonFailureShape(commandAt('decision', 'list'), () => DECISION_LIST_FAILURE_PAYLOAD);
|
|
549
|
+
// `edit` speaks no --json and is deliberately absent.
|
|
550
|
+
for (const [subcommand, payload] of Object.entries(CONFIG_FAILURE_PAYLOADS)) {
|
|
551
|
+
withJsonFailureShape(commandAt('config', subcommand), () => payload);
|
|
552
|
+
}
|
|
416
553
|
export { program };
|
|
554
|
+
/**
|
|
555
|
+
* The command's full invocation path, not just its leaf name. `command.name()`
|
|
556
|
+
* is wrong for a nested command: `--help` advice naming the leaf of `new change`
|
|
557
|
+
* points at no command at all, and the leaf of `decision new` is a real command
|
|
558
|
+
* answering a different question.
|
|
559
|
+
*/
|
|
560
|
+
function commandPath(command) {
|
|
561
|
+
const names = [];
|
|
562
|
+
// Stops at the program itself, whose name the caller's template already has.
|
|
563
|
+
for (let node = command; node?.parent; node = node.parent) {
|
|
564
|
+
names.unshift(node.name());
|
|
565
|
+
}
|
|
566
|
+
return names.join(' ');
|
|
567
|
+
}
|
|
417
568
|
export function runCli(argv = process.argv) {
|
|
418
|
-
program
|
|
569
|
+
throwOnCommanderExit(program);
|
|
570
|
+
try {
|
|
571
|
+
program.parse(argv);
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
if (error instanceof CommanderError) {
|
|
575
|
+
// `--help` and `--version` come through here having already printed what
|
|
576
|
+
// they were asked for, so only a non-zero exit is a failure.
|
|
577
|
+
if (error.exitCode === 0)
|
|
578
|
+
return;
|
|
579
|
+
const wantsJson = argv.includes('--json');
|
|
580
|
+
if (wantsJson) {
|
|
581
|
+
const command = error.tospecCommand;
|
|
582
|
+
const shape = command ? jsonFailureShapes.get(command) : undefined;
|
|
583
|
+
emitFailureStatus(shape ? shape(argv) : {}, {
|
|
584
|
+
severity: 'error',
|
|
585
|
+
code: normalizeCommanderCode(error.code),
|
|
586
|
+
message: error.message,
|
|
587
|
+
fix: `See tospec ${commandPath(command)} --help`.replace(/\s+/g, ' ').trim(),
|
|
588
|
+
});
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
process.exitCode = error.exitCode || 1;
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
// The one abort mechanism for places that cannot stop the run by returning
|
|
595
|
+
// (commander's preAction hooks). Setting the exit code and returning lets
|
|
596
|
+
// stdout flush, which `process.exit` does not guarantee.
|
|
597
|
+
if (isCliAbort(error)) {
|
|
598
|
+
process.exitCode = 1;
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
throw error;
|
|
602
|
+
}
|
|
419
603
|
}
|
|
420
604
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
421
605
|
runCli();
|