@canivel/ralph 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.agents/ralph/PROMPT_build.md +126 -0
  2. package/.agents/ralph/agents.sh +15 -0
  3. package/.agents/ralph/config.sh +25 -0
  4. package/.agents/ralph/log-activity.sh +15 -0
  5. package/.agents/ralph/loop.sh +1001 -0
  6. package/.agents/ralph/references/CONTEXT_ENGINEERING.md +126 -0
  7. package/.agents/ralph/references/GUARDRAILS.md +174 -0
  8. package/AGENTS.md +20 -0
  9. package/README.md +266 -0
  10. package/bin/ralph +766 -0
  11. package/diagram.svg +55 -0
  12. package/examples/commands.md +46 -0
  13. package/package.json +39 -0
  14. package/ralph.webp +0 -0
  15. package/skills/commit/SKILL.md +219 -0
  16. package/skills/commit/references/commit_examples.md +292 -0
  17. package/skills/dev-browser/SKILL.md +211 -0
  18. package/skills/dev-browser/bun.lock +443 -0
  19. package/skills/dev-browser/package-lock.json +2988 -0
  20. package/skills/dev-browser/package.json +31 -0
  21. package/skills/dev-browser/references/scraping.md +155 -0
  22. package/skills/dev-browser/scripts/start-relay.ts +32 -0
  23. package/skills/dev-browser/scripts/start-server.ts +117 -0
  24. package/skills/dev-browser/server.sh +24 -0
  25. package/skills/dev-browser/src/client.ts +474 -0
  26. package/skills/dev-browser/src/index.ts +287 -0
  27. package/skills/dev-browser/src/relay.ts +731 -0
  28. package/skills/dev-browser/src/snapshot/__tests__/snapshot.test.ts +223 -0
  29. package/skills/dev-browser/src/snapshot/browser-script.ts +877 -0
  30. package/skills/dev-browser/src/snapshot/index.ts +14 -0
  31. package/skills/dev-browser/src/snapshot/inject.ts +13 -0
  32. package/skills/dev-browser/src/types.ts +34 -0
  33. package/skills/dev-browser/tsconfig.json +36 -0
  34. package/skills/dev-browser/vitest.config.ts +12 -0
  35. package/skills/prd/SKILL.md +235 -0
  36. package/tests/agent-loops.mjs +79 -0
  37. package/tests/agent-ping.mjs +39 -0
  38. package/tests/audit.md +56 -0
  39. package/tests/cli-smoke.mjs +47 -0
  40. package/tests/real-agents.mjs +127 -0
@@ -0,0 +1,223 @@
1
+ import { chromium } from "playwright";
2
+ import type { Browser, BrowserContext, Page } from "playwright";
3
+ import { beforeAll, afterAll, beforeEach, afterEach, describe, test, expect } from "vitest";
4
+ import { getSnapshotScript, clearSnapshotScriptCache } from "../browser-script";
5
+
6
+ let browser: Browser;
7
+ let context: BrowserContext;
8
+ let page: Page;
9
+
10
+ beforeAll(async () => {
11
+ browser = await chromium.launch();
12
+ });
13
+
14
+ afterAll(async () => {
15
+ await browser.close();
16
+ });
17
+
18
+ beforeEach(async () => {
19
+ context = await browser.newContext();
20
+ page = await context.newPage();
21
+ clearSnapshotScriptCache(); // Start fresh for each test
22
+ });
23
+
24
+ afterEach(async () => {
25
+ await context.close();
26
+ });
27
+
28
+ async function setContent(html: string): Promise<void> {
29
+ await page.setContent(html, { waitUntil: "domcontentloaded" });
30
+ }
31
+
32
+ async function getSnapshot(): Promise<string> {
33
+ const script = getSnapshotScript();
34
+ return await page.evaluate((s: string) => {
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ const w = globalThis as any;
37
+ if (!w.__devBrowser_getAISnapshot) {
38
+ // eslint-disable-next-line no-eval
39
+ eval(s);
40
+ }
41
+ return w.__devBrowser_getAISnapshot();
42
+ }, script);
43
+ }
44
+
45
+ async function selectRef(ref: string): Promise<unknown> {
46
+ return await page.evaluate((refId: string) => {
47
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
+ const w = globalThis as any;
49
+ const element = w.__devBrowser_selectSnapshotRef(refId);
50
+ return {
51
+ tagName: element.tagName,
52
+ textContent: element.textContent?.trim(),
53
+ };
54
+ }, ref);
55
+ }
56
+
57
+ describe("ARIA Snapshot", () => {
58
+ test("generates snapshot for simple page", async () => {
59
+ await setContent(`
60
+ <html>
61
+ <body>
62
+ <h1>Hello World</h1>
63
+ <button>Click me</button>
64
+ </body>
65
+ </html>
66
+ `);
67
+
68
+ const snapshot = await getSnapshot();
69
+
70
+ expect(snapshot).toContain("heading");
71
+ expect(snapshot).toContain("Hello World");
72
+ expect(snapshot).toContain("button");
73
+ expect(snapshot).toContain("Click me");
74
+ });
75
+
76
+ test("assigns refs to interactive elements", async () => {
77
+ await setContent(`
78
+ <html>
79
+ <body>
80
+ <button id="btn1">Button 1</button>
81
+ <button id="btn2">Button 2</button>
82
+ </body>
83
+ </html>
84
+ `);
85
+
86
+ const snapshot = await getSnapshot();
87
+
88
+ // Should have refs
89
+ expect(snapshot).toMatch(/\[ref=e\d+\]/);
90
+ });
91
+
92
+ test("refs persist on window.__devBrowserRefs", async () => {
93
+ await setContent(`
94
+ <html>
95
+ <body>
96
+ <button>Test Button</button>
97
+ </body>
98
+ </html>
99
+ `);
100
+
101
+ await getSnapshot();
102
+
103
+ // Check that refs are stored
104
+ const hasRefs = await page.evaluate(() => {
105
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
106
+ const w = globalThis as any;
107
+ return typeof w.__devBrowserRefs === "object" && Object.keys(w.__devBrowserRefs).length > 0;
108
+ });
109
+
110
+ expect(hasRefs).toBe(true);
111
+ });
112
+
113
+ test("selectSnapshotRef returns element for valid ref", async () => {
114
+ await setContent(`
115
+ <html>
116
+ <body>
117
+ <button>My Button</button>
118
+ </body>
119
+ </html>
120
+ `);
121
+
122
+ const snapshot = await getSnapshot();
123
+
124
+ // Extract a ref from the snapshot
125
+ const refMatch = snapshot.match(/\[ref=(e\d+)\]/);
126
+ expect(refMatch).toBeTruthy();
127
+ expect(refMatch![1]).toBeDefined();
128
+ const ref = refMatch![1] as string;
129
+
130
+ // Select the element by ref
131
+ const result = (await selectRef(ref)) as { tagName: string; textContent: string };
132
+ expect(result.tagName).toBe("BUTTON");
133
+ expect(result.textContent).toBe("My Button");
134
+ });
135
+
136
+ test("includes links with URLs", async () => {
137
+ await setContent(`
138
+ <html>
139
+ <body>
140
+ <a href="https://example.com">Example Link</a>
141
+ </body>
142
+ </html>
143
+ `);
144
+
145
+ const snapshot = await getSnapshot();
146
+
147
+ expect(snapshot).toContain("link");
148
+ expect(snapshot).toContain("Example Link");
149
+ // URL should be included as a prop
150
+ expect(snapshot).toContain("/url:");
151
+ });
152
+
153
+ test("includes form elements", async () => {
154
+ await setContent(`
155
+ <html>
156
+ <body>
157
+ <input type="text" placeholder="Enter name" />
158
+ <input type="checkbox" />
159
+ <select>
160
+ <option>Option 1</option>
161
+ <option>Option 2</option>
162
+ </select>
163
+ </body>
164
+ </html>
165
+ `);
166
+
167
+ const snapshot = await getSnapshot();
168
+
169
+ expect(snapshot).toContain("textbox");
170
+ expect(snapshot).toContain("checkbox");
171
+ expect(snapshot).toContain("combobox");
172
+ });
173
+
174
+ test("renders nested structure correctly", async () => {
175
+ await setContent(`
176
+ <html>
177
+ <body>
178
+ <nav>
179
+ <ul>
180
+ <li><a href="/home">Home</a></li>
181
+ <li><a href="/about">About</a></li>
182
+ </ul>
183
+ </nav>
184
+ </body>
185
+ </html>
186
+ `);
187
+
188
+ const snapshot = await getSnapshot();
189
+
190
+ expect(snapshot).toContain("navigation");
191
+ expect(snapshot).toContain("list");
192
+ expect(snapshot).toContain("listitem");
193
+ expect(snapshot).toContain("link");
194
+ });
195
+
196
+ test("handles disabled elements", async () => {
197
+ await setContent(`
198
+ <html>
199
+ <body>
200
+ <button disabled>Disabled Button</button>
201
+ </body>
202
+ </html>
203
+ `);
204
+
205
+ const snapshot = await getSnapshot();
206
+
207
+ expect(snapshot).toContain("[disabled]");
208
+ });
209
+
210
+ test("handles checked checkboxes", async () => {
211
+ await setContent(`
212
+ <html>
213
+ <body>
214
+ <input type="checkbox" checked />
215
+ </body>
216
+ </html>
217
+ `);
218
+
219
+ const snapshot = await getSnapshot();
220
+
221
+ expect(snapshot).toContain("[checked]");
222
+ });
223
+ });