@dynamicworks/br-openspec 1.3.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 +210 -0
- package/README.pt-BR.md +212 -0
- package/bin/openspec.js +3 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +484 -0
- package/dist/commands/change.d.ts +35 -0
- package/dist/commands/change.js +278 -0
- package/dist/commands/completion.d.ts +72 -0
- package/dist/commands/completion.js +258 -0
- package/dist/commands/config.d.ts +36 -0
- package/dist/commands/config.js +553 -0
- package/dist/commands/feedback.d.ts +9 -0
- package/dist/commands/feedback.js +184 -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 +133 -0
- package/dist/commands/spec.d.ts +15 -0
- package/dist/commands/spec.js +226 -0
- package/dist/commands/tools.d.ts +11 -0
- package/dist/commands/tools.js +252 -0
- package/dist/commands/validate.d.ts +24 -0
- package/dist/commands/validate.js +295 -0
- package/dist/commands/workflow/index.d.ts +17 -0
- package/dist/commands/workflow/index.js +12 -0
- package/dist/commands/workflow/instructions.d.ts +29 -0
- package/dist/commands/workflow/instructions.js +328 -0
- package/dist/commands/workflow/new-change.d.ts +11 -0
- package/dist/commands/workflow/new-change.js +44 -0
- package/dist/commands/workflow/schemas.d.ts +10 -0
- package/dist/commands/workflow/schemas.js +35 -0
- package/dist/commands/workflow/shared.d.ts +57 -0
- package/dist/commands/workflow/shared.js +117 -0
- package/dist/commands/workflow/status.d.ts +14 -0
- package/dist/commands/workflow/status.js +76 -0
- package/dist/commands/workflow/templates.d.ts +16 -0
- package/dist/commands/workflow/templates.js +70 -0
- package/dist/core/archive.d.ts +11 -0
- package/dist/core/archive.js +322 -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 +8 -0
- package/dist/core/artifact-graph/index.js +14 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +143 -0
- package/dist/core/artifact-graph/instruction-loader.js +217 -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 +258 -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 +45 -0
- package/dist/core/artifact-graph/types.js +43 -0
- package/dist/core/available-tools.d.ts +17 -0
- package/dist/core/available-tools.js +43 -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 +7 -0
- package/dist/core/completions/command-registry.js +462 -0
- package/dist/core/completions/completion-provider.d.ts +60 -0
- package/dist/core/completions/completion-provider.js +102 -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 +32 -0
- package/dist/core/completions/generators/bash-generator.js +174 -0
- package/dist/core/completions/generators/fish-generator.d.ts +32 -0
- package/dist/core/completions/generators/fish-generator.js +157 -0
- package/dist/core/completions/generators/powershell-generator.d.ts +33 -0
- package/dist/core/completions/generators/powershell-generator.js +208 -0
- package/dist/core/completions/generators/zsh-generator.d.ts +44 -0
- package/dist/core/completions/generators/zsh-generator.js +250 -0
- package/dist/core/completions/installers/bash-installer.d.ts +87 -0
- package/dist/core/completions/installers/bash-installer.js +319 -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 +400 -0
- package/dist/core/completions/installers/zsh-installer.d.ts +125 -0
- package/dist/core/completions/installers/zsh-installer.js +450 -0
- package/dist/core/completions/templates/bash-templates.d.ts +6 -0
- package/dist/core/completions/templates/bash-templates.js +24 -0
- package/dist/core/completions/templates/fish-templates.d.ts +7 -0
- package/dist/core/completions/templates/fish-templates.js +39 -0
- package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
- package/dist/core/completions/templates/powershell-templates.js +25 -0
- package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
- package/dist/core/completions/templates/zsh-templates.js +36 -0
- package/dist/core/completions/types.d.ts +79 -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 +38 -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 +44 -0
- package/dist/core/global-config.js +125 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +3 -0
- package/dist/core/init.d.ts +37 -0
- package/dist/core/init.js +549 -0
- package/dist/core/is-project-initialized.d.ts +12 -0
- package/dist/core/is-project-initialized.js +18 -0
- package/dist/core/legacy-cleanup.d.ts +162 -0
- package/dist/core/legacy-cleanup.js +515 -0
- package/dist/core/list.d.ts +9 -0
- package/dist/core/list.js +172 -0
- package/dist/core/migration.d.ts +23 -0
- package/dist/core/migration.js +109 -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 +228 -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/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 +224 -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 +393 -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 +20 -0
- package/dist/core/templates/skill-templates.js +19 -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 +308 -0
- package/dist/core/templates/workflows/archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/archive-change.js +271 -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 +232 -0
- package/dist/core/templates/workflows/explore.d.ts +10 -0
- package/dist/core/templates/workflows/explore.js +463 -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 +198 -0
- package/dist/core/templates/workflows/new-change.d.ts +10 -0
- package/dist/core/templates/workflows/new-change.js +21 -0
- package/dist/core/templates/workflows/onboard.d.ts +10 -0
- package/dist/core/templates/workflows/onboard.js +21 -0
- package/dist/core/templates/workflows/propose.d.ts +10 -0
- package/dist/core/templates/workflows/propose.js +216 -0
- package/dist/core/templates/workflows/sync-specs.d.ts +10 -0
- package/dist/core/templates/workflows/sync-specs.js +272 -0
- package/dist/core/templates/workflows/upstream-sync.d.ts +10 -0
- package/dist/core/templates/workflows/upstream-sync.js +116 -0
- package/dist/core/templates/workflows/verify-change.d.ts +10 -0
- package/dist/core/templates/workflows/verify-change.js +21 -0
- package/dist/core/tools-manager.d.ts +56 -0
- package/dist/core/tools-manager.js +215 -0
- package/dist/core/update.d.ts +77 -0
- package/dist/core/update.js +538 -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 +33 -0
- package/dist/core/validation/validator.js +419 -0
- package/dist/core/view.d.ts +8 -0
- package/dist/core/view.js +169 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/messages/index.d.ts +867 -0
- package/dist/messages/index.js +1960 -0
- package/dist/prompts/searchable-multi-select.d.ts +28 -0
- package/dist/prompts/searchable-multi-select.js +160 -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 +165 -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 +147 -0
- package/dist/utils/change-metadata.d.ts +51 -0
- package/dist/utils/change-metadata.js +147 -0
- package/dist/utils/change-utils.d.ts +62 -0
- package/dist/utils/change-utils.js +121 -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 +302 -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 +37 -0
- package/package.json +84 -0
- package/schemas/spec-driven/schema.yaml +153 -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/scripts/postinstall.js +83 -0
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
type InitCommandOptions = {
|
|
8
|
+
tools?: string;
|
|
9
|
+
force?: boolean;
|
|
10
|
+
interactive?: boolean;
|
|
11
|
+
profile?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare class InitCommand {
|
|
14
|
+
private readonly toolsArg?;
|
|
15
|
+
private readonly force;
|
|
16
|
+
private readonly interactiveOption?;
|
|
17
|
+
private readonly profileOverride?;
|
|
18
|
+
constructor(options?: InitCommandOptions);
|
|
19
|
+
execute(targetPath: string): Promise<void>;
|
|
20
|
+
private validate;
|
|
21
|
+
private canPromptInteractively;
|
|
22
|
+
private resolveProfileOverride;
|
|
23
|
+
private handleLegacyCleanup;
|
|
24
|
+
private performLegacyCleanup;
|
|
25
|
+
private getSelectedTools;
|
|
26
|
+
private resolveToolsArg;
|
|
27
|
+
private validateTools;
|
|
28
|
+
private createDirectoryStructure;
|
|
29
|
+
private generateSkillsAndCommands;
|
|
30
|
+
private createConfig;
|
|
31
|
+
private displaySuccessMessage;
|
|
32
|
+
private startSpinner;
|
|
33
|
+
private removeSkillDirs;
|
|
34
|
+
private removeCommandFiles;
|
|
35
|
+
}
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1,549 @@
|
|
|
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 { INIT_MESSAGES } from '../messages/index.js';
|
|
18
|
+
import { serializeConfig } from './config-prompts.js';
|
|
19
|
+
import { generateCommands, CommandAdapterRegistry, } from './command-generation/index.js';
|
|
20
|
+
import { removeOpenSpecSkillDirs, removeOpenSpecCommandFiles, } from './tools-manager.js';
|
|
21
|
+
import { detectLegacyArtifacts, cleanupLegacyArtifacts, formatCleanupSummary, formatDetectionSummary, } from './legacy-cleanup.js';
|
|
22
|
+
import { getToolsWithSkillsDir, getToolStates, getSkillTemplates, getCommandContents, generateSkillContent, } from './shared/index.js';
|
|
23
|
+
import { getGlobalConfig } from './global-config.js';
|
|
24
|
+
import { getProfileWorkflows } from './profiles.js';
|
|
25
|
+
import { getAvailableTools } from './available-tools.js';
|
|
26
|
+
import { migrateIfNeeded } from './migration.js';
|
|
27
|
+
const require = createRequire(import.meta.url);
|
|
28
|
+
const { version: OPENSPEC_VERSION } = require('../../package.json');
|
|
29
|
+
// -----------------------------------------------------------------------------
|
|
30
|
+
// Constants
|
|
31
|
+
// -----------------------------------------------------------------------------
|
|
32
|
+
const DEFAULT_SCHEMA = 'spec-driven';
|
|
33
|
+
const PROGRESS_SPINNER = {
|
|
34
|
+
interval: 80,
|
|
35
|
+
frames: ['░░░', '▒░░', '▒▒░', '▒▒▒', '▓▒▒', '▓▓▒', '▓▓▓', '▒▓▓', '░▒▓'],
|
|
36
|
+
};
|
|
37
|
+
// -----------------------------------------------------------------------------
|
|
38
|
+
// Init Command Class
|
|
39
|
+
// -----------------------------------------------------------------------------
|
|
40
|
+
export class InitCommand {
|
|
41
|
+
toolsArg;
|
|
42
|
+
force;
|
|
43
|
+
interactiveOption;
|
|
44
|
+
profileOverride;
|
|
45
|
+
constructor(options = {}) {
|
|
46
|
+
this.toolsArg = options.tools;
|
|
47
|
+
this.force = options.force ?? false;
|
|
48
|
+
this.interactiveOption = options.interactive;
|
|
49
|
+
this.profileOverride = options.profile;
|
|
50
|
+
}
|
|
51
|
+
async execute(targetPath) {
|
|
52
|
+
const projectPath = path.resolve(targetPath);
|
|
53
|
+
const openspecDir = OPENSPEC_DIR_NAME;
|
|
54
|
+
const openspecPath = path.join(projectPath, openspecDir);
|
|
55
|
+
// Validation happens silently in the background
|
|
56
|
+
const extendMode = await this.validate(projectPath, openspecPath);
|
|
57
|
+
// Check for legacy artifacts and handle cleanup
|
|
58
|
+
await this.handleLegacyCleanup(projectPath, extendMode);
|
|
59
|
+
// Detect available tools in the project (task 7.1)
|
|
60
|
+
const detectedTools = getAvailableTools(projectPath);
|
|
61
|
+
// Migration check: migrate existing projects to profile system (task 7.3)
|
|
62
|
+
if (extendMode) {
|
|
63
|
+
migrateIfNeeded(projectPath, detectedTools);
|
|
64
|
+
}
|
|
65
|
+
// Show animated welcome screen (interactive mode only)
|
|
66
|
+
const canPrompt = this.canPromptInteractively();
|
|
67
|
+
if (canPrompt) {
|
|
68
|
+
const { showWelcomeScreen } = await import('../ui/welcome-screen.js');
|
|
69
|
+
await showWelcomeScreen();
|
|
70
|
+
}
|
|
71
|
+
// Validate profile override early so invalid values fail before tool setup.
|
|
72
|
+
// The resolved value is consumed later when generation reads effective config.
|
|
73
|
+
this.resolveProfileOverride();
|
|
74
|
+
// Get tool states before processing
|
|
75
|
+
const toolStates = getToolStates(projectPath);
|
|
76
|
+
// Get tool selection (pass detected tools for pre-selection)
|
|
77
|
+
const selectedToolIds = await this.getSelectedTools(toolStates, extendMode, detectedTools, projectPath);
|
|
78
|
+
// Validate selected tools
|
|
79
|
+
const validatedTools = this.validateTools(selectedToolIds, toolStates);
|
|
80
|
+
// Create directory structure and config
|
|
81
|
+
await this.createDirectoryStructure(openspecPath, extendMode);
|
|
82
|
+
// Generate skills and commands for each tool
|
|
83
|
+
const results = await this.generateSkillsAndCommands(projectPath, validatedTools);
|
|
84
|
+
// Create config.yaml if needed
|
|
85
|
+
const configStatus = await this.createConfig(openspecPath, extendMode);
|
|
86
|
+
// Display success message
|
|
87
|
+
this.displaySuccessMessage(projectPath, validatedTools, results, configStatus);
|
|
88
|
+
}
|
|
89
|
+
// ═══════════════════════════════════════════════════════════
|
|
90
|
+
// VALIDATION & SETUP
|
|
91
|
+
// ═══════════════════════════════════════════════════════════
|
|
92
|
+
async validate(projectPath, openspecPath) {
|
|
93
|
+
const extendMode = await FileSystemUtils.directoryExists(openspecPath);
|
|
94
|
+
// Check write permissions
|
|
95
|
+
if (!(await FileSystemUtils.ensureWritePermissions(projectPath))) {
|
|
96
|
+
throw new Error(INIT_MESSAGES.insufficientPermissions(projectPath));
|
|
97
|
+
}
|
|
98
|
+
return extendMode;
|
|
99
|
+
}
|
|
100
|
+
canPromptInteractively() {
|
|
101
|
+
if (this.interactiveOption === false)
|
|
102
|
+
return false;
|
|
103
|
+
if (this.toolsArg !== undefined)
|
|
104
|
+
return false;
|
|
105
|
+
return isInteractive({ interactive: this.interactiveOption });
|
|
106
|
+
}
|
|
107
|
+
resolveProfileOverride() {
|
|
108
|
+
if (this.profileOverride === undefined) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
if (this.profileOverride === 'core' || this.profileOverride === 'custom') {
|
|
112
|
+
return this.profileOverride;
|
|
113
|
+
}
|
|
114
|
+
throw new Error(INIT_MESSAGES.invalidProfile(this.profileOverride));
|
|
115
|
+
}
|
|
116
|
+
// ═══════════════════════════════════════════════════════════
|
|
117
|
+
// LEGACY CLEANUP
|
|
118
|
+
// ═══════════════════════════════════════════════════════════
|
|
119
|
+
async handleLegacyCleanup(projectPath, extendMode) {
|
|
120
|
+
// Detect legacy artifacts
|
|
121
|
+
const detection = await detectLegacyArtifacts(projectPath);
|
|
122
|
+
if (!detection.hasLegacyArtifacts) {
|
|
123
|
+
return; // No legacy artifacts found
|
|
124
|
+
}
|
|
125
|
+
// Show what was detected
|
|
126
|
+
console.log();
|
|
127
|
+
console.log(formatDetectionSummary(detection));
|
|
128
|
+
console.log();
|
|
129
|
+
const canPrompt = this.canPromptInteractively();
|
|
130
|
+
if (this.force || !canPrompt) {
|
|
131
|
+
// --force flag or non-interactive mode: proceed with cleanup automatically.
|
|
132
|
+
// Legacy slash commands are 100% OpenSpec-managed, and config file cleanup
|
|
133
|
+
// only removes markers (never deletes files), so auto-cleanup is safe.
|
|
134
|
+
await this.performLegacyCleanup(projectPath, detection);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
// Interactive mode: prompt for confirmation
|
|
138
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
139
|
+
const shouldCleanup = await confirm({
|
|
140
|
+
message: INIT_MESSAGES.upgradeLegacyPrompt,
|
|
141
|
+
default: true,
|
|
142
|
+
});
|
|
143
|
+
if (!shouldCleanup) {
|
|
144
|
+
console.log(chalk.dim(INIT_MESSAGES.initializationCancelled));
|
|
145
|
+
console.log(chalk.dim(INIT_MESSAGES.skipPromptHint));
|
|
146
|
+
process.exit(0);
|
|
147
|
+
}
|
|
148
|
+
await this.performLegacyCleanup(projectPath, detection);
|
|
149
|
+
}
|
|
150
|
+
async performLegacyCleanup(projectPath, detection) {
|
|
151
|
+
const spinner = ora(INIT_MESSAGES.cleaningLegacy).start();
|
|
152
|
+
const result = await cleanupLegacyArtifacts(projectPath, detection);
|
|
153
|
+
spinner.succeed(INIT_MESSAGES.legacyCleaned);
|
|
154
|
+
const summary = formatCleanupSummary(result);
|
|
155
|
+
if (summary) {
|
|
156
|
+
console.log();
|
|
157
|
+
console.log(summary);
|
|
158
|
+
}
|
|
159
|
+
console.log();
|
|
160
|
+
}
|
|
161
|
+
// ═══════════════════════════════════════════════════════════
|
|
162
|
+
// TOOL SELECTION
|
|
163
|
+
// ═══════════════════════════════════════════════════════════
|
|
164
|
+
async getSelectedTools(toolStates, extendMode, detectedTools, projectPath) {
|
|
165
|
+
// Check for --tools flag first
|
|
166
|
+
const nonInteractiveSelection = this.resolveToolsArg();
|
|
167
|
+
if (nonInteractiveSelection !== null) {
|
|
168
|
+
return nonInteractiveSelection;
|
|
169
|
+
}
|
|
170
|
+
const validTools = getToolsWithSkillsDir();
|
|
171
|
+
const detectedToolIds = new Set(detectedTools.map((t) => t.value));
|
|
172
|
+
const configuredToolIds = new Set([...toolStates.entries()]
|
|
173
|
+
.filter(([, status]) => status.configured)
|
|
174
|
+
.map(([toolId]) => toolId));
|
|
175
|
+
const shouldPreselectDetected = !extendMode && configuredToolIds.size === 0;
|
|
176
|
+
const canPrompt = this.canPromptInteractively();
|
|
177
|
+
// Non-interactive mode: use detected tools as fallback (task 7.8)
|
|
178
|
+
if (!canPrompt) {
|
|
179
|
+
if (detectedToolIds.size > 0) {
|
|
180
|
+
return [...detectedToolIds];
|
|
181
|
+
}
|
|
182
|
+
throw new Error(INIT_MESSAGES.noToolsDetected(validTools.join('\n ')));
|
|
183
|
+
}
|
|
184
|
+
if (validTools.length === 0) {
|
|
185
|
+
throw new Error(INIT_MESSAGES.noToolsAvailable);
|
|
186
|
+
}
|
|
187
|
+
// Interactive mode: show searchable multi-select
|
|
188
|
+
const { searchableMultiSelect } = await import('../prompts/searchable-multi-select.js');
|
|
189
|
+
// Build choices: pre-select configured tools; keep detected tools visible but unselected.
|
|
190
|
+
const sortedChoices = validTools
|
|
191
|
+
.map((toolId) => {
|
|
192
|
+
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
193
|
+
const status = toolStates.get(toolId);
|
|
194
|
+
const configured = status?.configured ?? false;
|
|
195
|
+
const detected = detectedToolIds.has(toolId);
|
|
196
|
+
return {
|
|
197
|
+
name: tool?.name || toolId,
|
|
198
|
+
value: toolId,
|
|
199
|
+
configured,
|
|
200
|
+
detected: detected && !configured,
|
|
201
|
+
preSelected: configured || (shouldPreselectDetected && detected && !configured),
|
|
202
|
+
};
|
|
203
|
+
})
|
|
204
|
+
.sort((a, b) => {
|
|
205
|
+
// Configured tools first, then detected (not configured), then everything else.
|
|
206
|
+
if (a.configured && !b.configured)
|
|
207
|
+
return -1;
|
|
208
|
+
if (!a.configured && b.configured)
|
|
209
|
+
return 1;
|
|
210
|
+
if (a.detected && !b.detected)
|
|
211
|
+
return -1;
|
|
212
|
+
if (!a.detected && b.detected)
|
|
213
|
+
return 1;
|
|
214
|
+
return 0;
|
|
215
|
+
});
|
|
216
|
+
const configuredNames = validTools
|
|
217
|
+
.filter((toolId) => configuredToolIds.has(toolId))
|
|
218
|
+
.map((toolId) => AI_TOOLS.find((t) => t.value === toolId)?.name || toolId);
|
|
219
|
+
if (configuredNames.length > 0) {
|
|
220
|
+
console.log(INIT_MESSAGES.configuredPreselected(configuredNames.join(', ')));
|
|
221
|
+
}
|
|
222
|
+
const detectedOnlyNames = detectedTools
|
|
223
|
+
.filter((tool) => !configuredToolIds.has(tool.value))
|
|
224
|
+
.map((tool) => tool.name);
|
|
225
|
+
if (detectedOnlyNames.length > 0) {
|
|
226
|
+
const detectionLabel = shouldPreselectDetected
|
|
227
|
+
? INIT_MESSAGES.preselectedFirstTime
|
|
228
|
+
: INIT_MESSAGES.notPreselected;
|
|
229
|
+
console.log(INIT_MESSAGES.detectedToolsLabel(detectedOnlyNames.join(', '), detectionLabel));
|
|
230
|
+
}
|
|
231
|
+
const selectedTools = await searchableMultiSelect({
|
|
232
|
+
message: INIT_MESSAGES.selectToolsPrompt(validTools.length),
|
|
233
|
+
pageSize: 15,
|
|
234
|
+
choices: sortedChoices,
|
|
235
|
+
validate: (selected) => selected.length > 0 || INIT_MESSAGES.selectAtLeastOneTool,
|
|
236
|
+
});
|
|
237
|
+
if (selectedTools.length === 0) {
|
|
238
|
+
throw new Error(INIT_MESSAGES.atLeastOneToolRequired);
|
|
239
|
+
}
|
|
240
|
+
return selectedTools;
|
|
241
|
+
}
|
|
242
|
+
resolveToolsArg() {
|
|
243
|
+
if (typeof this.toolsArg === 'undefined') {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
const raw = this.toolsArg.trim();
|
|
247
|
+
if (raw.length === 0) {
|
|
248
|
+
throw new Error(INIT_MESSAGES.toolsOptionRequired);
|
|
249
|
+
}
|
|
250
|
+
const availableTools = getToolsWithSkillsDir();
|
|
251
|
+
const availableSet = new Set(availableTools);
|
|
252
|
+
const availableList = ['all', 'none', ...availableTools].join(', ');
|
|
253
|
+
const lowerRaw = raw.toLowerCase();
|
|
254
|
+
if (lowerRaw === 'all') {
|
|
255
|
+
return availableTools;
|
|
256
|
+
}
|
|
257
|
+
if (lowerRaw === 'none') {
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
const tokens = raw
|
|
261
|
+
.split(',')
|
|
262
|
+
.map((token) => token.trim())
|
|
263
|
+
.filter((token) => token.length > 0);
|
|
264
|
+
if (tokens.length === 0) {
|
|
265
|
+
throw new Error(INIT_MESSAGES.toolsOptionRequiresToolId);
|
|
266
|
+
}
|
|
267
|
+
const normalizedTokens = tokens.map((token) => token.toLowerCase());
|
|
268
|
+
if (normalizedTokens.some((token) => token === 'all' || token === 'none')) {
|
|
269
|
+
throw new Error(INIT_MESSAGES.cannotCombineReservedValues);
|
|
270
|
+
}
|
|
271
|
+
const invalidTokens = tokens.filter((_token, index) => !availableSet.has(normalizedTokens[index]));
|
|
272
|
+
if (invalidTokens.length > 0) {
|
|
273
|
+
throw new Error(INIT_MESSAGES.invalidTools(invalidTokens.join(', '), availableList));
|
|
274
|
+
}
|
|
275
|
+
// Deduplicate while preserving order
|
|
276
|
+
const deduped = [];
|
|
277
|
+
for (const token of normalizedTokens) {
|
|
278
|
+
if (!deduped.includes(token)) {
|
|
279
|
+
deduped.push(token);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return deduped;
|
|
283
|
+
}
|
|
284
|
+
validateTools(toolIds, toolStates) {
|
|
285
|
+
const validatedTools = [];
|
|
286
|
+
for (const toolId of toolIds) {
|
|
287
|
+
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
288
|
+
if (!tool) {
|
|
289
|
+
const validToolIds = getToolsWithSkillsDir();
|
|
290
|
+
throw new Error(INIT_MESSAGES.unknownTool(toolId, validToolIds.join('\n ')));
|
|
291
|
+
}
|
|
292
|
+
if (!tool.skillsDir) {
|
|
293
|
+
const validToolsWithSkills = getToolsWithSkillsDir();
|
|
294
|
+
throw new Error(INIT_MESSAGES.toolNoSkillSupport(toolId, validToolsWithSkills.join('\n ')));
|
|
295
|
+
}
|
|
296
|
+
const preState = toolStates.get(tool.value);
|
|
297
|
+
validatedTools.push({
|
|
298
|
+
value: tool.value,
|
|
299
|
+
name: tool.name,
|
|
300
|
+
skillsDir: tool.skillsDir,
|
|
301
|
+
wasConfigured: preState?.configured ?? false,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
return validatedTools;
|
|
305
|
+
}
|
|
306
|
+
// ═══════════════════════════════════════════════════════════
|
|
307
|
+
// DIRECTORY STRUCTURE
|
|
308
|
+
// ═══════════════════════════════════════════════════════════
|
|
309
|
+
async createDirectoryStructure(openspecPath, extendMode) {
|
|
310
|
+
if (extendMode) {
|
|
311
|
+
// In extend mode, just ensure directories exist without spinner
|
|
312
|
+
const directories = [
|
|
313
|
+
openspecPath,
|
|
314
|
+
path.join(openspecPath, 'specs'),
|
|
315
|
+
path.join(openspecPath, 'changes'),
|
|
316
|
+
path.join(openspecPath, 'changes', 'archive'),
|
|
317
|
+
];
|
|
318
|
+
for (const dir of directories) {
|
|
319
|
+
await FileSystemUtils.createDirectory(dir);
|
|
320
|
+
}
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const spinner = this.startSpinner(INIT_MESSAGES.creatingStructure);
|
|
324
|
+
const directories = [
|
|
325
|
+
openspecPath,
|
|
326
|
+
path.join(openspecPath, 'specs'),
|
|
327
|
+
path.join(openspecPath, 'changes'),
|
|
328
|
+
path.join(openspecPath, 'changes', 'archive'),
|
|
329
|
+
];
|
|
330
|
+
for (const dir of directories) {
|
|
331
|
+
await FileSystemUtils.createDirectory(dir);
|
|
332
|
+
}
|
|
333
|
+
spinner.stopAndPersist({
|
|
334
|
+
symbol: PALETTE.white('▌'),
|
|
335
|
+
text: PALETTE.white(INIT_MESSAGES.structureCreated),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// ═══════════════════════════════════════════════════════════
|
|
339
|
+
// SKILL & COMMAND GENERATION
|
|
340
|
+
// ═══════════════════════════════════════════════════════════
|
|
341
|
+
async generateSkillsAndCommands(projectPath, tools) {
|
|
342
|
+
const createdTools = [];
|
|
343
|
+
const refreshedTools = [];
|
|
344
|
+
const failedTools = [];
|
|
345
|
+
const commandsSkipped = [];
|
|
346
|
+
let removedCommandCount = 0;
|
|
347
|
+
let removedSkillCount = 0;
|
|
348
|
+
// Read global config for profile and delivery settings (use --profile override if set)
|
|
349
|
+
const globalConfig = getGlobalConfig();
|
|
350
|
+
const profile = this.resolveProfileOverride() ?? globalConfig.profile ?? 'core';
|
|
351
|
+
const delivery = globalConfig.delivery ?? 'both';
|
|
352
|
+
const workflows = getProfileWorkflows(profile, globalConfig.workflows);
|
|
353
|
+
// Get skill and command templates filtered by profile workflows
|
|
354
|
+
const shouldGenerateSkills = delivery !== 'commands';
|
|
355
|
+
const shouldGenerateCommands = delivery !== 'skills';
|
|
356
|
+
const skillTemplates = shouldGenerateSkills ? getSkillTemplates(workflows) : [];
|
|
357
|
+
const commandContents = shouldGenerateCommands ? getCommandContents(workflows) : [];
|
|
358
|
+
// Process each tool
|
|
359
|
+
for (const tool of tools) {
|
|
360
|
+
const spinner = ora(INIT_MESSAGES.settingUp(tool.name)).start();
|
|
361
|
+
try {
|
|
362
|
+
// Generate skill files if delivery includes skills
|
|
363
|
+
if (shouldGenerateSkills) {
|
|
364
|
+
// Use tool-specific skillsDir
|
|
365
|
+
const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
|
|
366
|
+
// Create skill directories and SKILL.md files
|
|
367
|
+
for (const { template, dirName } of skillTemplates) {
|
|
368
|
+
const skillDir = path.join(skillsDir, dirName);
|
|
369
|
+
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
370
|
+
// Generate SKILL.md content with YAML frontmatter including generatedBy
|
|
371
|
+
// Use hyphen-based command references for tools where filename = command name
|
|
372
|
+
const transformer = (tool.value === 'opencode' || tool.value === 'pi') ? transformToHyphenCommands : undefined;
|
|
373
|
+
const skillContent = generateSkillContent(template, OPENSPEC_VERSION, transformer);
|
|
374
|
+
// Write the skill file
|
|
375
|
+
await FileSystemUtils.writeFile(skillFile, skillContent);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (!shouldGenerateSkills) {
|
|
379
|
+
const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
|
|
380
|
+
removedSkillCount += await this.removeSkillDirs(skillsDir);
|
|
381
|
+
}
|
|
382
|
+
// Generate commands if delivery includes commands
|
|
383
|
+
if (shouldGenerateCommands) {
|
|
384
|
+
const adapter = CommandAdapterRegistry.get(tool.value);
|
|
385
|
+
if (adapter) {
|
|
386
|
+
const generatedCommands = generateCommands(commandContents, adapter);
|
|
387
|
+
for (const cmd of generatedCommands) {
|
|
388
|
+
const commandFile = path.isAbsolute(cmd.path) ? cmd.path : path.join(projectPath, cmd.path);
|
|
389
|
+
await FileSystemUtils.writeFile(commandFile, cmd.fileContent);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
commandsSkipped.push(tool.value);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (!shouldGenerateCommands) {
|
|
397
|
+
removedCommandCount += await this.removeCommandFiles(projectPath, tool.value);
|
|
398
|
+
}
|
|
399
|
+
spinner.succeed(INIT_MESSAGES.setupComplete(tool.name));
|
|
400
|
+
if (tool.wasConfigured) {
|
|
401
|
+
refreshedTools.push(tool);
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
createdTools.push(tool);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
spinner.fail(INIT_MESSAGES.setupFailed(tool.name));
|
|
409
|
+
failedTools.push({ name: tool.name, error: error });
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
createdTools,
|
|
414
|
+
refreshedTools,
|
|
415
|
+
failedTools,
|
|
416
|
+
commandsSkipped,
|
|
417
|
+
removedCommandCount,
|
|
418
|
+
removedSkillCount,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
// ═══════════════════════════════════════════════════════════
|
|
422
|
+
// CONFIG FILE
|
|
423
|
+
// ═══════════════════════════════════════════════════════════
|
|
424
|
+
async createConfig(openspecPath, extendMode) {
|
|
425
|
+
const configPath = path.join(openspecPath, 'config.yaml');
|
|
426
|
+
const configYmlPath = path.join(openspecPath, 'config.yml');
|
|
427
|
+
const configYamlExists = fs.existsSync(configPath);
|
|
428
|
+
const configYmlExists = fs.existsSync(configYmlPath);
|
|
429
|
+
if (configYamlExists || configYmlExists) {
|
|
430
|
+
return 'exists';
|
|
431
|
+
}
|
|
432
|
+
// In non-interactive mode without --force, skip config creation
|
|
433
|
+
if (!this.canPromptInteractively() && !this.force) {
|
|
434
|
+
return 'skipped';
|
|
435
|
+
}
|
|
436
|
+
try {
|
|
437
|
+
const yamlContent = serializeConfig({ schema: DEFAULT_SCHEMA });
|
|
438
|
+
await FileSystemUtils.writeFile(configPath, yamlContent);
|
|
439
|
+
return 'created';
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
return 'skipped';
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
// ═══════════════════════════════════════════════════════════
|
|
446
|
+
// UI & OUTPUT
|
|
447
|
+
// ═══════════════════════════════════════════════════════════
|
|
448
|
+
displaySuccessMessage(projectPath, tools, results, configStatus) {
|
|
449
|
+
console.log();
|
|
450
|
+
console.log(chalk.bold(INIT_MESSAGES.setupCompleteTitle));
|
|
451
|
+
console.log();
|
|
452
|
+
// Show created vs refreshed tools
|
|
453
|
+
if (results.createdTools.length > 0) {
|
|
454
|
+
console.log(INIT_MESSAGES.created(results.createdTools.map((t) => t.name).join(', ')));
|
|
455
|
+
}
|
|
456
|
+
if (results.refreshedTools.length > 0) {
|
|
457
|
+
console.log(INIT_MESSAGES.refreshed(results.refreshedTools.map((t) => t.name).join(', ')));
|
|
458
|
+
}
|
|
459
|
+
// Show counts (respecting profile filter)
|
|
460
|
+
const successfulTools = [...results.createdTools, ...results.refreshedTools];
|
|
461
|
+
if (successfulTools.length > 0) {
|
|
462
|
+
const globalConfig = getGlobalConfig();
|
|
463
|
+
const profile = this.profileOverride ?? globalConfig.profile ?? 'core';
|
|
464
|
+
const delivery = globalConfig.delivery ?? 'both';
|
|
465
|
+
const workflows = getProfileWorkflows(profile, globalConfig.workflows);
|
|
466
|
+
const toolDirs = [...new Set(successfulTools.map((t) => t.skillsDir))].join(', ');
|
|
467
|
+
const skillCount = delivery !== 'commands' ? getSkillTemplates(workflows).length : 0;
|
|
468
|
+
const commandCount = delivery !== 'skills' ? getCommandContents(workflows).length : 0;
|
|
469
|
+
if (skillCount > 0 && commandCount > 0) {
|
|
470
|
+
console.log(INIT_MESSAGES.skillsAndCommandsCount(skillCount, commandCount, toolDirs));
|
|
471
|
+
}
|
|
472
|
+
else if (skillCount > 0) {
|
|
473
|
+
console.log(INIT_MESSAGES.skillsCount(skillCount, toolDirs));
|
|
474
|
+
}
|
|
475
|
+
else if (commandCount > 0) {
|
|
476
|
+
console.log(INIT_MESSAGES.commandsCount(commandCount, toolDirs));
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
// Show failures
|
|
480
|
+
if (results.failedTools.length > 0) {
|
|
481
|
+
console.log(chalk.red(INIT_MESSAGES.failed(results.failedTools.map((f) => `${f.name} (${f.error.message})`).join(', '))));
|
|
482
|
+
}
|
|
483
|
+
// Show skipped commands
|
|
484
|
+
if (results.commandsSkipped.length > 0) {
|
|
485
|
+
console.log(chalk.dim(INIT_MESSAGES.commandsSkipped(results.commandsSkipped.join(', '))));
|
|
486
|
+
}
|
|
487
|
+
if (results.removedCommandCount > 0) {
|
|
488
|
+
console.log(chalk.dim(INIT_MESSAGES.removedCommands(results.removedCommandCount)));
|
|
489
|
+
}
|
|
490
|
+
if (results.removedSkillCount > 0) {
|
|
491
|
+
console.log(chalk.dim(INIT_MESSAGES.removedSkills(results.removedSkillCount)));
|
|
492
|
+
}
|
|
493
|
+
// Config status
|
|
494
|
+
if (configStatus === 'created') {
|
|
495
|
+
console.log(INIT_MESSAGES.configCreated(DEFAULT_SCHEMA));
|
|
496
|
+
}
|
|
497
|
+
else if (configStatus === 'exists') {
|
|
498
|
+
// Show actual filename (config.yaml or config.yml)
|
|
499
|
+
const configYaml = path.join(projectPath, OPENSPEC_DIR_NAME, 'config.yaml');
|
|
500
|
+
const configYml = path.join(projectPath, OPENSPEC_DIR_NAME, 'config.yml');
|
|
501
|
+
const configName = fs.existsSync(configYaml) ? 'config.yaml' : fs.existsSync(configYml) ? 'config.yml' : 'config.yaml';
|
|
502
|
+
console.log(INIT_MESSAGES.configExists(configName));
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
console.log(chalk.dim(INIT_MESSAGES.configSkipped));
|
|
506
|
+
}
|
|
507
|
+
// Getting started (task 7.6: show propose if in profile)
|
|
508
|
+
const globalCfg = getGlobalConfig();
|
|
509
|
+
const activeProfile = this.profileOverride ?? globalCfg.profile ?? 'core';
|
|
510
|
+
const activeWorkflows = [...getProfileWorkflows(activeProfile, globalCfg.workflows)];
|
|
511
|
+
console.log();
|
|
512
|
+
if (activeWorkflows.includes('propose')) {
|
|
513
|
+
console.log(chalk.bold(INIT_MESSAGES.gettingStarted));
|
|
514
|
+
console.log(INIT_MESSAGES.startFirstChangePropose('/opsx:propose "sua ideia"'));
|
|
515
|
+
}
|
|
516
|
+
else if (activeWorkflows.includes('new')) {
|
|
517
|
+
console.log(chalk.bold(INIT_MESSAGES.gettingStarted));
|
|
518
|
+
console.log(INIT_MESSAGES.startFirstChangeNew('/opsx:new "sua ideia"'));
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
console.log(INIT_MESSAGES.configureWorkflowsHint);
|
|
522
|
+
}
|
|
523
|
+
// Links
|
|
524
|
+
console.log();
|
|
525
|
+
console.log(INIT_MESSAGES.learnMore(chalk.cyan('https://github.com/dynamicworks-com-br/BR-OpenSpec')));
|
|
526
|
+
console.log(INIT_MESSAGES.feedback(chalk.cyan('https://github.com/dynamicworks-com-br/BR-OpenSpec/issues')));
|
|
527
|
+
// Restart instruction if any tools were configured
|
|
528
|
+
if (results.createdTools.length > 0 || results.refreshedTools.length > 0) {
|
|
529
|
+
console.log();
|
|
530
|
+
console.log(chalk.white(INIT_MESSAGES.restartIDE));
|
|
531
|
+
}
|
|
532
|
+
console.log();
|
|
533
|
+
}
|
|
534
|
+
startSpinner(text) {
|
|
535
|
+
return ora({
|
|
536
|
+
text,
|
|
537
|
+
stream: process.stdout,
|
|
538
|
+
color: 'gray',
|
|
539
|
+
spinner: PROGRESS_SPINNER,
|
|
540
|
+
}).start();
|
|
541
|
+
}
|
|
542
|
+
async removeSkillDirs(skillsDir) {
|
|
543
|
+
return removeOpenSpecSkillDirs(skillsDir);
|
|
544
|
+
}
|
|
545
|
+
async removeCommandFiles(projectPath, toolId) {
|
|
546
|
+
return removeOpenSpecCommandFiles(projectPath, toolId);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Initialization Check
|
|
3
|
+
*
|
|
4
|
+
* Utility for checking whether a project has been initialized with OpenSpec.
|
|
5
|
+
* Used to guard commands that require an already-initialized project.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Returns true if the project at the given path has been initialized with
|
|
9
|
+
* OpenSpec (i.e., `openspec/config.yaml` or `openspec/config.yml` exists).
|
|
10
|
+
*/
|
|
11
|
+
export declare function isProjectInitialized(projectPath: string): boolean;
|
|
12
|
+
//# sourceMappingURL=is-project-initialized.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Initialization Check
|
|
3
|
+
*
|
|
4
|
+
* Utility for checking whether a project has been initialized with OpenSpec.
|
|
5
|
+
* Used to guard commands that require an already-initialized project.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
/**
|
|
10
|
+
* Returns true if the project at the given path has been initialized with
|
|
11
|
+
* OpenSpec (i.e., `openspec/config.yaml` or `openspec/config.yml` exists).
|
|
12
|
+
*/
|
|
13
|
+
export function isProjectInitialized(projectPath) {
|
|
14
|
+
const base = path.join(projectPath, 'openspec');
|
|
15
|
+
return (existsSync(path.join(base, 'config.yaml')) ||
|
|
16
|
+
existsSync(path.join(base, 'config.yml')));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=is-project-initialized.js.map
|