feat: initial commit

This commit is contained in:
2026-08-29 13:17:59 +02:00
commit 142f5f5759
91 changed files with 6155 additions and 0 deletions
+382
View File
@@ -0,0 +1,382 @@
# Taskfile dla agentic-codemod-pipeline (https://taskfile.dev)
#
# Instalacja go-task:
# macOS: brew install go-task
# pip: pip install go-task-bin
# linux: sh -c "$(curl -sSL https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
#
# Wszystkie zadania pracują w lokalnym .venv. Nic nie jest instalowane do systemowego
# Pythona - na macOS z Homebrew kończyłoby się to błędem "externally-managed-environment"
# (PEP 668), a w najlepszym razie zaśmieceniem interpretera systemowego.
version: "3"
silent: true
# Lokalna konfiguracja developerska. Plik jest opcjonalny i nie trafia do repozytorium
# (.gitignore). Zmienne wyeksportowane w powłoce mają pierwszeństwo przed .env,
# więc `eval "$(task llm:env)"` zawsze wygrywa z tym, co tu wpisano.
dotenv: [".env"]
vars:
VENV: .venv
BIN: "{{.VENV}}/bin"
PY: "{{.VENV}}/bin/python"
CLI: "{{.VENV}}/bin/agentic-codemod"
FIXTURE: examples/fixtures/acme-app
# Interpreter bazowy do utworzenia venva. Na macOS z systemowym Pythonem 3.9:
# PYTHON=python3.12 task install
PYTHON: '{{.PYTHON | default "python3"}}'
# Backend LLM: auto (wykrycie platformy) | vllm-gpu | vllm-metal | ollama | mock
LLM_BACKEND: '{{.LLM_BACKEND | default "auto"}}'
LLM_RUN_DIR: .runs/llm
COMPOSE: docker compose -f llm/docker-compose.yml
BACKEND_PY: "{{.VENV}}/bin/python llm/scripts/backend.py"
env:
PYTHONDONTWRITEBYTECODE: "1"
tasks:
default:
desc: Lista dostępnych zadań
cmds:
- task --list
# ------------------------------------------------------------- środowisko
doctor:
desc: Diagnostyka środowiska - uruchom to najpierw, gdy coś nie działa
cmds:
- |
printf 'system : %s %s\n' "$(uname -s)" "$(uname -m)"
printf 'interpreter bazowy: %s -> %s\n' "{{.PYTHON}}" "$({{.PYTHON}} -V 2>&1 || echo BRAK)"
printf 'venv : %s\n' "$(test -x {{.PY}} && {{.PY}} -V 2>&1 || echo 'brak (uruchom: task install)')"
printf 'uv : %s\n' "$(command -v uv >/dev/null 2>&1 && uv --version || echo 'brak (opcjonalny, przyspiesza instalację)')"
printf 'git : %s\n' "$(git --version 2>/dev/null || echo BRAK)"
printf 'ripgrep : %s\n' "$(rg --version 2>/dev/null | head -1 || echo 'brak (opcjonalny, używany przez skille)')"
printf 'apm : %s\n' "$(apm --version 2>/dev/null || echo 'brak (potrzebny do apm install)')"
printf 'agno : %s\n' "$(test -x {{.PY}} && {{.PY}} -c 'import agno; print(agno.__version__)' 2>/dev/null || echo 'brak')"
venv:
internal: true
preconditions:
- sh: command -v {{.PYTHON}} >/dev/null 2>&1
msg: |
Nie znaleziono interpretera "{{.PYTHON}}".
Na macOS: brew install python@3.12, a potem PYTHON=python3.12 task install
- sh: '{{.PYTHON}} -c "import sys; sys.exit(0 if sys.version_info >= (3, 10) else 1)"'
msg: |
Wymagany Python 3.10 lub nowszy ({{.PYTHON}} jest starszy).
macOS ma w systemie 3.9 - zainstaluj nowszy i wskaż go:
brew install python@3.12 && PYTHON=python3.12 task install
status:
- test -x {{.PY}}
cmds:
- |
# Niekompletny venv (przerwana instalacja, skasowany interpreter) jest gorszy
# niż jego brak - narzędzia zgłaszają wtedy mylące błędy. Odtwarzamy go od zera.
if [ -d "{{.VENV}}" ] && [ ! -x "{{.PY}}" ]; then
echo "usuwam niekompletny {{.VENV}}"
rm -rf "{{.VENV}}"
fi
if command -v uv >/dev/null 2>&1; then
uv venv --python {{.PYTHON}} {{.VENV}}
else
{{.PYTHON}} -m venv {{.VENV}}
fi
install:
desc: Tworzy .venv i instaluje zależności (runtime + dev)
deps: [venv]
status:
- test -f {{.VENV}}/.install-stamp
- test {{.VENV}}/.install-stamp -nt pyproject.toml
cmds:
- |
if command -v uv >/dev/null 2>&1; then
uv pip install --python {{.PY}} -e ".[dev]"
else
{{.PY}} -m pip install --quiet --upgrade pip
{{.PY}} -m pip install -e ".[dev]"
fi
- touch {{.VENV}}/.install-stamp
- 'echo "gotowe: {{.CLI}}"'
context:
desc: Instaluje i audytuje kontekst agentowy (APM), potem go wypisuje
deps: [install]
preconditions:
- sh: command -v apm >/dev/null 2>&1
msg: |
Brak APM CLI. Instalacja:
macOS: brew install apm (albo pip install apm-cli)
inne: curl -sSL https://aka.ms/apm-unix | sh
Repozytorium działa też bez APM CLI - wtedy używany jest wyłącznie
lokalny kontekst z .apm/ (bez zależności zewnętrznych).
cmds:
- apm install
- apm audit
- "{{.CLI}} context"
context:show:
desc: Wypisuje kontekst agentowy widziany przez silnik (bez apm install)
deps: [install]
cmds:
- "{{.CLI}} context"
# ------------------------------------------------------------------ jakość
lint:
desc: Statyczna analiza i sprawdzenie formatowania
deps: [install]
cmds:
- "{{.BIN}}/ruff check src tests llm"
- "{{.BIN}}/ruff format --check src tests llm"
fmt:
desc: Automatyczna poprawa formatowania i prostych błędów
deps: [install]
cmds:
- "{{.BIN}}/ruff check --fix src tests llm"
- "{{.BIN}}/ruff format src tests llm"
test:
desc: Testy jednostkowe i integracyjne (bez modelu językowego)
deps: [install]
cmds:
- "{{.BIN}}/pytest -q {{.CLI_ARGS}}"
test:cov:
desc: Testy z raportem pokrycia
deps: [install]
cmds:
- "{{.BIN}}/pytest -q --cov=agentic_codemod --cov-report=term-missing"
ci:
desc: To, co sprawdza pipeline - uruchom przed pushem
cmds:
- task: lint
- task: test
- task: demo
# ------------------------------------------------------------- uruchomienia
demo:
desc: Pełny przebieg offline na fixture (bez LLM, bez sieci)
deps: [install]
vars:
RUN_DIR: '{{.RUN_DIR | default ".runs/demo"}}'
cmds:
- |
{{.CLI}} run \
--repo {{.FIXTURE}} \
--package acme-sdk --module acme --to-version 2.1.0 \
--offline --run-dir "{{.RUN_DIR}}"
- 'echo "diff: {{.RUN_DIR}}/changes.patch | manifest: {{.RUN_DIR}}/run.json"'
plan:
desc: 'Sam plan zmian, bez modyfikacji kodu. Użycie: task plan -- --repo ../repo --package acme-sdk --to-version 2.1.0'
deps: [install]
cmds:
- "{{.CLI}} run --plan-only --run-dir .runs/plan {{.CLI_ARGS}}"
run:
desc: 'Przebieg z modelem. Użycie: task run -- --repo ../repo --package acme-sdk --to-version 2.1.0'
deps: [install]
preconditions:
- sh: test -n "$CODEMOD_LLM_BASE_URL"
msg: |
Nie ustawiono CODEMOD_LLM_BASE_URL. Skopiuj .env.example do .env i wyeksportuj zmienne,
albo uruchom przebieg deterministyczny: task demo
cmds:
- "{{.CLI}} run --run-dir .runs/local {{.CLI_ARGS}}"
# ------------------------------------------------------------------- LLM
# Kontrakt jest jeden: endpoint zgodny z OpenAI pod CODEMOD_LLM_BASE_URL.
# Backendy różnią się wyłącznie tym, jak ten endpoint powstaje.
# Konfiguracja: llm/models.yml. Wybór ręczny: LLM_BACKEND=ollama task llm:up
llm:list:
desc: Dostępne backendy LLM i który pasuje do tej maszyny
deps: [install]
cmds:
- "{{.BACKEND_PY}} list"
llm:env:
desc: 'Zmienne środowiskowe dla wybranego backendu. Użycie: eval "$(task llm:env)"'
deps: [install]
cmds:
- "{{.BACKEND_PY}} env --backend {{.LLM_BACKEND}}"
llm:up:
desc: Uruchamia backend LLM (autodetekcja platformy) i czeka na gotowość endpointu
deps: [install]
cmds:
- mkdir -p {{.LLM_RUN_DIR}}
- |
set -e
BACKEND="$({{.BACKEND_PY}} detect --requested {{.LLM_BACKEND}})"
MODEL="$({{.BACKEND_PY}} get serve.model --backend "$BACKEND")"
BASE_URL="$({{.BACKEND_PY}} get base_url --backend "$BACKEND")"
echo "backend: $BACKEND | model: $MODEL | endpoint: $BASE_URL"
case "$BACKEND" in
vllm-gpu)
command -v docker >/dev/null || { echo "brak dockera - zainstaluj albo: LLM_BACKEND=ollama task llm:up" >&2; exit 1; }
LLM_SERVE_MODEL="$MODEL" {{.COMPOSE}} --profile vllm-gpu up -d
LOG=""
;;
vllm-metal)
LOG="{{.LLM_RUN_DIR}}/vllm-metal.log"
llm/scripts/vllm-metal.sh up "$MODEL" 8000 "$LOG" "{{.LLM_RUN_DIR}}/vllm-metal.pid"
;;
ollama)
if command -v ollama >/dev/null 2>&1; then
# macOS: natywna Ollama korzysta z Metala; w kontenerze liczyłaby na CPU
curl -fsS --max-time 3 http://localhost:11434/api/version >/dev/null 2>&1 || {
{{.PY}} llm/scripts/daemonize.py --pidfile "{{.LLM_RUN_DIR}}/ollama.pid" \
--log "{{.LLM_RUN_DIR}}/ollama.log" -- ollama serve
sleep 2
}
ollama pull "$MODEL"
else
{{.COMPOSE}} --profile ollama up -d
{{.COMPOSE}} exec -T ollama ollama pull "$MODEL"
fi
LOG="{{.LLM_RUN_DIR}}/ollama.log"
;;
mock)
LOG="{{.LLM_RUN_DIR}}/mock.log"
{{.PY}} llm/scripts/daemonize.py --pidfile "{{.LLM_RUN_DIR}}/mock.pid" --log "$LOG" -- \
{{.PY}} llm/mock_server.py --port 8077
;;
*)
echo "nieznany backend: $BACKEND" >&2; exit 1
;;
esac
echo "podgląd logów w drugim oknie: task llm:logs"
llm/scripts/wait-for-endpoint.sh "$BASE_URL" "${LLM_WAIT_TIMEOUT:-900}" "$LOG"
echo
echo "gotowe. Wyeksportuj zmienne do swojej powłoki:"
echo ' eval "$(task llm:env)"'
llm:down:
desc: Zatrzymuje backend LLM
deps: [install]
cmds:
- |
BACKEND="$({{.BACKEND_PY}} detect --requested {{.LLM_BACKEND}})"
case "$BACKEND" in
vllm-gpu) {{.COMPOSE}} --profile vllm-gpu down ;;
ollama)
if [ -f "{{.LLM_RUN_DIR}}/ollama.pid" ]; then
{{.PY}} llm/scripts/daemonize.py --pidfile "{{.LLM_RUN_DIR}}/ollama.pid" --log /dev/null --stop
else
{{.COMPOSE}} --profile ollama down
fi
;;
vllm-metal) llm/scripts/vllm-metal.sh down "{{.LLM_RUN_DIR}}/vllm-metal.pid" ;;
mock)
{{.PY}} llm/scripts/daemonize.py --pidfile "{{.LLM_RUN_DIR}}/mock.pid" --log /dev/null --stop
;;
esac
llm:status:
desc: Czy endpoint LLM odpowiada i jakie modele wystawia
deps: [install]
cmds:
- |
BASE_URL="$({{.BACKEND_PY}} get base_url --backend {{.LLM_BACKEND}})"
echo "endpoint: $BASE_URL"
curl -fsS --max-time 5 "$BASE_URL/models" || { echo "brak odpowiedzi - uruchom: task llm:up" >&2; exit 1; }
llm:smoke:
desc: Jedno zapytanie do endpointu - sprawdza, czy model w ogóle odpowiada
deps: [install]
cmds:
- |
BASE_URL="$({{.BACKEND_PY}} get base_url --backend {{.LLM_BACKEND}})"
MODEL="$({{.BACKEND_PY}} get serve.model --backend {{.LLM_BACKEND}})"
curl -fsS --max-time 120 "$BASE_URL/chat/completions" \
-H 'Content-Type: application/json' \
-d "{\"model\":\"$MODEL\",\"messages\":[{\"role\":\"user\",\"content\":\"Odpowiedz jednym slowem: dziala\"}],\"max_tokens\":16}"
echo
llm:logs:
desc: 'Logi backendu LLM na żywo. Warianty: TAIL=500, FOLLOW=false, GREP=wzorzec'
deps: [install]
vars:
TAIL: '{{.TAIL | default "100"}}'
FOLLOW: '{{.FOLLOW | default "true"}}'
GREP: '{{.GREP | default ""}}'
cmds:
- |
BACKEND="$({{.BACKEND_PY}} detect --requested {{.LLM_BACKEND}})"
llm/scripts/logs.sh \
--backend "$BACKEND" \
--run-dir "{{.LLM_RUN_DIR}}" \
--tail "{{.TAIL}}" \
{{if eq .FOLLOW "false"}}--no-follow{{else}}--follow{{end}} \
{{if .GREP}}--grep "{{.GREP}}"{{end}}
llm:logs:errors:
desc: Same błędy i ostrzeżenia z logów backendu (jednorazowy zrzut)
deps: [install]
vars:
TAIL: '{{.TAIL | default "2000"}}'
cmds:
- |
BACKEND="$({{.BACKEND_PY}} detect --requested {{.LLM_BACKEND}})"
llm/scripts/logs.sh --backend "$BACKEND" --run-dir "{{.LLM_RUN_DIR}}" \
--tail "{{.TAIL}}" --no-follow \
--grep "error|warning|traceback|exception|failed|out of memory|cuda|refused"
llm:logs:save:
desc: 'Zrzut logów do pliku, do dołączenia w zgłoszeniu. Domyślnie .runs/llm/dump.log'
deps: [install]
vars:
TAIL: '{{.TAIL | default "5000"}}'
OUT: '{{.OUT | default ".runs/llm/dump.log"}}'
cmds:
- |
BACKEND="$({{.BACKEND_PY}} detect --requested {{.LLM_BACKEND}})"
llm/scripts/logs.sh --backend "$BACKEND" --run-dir "{{.LLM_RUN_DIR}}" \
--tail "{{.TAIL}}" --save "{{.OUT}}"
llm:mock:
desc: Atrapa serwera OpenAI na pierwszym planie (Ctrl-C kończy)
deps: [install]
cmds:
- "{{.PY}} llm/mock_server.py --port 8077 --verbose"
# ------------------------------------------------------- przebiegi z LLM
demo:llm:
desc: Przebieg agentowy na fixture z aktualnym backendem LLM (domyślnie atrapa - deterministyczny)
deps: [install]
vars:
RUN_DIR: '{{.RUN_DIR | default ".runs/demo-llm"}}'
cmds:
- task: llm:up
vars: {LLM_BACKEND: "{{.LLM_BACKEND}}"}
- |
eval "$({{.BACKEND_PY}} env --backend {{.LLM_BACKEND}})"
{{.CLI}} run \
--repo {{.FIXTURE}} \
--package acme-sdk --module acme --to-version 2.1.0 \
--run-dir "{{.RUN_DIR}}"
test:llm:
desc: Testy ścieżki agentowej na atrapie LLM (bez GPU i bez sieci)
deps: [install]
cmds:
- "{{.BIN}}/pytest -q tests/test_llm_path.py"
# -------------------------------------------------------------- sprzątanie
clean:
desc: Usuwa artefakty przebiegów i cache narzędzi
cmds:
- rm -rf .runs workspace .pytest_cache .ruff_cache .coverage htmlcov
- find . -type d -name __pycache__ -prune -exec rm -rf {} +
distclean:
desc: Jak clean, plus .venv i zainstalowany kontekst APM
deps: [clean]
cmds:
- rm -rf {{.VENV}} apm_modules *.egg-info src/*.egg-info