From 83278f0760d444294464367b3491ecb6e6a2f09f Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 5 Jun 2026 03:57:52 +0000 Subject: [PATCH] PAP-10287 tighten teams catalog review fixes Co-Authored-By: Paperclip --- .../src/__tests__/teams-catalog-service.test.ts | 16 ++++++++++++++++ server/src/services/teams-catalog.ts | 9 ++++++++- ui/src/pages/TeamCatalog.test.tsx | 14 +++++++++++++- ui/src/pages/TeamCatalog.tsx | 8 ++++---- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/server/src/__tests__/teams-catalog-service.test.ts b/server/src/__tests__/teams-catalog-service.test.ts index 5e8af4089db..c4328046f87 100644 --- a/server/src/__tests__/teams-catalog-service.test.ts +++ b/server/src/__tests__/teams-catalog-service.test.ts @@ -291,6 +291,22 @@ describe("teamsCatalogService", () => { }); }); + it("omits the default-adapter warning when every agent has an explicit override", async () => { + const svc = teamsCatalogService({} as any); + + const result = await svc.installCatalogTeam("company-1", "core-exec-team", { + adapterOverrides: { + ceo: { adapterType: "opencode_local" }, + cto: { adapterType: "opencode_local" }, + qa: { adapterType: "opencode_local" }, + }, + }); + + expect(result.warnings).not.toEqual( + expect.arrayContaining([expect.stringContaining("default to claude_local")]), + ); + }); + it("passes install secretValues through to company portability import", async () => { const svc = teamsCatalogService({} as any); diff --git a/server/src/services/teams-catalog.ts b/server/src/services/teams-catalog.ts index cc0c1bd9ad0..70e598d25b2 100644 --- a/server/src/services/teams-catalog.ts +++ b/server/src/services/teams-catalog.ts @@ -1010,10 +1010,17 @@ export function teamsCatalogService(db: Db) { if (importPreview.errors.length > 0) { throw unprocessable(`Catalog team import preview has errors: ${importPreview.errors.join("; ")}`); } + const defaultedAdapterSlugs = prepared.team.agentSlugs.filter( + (slug) => !options.adapterOverrides?.[slug], + ); const warnings = [ ...prepared.warnings, ...importPreview.warnings, - `Catalog agents without explicit overrides default to ${DEFAULT_SAFE_CATALOG_ADAPTER_TYPE}. Pass adapterOverrides to use a different supported adapter.`, + ...(defaultedAdapterSlugs.length > 0 + ? [ + `Catalog agents without explicit overrides (${defaultedAdapterSlugs.join(", ")}) default to ${DEFAULT_SAFE_CATALOG_ADAPTER_TYPE}. Pass adapterOverrides to use a different supported adapter.`, + ] + : []), ...await prepareSkillInstalls(companyId, prepared), ]; const result = await portability.importBundle( diff --git a/ui/src/pages/TeamCatalog.test.tsx b/ui/src/pages/TeamCatalog.test.tsx index 5dceafe0fdd..fc6e5d0f97a 100644 --- a/ui/src/pages/TeamCatalog.test.tsx +++ b/ui/src/pages/TeamCatalog.test.tsx @@ -8,7 +8,7 @@ import type { CatalogTeamImportPreviewResult, } from "@paperclipai/shared"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { TeamCatalog } from "./TeamCatalog"; +import { TeamCatalog, parseTeamRoute, teamRoute } from "./TeamCatalog"; import { TooltipProvider } from "@/components/ui/tooltip"; const mockTeamCatalogApi = vi.hoisted(() => ({ @@ -85,6 +85,18 @@ if (typeof Element !== "undefined" && !Element.prototype.scrollIntoView) { Element.prototype.scrollIntoView = () => {}; } +describe("TeamCatalog routes", () => { + it("round-trips file paths containing literal tildes", () => { + const route = teamRoute("paperclipai/bundled/test/team", "agents/a~b/AGENTS.md"); + + expect(route).toBe("/teams-catalog/paperclipai%2Fbundled%2Ftest%2Fteam/files/agents%7Ea~b%7EAGENTS.md"); + expect(parseTeamRoute(route.replace("/teams-catalog/", ""))).toEqual({ + catalogRef: "paperclipai/bundled/test/team", + filePath: "agents/a~b/AGENTS.md", + }); + }); +}); + async function act(callback: () => void | Promise) { let result: void | Promise = undefined; flushSync(() => { diff --git a/ui/src/pages/TeamCatalog.tsx b/ui/src/pages/TeamCatalog.tsx index fc3b8bd64db..70e71dde1d3 100644 --- a/ui/src/pages/TeamCatalog.tsx +++ b/ui/src/pages/TeamCatalog.tsx @@ -174,19 +174,19 @@ function titleCase(slug: string): string { } function encodeTeamFilePath(filePath: string): string { - return filePath.split("/").map(encodeURIComponent).join("~"); + return filePath.split("/").map(encodeURIComponent).join("%7E"); } function decodeTeamFilePath(encoded: string | undefined): string | null { if (!encoded) return null; - return encoded.split("~").map(decodeURIComponent).join("/"); + return encoded.split("%7E").map(decodeURIComponent).join("/"); } type ParsedRoute = { catalogRef: string | null; filePath: string | null }; const TEAM_CATALOG_ROUTE_ROOT = "/teams-catalog"; -function parseTeamRoute(routePath: string | undefined): ParsedRoute { +export function parseTeamRoute(routePath: string | undefined): ParsedRoute { if (!routePath) return { catalogRef: null, filePath: null }; const segments = routePath.split("/").filter(Boolean); if (segments.length === 0) return { catalogRef: null, filePath: null }; @@ -197,7 +197,7 @@ function parseTeamRoute(routePath: string | undefined): ParsedRoute { return { catalogRef, filePath: null }; } -function teamRoute(catalogRef: string, filePath?: string | null): string { +export function teamRoute(catalogRef: string, filePath?: string | null): string { const base = `${TEAM_CATALOG_ROUTE_ROOT}/${encodeURIComponent(catalogRef)}`; if (filePath) return `${base}/files/${encodeTeamFilePath(filePath)}`; return base;