Includes System¶
Reuse configurations across multiple projects through inheritance and composition.
Overview¶
Includes work through configuration inheritance:
- Define common rules once in a shared configuration
- Share rules, context, skills, agents, and commands across projects
- Mix and match includes to create project-specific configurations
- Track changes through version control
How Includes Work¶
Includes allow one .ai-rulez/ configuration to inherit content from other configurations. Use for:
- Organization-wide coding standards
- Framework-specific guidelines (React, Go, Python)
- Consistent security policies
- Team-specific workflows
Basic Example¶
Creating a Shared Configuration¶
Create a .ai-rulez/ directory that others can include:
shared-rules/.ai-rulez/config.toml:
version = "4.0"
name = "shared-rules"
description = "Organization-wide AI rules"
presets = []
[profiles]
default = []
shared-rules/.ai-rulez/rules/security.md:
---
priority: critical
---
# Security Standards
- Always validate user input
- Use parameterized queries
- Never hardcode secrets
- Rotate credentials regularly
Including in Your Project¶
In your project's .ai-rulez/config.toml, reference the shared rules:
version = "4.0"
name = "my-project"
# Include rules from another directory
includes = [
{ name = "shared-rules", source = "../shared-rules/.ai-rulez" }
]
presets = ["claude", "cursor"]
[profiles]
default = []
Now your project includes all content from the shared configuration.
Include Paths¶
Includes can be:
- Relative paths:
../shared-rules/.ai-rulez,./team-guidelines/.ai-rulez - Absolute paths:
/etc/ai-rulez-standards/.ai-rulez - Git URLs:
https://github.com/org/shared-rules.git,git@github.com:org/shared-rules.git
Examples¶
Sibling directory:
[[includes]]
name = "shared-rules"
source = "../shared-rules"
include = ["rules", "context"]
merge_strategy = "local-override"
Subdirectory:
[[includes]]
name = "team-config"
source = "./config/shared"
include = ["rules", "skills"]
merge_strategy = "local-override"
Git repository (HTTPS):
[[includes]]
name = "org-standards"
source = "https://github.com/myorg/shared-rules.git"
ref = "main"
include = ["rules", "context", "skills", "agents"]
merge_strategy = "local-override"
Git repository (SSH):
[[includes]]
name = "company-policies"
source = "git@github.com:company/ai-rulez.git"
ref = "v1.2.3"
include = ["rules", "context"]
merge_strategy = "local-override"
Multiple includes:
[[includes]]
name = "team-guidelines"
source = "../team-guidelines"
include = ["rules", "context"]
merge_strategy = "local-override"
[[includes]]
name = "org-standards"
source = "git@gitlab.com:org/standards.git"
ref = "main"
include = ["rules", "skills"]
merge_strategy = "local-override"
[[includes]]
name = "security-policies"
source = "./security-policies"
include = ["rules"]
merge_strategy = "local-override"
Supported Git URL Formats¶
- HTTPS:
https://github.com/owner/repo.git - SSH:
git@github.com:owner/repo.git - SSH protocol:
ssh://git@github.com/owner/repo.git - GitLab:
https://gitlab.com/owner/repo.git,git@gitlab.com:owner/repo.git - Self-hosted GitLab:
git@git.example.com:owner/repo.git,https://git.example.com/owner/repo.git
SSH Cloning for Private Repositories¶
For private repositories that require SSH authentication, ai-rulez automatically uses git clone when it detects SSH URLs (git@... or ssh://...). This leverages your existing SSH key configuration.
Benefits of SSH cloning:
- Works with private repositories without needing access tokens
- Uses your configured SSH keys and agent
- Supports self-hosted Git servers (GitLab, Gitea, Gogs, etc.)
- Ideal for local development and multi-repo setups
Example with SSH:
[[includes]]
name = "private-rules"
source = "git@git.example.com:company/ai-rulez.git"
ref = "main"
include = ["rules", "context"]
merge_strategy = "local-override"
Requirements:
- Git must be installed and available in your PATH
- SSH keys must be configured for the git host
- SSH agent should be running (for passphrase-protected keys)
Repository Structure Support¶
An include source does not have to wrap its content in a .ai-rulez/ folder. ai-rulez
supports both a wrapped and a bare (flattened) layout and auto-detects which one a source uses.
- Wrapped structure: Content lives inside a
.ai-rulez/subdirectory
my-repo/
├── .ai-rulez/
│ ├── config.toml
│ ├── rules/
│ ├── context/
│ ├── skills/
│ └── agents/
└── other files...
- Bare / flattened structure (recommended for shared-module repos): the directory exposes
rules/,context/,skills/, andagents/directly — no.ai-rulez/wrapper needed. This works at the repository root or at any sub-path:
Point an include at the sub-path with the path field:
[[includes]]
name = "core"
source = "https://github.com/org/shared-modules.git"
path = "modules/core" # resolves modules/core/rules, modules/core/skills, ...
include = ["rules", "skills"]
merge_strategy = "local-override"
ai-rulez detects the layout automatically: it first looks for a .ai-rulez/ directory (at the
source root or under path), and otherwise treats a directory that contains any of rules/,
context/, skills/, agents/, or commands/ as a bare ai-rulez structure. The flat layout keeps
shared modules — especially skill-first modules that ship mostly skills/<id>/SKILL.md — clean and
free of boilerplate wrapping.
Note
ai-rulez include add with a local path validates that the directory contains a .ai-rulez/
subdirectory, so a bare/flattened local source is rejected by the CLI even though the resolver
accepts it. Add a bare local include by editing config.toml directly (git sources are not
restricted this way).
Private Repository Authentication (HTTPS)¶
When working with private Git repositories in includes, you can authenticate using an access token.
Using Environment Variable (Recommended)¶
Set the AI_RULEZ_GIT_TOKEN environment variable with your access token:
This is the recommended approach for CI/CD environments and automation scripts.
Using CLI Flag¶
Pass the token directly via the --token flag:
Creating Access Tokens¶
GitHub:
- Go to Settings → Developer settings → Personal access tokens
- Click "Generate new token (classic)"
- Select scopes:
repo- Required for accessing private repositories- Generate and copy the token
GitLab:
- Go to User Settings → Access Tokens
- Click "Add new token"
- Select scopes:
read_repository- Required for reading private repositories- Create token and copy it
Other Git Hosts:
Most Git hosting platforms that support Bearer token authentication will work with ai-rulez. The token is sent as a Bearer token in the Authorization header when fetching repository archives.
Security Best Practices¶
- Never commit tokens to your repository or configuration files
- Use environment variables in CI/CD pipelines (GitHub Actions secrets, GitLab CI/CD variables, etc.)
- Store tokens securely using secret management systems (AWS Secrets Manager, HashiCorp Vault, etc.)
- Rotate tokens regularly to limit exposure from potential leaks
- Use minimal permissions - only grant the token access to what's needed (read-only repository access)
- Use organization-level tokens when possible to manage access centrally
Example: CI/CD Integration¶
GitHub Actions:
name: Generate AI Rules
on: [push]
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate
env:
AI_RULEZ_GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
run: npx ai-rulez@latest generate
GitLab CI:
Include Options¶
name: Unique identifier for the includesource: Path or Git URL to the configurationpath: (Optional) Sub-path within the source to resolve (e.g.modules/core). Supports the bare/flattened layout described above; defaults to the source rootref: (Git only) Branch, tag, or commit SHA. Defaults to the remote's default branch (HEAD), not necessarilymain.include: List of content types to fetch:rules,context,skills,agents,commands. MCP servers are not importable from an include.install_to: (Optional) Import the included content into a specific domain instead of the root.local_override: (Optional) A local path used instead ofsource(for example a checkout you are developing). It is resolved against the project directory, andpathis appended to it. If the directory does not exist, the include is skipped silently (an info line is logged); the remote source is not used as a fallback.merge_strategy: How to handle conflicts:local-override: local content takes precedence (default)include-override: the included content takes precedenceerror: fail generation on a conflict
Each include carries its own strategy. Includes are not recursive: an include contributes its own
content, and any includes declared in the included configuration are ignored. Domains from an
include are always carried over; include filters content kinds, not domains.
An include that cannot be created, fetched or merged is logged as a warning and skipped; the other
includes and your own content are still generated. Run ai-rulez validate --verbose to see the
warning.
Machine-local includes and offline runs¶
ai-rulez include add <name> <source> --local(and the MCPadd_includewithlocal: true) writes the include to your gitignoredconfig.local.*overlay instead of the shared config, so a personal checkout can be included without affecting teammates. Overlay includes merge by name with the shared list;remove = truehides a shared include on your machine. See Local Configuration.ai-rulez generate --no-fetchskips network fetches and uses cached content for remote includes. CRUD commands that validate a change (including the--localones) read remote includes from cache only.
Include Priority¶
Resolution starts from your local content and folds each include in declaration order, using that
include's strategy (default local-override, which means the existing base wins):
- Your local configuration has the highest priority.
- The first include's content becomes part of the base and therefore beats later includes of the same name.
- A later include only wins where its
merge_strategyisinclude-override.
[[includes]]
name = "base"
source = "../base-rules/.ai-rulez" # loaded first
[[includes]]
name = "team"
source = "../team-rules/.ai-rulez" # only overrides "base" with merge_strategy = "include-override"
merge_strategy = "include-override"
If base and team both define rules/security.md, base wins unless team sets
merge_strategy = "include-override". Your own rules/ directory always wins.
Common Patterns¶
Organization-Wide Standards¶
Create a central repository with baseline rules:
Repository structure:
org-standards/
└── .ai-rulez/
├── config.toml
├── rules/
│ ├── security.md
│ ├── code-quality.md
│ └── git-workflow.md
└── context/
└── company-values.md
Each project includes it:
[[includes]]
name = "standards"
source = "https://github.com/myorg/standards.git"
path = ".ai-rulez"
Framework-Specific Rules¶
Create separate includes for each framework:
frameworks/
├── go-backend/
│ └── .ai-rulez/
│ ├── config.toml
│ └── rules/
│ ├── project-layout.md
│ ├── error-handling.md
│ └── testing.md
├── react-frontend/
│ └── .ai-rulez/
│ └── rules/
│ ├── component-guidelines.md
│ ├── hooks-patterns.md
│ └── styling.md
└── python-ml/
└── .ai-rulez/
└── rules/
├── numpy-conventions.md
└── ml-best-practices.md
Your project uses them:
presets = ["claude", "cursor"]
[[includes]]
name = "go-backend"
source = "../../frameworks/go-backend/.ai-rulez"
[[includes]]
name = "react-frontend"
source = "../../frameworks/react-frontend/.ai-rulez"
[profiles]
backend = ["backend"]
frontend = ["frontend"]
full = ["backend", "frontend"]
Monorepo with Shared and Team-Specific Rules¶
Repository structure:
monorepo/
├── shared-rules/.ai-rulez/ # Used by all teams
├── backend-team/
│ └── .ai-rulez/ # Includes shared + backend-specific
├── frontend-team/
│ └── .ai-rulez/ # Includes shared + frontend-specific
└── mobile-team/
└── .ai-rulez/ # Includes shared + mobile-specific
backend-team/.ai-rulez/config.toml:
version = "4.0"
name = "backend-api"
includes = [
{ name = "shared-rules", source = "../shared-rules/.ai-rulez" }
]
presets = ["claude", "cursor"]
[profiles]
default = []
Flat Composition¶
Includes do not nest. To combine several layers, list each one in the project's own includes:
org-base/.ai-rulez/ rules/security.md
go-framework/.ai-rulez/ rules/testing.md
team-standards/.ai-rulez/ rules/review.md
my-project/.ai-rulez/ config.toml with one [[includes]] entry per directory above
An includes list inside go-framework's own config.toml is ignored when my-project includes it,
so org-base must be listed in my-project too. Includes fold in declaration order (see
Include Priority).
Collision Handling¶
When includes define the same file:
shared-rules/.ai-rulez/rules/testing.md
go-framework/.ai-rulez/rules/testing.md
my-project/.ai-rulez/rules/testing.md
Under the default local-override strategy the earliest source wins, and your own content beats every
include:
my-project/rules/testing.md(your project content, highest priority)shared-rules/rules/testing.md(first include becomes part of the base)go-framework/rules/testing.md(only wins if it setsmerge_strategy = "include-override")
The losing copy is dropped; generate and validate log a Duplicate rule collapsed warning naming
the kept and dropped paths (see Deduplication by Name). With
merge_strategy = "error", the conflict fails generation instead.
Best Practices¶
Organize by Specificity¶
org-wide-rules/ Applies to everything
team-rules/ Team-specific
framework-rules/ Technology-specific
project-rules/ Project-specific
Use Clear Naming¶
Good:
org-standards/.ai-rulezreact-best-practices/.ai-rulezbackend-security/.ai-rulez
Bad:
rules/.ai-rulez(ambiguous)base/.ai-rulez(unclear scope)
Document Includes¶
Add comments to your config explaining why includes are needed:
version = "4.0"
name = "my-backend"
# Organization-wide coding standards
# Go-specific conventions
# Backend team standards
includes = [
{ name = "org-standards", source = "../org-standards/.ai-rulez" },
{ name = "go-guidelines", source = "../go-guidelines/.ai-rulez" },
{ name = "backend-team", source = "../backend-team/.ai-rulez" }
]
presets = ["claude", "cursor"]
Keep Includes Focused¶
Each include should have a single purpose:
Good:
security-policies/ Rules for security
error-handling/ Rules for error handling
code-style/ Rules for code style
Bad:
everything/ Security, errors, style, testing, etc.
Version Your Includes¶
Tag releases and reference specific versions:
[[includes]]
name = "standards"
source = "https://github.com/org/standards/.ai-rulez"
ref = "v1.0.0"
Troubleshooting¶
Include Path Not Found¶
# Or point at an absolute path
[[includes]]
name = "shared"
source = "/path/to/shared-rules/.ai-rulez"
Include Skipped or Nested Includes Ignored¶
Includes are not recursive, so cycles cannot occur. Give each include a unique name: duplicate names are not rejected and are processed independently.
If an include's content is missing from the output, run ai-rulez validate --verbose: a failed
include is logged as Failed to process include and skipped, and a local_override path that does not
exist skips the include silently.
Conflicting Rules¶
Use project-level rules to override, or change which include wins with merge_strategy:
[[includes]]
name = "stricter"
source = "../stricter-rules/.ai-rulez" # loads first, wins by default
[[includes]]
name = "lenient"
source = "../lenient-rules/.ai-rulez"
merge_strategy = "include-override" # makes this one win instead
Your .ai-rulez/rules/security.md overrides every include.
Content Not Merging¶
cat .ai-rulez/config.toml | grep includes
ls -la ../shared-rules/.ai-rulez/
ai-rulez validate --verbose
Migration Path¶
If you're currently using separate configurations:
- Extract common rules into a shared include:
- Add include to your config:
- Regenerate and test:
- Commit the include:
Next Steps¶
- Configuration Reference: Advanced config options
- Domains & Profiles: Team organization
- Quick Start: Getting started