Skip to content

Latest commit

 

History

History
1409 lines (1025 loc) · 23.9 KB

File metadata and controls

1409 lines (1025 loc) · 23.9 KB

Poniżej masz szczegółowy plan refaktoryzacji całego projektu reDUP, nie tylko funkcji intent. Traktuję to jako plan techniczny do wykonania etapami: najpierw porządkowanie architektury, potem stabilizacja pipeline, potem wdrożenie Intent DSL / RIDL, potem CLI/MCP/reportery/testy.

Założenie główne: nie przebudowywać wszystkiego naraz. reDUP ma już naturalne miejsca rozszerzeń: skaner, modele, pipeline wykrywania, CLI, reportery i testy. Dlatego nową funkcję intent duplication najlepiej dodać jako osobny tor analizy obok exact/structural/near/fuzzy/semantic, bez mieszania jej z obecnym hasherem i matcherem.


1. Cel refaktoryzacji całego projektu

Docelowo reDUP powinien mieć czystą architekturę:

CLI / MCP
  ↓
Config / ScanConfig
  ↓
Scanner / Extractors
  ↓
CodeBlock[]
  ↓
Detection Pipeline
  ├── exact duplicates
  ├── structural duplicates
  ├── near duplicates
  ├── fuzzy duplicates
  ├── intent duplicates
  └── semantic duplicates
  ↓
DuplicateGroup[]
  ↓
Planner / Decision / Refactor Suggestions
  ↓
Reporters JSON / YAML / Markdown / TOON / MCP

Główne cele:

1. Uporządkować modele domenowe.
2. Odchudzić duże moduły i hotspoty.
3. Rozdzielić skanowanie, detekcję, grupowanie, planowanie i raportowanie.
4. Dodać Intent DSL jako osobny, algorytmiczny detektor.
5. Ujednolicić CLI, MCP i config.
6. Wzmocnić testy kontraktowe i e2e.
7. Przygotować projekt do dalszego rozwoju bez dokładania chaosu.

2. Największe problemy obecnej architektury

Na podstawie mapy projektu i wcześniejszego planu największe ryzyka są takie:

1. Za dużo odpowiedzialności w CLI.
2. Pipeline jest funkcjonalny, ale zaczyna puchnąć.
3. Modele wynikowe mogą się rozrastać pod kolejne detektory.
4. Reportery mają dużo logiki formatowania i przeliczania.
5. MCP częściowo powiela logikę CLI/config.
6. Skaner ma dużo wariantów: normal, ultra_fast, memory_optimized, parallel.
7. Intent duplication może pogłębić chaos, jeśli zostanie wklejony bez warstwy domenowej.

Najważniejsze: nie dodawać kolejnych if intent_enabled w losowych miejscach. Dla intentów trzeba stworzyć osobny pakiet i wpiąć go tylko przez pipeline.


3. Docelowy podział modułów

Obecne główne warstwy

src/redup/core/
  hasher.py
  matcher.py
  lsh_matcher.py
  universal_fuzzy.py
  semantic.py
  scanner/
  pipeline/
  planner.py
  decision.py

Po refaktoryzacji

src/redup/core/
  models.py
  config.py

  scanner/
    __init__.py
    filters.py
    loader.py
    extraction.py
    strategies.py

  detection/
    __init__.py
    exact.py
    structural.py
    near.py
    fuzzy.py
    semantic.py
    intent.py

  intent/
    __init__.py
    schema.py
    ridl.py
    comments.py
    parser.py
    normalizer.py
    signature.py
    scoring.py
    detector.py
    grouping.py

  pipeline/
    __init__.py
    phases.py
    duplicate_finder.py
    groups.py
    result_builder.py

  planning/
    decision.py
    planner.py
    refactor_advisor.py

  reporting/
    evidence.py
    metrics.py

Nie musisz robić tego od razu. To jest kierunek docelowy.


4. Etap 0 — zamrożenie obecnego zachowania

Cel

Zanim ruszysz refaktoryzację, trzeba zabezpieczyć obecny stan.

Zadania

Uruchomić:

pytest -q
ruff check .
python -m redup scan ./src --format json
python -m redup scan ./src --format toon
python -m redup check ./src

Zapisać wyniki bazowe:

docs/refactor/baseline/
  pytest.txt
  ruff.txt
  scan-src.json
  scan-src.toon
  check-src.txt

Kryterium ukończenia

