Standardizing AI Tooling in Repositories

Table of Contents

Standardizing AI Tooling in Repositories

AI coding agents work better when they understand your project. Instead of relying on each contributor to configure their own tool, you can commit a small set of files that any AI agent picks up automatically. After working on this standardization for the ComplyTime organization, I want to share the practical outcomes: which files matter, why each one exists, how agents consume them, and how to maintain them.

The Problem

Without standardization, every contributor configures their AI agent differently. Standards drift, commands are duplicated, and onboarding takes longer than it should. The agent has no context about your project’s architecture, coding standards, or review process. It generates plausible but misaligned code.

The goal is simple: a contributor clones the repository, opens it in their AI agent, and everything works. No manual configuration beyond installing the tool.

Spec-Driven Frameworks: SpecKit and OpenSpec

Before diving into the files, it helps to understand the two spec-driven development frameworks that shaped this standardization.

SpecKit was the first framework adopted in the organization. It provides a structured workflow for feature development: specify requirements, plan implementation, generate tasks, and implement step by step. SpecKit uses the .specify/ directory convention and hardcodes the constitution path at .specify/memory/constitution.md. Its commands are prefixed with speckit.* and installed via plugin.

OpenSpec is the newer alternative, designed as the native specification framework for OpenCode . It follows a similar spec-driven workflow but uses its own directory structure (openspec/) and command conventions (prefixed opsx-*). OpenSpec is the recommended framework for the organization going forward.

Both frameworks are agent-agnostic: they work with OpenCode, Claude Code, or any AI tool that supports command loading. Neither is tied to a specific agent. The key design decision is that both share the same constitution and can coexist in the same repository. SpecKit outputs specifications to specs/, OpenSpec outputs to openspec/, and sequential numbering is coordinated across both directories so features form a unified chronological timeline regardless of which framework created them.

The constitution path at .specify/memory/constitution.md is a SpecKit requirement (it hardcodes this path in its commands). OpenSpec discovers the constitution at the same path by convention. This is not framework infrastructure; it is project-specific content that both frameworks consume. Moving it would break SpecKit without benefiting OpenSpec, so the path stays.

How Agents Discover These Files

AI agents do not magically know about your project. They rely on well-known file paths and auto-discovery conventions to load context, commands, and skills. Understanding these mechanisms is essential to making the file set work.

AGENTS.md is read automatically by OpenCode, Claude Code (via CLAUDE.md), and other agents that follow the convention of loading a root-level context file on startup. When the agent enters a repository, it reads AGENTS.md first to understand the project structure, available commands, constraints, and conventions. This file is the agent’s “first impression” of your project. Only the tool-neutral AGENTS.md should be committed. Tool-specific variants like CLAUDE.md are local-only and should be gitignored.

.opencode/command/ is OpenCode’s auto-discovery path for commands. Any .md file placed here with YAML frontmatter becomes an invocable command (e.g., /review_pr). OpenCode scans this directory on startup, no configuration needed. Framework commands (installed by SpecKit or OpenSpec plugins) also land here but are excluded from version control via .gitignore patterns, so only project-specific commands are committed.

.agents/skills/ is the agent-agnostic auto-discovery path for skills. OpenCode scans .agents/skills/, .opencode/skills/, and .claude/skills/ for skill definitions. By committing skills to .agents/skills/, they are discoverable by any compatible agent without coupling to a specific tool’s directory.

.specify/memory/constitution.md is not auto-discovered by agents directly, but it is referenced by AGENTS.md, by commands (like the PR review command), and by both spec frameworks. The agent reaches the constitution through these references. When a command says “Read .specify/memory/constitution.md for coding standards”, the agent loads it on demand rather than carrying it in context permanently.

The key insight is that each file has a specific discovery mechanism: root-level context files are read on startup, commands and skills are auto-discovered from known directories, and the constitution is loaded on demand through references. No manual configuration is needed. The agent follows the conventions, and the files are there.

The File Set

Here is the minimal set of committed files that achieves this. Each one has a specific role and a clear consumer.

.specify/memory/constitution.md    # Coding standards and governance
docs/AI_TOOLING.md                 # Human-readable setup guide
.agents/skills/                    # Domain-specific knowledge for the agent
.opencode/command/review_pr.md     # Project-specific AI commands
AGENTS.md                          # Machine-readable agent context
.gitignore                         # Boundary between committed and tool-managed files

1. Constitution (.specify/memory/constitution.md)

