From 7d5dc4807541efc8a34c63ff304a8e9a72d139e0 Mon Sep 17 00:00:00 2001 From: soimalfath Date: Mon, 18 May 2026 11:30:38 +0700 Subject: [PATCH] feat: implement core orchestrator, install command, and IDE detection system with support for multiple adapters --- package.json | 4 ++-- src/adapters/IdeDetector.ts | 26 ++++++++++++++++++++++---- src/commands/install.ts | 6 +++++- src/core/Orchestrator.ts | 5 ++++- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index a2f9c3f..922b4fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komerce-skill", - "version": "1.0.0", + "version": "1.3.0", "description": "AI Skills Registry CLI tool for managing AI assistant skills across multiple IDEs", "main": "dist/cli.js", "bin": { @@ -55,4 +55,4 @@ "dist/", "README.md" ] -} +} \ No newline at end of file diff --git a/src/adapters/IdeDetector.ts b/src/adapters/IdeDetector.ts index 0f28bd0..b0187a4 100644 --- a/src/adapters/IdeDetector.ts +++ b/src/adapters/IdeDetector.ts @@ -33,12 +33,24 @@ export class IdeDetector { /** * Returns the list of IDE adapters to use for the given workspace. * + * Strategy: + * 1. If forceIde is set → only that adapter + * 2. If skillTargets is set → use BOTH detected IDEs AND targeted IDEs (union) + * This ensures skills that explicitly target an IDE get installed even if + * the IDE config directory doesn't exist yet in the workspace. + * 3. If neither → auto-detect from workspace, fallback to all + * * @param workspaceRoot - Absolute path to the workspace root directory - * @param forceIde - If provided, only return the adapter matching this IDE name (case-insensitive) + * @param forceIde - If provided, only return the adapter matching this IDE name + * @param skillTargets - If provided, also include adapters matching these targets * @returns Array of applicable IDE adapters * @throws SkillError(IDE_NOT_SUPPORTED) if forceIde is provided but not found */ - async detectAll(workspaceRoot: string, forceIde?: string): Promise { + async detectAll( + workspaceRoot: string, + forceIde?: string + ): Promise { + // Force specific IDE if (forceIde !== undefined) { const normalized = forceIde.toLowerCase(); const adapter = this.allAdapters.find((a) => a.name.toLowerCase() === normalized); @@ -63,9 +75,15 @@ export class IdeDetector { } } - // Fallback: if none detected, return all adapters (install everywhere) + // Strict behavior: if no IDE config directories are found in the workspace, + // do not guess or fallback. Ask the user to explicitly specify. if (detected.length === 0) { - return [...this.allAdapters]; + throw new SkillError( + SkillErrorCode.IDE_NOT_SUPPORTED, + `No supported IDEs detected in the workspace.\n` + + `If you are in a fresh project, use the --ide flag to force installation for your IDE.\n` + + `Example: komerce-skill install --ide antigravity` + ); } return detected; diff --git a/src/commands/install.ts b/src/commands/install.ts index 0ffb5e7..7e0a319 100644 --- a/src/commands/install.ts +++ b/src/commands/install.ts @@ -39,7 +39,10 @@ export async function installCommand( if (successfulInstalls.length > 0) { try { - const adapters = await context.ideDetector.detectAll(context.workspaceRoot, options.ide); + const adapters = await context.ideDetector.detectAll( + context.workspaceRoot, + options.ide + ); for (const adapter of adapters) { for (const result of successfulInstalls) { @@ -47,6 +50,7 @@ export async function installCommand( if (skill) { try { await adapter.install(skill, context.workspaceRoot); + console.log(` → ${adapter.name}: config generated`); } catch (err) { // Non-fatal: log warning but continue const errorMessage = err instanceof Error ? err.message : String(err); diff --git a/src/core/Orchestrator.ts b/src/core/Orchestrator.ts index 3c747e9..e9120a6 100644 --- a/src/core/Orchestrator.ts +++ b/src/core/Orchestrator.ts @@ -75,7 +75,10 @@ export class Orchestrator { const successful = results.filter((r) => r.success); if (successful.length > 0) { try { - const adapters = await this.ideDetector.detectAll(this.workspaceRoot, options.ide); + const adapters = await this.ideDetector.detectAll( + this.workspaceRoot, + options.ide + ); for (const adapter of adapters) { for (const result of successful) {