@seanmars/tospec 0.19.0-beta.7 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -231
- package/README.md +69 -82
- package/assets/dashboard/app.js +14 -3
- package/assets/dashboard/style.css +7 -0
- package/assets/rules/tospec/decision.md +3 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +227 -86
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/config.d.ts +9 -17
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +366 -107
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/dashboard.d.ts +57 -99
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +224 -282
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/decision.d.ts +41 -19
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +385 -79
- package/dist/commands/decision.js.map +1 -1
- package/dist/commands/metrics.d.ts +28 -51
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +62 -93
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/shared-output.d.ts +18 -21
- package/dist/commands/shared-output.d.ts.map +1 -1
- package/dist/commands/shared-output.js +49 -23
- package/dist/commands/shared-output.js.map +1 -1
- package/dist/commands/show.d.ts +7 -0
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +29 -7
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/validate.d.ts +43 -45
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +262 -129
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/workflow/index.d.ts +6 -10
- package/dist/commands/workflow/index.d.ts.map +1 -1
- package/dist/commands/workflow/index.js +6 -10
- package/dist/commands/workflow/index.js.map +1 -1
- package/dist/commands/workflow/instructions.d.ts +21 -8
- package/dist/commands/workflow/instructions.d.ts.map +1 -1
- package/dist/commands/workflow/instructions.js +254 -95
- package/dist/commands/workflow/instructions.js.map +1 -1
- package/dist/commands/workflow/new-change.d.ts +4 -5
- package/dist/commands/workflow/new-change.d.ts.map +1 -1
- package/dist/commands/workflow/new-change.js +90 -25
- package/dist/commands/workflow/new-change.js.map +1 -1
- package/dist/commands/workflow/schemas.d.ts +3 -5
- package/dist/commands/workflow/schemas.d.ts.map +1 -1
- package/dist/commands/workflow/schemas.js +37 -11
- package/dist/commands/workflow/schemas.js.map +1 -1
- package/dist/commands/workflow/shared.d.ts +48 -21
- package/dist/commands/workflow/shared.d.ts.map +1 -1
- package/dist/commands/workflow/shared.js +36 -33
- package/dist/commands/workflow/shared.js.map +1 -1
- package/dist/commands/workflow/status.d.ts +10 -6
- package/dist/commands/workflow/status.d.ts.map +1 -1
- package/dist/commands/workflow/status.js +81 -42
- package/dist/commands/workflow/status.js.map +1 -1
- package/dist/commands/workflow/templates.d.ts +10 -3
- package/dist/commands/workflow/templates.d.ts.map +1 -1
- package/dist/commands/workflow/templates.js +39 -40
- package/dist/commands/workflow/templates.js.map +1 -1
- package/dist/core/archive.d.ts +26 -21
- package/dist/core/archive.d.ts.map +1 -1
- package/dist/core/archive.js +378 -235
- package/dist/core/archive.js.map +1 -1
- package/dist/core/artifact-graph/graph.d.ts +25 -42
- package/dist/core/artifact-graph/graph.d.ts.map +1 -1
- package/dist/core/artifact-graph/graph.js +45 -63
- package/dist/core/artifact-graph/graph.js.map +1 -1
- package/dist/core/artifact-graph/index.d.ts +1 -1
- package/dist/core/artifact-graph/index.d.ts.map +1 -1
- package/dist/core/artifact-graph/index.js +1 -1
- package/dist/core/artifact-graph/index.js.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +97 -103
- package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.js +146 -119
- package/dist/core/artifact-graph/instruction-loader.js.map +1 -1
- package/dist/core/artifact-graph/outputs.d.ts +9 -23
- package/dist/core/artifact-graph/outputs.d.ts.map +1 -1
- package/dist/core/artifact-graph/outputs.js +45 -38
- package/dist/core/artifact-graph/outputs.js.map +1 -1
- package/dist/core/artifact-graph/resolver.d.ts +44 -63
- package/dist/core/artifact-graph/resolver.d.ts.map +1 -1
- package/dist/core/artifact-graph/resolver.js +85 -86
- package/dist/core/artifact-graph/resolver.js.map +1 -1
- package/dist/core/artifact-graph/schema.d.ts +0 -6
- package/dist/core/artifact-graph/schema.d.ts.map +1 -1
- package/dist/core/artifact-graph/schema.js +7 -32
- package/dist/core/artifact-graph/schema.js.map +1 -1
- package/dist/core/artifact-graph/state.d.ts +1 -8
- package/dist/core/artifact-graph/state.d.ts.map +1 -1
- package/dist/core/artifact-graph/state.js +2 -17
- package/dist/core/artifact-graph/state.js.map +1 -1
- package/dist/core/artifact-graph/stub-detection.d.ts +12 -0
- package/dist/core/artifact-graph/stub-detection.d.ts.map +1 -0
- package/dist/core/artifact-graph/stub-detection.js +39 -0
- package/dist/core/artifact-graph/stub-detection.js.map +1 -0
- package/dist/core/artifact-graph/types.d.ts +4 -0
- package/dist/core/artifact-graph/types.d.ts.map +1 -1
- package/dist/core/artifact-graph/types.js +30 -10
- package/dist/core/artifact-graph/types.js.map +1 -1
- package/dist/core/available-tools.d.ts +3 -12
- package/dist/core/available-tools.d.ts.map +1 -1
- package/dist/core/available-tools.js +4 -13
- package/dist/core/available-tools.js.map +1 -1
- package/dist/core/change-metadata/schema.d.ts +1 -1
- package/dist/core/change-metadata/schema.d.ts.map +1 -1
- package/dist/core/change-metadata/schema.js +10 -7
- package/dist/core/change-metadata/schema.js.map +1 -1
- package/dist/core/change-presenter.d.ts +23 -18
- package/dist/core/change-presenter.d.ts.map +1 -1
- package/dist/core/change-presenter.js +102 -43
- package/dist/core/change-presenter.js.map +1 -1
- package/dist/core/change-status-policy.d.ts +8 -1
- package/dist/core/change-status-policy.d.ts.map +1 -1
- package/dist/core/change-status-policy.js +25 -1
- package/dist/core/change-status-policy.js.map +1 -1
- package/dist/core/codex-metrics.d.ts +25 -45
- package/dist/core/codex-metrics.d.ts.map +1 -1
- package/dist/core/codex-metrics.js +44 -88
- package/dist/core/codex-metrics.js.map +1 -1
- package/dist/core/codex-residue.d.ts +14 -15
- package/dist/core/codex-residue.d.ts.map +1 -1
- package/dist/core/codex-residue.js +18 -22
- package/dist/core/codex-residue.js.map +1 -1
- package/dist/core/command-generation/adapters/claude.d.ts +2 -9
- package/dist/core/command-generation/adapters/claude.d.ts.map +1 -1
- package/dist/core/command-generation/adapters/claude.js +2 -12
- package/dist/core/command-generation/adapters/claude.js.map +1 -1
- package/dist/core/command-generation/adapters/index.d.ts +1 -9
- package/dist/core/command-generation/adapters/index.d.ts.map +1 -1
- package/dist/core/command-generation/adapters/index.js +1 -9
- package/dist/core/command-generation/adapters/index.js.map +1 -1
- package/dist/core/command-generation/generator.d.ts +0 -17
- package/dist/core/command-generation/generator.d.ts.map +1 -1
- package/dist/core/command-generation/generator.js +0 -17
- package/dist/core/command-generation/generator.js.map +1 -1
- package/dist/core/command-generation/index.d.ts +2 -5
- package/dist/core/command-generation/index.d.ts.map +1 -1
- package/dist/core/command-generation/index.js +0 -9
- package/dist/core/command-generation/index.js.map +1 -1
- package/dist/core/command-generation/types.d.ts +10 -36
- package/dist/core/command-generation/types.d.ts.map +1 -1
- package/dist/core/command-generation/types.js +0 -6
- package/dist/core/command-generation/types.js.map +1 -1
- package/dist/core/command-generation/yaml.d.ts +3 -18
- package/dist/core/command-generation/yaml.d.ts.map +1 -1
- package/dist/core/command-generation/yaml.js +5 -23
- package/dist/core/command-generation/yaml.js.map +1 -1
- package/dist/core/config-prompts.d.ts +2 -4
- package/dist/core/config-prompts.d.ts.map +1 -1
- package/dist/core/config-prompts.js +2 -7
- package/dist/core/config-prompts.js.map +1 -1
- package/dist/core/config-schema.d.ts +8 -53
- package/dist/core/config-schema.d.ts.map +1 -1
- package/dist/core/config-schema.js +49 -62
- package/dist/core/config-schema.js.map +1 -1
- package/dist/core/config.d.ts +39 -26
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +36 -22
- package/dist/core/config.js.map +1 -1
- package/dist/core/dashboard-activity.d.ts +7 -9
- package/dist/core/dashboard-activity.d.ts.map +1 -1
- package/dist/core/dashboard-activity.js +26 -24
- package/dist/core/dashboard-activity.js.map +1 -1
- package/dist/core/dashboard-data.d.ts +40 -22
- package/dist/core/dashboard-data.d.ts.map +1 -1
- package/dist/core/dashboard-data.js +62 -70
- package/dist/core/dashboard-data.js.map +1 -1
- package/dist/core/global-config.d.ts +24 -53
- package/dist/core/global-config.d.ts.map +1 -1
- package/dist/core/global-config.js +38 -67
- package/dist/core/global-config.js.map +1 -1
- package/dist/core/init.d.ts +25 -19
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +168 -181
- package/dist/core/init.js.map +1 -1
- package/dist/core/list.d.ts +1 -1
- package/dist/core/list.d.ts.map +1 -1
- package/dist/core/list.js +121 -28
- package/dist/core/list.js.map +1 -1
- package/dist/core/local-server.d.ts +41 -83
- package/dist/core/local-server.d.ts.map +1 -1
- package/dist/core/local-server.js +53 -98
- package/dist/core/local-server.js.map +1 -1
- package/dist/core/markdown-render.d.ts +25 -0
- package/dist/core/markdown-render.d.ts.map +1 -0
- package/dist/core/markdown-render.js +94 -0
- package/dist/core/markdown-render.js.map +1 -0
- package/dist/core/migrate.d.ts +29 -14
- package/dist/core/migrate.d.ts.map +1 -1
- package/dist/core/migrate.js +184 -122
- package/dist/core/migrate.js.map +1 -1
- package/dist/core/parsers/change-parser.d.ts +7 -10
- package/dist/core/parsers/change-parser.d.ts.map +1 -1
- package/dist/core/parsers/change-parser.js +48 -56
- package/dist/core/parsers/change-parser.js.map +1 -1
- package/dist/core/parsers/markdown-parser.d.ts +8 -9
- package/dist/core/parsers/markdown-parser.d.ts.map +1 -1
- package/dist/core/parsers/markdown-parser.js +31 -22
- package/dist/core/parsers/markdown-parser.js.map +1 -1
- package/dist/core/parsers/requirement-blocks.d.ts +43 -15
- package/dist/core/parsers/requirement-blocks.d.ts.map +1 -1
- package/dist/core/parsers/requirement-blocks.js +142 -70
- package/dist/core/parsers/requirement-blocks.js.map +1 -1
- package/dist/core/parsers/requirement-text.d.ts +73 -79
- package/dist/core/parsers/requirement-text.d.ts.map +1 -1
- package/dist/core/parsers/requirement-text.js +137 -79
- package/dist/core/parsers/requirement-text.js.map +1 -1
- package/dist/core/parsers/spec-structure.d.ts.map +1 -1
- package/dist/core/parsers/spec-structure.js +10 -6
- package/dist/core/parsers/spec-structure.js.map +1 -1
- package/dist/core/profiles.d.ts +3 -10
- package/dist/core/profiles.d.ts.map +1 -1
- package/dist/core/profiles.js +5 -12
- package/dist/core/profiles.js.map +1 -1
- package/dist/core/project-config.d.ts +43 -44
- package/dist/core/project-config.d.ts.map +1 -1
- package/dist/core/project-config.js +107 -82
- package/dist/core/project-config.js.map +1 -1
- package/dist/core/project-layout.d.ts +9 -17
- package/dist/core/project-layout.d.ts.map +1 -1
- package/dist/core/project-layout.js +16 -26
- package/dist/core/project-layout.js.map +1 -1
- package/dist/core/root-selection.d.ts +11 -7
- package/dist/core/root-selection.d.ts.map +1 -1
- package/dist/core/root-selection.js +7 -8
- package/dist/core/root-selection.js.map +1 -1
- package/dist/core/rules.d.ts +6 -1
- package/dist/core/rules.d.ts.map +1 -1
- package/dist/core/rules.js +23 -2
- package/dist/core/rules.js.map +1 -1
- package/dist/core/schema-names.d.ts +16 -0
- package/dist/core/schema-names.d.ts.map +1 -0
- package/dist/core/schema-names.js +16 -0
- package/dist/core/schema-names.js.map +1 -0
- package/dist/core/schemas/base.schema.d.ts +3 -0
- package/dist/core/schemas/base.schema.d.ts.map +1 -1
- package/dist/core/schemas/base.schema.js +22 -6
- package/dist/core/schemas/base.schema.js.map +1 -1
- package/dist/core/schemas/change.schema.d.ts +16 -0
- package/dist/core/schemas/change.schema.d.ts.map +1 -1
- package/dist/core/schemas/change.schema.js +41 -10
- package/dist/core/schemas/change.schema.js.map +1 -1
- package/dist/core/schemas/spec.schema.d.ts +2 -0
- package/dist/core/schemas/spec.schema.d.ts.map +1 -1
- package/dist/core/shared/index.d.ts +3 -8
- package/dist/core/shared/index.d.ts.map +1 -1
- package/dist/core/shared/index.js +3 -8
- package/dist/core/shared/index.js.map +1 -1
- package/dist/core/shared/rules-generation.d.ts +19 -8
- package/dist/core/shared/rules-generation.d.ts.map +1 -1
- package/dist/core/shared/rules-generation.js +117 -50
- package/dist/core/shared/rules-generation.js.map +1 -1
- package/dist/core/shared/skill-generation.d.ts +28 -43
- package/dist/core/shared/skill-generation.d.ts.map +1 -1
- package/dist/core/shared/skill-generation.js +82 -51
- package/dist/core/shared/skill-generation.js.map +1 -1
- package/dist/core/shared/tool-detection.d.ts +39 -61
- package/dist/core/shared/tool-detection.d.ts.map +1 -1
- package/dist/core/shared/tool-detection.js +88 -80
- package/dist/core/shared/tool-detection.js.map +1 -1
- package/dist/core/skill-metrics.d.ts +36 -63
- package/dist/core/skill-metrics.d.ts.map +1 -1
- package/dist/core/skill-metrics.js +34 -73
- package/dist/core/skill-metrics.js.map +1 -1
- package/dist/core/spec-presenter.js +6 -6
- package/dist/core/spec-presenter.js.map +1 -1
- package/dist/core/specs-apply.d.ts +22 -23
- package/dist/core/specs-apply.d.ts.map +1 -1
- package/dist/core/specs-apply.js +166 -193
- package/dist/core/specs-apply.js.map +1 -1
- package/dist/core/templates/fragments/interview.d.ts +2 -6
- package/dist/core/templates/fragments/interview.d.ts.map +1 -1
- package/dist/core/templates/fragments/interview.js +2 -6
- package/dist/core/templates/fragments/interview.js.map +1 -1
- package/dist/core/templates/fragments/next-step.d.ts +4 -8
- package/dist/core/templates/fragments/next-step.d.ts.map +1 -1
- package/dist/core/templates/fragments/next-step.js +4 -8
- package/dist/core/templates/fragments/next-step.js.map +1 -1
- package/dist/core/templates/fragments/validate.d.ts +13 -0
- package/dist/core/templates/fragments/validate.d.ts.map +1 -0
- package/dist/core/templates/fragments/validate.js +13 -0
- package/dist/core/templates/fragments/validate.js.map +1 -0
- package/dist/core/templates/fragments/verify.d.ts +9 -12
- package/dist/core/templates/fragments/verify.d.ts.map +1 -1
- package/dist/core/templates/fragments/verify.js +9 -12
- package/dist/core/templates/fragments/verify.js.map +1 -1
- package/dist/core/templates/index.d.ts +0 -6
- package/dist/core/templates/index.d.ts.map +1 -1
- package/dist/core/templates/index.js +0 -7
- package/dist/core/templates/index.js.map +1 -1
- package/dist/core/templates/skill-templates.d.ts +1 -5
- package/dist/core/templates/skill-templates.d.ts.map +1 -1
- package/dist/core/templates/skill-templates.js +0 -5
- package/dist/core/templates/skill-templates.js.map +1 -1
- package/dist/core/templates/types.d.ts +3 -7
- package/dist/core/templates/types.d.ts.map +1 -1
- package/dist/core/templates/types.js +0 -3
- package/dist/core/templates/types.js.map +1 -1
- package/dist/core/templates/workflows/apply.d.ts +3 -9
- package/dist/core/templates/workflows/apply.d.ts.map +1 -1
- package/dist/core/templates/workflows/apply.js +11 -13
- package/dist/core/templates/workflows/apply.js.map +1 -1
- package/dist/core/templates/workflows/archive.d.ts +0 -6
- package/dist/core/templates/workflows/archive.d.ts.map +1 -1
- package/dist/core/templates/workflows/archive.js +19 -6
- package/dist/core/templates/workflows/archive.js.map +1 -1
- package/dist/core/templates/workflows/decision.js +4 -4
- package/dist/core/templates/workflows/decision.js.map +1 -1
- package/dist/core/templates/workflows/explore.js +1 -1
- package/dist/core/templates/workflows/grill.d.ts.map +1 -1
- package/dist/core/templates/workflows/grill.js +0 -2
- package/dist/core/templates/workflows/grill.js.map +1 -1
- package/dist/core/templates/workflows/issue.d.ts +0 -6
- package/dist/core/templates/workflows/issue.d.ts.map +1 -1
- package/dist/core/templates/workflows/issue.js +3 -2
- package/dist/core/templates/workflows/issue.js.map +1 -1
- package/dist/core/templates/workflows/propose.d.ts +0 -6
- package/dist/core/templates/workflows/propose.d.ts.map +1 -1
- package/dist/core/templates/workflows/propose.js +2 -2
- package/dist/core/templates/workflows/propose.js.map +1 -1
- package/dist/core/templates/workflows/sync.d.ts +2 -8
- package/dist/core/templates/workflows/sync.d.ts.map +1 -1
- package/dist/core/templates/workflows/sync.js +4 -3
- package/dist/core/templates/workflows/sync.js.map +1 -1
- package/dist/core/templates/workflows/update.d.ts +0 -6
- package/dist/core/templates/workflows/update.d.ts.map +1 -1
- package/dist/core/templates/workflows/update.js +9 -2
- package/dist/core/templates/workflows/update.js.map +1 -1
- package/dist/core/update.d.ts +23 -23
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +139 -118
- package/dist/core/update.js.map +1 -1
- package/dist/core/user-state-migration.d.ts +13 -15
- package/dist/core/user-state-migration.d.ts.map +1 -1
- package/dist/core/user-state-migration.js +16 -20
- package/dist/core/user-state-migration.js.map +1 -1
- package/dist/core/validation/constants.d.ts +4 -10
- package/dist/core/validation/constants.d.ts.map +1 -1
- package/dist/core/validation/constants.js +25 -20
- package/dist/core/validation/constants.js.map +1 -1
- package/dist/core/validation/prose-length.d.ts +15 -0
- package/dist/core/validation/prose-length.d.ts.map +1 -0
- package/dist/core/validation/prose-length.js +29 -0
- package/dist/core/validation/prose-length.js.map +1 -0
- package/dist/core/validation/purpose-placeholder.d.ts +9 -16
- package/dist/core/validation/purpose-placeholder.d.ts.map +1 -1
- package/dist/core/validation/purpose-placeholder.js +30 -44
- package/dist/core/validation/purpose-placeholder.js.map +1 -1
- package/dist/core/validation/section-validator.d.ts +4 -4
- package/dist/core/validation/section-validator.d.ts.map +1 -1
- package/dist/core/validation/section-validator.js +43 -7
- package/dist/core/validation/section-validator.js.map +1 -1
- package/dist/core/validation/task-numbering.d.ts +6 -3
- package/dist/core/validation/task-numbering.d.ts.map +1 -1
- package/dist/core/validation/task-numbering.js +23 -11
- package/dist/core/validation/task-numbering.js.map +1 -1
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.d.ts.map +1 -1
- package/dist/core/validation/types.js +12 -1
- package/dist/core/validation/types.js.map +1 -1
- package/dist/core/validation/validator.d.ts +43 -63
- package/dist/core/validation/validator.d.ts.map +1 -1
- package/dist/core/validation/validator.js +500 -267
- package/dist/core/validation/validator.js.map +1 -1
- package/dist/prompts/searchable-multi-select.d.ts +3 -8
- package/dist/prompts/searchable-multi-select.d.ts.map +1 -1
- package/dist/prompts/searchable-multi-select.js +16 -39
- package/dist/prompts/searchable-multi-select.js.map +1 -1
- package/dist/utils/change-metadata.d.ts +11 -50
- package/dist/utils/change-metadata.d.ts.map +1 -1
- package/dist/utils/change-metadata.js +48 -67
- package/dist/utils/change-metadata.js.map +1 -1
- package/dist/utils/change-utils.d.ts +30 -54
- package/dist/utils/change-utils.d.ts.map +1 -1
- package/dist/utils/change-utils.js +133 -91
- package/dist/utils/change-utils.js.map +1 -1
- package/dist/utils/file-lock.d.ts +39 -0
- package/dist/utils/file-lock.d.ts.map +1 -0
- package/dist/utils/file-lock.js +149 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/dist/utils/file-system.d.ts +12 -32
- package/dist/utils/file-system.d.ts.map +1 -1
- package/dist/utils/file-system.js +16 -40
- package/dist/utils/file-system.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +7 -11
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +11 -11
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/interactive.d.ts +4 -9
- package/dist/utils/interactive.d.ts.map +1 -1
- package/dist/utils/interactive.js +2 -4
- package/dist/utils/interactive.js.map +1 -1
- package/dist/utils/item-discovery.d.ts +15 -10
- package/dist/utils/item-discovery.d.ts.map +1 -1
- package/dist/utils/item-discovery.js +42 -47
- package/dist/utils/item-discovery.js.map +1 -1
- package/dist/utils/link.d.ts +9 -18
- package/dist/utils/link.d.ts.map +1 -1
- package/dist/utils/link.js +9 -18
- package/dist/utils/link.js.map +1 -1
- package/dist/utils/requirement-diff.d.ts +13 -23
- package/dist/utils/requirement-diff.d.ts.map +1 -1
- package/dist/utils/requirement-diff.js +13 -23
- package/dist/utils/requirement-diff.js.map +1 -1
- package/dist/utils/spec-files.d.ts +7 -20
- package/dist/utils/spec-files.d.ts.map +1 -1
- package/dist/utils/spec-files.js +26 -51
- package/dist/utils/spec-files.js.map +1 -1
- package/dist/utils/task-progress.d.ts +11 -9
- package/dist/utils/task-progress.d.ts.map +1 -1
- package/dist/utils/task-progress.js +53 -32
- package/dist/utils/task-progress.js.map +1 -1
- package/dist/utils/timestamp.d.ts +5 -8
- package/dist/utils/timestamp.d.ts.map +1 -1
- package/dist/utils/timestamp.js +5 -8
- package/dist/utils/timestamp.js.map +1 -1
- package/package.json +2 -3
- package/schemas/decision/templates/decision.md +3 -1
- package/schemas/decision/templates/index.md +2 -2
- package/schemas/issue/schema.yaml +8 -1
- package/schemas/issue/templates/spec.md +27 -2
- package/schemas/sdd/schema.yaml +20 -1
- package/schemas/sdd/templates/spec.md +27 -2
- /package/assets/rules/tospec/{single-sourc-of-truth.md → single-source-of-truth.md} +0 -0
package/dist/core/init.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Init Command
|
|
3
|
-
*
|
|
4
|
-
* Sets up tospec Agent Skills for Claude Code and Codex.
|
|
5
|
-
*/
|
|
1
|
+
/** Sets up tospec Agent Skills for Claude Code and Codex. */
|
|
6
2
|
import path from 'path';
|
|
7
3
|
import * as fs from 'fs';
|
|
8
4
|
import { createRequire } from 'module';
|
|
@@ -12,109 +8,150 @@ import { AI_TOOLS, TOSPEC_DIR_NAME, fallbackToolIds, resolveToolSelection, } fro
|
|
|
12
8
|
import { isInteractive } from '../utils/interactive.js';
|
|
13
9
|
import { serializeConfig } from './config-prompts.js';
|
|
14
10
|
import { copyDir } from '../utils/link.js';
|
|
15
|
-
import { removeLegacySkillDirs, removeLegacyToolSkillLayout, getToolsWithSkillsDir, getToolStates, getSkillTemplates, writeSkillDir, writeToolCommands,
|
|
11
|
+
import { removeLegacySkillDirs, removeUnselectedSkillDirs, removeLegacyToolSkillLayout, getToolsWithSkillsDir, getToolStates, getSkillTemplates, writeSkillDir, writeToolCommands, findWorkflowReferenceGaps, describeWorkflowReferenceGaps, } from './shared/index.js';
|
|
16
12
|
import { getGlobalConfig } from './global-config.js';
|
|
17
13
|
import { runLegacyUserStateMigration } from './user-state-migration.js';
|
|
18
14
|
import { getProfileWorkflows, ALL_WORKFLOWS } from './profiles.js';
|
|
19
15
|
import { getAvailableTools } from './available-tools.js';
|
|
20
16
|
import { resolveConfigFileInDir } from './project-config.js';
|
|
21
17
|
import { planWorkflowRules, writeWorkflowRules } from './shared/rules-generation.js';
|
|
22
|
-
import { hasResidualCodexOutput, RESIDUAL_CODEX_NOTICE } from './codex-residue.js';
|
|
18
|
+
import { hasResidualCodexOutput, RESIDUAL_CODEX_NOTICE, RESIDUAL_CODEX_FIX } from './codex-residue.js';
|
|
19
|
+
import { emitSuccess } from '../commands/shared-output.js';
|
|
23
20
|
const require = createRequire(import.meta.url);
|
|
24
21
|
const { version: TOSPEC_VERSION } = require('../../package.json');
|
|
25
|
-
// -----------------------------------------------------------------------------
|
|
26
|
-
// Constants
|
|
27
|
-
// -----------------------------------------------------------------------------
|
|
28
22
|
const DEFAULT_SCHEMA = 'sdd';
|
|
29
|
-
/**
|
|
30
|
-
* The canonical skills directory every dependent target is copied from. Named
|
|
31
|
-
* once because two things now key off it — the loop that skips copying a
|
|
32
|
-
* directory onto itself, and the summary line that only applies to targets
|
|
33
|
-
* holding a copy.
|
|
34
|
-
*/
|
|
23
|
+
/** The canonical skills directory every dependent target is copied from. */
|
|
35
24
|
function agentsSkillsDirFor(projectPath) {
|
|
36
25
|
return path.join(projectPath, '.agents', 'skills');
|
|
37
26
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// -----------------------------------------------------------------------------
|
|
27
|
+
/** The `--json` null-shape; mirrors the success payload with `init` nulled. */
|
|
28
|
+
export const INIT_FAILURE_PAYLOAD = { init: null, root: null };
|
|
41
29
|
export class InitCommand {
|
|
42
30
|
toolsArg;
|
|
43
31
|
force;
|
|
44
32
|
interactiveOption;
|
|
45
33
|
profileOverride;
|
|
34
|
+
json;
|
|
46
35
|
constructor(options = {}) {
|
|
47
36
|
this.toolsArg = options.tools;
|
|
48
37
|
this.force = options.force ?? false;
|
|
49
38
|
this.interactiveOption = options.interactive;
|
|
50
39
|
this.profileOverride = options.profile;
|
|
40
|
+
this.json = options.json ?? false;
|
|
51
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Progress prose, suppressed under `--json`: the envelope contract allows only
|
|
44
|
+
* one document on stdout.
|
|
45
|
+
*/
|
|
46
|
+
say(...args) {
|
|
47
|
+
if (!this.json)
|
|
48
|
+
console.log(...args);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Unlike `say`, a warning has no other carrier — dropping it under `--json`
|
|
52
|
+
* would leave an agent caller less informed than a human.
|
|
53
|
+
*/
|
|
54
|
+
warn(code, message, fix) {
|
|
55
|
+
this.jsonWarnings.push({ severity: 'warning', code, message, ...(fix ? { fix } : {}) });
|
|
56
|
+
if (!this.json) {
|
|
57
|
+
console.log(`Warning: ${message}`);
|
|
58
|
+
if (fix)
|
|
59
|
+
console.log(fix);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Emitted alongside the success payload; empty means the key is omitted. */
|
|
63
|
+
jsonWarnings = [];
|
|
52
64
|
async execute(targetPath) {
|
|
53
65
|
const projectPath = path.resolve(targetPath);
|
|
54
66
|
const tospecDir = TOSPEC_DIR_NAME;
|
|
55
67
|
const tospecPath = path.join(projectPath, tospecDir);
|
|
56
68
|
// Detect without writing: even the permission probe must follow rule preflight.
|
|
57
69
|
const extendMode = await FileSystemUtils.directoryExists(tospecPath);
|
|
58
|
-
// Detect available tools in the project (task 7.1)
|
|
59
70
|
const detectedTools = getAvailableTools(projectPath);
|
|
60
|
-
// Show animated welcome screen (interactive mode only)
|
|
61
71
|
const canPrompt = this.canPromptInteractively();
|
|
62
72
|
if (canPrompt) {
|
|
63
73
|
const { showWelcomeScreen } = await import('../ui/welcome-screen.js');
|
|
64
74
|
await showWelcomeScreen();
|
|
65
75
|
}
|
|
66
|
-
//
|
|
67
|
-
//
|
|
76
|
+
// Validated early so an invalid value fails before tool setup; the resolved
|
|
77
|
+
// value is read again later, when generation needs the effective config.
|
|
68
78
|
this.resolveProfileOverride();
|
|
69
|
-
// Get tool states before processing
|
|
70
79
|
const toolStates = getToolStates(projectPath);
|
|
71
|
-
// Get tool selection (pass detected tools for pre-selection)
|
|
72
80
|
const selectedToolIds = await this.getSelectedTools(toolStates, extendMode, detectedTools, projectPath);
|
|
73
|
-
// Validate selected tools
|
|
74
81
|
const validatedTools = this.validateTools(selectedToolIds, toolStates);
|
|
75
82
|
const rulePlan = await planWorkflowRules(projectPath, validatedTools.map((tool) => tool.value));
|
|
76
83
|
// Migration remains before every getGlobalConfig(), but after conflict checks.
|
|
77
84
|
await runLegacyUserStateMigration();
|
|
78
|
-
await this.validate(projectPath
|
|
79
|
-
// The decision rule is always installed (only skipped when no agent dir exists).
|
|
80
|
-
const installRules = validatedTools.length > 0;
|
|
81
|
-
// Create directory structure and config
|
|
85
|
+
await this.validate(projectPath);
|
|
82
86
|
await this.createDirectoryStructure(tospecPath, extendMode);
|
|
83
|
-
//
|
|
87
|
+
// Must run before skill/command generation.
|
|
84
88
|
const configStatus = await this.createConfig(tospecPath, extendMode);
|
|
85
|
-
//
|
|
86
|
-
|
|
89
|
+
// A profile left at `custom` with nothing picked resolves to zero workflows,
|
|
90
|
+
// so init would report both tools `created` with no skill on disk. Raised
|
|
91
|
+
// before the write, and only when a tool was asked for — "no tools selected"
|
|
92
|
+
// has its own line and is not a profile problem.
|
|
93
|
+
const initConfig = getGlobalConfig();
|
|
94
|
+
const initProfile = this.profileOverride ?? initConfig.profile ?? 'core';
|
|
95
|
+
if (validatedTools.length > 0 &&
|
|
96
|
+
getProfileWorkflows(initProfile, initConfig.workflows).length === 0) {
|
|
97
|
+
this.warn('init_profile_selects_no_workflows', `Profile '${initProfile}' selects no workflows, so this init writes no tospec skills.`, 'Run "tospec config profile core" then "tospec init" to install them.');
|
|
98
|
+
}
|
|
99
|
+
// The same check `update` runs, for the same reason: the skills about to be
|
|
100
|
+
// written name their siblings in fixed prose, so a partial selection
|
|
101
|
+
// installs instructions that point at workflows this project will not have.
|
|
102
|
+
if (validatedTools.length > 0) {
|
|
103
|
+
const referenceGaps = findWorkflowReferenceGaps(getProfileWorkflows(initProfile, initConfig.workflows));
|
|
104
|
+
if (referenceGaps.length > 0) {
|
|
105
|
+
const missing = [...new Set(referenceGaps.map((gap) => gap.missing))].sort();
|
|
106
|
+
this.warn('init_workflow_reference_missing', `Installed skills point at workflows this profile leaves out: ${describeWorkflowReferenceGaps(referenceGaps)}.`, `Add them with "tospec config set workflows" (missing: ${missing.join(', ')}), or expect agents to hit instructions they cannot follow.`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const results = await this.generateSkillsAndCommands(projectPath, validatedTools);
|
|
87
110
|
const successfulIds = new Set([...results.createdTools, ...results.refreshedTools].map((tool) => tool.value));
|
|
88
|
-
await writeWorkflowRules(rulePlan.filter((entry) => successfulIds.has(entry.toolId))
|
|
89
|
-
|
|
111
|
+
await writeWorkflowRules(rulePlan.filter((entry) => successfulIds.has(entry.toolId)), {
|
|
112
|
+
silent: this.json,
|
|
113
|
+
});
|
|
90
114
|
this.displaySuccessMessage(projectPath, validatedTools, results, configStatus);
|
|
91
|
-
//
|
|
92
|
-
// advice about a state it did not reach
|
|
115
|
+
// After every write, so a run that dies in rule preflight never leaves
|
|
116
|
+
// advice about a state it did not reach; before the JSON envelope, or the
|
|
117
|
+
// warning is recorded into a document already on stdout.
|
|
93
118
|
if (hasResidualCodexOutput(projectPath)) {
|
|
94
|
-
|
|
119
|
+
this.warn('codex_residue', RESIDUAL_CODEX_NOTICE, RESIDUAL_CODEX_FIX);
|
|
120
|
+
}
|
|
121
|
+
if (this.json) {
|
|
122
|
+
const globalCfg = getGlobalConfig();
|
|
123
|
+
const activeProfile = this.profileOverride ?? globalCfg.profile ?? 'core';
|
|
124
|
+
const activeWorkflows = [...getProfileWorkflows(activeProfile, globalCfg.workflows)];
|
|
125
|
+
emitSuccess({
|
|
126
|
+
init: {
|
|
127
|
+
profile: activeProfile,
|
|
128
|
+
workflows: activeWorkflows,
|
|
129
|
+
created: results.createdTools.map((t) => ({ tool: t.value, dir: t.skillsDir })),
|
|
130
|
+
refreshed: results.refreshedTools.map((t) => ({ tool: t.value, dir: t.skillsDir })),
|
|
131
|
+
failed: results.failedTools.map((f) => ({ name: f.name, error: f.error.message })),
|
|
132
|
+
// `created` only records that a tool was configured, so this is the
|
|
133
|
+
// only field saying what was installed, and it must count what
|
|
134
|
+
// reached a tool directory: `--tools none` writes nothing anywhere.
|
|
135
|
+
skills: results.createdTools.length + results.refreshedTools.length > 0
|
|
136
|
+
? getSkillTemplates(activeWorkflows).length
|
|
137
|
+
: 0,
|
|
138
|
+
config: configStatus,
|
|
139
|
+
},
|
|
140
|
+
...(this.jsonWarnings.length ? { status: this.jsonWarnings } : {}),
|
|
141
|
+
}, { path: projectPath, source: 'explicit' });
|
|
95
142
|
}
|
|
96
143
|
}
|
|
97
|
-
|
|
98
|
-
// VALIDATION & SETUP
|
|
99
|
-
// ═══════════════════════════════════════════════════════════
|
|
100
|
-
async validate(projectPath, tospecPath) {
|
|
101
|
-
const extendMode = await FileSystemUtils.directoryExists(tospecPath);
|
|
102
|
-
// Check write permissions
|
|
144
|
+
async validate(projectPath) {
|
|
103
145
|
if (!(await FileSystemUtils.ensureWritePermissions(projectPath))) {
|
|
104
146
|
throw new Error(`Insufficient permissions to write to ${projectPath}`);
|
|
105
147
|
}
|
|
106
|
-
return extendMode;
|
|
107
148
|
}
|
|
108
149
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* the answer. Derived from the table so a future dependency documents itself.
|
|
150
|
+
* A row can arrive uninvited by being required by another, which has to be
|
|
151
|
+
* said *before* the prompt to inform the answer. Derived from the table so a
|
|
152
|
+
* future dependency documents itself.
|
|
113
153
|
*
|
|
114
|
-
*
|
|
115
|
-
* `--tools` or `--interactive=false` has no answer to inform, so the same
|
|
116
|
-
* lines there are just an unprompted paragraph about targets the user may not
|
|
117
|
-
* have picked — `tospec init --tools agents` explaining Claude Code.
|
|
154
|
+
* Prompt path only: a `--tools` run has no answer to inform.
|
|
118
155
|
*/
|
|
119
156
|
discloseToolDependencies() {
|
|
120
157
|
for (const tool of AI_TOOLS) {
|
|
@@ -127,7 +164,7 @@ export class InitCommand {
|
|
|
127
164
|
continue;
|
|
128
165
|
const names = required.map((candidate) => candidate.name).join(', ');
|
|
129
166
|
const dirs = required.map((candidate) => `${candidate.skillsDir}/skills/`).join(', ');
|
|
130
|
-
|
|
167
|
+
this.say(`${tool.name} copies its skills from ${dirs}, so selecting it also selects ${names}.`);
|
|
131
168
|
}
|
|
132
169
|
}
|
|
133
170
|
canPromptInteractively() {
|
|
@@ -146,11 +183,7 @@ export class InitCommand {
|
|
|
146
183
|
}
|
|
147
184
|
throw new Error(`Invalid profile "${this.profileOverride}". Available profiles: core, custom`);
|
|
148
185
|
}
|
|
149
|
-
// ═══════════════════════════════════════════════════════════
|
|
150
|
-
// TOOL SELECTION
|
|
151
|
-
// ═══════════════════════════════════════════════════════════
|
|
152
186
|
async getSelectedTools(toolStates, extendMode, detectedTools, projectPath) {
|
|
153
|
-
// Check for --tools flag first
|
|
154
187
|
const nonInteractiveSelection = this.resolveToolsArg();
|
|
155
188
|
if (nonInteractiveSelection !== null) {
|
|
156
189
|
return nonInteractiveSelection;
|
|
@@ -162,11 +195,9 @@ export class InitCommand {
|
|
|
162
195
|
.map(([toolId]) => toolId));
|
|
163
196
|
const shouldPreselectDetected = !extendMode && configuredToolIds.size === 0;
|
|
164
197
|
const canPrompt = this.canPromptInteractively();
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
// than leaving a first-time non-interactive setup with no skills at all.
|
|
169
|
-
// (An explicitly cleared menu *is* a decision, and writes nothing.)
|
|
198
|
+
// Detecting nothing is an absence of information, not a decision to write
|
|
199
|
+
// nothing, so it falls back to the base target. (A cleared menu *is* a
|
|
200
|
+
// decision.)
|
|
170
201
|
if (!canPrompt) {
|
|
171
202
|
const detected = [...detectedToolIds];
|
|
172
203
|
return resolveToolSelection(detected.length > 0 ? detected : fallbackToolIds());
|
|
@@ -174,14 +205,11 @@ export class InitCommand {
|
|
|
174
205
|
if (validTools.length === 0) {
|
|
175
206
|
throw new Error(`No tools available for skill generation.`);
|
|
176
207
|
}
|
|
177
|
-
// Interactive mode: show searchable multi-select
|
|
178
208
|
const { searchableMultiSelect } = await import('../prompts/searchable-multi-select.js');
|
|
179
|
-
// Every target with an output directory is offered, including the one
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
// before answering, not by a checkbox they cannot clear.
|
|
209
|
+
// Every target with an output directory is offered, including the one others
|
|
210
|
+
// copy from: what the list shows is what gets written, and a row pulled in
|
|
211
|
+
// by another comes from a disclosed expansion, not a locked checkbox.
|
|
183
212
|
const offeredTools = getToolsWithSkillsDir();
|
|
184
|
-
// Build choices: pre-select configured tools; keep detected tools visible but unselected.
|
|
185
213
|
const sortedChoices = offeredTools
|
|
186
214
|
.map((toolId) => {
|
|
187
215
|
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
@@ -197,7 +225,7 @@ export class InitCommand {
|
|
|
197
225
|
};
|
|
198
226
|
})
|
|
199
227
|
.sort((a, b) => {
|
|
200
|
-
// Configured
|
|
228
|
+
// Configured first, then detected-but-unconfigured, then the rest.
|
|
201
229
|
if (a.configured && !b.configured)
|
|
202
230
|
return -1;
|
|
203
231
|
if (!a.configured && b.configured)
|
|
@@ -212,7 +240,7 @@ export class InitCommand {
|
|
|
212
240
|
.filter((toolId) => configuredToolIds.has(toolId))
|
|
213
241
|
.map((toolId) => AI_TOOLS.find((t) => t.value === toolId)?.name || toolId);
|
|
214
242
|
if (configuredNames.length > 0) {
|
|
215
|
-
|
|
243
|
+
this.say(`tospec configured: ${configuredNames.join(', ')} (pre-selected)`);
|
|
216
244
|
}
|
|
217
245
|
const detectedOnlyNames = detectedTools
|
|
218
246
|
.filter((tool) => !configuredToolIds.has(tool.value) && offeredTools.includes(tool.value))
|
|
@@ -221,14 +249,13 @@ export class InitCommand {
|
|
|
221
249
|
const detectionLabel = shouldPreselectDetected
|
|
222
250
|
? 'pre-selected for first-time setup'
|
|
223
251
|
: 'not pre-selected';
|
|
224
|
-
|
|
252
|
+
this.say(`Detected tool directories: ${detectedOnlyNames.join(', ')} (${detectionLabel})`);
|
|
225
253
|
}
|
|
226
254
|
// Last thing before the question, so the dependency is on screen while it
|
|
227
255
|
// can still change the answer.
|
|
228
256
|
this.discloseToolDependencies();
|
|
229
|
-
// No minimum: clearing every checkbox means "write no agent content",
|
|
230
|
-
//
|
|
231
|
-
// honour, and honouring it partially would make the list a suggestion.
|
|
257
|
+
// No minimum: clearing every checkbox means "write no agent content", as
|
|
258
|
+
// `--tools none` does.
|
|
232
259
|
const selectedTools = await searchableMultiSelect({
|
|
233
260
|
message: `Select tools to set up (${offeredTools.length} available)`,
|
|
234
261
|
pageSize: 15,
|
|
@@ -270,7 +297,7 @@ export class InitCommand {
|
|
|
270
297
|
throw new Error(`Invalid tool(s): ${invalidTokens.join(', ')}. Available values: ${availableList}`);
|
|
271
298
|
}
|
|
272
299
|
// Expansion deduplicates and orders dependencies first, so naming a tool
|
|
273
|
-
// whose skills
|
|
300
|
+
// whose skills copy from another never produces a copy with no source.
|
|
274
301
|
return resolveToolSelection(normalizedTokens);
|
|
275
302
|
}
|
|
276
303
|
validateTools(toolIds, toolStates) {
|
|
@@ -295,20 +322,14 @@ export class InitCommand {
|
|
|
295
322
|
}
|
|
296
323
|
return validatedTools;
|
|
297
324
|
}
|
|
298
|
-
// ═══════════════════════════════════════════════════════════
|
|
299
|
-
// DIRECTORY STRUCTURE
|
|
300
|
-
// ═══════════════════════════════════════════════════════════
|
|
301
325
|
/**
|
|
302
|
-
* Anchoring runs on every init, not only the first
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* extendMode differs only in staying quiet — the work itself is identical, so
|
|
307
|
-
* it is a pair of guards rather than a second copy of the loop.
|
|
326
|
+
* Anchoring runs on every init, not only the first, so a project predating the
|
|
327
|
+
* rule reaches the same state without a migration step. `extendMode` differs
|
|
328
|
+
* only in staying quiet.
|
|
308
329
|
*/
|
|
309
330
|
async createDirectoryStructure(tospecPath, extendMode) {
|
|
310
331
|
if (!extendMode)
|
|
311
|
-
|
|
332
|
+
this.say('Creating tospec structure...');
|
|
312
333
|
const layout = tospecLayoutDirs(tospecPath);
|
|
313
334
|
for (const dir of [tospecPath, ...layout]) {
|
|
314
335
|
await FileSystemUtils.createDirectory(dir);
|
|
@@ -317,71 +338,56 @@ export class InitCommand {
|
|
|
317
338
|
await ensureDirectoryAnchor(dir);
|
|
318
339
|
}
|
|
319
340
|
if (!extendMode)
|
|
320
|
-
|
|
341
|
+
this.say('Created tospec structure');
|
|
321
342
|
}
|
|
322
|
-
|
|
323
|
-
// SKILL GENERATION
|
|
324
|
-
// ═══════════════════════════════════════════════════════════
|
|
325
|
-
async generateSkillsAndCommands(projectPath, tools, installRules) {
|
|
343
|
+
async generateSkillsAndCommands(projectPath, tools) {
|
|
326
344
|
const createdTools = [];
|
|
327
345
|
const refreshedTools = [];
|
|
328
346
|
const failedTools = [];
|
|
329
347
|
let removedCommandCount = 0;
|
|
330
|
-
// Read global config for profile settings (use --profile override if set)
|
|
331
348
|
const globalConfig = getGlobalConfig();
|
|
332
349
|
const profile = this.resolveProfileOverride() ?? globalConfig.profile ?? 'core';
|
|
333
350
|
const workflows = getProfileWorkflows(profile, globalConfig.workflows);
|
|
334
351
|
const skillTemplates = getSkillTemplates(workflows);
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
// Gated on the selection rather than unconditional, so an empty selection
|
|
339
|
-
// (and `--tools none`) really writes nothing. The condition is "any target
|
|
340
|
-
// at all" rather than "is .agents selected": dependency expansion already
|
|
341
|
-
// guarantees it is present whenever anything else is, and a selection that
|
|
342
|
-
// somehow omitted it would still need canonical content or its dependents
|
|
343
|
-
// would link at nothing.
|
|
352
|
+
// Canonical files, written once to .agents/. Gated on "any target at all" so
|
|
353
|
+
// `--tools none` writes nothing; dependency expansion guarantees .agents is
|
|
354
|
+
// present whenever anything else is.
|
|
344
355
|
const agentsSkillsDir = agentsSkillsDirFor(projectPath);
|
|
345
356
|
if (tools.length > 0) {
|
|
346
357
|
for (const { template, dirName } of skillTemplates) {
|
|
347
358
|
await writeSkillDir(agentsSkillsDir, dirName, template, TOSPEC_VERSION);
|
|
348
359
|
}
|
|
349
360
|
}
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
//
|
|
353
|
-
//
|
|
354
|
-
// which is the opposite of what they asked for.
|
|
361
|
+
// Both sweeps sit outside that gate: retiring a workflow is not conditional
|
|
362
|
+
// on this run writing anything. Gated, `--tools none` would leave a retired
|
|
363
|
+
// skill dir loadable by every agent reading .agents/skills/, and a dropped
|
|
364
|
+
// workflow would come back on the next copy from the canonical directory.
|
|
355
365
|
await removeLegacySkillDirs(agentsSkillsDir);
|
|
366
|
+
await removeUnselectedSkillDirs(agentsSkillsDir, workflows);
|
|
356
367
|
removedCommandCount += await this.removeLegacyCommands(projectPath);
|
|
357
|
-
// Process each tool
|
|
358
368
|
for (const tool of tools) {
|
|
359
|
-
|
|
369
|
+
this.say(`Setting up ${tool.name}...`);
|
|
360
370
|
try {
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
// so skills stay flat and prefixed:
|
|
364
|
-
// `<tool>/skills/tospec-<workflow>/SKILL.md`. Clean up the intermediate
|
|
365
|
-
// grouped/bare layouts (and the retired verify skill) from older builds first.
|
|
371
|
+
// Project-scope skills cannot live in a nested sub-folder, so they stay
|
|
372
|
+
// flat and prefixed: `<tool>/skills/tospec-<workflow>/SKILL.md`.
|
|
366
373
|
const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
|
|
367
374
|
await removeLegacyToolSkillLayout(path.join(projectPath, tool.skillsDir));
|
|
368
375
|
await removeLegacySkillDirs(skillsDir);
|
|
369
|
-
// Independent copies, not links: see the header of utils/link.ts.
|
|
370
|
-
//
|
|
371
|
-
//
|
|
376
|
+
// Independent copies, not links: see the header of utils/link.ts.
|
|
377
|
+
// copyDir clears its dest, so copying .agents/skills onto itself would
|
|
378
|
+
// delete the source.
|
|
372
379
|
if (path.resolve(skillsDir) !== path.resolve(agentsSkillsDir)) {
|
|
373
380
|
for (const { dirName } of skillTemplates) {
|
|
374
381
|
await copyDir(path.join(agentsSkillsDir, dirName), path.join(skillsDir, dirName));
|
|
375
382
|
}
|
|
376
383
|
}
|
|
377
|
-
//
|
|
378
|
-
//
|
|
384
|
+
// Per-tool prune. The canonical sweep above cannot reach a copy that a
|
|
385
|
+
// previous, wider profile left in this tool's directory.
|
|
386
|
+
await removeUnselectedSkillDirs(skillsDir, workflows);
|
|
387
|
+
// Tools with a registered adapter (Claude Code) get
|
|
388
|
+
// `.claude/commands/tosx/<id>.md`, invokable as `/tosx:<id>`.
|
|
379
389
|
await writeToolCommands(projectPath, tool.value, workflows);
|
|
380
|
-
|
|
381
|
-
if (installRules) {
|
|
382
|
-
await writeToolRules(projectPath, tool.skillsDir);
|
|
383
|
-
}
|
|
384
|
-
console.log(`Setup complete for ${tool.name}`);
|
|
390
|
+
this.say(`Setup complete for ${tool.name}`);
|
|
385
391
|
if (tool.wasConfigured) {
|
|
386
392
|
refreshedTools.push(tool);
|
|
387
393
|
}
|
|
@@ -390,7 +396,7 @@ export class InitCommand {
|
|
|
390
396
|
}
|
|
391
397
|
}
|
|
392
398
|
catch (error) {
|
|
393
|
-
|
|
399
|
+
this.say(`Failed for ${tool.name}`);
|
|
394
400
|
failedTools.push({ name: tool.name, error: error });
|
|
395
401
|
}
|
|
396
402
|
}
|
|
@@ -401,9 +407,6 @@ export class InitCommand {
|
|
|
401
407
|
removedCommandCount,
|
|
402
408
|
};
|
|
403
409
|
}
|
|
404
|
-
// ═══════════════════════════════════════════════════════════
|
|
405
|
-
// CONFIG FILE
|
|
406
|
-
// ═══════════════════════════════════════════════════════════
|
|
407
410
|
async createConfig(tospecPath, extendMode) {
|
|
408
411
|
const configPath = path.join(tospecPath, 'config.yaml');
|
|
409
412
|
if (resolveConfigFileInDir(tospecPath)) {
|
|
@@ -418,102 +421,86 @@ export class InitCommand {
|
|
|
418
421
|
return 'skipped';
|
|
419
422
|
}
|
|
420
423
|
}
|
|
421
|
-
// ═══════════════════════════════════════════════════════════
|
|
422
|
-
// UI & OUTPUT
|
|
423
|
-
// ═══════════════════════════════════════════════════════════
|
|
424
424
|
displaySuccessMessage(projectPath, tools, results, configStatus) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
425
|
+
const globalConfig = getGlobalConfig();
|
|
426
|
+
const activeProfile = this.profileOverride ?? globalConfig.profile ?? 'core';
|
|
427
|
+
const activeWorkflows = [...getProfileWorkflows(activeProfile, globalConfig.workflows)];
|
|
428
|
+
this.say();
|
|
429
|
+
this.say('tospec Setup Complete');
|
|
430
|
+
this.say();
|
|
429
431
|
if (results.createdTools.length > 0) {
|
|
430
|
-
|
|
432
|
+
this.say(`Created: ${results.createdTools.map((t) => t.name).join(', ')}`);
|
|
431
433
|
}
|
|
432
434
|
if (results.refreshedTools.length > 0) {
|
|
433
|
-
|
|
435
|
+
this.say(`Refreshed: ${results.refreshedTools.map((t) => t.name).join(', ')}`);
|
|
434
436
|
}
|
|
435
|
-
//
|
|
436
|
-
//
|
|
437
|
-
// selection, which left the run printing the "Setup Complete" banner and a
|
|
438
|
-
// config line with no statement anywhere that no skills exist — the user
|
|
439
|
-
// finds out by looking for them.
|
|
437
|
+
// Every block below is keyed off a non-empty selection, so an empty one
|
|
438
|
+
// needs its own line saying no skills exist.
|
|
440
439
|
if (tools.length === 0) {
|
|
441
|
-
|
|
440
|
+
this.say('No tools selected: no skills, commands or rules were written.');
|
|
442
441
|
}
|
|
443
|
-
// Show counts (respecting profile filter)
|
|
444
442
|
const successfulTools = [...results.createdTools, ...results.refreshedTools];
|
|
445
443
|
if (successfulTools.length > 0) {
|
|
446
|
-
const globalConfig = getGlobalConfig();
|
|
447
|
-
const profile = this.profileOverride ?? globalConfig.profile ?? 'core';
|
|
448
|
-
const workflows = getProfileWorkflows(profile, globalConfig.workflows);
|
|
449
444
|
// Trailing slash per directory, not once at the end: with more than one
|
|
450
445
|
// target ".agents, .claude/" reads as a single odd path.
|
|
451
446
|
const toolDirs = [...new Set(successfulTools.map((t) => `${t.skillsDir}/`))].join(', ');
|
|
452
|
-
const skillCount = getSkillTemplates(
|
|
453
|
-
|
|
447
|
+
const skillCount = getSkillTemplates(activeWorkflows).length;
|
|
448
|
+
this.say(`${skillCount} skills in ${toolDirs}`);
|
|
454
449
|
// Dependent targets hold independent copies, so an edit to the canonical
|
|
455
|
-
// skill does not reach them
|
|
456
|
-
//
|
|
450
|
+
// skill does not reach them. Only when something was copied: under an
|
|
451
|
+
// empty profile this would claim a copy from a directory holding none.
|
|
457
452
|
const dependents = successfulTools.filter((t) => path.resolve(projectPath, t.skillsDir, 'skills') !== path.resolve(agentsSkillsDirFor(projectPath)));
|
|
458
|
-
if (dependents.length > 0) {
|
|
459
|
-
|
|
453
|
+
if (skillCount > 0 && dependents.length > 0) {
|
|
454
|
+
this.say(`Copied from .agents/skills/: ${dependents.map((t) => t.name).join(', ')} — rerun 'tospec update' after editing the canonical skills`);
|
|
460
455
|
}
|
|
461
456
|
}
|
|
462
|
-
// Show failures
|
|
463
457
|
if (results.failedTools.length > 0) {
|
|
464
|
-
|
|
458
|
+
this.say(`Failed: ${results.failedTools.map((f) => `${f.name} (${f.error.message})`).join(', ')}`);
|
|
465
459
|
}
|
|
466
460
|
if (results.removedCommandCount > 0) {
|
|
467
|
-
|
|
461
|
+
this.say(`Removed: ${results.removedCommandCount} legacy command entries`);
|
|
468
462
|
}
|
|
469
|
-
// Config status
|
|
470
463
|
if (configStatus === 'created') {
|
|
471
|
-
|
|
464
|
+
this.say(`Config: tospec/config.yaml (schema: ${DEFAULT_SCHEMA})`);
|
|
472
465
|
}
|
|
473
466
|
else if (configStatus === 'exists') {
|
|
474
|
-
//
|
|
467
|
+
// Name the file that is actually there: config.yaml or config.yml.
|
|
475
468
|
const configFile = resolveConfigFileInDir(path.join(projectPath, TOSPEC_DIR_NAME));
|
|
476
469
|
const configName = configFile ? path.basename(configFile) : 'config.yaml';
|
|
477
|
-
|
|
470
|
+
this.say(`Config: tospec/${configName} (exists)`);
|
|
478
471
|
}
|
|
479
472
|
else {
|
|
480
|
-
|
|
473
|
+
this.say(`Config: skipped (non-interactive mode)`);
|
|
481
474
|
}
|
|
482
|
-
|
|
483
|
-
const globalCfg = getGlobalConfig();
|
|
484
|
-
const activeProfile = this.profileOverride ?? globalCfg.profile ?? 'core';
|
|
485
|
-
const activeWorkflows = [...getProfileWorkflows(activeProfile, globalCfg.workflows)];
|
|
486
|
-
console.log();
|
|
475
|
+
this.say();
|
|
487
476
|
if (tools.length === 0) {
|
|
488
|
-
// `tospec-propose` is a skill
|
|
489
|
-
// would
|
|
490
|
-
|
|
491
|
-
|
|
477
|
+
// `tospec-propose` is a skill and this run wrote none, so advising it
|
|
478
|
+
// would name something not on disk.
|
|
479
|
+
this.say('Getting started:');
|
|
480
|
+
this.say(" Add agent skills: tospec init --tools <tool> (or rerun 'tospec init')");
|
|
492
481
|
}
|
|
493
482
|
else if (activeWorkflows.includes('propose')) {
|
|
494
|
-
|
|
495
|
-
|
|
483
|
+
this.say('Getting started:');
|
|
484
|
+
this.say(' Start your first change: tospec-propose "your idea"');
|
|
496
485
|
}
|
|
497
486
|
else {
|
|
498
|
-
|
|
487
|
+
this.say("Done. Run 'tospec config profile' to configure your workflows.");
|
|
499
488
|
}
|
|
500
|
-
// Restart instruction if any tools were configured
|
|
501
489
|
if (results.createdTools.length > 0 || results.refreshedTools.length > 0) {
|
|
502
|
-
|
|
503
|
-
|
|
490
|
+
this.say();
|
|
491
|
+
this.say('Restart your agent for skills to take effect.');
|
|
504
492
|
}
|
|
505
|
-
|
|
493
|
+
this.say();
|
|
506
494
|
}
|
|
507
495
|
async removeLegacyCommands(projectPath) {
|
|
508
496
|
let removed = 0;
|
|
509
|
-
// Commands
|
|
510
|
-
//
|
|
511
|
-
// the old `.claude/commands/tospec/` namespace this replaced.
|
|
497
|
+
// Commands live under `.claude/commands/tosx/` now (writeToolCommands);
|
|
498
|
+
// these are the locations it replaced.
|
|
512
499
|
const legacyPaths = [
|
|
513
500
|
path.join(projectPath, '.agents', 'commands', 'tospec'),
|
|
514
501
|
path.join(projectPath, '.claude', 'commands', 'tospec'),
|
|
515
|
-
// Cursor support was removed; this
|
|
516
|
-
//
|
|
502
|
+
// Cursor support was removed; this stays so stale .cursor command files
|
|
503
|
+
// are still swept away.
|
|
517
504
|
...ALL_WORKFLOWS.map((workflow) => path.join(projectPath, '.cursor', 'commands', `tospec-${workflow}.md`)),
|
|
518
505
|
];
|
|
519
506
|
for (const legacyPath of legacyPaths) {
|