Masz punkt odniesienia. Każdy kolejny etap porównujesz do baseline.


5. Etap 1 — uporządkowanie modeli domenowych

Plik główny

src/redup/core/models.py

Problem

models.py jest centralny dla całego systemu, więc każda nowa funkcja może go rozpychać. Jeśli dodasz pola typu intent_label, intent_signature, intent_score_breakdown bezpośrednio do DuplicateGroup, za chwilę model będzie zawierał pola dla każdego detektora.

Refaktoryzacja

Dodać ogólny model dowodów:

class DuplicateEvidence(BaseModel):
    kind: str
    score: float
    reason: dict[str, Any] = Field(default_factory=dict)

Rozszerzyć DuplicateGroup:

evidence: list[DuplicateEvidence] = Field(default_factory=list)
metadata: dict[str, Any] = Field(default_factory=dict)

Dodać DuplicateType.INTENT:

class DuplicateType(str, Enum):
    EXACT = "exact"
    STRUCTURAL = "structural"
    NEAR = "near"
    FUZZY = "fuzzy"
    SEMANTIC = "semantic"
    INTENT = "intent"

Testy

tests/test_models.py

Dodać:

def test_duplicate_type_has_intent():
    assert DuplicateType.INTENT.value == "intent"


def test_duplicate_group_accepts_evidence():
    ...

Efekt

Każdy detektor może podać własne wyjaśnienie bez psucia modeli.


6. Etap 2 — uporządkowanie konfiguracji

Pliki

src/redup/core/models.py
src/redup/core/config.py
src/redup/cli_app/config_builder.py
src/redup/mcp/handlers.py

Problem

Konfiguracja jest rozproszona między CLI, ScanConfig, config builderem i MCP.

Docelowo

ScanConfig powinien być jednym źródłem prawdy.

Dodać sekcję intent:

intent_enabled: bool = False
intent_threshold: float = 0.84
intent_source: Literal["explicit", "comment", "heuristic"] = "explicit"
intent_format: Literal["ridl", "yaml", "auto"] = "auto"
intent_context_lines: int = 12

Nie używałbym jednocześnie:

intent_comments_only
intent_require_tag
intent_mode

bo to będzie mylące. Lepiej mieć jedno:

intent_source = explicit | comment | heuristic

Konfiguracja TOML

[redup.intent]
enabled = true
threshold = 0.84
source = "explicit"
format = "auto"
context_lines = 12

Testy

tests/test_config.py
tests/test_e2e.py
tests/test_mcp_server.py

Kryterium ukończenia

CLI, config file i MCP tworzą ten sam ScanConfig.


7. Etap 3 — odchudzenie CLI

Pliki

src/redup/cli_app/main.py
src/redup/cli_app/scan_commands.py
src/redup/cli_app/compare_command.py
src/redup/cli_app/tasks_command.py
src/redup/cli_app/output_writer.py

Problem

CLI powinno tylko:

1. zebrać parametry,
2. zbudować config,
3. odpalić core,
4. przekazać wynik do reportera.

Nie powinno zawierać logiki detekcji, filtrowania, grupowania ani refaktoryzacji.

Refaktoryzacja

Wydzielić:

src/redup/cli_app/options.py
src/redup/cli_app/commands/scan.py
src/redup/cli_app/commands/compare.py
src/redup/cli_app/commands/check.py
src/redup/cli_app/commands/config.py
src/redup/cli_app/commands/tasks.py

Jeżeli nie chcesz przebudowywać katalogów od razu, najpierw wydziel tylko helpery:

src/redup/cli_app/scan_options.py
src/redup/cli_app/scan_runner.py

Docelowa funkcja CLI

def scan_command(...):
    config = build_scan_config_from_cli(...)
    result = run_scan(config)
    write_results(result, format=format, output=output)

Kryterium

scan_commands.py nie powinien mieć logiki skanowania ani detekcji.


8. Etap 4 — ujednolicenie pipeline

Pliki

src/redup/core/pipeline/__init__.py
src/redup/core/pipeline/duplicate_finder.py
src/redup/core/pipeline/phases.py
src/redup/core/pipeline/groups.py

Problem

Pipeline ma kilka wariantów:

analyze
analyze_optimized
analyze_parallel
find_duplicates_phase_optimized
find_duplicates_phase_lazy

To działa, ale nowy detektor intent zwiększy liczbę gałęzi.

Docelowy model

Wprowadzić jawne fazy:

ScanPhase
ProcessBlocksPhase
DetectionPhase
GroupFinalizePhase
SuggestionPhase
ReportPhase

Technicznie:

class DetectionEngine(Protocol):
    name: str

    def enabled(self, config: ScanConfig) -> bool:
        ...

    def detect(self, blocks: list[CodeBlock], config: ScanConfig) -> list[DuplicateGroup]:
        ...

Detektory jako lista

DETECTORS = [
    ExactDuplicateDetector(),
    StructuralDuplicateDetector(),
    NearDuplicateDetector(),
    FuzzyDuplicateDetector(),
    IntentDuplicateDetector(),
    SemanticDuplicateDetector(),
]

Pipeline:

groups = []

for detector in DETECTORS:
    if detector.enabled(config):
        groups.extend(detector.detect(blocks, config))

Zaleta

Nie dodajesz kolejnych if-ów w duplicate_finder.py.

Etap pośredni

Jeśli to za duża zmiana na raz, zostaw obecny pipeline, ale dodaj jedną funkcję:

def find_intent_groups(blocks, config) -> list[DuplicateGroup]:
    ...

i wpiąć ją po find_near_duplicate_groups, przed find_semantic_groups.


9. Etap 5 — przeniesienie detektorów do osobnej warstwy

Nowy katalog

src/redup/core/detection/
  __init__.py
  exact.py
  structural.py
  near.py
  fuzzy.py
  semantic.py
  intent.py

Mapowanie obecnych modułów

hasher.py                → detection/exact.py + detection/structural.py
lsh_matcher.py           → detection/near.py
universal_fuzzy.py       → detection/fuzzy.py
semantic.py              → detection/semantic.py
intent/detector.py       → detection/intent.py

Na początku nie przenoś całego kodu. Możesz zrobić adaptery:

class ExactDuplicateDetector:
    def detect(...):
        return find_exact_groups(...)

Kryterium

Pipeline zna detektory, a nie szczegóły hashy/LSH/fuzzy.


10. Etap 6 — refaktoryzacja skanera

Pliki

src/redup/core/scanner/__init__.py
src/redup/core/scanner_filters.py
src/redup/core/scanner_loader.py
src/redup/core/scanner_utils.py
src/redup/core/scanner_types.py
src/redup/core/ts_extractor/

Problem

Skaner ma kilka trybów i dużo odpowiedzialności:

- zbieranie plików,
- filtrowanie,
- czytanie,
- cache,
- ekstrakcja bloków,
- równoległość,
- fallbacki językowe.

Docelowy podział

scanner/
  filters.py       # include/exclude/test files/target files
  loader.py        # read file, mmap, memory cache
  extractor.py     # source -> CodeBlock[]
  strategies.py    # simple, parallel, memory_optimized
  service.py       # scan_project()

Najważniejsza zasada

scan_project() powinno tylko orkiestrwać:

files = collect_files(config)
sources = load_sources(files, strategy)
blocks = extract_blocks(sources, config)
return ScannedProject(files, blocks, stats)

Kryterium

Ekstrakcja komentarzy intent nie powinna być częścią skanera. Skaner daje CodeBlock, a intent parser działa później.


11. Etap 7 — refaktoryzacja CodeBlock

Plik

src/redup/core/scanner_types.py

Problem

Do intentów potrzebujesz stabilnego identyfikatora i metadanych.

Dodać / ujednolicić

class CodeBlock(BaseModel):
    file_path: str
    start_line: int
    end_line: int
    content: str

    language: str | None = None
    block_type: Literal["line", "block", "function", "method", "class", "file"] = "block"
    name: str | None = None
    class_name: str | None = None

    @property
    def stable_id(self) -> str:
        ...

Kryterium

Każdy detektor dostaje ten sam format wejściowy.


12. Etap 8 — wdrożenie Intent DSL / RIDL

Nowy katalog

src/redup/core/intent/
  __init__.py
  schema.py
  ridl.py
  comments.py
  parser.py
  normalizer.py
  signature.py
  scoring.py
  detector.py
  grouping.py

Wcześniejszy plan też wskazywał osobny pakiet core/intent z odpowiedzialnościami: ekstrakcja komentarzy, parsowanie intencji, normalizacja, podpis intencji i detekcja podobieństw.

Obsługiwany format

# @intent parse:extensions !p2 @cli in:raw out:list fx:none #config scope:function

Parser

