Sharing Rules and Skills Across Projects
An LLM forgets your conventions the moment a chat ends. CodeCompanion gives you two ways to hand them over once: rules, which are files added to every new chat, and skills, which the LLM only reads in full when a task calls for them.
Rules Already in Your Project
If your project has an AGENTS.md or CLAUDE.md, there's nothing to configure. Every new chat buffer loads the default rule group, which picks up whichever of these files exist:
| File | Location |
|---|---|
AGENTS.md, AGENT.md | Current working directory |
CLAUDE.md, CLAUDE.local.md | Current working directory |
~/.claude/CLAUDE.md | Your home directory |
.github/copilot-instructions.md | Current working directory |
.cursorrules, .clinerules, .windsurfrules, .goosehints, .rules | Current working directory |
Files that don't exist are skipped. @path imports inside AGENTS.md and CLAUDE.md are followed, so a line like @docs/STYLE.md shares that file too. See Using Rules for how the paths are resolved.
Cursor's newer .cursor/rules directory isn't in the default group. To add it alongside the defaults, create a group for it and autoload both:
require("codecompanion").setup({
rules = {
cursor = {
description = "Cursor rules",
files = {
".cursor/rules/**/*.mdc",
},
},
opts = {
chat = {
autoload = { "default", "cursor" },
},
},
},
})A glob matches nothing in a project without the directory, so this is safe to leave on everywhere. Cursor's frontmatter, such as globs and alwaysApply, isn't interpreted - every matching file is shared in full.
Personal Rules in Every Project
~/.claude/CLAUDE.md is already loaded everywhere. If you'd rather keep your own conventions outside of Claude's directory, put them in a group of their own and autoload it after default:
require("codecompanion").setup({
rules = {
personal = {
description = "My conventions for every project",
files = {
{ path = "~/.config/codecompanion/rules", files = "*.md" },
},
},
opts = {
chat = {
autoload = { "default", "personal" },
},
},
},
})Groups are loaded in the order they're listed, and a file that appears in more than one group is only added once. ~/.config/codecompanion/rules isn't a location CodeCompanion knows about - it's just a directory you create. Any path works.
To load a group only in certain projects, make autoload a function:
require("codecompanion").setup({
rules = {
opts = {
chat = {
---@return string|string[]
autoload = function()
if vim.fn.getcwd():find("work", 1, true) then
return { "default", "personal", "work" }
end
return { "default", "personal" }
end,
},
},
},
})Any group can also be added to an open chat with the /rules slash command, and gM removes every rule from the chat. See Configuring Rules for directories, nested groups and parsers.
Different Rules for Different Adapters
rules.opts.chat.enabled can be a function. It's called with the chat when the chat is created, so you can check the adapter:
require("codecompanion").setup({
rules = {
opts = {
chat = {
---@param chat CodeCompanion.Chat
---@return boolean
enabled = function(chat)
return chat.adapter.type == "http"
end,
},
},
},
})This skips rules entirely for agents, which is what you want if the agent already reads CLAUDE.md or AGENTS.md itself. Check chat.adapter.name, such as "claude_code" or "codex", to target one agent.
NOTE
enabled is only checked when the chat is created. Switching adapter in an open chat doesn't add or remove rules
Skills
A skill is a folder with a SKILL.md inside, following Claude's format. Only its name and description go into the chat. The LLM reads the rest with the read_file tool when it decides the skill applies, so a long skill costs almost nothing until it's used.
CodeCompanion looks for skills in these directories, and a later one wins if two skills share a name:
| Directory | Scope |
|---|---|
~/.config/codecompanion/skills | Personal |
.codecompanion/skills | Project |
~/.agents/skills | Personal, shared with other agents |
~/.claude/skills | Personal, shared with Claude Code |
.claude/skills | Project, shared with Claude Code |
So a skill in ~/.claude/skills is already available, and a project's .claude/skills overrides a personal skill of the same name. Skills sit one level down, as <dir>/<skill-name>/SKILL.md.
The LLM only knows about the skills you add to the chat. Add them with the /skills slash command, or have some added to every chat:
require("codecompanion").setup({
skills = {
opts = {
chat = {
autoload = { "lua-developer", "code-review" },
},
},
},
})autoload takes skill names or group names, and a group can be added in one go with /skills-group. Descriptions can span several lines using YAML's > folded style:
---
name: lua-developer
description: >
The Lua conventions for this project.
Use when writing or reviewing Lua.
---See Configuring Skills for the full set of options and Creating Skills for how to write one.
Agents
Agents like Claude Code and Codex bring their own way of loading project instructions, so it's worth knowing what CodeCompanion adds on top:
- Rules - Loaded by default, and sent to the agent as text with your first message. A system prompt taken from a rules file isn't sent, as agents don't take one
- Skills - Not added.
/skillsand/skills-groupare hidden, andautoloadis skipped. Use the agent's own skills through the\trigger instead, as covered in Skills on ACP Adapters
If your agent already reads CLAUDE.md or AGENTS.md, turn rules off for agents with the enabled function above so the file isn't sent twice.
Rules, Skills, System Prompts or Prompts
| Use | When | Applies to |
|---|---|---|
| Rules | The LLM should always know it, like coding style or project layout | Every chat, or the chats you add it to |
| Skills | The LLM needs it for certain tasks, like writing a migration or a release | Chats you add it to, read only when relevant |
| System prompt | You want to change how the LLM behaves in every chat, whatever the project | Every chat with an http adapter |
| Prompt library | You repeat the same request, like writing a commit message | The chat you start from the prompt |
A prompt library item can name its own rules and skills, so a prompt like "Review this PR" can always bring your code-review skill with it. See rules in prompts and skills in prompts.
Limitations
- Skills need the Tree-sitter
yamlparser. Run:TSInstall yamlif/skillsshows nothing - Skills need an http adapter with tool use. They're not added for a model that has tools turned off
- A group's own
enabledfunction only hides it from/rulesand the Action Palette. It doesn't stop the group being autoloaded, so use anautoloadfunction for that