Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions packages/config-yaml/src/load/unroll.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getTemplateVariables,
parseMarkdownRuleOrAssistantUnrolled,
replaceInputsWithSecrets,
unrollBlocks,
} from "./unroll.js";

describe("parseMarkdownRuleOrAssistantUnrolled tests", () => {
Expand Down Expand Up @@ -92,6 +93,41 @@ model: # should be models
expect(rule).toHaveProperty("globs", undefined);
expect(rule).toHaveProperty("rule", dubiousContent);
});

it("unrolls markdown files used in prompts as prompts", async () => {
const markdownContent = `
---
name: scotty
description: A reusable prompt
---
Generate a concise repair plan.
`;

const registry = {
getContent: async () => markdownContent,
};

const result = await unrollBlocks(
{
name: "Test Agent",
version: "1.0.0",
prompts: [{ uses: "file://prompts/scotty.md" }],
},
registry,
undefined,
);

expect(result.errors).toBeUndefined();
expect(result.config?.rules).toBeUndefined();
expect(result.config?.prompts).toEqual([
{
name: "scotty",
description: "A reusable prompt",
prompt: "Generate a concise repair plan.",
sourceFile: "prompts/scotty.md",
},
]);
});
});

describe("replaceInputsWithSecrets tests", () => {
Expand Down
48 changes: 43 additions & 5 deletions packages/config-yaml/src/load/unroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
blockSchema,
ConfigYaml,
configYamlSchema,
Prompt,
Rule,
} from "../schemas/index.js";
import { ConfigResult, ConfigValidationError } from "../validation.js";
Expand Down Expand Up @@ -448,6 +449,7 @@ export async function unrollBlocks(
blockIdentifier,
unrolledBlock.with,
registry,
section,
);
const block = blockConfigYaml[section]?.[0];
if (block) {
Expand Down Expand Up @@ -514,6 +516,7 @@ export async function unrollBlocks(
decodePackageIdentifier(rule.uses),
rule.with,
registry,
"rules",
);
const block = blockConfigYaml.rules?.[0];
return { index, rule: block || null, error: null };
Expand Down Expand Up @@ -731,6 +734,7 @@ export async function resolveBlock(
id: PackageIdentifier,
inputs: Record<string, string | undefined> | undefined,
registry: Registry,
sectionHint?: BlockType,
): Promise<AssistantUnrolled> {
// Retrieve block raw yaml
const rawYaml = await registry.getContent(id);
Expand Down Expand Up @@ -764,7 +768,11 @@ export async function resolveBlock(
}

// Add source slug for mcp servers
const parsed = parseMarkdownRuleOrAssistantUnrolled(templatedYaml, id);
const parsed = parseMarkdownRuleOrAssistantUnrolled(
templatedYaml,
id,
sectionHint,
);
if (
id.uriType === "slug" &&
"mcpServers" in parsed &&
Expand All @@ -779,8 +787,14 @@ export async function resolveBlock(
export function parseMarkdownRuleOrAssistantUnrolled(
content: string,
id: PackageIdentifier,
sectionHint?: BlockType,
): AssistantUnrolled {
return parseYamlOrMarkdownRule<AssistantUnrolled>(content, id, parseBlock);
return parseYamlOrMarkdownRule<AssistantUnrolled>(
content,
id,
parseBlock,
sectionHint,
);
}

function parseMarkdownRuleOrConfigYaml(
Expand All @@ -790,10 +804,21 @@ function parseMarkdownRuleOrConfigYaml(
return parseYamlOrMarkdownRule<ConfigYaml>(content, id, parseConfigYaml);
}

function markdownToPrompt(content: string, id: PackageIdentifier): Prompt {
const rule = markdownToRule(content, id);
return {
name: rule.name,
description: rule.description,
prompt: rule.rule,
sourceFile: rule.sourceFile,
};
}

function parseYamlOrMarkdownRule<T>(
content: string,
id: PackageIdentifier,
parseYamlFn: (content: string) => T,
sectionHint?: BlockType,
): T {
let parsedYaml: T;
try {
Expand All @@ -808,9 +833,22 @@ function parseYamlOrMarkdownRule<T>(
}
// If YAML parsing fails, try parsing as markdown rule
try {
const rule = markdownToRule(content, id);
// Convert the rule object to the expected format
parsedYaml = { name: rule.name, version: "1.0.0", rules: [rule] } as T;
if (sectionHint === "prompts") {
const prompt = markdownToPrompt(content, id);
parsedYaml = {
name: prompt.name,
version: "1.0.0",
prompts: [prompt],
} as T;
} else {
const rule = markdownToRule(content, id);
// Convert the rule object to the expected format
parsedYaml = {
name: rule.name,
version: "1.0.0",
rules: [rule],
} as T;
}
} catch (markdownError) {
// If both fail, throw the original YAML error
throw yamlError;
Expand Down
Loading