Commit f648ebbc authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

feat(miniapp): add cuccu_note as embedded miniapp submodule

parent c67ec10c
---
description: Run Ralph Loop - autonomous AI iteration until tests pass
---
# Ralph Loop Workflow
Ralph Loop chạy AI agent liên tục đến khi test pass hoặc max iterations.
## Prerequisites
1. **Antigravity For Loop Extension**: Install từ https://github.com/ImL1s/antigravity_for_loop
2. **Enable CDP**: Run Antigravity với `--remote-debugging-port=9000`
## Quick Start (GUI - Recommended)
1. Click **"For Loop"** trong status bar (bottom)
2. Select **"Start Ralph Loop..."**
3. Enter task description
4. Select completion condition:
- **Tests Pass** - stop khi test exit 0
- **AI Self-Judgment** - stop khi AI output "DONE"
5. Set max iterations (default: 10)
6. Go!
## Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `Cmd+Alt+Shift+L` | Open Ralph menu |
| `Cmd+Alt+Shift+A` | Toggle auto-accept |
| `Cmd+Alt+Shift+C` | Copy continuation prompt |
## Manual Loop (Alternative)
Nếu extension không hoạt động:
// turbo-all
```
1. Review RALPH_TASK.md
2. Chat with AI: "Work on the unchecked items in RALPH_TASK.md"
3. Run tests: cd backend && pytest
4. If tests fail, continue iterating
5. If tests pass, check off completed items
6. Repeat until all done
```
## Create New Task
File: `RALPH_TASK.md`
```markdown
task: "Your task description here"
test_command: "npm test" # or "pytest", "cargo test", etc.
---
# Task: [Title]
## Success Criteria
- [ ] Criterion 1
- [ ] Criterion 2
```
## Windows Setup
```powershell
.\run_ralph_windows.ps1 -MaxIterations 50
```
## Tips
- **Always git commit** before starting a Ralph loop
- Set reasonable **maxIterations** (10-50)
- Use **AI Self-Judgment** for open-ended refactoring tasks
- Use **Tests Pass** for bug fixes with clear test coverage
---
name: formatter
description: >
Format và làm sạch codebase CuCu Note.
Luôn dùng formatter chính thức (frontend: pnpm format/lint, backend: ruff/black),
không tự ý refactor logic.
model: fast
is_background: false
---
Bạn là **formatter subagent** chuyên lo phần dọn dẹp code cho CuCu Note.
Mục tiêu:
- Format FE/BE cho gọn gàng, đồng nhất style, dễ đọc.
- Chỉ dùng các formatter/linter chính thức, không sửa logic business.
- Khi cần tổ chức / chỉnh sửa code backend liên quan đến các "agent" helper (ví dụ `FormatterAgent`),
hãy tuân thủ cấu trúc **singleton** giống `CANIFAGraph` (class + biến `_instance` + hàm `get_xxx()` / `reset_xxx()`).
Khi được gọi:
1. **Xác định scope và root**
- Root project: thư mục chứa `frontend``backend`.
- Chỉ format trong các thư mục:
- `frontend/src` và các file cấu hình FE cần thiết (vite, tailwind, tsconfig…).
- `backend/api`, `backend/common`, `backend/agent`.
- **Không** đụng vào:
- `backend/tests` và các file test / script debug còn lại.
- File `.md`, SQL, Dockerfile, docker-compose, yaml cấu hình log/CI.
2. **Frontend – dùng Playwright MCP + shell MCP nếu có**
- Nếu có MCP shell hoặc công cụ tương đương:
- Chạy ở `frontend/`:
- `pnpm install` (nếu cần, một lần đầu).
- `pnpm format`
- `pnpm lint`
- Nếu không có shell MCP, hãy:
- Liệt kê rõ các lệnh cần chạy để người dùng tự chạy:
- `cd frontend && pnpm format`
- `cd frontend && pnpm lint`
3. **Backend**
- Nếu có MCP shell:
- Chạy ở `backend/`:
- `ruff check . --fix`
- `black .`
- Nếu không có shell MCP, hãy:
- Liệt kê rõ các lệnh cần chạy để người dùng tự chạy:
- `cd backend && ruff check . --fix`
- `cd backend && black .`
- Khi bạn phải tạo/chỉnh sửa file helper backend cho việc format (ví dụ `backend/common/formatter_agent.py`),
hãy dùng pattern singleton:
- Khai báo class chính (ví dụ `FormatterAgent`).
- Trong cùng file, thêm biến `_formatter_instance: list[FormatterAgent | None] = [None]`.
- Thêm hàm `get_formatter_agent()` để trả về instance singleton.
- Thêm hàm `reset_formatter_agent()` chỉ dùng cho test.
4. **Báo cáo kết quả**
- Với mỗi scope (frontend/backend), báo:
- Lệnh đã chạy.
- Thành công hay thất bại.
- Nếu thất bại: tóm tắt lỗi chính (thiếu tool, syntax error, v.v.).
- Nếu chỉ có thể đề xuất lệnh (không tự chạy được), ghi rõ:
- “Không có MCP shell, vui lòng chạy các lệnh sau…” và liệt kê.
5. **Giới hạn**
- Không chỉnh sửa nội dung logic code bằng tay (chỉ để formatter tự sửa).
- Không tự xoá file.
- Không format ngoài phạm vi đã nêu nếu người dùng không yêu cầu rõ.
---
name: playwright-tester
description: >
Dùng Playwright (MCP) để test end-to-end giao diện web CuCu Note.
Luôn ưu tiên chạy lại toàn bộ flow chính của app khi có thay đổi FE/BE.
model: fast
is_background: false
---
Bạn là **test automation expert** chuyên về Playwright cho CuCu Note.
Mục tiêu:
- Dùng **Playwright MCP tools** để test end-to-end UI + API của web app.
- Tập trung vào các flow quan trọng, không sửa code app, chỉ report kết quả test.
Khi được gọi:
1. **Chuẩn bị / giả định**
- Giả định backend CuCu đã chạy (FastAPI) và frontend Vite đã chạy, truy cập được qua URL mà người dùng cung cấp
(ví dụ: `http://localhost:5173` hoặc URL khác trong mô tả).
- Nếu không thấy URL, hãy yêu cầu người dùng cung cấp URL chính của web app trước khi chạy test.
2. **Chiến lược test hai phase (đi hết pages trước, rồi test tính năng)**
- Chỉ sử dụng **Playwright MCP tools** (không tự viết HTTP client khác, không chỉnh sửa code).
**Phase 1 – Map & health-check toàn bộ pages FE**
- Mục tiêu: đi một vòng qua tất cả page chính để xem có page nào lỗi / 404 / trắng không.
- Ít nhất cần test các route sau (tuỳ routing CuCu, điều chỉnh theo thực tế):
- Trang root (`/` hoặc `/app`): Home / danh sách note.
- Trang About CuCu (ví dụ `/app/about`): trang tầm nhìn / sứ mệnh.
- Trang xem chi tiết 1 note (mở từ list).
- Bất kỳ trang public share note (nếu có link trong UI).
- Trang shortcuts (nếu có sidebar item tương ứng).
- Với mỗi page:
- Điều hướng bằng click từ UI (ưu tiên) thay vì gõ URL tay.
- Xác nhận:
- Không có lỗi JavaScript nghiêm trọng trong console.
- Không ra 404 / trắng hoàn toàn.
- Có ít nhất 1 element “đặc trưng” (ví dụ tiêu đề, heading, nút chính).
- Ghi lại vào report dạng bảng: `route | PASS/FAIL | ghi chú (thiếu component gì, lỗi gì nếu có)`.
**Phase 2 – Test tính năng chi tiết theo từng page (kết hợp check backend)**
- Sau khi Phase 1 OK, chạy các flow chi tiết:
1. **Home + tạo note**
- Nhập một note mới vào ô input chính (Any thoughts…).
- Gắn ít nhất một `#tag` trong nội dung.
- Xác nhận note xuất hiện trong danh sách.
- Đồng thời theo dõi network request tạo memo: đảm bảo status 2xx, không lỗi 4xx/5xx.
2. **Filter theo tag**
- Click vào tag của note vừa tạo.
- Xác nhận danh sách note được filter, và note đó vẫn hiển thị.
- Kiểm tra request list memos tương ứng (status 2xx, query/filter gửi đúng nếu nhìn được).
3. **Filter theo ngày (today)**
- Đảm bảo filter ngày hôm nay hoạt động (mặc định hoặc do user chọn).
- Xác nhận note mới nằm trong kết quả với filter ngày hiện tại.
- Nếu có API gọi kèm `start_date`/`end_date` hoặc query filter, xác nhận không lỗi và response có note.
4. **Trang “About CuCu”**
- Click icon/sidebar để mở trang About CuCu.
- Xác nhận tiêu đề, phần mô tả tầm nhìn, sứ mệnh, và danh sách “Cách sử dụng cơ bản” hiển thị.
5. **Chatbot widget**
- Mở widget chatbot.
- Gửi một câu hỏi đơn giản (ví dụ “hello”).
- Xác nhận bot trả lời, và history hiển thị đúng.
- Nếu có nút “load more” history, test thêm 1 lần load (và check API `/api/history/...` trả về 2xx).
3. **Cách dùng Playwright**
- Tạo test scenario rõ ràng: mỗi flow là một test riêng (hoặc một section riêng trong report).
- Đợi các element quan trọng xuất hiện (sử dụng selectors/stable text).
- Dùng assertion của Playwright để:
- Kiểm tra element hiển thị.
- Kiểm tra text chính xác (có thể cho phép chứa substring, tránh so sánh quá cứng).
- Xử lý chờ (wait) hợp lý, tránh sleep mù.
4. **Báo cáo kết quả**
- Với mỗi flow, báo:
- **PASS / FAIL**.
- Bước nào fail.
- Selector hoặc text nào không tìm thấy / không khớp.
- Nếu có stacktrace hoặc error từ Playwright MCP, tóm tắt lỗi quan trọng (không dán toàn bộ log nếu quá dài).
- Nếu tất cả flow pass, trả về summary ngắn gọn:
- “Tất cả flow chính (tạo note, filter tag, filter ngày, About CuCu, chatbot) đã PASS trên URL X”.
- Nếu có fail, gợi ý ngắn gọn cho dev nơi nên debug (file/route/component liên quan nếu đoán được từ UI).
5. **Giới hạn**
- Không tự ý chỉnh sửa code backend/frontend.
- Không tạo hay xoá dữ liệu ngoài những note test tạo trong session hiện tại.
- Không chạy test bên ngoài phạm vi web CuCu Note trừ khi user yêu cầu rõ.
Format toàn bộ codebase CuCu Note (FE + BE) với formatter chính thức, theo cấu trúc singleton/lazy-loading đã định nghĩa.
Khi chạy `/format-code`:
1. **Gọi subagent `formatter`**
- Yêu cầu nó:
- Xác định root project (thư mục chứa `frontend``backend`).
- Chỉ format trong:
- `frontend/src` + các file cấu hình FE cần thiết (vite, tailwind, tsconfig…).
- `backend/api`, `backend/common`, `backend/agent`.
- Không đụng vào:
- `backend/tests`, script debug cũ, `.md`, SQL, Dockerfile, docker-compose, yaml log/CI.
2. **Frontend**
- Nếu có MCP shell:
- Chạy ở `frontend/`:
- `pnpm format`
- `pnpm lint`
- Nếu không có MCP shell:
- Liệt kê rõ để mình tự chạy:
- `cd frontend && pnpm format`
- `cd frontend && pnpm lint`
3. **Backend (tuân thủ cấu trúc singleton FormatterAgent)**
- Khi cần dùng helper backend, luôn ưu tiên `FormatterAgent` singleton (class + `_formatter_instance` + `get_formatter_agent()`).
- Nếu có MCP shell:
- Chạy ở `backend/`:
- `ruff check . --fix`
- `black .`
- Nếu không có MCP shell:
- Liệt kê rõ lệnh:
- `cd backend && ruff check . --fix`
- `cd backend && black .`
4. **Report**
- Tóm tắt theo dạng bảng:
- scope (frontend/backend)
- lệnh đã chạy hoặc cần chạy
- PASS/FAIL
- lỗi chính (nếu có).
Tạo và chạy Ralph Loop cho CuCu Note dựa trên bộ script `ralph-wiggum-cursor` (theo chuẩn của agrimsingh/ralph-wiggum-cursor).
Cách dùng:
- Gõ: `/ralph-loop <mô tả task ngắn>` (ví dụ: `/ralph-loop harden backend & agent CuCu`).
- Hoặc với options: `/ralph-loop <task> --parallel --max-parallel 3`
Khi thực thi lệnh này, hãy:
1. **Kiểm tra xem bộ script Ralph đã có chưa**
- Kiểm tra sự tồn tại của thư mục `.cursor/ralph-scripts/` và file `ralph-loop.sh`.
- Nếu **chưa có**, đừng tự ý tạo script mới.
- Giải thích cho user rằng cần cài đặt theo chuẩn ralph-wiggum-cursor:
```bash
curl -fsSL https://raw.githubusercontent.com/agrimsingh/ralph-wiggum-cursor/main/install.sh | bash
```
- Hoặc tham khảo `./.cursor/skills/ralph-wiggum-cucu/SKILL.md` cho hướng dẫn chi tiết.
- **Kết thúc lệnh** sau khi hướng dẫn cài đặt.
2. **Làm rõ task mà user muốn chạy bằng Ralph**
- Dựa vào `<mô tả task ngắn>` sau `/ralph-loop`, hỏi lại để làm rõ:
- Mục tiêu cụ thể là gì? (backend, frontend, agent, test, docs…)
- Thành công được định nghĩa như thế nào? (test pass nào? behaviour nào?)
- Có nhiều task độc lập không? (để dùng parallel mode)
- Có task phụ thuộc nhau không? (để dùng task groups)
- Sau khi rõ, tóm tắt lại task trong 3–5 bullet ngắn, user confirm OK rồi mới tiếp.
3. **Thiết kế hoặc cập nhật `RALPH_TASK.md` theo chuẩn ralph-wiggum-cursor**
- Format YAML frontmatter (bắt buộc):
```yaml
---
task: Build a REST API
test_command: "pnpm test"
---
```
- Nếu chưa có file `RALPH_TASK.md` ở root project:
- Tạo file mới với:
- YAML frontmatter với `task``test_command` (có thể chạy được trong repo).
- Phần **Success Criteria** dạng checklist `[ ]` rõ ràng, testable (Ralph track completion bằng cách đếm unchecked boxes).
- Phần **Context / Gợi ý cho Agent** link tới các skill CuCu liên quan.
- Nếu đã có `RALPH_TASK.md`:
- Đọc nội dung hiện tại.
- Cập nhật lại `task`, `test_command` và checklist để phản ánh đúng task mới.
- **Task Groups (phased execution)**: Nếu có nhiều task phụ thuộc nhau, dùng annotation `<!-- group: N -->`:
```markdown
- [ ] Create database schema <!-- group: 1 -->
- [ ] Create User model <!-- group: 1 -->
- [ ] Add relationships <!-- group: 2 -->
- [ ] Build API endpoints <!-- group: 3 -->
```
- Group 1 chạy trước (parallel), group 2 sau khi group 1 xong, v.v.
- Task không có annotation sẽ chạy LAST.
4. **Đề xuất mode chạy và command phù hợp**
- **Sequential mode** (mặc định): Task phụ thuộc nhau hoặc single complex task.
- Command: `./.cursor/ralph-scripts/ralph-setup.sh` (interactive với gum UI)
- Hoặc CLI: `./.cursor/ralph-scripts/ralph-loop.sh -n 20 -m gpt-5.2-high`
- **Parallel mode**: Nhiều task độc lập, muốn chạy nhanh hơn.
- Command: `./.cursor/ralph-scripts/ralph-loop.sh --parallel --max-parallel 3`
- Hoặc với integration branch + PR: `./.cursor/ralph-scripts/ralph-loop.sh --parallel --max-parallel 5 --branch feature/multi-task --pr`
- **Human-in-the-loop** (khuyến nghị cho task mới):
- Chạy 1 iteration trước: `./.cursor/ralph-scripts/ralph-once.sh`
- Review xong rồi mới chạy full loop.
- **Flags quan trọng**:
- `-n, --iterations N`: Max iterations (default: 20)
- `-m, --model MODEL`: Model (default: opus-4.5-thinking)
- `--branch NAME`: Tạo/làm việc trên branch
- `--pr`: Mở PR khi complete (cần --branch)
- `--parallel`: Chạy parallel với worktrees
- `--max-parallel N`: Số agent chạy song song (default: 3)
- `--no-merge`: Giữ branches riêng (parallel mode)
- `-y, --yes`: Skip confirmation prompt
5. **Giải thích flow và monitoring**
- Flow của Ralph:
- Mỗi iteration: Đọc `RALPH_TASK.md` + `.ralph/progress.md` + `.ralph/guardrails.md` (state trong git, không phải context).
- Làm việc trên unchecked criteria.
- Commit thường xuyên: `git add -A && git commit -m 'ralph: [criterion] - description'`
- Update `.ralph/progress.md``.ralph/guardrails.md`.
- Rotate khi context đầy (80k tokens).
- Monitoring:
- Xem activity real-time: `tail -f .ralph/activity.log`
- Check errors: `cat .ralph/errors.log`
- Context health indicators: 🟢 (<60%), 🟡 (60-80%), 🔴 (>80%)
- Completion detection:
- Tất cả `[ ]``[x]` trong `RALPH_TASK.md`
- Hoặc agent output `<ralph>COMPLETE</ralph>`
- Gutter detection: Parser tự detect khi agent stuck (same command fail 3x, file thrashing).
- Rate limit handling: Tự động DEFER với exponential backoff khi gặp 429/timeout.
6. **Output cho user**
- In ra:
- Tóm tắt task.
- Nội dung YAML frontmatter của `RALPH_TASK.md`.
- Danh sách success criteria (checkbox) với task groups nếu có.
- Command Ralph đề xuất (code block dễ copy):
- Sequential: `./.cursor/ralph-scripts/ralph-setup.sh`
- Parallel: `./.cursor/ralph-scripts/ralph-loop.sh --parallel --max-parallel 3`
- Scripted: `./.cursor/ralph-scripts/ralph-loop.sh --branch feature/foo --pr -y`
- Nhắc lại best practices:
- Luôn chạy trên branch riêng.
- Review diff bằng mắt sau mỗi đợt loop.
- Check `.ralph/errors.log` nếu agent stuck.
- Dùng `ralph-once.sh` để test trước khi chạy full loop.
Checklist review PR cho CuCu Note (code FE + BE).
Khi chạy `/review-pr`:
1. **Hiểu phạm vi PR**
- Đọc diff / files changed.
- Xác định:
- Phần nào là FE (React/Vite).
- Phần nào là BE (FastAPI, agent, common).
- Có thay đổi DB/schema/API contract không.
2. **Style & cấu trúc**
- FE:
- Component nhỏ, dễ test, không quá nhiều trách nhiệm.
- Hook (`useMemo`, `useEffect`, `useQuery`…) dùng đúng dependency.
- Không lặp lại logic filter/query – ưu tiên reuse hook/context có sẵn.
- BE:
- Route chia theo module rõ (memos, chatbot, common).
- Service/helper theo pattern singleton (như `CANIFAGraph`, `FormatterAgent`) khi phù hợp.
- Không trộn lẫn logic domain với code infra (db client, cache, v.v.).
3. **Performance**
- FE:
- Tránh re-render vô ích (props ổn định, key chuẩn, tránh inline function quá nhiều).
- List lớn (memos) dùng pagination / infinite scroll đã có, không load all.
- BE:
- Query Mongo tối ưu (có filter, limit, sort rõ ràng, tránh scan toàn bộ).
- Không gọi LLM/tool thừa trong agent (`memo_retrieval_tool` chỉ khi cần).
4. **Bảo mật & độ tin cậy**
- Không hardcode secret (API keys, DB URI) trực tiếp trong code.
- Check:
- Auth/visibility memos: PRIVATE/PROTECTED/PUBLIC xử lý đúng.
- Chatbot không lộ thông tin nhạy cảm trong log.
- Validate input cơ bản (id, filter, query string) trước khi dùng.
5. **Test & DX**
- Gợi ý test nên chạy:
- FE: `pnpm lint`, `pnpm test:e2e` (hoặc `/run-e2e`).
- BE: pytest, ruff/black (`/format-code` cũng là hint).
- Ghi nhận:
- Có test mới cho logic quan trọng không (memos, comment, chatbot history)?
- Code mới có dễ hiểu cho dev khác (tên biến, tên hàm, comment ngắn gọn)?
6. **Output**
- Tóm tắt review theo 3 phần:
-**Điểm tốt** (style, kiến trúc, UX…).
- ⚠️ **Cần cải thiện** (ưu tiên theo mức độ: blocking / nên sửa / nice-to-have).
- 🧪 **Đề xuất test cần chạy** trước khi merge.
Chạy bộ test end-to-end cho CuCu Note bằng subagent Playwright.
Cách dùng:
- Gõ: `/run-e2e http://localhost:3001` (hoặc URL khác của FE CuCu).
Khi thực thi lệnh này, hãy:
1. Nếu người dùng không truyền URL thì hỏi lại cho rõ (ví dụ: `http://localhost:3001`).
2. Gọi subagent **`playwright-tester`** với nội dung:
- “Test lại tất cả pages và flow chính của CuCu Note ở URL: <URL người dùng truyn>”.
- Yêu cầu chạy đúng **2 phase** như prompt của subagent:
- Phase 1: đi một vòng tất cả pages chính (Home, About CuCu, detail note, share page, shortcuts…) để check 404/lỗi trắng.
- Phase 2: test chi tiết các flow:
- Tạo note mới + `#tag`.
- Filter theo tag.
- Filter theo ngày hôm nay.
- Trang About CuCu (tầm nhìn, sứ mệnh, cách dùng).
- Chatbot widget + history + load-more.
3. Sau khi subagent xong, tóm tắt lại cho user:
- Bảng pages: route | PASS/FAIL | ghi chú.
- Bảng flows: flow | PASS/FAIL | lỗi/khuyến nghị fix.
Checklist setup tính năng mới cho CuCu Note (FE + BE).
Khi chạy `/setup-new-feature <mô tả ngắn>`:
1. **Làm rõ yêu cầu**
- Tóm tắt lại mô tả ngắn của user thành:
- Mục tiêu business.
- Ảnh hưởng tới: FE, BE, DB, agent (chatbot) hay chỉ 1 phần.
2. **Thiết kế BE**
- Xác định:
- Cần route API mới hay reuse route cũ?
- Nếu liên quan memos → ưu tiên module `backend/api/memos`.
- Nếu liên quan chatbot/AI → ưu tiên `backend/api/chatbot` + `backend/agent`.
- Cần schema mới trong `common/memos_core/schemas.py` không?
- Đề xuất:
- Service/helper mới (tuân thủ pattern singleton nếu là “agent” kiểu `FormatterAgent`/`CANIFAGraph`).
- Cách đặt tên route, request/response, status code.
3. **Thiết kế FE**
- Xác định page / component nào:
- Có cần route mới trong `frontend/src/router` không.
- Có cần item mới trong sidebar/navbar không.
- Đề xuất:
- Component chính (tên + role).
- Hook/context cần tạo hoặc reuse (filter, query, state).
- UX flow cơ bản (từ Home / từ navigation vào feature).
4. **Testing plan**
- BE:
- Các test cần có (unit/integration) cho route/service mới.
- FE:
- Kịch bản Playwright cần thêm hoặc cập nhật (có thể reuse `/run-e2e` sau khi build xong).
- Gợi ý dùng:
- `/format-code` sau khi code xong để dọn style.
- `/review-pr` khi mở PR.
5. **Output**
- Trả về dưới dạng checklist chi tiết:
- [ ] BE – route/schema/service.
- [ ] FE – route/component/hook.
- [ ] Test – BE/FE.
- [ ] Docs / About (nếu feature ảnh hưởng tới triết lý/UX của CuCu).
#!/bin/bash
# Wrapper script to call Windows PowerShell 'agent' from Git Bash
# This allows Ralph scripts to use 'cursor-agent' command on Windows
if command -v cursor-agent &> /dev/null; then
# Native cursor-agent exists (Linux/Mac)
exec cursor-agent "$@"
elif command -v agent &> /dev/null && [[ "$OSTYPE" != "msys" && "$OSTYPE" != "win32" && -z "$WINDIR" ]]; then
# agent exists and we're not on Windows
exec agent "$@"
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" || -n "$WINDIR" ]]; then
# Windows: use PowerShell to call agent
# Convert Unix-style paths to Windows paths if needed
args=()
for arg in "$@"; do
# Convert /e/opennotion to E:\opennotion for PowerShell
if [[ "$arg" =~ ^/[a-zA-Z]/ ]]; then
drive=$(echo "$arg" | cut -c2 | tr '[:lower:]' '[:upper:]')
path_part=$(echo "$arg" | cut -c3- | tr '/' '\\')
args+=("${drive}:\\${path_part}")
else
args+=("$arg")
fi
done
# Call PowerShell agent with arguments
powershell -Command "& agent $*"
else
echo "Error: cursor-agent not found" >&2
exit 1
fi
#!/bin/bash
# Ralph Wiggum: Initialize Ralph in a project
# Sets up Ralph tracking for CLI mode
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
echo "═══════════════════════════════════════════════════════════════════"
echo "🐛 Ralph Wiggum Initialization"
echo "═══════════════════════════════════════════════════════════════════"
echo ""
# Check if we're in a git repo
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo "⚠️ Warning: Not in a git repository."
echo " Ralph works best with git for state persistence."
echo ""
read -p "Continue anyway? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
# Check for cursor-agent CLI
if ! command -v cursor-agent &> /dev/null; then
echo "⚠️ Warning: cursor-agent CLI not found."
echo " Install via: curl https://cursor.com/install -fsS | bash"
echo ""
fi
# Create directories
mkdir -p .ralph
mkdir -p .cursor/ralph-scripts
# =============================================================================
# CREATE RALPH_TASK.md IF NOT EXISTS
# =============================================================================
if [[ ! -f "RALPH_TASK.md" ]]; then
echo "📝 Creating RALPH_TASK.md template..."
if [[ -f "$SKILL_DIR/assets/RALPH_TASK_TEMPLATE.md" ]]; then
cp "$SKILL_DIR/assets/RALPH_TASK_TEMPLATE.md" RALPH_TASK.md
else
cat > RALPH_TASK.md << 'EOF'
---
task: Your task description here
test_command: "pnpm test"
---
# Task
Describe what you want to accomplish.
## Success Criteria
1. [ ] First thing to complete
2. [ ] Second thing to complete
3. [ ] Third thing to complete
## Context
Any additional context the agent should know.
EOF
fi
echo " Edit RALPH_TASK.md to define your task."
else
echo "✓ RALPH_TASK.md already exists"
fi
# =============================================================================
# INITIALIZE STATE FILES
# =============================================================================
echo "📁 Initializing .ralph/ directory..."
cat > .ralph/guardrails.md << 'EOF'
# Ralph Guardrails (Signs)
> Lessons learned from past failures. READ THESE BEFORE ACTING.
## Core Signs
### Sign: Read Before Writing
- **Trigger**: Before modifying any file
- **Instruction**: Always read the existing file first
- **Added after**: Core principle
### Sign: Test After Changes
- **Trigger**: After any code change
- **Instruction**: Run tests to verify nothing broke
- **Added after**: Core principle
### Sign: Commit Checkpoints
- **Trigger**: Before risky changes
- **Instruction**: Commit current working state first
- **Added after**: Core principle
---
## Learned Signs
(Signs added from observed failures will appear below)
EOF
cat > .ralph/progress.md << 'EOF'
# Progress Log
> Updated by the agent after significant work.
## Summary
- Iterations completed: 0
- Current status: Initialized
## How This Works
Progress is tracked in THIS FILE, not in LLM context.
When context is rotated (fresh agent), the new agent reads this file.
This is how Ralph maintains continuity across iterations.
## Session History
EOF
cat > .ralph/errors.log << 'EOF'
# Error Log
> Failures detected by stream-parser. Use to update guardrails.
EOF
cat > .ralph/activity.log << 'EOF'
# Activity Log
> Real-time tool call logging from stream-parser.
EOF
echo "0" > .ralph/.iteration
# =============================================================================
# INSTALL SCRIPTS
# =============================================================================
echo "📦 Installing scripts..."
# Copy scripts
cp "$SKILL_DIR/scripts/"*.sh .cursor/ralph-scripts/ 2>/dev/null || true
chmod +x .cursor/ralph-scripts/*.sh 2>/dev/null || true
echo "✓ Scripts installed to .cursor/ralph-scripts/"
# =============================================================================
# UPDATE .gitignore
# =============================================================================
if [[ -f ".gitignore" ]]; then
# Don't gitignore .ralph/ - we want it tracked for state persistence
if ! grep -q "ralph-config.json" .gitignore; then
echo "" >> .gitignore
echo "# Ralph config (may contain API keys)" >> .gitignore
echo ".cursor/ralph-config.json" >> .gitignore
fi
echo "✓ Updated .gitignore"
else
cat > .gitignore << 'EOF'
# Ralph config (may contain API keys)
.cursor/ralph-config.json
EOF
echo "✓ Created .gitignore"
fi
# =============================================================================
# SUMMARY
# =============================================================================
echo ""
echo "═══════════════════════════════════════════════════════════════════"
echo "✅ Ralph initialized!"
echo "═══════════════════════════════════════════════════════════════════"
echo ""
echo "Files created:"
echo " • RALPH_TASK.md - Define your task here"
echo " • .ralph/guardrails.md - Lessons learned (agent updates this)"
echo " • .ralph/progress.md - Progress log (agent updates this)"
echo " • .ralph/activity.log - Tool call log (parser updates this)"
echo " • .ralph/errors.log - Failure log (parser updates this)"
echo ""
echo "Next steps:"
echo " 1. Edit RALPH_TASK.md to define your task and criteria"
echo " 2. Run: ./scripts/ralph-loop.sh"
echo " (or: .cursor/ralph-scripts/ralph-loop.sh)"
echo ""
echo "The agent will work autonomously, rotating context as needed."
echo "Monitor progress: tail -f .ralph/activity.log"
echo ""
echo "Learn more: https://ghuntley.com/ralph/"
echo "═══════════════════════════════════════════════════════════════════"
This diff is collapsed.
#!/bin/bash
# Ralph Wiggum: The Loop (CLI Mode)
#
# Runs cursor-agent locally with stream-json parsing for accurate token tracking.
# Handles context rotation via --resume when thresholds are hit.
#
# This script is for power users and scripting. For interactive use, see ralph-setup.sh.
#
# Usage:
# ./ralph-loop.sh # Start from current directory
# ./ralph-loop.sh /path/to/project # Start from specific project
# ./ralph-loop.sh -n 50 -m gpt-5.2-high # Custom iterations and model
# ./ralph-loop.sh --branch feature/foo --pr # Create branch and PR
# ./ralph-loop.sh -y # Skip confirmation (for scripting)
#
# Flags:
# -n, --iterations N Max iterations (default: 20)
# -m, --model MODEL Model to use (default: opus-4.5-thinking)
# --branch NAME Sequential: create/work on branch; Parallel: integration branch name
# --pr Sequential: open PR (requires --branch); Parallel: open ONE integration PR (branch optional)
# --parallel Run tasks in parallel with worktrees
# --max-parallel N Max parallel agents (default: 3)
# --no-merge Skip auto-merge in parallel mode
# -y, --yes Skip confirmation prompt
# -h, --help Show this help
#
# Requirements:
# - RALPH_TASK.md in the project root
# - Git repository
# - cursor-agent CLI installed
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Source common functions
source "$SCRIPT_DIR/ralph-common.sh"
source "$SCRIPT_DIR/task-parser.sh"
# Source parallel execution (if available)
if [[ -f "$SCRIPT_DIR/ralph-parallel.sh" ]]; then
source "$SCRIPT_DIR/ralph-parallel.sh"
fi
# =============================================================================
# FLAG PARSING
# =============================================================================
show_help() {
cat << 'EOF'
Ralph Wiggum: The Loop (CLI Mode)
Usage:
./ralph-loop.sh [options] [workspace]
Options:
-n, --iterations N Max iterations (default: 20)
-m, --model MODEL Model to use (default: opus-4.5-thinking)
--branch NAME Sequential: create/work on branch; Parallel: integration branch name
--pr Sequential: open PR (requires --branch); Parallel: open ONE integration PR (branch optional)
--parallel Run tasks in parallel with worktrees
--max-parallel N Max parallel agents (default: 3)
--no-merge Skip auto-merge in parallel mode
-y, --yes Skip confirmation prompt
-h, --help Show this help
Examples:
./ralph-loop.sh # Interactive mode
./ralph-loop.sh -n 50 # 50 iterations max
./ralph-loop.sh -m gpt-5.2-high # Use GPT model
./ralph-loop.sh --branch feature/api --pr -y # Scripted PR workflow
./ralph-loop.sh --parallel --max-parallel 4 # Run 4 agents in parallel
Environment:
RALPH_MODEL Override default model (same as -m flag)
For interactive setup with a beautiful UI, use ralph-setup.sh instead.
EOF
}
# Parallel mode settings
PARALLEL_MODE=false
MAX_PARALLEL=3
SKIP_MERGE=false
# Parse command line arguments
WORKSPACE=""
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--iterations)
MAX_ITERATIONS="$2"
shift 2
;;
-m|--model)
MODEL="$2"
shift 2
;;
--branch)
USE_BRANCH="$2"
shift 2
;;
--pr)
OPEN_PR=true
shift
;;
--parallel)
PARALLEL_MODE=true
shift
;;
--max-parallel)
MAX_PARALLEL="$2"
PARALLEL_MODE=true
shift 2
;;
--no-merge)
SKIP_MERGE=true
shift
;;
-y|--yes)
SKIP_CONFIRM=true
shift
;;
-h|--help)
show_help
exit 0
;;
-*)
echo "Unknown option: $1"
echo "Use -h for help."
exit 1
;;
*)
# Positional argument = workspace
WORKSPACE="$1"
shift
;;
esac
done
# =============================================================================
# MAIN
# =============================================================================
main() {
# Resolve workspace
if [[ -z "$WORKSPACE" ]]; then
WORKSPACE="$(pwd)"
elif [[ "$WORKSPACE" == "." ]]; then
WORKSPACE="$(pwd)"
else
WORKSPACE="$(cd "$WORKSPACE" && pwd)"
fi
local task_file="$WORKSPACE/RALPH_TASK.md"
# Show banner
show_banner
# Check prerequisites
if ! check_prerequisites "$WORKSPACE"; then
exit 1
fi
# Validate: PR requires branch (sequential mode only)
if [[ "$PARALLEL_MODE" != "true" ]] && [[ "$OPEN_PR" == "true" ]] && [[ -z "$USE_BRANCH" ]]; then
echo "❌ --pr requires --branch (sequential mode)"
echo " Example: ./ralph-loop.sh --branch feature/foo --pr"
exit 1
fi
# Initialize .ralph directory
init_ralph_dir "$WORKSPACE"
echo "Workspace: $WORKSPACE"
echo "Task: $task_file"
echo ""
# Show task summary
echo "📋 Task Summary:"
echo "─────────────────────────────────────────────────────────────────"
head -30 "$task_file"
echo "─────────────────────────────────────────────────────────────────"
echo ""
# Count criteria
local total_criteria done_criteria remaining
# Only count actual checkbox list items (- [ ], * [x], 1. [ ], etc.)
total_criteria=$(grep -cE '^[[:space:]]*([-*]|[0-9]+\.)[[:space:]]+\[(x| )\]' "$task_file" 2>/dev/null) || total_criteria=0
done_criteria=$(grep -cE '^[[:space:]]*([-*]|[0-9]+\.)[[:space:]]+\[x\]' "$task_file" 2>/dev/null) || done_criteria=0
remaining=$((total_criteria - done_criteria))
echo "Progress: $done_criteria / $total_criteria criteria complete ($remaining remaining)"
echo "Model: $MODEL"
echo "Max iter: $MAX_ITERATIONS"
[[ -n "$USE_BRANCH" ]] && echo "Branch: $USE_BRANCH"
[[ "$OPEN_PR" == "true" ]] && echo "Open PR: Yes"
[[ "$PARALLEL_MODE" == "true" ]] && echo "Parallel: Yes ($MAX_PARALLEL agents)"
[[ "$SKIP_MERGE" == "true" ]] && echo "Merge: Skipped"
echo ""
if [[ "$remaining" -eq 0 ]] && [[ "$total_criteria" -gt 0 ]]; then
echo "🎉 Task already complete! All criteria are checked."
exit 0
fi
# Confirm before starting (unless -y flag)
if [[ "$SKIP_CONFIRM" != "true" ]]; then
echo "This will run cursor-agent locally to work on this task."
echo "The agent will be rotated when context fills up (~80k tokens)."
echo ""
echo "Tip: Use ralph-setup.sh for interactive model/option selection."
echo " Use -y flag to skip this prompt."
echo ""
read -p "Start Ralph loop? [y/N] " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 0
fi
fi
# Run in parallel or sequential mode
if [[ "$PARALLEL_MODE" == "true" ]]; then
# Check if parallel functions are available
if ! type run_parallel_tasks &>/dev/null; then
echo "❌ Parallel execution not available (ralph-parallel.sh not found)"
exit 1
fi
# Export settings for parallel execution
export MODEL
export SKIP_MERGE
# Parallel PR behavior: one integration branch + one PR
export CREATE_PR="$OPEN_PR"
local base_branch
base_branch="$(git -C "$WORKSPACE" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")"
# Args: workspace, max_parallel, base_branch, integration_branch(optional)
run_parallel_tasks "$WORKSPACE" "$MAX_PARALLEL" "$base_branch" "$USE_BRANCH"
exit $?
else
# Run the sequential loop
run_ralph_loop "$WORKSPACE" "$SCRIPT_DIR"
exit $?
fi
}
main
#!/bin/bash
# Ralph Wiggum: Single Iteration (Human-in-the-Loop)
#
# Runs exactly ONE iteration of the Ralph loop, then stops.
# Useful for testing your task definition before going AFK.
#
# Usage:
# ./ralph-once.sh # Run single iteration
# ./ralph-once.sh /path/to/project # Run in specific project
# ./ralph-once.sh -m gpt-5.2-high # Use specific model
#
# After running:
# - Review the changes made
# - Check git log for commits
# - If satisfied, run ralph-setup.sh or ralph-loop.sh for full loop
#
# Requirements:
# - RALPH_TASK.md in the project root
# - Git repository
# - cursor-agent CLI installed
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Source common functions
source "$SCRIPT_DIR/ralph-common.sh"
# =============================================================================
# FLAG PARSING
# =============================================================================
show_help() {
cat << 'EOF'
Ralph Wiggum: Single Iteration (Human-in-the-Loop)
Runs exactly ONE iteration, then stops for review.
This is the recommended way to test your task definition.
Usage:
./ralph-once.sh [options] [workspace]
Options:
-m, --model MODEL Model to use (default: opus-4.5-thinking)
-h, --help Show this help
Examples:
./ralph-once.sh # Run one iteration
./ralph-once.sh -m sonnet-4.5-thinking # Use Sonnet model
After reviewing the results:
- If satisfied: run ./ralph-setup.sh for full loop
- If issues: fix them, update RALPH_TASK.md or guardrails, run again
EOF
}
# Parse command line arguments
WORKSPACE=""
while [[ $# -gt 0 ]]; do
case "$1" in
-m|--model)
MODEL="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
-*)
echo "Unknown option: $1"
echo "Use -h for help."
exit 1
;;
*)
# Positional argument = workspace
WORKSPACE="$1"
shift
;;
esac
done
# =============================================================================
# MAIN
# =============================================================================
main() {
# Resolve workspace
if [[ -z "$WORKSPACE" ]]; then
WORKSPACE="$(pwd)"
elif [[ "$WORKSPACE" == "." ]]; then
WORKSPACE="$(pwd)"
else
WORKSPACE="$(cd "$WORKSPACE" && pwd)"
fi
local task_file="$WORKSPACE/RALPH_TASK.md"
# Show banner
echo "═══════════════════════════════════════════════════════════════════"
echo "🐛 Ralph Wiggum: Single Iteration (Human-in-the-Loop)"
echo "═══════════════════════════════════════════════════════════════════"
echo ""
echo " This runs ONE iteration, then stops for your review."
echo " Use this to test your task before going AFK."
echo ""
echo "═══════════════════════════════════════════════════════════════════"
echo ""
# Check prerequisites
if ! check_prerequisites "$WORKSPACE"; then
exit 1
fi
# Initialize .ralph directory
init_ralph_dir "$WORKSPACE"
echo "Workspace: $WORKSPACE"
echo "Model: $MODEL"
echo ""
# Show task summary
echo "📋 Task Summary:"
echo "─────────────────────────────────────────────────────────────────"
head -30 "$task_file"
echo "─────────────────────────────────────────────────────────────────"
echo ""
# Count criteria
local total_criteria done_criteria remaining
# Only count actual checkbox list items (- [ ], * [x], 1. [ ], etc.)
total_criteria=$(grep -cE '^[[:space:]]*([-*]|[0-9]+\.)[[:space:]]+\[(x| )\]' "$task_file" 2>/dev/null) || total_criteria=0
done_criteria=$(grep -cE '^[[:space:]]*([-*]|[0-9]+\.)[[:space:]]+\[x\]' "$task_file" 2>/dev/null) || done_criteria=0
remaining=$((total_criteria - done_criteria))
echo "Progress: $done_criteria / $total_criteria criteria complete ($remaining remaining)"
echo ""
if [[ "$remaining" -eq 0 ]] && [[ "$total_criteria" -gt 0 ]]; then
echo "🎉 Task already complete! All criteria are checked."
exit 0
fi
# Confirm
read -p "Run single iteration? [Y/n] " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Nn]$ ]]; then
echo "Aborted."
exit 0
fi
# Commit any uncommitted work first
cd "$WORKSPACE"
if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then
echo "📦 Committing uncommitted changes..."
git add -A
git commit -m "ralph: checkpoint before single iteration" || true
fi
echo ""
echo "🚀 Running single iteration..."
echo ""
# Run exactly one iteration
local signal
signal=$(run_iteration "$WORKSPACE" "1" "" "$SCRIPT_DIR")
# Check result
local task_status
task_status=$(check_task_complete "$WORKSPACE")
echo ""
echo "═══════════════════════════════════════════════════════════════════"
echo "📋 Single Iteration Complete"
echo "═══════════════════════════════════════════════════════════════════"
echo ""
case "$signal" in
"COMPLETE")
if [[ "$task_status" == "COMPLETE" ]]; then
echo "🎉 Task completed in single iteration!"
echo ""
echo "All criteria are checked. You're done!"
else
echo "⚠️ Agent signaled complete but some criteria remain unchecked."
echo " Review the results and run again if needed."
fi
;;
"GUTTER")
echo "🚨 Gutter detected - agent got stuck."
echo ""
echo "Review .ralph/errors.log and consider:"
echo " 1. Adding a guardrail to .ralph/guardrails.md"
echo " 2. Simplifying the task"
echo " 3. Fixing the blocking issue manually"
;;
"ROTATE")
echo "🔄 Context rotation was triggered."
echo ""
echo "The agent used a lot of context. This is normal for complex tasks."
echo "Review the progress and run again or proceed to full loop."
;;
*)
if [[ "$task_status" == "COMPLETE" ]]; then
echo "🎉 Task completed in single iteration!"
else
local remaining_count=${task_status#INCOMPLETE:}
echo "Agent finished with $remaining_count criteria remaining."
fi
;;
esac
echo ""
echo "Review the changes:"
echo " • git log --oneline -5 # See recent commits"
echo " • git diff HEAD~1 # See changes"
echo " • cat .ralph/progress.md # See progress log"
echo ""
echo "Next steps:"
echo " • If satisfied: ./ralph-setup.sh # Run full loop"
echo " • If issues: fix, update task/guardrails, ./ralph-once.sh again"
echo ""
}
main
This diff is collapsed.
#!/bin/bash
# Ralph Wiggum: Retry Logic with Exponential Backoff
#
# Provides retry utilities for handling transient failures:
# - Exponential backoff with configurable jitter
# - Retryable error detection
# - Command execution with automatic retries
#
# Usage:
# source scripts/ralph-retry.sh
# with_retry 3 1 "curl https://api.example.com/data"
# =============================================================================
# CONFIGURATION (can be overridden before sourcing)
# =============================================================================
# Default retry settings
DEFAULT_MAX_RETRIES="${DEFAULT_MAX_RETRIES:-3}"
DEFAULT_BASE_DELAY="${DEFAULT_BASE_DELAY:-1}"
DEFAULT_MAX_DELAY="${DEFAULT_MAX_DELAY:-60}"
DEFAULT_USE_JITTER="${DEFAULT_USE_JITTER:-true}"
# =============================================================================
# UTILITY FUNCTIONS
# =============================================================================
# Sleep for a specified number of milliseconds
# Args: milliseconds
sleep_ms() {
local ms="$1"
# Convert milliseconds to seconds (with decimal precision)
# Use awk or bc for floating point division if available
if command -v awk &> /dev/null; then
local seconds
seconds=$(awk "BEGIN {printf \"%.3f\", $ms / 1000}")
sleep "$seconds"
elif command -v bc &> /dev/null; then
local seconds
seconds=$(echo "scale=3; $ms / 1000" | bc)
sleep "$seconds"
else
# Fallback: round to nearest second (minimum 1 second)
local seconds=$(( (ms + 500) / 1000 ))
[[ $seconds -lt 1 ]] && seconds=1
sleep "$seconds"
fi
}
# Calculate exponential backoff delay with optional jitter
# Args: attempt, base_delay_seconds, max_delay_seconds, use_jitter(true/false)
# Returns: delay in milliseconds (as integer)
# Formula: base_delay * 2^(attempt-1), capped at max_delay
# Jitter: adds 0-25% random to prevent thundering herd
calculate_backoff_delay() {
local attempt="$1"
local base_delay_seconds="$2"
local max_delay_seconds="$3"
local use_jitter="${4:-true}"
# Convert base delay to milliseconds for precision
local base_delay_ms=$((base_delay_seconds * 1000))
local max_delay_ms=$((max_delay_seconds * 1000))
# Calculate exponential backoff: base_delay * 2^(attempt-1)
# For attempt=1: 2^0 = 1, so delay = base_delay
# For attempt=2: 2^1 = 2, so delay = base_delay * 2
# For attempt=3: 2^2 = 4, so delay = base_delay * 4
local delay_ms=$((base_delay_ms * (1 << (attempt - 1))))
# Cap at max delay
if [[ $delay_ms -gt $max_delay_ms ]]; then
delay_ms=$max_delay_ms
fi
# Add jitter: 0-25% random addition
if [[ "$use_jitter" == "true" ]] || [[ "$use_jitter" == "1" ]]; then
# Generate random number 0-250 (representing 0-25% of delay_ms)
# Using $RANDOM which gives 0-32767, scale to 0-250
local jitter_percent=$((RANDOM % 251))
local jitter_ms=$((delay_ms * jitter_percent / 1000))
delay_ms=$((delay_ms + jitter_ms))
fi
echo "$delay_ms"
}
# Check if an error message indicates a retryable error
# Args: error_message (from stderr or exit code context)
# Returns: 0 if retryable, 1 if not retryable
# Patterns: rate limit, quota, too many requests, 429, timeout, network, connection errors
is_retryable_error() {
local error_msg="$1"
# Convert to lowercase for case-insensitive matching
local lower_msg
lower_msg=$(echo "$error_msg" | tr '[:upper:]' '[:lower:]')
# Check for retryable error patterns
if [[ "$lower_msg" =~ (rate[[:space:]]*limit|rate[[:space:]]*limiting) ]] || \
[[ "$lower_msg" =~ (quota[[:space:]]*exceeded|quota[[:space:]]*limit) ]] || \
[[ "$lower_msg" =~ (too[[:space:]]*many[[:space:]]*requests) ]] || \
[[ "$lower_msg" =~ (429|http[[:space:]]*429) ]] || \
[[ "$lower_msg" =~ (timeout|timed[[:space:]]*out|connection[[:space:]]*timeout) ]] || \
[[ "$lower_msg" =~ (network[[:space:]]*error|network[[:space:]]*unavailable) ]] || \
[[ "$lower_msg" =~ (connection[[:space:]]*refused|connection[[:space:]]*reset) ]] || \
[[ "$lower_msg" =~ (connection[[:space:]]*closed|connection[[:space:]]*failed) ]] || \
[[ "$lower_msg" =~ (temporary[[:space:]]*failure|temporary[[:space:]]*error) ]] || \
[[ "$lower_msg" =~ (service[[:space:]]*unavailable|503) ]] || \
[[ "$lower_msg" =~ (bad[[:space:]]*gateway|502) ]] || \
[[ "$lower_msg" =~ (gateway[[:space:]]*timeout|504) ]]; then
return 0 # Retryable
fi
return 1 # Not retryable
}
# Execute a command with retry logic and exponential backoff
# Args: max_retries, base_delay_seconds, max_delay_seconds, use_jitter, command...
# OR: max_retries, base_delay_seconds, command... (uses defaults for max_delay and jitter)
# Returns: exit code of the command (0 on success, non-zero on failure after all retries)
# Logs retry attempts to stderr
with_retry() {
local max_retries="$1"
local base_delay="$2"
local max_delay="${DEFAULT_MAX_DELAY}"
local use_jitter="${DEFAULT_USE_JITTER}"
local command_start=3
# Check if 3rd arg is numeric (max_delay) or a command
if [[ $# -ge 4 ]] && [[ "$3" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
max_delay="$3"
command_start=4
# Check if 4th arg is jitter flag
if [[ $# -ge 5 ]] && [[ "$4" == "true" ]] || [[ "$4" == "false" ]] || [[ "$4" == "1" ]] || [[ "$4" == "0" ]]; then
use_jitter="$4"
command_start=5
fi
elif [[ $# -ge 4 ]] && [[ "$3" == "true" ]] || [[ "$3" == "false" ]] || [[ "$3" == "1" ]] || [[ "$3" == "0" ]]; then
# 3rd arg is jitter flag, use default max_delay
use_jitter="$3"
command_start=4
fi
# Extract the command (everything from command_start onwards)
local cmd=("${@:$command_start}")
if [[ ${#cmd[@]} -eq 0 ]]; then
echo "Error: with_retry requires a command to execute" >&2
return 1
fi
local attempt=1
local last_exit_code=0
local last_error=""
while [[ $attempt -le $max_retries ]]; do
# Execute the command, capturing both stdout and stderr
local output
output=$("${cmd[@]}" 2>&1)
last_exit_code=$?
# If command succeeded, return success
if [[ $last_exit_code -eq 0 ]]; then
echo "$output"
return 0
fi
# Command failed - capture error message
last_error="$output"
# Check if this is the last attempt
if [[ $attempt -eq $max_retries ]]; then
echo "Error: Command failed after $max_retries attempts" >&2
echo "Last error: $last_error" >&2
echo "$last_error" >&2
return $last_exit_code
fi
# Check if error is retryable
if ! is_retryable_error "$last_error"; then
echo "Error: Non-retryable error detected, aborting retries" >&2
echo "Error: $last_error" >&2
echo "$last_error" >&2
return $last_exit_code
fi
# Calculate backoff delay
local delay_ms
delay_ms=$(calculate_backoff_delay "$attempt" "$base_delay" "$max_delay" "$use_jitter")
local delay_seconds=$((delay_ms / 1000))
# Log retry attempt
echo "⚠️ Attempt $attempt/$max_retries failed (exit code: $last_exit_code)" >&2
echo " Retrying in ${delay_seconds}s (with exponential backoff)..." >&2
if [[ -n "$last_error" ]]; then
echo " Error: ${last_error:0:200}${last_error:200:+...}" >&2
fi
# Sleep before retry
sleep_ms "$delay_ms"
attempt=$((attempt + 1))
done
# Should never reach here, but handle it anyway
echo "$last_error" >&2
return $last_exit_code
}
# =============================================================================
# CONVENIENCE WRAPPERS
# =============================================================================
# Simplified retry wrapper with defaults
# Args: command...
# Uses: DEFAULT_MAX_RETRIES, DEFAULT_BASE_DELAY, DEFAULT_MAX_DELAY, DEFAULT_USE_JITTER
retry() {
with_retry "$DEFAULT_MAX_RETRIES" "$DEFAULT_BASE_DELAY" "$DEFAULT_MAX_DELAY" "$DEFAULT_USE_JITTER" "$@"
}
This diff is collapsed.
# Ralph Wiggum: Windows PowerShell Wrapper
#
# Workaround for Windows where cursor-agent CLI is not available.
# This script uses Cursor's API/MCP to run Ralph iterations.
#
# Usage:
# .\.cursor\ralph-scripts\ralph-windows.ps1
# .\.cursor\ralph-scripts\ralph-windows.ps1 -Iterations 5 -Model "gpt-5.2-high"
param(
[int]$Iterations = 1,
[string]$Model = "opus-4.5-thinking",
[string]$Workspace = ".",
[switch]$SkipConfirm
)
$ErrorActionPreference = "Stop"
# Resolve workspace path
if ($Workspace -eq ".") {
$Workspace = (Get-Location).Path
} else {
$Workspace = (Resolve-Path $Workspace).Path
}
$RalphDir = Join-Path $Workspace ".ralph"
$TaskFile = Join-Path $Workspace "RALPH_TASK.md"
# Check prerequisites
if (-not (Test-Path $TaskFile)) {
Write-Host "❌ RALPH_TASK.md not found in $Workspace" -ForegroundColor Red
exit 1
}
# Initialize .ralph directory
if (-not (Test-Path $RalphDir)) {
New-Item -ItemType Directory -Path $RalphDir -Force | Out-Null
}
Write-Host "═══════════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host "🐛 Ralph Wiggum: Windows PowerShell Wrapper" -ForegroundColor Cyan
Write-Host "═══════════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
Write-Host "⚠️ Note: cursor-agent CLI is not available on Windows." -ForegroundColor Yellow
Write-Host " This script will guide you to run Ralph manually via Cursor chat." -ForegroundColor Yellow
Write-Host ""
Write-Host "Workspace: $Workspace" -ForegroundColor Green
Write-Host "Iterations: $Iterations" -ForegroundColor Green
Write-Host "Model: $Model" -ForegroundColor Green
Write-Host ""
# Read task file
$TaskContent = Get-Content $TaskFile -Raw
Write-Host "📋 Task Summary:" -ForegroundColor Cyan
Write-Host "─────────────────────────────────────────────────────────────────" -ForegroundColor Gray
Get-Content $TaskFile -Head 30
Write-Host "─────────────────────────────────────────────────────────────────" -ForegroundColor Gray
Write-Host ""
# Count criteria
$TotalCriteria = ([regex]::Matches($TaskContent, '(?m)^\s*([-*]|\d+\.)\s+\[(x| )\]')).Count
$DoneCriteria = ([regex]::Matches($TaskContent, '(?m)^\s*([-*]|\d+\.)\s+\[x\]')).Count
$Remaining = $TotalCriteria - $DoneCriteria
Write-Host "Progress: $DoneCriteria / $TotalCriteria criteria complete ($Remaining remaining)" -ForegroundColor $(if ($Remaining -eq 0) { "Green" } else { "Yellow" })
Write-Host ""
if ($Remaining -eq 0 -and $TotalCriteria -gt 0) {
Write-Host "🎉 Task already complete! All criteria are checked." -ForegroundColor Green
exit 0
}
if (-not $SkipConfirm) {
$Confirm = Read-Host "Run $Iterations iteration(s)? [Y/n]"
if ($Confirm -match "^[Nn]$") {
Write-Host "Aborted." -ForegroundColor Yellow
exit 0
}
}
Write-Host ""
Write-Host "═══════════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host "📝 Manual Ralph Execution Guide" -ForegroundColor Cyan
Write-Host "═══════════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
Write-Host "Since cursor-agent CLI is not available on Windows, you need to:" -ForegroundColor Yellow
Write-Host ""
Write-Host "1. Open Cursor IDE" -ForegroundColor White
Write-Host "2. Open this workspace: $Workspace" -ForegroundColor White
Write-Host "3. In Cursor chat, use this command:" -ForegroundColor White
Write-Host ""
Write-Host " /ralph-loop `"Refactor & tối ưu backend CuCu: kiến trúc FastAPI + agent clean, tách rõ layer, xoá dead code, tránh bottleneck (N+1 query, DB, cache, rate limit), đảm bảo 'cd backend && pytest' luôn xanh.`"" -ForegroundColor Cyan
Write-Host ""
Write-Host "4. The command will update RALPH_TASK.md and show you bash commands" -ForegroundColor White
Write-Host "5. Since bash commands won't work, you can:" -ForegroundColor White
Write-Host " - Use Cursor's built-in agent features to work on the task" -ForegroundColor White
Write-Host " - Or use WSL (Windows Subsystem for Linux) to run bash scripts" -ForegroundColor White
Write-Host ""
Write-Host "═══════════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
# Alternative: Try to use Cursor MCP if available
Write-Host "💡 Alternative: Using Cursor MCP Server (if configured)" -ForegroundColor Cyan
Write-Host ""
Write-Host "If you have Cursor MCP server configured, you can use it to run Ralph." -ForegroundColor Yellow
Write-Host "Check your MCP configuration in Cursor settings." -ForegroundColor Yellow
Write-Host ""
# Save iteration state
$IterationFile = Join-Path $RalphDir ".iteration"
$CurrentIteration = if (Test-Path $IterationFile) { [int](Get-Content $IterationFile) } else { 0 }
$NextIteration = $CurrentIteration + 1
$NextIteration | Out-File $IterationFile -NoNewline
Write-Host "✅ Iteration state saved: $NextIteration" -ForegroundColor Green
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Cyan
Write-Host " • Review RALPH_TASK.md" -ForegroundColor White
Write-Host " • Use Cursor chat with /ralph-loop command" -ForegroundColor White
Write-Host " • Or install WSL and run bash scripts there" -ForegroundColor White
Write-Host ""
#!/bin/bash
# Setup script to create cursor-agent alias for Windows Git Bash
echo "🔧 Setting up cursor-agent for Windows..."
# Create a simple wrapper script in a location that's in PATH
WRAPPER_DIR="$HOME/.local/bin"
mkdir -p "$WRAPPER_DIR"
# Create wrapper script
cat > "$WRAPPER_DIR/cursor-agent" << 'EOF'
#!/bin/bash
# Wrapper to call Windows PowerShell 'agent' command
# PowerShell needs full PATH to find agent
# Get agent path from PowerShell and use it
powershell.exe -NoProfile -Command "
\$env:Path = [System.Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path','User')
\$agentCmd = Get-Command agent -ErrorAction SilentlyContinue
if (\$agentCmd) {
& agent $*
} else {
Write-Error 'agent command not found. Make sure Cursor CLI is installed.'
exit 1
}
"
EOF
chmod +x "$WRAPPER_DIR/cursor-agent"
# Add to PATH if not already there
if [[ ":$PATH:" != *":$WRAPPER_DIR:"* ]]; then
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
export PATH="$HOME/.local/bin:$PATH"
echo "✅ Added ~/.local/bin to PATH"
fi
echo "✅ cursor-agent wrapper created at: $WRAPPER_DIR/cursor-agent"
echo ""
echo "Test it with:"
echo " cursor-agent --version"
echo ""
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{
"setup-worktree": [
"npm install"
]
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# Ignore embedded repo
preference/
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment