Skip to Content

Plugs guide

Experimental feature. Plugs are functional but under active development. APIs may change. Most solo developers can skip this feature entirely.

Solo developer? You can skip this page unless you’re using rule Aligns that require project-specific values. Most solo workflows don’t need plugs.

Fills are set only in .aligntrue/config.yaml (IR fills are ignored). Supported formats: command, text. file and url are deprecated and treated as text. Sync fails if required plugs are missing.

Plugs provide template-based customization for rules that need different values across projects, stacks, or teams. Instead of forking rules to change test commands or file paths, you declare slots and fill them with project-specific values.

You need plugs when:

  • You use Aligns that contain [[plug:...]] template slots
  • You want to customize test commands or file paths across projects
  • You need organization-wide metadata (company name, URLs) in rules

Real-world scenarios: See 3 complete examples below for test command customization, organization metadata, and stack-specific paths.

On this page: Quick Example · Declaring Slots · Providing Fills · Scenarios · Performance

Quick example

Rule with plug:

rules: - id: testing.run-tests summary: Run tests before committing guidance: | Run tests with: [[plug:test.cmd]] applies_to: ["**/*"] plugs: slots: test.cmd: description: "Command to run the project's tests" format: command required: true example: "pytest -q"

Fill the slot:

aligntrue plugs set test.cmd "pnpm test"

Result after sync:

Run tests with: pnpm test

When to use plugs

Good use cases

  • Test commands - pytest, pnpm test, cargo test, etc.
  • File paths - Config files, documentation URLs, build outputs
  • Project metadata - Author names, organization names, repository URLs
  • Stack-specific values - Package managers, build tools, deployment targets

Not suitable for

  • Check logic - Use overlays to adjust severity/inputs, or fork for logic changes
  • Multi-line content - Plugs are single-line only
  • Secret values - Don’t put secrets in fills (use environment variables at runtime)
  • Computed values - Plugs are static strings, not expressions

Declaring slots

Slots define the template variables in your rules.

Slot structure

plugs: slots: <key>: description: "One-line description" format: command | text | file | url required: true | false example: "example value" # Recommended for required slots

Format types

command

  • Single-line shell command
  • No environment variable interpolation (except CI=true)
  • Examples: pnpm test, cargo build --release

text

  • Any single-line UTF-8 string
  • Examples: John Doe, Acme Corp, v1.0.0

file

  • Repo-relative POSIX path
  • No .. segments (parent directory traversal blocked)
  • No absolute paths
  • Examples: config/settings.json, docs/README.md

url

  • Must start with http:// or https://
  • Examples: https://docs.example.com, https://github.com/org/repo

For concrete examples of these formats in action, see Scenarios.

Key naming rules

  • Lowercase letters, numbers, dots, hyphens, underscores: ^[a-z0-9._-]+$
  • Cannot start with stack. or sys. (reserved)
  • Use dots for namespacing: test.cmd, docs.url, author.name

Example declarations

plugs: slots: # Required command test.cmd: description: "Command to run tests" format: command required: true example: "pytest -q" # Optional URL docs.url: description: "Documentation website URL" format: url required: false example: "https://example.com/docs" # Required file path config.file: description: "Path to configuration file" format: file required: true example: "config/settings.json" # Optional text author.name: description: "Primary author name" format: text required: false

Providing fills

Fills provide the actual values for slots.

Fill sources (merge order)

Fills merge in this order (first source wins):

  1. Base align - Default fills from upstream
  2. Stack align(s) - Stack-specific defaults (e.g., nextjs-align provides test.cmd: "pnpm test")
  3. Repo-local - Your project’s fills (highest priority)

Setting fills

There are two ways to provide fills: via CLI commands or via configuration file.

aligntrue plugs set <key> <value>

Examples:

# Set test command aligntrue plugs set test.cmd "pnpm test" # Set author name aligntrue plugs set author.name "Jane Smith" # Set documentation URL aligntrue plugs set docs.url "https://docs.example.com" # Set config file path aligntrue plugs set config.file "config/production.json"

The CLI command validates the fill format and updates your .aligntrue/config.yaml file.

Via config file

