SLOPSHOPPER

straznik-petli

Po 3 takich samych błędach narzędzia z rzędu zatrzymuje Claude'a i pyta, co dalej

newguardtoastprompt
A shopper browsing a rack in a slop shop
README

Radek Wajbkoduje — darmowe rzeczy z kanału

Tu leży wszystko, co rozdaję na kanale. Za darmo, bez zapisywania się na cokolwiek.

Jak pobrać: kliknij zielony przycisk Code u góry, potem Download ZIP. Rozpakuj na pulpit. Nie musisz wiedzieć, co to GitHub — wystarczy ten jeden przycisk.


⚠️ Zanim cokolwiek zainstalujesz — sprawdź to

Skill to instrukcja, którą AI wykona. W czymkolwiek ściągniętym z internetu może być napisane wszystko. Sprawdzaj każdą paczkę, zanim ją wrzucisz. Moją też.

Paczka leży jeszcze w zwykłym folderze na pulpicie, więc otwórz nową rozmowę z AI w tym folderze i wklej:

Zanim tego użyję: przeczytaj te pliki jak zwykły tekst i powiedz mi po ludzku,
co ta instrukcja każe robić. Czy każe coś kasować, wysyłać gdzieś dane,
uruchamiać komendy albo grzebać poza swoim folderem?

Zasada kciuka: jeśli w instrukcji jest „wyślij", „skasuj" albo „uruchom" coś, czego nie rozumiesz — nie instalujesz. Nie musisz rozumieć wszystkiego. Wystarczy, że wiesz, kiedy powiedzieć „nie".

To nie jest kontrola pancerna, ale wyłapuje zdecydowaną większość śmieci. Różnica jak między wzięciem cukierka od obcego a przeczytaniem składu.


Co tu jest

FolderCo to robiOdcinek
ghost/AI pisze maile i wiadomości twoim stylem, a nie jak robot„Nauczyłem AI pisać jak ja"
handoff/Kończysz rozmowę tak, żeby nowa wiedziała, na czym stanęliście — mniej zużytego limitu„Znowu masz limit"
krotko/Jedno słowo i z długiej odpowiedzi AI zostaje kilka zdań po ludzku„AI gada za dużo"
mody-limit/Trzech pomocników w Claude Code: pasek zużycia w kolorach, przycinanie gadatliwych wyników, stop dla AI kręcącego się w kółko(odcinek w przygotowaniu)

Każdy folder ma własny README.md z instrukcją krok po kroku.

Prywatność — dwie rzeczy, które obowiązują zawsze

  • To, co wklejasz do AI, leci na serwer firmy, która je robi. Nie wklejaj haseł, numerów kart ani cudzych danych.
  • Twoje pliki z tych paczek (np. profil stylu pisania) zostają u ciebie na dysku. Nigdzie ich nie wysyłasz i nikt ich nie widzi.

Coś nie działa?

Napisz w komentarzu pod odcinkiem — czytam wszystkie i z powtarzających się pytań robię następne filmy.

