In the previous chapter, Markdown-Based Configuration strategy, we learned how to write a style configuration using a simple Markdown file.
Now, we need to answer the question: Where do we put that file?
This chapter covers Hierarchical File Loading. This is the logic the system uses to find your files, and more importantly, decide which one to use if there are duplicates.
Imagine you are a handy person working on a specific job, like fixing a sink.
The Rule: When you need a tool, you look in your Portable Toolbox first.
Let's apply this to our AI styles.
pirate in your user folder. It makes the AI talk like a standard 17th-century sailor. You use this in most of your projects.
You don't want to delete your standard pirate style. Instead, you create a new pirate.md inside your project folder. Because of Hierarchical File Loading, the system sees the project file and effectively says: "For this specific project, 'pirate' means Space Pirate."
The application looks for styles in two specific places.
.claude/output-styles/ (inside your current project folder).~/.claude/output-styles/ (in your computer's home directory).When the application starts, it scans both locations and merges them into a single list.
Here is how the system decides which file to load.
Let's look at loadOutputStylesDir.ts. This file orchestrates the loading process.
It uses a helper function (which we won't dive into deeply, but we will see how it is called) that scans the directories for us.
The function getOutputStyleDirStyles is the entry point. It asks a utility to go look for files named output-styles.
// Inside loadOutputStylesDir.ts
// 'cwd' is your current project folder
export const getOutputStyleDirStyles = memoize(
async (cwd: string): Promise<OutputStyleConfig[]> => {
// This helper scans both Project and Home directories
const markdownFiles = await loadMarkdownFilesForSubdir(
'output-styles',
cwd,
)
// ... processing continues
Explanation: loadMarkdownFilesForSubdir does the hard work of walking to the "Garage" and the "Toolbox" and collecting everything it finds into the markdownFiles array.
Once we have the raw files, we transform them into the configuration objects we learned about in Output Style Configuration.
Interestingly, the system keeps track of where the file came from using a source property.
// Inside the .map() function
return {
name,
description,
prompt: content.trim(),
source, // This tells us if it came from 'project' or 'user'
keepCodingInstructions,
}
Explanation: The source variable helps the system debug or display where a specific style originated, confirming if you are using the generic version or the project-specific version.
File loading can sometimes fail (maybe a file is corrupted). The hierarchy logic includes safety nets to ensure one bad file doesn't crash the whole system.
// Inside the .map() function
} catch (error) {
logError(error)
return null // Return null if this specific file fails
}
})
.filter(style => style !== null) // Remove the failed ones
Explanation: The code tries to process a file. If it fails (catch), it logs an error and ignores that specific file, returning only the valid styles to the user.
To achieve our "Cyber-Pirate" goal:
~/.claude/output-styles/pirate.md./my-game/.claude/output-styles/pirate.md
When you run the application inside ./my-game/, the loadMarkdownFilesForSubdir function finds both. Because the names match (pirate), the Project version wins. The AI receives the instruction: "You are a Cyber-Pirate in the year 3000."
In this chapter, we learned about Hierarchical File Loading.
Now that we know how to write styles and where to put them, we need to look closer at the metadata (the headers in our files). Sometimes, users write messy metadata, and the system needs to clean it up.
In the next chapter, we will learn how the system handles this in Metadata Parsing and Coercion.
Generated by Code IQ