@dedesfr/prompter 0.6.15 → 0.7.1
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 +14 -0
- package/dist/cli/index.js +144 -12
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/archive.d.ts +11 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/archive.js +280 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/change.d.ts +35 -0
- package/dist/commands/change.d.ts.map +1 -0
- package/dist/commands/change.js +277 -0
- package/dist/commands/change.js.map +1 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +198 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +21 -5
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts +2 -2
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +153 -47
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/show.d.ts +14 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +132 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/spec.d.ts +15 -0
- package/dist/commands/spec.d.ts.map +1 -0
- package/dist/commands/spec.js +225 -0
- package/dist/commands/spec.js.map +1 -0
- package/dist/commands/validate.d.ts +24 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +294 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/core/artifact-graph/graph.d.ts +56 -0
- package/dist/core/artifact-graph/graph.d.ts.map +1 -0
- package/dist/core/artifact-graph/graph.js +141 -0
- package/dist/core/artifact-graph/graph.js.map +1 -0
- package/dist/core/artifact-graph/index.d.ts +7 -0
- package/dist/core/artifact-graph/index.d.ts.map +1 -0
- package/dist/core/artifact-graph/index.js +13 -0
- package/dist/core/artifact-graph/index.js.map +1 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +130 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -0
- package/dist/core/artifact-graph/instruction-loader.js +173 -0
- package/dist/core/artifact-graph/instruction-loader.js.map +1 -0
- package/dist/core/artifact-graph/resolver.d.ts +61 -0
- package/dist/core/artifact-graph/resolver.d.ts.map +1 -0
- package/dist/core/artifact-graph/resolver.js +187 -0
- package/dist/core/artifact-graph/resolver.js.map +1 -0
- package/dist/core/artifact-graph/schema.d.ts +13 -0
- package/dist/core/artifact-graph/schema.d.ts.map +1 -0
- package/dist/core/artifact-graph/schema.js +108 -0
- package/dist/core/artifact-graph/schema.js.map +1 -0
- package/dist/core/artifact-graph/state.d.ts +12 -0
- package/dist/core/artifact-graph/state.d.ts.map +1 -0
- package/dist/core/artifact-graph/state.js +54 -0
- package/dist/core/artifact-graph/state.js.map +1 -0
- package/dist/core/artifact-graph/types.d.ts +45 -0
- package/dist/core/artifact-graph/types.d.ts.map +1 -0
- package/dist/core/artifact-graph/types.js +43 -0
- package/dist/core/artifact-graph/types.js.map +1 -0
- package/dist/core/completions/command-registry.d.ts +7 -0
- package/dist/core/completions/command-registry.d.ts.map +1 -0
- package/dist/core/completions/command-registry.js +380 -0
- package/dist/core/completions/command-registry.js.map +1 -0
- package/dist/core/completions/completion-provider.d.ts +60 -0
- package/dist/core/completions/completion-provider.d.ts.map +1 -0
- package/dist/core/completions/completion-provider.js +102 -0
- package/dist/core/completions/completion-provider.js.map +1 -0
- package/dist/core/completions/generators/bash-generator.d.ts +32 -0
- package/dist/core/completions/generators/bash-generator.d.ts.map +1 -0
- package/dist/core/completions/generators/bash-generator.js +174 -0
- package/dist/core/completions/generators/bash-generator.js.map +1 -0
- package/dist/core/completions/generators/fish-generator.d.ts +32 -0
- package/dist/core/completions/generators/fish-generator.d.ts.map +1 -0
- package/dist/core/completions/generators/fish-generator.js +157 -0
- package/dist/core/completions/generators/fish-generator.js.map +1 -0
- package/dist/core/completions/generators/powershell-generator.d.ts +33 -0
- package/dist/core/completions/generators/powershell-generator.d.ts.map +1 -0
- package/dist/core/completions/generators/powershell-generator.js +207 -0
- package/dist/core/completions/generators/powershell-generator.js.map +1 -0
- package/dist/core/completions/generators/zsh-generator.d.ts +44 -0
- package/dist/core/completions/generators/zsh-generator.d.ts.map +1 -0
- package/dist/core/completions/generators/zsh-generator.js +250 -0
- package/dist/core/completions/generators/zsh-generator.js.map +1 -0
- package/dist/core/completions/templates/bash-templates.d.ts +6 -0
- package/dist/core/completions/templates/bash-templates.d.ts.map +1 -0
- package/dist/core/completions/templates/bash-templates.js +24 -0
- package/dist/core/completions/templates/bash-templates.js.map +1 -0
- package/dist/core/completions/templates/fish-templates.d.ts +7 -0
- package/dist/core/completions/templates/fish-templates.d.ts.map +1 -0
- package/dist/core/completions/templates/fish-templates.js +39 -0
- package/dist/core/completions/templates/fish-templates.js.map +1 -0
- package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
- package/dist/core/completions/templates/powershell-templates.d.ts.map +1 -0
- package/dist/core/completions/templates/powershell-templates.js +25 -0
- package/dist/core/completions/templates/powershell-templates.js.map +1 -0
- package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
- package/dist/core/completions/templates/zsh-templates.d.ts.map +1 -0
- package/dist/core/completions/templates/zsh-templates.js +36 -0
- package/dist/core/completions/templates/zsh-templates.js.map +1 -0
- package/dist/core/completions/types.d.ts +78 -0
- package/dist/core/completions/types.d.ts.map +1 -0
- package/dist/core/completions/types.js +2 -0
- package/dist/core/completions/types.js.map +1 -0
- package/dist/core/config-schema.d.ts +76 -0
- package/dist/core/config-schema.d.ts.map +1 -0
- package/dist/core/config-schema.js +200 -0
- package/dist/core/config-schema.js.map +1 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +18 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/configurators/slash/antigravity.d.ts.map +1 -1
- package/dist/core/configurators/slash/antigravity.js +6 -0
- package/dist/core/configurators/slash/antigravity.js.map +1 -1
- package/dist/core/configurators/slash/base.js +1 -1
- package/dist/core/configurators/slash/base.js.map +1 -1
- package/dist/core/configurators/slash/claude.d.ts.map +1 -1
- package/dist/core/configurators/slash/claude.js +6 -0
- package/dist/core/configurators/slash/claude.js.map +1 -1
- package/dist/core/configurators/slash/codex.d.ts.map +1 -1
- package/dist/core/configurators/slash/codex.js +6 -0
- package/dist/core/configurators/slash/codex.js.map +1 -1
- package/dist/core/configurators/slash/github-copilot.d.ts.map +1 -1
- package/dist/core/configurators/slash/github-copilot.js +6 -0
- package/dist/core/configurators/slash/github-copilot.js.map +1 -1
- package/dist/core/configurators/slash/kilocode.d.ts.map +1 -1
- package/dist/core/configurators/slash/kilocode.js +6 -0
- package/dist/core/configurators/slash/kilocode.js.map +1 -1
- package/dist/core/configurators/slash/opencode.d.ts.map +1 -1
- package/dist/core/configurators/slash/opencode.js +6 -0
- package/dist/core/configurators/slash/opencode.js.map +1 -1
- package/dist/core/converters/json-converter.d.ts +6 -0
- package/dist/core/converters/json-converter.d.ts.map +1 -0
- package/dist/core/converters/json-converter.js +51 -0
- package/dist/core/converters/json-converter.js.map +1 -0
- package/dist/core/global-config.d.ts +39 -0
- package/dist/core/global-config.d.ts.map +1 -0
- package/dist/core/global-config.js +115 -0
- package/dist/core/global-config.js.map +1 -0
- package/dist/core/parsers/change-parser.d.ts +13 -0
- package/dist/core/parsers/change-parser.d.ts.map +1 -0
- package/dist/core/parsers/change-parser.js +193 -0
- package/dist/core/parsers/change-parser.js.map +1 -0
- package/dist/core/parsers/markdown-parser.d.ts +22 -0
- package/dist/core/parsers/markdown-parser.d.ts.map +1 -0
- package/dist/core/parsers/markdown-parser.js +187 -0
- package/dist/core/parsers/markdown-parser.js.map +1 -0
- package/dist/core/parsers/requirement-blocks.d.ts +37 -0
- package/dist/core/parsers/requirement-blocks.d.ts.map +1 -0
- package/dist/core/parsers/requirement-blocks.js +201 -0
- package/dist/core/parsers/requirement-blocks.js.map +1 -0
- package/dist/core/prompt-templates.d.ts +3 -0
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +66 -0
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/schemas/base.schema.d.ts +13 -0
- package/dist/core/schemas/base.schema.d.ts.map +1 -0
- package/dist/core/schemas/base.schema.js +13 -0
- package/dist/core/schemas/base.schema.js.map +1 -0
- package/dist/core/schemas/change.schema.d.ts +73 -0
- package/dist/core/schemas/change.schema.d.ts.map +1 -0
- package/dist/core/schemas/change.schema.js +31 -0
- package/dist/core/schemas/change.schema.js.map +1 -0
- package/dist/core/schemas/index.d.ts +4 -0
- package/dist/core/schemas/index.d.ts.map +1 -0
- package/dist/core/schemas/index.js +4 -0
- package/dist/core/schemas/index.js.map +1 -0
- package/dist/core/schemas/spec.schema.d.ts +18 -0
- package/dist/core/schemas/spec.schema.d.ts.map +1 -0
- package/dist/core/schemas/spec.schema.js +15 -0
- package/dist/core/schemas/spec.schema.js.map +1 -0
- package/dist/core/specs-apply.d.ts +73 -0
- package/dist/core/specs-apply.d.ts.map +1 -0
- package/dist/core/specs-apply.js +384 -0
- package/dist/core/specs-apply.js.map +1 -0
- package/dist/core/styles/palette.d.ts +7 -0
- package/dist/core/styles/palette.d.ts.map +1 -0
- package/dist/core/styles/palette.js +8 -0
- package/dist/core/styles/palette.js.map +1 -0
- package/dist/core/templates/agents-template.d.ts +1 -1
- package/dist/core/templates/agents-template.d.ts.map +1 -1
- package/dist/core/templates/agents-template.js +443 -19
- package/dist/core/templates/agents-template.js.map +1 -1
- package/dist/core/templates/slash-command-templates.d.ts +1 -1
- package/dist/core/templates/slash-command-templates.d.ts.map +1 -1
- package/dist/core/templates/slash-command-templates.js +4 -1
- package/dist/core/templates/slash-command-templates.js.map +1 -1
- package/dist/core/validation/constants.d.ts +34 -0
- package/dist/core/validation/constants.d.ts.map +1 -0
- package/dist/core/validation/constants.js +40 -0
- package/dist/core/validation/constants.js.map +1 -0
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.d.ts.map +1 -0
- package/dist/core/validation/types.js +2 -0
- package/dist/core/validation/types.js.map +1 -0
- package/dist/core/validation/validator.d.ts +33 -0
- package/dist/core/validation/validator.d.ts.map +1 -0
- package/dist/core/validation/validator.js +409 -0
- package/dist/core/validation/validator.js.map +1 -0
- package/dist/core/view.d.ts +8 -0
- package/dist/core/view.d.ts.map +1 -0
- package/dist/core/view.js +168 -0
- package/dist/core/view.js.map +1 -0
- package/dist/utils/change-metadata.d.ts +47 -0
- package/dist/utils/change-metadata.d.ts.map +1 -0
- package/dist/utils/change-metadata.js +130 -0
- package/dist/utils/change-metadata.js.map +1 -0
- package/dist/utils/change-utils.d.ts +51 -0
- package/dist/utils/change-utils.d.ts.map +1 -0
- package/dist/utils/change-utils.js +100 -0
- package/dist/utils/change-utils.js.map +1 -0
- package/dist/utils/file-system.d.ts +25 -0
- package/dist/utils/file-system.d.ts.map +1 -0
- package/dist/utils/file-system.js +218 -0
- package/dist/utils/file-system.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/interactive.d.ts +18 -0
- package/dist/utils/interactive.d.ts.map +1 -0
- package/dist/utils/interactive.js +21 -0
- package/dist/utils/interactive.js.map +1 -0
- package/dist/utils/item-discovery.d.ts +4 -0
- package/dist/utils/item-discovery.d.ts.map +1 -0
- package/dist/utils/item-discovery.js +72 -0
- package/dist/utils/item-discovery.js.map +1 -0
- package/dist/utils/match.d.ts +3 -0
- package/dist/utils/match.d.ts.map +1 -0
- package/dist/utils/match.js +22 -0
- package/dist/utils/match.js.map +1 -0
- package/dist/utils/shell-detection.d.ts +20 -0
- package/dist/utils/shell-detection.d.ts.map +1 -0
- package/dist/utils/shell-detection.js +41 -0
- package/dist/utils/shell-detection.js.map +1 -0
- package/dist/utils/task-progress.d.ts +8 -0
- package/dist/utils/task-progress.d.ts.map +1 -0
- package/dist/utils/task-progress.js +36 -0
- package/dist/utils/task-progress.js.map +1 -0
- package/docs/tasks.md +1 -1
- package/package.json +6 -2
- package/prompt/apply.md +17 -0
- package/prompt/archive.md +21 -0
- package/prompt/proposal.md +22 -0
- package/src/cli/index.ts +151 -16
- package/src/commands/archive.ts +302 -0
- package/src/commands/change.ts +292 -0
- package/src/commands/config.ts +233 -0
- package/src/commands/init.ts +19 -5
- package/src/commands/list.ts +176 -66
- package/src/commands/show.ts +138 -0
- package/src/commands/spec.ts +251 -0
- package/src/commands/validate.ts +326 -0
- package/src/core/artifact-graph/graph.ts +167 -0
- package/src/core/artifact-graph/index.ts +44 -0
- package/src/core/artifact-graph/instruction-loader.ts +302 -0
- package/src/core/artifact-graph/resolver.ts +226 -0
- package/src/core/artifact-graph/schema.ts +124 -0
- package/src/core/artifact-graph/state.ts +64 -0
- package/src/core/artifact-graph/types.ts +65 -0
- package/src/core/completions/command-registry.ts +382 -0
- package/src/core/completions/completion-provider.ts +128 -0
- package/src/core/completions/generators/bash-generator.ts +191 -0
- package/src/core/completions/generators/fish-generator.ts +188 -0
- package/src/core/completions/generators/powershell-generator.ts +223 -0
- package/src/core/completions/generators/zsh-generator.ts +281 -0
- package/src/core/completions/templates/bash-templates.ts +24 -0
- package/src/core/completions/templates/fish-templates.ts +40 -0
- package/src/core/completions/templates/powershell-templates.ts +25 -0
- package/src/core/completions/templates/zsh-templates.ts +36 -0
- package/src/core/completions/types.ts +90 -0
- package/src/core/config-schema.ts +230 -0
- package/src/core/config.ts +18 -0
- package/src/core/configurators/slash/antigravity.ts +6 -0
- package/src/core/configurators/slash/base.ts +1 -1
- package/src/core/configurators/slash/claude.ts +6 -0
- package/src/core/configurators/slash/codex.ts +6 -0
- package/src/core/configurators/slash/github-copilot.ts +6 -0
- package/src/core/configurators/slash/kilocode.ts +6 -0
- package/src/core/configurators/slash/opencode.ts +6 -0
- package/src/core/converters/json-converter.ts +62 -0
- package/src/core/global-config.ts +136 -0
- package/src/core/parsers/change-parser.ts +234 -0
- package/src/core/parsers/markdown-parser.ts +237 -0
- package/src/core/parsers/requirement-blocks.ts +234 -0
- package/src/core/prompt-templates.ts +69 -0
- package/src/core/schemas/base.schema.ts +20 -0
- package/src/core/schemas/change.schema.ts +42 -0
- package/src/core/schemas/index.ts +20 -0
- package/src/core/schemas/spec.schema.ts +17 -0
- package/src/core/specs-apply.ts +483 -0
- package/src/core/styles/palette.ts +8 -0
- package/src/core/templates/agents-template.ts +443 -19
- package/src/core/templates/slash-command-templates.ts +7 -1
- package/src/core/validation/constants.ts +48 -0
- package/src/core/validation/types.ts +19 -0
- package/src/core/validation/validator.ts +449 -0
- package/src/core/view.ts +219 -0
- package/src/utils/change-metadata.ts +171 -0
- package/src/utils/change-utils.ts +131 -0
- package/src/utils/file-system.ts +252 -0
- package/src/utils/index.ts +12 -0
- package/src/utils/interactive.ts +29 -0
- package/src/utils/item-discovery.ts +66 -0
- package/src/utils/match.ts +26 -0
- package/src/utils/shell-detection.ts +62 -0
- package/src/utils/task-progress.ts +43 -0
- package/dist/commands/ai-humanizer.d.ts +0 -11
- package/dist/commands/ai-humanizer.d.ts.map +0 -1
- package/dist/commands/ai-humanizer.js +0 -97
- package/dist/commands/ai-humanizer.js.map +0 -1
- package/dist/commands/api-contract-generator.d.ts +0 -11
- package/dist/commands/api-contract-generator.d.ts.map +0 -1
- package/dist/commands/api-contract-generator.js +0 -97
- package/dist/commands/api-contract-generator.js.map +0 -1
- package/dist/commands/design-system.d.ts +0 -11
- package/dist/commands/design-system.d.ts.map +0 -1
- package/dist/commands/design-system.js +0 -97
- package/dist/commands/design-system.js.map +0 -1
- package/dist/commands/document-explainer.d.ts +0 -11
- package/dist/commands/document-explainer.d.ts.map +0 -1
- package/dist/commands/document-explainer.js +0 -97
- package/dist/commands/document-explainer.js.map +0 -1
- package/dist/commands/epic-generator.d.ts +0 -11
- package/dist/commands/epic-generator.d.ts.map +0 -1
- package/dist/commands/epic-generator.js +0 -97
- package/dist/commands/epic-generator.js.map +0 -1
- package/dist/commands/erd-generator.d.ts +0 -11
- package/dist/commands/erd-generator.d.ts.map +0 -1
- package/dist/commands/erd-generator.js +0 -97
- package/dist/commands/erd-generator.js.map +0 -1
- package/dist/commands/fsd-generator.d.ts +0 -11
- package/dist/commands/fsd-generator.d.ts.map +0 -1
- package/dist/commands/fsd-generator.js +0 -97
- package/dist/commands/fsd-generator.js.map +0 -1
- package/dist/commands/prd-agent-generator.d.ts +0 -11
- package/dist/commands/prd-agent-generator.d.ts.map +0 -1
- package/dist/commands/prd-agent-generator.js +0 -95
- package/dist/commands/prd-agent-generator.js.map +0 -1
- package/dist/commands/prd-generator.d.ts +0 -11
- package/dist/commands/prd-generator.d.ts.map +0 -1
- package/dist/commands/prd-generator.js +0 -97
- package/dist/commands/prd-generator.js.map +0 -1
- package/dist/commands/product-brief.d.ts +0 -11
- package/dist/commands/product-brief.d.ts.map +0 -1
- package/dist/commands/product-brief.js +0 -97
- package/dist/commands/product-brief.js.map +0 -1
- package/dist/commands/skill-creator.d.ts +0 -11
- package/dist/commands/skill-creator.d.ts.map +0 -1
- package/dist/commands/skill-creator.js +0 -101
- package/dist/commands/skill-creator.js.map +0 -1
- package/dist/commands/story-generator.d.ts +0 -11
- package/dist/commands/story-generator.d.ts.map +0 -1
- package/dist/commands/story-generator.js +0 -97
- package/dist/commands/story-generator.js.map +0 -1
- package/dist/commands/tdd-generator.d.ts +0 -11
- package/dist/commands/tdd-generator.d.ts.map +0 -1
- package/dist/commands/tdd-generator.js +0 -97
- package/dist/commands/tdd-generator.js.map +0 -1
- package/dist/commands/tdd-lite-generator.d.ts +0 -11
- package/dist/commands/tdd-lite-generator.d.ts.map +0 -1
- package/dist/commands/tdd-lite-generator.js +0 -97
- package/dist/commands/tdd-lite-generator.js.map +0 -1
- package/dist/commands/wireframe-generator.d.ts +0 -11
- package/dist/commands/wireframe-generator.d.ts.map +0 -1
- package/dist/commands/wireframe-generator.js +0 -97
- package/dist/commands/wireframe-generator.js.map +0 -1
- package/src/commands/ai-humanizer.ts +0 -118
- package/src/commands/api-contract-generator.ts +0 -118
- package/src/commands/design-system.ts +0 -118
- package/src/commands/document-explainer.ts +0 -118
- package/src/commands/epic-generator.ts +0 -118
- package/src/commands/erd-generator.ts +0 -118
- package/src/commands/fsd-generator.ts +0 -118
- package/src/commands/prd-agent-generator.ts +0 -115
- package/src/commands/prd-generator.ts +0 -118
- package/src/commands/product-brief.ts +0 -118
- package/src/commands/skill-creator.ts +0 -123
- package/src/commands/story-generator.ts +0 -118
- package/src/commands/tdd-generator.ts +0 -118
- package/src/commands/tdd-lite-generator.ts +0 -118
- package/src/commands/wireframe-generator.ts +0 -118
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import {
|
|
5
|
+
getGlobalConfigPath,
|
|
6
|
+
getGlobalConfig,
|
|
7
|
+
saveGlobalConfig,
|
|
8
|
+
GlobalConfig,
|
|
9
|
+
} from '../core/global-config.js';
|
|
10
|
+
import {
|
|
11
|
+
getNestedValue,
|
|
12
|
+
setNestedValue,
|
|
13
|
+
deleteNestedValue,
|
|
14
|
+
coerceValue,
|
|
15
|
+
formatValueYaml,
|
|
16
|
+
validateConfigKeyPath,
|
|
17
|
+
validateConfig,
|
|
18
|
+
DEFAULT_CONFIG,
|
|
19
|
+
} from '../core/config-schema.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Register the config command and all its subcommands.
|
|
23
|
+
*
|
|
24
|
+
* @param program - The Commander program instance
|
|
25
|
+
*/
|
|
26
|
+
export function registerConfigCommand(program: Command): void {
|
|
27
|
+
const configCmd = program
|
|
28
|
+
.command('config')
|
|
29
|
+
.description('View and modify global Prompter configuration')
|
|
30
|
+
.option('--scope <scope>', 'Config scope (only "global" supported currently)')
|
|
31
|
+
.hook('preAction', (thisCommand) => {
|
|
32
|
+
const opts = thisCommand.opts();
|
|
33
|
+
if (opts.scope && opts.scope !== 'global') {
|
|
34
|
+
console.error('Error: Project-local config is not yet implemented');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// config path
|
|
40
|
+
configCmd
|
|
41
|
+
.command('path')
|
|
42
|
+
.description('Show config file location')
|
|
43
|
+
.action(() => {
|
|
44
|
+
console.log(getGlobalConfigPath());
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// config list
|
|
48
|
+
configCmd
|
|
49
|
+
.command('list')
|
|
50
|
+
.description('Show all current settings')
|
|
51
|
+
.option('--json', 'Output as JSON')
|
|
52
|
+
.action((options: { json?: boolean }) => {
|
|
53
|
+
const config = getGlobalConfig();
|
|
54
|
+
|
|
55
|
+
if (options.json) {
|
|
56
|
+
console.log(JSON.stringify(config, null, 2));
|
|
57
|
+
} else {
|
|
58
|
+
console.log(formatValueYaml(config));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// config get
|
|
63
|
+
configCmd
|
|
64
|
+
.command('get <key>')
|
|
65
|
+
.description('Get a specific value (raw, scriptable)')
|
|
66
|
+
.action((key: string) => {
|
|
67
|
+
const config = getGlobalConfig();
|
|
68
|
+
const value = getNestedValue(config as Record<string, unknown>, key);
|
|
69
|
+
|
|
70
|
+
if (value === undefined) {
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (typeof value === 'object' && value !== null) {
|
|
76
|
+
console.log(JSON.stringify(value));
|
|
77
|
+
} else {
|
|
78
|
+
console.log(String(value));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// config set
|
|
83
|
+
configCmd
|
|
84
|
+
.command('set <key> <value>')
|
|
85
|
+
.description('Set a value (auto-coerce types)')
|
|
86
|
+
.option('--string', 'Force value to be stored as string')
|
|
87
|
+
.option('--allow-unknown', 'Allow setting unknown keys')
|
|
88
|
+
.action((key: string, value: string, options: { string?: boolean; allowUnknown?: boolean }) => {
|
|
89
|
+
const allowUnknown = Boolean(options.allowUnknown);
|
|
90
|
+
const keyValidation = validateConfigKeyPath(key);
|
|
91
|
+
if (!keyValidation.valid && !allowUnknown) {
|
|
92
|
+
const reason = keyValidation.reason ? ` ${keyValidation.reason}.` : '';
|
|
93
|
+
console.error(`Error: Invalid configuration key "${key}".${reason}`);
|
|
94
|
+
console.error('Use "prompter config list" to see available keys.');
|
|
95
|
+
console.error('Pass --allow-unknown to bypass this check.');
|
|
96
|
+
process.exitCode = 1;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const config = getGlobalConfig() as Record<string, unknown>;
|
|
101
|
+
const coercedValue = coerceValue(value, options.string || false);
|
|
102
|
+
|
|
103
|
+
// Create a copy to validate before saving
|
|
104
|
+
const newConfig = JSON.parse(JSON.stringify(config));
|
|
105
|
+
setNestedValue(newConfig, key, coercedValue);
|
|
106
|
+
|
|
107
|
+
// Validate the new config
|
|
108
|
+
const validation = validateConfig(newConfig);
|
|
109
|
+
if (!validation.success) {
|
|
110
|
+
console.error(`Error: Invalid configuration - ${validation.error}`);
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Apply changes and save
|
|
116
|
+
setNestedValue(config, key, coercedValue);
|
|
117
|
+
saveGlobalConfig(config as GlobalConfig);
|
|
118
|
+
|
|
119
|
+
const displayValue =
|
|
120
|
+
typeof coercedValue === 'string' ? `"${coercedValue}"` : String(coercedValue);
|
|
121
|
+
console.log(`Set ${key} = ${displayValue}`);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// config unset
|
|
125
|
+
configCmd
|
|
126
|
+
.command('unset <key>')
|
|
127
|
+
.description('Remove a key (revert to default)')
|
|
128
|
+
.action((key: string) => {
|
|
129
|
+
const config = getGlobalConfig() as Record<string, unknown>;
|
|
130
|
+
const existed = deleteNestedValue(config, key);
|
|
131
|
+
|
|
132
|
+
if (existed) {
|
|
133
|
+
saveGlobalConfig(config as GlobalConfig);
|
|
134
|
+
console.log(`Unset ${key} (reverted to default)`);
|
|
135
|
+
} else {
|
|
136
|
+
console.log(`Key "${key}" was not set`);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// config reset
|
|
141
|
+
configCmd
|
|
142
|
+
.command('reset')
|
|
143
|
+
.description('Reset configuration to defaults')
|
|
144
|
+
.option('--all', 'Reset all configuration (required)')
|
|
145
|
+
.option('-y, --yes', 'Skip confirmation prompts')
|
|
146
|
+
.action(async (options: { all?: boolean; yes?: boolean }) => {
|
|
147
|
+
if (!options.all) {
|
|
148
|
+
console.error('Error: --all flag is required for reset');
|
|
149
|
+
console.error('Usage: prompter config reset --all [-y]');
|
|
150
|
+
process.exitCode = 1;
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!options.yes) {
|
|
155
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
156
|
+
const confirmed = await confirm({
|
|
157
|
+
message: 'Reset all configuration to defaults?',
|
|
158
|
+
default: false,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (!confirmed) {
|
|
162
|
+
console.log('Reset cancelled.');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
saveGlobalConfig({ ...DEFAULT_CONFIG });
|
|
168
|
+
console.log('Configuration reset to defaults');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// config edit
|
|
172
|
+
configCmd
|
|
173
|
+
.command('edit')
|
|
174
|
+
.description('Open config in $EDITOR')
|
|
175
|
+
.action(async () => {
|
|
176
|
+
const editor = process.env.EDITOR || process.env.VISUAL;
|
|
177
|
+
|
|
178
|
+
if (!editor) {
|
|
179
|
+
console.error('Error: No editor configured');
|
|
180
|
+
console.error('Set the EDITOR or VISUAL environment variable to your preferred editor');
|
|
181
|
+
console.error('Example: export EDITOR=vim');
|
|
182
|
+
process.exitCode = 1;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const configPath = getGlobalConfigPath();
|
|
187
|
+
|
|
188
|
+
// Ensure config file exists with defaults
|
|
189
|
+
if (!fs.existsSync(configPath)) {
|
|
190
|
+
saveGlobalConfig({ ...DEFAULT_CONFIG });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Spawn editor and wait for it to close
|
|
194
|
+
// Avoid shell parsing to correctly handle paths with spaces in both
|
|
195
|
+
// the editor path and config path
|
|
196
|
+
const child = spawn(editor, [configPath], {
|
|
197
|
+
stdio: 'inherit',
|
|
198
|
+
shell: false,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await new Promise<void>((resolve, reject) => {
|
|
202
|
+
child.on('close', (code) => {
|
|
203
|
+
if (code === 0) {
|
|
204
|
+
resolve();
|
|
205
|
+
} else {
|
|
206
|
+
reject(new Error(`Editor exited with code ${code}`));
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
child.on('error', reject);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
const rawConfig = fs.readFileSync(configPath, 'utf-8');
|
|
214
|
+
const parsedConfig = JSON.parse(rawConfig);
|
|
215
|
+
const validation = validateConfig(parsedConfig);
|
|
216
|
+
|
|
217
|
+
if (!validation.success) {
|
|
218
|
+
console.error(`Error: Invalid configuration - ${validation.error}`);
|
|
219
|
+
process.exitCode = 1;
|
|
220
|
+
}
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
223
|
+
console.error(`Error: Config file not found at ${configPath}`);
|
|
224
|
+
} else if (error instanceof SyntaxError) {
|
|
225
|
+
console.error(`Error: Invalid JSON in ${configPath}`);
|
|
226
|
+
console.error(error.message);
|
|
227
|
+
} else {
|
|
228
|
+
console.error(`Error: Unable to validate configuration - ${error instanceof Error ? error.message : String(error)}`);
|
|
229
|
+
}
|
|
230
|
+
process.exitCode = 1;
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
package/src/commands/init.ts
CHANGED
|
@@ -120,18 +120,32 @@ export class InitCommand {
|
|
|
120
120
|
const projectMdPath = path.join(prompterPath, 'project.md');
|
|
121
121
|
const projectMdExists = await this.fileExists(projectMdPath);
|
|
122
122
|
if (!projectMdExists) {
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
try {
|
|
124
|
+
if (!projectTemplate) {
|
|
125
|
+
throw new Error('project.md template is undefined');
|
|
126
|
+
}
|
|
127
|
+
await fs.writeFile(projectMdPath, projectTemplate, 'utf-8');
|
|
128
|
+
console.log(chalk.green('✓') + ` Created ${chalk.cyan(PROMPTER_DIR + '/project.md')}`);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error(chalk.red('✗') + ` Failed to create project.md: ${error}`);
|
|
131
|
+
}
|
|
125
132
|
} else if (isReInitialization) {
|
|
126
133
|
console.log(chalk.gray(' project.md already exists, keeping it'));
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
// Create AGENTS.md for universal support
|
|
130
|
-
const agentsMdPath = path.join(
|
|
137
|
+
const agentsMdPath = path.join(prompterPath, 'AGENTS.md');
|
|
131
138
|
const agentsExists = await this.fileExists(agentsMdPath);
|
|
132
139
|
if (!agentsExists) {
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
try {
|
|
141
|
+
if (!agentsTemplate) {
|
|
142
|
+
throw new Error('AGENTS.md template is undefined');
|
|
143
|
+
}
|
|
144
|
+
await fs.writeFile(agentsMdPath, agentsTemplate, 'utf-8');
|
|
145
|
+
console.log(chalk.green('✓') + ` Created ${chalk.cyan(PROMPTER_DIR + '/AGENTS.md')}`);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(chalk.red('✗') + ` Failed to create AGENTS.md: ${error}`);
|
|
148
|
+
}
|
|
135
149
|
} else {
|
|
136
150
|
console.log(chalk.gray(' AGENTS.md already exists, skipping'));
|
|
137
151
|
}
|
package/src/commands/list.ts
CHANGED
|
@@ -1,84 +1,194 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { getTaskProgressForChange, formatTaskStatus } from '../utils/task-progress.js';
|
|
4
|
+
import { readFileSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { MarkdownParser } from '../core/parsers/markdown-parser.js';
|
|
7
|
+
|
|
8
|
+
interface ChangeInfo {
|
|
9
|
+
name: string;
|
|
10
|
+
completedTasks: number;
|
|
11
|
+
totalTasks: number;
|
|
12
|
+
lastModified: Date;
|
|
8
13
|
}
|
|
9
14
|
|
|
10
|
-
interface
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
createdAt?: string;
|
|
15
|
+
interface ListOptions {
|
|
16
|
+
sort?: 'recent' | 'name';
|
|
17
|
+
json?: boolean;
|
|
14
18
|
}
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Get the most recent modification time of any file in a directory (recursive).
|
|
22
|
+
* Falls back to the directory's own mtime if no files are found.
|
|
23
|
+
*/
|
|
24
|
+
async function getLastModified(dirPath: string): Promise<Date> {
|
|
25
|
+
let latest: Date | null = null;
|
|
26
|
+
|
|
27
|
+
async function walk(dir: string): Promise<void> {
|
|
28
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const fullPath = path.join(dir, entry.name);
|
|
31
|
+
if (entry.isDirectory()) {
|
|
32
|
+
await walk(fullPath);
|
|
33
|
+
} else {
|
|
34
|
+
const stat = await fs.stat(fullPath);
|
|
35
|
+
if (latest === null || stat.mtime > latest) {
|
|
36
|
+
latest = stat.mtime;
|
|
30
37
|
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
const prompts = await this.scanPrompts(prompterPath);
|
|
42
|
+
await walk(dirPath);
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
// If no files found, use the directory's own modification time
|
|
45
|
+
if (latest === null) {
|
|
46
|
+
const dirStat = await fs.stat(dirPath);
|
|
47
|
+
return dirStat.mtime;
|
|
48
|
+
}
|
|
39
49
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
console.log(chalk.gray('Use /prompter-enhance to create one.\n'));
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
50
|
+
return latest;
|
|
51
|
+
}
|
|
45
52
|
|
|
46
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Format a date as relative time (e.g., "2 hours ago", "3 days ago")
|
|
55
|
+
*/
|
|
56
|
+
function formatRelativeTime(date: Date): string {
|
|
57
|
+
const now = new Date();
|
|
58
|
+
const diffMs = now.getTime() - date.getTime();
|
|
59
|
+
const diffSecs = Math.floor(diffMs / 1000);
|
|
60
|
+
const diffMins = Math.floor(diffSecs / 60);
|
|
61
|
+
const diffHours = Math.floor(diffMins / 60);
|
|
62
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
63
|
+
|
|
64
|
+
if (diffDays > 30) {
|
|
65
|
+
return date.toLocaleDateString();
|
|
66
|
+
} else if (diffDays > 0) {
|
|
67
|
+
return `${diffDays}d ago`;
|
|
68
|
+
} else if (diffHours > 0) {
|
|
69
|
+
return `${diffHours}h ago`;
|
|
70
|
+
} else if (diffMins > 0) {
|
|
71
|
+
return `${diffMins}m ago`;
|
|
72
|
+
} else {
|
|
73
|
+
return 'just now';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
47
76
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
77
|
+
export class ListCommand {
|
|
78
|
+
async execute(targetPath: string = '.', mode: 'changes' | 'specs' = 'changes', options: ListOptions = {}): Promise<void> {
|
|
79
|
+
const { sort = 'recent', json = false } = options;
|
|
80
|
+
|
|
81
|
+
if (mode === 'changes') {
|
|
82
|
+
const changesDir = path.join(targetPath, 'prompter', 'changes');
|
|
83
|
+
|
|
84
|
+
// Check if changes directory exists
|
|
85
|
+
try {
|
|
86
|
+
await fs.access(changesDir);
|
|
87
|
+
} catch {
|
|
88
|
+
throw new Error("No Prompter changes directory found. Run 'prompter init' first.");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Get all directories in changes (excluding archive)
|
|
92
|
+
const entries = await fs.readdir(changesDir, { withFileTypes: true });
|
|
93
|
+
const changeDirs = entries
|
|
94
|
+
.filter(entry => entry.isDirectory() && entry.name !== 'archive')
|
|
95
|
+
.map(entry => entry.name);
|
|
96
|
+
|
|
97
|
+
if (changeDirs.length === 0) {
|
|
98
|
+
if (json) {
|
|
99
|
+
console.log(JSON.stringify({ changes: [] }));
|
|
100
|
+
} else {
|
|
101
|
+
console.log('No active changes found.');
|
|
51
102
|
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Collect information about each change
|
|
107
|
+
const changes: ChangeInfo[] = [];
|
|
108
|
+
|
|
109
|
+
for (const changeDir of changeDirs) {
|
|
110
|
+
const progress = await getTaskProgressForChange(changesDir, changeDir);
|
|
111
|
+
const changePath = path.join(changesDir, changeDir);
|
|
112
|
+
const lastModified = await getLastModified(changePath);
|
|
113
|
+
changes.push({
|
|
114
|
+
name: changeDir,
|
|
115
|
+
completedTasks: progress.completed,
|
|
116
|
+
totalTasks: progress.total,
|
|
117
|
+
lastModified
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Sort by preference (default: recent first)
|
|
122
|
+
if (sort === 'recent') {
|
|
123
|
+
changes.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
|
|
124
|
+
} else {
|
|
125
|
+
changes.sort((a, b) => a.name.localeCompare(b.name));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// JSON output for programmatic use
|
|
129
|
+
if (json) {
|
|
130
|
+
const jsonOutput = changes.map(c => ({
|
|
131
|
+
name: c.name,
|
|
132
|
+
completedTasks: c.completedTasks,
|
|
133
|
+
totalTasks: c.totalTasks,
|
|
134
|
+
lastModified: c.lastModified.toISOString(),
|
|
135
|
+
status: c.totalTasks === 0 ? 'no-tasks' : c.completedTasks === c.totalTasks ? 'complete' : 'in-progress'
|
|
136
|
+
}));
|
|
137
|
+
console.log(JSON.stringify({ changes: jsonOutput }, null, 2));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Display results
|
|
142
|
+
console.log('Changes:');
|
|
143
|
+
const padding = ' ';
|
|
144
|
+
const nameWidth = Math.max(...changes.map(c => c.name.length));
|
|
145
|
+
for (const change of changes) {
|
|
146
|
+
const paddedName = change.name.padEnd(nameWidth);
|
|
147
|
+
const status = formatTaskStatus({ total: change.totalTasks, completed: change.completedTasks });
|
|
148
|
+
const timeAgo = formatRelativeTime(change.lastModified);
|
|
149
|
+
console.log(`${padding}${paddedName} ${status.padEnd(12)} ${timeAgo}`);
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
52
153
|
|
|
53
|
-
|
|
154
|
+
// specs mode
|
|
155
|
+
const specsDir = path.join(targetPath, 'prompter', 'specs');
|
|
156
|
+
try {
|
|
157
|
+
await fs.access(specsDir);
|
|
158
|
+
} catch {
|
|
159
|
+
console.log('No specs found.');
|
|
160
|
+
return;
|
|
54
161
|
}
|
|
55
162
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
for (const entry of entries) {
|
|
63
|
-
if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
|
|
64
|
-
|
|
65
|
-
const enhancedPromptPath = path.join(prompterPath, entry.name, 'enhanced-prompt.md');
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
const stats = await fs.stat(enhancedPromptPath);
|
|
69
|
-
prompts.push({
|
|
70
|
-
id: entry.name,
|
|
71
|
-
path: path.join(PROMPTER_DIR, entry.name, 'enhanced-prompt.md'),
|
|
72
|
-
createdAt: stats.birthtime.toISOString()
|
|
73
|
-
});
|
|
74
|
-
} catch {
|
|
75
|
-
// No enhanced-prompt.md in this directory, skip
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
} catch (error) {
|
|
79
|
-
// Directory doesn't exist or can't be read
|
|
80
|
-
}
|
|
163
|
+
const entries = await fs.readdir(specsDir, { withFileTypes: true });
|
|
164
|
+
const specDirs = entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
165
|
+
if (specDirs.length === 0) {
|
|
166
|
+
console.log('No specs found.');
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
81
169
|
|
|
82
|
-
|
|
170
|
+
type SpecInfo = { id: string; requirementCount: number };
|
|
171
|
+
const specs: SpecInfo[] = [];
|
|
172
|
+
for (const id of specDirs) {
|
|
173
|
+
const specPath = join(specsDir, id, 'spec.md');
|
|
174
|
+
try {
|
|
175
|
+
const content = readFileSync(specPath, 'utf-8');
|
|
176
|
+
const parser = new MarkdownParser(content);
|
|
177
|
+
const spec = parser.parseSpec(id);
|
|
178
|
+
specs.push({ id, requirementCount: spec.requirements.length });
|
|
179
|
+
} catch {
|
|
180
|
+
// If spec cannot be read or parsed, include with 0 count
|
|
181
|
+
specs.push({ id, requirementCount: 0 });
|
|
182
|
+
}
|
|
83
183
|
}
|
|
84
|
-
|
|
184
|
+
|
|
185
|
+
specs.sort((a, b) => a.id.localeCompare(b.id));
|
|
186
|
+
console.log('Specs:');
|
|
187
|
+
const padding = ' ';
|
|
188
|
+
const nameWidth = Math.max(...specs.map(s => s.id.length));
|
|
189
|
+
for (const spec of specs) {
|
|
190
|
+
const padded = spec.id.padEnd(nameWidth);
|
|
191
|
+
console.log(`${padding}${padded} requirements ${spec.requirementCount}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { isInteractive } from '../utils/interactive.js';
|
|
3
|
+
import { getActiveChangeIds, getSpecIds } from '../utils/item-discovery.js';
|
|
4
|
+
import { ChangeCommand } from './change.js';
|
|
5
|
+
import { SpecCommand } from './spec.js';
|
|
6
|
+
import { nearestMatches } from '../utils/match.js';
|
|
7
|
+
|
|
8
|
+
type ItemType = 'change' | 'spec';
|
|
9
|
+
|
|
10
|
+
const CHANGE_FLAG_KEYS = new Set(['deltasOnly', 'requirementsOnly']);
|
|
11
|
+
const SPEC_FLAG_KEYS = new Set(['requirements', 'scenarios', 'requirement']);
|
|
12
|
+
|
|
13
|
+
export class ShowCommand {
|
|
14
|
+
async execute(itemName?: string, options: { json?: boolean; type?: string; noInteractive?: boolean; [k: string]: any } = {}): Promise<void> {
|
|
15
|
+
const interactive = isInteractive(options);
|
|
16
|
+
const typeOverride = this.normalizeType(options.type);
|
|
17
|
+
|
|
18
|
+
if (!itemName) {
|
|
19
|
+
if (interactive) {
|
|
20
|
+
const { select } = await import('@inquirer/prompts');
|
|
21
|
+
const type = await select<ItemType>({
|
|
22
|
+
message: 'What would you like to show?',
|
|
23
|
+
choices: [
|
|
24
|
+
{ name: 'Change', value: 'change' as const },
|
|
25
|
+
{ name: 'Spec', value: 'spec' as const },
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
await this.runInteractiveByType(type, options);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.printNonInteractiveHint();
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await this.showDirect(itemName, { typeOverride, options });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private normalizeType(value?: string): ItemType | undefined {
|
|
40
|
+
if (!value) return undefined;
|
|
41
|
+
const v = value.toLowerCase();
|
|
42
|
+
if (v === 'change' || v === 'spec') return v;
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private async runInteractiveByType(type: ItemType, options: { json?: boolean; noInteractive?: boolean; [k: string]: any }): Promise<void> {
|
|
47
|
+
const { select } = await import('@inquirer/prompts');
|
|
48
|
+
if (type === 'change') {
|
|
49
|
+
const changes = await getActiveChangeIds();
|
|
50
|
+
if (changes.length === 0) {
|
|
51
|
+
console.error('No changes found.');
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const picked = await select<string>({ message: 'Pick a change', choices: changes.map(id => ({ name: id, value: id })) });
|
|
56
|
+
const cmd = new ChangeCommand();
|
|
57
|
+
await cmd.show(picked, options as any);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const specs = await getSpecIds();
|
|
62
|
+
if (specs.length === 0) {
|
|
63
|
+
console.error('No specs found.');
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const picked = await select<string>({ message: 'Pick a spec', choices: specs.map(id => ({ name: id, value: id })) });
|
|
68
|
+
const cmd = new SpecCommand();
|
|
69
|
+
await cmd.show(picked, options as any);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private async showDirect(itemName: string, params: { typeOverride?: ItemType; options: { json?: boolean; [k: string]: any } }): Promise<void> {
|
|
73
|
+
// Optimize lookups when type is pre-specified
|
|
74
|
+
let isChange = false;
|
|
75
|
+
let isSpec = false;
|
|
76
|
+
let changes: string[] = [];
|
|
77
|
+
let specs: string[] = [];
|
|
78
|
+
if (params.typeOverride === 'change') {
|
|
79
|
+
changes = await getActiveChangeIds();
|
|
80
|
+
isChange = changes.includes(itemName);
|
|
81
|
+
} else if (params.typeOverride === 'spec') {
|
|
82
|
+
specs = await getSpecIds();
|
|
83
|
+
isSpec = specs.includes(itemName);
|
|
84
|
+
} else {
|
|
85
|
+
[changes, specs] = await Promise.all([getActiveChangeIds(), getSpecIds()]);
|
|
86
|
+
isChange = changes.includes(itemName);
|
|
87
|
+
isSpec = specs.includes(itemName);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const resolvedType = params.typeOverride ?? (isChange ? 'change' : isSpec ? 'spec' : undefined);
|
|
91
|
+
|
|
92
|
+
if (!resolvedType) {
|
|
93
|
+
console.error(`Unknown item '${itemName}'`);
|
|
94
|
+
const suggestions = nearestMatches(itemName, [...changes, ...specs]);
|
|
95
|
+
if (suggestions.length) console.error(`Did you mean: ${suggestions.join(', ')}?`);
|
|
96
|
+
process.exitCode = 1;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!params.typeOverride && isChange && isSpec) {
|
|
101
|
+
console.error(`Ambiguous item '${itemName}' matches both a change and a spec.`);
|
|
102
|
+
console.error('Pass --type change|spec, or use: prompter change show / prompter spec show');
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.warnIrrelevantFlags(resolvedType, params.options);
|
|
108
|
+
if (resolvedType === 'change') {
|
|
109
|
+
const cmd = new ChangeCommand();
|
|
110
|
+
await cmd.show(itemName, params.options as any);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const cmd = new SpecCommand();
|
|
114
|
+
await cmd.show(itemName, params.options as any);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private printNonInteractiveHint(): void {
|
|
118
|
+
console.error('Nothing to show. Try one of:');
|
|
119
|
+
console.error(' prompter show <item>');
|
|
120
|
+
console.error(' prompter change show');
|
|
121
|
+
console.error(' prompter spec show');
|
|
122
|
+
console.error('Or run in an interactive terminal.');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private warnIrrelevantFlags(type: ItemType, options: { [k: string]: any }): boolean {
|
|
126
|
+
const irrelevant: string[] = [];
|
|
127
|
+
if (type === 'change') {
|
|
128
|
+
for (const k of SPEC_FLAG_KEYS) if (k in options) irrelevant.push(k);
|
|
129
|
+
} else {
|
|
130
|
+
for (const k of CHANGE_FLAG_KEYS) if (k in options) irrelevant.push(k);
|
|
131
|
+
}
|
|
132
|
+
if (irrelevant.length > 0) {
|
|
133
|
+
console.error(`Warning: Ignoring flags not applicable to ${type}: ${irrelevant.join(', ')}`);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|