lazycodex-ai 5.0.0-beta.85 → 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.
Files changed (176) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/index.js +68 -41
  3. package/dist/cli-node/index.js +68 -41
  4. package/package.json +1 -1
  5. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  6. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +2 -0
  7. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  8. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  9. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  10. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  11. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  12. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  13. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  14. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  15. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  16. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  17. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  18. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-6.md +1 -1
  19. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  20. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  21. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  22. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  23. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  24. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/ultrawork/README.md +1 -1
  26. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -2
  27. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +63 -89
  28. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  29. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  30. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  31. package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +2 -2
  32. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  33. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  34. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +63 -89
  35. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  36. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/define-goal.md +2 -3
  38. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -2
  39. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  40. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  41. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  42. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  43. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  60. package/packages/omo-codex/plugin/package-lock.json +12 -12
  61. package/packages/omo-codex/plugin/package.json +1 -1
  62. package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +8 -2
  63. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +2 -2
  64. package/packages/omo-codex/plugin/skills/browser/ATTRIBUTION.md +26 -14
  65. package/packages/omo-codex/plugin/skills/browser/SKILL.md +65 -52
  66. package/packages/omo-codex/plugin/skills/browser/references/commands.md +81 -66
  67. package/packages/omo-codex/plugin/skills/browser/references/install.md +31 -34
  68. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/README.md +41 -21
  69. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/frames-and-humans.md +33 -20
  70. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/ladder.md +24 -22
  71. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/network.md +35 -15
  72. package/packages/omo-codex/plugin/skills/browser/references/recipes/1password.md +13 -11
  73. package/packages/omo-codex/plugin/skills/browser/references/remote.md +5 -4
  74. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/index.js +1534 -0
  75. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +9 -0
  76. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/page-bundle.js +1395 -0
  77. package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +37 -32
  78. package/packages/omo-codex/plugin/skills/browser/scripts/browser-install.mjs +31 -44
  79. package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +25 -0
  80. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +2 -2
  81. package/packages/omo-codex/plugin/skills/debugging/references/methodology/06-fix.md +3 -3
  82. package/packages/omo-codex/plugin/skills/debugging/references/methodology/08-qa.md +1 -1
  83. package/packages/omo-codex/plugin/skills/debugging/references/tools/browser-qa.md +104 -0
  84. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +1 -1
  85. package/packages/omo-codex/plugin/skills/frontend/references/design/clone-from-url.md +1 -1
  86. package/packages/omo-codex/plugin/skills/programming/SKILL.md +12 -18
  87. package/packages/omo-codex/plugin/skills/programming/references/rust/README.md +43 -15
  88. package/packages/omo-codex/plugin/skills/programming/references/rust/api-design.md +81 -0
  89. package/packages/omo-codex/plugin/skills/programming/references/rust/async-tokio.md +60 -28
  90. package/packages/omo-codex/plugin/skills/programming/references/rust/axum-stack.md +1 -13
  91. package/packages/omo-codex/plugin/skills/programming/references/rust/cargo-strict.md +44 -8
  92. package/packages/omo-codex/plugin/skills/programming/references/rust/clap-stack.md +8 -3
  93. package/packages/omo-codex/plugin/skills/programming/references/rust/concurrency.md +66 -52
  94. package/packages/omo-codex/plugin/skills/programming/references/rust/libraries.md +35 -25
  95. package/packages/omo-codex/plugin/skills/programming/references/rust/macros.md +63 -0
  96. package/packages/omo-codex/plugin/skills/programming/references/rust/one-liners.md +5 -3
  97. package/packages/omo-codex/plugin/skills/programming/references/rust/proptest-insta.md +8 -0
  98. package/packages/omo-codex/plugin/skills/programming/references/rust/type-state.md +50 -12
  99. package/packages/omo-codex/plugin/skills/programming/references/rust/unsafe-discipline.md +34 -6
  100. package/packages/omo-codex/plugin/skills/programming/references/rust/zero-cost-safety.md +62 -52
  101. package/packages/omo-codex/plugin/skills/programming/references/rust-ub/miri-sanitizers-loom.md +1 -1
  102. package/packages/omo-codex/plugin/skills/programming/references/rust-ub/ub-taxonomy.md +6 -3
  103. package/packages/omo-codex/plugin/skills/programming/scripts/rust/check-no-excuse-rules.sh +86 -75
  104. package/packages/omo-codex/plugin/skills/programming/scripts/rust/new-project.py +31 -28
  105. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +1 -1
  106. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +27 -20
  107. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  108. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +32 -100
  109. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +5 -11
  110. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/playwright.md +20 -37
  111. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +63 -89
  112. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +4 -4
  113. package/packages/omo-codex/plugin/skills/ulw-loop/references/define-goal.md +2 -3
  114. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -2
  115. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +1 -1
  116. package/packages/omo-codex/plugin/skills/visual-qa/references/browser-setup.md +46 -46
  117. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -2
  118. package/packages/omo-codex/scripts/install-dist/install-local.mjs +4 -2
  119. package/packages/prompts-core/prompts/ultrawork/codex.md +63 -89
  120. package/packages/shared-skills/skills/browser/ATTRIBUTION.md +26 -14
  121. package/packages/shared-skills/skills/browser/SKILL.md +65 -52
  122. package/packages/shared-skills/skills/browser/references/commands.md +81 -66
  123. package/packages/shared-skills/skills/browser/references/install.md +31 -34
  124. package/packages/shared-skills/skills/browser/references/owned-engine/README.md +41 -21
  125. package/packages/shared-skills/skills/browser/references/owned-engine/frames-and-humans.md +33 -20
  126. package/packages/shared-skills/skills/browser/references/owned-engine/ladder.md +24 -22
  127. package/packages/shared-skills/skills/browser/references/owned-engine/network.md +35 -15
  128. package/packages/shared-skills/skills/browser/references/recipes/1password.md +13 -11
  129. package/packages/shared-skills/skills/browser/references/remote.md +5 -4
  130. package/packages/shared-skills/skills/browser/runtime/omowright/index.js +1534 -0
  131. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +9 -0
  132. package/packages/shared-skills/skills/browser/runtime/omowright/page-bundle.js +1395 -0
  133. package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +37 -32
  134. package/packages/shared-skills/skills/browser/scripts/browser-install.mjs +31 -44
  135. package/packages/shared-skills/skills/browser/scripts/omowright.mjs +25 -0
  136. package/packages/shared-skills/skills/debugging/SKILL.md +2 -2
  137. package/packages/shared-skills/skills/debugging/references/methodology/06-fix.md +3 -3
  138. package/packages/shared-skills/skills/debugging/references/methodology/08-qa.md +1 -1
  139. package/packages/shared-skills/skills/debugging/references/tools/browser-qa.md +104 -0
  140. package/packages/shared-skills/skills/frontend/SKILL.md +1 -1
  141. package/packages/shared-skills/skills/frontend/references/design/clone-from-url.md +1 -1
  142. package/packages/shared-skills/skills/programming/SKILL.md +12 -18
  143. package/packages/shared-skills/skills/programming/references/rust/README.md +43 -15
  144. package/packages/shared-skills/skills/programming/references/rust/api-design.md +81 -0
  145. package/packages/shared-skills/skills/programming/references/rust/async-tokio.md +60 -28
  146. package/packages/shared-skills/skills/programming/references/rust/axum-stack.md +1 -13
  147. package/packages/shared-skills/skills/programming/references/rust/cargo-strict.md +44 -8
  148. package/packages/shared-skills/skills/programming/references/rust/clap-stack.md +8 -3
  149. package/packages/shared-skills/skills/programming/references/rust/concurrency.md +66 -52
  150. package/packages/shared-skills/skills/programming/references/rust/libraries.md +35 -25
  151. package/packages/shared-skills/skills/programming/references/rust/macros.md +63 -0
  152. package/packages/shared-skills/skills/programming/references/rust/one-liners.md +5 -3
  153. package/packages/shared-skills/skills/programming/references/rust/proptest-insta.md +8 -0
  154. package/packages/shared-skills/skills/programming/references/rust/type-state.md +50 -12
  155. package/packages/shared-skills/skills/programming/references/rust/unsafe-discipline.md +34 -6
  156. package/packages/shared-skills/skills/programming/references/rust/zero-cost-safety.md +62 -52
  157. package/packages/shared-skills/skills/programming/references/rust-ub/miri-sanitizers-loom.md +1 -1
  158. package/packages/shared-skills/skills/programming/references/rust-ub/ub-taxonomy.md +6 -3
  159. package/packages/shared-skills/skills/programming/scripts/rust/check-no-excuse-rules.sh +86 -75
  160. package/packages/shared-skills/skills/programming/scripts/rust/check-no-excuse-rules.test.ts +83 -0
  161. package/packages/shared-skills/skills/programming/scripts/rust/new-project.py +31 -28
  162. package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
  163. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +27 -20
  164. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  165. package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +32 -100
  166. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +5 -11
  167. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/playwright.md +20 -37
  168. package/packages/shared-skills/skills/ulw-execute/SKILL.md +4 -4
  169. package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
  170. package/packages/shared-skills/skills/visual-qa/references/browser-setup.md +46 -46
  171. package/packages/omo-codex/plugin/skills/browser/scripts/browser-env.mjs +0 -41
  172. package/packages/omo-codex/plugin/skills/debugging/references/tools/playwright-cli.md +0 -112
  173. package/packages/omo-codex/plugin/skills/programming/scripts/rust/check-no-excuse-rules.py +0 -296
  174. package/packages/shared-skills/skills/browser/scripts/browser-env.mjs +0 -41
  175. package/packages/shared-skills/skills/debugging/references/tools/playwright-cli.md +0 -112
  176. 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()