@seanmars/tospec 0.19.0-beta.8 → 0.21.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 +89 -296
- package/README.md +69 -82
- package/assets/dashboard/app.js +11 -0
- package/assets/dashboard/style.css +7 -0
- package/bin/tospec.js +1 -1
- package/dist/cli/index.d.ts +6 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +120 -112
- 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 +300 -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 +252 -330
- 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 +301 -131
- 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 +228 -142
- 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 +230 -132
- 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 +75 -34
- 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 +33 -31
- package/dist/core/archive.d.ts.map +1 -1
- package/dist/core/archive.js +322 -284
- 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 +46 -11
- 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 +34 -25
- 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 +93 -169
- package/dist/core/init.js.map +1 -1
- package/dist/core/list.d.ts.map +1 -1
- package/dist/core/list.js +95 -41
- 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 +162 -136
- 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 +30 -23
- 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 +474 -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 +41 -45
- 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,39 +71,129 @@ 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
|
+
// The graph's own rule, not `done` alone: an optional dependency, or one the
|
|
82
|
+
// change declared away (skip_specs), is met without a file. Reading `done`
|
|
83
|
+
// told an sdd change with skip_specs that `tasks` was blocked on `specs` —
|
|
84
|
+
// while `status` called it ready — and sent the agent to write the specs the
|
|
85
|
+
// change had just declared it has none of.
|
|
86
|
+
const unmetDependencies = instructions.dependencies
|
|
87
|
+
.filter((d) => !d.done &&
|
|
88
|
+
!(context.skippedArtifacts?.has(d.id) ?? false) &&
|
|
89
|
+
context.graph.getArtifact(d.id)?.optional !== true)
|
|
90
|
+
.map((d) => d.id);
|
|
91
|
+
// `status` says "skipped" for this artifact, so a brief that reads as a
|
|
92
|
+
// plain "write it" sends an agent into the contradiction `validate` rejects
|
|
93
|
+
// (marker set, spec files present). Said first, in both output modes.
|
|
94
|
+
const isSkipped = context.skippedArtifacts?.has(artifactId) ?? false;
|
|
85
95
|
if (options.json) {
|
|
86
|
-
|
|
96
|
+
// `configWarnings` becomes `status[]`, the envelope field every command
|
|
97
|
+
// reports non-fatal trouble in.
|
|
98
|
+
const { configWarnings, ...payload } = instructions;
|
|
99
|
+
// First, ahead of any config warning: it is the one entry telling an agent
|
|
100
|
+
// not to write the artifact yet. The alternative is inferring it from
|
|
101
|
+
// `dependencies[].done`.
|
|
102
|
+
const status = [
|
|
103
|
+
...skippedDiagnostics(instructions, changeName, isSkipped),
|
|
104
|
+
...blockedDiagnostics(instructions, changeName, unmetDependencies),
|
|
105
|
+
...(configWarnings ?? []).map(({ code, message }) => ({
|
|
106
|
+
severity: 'warning',
|
|
107
|
+
code,
|
|
108
|
+
message,
|
|
109
|
+
})),
|
|
110
|
+
];
|
|
111
|
+
emitSuccess({
|
|
112
|
+
...payload,
|
|
113
|
+
...(status.length ? { status } : {}),
|
|
114
|
+
}, toRootOutput(root));
|
|
87
115
|
return;
|
|
88
116
|
}
|
|
89
|
-
printInstructionsText(instructions,
|
|
117
|
+
printInstructionsText(instructions, unmetDependencies, { skipped: isSkipped });
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The "declared away" warning, as a `status[]` entry — the JSON half of the
|
|
121
|
+
* `<warning>` block the human output prints. Empty unless the change's
|
|
122
|
+
* metadata skips this artifact.
|
|
123
|
+
*/
|
|
124
|
+
function skippedDiagnostics(instructions, changeName, skipped) {
|
|
125
|
+
if (!skipped)
|
|
126
|
+
return [];
|
|
127
|
+
return [
|
|
128
|
+
{
|
|
129
|
+
severity: 'warning',
|
|
130
|
+
code: 'artifact_skipped',
|
|
131
|
+
message: skippedArtifactMessage(instructions.artifactId, changeName),
|
|
132
|
+
fix: `Remove skip_specs from the change's .tospec.yaml before writing ${instructions.artifactId}.`,
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
function skippedArtifactMessage(artifactId, changeName) {
|
|
137
|
+
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
138
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
139
|
+
/**
|
|
140
|
+
* The unmet-dependency warning, as a `status[]` entry — the JSON half of the
|
|
141
|
+
* `<warning>` block the human output prints. Empty when nothing is missing, so
|
|
142
|
+
* the caller can concatenate it unconditionally.
|
|
143
|
+
*/
|
|
144
|
+
function blockedDiagnostics(instructions, changeName, missing) {
|
|
145
|
+
if (missing.length === 0)
|
|
146
|
+
return [];
|
|
147
|
+
return [
|
|
148
|
+
{
|
|
149
|
+
severity: 'warning',
|
|
150
|
+
code: 'artifact_blocked',
|
|
151
|
+
message: `Artifact '${instructions.artifactId}' has unmet dependencies: ${missing.join(', ')}. Complete them first or proceed with caution.`,
|
|
152
|
+
fix: `Run: tospec instructions ${missing[0]} --change ${changeName}`,
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
export function printInstructionsText(instructions, unmetDependencies, flags = {}) {
|
|
157
|
+
const { artifactId, changeName, schemaName, changeDir, resolvedOutputPath, description, instruction, context, rules, template, validation, dependencies, unlocks, changeMetadata, } = instructions;
|
|
94
158
|
console.log(`<artifact id="${artifactId}" change="${changeName}" schema="${schemaName}">`);
|
|
95
159
|
console.log();
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
160
|
+
if (flags.skipped) {
|
|
161
|
+
console.log('<warning>');
|
|
162
|
+
console.log(skippedArtifactMessage(artifactId, changeName));
|
|
163
|
+
console.log(`Remove skip_specs from the change's .tospec.yaml before writing ${artifactId}.`);
|
|
164
|
+
console.log('</warning>');
|
|
165
|
+
console.log();
|
|
166
|
+
}
|
|
167
|
+
if (unmetDependencies.length > 0) {
|
|
99
168
|
console.log('<warning>');
|
|
100
169
|
console.log('This artifact has unmet dependencies. Complete them first or proceed with caution.');
|
|
101
|
-
console.log(`Missing: ${
|
|
170
|
+
console.log(`Missing: ${unmetDependencies.join(', ')}`);
|
|
102
171
|
console.log('</warning>');
|
|
103
172
|
console.log();
|
|
104
173
|
}
|
|
105
|
-
// Task directive
|
|
106
174
|
console.log('<task>');
|
|
107
175
|
console.log(`Create the ${artifactId} artifact for change "${changeName}".`);
|
|
108
176
|
console.log(description);
|
|
109
177
|
console.log('</task>');
|
|
110
178
|
console.log();
|
|
111
|
-
//
|
|
179
|
+
// What `tospec new change` recorded, same as the JSON payload's
|
|
180
|
+
// `changeMetadata`: without it the text reader never learns the goal or which
|
|
181
|
+
// ADRs drove the change.
|
|
182
|
+
const decisions = changeMetadata.decisions ?? [];
|
|
183
|
+
if (changeMetadata.goal || decisions.length > 0) {
|
|
184
|
+
console.log('<change_context>');
|
|
185
|
+
if (changeMetadata.goal) {
|
|
186
|
+
console.log(`Goal: ${changeMetadata.goal}`);
|
|
187
|
+
}
|
|
188
|
+
if (decisions.length > 0) {
|
|
189
|
+
console.log('Decisions driving this change (read them under tospec/decisions/):');
|
|
190
|
+
for (const file of decisions) {
|
|
191
|
+
console.log(`- ${file}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
console.log('</change_context>');
|
|
195
|
+
console.log();
|
|
196
|
+
}
|
|
112
197
|
if (context) {
|
|
113
198
|
console.log('<project_context>');
|
|
114
199
|
console.log('<!-- This is background information for you. Do NOT include this in your output. -->');
|
|
@@ -116,7 +201,6 @@ export function printInstructionsText(instructions, isBlocked) {
|
|
|
116
201
|
console.log('</project_context>');
|
|
117
202
|
console.log();
|
|
118
203
|
}
|
|
119
|
-
// Rules (AI constraint - do not include in output)
|
|
120
204
|
if (rules && rules.length > 0) {
|
|
121
205
|
console.log('<rules>');
|
|
122
206
|
console.log('<!-- These are constraints for you to follow. Do NOT include this in your output. -->');
|
|
@@ -126,7 +210,6 @@ export function printInstructionsText(instructions, isBlocked) {
|
|
|
126
210
|
console.log('</rules>');
|
|
127
211
|
console.log();
|
|
128
212
|
}
|
|
129
|
-
// Dependencies (files to read for context)
|
|
130
213
|
if (dependencies.length > 0) {
|
|
131
214
|
console.log('<dependencies>');
|
|
132
215
|
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 +225,23 @@ export function printInstructionsText(instructions, isBlocked) {
|
|
|
142
225
|
console.log('</dependencies>');
|
|
143
226
|
console.log();
|
|
144
227
|
}
|
|
145
|
-
// Output location
|
|
146
228
|
console.log('<output>');
|
|
147
229
|
console.log(`Write to: ${resolvedOutputPath}`);
|
|
148
230
|
console.log('</output>');
|
|
149
231
|
console.log();
|
|
150
|
-
// Instruction (guidance)
|
|
151
232
|
if (instruction) {
|
|
152
233
|
console.log('<instruction>');
|
|
153
234
|
console.log(instruction.trim());
|
|
154
235
|
console.log('</instruction>');
|
|
155
236
|
console.log();
|
|
156
237
|
}
|
|
157
|
-
// Template
|
|
158
238
|
console.log('<template>');
|
|
159
239
|
console.log('<!-- Use this as the structure for your output file. Fill in the sections. -->');
|
|
160
240
|
console.log(template.trim());
|
|
161
241
|
console.log('</template>');
|
|
162
242
|
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.
|
|
243
|
+
// The schema's own acceptance criteria, present only when it declares some:
|
|
244
|
+
// an empty block would claim there are none.
|
|
168
245
|
if (validation) {
|
|
169
246
|
console.log('<success_criteria>');
|
|
170
247
|
if (validation.requiredSections.length > 0) {
|
|
@@ -173,26 +250,23 @@ export function printInstructionsText(instructions, isBlocked) {
|
|
|
173
250
|
for (const [section, minLength] of Object.entries(validation.minSectionLength)) {
|
|
174
251
|
console.log(`Section "${section}" must be at least ${minLength} characters.`);
|
|
175
252
|
}
|
|
253
|
+
for (const [section, maxLength] of Object.entries(validation.maxSectionLength ?? {})) {
|
|
254
|
+
console.log(`Section "${section}" should stay under ${maxLength} characters.`);
|
|
255
|
+
}
|
|
176
256
|
console.log('</success_criteria>');
|
|
177
257
|
console.log();
|
|
178
258
|
}
|
|
179
|
-
// Unlocks
|
|
180
259
|
if (unlocks.length > 0) {
|
|
181
260
|
console.log('<unlocks>');
|
|
182
261
|
console.log(`Completing this artifact enables: ${unlocks.join(', ')}`);
|
|
183
262
|
console.log('</unlocks>');
|
|
184
263
|
console.log();
|
|
185
264
|
}
|
|
186
|
-
// Closing tag
|
|
187
265
|
console.log('</artifact>');
|
|
188
266
|
}
|
|
189
|
-
// -----------------------------------------------------------------------------
|
|
190
|
-
// Apply Instructions Command
|
|
191
|
-
// -----------------------------------------------------------------------------
|
|
192
267
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* (see skill-generation.ts for the generated list).
|
|
268
|
+
* The planning skill that produces a schema's artifacts, so blocked-state
|
|
269
|
+
* instructions point at a skill that exists.
|
|
196
270
|
*/
|
|
197
271
|
function planningSkillFor(schemaName) {
|
|
198
272
|
if (schemaName === 'issue')
|
|
@@ -202,44 +276,50 @@ function planningSkillFor(schemaName) {
|
|
|
202
276
|
return 'tospec-propose (sdd) or tospec-issue (issue)';
|
|
203
277
|
}
|
|
204
278
|
/**
|
|
205
|
-
*
|
|
279
|
+
* How to name the tracked task file in a message to the user. `path.basename`
|
|
280
|
+
* alone turns a glob (`tasks/*.md`) into a bare `*.md`, which names no file, so
|
|
281
|
+
* a glob keeps the `tracks` value as the schema authored it.
|
|
282
|
+
*/
|
|
283
|
+
function describeTracksFile(tracksFile) {
|
|
284
|
+
return /[*?[\]]/.test(tracksFile) ? tracksFile : path.basename(tracksFile);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
206
287
|
* Shares `parseTasksFromContent` with task progress so the apply instructions
|
|
207
288
|
* and the reported `x/y` can never disagree about what counts as a task.
|
|
208
289
|
*/
|
|
209
290
|
function parseTasksFile(content) {
|
|
210
|
-
return parseTasksFromContent(content).map((task, i) =>
|
|
211
|
-
// The number the
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
291
|
+
return parseTasksFromContent(content).map((task, i) => {
|
|
292
|
+
// The number the file gives the task, not this array's index: they diverge
|
|
293
|
+
// the moment a slice starts at 2.1 or a task is deleted, and the id is what
|
|
294
|
+
// a caller quotes back. Falls back to the ordinal when there is none.
|
|
295
|
+
const numbered = /^(\d+(?:\.\d+)*)\s+(.*)$/.exec(task.text);
|
|
296
|
+
return {
|
|
297
|
+
id: numbered?.[1] ?? `${i + 1}`,
|
|
298
|
+
// Without that number, since `id` carries it and a consumer joining the
|
|
299
|
+
// two would render "1.1 1.1 Write a failing test".
|
|
300
|
+
description: numbered?.[2] ?? task.text,
|
|
301
|
+
done: task.done,
|
|
302
|
+
};
|
|
303
|
+
});
|
|
220
304
|
}
|
|
221
305
|
/**
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* required artifacts, tracking file, and instruction.
|
|
306
|
+
* Apply instructions for a change, from the schema's `apply` block: required
|
|
307
|
+
* artifacts, tracking file, and instruction.
|
|
225
308
|
*/
|
|
226
309
|
export async function generateApplyInstructions(projectRoot, changeName, schemaName, options = {}) {
|
|
227
310
|
const planningHome = options.planningHome ?? resolveCurrentPlanningHomeSync({ startPath: projectRoot });
|
|
228
|
-
//
|
|
311
|
+
// The schema comes from the change's metadata when not given.
|
|
229
312
|
const context = loadChangeContext(projectRoot, changeName, schemaName, {
|
|
230
313
|
changeDir: getChangeDir(planningHome, changeName),
|
|
231
314
|
planningHome,
|
|
232
315
|
});
|
|
233
316
|
const changeDir = context.changeDir;
|
|
234
|
-
// Get the full schema to access the apply phase configuration
|
|
235
317
|
const schema = resolveSchema(context.schemaName, projectRoot);
|
|
236
318
|
const applyConfig = schema.apply;
|
|
237
|
-
//
|
|
238
|
-
// Fallback: if no apply block, require all artifacts
|
|
319
|
+
// No apply block: require every artifact.
|
|
239
320
|
const requiredArtifactIds = applyConfig?.requires ?? schema.artifacts.map((a) => a.id);
|
|
240
321
|
const tracksFile = applyConfig?.tracks ?? null;
|
|
241
322
|
const schemaInstruction = applyConfig?.instruction ?? null;
|
|
242
|
-
// Check which required artifacts are missing
|
|
243
323
|
const missingArtifacts = [];
|
|
244
324
|
for (const artifactId of requiredArtifactIds) {
|
|
245
325
|
const artifact = schema.artifacts.find((a) => a.id === artifactId);
|
|
@@ -247,7 +327,6 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
247
327
|
missingArtifacts.push(artifactId);
|
|
248
328
|
}
|
|
249
329
|
}
|
|
250
|
-
// Build context files from all existing artifacts in schema
|
|
251
330
|
const contextFiles = {};
|
|
252
331
|
for (const artifact of schema.artifacts) {
|
|
253
332
|
const outputs = resolveArtifactOutputs(changeDir, artifact.generates);
|
|
@@ -255,22 +334,22 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
255
334
|
contextFiles[artifact.id] = outputs;
|
|
256
335
|
}
|
|
257
336
|
}
|
|
258
|
-
//
|
|
337
|
+
// Through `resolveTaskFiles`, the resolver `list`, the dashboard and archive's
|
|
338
|
+
// completeness gate share: `apply.tracks` names an artifact by its `generates`
|
|
339
|
+
// value, which may be a glob (`tasks/*.md`). Treated as a literal path it
|
|
340
|
+
// finds no file, so a multi-file schema reports `blocked` and `0/0`.
|
|
259
341
|
let tasks = [];
|
|
260
342
|
let tracksFileExists = false;
|
|
261
343
|
if (tracksFile) {
|
|
262
|
-
const
|
|
263
|
-
tracksFileExists =
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
tasks = parseTasksFile(tasksContent);
|
|
344
|
+
const trackedFiles = resolveTaskFiles(changeDir, projectRoot).filter((file) => fs.existsSync(file));
|
|
345
|
+
tracksFileExists = trackedFiles.length > 0;
|
|
346
|
+
for (const file of trackedFiles) {
|
|
347
|
+
tasks.push(...parseTasksFile(await fs.promises.readFile(file, 'utf-8')));
|
|
267
348
|
}
|
|
268
349
|
}
|
|
269
|
-
// Calculate progress
|
|
270
350
|
const total = tasks.length;
|
|
271
351
|
const complete = tasks.filter((t) => t.done).length;
|
|
272
352
|
const remaining = total - complete;
|
|
273
|
-
// Determine state and instruction
|
|
274
353
|
let state;
|
|
275
354
|
let instruction;
|
|
276
355
|
const planningSkill = planningSkillFor(context.schemaName);
|
|
@@ -279,14 +358,12 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
279
358
|
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
359
|
}
|
|
281
360
|
else if (tracksFile && !tracksFileExists) {
|
|
282
|
-
|
|
283
|
-
const tracksFilename = path.basename(tracksFile);
|
|
361
|
+
const tracksFilename = describeTracksFile(tracksFile);
|
|
284
362
|
state = 'blocked';
|
|
285
363
|
instruction = `The ${tracksFilename} file is missing and must be created.\nStop and send the user back to ${planningSkill} to generate the tracking file.`;
|
|
286
364
|
}
|
|
287
365
|
else if (tracksFile && tracksFileExists && total === 0) {
|
|
288
|
-
|
|
289
|
-
const tracksFilename = path.basename(tracksFile);
|
|
366
|
+
const tracksFilename = describeTracksFile(tracksFile);
|
|
290
367
|
state = 'blocked';
|
|
291
368
|
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
369
|
}
|
|
@@ -295,7 +372,6 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
295
372
|
instruction = 'All tasks are complete! This change is ready to be archived.\nConsider running tests and reviewing the changes before archiving.';
|
|
296
373
|
}
|
|
297
374
|
else if (!tracksFile) {
|
|
298
|
-
// No tracking file configured in schema - ready to apply
|
|
299
375
|
state = 'ready';
|
|
300
376
|
instruction = schemaInstruction?.trim() ?? 'All required artifacts complete. Proceed with implementation.';
|
|
301
377
|
}
|
|
@@ -303,12 +379,10 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
303
379
|
state = 'ready';
|
|
304
380
|
instruction = schemaInstruction?.trim() ?? 'Read context files, work through pending tasks, mark complete as you go.\nPause if you hit blockers or need clarification.';
|
|
305
381
|
}
|
|
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.
|
|
382
|
+
// Absent artifacts `apply.requires` does not insist on: reported, not
|
|
383
|
+
// blocking. sdd's apply requires only `tasks`, so a change missing
|
|
384
|
+
// proposal/design/specs reaches `ready` while its instruction still refers
|
|
385
|
+
// to them.
|
|
312
386
|
const missingContext = schema.artifacts
|
|
313
387
|
.filter((artifact) => !contextFiles[artifact.id] && !missingArtifacts.includes(artifact.id))
|
|
314
388
|
.map((artifact) => artifact.id);
|
|
@@ -317,6 +391,10 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
317
391
|
|
|
318
392
|
Not present in this change: ${missingContext.join(', ')}. Ignore any part of the above that refers to them.`;
|
|
319
393
|
}
|
|
394
|
+
// The project's own context and rules, as every artifact's instructions carry.
|
|
395
|
+
const { config: projectConfig, diagnostics: configWarnings } = readProjectConfigWithDiagnostics(projectRoot);
|
|
396
|
+
const configContext = projectConfig?.context?.trim() || undefined;
|
|
397
|
+
const applyRules = projectConfig?.rules?.[APPLY_RULES_KEY];
|
|
320
398
|
return {
|
|
321
399
|
changeName,
|
|
322
400
|
changeDir,
|
|
@@ -328,39 +406,61 @@ Not present in this change: ${missingContext.join(', ')}. Ignore any part of the
|
|
|
328
406
|
missingArtifacts: missingArtifacts.length > 0 ? missingArtifacts : undefined,
|
|
329
407
|
missingContext: missingContext.length > 0 ? missingContext : undefined,
|
|
330
408
|
instruction,
|
|
409
|
+
...(configContext ? { context: configContext } : {}),
|
|
410
|
+
...(applyRules?.length ? { rules: applyRules } : {}),
|
|
411
|
+
...(configWarnings.length ? { configWarnings } : {}),
|
|
331
412
|
};
|
|
332
413
|
}
|
|
333
414
|
export async function applyInstructionsCommand(options) {
|
|
334
|
-
const
|
|
335
|
-
if (!
|
|
415
|
+
const target = await resolveInstructionsTarget(options);
|
|
416
|
+
if (!target) {
|
|
336
417
|
return;
|
|
337
418
|
}
|
|
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
|
|
419
|
+
const { root, planningHome, projectRoot, changeName } = target;
|
|
349
420
|
const instructions = await generateApplyInstructions(projectRoot, changeName, options.schema, {
|
|
350
421
|
planningHome,
|
|
351
422
|
});
|
|
352
423
|
if (options.json) {
|
|
353
|
-
|
|
424
|
+
// As in the artifact payload: `configWarnings` becomes the envelope's
|
|
425
|
+
// `status[]`, not a field of its own.
|
|
426
|
+
const { configWarnings, ...payload } = instructions;
|
|
427
|
+
emitSuccess({
|
|
428
|
+
...payload,
|
|
429
|
+
...(configWarnings?.length
|
|
430
|
+
? {
|
|
431
|
+
status: configWarnings.map(({ code, message }) => ({
|
|
432
|
+
severity: 'warning',
|
|
433
|
+
code,
|
|
434
|
+
message,
|
|
435
|
+
})),
|
|
436
|
+
}
|
|
437
|
+
: {}),
|
|
438
|
+
}, toRootOutput(root));
|
|
354
439
|
return;
|
|
355
440
|
}
|
|
356
441
|
printApplyInstructionsText(instructions);
|
|
357
442
|
}
|
|
358
443
|
export function printApplyInstructionsText(instructions) {
|
|
359
|
-
const { changeName, schemaName, contextFiles, progress, tasks, state, missingArtifacts, instruction } = instructions;
|
|
444
|
+
const { changeName, schemaName, contextFiles, progress, tasks, state, missingArtifacts, instruction, context, rules, } = instructions;
|
|
360
445
|
console.log(`## Apply: ${changeName}`);
|
|
361
446
|
console.log(`Schema: ${schemaName}`);
|
|
362
447
|
console.log();
|
|
363
|
-
//
|
|
448
|
+
// The same tags the artifact instructions use, so an agent reading either
|
|
449
|
+
// treats project constraints the same way.
|
|
450
|
+
if (context) {
|
|
451
|
+
console.log('<project_context>');
|
|
452
|
+
console.log(context);
|
|
453
|
+
console.log('</project_context>');
|
|
454
|
+
console.log();
|
|
455
|
+
}
|
|
456
|
+
if (rules && rules.length > 0) {
|
|
457
|
+
console.log('<rules>');
|
|
458
|
+
for (const rule of rules) {
|
|
459
|
+
console.log(`- ${rule}`);
|
|
460
|
+
}
|
|
461
|
+
console.log('</rules>');
|
|
462
|
+
console.log();
|
|
463
|
+
}
|
|
364
464
|
if (state === 'blocked' && missingArtifacts) {
|
|
365
465
|
console.log('### ⚠️ Blocked');
|
|
366
466
|
console.log();
|
|
@@ -368,7 +468,6 @@ export function printApplyInstructionsText(instructions) {
|
|
|
368
468
|
console.log(`Use the ${planningSkillFor(schemaName)} skill to create these first.`);
|
|
369
469
|
console.log();
|
|
370
470
|
}
|
|
371
|
-
// Context files (dynamically from schema)
|
|
372
471
|
const contextFileEntries = Object.entries(contextFiles);
|
|
373
472
|
if (contextFileEntries.length > 0) {
|
|
374
473
|
console.log('### Context Files');
|
|
@@ -379,7 +478,6 @@ export function printApplyInstructionsText(instructions) {
|
|
|
379
478
|
}
|
|
380
479
|
console.log();
|
|
381
480
|
}
|
|
382
|
-
// Progress (only show if we have tracking)
|
|
383
481
|
if (progress.total > 0 || tasks.length > 0) {
|
|
384
482
|
console.log('### Progress');
|
|
385
483
|
if (state === 'all_done') {
|
|
@@ -390,16 +488,16 @@ export function printApplyInstructionsText(instructions) {
|
|
|
390
488
|
}
|
|
391
489
|
console.log();
|
|
392
490
|
}
|
|
393
|
-
// Tasks
|
|
394
491
|
if (tasks.length > 0) {
|
|
395
492
|
console.log('### Tasks');
|
|
396
493
|
for (const task of tasks) {
|
|
397
494
|
const checkbox = task.done ? '[x]' : '[ ]';
|
|
398
|
-
|
|
495
|
+
// Joined again here: the number is stored apart only so a caller can
|
|
496
|
+
// address a task by it.
|
|
497
|
+
console.log(`- ${checkbox} ${task.id} ${task.description}`);
|
|
399
498
|
}
|
|
400
499
|
console.log();
|
|
401
500
|
}
|
|
402
|
-
// Instruction
|
|
403
501
|
console.log('### Instruction');
|
|
404
502
|
console.log(instruction);
|
|
405
503
|
}
|