Skip to content

Commit 2c6ef8a

Browse files
allow relative path from pacakge items to outside of packages directory
1 parent 5eaf8ba commit 2c6ef8a

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

src/services/marketplace/MetadataScanner.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,37 @@ export class MetadataScanner {
311311
parentPath: string = "",
312312
): Promise<void> {
313313
try {
314-
const entries = await fs.readdir(packageDir, { withFileTypes: true })
314+
// First check for explicitly listed items in package metadata
315+
const metadataPath = path.join(packageDir, "metadata.en.yml")
316+
try {
317+
const content = await fs.readFile(metadataPath, "utf-8")
318+
const parsed = yaml.load(content) as PackageMetadata
319+
320+
if (parsed.items) {
321+
for (const item of parsed.items) {
322+
// For relative paths starting with ../, resolve from package directory
323+
const itemPath = path.join(packageDir, item.path)
324+
const subMetadata = await this.loadComponentMetadata(itemPath)
325+
if (subMetadata) {
326+
const localizedSubMetadata = this.getLocalizedMetadata(subMetadata)
327+
if (localizedSubMetadata) {
328+
packageItem.items = packageItem.items || []
329+
packageItem.items.push({
330+
type: localizedSubMetadata.type,
331+
path: item.path,
332+
metadata: localizedSubMetadata,
333+
lastUpdated: await this.getLastModifiedDate(itemPath),
334+
})
335+
}
336+
}
337+
}
338+
}
339+
} catch (error) {
340+
// Ignore errors reading metadata.en.yml - we'll still scan subdirectories
341+
}
315342

316-
// Process directories sequentially
343+
// Then scan subdirectories for implicit components
344+
const entries = await fs.readdir(packageDir, { withFileTypes: true })
317345
for (const entry of entries) {
318346
if (!entry.isDirectory()) continue
319347

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as path from "path"
2+
import { MetadataScanner } from "../MetadataScanner"
3+
import { GitFetcher } from "../GitFetcher"
4+
import * as vscode from "vscode"
5+
6+
describe("MetadataScanner External References", () => {
7+
it("should find all subcomponents in Project Manager package including external references", async () => {
8+
// Create a GitFetcher instance using the project's mock settings directory
9+
const mockContext = {
10+
globalStorageUri: { fsPath: path.resolve(__dirname, "../../../../mock/settings") },
11+
} as vscode.ExtensionContext
12+
const gitFetcher = new GitFetcher(mockContext)
13+
14+
// Fetch the marketplace repository
15+
const repoUrl = "https://github.com/RooVetGit/Roo-Code-Marketplace"
16+
const repo = await gitFetcher.fetchRepository(repoUrl)
17+
18+
// Find the Project Manager package
19+
const projectManager = repo.items.find((item) => item.name === "Project Manager Package")
20+
expect(projectManager).toBeDefined()
21+
expect(projectManager?.type).toBe("package")
22+
23+
// Verify it has exactly 2 subcomponents
24+
expect(projectManager?.items).toBeDefined()
25+
expect(projectManager?.items?.length).toBe(2)
26+
27+
// Verify one is a mode and one is an MCP server
28+
const hasMode = projectManager?.items?.some((item) => item.type === "mode")
29+
const hasMcpServer = projectManager?.items?.some((item) => item.type === "mcp server")
30+
expect(hasMode).toBe(true)
31+
expect(hasMcpServer).toBe(true)
32+
33+
// Verify the MCP server is the Smartsheet component
34+
const smartsheet = projectManager?.items?.find(
35+
(item) => item.metadata?.name === "Smartsheet MCP - Project Management",
36+
)
37+
expect(smartsheet).toBeDefined()
38+
expect(smartsheet?.type).toBe("mcp server")
39+
})
40+
})

0 commit comments

Comments
 (0)