lazycodex-ai 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/dist/cli/index.js +27 -21
- package/dist/cli-node/index.js +27 -21
- package/package.json +1 -1
- 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/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
|
@@ -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()
|