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.
Files changed (70) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +770 -0
  3. package/adapters/tokenless/claude-code/.claude-plugin/marketplace.json +15 -0
  4. package/adapters/tokenless/claude-code/.claude-plugin/plugin.json +9 -0
  5. package/adapters/tokenless/claude-code/hooks/hooks.json +38 -0
  6. package/adapters/tokenless/claude-code/scripts/detect.sh +193 -0
  7. package/adapters/tokenless/claude-code/scripts/install.sh +92 -0
  8. package/adapters/tokenless/claude-code/scripts/uninstall.sh +50 -0
  9. package/adapters/tokenless/codex/.codex-plugin/plugin.json +20 -0
  10. package/adapters/tokenless/codex/README.md +160 -0
  11. package/adapters/tokenless/codex/hooks/hooks.json +52 -0
  12. package/adapters/tokenless/codex/scripts/_common.sh +25 -0
  13. package/adapters/tokenless/codex/scripts/check-tokenless +94 -0
  14. package/adapters/tokenless/codex/scripts/compress-response +441 -0
  15. package/adapters/tokenless/codex/scripts/detect.sh +61 -0
  16. package/adapters/tokenless/codex/scripts/install.sh +151 -0
  17. package/adapters/tokenless/codex/scripts/rewrite-hook +282 -0
  18. package/adapters/tokenless/codex/scripts/tool-ready +303 -0
  19. package/adapters/tokenless/codex/scripts/uninstall.sh +78 -0
  20. package/adapters/tokenless/common/commands/tokenless-stats.toml +2 -0
  21. package/adapters/tokenless/common/cosh-extension.json +65 -0
  22. package/adapters/tokenless/common/hooks/compress_response_hook.py +487 -0
  23. package/adapters/tokenless/common/hooks/compress_schema_hook.py +144 -0
  24. package/adapters/tokenless/common/hooks/compress_toon_hook.py +160 -0
  25. package/adapters/tokenless/common/hooks/hook_utils.py +584 -0
  26. package/adapters/tokenless/common/hooks/rewrite_hook.py +223 -0
  27. package/adapters/tokenless/common/hooks/run-hook.sh +62 -0
  28. package/adapters/tokenless/common/hooks/tool_categories.json +99 -0
  29. package/adapters/tokenless/common/hooks/tool_ready_hook.sh +571 -0
  30. package/adapters/tokenless/common/tokenless-env-fix.sh +730 -0
  31. package/adapters/tokenless/common/tool-ready-spec.json +113 -0
  32. package/adapters/tokenless/dsh/cordis.patch.yml +8 -0
  33. package/adapters/tokenless/dsh/dist/index.js +399 -0
  34. package/adapters/tokenless/dsh/package.json +26 -0
  35. package/adapters/tokenless/hermes/__init__.py +572 -0
  36. package/adapters/tokenless/hermes/plugin.yaml +9 -0
  37. package/adapters/tokenless/hermes/scripts/detect.sh +80 -0
  38. package/adapters/tokenless/hermes/scripts/install.sh +60 -0
  39. package/adapters/tokenless/hermes/scripts/uninstall.sh +45 -0
  40. package/adapters/tokenless/manifest.json +147 -0
  41. package/adapters/tokenless/openclaw/dist/index.d.ts +27 -0
  42. package/adapters/tokenless/openclaw/dist/index.js +598 -0
  43. package/adapters/tokenless/openclaw/dist/tool_categories.json +99 -0
  44. package/adapters/tokenless/openclaw/index.ts +720 -0
  45. package/adapters/tokenless/openclaw/openclaw.plugin.json +40 -0
  46. package/adapters/tokenless/openclaw/package.json +24 -0
  47. package/adapters/tokenless/openclaw/scripts/detect.sh +102 -0
  48. package/adapters/tokenless/openclaw/scripts/install.sh +75 -0
  49. package/adapters/tokenless/openclaw/scripts/uninstall.sh +46 -0
  50. package/adapters/tokenless/openclaw/tsconfig.json +13 -0
  51. package/adapters/tokenless/opencode/plugin.js +246 -0
  52. package/adapters/tokenless/opencode/scripts/detect.sh +38 -0
  53. package/adapters/tokenless/opencode/scripts/install.sh +56 -0
  54. package/adapters/tokenless/opencode/scripts/uninstall.sh +29 -0
  55. package/adapters/tokenless/qoder/.qoder-plugin/plugin.json +11 -0
  56. package/adapters/tokenless/qoder/commands/tokenless-stats.md +8 -0
  57. package/adapters/tokenless/qoder/hooks/hooks.json +36 -0
  58. package/adapters/tokenless/qoder/hooks/run-hook.sh +51 -0
  59. package/adapters/tokenless/qoder/scripts/detect.sh +35 -0
  60. package/adapters/tokenless/qoder/scripts/install.sh +136 -0
  61. package/adapters/tokenless/qoder/scripts/uninstall.sh +106 -0
  62. package/adapters/tokenless/qwencode/qwen-extension.json +69 -0
  63. package/adapters/tokenless/qwencode/scripts/detect.sh +125 -0
  64. package/adapters/tokenless/qwencode/scripts/install.sh +128 -0
  65. package/adapters/tokenless/qwencode/scripts/uninstall.sh +62 -0
  66. package/bin/rtk +6 -0
  67. package/bin/tokenless +6 -0
  68. package/bin/toon +6 -0
  69. package/package.json +57 -0
  70. package/scripts/postinstall.js +208 -0
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "tokenless",
3
+ "version": "0.7.7",
4
+ "description": "Token-Less LLM Token Optimization Toolkit for Qoder CLI",
5
+ "author": {
6
+ "name": "Shile Zhang",
7
+ "email": "shile.zhang@linux.alibaba.com"
8
+ },
9
+ "homepage": "https://github.com/alibaba/anolisa",
10
+ "license": "Apache-2.0"
11
+ }
@@ -0,0 +1,8 @@
1
+ ---
2
+ name: tokenless-stats
3
+ description: Show Tokenless compression statistics
4
+ ---
5
+
6
+ Run `tokenless stats` and summarize the compression statistics. If the
7
+ `tokenless` command is unavailable, explain that Tokenless must be installed
8
+ before this command can report statistics.
@@ -0,0 +1,36 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "TOKENLESS_AGENT_ID=qoder-cli bash \"${QODER_PLUGIN_ROOT}/hooks/run-hook.sh\" rewrite_hook.py",
10
+ "timeout": 10
11
+ }
12
+ ]
13
+ },
14
+ {
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "TOKENLESS_AGENT_ID=qoder-cli bash \"${QODER_PLUGIN_ROOT}/hooks/run-hook.sh\" tool_ready_hook.sh",
19
+ "timeout": 15
20
+ }
21
+ ]
22
+ }
23
+ ],
24
+ "PostToolUse": [
25
+ {
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": "TOKENLESS_AGENT_ID=qoder-cli bash \"${QODER_PLUGIN_ROOT}/hooks/run-hook.sh\" compress_response_hook.py",
30
+ "timeout": 15
31
+ }
32
+ ]
33
+ }
34
+ ]
35
+ }
36
+ }
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # run-hook.sh — Locate and exec a shared Tokenless hook script.
3
+ #
4
+ # Prefer hooks from the wrapper's own adapter tree so source and staged
5
+ # installations stay version-aligned. A qodercli cache contains only the
6
+ # Qoder plugin, so packaged plugins use the ANOLISA FHS fallbacks.
7
+ #
8
+ # Fail open when the shared hook or its interpreter is unavailable so a
9
+ # Tokenless packaging problem never blocks a Qoder tool call.
10
+ set -euo pipefail
11
+
12
+ SCRIPT_DIR="$(CDPATH='' cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
13
+ SCRIPT="${1:?usage: run-hook.sh <hook-script-basename> [args...]}"
14
+ shift
15
+
16
+ fail_open() { echo "{}"; exit 0; }
17
+
18
+ case "$SCRIPT" in
19
+ compress_response_hook.py|rewrite_hook.py|tool_ready_hook.sh) ;;
20
+ *) fail_open ;;
21
+ esac
22
+
23
+ CANDIDATES=(
24
+ "${SCRIPT_DIR}/../../common/hooks/${SCRIPT}"
25
+ "/usr/local/share/anolisa/adapters/tokenless/common/hooks/${SCRIPT}"
26
+ "/usr/share/anolisa/adapters/tokenless/common/hooks/${SCRIPT}"
27
+ )
28
+
29
+ # Minimal runners may omit HOME. Keep the user-local fallback conditional so
30
+ # an environment omission cannot bypass the fail-open behavior below.
31
+ if [ -n "${HOME:-}" ]; then
32
+ CANDIDATES+=("${HOME}/.local/share/anolisa/adapters/tokenless/common/hooks/${SCRIPT}")
33
+ fi
34
+
35
+ for candidate in "${CANDIDATES[@]}"; do
36
+ [ -f "$candidate" ] || continue
37
+ case "$candidate" in
38
+ *.py)
39
+ command -v python3 >/dev/null 2>&1 || fail_open
40
+ exec python3 "$candidate" "$@"
41
+ ;;
42
+ *.sh)
43
+ exec bash "$candidate" "$@"
44
+ ;;
45
+ *)
46
+ fail_open
47
+ ;;
48
+ esac
49
+ done
50
+
51
+ fail_open
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ # detect.sh — Check whether Qoder CLI supports the native plugin lifecycle.
3
+ set -euo pipefail
4
+
5
+ AGENT="${ANOLISA_TARGET:-qoder}"
6
+ COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
7
+
8
+ versioned_glob="$HOME/.qoder/bin/qodercli/qodercli-${ANOLISA_QODER_VERSION:-*}"
9
+ # shellcheck disable=SC2086 # Intentional version glob expansion.
10
+ latest_versioned="$(ls -d $versioned_glob 2>/dev/null | sort -V | tail -1 || true)"
11
+
12
+ QODERCLI=""
13
+ for candidate in "$latest_versioned" \
14
+ "$HOME/.qoder/bin/qodercli/qodercli" \
15
+ "qodercli"; do
16
+ [ -n "$candidate" ] || continue
17
+ if [ -x "$candidate" ] || command -v "$candidate" >/dev/null 2>&1; then
18
+ QODERCLI="$candidate"
19
+ break
20
+ fi
21
+ done
22
+
23
+ if [ -z "$QODERCLI" ]; then
24
+ echo "[${COMPONENT}] ${AGENT}: qodercli not found in standard locations" >&2
25
+ exit 1
26
+ fi
27
+
28
+ for subcommand in install list uninstall; do
29
+ if ! "$QODERCLI" plugins "$subcommand" --help >/dev/null 2>&1; then
30
+ echo "[${COMPONENT}] ${AGENT}: qodercli lacks 'plugins ${subcommand}' support" >&2
31
+ exit 1
32
+ fi
33
+ done
34
+
35
+ echo "[${COMPONENT}] ${AGENT}: detected compatible qodercli at ${QODERCLI}"
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env bash
2
+ # install.sh — Install the native Tokenless plugin for Qoder CLI.
3
+ set -euo pipefail
4
+
5
+ AGENT="${ANOLISA_TARGET:-qoder}"
6
+ COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
7
+ ADAPTER_DIR="${ANOLISA_ADAPTER_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
8
+ PLUGIN_DIR="$ADAPTER_DIR/qoder"
9
+ PLUGIN_JSON="$PLUGIN_DIR/.qoder-plugin/plugin.json"
10
+
11
+ find_qodercli() {
12
+ local versioned_glob latest_versioned candidate
13
+ versioned_glob="$HOME/.qoder/bin/qodercli/qodercli-${ANOLISA_QODER_VERSION:-*}"
14
+ # shellcheck disable=SC2086 # Intentional version glob expansion.
15
+ latest_versioned="$(ls -d $versioned_glob 2>/dev/null | sort -V | tail -1 || true)"
16
+
17
+ for candidate in "$latest_versioned" \
18
+ "$HOME/.qoder/bin/qodercli/qodercli" \
19
+ "qodercli"; do
20
+ [ -n "$candidate" ] || continue
21
+ if [ -x "$candidate" ] || command -v "$candidate" >/dev/null 2>&1; then
22
+ printf '%s\n' "$candidate"
23
+ return 0
24
+ fi
25
+ done
26
+ return 1
27
+ }
28
+
29
+ QODERCLI="$(find_qodercli || true)"
30
+ if [ -z "$QODERCLI" ]; then
31
+ echo "[${COMPONENT}] ERROR: qodercli not found, aborting plugin registration" >&2
32
+ echo " Install Qoder CLI first: https://qoder.com/cli" >&2
33
+ exit 1
34
+ fi
35
+
36
+ for subcommand in install list uninstall; do
37
+ if ! "$QODERCLI" plugins "$subcommand" --help >/dev/null 2>&1; then
38
+ echo "[${COMPONENT}] ERROR: qodercli lacks required 'plugins ${subcommand}' support" >&2
39
+ exit 1
40
+ fi
41
+ done
42
+
43
+ if ! command -v python3 >/dev/null 2>&1; then
44
+ echo "[${COMPONENT}] ERROR: python3 is required by the Tokenless hooks" >&2
45
+ exit 1
46
+ fi
47
+ if [ ! -f "$PLUGIN_JSON" ]; then
48
+ echo "[${COMPONENT}] ERROR: Qoder plugin manifest not found: ${PLUGIN_JSON}" >&2
49
+ exit 1
50
+ fi
51
+
52
+ VERSION="$(PLUGIN_JSON="$PLUGIN_JSON" python3 - <<'PYEOF'
53
+ import json
54
+ import os
55
+
56
+ with open(os.environ["PLUGIN_JSON"], encoding="utf-8") as manifest_file:
57
+ print(json.load(manifest_file).get("version", "unknown"))
58
+ PYEOF
59
+ )"
60
+
61
+ echo "[${COMPONENT}] Installing ${AGENT} plugin v${VERSION}..."
62
+
63
+ # Validation was added after the core plugin lifecycle commands. Keep it
64
+ # optional so older compatible clients can still install, while treating a
65
+ # validation failure as a malformed bundle when the capability exists.
66
+ if "$QODERCLI" plugins validate --help >/dev/null 2>&1; then
67
+ if ! VALIDATE_OUT="$("$QODERCLI" plugins validate "$PLUGIN_DIR" 2>&1)"; then
68
+ echo "[${COMPONENT}] ERROR: qodercli rejected the native plugin bundle" >&2
69
+ echo " Output: $VALIDATE_OUT" >&2
70
+ exit 1
71
+ fi
72
+ fi
73
+
74
+ echo "[${COMPONENT}] Registering native plugin with qodercli..."
75
+ if ! INSTALL_OUT="$("$QODERCLI" plugins install "$PLUGIN_DIR" --scope user 2>&1)"; then
76
+ echo "[${COMPONENT}] ERROR: qodercli plugins install failed" >&2
77
+ echo " Output: $INSTALL_OUT" >&2
78
+ exit 1
79
+ fi
80
+ [ -z "$INSTALL_OUT" ] || echo "$INSTALL_OUT"
81
+
82
+ if ! PLUGIN_LIST_OUT="$("$QODERCLI" plugins list --json)"; then
83
+ echo "[${COMPONENT}] ERROR: qodercli plugins list failed after installation" >&2
84
+ exit 1
85
+ fi
86
+
87
+ if ! QODER_PLUGIN_LIST="$PLUGIN_LIST_OUT" python3 - <<'PYEOF'
88
+ import json
89
+ import os
90
+ import sys
91
+
92
+ expected_id = "tokenless@local"
93
+ try:
94
+ inventory = json.loads(os.environ["QODER_PLUGIN_LIST"])
95
+ except json.JSONDecodeError as error:
96
+ print(f"invalid JSON from qodercli plugins list: {error}", file=sys.stderr)
97
+ sys.exit(1)
98
+
99
+ if isinstance(inventory, dict):
100
+ inventory = inventory.get("plugins")
101
+ if not isinstance(inventory, list):
102
+ print("qodercli plugin inventory is not a list", file=sys.stderr)
103
+ sys.exit(1)
104
+
105
+ matches = [
106
+ item
107
+ for item in inventory
108
+ if isinstance(item, dict) and item.get("id") == expected_id
109
+ ]
110
+ if len(matches) != 1:
111
+ print(
112
+ f"expected exactly one {expected_id} inventory entry, found {len(matches)}",
113
+ file=sys.stderr,
114
+ )
115
+ sys.exit(1)
116
+
117
+ plugin = matches[0]
118
+ if plugin.get("scope") != "user":
119
+ print(f"{expected_id} has unexpected scope: {plugin.get('scope')!r}", file=sys.stderr)
120
+ sys.exit(1)
121
+ if plugin.get("enabled") is not True:
122
+ print(f"{expected_id} is not enabled", file=sys.stderr)
123
+ sys.exit(1)
124
+
125
+ resources = plugin.get("resources")
126
+ hooks = resources.get("hooks") if isinstance(resources, dict) else None
127
+ if not isinstance(hooks, list) or not hooks:
128
+ print(f"{expected_id} loaded no hook resources", file=sys.stderr)
129
+ sys.exit(1)
130
+ PYEOF
131
+ then
132
+ echo "[${COMPONENT}] ERROR: installed Qoder plugin failed inventory verification" >&2
133
+ exit 1
134
+ fi
135
+
136
+ echo "[${COMPONENT}] ${AGENT} plugin v${VERSION} installed and activated."
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env bash
2
+ # uninstall.sh — Remove the native Tokenless plugin from Qoder CLI.
3
+ set -euo pipefail
4
+
5
+ AGENT="${ANOLISA_TARGET:-qoder}"
6
+ COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
7
+
8
+ find_qodercli() {
9
+ local versioned_glob latest_versioned candidate
10
+ versioned_glob="$HOME/.qoder/bin/qodercli/qodercli-${ANOLISA_QODER_VERSION:-*}"
11
+ # shellcheck disable=SC2086 # Intentional version glob expansion.
12
+ latest_versioned="$(ls -d $versioned_glob 2>/dev/null | sort -V | tail -1 || true)"
13
+
14
+ for candidate in "$latest_versioned" \
15
+ "$HOME/.qoder/bin/qodercli/qodercli" \
16
+ "qodercli"; do
17
+ [ -n "$candidate" ] || continue
18
+ if [ -x "$candidate" ] || command -v "$candidate" >/dev/null 2>&1; then
19
+ printf '%s\n' "$candidate"
20
+ return 0
21
+ fi
22
+ done
23
+ return 1
24
+ }
25
+
26
+ QODERCLI="$(find_qodercli || true)"
27
+ echo "[${COMPONENT}] Removing ${AGENT} plugin..."
28
+
29
+ if [ -z "$QODERCLI" ]; then
30
+ echo "[${COMPONENT}] WARNING: qodercli not found, cannot unregister plugin" >&2
31
+ exit 0
32
+ fi
33
+ for subcommand in list uninstall; do
34
+ if ! "$QODERCLI" plugins "$subcommand" --help >/dev/null 2>&1; then
35
+ echo "[${COMPONENT}] ERROR: qodercli lacks required 'plugins ${subcommand}' support" >&2
36
+ exit 1
37
+ fi
38
+ done
39
+ if ! command -v python3 >/dev/null 2>&1; then
40
+ echo "[${COMPONENT}] ERROR: python3 is required to verify Qoder plugin state" >&2
41
+ exit 1
42
+ fi
43
+
44
+ read_plugin_state() {
45
+ local plugin_list_out
46
+ if ! plugin_list_out="$("$QODERCLI" plugins list --json 2>&1)"; then
47
+ echo "[${COMPONENT}] ERROR: qodercli plugins list failed during uninstall" >&2
48
+ echo " Output: $plugin_list_out" >&2
49
+ return 1
50
+ fi
51
+
52
+ QODER_PLUGIN_LIST="$plugin_list_out" python3 - <<'PYEOF'
53
+ import json
54
+ import os
55
+ import sys
56
+
57
+ try:
58
+ inventory = json.loads(os.environ["QODER_PLUGIN_LIST"])
59
+ except json.JSONDecodeError as error:
60
+ print(f"invalid JSON from qodercli plugins list: {error}", file=sys.stderr)
61
+ sys.exit(1)
62
+
63
+ if isinstance(inventory, dict):
64
+ inventory = inventory.get("plugins")
65
+ if not isinstance(inventory, list):
66
+ print("qodercli plugin inventory is not a list", file=sys.stderr)
67
+ sys.exit(1)
68
+
69
+ matches = [
70
+ item
71
+ for item in inventory
72
+ if isinstance(item, dict)
73
+ and item.get("id") == "tokenless@local"
74
+ and item.get("scope") == "user"
75
+ ]
76
+ if len(matches) > 1:
77
+ print("multiple user-scoped tokenless@local entries found", file=sys.stderr)
78
+ sys.exit(1)
79
+ print("present" if matches else "absent")
80
+ PYEOF
81
+ }
82
+
83
+ if ! PLUGIN_STATE="$(read_plugin_state)"; then
84
+ exit 1
85
+ fi
86
+ if [ "$PLUGIN_STATE" = "absent" ]; then
87
+ echo "[${COMPONENT}] ${AGENT} plugin is already absent."
88
+ exit 0
89
+ fi
90
+
91
+ if ! UNINSTALL_OUT="$("$QODERCLI" plugins uninstall tokenless --scope user 2>&1)"; then
92
+ echo "[${COMPONENT}] ERROR: qodercli plugins uninstall failed" >&2
93
+ echo " Output: $UNINSTALL_OUT" >&2
94
+ exit 1
95
+ fi
96
+ [ -z "$UNINSTALL_OUT" ] || echo "$UNINSTALL_OUT"
97
+
98
+ if ! PLUGIN_STATE="$(read_plugin_state)"; then
99
+ exit 1
100
+ fi
101
+ if [ "$PLUGIN_STATE" != "absent" ]; then
102
+ echo "[${COMPONENT}] ERROR: user-scoped tokenless plugin remains after uninstall" >&2
103
+ exit 1
104
+ fi
105
+
106
+ echo "[${COMPONENT}] ${AGENT} plugin removed."
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "tokenless",
3
+ "version": "0.7.7",
4
+ "description": "Token-Less context compression for Qwen Code — RTK command rewriting, response/TOON/schema compression, and registered but hard-disabled Tool Ready",
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
+ "hooks": {
10
+ "PreToolUse": [
11
+ {
12
+ "matcher": "",
13
+ "sequential": true,
14
+ "hooks": [
15
+ {
16
+ "type": "command",
17
+ "name": "tokenless-tool-ready",
18
+ "description": "Registered hard-disabled Tool Ready pass-through; performs no check, repair, or block",
19
+ "command": "bash \"${extensionPath}/hooks/run-hook.sh\" tool_ready_hook.sh",
20
+ "timeout": 10000,
21
+ "env": { "TOKENLESS_AGENT_ID": "qwencode" }
22
+ }
23
+ ]
24
+ },
25
+ {
26
+ "matcher": "^(Bash|run_shell_command|terminal|Shell|exec|process)$",
27
+ "hooks": [
28
+ {
29
+ "type": "command",
30
+ "name": "tokenless-rewrite",
31
+ "description": "Rewrites shell commands via rtk for token savings",
32
+ "command": "bash \"${extensionPath}/hooks/run-hook.sh\" rewrite_hook.py",
33
+ "timeout": 5000,
34
+ "env": { "TOKENLESS_AGENT_ID": "qwencode" }
35
+ }
36
+ ]
37
+ }
38
+ ],
39
+ "PostToolUse": [
40
+ {
41
+ "matcher": "^(?!(?:Read|Glob|NotebookRead)$).+",
42
+ "hooks": [
43
+ {
44
+ "type": "command",
45
+ "name": "tokenless-compress-response",
46
+ "description": "Compresses tool responses and encodes to TOON format",
47
+ "command": "bash \"${extensionPath}/hooks/run-hook.sh\" compress_response_hook.py",
48
+ "timeout": 10000,
49
+ "env": { "TOKENLESS_AGENT_ID": "qwencode" }
50
+ }
51
+ ]
52
+ }
53
+ ],
54
+ "BeforeModel": [
55
+ {
56
+ "hooks": [
57
+ {
58
+ "type": "command",
59
+ "name": "tokenless-compress-schema",
60
+ "description": "Compresses tool schema definitions for token savings",
61
+ "command": "bash \"${extensionPath}/hooks/run-hook.sh\" compress_schema_hook.py",
62
+ "timeout": 10000,
63
+ "env": { "TOKENLESS_AGENT_ID": "qwencode" }
64
+ }
65
+ ]
66
+ }
67
+ ]
68
+ }
69
+ }
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env bash
2
+ # detect.sh — Inspect Qwen Code presence and the tokenless plugin state.
3
+ # Read-only. Tri-state exit aligns with claude-code/openclaw 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:-qwencode}"
11
+ ADAPTER_DIR="${ANOLISA_ADAPTER_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
12
+
13
+ PLUGIN_ID="tokenless"
14
+ PLUGIN_SRC="$ADAPTER_DIR/qwencode"
15
+
16
+ QWEN_BIN="${QWEN_BIN:-}"
17
+ export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
18
+
19
+ line() { printf '[%s] %s\n' "$COMPONENT" "$*"; }
20
+ field() { printf '[%s] %-26s %s\n' "$COMPONENT" "$1" "$2"; }
21
+
22
+ PREREQ_MISSING=()
23
+ INSTALL_MISSING=()
24
+ note_prereq_missing() { PREREQ_MISSING+=("$1"); }
25
+ note_install_missing() { INSTALL_MISSING+=("$1"); }
26
+
27
+ if [ -z "$QWEN_BIN" ]; then
28
+ QWEN_BIN="$(command -v qwen 2>/dev/null || true)"
29
+ fi
30
+
31
+ line "${AGENT} detect"
32
+ if [ -n "$QWEN_BIN" ] && [ -x "$QWEN_BIN" ]; then
33
+ QWEN_VER="$("$QWEN_BIN" --version 2>/dev/null | awk '{print $1}' || echo unknown)"
34
+ field "qwen CLI" "present (${QWEN_BIN}, v${QWEN_VER})"
35
+ else
36
+ field "qwen CLI" "missing"
37
+ note_prereq_missing "qwen CLI"
38
+ fi
39
+
40
+ # Informational only: Qwen Code stores global settings, extensions, and
41
+ # session data under ~/.qwen/ (upstream default). Absence is not a
42
+ # prerequisite failure — the directory is created on first run.
43
+ if [ -d "$HOME/.qwen" ]; then
44
+ field "qwen config dir" "present ($HOME/.qwen)"
45
+ else
46
+ field "qwen config dir" "missing (created on first Qwen Code run)"
47
+ fi
48
+
49
+ if [ -f "$PLUGIN_SRC/qwen-extension.json" ]; then
50
+ field "qwen-extension.json" "present"
51
+ else
52
+ field "qwen-extension.json" "missing"
53
+ note_prereq_missing "qwen-extension.json"
54
+ fi
55
+
56
+ if [ -n "$QWEN_BIN" ] && [ -x "$QWEN_BIN" ]; then
57
+ if "$QWEN_BIN" extensions list 2>/dev/null | grep -qE "(^|[[:space:]])${PLUGIN_ID}([[:space:]]|$)"; then
58
+ field "plugin install" "installed (${PLUGIN_ID})"
59
+ else
60
+ field "plugin install" "not installed"
61
+ note_install_missing "${PLUGIN_ID}"
62
+ fi
63
+ fi
64
+
65
+ if command -v python3 &>/dev/null; then
66
+ field "python3" "present ($(command -v python3))"
67
+ else
68
+ field "python3" "missing"
69
+ note_prereq_missing "python3"
70
+ fi
71
+
72
+ if command -v jq &>/dev/null; then
73
+ field "jq" "present ($(command -v jq))"
74
+ else
75
+ field "jq" "missing (tool-ready hook disabled)"
76
+ fi
77
+
78
+ runtime_bin="$(command -v tokenless 2>/dev/null || true)"
79
+ if [ -n "$runtime_bin" ]; then
80
+ field "tokenless binary" "present (${runtime_bin})"
81
+ else
82
+ field "tokenless binary" "missing"
83
+ note_prereq_missing "tokenless binary"
84
+ fi
85
+
86
+ rtk_bin="$(command -v rtk 2>/dev/null || true)"
87
+ if [ -n "$rtk_bin" ]; then
88
+ field "rtk binary" "present (${rtk_bin})"
89
+ else
90
+ field "rtk binary" "missing"
91
+ note_prereq_missing "rtk binary"
92
+ fi
93
+
94
+ # Shared hook scripts live under FHS; warn when missing so user knows to run
95
+ # `make install` (or install the RPM) before adapter actually fires.
96
+ SHARED_HOOKS_DIR=""
97
+ for d in /usr/local/share/anolisa/adapters/tokenless/common/hooks \
98
+ /usr/share/anolisa/adapters/tokenless/common/hooks \
99
+ "$HOME/.local/share/anolisa/adapters/tokenless/common/hooks"; do
100
+ if [ -d "$d" ]; then SHARED_HOOKS_DIR="$d"; break; fi
101
+ done
102
+ if [ -n "$SHARED_HOOKS_DIR" ]; then
103
+ field "shared hooks dir" "present ($SHARED_HOOKS_DIR)"
104
+ else
105
+ field "shared hooks dir" "missing (run: make -C src/tokenless install)"
106
+ note_prereq_missing "shared hooks dir"
107
+ fi
108
+
109
+ if [ -f "$PLUGIN_SRC/hooks/run-hook.sh" ]; then
110
+ field "hook dispatcher" "present"
111
+ else
112
+ field "hook dispatcher" "missing (hooks/run-hook.sh)"
113
+ note_prereq_missing "hook dispatcher"
114
+ fi
115
+
116
+ if [ ${#PREREQ_MISSING[@]} -gt 0 ]; then
117
+ line "${AGENT}: missing prerequisites (${PREREQ_MISSING[*]})"
118
+ exit 2
119
+ fi
120
+ if [ ${#INSTALL_MISSING[@]} -gt 0 ]; then
121
+ line "${AGENT}: not installed (ready to install)"
122
+ exit 1
123
+ fi
124
+ line "${AGENT}: ready"
125
+ exit 0