def parse_intent_tag_line(line: str) -> IntentDescriptor | None:
    ...

Sygnatura

def build_intent_signature(record: IntentRecord) -> IntentSignature:
    ...

Scoring

def score_intent_similarity(a: IntentSignature, b: IntentSignature) -> tuple[float, dict]:
    ...

Detector

class IntentDuplicateDetector:
    def detect(self, blocks: list[CodeBlock], config: ScanConfig) -> list[DuplicateGroup]:
        ...

Ważne

Domyślnie tylko:

intent_source = "explicit"

czyli analizujemy tylko @intent, nie każdy komentarz.


13. Etap 9 — grupowanie i evidence

Pliki

src/redup/core/intent/grouping.py
src/redup/core/pipeline/groups.py

Problem

Intent duplicate nie jest tym samym co exact/structural duplicate.

Zasada

Nie scalać automatycznie:

INTENT + EXACT
INTENT + STRUCTURAL
INTENT + NEAR
INTENT + SEMANTIC

Dodać Union-Find dla intent pairs

def group_intent_duplicate_pairs(pairs: list[IntentDuplicatePair]) -> list[set[str]]:
    ...

DuplicateGroup dla intent

DuplicateGroup(
    duplicate_type=DuplicateType.INTENT,
    similarity=avg_score,
    fragments=...,
    evidence=[
        DuplicateEvidence(
            kind="intent",
            score=avg_score,
            reason={...},
        )
    ],
)

14. Etap 10 — refaktoryzacja reporterów

Pliki

src/redup/reporters/json_reporter.py
src/redup/reporters/yaml_reporter.py
src/redup/reporters/markdown_reporter.py
src/redup/reporters/toon_reporter.py
src/redup/reporters/enhanced_reporter.py
src/redup/reporters/code2llm_reporter.py

Problem

Reportery mogą zawierać zbyt dużo logiki domenowej.

Docelowo

Wydzielić warstwę serializacji:

src/redup/reporters/serializers.py
src/redup/reporters/common.py
src/redup/reporters/evidence.py

Zasada

Reporter nie powinien obliczać logiki intentów. Ma tylko renderować:

group.evidence
group.metadata
group.fragments

JSON jako pierwszy

Najpierw rozszerzyć JSON, potem Markdown/TOON.

Przykład JSON

{
  "type": "intent",
  "similarity": 0.91,
  "evidence": [
    {
      "kind": "intent",
      "score": 0.91,
      "reason": {
        "action_score": 1.0,
        "object_score": 0.85,
        "domain_score": 1.0
      }
    }
  ]
}

15. Etap 11 — refaktoryzacja MCP

Pliki

src/redup/mcp/handlers.py
src/redup/mcp/schemas.py
src/redup/mcp/server.py
src/redup/mcp/utils.py

Problem

MCP nie powinien osobno interpretować reguł analizy. Powinien korzystać z tego samego ScanConfig i tego samego pipeline co CLI.

Zmiana

W _build_scan_config dodać:

intent_enabled=params.get("intent", False)
intent_threshold=params.get("intent_threshold", 0.84)
intent_source=params.get("intent_source", "explicit")

Schema

Dodać parametry:

{
  "intent": {
    "type": "boolean"
  },
  "intent_threshold": {
    "type": "number",
    "default": 0.84
  },
  "intent_source": {
    "type": "string",
    "enum": ["explicit", "comment", "heuristic"]
  }
}

Test

def test_analyze_project_accepts_intent_options():
    ...

16. Etap 12 — uporządkowanie refactor_advisor.py

Plik

src/redup/core/refactor_advisor.py

Problem

To jest warstwa LLM. Nie powinna być wymieszana z deterministycznymi regułami.

Docelowy podział

src/redup/core/planning/
  planner.py              # deterministic suggestions
  decision.py             # deterministic recommendations
  refactor_advisor.py     # LLM-only
  prompt_builder.py
  llm_response_parser.py

Zasada

Intent duplication nie wymaga LLM.

LLM może później tylko pomóc:

redup intent annotate --dry-run

ale nie powinien decydować, czy coś jest duplikatem.


17. Etap 13 — porządkowanie universal_fuzzy.py

Plik

src/redup/core/universal_fuzzy.py

Problem

To duży moduł i może być mieszanką:

- normalizacji,
- ekstrakcji sygnatur,
- scoringu,
- detekcji.

Refaktoryzacja

Podzielić na:

src/redup/core/fuzzy/
  signature.py
  extractor.py
  scoring.py
  detector.py

Uwaga

Nie robić tego w tym samym commicie co intent. Najpierw intent, potem fuzzy cleanup.


18. Etap 14 — porządkowanie cache

Pliki

src/redup/core/cache.py
src/redup/core/hash_cache.py
src/redup/core/scanner_cache.py

Problem

Są różne cache:

HashCache
MemoryFileCache
scanner cache
hash cache

Docelowo

src/redup/core/cache/
  file_cache.py
  hash_cache.py
  scan_cache.py
  intent_cache.py

Dla intentów

Cache powinien mieć:

file_hash
comment_hash
intent_signature_hash

Nie w MVP

Cache dla intentów dopiero po działającym detektorze.


19. Etap 15 — testy kontraktowe

Obecne testy

Masz testy dla:

CLI
compare
e2e
hasher
matcher
models
pipeline
planner
quality
reporters
scanner
ts_extractor
mcp

Dodać

tests/test_intent_schema.py
tests/test_intent_ridl.py
tests/test_intent_comments.py
tests/test_intent_parser.py
tests/test_intent_normalizer.py
tests/test_intent_signature.py
tests/test_intent_scoring.py
tests/test_intent_detector.py
tests/test_intent_grouping.py
tests/test_intent_pipeline.py

Fixtures

tests/fixtures/intent/
  explicit_same_intent/
  explicit_different_intent/
  different_code_same_intent/
  similar_code_different_intent/
  comments_without_intent/
  class_level_intent/
  file_level_intent/
  project_level_intent/

Najważniejsze testy

1. @intent parse:extensions jest wykrywany.
2. check → validate jest normalizowane.
3. read → parse jest normalizowane.
4. różny kod + ta sama intencja daje INTENT duplicate.
5. podobny kod + różna intencja nie daje INTENT duplicate.
6. komentarze bez @intent są ignorowane w explicit mode.
7. intent group nie scala się z exact group.
8. JSON reporter pokazuje evidence.
9. CLI --intent działa.
10. MCP przyjmuje intent=true.

20. Etap 16 — kalibracja progów

Problem

Próg 0.84 nie może być przypadkowy.

Dodać skrypt

scripts/calibrate-intent-threshold.py

Progi startowe

explicit: 0.82–0.84
comment: 0.88
heuristic: 0.92

Dlaczego?

Im mniej formalne źródło intencji, tym wyższy próg.

@intent DSL      → wysoka wiarygodność
zwykły komentarz → średnia wiarygodność
heurystyka nazw  → niższa wiarygodność

21. Etap 17 — dokumentacja

Pliki

README.md
docs/architecture.md
docs/intent-dsl.md
docs/refactoring-plan.md
SUMD.md

Dodać dokument

docs/intent-dsl.md

Zawartość:

1. Czym jest RIDL.
2. Składnia @intent.
3. Priorytety !p1–!p5.
4. Scope: line/block/function/class/file/project.
5. Przykłady dla Pythona, JS, C#, SQL, HTML.
6. Jak reDUP liczy similarity.
7. Ograniczenia.

Przykład

# @intent build:scan_config !p1 @scanner in:path,params out:ScanConfig fx:none #config scope:function

22. Etap 18 — release plan

Wersjonowanie

Obecnie projekt ma wersję 0.4.29. Proponuję:

0.4.30 — refactor models/config baseline
0.4.31 — pipeline detector registry
0.4.32 — intent DSL parser
0.4.33 — intent duplicate detector
0.4.34 — CLI + JSON reporter
0.4.35 — MCP + docs
0.5.0  — stable intent duplication feature

Changelog

Added
- Intent duplicate detection
- @intent DSL
- DuplicateEvidence model

Changed
- Pipeline supports detector registry
- Reporters include evidence

Fixed
- Reduced coupling between CLI and core analysis

23. Proponowana kolejność commitów

Commit 1

refactor(models): add duplicate evidence metadata

Pliki:

src/redup/core/models.py
tests/test_models.py

Commit 2

refactor(config): centralize intent scan options

Pliki:

src/redup/core/models.py
src/redup/core/config.py
src/redup/cli_app/config_builder.py

Commit 3

refactor(pipeline): isolate duplicate detection phases

Pliki:

src/redup/core/pipeline/duplicate_finder.py
src/redup/core/pipeline/groups.py
tests/test_pipeline.py

