Skip to content

OJCompany/OJ_Code_Refactor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OJ Refactor

AST-powered TypeScript refactoring agent with LLM-generated options, metric-driven decisions, and automatic validation.

한국어 · English


English

What is OJ Refactor?

OJ Refactor is a local CLI tool that analyzes TypeScript source files at the AST level, detects code smells, and presents refactoring options generated by a large language model — each one grounded in a different engineering trade-off. Every option includes before/after metrics and is validated by the TypeScript compiler before any file is touched.

The core principle: the algorithm decides what to fix, the LLM executes the fix, and the compiler verifies the result. No guess-work, no single "just do it" suggestion.


Why OJ Refactor?

Problem with existing tools How OJ Refactor solves it
Cursor / Copilot give one LLM answer with no rationale Catalog rules + 3 trade-off options + measurable metrics
Sourcery is rule-only with no AI flexibility Rules as the decision engine, LLM as the executor
CodeRabbit / Greptile are cloud-only and comment-only Local-first, applies changes, auto-commits
Linters auto-fix style but not structure Structural refactoring across 5 catalogs

System Architecture

┌─────────────────────────────────────────────────────────────┐
│                        CLI Entry Point                       │
│              npx refactor <file>  |  npx refactor --pr      │
└───────────────────────┬─────────────────────────────────────┘
                        │
          ┌─────────────▼──────────────┐
          │      Tidy First Advisor     │  ← Kent Beck "Tidy First?" §16, §21
          │  analyzePRReadiness()       │    classifies uncommitted diffs as
          │  whenToTidy()               │    FIRST / AFTER / LATER / NEVER
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │     Convention Selector     │  ← auto-detects from CONVENTIONS.md
          │  selectConvention()         │    or lets user pick enterprise preset
          │  Airbnb / Google / TS-strict│    (Airbnb, Google Standard, TS-strict…)
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │      Smell Detection        │
          │  detect()       → any types │  ← TypeScript Compiler API (AST walk)
          │  detectNesting()→ deep nest │
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │      Catalog Router         │
          │  replace-any                │  ← picks catalog based on
          │  guard-clauses              │    which smell dominates
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │     LLM Option Generator    │  ← Anthropic Claude (SDK)
          │  generate()                 │    sends catalog rule + full AST context
          │  generateGuardClauses()     │    returns 1 refactored option
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │     Metrics Engine          │
          │  measureComplexity()        │  ← Cyclomatic complexity, LOC,
          │  before / after diff        │    max nesting depth — shown on
          └─────────────┬──────────────┘    the option card
                        │
          ┌─────────────▼──────────────┐
          │  Interactive Option Card    │  ← terminal diff rendered side-by-side
          │  formatSingle()             │    user confirms or skips
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │     Apply + Validate        │
          │  apply()                    │  ← writes file, keeps .bak backup
          │  tscCheckAsync()            │    runs tsc --noEmit
          │  validateConvention()       │    LLM checks style rule compliance
          │  rollback()                 │    reverts on any failure
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │     Commit + PR             │
          │  buildCommitMessage()       │  ← semantic commit message
          │  commitRefactoring()        │    optional git commit
          └─────────────────────────────┘

Refactoring Catalogs

Catalog Trigger What it does
replace-any any in params, returns, variables, generics, assertions Replaces any with precise types (unknown, interfaces, unions)
guard-clauses Nesting depth ≥ 3 Flattens deeply nested conditionals into early-return guard clauses

Each catalog carries a Tidy First timing label (FIRST / AFTER / LATER / NEVER) derived from Kent Beck's §21 decision rules, so developers know when to tidy, not just how.


Tidy First Integration

OJ Refactor embeds Kent Beck's Tidy First? methodology at two points:

  1. Pre-flight check — Before processing any file, analyzePRReadiness() uses Claude to classify uncommitted git diffs as TIDY | BEHAVIOR | MIXED. If structural and behavioral changes are mixed in the same branch, a warning is surfaced (§16: Separate Tidying).

  2. Timing advicewhenToTidy() evaluates issue severity and returns one of four timing labels per §21:

    • FIRST — tidy before the behavior change (high smell density, blocks comprehension)
    • AFTER — tidy in a follow-up commit
    • LATER — batch with other minor tidying
    • NEVER — issue count is zero or code is never touched again

Convention System

The tool detects your project's coding style automatically and enforces it during refactoring:

Source How it works
auto Scans CONVENTIONS.md in the repo root and passes it to the LLM as a style constraint
enterprise User selects from built-in presets: Airbnb JS, Google JS, Standard JS, Airbnb TS, Google TS, TS Strict

When parallel mode is active, TypeScript type-check and convention validation run concurrently to cut wait time.


Metrics

