Thank you for your interest in contributing to Venfork! This guide will help you get started.
- Node.js 18+ or Bun (recommended for faster development)
- GitHub CLI (
gh) installed and authenticated - Git configured with SSH keys
-
Fork and clone the repository
git clone https://github.com/YOUR_USERNAME/venfork.git cd venfork -
Install dependencies
bun install # or npm install -
Link for local testing
bun link # or npm link -
Run in development mode
bun run dev setup --help # or npm run dev setup --help
# Run all tests
bun test
# or
npm test
# Run tests in watch mode
bun run test:watch
# or
npm run test:watchImportant: All tests must pass before submitting a PR.
We use Biome for formatting and linting.
# Format code
bun run format
# or
npm run format
# Check and fix all issues (format + lint)
bun run check
# or
npm run check
# Check formatting only (used in CI)
bun run format:check
# or
npm run format:checkBefore committing: Always run bun run check to ensure code quality.
bun run build
# or
npm run buildThe built files will be in the dist/ directory.
venfork/
├── src/
│ ├── index.ts # CLI entry point
│ ├── commands.ts # Command implementations
│ ├── git.ts # Git/GitHub utilities
│ ├── utils.ts # Pure utility functions
│ └── errors.ts # Custom error types
├── tests/
│ ├── git.test.ts
│ ├── utils.test.ts
│ └── errors.test.ts
└── dist/ # Built output (gitignored)
git checkout -b feature/your-feature-name
# or
git checkout -b fix/bug-description- Follow TypeScript strict mode conventions
- Add JSDoc comments for public functions
- Keep functions small and focused
- Use descriptive variable names
- Write tests for new features in the appropriate test file
- Use
test(notit) for test blocks (project convention) - Ensure all tests pass:
bun test
Example:
test('descriptive test name', async () => {
const result = await yourFunction();
expect(result).toBe(expected);
});We use release-please to drive
versioning and the changelog. It reads commit subjects on main and opens a
release PR that bumps package.json, regenerates CHANGELOG.md, and tags the
release. You don't add a changeset file — the version bump is derived from
your commit prefix:
fix:→ patch bumpfeat:→ minor bumpfeat!:/BREAKING CHANGE:in the body → major bumpdocs:,test:,refactor:,chore:→ no version bump
Use a scope when it adds clarity (e.g. feat(workflow): …, fix(setup): …).
The commit subject becomes the CHANGELOG entry, so write it for end users.
# Run all checks
bun run check && bun test && bun run build
# or with npm
npm run check && npm test && npm run buildAll must pass before submitting a PR.
git add .
git commit -m "feat: add new feature"
# or
git commit -m "fix: resolve issue with X"The commit prefix drives the release flow (see step 4).
git push origin feature/your-feature-nameThen create a PR on GitHub with:
- Clear title describing the change
- Description of what changed and why
- Link to any related issues
- Screenshots/examples if applicable
- ✅ All tests pass (
bun test) - ✅ Code is formatted and linted (
bun run check) - ✅ Build succeeds (
bun run build) - ✅ Commit subject uses a conventional prefix so release-please picks it up
- ✅ Documentation updated if needed
- ✅ No unnecessary dependencies added
- What changed: Brief summary of the changes
- Why: Reason for the change
- How to test: Steps to verify the change works
- Breaking changes: Note any breaking changes (rare)
- Maintainers will review your PR
- Address any feedback or requested changes
- Once approved, your PR will be merged
- Your changes will be included in the next release
- Use strict mode (already configured)
- Avoid
anytypes unless absolutely necessary - Prefer
async/awaitover callbacks - Use descriptive type names
- Use custom error types from
src/errors.ts - Create new error types for new error cases
- Provide helpful error messages
Example:
if (!isValid) {
throw new CustomError('Clear description of what went wrong');
}- Use functions from
src/git.tsfor git/GitHub operations - Always use
{ reject: false }with execa for commands that might fail - Check exit codes for validation
- Use
@clack/promptsfor all CLI interactions - Follow the existing patterns:
p.intro()- Start of commandp.spinner()- Long-running operationsp.note()- Display informationp.outro()- End of command
- Unit tests: Test individual functions in isolation
- Integration tests: Test git operations (may run actual git commands)
- Keep tests fast: Avoid unnecessary delays
- Clear test names: Use descriptive test names that explain what's being tested
Releases are automated by release-please (see .github/workflows/release.yml and release-please-config.json).
- Every push to
maintriggersrelease-please-action. It scans new conventional-commit subjects since the last release and either opens or updates a "release PR" titledchore(main): release X.Y.Z. - The release PR contains the bumped version in
package.json, the new section inCHANGELOG.md, and.release-please-manifest.jsonupdated. Review and merge it when you're ready to ship. - Merging the release PR creates the git tag (e.g.
v0.5.0) and triggers the npm publish + binary release jobs in the same workflow.
- Make sure commits landing on
mainuse conventional prefixes (otherwise the release PR won't include them in the changelog). - Review the open release PR before merging — confirm version + changelog look right.
- Don't manually edit
package.jsonversion orCHANGELOG.md; release-please owns both.
While in 0.x.x:
- patch (0.0.1 → 0.0.2): Bug fixes, small improvements
- minor (0.1.0 → 0.2.0): New features, larger changes
- Breaking changes are OK in minor versions (API not yet stable)
After 1.0.0:
- patch (1.0.0 → 1.0.1): Bug fixes only
- minor (1.0.0 → 1.1.0): New features, backwards compatible
- major (1.0.0 → 2.0.0): Breaking changes required
- Questions: Open a GitHub Discussion
- Bugs: Open an Issue
- Feature Requests: Open an Issue with the "enhancement" label
By contributing, you agree that your contributions will be licensed under the MIT License.
Thank you for contributing to Venfork! 🎉