@superkou/openspec 1.4.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/LICENSE +22 -0
- package/README.md +213 -0
- package/bin/openspec.js +5 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +544 -0
- package/dist/commands/change.d.ts +35 -0
- package/dist/commands/change.js +277 -0
- package/dist/commands/completion.d.ts +72 -0
- package/dist/commands/completion.js +264 -0
- package/dist/commands/config.d.ts +36 -0
- package/dist/commands/config.js +611 -0
- package/dist/commands/context-store.d.ts +3 -0
- package/dist/commands/context-store.js +475 -0
- package/dist/commands/feedback.d.ts +9 -0
- package/dist/commands/feedback.js +183 -0
- package/dist/commands/initiative.d.ts +13 -0
- package/dist/commands/initiative.js +318 -0
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/schema.js +869 -0
- package/dist/commands/show.d.ts +14 -0
- package/dist/commands/show.js +132 -0
- package/dist/commands/spec.d.ts +15 -0
- package/dist/commands/spec.js +225 -0
- package/dist/commands/validate.d.ts +24 -0
- package/dist/commands/validate.js +294 -0
- package/dist/commands/workflow/index.d.ts +19 -0
- package/dist/commands/workflow/index.js +13 -0
- package/dist/commands/workflow/initiative-link.d.ts +24 -0
- package/dist/commands/workflow/initiative-link.js +47 -0
- package/dist/commands/workflow/instructions.d.ts +29 -0
- package/dist/commands/workflow/instructions.js +344 -0
- package/dist/commands/workflow/new-change.d.ts +17 -0
- package/dist/commands/workflow/new-change.js +141 -0
- package/dist/commands/workflow/schemas.d.ts +10 -0
- package/dist/commands/workflow/schemas.js +34 -0
- package/dist/commands/workflow/set-change.d.ts +13 -0
- package/dist/commands/workflow/set-change.js +87 -0
- package/dist/commands/workflow/shared.d.ts +59 -0
- package/dist/commands/workflow/shared.js +116 -0
- package/dist/commands/workflow/status.d.ts +14 -0
- package/dist/commands/workflow/status.js +90 -0
- package/dist/commands/workflow/templates.d.ts +16 -0
- package/dist/commands/workflow/templates.js +69 -0
- package/dist/commands/workspace/context-status.d.ts +4 -0
- package/dist/commands/workspace/context-status.js +59 -0
- package/dist/commands/workspace/open-target-selection.d.ts +13 -0
- package/dist/commands/workspace/open-target-selection.js +146 -0
- package/dist/commands/workspace/open-view.d.ts +62 -0
- package/dist/commands/workspace/open-view.js +249 -0
- package/dist/commands/workspace/open.d.ts +37 -0
- package/dist/commands/workspace/open.js +110 -0
- package/dist/commands/workspace/opener-selection.d.ts +11 -0
- package/dist/commands/workspace/opener-selection.js +98 -0
- package/dist/commands/workspace/operations.d.ts +29 -0
- package/dist/commands/workspace/operations.js +543 -0
- package/dist/commands/workspace/prompt-theme.d.ts +29 -0
- package/dist/commands/workspace/prompt-theme.js +24 -0
- package/dist/commands/workspace/registration.d.ts +13 -0
- package/dist/commands/workspace/registration.js +84 -0
- package/dist/commands/workspace/selection.d.ts +8 -0
- package/dist/commands/workspace/selection.js +104 -0
- package/dist/commands/workspace/setup-prompts.d.ts +13 -0
- package/dist/commands/workspace/setup-prompts.js +121 -0
- package/dist/commands/workspace/types.d.ts +103 -0
- package/dist/commands/workspace/types.js +36 -0
- package/dist/commands/workspace.d.ts +5 -0
- package/dist/commands/workspace.js +577 -0
- package/dist/core/archive.d.ts +11 -0
- package/dist/core/archive.js +318 -0
- package/dist/core/artifact-graph/graph.d.ts +56 -0
- package/dist/core/artifact-graph/graph.js +141 -0
- package/dist/core/artifact-graph/index.d.ts +9 -0
- package/dist/core/artifact-graph/index.js +14 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +183 -0
- package/dist/core/artifact-graph/instruction-loader.js +256 -0
- package/dist/core/artifact-graph/outputs.d.ts +14 -0
- package/dist/core/artifact-graph/outputs.js +39 -0
- package/dist/core/artifact-graph/resolver.d.ts +81 -0
- package/dist/core/artifact-graph/resolver.js +257 -0
- package/dist/core/artifact-graph/schema.d.ts +13 -0
- package/dist/core/artifact-graph/schema.js +108 -0
- package/dist/core/artifact-graph/state.d.ts +12 -0
- package/dist/core/artifact-graph/state.js +31 -0
- package/dist/core/artifact-graph/types.d.ts +40 -0
- package/dist/core/artifact-graph/types.js +29 -0
- package/dist/core/available-tools.d.ts +17 -0
- package/dist/core/available-tools.js +43 -0
- package/dist/core/change-metadata/index.d.ts +2 -0
- package/dist/core/change-metadata/index.js +2 -0
- package/dist/core/change-metadata/schema.d.ts +18 -0
- package/dist/core/change-metadata/schema.js +28 -0
- package/dist/core/change-status-policy.d.ts +50 -0
- package/dist/core/change-status-policy.js +70 -0
- package/dist/core/collections/index.d.ts +3 -0
- package/dist/core/collections/index.js +3 -0
- package/dist/core/collections/initiatives/collection.d.ts +4 -0
- package/dist/core/collections/initiatives/collection.js +17 -0
- package/dist/core/collections/initiatives/index.d.ts +6 -0
- package/dist/core/collections/initiatives/index.js +6 -0
- package/dist/core/collections/initiatives/operations.d.ts +49 -0
- package/dist/core/collections/initiatives/operations.js +175 -0
- package/dist/core/collections/initiatives/resolution.d.ts +87 -0
- package/dist/core/collections/initiatives/resolution.js +374 -0
- package/dist/core/collections/initiatives/schema.d.ts +41 -0
- package/dist/core/collections/initiatives/schema.js +134 -0
- package/dist/core/collections/initiatives/templates.d.ts +12 -0
- package/dist/core/collections/initiatives/templates.js +90 -0
- package/dist/core/collections/runtime.d.ts +46 -0
- package/dist/core/collections/runtime.js +194 -0
- package/dist/core/command-generation/adapters/amazon-q.d.ts +13 -0
- package/dist/core/command-generation/adapters/amazon-q.js +26 -0
- package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
- package/dist/core/command-generation/adapters/antigravity.js +26 -0
- package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
- package/dist/core/command-generation/adapters/auggie.js +27 -0
- package/dist/core/command-generation/adapters/bob.d.ts +14 -0
- package/dist/core/command-generation/adapters/bob.js +45 -0
- package/dist/core/command-generation/adapters/claude.d.ts +13 -0
- package/dist/core/command-generation/adapters/claude.js +50 -0
- package/dist/core/command-generation/adapters/cline.d.ts +14 -0
- package/dist/core/command-generation/adapters/cline.js +27 -0
- package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
- package/dist/core/command-generation/adapters/codebuddy.js +28 -0
- package/dist/core/command-generation/adapters/codex.d.ts +16 -0
- package/dist/core/command-generation/adapters/codex.js +39 -0
- package/dist/core/command-generation/adapters/continue.d.ts +13 -0
- package/dist/core/command-generation/adapters/continue.js +28 -0
- package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
- package/dist/core/command-generation/adapters/costrict.js +27 -0
- package/dist/core/command-generation/adapters/crush.d.ts +13 -0
- package/dist/core/command-generation/adapters/crush.js +30 -0
- package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
- package/dist/core/command-generation/adapters/cursor.js +44 -0
- package/dist/core/command-generation/adapters/factory.d.ts +13 -0
- package/dist/core/command-generation/adapters/factory.js +27 -0
- package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
- package/dist/core/command-generation/adapters/gemini.js +26 -0
- package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
- package/dist/core/command-generation/adapters/github-copilot.js +26 -0
- package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
- package/dist/core/command-generation/adapters/iflow.js +29 -0
- package/dist/core/command-generation/adapters/index.d.ts +32 -0
- package/dist/core/command-generation/adapters/index.js +32 -0
- package/dist/core/command-generation/adapters/junie.d.ts +13 -0
- package/dist/core/command-generation/adapters/junie.js +26 -0
- package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
- package/dist/core/command-generation/adapters/kilocode.js +23 -0
- package/dist/core/command-generation/adapters/kiro.d.ts +13 -0
- package/dist/core/command-generation/adapters/kiro.js +26 -0
- package/dist/core/command-generation/adapters/lingma.d.ts +13 -0
- package/dist/core/command-generation/adapters/lingma.js +30 -0
- package/dist/core/command-generation/adapters/opencode.d.ts +13 -0
- package/dist/core/command-generation/adapters/opencode.js +29 -0
- package/dist/core/command-generation/adapters/pi.d.ts +18 -0
- package/dist/core/command-generation/adapters/pi.js +55 -0
- package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
- package/dist/core/command-generation/adapters/qoder.js +30 -0
- package/dist/core/command-generation/adapters/qwen.d.ts +13 -0
- package/dist/core/command-generation/adapters/qwen.js +26 -0
- package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
- package/dist/core/command-generation/adapters/roocode.js +27 -0
- package/dist/core/command-generation/adapters/windsurf.d.ts +14 -0
- package/dist/core/command-generation/adapters/windsurf.js +51 -0
- package/dist/core/command-generation/generator.d.ts +21 -0
- package/dist/core/command-generation/generator.js +27 -0
- package/dist/core/command-generation/index.d.ts +22 -0
- package/dist/core/command-generation/index.js +24 -0
- package/dist/core/command-generation/registry.d.ts +36 -0
- package/dist/core/command-generation/registry.js +98 -0
- package/dist/core/command-generation/types.d.ts +56 -0
- package/dist/core/command-generation/types.js +8 -0
- package/dist/core/completions/command-registry.d.ts +3 -0
- package/dist/core/completions/command-registry.js +961 -0
- package/dist/core/completions/completion-provider.d.ts +71 -0
- package/dist/core/completions/completion-provider.js +129 -0
- package/dist/core/completions/factory.d.ts +64 -0
- package/dist/core/completions/factory.js +75 -0
- package/dist/core/completions/generators/bash-generator.d.ts +35 -0
- package/dist/core/completions/generators/bash-generator.js +230 -0
- package/dist/core/completions/generators/fish-generator.d.ts +32 -0
- package/dist/core/completions/generators/fish-generator.js +160 -0
- package/dist/core/completions/generators/powershell-generator.d.ts +36 -0
- package/dist/core/completions/generators/powershell-generator.js +266 -0
- package/dist/core/completions/generators/zsh-generator.d.ts +47 -0
- package/dist/core/completions/generators/zsh-generator.js +274 -0
- package/dist/core/completions/installers/bash-installer.d.ts +87 -0
- package/dist/core/completions/installers/bash-installer.js +318 -0
- package/dist/core/completions/installers/fish-installer.d.ts +43 -0
- package/dist/core/completions/installers/fish-installer.js +143 -0
- package/dist/core/completions/installers/powershell-installer.d.ts +102 -0
- package/dist/core/completions/installers/powershell-installer.js +387 -0
- package/dist/core/completions/installers/zsh-installer.d.ts +117 -0
- package/dist/core/completions/installers/zsh-installer.js +421 -0
- package/dist/core/completions/shared-flags.d.ts +12 -0
- package/dist/core/completions/shared-flags.js +28 -0
- package/dist/core/completions/templates/bash-templates.d.ts +6 -0
- package/dist/core/completions/templates/bash-templates.js +30 -0
- package/dist/core/completions/templates/fish-templates.d.ts +7 -0
- package/dist/core/completions/templates/fish-templates.js +45 -0
- package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
- package/dist/core/completions/templates/powershell-templates.js +34 -0
- package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
- package/dist/core/completions/templates/zsh-templates.js +45 -0
- package/dist/core/completions/types.d.ts +101 -0
- package/dist/core/completions/types.js +2 -0
- package/dist/core/config-prompts.d.ts +9 -0
- package/dist/core/config-prompts.js +34 -0
- package/dist/core/config-schema.d.ts +86 -0
- package/dist/core/config-schema.js +213 -0
- package/dist/core/config.d.ts +18 -0
- package/dist/core/config.js +39 -0
- package/dist/core/context-store/binding.d.ts +53 -0
- package/dist/core/context-store/binding.js +197 -0
- package/dist/core/context-store/errors.d.ts +20 -0
- package/dist/core/context-store/errors.js +22 -0
- package/dist/core/context-store/foundation.d.ts +55 -0
- package/dist/core/context-store/foundation.js +321 -0
- package/dist/core/context-store/index.d.ts +6 -0
- package/dist/core/context-store/index.js +6 -0
- package/dist/core/context-store/operations.d.ts +85 -0
- package/dist/core/context-store/operations.js +528 -0
- package/dist/core/context-store/registry.d.ts +45 -0
- package/dist/core/context-store/registry.js +229 -0
- package/dist/core/converters/json-converter.d.ts +6 -0
- package/dist/core/converters/json-converter.js +51 -0
- package/dist/core/global-config.d.ts +49 -0
- package/dist/core/global-config.js +124 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +7 -0
- package/dist/core/init.d.ts +37 -0
- package/dist/core/init.js +593 -0
- package/dist/core/legacy-cleanup.d.ts +162 -0
- package/dist/core/legacy-cleanup.js +514 -0
- package/dist/core/list.d.ts +9 -0
- package/dist/core/list.js +171 -0
- package/dist/core/migration.d.ts +23 -0
- package/dist/core/migration.js +108 -0
- package/dist/core/parsers/change-parser.d.ts +13 -0
- package/dist/core/parsers/change-parser.js +197 -0
- package/dist/core/parsers/markdown-parser.d.ts +26 -0
- package/dist/core/parsers/markdown-parser.js +227 -0
- package/dist/core/parsers/requirement-blocks.d.ts +37 -0
- package/dist/core/parsers/requirement-blocks.js +201 -0
- package/dist/core/parsers/spec-structure.d.ts +9 -0
- package/dist/core/parsers/spec-structure.js +88 -0
- package/dist/core/planning-home.d.ts +21 -0
- package/dist/core/planning-home.js +108 -0
- package/dist/core/profile-sync-drift.d.ts +38 -0
- package/dist/core/profile-sync-drift.js +200 -0
- package/dist/core/profiles.d.ts +26 -0
- package/dist/core/profiles.js +40 -0
- package/dist/core/project-config.d.ts +64 -0
- package/dist/core/project-config.js +223 -0
- package/dist/core/schemas/base.schema.d.ts +13 -0
- package/dist/core/schemas/base.schema.js +13 -0
- package/dist/core/schemas/change.schema.d.ts +73 -0
- package/dist/core/schemas/change.schema.js +31 -0
- package/dist/core/schemas/index.d.ts +4 -0
- package/dist/core/schemas/index.js +4 -0
- package/dist/core/schemas/spec.schema.d.ts +18 -0
- package/dist/core/schemas/spec.schema.js +15 -0
- package/dist/core/shared/index.d.ts +8 -0
- package/dist/core/shared/index.js +8 -0
- package/dist/core/shared/skill-generation.d.ts +49 -0
- package/dist/core/shared/skill-generation.js +96 -0
- package/dist/core/shared/tool-detection.d.ts +71 -0
- package/dist/core/shared/tool-detection.js +158 -0
- package/dist/core/specs-apply.d.ts +73 -0
- package/dist/core/specs-apply.js +392 -0
- package/dist/core/styles/palette.d.ts +7 -0
- package/dist/core/styles/palette.js +8 -0
- package/dist/core/templates/index.d.ts +8 -0
- package/dist/core/templates/index.js +9 -0
- package/dist/core/templates/skill-templates.d.ts +19 -0
- package/dist/core/templates/skill-templates.js +18 -0
- package/dist/core/templates/types.d.ts +19 -0
- package/dist/core/templates/types.js +5 -0
- package/dist/core/templates/workflows/apply-change.d.ts +10 -0
- package/dist/core/templates/workflows/apply-change.js +314 -0
- package/dist/core/templates/workflows/archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/archive-change.js +277 -0
- package/dist/core/templates/workflows/bulk-archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/bulk-archive-change.js +492 -0
- package/dist/core/templates/workflows/continue-change.d.ts +10 -0
- package/dist/core/templates/workflows/continue-change.js +234 -0
- package/dist/core/templates/workflows/explore.d.ts +10 -0
- package/dist/core/templates/workflows/explore.js +459 -0
- package/dist/core/templates/workflows/feedback.d.ts +9 -0
- package/dist/core/templates/workflows/feedback.js +108 -0
- package/dist/core/templates/workflows/ff-change.d.ts +10 -0
- package/dist/core/templates/workflows/ff-change.js +200 -0
- package/dist/core/templates/workflows/new-change.d.ts +10 -0
- package/dist/core/templates/workflows/new-change.js +143 -0
- package/dist/core/templates/workflows/onboard.d.ts +10 -0
- package/dist/core/templates/workflows/onboard.js +563 -0
- package/dist/core/templates/workflows/propose.d.ts +10 -0
- package/dist/core/templates/workflows/propose.js +218 -0
- package/dist/core/templates/workflows/sync-specs.d.ts +10 -0
- package/dist/core/templates/workflows/sync-specs.js +290 -0
- package/dist/core/templates/workflows/verify-change.d.ts +10 -0
- package/dist/core/templates/workflows/verify-change.js +338 -0
- package/dist/core/update.d.ts +82 -0
- package/dist/core/update.js +557 -0
- package/dist/core/validation/constants.d.ts +34 -0
- package/dist/core/validation/constants.js +40 -0
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.js +2 -0
- package/dist/core/validation/validator.d.ts +43 -0
- package/dist/core/validation/validator.js +435 -0
- package/dist/core/view.d.ts +8 -0
- package/dist/core/view.js +168 -0
- package/dist/core/workspace/foundation.d.ts +67 -0
- package/dist/core/workspace/foundation.js +295 -0
- package/dist/core/workspace/index.d.ts +8 -0
- package/dist/core/workspace/index.js +8 -0
- package/dist/core/workspace/legacy-state.d.ts +28 -0
- package/dist/core/workspace/legacy-state.js +200 -0
- package/dist/core/workspace/link-input.d.ts +9 -0
- package/dist/core/workspace/link-input.js +32 -0
- package/dist/core/workspace/open-surface.d.ts +45 -0
- package/dist/core/workspace/open-surface.js +215 -0
- package/dist/core/workspace/openers.d.ts +21 -0
- package/dist/core/workspace/openers.js +124 -0
- package/dist/core/workspace/registry.d.ts +24 -0
- package/dist/core/workspace/registry.js +146 -0
- package/dist/core/workspace/skills.d.ts +57 -0
- package/dist/core/workspace/skills.js +334 -0
- package/dist/core/workspace/state-io.d.ts +10 -0
- package/dist/core/workspace/state-io.js +121 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/prompts/searchable-multi-select.d.ts +28 -0
- package/dist/prompts/searchable-multi-select.js +159 -0
- package/dist/telemetry/config.d.ts +38 -0
- package/dist/telemetry/config.js +136 -0
- package/dist/telemetry/index.d.ts +31 -0
- package/dist/telemetry/index.js +164 -0
- package/dist/ui/ascii-patterns.d.ts +16 -0
- package/dist/ui/ascii-patterns.js +133 -0
- package/dist/ui/welcome-screen.d.ts +10 -0
- package/dist/ui/welcome-screen.js +146 -0
- package/dist/utils/change-metadata.d.ts +54 -0
- package/dist/utils/change-metadata.js +141 -0
- package/dist/utils/change-utils.d.ts +71 -0
- package/dist/utils/change-utils.js +123 -0
- package/dist/utils/command-references.d.ts +18 -0
- package/dist/utils/command-references.js +20 -0
- package/dist/utils/file-system.d.ts +41 -0
- package/dist/utils/file-system.js +301 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/interactive.d.ts +18 -0
- package/dist/utils/interactive.js +21 -0
- package/dist/utils/item-discovery.d.ts +4 -0
- package/dist/utils/item-discovery.js +72 -0
- package/dist/utils/match.d.ts +3 -0
- package/dist/utils/match.js +22 -0
- package/dist/utils/shell-detection.d.ts +20 -0
- package/dist/utils/shell-detection.js +41 -0
- package/dist/utils/task-progress.d.ts +8 -0
- package/dist/utils/task-progress.js +36 -0
- package/package.json +85 -0
- package/schemas/spec-driven/schema.yaml +151 -0
- package/schemas/spec-driven/templates/design.md +19 -0
- package/schemas/spec-driven/templates/proposal.md +23 -0
- package/schemas/spec-driven/templates/spec.md +8 -0
- package/schemas/spec-driven/templates/tasks.md +9 -0
- package/schemas/workspace-planning/schema.yaml +72 -0
- package/schemas/workspace-planning/templates/design.md +33 -0
- package/schemas/workspace-planning/templates/proposal.md +28 -0
- package/schemas/workspace-planning/templates/spec.md +9 -0
- package/schemas/workspace-planning/templates/tasks.md +15 -0
- package/scripts/postinstall.js +83 -0
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init Command
|
|
3
|
+
*
|
|
4
|
+
* Sets up OpenSpec with Agent Skills and /opsx:* slash commands.
|
|
5
|
+
* This is the unified setup command that replaces both the old init and experimental commands.
|
|
6
|
+
*/
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import { createRequire } from 'module';
|
|
12
|
+
import { FileSystemUtils } from '../utils/file-system.js';
|
|
13
|
+
import { transformToHyphenCommands } from '../utils/command-references.js';
|
|
14
|
+
import { AI_TOOLS, OPENSPEC_DIR_NAME, } from './config.js';
|
|
15
|
+
import { PALETTE } from './styles/palette.js';
|
|
16
|
+
import { isInteractive } from '../utils/interactive.js';
|
|
17
|
+
import { serializeConfig } from './config-prompts.js';
|
|
18
|
+
import { generateCommands, CommandAdapterRegistry, } from './command-generation/index.js';
|
|
19
|
+
import { detectLegacyArtifacts, cleanupLegacyArtifacts, formatCleanupSummary, formatDetectionSummary, } from './legacy-cleanup.js';
|
|
20
|
+
import { getToolsWithSkillsDir, getToolStates, getSkillTemplates, getCommandContents, generateSkillContent, } from './shared/index.js';
|
|
21
|
+
import { getGlobalConfig } from './global-config.js';
|
|
22
|
+
import { getProfileWorkflows, ALL_WORKFLOWS } from './profiles.js';
|
|
23
|
+
import { getAvailableTools } from './available-tools.js';
|
|
24
|
+
import { migrateIfNeeded } from './migration.js';
|
|
25
|
+
const require = createRequire(import.meta.url);
|
|
26
|
+
const { version: OPENSPEC_VERSION } = require('../../package.json');
|
|
27
|
+
// -----------------------------------------------------------------------------
|
|
28
|
+
// Constants
|
|
29
|
+
// -----------------------------------------------------------------------------
|
|
30
|
+
const DEFAULT_SCHEMA = 'spec-driven';
|
|
31
|
+
const PROGRESS_SPINNER = {
|
|
32
|
+
interval: 80,
|
|
33
|
+
frames: ['░░░', '▒░░', '▒▒░', '▒▒▒', '▓▒▒', '▓▓▒', '▓▓▓', '▒▓▓', '░▒▓'],
|
|
34
|
+
};
|
|
35
|
+
const WORKFLOW_TO_SKILL_DIR = {
|
|
36
|
+
'explore': 'openspec-explore',
|
|
37
|
+
'new': 'openspec-new-change',
|
|
38
|
+
'continue': 'openspec-continue-change',
|
|
39
|
+
'apply': 'openspec-apply-change',
|
|
40
|
+
'ff': 'openspec-ff-change',
|
|
41
|
+
'sync': 'openspec-sync-specs',
|
|
42
|
+
'archive': 'openspec-archive-change',
|
|
43
|
+
'bulk-archive': 'openspec-bulk-archive-change',
|
|
44
|
+
'verify': 'openspec-verify-change',
|
|
45
|
+
'onboard': 'openspec-onboard',
|
|
46
|
+
'propose': 'openspec-propose',
|
|
47
|
+
};
|
|
48
|
+
// -----------------------------------------------------------------------------
|
|
49
|
+
// Init Command Class
|
|
50
|
+
// -----------------------------------------------------------------------------
|
|
51
|
+
export class InitCommand {
|
|
52
|
+
toolsArg;
|
|
53
|
+
force;
|
|
54
|
+
interactiveOption;
|
|
55
|
+
profileOverride;
|
|
56
|
+
constructor(options = {}) {
|
|
57
|
+
this.toolsArg = options.tools;
|
|
58
|
+
this.force = options.force ?? false;
|
|
59
|
+
this.interactiveOption = options.interactive;
|
|
60
|
+
this.profileOverride = options.profile;
|
|
61
|
+
}
|
|
62
|
+
async execute(targetPath) {
|
|
63
|
+
const projectPath = path.resolve(targetPath);
|
|
64
|
+
const openspecDir = OPENSPEC_DIR_NAME;
|
|
65
|
+
const openspecPath = path.join(projectPath, openspecDir);
|
|
66
|
+
// Validation happens silently in the background
|
|
67
|
+
const extendMode = await this.validate(projectPath, openspecPath);
|
|
68
|
+
// Check for legacy artifacts and handle cleanup
|
|
69
|
+
await this.handleLegacyCleanup(projectPath, extendMode);
|
|
70
|
+
// Detect available tools in the project (task 7.1)
|
|
71
|
+
const detectedTools = getAvailableTools(projectPath);
|
|
72
|
+
// Migration check: migrate existing projects to profile system (task 7.3)
|
|
73
|
+
if (extendMode) {
|
|
74
|
+
migrateIfNeeded(projectPath, detectedTools);
|
|
75
|
+
}
|
|
76
|
+
// Show animated welcome screen (interactive mode only)
|
|
77
|
+
const canPrompt = this.canPromptInteractively();
|
|
78
|
+
if (canPrompt) {
|
|
79
|
+
const { showWelcomeScreen } = await import('../ui/welcome-screen.js');
|
|
80
|
+
await showWelcomeScreen();
|
|
81
|
+
}
|
|
82
|
+
// Validate profile override early so invalid values fail before tool setup.
|
|
83
|
+
// The resolved value is consumed later when generation reads effective config.
|
|
84
|
+
this.resolveProfileOverride();
|
|
85
|
+
// Get tool states before processing
|
|
86
|
+
const toolStates = getToolStates(projectPath);
|
|
87
|
+
// Get tool selection (pass detected tools for pre-selection)
|
|
88
|
+
const selectedToolIds = await this.getSelectedTools(toolStates, extendMode, detectedTools, projectPath);
|
|
89
|
+
// Validate selected tools
|
|
90
|
+
const validatedTools = this.validateTools(selectedToolIds, toolStates);
|
|
91
|
+
// Create directory structure and config
|
|
92
|
+
await this.createDirectoryStructure(openspecPath, extendMode);
|
|
93
|
+
// Generate skills and commands for each tool
|
|
94
|
+
const results = await this.generateSkillsAndCommands(projectPath, validatedTools);
|
|
95
|
+
// Create config.yaml if needed
|
|
96
|
+
const configStatus = await this.createConfig(openspecPath, extendMode);
|
|
97
|
+
// Display success message
|
|
98
|
+
this.displaySuccessMessage(projectPath, validatedTools, results, configStatus);
|
|
99
|
+
}
|
|
100
|
+
// ═══════════════════════════════════════════════════════════
|
|
101
|
+
// VALIDATION & SETUP
|
|
102
|
+
// ═══════════════════════════════════════════════════════════
|
|
103
|
+
async validate(projectPath, openspecPath) {
|
|
104
|
+
const extendMode = await FileSystemUtils.directoryExists(openspecPath);
|
|
105
|
+
// Check write permissions
|
|
106
|
+
if (!(await FileSystemUtils.ensureWritePermissions(projectPath))) {
|
|
107
|
+
throw new Error(`Insufficient permissions to write to ${projectPath}`);
|
|
108
|
+
}
|
|
109
|
+
return extendMode;
|
|
110
|
+
}
|
|
111
|
+
canPromptInteractively() {
|
|
112
|
+
if (this.interactiveOption === false)
|
|
113
|
+
return false;
|
|
114
|
+
if (this.toolsArg !== undefined)
|
|
115
|
+
return false;
|
|
116
|
+
return isInteractive({ interactive: this.interactiveOption });
|
|
117
|
+
}
|
|
118
|
+
resolveProfileOverride() {
|
|
119
|
+
if (this.profileOverride === undefined) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
if (this.profileOverride === 'core' || this.profileOverride === 'custom') {
|
|
123
|
+
return this.profileOverride;
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`Invalid profile "${this.profileOverride}". Available profiles: core, custom`);
|
|
126
|
+
}
|
|
127
|
+
// ═══════════════════════════════════════════════════════════
|
|
128
|
+
// LEGACY CLEANUP
|
|
129
|
+
// ═══════════════════════════════════════════════════════════
|
|
130
|
+
async handleLegacyCleanup(projectPath, extendMode) {
|
|
131
|
+
// Detect legacy artifacts
|
|
132
|
+
const detection = await detectLegacyArtifacts(projectPath);
|
|
133
|
+
if (!detection.hasLegacyArtifacts) {
|
|
134
|
+
return; // No legacy artifacts found
|
|
135
|
+
}
|
|
136
|
+
// Show what was detected
|
|
137
|
+
console.log();
|
|
138
|
+
console.log(formatDetectionSummary(detection));
|
|
139
|
+
console.log();
|
|
140
|
+
const canPrompt = this.canPromptInteractively();
|
|
141
|
+
if (this.force || !canPrompt) {
|
|
142
|
+
// --force flag or non-interactive mode: proceed with cleanup automatically.
|
|
143
|
+
// Legacy slash commands are 100% OpenSpec-managed, and config file cleanup
|
|
144
|
+
// only removes markers (never deletes files), so auto-cleanup is safe.
|
|
145
|
+
await this.performLegacyCleanup(projectPath, detection);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
// Interactive mode: prompt for confirmation
|
|
149
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
150
|
+
const shouldCleanup = await confirm({
|
|
151
|
+
message: '升级并清理旧版文件?',
|
|
152
|
+
default: true,
|
|
153
|
+
});
|
|
154
|
+
if (!shouldCleanup) {
|
|
155
|
+
console.log(chalk.dim('初始化已取消。'));
|
|
156
|
+
console.log(chalk.dim('使用 --force 跳过此提示,或手动删除旧版文件。'));
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
await this.performLegacyCleanup(projectPath, detection);
|
|
160
|
+
}
|
|
161
|
+
async performLegacyCleanup(projectPath, detection) {
|
|
162
|
+
const spinner = ora('正在清理旧版文件...').start();
|
|
163
|
+
const result = await cleanupLegacyArtifacts(projectPath, detection);
|
|
164
|
+
spinner.succeed('旧版文件已清理');
|
|
165
|
+
const summary = formatCleanupSummary(result);
|
|
166
|
+
if (summary) {
|
|
167
|
+
console.log();
|
|
168
|
+
console.log(summary);
|
|
169
|
+
}
|
|
170
|
+
console.log();
|
|
171
|
+
}
|
|
172
|
+
// ═══════════════════════════════════════════════════════════
|
|
173
|
+
// TOOL SELECTION
|
|
174
|
+
// ═══════════════════════════════════════════════════════════
|
|
175
|
+
async getSelectedTools(toolStates, extendMode, detectedTools, projectPath) {
|
|
176
|
+
// Check for --tools flag first
|
|
177
|
+
const nonInteractiveSelection = this.resolveToolsArg();
|
|
178
|
+
if (nonInteractiveSelection !== null) {
|
|
179
|
+
return nonInteractiveSelection;
|
|
180
|
+
}
|
|
181
|
+
const validTools = getToolsWithSkillsDir();
|
|
182
|
+
const detectedToolIds = new Set(detectedTools.map((t) => t.value));
|
|
183
|
+
const configuredToolIds = new Set([...toolStates.entries()]
|
|
184
|
+
.filter(([, status]) => status.configured)
|
|
185
|
+
.map(([toolId]) => toolId));
|
|
186
|
+
const shouldPreselectDetected = !extendMode && configuredToolIds.size === 0;
|
|
187
|
+
const canPrompt = this.canPromptInteractively();
|
|
188
|
+
// Non-interactive mode: use detected tools as fallback (task 7.8)
|
|
189
|
+
if (!canPrompt) {
|
|
190
|
+
if (detectedToolIds.size > 0) {
|
|
191
|
+
return [...detectedToolIds];
|
|
192
|
+
}
|
|
193
|
+
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,cursor,...`);
|
|
194
|
+
}
|
|
195
|
+
if (validTools.length === 0) {
|
|
196
|
+
throw new Error(`No tools available for skill generation.`);
|
|
197
|
+
}
|
|
198
|
+
// Interactive mode: show searchable multi-select
|
|
199
|
+
const { searchableMultiSelect } = await import('../prompts/searchable-multi-select.js');
|
|
200
|
+
// Build choices: pre-select configured tools; keep detected tools visible but unselected.
|
|
201
|
+
const sortedChoices = validTools
|
|
202
|
+
.map((toolId) => {
|
|
203
|
+
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
204
|
+
const status = toolStates.get(toolId);
|
|
205
|
+
const configured = status?.configured ?? false;
|
|
206
|
+
const detected = detectedToolIds.has(toolId);
|
|
207
|
+
return {
|
|
208
|
+
name: tool?.name || toolId,
|
|
209
|
+
value: toolId,
|
|
210
|
+
configured,
|
|
211
|
+
detected: detected && !configured,
|
|
212
|
+
preSelected: configured || (shouldPreselectDetected && detected && !configured),
|
|
213
|
+
};
|
|
214
|
+
})
|
|
215
|
+
.sort((a, b) => {
|
|
216
|
+
// Configured tools first, then detected (not configured), then everything else.
|
|
217
|
+
if (a.configured && !b.configured)
|
|
218
|
+
return -1;
|
|
219
|
+
if (!a.configured && b.configured)
|
|
220
|
+
return 1;
|
|
221
|
+
if (a.detected && !b.detected)
|
|
222
|
+
return -1;
|
|
223
|
+
if (!a.detected && b.detected)
|
|
224
|
+
return 1;
|
|
225
|
+
return 0;
|
|
226
|
+
});
|
|
227
|
+
const configuredNames = validTools
|
|
228
|
+
.filter((toolId) => configuredToolIds.has(toolId))
|
|
229
|
+
.map((toolId) => AI_TOOLS.find((t) => t.value === toolId)?.name || toolId);
|
|
230
|
+
if (configuredNames.length > 0) {
|
|
231
|
+
console.log(`已配置 OpenSpec:${configuredNames.join(', ')}(预选)`);
|
|
232
|
+
}
|
|
233
|
+
const detectedOnlyNames = detectedTools
|
|
234
|
+
.filter((tool) => !configuredToolIds.has(tool.value))
|
|
235
|
+
.map((tool) => tool.name);
|
|
236
|
+
if (detectedOnlyNames.length > 0) {
|
|
237
|
+
const detectionLabel = shouldPreselectDetected
|
|
238
|
+
? '为首次设置预选'
|
|
239
|
+
: '未预选';
|
|
240
|
+
console.log(`检测到的工具目录:${detectedOnlyNames.join(', ')}(${detectionLabel})`);
|
|
241
|
+
}
|
|
242
|
+
const selectedTools = await searchableMultiSelect({
|
|
243
|
+
message: `选择要设置的工具(${validTools.length} 个可用)`,
|
|
244
|
+
pageSize: 15,
|
|
245
|
+
choices: sortedChoices,
|
|
246
|
+
validate: (selected) => selected.length > 0 || '请至少选择一个工具',
|
|
247
|
+
});
|
|
248
|
+
if (selectedTools.length === 0) {
|
|
249
|
+
throw new Error('At least one tool must be selected');
|
|
250
|
+
}
|
|
251
|
+
return selectedTools;
|
|
252
|
+
}
|
|
253
|
+
resolveToolsArg() {
|
|
254
|
+
if (typeof this.toolsArg === 'undefined') {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
const raw = this.toolsArg.trim();
|
|
258
|
+
if (raw.length === 0) {
|
|
259
|
+
throw new Error('The --tools option requires a value. Use "all", "none", or a comma-separated list of tool IDs.');
|
|
260
|
+
}
|
|
261
|
+
const availableTools = getToolsWithSkillsDir();
|
|
262
|
+
const availableSet = new Set(availableTools);
|
|
263
|
+
const availableList = ['all', 'none', ...availableTools].join(', ');
|
|
264
|
+
const lowerRaw = raw.toLowerCase();
|
|
265
|
+
if (lowerRaw === 'all') {
|
|
266
|
+
return availableTools;
|
|
267
|
+
}
|
|
268
|
+
if (lowerRaw === 'none') {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
const tokens = raw
|
|
272
|
+
.split(',')
|
|
273
|
+
.map((token) => token.trim())
|
|
274
|
+
.filter((token) => token.length > 0);
|
|
275
|
+
if (tokens.length === 0) {
|
|
276
|
+
throw new Error('The --tools option requires at least one tool ID when not using "all" or "none".');
|
|
277
|
+
}
|
|
278
|
+
const normalizedTokens = tokens.map((token) => token.toLowerCase());
|
|
279
|
+
if (normalizedTokens.some((token) => token === 'all' || token === 'none')) {
|
|
280
|
+
throw new Error('Cannot combine reserved values "all" or "none" with specific tool IDs.');
|
|
281
|
+
}
|
|
282
|
+
const invalidTokens = tokens.filter((_token, index) => !availableSet.has(normalizedTokens[index]));
|
|
283
|
+
if (invalidTokens.length > 0) {
|
|
284
|
+
throw new Error(`Invalid tool(s): ${invalidTokens.join(', ')}. Available values: ${availableList}`);
|
|
285
|
+
}
|
|
286
|
+
// Deduplicate while preserving order
|
|
287
|
+
const deduped = [];
|
|
288
|
+
for (const token of normalizedTokens) {
|
|
289
|
+
if (!deduped.includes(token)) {
|
|
290
|
+
deduped.push(token);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return deduped;
|
|
294
|
+
}
|
|
295
|
+
validateTools(toolIds, toolStates) {
|
|
296
|
+
const validatedTools = [];
|
|
297
|
+
for (const toolId of toolIds) {
|
|
298
|
+
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
299
|
+
if (!tool) {
|
|
300
|
+
const validToolIds = getToolsWithSkillsDir();
|
|
301
|
+
throw new Error(`Unknown tool '${toolId}'. Valid tools:\n ${validToolIds.join('\n ')}`);
|
|
302
|
+
}
|
|
303
|
+
if (!tool.skillsDir) {
|
|
304
|
+
const validToolsWithSkills = getToolsWithSkillsDir();
|
|
305
|
+
throw new Error(`Tool '${toolId}' does not support skill generation.\nTools with skill generation support:\n ${validToolsWithSkills.join('\n ')}`);
|
|
306
|
+
}
|
|
307
|
+
const preState = toolStates.get(tool.value);
|
|
308
|
+
validatedTools.push({
|
|
309
|
+
value: tool.value,
|
|
310
|
+
name: tool.name,
|
|
311
|
+
skillsDir: tool.skillsDir,
|
|
312
|
+
wasConfigured: preState?.configured ?? false,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
return validatedTools;
|
|
316
|
+
}
|
|
317
|
+
// ═══════════════════════════════════════════════════════════
|
|
318
|
+
// DIRECTORY STRUCTURE
|
|
319
|
+
// ═══════════════════════════════════════════════════════════
|
|
320
|
+
async createDirectoryStructure(openspecPath, extendMode) {
|
|
321
|
+
if (extendMode) {
|
|
322
|
+
// In extend mode, just ensure directories exist without spinner
|
|
323
|
+
const directories = [
|
|
324
|
+
openspecPath,
|
|
325
|
+
path.join(openspecPath, 'specs'),
|
|
326
|
+
path.join(openspecPath, 'changes'),
|
|
327
|
+
path.join(openspecPath, 'changes', 'archive'),
|
|
328
|
+
];
|
|
329
|
+
for (const dir of directories) {
|
|
330
|
+
await FileSystemUtils.createDirectory(dir);
|
|
331
|
+
}
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const spinner = this.startSpinner('Creating OpenSpec structure...');
|
|
335
|
+
const directories = [
|
|
336
|
+
openspecPath,
|
|
337
|
+
path.join(openspecPath, 'specs'),
|
|
338
|
+
path.join(openspecPath, 'changes'),
|
|
339
|
+
path.join(openspecPath, 'changes', 'archive'),
|
|
340
|
+
];
|
|
341
|
+
for (const dir of directories) {
|
|
342
|
+
await FileSystemUtils.createDirectory(dir);
|
|
343
|
+
}
|
|
344
|
+
spinner.stopAndPersist({
|
|
345
|
+
symbol: PALETTE.white('▌'),
|
|
346
|
+
text: PALETTE.white('OpenSpec structure created'),
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
// ═══════════════════════════════════════════════════════════
|
|
350
|
+
// SKILL & COMMAND GENERATION
|
|
351
|
+
// ═══════════════════════════════════════════════════════════
|
|
352
|
+
async generateSkillsAndCommands(projectPath, tools) {
|
|
353
|
+
const createdTools = [];
|
|
354
|
+
const refreshedTools = [];
|
|
355
|
+
const failedTools = [];
|
|
356
|
+
const commandsSkipped = [];
|
|
357
|
+
let removedCommandCount = 0;
|
|
358
|
+
let removedSkillCount = 0;
|
|
359
|
+
// Read global config for profile and delivery settings (use --profile override if set)
|
|
360
|
+
const globalConfig = getGlobalConfig();
|
|
361
|
+
const profile = this.resolveProfileOverride() ?? globalConfig.profile ?? 'core';
|
|
362
|
+
const delivery = globalConfig.delivery ?? 'both';
|
|
363
|
+
const workflows = getProfileWorkflows(profile, globalConfig.workflows);
|
|
364
|
+
// Get skill and command templates filtered by profile workflows
|
|
365
|
+
const shouldGenerateSkills = delivery !== 'commands';
|
|
366
|
+
const shouldGenerateCommands = delivery !== 'skills';
|
|
367
|
+
const skillTemplates = shouldGenerateSkills ? getSkillTemplates(workflows) : [];
|
|
368
|
+
const commandContents = shouldGenerateCommands ? getCommandContents(workflows) : [];
|
|
369
|
+
// Process each tool
|
|
370
|
+
for (const tool of tools) {
|
|
371
|
+
const spinner = ora(`Setting up ${tool.name}...`).start();
|
|
372
|
+
try {
|
|
373
|
+
// Generate skill files if delivery includes skills
|
|
374
|
+
if (shouldGenerateSkills) {
|
|
375
|
+
// Use tool-specific skillsDir
|
|
376
|
+
const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
|
|
377
|
+
// Create skill directories and SKILL.md files
|
|
378
|
+
for (const { template, dirName } of skillTemplates) {
|
|
379
|
+
const skillDir = path.join(skillsDir, dirName);
|
|
380
|
+
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
381
|
+
// Generate SKILL.md content with YAML frontmatter including generatedBy
|
|
382
|
+
// Use hyphen-based command references for tools where filename = command name
|
|
383
|
+
const transformer = (tool.value === 'opencode' || tool.value === 'pi') ? transformToHyphenCommands : undefined;
|
|
384
|
+
const skillContent = generateSkillContent(template, OPENSPEC_VERSION, transformer);
|
|
385
|
+
// Write the skill file
|
|
386
|
+
await FileSystemUtils.writeFile(skillFile, skillContent);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (!shouldGenerateSkills) {
|
|
390
|
+
const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
|
|
391
|
+
removedSkillCount += await this.removeSkillDirs(skillsDir);
|
|
392
|
+
}
|
|
393
|
+
// Generate commands if delivery includes commands
|
|
394
|
+
if (shouldGenerateCommands) {
|
|
395
|
+
const adapter = CommandAdapterRegistry.get(tool.value);
|
|
396
|
+
if (adapter) {
|
|
397
|
+
const generatedCommands = generateCommands(commandContents, adapter);
|
|
398
|
+
for (const cmd of generatedCommands) {
|
|
399
|
+
const commandFile = path.isAbsolute(cmd.path) ? cmd.path : path.join(projectPath, cmd.path);
|
|
400
|
+
await FileSystemUtils.writeFile(commandFile, cmd.fileContent);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
commandsSkipped.push(tool.value);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (!shouldGenerateCommands) {
|
|
408
|
+
removedCommandCount += await this.removeCommandFiles(projectPath, tool.value);
|
|
409
|
+
}
|
|
410
|
+
spinner.succeed(`Setup complete for ${tool.name}`);
|
|
411
|
+
if (tool.wasConfigured) {
|
|
412
|
+
refreshedTools.push(tool);
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
createdTools.push(tool);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
spinner.fail(`Failed for ${tool.name}`);
|
|
420
|
+
failedTools.push({ name: tool.name, error: error });
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return {
|
|
424
|
+
createdTools,
|
|
425
|
+
refreshedTools,
|
|
426
|
+
failedTools,
|
|
427
|
+
commandsSkipped,
|
|
428
|
+
removedCommandCount,
|
|
429
|
+
removedSkillCount,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
// ═══════════════════════════════════════════════════════════
|
|
433
|
+
// CONFIG FILE
|
|
434
|
+
// ═══════════════════════════════════════════════════════════
|
|
435
|
+
async createConfig(openspecPath, extendMode) {
|
|
436
|
+
const configPath = path.join(openspecPath, 'config.yaml');
|
|
437
|
+
const configYmlPath = path.join(openspecPath, 'config.yml');
|
|
438
|
+
const configYamlExists = fs.existsSync(configPath);
|
|
439
|
+
const configYmlExists = fs.existsSync(configYmlPath);
|
|
440
|
+
if (configYamlExists || configYmlExists) {
|
|
441
|
+
return 'exists';
|
|
442
|
+
}
|
|
443
|
+
// In non-interactive mode without --force, skip config creation
|
|
444
|
+
if (!this.canPromptInteractively() && !this.force) {
|
|
445
|
+
return 'skipped';
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
const yamlContent = serializeConfig({ schema: DEFAULT_SCHEMA });
|
|
449
|
+
await FileSystemUtils.writeFile(configPath, yamlContent);
|
|
450
|
+
return 'created';
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
return 'skipped';
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// ═══════════════════════════════════════════════════════════
|
|
457
|
+
// UI & OUTPUT
|
|
458
|
+
// ═══════════════════════════════════════════════════════════
|
|
459
|
+
displaySuccessMessage(projectPath, tools, results, configStatus) {
|
|
460
|
+
console.log();
|
|
461
|
+
console.log(chalk.bold('OpenSpec 设置完成'));
|
|
462
|
+
console.log();
|
|
463
|
+
// Show created vs refreshed tools
|
|
464
|
+
if (results.createdTools.length > 0) {
|
|
465
|
+
console.log(`已创建:${results.createdTools.map((t) => t.name).join(', ')}`);
|
|
466
|
+
}
|
|
467
|
+
if (results.refreshedTools.length > 0) {
|
|
468
|
+
console.log(`已刷新:${results.refreshedTools.map((t) => t.name).join(', ')}`);
|
|
469
|
+
}
|
|
470
|
+
// Show counts (respecting profile filter)
|
|
471
|
+
const successfulTools = [...results.createdTools, ...results.refreshedTools];
|
|
472
|
+
if (successfulTools.length > 0) {
|
|
473
|
+
const globalConfig = getGlobalConfig();
|
|
474
|
+
const profile = this.profileOverride ?? globalConfig.profile ?? 'core';
|
|
475
|
+
const delivery = globalConfig.delivery ?? 'both';
|
|
476
|
+
const workflows = getProfileWorkflows(profile, globalConfig.workflows);
|
|
477
|
+
const toolDirs = [...new Set(successfulTools.map((t) => t.skillsDir))].join(', ');
|
|
478
|
+
const skillCount = delivery !== 'commands' ? getSkillTemplates(workflows).length : 0;
|
|
479
|
+
const commandCount = delivery !== 'skills' ? getCommandContents(workflows).length : 0;
|
|
480
|
+
if (skillCount > 0 && commandCount > 0) {
|
|
481
|
+
console.log(`${skillCount} 项技能和 ${commandCount} 项命令,位于 ${toolDirs}/`);
|
|
482
|
+
}
|
|
483
|
+
else if (skillCount > 0) {
|
|
484
|
+
console.log(`${skillCount} 项技能,位于 ${toolDirs}/`);
|
|
485
|
+
}
|
|
486
|
+
else if (commandCount > 0) {
|
|
487
|
+
console.log(`${commandCount} 项命令,位于 ${toolDirs}/`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// Show failures
|
|
491
|
+
if (results.failedTools.length > 0) {
|
|
492
|
+
console.log(chalk.red(`失败:${results.failedTools.map((f) => `${f.name} (${f.error.message})`).join(', ')}`));
|
|
493
|
+
}
|
|
494
|
+
// Show skipped commands
|
|
495
|
+
if (results.commandsSkipped.length > 0) {
|
|
496
|
+
console.log(chalk.dim(`已跳过命令:${results.commandsSkipped.join(', ')}(无适配器)`));
|
|
497
|
+
}
|
|
498
|
+
if (results.removedCommandCount > 0) {
|
|
499
|
+
console.log(chalk.dim(`已移除:${results.removedCommandCount} 个命令文件(交付方式:skills)`));
|
|
500
|
+
}
|
|
501
|
+
if (results.removedSkillCount > 0) {
|
|
502
|
+
console.log(chalk.dim(`已移除:${results.removedSkillCount} 个技能目录(交付方式:commands)`));
|
|
503
|
+
}
|
|
504
|
+
// Config status
|
|
505
|
+
if (configStatus === 'created') {
|
|
506
|
+
console.log(`配置:openspec/config.yaml(架构:${DEFAULT_SCHEMA})`);
|
|
507
|
+
}
|
|
508
|
+
else if (configStatus === 'exists') {
|
|
509
|
+
// Show actual filename (config.yaml or config.yml)
|
|
510
|
+
const configYaml = path.join(projectPath, OPENSPEC_DIR_NAME, 'config.yaml');
|
|
511
|
+
const configYml = path.join(projectPath, OPENSPEC_DIR_NAME, 'config.yml');
|
|
512
|
+
const configName = fs.existsSync(configYaml) ? 'config.yaml' : fs.existsSync(configYml) ? 'config.yml' : 'config.yaml';
|
|
513
|
+
console.log(`配置:openspec/${configName}(已存在)`);
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
console.log(chalk.dim(`配置:已跳过(非交互模式)`));
|
|
517
|
+
}
|
|
518
|
+
// Getting started (task 7.6: show propose if in profile)
|
|
519
|
+
const globalCfg = getGlobalConfig();
|
|
520
|
+
const activeProfile = this.profileOverride ?? globalCfg.profile ?? 'core';
|
|
521
|
+
const activeWorkflows = [...getProfileWorkflows(activeProfile, globalCfg.workflows)];
|
|
522
|
+
console.log();
|
|
523
|
+
if (activeWorkflows.includes('propose')) {
|
|
524
|
+
console.log(chalk.bold('快速上手:'));
|
|
525
|
+
console.log(' 开始你的第一个变更: /opsx:propose "你的想法"');
|
|
526
|
+
}
|
|
527
|
+
else if (activeWorkflows.includes('new')) {
|
|
528
|
+
console.log(chalk.bold('快速上手:'));
|
|
529
|
+
console.log(' 开始你的第一个变更: /opsx:new "你的想法"');
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
console.log("完成。运行 'openspec config profile' 配置你的工作流。");
|
|
533
|
+
}
|
|
534
|
+
// Links
|
|
535
|
+
console.log();
|
|
536
|
+
console.log(`了解更多: ${chalk.cyan('https://github.com/Fission-AI/OpenSpec')}`);
|
|
537
|
+
console.log(`反馈: ${chalk.cyan('https://github.com/Fission-AI/OpenSpec/issues')}`);
|
|
538
|
+
// Restart instruction if any tools were configured
|
|
539
|
+
if (results.createdTools.length > 0 || results.refreshedTools.length > 0) {
|
|
540
|
+
console.log();
|
|
541
|
+
console.log(chalk.white('重启你的 IDE 以使斜杠命令生效。'));
|
|
542
|
+
}
|
|
543
|
+
console.log();
|
|
544
|
+
}
|
|
545
|
+
startSpinner(text) {
|
|
546
|
+
return ora({
|
|
547
|
+
text,
|
|
548
|
+
stream: process.stdout,
|
|
549
|
+
color: 'gray',
|
|
550
|
+
spinner: PROGRESS_SPINNER,
|
|
551
|
+
}).start();
|
|
552
|
+
}
|
|
553
|
+
async removeSkillDirs(skillsDir) {
|
|
554
|
+
let removed = 0;
|
|
555
|
+
for (const workflow of ALL_WORKFLOWS) {
|
|
556
|
+
const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
|
|
557
|
+
if (!dirName)
|
|
558
|
+
continue;
|
|
559
|
+
const skillDir = path.join(skillsDir, dirName);
|
|
560
|
+
try {
|
|
561
|
+
if (fs.existsSync(skillDir)) {
|
|
562
|
+
await fs.promises.rm(skillDir, { recursive: true, force: true });
|
|
563
|
+
removed++;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
// Ignore errors
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return removed;
|
|
571
|
+
}
|
|
572
|
+
async removeCommandFiles(projectPath, toolId) {
|
|
573
|
+
let removed = 0;
|
|
574
|
+
const adapter = CommandAdapterRegistry.get(toolId);
|
|
575
|
+
if (!adapter)
|
|
576
|
+
return 0;
|
|
577
|
+
for (const workflow of ALL_WORKFLOWS) {
|
|
578
|
+
const cmdPath = adapter.getFilePath(workflow);
|
|
579
|
+
const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
|
|
580
|
+
try {
|
|
581
|
+
if (fs.existsSync(fullPath)) {
|
|
582
|
+
await fs.promises.unlink(fullPath);
|
|
583
|
+
removed++;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
catch {
|
|
587
|
+
// Ignore errors
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return removed;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
//# sourceMappingURL=init.js.map
|