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
|
@@ -60,6 +60,8 @@ from hooks_constants.multi_edit_reconstruction import (
|
|
|
60
60
|
edits_for_tool,
|
|
61
61
|
)
|
|
62
62
|
|
|
63
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
64
|
+
|
|
63
65
|
VALIDATORS_DIR = Path(__file__).parent
|
|
64
66
|
hooks_dir = VALIDATORS_DIR.parent
|
|
65
67
|
package_name = VALIDATORS_DIR.name
|
|
@@ -131,6 +133,7 @@ def invoke_validator_module(module_stem: str, forwarded_file_paths: List[str]) -
|
|
|
131
133
|
text=True,
|
|
132
134
|
cwd=working_directory_string,
|
|
133
135
|
env=environment,
|
|
136
|
+
creationflags=hidden_window_creation_flags(),
|
|
134
137
|
)
|
|
135
138
|
|
|
136
139
|
|
|
@@ -160,6 +163,7 @@ def run_validators_entrypoint_subprocess(
|
|
|
160
163
|
cwd=working_directory_string,
|
|
161
164
|
env=environment,
|
|
162
165
|
input=stdin_text,
|
|
166
|
+
creationflags=hidden_window_creation_flags(),
|
|
163
167
|
)
|
|
164
168
|
|
|
165
169
|
|
|
@@ -347,6 +351,7 @@ def get_project_root() -> Optional[Path]:
|
|
|
347
351
|
["git", "-C", str(hooks_dir), "rev-parse", "--show-toplevel"],
|
|
348
352
|
capture_output=True,
|
|
349
353
|
text=True,
|
|
354
|
+
creationflags=hidden_window_creation_flags(),
|
|
350
355
|
)
|
|
351
356
|
if completed_git_lookup.returncode == 0:
|
|
352
357
|
return Path(completed_git_lookup.stdout.strip())
|
|
@@ -674,6 +679,7 @@ def get_changed_files() -> List[Path]:
|
|
|
674
679
|
["git", "diff", "--cached", "--name-only"],
|
|
675
680
|
capture_output=True,
|
|
676
681
|
text=True,
|
|
682
|
+
creationflags=hidden_window_creation_flags(),
|
|
677
683
|
)
|
|
678
684
|
|
|
679
685
|
files = result.stdout.strip().split("\n") if result.stdout.strip() else []
|
|
@@ -684,6 +690,7 @@ def get_changed_files() -> List[Path]:
|
|
|
684
690
|
["git", "diff", "--name-only", "HEAD~1"],
|
|
685
691
|
capture_output=True,
|
|
686
692
|
text=True,
|
|
693
|
+
creationflags=hidden_window_creation_flags(),
|
|
687
694
|
)
|
|
688
695
|
files = result.stdout.strip().split("\n") if result.stdout.strip() else []
|
|
689
696
|
|
|
@@ -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
|
+
]
|
|
@@ -14,6 +14,12 @@ from collections.abc import Mapping
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from types import ModuleType
|
|
16
16
|
|
|
17
|
+
_subprocess_window_hooks_directory = str(Path(__file__).resolve().parents[2] / "hooks")
|
|
18
|
+
if _subprocess_window_hooks_directory not in sys.path:
|
|
19
|
+
sys.path.append(_subprocess_window_hooks_directory)
|
|
20
|
+
|
|
21
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
22
|
+
|
|
17
23
|
NOTIFICATION_UTILS_DIRECTORY = os.path.join(
|
|
18
24
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "notification"
|
|
19
25
|
)
|
|
@@ -118,6 +124,7 @@ def is_untracked_in_git(file_path: str) -> bool:
|
|
|
118
124
|
cwd=containing_directory,
|
|
119
125
|
timeout=GIT_LS_FILES_TIMEOUT_SECONDS,
|
|
120
126
|
env=_build_git_command_environment(),
|
|
127
|
+
creationflags=hidden_window_creation_flags(),
|
|
121
128
|
)
|
|
122
129
|
return git_check.returncode != 0
|
|
123
130
|
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
@@ -198,6 +205,7 @@ def _run_command(
|
|
|
198
205
|
capture_output=True,
|
|
199
206
|
text=True,
|
|
200
207
|
timeout=timeout_seconds,
|
|
208
|
+
creationflags=hidden_window_creation_flags(),
|
|
201
209
|
)
|
|
202
210
|
except FileNotFoundError:
|
|
203
211
|
return None, False
|
|
@@ -35,6 +35,8 @@ HOOKS_JSON_PATH = os.path.join(HOOKS_DIRECTORY_PATH, "hooks.json")
|
|
|
35
35
|
POST_TOOL_USE_DISPATCHER_COMMAND_FRAGMENT = "validation/post_tool_use_dispatcher.py"
|
|
36
36
|
UNUSED_IMPORT_SOURCE = "import os\n\n\nVALUE = 1\n"
|
|
37
37
|
HOOK_RUN_TIMEOUT_SECONDS = 60
|
|
38
|
+
WINDOWS_NO_WINDOW_FLAG = 0x08000000
|
|
39
|
+
EXPECTED_HIDDEN_WINDOW_FLAGS = WINDOWS_NO_WINDOW_FLAG if sys.platform == "win32" else 0
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
def build_fixture_git_environment() -> dict[str, str]:
|
|
@@ -415,3 +417,40 @@ class TestPythonFormatTimeoutBudget:
|
|
|
415
417
|
budgeted_total = auto_formatter_module.budgeted_python_format_seconds()
|
|
416
418
|
|
|
417
419
|
assert budgeted_total < _registered_auto_formatter_timeout()
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def _record_creation_flags(
|
|
423
|
+
auto_formatter_module: ModuleType, monkeypatch: pytest.MonkeyPatch
|
|
424
|
+
) -> dict[str, object]:
|
|
425
|
+
all_recorded_keyword_arguments: dict[str, object] = {}
|
|
426
|
+
|
|
427
|
+
def _record(*args: object, **kwargs: object) -> subprocess.CompletedProcess[str]:
|
|
428
|
+
del args
|
|
429
|
+
all_recorded_keyword_arguments.update(kwargs)
|
|
430
|
+
return subprocess.CompletedProcess([], 1, stdout="", stderr="")
|
|
431
|
+
|
|
432
|
+
monkeypatch.setattr(auto_formatter_module.subprocess, "run", _record)
|
|
433
|
+
return all_recorded_keyword_arguments
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def test_untracked_probe_should_start_without_a_console_window(
|
|
437
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
438
|
+
) -> None:
|
|
439
|
+
auto_formatter_module = _load_auto_formatter_module()
|
|
440
|
+
all_recorded_keyword_arguments = _record_creation_flags(auto_formatter_module, monkeypatch)
|
|
441
|
+
|
|
442
|
+
assert auto_formatter_module.is_untracked_in_git(str(tmp_path / "module.py")) is True
|
|
443
|
+
assert all_recorded_keyword_arguments["creationflags"] == EXPECTED_HIDDEN_WINDOW_FLAGS
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def test_formatter_command_should_start_without_a_console_window(
|
|
447
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
448
|
+
) -> None:
|
|
449
|
+
auto_formatter_module = _load_auto_formatter_module()
|
|
450
|
+
all_recorded_keyword_arguments = _record_creation_flags(auto_formatter_module, monkeypatch)
|
|
451
|
+
|
|
452
|
+
auto_formatter_module._run_command(
|
|
453
|
+
["ruff", "format"], str(tmp_path / "module.py"), HOOK_RUN_TIMEOUT_SECONDS
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
assert all_recorded_keyword_arguments["creationflags"] == EXPECTED_HIDDEN_WINDOW_FLAGS
|
package/package.json
CHANGED
package/rules/pstack-models.md
CHANGED
|
@@ -28,9 +28,10 @@ The file contains this shape:
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Offer only model IDs the active native tool accepts. Keep the user's order.
|
|
31
|
-
Treat `inherit-parent` and `auto` as parent inheritance, not model IDs. A
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
Treat `inherit-parent` and `auto` as parent inheritance, not model IDs. A full
|
|
32
|
+
install seeds the Claude file and the Codex file from the package when a host
|
|
33
|
+
file is absent, and leaves an existing file untouched. A different host never
|
|
34
|
+
copies another host's preference file.
|
|
34
35
|
|
|
35
36
|
## Selection before every delegation
|
|
36
37
|
|
|
@@ -10,7 +10,14 @@ from .config.constants import (
|
|
|
10
10
|
GIT_EXECUTABLE,
|
|
11
11
|
)
|
|
12
12
|
from .model import AdvisoryRegistration, LocalCheckout
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
_subprocess_window_hooks_directory = str(Path(__file__).resolve().parents[2] / "hooks")
|
|
17
|
+
if _subprocess_window_hooks_directory not in sys.path:
|
|
18
|
+
sys.path.append(_subprocess_window_hooks_directory)
|
|
19
|
+
|
|
20
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
14
21
|
|
|
15
22
|
|
|
16
23
|
def read_local_checkout(checkout_path: Path) -> LocalCheckout:
|
|
@@ -38,7 +38,14 @@ from automatic_advisory.runner import (
|
|
|
38
38
|
AdvisoryGitHub,
|
|
39
39
|
AutomaticAdvisoryRunner,
|
|
40
40
|
)
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
_subprocess_window_hooks_directory = str(Path(__file__).resolve().parents[2] / "hooks")
|
|
43
|
+
if _subprocess_window_hooks_directory not in sys.path:
|
|
44
|
+
sys.path.append(_subprocess_window_hooks_directory)
|
|
45
|
+
|
|
46
|
+
from hooks_constants.subprocess_window import (
|
|
47
|
+
detached_hidden_window_creation_flags,
|
|
48
|
+
)
|
|
42
49
|
|
|
43
50
|
|
|
44
51
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -134,7 +141,6 @@ def start_polling(settings_path: Path) -> int:
|
|
|
134
141
|
Returns:
|
|
135
142
|
Zero after the child process starts.
|
|
136
143
|
"""
|
|
137
|
-
process_flags = detached_poller_creation_flags()
|
|
138
144
|
resolved_settings_path = settings_path.resolve()
|
|
139
145
|
subprocess.Popen(
|
|
140
146
|
(
|
|
@@ -148,7 +154,7 @@ def start_polling(settings_path: Path) -> int:
|
|
|
148
154
|
stdin=subprocess.DEVNULL,
|
|
149
155
|
stdout=subprocess.DEVNULL,
|
|
150
156
|
stderr=subprocess.DEVNULL,
|
|
151
|
-
creationflags=
|
|
157
|
+
creationflags=detached_hidden_window_creation_flags(),
|
|
152
158
|
start_new_session=True,
|
|
153
159
|
)
|
|
154
160
|
return SUCCESS_EXIT_CODE
|
|
@@ -75,8 +75,6 @@ WINDOWS_EXTENDED_LIMIT_INFORMATION_CLASS = 9
|
|
|
75
75
|
WINDOWS_JOB_KILL_ON_CLOSE_LIMIT = 0x2000
|
|
76
76
|
DEFAULT_CLONE_URL_SUFFIX = ".git"
|
|
77
77
|
POLL_LOCK_DIRECTORY_NAME = ".poll-process"
|
|
78
|
-
CREATE_NEW_PROCESS_GROUP_ATTRIBUTE = "CREATE_NEW_PROCESS_GROUP"
|
|
79
|
-
CREATE_NO_WINDOW_ATTRIBUTE = "CREATE_NO_WINDOW"
|
|
80
78
|
CHILD_MANIFEST_FLAG = "--manifest"
|
|
81
79
|
CHILD_REPOSITORY_FLAG = "--repo"
|
|
82
80
|
CHILD_BASE_FLAG = "--base"
|
|
@@ -26,7 +26,6 @@ from .config.constants import (
|
|
|
26
26
|
)
|
|
27
27
|
from .config.timing import CHILD_CLEANUP_TIMEOUT_SECONDS
|
|
28
28
|
from .model import AdvisoryRegistration, ChildOutcome
|
|
29
|
-
from .window_flags import hidden_window_creation_flags
|
|
30
29
|
from .windows_job import (
|
|
31
30
|
_assign_process,
|
|
32
31
|
_close_handle,
|
|
@@ -34,6 +33,12 @@ from .windows_job import (
|
|
|
34
33
|
_terminate_job,
|
|
35
34
|
)
|
|
36
35
|
|
|
36
|
+
_subprocess_window_hooks_directory = str(Path(__file__).resolve().parents[2] / "hooks")
|
|
37
|
+
if _subprocess_window_hooks_directory not in sys.path:
|
|
38
|
+
sys.path.append(_subprocess_window_hooks_directory)
|
|
39
|
+
|
|
40
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
41
|
+
|
|
37
42
|
|
|
38
43
|
@dataclass(frozen=True)
|
|
39
44
|
class OwnedChildProcess:
|
|
@@ -165,6 +170,7 @@ def _start_posix_child(
|
|
|
165
170
|
text=True,
|
|
166
171
|
env=all_child_environment,
|
|
167
172
|
start_new_session=True,
|
|
173
|
+
creationflags=hidden_window_creation_flags(),
|
|
168
174
|
)
|
|
169
175
|
except OSError:
|
|
170
176
|
return None
|
|
@@ -17,7 +17,15 @@ from .config.constants import (
|
|
|
17
17
|
)
|
|
18
18
|
from .config.timing import GIT_FETCH_TIMEOUT_SECONDS
|
|
19
19
|
from .model import AdvisoryRegistration
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
_subprocess_window_hooks_directory = str(Path(__file__).resolve().parents[2] / "hooks")
|
|
25
|
+
if _subprocess_window_hooks_directory not in sys.path:
|
|
26
|
+
sys.path.append(_subprocess_window_hooks_directory)
|
|
27
|
+
|
|
28
|
+
from hooks_constants.subprocess_window import hidden_window_creation_flags
|
|
21
29
|
|
|
22
30
|
|
|
23
31
|
def _fetch_base(
|
|
@@ -8,6 +8,8 @@ from pr_verification.config.constants import INCOMPLETE_EXIT_CODE
|
|
|
8
8
|
|
|
9
9
|
from .config.constants import WINDOWS_PROCESS_START_SIGNAL
|
|
10
10
|
|
|
11
|
+
from subprocess_window_access import inherited_stream_startup_info
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
def run_owned_child(all_arguments: Sequence[str]) -> int:
|
|
13
15
|
"""Wait for ownership, then run the advisory child.
|
|
@@ -24,6 +26,7 @@ def run_owned_child(all_arguments: Sequence[str]) -> int:
|
|
|
24
26
|
tuple(all_arguments),
|
|
25
27
|
shell=False,
|
|
26
28
|
check=False,
|
|
29
|
+
startupinfo=inherited_stream_startup_info(),
|
|
27
30
|
)
|
|
28
31
|
return completed_process.returncode
|
|
29
32
|
|
|
@@ -131,6 +131,8 @@ from dev_env_scripts_constants.claude_chain_constants import (
|
|
|
131
131
|
UTF8_ENCODING,
|
|
132
132
|
)
|
|
133
133
|
|
|
134
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
135
|
+
|
|
134
136
|
|
|
135
137
|
def _decode_captured_stream(raw_bytes: bytes, encoding: str, errors: str) -> str:
|
|
136
138
|
"""Decode captured *raw_bytes* with ``text=True`` universal-newline semantics.
|
|
@@ -222,6 +224,7 @@ def _run_captured_subprocess(
|
|
|
222
224
|
else None
|
|
223
225
|
),
|
|
224
226
|
input=input_bytes,
|
|
227
|
+
creationflags=hidden_window_creation_flags(),
|
|
225
228
|
)
|
|
226
229
|
except subprocess.TimeoutExpired as timeout_error:
|
|
227
230
|
_attach_partial_timeout_streams(
|
|
@@ -41,6 +41,8 @@ from dev_env_scripts_constants.gh_artifact_upload_constants import (
|
|
|
41
41
|
UTF8_ENCODING,
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
45
|
+
|
|
44
46
|
|
|
45
47
|
class ArtifactUploadError(Exception):
|
|
46
48
|
"""Raised when creating the release or uploading the asset fails."""
|
|
@@ -53,6 +55,7 @@ def _run_gh(all_arguments: list[str]) -> subprocess.CompletedProcess[str]:
|
|
|
53
55
|
text=True,
|
|
54
56
|
encoding=UTF8_ENCODING,
|
|
55
57
|
check=False,
|
|
58
|
+
creationflags=hidden_window_creation_flags(),
|
|
56
59
|
)
|
|
57
60
|
|
|
58
61
|
|
|
@@ -85,6 +85,8 @@ from process_tree_kill import ( # noqa: E402
|
|
|
85
85
|
terminate_process_tree,
|
|
86
86
|
)
|
|
87
87
|
|
|
88
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
89
|
+
|
|
88
90
|
runner_popen = subprocess.Popen
|
|
89
91
|
|
|
90
92
|
|
|
@@ -361,6 +363,7 @@ def _invoke_process(
|
|
|
361
363
|
encoding=UTF8_ENCODING,
|
|
362
364
|
errors=UTF8_DECODE_ERRORS,
|
|
363
365
|
start_new_session=should_start_new_session(),
|
|
366
|
+
creationflags=hidden_window_creation_flags(),
|
|
364
367
|
)
|
|
365
368
|
except FileNotFoundError:
|
|
366
369
|
return _missing_binary_outcome()
|
|
@@ -27,6 +27,8 @@ from dev_env_scripts_constants.grok_run_ledger_constants import (
|
|
|
27
27
|
UTF8_ENCODING,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
31
|
+
|
|
30
32
|
|
|
31
33
|
def compute_sha256_hex(content: bytes) -> str:
|
|
32
34
|
"""Return the hex SHA-256 digest of raw bytes.
|
|
@@ -58,11 +60,13 @@ def extract_worktree_diff(
|
|
|
58
60
|
["git", "-C", str(worktree_path), "diff", base_sha],
|
|
59
61
|
text=True,
|
|
60
62
|
encoding=UTF8_ENCODING,
|
|
63
|
+
creationflags=hidden_window_creation_flags(),
|
|
61
64
|
)
|
|
62
65
|
changed_paths_listing = subprocess.check_output(
|
|
63
66
|
["git", "-C", str(worktree_path), "diff", "--name-only", base_sha],
|
|
64
67
|
text=True,
|
|
65
68
|
encoding=UTF8_ENCODING,
|
|
69
|
+
creationflags=hidden_window_creation_flags(),
|
|
66
70
|
)
|
|
67
71
|
changed_paths = tuple(
|
|
68
72
|
each_line.strip()
|
|
@@ -90,6 +90,8 @@ from dev_env_scripts_constants.grok_worker_constants import (
|
|
|
90
90
|
UTF8_ENCODING,
|
|
91
91
|
)
|
|
92
92
|
|
|
93
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
94
|
+
|
|
93
95
|
preflight_which = shutil.which
|
|
94
96
|
preflight_subprocess_runner = subprocess.run
|
|
95
97
|
preflight_time = time.time
|
|
@@ -259,6 +261,7 @@ def _run_grok_command(
|
|
|
259
261
|
errors=UTF8_DECODE_ERRORS,
|
|
260
262
|
timeout=timeout_seconds,
|
|
261
263
|
check=False,
|
|
264
|
+
creationflags=hidden_window_creation_flags(),
|
|
262
265
|
)
|
|
263
266
|
except OSError:
|
|
264
267
|
return None
|
|
@@ -104,6 +104,8 @@ from dev_env_scripts_constants.timing import ( # noqa: E402
|
|
|
104
104
|
DEFAULT_CODE_REVIEW_TIMEOUT_SECONDS,
|
|
105
105
|
)
|
|
106
106
|
|
|
107
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
108
|
+
|
|
107
109
|
|
|
108
110
|
@dataclass(frozen=True)
|
|
109
111
|
class CodeReviewOutcome:
|
|
@@ -265,6 +267,7 @@ def is_working_tree_dirty(working_directory: Path) -> bool:
|
|
|
265
267
|
capture_output=True,
|
|
266
268
|
text=True,
|
|
267
269
|
check=False,
|
|
270
|
+
creationflags=hidden_window_creation_flags(),
|
|
268
271
|
)
|
|
269
272
|
if completion.returncode != 0:
|
|
270
273
|
return True
|
|
@@ -11,6 +11,8 @@ from .config import (
|
|
|
11
11
|
)
|
|
12
12
|
from .model import CommandCapture
|
|
13
13
|
|
|
14
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
def run_command(
|
|
16
18
|
all_arguments: tuple[str, ...], check_directory: Path, timeout_seconds: float
|
|
@@ -73,6 +75,7 @@ def _run_subprocess(
|
|
|
73
75
|
encoding=UTF8_ENCODING,
|
|
74
76
|
errors="replace",
|
|
75
77
|
timeout=timeout_seconds,
|
|
78
|
+
creationflags=hidden_window_creation_flags(),
|
|
76
79
|
)
|
|
77
80
|
|
|
78
81
|
|
|
@@ -23,6 +23,8 @@ from .config import (
|
|
|
23
23
|
)
|
|
24
24
|
from .config.timing import GIT_METADATA_TIMEOUT_SECONDS
|
|
25
25
|
|
|
26
|
+
from subprocess_window_access import hidden_window_creation_flags
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
@dataclass(frozen=True)
|
|
28
30
|
class CandidateSnapshot:
|
|
@@ -82,6 +84,7 @@ def _run_git_bytes(
|
|
|
82
84
|
capture_output=True,
|
|
83
85
|
check=False,
|
|
84
86
|
timeout=GIT_METADATA_TIMEOUT_SECONDS,
|
|
87
|
+
creationflags=hidden_window_creation_flags(),
|
|
85
88
|
)
|
|
86
89
|
except (OSError, subprocess.SubprocessError):
|
|
87
90
|
return None
|
|
@@ -4,6 +4,7 @@ from collections.abc import Iterable
|
|
|
4
4
|
from contextlib import redirect_stderr, redirect_stdout
|
|
5
5
|
from io import StringIO
|
|
6
6
|
from pathlib import Path, PurePosixPath
|
|
7
|
+
from types import ModuleType
|
|
7
8
|
|
|
8
9
|
from . import adapter_support
|
|
9
10
|
from .config import constants
|
|
@@ -118,10 +119,49 @@ def validator_diagnostics(
|
|
|
118
119
|
return adapter_support._diagnostics_for_messages(document, "validators", all_messages)
|
|
119
120
|
|
|
120
121
|
|
|
122
|
+
def _is_untouched_function(
|
|
123
|
+
message: str, all_changed_lines: frozenset[int], scoping_module: ModuleType
|
|
124
|
+
) -> bool:
|
|
125
|
+
span_range = scoping_module.function_length_span_range(message)
|
|
126
|
+
return span_range is not None and all_changed_lines.isdisjoint(span_range)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _change_scoped_messages(
|
|
130
|
+
all_messages: Iterable[str],
|
|
131
|
+
document: Document,
|
|
132
|
+
load_pr_loop_module: adapter_support.HookModuleLoader,
|
|
133
|
+
) -> tuple[str, ...]:
|
|
134
|
+
"""Drop function-length messages for functions the change never touched.
|
|
135
|
+
|
|
136
|
+
::
|
|
137
|
+
|
|
138
|
+
an import added above an existing long ``main`` -> dropped
|
|
139
|
+
a statement added inside that ``main`` -> kept
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
all_messages: Every message the code-rule engine returned.
|
|
143
|
+
document: Current document and the lines this change touched.
|
|
144
|
+
load_pr_loop_module: Loader for the shared pull-request-loop scripts.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
The messages that remain in scope for this change.
|
|
148
|
+
"""
|
|
149
|
+
all_changed_lines = document.changed_lines
|
|
150
|
+
if all_changed_lines is None:
|
|
151
|
+
return tuple(all_messages)
|
|
152
|
+
scoping_module = load_pr_loop_module(constants.VIOLATION_SCOPING_MODULE_NAME)
|
|
153
|
+
return tuple(
|
|
154
|
+
each_message
|
|
155
|
+
for each_message in all_messages
|
|
156
|
+
if not _is_untouched_function(each_message, all_changed_lines, scoping_module)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
121
160
|
def code_rule_diagnostics(
|
|
122
161
|
document: Document,
|
|
123
162
|
repository_root: Path,
|
|
124
163
|
load_module: adapter_support.HookModuleLoader,
|
|
164
|
+
load_pr_loop_module: adapter_support.HookModuleLoader,
|
|
125
165
|
) -> tuple[Diagnostic, ...]:
|
|
126
166
|
"""Run the code-rule engine on one document.
|
|
127
167
|
|
|
@@ -129,9 +169,10 @@ def code_rule_diagnostics(
|
|
|
129
169
|
document: Current text and optional prior text.
|
|
130
170
|
repository_root: Request repository root for sibling-file resolution.
|
|
131
171
|
load_module: Hook module loader.
|
|
172
|
+
load_pr_loop_module: Loader for the shared pull-request-loop scripts.
|
|
132
173
|
|
|
133
174
|
Returns:
|
|
134
|
-
Diagnostics from the code-rule engine.
|
|
175
|
+
Diagnostics from the code-rule engine, scoped to the change.
|
|
135
176
|
"""
|
|
136
177
|
absolute_path = adapter_support._document_path(repository_root, document)
|
|
137
178
|
legacy_module = load_module("blocking.code_rules_enforcer")
|
|
@@ -146,7 +187,12 @@ def code_rule_diagnostics(
|
|
|
146
187
|
sibling_directory=absolute_path.parent,
|
|
147
188
|
include_comment_policy=True,
|
|
148
189
|
)
|
|
149
|
-
|
|
190
|
+
all_scoped_messages = _change_scoped_messages(
|
|
191
|
+
all_messages, document, load_pr_loop_module
|
|
192
|
+
)
|
|
193
|
+
return adapter_support._diagnostics_for_messages(
|
|
194
|
+
document, "code-rules", all_scoped_messages
|
|
195
|
+
)
|
|
150
196
|
|
|
151
197
|
|
|
152
198
|
def state_description_diagnostics(
|