shrinker-ai 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.copilot-instructions.md +28 -0
- package/CLAUDE.md +28 -0
- package/README.md +363 -363
- package/dist/src/cli.js +18 -18
- package/dist/src/metrics/dashboard.js +65 -65
- package/dist/src/metrics/stats-store.js +43 -43
- package/integrations/macos/install.sh +1 -4
- package/integrations/macos/shrinker-profile.zsh +98 -98
- package/integrations/macos/uninstall.sh +0 -0
- package/integrations/windows/install.ps1 +113 -113
- package/integrations/windows/shrinker-profile.ps1 +136 -136
- package/integrations/windows/uninstall.ps1 +57 -57
- package/package.json +39 -39
- package/templates/agent-rules.md +18 -18
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
param(
|
|
2
|
-
[switch]$Local,
|
|
3
|
-
[string]$PackageName = "shrinker-ai",
|
|
4
|
-
[string]$Registry = "https://registry.npmjs.org",
|
|
5
|
-
[string]$Version,
|
|
6
|
-
[switch]$SkipNpmInstall,
|
|
7
|
-
[switch]$SkipBuild,
|
|
8
|
-
[switch]$SkipLink,
|
|
9
|
-
[switch]$EnableProfileRouting,
|
|
10
|
-
[switch]$SkipProfile,
|
|
11
|
-
[switch]$SkipAgentRules,
|
|
12
|
-
[switch]$CopilotOnly,
|
|
13
|
-
[switch]$ClaudeOnly,
|
|
14
|
-
[string]$ProfilePath = $PROFILE
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
$ErrorActionPreference = "Stop"
|
|
18
|
-
$InstallStep = 0
|
|
19
|
-
|
|
20
|
-
function Write-InstallStep {
|
|
21
|
-
param([string]$Emoji, [string]$Message)
|
|
22
|
-
$script:InstallStep++
|
|
23
|
-
Write-Host "$($script:InstallStep). $Emoji $Message"
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if ($EnableProfileRouting -and $SkipProfile) { throw "Use either -EnableProfileRouting or -SkipProfile, not both." }
|
|
27
|
-
if ($CopilotOnly -and $ClaudeOnly) { throw "Use either -CopilotOnly or -ClaudeOnly, not both." }
|
|
28
|
-
|
|
29
|
-
function Set-AgentRules {
|
|
30
|
-
param([string]$Body)
|
|
31
|
-
$start = "<!-- shrinker agent rules start -->"
|
|
32
|
-
$end = "<!-- shrinker agent rules end -->"
|
|
33
|
-
foreach ($target in @((Join-Path $HOME ".copilot\copilot-instructions.md"), (Join-Path $HOME ".claude\CLAUDE.md"))) {
|
|
34
|
-
if ($ClaudeOnly -and $target.EndsWith(".copilot-instructions.md")) { continue }
|
|
35
|
-
if ($CopilotOnly -and $target.EndsWith("CLAUDE.md")) { continue }
|
|
36
|
-
New-Item -ItemType Directory -Path (Split-Path $target) -Force | Out-Null
|
|
37
|
-
$content = if (Test-Path $target) { Get-Content $target -Raw } else { "" }
|
|
38
|
-
$block = "$start`n$Body`n$end"
|
|
39
|
-
if ($null -ne $content -and $content.Contains($start) -and $content.Contains($end)) {
|
|
40
|
-
$content = [regex]::Replace($content, [regex]::Escape($start) + ".*?" + [regex]::Escape($end), $block, 'Singleline')
|
|
41
|
-
Set-Content $target $content
|
|
42
|
-
} elseif ($content) { Add-Content $target "`n$block`n" }
|
|
43
|
-
else { Set-Content $target "$block`n" }
|
|
44
|
-
Write-InstallStep "📄" "Installed managed rules in: $target"
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function Add-ProfileIntegration {
|
|
49
|
-
param([string]$ProfileFile, [string]$IntegrationFile)
|
|
50
|
-
if (-not (Test-Path $ProfileFile)) { New-Item -ItemType File -Path $ProfileFile -Force | Out-Null }
|
|
51
|
-
$start = "# >>> shrinker integration >>>"
|
|
52
|
-
if ((Get-Content $ProfileFile -Raw).Contains($start)) { return }
|
|
53
|
-
Add-Content $ProfileFile "`n$start`n. `"$IntegrationFile`"`n# <<< shrinker integration <<<`n"
|
|
54
|
-
Write-InstallStep "🔧" "Added shrinker integration block to profile: $ProfileFile"
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (-not $Local) {
|
|
58
|
-
$packageSpec = if ($Version) { "$PackageName@$Version" } else { $PackageName }
|
|
59
|
-
Write-InstallStep "📦" "Installing $packageSpec from $Registry..."
|
|
60
|
-
& npm install --silent --global $packageSpec "--registry=$Registry"
|
|
61
|
-
if ($LASTEXITCODE -ne 0) { throw "npm package installation failed." }
|
|
62
|
-
Write-InstallStep "🔎" "Locating the installed package..."
|
|
63
|
-
$globalRoot = (& npm root --global).Trim()
|
|
64
|
-
if (-not $globalRoot) { throw "Could not determine the global npm package directory." }
|
|
65
|
-
$packageRoot = Join-Path $globalRoot ($PackageName -replace '/', '\')
|
|
66
|
-
$entrypoint = Join-Path $packageRoot "integrations\windows\install.ps1"
|
|
67
|
-
if (-not (Test-Path $entrypoint)) { throw "Installed package installer not found: $entrypoint" }
|
|
68
|
-
Write-InstallStep "⚙️" "Configuring the installed package..."
|
|
69
|
-
& pwsh -ExecutionPolicy Bypass -File $entrypoint -Local -SkipNpmInstall
|
|
70
|
-
if ($LASTEXITCODE -ne 0) { throw "Repository installer failed with exit code $LASTEXITCODE" }
|
|
71
|
-
Write-InstallStep "✅" "Install complete."
|
|
72
|
-
exit 0
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
$scriptDir = $PSScriptRoot
|
|
76
|
-
$repoRoot = (Resolve-Path (Join-Path $scriptDir "..\..")).Path
|
|
77
|
-
$templatePath = Join-Path $repoRoot "templates\agent-rules.md"
|
|
78
|
-
$integrationPath = (Resolve-Path (Join-Path $scriptDir "shrinker-profile.ps1")).Path
|
|
79
|
-
if (-not $SkipAgentRules) {
|
|
80
|
-
if (-not (Test-Path $templatePath)) { throw "Agent rules template not found: $templatePath" }
|
|
81
|
-
$rulesBody = Get-Content $templatePath -Raw
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
$versionText = & node -v 2>$null
|
|
85
|
-
if (-not $versionText) { throw "Node.js was not found on PATH. Install Node.js 22.13+ first." }
|
|
86
|
-
$version = [version]($versionText.TrimStart('v'))
|
|
87
|
-
if ($version -lt [version]'22.13.0') { throw "Node.js 22.13+ is required. Found $versionText" }
|
|
88
|
-
|
|
89
|
-
Push-Location $repoRoot
|
|
90
|
-
try {
|
|
91
|
-
Write-InstallStep "📦" "Installing shrinker from: $repoRoot"
|
|
92
|
-
if (-not $SkipNpmInstall) {
|
|
93
|
-
Write-InstallStep "📥" "Installing dependencies..."
|
|
94
|
-
& npm install --silent
|
|
95
|
-
if ($LASTEXITCODE -ne 0) { throw "npm install failed." }
|
|
96
|
-
}
|
|
97
|
-
if (-not $SkipBuild) {
|
|
98
|
-
Write-InstallStep "🏗️" "Building shrinker..."
|
|
99
|
-
& npm run build --silent
|
|
100
|
-
if ($LASTEXITCODE -ne 0) { throw "npm run build failed." }
|
|
101
|
-
}
|
|
102
|
-
if (-not $SkipLink) {
|
|
103
|
-
Write-InstallStep "🔗" "Linking shrinker globally..."
|
|
104
|
-
& npm link --silent
|
|
105
|
-
if ($LASTEXITCODE -ne 0) { throw "npm link failed." }
|
|
106
|
-
}
|
|
107
|
-
} finally { Pop-Location }
|
|
108
|
-
|
|
109
|
-
if (-not $SkipProfile -and $EnableProfileRouting) { Add-ProfileIntegration $ProfilePath $integrationPath }
|
|
110
|
-
if (-not $SkipAgentRules) { Set-AgentRules $rulesBody }
|
|
111
|
-
Write-InstallStep "✅" "Install complete."
|
|
112
|
-
Write-Host " "
|
|
113
|
-
Write-Host "💡 Try: shrinker help"
|
|
1
|
+
param(
|
|
2
|
+
[switch]$Local,
|
|
3
|
+
[string]$PackageName = "shrinker-ai",
|
|
4
|
+
[string]$Registry = "https://registry.npmjs.org",
|
|
5
|
+
[string]$Version,
|
|
6
|
+
[switch]$SkipNpmInstall,
|
|
7
|
+
[switch]$SkipBuild,
|
|
8
|
+
[switch]$SkipLink,
|
|
9
|
+
[switch]$EnableProfileRouting,
|
|
10
|
+
[switch]$SkipProfile,
|
|
11
|
+
[switch]$SkipAgentRules,
|
|
12
|
+
[switch]$CopilotOnly,
|
|
13
|
+
[switch]$ClaudeOnly,
|
|
14
|
+
[string]$ProfilePath = $PROFILE
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
$ErrorActionPreference = "Stop"
|
|
18
|
+
$InstallStep = 0
|
|
19
|
+
|
|
20
|
+
function Write-InstallStep {
|
|
21
|
+
param([string]$Emoji, [string]$Message)
|
|
22
|
+
$script:InstallStep++
|
|
23
|
+
Write-Host "$($script:InstallStep). $Emoji $Message"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if ($EnableProfileRouting -and $SkipProfile) { throw "Use either -EnableProfileRouting or -SkipProfile, not both." }
|
|
27
|
+
if ($CopilotOnly -and $ClaudeOnly) { throw "Use either -CopilotOnly or -ClaudeOnly, not both." }
|
|
28
|
+
|
|
29
|
+
function Set-AgentRules {
|
|
30
|
+
param([string]$Body)
|
|
31
|
+
$start = "<!-- shrinker agent rules start -->"
|
|
32
|
+
$end = "<!-- shrinker agent rules end -->"
|
|
33
|
+
foreach ($target in @((Join-Path $HOME ".copilot\copilot-instructions.md"), (Join-Path $HOME ".claude\CLAUDE.md"))) {
|
|
34
|
+
if ($ClaudeOnly -and $target.EndsWith(".copilot-instructions.md")) { continue }
|
|
35
|
+
if ($CopilotOnly -and $target.EndsWith("CLAUDE.md")) { continue }
|
|
36
|
+
New-Item -ItemType Directory -Path (Split-Path $target) -Force | Out-Null
|
|
37
|
+
$content = if (Test-Path $target) { Get-Content $target -Raw } else { "" }
|
|
38
|
+
$block = "$start`n$Body`n$end"
|
|
39
|
+
if ($null -ne $content -and $content.Contains($start) -and $content.Contains($end)) {
|
|
40
|
+
$content = [regex]::Replace($content, [regex]::Escape($start) + ".*?" + [regex]::Escape($end), $block, 'Singleline')
|
|
41
|
+
Set-Content $target $content
|
|
42
|
+
} elseif ($content) { Add-Content $target "`n$block`n" }
|
|
43
|
+
else { Set-Content $target "$block`n" }
|
|
44
|
+
Write-InstallStep "📄" "Installed managed rules in: $target"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function Add-ProfileIntegration {
|
|
49
|
+
param([string]$ProfileFile, [string]$IntegrationFile)
|
|
50
|
+
if (-not (Test-Path $ProfileFile)) { New-Item -ItemType File -Path $ProfileFile -Force | Out-Null }
|
|
51
|
+
$start = "# >>> shrinker integration >>>"
|
|
52
|
+
if ((Get-Content $ProfileFile -Raw).Contains($start)) { return }
|
|
53
|
+
Add-Content $ProfileFile "`n$start`n. `"$IntegrationFile`"`n# <<< shrinker integration <<<`n"
|
|
54
|
+
Write-InstallStep "🔧" "Added shrinker integration block to profile: $ProfileFile"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (-not $Local) {
|
|
58
|
+
$packageSpec = if ($Version) { "$PackageName@$Version" } else { $PackageName }
|
|
59
|
+
Write-InstallStep "📦" "Installing $packageSpec from $Registry..."
|
|
60
|
+
& npm install --silent --global $packageSpec "--registry=$Registry"
|
|
61
|
+
if ($LASTEXITCODE -ne 0) { throw "npm package installation failed." }
|
|
62
|
+
Write-InstallStep "🔎" "Locating the installed package..."
|
|
63
|
+
$globalRoot = (& npm root --global).Trim()
|
|
64
|
+
if (-not $globalRoot) { throw "Could not determine the global npm package directory." }
|
|
65
|
+
$packageRoot = Join-Path $globalRoot ($PackageName -replace '/', '\')
|
|
66
|
+
$entrypoint = Join-Path $packageRoot "integrations\windows\install.ps1"
|
|
67
|
+
if (-not (Test-Path $entrypoint)) { throw "Installed package installer not found: $entrypoint" }
|
|
68
|
+
Write-InstallStep "⚙️" "Configuring the installed package..."
|
|
69
|
+
& pwsh -ExecutionPolicy Bypass -File $entrypoint -Local -SkipNpmInstall -SkipBuild -SkipLink -EnableProfileRouting:$EnableProfileRouting -SkipProfile:$SkipProfile -SkipAgentRules:$SkipAgentRules -CopilotOnly:$CopilotOnly -ClaudeOnly:$ClaudeOnly -ProfilePath $ProfilePath
|
|
70
|
+
if ($LASTEXITCODE -ne 0) { throw "Repository installer failed with exit code $LASTEXITCODE" }
|
|
71
|
+
Write-InstallStep "✅" "Install complete."
|
|
72
|
+
exit 0
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
$scriptDir = $PSScriptRoot
|
|
76
|
+
$repoRoot = (Resolve-Path (Join-Path $scriptDir "..\..")).Path
|
|
77
|
+
$templatePath = Join-Path $repoRoot "templates\agent-rules.md"
|
|
78
|
+
$integrationPath = (Resolve-Path (Join-Path $scriptDir "shrinker-profile.ps1")).Path
|
|
79
|
+
if (-not $SkipAgentRules) {
|
|
80
|
+
if (-not (Test-Path $templatePath)) { throw "Agent rules template not found: $templatePath" }
|
|
81
|
+
$rulesBody = Get-Content $templatePath -Raw
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
$versionText = & node -v 2>$null
|
|
85
|
+
if (-not $versionText) { throw "Node.js was not found on PATH. Install Node.js 22.13+ first." }
|
|
86
|
+
$version = [version]($versionText.TrimStart('v'))
|
|
87
|
+
if ($version -lt [version]'22.13.0') { throw "Node.js 22.13+ is required. Found $versionText" }
|
|
88
|
+
|
|
89
|
+
Push-Location $repoRoot
|
|
90
|
+
try {
|
|
91
|
+
Write-InstallStep "📦" "Installing shrinker from: $repoRoot"
|
|
92
|
+
if (-not $SkipNpmInstall) {
|
|
93
|
+
Write-InstallStep "📥" "Installing dependencies..."
|
|
94
|
+
& npm install --silent
|
|
95
|
+
if ($LASTEXITCODE -ne 0) { throw "npm install failed." }
|
|
96
|
+
}
|
|
97
|
+
if (-not $SkipBuild) {
|
|
98
|
+
Write-InstallStep "🏗️" "Building shrinker..."
|
|
99
|
+
& npm run build --silent
|
|
100
|
+
if ($LASTEXITCODE -ne 0) { throw "npm run build failed." }
|
|
101
|
+
}
|
|
102
|
+
if (-not $SkipLink) {
|
|
103
|
+
Write-InstallStep "🔗" "Linking shrinker globally..."
|
|
104
|
+
& npm link --silent
|
|
105
|
+
if ($LASTEXITCODE -ne 0) { throw "npm link failed." }
|
|
106
|
+
}
|
|
107
|
+
} finally { Pop-Location }
|
|
108
|
+
|
|
109
|
+
if (-not $SkipProfile -and $EnableProfileRouting) { Add-ProfileIntegration $ProfilePath $integrationPath }
|
|
110
|
+
if (-not $SkipAgentRules) { Set-AgentRules $rulesBody }
|
|
111
|
+
Write-InstallStep "✅" "Install complete."
|
|
112
|
+
Write-Host " "
|
|
113
|
+
Write-Host "💡 Try: shrinker help"
|
|
@@ -1,136 +1,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
|
-
function Invoke-ShrinkOrNative {
|
|
99
|
-
param(
|
|
100
|
-
[string]$CommandName,
|
|
101
|
-
[object[]]$Arguments
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
$routeToShrink = Use-ShrinkRouting -CommandName $CommandName -Arguments $Arguments
|
|
105
|
-
$shrinkCommand = Get-ShrinkCommandPath
|
|
106
|
-
|
|
107
|
-
if ($routeToShrink -and $shrinkCommand) {
|
|
108
|
-
& $shrinkCommand $CommandName @Arguments
|
|
109
|
-
return
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
$nativePath = $global:ShrinkNativePaths[$CommandName]
|
|
113
|
-
if ($nativePath) {
|
|
114
|
-
& $nativePath @Arguments
|
|
115
|
-
return
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
switch ($CommandName) {
|
|
119
|
-
"cat" { Microsoft.PowerShell.Management\Get-Content @Arguments; return }
|
|
120
|
-
"ls" { Microsoft.PowerShell.Management\Get-ChildItem @Arguments; return }
|
|
121
|
-
"dir" { Microsoft.PowerShell.Management\Get-ChildItem @Arguments; return }
|
|
122
|
-
default { throw "No native fallback found for '$CommandName'." }
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function global:git { Invoke-ShrinkOrNative -CommandName "git" -Arguments $args }
|
|
127
|
-
function global:npm { Invoke-ShrinkOrNative -CommandName "npm" -Arguments $args }
|
|
128
|
-
function global:docker { Invoke-ShrinkOrNative -CommandName "docker" -Arguments $args }
|
|
129
|
-
function global:kubectl { Invoke-ShrinkOrNative -CommandName "kubectl" -Arguments $args }
|
|
130
|
-
function global:gh { Invoke-ShrinkOrNative -CommandName "gh" -Arguments $args }
|
|
131
|
-
function global:rg { Invoke-ShrinkOrNative -CommandName "rg" -Arguments $args }
|
|
132
|
-
function global:find { Invoke-ShrinkOrNative -CommandName "find" -Arguments $args }
|
|
133
|
-
function global:tail { Invoke-ShrinkOrNative -CommandName "tail" -Arguments $args }
|
|
134
|
-
function global:cat { Invoke-ShrinkOrNative -CommandName "cat" -Arguments $args }
|
|
135
|
-
function global:ls { Invoke-ShrinkOrNative -CommandName "ls" -Arguments $args }
|
|
136
|
-
function global:dir { Invoke-ShrinkOrNative -CommandName "dir" -Arguments $args }
|
|
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
|
+
function Invoke-ShrinkOrNative {
|
|
99
|
+
param(
|
|
100
|
+
[string]$CommandName,
|
|
101
|
+
[object[]]$Arguments
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
$routeToShrink = Use-ShrinkRouting -CommandName $CommandName -Arguments $Arguments
|
|
105
|
+
$shrinkCommand = Get-ShrinkCommandPath
|
|
106
|
+
|
|
107
|
+
if ($routeToShrink -and $shrinkCommand) {
|
|
108
|
+
& $shrinkCommand $CommandName @Arguments
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
$nativePath = $global:ShrinkNativePaths[$CommandName]
|
|
113
|
+
if ($nativePath) {
|
|
114
|
+
& $nativePath @Arguments
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
switch ($CommandName) {
|
|
119
|
+
"cat" { Microsoft.PowerShell.Management\Get-Content @Arguments; return }
|
|
120
|
+
"ls" { Microsoft.PowerShell.Management\Get-ChildItem @Arguments; return }
|
|
121
|
+
"dir" { Microsoft.PowerShell.Management\Get-ChildItem @Arguments; return }
|
|
122
|
+
default { throw "No native fallback found for '$CommandName'." }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function global:git { Invoke-ShrinkOrNative -CommandName "git" -Arguments $args }
|
|
127
|
+
function global:npm { Invoke-ShrinkOrNative -CommandName "npm" -Arguments $args }
|
|
128
|
+
function global:docker { Invoke-ShrinkOrNative -CommandName "docker" -Arguments $args }
|
|
129
|
+
function global:kubectl { Invoke-ShrinkOrNative -CommandName "kubectl" -Arguments $args }
|
|
130
|
+
function global:gh { Invoke-ShrinkOrNative -CommandName "gh" -Arguments $args }
|
|
131
|
+
function global:rg { Invoke-ShrinkOrNative -CommandName "rg" -Arguments $args }
|
|
132
|
+
function global:find { Invoke-ShrinkOrNative -CommandName "find" -Arguments $args }
|
|
133
|
+
function global:tail { Invoke-ShrinkOrNative -CommandName "tail" -Arguments $args }
|
|
134
|
+
function global:cat { Invoke-ShrinkOrNative -CommandName "cat" -Arguments $args }
|
|
135
|
+
function global:ls { Invoke-ShrinkOrNative -CommandName "ls" -Arguments $args }
|
|
136
|
+
function global:dir { Invoke-ShrinkOrNative -CommandName "dir" -Arguments $args }
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
param(
|
|
2
|
-
[switch]$SkipUnlink,
|
|
3
|
-
[switch]$SkipAgentRules,
|
|
4
|
-
[switch]$CopilotOnly,
|
|
5
|
-
[switch]$ClaudeOnly,
|
|
6
|
-
[string]$ProfilePath = $PROFILE
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
$ErrorActionPreference = "Stop"
|
|
10
|
-
$UninstallStep = 0
|
|
11
|
-
|
|
12
|
-
function Write-UninstallStep {
|
|
13
|
-
param([string]$Emoji, [string]$Message)
|
|
14
|
-
$script:UninstallStep++
|
|
15
|
-
Write-Host "$($script:UninstallStep). $Emoji $Message"
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function Remove-AgentRules {
|
|
19
|
-
param([string]$Path)
|
|
20
|
-
if (-not (Test-Path $Path)) { return }
|
|
21
|
-
$content = Get-Content $Path -Raw
|
|
22
|
-
$start = "<!-- shrinker agent rules start -->"
|
|
23
|
-
$end = "<!-- shrinker agent rules end -->"
|
|
24
|
-
if ($null -ne $content -and $content.Contains($start) -and $content.Contains($end)) {
|
|
25
|
-
$pattern = [regex]::Escape($start) + ".*?" + [regex]::Escape($end)
|
|
26
|
-
Set-Content $Path ([regex]::Replace($content, $pattern, "", 'Singleline').TrimEnd() + "`n")
|
|
27
|
-
Write-UninstallStep "📄" "Removed managed rules from: $Path"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function Remove-ProfileIntegration {
|
|
32
|
-
param([string]$ProfileFile)
|
|
33
|
-
if (-not (Test-Path $ProfileFile)) { return }
|
|
34
|
-
$content = Get-Content $ProfileFile -Raw
|
|
35
|
-
if ($null -eq $content) { return }
|
|
36
|
-
$start = $content.IndexOf("# >>> shrinker integration >>>")
|
|
37
|
-
$endMarker = "# <<< shrinker integration <<<"
|
|
38
|
-
if ($start -ge 0) {
|
|
39
|
-
$end = $content.IndexOf($endMarker, $start)
|
|
40
|
-
if ($end -ge 0) { Set-Content $ProfileFile $content.Remove($start, $end + $endMarker.Length - $start) }
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (-not $SkipUnlink) {
|
|
45
|
-
Write-UninstallStep "🔗" "Unlinking shrinker globally..."
|
|
46
|
-
& npm unlink --silent --global shrinker-ai
|
|
47
|
-
if ($LASTEXITCODE -ne 0) { throw "npm unlink failed." }
|
|
48
|
-
}
|
|
49
|
-
else { Write-UninstallStep "⏭️" "Skipped global npm unlink." }
|
|
50
|
-
Write-UninstallStep "🔧" "Removing PowerShell profile integration..."
|
|
51
|
-
Remove-ProfileIntegration $ProfilePath
|
|
52
|
-
if (-not $SkipAgentRules) {
|
|
53
|
-
if (-not $ClaudeOnly) { Remove-AgentRules (Join-Path $HOME ".copilot\copilot-instructions.md") }
|
|
54
|
-
if (-not $CopilotOnly) { Remove-AgentRules (Join-Path $HOME ".claude\CLAUDE.md") }
|
|
55
|
-
}
|
|
56
|
-
else { Write-UninstallStep "⏭️" "Skipped managed agent rules." }
|
|
57
|
-
Write-UninstallStep "✅" "Uninstall complete."
|
|
1
|
+
param(
|
|
2
|
+
[switch]$SkipUnlink,
|
|
3
|
+
[switch]$SkipAgentRules,
|
|
4
|
+
[switch]$CopilotOnly,
|
|
5
|
+
[switch]$ClaudeOnly,
|
|
6
|
+
[string]$ProfilePath = $PROFILE
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
$ErrorActionPreference = "Stop"
|
|
10
|
+
$UninstallStep = 0
|
|
11
|
+
|
|
12
|
+
function Write-UninstallStep {
|
|
13
|
+
param([string]$Emoji, [string]$Message)
|
|
14
|
+
$script:UninstallStep++
|
|
15
|
+
Write-Host "$($script:UninstallStep). $Emoji $Message"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function Remove-AgentRules {
|
|
19
|
+
param([string]$Path)
|
|
20
|
+
if (-not (Test-Path $Path)) { return }
|
|
21
|
+
$content = Get-Content $Path -Raw
|
|
22
|
+
$start = "<!-- shrinker agent rules start -->"
|
|
23
|
+
$end = "<!-- shrinker agent rules end -->"
|
|
24
|
+
if ($null -ne $content -and $content.Contains($start) -and $content.Contains($end)) {
|
|
25
|
+
$pattern = [regex]::Escape($start) + ".*?" + [regex]::Escape($end)
|
|
26
|
+
Set-Content $Path ([regex]::Replace($content, $pattern, "", 'Singleline').TrimEnd() + "`n")
|
|
27
|
+
Write-UninstallStep "📄" "Removed managed rules from: $Path"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function Remove-ProfileIntegration {
|
|
32
|
+
param([string]$ProfileFile)
|
|
33
|
+
if (-not (Test-Path $ProfileFile)) { return }
|
|
34
|
+
$content = Get-Content $ProfileFile -Raw
|
|
35
|
+
if ($null -eq $content) { return }
|
|
36
|
+
$start = $content.IndexOf("# >>> shrinker integration >>>")
|
|
37
|
+
$endMarker = "# <<< shrinker integration <<<"
|
|
38
|
+
if ($start -ge 0) {
|
|
39
|
+
$end = $content.IndexOf($endMarker, $start)
|
|
40
|
+
if ($end -ge 0) { Set-Content $ProfileFile $content.Remove($start, $end + $endMarker.Length - $start) }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (-not $SkipUnlink) {
|
|
45
|
+
Write-UninstallStep "🔗" "Unlinking shrinker globally..."
|
|
46
|
+
& npm unlink --silent --global shrinker-ai
|
|
47
|
+
if ($LASTEXITCODE -ne 0) { throw "npm unlink failed." }
|
|
48
|
+
}
|
|
49
|
+
else { Write-UninstallStep "⏭️" "Skipped global npm unlink." }
|
|
50
|
+
Write-UninstallStep "🔧" "Removing PowerShell profile integration..."
|
|
51
|
+
Remove-ProfileIntegration $ProfilePath
|
|
52
|
+
if (-not $SkipAgentRules) {
|
|
53
|
+
if (-not $ClaudeOnly) { Remove-AgentRules (Join-Path $HOME ".copilot\copilot-instructions.md") }
|
|
54
|
+
if (-not $CopilotOnly) { Remove-AgentRules (Join-Path $HOME ".claude\CLAUDE.md") }
|
|
55
|
+
}
|
|
56
|
+
else { Write-UninstallStep "⏭️" "Skipped managed agent rules." }
|
|
57
|
+
Write-UninstallStep "✅" "Uninstall complete."
|