claude-dev-env 1.93.1 → 1.95.0
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/_shared/advisor/CLAUDE.md +2 -2
- package/_shared/advisor/advisor-protocol.md +35 -27
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +3 -2
- package/_shared/advisor/scripts/model_tier_run_validator.py +23 -15
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +81 -17
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/audit-contract.md +1 -1
- package/_shared/pr-loop/gh-payloads.md +3 -3
- package/_shared/pr-loop/post-audit-thread-contract.md +51 -0
- package/_shared/pr-loop/scripts/README.md +2 -2
- package/_shared/pr-loop/scripts/fix_hookspath.py +47 -12
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -69
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -374
- package/_shared/pr-loop/state-schema.md +3 -22
- package/bin/CLAUDE.md +11 -2
- package/bin/ever-shipped-skills.mjs +70 -0
- package/bin/install.mjs +138 -13
- package/bin/install.prune.test.mjs +457 -0
- package/bin/install.test.mjs +10 -4
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/CLAUDE.md +2 -0
- package/hooks/blocking/code_rules_enforcer.py +4 -0
- package/hooks/blocking/code_rules_shared.py +82 -0
- package/hooks/blocking/code_rules_test_layout.py +9 -3
- package/hooks/blocking/pii_commit_command.py +398 -0
- package/hooks/blocking/pii_payload_scan.py +133 -0
- package/hooks/blocking/pii_prevention_blocker.py +151 -449
- package/hooks/blocking/plain_language_blocker.py +138 -4
- package/hooks/blocking/sensitive_file_protector.py +114 -48
- package/hooks/blocking/tdd_enforcer.py +9 -2
- package/hooks/blocking/test_code_rules_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/test_code_rules_shared.py +181 -0
- package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
- package/hooks/blocking/test_plain_language_blocker_allowlist.py +184 -0
- package/hooks/blocking/test_sensitive_file_protector.py +185 -0
- package/hooks/blocking/test_tdd_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
- package/hooks/hooks_constants/CLAUDE.md +4 -2
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
- package/hooks/hooks_constants/harness_scratchpad_constants.py +17 -0
- package/hooks/hooks_constants/local_identity.py +65 -15
- package/hooks/hooks_constants/pii_prevention_constants.py +16 -0
- package/hooks/hooks_constants/plain_language_blocker_constants.py +5 -0
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +42 -0
- package/hooks/hooks_constants/test_local_identity.py +68 -0
- package/hooks/pyproject.toml +75 -4
- package/hooks/validators/CLAUDE.md +1 -1
- package/hooks/validators/README.md +2 -0
- package/hooks/validators/python_style_checks.py +114 -136
- package/hooks/validators/python_style_helpers.py +95 -0
- package/hooks/validators/test_python_style_checks.py +0 -164
- package/hooks/validators/test_python_style_checks_decorator_gap.py +119 -0
- package/hooks/validators/test_python_style_fixes.py +251 -0
- package/hooks/validators/test_python_style_helpers.py +125 -0
- package/package.json +1 -1
- package/rules/CLAUDE.md +1 -0
- package/rules/anti-corollary-tests.md +69 -0
- package/rules/bdd.md +1 -3
- package/rules/code-reviews.md +1 -1
- package/rules/gh-paginate.md +1 -1
- package/rules/plain-language.md +2 -0
- package/scripts/CLAUDE.md +10 -1
- package/scripts/Get-SessionAccount.ps1 +210 -0
- package/scripts/tests/CLAUDE.md +15 -1
- package/scripts/tests/Get-SessionAccount.Tests.ps1 +350 -0
- package/skills/CLAUDE.md +6 -27
- package/skills/anthropic-plan/SKILL.md +1 -13
- package/skills/autoconverge/CLAUDE.md +5 -2
- package/skills/autoconverge/SKILL.md +112 -387
- package/skills/autoconverge/reference/CLAUDE.md +4 -1
- package/skills/autoconverge/reference/closing-report.md +9 -2
- package/skills/autoconverge/reference/convergence.md +26 -22
- package/skills/autoconverge/reference/headless-safety.md +44 -0
- package/skills/autoconverge/reference/multi-pr.md +88 -0
- package/skills/autoconverge/reference/self-closing-loop.md +84 -0
- package/skills/autoconverge/reference/stop-conditions.md +21 -13
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +87 -27
- package/skills/autoconverge/workflow/converge.contract.test.mjs +40 -10
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +179 -47
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +286 -96
- package/skills/bugteam/CLAUDE.md +1 -1
- package/skills/bugteam/CONSTRAINTS.md +3 -3
- package/skills/bugteam/PROMPTS.md +8 -19
- package/skills/bugteam/SKILL.md +13 -14
- package/skills/bugteam/reference/CLAUDE.md +1 -1
- package/skills/bugteam/reference/README.md +2 -3
- package/skills/bugteam/reference/audit-and-teammates.md +2 -2
- package/skills/bugteam/reference/copilot-gap-analysis.md +2 -0
- package/skills/bugteam/reference/github-pr-reviews.md +56 -38
- package/skills/bugteam/reference/team-setup.md +11 -11
- package/skills/bugteam/scripts/CLAUDE.md +6 -6
- package/skills/bugteam/scripts/README.md +20 -8
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +13 -1463
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +23 -358
- package/skills/bugteam/scripts/bugteam_preflight.py +13 -315
- package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +2 -3
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -1053
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +30 -347
- package/skills/bugteam/scripts/test_bugteam_preflight.py +30 -305
- package/skills/closeout/SKILL.md +153 -0
- package/skills/closeout/reference/handoff-prompt-template.md +72 -0
- package/skills/closeout/reference/issue-body-templates.md +108 -0
- package/skills/closeout/reference/pii-redaction-checklist.md +36 -0
- package/skills/copilot-finding-triage/SKILL.md +80 -28
- package/skills/copilot-finding-triage/reference/tier-rubric.md +70 -11
- package/skills/copilot-finding-triage/templates/notification.md +8 -3
- package/skills/everything-search/SKILL.md +1 -2
- package/skills/grokify/SKILL.md +58 -0
- package/skills/grokify/templates/handoff-template.md +92 -0
- package/skills/orchestrator/SKILL.md +35 -25
- package/skills/orchestrator-refresh/SKILL.md +15 -11
- package/skills/pr-converge/CLAUDE.md +1 -1
- package/skills/pr-converge/SKILL.md +132 -233
- package/skills/pr-converge/reference/convergence-gates.md +102 -66
- package/skills/pr-converge/reference/examples.md +37 -25
- package/skills/pr-converge/reference/fix-protocol.md +7 -7
- package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -4
- package/skills/pr-converge/reference/per-tick.md +64 -41
- package/skills/pr-converge/reference/state-schema.md +27 -2
- package/skills/pr-converge/scripts/CLAUDE.md +8 -0
- package/skills/pr-converge/scripts/README.md +4 -3
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +23 -0
- package/skills/pr-converge/scripts/check_convergence.py +189 -645
- package/skills/pr-converge/scripts/check_convergence_gates.py +235 -0
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +219 -0
- package/skills/pr-converge/scripts/conftest.py +8 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -0
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +26 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +73 -228
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +321 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +68 -0
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +75 -0
- package/skills/pr-fix-protocol/SKILL.md +20 -20
- package/skills/privacy-hygiene/SKILL.md +4 -3
- package/skills/reviewer-gates/SKILL.md +26 -8
- package/skills/session-log/SKILL.md +11 -12
- package/skills/session-tidy/SKILL.md +40 -30
- package/skills/skill-builder/CLAUDE.md +13 -10
- package/skills/skill-builder/SKILL.md +31 -31
- package/skills/skill-builder/references/CLAUDE.md +10 -6
- package/skills/skill-builder/references/delegation-map.md +13 -4
- package/skills/skill-builder/references/description-field.md +113 -0
- package/skills/skill-builder/references/self-audit-checklist.md +21 -4
- package/skills/skill-builder/references/skill-modularity.md +113 -0
- package/skills/skill-builder/references/skill-types.md +4 -0
- package/skills/skill-builder/templates/CLAUDE.md +4 -4
- package/skills/skill-builder/templates/gap-analysis.md +25 -1
- package/skills/skill-builder/workflows/CLAUDE.md +6 -4
- package/skills/skill-builder/workflows/improve-skill.md +17 -16
- package/skills/skill-builder/workflows/new-skill.md +34 -10
- package/skills/skill-builder/workflows/polish-skill.md +59 -43
- package/skills/team-advisor/SKILL.md +15 -11
- package/skills/usage-pause/SKILL.md +10 -14
- package/system-prompts/software-engineer.xml +7 -6
- package/hooks/validators/test_verify_paths.py +0 -32
- package/hooks/validators/verify_paths.py +0 -57
- package/skills/bdd-protocol/CLAUDE.md +0 -26
- package/skills/bdd-protocol/SKILL.md +0 -30
- package/skills/bdd-protocol/references/CLAUDE.md +0 -10
- package/skills/bdd-protocol/references/anti-patterns.md +0 -26
- package/skills/bdd-protocol/references/example-mapping.md +0 -23
- package/skills/bg-agent/CLAUDE.md +0 -17
- package/skills/bg-agent/SKILL.md +0 -69
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +0 -55
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_fix_hookspath_constants.py +0 -26
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_preflight_constants.py +0 -35
- package/skills/caveman/CLAUDE.md +0 -15
- package/skills/caveman/SKILL.md +0 -38
- package/skills/code/CLAUDE.md +0 -17
- package/skills/code/SKILL.md +0 -178
- package/skills/deep-research/CLAUDE.md +0 -17
- package/skills/deep-research/SKILL.md +0 -80
- package/skills/findbugs/CLAUDE.md +0 -20
- package/skills/findbugs/SKILL.md +0 -241
- package/skills/fixbugs/CLAUDE.md +0 -19
- package/skills/fixbugs/SKILL.md +0 -142
- package/skills/gh-paginate/CLAUDE.md +0 -18
- package/skills/gh-paginate/SKILL.md +0 -84
- package/skills/gotcha/CLAUDE.md +0 -33
- package/skills/gotcha/SKILL.md +0 -73
- package/skills/implement/CLAUDE.md +0 -27
- package/skills/implement/SKILL.md +0 -66
- package/skills/implement/scripts/CLAUDE.md +0 -22
- package/skills/implement/scripts/append_note.py +0 -133
- package/skills/implement/scripts/implement_scripts_constants/CLAUDE.md +0 -22
- package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
- package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +0 -12
- package/skills/implement/scripts/test_append_note.py +0 -191
- package/skills/log-audit/CLAUDE.md +0 -20
- package/skills/log-audit/SKILL.md +0 -66
- package/skills/log-audit/reference/CLAUDE.md +0 -9
- package/skills/log-audit/reference/charter.md +0 -52
- package/skills/log-audit/scripts/CLAUDE.md +0 -27
- package/skills/log-audit/scripts/cluster_recurrences.py +0 -261
- package/skills/log-audit/scripts/collect_log_window.py +0 -199
- package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +0 -12
- package/skills/log-audit/scripts/log_audit_constants/__init__.py +0 -0
- package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +0 -23
- package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +0 -24
- package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +0 -49
- package/skills/log-audit/scripts/mine_copilot_findings.py +0 -302
- package/skills/log-audit/scripts/test_cluster_recurrences.py +0 -160
- package/skills/log-audit/scripts/test_collect_log_window.py +0 -111
- package/skills/log-audit/scripts/test_mine_copilot_findings.py +0 -126
- package/skills/logifix/CLAUDE.md +0 -36
- package/skills/logifix/SKILL.md +0 -69
- package/skills/logifix/scripts/CLAUDE.md +0 -16
- package/skills/logifix/scripts/logifix.ps1 +0 -205
- package/skills/monitor-open-prs/CLAUDE.md +0 -34
- package/skills/monitor-open-prs/SKILL.md +0 -88
- package/skills/monitor-open-prs/scripts/CLAUDE.md +0 -17
- package/skills/monitor-open-prs/scripts/discover_open_prs.py +0 -69
- package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +0 -149
- package/skills/monitor-open-prs/test_skill_contract.py +0 -27
- package/skills/post-audit-findings/SKILL.md +0 -80
- package/skills/pr-consistency-audit/CLAUDE.md +0 -34
- package/skills/pr-consistency-audit/SKILL.md +0 -116
- package/skills/pr-consistency-audit/reference/CLAUDE.md +0 -16
- package/skills/pr-consistency-audit/reference/detection-rules.md +0 -96
- package/skills/pr-consistency-audit/reference/illustrations.md +0 -78
- package/skills/pr-review-responder/CLAUDE.md +0 -35
- package/skills/pr-review-responder/EXAMPLES.md +0 -590
- package/skills/pr-review-responder/PRINCIPLES.md +0 -533
- package/skills/pr-review-responder/README.md +0 -168
- package/skills/pr-review-responder/SKILL.md +0 -203
- package/skills/pr-review-responder/TESTING.md +0 -350
- package/skills/pr-scope-resolve/SKILL.md +0 -45
- package/skills/pre-compact/CLAUDE.md +0 -24
- package/skills/pre-compact/SKILL.md +0 -134
- package/skills/qbug/CLAUDE.md +0 -40
- package/skills/qbug/SKILL.md +0 -387
- package/skills/qbug/test_qbug_skill_audit_schema.py +0 -152
- package/skills/qbug/test_qbug_skill_post_fix_audit.py +0 -103
- package/skills/refine/CLAUDE.md +0 -44
- package/skills/refine/SKILL.md +0 -262
- package/skills/refine/templates/CLAUDE.md +0 -17
- package/skills/refine/templates/implementation-notes-template.html +0 -56
- package/skills/refine/templates/plan-template.md +0 -60
- package/skills/research-mode/CLAUDE.md +0 -35
- package/skills/research-mode/SKILL.md +0 -53
- package/skills/structure-prompt/CLAUDE.md +0 -42
- package/skills/structure-prompt/SKILL.md +0 -44
- package/skills/structure-prompt/reference/CLAUDE.md +0 -28
- package/skills/structure-prompt/reference/adversarial-tuning.md +0 -62
- package/skills/structure-prompt/reference/block-classification.md +0 -27
- package/skills/structure-prompt/reference/canonical-case.md +0 -48
- package/skills/structure-prompt/reference/citation-depth.md +0 -70
- package/skills/structure-prompt/reference/cleanup.md +0 -33
- package/skills/structure-prompt/reference/constraints.md +0 -33
- package/skills/structure-prompt/reference/directives.md +0 -37
- package/skills/structure-prompt/reference/examples.md +0 -72
- package/skills/structure-prompt/reference/instantiation.md +0 -51
- package/skills/structure-prompt/reference/output-contract.md +0 -72
- package/skills/structure-prompt/reference/per-category.md +0 -23
- package/skills/structure-prompt/reference/persona.md +0 -38
- package/skills/structure-prompt/reference/research.md +0 -33
- package/skills/structure-prompt/reference/structure.md +0 -28
- package/skills/verified-build/CLAUDE.md +0 -33
- package/skills/verified-build/SKILL.md +0 -33
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
<#
|
|
2
|
-
.SYNOPSIS
|
|
3
|
-
Restores the Logitech Gaming Software (LCore) tray icon when it is missing on Windows.
|
|
4
|
-
|
|
5
|
-
.DESCRIPTION
|
|
6
|
-
Reproduces the verified recovery procedure from
|
|
7
|
-
sessions/System Support/2. Logitech Tray Icon Fix Recurrence.md (2026-04-25):
|
|
8
|
-
1. Stops LCore in user context.
|
|
9
|
-
2. Single elevated UAC step:
|
|
10
|
-
- Starts LogiRegistryService (handles the case where it is Stopped).
|
|
11
|
-
- Stops explorer.exe and lets Windows shell auto-respawn rebuild it.
|
|
12
|
-
Deliberately omits Start-Process explorer from the elevated block to
|
|
13
|
-
avoid the duplicate admin-context explorer that blocked tray registration
|
|
14
|
-
during Session 2.
|
|
15
|
-
3. Waits for shell auto-respawn.
|
|
16
|
-
4. Verifies a single user-session explorer.
|
|
17
|
-
5. Performs LCoreRelaunchAttemptCount full stop-then-launch cycles of
|
|
18
|
-
LCore /minimized. Always runs the full count: a responsive LCore on the
|
|
19
|
-
first attempt does NOT imply Shell_NotifyIcon registered, per Session 2
|
|
20
|
-
gotcha #2 (responsive process, no tray icon).
|
|
21
|
-
|
|
22
|
-
.PARAMETER ExplorerAutoRespawnWaitSeconds
|
|
23
|
-
Seconds to wait after the elevated explorer kill before verifying respawn.
|
|
24
|
-
|
|
25
|
-
.PARAMETER LCoreInitializationWaitSeconds
|
|
26
|
-
Seconds to wait after each LCore relaunch before checking responsiveness.
|
|
27
|
-
|
|
28
|
-
.PARAMETER LCoreRelaunchAttemptCount
|
|
29
|
-
Total number of LCore relaunch cycles to perform (default 2). The full count
|
|
30
|
-
always runs, because the documented failure mode is a responsive LCore that
|
|
31
|
-
never registered a tray icon -- only a second stop+launch reliably triggers
|
|
32
|
-
Shell_NotifyIcon registration.
|
|
33
|
-
#>
|
|
34
|
-
|
|
35
|
-
[CmdletBinding()]
|
|
36
|
-
param(
|
|
37
|
-
[int] $ExplorerAutoRespawnWaitSeconds = 5,
|
|
38
|
-
[int] $LCoreInitializationWaitSeconds = 5,
|
|
39
|
-
[int] $LCoreRelaunchAttemptCount = 2
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
Set-StrictMode -Version Latest
|
|
43
|
-
$ErrorActionPreference = 'Stop'
|
|
44
|
-
|
|
45
|
-
$script:LCoreExecutablePath = 'C:\Program Files\Logitech Gaming Software\LCore.exe'
|
|
46
|
-
$script:LCoreProcessName = 'LCore'
|
|
47
|
-
|
|
48
|
-
function Get-CurrentInteractiveSessionId {
|
|
49
|
-
[OutputType([int])]
|
|
50
|
-
param()
|
|
51
|
-
return (Get-CimInstance Win32_Process -Filter "ProcessId=$PID").SessionId
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function Get-ExplorerProcessesInSession {
|
|
55
|
-
[OutputType([object[]])]
|
|
56
|
-
param([Parameter(Mandatory)] [int] $TargetSessionId)
|
|
57
|
-
$allExplorers = Get-CimInstance Win32_Process -Filter "Name='explorer.exe'"
|
|
58
|
-
$matchingExplorers = @()
|
|
59
|
-
foreach ($eachExplorer in $allExplorers) {
|
|
60
|
-
if ($eachExplorer.SessionId -ne $TargetSessionId) { continue }
|
|
61
|
-
$ownerInfo = Invoke-CimMethod -InputObject $eachExplorer -MethodName GetOwner -ErrorAction SilentlyContinue
|
|
62
|
-
$matchingExplorers += [PSCustomObject]@{
|
|
63
|
-
ProcessId = $eachExplorer.ProcessId
|
|
64
|
-
SessionId = $eachExplorer.SessionId
|
|
65
|
-
OwnerUser = if ($ownerInfo) { $ownerInfo.User } else { $null }
|
|
66
|
-
CreationDate = $eachExplorer.CreationDate
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return ,$matchingExplorers
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function Stop-LCoreIfRunning {
|
|
73
|
-
[OutputType([void])]
|
|
74
|
-
param()
|
|
75
|
-
$stopSettleMilliseconds = 500
|
|
76
|
-
Stop-Process -Name $script:LCoreProcessName -Force -ErrorAction SilentlyContinue
|
|
77
|
-
Start-Sleep -Milliseconds $stopSettleMilliseconds
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function Invoke-ElevatedExplorerRebuild {
|
|
81
|
-
[OutputType([bool])]
|
|
82
|
-
param()
|
|
83
|
-
$elevatedScriptBlockText = @'
|
|
84
|
-
Start-Service -Name LogiRegistryService -ErrorAction SilentlyContinue
|
|
85
|
-
Stop-Process -Name explorer -Force -ErrorAction SilentlyContinue
|
|
86
|
-
'@
|
|
87
|
-
$encodedElevatedCommand = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($elevatedScriptBlockText))
|
|
88
|
-
try {
|
|
89
|
-
Start-Process -FilePath 'pwsh' `
|
|
90
|
-
-ArgumentList '-NoProfile', '-EncodedCommand', $encodedElevatedCommand `
|
|
91
|
-
-Verb RunAs `
|
|
92
|
-
-Wait `
|
|
93
|
-
-ErrorAction Stop
|
|
94
|
-
return $true
|
|
95
|
-
} catch {
|
|
96
|
-
Write-Warning "Elevated step did not run: $($_.Exception.Message)"
|
|
97
|
-
return $false
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function Start-LCoreMinimized {
|
|
102
|
-
[OutputType([void])]
|
|
103
|
-
param()
|
|
104
|
-
$launchArguments = '/minimized'
|
|
105
|
-
Start-Process -FilePath $script:LCoreExecutablePath -ArgumentList $launchArguments
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function Test-LCoreIsResponding {
|
|
109
|
-
[OutputType([bool])]
|
|
110
|
-
param([Parameter(Mandatory)] [int] $WaitSeconds)
|
|
111
|
-
Start-Sleep -Seconds $WaitSeconds
|
|
112
|
-
$lcoreProcesses = Get-Process -Name $script:LCoreProcessName -ErrorAction SilentlyContinue
|
|
113
|
-
if (-not $lcoreProcesses) { return $false }
|
|
114
|
-
foreach ($eachLCore in @($lcoreProcesses)) {
|
|
115
|
-
if (-not $eachLCore.Responding) { return $false }
|
|
116
|
-
}
|
|
117
|
-
return $true
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function Write-StateSnapshot {
|
|
121
|
-
[OutputType([void])]
|
|
122
|
-
param([Parameter(Mandatory)] [string] $Label)
|
|
123
|
-
$logitechServiceNamesToVerify = @('LogiRegistryService', 'logi_lamparray_service')
|
|
124
|
-
Write-Host ""
|
|
125
|
-
Write-Host "=== $Label ==="
|
|
126
|
-
$sessionId = Get-CurrentInteractiveSessionId
|
|
127
|
-
$explorersInSession = Get-ExplorerProcessesInSession -TargetSessionId $sessionId
|
|
128
|
-
Write-Host "Explorer instances in session ${sessionId}: $($explorersInSession.Count)"
|
|
129
|
-
if ($explorersInSession.Count -gt 0) {
|
|
130
|
-
$explorersInSession | Format-Table ProcessId, OwnerUser, CreationDate -AutoSize | Out-String | Write-Host
|
|
131
|
-
}
|
|
132
|
-
$servicesObserved = Get-Service -Name $logitechServiceNamesToVerify -ErrorAction SilentlyContinue
|
|
133
|
-
if ($servicesObserved) {
|
|
134
|
-
$servicesObserved | Format-Table Name, Status -AutoSize | Out-String | Write-Host
|
|
135
|
-
}
|
|
136
|
-
$lcoreProcessesObserved = Get-Process -Name $script:LCoreProcessName -ErrorAction SilentlyContinue
|
|
137
|
-
if ($lcoreProcessesObserved) {
|
|
138
|
-
$lcoreProcessesObserved | Select-Object Id, Responding | Format-Table -AutoSize | Out-String | Write-Host
|
|
139
|
-
} else {
|
|
140
|
-
Write-Host "LCore not running."
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function Invoke-LogifixRecovery {
|
|
145
|
-
[OutputType([void])]
|
|
146
|
-
param()
|
|
147
|
-
|
|
148
|
-
Write-StateSnapshot -Label 'Before'
|
|
149
|
-
|
|
150
|
-
if (-not (Test-Path -Path $script:LCoreExecutablePath)) {
|
|
151
|
-
Write-Error "LCore.exe not found at expected path: $script:LCoreExecutablePath"
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
Stop-LCoreIfRunning
|
|
156
|
-
|
|
157
|
-
$elevatedSucceeded = Invoke-ElevatedExplorerRebuild
|
|
158
|
-
if (-not $elevatedSucceeded) {
|
|
159
|
-
Write-Warning "UAC was canceled or the elevated step failed."
|
|
160
|
-
Write-Warning "Fallback: open Task Manager (Ctrl+Shift+Esc), find 'Windows Explorer', right-click, Restart. Then re-run /logifix."
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
Start-Sleep -Seconds $ExplorerAutoRespawnWaitSeconds
|
|
165
|
-
|
|
166
|
-
$sessionId = Get-CurrentInteractiveSessionId
|
|
167
|
-
$explorersAfterRespawn = Get-ExplorerProcessesInSession -TargetSessionId $sessionId
|
|
168
|
-
$explorerCountAfterRespawn = @($explorersAfterRespawn).Count
|
|
169
|
-
if ($explorerCountAfterRespawn -ne 1) {
|
|
170
|
-
if ($explorerCountAfterRespawn -eq 0) {
|
|
171
|
-
Write-Warning "No explorer.exe found in session $sessionId after $ExplorerAutoRespawnWaitSeconds-second wait."
|
|
172
|
-
} else {
|
|
173
|
-
Write-Warning "Expected exactly 1 explorer.exe in session $sessionId after shell auto-respawn, but found $explorerCountAfterRespawn."
|
|
174
|
-
Write-Warning "Multiple explorer.exe processes in the same session reproduce the Session 2 duplicate-explorer failure mode that blocks tray icon registration."
|
|
175
|
-
$explorersAfterRespawn | Format-Table ProcessId, OwnerUser, CreationDate -AutoSize | Out-String | Write-Host
|
|
176
|
-
}
|
|
177
|
-
Write-Warning "Restart Explorer manually via Task Manager (Ctrl+Shift+Esc, Windows Explorer, Restart), then re-run /logifix."
|
|
178
|
-
return
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
$lastAttemptResponded = $false
|
|
182
|
-
for ($attemptIndex = 1; $attemptIndex -le $LCoreRelaunchAttemptCount; $attemptIndex++) {
|
|
183
|
-
Stop-LCoreIfRunning
|
|
184
|
-
Start-LCoreMinimized
|
|
185
|
-
$lastAttemptResponded = Test-LCoreIsResponding -WaitSeconds $LCoreInitializationWaitSeconds
|
|
186
|
-
if ($lastAttemptResponded) {
|
|
187
|
-
Write-Host "LCore relaunch attempt ${attemptIndex}: process is responding."
|
|
188
|
-
} else {
|
|
189
|
-
Write-Warning "LCore relaunch attempt ${attemptIndex}: process not responding."
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
Write-StateSnapshot -Label 'After'
|
|
194
|
-
|
|
195
|
-
if (-not $lastAttemptResponded) {
|
|
196
|
-
Write-Warning "LCore did not respond after $LCoreRelaunchAttemptCount attempts."
|
|
197
|
-
Write-Warning "Use Task Manager to Restart Windows Explorer, then re-run /logifix."
|
|
198
|
-
return
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
Write-Host ""
|
|
202
|
-
Write-Host "Recovery complete ($LCoreRelaunchAttemptCount stop+launch cycles performed). If the tray icon is still not visible, use Task Manager to Restart Windows Explorer (guaranteed correct session/elevation), then re-run /logifix."
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
Invoke-LogifixRecovery
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# monitor-open-prs skill
|
|
2
|
-
|
|
3
|
-
Discovers every open pull request across the `jl-cmd/*` and `JonEcho/*` owner scopes, dispatches `/bugteam` on each with the `--bugbot-retrigger` flag, and polls for new Cursor bugbot replies after each bugteam run.
|
|
4
|
-
|
|
5
|
-
**Trigger:** `/monitor-open-prs`, "sweep the open PRs", "audit the open PR backlog".
|
|
6
|
-
|
|
7
|
-
## Key files
|
|
8
|
-
|
|
9
|
-
| File | Purpose |
|
|
10
|
-
|---|---|
|
|
11
|
-
| `SKILL.md` | Full workflow: refusal cases, discovery, dispatch, post-convergence polling, final report |
|
|
12
|
-
| `test_skill_contract.py` | Contract tests for the skill |
|
|
13
|
-
| `packages/claude-dev-env/skills/monitor-open-prs/scripts/discover_open_prs.py` | Shells out to `gh search prs` per owner scope and flattens results to a uniform dict shape |
|
|
14
|
-
| `packages/claude-dev-env/skills/monitor-open-prs/scripts/test_discover_open_prs.py` | Tests for the discovery helper |
|
|
15
|
-
|
|
16
|
-
## Subdirectories
|
|
17
|
-
|
|
18
|
-
| Directory | Role |
|
|
19
|
-
|---|---|
|
|
20
|
-
| `scripts/` | Discovery helper and its tests |
|
|
21
|
-
|
|
22
|
-
## Workflow summary
|
|
23
|
-
|
|
24
|
-
1. Call `discover_open_prs.discover_open_prs(all_owners=["jl-cmd", "JonEcho"])` to get the full open-PR list.
|
|
25
|
-
2. For each PR, invoke `/bugteam <pr_number>`; include `--bugbot-retrigger` only when bugbot is enabled (`reviews_disabled.py --reviewer bugbot` exits 1).
|
|
26
|
-
3. After each bugteam run, poll for new bugbot comments on a 60s → 120s → 240s → 480s → 960s backoff. Re-invoke bugteam if new findings appear.
|
|
27
|
-
4. Emit a sweep summary when all PRs exit polling.
|
|
28
|
-
|
|
29
|
-
## Refusals (checked first)
|
|
30
|
-
|
|
31
|
-
- `CLAUDE_REVIEWS_DISABLED` has `bugteam` → stop with a message.
|
|
32
|
-
- GitHub API not accessible → stop.
|
|
33
|
-
- Uncommitted changes in the caller's repo → stop.
|
|
34
|
-
- Required subagent types missing (`code-quality-agent`, `clean-coder`) → stop.
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: monitor-open-prs
|
|
3
|
-
description: >-
|
|
4
|
-
Discover every open pull request across the jl-cmd/* and JonEcho/*
|
|
5
|
-
owner scopes, spawn /bugteam on each in parallel with the bugbot
|
|
6
|
-
re-trigger flag (--bugbot-retrigger), and poll Cursor's bugbot replies
|
|
7
|
-
after convergence so any post-bugteam findings loop back through
|
|
8
|
-
/bugteam. Triggers: '/monitor-open-prs', 'sweep the open PRs',
|
|
9
|
-
'audit the open PR backlog'.
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Monitor Open PRs
|
|
13
|
-
|
|
14
|
-
**Core principle:** One sweep covers every open PR across both owner scopes. Claude discovers PRs live via `scripts/discover_open_prs.py` which shells out to `gh search prs --owner <owner> --state open --json ...`, dispatches `/bugteam --bugbot-retrigger` per PR, then polls Cursor's bugbot replies until each PR is quiet for a full backoff cycle.
|
|
15
|
-
|
|
16
|
-
## Contents
|
|
17
|
-
|
|
18
|
-
- Transport check — gh presence and auth route the sweep local or through the cloud transport
|
|
19
|
-
- When this skill applies — refusal cases and trigger conditions
|
|
20
|
-
- Discovery — `scripts/discover_open_prs.py` queries via `gh search prs` across both owner scopes
|
|
21
|
-
- Dispatch — `/bugteam --bugbot-retrigger <pr_numbers...>`
|
|
22
|
-
- Post-convergence polling — bugbot replies and re-invocation
|
|
23
|
-
- `scripts/discover_open_prs.py` — the discovery helper
|
|
24
|
-
|
|
25
|
-
## Transport check (before any GitHub step)
|
|
26
|
-
|
|
27
|
-
Run `command -v gh`; when it succeeds, run `gh auth status`; for each PR the sweep dispatches, `gh api repos/<owner>/<repo> --jq .permissions.push` must print `true`. When any check fails, run the `pr-loop-cloud-transport` skill first and route every `gh` operation in this skill through its substitution matrix, including the discovery query (`mcp__github__search_pull_requests` with `perPage` in place of `gh search prs`).
|
|
28
|
-
|
|
29
|
-
## When this skill applies
|
|
30
|
-
|
|
31
|
-
`/monitor-open-prs` authorizes one full sweep over all open PRs in both owner scopes.
|
|
32
|
-
|
|
33
|
-
Refusals — first match wins; respond with the quoted line exactly and stop:
|
|
34
|
-
|
|
35
|
-
- **Disabled via environment.** When `CLAUDE_REVIEWS_DISABLED` (comma-separated, case-insensitive, whitespace-tolerant) has the token `bugteam`: `/monitor-open-prs is a /bugteam dispatcher and /bugteam is disabled via CLAUDE_REVIEWS_DISABLED.` Gate semantics live in the `reviewer-gates` skill ([../reviewer-gates/SKILL.md](../reviewer-gates/SKILL.md)).
|
|
36
|
-
- **GitHub API not accessible.** `get_me failed. /monitor-open-prs needs active GitHub MCP credentials.`
|
|
37
|
-
- **Dirty tree on the caller's repo.** `Uncommitted changes detected. Stash, commit, or revert before /monitor-open-prs.`
|
|
38
|
-
- **Required subagents missing.** Confirm `code-quality-agent` and `clean-coder` exist. Else: `Required subagent type <name> not installed.`
|
|
39
|
-
|
|
40
|
-
## Discovery
|
|
41
|
-
|
|
42
|
-
Call `scripts/discover_open_prs.discover_open_prs(all_owners=["jl-cmd", "JonEcho"])` to merge the live open-PR list across both scopes. The helper shells out to `gh search prs --owner <owner> --state open --json number,repository,url,headRefName,baseRefName` for each owner scope and flattens the result to a uniform dict shape with keys `number`, `owner`, `repo`, `head_ref`, `base_ref`, `url`. Empty scopes contribute empty lists; an entirely empty sweep returns `[]` and exits cleanly.
|
|
43
|
-
|
|
44
|
-
## Dispatch
|
|
45
|
-
|
|
46
|
-
For each discovered PR:
|
|
47
|
-
|
|
48
|
-
1. Resolve the PR's repo checkout (existing worktree or fresh `git clone`).
|
|
49
|
-
2. From that checkout, invoke `/bugteam <pr_number>`. Include `--bugbot-retrigger` only when bugbot is enabled for the run — when `python "$HOME/.claude/_shared/pr-loop/scripts/reviews_disabled.py" --reviewer bugbot` exits 1. On exit 0 (bugbot disabled — the default) omit `--bugbot-retrigger` so the bugbot leg sits out the run.
|
|
50
|
-
3. The `--bugbot-retrigger` flag tells bugteam to post `bugbot run` as an issue comment after every successful FIX push so Cursor's bugbot re-evaluates the new commit.
|
|
51
|
-
4. Bugteam runs its own 20-loop audit/fix cycle per PR; this skill waits for each bugteam invocation to return before dispatching the next (or fanning out — see below).
|
|
52
|
-
|
|
53
|
-
**Fan-out (optional):** when the discovered list has more than one PR, the skill may spawn `/bugteam` dispatches in parallel by issuing multiple `Agent` calls in a single assistant message. Each dispatch operates in its own per-PR worktree (bugteam Step 1.1). Serialize when the caller sets an explicit `--serial` flag.
|
|
54
|
-
|
|
55
|
-
## Post-Convergence Polling
|
|
56
|
-
|
|
57
|
-
After a `/bugteam` invocation returns (converged, cap reached, stuck, or error), the PR may accumulate new Cursor bugbot comments within minutes. Poll for them:
|
|
58
|
-
|
|
59
|
-
1. Baseline: capture `since_timestamp` as the PR's last commit timestamp.
|
|
60
|
-
2. Every 60 seconds, call `pull_request_read(method="get", pullNumber=<pr_number>, owner=<owner>, repo=<repo>)` and filter the response's `comments` array for entries whose `.author.login` matches `"bugbot"` or `"cursor"` and `.createdAt` is after `<since_timestamp>`.
|
|
61
|
-
3. Back off: 60s → 120s → 240s → 480s → 960s. If five successive polls return empty, exit polling for this PR.
|
|
62
|
-
4. If bugbot posts a new finding in any poll, re-invoke `/bugteam <pr_number>` with the bugbot finding text seeded into the invocation's `bugs_to_fix` preamble. Reset the backoff.
|
|
63
|
-
|
|
64
|
-
### Polling Cost and Cadence
|
|
65
|
-
|
|
66
|
-
The five-step backoff sums to `60 + 120 + 240 + 480 + 960 = 1860` seconds (~31 minutes) of idle polling per PR before the skill declares bugbot quiet. A sweep over ten open PRs therefore retains up to ~5 wall-clock hours of bugbot-watch time beyond the active `/bugteam` work. Callers who need faster turnaround should pass `--serial` to disable fan-out (so polling starts only after the previous PR finishes) or accept the tradeoff: the backoff exists specifically to catch late bugbot analyses that can take several minutes to appear after a push.
|
|
67
|
-
|
|
68
|
-
## Final Report
|
|
69
|
-
|
|
70
|
-
After every PR has exited polling, emit:
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
/monitor-open-prs sweep summary
|
|
74
|
-
PRs discovered: <N>
|
|
75
|
-
jl-cmd/*: <count>
|
|
76
|
-
JonEcho/*: <count>
|
|
77
|
-
PRs converged clean: <count>
|
|
78
|
-
PRs hit 20-loop cap: <count>
|
|
79
|
-
PRs stuck: <count>
|
|
80
|
-
PRs errored: <count>
|
|
81
|
-
Bugbot re-triggers fired: <count>
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## Non-Negotiable Guardrails
|
|
85
|
-
|
|
86
|
-
- Never pass `--no-verify` or `--no-gpg-sign` to git in any dispatched bugteam run.
|
|
87
|
-
- Never open a PR from this skill; only comment on existing ones.
|
|
88
|
-
- Never merge or close PRs; the skill is read + audit + patch only.
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# monitor-open-prs/scripts
|
|
2
|
-
|
|
3
|
-
Discovery helper for the `monitor-open-prs` skill.
|
|
4
|
-
|
|
5
|
-
## Key files
|
|
6
|
-
|
|
7
|
-
| File | Purpose |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `discover_open_prs.py` | Queries `gh search prs` for each owner scope and flattens results to a uniform list of dicts |
|
|
10
|
-
| `test_discover_open_prs.py` | Tests for the discovery helper |
|
|
11
|
-
|
|
12
|
-
## Conventions
|
|
13
|
-
|
|
14
|
-
- `discover_open_prs(all_owners: list[str]) -> list[dict]` is the single entry point. It shells out to `gh search prs --owner <owner> --state open --json number,repository,url,headRefName,baseRefName` once per owner and merges the results.
|
|
15
|
-
- Each returned dict has keys: `number`, `owner`, `repo`, `head_ref`, `base_ref`, `url`.
|
|
16
|
-
- An empty scope or an empty sweep returns an empty list and exits cleanly with no errors.
|
|
17
|
-
- The module is stateless and has no filesystem side effects.
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"""Discover every open pull request across a list of owner scopes.
|
|
2
|
-
|
|
3
|
-
Shells out to ``gh search prs --owner <owner> --state open --json ...`` once per
|
|
4
|
-
owner, parses the JSON output, and flattens each entry to a uniform dict shape.
|
|
5
|
-
The module is stateless, takes no filesystem side effects, and exposes a single
|
|
6
|
-
``discover_open_prs`` entry point consumed by the monitor-open-prs skill.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
import json
|
|
12
|
-
import subprocess
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def build_gh_search_argv(owner: str) -> list[str]:
|
|
16
|
-
gh_command_name = "gh"
|
|
17
|
-
search_subcommand = ("search", "prs")
|
|
18
|
-
owner_flag = "--owner"
|
|
19
|
-
state_flag = "--state"
|
|
20
|
-
state_open_value = "open"
|
|
21
|
-
json_fields_flag = "--json"
|
|
22
|
-
json_fields_value = "number,repository,url,headRefName,baseRefName"
|
|
23
|
-
return [
|
|
24
|
-
gh_command_name,
|
|
25
|
-
*search_subcommand,
|
|
26
|
-
owner_flag,
|
|
27
|
-
owner,
|
|
28
|
-
state_flag,
|
|
29
|
-
state_open_value,
|
|
30
|
-
json_fields_flag,
|
|
31
|
-
json_fields_value,
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def split_repository_name(name_with_owner: str) -> tuple[str, str]:
|
|
36
|
-
repo_separator = "/"
|
|
37
|
-
if repo_separator not in name_with_owner:
|
|
38
|
-
return "", name_with_owner
|
|
39
|
-
owner_segment, repository_segment = name_with_owner.split(repo_separator, 1)
|
|
40
|
-
return owner_segment, repository_segment
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def flatten_pr_entry(raw_pr_entry: dict) -> dict:
|
|
44
|
-
name_with_owner = raw_pr_entry.get("repository", {}).get("nameWithOwner", "")
|
|
45
|
-
owner_segment, repository_segment = split_repository_name(name_with_owner)
|
|
46
|
-
return {
|
|
47
|
-
"number": raw_pr_entry.get("number"),
|
|
48
|
-
"owner": owner_segment,
|
|
49
|
-
"repo": repository_segment,
|
|
50
|
-
"head_ref": raw_pr_entry.get("headRefName", ""),
|
|
51
|
-
"base_ref": raw_pr_entry.get("baseRefName", ""),
|
|
52
|
-
"url": raw_pr_entry.get("url", ""),
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def fetch_open_prs_for_owner(owner: str) -> list[dict]:
|
|
57
|
-
search_argv = build_gh_search_argv(owner)
|
|
58
|
-
completed_process = subprocess.run(
|
|
59
|
-
search_argv, check=True, capture_output=True, text=True
|
|
60
|
-
)
|
|
61
|
-
raw_pr_entries = json.loads(completed_process.stdout)
|
|
62
|
-
return [flatten_pr_entry(each_entry) for each_entry in raw_pr_entries]
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def discover_open_prs(all_owners: list[str]) -> list[dict]:
|
|
66
|
-
all_discovered: list[dict] = []
|
|
67
|
-
for each_owner in all_owners:
|
|
68
|
-
all_discovered.extend(fetch_open_prs_for_owner(each_owner))
|
|
69
|
-
return all_discovered
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
"""Tests for discover_open_prs.py — merges gh-search output across owners."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import importlib.util
|
|
6
|
-
import json
|
|
7
|
-
import pathlib
|
|
8
|
-
import sys
|
|
9
|
-
|
|
10
|
-
import pytest
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _load_discovery_module():
|
|
14
|
-
scripts_directory = pathlib.Path(__file__).parent
|
|
15
|
-
sys.path.insert(0, str(scripts_directory))
|
|
16
|
-
module_path = scripts_directory / "discover_open_prs.py"
|
|
17
|
-
module_spec = importlib.util.spec_from_file_location(
|
|
18
|
-
"discover_open_prs", module_path
|
|
19
|
-
)
|
|
20
|
-
loaded_module = importlib.util.module_from_spec(module_spec)
|
|
21
|
-
sys.modules["discover_open_prs"] = loaded_module
|
|
22
|
-
module_spec.loader.exec_module(loaded_module)
|
|
23
|
-
return loaded_module
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
discover_open_prs = _load_discovery_module()
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _make_fake_gh(responses_by_owner: dict):
|
|
30
|
-
"""Return a callable whose behavior mocks subprocess.run for gh search."""
|
|
31
|
-
|
|
32
|
-
class FakeCompletedProcess:
|
|
33
|
-
def __init__(self, stdout_text: str):
|
|
34
|
-
self.stdout = stdout_text
|
|
35
|
-
self.returncode = 0
|
|
36
|
-
|
|
37
|
-
def fake_run(command_argv, check, capture_output, text):
|
|
38
|
-
owner = _extract_owner_argument(command_argv)
|
|
39
|
-
stdout_text = json.dumps(responses_by_owner.get(owner, []))
|
|
40
|
-
return FakeCompletedProcess(stdout_text)
|
|
41
|
-
|
|
42
|
-
return fake_run
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _extract_owner_argument(command_argv) -> str:
|
|
46
|
-
for each_index, each_token in enumerate(command_argv):
|
|
47
|
-
if each_token == "--owner":
|
|
48
|
-
return command_argv[each_index + 1]
|
|
49
|
-
return ""
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class TestDiscoverOpenPrsBothOwnersReturnResults:
|
|
53
|
-
def test_merges_prs_across_both_owners(self, monkeypatch):
|
|
54
|
-
first_owner_prs = [
|
|
55
|
-
{
|
|
56
|
-
"number": 1,
|
|
57
|
-
"repository": {"nameWithOwner": "jl-cmd/alpha"},
|
|
58
|
-
"url": "https://github.com/jl-cmd/alpha/pull/1",
|
|
59
|
-
"headRefName": "feat-one",
|
|
60
|
-
"baseRefName": "main",
|
|
61
|
-
}
|
|
62
|
-
]
|
|
63
|
-
second_owner_prs = [
|
|
64
|
-
{
|
|
65
|
-
"number": 2,
|
|
66
|
-
"repository": {"nameWithOwner": "JonEcho/beta"},
|
|
67
|
-
"url": "https://github.com/JonEcho/beta/pull/2",
|
|
68
|
-
"headRefName": "feat-two",
|
|
69
|
-
"baseRefName": "main",
|
|
70
|
-
}
|
|
71
|
-
]
|
|
72
|
-
fake_run = _make_fake_gh(
|
|
73
|
-
{"jl-cmd": first_owner_prs, "JonEcho": second_owner_prs}
|
|
74
|
-
)
|
|
75
|
-
monkeypatch.setattr(discover_open_prs.subprocess, "run", fake_run)
|
|
76
|
-
|
|
77
|
-
all_discovered = discover_open_prs.discover_open_prs(
|
|
78
|
-
all_owners=["jl-cmd", "JonEcho"]
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
assert len(all_discovered) == 2
|
|
82
|
-
discovered_numbers = sorted(each["number"] for each in all_discovered)
|
|
83
|
-
assert discovered_numbers == [1, 2]
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class TestDiscoverOpenPrsOneOwnerEmpty:
|
|
87
|
-
def test_handles_single_owner_returning_empty_list(self, monkeypatch):
|
|
88
|
-
first_owner_prs = [
|
|
89
|
-
{
|
|
90
|
-
"number": 5,
|
|
91
|
-
"repository": {"nameWithOwner": "jl-cmd/gamma"},
|
|
92
|
-
"url": "https://github.com/jl-cmd/gamma/pull/5",
|
|
93
|
-
"headRefName": "feat-five",
|
|
94
|
-
"baseRefName": "main",
|
|
95
|
-
}
|
|
96
|
-
]
|
|
97
|
-
fake_run = _make_fake_gh({"jl-cmd": first_owner_prs, "JonEcho": []})
|
|
98
|
-
monkeypatch.setattr(discover_open_prs.subprocess, "run", fake_run)
|
|
99
|
-
|
|
100
|
-
all_discovered = discover_open_prs.discover_open_prs(
|
|
101
|
-
all_owners=["jl-cmd", "JonEcho"]
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
assert len(all_discovered) == 1
|
|
105
|
-
assert all_discovered[0]["number"] == 5
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
class TestDiscoverOpenPrsBothEmpty:
|
|
109
|
-
def test_returns_empty_list_when_no_owner_has_open_prs(self, monkeypatch):
|
|
110
|
-
fake_run = _make_fake_gh({"jl-cmd": [], "JonEcho": []})
|
|
111
|
-
monkeypatch.setattr(discover_open_prs.subprocess, "run", fake_run)
|
|
112
|
-
|
|
113
|
-
all_discovered = discover_open_prs.discover_open_prs(
|
|
114
|
-
all_owners=["jl-cmd", "JonEcho"]
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
assert all_discovered == []
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
class TestDiscoverOpenPrsEntryShape:
|
|
121
|
-
def test_each_entry_exposes_number_owner_repo_refs_and_url(self, monkeypatch):
|
|
122
|
-
fake_run = _make_fake_gh(
|
|
123
|
-
{
|
|
124
|
-
"jl-cmd": [
|
|
125
|
-
{
|
|
126
|
-
"number": 42,
|
|
127
|
-
"repository": {"nameWithOwner": "jl-cmd/demo"},
|
|
128
|
-
"url": "https://github.com/jl-cmd/demo/pull/42",
|
|
129
|
-
"headRefName": "feat-demo",
|
|
130
|
-
"baseRefName": "main",
|
|
131
|
-
}
|
|
132
|
-
],
|
|
133
|
-
"JonEcho": [],
|
|
134
|
-
}
|
|
135
|
-
)
|
|
136
|
-
monkeypatch.setattr(discover_open_prs.subprocess, "run", fake_run)
|
|
137
|
-
|
|
138
|
-
all_discovered = discover_open_prs.discover_open_prs(
|
|
139
|
-
all_owners=["jl-cmd", "JonEcho"]
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
assert len(all_discovered) == 1
|
|
143
|
-
only_entry = all_discovered[0]
|
|
144
|
-
assert only_entry["number"] == 42
|
|
145
|
-
assert only_entry["owner"] == "jl-cmd"
|
|
146
|
-
assert only_entry["repo"] == "demo"
|
|
147
|
-
assert only_entry["head_ref"] == "feat-demo"
|
|
148
|
-
assert only_entry["base_ref"] == "main"
|
|
149
|
-
assert only_entry["url"].endswith("/pull/42")
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"""Markdown assertion tests for monitor-open-prs SKILL.md."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import pathlib
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _read_skill_text() -> str:
|
|
9
|
-
skill_path = pathlib.Path(__file__).parent / "SKILL.md"
|
|
10
|
-
return skill_path.read_text(encoding="utf-8")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_skill_has_frontmatter_name():
|
|
14
|
-
skill_text = _read_skill_text()
|
|
15
|
-
assert skill_text.startswith("---\n")
|
|
16
|
-
assert "name: monitor-open-prs" in skill_text
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def test_skill_references_bugbot_retrigger_flag():
|
|
20
|
-
skill_text = _read_skill_text()
|
|
21
|
-
assert "--bugbot-retrigger" in skill_text
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def test_skill_enumerates_both_owner_scopes():
|
|
25
|
-
skill_text = _read_skill_text()
|
|
26
|
-
assert "jl-cmd" in skill_text
|
|
27
|
-
assert "JonEcho" in skill_text
|