Welcome to the outputStyles project! If you've ever wanted to make an AI sound like a 17th-century pirate, a strict code reviewer, or a friendly tutor, you are in the right place.
In this first chapter, we will explore the core building block of this system: the Output Style Configuration.
Imagine you run a restaurant (the application) and you have a chef (the AI). You want the chef to cook different dishes depending on what the customer asks for.
To do this, you hand the chef a Recipe Card. This card needs to follow a standard format so the chef can understand it quickly. It usually contains:
In our software, the Output Style Configuration is that Recipe Card. It is a simple data structure that bundles the instructions for the AI into a single, easy-to-pass-around package.
Throughout this chapter, we will try to solve a simple goal: We want the AI to speak like a pirate.
To do this, we need to create a configuration object that contains the rules for "Pirate Mode" so the rest of the application knows how to handle it.
The configuration is just an object (a collection of data). Here are the key parts that make up our "Recipe Card":
By ensuring every style looks like this, the system doesn't care if the style came from a file, a plugin, or user input. It handles them all exactly the same way.
Here is what this structure looks like in a simple TypeScript object.
// This is our "Recipe Card"
const pirateStyle = {
name: "pirate",
description: "Talks like a sea captain",
prompt: "You are a pirate. Start sentences with 'Arrr'.",
source: "project" // Just a note on where it came from
};
This simple object is the destination we are trying to reach. Everything else in this project exists to help us build objects like this one.
How does the application actually build these objects? It usually reads raw data (like a text file) and converts it into this structured format.
Here is a high-level view of what happens when the application processes a style.
OutputStyleConfig object.
Let's look at the actual code in loadOutputStylesDir.ts. This file is responsible for taking raw file data and turning it into our configuration object.
We will focus on the .map() function, which is the factory line creating these objects.
First, the code needs to decide what to call the style. It prefers a specific name provided in the metadata, but if that's missing, it simply uses the filename.
// Inside loadOutputStylesDir.ts
const fileName = basename(filePath)
const styleName = fileName.replace(/\.md$/, '')
// Use name from frontmatter (metadata), or fallback to filename
const name = (frontmatter['name'] || styleName) as string
Explanation: If your file is named pirate.md, the styleName becomes pirate.
Next, it looks for a description. It tries to read it from metadata first. If that fails, it tries to read the first few lines of the text content itself.
// Try to get description from frontmatter or extract from content
const description =
coerceDescriptionToString(
frontmatter['description'],
styleName,
) ??
extractDescriptionFromMarkdown(content, `Custom ${styleName} output style`)
Explanation: We will learn more about how this parsing works in Metadata Parsing and Coercion.
Sometimes, we have specific instructions, like keep-coding-instructions. This tells the AI if it should strictly follow coding rules or relax them.
// Parse the boolean flag from the raw data
const keepCodingInstructionsRaw = frontmatter['keep-coding-instructions']
const keepCodingInstructions =
keepCodingInstructionsRaw === true || keepCodingInstructionsRaw === 'true'
? true
: undefined // logic simplified for clarity
Explanation: This adds an extra checkbox to our "Recipe Card" regarding coding rules.
Finally, the code returns the nice, clean object we discussed at the beginning.
// Return the structured configuration
return {
name,
description,
prompt: content.trim(),
source,
keepCodingInstructions,
}
Explanation: content.trim() ensures the prompt doesn't have accidental empty space around it.
In this chapter, we learned about the Output Style Configuration. It acts as a standardized "Recipe Card" containing a Name, Description, and Prompt. This abstraction ensures that no matter how complex the instructions are, the application treats them all the same way.
But how do we write these recipes without hard-coding them into the software? We need a way to write them in plain text.
In the next chapter, we will learn how to write these recipes using the Markdown-Based Configuration strategy.
Generated by Code IQ