Every option card shows measurable before/after numbers:

Metric Description
Cyclomatic Complexity Branch point count (if, for, while, &&, ||, ??)
Lines of Code Non-blank lines
Max Nesting Depth Deepest control-flow level

Example card output:

  ◆  auth.ts  ·  12 `any` occurrences

  Tidy FIRST  any 12개는 동작 변경 전 먼저 제거해야 합니다

  ─ before ──────────────────────────────
  function fetchUser(id: any): any { ... }

  ─ after ────────────────────────────────
  function fetchUser(id: string): Promise<User> { ... }

  complexity  14 → 8  (-43%)
  lines       84 → 61  (-27%)
  depth        4 → 2  (-50%)

Installation

Prerequisites

  • Node.js 20+
  • An Anthropic API key
# Clone and install
git clone <repo-url>
cd OJ_Code_Refactor
npm install

# Set your API key
export ANTHROPIC_API_KEY=sk-ant-...

# Build
npm run build

Usage

Single-file mode

npx refactor src/auth.ts
  1. Select language (English / 한국어)
  2. Optionally enable inline comments in the generated code
  3. Tool detects smells, shows a refactoring option with metrics
  4. Confirm to apply — tsc validates the output automatically
  5. Optionally commit with a generated semantic commit message

PR mode — process all changed TypeScript files

npx refactor --pr

Works on every .ts file changed since your base branch (main / develop). Supports auto-apply (no confirmation prompt per file) or confirm-each modes. A rollback prompt appears if any file fails validation.

Optional flags

Flag Effect
--pr PR mode: scan all changed TS files
--quokka Enable the animated quokka mascot

End-to-End Workflow

Developer runs: npx refactor src/api.ts
       │
       ▼
[1] Tidy First pre-flight
    └─ any uncommitted mixed changes? → warn to separate tidying from behavior
       │
       ▼
[2] Convention detection
    └─ CONVENTIONS.md found → "Airbnb TS" preset → locked in for this session
       │
       ▼
[3] AST smell detection
    └─ detect(): 5 `any` usages found
    └─ detectNesting(): 1 nesting depth violation (depth 4)
    └─ replace-any selected (5 > 1)
       │
       ▼
[4] Timing advice
    └─ 5 occurrences → AFTER (tidy in a follow-up commit)
       │
       ▼
[5] LLM generation (Claude)
    └─ catalog rule + full source → 1 refactored option returned
       │
       ▼
[6] Metrics computed
    └─ complexity 14 → 8, lines 84 → 61, depth 4 → 2
       │
       ▼
[7] Option card displayed → user confirms
       │
       ▼
[8] apply() writes file, .bak backup created
       │
       ▼
[9] tsc --noEmit + convention check (parallel)
    └─ pass → done
    └─ fail → rollback() restores original, error shown
       │
       ▼
[10] Semantic commit message shown
     └─ "refactor(replace-any): replace 5 any usages in api.ts"
     └─ user chooses to commit or skip

Non-Goals (v1.0)

  • Languages other than TypeScript
  • Multi-file refactoring
  • IDE plugins
  • Cloud / SaaS mode
  • GitLab / Bitbucket PR adapters (interface exists, implementation is v1.1+)
  • Self-hosted LLM hosting

Roadmap

Version Milestone
v1.0 replace-any + guard-clauses, metrics, tsc validation, conventional commits, PR mode
v1.1 +3 catalog rules (loop-pipeline, async-await, extract-function), GitLab adapter
v1.2 Python catalog (5 rules), multi-file analysis
Future IDE plugin, opt-in telemetry, option-selection learning

License

MIT



한국어

OJ Refactor란?

OJ Refactor는 TypeScript 소스 파일을 AST 수준에서 분석하고, 코드 스멜을 감지한 뒤, 대형 언어 모델(LLM)이 생성한 리팩토링 옵션을 제시하는 로컬 CLI 도구입니다. 각 옵션은 서로 다른 엔지니어링 트레이드오프를 기반으로 하며, Before/After 메트릭을 포함합니다. 변경 사항은 TypeScript 컴파일러가 검증한 후에만 파일에 적용됩니다.

핵심 철학: 알고리즘이 무엇을 고칠지 결정하고, LLM이 고치고, 컴파일러가 검증합니다. 단일 "그냥 해줘" 제안 없이, 측정 가능한 근거 위에서 의사결정합니다.


왜 OJ Refactor인가?

기존 도구의 한계 OJ Refactor의 해결 방식
Cursor/Copilot은 LLM 단일 답, 근거 없음 카탈로그 룰 + 트레이드오프 기반 옵션 + 메트릭
Sourcery는 룰만, AI 유연성 없음 룰이 의사결정, LLM이 실행
CodeRabbit/Greptile은 클라우드 전용, 코멘트만 로컬 우선, 변경 적용 + 자동 커밋
린터는 스타일만, 구조 리팩토링 없음 5종 카탈로그 기반 구조 리팩토링

