phantom-pr
v1.0.0
Published
CI-friendly CLI that opens PRs with passing Jest unit tests.
Readme
phantom-pr
phantom-pr is a CI-friendly CLI that opens PRs with passing Jest/Vitest/pytest unit tests (bounded + deterministic, PR-only, no auto-merge).
Installation
npm install -g phantom-prOr use directly with npx:
npx phantom-pr --helpQuick Start
# 1. Create config file
cat > phantom-pr.yml << EOF
baseBranch: main
github:
owner: your-org
repo: your-repo
tokenEnvVar: GITHUB_TOKEN
mode:
dryRun: true
test:
command: npm test
EOF
# 2. Index your codebase
phantom-pr index --root .
# 3. Generate a plan
phantom-pr plan --root .
# 4. Run in dry mode first
phantom-pr full --root . --dryRunFeatures
🧪 Automatic Test Generation
Generate unit tests for any file:
phantom-pr tests src/components/Button.tsx --local🔍 AST-Powered Analysis (New in 0.5.0)
The test generator now uses AST analysis to:
- Detect branches - Finds
if, ternary, switch, and&&/||operators - Identify effects - Detects
useEffect,useLayoutEffectwith cleanup detection - Map child components - Finds components that need mocking
- Track external calls - Identifies imported functions being called
This enables smarter test generation with better branch coverage.
🔄 Smart Retry with Error Parsing
When tests fail, the retry system now:
- Parses Jest/Vitest error output
- Identifies specific error types (mock not called, assertion mismatch, import errors)
- Generates targeted fix suggestions
- Creates focused retry prompts
📊 Post-Generation Validation
After generating tests, validation checks:
- Branch coverage completeness
- Effect cleanup testing (for
useEffectwith cleanup) - Child component mocking
- Returns specific
llm_reject_*codes for failures
Commands
| Command | Description |
|---------|-------------|
| phantom-pr status | Show config and status |
| phantom-pr index | Index the codebase |
| phantom-pr plan | Generate test plan |
| phantom-pr tests <file> | Generate tests for a file |
| phantom-pr full | Full orchestration (CI mode) |
| phantom-pr pr --prNumber <n> | PR companion mode |
| phantom-pr testability <file> | Add data-testid attributes |
Configuration
Create phantom-pr.yml at repo root:
baseBranch: main
github:
owner: your-org
repo: your-repo
tokenEnvVar: GITHUB_TOKEN
allowForkPrs: false # Safety default
limits:
maxFilesChanged: 50
maxLinesChanged: 5000
maxIterations: 5
maxOpenBotPRs: 3
maxPRsPerRun: 1
mode:
dryRun: true # Set false for real PRs
test:
command: npm testConfig Options
| Option | Description | Default |
|--------|-------------|---------|
| baseBranch | Target branch for PRs | main |
| limits.maxIterations | Max test retry attempts | 5 |
| limits.maxPRsPerRun | Max PRs per CI run | 1 |
| mode.dryRun | Disable real Git/GitHub operations | true |
| test.command | Test command to run | npm test |
CI Integration
Scheduled Runs (Recommended: 4x/day)
# GitHub Actions example
on:
schedule:
- cron: '0 1,7,13,19 * * *'
jobs:
phantom-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npx phantom-pr full --root .
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Jenkins
See docs/jenkins.md for Pipeline examples.
Understanding Reports
tests command → .phantom-pr/report.json
See docs/report-json.md for schema details.
Key fields:
finalOutcome:pass|fail|timeoutattempts: Array of test run attemptsgeneratorWarnings: Any validation issues
full command → .phantom-pr/full.json
caps: Open PR count and limitschosenTarget: What file was selectedlane:tests|testability|skipped
Contributing
Development Setup
git clone https://github.com/norralak/blacksand-phantom-pr.git
cd blacksand-phantom-pr
npm install
npm run build
npm testProject Structure
src/
├── cli.ts # CLI entrypoint
├── commands/ # Command implementations
├── adapters/ # IO boundaries (git, github, fs)
└── core/
├── analysis/ # AST extraction (NEW)
├── context/ # Context packing & selection
├── validation/ # Post-generation validation (NEW)
├── retry/ # Error parsing & feedback (NEW)
├── generator/ # Test generators (LLM, smoke)
├── testRunner/ # Test execution
└── ...Running Tests
# All tests
npm test
# Specific test file
npm test -- tests/ast-extractor.test.mjs
# With verbose output
npm test -- --test-reporter=specCode Style
- TypeScript: Strict mode enabled
- Formatting: Prettier (run
npm run format) - Linting: TypeScript compiler (
npm run lint)
Adding New Features
- Create types first in
types.ts - Implement with tests - follow TDD
- Add barrel export in
index.ts - Update README if user-facing
Risk Register
When adding features with heuristics (parsing, detection), document risks:
| When to add risk | Example | |------------------|---------| | Regex-based parsing | Error parser patterns | | Static lists | External package detection | | Heuristic detection | Branch/effect identification |
Pull Request Guidelines
- One feature per PR
- Include tests for new functionality
- Update documentation
- Run
npm testbefore submitting
Required Permissions
Git
- Create branches
- Push commits
GitHub API (via tokenEnvVar)
- Contents: Read & Write
- Pull requests: Read & Write
- Issues: Read & Write (for comments)
- Metadata: Read
Troubleshooting
"Cannot find module" errors
The test generator mocks child components. If you see import errors:
// Add to test file
jest.mock('./ChildComponent');Tests timing out
- Check for async operations without proper
await - Use
waitFor()from testing-library - Increase timeout in jest config
LLM not generating tests
Check .phantom-pr/report.json for:
llm.allowed: Must betruegeneratorWarnings: Shows rejection reasons
License
Proprietary — All Rights Reserved. See LICENSE.md.
