@seanmars/tospec 0.19.0-beta.8 → 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 -310
- package/README.md +69 -82
- package/assets/dashboard/app.js +11 -0
- package/assets/dashboard/style.css +7 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +89 -106
- 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 +293 -145
- 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 +190 -313
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/decision.d.ts +38 -27
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +296 -128
- 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 +12 -27
- package/dist/commands/shared-output.d.ts.map +1 -1
- package/dist/commands/shared-output.js +22 -45
- package/dist/commands/shared-output.js.map +1 -1
- package/dist/commands/show.d.ts +4 -7
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +23 -11
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/validate.d.ts +34 -58
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +225 -141
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/workflow/index.d.ts +1 -5
- package/dist/commands/workflow/index.d.ts.map +1 -1
- package/dist/commands/workflow/index.js +1 -5
- package/dist/commands/workflow/index.js.map +1 -1
- package/dist/commands/workflow/instructions.d.ts +14 -24
- package/dist/commands/workflow/instructions.d.ts.map +1 -1
- package/dist/commands/workflow/instructions.js +219 -128
- package/dist/commands/workflow/instructions.js.map +1 -1
- package/dist/commands/workflow/new-change.d.ts +2 -5
- package/dist/commands/workflow/new-change.d.ts.map +1 -1
- package/dist/commands/workflow/new-change.js +69 -32
- package/dist/commands/workflow/new-change.js.map +1 -1
- package/dist/commands/workflow/schemas.d.ts +1 -5
- package/dist/commands/workflow/schemas.d.ts.map +1 -1
- package/dist/commands/workflow/schemas.js +6 -17
- package/dist/commands/workflow/schemas.js.map +1 -1
- package/dist/commands/workflow/shared.d.ts +37 -42
- package/dist/commands/workflow/shared.d.ts.map +1 -1
- package/dist/commands/workflow/shared.js +23 -54
- package/dist/commands/workflow/shared.js.map +1 -1
- package/dist/commands/workflow/status.d.ts +7 -17
- package/dist/commands/workflow/status.d.ts.map +1 -1
- package/dist/commands/workflow/status.js +57 -72
- package/dist/commands/workflow/status.js.map +1 -1
- package/dist/commands/workflow/templates.d.ts +8 -8
- package/dist/commands/workflow/templates.d.ts.map +1 -1
- package/dist/commands/workflow/templates.js +32 -46
- package/dist/commands/workflow/templates.js.map +1 -1
- package/dist/core/archive.d.ts +22 -31
- package/dist/core/archive.d.ts.map +1 -1
- package/dist/core/archive.js +296 -283
- 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 +54 -120
- package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.js +129 -111
- 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 +36 -81
- package/dist/core/artifact-graph/resolver.d.ts.map +1 -1
- package/dist/core/artifact-graph/resolver.js +60 -101
- 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 +6 -14
- package/dist/core/artifact-graph/stub-detection.d.ts.map +1 -1
- package/dist/core/artifact-graph/stub-detection.js +13 -16
- package/dist/core/artifact-graph/stub-detection.js.map +1 -1
- 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 +16 -27
- package/dist/core/change-presenter.d.ts.map +1 -1
- package/dist/core/change-presenter.js +53 -53
- package/dist/core/change-presenter.js.map +1 -1
- package/dist/core/change-status-policy.d.ts +4 -8
- package/dist/core/change-status-policy.d.ts.map +1 -1
- package/dist/core/change-status-policy.js +9 -17
- 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 +7 -41
- package/dist/core/config-schema.d.ts.map +1 -1
- package/dist/core/config-schema.js +35 -74
- package/dist/core/config-schema.js.map +1 -1
- package/dist/core/config.d.ts +25 -49
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +22 -45
- 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 +35 -22
- package/dist/core/dashboard-data.d.ts.map +1 -1
- package/dist/core/dashboard-data.js +57 -72
- 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 +12 -28
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +90 -168
- package/dist/core/init.js.map +1 -1
- package/dist/core/list.d.ts.map +1 -1
- package/dist/core/list.js +80 -38
- 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 +15 -23
- package/dist/core/markdown-render.d.ts.map +1 -1
- package/dist/core/markdown-render.js +25 -34
- package/dist/core/markdown-render.js.map +1 -1
- package/dist/core/migrate.d.ts +19 -16
- package/dist/core/migrate.d.ts.map +1 -1
- package/dist/core/migrate.js +151 -135
- 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 +23 -30
- 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 +8 -14
- package/dist/core/root-selection.d.ts.map +1 -1
- package/dist/core/root-selection.js +3 -6
- package/dist/core/root-selection.js.map +1 -1
- package/dist/core/rules.d.ts.map +1 -1
- package/dist/core/rules.js +2 -3
- 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.map +1 -1
- package/dist/core/schemas/base.schema.js +6 -12
- package/dist/core/schemas/base.schema.js.map +1 -1
- package/dist/core/schemas/change.schema.d.ts +8 -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/shared/index.d.ts +2 -7
- package/dist/core/shared/index.d.ts.map +1 -1
- package/dist/core/shared/index.js +2 -7
- package/dist/core/shared/index.js.map +1 -1
- package/dist/core/shared/rules-generation.d.ts +5 -15
- package/dist/core/shared/rules-generation.d.ts.map +1 -1
- package/dist/core/shared/rules-generation.js +33 -37
- 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 +35 -76
- package/dist/core/shared/tool-detection.d.ts.map +1 -1
- package/dist/core/shared/tool-detection.js +73 -93
- 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 +5 -10
- package/dist/core/spec-presenter.js.map +1 -1
- package/dist/core/specs-apply.d.ts +16 -31
- package/dist/core/specs-apply.d.ts.map +1 -1
- package/dist/core/specs-apply.js +146 -195
- 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 +16 -5
- package/dist/core/templates/workflows/archive.js.map +1 -1
- package/dist/core/templates/workflows/decision.js +3 -3
- 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 +10 -36
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +59 -126
- 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 -25
- 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 +30 -12
- 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 +42 -68
- package/dist/core/validation/validator.d.ts.map +1 -1
- package/dist/core/validation/validator.js +468 -285
- 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 +24 -76
- package/dist/utils/change-utils.d.ts.map +1 -1
- package/dist/utils/change-utils.js +95 -142
- 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 +10 -20
- package/dist/utils/item-discovery.d.ts.map +1 -1
- package/dist/utils/item-discovery.js +31 -55
- 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/issue/schema.yaml +8 -1
- package/schemas/issue/templates/spec.md +20 -3
- package/schemas/sdd/schema.yaml +20 -1
- package/schemas/sdd/templates/spec.md +20 -3
|
@@ -1,73 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Generates enriched instructions for creating artifacts or applying tasks.
|
|
5
|
-
* Includes both artifact instructions and apply instructions.
|
|
2
|
+
* `tospec instructions` — enriched briefs for creating an artifact, or for the
|
|
3
|
+
* apply phase.
|
|
6
4
|
*/
|
|
7
5
|
import path from 'path';
|
|
8
6
|
import * as fs from 'fs';
|
|
9
7
|
import { loadChangeContext, generateInstructions, resolveSchema, resolveArtifactOutputs, } from '../../core/artifact-graph/index.js';
|
|
10
8
|
import { getChangeDir, resolveCurrentPlanningHomeSync, } from '../../core/planning-home.js';
|
|
11
9
|
import { resolveRootForCommand, toPlanningHome, toRootOutput, } from '../../core/root-selection.js';
|
|
12
|
-
import {
|
|
13
|
-
import { parseTasksFromContent } from '../../utils/task-progress.js';
|
|
10
|
+
import { APPLY_RULES_KEY, readProjectConfigWithDiagnostics } from '../../core/project-config.js';
|
|
11
|
+
import { parseTasksFromContent, resolveTaskFiles } from '../../utils/task-progress.js';
|
|
14
12
|
import { validateChangeExists, validateSchemaExists, assertChangeSchema, } from './shared.js';
|
|
15
13
|
import { emitSuccess } from '../shared-output.js';
|
|
16
|
-
// -----------------------------------------------------------------------------
|
|
17
|
-
// Types
|
|
18
|
-
// -----------------------------------------------------------------------------
|
|
19
14
|
/**
|
|
20
|
-
* The `--json` null-shape.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* the rest sit at the top level, and the skill templates read them from there —
|
|
24
|
-
* so there is no wrapper key to null. The shape therefore nulls `instruction`,
|
|
25
|
-
* which both success shapes (artifact and apply) actually carry; the previous
|
|
26
|
-
* `instructions: null` named a key that appeared in neither, so the rule
|
|
27
|
-
* "a failure payload mirrors its success payload with the data keys nulled"
|
|
28
|
-
* could not be checked against anything.
|
|
15
|
+
* The `--json` null-shape. This command's success payload is flat, so there is
|
|
16
|
+
* no wrapper key to null; `instruction` is the one key both success shapes
|
|
17
|
+
* (artifact and apply) carry.
|
|
29
18
|
*
|
|
30
|
-
* Exported so the commander-level shape table and this command's
|
|
31
|
-
*
|
|
19
|
+
* Exported so the commander-level shape table and this command's catch block
|
|
20
|
+
* cannot drift apart.
|
|
32
21
|
*/
|
|
33
22
|
export const INSTRUCTIONS_FAILURE_PAYLOAD = {
|
|
34
23
|
instruction: null,
|
|
35
24
|
root: null,
|
|
36
25
|
};
|
|
37
26
|
/**
|
|
38
|
-
* The one argument this command accepts that is not a schema artifact
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* Exported so the CLI's dispatch and the "valid arguments" list read the same
|
|
42
|
-
* value — they did not, and the list left `apply` out (round 4 report 13).
|
|
27
|
+
* The one argument this command accepts that is not a schema artifact. Exported
|
|
28
|
+
* so the CLI's dispatch and the "valid arguments" list read one value.
|
|
43
29
|
*/
|
|
44
30
|
export const APPLY_ARGUMENT = 'apply';
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
const root = await resolveRootForCommand(options, {
|
|
31
|
+
async function resolveInstructionsTarget(options) {
|
|
32
|
+
// No implicit root, as `status`: "Change 'x' not found. No changes exist."
|
|
33
|
+
// about a directory that is no project sends the agent to `new change`
|
|
34
|
+
// instead of `init`.
|
|
35
|
+
const root = await resolveRootForCommand(options, {
|
|
36
|
+
json: options.json,
|
|
37
|
+
allowImplicitRoot: false,
|
|
38
|
+
failurePayload: INSTRUCTIONS_FAILURE_PAYLOAD,
|
|
39
|
+
});
|
|
50
40
|
if (!root) {
|
|
51
|
-
return;
|
|
41
|
+
return null;
|
|
52
42
|
}
|
|
53
|
-
const planningHome = toPlanningHome(root);
|
|
54
43
|
const projectRoot = root.path;
|
|
55
|
-
// Validate the schema before resolving the change (round 4 report 15): after
|
|
56
|
-
// it, the guard never fired in an empty project, so the caller was told to
|
|
57
|
-
// create a change first and only then learned the schema was never usable.
|
|
58
44
|
if (options.schema) {
|
|
59
45
|
validateSchemaExists(options.schema, projectRoot);
|
|
60
46
|
assertChangeSchema(options.schema);
|
|
61
47
|
}
|
|
62
|
-
const changeName = await validateChangeExists(options.change, projectRoot, root.changesDir, {
|
|
63
|
-
|
|
48
|
+
const changeName = await validateChangeExists(options.change, projectRoot, root.changesDir, {
|
|
49
|
+
newChangeHint: 'tospec new change <name>',
|
|
50
|
+
});
|
|
51
|
+
return { root, planningHome: toPlanningHome(root), projectRoot, changeName };
|
|
52
|
+
}
|
|
53
|
+
export async function instructionsCommand(artifactId, options) {
|
|
54
|
+
const target = await resolveInstructionsTarget(options);
|
|
55
|
+
if (!target) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const { root, planningHome, projectRoot, changeName } = target;
|
|
59
|
+
// The schema comes from the change's metadata when not given.
|
|
64
60
|
const context = loadChangeContext(projectRoot, changeName, options.schema, {
|
|
65
61
|
changeDir: getChangeDir(planningHome, changeName),
|
|
66
62
|
planningHome,
|
|
67
63
|
});
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
// artifact, and the one a reader who mistyped is most likely to have wanted.
|
|
64
|
+
// Includes `apply`: the schema's artifacts alone omit the one valid argument
|
|
65
|
+
// that is not an artifact.
|
|
71
66
|
const validArgumentList = () => [...context.graph.getAllArtifacts().map((a) => a.id), APPLY_ARGUMENT].join('\n ');
|
|
72
67
|
if (!artifactId) {
|
|
73
68
|
throw new Error(`Missing required argument <artifact>. Valid arguments:\n ${validArgumentList()}`);
|
|
@@ -76,24 +71,91 @@ export async function instructionsCommand(artifactId, options) {
|
|
|
76
71
|
if (!artifact) {
|
|
77
72
|
throw new Error(`Artifact '${artifactId}' not found in schema '${context.schemaName}'. Valid arguments:\n ${validArgumentList()}`);
|
|
78
73
|
}
|
|
79
|
-
//
|
|
80
|
-
|
|
74
|
+
// Never throws: a missing or unparseable config comes back null with the
|
|
75
|
+
// reason in `diagnostics`.
|
|
76
|
+
const { config: projectConfig, diagnostics: configDiagnostics } = readProjectConfigWithDiagnostics(root.path);
|
|
81
77
|
const instructions = generateInstructions(context, artifactId, projectRoot, {
|
|
82
78
|
projectConfig,
|
|
79
|
+
configDiagnostics,
|
|
83
80
|
});
|
|
84
81
|
const isBlocked = instructions.dependencies.some((d) => !d.done);
|
|
82
|
+
// `status` says "skipped" for this artifact, so a brief that reads as a
|
|
83
|
+
// plain "write it" sends an agent into the contradiction `validate` rejects
|
|
84
|
+
// (marker set, spec files present). Said first, in both output modes.
|
|
85
|
+
const isSkipped = context.skippedArtifacts?.has(artifactId) ?? false;
|
|
85
86
|
if (options.json) {
|
|
86
|
-
|
|
87
|
+
// `configWarnings` becomes `status[]`, the envelope field every command
|
|
88
|
+
// reports non-fatal trouble in.
|
|
89
|
+
const { configWarnings, ...payload } = instructions;
|
|
90
|
+
// First, ahead of any config warning: it is the one entry telling an agent
|
|
91
|
+
// not to write the artifact yet. The alternative is inferring it from
|
|
92
|
+
// `dependencies[].done`.
|
|
93
|
+
const status = [
|
|
94
|
+
...skippedDiagnostics(instructions, changeName, isSkipped),
|
|
95
|
+
...blockedDiagnostics(instructions, changeName),
|
|
96
|
+
...(configWarnings ?? []).map(({ code, message }) => ({
|
|
97
|
+
severity: 'warning',
|
|
98
|
+
code,
|
|
99
|
+
message,
|
|
100
|
+
})),
|
|
101
|
+
];
|
|
102
|
+
emitSuccess({
|
|
103
|
+
...payload,
|
|
104
|
+
...(status.length ? { status } : {}),
|
|
105
|
+
}, toRootOutput(root));
|
|
87
106
|
return;
|
|
88
107
|
}
|
|
89
|
-
printInstructionsText(instructions, isBlocked);
|
|
108
|
+
printInstructionsText(instructions, isBlocked, { skipped: isSkipped });
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The "declared away" warning, as a `status[]` entry — the JSON half of the
|
|
112
|
+
* `<warning>` block the human output prints. Empty unless the change's
|
|
113
|
+
* metadata skips this artifact.
|
|
114
|
+
*/
|
|
115
|
+
function skippedDiagnostics(instructions, changeName, skipped) {
|
|
116
|
+
if (!skipped)
|
|
117
|
+
return [];
|
|
118
|
+
return [
|
|
119
|
+
{
|
|
120
|
+
severity: 'warning',
|
|
121
|
+
code: 'artifact_skipped',
|
|
122
|
+
message: skippedArtifactMessage(instructions.artifactId, changeName),
|
|
123
|
+
fix: `Remove skip_specs from the change's .tospec.yaml before writing ${instructions.artifactId}.`,
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
function skippedArtifactMessage(artifactId, changeName) {
|
|
128
|
+
return `Change '${changeName}' declared '${artifactId}' away (skip_specs: true in .tospec.yaml). Writing it now leaves the marker contradicting the files, which validation rejects.`;
|
|
90
129
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
130
|
+
/**
|
|
131
|
+
* The unmet-dependency warning, as a `status[]` entry — the JSON half of the
|
|
132
|
+
* `<warning>` block the human output prints. Empty when nothing is missing, so
|
|
133
|
+
* the caller can concatenate it unconditionally.
|
|
134
|
+
*/
|
|
135
|
+
function blockedDiagnostics(instructions, changeName) {
|
|
136
|
+
const missing = instructions.dependencies.filter((d) => !d.done).map((d) => d.id);
|
|
137
|
+
if (missing.length === 0)
|
|
138
|
+
return [];
|
|
139
|
+
return [
|
|
140
|
+
{
|
|
141
|
+
severity: 'warning',
|
|
142
|
+
code: 'artifact_blocked',
|
|
143
|
+
message: `Artifact '${instructions.artifactId}' has unmet dependencies: ${missing.join(', ')}. Complete them first or proceed with caution.`,
|
|
144
|
+
fix: `Run: tospec instructions ${missing[0]} --change ${changeName}`,
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
export function printInstructionsText(instructions, isBlocked, flags = {}) {
|
|
149
|
+
const { artifactId, changeName, schemaName, changeDir, resolvedOutputPath, description, instruction, context, rules, template, validation, dependencies, unlocks, changeMetadata, } = instructions;
|
|
94
150
|
console.log(`<artifact id="${artifactId}" change="${changeName}" schema="${schemaName}">`);
|
|
95
151
|
console.log();
|
|
96
|
-
|
|
152
|
+
if (flags.skipped) {
|
|
153
|
+
console.log('<warning>');
|
|
154
|
+
console.log(skippedArtifactMessage(artifactId, changeName));
|
|
155
|
+
console.log(`Remove skip_specs from the change's .tospec.yaml before writing ${artifactId}.`);
|
|
156
|
+
console.log('</warning>');
|
|
157
|
+
console.log();
|
|
158
|
+
}
|
|
97
159
|
if (isBlocked) {
|
|
98
160
|
const missing = dependencies.filter((d) => !d.done).map((d) => d.id);
|
|
99
161
|
console.log('<warning>');
|
|
@@ -102,13 +164,29 @@ export function printInstructionsText(instructions, isBlocked) {
|
|
|
102
164
|
console.log('</warning>');
|
|
103
165
|
console.log();
|
|
104
166
|
}
|
|
105
|
-
// Task directive
|
|
106
167
|
console.log('<task>');
|
|
107
168
|
console.log(`Create the ${artifactId} artifact for change "${changeName}".`);
|
|
108
169
|
console.log(description);
|
|
109
170
|
console.log('</task>');
|
|
110
171
|
console.log();
|
|
111
|
-
//
|
|
172
|
+
// What `tospec new change` recorded, same as the JSON payload's
|
|
173
|
+
// `changeMetadata`: without it the text reader never learns the goal or which
|
|
174
|
+
// ADRs drove the change.
|
|
175
|
+
const decisions = changeMetadata.decisions ?? [];
|
|
176
|
+
if (changeMetadata.goal || decisions.length > 0) {
|
|
177
|
+
console.log('<change_context>');
|
|
178
|
+
if (changeMetadata.goal) {
|
|
179
|
+
console.log(`Goal: ${changeMetadata.goal}`);
|
|
180
|
+
}
|
|
181
|
+
if (decisions.length > 0) {
|
|
182
|
+
console.log('Decisions driving this change (read them under tospec/decisions/):');
|
|
183
|
+
for (const file of decisions) {
|
|
184
|
+
console.log(`- ${file}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
console.log('</change_context>');
|
|
188
|
+
console.log();
|
|
189
|
+
}
|
|
112
190
|
if (context) {
|
|
113
191
|
console.log('<project_context>');
|
|
114
192
|
console.log('<!-- This is background information for you. Do NOT include this in your output. -->');
|
|
@@ -116,7 +194,6 @@ export function printInstructionsText(instructions, isBlocked) {
|
|
|
116
194
|
console.log('</project_context>');
|
|
117
195
|
console.log();
|
|
118
196
|
}
|
|
119
|
-
// Rules (AI constraint - do not include in output)
|
|
120
197
|
if (rules && rules.length > 0) {
|
|
121
198
|
console.log('<rules>');
|
|
122
199
|
console.log('<!-- These are constraints for you to follow. Do NOT include this in your output. -->');
|
|
@@ -126,7 +203,6 @@ export function printInstructionsText(instructions, isBlocked) {
|
|
|
126
203
|
console.log('</rules>');
|
|
127
204
|
console.log();
|
|
128
205
|
}
|
|
129
|
-
// Dependencies (files to read for context)
|
|
130
206
|
if (dependencies.length > 0) {
|
|
131
207
|
console.log('<dependencies>');
|
|
132
208
|
console.log('Read the current contents of these files before creating this artifact (re-read them from disk even if you saw them earlier - they may have been edited since):');
|
|
@@ -142,29 +218,23 @@ export function printInstructionsText(instructions, isBlocked) {
|
|
|
142
218
|
console.log('</dependencies>');
|
|
143
219
|
console.log();
|
|
144
220
|
}
|
|
145
|
-
// Output location
|
|
146
221
|
console.log('<output>');
|
|
147
222
|
console.log(`Write to: ${resolvedOutputPath}`);
|
|
148
223
|
console.log('</output>');
|
|
149
224
|
console.log();
|
|
150
|
-
// Instruction (guidance)
|
|
151
225
|
if (instruction) {
|
|
152
226
|
console.log('<instruction>');
|
|
153
227
|
console.log(instruction.trim());
|
|
154
228
|
console.log('</instruction>');
|
|
155
229
|
console.log();
|
|
156
230
|
}
|
|
157
|
-
// Template
|
|
158
231
|
console.log('<template>');
|
|
159
232
|
console.log('<!-- Use this as the structure for your output file. Fill in the sections. -->');
|
|
160
233
|
console.log(template.trim());
|
|
161
234
|
console.log('</template>');
|
|
162
235
|
console.log();
|
|
163
|
-
// The schema's own acceptance criteria,
|
|
164
|
-
//
|
|
165
|
-
// in order not to fail validation was the one thing the brief left out — and
|
|
166
|
-
// an empty block claimed the artifact had no criteria at all. Printed like
|
|
167
|
-
// <project_context> and <rules>: present when there is something to say.
|
|
236
|
+
// The schema's own acceptance criteria, present only when it declares some:
|
|
237
|
+
// an empty block would claim there are none.
|
|
168
238
|
if (validation) {
|
|
169
239
|
console.log('<success_criteria>');
|
|
170
240
|
if (validation.requiredSections.length > 0) {
|
|
@@ -173,26 +243,23 @@ export function printInstructionsText(instructions, isBlocked) {
|
|
|
173
243
|
for (const [section, minLength] of Object.entries(validation.minSectionLength)) {
|
|
174
244
|
console.log(`Section "${section}" must be at least ${minLength} characters.`);
|
|
175
245
|
}
|
|
246
|
+
for (const [section, maxLength] of Object.entries(validation.maxSectionLength ?? {})) {
|
|
247
|
+
console.log(`Section "${section}" should stay under ${maxLength} characters.`);
|
|
248
|
+
}
|
|
176
249
|
console.log('</success_criteria>');
|
|
177
250
|
console.log();
|
|
178
251
|
}
|
|
179
|
-
// Unlocks
|
|
180
252
|
if (unlocks.length > 0) {
|
|
181
253
|
console.log('<unlocks>');
|
|
182
254
|
console.log(`Completing this artifact enables: ${unlocks.join(', ')}`);
|
|
183
255
|
console.log('</unlocks>');
|
|
184
256
|
console.log();
|
|
185
257
|
}
|
|
186
|
-
// Closing tag
|
|
187
258
|
console.log('</artifact>');
|
|
188
259
|
}
|
|
189
|
-
// -----------------------------------------------------------------------------
|
|
190
|
-
// Apply Instructions Command
|
|
191
|
-
// -----------------------------------------------------------------------------
|
|
192
260
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* (see skill-generation.ts for the generated list).
|
|
261
|
+
* The planning skill that produces a schema's artifacts, so blocked-state
|
|
262
|
+
* instructions point at a skill that exists.
|
|
196
263
|
*/
|
|
197
264
|
function planningSkillFor(schemaName) {
|
|
198
265
|
if (schemaName === 'issue')
|
|
@@ -202,44 +269,50 @@ function planningSkillFor(schemaName) {
|
|
|
202
269
|
return 'tospec-propose (sdd) or tospec-issue (issue)';
|
|
203
270
|
}
|
|
204
271
|
/**
|
|
205
|
-
*
|
|
272
|
+
* How to name the tracked task file in a message to the user. `path.basename`
|
|
273
|
+
* alone turns a glob (`tasks/*.md`) into a bare `*.md`, which names no file, so
|
|
274
|
+
* a glob keeps the `tracks` value as the schema authored it.
|
|
275
|
+
*/
|
|
276
|
+
function describeTracksFile(tracksFile) {
|
|
277
|
+
return /[*?[\]]/.test(tracksFile) ? tracksFile : path.basename(tracksFile);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
206
280
|
* Shares `parseTasksFromContent` with task progress so the apply instructions
|
|
207
281
|
* and the reported `x/y` can never disagree about what counts as a task.
|
|
208
282
|
*/
|
|
209
283
|
function parseTasksFile(content) {
|
|
210
|
-
return parseTasksFromContent(content).map((task, i) =>
|
|
211
|
-
// The number the
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
284
|
+
return parseTasksFromContent(content).map((task, i) => {
|
|
285
|
+
// The number the file gives the task, not this array's index: they diverge
|
|
286
|
+
// the moment a slice starts at 2.1 or a task is deleted, and the id is what
|
|
287
|
+
// a caller quotes back. Falls back to the ordinal when there is none.
|
|
288
|
+
const numbered = /^(\d+(?:\.\d+)*)\s+(.*)$/.exec(task.text);
|
|
289
|
+
return {
|
|
290
|
+
id: numbered?.[1] ?? `${i + 1}`,
|
|
291
|
+
// Without that number, since `id` carries it and a consumer joining the
|
|
292
|
+
// two would render "1.1 1.1 Write a failing test".
|
|
293
|
+
description: numbered?.[2] ?? task.text,
|
|
294
|
+
done: task.done,
|
|
295
|
+
};
|
|
296
|
+
});
|
|
220
297
|
}
|
|
221
298
|
/**
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* required artifacts, tracking file, and instruction.
|
|
299
|
+
* Apply instructions for a change, from the schema's `apply` block: required
|
|
300
|
+
* artifacts, tracking file, and instruction.
|
|
225
301
|
*/
|
|
226
302
|
export async function generateApplyInstructions(projectRoot, changeName, schemaName, options = {}) {
|
|
227
303
|
const planningHome = options.planningHome ?? resolveCurrentPlanningHomeSync({ startPath: projectRoot });
|
|
228
|
-
//
|
|
304
|
+
// The schema comes from the change's metadata when not given.
|
|
229
305
|
const context = loadChangeContext(projectRoot, changeName, schemaName, {
|
|
230
306
|
changeDir: getChangeDir(planningHome, changeName),
|
|
231
307
|
planningHome,
|
|
232
308
|
});
|
|
233
309
|
const changeDir = context.changeDir;
|
|
234
|
-
// Get the full schema to access the apply phase configuration
|
|
235
310
|
const schema = resolveSchema(context.schemaName, projectRoot);
|
|
236
311
|
const applyConfig = schema.apply;
|
|
237
|
-
//
|
|
238
|
-
// Fallback: if no apply block, require all artifacts
|
|
312
|
+
// No apply block: require every artifact.
|
|
239
313
|
const requiredArtifactIds = applyConfig?.requires ?? schema.artifacts.map((a) => a.id);
|
|
240
314
|
const tracksFile = applyConfig?.tracks ?? null;
|
|
241
315
|
const schemaInstruction = applyConfig?.instruction ?? null;
|
|
242
|
-
// Check which required artifacts are missing
|
|
243
316
|
const missingArtifacts = [];
|
|
244
317
|
for (const artifactId of requiredArtifactIds) {
|
|
245
318
|
const artifact = schema.artifacts.find((a) => a.id === artifactId);
|
|
@@ -247,7 +320,6 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
247
320
|
missingArtifacts.push(artifactId);
|
|
248
321
|
}
|
|
249
322
|
}
|
|
250
|
-
// Build context files from all existing artifacts in schema
|
|
251
323
|
const contextFiles = {};
|
|
252
324
|
for (const artifact of schema.artifacts) {
|
|
253
325
|
const outputs = resolveArtifactOutputs(changeDir, artifact.generates);
|
|
@@ -255,22 +327,22 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
255
327
|
contextFiles[artifact.id] = outputs;
|
|
256
328
|
}
|
|
257
329
|
}
|
|
258
|
-
//
|
|
330
|
+
// Through `resolveTaskFiles`, the resolver `list`, the dashboard and archive's
|
|
331
|
+
// completeness gate share: `apply.tracks` names an artifact by its `generates`
|
|
332
|
+
// value, which may be a glob (`tasks/*.md`). Treated as a literal path it
|
|
333
|
+
// finds no file, so a multi-file schema reports `blocked` and `0/0`.
|
|
259
334
|
let tasks = [];
|
|
260
335
|
let tracksFileExists = false;
|
|
261
336
|
if (tracksFile) {
|
|
262
|
-
const
|
|
263
|
-
tracksFileExists =
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
tasks = parseTasksFile(tasksContent);
|
|
337
|
+
const trackedFiles = resolveTaskFiles(changeDir, projectRoot).filter((file) => fs.existsSync(file));
|
|
338
|
+
tracksFileExists = trackedFiles.length > 0;
|
|
339
|
+
for (const file of trackedFiles) {
|
|
340
|
+
tasks.push(...parseTasksFile(await fs.promises.readFile(file, 'utf-8')));
|
|
267
341
|
}
|
|
268
342
|
}
|
|
269
|
-
// Calculate progress
|
|
270
343
|
const total = tasks.length;
|
|
271
344
|
const complete = tasks.filter((t) => t.done).length;
|
|
272
345
|
const remaining = total - complete;
|
|
273
|
-
// Determine state and instruction
|
|
274
346
|
let state;
|
|
275
347
|
let instruction;
|
|
276
348
|
const planningSkill = planningSkillFor(context.schemaName);
|
|
@@ -279,14 +351,12 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
279
351
|
instruction = `Cannot apply this change yet. Missing artifacts: ${missingArtifacts.join(', ')}.\nStop and send the user back to ${planningSkill} to create the missing artifacts first.`;
|
|
280
352
|
}
|
|
281
353
|
else if (tracksFile && !tracksFileExists) {
|
|
282
|
-
|
|
283
|
-
const tracksFilename = path.basename(tracksFile);
|
|
354
|
+
const tracksFilename = describeTracksFile(tracksFile);
|
|
284
355
|
state = 'blocked';
|
|
285
356
|
instruction = `The ${tracksFilename} file is missing and must be created.\nStop and send the user back to ${planningSkill} to generate the tracking file.`;
|
|
286
357
|
}
|
|
287
358
|
else if (tracksFile && tracksFileExists && total === 0) {
|
|
288
|
-
|
|
289
|
-
const tracksFilename = path.basename(tracksFile);
|
|
359
|
+
const tracksFilename = describeTracksFile(tracksFile);
|
|
290
360
|
state = 'blocked';
|
|
291
361
|
instruction = `The ${tracksFilename} file exists but contains no tasks.\nAdd tasks to ${tracksFilename} or stop and send the user back to ${planningSkill} to regenerate it.`;
|
|
292
362
|
}
|
|
@@ -295,7 +365,6 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
295
365
|
instruction = 'All tasks are complete! This change is ready to be archived.\nConsider running tests and reviewing the changes before archiving.';
|
|
296
366
|
}
|
|
297
367
|
else if (!tracksFile) {
|
|
298
|
-
// No tracking file configured in schema - ready to apply
|
|
299
368
|
state = 'ready';
|
|
300
369
|
instruction = schemaInstruction?.trim() ?? 'All required artifacts complete. Proceed with implementation.';
|
|
301
370
|
}
|
|
@@ -303,12 +372,10 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
303
372
|
state = 'ready';
|
|
304
373
|
instruction = schemaInstruction?.trim() ?? 'Read context files, work through pending tasks, mark complete as you go.\nPause if you hit blockers or need clarification.';
|
|
305
374
|
}
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
// chose". Naming the absent files is what stops that reading as a reference to
|
|
311
|
-
// something the agent simply failed to find.
|
|
375
|
+
// Absent artifacts `apply.requires` does not insist on: reported, not
|
|
376
|
+
// blocking. sdd's apply requires only `tasks`, so a change missing
|
|
377
|
+
// proposal/design/specs reaches `ready` while its instruction still refers
|
|
378
|
+
// to them.
|
|
312
379
|
const missingContext = schema.artifacts
|
|
313
380
|
.filter((artifact) => !contextFiles[artifact.id] && !missingArtifacts.includes(artifact.id))
|
|
314
381
|
.map((artifact) => artifact.id);
|
|
@@ -317,6 +384,10 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
317
384
|
|
|
318
385
|
Not present in this change: ${missingContext.join(', ')}. Ignore any part of the above that refers to them.`;
|
|
319
386
|
}
|
|
387
|
+
// The project's own context and rules, as every artifact's instructions carry.
|
|
388
|
+
const { config: projectConfig, diagnostics: configWarnings } = readProjectConfigWithDiagnostics(projectRoot);
|
|
389
|
+
const configContext = projectConfig?.context?.trim() || undefined;
|
|
390
|
+
const applyRules = projectConfig?.rules?.[APPLY_RULES_KEY];
|
|
320
391
|
return {
|
|
321
392
|
changeName,
|
|
322
393
|
changeDir,
|
|
@@ -328,39 +399,61 @@ Not present in this change: ${missingContext.join(', ')}. Ignore any part of the
|
|
|
328
399
|
missingArtifacts: missingArtifacts.length > 0 ? missingArtifacts : undefined,
|
|
329
400
|
missingContext: missingContext.length > 0 ? missingContext : undefined,
|
|
330
401
|
instruction,
|
|
402
|
+
...(configContext ? { context: configContext } : {}),
|
|
403
|
+
...(applyRules?.length ? { rules: applyRules } : {}),
|
|
404
|
+
...(configWarnings.length ? { configWarnings } : {}),
|
|
331
405
|
};
|
|
332
406
|
}
|
|
333
407
|
export async function applyInstructionsCommand(options) {
|
|
334
|
-
const
|
|
335
|
-
if (!
|
|
408
|
+
const target = await resolveInstructionsTarget(options);
|
|
409
|
+
if (!target) {
|
|
336
410
|
return;
|
|
337
411
|
}
|
|
338
|
-
const planningHome =
|
|
339
|
-
const projectRoot = root.path;
|
|
340
|
-
// Validate the schema before resolving the change (round 4 report 15): after
|
|
341
|
-
// it, the guard never fired in an empty project, so the caller was told to
|
|
342
|
-
// create a change first and only then learned the schema was never usable.
|
|
343
|
-
if (options.schema) {
|
|
344
|
-
validateSchemaExists(options.schema, projectRoot);
|
|
345
|
-
assertChangeSchema(options.schema);
|
|
346
|
-
}
|
|
347
|
-
const changeName = await validateChangeExists(options.change, projectRoot, root.changesDir, { newChangeHint: 'tospec new change <name>' });
|
|
348
|
-
// generateApplyInstructions uses loadChangeContext which auto-detects schema
|
|
412
|
+
const { root, planningHome, projectRoot, changeName } = target;
|
|
349
413
|
const instructions = await generateApplyInstructions(projectRoot, changeName, options.schema, {
|
|
350
414
|
planningHome,
|
|
351
415
|
});
|
|
352
416
|
if (options.json) {
|
|
353
|
-
|
|
417
|
+
// As in the artifact payload: `configWarnings` becomes the envelope's
|
|
418
|
+
// `status[]`, not a field of its own.
|
|
419
|
+
const { configWarnings, ...payload } = instructions;
|
|
420
|
+
emitSuccess({
|
|
421
|
+
...payload,
|
|
422
|
+
...(configWarnings?.length
|
|
423
|
+
? {
|
|
424
|
+
status: configWarnings.map(({ code, message }) => ({
|
|
425
|
+
severity: 'warning',
|
|
426
|
+
code,
|
|
427
|
+
message,
|
|
428
|
+
})),
|
|
429
|
+
}
|
|
430
|
+
: {}),
|
|
431
|
+
}, toRootOutput(root));
|
|
354
432
|
return;
|
|
355
433
|
}
|
|
356
434
|
printApplyInstructionsText(instructions);
|
|
357
435
|
}
|
|
358
436
|
export function printApplyInstructionsText(instructions) {
|
|
359
|
-
const { changeName, schemaName, contextFiles, progress, tasks, state, missingArtifacts, instruction } = instructions;
|
|
437
|
+
const { changeName, schemaName, contextFiles, progress, tasks, state, missingArtifacts, instruction, context, rules, } = instructions;
|
|
360
438
|
console.log(`## Apply: ${changeName}`);
|
|
361
439
|
console.log(`Schema: ${schemaName}`);
|
|
362
440
|
console.log();
|
|
363
|
-
//
|
|
441
|
+
// The same tags the artifact instructions use, so an agent reading either
|
|
442
|
+
// treats project constraints the same way.
|
|
443
|
+
if (context) {
|
|
444
|
+
console.log('<project_context>');
|
|
445
|
+
console.log(context);
|
|
446
|
+
console.log('</project_context>');
|
|
447
|
+
console.log();
|
|
448
|
+
}
|
|
449
|
+
if (rules && rules.length > 0) {
|
|
450
|
+
console.log('<rules>');
|
|
451
|
+
for (const rule of rules) {
|
|
452
|
+
console.log(`- ${rule}`);
|
|
453
|
+
}
|
|
454
|
+
console.log('</rules>');
|
|
455
|
+
console.log();
|
|
456
|
+
}
|
|
364
457
|
if (state === 'blocked' && missingArtifacts) {
|
|
365
458
|
console.log('### ⚠️ Blocked');
|
|
366
459
|
console.log();
|
|
@@ -368,7 +461,6 @@ export function printApplyInstructionsText(instructions) {
|
|
|
368
461
|
console.log(`Use the ${planningSkillFor(schemaName)} skill to create these first.`);
|
|
369
462
|
console.log();
|
|
370
463
|
}
|
|
371
|
-
// Context files (dynamically from schema)
|
|
372
464
|
const contextFileEntries = Object.entries(contextFiles);
|
|
373
465
|
if (contextFileEntries.length > 0) {
|
|
374
466
|
console.log('### Context Files');
|
|
@@ -379,7 +471,6 @@ export function printApplyInstructionsText(instructions) {
|
|
|
379
471
|
}
|
|
380
472
|
console.log();
|
|
381
473
|
}
|
|
382
|
-
// Progress (only show if we have tracking)
|
|
383
474
|
if (progress.total > 0 || tasks.length > 0) {
|
|
384
475
|
console.log('### Progress');
|
|
385
476
|
if (state === 'all_done') {
|
|
@@ -390,16 +481,16 @@ export function printApplyInstructionsText(instructions) {
|
|
|
390
481
|
}
|
|
391
482
|
console.log();
|
|
392
483
|
}
|
|
393
|
-
// Tasks
|
|
394
484
|
if (tasks.length > 0) {
|
|
395
485
|
console.log('### Tasks');
|
|
396
486
|
for (const task of tasks) {
|
|
397
487
|
const checkbox = task.done ? '[x]' : '[ ]';
|
|
398
|
-
|
|
488
|
+
// Joined again here: the number is stored apart only so a caller can
|
|
489
|
+
// address a task by it.
|
|
490
|
+
console.log(`- ${checkbox} ${task.id} ${task.description}`);
|
|
399
491
|
}
|
|
400
492
|
console.log();
|
|
401
493
|
}
|
|
402
|
-
// Instruction
|
|
403
494
|
console.log('### Instruction');
|
|
404
495
|
console.log(instruction);
|
|
405
496
|
}
|