tokenjar 1.0.1__py3-none-any.whl
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.
- tokenjar/__init__.py +3 -0
- tokenjar/__main__.py +629 -0
- tokenjar/cache/__init__.py +1 -0
- tokenjar/cache/persistent_cache.py +371 -0
- tokenjar/cache/session_cache.py +259 -0
- tokenjar/config.py +195 -0
- tokenjar/filters/__init__.py +1 -0
- tokenjar/filters/ansi.py +11 -0
- tokenjar/filters/build_tools.py +95 -0
- tokenjar/filters/git.py +23 -0
- tokenjar/filters/lockfile.py +275 -0
- tokenjar/filters/test_runners.py +137 -0
- tokenjar/hooks/__init__.py +1 -0
- tokenjar/hooks/manager.py +870 -0
- tokenjar/parsers/__init__.py +1 -0
- tokenjar/parsers/languages.py +66 -0
- tokenjar/rules/manager.py +297 -0
- tokenjar/server.py +81 -0
- tokenjar/telemetry/__init__.py +1 -0
- tokenjar/telemetry/stats.py +293 -0
- tokenjar/tools/__init__.py +1 -0
- tokenjar/tools/output_pruner.py +178 -0
- tokenjar/tools/repo_map.py +493 -0
- tokenjar/tools/skeleton.py +248 -0
- tokenjar/tools/smart_reader.py +171 -0
- tokenjar/tools/symbol_index.py +501 -0
- tokenjar/ui/__init__.py +11 -0
- tokenjar/ui/server.py +408 -0
- tokenjar/ui/static/index.html +773 -0
- tokenjar/utils/__init__.py +1 -0
- tokenjar/utils/file_utils.py +250 -0
- tokenjar/utils/token_counter.py +87 -0
- tokenjar-1.0.1.dist-info/METADATA +474 -0
- tokenjar-1.0.1.dist-info/RECORD +37 -0
- tokenjar-1.0.1.dist-info/WHEEL +4 -0
- tokenjar-1.0.1.dist-info/entry_points.txt +2 -0
- tokenjar-1.0.1.dist-info/licenses/LICENSE +41 -0
tokenjar/__init__.py
ADDED
tokenjar/__main__.py
ADDED
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
"""TokenJar CLI & MCP Entry Point.
|
|
2
|
+
|
|
3
|
+
Supports running both as an MCP server for AI coding assistants
|
|
4
|
+
and as a standalone CLI tool for developers (run, stats, hook, unhook).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
# Configure stdout and stderr to handle UTF-8 cleanly on Windows/legacy terminals
|
|
13
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
14
|
+
try:
|
|
15
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
16
|
+
except Exception:
|
|
17
|
+
pass
|
|
18
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
19
|
+
try:
|
|
20
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
21
|
+
except Exception:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main() -> None:
|
|
26
|
+
"""Main CLI entry point for TokenJar."""
|
|
27
|
+
parser = argparse.ArgumentParser(
|
|
28
|
+
prog="tokenjar",
|
|
29
|
+
description="TokenJar: Zero-cost token optimization engine for AI coding assistants and developers.",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"-v",
|
|
33
|
+
"--version",
|
|
34
|
+
action="version",
|
|
35
|
+
version="tokenjar 1.0.1",
|
|
36
|
+
help="Show program's version number and exit",
|
|
37
|
+
)
|
|
38
|
+
subparsers = parser.add_subparsers(dest="subcommand", metavar="<command>", help="Available subcommands")
|
|
39
|
+
|
|
40
|
+
# Subcommand: version
|
|
41
|
+
subparsers.add_parser("version", help="Show program's version number and exit")
|
|
42
|
+
|
|
43
|
+
# Subcommand: server (default if no args)
|
|
44
|
+
subparsers.add_parser("server", help="Start the MCP server (stdio transport)")
|
|
45
|
+
|
|
46
|
+
# Subcommand: stats
|
|
47
|
+
subparsers.add_parser("stats", help="Display cumulative token and financial savings dashboard")
|
|
48
|
+
|
|
49
|
+
# Subcommand: status
|
|
50
|
+
status_parser = subparsers.add_parser(
|
|
51
|
+
"status",
|
|
52
|
+
help="Check comprehensive live operational status of TokenJar across all AI CLIs and project rules",
|
|
53
|
+
)
|
|
54
|
+
status_parser.add_argument(
|
|
55
|
+
"--path",
|
|
56
|
+
default=".",
|
|
57
|
+
help="Target project directory to check rules for (default: current directory)",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Subcommand: reset-stats
|
|
61
|
+
subparsers.add_parser("reset-stats", help="Reset cumulative telemetry metrics")
|
|
62
|
+
|
|
63
|
+
# Subcommand: run
|
|
64
|
+
run_parser = subparsers.add_parser("run", help="Execute a shell command with intelligent output filtering")
|
|
65
|
+
run_parser.add_argument("command", nargs=argparse.REMAINDER, help="The command to execute (e.g. pytest, npm test)")
|
|
66
|
+
|
|
67
|
+
# Subcommand: hook
|
|
68
|
+
hook_parser = subparsers.add_parser("hook", help="Install non-intrusive transparent shell hooks")
|
|
69
|
+
hook_parser.add_argument(
|
|
70
|
+
"--shell",
|
|
71
|
+
choices=["auto", "powershell", "bash"],
|
|
72
|
+
default="auto",
|
|
73
|
+
help="Target shell environment (default: auto)",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Subcommand: unhook
|
|
77
|
+
subparsers.add_parser("unhook", help="Safely remove all installed shell hooks")
|
|
78
|
+
|
|
79
|
+
# Subcommand: on / enable
|
|
80
|
+
on_parser = subparsers.add_parser(
|
|
81
|
+
"on", aliases=["enable"], help="Activate TokenJar for current project (or use --global for all IDEs)"
|
|
82
|
+
)
|
|
83
|
+
on_parser.add_argument(
|
|
84
|
+
"-g",
|
|
85
|
+
"--global",
|
|
86
|
+
dest="global_scope",
|
|
87
|
+
action="store_true",
|
|
88
|
+
help="Configure MCP server globally in all detected IDEs without modifying project files",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Subcommand: off / disable
|
|
92
|
+
off_parser = subparsers.add_parser(
|
|
93
|
+
"off",
|
|
94
|
+
aliases=["disable"],
|
|
95
|
+
help="Deactivate TokenJar for current project (or use --global to uninstall from IDEs)",
|
|
96
|
+
)
|
|
97
|
+
off_parser.add_argument(
|
|
98
|
+
"-g",
|
|
99
|
+
"--global",
|
|
100
|
+
dest="global_scope",
|
|
101
|
+
action="store_true",
|
|
102
|
+
help="Uninstall TokenJar MCP configuration globally from all IDEs",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Subcommand: install-mcp
|
|
106
|
+
install_mcp_parser = subparsers.add_parser(
|
|
107
|
+
"install-mcp",
|
|
108
|
+
help="Configure TokenJar MCP server in Claude Desktop, Cursor, Windsurf, VS Code with safe backup",
|
|
109
|
+
)
|
|
110
|
+
install_mcp_parser.add_argument(
|
|
111
|
+
"--all",
|
|
112
|
+
action="store_true",
|
|
113
|
+
help="Configure for all supported IDEs even if not currently detected on system",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Subcommand: uninstall-mcp
|
|
117
|
+
subparsers.add_parser(
|
|
118
|
+
"uninstall-mcp",
|
|
119
|
+
help="Safely remove TokenJar MCP configuration and restore exact original state from backup",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Subcommand: ui
|
|
123
|
+
ui_parser = subparsers.add_parser(
|
|
124
|
+
"ui",
|
|
125
|
+
help="Launch the lightweight On-Demand Settings & Dashboard UI (Zero Background RAM)",
|
|
126
|
+
)
|
|
127
|
+
ui_parser.add_argument(
|
|
128
|
+
"--port",
|
|
129
|
+
type=int,
|
|
130
|
+
default=4141,
|
|
131
|
+
help="Port to bind the local dashboard server (default: 4141)",
|
|
132
|
+
)
|
|
133
|
+
ui_parser.add_argument(
|
|
134
|
+
"--no-open",
|
|
135
|
+
action="store_true",
|
|
136
|
+
help="Do not automatically open the browser or native app window",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Subcommand: setup-commands
|
|
140
|
+
subparsers.add_parser(
|
|
141
|
+
"setup-commands",
|
|
142
|
+
help="Install /tokenjar slash command definitions across AGY CLI and Claude Code",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Subcommand: output (toggle compact output on/off/status)
|
|
146
|
+
output_parser = subparsers.add_parser(
|
|
147
|
+
"output",
|
|
148
|
+
help="Manage AI output mode: 'tokenjar output on' or 'tokenjar output off'",
|
|
149
|
+
)
|
|
150
|
+
output_parser.add_argument(
|
|
151
|
+
"state",
|
|
152
|
+
nargs="?",
|
|
153
|
+
choices=["on", "off", "status"],
|
|
154
|
+
default="status",
|
|
155
|
+
help="Output mode action: 'on' (compact surgical diffs), 'off' (default output), or 'status'",
|
|
156
|
+
)
|
|
157
|
+
output_parser.add_argument(
|
|
158
|
+
"--path",
|
|
159
|
+
default=".",
|
|
160
|
+
help="Target project directory (default: current directory)",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Subcommand: init / init-rules
|
|
164
|
+
rules_parser = subparsers.add_parser(
|
|
165
|
+
"init",
|
|
166
|
+
aliases=["init-rules"],
|
|
167
|
+
help="Install agent steering rules into AGENTS.md, .cursorrules, .windsurfrules, and CLAUDE.md",
|
|
168
|
+
)
|
|
169
|
+
rules_parser.add_argument(
|
|
170
|
+
"--path",
|
|
171
|
+
default=".",
|
|
172
|
+
help="Target project directory (default: current directory)",
|
|
173
|
+
)
|
|
174
|
+
rules_parser.add_argument(
|
|
175
|
+
"--compact",
|
|
176
|
+
dest="compact_output",
|
|
177
|
+
action="store_true",
|
|
178
|
+
default=None,
|
|
179
|
+
help="Enforce compact surgical output rules",
|
|
180
|
+
)
|
|
181
|
+
rules_parser.add_argument(
|
|
182
|
+
"--no-compact",
|
|
183
|
+
dest="compact_output",
|
|
184
|
+
action="store_false",
|
|
185
|
+
help="Disable compact output restrictions in agent rules",
|
|
186
|
+
)
|
|
187
|
+
rules_parser.add_argument(
|
|
188
|
+
"--all",
|
|
189
|
+
action="store_true",
|
|
190
|
+
help="Generate rule files for all AI coding assistants (default: auto-detect installed assistants)",
|
|
191
|
+
)
|
|
192
|
+
rules_parser.add_argument(
|
|
193
|
+
"--clean",
|
|
194
|
+
action="store_true",
|
|
195
|
+
help="Clean steering rules from target project directory instead of installing",
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Subcommand: cache-prune / cache-clear / cache-reset
|
|
199
|
+
prune_parser = subparsers.add_parser(
|
|
200
|
+
"cache-prune",
|
|
201
|
+
aliases=["cache-clear", "cache-reset"],
|
|
202
|
+
help="Prune expired entries or completely reset L2 SQLite cache",
|
|
203
|
+
)
|
|
204
|
+
prune_parser.add_argument(
|
|
205
|
+
"--all",
|
|
206
|
+
action="store_true",
|
|
207
|
+
help="Completely clear all cached files and symbols",
|
|
208
|
+
)
|
|
209
|
+
prune_parser.add_argument(
|
|
210
|
+
"--max-entries",
|
|
211
|
+
type=int,
|
|
212
|
+
default=5000,
|
|
213
|
+
help="Maximum cache entries to retain (default: 5000)",
|
|
214
|
+
)
|
|
215
|
+
prune_parser.add_argument(
|
|
216
|
+
"--ttl-days",
|
|
217
|
+
type=int,
|
|
218
|
+
default=30,
|
|
219
|
+
help="Evict entries older than N days (default: 30)",
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Subcommand: update / upgrade
|
|
223
|
+
update_parser = subparsers.add_parser(
|
|
224
|
+
"update",
|
|
225
|
+
aliases=["upgrade"],
|
|
226
|
+
help="Check for updates and automatically upgrade TokenJar to the latest release",
|
|
227
|
+
)
|
|
228
|
+
update_parser.add_argument(
|
|
229
|
+
"--force",
|
|
230
|
+
action="store_true",
|
|
231
|
+
help="Force reinstallation even if already on the latest version",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Subcommand: uninstall / purge
|
|
235
|
+
uninstall_parser = subparsers.add_parser(
|
|
236
|
+
"uninstall",
|
|
237
|
+
aliases=["purge", "self-destruct"],
|
|
238
|
+
help="Completely uninstall TokenJar: revert IDE configs, remove project rules, hooks, cache, and PATH",
|
|
239
|
+
)
|
|
240
|
+
uninstall_parser.add_argument(
|
|
241
|
+
"-y",
|
|
242
|
+
"--yes",
|
|
243
|
+
action="store_true",
|
|
244
|
+
help="Skip confirmation prompt and immediately purge all TokenJar traces",
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# If called with no arguments:
|
|
248
|
+
# If piped by AI assistant (Cursor / Claude Desktop / Windsurf) -> run Stdio MCP Server!
|
|
249
|
+
# If user runs interactively in terminal -> open Web Dashboard UI in browser (matching Rust binary)!
|
|
250
|
+
if len(sys.argv) == 1:
|
|
251
|
+
if sys.stdin.isatty():
|
|
252
|
+
print("๐ Launching TokenJar Web Dashboard in your browser...")
|
|
253
|
+
from tokenjar.ui.server import start_ui_server
|
|
254
|
+
|
|
255
|
+
start_ui_server(port=4141, open_browser=True)
|
|
256
|
+
return
|
|
257
|
+
else:
|
|
258
|
+
from tokenjar.server import mcp
|
|
259
|
+
|
|
260
|
+
mcp.run(transport="stdio")
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
args = parser.parse_args()
|
|
264
|
+
|
|
265
|
+
if args.subcommand == "version":
|
|
266
|
+
print("tokenjar 1.0.1")
|
|
267
|
+
return
|
|
268
|
+
|
|
269
|
+
if args.subcommand in (None, "server"):
|
|
270
|
+
from tokenjar.server import mcp
|
|
271
|
+
|
|
272
|
+
mcp.run(transport="stdio")
|
|
273
|
+
|
|
274
|
+
elif args.subcommand == "stats":
|
|
275
|
+
from tokenjar.telemetry.stats import tracker
|
|
276
|
+
|
|
277
|
+
print(tracker.render_dashboard())
|
|
278
|
+
|
|
279
|
+
elif args.subcommand == "status":
|
|
280
|
+
import json
|
|
281
|
+
from pathlib import Path
|
|
282
|
+
|
|
283
|
+
from tokenjar.cache.persistent_cache import PersistentCache
|
|
284
|
+
from tokenjar.hooks.manager import HookManager
|
|
285
|
+
from tokenjar.rules.manager import RULES_MARKER_START, RulesManager
|
|
286
|
+
|
|
287
|
+
project_path = Path(args.path).resolve()
|
|
288
|
+
configs = HookManager.get_supported_cli_configs()
|
|
289
|
+
|
|
290
|
+
print("=" * 60)
|
|
291
|
+
print("๐ TOKENJAR SYSTEM STATUS REPORT")
|
|
292
|
+
print("=" * 60)
|
|
293
|
+
|
|
294
|
+
# 1. Check CLI MCP Integrations
|
|
295
|
+
active_clis = []
|
|
296
|
+
inactive_clis = []
|
|
297
|
+
for name, cfg_path in configs.items():
|
|
298
|
+
is_inst = HookManager.is_cli_installed(name)
|
|
299
|
+
is_active = False
|
|
300
|
+
if cfg_path.exists():
|
|
301
|
+
try:
|
|
302
|
+
with open(cfg_path, "r", encoding="utf-8") as f:
|
|
303
|
+
data = json.load(f)
|
|
304
|
+
if "tokenjar" in data.get("mcpServers", {}):
|
|
305
|
+
is_active = True
|
|
306
|
+
except Exception:
|
|
307
|
+
pass
|
|
308
|
+
if is_active:
|
|
309
|
+
active_clis.append(f"๐ข {name} (Active in {cfg_path.name})")
|
|
310
|
+
elif is_inst:
|
|
311
|
+
inactive_clis.append(f"๐ด {name} (Installed, but TokenJar disabled)")
|
|
312
|
+
else:
|
|
313
|
+
inactive_clis.append(f"โช {name} (Not installed)")
|
|
314
|
+
|
|
315
|
+
overall_active = len(active_clis) > 0
|
|
316
|
+
overall_badge = "๐ข ACTIVE (Operational)" if overall_active else "๐ด INACTIVE (Turn on with 'tokenjar on')"
|
|
317
|
+
print(f"Overall Engine Status : {overall_badge}\n")
|
|
318
|
+
|
|
319
|
+
print("AI Assistant Integrations:")
|
|
320
|
+
for line in active_clis + inactive_clis:
|
|
321
|
+
print(f" โข {line}")
|
|
322
|
+
|
|
323
|
+
# 2. Check Output Optimization Status
|
|
324
|
+
output_active = RulesManager.get_output_mode(project_path)
|
|
325
|
+
out_badge = (
|
|
326
|
+
"๐ข ON (Compact surgical diffs & zero-truncation)" if output_active else "โช OFF (Default full output)"
|
|
327
|
+
)
|
|
328
|
+
print(f"\nOutput Optimization : {out_badge}")
|
|
329
|
+
|
|
330
|
+
# 3. Check Project Steering Rules
|
|
331
|
+
rule_files_found = []
|
|
332
|
+
for r_name in RulesManager.SUPPORTED_RULE_FILES:
|
|
333
|
+
r_path = project_path / r_name
|
|
334
|
+
if r_path.exists():
|
|
335
|
+
try:
|
|
336
|
+
content = r_path.read_text(encoding="utf-8")
|
|
337
|
+
if RULES_MARKER_START in content:
|
|
338
|
+
rule_files_found.append(r_name)
|
|
339
|
+
except Exception:
|
|
340
|
+
pass
|
|
341
|
+
if rule_files_found:
|
|
342
|
+
print(f"Project Steering Rules: ๐ข INSTALLED ({', '.join(rule_files_found)})")
|
|
343
|
+
else:
|
|
344
|
+
print("Project Steering Rules: ๐ด NOT INSTALLED (Run 'tokenjar init-rules')")
|
|
345
|
+
|
|
346
|
+
# 4. Check L2 Persistent Cache
|
|
347
|
+
try:
|
|
348
|
+
cache = PersistentCache()
|
|
349
|
+
entries = cache.count_entries()
|
|
350
|
+
print(f"L2 Persistent Cache : ๐ข ONLINE ({entries} cached entries in ~/.tokenjar/cache.db)")
|
|
351
|
+
except Exception as e:
|
|
352
|
+
print(f"L2 Persistent Cache : โ ๏ธ Error accessing DB: {e}")
|
|
353
|
+
|
|
354
|
+
print("=" * 60)
|
|
355
|
+
print("Useful Commands:")
|
|
356
|
+
print(" tokenjar on -> Enable TokenJar for THIS project")
|
|
357
|
+
print(" tokenjar off -> Disable TokenJar for THIS project")
|
|
358
|
+
print(" tokenjar on --global -> Enable MCP across all IDEs globally")
|
|
359
|
+
print(" tokenjar off --global -> Disable MCP across all IDEs globally")
|
|
360
|
+
print(" tokenjar ui -> Open Web Dashboard in browser")
|
|
361
|
+
print(" tokenjar stats -> View live token and financial savings")
|
|
362
|
+
print(" tokenjar status -> Check operational status")
|
|
363
|
+
print("=" * 60)
|
|
364
|
+
if not overall_active:
|
|
365
|
+
sys.exit(1)
|
|
366
|
+
|
|
367
|
+
elif args.subcommand == "reset-stats":
|
|
368
|
+
from tokenjar.telemetry.stats import tracker
|
|
369
|
+
|
|
370
|
+
tracker.reset()
|
|
371
|
+
print("Telemetry metrics have been successfully reset.")
|
|
372
|
+
|
|
373
|
+
elif args.subcommand == "run":
|
|
374
|
+
if not args.command:
|
|
375
|
+
print("Error: No command provided to run. Example: tokenjar run pytest")
|
|
376
|
+
sys.exit(1)
|
|
377
|
+
from tokenjar.hooks.manager import execute_filtered_command
|
|
378
|
+
|
|
379
|
+
exit_code = execute_filtered_command(args.command)
|
|
380
|
+
sys.exit(exit_code)
|
|
381
|
+
|
|
382
|
+
elif args.subcommand == "hook":
|
|
383
|
+
from tokenjar.hooks.manager import HookManager
|
|
384
|
+
|
|
385
|
+
success, msg = HookManager.install_shell_hook(target_shell=args.shell)
|
|
386
|
+
print(msg)
|
|
387
|
+
if not success:
|
|
388
|
+
sys.exit(1)
|
|
389
|
+
|
|
390
|
+
elif args.subcommand == "unhook":
|
|
391
|
+
from tokenjar.hooks.manager import HookManager
|
|
392
|
+
|
|
393
|
+
success, msg = HookManager.uninstall_shell_hook()
|
|
394
|
+
print(msg)
|
|
395
|
+
if not success:
|
|
396
|
+
sys.exit(1)
|
|
397
|
+
|
|
398
|
+
elif args.subcommand in ("on", "enable"):
|
|
399
|
+
from tokenjar.hooks.manager import HookManager
|
|
400
|
+
from tokenjar.rules.manager import RulesManager
|
|
401
|
+
|
|
402
|
+
if getattr(args, "global_scope", False):
|
|
403
|
+
results = HookManager.enable_all()
|
|
404
|
+
for name, ok, msg in results:
|
|
405
|
+
if "skipped" in msg.lower():
|
|
406
|
+
status = "โช"
|
|
407
|
+
else:
|
|
408
|
+
status = "๐ข" if ok else "โ"
|
|
409
|
+
print(f"{status} {name}: {msg}")
|
|
410
|
+
print("\nโจ TokenJar is now GLOBALLY ACTIVE across detected IDEs!")
|
|
411
|
+
print("๐ก Projects remain clean by default. To enable for a specific project, run:")
|
|
412
|
+
print(" tokenjar on")
|
|
413
|
+
else:
|
|
414
|
+
# Local on
|
|
415
|
+
HookManager.enable_all()
|
|
416
|
+
rule_results = RulesManager.install_rules(".")
|
|
417
|
+
for name, ok, msg in rule_results:
|
|
418
|
+
print(f"๐ข Rules: {msg}")
|
|
419
|
+
print("\nโจ TokenJar is now ACTIVE for this project!")
|
|
420
|
+
print("๐ก Other projects remain unaffected unless explicitly enabled.")
|
|
421
|
+
|
|
422
|
+
elif args.subcommand in ("off", "disable"):
|
|
423
|
+
from tokenjar.hooks.manager import HookManager
|
|
424
|
+
from tokenjar.rules.manager import RulesManager
|
|
425
|
+
|
|
426
|
+
if getattr(args, "global_scope", False):
|
|
427
|
+
results = HookManager.disable_all()
|
|
428
|
+
for name, ok, msg in results:
|
|
429
|
+
if "skipped" in msg.lower():
|
|
430
|
+
status = "โช"
|
|
431
|
+
else:
|
|
432
|
+
status = "๐ด" if ok else "โ"
|
|
433
|
+
print(f"{status} {name}: {msg}")
|
|
434
|
+
print("\nโช TokenJar has been deactivated globally across all IDEs.")
|
|
435
|
+
else:
|
|
436
|
+
rule_results = RulesManager.remove_rules(".")
|
|
437
|
+
for name, ok, msg in rule_results:
|
|
438
|
+
print(f"๐ด Rules: {msg}")
|
|
439
|
+
print("\nโช TokenJar has been deactivated for THIS project.")
|
|
440
|
+
print("๐ก Global MCP and other projects remain active and unaffected.")
|
|
441
|
+
print(" (To remove globally from all IDEs, run: tokenjar off --global)")
|
|
442
|
+
|
|
443
|
+
elif args.subcommand in ("install-mcp", "enable-mcp"):
|
|
444
|
+
from tokenjar.hooks.manager import HookManager
|
|
445
|
+
|
|
446
|
+
results = HookManager.enable_all(only_installed=not getattr(args, "all", False))
|
|
447
|
+
for name, ok, msg in results:
|
|
448
|
+
if "skipped" in msg.lower():
|
|
449
|
+
status = "โช"
|
|
450
|
+
else:
|
|
451
|
+
status = "๐ข" if ok else "โ"
|
|
452
|
+
print(f"{status} {name}: {msg}")
|
|
453
|
+
|
|
454
|
+
elif args.subcommand in ("uninstall-mcp", "disable-mcp"):
|
|
455
|
+
from tokenjar.hooks.manager import HookManager
|
|
456
|
+
|
|
457
|
+
results = HookManager.disable_all()
|
|
458
|
+
for name, ok, msg in results:
|
|
459
|
+
if "skipped" in msg.lower():
|
|
460
|
+
status = "โช"
|
|
461
|
+
else:
|
|
462
|
+
status = "๐ด" if ok else "โ"
|
|
463
|
+
print(f"{status} {name}: {msg}")
|
|
464
|
+
|
|
465
|
+
elif args.subcommand in ("setup-commands", "install-commands"):
|
|
466
|
+
from tokenjar.hooks.manager import HookManager
|
|
467
|
+
|
|
468
|
+
results = HookManager.install_all_slash_commands(only_installed=True)
|
|
469
|
+
all_ok = True
|
|
470
|
+
for name, ok, msg in results:
|
|
471
|
+
if "skipped" in msg.lower():
|
|
472
|
+
status = "โช"
|
|
473
|
+
else:
|
|
474
|
+
status = "โ
" if ok else "โ"
|
|
475
|
+
if not ok:
|
|
476
|
+
all_ok = False
|
|
477
|
+
print(f"{status} {name}: {msg}")
|
|
478
|
+
if not all_ok:
|
|
479
|
+
sys.exit(1)
|
|
480
|
+
|
|
481
|
+
elif args.subcommand in ("init", "init-rules"):
|
|
482
|
+
from tokenjar.rules.manager import RulesManager
|
|
483
|
+
|
|
484
|
+
if getattr(args, "clean", False):
|
|
485
|
+
results = RulesManager.remove_rules(args.path)
|
|
486
|
+
for name, ok, msg in results:
|
|
487
|
+
print(f"๐ด Rules: {msg}")
|
|
488
|
+
else:
|
|
489
|
+
results = RulesManager.install_rules(
|
|
490
|
+
args.path,
|
|
491
|
+
compact_output=args.compact_output,
|
|
492
|
+
only_installed=not args.all,
|
|
493
|
+
)
|
|
494
|
+
all_ok = True
|
|
495
|
+
for name, ok, msg in results:
|
|
496
|
+
if "skipped" in msg.lower():
|
|
497
|
+
status = "โช"
|
|
498
|
+
else:
|
|
499
|
+
status = "โ
" if ok else "โ"
|
|
500
|
+
if not ok:
|
|
501
|
+
all_ok = False
|
|
502
|
+
print(f"{status} {name}: {msg}")
|
|
503
|
+
if not all_ok:
|
|
504
|
+
sys.exit(1)
|
|
505
|
+
|
|
506
|
+
elif args.subcommand == "output":
|
|
507
|
+
from tokenjar.rules.manager import RulesManager
|
|
508
|
+
|
|
509
|
+
if args.state == "on":
|
|
510
|
+
ok, msg, files = RulesManager.set_output_mode(args.path, enabled=True)
|
|
511
|
+
if ok:
|
|
512
|
+
print("๐ข Output Optimization: ON (Compact Mode Active)")
|
|
513
|
+
print(" โข Enforces surgical diffs and targeted block replacements.")
|
|
514
|
+
print(" โข ZERO TRUNCATION MANDATE active (no lazy comments).")
|
|
515
|
+
print(f" โข Updated files: {', '.join(files)}")
|
|
516
|
+
else:
|
|
517
|
+
print(f"โ Error: {msg}")
|
|
518
|
+
sys.exit(1)
|
|
519
|
+
elif args.state == "off":
|
|
520
|
+
ok, msg, files = RulesManager.set_output_mode(args.path, enabled=False)
|
|
521
|
+
if ok:
|
|
522
|
+
print("โช Output Optimization: OFF (Default Output Restored)")
|
|
523
|
+
print(" โข AI assistant will use standard, unrestricted output.")
|
|
524
|
+
print(" โข Output format returned to default.")
|
|
525
|
+
print(f" โข Updated files: {', '.join(files)}")
|
|
526
|
+
else:
|
|
527
|
+
print(f"โ Error: {msg}")
|
|
528
|
+
sys.exit(1)
|
|
529
|
+
else:
|
|
530
|
+
active = RulesManager.get_output_mode(args.path)
|
|
531
|
+
state_str = "๐ข ON (Compact Mode Active)" if active else "โช OFF (Default Output)"
|
|
532
|
+
print(f"Output Optimization Status: {state_str}")
|
|
533
|
+
print("\nUsage:")
|
|
534
|
+
print(" tokenjar output on -> Activate compact surgical diffs & zero-truncation")
|
|
535
|
+
print(" tokenjar output off -> Revert to default normal/verbose output")
|
|
536
|
+
|
|
537
|
+
elif args.subcommand in ("cache-prune", "cache-clear", "cache-reset"):
|
|
538
|
+
from tokenjar.cache.persistent_cache import PersistentCache
|
|
539
|
+
|
|
540
|
+
p = PersistentCache()
|
|
541
|
+
before = p.count_entries()
|
|
542
|
+
if getattr(args, "all", False) or args.subcommand in ("cache-clear", "cache-reset"):
|
|
543
|
+
p.clear()
|
|
544
|
+
after = p.count_entries()
|
|
545
|
+
print(f"๐งน L2 Cache Reset: {before} entries cleared. ({after} remaining in SQLite)")
|
|
546
|
+
else:
|
|
547
|
+
deleted = p.prune(max_entries=args.max_entries, max_age_days=args.ttl_days)
|
|
548
|
+
after = p.count_entries()
|
|
549
|
+
print(f"L2 Cache Pruned: {deleted} entries removed. ({before} -> {after} entries remaining)")
|
|
550
|
+
|
|
551
|
+
elif args.subcommand == "ui":
|
|
552
|
+
from tokenjar.hooks.manager import HookManager
|
|
553
|
+
|
|
554
|
+
HookManager.ensure_in_user_path()
|
|
555
|
+
from tokenjar.ui.server import start_ui_server
|
|
556
|
+
|
|
557
|
+
start_ui_server(port=args.port, open_browser=not args.no_open)
|
|
558
|
+
|
|
559
|
+
elif args.subcommand in ("uninstall", "purge", "self-destruct"):
|
|
560
|
+
if not getattr(args, "yes", False):
|
|
561
|
+
try:
|
|
562
|
+
confirm = input("โ ๏ธ Are you sure you want to completely uninstall TokenJar from this computer? (y/N): ")
|
|
563
|
+
if confirm.strip().lower() not in ("y", "yes"):
|
|
564
|
+
print("Aborted.")
|
|
565
|
+
sys.exit(0)
|
|
566
|
+
except (KeyboardInterrupt, EOFError):
|
|
567
|
+
print("\nAborted.")
|
|
568
|
+
sys.exit(0)
|
|
569
|
+
|
|
570
|
+
from tokenjar.hooks.manager import HookManager
|
|
571
|
+
|
|
572
|
+
HookManager.full_uninstall()
|
|
573
|
+
|
|
574
|
+
elif args.subcommand in ("update", "upgrade"):
|
|
575
|
+
import json
|
|
576
|
+
import subprocess
|
|
577
|
+
import urllib.request
|
|
578
|
+
|
|
579
|
+
from tokenjar import __version__
|
|
580
|
+
|
|
581
|
+
print("=" * 60)
|
|
582
|
+
print("๐ TOKENJAR AUTOMATIC UPDATE MANAGER")
|
|
583
|
+
print("=" * 60)
|
|
584
|
+
print(f"Current Engine Version : v{__version__}")
|
|
585
|
+
print("Checking for latest release on PyPI...")
|
|
586
|
+
|
|
587
|
+
latest_version = None
|
|
588
|
+
try:
|
|
589
|
+
req = urllib.request.Request(
|
|
590
|
+
"https://pypi.org/pypi/tokenjar-engine/json",
|
|
591
|
+
headers={"User-Agent": f"tokenjar/{__version__}"},
|
|
592
|
+
)
|
|
593
|
+
with urllib.request.urlopen(req, timeout=5) as response:
|
|
594
|
+
data = json.loads(response.read().decode("utf-8"))
|
|
595
|
+
latest_version = data.get("info", {}).get("version")
|
|
596
|
+
except Exception as e:
|
|
597
|
+
print(f"Note: Could not reach PyPI index directly ({e}). Proceeding to pip upgrade check...")
|
|
598
|
+
|
|
599
|
+
if latest_version:
|
|
600
|
+
print(f"Latest PyPI Release : v{latest_version}")
|
|
601
|
+
|
|
602
|
+
if latest_version and latest_version == __version__ and not getattr(args, "force", False):
|
|
603
|
+
print("\nโจ TokenJar is already on the latest version!")
|
|
604
|
+
print(f" (No action needed. Current: v{__version__})")
|
|
605
|
+
return
|
|
606
|
+
|
|
607
|
+
if latest_version and latest_version != __version__:
|
|
608
|
+
print(f"\n๐ New version detected: v{latest_version} (installed: v{__version__})")
|
|
609
|
+
print("\n๐ฆ Upgrading tokenjar-engine via pip...")
|
|
610
|
+
|
|
611
|
+
try:
|
|
612
|
+
cmd = [sys.executable, "-m", "pip", "install", "--upgrade", "tokenjar-engine"]
|
|
613
|
+
if getattr(args, "force", False):
|
|
614
|
+
cmd.append("--force-reinstall")
|
|
615
|
+
res = subprocess.run(cmd, capture_output=True, text=True)
|
|
616
|
+
if res.returncode == 0:
|
|
617
|
+
target_v = latest_version or "latest"
|
|
618
|
+
print(f"\n๐ TokenJar has been successfully updated to v{target_v}!")
|
|
619
|
+
print("๐ก Tip: Restart any open AI coding sessions or IDE windows to load updated middleware.")
|
|
620
|
+
else:
|
|
621
|
+
print(f"\nโ Pip update command returned error:\n{res.stderr or res.stdout}")
|
|
622
|
+
sys.exit(res.returncode)
|
|
623
|
+
except Exception as e:
|
|
624
|
+
print(f"\nโ Failed to execute pip updater: {e}")
|
|
625
|
+
sys.exit(1)
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
if __name__ == "__main__":
|
|
629
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""TokenJar cache package โ Session-level file caching."""
|