oh-my-opencode 5.0.0-beta.86 → 5.0.0-beta.87
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/.agents/skills/hyperplan/SKILL.md +1 -1
- package/.opencode/skills/hyperplan/SKILL.md +1 -1
- package/dist/cli/index.js +27 -21
- package/dist/cli-node/index.js +27 -21
- package/dist/index.js +85 -87
- package/dist/skills/browser/ATTRIBUTION.md +26 -14
- package/dist/skills/browser/SKILL.md +65 -52
- package/dist/skills/browser/references/commands.md +81 -66
- package/dist/skills/browser/references/install.md +31 -34
- package/dist/skills/browser/references/owned-engine/README.md +41 -21
- package/dist/skills/browser/references/owned-engine/frames-and-humans.md +33 -20
- package/dist/skills/browser/references/owned-engine/ladder.md +24 -22
- package/dist/skills/browser/references/owned-engine/network.md +35 -15
- package/dist/skills/browser/references/recipes/1password.md +13 -11
- package/dist/skills/browser/references/remote.md +5 -4
- package/dist/skills/browser/runtime/omowright/index.js +1534 -0
- package/dist/skills/browser/runtime/omowright/manifest.json +9 -0
- package/dist/skills/browser/runtime/omowright/page-bundle.js +1395 -0
- package/dist/skills/browser/scripts/browser-doctor.mjs +37 -32
- package/dist/skills/browser/scripts/browser-install.mjs +31 -44
- package/dist/skills/browser/scripts/omowright.mjs +25 -0
- package/dist/skills/debugging/SKILL.md +2 -2
- package/dist/skills/debugging/references/methodology/06-fix.md +3 -3
- package/dist/skills/debugging/references/methodology/08-qa.md +1 -1
- package/dist/skills/debugging/references/tools/browser-qa.md +104 -0
- package/dist/skills/frontend/SKILL.md +1 -1
- package/dist/skills/frontend/references/design/clone-from-url.md +1 -1
- package/dist/skills/programming/SKILL.md +12 -18
- package/dist/skills/programming/references/rust/README.md +43 -15
- package/dist/skills/programming/references/rust/api-design.md +81 -0
- package/dist/skills/programming/references/rust/async-tokio.md +60 -28
- package/dist/skills/programming/references/rust/axum-stack.md +1 -13
- package/dist/skills/programming/references/rust/cargo-strict.md +44 -8
- package/dist/skills/programming/references/rust/clap-stack.md +8 -3
- package/dist/skills/programming/references/rust/concurrency.md +66 -52
- package/dist/skills/programming/references/rust/libraries.md +35 -25
- package/dist/skills/programming/references/rust/macros.md +63 -0
- package/dist/skills/programming/references/rust/one-liners.md +5 -3
- package/dist/skills/programming/references/rust/proptest-insta.md +8 -0
- package/dist/skills/programming/references/rust/type-state.md +50 -12
- package/dist/skills/programming/references/rust/unsafe-discipline.md +34 -6
- package/dist/skills/programming/references/rust/zero-cost-safety.md +62 -52
- package/dist/skills/programming/references/rust-ub/miri-sanitizers-loom.md +1 -1
- package/dist/skills/programming/references/rust-ub/ub-taxonomy.md +6 -3
- package/dist/skills/programming/scripts/rust/check-no-excuse-rules.sh +86 -75
- package/dist/skills/programming/scripts/rust/check-no-excuse-rules.test.ts +83 -0
- package/dist/skills/programming/scripts/rust/new-project.py +31 -28
- package/dist/skills/review-work/SKILL.md +1 -1
- package/dist/skills/ultimate-browsing/SKILL.md +27 -20
- package/dist/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/dist/skills/ultimate-browsing/references/chrome-stealth.md +32 -100
- package/dist/skills/ultimate-browsing/references/insane-search/README.md +5 -11
- package/dist/skills/ultimate-browsing/references/insane-search/playwright.md +20 -37
- package/dist/skills/ulw-execute/SKILL.md +4 -4
- package/dist/skills/visual-qa/SKILL.md +1 -1
- package/dist/skills/visual-qa/references/browser-setup.md +46 -46
- package/dist/tui.js +6 -3
- package/package.json +15 -14
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +2 -0
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-6.md +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/README.md +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +63 -89
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +2 -2
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +63 -89
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/define-goal.md +2 -3
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -2
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +8 -2
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +2 -2
- package/packages/omo-codex/plugin/skills/browser/ATTRIBUTION.md +26 -14
- package/packages/omo-codex/plugin/skills/browser/SKILL.md +65 -52
- package/packages/omo-codex/plugin/skills/browser/references/commands.md +81 -66
- package/packages/omo-codex/plugin/skills/browser/references/install.md +31 -34
- package/packages/omo-codex/plugin/skills/browser/references/owned-engine/README.md +41 -21
- package/packages/omo-codex/plugin/skills/browser/references/owned-engine/frames-and-humans.md +33 -20
- package/packages/omo-codex/plugin/skills/browser/references/owned-engine/ladder.md +24 -22
- package/packages/omo-codex/plugin/skills/browser/references/owned-engine/network.md +35 -15
- package/packages/omo-codex/plugin/skills/browser/references/recipes/1password.md +13 -11
- package/packages/omo-codex/plugin/skills/browser/references/remote.md +5 -4
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/index.js +1534 -0
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +9 -0
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/page-bundle.js +1395 -0
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +37 -32
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-install.mjs +31 -44
- package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +25 -0
- package/packages/omo-codex/plugin/skills/debugging/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/debugging/references/methodology/06-fix.md +3 -3
- package/packages/omo-codex/plugin/skills/debugging/references/methodology/08-qa.md +1 -1
- package/packages/omo-codex/plugin/skills/debugging/references/tools/browser-qa.md +104 -0
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/clone-from-url.md +1 -1
- package/packages/omo-codex/plugin/skills/programming/SKILL.md +12 -18
- package/packages/omo-codex/plugin/skills/programming/references/rust/README.md +43 -15
- package/packages/omo-codex/plugin/skills/programming/references/rust/api-design.md +81 -0
- package/packages/omo-codex/plugin/skills/programming/references/rust/async-tokio.md +60 -28
- package/packages/omo-codex/plugin/skills/programming/references/rust/axum-stack.md +1 -13
- package/packages/omo-codex/plugin/skills/programming/references/rust/cargo-strict.md +44 -8
- package/packages/omo-codex/plugin/skills/programming/references/rust/clap-stack.md +8 -3
- package/packages/omo-codex/plugin/skills/programming/references/rust/concurrency.md +66 -52
- package/packages/omo-codex/plugin/skills/programming/references/rust/libraries.md +35 -25
- package/packages/omo-codex/plugin/skills/programming/references/rust/macros.md +63 -0
- package/packages/omo-codex/plugin/skills/programming/references/rust/one-liners.md +5 -3
- package/packages/omo-codex/plugin/skills/programming/references/rust/proptest-insta.md +8 -0
- package/packages/omo-codex/plugin/skills/programming/references/rust/type-state.md +50 -12
- package/packages/omo-codex/plugin/skills/programming/references/rust/unsafe-discipline.md +34 -6
- package/packages/omo-codex/plugin/skills/programming/references/rust/zero-cost-safety.md +62 -52
- package/packages/omo-codex/plugin/skills/programming/references/rust-ub/miri-sanitizers-loom.md +1 -1
- package/packages/omo-codex/plugin/skills/programming/references/rust-ub/ub-taxonomy.md +6 -3
- package/packages/omo-codex/plugin/skills/programming/scripts/rust/check-no-excuse-rules.sh +86 -75
- package/packages/omo-codex/plugin/skills/programming/scripts/rust/new-project.py +31 -28
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +27 -20
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +32 -100
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +5 -11
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/playwright.md +20 -37
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +63 -89
- package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +4 -4
- package/packages/omo-codex/plugin/skills/ulw-loop/references/define-goal.md +2 -3
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -2
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/visual-qa/references/browser-setup.md +46 -46
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -2
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +4 -2
- package/packages/prompts-core/prompts/ultrawork/codex.md +63 -89
- package/packages/shared-skills/skills/browser/ATTRIBUTION.md +26 -14
- package/packages/shared-skills/skills/browser/SKILL.md +65 -52
- package/packages/shared-skills/skills/browser/references/commands.md +81 -66
- package/packages/shared-skills/skills/browser/references/install.md +31 -34
- package/packages/shared-skills/skills/browser/references/owned-engine/README.md +41 -21
- package/packages/shared-skills/skills/browser/references/owned-engine/frames-and-humans.md +33 -20
- package/packages/shared-skills/skills/browser/references/owned-engine/ladder.md +24 -22
- package/packages/shared-skills/skills/browser/references/owned-engine/network.md +35 -15
- package/packages/shared-skills/skills/browser/references/recipes/1password.md +13 -11
- package/packages/shared-skills/skills/browser/references/remote.md +5 -4
- package/packages/shared-skills/skills/browser/runtime/omowright/index.js +1534 -0
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +9 -0
- package/packages/shared-skills/skills/browser/runtime/omowright/page-bundle.js +1395 -0
- package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +37 -32
- package/packages/shared-skills/skills/browser/scripts/browser-install.mjs +31 -44
- package/packages/shared-skills/skills/browser/scripts/omowright.mjs +25 -0
- package/packages/shared-skills/skills/debugging/SKILL.md +2 -2
- package/packages/shared-skills/skills/debugging/references/methodology/06-fix.md +3 -3
- package/packages/shared-skills/skills/debugging/references/methodology/08-qa.md +1 -1
- package/packages/shared-skills/skills/debugging/references/tools/browser-qa.md +104 -0
- package/packages/shared-skills/skills/frontend/SKILL.md +1 -1
- package/packages/shared-skills/skills/frontend/references/design/clone-from-url.md +1 -1
- package/packages/shared-skills/skills/programming/SKILL.md +12 -18
- package/packages/shared-skills/skills/programming/references/rust/README.md +43 -15
- package/packages/shared-skills/skills/programming/references/rust/api-design.md +81 -0
- package/packages/shared-skills/skills/programming/references/rust/async-tokio.md +60 -28
- package/packages/shared-skills/skills/programming/references/rust/axum-stack.md +1 -13
- package/packages/shared-skills/skills/programming/references/rust/cargo-strict.md +44 -8
- package/packages/shared-skills/skills/programming/references/rust/clap-stack.md +8 -3
- package/packages/shared-skills/skills/programming/references/rust/concurrency.md +66 -52
- package/packages/shared-skills/skills/programming/references/rust/libraries.md +35 -25
- package/packages/shared-skills/skills/programming/references/rust/macros.md +63 -0
- package/packages/shared-skills/skills/programming/references/rust/one-liners.md +5 -3
- package/packages/shared-skills/skills/programming/references/rust/proptest-insta.md +8 -0
- package/packages/shared-skills/skills/programming/references/rust/type-state.md +50 -12
- package/packages/shared-skills/skills/programming/references/rust/unsafe-discipline.md +34 -6
- package/packages/shared-skills/skills/programming/references/rust/zero-cost-safety.md +62 -52
- package/packages/shared-skills/skills/programming/references/rust-ub/miri-sanitizers-loom.md +1 -1
- package/packages/shared-skills/skills/programming/references/rust-ub/ub-taxonomy.md +6 -3
- package/packages/shared-skills/skills/programming/scripts/rust/check-no-excuse-rules.sh +86 -75
- package/packages/shared-skills/skills/programming/scripts/rust/check-no-excuse-rules.test.ts +83 -0
- package/packages/shared-skills/skills/programming/scripts/rust/new-project.py +31 -28
- package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
- package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +27 -20
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +32 -100
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +5 -11
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/playwright.md +20 -37
- package/packages/shared-skills/skills/ulw-execute/SKILL.md +4 -4
- package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
- package/packages/shared-skills/skills/visual-qa/references/browser-setup.md +46 -46
- package/dist/skills/browser/scripts/browser-env.mjs +0 -41
- package/dist/skills/debugging/references/tools/playwright-cli.md +0 -112
- package/dist/skills/programming/scripts/rust/check-no-excuse-rules.py +0 -296
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-env.mjs +0 -41
- package/packages/omo-codex/plugin/skills/debugging/references/tools/playwright-cli.md +0 -112
- package/packages/omo-codex/plugin/skills/programming/scripts/rust/check-no-excuse-rules.py +0 -296
- package/packages/shared-skills/skills/browser/scripts/browser-env.mjs +0 -41
- package/packages/shared-skills/skills/debugging/references/tools/playwright-cli.md +0 -112
- package/packages/shared-skills/skills/programming/scripts/rust/check-no-excuse-rules.py +0 -296
|
@@ -49,7 +49,7 @@ Before any reviewer sees an image, verify each capture yourself: the file signat
|
|
|
49
49
|
### Web
|
|
50
50
|
|
|
51
51
|
1. Capture a REFERENCE image: the user's mock/target, generated page snapshot, Figma export, source-site capture, or known-good baseline. Save as PNG. If the user provided overview text or annotations, save them next to the image and treat them as part of the reference packet.
|
|
52
|
-
2. Capture the ACTUAL rendered screenshot at the reference viewport
|
|
52
|
+
2. Capture the ACTUAL rendered screenshot at the reference viewport with omowright from js eval (the library is staged in the `browser` skill): the owned engine (`connectPipe` on a task-owned profile, viewport pinned with `emulate`, then `page.screenshot()`) for anything unauthenticated, or the attached engine (`connectBrowserSkill()` → `session.screenshot()`) when the page needs the user's login — never a clone of, or a launch against, the user's live profile. Save PNG and return its path; close the browser or stop the session. See `$SKILL_DIR/references/browser-setup.md` for fixed-viewport examples and prerequisites.
|
|
53
53
|
3. Run the diff and keep the JSON:
|
|
54
54
|
|
|
55
55
|
```
|
|
@@ -1,75 +1,75 @@
|
|
|
1
1
|
# Browser setup (Web capture)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Capture with omowright from the js-eval kernel. The library is staged inside the `browser` skill;
|
|
4
|
+
load it once per session:
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
```js
|
|
7
|
+
const { loadOmowright } = await import("<browser-skill-root>/scripts/omowright.mjs")
|
|
8
|
+
const { omowright } = await loadOmowright()
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Owned engine (default for QA)
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
and installed Chrome/Chromium/Edge. WebView is headless; WebKit has no CDP,
|
|
11
|
-
and `type()` emits no keyboard events. Use tier 2 when these differences matter.
|
|
13
|
+
A browser your code launches, with a task-owned profile, pinned viewport and no user state.
|
|
14
|
+
`connectPipe` opens no listening port and reaps the process on `close()`.
|
|
12
15
|
|
|
13
16
|
```js
|
|
14
17
|
// js-eval cell; url and pngPath belong to this QA run.
|
|
15
|
-
const
|
|
18
|
+
const { mkdtempSync, rmSync } = await import("node:fs")
|
|
19
|
+
const profile = mkdtempSync(`${(await import("node:os")).tmpdir()}/visual-qa-`)
|
|
20
|
+
const browser = await omowright.connectPipe({
|
|
21
|
+
browserPath: chromeBinary, // installed Chrome, Chromium, CloakBrowser or chrome-headless-shell
|
|
22
|
+
browserArgs: ["--headless", "--no-first-run", `--user-data-dir=${profile}`],
|
|
23
|
+
storageRoot: profile,
|
|
24
|
+
})
|
|
16
25
|
try {
|
|
17
|
-
await
|
|
18
|
-
await
|
|
26
|
+
const page = await browser.newTab("about:blank")
|
|
27
|
+
await omowright.emulate(page, { width: 1280, height: 720, deviceScaleFactor: 1, mobile: false, hasTouch: false })
|
|
28
|
+
await page.goto(url, { waitUntil: "load" })
|
|
29
|
+
await Bun.write(pngPath, await page.screenshot())
|
|
19
30
|
console.log(pngPath)
|
|
20
31
|
} finally {
|
|
21
|
-
|
|
32
|
+
await browser.close()
|
|
33
|
+
rmSync(profile, { recursive: true, force: true })
|
|
22
34
|
}
|
|
23
35
|
```
|
|
24
36
|
|
|
25
|
-
|
|
37
|
+
Chrome must already be installed; report an absent executable rather than downloading a managed
|
|
38
|
+
browser. For bot-scored or WAF targets use `connectCloakProfile({ profileDir })` (CloakBrowser
|
|
39
|
+
with a pinned fingerprint seed) — the `browser` skill's `references/owned-engine/README.md`
|
|
40
|
+
covers it.
|
|
41
|
+
|
|
42
|
+
## Attached engine (authenticated pages)
|
|
26
43
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
dependency and execute it from js eval. The user installs `playwright-core`
|
|
30
|
-
once with `bun add playwright-core` if needed; Chrome must already exist.
|
|
31
|
-
No managed browser download is needed.
|
|
44
|
+
When the capture needs the user's login, drive the browser they are signed into instead of
|
|
45
|
+
cloning its profile:
|
|
32
46
|
|
|
33
47
|
```js
|
|
34
|
-
|
|
35
|
-
import { chromium } from "playwright-core"
|
|
36
|
-
const [url, pngPath] = process.argv.slice(2)
|
|
37
|
-
const browser = await chromium.launch({ channel: "chrome", headless: true })
|
|
48
|
+
const session = await omowright.connectBrowserSkill({ name: "visual-qa capture", focused: false })
|
|
38
49
|
try {
|
|
39
|
-
|
|
40
|
-
await
|
|
41
|
-
await
|
|
42
|
-
|
|
50
|
+
await session.navigate(url, { waitUntil: "load" })
|
|
51
|
+
await session.resize(1280, 720)
|
|
52
|
+
const shot = await session.screenshot() // { buffer, width, height }
|
|
53
|
+
await Bun.write(pngPath, shot.buffer)
|
|
43
54
|
} finally {
|
|
44
|
-
await
|
|
55
|
+
await session.stop()
|
|
45
56
|
}
|
|
46
57
|
```
|
|
47
58
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const { execFile } = await import("node:child_process")
|
|
53
|
-
const result = await promisify(execFile)("node", [scriptPath, url, pngPath], { timeout: 45000 })
|
|
54
|
-
console.log(result.stdout)
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
For auth, CLONE the user-data directory to a private task-owned directory
|
|
58
|
-
and use `chromium.launchPersistentContext(clonePath, { channel: "chrome" })`.
|
|
59
|
-
NEVER launch against or clear cookies/cache/site data from the live profile.
|
|
60
|
-
Close the context and remove only the clone after QA. For script-based stealth,
|
|
61
|
-
read the ultimate-browsing skill's `references/chrome-stealth.md`.
|
|
59
|
+
NEVER launch anything against, or clear cookies/cache/site data from, the user's live profile;
|
|
60
|
+
the attached engine is the only sanctioned way to a signed-in page. If no extension is connected,
|
|
61
|
+
run the `browser` skill's `scripts/browser-install.mjs`, relay its one human step, and wait — do
|
|
62
|
+
not fall back to the owned engine for an authenticated criterion.
|
|
62
63
|
|
|
63
64
|
## Capture a screenshot at a fixed viewport
|
|
64
65
|
|
|
65
|
-
Match CSS viewport AND PNG dimensions:
|
|
66
|
-
(
|
|
67
|
-
|
|
68
|
-
Wait for the specific page state, not a sleep, then compare:
|
|
66
|
+
Match CSS viewport AND PNG dimensions: pin `deviceScaleFactor` through `emulate` (owned) or
|
|
67
|
+
`resize` (attached) instead of resizing the PNG to force a pass. Wait for the specific page state
|
|
68
|
+
(a locator, a `waitForURL`, a `createNetworkSnoop(page).waitFor(...)`), not a sleep, then compare:
|
|
69
69
|
|
|
70
70
|
```sh
|
|
71
71
|
node "$SKILL_DIR/scripts/visual-qa.mjs" image-diff reference.png actual.png
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
Inspect `dimensionsMatch` and `diffRatio`, then inspect the image. Close every
|
|
75
|
-
|
|
74
|
+
Inspect `dimensionsMatch` and `diffRatio`, then inspect the image. Close every browser and session
|
|
75
|
+
and the fixture server, even on a failed capture; remove the owned profile in the same `finally`.
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process"
|
|
2
|
-
import { existsSync } from "node:fs"
|
|
3
|
-
import { homedir, platform } from "node:os"
|
|
4
|
-
import { join } from "node:path"
|
|
5
|
-
import { promisify } from "node:util"
|
|
6
|
-
|
|
7
|
-
const run = promisify(execFile)
|
|
8
|
-
|
|
9
|
-
export const STORE_LISTINGS = {
|
|
10
|
-
chrome: "https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi",
|
|
11
|
-
edge: "https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg",
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const SUPPORTED_PLATFORMS = new Set(["darwin", "linux", "win32"])
|
|
15
|
-
|
|
16
|
-
export function localBinCandidates() {
|
|
17
|
-
const bin = join(homedir(), ".local", "bin")
|
|
18
|
-
return platform() === "win32" ? [join(bin, "bsk.exe"), join(bin, "bsk")] : [join(bin, "bsk")]
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function resolveCli() {
|
|
22
|
-
for (const candidate of localBinCandidates()) {
|
|
23
|
-
if (existsSync(candidate)) return candidate
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
await run("bsk", ["--version"], { timeout: 15000, windowsHide: true })
|
|
27
|
-
return "bsk"
|
|
28
|
-
} catch {
|
|
29
|
-
return undefined
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function readStatus(cli) {
|
|
34
|
-
const { stdout } = await run(cli, ["status", "--json"], { timeout: 30000, windowsHide: true })
|
|
35
|
-
return JSON.parse(stdout)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function connectedBrowsers(status) {
|
|
39
|
-
const browsers = status?.browsers
|
|
40
|
-
return Array.isArray(browsers) ? browsers : []
|
|
41
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
# Browser QA — Scripts from js eval
|
|
2
|
-
|
|
3
|
-
A browser UI bug needs a rendered browser, not curl. Use two tiers from the
|
|
4
|
-
js-eval kernel: `new Bun.WebView()` on Bun >= 1.4 for ordinary captures (macOS
|
|
5
|
-
uses WebKit; Linux/Windows need installed Chrome/Chromium/Edge), otherwise a
|
|
6
|
-
written `playwright-core` script against local Chrome. Use the script tier for
|
|
7
|
-
Chrome semantics, stealth, traces, keyboard events, or authenticated profiles.
|
|
8
|
-
In Codex, prefer `browser:control-in-app-browser` for ordinary page control.
|
|
9
|
-
|
|
10
|
-
## When to reach for Playwright
|
|
11
|
-
|
|
12
|
-
Use the script tier for forms, hydration, responsive CSS, cookie/session flows,
|
|
13
|
-
service workers, or client-side navigation. For a backend-only status/body bug,
|
|
14
|
-
use HTTP. WebView is headless, WebKit has no CDP, and `type()` emits no keyboard
|
|
15
|
-
events; choose real Chrome when these distinctions affect the reproduction.
|
|
16
|
-
|
|
17
|
-
## Install (per-project)
|
|
18
|
-
|
|
19
|
-
Reuse installed `playwright-core`. If absent, the user can install the script
|
|
20
|
-
dependency once with `bun add playwright-core`. Chrome must already be installed;
|
|
21
|
-
report an absent executable rather than downloading a managed browser. Write the
|
|
22
|
-
script beside the dependency so normal module resolution works.
|
|
23
|
-
|
|
24
|
-
For stealth, use the ultimate-browsing reference `references/chrome-stealth.md`:
|
|
25
|
-
its optional plugins are user-installed once in the engine directory and executed
|
|
26
|
-
as a script, not injected into WebView.
|
|
27
|
-
|
|
28
|
-
## The four things you'll actually use
|
|
29
|
-
|
|
30
|
-
### 1. Write a reproduction script
|
|
31
|
-
|
|
32
|
-
Record the exact URL, inputs, viewport, expected UI state, and output artifact.
|
|
33
|
-
For auth, CLONE the user-data directory and use `launchPersistentContext` on the
|
|
34
|
-
clone. NEVER launch against or clear data from the live profile.
|
|
35
|
-
|
|
36
|
-
### 2. Reproduce + capture
|
|
37
|
-
|
|
38
|
-
```js
|
|
39
|
-
// debug-repro.mjs
|
|
40
|
-
import { chromium } from 'playwright-core';
|
|
41
|
-
const [url, pngPath, tracePath] = process.argv.slice(2);
|
|
42
|
-
const browser = await chromium.launch({ channel: 'chrome', headless: true });
|
|
43
|
-
try {
|
|
44
|
-
const context = await browser.newContext({ viewport: { width: 1280, height: 720 } });
|
|
45
|
-
await context.tracing.start({ screenshots: true, snapshots: true });
|
|
46
|
-
try {
|
|
47
|
-
const page = await context.newPage();
|
|
48
|
-
page.on('pageerror', error => console.error(error));
|
|
49
|
-
await page.goto(url, { waitUntil: 'load', timeout: 30000 });
|
|
50
|
-
// Add the scenario's actions and locator/state assertions here.
|
|
51
|
-
await page.screenshot({ path: pngPath });
|
|
52
|
-
console.log(pngPath);
|
|
53
|
-
} finally {
|
|
54
|
-
await context.tracing.stop({ path: tracePath });
|
|
55
|
-
await context.close();
|
|
56
|
-
}
|
|
57
|
-
} finally {
|
|
58
|
-
await browser.close();
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
A screenshot alone is not a reproduction assertion. Use locators for the exact
|
|
63
|
-
observable state; fail the script when that state is absent.
|
|
64
|
-
|
|
65
|
-
### 3. Run from the js-eval kernel
|
|
66
|
-
|
|
67
|
-
```js
|
|
68
|
-
const { promisify } = await import('node:util')
|
|
69
|
-
const { execFile } = await import('node:child_process')
|
|
70
|
-
const result = await promisify(execFile)('node', [scriptPath, url, pngPath, tracePath], { timeout: 60000 })
|
|
71
|
-
console.log(result.stdout)
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### 4. Inspect the trace
|
|
75
|
-
|
|
76
|
-
Retain the trace ZIP for the project's existing trace viewer and inspect the PNG.
|
|
77
|
-
Trace snapshots, network events, and screenshots identify where the UI diverged.
|
|
78
|
-
Keep auth-bearing traces private; do not upload them to public viewers.
|
|
79
|
-
|
|
80
|
-
## Headless vs headed during debugging
|
|
81
|
-
|
|
82
|
-
Use `headless: false` with an available display when reproducing headed-only
|
|
83
|
-
behavior. State which mode produced the evidence; do not claim a headless capture
|
|
84
|
-
proves desktop-browser permissions or window behavior.
|
|
85
|
-
|
|
86
|
-
## Catching the silent-failure patterns Playwright is good at
|
|
87
|
-
|
|
88
|
-
Subscribe before triggering the action: `page.on('pageerror', ...)`,
|
|
89
|
-
`page.on('requestfailed', ...)`, or a bounded `page.waitForResponse(...)` promise.
|
|
90
|
-
Await that specific response/state rather than sleeping or waiting for generic
|
|
91
|
-
network idleness. Propagate failed assertions and process exit codes.
|
|
92
|
-
|
|
93
|
-
## Viewport and device emulation
|
|
94
|
-
|
|
95
|
-
Use `page.setViewportSize({ width: 375, height: 667 })` for a narrow capture.
|
|
96
|
-
`devices` comes from `playwright-core`; apply the desired preset when creating
|
|
97
|
-
a context. Match the reference's pixel scale as well as its viewport.
|
|
98
|
-
|
|
99
|
-
## Gotchas
|
|
100
|
-
|
|
101
|
-
- Wait for state, not time: locator visibility or the exact response is the signal.
|
|
102
|
-
- Locators re-resolve after UI changes; stale element handles do not.
|
|
103
|
-
- Fresh task-owned contexts avoid cached state leaking between runs.
|
|
104
|
-
- Auth belongs in a private clone, never the live browser profile.
|
|
105
|
-
- Give every run its own output directory and bounded process lifetime.
|
|
106
|
-
|
|
107
|
-
## Phase 9 cleanup specifics
|
|
108
|
-
|
|
109
|
-
Close WebView/browser contexts even on failure, stop the fixture server, and remove
|
|
110
|
-
only this run's temporary scripts and profile clone. Preserve requested PNG/trace
|
|
111
|
-
artifacts, then record the cleanup receipt. Do not kill unrelated browsers or
|
|
112
|
-
remove a user's installed Chrome.
|
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env -S uv run --script
|
|
2
|
-
# /// script
|
|
3
|
-
# requires-python = ">=3.11"
|
|
4
|
-
# dependencies = []
|
|
5
|
-
# ///
|
|
6
|
-
#
|
|
7
|
-
# How to run:
|
|
8
|
-
# uv run --script check-no-excuse-rules.py src/lib.rs src/main.rs
|
|
9
|
-
# uv run --script check-no-excuse-rules.py src/ # recursively finds .rs files
|
|
10
|
-
# uv run --script check-no-excuse-rules.py . # entire tree
|
|
11
|
-
#
|
|
12
|
-
# No-excuse rule checker for Rust files — Python rewrite of check-no-excuse-rules.sh.
|
|
13
|
-
# Only rules enforceable via pure text matching live here.
|
|
14
|
-
# Everything semantic is on clippy + miri + nextest.
|
|
15
|
-
#
|
|
16
|
-
# Rules:
|
|
17
|
-
# unwrap .unwrap() outside tests without // SAFE-UNWRAP:
|
|
18
|
-
# expect .expect() outside tests without // SAFE-EXPECT:
|
|
19
|
-
# placeholder-macro todo!/unimplemented!/unreachable!/unreachable_unchecked! in committed code
|
|
20
|
-
# box-dyn-error Box<dyn Error> in non-test code
|
|
21
|
-
# lib-panic panic!() in library code
|
|
22
|
-
# unsafe-no-safety unsafe { without // SAFETY: in preceding 5 lines
|
|
23
|
-
# unjustified-clippy-allow #[allow(clippy::...)] without // CLIPPY-ALLOW:
|
|
24
|
-
# narrowing-as-cast possible narrowing 'as' cast
|
|
25
|
-
#
|
|
26
|
-
# Opt-out: place the appropriate comment on the previous line:
|
|
27
|
-
# // SAFE-UNWRAP: <reason>
|
|
28
|
-
# // SAFE-EXPECT: <reason>
|
|
29
|
-
# // SAFETY: <reason> (for unsafe blocks, within 5 lines above)
|
|
30
|
-
# // CLIPPY-ALLOW: <reason>
|
|
31
|
-
#
|
|
32
|
-
# Test paths (exempt from unwrap/expect/placeholder/box-dyn-error/lib-panic):
|
|
33
|
-
# tests/, benches/, examples/, build.rs, *_test.rs, #[cfg(test)] regions
|
|
34
|
-
|
|
35
|
-
from __future__ import annotations
|
|
36
|
-
|
|
37
|
-
import re
|
|
38
|
-
import sys
|
|
39
|
-
from pathlib import Path
|
|
40
|
-
|
|
41
|
-
# ---------------------------------------------------------------------------
|
|
42
|
-
# Patterns (compiled once)
|
|
43
|
-
# ---------------------------------------------------------------------------
|
|
44
|
-
|
|
45
|
-
RE_UNWRAP = re.compile(r"\.unwrap\(\)")
|
|
46
|
-
RE_EXPECT = re.compile(r"\.expect\(")
|
|
47
|
-
RE_PLACEHOLDER = re.compile(r"\b(todo!|unimplemented!|unreachable!|unreachable_unchecked!)")
|
|
48
|
-
RE_BOX_DYN_ERROR = re.compile(r"Box<dyn\s+Error")
|
|
49
|
-
RE_PANIC = re.compile(r"\bpanic!\(")
|
|
50
|
-
RE_UNSAFE_BLOCK = re.compile(r"\bunsafe\s*\{")
|
|
51
|
-
RE_CLIPPY_ALLOW = re.compile(r"#\[allow\(clippy::")
|
|
52
|
-
RE_CFG_TEST = re.compile(r"#\[cfg\(test\)\]")
|
|
53
|
-
RE_SAFE_UNWRAP = re.compile(r"//\s*SAFE-UNWRAP:")
|
|
54
|
-
RE_SAFE_EXPECT = re.compile(r"//\s*SAFE-EXPECT:")
|
|
55
|
-
RE_SAFETY = re.compile(r"//\s*SAFETY:")
|
|
56
|
-
RE_CLIPPY_ALLOW_JUST = re.compile(r"//\s*CLIPPY-ALLOW:")
|
|
57
|
-
|
|
58
|
-
# Narrowing cast: (wider) as (narrower)
|
|
59
|
-
# Wider types that lose bits when cast to narrower targets.
|
|
60
|
-
# No leading \b — must match e.g. `999u64 as u32` where a digit precedes the type.
|
|
61
|
-
_WIDER = r"(?:u16|u32|u64|u128|usize|i16|i32|i64|i128|isize)"
|
|
62
|
-
_NARROWER = r"(?:u8|u16|u32|i8|i16|i32)"
|
|
63
|
-
RE_NARROWING_CAST = re.compile(
|
|
64
|
-
rf"{_WIDER}\s+as\s+{_NARROWER}"
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
# Test-path fragments.
|
|
68
|
-
_TEST_PATH_PARTS = {"tests", "benches", "examples"}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
# ---------------------------------------------------------------------------
|
|
72
|
-
# Helpers
|
|
73
|
-
# ---------------------------------------------------------------------------
|
|
74
|
-
|
|
75
|
-
violations = 0
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def report(file: str, line: int, rule: str, detail: str) -> None:
|
|
79
|
-
"""Emit a GitHub-Actions-compatible error annotation to stderr."""
|
|
80
|
-
global violations
|
|
81
|
-
print(f"::error file={file},line={line}::[{rule}] {detail}", file=sys.stderr)
|
|
82
|
-
violations += 1
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def is_test_path(path: Path) -> bool:
|
|
86
|
-
"""Return True if *path* is in a test/bench/example directory or is a test file."""
|
|
87
|
-
parts = path.parts
|
|
88
|
-
for part in parts:
|
|
89
|
-
if part in _TEST_PATH_PARTS:
|
|
90
|
-
return True
|
|
91
|
-
if path.name == "build.rs":
|
|
92
|
-
return True
|
|
93
|
-
if path.name.endswith("_test.rs"):
|
|
94
|
-
return True
|
|
95
|
-
return False
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def is_lib_path(file: Path) -> bool:
|
|
99
|
-
"""Heuristic: is this file library code (not main.rs, not src/bin/*)."""
|
|
100
|
-
parts = path_parts_str(file)
|
|
101
|
-
# Must live under src/
|
|
102
|
-
if "src" not in parts:
|
|
103
|
-
return False
|
|
104
|
-
if file.name == "main.rs":
|
|
105
|
-
return False
|
|
106
|
-
# src/bin/* is binary code
|
|
107
|
-
try:
|
|
108
|
-
src_idx = parts.index("src")
|
|
109
|
-
if src_idx + 1 < len(parts) and parts[src_idx + 1] == "bin":
|
|
110
|
-
return False
|
|
111
|
-
except ValueError:
|
|
112
|
-
return False
|
|
113
|
-
return True
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def path_parts_str(p: Path) -> list[str]:
|
|
117
|
-
return list(p.parts)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def strip_line_comment(line: str) -> str:
|
|
121
|
-
"""Return the portion of *line* before any ``//`` line comment.
|
|
122
|
-
|
|
123
|
-
This is a crude heuristic — it does not handle ``//`` inside string
|
|
124
|
-
literals, but matches the behaviour of the bash version.
|
|
125
|
-
"""
|
|
126
|
-
idx = line.find("//")
|
|
127
|
-
if idx == -1:
|
|
128
|
-
return line
|
|
129
|
-
return line[:idx]
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def collect_rs_files(args: list[str]) -> list[Path]:
|
|
133
|
-
"""Expand CLI arguments: files are kept as-is, directories are walked."""
|
|
134
|
-
result: list[Path] = []
|
|
135
|
-
for arg in args:
|
|
136
|
-
p = Path(arg)
|
|
137
|
-
if p.is_file():
|
|
138
|
-
if p.suffix == ".rs":
|
|
139
|
-
result.append(p)
|
|
140
|
-
elif p.is_dir():
|
|
141
|
-
result.extend(sorted(p.rglob("*.rs")))
|
|
142
|
-
# Ignore non-existent / non-.rs
|
|
143
|
-
return result
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
# ---------------------------------------------------------------------------
|
|
147
|
-
# Main checker
|
|
148
|
-
# ---------------------------------------------------------------------------
|
|
149
|
-
|
|
150
|
-
def check_file(file: Path) -> None:
|
|
151
|
-
in_test_file = is_test_path(file)
|
|
152
|
-
in_cfg_test = False
|
|
153
|
-
cfg_test_brace_depth = 0
|
|
154
|
-
|
|
155
|
-
try:
|
|
156
|
-
lines = file.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
157
|
-
except OSError as exc:
|
|
158
|
-
print(f"warning: cannot read {file}: {exc}", file=sys.stderr)
|
|
159
|
-
return
|
|
160
|
-
|
|
161
|
-
for line_no_0, raw_line in enumerate(lines):
|
|
162
|
-
line_no = line_no_0 + 1 # 1-indexed
|
|
163
|
-
|
|
164
|
-
# --- #[cfg(test)] region tracker ---
|
|
165
|
-
if RE_CFG_TEST.search(raw_line):
|
|
166
|
-
in_cfg_test = True
|
|
167
|
-
cfg_test_brace_depth = 0
|
|
168
|
-
|
|
169
|
-
if in_cfg_test:
|
|
170
|
-
opens = raw_line.count("{")
|
|
171
|
-
closes = raw_line.count("}")
|
|
172
|
-
cfg_test_brace_depth += opens - closes
|
|
173
|
-
if cfg_test_brace_depth <= 0 and not RE_CFG_TEST.search(raw_line):
|
|
174
|
-
in_cfg_test = False
|
|
175
|
-
|
|
176
|
-
exempt = in_test_file or in_cfg_test
|
|
177
|
-
code_only = strip_line_comment(raw_line)
|
|
178
|
-
|
|
179
|
-
if not exempt:
|
|
180
|
-
# .unwrap()
|
|
181
|
-
if RE_UNWRAP.search(code_only):
|
|
182
|
-
prev = lines[line_no_0 - 1] if line_no_0 > 0 else ""
|
|
183
|
-
if not RE_SAFE_UNWRAP.search(prev):
|
|
184
|
-
report(
|
|
185
|
-
str(file), line_no, "unwrap",
|
|
186
|
-
".unwrap() outside tests - use ? / ok_or / pattern match "
|
|
187
|
-
"or annotate previous line with // SAFE-UNWRAP: <reason>",
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
# .expect(...)
|
|
191
|
-
if RE_EXPECT.search(code_only):
|
|
192
|
-
prev = lines[line_no_0 - 1] if line_no_0 > 0 else ""
|
|
193
|
-
if not RE_SAFE_EXPECT.search(prev):
|
|
194
|
-
report(
|
|
195
|
-
str(file), line_no, "expect",
|
|
196
|
-
".expect() outside tests - use ? or annotate previous "
|
|
197
|
-
"line with // SAFE-EXPECT: <reason>",
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
# todo!/unimplemented!/unreachable!/unreachable_unchecked!
|
|
201
|
-
if RE_PLACEHOLDER.search(code_only):
|
|
202
|
-
report(
|
|
203
|
-
str(file), line_no, "placeholder-macro",
|
|
204
|
-
"todo!/unimplemented!/unreachable! in committed code",
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
# Box<dyn Error>
|
|
208
|
-
if RE_BOX_DYN_ERROR.search(code_only):
|
|
209
|
-
report(
|
|
210
|
-
str(file), line_no, "box-dyn-error",
|
|
211
|
-
"Box<dyn Error> in non-test code - use anyhow::Error (apps) "
|
|
212
|
-
"or thiserror enum (libs)",
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
# panic!() in library code
|
|
216
|
-
if is_lib_path(file) and RE_PANIC.search(code_only):
|
|
217
|
-
report(
|
|
218
|
-
str(file), line_no, "lib-panic",
|
|
219
|
-
"panic!() in library code - return Result",
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
# unsafe { without // SAFETY: — always enforced, even in tests
|
|
223
|
-
if RE_UNSAFE_BLOCK.search(code_only):
|
|
224
|
-
start = max(0, line_no_0 - 5)
|
|
225
|
-
window = "\n".join(lines[start : line_no_0 + 1])
|
|
226
|
-
if not RE_SAFETY.search(window):
|
|
227
|
-
report(
|
|
228
|
-
str(file), line_no, "unsafe-no-safety-comment",
|
|
229
|
-
"unsafe block without // SAFETY: comment in preceding 5 lines",
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
# #[allow(clippy::...)] without // CLIPPY-ALLOW: — always enforced
|
|
233
|
-
if RE_CLIPPY_ALLOW.search(code_only):
|
|
234
|
-
prev = lines[line_no_0 - 1] if line_no_0 > 0 else ""
|
|
235
|
-
if not RE_CLIPPY_ALLOW_JUST.search(prev):
|
|
236
|
-
report(
|
|
237
|
-
str(file), line_no, "unjustified-clippy-allow",
|
|
238
|
-
"#[allow(clippy::...)] without // CLIPPY-ALLOW: <reason> on "
|
|
239
|
-
"previous line",
|
|
240
|
-
)
|
|
241
|
-
|
|
242
|
-
# Narrowing numeric `as` casts
|
|
243
|
-
if RE_NARROWING_CAST.search(code_only):
|
|
244
|
-
report(
|
|
245
|
-
str(file), line_no, "narrowing-as-cast",
|
|
246
|
-
"possible narrowing 'as' cast - use TryFrom / try_into() for "
|
|
247
|
-
"fallible conversion",
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
# ---------------------------------------------------------------------------
|
|
252
|
-
# Entry point
|
|
253
|
-
# ---------------------------------------------------------------------------
|
|
254
|
-
|
|
255
|
-
def main() -> None:
|
|
256
|
-
global violations
|
|
257
|
-
|
|
258
|
-
if len(sys.argv) < 2:
|
|
259
|
-
print(f"Usage: {sys.argv[0]} <file.rs|dir> [file.rs|dir ...]", file=sys.stderr)
|
|
260
|
-
sys.exit(2)
|
|
261
|
-
|
|
262
|
-
files = collect_rs_files(sys.argv[1:])
|
|
263
|
-
if not files:
|
|
264
|
-
print("warning: no .rs files found in the given arguments", file=sys.stderr)
|
|
265
|
-
sys.exit(0)
|
|
266
|
-
|
|
267
|
-
for f in files:
|
|
268
|
-
check_file(f)
|
|
269
|
-
|
|
270
|
-
if violations > 0:
|
|
271
|
-
print("", file=sys.stderr)
|
|
272
|
-
print(
|
|
273
|
-
f"rust-programmer: {violations} violation(s). Fix before declaring work done.",
|
|
274
|
-
file=sys.stderr,
|
|
275
|
-
)
|
|
276
|
-
print("", file=sys.stderr)
|
|
277
|
-
print("Then run the full toolchain gate:", file=sys.stderr)
|
|
278
|
-
print(" cargo +stable fmt --all -- --check", file=sys.stderr)
|
|
279
|
-
print(
|
|
280
|
-
" cargo +stable clippy --all-targets --all-features -- -D warnings",
|
|
281
|
-
file=sys.stderr,
|
|
282
|
-
)
|
|
283
|
-
print(" cargo nextest run --all-targets --all-features", file=sys.stderr)
|
|
284
|
-
print(
|
|
285
|
-
" cargo +nightly miri nextest run --all-features # if unsafe touched",
|
|
286
|
-
file=sys.stderr,
|
|
287
|
-
)
|
|
288
|
-
print(" cargo machete", file=sys.stderr)
|
|
289
|
-
print(" cargo deny check", file=sys.stderr)
|
|
290
|
-
sys.exit(1)
|
|
291
|
-
|
|
292
|
-
print(f"rust-programmer: no-excuse rules passed for {len(files)} file(s).")
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if __name__ == "__main__":
|
|
296
|
-
main()
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process"
|
|
2
|
-
import { existsSync } from "node:fs"
|
|
3
|
-
import { homedir, platform } from "node:os"
|
|
4
|
-
import { join } from "node:path"
|
|
5
|
-
import { promisify } from "node:util"
|
|
6
|
-
|
|
7
|
-
const run = promisify(execFile)
|
|
8
|
-
|
|
9
|
-
export const STORE_LISTINGS = {
|
|
10
|
-
chrome: "https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi",
|
|
11
|
-
edge: "https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg",
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const SUPPORTED_PLATFORMS = new Set(["darwin", "linux", "win32"])
|
|
15
|
-
|
|
16
|
-
export function localBinCandidates() {
|
|
17
|
-
const bin = join(homedir(), ".local", "bin")
|
|
18
|
-
return platform() === "win32" ? [join(bin, "bsk.exe"), join(bin, "bsk")] : [join(bin, "bsk")]
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function resolveCli() {
|
|
22
|
-
for (const candidate of localBinCandidates()) {
|
|
23
|
-
if (existsSync(candidate)) return candidate
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
await run("bsk", ["--version"], { timeout: 15000, windowsHide: true })
|
|
27
|
-
return "bsk"
|
|
28
|
-
} catch {
|
|
29
|
-
return undefined
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function readStatus(cli) {
|
|
34
|
-
const { stdout } = await run(cli, ["status", "--json"], { timeout: 30000, windowsHide: true })
|
|
35
|
-
return JSON.parse(stdout)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function connectedBrowsers(status) {
|
|
39
|
-
const browsers = status?.browsers
|
|
40
|
-
return Array.isArray(browsers) ? browsers : []
|
|
41
|
-
}
|