diff --git a/packages/core/src/__tests__/config-validation.test.ts b/packages/core/src/__tests__/config-validation.test.ts index a76ea7516..97310b972 100644 --- a/packages/core/src/__tests__/config-validation.test.ts +++ b/packages/core/src/__tests__/config-validation.test.ts @@ -418,4 +418,44 @@ describe("Config Defaults", () => { const validated = validateConfig(config); expect(validated.projects.proj1.tracker).toEqual({ plugin: "github" }); }); + + it("infers GitLab tracker default from scm plugin", () => { + const config = { + projects: { + proj1: { + path: "/repos/test", + repo: "org/test", + defaultBranch: "main", + scm: { + plugin: "gitlab", + host: "gitlab.company.com", + }, + }, + }, + }; + + const validated = validateConfig(config); + expect(validated.projects.proj1.scm).toEqual({ plugin: "gitlab", host: "gitlab.company.com" }); + expect(validated.projects.proj1.tracker).toEqual({ plugin: "gitlab" }); + }); + + it("infers GitLab scm default from tracker plugin", () => { + const config = { + projects: { + proj1: { + path: "/repos/test", + repo: "org/test", + defaultBranch: "main", + tracker: { + plugin: "gitlab", + host: "gitlab.com", + }, + }, + }, + }; + + const validated = validateConfig(config); + expect(validated.projects.proj1.tracker).toEqual({ plugin: "gitlab", host: "gitlab.com" }); + expect(validated.projects.proj1.scm).toEqual({ plugin: "gitlab" }); + }); }); diff --git a/packages/core/src/__tests__/plugin-registry.test.ts b/packages/core/src/__tests__/plugin-registry.test.ts index 4d6e0b861..c8abc67ca 100644 --- a/packages/core/src/__tests__/plugin-registry.test.ts +++ b/packages/core/src/__tests__/plugin-registry.test.ts @@ -165,6 +165,26 @@ describe("loadBuiltins", () => { expect(registry.get("agent", "opencode")).not.toBeNull(); }); + it("registers gitlab tracker and scm plugins from importFn", async () => { + const registry = createPluginRegistry(); + + const fakeTracker = makePlugin("tracker", "gitlab"); + const fakeScm = makePlugin("scm", "gitlab"); + + await registry.loadBuiltins(undefined, async (pkg: string) => { + if (pkg === "@composio/ao-plugin-tracker-gitlab") return fakeTracker; + if (pkg === "@composio/ao-plugin-scm-gitlab") return fakeScm; + throw new Error(`Not found: ${pkg}`); + }); + + expect(registry.list("tracker")).toContainEqual( + expect.objectContaining({ name: "gitlab", slot: "tracker" }), + ); + expect(registry.list("scm")).toContainEqual( + expect.objectContaining({ name: "gitlab", slot: "scm" }), + ); + }); + it("passes configured notifier plugin config to create()", async () => { const registry = createPluginRegistry(); const fakeWebhookNotifier = makePlugin("notifier", "webhook"); diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 11ca6fb23..743d66fe2 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -18,6 +18,34 @@ import { z } from "zod"; import type { OrchestratorConfig } from "./types.js"; import { generateSessionPrefix } from "./paths.js"; +function inferScmPlugin(project: { + repo: string; + scm?: Record; + tracker?: Record; +}): "github" | "gitlab" { + const scmPlugin = project.scm?.["plugin"]; + if (scmPlugin === "gitlab") { + return "gitlab"; + } + + const scmHost = project.scm?.["host"]; + if (typeof scmHost === "string" && scmHost.toLowerCase().includes("gitlab")) { + return "gitlab"; + } + + const trackerPlugin = project.tracker?.["plugin"]; + if (trackerPlugin === "gitlab") { + return "gitlab"; + } + + const trackerHost = project.tracker?.["host"]; + if (typeof trackerHost === "string" && trackerHost.toLowerCase().includes("gitlab")) { + return "gitlab"; + } + + return "github"; +} + // ============================================================================= // ZOD SCHEMAS // ============================================================================= @@ -166,14 +194,16 @@ function applyProjectDefaults(config: OrchestratorConfig): OrchestratorConfig { project.sessionPrefix = generateSessionPrefix(projectId); } + const inferredPlugin = inferScmPlugin(project); + // Infer SCM from repo if not set if (!project.scm && project.repo.includes("/")) { - project.scm = { plugin: "github" }; + project.scm = { plugin: inferredPlugin }; } // Infer tracker from repo if not set (default to github issues) if (!project.tracker) { - project.tracker = { plugin: "github" }; + project.tracker = { plugin: inferredPlugin }; } } diff --git a/packages/core/src/plugin-registry.ts b/packages/core/src/plugin-registry.ts index 9892786f7..46b80f6df 100644 --- a/packages/core/src/plugin-registry.ts +++ b/packages/core/src/plugin-registry.ts @@ -38,8 +38,10 @@ const BUILTIN_PLUGINS: Array<{ slot: PluginSlot; name: string; pkg: string }> = // Trackers { slot: "tracker", name: "github", pkg: "@composio/ao-plugin-tracker-github" }, { slot: "tracker", name: "linear", pkg: "@composio/ao-plugin-tracker-linear" }, + { slot: "tracker", name: "gitlab", pkg: "@composio/ao-plugin-tracker-gitlab" }, // SCM { slot: "scm", name: "github", pkg: "@composio/ao-plugin-scm-github" }, + { slot: "scm", name: "gitlab", pkg: "@composio/ao-plugin-scm-gitlab" }, // Notifiers { slot: "notifier", name: "composio", pkg: "@composio/ao-plugin-notifier-composio" }, { slot: "notifier", name: "desktop", pkg: "@composio/ao-plugin-notifier-desktop" }, @@ -64,9 +66,7 @@ function extractPluginConfig( if (!notifierConfig || typeof notifierConfig !== "object") continue; const configuredPlugin = (notifierConfig as Record)["plugin"]; const hasExplicitPlugin = typeof configuredPlugin === "string" && configuredPlugin.length > 0; - const matches = hasExplicitPlugin - ? configuredPlugin === name - : notifierName === name; + const matches = hasExplicitPlugin ? configuredPlugin === name : notifierName === name; if (matches) { const { plugin: _plugin, ...rest } = notifierConfig as Record; return rest;