(targetPath: string)
| 105 | } |
| 106 | |
| 107 | async execute(targetPath: string): Promise<void> { |
| 108 | const projectPath = path.resolve(targetPath); |
| 109 | const openspecDir = OPENSPEC_DIR_NAME; |
| 110 | const openspecPath = path.join(projectPath, openspecDir); |
| 111 | |
| 112 | // Validation happens silently in the background |
| 113 | const extendMode = await this.validate(projectPath, openspecPath); |
| 114 | |
| 115 | // Pointer guard (slice 3.2): a config-only openspec/ with a store: |
| 116 | // declaration is externalized planning, not a root to extend — and a |
| 117 | // subdirectory of such a repo must not silently grow a nested root. |
| 118 | // Refuse before legacy cleanup, migration, or prompts touch anything. |
| 119 | // In extend mode the walk finds projectPath itself; otherwise it |
| 120 | // finds the nearest ancestor root (so pointer-repo subdirectories |
| 121 | // refuse exactly where a normal command would resolve the pointer). |
| 122 | const guardRoot = findRepoPlanningRootSync(projectPath); |
| 123 | if (guardRoot) { |
| 124 | const { hasPlanningShape, pointer } = classifyOpenSpecDir(guardRoot); |
| 125 | if (!hasPlanningShape) { |
| 126 | if (pointer.malformed) { |
| 127 | throw new Error( |
| 128 | `The store declaration in ${pointer.filePath} is invalid (` + |
| 129 | storePointerProblem(pointer.malformed) + |
| 130 | `). Fix or remove the store: line before running openspec init.` |
| 131 | ); |
| 132 | } |
| 133 | if (pointer.value !== undefined) { |
| 134 | throw new Error( |
| 135 | `This repo's planning is externalized to store '${pointer.value}' (${pointer.filePath}). ` + |
| 136 | `Remove the store: line first to convert this repo to a local OpenSpec root.` |
| 137 | ); |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | // Check for legacy artifacts and handle cleanup |
| 143 | await this.handleLegacyCleanup(projectPath, extendMode); |
| 144 | |
| 145 | // Detect available tools in the project (task 7.1) |
| 146 | const detectedTools = getAvailableTools(projectPath); |
| 147 | |
| 148 | // Migration check: migrate existing projects to profile system (task 7.3) |
| 149 | if (extendMode) { |
| 150 | migrateIfNeeded(projectPath, detectedTools); |
| 151 | } |
| 152 | |
| 153 | // Show animated welcome screen (interactive mode only) |
| 154 | const canPrompt = this.canPromptInteractively(); |
| 155 | if (canPrompt) { |
| 156 | const { showWelcomeScreen } = await import('../ui/welcome-screen.js'); |
| 157 | await showWelcomeScreen(); |
| 158 | } |
| 159 | |
| 160 | // Validate profile override early so invalid values fail before tool setup. |
| 161 | // The resolved value is consumed later when generation reads effective config. |
| 162 | this.resolveProfileOverride(); |
| 163 | |
| 164 | // Get tool states before processing |
nothing calls this directly
no test coverage detected