@canivel/ralph 0.2.0 → 0.2.3

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 (39) hide show
  1. package/.agents/ralph/PROMPT_build.md +126 -126
  2. package/.agents/ralph/agents.sh +17 -15
  3. package/.agents/ralph/config.sh +25 -25
  4. package/.agents/ralph/log-activity.sh +15 -15
  5. package/.agents/ralph/loop.sh +1027 -1001
  6. package/.agents/ralph/references/CONTEXT_ENGINEERING.md +126 -126
  7. package/.agents/ralph/references/GUARDRAILS.md +174 -174
  8. package/AGENTS.md +20 -20
  9. package/README.md +270 -266
  10. package/bin/ralph +766 -765
  11. package/diagram.svg +55 -55
  12. package/examples/commands.md +46 -46
  13. package/package.json +39 -39
  14. package/skills/commit/SKILL.md +219 -219
  15. package/skills/commit/references/commit_examples.md +292 -292
  16. package/skills/dev-browser/SKILL.md +211 -211
  17. package/skills/dev-browser/bun.lock +443 -443
  18. package/skills/dev-browser/package-lock.json +2988 -2988
  19. package/skills/dev-browser/package.json +31 -31
  20. package/skills/dev-browser/references/scraping.md +155 -155
  21. package/skills/dev-browser/scripts/start-relay.ts +32 -32
  22. package/skills/dev-browser/scripts/start-server.ts +117 -117
  23. package/skills/dev-browser/server.sh +24 -24
  24. package/skills/dev-browser/src/client.ts +474 -474
  25. package/skills/dev-browser/src/index.ts +287 -287
  26. package/skills/dev-browser/src/relay.ts +731 -731
  27. package/skills/dev-browser/src/snapshot/__tests__/snapshot.test.ts +223 -223
  28. package/skills/dev-browser/src/snapshot/browser-script.ts +877 -877
  29. package/skills/dev-browser/src/snapshot/index.ts +14 -14
  30. package/skills/dev-browser/src/snapshot/inject.ts +13 -13
  31. package/skills/dev-browser/src/types.ts +34 -34
  32. package/skills/dev-browser/tsconfig.json +36 -36
  33. package/skills/dev-browser/vitest.config.ts +12 -12
  34. package/skills/prd/SKILL.md +235 -235
  35. package/tests/agent-loops.mjs +79 -79
  36. package/tests/agent-ping.mjs +39 -39
  37. package/tests/audit.md +56 -56
  38. package/tests/cli-smoke.mjs +47 -47
  39. package/tests/real-agents.mjs +127 -127
@@ -1,223 +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
- });
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
+ });