시스템 아키텍처

┌─────────────────────────────────────────────────────────────┐
│                      CLI 진입점                              │
│          npx refactor <파일>  |  npx refactor --pr           │
└───────────────────────┬─────────────────────────────────────┘
                        │
          ┌─────────────▼──────────────┐
          │    Tidy First 어드바이저    │  ← Kent Beck "Tidy First?" §16, §21
          │  analyzePRReadiness()       │    미커밋 diff를 FIRST/AFTER/LATER/NEVER
          │  whenToTidy()               │    로 분류
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │      컨벤션 선택기          │  ← CONVENTIONS.md 자동 감지
          │  selectConvention()         │    또는 기업 프리셋 선택
          │  Airbnb / Google / TS-strict│    (Airbnb, Google, TS Strict…)
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │      코드 스멜 감지         │
          │  detect()       → any 타입  │  ← TypeScript Compiler API (AST 탐색)
          │  detectNesting()→ 깊은 중첩 │
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │      카탈로그 라우터        │
          │  replace-any                │  ← 지배적인 스멜에 따라
          │  guard-clauses              │    카탈로그 자동 선택
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │     LLM 옵션 생성기         │  ← Anthropic Claude (SDK)
          │  generate()                 │    카탈로그 룰 + 전체 AST 컨텍스트 전송
          │  generateGuardClauses()     │    리팩토링된 옵션 1개 반환
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │      메트릭 엔진            │
          │  measureComplexity()        │  ← 순환 복잡도, LOC, 최대 중첩 깊이
          │  before / after 비교        │    옵션 카드에 수치로 표시
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │   인터랙티브 옵션 카드      │  ← 터미널 diff 표시
          │  formatSingle()             │    사용자가 확인 또는 건너뜀
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │      적용 + 검증            │
          │  apply()                    │  ← 파일 쓰기, .bak 백업 생성
          │  tscCheckAsync()            │    tsc --noEmit 실행
          │  validateConvention()       │    LLM 스타일 룰 준수 확인
          │  rollback()                 │    실패 시 자동 복구
          └─────────────┬──────────────┘
                        │
          ┌─────────────▼──────────────┐
          │      커밋 + PR              │
          │  buildCommitMessage()       │  ← 시맨틱 커밋 메시지 생성
          │  commitRefactoring()        │    선택적 git commit
          └─────────────────────────────┘

리팩토링 카탈로그

카탈로그 감지 조건 수행 내용
replace-any 파라미터·반환·변수·제네릭·단언의 any anyunknown, 인터페이스, 유니온 등 정밀 타입으로 교체
guard-clauses 중첩 깊이 ≥ 3 깊은 중첩 조건문을 early-return 가드 클로즈로 평탄화

각 카탈로그에는 Kent Beck §21 기준으로 도출된 Tidy First 타이밍 레이블(FIRST / AFTER / LATER / NEVER)이 붙어, 개발자가 언제 정리해야 하는지 바로 알 수 있습니다.


Tidy First 통합

OJ Refactor는 Kent Beck의 Tidy First? 방법론을 두 지점에 내재화합니다.

  1. 사전 점검 — 파일 처리 전 analyzePRReadiness()가 Claude를 사용해 미커밋 git diff를 TIDY | BEHAVIOR | MIXED로 분류합니다. 구조 변경과 동작 변경이 같은 브랜치에 섞여 있으면 경고를 표시합니다 (§16: 정리 분리).

  2. 타이밍 조언whenToTidy()가 이슈 심각도를 평가해 §21 기준으로 네 가지 타이밍 레이블 중 하나를 반환합니다.

    • FIRST — 동작 변경 전 먼저 정리 (스멜 밀도 높음, 이해를 막음)
    • AFTER — 동작 변경 후 별도 커밋으로 정리
    • LATER — 나중에 일괄 처리
    • NEVER — 이슈가 없거나 해당 코드를 다시 건드릴 일이 없음

컨벤션 시스템

도구가 프로젝트 코딩 스타일을 자동으로 감지하고, 리팩토링 시 해당 스타일을 강제합니다.

소스 동작 방식
auto 저장소 루트의 CONVENTIONS.md를 스캔해 LLM에 스타일 제약으로 전달
enterprise 내장 프리셋 중 선택: Airbnb JS, Google JS, Standard JS, Airbnb TS, Google TS, TS Strict

parallel 모드에서는 TypeScript 타입 체크와 컨벤션 검증이 동시에 실행되어 대기 시간을 줄입니다.