Commit 4

feat(intent): add RIDL schema and parser

Pliki:

src/redup/core/intent/__init__.py
src/redup/core/intent/schema.py
src/redup/core/intent/ridl.py
tests/test_intent_ridl.py

Commit 5

feat(intent): add intent normalization and signatures

Pliki:

src/redup/core/intent/normalizer.py
src/redup/core/intent/signature.py
tests/test_intent_normalizer.py
tests/test_intent_signature.py

Commit 6

feat(intent): add scoring and duplicate detector

Pliki:

src/redup/core/intent/scoring.py
src/redup/core/intent/detector.py
src/redup/core/intent/grouping.py
tests/test_intent_scoring.py
tests/test_intent_detector.py

Commit 7

feat(pipeline): include intent duplicate phase

Pliki:

src/redup/core/pipeline/duplicate_finder.py
tests/test_intent_pipeline.py

Commit 8

feat(cli): expose intent detection options

Pliki:

src/redup/cli_app/main.py
src/redup/cli_app/scan_commands.py
src/redup/cli_app/config_builder.py
tests/test_e2e.py

Commit 9

feat(reporters): render intent evidence

Pliki:

src/redup/reporters/json_reporter.py
src/redup/reporters/markdown_reporter.py
src/redup/reporters/toon_reporter.py
tests/test_reporters.py

Commit 10

feat(mcp): support intent analysis parameters

Pliki:

src/redup/mcp/handlers.py
src/redup/mcp/schemas.py
tests/test_mcp_server.py

Commit 11

docs(intent): document RIDL and intent duplication

Pliki:

README.md
docs/intent-dsl.md
SUMD.md

24. Plan refaktoryzacji według priorytetu

Priorytet P1 — konieczne

1. DuplicateEvidence.
2. DuplicateType.INTENT.
3. Intent DSL parser.
4. Intent signature.
5. Intent scoring.
6. Intent detector.
7. Pipeline integration.
8. JSON output.
9. CLI --intent.
10. Testy e2e.

Priorytet P2 — ważne

1. Detector registry.
2. MCP integration.
3. Markdown/TOON output.
4. Config file support.
5. Documentation.
6. Threshold calibration.

Priorytet P3 — później

1. Heuristic intent mode.
2. File-level/project-level inheritance.
3. Intent cache.
4. LLM-assisted annotation.
5. RDF/ontology export.
6. Full cleanup universal_fuzzy.
7. Full scanner split.

25. Minimalny MVP

MVP powinien być bardzo konkretny:

Użytkownik dodaje w kodzie:

# @intent parse:extensions !p2 @cli in:raw out:list #config

Uruchamia:

python -m redup scan ./src --intent --format json

Dostaje:

DuplicateType.INTENT z evidence i similarity.

Bez MVP:

- brak LLM,
- brak heurystyki,
- brak automatycznego generowania tagów,
- brak RDF/OWL,
- brak przebudowy całego skanera.

26. Kryteria ukończenia całej refaktoryzacji

Projekt można uznać za zrefaktoryzowany, gdy:

pytest -q
ruff check .
python -m redup scan ./src --format json
python -m redup scan ./src --intent --format json
python -m redup check ./src

przechodzą poprawnie, a dodatkowo:

1. Pipeline obsługuje intent jako osobny detektor.
2. CLI i MCP używają tego samego ScanConfig.
3. JSON/Markdown/TOON pokazują evidence.
4. Intent duplicate nie scala się automatycznie z exact/structural.
5. Testy fixtures wykrywają różny kod o tej samej intencji.
6. Zwykłe komentarze bez @intent nie tworzą false positive w explicit mode.
7. Dokumentacja opisuje @intent DSL i przykłady.

27. Najważniejsza rekomendacja

Nie zaczynałbym od wielkiej przebudowy całego projektu. Najbezpieczniejsza ścieżka to:

1. Najpierw evidence + config.
2. Potem osobny moduł intent.
3. Potem pipeline.
4. Potem CLI/reportery.
5. Dopiero potem większe porządkowanie scanner/fuzzy/MCP.

Czyli refaktoryzacja ma iść od stabilnego jądra do interfejsów, a nie odwrotnie. Dzięki temu nie rozwalisz działającego reDUP, a jednocześnie przygotujesz projekt pod dowolnie złożone intencje: od pojedynczej linii, przez funkcję i klasę, aż po cały projekt.