@seanmars/tospec 0.19.0-beta.7 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -231
- package/README.md +69 -82
- package/assets/dashboard/app.js +14 -3
- package/assets/dashboard/style.css +7 -0
- package/assets/rules/tospec/decision.md +3 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +227 -86
- 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 +366 -107
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/dashboard.d.ts +57 -99
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +224 -282
- 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 +385 -79
- package/dist/commands/decision.js.map +1 -1
- package/dist/commands/metrics.d.ts +28 -51
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +62 -93
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/shared-output.d.ts +18 -21
- package/dist/commands/shared-output.d.ts.map +1 -1
- package/dist/commands/shared-output.js +49 -23
- 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 +29 -7
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/validate.d.ts +43 -45
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +262 -129
- 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 +81 -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 +378 -235
- package/dist/core/archive.js.map +1 -1
- package/dist/core/artifact-graph/graph.d.ts +25 -42
- package/dist/core/artifact-graph/graph.d.ts.map +1 -1
- package/dist/core/artifact-graph/graph.js +45 -63
- package/dist/core/artifact-graph/graph.js.map +1 -1
- package/dist/core/artifact-graph/index.d.ts +1 -1
- package/dist/core/artifact-graph/index.d.ts.map +1 -1
- package/dist/core/artifact-graph/index.js +1 -1
- package/dist/core/artifact-graph/index.js.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +97 -103
- package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.js +146 -119
- package/dist/core/artifact-graph/instruction-loader.js.map +1 -1
- package/dist/core/artifact-graph/outputs.d.ts +9 -23
- package/dist/core/artifact-graph/outputs.d.ts.map +1 -1
- package/dist/core/artifact-graph/outputs.js +45 -38
- 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 +14 -15
- package/dist/core/codex-residue.d.ts.map +1 -1
- package/dist/core/codex-residue.js +18 -22
- package/dist/core/codex-residue.js.map +1 -1
- 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 +39 -26
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +36 -22
- package/dist/core/config.js.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 +62 -70
- 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 +38 -67
- package/dist/core/global-config.js.map +1 -1
- package/dist/core/init.d.ts +25 -19
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +168 -181
- 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 +41 -83
- package/dist/core/local-server.d.ts.map +1 -1
- package/dist/core/local-server.js +53 -98
- 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 +29 -14
- package/dist/core/migrate.d.ts.map +1 -1
- package/dist/core/migrate.js +184 -122
- 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 +43 -15
- package/dist/core/parsers/requirement-blocks.d.ts.map +1 -1
- package/dist/core/parsers/requirement-blocks.js +142 -70
- 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.map +1 -1
- package/dist/core/parsers/spec-structure.js +10 -6
- package/dist/core/parsers/spec-structure.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 +9 -17
- 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 +6 -1
- package/dist/core/rules.d.ts.map +1 -1
- package/dist/core/rules.js +23 -2
- package/dist/core/rules.js.map +1 -1
- 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 +19 -8
- package/dist/core/shared/rules-generation.d.ts.map +1 -1
- package/dist/core/shared/rules-generation.js +117 -50
- package/dist/core/shared/rules-generation.js.map +1 -1
- package/dist/core/shared/skill-generation.d.ts +28 -43
- 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 +39 -61
- 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.js +6 -6
- package/dist/core/spec-presenter.js.map +1 -1
- package/dist/core/specs-apply.d.ts +22 -23
- package/dist/core/specs-apply.d.ts.map +1 -1
- package/dist/core/specs-apply.js +166 -193
- 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/validate.d.ts +13 -0
- package/dist/core/templates/fragments/validate.d.ts.map +1 -0
- package/dist/core/templates/fragments/validate.js +13 -0
- package/dist/core/templates/fragments/validate.js.map +1 -0
- 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 +11 -13
- 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 +19 -6
- 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 -2
- 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 +2 -2
- 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 +4 -3
- 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 +9 -2
- package/dist/core/templates/workflows/update.js.map +1 -1
- package/dist/core/update.d.ts +23 -23
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +139 -118
- 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 +4 -10
- 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 +43 -63
- package/dist/core/validation/validator.d.ts.map +1 -1
- package/dist/core/validation/validator.js +500 -267
- 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 +30 -54
- package/dist/utils/change-utils.d.ts.map +1 -1
- package/dist/utils/change-utils.js +133 -91
- 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 +9 -18
- package/dist/utils/link.d.ts.map +1 -1
- package/dist/utils/link.js +9 -18
- package/dist/utils/link.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 +7 -20
- package/dist/utils/spec-files.d.ts.map +1 -1
- package/dist/utils/spec-files.js +26 -51
- 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 +2 -3
- package/schemas/decision/templates/decision.md +3 -1
- package/schemas/decision/templates/index.md +2 -2
- package/schemas/issue/schema.yaml +8 -1
- package/schemas/issue/templates/spec.md +27 -2
- package/schemas/sdd/schema.yaml +20 -1
- package/schemas/sdd/templates/spec.md +27 -2
- /package/assets/rules/tospec/{single-sourc-of-truth.md → single-source-of-truth.md} +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cross-process advisory lock for read-compute-write sequences.
|
|
3
|
+
*
|
|
4
|
+
* `tospec archive` is one: it reads every target spec, merges in memory, then
|
|
5
|
+
* writes. Two overlapping archives both read the pre-merge text, so the second
|
|
6
|
+
* write erases the first one's requirement while both report success — measured
|
|
7
|
+
* at roughly one in five overlapping pairs.
|
|
8
|
+
*
|
|
9
|
+
* Advisory, not mandatory: it stops two of *our* merges from interleaving, not
|
|
10
|
+
* arbitrary writers.
|
|
11
|
+
*/
|
|
12
|
+
import { promises as fs } from 'node:fs';
|
|
13
|
+
import * as os from 'node:os';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
/**
|
|
16
|
+
* Two independent expiries. `timeoutMs` bounds how long *we* wait and is short,
|
|
17
|
+
* because waiting a minute on a few file reads means something is wrong, not
|
|
18
|
+
* busy. `staleMs` bounds how long a lock survives its holder and is long,
|
|
19
|
+
* because expiring it on an alive-but-slow holder hands the lock to a second
|
|
20
|
+
* writer — the exact failure being prevented.
|
|
21
|
+
*/
|
|
22
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
23
|
+
const DEFAULT_STALE_MS = 10 * 60_000;
|
|
24
|
+
const RETRY_INTERVAL_MS = 100;
|
|
25
|
+
/**
|
|
26
|
+
* How long an unreadable lock is left alone before it counts as abandoned.
|
|
27
|
+
*
|
|
28
|
+
* `open(…, 'wx')` creates the file before the record is written, so a held lock
|
|
29
|
+
* is briefly an *empty* file. Without this grace a waiter reads that emptiness
|
|
30
|
+
* as "no record", deletes the lock and takes it, and both processes hold it. The
|
|
31
|
+
* holder fills the file in microseconds, and a second is still short enough that
|
|
32
|
+
* a genuinely corrupt lock clears on the next run.
|
|
33
|
+
*/
|
|
34
|
+
const UNREADABLE_GRACE_MS = 1_000;
|
|
35
|
+
export class LockTimeoutError extends Error {
|
|
36
|
+
holder;
|
|
37
|
+
constructor(message, holder) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.holder = holder;
|
|
40
|
+
this.name = 'LockTimeoutError';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Whether a pid is a live process on this machine. */
|
|
44
|
+
function isAlive(pid) {
|
|
45
|
+
try {
|
|
46
|
+
// Signal 0 probes without delivering anything; EPERM means it exists and
|
|
47
|
+
// belongs to someone else, so still alive.
|
|
48
|
+
process.kill(pid, 0);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
return err.code === 'EPERM';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function readRecord(lockPath) {
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(await fs.readFile(lockPath, 'utf-8'));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Unreadable, or read in the gap between the exclusive create and the
|
|
61
|
+
// record write, so the caller waits UNREADABLE_GRACE_MS before judging it.
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* True when the lock on disk should be taken over: older than `staleMs`, or its
|
|
67
|
+
* holder is gone. Pids are only meaningful locally, so a foreign host's lock is
|
|
68
|
+
* judged purely by age. An unreadable record is never stale on sight — the
|
|
69
|
+
* caller decides that, after UNREADABLE_GRACE_MS.
|
|
70
|
+
*/
|
|
71
|
+
function isStale(record, staleMs) {
|
|
72
|
+
if (!record)
|
|
73
|
+
return false;
|
|
74
|
+
const age = Date.now() - Date.parse(record.startedAt);
|
|
75
|
+
if (Number.isFinite(age) && age > staleMs)
|
|
76
|
+
return true;
|
|
77
|
+
if (record.host !== os.hostname())
|
|
78
|
+
return false;
|
|
79
|
+
return !isAlive(record.pid);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Runs `fn` while holding an exclusive lock at `lockPath`. Released in a
|
|
83
|
+
* `finally`, so a throwing `fn` leaves nothing behind; a killed process is
|
|
84
|
+
* covered by the staleness rules above.
|
|
85
|
+
*/
|
|
86
|
+
export async function withFileLock(lockPath, fn, options = {}) {
|
|
87
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
88
|
+
const staleMs = options.staleMs ?? DEFAULT_STALE_MS;
|
|
89
|
+
const record = {
|
|
90
|
+
pid: process.pid,
|
|
91
|
+
host: os.hostname(),
|
|
92
|
+
startedAt: new Date().toISOString(),
|
|
93
|
+
operation: options.operation ?? 'operation',
|
|
94
|
+
};
|
|
95
|
+
await fs.mkdir(path.dirname(lockPath), { recursive: true });
|
|
96
|
+
const deadline = Date.now() + timeoutMs;
|
|
97
|
+
/** When the lock first read as unreadable, or null while it reads cleanly. */
|
|
98
|
+
let unreadableSince = null;
|
|
99
|
+
for (;;) {
|
|
100
|
+
try {
|
|
101
|
+
// 'wx' is the whole mechanism: create-or-EEXIST in one syscall, so two
|
|
102
|
+
// processes cannot both believe they created it.
|
|
103
|
+
const handle = await fs.open(lockPath, 'wx');
|
|
104
|
+
try {
|
|
105
|
+
await handle.writeFile(JSON.stringify(record), 'utf-8');
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
await handle.close();
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
if (err.code !== 'EEXIST')
|
|
114
|
+
throw err;
|
|
115
|
+
const holder = await readRecord(lockPath);
|
|
116
|
+
// Only a lock that stays unreadable past the grace period is abandoned
|
|
117
|
+
// rather than mid-creation.
|
|
118
|
+
if (holder === null)
|
|
119
|
+
unreadableSince ??= Date.now();
|
|
120
|
+
else
|
|
121
|
+
unreadableSince = null;
|
|
122
|
+
const graceExpired = unreadableSince !== null && Date.now() - unreadableSince >= UNREADABLE_GRACE_MS;
|
|
123
|
+
if (isStale(holder, staleMs) || graceExpired) {
|
|
124
|
+
// Unlink and loop rather than write over it: the create-exclusive retry
|
|
125
|
+
// decides the winner when two waiters both judge it stale.
|
|
126
|
+
await fs.rm(lockPath, { force: true });
|
|
127
|
+
unreadableSince = null;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
// Before the sleep, so a `timeoutMs` shorter than the grace period is
|
|
131
|
+
// still honoured.
|
|
132
|
+
if (Date.now() >= deadline) {
|
|
133
|
+
const held = holder
|
|
134
|
+
? `held by pid ${holder.pid} on ${holder.host} since ${holder.startedAt} (${holder.operation})`
|
|
135
|
+
: 'held by an unidentified process';
|
|
136
|
+
throw new LockTimeoutError(`Timed out after ${Math.round(timeoutMs / 1000)}s waiting for the lock at ${lockPath}, ${held}. ` +
|
|
137
|
+
'Another tospec process is probably still running; if none is, delete that file and retry.', holder);
|
|
138
|
+
}
|
|
139
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_INTERVAL_MS));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
return await fn();
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
await fs.rm(lockPath, { force: true });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=file-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../../src/utils/file-lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Most-recent mtime of any file under `dirPath
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Shared by `tospec list --sort recent` and the dashboard so both rank changes
|
|
6
|
-
* by the same clock; they used to walk separately and could disagree.
|
|
7
|
-
* ponytail: recursive walk; fine at repo scale (dozens of changes).
|
|
2
|
+
* Most-recent mtime of any file under `dirPath`, falling back to the directory's
|
|
3
|
+
* own mtime when it holds none. Shared by `tospec list --sort recent` and the
|
|
4
|
+
* dashboard so both rank changes by the same clock.
|
|
8
5
|
*/
|
|
9
6
|
export declare function getLastModified(dirPath: string): Promise<Date>;
|
|
10
7
|
/**
|
|
@@ -17,39 +14,22 @@ export declare function getLastModified(dirPath: string): Promise<Date>;
|
|
|
17
14
|
export declare function extractNameFromPath(filePath: string): string;
|
|
18
15
|
/**
|
|
19
16
|
* Whether a failed read of a *targeted path* means it is not there, as opposed
|
|
20
|
-
* to being there and unusable.
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
24
|
-
* `EACCES`, `EISDIR`, `EIO`, `EPERM` — describes a path that exists and could not
|
|
25
|
-
* be read, and callers that treat those as absence state a conclusion the read
|
|
26
|
-
* never reached.
|
|
27
|
-
*
|
|
28
|
-
* Use this when "there is nothing at this path yet" is a legitimate outcome for
|
|
29
|
-
* a specific file. For enumerating a directory, use `isAbsentDirectoryError` —
|
|
30
|
-
* the two differ on `ENOTDIR` and the difference is load-bearing.
|
|
17
|
+
* to being there and unusable. `ENOTDIR` counts because a parent component that
|
|
18
|
+
* is a file makes the path unreachable for the same reason;
|
|
19
|
+
* `EACCES`/`EISDIR`/`EIO` describe a path that exists. For enumerating a
|
|
20
|
+
* directory use `isAbsentDirectoryError`: the two differ on `ENOTDIR`.
|
|
31
21
|
*/
|
|
32
22
|
export declare function isMissingPathError(error: unknown): boolean;
|
|
33
23
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* means something that is not a directory occupies the path, and answering
|
|
38
|
-
* "empty" for that is how a broken layout comes to look like an absent one. For
|
|
39
|
-
* a targeted read the same code means the parent is unreachable, which is
|
|
40
|
-
* genuine absence — hence two predicates rather than one plus an exception.
|
|
24
|
+
* Narrower than `isMissingPathError`: `ENOTDIR` from a `readdir` means a
|
|
25
|
+
* non-directory occupies the path, and answering "empty" makes a broken layout
|
|
26
|
+
* look like an absent one.
|
|
41
27
|
*/
|
|
42
28
|
export declare function isAbsentDirectoryError(error: unknown): boolean;
|
|
43
29
|
export declare class FileSystemUtils {
|
|
44
|
-
/**
|
|
45
|
-
* Converts a path to use forward slashes (POSIX style).
|
|
46
|
-
* Essential for cross-platform compatibility with glob libraries like fast-glob.
|
|
47
|
-
*/
|
|
30
|
+
/** fast-glob and friends only accept forward slashes. */
|
|
48
31
|
static toPosixPath(p: string): string;
|
|
49
|
-
/**
|
|
50
|
-
* Returns a canonical absolute path when the target exists.
|
|
51
|
-
* Falls back to path.resolve() so callers can still produce a stable absolute path.
|
|
52
|
-
*/
|
|
32
|
+
/** Falls back to path.resolve() when the target does not exist. */
|
|
53
33
|
static canonicalizeExistingPath(targetPath: string): string;
|
|
54
34
|
private static isWindowsBasePath;
|
|
55
35
|
private static normalizeSegments;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../../src/utils/file-system.ts"],"names":[],"mappings":"AAKA
|
|
1
|
+
{"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../../src/utils/file-system.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBpE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY5D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE9D;AAED,qBAAa,eAAe;IAC1B,yDAAyD;IACzD,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEpC;IAED,mEAAmE;IACnE,MAAM,CAAC,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAW1D;IAED,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAIhC,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAMhC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAe/D;IAED,OAAa,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3D;IAED,OAAa,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU1D;IAED,OAAa,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU9D;IAED,OAAa,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIvE;IAED,OAAa,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEvD;IAED,OAAa,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCrE;CACF"}
|
|
@@ -2,19 +2,16 @@ import * as nodeFs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
const fs = nodeFs.promises;
|
|
4
4
|
/**
|
|
5
|
-
* Most-recent mtime of any file under `dirPath
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Shared by `tospec list --sort recent` and the dashboard so both rank changes
|
|
9
|
-
* by the same clock; they used to walk separately and could disagree.
|
|
10
|
-
* ponytail: recursive walk; fine at repo scale (dozens of changes).
|
|
5
|
+
* Most-recent mtime of any file under `dirPath`, falling back to the directory's
|
|
6
|
+
* own mtime when it holds none. Shared by `tospec list --sort recent` and the
|
|
7
|
+
* dashboard so both rank changes by the same clock.
|
|
11
8
|
*/
|
|
12
9
|
export async function getLastModified(dirPath) {
|
|
13
10
|
let latest = null;
|
|
14
11
|
async function walk(dir) {
|
|
15
12
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
16
13
|
// Entries are independent and `latest` is an order-insensitive max, so the
|
|
17
|
-
// stats fan out
|
|
14
|
+
// stats fan out rather than running one await at a time.
|
|
18
15
|
await Promise.all(entries.map(async (entry) => {
|
|
19
16
|
const full = path.join(dir, entry.name);
|
|
20
17
|
if (entry.isDirectory()) {
|
|
@@ -49,49 +46,32 @@ export function extractNameFromPath(filePath) {
|
|
|
49
46
|
}
|
|
50
47
|
/**
|
|
51
48
|
* Whether a failed read of a *targeted path* means it is not there, as opposed
|
|
52
|
-
* to being there and unusable.
|
|
53
|
-
*
|
|
54
|
-
* `
|
|
55
|
-
*
|
|
56
|
-
* `EACCES`, `EISDIR`, `EIO`, `EPERM` — describes a path that exists and could not
|
|
57
|
-
* be read, and callers that treat those as absence state a conclusion the read
|
|
58
|
-
* never reached.
|
|
59
|
-
*
|
|
60
|
-
* Use this when "there is nothing at this path yet" is a legitimate outcome for
|
|
61
|
-
* a specific file. For enumerating a directory, use `isAbsentDirectoryError` —
|
|
62
|
-
* the two differ on `ENOTDIR` and the difference is load-bearing.
|
|
49
|
+
* to being there and unusable. `ENOTDIR` counts because a parent component that
|
|
50
|
+
* is a file makes the path unreachable for the same reason;
|
|
51
|
+
* `EACCES`/`EISDIR`/`EIO` describe a path that exists. For enumerating a
|
|
52
|
+
* directory use `isAbsentDirectoryError`: the two differ on `ENOTDIR`.
|
|
63
53
|
*/
|
|
64
54
|
export function isMissingPathError(error) {
|
|
65
55
|
const code = error?.code;
|
|
66
56
|
return code === 'ENOENT' || code === 'ENOTDIR';
|
|
67
57
|
}
|
|
68
58
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* means something that is not a directory occupies the path, and answering
|
|
73
|
-
* "empty" for that is how a broken layout comes to look like an absent one. For
|
|
74
|
-
* a targeted read the same code means the parent is unreachable, which is
|
|
75
|
-
* genuine absence — hence two predicates rather than one plus an exception.
|
|
59
|
+
* Narrower than `isMissingPathError`: `ENOTDIR` from a `readdir` means a
|
|
60
|
+
* non-directory occupies the path, and answering "empty" makes a broken layout
|
|
61
|
+
* look like an absent one.
|
|
76
62
|
*/
|
|
77
63
|
export function isAbsentDirectoryError(error) {
|
|
78
64
|
return error?.code === 'ENOENT';
|
|
79
65
|
}
|
|
80
66
|
export class FileSystemUtils {
|
|
81
|
-
/**
|
|
82
|
-
* Converts a path to use forward slashes (POSIX style).
|
|
83
|
-
* Essential for cross-platform compatibility with glob libraries like fast-glob.
|
|
84
|
-
*/
|
|
67
|
+
/** fast-glob and friends only accept forward slashes. */
|
|
85
68
|
static toPosixPath(p) {
|
|
86
69
|
return p.replace(/\\/g, '/');
|
|
87
70
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Returns a canonical absolute path when the target exists.
|
|
90
|
-
* Falls back to path.resolve() so callers can still produce a stable absolute path.
|
|
91
|
-
*/
|
|
71
|
+
/** Falls back to path.resolve() when the target does not exist. */
|
|
92
72
|
static canonicalizeExistingPath(targetPath) {
|
|
93
73
|
try {
|
|
94
|
-
//
|
|
74
|
+
// The native resolver expands Windows short-path aliases.
|
|
95
75
|
return nodeFs.realpathSync.native(targetPath);
|
|
96
76
|
}
|
|
97
77
|
catch {
|
|
@@ -161,7 +141,6 @@ export class FileSystemUtils {
|
|
|
161
141
|
}
|
|
162
142
|
static async ensureWritePermissions(dirPath) {
|
|
163
143
|
try {
|
|
164
|
-
// If directory doesn't exist, check parent directory permissions
|
|
165
144
|
if (!await this.directoryExists(dirPath)) {
|
|
166
145
|
const parentDir = path.dirname(dirPath);
|
|
167
146
|
if (!await this.directoryExists(parentDir)) {
|
|
@@ -171,8 +150,7 @@ export class FileSystemUtils {
|
|
|
171
150
|
}
|
|
172
151
|
const testFile = path.join(dirPath, '.tospec-test-' + Date.now() + '-' + Math.random().toString(36).slice(2));
|
|
173
152
|
await fs.writeFile(testFile, '');
|
|
174
|
-
// On Windows
|
|
175
|
-
// Retry unlink with a small delay if it fails.
|
|
153
|
+
// On Windows the file may be briefly locked by antivirus or indexing.
|
|
176
154
|
const maxRetries = 3;
|
|
177
155
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
178
156
|
try {
|
|
@@ -181,12 +159,10 @@ export class FileSystemUtils {
|
|
|
181
159
|
}
|
|
182
160
|
catch (unlinkError) {
|
|
183
161
|
if (attempt === maxRetries - 1) {
|
|
184
|
-
//
|
|
185
|
-
// Just log and continue - the temp file will be cleaned up eventually
|
|
162
|
+
// The write succeeded, so permissions are fine; only cleanup failed.
|
|
186
163
|
console.debug(`Could not clean up test file ${testFile}: ${unlinkError.message}`);
|
|
187
164
|
}
|
|
188
165
|
else {
|
|
189
|
-
// Wait briefly before retrying (Windows file lock release)
|
|
190
166
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
191
167
|
}
|
|
192
168
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-system.js","sourceRoot":"","sources":["../../src/utils/file-system.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,IAAI,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;AAE3B
|
|
1
|
+
{"version":3,"file":"file-system.js","sourceRoot":"","sources":["../../src/utils/file-system.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,IAAI,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;AAE3B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe;IACnD,IAAI,MAAM,GAAkB,IAAI,CAAC;IAEjC,KAAK,UAAU,IAAI,CAAC,GAAW;QAC7B,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,2EAA2E;QAC3E,yDAAyD;QACzD,MAAM,OAAO,CAAC,GAAG,CACf,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;gBACjB,OAAO;YACT,CAAC;YACD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,GAAG,MAAM;gBAAE,MAAM,GAAG,OAAO,CAAC;QAC5D,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,MAAM,KAAK,GAAG,eAAe,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE/D,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7E,OAAO,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC3C,OAAO,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC/D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,MAAM,IAAI,GAAI,KAAsC,EAAE,IAAI,CAAC;IAC3D,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,OAAQ,KAAsC,EAAE,IAAI,KAAK,QAAQ,CAAC;AACpE,CAAC;AAED,MAAM,OAAO,eAAe;IAC1B,yDAAyD;IACzD,MAAM,CAAC,WAAW,CAAC,CAAS;QAC1B,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,mEAAmE;IACnE,MAAM,CAAC,wBAAwB,CAAC,UAAkB;QAChD,IAAI,CAAC;YACH,0DAA0D;YAC1D,OAAO,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC;gBACH,OAAO,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAEO,MAAM,CAAC,iBAAiB,CAAC,QAAgB;QAC/C,OAAO,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACvE,CAAC;IAEO,MAAM,CAAC,iBAAiB,CAAC,QAAkB;QACjD,OAAO,QAAQ;aACZ,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;aAC9C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,QAAgB,EAAE,GAAG,QAAkB;QACrD,MAAM,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAE5D,IAAI,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrC,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC1D,OAAO,kBAAkB,CAAC,MAAM;gBAC9B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,kBAAkB,CAAC;gBAC5D,CAAC,CAAC,kBAAkB,CAAC;QACzB,CAAC;QAED,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAEnD,OAAO,kBAAkB,CAAC,MAAM;YAC9B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,kBAAkB,CAAC;YACvD,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,OAAe;QAC1C,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,QAAgB;QACtC,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC5B,OAAO,CAAC,KAAK,CAAC,qCAAqC,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACnF,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,OAAe;QAC1C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACrC,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC5B,OAAO,CAAC,KAAK,CAAC,0CAA0C,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACvF,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,QAAgB,EAAE,OAAe;QACtD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAgB;QACpC,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,OAAe;QACjD,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBACxC,IAAI,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC3C,MAAM,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;gBACxC,CAAC;gBACD,OAAO,MAAM,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;YACtD,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9G,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAEjC,sEAAsE;YACtE,MAAM,UAAU,GAAG,CAAC,CAAC;YACrB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;gBACtD,IAAI,CAAC;oBACH,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;oBAC1B,MAAM;gBACR,CAAC;gBAAC,OAAO,WAAgB,EAAE,CAAC;oBAC1B,IAAI,OAAO,KAAK,UAAU,GAAG,CAAC,EAAE,CAAC;wBAC/B,qEAAqE;wBACrE,OAAO,CAAC,KAAK,CAAC,gCAAgC,QAAQ,KAAK,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;oBACpF,CAAC;yBAAM,CAAC;wBACN,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;oBAC1D,CAAC;gBACH,CAAC;YACH,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,wCAAwC,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACnF,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The single reader for ticket/change frontmatter, so the dashboard,
|
|
3
|
+
* `tospec show` and archive agree on which ticket belongs to which change.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Malformed yaml *throws* — the dashboard reports it per ticket as a
|
|
9
|
-
* `parseError` rather than silently showing a ticket with no metadata. Callers
|
|
10
|
-
* that would rather degrade than fail should use `frontmatterString`.
|
|
5
|
+
* Malformed yaml *throws*, so the dashboard can report it per ticket as a
|
|
6
|
+
* `parseError` instead of showing a ticket with no metadata. Callers that would
|
|
7
|
+
* rather degrade than fail should use `frontmatterString`.
|
|
11
8
|
*/
|
|
12
9
|
export declare function splitFrontmatter(content: string): {
|
|
13
10
|
data: Record<string, unknown>;
|
|
14
11
|
body: string;
|
|
15
12
|
};
|
|
16
13
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* lookup paths, where a broken header should fall back rather than fail.
|
|
14
|
+
* Null when the field is absent, non-string, or the frontmatter does not parse —
|
|
15
|
+
* for display and lookup paths, where a broken header should degrade, not fail.
|
|
20
16
|
*/
|
|
21
17
|
export declare function frontmatterString(content: string, field: string): string | null;
|
|
22
18
|
//# sourceMappingURL=frontmatter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/utils/frontmatter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/utils/frontmatter.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IACjD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;CACd,CAaA;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO/E"}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { parse as parseYaml } from 'yaml';
|
|
2
|
+
import { stripBom } from '../core/parsers/requirement-text.js';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* The single reader for ticket/change frontmatter, so the dashboard,
|
|
5
|
+
* `tospec show` and archive agree on which ticket belongs to which change.
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Malformed yaml *throws* — the dashboard reports it per ticket as a
|
|
10
|
-
* `parseError` rather than silently showing a ticket with no metadata. Callers
|
|
11
|
-
* that would rather degrade than fail should use `frontmatterString`.
|
|
7
|
+
* Malformed yaml *throws*, so the dashboard can report it per ticket as a
|
|
8
|
+
* `parseError` instead of showing a ticket with no metadata. Callers that would
|
|
9
|
+
* rather degrade than fail should use `frontmatterString`.
|
|
12
10
|
*/
|
|
13
11
|
export function splitFrontmatter(content) {
|
|
12
|
+
// A leading BOM fails the `---` test below and hands back empty `data`, which
|
|
13
|
+
// reads as "this ticket has no frontmatter" with nothing reporting a problem.
|
|
14
|
+
content = stripBom(content);
|
|
14
15
|
if (!content.startsWith('---'))
|
|
15
16
|
return { data: {}, body: content };
|
|
16
17
|
const end = content.indexOf('\n---', 3);
|
|
@@ -23,9 +24,8 @@ export function splitFrontmatter(content) {
|
|
|
23
24
|
return { data, body };
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* lookup paths, where a broken header should fall back rather than fail.
|
|
27
|
+
* Null when the field is absent, non-string, or the frontmatter does not parse —
|
|
28
|
+
* for display and lookup paths, where a broken header should degrade, not fail.
|
|
29
29
|
*/
|
|
30
30
|
export function frontmatterString(content, field) {
|
|
31
31
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/utils/frontmatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/utils/frontmatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAI9C,8EAA8E;IAC9E,8EAA8E;IAC9E,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC5B,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACnE,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACxC,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAEnD,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAE,MAAkC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7F,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IACpE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,KAAa;IAC9D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
export type InteractiveOptions = {
|
|
2
|
-
/**
|
|
3
|
-
* Explicit "disable prompts" flag passed by internal callers.
|
|
4
|
-
*/
|
|
2
|
+
/** Explicit "disable prompts" flag passed by internal callers. */
|
|
5
3
|
noInteractive?: boolean;
|
|
6
|
-
/**
|
|
7
|
-
* Commander-style negated option: `--no-interactive` sets this to false.
|
|
8
|
-
*/
|
|
4
|
+
/** Commander-style negated option: `--no-interactive` sets this to false. */
|
|
9
5
|
interactive?: boolean;
|
|
10
6
|
};
|
|
11
7
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* Use this helper instead of manually checking options.noInteractive to avoid bugs.
|
|
8
|
+
* The one reader of both spellings. Checking `options.noInteractive` by hand misses
|
|
9
|
+
* commander's `interactive: false`.
|
|
15
10
|
*/
|
|
16
11
|
export declare function resolveNoInteractive(value?: boolean | InteractiveOptions): boolean;
|
|
17
12
|
export declare function isInteractive(value?: boolean | InteractiveOptions): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/utils/interactive.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B
|
|
1
|
+
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/utils/interactive.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,kEAAkE;IAClE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,kBAAkB,GAAG,OAAO,CAGlF;AAED,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,kBAAkB,GAAG,OAAO,CAK3E"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Use this helper instead of manually checking options.noInteractive to avoid bugs.
|
|
2
|
+
* The one reader of both spellings. Checking `options.noInteractive` by hand misses
|
|
3
|
+
* commander's `interactive: false`.
|
|
5
4
|
*/
|
|
6
5
|
export function resolveNoInteractive(value) {
|
|
7
6
|
if (typeof value === 'boolean')
|
|
@@ -13,7 +12,6 @@ export function isInteractive(value) {
|
|
|
13
12
|
return false;
|
|
14
13
|
if (process.env.OPEN_SPEC_INTERACTIVE === '0')
|
|
15
14
|
return false;
|
|
16
|
-
// Respect the standard CI environment variable (set by GitHub Actions, GitLab CI, Travis, etc.)
|
|
17
15
|
if ('CI' in process.env)
|
|
18
16
|
return false;
|
|
19
17
|
return !!process.stdin.isTTY;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.js","sourceRoot":"","sources":["../../src/utils/interactive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"interactive.js","sourceRoot":"","sources":["../../src/utils/interactive.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAoC;IACvE,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,KAAK,EAAE,aAAa,KAAK,IAAI,IAAI,KAAK,EAAE,WAAW,KAAK,KAAK,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAoC;IAChE,IAAI,oBAAoB,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9C,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC5D,IAAI,IAAI,IAAI,OAAO,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACtC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;AAC/B,CAAC"}
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Returns the absolute path, or undefined if none matches.
|
|
2
|
+
* Every ticket in the active `tospec/tickets/` index whose frontmatter `change:`
|
|
3
|
+
* names this change, oldest first (the filename is the `yyyyMMdd_HHmmss` stamp).
|
|
4
|
+
* Non-recursive, so `tickets/archive/` is excluded.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Plural because the index can hold more than one: `rm -rf` on a change leaves
|
|
7
|
+
* its ticket behind, and recreating the name adds a second. Callers that can
|
|
8
|
+
* report the ambiguity should; `findTicketPath` picks for the rest.
|
|
9
|
+
*/
|
|
10
|
+
export declare function findTicketPaths(root: string, changeName: string): Promise<string[]>;
|
|
11
|
+
/**
|
|
12
|
+
* The newest wins: a second ticket exists because the change was recreated, so
|
|
13
|
+
* the later stamp describes the change on disk. Taking the first `readdir` entry
|
|
14
|
+
* files the run under the abandoned attempt's ticket.
|
|
9
15
|
*/
|
|
10
16
|
export declare function findTicketPath(root: string, changeName: string): Promise<string | undefined>;
|
|
11
17
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* and status each filtered slightly differently, so a stray `.tmp/` showed up
|
|
15
|
-
* in some listings and not others.
|
|
18
|
+
* The one rule for "which changes exist". Filtered separately per command, a
|
|
19
|
+
* stray `.tmp/` shows up in some listings and not others.
|
|
16
20
|
*/
|
|
17
21
|
export declare function listActiveChangeDirs(changesDir: string): Promise<string[]>;
|
|
18
22
|
export declare function getActiveChangeIds(root?: string): Promise<string[]>;
|
|
23
|
+
/** A capability directory counts as a spec only once it holds a `spec.md`. */
|
|
19
24
|
export declare function getSpecIds(root?: string): Promise<string[]>;
|
|
20
25
|
export declare function getArchivedChangeIds(root?: string): Promise<string[]>;
|
|
21
26
|
//# sourceMappingURL=item-discovery.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item-discovery.d.ts","sourceRoot":"","sources":["../../src/utils/item-discovery.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"item-discovery.d.ts","sourceRoot":"","sources":["../../src/utils/item-discovery.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBzF;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAGlG;AAeD;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGhF;AAED,wBAAsB,kBAAkB,CAAC,IAAI,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAExF;AAED,8EAA8E;AAC9E,wBAAsB,UAAU,CAAC,IAAI,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAchF;AAED,wBAAsB,oBAAoB,CAAC,IAAI,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE1F"}
|