This document describes the current architecture of the Ballast repository.
Ballast is a monorepo that ships AI rule installers for multiple language profiles and multiple target tools. The repository contains:
- a TypeScript package published to npm as
@everydaydevopsio/ballast - a Python package published as release artifacts
- a Go package published as release artifacts
- a Go-based
ballastwrapper CLI for Homebrew-style installs and upgrades
Ballast installs repo-local AI guidance into the locations expected by Cursor, Claude Code, OpenCode, and Codex.
The source-of-truth rule and skill content lives in this repository:
agents/common/*for shared agentsagents/typescript/*,agents/python/*,agents/go/*for language-specific agentsskills/common/*for shared skills
The TypeScript package is the reference implementation for rule assembly and install behavior. The Python and Go packages ship the same content model for their own CLIs.
ballast/
├── agents/
│ ├── common/ # local-dev, cicd, observability
│ ├── typescript/ # linting, logging, testing
│ ├── python/ # linting, logging, testing
│ └── go/ # linting, logging, testing
├── skills/
│ └── common/
│ └── owasp-security-scan/
├── cli/
│ └── ballast/ # Go wrapper CLI used for install/upgrade/doctor flows
├── packages/
│ ├── ballast-typescript/ # npm package and reference installer implementation
│ ├── ballast-python/ # Python CLI package
│ └── ballast-go/ # Go CLI package
├── docs/ # user-facing docs
└── .github/workflows/ # validation and publish workflows
Within packages/ballast-typescript/:
packages/ballast-typescript/
├── src/
│ ├── cli.ts # install + doctor command surface
│ ├── install.ts # target/agent/skill resolution and writes
│ ├── build.ts # content assembly and destination mapping
│ ├── config.ts # config load/save and project root detection
│ ├── agents.ts # agent/skill registries and lookup
│ ├── patch.ts # merge support for --patch
│ └── *.test.ts
├── agents/ # packaged fallback copy of rule content
├── skills/ # packaged fallback copy of skill content
├── bin/ballast.js # shebang entrypoint
└── dist/ # compiled output
Ballast exposes these public agent IDs:
- Common:
local-dev,cicd,observability - Language-specific:
linting,logging,testing
Each agent directory contains:
content.mdfor the primary rule body- optional
content-<suffix>.mdfiles for additional rule variants templates/files for per-target wrappers
Rule suffixes allow one logical agent to emit multiple installed files. The installer discovers content.md and any content-*.md variants automatically.
Ballast currently ships one common skill:
owasp-security-scan
Each skill directory contains SKILL.md and may include a references/ directory. Claude installs the skill as a bundled .skill archive; the other targets install Markdown-based skill files.
Supported targets are:
cursorclaudeopencodecodex
Rule installation paths:
| Target | Directory | Extension |
|---|---|---|
| Cursor | .cursor/rules/ |
.mdc |
| Claude | .claude/rules/ |
.md |
| OpenCode | .opencode/ |
.md |
| Codex | .codex/rules/ |
.md |
Skill installation paths:
| Target | Directory | Format |
|---|---|---|
| Cursor | .cursor/rules/ |
.mdc |
| Claude | .claude/skills/ |
.skill zip bundle |
| OpenCode | .opencode/skills/ |
.md |
| Codex | .codex/rules/ |
.md |
Support files:
- Claude installs or patches a root
CLAUDE.md - Codex installs or patches a root
AGENTS.md
If BALLAST_RULE_SUBDIR is set, installed rule files are scoped into subdirectories such as .codex/rules/<subdir>/..., and the emitted filenames are prefixed to avoid collisions.
The TypeScript build layer assembles output by combining agent content with target-specific templates.
build.ts performs the following:
- Resolve the source directory for the selected agent and language.
- Load
content.mdor acontent-<suffix>.mdvariant. - Load the target template:
- Cursor:
cursor-frontmatter.yaml - Claude:
claude-header.md - OpenCode:
opencode-frontmatter.yaml - Codex:
codex-header.md, falling back toclaude-header.md
- Cursor:
- Inject linting hook guidance for the selected language and repo shape when the content uses the
{{BALLAST_HOOK_GUIDANCE}}token. - Emit the final target file content.
build.ts also builds skills per target:
- Cursor: frontmatter + skill body from
SKILL.md - Claude: stored zip archive containing
SKILL.mdand anyreferences/* - OpenCode: Markdown body from
SKILL.md - Codex: Markdown body from
SKILL.md
buildCodexAgentsMd() and buildClaudeMd() generate the root support files that enumerate installed rules and skills for those tools.
The reference install flow lives in packages/ballast-typescript/src/install.ts.
- Detect the project root using the nearest directory containing
.rulesrc.json, a legacy.rulesrc.<lang>.jsonfile, orpackage.json. - Load config from
.rulesrc.jsonwhen available. Legacy per-language config filenames are still accepted as inputs. - Resolve target, agents, and skills from flags, config, or interactive prompts.
- In CI or
--yesmode, require explicit install choices when no config is present. - Save config back to
.rulesrc.json, includingtarget,agents, optionalskills,ballastVersion, and merged language/path metadata when relevant. - For each selected agent:
- enumerate all rule suffixes
- calculate destination paths
- skip existing files unless
--forceor--patchis set - write generated content, or merge updates with
patch.tswhen--patchis used
- For each selected skill:
- write the target-specific skill format
- skip existing files unless
--forceis set
- For Claude and Codex:
- create or update
CLAUDE.mdorAGENTS.md - optionally patch existing support files when patch mode is enabled
- create or update
The canonical config file is .rulesrc.json.
Current saved shape:
{
"target": "codex",
"agents": ["local-dev", "linting"],
"skills": ["owasp-security-scan"],
"ballastVersion": "5.2.0",
"languages": ["typescript"],
"paths": {
"typescript": ["."]
}
}Notes:
.rulesrc.ts.json,.rulesrc.python.json, and.rulesrc.go.jsonare legacy compatibility filenames.- The installer reads legacy files, but writes the canonical
.rulesrc.json. findProjectRoot()still treats either canonical or legacy config files as root markers.
The TypeScript CLI currently exposes:
installas the default commanddoctorfor local CLI/config inspection
Supported install flags include:
--target,-t--language,-l--agent,-a--skill,-s--all--all-skills--force,-f--patch,-p--yes,-y--help,-h--version,-v
For TypeScript installs, Ballast distinguishes between standalone and monorepo layouts to tailor linting hook guidance:
- standalone TypeScript repos get
pre-commitguidance - TypeScript workspace monorepos get Husky guidance
- Python and Go profiles get their own
pre-commitguidance
The detection logic uses config metadata first and falls back to workspace manifest/package discovery.
GitHub Actions publish and validate the repository.
Key workflows include:
test.ymllint.yamlcross-language-validate.ymlexamples-smoke.ymllanguage-packs.ymlpublish.yml
publish.yml is the top-level release workflow. It:
- optionally bumps versions and creates a Git tag on manual dispatch
- publishes the TypeScript package to npm
- uploads Python build artifacts to the GitHub release
- publishes Go binaries with GoReleaser
- publishes the wrapper CLI with GoReleaser
- Ballast only installs content shipped in this repository.
- Existing files are preserved by default.
--forceoverwrites generated files.--patchmerges upstream updates into existing generated files where supported.- Common agents stay language-agnostic at the public ID level; language-specific agents are namespaced internally by source directory and emitted filename.