@seanmars/tospec 0.19.0-beta.0 → 0.19.0-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +578 -0
- package/README.md +71 -77
- package/assets/dashboard/app.js +14 -3
- package/assets/dashboard/style.css +7 -0
- package/assets/metrics/app.js +22 -0
- package/assets/metrics/style.css +8 -0
- package/assets/rules/tospec/decision.md +3 -0
- package/assets/rules/tospec/single-source-of-truth.md +19 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +266 -82
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/config.d.ts +9 -17
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +374 -95
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/dashboard.d.ts +64 -81
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +350 -218
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/decision.d.ts +41 -19
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +400 -70
- package/dist/commands/decision.js.map +1 -1
- package/dist/commands/metrics.d.ts +34 -48
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +74 -84
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/shared-output.d.ts +24 -10
- package/dist/commands/shared-output.d.ts.map +1 -1
- package/dist/commands/shared-output.js +62 -11
- package/dist/commands/shared-output.js.map +1 -1
- package/dist/commands/show.d.ts +7 -0
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +39 -8
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/validate.d.ts +47 -30
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +282 -108
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/workflow/index.d.ts +6 -10
- package/dist/commands/workflow/index.d.ts.map +1 -1
- package/dist/commands/workflow/index.js +6 -10
- package/dist/commands/workflow/index.js.map +1 -1
- package/dist/commands/workflow/instructions.d.ts +21 -8
- package/dist/commands/workflow/instructions.d.ts.map +1 -1
- package/dist/commands/workflow/instructions.js +254 -95
- package/dist/commands/workflow/instructions.js.map +1 -1
- package/dist/commands/workflow/new-change.d.ts +4 -5
- package/dist/commands/workflow/new-change.d.ts.map +1 -1
- package/dist/commands/workflow/new-change.js +90 -25
- package/dist/commands/workflow/new-change.js.map +1 -1
- package/dist/commands/workflow/schemas.d.ts +3 -5
- package/dist/commands/workflow/schemas.d.ts.map +1 -1
- package/dist/commands/workflow/schemas.js +37 -11
- package/dist/commands/workflow/schemas.js.map +1 -1
- package/dist/commands/workflow/shared.d.ts +48 -21
- package/dist/commands/workflow/shared.d.ts.map +1 -1
- package/dist/commands/workflow/shared.js +36 -33
- package/dist/commands/workflow/shared.js.map +1 -1
- package/dist/commands/workflow/status.d.ts +10 -6
- package/dist/commands/workflow/status.d.ts.map +1 -1
- package/dist/commands/workflow/status.js +84 -42
- package/dist/commands/workflow/status.js.map +1 -1
- package/dist/commands/workflow/templates.d.ts +10 -3
- package/dist/commands/workflow/templates.d.ts.map +1 -1
- package/dist/commands/workflow/templates.js +39 -40
- package/dist/commands/workflow/templates.js.map +1 -1
- package/dist/core/archive.d.ts +26 -21
- package/dist/core/archive.d.ts.map +1 -1
- package/dist/core/archive.js +415 -201
- package/dist/core/archive.js.map +1 -1
- package/dist/core/artifact-graph/graph.d.ts +29 -36
- package/dist/core/artifact-graph/graph.d.ts.map +1 -1
- package/dist/core/artifact-graph/graph.js +50 -58
- package/dist/core/artifact-graph/graph.js.map +1 -1
- package/dist/core/artifact-graph/index.d.ts +2 -2
- package/dist/core/artifact-graph/index.d.ts.map +1 -1
- package/dist/core/artifact-graph/index.js +2 -2
- package/dist/core/artifact-graph/index.js.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +105 -100
- package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.js +176 -110
- package/dist/core/artifact-graph/instruction-loader.js.map +1 -1
- package/dist/core/artifact-graph/outputs.d.ts +13 -6
- package/dist/core/artifact-graph/outputs.d.ts.map +1 -1
- package/dist/core/artifact-graph/outputs.js +146 -8
- package/dist/core/artifact-graph/outputs.js.map +1 -1
- package/dist/core/artifact-graph/resolver.d.ts +44 -63
- package/dist/core/artifact-graph/resolver.d.ts.map +1 -1
- package/dist/core/artifact-graph/resolver.js +85 -86
- package/dist/core/artifact-graph/resolver.js.map +1 -1
- package/dist/core/artifact-graph/schema.d.ts +0 -6
- package/dist/core/artifact-graph/schema.d.ts.map +1 -1
- package/dist/core/artifact-graph/schema.js +7 -32
- package/dist/core/artifact-graph/schema.js.map +1 -1
- package/dist/core/artifact-graph/state.d.ts +1 -8
- package/dist/core/artifact-graph/state.d.ts.map +1 -1
- package/dist/core/artifact-graph/state.js +2 -17
- package/dist/core/artifact-graph/state.js.map +1 -1
- package/dist/core/artifact-graph/stub-detection.d.ts +12 -0
- package/dist/core/artifact-graph/stub-detection.d.ts.map +1 -0
- package/dist/core/artifact-graph/stub-detection.js +39 -0
- package/dist/core/artifact-graph/stub-detection.js.map +1 -0
- package/dist/core/artifact-graph/types.d.ts +4 -0
- package/dist/core/artifact-graph/types.d.ts.map +1 -1
- package/dist/core/artifact-graph/types.js +30 -10
- package/dist/core/artifact-graph/types.js.map +1 -1
- package/dist/core/available-tools.d.ts +3 -12
- package/dist/core/available-tools.d.ts.map +1 -1
- package/dist/core/available-tools.js +4 -13
- package/dist/core/available-tools.js.map +1 -1
- package/dist/core/change-metadata/schema.d.ts +1 -1
- package/dist/core/change-metadata/schema.d.ts.map +1 -1
- package/dist/core/change-metadata/schema.js +10 -7
- package/dist/core/change-metadata/schema.js.map +1 -1
- package/dist/core/change-presenter.d.ts +23 -18
- package/dist/core/change-presenter.d.ts.map +1 -1
- package/dist/core/change-presenter.js +102 -43
- package/dist/core/change-presenter.js.map +1 -1
- package/dist/core/change-status-policy.d.ts +8 -1
- package/dist/core/change-status-policy.d.ts.map +1 -1
- package/dist/core/change-status-policy.js +25 -1
- package/dist/core/change-status-policy.js.map +1 -1
- package/dist/core/codex-metrics.d.ts +25 -45
- package/dist/core/codex-metrics.d.ts.map +1 -1
- package/dist/core/codex-metrics.js +44 -88
- package/dist/core/codex-metrics.js.map +1 -1
- package/dist/core/codex-residue.d.ts +22 -0
- package/dist/core/codex-residue.d.ts.map +1 -0
- package/dist/core/codex-residue.js +61 -0
- package/dist/core/codex-residue.js.map +1 -0
- package/dist/core/command-generation/adapters/claude.d.ts +2 -9
- package/dist/core/command-generation/adapters/claude.d.ts.map +1 -1
- package/dist/core/command-generation/adapters/claude.js +2 -12
- package/dist/core/command-generation/adapters/claude.js.map +1 -1
- package/dist/core/command-generation/adapters/index.d.ts +1 -9
- package/dist/core/command-generation/adapters/index.d.ts.map +1 -1
- package/dist/core/command-generation/adapters/index.js +1 -9
- package/dist/core/command-generation/adapters/index.js.map +1 -1
- package/dist/core/command-generation/generator.d.ts +0 -17
- package/dist/core/command-generation/generator.d.ts.map +1 -1
- package/dist/core/command-generation/generator.js +0 -17
- package/dist/core/command-generation/generator.js.map +1 -1
- package/dist/core/command-generation/index.d.ts +2 -5
- package/dist/core/command-generation/index.d.ts.map +1 -1
- package/dist/core/command-generation/index.js +0 -9
- package/dist/core/command-generation/index.js.map +1 -1
- package/dist/core/command-generation/types.d.ts +10 -36
- package/dist/core/command-generation/types.d.ts.map +1 -1
- package/dist/core/command-generation/types.js +0 -6
- package/dist/core/command-generation/types.js.map +1 -1
- package/dist/core/command-generation/yaml.d.ts +3 -18
- package/dist/core/command-generation/yaml.d.ts.map +1 -1
- package/dist/core/command-generation/yaml.js +5 -23
- package/dist/core/command-generation/yaml.js.map +1 -1
- package/dist/core/config-prompts.d.ts +2 -4
- package/dist/core/config-prompts.d.ts.map +1 -1
- package/dist/core/config-prompts.js +2 -7
- package/dist/core/config-prompts.js.map +1 -1
- package/dist/core/config-schema.d.ts +8 -53
- package/dist/core/config-schema.d.ts.map +1 -1
- package/dist/core/config-schema.js +49 -62
- package/dist/core/config-schema.js.map +1 -1
- package/dist/core/config.d.ts +56 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +73 -2
- package/dist/core/config.js.map +1 -1
- package/dist/core/converters/json-converter.d.ts.map +1 -1
- package/dist/core/dashboard-activity.d.ts +7 -9
- package/dist/core/dashboard-activity.d.ts.map +1 -1
- package/dist/core/dashboard-activity.js +26 -24
- package/dist/core/dashboard-activity.js.map +1 -1
- package/dist/core/dashboard-data.d.ts +40 -22
- package/dist/core/dashboard-data.d.ts.map +1 -1
- package/dist/core/dashboard-data.js +84 -68
- package/dist/core/dashboard-data.js.map +1 -1
- package/dist/core/global-config.d.ts +24 -53
- package/dist/core/global-config.d.ts.map +1 -1
- package/dist/core/global-config.js +43 -62
- package/dist/core/global-config.js.map +1 -1
- package/dist/core/init.d.ts +29 -11
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +232 -164
- package/dist/core/init.js.map +1 -1
- package/dist/core/list.d.ts +1 -1
- package/dist/core/list.d.ts.map +1 -1
- package/dist/core/list.js +121 -28
- package/dist/core/list.js.map +1 -1
- package/dist/core/local-server.d.ts +63 -39
- package/dist/core/local-server.d.ts.map +1 -1
- package/dist/core/local-server.js +99 -53
- package/dist/core/local-server.js.map +1 -1
- package/dist/core/markdown-render.d.ts +25 -0
- package/dist/core/markdown-render.d.ts.map +1 -0
- package/dist/core/markdown-render.js +94 -0
- package/dist/core/markdown-render.js.map +1 -0
- package/dist/core/migrate.d.ts +32 -15
- package/dist/core/migrate.d.ts.map +1 -1
- package/dist/core/migrate.js +220 -108
- package/dist/core/migrate.js.map +1 -1
- package/dist/core/parsers/change-parser.d.ts +7 -10
- package/dist/core/parsers/change-parser.d.ts.map +1 -1
- package/dist/core/parsers/change-parser.js +48 -56
- package/dist/core/parsers/change-parser.js.map +1 -1
- package/dist/core/parsers/markdown-parser.d.ts +8 -9
- package/dist/core/parsers/markdown-parser.d.ts.map +1 -1
- package/dist/core/parsers/markdown-parser.js +31 -22
- package/dist/core/parsers/markdown-parser.js.map +1 -1
- package/dist/core/parsers/requirement-blocks.d.ts +53 -11
- package/dist/core/parsers/requirement-blocks.d.ts.map +1 -1
- package/dist/core/parsers/requirement-blocks.js +200 -60
- package/dist/core/parsers/requirement-blocks.js.map +1 -1
- package/dist/core/parsers/requirement-text.d.ts +73 -79
- package/dist/core/parsers/requirement-text.d.ts.map +1 -1
- package/dist/core/parsers/requirement-text.js +137 -79
- package/dist/core/parsers/requirement-text.js.map +1 -1
- package/dist/core/parsers/spec-structure.d.ts +1 -1
- package/dist/core/parsers/spec-structure.d.ts.map +1 -1
- package/dist/core/parsers/spec-structure.js +30 -3
- package/dist/core/parsers/spec-structure.js.map +1 -1
- package/dist/core/planning-home.js.map +1 -1
- package/dist/core/profiles.d.ts +3 -10
- package/dist/core/profiles.d.ts.map +1 -1
- package/dist/core/profiles.js +5 -12
- package/dist/core/profiles.js.map +1 -1
- package/dist/core/project-config.d.ts +43 -44
- package/dist/core/project-config.d.ts.map +1 -1
- package/dist/core/project-config.js +107 -82
- package/dist/core/project-config.js.map +1 -1
- package/dist/core/project-layout.d.ts +10 -18
- package/dist/core/project-layout.d.ts.map +1 -1
- package/dist/core/project-layout.js +16 -26
- package/dist/core/project-layout.js.map +1 -1
- package/dist/core/root-selection.d.ts +11 -7
- package/dist/core/root-selection.d.ts.map +1 -1
- package/dist/core/root-selection.js +7 -8
- package/dist/core/root-selection.js.map +1 -1
- package/dist/core/rules.d.ts +10 -0
- package/dist/core/rules.d.ts.map +1 -0
- package/dist/core/rules.js +43 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/schema-names.d.ts +16 -0
- package/dist/core/schema-names.d.ts.map +1 -0
- package/dist/core/schema-names.js +16 -0
- package/dist/core/schema-names.js.map +1 -0
- package/dist/core/schemas/base.schema.d.ts +3 -0
- package/dist/core/schemas/base.schema.d.ts.map +1 -1
- package/dist/core/schemas/base.schema.js +22 -6
- package/dist/core/schemas/base.schema.js.map +1 -1
- package/dist/core/schemas/change.schema.d.ts +16 -0
- package/dist/core/schemas/change.schema.d.ts.map +1 -1
- package/dist/core/schemas/change.schema.js +41 -10
- package/dist/core/schemas/change.schema.js.map +1 -1
- package/dist/core/schemas/spec.schema.d.ts +2 -0
- package/dist/core/schemas/spec.schema.d.ts.map +1 -1
- package/dist/core/shared/index.d.ts +3 -8
- package/dist/core/shared/index.d.ts.map +1 -1
- package/dist/core/shared/index.js +3 -8
- package/dist/core/shared/index.js.map +1 -1
- package/dist/core/shared/rules-generation.d.ts +27 -8
- package/dist/core/shared/rules-generation.d.ts.map +1 -1
- package/dist/core/shared/rules-generation.js +151 -16
- package/dist/core/shared/rules-generation.js.map +1 -1
- package/dist/core/shared/skill-generation.d.ts +38 -53
- package/dist/core/shared/skill-generation.d.ts.map +1 -1
- package/dist/core/shared/skill-generation.js +82 -51
- package/dist/core/shared/skill-generation.js.map +1 -1
- package/dist/core/shared/tool-detection.d.ts +40 -62
- package/dist/core/shared/tool-detection.d.ts.map +1 -1
- package/dist/core/shared/tool-detection.js +88 -80
- package/dist/core/shared/tool-detection.js.map +1 -1
- package/dist/core/skill-metrics.d.ts +36 -63
- package/dist/core/skill-metrics.d.ts.map +1 -1
- package/dist/core/skill-metrics.js +34 -73
- package/dist/core/skill-metrics.js.map +1 -1
- package/dist/core/spec-presenter.d.ts.map +1 -1
- package/dist/core/spec-presenter.js +6 -6
- package/dist/core/spec-presenter.js.map +1 -1
- package/dist/core/specs-apply.d.ts +24 -23
- package/dist/core/specs-apply.d.ts.map +1 -1
- package/dist/core/specs-apply.js +188 -173
- package/dist/core/specs-apply.js.map +1 -1
- package/dist/core/templates/fragments/interview.d.ts +2 -6
- package/dist/core/templates/fragments/interview.d.ts.map +1 -1
- package/dist/core/templates/fragments/interview.js +2 -6
- package/dist/core/templates/fragments/interview.js.map +1 -1
- package/dist/core/templates/fragments/next-step.d.ts +4 -8
- package/dist/core/templates/fragments/next-step.d.ts.map +1 -1
- package/dist/core/templates/fragments/next-step.js +4 -8
- package/dist/core/templates/fragments/next-step.js.map +1 -1
- package/dist/core/templates/fragments/verify.d.ts +9 -12
- package/dist/core/templates/fragments/verify.d.ts.map +1 -1
- package/dist/core/templates/fragments/verify.js +9 -12
- package/dist/core/templates/fragments/verify.js.map +1 -1
- package/dist/core/templates/index.d.ts +0 -6
- package/dist/core/templates/index.d.ts.map +1 -1
- package/dist/core/templates/index.js +0 -7
- package/dist/core/templates/index.js.map +1 -1
- package/dist/core/templates/skill-templates.d.ts +1 -5
- package/dist/core/templates/skill-templates.d.ts.map +1 -1
- package/dist/core/templates/skill-templates.js +0 -5
- package/dist/core/templates/skill-templates.js.map +1 -1
- package/dist/core/templates/types.d.ts +3 -7
- package/dist/core/templates/types.d.ts.map +1 -1
- package/dist/core/templates/types.js +0 -3
- package/dist/core/templates/types.js.map +1 -1
- package/dist/core/templates/workflows/apply.d.ts +3 -9
- package/dist/core/templates/workflows/apply.d.ts.map +1 -1
- package/dist/core/templates/workflows/apply.js +9 -12
- package/dist/core/templates/workflows/apply.js.map +1 -1
- package/dist/core/templates/workflows/archive.d.ts +0 -6
- package/dist/core/templates/workflows/archive.d.ts.map +1 -1
- package/dist/core/templates/workflows/archive.js +7 -5
- package/dist/core/templates/workflows/archive.js.map +1 -1
- package/dist/core/templates/workflows/decision.js +4 -4
- package/dist/core/templates/workflows/decision.js.map +1 -1
- package/dist/core/templates/workflows/explore.js +1 -1
- package/dist/core/templates/workflows/grill.d.ts.map +1 -1
- package/dist/core/templates/workflows/grill.js +0 -2
- package/dist/core/templates/workflows/grill.js.map +1 -1
- package/dist/core/templates/workflows/issue.d.ts +0 -6
- package/dist/core/templates/workflows/issue.d.ts.map +1 -1
- package/dist/core/templates/workflows/issue.js +3 -0
- package/dist/core/templates/workflows/issue.js.map +1 -1
- package/dist/core/templates/workflows/propose.d.ts +0 -6
- package/dist/core/templates/workflows/propose.d.ts.map +1 -1
- package/dist/core/templates/workflows/propose.js +0 -1
- package/dist/core/templates/workflows/propose.js.map +1 -1
- package/dist/core/templates/workflows/sync.d.ts +2 -8
- package/dist/core/templates/workflows/sync.d.ts.map +1 -1
- package/dist/core/templates/workflows/sync.js +2 -2
- package/dist/core/templates/workflows/sync.js.map +1 -1
- package/dist/core/templates/workflows/update.d.ts +0 -6
- package/dist/core/templates/workflows/update.d.ts.map +1 -1
- package/dist/core/templates/workflows/update.js.map +1 -1
- package/dist/core/update.d.ts +26 -21
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +165 -116
- package/dist/core/update.js.map +1 -1
- package/dist/core/user-state-migration.d.ts +13 -15
- package/dist/core/user-state-migration.d.ts.map +1 -1
- package/dist/core/user-state-migration.js +16 -20
- package/dist/core/user-state-migration.js.map +1 -1
- package/dist/core/validation/constants.d.ts +19 -25
- package/dist/core/validation/constants.d.ts.map +1 -1
- package/dist/core/validation/constants.js +25 -20
- package/dist/core/validation/constants.js.map +1 -1
- package/dist/core/validation/prose-length.d.ts +15 -0
- package/dist/core/validation/prose-length.d.ts.map +1 -0
- package/dist/core/validation/prose-length.js +29 -0
- package/dist/core/validation/prose-length.js.map +1 -0
- package/dist/core/validation/purpose-placeholder.d.ts +9 -16
- package/dist/core/validation/purpose-placeholder.d.ts.map +1 -1
- package/dist/core/validation/purpose-placeholder.js +30 -44
- package/dist/core/validation/purpose-placeholder.js.map +1 -1
- package/dist/core/validation/section-validator.d.ts +4 -4
- package/dist/core/validation/section-validator.d.ts.map +1 -1
- package/dist/core/validation/section-validator.js +43 -7
- package/dist/core/validation/section-validator.js.map +1 -1
- package/dist/core/validation/task-numbering.d.ts +6 -3
- package/dist/core/validation/task-numbering.d.ts.map +1 -1
- package/dist/core/validation/task-numbering.js +23 -11
- package/dist/core/validation/task-numbering.js.map +1 -1
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.d.ts.map +1 -1
- package/dist/core/validation/types.js +12 -1
- package/dist/core/validation/types.js.map +1 -1
- package/dist/core/validation/validator.d.ts +50 -51
- package/dist/core/validation/validator.d.ts.map +1 -1
- package/dist/core/validation/validator.js +486 -263
- package/dist/core/validation/validator.js.map +1 -1
- package/dist/prompts/searchable-multi-select.d.ts +3 -8
- package/dist/prompts/searchable-multi-select.d.ts.map +1 -1
- package/dist/prompts/searchable-multi-select.js +16 -39
- package/dist/prompts/searchable-multi-select.js.map +1 -1
- package/dist/utils/change-metadata.d.ts +11 -50
- package/dist/utils/change-metadata.d.ts.map +1 -1
- package/dist/utils/change-metadata.js +48 -67
- package/dist/utils/change-metadata.js.map +1 -1
- package/dist/utils/change-utils.d.ts +31 -54
- package/dist/utils/change-utils.d.ts.map +1 -1
- package/dist/utils/change-utils.js +143 -100
- package/dist/utils/change-utils.js.map +1 -1
- package/dist/utils/file-lock.d.ts +39 -0
- package/dist/utils/file-lock.d.ts.map +1 -0
- package/dist/utils/file-lock.js +149 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/dist/utils/file-system.d.ts +12 -32
- package/dist/utils/file-system.d.ts.map +1 -1
- package/dist/utils/file-system.js +16 -40
- package/dist/utils/file-system.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +7 -11
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +11 -11
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/interactive.d.ts +4 -9
- package/dist/utils/interactive.d.ts.map +1 -1
- package/dist/utils/interactive.js +2 -4
- package/dist/utils/interactive.js.map +1 -1
- package/dist/utils/item-discovery.d.ts +15 -10
- package/dist/utils/item-discovery.d.ts.map +1 -1
- package/dist/utils/item-discovery.js +42 -47
- package/dist/utils/item-discovery.js.map +1 -1
- package/dist/utils/link.d.ts +13 -4
- package/dist/utils/link.d.ts.map +1 -1
- package/dist/utils/link.js +13 -4
- package/dist/utils/link.js.map +1 -1
- package/dist/utils/match.js.map +1 -1
- package/dist/utils/requirement-diff.d.ts +13 -23
- package/dist/utils/requirement-diff.d.ts.map +1 -1
- package/dist/utils/requirement-diff.js +13 -23
- package/dist/utils/requirement-diff.js.map +1 -1
- package/dist/utils/spec-files.d.ts +10 -11
- package/dist/utils/spec-files.d.ts.map +1 -1
- package/dist/utils/spec-files.js +31 -22
- package/dist/utils/spec-files.js.map +1 -1
- package/dist/utils/task-progress.d.ts +11 -9
- package/dist/utils/task-progress.d.ts.map +1 -1
- package/dist/utils/task-progress.js +53 -32
- package/dist/utils/task-progress.js.map +1 -1
- package/dist/utils/timestamp.d.ts +5 -8
- package/dist/utils/timestamp.d.ts.map +1 -1
- package/dist/utils/timestamp.js +5 -8
- package/dist/utils/timestamp.js.map +1 -1
- package/package.json +9 -10
- package/schemas/decision/templates/decision.md +3 -1
- package/schemas/decision/templates/index.md +2 -2
- package/schemas/issue/schema.yaml +11 -2
- package/schemas/issue/templates/spec.md +37 -3
- package/schemas/sdd/schema.yaml +24 -1
- package/schemas/sdd/templates/spec.md +37 -3
package/dist/core/init.js
CHANGED
|
@@ -1,90 +1,171 @@
|
|
|
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';
|
|
9
5
|
import { FileSystemUtils } from '../utils/file-system.js';
|
|
10
6
|
import { ensureDirectoryAnchor, tospecLayoutDirs } from './project-layout.js';
|
|
11
|
-
import { AI_TOOLS, TOSPEC_DIR_NAME, } from './config.js';
|
|
7
|
+
import { AI_TOOLS, TOSPEC_DIR_NAME, fallbackToolIds, resolveToolSelection, } from './config.js';
|
|
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';
|
|
17
|
+
import { planWorkflowRules, writeWorkflowRules } from './shared/rules-generation.js';
|
|
18
|
+
import { hasResidualCodexOutput, RESIDUAL_CODEX_NOTICE, RESIDUAL_CODEX_FIX } from './codex-residue.js';
|
|
19
|
+
import { emitSuccess } from '../commands/shared-output.js';
|
|
21
20
|
const require = createRequire(import.meta.url);
|
|
22
21
|
const { version: TOSPEC_VERSION } = require('../../package.json');
|
|
23
|
-
// -----------------------------------------------------------------------------
|
|
24
|
-
// Constants
|
|
25
|
-
// -----------------------------------------------------------------------------
|
|
26
22
|
const DEFAULT_SCHEMA = 'sdd';
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
/** The canonical skills directory every dependent target is copied from. */
|
|
24
|
+
function agentsSkillsDirFor(projectPath) {
|
|
25
|
+
return path.join(projectPath, '.agents', 'skills');
|
|
26
|
+
}
|
|
27
|
+
/** The `--json` null-shape; mirrors the success payload with `init` nulled. */
|
|
28
|
+
export const INIT_FAILURE_PAYLOAD = { init: null, root: null };
|
|
30
29
|
export class InitCommand {
|
|
31
30
|
toolsArg;
|
|
32
31
|
force;
|
|
33
32
|
interactiveOption;
|
|
34
33
|
profileOverride;
|
|
34
|
+
json;
|
|
35
35
|
constructor(options = {}) {
|
|
36
36
|
this.toolsArg = options.tools;
|
|
37
37
|
this.force = options.force ?? false;
|
|
38
38
|
this.interactiveOption = options.interactive;
|
|
39
39
|
this.profileOverride = options.profile;
|
|
40
|
+
this.json = options.json ?? false;
|
|
40
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 = [];
|
|
41
64
|
async execute(targetPath) {
|
|
42
|
-
// Above every getGlobalConfig() below, so this run reads the migrated file
|
|
43
|
-
// rather than the legacy fallback.
|
|
44
|
-
await runLegacyUserStateMigration();
|
|
45
65
|
const projectPath = path.resolve(targetPath);
|
|
46
66
|
const tospecDir = TOSPEC_DIR_NAME;
|
|
47
67
|
const tospecPath = path.join(projectPath, tospecDir);
|
|
48
|
-
//
|
|
49
|
-
const extendMode = await
|
|
50
|
-
// Detect available tools in the project (task 7.1)
|
|
68
|
+
// Detect without writing: even the permission probe must follow rule preflight.
|
|
69
|
+
const extendMode = await FileSystemUtils.directoryExists(tospecPath);
|
|
51
70
|
const detectedTools = getAvailableTools(projectPath);
|
|
52
|
-
// Show animated welcome screen (interactive mode only)
|
|
53
71
|
const canPrompt = this.canPromptInteractively();
|
|
54
72
|
if (canPrompt) {
|
|
55
73
|
const { showWelcomeScreen } = await import('../ui/welcome-screen.js');
|
|
56
74
|
await showWelcomeScreen();
|
|
57
75
|
}
|
|
58
|
-
//
|
|
59
|
-
//
|
|
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.
|
|
60
78
|
this.resolveProfileOverride();
|
|
61
|
-
// Get tool states before processing
|
|
62
79
|
const toolStates = getToolStates(projectPath);
|
|
63
|
-
// Get tool selection (pass detected tools for pre-selection)
|
|
64
80
|
const selectedToolIds = await this.getSelectedTools(toolStates, extendMode, detectedTools, projectPath);
|
|
65
|
-
// Validate selected tools
|
|
66
81
|
const validatedTools = this.validateTools(selectedToolIds, toolStates);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
const rulePlan = await planWorkflowRules(projectPath, validatedTools.map((tool) => tool.value));
|
|
83
|
+
// Migration remains before every getGlobalConfig(), but after conflict checks.
|
|
84
|
+
await runLegacyUserStateMigration();
|
|
85
|
+
await this.validate(projectPath);
|
|
70
86
|
await this.createDirectoryStructure(tospecPath, extendMode);
|
|
71
|
-
//
|
|
87
|
+
// Must run before skill/command generation.
|
|
72
88
|
const configStatus = await this.createConfig(tospecPath, extendMode);
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
//
|
|
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);
|
|
110
|
+
const successfulIds = new Set([...results.createdTools, ...results.refreshedTools].map((tool) => tool.value));
|
|
111
|
+
await writeWorkflowRules(rulePlan.filter((entry) => successfulIds.has(entry.toolId)), {
|
|
112
|
+
silent: this.json,
|
|
113
|
+
});
|
|
76
114
|
this.displaySuccessMessage(projectPath, validatedTools, results, configStatus);
|
|
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.
|
|
118
|
+
if (hasResidualCodexOutput(projectPath)) {
|
|
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' });
|
|
142
|
+
}
|
|
77
143
|
}
|
|
78
|
-
|
|
79
|
-
// VALIDATION & SETUP
|
|
80
|
-
// ═══════════════════════════════════════════════════════════
|
|
81
|
-
async validate(projectPath, tospecPath) {
|
|
82
|
-
const extendMode = await FileSystemUtils.directoryExists(tospecPath);
|
|
83
|
-
// Check write permissions
|
|
144
|
+
async validate(projectPath) {
|
|
84
145
|
if (!(await FileSystemUtils.ensureWritePermissions(projectPath))) {
|
|
85
146
|
throw new Error(`Insufficient permissions to write to ${projectPath}`);
|
|
86
147
|
}
|
|
87
|
-
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
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.
|
|
153
|
+
*
|
|
154
|
+
* Prompt path only: a `--tools` run has no answer to inform.
|
|
155
|
+
*/
|
|
156
|
+
discloseToolDependencies() {
|
|
157
|
+
for (const tool of AI_TOOLS) {
|
|
158
|
+
if (!tool.requires?.length)
|
|
159
|
+
continue;
|
|
160
|
+
const required = tool.requires
|
|
161
|
+
.map((id) => AI_TOOLS.find((candidate) => candidate.value === id))
|
|
162
|
+
.filter((candidate) => Boolean(candidate));
|
|
163
|
+
if (required.length === 0)
|
|
164
|
+
continue;
|
|
165
|
+
const names = required.map((candidate) => candidate.name).join(', ');
|
|
166
|
+
const dirs = required.map((candidate) => `${candidate.skillsDir}/skills/`).join(', ');
|
|
167
|
+
this.say(`${tool.name} copies its skills from ${dirs}, so selecting it also selects ${names}.`);
|
|
168
|
+
}
|
|
88
169
|
}
|
|
89
170
|
canPromptInteractively() {
|
|
90
171
|
if (this.interactiveOption === false)
|
|
@@ -102,11 +183,7 @@ export class InitCommand {
|
|
|
102
183
|
}
|
|
103
184
|
throw new Error(`Invalid profile "${this.profileOverride}". Available profiles: core, custom`);
|
|
104
185
|
}
|
|
105
|
-
// ═══════════════════════════════════════════════════════════
|
|
106
|
-
// TOOL SELECTION
|
|
107
|
-
// ═══════════════════════════════════════════════════════════
|
|
108
186
|
async getSelectedTools(toolStates, extendMode, detectedTools, projectPath) {
|
|
109
|
-
// Check for --tools flag first
|
|
110
187
|
const nonInteractiveSelection = this.resolveToolsArg();
|
|
111
188
|
if (nonInteractiveSelection !== null) {
|
|
112
189
|
return nonInteractiveSelection;
|
|
@@ -118,20 +195,22 @@ export class InitCommand {
|
|
|
118
195
|
.map(([toolId]) => toolId));
|
|
119
196
|
const shouldPreselectDetected = !extendMode && configuredToolIds.size === 0;
|
|
120
197
|
const canPrompt = this.canPromptInteractively();
|
|
121
|
-
//
|
|
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.)
|
|
122
201
|
if (!canPrompt) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
throw new Error(`No tools detected and no --tools flag provided. Valid tools:\n ${validTools.join('\n ')}\n\nUse --tools all, --tools none, or --tools claude,codex.`);
|
|
202
|
+
const detected = [...detectedToolIds];
|
|
203
|
+
return resolveToolSelection(detected.length > 0 ? detected : fallbackToolIds());
|
|
127
204
|
}
|
|
128
205
|
if (validTools.length === 0) {
|
|
129
206
|
throw new Error(`No tools available for skill generation.`);
|
|
130
207
|
}
|
|
131
|
-
// Interactive mode: show searchable multi-select
|
|
132
208
|
const { searchableMultiSelect } = await import('../prompts/searchable-multi-select.js');
|
|
133
|
-
//
|
|
134
|
-
|
|
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.
|
|
212
|
+
const offeredTools = getToolsWithSkillsDir();
|
|
213
|
+
const sortedChoices = offeredTools
|
|
135
214
|
.map((toolId) => {
|
|
136
215
|
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
137
216
|
const status = toolStates.get(toolId);
|
|
@@ -146,7 +225,7 @@ export class InitCommand {
|
|
|
146
225
|
};
|
|
147
226
|
})
|
|
148
227
|
.sort((a, b) => {
|
|
149
|
-
// Configured
|
|
228
|
+
// Configured first, then detected-but-unconfigured, then the rest.
|
|
150
229
|
if (a.configured && !b.configured)
|
|
151
230
|
return -1;
|
|
152
231
|
if (!a.configured && b.configured)
|
|
@@ -157,31 +236,32 @@ export class InitCommand {
|
|
|
157
236
|
return 1;
|
|
158
237
|
return 0;
|
|
159
238
|
});
|
|
160
|
-
const configuredNames =
|
|
239
|
+
const configuredNames = offeredTools
|
|
161
240
|
.filter((toolId) => configuredToolIds.has(toolId))
|
|
162
241
|
.map((toolId) => AI_TOOLS.find((t) => t.value === toolId)?.name || toolId);
|
|
163
242
|
if (configuredNames.length > 0) {
|
|
164
|
-
|
|
243
|
+
this.say(`tospec configured: ${configuredNames.join(', ')} (pre-selected)`);
|
|
165
244
|
}
|
|
166
245
|
const detectedOnlyNames = detectedTools
|
|
167
|
-
.filter((tool) => !configuredToolIds.has(tool.value))
|
|
246
|
+
.filter((tool) => !configuredToolIds.has(tool.value) && offeredTools.includes(tool.value))
|
|
168
247
|
.map((tool) => tool.name);
|
|
169
248
|
if (detectedOnlyNames.length > 0) {
|
|
170
249
|
const detectionLabel = shouldPreselectDetected
|
|
171
250
|
? 'pre-selected for first-time setup'
|
|
172
251
|
: 'not pre-selected';
|
|
173
|
-
|
|
252
|
+
this.say(`Detected tool directories: ${detectedOnlyNames.join(', ')} (${detectionLabel})`);
|
|
174
253
|
}
|
|
254
|
+
// Last thing before the question, so the dependency is on screen while it
|
|
255
|
+
// can still change the answer.
|
|
256
|
+
this.discloseToolDependencies();
|
|
257
|
+
// No minimum: clearing every checkbox means "write no agent content", as
|
|
258
|
+
// `--tools none` does.
|
|
175
259
|
const selectedTools = await searchableMultiSelect({
|
|
176
|
-
message: `Select tools to set up (${
|
|
260
|
+
message: `Select tools to set up (${offeredTools.length} available)`,
|
|
177
261
|
pageSize: 15,
|
|
178
262
|
choices: sortedChoices,
|
|
179
|
-
validate: (selected) => selected.length > 0 || 'Select at least one tool',
|
|
180
263
|
});
|
|
181
|
-
|
|
182
|
-
throw new Error('At least one tool must be selected');
|
|
183
|
-
}
|
|
184
|
-
return selectedTools;
|
|
264
|
+
return resolveToolSelection(selectedTools);
|
|
185
265
|
}
|
|
186
266
|
resolveToolsArg() {
|
|
187
267
|
if (typeof this.toolsArg === 'undefined') {
|
|
@@ -196,7 +276,7 @@ export class InitCommand {
|
|
|
196
276
|
const availableList = ['all', 'none', ...availableTools].join(', ');
|
|
197
277
|
const lowerRaw = raw.toLowerCase();
|
|
198
278
|
if (lowerRaw === 'all') {
|
|
199
|
-
return availableTools;
|
|
279
|
+
return resolveToolSelection(availableTools);
|
|
200
280
|
}
|
|
201
281
|
if (lowerRaw === 'none') {
|
|
202
282
|
return [];
|
|
@@ -216,14 +296,9 @@ export class InitCommand {
|
|
|
216
296
|
if (invalidTokens.length > 0) {
|
|
217
297
|
throw new Error(`Invalid tool(s): ${invalidTokens.join(', ')}. Available values: ${availableList}`);
|
|
218
298
|
}
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (!deduped.includes(token)) {
|
|
223
|
-
deduped.push(token);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return deduped;
|
|
299
|
+
// Expansion deduplicates and orders dependencies first, so naming a tool
|
|
300
|
+
// whose skills copy from another never produces a copy with no source.
|
|
301
|
+
return resolveToolSelection(normalizedTokens);
|
|
227
302
|
}
|
|
228
303
|
validateTools(toolIds, toolStates) {
|
|
229
304
|
const validatedTools = [];
|
|
@@ -247,20 +322,14 @@ export class InitCommand {
|
|
|
247
322
|
}
|
|
248
323
|
return validatedTools;
|
|
249
324
|
}
|
|
250
|
-
// ═══════════════════════════════════════════════════════════
|
|
251
|
-
// DIRECTORY STRUCTURE
|
|
252
|
-
// ═══════════════════════════════════════════════════════════
|
|
253
325
|
/**
|
|
254
|
-
* Anchoring runs on every init, not only the first
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
* extendMode differs only in staying quiet — the work itself is identical, so
|
|
259
|
-
* 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.
|
|
260
329
|
*/
|
|
261
330
|
async createDirectoryStructure(tospecPath, extendMode) {
|
|
262
331
|
if (!extendMode)
|
|
263
|
-
|
|
332
|
+
this.say('Creating tospec structure...');
|
|
264
333
|
const layout = tospecLayoutDirs(tospecPath);
|
|
265
334
|
for (const dir of [tospecPath, ...layout]) {
|
|
266
335
|
await FileSystemUtils.createDirectory(dir);
|
|
@@ -269,58 +338,56 @@ export class InitCommand {
|
|
|
269
338
|
await ensureDirectoryAnchor(dir);
|
|
270
339
|
}
|
|
271
340
|
if (!extendMode)
|
|
272
|
-
|
|
341
|
+
this.say('Created tospec structure');
|
|
273
342
|
}
|
|
274
|
-
|
|
275
|
-
// SKILL GENERATION
|
|
276
|
-
// ═══════════════════════════════════════════════════════════
|
|
277
|
-
async generateSkillsAndCommands(projectPath, tools, installRules) {
|
|
343
|
+
async generateSkillsAndCommands(projectPath, tools) {
|
|
278
344
|
const createdTools = [];
|
|
279
345
|
const refreshedTools = [];
|
|
280
346
|
const failedTools = [];
|
|
281
347
|
let removedCommandCount = 0;
|
|
282
|
-
// Read global config for profile settings (use --profile override if set)
|
|
283
348
|
const globalConfig = getGlobalConfig();
|
|
284
349
|
const profile = this.resolveProfileOverride() ?? globalConfig.profile ?? 'core';
|
|
285
350
|
const workflows = getProfileWorkflows(profile, globalConfig.workflows);
|
|
286
351
|
const skillTemplates = getSkillTemplates(workflows);
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
//
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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.
|
|
355
|
+
const agentsSkillsDir = agentsSkillsDirFor(projectPath);
|
|
356
|
+
if (tools.length > 0) {
|
|
357
|
+
for (const { template, dirName } of skillTemplates) {
|
|
358
|
+
await writeSkillDir(agentsSkillsDir, dirName, template, TOSPEC_VERSION);
|
|
359
|
+
}
|
|
294
360
|
}
|
|
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.
|
|
295
365
|
await removeLegacySkillDirs(agentsSkillsDir);
|
|
366
|
+
await removeUnselectedSkillDirs(agentsSkillsDir, workflows);
|
|
296
367
|
removedCommandCount += await this.removeLegacyCommands(projectPath);
|
|
297
|
-
// Process each tool
|
|
298
368
|
for (const tool of tools) {
|
|
299
|
-
|
|
369
|
+
this.say(`Setting up ${tool.name}...`);
|
|
300
370
|
try {
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
// sub-folder, so skills stay flat and prefixed:
|
|
304
|
-
// `<tool>/skills/tospec-<workflow>/SKILL.md`. Clean up the intermediate
|
|
305
|
-
// 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`.
|
|
306
373
|
const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
|
|
307
374
|
await removeLegacyToolSkillLayout(path.join(projectPath, tool.skillsDir));
|
|
308
375
|
await removeLegacySkillDirs(skillsDir);
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
|
|
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.
|
|
379
|
+
if (path.resolve(skillsDir) !== path.resolve(agentsSkillsDir)) {
|
|
380
|
+
for (const { dirName } of skillTemplates) {
|
|
381
|
+
await copyDir(path.join(agentsSkillsDir, dirName), path.join(skillsDir, dirName));
|
|
382
|
+
}
|
|
315
383
|
}
|
|
316
|
-
//
|
|
317
|
-
//
|
|
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>`.
|
|
318
389
|
await writeToolCommands(projectPath, tool.value, workflows);
|
|
319
|
-
|
|
320
|
-
if (installRules) {
|
|
321
|
-
await writeToolRules(projectPath, tool.skillsDir);
|
|
322
|
-
}
|
|
323
|
-
console.log(`Setup complete for ${tool.name}`);
|
|
390
|
+
this.say(`Setup complete for ${tool.name}`);
|
|
324
391
|
if (tool.wasConfigured) {
|
|
325
392
|
refreshedTools.push(tool);
|
|
326
393
|
}
|
|
@@ -329,7 +396,7 @@ export class InitCommand {
|
|
|
329
396
|
}
|
|
330
397
|
}
|
|
331
398
|
catch (error) {
|
|
332
|
-
|
|
399
|
+
this.say(`Failed for ${tool.name}`);
|
|
333
400
|
failedTools.push({ name: tool.name, error: error });
|
|
334
401
|
}
|
|
335
402
|
}
|
|
@@ -340,9 +407,6 @@ export class InitCommand {
|
|
|
340
407
|
removedCommandCount,
|
|
341
408
|
};
|
|
342
409
|
}
|
|
343
|
-
// ═══════════════════════════════════════════════════════════
|
|
344
|
-
// CONFIG FILE
|
|
345
|
-
// ═══════════════════════════════════════════════════════════
|
|
346
410
|
async createConfig(tospecPath, extendMode) {
|
|
347
411
|
const configPath = path.join(tospecPath, 'config.yaml');
|
|
348
412
|
if (resolveConfigFileInDir(tospecPath)) {
|
|
@@ -357,82 +421,86 @@ export class InitCommand {
|
|
|
357
421
|
return 'skipped';
|
|
358
422
|
}
|
|
359
423
|
}
|
|
360
|
-
// ═══════════════════════════════════════════════════════════
|
|
361
|
-
// UI & OUTPUT
|
|
362
|
-
// ═══════════════════════════════════════════════════════════
|
|
363
424
|
displaySuccessMessage(projectPath, tools, results, configStatus) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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();
|
|
368
431
|
if (results.createdTools.length > 0) {
|
|
369
|
-
|
|
432
|
+
this.say(`Created: ${results.createdTools.map((t) => t.name).join(', ')}`);
|
|
370
433
|
}
|
|
371
434
|
if (results.refreshedTools.length > 0) {
|
|
372
|
-
|
|
435
|
+
this.say(`Refreshed: ${results.refreshedTools.map((t) => t.name).join(', ')}`);
|
|
436
|
+
}
|
|
437
|
+
// Every block below is keyed off a non-empty selection, so an empty one
|
|
438
|
+
// needs its own line saying no skills exist.
|
|
439
|
+
if (tools.length === 0) {
|
|
440
|
+
this.say('No tools selected: no skills, commands or rules were written.');
|
|
373
441
|
}
|
|
374
|
-
// Show counts (respecting profile filter)
|
|
375
442
|
const successfulTools = [...results.createdTools, ...results.refreshedTools];
|
|
376
443
|
if (successfulTools.length > 0) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
444
|
+
// Trailing slash per directory, not once at the end: with more than one
|
|
445
|
+
// target ".agents, .claude/" reads as a single odd path.
|
|
446
|
+
const toolDirs = [...new Set(successfulTools.map((t) => `${t.skillsDir}/`))].join(', ');
|
|
447
|
+
const skillCount = getSkillTemplates(activeWorkflows).length;
|
|
448
|
+
this.say(`${skillCount} skills in ${toolDirs}`);
|
|
449
|
+
// Dependent targets hold independent copies, so an edit to the canonical
|
|
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.
|
|
452
|
+
const dependents = successfulTools.filter((t) => path.resolve(projectPath, t.skillsDir, 'skills') !== path.resolve(agentsSkillsDirFor(projectPath)));
|
|
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`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
385
457
|
if (results.failedTools.length > 0) {
|
|
386
|
-
|
|
458
|
+
this.say(`Failed: ${results.failedTools.map((f) => `${f.name} (${f.error.message})`).join(', ')}`);
|
|
387
459
|
}
|
|
388
460
|
if (results.removedCommandCount > 0) {
|
|
389
|
-
|
|
461
|
+
this.say(`Removed: ${results.removedCommandCount} legacy command entries`);
|
|
390
462
|
}
|
|
391
|
-
// Config status
|
|
392
463
|
if (configStatus === 'created') {
|
|
393
|
-
|
|
464
|
+
this.say(`Config: tospec/config.yaml (schema: ${DEFAULT_SCHEMA})`);
|
|
394
465
|
}
|
|
395
466
|
else if (configStatus === 'exists') {
|
|
396
|
-
//
|
|
467
|
+
// Name the file that is actually there: config.yaml or config.yml.
|
|
397
468
|
const configFile = resolveConfigFileInDir(path.join(projectPath, TOSPEC_DIR_NAME));
|
|
398
469
|
const configName = configFile ? path.basename(configFile) : 'config.yaml';
|
|
399
|
-
|
|
470
|
+
this.say(`Config: tospec/${configName} (exists)`);
|
|
400
471
|
}
|
|
401
472
|
else {
|
|
402
|
-
|
|
473
|
+
this.say(`Config: skipped (non-interactive mode)`);
|
|
474
|
+
}
|
|
475
|
+
this.say();
|
|
476
|
+
if (tools.length === 0) {
|
|
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')");
|
|
403
481
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const activeWorkflows = [...getProfileWorkflows(activeProfile, globalCfg.workflows)];
|
|
408
|
-
console.log();
|
|
409
|
-
if (activeWorkflows.includes('propose')) {
|
|
410
|
-
console.log('Getting started:');
|
|
411
|
-
console.log(' Start your first change: tospec-propose "your idea"');
|
|
482
|
+
else if (activeWorkflows.includes('propose')) {
|
|
483
|
+
this.say('Getting started:');
|
|
484
|
+
this.say(' Start your first change: tospec-propose "your idea"');
|
|
412
485
|
}
|
|
413
486
|
else {
|
|
414
|
-
|
|
487
|
+
this.say("Done. Run 'tospec config profile' to configure your workflows.");
|
|
415
488
|
}
|
|
416
|
-
// Links
|
|
417
|
-
console.log();
|
|
418
|
-
console.log(`Documentation: README.md`);
|
|
419
|
-
// Restart instruction if any tools were configured
|
|
420
489
|
if (results.createdTools.length > 0 || results.refreshedTools.length > 0) {
|
|
421
|
-
|
|
422
|
-
|
|
490
|
+
this.say();
|
|
491
|
+
this.say('Restart your agent for skills to take effect.');
|
|
423
492
|
}
|
|
424
|
-
|
|
493
|
+
this.say();
|
|
425
494
|
}
|
|
426
495
|
async removeLegacyCommands(projectPath) {
|
|
427
496
|
let removed = 0;
|
|
428
|
-
// Commands
|
|
429
|
-
//
|
|
430
|
-
// the old `.claude/commands/tospec/` namespace this replaced.
|
|
497
|
+
// Commands live under `.claude/commands/tosx/` now (writeToolCommands);
|
|
498
|
+
// these are the locations it replaced.
|
|
431
499
|
const legacyPaths = [
|
|
432
500
|
path.join(projectPath, '.agents', 'commands', 'tospec'),
|
|
433
501
|
path.join(projectPath, '.claude', 'commands', 'tospec'),
|
|
434
|
-
// Cursor support was removed; this
|
|
435
|
-
//
|
|
502
|
+
// Cursor support was removed; this stays so stale .cursor command files
|
|
503
|
+
// are still swept away.
|
|
436
504
|
...ALL_WORKFLOWS.map((workflow) => path.join(projectPath, '.cursor', 'commands', `tospec-${workflow}.md`)),
|
|
437
505
|
];
|
|
438
506
|
for (const legacyPath of legacyPaths) {
|