anolisa-tokenless 0.7.7
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/LICENSE +190 -0
- package/README.md +770 -0
- package/adapters/tokenless/claude-code/.claude-plugin/marketplace.json +15 -0
- package/adapters/tokenless/claude-code/.claude-plugin/plugin.json +9 -0
- package/adapters/tokenless/claude-code/hooks/hooks.json +38 -0
- package/adapters/tokenless/claude-code/scripts/detect.sh +193 -0
- package/adapters/tokenless/claude-code/scripts/install.sh +92 -0
- package/adapters/tokenless/claude-code/scripts/uninstall.sh +50 -0
- package/adapters/tokenless/codex/.codex-plugin/plugin.json +20 -0
- package/adapters/tokenless/codex/README.md +160 -0
- package/adapters/tokenless/codex/hooks/hooks.json +52 -0
- package/adapters/tokenless/codex/scripts/_common.sh +25 -0
- package/adapters/tokenless/codex/scripts/check-tokenless +94 -0
- package/adapters/tokenless/codex/scripts/compress-response +441 -0
- package/adapters/tokenless/codex/scripts/detect.sh +61 -0
- package/adapters/tokenless/codex/scripts/install.sh +151 -0
- package/adapters/tokenless/codex/scripts/rewrite-hook +282 -0
- package/adapters/tokenless/codex/scripts/tool-ready +303 -0
- package/adapters/tokenless/codex/scripts/uninstall.sh +78 -0
- package/adapters/tokenless/common/commands/tokenless-stats.toml +2 -0
- package/adapters/tokenless/common/cosh-extension.json +65 -0
- package/adapters/tokenless/common/hooks/compress_response_hook.py +487 -0
- package/adapters/tokenless/common/hooks/compress_schema_hook.py +144 -0
- package/adapters/tokenless/common/hooks/compress_toon_hook.py +160 -0
- package/adapters/tokenless/common/hooks/hook_utils.py +584 -0
- package/adapters/tokenless/common/hooks/rewrite_hook.py +223 -0
- package/adapters/tokenless/common/hooks/run-hook.sh +62 -0
- package/adapters/tokenless/common/hooks/tool_categories.json +99 -0
- package/adapters/tokenless/common/hooks/tool_ready_hook.sh +571 -0
- package/adapters/tokenless/common/tokenless-env-fix.sh +730 -0
- package/adapters/tokenless/common/tool-ready-spec.json +113 -0
- package/adapters/tokenless/dsh/cordis.patch.yml +8 -0
- package/adapters/tokenless/dsh/dist/index.js +399 -0
- package/adapters/tokenless/dsh/package.json +26 -0
- package/adapters/tokenless/hermes/__init__.py +572 -0
- package/adapters/tokenless/hermes/plugin.yaml +9 -0
- package/adapters/tokenless/hermes/scripts/detect.sh +80 -0
- package/adapters/tokenless/hermes/scripts/install.sh +60 -0
- package/adapters/tokenless/hermes/scripts/uninstall.sh +45 -0
- package/adapters/tokenless/manifest.json +147 -0
- package/adapters/tokenless/openclaw/dist/index.d.ts +27 -0
- package/adapters/tokenless/openclaw/dist/index.js +598 -0
- package/adapters/tokenless/openclaw/dist/tool_categories.json +99 -0
- package/adapters/tokenless/openclaw/index.ts +720 -0
- package/adapters/tokenless/openclaw/openclaw.plugin.json +40 -0
- package/adapters/tokenless/openclaw/package.json +24 -0
- package/adapters/tokenless/openclaw/scripts/detect.sh +102 -0
- package/adapters/tokenless/openclaw/scripts/install.sh +75 -0
- package/adapters/tokenless/openclaw/scripts/uninstall.sh +46 -0
- package/adapters/tokenless/openclaw/tsconfig.json +13 -0
- package/adapters/tokenless/opencode/plugin.js +246 -0
- package/adapters/tokenless/opencode/scripts/detect.sh +38 -0
- package/adapters/tokenless/opencode/scripts/install.sh +56 -0
- package/adapters/tokenless/opencode/scripts/uninstall.sh +29 -0
- package/adapters/tokenless/qoder/.qoder-plugin/plugin.json +11 -0
- package/adapters/tokenless/qoder/commands/tokenless-stats.md +8 -0
- package/adapters/tokenless/qoder/hooks/hooks.json +36 -0
- package/adapters/tokenless/qoder/hooks/run-hook.sh +51 -0
- package/adapters/tokenless/qoder/scripts/detect.sh +35 -0
- package/adapters/tokenless/qoder/scripts/install.sh +136 -0
- package/adapters/tokenless/qoder/scripts/uninstall.sh +106 -0
- package/adapters/tokenless/qwencode/qwen-extension.json +69 -0
- package/adapters/tokenless/qwencode/scripts/detect.sh +125 -0
- package/adapters/tokenless/qwencode/scripts/install.sh +128 -0
- package/adapters/tokenless/qwencode/scripts/uninstall.sh +62 -0
- package/bin/rtk +6 -0
- package/bin/tokenless +6 -0
- package/bin/toon +6 -0
- package/package.json +57 -0
- package/scripts/postinstall.js +208 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "anolisa-tokenless",
|
|
3
|
+
"description": "ANOLISA local marketplace — bundles the Token-Less plugin for Claude Code",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "ANOLISA"
|
|
6
|
+
},
|
|
7
|
+
"plugins": [
|
|
8
|
+
{
|
|
9
|
+
"name": "tokenless",
|
|
10
|
+
"source": "./",
|
|
11
|
+
"description": "Token-Less context compression for Claude Code — RTK command rewriting, response/TOON compression, and Tool Ready environment pre-check",
|
|
12
|
+
"category": "productivity"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tokenless",
|
|
3
|
+
"version": "0.7.7",
|
|
4
|
+
"description": "Token-Less context compression for Claude Code — RTK command rewriting, response/TOON compression, and Tool Ready environment pre-check",
|
|
5
|
+
"author": { "name": "ANOLISA" },
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"homepage": "https://github.com/alibaba/anolisa/tree/main/src/tokenless",
|
|
8
|
+
"keywords": ["token-optimization", "rtk", "compression", "toon", "tool-ready"]
|
|
9
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Bash",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "TOKENLESS_AGENT_ID=claude-code bash \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.sh\" rewrite_hook.py",
|
|
10
|
+
"timeout": 10
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"matcher": "",
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": "TOKENLESS_AGENT_ID=claude-code bash \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.sh\" tool_ready_hook.sh",
|
|
20
|
+
"timeout": 15
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"PostToolUse": [
|
|
26
|
+
{
|
|
27
|
+
"matcher": "^(?!(?:Read|Glob|NotebookRead)$).+",
|
|
28
|
+
"hooks": [
|
|
29
|
+
{
|
|
30
|
+
"type": "command",
|
|
31
|
+
"command": "TOKENLESS_AGENT_ID=claude-code bash \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.sh\" compress_response_hook.py",
|
|
32
|
+
"timeout": 15
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# detect.sh — Inspect Claude Code presence and the tokenless plugin state.
|
|
3
|
+
# Read-only. Tri-state exit aligns with openclaw/hermes detect.sh:
|
|
4
|
+
# 0 = installed and ready
|
|
5
|
+
# 1 = not installed but installable (prereqs OK)
|
|
6
|
+
# 2 = missing prerequisites
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
|
|
10
|
+
AGENT="${ANOLISA_TARGET:-claude-code}"
|
|
11
|
+
ADAPTER_DIR="${ANOLISA_ADAPTER_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
|
|
12
|
+
|
|
13
|
+
PLUGIN_ID="${COMPONENT}@anolisa-${COMPONENT}"
|
|
14
|
+
PLUGIN_SRC="$ADAPTER_DIR/claude-code"
|
|
15
|
+
|
|
16
|
+
CLAUDE_BIN="${CLAUDE_BIN:-}"
|
|
17
|
+
export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
|
|
18
|
+
|
|
19
|
+
# First-run settling retries: right after provisioning, the claude binary or
|
|
20
|
+
# the plugin registry may be transiently invisible on the very first detect.sh
|
|
21
|
+
# execution (filesystem/PATH init timing race). settle() only retries checks
|
|
22
|
+
# that report a retryable failure (exit status 1); checks that succeed or
|
|
23
|
+
# report a definitive result return immediately, so steady-state runs stay
|
|
24
|
+
# fast.
|
|
25
|
+
DETECT_RETRIES="${TOKENLESS_DETECT_RETRIES:-3}"
|
|
26
|
+
DETECT_RETRY_DELAY="${TOKENLESS_DETECT_RETRY_DELAY:-1}"
|
|
27
|
+
|
|
28
|
+
# settle <cmd...> — run cmd once; if it reports a retryable failure (exit
|
|
29
|
+
# status 1), sleep DETECT_RETRY_DELAY and retry, up to DETECT_RETRIES retries
|
|
30
|
+
# (that is, at most 1 + DETECT_RETRIES attempts in total). Exit status 0 is
|
|
31
|
+
# success. Any other exit status is a definitive result and is returned
|
|
32
|
+
# immediately without further retries. Returns the final exit status.
|
|
33
|
+
# Callers must therefore reserve exit status 1 for conditions that a retry
|
|
34
|
+
# may still resolve.
|
|
35
|
+
settle() {
|
|
36
|
+
local retry=0 rc=0
|
|
37
|
+
"$@"; rc=$?
|
|
38
|
+
while [ "$rc" -eq 1 ] && [ "$retry" -lt "$DETECT_RETRIES" ]; do
|
|
39
|
+
retry=$((retry + 1))
|
|
40
|
+
sleep "$DETECT_RETRY_DELAY"
|
|
41
|
+
"$@"; rc=$?
|
|
42
|
+
done
|
|
43
|
+
return "$rc"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
line() { printf '[%s] %s\n' "$COMPONENT" "$*"; }
|
|
47
|
+
field() { printf '[%s] %-26s %s\n' "$COMPONENT" "$1" "$2"; }
|
|
48
|
+
|
|
49
|
+
PREREQ_MISSING=()
|
|
50
|
+
INSTALL_MISSING=()
|
|
51
|
+
note_prereq_missing() { PREREQ_MISSING+=("$1"); }
|
|
52
|
+
note_install_missing() { INSTALL_MISSING+=("$1"); }
|
|
53
|
+
|
|
54
|
+
find_claude_bin() {
|
|
55
|
+
CLAUDE_BIN="$(command -v claude 2>/dev/null || true)"
|
|
56
|
+
[ -n "$CLAUDE_BIN" ]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if [ -z "$CLAUDE_BIN" ]; then
|
|
60
|
+
# PATH/filesystem may still be settling right after install; re-check
|
|
61
|
+
# briefly before declaring the CLI missing. A genuinely absent CLI cannot
|
|
62
|
+
# be distinguished from that settling race, so it spends one retry budget
|
|
63
|
+
# (set TOKENLESS_DETECT_RETRIES=0 to opt out).
|
|
64
|
+
settle find_claude_bin || true
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
line "${AGENT} detect"
|
|
68
|
+
if [ -n "$CLAUDE_BIN" ] && [ -x "$CLAUDE_BIN" ]; then
|
|
69
|
+
CLAUDE_VER="$("$CLAUDE_BIN" --version 2>/dev/null | awk '{print $1}' || echo unknown)"
|
|
70
|
+
field "claude CLI" "present (${CLAUDE_BIN}, v${CLAUDE_VER})"
|
|
71
|
+
else
|
|
72
|
+
field "claude CLI" "missing"
|
|
73
|
+
note_prereq_missing "claude CLI"
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Informational only: claude creates ~/.claude on first run; absence is
|
|
77
|
+
# not a prerequisite failure. Check once, without settling retries: nothing in
|
|
78
|
+
# this script creates the directory before `claude plugin list` runs, and that
|
|
79
|
+
# call carries its own retry budget and initializes ~/.claude itself, so
|
|
80
|
+
# retrying this probe would only delay the report.
|
|
81
|
+
if [ -d "$HOME/.claude" ]; then
|
|
82
|
+
field "claude config dir" "present ($HOME/.claude)"
|
|
83
|
+
else
|
|
84
|
+
field "claude config dir" "missing (created on first claude run)"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
if [ -f "$PLUGIN_SRC/.claude-plugin/marketplace.json" ]; then
|
|
88
|
+
field "marketplace.json" "present"
|
|
89
|
+
else
|
|
90
|
+
field "marketplace.json" "missing"
|
|
91
|
+
note_prereq_missing "marketplace.json"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
if [ -f "$PLUGIN_SRC/.claude-plugin/plugin.json" ]; then
|
|
95
|
+
field "plugin.json" "present"
|
|
96
|
+
else
|
|
97
|
+
field "plugin.json" "missing (run: make stamp-adapter-templates)"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# claude_plugin_listed — probe the plugin registry, using the settle()
|
|
101
|
+
# exit-status contract: 0 = plugin listed; 1 = `claude plugin list` itself
|
|
102
|
+
# failed (the CLI may still be initializing ~/.claude on first run, so a
|
|
103
|
+
# retry may still succeed); 2 = `plugin list` ran successfully but did not
|
|
104
|
+
# list the plugin. That is a definitive absent result — no retry can change
|
|
105
|
+
# it — so settle() must return immediately instead of sleeping out the
|
|
106
|
+
# retry budget and invoking the CLI DETECT_RETRIES + 1 times.
|
|
107
|
+
claude_plugin_listed() {
|
|
108
|
+
local listing
|
|
109
|
+
if ! listing="$("$CLAUDE_BIN" plugin list 2>&1)"; then
|
|
110
|
+
return 1
|
|
111
|
+
fi
|
|
112
|
+
if printf '%s\n' "$listing" | grep -qF "$PLUGIN_ID"; then
|
|
113
|
+
return 0
|
|
114
|
+
fi
|
|
115
|
+
return 2
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if [ -n "$CLAUDE_BIN" ] && [ -x "$CLAUDE_BIN" ]; then
|
|
119
|
+
# First-run race: `claude plugin list` may transiently fail while the CLI
|
|
120
|
+
# initializes ~/.claude; settle() retries while that failure (status 1)
|
|
121
|
+
# persists. A successful list that simply omits the plugin is definitive
|
|
122
|
+
# (status 2) and is reported as "not installed" without further retries.
|
|
123
|
+
if settle claude_plugin_listed; then
|
|
124
|
+
field "plugin install" "installed ($PLUGIN_ID)"
|
|
125
|
+
else
|
|
126
|
+
field "plugin install" "not installed"
|
|
127
|
+
note_install_missing "$PLUGIN_ID"
|
|
128
|
+
fi
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
if [ -f "$PLUGIN_SRC/hooks/run-hook.sh" ]; then
|
|
132
|
+
field "hook dispatcher" "present"
|
|
133
|
+
else
|
|
134
|
+
field "hook dispatcher" "missing (hooks/run-hook.sh)"
|
|
135
|
+
note_prereq_missing "hook dispatcher"
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
if command -v python3 &>/dev/null; then
|
|
139
|
+
field "python3" "present ($(command -v python3))"
|
|
140
|
+
else
|
|
141
|
+
field "python3" "missing"
|
|
142
|
+
note_prereq_missing "python3"
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
# jq is required by tool_ready_hook.sh; absence disables that hook only
|
|
146
|
+
# (rewrite + compress-response still work). Treat as informational.
|
|
147
|
+
if command -v jq &>/dev/null; then
|
|
148
|
+
field "jq" "present ($(command -v jq))"
|
|
149
|
+
else
|
|
150
|
+
field "jq" "missing (tool-ready hook disabled)"
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
runtime_bin="$(command -v tokenless 2>/dev/null || true)"
|
|
154
|
+
if [ -n "$runtime_bin" ]; then
|
|
155
|
+
field "tokenless binary" "present (${runtime_bin})"
|
|
156
|
+
else
|
|
157
|
+
field "tokenless binary" "missing"
|
|
158
|
+
note_prereq_missing "tokenless binary"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
rtk_bin="$(command -v rtk 2>/dev/null || true)"
|
|
162
|
+
if [ -n "$rtk_bin" ]; then
|
|
163
|
+
field "rtk binary" "present (${rtk_bin})"
|
|
164
|
+
else
|
|
165
|
+
field "rtk binary" "missing"
|
|
166
|
+
note_prereq_missing "rtk binary"
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# Shared hook scripts live under FHS; warn when missing so user knows to run
|
|
170
|
+
# `make install` (or install the RPM) before adapter actually fires.
|
|
171
|
+
SHARED_HOOKS_DIR=""
|
|
172
|
+
for d in /usr/local/share/anolisa/adapters/tokenless/common/hooks \
|
|
173
|
+
/usr/share/anolisa/adapters/tokenless/common/hooks \
|
|
174
|
+
"$HOME/.local/share/anolisa/adapters/tokenless/common/hooks"; do
|
|
175
|
+
if [ -d "$d" ]; then SHARED_HOOKS_DIR="$d"; break; fi
|
|
176
|
+
done
|
|
177
|
+
if [ -n "$SHARED_HOOKS_DIR" ]; then
|
|
178
|
+
field "shared hooks dir" "present ($SHARED_HOOKS_DIR)"
|
|
179
|
+
else
|
|
180
|
+
field "shared hooks dir" "missing (run: make -C src/tokenless install)"
|
|
181
|
+
note_prereq_missing "shared hooks dir"
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
if [ ${#PREREQ_MISSING[@]} -gt 0 ]; then
|
|
185
|
+
line "${AGENT}: missing prerequisites (${PREREQ_MISSING[*]})"
|
|
186
|
+
exit 2
|
|
187
|
+
fi
|
|
188
|
+
if [ ${#INSTALL_MISSING[@]} -gt 0 ]; then
|
|
189
|
+
line "${AGENT}: not installed (ready to install)"
|
|
190
|
+
exit 1
|
|
191
|
+
fi
|
|
192
|
+
line "${AGENT}: ready"
|
|
193
|
+
exit 0
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install.sh — Register tokenless as a local Claude Code marketplace and
|
|
3
|
+
# install the plugin via the official `claude plugin` CLI.
|
|
4
|
+
#
|
|
5
|
+
# Responsibility boundary (mirrors openclaw/scripts/install.sh):
|
|
6
|
+
# - This script ONLY deploys an already-stamped plugin manifest.
|
|
7
|
+
# - Manifest stamping (plugin.json.in -> plugin.json) is the Makefile's job:
|
|
8
|
+
# make -C src/tokenless stamp-adapter-templates
|
|
9
|
+
# which `make install` runs automatically before `install-adapter-resources`
|
|
10
|
+
# copies the result into $SHARE_DIR/claude-code.
|
|
11
|
+
# - A dev-only fallback stamps the manifest in place when called outside the
|
|
12
|
+
# RPM/Makefile flow, so adapter-install works on a freshly checked-out tree.
|
|
13
|
+
#
|
|
14
|
+
# Claude Code v2 requires plugins to be sourced from a registered marketplace.
|
|
15
|
+
# We expose the adapter's claude-code/ directory itself as a single-plugin
|
|
16
|
+
# marketplace ("anolisa-tokenless"), then install tokenless@anolisa-tokenless
|
|
17
|
+
# from it. The marketplace name is component-scoped so multiple ANOLISA
|
|
18
|
+
# components can each register their own without colliding.
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
AGENT="${ANOLISA_TARGET:-claude-code}"
|
|
22
|
+
COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
|
|
23
|
+
ADAPTER_DIR="${ANOLISA_ADAPTER_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
|
|
24
|
+
|
|
25
|
+
PLUGIN_SRC="$ADAPTER_DIR/claude-code"
|
|
26
|
+
MARKETPLACE_NAME="anolisa-${COMPONENT}"
|
|
27
|
+
PLUGIN_ID="${COMPONENT}@${MARKETPLACE_NAME}"
|
|
28
|
+
|
|
29
|
+
CLAUDE_BIN="${CLAUDE_BIN:-claude}"
|
|
30
|
+
export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
|
|
31
|
+
|
|
32
|
+
echo "[${COMPONENT}] Installing ${AGENT} plugin..."
|
|
33
|
+
|
|
34
|
+
if ! command -v "$CLAUDE_BIN" &>/dev/null; then
|
|
35
|
+
echo "[${COMPONENT}] claude CLI not found (CLAUDE_BIN=${CLAUDE_BIN}) — skipping plugin installation."
|
|
36
|
+
echo "[${COMPONENT}] Install Claude Code first, then run this script again."
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
if [ ! -d "$PLUGIN_SRC" ]; then
|
|
41
|
+
echo "[${COMPONENT}] Plugin source not found: $PLUGIN_SRC" >&2
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [ ! -f "$PLUGIN_SRC/.claude-plugin/marketplace.json" ]; then
|
|
46
|
+
echo "[${COMPONENT}] ERROR: $PLUGIN_SRC/.claude-plugin/marketplace.json missing." >&2
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Dev-only fallback: stamp plugin.json from .in template when Makefile hasn't
|
|
51
|
+
# run yet. Production installs (RPM, `make install`) ship a stamped manifest
|
|
52
|
+
# inside SHARE_DIR, so this branch is a no-op in those flows.
|
|
53
|
+
PLUGIN_MANIFEST="$PLUGIN_SRC/.claude-plugin/plugin.json"
|
|
54
|
+
PLUGIN_TEMPLATE="$PLUGIN_SRC/.claude-plugin/plugin.json.in"
|
|
55
|
+
if [ ! -f "$PLUGIN_MANIFEST" ] && [ -f "$PLUGIN_TEMPLATE" ]; then
|
|
56
|
+
VERSION="${TOKENLESS_VERSION:-${ANOLISA_VERSION:-0.0.0-dev}}"
|
|
57
|
+
sed "s/@VERSION@/${VERSION}/g" "$PLUGIN_TEMPLATE" > "$PLUGIN_MANIFEST"
|
|
58
|
+
echo "[${COMPONENT}] dev-fallback: stamped plugin.json (version=${VERSION}) — production builds should stamp via Makefile"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
if [ ! -f "$PLUGIN_MANIFEST" ] ; then
|
|
62
|
+
echo "[${COMPONENT}] ERROR: $PLUGIN_MANIFEST missing." >&2
|
|
63
|
+
echo "[${COMPONENT}] Stamp the manifest first:" >&2
|
|
64
|
+
echo "[${COMPONENT}] make -C src/tokenless stamp-adapter-templates" >&2
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Validate the stamped plugin via the official CLI — same gate the install
|
|
69
|
+
# call would hit; surfacing the error here gives a cleaner failure message.
|
|
70
|
+
"$CLAUDE_BIN" plugin validate "$PLUGIN_SRC" >/dev/null \
|
|
71
|
+
|| { echo "[${COMPONENT}] plugin validation failed" >&2; exit 1; }
|
|
72
|
+
|
|
73
|
+
# Idempotent marketplace add. We probe `marketplace list` first so that a
|
|
74
|
+
# `marketplace add` failure surfaces its real cause (bad path, malformed
|
|
75
|
+
# manifest, settings.json write error) instead of being swallowed as
|
|
76
|
+
# "already registered".
|
|
77
|
+
echo "[${COMPONENT}] registering marketplace '${MARKETPLACE_NAME}' from ${PLUGIN_SRC}..."
|
|
78
|
+
if "$CLAUDE_BIN" plugin marketplace list 2>/dev/null \
|
|
79
|
+
| grep -qE "(^|[[:space:]])${MARKETPLACE_NAME}([[:space:]]|$)"; then
|
|
80
|
+
echo "[${COMPONENT}] marketplace '${MARKETPLACE_NAME}' already registered"
|
|
81
|
+
else
|
|
82
|
+
"$CLAUDE_BIN" plugin marketplace add "$PLUGIN_SRC" \
|
|
83
|
+
|| { echo "[${COMPONENT}] marketplace add failed" >&2; exit 1; }
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Idempotent plugin install.
|
|
87
|
+
echo "[${COMPONENT}] installing ${PLUGIN_ID}..."
|
|
88
|
+
"$CLAUDE_BIN" plugin install "$PLUGIN_ID" 2>&1 \
|
|
89
|
+
|| { echo "[${COMPONENT}] plugin install failed" >&2; exit 1; }
|
|
90
|
+
|
|
91
|
+
echo "[${COMPONENT}] ${AGENT} plugin installed via claude CLI."
|
|
92
|
+
echo "[${COMPONENT}] Restart claude (or run /plugin) to activate."
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# uninstall.sh — Remove the tokenless plugin and the anolisa marketplace via
|
|
3
|
+
# the official `claude plugin` CLI. Falls back to manual cleanup of the
|
|
4
|
+
# settings.json + plugin cache when claude is unavailable (e.g. RPM %preun
|
|
5
|
+
# running after the user uninstalled claude themselves).
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
AGENT="${ANOLISA_TARGET:-claude-code}"
|
|
9
|
+
COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
|
|
10
|
+
|
|
11
|
+
MARKETPLACE_NAME="anolisa-${COMPONENT}"
|
|
12
|
+
PLUGIN_ID="${COMPONENT}@${MARKETPLACE_NAME}"
|
|
13
|
+
|
|
14
|
+
CLAUDE_BIN="${CLAUDE_BIN:-claude}"
|
|
15
|
+
export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
|
|
16
|
+
|
|
17
|
+
echo "[${COMPONENT}] Uninstalling ${AGENT} plugin..."
|
|
18
|
+
|
|
19
|
+
if command -v "$CLAUDE_BIN" &>/dev/null; then
|
|
20
|
+
"$CLAUDE_BIN" plugin uninstall "$PLUGIN_ID" 2>&1 || true
|
|
21
|
+
"$CLAUDE_BIN" plugin marketplace remove "$MARKETPLACE_NAME" 2>&1 || true
|
|
22
|
+
echo "[${COMPONENT}] ${AGENT} plugin removed via claude CLI."
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
echo "[${COMPONENT}] claude CLI not found — falling back to manual cleanup."
|
|
27
|
+
SETTINGS="$HOME/.claude/settings.json"
|
|
28
|
+
if command -v jq &>/dev/null && [ -f "$SETTINGS" ]; then
|
|
29
|
+
# del(.[$key]) silently no-ops on non-object values. Verify shape up-front
|
|
30
|
+
# so a schema change in a future Claude Code release surfaces as a clear
|
|
31
|
+
# warning instead of a silent "cleaned" message that left residue behind.
|
|
32
|
+
EP_TYPE=$(jq -r '.enabledPlugins | type' "$SETTINGS" 2>/dev/null || echo "missing")
|
|
33
|
+
MK_TYPE=$(jq -r '.extraKnownMarketplaces | type' "$SETTINGS" 2>/dev/null || echo "missing")
|
|
34
|
+
if [ "$EP_TYPE" != "object" ] && [ "$EP_TYPE" != "null" ] && [ "$EP_TYPE" != "missing" ]; then
|
|
35
|
+
echo "[${COMPONENT}] WARN: .enabledPlugins is ${EP_TYPE}, expected object — skipping jq cleanup." >&2
|
|
36
|
+
echo "[${COMPONENT}] WARN: remove '${PLUGIN_ID}' from $SETTINGS manually." >&2
|
|
37
|
+
elif [ "$MK_TYPE" != "object" ] && [ "$MK_TYPE" != "null" ] && [ "$MK_TYPE" != "missing" ]; then
|
|
38
|
+
echo "[${COMPONENT}] WARN: .extraKnownMarketplaces is ${MK_TYPE}, expected object — skipping jq cleanup." >&2
|
|
39
|
+
echo "[${COMPONENT}] WARN: remove '${MARKETPLACE_NAME}' from $SETTINGS manually." >&2
|
|
40
|
+
else
|
|
41
|
+
tmp="$(mktemp "${SETTINGS}.XXXXXX")"
|
|
42
|
+
jq --arg id "$PLUGIN_ID" --arg mkt "$MARKETPLACE_NAME" '
|
|
43
|
+
if .enabledPlugins then .enabledPlugins |= del(.[$id]) else . end
|
|
44
|
+
| if .extraKnownMarketplaces then .extraKnownMarketplaces |= del(.[$mkt]) else . end
|
|
45
|
+
' "$SETTINGS" > "$tmp" && mv "$tmp" "$SETTINGS"
|
|
46
|
+
echo "[${COMPONENT}] cleaned $SETTINGS"
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
rm -rf "$HOME/.claude/plugins/cache/${MARKETPLACE_NAME}" 2>/dev/null || true
|
|
50
|
+
echo "[${COMPONENT}] manual cleanup complete."
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tokenless",
|
|
3
|
+
"version": "0.7.7",
|
|
4
|
+
"description": "Intelligent tool response compression, environment error detection, and token optimization. Strips noise (debug fields, nulls, empty values), truncates long strings/arrays, applies TOON encoding for JSON responses, and classifies environment errors with actionable fix hints.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"compression",
|
|
7
|
+
"token",
|
|
8
|
+
"context",
|
|
9
|
+
"optimization",
|
|
10
|
+
"environment",
|
|
11
|
+
"diagnostics"
|
|
12
|
+
],
|
|
13
|
+
"hooks": "./hooks/hooks.json",
|
|
14
|
+
"interface": {
|
|
15
|
+
"displayName": "Tokenless",
|
|
16
|
+
"shortDescription": "Compress tool responses and detect environment errors to save tokens",
|
|
17
|
+
"category": "developer-tools",
|
|
18
|
+
"capabilities": ["hook"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Tokenless Plugin for Codex
|
|
2
|
+
|
|
3
|
+
Intelligent tool response compression and environment error detection plugin for
|
|
4
|
+
[Codex](https://github.com/openai/codex). Reduces token consumption by stripping
|
|
5
|
+
noise, truncating verbose output, and classifying environment errors with
|
|
6
|
+
actionable fix hints.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
| Feature | Description |
|
|
11
|
+
|---------|-------------|
|
|
12
|
+
| **Response Compression** | Strips debug fields, nulls, empty values; truncates long strings (512 chars) and arrays (16 items); limits depth to 8 levels |
|
|
13
|
+
| **TOON Encoding** | Further compresses valid JSON responses (15-40% additional savings vs. compressed JSON) |
|
|
14
|
+
| **Environment Error Detection** | Classifies tool failures as dependency/permission/file/network/package issues; injects fix hints to prevent retry loops |
|
|
15
|
+
| **Statistics Tracking** | Records every compression operation to SQLite for auditing and optimization |
|
|
16
|
+
|
|
17
|
+
## How It Works
|
|
18
|
+
|
|
19
|
+
The plugin registers four hooks with Codex:
|
|
20
|
+
|
|
21
|
+
1. **`SessionStart`** — verifies the `tokenless` CLI is installed and functional (non-blocking)
|
|
22
|
+
2. **`PreToolUse` (tool-ready, hard-disabled)** — remains registered but is a
|
|
23
|
+
silent pass-through. `tokenless env-check` returns `UNKNOWN` with
|
|
24
|
+
`enabled:false`, so the hook emits no context, performs no repair, and never
|
|
25
|
+
blocks a tool. Re-enabling the retained legacy pipeline requires a source
|
|
26
|
+
change and a new release.
|
|
27
|
+
3. **`PreToolUse` (rewrite)** — runs before shell command execution:
|
|
28
|
+
- Rewrites commands via `rtk rewrite` for token optimization
|
|
29
|
+
- Only applies to Bash/Shell/terminal/programmatic tools
|
|
30
|
+
4. **`PostToolUse`** — runs after every tool execution:
|
|
31
|
+
- Skips content-reading tools (Read, Glob) and small responses (< 500 chars)
|
|
32
|
+
- Classifies environment errors and injects fix hints
|
|
33
|
+
- Compresses large JSON responses via `tokenless compress-response`
|
|
34
|
+
- Applies TOON encoding for additional savings
|
|
35
|
+
- Injects a compressed summary as `additionalContext`
|
|
36
|
+
|
|
37
|
+
> **Codex Protocol Constraint**: PostToolUse hooks cannot suppress the original
|
|
38
|
+
> tool output (`suppressOutput` is rejected). The plugin therefore injects a
|
|
39
|
+
> compressed *summary* as `additionalContext` — the model sees both the original
|
|
40
|
+
> output and the compressed summary, and can use the summary for efficient
|
|
41
|
+
> processing of large outputs.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
### Prerequisites
|
|
46
|
+
|
|
47
|
+
- Rust toolchain ≥ 1.89.0
|
|
48
|
+
- The `anolisa` source tree (this repository)
|
|
49
|
+
|
|
50
|
+
### Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
cd src/tokenless/adapters/tokenless/codex
|
|
54
|
+
./scripts/install.sh
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This builds the `tokenless` Rust CLI in release mode and installs it to
|
|
58
|
+
`~/.local/bin/`. Add `~/.local/bin` to your `PATH` if not already present.
|
|
59
|
+
|
|
60
|
+
### Configure Codex
|
|
61
|
+
|
|
62
|
+
Add the plugin to your Codex `config.toml`:
|
|
63
|
+
|
|
64
|
+
```toml
|
|
65
|
+
[plugins]
|
|
66
|
+
tokenless = { enabled = true }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Or install from the marketplace (once published).
|
|
70
|
+
|
|
71
|
+
### Verify
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
./scripts/detect.sh
|
|
75
|
+
# {"installed": true, "version": "1.0.0", "path": "/home/user/.local/bin/tokenless"}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Hook Output Format
|
|
79
|
+
|
|
80
|
+
The plugin injects `additionalContext` in the following format:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
[tokenless:compressed] Bash: 45,230 → 2,100 chars (95% reduction)
|
|
84
|
+
[tokenless:env:ENV_DEPENDENCY_MISSING] Missing dependency detected. ...
|
|
85
|
+
Do NOT retry the same command — fix the environment first.
|
|
86
|
+
--- compressed content ---
|
|
87
|
+
{... compressed JSON or TOON content ...}
|
|
88
|
+
--- end compressed content ---
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
Configuration is managed through the `tokenless` CLI's environment and config file:
|
|
94
|
+
|
|
95
|
+
| Variable | Default | Description |
|
|
96
|
+
|----------|---------|-------------|
|
|
97
|
+
| `TOKENLESS_AGENT_ID` | `codex` | Agent identifier for statistics |
|
|
98
|
+
| `TOKENLESS_BIN` | (auto-detected) | Path to tokenless binary |
|
|
99
|
+
| `TOKENLESS_STATS_ENABLED` | `1` | Enable statistics recording |
|
|
100
|
+
| `TOKENLESS_DATA_DIR` | `~/.tokenless` | Directory for `stats.db` and `stash.db` |
|
|
101
|
+
| `TOKENLESS_STATS_DB` | `~/.tokenless/stats.db` | Statistics database path |
|
|
102
|
+
| `TOKENLESS_STASH_DB` | `~/.tokenless/stash.db` | Reversible stash database path |
|
|
103
|
+
|
|
104
|
+
`TOKENLESS_STATS_DB` and `TOKENLESS_STASH_DB` take precedence over
|
|
105
|
+
`TOKENLESS_DATA_DIR`, which may be any accessible absolute non-root directory.
|
|
106
|
+
Custom database files must remain under the real user home or selected data
|
|
107
|
+
directory.
|
|
108
|
+
|
|
109
|
+
### View Statistics
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
tokenless stats summary
|
|
113
|
+
tokenless stats list --limit 20
|
|
114
|
+
tokenless stats show <id>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Compression Pipeline
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
Raw tool_response (JSON)
|
|
121
|
+
│
|
|
122
|
+
├─ Step 1: ResponseCompressor
|
|
123
|
+
│ ├─ Drop: debug, trace, stack, stacktrace, logs, logging fields
|
|
124
|
+
│ ├─ Truncate: strings > 512 chars, arrays > 16 items
|
|
125
|
+
│ ├─ Drop: null values, empty strings, empty arrays, empty objects
|
|
126
|
+
│ └─ Limit: max depth 8 levels
|
|
127
|
+
│
|
|
128
|
+
├─ Step 2: TOON Encoding (if result is still valid JSON)
|
|
129
|
+
│ └─ Binary-to-TOON format encoding (~15-40% additional savings)
|
|
130
|
+
│
|
|
131
|
+
└─ Guard: output only if smaller than input (safety check)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Architecture
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
codex-plugin-tokenless/
|
|
138
|
+
├── plugin.json.in # Codex plugin manifest (version-stamped by Makefile)
|
|
139
|
+
├── hooks/
|
|
140
|
+
│ └── hooks.json # Hook definitions (SessionStart, PreToolUse, PostToolUse)
|
|
141
|
+
├── scripts/
|
|
142
|
+
│ ├── compress-response # PostToolUse: response compression + env error detection
|
|
143
|
+
│ ├── rewrite-hook # PreToolUse: RTK command rewriting
|
|
144
|
+
│ ├── tool-ready # PreToolUse: registered hard-disabled pass-through
|
|
145
|
+
│ ├── check-tokenless # SessionStart: version/availability check
|
|
146
|
+
│ ├── install.sh # Build and install tokenless CLI + register plugin
|
|
147
|
+
│ ├── detect.sh # Detect tokenless availability
|
|
148
|
+
│ └── uninstall.sh # Cleanup plugin registration + marketplace
|
|
149
|
+
└── README.md
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Related
|
|
153
|
+
|
|
154
|
+
- [Tokenless Rust CLI](../../crates/tokenless-cli/) — core compression engine
|
|
155
|
+
- [OpenClaw Plugin](../openclaw/) — same compression for OpenClaw
|
|
156
|
+
- [Hermes Plugin](../hermes/) — same compression for Hermes
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
Same as the ANOLISA project.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "*",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "${PLUGIN_ROOT}/scripts/tool-ready",
|
|
10
|
+
"timeout": 10
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"matcher": "Bash|run_shell_command|terminal|Shell|exec|process",
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": "${PLUGIN_ROOT}/scripts/rewrite-hook",
|
|
20
|
+
"timeout": 10,
|
|
21
|
+
"statusMessage": "Rewriting command via RTK..."
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"PostToolUse": [
|
|
27
|
+
{
|
|
28
|
+
"matcher": "*",
|
|
29
|
+
"hooks": [
|
|
30
|
+
{
|
|
31
|
+
"type": "command",
|
|
32
|
+
"command": "${PLUGIN_ROOT}/scripts/compress-response",
|
|
33
|
+
"timeout": 15,
|
|
34
|
+
"statusMessage": "Compressing tool response..."
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"SessionStart": [
|
|
40
|
+
{
|
|
41
|
+
"matcher": null,
|
|
42
|
+
"hooks": [
|
|
43
|
+
{
|
|
44
|
+
"type": "command",
|
|
45
|
+
"command": "${PLUGIN_ROOT}/scripts/check-tokenless",
|
|
46
|
+
"timeout": 5
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# _common.sh — Shared helpers for codex adapter scripts.
|
|
3
|
+
# Source this file from install.sh and uninstall.sh.
|
|
4
|
+
|
|
5
|
+
# Resolve the codex CLI binary.
|
|
6
|
+
resolve_codex() {
|
|
7
|
+
if [[ -n "$CODEX_BIN" ]] && command -v "$CODEX_BIN" &>/dev/null; then
|
|
8
|
+
echo "$CODEX_BIN"
|
|
9
|
+
return
|
|
10
|
+
fi
|
|
11
|
+
for candidate in codex /usr/local/bin/codex /usr/bin/codex "$HOME/.local/bin/codex"; do
|
|
12
|
+
if command -v "$candidate" &>/dev/null; then
|
|
13
|
+
echo "$candidate"
|
|
14
|
+
return
|
|
15
|
+
fi
|
|
16
|
+
done
|
|
17
|
+
# Last resort: direct path check
|
|
18
|
+
for candidate in /usr/local/bin/codex /usr/bin/codex "$HOME/.local/bin/codex"; do
|
|
19
|
+
if [[ -x "$candidate" ]]; then
|
|
20
|
+
echo "$candidate"
|
|
21
|
+
return
|
|
22
|
+
fi
|
|
23
|
+
done
|
|
24
|
+
echo ""
|
|
25
|
+
}
|