메트릭

모든 옵션 카드에 측정 가능한 Before/After 수치가 표시됩니다.

메트릭 설명
순환 복잡도 분기점 수 (if, for, while, &&, ||, ??)
코드 라인 수 비공백 라인 수
최대 중첩 깊이 가장 깊은 제어 흐름 레벨

옵션 카드 예시:

  ◆  auth.ts  ·  12개 `any` 감지

  Tidy FIRST  any 12개는 동작 변경 전 먼저 제거해야 합니다

  ─ 변경 전 ──────────────────────────────
  function fetchUser(id: any): any { ... }

  ─ 변경 후 ──────────────────────────────
  function fetchUser(id: string): Promise<User> { ... }

  복잡도  14 → 8  (-43%)
  라인 수  84 → 61  (-27%)
  중첩 깊이  4 → 2  (-50%)

설치

필요 사항

  • Node.js 20 이상
  • Anthropic API 키
# 클론 및 설치
git clone <repo-url>
cd OJ_Code_Refactor
npm install

# API 키 설정
export ANTHROPIC_API_KEY=sk-ant-...

# 빌드
npm run build

사용 방법

단일 파일 모드

npx refactor src/auth.ts
  1. 언어 선택 (English / 한국어)
  2. 생성된 코드에 인라인 주석 포함 여부 선택
  3. 도구가 스멜을 감지하고, 메트릭이 포함된 리팩토링 옵션을 표시
  4. 확인 시 적용 — tsc가 자동으로 결과를 검증
  5. 생성된 시맨틱 커밋 메시지로 커밋 선택 가능

PR 모드 — 변경된 모든 TypeScript 파일 처리

npx refactor --pr

베이스 브랜치(main / develop) 이후 변경된 모든 .ts 파일에 대해 동작합니다. 자동 적용 (파일별 확인 없음) 또는 개별 확인 모드를 선택할 수 있습니다. 어느 파일이라도 검증에 실패하면 전체 롤백 프롬프트가 표시됩니다.

선택적 플래그

플래그 효과
--pr PR 모드: 변경된 모든 TS 파일 스캔
--quokka 쿼카 마스코트 애니메이션 활성화

엔드-투-엔드 워크플로우

개발자 실행: npx refactor src/api.ts
       │
       ▼
[1] Tidy First 사전 점검
    └─ 미커밋 변경에 구조·동작 혼재? → 분리 커밋 권장 경고
       │
       ▼
[2] 컨벤션 감지
    └─ CONVENTIONS.md 발견 → "Airbnb TS" 프리셋 → 세션 고정
       │
       ▼
[3] AST 스멜 감지
    └─ detect(): `any` 5곳 발견
    └─ detectNesting(): 중첩 깊이 위반 1곳 (깊이 4)
    └─ replace-any 선택 (5 > 1)
       │
       ▼
[4] 타이밍 조언
    └─ 5건 → AFTER (동작 변경 후 별도 커밋으로 정리)
       │
       ▼
[5] LLM 생성 (Claude)
    └─ 카탈로그 룰 + 전체 소스 → 리팩토링 옵션 1개 반환
       │
       ▼
[6] 메트릭 계산
    └─ 복잡도 14 → 8, 라인 84 → 61, 중첩 깊이 4 → 2
       │
       ▼
[7] 옵션 카드 표시 → 사용자 확인
       │
       ▼
[8] apply()가 파일 쓰기, .bak 백업 생성
       │
       ▼
[9] tsc --noEmit + 컨벤션 검사 (병렬)
    └─ 통과 → 완료
    └─ 실패 → rollback()으로 원본 복구, 오류 표시
       │
       ▼
[10] 시맨틱 커밋 메시지 표시
     └─ "refactor(replace-any): api.ts의 any 5곳 교체"
     └─ 사용자가 커밋 또는 건너뜀 선택

비목표 (v1.0)

  • TypeScript 외 언어
  • 다중 파일 리팩토링
  • IDE 플러그인
  • 클라우드 / SaaS 모드
  • GitLab / Bitbucket PR 어댑터 (인터페이스 존재, 구현은 v1.1+)
  • 자체 LLM 호스팅

로드맵

버전 마일스톤
v1.0 replace-any + guard-clauses, 메트릭, tsc 검증, 컨벤셔널 커밋, PR 모드
v1.1 카탈로그 +3개 (loop-pipeline, async-await, extract-function), GitLab 어댑터
v1.2 Python 카탈로그 (5개 룰), 다중 파일 분석
향후 IDE 플러그인, opt-in 텔레메트리, 옵션 선택 학습

라이선스

MIT

About

AI agent for code review, refactoring, and PR creation — built as a pi-coding-agent package with company-specific convention support.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors