claude-dev-env 7.5.0 → 7.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/everything-search/scripts/everything_search.py +11 -0
- package/.agents/skills/everything-search/scripts/test_everything_search.py +1 -0
- package/.agents/skills/fresh-branch/SKILL.md +1 -0
- package/.agents/skills/fresh-branch/scripts/fresh_branch_git_commands.py +3 -0
- package/.agents/skills/fresh-branch/scripts/subprocess_window_access.py +43 -0
- package/.agents/skills/session-continuity/README.md +19 -9
- package/.agents/skills/syncing-submodules/SKILL.md +1 -0
- package/.agents/skills/syncing-submodules/scripts/submodule_sync.py +4 -0
- package/.agents/skills/syncing-submodules/scripts/subprocess_window_access.py +43 -0
- package/.agents/skills/test-runner/SKILL.md +1 -0
- package/.agents/skills/test-runner/scripts/preflight_checks.py +5 -0
- package/.agents/skills/test-runner/scripts/run_tests.py +11 -0
- package/.agents/skills/test-runner/scripts/subprocess_window_access.py +43 -0
- package/.agents/skills/test-runner/scripts/test_run_tests.py +9 -7
- package/_shared/pr-loop/scripts/check_bugbot_ci.py +3 -0
- package/_shared/pr-loop/scripts/check_convergence_gates.py +3 -0
- package/_shared/pr-loop/scripts/check_pending_reviews.py +3 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/added_line_maps.py +3 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/baseline_import_isolation.py +3 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +5 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_file_sets.py +4 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_regression.py +3 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +3 -0
- package/_shared/pr-loop/scripts/codex_usage_probe.py +3 -0
- package/_shared/pr-loop/scripts/copilot_quota.py +3 -0
- package/_shared/pr-loop/scripts/fetch_copilot_reviews.py +3 -0
- package/_shared/pr-loop/scripts/fix_hookspath.py +7 -0
- package/_shared/pr-loop/scripts/post_fix_reply.py +4 -0
- package/_shared/pr-loop/scripts/preflight.py +7 -0
- package/_shared/pr-loop/scripts/preflight_self_heal.py +5 -0
- package/_shared/pr-loop/scripts/run_codex_review.py +3 -0
- package/_shared/pr-loop/scripts/subprocess_window_access.py +41 -0
- package/_shared/pr-loop/scripts/terminology_sweep.py +5 -0
- package/_shared/process-tree/scripts/process_tree_kill.py +3 -0
- package/_shared/process-tree/scripts/subprocess_window_access.py +41 -0
- package/bin/install-constants.mjs +4 -2
- package/bin/install-session-continuity.mjs +73 -30
- package/bin/install-session-continuity.test.mjs +52 -1
- package/bin/install.cursor-rules.test.mjs +57 -15
- package/bin/install.mjs +80 -15
- package/bin/install.test.mjs +57 -0
- package/bin/pstack-model-preferences.claude.json +23 -0
- package/hooks/advisory/pr_done_reminder.py +7 -0
- package/hooks/advisory/refactor_guard.py +8 -0
- package/hooks/advisory/test_pr_done_reminder.py +21 -0
- package/hooks/advisory/test_refactor_guard.py +40 -0
- package/hooks/blocking/block_main_commit.py +4 -0
- package/hooks/blocking/code_rules_comments.py +39 -5
- package/hooks/blocking/pii_prevention_blocker_parts/repository_exemption.py +3 -0
- package/hooks/blocking/precommit_code_rules_gate.py +3 -0
- package/hooks/blocking/tdd_enforcer_parts/content_analysis.py +19 -3
- package/hooks/blocking/tdd_enforcer_parts/git_tracking.py +4 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_content_analysis.py +25 -0
- package/hooks/blocking/test_code_rules_enforcer_comment_string_awareness.py +27 -0
- package/hooks/blocking/test_code_rules_enforcer_exempt_marker_chained.py +10 -0
- package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +0 -2
- package/hooks/git-hooks/pull_request_handoff.py +3 -0
- package/hooks/git-hooks/subprocess_window_access.py +41 -0
- package/hooks/git-hooks/test_verification_start.py +4 -0
- package/hooks/git-hooks/verification_notice_context.py +3 -0
- package/hooks/git-hooks/verification_start.py +7 -8
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +2 -0
- package/hooks/hooks_constants/subprocess_window.py +141 -0
- package/hooks/hooks_constants/subprocess_window_constants.py +9 -0
- package/hooks/hooks_constants/test_code_rules_enforcer_constants.py +6 -0
- package/hooks/hooks_constants/test_subprocess_window.py +128 -0
- package/hooks/lifecycle/enter_worktree_origin_prefetch.py +3 -0
- package/hooks/validation/mypy_validator.py +4 -0
- package/hooks/validators/README.md +1 -0
- package/hooks/validators/git_checks.py +4 -0
- package/hooks/validators/health_check.py +3 -0
- package/hooks/validators/hook_timing_harness.py +3 -0
- package/hooks/validators/mypy_integration.py +4 -0
- package/hooks/validators/ruff_integration.py +6 -0
- package/hooks/validators/run_all_validators.py +7 -0
- package/hooks/validators/subprocess_window_access.py +41 -0
- package/hooks/workflow/auto_formatter.py +8 -0
- package/hooks/workflow/test_auto_formatter.py +39 -0
- package/package.json +1 -1
- package/rules/pstack-models.md +4 -3
- package/scripts/automatic_advisory/checkout.py +8 -1
- package/scripts/automatic_advisory/cli.py +9 -3
- package/scripts/automatic_advisory/config/constants.py +0 -2
- package/scripts/automatic_advisory/execution.py +7 -1
- package/scripts/automatic_advisory/git.py +9 -1
- package/scripts/automatic_advisory/process_host.py +3 -0
- package/scripts/claude_chain_runner.py +3 -0
- package/scripts/gh_artifact_upload.py +3 -0
- package/scripts/grok_headless_runner.py +3 -0
- package/scripts/grok_patch_artifacts.py +4 -0
- package/scripts/grok_worker_preflight.py +3 -0
- package/scripts/invoke_code_review.py +3 -0
- package/scripts/local_verification/command_runner.py +3 -0
- package/scripts/local_verification/git_state.py +3 -0
- package/scripts/policy_lint/adapter_detectors.py +48 -2
- package/scripts/policy_lint/adapter_pairing.py +150 -2
- package/scripts/policy_lint/adapters.py +46 -4
- package/scripts/policy_lint/config/approved_test_pairs.py +25 -9
- package/scripts/policy_lint/config/constants.py +3 -0
- package/scripts/policy_lint/registry.py +1 -1
- package/scripts/policy_lint/selection_git.py +11 -0
- package/scripts/repository_checks/config/constants.py +5 -0
- package/scripts/repository_checks/config/policy_document.py +19 -0
- package/scripts/repository_checks/email_exemptions.py +14 -71
- package/scripts/repository_checks/path_exemptions.py +35 -0
- package/scripts/repository_checks/policy_document.py +120 -0
- package/scripts/repository_checks/tracked_secrets.py +27 -4
- package/scripts/setup_project_paths.py +3 -0
- package/scripts/spawn_grok_batch.py +3 -0
- package/scripts/subprocess_window_access.py +41 -0
- package/scripts/test_dispatcher_profile_import.py +4 -0
- package/scripts/tests/test_automatic_advisory_window_flags.py +17 -11
- package/scripts/tests/test_email_exemptions.py +37 -0
- package/scripts/tests/test_path_exemptions.py +164 -0
- package/scripts/tests/test_policy_document.py +104 -0
- package/scripts/tests/test_policy_lint_rules_function_length_scope.py +101 -0
- package/scripts/tests/test_policy_lint_rules_pairing.py +375 -0
- package/scripts/tests/test_policy_lint_rules_registry.py +116 -0
- package/scripts/tests/test_policy_lint_selection.py +34 -0
- package/scripts/tests/test_subprocess_window_access.py +118 -0
- package/scripts/tests/test_tracked_secrets.py +79 -0
- package/scripts/validate_instruction_pairs.py +3 -0
- package/scripts/verify_installable_package.py +5 -0
- package/scripts/automatic_advisory/window_flags.py +0 -41
- package/scripts/repository_checks/config/email_exemptions.py +0 -10
|
@@ -156,6 +156,16 @@ def test_should_exempt_fixme_with_trailing_prose() -> None:
|
|
|
156
156
|
assert _is_exempt_python_comment(token) is True
|
|
157
157
|
|
|
158
158
|
|
|
159
|
+
def test_should_exempt_stealth_keep_with_trailing_reason() -> None:
|
|
160
|
+
token = _build_comment_token("# STEALTH: Keep bypass reviewed in incident 4021")
|
|
161
|
+
assert _is_exempt_python_comment(token) is True
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_should_flag_stealth_marker_missing_required_space_after_colon() -> None:
|
|
165
|
+
token = _build_comment_token("# STEALTH:Keep bypass reviewed in incident 4021")
|
|
166
|
+
assert _is_exempt_python_comment(token) is False
|
|
167
|
+
|
|
168
|
+
|
|
159
169
|
def test_should_exempt_shebang_at_line_one_column_zero() -> None:
|
|
160
170
|
token = tokenize.TokenInfo(
|
|
161
171
|
type=tokenize.COMMENT,
|
|
@@ -111,6 +111,4 @@ ALL_RUNNER_FIELDS: frozenset[str] = frozenset(
|
|
|
111
111
|
)
|
|
112
112
|
AUTOMATIC_ADVISORY_DIRECTORY_NAME: str = "automatic_advisory"
|
|
113
113
|
AUTOMATIC_ADVISORY_CLI_FILE_NAME: str = "cli.py"
|
|
114
|
-
WINDOWS_PLATFORM: str = "win32"
|
|
115
|
-
CREATE_NO_WINDOW_ATTRIBUTE: str = "CREATE_NO_WINDOW"
|
|
116
114
|
POWERSHELL_CALL_OPERATOR: str = "& "
|
|
@@ -9,6 +9,8 @@ from git_hooks_constants import (
|
|
|
9
9
|
GH_PR_VIEW_TIMEOUT_SECONDS,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
def get_pull_request_url(repo_dir: Path) -> str | None:
|
|
14
16
|
try:
|
|
@@ -19,6 +21,7 @@ def get_pull_request_url(repo_dir: Path) -> str | None:
|
|
|
19
21
|
capture_output=True,
|
|
20
22
|
text=True,
|
|
21
23
|
timeout=GH_PR_VIEW_TIMEOUT_SECONDS,
|
|
24
|
+
creationflags=hidden_window_creation_flags(),
|
|
22
25
|
)
|
|
23
26
|
except (OSError, subprocess.SubprocessError):
|
|
24
27
|
return None
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Reach the hidden-window helpers from the scripts this directory deploys.
|
|
2
|
+
|
|
3
|
+
The installer copies this directory into a profile that holds no installed
|
|
4
|
+
Python distribution, and this repository's own tests run the deployed scripts
|
|
5
|
+
under ``python -S -E``, which hides every installed package from the child
|
|
6
|
+
process. On both routes ``hooks_constants`` is reachable only through the
|
|
7
|
+
deployed ``hooks`` directory, so this module puts that directory on
|
|
8
|
+
``sys.path`` and re-exports the helpers a caller asks for::
|
|
9
|
+
|
|
10
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
11
|
+
|
|
12
|
+
The directory goes on the end of ``sys.path``. ``PathFinder`` runs ahead of
|
|
13
|
+
every editable-install finder, so a trailing entry still selects the deployed
|
|
14
|
+
copy, and the head of ``sys.path`` stays exactly as the caller left it. A
|
|
15
|
+
caller that prepended its own roots keeps them first.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
_subprocess_window_hooks_directory = str(
|
|
24
|
+
Path(__file__).resolve().parents[2] / "hooks"
|
|
25
|
+
)
|
|
26
|
+
if _subprocess_window_hooks_directory not in sys.path:
|
|
27
|
+
sys.path.append(_subprocess_window_hooks_directory)
|
|
28
|
+
|
|
29
|
+
from hooks_constants.subprocess_window import (
|
|
30
|
+
HiddenWindowStartupInfo,
|
|
31
|
+
detached_hidden_window_creation_flags,
|
|
32
|
+
hidden_window_creation_flags,
|
|
33
|
+
inherited_stream_startup_info,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"HiddenWindowStartupInfo",
|
|
38
|
+
"detached_hidden_window_creation_flags",
|
|
39
|
+
"hidden_window_creation_flags",
|
|
40
|
+
"inherited_stream_startup_info",
|
|
41
|
+
]
|
|
@@ -15,6 +15,9 @@ from verification_notice import build_verification_notice
|
|
|
15
15
|
from verification_notice_context import VerificationNoticeContext
|
|
16
16
|
from verification_start import start_automatic_advisory
|
|
17
17
|
|
|
18
|
+
WINDOWS_NO_WINDOW_FLAG = 0x08000000
|
|
19
|
+
EXPECTED_HIDDEN_WINDOW_FLAGS = WINDOWS_NO_WINDOW_FLAG if sys.platform == "win32" else 0
|
|
20
|
+
|
|
18
21
|
|
|
19
22
|
def build_context(
|
|
20
23
|
repository_root: Path,
|
|
@@ -102,6 +105,7 @@ def _assert_launch_matches_runner(
|
|
|
102
105
|
assert launch_options["stdin"] is subprocess.DEVNULL
|
|
103
106
|
assert launch_options["stdout"] is subprocess.DEVNULL
|
|
104
107
|
assert launch_options["stderr"] is subprocess.DEVNULL
|
|
108
|
+
assert launch_options["creationflags"] == EXPECTED_HIDDEN_WINDOW_FLAGS
|
|
105
109
|
assert "--once" not in command
|
|
106
110
|
|
|
107
111
|
|
|
@@ -36,6 +36,8 @@ from git_hooks_constants.verification_notice_constants import (
|
|
|
36
36
|
)
|
|
37
37
|
from local_verification.manifest import ManifestRunFatal, load_manifest
|
|
38
38
|
|
|
39
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
40
|
+
|
|
39
41
|
|
|
40
42
|
@dataclass(frozen=True)
|
|
41
43
|
class VerificationNoticeContext:
|
|
@@ -202,6 +204,7 @@ def _run_git_query(
|
|
|
202
204
|
encoding=GIT_OUTPUT_ENCODING_NAME,
|
|
203
205
|
errors=GIT_OUTPUT_DECODE_ERRORS_POLICY,
|
|
204
206
|
timeout=GIT_COMMAND_TIMEOUT_SECONDS,
|
|
207
|
+
creationflags=hidden_window_creation_flags(),
|
|
205
208
|
)
|
|
206
209
|
except (OSError, subprocess.SubprocessError, UnicodeError):
|
|
207
210
|
return None
|
|
@@ -13,7 +13,6 @@ from git_hooks_constants.verification_notice_constants import (
|
|
|
13
13
|
ALL_RUNNER_FIELDS,
|
|
14
14
|
AUTOMATIC_ADVISORY_CLI_FILE_NAME,
|
|
15
15
|
AUTOMATIC_ADVISORY_DIRECTORY_NAME,
|
|
16
|
-
CREATE_NO_WINDOW_ATTRIBUTE,
|
|
17
16
|
EXECUTOR_DIRECTORY_NAME,
|
|
18
17
|
GIT_DIRECTORY_NAME,
|
|
19
18
|
JSON_ENCODING,
|
|
@@ -22,10 +21,15 @@ from git_hooks_constants.verification_notice_constants import (
|
|
|
22
21
|
RUNNER_PYTHON_FIELD,
|
|
23
22
|
RUNNER_SETTINGS_FIELD,
|
|
24
23
|
TARGET_REPOSITORY_REMOTE,
|
|
25
|
-
WINDOWS_PLATFORM,
|
|
26
24
|
)
|
|
27
25
|
from verification_notice_context import VerificationNoticeContext
|
|
28
26
|
|
|
27
|
+
_subprocess_window_hooks_directory = str(Path(__file__).resolve().parents[2] / "hooks")
|
|
28
|
+
if _subprocess_window_hooks_directory not in sys.path:
|
|
29
|
+
sys.path.append(_subprocess_window_hooks_directory)
|
|
30
|
+
|
|
31
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
32
|
+
|
|
29
33
|
|
|
30
34
|
def start_automatic_advisory(context: VerificationNoticeContext) -> None:
|
|
31
35
|
"""Start the detached advisory poller when native metadata is valid."""
|
|
@@ -66,7 +70,7 @@ def _launch_advisory(
|
|
|
66
70
|
stdin=subprocess.DEVNULL,
|
|
67
71
|
stdout=subprocess.DEVNULL,
|
|
68
72
|
stderr=subprocess.DEVNULL,
|
|
69
|
-
creationflags=
|
|
73
|
+
creationflags=hidden_window_creation_flags(),
|
|
70
74
|
)
|
|
71
75
|
|
|
72
76
|
|
|
@@ -93,8 +97,3 @@ def load_runner_configuration(
|
|
|
93
97
|
return None
|
|
94
98
|
return python_path, settings_path
|
|
95
99
|
|
|
96
|
-
|
|
97
|
-
def _windows_creation_flags() -> int:
|
|
98
|
-
if sys.platform != WINDOWS_PLATFORM:
|
|
99
|
-
return 0
|
|
100
|
-
return getattr(subprocess, CREATE_NO_WINDOW_ATTRIBUTE, 0)
|
|
@@ -132,12 +132,14 @@ ALL_TOKEN_ANCHORED_EXEMPT_COMMENT_BODIES: tuple[str, ...] = (
|
|
|
132
132
|
"pragma:",
|
|
133
133
|
)
|
|
134
134
|
ALL_TOKEN_ANCHORED_DIRECTIVE_BOUNDARY_CHARACTERS: frozenset[str] = frozenset({":"})
|
|
135
|
+
STEALTH_KEEP_COMMENT_MARKER: str = "STEALTH: Keep"
|
|
135
136
|
ALL_FREE_FORM_EXEMPT_COMMENT_BODIES: tuple[str, ...] = (
|
|
136
137
|
"type:",
|
|
137
138
|
"TODO",
|
|
138
139
|
"FIXME",
|
|
139
140
|
"HACK",
|
|
140
141
|
"XXX",
|
|
142
|
+
STEALTH_KEEP_COMMENT_MARKER,
|
|
141
143
|
)
|
|
142
144
|
CHAINED_INLINE_COMMENT_PATTERN = re.compile(r"#")
|
|
143
145
|
ALL_JAVASCRIPT_EXEMPT_COMMENT_PREFIXES: tuple[str, ...] = (
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Creation flags that keep a spawned child off the operator's screen on Windows.
|
|
2
|
+
|
|
3
|
+
A hook or script often runs with no console of its own. When such a process
|
|
4
|
+
starts a child on Windows, the operating system gives that child a brand new
|
|
5
|
+
console, and the operator sees a black window flash open and shut while agents
|
|
6
|
+
work. ``CREATE_NO_WINDOW`` tells Windows to skip that console. Every child this
|
|
7
|
+
package spawns passes the flag, so the desktop stays quiet::
|
|
8
|
+
|
|
9
|
+
subprocess.run(
|
|
10
|
+
["git", "status"],
|
|
11
|
+
creationflags=hidden_window_creation_flags(),
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
On Windows that call returns ``0x08000000`` and no window appears. On Linux and
|
|
15
|
+
macOS it returns ``0``, which is the value ``subprocess`` already uses when a
|
|
16
|
+
caller passes no ``creationflags`` at all, so behaviour there is unchanged.
|
|
17
|
+
``CREATE_NO_WINDOW`` is absent from a non-Windows Python build, so the lookup
|
|
18
|
+
reads the attribute at call time and falls back to ``0``.
|
|
19
|
+
|
|
20
|
+
``CREATE_NO_WINDOW`` gives the child a console of its own. A child that writes
|
|
21
|
+
to a stream the caller redirected is unaffected, because the redirected handle
|
|
22
|
+
travels with it. A child that inherits the caller's terminal instead loses that
|
|
23
|
+
terminal, and the operator stops seeing its output. A few call sites hand their
|
|
24
|
+
streams straight to the operator, and those pass
|
|
25
|
+
``startupinfo=inherited_stream_startup_info()`` instead. That startup record
|
|
26
|
+
keeps the inherited streams and hides the console window, so the operator still
|
|
27
|
+
reads the output and still sees no window.
|
|
28
|
+
|
|
29
|
+
``subprocess.STARTUPINFO`` is a Windows-only name. Typeshed declares it under a
|
|
30
|
+
``sys.platform == "win32"`` guard, so a type check that runs on Linux, as this
|
|
31
|
+
repository's continuous integration does, cannot see the name at all. The
|
|
32
|
+
startup record is therefore described here as a structural ``Protocol`` naming
|
|
33
|
+
the two fields this module sets, and the record itself is fetched through
|
|
34
|
+
``getattr`` at call time. Both moves match what the module already does for the
|
|
35
|
+
creation flags, and they keep the runtime ``sys.platform`` test in one place, so
|
|
36
|
+
one function serves every platform.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import subprocess
|
|
42
|
+
import sys
|
|
43
|
+
from typing import Protocol
|
|
44
|
+
|
|
45
|
+
from hooks_constants.subprocess_window_constants import (
|
|
46
|
+
CREATE_NEW_PROCESS_GROUP_ATTRIBUTE,
|
|
47
|
+
CREATE_NO_WINDOW_ATTRIBUTE,
|
|
48
|
+
HIDE_WINDOW_COMMAND_ATTRIBUTE,
|
|
49
|
+
NO_CREATION_FLAGS,
|
|
50
|
+
SHOW_WINDOW_FLAG_ATTRIBUTE,
|
|
51
|
+
STARTUP_INFO_ATTRIBUTE,
|
|
52
|
+
WINDOWS_PLATFORM,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
__all__ = [
|
|
56
|
+
"HiddenWindowStartupInfo",
|
|
57
|
+
"detached_hidden_window_creation_flags",
|
|
58
|
+
"hidden_window_creation_flags",
|
|
59
|
+
"inherited_stream_startup_info",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class HiddenWindowStartupInfo(Protocol):
|
|
64
|
+
"""The two ``subprocess.STARTUPINFO`` fields that hide a child's console."""
|
|
65
|
+
|
|
66
|
+
dwFlags: int
|
|
67
|
+
wShowWindow: int
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _windows_flag(flag_attribute_name: str) -> int:
|
|
71
|
+
"""Return one Windows creation flag, or zero on any other platform.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
flag_attribute_name: The name of a ``subprocess`` creation-flag constant.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
The flag value on Windows, and zero elsewhere or when the running
|
|
78
|
+
Python build does not define that constant.
|
|
79
|
+
"""
|
|
80
|
+
if sys.platform != WINDOWS_PLATFORM:
|
|
81
|
+
return NO_CREATION_FLAGS
|
|
82
|
+
return int(getattr(subprocess, flag_attribute_name, NO_CREATION_FLAGS))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def hidden_window_creation_flags() -> int:
|
|
86
|
+
"""Return the creation flags that start a child without a console window.
|
|
87
|
+
|
|
88
|
+
Pass the result as the ``creationflags`` argument of ``subprocess.run``,
|
|
89
|
+
``subprocess.Popen``, ``subprocess.check_output``, or ``subprocess.call``::
|
|
90
|
+
|
|
91
|
+
subprocess.run(["git", "rev-parse", "HEAD"],
|
|
92
|
+
creationflags=hidden_window_creation_flags())
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
The hidden-console flag on Windows, and zero on every other platform.
|
|
96
|
+
"""
|
|
97
|
+
return _windows_flag(CREATE_NO_WINDOW_ATTRIBUTE)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def detached_hidden_window_creation_flags() -> int:
|
|
101
|
+
"""Return the creation flags for a long-lived child that outlives its parent.
|
|
102
|
+
|
|
103
|
+
A detached poller needs its own process group so a console interrupt aimed
|
|
104
|
+
at the parent never reaches it, and it needs the hidden console for the same
|
|
105
|
+
reason every other child does::
|
|
106
|
+
|
|
107
|
+
subprocess.Popen(argv,
|
|
108
|
+
creationflags=detached_hidden_window_creation_flags())
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
A new process group combined with the hidden-console flag on Windows,
|
|
112
|
+
and zero on every other platform.
|
|
113
|
+
"""
|
|
114
|
+
return _windows_flag(CREATE_NEW_PROCESS_GROUP_ATTRIBUTE) | hidden_window_creation_flags()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def inherited_stream_startup_info() -> HiddenWindowStartupInfo | None:
|
|
118
|
+
"""Return a startup record that hides the console of a stream-inheriting child.
|
|
119
|
+
|
|
120
|
+
Use this at a call site that lets the child write straight to the operator's
|
|
121
|
+
terminal, where ``hidden_window_creation_flags`` would take that terminal
|
|
122
|
+
away::
|
|
123
|
+
|
|
124
|
+
subprocess.run(["python", "-m", "pytest"],
|
|
125
|
+
startupinfo=inherited_stream_startup_info())
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
A Windows startup record asking for a hidden window, and ``None`` on
|
|
129
|
+
every other platform, which is the value ``subprocess`` already uses
|
|
130
|
+
when a caller passes no ``startupinfo`` at all.
|
|
131
|
+
"""
|
|
132
|
+
if sys.platform != WINDOWS_PLATFORM:
|
|
133
|
+
return None
|
|
134
|
+
startup_information = getattr(subprocess, STARTUP_INFO_ATTRIBUTE)()
|
|
135
|
+
startup_information.dwFlags |= getattr(
|
|
136
|
+
subprocess, SHOW_WINDOW_FLAG_ATTRIBUTE, NO_CREATION_FLAGS
|
|
137
|
+
)
|
|
138
|
+
startup_information.wShowWindow = getattr(
|
|
139
|
+
subprocess, HIDE_WINDOW_COMMAND_ATTRIBUTE, NO_CREATION_FLAGS
|
|
140
|
+
)
|
|
141
|
+
return startup_information
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Configuration constants for the shared hidden-console creation flags."""
|
|
2
|
+
|
|
3
|
+
WINDOWS_PLATFORM: str = "win32"
|
|
4
|
+
CREATE_NO_WINDOW_ATTRIBUTE: str = "CREATE_NO_WINDOW"
|
|
5
|
+
CREATE_NEW_PROCESS_GROUP_ATTRIBUTE: str = "CREATE_NEW_PROCESS_GROUP"
|
|
6
|
+
STARTUP_INFO_ATTRIBUTE: str = "STARTUPINFO"
|
|
7
|
+
SHOW_WINDOW_FLAG_ATTRIBUTE: str = "STARTF_USESHOWWINDOW"
|
|
8
|
+
HIDE_WINDOW_COMMAND_ATTRIBUTE: str = "SW_HIDE"
|
|
9
|
+
NO_CREATION_FLAGS: int = 0
|
|
@@ -17,6 +17,7 @@ if str(_HOOKS_ROOT) not in sys.path:
|
|
|
17
17
|
sys.path.insert(0, str(_HOOKS_ROOT))
|
|
18
18
|
|
|
19
19
|
from hooks_constants.code_rules_enforcer_constants import (
|
|
20
|
+
ALL_FREE_FORM_EXEMPT_COMMENT_BODIES,
|
|
20
21
|
ALL_VALIDATION_PHASES,
|
|
21
22
|
CONFIG_DIRECTORY_SEGMENT,
|
|
22
23
|
CONSTANTS_MODULE_SUFFIX,
|
|
@@ -26,6 +27,7 @@ from hooks_constants.code_rules_enforcer_constants import (
|
|
|
26
27
|
JSDOC_RETURNS_STRUCTURED_OBJECT_PROMISE_PATTERN,
|
|
27
28
|
RETURN_CALL_OPENING_PARENTHESIS_PATTERN,
|
|
28
29
|
SCHEMA_OPTIONS_PROPERTY_KEY_PATTERN,
|
|
30
|
+
STEALTH_KEEP_COMMENT_MARKER,
|
|
29
31
|
UNKNOWN_VALIDATION_PHASE_MESSAGE_TEMPLATE,
|
|
30
32
|
apply_edits,
|
|
31
33
|
edits_for_tool,
|
|
@@ -106,6 +108,10 @@ def test_constants_module_suffix_and_config_directory_segment_are_defined() -> N
|
|
|
106
108
|
assert CONFIG_DIRECTORY_SEGMENT == "config"
|
|
107
109
|
|
|
108
110
|
|
|
111
|
+
def test_free_form_exempt_comment_bodies_include_stealth_keep_marker() -> None:
|
|
112
|
+
assert STEALTH_KEEP_COMMENT_MARKER in ALL_FREE_FORM_EXEMPT_COMMENT_BODIES
|
|
113
|
+
|
|
114
|
+
|
|
109
115
|
def test_validation_phase_constants_are_reexported_and_render() -> None:
|
|
110
116
|
assert ALL_VALIDATION_PHASES == {EDIT_LANE_PHASE, FULL_GATE_PHASE}
|
|
111
117
|
rendered_message = UNKNOWN_VALIDATION_PHASE_MESSAGE_TEMPLATE.format(
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Tests for the shared hidden-console creation flags."""
|
|
2
|
+
|
|
3
|
+
import pathlib
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from typing import Any, Mapping
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
_HOOKS_ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
11
|
+
if str(_HOOKS_ROOT) not in sys.path:
|
|
12
|
+
sys.path.insert(0, str(_HOOKS_ROOT))
|
|
13
|
+
|
|
14
|
+
from hooks_constants.subprocess_window import (
|
|
15
|
+
detached_hidden_window_creation_flags,
|
|
16
|
+
hidden_window_creation_flags,
|
|
17
|
+
inherited_stream_startup_info,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
WINDOWS_NO_WINDOW_FLAG = 0x08000000
|
|
21
|
+
WINDOWS_NEW_PROCESS_GROUP_FLAG = 0x00000200
|
|
22
|
+
WINDOWS_ONLY = pytest.mark.skipif(
|
|
23
|
+
sys.platform != "win32", reason="subprocess.STARTUPINFO exists only on Windows"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_windows_reports_the_hidden_console_flag(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
28
|
+
monkeypatch.setattr(sys, "platform", "win32")
|
|
29
|
+
monkeypatch.setattr(subprocess, "CREATE_NO_WINDOW", WINDOWS_NO_WINDOW_FLAG, raising=False)
|
|
30
|
+
assert hidden_window_creation_flags() == WINDOWS_NO_WINDOW_FLAG
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_other_platforms_report_no_creation_flags(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
34
|
+
monkeypatch.setattr(sys, "platform", "linux")
|
|
35
|
+
assert hidden_window_creation_flags() == 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_windows_without_the_flag_attribute_reports_no_creation_flags(
|
|
39
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
40
|
+
) -> None:
|
|
41
|
+
monkeypatch.setattr(sys, "platform", "win32")
|
|
42
|
+
monkeypatch.delattr(subprocess, "CREATE_NO_WINDOW", raising=False)
|
|
43
|
+
assert hidden_window_creation_flags() == 0
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_windows_detached_flags_add_a_new_process_group(
|
|
47
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
48
|
+
) -> None:
|
|
49
|
+
monkeypatch.setattr(sys, "platform", "win32")
|
|
50
|
+
monkeypatch.setattr(subprocess, "CREATE_NO_WINDOW", WINDOWS_NO_WINDOW_FLAG, raising=False)
|
|
51
|
+
monkeypatch.setattr(
|
|
52
|
+
subprocess,
|
|
53
|
+
"CREATE_NEW_PROCESS_GROUP",
|
|
54
|
+
WINDOWS_NEW_PROCESS_GROUP_FLAG,
|
|
55
|
+
raising=False,
|
|
56
|
+
)
|
|
57
|
+
assert detached_hidden_window_creation_flags() == (
|
|
58
|
+
WINDOWS_NO_WINDOW_FLAG | WINDOWS_NEW_PROCESS_GROUP_FLAG
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_other_platforms_report_no_detached_creation_flags(
|
|
63
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
64
|
+
) -> None:
|
|
65
|
+
monkeypatch.setattr(sys, "platform", "linux")
|
|
66
|
+
assert detached_hidden_window_creation_flags() == 0
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _captured_run_options(monkeypatch: pytest.MonkeyPatch) -> Mapping[str, Any]:
|
|
70
|
+
"""Run one command through a patched subprocess.run and return its options."""
|
|
71
|
+
all_captured_options: dict[str, Any] = {}
|
|
72
|
+
|
|
73
|
+
def fake_run(*_all_arguments: Any, **all_options: Any) -> None:
|
|
74
|
+
all_captured_options.update(all_options)
|
|
75
|
+
|
|
76
|
+
monkeypatch.setattr(subprocess, "run", fake_run)
|
|
77
|
+
subprocess.run(
|
|
78
|
+
["git", "status"],
|
|
79
|
+
creationflags=hidden_window_creation_flags(),
|
|
80
|
+
)
|
|
81
|
+
return all_captured_options
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_a_call_site_passes_the_hidden_flag_on_windows(
|
|
85
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
86
|
+
) -> None:
|
|
87
|
+
monkeypatch.setattr(sys, "platform", "win32")
|
|
88
|
+
monkeypatch.setattr(subprocess, "CREATE_NO_WINDOW", WINDOWS_NO_WINDOW_FLAG, raising=False)
|
|
89
|
+
assert _captured_run_options(monkeypatch)["creationflags"] == WINDOWS_NO_WINDOW_FLAG
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_a_call_site_passes_the_default_flags_off_windows(
|
|
93
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
94
|
+
) -> None:
|
|
95
|
+
monkeypatch.setattr(sys, "platform", "linux")
|
|
96
|
+
assert _captured_run_options(monkeypatch)["creationflags"] == 0
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@WINDOWS_ONLY
|
|
100
|
+
def test_windows_startup_info_asks_for_a_hidden_window(
|
|
101
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
102
|
+
) -> None:
|
|
103
|
+
monkeypatch.setattr(sys, "platform", "win32")
|
|
104
|
+
startup_information = inherited_stream_startup_info()
|
|
105
|
+
assert startup_information is not None
|
|
106
|
+
assert startup_information.dwFlags & subprocess.STARTF_USESHOWWINDOW
|
|
107
|
+
assert startup_information.wShowWindow == subprocess.SW_HIDE
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_other_platforms_report_no_startup_info(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
111
|
+
monkeypatch.setattr(sys, "platform", "linux")
|
|
112
|
+
assert inherited_stream_startup_info() is None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@WINDOWS_ONLY
|
|
116
|
+
def test_a_stream_inheriting_call_site_passes_no_creation_flags(
|
|
117
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
118
|
+
) -> None:
|
|
119
|
+
all_captured_options: dict[str, Any] = {}
|
|
120
|
+
|
|
121
|
+
def fake_run(*_all_arguments: Any, **all_options: Any) -> None:
|
|
122
|
+
all_captured_options.update(all_options)
|
|
123
|
+
|
|
124
|
+
monkeypatch.setattr(sys, "platform", "win32")
|
|
125
|
+
monkeypatch.setattr(subprocess, "run", fake_run)
|
|
126
|
+
subprocess.run(["python", "-m", "pytest"], startupinfo=inherited_stream_startup_info())
|
|
127
|
+
assert "creationflags" not in all_captured_options
|
|
128
|
+
assert all_captured_options["startupinfo"] is not None
|
|
@@ -44,6 +44,8 @@ from hooks_constants.enter_worktree_prefetch_constants import ( # noqa: E402
|
|
|
44
44
|
)
|
|
45
45
|
from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin # noqa: E402
|
|
46
46
|
|
|
47
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
48
|
+
|
|
47
49
|
|
|
48
50
|
def is_enter_worktree_creation(payload_by_field: dict[str, object]) -> bool:
|
|
49
51
|
"""Return True when this hook invocation is an EnterWorktree creation call.
|
|
@@ -91,6 +93,7 @@ def _run_git_quietly(
|
|
|
91
93
|
timeout=timeout_seconds,
|
|
92
94
|
cwd=repo_directory,
|
|
93
95
|
check=False,
|
|
96
|
+
creationflags=hidden_window_creation_flags(),
|
|
94
97
|
)
|
|
95
98
|
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
|
96
99
|
return None
|
|
@@ -57,6 +57,8 @@ from hooks_constants.mypy_validator_cache_constants import ( # noqa: E402
|
|
|
57
57
|
)
|
|
58
58
|
from json_file_reader import read_json_object # noqa: E402
|
|
59
59
|
|
|
60
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
61
|
+
|
|
60
62
|
|
|
61
63
|
def load_notification_utils() -> ModuleType | None:
|
|
62
64
|
try:
|
|
@@ -88,6 +90,7 @@ def discover_project_root(target_file: str) -> Path | None:
|
|
|
88
90
|
text=True,
|
|
89
91
|
timeout=GIT_COMMAND_TIMEOUT_SECONDS,
|
|
90
92
|
cwd=str(Path(target_file).parent),
|
|
93
|
+
creationflags=hidden_window_creation_flags(),
|
|
91
94
|
)
|
|
92
95
|
if completed_process.returncode != 0:
|
|
93
96
|
return None
|
|
@@ -493,6 +496,7 @@ def run_mypy(target_file: str, project_root: str) -> tuple[int, str]:
|
|
|
493
496
|
env=os.environ.copy(),
|
|
494
497
|
timeout=MYPY_TIMEOUT_SECONDS,
|
|
495
498
|
cwd=project_root,
|
|
499
|
+
creationflags=hidden_window_creation_flags(),
|
|
496
500
|
)
|
|
497
501
|
|
|
498
502
|
stdout_output = completed_process.stdout.strip()
|
|
@@ -31,6 +31,7 @@ The checks live in `python_style_checks.py`. Shared source-line splitting, funct
|
|
|
31
31
|
| `ruff_integration.py` | Ruff lint integration |
|
|
32
32
|
| `run_all_validators.py` | Pre-push validator orchestration and report |
|
|
33
33
|
| `security_checks.py` | Hardcoded-secret, SQL injection, and XSS checks |
|
|
34
|
+
| `subprocess_window_access.py` | Deployed-path shim re-exporting the hidden-window helpers |
|
|
34
35
|
| `system_temporary_roots.py` | System temp-root membership for staged copies |
|
|
35
36
|
| `todo_checks.py` | TODO/FIXME tracking with issue-reference requirement |
|
|
36
37
|
| `type_safety_checks.py` | Missing type hints and Any usage checks |
|
|
@@ -6,6 +6,8 @@ import sys
|
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from typing import List
|
|
8
8
|
|
|
9
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
10
|
+
|
|
9
11
|
SUBPROCESS_TIMEOUT_SECONDS = 30
|
|
10
12
|
|
|
11
13
|
|
|
@@ -26,6 +28,7 @@ def get_current_branch() -> str:
|
|
|
26
28
|
text=True,
|
|
27
29
|
check=True,
|
|
28
30
|
timeout=SUBPROCESS_TIMEOUT_SECONDS,
|
|
31
|
+
creationflags=hidden_window_creation_flags(),
|
|
29
32
|
)
|
|
30
33
|
return result.stdout.strip()
|
|
31
34
|
except (FileNotFoundError, subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
|
@@ -55,6 +58,7 @@ def check_draft_pr_state() -> List[Violation]:
|
|
|
55
58
|
text=True,
|
|
56
59
|
check=True,
|
|
57
60
|
timeout=SUBPROCESS_TIMEOUT_SECONDS,
|
|
61
|
+
creationflags=hidden_window_creation_flags(),
|
|
58
62
|
)
|
|
59
63
|
except FileNotFoundError:
|
|
60
64
|
return []
|
|
@@ -16,6 +16,8 @@ from enum import StrEnum
|
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
from typing import Dict, Optional
|
|
18
18
|
|
|
19
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
20
|
+
|
|
19
21
|
VALIDATOR_FILES = [
|
|
20
22
|
"python_style_checks.py",
|
|
21
23
|
"test_safety_checks.py",
|
|
@@ -144,6 +146,7 @@ def check_optional_tool(tool_name: str) -> bool:
|
|
|
144
146
|
check=False,
|
|
145
147
|
capture_output=True,
|
|
146
148
|
text=True,
|
|
149
|
+
creationflags=hidden_window_creation_flags(),
|
|
147
150
|
)
|
|
148
151
|
return result.returncode == 0
|
|
149
152
|
except FileNotFoundError:
|
|
@@ -41,6 +41,8 @@ from pathlib import Path
|
|
|
41
41
|
from types import ModuleType
|
|
42
42
|
from typing import TextIO
|
|
43
43
|
|
|
44
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
45
|
+
|
|
44
46
|
|
|
45
47
|
def _load_module_from_path(module_name: str, module_path: Path) -> ModuleType:
|
|
46
48
|
"""Load and execute the module found at module_path, under module_name.
|
|
@@ -197,6 +199,7 @@ def run_hosted_command_once_milliseconds(
|
|
|
197
199
|
text=True,
|
|
198
200
|
timeout=_harness_constants.SUBPROCESS_TIMEOUT_SECONDS,
|
|
199
201
|
check=False,
|
|
202
|
+
creationflags=hidden_window_creation_flags(),
|
|
200
203
|
)
|
|
201
204
|
finished_at = time.perf_counter()
|
|
202
205
|
return (finished_at - started_at) * 1000.0
|
|
@@ -9,6 +9,8 @@ from collections.abc import Iterator
|
|
|
9
9
|
from dataclasses import dataclass
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
13
|
+
|
|
12
14
|
_validators_directory = str(Path(__file__).resolve().parent)
|
|
13
15
|
_hooks_directory = str(Path(__file__).resolve().parent.parent)
|
|
14
16
|
|
|
@@ -66,6 +68,7 @@ def check_mypy_available() -> bool:
|
|
|
66
68
|
check=False,
|
|
67
69
|
capture_output=True,
|
|
68
70
|
text=True,
|
|
71
|
+
creationflags=hidden_window_creation_flags(),
|
|
69
72
|
)
|
|
70
73
|
return result.returncode == 0
|
|
71
74
|
except FileNotFoundError:
|
|
@@ -224,6 +227,7 @@ def _run_mypy_subprocess(
|
|
|
224
227
|
text=True,
|
|
225
228
|
cwd=working_directory,
|
|
226
229
|
timeout=timeout_seconds,
|
|
230
|
+
creationflags=hidden_window_creation_flags(),
|
|
227
231
|
)
|
|
228
232
|
except subprocess.TimeoutExpired:
|
|
229
233
|
logger.warning(detached_timeout_skip_message)
|
|
@@ -6,6 +6,8 @@ import sys
|
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
10
|
+
|
|
9
11
|
_validators_directory = str(Path(__file__).resolve().parent)
|
|
10
12
|
_hooks_directory = str(Path(__file__).resolve().parent.parent)
|
|
11
13
|
|
|
@@ -53,6 +55,7 @@ def check_ruff_available() -> bool:
|
|
|
53
55
|
check=False,
|
|
54
56
|
capture_output=True,
|
|
55
57
|
text=True,
|
|
58
|
+
creationflags=hidden_window_creation_flags(),
|
|
56
59
|
)
|
|
57
60
|
return result.returncode == 0
|
|
58
61
|
except FileNotFoundError:
|
|
@@ -201,6 +204,7 @@ def _staged_ruff_result(
|
|
|
201
204
|
input=staged_content,
|
|
202
205
|
cwd=str(resolved_pyproject.parent),
|
|
203
206
|
env=_ruff_subprocess_environment(),
|
|
207
|
+
creationflags=hidden_window_creation_flags(),
|
|
204
208
|
)
|
|
205
209
|
return RuffResult(
|
|
206
210
|
passed=result.returncode == 0,
|
|
@@ -247,6 +251,7 @@ def _run_native_ruff_check(
|
|
|
247
251
|
capture_output=True,
|
|
248
252
|
text=True,
|
|
249
253
|
env=_ruff_subprocess_environment(),
|
|
254
|
+
creationflags=hidden_window_creation_flags(),
|
|
250
255
|
)
|
|
251
256
|
return RuffResult(
|
|
252
257
|
passed=result.returncode == 0,
|
|
@@ -308,6 +313,7 @@ def run_ruff_fix(all_files: list[Path]) -> RuffResult:
|
|
|
308
313
|
capture_output=True,
|
|
309
314
|
text=True,
|
|
310
315
|
env=_ruff_subprocess_environment(),
|
|
316
|
+
creationflags=hidden_window_creation_flags(),
|
|
311
317
|
)
|
|
312
318
|
return RuffResult(
|
|
313
319
|
passed=result.returncode == 0,
|