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.
Files changed (125) hide show
  1. package/.agents/skills/everything-search/scripts/everything_search.py +11 -0
  2. package/.agents/skills/everything-search/scripts/test_everything_search.py +1 -0
  3. package/.agents/skills/fresh-branch/SKILL.md +1 -0
  4. package/.agents/skills/fresh-branch/scripts/fresh_branch_git_commands.py +3 -0
  5. package/.agents/skills/fresh-branch/scripts/subprocess_window_access.py +43 -0
  6. package/.agents/skills/session-continuity/README.md +19 -9
  7. package/.agents/skills/syncing-submodules/SKILL.md +1 -0
  8. package/.agents/skills/syncing-submodules/scripts/submodule_sync.py +4 -0
  9. package/.agents/skills/syncing-submodules/scripts/subprocess_window_access.py +43 -0
  10. package/.agents/skills/test-runner/SKILL.md +1 -0
  11. package/.agents/skills/test-runner/scripts/preflight_checks.py +5 -0
  12. package/.agents/skills/test-runner/scripts/run_tests.py +11 -0
  13. package/.agents/skills/test-runner/scripts/subprocess_window_access.py +43 -0
  14. package/.agents/skills/test-runner/scripts/test_run_tests.py +9 -7
  15. package/_shared/pr-loop/scripts/check_bugbot_ci.py +3 -0
  16. package/_shared/pr-loop/scripts/check_convergence_gates.py +3 -0
  17. package/_shared/pr-loop/scripts/check_pending_reviews.py +3 -0
  18. package/_shared/pr-loop/scripts/code_rules_gate_parts/added_line_maps.py +3 -0
  19. package/_shared/pr-loop/scripts/code_rules_gate_parts/baseline_import_isolation.py +3 -0
  20. package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +5 -0
  21. package/_shared/pr-loop/scripts/code_rules_gate_parts/git_file_sets.py +4 -0
  22. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_regression.py +3 -0
  23. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +3 -0
  24. package/_shared/pr-loop/scripts/codex_usage_probe.py +3 -0
  25. package/_shared/pr-loop/scripts/copilot_quota.py +3 -0
  26. package/_shared/pr-loop/scripts/fetch_copilot_reviews.py +3 -0
  27. package/_shared/pr-loop/scripts/fix_hookspath.py +7 -0
  28. package/_shared/pr-loop/scripts/post_fix_reply.py +4 -0
  29. package/_shared/pr-loop/scripts/preflight.py +7 -0
  30. package/_shared/pr-loop/scripts/preflight_self_heal.py +5 -0
  31. package/_shared/pr-loop/scripts/run_codex_review.py +3 -0
  32. package/_shared/pr-loop/scripts/subprocess_window_access.py +41 -0
  33. package/_shared/pr-loop/scripts/terminology_sweep.py +5 -0
  34. package/_shared/process-tree/scripts/process_tree_kill.py +3 -0
  35. package/_shared/process-tree/scripts/subprocess_window_access.py +41 -0
  36. package/bin/install-constants.mjs +4 -2
  37. package/bin/install-session-continuity.mjs +73 -30
  38. package/bin/install-session-continuity.test.mjs +52 -1
  39. package/bin/install.cursor-rules.test.mjs +57 -15
  40. package/bin/install.mjs +80 -15
  41. package/bin/install.test.mjs +57 -0
  42. package/bin/pstack-model-preferences.claude.json +23 -0
  43. package/hooks/advisory/pr_done_reminder.py +7 -0
  44. package/hooks/advisory/refactor_guard.py +8 -0
  45. package/hooks/advisory/test_pr_done_reminder.py +21 -0
  46. package/hooks/advisory/test_refactor_guard.py +40 -0
  47. package/hooks/blocking/block_main_commit.py +4 -0
  48. package/hooks/blocking/code_rules_comments.py +39 -5
  49. package/hooks/blocking/pii_prevention_blocker_parts/repository_exemption.py +3 -0
  50. package/hooks/blocking/precommit_code_rules_gate.py +3 -0
  51. package/hooks/blocking/tdd_enforcer_parts/content_analysis.py +19 -3
  52. package/hooks/blocking/tdd_enforcer_parts/git_tracking.py +4 -0
  53. package/hooks/blocking/tdd_enforcer_parts/tests/test_content_analysis.py +25 -0
  54. package/hooks/blocking/test_code_rules_enforcer_comment_string_awareness.py +27 -0
  55. package/hooks/blocking/test_code_rules_enforcer_exempt_marker_chained.py +10 -0
  56. package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +0 -2
  57. package/hooks/git-hooks/pull_request_handoff.py +3 -0
  58. package/hooks/git-hooks/subprocess_window_access.py +41 -0
  59. package/hooks/git-hooks/test_verification_start.py +4 -0
  60. package/hooks/git-hooks/verification_notice_context.py +3 -0
  61. package/hooks/git-hooks/verification_start.py +7 -8
  62. package/hooks/hooks_constants/code_rules_enforcer_constants.py +2 -0
  63. package/hooks/hooks_constants/subprocess_window.py +141 -0
  64. package/hooks/hooks_constants/subprocess_window_constants.py +9 -0
  65. package/hooks/hooks_constants/test_code_rules_enforcer_constants.py +6 -0
  66. package/hooks/hooks_constants/test_subprocess_window.py +128 -0
  67. package/hooks/lifecycle/enter_worktree_origin_prefetch.py +3 -0
  68. package/hooks/validation/mypy_validator.py +4 -0
  69. package/hooks/validators/README.md +1 -0
  70. package/hooks/validators/git_checks.py +4 -0
  71. package/hooks/validators/health_check.py +3 -0
  72. package/hooks/validators/hook_timing_harness.py +3 -0
  73. package/hooks/validators/mypy_integration.py +4 -0
  74. package/hooks/validators/ruff_integration.py +6 -0
  75. package/hooks/validators/run_all_validators.py +7 -0
  76. package/hooks/validators/subprocess_window_access.py +41 -0
  77. package/hooks/workflow/auto_formatter.py +8 -0
  78. package/hooks/workflow/test_auto_formatter.py +39 -0
  79. package/package.json +1 -1
  80. package/rules/pstack-models.md +4 -3
  81. package/scripts/automatic_advisory/checkout.py +8 -1
  82. package/scripts/automatic_advisory/cli.py +9 -3
  83. package/scripts/automatic_advisory/config/constants.py +0 -2
  84. package/scripts/automatic_advisory/execution.py +7 -1
  85. package/scripts/automatic_advisory/git.py +9 -1
  86. package/scripts/automatic_advisory/process_host.py +3 -0
  87. package/scripts/claude_chain_runner.py +3 -0
  88. package/scripts/gh_artifact_upload.py +3 -0
  89. package/scripts/grok_headless_runner.py +3 -0
  90. package/scripts/grok_patch_artifacts.py +4 -0
  91. package/scripts/grok_worker_preflight.py +3 -0
  92. package/scripts/invoke_code_review.py +3 -0
  93. package/scripts/local_verification/command_runner.py +3 -0
  94. package/scripts/local_verification/git_state.py +3 -0
  95. package/scripts/policy_lint/adapter_detectors.py +48 -2
  96. package/scripts/policy_lint/adapter_pairing.py +150 -2
  97. package/scripts/policy_lint/adapters.py +46 -4
  98. package/scripts/policy_lint/config/approved_test_pairs.py +25 -9
  99. package/scripts/policy_lint/config/constants.py +3 -0
  100. package/scripts/policy_lint/registry.py +1 -1
  101. package/scripts/policy_lint/selection_git.py +11 -0
  102. package/scripts/repository_checks/config/constants.py +5 -0
  103. package/scripts/repository_checks/config/policy_document.py +19 -0
  104. package/scripts/repository_checks/email_exemptions.py +14 -71
  105. package/scripts/repository_checks/path_exemptions.py +35 -0
  106. package/scripts/repository_checks/policy_document.py +120 -0
  107. package/scripts/repository_checks/tracked_secrets.py +27 -4
  108. package/scripts/setup_project_paths.py +3 -0
  109. package/scripts/spawn_grok_batch.py +3 -0
  110. package/scripts/subprocess_window_access.py +41 -0
  111. package/scripts/test_dispatcher_profile_import.py +4 -0
  112. package/scripts/tests/test_automatic_advisory_window_flags.py +17 -11
  113. package/scripts/tests/test_email_exemptions.py +37 -0
  114. package/scripts/tests/test_path_exemptions.py +164 -0
  115. package/scripts/tests/test_policy_document.py +104 -0
  116. package/scripts/tests/test_policy_lint_rules_function_length_scope.py +101 -0
  117. package/scripts/tests/test_policy_lint_rules_pairing.py +375 -0
  118. package/scripts/tests/test_policy_lint_rules_registry.py +116 -0
  119. package/scripts/tests/test_policy_lint_selection.py +34 -0
  120. package/scripts/tests/test_subprocess_window_access.py +118 -0
  121. package/scripts/tests/test_tracked_secrets.py +79 -0
  122. package/scripts/validate_instruction_pairs.py +3 -0
  123. package/scripts/verify_installable_package.py +5 -0
  124. package/scripts/automatic_advisory/window_flags.py +0 -41
  125. 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=_windows_creation_flags(),
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,