claude-dev-env 1.31.0 → 1.32.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.
@@ -0,0 +1,205 @@
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
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: rebase
3
+ description: Rebase a branch onto its base ref with the verification gates needed to catch logically broken results before pushing. Use when the user invokes `/rebase`, says "rebase this branch", "PR has merge conflicts", "rebase onto main", or asks for a force-push to update a remote branch's history. Critical for stacked PRs where the base merged via squash, and for any rebase that includes deletions or renames.
4
+ ---
5
+
6
+ # /rebase
7
+
8
+ Rebase the current branch (or a named branch's worktree) onto its correct base ref. Resolve conflicts. Verify the result is **logically correct**, not just textually clean. Push only after explicit authorization.
9
+
10
+ The default failure mode is shipping a rebase that compiled but doesn't run. This skill exists to prevent that.
11
+
12
+ ## When to rebase vs. merge
13
+
14
+ **Default to rebase** when:
15
+
16
+ - Solo branch (you are the only author of the commits being rebased).
17
+ - 1–5 commits ahead of base.
18
+ - Stacked PR whose base just merged via squash.
19
+
20
+ **Default to merge** when:
21
+
22
+ - Branch has multiple authors (force-push would clobber their state).
23
+ - More than ~5 commits or more than ~2 weeks of divergence (rebase complexity grows non-linearly).
24
+ - The user said "merge", not "rebase".
25
+ - Open PR with approving reviews already on the current SHA (force-push invalidates them).
26
+
27
+ When in doubt, ask. Both work; the choice affects history shape, not correctness.
28
+
29
+ ## Phase 1 — Pre-rebase analysis
30
+
31
+ 1. **Resolve the canonical base.** Stacked-PR base refs change when the base PR merges. Always re-resolve:
32
+
33
+ ```
34
+ gh pr view <N> --json baseRefName,mergeable,mergeStateStatus
35
+ ```
36
+
37
+ Trust GitHub's `baseRefName`, not whatever the local branch was originally based on.
38
+
39
+ 2. **Identify the rebase scenario.** Three playbooks:
40
+
41
+ | Scenario | Signal | Approach |
42
+ |---|---|---|
43
+ | Stacked PR, base merged via squash | base PR `state: MERGED`, base ref redirected to main | Rebase onto main; expect to `--skip` the squash-absorbed commit |
44
+ | Stacked PR, base still open | base PR `state: OPEN` | Rebase onto base PR's tip, not main |
45
+ | Long-lived feature branch | many commits, no stacking | Straight `rebase origin/main` |
46
+
47
+ 3. **Fetch fresh.** `git fetch origin <base-ref> <head-ref>` before starting. Stale local refs cause false conflicts.
48
+
49
+ 4. **Read every rebased commit's message.** Author intent lives in commit messages ("removes orphan constants X, Y, Z"). For every named symbol mentioned as deleted or renamed, scan `origin/main` for new consumers — if main has new uses of those symbols, the rebase will produce broken state without conflicting at the textual level.
50
+
51
+ **Tool preference for symbol scans** (in order):
52
+
53
+ | Tool | Use when | Example |
54
+ |---|---|---|
55
+ | `mcp__serena__find_symbol` / `find_referencing_symbols` | Symbol-aware language server is available — definition vs. reference distinction matters, and you want call-site context | `find_referencing_symbols(symbol_name)` returns every caller with file/line and surrounding code |
56
+ | `mcp__zoekt__search_symbols` / `search` | Cross-repo or large codebase indexed in zoekt; faster than grep on big trees | `search(query)` returns ranked matches with snippets |
57
+ | `Grep` tool (ripgrep) | Local single-repo plain-text scan; no symbol awareness needed | `Grep(pattern, type="py")` — much faster than shell `grep` and respects `.gitignore` |
58
+ | `grep -rn` | Last resort; only when the above are unavailable | — |
59
+
60
+ The Grep tool is the default for plain-text scans (faster than shell grep, respects gitignore). Reach for serena when you need to distinguish "this name is defined here" from "this name is referenced here," which catches false positives from comments, docstrings, and string literals. Reach for zoekt for cross-repo scans.
61
+
62
+ This is the bug that hides best. Don't skip it.
63
+
64
+ ## Phase 2 — During rebase
65
+
66
+ 5. **`--skip` only after verifying content overlap.** When a commit fails to apply because main already has its content (squash-merge case), confirm the content is actually equivalent:
67
+
68
+ ```
69
+ git diff <skipped-commit> origin/<base> -- <files-touched-by-skipped-commit>
70
+ ```
71
+
72
+ Skip only after that diff shows the equivalent content lives in main. Don't skip on the heuristic "main is downstream."
73
+
74
+ 6. **Audit auto-merged files.** Files that git merged without conflict markers are not automatically correct. After each commit applies, run:
75
+
76
+ ```
77
+ git diff --name-only --diff-filter=M HEAD@{1}
78
+ ```
79
+
80
+ For each modified file with no conflict markers, eyeball the changes — auto-merge can produce duplicate blocks (when both sides added similar content) or silently drop content (when both sides removed adjacent lines). Pay extra attention to `config/`, `constants.*`, and `__init__.py` files where additions often sit near each other.
81
+
82
+ 7. **At every conflict, take both sides' intent seriously.** Read both, then decide based on the post-rebase logical state. Do not reflex-pick HEAD or `origin/main`. Document the resolution reasoning in the commit message if it is non-obvious.
83
+
84
+ ## Phase 3 — Verification gates (mandatory before push)
85
+
86
+ `py_compile`, `tsc --noEmit`, `cargo check`, etc. validate **syntax and types**, not **import resolution and runtime correctness**. Run real checks:
87
+
88
+ 8. **Real import check.** For Python:
89
+
90
+ ```
91
+ python -c "import <every_top_level_module_in_changed_packages>"
92
+ ```
93
+
94
+ This catches the most common rebase failure: an import that survived the rebase pointing at a name the rebased commits removed or renamed.
95
+
96
+ 9. **Test collection.** `pytest --collect-only -q` on the changed packages catches NameError, AttributeError, and ImportError surfaces beyond plain imports.
97
+
98
+ 10. **Targeted test run.** Run the test suite for every package the rebase touched. Do not push a rebase that dropped or broke test coverage that was passing pre-rebase.
99
+
100
+ 11. **Reference scan for removals/renames.** For every symbol the rebase deleted or renamed (per the commit messages from step 4), scan the post-rebase tree using the same tool-preference order as step 4:
101
+
102
+ - **Preferred:** `mcp__serena__find_referencing_symbols` (symbol-aware; ignores false matches in comments and string literals).
103
+ - **Fallback:** `mcp__zoekt__search` for cross-repo or large trees.
104
+ - **Then:** the `Grep` tool (e.g., `Grep(pattern="<symbol>", type="py")`) for fast in-repo scans.
105
+ - **Last resort:** `grep -rn "<symbol>" --include='*.py' --include='*.ts' --include='*.json' .`
106
+
107
+ Any reference outside the rebased commits' own changes is a stale reference. Either update it (with user authorization) or surface it and refuse to push.
108
+
109
+ 12. **"Unchanged" files are not safe.** A rebase can break files it never textually modified if their imports depend on something the rebase removed or renamed. List `git diff --name-only origin/<base>..HEAD`, then for every file NOT in that list but that imports from a file IN that list, manually verify the imports still resolve.
110
+
111
+ ## Phase 4 — Push
112
+
113
+ 13. **Force-push requires explicit authorization.** Auto mode does not bypass this. Before any `git push --force` or `--force-with-lease`:
114
+
115
+ - State the rewrite scope: "this rewrites N commits of remote history on branch `<name>`".
116
+ - State the branch's PR state: "PR #M is OPEN with K approving reviews on the current SHA" (force-push invalidates approvals).
117
+ - Ask for explicit authorization.
118
+ - If denied: leave the rebase result locally, report merge-instead as the alternative, stop.
119
+
120
+ 14. **Refuse to force-push** `main`, `master`, `release/*`, `production`, or any branch with more than one author in `git log --format='%ae' origin/<branch> | sort -u`. Surface the refusal; do not ask for authorization on these.
121
+
122
+ 15. **Always `--force-with-lease=<branch>:<sha>`**, never bare `--force`. Pin the lease to the SHA you started from so concurrent pushes are detected as a lease mismatch instead of clobbered.
123
+
124
+ 16. **Confirm mergeability** post-push:
125
+
126
+ ```
127
+ gh pr view <N> --json mergeable,mergeStateStatus,headRefOid
128
+ ```
129
+
130
+ Expect `mergeable: MERGEABLE`, `mergeStateStatus: CLEAN`. Anything else means the rebase didn't actually unblock the PR — investigate before declaring done.
131
+
132
+ ## Anti-patterns (the failure modes this skill prevents)
133
+
134
+ - **`py_compile` exit 0 → "verified"**. Wrong. Syntax-clean is not import-clean. Always run a real `import` check.
135
+ - **Skipping squash-absorbed commits without diff verification.** Heuristically usually right, occasionally wrong, never confirmed without the diff.
136
+ - **Auditing only `git diff --name-only`.** Files unchanged by the rebase can still break if their imports depended on what the rebase removed.
137
+ - **Force-pushing under "the user asked me to fix the conflicts" interpretation.** Force-push is a separate authorization from "fix this." Ask.
138
+ - **Bare `--force` instead of `--force-with-lease=<branch>:<sha>`.** Loses the safety net against concurrent pushes.
139
+
140
+ ## Quick decision flowchart
141
+
142
+ ```
143
+ Branch shared with others? ──► merge instead
144
+ Stacked PR, base merged? ──► rebase onto main, expect --skip
145
+ Stacked PR, base open? ──► rebase onto base PR tip
146
+ Solo, ≤5 commits ahead? ──► straight rebase
147
+
148
+ After rebase, BEFORE push:
149
+ python -c "import …" ──► must succeed
150
+ pytest --collect-only ──► must succeed
151
+ targeted pytest run ──► must pass
152
+ symbol scan (serena → zoekt → Grep) ──► no stale references
153
+
154
+ Push:
155
+ not main/master/release ──► force-with-lease, ask first
156
+ main/master/release ──► refuse
157
+ ```