[Kill Process] Make app grouping work on Windows - #30983
Conversation
|
Thank you for your contribution! 🎉 🔔 @rolandleth @crazyones110 @zhenpewu @Saafo @erics118 @xilopaint @validatedev @nmder @mateusbadalotti @jomifepe @0xdhrv you might want to have a look. You can use this guide to learn how to check out the Pull Request locally in order to test it. 📋 Quick checkout commandsBRANCH="fix/kill-process-windows-grouping"
FORK_URL="https://github.com/Just-Me-22/extensions.git"
EXTENSION_NAME="kill-process"
REPO_NAME="extensions"
git clone -n --depth=1 --filter=tree:0 -b $BRANCH $FORK_URL
cd $REPO_NAME
git sparse-checkout set --no-cone "extensions/$EXTENSION_NAME"
git checkout
cd "extensions/$EXTENSION_NAME"
npm install && npm run devWe're currently experiencing a high volume of incoming requests. As a result, the initial review may take up to 15 business days. |
Greptile SummaryKill Process now groups related Windows processes by executable and parent chain instead of looking for macOS app bundles. Grouped Windows rows also kill every listed process and keep the executable's icon.
Confidence Score: 4/5The code appears safe, but the changelog rule must be met before merge. The process grouping fix has no accepted code bug. The changelog still breaks an explicit repository rule and must be fixed. Three earlier threads were manually resolved without explanatory replies. Files Needing Attention: Important Files Changed
Prompt To Fix All With AI### Issue 1
extensions/kill-process/CHANGELOG.md:3
The changelog rule requires new titles to keep `{PR_MERGE_DATE}` so the release job can fill in the real date. This change replaces it with `2026-09-16`. Restore the placeholder.
```suggestion
## [Fix] - {PR_MERGE_DATE}
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (7): Last reviewed commit: "Update CHANGELOG.md" | Re-trigger Greptile |
|
Both findings were right, and the first one was worse than the review says. Grouping is on by default and the group kill names every process in the row, so on my machine one A group is now a process plus the descendants running the same executable, keyed on the topmost ancestor still running it. That is the "relationship that identifies one app instance" rather than an exclusion list, so it needs no list of generic binaries to maintain. Measured over the 436 processes on my machine:
Apps that spawn their own helpers still collapse into one row. This also removed a bug I had introduced. The strategy was picked from the host platform, so On the tests, you were right that this needed coverage and I had missed that the extension already has a suite. Three added to The second covers exactly what you flagged. The third covers a protected process with no path and a parent cycle, since walking ancestors would otherwise be a place to hang. All 7 tests pass, along with |
pernielsentikaer
left a comment
There was a problem hiding this comment.
Hi @Just-Me-22 👋
Thanks for fixing Windows grouping 💪
The path normalization in parseWindowsProcesses breaks extended UNC paths: \\?\UNC\server\share\App.exe becomes relative. Restart kills the process first, then fails to relaunch it.
Could you handle UNC paths separately and restrict ordinary prefix removal to drive paths?
path: (proc.path || "")
.replace(/^\\\\\?\\UNC\\/i, "\\\\")
.replace(/^\\\\\?\\(?=[A-Za-z]:\\)/, ""),A UNC parser fixture would be helpful too.
I’ll convert this PR into a draft after submitting this review. Please press Ready for review when it’s ready and we’ll have a look 😊
|
Good catch, and the consequence is worse than a wrong string. Restart terminates first and relaunches after, so a UNC path that no longer resolves means the app is gone and cannot be brought back. Applied your version: export function normalizeWindowsPath(path: string): string {
return path.replace(/^\\\\\?\\UNC\\/i, "\\\\").replace(/^\\\\\?\\(?=[A-Za-z]:\\)/, "");
}Pulled out into a named function because the lookahead is doing something that is not obvious from the call site, and so the fixture can hit it directly. The fixture is in
The UNC row fails against the version you reviewed, which is what makes it worth keeping. I added a second test asserting the parent process id survives parsing, since grouping now depends on it and nothing covered that. All 9 tests pass, along with For completeness on the earlier round: grouping now keys on a process plus the descendants running the same executable, so unrelated |
pernielsentikaer
left a comment
There was a problem hiding this comment.
Hi @Just-Me-22 👋
Thanks for pushing this through both rounds 💪 The instance-based key is a much better answer than an exclusion list, and the UNC fix is applied exactly as asked, with the fixture going through parseWindowsProcesses. On dc4de63 I get 9/9 tests passing, clean ray lint, and a successful ray build -e dist.
One thing before this can merge:
extensions/kill-process/CHANGELOG.md conflicts with main — main added its own [Fix] - 2026-09-14 entry in the same place, so the PR no longer merges. Could you rebase on main and keep your entry below main's? The other seven files merge cleanly, so it's only the changelog.
Two optional notes, neither is a blocker:
- Grouped Windows rows lose their icon.
getFileIconreturns the executable icon forprocess.type === "app"only, and grouped rows areaggregatedApp, so they show the 🖥️ emoji instead of the app's icon. Adding|| process.type === "aggregatedApp"to that Windows check keeps the grouped row looking like the app it replaced. getKillGroupCommand's multi-/PIDstring has no test, and since the key resolver only falls back to the Windows key when the host is Windows,groupRelatedProcessesreturns ungrouped Windows data on a macOS test run — so the aggregation path itself isn't covered. A small fixture for the generated command would be a cheap win if you're up for it.
Thanks again 🙂
I’ll convert this PR into a draft after submitting this review. Please press Ready for review when it’s ready and we’ll have a look 😊
Grouping keyed off macOS .app bundle paths, which no Windows path has, so the toggle did nothing there. Windows now groups by executable path. Grouped processes on Windows are usually siblings rather than a tree, so killing a group cannot rely on taskkill /T reaching them from the main process. The group kill names every process it is removing instead. The process list comes from Win32_Process now, because app grouping needs ParentProcessId to tell which process owns a group and Get-Process does not report one.
Keying Windows groups on the executable alone merged unrelated instances of generic binaries. Twenty-two node processes with different parents became one row, and because grouping is on by default and the group kill names every process in the row, killing that row would have taken out twenty-two unrelated workloads. A group is now a process plus the descendants running the same executable, so Spotify and Discord still collapse into one row while node, cmd, conhost and svchost stay separate. The bundle path is checked before the Windows key so the strategy follows the data rather than the host. The existing macOS grouping test failed when run on Windows before this.
Stripping the extended length prefix turned \?\UNC\server\share\App.exe into a relative path, so Restart killed the process and then could not find the executable to launch it again. The UNC form now becomes \server\share and the plain prefix is only removed ahead of a drive letter.
getFileIcon only returned the executable icon for app processes, so a grouped row fell back to the generic icon and stopped looking like the app it replaced. Also covers the group kill command, which had no test.
e300776 to
a5dfa6d
Compare
|
Thank you for waiting on this one. I will admit it turned into a much harder fix than I expected when I picked up the issue, but here is everything you asked for. I had a go at the optional bits too, so it should all be working now. Rebased on Both optional notes are done as well. The grouped rows keep their icon now.
It pins the generated string for one pid and for several, with and without force, so the multi 16 tests pass, Thanks again for the patience on this, and I hope you have a good day 🙂 |
The placeholder becomes the merge date, which is later than the entry below it, so descending order puts it first.
|
Fixed, and I want to flag that I have gone against the letter of what you asked, so please tell me if you would rather have it the other way. You asked me to keep my entry below yours, and Greptile then flagged that a So it reads: My read is that you meant keep your entry as well as mine rather than a specific order, and the ordering rule settles the rest. If you did mean the order literally then say so and I will flip it straight back, since it is your extension and one commit either way. Nothing else changed. 16 tests, |
pernielsentikaer
left a comment
There was a problem hiding this comment.
Looks good to me, approved 🔥
|
Published to the Raycast Store: |
|
🎉 🎉 🎉 We've rewarded your Raycast account with some credits. You will soon be able to exchange them for some swag. |
| @@ -1,5 +1,11 @@ | |||
| # Kill Process Changelog | |||
|
|
|||
| ## [Fix] - 2026-09-16 | |||
There was a problem hiding this comment.
The changelog rule requires new titles to keep
{PR_MERGE_DATE} so the release job can fill in the real date. This change replaces it with 2026-09-16. Restore the placeholder.
| ## [Fix] - 2026-09-16 | |
| ## [Fix] - {PR_MERGE_DATE} |
Rule Used: What: Changelog entries must use {PR_MERGE_DATE} placeholder in titles, be placed at the top of the file, and maintain descending version order. Why: Standardized placeholders and consistent ordering ensure changelogs are maintainable and merge da... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/kill-process/CHANGELOG.md
Line: 3
Comment:
The changelog rule requires new titles to keep `{PR_MERGE_DATE}` so the release job can fill in the real date. This change replaces it with `2026-09-16`. Restore the placeholder.
```suggestion
## [Fix] - {PR_MERGE_DATE}
```
**Rule Used:** What: Changelog entries must use `{PR_MERGE_DATE}` placeholder in titles, be placed at the top of the file, and maintain descending version order. Why: Standardized placeholders and consistent ordering ensure changelogs are maintainable and merge da... ([source](https://app.greptile.com/raycast/-/custom-context?memory=c2214c11-df56-490a-b1c0-09a385df481a))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Description
Closes #26386.
App Grouping does nothing on Windows.
groupRelatedProcesseskeys off the outer.appbundle path, and no Windows path has one, so every process falls straight through to the ungrouped list. Against 434 live processes on my machine, exactly 0 matched that regex, which is why the reporter sees Spotify running as eight separate rows with the toggle on.Windows now groups by executable path. On the same machine that takes the list from 434 rows to 250, with Spotify's 8 processes collapsing into one row the way the report expects.
Two things came out of testing that changed the shape of this.
Grouped processes on Windows are usually siblings, not a tree. Killing a group ran
taskkill /Ton the group's main process, which is right on macOS where a bundle's processes really are descendants of the app. On Windows I measured how many group members that actually reaches:/T117 of 187 grouped children would have survived, while the UI removed their rows anyway, since it drops
[id, ...childProcessIds]from the list on success. Apps that spawn their own helpers are fine. Utility processes are siblings under different parents, so no member is an ancestor of the others. The group kill now names every process it is removing, which is exact regardless of the tree shape. I verifiedtaskkill /F /T /PID a /PID b /PID con three sibling processes: all three terminated.The Windows process list had no parent ids.
parseWindowsProcesseshardcodedpid: 0, sofindMainProcesscould not tell which process owned a group and would have picked an arbitrary one.Get-Processdoes not report a parent at all, so the list now comes fromWin32_Process, which does. Costs are 472ms before and 621ms after on my machine. Building the parent map from a separateGet-CimInstancecall and joining it toGet-Processwas much worse at 2318ms, so the single query is the cheaper of the two ways to get this.Swapping the source needed two adjustments to keep the data identical:
Win32_ProcessreportsSpotify.exewhereGet-ProcessreportsSpotify, and that name is fed toGet-Process -NamebygetKillAllCommand, which wants it without the extension. The script strips it. I compared both sources across all 435 processes: 0 name mismatches after stripping.Win32_Processsometimes returns the\\?\extended-length prefix on a path. 4 of 435 differed, allconhost.exe. Left alone that would have split conhost into two groups, so the parser normalises it.One judgement call worth your view: grouping strictly by executable means
conhost.exebecomes a single row of 29, andsvchost.exea single row of 9. It is consistent and the kill is now honest about what it removes, but if you would rather leave system paths ungrouped I am happy to add that.Checklist
npm run buildand tested this distribution build in Raycastassetsfolder are used by the extension itselfREADMEare located outside the metadata folder if they were not generated with our metadata tool