Like Cursor.com but it still v.0.1
[event: kursor:requested]
│
▼
┌─────────────────────────────────────────────────────────────┐
│ STAGE 1 — planner worker │
│ │
│ codebase-discovery ──► codebase.discovery (child) │
│ │ ├── clone-repo │
│ │ │ (existing? git pull) │
│ │ ├── build-file-tree │
│ │ └── chunk-and-embed │
│ ▼ │
│ codebase-analysis ──► codebase.analysis (child) │
│ │ ├── scan-debt │
│ │ │ dedup via scan-index │
│ │ │ + open PR check │
│ │ ├── structure-findings │
│ │ ├── group-by-file │
│ │ └── generate-plan-json │
│ ▼ │
│ await-human-approval (durable, waits up to 24h) │
│ │ │
│ │ ◄── pnpm scenario:approve <runId> │
│ ▼ │
│ dispatch-executor ── emits kursor:execute:requested │
│ once per topic (includes totalTopics)│
└─────────────────────────────────────────────────────────────┘
│ [event: kursor:execute:requested × N topics]
▼
┌─────────────────────────────────────────────────────────────┐
│ STAGE 2 — executor worker (max 5/tenant, env var) │
│ (one workflow run per topic, runs in parallel batches) │
│ │
│ snapshot-files (copy originals to /tmp/snapshots/) │
│ │ │
│ create-worktree (git worktree add — isolated per topic) │
│ │ │
│ fan-out-verify ──► executor.verify-file (child, parallel) │
│ │ ├── probe.extract │
│ │ ├── llm-refactor │
│ │ ├── lint (ESLint) │
│ │ └── run-tests (up to 3 retries) │
│ ▼ │
│ check-or-rollback (restore snapshot on failure) │
│ │ │
│ emit-result ── emits kursor:verified:requested │
│ worktree kept alive for deploy stage │
└─────────────────────────────────────────────────────────────┘
│ [event: kursor:verified:requested × N]
▼ (fan-in via scan index — N invocations, 1 deploy)
┌─────────────────────────────────────────────────────────────┐
│ STAGE 3 — verifier worker (max 10/tenant) │
│ (triggered N times; only last invocation proceeds) │
│ │
│ aggregate-results │
│ │ records topic result in scan index │
│ │ completedCount < totalTopics → stop, wait │
│ │ completedCount = totalTopics → continue │
│ ▼ │
│ quality gate (>50% failure rate → abort) │
│ │ │
│ emit kursor:deploy:requested │
│ (all passing topics + worktreeDirs) │
└─────────────────────────────────────────────────────────────┘
│ [event: kursor:deploy:requested — once per plan]
▼
┌─────────────────────────────────────────────────────────────┐
│ STAGE 4 — deploy worker (max 1/plan) │
│ │
│ commit-all (git commit in each worktree, parallel) │
│ │ │
│ push-branches (git push authenticated URL, parallel) │
│ │ │
│ open-prs (octokit PR create, LLM body, parallel) │
│ │ │
│ update-index (markInProgress + markFixed in scan index) │
│ │ │
│ cleanup (rm worktrees + workspace + snapshots) │
│ │ │
│ └── emits kursor:completed │
└─────────────────────────────────────────────────────────────┘
│ [event: kursor:completed]
▼
downstream consumers (UI, webhook, etc.)
Executor create-worktree → worktree created
Executor fan-out-verify → LLM edits files inside worktree
Executor check-or-rollback → validate, rollback on failure
Executor emit-result → worktree KEPT ALIVE (not cleaned up)
[verifier fan-in]
Deploy commit-all → git commit inside worktree
Deploy push-branches → git push from worktree
Deploy open-prs → PR opened per topic
Deploy cleanup → git worktree remove (all at once)
Persistent state machine at /tmp/kursorai-index/<tenantId>/scan-index.json that tracks debt findings across runs and fan-in state across verifier invocations:
found → in_progress → fixed → verified
↑ │
└── regressed ◄─┘ (new commit after gitPullLatest)
- Prevents duplicate PRs —
shouldSkip(file, debtType)returns true forin_progress / fixed / verified - Detects regressions —
syncWithCommit(newHead)flagsfixedentries asregressedon new commit - Fan-in counter —
markTopicResult(planId, topicId, result)accumulates N executor results; verifier checkscompletedCount === totalTopicsto know when to dispatch deploy
Run
make infra-upbefore any of the steps below.
# All 4 workers at once (recommended)
pnpm worker
# Or individually in separate terminals
pnpm worker:planner
pnpm worker:executor
pnpm worker:verifier
pnpm worker:deployTune executor parallelism and iteration count via env:
# Run all topics in parallel, 1 LLM call per file (fast for dev)
MAX_VERIFY_ITERATIONS=1 EXECUTOR_MAX_RUNS=9 pnpm worker:executorpnpm triggerWatch the planner terminal — when it reaches the approval step it prints:
[kursor] ⏳ Waiting for human approval
[kursor] Plan : plan-abc123
[kursor] RunID : 550e8400-e29b-41d4-a716-446655440000
[kursor] Run : pnpm scenario:approve 550e8400-e29b-41d4-a716-446655440000
[kursor] Reject: pnpm scenario:approve 550e8400-e29b-41d4-a716-446655440000 --reject "reason"
# Approve
pnpm scenario:approve <runId>
# Reject with reason
pnpm scenario:approve <runId> --reject "plan looks wrong"pnpm test:unit # scan index engine unit tests
pnpm test:planner # planner + shared workflows
pnpm test:resilience # crash-resume across all workers
pnpm test:concurrency # multi-tenant concurrencyWorker names (kursor-planner-worker, kursor-executor-worker, kursor-verifier-worker, kursor-deploy-worker) identify the type of worker, not a specific tenant. Hatchet load-balances events across all available instances of the same worker name.
Tenant isolation is enforced via tenantId:
- Snapshots:
/tmp/snapshots/${tenantId}/ - Workspaces:
/tmp/workspaces/${tenantId}/ - Scan index:
/tmp/kursorai-index/${tenantId}/scan-index.json - Concurrency keys scoped per
tenantId
| Workflow | Concurrency key | Max parallel runs |
|---|---|---|
planner |
— | unlimited (one per trigger) |
executor.run |
input.tenantId |
5 per tenant (env: EXECUTOR_MAX_RUNS) |
verifier.run |
input.tenantId |
10 per tenant |
deploy.run |
input.planId |
1 per plan |
The deploy workflow is keyed by planId (not tenantId) to ensure exactly one deploy runs per plan, even if the verifier emits the event more than once.
# 1. Start infra
make infra-up
# 2. Build & start worker containers
make workers-build
make workers-up
# 3. Monitor logs
make workers-logsFour worker containers: planner, executor, verifier, deploy.
Normal clone:
/tmp/workspaces/tenant-demo/
.git/
src/
With worktrees (one per executor topic):
/tmp/workspaces/tenant-demo/
.git/
objects/ ← SHARED object store
worktrees/
worktree-topic-A/
worktree-topic-B/
src/
/tmp/workspaces/worktree-topic-A/
.git (file, not folder — points to tenant-demo/.git/worktrees/topic-A)
src/
cases/index.tsx ← EDITED BY TOPIC A
/tmp/workspaces/worktree-topic-B/
.git
src/
utils/api.ts ← EDITED BY TOPIC B
Worktrees are removed by the deploy worker after PRs are opened.
found → (PR opened) → in_progress → (reconcile check PR status)
├─ merged → fixed (commit merge to main)
└─ closed → found (re-scan)