1
2
3
4
5
6
7
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env bash
# basemind PostToolUse output compressor.
#
# Fires after Bash tool calls (matcher "Bash" in hooks.json) and pipes the
# command output through `basemind compress-output`, replacing the result the
# agent sees with a compact summary. The compressor is fail-open and
# credential-preserving: it returns the RAW input unchanged on any failure,
# on detected credentials, or when it would save <10%.
#
# OPT-IN, default OFF — gated on BASEMIND_COMPRESS_OUTPUT, mirroring the
# BASEMIND_GUARD convention used by the pre-tool-guard hook:
#
# unset / off / 0 (default) — no-op passthrough; the original output stands.
# 1 / on / true — active; compress Bash output before the agent sees it.
#
# Safety: this wrapper NEVER errors out the tool call. On any missing
# dependency, unreadable payload, or compressor failure it exits 0 with no
# output, leaving the original tool result untouched (passthrough).
#
# Output protocol: a PostToolUse JSON result on stdout whose
# hookSpecificOutput.updatedToolOutput replaces the tool result string. We only
# emit a replacement when compression actually shrank the output; otherwise we
# stay silent (exit 0) so the original output is preserved verbatim.
# Gate: anything other than an explicit on-value is a fast no-op passthrough.
MODE=""
# Need jq to read the event payload + emit well-formed JSON; degrade to a
# passthrough no-op if absent.
||
input=""
[ ||
tool=""
[ ||
# Pull stdout + stderr from the tool result. Field shape: tool_output.{stdout,
# stderr}. Concatenate (stderr appended) so the compressor sees the full text;
# an all-empty payload is nothing to compress.
combined=""
[ ||
# Resolve the basemind binary the same way the launcher does: explicit override,
# then the managed per-user version cache, then PATH. We avoid triggering a
# network install here (PostToolUse is on the hot path) — if no local binary is
# found, pass through untouched.
SCRIPT_DIR=""
PLUGIN_ROOT=""
BINARY_NAME="basemind"
BIN=""
[ ||
# Run the compressor. It reads stdin, writes the (possibly compressed) text to
# stdout, and a savings note to stderr. It is itself fail-open, but guard the
# wrapper too: any non-zero exit or empty result ⇒ passthrough.
compressed=""
[ ||
# Only replace when compression actually shrank the output. The CLI returns the
# RAW input unchanged when it can't save ≥10% / hits credentials / fails, so an
# equal-or-larger result means "leave it alone" — stay silent (passthrough).
if [; then
fi
# Emit the replacement. updatedToolOutput is a string the agent sees in place of
# the original tool result. suppressOutput hides the hook's own chatter.