( projectPath: string, toolIds: string[], scope: InstallScope, shouldInstallCli = true, )
| 241 | } |
| 242 | |
| 243 | async function installOpenSpec( |
| 244 | projectPath: string, |
| 245 | toolIds: string[], |
| 246 | scope: InstallScope, |
| 247 | shouldInstallCli = true, |
| 248 | ): Promise<'installed' | 'failed' | 'skipped'> { |
| 249 | const cliStatus = await ensureOpenSpecCli(scope, projectPath, shouldInstallCli); |
| 250 | if (cliStatus === 'failed') { |
| 251 | console.error( |
| 252 | ` OpenSpec CLI not available. Install manually: npm install -g @fission-ai/openspec@latest`, |
| 253 | ); |
| 254 | return 'failed'; |
| 255 | } |
| 256 | if (cliStatus === 'missing') { |
| 257 | return 'skipped'; |
| 258 | } |
| 259 | |
| 260 | const unknownIds = toolIds.filter((id) => !VALID_TOOL_IDS.has(id)); |
| 261 | if (unknownIds.length > 0) { |
| 262 | throw new Error(`Unknown tool IDs: ${unknownIds.join(', ')}`); |
| 263 | } |
| 264 | |
| 265 | let configHome: string | undefined; |
| 266 | let configBackup: ConfigBackup | null = null; |
| 267 | try { |
| 268 | const openspecEnv = createOpenSpecAllWorkflowsEnv(); |
| 269 | configHome = openspecEnv.configHome; |
| 270 | |
| 271 | configBackup = writeAllWorkflowsToDefaultConfig(); |
| 272 | |
| 273 | const invocation = buildOpenSpecInitInvocation(projectPath, toolIds, scope); |
| 274 | try { |
| 275 | execFileSync(invocation.command, invocation.args, { |
| 276 | cwd: projectPath, |
| 277 | env: openspecEnv.env, |
| 278 | stdio: ['inherit', 'inherit', 'pipe'], |
| 279 | timeout: 120_000, |
| 280 | shell: process.platform === 'win32', |
| 281 | }); |
| 282 | } catch (firstError) { |
| 283 | const stderrText = (firstError as { stderr?: Buffer }).stderr?.toString() ?? ''; |
| 284 | if (stderrText.includes('unknown option') && stderrText.includes('--profile')) { |
| 285 | console.warn(' OpenSpec does not support --profile flag, retrying without it...'); |
| 286 | const fallbackInvocation = buildOpenSpecInitInvocation( |
| 287 | projectPath, |
| 288 | toolIds, |
| 289 | scope, |
| 290 | os.homedir(), |
| 291 | false, |
| 292 | ); |
| 293 | execFileSync(fallbackInvocation.command, fallbackInvocation.args, { |
| 294 | cwd: projectPath, |
| 295 | env: openspecEnv.env, |
| 296 | stdio: 'inherit', |
| 297 | timeout: 120_000, |
| 298 | shell: process.platform === 'win32', |
| 299 | }); |
| 300 | } else { |
no test coverage detected