openspec-cn 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +153 -0
- package/bin/openspec.js +3 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +480 -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 +257 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +198 -0
- package/dist/commands/feedback.d.ts +9 -0
- package/dist/commands/feedback.js +183 -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 +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 +381 -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 +34 -0
- package/dist/commands/workflow/shared.d.ts +52 -0
- package/dist/commands/workflow/shared.js +111 -0
- package/dist/commands/workflow/status.d.ts +14 -0
- package/dist/commands/workflow/status.js +58 -0
- package/dist/commands/workflow/templates.d.ts +16 -0
- package/dist/commands/workflow/templates.js +68 -0
- package/dist/core/archive.d.ts +11 -0
- package/dist/core/archive.js +280 -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 +7 -0
- package/dist/core/artifact-graph/index.js +13 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +143 -0
- package/dist/core/artifact-graph/instruction-loader.js +214 -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 +54 -0
- package/dist/core/artifact-graph/types.d.ts +45 -0
- package/dist/core/artifact-graph/types.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/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 +13 -0
- package/dist/core/command-generation/adapters/codex.js +27 -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 +27 -0
- package/dist/core/command-generation/adapters/index.js +27 -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/opencode.d.ts +13 -0
- package/dist/core/command-generation/adapters/opencode.js +26 -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 +88 -0
- package/dist/core/command-generation/types.d.ts +55 -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 +456 -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 +207 -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 +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 +88 -0
- package/dist/core/completions/installers/powershell-installer.js +327 -0
- package/dist/core/completions/installers/zsh-installer.d.ts +125 -0
- package/dist/core/completions/installers/zsh-installer.js +449 -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 +76 -0
- package/dist/core/config-schema.js +200 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.js +30 -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 +39 -0
- package/dist/core/global-config.js +115 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +3 -0
- package/dist/core/init.d.ts +32 -0
- package/dist/core/init.js +433 -0
- package/dist/core/legacy-cleanup.d.ts +162 -0
- package/dist/core/legacy-cleanup.js +501 -0
- package/dist/core/list.d.ts +9 -0
- package/dist/core/list.js +171 -0
- package/dist/core/parsers/change-parser.d.ts +13 -0
- package/dist/core/parsers/change-parser.js +193 -0
- package/dist/core/parsers/markdown-parser.d.ts +22 -0
- package/dist/core/parsers/markdown-parser.js +187 -0
- package/dist/core/parsers/requirement-blocks.d.ts +37 -0
- package/dist/core/parsers/requirement-blocks.js +201 -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 +41 -0
- package/dist/core/shared/skill-generation.js +74 -0
- package/dist/core/shared/tool-detection.d.ts +66 -0
- package/dist/core/shared/tool-detection.js +140 -0
- package/dist/core/specs-apply.d.ts +73 -0
- package/dist/core/specs-apply.js +384 -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 +112 -0
- package/dist/core/templates/skill-templates.js +2893 -0
- package/dist/core/update.d.ts +42 -0
- package/dist/core/update.js +306 -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 +409 -0
- package/dist/core/view.d.ts +8 -0
- package/dist/core/view.js +168 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/prompts/searchable-multi-select.d.ts +27 -0
- package/dist/prompts/searchable-multi-select.js +149 -0
- package/dist/telemetry/config.d.ts +32 -0
- package/dist/telemetry/config.js +68 -0
- package/dist/telemetry/index.d.ts +31 -0
- package/dist/telemetry/index.js +145 -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 +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/file-system.d.ts +36 -0
- package/dist/utils/file-system.js +281 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +7 -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 +84 -0
- package/schemas/spec-driven/schema.yaml +148 -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/tdd/schema.yaml +213 -0
- package/schemas/tdd/templates/docs.md +15 -0
- package/schemas/tdd/templates/implementation.md +11 -0
- package/schemas/tdd/templates/spec.md +11 -0
- package/schemas/tdd/templates/test.md +11 -0
- package/scripts/postinstall.js +147 -0
|
@@ -0,0 +1,433 @@
|
|
|
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 { AI_TOOLS, OPENSPEC_DIR_NAME, } from './config.js';
|
|
14
|
+
import { PALETTE } from './styles/palette.js';
|
|
15
|
+
import { isInteractive } from '../utils/interactive.js';
|
|
16
|
+
import { serializeConfig } from './config-prompts.js';
|
|
17
|
+
import { generateCommands, CommandAdapterRegistry, } from './command-generation/index.js';
|
|
18
|
+
import { detectLegacyArtifacts, cleanupLegacyArtifacts, formatCleanupSummary, formatDetectionSummary, } from './legacy-cleanup.js';
|
|
19
|
+
import { getToolsWithSkillsDir, getToolStates, getSkillTemplates, getCommandContents, generateSkillContent, } from './shared/index.js';
|
|
20
|
+
const require = createRequire(import.meta.url);
|
|
21
|
+
const { version: OPENSPEC_VERSION } = require('../../package.json');
|
|
22
|
+
// -----------------------------------------------------------------------------
|
|
23
|
+
// Constants
|
|
24
|
+
// -----------------------------------------------------------------------------
|
|
25
|
+
const DEFAULT_SCHEMA = 'spec-driven';
|
|
26
|
+
const PROGRESS_SPINNER = {
|
|
27
|
+
interval: 80,
|
|
28
|
+
frames: ['░░░', '▒░░', '▒▒░', '▒▒▒', '▓▒▒', '▓▓▒', '▓▓▓', '▒▓▓', '░▒▓'],
|
|
29
|
+
};
|
|
30
|
+
// -----------------------------------------------------------------------------
|
|
31
|
+
// Init Command Class
|
|
32
|
+
// -----------------------------------------------------------------------------
|
|
33
|
+
export class InitCommand {
|
|
34
|
+
toolsArg;
|
|
35
|
+
force;
|
|
36
|
+
interactiveOption;
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
this.toolsArg = options.tools;
|
|
39
|
+
this.force = options.force ?? false;
|
|
40
|
+
this.interactiveOption = options.interactive;
|
|
41
|
+
}
|
|
42
|
+
async execute(targetPath) {
|
|
43
|
+
const projectPath = path.resolve(targetPath);
|
|
44
|
+
const openspecDir = OPENSPEC_DIR_NAME;
|
|
45
|
+
const openspecPath = path.join(projectPath, openspecDir);
|
|
46
|
+
// Validation happens silently in the background
|
|
47
|
+
const extendMode = await this.validate(projectPath, openspecPath);
|
|
48
|
+
// Check for legacy artifacts and handle cleanup
|
|
49
|
+
await this.handleLegacyCleanup(projectPath, extendMode);
|
|
50
|
+
// Show animated welcome screen (interactive mode only)
|
|
51
|
+
const canPrompt = this.canPromptInteractively();
|
|
52
|
+
if (canPrompt) {
|
|
53
|
+
const { showWelcomeScreen } = await import('../ui/welcome-screen.js');
|
|
54
|
+
await showWelcomeScreen();
|
|
55
|
+
}
|
|
56
|
+
// Get tool states before processing
|
|
57
|
+
const toolStates = getToolStates(projectPath);
|
|
58
|
+
// Get tool selection
|
|
59
|
+
const selectedToolIds = await this.getSelectedTools(toolStates, extendMode);
|
|
60
|
+
// Validate selected tools
|
|
61
|
+
const validatedTools = this.validateTools(selectedToolIds, toolStates);
|
|
62
|
+
// Create directory structure and config
|
|
63
|
+
await this.createDirectoryStructure(openspecPath, extendMode);
|
|
64
|
+
// Generate skills and commands for each tool
|
|
65
|
+
const results = await this.generateSkillsAndCommands(projectPath, validatedTools);
|
|
66
|
+
// Create config.yaml if needed
|
|
67
|
+
const configStatus = await this.createConfig(openspecPath, extendMode);
|
|
68
|
+
// Display success message
|
|
69
|
+
this.displaySuccessMessage(projectPath, validatedTools, results, configStatus);
|
|
70
|
+
}
|
|
71
|
+
// ═══════════════════════════════════════════════════════════
|
|
72
|
+
// VALIDATION & SETUP
|
|
73
|
+
// ═══════════════════════════════════════════════════════════
|
|
74
|
+
async validate(projectPath, openspecPath) {
|
|
75
|
+
const extendMode = await FileSystemUtils.directoryExists(openspecPath);
|
|
76
|
+
// Check write permissions
|
|
77
|
+
if (!(await FileSystemUtils.ensureWritePermissions(projectPath))) {
|
|
78
|
+
throw new Error(`Insufficient permissions to write to ${projectPath}`);
|
|
79
|
+
}
|
|
80
|
+
return extendMode;
|
|
81
|
+
}
|
|
82
|
+
canPromptInteractively() {
|
|
83
|
+
if (this.interactiveOption === false)
|
|
84
|
+
return false;
|
|
85
|
+
if (this.toolsArg !== undefined)
|
|
86
|
+
return false;
|
|
87
|
+
return isInteractive({ interactive: this.interactiveOption });
|
|
88
|
+
}
|
|
89
|
+
// ═══════════════════════════════════════════════════════════
|
|
90
|
+
// LEGACY CLEANUP
|
|
91
|
+
// ═══════════════════════════════════════════════════════════
|
|
92
|
+
async handleLegacyCleanup(projectPath, extendMode) {
|
|
93
|
+
// Detect legacy artifacts
|
|
94
|
+
const detection = await detectLegacyArtifacts(projectPath);
|
|
95
|
+
if (!detection.hasLegacyArtifacts) {
|
|
96
|
+
return; // No legacy artifacts found
|
|
97
|
+
}
|
|
98
|
+
// Show what was detected
|
|
99
|
+
console.log();
|
|
100
|
+
console.log(formatDetectionSummary(detection));
|
|
101
|
+
console.log();
|
|
102
|
+
const canPrompt = this.canPromptInteractively();
|
|
103
|
+
if (this.force) {
|
|
104
|
+
// --force flag: proceed with cleanup automatically
|
|
105
|
+
await this.performLegacyCleanup(projectPath, detection);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!canPrompt) {
|
|
109
|
+
// Non-interactive mode without --force: abort
|
|
110
|
+
console.log(chalk.red('Legacy files detected in non-interactive mode.'));
|
|
111
|
+
console.log(chalk.dim('Run interactively to upgrade, or use --force to auto-cleanup.'));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
// Interactive mode: prompt for confirmation
|
|
115
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
116
|
+
const shouldCleanup = await confirm({
|
|
117
|
+
message: 'Upgrade and clean up legacy files?',
|
|
118
|
+
default: true,
|
|
119
|
+
});
|
|
120
|
+
if (!shouldCleanup) {
|
|
121
|
+
console.log(chalk.dim('Initialization cancelled.'));
|
|
122
|
+
console.log(chalk.dim('Run with --force to skip this prompt, or manually remove legacy files.'));
|
|
123
|
+
process.exit(0);
|
|
124
|
+
}
|
|
125
|
+
await this.performLegacyCleanup(projectPath, detection);
|
|
126
|
+
}
|
|
127
|
+
async performLegacyCleanup(projectPath, detection) {
|
|
128
|
+
const spinner = ora('Cleaning up legacy files...').start();
|
|
129
|
+
const result = await cleanupLegacyArtifacts(projectPath, detection);
|
|
130
|
+
spinner.succeed('Legacy files cleaned up');
|
|
131
|
+
const summary = formatCleanupSummary(result);
|
|
132
|
+
if (summary) {
|
|
133
|
+
console.log();
|
|
134
|
+
console.log(summary);
|
|
135
|
+
}
|
|
136
|
+
console.log();
|
|
137
|
+
}
|
|
138
|
+
// ═══════════════════════════════════════════════════════════
|
|
139
|
+
// TOOL SELECTION
|
|
140
|
+
// ═══════════════════════════════════════════════════════════
|
|
141
|
+
async getSelectedTools(toolStates, extendMode) {
|
|
142
|
+
// Check for --tools flag first
|
|
143
|
+
const nonInteractiveSelection = this.resolveToolsArg();
|
|
144
|
+
if (nonInteractiveSelection !== null) {
|
|
145
|
+
return nonInteractiveSelection;
|
|
146
|
+
}
|
|
147
|
+
const validTools = getToolsWithSkillsDir();
|
|
148
|
+
const canPrompt = this.canPromptInteractively();
|
|
149
|
+
if (!canPrompt || validTools.length === 0) {
|
|
150
|
+
throw new Error(`Missing required option --tools. Valid tools:\n ${validTools.join('\n ')}\n\nUse --tools all, --tools none, or --tools claude,cursor,...`);
|
|
151
|
+
}
|
|
152
|
+
// Interactive mode: show searchable multi-select
|
|
153
|
+
const { searchableMultiSelect } = await import('../prompts/searchable-multi-select.js');
|
|
154
|
+
// Build choices with configured status and sort configured tools first
|
|
155
|
+
const sortedChoices = validTools
|
|
156
|
+
.map((toolId) => {
|
|
157
|
+
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
158
|
+
const status = toolStates.get(toolId);
|
|
159
|
+
const configured = status?.configured ?? false;
|
|
160
|
+
return {
|
|
161
|
+
name: tool?.name || toolId,
|
|
162
|
+
value: toolId,
|
|
163
|
+
configured,
|
|
164
|
+
preSelected: configured, // Pre-select configured tools for easy refresh
|
|
165
|
+
};
|
|
166
|
+
})
|
|
167
|
+
.sort((a, b) => {
|
|
168
|
+
// Configured tools first
|
|
169
|
+
if (a.configured && !b.configured)
|
|
170
|
+
return -1;
|
|
171
|
+
if (!a.configured && b.configured)
|
|
172
|
+
return 1;
|
|
173
|
+
return 0;
|
|
174
|
+
});
|
|
175
|
+
const selectedTools = await searchableMultiSelect({
|
|
176
|
+
message: `Select tools to set up (${validTools.length} available)`,
|
|
177
|
+
pageSize: 15,
|
|
178
|
+
choices: sortedChoices,
|
|
179
|
+
validate: (selected) => selected.length > 0 || 'Select at least one tool',
|
|
180
|
+
});
|
|
181
|
+
if (selectedTools.length === 0) {
|
|
182
|
+
throw new Error('At least one tool must be selected');
|
|
183
|
+
}
|
|
184
|
+
return selectedTools;
|
|
185
|
+
}
|
|
186
|
+
resolveToolsArg() {
|
|
187
|
+
if (typeof this.toolsArg === 'undefined') {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const raw = this.toolsArg.trim();
|
|
191
|
+
if (raw.length === 0) {
|
|
192
|
+
throw new Error('The --tools option requires a value. Use "all", "none", or a comma-separated list of tool IDs.');
|
|
193
|
+
}
|
|
194
|
+
const availableTools = getToolsWithSkillsDir();
|
|
195
|
+
const availableSet = new Set(availableTools);
|
|
196
|
+
const availableList = ['all', 'none', ...availableTools].join(', ');
|
|
197
|
+
const lowerRaw = raw.toLowerCase();
|
|
198
|
+
if (lowerRaw === 'all') {
|
|
199
|
+
return availableTools;
|
|
200
|
+
}
|
|
201
|
+
if (lowerRaw === 'none') {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
const tokens = raw
|
|
205
|
+
.split(',')
|
|
206
|
+
.map((token) => token.trim())
|
|
207
|
+
.filter((token) => token.length > 0);
|
|
208
|
+
if (tokens.length === 0) {
|
|
209
|
+
throw new Error('The --tools option requires at least one tool ID when not using "all" or "none".');
|
|
210
|
+
}
|
|
211
|
+
const normalizedTokens = tokens.map((token) => token.toLowerCase());
|
|
212
|
+
if (normalizedTokens.some((token) => token === 'all' || token === 'none')) {
|
|
213
|
+
throw new Error('Cannot combine reserved values "all" or "none" with specific tool IDs.');
|
|
214
|
+
}
|
|
215
|
+
const invalidTokens = tokens.filter((_token, index) => !availableSet.has(normalizedTokens[index]));
|
|
216
|
+
if (invalidTokens.length > 0) {
|
|
217
|
+
throw new Error(`Invalid tool(s): ${invalidTokens.join(', ')}. Available values: ${availableList}`);
|
|
218
|
+
}
|
|
219
|
+
// Deduplicate while preserving order
|
|
220
|
+
const deduped = [];
|
|
221
|
+
for (const token of normalizedTokens) {
|
|
222
|
+
if (!deduped.includes(token)) {
|
|
223
|
+
deduped.push(token);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return deduped;
|
|
227
|
+
}
|
|
228
|
+
validateTools(toolIds, toolStates) {
|
|
229
|
+
const validatedTools = [];
|
|
230
|
+
for (const toolId of toolIds) {
|
|
231
|
+
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
232
|
+
if (!tool) {
|
|
233
|
+
const validToolIds = getToolsWithSkillsDir();
|
|
234
|
+
throw new Error(`Unknown tool '${toolId}'. Valid tools:\n ${validToolIds.join('\n ')}`);
|
|
235
|
+
}
|
|
236
|
+
if (!tool.skillsDir) {
|
|
237
|
+
const validToolsWithSkills = getToolsWithSkillsDir();
|
|
238
|
+
throw new Error(`Tool '${toolId}' does not support skill generation.\nTools with skill generation support:\n ${validToolsWithSkills.join('\n ')}`);
|
|
239
|
+
}
|
|
240
|
+
const preState = toolStates.get(tool.value);
|
|
241
|
+
validatedTools.push({
|
|
242
|
+
value: tool.value,
|
|
243
|
+
name: tool.name,
|
|
244
|
+
skillsDir: tool.skillsDir,
|
|
245
|
+
wasConfigured: preState?.configured ?? false,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
return validatedTools;
|
|
249
|
+
}
|
|
250
|
+
// ═══════════════════════════════════════════════════════════
|
|
251
|
+
// DIRECTORY STRUCTURE
|
|
252
|
+
// ═══════════════════════════════════════════════════════════
|
|
253
|
+
async createDirectoryStructure(openspecPath, extendMode) {
|
|
254
|
+
if (extendMode) {
|
|
255
|
+
// In extend mode, just ensure directories exist without spinner
|
|
256
|
+
const directories = [
|
|
257
|
+
openspecPath,
|
|
258
|
+
path.join(openspecPath, 'specs'),
|
|
259
|
+
path.join(openspecPath, 'changes'),
|
|
260
|
+
path.join(openspecPath, 'changes', 'archive'),
|
|
261
|
+
];
|
|
262
|
+
for (const dir of directories) {
|
|
263
|
+
await FileSystemUtils.createDirectory(dir);
|
|
264
|
+
}
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const spinner = this.startSpinner('Creating OpenSpec structure...');
|
|
268
|
+
const directories = [
|
|
269
|
+
openspecPath,
|
|
270
|
+
path.join(openspecPath, 'specs'),
|
|
271
|
+
path.join(openspecPath, 'changes'),
|
|
272
|
+
path.join(openspecPath, 'changes', 'archive'),
|
|
273
|
+
];
|
|
274
|
+
for (const dir of directories) {
|
|
275
|
+
await FileSystemUtils.createDirectory(dir);
|
|
276
|
+
}
|
|
277
|
+
spinner.stopAndPersist({
|
|
278
|
+
symbol: PALETTE.white('▌'),
|
|
279
|
+
text: PALETTE.white('OpenSpec structure created'),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
// ═══════════════════════════════════════════════════════════
|
|
283
|
+
// SKILL & COMMAND GENERATION
|
|
284
|
+
// ═══════════════════════════════════════════════════════════
|
|
285
|
+
async generateSkillsAndCommands(projectPath, tools) {
|
|
286
|
+
const createdTools = [];
|
|
287
|
+
const refreshedTools = [];
|
|
288
|
+
const failedTools = [];
|
|
289
|
+
const commandsSkipped = [];
|
|
290
|
+
// Get skill and command templates once (shared across all tools)
|
|
291
|
+
const skillTemplates = getSkillTemplates();
|
|
292
|
+
const commandContents = getCommandContents();
|
|
293
|
+
// Process each tool
|
|
294
|
+
for (const tool of tools) {
|
|
295
|
+
const spinner = ora(`Setting up ${tool.name}...`).start();
|
|
296
|
+
try {
|
|
297
|
+
// Use tool-specific skillsDir
|
|
298
|
+
const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
|
|
299
|
+
// Create skill directories and SKILL.md files
|
|
300
|
+
for (const { template, dirName } of skillTemplates) {
|
|
301
|
+
const skillDir = path.join(skillsDir, dirName);
|
|
302
|
+
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
303
|
+
// Generate SKILL.md content with YAML frontmatter including generatedBy
|
|
304
|
+
const skillContent = generateSkillContent(template, OPENSPEC_VERSION);
|
|
305
|
+
// Write the skill file
|
|
306
|
+
await FileSystemUtils.writeFile(skillFile, skillContent);
|
|
307
|
+
}
|
|
308
|
+
// Generate commands using the adapter system
|
|
309
|
+
const adapter = CommandAdapterRegistry.get(tool.value);
|
|
310
|
+
if (adapter) {
|
|
311
|
+
const generatedCommands = generateCommands(commandContents, adapter);
|
|
312
|
+
for (const cmd of generatedCommands) {
|
|
313
|
+
const commandFile = path.join(projectPath, cmd.path);
|
|
314
|
+
await FileSystemUtils.writeFile(commandFile, cmd.fileContent);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
commandsSkipped.push(tool.value);
|
|
319
|
+
}
|
|
320
|
+
spinner.succeed(`Setup complete for ${tool.name}`);
|
|
321
|
+
if (tool.wasConfigured) {
|
|
322
|
+
refreshedTools.push(tool);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
createdTools.push(tool);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
spinner.fail(`Failed for ${tool.name}`);
|
|
330
|
+
failedTools.push({ name: tool.name, error: error });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return { createdTools, refreshedTools, failedTools, commandsSkipped };
|
|
334
|
+
}
|
|
335
|
+
// ═══════════════════════════════════════════════════════════
|
|
336
|
+
// CONFIG FILE
|
|
337
|
+
// ═══════════════════════════════════════════════════════════
|
|
338
|
+
async createConfig(openspecPath, extendMode) {
|
|
339
|
+
const configPath = path.join(openspecPath, 'config.yaml');
|
|
340
|
+
const configYmlPath = path.join(openspecPath, 'config.yml');
|
|
341
|
+
const configYamlExists = fs.existsSync(configPath);
|
|
342
|
+
const configYmlExists = fs.existsSync(configYmlPath);
|
|
343
|
+
if (configYamlExists || configYmlExists) {
|
|
344
|
+
return 'exists';
|
|
345
|
+
}
|
|
346
|
+
// In non-interactive mode without --force, skip config creation
|
|
347
|
+
if (!this.canPromptInteractively() && !this.force) {
|
|
348
|
+
return 'skipped';
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
const yamlContent = serializeConfig({ schema: DEFAULT_SCHEMA });
|
|
352
|
+
await FileSystemUtils.writeFile(configPath, yamlContent);
|
|
353
|
+
return 'created';
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
return 'skipped';
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
// ═══════════════════════════════════════════════════════════
|
|
360
|
+
// UI & OUTPUT
|
|
361
|
+
// ═══════════════════════════════════════════════════════════
|
|
362
|
+
displaySuccessMessage(projectPath, tools, results, configStatus) {
|
|
363
|
+
console.log();
|
|
364
|
+
console.log(chalk.bold('OpenSpec Setup Complete'));
|
|
365
|
+
console.log();
|
|
366
|
+
// Show created vs refreshed tools
|
|
367
|
+
if (results.createdTools.length > 0) {
|
|
368
|
+
console.log(`Created: ${results.createdTools.map((t) => t.name).join(', ')}`);
|
|
369
|
+
}
|
|
370
|
+
if (results.refreshedTools.length > 0) {
|
|
371
|
+
console.log(`Refreshed: ${results.refreshedTools.map((t) => t.name).join(', ')}`);
|
|
372
|
+
}
|
|
373
|
+
// Show counts
|
|
374
|
+
const successfulTools = [...results.createdTools, ...results.refreshedTools];
|
|
375
|
+
if (successfulTools.length > 0) {
|
|
376
|
+
const toolDirs = [...new Set(successfulTools.map((t) => t.skillsDir))].join(', ');
|
|
377
|
+
const hasCommands = results.commandsSkipped.length < successfulTools.length;
|
|
378
|
+
if (hasCommands) {
|
|
379
|
+
console.log(`${getSkillTemplates().length} skills and ${getCommandContents().length} commands in ${toolDirs}/`);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
console.log(`${getSkillTemplates().length} skills in ${toolDirs}/`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// Show failures
|
|
386
|
+
if (results.failedTools.length > 0) {
|
|
387
|
+
console.log(chalk.red(`Failed: ${results.failedTools.map((f) => `${f.name} (${f.error.message})`).join(', ')}`));
|
|
388
|
+
}
|
|
389
|
+
// Show skipped commands
|
|
390
|
+
if (results.commandsSkipped.length > 0) {
|
|
391
|
+
console.log(chalk.dim(`Commands skipped for: ${results.commandsSkipped.join(', ')} (no adapter)`));
|
|
392
|
+
}
|
|
393
|
+
// Config status
|
|
394
|
+
if (configStatus === 'created') {
|
|
395
|
+
console.log(`Config: openspec/config.yaml (schema: ${DEFAULT_SCHEMA})`);
|
|
396
|
+
}
|
|
397
|
+
else if (configStatus === 'exists') {
|
|
398
|
+
// Show actual filename (config.yaml or config.yml)
|
|
399
|
+
const configYaml = path.join(projectPath, OPENSPEC_DIR_NAME, 'config.yaml');
|
|
400
|
+
const configYml = path.join(projectPath, OPENSPEC_DIR_NAME, 'config.yml');
|
|
401
|
+
const configName = fs.existsSync(configYaml) ? 'config.yaml' : fs.existsSync(configYml) ? 'config.yml' : 'config.yaml';
|
|
402
|
+
console.log(`Config: openspec/${configName} (exists)`);
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
console.log(chalk.dim(`Config: skipped (non-interactive mode)`));
|
|
406
|
+
}
|
|
407
|
+
// Getting started
|
|
408
|
+
console.log();
|
|
409
|
+
console.log(chalk.bold('Getting started:'));
|
|
410
|
+
console.log(' /opsx:new Start a new change');
|
|
411
|
+
console.log(' /opsx:continue Create the next artifact');
|
|
412
|
+
console.log(' /opsx:apply Implement tasks');
|
|
413
|
+
// Links
|
|
414
|
+
console.log();
|
|
415
|
+
console.log(`Learn more: ${chalk.cyan('https://github.com/Fission-AI/OpenSpec')}`);
|
|
416
|
+
console.log(`Feedback: ${chalk.cyan('https://github.com/Fission-AI/OpenSpec/issues')}`);
|
|
417
|
+
// Restart instruction if any tools were configured
|
|
418
|
+
if (results.createdTools.length > 0 || results.refreshedTools.length > 0) {
|
|
419
|
+
console.log();
|
|
420
|
+
console.log(chalk.white('Restart your IDE for slash commands to take effect.'));
|
|
421
|
+
}
|
|
422
|
+
console.log();
|
|
423
|
+
}
|
|
424
|
+
startSpinner(text) {
|
|
425
|
+
return ora({
|
|
426
|
+
text,
|
|
427
|
+
stream: process.stdout,
|
|
428
|
+
color: 'gray',
|
|
429
|
+
spinner: PROGRESS_SPINNER,
|
|
430
|
+
}).start();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy cleanup module for detecting and removing OpenSpec artifacts
|
|
3
|
+
* from previous init versions during the migration to the skill-based workflow.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Legacy config file names from the old ToolRegistry.
|
|
7
|
+
* These were config files created at project root with OpenSpec markers.
|
|
8
|
+
*/
|
|
9
|
+
export declare const LEGACY_CONFIG_FILES: readonly ["CLAUDE.md", "CLINE.md", "CODEBUDDY.md", "COSTRICT.md", "QODER.md", "IFLOW.md", "AGENTS.md", "QWEN.md"];
|
|
10
|
+
/**
|
|
11
|
+
* Legacy slash command patterns from the old SlashCommandRegistry.
|
|
12
|
+
* These map toolId to the path pattern where legacy commands were created.
|
|
13
|
+
* Some tools used a directory structure, others used individual files.
|
|
14
|
+
*/
|
|
15
|
+
export declare const LEGACY_SLASH_COMMAND_PATHS: Record<string, LegacySlashCommandPattern>;
|
|
16
|
+
/**
|
|
17
|
+
* Pattern types for legacy slash commands
|
|
18
|
+
*/
|
|
19
|
+
export interface LegacySlashCommandPattern {
|
|
20
|
+
type: 'directory' | 'files';
|
|
21
|
+
path?: string;
|
|
22
|
+
pattern?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Result of legacy artifact detection
|
|
26
|
+
*/
|
|
27
|
+
export interface LegacyDetectionResult {
|
|
28
|
+
/** Config files with OpenSpec markers detected */
|
|
29
|
+
configFiles: string[];
|
|
30
|
+
/** Config files to update (remove markers only, never delete) */
|
|
31
|
+
configFilesToUpdate: string[];
|
|
32
|
+
/** Legacy slash command directories found */
|
|
33
|
+
slashCommandDirs: string[];
|
|
34
|
+
/** Legacy slash command files found (for file-based tools) */
|
|
35
|
+
slashCommandFiles: string[];
|
|
36
|
+
/** Whether openspec/AGENTS.md exists */
|
|
37
|
+
hasOpenspecAgents: boolean;
|
|
38
|
+
/** Whether openspec/project.md exists (preserved, migration hint only) */
|
|
39
|
+
hasProjectMd: boolean;
|
|
40
|
+
/** Whether root AGENTS.md has OpenSpec markers */
|
|
41
|
+
hasRootAgentsWithMarkers: boolean;
|
|
42
|
+
/** Whether any legacy artifacts were found */
|
|
43
|
+
hasLegacyArtifacts: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Detects all legacy OpenSpec artifacts in a project.
|
|
47
|
+
*
|
|
48
|
+
* @param projectPath - The root path of the project
|
|
49
|
+
* @returns Detection result with all found legacy artifacts
|
|
50
|
+
*/
|
|
51
|
+
export declare function detectLegacyArtifacts(projectPath: string): Promise<LegacyDetectionResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Detects legacy config files with OpenSpec markers.
|
|
54
|
+
* All config files with markers are candidates for update (marker removal only).
|
|
55
|
+
* Config files are NEVER deleted - they belong to the user's project root.
|
|
56
|
+
*
|
|
57
|
+
* @param projectPath - The root path of the project
|
|
58
|
+
* @returns Object with all files found and files to update
|
|
59
|
+
*/
|
|
60
|
+
export declare function detectLegacyConfigFiles(projectPath: string): Promise<{
|
|
61
|
+
allFiles: string[];
|
|
62
|
+
filesToUpdate: string[];
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* Detects legacy slash command directories and files.
|
|
66
|
+
*
|
|
67
|
+
* @param projectPath - The root path of the project
|
|
68
|
+
* @returns Object with directories and individual files found
|
|
69
|
+
*/
|
|
70
|
+
export declare function detectLegacySlashCommands(projectPath: string): Promise<{
|
|
71
|
+
directories: string[];
|
|
72
|
+
files: string[];
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Detects legacy OpenSpec structure files (AGENTS.md and project.md).
|
|
76
|
+
*
|
|
77
|
+
* @param projectPath - The root path of the project
|
|
78
|
+
* @returns Object with detection results for structure files
|
|
79
|
+
*/
|
|
80
|
+
export declare function detectLegacyStructureFiles(projectPath: string): Promise<{
|
|
81
|
+
hasOpenspecAgents: boolean;
|
|
82
|
+
hasProjectMd: boolean;
|
|
83
|
+
hasRootAgentsWithMarkers: boolean;
|
|
84
|
+
}>;
|
|
85
|
+
/**
|
|
86
|
+
* Checks if content contains OpenSpec markers.
|
|
87
|
+
*
|
|
88
|
+
* @param content - File content to check
|
|
89
|
+
* @returns True if both start and end markers are present
|
|
90
|
+
*/
|
|
91
|
+
export declare function hasOpenSpecMarkers(content: string): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Checks if file content is 100% OpenSpec content (only markers and whitespace outside).
|
|
94
|
+
*
|
|
95
|
+
* @param content - File content to check
|
|
96
|
+
* @returns True if content outside markers is only whitespace
|
|
97
|
+
*/
|
|
98
|
+
export declare function isOnlyOpenSpecContent(content: string): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Removes the OpenSpec marker block from file content.
|
|
101
|
+
* Only removes markers that are on their own lines (ignores inline mentions).
|
|
102
|
+
* Cleans up double blank lines that may result from removal.
|
|
103
|
+
*
|
|
104
|
+
* @param content - File content with OpenSpec markers
|
|
105
|
+
* @returns Content with marker block removed
|
|
106
|
+
*/
|
|
107
|
+
export declare function removeMarkerBlock(content: string): string;
|
|
108
|
+
/**
|
|
109
|
+
* Result of cleanup operation
|
|
110
|
+
*/
|
|
111
|
+
export interface CleanupResult {
|
|
112
|
+
/** Files that were deleted entirely */
|
|
113
|
+
deletedFiles: string[];
|
|
114
|
+
/** Files that had marker blocks removed */
|
|
115
|
+
modifiedFiles: string[];
|
|
116
|
+
/** Directories that were deleted */
|
|
117
|
+
deletedDirs: string[];
|
|
118
|
+
/** Whether project.md exists and needs manual migration */
|
|
119
|
+
projectMdNeedsMigration: boolean;
|
|
120
|
+
/** Error messages if any operations failed */
|
|
121
|
+
errors: string[];
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Cleans up legacy OpenSpec artifacts from a project.
|
|
125
|
+
* Preserves openspec/project.md (shows migration hint instead of deleting).
|
|
126
|
+
*
|
|
127
|
+
* @param projectPath - The root path of the project
|
|
128
|
+
* @param detection - Detection result from detectLegacyArtifacts
|
|
129
|
+
* @returns Cleanup result with summary of actions taken
|
|
130
|
+
*/
|
|
131
|
+
export declare function cleanupLegacyArtifacts(projectPath: string, detection: LegacyDetectionResult): Promise<CleanupResult>;
|
|
132
|
+
/**
|
|
133
|
+
* Generates a cleanup summary message for display.
|
|
134
|
+
*
|
|
135
|
+
* @param result - Cleanup result from cleanupLegacyArtifacts
|
|
136
|
+
* @returns Formatted summary string for console output
|
|
137
|
+
*/
|
|
138
|
+
export declare function formatCleanupSummary(result: CleanupResult): string;
|
|
139
|
+
/**
|
|
140
|
+
* Generates a detection summary message for display before cleanup.
|
|
141
|
+
* Groups files by action type: removals, updates, and manual migration.
|
|
142
|
+
*
|
|
143
|
+
* @param detection - Detection result from detectLegacyArtifacts
|
|
144
|
+
* @returns Formatted summary string showing what was found
|
|
145
|
+
*/
|
|
146
|
+
export declare function formatDetectionSummary(detection: LegacyDetectionResult): string;
|
|
147
|
+
/**
|
|
148
|
+
* Extract tool IDs from detected legacy artifacts.
|
|
149
|
+
* Uses LEGACY_SLASH_COMMAND_PATHS to map paths back to tool IDs.
|
|
150
|
+
*
|
|
151
|
+
* @param detection - Detection result from detectLegacyArtifacts
|
|
152
|
+
* @returns Array of tool IDs that had legacy artifacts
|
|
153
|
+
*/
|
|
154
|
+
export declare function getToolsFromLegacyArtifacts(detection: LegacyDetectionResult): string[];
|
|
155
|
+
/**
|
|
156
|
+
* Generates a migration hint message for project.md.
|
|
157
|
+
* This is shown when project.md exists and needs manual migration to config.yaml.
|
|
158
|
+
*
|
|
159
|
+
* @returns Formatted migration hint string for console output
|
|
160
|
+
*/
|
|
161
|
+
export declare function formatProjectMdMigrationHint(): string;
|
|
162
|
+
//# sourceMappingURL=legacy-cleanup.d.ts.map
|