Source 1 files
hooks/register.ts 109 lines
1import type { EngineInterface, Register, ToolCallResult } from 'claude-code'
2
3// Tyle takich samych błędów z rzędu i Strażnik pyta użytkownika.
4const LIMIT = 3
5
6const STOP = 'Stop, niech wyjaśni'
7const AGAIN = 'Niech próbuje dalej'
8
9// Błąd → ile razy z rzędu. Klucz: kto (główna rozmowa / subagent) + narzędzie + treść błędu.
10let failures = new Map<string, number>()
11// Błędy, przy których użytkownik kazał próbować dalej — do jego następnej wiadomości.
12let allowed = new Set<string>()
13// Błędy, przy których użytkownik kazał przestać — kolejna taka próba nie rusza.
14let stopped = new Set<string>()
15
16const reset = () => {
17  failures = new Map()
18  allowed = new Set()
19  stopped = new Set()
20}
21
22// Ten sam błąd mimo innych liczb (czas, numer linii, id) liczy się jako ten sam.
23export const signature = (who: string, tool: string, error: string) =>
24  `${who}|${tool}|${error.toLowerCase().replace(/\d+/g, '#').replace(/\s+/g, ' ').trim().slice(0, 300)}`
25
26export const register: Register = on => {
27  on('session.start', async ($, e, next) => {
28    reset()
29    return next(e)
30  })
31
32  // Nowa wiadomość od użytkownika: zaczynamy liczyć od zera.
33  on('prompt.submit', async ($, e, next) => {
34    reset()
35    return next(e)
36  })
37
38  on('tool.call', async ($, e, next) => {
39    if (e.tool === 'AskUserQuestion') {
40      return next(e)
41    }
42
43    const who = e.agentId ?? 'main'
44    const ran = await next(e)
45    if (ran.deny !== undefined) {
46      return ran
47    }
48
49    // Udane wywołanie tego narzędzia przerywa serię jego błędów.
50    if (ran.isError !== true) {
51      for (const key of failures.keys()) {
52        if (key.startsWith(`${who}|${e.tool}|`)) failures.delete(key)
53      }
54      return ran
55    }
56
57    const key = signature(who, e.tool, ran.text ?? String(ran.result ?? ''))
58    if (allowed.has(key)) {
59      return ran
60    }
61    if (stopped.has(key)) {
62      return withNote(ran, 'Użytkownik już kazał przestać przy tym błędzie. Nie próbuj ponownie — napisz mu, co się dzieje, i czekaj na odpowiedź.')
63    }
64
65    const count = (failures.get(key) ?? 0) + 1
66    failures.set(key, count)
67    if (count < LIMIT) {
68      return ran
69    }
70
71    failures.delete(key)
72    return ask($, e.tool, who, key, ran)
73  })
74}
75
76async function ask($: EngineInterface, tool: string, who: string, key: string, ran: ToolCallResult): Promise<ToolCallResult> {
77  const error = (ran.text ?? '').replace(/\s+/g, ' ').trim().slice(0, 160)
78  const agent = who === 'main' ? 'Claude' : 'Subagent Claude’a'
79  const question = `${agent} ${LIMIT} razy z rzędu dostał ten sam błąd w ${tool}: „${error}”. Co robimy?`
80
81  let answer: string
82  try {
83    answer = await $.ui.ask(question, { options: [STOP, AGAIN], header: 'Pętla' })
84  } catch (err) {
85    // Nie było kogo zapytać albo okno zamknięto: bezpieczniej stanąć.
86    $.ui.log(`straznik-petli: okienko pytania nie wyszło: ${String(err).slice(0, 300)}`)
87    stopped.add(key)
88    $.ui.toast(`Strażnik pętli zatrzymał Claude'a: ten sam błąd ${LIMIT} razy w ${tool}.`, { timeoutMs: 15000 })
89    return withNote(ran, `Strażnik pętli: ten sam błąd padł ${LIMIT} razy z rzędu (nie dało się zapytać użytkownika). Nie próbuj ponownie. Napisz mu krótko, prostym językiem: co chciałeś zrobić, co próbowałeś i co Twoim zdaniem jest przyczyną — potem zapytaj, co dalej.`)
90  }
91
92  if (answer === AGAIN) {
93    allowed.add(key)
94    return withNote(ran, `Strażnik pętli: ten sam błąd padł ${LIMIT} razy, ale użytkownik pozwolił próbować dalej. Zmień podejście, zamiast powtarzać to samo.`)
95  }
96  if (answer === STOP) {
97    stopped.add(key)
98    return withNote(ran, `Strażnik pętli: ten sam błąd padł ${LIMIT} razy z rzędu i użytkownik kazał przestać. Nie próbuj ponownie. Napisz mu krótko, prostym językiem: co chciałeś zrobić, co próbowałeś i co Twoim zdaniem jest przyczyną — potem zapytaj, co dalej.`)
99  }
100
101  // Użytkownik wpisał własną odpowiedź w „Other”.
102  allowed.add(key)
103  return withNote(ran, `Strażnik pętli: ten sam błąd padł ${LIMIT} razy z rzędu. Użytkownik odpowiedział: „${answer}”. Postępuj według tego.`)
104}
105
106// Notka trafia do Claude'a zaraz po wyniku narzędzia (użytkownik jej nie widzi).
107const withNote = (ran: ToolCallResult, note: string): ToolCallResult =>
108  ran.deny !== undefined ? ran : { ...ran, context: [...(ran.context ?? []), note] }
109