shrinker-ai 0.3.3 โ 0.7.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/.gitattributes +1 -0
- package/README.md +494 -363
- package/dist/src/cli.js +144 -25
- package/dist/src/config.js +42 -0
- package/dist/src/execution/run-command.js +2 -2
- package/dist/src/filters/select-filter.js +26 -18
- package/dist/src/metrics/coverage.js +67 -0
- package/dist/src/metrics/dashboard-template.generated.js +4 -0
- package/dist/src/metrics/dashboard.js +137 -115
- package/dist/src/metrics/stats-store.js +211 -51
- package/integrations/macos/install.sh +150 -7
- package/integrations/macos/shrinker-profile.zsh +165 -98
- package/integrations/macos/uninstall.sh +70 -3
- package/integrations/windows/install.ps1 +239 -113
- package/integrations/windows/shrinker-profile.ps1 +206 -136
- package/integrations/windows/uninstall.ps1 +126 -57
- package/package.json +49 -39
- package/scripts/package-release.mjs +129 -0
- package/templates/agent-rules.md +18 -18
- package/.copilot-instructions.md +0 -28
- package/CLAUDE.md +0 -28
|
@@ -1,136 +1,206 @@
|
|
|
1
|
-
$global:ShrinkPowerShellRules = @{
|
|
2
|
-
git = @("status", "diff", "log", "show", "reflog", "branch", "tag", "stash")
|
|
3
|
-
npm = @("test", "t", "install", "i", "ci", "ls", "list")
|
|
4
|
-
docker = @("ps", "logs", "images", "compose")
|
|
5
|
-
kubectl = @("get", "describe", "logs")
|
|
6
|
-
gh = @("pr", "issue", "run")
|
|
7
|
-
rg = @("*")
|
|
8
|
-
find = @("*")
|
|
9
|
-
tail = @("*")
|
|
10
|
-
cat = @("*")
|
|
11
|
-
ls = @("*")
|
|
12
|
-
dir = @("*")
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
$global:ShrinkOptionValueFlags = @{
|
|
16
|
-
git = @("-C", "-c", "--git-dir", "--work-tree", "--namespace")
|
|
17
|
-
npm = @("--prefix", "--cache", "--registry", "--workspace", "--userconfig", "-w", "-C")
|
|
18
|
-
docker = @("-H", "--host", "--context", "--config")
|
|
19
|
-
kubectl = @("-n", "--namespace", "-o", "--output", "--context", "--kubeconfig", "--cluster", "--user")
|
|
20
|
-
gh = @("-R", "--repo")
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
$global:ShrinkNativePaths = @{}
|
|
24
|
-
foreach ($name in @("git", "npm", "docker", "kubectl", "gh", "rg", "find", "tail")) {
|
|
25
|
-
$command = Get-Command $name -CommandType Application -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
26
|
-
if ($command) {
|
|
27
|
-
$global:ShrinkNativePaths[$name] = $command.Source
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function Get-ShrinkSubcommand {
|
|
32
|
-
param(
|
|
33
|
-
[string]$CommandName,
|
|
34
|
-
[object[]]$Arguments
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
$valueFlags = @($global:ShrinkOptionValueFlags[$CommandName])
|
|
38
|
-
for ($i = 0; $i -lt $Arguments.Count; $i += 1) {
|
|
39
|
-
$part = [string]$Arguments[$i]
|
|
40
|
-
if (-not $part) { continue }
|
|
41
|
-
if ($valueFlags -contains $part) {
|
|
42
|
-
$i += 1
|
|
43
|
-
continue
|
|
44
|
-
}
|
|
45
|
-
if ($part.StartsWith("-")) { continue }
|
|
46
|
-
return $part.ToLowerInvariant()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return ""
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function Use-ShrinkRouting {
|
|
53
|
-
param(
|
|
54
|
-
[string]$CommandName,
|
|
55
|
-
[object[]]$Arguments
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
$allowlist = @($global:ShrinkPowerShellRules[$CommandName])
|
|
59
|
-
if ($allowlist.Count -eq 0) {
|
|
60
|
-
return $false
|
|
61
|
-
}
|
|
62
|
-
if ($allowlist -contains "*") {
|
|
63
|
-
return $true
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
$subcommand = Get-ShrinkSubcommand -CommandName $CommandName -Arguments $Arguments
|
|
67
|
-
return $allowlist -contains $subcommand
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function Get-ShrinkCommandPath {
|
|
71
|
-
$candidates = Get-Command shrinker -All -ErrorAction SilentlyContinue
|
|
72
|
-
if (-not $candidates) {
|
|
73
|
-
return $null
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
foreach ($candidate in $candidates) {
|
|
77
|
-
$source = [string]$candidate.Source
|
|
78
|
-
if ($source -and $source.ToLowerInvariant().EndsWith(".cmd")) {
|
|
79
|
-
return $source
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
foreach ($candidate in $candidates) {
|
|
83
|
-
$source = [string]$candidate.Source
|
|
84
|
-
if ($source -and $source.ToLowerInvariant().EndsWith(".exe")) {
|
|
85
|
-
return $source
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
foreach ($candidate in $candidates) {
|
|
89
|
-
$source = [string]$candidate.Source
|
|
90
|
-
if ($source -and $source.ToLowerInvariant().EndsWith(".ps1")) {
|
|
91
|
-
return $source
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return [string]($candidates | Select-Object -First 1).Source
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
$
|
|
113
|
-
if ($
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
1
|
+
$global:ShrinkPowerShellRules = @{
|
|
2
|
+
git = @("status", "diff", "log", "show", "reflog", "branch", "tag", "stash")
|
|
3
|
+
npm = @("test", "t", "install", "i", "ci", "ls", "list")
|
|
4
|
+
docker = @("ps", "logs", "images", "compose")
|
|
5
|
+
kubectl = @("get", "describe", "logs")
|
|
6
|
+
gh = @("pr", "issue", "run")
|
|
7
|
+
rg = @("*")
|
|
8
|
+
find = @("*")
|
|
9
|
+
tail = @("*")
|
|
10
|
+
cat = @("*")
|
|
11
|
+
ls = @("*")
|
|
12
|
+
dir = @("*")
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
$global:ShrinkOptionValueFlags = @{
|
|
16
|
+
git = @("-C", "-c", "--git-dir", "--work-tree", "--namespace")
|
|
17
|
+
npm = @("--prefix", "--cache", "--registry", "--workspace", "--userconfig", "-w", "-C")
|
|
18
|
+
docker = @("-H", "--host", "--context", "--config")
|
|
19
|
+
kubectl = @("-n", "--namespace", "-o", "--output", "--context", "--kubeconfig", "--cluster", "--user")
|
|
20
|
+
gh = @("-R", "--repo")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
$global:ShrinkNativePaths = @{}
|
|
24
|
+
foreach ($name in @("git", "npm", "docker", "kubectl", "gh", "rg", "find", "tail")) {
|
|
25
|
+
$command = Get-Command $name -CommandType Application -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
26
|
+
if ($command) {
|
|
27
|
+
$global:ShrinkNativePaths[$name] = $command.Source
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function Get-ShrinkSubcommand {
|
|
32
|
+
param(
|
|
33
|
+
[string]$CommandName,
|
|
34
|
+
[object[]]$Arguments
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
$valueFlags = @($global:ShrinkOptionValueFlags[$CommandName])
|
|
38
|
+
for ($i = 0; $i -lt $Arguments.Count; $i += 1) {
|
|
39
|
+
$part = [string]$Arguments[$i]
|
|
40
|
+
if (-not $part) { continue }
|
|
41
|
+
if ($valueFlags -contains $part) {
|
|
42
|
+
$i += 1
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
if ($part.StartsWith("-")) { continue }
|
|
46
|
+
return $part.ToLowerInvariant()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return ""
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function Use-ShrinkRouting {
|
|
53
|
+
param(
|
|
54
|
+
[string]$CommandName,
|
|
55
|
+
[object[]]$Arguments
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
$allowlist = @($global:ShrinkPowerShellRules[$CommandName])
|
|
59
|
+
if ($allowlist.Count -eq 0) {
|
|
60
|
+
return $false
|
|
61
|
+
}
|
|
62
|
+
if ($allowlist -contains "*") {
|
|
63
|
+
return $true
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
$subcommand = Get-ShrinkSubcommand -CommandName $CommandName -Arguments $Arguments
|
|
67
|
+
return $allowlist -contains $subcommand
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function Get-ShrinkCommandPath {
|
|
71
|
+
$candidates = Get-Command shrinker -All -ErrorAction SilentlyContinue
|
|
72
|
+
if (-not $candidates) {
|
|
73
|
+
return $null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
foreach ($candidate in $candidates) {
|
|
77
|
+
$source = [string]$candidate.Source
|
|
78
|
+
if ($source -and $source.ToLowerInvariant().EndsWith(".cmd")) {
|
|
79
|
+
return $source
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
foreach ($candidate in $candidates) {
|
|
83
|
+
$source = [string]$candidate.Source
|
|
84
|
+
if ($source -and $source.ToLowerInvariant().EndsWith(".exe")) {
|
|
85
|
+
return $source
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
foreach ($candidate in $candidates) {
|
|
89
|
+
$source = [string]$candidate.Source
|
|
90
|
+
if ($source -and $source.ToLowerInvariant().EndsWith(".ps1")) {
|
|
91
|
+
return $source
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return [string]($candidates | Select-Object -First 1).Source
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Resolved once at load so wrapped commands never pay for reading the config file.
|
|
99
|
+
$script:ShrinkTrackUncoveredDefault = ""
|
|
100
|
+
$script:ShrinkConfigPath = if ($env:SHRINKER_CONFIG_PATH) { $env:SHRINKER_CONFIG_PATH } else { Join-Path $HOME ".shrinker/config" }
|
|
101
|
+
if (Test-Path -LiteralPath $script:ShrinkConfigPath) {
|
|
102
|
+
foreach ($line in Get-Content -LiteralPath $script:ShrinkConfigPath) {
|
|
103
|
+
$stripped = ($line -split "#", 2)[0].Trim()
|
|
104
|
+
if ($stripped -match '^\s*SHRINKER_TRACK_UNCOVERED\s*=\s*(.*)$') {
|
|
105
|
+
$script:ShrinkTrackUncoveredDefault = $Matches[1].Trim()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function Test-ShrinkTrackingEnabled {
|
|
111
|
+
$value = [string]$env:SHRINKER_TRACK_UNCOVERED
|
|
112
|
+
if (-not $value) { $value = $script:ShrinkTrackUncoveredDefault }
|
|
113
|
+
if (-not $value) { return $false }
|
|
114
|
+
return @("1", "true", "yes") -contains $value.Trim().ToLowerInvariant()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function Write-ShrinkUncovered {
|
|
118
|
+
param(
|
|
119
|
+
[string]$CommandName,
|
|
120
|
+
[object[]]$Arguments,
|
|
121
|
+
[int]$Bytes,
|
|
122
|
+
[int]$ExitCode
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
if (-not (Test-ShrinkTrackingEnabled)) { return }
|
|
126
|
+
$shrinkCommand = Get-ShrinkCommandPath
|
|
127
|
+
if (-not $shrinkCommand) { return }
|
|
128
|
+
|
|
129
|
+
$trackArgs = @("track", "--executable", $CommandName, "--bytes", $Bytes, "--exit-code", $ExitCode)
|
|
130
|
+
$subcommand = Get-ShrinkSubcommand -CommandName $CommandName -Arguments $Arguments
|
|
131
|
+
if ($subcommand) {
|
|
132
|
+
$trackArgs += @("--subcommand", $subcommand)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
Start-Process -FilePath $shrinkCommand -ArgumentList $trackArgs -WindowStyle Hidden -ErrorAction SilentlyContinue | Out-Null
|
|
137
|
+
} catch {}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function Invoke-ShrinkNativeTracked {
|
|
141
|
+
param(
|
|
142
|
+
[scriptblock]$Invoke,
|
|
143
|
+
[string]$CommandName,
|
|
144
|
+
[object[]]$Arguments
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if (-not (Test-ShrinkTrackingEnabled)) {
|
|
148
|
+
& $Invoke
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Only measure output volume when stdout is redirected (the agent case). Interactive
|
|
153
|
+
# consoles stream the native output untouched so paging and colours still work.
|
|
154
|
+
if (-not [Console]::IsOutputRedirected) {
|
|
155
|
+
& $Invoke
|
|
156
|
+
Write-ShrinkUncovered -CommandName $CommandName -Arguments $Arguments -Bytes 0 -ExitCode ([int]$LASTEXITCODE)
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
$output = & $Invoke | Out-String -Stream
|
|
161
|
+
$exitCode = [int]$LASTEXITCODE
|
|
162
|
+
$text = ($output -join [Environment]::NewLine)
|
|
163
|
+
$bytes = [System.Text.Encoding]::UTF8.GetByteCount($text)
|
|
164
|
+
$output | ForEach-Object { $_ }
|
|
165
|
+
Write-ShrinkUncovered -CommandName $CommandName -Arguments $Arguments -Bytes $bytes -ExitCode $exitCode
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function Invoke-ShrinkOrNative {
|
|
169
|
+
param(
|
|
170
|
+
[string]$CommandName,
|
|
171
|
+
[object[]]$Arguments
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
$routeToShrink = Use-ShrinkRouting -CommandName $CommandName -Arguments $Arguments
|
|
175
|
+
$shrinkCommand = Get-ShrinkCommandPath
|
|
176
|
+
|
|
177
|
+
if ($routeToShrink -and $shrinkCommand) {
|
|
178
|
+
& $shrinkCommand $CommandName @Arguments
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
$nativePath = $global:ShrinkNativePaths[$CommandName]
|
|
183
|
+
if ($nativePath) {
|
|
184
|
+
Invoke-ShrinkNativeTracked -CommandName $CommandName -Arguments $Arguments -Invoke { & $nativePath @Arguments }
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
switch ($CommandName) {
|
|
189
|
+
"cat" { Invoke-ShrinkNativeTracked -CommandName $CommandName -Arguments $Arguments -Invoke { Microsoft.PowerShell.Management\Get-Content @Arguments }; return }
|
|
190
|
+
"ls" { Invoke-ShrinkNativeTracked -CommandName $CommandName -Arguments $Arguments -Invoke { Microsoft.PowerShell.Management\Get-ChildItem @Arguments }; return }
|
|
191
|
+
"dir" { Invoke-ShrinkNativeTracked -CommandName $CommandName -Arguments $Arguments -Invoke { Microsoft.PowerShell.Management\Get-ChildItem @Arguments }; return }
|
|
192
|
+
default { throw "No native fallback found for '$CommandName'." }
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function global:git { Invoke-ShrinkOrNative -CommandName "git" -Arguments $args }
|
|
197
|
+
function global:npm { Invoke-ShrinkOrNative -CommandName "npm" -Arguments $args }
|
|
198
|
+
function global:docker { Invoke-ShrinkOrNative -CommandName "docker" -Arguments $args }
|
|
199
|
+
function global:kubectl { Invoke-ShrinkOrNative -CommandName "kubectl" -Arguments $args }
|
|
200
|
+
function global:gh { Invoke-ShrinkOrNative -CommandName "gh" -Arguments $args }
|
|
201
|
+
function global:rg { Invoke-ShrinkOrNative -CommandName "rg" -Arguments $args }
|
|
202
|
+
function global:find { Invoke-ShrinkOrNative -CommandName "find" -Arguments $args }
|
|
203
|
+
function global:tail { Invoke-ShrinkOrNative -CommandName "tail" -Arguments $args }
|
|
204
|
+
function global:cat { Invoke-ShrinkOrNative -CommandName "cat" -Arguments $args }
|
|
205
|
+
function global:ls { Invoke-ShrinkOrNative -CommandName "ls" -Arguments $args }
|
|
206
|
+
function global:dir { Invoke-ShrinkOrNative -CommandName "dir" -Arguments $args }
|
|
@@ -1,57 +1,126 @@
|
|
|
1
|
-
param(
|
|
2
|
-
[switch]$
|
|
3
|
-
[switch]$
|
|
4
|
-
[switch]$
|
|
5
|
-
[switch]$
|
|
6
|
-
[
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
$
|
|
10
|
-
$
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function
|
|
19
|
-
param([string]$
|
|
20
|
-
|
|
21
|
-
$
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (-not $
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
1
|
+
param(
|
|
2
|
+
[switch]$UseNpm,
|
|
3
|
+
[switch]$SkipUnlink,
|
|
4
|
+
[switch]$SkipAgentRules,
|
|
5
|
+
[switch]$CopilotOnly,
|
|
6
|
+
[switch]$ClaudeOnly,
|
|
7
|
+
[switch]$PurgeData,
|
|
8
|
+
[int]$Port = 4317,
|
|
9
|
+
[string]$InstallDir = $(Join-Path $HOME ".shrinker"),
|
|
10
|
+
[string]$ProfilePath = $PROFILE,
|
|
11
|
+
[string]$ConfigPath = $(if ($env:SHRINKER_CONFIG_PATH) { $env:SHRINKER_CONFIG_PATH } else { Join-Path $HOME ".shrinker/config" })
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
$ErrorActionPreference = "Stop"
|
|
15
|
+
$UninstallStep = 0
|
|
16
|
+
$DataDir = Split-Path -Parent $ConfigPath
|
|
17
|
+
|
|
18
|
+
function Write-UninstallStep {
|
|
19
|
+
param([string]$Emoji, [string]$Message)
|
|
20
|
+
$script:UninstallStep++
|
|
21
|
+
Write-Host "$($script:UninstallStep). $Emoji $Message"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function Remove-AgentRules {
|
|
25
|
+
param([string]$Path)
|
|
26
|
+
if (-not (Test-Path $Path)) { return }
|
|
27
|
+
$content = Get-Content $Path -Raw
|
|
28
|
+
$start = "<!-- shrinker agent rules start -->"
|
|
29
|
+
$end = "<!-- shrinker agent rules end -->"
|
|
30
|
+
if ($null -ne $content -and $content.Contains($start) -and $content.Contains($end)) {
|
|
31
|
+
$pattern = [regex]::Escape($start) + ".*?" + [regex]::Escape($end)
|
|
32
|
+
Set-Content $Path ([regex]::Replace($content, $pattern, "", 'Singleline').TrimEnd() + "`n")
|
|
33
|
+
Write-UninstallStep "๐" "Removed managed rules from: $Path"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function Remove-ProfileIntegration {
|
|
38
|
+
param([string]$ProfileFile)
|
|
39
|
+
if (-not (Test-Path $ProfileFile)) { return }
|
|
40
|
+
$content = Get-Content $ProfileFile -Raw
|
|
41
|
+
if ($null -eq $content) { return }
|
|
42
|
+
$start = $content.IndexOf("# >>> shrinker integration >>>")
|
|
43
|
+
$endMarker = "# <<< shrinker integration <<<"
|
|
44
|
+
if ($start -ge 0) {
|
|
45
|
+
$end = $content.IndexOf($endMarker, $start)
|
|
46
|
+
if ($end -ge 0) { Set-Content $ProfileFile $content.Remove($start, $end + $endMarker.Length - $start) }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function Remove-UserPathEntry {
|
|
51
|
+
param([string]$Directory)
|
|
52
|
+
$currentUserPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
53
|
+
if (-not $currentUserPath) { return }
|
|
54
|
+
$entries = @($currentUserPath -split [IO.Path]::PathSeparator | Where-Object { $_ -and $_ -ne $Directory })
|
|
55
|
+
$newPath = $entries -join [IO.Path]::PathSeparator
|
|
56
|
+
if ($newPath -ne $currentUserPath) {
|
|
57
|
+
[Environment]::SetEnvironmentVariable("Path", $newPath, "User")
|
|
58
|
+
Write-UninstallStep "๐งญ" "Removed shrinker from the user PATH: $Directory"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function Remove-ReleaseInstall {
|
|
63
|
+
foreach ($name in @("bin", "integrations", "templates", "manifest.json")) {
|
|
64
|
+
Remove-Item -LiteralPath (Join-Path $InstallDir $name) -Recurse -Force -ErrorAction SilentlyContinue
|
|
65
|
+
}
|
|
66
|
+
Remove-UserPathEntry (Join-Path $InstallDir "bin")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# The dashboard runs as a detached daemon, so unlinking the package never stops it.
|
|
70
|
+
function Stop-DashboardServer {
|
|
71
|
+
param([int]$DashboardPort)
|
|
72
|
+
try {
|
|
73
|
+
Invoke-WebRequest -Uri "http://127.0.0.1:$DashboardPort/__shrinker_shutdown" -Method Post -TimeoutSec 3 -UseBasicParsing | Out-Null
|
|
74
|
+
Start-Sleep -Seconds 1
|
|
75
|
+
}
|
|
76
|
+
catch { }
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
$listener = Get-NetTCPConnection -LocalPort $DashboardPort -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
80
|
+
if ($listener) { Stop-Process -Id $listener.OwningProcess -Force -ErrorAction SilentlyContinue }
|
|
81
|
+
}
|
|
82
|
+
catch { }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Only drop installer-managed keys so hand-written settings survive.
|
|
86
|
+
function Remove-ManagedConfig {
|
|
87
|
+
if (-not (Test-Path -LiteralPath $ConfigPath)) { return }
|
|
88
|
+
$remaining = @(Get-Content -LiteralPath $ConfigPath | Where-Object { $_ -notmatch '^\s*SHRINKER_TRACK_UNCOVERED\s*=' })
|
|
89
|
+
if ($remaining.Count -gt 0) { Set-Content -LiteralPath $ConfigPath -Value $remaining -Encoding utf8 }
|
|
90
|
+
else { Remove-Item -LiteralPath $ConfigPath -Force -ErrorAction SilentlyContinue }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Write-UninstallStep "๐" "Stopping the dashboard server on port $Port..."
|
|
94
|
+
Stop-DashboardServer $Port
|
|
95
|
+
|
|
96
|
+
if (-not $SkipUnlink -and $UseNpm) {
|
|
97
|
+
Write-UninstallStep "๐" "Unlinking shrinker globally..."
|
|
98
|
+
& npm unlink --silent --global shrinker-ai
|
|
99
|
+
if ($LASTEXITCODE -ne 0) { throw "npm unlink failed." }
|
|
100
|
+
}
|
|
101
|
+
elseif (-not $SkipUnlink) {
|
|
102
|
+
Write-UninstallStep "๐" "Removing release-installed shrinker files..."
|
|
103
|
+
Remove-ReleaseInstall
|
|
104
|
+
}
|
|
105
|
+
else { Write-UninstallStep "โญ๏ธ" "Skipped global npm unlink." }
|
|
106
|
+
Write-UninstallStep "๐ง" "Removing PowerShell profile integration..."
|
|
107
|
+
Remove-ProfileIntegration $ProfilePath
|
|
108
|
+
if (-not $SkipAgentRules) {
|
|
109
|
+
if (-not $ClaudeOnly) { Remove-AgentRules (Join-Path $HOME ".copilot\copilot-instructions.md") }
|
|
110
|
+
if (-not $CopilotOnly) { Remove-AgentRules (Join-Path $HOME ".claude\CLAUDE.md") }
|
|
111
|
+
}
|
|
112
|
+
else { Write-UninstallStep "โญ๏ธ" "Skipped managed agent rules." }
|
|
113
|
+
|
|
114
|
+
Write-UninstallStep "๐ง" "Removing managed settings..."
|
|
115
|
+
Remove-ManagedConfig
|
|
116
|
+
Remove-Item -LiteralPath (Join-Path $DataDir "dashboard.html") -Force -ErrorAction SilentlyContinue
|
|
117
|
+
|
|
118
|
+
if ($PurgeData) {
|
|
119
|
+
Remove-Item -LiteralPath $DataDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
120
|
+
Write-UninstallStep "๐งน" "Removed local data: $DataDir"
|
|
121
|
+
}
|
|
122
|
+
elseif (Test-Path -LiteralPath $DataDir) {
|
|
123
|
+
Write-UninstallStep "๐พ" "Kept saved stats in $DataDir (use -PurgeData to delete)."
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
Write-UninstallStep "โ
" "Uninstall complete."
|
package/package.json
CHANGED
|
@@ -1,39 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "shrinker-ai",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Local deterministic command-output shrinker for coding agents",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"shrinker": "dist/src/cli.js"
|
|
8
|
-
},
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "shrinker-ai",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Local deterministic command-output shrinker for coding agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"shrinker": "dist/src/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"workspaces": [
|
|
10
|
+
"packages/*"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"start": "npm run build --silent && npm run dev:ui",
|
|
14
|
+
"build:ui": "npm run build --workspace @shrinker/dashboard-ui",
|
|
15
|
+
"dev:ui": "npm run dev --workspace @shrinker/dashboard-ui",
|
|
16
|
+
"build": "npm run build:ui && tsc -p tsconfig.json",
|
|
17
|
+
"dashboard": "npm run build --silent && node dist/src/cli.js stats --dashboard",
|
|
18
|
+
"dashboard:restart": "npm run build --silent && node dist/src/cli.js stats --dashboard --restart",
|
|
19
|
+
"pack": "npm run build --silent && npm pack",
|
|
20
|
+
"package:release": "npm run build --silent && node scripts/package-release.mjs",
|
|
21
|
+
"package:release:current": "node scripts/package-release.mjs",
|
|
22
|
+
"test": "npm run build --silent && node --test \"dist/tests/*.test.js\"",
|
|
23
|
+
"demo": "npm run build --silent && node dist/demo/run-demo.js",
|
|
24
|
+
"install:local": "pwsh -ExecutionPolicy Bypass -File ./integrations/windows/install.ps1 -Local",
|
|
25
|
+
"install:local:macos": "./integrations/macos/install.sh --local",
|
|
26
|
+
"install:github": "pwsh -ExecutionPolicy Bypass -File ./integrations/windows/install.ps1",
|
|
27
|
+
"install:github:macos": "./integrations/macos/install.sh",
|
|
28
|
+
"uninstall:local": "pwsh -ExecutionPolicy Bypass -File ./integrations/windows/uninstall.ps1",
|
|
29
|
+
"uninstall:local:macos": "./integrations/macos/uninstall.sh",
|
|
30
|
+
"uninstall:github": "pwsh -ExecutionPolicy Bypass -File ./integrations/windows/uninstall.ps1",
|
|
31
|
+
"uninstall:github:macos": "./integrations/macos/uninstall.sh"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^24.0.0",
|
|
35
|
+
"@yao-pkg/pkg": "^6.22.0",
|
|
36
|
+
"typescript": "^5.9.0"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=22.13"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"registry": "https://registry.npmjs.org"
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/ivanduplenskikh/shrinker.git"
|
|
48
|
+
}
|
|
49
|
+
}
|