Files
oh-my-opencode/script/sync-lazyclaudecode-marketplace.test.ts
T
YeonGyu-Kim 7590c7df04 feat(script): add lazyclaudecode marketplace syncer with CC paths and filter
Forks sync-lazycodex-marketplace: reads .claude-plugin/plugin.json, writes
.claude-plugin/marketplace.json + plugins/omo, asserts sisyphuslabs/omo
identity, excludes src/test/tsconfig*/.omo from the shipped tree. RED->GREEN
test (6 pass).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:40:10 +09:00

171 lines
7.5 KiB
TypeScript

/// <reference types="bun-types" />
import { describe, expect, test } from "bun:test"
import { mkdir, mkdtemp, readFile, stat, writeFile } from "node:fs/promises"
import { tmpdir } from "node:os"
import { dirname, join } from "node:path"
import { syncLazyclaudecodeMarketplace } from "./sync-lazyclaudecode-marketplace"
async function writeJson(path: string, value: unknown): Promise<void> {
await mkdir(dirname(path), { recursive: true })
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`)
}
async function writeText(path: string, text: string): Promise<void> {
await mkdir(dirname(path), { recursive: true })
await writeFile(path, text)
}
interface PluginFixtureOptions {
readonly marketplaceName?: string
readonly pluginName?: string
}
async function writePluginFixture(sourceRoot: string, options: PluginFixtureOptions = {}): Promise<void> {
const marketplaceName = options.marketplaceName ?? "sisyphuslabs"
const pluginName = options.pluginName ?? "omo"
const pluginRoot = join(sourceRoot, "packages", "omo-claude", "plugin")
await writeJson(join(sourceRoot, "packages", "omo-claude", "marketplace.json"), {
name: marketplaceName,
owner: { name: "Yeongyu Kim", url: "https://github.com/code-yeongyu" },
plugins: [{ name: pluginName, source: "./plugins/omo" }],
})
await writeJson(join(pluginRoot, ".claude-plugin", "plugin.json"), {
name: pluginName,
version: "1.2.3",
})
// Runtime artifacts that MUST be shipped.
await writeText(join(pluginRoot, "README.md"), "omo\n")
await writeJson(join(pluginRoot, ".mcp.json"), { mcpServers: {} })
await writeText(join(pluginRoot, "hooks", "hooks.json"), "{}\n")
await writeText(join(pluginRoot, "skills", "demo", "SKILL.md"), "# demo\n")
await writeText(join(pluginRoot, "agents", "planner.md"), "# planner\n")
await writeText(join(pluginRoot, "components", "rules", "dist", "cli.js"), "// built\n")
await writeText(join(pluginRoot, "mcp", "ast-grep", "cli.js"), "// mcp\n")
// Entries that MUST be excluded by the copy filter.
await writeText(join(pluginRoot, "node_modules", "ignored", "file.txt"), "ignored\n")
await writeText(join(pluginRoot, "components", "rules", "src", "cli.ts"), "// source\n")
await writeText(join(pluginRoot, "components", "rules", "tsconfig.build.json"), "{}\n")
await writeText(join(pluginRoot, "components", "rules", "test", "cli.test.ts"), "// test\n")
await writeText(join(pluginRoot, "src", "index.ts"), "// source\n")
await writeText(join(pluginRoot, "test", "smoke.test.ts"), "// test\n")
await writeText(join(pluginRoot, "tsconfig.json"), "{}\n")
await writeText(join(pluginRoot, ".omo", "ultragoal", "state.json"), "{}\n")
}
async function exists(path: string): Promise<boolean> {
try {
await stat(path)
return true
} catch {
return false
}
}
describe("sync-lazyclaudecode-marketplace", () => {
test("reads .claude-plugin/plugin.json and writes .claude-plugin/marketplace.json into the dest", async () => {
// given
const sourceRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-source-"))
const lazyclaudecodeRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-dest-"))
await writePluginFixture(sourceRoot)
// when
await syncLazyclaudecodeMarketplace({ sourceRoot, lazyclaudecodeRoot })
// then — destination marketplace at the CC path
const marketplace = JSON.parse(
await readFile(join(lazyclaudecodeRoot, ".claude-plugin", "marketplace.json"), "utf8"),
)
expect(marketplace.name).toBe("sisyphuslabs")
expect(marketplace.plugins[0].source).toBe("./plugins/omo")
// the plugin manifest came from .claude-plugin/plugin.json and landed under plugins/omo
const manifest = JSON.parse(
await readFile(join(lazyclaudecodeRoot, "plugins", "omo", ".claude-plugin", "plugin.json"), "utf8"),
)
expect(manifest).toMatchObject({ name: "omo", version: "1.2.3" })
// NOT the codex dest path
expect(await exists(join(lazyclaudecodeRoot, ".agents", "plugins", "marketplace.json"))).toBe(false)
})
test("ships runtime artifacts into plugins/omo", async () => {
// given
const sourceRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-source-"))
const lazyclaudecodeRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-dest-"))
await writePluginFixture(sourceRoot)
// when
await syncLazyclaudecodeMarketplace({ sourceRoot, lazyclaudecodeRoot })
// then
const pluginDest = join(lazyclaudecodeRoot, "plugins", "omo")
expect(await exists(join(pluginDest, ".mcp.json"))).toBe(true)
expect(await exists(join(pluginDest, "hooks", "hooks.json"))).toBe(true)
expect(await exists(join(pluginDest, "skills", "demo", "SKILL.md"))).toBe(true)
expect(await exists(join(pluginDest, "agents", "planner.md"))).toBe(true)
expect(await exists(join(pluginDest, "components", "rules", "dist", "cli.js"))).toBe(true)
expect(await exists(join(pluginDest, "mcp", "ast-grep", "cli.js"))).toBe(true)
})
test("copy filter excludes node_modules, src/, test/, tsconfig*.json, and .omo/", async () => {
// given
const sourceRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-source-"))
const lazyclaudecodeRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-dest-"))
await writePluginFixture(sourceRoot)
// when
await syncLazyclaudecodeMarketplace({ sourceRoot, lazyclaudecodeRoot })
// then
const pluginDest = join(lazyclaudecodeRoot, "plugins", "omo")
expect(await exists(join(pluginDest, "node_modules"))).toBe(false)
expect(await exists(join(pluginDest, "src"))).toBe(false)
expect(await exists(join(pluginDest, "test"))).toBe(false)
expect(await exists(join(pluginDest, "tsconfig.json"))).toBe(false)
expect(await exists(join(pluginDest, ".omo"))).toBe(false)
// nested src/test/tsconfig under a component are excluded too
expect(await exists(join(pluginDest, "components", "rules", "src"))).toBe(false)
expect(await exists(join(pluginDest, "components", "rules", "test"))).toBe(false)
expect(await exists(join(pluginDest, "components", "rules", "tsconfig.build.json"))).toBe(false)
})
test("throws when the marketplace name is not sisyphuslabs", async () => {
// given
const sourceRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-source-"))
const lazyclaudecodeRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-dest-"))
await writePluginFixture(sourceRoot, { marketplaceName: "wrong" })
// when / then
await expect(syncLazyclaudecodeMarketplace({ sourceRoot, lazyclaudecodeRoot })).rejects.toThrow("sisyphuslabs")
})
test("throws when the plugin name is not omo", async () => {
// given
const sourceRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-source-"))
const lazyclaudecodeRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-dest-"))
await writePluginFixture(sourceRoot, { pluginName: "not-omo" })
// when / then
await expect(syncLazyclaudecodeMarketplace({ sourceRoot, lazyclaudecodeRoot })).rejects.toThrow("omo")
})
test("throws when the .claude-plugin/plugin.json manifest is missing", async () => {
// given
const sourceRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-source-"))
const lazyclaudecodeRoot = await mkdtemp(join(tmpdir(), "omo-claude-sync-dest-"))
await writeJson(join(sourceRoot, "packages", "omo-claude", "marketplace.json"), {
name: "sisyphuslabs",
plugins: [{ name: "omo", source: "./plugins/omo" }],
})
// when / then
await expect(syncLazyclaudecodeMarketplace({ sourceRoot, lazyclaudecodeRoot })).rejects.toThrow(
".claude-plugin/plugin.json",
)
})
})