Thank you for your interest in contributing to oh-my-customcode!
Every commit must pass all tests. No exceptions.
This isn't negotiable. Tests are our safety net that ensures oh-my-customcode works as intended for every user.
Our tests are NOT about testing implementation logic. They test:
| Focus | Description | Example |
|---|---|---|
| Philosophy | Does the component behave according to our design principles? | Does the agent identification show up in every response? |
| Workflow | Does the intended user workflow work end-to-end? | Can a user create a custom agent and have it detected? |
| Functionality | Does the feature work as users expect? | Does omcustom init create all required directories? |
Bad test (tests implementation):
it('should call fs.writeFile with correct parameters', () => {
// This tests HOW we do something, not WHAT we achieve
});Good test (tests intent):
it('should create a working agent that Claude can use', async () => {
await createAgent('my-agent');
const agents = await listAgents();
expect(agents).toContainEqual(expect.objectContaining({ name: 'my-agent' }));
});| Metric | Target | Rationale |
|---|---|---|
| Function Coverage | 100% | Every function must be exercised |
| Line Coverage | 95%+ | Defensive error handling may remain uncovered |
What we DON'T test:
- Error handling
catchblocks that only exist for defensive programming - Edge cases that require mocking the file system
- Internal implementation details
Why not 100% line coverage?
Uncovered lines should only be:
catchblocks that handle unexpected filesystem errors- Fallback code paths that protect against edge cases
- Code that would require mocking system APIs to test
These defensive code paths exist for production safety, not for behavior specification. Testing them would mean testing implementation details, which violates our testing philosophy.
- Customization is King - Every feature should be easily customizable
- Batteries Included - Work out of the box, customize when needed
- Non-Destructive - User customizations are never overwritten
- Simple > Complex - If it needs a manual, it's too complicated
The following tools are required (or strongly recommended) before you begin development.
This project uses jq in hook scripts and verification utilities (e.g., .claude/hooks/scripts/git-delegation-guard.sh, scripts/verify-version-sync.sh). Hooks gracefully skip if jq is unavailable, but installing it enables full pre-commit validation.
Install:
| Platform | Command |
|---|---|
| macOS (Homebrew) | brew install jq |
| Linux (apt) | sudo apt-get install -y jq |
| Linux (yum/dnf) | sudo yum install -y jq |
| Static binary | stedolan.github.io/jq/download |
Verify:
jq --version # expect: jq-1.6 or later| Tool | Purpose | Install |
|---|---|---|
| gh (GitHub CLI) | Issue/PR operations | cli.github.com |
| bun | Runtime + test runner | bun.sh |
| yq (optional) | YAML inspection tool. Not required by current verification scripts (verify-wiki-sync.sh uses grep/awk fallback), but useful for manual yaml debugging. | brew install yq |
-
Clone the repository
git clone https://github.com/baekenough/oh-my-customcode.git cd oh-my-customcode -
Install dependencies
bun install
-
Set up git hooks
bun run setup:hooks
We use a simplified Git Flow model optimized for npm package development.
| Branch | Purpose | Protection |
|---|---|---|
develop |
Main development branch (default) | Required reviews, status checks |
feature/* |
New features | None |
release/* |
Release preparation | None |
hotfix/* |
Emergency production fixes | None |
feature/new-feature ──┐
├──► develop ──► release/x.y.z ──► PR merge ──► auto-tag ──► npm publish
feature/another ──────┘ │
└── GitHub Release
hotfix/critical ──► PR merge ──► auto-tag ──► npm publish ──► merge to develop
-
Create feature branch from
develop:git checkout develop git pull origin develop git checkout -b feature/my-feature
-
Make changes and ensure ALL tests pass:
bun test # Must show 0 failures bun run lint # Must pass bun run typecheck # Must pass
-
Commit with conventional message:
git commit -m "feat: add my new feature" -
Push and create PR to
develop:git push -u origin feature/my-feature gh pr create --base develop
-
After merge, delete feature branch:
git checkout develop git pull git branch -d feature/my-feature
When your PR introduces user-visible behavior changes, add a one-line entry under ## [Unreleased] in CHANGELOG.md. Use Keep a Changelog categories:
- Added — new features
- Fixed — bug fixes
- Changed — non-breaking changes
- Removed — removed features
- Deprecated — soon-to-be-removed
- Security — vulnerability fixes
Skip this for internal-only changes (refactors, test-only commits, build tweaks).
The release process automatically promotes ## [Unreleased] to ## [vX.Y.Z] via the omcustom-release-notes skill (Phase 5). See .claude/skills/omcustom-release-notes/SKILL.md.
Important: All npm publishing happens automatically via CI after PR merge. Never push tags or run npm publish manually.
develop ──► release/x.y.z ──► PR merge ──► auto-tag ──► release.yml (npm publish + GitHub Release)
The release pipeline is fully automated:
-
Merging a
release/*PR todeveloptriggers theauto-tagworkflow -
auto-tagreadspackage.json, creates and pushes the version tag -
release.ymltriggers on the new tag and handles npm publish + GitHub Release -
Create release branch from
develop:git checkout develop git pull origin develop git checkout -b release/x.y.z
-
Bump version and promote CHANGELOG [Unreleased] section:
npm version [major|minor|patch] # Recommended: invoke /omcustom-release-notes <version> to auto-promote [Unreleased] # Or manually: replace `## [Unreleased]` header with empty `## [Unreleased]` # plus a new `## [vX.Y.Z] - YYYY-MM-DD` section containing the moved entries. git add . git commit -m "chore: prepare release x.y.z"
-
Push release branch and open PR to
develop:git push -u origin release/x.y.z gh pr create --base develop --title "chore: release x.y.z" -
Wait for CI to pass, then merge the PR.
After merge, the
auto-tagworkflow automatically:- Extracts the version from
package.json - Creates and pushes the
vx.y.ztag - Triggers
release.ymlwhich publishes to npm and creates a GitHub Release
- Extracts the version from
-
Merge release branch back to
develop(if not already via PR):git checkout develop git pull origin develop
-
Delete release branch:
git branch -d release/x.y.z git push origin --delete release/x.y.z
For critical bugs in production:
-
Create hotfix branch from
develop(or latest tag if develop has moved ahead):git checkout develop git checkout -b hotfix/critical-bug # OR from a specific tag: # git checkout vx.y.z && git checkout -b hotfix/critical-bug
-
Fix, test, and bump patch version:
# Make fix bun test npm version patch # Update CHANGELOG.md git add . git commit -m "fix: critical bug description"
-
Push and open PR to
develop:git push -u origin hotfix/critical-bug gh pr create --base develop --title "fix: critical bug (hotfix)" -
Wait for CI to pass, then merge the PR.
Note: Hotfix branches are named
hotfix/*, notrelease/*, soauto-tagwill NOT trigger automatically. For hotfixes, manually push the tag after merging:git checkout develop && git pull git tag vx.y.(z+1) git push origin vx.y.(z+1)
Configure these rules in Settings → Branches → Branch protection rules:
For develop branch:
- ✅ Require pull request before merging
- ✅ Require status checks to pass (CI, tests)
- ✅ Require conversation resolution before merging
- ❌ Allow force pushes (disabled)
We follow Conventional Commits:
feat:- New featuresfix:- Bug fixesdocs:- Documentation changesrefactor:- Code refactoringtest:- Test changeschore:- Build/tooling changes
The following checks run automatically before each commit:
- TypeScript type checking
- Biome linting
- All tests must pass
tests/
├── unit/ # Unit tests - test individual functions
├── integration/ # Integration tests - test module interactions
└── e2e/ # E2E tests - test full CLI workflows
| Test Type | What to Verify |
|---|---|
| Unit | Individual functions work correctly |
| Integration | Modules work together as expected |
| E2E | Complete user workflows succeed |
bun test # Run all tests (MUST pass before commit)
bun test:unit # Run unit tests only
bun test:integration # Run integration tests
bun test:e2e # Run end-to-end tests
bun test --coverage # Check coverage (target: 100%)When adding a feature, ask yourself:
- What workflow does this enable? → Write E2E test
- How do modules interact? → Write integration test
- What edge cases exist? → Write unit tests
-
Create directory structure:
templates/agents/{category}/{agent-name}/ ├── AGENT.md # Agent definition ├── index.yaml # Metadata └── refs/ # Symlinks to skills/guides (optional) -
Update
templates/agents/index.yaml -
Update
templates/skills/orchestration/intent-detection/patterns/agent-triggers.yamlif adding intent triggers -
Add tests to verify the agent is detected and works
-
Create directory structure:
templates/skills/{category}/{skill-name}/ ├── SKILL.md # Skill instructions └── index.yaml # Metadata -
Link from relevant agents using symlinks in their
refs/directory -
Add tests to verify the skill is loaded correctly
- TypeScript with strict mode
- Biome for linting and formatting
- No console.log in library code (CLI output is allowed)
Open an issue or discussion on GitHub.
Remember: If tests don't pass, the PR doesn't merge. This protects everyone.