Identify and Refactor
Scan a codebase for refactoring opportunities, then implement each refactor in an isolated git worktree with LLM-powered review.
Workflow​
From demos/identify-and-address-refactors/run.ts:
runPipeline(
pipe(
constant({ folder: srcDir }),
listTargetFiles,
forEach(analyze).flatten(),
forEach(assessWorthiness).then(Option.collect()),
forEach(
withResource({
create: createBranchWorktree,
action: implementAndReview,
dispose: deleteWorktree,
}),
),
),
);
Stages​
- List files — find all files in the target directory.
- Analyze — for each file, identify refactoring opportunities.
flatten()merges the per-file arrays into a single list. - Filter — an LLM assesses whether each refactor is worth doing.
Option.collect()filters out theNonevalues. - Implement in worktrees — each surviving refactor gets its own git worktree via
withResource. The worktree is created before work starts and cleaned up after, regardless of success or failure.
The implementation loop​
From demos/identify-and-address-refactors/handlers/refactor.ts:
export const implementAndReview = bindInput<ImplementAndReviewParams>((params) => pipe(
params.pick("worktreePath", "description").then(implement).drop(),
params.pick("worktreePath").then(typeCheckFix).drop(),
loop((recur) =>
pipe(judgeRefactor, classifyJudgment).branch({
NeedsWork: pipe(
applyFeedback.drop(),
params.pick("worktreePath").then(typeCheckFix),
).drop().then(recur),
Approved: drop,
}),
).drop(),
params.pick("worktreePath").then(commit).drop(),
pipe(params.pick("branch", "description"), preparePRInput, createPR),
));
Each refactor goes through: implement → type-check/fix → judge → revise if needed → commit → PR. The bindInput gives every step access to the original parameters (worktree path, branch name, description) without threading them through the pipeline.
Key points​
- Worktree isolation: each refactor runs in its own git worktree. Parallel refactors don't interfere with each other.
withResource: guarantees worktree cleanup even if the refactor fails.bindInput: the worktree path is needed at multiple points (type-check, commit, etc.) —bindInputmakes it available everywhere without threading.- Adversarial loop: a judge LLM reviews the refactor and requests revisions. The implementing agent never sees the judge's criteria.
- Type-check/fix loop: after every change, the type-check/fix loop runs until the code compiles cleanly.