refactor(install): prefer native plugin install across targets (#609)
Co-authored-by: John Cavanaugh <cavanaug@users.noreply.github.com>
This commit is contained in:
@@ -46,7 +46,7 @@ const fixturePlugin: ClaudePlugin = {
|
||||
}
|
||||
|
||||
describe("convertClaudeToCodex", () => {
|
||||
test("converts commands to prompts and agents to skills", () => {
|
||||
test("converts commands to prompts and agents to custom agents", () => {
|
||||
const bundle = convertClaudeToCodex(fixturePlugin, {
|
||||
agentMode: "subagent",
|
||||
inferTemperature: false,
|
||||
@@ -64,7 +64,8 @@ describe("convertClaudeToCodex", () => {
|
||||
expect(parsedPrompt.body).toContain("Plan the work.")
|
||||
|
||||
expect(bundle.skillDirs[0]?.name).toBe("existing-skill")
|
||||
expect(bundle.generatedSkills).toHaveLength(2)
|
||||
expect(bundle.generatedSkills).toHaveLength(1)
|
||||
expect(bundle.agents).toHaveLength(1)
|
||||
|
||||
const commandSkill = bundle.generatedSkills.find((skill) => skill.name === "workflows-plan")
|
||||
expect(commandSkill).toBeDefined()
|
||||
@@ -73,16 +74,14 @@ describe("convertClaudeToCodex", () => {
|
||||
expect(parsedCommandSkill.data.description).toBe("Planning command")
|
||||
expect(parsedCommandSkill.body).toContain("Allowed tools")
|
||||
|
||||
const agentSkill = bundle.generatedSkills.find((skill) => skill.name === "security-reviewer")
|
||||
expect(agentSkill).toBeDefined()
|
||||
const parsedSkill = parseFrontmatter(agentSkill!.content)
|
||||
expect(parsedSkill.data.name).toBe("security-reviewer")
|
||||
expect(parsedSkill.data.description).toBe("Security-focused agent")
|
||||
expect(parsedSkill.body).toContain("Capabilities")
|
||||
expect(parsedSkill.body).toContain("Threat modeling")
|
||||
const agent = bundle.agents.find((item) => item.name === "security-reviewer")
|
||||
expect(agent).toBeDefined()
|
||||
expect(agent!.description).toBe("Security-focused agent")
|
||||
expect(agent!.instructions).toContain("Capabilities")
|
||||
expect(agent!.instructions).toContain("Threat modeling")
|
||||
})
|
||||
|
||||
test("drops model field (Codex skill frontmatter does not support model)", () => {
|
||||
test("drops model field from Codex custom agents", () => {
|
||||
const plugin: ClaudePlugin = {
|
||||
...fixturePlugin,
|
||||
agents: [
|
||||
@@ -104,8 +103,9 @@ describe("convertClaudeToCodex", () => {
|
||||
permissions: "none",
|
||||
})
|
||||
|
||||
const skill = bundle.generatedSkills.find((s) => s.name === "fast-agent")
|
||||
expect(parseFrontmatter(skill!.content).data.model).toBeUndefined()
|
||||
const agent = bundle.agents.find((s) => s.name === "fast-agent")
|
||||
expect(agent).toBeDefined()
|
||||
expect("model" in agent!).toBe(false)
|
||||
})
|
||||
|
||||
test("copies workflow skills as regular skills and omits workflows aliases", () => {
|
||||
@@ -190,7 +190,7 @@ describe("convertClaudeToCodex", () => {
|
||||
expect(bundle.mcpServers?.local?.args).toEqual(["hello"])
|
||||
})
|
||||
|
||||
test("transforms Task agent calls to skill references", () => {
|
||||
test("transforms known Task agent calls to custom agent spawns", () => {
|
||||
const plugin: ClaudePlugin = {
|
||||
...fixturePlugin,
|
||||
commands: [
|
||||
@@ -208,7 +208,26 @@ Task best-practices-researcher(topic)`,
|
||||
sourcePath: "/tmp/plugin/commands/plan.md",
|
||||
},
|
||||
],
|
||||
agents: [],
|
||||
agents: [
|
||||
{
|
||||
name: "repo-research-analyst",
|
||||
description: "Repo research",
|
||||
body: "Research repositories.",
|
||||
sourcePath: "/tmp/plugin/agents/repo-research-analyst.md",
|
||||
},
|
||||
{
|
||||
name: "learnings-researcher",
|
||||
description: "Learning research",
|
||||
body: "Search learnings.",
|
||||
sourcePath: "/tmp/plugin/agents/learnings-researcher.md",
|
||||
},
|
||||
{
|
||||
name: "best-practices-researcher",
|
||||
description: "Best practices",
|
||||
body: "Search best practices.",
|
||||
sourcePath: "/tmp/plugin/agents/best-practices-researcher.md",
|
||||
},
|
||||
],
|
||||
skills: [],
|
||||
}
|
||||
|
||||
@@ -222,17 +241,16 @@ Task best-practices-researcher(topic)`,
|
||||
expect(commandSkill).toBeDefined()
|
||||
const parsed = parseFrontmatter(commandSkill!.content)
|
||||
|
||||
// Task calls should be transformed to skill references
|
||||
expect(parsed.body).toContain("Use the $repo-research-analyst skill to: feature_description")
|
||||
expect(parsed.body).toContain("Use the $learnings-researcher skill to: feature_description")
|
||||
expect(parsed.body).toContain("Use the $best-practices-researcher skill to: topic")
|
||||
expect(parsed.body).toContain("Spawn the custom agent `repo-research-analyst` with task: feature_description")
|
||||
expect(parsed.body).toContain("Spawn the custom agent `learnings-researcher` with task: feature_description")
|
||||
expect(parsed.body).toContain("Spawn the custom agent `best-practices-researcher` with task: topic")
|
||||
|
||||
// Original Task syntax should not remain
|
||||
expect(parsed.body).not.toContain("Task repo-research-analyst")
|
||||
expect(parsed.body).not.toContain("Task learnings-researcher")
|
||||
})
|
||||
|
||||
test("transforms namespaced Task agent calls to skill references using final segment", () => {
|
||||
test("transforms namespaced Task agent calls to category-qualified custom agents", () => {
|
||||
const plugin: ClaudePlugin = {
|
||||
...fixturePlugin,
|
||||
commands: [
|
||||
@@ -241,16 +259,35 @@ Task best-practices-researcher(topic)`,
|
||||
description: "Planning with namespaced agents",
|
||||
body: `Run these agents in parallel:
|
||||
|
||||
- Task compound-engineering:research:repo-research-analyst(feature_description)
|
||||
- Task compound-engineering:research:learnings-researcher(feature_description)
|
||||
- Task compound-engineering:research:ce-repo-research-analyst(feature_description)
|
||||
- Task compound-engineering:research:ce-learnings-researcher(feature_description)
|
||||
|
||||
Then consolidate findings.
|
||||
|
||||
Task compound-engineering:review:security-reviewer(code_diff)`,
|
||||
Task compound-engineering:review:ce-security-reviewer(code_diff)`,
|
||||
sourcePath: "/tmp/plugin/commands/plan.md",
|
||||
},
|
||||
],
|
||||
agents: [],
|
||||
agents: [
|
||||
{
|
||||
name: "ce-repo-research-analyst",
|
||||
description: "Repo research",
|
||||
body: "Research repositories.",
|
||||
sourcePath: "/tmp/plugin/agents/research/ce-repo-research-analyst.agent.md",
|
||||
},
|
||||
{
|
||||
name: "ce-learnings-researcher",
|
||||
description: "Learning research",
|
||||
body: "Search learnings.",
|
||||
sourcePath: "/tmp/plugin/agents/research/ce-learnings-researcher.agent.md",
|
||||
},
|
||||
{
|
||||
name: "ce-security-reviewer",
|
||||
description: "Security review",
|
||||
body: "Review security.",
|
||||
sourcePath: "/tmp/plugin/agents/review/ce-security-reviewer.agent.md",
|
||||
},
|
||||
],
|
||||
skills: [],
|
||||
}
|
||||
|
||||
@@ -264,10 +301,9 @@ Task compound-engineering:review:security-reviewer(code_diff)`,
|
||||
expect(commandSkill).toBeDefined()
|
||||
const parsed = parseFrontmatter(commandSkill!.content)
|
||||
|
||||
// Namespaced Task calls should use only the final segment as the skill name
|
||||
expect(parsed.body).toContain("Use the $repo-research-analyst skill to: feature_description")
|
||||
expect(parsed.body).toContain("Use the $learnings-researcher skill to: feature_description")
|
||||
expect(parsed.body).toContain("Use the $security-reviewer skill to: code_diff")
|
||||
expect(parsed.body).toContain("Spawn the custom agent `research-ce-repo-research-analyst` with task: feature_description")
|
||||
expect(parsed.body).toContain("Spawn the custom agent `research-ce-learnings-researcher` with task: feature_description")
|
||||
expect(parsed.body).toContain("Spawn the custom agent `review-ce-security-reviewer` with task: code_diff")
|
||||
|
||||
// Original namespaced Task syntax should not remain
|
||||
expect(parsed.body).not.toContain("Task compound-engineering:")
|
||||
@@ -284,7 +320,14 @@ Task compound-engineering:review:security-reviewer(code_diff)`,
|
||||
sourcePath: "/tmp/plugin/commands/review.md",
|
||||
},
|
||||
],
|
||||
agents: [],
|
||||
agents: [
|
||||
{
|
||||
name: "ce-code-simplicity-reviewer",
|
||||
description: "Simplicity review",
|
||||
body: "Review simplicity.",
|
||||
sourcePath: "/tmp/plugin/agents/review/ce-code-simplicity-reviewer.agent.md",
|
||||
},
|
||||
],
|
||||
skills: [],
|
||||
}
|
||||
|
||||
@@ -297,7 +340,7 @@ Task compound-engineering:review:security-reviewer(code_diff)`,
|
||||
const commandSkill = bundle.generatedSkills.find((s) => s.name === "review")
|
||||
expect(commandSkill).toBeDefined()
|
||||
const parsed = parseFrontmatter(commandSkill!.content)
|
||||
expect(parsed.body).toContain("Use the $code-simplicity-reviewer skill")
|
||||
expect(parsed.body).toContain("Spawn the custom agent `review-ce-code-simplicity-reviewer`")
|
||||
expect(parsed.body).not.toContain("compound-engineering:")
|
||||
expect(parsed.body).not.toContain("skill to:")
|
||||
})
|
||||
@@ -372,15 +415,14 @@ Don't confuse with file paths like /tmp/output.md or /dev/null.`,
|
||||
permissions: "none",
|
||||
})
|
||||
|
||||
const agentSkill = bundle.generatedSkills.find((s) => s.name === "session-historian")
|
||||
expect(agentSkill).toBeDefined()
|
||||
expect(agentSkill!.sidecarDirs).toEqual([
|
||||
const agent = bundle.agents.find((s) => s.name === "research-session-historian")
|
||||
expect(agent).toBeDefined()
|
||||
expect(agent!.sidecarDirs).toEqual([
|
||||
{ sourceDir: scriptDir, targetName: "session-history-scripts" },
|
||||
])
|
||||
|
||||
const parsed = parseFrontmatter(agentSkill!.content)
|
||||
expect(parsed.body).toContain("<script-dir>/discover-sessions.sh")
|
||||
expect(parsed.body).not.toContain("<script-dir>/prompts:discover-sessions.sh")
|
||||
expect(agent!.instructions).toContain("<script-dir>/discover-sessions.sh")
|
||||
expect(agent!.instructions).not.toContain("<script-dir>/prompts:discover-sessions.sh")
|
||||
})
|
||||
|
||||
test("transforms workflow skill slash commands to Codex skill references", () => {
|
||||
@@ -509,7 +551,7 @@ Run \`/compound-engineering-setup\` to create a settings file.`,
|
||||
expect(parsed.body).toContain("compound-engineering.local.md")
|
||||
})
|
||||
|
||||
test("rewrites .claude/ paths in agent skill bodies", () => {
|
||||
test("preserves tool-agnostic paths in Codex custom agent instructions", () => {
|
||||
const plugin: ClaudePlugin = {
|
||||
...fixturePlugin,
|
||||
commands: [],
|
||||
@@ -530,15 +572,12 @@ Run \`/compound-engineering-setup\` to create a settings file.`,
|
||||
permissions: "none",
|
||||
})
|
||||
|
||||
const agentSkill = bundle.generatedSkills.find((s) => s.name === "config-reader")
|
||||
expect(agentSkill).toBeDefined()
|
||||
const parsed = parseFrontmatter(agentSkill!.content)
|
||||
|
||||
// Tool-agnostic path in project root — no rewriting needed
|
||||
expect(parsed.body).toContain("compound-engineering.local.md")
|
||||
const agent = bundle.agents.find((s) => s.name === "config-reader")
|
||||
expect(agent).toBeDefined()
|
||||
expect(agent!.instructions).toContain("compound-engineering.local.md")
|
||||
})
|
||||
|
||||
test("truncates generated skill descriptions to Codex limits and single line", () => {
|
||||
test("truncates custom agent descriptions to Codex limits and single line", () => {
|
||||
const longDescription = `Line one\nLine two ${"a".repeat(2000)}`
|
||||
const plugin: ClaudePlugin = {
|
||||
...fixturePlugin,
|
||||
@@ -560,9 +599,7 @@ Run \`/compound-engineering-setup\` to create a settings file.`,
|
||||
permissions: "none",
|
||||
})
|
||||
|
||||
const generated = bundle.generatedSkills[0]
|
||||
const parsed = parseFrontmatter(generated.content)
|
||||
const description = String(parsed.data.description ?? "")
|
||||
const description = bundle.agents[0].description
|
||||
expect(description.length).toBeLessThanOrEqual(1024)
|
||||
expect(description).not.toContain("\n")
|
||||
expect(description.endsWith("...")).toBe(true)
|
||||
|
||||
Reference in New Issue
Block a user