You can also set fills directly in .aligntrue/config.yaml:

version: "1" mode: solo plugs: fills: test.cmd: "pnpm test" author.name: "Jane Smith" docs.url: "https://docs.example.com" config.file: "config/production.json" exporters: - agents-md - cursor

Config fills take precedence over IR fills, allowing you to override align defaults without modifying the align itself.

Removing fills

# Remove a fill aligntrue plugs unset test.cmd # Or edit config.yaml and remove the fill entry

Fill validation

AlignTrue validates fills against slot formats:

# Valid aligntrue plugs set test.cmd "pnpm test" # ✓ Single-line command # Invalid aligntrue plugs set test.cmd "pnpm test && echo done" # ✗ Complex command with && aligntrue plugs set config.file "../secrets.json" # ✗ Parent directory traversal aligntrue plugs set docs.url "example.com" # ✗ Missing http:// or https://

Resolution algorithm

Plugs resolve during aligntrue sync before exporting to agents.

Resolution steps

  1. Normalize line endings - Convert CRLF/CR to LF
  2. Protect escapes - Temporarily replace [[\plug: with sentinel
  3. Resolve each plug:
    • If fill exists → replace [[plug:key]] with fill value
    • If required and no fill → insert TODO block
    • If optional and no fill → replace with empty string
  4. Unescape literals - Restore [[\plug: to [[plug:
  5. Normalize output - Ensure single trailing LF

Resolution examples

Filled plug:

Input: Run tests with: [[plug:test.cmd]] Fill: test.cmd: "pnpm test" Output: Run tests with: pnpm test

Unresolved required plug (with example):

Input: Run tests with: [[plug:test.cmd]] Slot: required: true, example: "pytest -q" Fill: (none) Output: Run tests with: TODO(plug:test.cmd): Provide a value for this plug. Examples: pytest -q

Unresolved optional plug:

Input: Documentation: [[plug:docs.url]] Slot: required: false Fill: (none) Output: Documentation:

Escaped literal:

Input: Use [[\plug:key]] syntax for plugs Output: Use [[plug:key]] syntax for plugs

CLI commands

list - List slots and fills

aligntrue plugs list

Shows:

  • All declared slots with descriptions and formats
  • Current fill values
  • Resolution status (filled, required, optional)
  • Orphan fills (fills without slots)
  • Summary statistics

Example output:

📌 Plugs List ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Slots declared: test.cmd Description: Command to run the project's tests Format: command Required: true Example: pytest -q Status: ✓ filled Fill: pnpm test docs.url Description: Documentation website URL Format: url Required: false Status: ○ optional ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Summary: Total slots: 2 Required slots: 1 Filled required: 1

resolve - Preview resolution

aligntrue plugs resolve [--dry-run]

Previews plug resolution without writing changes. Shows resolved text and lists unresolved required plugs.

Example output:

✓ Resolved 2 plugs Resolved text preview: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Run tests with: pnpm test Documentation: https://docs.example.com ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Unresolved required plugs: 0

validate - Check for errors

aligntrue plugs validate

Validates plugs without writing changes. Fails fast on undeclared slots, missing required fills, or format errors; use it before aligntrue sync or in CI.

Example output:

✓ All plugs valid

Example failure:

✗ Plugs validation failed - Missing required plug: test.cmd - Undeclared plug reference: missing.key

set - Set fill value

aligntrue plugs set <key> <value>

Sets a config-based fill with format validation.

Example:

aligntrue plugs set test.cmd "pnpm test"

Output:

✓ Set plug fill: test.cmd = "pnpm test" Run 'aligntrue sync' to apply the fill

The command validates the fill against the slot’s format (command, file, url, text) and updates .aligntrue/config.yaml.

Note: plugs set is interactive-only and does not support --yes. Run it in a TTY instead of non-interactive scripts.

unset - Remove fill value

aligntrue plugs unset <key>

Removes a config-based fill.

Example:

aligntrue plugs unset test.cmd

Output:

✓ Removed plug fill: test.cmd Run 'aligntrue sync' to apply changes

Note: plugs unset is also interactive-only (no --yes).

See CLI Reference for complete command documentation.

Scenario-based examples

Scenario 1: Solo developer with custom test command

Goal: Use shared TypeScript rules but customize test command for your project.

Setup:

# Pull upstream TypeScript align sources: - git: https://github.com/org/typescript-standards ref: v1.0.0

Upstream align includes:

rules: - id: testing.run-before-commit guidance: | Run tests before committing: [[plug:test.cmd]] plugs: slots: test.cmd: description: "Command to run tests" format: command required: true example: "pytest -q"

Your customization:

# Set your project's test command aligntrue plugs set test.cmd "pnpm test" # Sync to agents aligntrue sync

Result: Rules use pnpm test instead of upstream’s pytest -q example.

Scenario 2: Team sharing stack-specific configs

Goal: Team uses shared base rules with stack-specific test commands.

Base align (shared):

# base-rules.yaml rules: - id: testing.run-tests guidance: | Run tests: [[plug:test.cmd]] plugs: slots: test.cmd: description: "Command to run tests" format: command required: true

Stack align (Next.js team):

# nextjs-align.yaml plugs: fills: test.cmd: "pnpm test" # Default for Next.js projects

Stack align (Python team):

# python-align.yaml plugs: fills: test.cmd: "pytest -q" # Default for Python projects

Project overrides (if needed):

# Override stack default for specific project aligntrue plugs set test.cmd "pnpm test:ci"

Merge order:

  1. Base align: declares slot
  2. Stack align: provides stack default
  3. Repo-local: overrides if needed

Scenario 3: Multi-stack monorepo

Goal: Different test commands per scope in monorepo.

Config:

scopes: - path: "apps/web" rulesets: ["base-rules", "nextjs-rules"] - path: "services/api" rulesets: ["base-rules", "python-rules"] plugs: fills: # Global fills (used if scope doesn't override) test.cmd: "pnpm test"

Scope-specific fills:

# apps/web/.aligntrue/rules plugs: fills: test.cmd: "pnpm test:web" # services/api/.aligntrue/rules plugs: fills: test.cmd: "pytest -q"

Result: Each scope uses its own test command.

Scenario 4: Documentation URLs per project

Goal: Shared rules reference project-specific documentation.

Shared rule:

rules: - id: docs.link-in-readme summary: Link to documentation in README guidance: | Add documentation link to README: [[plug:docs.url]] applies_to: ["README.md"] plugs: slots: docs.url: description: "Project documentation URL" format: url required: false # Optional - not all projects have docs example: "https://docs.example.com"

Project A:

aligntrue plugs set docs.url "https://docs.projecta.com"

Project B (no docs yet):

# Don't set fill - optional plug resolves to empty string

Result:

  • Project A: Guidance includes docs URL
  • Project B: Guidance omits docs URL (empty string)

Troubleshooting

Unresolved required plugs

Problem: Sync fails with “Unresolved required plugs”

Solution:

# 1. Validate to see missing or undeclared plugs aligntrue plugs validate # 2. Set missing fills aligntrue plugs set <key> <value> # 3. Sync again aligntrue sync

Format validation errors

Problem: Format validation failed: file

Solution: Check format requirements:

# Bad: Parent directory traversal aligntrue plugs set config.file "../secrets.json" # ✗ # Good: Repo-relative path aligntrue plugs set config.file "config/settings.json" # ✓ # Bad: Absolute path aligntrue plugs set config.file "/etc/config.json" # ✗ # Good: Relative to repo root aligntrue plugs set config.file "config/production.json" # ✓

Orphan fills

Problem: plugs list or plugs validate shows “Fills without declared slots”

Cause: Fill exists but no slot declared (typo or removed slot)

Solution:

# Option 1: Fix typo in fill key aligntrue plugs set correct.key "value" # Option 2: Remove orphan fill # Edit .aligntrue/rules and remove the fill

Multi-line values

Problem: Need multi-line guidance with plug

Solution: Plugs are single-line only. Use multiple plugs or overlays:

# Bad: Multi-line plug (not supported) guidance: | [[plug:multi.line]] # Good: Multiple single-line plugs guidance: | Step 1: [[plug:step.one]] Step 2: [[plug:step.two]] # Alternative: Use overlay to replace entire guidance overlays: overrides: - selector: "rule[id=my-rule]" set: guidance: | Custom multi-line guidance here

Escaping plug syntax

Problem: Need to show [[plug:key]] literally in guidance

Solution: Use double backslash escape:

guidance: | Use [[\plug:key]] syntax for plugs

Result after resolution:

Use [[plug:key]] syntax for plugs

Determinism and hashing

Lock hash (pre-resolution)

Computed over canonicalized YAML before plug resolution:

# This YAML is hashed (with plugs unresolved) rules: - id: test guidance: "Run: [[plug:test.cmd]]" plugs: slots: test.cmd: { ... } fills: test.cmd: "pnpm test"

Purpose: Detect changes to rule structure and plug declarations.

Export hash (post-resolution)

Computed over resolved text after plug resolution:

# This text is hashed (with plugs resolved) Run: pnpm test

Purpose: Detect changes to exported agent files.

Volatile fields

Fills are not volatile - they affect export hash. If you change a fill, the export hash changes.

Best practices

For rule authors

  1. Use recommended conventions - See Conventions Reference for standard plug keys
  2. Provide examples - Always include examples for required plugs
  3. Clear descriptions - One-line descriptions that explain the purpose
  4. Appropriate formats - Use the most restrictive format (command > file > text)
  5. Optional by default - Only mark as required if truly necessary
  6. Namespace keys - Use dots for organization: test.cmd, docs.url, build.output

For solo developers

  1. Set fills early - Run aligntrue plugs list after init
  2. Use stack Aligns - Let stack Aligns provide sensible defaults
  3. Override selectively - Only set fills when defaults don’t work
  4. Document decisions - Add comments in .aligntrue/rules explaining custom fills

For teams

  1. Share stack Aligns - Create org-specific stack Aligns with common fills
  2. Document required plugs - List required plugs in team onboarding docs
  3. Validate in CI - Run aligntrue plugs list in CI to catch missing fills
  4. Review fill changes - Treat fill changes like code changes (PR review)
  5. Avoid secrets - Never put secrets in fills (use env vars at runtime)

Scenarios

Real-world examples showing how to use plugs for project-specific customization:

Test command customization

Problem: You’re using a shared rule align that references [[plug:test.cmd]] but your project uses a specific test runner. Some projects use pytest -q, others use pnpm test, and some use cargo test. You need to customize the test command without forking the align.

Solution: Use plugs to fill the test command slot with your project-specific value.

Configuration:

version: "1" mode: solo sources: - type: local path: "shared-align.yaml" plugs: fills: test.cmd: "pnpm test" build.cmd: "pnpm build" lint.cmd: "pnpm lint" exporters: - agents-md - cursor

Expected outcome: The align references [[plug:test.cmd]] in rules, your project fills it with pnpm test, and exported rules contain the resolved command. No align forking required.

Keywords: test command, different test runners, pytest vs jest, custom test command, project-specific testing, test automation

Organization metadata

Problem: Your team uses shared rule Aligns that reference organization-specific metadata like company name ([[plug:org.name]]), documentation URL ([[plug:docs.url]]), support email ([[plug:support.email]]), and default author ([[plug:author.name]]). Each project needs to fill these with your organization’s values.

Solution: Use plugs to provide organization metadata that gets resolved across all rules.

Configuration:

version: "1" mode: team sources: - type: local path: "team-align.yaml" plugs: fills: org.name: "Acme Corp" docs.url: "https://docs.acme.com" support.email: "support@acme.com" author.name: "Acme Engineering Team" repo.url: "https://github.com/acme/project" exporters: - agents-md - cursor

Expected outcome: Rules reference organization metadata via plugs, all projects use consistent values, easy to update across all projects, and no hardcoded company-specific values in Aligns.

Keywords: company name, organization URLs, author names, team metadata, company-specific values, organization branding

Stack-Specific Paths

Problem: Your shared rule align references configuration file paths like config file ([[plug:config.file]]), environment file ([[plug:env.file]]), and build output ([[plug:build.output]]). Different stacks use different locations: Next.js uses next.config.js, .env.local, .next/; Node.js uses config/default.json, .env, dist/; Python uses config.yaml, .env, build/.

Solution: Use plugs to specify stack-specific paths that match your project structure.

Configuration:

version: "1" mode: solo sources: - type: local path: "base-align.yaml" - type: local path: "nextjs-align.yaml" plugs: fills: config.file: "next.config.js" env.file: ".env.local" build.output: ".next" tsconfig.file: "tsconfig.json" package.manager: "pnpm" exporters: - agents-md - cursor

Expected outcome: Rules reference file paths via plugs, each stack uses appropriate locations, no path assumptions in shared Aligns, and easy to adapt to different project structures.

Keywords: config file paths, stack-specific locations, different file locations, path customization, project structure, framework conventions

Performance characteristics

Plug resolution:

  • O(n) where n = number of plugs (typically < 20)
  • Resolved once during bundle merge, cached for exports
  • No runtime overhead after resolution
  • Validation happens at config load time

Memory usage:

  • Minimal overhead: ~100 bytes per plug slot/fill
  • Resolved values cached in memory during sync
  • No impact on bundle or lockfile size

Best practices:

  • Keep plug count under 50 for optimal performance
  • Use descriptive keys for maintainability
  • Validate format constraints early (command, file, url, text)

Common issues

”Plug references without slot definitions” error

What it means: The CLI found [[plug:...]] syntax in your rules, but the corresponding slot is not declared.

Why this happens:

  • Slots must be declared in rule frontmatter YAML, not inline
  • Typo in slot key (slot declared as test.cmd, referenced as test_cmd)
  • Slot was removed but references remain
  • Rule hasn’t been properly loaded into the IR

Fix:

  1. Check slot declaration - Verify your rule frontmatter includes the plugs section:
--- title: Your Rule plugs: slots: test.cmd: description: "Command to run tests" format: command required: true example: "pytest -q" ---
  1. Verify key spelling - Ensure referenced keys match declared keys exactly:
# Declared as: plugs: slots: test.cmd: ... # Reference must be: Run tests: [[plug:test.cmd]] # ✓ Correct # NOT: Run tests: [[plug:test_cmd]] # ✗ Wrong - underscore instead of dot
  1. Run sync - After fixing, sync to resolve the plugs:
aligntrue sync

Plugs don’t resolve during aligntrue plugs list

What to expect: The aligntrue plugs list command shows status but does NOT resolve plugs yet. That happens during aligntrue sync.

Why: There are three separate operations:

  1. aligntrue plugs list - Shows declared slots, fills, and status
  2. aligntrue plugs resolve - Previews what resolution will look like
  3. aligntrue sync - Actually applies fills to rules and exports them

Workflow:

# 1. List to see what needs filling aligntrue plugs list # 2. Set missing fills aligntrue plugs set test.cmd "pnpm test" # 3. Preview resolution aligntrue plugs resolve # 4. Apply to agents aligntrue sync

Fills aren’t persisting after aligntrue plugs set

Expected behavior: Fills persist to .aligntrue/config.yaml automatically.

If fills are missing:

  1. Check that set command succeeded:
aligntrue plugs set test.cmd "pnpm test" # Should output: ✓ Set plug fill: test.cmd = "pnpm test"
  1. Verify config file exists and is writable:
ls -la .aligntrue/config.yaml
  1. Inspect config to see stored fills:
grep -A 5 "plugs:" .aligntrue/config.yaml
  1. Run sync to apply:
aligntrue sync

Slots defined in frontmatter but not recognized

Why this happens: Slots are defined in rule YAML frontmatter, but the rule file needs to be loaded into the IR for them to be available.

Workflow:

  1. Define slots in rule file:
# .aligntrue/rules/testing.md --- title: Testing Standards plugs: slots: test.cmd: description: "Command to run tests" format: command required: true --- ## Run tests Run tests with: [[plug:test.cmd]]
  1. Run sync to load rules into IR:
aligntrue sync
  1. Now slots are available:
aligntrue plugs list # Should show test.cmd slot
Last updated on