@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
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Plumbing shared by every local web command (`dashboard`, `metrics`).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `serveAsset` takes its root directory as an argument — the commands serve
|
|
10
|
-
* different asset trees, and parameterising the directory is what lets one
|
|
11
|
-
* implementation serve both.
|
|
4
|
+
* Shared rather than copied per command because several of these are trust
|
|
5
|
+
* boundaries — `isAllowedHostHeader` (DNS rebinding), `serveAsset` (path
|
|
6
|
+
* escape), `assertBindableHost` (bind policy). A second implementation of any of
|
|
7
|
+
* them is a hole that survives fixing the first.
|
|
12
8
|
*/
|
|
13
9
|
import { promises as fs } from 'node:fs';
|
|
14
10
|
import * as path from 'node:path';
|
|
@@ -19,34 +15,82 @@ const ASSET_CONTENT_TYPES = {
|
|
|
19
15
|
'.js': 'text/javascript',
|
|
20
16
|
'.css': 'text/css',
|
|
21
17
|
};
|
|
22
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Canonical form of a bind address or Host-header name, so the sets below hold
|
|
20
|
+
* addresses rather than spellings: `::`, `::0` and `0:0:0:0:0:0:0:0` are one
|
|
21
|
+
* address a user may type after `--host`.
|
|
22
|
+
*
|
|
23
|
+
* The WHATWG URL parser is the normalizer because it is Node's only built-in
|
|
24
|
+
* IPv6/IPv4 canonicalizer. An unparseable value is returned lowercased, and
|
|
25
|
+
* every check here treats it as neither loopback nor wildcard.
|
|
26
|
+
*/
|
|
27
|
+
export function normalizeHost(host) {
|
|
28
|
+
const bare = host.trim().toLowerCase().replace(/^\[|\]$/g, '');
|
|
29
|
+
try {
|
|
30
|
+
// A bare IPv6 literal has to be bracketed before the URL parser will read
|
|
31
|
+
// its colons as an address rather than as a port separator.
|
|
32
|
+
const authority = bare.includes(':') ? `[${bare}]` : bare;
|
|
33
|
+
return new URL(`http://${authority}/`).hostname.replace(/^\[|\]$/g, '');
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return bare;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Hosts considered loopback for binding and Host-header checks, in the
|
|
41
|
+
* canonical form `normalizeHost` produces.
|
|
42
|
+
*/
|
|
23
43
|
export const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1']);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Bind addresses meaning "every interface", canonical forms only. No client
|
|
46
|
+
* sends these as a Host header, which is why `isAllowedHostHeader`'s literal
|
|
47
|
+
* match can never satisfy them. `::ffff:0:0` is listed because it survives
|
|
48
|
+
* normalization as a distinct string.
|
|
49
|
+
*/
|
|
50
|
+
export const WILDCARD_HOSTS = new Set(['0.0.0.0', '::', '::ffff:0:0']);
|
|
51
|
+
/**
|
|
52
|
+
* A URL a browser can actually open for a server bound to `host`. A wildcard
|
|
53
|
+
* bind has no connectable spelling — `http://0.0.0.0:5620` is not visitable —
|
|
54
|
+
* and `dashboard --list` reconstructs the loopback URL the same way.
|
|
55
|
+
*/
|
|
56
|
+
export function connectableUrl(host, port) {
|
|
57
|
+
// Normalized like every other check here: `::0` is a wildcard bind that
|
|
58
|
+
// `acceptsAnyHostHeader` recognises, so printing `http://[::0]:5620` for it
|
|
59
|
+
// handed the user a URL no browser can open.
|
|
60
|
+
const displayHost = WILDCARD_HOSTS.has(normalizeHost(host)) ? '127.0.0.1' : host;
|
|
61
|
+
// IPv6 literals need brackets in a URL; a bare `::1` parses as host `` with a
|
|
62
|
+
// port of `:1`.
|
|
63
|
+
const bracketed = displayHost.includes(':') ? `[${displayHost}]` : displayHost;
|
|
64
|
+
return `http://${bracketed}:${port}`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The bind guard, shared so a new local server cannot silently bind `0.0.0.0`.
|
|
68
|
+
*
|
|
69
|
+
* `remedy` is the caller's: `--allow-remote` is a dashboard flag that
|
|
70
|
+
* `skill-metrics` does not register, so each server states its own escape hatch.
|
|
71
|
+
*/
|
|
72
|
+
export function assertBindableHost(host, serverName, allowRemote, remedy) {
|
|
73
|
+
if (LOOPBACK_HOSTS.has(normalizeHost(host)) || allowRemote)
|
|
74
|
+
return;
|
|
75
|
+
throw new Error(`Refusing to bind non-loopback host '${host}': the ${serverName} would be readable by anyone who can reach this machine. ${remedy}`);
|
|
76
|
+
}
|
|
27
77
|
export function sendJson(res, status, payload) {
|
|
28
78
|
res.writeHead(status, { 'content-type': 'application/json; charset=utf-8' });
|
|
29
79
|
res.end(JSON.stringify(payload));
|
|
30
80
|
}
|
|
31
81
|
/**
|
|
32
|
-
* Absolute path to a shipped asset tree (`assets/<name>`), resolved
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* The depth is load-bearing: compiled to `dist/core/`, two levels up is the
|
|
36
|
-
* package root, which is also what `dist/commands/` yields. Assets are shipped
|
|
37
|
-
* as package `files` and never compiled, so moving this module to a different
|
|
38
|
-
* depth would silently resolve outside the package. Every caller goes through
|
|
39
|
-
* here so that constraint has exactly one home.
|
|
82
|
+
* Absolute path to a shipped asset tree (`assets/<name>`), resolved from this
|
|
83
|
+
* module's own URL. The depth is load-bearing: compiled to `dist/core/`, two
|
|
84
|
+
* levels up is the package root, so moving this module resolves outside it.
|
|
40
85
|
*/
|
|
41
86
|
export function packageAssetsDir(name) {
|
|
42
87
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
43
88
|
return path.join(here, '..', '..', 'assets', name);
|
|
44
89
|
}
|
|
45
90
|
/**
|
|
46
|
-
* Serves one file from `dir`.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* request and the rest of the filesystem.
|
|
91
|
+
* Serves one file from `dir`. URL parsing already normalises `..`, but a
|
|
92
|
+
* resolved path outside `dir` is refused regardless: this is the only thing
|
|
93
|
+
* between a crafted request and the rest of the filesystem.
|
|
50
94
|
*/
|
|
51
95
|
export async function serveAsset(res, dir, rawName) {
|
|
52
96
|
const name = decodeURIComponent(rawName);
|
|
@@ -68,31 +112,45 @@ export async function serveAsset(res, dir, rawName) {
|
|
|
68
112
|
res.writeHead(200, { 'content-type': `${type}; charset=utf-8` });
|
|
69
113
|
res.end(content);
|
|
70
114
|
}
|
|
71
|
-
// -----------------------------------------------------------------------------
|
|
72
|
-
// Trust boundary
|
|
73
|
-
// -----------------------------------------------------------------------------
|
|
74
115
|
/**
|
|
75
116
|
* DNS-rebinding guard: only requests whose Host header names loopback (or the
|
|
76
117
|
* explicitly bound non-loopback address) are served. A malicious page can make
|
|
77
118
|
* a victim's browser resolve an attacker domain to 127.0.0.1 and read the
|
|
78
|
-
* server's endpoints; the attacker domain in the Host header
|
|
79
|
-
* away.
|
|
119
|
+
* server's endpoints; the attacker domain in the Host header gives that away.
|
|
80
120
|
*/
|
|
81
|
-
export function isAllowedHostHeader(hostHeader, boundHost) {
|
|
121
|
+
export function isAllowedHostHeader(hostHeader, boundHost, acceptAnyHost = false) {
|
|
82
122
|
if (!hostHeader)
|
|
83
123
|
return false;
|
|
84
124
|
// Strip the port; IPv6 hosts arrive bracketed ([::1]:5620).
|
|
85
125
|
const match = hostHeader.match(/^(\[[^\]]+\]|[^:]+)(:\d+)?$/);
|
|
86
126
|
if (!match)
|
|
87
127
|
return false;
|
|
88
|
-
|
|
128
|
+
// Normalized on both sides so the comparison is between addresses, not
|
|
129
|
+
// between the client's spelling and the operator's.
|
|
130
|
+
const name = normalizeHost(match[1]);
|
|
89
131
|
if (LOOPBACK_HOSTS.has(name))
|
|
90
132
|
return true;
|
|
91
|
-
|
|
133
|
+
// Last, and after parsing: the opt-in widens who may be named, not what counts
|
|
134
|
+
// as a name.
|
|
135
|
+
if (acceptAnyHost)
|
|
136
|
+
return true;
|
|
137
|
+
return name === normalizeHost(boundHost);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The Host-header policy, resolved once at startup rather than per request —
|
|
141
|
+
* see decisions/20260910_150312-allow-remote-wildcard-host-policy.md.
|
|
142
|
+
*
|
|
143
|
+
* A wildcard bind has no Host header that can literally match it, so
|
|
144
|
+
* `--allow-remote --host 0.0.0.0` would listen on every interface then 403 every
|
|
145
|
+
* request. Only that combination is relaxed; a concrete address names the host
|
|
146
|
+
* worth accepting. Kept out of `isAllowedHostHeader` so "wildcard means trust
|
|
147
|
+
* everyone" stays visible at the call site.
|
|
148
|
+
*/
|
|
149
|
+
export function acceptsAnyHostHeader(boundHost, allowRemote) {
|
|
150
|
+
if (!allowRemote)
|
|
151
|
+
return false;
|
|
152
|
+
return WILDCARD_HOSTS.has(normalizeHost(boundHost));
|
|
92
153
|
}
|
|
93
|
-
// -----------------------------------------------------------------------------
|
|
94
|
-
// Lifecycle
|
|
95
|
-
// -----------------------------------------------------------------------------
|
|
96
154
|
/** Resolves once the server is listening, or rejects with the listen error. */
|
|
97
155
|
export function listenOnce(server, port, host) {
|
|
98
156
|
return new Promise((resolve, reject) => {
|
|
@@ -110,17 +168,12 @@ export function listenOnce(server, port, host) {
|
|
|
110
168
|
});
|
|
111
169
|
}
|
|
112
170
|
/**
|
|
113
|
-
* Binds `basePort`, advancing
|
|
114
|
-
*
|
|
115
|
-
* rule — a predictable port you can keep in your head, that still yields rather
|
|
116
|
-
* than failing when something already holds it.
|
|
171
|
+
* Binds `basePort`, advancing on `EADDRINUSE` until one is free, and returns the
|
|
172
|
+
* port actually bound.
|
|
117
173
|
*
|
|
118
|
-
* `port: 0`
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* The attempt bound is a backstop, not a policy: without it a machine that
|
|
123
|
-
* refuses every bind for some other reason would spin through 65k ports.
|
|
174
|
+
* `port: 0` passes straight through — the OS picks a free port, so there is
|
|
175
|
+
* nothing to retry. `maxAttempts` stops a machine that refuses every bind from
|
|
176
|
+
* spinning through 65k ports.
|
|
124
177
|
*/
|
|
125
178
|
export async function listenFrom(server, basePort, host, maxAttempts = 200) {
|
|
126
179
|
let port = basePort;
|
|
@@ -141,14 +194,7 @@ export async function listenFrom(server, basePort, host, maxAttempts = 200) {
|
|
|
141
194
|
const address = server.address();
|
|
142
195
|
return typeof address === 'object' && address ? address.port : port;
|
|
143
196
|
}
|
|
144
|
-
|
|
145
|
-
// Browser
|
|
146
|
-
// -----------------------------------------------------------------------------
|
|
147
|
-
/**
|
|
148
|
-
* Builds the platform command that opens `url` in the default browser.
|
|
149
|
-
* Pure (no side effects) so the platform branching is testable without
|
|
150
|
-
* launching anything.
|
|
151
|
-
*/
|
|
197
|
+
/** Pure, so the platform branching is testable without launching anything. */
|
|
152
198
|
export function browserOpenCommand(url, platform = process.platform) {
|
|
153
199
|
if (platform === 'win32')
|
|
154
200
|
return `start "" "${url}"`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-server.js","sourceRoot":"","sources":["../../src/core/local-server.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"local-server.js","sourceRoot":"","sources":["../../src/core/local-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown → HTML for the dashboard's `/api/render`, and a trust boundary rather
|
|
3
|
+
* than a formatting detail: the output is assigned with `innerHTML`, so it runs
|
|
4
|
+
* in the same origin that answers `POST /api/task`. The CSRF defence on
|
|
5
|
+
* `handleTaskUpdate` only stops *cross-origin* callers, so a handler smuggled in
|
|
6
|
+
* through a markdown file would sit inside it.
|
|
7
|
+
*
|
|
8
|
+
* Raw HTML is escaped, not allowlisted: an allowlist has to stay ahead of every
|
|
9
|
+
* attribute and scheme that can execute, and these documents need no markup
|
|
10
|
+
* markdown cannot express.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The URL when it is safe to emit as an attribute, otherwise null. Two rules,
|
|
14
|
+
* both against spellings a naive scheme regex waves through:
|
|
15
|
+
*
|
|
16
|
+
* 1. Tabs and newlines are stripped first. `java	script:alert(1)` matches no
|
|
17
|
+
* `^javascript:` regex, but the URL parser — and the browser — strips them
|
|
18
|
+
* and is left with `javascript:`.
|
|
19
|
+
* 2. The scheme comes from `URL`, not the raw text, so case variants and
|
|
20
|
+
* percent-encoded forms normalize before comparison.
|
|
21
|
+
*/
|
|
22
|
+
export declare function safeUrl(href: string): string | null;
|
|
23
|
+
/** Renders markdown to HTML with raw HTML escaped and URL schemes restricted. */
|
|
24
|
+
export declare function renderMarkdownSafe(markdown: string): Promise<string>;
|
|
25
|
+
//# sourceMappingURL=markdown-render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-render.d.ts","sourceRoot":"","sources":["../../src/core/markdown-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA6BH;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAenD;AA+BD,iFAAiF;AACjF,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE1E"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown → HTML for the dashboard's `/api/render`, and a trust boundary rather
|
|
3
|
+
* than a formatting detail: the output is assigned with `innerHTML`, so it runs
|
|
4
|
+
* in the same origin that answers `POST /api/task`. The CSRF defence on
|
|
5
|
+
* `handleTaskUpdate` only stops *cross-origin* callers, so a handler smuggled in
|
|
6
|
+
* through a markdown file would sit inside it.
|
|
7
|
+
*
|
|
8
|
+
* Raw HTML is escaped, not allowlisted: an allowlist has to stay ahead of every
|
|
9
|
+
* attribute and scheme that can execute, and these documents need no markup
|
|
10
|
+
* markdown cannot express.
|
|
11
|
+
*/
|
|
12
|
+
import { Marked } from 'marked';
|
|
13
|
+
/**
|
|
14
|
+
* Schemes a link or image may keep. Relative paths and fragments carry no
|
|
15
|
+
* scheme and are allowed by falling through the `URL` parse below.
|
|
16
|
+
*
|
|
17
|
+
* `data:` is excluded: `data:text/html` navigates to attacker markup in this
|
|
18
|
+
* origin, and no spec document needs an inline blob.
|
|
19
|
+
*/
|
|
20
|
+
const SAFE_URL_SCHEMES = new Set(['http:', 'https:', 'mailto:']);
|
|
21
|
+
function escapeHtml(text) {
|
|
22
|
+
return text
|
|
23
|
+
.replace(/&/g, '&')
|
|
24
|
+
.replace(/</g, '<')
|
|
25
|
+
.replace(/>/g, '>')
|
|
26
|
+
.replace(/"/g, '"')
|
|
27
|
+
.replace(/'/g, ''');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Base for resolving relative hrefs: a relative URL inherits this scheme, so
|
|
31
|
+
* `parsed.protocol` lands in the allowlist and one check covers both absolute
|
|
32
|
+
* and relative inputs. The host is never emitted.
|
|
33
|
+
*/
|
|
34
|
+
const RELATIVE_BASE = 'https://tospec.invalid/';
|
|
35
|
+
/**
|
|
36
|
+
* The URL when it is safe to emit as an attribute, otherwise null. Two rules,
|
|
37
|
+
* both against spellings a naive scheme regex waves through:
|
|
38
|
+
*
|
|
39
|
+
* 1. Tabs and newlines are stripped first. `java	script:alert(1)` matches no
|
|
40
|
+
* `^javascript:` regex, but the URL parser — and the browser — strips them
|
|
41
|
+
* and is left with `javascript:`.
|
|
42
|
+
* 2. The scheme comes from `URL`, not the raw text, so case variants and
|
|
43
|
+
* percent-encoded forms normalize before comparison.
|
|
44
|
+
*/
|
|
45
|
+
export function safeUrl(href) {
|
|
46
|
+
// The same normalization the URL parser applies: C0 controls and space
|
|
47
|
+
// trimmed from both ends, tab/LF/CR removed throughout.
|
|
48
|
+
const cleaned = href.replace(/[\x00- ]+$/, '').replace(/^[\x00- ]+/, '').replace(/[\t\n\r]/g, '');
|
|
49
|
+
if (cleaned === '')
|
|
50
|
+
return null;
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = new URL(cleaned, RELATIVE_BASE);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (!SAFE_URL_SCHEMES.has(parsed.protocol))
|
|
59
|
+
return null;
|
|
60
|
+
// The cleaned form, not the input: what was validated has to be what is
|
|
61
|
+
// emitted, or the browser re-reads the characters removed above.
|
|
62
|
+
return cleaned;
|
|
63
|
+
}
|
|
64
|
+
const renderer = new Marked({
|
|
65
|
+
renderer: {
|
|
66
|
+
// Both block-level and inline raw HTML arrive here.
|
|
67
|
+
html({ text }) {
|
|
68
|
+
return escapeHtml(text);
|
|
69
|
+
},
|
|
70
|
+
link(token) {
|
|
71
|
+
const body = this.parser.parseInline(token.tokens);
|
|
72
|
+
const href = safeUrl(token.href);
|
|
73
|
+
// A rejected scheme keeps the text and loses the link; dropping the node
|
|
74
|
+
// would hide that the author wrote something the renderer refused.
|
|
75
|
+
if (href === null)
|
|
76
|
+
return body;
|
|
77
|
+
const title = token.title ? ` title="${escapeHtml(token.title)}"` : '';
|
|
78
|
+
return `<a href="${escapeHtml(href)}"${title}>${body}</a>`;
|
|
79
|
+
},
|
|
80
|
+
image(token) {
|
|
81
|
+
const src = safeUrl(token.href);
|
|
82
|
+
const alt = escapeHtml(token.text);
|
|
83
|
+
if (src === null)
|
|
84
|
+
return alt;
|
|
85
|
+
const title = token.title ? ` title="${escapeHtml(token.title)}"` : '';
|
|
86
|
+
return `<img src="${escapeHtml(src)}" alt="${alt}"${title}>`;
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
/** Renders markdown to HTML with raw HTML escaped and URL schemes restricted. */
|
|
91
|
+
export async function renderMarkdownSafe(markdown) {
|
|
92
|
+
return renderer.parse(markdown);
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=markdown-render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-render.js","sourceRoot":"","sources":["../../src/core/markdown-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAEjE,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI;SACR,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,aAAa,GAAG,yBAAyB,CAAC;AAEhD;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,uEAAuE;IACvE,wDAAwD;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAClG,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACxD,wEAAwE;IACxE,iEAAiE;IACjE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC;IAC1B,QAAQ,EAAE;QACR,oDAAoD;QACpD,IAAI,CAAC,EAAE,IAAI,EAAoB;YAC7B,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,CAA+D,KAIlE;YACC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACjC,yEAAyE;YACzE,mEAAmE;YACnE,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAC;YAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,OAAO,YAAY,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC;QAC7D,CAAC;QACD,KAAK,CAAC,KAA4D;YAChE,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,GAAG,KAAK,IAAI;gBAAE,OAAO,GAAG,CAAC;YAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,OAAO,aAAa,UAAU,CAAC,GAAG,CAAC,UAAU,GAAG,IAAI,KAAK,GAAG,CAAC;QAC/D,CAAC;KACF;CACF,CAAC,CAAC;AAEH,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,QAAgB;IACvD,OAAO,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC"}
|
package/dist/core/migrate.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { type CommandDiagnostic } from '../commands/shared-output.js';
|
|
1
2
|
export interface MigrateOptions {
|
|
2
3
|
force?: boolean;
|
|
4
|
+
json?: boolean;
|
|
3
5
|
}
|
|
6
|
+
/** The `--json` null-shape; mirrors the success payload with `migrate` nulled. */
|
|
7
|
+
export declare const MIGRATE_FAILURE_PAYLOAD: Record<string, unknown>;
|
|
4
8
|
export interface MigrateSummary {
|
|
5
9
|
specs: {
|
|
6
10
|
copied: number;
|
|
@@ -13,24 +17,33 @@ export interface MigrateSummary {
|
|
|
13
17
|
archived: number;
|
|
14
18
|
metadataCreated: number;
|
|
15
19
|
};
|
|
20
|
+
/** How the source project context was handled. */
|
|
21
|
+
context: 'carried' | 'conflict' | 'empty' | 'absent';
|
|
16
22
|
/** REMOVED requirements dropped during main-spec normalization (`spec/name`). */
|
|
17
23
|
removedRequirements: string[];
|
|
18
|
-
/** RENAMED
|
|
24
|
+
/** Renames a RENAMED section asked for and normalization applied (`spec/from -> to`). */
|
|
25
|
+
renamedRequirements: string[];
|
|
26
|
+
/**
|
|
27
|
+
* RENAMED entries normalization could not act on, so the old name stands
|
|
28
|
+
* (`spec/name`). Reported for manual review rather than dropped.
|
|
29
|
+
*/
|
|
19
30
|
renamedKept: string[];
|
|
20
31
|
}
|
|
21
32
|
export declare class MigrateError extends Error {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
/**
|
|
34
|
+
* A full `CommandDiagnostic`: `asStatus` recognises one by `typeof
|
|
35
|
+
* diagnostic.code === 'string'`, so a bare `{ fix }` is dropped.
|
|
36
|
+
*/
|
|
37
|
+
readonly diagnostic: CommandDiagnostic;
|
|
38
|
+
constructor(message: string, options?: {
|
|
26
39
|
fix?: string;
|
|
27
|
-
|
|
40
|
+
code?: string;
|
|
41
|
+
});
|
|
28
42
|
}
|
|
29
43
|
/**
|
|
30
44
|
* Converts an OpenSpec archive dir name (`YYYY-MM-DD-<name>`) to the tospec
|
|
31
|
-
* stamp form (`YYYYMMDD_000000-<name>`). OpenSpec only recorded the date, so
|
|
32
|
-
*
|
|
33
|
-
* unchanged (they already parse as `<stamp>-<name>` no worse than before).
|
|
45
|
+
* stamp form (`YYYYMMDD_000000-<name>`). OpenSpec only recorded the date, so the
|
|
46
|
+
* time part is zero-filled. Names without the date prefix pass through.
|
|
34
47
|
*/
|
|
35
48
|
export declare function convertArchiveDirName(dirName: string): string;
|
|
36
49
|
/**
|
|
@@ -43,12 +56,17 @@ export declare function normalizeMainSpec(content: string, capabilityName: strin
|
|
|
43
56
|
fixed: 'delta' | 'headerless';
|
|
44
57
|
/** Names of requirements a REMOVED section dropped (recorded, not silent). */
|
|
45
58
|
removed: string[];
|
|
46
|
-
/**
|
|
59
|
+
/** Renames a RENAMED section asked for and this rebuild applied. */
|
|
60
|
+
renamed: Array<{
|
|
61
|
+
from: string;
|
|
62
|
+
to: string;
|
|
63
|
+
}>;
|
|
64
|
+
/** RENAMED entries nothing could be applied for; the old name stands. */
|
|
47
65
|
renamedKept: string[];
|
|
48
66
|
} | null;
|
|
49
67
|
export declare class MigrateCommand {
|
|
50
68
|
execute(sourceDir?: string, options?: MigrateOptions): Promise<void>;
|
|
51
|
-
/** Copies config.yaml with the schema value rewritten
|
|
69
|
+
/** Copies config.yaml with the schema value rewritten, carrying project.md context when needed. */
|
|
52
70
|
private migrateConfig;
|
|
53
71
|
/**
|
|
54
72
|
* Replaces `.openspec.yaml` with `.tospec.yaml` (schema rewritten to sdd,
|
|
@@ -59,10 +77,9 @@ export declare class MigrateCommand {
|
|
|
59
77
|
*/
|
|
60
78
|
private migrateMetadata;
|
|
61
79
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* replaces its counterpart. Reuses archive's EPERM/EXDEV-safe move helpers.
|
|
80
|
+
* Promotes the built staging tree into the target: one move when the target is
|
|
81
|
+
* absent, else entry-by-entry replacement into the content-free tospec/ that
|
|
82
|
+
* `tospec init` leaves. Uses archive's EPERM/EXDEV-safe helpers.
|
|
66
83
|
*/
|
|
67
84
|
private promoteStaging;
|
|
68
85
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/core/migrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/core/migrate.ts"],"names":[],"mappings":"AAQA,OAAO,EAAe,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAkCnF,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,kFAAkF;AAClF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAiC,CAAC;AAE9F,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,oBAAoB,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACvG,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,kDAAkD;IAClD,OAAO,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;IACrD,iFAAiF;IACjF,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,yFAAyF;IACzF,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B;;;OAGG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IAEvC,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EASrE;CACF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAO7D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,GACrB;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,GAAG,YAAY,CAAC;IAC9B,8EAA8E;IAC9E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oEAAoE;IACpE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7C,yEAAyE;IACzE,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,GAAG,IAAI,CAqFP;AAqED,qBAAa,cAAc;IACnB,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAiLzE;IAED,mGAAmG;YACrF,aAAa;IA6B3B;;;;;;OAMG;YACW,eAAe;IAe7B;;;;OAIG;YACW,cAAc;CAiB7B"}
|