tamash-playwright 0.6.1 → 0.7.0-beta.1

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.
package/README.md CHANGED
@@ -12,6 +12,8 @@ Websites change often. A button gets renamed or moved, and your test can't find
12
12
 
13
13
  **Want to see it working before you set it up yourself?** Clone the sample repo — [github.com/qtpsudhakarproducts/tamash-playwright-typescript-playwright](https://github.com/qtpsudhakarproducts/tamash-playwright-typescript-playwright) — a full worked example with both a plain-locator test and a Page Object Model test, an intentionally broken selector, and step-by-step setup instructions.
14
14
 
15
+ **Looking for the complete reference** — caching, `apply-heals`, sharded-CI and PR automation, every env var and CLI flag? See [usage.md](usage.md).
16
+
15
17
  Here are the detailed steps to use this package.
16
18
 
17
19
  ## Step 1: Install it
@@ -184,6 +186,149 @@ Sometimes an element has nothing useful to match on by text — an icon-only but
184
186
 
185
187
  Occasionally a locator heals correctly — the AI found the right element — but the *action* on it still fails, e.g. it's covered by an overlay or needs scrolling into view first. Set `HEALER_ACTION_RECOVERY_ENABLED=true` to let the AI pick a recovery tactic from a fixed, safe set (scroll into view, retry bypassing actionability checks, wait briefly and retry, or dispatch the DOM event directly) before giving up. It's off by default since it's a second, more speculative layer of intervention beyond selector healing — the AI only ever picks from that fixed menu, it never decides how to interact with the page on its own.
186
188
 
189
+ ## Not paying for the same heal twice
190
+
191
+ Once a locator heals successfully, `tamash-playwright` remembers the fix in `.tamash-playwright/heals.jsonl` — the same file `apply-heals` reads (see below). The next time that exact locator breaks the same way, it tries the previously-confirmed selector *first*, with no ARIA snapshot and no AI call. Only if that no longer works (the page changed again) does it fall through to a fresh snapshot-and-AI-call, exactly as before — so there's no correctness risk in trying the cached selector, only a cost/time saving when it still works.
192
+
193
+ This persists across runs, not just within one — since it reads from disk rather than an in-memory cache, it also helps the case that costs the most in practice: the same broken selector otherwise getting healed by AI on every single CI run until someone gets around to running `apply-heals`. You'll see it in the console line as `provider=cache` with no token count, instead of the real provider name:
194
+
195
+ ```
196
+ [self-healer] tests/sampletest.spec.ts:13 — locator.fill "User Name Textbox (placeholder "xyz")" -> HEALED [provider=cache, vision=no, actionRecovery=no, suggested="role:textbox:Username"] — locator.fill: Timeout 8000ms exceeded.
197
+ ```
198
+
199
+ A cache hit doesn't re-log itself (there's nothing new to record), so the log doesn't grow just from repeated confirmations of the same already-known fix — it only grows when a *new* AI call produces a fresh suggestion.
200
+
201
+ ## Making a heal permanent: `apply-heals`
202
+
203
+ Runtime healing (including the caching above) never touches your source code — the original locator stays broken in your test file or Page Object forever, healed at runtime on every run, until you fix it yourself. `apply-heals` closes that loop: it rewrites the original broken locator to the selector that actually worked, so the next run doesn't need healing — from cache or otherwise — at all.
204
+
205
+ ```sh
206
+ npx playwright test # heals at runtime, and records what it healed
207
+ npx tamash-playwright apply-heals --dry-run # preview the source changes it would make
208
+ npx tamash-playwright apply-heals # write them
209
+ ```
210
+
211
+ ```
212
+ [FIX] src/pages/loginpage.ts:11
213
+ - .locator('input[name="username1"]')
214
+ + .getByRole("textbox", { name: "Username" })
215
+
216
+ 1 fix(es) applied to 1 file(s), 0 skipped.
217
+ Review the changes (e.g. `git diff`) before committing.
218
+ ```
219
+
220
+ A few things worth knowing:
221
+
222
+ - **Nothing is applied automatically.** `apply-heals` is a separate, deliberate command — a test run never edits your source on its own.
223
+ - **Only real selector fixes are eligible.** A heal only qualifies if it's text/ARIA-based (not the screenshot-based vision fallback, which has no reusable source form — see below) and the locator itself was actually replaced (not an action-recovery heal, where the original locator was already correct and only the action needed help).
224
+ - **Only the matched call is touched.** `.describe('...')` and everything else on the line is left exactly as written; only the `.locator(...)`/`.getByRole(...)`/etc. call itself is replaced.
225
+ - **Always review the diff before committing** — this rewrites your source files, so treat it like any other automated code change: check `git diff`, run your tests again, and commit deliberately.
226
+
227
+ Every run also writes a before/after report to `.tamash-playwright/` — `apply-heals-report.json` (machine-readable) and `apply-heals-report.md` (human-readable, one section per fix with the exact before/after code). This is what feeds the PR body in the CI pattern below, so a reviewer sees the actual change without digging through logs — worth knowing about even if you only ever run this locally, since it's a persisted record of every fix beyond whatever's still in your scrollback.
228
+
229
+ Those two filenames always mean "the latest run" — each run overwrites them. Since `.tamash-playwright/` is gitignored (never committed), that would otherwise mean a second run erases all trace of the first. To keep a real history, every run *also* archives a timestamped copy of both reports, plus the raw `heals.jsonl` that produced them, under `.tamash-playwright/history/` — nothing in there is ever overwritten or deleted by a later run, so it's safe to just leave it accumulating, or clean it out yourself whenever you want.
230
+
231
+ ### Running `apply-heals` in CI (sharded or not)
232
+
233
+ `apply-heals` never touches git itself — in CI, that means the fix only exists in that job's ephemeral checkout unless something turns it into a real, reviewable change. The recommended shape: a separate job that runs *after* your test job(s), downloads whatever got healed, and opens a PR rather than pushing straight to a branch.
234
+
235
+ If your suite runs sharded (`--shard=N/M` across multiple CI machines), each shard only sees its own slice of what got healed — `.tamash-playwright/heals.jsonl` ends up fragmented, one partial file per shard. `--logs-dir` is built for exactly this: point it at a directory containing any number of `heals.jsonl` files, nested however you like, and it merges all of them before planning fixes (deduplicating by keeping only the newest entry per file:line, so two shards healing the same line is harmless):
236
+
237
+ ```sh
238
+ npx tamash-playwright apply-heals --logs-dir shard-logs
239
+ ```
240
+
241
+ A GitHub Actions example — each test job uploads its own log as an artifact; a separate job merges them, applies the fixes on a fresh branch, **re-runs the suite against just those fixes to prove they actually work**, and only then opens a PR (labeled with whether verification passed, either way):
242
+
243
+ ```yaml
244
+ jobs:
245
+ test:
246
+ # ...your existing test job(s), sharded or not...
247
+ steps:
248
+ - run: npx playwright test
249
+ - uses: actions/upload-artifact@v4
250
+ if: ${{ !cancelled() }}
251
+ with:
252
+ name: heals-log-${{ strategy.job-index }}
253
+ path: .tamash-playwright/heals.jsonl
254
+ if-no-files-found: ignore
255
+
256
+ apply-heals:
257
+ needs: test
258
+ if: ${{ !cancelled() && github.event_name == 'push' }} # not pull_request — see note below
259
+ runs-on: ubuntu-latest
260
+ permissions:
261
+ contents: write
262
+ pull-requests: write
263
+ steps:
264
+ - uses: actions/checkout@v4
265
+ - uses: actions/setup-node@v4
266
+ with: { node-version: lts/* }
267
+ - run: npm ci
268
+
269
+ - uses: actions/download-artifact@v4
270
+ with:
271
+ pattern: heals-log-*
272
+ path: shard-logs
273
+ continue-on-error: true # no artifact at all when nothing needed healing — the common case
274
+ - run: npx tamash-playwright apply-heals --logs-dir shard-logs
275
+
276
+ - name: Check whether any fixes were applied
277
+ id: check
278
+ run: echo "changed=$(git diff --quiet || echo true)" >> "$GITHUB_OUTPUT"
279
+
280
+ # HEALER_ENABLED=false here is deliberate: this proves the *written* fix works standalone —
281
+ # leaving healing on could let a still-broken selector get silently re-healed at runtime
282
+ # again, reporting green without ever proving the applied source fix was actually correct.
283
+ - name: Verify the healed selectors work on their own
284
+ id: verify
285
+ if: steps.check.outputs.changed == 'true'
286
+ run: npx playwright test
287
+ continue-on-error: true
288
+ env:
289
+ HEALER_ENABLED: false
290
+
291
+ # apply-heals already wrote .tamash-playwright/apply-heals-report.md with a before/after per
292
+ # fix — this prepends the verification result so the PR body is one linked story (what
293
+ # broke, what changed, whether it's proven to work) instead of three things to go find.
294
+ - name: Compose PR body
295
+ if: steps.check.outputs.changed == 'true'
296
+ run: |
297
+ {
298
+ echo "Auto-generated by \`tamash-playwright apply-heals\` after self-healing kicked in during CI."
299
+ echo ""
300
+ if [ "${{ steps.verify.outcome }}" = "success" ]; then
301
+ echo "**Verification run (healing disabled): ✅ passed.**"
302
+ else
303
+ echo "**Verification run (healing disabled): ⚠️ FAILED — review carefully before merging.**"
304
+ fi
305
+ echo ""
306
+ cat .tamash-playwright/apply-heals-report.md
307
+ } > .tamash-playwright/pr-body.md
308
+
309
+ - name: Open PR with healed selectors
310
+ if: steps.check.outputs.changed == 'true'
311
+ uses: peter-evans/create-pull-request@v6
312
+ with:
313
+ commit-message: "fix: apply self-healed selectors from CI"
314
+ title: "Apply self-healed selectors (verification ${{ steps.verify.outcome == 'success' && 'passed' || 'FAILED' }})"
315
+ body-path: .tamash-playwright/pr-body.md
316
+ branch: tamash-playwright/apply-heals
317
+ delete-branch: true
318
+
319
+ - name: Fail the job if verification didn't pass
320
+ if: steps.check.outputs.changed == 'true' && steps.verify.outcome != 'success'
321
+ run: exit 1 # PR is still opened above for review — this just keeps CI status honest
322
+ ```
323
+
324
+ One more thing worth flagging if you copy this: `.tamash-playwright/` needs to be in your `.gitignore`. `peter-evans/create-pull-request` commits whatever differs from `HEAD` in the working tree — without the ignore, the report/log files themselves would get swept into the PR alongside the actual source fix.
325
+
326
+ A few choices worth calling out:
327
+
328
+ - **Gated on `push`, not `pull_request`.** A `pull_request` run from a fork gets a read-only `GITHUB_TOKEN` (so it couldn't open a PR anyway), and "open a PR to fix this still-open PR" isn't a sensible flow regardless. Running after merges to `main`/`master` avoids both problems.
329
+ - **The PR is opened either way**, verification passed or failed — a failed verification is still worth a human's attention (maybe the fix is right and something else was flaky); it just gets an honest label instead of a silent false-positive. The final step fails the *job* itself when verification fails, so CI status stays truthful even though the PR still exists for review.
330
+ - [`peter-evans/create-pull-request`](https://github.com/peter-evans/create-pull-request) is a no-op if there's nothing to commit, so the job is safe to run on every push — it only ever opens a PR when there's an actual fix to review, and reuses the same branch/PR on subsequent runs rather than piling up duplicates.
331
+
187
332
  ## Checking what actually happened
188
333
 
189
334
  Every healing attempt — whether it succeeded or not — shows up in Playwright's own HTML report (`npx playwright show-report`), no separate report to check:
@@ -0,0 +1,15 @@
1
+ import { type HealLogEntry } from '../healer/heal-log';
2
+ export type FixOutcome = {
3
+ file: string;
4
+ line: number;
5
+ before: string;
6
+ after: string;
7
+ applied: boolean;
8
+ reason?: string;
9
+ };
10
+ export declare function planFixes(cwd?: string, rawEntries?: HealLogEntry[]): {
11
+ outcomes: FixOutcome[];
12
+ fileContents: Map<string, string>;
13
+ };
14
+ export declare function runApplyHeals(args?: string[]): Promise<void>;
15
+ //# sourceMappingURL=applyHeals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyHeals.d.ts","sourceRoot":"","sources":["../../src/cli/applyHeals.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAiHvH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAqBF,wBAAgB,SAAS,CAAC,GAAG,GAAE,MAAsB,EAAE,UAAU,CAAC,EAAE,YAAY,EAAE,GAAG;IAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CA2DjJ;AA2ED,wBAAsB,aAAa,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDtE"}
@@ -0,0 +1,283 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.planFixes = planFixes;
7
+ exports.runApplyHeals = runApplyHeals;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const heal_log_1 = require("../healer/heal-log");
11
+ const LOCATOR_FACTORY_METHODS = ['locator', 'getByRole', 'getByLabel', 'getByPlaceholder', 'getByText', 'getByAltText', 'getByTitle', 'getByTestId'];
12
+ const FACTORY_METHOD_PATTERN = new RegExp(`\\.(${LOCATOR_FACTORY_METHODS.join('|')})\\(`);
13
+ function generateReplacementCall(suggestion) {
14
+ switch (suggestion.strategy) {
15
+ case 'role':
16
+ return suggestion.name
17
+ ? `getByRole(${JSON.stringify(suggestion.role)}, { name: ${JSON.stringify(suggestion.name)} })`
18
+ : `getByRole(${JSON.stringify(suggestion.role)})`;
19
+ case 'text':
20
+ return `getByText(${JSON.stringify(suggestion.text)})`;
21
+ case 'testId':
22
+ return `getByTestId(${JSON.stringify(suggestion.testId)})`;
23
+ case 'label':
24
+ return `getByLabel(${JSON.stringify(suggestion.label)})`;
25
+ case 'placeholder':
26
+ return `getByPlaceholder(${JSON.stringify(suggestion.placeholder)})`;
27
+ case 'css':
28
+ return `locator(${JSON.stringify(suggestion.css)})`;
29
+ default:
30
+ return undefined;
31
+ }
32
+ }
33
+ // Finds the exact [dotIndex, callEnd) range of a locator-factory call (`.locator(...)`,
34
+ // `.getByRole(...)`, etc.) starting on `targetLine` (1-based) — the exact call site captured at
35
+ // heal time via resolveCallerLocation in bindings/locator.binding.ts. Deliberately not a real
36
+ // parser: a small string/paren-balance scanner anchored at a known line, in the same "good enough
37
+ // for a human to review, not an exhaustive linter" spirit as scanDescribe.ts and
38
+ // scanActionTimeout.ts — and it means this file has zero dependency on whichever TypeScript
39
+ // version (if any) happens to be resolvable in the consumer's own node_modules.
40
+ // `/` starts a regex literal (e.g. `getByRole('button', { name: /submit/i })` — a completely
41
+ // normal Playwright pattern) unless the preceding significant character means it's actually
42
+ // division, which can't happen in a locator argument list except in wildly unrealistic
43
+ // expressions. Standard division-vs-regex disambiguation heuristic: division only follows
44
+ // something that evaluates to a value — an identifier, number, or a closing bracket/paren/quote.
45
+ function isRegexLiteralStart(content, index) {
46
+ let j = index - 1;
47
+ while (j >= 0 && /\s/.test(content[j])) {
48
+ j--;
49
+ }
50
+ if (j < 0) {
51
+ return true;
52
+ }
53
+ return !/[\w$)\]`'"]/.test(content[j]);
54
+ }
55
+ function findFactoryCallOnLine(content, targetLine) {
56
+ const lines = content.split('\n');
57
+ if (targetLine < 1 || targetLine > lines.length) {
58
+ return undefined;
59
+ }
60
+ let lineStartOffset = 0;
61
+ for (let i = 0; i < targetLine - 1; i++) {
62
+ lineStartOffset += lines[i].length + 1; // +1 for the '\n' each split() consumed
63
+ }
64
+ const match = lines[targetLine - 1].match(FACTORY_METHOD_PATTERN);
65
+ if (!match || match.index === undefined) {
66
+ return undefined;
67
+ }
68
+ const dotIndex = lineStartOffset + match.index;
69
+ const openParenIndex = dotIndex + match[0].length - 1;
70
+ let depth = 1;
71
+ let i = openParenIndex + 1;
72
+ // Tracks whichever of `"`, `'`, `` ` ``, or `/` (regex) we're currently inside — parens found
73
+ // while this is set (e.g. an escaped `\(` inside a regex literal, or any paren at all inside a
74
+ // string) don't affect depth, since they're not real call-argument boundaries.
75
+ let inString = null;
76
+ while (i < content.length && depth > 0) {
77
+ const ch = content[i];
78
+ if (inString) {
79
+ if (ch === '\\') {
80
+ i += 2;
81
+ continue;
82
+ }
83
+ if (ch === inString) {
84
+ inString = null;
85
+ }
86
+ }
87
+ else if (ch === '"' || ch === "'" || ch === '`') {
88
+ inString = ch;
89
+ }
90
+ else if (ch === '/' && isRegexLiteralStart(content, i)) {
91
+ inString = '/';
92
+ }
93
+ else if (ch === '(') {
94
+ depth++;
95
+ }
96
+ else if (ch === ')') {
97
+ depth--;
98
+ }
99
+ i++;
100
+ }
101
+ if (depth !== 0) {
102
+ // Unbalanced, or the argument list contains something this scanner can't safely track — bail
103
+ // out rather than guess at a range that might be wrong.
104
+ return undefined;
105
+ }
106
+ return { dotIndex, callEnd: i };
107
+ }
108
+ // Only the most recent entry per file:line survives — later runs supersede earlier ones, and
109
+ // there's nothing useful about applying the same location twice.
110
+ function latestPerLocation(entries) {
111
+ const byKey = new Map();
112
+ for (const entry of entries) {
113
+ const key = `${entry.file}:${entry.line}`;
114
+ const existing = byKey.get(key);
115
+ if (!existing || entry.timestamp > existing.timestamp) {
116
+ byKey.set(key, entry);
117
+ }
118
+ }
119
+ return [...byKey.values()];
120
+ }
121
+ // Computes every fix and the resulting file contents, but never touches disk — the CLI layer
122
+ // decides whether to actually write based on --dry-run. Only the matched `.locator(...)`/
123
+ // `.getByX(...)` text itself is replaced; the receiver (`page`, `this.page`, `container`, ...) and
124
+ // any subsequent chained calls like `.describe(...)` are left completely untouched since they sit
125
+ // outside the replaced range entirely.
126
+ function planFixes(cwd = process.cwd(), rawEntries) {
127
+ const entries = latestPerLocation(rawEntries ?? (0, heal_log_1.readHealLog)(cwd));
128
+ const byFile = new Map();
129
+ for (const entry of entries) {
130
+ const list = byFile.get(entry.file) ?? [];
131
+ list.push(entry);
132
+ byFile.set(entry.file, list);
133
+ }
134
+ const outcomes = [];
135
+ const fileContents = new Map();
136
+ for (const [relativeFile, fileEntries] of byFile) {
137
+ const fullPath = path_1.default.resolve(cwd, relativeFile);
138
+ if (!fs_1.default.existsSync(fullPath)) {
139
+ for (const entry of fileEntries) {
140
+ outcomes.push({ file: relativeFile, line: entry.line, before: '', after: '', applied: false, reason: 'File no longer exists.' });
141
+ }
142
+ continue;
143
+ }
144
+ let content = fs_1.default.readFileSync(fullPath, 'utf-8');
145
+ let changed = false;
146
+ // Bottom-to-top: replacing a later line first means an earlier line's own offsets are never
147
+ // invalidated by a change below it.
148
+ const sortedEntries = [...fileEntries].sort((a, b) => b.line - a.line);
149
+ for (const entry of sortedEntries) {
150
+ const range = findFactoryCallOnLine(content, entry.line);
151
+ const replacement = range ? generateReplacementCall(entry.suggestion) : undefined;
152
+ if (!range || !replacement) {
153
+ outcomes.push({
154
+ file: relativeFile,
155
+ line: entry.line,
156
+ before: '',
157
+ after: '',
158
+ applied: false,
159
+ reason: !range
160
+ ? 'Could not find the original locator call on this line — the file may have changed since this heal was recorded.'
161
+ : `Unsupported suggestion strategy "${entry.suggestion.strategy}".`,
162
+ });
163
+ continue;
164
+ }
165
+ const before = content.slice(range.dotIndex, range.callEnd);
166
+ const after = `.${replacement}`;
167
+ content = content.slice(0, range.dotIndex) + after + content.slice(range.callEnd);
168
+ changed = true;
169
+ outcomes.push({ file: relativeFile, line: entry.line, before, after, applied: true });
170
+ }
171
+ if (changed) {
172
+ fileContents.set(fullPath, content);
173
+ }
174
+ }
175
+ return { outcomes, fileContents };
176
+ }
177
+ const REPORT_DIR = '.tamash-playwright';
178
+ // Filesystem-safe version of an ISO timestamp (`:` isn't valid in a Windows filename) — shared
179
+ // across the report files and the archived heal-log for one run, so all three end up correlated
180
+ // by the same label.
181
+ function timestampLabel() {
182
+ return new Date().toISOString().replace(/[:.]/g, '-');
183
+ }
184
+ // Persists the before/after of every fix (applied or skipped) as both a machine-readable JSON
185
+ // report and a human-readable markdown one — the latter is written specifically so a CI job can
186
+ // hand its contents straight to a PR body (see the "Running apply-heals in CI" README section),
187
+ // so a reviewer sees exactly what changed without having to dig through CI logs or a separate
188
+ // artifact.
189
+ //
190
+ // Written twice: once at a stable filename (what CI and anything else automated should read —
191
+ // always "the latest run"), and once archived under history/<timestamp>.* — otherwise a second
192
+ // apply-heals run silently erases the first's report with no trace, and since .tamash-playwright/
193
+ // is gitignored, that's not recoverable from git either.
194
+ function writeReports(outcomes, dryRun, cwd, label) {
195
+ const dir = path_1.default.join(cwd, REPORT_DIR);
196
+ const historyDir = path_1.default.join(dir, 'history');
197
+ if (!fs_1.default.existsSync(historyDir)) {
198
+ fs_1.default.mkdirSync(historyDir, { recursive: true });
199
+ }
200
+ const applied = outcomes.filter((o) => o.applied);
201
+ const skipped = outcomes.filter((o) => !o.applied);
202
+ const timestamp = new Date().toISOString();
203
+ const jsonContent = JSON.stringify({ timestamp, dryRun, applied: applied.length, skipped: skipped.length, fixes: outcomes }, null, 2);
204
+ const jsonPath = path_1.default.join(dir, 'apply-heals-report.json');
205
+ fs_1.default.writeFileSync(jsonPath, jsonContent, 'utf-8');
206
+ fs_1.default.writeFileSync(path_1.default.join(historyDir, `${label}.apply-heals-report.json`), jsonContent, 'utf-8');
207
+ const lines = [
208
+ '# Self-healing fixes',
209
+ '',
210
+ `Generated ${timestamp} by \`tamash-playwright apply-heals\`${dryRun ? ' (--dry-run, nothing written)' : ''}.`,
211
+ '',
212
+ ];
213
+ if (applied.length > 0) {
214
+ lines.push(`## Applied (${applied.length})`, '');
215
+ for (const fix of applied) {
216
+ lines.push(`### \`${fix.file}:${fix.line}\``, '', '**Before:**', '```ts', fix.before, '```', '', '**After:**', '```ts', fix.after, '```', '');
217
+ }
218
+ }
219
+ if (skipped.length > 0) {
220
+ lines.push(`## Skipped (${skipped.length})`, '');
221
+ for (const fix of skipped) {
222
+ lines.push(`- \`${fix.file}:${fix.line}\` — ${fix.reason}`);
223
+ }
224
+ lines.push('');
225
+ }
226
+ const markdownContent = lines.join('\n');
227
+ const markdownPath = path_1.default.join(dir, 'apply-heals-report.md');
228
+ fs_1.default.writeFileSync(markdownPath, markdownContent, 'utf-8');
229
+ fs_1.default.writeFileSync(path_1.default.join(historyDir, `${label}.apply-heals-report.md`), markdownContent, 'utf-8');
230
+ return { jsonPath, markdownPath };
231
+ }
232
+ function parseArgs(args) {
233
+ const logsDirIndex = args.indexOf('--logs-dir');
234
+ return {
235
+ dryRun: args.includes('--dry-run'),
236
+ logsDir: logsDirIndex !== -1 ? args[logsDirIndex + 1] : undefined,
237
+ };
238
+ }
239
+ async function runApplyHeals(args = []) {
240
+ const { dryRun, logsDir } = parseArgs(args);
241
+ console.log('tamash-playwright apply-heals\n');
242
+ // --logs-dir supports the sharded-CI pattern: a merge job downloads every shard's heals.jsonl
243
+ // artifact into one directory and points here instead of relying on a single local log file.
244
+ const rawEntries = logsDir ? (0, heal_log_1.readHealLogsFromDir)(path_1.default.resolve(process.cwd(), logsDir)) : undefined;
245
+ const { outcomes, fileContents } = planFixes(process.cwd(), rawEntries);
246
+ if (outcomes.length === 0) {
247
+ console.log(logsDir ? `No eligible heals found under ${logsDir}.` : 'No eligible heals found in .tamash-playwright/heals.jsonl.');
248
+ console.log('(Only text/ARIA-based heals with a known source location are eligible — run your tests first.)');
249
+ return;
250
+ }
251
+ const applied = outcomes.filter((o) => o.applied);
252
+ const skipped = outcomes.filter((o) => !o.applied);
253
+ for (const outcome of applied) {
254
+ console.log(`${dryRun ? '[WOULD FIX]' : '[FIX]'} ${outcome.file}:${outcome.line}`);
255
+ console.log(` - ${outcome.before}`);
256
+ console.log(` + ${outcome.after}`);
257
+ }
258
+ for (const outcome of skipped) {
259
+ console.log(`[SKIP] ${outcome.file}:${outcome.line} — ${outcome.reason}`);
260
+ }
261
+ console.log('');
262
+ const label = timestampLabel();
263
+ const { markdownPath } = writeReports(outcomes, dryRun, process.cwd(), label);
264
+ console.log(`Report written to ${path_1.default.relative(process.cwd(), markdownPath)} (and the matching .json).`);
265
+ console.log(`Archived under .tamash-playwright/history/${label}.* — kept even after the next run.`);
266
+ if (dryRun) {
267
+ console.log(`${applied.length} fix(es) would be applied, ${skipped.length} skipped. Re-run without --dry-run to write them.`);
268
+ return;
269
+ }
270
+ for (const [fullPath, content] of fileContents) {
271
+ fs_1.default.writeFileSync(fullPath, content, 'utf-8');
272
+ }
273
+ console.log(`${applied.length} fix(es) applied to ${fileContents.size} file(s), ${skipped.length} skipped.`);
274
+ console.log('Review the changes (e.g. `git diff`) before committing.');
275
+ // Applied entries are now baked into source — clearing avoids re-applying (harmlessly, since a
276
+ // second pass would just fail to find the old call text, but noisily) or double-counting them if
277
+ // apply-heals is run again before the next test run produces new entries. Archived first (using
278
+ // the same label as this run's reports, so all three correlate by filename) so clearing doesn't
279
+ // also erase the only record of what heals.jsonl actually contained.
280
+ (0, heal_log_1.archiveHealLog)(label, process.cwd());
281
+ (0, heal_log_1.clearHealLog)();
282
+ }
283
+ //# sourceMappingURL=applyHeals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyHeals.js","sourceRoot":"","sources":["../../src/cli/applyHeals.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -2,20 +2,25 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const doctor_1 = require("./doctor");
5
+ const applyHeals_1 = require("./applyHeals");
6
+ const USAGE = 'Usage: tamash-playwright doctor [--dir <path>] | apply-heals [--dry-run] [--logs-dir <path>]';
5
7
  async function main() {
6
8
  const [, , command, ...rest] = process.argv;
7
9
  switch (command) {
8
10
  case 'doctor':
9
11
  await (0, doctor_1.runDoctor)(rest);
10
12
  break;
13
+ case 'apply-heals':
14
+ await (0, applyHeals_1.runApplyHeals)(rest);
15
+ break;
11
16
  case undefined:
12
17
  case '--help':
13
18
  case '-h':
14
- console.log('Usage: tamash-playwright doctor [--dir <path>]');
19
+ console.log(USAGE);
15
20
  break;
16
21
  default:
17
22
  console.log(`Unknown command: ${command}`);
18
- console.log('Usage: tamash-playwright doctor [--dir <path>]');
23
+ console.log(USAGE);
19
24
  process.exitCode = 1;
20
25
  }
21
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;;AACA,qCAAqC;AAErC,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,EAAE,AAAD,EAAG,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAE5C,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,MAAM,IAAA,kBAAS,EAAC,IAAI,CAAC,CAAC;YACtB,MAAM;QACR,KAAK,SAAS,CAAC;QACf,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI;YACP,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;YAC9D,MAAM;QACR;YACE,OAAO,CAAC,GAAG,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;YAC9D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACzB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;;AACA,qCAAqC;AACrC,6CAA6C;AAE7C,MAAM,KAAK,GAAG,8FAA8F,CAAC;AAE7G,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,EAAE,AAAD,EAAG,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAE5C,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,MAAM,IAAA,kBAAS,EAAC,IAAI,CAAC,CAAC;YACtB,MAAM;QACR,KAAK,aAAa;YAChB,MAAM,IAAA,0BAAa,EAAC,IAAI,CAAC,CAAC;YAC1B,MAAM;QACR,KAAK,SAAS,CAAC;QACf,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI;YACP,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACnB,MAAM;QACR;YACE,OAAO,CAAC,GAAG,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACzB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { SelectorSuggestion } from './providers/types';
2
+ export type HealLogEntry = {
3
+ timestamp: string;
4
+ file: string;
5
+ line: number;
6
+ action: string;
7
+ description?: string;
8
+ suggestion: SelectorSuggestion;
9
+ };
10
+ export declare function parseSourceLocation(sourceLocation: string): {
11
+ file: string;
12
+ line: number;
13
+ } | undefined;
14
+ export declare function appendHealLogEntry(entry: HealLogEntry, cwd?: string): void;
15
+ export declare function readHealLog(cwd?: string): HealLogEntry[];
16
+ export declare function readHealLogsFromDir(dir: string): HealLogEntry[];
17
+ export declare function findCachedSuggestion(sourceLocation: string, cwd?: string): SelectorSuggestion | undefined;
18
+ export declare function archiveHealLog(label: string, cwd?: string): void;
19
+ export declare function clearHealLog(cwd?: string): void;
20
+ //# sourceMappingURL=heal-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heal-log.d.ts","sourceRoot":"","sources":["../../src/healer/heal-log.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAO5D,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,kBAAkB,CAAC;CAChC,CAAC;AAYF,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAYtG;AAKD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,GAAE,MAAsB,GAAG,IAAI,CAUzF;AAmBD,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAsB,GAAG,YAAY,EAAE,CAEvE;AAOD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAe/D;AAQD,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,kBAAkB,GAAG,SAAS,CAiBxH;AAOD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,IAAI,CAc/E;AAED,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAsB,GAAG,IAAI,CAM9D"}
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseSourceLocation = parseSourceLocation;
7
+ exports.appendHealLogEntry = appendHealLogEntry;
8
+ exports.readHealLog = readHealLog;
9
+ exports.readHealLogsFromDir = readHealLogsFromDir;
10
+ exports.findCachedSuggestion = findCachedSuggestion;
11
+ exports.archiveHealLog = archiveHealLog;
12
+ exports.clearHealLog = clearHealLog;
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const path_1 = __importDefault(require("path"));
15
+ const LOG_DIR = '.tamash-playwright';
16
+ const LOG_FILE = 'heals.jsonl';
17
+ function logPath(cwd) {
18
+ return path_1.default.join(cwd, LOG_DIR, LOG_FILE);
19
+ }
20
+ // sourceLocation is always "file:line" (see resolveCallerLocation in bindings/locator.binding.ts)
21
+ // — relative paths never contain a colon, even on Windows, since path.relative() already strips
22
+ // any drive letter, so splitting on the *last* colon is safe.
23
+ function parseSourceLocation(sourceLocation) {
24
+ const separatorIndex = sourceLocation.lastIndexOf(':');
25
+ if (separatorIndex === -1) {
26
+ return undefined;
27
+ }
28
+ const line = Number(sourceLocation.slice(separatorIndex + 1));
29
+ if (!Number.isFinite(line)) {
30
+ return undefined;
31
+ }
32
+ return { file: sourceLocation.slice(0, separatorIndex), line };
33
+ }
34
+ // Best-effort by design: a logging failure (disk full, permissions, whatever) must never break
35
+ // the test run that triggered it — this is a convenience trail for `apply-heals`, not something
36
+ // any test's pass/fail should ever depend on.
37
+ function appendHealLogEntry(entry, cwd = process.cwd()) {
38
+ try {
39
+ const dir = path_1.default.join(cwd, LOG_DIR);
40
+ if (!fs_1.default.existsSync(dir)) {
41
+ fs_1.default.mkdirSync(dir, { recursive: true });
42
+ }
43
+ fs_1.default.appendFileSync(logPath(cwd), `${JSON.stringify(entry)}\n`, 'utf-8');
44
+ }
45
+ catch {
46
+ // See comment above — swallow deliberately.
47
+ }
48
+ }
49
+ function parseHealLogFile(filePath) {
50
+ if (!fs_1.default.existsSync(filePath)) {
51
+ return [];
52
+ }
53
+ const entries = [];
54
+ for (const line of fs_1.default.readFileSync(filePath, 'utf-8').split('\n')) {
55
+ if (!line.trim())
56
+ continue;
57
+ try {
58
+ entries.push(JSON.parse(line));
59
+ }
60
+ catch {
61
+ // A single corrupted line (e.g. a run killed mid-write) shouldn't take down the whole log.
62
+ }
63
+ }
64
+ return entries;
65
+ }
66
+ function readHealLog(cwd = process.cwd()) {
67
+ return parseHealLogFile(logPath(cwd));
68
+ }
69
+ // Supports the sharded-CI pattern: each shard uploads its own heals.jsonl as a CI artifact, a
70
+ // separate job downloads all of them (each landing in its own subdirectory, e.g.
71
+ // `shard-logs/heals-log-1/heals.jsonl`), and this reads every `heals.jsonl` found anywhere under
72
+ // `dir` into one combined list — `apply-heals`'s own per-location dedup (see applyHeals.ts) then
73
+ // takes care of two shards having (harmlessly) healed the same line.
74
+ function readHealLogsFromDir(dir) {
75
+ if (!fs_1.default.existsSync(dir)) {
76
+ return [];
77
+ }
78
+ const entries = [];
79
+ for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
80
+ const fullPath = path_1.default.join(dir, entry.name);
81
+ if (entry.isDirectory()) {
82
+ entries.push(...readHealLogsFromDir(fullPath));
83
+ }
84
+ else if (entry.name === LOG_FILE) {
85
+ entries.push(...parseHealLogFile(fullPath));
86
+ }
87
+ }
88
+ return entries;
89
+ }
90
+ // The read side of the same log `apply-heals` consumes, reused here as an in-run/cross-run cache:
91
+ // before healActionFailure pays for an ARIA snapshot and an AI call, it checks whether this exact
92
+ // source location has already healed successfully before and, if so, tries that suggestion first
93
+ // — no snapshot, no AI call, no tokens spent. A stale entry just fails to replay and the caller
94
+ // falls straight through to the normal fresh-AI flow, so there's no correctness risk in trusting
95
+ // this opportunistically.
96
+ function findCachedSuggestion(sourceLocation, cwd = process.cwd()) {
97
+ const location = parseSourceLocation(sourceLocation);
98
+ if (!location) {
99
+ return undefined;
100
+ }
101
+ let newest;
102
+ for (const entry of readHealLog(cwd)) {
103
+ if (entry.file !== location.file || entry.line !== location.line) {
104
+ continue;
105
+ }
106
+ if (!newest || entry.timestamp > newest.timestamp) {
107
+ newest = entry;
108
+ }
109
+ }
110
+ return newest?.suggestion;
111
+ }
112
+ // Preserves the raw log (including fields the rendered report doesn't carry, like each entry's
113
+ // own timestamp and description) before clearHealLog deletes the active one — otherwise a second
114
+ // apply-heals run leaves zero trace of what the first one was based on, since .tamash-playwright/
115
+ // is gitignored and never committed. `label` should match whatever writeReports (applyHeals.ts)
116
+ // used for that same run's report files, so all three end up correlated by filename.
117
+ function archiveHealLog(label, cwd = process.cwd()) {
118
+ try {
119
+ const src = logPath(cwd);
120
+ if (!fs_1.default.existsSync(src)) {
121
+ return;
122
+ }
123
+ const historyDir = path_1.default.join(cwd, LOG_DIR, 'history');
124
+ if (!fs_1.default.existsSync(historyDir)) {
125
+ fs_1.default.mkdirSync(historyDir, { recursive: true });
126
+ }
127
+ fs_1.default.copyFileSync(src, path_1.default.join(historyDir, `${label}.heals.jsonl`));
128
+ }
129
+ catch {
130
+ // Same best-effort stance as appendHealLogEntry — archiving must never break apply-heals.
131
+ }
132
+ }
133
+ function clearHealLog(cwd = process.cwd()) {
134
+ try {
135
+ fs_1.default.rmSync(logPath(cwd), { force: true });
136
+ }
137
+ catch {
138
+ // Same best-effort stance as appendHealLogEntry.
139
+ }
140
+ }
141
+ //# sourceMappingURL=heal-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heal-log.js","sourceRoot":"","sources":["../../src/healer/heal-log.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/healer/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI3E,OAAO,KAAK,EAAoC,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAOtF,KAAK,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,cAAc,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IAKtB,UAAU,EAAE,OAAO,CAAC;IAIpB,kBAAkB,EAAE,OAAO,CAAC;IAK5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAsSF,wBAAgB,gBAAgB,IAAI,OAAO,CAG1C;AAMD,wBAAgB,uBAAuB,IAAI,OAAO,CAEjD;AAmED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,KAAK,CAAC;IAChC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IAKrB,UAAU,CAAC,EAAE,YAAY,CAAC;IAG1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAyJ/E;AAED,wBAAgB,iBAAiB,IAAI,iBAAiB,EAAE,CAEvD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/healer/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI3E,OAAO,KAAK,EAAoC,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAQtF,KAAK,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,cAAc,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IAKtB,UAAU,EAAE,OAAO,CAAC;IAIpB,kBAAkB,EAAE,OAAO,CAAC;IAK5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAsSF,wBAAgB,gBAAgB,IAAI,OAAO,CAG1C;AAMD,wBAAgB,uBAAuB,IAAI,OAAO,CAEjD;AA4FD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,KAAK,CAAC;IAChC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IAKrB,UAAU,CAAC,EAAE,YAAY,CAAC;IAG1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAqM/E;AAED,wBAAgB,iBAAiB,IAAI,iBAAiB,EAAE,CAEvD"}
@@ -14,6 +14,7 @@ const providers_1 = require("./providers");
14
14
  const vision_1 = require("./vision");
15
15
  const replay_action_1 = require("./replay-action");
16
16
  const action_recovery_1 = require("./action-recovery");
17
+ const heal_log_1 = require("./heal-log");
17
18
  dotenv_1.default.config({ path: path_1.default.resolve(process.cwd(), '.env') });
18
19
  const reports = [];
19
20
  function normalizeError(error) {
@@ -318,6 +319,28 @@ function attachReportToRunningTest(report) {
318
319
  // Not running inside a Playwright test worker (e.g. a unit test calling this directly) — skip.
319
320
  }
320
321
  }
322
+ // Feeds `npx tamash-playwright apply-heals` (src/cli/applyHeals.ts), which rewrites the original
323
+ // source line to the healed selector so the next run doesn't need to heal it at all. Deliberately
324
+ // narrow about what's eligible: vision heals have no reusable source form (the "selector" is a
325
+ // runtime screenshot coordinate, tagged onto a temporary DOM attribute), and an action-recovery
326
+ // heal means the *locator* was already right — nothing about the selector itself needs fixing.
327
+ function logEligibleHeal(report, suggestion, usedVision, usedActionRecovery) {
328
+ if (!report.healed || !report.sourceLocation || !suggestion || usedVision || usedActionRecovery) {
329
+ return;
330
+ }
331
+ const location = (0, heal_log_1.parseSourceLocation)(report.sourceLocation);
332
+ if (!location) {
333
+ return;
334
+ }
335
+ (0, heal_log_1.appendHealLogEntry)({
336
+ timestamp: new Date().toISOString(),
337
+ file: location.file,
338
+ line: location.line,
339
+ action: report.action,
340
+ description: report.description,
341
+ suggestion,
342
+ });
343
+ }
321
344
  async function healActionFailure(context) {
322
345
  const reason = normalizeError(context.error);
323
346
  const callArgs = context.callArgs ?? [];
@@ -341,17 +364,53 @@ async function healActionFailure(context) {
341
364
  // problem — no point paying for a snapshot (or an LLM call) just to throw the result away.
342
365
  const actionTimeoutMs = resolveActionTimeoutMs();
343
366
  const pageContext = attemptHealing ? (context.frameScope ?? (await resolvePageContext(context.target))) : undefined;
344
- const ariaSnapshot = attemptHealing ? await captureAriaSnapshot(pageContext, actionTimeoutMs ?? 2000) : undefined;
345
367
  let healing = null;
346
368
  let usage;
347
369
  let suggestedSelector;
348
370
  let providerName;
349
371
  let usedVision = false;
350
372
  let usedActionRecovery = false;
373
+ let usedCache = false;
374
+ // The structured suggestion behind suggestedSelector's human-readable string — kept separately
375
+ // so `apply-heals` (src/cli/applyHeals.ts) can regenerate real Playwright source from it, rather
376
+ // than trying to re-parse a display string like `role:button:Submit` back into data.
377
+ let capturedSuggestion;
378
+ // Tried before paying for anything else: if this exact source location has already healed
379
+ // successfully before — this run, or a previous one, since the log persists on disk (see
380
+ // heal-log.ts) — try that suggestion directly. No ARIA snapshot, no AI call, no tokens spent.
381
+ // Deliberately independent of whether a provider is even configured: a selector already proven
382
+ // to work doesn't need one. A stale entry (the page changed again) just fails to replay and
383
+ // execution falls straight through to the normal snapshot+AI flow below, so there's no
384
+ // correctness risk in trying this opportunistically — only ever a cost/time saving when it works.
385
+ if (attemptHealing && pageContext && context.sourceLocation) {
386
+ const cached = (0, heal_log_1.findCachedSuggestion)(context.sourceLocation);
387
+ if (cached) {
388
+ const locator = buildLocatorFromSuggestion(pageContext, cached);
389
+ if (locator) {
390
+ try {
391
+ const replayResult = await (0, replay_action_1.replayAction)(locator, context.action, callArgs);
392
+ suggestedSelector = describeSuggestion(cached);
393
+ capturedSuggestion = cached;
394
+ usedCache = true;
395
+ healing = {
396
+ provider: 'cache',
397
+ warning: `Recovered using a previously-confirmed selector (${suggestedSelector ?? 'cached selector'}) — no AI call needed.`,
398
+ suggestedSelector,
399
+ result: replayResult,
400
+ };
401
+ failureStage = undefined;
402
+ }
403
+ catch {
404
+ // Cached suggestion no longer resolves — fall through to the normal flow below.
405
+ }
406
+ }
407
+ }
408
+ }
409
+ const ariaSnapshot = attemptHealing && !healing ? await captureAriaSnapshot(pageContext, actionTimeoutMs ?? 2000) : undefined;
351
410
  // pageContext alone (not `&& ariaSnapshot`) is enough to enter this block now: the vision
352
411
  // fallback below needs a provider and a pageContext to screenshot, but has no use for an ARIA
353
412
  // snapshot at all — it's specifically useful for the `no_snapshot` case too.
354
- if (attemptHealing && pageContext) {
413
+ if (attemptHealing && !healing && pageContext) {
355
414
  const provider = (0, providers_1.getHealProvider)();
356
415
  if (!provider) {
357
416
  failureStage = 'no_provider';
@@ -380,6 +439,7 @@ async function healActionFailure(context) {
380
439
  // turned out to also fail is exactly the case worth seeing in the report, not
381
440
  // silently discarding in favor of the original error alone.
382
441
  suggestedSelector = describeSuggestion(suggestion);
442
+ capturedSuggestion = suggestion;
383
443
  failureStage = 'replay_failed';
384
444
  try {
385
445
  const replayResult = await (0, replay_action_1.replayAction)(locator, context.action, callArgs);
@@ -469,6 +529,12 @@ async function healActionFailure(context) {
469
529
  console.warn(formatConsoleLine(report));
470
530
  }
471
531
  attachReportToRunningTest(report);
532
+ // A cache hit is already backed by an existing log entry (that's what made it a hit) — re-logging
533
+ // it on every single confirmation would just grow the log unboundedly for a fix nobody's applied
534
+ // yet, with no new information to add.
535
+ if (!usedCache) {
536
+ logEligibleHeal(report, capturedSuggestion, usedVision, usedActionRecovery);
537
+ }
472
538
  return {
473
539
  report,
474
540
  recovered: Boolean(healing),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/healer/index.ts"],"names":[],"mappings":";;;;;;;;;AAAA,gDAAwB;AAExB,2CAAwC;AACxC,oDAA4B;AAC5B,2CAA8C;AAE9C,qCAAuI;AACvI,mDAA+C;AAC/C,uDAAsD;AAEtD,gBAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,cAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;AA+B7D,MAAM,OAAO,GAAwB,EAAE,CAAC;AAExC,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,wBAAwB,CAAC;AAClC,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC;AAED,+FAA+F;AAC/F,2FAA2F;AAC3F,gGAAgG;AAChG,2FAA2F;AAC3F,2CAA2C;AAC3C,SAAS,sBAAsB;IAC7B,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,WAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;QAC5D,OAAO,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,MAA+B;IAC/D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAQ,MAAkB,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACnD,IAAI,CAAC;YACH,OAAO,MAAO,MAAkB,CAAC,IAAI,EAAE,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,MAAsB,CAAC;AAChC,CAAC;AAED,sFAAsF;AACtF,0FAA0F;AAC1F,oEAAoE;AACpE,KAAK,UAAU,mBAAmB,CAAC,WAAoC,EAAE,SAAiB;IACxF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC;YACH,OAAO,MAAM,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACjF,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,0BAA0B,CAAC,WAAwB,EAAE,UAA8B;IAC1F,QAAQ,UAAU,CAAC,QAAQ,EAAE,CAAC;QAC5B,KAAK,MAAM;YACT,OAAO,WAAW,CAAC,SAAS,CAAC,UAAU,CAAC,IAAa,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACjJ,KAAK,MAAM;YACT,OAAO,WAAW,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,KAAK,QAAQ;YACX,OAAO,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACpD,KAAK,OAAO;YACV,OAAO,WAAW,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QACpE,KAAK,aAAa;YAChB,OAAO,WAAW,CAAC,gBAAgB,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAChF,KAAK,KAAK;YACR,OAAO,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC7C;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,UAA8B;IACxD,QAAQ,UAAU,CAAC,QAAQ,EAAE,CAAC;QAC5B,KAAK,MAAM;YACT,OAAO,QAAQ,UAAU,CAAC,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAClF,KAAK,MAAM;YACT,OAAO,QAAQ,UAAU,CAAC,IAAI,EAAE,CAAC;QACnC,KAAK,QAAQ;YACX,OAAO,UAAU,UAAU,CAAC,MAAM,EAAE,CAAC;QACvC,KAAK,OAAO;YACV,OAAO,SAAS,UAAU,CAAC,KAAK,EAAE,CAAC;QACrC,KAAK,aAAa;YAChB,OAAO,eAAe,UAAU,CAAC,WAAW,EAAE,CAAC;QACjD,KAAK,KAAK;YACR,OAAO,OAAO,UAAU,CAAC,GAAG,EAAE,CAAC;QACjC;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AA8BD,gGAAgG;AAChG,+FAA+F;AAC/F,gGAAgG;AAChG,8FAA8F;AAC9F,uFAAuF;AACvF,KAAK,UAAU,6BAA6B,CAC1C,QAAsB,EACtB,OAAgB,EAChB,MAAc,EACd,QAAmB,EACnB,WAAoB,EACpB,SAA6B,EAC7B,0BAA8C,EAC9C,SAAiC;IAEjC,MAAM,cAAc,GAAG,MAAM,IAAA,mCAAiB,EAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC,CAAC;IAC5H,MAAM,aAAa,GAAG,SAAS,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC;IAE/I,IAAI,cAAc,CAAC,OAAO,EAAE,CAAC;QAC3B,OAAO;YACL,OAAO,EAAE;gBACP,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,QAAQ;gBACzC,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO;gBACvC,iBAAiB,EAAE,0BAA0B;gBAC7C,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM;aACtC;YACD,KAAK,EAAE,aAAa;SACrB,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,cAAc,CAAC,KAAK,EAAE,CAAC;AAC9E,CAAC;AAED,gGAAgG;AAChG,4FAA4F;AAC5F,+FAA+F;AAC/F,sFAAsF;AACtF,kGAAkG;AAClG,gGAAgG;AAChG,+BAA+B;AAC/B,KAAK,UAAU,iBAAiB,CAC9B,QAAsB,EACtB,WAAwB,EACxB,MAAc,EACd,WAA+B,EAC/B,QAAmB,EACnB,SAA6B;IAE7B,MAAM,kBAAkB,GAAG,SAAS,IAAI,IAAI,CAAC;IAC7C,MAAM,OAAO,GAAG,MAAM,IAAA,gCAAuB,EAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;IAC/E,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,wBAAwB,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC;IAC7H,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC;IAC3D,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAChC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC5D,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,IAAA,oCAA2B,EAAC,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAClH,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC;IAChE,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,IAAA,0BAAiB,EAAC,EAAE,CAAC,CAAC,CAAC;IAE3D,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,IAAA,4BAAY,EAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACnE,OAAO;YACL,OAAO,EAAE;gBACP,QAAQ,EAAE,QAAQ,CAAC,IAAI;gBACvB,OAAO,EAAE,mBAAmB,QAAQ,CAAC,IAAI,oBAAoB;gBAC7D,iBAAiB,EAAE,oBAAoB,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,GAAG;gBAC5D,MAAM,EAAE,YAAY;aACrB;YACD,KAAK;SACN,CAAC;IACJ,CAAC;IAAC,OAAO,WAAW,EAAE,CAAC;QACrB,IAAI,CAAC,uBAAuB,EAAE,EAAE,CAAC;YAC/B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC;QACjE,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,6BAA6B,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,oBAAoB,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACpK,OAAO,EAAE,GAAG,QAAQ,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACnD,CAAC;YAAS,CAAC;QACT,MAAM,IAAA,yBAAgB,EAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC;AAED,gGAAgG;AAChG,+FAA+F;AAC/F,iGAAiG;AACjG,gGAAgG;AAChG,mGAAmG;AACnG,mGAAmG;AACnG,mGAAmG;AACnG,8FAA8F;AAC9F,MAAM,sBAAsB,GAA2B;IACrD,QAAQ,EAAE,+CAA+C;IACzD,sBAAsB,EAAE,sIAAsI;IAC9J,WAAW,EAAE,gEAAgE;IAC7E,WAAW,EAAE,uDAAuD;IACpE,cAAc,EAAE,oIAAoI;IACpJ,WAAW,EAAE,yGAAyG;IACtH,sBAAsB,EAAE,gEAAgE;IACxF,aAAa,EAAE,sEAAsE;IACrF,2FAA2F;IAC3F,yFAAyF;IACzF,qFAAqF;IACrF,qBAAqB,EAAE,uGAAuG;IAC9H,eAAe,EAAE,uGAAuG;IACxH,mBAAmB,EAAE,yIAAyI;IAC9J,oBAAoB,EAAE,+EAA+E;IACrG,0FAA0F;IAC1F,4FAA4F;IAC5F,sFAAsF;IACtF,wBAAwB,EAAE,qHAAqH;IAC/I,sBAAsB,EAAE,mHAAmH;CAC5I,CAAC;AAEF,SAAS,WAAW,CAAC,CAAU,EAAE,CAAU;IACzC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;QACvC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,0FAA0F;AAC1F,8FAA8F;AAC9F,iFAAiF;AACjF,SAAS,aAAa,CAAC,CAAa,EAAE,CAAa;IACjD,OAAO;QACL,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC;QACtD,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,YAAY,CAAC;QACzD,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC;KACvD,CAAC;AACJ,CAAC;AAED,kGAAkG;AAClG,iGAAiG;AACjG,4FAA4F;AAC5F,6FAA6F;AAC7F,iEAAiE;AACjE,EAAE;AACF,uFAAuF;AACvF,wFAAwF;AACxF,+FAA+F;AAC/F,iGAAiG;AACjG,oFAAoF;AACpF,SAAS,sBAAsB,CAAC,MAAc;IAC5C,OAAO,yBAAyB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAChD,CAAC;AAED,+FAA+F;AAC/F,qFAAqF;AACrF;IACE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/D,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,GAAG,CAAC;AAC5C,CAAC;AAED,2FAA2F;AAC3F,gGAAgG;AAChG,kGAAkG;AAClG,iBAAiB;AACjB;IACE,OAAO,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;AACrF,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAiB;IACzC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,QAAQ,CAAC,CAAC;IAC9E,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,YAAY,SAAS,CAAC,CAAC;IACjF,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,OAAO,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,WAAW,UAAU,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC;AAC5G,CAAC;AAED,iGAAiG;AACjG,kGAAkG;AAClG,8FAA8F;AAC9F,mGAAmG;AACnG,kGAAkG;AAClG,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,MAAyB;IAClD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC;IACxD,MAAM,IAAI,GAAa;QACrB,YAAY,MAAM,CAAC,QAAQ,EAAE;QAC7B,UAAU,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;QAC5C,kBAAkB,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;KAC7D,CAAC;IACF,IAAI,MAAM,CAAC,iBAAiB,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,iBAAiB,GAAG,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,8FAA8F;QAC9F,6FAA6F;QAC7F,2BAA2B;QAC3B,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,+FAA+F;IAC/F,iGAAiG;IACjG,oDAAoD;IACpD,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,8FAA8F;IAC9F,2FAA2F;IAC3F,8CAA8C;IAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,cAAc,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,OAAO,iBAAiB,QAAQ,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,GAAG,WAAW,OAAO,OAAO,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,eAAe,EAAE,CAAC;AAC1I,CAAC;AAED,SAAS,yBAAyB,CAAC,MAAyB;IAC1D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,WAAI,CAAC,IAAI,EAAE,CAAC;QAC7B,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,kBAAkB,EAAE,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QACrH,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC;gBACxB,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,GAAG,MAAM,CAAC,QAAQ,MAAM,MAAM,CAAC,MAAM,KAAK,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE;aAC7F,CAAC,CAAC;QACL,CAAC;QACD,KAAK,QAAQ,CAAC,MAAM,CAAC,gBAAgB,MAAM,CAAC,MAAM,EAAE,EAAE;YACpD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACrC,WAAW,EAAE,kBAAkB;SAChC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,+FAA+F;IACjG,CAAC;AACH,CAAC;AAEM,KAAK,4BAA4B,OAevC;IACC,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;IACxC,MAAM,cAAc,GAAG,gBAAgB,EAAE,CAAC;IAC1C,MAAM,oBAAoB,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC5D,MAAM,cAAc,GAAG,cAAc,IAAI,CAAC,oBAAoB,CAAC;IAE/D,4FAA4F;IAC5F,6BAA6B;IAC7B,IAAI,YAAgC,CAAC;IACrC,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,YAAY,GAAG,UAAU,CAAC;IAC5B,CAAC;SAAM,IAAI,oBAAoB,EAAE,CAAC;QAChC,YAAY,GAAG,sBAAsB,CAAC;IACxC,CAAC;SAAM,CAAC;QACN,YAAY,GAAG,aAAa,CAAC,CAAC,yCAAyC;IACzE,CAAC;IAED,yFAAyF;IACzF,+FAA+F;IAC/F,2FAA2F;IAC3F,MAAM,eAAe,GAAG,sBAAsB,EAAE,CAAC;IACjD,MAAM,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,MAAM,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACpH,MAAM,YAAY,GAAG,cAAc,CAAC,CAAC,CAAC,MAAM,mBAAmB,CAAC,WAAW,EAAE,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAElH,IAAI,OAAO,GAA2B,IAAI,CAAC;IAC3C,IAAI,KAA6B,CAAC;IAClC,IAAI,iBAAqC,CAAC;IAC1C,IAAI,YAAgC,CAAC;IACrC,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAE/B,0FAA0F;IAC1F,8FAA8F;IAC9F,6EAA6E;IAC7E,IAAI,cAAc,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAA,2BAAe,GAAE,CAAC;QACnC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,YAAY,GAAG,aAAa,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC;YAE7B,IAAI,YAAY,EAAE,CAAC;gBACjB,sFAAsF;gBACtF,0FAA0F;gBAC1F,4DAA4D;gBAC5D,YAAY,GAAG,gBAAgB,CAAC;gBAChC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;gBACtJ,IAAI,MAAM,EAAE,CAAC;oBACX,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;oBACrB,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;oBAC9B,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;wBAClD,YAAY,GAAG,aAAa,CAAC;oBAC/B,CAAC;yBAAM,CAAC;wBACN,MAAM,OAAO,GAAG,0BAA0B,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;wBACpE,IAAI,CAAC,OAAO,EAAE,CAAC;4BACb,YAAY,GAAG,wBAAwB,CAAC;wBAC1C,CAAC;6BAAM,CAAC;4BACN,gFAAgF;4BAChF,8EAA8E;4BAC9E,4DAA4D;4BAC5D,iBAAiB,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;4BACnD,YAAY,GAAG,eAAe,CAAC;4BAC/B,IAAI,CAAC;gCACH,MAAM,YAAY,GAAG,MAAM,IAAA,4BAAY,EAAC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gCAC3E,OAAO,GAAG;oCACR,QAAQ,EAAE,QAAQ,CAAC,IAAI;oCACvB,OAAO,EAAE,mBAAmB,QAAQ,CAAC,IAAI,KAAK,iBAAiB,IAAI,oBAAoB,IAAI;oCAC3F,iBAAiB;oCACjB,MAAM,EAAE,YAAY;iCACrB,CAAC;gCACF,YAAY,GAAG,SAAS,CAAC;4BAC3B,CAAC;4BAAC,OAAO,WAAW,EAAE,CAAC;gCACrB,IAAI,uBAAuB,EAAE,EAAE,CAAC;oCAC9B,MAAM,QAAQ,GAAG,MAAM,6BAA6B,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAC;oCAC1J,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;oCACvB,kBAAkB,GAAG,IAAI,CAAC;oCAC1B,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;oCAC3B,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;gCAC/D,CAAC;qCAAM,CAAC;oCACN,OAAO,GAAG,IAAI,CAAC;gCACjB,CAAC;4BACH,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAED,qFAAqF;YACrF,0FAA0F;YAC1F,uCAAuC;YACvC,IAAI,CAAC,OAAO,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;gBACxC,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;gBACrI,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;oBACxB,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC;gBAClF,CAAC;gBACD,IAAI,aAAa,CAAC,kBAAkB,EAAE,CAAC;oBACrC,kBAAkB,GAAG,IAAI,CAAC;gBAC5B,CAAC;gBACD,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;oBAC1B,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC;oBAChC,UAAU,GAAG,IAAI,CAAC;oBAClB,YAAY,GAAG,SAAS,CAAC;gBAC3B,CAAC;qBAAM,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;oBAC/B,UAAU,GAAG,IAAI,CAAC;oBAClB,YAAY,GAAG,aAAa,CAAC,KAAK,CAAC;gBACrC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,OAAe,CAAC;IACpB,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAC5B,CAAC;SAAM,IAAI,YAAY,KAAK,eAAe,EAAE,CAAC;QAC5C,OAAO,GAAG,WAAW,OAAO,CAAC,MAAM,aAAa,MAAM,mBAAmB,iBAAiB,yBAAyB,CAAC;IACtH,CAAC;SAAM,IAAI,YAAY,KAAK,sBAAsB,EAAE,CAAC;QACnD,OAAO,GAAG,WAAW,OAAO,CAAC,MAAM,aAAa,MAAM,2DAA2D,CAAC;IACpH,CAAC;SAAM,IAAI,YAAY,EAAE,CAAC;QACxB,OAAO,GAAG,WAAW,OAAO,CAAC,MAAM,aAAa,MAAM,MAAM,sBAAsB,CAAC,YAAY,CAAC,GAAG,CAAC;IACtG,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,WAAW,OAAO,CAAC,MAAM,aAAa,MAAM,GAAG,CAAC;IAC5D,CAAC;IAED,MAAM,MAAM,GAAsB;QAChC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,YAAY,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QAC1H,UAAU,EAAE,KAAK;QACjB,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC;QACxB,OAAO;QACP,MAAM;QACN,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,IAAI,iBAAiB;QAClE,YAAY;QACZ,UAAU;QACV,kBAAkB;QAClB,cAAc,EAAE,OAAO,CAAC,cAAc;KACvC,CAAC;IAEF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;IACzC,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,CAAC;IACD,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAElC,OAAO;QACL,MAAM;QACN,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC;QAC3B,MAAM,EAAE,OAAO,EAAE,MAAM;KACxB,CAAC;AACJ,CAAC;AAED;IACE,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/healer/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamash-playwright",
3
- "version": "0.6.1",
3
+ "version": "0.7.0-beta.1",
4
4
  "description": "Plug and Play Self-healing for Playwright and automatically recovers broken selectors using an AI model (Ollama, OpenAI, Anthropic, or Gemini).",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "dist",
12
12
  "README.md",
13
+ "usage.md",
13
14
  ".env.example"
14
15
  ],
15
16
  "scripts": {
package/usage.md ADDED
@@ -0,0 +1,351 @@
1
+ # tamash-playwright — Usage Guide
2
+
3
+ Everything from a five-minute install to wiring a sharded CI pipeline that opens pull requests for its own fixes.
4
+
5
+ For a quicker, high-level overview see [README.md](README.md); this file is the complete reference.
6
+
7
+ ## Contents
8
+
9
+ - [Why you need this](#why-you-need-this)
10
+ - [1. Install](#1-install)
11
+ - [2. Connect an AI model](#2-connect-an-ai-model)
12
+ - [Required: set `actionTimeout`](#required-set-actiontimeout)
13
+ - [3. Check your setup](#3-check-your-setup)
14
+ - [4. Use it in your tests](#4-use-it-in-your-tests)
15
+ - [What else it heals — no extra setup](#what-else-it-heals--no-extra-setup)
16
+ - [Vision fallback](#when-text-alone-isnt-enough-vision-fallback)
17
+ - [Action recovery](#action-recovery-optional)
18
+ - [Not paying for the same heal twice](#not-paying-for-the-same-heal-twice)
19
+ - [Making a heal permanent: `apply-heals`](#making-a-heal-permanent-apply-heals)
20
+ - [Running `apply-heals` in CI](#running-apply-heals-in-ci)
21
+ - [Reading reports](#reading-reports)
22
+ - [Environment variables](#environment-variables)
23
+ - [CLI commands](#cli-commands)
24
+
25
+ ## Why you need this
26
+
27
+ Websites change often. A button gets renamed or moved, and your test can't find it anymore — even though the app still works fine for real users. Normally, that just means a broken test and a maintenance chore.
28
+
29
+ `tamash-playwright` fixes this at the point of failure. When a Playwright action can't find its element, it asks a configured AI model to find it on the current page and tries again. If it succeeds, your test keeps going. If not, it fails exactly as it would have without the package — healing never masks a real failure.
30
+
31
+ It's a drop-in replacement for `@playwright/test`'s own `test`/`expect` — you don't rewrite your tests to use it.
32
+
33
+ ## 1. Install
34
+
35
+ ```sh
36
+ npm install tamash-playwright
37
+ npm install -D @playwright/test # if you don't already have it
38
+ ```
39
+
40
+ That's the whole install. No config files created, no postinstall scripts.
41
+
42
+ ## 2. Connect an AI model
43
+
44
+ `tamash-playwright` needs an AI model to decide where a broken element actually went. Pick one of Ollama, OpenAI, Anthropic (Claude), or Google Gemini, and give it an API key in a `.env` file at your project root.
45
+
46
+ ```sh
47
+ # Master on/off switch. Leave this as true, or remove the line entirely.
48
+ HEALER_ENABLED=true
49
+
50
+ # Pick one: ollama | openai | anthropic | gemini
51
+ HEALER_PROVIDER=ollama
52
+
53
+ # Optional, off by default — see "Action recovery" below.
54
+ # HEALER_ACTION_RECOVERY_ENABLED=true
55
+
56
+ # --- Ollama Cloud (https://ollama.com) ---
57
+ OLLAMA_MODEL=gpt-oss:120b
58
+ OLLAMA_API_KEY=
59
+
60
+ # --- OpenAI ---
61
+ # OPENAI_MODEL=gpt-4.1-mini
62
+ # OPENAI_API_KEY=
63
+
64
+ # --- Anthropic (Claude) ---
65
+ # ANTHROPIC_MODEL=claude-haiku-4-5
66
+ # ANTHROPIC_API_KEY=
67
+
68
+ # --- Google Gemini ---
69
+ # GEMINI_MODEL=
70
+ # GEMINI_API_KEY=
71
+ ```
72
+
73
+ Fill in the API key and model for whichever provider you pick, and leave the rest as-is or delete them.
74
+
75
+ ### Fastest free option: Ollama Cloud
76
+
77
+ 1. Create an account at [ollama.com](https://ollama.com/).
78
+ 2. Go to [ollama.com/settings/keys](https://ollama.com/settings/keys) and create a new API key.
79
+ 3. Paste it into `.env` as `OLLAMA_API_KEY`. `HEALER_PROVIDER=ollama` and `OLLAMA_MODEL=gpt-oss:120b` above are already set — nothing else is required.
80
+
81
+ ## Required: set `actionTimeout`
82
+
83
+ By default, Playwright lets a broken locator retry silently for your *entire* test timeout before it ever throws an error — and healing only kicks in once an action actually fails. Set `actionTimeout` well below your test `timeout`, so a broken locator fails fast and leaves real time for healing to run.
84
+
85
+ ```ts
86
+ export default defineConfig({
87
+ timeout: 60000, // your overall test timeout
88
+ use: {
89
+ actionTimeout: 8000, // comfortably less than the timeout above
90
+ },
91
+ });
92
+ ```
93
+
94
+ Skip this and healing attempts will show `stage=no_snapshot` in the console and never recover anything — not because healing failed, but because it never had time to run before the whole test was torn down.
95
+
96
+ ## 3. Check your setup
97
+
98
+ ```sh
99
+ npx tamash-playwright doctor
100
+ ```
101
+
102
+ | # | Check | What it does |
103
+ |---|-------|---------------|
104
+ | 1 | AI connectivity | Confirms `HEALER_ENABLED`/`HEALER_PROVIDER` are set, and actually calls the provider to verify the key and model work. |
105
+ | 2 | `actionTimeout` | Reads `playwright.config.ts`; flags a missing or too-close-to-`timeout` value. |
106
+ | 3 | Action recovery | Reports whether `HEALER_ACTION_RECOVERY_ENABLED` is on. |
107
+ | 4 | Vision capability | Checks by name whether your configured model is expected to support the screenshot fallback. |
108
+ | 5 | Missing `.describe()` | Scans `tests/` (or `--dir <path>`) for locators without a label, raw CSS/XPath first. |
109
+ | 6 | Inline locators | Flags locators written directly in test files rather than a Page Object class. |
110
+
111
+ If it finds issues, the fastest fix is to open the project in an AI coding assistant and ask it to address what's flagged — add `.describe()` calls, or extract locators into Page Objects. A standing rule in that assistant's instructions file (`CLAUDE.md`, `.cursor/rules`, etc.) keeps it doing both automatically going forward.
112
+
113
+ ## 4. Use it in your tests
114
+
115
+ Change one line at the top of your test file — everything else about how you write tests stays the same.
116
+
117
+ ```diff
118
+ - import { test, expect } from '@playwright/test';
119
+ + import { test, expect } from 'tamash-playwright';
120
+ ```
121
+
122
+ ```ts
123
+ import { test, expect } from 'tamash-playwright';
124
+
125
+ test('logs in', async ({ page }) => {
126
+ await page.goto('/');
127
+ const txtUserName = page.locator('input[name="username"]').describe('User Name Textbox');
128
+ await txtUserName.fill('testadmin');
129
+
130
+ const txtPassword = page.locator('input[placeholder="Password"]').describe('Password Textbox');
131
+ await txtPassword.fill('secret');
132
+
133
+ const btnLogin = page.locator('button[type="submit"]').describe('Login Button');
134
+ await btnLogin.click();
135
+
136
+ await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
137
+ });
138
+ ```
139
+
140
+ ### Label CSS/XPath selectors with `.describe()`
141
+
142
+ Playwright's semantic locators (`getByRole`, `getByPlaceholder`, …) already carry enough context to heal well. A raw `page.locator('input[name="username"]')` doesn't — chain `.describe('...')` onto it so the healer knows what it's actually looking for:
143
+
144
+ ```ts
145
+ page.locator('input[name="username"]').describe('User Name Textbox')
146
+ ```
147
+
148
+ Optional, but without it the healer has to guess purely from a broken CSS selector — a lot less to work with.
149
+
150
+ ## What else it heals — no extra setup
151
+
152
+ - **Popups and extra tabs.** A page opened via `context.newPage()`, `window.open`, or a `target="_blank"` link is just as healing-aware as your main `page`.
153
+ - **Elements inside `<iframe>`s.** `page.frameLocator('#my-iframe')` and anything chained off it heals the same way, correctly scoped to the iframe's own document.
154
+ - **Most of the Playwright API surface**, not just clicks and fills — `check`, `selectOption`, `dragTo`, `dispatchEvent`, read methods like `textContent`/`getAttribute`/`isChecked`, `screenshot`, and more. `dragTo` and `drop` are reported honestly on failure rather than guessed at.
155
+
156
+ ## When text alone isn't enough: vision fallback
157
+
158
+ Sometimes an element has nothing useful to match on by text — an icon-only button with no label, or several visually distinct elements that look identical in the accessibility tree. If your configured model supports image input (`gpt-4o`, `claude-haiku-4-5`, `gemini-2.0-flash`, …), `tamash-playwright` automatically falls back to a screenshot-based search after the normal text attempt fails — same provider, same key, no separate setup. Run `npx tamash-playwright doctor` to check whether your model is expected to support it.
159
+
160
+ ## Action recovery (optional)
161
+
162
+ Occasionally a locator heals correctly — the AI found the right element — but the *action* on it still fails: covered by an overlay, needs scrolling into view first. Set `HEALER_ACTION_RECOVERY_ENABLED=true` to let the AI pick a tactic from a fixed, safe menu (scroll into view, bypass actionability checks, wait and retry, or dispatch the DOM event directly) before giving up.
163
+
164
+ Off by default — it's a second, more speculative layer beyond selector healing. The AI only ever picks from that fixed menu; it never decides how to interact with the page on its own.
165
+
166
+ ## Not paying for the same heal twice
167
+
168
+ Once a locator heals successfully, `tamash-playwright` remembers the fix in `.tamash-playwright/heals.jsonl`. The next time that exact locator breaks the same way, it tries the previously-confirmed selector *first* — no ARIA snapshot, no AI call. Only if that no longer works does it fall through to a fresh snapshot-and-AI-call, exactly as before.
169
+
170
+ This persists across runs, not just within one, since it reads from disk rather than an in-memory cache. That also covers the case that costs the most in practice: the same broken selector otherwise getting healed by AI on *every single CI run* until someone gets around to running `apply-heals`.
171
+
172
+ ```
173
+ [self-healer] tests/sampletest.spec.ts:13 — locator.fill "User Name Textbox" -> HEALED [provider=cache, vision=no, actionRecovery=no, suggested="role:textbox:Username"] — locator.fill: Timeout 8000ms exceeded.
174
+ ```
175
+
176
+ No token count — nothing was called. A cache hit doesn't re-log itself, so the log only grows when a genuinely new AI suggestion is produced, not from repeated confirmations of one already-known fix.
177
+
178
+ ## Making a heal permanent: `apply-heals`
179
+
180
+ Runtime healing — cached or not — never touches your source code. The original locator stays broken in your test file or Page Object forever, healed at runtime on every run, until you fix it yourself. `apply-heals` closes that loop: it rewrites the original broken locator to the selector that actually worked, so the next run doesn't need healing at all.
181
+
182
+ ```sh
183
+ npx playwright test # heals at runtime, records what it healed
184
+ npx tamash-playwright apply-heals --dry-run # preview the source changes
185
+ npx tamash-playwright apply-heals # write them
186
+ ```
187
+
188
+ ```
189
+ [FIX] src/pages/loginpage.ts:11
190
+ - .locator('input[name="username1"]')
191
+ + .getByRole("textbox", { name: "Username" })
192
+
193
+ 1 fix(es) applied to 1 file(s), 0 skipped.
194
+ Review the changes (e.g. `git diff`) before committing.
195
+ ```
196
+
197
+ A few things worth knowing:
198
+
199
+ - **Nothing runs automatically.** `apply-heals` is a separate, deliberate command. A test run never edits your source on its own.
200
+ - **Only real selector fixes qualify.** Text/ARIA-based heals only — not vision (no reusable source form) and not action-recovery (the locator was already right).
201
+ - **Surgical edits.** `.describe('...')` and everything else on the line is untouched — only the `.locator(...)`/`.getByRole(...)` call itself is replaced.
202
+ - **Always review before committing.** This rewrites source files. Check `git diff`, rerun tests, commit deliberately — like any other automated change.
203
+
204
+ ### Every run leaves a permanent record
205
+
206
+ Each run writes a before/after report to `.tamash-playwright/` — `apply-heals-report.json` and `apply-heals-report.md`, one section per fix with the exact before/after code. Those two filenames always mean *the latest run* — a second run overwrites them. Since `.tamash-playwright/` is gitignored, that would otherwise mean the first run's record is gone with no trace.
207
+
208
+ So every run *also* archives a timestamped copy of both reports, plus the raw `heals.jsonl` behind them, under `.tamash-playwright/history/` — nothing there is ever overwritten or deleted by a later run.
209
+
210
+ ## Running `apply-heals` in CI
211
+
212
+ `apply-heals` never touches git — in CI that means a fix only exists in that job's ephemeral checkout unless something turns it into a real, reviewable change. The shape that works: a job that runs *after* your test job(s), downloads whatever got healed, applies it on a fresh branch, **re-runs the suite to prove the fix actually works**, and only then opens a PR.
213
+
214
+ ### Sharded suites
215
+
216
+ If your suite runs `--shard=N/M` across multiple machines, each shard only sees its own slice of what got healed — `heals.jsonl` ends up fragmented, one partial file per shard. `--logs-dir` merges any number of them, nested however you like, deduplicating by keeping the newest entry per file:line:
217
+
218
+ ```sh
219
+ npx tamash-playwright apply-heals --logs-dir shard-logs
220
+ ```
221
+
222
+ ### Full GitHub Actions example
223
+
224
+ ```yaml
225
+ jobs:
226
+ test:
227
+ # ...your existing test job(s), sharded or not...
228
+ steps:
229
+ - run: npx playwright test
230
+ - uses: actions/upload-artifact@v4
231
+ if: ${{ !cancelled() }}
232
+ with:
233
+ name: heals-log-${{ strategy.job-index }}
234
+ path: .tamash-playwright/heals.jsonl
235
+ if-no-files-found: ignore
236
+
237
+ apply-heals:
238
+ needs: test
239
+ if: ${{ !cancelled() && github.event_name == 'push' }} # not pull_request — see below
240
+ runs-on: ubuntu-latest
241
+ permissions:
242
+ contents: write
243
+ pull-requests: write
244
+ steps:
245
+ - uses: actions/checkout@v4
246
+ - uses: actions/setup-node@v4
247
+ with: { node-version: lts/* }
248
+ - run: npm ci
249
+
250
+ - uses: actions/download-artifact@v4
251
+ with:
252
+ pattern: heals-log-*
253
+ path: shard-logs
254
+ continue-on-error: true # no artifact when nothing needed healing
255
+
256
+ - run: npx tamash-playwright apply-heals --logs-dir shard-logs
257
+
258
+ - name: Check whether any fixes were applied
259
+ id: check
260
+ run: echo "changed=$(git diff --quiet || echo true)" >> "$GITHUB_OUTPUT"
261
+
262
+ # HEALER_ENABLED=false is deliberate: proves the *written* fix works standalone —
263
+ # leaving healing on could let a still-broken selector get silently re-healed
264
+ # again, reporting green without ever proving the applied fix was correct.
265
+ - name: Verify the healed selectors work on their own
266
+ id: verify
267
+ if: steps.check.outputs.changed == 'true'
268
+ run: npx playwright test
269
+ continue-on-error: true
270
+ env:
271
+ HEALER_ENABLED: false
272
+
273
+ - name: Compose PR body
274
+ if: steps.check.outputs.changed == 'true'
275
+ run: |
276
+ {
277
+ echo "Auto-generated by \`tamash-playwright apply-heals\`."
278
+ echo ""
279
+ if [ "${{ steps.verify.outcome }}" = "success" ]; then
280
+ echo "**Verification (healing disabled): ✅ passed.**"
281
+ else
282
+ echo "**Verification (healing disabled): ⚠️ FAILED — review carefully.**"
283
+ fi
284
+ echo ""
285
+ cat .tamash-playwright/apply-heals-report.md
286
+ } > .tamash-playwright/pr-body.md
287
+
288
+ - name: Open PR with healed selectors
289
+ if: steps.check.outputs.changed == 'true'
290
+ uses: peter-evans/create-pull-request@v6
291
+ with:
292
+ commit-message: "fix: apply self-healed selectors from CI"
293
+ title: "Apply self-healed selectors (verification ${{ steps.verify.outcome == 'success' && 'passed' || 'FAILED' }})"
294
+ body-path: .tamash-playwright/pr-body.md
295
+ branch: tamash-playwright/apply-heals
296
+ delete-branch: true
297
+
298
+ - name: Fail the job if verification didn't pass
299
+ if: steps.check.outputs.changed == 'true' && steps.verify.outcome != 'success'
300
+ run: exit 1 # PR still opened above — this just keeps CI status honest
301
+ ```
302
+
303
+ **Before you copy this**: `.tamash-playwright/` must be in your `.gitignore`. `peter-evans/create-pull-request` commits whatever differs from `HEAD` in the working tree — without the ignore, the report/log files themselves get swept into the PR alongside the actual fix.
304
+
305
+ ### Three choices worth understanding
306
+
307
+ - **Gated on `push`, not `pull_request`.** A `pull_request` run from a fork gets a read-only `GITHUB_TOKEN` — it couldn't open a PR anyway — and "open a PR to fix this still-open PR" isn't a sensible flow regardless.
308
+ - **The PR opens either way**, verification passed or failed. A failed verification is still worth a human's attention (maybe the fix is right and something else was flaky) — it gets an honest label instead of a silent false-positive. The job itself still fails on a bad verification, so CI status stays truthful even though the PR still exists for review.
309
+ - **[peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) is a no-op with nothing to commit**, so this job is safe to run on every push — it only opens a PR when there's an actual fix, reusing the same branch on later runs rather than piling up duplicates.
310
+
311
+ ## Reading reports
312
+
313
+ Every healing attempt — succeeded or not — shows up in Playwright's own HTML report (`npx playwright show-report`):
314
+
315
+ - An annotation summarizing what happened, e.g. `Recovered using ollama:gpt-oss:120b (role:button:Submit)`.
316
+ - A JSON attachment with the full detail: provider used, whether vision or action-recovery was involved, the suggested selector, token cost, and — on failure — which stage it stopped at (`ai_declined`, `replay_failed`, …).
317
+ - Exactly where in your own code the locator was created, so you know which line to fix even without opening the report again.
318
+
319
+ The same detail prints to the console as it happens:
320
+
321
+ ```
322
+ [self-healer] src/pages/loginpage.ts:11 — locator.fill "Username Textbox" -> HEALED [provider=ollama:gpt-oss:120b, vision=no, actionRecovery=no, suggested="role:textbox:Username", 620 tokens (489 input + 131 output)] — locator.fill: Timeout 8000ms exceeded.
323
+ ```
324
+
325
+ ## Environment variables
326
+
327
+ | Variable | Default | Purpose |
328
+ |----------|---------|---------|
329
+ | `HEALER_ENABLED` | `true` | Master on/off switch. Any value other than `false`/`0` leaves healing on. |
330
+ | `HEALER_PROVIDER` | unset | `ollama` \| `openai` \| `anthropic` \| `gemini`. Unset or missing key/model still allows cache hits, just no fresh AI calls. |
331
+ | `HEALER_ACTION_RECOVERY_ENABLED` | `false` | Opt-in second-layer recovery for actionability failures on an already-correctly-healed locator. |
332
+ | `OLLAMA_MODEL` / `OLLAMA_API_KEY` | — | Ollama Cloud provider config. |
333
+ | `OPENAI_MODEL` / `OPENAI_API_KEY` | — | OpenAI provider config. |
334
+ | `ANTHROPIC_MODEL` / `ANTHROPIC_API_KEY` | — | Anthropic (Claude) provider config. |
335
+ | `GEMINI_MODEL` / `GEMINI_API_KEY` | — | Google Gemini provider config. |
336
+
337
+ ## CLI commands
338
+
339
+ | Command | Flags | What it does |
340
+ |---------|-------|---------------|
341
+ | `npx tamash-playwright doctor` | `--dir <path>` | Pre-flight checks: AI connectivity, `actionTimeout`, action recovery, vision capability, missing `.describe()`, inline locators. |
342
+ | `npx tamash-playwright apply-heals` | `--dry-run` | Preview fixes without writing anything. |
343
+ | | `--logs-dir <path>` | Merge every `heals.jsonl` found under `<path>` (any nesting) instead of the local log — for sharded CI. |
344
+
345
+ ## License
346
+
347
+ Free to use, including commercially. The source code may not be copied, modified, redistributed, or resold without prior written permission. See the [LICENSE](LICENSE) file included in this package for the full terms.
348
+
349
+ ## Support
350
+
351
+ For questions or concerns, contact us at support@vibetestq.com.