From 2c6ef8a1849563adafd7814151cc4134c4e3fd0d Mon Sep 17 00:00:00 2001 From: Smartsheet-JB-Brown Date: Sun, 20 Apr 2025 20:56:53 -0700 Subject: [PATCH] allow relative path from pacakge items to outside of packages directory --- src/services/marketplace/MetadataScanner.ts | 32 ++++++++++++++- .../MetadataScanner.external.test.ts | 40 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/services/marketplace/__tests__/MetadataScanner.external.test.ts diff --git a/src/services/marketplace/MetadataScanner.ts b/src/services/marketplace/MetadataScanner.ts index 7a722ad4b4..af2495efcd 100644 --- a/src/services/marketplace/MetadataScanner.ts +++ b/src/services/marketplace/MetadataScanner.ts @@ -311,9 +311,37 @@ export class MetadataScanner { parentPath: string = "", ): Promise { try { - const entries = await fs.readdir(packageDir, { withFileTypes: true }) + // First check for explicitly listed items in package metadata + const metadataPath = path.join(packageDir, "metadata.en.yml") + try { + const content = await fs.readFile(metadataPath, "utf-8") + const parsed = yaml.load(content) as PackageMetadata - // Process directories sequentially + if (parsed.items) { + for (const item of parsed.items) { + // For relative paths starting with ../, resolve from package directory + const itemPath = path.join(packageDir, item.path) + const subMetadata = await this.loadComponentMetadata(itemPath) + if (subMetadata) { + const localizedSubMetadata = this.getLocalizedMetadata(subMetadata) + if (localizedSubMetadata) { + packageItem.items = packageItem.items || [] + packageItem.items.push({ + type: localizedSubMetadata.type, + path: item.path, + metadata: localizedSubMetadata, + lastUpdated: await this.getLastModifiedDate(itemPath), + }) + } + } + } + } + } catch (error) { + // Ignore errors reading metadata.en.yml - we'll still scan subdirectories + } + + // Then scan subdirectories for implicit components + const entries = await fs.readdir(packageDir, { withFileTypes: true }) for (const entry of entries) { if (!entry.isDirectory()) continue diff --git a/src/services/marketplace/__tests__/MetadataScanner.external.test.ts b/src/services/marketplace/__tests__/MetadataScanner.external.test.ts new file mode 100644 index 0000000000..bb79b226c4 --- /dev/null +++ b/src/services/marketplace/__tests__/MetadataScanner.external.test.ts @@ -0,0 +1,40 @@ +import * as path from "path" +import { MetadataScanner } from "../MetadataScanner" +import { GitFetcher } from "../GitFetcher" +import * as vscode from "vscode" + +describe("MetadataScanner External References", () => { + it("should find all subcomponents in Project Manager package including external references", async () => { + // Create a GitFetcher instance using the project's mock settings directory + const mockContext = { + globalStorageUri: { fsPath: path.resolve(__dirname, "../../../../mock/settings") }, + } as vscode.ExtensionContext + const gitFetcher = new GitFetcher(mockContext) + + // Fetch the marketplace repository + const repoUrl = "https://github.com/RooVetGit/Roo-Code-Marketplace" + const repo = await gitFetcher.fetchRepository(repoUrl) + + // Find the Project Manager package + const projectManager = repo.items.find((item) => item.name === "Project Manager Package") + expect(projectManager).toBeDefined() + expect(projectManager?.type).toBe("package") + + // Verify it has exactly 2 subcomponents + expect(projectManager?.items).toBeDefined() + expect(projectManager?.items?.length).toBe(2) + + // Verify one is a mode and one is an MCP server + const hasMode = projectManager?.items?.some((item) => item.type === "mode") + const hasMcpServer = projectManager?.items?.some((item) => item.type === "mcp server") + expect(hasMode).toBe(true) + expect(hasMcpServer).toBe(true) + + // Verify the MCP server is the Smartsheet component + const smartsheet = projectManager?.items?.find( + (item) => item.metadata?.name === "Smartsheet MCP - Project Management", + ) + expect(smartsheet).toBeDefined() + expect(smartsheet?.type).toBe("mcp server") + }) +})