What it is: A single document defining your project’s coding standards, architectural principles, contribution workflow, and governance rules. Written in Markdown with RFC 2119 language (MUST, SHOULD, MAY).

Why it exists: AI agents need authoritative rules to follow. Without a constitution, the agent guesses at style, naming, error handling, and testing conventions. With one, it generates code that conforms to your project’s actual standards, not generic best practices.

Why this path: The .specify/memory/constitution.md path is a SpecKit requirement. SpecKit was adopted first in the organization and hardcodes this path in its commands. When OpenSpec was added later, it was configured to discover the constitution at the same location by convention. The path is a project decision, not a framework dependency: the file is committed project content consumed by both frameworks. If your project only uses OpenSpec, you could place the constitution elsewhere, but this path works for both.

How it is consumed: Both SpecKit and OpenSpec reference the constitution when generating specifications, plans, and code. PR review commands check code against constitution principles. The agent reaches it through references in AGENTS.md and commands, loading it on demand.

How to maintain it: Treat it like a versioned document. Use semantic versioning (MAJOR for principle changes, MINOR for new sections, PATCH for wording fixes). Require at least two reviewers for amendments. Individual repositories can extend the organization-wide constitution but must not relax its MUST directives.

Example structure:

# Project Constitution

## Core Principles
### I. Single Source of Truth
Values used in multiple places MUST be centralized...

### II. Simplicity & Isolation
Functions MUST follow the Single Responsibility Principle...

## Coding Standards
### Python
- All code MUST use type hints
- Code MUST pass ruff linting
- Line length: 99 characters

### YAML / GitHub Actions
- Reusable workflows MUST use `reusable_` prefix
- All action references MUST be pinned to full commit SHAs

## Commit Messages
All commits MUST follow Conventional Commits...

2. AI Tooling Documentation (docs/AI_TOOLING.md)

What it is: A human-readable guide covering how to set up AI tools in the repository, available commands, how to create new commands and skills, and key file locations.

Why it exists: Not all information should be embedded in machine-readable files. Contributors need a single entry point to understand the AI tooling setup, especially those using agents other than the default. This file targets under 3 minutes to read.

Why docs/ and not a dedicated directory: If you already have a docs/ directory for project documentation, use it. Creating a dedicated ai/ directory for a single Markdown file adds unnecessary structure. Follow the convention your project already has.

How it is consumed: By humans. Contributors read it when onboarding or when creating new commands and skills. It links to the constitution and other key files.

How to maintain it: Update it when commands, skills, or directory conventions change. Keep it short. If it grows beyond one page, you are probably duplicating content that belongs in the constitution or in individual command/skill files.

3. Skills Directory (.agents/skills/)

What it is: A directory where domain-specific knowledge files (skills) live. Each skill is a subdirectory containing a SKILL.md with YAML frontmatter.

Why it exists: Skills teach the agent how to think about a specific domain (e.g., “OSCAL compliance requirements”, “Go error handling patterns”). Unlike commands (which define a workflow), skills provide persistent context loaded into the agent’s session.

Why .agents/skills/ and not .opencode/skills/: The .agents/ path is agent-agnostic. OpenCode, and potentially other compatible tools, discover skills from this path. Using a tool-specific directory (like .opencode/skills/) couples your project to one agent.

How it is consumed: The AI agent auto-discovers skills from this directory. When a skill is activated, the agent loads its content into context for the duration of the session. Skills consume tokens for the entire session, so they should be concise (under 200 lines).

How to maintain it: Start with an empty directory (.gitkeep). Contributors add skills via PR as workflows mature. Each skill has a clear name and description in its frontmatter. Review skills for token efficiency, duplicated content, and alignment with the constitution.

Skill file structure:

---
name: security-review
description: "Domain knowledge for security-focused code review"
---
# Security Review Patterns

## Input Validation
All external inputs MUST be validated before use in...

4. Project-Specific Commands (.opencode/command/)

What it is: Markdown files that define reusable prompt templates the agent executes when invoked. Each file is a command with YAML frontmatter and step-by-step instructions.

Why it exists: Commands encode project-specific workflows that would otherwise be ad-hoc prompts. A PR review command, for example, ensures every review follows the same process: check CI, run local tools, then apply AI judgment. Without commands, each contributor writes their own prompt, producing inconsistent results.

Why selective gitignore matters: This directory also contains framework-managed commands (like speckit.* or opsx-*) that are installed by plugins and should not be committed. Use .gitignore patterns to exclude framework commands while keeping project-specific ones:

.opencode/command/speckit.*
.opencode/command/opsx-*

How it is consumed: The AI agent auto-discovers commands from .opencode/command/. When a user invokes /review_pr 42, the agent loads review_pr.md and executes its steps.

How to maintain it: Keep each command focused on a single workflow. When writing commands, delegate deterministic checks (lint, tests) to tools first, then use AI for judgment-based analysis. Reference the constitution instead of inlining rules. This prevents drift and saves tokens.

5. Agent Context File (AGENTS.md)

What it is: A machine-readable Markdown file at the repository root that gives the AI agent immediate context about the project: directory structure, available commands, constraints, and conventions.

Why it exists: When the agent opens a repository, it reads AGENTS.md to understand the project layout without scanning the entire codebase. This reduces token usage and prevents hallucinated assumptions about project structure.

How it is consumed: AI agents (OpenCode, Claude Code, and others) read this file automatically when entering a repository. It provides the “first impression” of the project.

How to maintain it: Auto-derive it from the constitution and feature plans when possible. Tools like update-agent-context.sh can regenerate it during planning workflows. Manually add project-specific sections (like directory structure explanations) between designated markers. Tool-specific agent context files (e.g., CLAUDE.md) should be gitignored, and only the tool-neutral AGENTS.md should be committed.

6. Gitignore (.gitignore)

What it is: The boundary enforcement between committed project-specific content and tool-managed files.

Why it exists: AI tools and spec frameworks install scripts, templates, commands, and plugins locally. These should never be committed. Without explicit gitignore patterns, contributors accidentally commit tool-managed files, creating maintenance burden and version conflicts.

Key patterns to include:

# Framework infrastructure (tool-managed)
.specify/scripts
.specify/templates

# Framework commands (plugin-managed, not project-specific)
.opencode/command/speckit.*
.opencode/command/opsx-*

# Plugin artifacts
.opencode/node_modules/
.opencode/package.json

# Tool-specific directories (local only)
.claude/
.cursor/

# Tool-specific agent context (local only)
CLAUDE.md

The Boundary Principle

The most important design decision is the boundary between what you commit and what you gitignore:

Committed (project-specific) Gitignored (tool-managed)
Constitution Framework scripts and templates
Project-specific commands Framework commands
Skills directory Plugin artifacts (node_modules, etc.)
Agent context (AGENTS.md) Tool-specific context (CLAUDE.md)
AI tooling documentation Tool-specific directories (.claude/, .cursor/)

This boundary ensures that:

  • Contributors using different AI tools share the same standards
  • Framework updates do not require repository changes
  • Project-specific customizations are version-controlled and reviewable

Organization-Wide Distribution

If you manage multiple repositories, you can distribute these files from a central infrastructure repository using a sync mechanism. The files that make sense to sync across all repos are:

  • Constitution (governance standards)
  • AI tooling documentation (setup guide)
  • Skills directory structure (empty .gitkeep)
  • Project-specific commands (like PR review)

Each target repository inherits the organization’s AI tooling setup automatically. Individual repos can add their own commands and skills without conflicting with the synced baseline.

Getting Started

To implement this in your repository:

  1. Create the constitution: Start with your existing coding standards. Formalize them with MUST/SHOULD/MAY language. Place it at a well-known path.

  2. Add the documentation: Create docs/AI_TOOLING.md (or equivalent) explaining the setup and key files.

  3. Set up the skills directory: Create .agents/skills/.gitkeep. Add skills later as workflows mature.

  4. Write your first command: Start with a PR review command or a code generation command specific to your project.

  5. Create AGENTS.md: Document the project structure, available commands, and constraints.

  6. Configure .gitignore: Add patterns for framework infrastructure, plugin artifacts, and tool-specific directories.

  7. Test the flow: Clone the repository fresh, open it in your AI agent, and verify that commands are discovered, the constitution is accessible, and the agent context is loaded.

Conclusion

The value of standardizing AI tooling is not in the individual files. It is in the contract they create: a shared understanding between your project and any AI agent that enters it. The constitution defines the rules. Commands encode the workflows. Skills provide the domain knowledge. The agent context gives the first impression. And the gitignore enforces the boundary between what your project owns and what each tool manages.

The setup is small (6 files), the maintenance is low (update when standards change), and the payoff is immediate: every contributor, regardless of which AI tool they use, gets consistent, project-aligned assistance from the first clone.