shrinker-ai 0.1.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/.copilot-instructions.md +2 -0
- package/CLAUDE.md +2 -0
- package/README.md +295 -0
- package/dist/src/cli.js +265 -0
- package/dist/src/execution/raw-output-store.js +132 -0
- package/dist/src/execution/run-command.js +172 -0
- package/dist/src/filters/cat.js +13 -0
- package/dist/src/filters/docker.js +42 -0
- package/dist/src/filters/find.js +63 -0
- package/dist/src/filters/generic-log.js +38 -0
- package/dist/src/filters/gh.js +38 -0
- package/dist/src/filters/git-diff.js +56 -0
- package/dist/src/filters/git-list.js +25 -0
- package/dist/src/filters/git-log.js +256 -0
- package/dist/src/filters/git-status.js +67 -0
- package/dist/src/filters/kubectl.js +76 -0
- package/dist/src/filters/npm.js +44 -0
- package/dist/src/filters/rg.js +64 -0
- package/dist/src/filters/select-filter.js +119 -0
- package/dist/src/filters/table.js +27 -0
- package/dist/src/filters/tail.js +6 -0
- package/dist/src/filters/test-output.js +54 -0
- package/dist/src/filters/types.js +2 -0
- package/dist/src/formatting/ansi.js +13 -0
- package/dist/src/formatting/limits.js +17 -0
- package/dist/src/metrics/dashboard.js +138 -0
- package/dist/src/metrics/measure.js +29 -0
- package/dist/src/metrics/stats-store.js +166 -0
- package/integrations/install-shrinker.ps1 +166 -0
- package/integrations/install.ps1 +46 -0
- package/integrations/shrinker-profile.ps1 +136 -0
- package/integrations/uninstall-shrinker.ps1 +79 -0
- package/integrations/uninstall.ps1 +35 -0
- package/package.json +31 -0
- package/templates/agent-rules.md +19 -0
|
@@ -0,0 +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 }
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[switch]$SkipUnlink,
|
|
3
|
+
[switch]$SkipAgentRules,
|
|
4
|
+
[switch]$CopilotOnly,
|
|
5
|
+
[switch]$ClaudeOnly,
|
|
6
|
+
[string]$ProfilePath = $PROFILE
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
$ErrorActionPreference = "Stop"
|
|
10
|
+
|
|
11
|
+
if ($CopilotOnly -and $ClaudeOnly) {
|
|
12
|
+
throw "Use either -CopilotOnly or -ClaudeOnly, not both."
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
$blockStart = "<!-- shrinker agent rules start -->"
|
|
16
|
+
$blockEnd = "<!-- shrinker agent rules end -->"
|
|
17
|
+
|
|
18
|
+
function Remove-AgentRules {
|
|
19
|
+
param([string]$Path)
|
|
20
|
+
|
|
21
|
+
if (-not (Test-Path $Path)) { return }
|
|
22
|
+
$content = Get-Content -Path $Path -Raw
|
|
23
|
+
if (-not ($content.Contains($blockStart) -and $content.Contains($blockEnd))) { return }
|
|
24
|
+
$pattern = [regex]::Escape($blockStart) + ".*?" + [regex]::Escape($blockEnd)
|
|
25
|
+
Set-Content -Path $Path -Value ([regex]::Replace($content, $pattern, "", [System.Text.RegularExpressions.RegexOptions]::Singleline).TrimEnd() + "`n")
|
|
26
|
+
Write-Host "Removed managed rules from: $Path"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function Remove-ProfileIntegration {
|
|
30
|
+
param([string]$ProfileFile)
|
|
31
|
+
|
|
32
|
+
if (-not (Test-Path $ProfileFile)) {
|
|
33
|
+
Write-Host "Profile not found: $ProfileFile"
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
$startMarker = "# >>> shrinker integration >>>"
|
|
38
|
+
$endMarker = "# <<< shrinker integration <<<"
|
|
39
|
+
$content = Get-Content -Path $ProfileFile -Raw
|
|
40
|
+
if ($null -eq $content -or $content.Length -eq 0) {
|
|
41
|
+
Write-Host "Profile is empty; nothing to remove."
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
$start = $content.IndexOf($startMarker)
|
|
46
|
+
if ($start -lt 0) {
|
|
47
|
+
Write-Host "No shrinker integration block found in profile."
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
$end = $content.IndexOf($endMarker, $start)
|
|
52
|
+
if ($end -lt 0) {
|
|
53
|
+
Write-Host "Integration block start found but end marker is missing; leaving profile unchanged."
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
$end += $endMarker.Length
|
|
58
|
+
$updated = $content.Remove($start, $end - $start)
|
|
59
|
+
Set-Content -Path $ProfileFile -Value $updated
|
|
60
|
+
Write-Host "Removed shrinker integration block from: $ProfileFile"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (-not $SkipUnlink) {
|
|
64
|
+
Write-Host "Running npm unlink -g shrinker..."
|
|
65
|
+
& npm unlink -g shrinker
|
|
66
|
+
if ($LASTEXITCODE -ne 0) {
|
|
67
|
+
throw "npm unlink failed with exit code $LASTEXITCODE"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Remove-ProfileIntegration -ProfileFile $ProfilePath
|
|
72
|
+
Write-Host "If this terminal previously loaded shrinker-profile.ps1, restart terminal or remove Function:git/Function:rg wrappers from current session."
|
|
73
|
+
|
|
74
|
+
if (-not $SkipAgentRules) {
|
|
75
|
+
if (-not $ClaudeOnly) { Remove-AgentRules -Path (Join-Path (Get-Location).Path ".copilot-instructions.md") }
|
|
76
|
+
if (-not $CopilotOnly) { Remove-AgentRules -Path (Join-Path (Get-Location).Path "CLAUDE.md") }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
Write-Host "Uninstall complete."
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[string]$PackageName = "shrinker-ai",
|
|
3
|
+
[string]$Registry = "https://registry.npmjs.org",
|
|
4
|
+
[switch]$SkipAgentRules,
|
|
5
|
+
[switch]$CopilotOnly,
|
|
6
|
+
[switch]$ClaudeOnly
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
$ErrorActionPreference = "Stop"
|
|
10
|
+
|
|
11
|
+
if ($CopilotOnly -and $ClaudeOnly) {
|
|
12
|
+
throw "Use either -CopilotOnly or -ClaudeOnly, not both."
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
$globalRoot = (& npm root --global).Trim()
|
|
16
|
+
$packageRoot = Join-Path $globalRoot ($PackageName -replace '/', '\')
|
|
17
|
+
$localUninstaller = Join-Path $packageRoot "integrations\uninstall-shrinker.ps1"
|
|
18
|
+
if (Test-Path $localUninstaller) {
|
|
19
|
+
$localArgs = @("-ExecutionPolicy", "Bypass", "-File", $localUninstaller, "-SkipUnlink")
|
|
20
|
+
if ($SkipAgentRules) { $localArgs += "-SkipAgentRules" }
|
|
21
|
+
if ($CopilotOnly) { $localArgs += "-CopilotOnly" }
|
|
22
|
+
if ($ClaudeOnly) { $localArgs += "-ClaudeOnly" }
|
|
23
|
+
& pwsh @localArgs
|
|
24
|
+
if ($LASTEXITCODE -ne 0) {
|
|
25
|
+
throw "Installed package integration failed with exit code $LASTEXITCODE"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
Write-Host "Removing $PackageName..."
|
|
30
|
+
& npm uninstall --global $PackageName "--registry=$Registry"
|
|
31
|
+
if ($LASTEXITCODE -ne 0) {
|
|
32
|
+
throw "npm package removal failed with exit code $LASTEXITCODE"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
Write-Host "Package uninstallation complete."
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "shrinker-ai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local deterministic command-output shrinker for coding agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"shrinker": "./dist/src/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "tsc -w -p tsconfig.json",
|
|
11
|
+
"build": "tsc -p tsconfig.json",
|
|
12
|
+
"pack": "npm run build --silent && npm pack",
|
|
13
|
+
"test": "npm run build --silent && node --test \"dist/tests/*.test.js\"",
|
|
14
|
+
"demo": "npm run build --silent && node dist/demo/run-demo.js",
|
|
15
|
+
"install:local": "pwsh -ExecutionPolicy Bypass -File ./integrations/install-shrinker.ps1",
|
|
16
|
+
"uninstall:local": "pwsh -ExecutionPolicy Bypass -File ./integrations/uninstall-shrinker.ps1",
|
|
17
|
+
"install:package": "pwsh -ExecutionPolicy Bypass -File ./integrations/install.ps1",
|
|
18
|
+
"uninstall:package": "pwsh -ExecutionPolicy Bypass -File ./integrations/uninstall.ps1"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^24.0.0",
|
|
22
|
+
"typescript": "^5.9.0"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22.13"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"registry": "https://registry.npmjs.org"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT"
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Use `shrinker` for high-volume terminal outputs to reduce tokens.
|
|
2
|
+
|
|
3
|
+
Routing policy:
|
|
4
|
+
- `git status|diff|log|show|reflog|branch|tag|stash` -> `shrinker git ...`
|
|
5
|
+
- `npm test|t|install|i|ci|ls|list` -> `shrinker npm ...`
|
|
6
|
+
- `docker ps|logs|images|compose` -> `shrinker docker ...`
|
|
7
|
+
- `kubectl get|describe|logs` -> `shrinker kubectl ...`
|
|
8
|
+
- `gh pr|issue|run` -> `shrinker gh ...`
|
|
9
|
+
- `rg`, `find`, `tail`, `cat`, `ls`, `dir` -> `shrinker <command> ...`
|
|
10
|
+
|
|
11
|
+
Bypass shrinker when:
|
|
12
|
+
- command modifies remote state and output volume is already tiny
|
|
13
|
+
- command requires interactive stdin
|
|
14
|
+
- command explicitly needs `shrinker --raw`
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
- `shrinker git log -n 20`
|
|
18
|
+
- `shrinker rg "pattern" src`
|
|
19
|
+
- `shrinker docker logs api --tail 500`
|