Thanks for your interest in improving Operator. This guide covers local setup, the checks every change must pass, and the conventions the codebase enforces.
- Node.js 24+ and npm.
- git 2.40+ on
$PATH(the engine shells out togitin managed workspaces). - One agent CLI (e.g. Claude Code) on
$PATHfor end-to-end runs. - A GitHub token with repo scope for runs against a real repository.
npm install # install all workspaces
cp config/repos.yaml.example config/repos.yaml # then edit for your repo
cp .env.local.example .env.local # then fill in MANAGED_REPO_GH_TOKEN + agent key(s)Every change must pass all three before it is merged:
npm run typecheck # tsc --noEmit across all workspaces
npm test # vitest with the coverage gate from vitest.config.ts
npm run lint # eslint + ts-prune + knip (dead-code gates)One manual cycle against a configured repo:
npx tsx --env-file=.env.local engine/entry.ts --once --repo <repo-id>engine/— the daemon (composition rootengine/entry.ts, flat layout).engine/content/— bundled prompts/templates/defaults, seeded into the KV store.packages/core— shared types, interfaces, Zod schemas, and error classes.@operator/coreruntime carries no I/O and no cross-workspace imports;zodis its single runtime dependency.packages/adapters— concrete implementations (SQLite KV, GitHub VCS, …).app/— Next.js observability UI.deployment/— container image + compose for running the engine.
See docs/ for the architecture, workflow, and vision documents.
These are enforced in review and partly in CI. The non-negotiables:
- TypeScript strict. No
any, no@ts-ignore, noas any. - Named exports only — no default exports. Use
import typefor types. - No dead code. Every exported symbol is reachable from
engine/entry.tsor a colocated test;ts-pruneandkniprun in CI. - Colocated
*.test.tsfor every implementation file,>=90%coverage on touched files (>=95%forengine/pipeline/primitives/) — reviewed per change. The gate CI enforces is project-wide and lives invitest.config.ts: 90% lines, functions, and statements, 81% branches. Every bug fix ships with a regression test named for the bug scenario. OperationContextthreaded through every function that touches state, git, VCS, or the filesystem.- Layer boundaries.
@operator/corenever imports adapters/engine/app;@operator/adaptersnever imports engine/app. Onlyengine/pipeline/primitives/may callgit.*/PRManager.*/VCSPlatform.*/AgentRuntime.*directly. Branch management lives only inWorkspaceScope. - No force-push, ever (including
--force-with-lease). The operator never pushes tomaster/main/develop— it authors feature branches + PRs. - Platform-neutral vocabulary in core (
CodeReview,WorkItem), neverPullRequest/Issueoutsideengine/platforms/github/. - Observability. Every externally visible action (commit, push, PR change, label flip, comment) and every decision gets an INFO log line with enough context to reconstruct the run from logs alone.
- English-only source — all output, comments, identifiers, and docs.
- LF line endings on all
.ts,.md,.yaml,.yml,.sh. - File size:
engine/pipeline/**≤200 lines, elsewhere ≤300 (logging, comments, and JSDoc do not count toward the cap).
Exactly one line: a capital letter, past tense, no prefixes (no feat:,
fix:, chore:), and no Co-authored-by / Signed-off-by trailers. Describe
what changed, not what was tried — e.g. Added the KVStore SQLite adapter.
- Branch from
master. - Keep each PR to a single logical change with a clean revert.
- Ensure
npm run typecheck && npm test && npm run lintare green. - List anything the PR deletes in the description.
Do not open a public issue or pull request for a suspected vulnerability. Report it privately through the repository's Security tab — see SECURITY.md for the process and what to expect.
By contributing you agree that your contributions are licensed under the project's MIT License.